PROCESSOR 6502 LIST ON ; ;-----------------------------------------------------------------------; ; The Wozanium Pack ; ; This file is part one of the Wozanium Pack. ; ; Apple 1 basic is the other part ; ;-----------------------------------------------------------------------; ; Copyright 2010,2011 Mike Willegal ; A1 monitor and A1 apple cassette interface derived from ; original Apple 1 implemenations by Steve Wozniak ; ; The Wozanium Pack is free software: ; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; ; The Wozanium Pack is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with the Wozanium Pack. If not, see . ; ;-----------------------------------------------------------------------; ;------------------------------------------------------------------------- ; Defines - this code can be built one of four ways ; 1. clone/real Apple 1 HW ; 2. runs in ram of real or virtual Apple 2 ; 3. runs in virtual apple 2 as ROM ; 4. runs in plug in board of real Apple 2 ; ; select one of these three options ;------------------------------------------------------------------------- ; Build with dasm 6502 assembler and the following command line ;dasm a2a1emulv5_1.asm -DBLD4ROMBD=0 -DHUSTNBASIC=0 -oa2a1rbh.o -la2a1rbh.lst ; ;BLD4APPLE1 EQU 0 ;ACTUAL APPLE 1 or CLONE ;BLD4RAM EQU 0 ;RAM of virtual or real A2 ;BLD4EMULROM EQU 0 ;ROM of virtual A2 ;BLD4ROMBD EQU 0 ;ROM board in Real A2 ;------------------------------------------------------------------------- ; Constants ;------------------------------------------------------------------------- BS EQU $DF ;Backspace key, arrow left key CR EQU $8D ;Carriage Return ESC EQU $9B ;ESC key PROMPT EQU "\" ;Prompt character ;------------------------------------------------------------------------- ; scratchpad memory - uses last 1k of apple ii 48k ;------------------------------------------------------------------------- MAC STORAGE TURBO DS.b 1 ; any bit non-zero is turbo mode TURBOUSR EQU $01 ; USER TURBO MODE TURBOCMP EQU $02 ; COMPUTER DRIVEN TURBO MODE RDCONVERT DS.b 1 READVECTOR DS.w 1 ;allows user override of default keyboard read function WRITVECTOR DS.w 1 ;allows user override of default video out function savey DS.b 1 savex DS.b 1 POWERUPINIT DS.b 1 SCRNCLRD DS.b 1 CHAR DS.b 1 TMP1 DS.b 1 CURROW DS.b 1 CURCOL DS.b 1 COUNTER0 DS.b 1 COUNTER1 DS.b 1 CURSOR DS.b 1 RDSTRTL DS.b 1 RDSTRTH DS.b 1 HEX2LB DS.b 1 ;Begin address of dump block HEX2HB DS.b 1 ;;; zero page back up so graphics routines can use them TMPG0 DS.b 16 PG0SAVD DS.b 1 ENDM IFNCONST BLD4APPLE1 IFNCONST BLD4RAM ORG $bc00 STORAGE ENDIF ENDIF ;------------------------------------------------------------------------- ; Memory declaration ;------------------------------------------------------------------------- HEX1L EQU $24 ;End address of dump block HEX1H EQU $25 HEX2L EQU $26 ;Begin address of dump block HEX2H EQU $27 SAVEINDEX EQU $28 ;Save index in input buffer LASTSTATE EQU $29 ;Last input state IN EQU $0200 ;Input buffer IFCONST BLD4APPLE1 FLIP EQU $C000 ;Output flip-flop TAPEIN EQU $C081 ;Tape input KBD EQU $D010 ;PIA.A keyboard input KBDCR EQU $D011 ;PIA.A keyboard control register ELSE FLIP EQU $C020 ;Output flip-flop TAPEIN EQU $C060 ;Tape input KBD EQU $C000 ;keyboard input KBDCR EQU $C010 ;keybaord strobe clear ENDIF ;------------------------------------------------------------------------- ; Constants ;------------------------------------------------------------------------- CR EQU $8D ;Carriage Return ESC EQU $9B ;ASCII ESC CNTSTRT EQU $E0 ;--------------------------------------------------------------------------- ; build in ACI Driver ;--------------------------------------------------------------------------- IFCONST BLD4EMULROM ORG $D000 ; EMULATOR ROM image ELSE IFCONST BLD4RAM ORG $6000 ; build for memory ELSE ORG $C100 ; real apple 1 or plug in ROM board for A2 ENDIF ENDIF ;------------------------------------------------------------------------- ; ; The WOZ Apple Cassette Interface for the Apple 1 ; Written by Steve Wozniak somewhere around 1976 ; ;------------------------------------------------------------------------- WOZACI LDA #$aa ;Print the Tape prompt IFNCONST BLD4APPLE1 JMP A2ACIDRIVER ;If not actual Apple 1, use A2 driver ELSE JSR ECHO ENDIF LDA #CR ;And drop the cursor one line JSR ECHO LDY #-1 ;Reset the input buffer index ACINEXTCHAR INY ACIWAITCHAR LDA KBDCR ;Wait for key press BPL ACIWAITCHAR ;No key yet! LDA KBD ;Clear strobe STA IN,Y ;Save it into buffer JSR ECHO ;And type it on the screen CMP #ESC BEQ WOZACI ;Start from scratch if ESC! CMP #CR BNE ACINEXTCHAR ;Read keys until CR LDX #-1 ;Initialize parse buffer pointer ;------------------------------------------------------------------------- ; Start parsing first or a new tape command ;------------------------------------------------------------------------- NEXTCMD LDA #0 ;Clear begin and end values STA HEX1L STA HEX1H STA HEX2L STA HEX2H NEXTCHR INX ;Increment input pointer LDA IN,X ;Get next char from input line CMP #$d2 ;Read command? BEQ READ ;Yes! CMP #$d7 ;Write command? BEQ WRITE ;Yes! (note: CY=1) CMP #$ae ;Separator? BEQ SEP ;Yes! CMP #CR ;End of line? BEQ GOESC ;Escape to monitor! We're done CMP #$a0 ;Ignore spaces BEQ NEXTCHR EOR #$b0 ;Map digits to 0-9 CMP #9+1 ;Is it a decimal digit? BCC ACIDIG ;Yes! ADC #$88 ;Map letter "A"-"F" to $FA-$FF CMP #$FA ;Hex letter? BCC WOZACI ;No! Character not hex! ACIDIG ASL ;Hex digit to MSD of A ASL ASL ASL LDY #4 ;Shift count ACIHEXSHIFT ASL ;Hex digit left, MSB to carry ROL HEX1L ;Rotate into LSD ROL HEX1H ;Rotate into MSD DEY ;Done 4 shifts? BNE ACIHEXSHIFT ;No! Loop BEQ NEXTCHR ;Handle next character ;------------------------------------------------------------------------- ; Return to monitor, prints \ first ;------------------------------------------------------------------------- GOESC JMP ESCAPE ;Escape back to monitor ;------------------------------------------------------------------------- ; Separating . found. Copy HEX1 to Hex2. Doesn't clear HEX1!!! ;------------------------------------------------------------------------- SEP LDA HEX1L ;Copy hex value 1 to hex value 2 STA HEX2L LDA HEX1H STA HEX2H BCS NEXTCHR ;Always taken! ;------------------------------------------------------------------------- ; Write a block of memory to tape ;------------------------------------------------------------------------- WRITE LDA #64 ;Write 10 second header JSR WHEADER WRNEXT DEY ;Compensate timing for extra work LDX #0 ;Get next byte to write LDA (HEX2L,X) LDX #8*2 ;Shift 8 bits (decremented twice) WBITLOOP ASL ;Shift MSB to carry JSR WRITEBIT ;Write this bit BNE WBITLOOP ;Do all 8 bits! JSR INCADDR ;Increment address LDY #30 ;Compensate timer for extra work BCC WRNEXT ;Not done yet! Write next byte RESTIDX LDX SAVEINDEX ;Restore index in input line BCS NEXTCMD ;Always taken! ;------------------------------------------------------------------------- ; For case when ACI must fit in c100-c1ff - the read function must be moved ; because the standard read doesn't fit with the extra mask instruction ; required for the Apple II hardware ;------------------------------------------------------------------------- READ: JSR FULLCYCLE ;Wait until full cycle is detected LDA #22 ;Introduce some delay to allow JSR WHEADER ; the tape speed to stabilize JSR FULLCYCLE ;Synchronize with full cycle NOTSTART LDY #31 ;Try to detect the much shorter JSR CMPLEVEL ; start bit BCS NOTSTART ;Start bit not detected yet! JSR CMPLEVEL ;Wait for 2nd phase of start bit LDY #58 ;Set threshold value in middle RDBYTE LDX #8 ;Receiver 8 bits RDBIT PHA JSR FULLCYCLE ;Detect a full cycle PLA ROL ;Roll new bit into result LDY #57 ;Set threshold value in middle DEX ;Decrement bit counter BNE RDBIT ;Read next bit! STA (HEX2L,X) ;Save new byte JSR INCADDR ;Increment address LDY #53 ;Compensate threshold with workload BCC RDBYTE ;Do next byte if not done yet! BCS RESTIDX ;Always taken! Restore parse index FULLCYCLE JSR CMPLEVEL ;Wait for two level changes CMPLEVEL DEY ;Decrement time counter LDA TAPEIN ;Get Tape In data ;------------------------------------------------------------------------- ; the next instruction must be added for apple II ; since it doesn't fit in the 256 bytes allowed in the ; original PROM, a substitute A2 read function is called instead ; (READ_APPLE2_VERSION)which is located in a different bank ; ; AND #$80 ;CLEAR floating bits ;------------------------------------------------------------------------- CMP LASTSTATE ;Same as before? BEQ CMPLEVEL ;Yes! STA LASTSTATE ;Save new data CPY #128 ;Compare threshold RTS ;------------------------------------------------------------------------- ; Write header to tape ; ; The header consists of an asymmetric cycle, starting with one phase of ; approximately (66+47)x5=565us, followed by a second phase of ; approximately (44+47)x5=455us. ; Total cycle duration is approximately 1020us ~ 1kHz. The actual ; frequencywill be a bit lower because of the additional workload between ; the twoloops. ; The header ends with a short phase of (30+47)x5=385us and a normal ; phase of (44+47)x5=455us. This start bit must be detected by the read ; routine to trigger the reading of the actual data. ;------------------------------------------------------------------------- WHEADER STX SAVEINDEX ;Save index in input line HCOUNT LDY #66 ;Extra long delay JSR WDELAY ;CY is constantly 1, writing a 1 BNE HCOUNT ;Do this 64 * 256 time! ADC #-2 ;Decrement A (CY=1 all the time) BCS HCOUNT ;Not all done! LDY #30 ;Write a final short bit (start) ;------------------------------------------------------------------------- ; Write a full bit cycle ; ; Upon entry Y contains a compensated value for the first phase of 0 ; bit length. All subsequent loops don't have to be time compensated. ;------------------------------------------------------------------------- WRITEBIT JSR WDELAY ;Do two equal phases LDY #44 ;Load 250us counter - compensation WDELAY DEY ;Delay 250us (one phase of 2kHz) BNE WDELAY BCC WRITE1 ;Write a '1' (2kHz) LDY #47 ;Additional delay for '0' (1kHz) WDELAY0 DEY ; (delay 250us) BNE WDELAY0 WRITE1 LDY FLIP,X ;Flip the output bit LDY #41 ;Reload 250us cntr (compensation) DEX ;Decrement bit counter RTS ;------------------------------------------------------------------------- ; Increment current address and compare with last address ;------------------------------------------------------------------------- INCADDR LDA HEX2L ;Compare current address with CMP HEX1L ; end address LDA HEX2H SBC HEX1H INC HEX2L ;And increment current address BNE NOCARRY ;No carry to MSB! INC HEX2H NOCARRY RTS ;--------------------------------------------------------------------------- ; all the following code is needed for the A2 version ; skip to Basic for the real thing ;--------------------------------------------------------------------------- IFNCONST BLD4APPLE1 ;--------------------------------------------------------------------------- ; build in an A2 ACI driver for ROM version ; if using an A2, the version at C100 jumps to this version ; and this version is at D000 (so slots 0 & 2-7 can be used) ;--------------------------------------------------------------------------- IFNCONST BLD4EMULROM IFNCONST BLD4RAM ORG $D000 ; A2 ROM or image ENDIF ENDIF ;------------------------------------------------------------------------- ; ACI DRIVER ;------------------------------------------------------------------------- A2ACIDRIVER LDA #$aa ;Print the Tape prompt JSR ECHO LDA #CR ;And drop the cursor one line JSR ECHO LDY #-1 ;Reset the input buffer index RB_ACINEXTCHAR INY JSR A2GETCHAR STA IN,Y ;Save it into buffer JSR ECHO ;And type it on the screen CMP #ESC BEQ A2ACIDRIVER ;Start from scratch if ESC! CMP #CR BNE RB_ACINEXTCHAR ;Read keys until CR LDX #-1 ;Initialize parse buffer pointer ;------------------------------------------------------------------------- ; Start parsing first or a new tape command ;------------------------------------------------------------------------- RB_NEXTCMD LDA #0 ;Clear begin and end values STA HEX1L STA HEX1H STA HEX2L STA HEX2H RB_NEXTCHR INX ;Increment input pointer LDA IN,X ;Get next char from input line CMP #$d2 ;Read command? BEQ RB_READ ;Yes! CMP #$d7 ;Write command? BEQ RB_WRITE ;Yes! (note: CY=1) CMP #$ae ;Separator? BEQ RB_SEP ;Yes! CMP #CR ;End of line? BEQ RB_GOESC ;Escape to monitor! We're done CMP #$a0 ;Ignore spaces BEQ RB_NEXTCHR EOR #$b0 ;Map digits to 0-9 CMP #9+1 ;Is it a decimal digit? BCC RB_ACIDIG ;Yes! ADC #$88 ;Map letter "A"-"F" to $FA-$FF CMP #$FA ;Hex letter? BCC A2ACIDRIVER ;No! Character not hex! RB_ACIDIG ASL ;Hex digit to MSD of A ASL ASL ASL LDY #4 ;Shift count RB_ACIHEXSHIFT ASL ;Hex digit left, MSB to carry ROL HEX1L ;Rotate into LSD ROL HEX1H ;Rotate into MSD DEY ;Done 4 shifts? BNE RB_ACIHEXSHIFT ;No! Loop BEQ RB_NEXTCHR ;Handle next character ;------------------------------------------------------------------------- ; Return to monitor, prints \ first ;------------------------------------------------------------------------- RB_GOESC JMP ESCAPE ;Escape back to monitor ;------------------------------------------------------------------------- ; Separating . found. Copy HEX1 to Hex2. Doesn't clear HEX1!!! ;------------------------------------------------------------------------- RB_SEP LDA HEX1L ;Copy hex value 1 to hex value 2 STA HEX2L LDA HEX1H STA HEX2H BCS RB_NEXTCHR ;Always taken! ;------------------------------------------------------------------------- ; Write a block of memory to tape ;------------------------------------------------------------------------- RB_WRITE ;------------------------------------------------------------------------- ; save write start addresses ; so we can check for keyboard ; or console write sequences ; and dynamicly modify code to ; use original A1 driver ; and then revert later on ;------------------------------------------------------------------------- LDA HEX2L STA RDSTRTL LDA HEX2H STA RDSTRTH STX SAVEINDEX ;Save index in input line JSR A2_WRITECONVERT ; convert I/O to A1 compatible format ;------------------------------------------------------------------------- ; now start write of this block ;------------------------------------------------------------------------- LDA #64 ;Write 10 second header JSR RB_WHEADER RB_WRNEXT DEY ;Compensate timing for extra work LDX #0 ;Get next byte to write LDA (HEX2L,X) LDX #8*2 ;Shift 8 bits (decremented twice) RB_WBITLOOP ASL ;Shift MSB to carry JSR RB_WRITEBIT ;Write this bit BNE RB_WBITLOOP ;Do all 8 bits! JSR A2_INCADDR ;Increment address LDY #30 ;Compensate timer for extra work BCC RB_WRNEXT ;Not done yet! Write next byte RB_RESTIDX JSR A2_READCONVERT ; convert buffer to A2 I/O LDX SAVEINDEX ;Restore index in input line JMP RB_NEXTCMD ;Always taken! ;------------------------------------------------------------------------- ; For case when ACI must fit in c100-c1ff - the read function must be moved ; because the standard read doesn't fit with the extra mask instruction ; required for the Apple II hardware ;------------------------------------------------------------------------- RB_READ: ;------------------------------------------------------------------------- ; save read start addresses ; so we can check for keyboard ; or console write sequences ; and dynamicly modify code to ; use A2 driver ;------------------------------------------------------------------------- LDA HEX2L STA RDSTRTL LDA HEX2H STA RDSTRTH JSR A2_FULLCYCLE ;Wait until full cycle is detected STX SAVEINDEX ;Save index in input line LDA #22 ;Introduce some delay to allow JSR RB_WHEADER ; the tape speed to stabilize ;------------------------------------------------------------------------- ; ; This read function will optionally convert and A1 keyboard reads ; to calls to use our driver to read from A2 hardware by scanning ; read data looking for one of three code sequences ;------------------------------------------------------------------------- ; ; Normal start ; JSR A2_FULLCYCLE ;Synchronize with full cycle A2_NOTSTART LDY #28 ;Try to detect the much shorter JSR A2_CMPLEVEL ; start bit BCS A2_NOTSTART ;Start bit not detected yet! JSR A2_CMPLEVEL ;Wait for 2nd phase of start bit LDY #53 ;Set threshold value in middle A2_RDBYTE LDX #8 ;Receiver 8 bits A2_RDBIT PHA JSR A2_FULLCYCLE ;Detect a full cycle PLA ROL ;Roll new bit into result LDY #52 ;Set threshold value in middle DEX ;Decrement bit counter BNE A2_RDBIT ;Read next bit! STA (HEX2L,X) ;Save new byte JSR A2_INCADDR ;Increment address LDY #46 ;Compensate threshold with workload BCC A2_RDBYTE ;Do next byte if not done yet! JMP RB_RESTIDX ;------------------------------------------------------------------------- ; Write header to tape ; ; The header consists of an asymmetric cycle, starting with one phase of ; approximately (66+47)x5=565us, followed by a second phase of ; approximately (44+47)x5=455us. ; Total cycle duration is approximately 1020us ~ 1kHz. The actual ; frequencywill be a bit lower because of the additional workload between ; the twoloops. ; The header ends with a short phase of (30+47)x5=385us and a normal ; phase of (44+47)x5=455us. This start bit must be detected by the read ; routine to trigger the reading of the actual data. ;------------------------------------------------------------------------- RB_WHEADER RB_HCOUNT LDY #72 ;Extra long delay JSR RB_WDELAY ;CY is constantly 1, writing a 1 BNE RB_HCOUNT ;Do this 64 * 256 time! ADC #-2 ;Decrement A (CY=1 all the time) BCS RB_HCOUNT ;Not all done! LDY #32 ;Write a final short bit (start) ;------------------------------------------------------------------------- ; Write a full bit cycle ; ; Upon entry Y contains a compensated value for the first phase of 0 ; bit length. All subsequent loops don't have to be time compensated. ;------------------------------------------------------------------------- RB_WRITEBIT JSR RB_WDELAY ;Do two equal phases LDY #47 ;Load 250us counter - compensation RB_WDELAY DEY ;Delay 250us (one phase of 2kHz) BNE RB_WDELAY BCC RB_WRITE1 ;Write a '1' (2kHz) LDY #50 ;Additional delay for '0' (1kHz) RB_WDELAY0 DEY ; (delay 250us) BNE RB_WDELAY0 RB_WRITE1 LDY FLIP ;Flip the output bit LDY #46 ;Reload 250us cntr (compensation) DEX ;Decrement bit counter RTS ;------------------------------------------------------------------------- ; Wait for FULL cycle (cmplevel- waits till level transisiton) ;------------------------------------------------------------------------- A2_FULLCYCLE JSR A2_CMPLEVEL ;Wait for two level changes A2_CMPLEVEL DEY ;Decrement time counter LDA TAPEIN ;Get Tape In data ; the next instruction must be added for apple II AND #$80 ;CLEAR floating bits CMP LASTSTATE ;Same as before? BEQ A2_CMPLEVEL ;Yes! STA LASTSTATE ;Save new data CPY #128 ;Compare threshold RTS ;------------------------------------------------------------------------- ; Increment current address and compare with last address ;------------------------------------------------------------------------- A2_INCADDR LDA HEX2L ;Compare current address with CMP HEX1L ; end address LDA HEX2H SBC HEX1H INC HEX2L ;And increment current address BNE A2_NOCARRY ;No carry to MSB! INC HEX2H A2_NOCARRY RTS ;------------------------------------------------------------------------- ; one block read ; modify A1 code that touches PIA to A2 version as it is loaded ; these are keyboard and display functions ; ; APPLE 1 version ;ACIWAITCHAR ; ad 11 d0 LDA KBDCR ;Wait for key press ; 30 FB BPL ACIWAITCHAR ;No key yet! ; ad 10 d0 LDA KBD ;Clear strobe ; converted to our version ; 20 3a d1 JSR A2GETCHAR ; ea NOP ; ea NOP ; ea NOP ; ea NOP ; ea NOP ; ; instead if BPL is not present or has mismatching branch offset ; ad 11 d0 LDA KBDCR ; is converted to ; ad 00 c0 LDA KBD ; ; ; ; ad 10 d0 LDA KBD ; is converted to ; 8d 10 c0 STA KBDCR ;Clear strobe ; ; finally there are some cases where presence of character is ; determined with the bit command ; 2c 11 d0 BIT KBDCR ; is converted to ; 2c 00 c00 BIT KBD ; ; ; ;------------------------------------------------------------------------- CNVRTTERM EQU $77 ; UNIQUE CHAR NEEDED TO TERMINATE CONVERSION STRINGS A2_READCONVERT LDA #0 ; RDCONVERT can be turned off CMP RDCONVERT BNE A2_READCONVERTDONE ; not zero, then skip conversion LDX #0 LDY #0 ; ; check next string through all of memory ; A2_READCONVERT_1 ; for this string, scan all of read memory STY savey ;save compare string starting point LDA CNVRT_IN,Y CMP #CNVRTTERM ; Termination character? BEQ A2_READCONVERTDONE ;all done checking, exit LDA RDSTRTL ; start of memory load STA HEX2L LDA RDSTRTH STA HEX2H ; ; restart currnet string ; A2_READCONVERT_8 LDA HEX2L ;save memory starting point STA HEX2LB LDA HEX2H STA HEX2HB LDY savey A2_READCONVERT1 LDA (HEX2L),X ; fetch byte from memory CMP CNVRT_IN,Y ; compare BEQ A2_READCONVERT1_2 ; this byte does match, process ; ; mo match, restart match string ; JSR A2_INCADDR BCC A2_READCONVERT_8 ; not end of memory - restart scan for current string ; ; end of memory block - go to next string ; A2_READCONVERT1_3 INY LDA CNVRT_IN,Y CMP #CNVRTTERM ; Termination character? BNE A2_READCONVERT1_3 ; not end of block, keep looking INY ; found end, move to start of next block JMP A2_READCONVERT_1 ; rescan memory with next string ; ; match - keep going until mismatch or end of string ; A2_READCONVERT1_2 INY LDA CNVRT_IN,Y CMP #CNVRTTERM ; Termination character? BEQ A2_READCONVERT3 ; end of string - this is match do substitute JSR A2_INCADDR ; next memory address BCC A2_READCONVERT1 ; not done - keep scanning JMP A2_READCONVERT1_3 ; not a complete match - try next string ;------------------------------------------------------------------------- ; Finished with READ ;------------------------------------------------------------------------- A2_READCONVERTDONE RTS ;------------------------------------------------------------------------- ; Substitute string here ;------------------------------------------------------------------------- A2_READCONVERT3 ; match - substitute here LDY savey LDA HEX2LB ;restore memory starting point STA HEX2L LDA HEX2HB STA HEX2H A2_READCONVERT4 LDA CNVRT_IN,Y CMP #CNVRTTERM ; Termination character? BEQ A2_READCONVERT_8 ; done with sustibute, continue checking LDA CNVRT_OUT,Y INY STA (HEX2L),X JSR A2_INCADDR JMP A2_READCONVERT4 ;------------------------------------------------------------------------- ; conversion strings ; IN(what we are looking for ; OUT (what we change it to) ;------------------------------------------------------------------------- CNVRT_IN CI1 LDA $d011 BPL CI1 LDA $d010 DC.b CNVRTTERM LDA $d011 DC.b CNVRTTERM LDA $d010 DC.b CNVRTTERM BIT $d011 DC.b CNVRTTERM CI2 BIT $D012 BMI CI2 STA $D012 DC.b CNVRTTERM IFCONST BLD4RAM JSR $FFEF DC.b CNVRTTERM ENDIF DC.b CNVRTTERM CNVRT_OUT JSR A2GETCHAR NOP NOP NOP NOP NOP DC.b CNVRTTERM LDA KBD DC.b CNVRTTERM STA KBDCR DC.b CNVRTTERM BIT KBD DC.b CNVRTTERM JSR ECHO NOP NOP NOP NOP NOP DC.b CNVRTTERM IFCONST BLD4RAM JSR ECHO DC.b CNVRTTERM ENDIF DC.b CNVRTTERM ;------------------------------------------------------------------------- ; one block write ; undo read convert when writing to tape so tape ; can be loaded and run on a real actual A1 ;------------------------------------------------------------------------- A2_WRITECONVERT LDA #0 ; RDCONVERT can be turned off CMP RDCONVERT BNE A2_WRITECONVERTDONE ; not zero, then skip conversion LDX #0 LDY #0 ; ; check next string through all of memory ; A2_WRITECONVERT_1 ; for this string, scan all of read memory STY savey ;save compare string starting point LDA CNVRT_OUT,Y CMP #CNVRTTERM ; Termination character? BEQ A2_WRITECONVERTDONE ;all done checking, exit LDA RDSTRTL ; reset block address STA HEX2L LDA RDSTRTH STA HEX2H ; ; restart currnet string ; A2_WRITECONVERT_8 LDA HEX2L ;save memory starting point STA HEX2LB LDA HEX2H STA HEX2HB LDY savey A2_WRITECONVERT1 LDA (HEX2L),X ; fetch byte from memory CMP CNVRT_OUT,Y ; compare BEQ A2_WRITECONVERT1_2 ; this byte does match, process ; ; mo match, restart match string ; JSR A2_INCADDR BCC A2_WRITECONVERT_8 ; not end of memory - restart scan for current string ; ; end of memory block - go to next string ; A2_WRITECONVERT1_3 INY LDA CNVRT_OUT,Y CMP #CNVRTTERM ; Termination character? BNE A2_WRITECONVERT1_3 ; not end of block, keep looking INY ; found end, move to start of next block JMP A2_WRITECONVERT_1 ; rescan memory with next string ; ; match - keep going until mismatch or end of string ; A2_WRITECONVERT1_2 INY LDA CNVRT_OUT,Y CMP #CNVRTTERM ; Termination character? BEQ A2_WRITECONVERT3 ; end of string - this is match do substitute JSR A2_INCADDR ; next memory address BCC A2_WRITECONVERT1 ; not done - keep scanning JMP A2_WRITECONVERT1_3 ; not a complete match - try next string ;------------------------------------------------------------------------- ; Finished with WRITE CONVERSION ;------------------------------------------------------------------------- A2_WRITECONVERTDONE LDA RDSTRTL ; reset block address STA HEX2L LDA RDSTRTH STA HEX2H RTS ;------------------------------------------------------------------------- ; Substitute string here ;------------------------------------------------------------------------- A2_WRITECONVERT3 ; match - substitute here LDY savey LDA HEX2LB ;restore memory starting point STA HEX2L LDA HEX2HB STA HEX2H A2_WRITECONVERT4 LDA CNVRT_OUT,Y CMP #CNVRTTERM ; Termination character? BEQ A2_WRITECONVERT_8 ; done with sustibute, continue checking LDA CNVRT_IN,Y INY STA (HEX2L),X JSR A2_INCADDR JMP A2_WRITECONVERT4 ;------------------------------------------------------------------------- ; output driver - uses hires memory ;------------------------------------------------------------------------- A2GETCHAR: JMP (READVECTOR) ;Allow user override of default get char function A2GETCHAR2: JSR TOGGLE LDA KBD ;Wait for key press BPL A2GETCHAR ;No key yet! STA KBDCR ;Clear strobe CMP #$88 ; left arrow BNE A2_GC_NOT_BS ; brnch no LDA #BS ; convert to _ A2_GC_NOT_BS: CMP #$95 ; right arrow BNE A2_GC_RET ; no, exit JSR CLEAR ; yes, clear screen and JMP A2GETCHAR ; get next char (this is a special HW emulation ; function so skip call to READVECTOR) A2_GC_RET: RTS ;------------------------------------------------------------------------- ; output driver - uses hires memory ;------------------------------------------------------------------------- ;;; Magic Numbers SCRINIT EQU $f0 PG0SAVEFLG EQU $f0 ;;; Definitions HRPG1 EQU $C054 HRPG2 EQU $C055 LORES EQU $C056 HIRES EQU $C057 TXTCLR EQU $C050 TXTMOD EQU $C051 MIXCLR EQU $C052 GETCHAR EQU $FD0C ;;; Page Zero Temps (8 locations reserved) TRGLOW EQU $00 TRGHIGH EQU $01 SRCLOW EQU $02 SRCHIGH EQU $03 CNT2 EQU $05 CNT3 EQU $06 ; Last location of low res LASTLOCATION EQU $7F8 ;;; Entry point for testing START JSR INIT L0 JSR GETCHAR JSR PUTCH JMP L0 BRK ;;; Move the cursor MVCSR INC CURCOL LDA CURCOL CMP #40 BPL NXTROW MD LDA #0 CMP TURBO BNE MR LDY #0 ML0 LDX #12 ; speed fine tuning ML1 INX BNE ML1 INY CPY #$9 BNE ML0 MR RTS NXTROW LDA #0 STA CURCOL LDA CURROW ; don't increment current row until in case ; we are already at bottom of screen CMP #23 ; if a reset comes in, it could leave us on an illegal row BMI NXTROW2 JSR SCROLL ; scrolling bottom line, do not advance CURROW JMP MD NXTROW2 INC CURROW ; not at bottom of screen advance to next row (CURROW) JMP MD ;;; Toggle the cursor TOGGLE INC COUNTER0 BNE DT INC COUNTER1 BNE DT PHA LDA #CNTSTRT STA COUNTER1 ; ; if screen has not been cleared- toggle betweeen hi res pages ; LDA SCRNCLRD BMI TOGGLE2 ; ; toggle from hi-res to low-res ; LDA CURSOR BNE TOGGLE1 LDA #32 STA CURSOR LDA HRPG1 JMP TOGGLE4 TOGGLE1 LDA #0 STA CURSOR LDA HRPG2 JMP TOGGLE4 ; ; else toggle cursor ; TOGGLE2 STX savex STY savey JSR SAVPG0 LDA CURSOR BNE SETSPC LDA #32 JMP DRWCUR SETSPC LDA #0 DRWCUR STA CURSOR LDX CURCOL LDY CURROW JSR GETBLOK LDX CURSOR JSR GETCHB JSR DRAWCH JSR LODPG0 LDX savex LDY savey TOGGLE4 PLA DT RTS ;;; Scrolls the screen at the end SCROLL ;------------------------------------------------------------------------------------------------------------ ; ; HIRES is organized ; into three blocks, offset by 0x28 bytes each, starting at 2000 ; each block holds 8 lines of text, offset by 0x80 bytes ; each line of text is split into 8 rows of pixels offset by 0x400 bytes ; ; this function starts at second from top row copy all pixels to row above it and continues down the screen ; ;------------------------------------------------------------------------------------------------------------ ; first block -set up starting addresses ; ldx #0 ;index into graphics table - starts at zero ; top loop - 24 lines of characters per page - copy bottom 23 lines (first line scrolls off top) LDA #23 STA CNT2 ; ; next line of text ; scr1 LDA PG1ROWS,x ; target STA TRGHIGH inx LDA PG1ROWS,x ; target STA TRGLOW inx LDA PG1ROWS,x ; src STA SRCHIGH inx LDA PG1ROWS,x ; scr STA SRCLOW DEX ;next pass target is current source LDA #8 STA CNT3 JMP scr2.1 ; ; adjust address to next line of pixels ; scr2 LDA #$4 CLC ADC SRCHIGH STA SRCHIGH LDA #$4 CLC ADC TRGHIGH STA TRGHIGH scr2.1 LDY #39 ;copy 40 characters that make up a line of pixels scr3 LDA (SRCLOW),y STA (TRGLOW),y DEY BPL scr3 ; repeat for 40 characters that make line of pixels DEC CNT3 BNE scr2 ; done with this line of pixels =- goto to next liine of pixels DEC CNT2 BNE scr1 ; done with this line of characters - goto next line of chars JSR CLEAR_LINE RTS ; ; clear line - X contains line # ; CLEAR_LINE LDA PG1ROWS,x ; target (was last source) STA TRGHIGH inx LDA PG1ROWS,x ; target STA TRGLOW LDA #8 STA CNT3 JMP CL4.1 ; ; adjust address to next line of pixels ; CL4 LDA #$4 CLC ADC TRGHIGH STA TRGHIGH CL4.1 LDY #39 lda #$0 ;copy 40 characters that make up a line of pixels CL5 STA (TRGLOW),y DEY BPL CL5 ; repeat for 40 characters that make line of pixels DEC CNT3 BNE CL4 ; done with this line of pixels =- goto to next liine of pixels RTS ;;; Initialize the graphics system, set cursor and clear the screen INIT ; ; Initialize default keyboard in and video out routines LDA #PUTCH2 STA WRITVECTOR+1 LDA #A2GETCHAR2 STA READVECTOR+1 LDA TURBO ;clear computer driven turbo mode AND #TURBOUSR ;but save user turbo mode STA TURBO ; ; reset could have occurred during video driver operations ; attempt to restore page 0 if possible ; there is a case where we were in the process of saving ; or restoring page zero variables when reset occurred ; we cannot recover from that case ; JSR LODPG0 ;restore page zero variables ; ; determine whether we should emulate power up screen ; LDA #SCRINIT CMP POWERUPINIT ;have we initialized power up screen BNE INITSCREEN ;no, let's do it CMP SCRNCLRD ;has user cleared the screen? BNE INITEXIT ;no, leave graphics mode alone ; ; clear screen already done - set HGR PG 2 mode ; STA HIRES ; set high res pg 2 graphics mode STA HRPG2 ; if this is power up, this will be changed below STA TXTCLR STA MIXCLR INITEXIT RTS ; ; initialize lowres page 1 as startup screen ; INITSCREEN LDA #$04 ;START ADDRESS STA HEX2H LDA #$0 STA HEX2L LDA #$0B ;END ADDRESS STA HEX1H LDA #$F8 STA HEX1L LDY #0 INIT1 LDA #$DF ; underbar STA (HEX2L),y JSR A2_INCADDR BCS INIT2 LDA #$08 ; check address range <800 use at sign BIT HEX2H ; >800 use space BNE INIT1_1 LDA #$C0 ; at sign BMI INIT1_2 INIT1_1 LDA #$A0 ; space INIT1_2 STA (HEX2L),y ; save it JSR A2_INCADDR BCC INIT1 INIT2 LDA #0 STA SCRNCLRD ; now indicate that screen has been not cleared STA CURROW STA CURCOL STA TURBO ; default not turbo mode STA RDCONVERT ; default convert cassette reads LDA #CNTSTRT STA COUNTER1 STA LORES ; set lowres pg 1 graphics mode for start up screen STA HRPG1 ; use page 1 STA TXTMOD STA MIXCLR LDA #SCRINIT STA POWERUPINIT ; indicate power on init done RTS ;;; Clears hires page 1 CLEAR STY savey STX savex JSR SAVPG0 LDX #0 CLEAR2 JSR CLEAR_LINE INX CPX #48 BNE CLEAR2 ;; page cleared LDA #SCRINIT STA SCRNCLRD ;indicate screen cleared STA HIRES ; set high res pg 2 graphics mode STA HRPG2 STA TXTCLR STA MIXCLR LDA #$00 ; ; cursor to top left ; STA CURROW STA CURCOL JSR LODPG0 LDY savey LDX savex RTS ;;; Prints character from A to the screen PUTCH JMP (WRITVECTOR) ;allow user override of default video out routine PUTCH2: PHA STY savey STX savex LDY SCRNCLRD BPL PUTCH_DROP ;if plus, screen has not been cleared, so drop JSR SAVPG0 ;; drop the high bit AND #$7F ;; check for return CMP #$0D BEQ ENTERKY ;; it's a regular key JSR GETCODE STA CHAR ;; get the block address LDX CURCOL LDY CURROW JSR GETBLOK ;; get block bytes LDX CHAR JSR GETCHB ;; render the char to the block JSR DRAWCH ;; restore state and exit PD JSR MVCSR JSR LODPG0 PUTCH_DROP LDY savey LDX savex PLA RTS ENTERKY ; print spaces until end of line (use turbo mode) LDA TURBO ORA #TURBOCMP ; set computer turbo mode STA TURBO ; turbo mode to clear end of line ENTERKY1 LDY CURROW LDX CURCOL CPX #40 BEQ ENTERKY_EXIT JSR GETBLOK LDX #0 ; space key JSR GETCHB JSR DRAWCH INC CURCOL JMP ENTERKY1 ENTERKY_EXIT LDA TURBO AND #TURBOUSR ; reset computer turbo mode, saving user mode STA TURBO ; JMP PD ;;; Draws character to block DRAWCH LDX #0 L6 TXA TAY LDA (SRCLOW),Y LDY #0 STA (TRGLOW),Y INX LDA TRGHIGH CLC ADC #$4 STA TRGHIGH CPX #8 BMI L6 RTS ;;; Get byte for char in X GETCHB LDY #SPCODE STY SRCHIGH L5 CPX #0 BEQ D DEX LDA #8 CLC ADC SRCLOW STA SRCLOW BCS AC JMP L5 AC LDA #0 ADC SRCHIGH STA SRCHIGH JMP L5 D RTS ;;; Gets the block address at X,Y GETBLOK TYA JSR GETROW ;; add the column TXA CLC ADC TRGLOW STA TRGLOW BCS A1 RTS A1 LDA #0 ADC TRGHIGH RTS ;;; Gets the row (A) address GETROW ASL ; multiply row by two, there are two bytes per address TAY LDA PG1ROWS,Y STA TRGHIGH INY LDA PG1ROWS,Y STA TRGLOW RTS ;;; Converts ASCII code to table index GETCODE SEC SBC #$20 BMI NC CMP #$40 BPL NC RTS NC LDA #0 RTS ;;; Store page zero data SAVPG0 PHA STX TMP1 LDA #PG0SAVEFLG ;check saved flag CMP PG0SAVD BEQ SD ;already saved, just exit LDX #0 L1 LDA $00,X STA TMPG0,X INX CPX #$8 BNE L1 LDA #PG0SAVEFLG ;set save flag to saved after completely saved STA PG0SAVD ;this way, if a reset comes in before we are done ;we will not corrupt zero page ;as the reset code restores zero page if flag set SD LDX TMP1 PLA RTS ;;; Restore page zero data LODPG0 PHA STX TMP1 LDA #PG0SAVEFLG ;checked saved flag CMP PG0SAVD BNE LD ;not saved, exit LDX #0 L2 LDA TMPG0,X STA $00,X INX CPX #$8 BNE L2 LDA #<(~PG0SAVEFLG) ;clear saved flag to not saved after completely restored ;this way, if a reset comes in before we are done STA PG0SAVD ;as the reset code restores zero page if flag set LD LDX TMP1 PLA RTS ;;; tables PG1ROWS HEX 4000 4080 4100 4180 4200 4280 4300 4380 4028 40A8 4128 41A8 4228 42A8 4328 43A8 4050 40D0 4150 41D0 4250 42D0 4350 43D0 SPCODE HEX 00 00 00 00 00 00 00 00 EXPCODE HEX 00 08 08 08 08 08 00 08 QUOCODE HEX 00 14 14 14 00 00 00 00 NUMCODE HEX 00 14 14 3e 14 3e 14 14 STRCODE HEX 00 08 3c 0a 1c 28 1e 08 PERCODE HEX 00 06 26 10 08 04 32 30 AMPCODE HEX 00 04 0a 0a 04 2a 12 2c SQCODE HEX 00 08 08 08 00 00 00 00 RPCODE HEX 00 08 04 02 02 02 04 08 LPCODE HEX 00 08 10 20 20 20 10 08 STACODE HEX 00 08 2a 1c 08 1c 2a 08 PLSCODE HEX 00 00 08 08 3e 08 08 00 CMACODE HEX 00 00 00 00 00 08 08 04 MINCODE HEX 00 00 00 00 3e 00 00 00 DOTCODE HEX 00 00 00 00 00 00 00 08 FSCODE HEX 00 00 20 10 08 04 02 00 0CODE HEX 00 1c 22 32 2a 26 22 1c 1CODE HEX 00 08 0c 08 08 08 08 1c 2CODE HEX 00 1c 22 20 18 04 02 3e 3CODE HEX 00 3e 20 10 18 20 22 1c 4CODE HEX 00 10 18 14 12 3e 10 10 5CODE HEX 00 3e 02 1e 20 20 22 1c 6CODE HEX 00 38 04 02 1e 22 22 1c 7CODE HEX 00 3e 20 10 08 04 04 04 8CODE HEX 00 1c 22 22 1c 22 22 1c 9CODE HEX 00 1c 22 22 3c 20 10 0e COLCODE HEX 00 00 00 08 00 08 00 00 SEMCODE HEX 00 00 00 08 00 08 08 04 LTCODE HEX 00 10 08 04 02 04 08 10 EQCODE HEX 00 00 00 3e 00 3e 00 00 GTCODE HEX 00 04 08 10 20 10 08 04 QESCODE HEX 00 1c 22 10 08 08 00 08 ATCODE HEX 00 1C 22 2A 3A 1A 02 3C ACODE HEX 00 08 14 22 22 3e 22 22 BCODE HEX 00 1e 22 22 1e 22 22 1e CCODE HEX 00 1c 22 02 02 02 22 1c DCODE HEX 00 1e 22 22 22 22 22 1e ECODE HEX 00 3e 02 02 1e 02 02 3e FCODE HEX 00 3e 02 02 1e 02 02 02 GCODE HEX 00 3c 02 02 02 32 22 3c HCODE HEX 00 22 22 22 3e 22 22 22 ICODE HEX 00 1c 08 08 08 08 08 1c JCODE HEX 00 20 20 20 20 20 22 1c KCODE HEX 00 22 12 0a 06 0a 12 22 LCODE HEX 00 02 02 02 02 02 02 3e MCODE HEX 00 22 36 2a 2a 22 22 22 NCODE HEX 00 22 22 26 2a 32 22 22 OCODE HEX 00 1c 22 22 22 22 22 1c PCODE HEX 00 1e 22 22 1e 02 02 02 QCODE HEX 00 1c 22 22 22 2a 12 2c RCODE HEX 00 1e 22 22 1e 0a 12 22 SCODE HEX 00 1c 22 02 1c 20 22 1c TCODE HEX 00 3e 08 08 08 08 08 08 UCODE HEX 00 22 22 22 22 22 22 1c VCODE HEX 00 22 22 22 22 22 14 08 WCODE HEX 00 22 22 22 2a 2a 36 22 XCODE HEX 00 22 22 14 08 14 22 22 YCODE HEX 00 22 22 14 08 08 08 08 ZCODE HEX 00 3e 20 10 08 04 02 3e LBCODE HEX 00 3e 06 06 06 06 06 3e BSCODE HEX 00 00 02 04 08 10 20 00 RBCODE HEX 00 3e 30 30 30 30 30 3e CRTCODE HEX 00 00 00 08 14 22 00 00 UNDCODE HEX 00 00 00 00 00 00 00 3e ;------------------------------------------------------------------------ ; BASIC SUPPORT FUNCTIONs ; peek and poke of the PIA is emulated ; using A2 hardware drivers ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ ; POKE of D012 is emulated by calling ; A2 putchar routine ;------------------------------------------------------------------------ ACC EQU $ce ; must be same as basic "acc" A2POKE JSR getbyte LDA ACC PHA JSR get16bit LDA #$D0 CMP ACC+1 BEQ A2POKE2 A2POKE1 PLA STA (ACC),Y RTS A2POKE2 LDA #$12 CMP ACC BNE A2POKE1 ;------------------------------------------------------------------------ ; Poke D012 == A2 ECHO CALL ;------------------------------------------------------------------------ PLA JMP ECHO ;------------------------------------------------------------------------ ; PEEK of D012, D011 and D010 is emulated by using ; equivalent A2 functionality ;------------------------------------------------------------------------ A2PEEK JSR get16bit LDA #$D0 CMP ACC+1 BEQ A2PEEK3 A2PEEK1 LDA (ACC),Y A2PEEK2 STY syn_stk_l+31,X JMP push_ya_noun_stk ;------------------------------------------------------------------------ ; Peek D012 == determine if terminal is ready for output ; in the A2 memory mapped video driver case - the answer ; is always yes -so return positive number(or zero in this case) ;------------------------------------------------------------------------ A2PEEK3 LDA #$12 CMP ACC BNE A2PEEK4 LDA #$00 JMP A2PEEK2 ;------------------------------------------------------------------------ ; Peek D011 == A2 read c000 (does keyboard have char, yes if MSB set) ;------------------------------------------------------------------------ A2PEEK4 LDA #$11 CMP ACC BNE A2PEEK5 LDA KBD JMP A2PEEK2 ;------------------------------------------------------------------------ ; Peek D010 == A2 getchar (also clears strobe) ;------------------------------------------------------------------------ A2PEEK5 LDA #$10 CMP ACC BNE A2PEEK1 LDA KBD STA KBDCR JMP A2PEEK2 IFCONST BLD4RAM STORAGE ENDIF ENDIF ; end of A2 driver code ;------------------------------------------------------------------------ ; VERSION ;------------------------------------------------------------------------ IFCONST BLD4RAM ORG $6FFE ELSE ORG $DFFE ENDIF DC.w $0105 ;------------------------------------------------------------------------ ; BASIC ;------------------------------------------------------------------------ INCLUDE a1basic-universal.asm ;------------------------------------------------------------------------- ; ; The WOZ Monitor for the Apple 1 ; Written by Steve Wozniak 1976 ; ;------------------------------------------------------------------------- IFNCONST BLD4RAM ORG $FF00 ENDIF ;------------------------------------------------------------------------- ; Memory declaration ;------------------------------------------------------------------------- XAML EQU $24 ;Last "opened" location Low XAMH EQU $25 ;Last "opened" location High STL EQU $26 ;Store address Low STH EQU $27 ;Store address High L EQU $28 ;Hex value parsing Low H EQU $29 ;Hex value parsing High YSAV EQU $2A ;Used to see if hex value is given MODE EQU $2B ;$00=XAM, $7F=STOR, $AE=BLOCK XAM IN EQU $0200 ;Input buffer IFCONST BLD4APPLE1 MONDSP EQU $D012 ;PIA.B display output register MONDSPCR EQU $D013 ;PIA.B display control register ENDIF ; KBD b7..b0 are inputs, b6..b0 is ASCII input, b7 is constant high ; Programmed to respond to low to high KBD strobe ; DSP b6..b0 are outputs, b7 is input ; CB2 goes low when data is written, returns high when CB1 goes high ; Interrupts are enabled, though not used. KBD can be jumpered to IRQ, ; whereas DSP can be jumpered to NMI. ;------------------------------------------------------------------------- ; Let's get started ; ; Remark the RESET routine is only to be entered by asserting the RESET ; line of the system. This ensures that the data direction registers ; are selected. ;------------------------------------------------------------------------- RESET CLD ;Clear decimal arithmetic mode CLI IFNCONST BLD4APPLE1 JSR INIT ;init display driver ENDIF LDY #$7f ;Mask for DSP data direction reg IFCONST BLD4APPLE1 STY MONDSP ; (DDR mode is assumed after reset) ENDIF LDA #$a7 ;KBD and DSP control register mask IFCONST BLD4APPLE1 STA KBDCR ;Enable interrupts, set CA1, CB1 for STA MONDSPCR ; positive edge sense/output mode. ELSE NOP NOP NOP NOP NOP NOP ENDIF ; Program falls through to the GETLINE routine to save some program bytes ; Please note that Y still holds $7F, which will cause an automatic Escape ;------------------------------------------------------------------------- ; The GETLINE process ;------------------------------------------------------------------------- NOTCR CMP #BS ;Backspace key? BEQ BACKSPACE ;Yes CMP #ESC ;ESC? BEQ ESCAPE ;Yes INY ;Advance text index BPL NEXTCHAR ;Auto ESC if line longer than 127 ESCAPE LDA #PROMPT ;Print prompt character JSR ECHO ;Output it. GETLINE LDA #CR ;Send CR JSR ECHO LDY #0+1 ;Start a new input line BACKSPACE DEY ;Backup text index BMI GETLINE ;Oops, line's empty, reinitialize NEXTCHAR IFCONST BLD4APPLE1 LDA KBDCR ;Wait for key press BPL NEXTCHAR ;No key yet! LDA KBD ;Clear strobe ELSE JSR A2GETCHAR NOP NOP NOP NOP NOP ENDIF STA IN,Y ;Add to text buffer JSR ECHO ;Display character CMP #CR BNE NOTCR ;It's not CR! ; Line received, now let's parse it LDY #-1 ;Reset text index LDA #0 ;Default mode is XAM TAX ;X=0 SETSTOR ASL ;Leaves $7B if setting STOR mode SETMODE STA MODE ;Set mode flags BLSKIP INY ;Advance text index NEXTITEM LDA IN,Y ;Get character CMP #CR BEQ GETLINE ;We're done if it's CR! CMP #$AE ;"." BCC BLSKIP ;Ignore everything below "."! BEQ SETMODE ;Set BLOCK XAM mode ("." = $AE) CMP #$BA ;":" BEQ SETSTOR ;Set STOR mode! $BA will become $7B CMP #$d2 ;"R" BEQ RUN ;Run the program! Forget the rest STX L ;Clear input value (X=0) STX H STY YSAV ;Save Y for comparison ; Here we're trying to parse a new hex value NEXTHEX LDA IN,Y ;Get character for hex test EOR #$B0 ;Map digits to 0-9 CMP #9+1 ;Is it a decimal digit? BCC DIG ;Yes! ADC #$88 ;Map letter "A"-"F" to $FA-FF CMP #$FA ;Hex letter? BCC NOTHEX ;No! Character not hex DIG ASL ASL ;Hex digit to MSD of A ASL ASL LDX #4 ;Shift count HEXSHIFT ASL ;Hex digit left, MSB to carry ROL L ;Rotate into LSD ROL H ;Rotate into MSD's DEX ;Done 4 shifts? BNE HEXSHIFT ;No, loop INY ;Advance text index BNE NEXTHEX ;Always taken NOTHEX CPY YSAV ;Was at least 1 hex digit given? BEQ ESCAPE ;No! Ignore all, start from scratch BIT MODE ;Test MODE byte BVC NOTSTOR ;B6=0 is STOR, 1 is XAM or BLOCK XAM ; STOR mode, save LSD of new hex byte LDA L ;LSD's of hex data STA (STL,X) ;Store current 'store index'(X=0) INC STL ;Increment store index. BNE NEXTITEM ;No carry! INC STH ;Add carry to 'store index' high TONEXTITEM JMP NEXTITEM ;Get next command item. ;------------------------------------------------------------------------- ; RUN user's program from last opened location ;------------------------------------------------------------------------- RUN JMP (XAML) ;Run user's program ;------------------------------------------------------------------------- ; We're not in Store mode ;------------------------------------------------------------------------- NOTSTOR BMI XAMNEXT ;B7 = 0 for XAM, 1 for BLOCK XAM ; We're in XAM mode now LDX #2 ;Copy 2 bytes SETADR LDA L-1,X ;Copy hex data to STA STL-1,X ; 'store index' STA XAML-1,X ; and to 'XAM index' DEX ;Next of 2 bytes BNE SETADR ;Loop unless X = 0 ; Print address and data from this address, fall through next BNE. NXTPRNT BNE PRDATA ;NE means no address to print LDA #CR ;Print CR first JSR ECHO LDA XAMH ;Output high-order byte of address JSR PRBYTE LDA XAML ;Output low-order byte of address JSR PRBYTE LDA #":" ;Print colon JSR ECHO PRDATA LDA #" " ;Print space JSR ECHO LDA (XAML,X) ;Get data from address (X=0) JSR PRBYTE ;Output it in hex format XAMNEXT STX MODE ;0 -> MODE (XAM mode). LDA XAML ;See if there's more to print CMP L LDA XAMH SBC H BCS TONEXTITEM ;Not less! No more data to output INC XAML ;Increment 'examine index' BNE MOD8CHK ;No carry! INC XAMH MOD8CHK LDA XAML ;If address MOD 8 = 0 start new line AND #$07 BPL NXTPRNT ;Always taken. ;------------------------------------------------------------------------- ; Subroutine to print a byte in A in hex form (destructive) ;------------------------------------------------------------------------- PRBYTE PHA ;Save A for LSD LSR LSR LSR ;MSD to LSD position LSR JSR PRHEX ;Output hex digit PLA ;Restore A ; Fall through to print hex routine ;------------------------------------------------------------------------- ; Subroutine to print a hexadecimal digit ;------------------------------------------------------------------------- PRHEX AND #$0F ;Mask LSD for hex print ORA #"0" ;Add "0" CMP #"9"+1 ;Is it a decimal digit? BCC ECHO ;Yes! output it ADC #6 ;Add offset for letter A-F ; Fall through to print routine ;------------------------------------------------------------------------- ; Subroutine to print a character to the terminal ;------------------------------------------------------------------------- ECHO IFCONST BLD4APPLE1 BIT MONDSP ;DA bit (B7) cleared yet? BMI ECHO ;No! Wait for display ready STA MONDSP ;Output character. Sets DA RTS ELSE CMP #$20 BMI ECHO1 ORA #$80 ECHO1 JMP PUTCH ; use hi-res screen ENDIF ;------------------------------------------------------------------------- ; Vector area ;------------------------------------------------------------------------- IFNCONST BLD4RAM ORG $fff8 DC.W $0000 ;Unused, what a pity NMI_VEC DC.W $0F00 ;NMI vector RESET_VEC DC.W RESET ;RESET vector IRQ_VEC DC.W $0000 ;IRQ vector ENDIF ;-------------------------------------------------------------------------