ca65 V2.17 - Git 582aa41 Main file : Atom_COS.ca65 Current file: Atom_COS.ca65 000000r 1 ; 000000r 1 ; Acorn Atom COS 000000r 1 ; 000000r 1 ; Produced from an original ROM from an Atom purchased in the early 1980s, 000000r 1 ; disassembled at that time, updated and transferred to CA65 format in 2018. 000000r 1 ; Revisited in 2020, commenting added to and mistakes corrected. 000000r 1 ; 000000r 1 ; Chris Oddy 000000r 1 ; 000000r 1 .setcpu "6502" 000000r 1 .listbytes unlimited 000000r 1 ; 000000r 1 ; *** Zeropage Registers *** 000000r 1 ; 000000r 1 txtspc := $12 ; BASIC text space pointer 000000r 1 000000r 1 byte := $C0 ; BGT/BPT byte to get/send (1 byte) 000000r 1 bptcnt := $C1 ; BPT bit counter (1 byte) 000000r 1 leadct := $C2 ; leader counter (2 bytes) 000000r 1 tempy1 := $C3 ; temporary Y storage (1 byte) 000000r 1 count := $C4 ; BGT/BPT counter (1 byte) 000000r 1 lastc := $C5 ; last value of port C (CASIN) (1 byte) 000000r 1 000000r 1 filead := $C9 ; filename address (2 bytes) 000000r 1 ; 000000r 1 ; Note: some registers are used for different purposes during load and save 000000r 1 ; Used during save 000000r 1 loadad := $CB ; file load address (2 bytes) 000000r 1 execad := $CD ; file execution address (2 bytes) 000000r 1 starad := $CF ; data start address (2 bytes) 000000r 1 endad := $D1 ; data end address +1 (2 bytes) 000000r 1 sy1sta := $CB ; system 1 format file start address (2 bytes) 000000r 1 sy1end := $CD ; system 1 format file end address +1 (2 bytes) 000000r 1 blksiz := $CF ; block size (1 byte) 000000r 1 blkno := $D0 ; block number (2 bytes) 000000r 1 blkflg := $D2 ; block flags (bit 5 clear for 1st block, bit 6 clear if block has no data, bit 7 clear for last block) (1 byte) 000000r 1 starpt := $D3 ; start data pointer (2 bytes) 000000r 1 endpt := $D5 ; end data pointer (2 bytes) 000000r 1 ; Used during load 000000r 1 lflag := $CD ; load flag (bit 7 set if load address valid) (1 byte) 000000r 1 ldchk := $CE ; load checksum (1 byte) 000000r 1 stradr := $CF ; file (or data) start address (2 bytes) 000000r 1 endadr := $D1 ; file (or data) end address +1 (2 bytes) 000000r 1 ; System 1 format file header 000000r 1 fhstar := $D4 ; file header start address (2 bytes) 000000r 1 fhend := $D6 ; file header end address (2 bytes) 000000r 1 ; ; System 2 format block header 000000r 1 bhload := $D4 ; block header load address (2 bytes) 000000r 1 bhexec := $D6 ; block header execution address (2 bytes) 000000r 1 bhlen := $D8 ; block header block length (1 byte) 000000r 1 bhno := $D9 ; block header block number (2 bytes) 000000r 1 bhflag := $DB ; block header block flags (bit 5 clear for 1st block, bit 6 clear if block has no data, bit 7 clear for last block) (1 byte) 000000r 1 ; 000000r 1 chksum := $DC ; checksum (1 byte) 000000r 1 chkflg := $DD ; check flags (bit 6 set if System 1 'unnamed' file, bit 7 set for FLOAD 'don't check blocks') (1 byte) 000000r 1 lineno := $DE ; screen current linenumber, 0 is bottom of screen (1 byte) 000000r 1 cursor := $E0 ; cursor position on the screen current line, bit 7 is set if screen output is disabled (1 byte) (1 byte) 000000r 1 curctl := $E1 ; cursor control, bit 7 (invert) (1 byte) 000000r 1 jump := $E2 ; jump address for vdu/key code routines (2 bytes) 000000r 1 tempx1 := $E4 ; temporary X storage (1 byte) 000000r 1 temp_y := $E5 ; temporary Y storage (1 byte) 000000r 1 scrnln := $E6 ; current screen line pointer, bit 7=0 if page mode enabled (1 byte) 000000r 1 lock := $E7 ; lock key state (1 byte) 000000r 1 pc := $E8 ; program counter (2 bytes) 000000r 1 mesage := $EA ; message (1 byte) 000000r 1 ; $EB ; not used 000000r 1 tempx2 := $EC ; temp X storage (1 byte) 000000r 1 temp_a := $ED ; temp A storage (1 byte) 000000r 1 ; $EE - FD ; not used 000000r 1 not_pr := $FE ; non-printing character (1 byte) 000000r 1 irqa := $FF ; storage for A on IRQ (1 byte) 000000r 1 ; 000000r 1 buffer := $0100 ; input buffer (64 bytes) 000000r 1 filnam := $0140 ; string buffer (64 bytes) 000000r 1 ; 000000r 1 ; *** RAM Workspace *** 000000r 1 ; 000000r 1 stack := $01FF ; top of processor stack ($0180 to $01FF) 000000r 1 array := $02EB ; BASIC array storage 000000r 1 ; 000000r 1 ; OS Redirection Vectors ($0200 to $021D) 000000r 1 ; 000000r 1 NMIVEC := $0200 ; vector to NMI service routine 000000r 1 BRKVEC := $0202 ; vector to BRK service routine 000000r 1 IRQVEC := $0204 ; vector to IRQ service routine 000000r 1 COMVEC := $0206 ; vector to COS Command Line Interpreter (OSCCLI) 000000r 1 WRCVEC := $0208 ; vector to OS WRite CHaracter (OSWRCH) 000000r 1 RDCVEC := $020A ; vector to OS REad CHaracter (OSRDCH 000000r 1 LODVEC := $020C ; vector to OS LOAD file (OSLOAD) 000000r 1 SAVVEC := $020E ; vector to OS SAVE file (OSSAVE) 000000r 1 RDRVEC := $0210 ; vector to OS ReaD ARguments (OSRDAR) 000000r 1 STRVEC := $0212 ; vector to OS SeT ARguments (OSSTAR) 000000r 1 BGTVEC := $0214 ; vector to OS Byte GET (OSBGET) 000000r 1 BPTVEC := $0216 ; vector to OS Byte PUT (OSBPUT) 000000r 1 FNDVEC := $0218 ; vector to OS FiND (open) file (OSFIND) 000000r 1 SHTVEC := $021A ; vector to OS SHUT file (OSSHUT) 000000r 1 ECOVEC := $021C ; vector to ECONET handler 000000r 1 ; 000000r 1 ; BASIC Text Space 000000r 1 ; 000000r 1 lowtxt := $2900 ; lower text space (expanded Atom) 000000r 1 upptxt := $8200 ; upper text space (unexpanded Atom) 000000r 1 ; 000000r 1 ; *** VDU *** 000000r 1 ; 000000r 1 vdu := $8000 ; VDU RAM base address 000000r 1 vdua := $8000 ; VDU character mode RAM page 1 000000r 1 vdub := $8100 ; VDU character mode RAM page 2 000000r 1 vdumd1 := $8200 ; VDU graphics RAM Mode 1 000000r 1 vdumd2 := $8400 ; VDU graphics RAM Mode 2 000000r 1 vdumd3 := $8600 ; VDU graphics RAM Mode 3 000000r 1 vdumd4 := $8C00 ; VDU graphics RAM Mode 4 000000r 1 ; 000000r 1 ; *** Hardware Addresses *** 000000r 1 ; 000000r 1 ; 8255 PIA (IC25) Keyboard, VDU, cassette and speaker interfaces 000000r 1 ; 000000r 1 port_A := $B000 ; bits 0-3: keyboard row (output) 000000r 1 ; bits 4-7: graphics mode (output) 000000r 1 port_B := $B001 ; bits 0-5: keyboard column (input) 000000r 1 ; bit 6: CTRL key (input, active low) 000000r 1 ; bit 7: SHIFT keys (input, active low) 000000r 1 port_C := $B002 ; bit 0: cassette interface output (CASOUT) 000000r 1 ; bit 1: 2.4kHz enable (output) 000000r 1 ; bit 2: loudspeaker output 000000r 1 ; bit 3: not used 000000r 1 ; bit 4: cassette interface 2.4kHz input 000000r 1 ; bit 5: cassette interface input (CASIN) 000000r 1 ; bit 6: repeat key (input, active low) 000000r 1 ; bit 7: (60Hz) Field Sync input from CRTC (low during fly back) 000000r 1 control := $B003 ; control register, normally $8A (mode 0) 000000r 1 ; outputs: port A, port C lower 000000r 1 ; inputs: port B, port C upper 000000r 1 ; 000000r 1 ; VIA 6522 (IC1) Printer Interface 000000r 1 ; 000000r 1 dr_b := $B800 ; Port B Data Register (not used) 000000r 1 dr_a := $B801 ; Port A Data Register (printer port) 000000r 1 dd_b := $B802 ; Port B Data Direction Register 000000r 1 dd_a := $B803 ; Port A Data Direction Register 000000r 1 pcr := $B80C ; Peripheral Control Register 000000r 1 ; 000000r 1 ; *** ROMs *** 000000r 1 ; 000000r 1 ; Utility ROM 000000r 1 UTILTY := $A000 ; start address of Utility ROM 000000r 1 ; 000000r 1 ; BASIC ROM 000000r 1 BASIC := $C000 ; start address of BASIC ROM 000000r 1 BASIC1 := $C2B2 ; BASIC1 entry point 000000r 1 BASIC2 := $C2B6 ; BASIC2 entry point 000000r 1 RTN := $C278 ; called by OSSHUT 000000r 1 ERR174 := $C2AC ; called by OSSTAR and OSRDAR 000000r 1 ; 000000r 1 ; DOS ROM 000000r 1 DOSROM := $D000 ; start address of DOS ROM 000000r 1 DOS := $E000 ; Entry Point 000000r 1 ; 000000r 1 ; *** ASCII Control Codes *** 000000r 1 ; 000000r 1 NUL := $00 ; NULl 000000r 1 STXX := $02 ; Start of TeXt (Start Printer) 000000r 1 ETX := $03 ; End of TeXt (End Printer) 000000r 1 ACK := $06 ; ACKnowledge (Start Screen) 000000r 1 BEL := $07 ; BELL (Bleep) 000000r 1 BS := $08 ; BackSpace 000000r 1 HT := $09 ; Horizontal Tab 000000r 1 LF := $0A ; LineFeed 000000r 1 VT := $0B ; Vertical Tab 000000r 1 FF := $0C ; Form Feed 000000r 1 CR := $0D ; Carriage Return 000000r 1 SO := $0E ; Shift Out (Page Mode On 000000r 1 SI := $0F ; Shift In (Page Mode Off) 000000r 1 NAK := $15 ; Negative AcKnowledge (End Screen) 000000r 1 CAN := $18 ; CANcel 000000r 1 ESC := $1B ; ESCape 000000r 1 RS := $1E ; Record Separator (Home Cursor) 000000r 1 SPACE := ' ' ; Space Bar 000000r 1 DEL := $7F ; DELete 000000r 1 ; 000000r 1 .org $F7D1 ; start address of Atom COS 00F7D1 1 ; 00F7D1 1 ; Output a String of Characters 00F7D1 1 68 OUTSTR: pla ; retrieve return PC (-1) LSB 00F7D2 1 85 E8 sta pc 00F7D4 1 68 pla 00F7D5 1 85 E9 sta pc+1 ; and MSB 00F7D7 1 A0 00 outnxt: ldy #$00 ; Y=0 00F7D9 1 E6 E8 inc pc ; increment pointer 00F7DB 1 D0 02 bne noinpc 00F7DD 1 E6 E9 inc pc+1 00F7DF 1 B1 E8 noinpc: lda (pc),y ; get character from string 00F7E1 1 30 06 bmi eos ; end of string ? 00F7E3 1 20 F4 FF jsr OSWRCH ; output character 00F7E6 1 4C D7 F7 jmp outnxt ; next character 00F7E9 1 6C E8 00 eos: jmp (pc) ; continue execution after string 00F7EC 1 00F7EC 1 ; Output Two Addresses 00F7EC 1 A2 D4 OUT2WD: ldx #fhstar ; pointer to System 1 file header start address 00F7EE 1 ; or System 2 block header load address 00F7EE 1 20 F1 F7 jsr OUTADR ; output first address, a space, then the second address 00F7F1 1 00F7F1 1 ; Output the Address at X+1, X 00F7F1 1 B5 01 OUTADR: lda $01,x ; first byte 00F7F3 1 20 02 F8 jsr OUTBYT ; output the second byte (at X+1) 00F7F6 1 E8 inx ; increment the pointer to the next address 00F7F7 1 E8 inx 00F7F8 1 B5 FE lda $FE,x ; output the first byte (now at X-2) 00F7FA 1 20 02 F8 jsr OUTBYT 00F7FD 1 A9 20 outspc: lda #SPACE ; followed by a space 00F7FF 1 4C F4 FF jmp OSWRCH ; and return 00F802 1 00F802 1 ; Output the Byte in A In Hex 00F802 1 48 OUTBYT: pha ; save A 00F803 1 4A lsr a ; start with the upper nibble 00F804 1 4A lsr a ; shift to the lower nibble position 00F805 1 4A lsr a 00F806 1 4A lsr a 00F807 1 20 0B F8 jsr OUTDIG ; and output 00F80A 1 68 pla ; retrieve A and output the lower nibble 00F80B 1 00F80B 1 ; Output the Hex Digit in A 00F80B 1 29 0F OUTDIG: and #$0F ; mask off the upper nibble 00F80D 1 C9 0A cmp #$0A ; is it A-F ? 00F80F 1 90 02 bcc ascii ; no 00F811 1 69 06 adc #$06 ; yes - add offset to 'A' 00F813 1 69 30 ascii: adc #'0' ; convert to ASCII 00F815 1 4C F4 FF jmp OSWRCH ; output character and return 00F818 1 00F818 1 ; Get Filename From Input Buffer 00F818 1 20 76 F8 GTFLNM: jsr RDIBUF ; read next non-space character from input buffer 00F81B 1 A2 00 ldx #$00 ; filename buffer pointer 00F81D 1 C9 22 cmp #'"' ; is it a " ? 00F81F 1 F0 06 beq quote ; yes 00F821 1 E8 inx ; increment filename buffer pointer 00F822 1 D0 1B bne noname ; no - must be a System 1 'unnamed file' (always branch) 00F824 1 4C 7D FA namerr: jmp synerr 00F827 1 C8 quote: iny ; increment input buffer pointer 00F828 1 B9 00 01 lda buffer,y ; read another character 00F82B 1 C9 0D cmp #CR ; CR ? 00F82D 1 F0 F5 beq namerr ; yes - syntax error 00F82F 1 9D 40 01 sta filnam,x ; put character in filename buffer 00F832 1 E8 inx ; increment filename buffer pointer 00F833 1 C9 22 cmp #'"' ; was it a second " ? 00F835 1 D0 F0 bne quote ; no keep reading buffer 00F837 1 C8 iny ; yes - increment input buffer pointer 00F838 1 B9 00 01 lda buffer,y ; check for further " 00F83B 1 C9 22 cmp #'"' ; could be a " in filename 00F83D 1 F0 E8 beq quote ; yes - represented by "", continue getting filename 00F83F 1 A9 0D noname: lda #CR 00F841 1 9D 3F 01 sta filnam-1,x ; terminate filename with CR 00F844 1 A9 40 lda #filnam 00F84A 1 85 CA sta filead+1 00F84C 1 A2 C9 ldx #filead ; point X at filename pointer 00F84E 1 60 rts ; and return 00F84F 1 00F84F 1 ; Copy File Parameters and Check Filename Length 00F84F 1 A0 00 CPYPAR: ldy #$00 ; source file parameters pointer 00F851 1 B5 00 move: lda $00,x ; read source file parameter 00F853 1 99 C9 00 sta filead,y ; copy to file parameter table 00F856 1 E8 inx ; increment source pointers 00F857 1 C8 iny ; increment destination pointer 00F858 1 C0 0A cpy #$0A ; have we copied all 10 bytes (5 addresses) ? 00F85A 1 90 F5 bcc move ; no - continue 00F85C 1 A0 FF ldy #$FF ; Y=-1 00F85E 1 A9 0D lda #CR ; check the length of the filename 00F860 1 C8 notcr: iny 00F861 1 C0 0E cpy #$0E ; filename too long, >13 characters ? 00F863 1 B0 07 bcs outnam 00F865 1 D1 C9 cmp (filead),y ; compare next filename character with CR 00F867 1 D0 F7 bne notcr ; if not CR go around again 00F869 1 C0 00 cpy #$00 ; CR - return with Y containing the length of the filename 00F86B 1 60 rts ; and Z=1 if length is 0 00F86C 1 20 D1 F7 outnam: jsr OUTSTR 00F86F 1 4E 41 4D 45 .byte "NAME" ; output 'Name' error message 00F873 1 EA nop 00F874 1 00 brk ; and break 00F875 1 00F875 1 C8 space: iny 00F876 1 ; Read the Yth Character From the Input Buffer 00F876 1 B9 00 01 RDIBUF: lda buffer,y 00F879 1 C9 20 cmp #SPACE ; ignoring spaces 00F87B 1 F0 F8 beq space 00F87D 1 60 rts ; and return 00F87E 1 00F87E 1 ; Test Key Value In A For Hex 00F87E 1 C9 30 HEXKEY: cmp #'0' ; > '0' ? 00F880 1 90 0F bcc nothex ; return invalid hex 00F882 1 C9 3A cmp #':' ; < '9' ? 00F884 1 90 08 bcc hex ; yes - valid hex 00F886 1 E9 07 sbc #$07 ; convert 'A' to 'F' 00F888 1 90 07 bcc nothex ; if < 'A' then not hex 00F88A 1 C9 40 cmp #'@' ; > 'F' ? 00F88C 1 B0 02 bcs hexrtn ; return with C=1, invalid hex digit 00F88E 1 29 0F hex: and #$0F ; valid hex digit, convert to ASCII 00F890 1 60 hexrtn: rts ; and return with C=0, valid hex digit 00F891 1 38 nothex: sec ; C=1 00F892 1 60 rts ; and return, invalid hex digit 00F893 1 00F893 1 ; Get (up to) 4-digit Hex Parameter from Input Buffer and Store at X 00F893 1 A9 00 HXPARA: lda #$00 ; set parameter to 0 00F895 1 95 00 sta $00,x 00F897 1 95 01 sta $01,x 00F899 1 95 02 sta $02,x 00F89B 1 20 76 F8 jsr RDIBUF ; ignore leading spaces 00F89E 1 B9 00 01 rdnxch: lda buffer,y ; read character from input buffer 00F8A1 1 20 7E F8 jsr HEXKEY ; is it hex ? 00F8A4 1 B0 15 bcs delim ; branch if not hex (delimiter character ?) 00F8A6 1 0A asl a ; shift up a digit 00F8A7 1 0A asl a 00F8A8 1 0A asl a 00F8A9 1 0A asl a 00F8AA 1 94 02 sty $02,x ; temporarily save the input buffer pointer 00F8AC 1 A0 04 ldy #$04 00F8AE 1 0A shfdig: asl a ; shift X, X+1 and A left one digit (4-bits) 00F8AF 1 36 00 rol $00,x 00F8B1 1 36 01 rol $01,x 00F8B3 1 88 dey 00F8B4 1 D0 F8 bne shfdig 00F8B6 1 B4 02 ldy $02,x ; retrieve the input buffer pointer 00F8B8 1 C8 iny ; increment buffer pointer 00F8B9 1 D0 E3 bne rdnxch ; always branch to read next character 00F8BB 1 B5 02 delim: lda $02,x ; set Z if no parameter found 00F8BD 1 60 rts ; and return 00F8BE 1 00F8BE 1 ; COS Command Table 00F8BE 1 43 41 54 FA COMTAB: .byte "CAT",>CCAT,LOAD,SAVE,CRUN,MON,NOMON,FLOAD,DOS,COMERR,7 loops - 1200Hz, not 2400Hz leader so round again 00FBA9 1 C6 C3 dec leadct+1 ; decrement LSB of leader counter 00FBAB 1 D0 F0 bne nlead 00FBAD 1 C6 C2 dec leadct ; and MSB 00FBAF 1 D0 EC bne nlead ; until we get to zero, 2 seconds (4096 cycles) of 2400Hz 00FBB1 1 70 01 V: bvs header ; was CTRL pressed ? no - continue to load header 00FBB3 1 60 rts ; CTRL pressed - return 00FBB4 1 A0 04 header: ldy #$04 ; 4 header bytes 00FBB6 1 08 php ; save status register (key pressed) 00FBB7 1 20 E4 FB jsr GTHEDY ; get header bytes 00FBBA 1 28 plp ; (retrieve status register for return) 00FBBB 1 A0 04 ldy #$04 ; for System 2 File format these will be **** 00FBBD 1 A9 2A lda #'*' 00FBBF 1 D9 D3 00 hedcmp: cmp fhstar-1,y ; compare 00FBC2 1 D0 03 bne rdrtn 00FBC4 1 88 dey 00FBC5 1 D0 F8 bne hedcmp ; compare all 4 bytes 00FBC7 1 60 rdrtn: rts ; and return 00FBC8 1 00FBC8 1 C8 nextch: iny 00FBC9 1 ; Get Filename From Cassette 00FBC9 1 20 D4 FF GTNAME: jsr OSBGET ; get byte 00FBCC 1 99 ED 00 sta temp_a,y ; put it in buffer 00FBCF 1 C9 0D cmp #CR ; CR ? 00FBD1 1 D0 F5 bne nextch ; no - get next character 00FBD3 1 A0 FF ldy #$FF ; reset filename pointer 00FBD5 1 00FBD5 1 ; Compare Filenames 00FBD5 1 C8 NAMCMP: iny ; increment filename pointer 00FBD6 1 B1 C9 lda (filead),y ; compare filename with required file 00FBD8 1 D9 ED 00 cmp temp_a,y 00FBDB 1 D0 EA bne rdrtn ; no match - return 00FBDD 1 C9 0D cmp #CR ; match - is it terminating CR ? 00FBDF 1 D0 F4 bne NAMCMP ; no - compare next character 00FBE1 1 60 rts ; yes - return 00FBE2 1 00FBE2 1 ; Get 8 Header Bytes 00FBE2 1 A0 08 GTHEAD: ldy #$08 00FBE4 1 ; Get Y Header Bytes 00FBE4 1 20 D4 FF GTHEDY: jsr OSBGET ; get byte 00FBE7 1 99 D3 00 sta fhstar-1,y ; and save 00FBEA 1 88 dey 00FBEB 1 D0 F7 bne GTHEDY ; do required number 00FBED 1 60 rts ; and return 00FBEE 1 00FBEE 1 ; Get Byte from Cassette 00FBEE 1 86 EC BGT: stx tempx2 ; save X 00FBF0 1 84 C3 sty tempy1 ; save Y 00FBF2 1 08 php ; and save status on stack 00FBF3 1 78 sei ; disable interrupts 00FBF4 1 A9 78 nstart: lda #$78 ; wait for start bit (0) 00FBF6 1 85 C0 sta byte ; set edge counter starting value 00FBF8 1 20 BD FC nbit0: jsr TCASIN ; determine time between CASIN edges 00FBFB 1 90 F7 bcc nstart ; <8 loops - 2400Hz, not a start bit so round again 00FBFD 1 E6 C0 inc byte ; increment edge counter 00FBFF 1 10 F7 bpl nbit0 ; 0 bit ?, (reached $80 i.e. 8 cycles of 1200Hz) 00FC01 1 A9 53 get8: lda #$53 ; get byte - set loop time to one bit period, 3.3mS 00FC03 1 85 C4 sta count ; loop counter 00FC05 1 A2 00 ldx #$00 ; reset the edge count 00FC07 1 AC 02 B0 ldy port_C ; Y = port_C 00FC0A 1 20 CD FC bitlop: jsr CASIN ; has CASIN changed state ? 00FC0D 1 F0 00 beq delay1 ; no edge detected - delay 00FC0F 1 F0 01 delay1: beq delay2 ; no edge detected - continue 00FC11 1 E8 inx ; edge detected - increment edge count 00FC12 1 C6 C4 delay2: dec count ; decrement the loop count 00FC14 1 D0 F4 bne bitlop ; continue $53 times (=3.3mS) 00FC16 1 E0 0C cpx #$0C ; compare edges with 12 ? (ideally 8='0' and 16='1') to generate received bit 00FC18 1 66 C0 ror byte ; rotate the bit into the received byte 00FC1A 1 90 E5 bcc get8 ; get all 8 bits 00FC1C 1 A5 C0 lda byte ; return with received byte in A 00FC1E 1 28 plp ; retrieve status from stack 00FC1F 1 A4 C3 ldy tempy1 ; retrieve Y 00FC21 1 A6 EC ldx tempx2 ; retrieve X 00FC23 1 00FC23 1 ; Add Byte to Checksum 00FC23 1 48 ADDCHK: pha ; save A on stack 00FC24 1 18 clc 00FC25 1 65 DC adc chksum ; add byte to checksum 00FC27 1 85 DC sta chksum ; and update 00FC29 1 68 pla ; retrieve A from stack 00FC2A 1 60 rts ; and return 00FC2B 1 00FC2B 1 ; Set File Load Address 00FC2B 1 A5 CD LODADR: lda execad ; is bit 7 of execution address set ? 00FC2D 1 30 08 bmi lodrtn ; if set - use current load address 00FC2F 1 A5 D4 lda fhstar ; if clear - use file header start address as load address 00FC31 1 85 CB sta loadad 00FC33 1 A5 D5 lda fhstar+1 00FC35 1 85 CC sta loadad+1 00FC37 1 60 lodrtn: rts ; and return 00FC38 1 00FC38 1 FND: ; Output 'PLAY TAPE' Message 00FC38 1 B0 04 bcs PLYMES 00FC3A 1 A9 06 lda #$06 00FC3C 1 D0 02 bne CASMES 00FC3E 1 00FC3E 1 ; Output 'PLAY TAPE' Message 00FC3E 1 A9 04 PLYMES: lda #$04 ; A<5 00FC40 1 00FC40 1 ; Output Cassette Message in A 00FC40 1 A2 07 CASMES: ldx #$07 00FC42 1 8E 02 B0 stx port_C ; enable high tone 00FC45 1 24 EA bit mesage ; are messages enabled ? 00FC47 1 D0 2D bne nomes ; no - bypass message 00FC49 1 C9 05 cmp #$05 ; yes - output required message, which message ? 00FC4B 1 F0 16 beq rewind ; 5 - 'rewind' 00FC4D 1 B0 09 bcs record ; >5 - 'record' 00FC4F 1 20 D1 F7 jsr OUTSTR ; must be <5 - 'PLAY' 00FC52 1 50 4C 41 59 .byte "PLAY" 00FC56 1 D0 15 bne outape ; followed by 00FC58 1 20 D1 F7 record: jsr OUTSTR ; 'RECORD' 00FC5B 1 52 45 43 4F .byte "RECORD" 00FC5F 1 52 44 00FC61 1 D0 0A bne outape 00FC63 1 20 D1 F7 rewind: jsr OUTSTR ; followed by 00FC66 1 52 45 57 49 .byte "REWIND" ; 'REWIND' 00FC6A 1 4E 44 00FC6C 1 EA nop 00FC6D 1 20 D1 F7 outape: jsr OUTSTR ; ' TAPE' 00FC70 1 20 54 41 50 .byte " TAPE" 00FC74 1 45 00FC75 1 EA nop 00FC76 1 20 E3 FF nomes: jsr OSRDCH ; wait for key press 00FC79 1 4C ED FF jmp OSCRLF ; output CR LF and return 00FC7C 1 00FC7C 1 ; Put Byte To Tape 00FC7C 1 86 EC BPT: stx tempx2 ; save X 00FC7E 1 84 C3 sty tempy1 ; save Y 00FC80 1 08 php ; save status register on stack 00FC81 1 78 sei ; disable interrupts 00FC82 1 48 pha ; save A on stack 00FC83 1 20 23 FC jsr ADDCHK ; update checksum 00FC86 1 85 C0 sta byte ; save byte to send 00FC88 1 20 D8 FC jsr WAIT24 ; wait for next falling edge of 2.4kHz tone 00FC8B 1 A9 0A lda #$0A ; send 10 bits per byte - 1 start (0), 8 data, 1 stop (1) 00FC8D 1 85 C1 sta bptcnt ; bit counter 00FC8F 1 18 clc ; send an initial 0 start bit 00FC90 1 90 0A putbit: bcc bit0 ; are we sending a 0 or 1 ? 00FC92 1 A2 07 ldx #$07 ; send a 1 bit (4 cycles of 2.4kHz tone) 00FC94 1 8E 02 B0 stx port_C ; enable CASOUT and 2.4kHz tone 00FC97 1 20 DA FC jsr WATX24 ; wait for 8 falling edges of the 2.4kHz tone 00FC9A 1 30 13 bmi nxtbit ; always branch 00FC9C 1 A0 04 bit0: ldy #$04 ; send a 0 bit (8 cycles of 1.2kHz tone) 00FC9E 1 A9 04 cycle0: lda #$04 00FCA0 1 8D 02 B0 sta port_C ; disable CASOUT (and 2.4kHz tone) 00FCA3 1 20 D8 FC jsr WAIT24 ; wait for next falling edge of 2.4kHz tone 00FCA6 1 EE 02 B0 inc port_C ; enable CASOUT 00FCA9 1 20 D8 FC jsr WAIT24 ; wait for next falling edge of 2.4kHz tone 00FCAC 1 88 dey ; decrement cycle count 00FCAD 1 D0 EF bne cycle0 00FCAF 1 38 nxtbit: sec ; shift a 1 into byte to send for final stop bit 00FCB0 1 66 C0 ror byte ; shift next bit to send 00FCB2 1 C6 C1 dec bptcnt ; decrement the bit counter 00FCB4 1 D0 DA bne putbit ; and send next bit 00FCB6 1 A4 C3 ldy tempy1 ; retrieve Y 00FCB8 1 A6 EC ldx tempx2 ; retrieve X 00FCBA 1 68 pla ; retrieve A from stack 00FCBB 1 28 plp ; retrieve status from stack 00FCBC 1 60 rts ; and return 00FCBD 1 00FCBD 1 ; Time CASIN 00FCBD 1 A2 00 TCASIN: ldx #$00 ; reset edge count 00FCBF 1 AC 02 B0 ldy port_C ; read port C 00FCC2 1 E8 noedge: inx ; increment edge count 00FCC3 1 F0 07 beq trtn ; after 256 times around without an edge return 00FCC5 1 20 CD FC jsr CASIN ; has CASIN input changed state ? 00FCC8 1 F0 F8 beq noedge ; no edge detected - go round again 00FCCA 1 E0 08 cpx #$08 ; is edge count < 8 ? 00FCCC 1 60 trtn: rts ; and return 00FCCD 1 00FCCD 1 ; Detect CASIN State Change 00FCCD 1 84 C5 CASIN: sty lastc ; save last port C state 00FCCF 1 AD 02 B0 lda port_C ; read port C again 00FCD2 1 A8 tay ; save in Y 00FCD3 1 45 C5 eor lastc ; compare the two 00FCD5 1 29 20 and #$20 ; mask off CASIN (bit 5) 00FCD7 1 60 rts ; and return (Z is set if CASIN unchanged) 00FCD8 1 00FCD8 1 ; Wait for Next Falling Edge of 2.4kHz Tone 00FCD8 1 A2 00 WAIT24: ldx #$00 ; wait for 1 cycle 00FCDA 1 00FCDA 1 ; Wait for X Falling Edges of 2.4kHz Tone 00FCDA 1 A9 10 WATX24: lda #$10 ; mask for 2.4kHz input (bit 4) 00FCDC 1 2C 02 B0 lo: bit port_C ; test 2.4kHz input 00FCDF 1 F0 FB beq lo ; if low, wait until it goes high 00FCE1 1 2C 02 B0 hi: bit port_C ; test 2.4kHz input 00FCE4 1 D0 FB bne hi ; if high, wait until it goes low 00FCE6 1 CA dex ; decrement count 00FCE7 1 10 F3 bpl lo ; repeat cycling until X=$FF 00FCE9 1 60 rts ; and return 00FCEA 1 00FCEA 1 ; VDU Handler 00FCEA 1 C9 06 VDU: cmp #ACK ; start screen ? 00FCEC 1 F0 1D beq VDUON ; enable screen output and return 00FCEE 1 C9 15 cmp #NAK ; end screen ? 00FCF0 1 F0 1F beq VDUOFF ; disable screen output and return 00FCF2 1 A4 E0 ldy cursor ; Y = cursor position on current line 00FCF4 1 30 23 bmi vdurtn ; if bit 7 is set screen output is disabled, branch to return 00FCF6 1 C9 1B cmp #ESC ; reset to character mode ? 00FCF8 1 F0 11 beq VDUON ; enable screen output and return 00FCFA 1 C9 07 cmp #BEL ; BELL ? 00FCFC 1 F0 1C beq BELL ; ring the BELL and return 00FCFE 1 20 44 FD jsr INVERT ; invert the character at the cursor position to remove highlight 00FD01 1 A2 0A ldx #$0A ; pointer to the end of the VDU code table 00FD03 1 20 C5 FE jsr FNDCOD ; look up VDU control code in table 00FD06 1 D0 21 bne VDUCHR ; not a control code so write the character to the screen 00FD08 1 4C B7 FE jmp JMPTAB ; jump to the control code routine 00FD0B 1 00FD0B 1 18 VDUON: clc ; Enable Screen Output 00FD0C 1 A2 00 ldx #$00 00FD0E 1 8E 00 B0 stx port_A ; reset screen to character mode (mode 0) 00FD11 1 00FD11 1 A2 02 VDUOFF: ldx #$02 ; Disable Screen Output (carry is set on entry) 00FD13 1 00FD13 1 ; Update MSB of a VDU Control Byte 00FD13 1 08 UPDBIT: php ; save status register on stack 00FD14 1 16 DE asl lineno,x ; discard bit 7 of curctl (X=2) or scrnln (X=8) 00FD16 1 28 plp ; retrieve status register from stack 00FD17 1 76 DE ror lineno,x ; replace with new bit from carry 00FD19 1 60 vdurtn: rts ; and return 00FD1A 1 00FD1A 1 A9 05 BELL: lda #$05 ; ring BELL 00FD1C 1 A8 tay ; Y=5 00FD1D 1 8D 03 B0 beldur: sta control ; write to 8255 data direction register (port C lower 5=input, 4=output) 00FD20 1 CA belfrq: dex 00FD21 1 D0 FD bne belfrq ; short loop 256 times (sets the frequency) 00FD23 1 49 01 eor #$01 ; invert control byte bit 1, value toggles between 5 and 4 00FD25 1 C8 iny 00FD26 1 10 F5 bpl beldur ; repeat 122 times (sets the duration) 00FD28 1 60 rts ; and return 00FD29 1 00FD29 1 ; Output Character to VDU 00FD29 1 C9 20 VDUCHR: cmp #SPACE ; if control code, =$60 (e.g. lower case) are converted to inverted 00FD31 1 49 60 eor #$60 ; for the rest invert bits 5 and 6 to get the VDU code 00FD33 1 20 6B FE gt60: jsr FLYBAK ; wait for VDU fly back 00FD36 1 91 DE sta (lineno),y ; write to the start address of current screen line, then 00FD38 1 00FD38 1 ; Handle the HT VDU Code (move cursor right) 00FD38 1 C8 VDUHT: iny ; increment cursor horizontal position 00FD39 1 C0 20 cpy #$20 ; end of line (32 characters/line) 00FD3B 1 90 05 bcc updcur ; no - update cursor position 00FD3D 1 20 EC FD jsr CURDWN ; yes - move cursor to the start of a new line and scroll if required 00FD40 1 00FD40 1 ; Handle the CR VDU Code (move cursor to start of current line) 00FD40 1 A0 00 VDUCR: ldy #$00 ; set cursor position to the start of line 00FD42 1 84 E0 updcur: sty cursor ; update cursor position 00FD44 1 00FD44 1 ; Invert the Character at the Cursor Position 00FD44 1 48 INVERT: pha ; save A on stack 00FD45 1 20 6B FE jsr FLYBAK ; wait for VDU fly back 00FD48 1 B1 DE lda (lineno),y ; read the character at the start address of current screen line + Y 00FD4A 1 45 E1 eor curctl ; and invert 00FD4C 1 91 DE sta (lineno),y ; update the character 00FD4E 1 68 pla ; retrieve A from stack 00FD4F 1 60 rts ; and return 00FD50 1 00FD50 1 ; Handle the DEL VDU Code (cursor left and clear character) 00FD50 1 20 35 FE VDUDEL: jsr CURLFT ; move cursor left 00FD53 1 A9 20 lda #SPACE ; and overwrite with a space 00FD55 1 20 6B FE jsr FLYBAK ; wait for VDU fly back 00FD58 1 91 DE sta (lineno),y ; update start address of current screen line 00FD5A 1 10 E6 bpl updcur ; update cursor position and invert character to highlight position (always branch) 00FD5C 1 00FD5C 1 ; Handle the BS VDU Code (move cursor left one character) 00FD5C 1 20 35 FE VDUBS: jsr CURLFT ; move cursor left 00FD5F 1 4C 42 FD jmp updcur ; update cursor position and invert character to highlight position 00FD62 1 00FD62 1 ; Handle the LF VDU Code (move cursor down one line) 00FD62 1 20 EC FD VDULF: jsr CURDWN ; move cursor down 00FD65 1 A4 E0 rupdcr: ldy cursor ; horizontal position remains the same so retrieve it 00FD67 1 10 D9 bpl updcur ; update cursor position and invert character to highlight position 00FD69 1 00FD69 1 ; Handle the FF VDU Code (clear screen) 00FD69 1 A0 80 VDUFF: ldy #$80 00FD6B 1 84 E1 sty curctl ; set bit 7 of cursor control 00FD6D 1 A0 00 ldy #$00 ; pointer to screen RAM 00FD6F 1 8C 00 B0 sty port_A ; set VDU to character mode (0) 00FD72 1 A9 20 lda #SPACE ; fill RAM with spaces 00FD74 1 99 00 80 clrvdu: sta vdua,y ; both pages 00FD77 1 99 00 81 sta vdub,y 00FD7A 1 C8 iny ; increment pointer 00FD7B 1 D0 F7 bne clrvdu ; do all 256 bytes of each page 00FD7D 1 00FD7D 1 ; Handle the RS VDU Code (home cursor) 00FD7D 1 A9 80 VDURS: lda #$80 00FD7F 1 A0 00 ldy #$00 ; set cursor position to start of line 00FD81 1 85 DF sta lineno+1 ; set start address of current screen line to $8000 00FD83 1 84 DE sty lineno 00FD85 1 F0 BB beq updcur ; update cursor position and invert character to highlight position 00FD87 1 00FD87 1 ; Handle the VT VDU Code (move cursor up one line) 00FD87 1 20 3A FE VDUVT: jsr CURSUP ; move cursor up 00FD8A 1 4C 42 FD jmp updcur ; and update cursor position and invert character to highlight position 00FD8D 1 00FD8D 1 ; Handle the SO VDU Code (turn on paged mode) 00FD8D 1 18 VDUSO: clc ; clear carry 00FD8E 1 A9 10 lda #$10 ; number of lines on screen 00FD90 1 85 E6 sta scrnln ; set current screen line pointer to top of screen 00FD92 1 00FD92 1 ; Handle the SI VDU Code (turn off paged mode) 00FD92 1 A2 08 VDUSI: ldx #$08 ; point X at screen line pointer 00FD94 1 20 13 FD jsr UPDBIT ; and update bit 7 (carry is set for SI, clear for SO) 00FD97 1 4C 44 FD jmp INVERT ; invert character at cursor to highlight position 00FD9A 1 00FD9A 1 ; Handle the LOCK Key Code (5) 00FD9A 1 A5 E7 KLOCK: lda lock ; read current lock state 00FD9C 1 49 60 eor #$60 ; toggle bits 5 and 6 00FD9E 1 85 E7 sta lock ; update lock state 00FDA0 1 B0 09 bcs jmpky ; and get another key 00FDA2 1 00FDA2 1 ; Handle the Cursor Key Codes (6 and 7) 00FDA2 1 29 05 KCURSR: and #$05 ; key codes become 4 and 5 00FDA4 1 2E 01 B0 rol port_B ; rotate SHIFT key bit into carry 00FDA7 1 2A rol a ; rotate into key code to convert to VDU cursor codes 8 to B 00FDA8 1 20 EA FC jsr VDU ; action the VDU cursor code 00FDAB 1 4C 9A FE jmpky: jmp waitky ; and get another key 00FDAE 1 00FDAE 1 ; Handle the COPY Key Code ($E) 00FDAE 1 A4 E0 KCOPY: ldy cursor ; get the cursor position on the screen 00FDB0 1 20 6B FE jsr FLYBAK ; wait for VDU fly back 00FDB3 1 B1 DE lda (lineno),y ; get the character from the start address of current screen line + Y 00FDB5 1 45 E1 eor curctl ; remove cursor 00FDB7 1 30 02 bmi convrt ; convert character VDU code to ASCII 00FDB9 1 49 60 eor #$60 00FDBB 1 E9 20 convrt: sbc #$20 00FDBD 1 4C E9 FD jmp kcrtn ; retrieve X, Y & status register and return 00FDC0 1 00FDC0 1 ; Handle the DEL Key Code ($F) 00FDC0 1 A9 5F KDEL: lda #$5F ; $5F eor $20 results in $7F 00FDC2 1 00FDC2 1 ; Handle the ESC Key Code ($3B) 00FDC2 1 49 20 KESC: eor #$20 ; invert bit 5 converts $3B to $1B ASCII ESC 00FDC4 1 D0 23 bne kcrtn ; retrieve X, Y & status register and return 00FDC6 1 00FDC6 1 ; Handle the A to Z Key Codes ($21 to $3A) 00FDC6 1 45 E7 KALPHA: eor lock ; if lock is on ($60) converts key codes to $41 to $5A 00FDC8 1 00FDC8 1 ; Handle the @ Key Code ($20) 00FDC8 1 2C 01 B0 KAT: bit port_B ; test SHIFT key 00FDCB 1 30 02 bmi jmpkc1 ; jump if not pressed 00FDCD 1 49 60 eor #$60 ; invert bits 5 and 6 00FDCF 1 4C DF FD jmpkc1: jmp KADD20 ; add $20 and return 00FDD2 1 00FDD2 1 ; Handle the [\]^ Key Codes (1 to 4) 00FDD2 1 69 39 KPUNC1: adc #$39 ; add $40 (carry set), converts to $3B to $3E 00FDD4 1 90 F2 bcc KAT ; add $20 (converts to $5B to $5E) and return 00FDD6 1 00FDD6 1 ; Handle the ,-./ Key Codes ($1C to $1F) 00FDD6 1 49 10 KPUNC2: eor #$10 ; invert bit 4, converts to $C to $F 00FDD8 1 00FDD8 1 ; Handle the 123456789:; Key Codes ($11 to $1B) 00FDD8 1 2C 01 B0 KNUMBR: bit port_B ; test SHIFT key 00FDDB 1 30 02 bmi KADD20 ; branch if not pressed 00FDDD 1 49 10 eor #$10 ; invert bit 4, converts to shifted keys !"#$%&'()*+ or <=>? 00FDDF 1 00FDDF 1 ; Handle SPACE and 0 Key Codes (0 and $10) 00FDDF 1 18 KADD20: clc 00FDE0 1 69 20 adc #$20 ; add $20, becomes $20 and $30 ASCII SPACE and '0' 00FDE2 1 00FDE2 1 ; Handle the RET Key Code ($13) 00FDE2 1 2C 01 B0 KRET: bit port_B ; test CTRL key 00FDE5 1 70 02 bvs kcrtn ; not pressed return 00FDE7 1 29 1F and #$1F ; if pressed mask off top 3 bits to convert to ASCII CTRL codes 00FDE9 1 4C 60 FE kcrtn: jmp wrcrtn ; retrieve X, Y & status register and return 00FDEC 1 00FDEC 1 ; Move the Cursor Down One Line 00FDEC 1 A5 DE CURDWN: lda lineno ; A=LSB of start address of current screen line 00FDEE 1 A4 DF ldy lineno+1 ; Y=MSB 00FDF0 1 C0 81 cpy #$81 ; which page are we in ? 00FDF2 1 90 38 bcc nscrol ; branch if lower screen page ($8000) 00FDF4 1 C9 E0 cmp #$E0 ; must be upper screen page ($8100), have we reached the last line ? 00FDF6 1 90 34 bcc nscrol ; no - branch 00FDF8 1 A4 E6 ldy scrnln ; yes so we need to scroll, check the page mode flag (bit 7) 00FDFA 1 30 0C bmi scroll ; if page mode not active (bit 7=1) branch and scroll the screen 00FDFC 1 88 dey ; otherwise decrement the page mode count 00FDFD 1 D0 07 bne nowait ; have we reached the bottom line ? 00FDFF 1 20 71 FE pagewt: jsr SCNKEY ; yes - scan the keyboard and wait for a key press 00FE02 1 B0 FB bcs pagewt 00FE04 1 A0 10 ldy #$10 ; reset the current screen line pointer to top of screen 00FE06 1 84 E6 nowait: sty scrnln ; and update 00FE08 1 A0 20 scroll: ldy #$20 ; scroll the screen, point Y at the second line ($8020) 00FE0A 1 20 66 FE jsr SYNC ; wait for the start of the next VDU fly back 00FE0D 1 B9 00 80 scrol1: lda vdua,y ; move the first page of the screen up one line 00FE10 1 99 E0 7F sta vdua-$20,y 00FE13 1 C8 iny ; increment the pointer 00FE14 1 D0 F7 bne scrol1 ; do the whole page 00FE16 1 20 6B FE jsr FLYBAK ; wait for VDU fly back 00FE19 1 B9 00 81 scrol2: lda vdub,y ; and move the second page of the screen 00FE1C 1 99 E0 80 sta vdub-$20,y 00FE1F 1 C8 iny ; increment the pointer 00FE20 1 D0 F7 bne scrol2 ; again the whole page 00FE22 1 A0 1F ldy #$1F ; then need to clear the bottom line 00FE24 1 A9 20 lda #SPACE ; fill with spaces 00FE26 1 91 DE clrln: sta (lineno),y 00FE28 1 88 dey ; decrement the pointer 00FE29 1 10 FB bpl clrln ; clear the whole line 00FE2B 1 60 rts ; and return 00FE2C 1 69 20 nscrol: adc #$20 ; add a line ($20) to the current address (C-0) 00FE2E 1 85 DE sta lineno ; and update 00FE30 1 D0 02 bne novflw ; if overflow 00FE32 1 E6 DF inc lineno+1 ; then update the MSB 00FE34 1 60 novflw: rts ; and return 00FE35 1 00FE35 1 ; Move the Cursor Left One Character 00FE35 1 88 CURLFT: dey ; decrement horizontal cursor position 00FE36 1 10 19 bpl currtn ; is it past the start of the line ?, no - return 00FE38 1 A0 1F ldy #$1F ; yes - set cursor position to end of upper line and 00FE3A 1 00FE3A 1 ; Move the Cursor Up One Line 00FE3A 1 A5 DE CURSUP: lda lineno ; load LSB of start address of current screen line into A 00FE3C 1 D0 0B bne upline ; if zero then we are at the top of a page 00FE3E 1 A6 DF ldx lineno+1 ; load MSB of start address of current screen line into X 00FE40 1 E0 80 cpx #$80 ; which page ? 00FE42 1 D0 05 bne upline ; if $8100 then jump 00FE44 1 68 pla ; must be $8000 so we are already at the top of the screen 00FE45 1 68 pla ; remove return address 00FE46 1 4C 65 FD jmp rupdcr ; and jump to retrieve and update cursor position 00FE49 1 00FE49 1 E9 20 upline: sbc #$20 ; subtract a line (32 characters) 00FE4B 1 85 DE sta lineno ; and update LSB of start address of current screen line 00FE4D 1 B0 02 bcs currtn ; if we were in page 81 and there was an overflow then 00FE4F 1 C6 DF dec lineno+1 ; decrement the MSB of the start address of current screen line to $8000 00FE51 1 60 currtn: rts ; and return 00FE52 1 00FE52 1 ; Write Character to Output Channel(s) 00FE52 1 20 FB FE WRC: jsr PRINT ; call printer handler 00FE55 1 08 php ; save status register on stack 00FE56 1 48 pha ; save A on stack 00FE57 1 D8 cld ; make sure we're in binary mode 00FE58 1 84 E5 sty temp_y ; save Y 00FE5A 1 86 E4 stx tempx1 ; save X 00FE5C 1 20 EA FC jsr VDU ; call the VDU handler 00FE5F 1 68 pla ; retrieve A from stack 00FE60 1 A6 E4 wrcrtn: ldx tempx1 ; retrieve X 00FE62 1 A4 E5 ldy temp_y ; retrieve Y 00FE64 1 28 plp ; retrieve status register from stack 00FE65 1 60 rts ; and return 00FE66 1 00FE66 1 ; Wait for the Start of the Next VDU Fly back 00FE66 1 2C 02 B0 SYNC: bit port_C ; test field sync input from CRTC 00FE69 1 10 FB bpl SYNC ; wait till high (screen scan) and then 00FE6B 1 00FE6B 1 ; Wait for VDU Fly back 00FE6B 1 2C 02 B0 FLYBAK: bit port_C ; test Field Sync input from CRTC 00FE6E 1 30 FB bmi FLYBAK ; wait till low (fly back) 00FE70 1 60 rts ; and return 00FE71 1 00FE71 1 ; Scan the Keyboard 00FE71 1 A0 3B SCNKEY: ldy #$3B ; number of keys to scan-1 (60) 00FE73 1 18 clc ; clear carry for key pressed 00FE74 1 A9 20 lda #$20 ; bit mask for keyboard column, starting at bit 5 00FE76 1 A2 0A nxtcol: ldx #$0A ; scan 10 keyboard rows 00FE78 1 2C 01 B0 scanrw: bit port_B ; has a key been pressed ? 00FE7B 1 F0 08 beq keyprs ; yes - branch 00FE7D 1 EE 00 B0 inc port_A ; no - increment the row 00FE80 1 88 dey ; decrement the key code 00FE81 1 CA dex ; decrement the row counter 00FE82 1 D0 F4 bne scanrw ; and repeat until all 10 rows scanned 00FE84 1 4A lsr a ; shift bit down to scan next column (carry set on last column) 00FE85 1 08 keyprs: php ; save status register 00FE86 1 48 pha ; and accumulator (row) 00FE87 1 AD 00 B0 lda port_A ; read the port 00FE8A 1 29 F0 and #$F0 ; retain the graphics mode bits, clearing the row bits 00FE8C 1 8D 00 B0 sta port_A ; and update 00FE8F 1 68 pla ; retrieve A 00FE90 1 28 plp ; retrieve status register 00FE91 1 D0 E3 bne nxtcol ; was a key pressed or last column ? - no round again for next column 00FE93 1 60 rts ; yes - either return with carry clear and key code in Y or carry set and no key 00FE94 1 00FE94 1 ; Read Character From Input Channel 00FE94 1 08 RDC: php ; save status register on stack 00FE95 1 D8 cld ; make sure we're in binary mode 00FE96 1 86 E4 stx tempx1 ; save X 00FE98 1 84 E5 sty temp_y ; save Y 00FE9A 1 2C 02 B0 waitky: bit port_C ; check for repeat key (bit 6) 00FE9D 1 50 05 bvc repeat ; if pressed don't wait for key release 00FE9F 1 20 71 FE jsr SCNKEY ; scan the keyboard 00FEA2 1 90 F6 bcc waitky ; and repeat until the key is released 00FEA4 1 20 8A FB repeat: jsr WAT100 ; wait 100mS for debounce 00FEA7 1 20 71 FE scanky: jsr SCNKEY ; has a key been pressed ? 00FEAA 1 B0 FB bcs scanky ; no - keep scanning 00FEAC 1 20 71 FE jsr SCNKEY ; yes - check still pressed 00FEAF 1 B0 F6 bcs scanky ; no - keep scanning 00FEB1 1 98 tya ; transfer key code to A (ASCII-$20) 00FEB2 1 A2 17 ldx #$17 ; pointer to end of RDCKEY table 00FEB4 1 20 C5 FE jsr FNDCOD ; look up key code in table 00FEB7 1 00FEB7 1 ; Look-up VDU/Key Code Handler Routine Address 00FEB7 1 BD E3 FE JMPTAB: lda VDUADR,x ; get LSB of routine address from appropriate table 00FEBA 1 85 E2 sta jump 00FEBC 1 A9 FD lda #$FD ; MSB of address for all routines is $FD 00FEBE 1 85 E3 sta jump+1 00FEC0 1 98 tya ; transfer code to A 00FEC1 1 6C E2 00 jmp (jump) ; jump to vdu/key code routine 00FEC4 1 00FEC4 1 CA search: dex ; decrement code table pointer 00FEC5 1 00FEC5 1 ; Search VDU/Key Code Table 00FEC5 1 DD CB FE FNDCOD: cmp VDUCOD,x ; compare table value with code in A 00FEC8 1 90 FA bcc search ; search until code is greater than or equal to table value 00FECA 1 60 rts ; and return 00FECB 1 00FECB 1 ; VDU Control Codes Table 00FECB 1 00 VDUCOD: .byte NUL ; NULl 00FECC 1 08 .byte BS ; BackSpace 00FECD 1 09 .byte HT ; Horizontal Tab 00FECE 1 0A .byte LF ; LineFeed 00FECF 1 0B .byte VT ; Vertical Tab 00FED0 1 0C .byte FF ; FormFeed 00FED1 1 0D .byte CR ; Carriage Return 00FED2 1 0E .byte SO ; Shift Out 00FED3 1 0F .byte SI ; Shift In 00FED4 1 1E .byte RS ; Record Separator 00FED5 1 7F .byte DEL ; DELete 00FED6 1 00FED6 1 ; RDC Key Codes Lookup Table 00FED6 1 00 RDCKEY: .byte $00 ; SPACE 00FED7 1 01 .byte $01 ; [\]^ 00FED8 1 05 .byte $05 ; LOCK 00FED9 1 06 .byte $06 ; Cursors 00FEDA 1 08 .byte $08 ; RETURN 00FEDB 1 0E .byte $0E ; COPY 00FEDC 1 0F .byte $0F ; DEL 00FEDD 1 10 .byte $10 ; 0 00FEDE 1 11 .byte $11 ; 123456789:; 00FEDF 1 1C .byte $1C ; ,-./ 00FEE0 1 20 .byte $20 ; @ 00FEE1 1 21 .byte $21 ; A to Z 00FEE2 1 3B .byte $3B ; ESC 00FEE3 1 00FEE3 1 ; VDU Code Handler Address Lookup Table 00FEE3 1 44 VDUADR: .byte upptxt ; address of unexpanded Atom BASIC upper text space ($8200) 00FF7E 1 85 12 sta txtspc ; set BASIC start of text space 00FF80 1 58 cli ; enable interrupts 00FF81 1 A9 55 lda #$55 ; check for presence of expanded Atom lower text space ($2900) 00FF83 1 8D 01 29 sta lowtxt+1 00FF86 1 CD 01 29 cmp lowtxt+1 00FF89 1 D0 0C bne notexp ; if not present this Atom has not been expanded 00FF8B 1 0A asl a ; A=$AA 00FF8C 1 8D 01 29 sta lowtxt+1 00FF8F 1 CD 01 29 cmp lowtxt+1 00FF92 1 D0 03 bne notexp ; if not present this Atom has not been expanded 00FF94 1 4C B2 C2 jmp BASIC1 ; start BASIC with TOP set to $2900 00FF97 1 4C B6 C2 notexp: jmp BASIC2 ; start BASIC with TOP set to $8200 00FF9A 1 00FF9A 1 ; Table Of OS Call Vectors 00FF9A 1 00 A0 VECTAB: .byte UTILTY ; vector to IRQ handler (points to start of Utility ROM) 00FF9C 1 EF F8 .byte COSCLI ; vector to COS Command Line Interpreter 00FF9E 1 52 FE .byte WRC ; vector to write character 00FFA0 1 94 FE .byte RDC ; vector to read character 00FFA2 1 6E F9 .byte CLOD ; vector to load 00FFA4 1 E5 FA .byte CSAV ; vector to save 00FFA6 1 AC C2 .byte ERR174 ; vector to read arguments (jumps to a BRK in BASIC ROM which causes display of err174 message) 00FFA8 1 AC C2 .byte ERR174 ; vector to set arguments (jumps to a BRK in BASIC ROM which causes display of err174 message) 00FFAA 1 EE FB .byte BGT ; vector to get byte from cassette 00FFAC 1 7C FC .byte BPT ; vector to put byte to cassette 00FFAE 1 38 FC .byte FND ; vector to find file 00FFB0 1 78 C2 .byte RTN ; vector to shut file (jumps to an RTS in BASIC ROM) 00FFB2 1 00FFB2 1 ; IRQ Handler Entry Point 00FFB2 1 85 FF IRQ: sta irqa ; save A 00FFB4 1 68 pla ; retrieve the status register 00FFB5 1 48 pha ; and put it back on the stack 00FFB6 1 29 10 and #$10 ; which interrupt was it, IRQ or BRK ? 00FFB8 1 D0 06 bne brkset 00FFBA 1 A5 FF lda irqa ; IRQ - retrieve A 00FFBC 1 48 pha ; and save on stack 00FFBD 1 6C 04 02 jmp (IRQVEC) ; jump to the IRQ vector 00FFC0 1 A5 FF brkset: lda irqa ; BRK - retrieve A 00FFC2 1 28 plp ; get a copy of the status register from the stack 00FFC3 1 08 php 00FFC4 1 6C 02 02 jmp (BRKVEC) ; and jump to the BRK vector 00FFC7 1 00FFC7 1 ; NMI Handler Entry Point 00FFC7 1 48 NMI: pha ; save accumulator on stack 00FFC8 1 6C 00 02 jmp (NMIVEC) ; jump to NMI vector (not defined by COS) 00FFCB 1 6C 1A 02 OSSHUT: jmp (SHTVEC) ; OS Call: SHUT file 00FFCE 1 6C 18 02 OSFIND: jmp (FNDVEC) ; OS Call: FIND file 00FFD1 1 6C 16 02 OSBPUT: jmp (BPTVEC) ; OS Call: PUT Byte to open file 00FFD4 1 6C 14 02 OSBGET: jmp (BGTVEC) ; OS Call: GET Byte from open file 00FFD7 1 6C 12 02 OSSTAR: jmp (STRVEC) ; OS Call: SeT ARguments 00FFDA 1 6C 10 02 OSRDAR: jmp (RDRVEC) ; OS Call: ReaD ARguments 00FFDD 1 6C 0E 02 OSSAVE: jmp (SAVVEC) ; OS Call: SAVE program 00FFE0 1 6C 0C 02 OSLOAD: jmp (LODVEC) ; OS Call: LOAD program 00FFE3 1 6C 0A 02 OSRDCH: jmp (RDCVEC) ; OS CAll: ReaD byte from input CHannel 00FFE6 1 20 E3 FF OSECHO: jsr OSRDCH ; OS Call: ECHO byte from input channel To output channel 00FFE9 1 C9 0D OSASCI: cmp #CR ; OS Call: writes character to output channel, if CR output LF as well 00FFEB 1 D0 07 bne OSWRCH ; if not CR - output the character 00FFED 1 A9 0A OSCRLF: lda #LF ; OS Call: writes CR LF to output channel 00FFEF 1 20 F4 FF jsr OSWRCH ; if CR - output LF 00FFF2 1 A9 0D lda #CR ; and then output CR 00FFF4 1 6C 08 02 OSWRCH: jmp (WRCVEC) ; OS Call: WRite byte to output CHannel 00FFF7 1 6C 06 02 OSCCLI: jmp (COMVEC) ; OS Call: execute command in input buffer 00FFFA 1 00FFFA 1 C7 FF .byte NMI ; Processor NMI Vector 00FFFC 1 3F FF .byte RST ; Processor Reset Vector 00FFFE 1 B2 FF .byte IRQ ; Processor IRQ Vector 00FFFF 1