News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu

MC START/BOOT PROGRAM WITHOUT reseting firmware jumpblock?

Started by ikonsgr, 13:47, 30 January 19

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Docent

Quote from: ikonsgr on 21:29, 10 February 19
Thanks again docent,i really appreciate your help on this!
Now a couple of thinks i also found out:

- KL ROM WALK (and probably KL ROM INIT and JUMP RESTORE too) destroy, not only the jumpblock patches, but also the actual code itself, so it's better to "disable" it by just returning back the call, after all it makes no difference in a stock Amstrad without any extra Roms.
It probably destroys the code if it is in the disk buffer, probably amsdos rom zeroes this buffer during rom initialization. Anyway I commented out KL ROM WALK & KL ROM INIT due to space constrains and changed JMP RESTORE to be just a ret
Quote
- The method you are using for setting the addresses to the RST 1, instructions, doesn't seem to work right. I've managed to actually make it work only when i "hard coded" the address like in my code, for example RST 1,&85ED, after all these addresses are always "fixed" and unchanged so there is no need to use aliases for them.
You cant have hardcoded values here - it wont work on other cpc models. Each rom (there are 4 versions - for cpc464, 664, 128 & 128+) has different offsets!
I think I found the problem why it didn't work for you - after rst 1 there should be 1 word. WinApe assembles rst 1, 0 as rst 1; dw 0 - I don't know what assembler you use but there are chances that it was assembled as rst1;db 0.
I modified this to be explicit rst 1; dw 0 - see attached source.
Quote
- In MC BOOT PROGRAM patched routine ,i had to add an LD C,&FF (=disable roms and address to RAM) instruction ,just before calling the MC START PROGRAM, otherwise i got reset in some games.
You're right, it's missing there- fixed
Quote
- The first 69 bytes of header must be placed at &A755, otherwise programs doesn't seem to be loaded correctly.But since header is 128 bytes long, (and some games might use some of the extra unused header bytes to load data) ,i modify the header receive code in order to place the hole 128 bytes on &A7E4 and the 69 bytes at &A755.
This is actually interesting - as all loading has been patched, there shouldn't be any reason to keep the header in this system area. Anyway, I thought it would be better to keep just one routine for loading, so instead I just copied there these 69 bytes from the header buffer when it got loaded. See attached source
Quote
Now, about placing the code at the base of the stack &BE80, i'm afraid it will have much less chances to remain untouched than with the 512bytes of sector buffer. I've already tried to place my code @ &BE80 before, and i end up with crashes very often! It seems that stack region is much more used by games and programs than the sector buffer.
And since the code we are having is rather large, in the range of 200-300 bytes, i doubt it would survive many games... As for you concerns about the need to modify the CAS OUT OPEN routine in order to avoid erasing of code placed at sector buffer, i believe that the vast majority of amstrad games (~99% or more) are actually "Read only", they don't offer  any saving options , so most probable it will not matter after all....  :)
You're probably right - putting code in either location is risky because it can get overwritten.
Just for testing I added an option to put the code for patched routines and actual patching code into different areas and built two binaries (see attachment). One (DIRECTBE.BIN) puts the patch code at #abb0, functions at #be80 and buffer header at #a9b0, the other (DIRECTA9.BIN)  puts the patch code at #aab0, functions at #a9b0 and buffer header at #ab80. They both load at #a000 and need a call #a000 to initialize.  If you have time you can try both and see which one works better (if at all :)
You can modify the location easily in the source adjusting func_loc, patch_loc, header and load_loc.
btw: I use assembler included in  winape emulator, so any maxam compatible assembler should do.
func_size equ new_functions_end-new_functions
patch_size equ patch_end-patch_jumpblocks

load_loc equ #a000

func_loc equ #be80
patch_loc equ #abb0
header equ #a9b0
write"DIRECTBE.BIN"
;func_loc equ #a9b0
;patch_loc equ #a9b0+#100
;header equ #ab80
;write"DIRECTA9.BIN"

; nolist
org load_loc
start:
ld hl, end_main
ld de, func_loc
ld bc, func_size
ldir
ld hl, end_main+func_size
ld de, patch_loc
ld bc, patch_size
ldir
jp apply_patches
end_main:

org func_loc
new_functions:
; data trasfer
byte_check:
ld a,#FB
in a,(#D1)
dec a
jr z, byte_check
ret

load_data:
; hl - address
; de - count
di
load_data_loop
call byte_check
ld a ,#FB
in a,(#D0)
ld (hl), a
inc hl
dec de
ld a,d
or e
jr z, load_completed
jr load_data_loop
load_completed
ei
ret

send_data:
; hl - address
; d - count
di
ld a, 1
ld bc, #FBD1
out (c), a
ld a, d
; ld bc, #FBD0
dec bc
out (c), a

send_data_loop:
ld a,(hl)
out (c), a
inc hl
dec d
jr nz, send_data_loop
ei
ret

; patched routines
; cas in open
cas_in_open:
; check if stream has been already opened
ld a, (stream_opened)
or a
jr nz, cas_in_open_error
; send file name
ld d, b
call send_data
; receive file header
ld hl, header
ld de, 128
call load_data
; receive filesize
ld hl, filesize
ld de, 2
call load_data
; copy header to amsdos system area
ld hl, header
ld de, #a755
ld bc, 69
ldir
; check if filesize is 0 - if so, file doesnt exists
ld bc, (filesize)
ld a, b
or c
jr nz, file_exists
xor a
jr cas_error_return
file_exists:
; set return flags
xor a
inc a
scf
; mark stream as opened
ld (stream_opened), a
; setup return values
ld hl, header
ld de, (header+#15)
ld a,  (header+#12)
ret

; cas in close
cas_in_close:
; check if stream has been opened
ld a, (stream_opened)
or a
jr z, cas_error_return
; reset stream opened flag
xor a
ld (stream_opened), a
scf
ret

; cas in direct
cas_in_direct:
ld a, (stream_opened)
or a
jr z, cas_in_direct_error_return
ld de,(filesize)
call load_data
ld hl,(header+26)
; set return flags
xor a
inc a
scf
ret

cas_in_open_error:
xor a
cas_in_direct_error_return:
; setup zero flag
inc a
cas_error_return:
; error - stream was not opened
ld a, #e
ccf
ret

mc_boot_program:
ld (mc_boot_start_addr+1), hl
ld hl, mc_boot_continue
mc_boot_patch:
rst 1
dw 0
mc_boot_continue:
call apply_patches
mc_boot_start_addr:
ld hl, 0
push hl
call jumphl
pop hl
; disable all roms for mc_start_program
ld c, #ff
jr c, mc_start_program
rst 0
jumphl:
jp (hl)

mc_start_program:
ld (mc_start_start_addr+1), hl
ld hl, mc_start_continue
mc_start_patch:
rst 1
dw 0
mc_start_continue:
call apply_patches
mc_start_start_addr:
ld hl, 0
call jumphl
rst 0

;kl_init_back:
;kl_init_back_patch:
; rst 1
; dw 0
; jr apply_patches

;kl_rom_walk:
;kl_rom_walk_patch:
; rst 1
; dw 0
; jr apply_patches

;mc_jump_restore:
;mc_jump_restore_patch:
; rst 1
; dw 0

apply_patches:
xor a
ld (patches_applied), a
jp patch_jumpblocks

stream_opened:
db 0
filesize:
dw 0

new_functions_end:


org patch_loc

patch_jumpblocks:
push hl
xor a
ld hl, patches_applied
or (hl)
jr nz, patches_exit
inc (hl)

ld a, #c3 ; jump opcode
;patch cas in open

ld (#bc77), a
ld hl, cas_in_open
ld (#bc78), hl

; patch cas in direct

ld (#bc83), a
ld hl, cas_in_direct
ld (#bc84), hl

; patch cas in close

ld (#bc7a), a
ld hl, cas_in_close
ld (#bc7b), hl

; patch mc boot program

ld hl, (#bd14)
ld (mc_boot_patch+1), hl
ld (#bd13), a
ld hl, mc_boot_program
ld (#bd14), hl

; patch mc start program

ld hl, (#bd17)
ld (mc_start_patch+1), hl
ld (#bd16), a
ld hl, mc_start_program
ld (#bd17), hl

; patch jump restore
;
; ld hl, (#bd38)
; ld (mc_jump_restore_patch+1), hl
; ld (#bd37), a
; ld hl, mc_jump_restore
; ld (#bd38), hl

ld a, #c9 ; ret opcode
ld (#bd37), a

;; patch kl rom walk
;
; ld hl, (#bccc)
; ld (kl_rom_walk_patch+1), hl
; ld (#bccb), a
; ld hl, kl_rom_walk
; ld (#bccc), hl

;; patch kl init back
;
; ld hl, (#bccf)
; ld (kl_init_back_patch+1), hl
; ld (#bcce), a
; ld hl, kl_init_back
; ld (#bccf), hl
patches_exit:
pop hl
ret
patches_applied:
db 0
patch_end:

ikonsgr

Indeed i'm using winape assembler. Thanks again Docent,i will surely give it a try and let you know!

ikonsgr

Well Docent, after many hours of testing (and failures...) , i've decided to take a different approach in order to solve the problem of the overwrite code:
I made a rather small routine  (~45 bytes,i might even get it down to only 37bytes!) that reloads the "big" code from serial port  and place it again to it's place! This routine is called everytime a file is requested e.g. just before the patched CAS IN OPEN routine (actually the jumpblock for cas in open, calls this routine, where the last instruction of this small routine, is a jump to the actual cas in open).
I've managed to run two more games with this method, Super cars and saboteur 2, where the failure of load, seem to caused from code overwrite. Also, there is no need to patch CAS OUT OPEN anymore, since the overwrite of code that caused by openout"" is fixed by reloading the code, and so now, any game/program can use disk or tape for saving files along with the patched loading from serial port!  ;)
So now, the only problem is to put this small code to the "safest" place we can get! I've tried &be80, &be00, even somewhere inside the 255 bytes used for non tokenised  basic commands (around &AD00), but despite my long efforts, none of the other non working games seemed to load succesfully (and the 2 games that i manage to load ,worked for puting the small code in different ram places) . Many of these are using the same "crack intro" (usually with cheats), and most of them ,load initially but then, they just stop, without reset or crashes, just waiting indefinetely without sending any request for file to the pc. Maybe they use a custom loader that bypass firmware routines?  ::)
So i was wondering if you can suggest me some other places in ram that we can fit these few bytes safely.

Btw, here is the code i used, in case you are interested:
LIMIT &FFFF
reload equ #be80
header equ #a755
header_full equ #a7e4
ORG &A9B0
;org #be80
;;NOLIST
write"DIRECT.BIN"

start:
call patch_jumpblocks
ret

; data trasfer
byte_check:
ld a,#FB
in a,(#D1)
dec a
jr z, byte_check
ret

load_data:
; hl - address
; de - count
di
load_data_loop
.check_BYTE
        LD A,&FB
        iN A,(&D1) 
        DEC A
        JR Z,check_BYTE
ld a ,#FB
in a,(#D0)
ld (hl), a
inc hl
dec de
ld a,d
or e
jr z, load_completed
jr load_data_loop
load_completed
ei
ret

send_data:
; hl - address
; d - count
di
ld      A,1
ld bc, #FBD1
out (c), a
ld a, d
ld bc, #FBD0
out (c), a

send_data_loop:
ld a,(hl)
out (c), a
inc hl
dec d
jr nz, send_data_loop
ei
ret

; patched routines

;;cas in open- first reload all code from serial port-
cas_in_open:
push BC
push HL
push DE
        xor a
ld bc, #FBD0
out (c), a
ld HL,&A9B0
ld DE,307
di
load_code_data:
; hl - address
; de - count
       .check_code_BYTE
        LD A,&FB
        iN A,(&D1) 
        DEC A
        JR Z,check_code_BYTE
ld a ,#FB
in a,(#D0)
ld (hl), a
inc hl
dec de
ld a,d
or e
jr z, load_code_completed
jr load_code_data
ei
load_code_completed:
call patch_jumpblocks
pop DE
pop HL
pop BC
        jp cas_in_open_continue


; this is the original cas_in_open start

cas_in_open_continue:
; send file name
ld d, b
call send_data

; receive file header
ld hl, header_FULL
ld de, 128
call load_data

; copy header to amsdos system area
ld hl, header_full
ld de, header
ld bc, 69
ldir
; receive filesize
ld hl, filesize
ld de, 2
call load_data



; setup return values
        LD A,2
        DEC A
ld bc, (filesize)
ld hl, header
ld de, (header+#15)
ld a,  (header+#12)
scf
ret

cas_in_direct:
ld de,(filesize)
call load_data
ld hl,(header+26)
        LD A,2
        DEC A
scf
ret


mc_boot_program:
ld (mc_boot_start_addr+1), hl
ld hl, mc_boot_continue
mc_boot_patch:
rst 1,&85ED

mc_boot_continue:
call patch_jumpblocks
mc_boot_start_addr:
ld hl, 0
call jumphl

push hl
call patch_jumpblocks
pop hl
; disable all roms for mc_start_program
ld c, #ff
jr c,mc_start_program
ret


mc_start_program:
ld (mc_start_start_addr+1), hl
ld hl, mc_start_continue
mc_start_patch:
rst 1,&861C

mc_start_continue:
call patch_jumpblocks
mc_start_start_addr:
ld hl, 0
; jp (hl)
call jumphl
ret

;kl_init_back:
;kl_init_back_patch:
; rst 1,&8330
; jr continue

;kl_rom_walk:
;kl_rom_walk_patch:
; rst 1,&8326
; jr continue

;mc_jump_restore:
;mc_jump_restore_patch:
; rst 1,&BD88
;continue:
; push hl
; call patch_jumpblocks
; pop hl
; ret

cas_in_close:
        scf
ret

patch_jumpblocks:

ld a, #c3 ; jump opcode

;patch cas in open

ld (#bc77), a
  ld hl,reload
; ld hl,cas_in_open_continue
ld (#bc78), hl

; patch cas in direct

ld (#bc83), a
ld hl, cas_in_direct
ld (#bc84), hl

; patch cas in close

ld (#bc7a), a
ld hl, cas_in_close
ld (#bc7b), hl

; patch mc boot program

; ld hl, (#bd14)
; ld (mc_boot_patch+1), hl
ld (#bd13), a
ld hl, mc_boot_program
ld (#bd14), hl


; patch mc start program

; ld hl, (#bd17)
; ld (mc_start_patch+1), hl
ld (#bd16), a
ld hl, mc_start_program
ld (#bd17), hl


LD HL,&BCCB  ;;KL ROM WALK rst 1,&8326
LD A,&C9
LD (HL),A

LD HL,&BC65  ;;CAS INITIALIZE
LD A,&C9
LD (HL),A

LD HL,&BCCE  ;;KL ROM WALK
LD A,&C9
LD (HL),A

LD HL,&BD37  ;;JUMP RESTORE
LD A,&C9
LD (HL),A

; patch jump restore

; ld hl, (#bd38)
; ld (mc_jump_restore_patch+1), hl
; ld (#bd37), a
; ld hl, mc_jump_restore
; ld (#bd38), hl

; patch kl rom walk

; ld hl, (#bccc)
; ld (kl_rom_walk_patch+1), hl
; ld (#bccb), a
; ld hl, kl_rom_walk
; ld (#bccc), hl

; patch kl init back

; ld hl, (#bccf)
; ld (kl_init_back_patch+1), hl
; ld (#bcce), a
; ld hl, kl_init_back
; ld (#bccf), hl
ret

jumphl:
jp (hl)

place_realod_routine ;this small routine is called by the basic program to put the small routine in it's right place

ld bc,46
ld de,reload
ld hl,cas_in_open
ldir
ret
filesize:
dw 0


Btw,i found out why the indirect way of getting the original jumpblocks for MC BOOT and MC START program didn't work right, and only when i "hard code" RST1,&XXXX  seemed to work: if the jumpblocks are already patched and the "patch jumpblock" routine is called again, it takes the patched memory addresses and use them for the RST 1, instructions!  So, i'm afraid the only way to do it right, is by hardcoding the RST1 instructions and make 3 different code binaries, one for each model 464, 664,6128 and using some radio buttons or a small list in the windows application i made, you could select the proper code to be loaded!  ;) Of course i will need someone to give me the correct rom addresses for mc boot and mc start routines for 464 and 664...  ::)

Docent

Quote from: ikonsgr on 22:44, 17 February 19
Well Docent, after many hours of testing (and failures...) , i've decided to take a different approach in order to solve the problem of the overwrite code:
I made a rather small routine  (~45 bytes,i might even get it down to only 37bytes!) that reloads the "big" code from serial port  and place it again to it's place! This routine is called everytime a file is requested e.g. just before the patched CAS IN OPEN routine (actually the jumpblock for cas in open, calls this routine, where the last instruction of this small routine, is a jump to the actual cas in open).
I've managed to run two more games with this method, Super cars and saboteur 2, where the failure of load, seem to caused from code overwrite. Also, there is no need to patch CAS OUT OPEN anymore, since the overwrite of code that caused by openout"" is fixed by reloading the code, and so now, any game/program can use disk or tape for saving files along with the patched loading from serial port!  ;)
Seems to be a good idea if you couldn't resolve problems with overwriting
Quote
So now, the only problem is to put this small code to the "safest" place we can get! I've tried &be80, &be00, even somewhere inside the 255 bytes used for non tokenised  basic commands (around &AD00), but despite my long efforts, none of the other non working games seemed to load succesfully (and the 2 games that i manage to load ,worked for puting the small code in different ram places) . Many of these are using the same "crack intro" (usually with cheats), and most of them ,load initially but then, they just stop, without reset or crashes, just waiting indefinetely without sending any request for file to the pc. Maybe they use a custom loader that bypass firmware routines?  ::)
Quite possible, maybe there is some sort of jumpblock checksum tested by the loader?
Quote
So i was wondering if you can suggest me some other places in ram that we can fit these few bytes safely.

try &b0c7-&b0ff - 57 bytes that are not used on both 464 & 6128
Quote
Btw,i found out why the indirect way of getting the original jumpblocks for MC BOOT and MC START program didn't work right, and only when i "hard code" RST1,&XXXX  seemed to work: if the jumpblocks are already patched and the "patch jumpblock" routine is called again, it takes the patched memory addresses and use them for the RST 1, instructions!  So, i'm afraid the only way to do it right, is by hardcoding the RST1 instructions and make 3 different code binaries, one for each model 464, 664,6128 and using some radio buttons or a small list in the windows application i made, you could select the proper code to be loaded!  ;) Of course i will need someone to give me the correct rom addresses for mc boot and mc start routines for 464 and 664...  ::)

I got it fixed earlier - there is a test for patches_applied variable ath the beginning of patch routine that guards against such behavior. Have a look at the latest code from my previous post. There are a few other optimizations.

ikonsgr

Yeap, it seems that &B0C7 works quite well, i've tried a few dozens of games and all seemed to work fine  (including the 2 games that needed extra code reloading ).
Btw, in the firmware manual (page 17), is mentioned that 6128 has &5B unused bytes starting at &B0A5, while 464 has &39free bytes starting at &B0C7. The strange thing is that next memory loaction described is &B100 for 6128 (which is next location after &B0A5+&5B) BUT, it "jumps" to &B8E4 for 464! There is a rather big "gap" of 100's of "unknown" bytes (from &B100 to &B8E3) for CPC464, that firmware manual doesn't mention anything about...   ::)
Now, unfortunately the test for patches_applied in your code didn't seem to work with the addition of reloading the code, because memory locations, where you initially put the RST1 addresses, where cleared every time the code is reloaded (so if the jumpblocks are patched, there is no way to retrieve the original RST 1 addresses...). Fortunately i manage to overcome this problem by changing the relative memory addresses you used ((mc_boot_patch+1) and (mc_start_patch+1))  to fix addresses at the begining of the "protected" area &BC07! First i declared the 2 addresses at the beggining of the code:

rst1_boot equ #b0c7
rst1_start equ #b0c9
reload equ #b0cb
....

And then i add the loading of the RST1 addresses in the initial code (which executed only oncewith a CALL &9AB0 from the small basic program):

ORG &A9B0
write"DIRECT.BIN"
start:
   ld   hl, (#bd14)
   ld   (rst1_boot),hl
   ld   hl, (#bd17)
   ld    (rst1_start),hl   
   call   patch_jumpblocks
   call    place_relaod_routine
   ret


Finally i modified the begining of the MC BOOT PROGRAM and MC START PROGRAM in order to receive the RST1 addresses:

mc_boot_program:
   ld    (mc_boot_start_addr+1), hl
   ld   hl, mc_boot_continue
   ld   de,(rst1_boot)
   ld   (mc_boot_patch+1),de

mc_boot_patch:   
   rst    1
   dw   0
mc_boot_continue:
...



Using this "trick" everyting worked fine without needing to "Hardcode" the rst1 instructions, so i suppose it will work on any CPC model!
Trying to make any of the nonworking games to load,i even tried to reload the code,not only before CAS IN OPEN, but also after calling the loader routine in the MS BOOT PROGRAM (in case loader overwrites  my code and so the returing of the loader routine would crash), but none of the non working games seemed to work... So i suppose these might use AMSDOS routines like BIOS READ SECTOR for loading (i may paptch this too in the future...  :) ) or non firmware custom routines that communicate with 765 FDC directly...  ::)
Finally, i reduce the BASIC listing even more:

10 OUT &FBD1,0:OUT &FBD1,1:EI
20 FOR I=0 TO 341
30 POKE &A9B0+I,INP(&FBD0):NEXT i
40 CALL &A9B0


So now ,i think we cross the point of .."as good as it gets"!



ikonsgr

Btw Docent, do you think that the &50 bytes at &ABB0 (e.g. just above the &200 bytes of sector buffer) for both 464 and 6128, is might be even "safer" place than the &B0C7?
And something else i just thought: do you think that some games/programs might use the CAS IN CHAR routine instead of CAS IN DIRECT? Is it worth to try patching CAS IN CHAR too? Although, considering how slow is reading a file using CAS IN CHAR,i don't understand why they implement this routine in the first place....  ::)

Docent

Quote from: ikonsgr on 00:10, 23 February 19
Btw Docent, do you think that the &50 bytes at &ABB0 (e.g. just above the &200 bytes of sector buffer) for both 464 and 6128, is might be even "safer" place than the &B0C7?
On cpc 464 it can be overwritten because it is in free memory area - the cpc 464 system area starts at &ac00
Quote
And something else i just thought: do you think that some games/programs might use the CAS IN CHAR routine instead of CAS IN DIRECT? Is it worth to try patching CAS IN CHAR too? Although, considering how slow is reading a file using CAS IN CHAR,i don't understand why they implement this routine in the first place....  ::)
I'm sure they are - to load data from file byte by byte, for example save game state, settings etc.
This routine is used to read single bytes from file - all other routines handle loading in one go, without possibility to read single bytes from file.
CAS CHAR IN can be slow if you just redirect each read to serial, you should probably implement something like buffered read - on first read transfer a whole sector from file (eg. 512 bytes) and then just provide data on following reads from this buffer until reaching its end.

Docent

Quote from: ikonsgr on 17:07, 21 February 19
Yeap, it seems that &B0C7 works quite well, i've tried a few dozens of games and all seemed to work fine  (including the 2 games that needed extra code reloading ).
Btw, in the firmware manual (page 17), is mentioned that 6128 has &5B unused bytes starting at &B0A5, while 464 has &39free bytes starting at &B0C7. The strange thing is that next memory loaction described is &B100 for 6128 (which is next location after &B0A5+&5B) BUT, it "jumps" to &B8E4 for 464! There is a rather big "gap" of 100's of "unknown" bytes (from &B100 to &B8E3) for CPC464, that firmware manual doesn't mention anything about...   ::)
The manual has addresses sorted by their 6128 location, some addresses on cpc 464 have different positions so their addreses are also different. &b100 for cpc 464 is on page 27
Quote
Now, unfortunately the test for patches_applied in your code didn't seem to work with the addition of reloading the code, because memory locations, where you initially put the RST1 addresses, where cleared every time the code is reloaded (so if the jumpblocks are patched, there is no way to retrieve the original RST 1 addresses...).
I think the better solution would be to just store jump restore rst vector and after each reload call this vector and then call patching routine. this way you'll need to store only one original address and use patching routines without modification.
Quote
Trying to make any of the nonworking games to load,i even tried to reload the code,not only before CAS IN OPEN, but also after calling the loader routine in the MS BOOT PROGRAM (in case loader overwrites  my code and so the returing of the loader routine would crash), but none of the non working games seemed to work... So i suppose these might use AMSDOS routines like BIOS READ SECTOR for loading (i may paptch this too in the future...  :) ) or non firmware custom routines that communicate with 765 FDC directly...  ::)
Makethis working can be hard, I've seen a few loaders that were directly calling routines in amsdos rom without any jumpblocks
Quote
Finally, i reduce the BASIC listing even more:


10 OUT &FBD1,0:OUT &FBD1,1:EI
20 FOR I=0 TO 341
30 POKE &A9B0+I,INP(&FBD0):NEXT i
40 CALL &A9B0

Nice :) if you send the length of the file as first 2 bytes, then you can get rid of loop counter Send also start address and you'll get an automatic booter that you can control no matter the size or start address of loader.

10 OUT &FBD1,0:OUT &FBD1,1:EI
15 loop=INP(&FBD0)*256: loop=loop+INP(&FBD0)
16 start=INP(&FBD0)*256: start=start+INP(&FBD0)
20 FOR I=0 TO loop
30 POKE start+I,INP(&FBD0):NEXT i
40 CALL start

btw: do you need an EI in line 10? This will enable interrupts and can generate transmission errors. I think it should be DI here.

Quote
So now ,i think we cross the point of .."as good as it gets"!
Yes, you probably could improve it by putting into rom :)

ikonsgr

Quote from: Docent on 18:45, 23 February 19
I'm sure they are - to load data from file byte by byte, for example save game state, settings etc.
Well, maybe i will try to patch CAS IN CHAR routine too in the future, although after a quick check, none of the non working games seem to have headerless ASCII files for loading (that might used CAS IN CHAR).

Quote from: Docent on 19:21, 23 February 19
The manual has addresses sorted by their 6128 location, some addresses on cpc 464 have different positions so their addreses are also different. &b100 for cpc 464 is on page 27
I see...,thanks for clarification!
Quote from: Docent on 19:21, 23 February 19
I think the better solution would be to just store jump restore rst vector and after each reload call this vector and then call patching routine. this way you'll need to store only one original address and use patching routines without modification.
Well,if i'm not mistaken this is exactly what i did in the end!  :)

Quote from: Docent on 19:21, 23 February 19
Make this working can be hard, I've seen a few loaders that were directly calling routines in amsdos rom without any jumpblocksNice :)
Well that should explain why some of the non working games are not loading... If a loader uses direct calls  (all jumpblocks regarding file access routines like CAS IN/OUT OPEN, CAS IN/OUT DIRECT etc, are having the same 3 bytes: RST3,&A88B, where &A88B is actually a: &CD30, &07 =call to &CD30 @ ROM 7)  it bypass the patched jumpblocks.
This can also explains why i get "file not found" messages in a few non working games that don't crash or reset, which i couldn't explain so far. In any case, this was a very bad programming practice and i don't understand why they did it back then....


Quote from: Docent on 19:21, 23 February 19
if you send the length of the file as first 2 bytes, then you can get rid of loop counter Send also start address and you'll get an automatic booter that you can control no matter the size or start address of loader.
10 OUT &FBD1,0:OUT &FBD1,1:EI
15 loop=INP(&FBD0)*256: loop=loop+INP(&FBD0)
16 start=INP(&FBD0)*256: start=start+INP(&FBD0)
20 FOR I=0 TO loop
30 POKE start+I,INP(&FBD0):NEXT i
40 CALL start
That's really very nice idea, but you know, my first priority here, is to make the Basic program as small as it can be, as this must be typed (or load) and then executed, every time you reset amstrad.

Quote from: Docent on 19:21, 23 February 19btw: do you need an EI in line 10? This will enable interrupts and can generate transmission errors. I think it should be DI here.
Well, the only reason i put it, is to delay Amstrad a bit,in order for the routine to be downloaded from PC to pic's buffer. That way, we save typing the "While wend" loop for checking if a byte is available, thus making the listing smaller!  ;)  In any case, maybe you are right and change it to DI instead ,although i think  amstrad starts with the interrupts enabled anyway.


Docent

Quote from: ikonsgr on 15:04, 24 February 19
Well, maybe i will try to patch CAS IN CHAR routine too in the future, although after a quick check, none of the non working games seem to have headerless ASCII files for loading (that might used CAS IN CHAR).
True but you'll probably find some basic games that may use such approach.  I'm pretty sure that many utilities use such files to load/store data, so it might be worth to patch it also. btw How about allowing to save data the same way?
Quote
I see...,thanks for clarification!Well,if i'm not mistaken this is exactly what i did in the end!  :)
Well that should explain why some of the non working games are not loading... If a loader uses direct calls  (all jumpblocks regarding file access routines like CAS IN/OUT OPEN, CAS IN/OUT DIRECT etc, are having the same 3 bytes: RST3,&A88B, where &A88B is actually a: &CD30, &07 =call to &CD30 @ ROM 7)  it bypass the patched jumpblocks.
Its even worse - they enable upper rom and jump directly to some address :)
Quote
This can also explains why i get "file not found" messages in a few non working games that don't crash or reset, which i couldn't explain so far. In any case, this was a very bad programming practice and i don't understand why they did it back then....
I bet that they were teenagers back then :)
Quote
That's really very nice idea, but you know, my first priority here, is to make the Basic program as small as it can be, as this must be typed (or load) and then executed, every time you reset amstrad.
Well, the only reason i put it, is to delay Amstrad a bit,in order for the routine to be downloaded from PC to pic's buffer. That way, we save typing the "While wend" loop for checking if a byte is available, thus making the listing smaller!  ;)  In any case, maybe you are right and change it to DI instead ,although i think  amstrad starts with the interrupts enabled anyway.
You said that EI in this basic routine delays main loop? That's interesting to know.
I thought that disabling interrupts would be actually better because system has its own interrupt handler, so leaving it enabled during transfer may affect timings and delay main loop or cause errors. That's why I put di in my assembly routine posted earlier :)

Btw: do you still have some hardware available? I think that it would be very useful, especially that I have cpc464 lying around to test on...

ikonsgr

Quote from: Docent on 20:04, 24 February 19
btw, how about allowing to save data the same way?
I thought of it too, but i'm afraid after adding patches for CAS OUT OPEN, CAS OUT CHAR, CAS OUT DIRECT, CAS OUT CLOSE too, the resulting code would probably be so big, that it wouldn't fit in 512bytes of sector buffer!But i suppose, as all save routines work as they  are, you can save any files needed on disk and transfer them to PC using cpc2pc utlility in order to be loaded again using the patched loading routines! :) Besides, i'm planning of patching AMSDOS READ SECTOR routine in the future, in order to enable direct reading of sectors directly from the dsk image, as this reading method was used by many games,especially in later years. So, all the games that had only a "disc" loader file and obviously used this loading method they might work too!

Quote from: Docent on 20:04, 24 February 19
You said that EI in this basic routine delays main loop? That's interesting to know.
I thought that disabling interrupts would be actually better because system has its own interrupt handler, so leaving it enabled during transfer may affect timings and delay main loop or cause errors. That's why I put di in my assembly routine posted earlier :)
Well, what i actually mean is that the EI instruction delays the starting of the reading loop that follows immediately after. You see, when the OUT &FBD0,0 instruction is issued, PC responds by sending the hole code at once.I found out that if the reading loop starts  immediately after (without checking if byte is available), it didn't work well, so i decided to put an instruction (btw it could be any instruction, a print, a tag etc),just to cause a tiny delay in order for the fist bytes of the routine would be available when reading loop begins! And i choose EI just because it's only 2 characters to save typing!  :D
Quote from: Docent on 20:04, 24 February 19
Btw: do you still have some hardware available? I think that it would be very useful, especially that I have cpc464 lying around to test on...
Of course, send me a pm to arrange it. It would be great if you got one and maybe develop your own apps for the serial interface !  ;)


Docent

Quote from: ikonsgr on 23:48, 24 February 19
I thought of it too, but i'm afraid after adding patches for CAS OUT OPEN, CAS OUT CHAR, CAS OUT DIRECT, CAS OUT CLOSE too, the resulting code would probably be so big, that it wouldn't fit in 512bytes of sector buffer!But i suppose, as all save routines work as they  are, you can save any
there is a sending routine already in the code, so maybe it will not take too much space,
Quote
Besides, i'm planning of patching AMSDOS READ SECTOR routine in the future, in order to enable direct reading of sectors directly from the dsk image, as this reading method was used by many games,especially in later years. So, all the games that had only a "disc" loader file and obviously used this loading method they might work too!
Probably the best would be just forward routine params via serial to pc and let it arrange the transfer of proper sector
Quote
Well, what i actually mean is that the EI instruction delays the starting of the reading loop that follows immediately after. You see, when the OUT &FBD0,0 instruction is issued, PC responds by sending the hole code at once.I found out that if the reading loop starts  immediately after (without checking if byte is available), it didn't work well, so i decided to put an instruction (btw it could be any instruction, a print, a tag etc),just to cause a tiny delay in order for the fist bytes of the routine would be available when reading loop begins! And i choose EI just because it's only 2 characters to save typing!  :D
Ok, that was a reason for EI :)
Quote
Of course, send me a pm to arrange it. It would be great if you got one and maybe develop your own apps for the serial interface !  ;)

Pm sent :)

Powered by SMFPacks Menu Editor Mod