I’ve fixed up the source code so it actually assembles properly into a
binary identical to INSTAL.CO. Much of it is straightforward, but there are
still some mysteries I haven't solved, like why is the handler for `RST 7
18h` (BASIC OPEN statement) being set up? And why do the interrupt handlers
PUSH and POP the A register seemingly needlessly?

—b9


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
INSTAL.ASM:;;;;;;     Install charset mapping for TERM and Printer on
the;;;     TRS-80 Televerket Modell 100 (Norwegian Model 100);;;;;;
Released on tape by Tandy Radio-Shack in 1984.;;; Recovered from tape
by Rune Dalvik.;;; Disassembled and commented by hackerb9.
;;; Can be assembled to .CO using 'asmx -b8003h instal.asm'.
;;; ROM Calls
CLS             EQU     0x4231
MENU            EQU     0x5797  ; Main menu
INTRET          EQU     0x71f7  ; Interrupt exit routine (pop all regs & RET)
SCANKBD         EQU     0x7242
BSBEEP          EQU     0x7662;;; RAM Locations
RS232RxHook     EQU     0xf5fc  ; RST 6.5 RAM Vector (VUARTH Data Ready)
                                ; (called on RS232 receive char interrupt)
PrtrOutVector   EQU     0xfae4  ; RST 7 jump table, index 0x0A
                                ; (called when sending a char to printer)
OpenVector      EQU     0xfb0a  ; RST 7 jump table, index 0x18
                                ; (called by BASIC OPEN statement)
ComLF           EQU     0xf65a
ParityControl   EQU     0xff8d

        CPU     8085            ; Hint for asmx assembler
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Six byte .CO header for Kyotronic-85 and kin.;; * START - Where the
program is ORG'd to run from.;; * LENGTH - Size of executable data
sans this 6 byte header.;; * ENTRY - Where the program will be entered
(entry point).;; - Executable data follow, ORG'ed at START.
BEGINP  EQU     8003h
        ORG     BEGINP
        DW      BEGINP, ENDP-BEGINP, BEGINP
        RORG    BEGINP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
INSTAL main routine

InstallStart:
        CALL    CLS
        MVI     B,193           ; Menu is 193 chars long
        LXI     H,MenuText      ; "\tDEFINISION AV KARAKTERSETT\n\r..."
ShowMenuText:
        MOV     A,M
        RST     4               ; RST 4 is putchar
        DCR     B
        INX     H
        JNZ     ShowMenuText
        XRA     A               ; Clear Z and C flags
WaitForKey:
        CALL    SCANKBD
        JZ      WaitForKey
        JC      GotSpecial
        CPI     '1'
        CZ      SetupRS232Hook  ; RS-232 interrupt on byte received
        CPI     '2'
        CZ      RemoveRS232Hook
        CPI     '3'
        CZ      SetupPrinterHook ; Intercept bytes sent to printer
        CPI     '4'
        CZ      RemovePrinterHook
        CPI     '5'
        CZ      DisableTxAutoLF
        CPI     '6'
        CZ      EnableTxAutoLF
        XRA     A
        CALL    BSBEEP
        JMP     InstallStart
GotSpecial:
        CPI     0x7             ; F8 key
        CZ      MENU
        JMP     WaitForKey

RemoveRS232Hook:
        MVI     A,0xc9          ; C9 is RET
        STA     (RS232RxHook)   ; F5FC is RST6.5 hook, called on Rx interrupt
        LXI     H,0x0
        SHLD    (RS232RxHook+1)
        LXI     H,0x7ff3        ; Address 7FF3 holds a RET statement
        SHLD    (OpenVector)
        MVI     A,'.'
        STA     (RS232HookStatus)
        RET

DisableTxAutoLF:
        MVI     A,0x1
        STA     (ComLF)         ; Do not add a LF after transmitting CR
        MVI     A,'*'
        STA     (AutoLFStatus)
        RET

EnableTxAutoLF:
        MVI     A,0x0
        STA     (COMLF)         ; Automatically send LF after CR
        MVI     A,'.'
        STA     (AutoLFStatus)
        RET

SetupRS232Hook:
        MVI     A,0xc3          ;C3 is JMP
        STA     (RS232RxHook)   ; 0xf5fc
        LXI     H,RxHandler     ; 0x808d
        SHLD    (RS232RxHook+1) ; 0xf5fd
        LXI     H,OpenHandler   ; 0x80b8
        SHLD    (OpenVector)    ; 0xfb0a
        MVI     A,'*'
        STA     (RS232HookStatus)
        RET
;;; This handler is called by the RST 6.5 routine (6DAC).;; It
replaces RS232 received [\]{|} with ÆØÅæøå (M100 accents)
RxHandler:
        DI                      ; Disable interrupt while handling RST 6.5
        ; A POP is necessary to discard the caller since we will not RET.
        XTHL                    ; Swap HL with (SP), essentially POP+PUSH HL
        PUSH    D               ; push everything: DE, BC, AF
        PUSH    B
        PUSH    PSW
        LXI     H,INTRET        ; 71F7 = Pop all, enable interrupts, and RET
        PUSH    H               ; Make next "RET" jump to 71F7.
        IN      0xC8            ; Read byte from UART Receive Buffer
        LXI     H,ParityControl ; FF8D = RS232 parity control byte
        ANA     M
        ;; The above is how the standard Rx handler at 0x6DAC starts.
        ;; Below is where things change.
        PUSH    PSW             ; Why this weird PUSH/POP of A?
        LXI     H,Mapping       ; HL=>mapping = [Æ\Ø]Å{æ|ø}å
RxCheckForBrackets:
        POP     PSW
        CMP     M               ; [\]{|}
        JZ      RxReplaceWithAccents
        INX     H
        INX     H
        PUSH    PSW
        MOV     A,M
        CPI     0xff
        JNZ     RxCheckForBrackets
        POP     PSW
        JMP     RxDone
RxReplaceWithAccents:
        INX     H               ; ÆØÅæøå
        MOV     A,M
RxDone:
        EI
        JMP     0x6dbd          ; Jump back into the standard Rx handler.
        ;; Note: The next RET will jump to INTRET (71f7) which will
        ;; POP all the registers and reenable interrupts before
        ;; actually returning.
;;; This handler is called by the ROM BSOPEN statement (4CCD).;; It
replaces ÆØÅæøå (M100 accents) with [\]{|}.;; Input and output in
register C.
OpenHandler:
        DI                      ; Disable interrupts while in RST 7+18h hook
        PUSH    H
        MOV     A,C
        PUSH    PSW
        LXI     H,RevMapping    ; From M100 to ASCII Æ[Ø\Å]æ{ø|å}
OpenCheckForAccents:
        POP     PSW
        CMP     M               ; ÆØÅæøå
        JZ      OpenReplaceWithBrackets
        INX     H
        INX     H
        PUSH    PSW
        MOV     A,M
        CPI     0xff
        JNZ     OpenCheckForAccents
        POP     PSW
        JMP     OpenDone
OpenReplaceWithBrackets:
        INX     H               ; [\]{|}
        MOV     A,M
OpenDone:
        MOV     C,A
        POP     H
        EI                      ; All done with handler, reenable interrupts.
        RET

Mapping:                        ; From ASCII to M100 [Æ\Ø]Å{æ|ø}å
        db      '[', D0h
        db      '\\',D4h
        db      ']', D2h
        db      '{', D1h
        db      '|', D5h
        db      '}', D3h
        db        FFh

RevMapping:                     ; From M100 to ASCII Æ[Ø\Å]æ{ø|å}
        db      D0h,'['
        db      D4h,'\\'
        db      D2h,']'
        db      D1h,'{'
        db      D5h,'|'
        db      D3h,'}'
        db       FFh
;;; In Norway do they prefer Line Feed, Carriage Return instead of CRLF?
MenuText:
        db      "\tDEFINISION AV KARAKTERSETT\n\r"
        db      "   1: norsk telcom   2: engelsk telcom\n\r"
        db      "   3: norsk skriver  4: engelsk skriver\n\r"
        db      "   5:  CR & LF       6:   CR\n\r"
        db      "\t    < F8 > MENY\n\r\n"
        db      "    TAST ",D4h,"NSKET KODE\n\r "
;;; The following variables get set to '*' or '.' to indicate their;;;
status, but they are never used.
RS232HookStatus:
        db      ".    "
PrinterHookStatus:
        db      ".    "
AutoLFStatus:
        db      "."

SetupPrinterHook:
        LXI     H,PrinterHandler
        SHLD    (PrtrOutVector)
        MVI     A,'*'
        STA     (PrinterHookStatus)
        RET

RemovePrinterHook:
        LXI     H,0x7ff3
        SHLD    (PrtrOutVector)
        MVI     A,'.'
        STA     (PrinterHookStatus)
        RET
;; Replace ÆØÅæøå (M100 accents) sent to printer with [\]{|};; This
presumes a Norwegian printer.
PrinterHandler:
        PUSH    H
        PUSH    B
        PUSH    PSW
        LXI     H,RevMapping    ; From M100 to ASCII Æ[Ø\Å]æ{ø|å}
PrtrCheckForAccents:
        POP     PSW
        CMP     M
        JZ      PrtrReplaceWithBrackets
        INX     H
        INX     H
        PUSH    PSW
        MOV     A,M
        CPI     0xff
        JNZ     PrtrCheckForAccents
        POP     PSW
        JMP     PrtrDone
PrtrReplaceWithBrackets:
        INX     H               ; [\]{|}
        MOV     A,M
PrtrDone:
        POP     B
        POP     H
        RET
        NOP
        NOP

ENDP:
;;; Note: This program relies on RESRAM having been run first;;; to
reserve 512 bytes in the lower RAM area.;;; The guts of RESRAM are:
;; 160 BH=PEEK(64193) AND 240;; 190 POKE BH*256,2;; 200 POKE
64193,BH+2;; 210 CALL 32237
;;; Open Question:;;;;;; ??? What is the purpose of the OpenHandler?
TELECOM doesn't use;;; BASIC's OPEN when uploading or downloading
files. Is this for;;; programs that do something like OPEN "COM:88N1E"
FOR OUTPUT AS #1?;;; How would it be helpful?;;;;;; The handler seems
to get run only when someone types 'OPEN "FOO";;; FOR INPUT AS #1',
but only _once_, not multiple times as one would;;; expect for
converting a filename or buffer.;;;;;; Register C, the nominal input
and output, seems to be set to the;;; file number, which doesn't make
any sense to transliterate.;;;


On Sat, Apr 4, 2026 at 7:17 PM B9 <[email protected]> wrote:

> On Sat, Apr 4, 2026 at 2:48 AM Rune Devik <[email protected]> wrote:
>
> > [...] I find re-implementing code a sure way to actually understand
> what's
> > going on ;) Sorry about that :D
> >
>
> That's the best way to learn.  We don't know what it is that we don't
> know. Trying to articulate our thoughts in code can sometimes reveal those
> blindspots.
>
>
> > [...] It still only handles ASCII though and not CP865 correctly so my
> > ÆØÅ's come out a bit funky but I can live with that :)
> >
>
> Leaving the "high ASCII" as is in strings is the correct thing to do. When
> the .DO file is transferred to an M100, it'll show up with the right
> accents. If you want to view M100 files on a modern computer, you'd convert
> them using a tool like iconv.
>
> By the way, the BASIC program is not actually in CP865, but a special
> character set that I do not think anyone knows the correct name for. It was
> used only on the Tandy/Radio-Shack portables, so some people call it the
> Model 100 charset. It appears your "Modell 100" actually uses a slight
> variant, the same as seen on the Tandy 200 (and later on the Tandy 102).
>
> --b9
>
> P.S. If anyone has knowledge of where the M100 character set came from or
> what it is actually named, I'd like to hear about it!
>
>

Attachment: instal.co
Description: Binary data

Attachment: instal.asm
Description: Binary data

Reply via email to