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