Added support for LOAD & SAVE commands via Apple-1 Serial Interface

This commit is contained in:
flowenol 2021-01-16 01:08:36 +01:00
parent 6a1e34ba6f
commit a1e942851f
9 changed files with 282 additions and 408 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.bin
*.o

View File

@ -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)

View File

@ -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

7
apple1cartridge.cfg Normal file
View File

@ -0,0 +1,7 @@
MEMORY {
BASROM: start = $6000, size = $2000, fill = yes, file = %O;
}
SEGMENTS {
BASIC: load = BASROM, type = ro;
}

233
apple1serial.s Normal file
View File

@ -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

View File

@ -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
ldx #<INPUTBUFFER-1+256
ldy #>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 #<INPUTBUFFER-1
lda #<INPUTBUFFER-1+256
sta TXTPTR
rts
@ -941,7 +941,7 @@ LIST4: bpl LIST2 ; BRANCH IF NOT A TOKEN
sty FAC ; POINT FAC TO TABLE
ldy #>(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 ; POINT TO STACK-1
lda #<STACK-1+256 ; POINT TO STACK-1
ldy #>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 ; MULTIPLY BY 1E9
ldy #>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

185
cffa1.s
View File

@ -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 ; Load address of file name string
ldy #>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

View File

@ -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;
}

183
wozmon.s
View File

@ -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)