This commit is contained in:
Tom Greene 2017-05-11 16:42:22 -04:00
commit 6a1e34ba6f
9 changed files with 6097 additions and 0 deletions

23
Makefile Normal file
View File

@ -0,0 +1,23 @@
AFLAGS =
LFLAGS = -C replica1.cfg
BINFILE = applesoft-lite.bin
OBJS = applesoft-lite.o io.o cffa1.o wozmon.o
$(BINFILE): $(OBJS)
ld65 $(LFLAGS) $(OBJS) -o $(BINFILE)
applesoft-lite.o: applesoft-lite.s
ca65 $(AFLAGS) $<
wozmon.o: wozmon.s
ca65 $(AFLAGS) $<
cffa1.o: cffa1.s
ca65 $(AFLAGS) $<
io.o: io.s
ca65 $(AFLAGS) $<
clean:
rm $(OBJS) $(BINFILE)

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# applesoft-lite
This is a stripped-down version of Applesoft BASIC (Microsoft 6502 BASIC) for the Apple-1.
See https://cowgod.org/replica1/applesoft/

5501
applesoft-lite.s Normal file

File diff suppressed because it is too large Load Diff

185
cffa1.s Normal file
View File

@ -0,0 +1,185 @@
; 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

92
io.s Normal file
View File

@ -0,0 +1,92 @@
; Apple-1 I/O routines for Applesoft Lite
; Last modified 10-May-2008
.setcpu "6502"
.segment "BASIC"
.export KEYBOARD, GETLN, RDKEY, CLS, OUTDO, CRDO, OUTQUES, OUTSP
.import INPUTBUFFER
.include "zeropage.s"
; ----------------------------------------------------------------------------
; I/O Locations
; ----------------------------------------------------------------------------
KEYBOARD := $D010
KEYBOARDCR := $D011
; ----------------------------------------------------------------------------
; Monitor Subroutines
; ----------------------------------------------------------------------------
MONECHO := $FFEF
; ----------------------------------------------------------------------------
; Get keystroke from keyboard (RDKEY)
; ----------------------------------------------------------------------------
RDKEY:
lda KEYBOARDCR ; Key ready?
bpl RDKEY ; Loop until ready
lda KEYBOARD ; Load character
and #$7F ; Clear hi bit
rts
; ----------------------------------------------------------------------------
; Get line of input (GETLN)
; adapted from Apple II monitor
; ----------------------------------------------------------------------------
NOTCR:
cmp #$18 ; CTRL-X?
beq CANCEL ; Cancel line if so
jsr MONECHO ; Output using monitor ECHO routine
cmp #'_' ; backspace?
beq BCKSPC ; Yes, do backspace...
NOTCR1: inx
bne NXTCHAR ; Wasn't backspace or CTRL+X, get next key
CANCEL: jsr OUTSLASH ; Output a "\" to indicate cancelled line
GETLNZ: jsr CRDO ; new line
GETLN: jsr OUTPROMPT ; Display the prompt
ldx #$01 ; Set cursor at 1, it gets decremented later
BCKSPC: txa
beq GETLNZ ; Backspace with nothing on the line? start new line
dex ; Move "cursor" back one space
NXTCHAR:
jsr RDKEY ; Read key from keyboard
ADDINP: sta INPUTBUFFER,x ; Put it in the input buffer
cmp #$0D ; CR?
bne NOTCR ; No, keep looping
jsr CRDO ; Output CR
rts
; ----------------------------------------------------------------------------
; These moved here from the main Applesoft code to save a few bytes
; ----------------------------------------------------------------------------
OUTSLASH:
lda #'\'
.byte $2C ; Fake BIT instruction to skip next 2 bytes
OUTPROMPT:
lda PROMPT
.byte $2C
OUTSP: lda #' '
.byte $2C
OUTQUES:
lda #'?'
.byte $2C
CRDO: lda #$0D
OUTDO: ora #$80 ; Set hi bit
jsr MONECHO ; Send character to monitor ECHO
and #$7F ; clear hi bit
rts
; ----------------------------------------------------------------------------
; Corny method of clearing the screen by sending a bunch of CR's.
; ----------------------------------------------------------------------------
CLS:
ldy #24 ; loop 24 times
@1: jsr CRDO ; ouput CR
dey
bpl @1 ; ... do it again
rts

10
macros.s Normal file
View File

@ -0,0 +1,10 @@
; ----------------------------------------------------------------------------
; Macros
; htasc - set the hi bit on the last byte of a string for termination
.macro htasc str
.repeat .strlen(str)-1,I
.byte .strat(str,I)
.endrep
.byte .strat(str,.strlen(str)-1) | $80
.endmacro

10
replica1.cfg Normal file
View File

@ -0,0 +1,10 @@
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 Normal file
View File

@ -0,0 +1,183 @@
; 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)

88
zeropage.s Normal file
View File

@ -0,0 +1,88 @@
; Zero Page locatinos used by Applesoft Lite
GOWARM := $0000 ; Gets "jmp RESTART"
GOSTROUTZ := $0003 ; Gets "jmp STROUT"
CHARAC := $000D ; Alternate string terminator
ENDCHR := $000E ; String terminator
TKNCNTR := $000F ; Used in PARSE
EOLPNTR := $000F ; Used in NXLIN
NUMDIM := $000F ; Used in array routines
DIMFLG := $0010
VALTYP := $0011 ; $: VALTYP=$FF; %: VALTYP+1=$80
DATAFLG := $0013 ; Used in PARSE
GARFLG := $0013 ; Used in GARBAG
SUBFLG := $0014
INPUTFLG := $0015 ; = $40 for GET, $98 for READ
CPRMASK := $0016 ; Receives CPRTYP in FRMEVL
PROMPT := $0033
LINNUM := $0050 ; Converted line #
TEMPPT := $0052 ; Last used temp string desc
LASTPT := $0053 ; Last used temp string pntr
TEMPST := $0055 ; Holds up to 3 descriptors
INDEX := $005E
DEST := $0060
RESULT := $0062 ; Result of last * or /
TXTTAB := $0067 ; Start of program text
VARTAB := $0069 ; Start of variable storage
ARYTAB := $006B ; Start of array storage
STREND := $006D ; End of array storage
FRETOP := $006F ; Start of string storage
FRESPC := $0071 ; Temp pntr, string routines
MEMSIZ := $0073 ; End of string space (HIMEM)
CURLIN := $0075 ; Current line number
OLDLIN := $0077 ; Addr. of last line executed
OLDTEXT := $0079
DATLIN := $007B ; Line # of current data stt.
DATPTR := $007D ; Addr of current data stt.
INPTR := $007F
VARNAM := $0081 ; Name of variable
VARPNT := $0083 ; Addr of variable
FORPNT := $0085
TXPSV := $0087 ; Used in INPUT
LASTOP := $0087 ; Scratch flag used in FRMEVL
CPRTYP := $0089 ; >,=,< flag in FRMEVL
TEMP3 := $008A
FNCNAM := $008A
DSCPTR := $008C
DSCLEN := $008F ; used in GARBAG
JMPADRS := $0090 ; gets "jmp ...."
LENGTH := $0091 ; used in GARBAG
ARGEXTENSION := $0092 ; FP extra precision
TEMP1 := $0093 ; save areas for FAC
ARYPNT := $0094 ; used in GARBAG
HIGHDS := $0094 ; pntr for BLTU
HIGHTR := $0096 ; pntr for BLTU
TEMP2 := $0098
TMPEXP := $0099 ; used in FIN (EVAL)
INDX := $0099 ; used by array rtns
EXPON := $009A ; "
DPFLG := $009B ; flags dec pnt in FIN
LOWTR := $009B
EXPSGN := $009C
FAC := $009D ; main floating point accumulator
VPNT := $00A0 ; temp var ptr
FACSIGN := $00A2 ; holds unpacked sign
SERLEN := $00A3 ; holds length of series - 1
SHIFTSIGNEXT := $00A4 ; sign extension, right shifts
ARG := $00A5 ; secondary FP accumulator
ARGSIGN := $00AA
SGNCPR := $00AB ; flags opp sign in FP routines
FACEXTENSION := $00AC ; FAC extension byte
SERPNT := $00AD ; pntr to series data in FP
STRNG1 := $00AB
STRNG2 := $00AD
PRGEND := $00AF
CHRGET := $00B1
CHRGOT := $00B7
TXTPTR := $00B8
RNDSEED := $00C9
LOCK := $00D6 ; no user access if > 127
ERRFLG := $00D8 ; $80 if ON ERR active
ERRLIN := $00DA ; line # where error occurred
ERRPOS := $00DC ; TXTPTR save for HANDLERR
ERRNUM := $00DE ; which error occurrred
ERRSTK := $00DF ; stack pntr before error
TRCFLG := $00F2
TXTPSV := $00F4
CURLSV := $00F6
REMSTK := $00F8 ; stack pntr before each stt.