LOAD/SAVE of BASIC files

This commit is contained in:
Kilian committed 2026-09-28 22:53:04 +02:00
1 parent e6a2af6f8a
commit 33faa4fac2
9 files changed
+353 -2887

No files matched your search

+318 -1
View File
@@ -1,3 +1,320 @@
.segment "CODE"
; None yet
LOADSAVE_LFN = $FF
; ============================================================================
; COPY STRING EXPRESSION TO FILE_NAME_BUFFER
;
; Entry:
; TXTPTR points at string expression
;
; Exit:
; FILE_NAME_BUFFER contains NUL-terminated filename
;
; Clobbers:
; A,X,Y,INDEX,STRNG2
; ============================================================================
PARSE_LOADSAVE_FILENAME:
jsr FRMEVL
jsr CHKSTR
jsr FREFAC
; FREFAC:
; A = length
; INDEX = string address
sta STRNG2
ldy #$00
@copy:
cpy STRNG2
beq @done
lda (INDEX),y
sta FILE_NAME_BUFFER,y
iny
bne @copy
@done:
lda #$00
sta FILE_NAME_BUFFER,y
rts
; ============================================================================
; SET UP THE COMMON FILE ARGUMENTS
; ============================================================================
LOADSAVE_SET_FILE:
lda #LOADSAVE_LFN
sta IO_FILE_DESCRIPTOR
lda #<FILE_NAME_BUFFER
sta IO_FILE_DATA
lda #>FILE_NAME_BUFFER
sta IO_FILE_DATA+1
rts
; ============================================================================
; WAIT FOR FILE COMMAND COMPLETION
;
; Jumps to IQERR on host-side failure.
; ============================================================================
LOADSAVE_WAIT:
lda IO_FILE_STATUS
and #FILE_STATUS_READY
beq LOADSAVE_WAIT
lda IO_FILE_STATUS
and #FILE_STATUS_ERROR
beq @ok
jmp IQERR
@ok:
rts
; ============================================================================
; OPEN FOR READING
; ============================================================================
LOADSAVE_OPEN_R:
jsr LOADSAVE_SET_FILE
lda #IO_COMMAND_OPEN_R
sta IO_FILE_COMMAND
jmp LOADSAVE_WAIT
; ============================================================================
; OPEN FOR WRITING / TRUNCATE
; ============================================================================
LOADSAVE_OPEN_W:
jsr LOADSAVE_SET_FILE
lda #IO_COMMAND_OPEN_W
sta IO_FILE_COMMAND
jmp LOADSAVE_WAIT
; ============================================================================
; CLOSE INTERNAL LOAD/SAVE FILE
; ============================================================================
LOADSAVE_CLOSE:
lda #LOADSAVE_LFN
sta IO_FILE_DESCRIPTOR
lda #IO_COMMAND_CLOSE
sta IO_FILE_COMMAND
jsr LOADSAVE_WAIT
jsr CLRCH
rts
; ============================================================================
; SAVE "filename"
;
; Saves plain ASCII BASIC source, equivalent to LIST output.
; ============================================================================
SAVE:
jsr PARSE_LOADSAVE_FILENAME
jsr LOADSAVE_OPEN_W
; Route OUTDO/CHROUT to our internal file.
ldx #LOADSAVE_LFN
jsr CHKOUT
stx CURDVC
; Start with first BASIC line.
lda TXTTAB
sta LOWTRX
lda TXTTAB+1
sta LOWTRX+1
SAVE_NEXT_LINE:
; The high byte of the forward link is zero at end of program.
ldy #$01
lda (LOWTRX),y
beq SAVE_DONE
; ------------------------------------------------------------
; Print line number.
;
; record:
; +0/+1 = next line
; +2/+3 = line number
; +4... = tokenized body
; ------------------------------------------------------------
iny ; Y=2
lda (LOWTRX),y
tax
iny ; Y=3
lda (LOWTRX),y
jsr LINPRT
lda #' '
jsr OUTDO
; DATAFLG follows LIST's quoting/token rules.
lda #$01
sta DATAFLG
ldy #$04
SAVE_LINE_CHAR:
lda (LOWTRX),y
beq SAVE_LINE_DONE
; Test whether it's plain ASCII/token BEFORE doing CMPs
bpl SAVE_PLAIN_CHAR
cmp #$FF
beq SAVE_PLAIN_CHAR
bit DATAFLG
bmi SAVE_PLAIN_CHAR
; Token
sec
sbc #$7F
tax
sty FORPNT
ldy #$FF
SAVE_FIND_TOKEN:
dex
beq SAVE_TOKEN_FOUND
SAVE_SKIP_TOKEN:
iny
lda TOKEN_NAME_TABLE,y
bpl SAVE_SKIP_TOKEN
bmi SAVE_FIND_TOKEN
SAVE_TOKEN_FOUND:
iny
lda TOKEN_NAME_TABLE,y
bmi SAVE_TOKEN_LAST
jsr OUTDO
bne SAVE_TOKEN_FOUND
SAVE_TOKEN_LAST:
and #$7F
jsr OUTDO
ldy FORPNT
iny
jmp SAVE_LINE_CHAR
SAVE_PLAIN_CHAR:
and #$7F
jsr OUTDO
; Quote state must be updated AFTER output/test.
cmp #$22
bne SAVE_PLAIN_DONE
lda DATAFLG
eor #$FF
sta DATAFLG
SAVE_PLAIN_DONE:
iny
jmp SAVE_LINE_CHAR
SAVE_LINE_DONE:
; Every saved source line ends in LF.
jsr LFDO
; Follow next-line pointer.
ldy #$00
lda (LOWTRX),y
tax
iny
lda (LOWTRX),y
stx LOWTRX
sta LOWTRX+1
jmp SAVE_NEXT_LINE
SAVE_DONE:
jsr LOADSAVE_CLOSE
rts
; ============================================================================
; LOAD "filename"
;
; Replaces current program.
; Reads ASCII source one line at a time, tokenizes through the normal
; BASIC parser, and inserts using NUMBERED_LINE.
;
; IMPORTANT:
; NUMBERED_LINE eventually reaches FIX_LINKS -> SETPTRS -> STKINI, which
; resets the hardware stack. Therefore LOAD cannot expect NUMBERED_LINE to
; return with RTS. FIX_LINKS jumps back to LOAD_NEXT_LINE while LOAD_ACTIVE
; is set.
; ============================================================================
LOAD:
; Copy the filename before SCRTCH destroys temporary strings.
jsr PARSE_LOADSAVE_FILENAME
jsr LOADSAVE_OPEN_R
; Delete current program.
jsr SCRTCH
; Route CHRIN to the file.
ldx #LOADSAVE_LFN
jsr CHKIN
stx CURDVC
; Tell FIX_LINKS that program insertion belongs to LOAD.
lda #$01
sta LOAD_ACTIVE
; ----------------------------------------------------------------------------
; Entry point used both initially and by FIX_LINKS after inserting each line.
; Must be a globally visible label if FIX_LINKS lives in another source file.
; ----------------------------------------------------------------------------
LOAD_NEXT_LINE:
ldx #$00
; ----------------------------------------------------------------------------
; Read one physical source line into INPUTBUFFER.
;
; "10 PRINT 1\n" -> normal line
; "10 PRINT 1"<EOF> -> final line still accepted
; EOF with X=0 -> done loading
; ----------------------------------------------------------------------------
@read_char:
jsr CHRIN
; If host says EOF, A is not considered valid data.
pha
lda IO_FILE_STATUS
and #FILE_STATUS_EOF
bne @eof
pla
; LF terminates a source line.
cmp #LF
beq @line_complete
; Accept CRLF files too.
cmp #$0D
beq @read_char
; Same practical line limit as interactive INLIN.
cpx #$47
bcs @line_too_long
sta INPUTBUFFER,x
inx
jmp @read_char
@eof:
pla
; EOF with no accumulated characters means real end of file.
cpx #$00
beq @load_done
; Otherwise process the unterminated final line.
@line_complete:
lda #$00
sta INPUTBUFFER,x
; Point CHRGET at INPUTBUFFER-1, exactly like INLIN returns.
lda #<(INPUTBUFFER-1)
sta TXTPTR
lda #>(INPUTBUFFER-1)
sta TXTPTR+1
jsr CHRGET
; Ignore empty physical lines.
beq LOAD_NEXT_LINE
; A source file must consist of numbered program lines.
;
; CHRGET leaves carry clear for a leading decimal digit,
; exactly like the normal RESTART path.
bcc @numbered
jmp @syntax_error
@numbered:
; NUMBERED_LINE performs:
;
; LINGET
; PARSE_INPUT_LINE
; FNDLIN
; PUT_NEW_LINE
; FIX_LINKS
;
; DO NOT JSR here.
;
; FIX_LINKS eventually calls SETPTRS/STKINI, which resets the
; hardware stack. FIX_LINKS will detect LOAD_ACTIVE and jump back
; to LOAD_NEXT_LINE.
jmp NUMBERED_LINE
@line_too_long:
@syntax_error:
lda #$00
sta LOAD_ACTIVE
jsr CLRCH
jsr LOADSAVE_CLOSE
jmp SYNERR
@load_done:
lda #$00
sta LOAD_ACTIVE
jsr CLRCH
jsr LOADSAVE_CLOSE
; Do NOT JSR SETPTRS here.
;
; Every successfully inserted line already passed through
; FIX_LINKS -> SETPTRS -> LE33D.
;
; Also do not RTS: SETPTRS/STKINI during loading may have destroyed
; the original LOAD command's return frame.
jmp RESTART