That piece I did yesterday was just typed up in Notepad and pasted into Spin's assembler. On a real 48k Spectrum I use OCP's Editor/Assembler and Test Tool, but I'd expect them to be incompatible with the 128k+3. As you might have gathered, machine coding on the 128k Speccies is even trickier than usual because of the memory bank paging.
I cross-develop under Linux, using zmakebas to generate BASIC loaders, z80asm to assemble machine code and taptools to combine the results into a tapefile.
In the case of this program, where I had to generate a BASIC program that poked machine code into a REM statement, I wrote a custom program in C to generate the listing from the output of z80asm. Then I used zmakebas to convert the listing into a tapefile that I could load and test. (Exercise for the interested reader: spot the minor mistake I made in this process).
Correct me if I'm wrong, but wouldn't such a low CLEAR leave virtually no space for anything else than the BASIC MC loader? Why not put such code just below the UDG area, and leave the whole RAM for the BASIC program?
Code that calls +3DOS has to be below 0C000h. And the stack has to be below 0BFE0h. That's why I put my code (and stack) in a REM statement -- it's very unlikely that the program will start that high in memory.
You can look at the disassembly in Spin. Just load & run that BASIC demo from #30, select Tools->Debugger, right-click in the disassembly pane, select Go To ..., type in the start address of the m/code and you can scroll through the disassembly from there.
But exist a way to copy and paste the basic code on teh zx spin ?, because the asm editor allow us to copy and paste.
If there is, it's very well hidden ...
If I want to load a BASIC program from a text file I use Martijn's BAS2TAP which is in the Utilities section here. There's BASin as well, of course, but I've never used that - and Taper - Convert which comes with the Z80 emulator - probably others that I don't know about.
spodula, can you show the assembler code of this last one you posted ? I am trying to understand it.
thx in advance.
Again, this is battlebunny's code, not mine. I'm just doing some hand-disassembly using the manual. Just like old times :p
Note, while doing this i have spotted some errors with my code. Ah well.
Basically, i didnt change the address of the filename in the code. Likewise, the stack and its storage should be set above RAMTOP.
;Initially, we need to setup an environment to call the DOS routines.
;This consists of paging in ROM 2 to $0000-$3FFF and RAM 7 to $C000-$FFFF
48896 DI ;While doing paging, Interrupts should be disabled
;so we dont page out something important.
;(Not required in this code because its in the
;middle 32K, but its a good habit to get into anyway)
48897 LD (36864),SP ;Store the BASIC stack pointer. Note, this again
;is probably a bit supurfluous as RAMTOP is in
;the middle 32K
;When messing with memory pages from a program
;that expects to return to BASIC, or a program that
;expects to use the ROM routines, we need to keep the contents of
;the BANKM system variable up to date with the last value written to 7FFD
48901 LD BC, 32765 ;Load BC with the address of the paging port. 7FFD
48904 LD A,(23388) ;Get the contents of BANKM (last value written to 7FFD)
48907 RES 4,A ;Set bit for ROM LSB to 0 (switching ROM3 to ROM2)
48909 OR %00000111 ;Merge in 7 (Selecting RAM page 7 to $C000)
48911 LD (23387),A ;set last value outputted
48914 OUT (C),A ;and actually output it.
48916 LD SP, 40959 ;Set a stack above ramtop, but below $C000
48919 EI ;and re-enable interrupts.
;This bit of the code is responsible for calling the ROM routine.
;This attempts to open the named file and returns 1 if it succeeds, or 0
;if fails. (See the +3 Manual for more information on DOS_OPEN
48920 LD B,3 ;Choose DOS channel 3
48922 LD C,5 ;Access mode shared-read
48924 LD D,0 ;Create action = 0. (Ie, Dont, just Error if file doesnt exist)
48926 LD E,1 ;Open action = 1. (Open the file)
48928 LD HL,28659 ;Address of the filename (Note, this is where my error is!)
48931 CALL 262 ;call DOS_OPEN ($106)
48934 LD BC,0 ;Default return value = 0
48937 JR NC,+3 ;if carry=false, skip over the next statement.
48939 LD BC,1 ;If however, that isnt the case, return value = 1
48941 PUSH BC ;store return value
48942 LD BC, 32765 ;Load BC with the address of the paging port. 7FFD
48945 LD A,(23387) ;Get the contents of BANKM
48948 SET 4,A ;Set bit for ROM LSB to 1 (switching ROM2 to ROM3)
48950 AND %11111000 ;Reset the last three bits. (for Ram page 0)
48952 LD (23387),A ;set last value outputted
48955 OUT (C),A ;and actually output it.
48957 POP BC ;Set back our return value from the stack.
48958 LD SP,(36864) ;And restore the original stack.
48961 RET ;and were done.
;For details of the following block, see above.
48962 DI
48963 LD (36864),SP
48967 LD BC, 32765
48970 LD A,(23387)
48973 RES 4,A
48975 OR %00000111
48977 LD (23387),A
48980 OUT (C),A
48982 LD SP, 40959
48985 EI
;This simply calls DOS_CLOSE to ensure we dont leave the file
;hanging.
48986 LD B,3 ;DOS channel #3
48988 CALL 265 ;call DOS_CLOSE
;For details of the following block, see above.
48991 LD BC, 32765
48994 LD A,(23387)
48996 SET 4,A
48998 AND %11111000
49000 LD (23387),A
49003 OUT (C),A
49005 LD SP,(36864)
49008 RET
As proposed in #21, here's a version of the file check routine which uses DEF FN to define the file name as a parameter, so it doesn't need to be POKEd anywhere and the code becomes relocatable. Done with some help from chapter 9 of Toni Baker's "Mastering Machine Code" book.
The function definition is:
DEF FN o(f$)=USR 49050
where f$ is the file name + CHR$ 255.
;set up DOS environment
di ; 49050 243
ld bc, 32765 ; 49051 1 253 127
ld a, (23388) ; 49054 58 92 91
res 4, a ; 49057 203 167
or 7 ; 49059 246 7
ld (23388), a ; 49061 50 92 91
out (c), a ; 49064 237 121
;check if file exists
ld hl, (23563) ; 49066 42 11 92 ;DEFADD holds start of FN o(f$) arg
ld de, 4 ; 49069 17 4 0
add hl, de ; 49072 25 ;HL=location of f$ address
ld e, (hl) ; 49073 94 ;E =lo byte of f$ address
inc hl ; 49074 35
ld d, (hl) ; 49075 86 ;D =hi byte of f$ address
ex de, hl ; 49076 235 ;HL=f$ address
ld bc, 773 ; 49077 1 5 3 ;B =file ID 3, C =access mode 5
ld de, 1 ; 49080 17 1 0 ;D =create action 0, E =open action 1
call 262 ; 49083 205 6 1 ;call DOS-OPEN
ld bc, 0 ; 49086 1 0 0 ;BC=flag error
jr nc, l_bfc6 ; 49089 48 3 ;jump if error (Carry unset)
ld bc, 1 ; 49091 1 1 0 ;BC=flag OK
l_bfc6: push bc ; 49094 197 ;save status
ld b, 3 ; 49095 6 3 ;B =file ID 3
call 265 ; 49097 205 9 1 ;call DOS-CLOSE
;restore BASIC environment
ld bc, 32765 ; 49100 1 253 127
ld a, (23388) ; 49103 58 92 91
set 4, a ; 49106 203 231
and 248 ; 49108 230 248
ld (23388), a ; 49110 50 92 91
out (c), a ; 49113 237 121
ei ; 49115 251
pop bc ; 49116 193 ;restore status
ret ; 49117 201 ;return status
and why not to get the filename from the same asm code ? example from some var .. or dont know. Imagine that we want to load a file that was selected in a menu. Then we must pass the filename to these routines. How can be this done ?
and why not to get the filename from the same asm code ? example from some var .. or dont know. Imagine that we want to load a file that was selected in a menu. Then we must pass the filename to these routines. How can be this done ?
Errrm ... use DEF FN to pass arguments to USR routines, as exampled in the listing in #40 above. Check your PMs for a link to the MENU program which I sent you yesterday, which does just that.
well examining the ams codes on the previous page of this topic, i dont understand where the filename text is passed to the routine. Maybe i dont see because i expected to do all from the asm code with no basic.
There's an example of its use in the program I sent to you the other day. Specifically, the use of FN o(f$) in these lines:
10 CLEAR 46999: GO SUB 9120: GO SUB 9134: GO SUB 9220: DEF FN o(f$)=USR 49050: DEF FN p(t,e)=USR 47000
...
900 LET r$=l$(item): IF FN o(r$+CHR$ 255) THEN CLS : LOAD r$
The USR 49050 routine is the one given in #40 above, with comments explaining how it works. It references the DEFADD system variables area to get the file name passed in the FN o(f$) call.
If you want to pass the file name from the l$() array to the file check routine all in m/code then you'd have to pick up the program name from j$(), cross-reference the corresponding entry in l$(), then set up the address of that entry in HL so it could be used by the DOS-OPEN routine called from (a slightly modified version of) USR 49050. The file name string referenced by DOS-OPEN has to be terminated with FFh, so either every entry in l$() would need an FFh at the end, or (better) copy the selected one somewhere else and tag on an FFh before setting the address in HL.
Using m/code to search for variables in the VARS area and select an entry in a string array is also covered in the program which I sent the other day. Using string arrays defined outside of BASIC has been covered in another thread. My work here is done now.
Comments
In the case of this program, where I had to generate a BASIC program that poked machine code into a REM statement, I wrote a custom program in C to generate the listing from the output of z80asm. Then I used zmakebas to convert the listing into a tapefile that I could load and test. (Exercise for the interested reader: spot the minor mistake I made in this process).
Code that calls +3DOS has to be below 0C000h. And the stack has to be below 0BFE0h. That's why I put my code (and stack) in a REM statement -- it's very unlikely that the program will start that high in memory.
thx in advance.
But exist a way to copy and paste the basic code on teh zx spin ?, because the asm editor allow us to copy and paste.
If there is, it's very well hidden ...
If I want to load a BASIC program from a text file I use Martijn's BAS2TAP which is in the Utilities section here. There's BASin as well, of course, but I've never used that - and Taper - Convert which comes with the Z80 emulator - probably others that I don't know about.
Again, this is battlebunny's code, not mine. I'm just doing some hand-disassembly using the manual. Just like old times :p
Note, while doing this i have spotted some errors with my code. Ah well.
Basically, i didnt change the address of the filename in the code. Likewise, the stack and its storage should be set above RAMTOP.
;Initially, we need to setup an environment to call the DOS routines. ;This consists of paging in ROM 2 to $0000-$3FFF and RAM 7 to $C000-$FFFF 48896 DI ;While doing paging, Interrupts should be disabled ;so we dont page out something important. ;(Not required in this code because its in the ;middle 32K, but its a good habit to get into anyway) 48897 LD (36864),SP ;Store the BASIC stack pointer. Note, this again ;is probably a bit supurfluous as RAMTOP is in ;the middle 32K ;When messing with memory pages from a program ;that expects to return to BASIC, or a program that ;expects to use the ROM routines, we need to keep the contents of ;the BANKM system variable up to date with the last value written to 7FFD 48901 LD BC, 32765 ;Load BC with the address of the paging port. 7FFD 48904 LD A,(23388) ;Get the contents of BANKM (last value written to 7FFD) 48907 RES 4,A ;Set bit for ROM LSB to 0 (switching ROM3 to ROM2) 48909 OR %00000111 ;Merge in 7 (Selecting RAM page 7 to $C000) 48911 LD (23387),A ;set last value outputted 48914 OUT (C),A ;and actually output it. 48916 LD SP, 40959 ;Set a stack above ramtop, but below $C000 48919 EI ;and re-enable interrupts. ;This bit of the code is responsible for calling the ROM routine. ;This attempts to open the named file and returns 1 if it succeeds, or 0 ;if fails. (See the +3 Manual for more information on DOS_OPEN 48920 LD B,3 ;Choose DOS channel 3 48922 LD C,5 ;Access mode shared-read 48924 LD D,0 ;Create action = 0. (Ie, Dont, just Error if file doesnt exist) 48926 LD E,1 ;Open action = 1. (Open the file) 48928 LD HL,28659 ;Address of the filename (Note, this is where my error is!) 48931 CALL 262 ;call DOS_OPEN ($106) 48934 LD BC,0 ;Default return value = 0 48937 JR NC,+3 ;if carry=false, skip over the next statement. 48939 LD BC,1 ;If however, that isnt the case, return value = 1 48941 PUSH BC ;store return value 48942 LD BC, 32765 ;Load BC with the address of the paging port. 7FFD 48945 LD A,(23387) ;Get the contents of BANKM 48948 SET 4,A ;Set bit for ROM LSB to 1 (switching ROM2 to ROM3) 48950 AND %11111000 ;Reset the last three bits. (for Ram page 0) 48952 LD (23387),A ;set last value outputted 48955 OUT (C),A ;and actually output it. 48957 POP BC ;Set back our return value from the stack. 48958 LD SP,(36864) ;And restore the original stack. 48961 RET ;and were done. ;For details of the following block, see above. 48962 DI 48963 LD (36864),SP 48967 LD BC, 32765 48970 LD A,(23387) 48973 RES 4,A 48975 OR %00000111 48977 LD (23387),A 48980 OUT (C),A 48982 LD SP, 40959 48985 EI ;This simply calls DOS_CLOSE to ensure we dont leave the file ;hanging. 48986 LD B,3 ;DOS channel #3 48988 CALL 265 ;call DOS_CLOSE ;For details of the following block, see above. 48991 LD BC, 32765 48994 LD A,(23387) 48996 SET 4,A 48998 AND %11111000 49000 LD (23387),A 49003 OUT (C),A 49005 LD SP,(36864) 49008 RETThe function definition is:
DEF FN o(f$)=USR 49050
where f$ is the file name + CHR$ 255.
;set up DOS environment di ; 49050 243 ld bc, 32765 ; 49051 1 253 127 ld a, (23388) ; 49054 58 92 91 res 4, a ; 49057 203 167 or 7 ; 49059 246 7 ld (23388), a ; 49061 50 92 91 out (c), a ; 49064 237 121 ;check if file exists ld hl, (23563) ; 49066 42 11 92 ;DEFADD holds start of FN o(f$) arg ld de, 4 ; 49069 17 4 0 add hl, de ; 49072 25 ;HL=location of f$ address ld e, (hl) ; 49073 94 ;E =lo byte of f$ address inc hl ; 49074 35 ld d, (hl) ; 49075 86 ;D =hi byte of f$ address ex de, hl ; 49076 235 ;HL=f$ address ld bc, 773 ; 49077 1 5 3 ;B =file ID 3, C =access mode 5 ld de, 1 ; 49080 17 1 0 ;D =create action 0, E =open action 1 call 262 ; 49083 205 6 1 ;call DOS-OPEN ld bc, 0 ; 49086 1 0 0 ;BC=flag error jr nc, l_bfc6 ; 49089 48 3 ;jump if error (Carry unset) ld bc, 1 ; 49091 1 1 0 ;BC=flag OK l_bfc6: push bc ; 49094 197 ;save status ld b, 3 ; 49095 6 3 ;B =file ID 3 call 265 ; 49097 205 9 1 ;call DOS-CLOSE ;restore BASIC environment ld bc, 32765 ; 49100 1 253 127 ld a, (23388) ; 49103 58 92 91 set 4, a ; 49106 203 231 and 248 ; 49108 230 248 ld (23388), a ; 49110 50 92 91 out (c), a ; 49113 237 121 ei ; 49115 251 pop bc ; 49116 193 ;restore status ret ; 49117 201 ;return statusErrrm ... use DEF FN to pass arguments to USR routines, as exampled in the listing in #40 above. Check your PMs for a link to the MENU program which I sent you yesterday, which does just that.
10 CLEAR 46999: GO SUB 9120: GO SUB 9134: GO SUB 9220: DEF FN o(f$)=USR 49050: DEF FN p(t,e)=USR 47000
...
900 LET r$=l$(item): IF FN o(r$+CHR$ 255) THEN CLS : LOAD r$
The USR 49050 routine is the one given in #40 above, with comments explaining how it works. It references the DEFADD system variables area to get the file name passed in the FN o(f$) call.
If you want to pass the file name from the l$() array to the file check routine all in m/code then you'd have to pick up the program name from j$(), cross-reference the corresponding entry in l$(), then set up the address of that entry in HL so it could be used by the DOS-OPEN routine called from (a slightly modified version of) USR 49050. The file name string referenced by DOS-OPEN has to be terminated with FFh, so either every entry in l$() would need an FFh at the end, or (better) copy the selected one somewhere else and tag on an FFh before setting the address in HL.
Using m/code to search for variables in the VARS area and select an entry in a string array is also covered in the program which I sent the other day. Using string arrays defined outside of BASIC has been covered in another thread. My work here is done now.