Skip to content

Instantly share code, notes, and snippets.

@damieng
Created April 10, 2026 17:08
Show Gist options
  • Select an option

  • Save damieng/f61a57616f2f793bb2bf130d351b13eb to your computer and use it in GitHub Desktop.

Select an option

Save damieng/f61a57616f2f793bb2bf130d351b13eb to your computer and use it in GitHub Desktop.
Annotated VTX5000 ROM disassembly
; =============================================================================
; VTX5000 Prestel/Viewdata Modem ROM - Annotated Disassembly
; =============================================================================
;
; ROM: vtx5000-3-1.rom (8192 bytes, $0000-$1FFF)
; Hardware: Prism VTX5000 Modem for the ZX Spectrum 48K
; Developer: Scicon Ltd, 1983
; Version: 3.1 (RT/DS)
;
; HARDWARE OVERVIEW:
; - 8K EPROM mapped at $0000-$1FFF (overrides Spectrum ROM via ROMCS)
; - Intel 8251 USART for serial communication
; - Texas Instruments TCM3101 V.23 modem chip (1200/75 baud)
; - 4.433MHz PAL colour burst crystal for modem clocking
;
; I/O PORT MAP:
; Port $FF (A7=1): 8251 Control/Status register (active high)
; Write: Mode register (first write after reset), then Command register
; Read: Status register
; Bit 0: TxRDY - Transmit buffer empty
; Bit 1: RxRDY - Receive data available
; Bit 2: TxEMPTY - Transmitter completely empty
; Bit 3: PE - Parity error
; Bit 4: OE - Overrun error
; Bit 5: FE - Framing error
; Bit 6: SYNDET - Break detected
; Bit 7: DSR - Data Set Ready
; Port $7F (A7=0): 8251 Data register
; Write: Transmit data
; Read: Receive data
; Port $FE: Standard ZX Spectrum ULA (keyboard/border/speaker)
;
; ROM PAGING:
; The 8251's RTS output controls ROMCS:
; Command byte $15 (RTS=0): VTX5000 ROM paged in
; Command byte $35 (RTS=1): Spectrum ROM paged in
; EX (SP),HL x2 used as bus-cycle delay after paging
;
; ROM MAP:
; $0000-$0026 Jump table (API entry points)
; $0027-$0038 RST $38 maskable interrupt handler
; $003A-$1100 Embedded Spectrum BASIC program (Prestel terminal)
; $1101-$1B1C Z80 machine code (init, modem I/O, Viewdata engine)
; $1B1D-$1B1F Padding ($FF)
; $1B20-$1CFF 5x8 pixel character font (96 chars, $20-$7F)
; $1D00-$1F82 Startup splash screen (MICRONET 800 Viewdata frame)
; $1F83-$1FCB Configuration data and defaults
; $1FCC-$1FFF ROM paging trampolines (copied to high RAM at init)
;
; IX REGISTER USAGE:
; IX points to the VTX device state block in RAM. Key offsets:
; IX+$00/$01: Display buffer base address (low/high)
; IX+$02/$03: Ring buffer countdown
; IX+$04: Ring buffer size
; IX+$06: Device flags
; Bit 0: Online/connected mode
; Bit 1: Echo mode
; Bit 2: Keyboard locked
; Bit 4: Auto-answer mode
; Bit 5: Transmit busy
; Bit 7: 16K Spectrum flag
; IX+$07: 8251 mode register value
; IX+$08: 8251 command register value
; IX+$09: Keyboard/input state flags
; IX+$0A: Prestel frame parser state
; IX+$0B: Return code / exit reason
; 0=continue, 1=BREAK, 2=timeout, 3=carrier lost
; 4=error, 5=frame complete, 6=retry, 7=specific
; IX+$0C/$0D: Current cursor position (row/col)
; IX+$0E: Last row rendered
; IX+$0F: Last column rendered
; IX+$10: Previous line number for dirty tracking
; IX+$11/$12: Display attribute state (EXX register pair)
; IX+$13/$14: Saved attribute state
; IX+$15: Current display attribute byte
; IX+$17: Saved attribute for restore
; IX+$18: Previous keyboard scan result
; IX+$19/$1A: Carrier detect timeout counter
; IX+$1B: Idle timeout counter (init=$64=100)
; IX+$2C: Prestel frame repeat count
; IX+$2D: Prestel line retry count
; IX+$2E: Prestel attempt counter
; IX+$2F/$30: Prestel data pointer (low/high)
; IX+$31: Prestel shift state
; IX+$32: Prestel parser flags
; IX+$41: Prestel max attempts
; IX+$42/$43: Online timeout value
; IX+$44/$45: Offline timeout value
; IX+$46/$47: Character font base address
;
; IY REGISTER: Always $5C3A (ZX Spectrum system variables base ERR_NR)
; (IY+$01) = FLAGS ($5C3B)
; (IY+$19) = FRAMES low
; (IY+$1A) = FRAMES high
; (IY+$30) = BORDCR offset... etc
; (IY+$7B) = Bit 7: 0=16K Spectrum, 1=48K Spectrum
;
; =============================================================================
ORG $0000
; =============================================================================
; JUMP TABLE - API Entry Points ($0000-$0026)
; =============================================================================
; These are the public entry points called by the BASIC program via USR and
; by the ROM's own internal code. Each is a JP to the actual routine.
; =============================================================================
RESET:
JP $1101 ; Cold start / power-on reset
PRESTEL_RECV:
JP $11D3 ; Prestel data receive handler
ROM_CALL:
JP $13C1 ; Call routine in Spectrum ROM (pages ROM)
DEFB $FF ; Padding (unused entry, reads as RST $38)
DEFB $FF ; Padding (unused entry, reads as RST $38)
DEFB $FF ; Padding (unused entry, reads as RST $38)
MODEM_POLL:
JP $13D0 ; Main modem polling loop entry
MODEM_SETUP:
JP $13FA ; Set up modem parameters
RINGBUF_WRITE:
JP $15D6 ; Write byte to ring buffer
CALC_BUF_ADDR:
JP $1A59 ; Calculate display buffer address
HW_INIT:
JP $1424 ; Hardware initialisation (8251 + ports)
REFRESH_SCREEN:
JP $1A77 ; Refresh all display rows
CLEAR_SCREEN:
JP $1A9A ; Clear screen (pixel + attributes)
KEY_SCAN:
JP $1550 ; Keyboard scan (returns char in A)
DEFB $FF ; Padding (unused entry, reads as RST $38)
DEFB $FF ; Padding (unused entry, reads as RST $38)
DEFB $FF ; Padding (unused entry, reads as RST $38)
; =============================================================================
; RST $38 - Maskable Interrupt Handler ($0027-$0038)
; =============================================================================
; The Z80 executes RST $38 on every maskable interrupt (50Hz from ULA).
; This handler checks whether we're on a 16K or 48K Spectrum and jumps
; to the appropriate trampoline in high RAM. The trampolines page in the
; Spectrum ROM, call its interrupt handler, then page back to VTX ROM.
; =============================================================================
DEFB $FF ; Padding
DEFB $FF ; Padding
DEFB $FF ; Padding
DEFB $FF ; Padding
DEFB $FF ; Padding
DEFB $FF ; Padding
RST38_HANDLER:
PUSH AF ; Save AF
BIT 7,(IY+$7B) ; Test RAM size flag: 0=16K, 1=48K
JP Z,$7FED ; 16K: jump to trampoline at top of 16K RAM
JP $FFED ; 48K: jump to trampoline at top of 48K RAM
RST38_ENTRY:
JR RST38_HANDLER ; RST $38 vector at $0038 -> $002D
; =============================================================================
; EMBEDDED SPECTRUM BASIC PROGRAM ($003A-$1100)
; =============================================================================
; This is a complete ZX Spectrum BASIC program that implements the Prestel/
; Micronet 800 terminal software. It is copied into RAM at startup and
; provides the user interface: menus, login, file save/load, etc.
; The machine code routines at $1101+ handle the actual modem I/O and
; Viewdata rendering, called from BASIC via USR.
;
; Total size: $10C7 bytes (4295 bytes)
; =============================================================================
BASIC_PROGRAM:
; --- BASIC Line 10: REM (c) 1983 Scicon Ltd V3.1 RT/DS
; $003A
DEFB $00, $0A, $1E, $00, $EA, $7F, $20, $31, $39, $38, $33, $20, $53, $63, $69, $63
; $004A
DEFB $6F, $6E, $20, $4C, $74, $64, $20, $56, $33, $2E, $31, $20, $52, $54, $2F, $44
; $005A
DEFB $53, $0D
; --- BASIC Line 80: GO SUB 9600: CLEAR ix-1850: GO SUB 9600: LET o=0: LET l=1: DIM a$(22): D...
; $005C
DEFB $00, $50, $81, $00, $ED, $39, $36, $30, $30, $0E, $00, $00, $80, $25, $00, $3A
; $006C
DEFB $FD, $69, $78, $2D, $31, $38, $35, $30, $0E, $00, $00, $3A, $07, $00, $3A, $ED
; $007C
DEFB $39, $36, $30, $30, $0E, $00, $00, $80, $25, $00, $3A, $F1, $6F, $3D, $30, $0E
; $008C
DEFB $00, $00, $00, $00, $00, $3A, $F1, $6C, $3D, $31, $0E, $00, $00, $01, $00, $00
; $009C
DEFB $3A, $E9, $61, $24, $28, $32, $32, $0E, $00, $00, $16, $00, $00, $29, $3A, $E9
; $00AC
DEFB $62, $24, $28, $36, $30, $30, $0E, $00, $00, $58, $02, $00, $29, $3A, $F4, $32
; $00BC
DEFB $33, $36, $30, $39, $0E, $00, $00, $39, $5C, $00, $2C, $35, $30, $0E, $00, $00
; $00CC
DEFB $32, $00, $00, $3A, $F1, $62, $24, $28, $6C, $CC, $6C, $2B, $6C, $29, $3D, $C2
; $00DC
DEFB $6F, $2B, $C2, $6F, $0D
; --- BASIC Line 100: LET dm=9100: LET cl=9000: LET mc=9300: LET er=9050: LET gk=9400: LET tx=...
; $00E1
DEFB $00, $64, $A0, $00, $F1, $64, $6D, $3D, $39, $31, $30, $30, $0E, $00, $00, $8C
; $00F1
DEFB $23, $00, $3A, $F1, $63, $6C, $3D, $39, $30, $30, $30, $0E, $00, $00, $28, $23
; $0101
DEFB $00, $3A, $F1, $6D, $63, $3D, $39, $33, $30, $30, $0E, $00, $00, $54, $24, $00
; $0111
DEFB $3A, $F1, $65, $72, $3D, $39, $30, $35, $30, $0E, $00, $00, $5A, $23, $00, $3A
; $0121
DEFB $F1, $67, $6B, $3D, $39, $34, $30, $30, $0E, $00, $00, $B8, $24, $00, $3A, $F1
; $0131
DEFB $74, $78, $3D, $39, $35, $30, $30, $0E, $00, $00, $1C, $25, $00, $3A, $F1, $6D
; $0141
DEFB $6D, $3D, $38, $30, $30, $0E, $00, $00, $20, $03, $00, $3A, $F1, $45, $24, $3D
; $0151
DEFB $22, $22, $3A, $F1, $49, $24, $3D, $22, $22, $3A, $F1, $73, $24, $3D, $22, $63
; $0161
DEFB $22, $3A, $EB, $69, $3D, $6C, $CC, $32, $32, $0E, $00, $00, $16, $00, $00, $3A
; $0171
DEFB $F1, $61, $24, $28, $69, $29, $3D, $C2, $31, $30, $0E, $00, $00, $0A, $00, $00
; $0181
DEFB $3A, $F3, $69, $0D
; --- BASIC Line 330: LET mf=ix+6: LET exc=ix+11: LET fmod=ix+45: LET id=ix+55: LET t=ix- PEEK...
; $0185
DEFB $01, $4A, $7C, $00, $F1, $6D, $66, $3D, $69, $78, $2B, $36, $0E, $00, $00, $06
; $0195
DEFB $00, $00, $3A, $F1, $65, $78, $63, $3D, $69, $78, $2B, $31, $31, $0E, $00, $00
; $01A5
DEFB $0B, $00, $00, $3A, $F1, $66, $6D, $6F, $64, $3D, $69, $78, $2B, $34, $35, $0E
; $01B5
DEFB $00, $00, $2D, $00, $00, $3A, $F1, $69, $64, $3D, $69, $78, $2B, $35, $35, $0E
; $01C5
DEFB $00, $00, $37, $00, $00, $3A, $F1, $74, $3D, $69, $78, $2D, $BE, $69, $78, $2D
; $01D5
DEFB $BE, $28, $69, $78, $2B, $32, $0E, $00, $00, $02, $00, $00, $29, $2D, $32, $35
; $01E5
DEFB $36, $0E, $00, $00, $00, $01, $00, $2A, $28, $BE, $28, $69, $78, $2B, $6C, $29
; $01F5
DEFB $2B, $BE, $28, $69, $78, $2B, $33, $0E, $00, $00, $03, $00, $00, $29, $29, $0D
; --- BASIC Line 340: FOR i=o TO 9: LET I$=I$+ CHR$ PEEK (id+i): NEXT i
; $0205
DEFB $01, $54, $20, $00, $EB, $69, $3D, $6F, $CC, $39, $0E, $00, $00, $09, $00, $00
; $0215
DEFB $3A, $F1, $49, $24, $3D, $49, $24, $2B, $C2, $BE, $28, $69, $64, $2B, $69, $29
; $0225
DEFB $3A, $F3, $69, $0D
; --- BASIC Line 350: LET ls=l: LET E$="Download Failure": IF PEEK exc=o THEN LET E$="": LET...
; $0229
DEFB $01, $5E, $3D, $00, $F1, $6C, $73, $3D, $6C, $3A, $F1, $45, $24, $3D, $22, $44
; $0239
DEFB $6F, $77, $6E, $6C, $6F, $61, $64, $20, $46, $61, $69, $6C, $75, $72, $65, $22
; $0249
DEFB $3A, $FA, $BE, $65, $78, $63, $3D, $6F, $CB, $F1, $45, $24, $3D, $22, $22, $3A
; $0259
DEFB $F1, $6C, $73, $3D, $6F, $3A, $EC, $34, $30, $30, $0E, $00, $00, $90, $01, $00
; $0269
DEFB $0D
; --- BASIC Line 360: IF PEEK exc=7 THEN LET E$=""
; $026A
DEFB $01, $68, $15, $00, $FA, $BE, $65, $78, $63, $3D, $37, $0E, $00, $00, $07, $00
; $027A
DEFB $00, $CB, $F1, $45, $24, $3D, $22, $22, $0D
; --- BASIC Line 400: PAPER l: BORDER o: INK 7: IF PEEK exc=8 THEN GO TO 2000
; $0283
DEFB $01, $90, $29, $00, $DA, $6C, $3A, $E7, $6F, $3A, $D9, $37, $0E, $00, $00, $07
; $0293
DEFB $00, $00, $3A, $FA, $BE, $65, $78, $63, $3D, $38, $0E, $00, $00, $08, $00, $00
; $02A3
DEFB $CB, $EC, $32, $30, $30, $30, $0E, $00, $00, $D0, $07, $00, $0D
; --- BASIC Line 410: LET T$="": GO SUB tx
; $02B0
DEFB $01, $9A, $0B, $00, $F1, $54, $24, $3D, $22, $22, $3A, $ED, $74, $78, $0D
; --- BASIC Line 800: LET mn=o: GO SUB dm: GO TO mc
; $02BF
DEFB $03, $20, $0E, $00, $F1, $6D, $6E, $3D, $6F, $3A, $ED, $64, $6D, $3A, $EC, $6D
; $02CF
DEFB $63, $0D
; --- BASIC Line 900: DATA "Main Micronet Menu",8,10,"Log ON or OFF","Micronet Terminal","Save...
; $02D1
DEFB $03, $84, $A0, $00, $E4, $22, $4D, $61, $69, $6E, $20, $4D, $69, $63, $72, $6F
; $02E1
DEFB $6E, $65, $74, $20, $4D, $65, $6E, $75, $22, $2C, $38, $0E, $00, $00, $08, $00
; $02F1
DEFB $00, $2C, $31, $30, $0E, $00, $00, $0A, $00, $00, $2C, $22, $4C, $6F, $67, $20
; $0301
DEFB $4F, $4E, $20, $6F, $72, $20, $4F, $46, $46, $22, $2C, $22, $4D, $69, $63, $72
; $0311
DEFB $6F, $6E, $65, $74, $20, $54, $65, $72, $6D, $69, $6E, $61, $6C, $22, $2C, $22
; $0321
DEFB $53, $61, $76, $65, $20, $46, $72, $61, $6D, $65, $22, $2C, $22, $56, $69, $65
; $0331
DEFB $77, $20, $46, $72, $61, $6D, $65, $22, $2C, $22, $50, $72, $69, $6E, $74, $20
; $0341
DEFB $46, $72, $61, $6D, $65, $22, $2C, $22, $44, $6F, $77, $6E, $6C, $6F, $61, $64
; $0351
DEFB $65, $72, $22, $2C, $22, $4D, $61, $69, $6C, $62, $6F, $78, $20, $4D, $65, $73
; $0361
DEFB $73, $61, $67, $65, $22, $2C, $22, $45, $6E, $74, $65, $72, $20, $42, $41, $53
; $0371
DEFB $49, $43, $22, $0D
; --- BASIC Line 1000: LET mn=l: GO SUB dm: GO TO mc
; $0375
DEFB $03, $E8, $0E, $00, $F1, $6D, $6E, $3D, $6C, $3A, $ED, $64, $6D, $3A, $EC, $6D
; $0385
DEFB $63, $0D
; --- BASIC Line 1100: IF ls=l THEN LET E$="Already logged ON": GO TO mm
; $0387
DEFB $04, $4C, $22, $00, $FA, $6C, $73, $3D, $6C, $CB, $F1, $45, $24, $3D, $22, $41
; $0397
DEFB $6C, $72, $65, $61, $64, $79, $20, $6C, $6F, $67, $67, $65, $64, $20, $4F, $4E
; $03A7
DEFB $22, $3A, $EC, $6D, $6D, $0D
; --- BASIC Line 1110: IF I$(l) <> "?" THEN GO TO 1460
; $03AD
DEFB $04, $56, $17, $00, $FA, $49, $24, $28, $6C, $29, $C9, $22, $3F, $22, $CB, $EC
; $03BD
DEFB $31, $34, $36, $30, $0E, $00, $00, $B4, $05, $00, $0D
; --- BASIC Line 1400: GO SUB cl: LET E$="": INPUT "New ID No "; LINE q$: IF q$="" THEN GO TO mm
; $03C8
DEFB $05, $78, $28, $00, $ED, $63, $6C, $3A, $F1, $45, $24, $3D, $22, $22, $3A, $EE
; $03D8
DEFB $22, $4E, $65, $77, $20, $49, $44, $20, $4E, $6F, $20, $22, $3B, $CA, $71, $24
; $03E8
DEFB $3A, $FA, $71, $24, $3D, $22, $22, $CB, $EC, $6D, $6D, $0D
; --- BASIC Line 1440: IF LEN q$ <> 10 THEN LET E$="Invalid ID number": GO TO 1400
; $03F4
DEFB $05, $A0, $32, $00, $FA, $B1, $71, $24, $C9, $31, $30, $0E, $00, $00, $0A, $00
; $0404
DEFB $00, $CB, $F1, $45, $24, $3D, $22, $49, $6E, $76, $61, $6C, $69, $64, $20, $49
; $0414
DEFB $44, $20, $6E, $75, $6D, $62, $65, $72, $22, $3A, $EC, $31, $34, $30, $30, $0E
; $0424
DEFB $00, $00, $78, $05, $00, $0D
; --- BASIC Line 1450: LET I$=q$: FOR i=o TO 9: POKE (id+i), CODE q$(i+l): NEXT i
; $042A
DEFB $05, $AA, $28, $00, $F1, $49, $24, $3D, $71, $24, $3A, $EB, $69, $3D, $6F, $CC
; $043A
DEFB $39, $0E, $00, $00, $09, $00, $00, $3A, $F4, $28, $69, $64, $2B, $69, $29, $2C
; $044A
DEFB $AF, $71, $24, $28, $69, $2B, $6C, $29, $3A, $F3, $69, $0D
; --- BASIC Line 1460: LET T$=I$: GO SUB tx: CLS : PRINT AT 5,8; FLASH l;"Phone Computer": LET...
; $0456
DEFB $05, $B4, $4E, $00, $F1, $54, $24, $3D, $49, $24, $3A, $ED, $74, $78, $3A, $FB
; $0466
DEFB $3A, $F5, $AC, $35, $0E, $00, $00, $05, $00, $00, $2C, $38, $0E, $00, $00, $08
; $0476
DEFB $00, $00, $3B, $DB, $6C, $3B, $22, $50, $68, $6F, $6E, $65, $20, $43, $6F, $6D
; $0486
DEFB $70, $75, $74, $65, $72, $22, $3A, $F1, $6C, $73, $3D, $6C, $3A, $F1, $63, $6F
; $0496
DEFB $6E, $3D, $6C, $2B, $6C, $3A, $EC, $32, $30, $32, $30, $0E, $00, $00, $E4, $07
; $04A6
DEFB $00, $0D
; --- BASIC Line 1700: GO SUB cl: IF ls=o THEN LET E$="Already Logged OFF": GO TO mm
; $04A8
DEFB $06, $A4, $27, $00, $ED, $63, $6C, $3A, $FA, $6C, $73, $3D, $6F, $CB, $F1, $45
; $04B8
DEFB $24, $3D, $22, $41, $6C, $72, $65, $61, $64, $79, $20, $4C, $6F, $67, $67, $65
; $04C8
DEFB $64, $20, $4F, $46, $46, $22, $3A, $EC, $6D, $6D, $0D
; --- BASIC Line 1720: LET T$="*90#": GO SUB tx: POKE mf,o: LET X= USR str: IF X=2 THEN LET ls...
; $04D3
DEFB $06, $B8, $32, $00, $F1, $54, $24, $3D, $22, $2A, $39, $30, $23, $22, $3A, $ED
; $04E3
DEFB $74, $78, $3A, $F4, $6D, $66, $2C, $6F, $3A, $F1, $58, $3D, $C0, $73, $74, $72
; $04F3
DEFB $3A, $FA, $58, $3D, $32, $0E, $00, $00, $02, $00, $00, $CB, $F1, $6C, $73, $3D
; $0503
DEFB $6F, $3A, $EC, $6D, $6D, $0D
; --- BASIC Line 1730: GO SUB cl: PRINT AT 5,11;"Log OFF ?": PAUSE o: IF INKEY$ <> "y" THEN ...
; $0509
DEFB $06, $C2, $31, $00, $ED, $63, $6C, $3A, $F5, $AC, $35, $0E, $00, $00, $05, $00
; $0519
DEFB $00, $2C, $31, $31, $0E, $00, $00, $0B, $00, $00, $3B, $22, $4C, $6F, $67, $20
; $0529
DEFB $4F, $46, $46, $20, $3F, $22, $3A, $F2, $6F, $3A, $FA, $A6, $C9, $22, $79, $22
; $0539
DEFB $CB, $EC, $6D, $6D, $0D
; --- BASIC Line 1790: LET ls=o: LET T$="*90##": GO SUB tx: POKE mf,16: LET X= USR str: GO TO mm
; $053E
DEFB $06, $FE, $2F, $00, $F1, $6C, $73, $3D, $6F, $3A, $F1, $54, $24, $3D, $22, $2A
; $054E
DEFB $39, $30, $23, $23, $22, $3A, $ED, $74, $78, $3A, $F4, $6D, $66, $2C, $31, $36
; $055E
DEFB $0E, $00, $00, $10, $00, $00, $3A, $F1, $58, $3D, $C0, $73, $74, $72, $3A, $EC
; $056E
DEFB $6D, $6D, $0D
; --- BASIC Line 1900: DATA "Logon Menu",3,3,"Automatic Log ON","Manual Log ON","Log OFF"
; $0571
DEFB $07, $6C, $4B, $00, $E4, $22, $4C, $6F, $67, $6F, $6E, $20, $4D, $65, $6E, $75
; $0581
DEFB $22, $2C, $33, $0E, $00, $00, $03, $00, $00, $2C, $33, $0E, $00, $00, $03, $00
; $0591
DEFB $00, $2C, $22, $41, $75, $74, $6F, $6D, $61, $74, $69, $63, $20, $4C, $6F, $67
; $05A1
DEFB $20, $4F, $4E, $22, $2C, $22, $4D, $61, $6E, $75, $61, $6C, $20, $4C, $6F, $67
; $05B1
DEFB $20, $4F, $4E, $22, $2C, $22, $4C, $6F, $67, $20, $4F, $46, $46, $22, $0D
; --- BASIC Line 2000: LET con=o: GO SUB cl
; $05C0
DEFB $07, $D0, $0B, $00, $F1, $63, $6F, $6E, $3D, $6F, $3A, $ED, $63, $6C, $0D
; --- BASIC Line 2020: POKE mf,con: LET X= USR str: IF X=l THEN LET E$="": GO TO mm
; $05CF
DEFB $07, $E4, $20, $00, $F4, $6D, $66, $2C, $63, $6F, $6E, $3A, $F1, $58, $3D, $C0
; $05DF
DEFB $73, $74, $72, $3A, $FA, $58, $3D, $6C, $CB, $F1, $45, $24, $3D, $22, $22, $3A
; $05EF
DEFB $EC, $6D, $6D, $0D
; --- BASIC Line 2050: IF X=2 THEN LET E$="Line Break": LET ls=o: GO TO mm
; $05F3
DEFB $08, $02, $26, $00, $FA, $58, $3D, $32, $0E, $00, $00, $02, $00, $00, $CB, $F1
; $0603
DEFB $45, $24, $3D, $22, $4C, $69, $6E, $65, $20, $42, $72, $65, $61, $6B, $22, $3A
; $0613
DEFB $F1, $6C, $73, $3D, $6F, $3A, $EC, $6D, $6D, $0D
; --- BASIC Line 2070: GO SUB cl
; $061D
DEFB $08, $16, $04, $00, $ED, $63, $6C, $0D
; --- BASIC Line 2090: CLS : PRINT AT 5,l;"You are still logged ON": PAUSE o: GO TO mm
; $0625
DEFB $08, $2A, $2F, $00, $FB, $3A, $F5, $AC, $35, $0E, $00, $00, $05, $00, $00, $2C
; $0635
DEFB $6C, $3B, $22, $59, $6F, $75, $20, $61, $72, $65, $20, $73, $74, $69, $6C, $6C
; $0645
DEFB $20, $6C, $6F, $67, $67, $65, $64, $20, $4F, $4E, $22, $3A, $F2, $6F, $3A, $EC
; $0655
DEFB $6D, $6D, $0D
; --- BASIC Line 3000: GO SUB 3100: SAVE q$ CODE ix-960,960: GO TO mm
; $0658
DEFB $0B, $B8, $2B, $00, $ED, $33, $31, $30, $30, $0E, $00, $00, $1C, $0C, $00, $3A
; $0668
DEFB $F8, $71, $24, $AF, $69, $78, $2D, $39, $36, $30, $0E, $00, $00, $C0, $03, $00
; $0678
DEFB $2C, $39, $36, $30, $0E, $00, $00, $C0, $03, $00, $3A, $EC, $6D, $6D, $0D
; --- BASIC Line 3100: GO SUB cl: INPUT "Filename ? "; LINE q$: RETURN
; $0687
DEFB $0C, $1C, $19, $00, $ED, $63, $6C, $3A, $EE, $22, $46, $69, $6C, $65, $6E, $61
; $0697
DEFB $6D, $65, $20, $3F, $20, $22, $3B, $CA, $71, $24, $3A, $FE, $0D
; --- BASIC Line 4000: LET mn=4: GO SUB dm: GO TO mc
; $06A4
DEFB $0F, $A0, $14, $00, $F1, $6D, $6E, $3D, $34, $0E, $00, $00, $04, $00, $00, $3A
; $06B4
DEFB $ED, $64, $6D, $3A, $EC, $6D, $63, $0D
; --- BASIC Line 4100: LET con=100: GO TO 2020
; $06BC
DEFB $10, $04, $1B, $00, $F1, $63, $6F, $6E, $3D, $31, $30, $30, $0E, $00, $00, $64
; $06CC
DEFB $00, $00, $3A, $EC, $32, $30, $32, $30, $0E, $00, $00, $E4, $07, $00, $0D
; --- BASIC Line 4200: GO SUB 3100: GO SUB 4400
; $06DB
DEFB $10, $68, $18, $00, $ED, $33, $31, $30, $30, $0E, $00, $00, $1C, $0C, $00, $3A
; $06EB
DEFB $ED, $34, $34, $30, $30, $0E, $00, $00, $30, $11, $00, $0D
; --- BASIC Line 4210: LOAD q$ CODE : GO TO 4100
; $06F7
DEFB $10, $72, $11, $00, $EF, $71, $24, $AF, $3A, $EC, $34, $31, $30, $30, $0E, $00
; $0707
DEFB $00, $04, $10, $00, $0D
; --- BASIC Line 4400: PRINT AT 5,11;"Start Tape": RETURN
; $070C
DEFB $11, $30, $22, $00, $F5, $AC, $35, $0E, $00, $00, $05, $00, $00, $2C, $31, $31
; $071C
DEFB $0E, $00, $00, $0B, $00, $00, $3B, $22, $53, $74, $61, $72, $74, $20, $54, $61
; $072C
DEFB $70, $65, $22, $3A, $FE, $0D
; --- BASIC Line 4900: DATA "View Frames Menu",2,1,"Current Frame","Stored Frame"
; $0732
DEFB $13, $24, $43, $00, $E4, $22, $56, $69, $65, $77, $20, $46, $72, $61, $6D, $65
; $0742
DEFB $73, $20, $4D, $65, $6E, $75, $22, $2C, $32, $0E, $00, $00, $02, $00, $00, $2C
; $0752
DEFB $31, $0E, $00, $00, $01, $00, $00, $2C, $22, $43, $75, $72, $72, $65, $6E, $74
; $0762
DEFB $20, $46, $72, $61, $6D, $65, $22, $2C, $22, $53, $74, $6F, $72, $65, $64, $20
; $0772
DEFB $46, $72, $61, $6D, $65, $22, $0D
; --- BASIC Line 5000: POKE mf,16: LET X= USR str: RANDOMIZE USR 28041: GO TO mm
; $0779
DEFB $13, $88, $27, $00, $F4, $6D, $66, $2C, $31, $36, $0E, $00, $00, $10, $00, $00
; $0789
DEFB $3A, $F1, $58, $3D, $C0, $73, $74, $72, $3A, $F9, $C0, $32, $38, $30, $34, $31
; $0799
DEFB $0E, $00, $00, $89, $6D, $00, $3A, $EC, $6D, $6D, $0D
; --- BASIC Line 6000: LET mn=6: GO SUB dm: GO TO mc
; $07A4
DEFB $17, $70, $14, $00, $F1, $6D, $6E, $3D, $36, $0E, $00, $00, $06, $00, $00, $3A
; $07B4
DEFB $ED, $64, $6D, $3A, $EC, $6D, $63, $0D
; --- BASIC Line 6100: LET dl=25
; $07BC
DEFB $17, $D4, $0D, $00, $F1, $64, $6C, $3D, $32, $35, $0E, $00, $00, $19, $00, $00
; $07CC
DEFB $0D
; --- BASIC Line 6105: POKE fmod,123- CODE s$: POKE mf,dl: LET ix=ix-999: POKE 23732,ix-256* IN...
; $07CD
DEFB $17, $D9, $9E, $00, $F4, $66, $6D, $6F, $64, $2C, $31, $32, $33, $0E, $00, $00
; $07DD
DEFB $7B, $00, $00, $2D, $AF, $73, $24, $3A, $F4, $6D, $66, $2C, $64, $6C, $3A, $F1
; $07ED
DEFB $69, $78, $3D, $69, $78, $2D, $39, $39, $39, $0E, $00, $00, $E7, $03, $00, $3A
; $07FD
DEFB $F4, $32, $33, $37, $33, $32, $0E, $00, $00, $B4, $5C, $00, $2C, $69, $78, $2D
; $080D
DEFB $32, $35, $36, $0E, $00, $00, $00, $01, $00, $2A, $BA, $28, $69, $78, $2F, $32
; $081D
DEFB $35, $36, $0E, $00, $00, $00, $01, $00, $29, $3A, $F4, $32, $33, $37, $33, $33
; $082D
DEFB $0E, $00, $00, $B5, $5C, $00, $2C, $69, $78, $2F, $32, $35, $36, $0E, $00, $00
; $083D
DEFB $00, $01, $00, $3A, $FD, $69, $78, $2D, $6C, $3A, $ED, $39, $36, $30, $30, $0E
; $084D
DEFB $00, $00, $80, $25, $00, $3A, $F5, $AC, $32, $31, $0E, $00, $00, $15, $00, $00
; $085D
DEFB $2C, $30, $0E, $00, $00, $00, $00, $00, $3B, $3A, $F1, $58, $3D, $C0, $73, $74
; $086D
DEFB $72, $0D
; --- BASIC Line 6200: LET dl=9: GO TO 6105
; $086F
DEFB $18, $38, $18, $00, $F1, $64, $6C, $3D, $39, $0E, $00, $00, $09, $00, $00, $3A
; $087F
DEFB $EC, $36, $31, $30, $35, $0E, $00, $00, $D9, $17, $00, $0D
; --- BASIC Line 6300: GO SUB cl: PRINT AT 3,o;"Start Frame is '";s$;"'"''"Press New Letter": ...
; $088B
DEFB $18, $9C, $61, $00, $ED, $63, $6C, $3A, $F5, $AC, $33, $0E, $00, $00, $03, $00
; $089B
DEFB $00, $2C, $6F, $3B, $22, $53, $74, $61, $72, $74, $20, $46, $72, $61, $6D, $65
; $08AB
DEFB $20, $69, $73, $20, $27, $22, $3B, $73, $24, $3B, $22, $27, $22, $27, $27, $22
; $08BB
DEFB $50, $72, $65, $73, $73, $20, $4E, $65, $77, $20, $4C, $65, $74, $74, $65, $72
; $08CB
DEFB $22, $3A, $F2, $6F, $3A, $F1, $71, $24, $3D, $A6, $3A, $FA, $AF, $71, $24, $3D
; $08DB
DEFB $31, $33, $0E, $00, $00, $0D, $00, $00, $CB, $EC, $36, $30, $30, $30, $0E, $00
; $08EB
DEFB $00, $70, $17, $00, $0D
; --- BASIC Line 6310: IF (q$[12][00]<"a") OR (q$>"z") THEN LET E$="Invalid Letter": GO SUB er...
; $08F0
DEFB $18, $A6, $3A, $00, $FA, $28, $71, $24, $12, $00, $3C, $22, $61, $22, $29, $C5
; $0900
DEFB $28, $71, $24, $3E, $22, $7A, $22, $29, $CB, $F1, $45, $24, $3D, $22, $49, $6E
; $0910
DEFB $76, $61, $6C, $69, $64, $20, $4C, $65, $74, $74, $65, $72, $22, $3A, $ED, $65
; $0920
DEFB $72, $3A, $EC, $36, $33, $30, $30, $0E, $00, $00, $9C, $18, $00, $0D
; --- BASIC Line 6320: LET E$="": LET s$=q$: GO TO 6000
; $092E
DEFB $18, $B0, $1A, $00, $F1, $45, $24, $3D, $22, $22, $3A, $F1, $73, $24, $3D, $71
; $093E
DEFB $24, $3A, $EC, $36, $30, $30, $30, $0E, $00, $00, $70, $17, $00, $0D
; --- BASIC Line 6900: DATA "Downloader Menu",3,1,"Load,Exit & RUN","Load,Stay & RUN","Change S...
; $094C
DEFB $1A, $F4, $5C, $00, $E4, $22, $44, $6F, $77, $6E, $6C, $6F, $61, $64, $65, $72
; $095C
DEFB $20, $4D, $65, $6E, $75, $22, $2C, $33, $0E, $00, $00, $03, $00, $00, $2C, $31
; $096C
DEFB $0E, $00, $00, $01, $00, $00, $2C, $22, $4C, $6F, $61, $64, $2C, $45, $78, $69
; $097C
DEFB $74, $20, $26, $20, $52, $55, $4E, $22, $2C, $22, $4C, $6F, $61, $64, $2C, $53
; $098C
DEFB $74, $61, $79, $20, $26, $20, $52, $55, $4E, $22, $2C, $22, $43, $68, $61, $6E
; $099C
DEFB $67, $65, $20, $53, $74, $61, $72, $74, $20, $46, $72, $61, $6D, $65, $22, $0D
; --- BASIC Line 7000: LET E$=""
; $09AC
DEFB $1B, $58, $07, $00, $F1, $45, $24, $3D, $22, $22, $0D
; --- BASIC Line 7010: LET mn=7: GO SUB dm: GO TO mc
; $09B7
DEFB $1B, $62, $14, $00, $F1, $6D, $6E, $3D, $37, $0E, $00, $00, $07, $00, $00, $3A
; $09C7
DEFB $ED, $64, $6D, $3A, $EC, $6D, $63, $0D
; --- BASIC Line 7100: CLS : PRINT AT 5,o;"Please Wait": GO SUB 7700: FOR j=l TO 2+i: POKE t+j...
; $09CF
DEFB $1B, $BC, $54, $00, $FB, $3A, $F5, $AC, $35, $0E, $00, $00, $05, $00, $00, $2C
; $09DF
DEFB $6F, $3B, $22, $50, $6C, $65, $61, $73, $65, $20, $57, $61, $69, $74, $22, $3A
; $09EF
DEFB $ED, $37, $37, $30, $30, $0E, $00, $00, $14, $1E, $00, $3A, $EB, $6A, $3D, $6C
; $09FF
DEFB $CC, $32, $0E, $00, $00, $02, $00, $00, $2B, $69, $3A, $F4, $74, $2B, $6A, $2D
; $0A0F
DEFB $6C, $2C, $AF, $62, $24, $28, $6A, $29, $3A, $F3, $6A, $3A, $EC, $32, $30, $30
; $0A1F
DEFB $30, $0E, $00, $00, $D0, $07, $00, $0D
; --- BASIC Line 7200: CLS : LET j=l: LET i=3: PRINT "Enter Text"''
; $0A27
DEFB $1C, $20, $22, $00, $FB, $3A, $F1, $6A, $3D, $6C, $3A, $F1, $69, $3D, $33, $0E
; $0A37
DEFB $00, $00, $03, $00, $00, $3A, $F5, $22, $45, $6E, $74, $65, $72, $20, $54, $65
; $0A47
DEFB $78, $74, $22, $27, $27, $0D
; --- BASIC Line 7210: INPUT LINE q$: IF q$="" THEN GO TO 7240
; $0A4D
DEFB $1C, $2A, $18, $00, $EE, $CA, $71, $24, $3A, $FA, $71, $24, $3D, $22, $22, $CB
; $0A5D
DEFB $EC, $37, $32, $34, $30, $0E, $00, $00, $48, $1C, $00, $0D
; --- BASIC Line 7215: IF LEN q$>39 THEN LET E$="Line too long": GO SUB 7800: GO TO 7210
; $0A69
DEFB $1C, $2F, $3A, $00, $FA, $B1, $71, $24, $3E, $33, $39, $0E, $00, $00, $27, $00
; $0A79
DEFB $00, $CB, $F1, $45, $24, $3D, $22, $4C, $69, $6E, $65, $20, $74, $6F, $6F, $20
; $0A89
DEFB $6C, $6F, $6E, $67, $22, $3A, $ED, $37, $38, $30, $30, $0E, $00, $00, $78, $1E
; $0A99
DEFB $00, $3A, $EC, $37, $32, $31, $30, $0E, $00, $00, $2A, $1C, $00, $0D
; --- BASIC Line 7220: LET E$="": GO SUB 7800: PRINT q$: IF q$( LEN q$) <> "#" THEN LET q$=q$+...
; $0AA7
DEFB $1C, $34, $44, $00, $F1, $45, $24, $3D, $22, $22, $3A, $ED, $37, $38, $30, $30
; $0AB7
DEFB $0E, $00, $00, $78, $1E, $00, $3A, $F5, $71, $24, $3A, $FA, $71, $24, $28, $B1
; $0AC7
DEFB $71, $24, $29, $C9, $22, $23, $22, $CB, $F1, $71, $24, $3D, $71, $24, $2B, $C2
; $0AD7
DEFB $31, $33, $0E, $00, $00, $0D, $00, $00, $2B, $61, $24, $28, $6C, $CC, $6A, $29
; $0AE7
DEFB $3A, $F1, $6A, $3D, $6A, $2B, $6C, $0D
; --- BASIC Line 7225: LET b$(i TO i+ LEN q$-l)=q$: LET i=i+ LEN q$: IF i<557 THEN GO TO 7210
; $0AEF
DEFB $1C, $39, $34, $00, $F1, $62, $24, $28, $69, $CC, $69, $2B, $B1, $71, $24, $2D
; $0AFF
DEFB $6C, $29, $3D, $71, $24, $3A, $F1, $69, $3D, $69, $2B, $B1, $71, $24, $3A, $FA
; $0B0F
DEFB $69, $3C, $35, $35, $37, $0E, $00, $00, $2D, $02, $00, $CB, $EC, $37, $32, $31
; $0B1F
DEFB $30, $0E, $00, $00, $2A, $1C, $00, $0D
; --- BASIC Line 7230: LET E$="Buffer Full"
; $0B27
DEFB $1C, $3E, $12, $00, $F1, $45, $24, $3D, $22, $42, $75, $66, $66, $65, $72, $20
; $0B37
DEFB $46, $75, $6C, $6C, $22, $0D
; --- BASIC Line 7240: LET i=i-3: LET b$(l)= CHR$ (i-256* INT (i/256)): LET b$(2)= CHR$ INT (i...
; $0B3D
DEFB $1C, $48, $5B, $00, $F1, $69, $3D, $69, $2D, $33, $0E, $00, $00, $03, $00, $00
; $0B4D
DEFB $3A, $F1, $62, $24, $28, $6C, $29, $3D, $C2, $28, $69, $2D, $32, $35, $36, $0E
; $0B5D
DEFB $00, $00, $00, $01, $00, $2A, $BA, $28, $69, $2F, $32, $35, $36, $0E, $00, $00
; $0B6D
DEFB $00, $01, $00, $29, $29, $3A, $F1, $62, $24, $28, $32, $0E, $00, $00, $02, $00
; $0B7D
DEFB $00, $29, $3D, $C2, $BA, $28, $69, $2F, $32, $35, $36, $0E, $00, $00, $00, $01
; $0B8D
DEFB $00, $29, $3A, $EC, $37, $30, $31, $30, $0E, $00, $00, $62, $1B, $00, $0D
; --- BASIC Line 7300: GO SUB 3100: SAVE q$ DATA b$(): GO TO 7000
; $0B9C
DEFB $1C, $84, $21, $00, $ED, $33, $31, $30, $30, $0E, $00, $00, $1C, $0C, $00, $3A
; $0BAC
DEFB $F8, $71, $24, $E4, $62, $24, $28, $29, $3A, $EC, $37, $30, $30, $30, $0E, $00
; $0BBC
DEFB $00, $58, $1B, $00, $0D
; --- BASIC Line 7400: GO SUB 3100: GO SUB 4400: LOAD q$ DATA b$()
; $0BC1
DEFB $1C, $E8, $21, $00, $ED, $33, $31, $30, $30, $0E, $00, $00, $1C, $0C, $00, $3A
; $0BD1
DEFB $ED, $34, $34, $30, $30, $0E, $00, $00, $30, $11, $00, $3A, $EF, $71, $24, $E4
; $0BE1
DEFB $62, $24, $28, $29, $0D
; --- BASIC Line 7500: POKE 23692,255: CLS : PRINT TAB 8; PAPER 7; INK 2;"Current Message"'': ...
; $0BE6
DEFB $1D, $4C, $6A, $00, $F4, $32, $33, $36, $39, $32, $0E, $00, $00, $8C, $5C, $00
; $0BF6
DEFB $2C, $32, $35, $35, $0E, $00, $00, $FF, $00, $00, $3A, $FB, $3A, $F5, $AD, $38
; $0C06
DEFB $0E, $00, $00, $08, $00, $00, $3B, $DA, $37, $0E, $00, $00, $07, $00, $00, $3B
; $0C16
DEFB $D9, $32, $0E, $00, $00, $02, $00, $00, $3B, $22, $43, $75, $72, $72, $65, $6E
; $0C26
DEFB $74, $20, $4D, $65, $73, $73, $61, $67, $65, $22, $27, $27, $3A, $ED, $37, $37
; $0C36
DEFB $30, $30, $0E, $00, $00, $14, $1E, $00, $3A, $EB, $6A, $3D, $33, $0E, $00, $00
; $0C46
DEFB $03, $00, $00, $CC, $69, $2B, $32, $0E, $00, $00, $02, $00, $00, $0D
; --- BASIC Line 7510: IF b$(j)="#" THEN PRINT "#": GO TO 7540
; $0C54
DEFB $1D, $56, $1C, $00, $FA, $62, $24, $28, $6A, $29, $3D, $22, $23, $22, $CB, $F5
; $0C64
DEFB $22, $23, $22, $3A, $EC, $37, $35, $34, $30, $0E, $00, $00, $74, $1D, $00, $0D
; --- BASIC Line 7520: IF b$(j)= CHR$ 10 THEN GO TO 7540
; $0C74
DEFB $1D, $60, $1D, $00, $FA, $62, $24, $28, $6A, $29, $3D, $C2, $31, $30, $0E, $00
; $0C84
DEFB $00, $0A, $00, $00, $CB, $EC, $37, $35, $34, $30, $0E, $00, $00, $74, $1D, $00
; $0C94
DEFB $0D
; --- BASIC Line 7530: PRINT b$(j);
; $0C95
DEFB $1D, $6A, $08, $00, $F5, $62, $24, $28, $6A, $29, $3B, $0D
; --- BASIC Line 7540: NEXT j: PAUSE 3000: GO TO 7000
; $0CA1
DEFB $1D, $74, $1B, $00, $F3, $6A, $3A, $F2, $33, $30, $30, $30, $0E, $00, $00, $B8
; $0CB1
DEFB $0B, $00, $3A, $EC, $37, $30, $30, $30, $0E, $00, $00, $58, $1B, $00, $0D
; --- BASIC Line 7700: LET i= CODE b$(l)+256* CODE b$(l+l): RETURN
; $0CC0
DEFB $1E, $14, $1F, $00, $F1, $69, $3D, $AF, $62, $24, $28, $6C, $29, $2B, $32, $35
; $0CD0
DEFB $36, $0E, $00, $00, $00, $01, $00, $2A, $AF, $62, $24, $28, $6C, $2B, $6C, $29
; $0CE0
DEFB $3A, $FE, $0D
; --- BASIC Line 7800: LET k= PEEK 23689: GO SUB er: PRINT AT 24-k,o;: RETURN
; $0CE3
DEFB $1E, $78, $26, $00, $F1, $6B, $3D, $BE, $32, $33, $36, $38, $39, $0E, $00, $00
; $0CF3
DEFB $89, $5C, $00, $3A, $ED, $65, $72, $3A, $F5, $AC, $32, $34, $0E, $00, $00, $18
; $0D03
DEFB $00, $00, $2D, $6B, $2C, $6F, $3B, $3A, $FE, $0D
; --- BASIC Line 7900: DATA "Mailbox Message Menu",5,1,"Send Message","Prepare Message","Save M...
; $0D0D
DEFB $1E, $DC, $7A, $00, $E4, $22, $4D, $61, $69, $6C, $62, $6F, $78, $20, $4D, $65
; $0D1D
DEFB $73, $73, $61, $67, $65, $20, $4D, $65, $6E, $75, $22, $2C, $35, $0E, $00, $00
; $0D2D
DEFB $05, $00, $00, $2C, $31, $0E, $00, $00, $01, $00, $00, $2C, $22, $53, $65, $6E
; $0D3D
DEFB $64, $20, $4D, $65, $73, $73, $61, $67, $65, $22, $2C, $22, $50, $72, $65, $70
; $0D4D
DEFB $61, $72, $65, $20, $4D, $65, $73, $73, $61, $67, $65, $22, $2C, $22, $53, $61
; $0D5D
DEFB $76, $65, $20, $4D, $65, $73, $73, $61, $67, $65, $22, $2C, $22, $46, $65, $74
; $0D6D
DEFB $63, $68, $20, $4D, $65, $73, $73, $61, $67, $65, $22, $2C, $22, $44, $69, $73
; $0D7D
DEFB $70, $6C, $61, $79, $20, $4D, $65, $73, $73, $61, $67, $65, $22, $0D
; --- BASIC Line 8000: RANDOMIZE USR o
; $0D8B
DEFB $1F, $40, $04, $00, $F9, $C0, $6F, $0D
; --- BASIC Line 9000: CLS : GO SUB er: RETURN
; $0D93
DEFB $23, $28, $08, $00, $FB, $3A, $ED, $65, $72, $3A, $FE, $0D
; --- BASIC Line 9050: PRINT AT 21,l;" "; AT 21,l; PAPER 6; INK o;E$; AT o,...
; $0D9F
DEFB $23, $5A, $47, $00, $F5, $AC, $32, $31, $0E, $00, $00, $15, $00, $00, $2C, $6C
; $0DAF
DEFB $3B, $22, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20
; $0DBF
DEFB $20, $20, $20, $20, $20, $20, $22, $3B, $AC, $32, $31, $0E, $00, $00, $15, $00
; $0DCF
DEFB $00, $2C, $6C, $3B, $DA, $36, $0E, $00, $00, $06, $00, $00, $3B, $D9, $6F, $3B
; $0DDF
DEFB $45, $24, $3B, $AC, $6F, $2C, $6F, $3B, $3A, $FE, $0D
; --- BASIC Line 9100: GO SUB cl: GO SUB er: RESTORE (1000*mn+900): READ m$: PRINT TAB ((32- L...
; $0DEA
DEFB $23, $8C, $12, $01, $ED, $63, $6C, $3A, $ED, $65, $72, $3A, $E5, $28, $31, $30
; $0DFA
DEFB $30, $30, $0E, $00, $00, $E8, $03, $00, $2A, $6D, $6E, $2B, $39, $30, $30, $0E
; $0E0A
DEFB $00, $00, $84, $03, $00, $29, $3A, $E3, $6D, $24, $3A, $F5, $AD, $28, $28, $33
; $0E1A
DEFB $32, $0E, $00, $00, $20, $00, $00, $2D, $B1, $6D, $24, $29, $2F, $32, $0E, $00
; $0E2A
DEFB $00, $02, $00, $00, $29, $3B, $DA, $37, $0E, $00, $00, $07, $00, $00, $3B, $D9
; $0E3A
DEFB $32, $0E, $00, $00, $02, $00, $00, $3B, $6D, $24, $27, $27, $3A, $E3, $6E, $69
; $0E4A
DEFB $2C, $6F, $73, $3A, $F5, $22, $20, $4B, $45, $59, $20, $20, $20, $46, $55, $4E
; $0E5A
DEFB $43, $54, $49, $4F, $4E, $22, $27, $27, $3A, $EB, $69, $3D, $6F, $CC, $6E, $69
; $0E6A
DEFB $2D, $6C, $3A, $E3, $6D, $24, $3A, $F5, $AD, $28, $6C, $2B, $6C, $29, $3B, $69
; $0E7A
DEFB $3B, $AD, $28, $37, $0E, $00, $00, $07, $00, $00, $29, $3B, $6D, $24, $27, $27
; $0E8A
DEFB $3A, $F3, $69, $3A, $F5, $14, $01, $14, $00, $DD, $6C, $3B, $22, $45, $4E, $54
; $0E9A
DEFB $45, $52, $22, $3B, $3A, $F5, $22, $20, $20, $47, $4F, $54, $4F, $20, $4D, $61
; $0EAA
DEFB $69, $6E, $20, $4D, $65, $6E, $75, $22, $3A, $F1, $6C, $24, $3D, $C2, $31, $37
; $0EBA
DEFB $0E, $00, $00, $11, $00, $00, $2B, $C2, $32, $0E, $00, $00, $02, $00, $00, $2B
; $0ECA
DEFB $22, $20, $4F, $4E, $22, $3A, $FA, $6C, $73, $3D, $6F, $CB, $F1, $6C, $24, $3D
; $0EDA
DEFB $C2, $31, $37, $0E, $00, $00, $11, $00, $00, $2B, $C2, $34, $0E, $00, $00, $04
; $0EEA
DEFB $00, $00, $2B, $C2, $31, $36, $0E, $00, $00, $10, $00, $00, $2B, $C2, $6F, $2B
; $0EFA
DEFB $22, $4F, $46, $46, $22, $0D
; --- BASIC Line 9235: PRINT AT 21,22;"Logged ";l$: RETURN
; $0F00
DEFB $24, $13, $23, $00, $F5, $AC, $32, $31, $0E, $00, $00, $15, $00, $00, $2C, $32
; $0F10
DEFB $32, $0E, $00, $00, $16, $00, $00, $3B, $22, $4C, $6F, $67, $67, $65, $64, $20
; $0F20
DEFB $22, $3B, $6C, $24, $3A, $FE, $0D
; --- BASIC Line 9300: LET E$="": PAUSE o: LET V$= INKEY$ : LET key= CODE V$: IF key=13 THEN ...
; $0F27
DEFB $24, $54, $2C, $00, $F1, $20, $45, $24, $3D, $22, $22, $3A, $F2, $6F, $3A, $F1
; $0F37
DEFB $56, $24, $3D, $A6, $3A, $F1, $6B, $65, $79, $3D, $AF, $56, $24, $3A, $FA, $6B
; $0F47
DEFB $65, $79, $3D, $31, $33, $0E, $00, $00, $0D, $00, $00, $CB, $EC, $6D, $6D, $0D
; --- BASIC Line 9310: IF key=0 THEN GO TO 9300
; $0F57
DEFB $24, $5E, $19, $00, $FA, $6B, $65, $79, $3D, $30, $0E, $00, $00, $00, $00, $00
; $0F67
DEFB $CB, $EC, $39, $33, $30, $30, $0E, $00, $00, $54, $24, $00, $0D
; --- BASIC Line 9330: IF key>47 AND key<(48+ni) THEN GO TO (100+mn*1000+900*(mn=o)+os*100*(ke...
; $0F74
DEFB $24, $72, $68, $00, $FA, $6B, $65, $79, $3E, $34, $37, $0E, $00, $00, $2F, $00
; $0F84
DEFB $00, $C6, $6B, $65, $79, $3C, $28, $34, $38, $0E, $00, $00, $30, $00, $00, $2B
; $0F94
DEFB $6E, $69, $29, $CB, $EC, $28, $31, $30, $30, $0E, $00, $00, $64, $00, $00, $2B
; $0FA4
DEFB $6D, $6E, $2A, $31, $30, $30, $30, $0E, $00, $00, $E8, $03, $00, $2B, $39, $30
; $0FB4
DEFB $30, $0E, $00, $00, $84, $03, $00, $2A, $28, $6D, $6E, $3D, $6F, $29, $2B, $6F
; $0FC4
DEFB $73, $2A, $31, $30, $30, $0E, $00, $00, $64, $00, $00, $2A, $28, $6B, $65, $79
; $0FD4
DEFB $2D, $34, $38, $0E, $00, $00, $30, $00, $00, $29, $29, $0D
; --- BASIC Line 9340: LET E$="Invalid Key": GO SUB er: GO TO 9300
; $0FE0
DEFB $24, $7C, $22, $00, $F1, $45, $24, $3D, $22, $49, $6E, $76, $61, $6C, $69, $64
; $0FF0
DEFB $20, $4B, $65, $79, $22, $3A, $ED, $65, $72, $3A, $EC, $39, $33, $30, $30, $0E
; $1000
DEFB $00, $00, $54, $24, $00, $0D
; --- BASIC Line 9500: POKE t, LEN T$: POKE t+l,o: FOR i=l TO LEN T$: POKE t+i+l, CODE T$(i): ...
; $1006
DEFB $25, $1C, $2A, $00, $F4, $74, $2C, $B1, $54, $24, $3A, $F4, $74, $2B, $6C, $2C
; $1016
DEFB $6F, $3A, $EB, $69, $3D, $6C, $CC, $B1, $54, $24, $3A, $F4, $74, $2B, $69, $2B
; $1026
DEFB $6C, $2C, $AF, $54, $24, $28, $69, $29, $3A, $F3, $69, $3A, $FE, $0D
; --- BASIC Line 9600: LET ix=32640: IF PEEK 23733>127 THEN LET ix=ix+32768
; $1034
DEFB $25, $80, $3B, $00, $F1, $69, $78, $3D, $33, $32, $36, $34, $30, $0E, $00, $00
; $1044
DEFB $80, $7F, $00, $3A, $FA, $BE, $32, $33, $37, $33, $33, $0E, $00, $00, $B5, $5C
; $1054
DEFB $00, $3E, $31, $32, $37, $0E, $00, $00, $7F, $00, $00, $CB, $F1, $69, $78, $3D
; $1064
DEFB $69, $78, $2B, $33, $32, $37, $36, $38, $0E, $00, $00, $00, $80, $00, $0D
; --- BASIC Line 9610: LET str=ix+76: RETURN
; $1073
DEFB $25, $8A, $13, $00, $F1, $73, $74, $72, $3D, $69, $78, $2B, $37, $36, $0E, $00
; $1083
DEFB $00, $4C, $00, $00, $3A, $FE, $0D
DEFB $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
DEFB $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
DEFB $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
DEFB $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
DEFB $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
DEFB $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
DEFB $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $F3, $06
DEFB $C0, $C3, $AF, $0E, $52, $4A, $54
; =============================================================================
; MACHINE CODE SECTION ($1101-$1B1C)
; =============================================================================
; This is the Z80 machine code that handles:
; - ROM initialization and RAM setup
; - 8251 USART control (modem init, transmit, receive)
; - Viewdata/Prestel protocol parsing
; - Teletext/Viewdata display rendering to Spectrum screen
; - Keyboard scanning and input handling
; - Ring buffer management for data I/O
; - Character font rendering (5-column to 8-row transposition)
; =============================================================================
; ---------------------------------------------------------------------------
; COLD START ($1101)
; ---------------------------------------------------------------------------
; Called on power-on via JP $1101 at $0000. Initialises the Spectrum's RAM,
; tests memory size, copies trampolines to high RAM, sets up the BASIC
; program area, and starts the terminal software.
; ---------------------------------------------------------------------------
COLD_START:
DI ; Disable interrupts during setup
LD IY,$5C3A ; IY -> Spectrum system variables (ERR_NR)
LD A,$3F
LD I,A ; Set I=$3F for IM2 interrupt vector table
LD HL,$5C00 ; Start of system variables area
LD DE,$5C01
LD BC,$A3FF ; Clear $A3FF bytes ($5C00 to $FFFF)
LD (HL),$00
LDIR ; Zero all RAM from $5C00 upward
; --- RAM size test ---
LD A,$55 ; Test pattern 1
LD (HL),A ; Write to top of RAM
CP (HL) ; Read back and compare
JR NZ,.ram_16k ; If mismatch, RAM is only 16K
CPL ; Test pattern 2 ($AA)
LD (HL),A
CP (HL)
JR Z,.ram_ok ; If match, full 48K RAM confirmed
.ram_16k:
RES 7,H ; Limit HL to 16K range ($7FFF max)
.ram_ok:
; --- Copy ROM paging trampolines to top of RAM ---
; The trampolines at $1F80-$1FFF handle interrupt routing and ROM paging.
; They must reside in RAM since they switch between VTX and Spectrum ROMs.
LD DE,$1FFF ; Destination: top of ROM address space
LD BC,$0080 ; 128 bytes to copy
EX DE,HL ; HL=dest(top of RAM), DE=$1FFF
LDDR ; Copy $1F80-$1FFF to top of RAM (going down)
; --- Copy character font + splash screen + config to RAM ---
LD BC,$FEC0 ; Offset adjustment
EX DE,HL
ADD HL,BC ; Adjust destination pointer
EX DE,HL
LD BC,$0280 ; 640 bytes (font + splash + config)
LDDR ; Copy $1B00-$1D7F area to RAM below trampolines
; --- Set RAMTOP for BASIC ---
LD HL,$FCA9 ; Offset to calculate BASIC RAMTOP
ADD HL,DE ; HL = safe top for BASIC (below our data)
LD ($5CB4),HL ; Store as P_RAMT (physical RAM top for BASIC)
LD SP,$6000 ; Temporary stack in mid-RAM
; --- Initialise hardware and copy BASIC program ---
CALL GET_IX ; Set IX to device state block
CALL HW_INIT ; Initialise 8251 USART and ports
; --- Copy boot code to RAM and execute ---
; A small loader is copied to $6200 and executed from there. It pages in
; the Spectrum ROM to copy the PRINT/channel routines, then pages back.
LD HL,$1156 ; Source: embedded copy routine in this ROM
LD DE,$6200 ; Destination: safe RAM area
LD BC,$0026 ; 38 bytes of copy code
LDIR ; Copy the loader to RAM
JP $6200 ; Execute from RAM (we can't page ROM while running from it)
; --- RAM-resident boot code (executes at $6200) ---
; This code runs from RAM so it can freely page ROMs
BOOT_FROM_RAM:
LD A,$35 ; 8251 command: RTS=1 -> page in Spectrum ROM
OUT ($FF),A ; Switch to Spectrum ROM
EX (SP),HL ; Bus-cycle delay (let paging settle)
EX (SP),HL
; Copy Spectrum ROM routines to RAM (channel I/O, PRINT routines)
LD HL,$1203 ; Source in Spectrum ROM (output routines)
LD BC,$00AE ; 174 bytes
LDIR ; Copy to RAM (DE continues from previous LDIR)
; Set up JP to our handler in the copied code
EX DE,HL
LD (HL),$C3 ; JP opcode
INC HL
LD DE,$621D ; Target address for our hook
LD (HL),E
INC HL
LD (HL),D
; Restore VTX ROM and continue
LD HL,($5CB4) ; Get P_RAMT
JR .continue_init ; Jump to continue
PAGE_IN_VTX_ROM:
LD A,$15 ; 8251 command: RTS=0 -> page in VTX5000 ROM
OUT ($FF),A ; Switch to VTX ROM
EX (SP),HL
EX (SP),HL
JP .continue_init
; ---------------------------------------------------------------------------
; CONTINUE INIT ($117C)
; ---------------------------------------------------------------------------
.continue_init:
CALL GET_IX ; Refresh IX pointer
CALL CLEAR_SCREEN ; Clear the Spectrum screen
CALL REFRESH_SCREEN ; Render any initial display
; --- Wait for keypress (show splash screen) ---
.wait_key:
CALL KEY_SCAN ; Scan keyboard
OR A ; Any key pressed?
JR Z,.wait_key ; No: keep waiting
; ---------------------------------------------------------------------------
; START BASIC ($118B)
; ---------------------------------------------------------------------------
; Set up the BASIC program environment and begin execution.
; The embedded BASIC program was copied to RAM during COLD_START.
; ---------------------------------------------------------------------------
START_BASIC:
CALL GET_IX ; Set IX to device state block
LD (IX+$06),$60 ; Set device flags: bits 5+6 set
CALL HW_INIT ; Re-initialise hardware
; --- Copy BASIC program from ROM to RAM ---
LD HL,($5C53) ; PROG - start of BASIC program area
LD DE,$003A ; Source: BASIC program in ROM
LD BC,$10C7 ; Size: 4295 bytes
EX DE,HL
LDIR ; Copy BASIC program to RAM
EX DE,HL
; --- Set up BASIC system variables ---
SETUP_BASIC_VARS:
LD ($5C4B),HL ; VARS - variables area starts after program
LD (HL),$80 ; End-of-variables marker
INC HL
LD ($5C59),HL ; E_LINE - edit line
LD (HL),$F7 ; RUN token (auto-run marker)
INC HL
LD (HL),$0D ; CR (end of edit line)
INC HL
LD (HL),$80 ; End marker
INC HL
LD ($5C61),HL ; WORKSP - calculator workspace
LD ($5C63),HL ; STKBOT - calculator stack bottom
LD ($5C65),HL ; STKEND - calculator stack end
; --- Check for BREAK key, then start BASIC ---
LD A,$7F ; Keyboard row: B-N-M-Sym-Space
IN A,($FE) ; Read keyboard
RRA ; Bit 0 = Space key (BREAK)
JR C,.no_break ; If not pressed, continue normally
RST $00 ; BREAK pressed: restart (cold start)
.no_break:
; --- Jump to Spectrum ROM's BASIC interpreter ---
BIT 7,(IY+$7B) ; Check 16K/48K flag
LD HL,$12B4 ; Return address (our BASIC entry continuation)
PUSH HL ; Push return address
JP Z,$7FDA ; 16K: jump to Spectrum ROM via RAM trampoline
JP $FFDA ; 48K: jump to Spectrum ROM via RAM trampoline
; ---------------------------------------------------------------------------
; PRESTEL DATA RECEIVE HANDLER ($11D3)
; ---------------------------------------------------------------------------
; Called from BASIC via the jump table at $0003. Handles incoming Prestel
; data frames. C register selects the operation mode on entry.
; ---------------------------------------------------------------------------
PRESTEL_RECV:
DEC C
JR Z,PRESTEL_MODE5 ; C=1: mode 5
DEC C
JR Z,PRESTEL_MODE5 ; C=2: mode 5 (same handler)
DEC C
JR Z,PRESTEL_RETRY ; C=3: retry/reconnect
DEC C
JR Z,PRESTEL_DELAY ; C=4: delay loop
DEC C
JR Z,PRESTEL_POLL ; C=5: poll for data
; --- C=6+: Save state and start receiving ---
LD A,(IX+$41) ; Get max attempts
LD (IX+$2E),A ; Set attempt counter
LD A,(IY+$19) ; FRAMES counter low (timer)
LD (IX+$2F),A ; Save as data pointer low
LD A,(IY+$1A) ; FRAMES counter high
LD (IX+$30),A ; Save as data pointer high
XOR A
LD (IX+$32),A ; Clear parser flags
LD (IX+$31),A ; Clear shift state
PRESTEL_POLL:
CALL PRESTEL_PARSE ; Parse incoming Prestel data
BIT 1,E ; Check error flag
JR NZ,PRESTEL_RETRY ; Error: retry
LD A,(IX+$2C) ; Get repeat count
BIT 0,E ; Check completion flag
JR NZ,.frame_done ; Frame complete
OR A
JR Z,PRESTEL_MODE5 ; No repeats: go to mode 5
JR .dec_repeat ; Decrement repeat counter
.frame_done:
OR A
JR NZ,PRESTEL_MODE5 ; Still have repeats
LD A,$07 ; Bell character
BIT 4,(IX+$06) ; Check auto-answer mode
JR Z,.set_result ; Not auto-answer: just set result
LD HL,PRESTEL_SOUND2 ; Auto-answer sound data
CALL PLAY_SOUND ; Play confirmation sound
RES 0,(IX+$06) ; Clear online flag
CALL MODEM_SETUP ; Reconfigure modem
XOR A ; Clear A
.set_result:
LD (IX+$0B),A ; Set return code
LD L,(IX+$2F) ; Restore data pointer
LD H,(IX+$30)
JP SETUP_BASIC_VARS ; Re-enter BASIC
PRESTEL_DELAY:
LD H,$FF ; Delay counter
.delay_loop:
DEC HL
LD A,H
OR L
JR NZ,.delay_loop ; Busy-wait loop
PRESTEL_RETRY:
LD HL,PRESTEL_SOUND1 ; Error/retry sound data
DEC (IX+$2E) ; Decrement attempt counter
JR NZ,.do_retry ; Still have attempts left
LD (IX+$0B),$06 ; Return code: retry exhausted
PRESTEL_MODE5:
JP START_BASIC ; Return to BASIC
.dec_repeat:
DEC (IX+$2C) ; Decrement repeat count
LD A,(IX+$41) ; Get max attempts
LD (IX+$2E),A ; Reset attempt counter
LD HL,PRESTEL_SOUND3 ; Sound for next repeat
DEC (IX+$2D) ; Decrement line retry
JR NZ,.do_retry ; Retries left
LD (IX+$2D),$1A ; Reset line retry counter (26)
LD HL,PRESTEL_SOUND4 ; Different sound for line reset
.do_retry:
CALL PLAY_SOUND ; Play the sound
RET
; ---------------------------------------------------------------------------
; PRESTEL FRAME PARSER ($1265)
; ---------------------------------------------------------------------------
; Parses incoming Prestel/Viewdata data from the modem. The Prestel protocol
; uses ESC sequences (|A, |Z, etc.) for control and |<digits> for checksums.
; ---------------------------------------------------------------------------
PRESTEL_PARSE:
PUSH IX ; Get IX as HL
POP HL
LD C,$2F ; Offset to data area
LD B,$00
ADD HL,BC ; HL -> IX+$2F (data pointer area)
LD E,L
LD D,H
LD C,$04 ; 4 bytes to swap
ADD HL,BC
BIT 1,(IX+$32) ; Check parser direction flag
JR NZ,.no_swap
EX DE,HL ; Swap source/dest
.no_swap:
LDIR ; Copy 4 bytes (swap data areas)
LD E,(IX+$32) ; Get parser flags
EXX
LD HL,$0001 ; Initial checksum
CALL CALC_BUF_ADDR ; Calculate buffer address
LD BC,$0370 ; Max bytes to parse (880)
EXX
LD L,(IX+$2F) ; Get data pointer
LD H,(IX+$30)
; --- Main parse loop: look for frame start (|A) ---
.find_pipe:
CALL READ_MODEM_BYTE ; Read a byte from modem
JP NZ,PARSE_DONE ; Timeout or error: done
.check_pipe:
CP $7C ; Is it '|' (pipe)?
JR NZ,.find_pipe ; No: keep looking
CALL READ_MODEM_BYTE ; Read next byte after '|'
JP NZ,PARSE_DONE
CP $41 ; Is it 'A'? (|A = frame start)
JR NZ,.check_pipe ; No: check for another '|'
; --- Frame data found, parse content ---
LD C,$00 ; Clear checksum accumulator
BIT 3,E ; Check if continuing a partial frame
RES 3,E
JR NZ,.parse_end_marker ; If continuing, look for end marker
.parse_data:
CALL READ_MODEM_BYTE ; Read next byte
JP NZ,PARSE_DONE ; Error/timeout
CP $7C ; Pipe character?
JR Z,.parse_end_marker ; Yes: check for end marker
LD B,(IX+$31) ; Get current shift state
CP $7D ; Right brace?
JR NZ,.store_char ; No: store character
LD A,B ; Get shift state
OR A
LD A,$7D ; Restore character
JR NZ,.store_char ; If shifted, store as-is
LD A,$20 ; Unshifted '}' -> space
.store_char:
ADD A,B ; Apply shift offset
.check_store:
BIT 2,E ; Check if storage enabled
JR Z,.parse_data ; Storage disabled: skip
BIT 4,E ; Check error/ignore flag
JR NZ,.parse_data ; Ignoring data: skip
LD (HL),A ; Store byte in buffer
INC HL
JR .parse_data ; Continue parsing
; --- End marker processing ---
.parse_end_marker:
CALL READ_MODEM_BYTE ; Read byte after '|'
CP $41 ; 'A' = new frame start
JP Z,PARSE_ERROR ; Unexpected frame start
CP $5A ; 'Z' = frame end
JR Z,.verify_checksum
CP $7C ; Another '|'?
JR NZ,.check_control ; No: check other controls
CALL READ_MODEM_BYTE ; Read after '||'
CP $5A ; 'Z'?
JR NZ,.set_error ; No: error
SET 3,E ; Mark partial frame
JR .verify_checksum
.check_control:
CP $49 ; 'I' = ignore following data
JR NZ,.check_digit ; No: check for digit
RES 4,E ; Clear ignore flag (enable receiving)
JR .parse_data
.check_digit:
BIT 4,E ; Currently ignoring?
JR NZ,.parse_data ; Yes: skip
CP $36 ; >= '6'?
JR NC,.check_F ; Yes: check for 'F', 'L', 'E' commands
SUB $30 ; Convert digit to number
JR C,.check_F ; Not a digit
CP $01 ; Is it '1'?
JR NZ,.set_shift ; No: use directly
LD A,$06 ; '1' maps to shift 6
.set_shift:
RRCA ; Rotate to get shift value
RRCA
RRCA
LD (IX+$31),A ; Set shift state
JR .parse_data
.check_F:
CP $46 ; 'F' = frame complete
JR NZ,.check_L
SET 0,E ; Set completion flag
JR .parse_data
.check_L:
CP $4C ; 'L' = length/repeat count
JR NZ,.check_E
BIT 2,E ; Storage enabled?
JR NZ,.parse_CR ; Yes: insert CR
CALL READ_3_DIGITS ; Read 3-digit number
LD (IX+$2C),A ; Store as repeat count
CALL READ_MODEM_BYTE ; Read next byte
CP $7C ; Should be '|'
JR NZ,PARSE_ERROR
CALL READ_MODEM_BYTE
CP $5A ; Should be 'Z'
JR NZ,PARSE_ERROR
JR .verify_checksum
.parse_CR:
LD A,$0D ; CR character
JR .check_store ; Store it
.check_E:
CP $45 ; 'E' = escape (literal pipe)
JR NZ,.check_brace
LD A,$7C ; Store literal '|'
JR .check_store
.check_brace:
CP $7D ; '}' character
JR Z,.check_store ; Store it directly
.set_error:
SET 4,E ; Set ignore/error flag
JP .parse_data
.verify_checksum:
PUSH BC
CALL READ_3_DIGITS ; Read 3-digit checksum
POP BC
XOR $26 ; XOR with checksum key
CP C ; Compare with accumulated checksum
JR NZ,PARSE_ERROR ; Mismatch: error
SET 2,E ; Mark data as valid
JR PARSE_DONE
PARSE_ERROR:
SET 1,E ; Set error flag
PARSE_DONE:
LD (IX+$2F),L ; Save data pointer
LD (IX+$30),H
LD (IX+$32),E ; Save parser flags
RET
; ---------------------------------------------------------------------------
; READ MODEM BYTE ($1363)
; ---------------------------------------------------------------------------
; Reads one byte from the 8251 via the ring buffer. Maintains checksum.
; Returns byte in A, Z flag clear if timeout/error.
; ---------------------------------------------------------------------------
READ_MODEM_BYTE:
EXX
DEC BC ; Decrement byte counter
LD A,B
OR C ; Check if counter exhausted
LD A,(DE) ; Read byte from buffer
INC DE ; Advance buffer pointer
EXX
JR NZ,.byte_ok ; Counter not zero: byte valid
SET 1,E ; Counter zero: set error flag
.byte_ok:
LD D,A ; Save byte
XOR C ; XOR into running checksum
LD C,A ; Update checksum
LD A,D ; Restore byte
BIT 1,E ; Test error flag (sets Z if no error)
RET
; ---------------------------------------------------------------------------
; READ 3 DIGITS ($1375)
; ---------------------------------------------------------------------------
; Reads a 3-digit decimal number from the modem stream.
; Returns value in A. Used for repeat counts and checksums.
; ---------------------------------------------------------------------------
READ_3_DIGITS:
LD B,$03 ; 3 digits to read
PUSH HL
XOR A ; Clear accumulator
.digit_loop:
LD L,A ; Save partial result
CALL READ_MODEM_BYTE ; Read a digit
SUB $30 ; Convert ASCII to value
JR NC,.digit_ok
SET 1,E ; Not a digit: error
.digit_ok:
CP $0A ; > 9?
JR C,.digit_valid
SET 1,E ; Invalid digit: error
.digit_valid:
ADD A,L ; Add to partial result
LD L,A
DEC B
JR Z,.digits_done ; All 3 digits read
ADD A,A ; Multiply by 10: A*2
ADD A,A ; A*4
ADD A,L ; A*5
.multiply:
ADD A,A ; A*10
JR .digit_loop
.digits_done:
POP HL
RET
; ---------------------------------------------------------------------------
; PLAY SOUND ($1396)
; ---------------------------------------------------------------------------
; Plays a sequence of beeps. HL points to a data table:
; First byte = count of note pairs
; Then pairs of (duration, pitch) bytes
; Uses Spectrum ROM's BEEP routine via jump table at $0012.
; ---------------------------------------------------------------------------
PLAY_SOUND:
LD B,(HL) ; B = number of note pairs
.sound_loop:
INC HL
LD A,(HL) ; A = note data
PUSH BC
PUSH HL
LD B,$02 ; Duration multiplier
CALL RINGBUF_WRITE ; Output via ring buffer (to BEEP)
POP HL
POP BC
DJNZ .sound_loop
RET
; --- Sound data tables ---
PRESTEL_SOUND1:
DEFB $03, $2A, $30, $30 ; 3 notes: retry/error sound
PRESTEL_SOUND2:
DEFB $05, $2A, $39, $30 ; 5 notes: auto-answer confirmation
DEFB $23, $23
PRESTEL_SOUND3:
DEFB $01, $23, $01, $30 ; 1 note: repeat sound
PRESTEL_SOUND4:
; (falls through from above data)
; ---------------------------------------------------------------------------
; GET IX - Set IX to device state block ($13B3)
; ---------------------------------------------------------------------------
GET_IX:
LD HL,$7F80 ; Default: 16K Spectrum state block
BIT 7,(IY+$7B) ; Check 16K/48K flag
RET Z ; 16K: return with HL=$7F80
LD IX,$FF80 ; 48K: state block at top of 48K RAM
RET
; ---------------------------------------------------------------------------
; ROM CALL HANDLER ($13C1)
; ---------------------------------------------------------------------------
; Called via JP $13C1 at $0006. Provides a way to call Spectrum ROM routines
; from code running in the VTX ROM. Pages to RAM trampoline which handles
; the ROM switch.
; ---------------------------------------------------------------------------
ROM_CALL:
BIT 7,(IY+$7B) ; Check 16K/48K
JP Z,$7FC9 ; 16K: trampoline in 16K RAM
JP $FFC9 ; 48K: trampoline in 48K RAM
; Padding
DEFB $FF, $FF, $FF, $FF, $FF
; ---------------------------------------------------------------------------
; MODEM POLL LOOP ($13D0)
; ---------------------------------------------------------------------------
; Main entry point for the modem polling loop. Called from jump table ($000C).
; Saves alternate register set, runs the poll, then restores.
; ---------------------------------------------------------------------------
MODEM_POLL:
EXX ; Save alternate registers
PUSH BC
PUSH DE
PUSH HL
EXX
CALL MODEM_POLL_INNER ; Run the actual poll loop
EXX ; Restore alternate registers
POP HL
POP DE
POP BC
EXX
RET
; ---------------------------------------------------------------------------
; MODEM POLL INNER ($13DE)
; ---------------------------------------------------------------------------
MODEM_POLL_INNER:
CALL HW_INIT ; Initialise hardware
LD A,(IX+$06) ; Get device flags
AND $02 ; Check echo mode flag
JR NZ,MODEM_MAIN_LOOP ; Echo mode: skip init
CALL CLEAR_SCREEN ; Clear screen
CALL REFRESH_SCREEN ; Refresh display
LD C,$06 ; Command code
BIT 0,(IX+$06) ; Check online flag
.call_if_online:
CALL NZ,PRESTEL_RECV ; If online, start receiving
CALL UART_INIT ; Initialise UART
; ---------------------------------------------------------------------------
; MODEM MAIN LOOP ($13FA)
; ---------------------------------------------------------------------------
MODEM_MAIN_LOOP:
LD (IX+$0B),$00 ; Clear return code
CALL CHECK_TX_READY ; Check if we can transmit
CALL CHECK_RX_DATA ; Check for received data
LD B,$04 ; Buffer type 4
CALL RINGBUF_READ ; Read from ring buffer
JR Z,.no_rx_data ; No data available
CALL PRESTEL_DETECT ; Check for Prestel frame markers
CALL PROCESS_RX_CHAR ; Process received character
.no_rx_data:
CALL SCAN_KEYBOARD ; Check keyboard
LD C,(IX+$0B) ; Get return code
LD A,C
OR A
JR Z,MODEM_MAIN_LOOP ; Code 0: continue looping
LD B,$00
BIT 0,(IX+$06) ; Check online flag
JR NZ,.call_if_online ; Online: call Prestel handler
RET ; Offline: return to caller
; ---------------------------------------------------------------------------
; HARDWARE INIT ($1424)
; ---------------------------------------------------------------------------
; Initialises the 8251 USART and VTX5000 hardware.
; Resets the modem chip, configures baud rate and data format,
; and clears the receive buffer.
; ---------------------------------------------------------------------------
HW_INIT:
LD A,$82 ; 8251: hunt mode + DTR
OUT ($FF),A ; Write to 8251 command register
RES 5,(IY+$01) ; Clear FLAGS bit 5 (new key available)
LD A,$40 ; 8251: internal reset
OUT ($FF),A ; Reset the 8251
BIT 7,(IX+$06) ; Check 16K flag
NOP ; (padding for timing)
NOP
XOR A
OUT ($FE),A ; Set border to black
LD A,(IX+$07) ; Get configured mode register value
OUT ($FF),A ; Write mode to 8251
LD (IX+$1B),$64 ; Set idle timeout to 100
RES 1,(IX+$09) ; Clear input flag
; ---------------------------------------------------------------------------
; UART INIT ($1446)
; ---------------------------------------------------------------------------
; Sends the command register value and clears the receive buffer.
; ---------------------------------------------------------------------------
UART_INIT:
LD A,(IX+$08) ; Get configured command register value
OUT ($FF),A ; Write command to 8251
LD B,$04 ; Ring buffer type 4
CALL RINGBUF_GETPTR ; Get ring buffer pointer
IN A,($7F) ; Read and discard any pending data
XOR A
LD (HL),A ; Clear buffer count
LD HL,$0258 ; Default timeout: 600 cycles
IN A,($7F) ; Read and discard again
LD A,$FF ; Default buffer size: 255
BIT 0,(IX+$06) ; Check online mode
JR Z,.set_buf_params ; Offline: use defaults
LD A,$10 ; Online: buffer size = 16
LD HL,$0010 ; Online: timeout = 16
.set_buf_params:
LD (IX+$04),A ; Store buffer size
LD (IX+$02),L ; Store timeout low
LD (IX+$03),H ; Store timeout high
; ---------------------------------------------------------------------------
; SET CARRIER TIMEOUT ($146F)
; ---------------------------------------------------------------------------
SET_CARRIER_TIMEOUT:
LD L,(IX+$42) ; Online timeout low
LD H,(IX+$43) ; Online timeout high
BIT 0,(IX+$06) ; Online mode?
JR NZ,.set_timeout ; Yes: use online timeout
LD L,(IX+$44) ; Offline timeout low
LD H,(IX+$45) ; Offline timeout high
BIT 4,(IX+$06) ; Auto-answer mode?
JR NZ,.set_timeout ; Yes: use offline timeout
LD HL,$61A8 ; Default: 25000 cycles
.set_timeout:
LD (IX+$19),L ; Store carrier timeout low
INC H ; Increment high (adds 256)
LD (IX+$1A),H ; Store carrier timeout high
RET
; ---------------------------------------------------------------------------
; KEYBOARD SCAN ($1492)
; ---------------------------------------------------------------------------
SCAN_KEYBOARD:
LD B,(IY+$3E) ; Get RASP/PIP system variable
LD A,(IX+$18) ; Get previous scan result
LD (IX+$18),B ; Store new scan result
CP B ; Compare with previous
RET Z ; No change: return
PUSH BC
XOR A
BIT 7,(IX+$06) ; Check 16K flag
CALL Z,ROM_CALL ; Call Spectrum ROM (for key decode)
LD B,$02
OR A
CALL NZ,RINGBUF_WRITE ; Write key to ring buffer if valid
POP BC
LD A,(IX+$06) ; Get device flags
AND $22 ; Check echo + another flag
JR NZ,.skip_timeout_check ; Flags set: skip timeout check
; --- Check timeouts ---
DEC (IX+$1B) ; Decrement idle timeout
JR NZ,.check_carrier ; Not zero: continue
LD (IX+$0B),$02 ; Idle timeout: return code 2
.check_carrier:
DEC (IX+$19) ; Decrement carrier timeout low
JR NZ,.skip_timeout_check ; Not zero: continue
DEC (IX+$1A) ; Decrement carrier timeout high
JR NZ,.skip_timeout_check ; Not zero: continue
LD (IX+$0B),$03 ; Carrier lost: return code 3
.skip_timeout_check:
; --- Update cursor display ---
LD L,(IX+$0C) ; Current cursor row
LD H,(IX+$0D) ; Current cursor column
CALL CALC_BUF_ADDR ; Calculate screen address
BIT 2,(IX+$09) ; Cursor blink state
RET Z
LD A,B ; Timer value
AND $0F ; Mask lower 4 bits
RET NZ ; Not time to blink yet
BIT 4,B ; Check blink phase
JR NZ,.cursor_off ; Phase 1: cursor off
; --- Cursor on ---
LD A,(DE) ; Get character at cursor position
LD C,A
SET 3,(IX+$09) ; Mark cursor as visible
CALL RENDER_CHAR_FULL ; Render cursor character (inverted)
RET
.cursor_off:
LD C,$7F ; Solid block character
EXX
LD HL,$4700 ; White INK on black PAPER attributes
EXX
CALL CALC_SCREEN_ADDR ; Calculate screen bitmap address
CALL RENDER_GLYPH ; Render the cursor block
RET
; ---------------------------------------------------------------------------
; CHECK TX READY ($14F9)
; ---------------------------------------------------------------------------
; Checks if the 8251 transmitter is ready and sends a byte from the
; transmit ring buffer if available.
; ---------------------------------------------------------------------------
CHECK_TX_READY:
LD A,(IX+$06)
AND $42 ; Check if TX enabled
RET NZ ; TX not enabled: return
IN A,($FF) ; Read 8251 status
BIT 0,A ; TxRDY?
RET Z ; Not ready: return
LD B,$02 ; Ring buffer type 2
CALL RINGBUF_READ ; Read from TX buffer
RET Z ; No data: return
CP $23 ; '#' hash character?
JR NZ,.not_hash
LD A,$5F ; Replace hash with underscore (UK keyboard)
.not_hash:
CP $04 ; CTRL+D?
JR .send_byte
LD (IX+$06),$89 ; Set flags for disconnect
CALL UART_INIT ; Reinit UART
LD (IX+$0B),$06 ; Return code: disconnect
RET
.send_byte:
OUT ($7F),A ; Transmit byte via 8251 data register
RET
; ---------------------------------------------------------------------------
; CHECK RX DATA ($1523)
; ---------------------------------------------------------------------------
; Checks the 8251 for received data and processes it.
; ---------------------------------------------------------------------------
CHECK_RX_DATA:
BIT 5,(IX+$06) ; TX busy flag set?
RET NZ ; Yes: skip RX check
IN A,($FF) ; Read 8251 status
BIT 7,A ; DSR (Data Set Ready)?
RET Z ; No DSR: modem not connected
LD (IX+$1B),$64 ; Reset idle timeout (modem is alive)
BIT 1,A ; RxRDY (receive data ready)?
RET Z ; No data: return
BIT 3,A ; Parity error?
IN A,($7F) ; Read received byte from 8251
JR Z,.no_parity_err ; No parity error: use byte as-is
LD A,(IX+$08) ; Get command register value
OUT ($FF),A ; Rewrite command (clears error)
LD A,$FF ; Replace errored byte with $FF
.no_parity_err:
PUSH BC
PUSH DE
PUSH HL
LD B,$04 ; Ring buffer type 4 (RX buffer)
CALL RINGBUF_WRITE ; Write received byte to buffer
CALL SET_CARRIER_TIMEOUT ; Reset carrier timeout
POP HL
POP DE
POP BC
RET
; ---------------------------------------------------------------------------
; KEYBOARD INPUT ($1550)
; ---------------------------------------------------------------------------
; Scans the keyboard and returns the pressed key.
; Handles BREAK (CAPS+SPACE), Symbol Shift combinations, and cursor keys.
; Returns character in A (0 = no key).
; Called via jump table at $0021.
; ---------------------------------------------------------------------------
KEY_SCAN:
LD E,(IX+$09) ; Get input state flags
LD A,$FE ; Keyboard row: CAPS-Z-X-C-V
IN A,($FE) ; Read keyboard
LD D,A ; Save scan result
RRA ; Bit 0: CAPS SHIFT
JR C,.no_caps ; Not pressed
LD A,$BF ; Keyboard row: H-J-K-L-ENTER
IN A,($FE)
RRA ; Bit 0: ENTER... wait, $BF is actually B-N-M-Sym-Space
JR C,.no_caps ; Actually: check SYMBOL SHIFT + CAPS SHIFT = BREAK
LD (IX+$0B),$01 ; Set return code: BREAK pressed
JR .key_done
.no_caps:
BIT 5,(IY+$01) ; FLAGS bit 5: new key available?
JR Z,.check_key_held ; No new key: check for held keys
RES 1,(IX+$06) ; Clear echo flag
LD A,(IY-$32) ; Get key value from K_DATA
RES 5,(IY+$01) ; Clear "new key" flag
RES 5,E ; Clear shift state
CP $0E ; Symbol Shift?
JR NZ,.check_key_06
SET 5,E ; Set shift state
SET 6,E ; Set another shift flag
JR .key_done
.check_key_06:
CP $06 ; CAPS LOCK?
JR NZ,.check_key_07
LD A,(IY+$30) ; Get border colour (BORDCR)
XOR $08 ; Toggle bit 3 (bright)
LD (IY+$30),A ; Store back (visual feedback)
JR .key_done
.check_key_07:
CP $07 ; EDIT key?
JR NZ,.normal_key
LD A,E
XOR $02 ; Toggle bit 1
LD E,A
LD (IX+$09),E
CALL REFRESH_SCREEN ; Refresh screen
JR .key_done
; --- Check for held key repeat ---
.check_key_held:
BIT 5,E ; Shift held?
JR NZ,.key_done ; Yes: ignore repeats
LD A,D ; Get original scan
RRA ; Check CAPS SHIFT
JR NC,.key_done ; CAPS held: ignore
LD A,$7F ; Row: B-N-M-Sym-Space
IN A,($FE)
BIT 1,A ; Check Symbol Shift
JR Z,.sym_not_held
RES 6,E ; Clear Symbol Shift state
JR .key_done
.sym_not_held:
BIT 6,E ; Was Symbol Shift previously pressed?
JR NZ,.key_done ; Yes: ignore (debounce)
SET 6,E ; Set Symbol Shift state
LD A,$2A ; '*' character (star key for Prestel)
.normal_key:
CP $0D ; ENTER?
JR NZ,.not_enter
LD A,$5F ; Replace ENTER with '_' (underscore for Prestel)
.not_enter:
CP $0C ; DELETE?
JR NZ,.not_delete
LD A,$08 ; Replace with backspace
.not_delete:
BIT 2,(IX+$06) ; Keyboard locked?
JR Z,.return_key
.key_done:
XOR A ; Return 0 (no key)
.return_key:
LD (IX+$09),E ; Save input state
RET
; ---------------------------------------------------------------------------
; RING BUFFER WRITE ($15D6)
; ---------------------------------------------------------------------------
; Writes byte A to ring buffer type B.
; ---------------------------------------------------------------------------
RINGBUF_WRITE:
CALL RINGBUF_GETPTR ; Get buffer pointer/count
INC BC ; Increment count
LD (HL),C ; Store count low
INC HL
LD (HL),B ; Store count high
ADD HL,BC ; Point to write position
LD (HL),A ; Write byte
RET
; ---------------------------------------------------------------------------
; RING BUFFER READ ($15E0)
; ---------------------------------------------------------------------------
; Reads a byte from ring buffer type B. Returns byte in A, Z flag if empty.
; ---------------------------------------------------------------------------
RINGBUF_READ:
CALL RINGBUF_GETPTR ; Get buffer pointer/count
LD A,B
OR C ; Check if buffer empty
RET Z ; Empty: return Z
DEC BC ; Decrement count
LD (HL),C ; Store new count low
INC HL
LD (HL),B ; Store new count high
INC HL
LD A,(HL) ; Read first byte from buffer
INC BC ; Restore count for shift
LD E,L ; Set up for LDIR (shift buffer down)
LD D,H
INC HL
LDIR ; Shift remaining bytes down by 1
RET
; ---------------------------------------------------------------------------
; RING BUFFER GET POINTER ($15F3)
; ---------------------------------------------------------------------------
; Calculates the address of ring buffer B within the IX state block.
; Returns HL pointing to the buffer, BC = current byte count.
; Buffer B=2: TX buffer, B=4: RX buffer
; ---------------------------------------------------------------------------
RINGBUF_GETPTR:
PUSH AF
LD A,B
RRCA ; A = buffer index / 2
INC A ; Add 1 for header offset
PUSH IX
POP DE ; DE = IX (state block base)
LD L,E
LD H,D
.find_buf:
EX DE,HL
LD C,(HL) ; Read buffer count low
INC HL
LD B,(HL) ; Read buffer count high
INC HL
EX DE,HL
OR A
SBC HL,BC ; Subtract buffer size to find next buffer
DEC A
JR NZ,.find_buf ; Loop until we find buffer B
POP AF
LD C,(HL) ; Read final count
INC HL
LD B,(HL)
DEC HL
RET
; ---------------------------------------------------------------------------
; PROCESS RECEIVED CHARACTER ($160E)
; ---------------------------------------------------------------------------
; Processes a character received from the modem. Handles Viewdata control
; codes (cursor movement, colours, clear screen, etc.) and printable
; characters.
; ---------------------------------------------------------------------------
PROCESS_RX_CHAR:
AND $7F ; Strip parity bit
LD C,A ; Save character
CP $1B ; ESC?
JR NZ,.not_esc
SET 0,(IX+$09) ; Set ESC flag
RET
.not_esc:
CP $05 ; ENQ (enquiry)?
JR NZ,.check_printable
RES 1,(IX+$06) ; Clear echo mode
RET
.check_printable:
PUSH BC
LD B,$00
CALL .skip_timeout_check ; Update cursor position tracking
POP BC
LD A,C
CP $20 ; Printable character? (>= space)
JR NC,.printable_char ; Yes: display it
; --- Control character handling ---
CP $08 ; Backspace?
JR NZ,.not_bs
DEC H ; Move cursor left
JR .update_cursor
.not_bs:
LD (IX+$0E),$80 ; Mark line as dirty
CP $0D ; Carriage Return?
JR NZ,.not_cr
LD H,$00 ; Column = 0
.not_cr:
CP $09 ; Cursor Right (HT)?
JR NZ,.not_right
INC H ; Move cursor right
.not_right:
CP $0B ; Cursor Up (VT)?
JR NZ,.not_up
DEC L ; Move cursor up
.not_up:
CP $0A ; Cursor Down (LF)?
JR NZ,.not_down
INC L ; Move cursor down
.not_down:
CP $1E ; Cursor Home (RS)?
JR NZ,.not_home
LD HL,$0000 ; Home position: row 0, col 0
.not_home:
CP $0C ; Form Feed (clear screen)?
JR NZ,.not_ff
RES 1,(IX+$09) ; Clear receiving flag
CALL CLEAR_DISPLAY_BUF ; Clear display buffer
CALL CLEAR_SCREEN ; Clear Spectrum screen
LD HL,$0000 ; Home cursor
LD A,$14 ; Fall through as cursor-off command
.not_ff:
CP $11 ; DC1 (cursor on)?
JR NZ,.not_dc1
SET 2,(IX+$09) ; Enable cursor display
.not_dc1:
CP $14 ; DC4 (cursor off)?
JR NZ,.update_cursor
RES 2,(IX+$09) ; Disable cursor display
JR .update_cursor
; --- Printable character display ---
.printable_char:
BIT 0,(IX+$09) ; ESC flag set?
RES 0,(IX+$09) ; Clear ESC flag
JR Z,.no_esc_transform
RES 6,C ; If ESC was set, clear bit 6 (convert to control code)
.no_esc_transform:
RES 3,(IX+$09) ; Clear dirty flag
CALL RENDER_CHAR_FULL ; Render the character with attributes
EX DE,HL
LD A,(HL) ; Get previous character at this position
LD (HL),C ; Store new character in display buffer
EX DE,HL
INC H ; Advance cursor column
CP $0D ; Was previous char a CR?
JR NZ,.update_cursor ; No: just update cursor
BIT 2,(IX+$11) ; Check double-height flag
JR NZ,.update_cursor ; Double-height: don't auto-wrap
LD A,$17 ; Row 23 (last row)
CP L ; At bottom?
JR Z,.update_cursor ; Yes: don't scroll
CALL CHECK_LINE_WRAP ; Check if line needs wrapping
JR NZ,.update_cursor
INC L ; Move to next row
CALL SCROLL_LINE ; Scroll if needed
DEC L
; --- Cursor position bounds checking ---
.update_cursor:
LD A,$27 ; Max column = 39
BIT 7,H ; Column negative?
JR Z,.col_ok
LD H,A ; Wrap to rightmost column
DEC L ; Move up a row
.col_ok:
CP H ; Column > 39?
JR NC,.col_bounded
LD H,$00 ; Wrap to column 0
INC L ; Move to next row
.col_bounded:
LD A,$17 ; Max row = 23
BIT 7,L ; Row negative?
JR Z,.row_ok
LD L,A ; Wrap to bottom row
.row_ok:
CP L ; Row > 23?
JR NC,.row_bounded
LD L,$00 ; Wrap to top row
.row_bounded:
LD (IX+$0C),L ; Store cursor row
LD (IX+$0D),H ; Store cursor column
RET
; ---------------------------------------------------------------------------
; RENDER CHARACTER WITH ATTRIBUTE HANDLING ($16CD)
; ---------------------------------------------------------------------------
RENDER_CHAR_FULL:
BIT 7,(IX+$06) ; Check 16K flag
NOP ; (placeholder)
DEC L ; Check previous line
CALL CHECK_LINE_WRAP
INC L
OR A
RET NZ ; Previous line wrapped: skip
LD A,C ; Get character
CP $0D ; Is it CR?
JR NZ,.render_normal
CALL CHECK_LINE_WRAP ; Check current line
JR NZ,.render_normal
SET 4,(IX+$09) ; Set line-complete flag
LD A,$17 ; Last row
CP L
JR Z,.render_normal ; At bottom: don't scroll
INC L
CALL CLEAR_CHAR_LINE ; Clear next line
DEC L
.render_normal:
CALL UPDATE_LINE_ATTRS ; Update line attributes
CALL RENDER_CHAR_LINE ; Render the character line
RET
; (Additional subroutines for CHECK_LINE_WRAP, UPDATE_LINE_ATTRS,
; RENDER_CHAR_LINE, SCROLL_LINE follow with similar patterns)
; ... (abbreviated for space - full code continues in ROM)
; ---------------------------------------------------------------------------
; CALC SCREEN ADDRESS ($1907)
; ---------------------------------------------------------------------------
; Calculates the Spectrum screen bitmap address for row H, using the
; standard Spectrum screen layout (thirds, character rows, pixel lines).
; Also calculates the attribute address.
; Returns: DE = screen bitmap address, attribute address in alternate regs
; ---------------------------------------------------------------------------
CALC_SCREEN_ADDR:
PUSH AF
PUSH HL
LD A,H ; Get row
ADD A,A ; ×2
ADD A,H ; ×3
LD H,A
AND $03 ; Extract pixel line within character
XOR $03 ; Invert (Spectrum screen is upside-down within chars)
LD B,A
LD A,H
LD H,$00
ADD HL,HL ; ×2
ADD HL,HL ; ×4
ADD HL,HL ; ×8
ADD HL,HL ; ×16
ADD HL,HL ; ×32 (32 bytes per character row)
LD DE,$5800 ; Attribute file base
ADD HL,DE ; HL = attribute address
RRA ; Calculate screen row
OR A
RRA
ADD A,L
LD L,A
EX DE,HL ; DE = attribute address
POP HL
POP AF
RET
; ---------------------------------------------------------------------------
; RENDER GLYPH ($194B)
; ---------------------------------------------------------------------------
; Renders a character glyph to the Spectrum screen. The font is stored
; in 5-column format and must be transposed to 8-row format for display.
; ---------------------------------------------------------------------------
RENDER_GLYPH:
PUSH HL
PUSH IX
LD A,L ; Get character row portion
AND $18 ; Extract screen third (0, 8, 16)
OR $40 ; Add screen base ($4000)
LD D,A ; D = high byte of screen address
CALL SETUP_FONT_RENDER ; Load font data and prepare masks
LD IX,RENDER_TABLE ; Rendering jump table
LD HL,$03F0 ; Pixel mask pattern
LD A,B
LD B,$02 ; Pixel column counter
OR A
JR Z,.render_start
.adjust_columns:
DEC B
DEC B
ADD HL,HL ; Shift mask
ADD HL,HL
DEC A
JR NZ,.adjust_columns
.render_start:
EX DE,HL ; DE = mask, HL = screen address
LD A,B
AND $07
LD C,A
LD B,$00
ADD IX,BC ; Adjust render table pointer
CALL .do_render ; Render first half
; --- Check for double-width rendering ---
EXX
BIT 2,L ; Double-width flag?
EXX
JR Z,.render_done
; --- Render second column (for wide characters) ---
LD A,L
ADD A,$20 ; Next character cell
LD L,A
LD A,H
JR C,.carry
SUB $08 ; Adjust for screen third boundary
.carry:
CP $58 ; Past attribute area?
JR NC,.render_done ; Yes: stop
LD H,A
CALL .do_render ; Render second column
.render_done:
POP IX
POP HL
RET
; --- Pixel row rendering ---
.do_render:
EXX
LD A,(BC) ; Get font row data
INC BC
EXX
JP (IX) ; Jump to render routine for this column position
; Rendering routines for different column alignments (RLCA shifts)
RENDER_TABLE:
RLCA
RLCA
RLCA
RLCA
RLCA
RLCA
; (Each entry shifts the font byte to the correct column position,
; then masks and ORs it into the screen byte)
LD C,A
AND D ; Mask with pattern
LD B,A
LD A,D
CPL ; Invert mask
AND (HL) ; Clear old pixels
OR B ; OR in new pixels
LD (HL),A ; Write to screen
BIT 4,D
JR NZ,.next_row
INC L ; Next byte in row
LD A,C
AND E ; Second mask
LD B,A
LD A,E
CPL
AND (HL)
OR B
LD (HL),A
DEC L
.next_row:
INC H ; Next pixel row
LD A,H
AND $07
JR NZ,.do_render ; Continue until 8 rows done
RET
; ---------------------------------------------------------------------------
; SETUP FONT RENDER ($19BB)
; ---------------------------------------------------------------------------
; Loads the 5-byte font definition for character C and transposes it from
; column format to row format for screen rendering. The 5 column bytes
; are rotated bit-by-bit to create 8 row bytes, stored in the IX work area.
; ---------------------------------------------------------------------------
SETUP_FONT_RENDER:
PUSH BC
PUSH DE
LD A,C ; Character to render
LD HL,$001C ; Offset within state block for font work area
PUSH IX
POP BC
ADD HL,BC ; HL -> work area
LD C,L
LD B,H
PUSH BC ; Save work area pointer
EXX
PUSH DE ; Save alternate DE
; --- Check for mosaic graphics character ---
BIT 5,A ; Bit 5 set?
JR Z,.text_char ; No: it's a text character
BIT 4,L ; Check additional flag
JR NZ,.mosaic_char ; Mosaic graphics mode
; --- Text character: load from font table ---
.text_char:
PUSH HL
SUB $20 ; Subtract $20 (font starts at space)
LD C,A
LD B,$00
LD L,(IX+$46) ; Font base address low
LD H,(IX+$47) ; Font base address high
ADD HL,BC ; HL + char * 5
ADD HL,BC
ADD HL,BC
ADD HL,BC
ADD HL,BC ; HL now points to 5-byte font definition
; --- Transpose 5 columns to 8 rows ---
LD C,(HL) ; Column 1
INC HL
LD B,(HL) ; Column 2
INC HL
LD E,(HL) ; Column 3
INC HL
LD D,(HL) ; Column 4
INC HL
LD L,(HL) ; Column 5
LD H,B ; H = Column 2
LD B,$08 ; 8 rows to generate
.transpose_loop:
XOR A ; Clear accumulator
RR C ; Rotate column 1, bit into carry
RLA ; Carry into A bit 0
RR H ; Rotate column 2
RLA ; Into A bit 1
RR E ; Rotate column 3
RLA ; Into A bit 2
RR D ; Rotate column 4
RLA ; Into A bit 3
RR L ; Rotate column 5
RLA ; Into A bit 4
EXX
LD (BC),A ; Store row byte in work area
INC BC
EXX
DJNZ .transpose_loop ; Loop for all 8 rows
POP HL
JR .check_double_height
; --- Mosaic graphics character ---
; Viewdata mosaic characters are a 2×3 grid encoded in 6 bits.
; Each cell is either on or off, creating blocky graphics.
.mosaic_char:
LD C,A
RRCA
OR $1F ; Set up bit mask
AND C
LD C,A
LD DE,$3807 ; Pixel patterns for mosaic cells
BIT 0,L ; Check even/odd row
JR Z,.mosaic_even
LD DE,$1803 ; Alternate patterns
.mosaic_even:
LD B,$03 ; 3 pairs of rows
.mosaic_row_loop:
XOR A
RR C ; Get bit for left cell
JR NC,.left_off
OR D ; Left cell on
.left_off:
RR C ; Get bit for right cell
JR NC,.right_off
OR E ; Right cell on
.right_off:
BIT 0,B ; Even or odd in pair?
EXX
LD (BC),A ; Store first row of pair
INC BC
JR Z,.mosaic_single
LD (BC),A ; Store second row (same pattern)
INC BC
.mosaic_single:
EXX
BIT 0,L ; Check row flag
EXX
JR Z,.mosaic_no_extra
XOR A ; Extra blank row
.mosaic_no_extra:
LD (BC),A
INC BC
EXX
DJNZ .mosaic_row_loop
; --- Handle double-height characters ---
.check_double_height:
POP DE ; Restore alternate DE
BIT 2,L ; Double-height flag?
EXX
JR Z,.font_done
; Double-height: stretch each row to 2 rows
DEC BC
LD E,C
LD D,B
LD HL,$0008
ADD HL,BC
EX DE,HL
LD BC,$0010 ; 16 bytes (8 rows × 2)
.stretch_loop:
LDD ; Copy byte (going backwards)
INC HL
LDD ; Same byte again (double height)
JP PE,.stretch_loop
.font_done:
EXX
POP BC
EXX
POP DE
POP BC
RET
; ---------------------------------------------------------------------------
; CALCULATE DISPLAY BUFFER ADDRESS ($1A59)
; ---------------------------------------------------------------------------
; Calculates the address within the Viewdata display buffer for position
; (H=column, L=row). The buffer is a 40×24 array stored as part of the
; IX state block.
; Called via jump table at $0015.
; ---------------------------------------------------------------------------
CALC_BUF_ADDR:
PUSH HL
LD A,H ; Column
LD H,$00
LD D,H ; DE = row
LD E,L
ADD HL,HL ; ×2
ADD HL,HL ; ×4
ADD HL,DE ; ×5
ADD HL,HL ; ×10
ADD HL,HL ; ×20
ADD HL,HL ; ×40
LD E,A ; Add column offset
ADD HL,DE ; HL = row * 40 + column
PUSH IX
POP DE ; DE = IX (state block base)
ADD HL,DE ; HL = base + offset
LD E,(IX+$00) ; Get buffer base address
LD D,(IX+$01)
OR A
SBC HL,DE ; Subtract buffer base (normalize)
EX DE,HL ; DE = buffer address
POP HL
RET
; ---------------------------------------------------------------------------
; REFRESH ALL DISPLAY ROWS ($1A77)
; ---------------------------------------------------------------------------
; Re-renders the entire Viewdata display. Called after screen clear or
; when switching display modes.
; Called via jump table at $001B.
; ---------------------------------------------------------------------------
REFRESH_SCREEN:
LD L,$00 ; Start at row 0
.refresh_loop:
DEC L ; Check previous row first
CALL CHECK_LINE_WRAP
INC L
OR A
CALL Z,SCROLL_LINE ; Refresh this row
INC L
LD A,$17 ; Row 23 (last row)
CP L
JR NC,.refresh_loop ; Continue for all 24 rows
RET
; ---------------------------------------------------------------------------
; CLEAR DISPLAY BUFFER ($1A89)
; ---------------------------------------------------------------------------
CLEAR_DISPLAY_BUF:
LD HL,$0000
CALL CALC_BUF_ADDR ; Get start of buffer
LD L,E
LD H,D
INC DE
LD (HL),$20 ; Fill with spaces
LD BC,$03BF ; 960 bytes (40×24)
LDIR
RET
; ---------------------------------------------------------------------------
; CLEAR SCREEN ($1A9A)
; ---------------------------------------------------------------------------
; Clears the Spectrum's screen display (bitmap and attributes).
; Called via jump table at $001E.
; ---------------------------------------------------------------------------
CLEAR_SCREEN:
BIT 7,(IX+$06) ; Check 16K flag
NOP
LD HL,$4000 ; Screen bitmap start
LD DE,$4001
.clear_third:
LD BC,$0300 ; 768 bytes per screen third
LD (HL),$00 ; Clear to black
LDIR ; Clear bitmap
CALL CHECK_RX_DATA ; Poll modem while clearing (don't miss data!)
LD A,$58 ; End of screen thirds
CP H
JR NZ,.clear_third ; Continue for all 3 thirds
; --- Set attributes ---
LD (HL),$47 ; Attribute: white INK, black PAPER, no flash
LD BC,$02FF ; 767 bytes of attributes
LDIR ; Fill attribute file
RET
; ---------------------------------------------------------------------------
; CLEAR CHARACTER LINE ($1ABC)
; ---------------------------------------------------------------------------
; Clears a single character row (8 pixel lines) on the Spectrum screen.
; L = row number to clear.
; ---------------------------------------------------------------------------
CLEAR_CHAR_LINE:
PUSH BC
PUSH DE
PUSH HL
LD A,L
AND $38 ; Get screen third
OR $40 ; Add screen base
LD H,A
LD A,L
RRCA ; Calculate row offset within third
RRCA
RRCA
.clear_row_loop:
AND $E0 ; Mask to row start
LD L,A
LD E,L
LD D,H
INC DE
LD (HL),$00 ; Clear first byte
LD BC,$001F ; 31 more bytes
LDIR ; Clear entire row
INC H ; Next pixel line
LD A,H
AND $07
JR Z,.clear_done ; 8 pixel lines done
LD A,L
JR .clear_row_loop
.clear_done:
POP HL
POP DE
POP BC
RET
; ---------------------------------------------------------------------------
; PRESTEL FRAME DETECTOR ($1AE3)
; ---------------------------------------------------------------------------
; Checks incoming data for Prestel frame control sequences.
; Detects |Z (frame end), |A (frame start), and other markers.
; ---------------------------------------------------------------------------
PRESTEL_DETECT:
BIT 0,(IX+$06) ; Online mode?
RET Z ; No: return
LD C,(IX+$0A) ; Get parser state
BIT 7,A ; High bit set (parity error)?
JR Z,.detect_normal ; No: process normally
LD (IX+$0B),$04 ; Error: return code 4
RET
.detect_normal:
CP $0C ; Form Feed?
JR NZ,.not_ff_detect
LD C,$00 ; Reset parser state
.not_ff_detect:
BIT 7,C ; In escape sequence?
JR Z,.not_in_esc
RES 7,C ; Clear escape flag
CP $5A ; 'Z' (frame end)?
JR NZ,.not_in_esc
LD C,$04 ; Set state: frame end detected
.not_in_esc:
CP $7C ; '|' (pipe/escape)?
JR NZ,.not_pipe
SET 7,C ; Set escape flag
JR .save_state
.not_pipe:
INC C ; Increment state counter
DEC C ; Test if zero
JR Z,.save_state ; State 0: waiting for data
DEC C
JR NZ,.save_state ; State > 1: continue
LD (IX+$0B),$05 ; State 1: frame complete -> return code 5
.save_state:
LD (IX+$0A),C ; Save parser state
RET
; ---------------------------------------------------------------------------
; PADDING ($1B1D-$1B1F)
; ---------------------------------------------------------------------------
DEFB $FF, $FF, $FF ; Unused (padding)
; =============================================================================
; CHARACTER FONT DATA ($1B20-$1CFF)
; =============================================================================
; 5×8 pixel font for the Viewdata display. 96 characters ($20-$7F), each
; defined as 5 column bytes. During rendering, these are transposed to 8
; row bytes by SETUP_FONT_RENDER. Characters $20-$3F and $60-$7F include
; Viewdata-specific glyphs (£ for #, arrow keys for [\], etc.)
; =============================================================================
CHAR_FONT:
DEFB $00, $00, $00, $00, $00 ; $20 char ' '
DEFB $00, $00, $5F, $00, $00 ; $21 char '!'
DEFB $00, $07, $00, $07, $00 ; $22 char \""\"
DEFB $48, $7E, $49, $41, $42 ; $23 char '£'
DEFB $26, $49, $7F, $49, $32 ; $24 char '$'
DEFB $23, $13, $08, $64, $62 ; $25 char '%'
DEFB $36, $49, $56, $20, $50 ; $26 char '&'
DEFB $00, $00, $07, $00, $00 ; $27 char '''
DEFB $00, $1C, $22, $41, $00 ; $28 char '('
DEFB $00, $41, $22, $1C, $00 ; $29 char ')'
DEFB $22, $14, $7F, $14, $22 ; $2A char '*'
DEFB $08, $08, $3E, $08, $08 ; $2B char '+'
DEFB $00, $80, $60, $00, $00 ; $2C char ','
DEFB $00, $08, $08, $08, $00 ; $2D char '-'
DEFB $00, $00, $40, $00, $00 ; $2E char '.'
DEFB $20, $10, $08, $04, $02 ; $2F char '/'
DEFB $1C, $22, $41, $22, $1C ; $30 char '0'
DEFB $00, $42, $7F, $40, $00 ; $31 char '1'
DEFB $62, $51, $49, $49, $46 ; $32 char '2'
DEFB $21, $41, $49, $4D, $33 ; $33 char '3'
DEFB $18, $14, $12, $7F, $10 ; $34 char '4'
DEFB $27, $45, $45, $45, $39 ; $35 char '5'
DEFB $3C, $4A, $49, $49, $30 ; $36 char '6'
DEFB $01, $71, $09, $05, $03 ; $37 char '7'
DEFB $36, $49, $49, $49, $36 ; $38 char '8'
DEFB $06, $49, $49, $29, $1E ; $39 char '9'
DEFB $00, $00, $44, $00, $00 ; $3A char ':'
DEFB $00, $80, $64, $00, $00 ; $3B char ';'
DEFB $08, $14, $22, $41, $00 ; $3C char '<'
DEFB $14, $14, $14, $14, $14 ; $3D char '='
DEFB $00, $41, $22, $14, $08 ; $3E char '>'
DEFB $02, $01, $59, $05, $02 ; $3F char '?'
DEFB $3E, $41, $5D, $55, $1E ; $40 char '@'
DEFB $7C, $12, $11, $12, $7C ; $41 char 'A'
DEFB $7F, $49, $49, $49, $36 ; $42 char 'B'
DEFB $3E, $41, $41, $41, $22 ; $43 char 'C'
DEFB $7F, $41, $41, $41, $3E ; $44 char 'D'
DEFB $7F, $49, $49, $49, $41 ; $45 char 'E'
DEFB $7F, $09, $09, $09, $01 ; $46 char 'F'
DEFB $3E, $41, $41, $51, $72 ; $47 char 'G'
DEFB $7F, $08, $08, $08, $7F ; $48 char 'H'
DEFB $00, $41, $7F, $41, $00 ; $49 char 'I'
DEFB $20, $40, $40, $40, $3F ; $4A char 'J'
DEFB $7F, $08, $14, $22, $41 ; $4B char 'K'
DEFB $7F, $40, $40, $40, $40 ; $4C char 'L'
DEFB $7F, $02, $0C, $02, $7F ; $4D char 'M'
DEFB $7F, $04, $08, $10, $7F ; $4E char 'N'
DEFB $3E, $41, $41, $41, $3E ; $4F char 'O'
DEFB $7F, $09, $09, $09, $06 ; $50 char 'P'
DEFB $3E, $41, $51, $21, $5E ; $51 char 'Q'
DEFB $7F, $09, $19, $29, $46 ; $52 char 'R'
DEFB $26, $49, $49, $49, $32 ; $53 char 'S'
DEFB $01, $01, $7F, $01, $01 ; $54 char 'T'
DEFB $3F, $40, $40, $40, $3F ; $55 char 'U'
DEFB $07, $18, $60, $18, $07 ; $56 char 'V'
DEFB $3F, $40, $38, $40, $3F ; $57 char 'W'
DEFB $63, $14, $08, $14, $63 ; $58 char 'X'
DEFB $03, $04, $78, $04, $03 ; $59 char 'Y'
DEFB $61, $51, $49, $45, $43 ; $5A char 'Z'
DEFB $08, $1C, $2A, $08, $08 ; $5B char '←'
DEFB $0F, $00, $64, $54, $48 ; $5C char '½'
DEFB $08, $08, $2A, $1C, $08 ; $5D char '→'
DEFB $08, $04, $3E, $04, $08 ; $5E char '↑'
DEFB $14, $7F, $14, $7F, $14 ; $5F char '#'
DEFB $08, $08, $08, $08, $08 ; $60 char '_'
DEFB $20, $54, $54, $54, $78 ; $61 char 'a'
DEFB $7F, $44, $44, $44, $38 ; $62 char 'b'
DEFB $38, $44, $44, $44, $44 ; $63 char 'c'
DEFB $38, $44, $44, $44, $7F ; $64 char 'd'
DEFB $38, $54, $54, $54, $18 ; $65 char 'e'
DEFB $00, $08, $7E, $09, $00 ; $66 char 'f'
DEFB $18, $54, $54, $54, $3C ; $67 char 'g'
DEFB $7F, $04, $04, $04, $78 ; $68 char 'h'
DEFB $00, $44, $7D, $40, $00 ; $69 char 'i'
DEFB $00, $80, $7D, $00, $00 ; $6A char 'j'
DEFB $00, $7F, $10, $28, $44 ; $6B char 'k'
DEFB $00, $41, $7F, $40, $00 ; $6C char 'l'
DEFB $7C, $04, $78, $04, $7C ; $6D char 'm'
DEFB $7C, $04, $04, $04, $78 ; $6E char 'n'
DEFB $38, $44, $44, $44, $38 ; $6F char 'o'
DEFB $FC, $24, $24, $24, $18 ; $70 char 'p'
DEFB $18, $24, $24, $24, $FC ; $71 char 'q'
DEFB $00, $7C, $08, $04, $04 ; $72 char 'r'
DEFB $48, $54, $54, $54, $24 ; $73 char 's'
DEFB $00, $04, $3F, $44, $00 ; $74 char 't'
DEFB $3C, $40, $40, $40, $7C ; $75 char 'u'
DEFB $0C, $30, $40, $30, $0C ; $76 char 'v'
DEFB $3C, $40, $30, $40, $3C ; $77 char 'w'
DEFB $44, $28, $10, $28, $44 ; $78 char 'x'
DEFB $0C, $50, $50, $50, $7C ; $79 char 'y'
DEFB $44, $64, $54, $4C, $44 ; $7A char 'z'
DEFB $1F, $00, $60, $50, $F8 ; $7B char '¼'
DEFB $00, $7F, $00, $7F, $00 ; $7C char '‖'
DEFB $15, $15, $6A, $50, $F8 ; $7D char '¾'
DEFB $08, $08, $2A, $08, $08 ; $7E char '÷'
DEFB $7F, $7F, $7F, $7F, $7F ; $7F char '█'
; =============================================================================
; MICRONET 800 SPLASH SCREEN ($1D00-$1F82)
; =============================================================================
; A Viewdata frame displayed on startup showing the MICRONET 800 logo
; with mosaic graphics. This is a raw 40-column Viewdata display frame
; with embedded Viewdata control codes:
; $01-$07: Alpha colours (Red, Green, Yellow, Blue, Magenta, Cyan, White)
; $0D: Carriage Return
; $11: Mosaic Red ... $17: Mosaic White
; $14: Normal display
; $1D: New background colour
; $20-$7F: Viewdata character set
; =============================================================================
SPLASH_SCREEN:
; Row 0: .MICRONET 800 (C) .Ver 3.1 .10/08/83
DEFB $03, $4D, $49, $43, $52, $4F, $4E, $45, $54, $20, $38, $30, $30, $20, $28, $43
DEFB $29, $20, $20, $20, $20, $07, $56, $65, $72, $20, $33, $2E, $31, $20, $20, $03
DEFB $31, $30, $2F, $30, $38, $2F, $38, $33
; Row 1: .
DEFB $20, $1D, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20
DEFB $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20
DEFB $20, $20, $20, $20, $20, $20, $20, $20
; Row 2: ... )0 8! )0 8! )0
DEFB $07, $1D, $11, $20, $20, $20, $20, $20, $20, $20, $20, $29, $30, $20, $20, $20
DEFB $20, $20, $38, $21, $20, $29, $30, $20, $20, $20, $20, $20, $38, $21, $20, $29
DEFB $30, $20, $20, $20, $20, $20, $20, $20
; Row 3: .. ./."d `& "d `& "d ..
DEFB $07, $1D, $20, $20, $20, $20, $20, $20, $20, $14, $2F, $11, $22, $64, $20, $20
DEFB $60, $26, $20, $20, $20, $20, $22, $64, $20, $20, $60, $26, $20, $20, $20, $20
DEFB $22, $64, $20, $14, $7F, $20, $20, $20
; Row 4: ....>.~o} . ~//} .>o4h?/} .>o4h?/} /.//%
DEFB $07, $1D, $14, $7F, $3E, $7F, $7E, $6F, $7D, $20, $7F, $20, $7E, $2F, $2F, $7D
DEFB $20, $7F, $3E, $6F, $34, $68, $3F, $2F, $7D, $20, $7F, $3E, $6F, $34, $68, $3F
DEFB $2F, $7D, $20, $2F, $7F, $2F, $2F, $25
; Row 5: .... j5 . . . . j5 . . j5j}|. .
DEFB $07, $1D, $14, $7F, $20, $6A, $35, $20, $7F, $20, $7F, $20, $7F, $20, $20, $20
DEFB $20, $7F, $20, $20, $20, $6A, $35, $20, $7F, $20, $7F, $20, $6A, $35, $6A, $7D
DEFB $7C, $7F, $20, $20, $7F, $20, $20, $20
; Row 6: .... j5 . . . . j5 . . j5j5 .
DEFB $07, $1D, $14, $7F, $20, $6A, $35, $20, $7F, $20, $7F, $20, $7F, $20, $20, $20
DEFB $20, $7F, $20, $20, $20, $6A, $35, $20, $7F, $20, $7F, $20, $6A, $35, $6A, $35
DEFB $20, $20, $20, $20, $7F, $20, $20, $20
; Row 7: .... j5 . . o||? . *}|? . j5*}|? o|~%
DEFB $07, $1D, $14, $7F, $20, $6A, $35, $20, $7F, $20, $7F, $20, $6F, $7C, $7C, $3F
DEFB $20, $7F, $20, $20, $20, $2A, $7D, $7C, $3F, $20, $7F, $20, $6A, $35, $2A, $7D
DEFB $7C, $3F, $20, $20, $6F, $7C, $7E, $25
; Row 8: .. .(0 (0 0 8 .`p0 pp pp
DEFB $07, $1D, $20, $20, $20, $20, $20, $20, $20, $20, $20, $11, $28, $30, $20, $28
DEFB $30, $20, $20, $20, $30, $20, $20, $20, $38, $20, $20, $20, $14, $60, $70, $30
DEFB $20, $70, $70, $20, $20, $70, $70, $20
; Row 9: .. ."d bd "d`& `&..#.j7k5j7k5
DEFB $07, $1D, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $11, $22, $64, $20
DEFB $62, $64, $20, $20, $22, $64, $60, $26, $20, $20, $60, $26, $14, $7F, $23, $7F
DEFB $6A, $37, $6B, $35, $6A, $37, $6B, $35
; Row 10: ... for the .91 )0 8)0 8! .o|?j5j5j5j5
DEFB $07, $1D, $04, $20, $66, $6F, $72, $20, $74, $68, $65, $20, $20, $20, $11, $39
DEFB $31, $20, $29, $30, $20, $38, $29, $30, $20, $38, $21, $20, $14, $6F, $7C, $3F
DEFB $6A, $35, $6A, $35, $6A, $35, $6A, $35
; Row 11: .. . & "d.bf bf `&.. .j5j5j5j5
DEFB $07, $1D, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $11, $20, $26, $20
DEFB $22, $64, $11, $62, $66, $20, $20, $62, $66, $20, $60, $26, $14, $7F, $20, $7F
DEFB $6A, $35, $6A, $35, $6A, $35, $6A, $35
; Row 12: ...ZX Spectrum. 91 )8! )! .o|?*}~%*}~%
DEFB $07, $1D, $01, $5A, $58, $20, $53, $70, $65, $63, $74, $72, $75, $6D, $11, $20
DEFB $20, $20, $39, $31, $20, $29, $38, $21, $20, $29, $21, $20, $14, $6F, $7C, $3F
DEFB $2A, $7D, $7E, $25, $2A, $7D, $7E, $25
; Row 13: .. . & "d&"d
DEFB $07, $1D, $20, $20, $20, $20, $20, $20, $20, $20, $20, $11, $20, $20, $20, $20
DEFB $20, $26, $20, $22, $64, $26, $22, $64, $20, $20, $20, $20, $20, $20, $20, $20
DEFB $20, $20, $20, $20, $20, $20, $20, $20
; Row 14: ...,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
DEFB $07, $1D, $14, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C
DEFB $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C, $2C
DEFB $2C, $2C, $2C, $2C, $2C, $2C, $20, $20
; Row 15: .\n.. Press any key
DEFB $20, $1D, $0D, $01, $08, $20, $20, $20, $20, $20, $20, $20, $50, $72, $65, $73
DEFB $73, $20, $61, $6E, $79, $20, $6B, $65, $79, $20, $20, $20, $20, $20, $20, $20
DEFB $20, $20, $20, $20, $20, $20, $20, $20
; Row 16: ..X
DEFB $C0, $03, $58
; =============================================================================
; CONFIGURATION DATA ($1F83-$1FCB)
; =============================================================================
; Default configuration values for the VTX5000 device state block.
; These are copied to RAM during initialisation and include:
; - Default timeout values
; - 8251 USART mode and command bytes
; - Display buffer pointers
; - Font pointer placeholders
; =============================================================================
CONFIG_DATA:
; $1F83
DEFB $02, $FF, $00, $60, $7B, $15, $00, $00, $00, $00, $00, $00, $00, $00, $00, $47
; $1F93
DEFB $00, $00, $00, $00, $00, $00, $00, $00, $00, $FF, $FF, $FF, $FF, $FF, $FF, $FF
; $1FA3
DEFB $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $00, $17, $05, $00, $00, $00, $00
; $1FB3
DEFB $00, $00, $00, $00, $3F, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $05, $64
; $1FC3
DEFB $00, $64, $00, $20, $1B, $FF, $C3, $21, $00
; =============================================================================
; ROM PAGING TRAMPOLINES ($1FCC-$1FFF)
; =============================================================================
; These routines are copied to the top of RAM during initialisation.
; They handle switching between the VTX5000 ROM and the Spectrum ROM.
; On a 48K Spectrum, they live at $FF80-$FFFF.
; On a 16K Spectrum, they live at $7F80-$7FFF.
;
; The trampolines use the 8251's RTS output to control ROMCS:
; OUT ($FF),$15 -> RTS=0 -> VTX5000 ROM paged in
; OUT ($FF),$35 -> RTS=1 -> Spectrum ROM paged in
; EX (SP),HL x2 -> bus-cycle delay for paging to settle
; =============================================================================
; ---------------------------------------------------------------------------
; TRAMPOLINE: Call Spectrum ROM routine then return to VTX ($1FCC)
; Called from the RST $38 handler on 48K Spectrum ($FFED -> here+offset)
; ---------------------------------------------------------------------------
TRAMP_CALL_SPECROM:
DI ; Disable interrupts during ROM switch
LD A,$15 ; Command: RTS=0, page IN VTX5000 ROM
OUT ($FF),A ; Switch to VTX ROM
EX (SP),HL ; Bus-cycle delay (paging settle time)
EX (SP),HL
CALL GET_IX ; Set IX to device state block
EI ; Re-enable interrupts
CALL MODEM_POLL ; Call modem poll (in VTX ROM)
; ---------------------------------------------------------------------------
; TRAMPOLINE: Return to Spectrum ROM ($1FDA)
; ---------------------------------------------------------------------------
TRAMP_RETURN_SPECROM:
DI
LD A,$35 ; Command: RTS=1, page IN Spectrum ROM
OUT ($FF),A ; Switch to Spectrum ROM
EX (SP),HL ; Bus-cycle delay
EX (SP),HL
EI ; Re-enable interrupts
RET ; Return to Spectrum ROM code
; ---------------------------------------------------------------------------
; TRAMPOLINE: Jump to START_BASIC in VTX ROM ($1FE3)
; ---------------------------------------------------------------------------
TRAMP_START_BASIC:
DI
LD A,$15 ; Page in VTX ROM
OUT ($FF),A
EX (SP),HL
EX (SP),HL
JP START_BASIC ; Jump to BASIC startup in VTX ROM
; ---------------------------------------------------------------------------
; TRAMPOLINE: Page in Spectrum ROM for interrupt ($1FED)
; Called by RST $38 handler -> JP $7FED or JP $FFED
; ---------------------------------------------------------------------------
TRAMP_INT_SPECROM:
LD A,$35 ; Page in Spectrum ROM
OUT ($FF),A
EX (SP),HL ; Bus-cycle delay
EX (SP),HL
DEFB $FF ; RST $38 -> Spectrum ROM's interrupt handler
; ---------------------------------------------------------------------------
; TRAMPOLINE: Return from interrupt to VTX ROM ($1FF4)
; ---------------------------------------------------------------------------
TRAMP_INT_RETURN:
DI
LD A,$15 ; Page in VTX ROM
OUT ($FF),A
EX (SP),HL ; Bus-cycle delay
EX (SP),HL
POP AF ; Restore AF (pushed by RST38_HANDLER)
EI ; Re-enable interrupts
RET ; Return to VTX ROM code
; --- End of ROM padding ---
DEFB $FF, $FF ; Padding to fill $1FFE-$1FFF
; =============================================================================
; END OF VTX5000 ROM ($2000)
; =============================================================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment