neilsf / xc-basic3

A BASIC cross compiler for MOS 6502-based machines
MIT License
44 stars 5 forks source link

Pet load and save routines tested and seem to work ok please add #257

Open davejduke opened 8 months ago

davejduke commented 8 months ago

The code is for basic 4, the comments shows calls for basic 2.

working code is

"LOAD" sample

LDA #FNTABLE.LO     ;PLACE POINTER TO FILE NAME IN ($DA)
STA $DA
LDA #FNTABLE.HI
STA $DB

LDA #$00            ; SPECIFY ADDRESS TO LOAD TO (IGNORE ADDRESS IN FILE)
STA $FB             ; IN THIS SAMPLE LOAD DIRECTLY TO THE SCREEN ($8000)
LDA #$80
STA $FC

LDA #FNTABLE.LENGTH ; SET FILENAME LENGTH (5 IN THIS CASE)
STA $D1

LDA #$08            ; SET DEVICE ADDRESS (8 IS DISK DRIVE)
STA $D4

LDA #$00
STA $9D             ; SPECIFY LOAD (AS OPPOSED TO LOAD/VERIFY)
STA $96             ; CLEAR THE STATUS BYTE

LDA #$60            ; SET SECONDARY ADDRESS TO 0 (OR'ED WITH #60)
STA $D3

JSR $F4A5           ; SEND NAME TO IEEE (F466 IN BASIC 2)
JSR $F0D2           ; SEND 'TALK' (F0B6 IN BASIC 2)

LDA $D3
JSR $F193           ; SEND SECONDARY ADDRESS (F128 IN BASIC 2)
JSR $F1C0           ; GET LOW BYTE OF ADDRESS (F18C IN BASIC 2) - USE IT IF YOU WANT, GOES INTO $FB
LDA $96             ; CHECK STATUS BIT (2ND FROM RIGHT)
LSR A
LSR A
BCC CNT             ; STATUS OK, SKIP ERROR OUT
JMP $F3C1           ; ABORT FILES, PRINT ?FILE NOT FOUND ERROR AND EXIT (F56E IN BASIC 2)

CNT JSR $F1C0 ; GET HIGH BYTE OF ADDRESS JSR $F1C0 ; READ 1 MORE BYTE TO SETUP FOR LOAD ROUTINE JSR $F391 ; REJOIN LOAD (WITHOUT PRINTING 'LOADING ...'). (F355 IN BASIC 2) RTS

.FNTABLE "FILE1" ($46 $49 $4C $45 $31)

"SAVE" sample

LDA #FNTABLE.LO     ;PLACE POINTER TO FILE NAME IN ($DA)
STA $DA
LDA #FNTABLE.HI
STA $DB

LDA #$00            ; SPECIFY SAVE START ADDRESS 
STA $FB             ; IN THIS SAMPLE SAVE DIRECTLY FROM THE SCREEN ($8000)
LDA #$80
STA $FC

LDA #$E9            ; SPECIFY SAVE END ADDRESS +1 (BOTTOM RIGHT OF SCREEN)
STA $C9             ; IN THIS SAMPLE SAVE DIRECTLY FROM THE SCREEN ($83E8)
LDA #$83
STA $CA

LDA #FNTABLE.LENGTH ; SET FILENAME LENGTH (5 IN THIS CASE)
STA $D1

LDA #$08            ; SET DEVICE ADDRESS (8 IS DISK DRIVE)
STA $D4

LDA #$00
STA $96             ; CLEAR THE STATUS BYTE

LDA #$61            ; SET SECONDARY ADDRESS TO 1 (OR'ED WITH #60)
STA $D3

JSR $F6E3           ; PREFORM "SAVE" (F6A4 IN BASIC 2)
RTS

.FNTABLE "FILE1" ($46 $49 $4C $45 $31)

davejduke commented 8 months ago

ok so you know which code to compile in,

targets pet2001 - Just use basic 2 in case it was upgraded to basic2 pet3008 -basic 2 pet3016, -basic 2 pet3032 -basic 2, pet4016 -basic 4, pet4032 - basic 4 pet8032 - basic 4

Hope this helps!

davejduke commented 8 months ago

Thanks to Ken Mclveen from canda, we now have a working routines, this code saves and saves from $4000 to end of screen, and loads it again. Just an example of 16k ish save and load.

if peek(57016)=52 then print "basic 4" end if

rem ================================================= rem SAVEMEM rem ================================================= SUB savemem (name$ AS STRING * 10) dim c as BYTE c=1 rem TBUFFR 027A-0329 634-825 Tape I/O Buffer #1 DIM a(22) AS BYTE dim flen as byte flen=len(name$) FOR x as byte = 0 to flen poke 634+x , ASC(MID$(name$, x, c)) NEXT poke 1,len(name$)

rem save screen asm

lda #$7a
STA $DA
LDA #$02
STA $DB
LDA #$00            ; SPECIFY SAVE START ADDRESS 
STA $FB             ; IN THIS SAMPLE SAVE DIRECTLY FROM THE SCREEN ($8000)
LDA #$40
STA $FC
LDA #$D0            ; SPECIFY SAVE END ADDRESS +1 (BOTTOM RIGHT OF SCREEN)
STA $C9             ; IN THIS SAMPLE SAVE DIRECTLY FROM THE SCREEN ($83E8)
LDA #$87
STA $CA
LDA $01 ; SET FILENAME LENGTH (5 IN THIS CASE)
STA $D1
LDA #$08            ; SET DEVICE ADDRESS (8 IS DISK DRIVE)
STA $D4
LDA #$00
STA $96             ; CLEAR THE STATUS BYTE
LDA #$61            ; SET SECONDARY ADDRESS TO 1 (OR'ED WITH #60)
STA $D3
JSR $F6E3           ; PREFORM "SAVE" (F6A4 IN BASIC 2)
RTS

end asm end sub

sub loadmem(name$ as string*10) rem ========================================== rem load screen rem ========================================== dim c as BYTE c=1 rem TBUFFR 027A-0329 634-825 Tape I/O Buffer #1 DIM a(22) AS BYTE dim flen as byte flen=len(name$) FOR x as byte = 0 to flen poke 634+x , ASC(MID$(name$, x, c)) NEXT poke 1,len(name$)

asm

lda #$7a
STA $DA
LDA #$02
STA $DB

LDA #$00  ; load to address
STA $FB 
LDA #$40
STA $FC

LDA $01 ; file len
STA $D1

LDA #$08 ;device name        
STA $D4

LDA #$00
STA $9D          
STA $96              

LDA #$60         
STA $D3

JSR $F4A5           
JSR $F0D2           

LDA $D3
JSR $F193           
JSR $F1C0           
JSR $F1C0           
JSR $F391           
rts

end asm print "saved" end sub call savemem("screenshot") print "{clr}" call loadmem("screenshot") print "loaded"

What would be good is a command that does this

Savemem("filename",$source,$dest_or_length) loadmem("filename",destination address of where to load into)

use this code to detect if basic 4 or 2

if peek(57016)=52 then print "basic 4" else print "basic2"

end if

neilsf commented 8 months ago

Does the above work in XC=BASIC now? I haven't yet had the time to test it out.

davejduke commented 8 months ago

Will get you the latest one over soon , I have file delete now also, just got the flu at the moment On 4 Feb 2024, at 14:49, Csaba Fekete @.***> wrote: Does the above work in XC=BASIC now? I haven't yet had the time to test it out.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: @.***>

kochs-online commented 4 months ago

Any update on this? Does it mean we can expect READ# and WRITE# to work on PETs any time soon? :smiley:

neilsf commented 4 months ago

Any update on this? Does it mean we can expect READ# and WRITE# to work on PETs any time soon? 😃

Due to the lack of unified KERNAL API in the PET, file I/O would need to be implemented differently for each PET versions. I cannot undertake this work for now.

The community accepted a trade-off when XC=BASIC was made compatible with the PET, and this trade-off was that file I/O could not be implemented.

kochs-online commented 2 months ago

"SAVE" sample

LDA #FNTABLE.LO               ; PLACE POINTER TO FILE NAME IN ($DA)
STA $DA
LDA #FNTABLE.HI
STA $DB

LDA #$00          ; SPECIFY SAVE START ADDRESS 
STA $FB               ; IN THIS SAMPLE SAVE DIRECTLY FROM THE SCREEN ($8000)
LDA #$80
STA $FC

LDA #$E9          ; SPECIFY SAVE END ADDRESS +1 (BOTTOM RIGHT OF SCREEN)
STA $C9               ; IN THIS SAMPLE SAVE DIRECTLY FROM THE SCREEN ($83E8)
LDA #$83
STA $CA

LDA #FNTABLE.LENGTH           ; SET FILENAME LENGTH (5 IN THIS CASE)
STA $D1

LDA #$08          ; SET DEVICE ADDRESS (8 IS DISK DRIVE)
STA $D4

LDA #$00
STA $96               ; CLEAR THE STATUS BYTE

LDA #$61          ; SET SECONDARY ADDRESS TO 1 (OR'ED WITH #60)
STA $D3

JSR $F6E3         ; PERFORM "SAVE" (F6A4 IN BASIC 2)
RTS

.FNTABLE "FILE1" ($46 $49 $4C $45 $31)

I just tested this code and can confirm it works fine for BASIC 4.

It's important to note, that FNTABLE.HI and FNTABLE.LO have to point to the first character of the filename string, i.e. skipping the preceding string length byte!

kochs-online commented 2 months ago

Adding relevant addresses for BASIC 1 (taken from "Programming the PET/CBM" by Raeto Collin West):

BASIC 1 BASIC 2 BASIC 4 Description
($F9) ($DA) ($DA) Pointer to filename
$EE $D1 $D1 Filename length
$F1 $D4 $D4 Device number
$F0 $D3 $D3 Secondary address
($F7) ($FB) ($FB) Pointer to save start address
($E5) ($C9) ($C9) Pointer to save end address
$020C $96 $96 Status byte ST
$F6B1 $F6A4 $F6E3 MLM ROM routine SAVE
kochs-online commented 2 months ago

I tested all BASIC variants of the SAVE example in VICE:

BASIC 1 did mess up the filename though. Turns out "Programming the PET/CBM" contains an error in the zero-page listing on page 395: The correct address for the filename length is $EE instead of $ED as in the book! (Although it is listed correctly in a footnote on page 420.)

I fixed this in my comment above now. Using $EE for the filename length my test for BASIC 1 was successful as well.

davejduke commented 2 months ago

Sweet glad I could help.Dave On 4 Aug 2024, at 20:42, Tobias @.***> wrote: I tested all variants in VICE:

PET 2001: BASIC 1 PET 3008: BASIC 2 PET 8032: BASIC 4

BASIC 1 did mess up the filename though. Turns out "Programming the PET/CBM" contains an error in the zero-page listing on page 395: The correct address for the filename length is $EE instead of $ED as in the book! (Although it is listed correctly in a footnote on page 420.) I fixed this in my comment above now. Using $EE for the filename length my test for BASIC 1 was successful as well.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: @.***>

neilsf commented 2 months ago

Thank you both for your work on investigating this. I scheduled implementing LOAD/SAVE for PET in version V3.2.0.

kochs-online commented 1 month ago

"LOAD" sample

LDA #FNTABLE.LO           ; PLACE POINTER TO FILE NAME IN ($DA)
STA $DA
LDA #FNTABLE.HI
STA $DB

LDA #$00          ; SPECIFY ADDRESS TO LOAD TO (IGNORE ADDRESS IN FILE)
STA $FB               ; IN THIS SAMPLE LOAD DIRECTLY TO THE SCREEN ($8000)
LDA #$80
STA $FC

LDA #FNTABLE.LENGTH       ; SET FILENAME LENGTH (5 IN THIS CASE)
STA $D1

LDA #$08          ; SET DEVICE ADDRESS (8 IS DISK DRIVE)
STA $D4

LDA #$00
STA $9D               ; SPECIFY LOAD (AS OPPOSED TO LOAD/VERIFY)
STA $96               ; CLEAR THE STATUS BYTE

LDA #$60          ; SET SECONDARY ADDRESS TO 0 (OR'ED WITH #60)
STA $D3

JSR $F4A5         ; SEND NAME TO IEEE (F466 IN BASIC 2)
JSR $F0D2         ; SEND 'TALK' (F0B6 IN BASIC 2)

LDA $D3
JSR $F193         ; SEND SECONDARY ADDRESS (F128 IN BASIC 2)
JSR $F1C0         ; GET LOW BYTE OF ADDRESS (F18C IN BASIC 2) - USE IT IF YOU WANT, GOES INTO $FB
LDA $96               ; CHECK STATUS BIT (2ND FROM RIGHT)
LSR A
LSR A
BCC CNT               ; STATUS OK, SKIP ERROR OUT
JMP $F3C1         ; ABORT FILES, PRINT ?FILE NOT FOUND ERROR AND EXIT (F56E IN BASIC 2)

CNT JSR $F1C0         ; GET HIGH BYTE OF ADDRESS
;JSR $F1C0            ; READ 1 MORE BYTE TO SETUP FOR LOAD ROUTINE
JSR $F391         ; REJOIN LOAD (WITHOUT PRINTING 'LOADING ...'). (F355 IN BASIC 2) RTS

.FNTABLE "FILE1" ($46 $49 $4C $45 $31)

I tested the code for the LOAD example as well and there were some minor difficulties. (It would have been nice to know that these code samples are based on the code in chapter 6.6 "Machine-code programming with CBM disk drives" of "Programming the PET/CBM" for all the additional explanations given there…)

I had to remove the second JSR $F1C0 after jump label CNT (cp. code in quote above). I couldn't figure out why this would help to "setup for LOAD routine" and the byte is obviously not available for the LOAD routine then.

It was a bit trickier to find out that the given BASIC 4 ROM routine addresses aren't exactly spot-on and the book is similarly contradictory as with the BASIC 1 filename length zero-page address (referring to my comment above).

Here are the differences I implemented for BASIC 4: address replaced by reason
$F193 $F143 additionally checks for time-out
$F3C1 $F5AD simply wrong, I think; $F3C1 is at the of the LOAD routine $F356 in BASIC 4, not part of the error handling
$F391 $F38F include required LDA #$FD

I also tried to find the equivalent BASIC 1 routines and found all but the routine that reads data to memory ($F355/$F38F in BASIC 2/BASIC 4). "Programming the PET/CBM" lists $F37E for this, but it's implemented completely different from BASIC 2 & 4 and seems to be part of the direct mode LOAD BASIC command which expects to drop back to BASIC in the end and thus jumps to $C581 to clear the stack! Took me about two days to find out what went wrong there and I don't want to touch it again with a 10-foot pole… :laughing:

After all these are the addresses and ROM routines I managed to find for LOAD: BASIC 1 BASIC 2 BASIC 4 Description
($F9) ($DA) ($DA) Pointer to filename
$EE $D1 $D1 Filename length
$F1 $D4 $D4 Device number
$F0 $D3 $D3 Secondary address
$020B $9D $9D LOAD (#$00) or VERIFY (#$01)
$020C $96 $96 Status byte ST
($F7) ($FB) ($FB) Pointer to memory address to load file contents to
$F462 $F466 $F4A5 Send filename string to IEEE-488 bus
$F0B6 $F0B6 $F0D2 Send "Talk" to IEEE-488 bus
$F12C $F128 $F143 Send command to IEEE-488 (and clear ATN line)
$F187 $F18C $F1C0 Read single byte from IEEE-488 bus into accumulator
$F579 $F56E $F5AD Print "?FILE NOT FOUND" error and exit to BASIC (!)
$F355 $F38F Read data to memory until EOI (end of input) as indicated by ST value #$40

And finally I came up with some code to replace the missing/inadequate BASIC 1 routine to read data to memory until EOI is encountered.

This is based on the ROM routine at $F37E, but stripped of the parts that are used for VERIFY and returning to BASIC afterwards:

iF37E jsr $F339 ; check stop key
   ldx $020C    ; read status byte
;   bvs iF3CC   ; (used in ROM; overflow is set when reading after EOI & accumulator holds #$0D; used for INPUT#?)
   bne iF3CC    ; do not wait for overflow (set when reading after EOI), but exit as soon as status != 0
   jsr $F187    ; read byte from IEEE-488 bus (NB: Will return $0D when reading after EOI!)

   ; skip usual LOAD/VERIFY check here

    ldy #$00    ; make sure Y register is 0 before using it for indirect STA
        ; TBD: use X register [holding status byte] instead?!

    sta ($F7),Y ; write byte to store address
    inc $F7 ; increment store address
    bne iF37E
    inc $F7+1
    jmp iF37E

iF3CC jsr $EF18 ; UNTALK & UNLISTEN, CLOSE IEEE-488

I used the BASIC 1 ROM addresses as labels [iF37E and iF3CC] so you can find the original code in disassembled ROMs if you want to compare (or in case I missed something).

The assembler code and all ROM routines are tested in VICE for BASIC 1, BASIC 2 and BASIC 4.

I haven't tested the FILE NOT FOUND error in depth though. Currently this should drop back to BASIC for all versions. I guess, this is not expected behavior in XC=BASIC?

neilsf commented 6 days ago

Great job, thanks.

I haven't tested the FILE NOT FOUND error in depth though. Currently this should drop back to BASIC for all versions. I guess, this is not expected behavior in XC=BASIC?

No, XC=BASIC will execute its own error trapping mechanism.