mirror of
https://github.com/flowenol/applesoft-lite.git
synced 2024-12-09 03:52:39 +00:00
Added support for LOAD & SAVE commands via Apple-1 Serial Interface
This commit is contained in:
parent
6a1e34ba6f
commit
a1e942851f
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.bin
|
||||
*.o
|
9
Makefile
9
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)
|
||||
|
||||
|
12
README.md
12
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
|
||||
|
7
apple1cartridge.cfg
Normal file
7
apple1cartridge.cfg
Normal 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
233
apple1serial.s
Normal 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
|
@ -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
185
cffa1.s
@ -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
|
||||
|
||||
|
10
replica1.cfg
10
replica1.cfg
@ -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
183
wozmon.s
@ -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)
|
Loading…
Reference in New Issue
Block a user