Created
April 10, 2026 17:08
-
-
Save damieng/f61a57616f2f793bb2bf130d351b13eb to your computer and use it in GitHub Desktop.
Annotated VTX5000 ROM disassembly
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ; ============================================================================= | |
| ; 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