Thank you, you are correct, Rune. I had missed that those variables are
just a continuation of the menu text. I hadn’t actually run the program,
just examined it and the ROM you shared with us in a disassembler.

I tried replacing Virtual T’s “M102/M102rom.bin” file with the Norwegian
ROM and... it works! Or at least, it seems to. The zeroes have dots in them
instead of slashes and the keys to the right of L are ø and æ. The only
thing that confuses me is that the key to the right of P shows ȧ and I have
to use CODE+1 to get å. Is that the way it is on the actual Televerket M100?
[image: Virtual T running Televerket Modell 100 ROM]


Ken, what’s the best way to switch easily between ROMs which can run on
otherwise identical Virtual T machines? I’m pretty sure the British M100
ROM could work just as easily. The NEC PC-8201 (not 8201*A*) and the
Japanese Kyotronic-85 would be nice to have as options in Virtual T, too.

—b9

On Sun, Apr 5, 2026 at 9:29 AM Rune Devik <[email protected]> wrote:

> Nice work! I don't understand much of this to be honest as my assembly is
> limited to a few credits at the University many many years ago and focused
> on IA32 and MIPS assembly ;)
>
> ;;; The following variables get set to '*' or '.' to indicate their ;;;
> status, but they are never used.
>
> I believe the selection on the left (1, 3 and 5) are the "Norwegian
> Settings" you may enable and the selection on the other side 2, 4 and 6 are
> the default "US/English" settings. The * and . has a very nice function in
> the UI as it lets you see what you currently have selected. If you for
> instance have selected Norwegian TELECOM, English Printer and Norwegian
> line feed then the display will show:   *   .    *
> at the bottom when the program is loaded. First time the program is loaded
> it will show:      .    .    .
> at the bottom indicating the default "US/English" settings.
>
> Regards,
> Rune
>
> On Sun, Apr 5, 2026 at 3:32 PM B 9 <[email protected]> wrote:
>
>> 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!
>>>
>>>
>
> --
> mvh,
> Rune Devik
>

Reply via email to