I used a Pico "RP2040". The pico has 2 spi's, a master one hosting the sd card with FATFS and a slave one to the 6502. In ROM I thus only needed space to make calls across the spi for disk commands and parameters, such as cd "some_directory", ls, rm "somefile", mv "somefile", load "somefile", save "somefile" and so on. I have these commands working for EHBASIC, Tali Forth, ED, and the A1 Assembler.
One the PICO side I did use C and spent some time creating the routines to respond to the calls from the 6502.
My EHBASIC's load and save commands operate on the ASCII source, specifically LOAD "MYWAVE.BAS" will instruct the pico to read and send the MYWAVE.BAS file (quickly but as if it was typed in) and save "MYBASPRG.BAS" followed by a list command will send the output of list to a new file on the SD card named MYBASPRG.BAS.
Other than writing the code for the load and save commands on the EHBASIC side, I also used one memory location as an indicator and added a couple lines to the EHBASIC assembly itself. There may be a better way to do it, but I needed an indicator that the list command had finished, and to accomplish this I update a memory location when READY is printed, such as when the list command finishes.
The SD card routines were written based on a 256 byte buffer, and they are quick.
The load and save ASCII routines were written based on the post by leeeeee at
viewtopic.php?f=5&t=5760&view=nextQuote:
If you want to save the program as binary you should save (Smeml) to (Svarl)-1.
If you want to save the program as ASCII you should redirect the character output vector to write to your filesystem and then call LIST. Doing this can also allow you to specify line numbers or ranges as with LIST. The output vector should be restored and the file closed when LIST returns or an error is encountered.
To load a binary program start loading it at (Smeml) and set (Svarl) to the last address + 1 then call LAB_1477 to clear the variables and reset the execution pointer. An easy way to do the first part is by copying (Smeml) to (Svarl) and using (Svarl) as a post incremented save pointer. If there is a chance that the program has been relocated it's probably a good idea to rebuild the line pointer chain.
To load an ASCII program redirect the character input vector to read from your filesystem and return to the main interpreter loop. The input vector should be restored and the file closed when the file end is reached or an error is encountered.
I prefer ASCII format as it's far more portable, easier to manipulate and can include direct commands and comment lines that aren't saved to memory.
Lee.
Below is my minmon.asm. It definently needs to be "cleaned up", but it works and posting for now as is in hopes it will help someone.
There may be a little context loss without also posting the c code on the pico side. For now I can only think to mention, on the pico side I use a buffer initialized to all 3's when sending a files contents. In doing so, on the 6502 side I know when I read a 3 while reading what should be ascii text, I can interpret it to mean end of text/end of file...
Code:
; minimal monitor for EhBASIC and 6502 simulator V1.05
; tabs converted to space, tabwidth=6
; To run EhBASIC on the simulator load and assemble [F7] this file, start the simulator
; running [F6] then start the code with the RESET [CTRL][SHIFT]R. Just selecting RUN
; will do nothing, you'll still have to do a reset to run the code.
.include "basic.asm"
; ######################################################################
; TO IDENTIFY AND IGNORE THE EXCESS LINEFEED CHARACTERS
; ######################################################################
LF = $0A ; Line feed character
CR = $0D
; ######################################################################
; put the IRQ and MNI code in RAM so that it can be changed
IRQ_vec = VEC_SV+2 ; IRQ code vector
NMI_vec = IRQ_vec+$0A ; NMI code vector
; ######################################################################
; Change of addresses to match my build
; ######################################################################
ACIA_data = $0210
ACIA_status = $0211
ACIA_command = $0212
ACIA_control = $0213
IO_AREA = $0210 ; set I/O area for this monitor
; now the code. all this does is set up the vectors and interrupt code
; and wait for the user to select [C]old or [W]arm start. nothing else
; fits in less than 128 bytes
; SOME ADDYS
PORTD = $0200
PORTC = $0201
DDRD = $0202
DDRC = $0203
MSGL = $91
MSGH = $92
FNMEL = $93
FNMEH = $94
MSGL2 = $95
MSGH2 = $96
STRLN = $99
IN = $3DAA
PRBYTE = $FFDC
PRHEX = $FFE5
KEY_LF = $0a
KEY_CR = $0d
;setup the VIA (LCD DISPALY)
PORTB = $0200
PORTA = $0201
DDRB = $0202
DDRA = $0203
T1CL = $0204
T1CH = $0205
SR = $020a
ACR = $020b
PCR = $020c
IFR = $020d
IER = $020e
; SPI
SCK = %00000001 ;Looking at chip 10000000
MOSI = %00000010 ;Looking at chip 01000000
CS = %00000100 ;Looking at chip 00100000
MISO = %01000000 ;Looking at chip 00000010
; The next two address locations are arbitrary, anywhere that is free
spiin = $F3
spiout = $F4
; The next two address locations are arbitrary, anywhere that is free
LB_ENT = $3BF4
HB_ENT = $3BF5
; -------------------------------------------------------------------------------------------------------
Get_Char = $db77 ; Check to see if there is a character, break on carry clear
kernel_putc = $db94 ; kernel_putc = $fc8c ; Send character to screen
; IN BASIC.ASM CHANGE THIS TO 400 AS SHOWN. ORIGIONALLY IT WAS 200 BUT I HAVE DEVICES THERE.
; I use $300-$3FF for an input buffer.
; ALSO SET MEMORY APPROPRIATELY
;Ram_base = $0400 ; start of user RAM (set as needed, should be page aligned)
;Ram_top = $8000 ; end of user RAM+1 (set as needed, should be page aligned)
; *= $FF80 ; pretend this is in a 1/8K ROM
.segment "ELOAD"
.WORD $1000 ; Run EHBASIC from RAM with a start address of $1000
; ######################################################################
; Change segment to match my build
; ######################################################################
.segment "EBASIC"
;.segment "OS"
; reset vector points here
.export EBASIC_RES
EBASIC_RES
CLD ; clear decimal mode
LDX #$FF ; empty stack
TXS ; set the stack
; set up vectors and interrupt code, copy them to page 2
LDY #END_CODE-LAB_vec ; set index/count
LAB_stlp
LDA LAB_vec-1,Y ; get byte from interrupt code
STA VEC_IN-1,Y ; save to RAM
DEY ; decrement index/count
BNE LAB_stlp ; loop if more to do
; now do the signon message, Y = $00 here
LAB_signon
LDA LAB_mess,Y ; get byte from sign on message
BEQ LAB_nokey ; exit loop if done
JSR V_OUTP ; output character
INY ; increment index
BNE LAB_signon ; loop, branch always
LAB_nokey
JSR V_INPT ; call scan input device
BCC LAB_nokey ; loop if no key
AND #$DF ; mask xx0x xxxx, ensure upper case
CMP #'W' ; compare with [W]arm start
BEQ LAB_dowarm ; branch if [W]arm start
CMP #'C' ; compare with [C]old start
BNE EBASIC_RES ; loop if not [C]old start
JMP LAB_COLD ; do EhBASIC cold start
LAB_dowarm
JMP LAB_WARM ; do EhBASIC warm start
ACIAout
CMP #LF ; Ignore line feed character
BEQ Ignore
jsr kernel_putc
Ignore
rts
ACIAin
clc
JSR Get_Char
BCC ACIAin_end
AND #$7F ;*Change to "standard ASCII"
ACIAin_end
RTS
LAB_nobyw
CLC ; flag no byte received
; ----------------------------------------------------------------------------------------------------------
;no_load ; empty load vector for EhBASIC
; http://forum.6502.org/viewtopic.php?f=5&t=1534&hilit=LAB_EVEX
; To load an ASCII program redirect the character input vector (ACIAin FROM LAB_VEC) to read from your
; filesystem and return to the main interpreter loop. The input vector should be restored and the file closed
; when the file end is reached or an error is encountered.
; http://forum.6502.org/viewtopic.php?f=5&t=5760&view=next
;Call the evaluate following expression routine, LAB_EVEZ, and look for a string by testing if the data type flag, Dtypef, is negative. If it is the string descriptor is on the descriptor stack so don't forget to pop it off there by calling LAB_22B6 once you're done with it.
;
;Lee.
; I think Lee meant LAB_EVEX
; ============================================================================================================
; START SEND COMMAND AND FILE NAME TO PICO. REMAP THE READ VECTOR THEN JUMP BACK INTO BASIC.
; ============================================================================================================
LOAD:
; ============================================================================================================
LDA #$FF ; $FF=load (Probably not needed here)
;STA $70
;FILESAVE:
; ; Push registers A, X and Y to the stack. Restore on exit.
PHA
PHX
PHY
JSR LAB_EVEX ; evaluate string
JSR LAB_EVST ; test it is a string
STA STRLN ; save string length
STX FNMEL ; save string pointer low byte
STY FNMEH ; save string pointer high byte
; Stage the "ESAVE FILENAME" command in the IN BUFFER
; ------------------------
; ESAVE
; ------------------------
LDA #<ELOAD
STA MSGL
LDA #>ELOAD
STA MSGH
LDY #0
LDX #0
PRINTLOAD
LDA (MSGL),Y
BEQ END_PRINTLOAD
STA IN,X
;JSR $FFEF
INX
INY
CPY MSGH
BNE PRINTLOAD
END_PRINTLOAD
; ------------------------
; FILENAME
; ------------------------
LDY #0
PRINTLOADNAME
LDA (FNMEL),Y
BEQ DONEPRINTLOADNAME
STA IN,X
;JSR $FFEF
;JSR PRBYTE
INX
INY
;CMP FNMEH
CPY STRLN
BNE PRINTLOADNAME
DONEPRINTLOADNAME
; END WITH A ZERO
LDA #0
STA IN,X
; ; Send it.
LDX #$0
; JSR SND
JSR SNDESAVECMD ; RENAME THIS SUBROUTINE
; Complete command, i.e. 256 bytes
JSR spi_spi_complete_cmd
; Restore origional memory addresses
PLY
PLX
PLA
STX _fd
RECEIVE:
LDX #0
LDY #0
lda #<fread_wrapper
sta VEC_IN
lda #>fread_wrapper
sta VEC_IN+1
JSR delay
JSR delay
jsr delay
jsr delay
;LDX #0 ; WE NEED X TO BE ZERO IN ORDER TO COUNT CHARS FROM UPCOMING LIST COMMAND
STZ $F8
JMP LAB_1319 ; reset and return
RTS ; << Should not be reached
; ============================================================================================================
; END SEND COMMAND AND FILE NAME TO PICO. REMAP THE READ VECTOR THEN JUMP BACK INTO BASIC.
; ============================================================================================================
fread_wrapper:
phx
phy
ldx _fd
; ----------------------------------------------------------------------
stz PORTA ; Set chip select low BEGIN PACKET
PHY
LDA #'X'
jsr spi_transceive ; Send
INC $F8
PLY
PHA
lda #CS
sta PORTA ; END PACKET
PLA
; ----------------------------------------------------------------------
;CMP #'~'
cmp #3 ; On the pico side, I initialize the buffer to all 3's, i.e. 3 means end of text. I then populate the buffer, so the 3's at the end indeed indicate the end of text.
BEQ PEXIT
; ----------------------------------------------------------------------
cmp #$0A ; replace with "basic end of line"
bne KEY_LF_DONE
lda #$0D
; ----------------------------------------------------------------------
KEY_LF_DONE
PHA
JSR delay
JSR delay
PLA
AND #$7F ;*Change to "standard ASCII" AS WOZMON DOES *KEY ONE*
sec
ply
plx
cmp #0
RTS
PEXIT:
; Complete command, i.e. 256 bytes
PHA
LDA $F8
TAX
PLA
CPX #0 ; HAS NOW CYCLED SO GET OUT
BEQ GETOUT
ply
plx
RTS
GETOUT
;JSR spi_spi_complete_cmd
jsr init_iovectors
LDA #<LAB_RMSG ; "READY"
LDY #>LAB_RMSG
JSR LAB_18C3
JMP LAB_1319 ; relink program and clear variables
init_iovectors:
lda #<ACIAin
sta VEC_IN
Lda #>ACIAin
sta VEC_IN+1
rts
; ----------------------------------------------------------------------------------------------------------
;no_save ; empty save vector for EhBASIC
; RTS
; If you want to save the program as ASCII you should redirect the character output vector to write to your
; filesystem and then call LIST. Doing this can also allow you to specify line numbers or ranges as with LIST.
;The output vector should be restored and the file closed when LIST returns or an error is encountered.
; ============================================================================================================
SAVE:
; ============================================================================================================
LDA #$FF ; $FF=load
STA $70
; Push registers A, X and Y to the stack. Restore on exit.
PHA
PHX
PHY
JSR LAB_EVEX ; evaluate string
JSR LAB_EVST ; test it is a string
STA STRLN ; save string length
STX FNMEL ; save string pointer low byte
STY FNMEH ; save string pointer high byte
; ------------------------
; ESAVE
; ------------------------
LDA #<ESAVE
STA MSGL
LDA #>ESAVE
STA MSGH
LDY #0
LDX #0
PRINTESAVE
LDA (MSGL),Y
BEQ END_PRINTESAVE
STA IN,X
;JSR $FFEF
INX
INY
CPY MSGH
BNE PRINTESAVE
END_PRINTESAVE
; ------------------------
; FILENAME
; ------------------------
LDY #0
PRINTSAVNAME
LDA (FNMEL),Y
BEQ DONEPRINTSAVNAME
STA IN,X
;JSR $FFEF
;JSR PRBYTE
INX
INY
;CMP FNMEH
CPY STRLN
BNE PRINTSAVNAME
DONEPRINTSAVNAME
; END WITH A ZERO
LDA #0
STA IN,X
; Send it.
LDX #$0
JSR SNDESAVECMD
; Complete command, i.e. 256 bytes
JSR spi_spi_complete_cmd
PLY
PLX
PLA
STX _fd
;LDX #0 ; WE NEED X TO BE ZERO IN ORDER TO COUNT CHARS FROM UPCOMING LIST COMMAND
STZ $50
; Here is where we remap output, then return to basic and issue the list command.
lda #<fwrite_wrapper
sta VEC_OUT
lda #>fwrite_wrapper
sta VEC_OUT+1
JMP LAB_1319 ; reset and return
RTS ; << Should not be reached
fwrite_wrapper:
;ldx #0 NEED TO DO THIS, AND I THINK BUILD 256 BYTES OR... PERHAPS I NEED TO USE A SPECIAL 1 BYTE BUFFER FOR THIS PART?
CMP #LF
;BNE GO
BEQ SKIP ; SKIP THE LF TO AVOID DOUBLE SPACED LINES, WE WILL STILL GET THE CARRIAGE RETURNS
;LDA #CR
GO
PHA
fwrite_wrapper_out_retry
LDA ACIA_status
AND #$10
BEQ fwrite_wrapper_out_retry
PLA
STA ACIA_data
SAVPPRINTLIST
stz PORTA ; Set chip select low BEGIN PACKET
PHY
jsr spi_transceive ; Send
INC $50
PLY
lda #CS
sta PORTA ; END PACKET
;PHA No need to push A, I don't care what came back in A from spi
LDA $70
BEQ SAVPDONE
;PLA
SKIP
RTS
SAVPDONE
; Complete command, i.e. 256 bytes
PHA
LDA $50
TAX
PLA
JSR spi_spi_complete_cmd
jsr SAV2init_iovectors
jmp LAB_1319 ; cleanup and Return to BASIC
rts
SAV2init_iovectors:
lda #<ACIAout
sta VEC_OUT
lda #>ACIAout
sta VEC_OUT+1
rts
LAB_vec
.word ACIAin ; input vector byte in from simulated ACIA
.word ACIAout ; output vecto byte out to simulated ACIA
.word LOAD ; load vector
.word SAVE ; save vector
;.word no_load ; null load vector for EhBASIC
;.word no_save ; null save vector for EhBASIC
; EhBASIC IRQ support
IRQ_CODE
PHA ; save A
LDA IrqBase ; get the IRQ flag byte
LSR ; shift the set b7 to b6, and on down ...
ORA IrqBase ; OR the original back in
STA IrqBase ; save the new IRQ flag byte
PLA ; restore A
RTI
; EhBASIC NMI support
NMI_CODE
PHA ; save A
LDA NmiBase ; get the NMI flag byte
LSR ; shift the set b7 to b6, and on down ...
ORA NmiBase ; OR the original back in
STA NmiBase ; save the new NMI flag byte
PLA ; restore A
RTI
END_CODE
LAB_mess
.byte $0D,$0A,"6502 EhBASIC [C]old/[W]arm ?",$00
; sign on string
; ###########################################################
; UTILITIES
; ###########################################################
delay
phx
phy
ldx #2
ldy #1
delloop
dey
bne delloop
dex
bne delloop
ply
plx
rts
; ######################################################################
; INITSPI
; ######################################################################
INITSPI:
;Initialize SPI
lda #CS
sta PORTA
lda #%00000111 ; cs/mosi/sck (MISO should be low)
sta DDRA
RTS
; ######################################################################
; SNDCMD
; ######################################################################
SNDESAVECMD:
LDY #$0
PRNT
LDA IN,Y
BEQ SNDESAVECMDDONE
stz PORTA ; Set chip select low BEGIN PACKET
PHY
jsr spi_transceive ; Send
PLY
lda #CS
sta PORTA ; END PACKET
INX
INY
BNE PRNT
SNDESAVECMDDONE
RTS
; ######################################################################
; SND
; ######################################################################
SND:
LDY #$0
PRINT
LDA (MSGL),Y
BEQ DONE
stz PORTA ; Set chip select low BEGIN PACKET
PHY
jsr spi_transceive ; Send
PLY
lda #CS
sta PORTA ; END PACKET
INX
INY
BNE PRINT
DONE
RTS
; ######################################################################
; SPI COMPLETE COMMAND
; ######################################################################
spi_spi_complete_cmd:
stz PORTA ; Set chip select low BEGIN PACKET
lda #$0
jsr spi_transceive ; Send
lda #CS
sta PORTA ; END PACKET
inx
cpx #$0
bne spi_spi_complete_cmd
RTS
; ######################################################################
; SPI TRANSCEIVE
; ######################################################################
spi_transceive:
stz spiin ; Clear the input buffer
sta spiout ; store the output data
ldy #8 ; Initialize the bit counter
lda #MOSI ; Set the A register to the MOSI bit mask
spi_loop:
asl spiout ; Move mSB of output into carry flag; next bit to send
bcs spi_1 ; Test the carry flag
trb PORTA ; MSB was zero, set MOSI LOW
jmp spi_2
spi_1:
tsb PORTA ; MSB was 1, set MOSI HIGH
spi_2:
inc PORTA ; Set SCK high
bit PORTA ; Put MOSI into overflow flag
clc ; Clear the carry flag
bvc spi_3 ; Test overflow flag
sec
spi_3:
rol spiin ; Rotate the carry flag into the receive buffer
dec PORTA ; set SCK low
dey ; Decrement the bit counter
bne spi_loop ; Continue sending/receiving bits
lda spiin ; Done; put the received data into A
clc
rts
SHWMSG LDY #$0
PRINT2 LDA IN,Y
BEQ DONE2
JSR $FFEF
INY
BNE PRINT2
DONE2 RTS
; PICO PARALLEL COMMANDS
ESAVE: .asciiz "ESAVE "
ELOAD: .asciiz "ELOAD "
ELIST: .asciiz "LIST"
_fd: .res 1
For the save command indicator, i.e. to indicate the list command is done, at the beginning of a save I set location $70 to $FF, then I watch in the code above for location $70 to become $00. This occurs when READY is printed at the end of a list, by the addition of the following line in basic.asm
Code:
LAB_1274
; clear ON IRQ/NMI bytes
LDA #$00 ; clear A
STA $70 ; INDIATE PICO SAVE COMMAND DONE
STA IrqBase ; clear enabled byte
STA NmiBase ; clear enabled byte