From a1e942851fb2905a6c1e05ae8a2f471bfbb2fb26 Mon Sep 17 00:00:00 2001 From: flowenol Date: Sat, 16 Jan 2021 01:08:36 +0100 Subject: [PATCH] Added support for LOAD & SAVE commands via Apple-1 Serial Interface --- .gitignore | 2 + Makefile | 9 +- README.md | 12 +++ apple1cartridge.cfg | 7 ++ apple1serial.s | 233 ++++++++++++++++++++++++++++++++++++++++++++ applesoft-lite.s | 49 +++++----- cffa1.s | 185 ----------------------------------- replica1.cfg | 10 -- wozmon.s | 183 ---------------------------------- 9 files changed, 282 insertions(+), 408 deletions(-) create mode 100644 .gitignore create mode 100644 apple1cartridge.cfg create mode 100644 apple1serial.s delete mode 100644 cffa1.s delete mode 100644 replica1.cfg delete mode 100644 wozmon.s diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..98f5968 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.bin +*.o diff --git a/Makefile b/Makefile index 9de2564..ddcb7b6 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ -AFLAGS = -LFLAGS = -C replica1.cfg +AFLAGS = +LFLAGS = -C apple1cartridge.cfg BINFILE = applesoft-lite.bin -OBJS = applesoft-lite.o io.o cffa1.o wozmon.o +OBJS = applesoft-lite.o io.o apple1serial.o $(BINFILE): $(OBJS) ld65 $(LFLAGS) $(OBJS) -o $(BINFILE) @@ -12,7 +12,7 @@ applesoft-lite.o: applesoft-lite.s wozmon.o: wozmon.s ca65 $(AFLAGS) $< -cffa1.o: cffa1.s +apple1serial.o: apple1serial.s ca65 $(AFLAGS) $< io.o: io.s @@ -20,4 +20,3 @@ io.o: io.s clean: rm $(OBJS) $(BINFILE) - diff --git a/README.md b/README.md index 2a56ee3..bcfec42 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,15 @@ This is a stripped-down version of Applesoft BASIC (Microsoft 6502 BASIC) for the Apple-1. See https://cowgod.org/replica1/applesoft/ + +# apple1serial + +This branch has been modified to perform LOAD and SAVE operations via Apple-1 Serial interface +https://github.com/flowenol/apple1serial + +# How to use LOAD? + +The LOAD command implemented on this branch takes as an argument the length of the program +as a decimal value, e.g., + +]LOAD 1024 diff --git a/apple1cartridge.cfg b/apple1cartridge.cfg new file mode 100644 index 0000000..109313b --- /dev/null +++ b/apple1cartridge.cfg @@ -0,0 +1,7 @@ +MEMORY { + BASROM: start = $6000, size = $2000, fill = yes, file = %O; +} + +SEGMENTS { + BASIC: load = BASROM, type = ro; +} diff --git a/apple1serial.s b/apple1serial.s new file mode 100644 index 0000000..1b48e05 --- /dev/null +++ b/apple1serial.s @@ -0,0 +1,233 @@ +.setcpu "6502" +.segment "BASIC" + +.include "zeropage.s" + +.importzp ERR_SYNTAX, ERR_NOSERIAL +.import ERROR, FIX_LINKS, OUTDO +.export SerialLoad, SerialSave, SerialMenu + +SERIAL_MONITOR := $C100 +SERIAL_READY := $C000 +SERIAL_ID := $C0FC +SERIAL_API_READ := $C1EC +SERIAL_API_WRITE := $C213 + +; ZP locations get backed up here +ZPTemp := $0380 + +;End address of dump block +END_ADDR_L := $34 +END_ADDR_H := $35 + +;Begin address of dump block +BEG_ADDR_L := $36 +BEG_ADDR_H := $37 + +;String to int ZP variables +STR2INT_BUF := $38 +STR2INT_INT := $3E + +STR2INT_END := $0A + +; ---------------------------------------------------------------------------- +; See if Apple-1 Serial Interface card is present and display error if not +; ---------------------------------------------------------------------------- +CheckSerial: + ldy SERIAL_ID + cpy #'A' + bne SerialErr + ldy SERIAL_ID + 1 + cpy #'1' + bne SerialErr + ldy SERIAL_ID + 2 + cpy #'S' + bne SerialErr + ldy SERIAL_ID + 3 + cpy #'I' + bne SerialErr + rts + +SerialErr: + ldx #ERR_NOSERIAL + .byte $2C ; Bogus BIT instruction +SynErr: + ldx #ERR_SYNTAX + jmp ERROR ; Jump to Applesoft ERROR routine + + +; ---------------------------------------------------------------------------- +; Bring up the Apple-1 Serial Interface menu +; ---------------------------------------------------------------------------- +SerialMenu: + jsr CheckSerial + jsr SERIAL_MONITOR + + +; ---------------------------------------------------------------------------- +; Save program via the Apple-1 Serial Interface +; ---------------------------------------------------------------------------- +SerialSave: + jsr CheckSerial + + ldy #0 +@1: + lda END_ADDR_L,y ; Back up 4 affected bytes of the ZP + sta ZPTemp,y + iny + cpy #4 + bne @1 + + lda PRGEND ; Set up end address + sta END_ADDR_L + lda PRGEND+1 + sta END_ADDR_H + lda TXTTAB ; Set up start address + sta BEG_ADDR_L + lda TXTTAB+1 + sta BEG_ADDR_H + + jsr SERIAL_API_WRITE + jsr RestoreZPForSave ; Put the zero page back + rts + +; ---------------------------------------------------------------------------- +; Restores the 4 bytes of the ZP which were saved during SerialSave +; ---------------------------------------------------------------------------- +RestoreZPForSave: + ldy #0 +@1: + lda ZPTemp,y ; Load byte from temporary storage + sta END_ADDR_L,y ; put it back in its original location + iny + cpy #4 ; Repeat for next 11 bytes + bne @1 + rts + + +; ---------------------------------------------------------------------------- +; Read program from Apple-1 Serial Interface +; ---------------------------------------------------------------------------- +SerialLoad: + jsr CheckSerial + + ldy #0 +@1: + lda END_ADDR_L,y ; Back up first 12 bytes of the ZP + sta ZPTemp,y + iny + cpy #12 + bne @1 + +GetLength: ; Get file name from input line + dec TXTPTR + ldy #0 +@1: + jsr CHRGET ; Get next character from the input line + beq @2 ; Is it null (EOL)? + cmp #$30 + bmi LengthValidationErr + cmp #$3A + bpl LengthValidationErr + and #$0F ; Convert ASCII to hex value $00-$09 + sta STR2INT_BUF,y ; Not EOL, store it in program length string + iny + cpy #5 ; 6 chars yet? + bne @1 ; no + +@2: + cpy #0 ; Read 6 chars or EOL, did we get anything? + beq SynErr + + lda #STR2INT_END ; append integer string end marker + sta STR2INT_BUF,y + jsr String2Int + lda STR2INT_INT + bne @3 + dec STR2INT_INT+1 +@3: + dec STR2INT_INT + clc +SetLoadAddresses: + lda TXTTAB ; Compute program end address + adc STR2INT_INT ; (Add file size to program start) + sta VARTAB ; Store end address + sta END_ADDR_L + lda TXTTAB+1 + adc STR2INT_INT+1 + sta VARTAB+1 + sta END_ADDR_H + + ;jsr SerialAPISetup + lda TXTTAB ; Set up start address + sta BEG_ADDR_L + lda TXTTAB+1 + sta BEG_ADDR_H + + jsr SERIAL_API_READ + jsr RestoreZPForLoad ; Put the zero page back + + jmp FIX_LINKS +LengthValidationErr: + jsr RestoreZPForLoad + jmp SynErr +; ---------------------------------------------------------------------------- +; Restores the affected 12 bytes of the ZP which were saved during SerialLoad +; ---------------------------------------------------------------------------- +RestoreZPForLoad: + ldy #0 +@1: + lda ZPTemp,y ; Load byte from temporary storage + sta END_ADDR_L,y ; put it back in its original location + iny + cpy #12 ; Repeat for next 11 bytes + bne @1 + rts + +; ---------------------------------------------------------------------------- +; Converts the integer string to 2 byte integer +; ---------------------------------------------------------------------------- +String2Int: + lda #0 + sta STR2INT_INT+1 + lda STR2INT_BUF + sta STR2INT_INT + ldx #1 + lda STR2INT_BUF,X + cmp #STR2INT_END + bne String2IntNext + clc + rts +String2IntNext: + jsr String2IntMult10 + bcs String2IntEnd + inx + lda STR2INT_BUF,X + cmp #$0A + bne String2IntNext + clc +String2IntEnd: + rts + +String2IntMult10: + lda STR2INT_INT+1 + pha + lda STR2INT_INT + asl STR2INT_INT + rol STR2INT_INT+1 + asl STR2INT_INT + rol STR2INT_INT+1 + adc STR2INT_INT + sta STR2INT_INT + pla + adc STR2INT_INT+1 + sta STR2INT_INT+1 + asl STR2INT_INT + rol STR2INT_INT+1 + lda STR2INT_BUF,X + adc STR2INT_INT + sta STR2INT_INT + lda #0 + adc STR2INT_INT+1 + sta STR2INT_INT+1 + rts diff --git a/applesoft-lite.s b/applesoft-lite.s index 0bd5820..0beedd3 100644 --- a/applesoft-lite.s +++ b/applesoft-lite.s @@ -1,4 +1,4 @@ -; Applesoft Lite + ; Applesoft Lite ; ; Disassembled from the Apple II+ ROMs with da65 V2.12.0 ; @@ -8,20 +8,20 @@ ; Adapted for the Replica-1 by Tom Greene ; 7-May-2008 ; -; v0.4 - - +; Changed to support LOAD & SAVE via Apple-1 Serial Interface by Piotr Jaczewski +; 15-Jan-2021 +; .setcpu "6502" .segment "BASIC" .export FIX_LINKS, ERROR, INPUTBUFFER -.exportzp ERR_SYNTAX, ERR_NOCFFA +.exportzp ERR_SYNTAX, ERR_NOSERIAL .import CLS, OUTDO, CRDO, OUTSP, OUTQUES ; Imports from io.s .import KEYBOARD, GETLN, RDKEY -.import CFFALoad, CFFASave, CFFAMenu ; Imports from cffa1.s +.import SerialLoad, SerialSave, SerialMenu ; Imports from apple1serial.s .include "macros.s" .include "zeropage.s" @@ -95,9 +95,9 @@ TOKEN_ADDRESS_TABLE: .addr CLEAR - 1 ; $9B... 154... CLEAR .addr GET - 1 ; $9C... 155... GET .addr NEW - 1 ; $9D... 156... NEW - .addr CFFAMenu - 1 ; $9E... 157... MENU - .addr CFFASave - 1 ; $9F... 158... SAVE - .addr CFFALoad - 1 ; $A0... 160... LOAD + .addr SerialMenu - 1 ; $9E... 157... MENU + .addr SerialSave - 1 ; $9F... 158... SAVE + .addr SerialLoad - 1 ; $A0... 160... LOAD .addr CLS - 1 ; $A1... 161... CLS ; ---------------------------------------------------------------------------- UNFNC: .addr SGN ; $B1... 177... SGN @@ -189,9 +189,9 @@ TOKEN_NAME_TABLE: htasc "CLEAR" ; $9B... 155 htasc "GET" ; $9C... 156 htasc "NEW" ; $9D... 157 - htasc "MENU" ; $9E... 158 New tokens + htasc "MENU" ; $9E... 158 New tokens htasc "SAVE" ; $9F... 159 for - htasc "LOAD" ; $A0... 160 CFFA I/O + htasc "LOAD" ; $A0... 160 Apple-1 Serial I/O htasc "CLS" ; $A1... 161 New token to clear screen htasc "TO" ; $A2... 162 htasc "SPC(" ; $A3... 163 @@ -281,8 +281,8 @@ ERR_FRMCPX := <(*-ERROR_MESSAGES) ERR_CANTCONT := <(*-ERROR_MESSAGES) htasc "CAN'T CONT" -ERR_NOCFFA := <(*-ERROR_MESSAGES) ; New error message for CFFA1 I/O - htasc "NO CFFA" +ERR_NOSERIAL := <(*-ERROR_MESSAGES) ; New error message for Apple 1 Serial IO + htasc "NO SERIAL" ; ---------------------------------------------------------------------------- QT_ERROR: .byte " ERR" @@ -415,7 +415,7 @@ REASON: cpy FRETOP+1 ; HIGH BYTE dex bpl @2 jsr GARBAG ; MAKE AS MUCH ROOM AS POSSIBLE - ldx #TEMP1-FAC+1 ; RESTORE TEMP1 AND TEMP2 + ldx #TEMP1-FAC+1+256 ; RESTORE TEMP1 AND TEMP2 @3: pla ; AND (Y,A) sta FAC,x inx @@ -638,7 +638,7 @@ INLIN2: stx PROMPT ; dex ; bne @2 ;@3: lda #0 ; (Y,X) POINTS AT BUFFER-1 - ldx #INPUTBUFFER-1 rts @@ -751,7 +751,7 @@ PARSE: inx ; NEXT INPUT CHARACTER ; ---END OF LINE------------------ @17: sta INPUTBUFFER-3,y ; STORE ANOTHER 00 ON END dec TXTPTR+1 ; SET TXTPTR = INPUTBUFFER-1 - lda #(TOKEN_NAME_TABLE-$100) sty FAC+1 - ldy #-1 + ldy #$FF @1: dex ; SKIP KEYWORDS UNTIL REACH THIS ONE beq @3 @2: jsr GETCHR ; BUMP Y, GET CHAR FROM TABLE @@ -2961,7 +2961,7 @@ STR: jsr CHKNUM ; EXPRESSION MUST BE NUMERIC jsr FOUT1 ; CONVERT FAC TO STRING pla ; POP RETURN OFF STACK pla - lda #STACK-1 ; (WHICH=0) beq STRLIT ; ...ALWAYS, CREATE DESC & MOVE STRING @@ -4258,7 +4258,7 @@ FDIVT: beq @8 ; FAC = 0, DIVIDE BY ZERO ERROR jsr ADD_EXPONENTS inc FAC beq JOV ; OVERFLOW - ldx #-4 ; INDEX FOR RESULT + ldx #252 ; INDEX FOR RESULT lda #1 ; SENTINEL @1: ldy ARG+1 ; SEE IF FAC CAN BE SUBTRACTED cpy FAC+1 @@ -4850,7 +4850,7 @@ FOUT1: lda #'-' ; IN CASE VALUE NEGATIVE @3: lda #CON_BILLION ; TO GIVE ADJUSTMENT A HEAD START jsr FMULT - lda #-9 ; EXPONENT ADJUSTMENT + lda #247 ; EXPONENT ADJUSTMENT @4: sta TMPEXP ; 0 OR -9 ; ---------------------------------------------------------------------------- ; ADJUST UNTIL 1E8 <= (FAC) <1E9 @@ -5061,7 +5061,7 @@ FPWRT: beq EXP ; IF FAC=0, ARG^FAC=EXP(0) tya ; MAKE ARG SIGN + AS IT IS MOVED TO FAC ldy CHARAC ; INTEGRAL, SO ALLOW NEGATIVE ARG @2: jsr MFA ; MOVE ARGUMENT TO FAC - tya ; SAVE FLAG FOR NEGATIVE ARG (0=+) + tya ; SAVE FLAG FOR NEGATIVE ARG (0=+) pha jsr LOG ; GET LOG(ARG) lda #TEMP3 ; MULTIPLY BY POWER @@ -5256,7 +5256,7 @@ RND: jsr SIGN ; REDUCE ARGUMENT TO -1, 0, OR +1 ; GENERIC COPY OF CHRGET SUBROUTINE, WHICH ; IS COPIED INTO $00B1...$00C8 DURING INITIALIZATION ; -; CORNELIS BONGERS DESCRIBED SEVERAL IMPROVEMENTS +; CORNELIS BONGERS DESCRIBED SEVERAL IMPROVEMENTS ; TO CHRGET IN MICRO MAGAZINE OR CALL A.P.P.L.E. ; (I DON'T REMEMBER WHICH OR EXACTLY WHEN) ; ---------------------------------------------------------------------------- @@ -5272,7 +5272,7 @@ GENERIC_CHRGET: sec ; TEST FOR NUMERIC RANGE IN WAY THAT sbc #'0' ; CLEARS CARRY IF CHAR IS DIGIT sec ; AND LEAVES CHAR IN A-REG - sbc #-'0' + sbc #-'0'+256 @2: rts @@ -5305,7 +5305,7 @@ COLDSTART: ; ---------------------------------------------------------------------------- ; MOVE GENERIC CHRGET AND RANDOM SEED INTO PLACE ; -; NOTE THAT LOOP VALUE IS WRONG! +; NOTE THAT LOOP VALUE IS WRONG! ; THE LAST BYTE OF THE RANDOM SEED IS NOT ; COPIED INTO PAGE ZERO! ; ---------------------------------------------------------------------------- @@ -5498,4 +5498,3 @@ RESUME: lda ERRLIN ; RESTORE LINE # AND TXTPTR ldx ERRSTK ; RETRIEVE STACK PNTR AS IT WAS txs ; BEFORE STATEMENT SCANNED jmp NEWSTT ; DO STATEMENT AGAIN - diff --git a/cffa1.s b/cffa1.s deleted file mode 100644 index 8c26821..0000000 --- a/cffa1.s +++ /dev/null @@ -1,185 +0,0 @@ -; CFFA1 I/O routines for Applesoft Lite -; by Tom Greene -; 8-May-2008 - -.setcpu "6502" -.segment "BASIC" - -.include "zeropage.s" - -.importzp ERR_SYNTAX, ERR_NOCFFA -.import ERROR, FIX_LINKS -.export CFFALoad, CFFASave, CFFAMenu - -; ---------------------------------------------------------------------------- -; CFFA1 firmware addresses - -;CFFA_ID1 := $AFFC ; CFFA1 API documentation says AFFC and AFFD... -;CFFA_ID2 := $AFFD -CFFA_ID1 := $AFDC ; But the CFFA1 firmware says AFDC and AFDD... -CFFA_ID2 := $AFDD -CFFA_MENU := $9006 ; Entry point for the CFFA1 menu -CFFA_API := $900C ; Entry point for the CFFA1 API - -; ---------------------------------------------------------------------------- -; CFFA1 API functions - -CFFA1_DisplayError = $04 ; Displays error message for error value in A -CFFA1_WriteFile = $20 -CFFA1_ReadFile = $22 - -; ---------------------------------------------------------------------------- -; CFFA1 API parameters in the ZP - -Destination := $00 ; File load/save address -Filename := $02 ; Pointer to file name -Filetype := $06 ; ProDOS file type -AuxType := $07 ; ProDOS file auxtype -FileSize := $09 ; File size - -; ---------------------------------------------------------------------------- -; Scratch memory used during API calls - -ZPTemp := $0380 ; ZP locations get backed up here -CFFAFileName := $03C0 ; File name string - -; ---------------------------------------------------------------------------- -; ProDOS file type value for write operations -; The CFFA1 doesn't really care, so this is just for show. - -SaveType = $F8 ; F8 = "PRG"? - -; ---------------------------------------------------------------------------- - - -; ---------------------------------------------------------------------------- -; See if CFFA1 card is present and display error if not -; ---------------------------------------------------------------------------- -CheckCFFA: - ldy CFFA_ID1 - cpy #$CF - bne CFFAErr - ldy CFFA_ID2 - cpy #$FA - bne CFFAErr - rts - - -; ---------------------------------------------------------------------------- -; Bring up the CFFA1 menu -; ---------------------------------------------------------------------------- -CFFAMenu: - jsr CheckCFFA - jmp CFFA_MENU - - -; ---------------------------------------------------------------------------- -; This sets up the zero page locations file name for the read/write calls -; ---------------------------------------------------------------------------- -APISetup: - jsr CheckCFFA ; Make sure CFFA card is present first - ldy #0 -@1: lda GOWARM,y ; Back up first 12 bytes of the ZP - sta ZPTemp,y ; so they can used during CFFA API call - iny - cpy #12 - bne @1 -GetFileName: ; Get file name from input line - dec TXTPTR - ldy #0 -@1: jsr CHRGET ; Get next character from the input line - beq @2 ; Is it null (EOL)? - sta CFFAFileName+1,y ; Not EOL, store it in filename string - iny - cpy #15 ; 15 chars yet? - bne @1 ; no, go back for more -@2: cpy #0 ; Read 15 chars or EOL, did we get anything? - beq SynErr ; No, syntax error - sty CFFAFileName ; Store file name length - lda #CFFAFileName - sta Filename ; and store it for API call - sty Filename+1 - lda PRGEND ; Set up file size - sbc TXTTAB ; (PRGEND - TXTTAB) - sta FileSize - lda PRGEND+1 - sbc TXTTAB+1 - sta FileSize+1 - lda TXTTAB ; Set up start address and auxtype - ldy TXTTAB+1 ; (these will be the same) - sta Destination - sta AuxType - sty Destination+1 - sty AuxType+1 - lda #SaveType ; Set up ProDOS file type - sta Filetype - rts - - -; ---------------------------------------------------------------------------- -; Display an error message if something went wrong during APISetup -; Uses the Applesoft ERROR routine -; ---------------------------------------------------------------------------- -CFFAErr: - ldx #ERR_NOCFFA - .byte $2C ; Bogus BIT instruction -SynErr: - ldx #ERR_SYNTAX - jmp ERROR ; Jump to Applesoft ERROR routine - - -; ---------------------------------------------------------------------------- -; Restores the first 12 bytes of the ZP which were saved during APISetup -; ---------------------------------------------------------------------------- -RestoreZP: - ldy #0 -@1: lda ZPTemp,y ; Load byte from temporary storage - sta GOWARM,y ; put it back in its original location - iny - cpy #12 ; Repeat for next 11 bytes - bne @1 - rts - - -; ---------------------------------------------------------------------------- -; Write a file to the CFFA card (SAVE command) -; ---------------------------------------------------------------------------- -CFFASave: - jsr APISetup ; Set up zero page locations for API call - ldx #CFFA1_WriteFile; Select WriteFile API function - jsr DoCFFACall ; Do it -@1: jsr RestoreZP ; put ZP back together -@2: rts - - -; ---------------------------------------------------------------------------- -; Read file from CFFA, then fix up Applesoft zero page to point to the -; loaded program (LOAD command) -; ---------------------------------------------------------------------------- -CFFALoad: - jsr APISetup - ldx #CFFA1_ReadFile ; Select ReadFile API function - jsr DoCFFACall ; Do it - lda TXTTAB ; Compute program end address - adc FileSize ; (Add file size to program start) - sta VARTAB ; Store end address - lda TXTTAB+1 - adc FileSize+1 - sta VARTAB+1 - jsr RestoreZP ; Put the zero page back - jmp FIX_LINKS ; Done loading, fix pointers and restart - - -; ---------------------------------------------------------------------------- -; Call the CFFA1 API and display error message if one occurred -; CFFA function number is passed in X -; CFFA API returns error status in C, error number in A -; ---------------------------------------------------------------------------- -DoCFFACall: - jsr CFFA_API ; Call CFFA API - ldx #CFFA1_DisplayError ; Set next command to show error message - bcs DoCFFACall ; Call API again if error occurred - rts - - diff --git a/replica1.cfg b/replica1.cfg deleted file mode 100644 index 2537660..0000000 --- a/replica1.cfg +++ /dev/null @@ -1,10 +0,0 @@ -MEMORY { - BASROM: start = $E000, size = $1F00, fill = yes, file = %O; - MONROM: start = $FF00, size = $100, fill = yes, file = %O; -} - -SEGMENTS { - BASIC: load = BASROM, type = ro; - MONITOR: load = MONROM, type = ro; -} - diff --git a/wozmon.s b/wozmon.s deleted file mode 100644 index 6918274..0000000 --- a/wozmon.s +++ /dev/null @@ -1,183 +0,0 @@ -; Apple I Monitor ROM -; Steve Wozniak -; 1976 -; -------------------------------------------------------- - -.setcpu "6502" -.segment "MONITOR" - -.export ECHO - -; -------------------------------------------------------- -; Zero page variables -XAML := $24 -XAMH := $25 -STL := $26 -STH := $27 -L := $28 -H := $29 -YSAV := $2A -MODE := $2B - -; I/O locations -IN := $0200 ; Input buffer is $0200 to $027F -KBD := $D010 ; Keyboard data -DSP := $D012 ; Display data -KBDCR := $D011 ; Keyboard control register -DSPCR := $D013 ; Display control register - -; ASCII codes -CR = $0D | $80 ; Carriage return -ESC = $1B | $80 ; Escape -SLASH = '\' | $80 ; \ -UNDERSC = '_' | $80 ; Underscore (acts as backspace) -DOT = '.' | $80 ; . -COLON = ':' | $80 ; : -R = 'R' | $80 ; "R" -SPACE = ' ' | $80 ; blank -ZERO = '0' | $80 ; "0" - -; -------------------------------------------------------- -RESET: cld ; Clear decimal arithmetic mode - cli - ldy #$7F ; Mask for DSP data direction register - sty DSP ; Set it up - lda #$A7 ; KBD and DSP control register mask - sta KBDCR ; Enable interrupts, set CA1, CB1 for - sta DSPCR ; positive edge sense/output mode -NOTCR: cmp #UNDERSC ; Backspace? - beq BACKSPACE ; Yes - cmp #ESC ; ESC? - beq ESCAPE ; Yes - iny ; Advance text index - bpl NEXTCHAR ; Auto ESC if > 127 -ESCAPE: lda #SLASH ; "\" - jsr ECHO ; Output it -GETLINE: - lda #CR ; CR - jsr ECHO ; Output it - ldy #$01 ; Initialize text index -BACKSPACE: - dey ; Back up text index - bmi GETLINE ; Beyond start of line, reinitialize -NEXTCHAR: - lda KBDCR ; Key ready? - bpl NEXTCHAR ; Loop until ready - lda KBD ; Load character. B7 should be '1' - sta IN,y ; Add to text buffer - jsr ECHO ; Display character - cmp #CR ; CR? - bne NOTCR ; No - ldy #$FF ; Reset text index - lda #$00 ; For XAM mode - tax ; 0 -> x -SETSTOR: - asl ; Leaves $7B if setting STOR mode -SETMODE: - sta MODE ; $00 = XAM, $7B = STOR, $AE = BLOCK XAM -BLSKIP: iny ; Advance text index -NEXTITEM: - lda IN,y ; Get character - cmp #CR ; CR? - beq GETLINE ; Yes, done this line - cmp #DOT ; "."? - bcc BLSKIP ; Skip delimiter - beq SETMODE ; Set BLOCK XAM mode - cmp #COLON ; ":"? - beq SETSTOR ; Yes, set STOR mode - cmp #R ; "R"? - beq RUN ; Yes, run user program - stx L ; $00 -> L - stx H ; and H - sty YSAV ; Save Y for comparison -NEXTHEX: - lda IN,y ; Get character for hex test - eor #$B0 ; Map digits to $0-9 - cmp #$0A ; Digit? - bcc DIG ; Yes - adc #$88 ; Map letter "A"-"F" to $FA-FF - cmp #$FA ; Hex letter? - bcc NOTHEX ; No, character not hext -DIG: asl - asl ; Hex digit to MSD of A - asl - asl - ldx #$04 ; 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. Check next character for hex -NOTHEX: - cpy YSAV ; Check if L, H empty (no hex digits) - beq ESCAPE ; Yes, generate ESC sequence - bit MODE ; Test MODE byte - bvc NOTSTOR ; B6 = 0 for STOR, 1 for XAM and BLOCK XAM - lda L ; LSD's of hex data - sta (STL,x) ; Store at current 'store index' - inc STL ; Increment store index - bne NEXTITEM ; Get next item (no carry) - inc STH ; Add carry to 'store index' high order -TONEXTITEM: - jmp NEXTITEM ; Get next command item -RUN: jmp (XAML) ; Run at current XAM index -NOTSTOR: - bmi XAMNEXT ; B7=0 for XAM, 1 for BLOCK XAM - ldx #$02 ; Byte count -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 -NXTPRNT: - bne PRDATA ; NE means no address to print - lda #CR ; CR - jsr ECHO ; Output it - lda XAMH ; 'Examine index' high-order byte - jsr PRBYTE ; Output it in hex format - lda XAML ; Low-order 'examine index' byte - jsr PRBYTE ; Output it in hex format - lda #COLON ; ":" - jsr ECHO ; Output it -PRDATA: lda #SPACE ; Blank - jsr ECHO ; Output it - lda (XAML,x) ; Get data byte at 'examine index' - jsr PRBYTE ; Output it in hex format -XAMNEXT: - stx MODE ; 0 -> MODE (XAM mode) - lda XAML - cmp L ; Compare 'examine index' to hex data - lda XAMH - sbc H - bcs TONEXTITEM ; Not less, so no more data to output - inc XAML - bne MOD8CHK ; Increment 'examine index' - inc XAMH -MOD8CHK: - lda XAML ; Check low-order 'examine index' byte - and #$07 ; For MOD 8 = 0 - bpl NXTPRNT ; Always taken -PRBYTE: pha ; Save A for LSD - lsr - lsr - lsr ; MSD to LSD position - lsr - jsr PRHEX ; Output hex digit - pla ; Restore A -PRHEX: and #$0F ; Mask LSD for hex print - ora #ZERO ; Add "0" - cmp #$BA ; Digit? - bcc ECHO ; Yes, output it - adc #$06 ; Add offset for letter -ECHO: bit DSP ; DA bit (B7) cleared yet? - bmi ECHO ; No, wait for display - sta DSP ; Output character. Sets DA. - rts ; Return -; -------------------------------------------------------- - .word $0000 ; (unused) - .addr $0F00 ; (NMI vector) - .addr RESET ; (RESET vector) - .addr $0000 ; (IRQ vector)