BASin - How to load code from assembler?

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
sn3j
Manic Miner
Posts: 505
Joined: Mon Oct 31, 2022 12:29 am
Location: Germany

BASin - How to load code from assembler?

Post by sn3j »

I'm trying to load a code block from my assembler routine without the print output to the screen.
So I've prepared the 17-byte header in IX and called LD_BYTES at ROM $556.
Behind that header I reserved the usual 17-byte space for the header to be received.
Address $556 is being trapped by BASin/BasinC, and what happens now is that the name in the header request (part 1 of the 34-byte area) is being replaced by something entirely different - either the file name from the last LOAD, the name of the Basic project if you saved it, or the name of an auto backup (autoback0..9).
Also, I find the same file name in the header received (part 2 of the area).
Which means, my load request is happily ignored and replaced with some spurious data held by BASin.
Bananas! Potzblitz!
I don't intend to have a tape attached, it would be nice to have it loaded from BASin's work directory.
How could I achieve this from within my code?
POKE 23614,10: STOP      1..0 hold, SS/m/n colors, b/spc toggle
Timmy
Manic Miner
Posts: 230
Joined: Sat Apr 23, 2022 7:13 pm
Location: The Netherlands

Re: BASin - How to load code from assembler?

Post by Timmy »

If you call the ROM code directly to load data, then it expects only the data part of the tape, without the header.

If you are lazy, then just call the ROM routine twice. The first time it loads the header (which you ignore), and use the same routine to load the data afterwards. :)
User avatar
Joefish
Rick Dangerous
Posts: 2060
Joined: Tue Nov 14, 2017 10:26 am

Re: BASin - How to load code from assembler?

Post by Joefish »

As far as I know, the only way to do it is to have a .TAP file attached as a tape. It doesn't jump out to any sort of special development environment file-handling, it just looks for the next sequential unit in a tape file, like any other emulator would.

I haven't tried loading using ROM calls from within the BASin emulation, but I've used the LOAD command from BASIC in BASin, to use my CharAde game engine, which is a chunk of machine code. I put something at the end of the BASIC program like:

9998 STOP
9999 CLEAR xxxxx : LOAD "" CODE : GOTO 1

I can then GOTO 9999 to load the CODE (after attaching a .TAP file containing just my code block) and the CLEAR and the CODE stays in the simulated machine's memory from one run of the BASIC program to another. And when I finally save the program, I re-load it all into a regular emulator, set up a simulated SAVE tape, and save it with LINE 9999. Then re-save the CODE immediately after it on the same tape.
sn3j
Manic Miner
Posts: 505
Joined: Mon Oct 31, 2022 12:29 am
Location: Germany

Re: BASin - How to load code from assembler?

Post by sn3j »

There's a 2nd trap in the ROM at $63a (1594), this is where the file name is taken from by BASin for the next LD_BYTES call.
Am I supposed to call this trap from assembler? It's a bit clumsy to mock all up.

Image
POKE 23614,10: STOP      1..0 hold, SS/m/n colors, b/spc toggle
sn3j
Manic Miner
Posts: 505
Joined: Mon Oct 31, 2022 12:29 am
Location: Germany

Re: BASin - How to load code from assembler?

Post by sn3j »

Thank you for commenting @Joefish and @Timmy.
Here's my hacky solution to get a file loaded from code:
  • Set up a 34-byte area with the following data:
    • [0] 3 - this byte tells we're requesting a CODE/SCREEN$ file
    • [1..10] - the name of our file, padded with blanks if less than 10 chars
    • [11..12] - requested length, may be zero, but better safe than sorry so set it to limit the loaded data to an allowed maximum
    • [13..14] - destination address of the data
    • [15..16] - zero/unused
    • [17..33] - 17 bytes space where the loaded header fits in
  • Set up a single byte with value 13, needed to pretend an end of line (any address will do).
  • From your code, set syntax evaluation mode, set CHADD to the end of line, set TADDR to 1 ('load') and call the ROM at $634 - this will provide BASin with your file name so that LD_BYTES knows where to find it.
    The syntax evaluation mode together with the end of line condition will cause the routine to return from ROM.
  • Restore CHADD and switch off syntax evaluation, then call LD_BYTES to get the header. Afterwards call it again to get the file content.
Here's the assembler listing. (Some commas may be missing, it's from my notepad.)

Code: Select all

FileHeader:
 .db 3                 ; [0] type: CODE/SCREEN$
 .db [10]              ; [1-10] name
 .dw 0                 ; [11-12] len
 .dw 0                 ; [13-14] start
 .dw 0                 ; [15-16] (proglen)
 .db[17]               ; work header
EndOfLine:
 .db 13

LoadFile               ; load code/screens from assembler in BASin
--                     ; ix = &header (34 bytes)
    ld hl (CHADD)
    push hl            ; save (CHADD)
    ld hl EndOfLine
    ld (CHADD) hl      ; indicate end of line
    ld (iy+58) 1       ; (TADDR) = 'load'
    res 7 (iy+1)       ; enable syntax checking mode
    push ix
    pop de
    inc de             ; de = &name
    ld bc 10           ; len
    ld hl LoadFile2
    push hl            ; set return addr to LoadFile2
    call $634          ; this call will drop its return addr and so continues at LoadFile2

Code: Select all

LoadFile2:             ; load file without printing to screen, pt.2
    pop hl
    ld (CHADD) hl      ; restore (CHADD)
    set 7 (iy+1)       ; disable syntax checking mode
    ld l (ix+13)
    ld h (ix+14)       ; hl = &dest
    ; from ROM $761, without the prints
    push hl            ; save &dest
    ld bc 17
    add ix bc          ; ix = work header
  L1:
    push ix
    ld de 17           ; load 17 bytes
    xor a              ; 'header'
    scf                ; 'load'
    call $556          ; LD_BYTES, trapped by BASin
    ; BASin jumps to REPORT_R if the file length is too small
    pop ix
    jr nc L1           ; repeat if error
    ld c 128
    ld a (ix+0)
    cp (ix-17)         ; compare file types
    jr nz 2
     ld c 246          ; types match, so count from -10
    cp 4
    jr nc L1           ; nonsense type, repeat
    push ix
    pop de
    ld hl $fff0
    add hl de          ; point to requested/retrieved name
    ld b 10
    ld a (hl)
    inc a              ; no name requested?
    jr nz 3
     ld a c
     add b
     ld c a            ; yes, always match
  L2:
    inc de
    ld a (de)
    cp (hl)            ; compare requested with retrieved name
    inc hl
    jr nz 1
     inc c             ; +1 for each matching char
    djnz L2
    bit 7 c            ; do names match?
    jr nz L1           ; no, load next header
    ; from ROM $7cc, load CODE/SCREEN$ body (you could also do a JP $7cc to spare 40 bytes of code, it's just a tad slower)
    ld l (ix-6)
    ld h (ix-5)        ; requested length
    ld e (ix+11)
    ld d (ix+12)       ; retrieved length (BASin cuts it to requested length if a file with greater length was found)
    ld a h
    or l
    jr z L3            ; take retrieved length if requested length not specified
    sbc hl de
    jr c L_ex
  L3:
    pop hl             ; restore &dest
    ld a h
    or l
    jr nz L4
    ld l (ix+13)
    ld h (ix+14)       ; take dest from header if not specified
  L4:
    push hl
    pop ix             ; ix = &dest
    scf                ; 'load'
    ld a $ff           ; 'data'
    call $556          ; load file content to ix
    ret c
  L_ex:
    rst err
    .db $1a            ; tape loading error
POKE 23614,10: STOP      1..0 hold, SS/m/n colors, b/spc toggle
Post Reply