Hi everyone,
I'm in a process of developing a code for direct load and execution of programs using the Serial interface (http://www.cpcwiki.eu/forum/amstrad-cpc-hardware/universal-serial-interface-for-amstrad-cpc/)i've also developed. I'm using the classic method of patching the firmware routines for CAS IN OPEN, CAS IN DIRECT, CAS IN CLOSE, and redirect them to execute my code. Everything works perfect for loading/running BASIC programs or single binary programs.
BUT,unfortunately whenever a binary loader uses MC START PROGRAM or MC BOOT PROGRAM routines, program either crashes or tries to load next file from disk (giving a "File not found" message), obviously because these routines reset the firmware jumpblocks.
So,i was wondering,is there a way to somehow "emulate" these two routines inside my code,in order to avoid the firmware jumpblock reset? Or perhaps, there is another way to do it?...
Quote from: ikonsgr on 13:47, 30 January 19
Hi everyone,
I'm in a process of developing a code for direct load and execution of programs using the Serial interface (http://www.cpcwiki.eu/forum/amstrad-cpc-hardware/universal-serial-interface-for-amstrad-cpc/)i've also developed. I'm using the classic method of patching the firmware routines for CAS IN OPEN, CAS IN DIRECT, CAS IN CLOSE, and redirect them to execute my code. Everything works perfect for loading/running BASIC programs or single binary programs.
BUT,unfortunately whenever a binary loader uses MC START PROGRAM or MC BOOT PROGRAM routines, program either crashes or tries to load next file from disk (giving a "File not found" message), obviously because these routines reset the firmware jumpblocks.
So,i was wondering,is there a way to somehow "emulate" these two routines inside my code,in order to avoid the firmware jumpblock reset? Or perhaps, there is another way to do it?...
Both routines do jumpblock reinitialization, so your changes are no longer there.
You have two options:
1. use rom disassembly and copy both routines from rom into your program so you can have full control over it, or
2. patch both calls to point to your own routine, first storing original pointers in your code. In this routine:
a. remember provided start address
b. set your own start address to your boot routine
c. call saved original routines so the system will perform initializations and will jump into your code
d. restore your jumpblock patches
e. call original start address from step a
I've checked these 2 routines, they both execute LOW jump calls 'RST 1' to OS rom. I tried to "isolate" the MC BOOT PROGRAM routine from the OS rom, and i used this online disassmbler:
https://onlinedisassembler.com/odaweb/
This is the assembly output code:
.data:00000000 31 00 c0 ld sp,0xc000
.data:00000003 e5 push hl
.data:00000004 cd e9 1f call 0x1fe9
.data:00000007 f3 di
.data:00000008 01 ff f8 ld bc,0xf8ff
.data:0000000b ed 49 out (c),c
.data:0000000d cd 5c 00 call 0x005c
.data:00000010 e1 pop hl
.data:00000011 d5 push de
.data:00000012 c5 push bc
.data:00000013 e5 push hl
.data:00000014 cd 98 1b call 0x1b98
.data:00000017 cd 84 10 call 0x1084
.data:0000001a cd d0 0a call 0x0ad0
.data:0000001d cd 5f ba call 0xba5f
.data:00000020 e1 pop hl
.data:00000021 cd 1e 00 call 0x001e
.data:00000024 c1 pop bc
.data:00000025 d1 pop de
.data:00000026 38 07 jr c,0x002f
.data:00000028 eb ex de,hl
.data:00000029 48 ld c,b
.data:0000002a 11 f9 06 ld de,0x06f9
.data:0000002d 18 03 jr 0x0032
.data:0000002f 11 37 07 ld de,0x0737
.data:00000032 f3 di
.data:00000033 ed 56 im 1
.data:00000035 d9 exx
.data:00000036 01 00 df ld bc,0xdf00
.data:00000039 ed 49 out (c),c
.data:0000003b 01 ff f8 ld bc,0xf8ff
.data:0000003e ed 49 out (c),c
.data:00000040 01 c0 7f ld bc,0x7fc0
.data:00000043 ed 49 out (c),c
.data:00000045 01 7e fa ld bc,0xfa7e
.data:00000048 af xor a
.data:00000049 ed 79 out (c),a
.data:0000004b 21 00 b1 ld hl,0xb100
.data:0000004e 11 01 b1 ld de,0xb101
.data:00000051 01 f9 07 ld bc,0x07f9
.data:00000054 77 ld (hl),a
.data:00000055 ed b0 ldir
.data:00000057 01 89 7f ld bc,0x7f89
.data:0000005a ed 49 out (c),c
.data:0000005c d9 exx
.data:0000005d af xor a
.data:0000005e 08 ex af,af'
.data:0000005f 31 00 c0 ld sp,0xc000
.data:00000062 e5 push hl
.data:00000063 c5 push bc
.data:00000064 d5 push de
.data:00000065 cd 44 00 call 0x0044
.data:00000068 cd bd 08 call 0x08bd
.data:0000006b cd 5c 1b call 0x1b5c
.data:0000006e cd e9 1f call 0x1fe9
.data:00000071 cd bf 0a call 0x0abf
.data:00000074 cd 74 10 call 0x1074
.data:00000077 cd a8 15 call 0x15a8
.data:0000007a cd bc 24 call 0x24bc
.data:0000007d cd e0 07 call 0x07e0
.data:00000080 fb ei
.data:00000081 e1 pop hl
.data:00000082 cd 1e 00 call 0x001e
.data:00000085 c1 pop bc
.data:00000086 e1 pop hl
.data:00000087 c3 77 00 jp 0x0077
.data:0000008a 21 02 02 ld hl,0x0202
.data:0000008d cd 70 11 call 0x1170
.data:00000090 cd 23 07 call 0x0723
.data:00000093 cd fc 06 call 0x06fc
.data:00000096 21 88 06 ld hl,0x0688
.data:00000099 18 74 jr 0x010f
.data:0000009b 20 31 jr nz,0x00ce
.data:0000009d 32 38 4b ld (0x4b38),a
.data:000000a0 20 4d jr nz,0x00ef
.data:000000a2 69 ld l,c
.data:000000a3 63 ld h,e
.data:000000a4 72 ld (hl),d
.data:000000a5 6f ld l,a
.data:000000a6 63 ld h,e
.data:000000a7 6f ld l,a
.data:000000a8 6d ld l,l
.data:000000a9 70 ld (hl),b
.data:000000aa 75 ld (hl),l
.data:000000ab 74 ld (hl),h
.data:000000ac 65 ld h,l
.data:000000ad 72 ld (hl),d
.data:000000ae 20 20 jr nz,0x00d0
.data:000000b0 28 76 jr z,0x0128
.data:000000b2 33 inc sp
.data:000000b3 29 add hl,hl
.data:000000b4 1f rra
.data:000000b5 02 ld (bc),a
.data:000000b6 04 inc b
.data:000000b7 43 ld b,e
.data:000000b8 6f ld l,a
.data:000000b9 70 ld (hl),b
.data:000000ba 79 ld a,c
.data:000000bb 72 ld (hl),d
.data:000000bc 69 ld l,c
.data:000000bd 67 ld h,a
.data:000000be 68 ld l,b
.data:000000bf 74 ld (hl),h
.data:000000c0 1f rra
.data:000000c1 02 ld (bc),a
.data:000000c2 04 inc b
.data:000000c3 a4 and h
.data:000000c4 31 39 38 ld sp,0x3839
.data:000000c7 35 dec (hl)
.data:000000c8 20 41 jr nz,0x010b
.data:000000ca 6d ld l,l
.data:000000cb 73 ld (hl),e
.data:000000cc 74 ld (hl),h
.data:000000cd 72 ld (hl),d
.data:000000ce 61 ld h,c
.data:000000cf 64 ld h,h
.data:000000d0 20 43 jr nz,0x0115
.data:000000d2 6f ld l,a
.data:000000d3 6e ld l,(hl)
.data:000000d4 73 ld (hl),e
.data:000000d5 75 ld (hl),l
.data:000000d6 6d ld l,l
.data:000000d7 65 ld h,l
.data:000000d8 72 ld (hl),d
.data:000000d9 20 45 jr nz,0x0120
.data:000000db 6c ld l,h
.data:000000dc 65 ld h,l
.data:000000dd 63 ld h,e
.data:000000de 74 ld (hl),h
.data:000000df 72 ld (hl),d
.data:000000e0 6f ld l,a
.data:000000e1 6e ld l,(hl)
.data:000000e2 69 ld l,c
.data:000000e3 63 ld h,e
.data:000000e4 73 ld (hl),e
.data:000000e5 20 70 jr nz,0x0157
.data:000000e7 6c ld l,h
.data:000000e8 63 ld h,e
.data:000000e9 1f rra
.data:000000ea 0c inc c
.data:000000eb 05 dec b
.data:000000ec 61 ld h,c
.data:000000ed 6e ld l,(hl)
.data:000000ee 64 ld h,h
.data:000000ef 20 4c jr nz,0x013d
.data:000000f1 6f ld l,a
.data:000000f2 63 ld h,e
.data:000000f3 6f ld l,a
.data:000000f4 6d ld l,l
.data:000000f5 6f ld l,a
.data:000000f6 74 ld (hl),h
.data:000000f7 69 ld l,c
.data:000000f8 76 halt
.data:000000f9 65 ld h,l
.data:000000fa 20 53 jr nz,0x014f
.data:000000fc 6f ld l,a
.data:000000fd 66 ld h,(hl)
.data:000000fe 74 ld (hl),h
.data:000000ff 77 ld (hl),a
.data:00000100 61 ld h,c
.data:00000101 72 ld (hl),d
.data:00000102 65 ld h,l
.data:00000103 20 4c jr nz,0x0151
.data:00000105 74 ld (hl),h
.data:00000106 64 ld h,h
.data:00000107 2e 1f ld l,0x1f
.data:00000109 01 07 00 ld bc,0x0007
.data:0000010c 21 05 07 ld hl,0x0705
.data:0000010f 7e ld a,(hl)
.data:00000110 23 inc hl
.data:00000111 b7 or a
.data:00000112 c8 ret z
.data:00000113 cd fe 13 call 0x13fe
.data:00000116 18 f7 jr 0x010f
.data:00000118 2a 2a 2a ld hl,(0x2a2a)
.data:0000011b 20 50 jr nz,0x016d
.data:0000011d 52 ld d,d
.data:0000011e 4f ld c,a
.data:0000011f 47 ld b,a
.data:00000120 52 ld d,d
.data:00000121 41 ld b,c
.data:00000122 4d ld c,l
.data:00000123 20 4c jr nz,0x0171
.data:00000125 4f ld c,a
.data:00000126 41 ld b,c
.data:00000127 44 ld b,h
.data:00000128 20 46 jr nz,0x0170
.data:0000012a 41 ld b,c
.data:0000012b 49 ld c,c
.data:0000012c 4c ld c,h
.data:0000012d 45 ld b,l
.data:0000012e 44 ld b,h
.data:0000012f 20 2a jr nz,0x015b
.data:00000131 2a 2a 0d ld hl,(0x0d2a)
.data:00000134 0a ld a,(bc)
.data:00000135 00 nop
.data:00000136 06 f5 ld b,0xf5
.data:00000138 ed 78 in a,(c)
.data:0000013a 2f cpl
.data:0000013b e6 0e and 0x0e
.data:0000013d 0f rrca
.data:0000013e 21 38 07 ld hl,0x0738
.data:00000141 3c inc a
.data:00000142 47 ld b,a
.data:00000143 7e ld a,(hl)
.data:00000144 23 inc hl
.data:00000145 b7 or a
.data:00000146 20 fb jr nz,0x0143
.data:00000148 10 f9 djnz 0x0143
.data:0000014a c9 ret
The problem is that, it includes more than a dozen of Calls all over the rom, so in order to get the MC boot program routine i would also include these, but i'm afraid it would be like adding the intire OS rom... ::)
ALso,i've tried to "emulate" the routine:
PUSH HL ;;HL holds the address of the loading routine
DI
CALL &BD06 ;;KL EVENT DISABLE
CALL &BCC8 ;;KL CHOKE OFF
CALL &BC02 ;;SCREEN RESET
CALL &BB03 ;;KM RESET
CALL &BCA7 ;;SOUND RESET
CALL &BB51 ;;TXT RESET
LD SP,&BFFE ;;reset stack pointer...I hope :-)
POP HL
JP (HL)
According to firmware guide, MC BOOT PROGRAM needs the address of the loading routine at HL, so after i reserve the addr into stack,i call all the init routines and then jump to loading address again. Unfortunately this doesn't seem to work right, it seems like i'm missing some things that have to be done, or i i'm not doing something right....
I've also tried to execute the RST 1 calls to routines, inside my code, and then repatch the routines i need:
RST 1,&85ED
CALL JUMPBLOCKS
RET
But unfortunately this ,didn't work too. It seems that the patched routines are reinitialized inside BOOT PROGRAM, before any loading is actually done, so when loader routine kicks in ,it tries to load the file using the firmware loading routines= crash...
Perhaps if anyone knows the inners of these two routines in more detail could give us some hint
Eventually this might help in conjunction with the Firmware:
LD HL, address; to start the program
JP &BD16 ; instead of RST Call > this is the entre for the MC Start Programm over RST#8
?
Well, the problem with MC BOOT PROGRAM is that, when it's called, HL registers does NOT have the execution address of the file to be executed, but rather the address of the assembly loader routine, that will load the actual file needed...
And unfortunately replacing MC BOOT PROGRAM with a simple jump to the loader routine address, doesn't seem to work right (although, in theory it should work, at least if anything else was initialized properly...)
Quote from: ikonsgr on 22:50, 30 January 19
Well, the problem with MC BOOT PROGRAM is that, when it's called, HL registers does NOT have the execution address of the file to be executed, but rather the address of the assembly loader routine, that will load the actual file needed...
And unfortunately replacing MC BOOT PROGRAM with a simple jump to the loader routine address, doesn't seem to work right (although, in theory it should work, at least if anything else was initialized properly...)
Use the second approach from my message above: patch both vectors, remembering their addresses first, store hl, call original vectors with hl pointing to your routine. In your routine patch vectors again and jump into address of previously stored hl
Very good trick, indeed, i use this code for the MCBOOTPROGRAM:
LD (&A924),HL (temporary saving of loader address)
LD HL,&AA56 (this is the addr just after the RST 1 instruction)
RST 1,&85ED (this is the initial instruction for MCBOOTPROGRAM)
CALL JUMPBLOCKS (this is &AA56)
LD HL,(&A924) (reload the correct loading address)
JP (HL)
But unfortunately still doesn't seem to work right. After some study of the situation, i conclude that the loader routine itself (the one called by MCBOOTPROGRAM), probably mess up with the address where my code is installed ,&A9B0-ABAF , these are 512 bytes normally used as loading buffer, usually contain the last sector loaded. Is this area often used by game loaders too?
Is there any other place you can suggest to put my code? Perhaps the 128 bytes @ &A7E4 (normally used for buffering records sent/load from disc), although i would need a total of at least 250 bytes for all the modified loading routines and the jumpblocks.
Quote from: ikonsgr on 15:25, 06 February 19
Very good trick, indeed, i use this code for the MCBOOTPROGRAM:
LD (&A924),HL (temporary saving of loader address)
LD HL,&AA56 (this is the addr just after the RST 1 instruction)
RST 1,&85ED (this is the initial instruction for MCBOOTPROGRAM)
CALL JUMPBLOCKS (this is &AA56)
LD HL,(&A924) (reload the correct loading address)
JP (HL)
But unfortunately still doesn't seem to work right. After some study of the situation, i conclude that the loader routine itself (the one called by MCBOOTPROGRAM), probably mess up with the address where my code is installed ,&A9B0-ABAF , these are 512 bytes normally used as loading buffer, usually contain the last sector loaded. Is this area often used by game loaders too?
Is there any other place you can suggest to put my code? Perhaps the 128 bytes @ &A7E4 (normally used for buffering records sent/load from disc), although i would need a total of at least 250 bytes for all the modified loading routines and the jumpblocks.
Use of the load buffer to store themselves is not the best idea for game loaders :)
They usually use temporary areas like screen or stack.
As for free areas (not counting smaller or various disk or basic buffers), you have:
- 80 bytes at &ABB0 (up to &abff)
- 58 bytes at &b0c7 (up to &b0ff)
- 64 bytes at &be00 (up to &be3f)
- 768 bytes at &be80 (up to &bfff), but the stack is setup to grow down from &bfff, so you can probably use max 100-200 bytes from this area or you risk overwriting your data.
I'd try to go with &be80 and see if it works.
I've tried to move to &be80 but it didn't work either.
After initial loading my code (using a small Basic program that initally loads my code to RAM), i've found out that even a soft reset doesn't erase it, and so, giving a single CALL instruction that replaces the jumpblocks (these are reseted to their original state after reset) is all you need to do to be able to load again directly from serial port!
The problem is that for some mysterious reason, whenever i try to run a binary loader file, it corrupts my code, in whatever place i tried to put it!
The good news is that i finally manage to load correctly some games using a somewhat "indirect" way. Instead of giving directly run"gamename", i give:
Memory loadaddr-1
load"gamename"
call execaddr
I've already tried it with Eagle's nest, 1942, terramex and some others, and all loaded succesfully using this method! Also,i manage to run Cauldron 2 directly (e.g. using run"cauldron.bas") which uses a small BASIC program that loads 3-4 separate files in different memory areas.
So it seems that if i use BASIC commands or the loader uses a BASIC program, everything works ok, but if a binary loader is used, it f@cks up my code! :)
So, the question is, why this thing happens? Is there any other Firmware routines that a loader might call and potentailly destroy my code placed in some reserved area like &A9B0? Or is there some other code inside binary loaders that could also destroy my code? ::)
Note also that i've also tried to "disable" KL ROM WALK,KL INIT BACK and MC JUMP RESTORE, by giving a simple RET at their jumpblock addresses (in case some of these might damage cod or the modified jumpblocks too), but with no luck....
FINALLY, for the 1st time i've managed to run some games with binary loaders! :D
I used this code for MC BOOTPROGRAM and MC STARTPROGRAM:
;;MCBOOT PROGRAM
LD A,&E9
LD (&ABB2),A
LD (&ABB3),HL
LD HL,&AA4C
RST 1,&85ED
CALL JUMPBLOCKS
LD HL,(&ABB3)
CALL &ABB2
JP (HL)
;;MC START PROGRAM
LD (&ABB0),HL
LD HL,&AA62
RST 1,&861C
CALL JUMPBLOCKS
LD HL,(&ABB0)
JP (HL)
The "trick" i used was,instead of direct jumping to (HL) address of the loading routine, i CALL the routine, using a jump (hl)=&e9 @ &ABB2 e.g. CALL &ABB2. The loading routine is supposed to return the execution address of the loaded program at (HL), so a simple JP (HL) after the CALL should cause the execution of the program.
I've tried this, with 15-20 games so far, and the results are:
- All binary files seemed to load succesfully from PC (previously, after initial run"file , no extra binary file was loaded from PC, my code seemed corrupted and i usually end up with a reset...)
- ~ half of the games are loaded and run succesfully, but unfortunately many games ,although the binary files was loaded correctly, they still seem to crash on execution.
I was wondering is there a chance that many games use custom loading routines instead of the firmware CAS IN OPEN,CAS IN DIRECT, CAS IN CLOSE?
And if yes, how exactly they do that? Is there a way to "talk" with 765 Floppy controller bypassing the firmware routines?
Quote from: ikonsgr on 15:27, 07 February 19
FINALLY, for the 1st time i've managed to run some games with binary loaders! :D
I used this code for MC BOOTPROGRAM and MC STARTPROGRAM:
;;MCBOOT PROGRAM
LD A,&E9
LD (&ABB2),A
LD (&ABB3),HL
LD HL,&AA4C
RST 1,&85ED
CALL JUMPBLOCKS
LD HL,(&ABB3)
CALL &ABB2
JP (HL)
;;MC START PROGRAM
LD (&ABB0),HL
LD HL,&AA62
RST 1,&861C
CALL JUMPBLOCKS
LD HL,(&ABB0)
JP (HL)
The "trick" i used was,instead of direct jumping to (HL) address of the loading routine, i CALL the routine, using a jump (hl)=&e9 @ &ABB2 e.g. CALL &ABB2. The loading routine is supposed to return the execution address of the loaded program at (HL), so a simple JP (HL) after the CALL should cause the execution of the program.
It is not a trick - You have just correctly emulated MC BOOT PROGRAM :)
I assumed that you knew the difference between them so didn't go into details how they operate...
Quote
I've tried this, with 15-20 games so far, and the results are:
- All binary files seemed to load succesfully from PC (previously, after initial run"file , no extra binary file was loaded from PC, my code seemed corrupted and i usually end up with a reset...)
- ~ half of the games are loaded and run succesfully, but unfortunately many games ,although the binary files was loaded correctly, they still seem to crash on execution.
I was wondering is there a chance that many games use custom loading routines instead of the firmware CAS IN OPEN,CAS IN DIRECT, CAS IN CLOSE?
And if yes, how exactly they do that? Is there a way to "talk" with 765 Floppy controller bypassing the firmware routines?
There are couple of methods, one is for example directly calling routines from amsdos rom.
Custom loading routines were used mainly by original games - most tape games used that approach. Some disk games used custom load or start through |cpm command, but then you will not see any files on disk.
You can probably assume that if a game has visible files on the disk it should load via standard firmware calls. Of course there are always exceptions - some multiload games (like those transferred from tape) can have their ingame loaders done via direct calling amsdos rom routines or using floppy controller.
So if you have files on a disk and the game loads and starts correctly without your patch, it should work with the patch. If it doesn't work, there are chances that your code is overwritten by a loader that copies itself into the same space or you have found one that do not use system to load data.
Yes, some games might indeed overwrite my code, and i'm afraid these can't be run directly with this method....
BUT, i noticed that several games, although they doesn't seem to run directly by giving: run"game loader name", they run if i "manually" give the basic commands:
memory loadaddress-1
load"game name loader"
call execution address
So, does anyone knows what exactly the basic command :run" ,does, in case of a binary file? Obviously first, loads the file using the CAS firmware routines, but then what? it calls the execution address? it calls the MCSTARTPROGRAM @&BD16? it calls the MCBOOTPROGRAM @&BD13? Or maybe something else?.. ::)
Quote from: ikonsgr on 20:05, 07 February 19
Yes, some games might indeed overwrite my code, and i'm afraid these can't be run directly with this method....
You can try to minimize the code size and put it into other free space, but I don't think you'll get 100% success ratio. You'll get better results if you put your code into rom.
Quote
So, does anyone knows what exactly the basic command :run" ,does, in case of a binary file? Obviously first, loads the file using the CAS firmware routines, but then what? it calls the execution address? it calls the MCSTARTPROGRAM @&BD16? it calls the MCBOOTPROGRAM @&BD13? Or maybe something else?.. ::)
run goes through MC BOOT PROGRAM with pointer to loading routine in rom that uses &bc83 (CAS IN DIRECT). After successful load it jumps to start address from loaded file.
Quote from: Docent on 21:00, 07 February 19
You can try to minimize the code size and put it into other free space, but I don't think you'll get 100% success ratio.
I've already tried alternative places, even by splitting code in two pieces, but unfortunately this didn't work either...
Quote from: Docent on 21:00, 07 February 19
You'll get better results if you put your code into rom.
Unfortunately
this is not an option (http://www.cpcwiki.eu/forum/amstrad-cpc-hardware/universal-serial-interface-for-amstrad-cpc/msg169876/#msg169876),at least not with the USIfAC (Universal Serial Interface for Amstrad CPC)
Quote from: Docent on 21:00, 07 February 19
run goes through MC BOOT PROGRAM with pointer to loading routine in rom that uses &bc83 (CAS IN DIRECT). After successful load it jumps to start address from loaded file.
Hmmm, i remember when i first got involved with this project, i managed to run a couple of these games using the RUN" command, when i "took over" loading and execution myself: everything was done using only a modified CAS IN OPEN code, i just loaded the file to ram, and then jump to execution address! This "brute" approach worked only for some games, but funny thing is, the new code has execution problems with EXACTLY these games! (besides the games that crashed because they seemed to overwrite my code...)
Most probable this "loading routine in rom " you mention, doesn't cooperate well with my code... ::)
If there was a way to distinguish if the loaded file comes from a RUN" command, maybe i could use an alternative code to load&execute these files myself, instead of leaving MCBOOTPROGRAM to screw it up! :)
Quote from: ikonsgr on 23:36, 07 February 19
I've already tried alternative places, even by splitting code in two pieces, but unfortunately this didn't work either...
Unfortunately this is not an option (http://www.cpcwiki.eu/forum/amstrad-cpc-hardware/universal-serial-interface-for-amstrad-cpc/msg169876/#msg169876),at least not with the USIfAC (Universal Serial Interface for Amstrad CPC)
Hmmm, i remember when i first got involved with this project, i managed to run a couple of these games using the RUN" command, when i "took over" loading and execution myself: everything was done using only a modified CAS IN OPEN code, i just loaded the file to ram, and then jump to execution address! This "brute" approach worked only for some games, but funny thing is, the new code has execution problems with EXACTLY these games! (besides the games that crashed because they seemed to overwrite my code...)
Most probable this "loading routine in rom " you mention, doesn't cooperate well with my code... ::)
If there was a way to distinguish if the loaded file comes from a RUN" command, maybe i could use an alternative code to load&execute these files myself, instead of leaving MCBOOTPROGRAM to screw it up! :)
Cant help you with this without seeing the code and an example game that did not work as you expected. If you want post it somewhere or send a pm with dsk and I can have a look
Ok, since you ask for it, here is the hole code:
LIMIT &FFFF
ORG &A9B0
;;NOLIST
write"DIRECT.BIN"
LD D,B
LD A,1
LD BC,&FBD1
OUT (C),A
LD A,D
LD BC,&FBD0
OUT (C),A
.LOOP_NAME
LD A,(HL)
LD BC,&FBD0
OUT (C),A
INC HL
DEC D
JR NZ,LOOP_NAME
;;RECEIVE HEADER
;;RECEIVE HEADER
LD B,69
LD DE,&A755
LD HL,&A7E4
.LOAD_HEADER
LD A,&FB
IN A,(&D1)
DEC A
JR Z,LOAD_HEADER
LD A,&FB
IN A,(&D0)
LD (DE),A
LD (HL),A
INC DE
INC HL
DEC B
JR NZ,LOAD_HEADER
LD B,59
.LOAD_FULL_HEADER
LD A,&FB
IN A,(&D1)
DEC A
JR Z,LOAD_FULL_HEADER
LD A,&FB
IN A,(&D0)
LD (HL),A
INC HL
DEC B
JR NZ,LOAD_FULL_HEADER
CALL BYTE_CHECK ;;FILESIZE TO DE (TEMP)
LD A,&FB
IN A,(&D0)
LD E,A
CALL BYTE_CHECK
LD A,&FB
IN A,(&D0)
LD D,A
LD (&A920),DE ;;A920=FILESIZE
PUSH DE
EX DE,HL ;; HL=SIZE OF FILE
;;EXIT CONDITIONS FOR CAS IN OPEN
SCF
LD A,2
DEC A
LD HL,&A7E4 ;;LD HL,&A755
LD DE,(&A76A)
POP BC
LD A,(&A767)
RET
;;CAS IN DIRECT
LD DE,(&A920)
LD (&A924),HL
.LOAD_BYTE
LD A,&FB
IN A,(&D1)
DEC A
JR Z,LOAD_BYTE
LD A,&FB
IN A,(&D0)
LD (HL),A ;;2
INC HL
DEC DE
LD A,D
OR E
JR Z,THE_END
JP LOAD_BYTE ;;22us
.THE_END
LD HL,(&A76F)
SCF
LD A,2
DEC A
RET
;;MCBOOT PROGRAM
LD A,&E9 ;;&E9 JP (HL)
LD (&A922),A
LD (&A923),HL
LD HL,&AA4C
RST 1,&85ED
CALL JUMPBLOCKS
LD HL,(&A923)
CALL &A922
;;CALL &ABB2
LD (&A923),HL
;;LD HL,(&A76F)
JP (HL)
;;MC START PROGRAM
LD (&A926),HL
LD HL,&AA62
RST 1,&861C
CALL JUMPBLOCKS
;;CALL ROUTINE
LD HL,(&A926)
JP (HL)
.BYTE_CHECK
LD A,&FB
IN A,(&D1)
DEC A
JR Z,BYTE_CHECK
RET
;;JUMPBLOCKS!
.JUMPBLOCKS
LD HL,&BC77 ;;JP &A9B0 CAS IN OPEN
LD A,&C3
LD (HL),A
INC HL
LD A,&B0
LD (HL),A
INC HL
LD A,&A9
LD (HL),A
LD HL,&BC83 ;;JP &BC83 CAS IN DIRECT
LD A,&C3
LD (HL),A
INC HL
LD A,&1A
LD (HL),A
INC HL
LD A,&AA
LD (HL),A
LD HL,&BC7A ;;JP CAS IN CLOSE
LD A,&37
LD (HL),A
INC HL
LD A,&C9
LD (HL),A
LD HL,&BD16 ;;RUN PROGRAM
LD A,&C3
LD (HL),A
INC HL
LD A,&59
LD (HL),A
INC HL
LD A,&AA
LD (HL),A
LD HL,&BD13 ;;JP BOOT PROGRAM
LD A,&C3
LD (HL),A
INC HL
LD A,&3E
LD (HL),A
INC HL
LD A,&AA
LD (HL),A
LD HL,&BC8C ;;CAS OUT OPEN
LD A,&37
LD (HL),A
INC HL
LD A,&C9
LD (HL),A
LD HL,&BCCB ;;KL ROM WALK rst 1,&8326
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
RET
And here i have two small videos, showing the difference behavior of the game 1943, when i try to load it using run"1943:
https://www.dropbox.com/s/sgv6vauvprko4kw/DSCN1163.AVI?dl=0
And here is when i use the memory loadaddr-1:load"1943":call execaddr method:
https://www.dropbox.com/s/bbo59k28nsfhq6t/DSCN1164.AVI?dl=0
In both cases, binary game file seemed to transferred succesfully from pc, but execution works only with 2nd method!
I have exactly the same problem with Eagle's nest game too.
Anyway, the good news is that i have managed to run succesfully games that use the method of:
openout"d";memory addr
Which ,if i understand it correctly, it's a metohd that allows to load a program in "forbiden" addresses, like below &170 or beyond &A06C (this was always a mystery to me, because i saw it in many game loaders,but after openout nothing was actually written to the "d" file...). It seems that many games use this loading method, and until now, the "openout" BASIC instruction, caused reset, obviously due to the modified loading routines i'm using.
And then, suddenly "hits me"! Openout is essentially the CAS OUT OPEN firmware routine, which... i hadn't modified it! By replacing the original jumpblock of the CAS OUT OPEN with only a SCF (=set carry flag, to "fool" amstrad thinks that file was opened succesfully) and RET instructions, ALL games which use the: openout"d" method, run succesfully! :)
Even Target renegade which uses openout"d" method, pokes some code, and loads many files at various addresses, is loaded and runned in ~8seconds! :D
UPDATE: I've checked dozens of games and ~75% seemed to run ok. This is a list of games that tested and run ok:
-1942
-1943
-cauldron 2
-Ghost'n' Goblins
-Target Renegade
-Solomon's key
-Thanatos
-Gryzor
-Stalone Cobra
-Robocop
-Ikari Warriors
-Shark
-Slyspy
-Rick Dangerous 2
-Billy2
-BubbleBobble2
-Silkworm
-Shinobi
-Switchblade
-Stryder
-Predator 2
-CYBERNOID 1&2
-Tank
-Uridium
-Wonder Boy
-Slug
-Titus The fox To Marakesh and Back
-Terramex
-Prehistoric
-Into the eagle's nest
-Xenon
-Pac-Mania
-Ghouls'n' ghosts
And this is alist of games that don't work:
-BabyJo
-Flimbo's Quest
-savage
-Double dragon 2&3
-Super Cauldron
-Terminator 2
-Super Cars
-Super Edge Grinder
-Twin World
-Titus the fox (***Program Load Failed***)
-Strider 2 (start ok, but then gives a message about rewind tape etc and the crashes...)
Golden Axe & UN squadron although they load ok, when they try to load the 1st level they freeze,so maybe the files are damaged or has some kind of copy protection (i'm also hearing the tape relay that clicks so maybe i need to disable some other tape routines too...).
Some of the above nonworking games are crashing or reset,most probable because they overwrite my code, but there are some that give a "***Program LOAD failed***" message, so maybe i will be able to find a way to run these too in the future.
Anyway, it seems that even as it is, a considerable number of games can be load and run directly from PC. Also all BASIC files and single binary files can be loaded directly too.
I suppose the interesting part, would be if this method works on a CPC 464 too, as it would be a rather cheap and easy solution for fast loading games and programs! ;-) Unfortunately i don't have any CPC 464 to verify it.... ::)
Quote from: ikonsgr on 14:08, 09 February 19
Ok, since you ask for it, here is the hole code:
LIMIT &FFFF
ORG &A9B0
;;NOLIST
write"DIRECT.BIN"
LD D,B
LD A,1
LD BC,&FBD1
OUT (C),A
LD A,D
LD BC,&FBD0
OUT (C),A
.LOOP_NAME
LD A,(HL)
LD BC,&FBD0
OUT (C),A
INC HL
DEC D
JR NZ,LOOP_NAME
;;RECEIVE HEADER
;;RECEIVE HEADER
LD B,69
LD DE,&A755
LD HL,&A7E4
.LOAD_HEADER
LD A,&FB
IN A,(&D1)
DEC A
JR Z,LOAD_HEADER
LD A,&FB
IN A,(&D0)
LD (DE),A
LD (HL),A
INC DE
INC HL
DEC B
JR NZ,LOAD_HEADER
LD B,59
.LOAD_FULL_HEADER
LD A,&FB
IN A,(&D1)
DEC A
JR Z,LOAD_FULL_HEADER
LD A,&FB
IN A,(&D0)
LD (HL),A
INC HL
DEC B
JR NZ,LOAD_FULL_HEADER
CALL BYTE_CHECK ;;FILESIZE TO DE (TEMP)
LD A,&FB
IN A,(&D0)
LD E,A
CALL BYTE_CHECK
LD A,&FB
IN A,(&D0)
LD D,A
LD (&A920),DE ;;A920=FILESIZE
PUSH DE
EX DE,HL ;; HL=SIZE OF FILE
;;EXIT CONDITIONS FOR CAS IN OPEN
SCF
LD A,2
DEC A
LD HL,&A7E4 ;;LD HL,&A755
LD DE,(&A76A)
POP BC
LD A,(&A767)
RET
;;CAS IN DIRECT
LD DE,(&A920)
LD (&A924),HL
.LOAD_BYTE
LD A,&FB
IN A,(&D1)
DEC A
JR Z,LOAD_BYTE
LD A,&FB
IN A,(&D0)
LD (HL),A ;;2
INC HL
DEC DE
LD A,D
OR E
JR Z,THE_END
JP LOAD_BYTE ;;22us
.THE_END
LD HL,(&A76F)
SCF
LD A,2
DEC A
RET
;;MCBOOT PROGRAM
LD A,&E9 ;;&E9 JP (HL)
LD (&A922),A
LD (&A923),HL
LD HL,&AA4C
RST 1,&85ED
CALL JUMPBLOCKS
LD HL,(&A923)
CALL &A922
;;CALL &ABB2
LD (&A923),HL
;;LD HL,(&A76F)
JP (HL)
;;MC START PROGRAM
LD (&A926),HL
LD HL,&AA62
RST 1,&861C
CALL JUMPBLOCKS
;;CALL ROUTINE
LD HL,(&A926)
JP (HL)
.BYTE_CHECK
LD A,&FB
IN A,(&D1)
DEC A
JR Z,BYTE_CHECK
RET
;;JUMPBLOCKS!
.JUMPBLOCKS
LD HL,&BC77 ;;JP &A9B0 CAS IN OPEN
LD A,&C3
LD (HL),A
INC HL
LD A,&B0
LD (HL),A
INC HL
LD A,&A9
LD (HL),A
LD HL,&BC83 ;;JP &BC83 CAS IN DIRECT
LD A,&C3
LD (HL),A
INC HL
LD A,&1A
LD (HL),A
INC HL
LD A,&AA
LD (HL),A
LD HL,&BC7A ;;JP CAS IN CLOSE
LD A,&37
LD (HL),A
INC HL
LD A,&C9
LD (HL),A
LD HL,&BD16 ;;RUN PROGRAM
LD A,&C3
LD (HL),A
INC HL
LD A,&59
LD (HL),A
INC HL
LD A,&AA
LD (HL),A
LD HL,&BD13 ;;JP BOOT PROGRAM
LD A,&C3
LD (HL),A
INC HL
LD A,&3E
LD (HL),A
INC HL
LD A,&AA
LD (HL),A
LD HL,&BC8C ;;CAS OUT OPEN
LD A,&37
LD (HL),A
INC HL
LD A,&C9
LD (HL),A
LD HL,&BCCB ;;KL ROM WALK rst 1,&8326
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
RET
Anyway, the good news is that i have managed to run succesfully games that use the method of:
openout"d";memory addr
Which ,if i understand it correctly, it's a metohd that allows to load a program in "forbiden" addresses, like below &170 or beyond &A06C (this was always a mystery to me, because i saw it in many game loaders,but after openout nothing was actually written to the "d" file...). It seems that many games use this loading method, and until now, the "openout" BASIC instruction, caused reset, obviously due to the modified loading routines i'm using.
openout is a sort of trick that allows you to load binaries from basic into very low addresses but the address cannot be lower that &170 ie below the address basic programs are stored.
Quote
And then, suddenly "hits me"! Openout is essentially the CAS OUT OPEN firmware routine, which... i hadn't modified it! By replacing the original jumpblock of the CAS OUT OPEN with only a SCF (=set carry flag, to "fool" amstrad thinks that file was opened succesfully) and RET instructions, ALL games which use the: openout"d" method, run succesfully! :)
It runs, but it is just a hack and some programs that save files will not work. I think that openout overwrites your code in the disk buffer or some other problem happens.
Quote
Even Target renegade which uses openout"d" method, pokes some code, and loads many files at various addresses, is loaded and runned in ~8seconds! :D
I have some free time today so I rewrote your code - now it is smaller, is easier to reassemble to another address and does more :)
- patches try to be more similar in functionality to routines in rom
- you should probably remove patching of cas open out as it shouldnt be modified this way
- header equ is the address of the header buffer - you can modify it to other suitable address
- try the #be80 org address - maybe it will work better
LIMIT &FFFF
header equ #ab80
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
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
load_completed
ret
send_data:
; hl - address
; d - count
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
ret
; patched routines
cas_in_open:
; 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
; setup return values
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)
xor a
scf
ret
mc_boot_program:
ld (mc_boot_start_addr+1), hl
ld hl, mc_boot_continue
mc_boot_patch:
rst 1,0
mc_boot_continue:
call patch_jumpblocks
mc_boot_start_addr:
ld hl, 0
call jumphl
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,0
mc_start_continue:
call patch_jumpblocks
mc_start_start_addr:
ld hl, 0
call jumphl
rst 0
kl_init_back:
kl_init_back_patch:
rst 1,0
jr continue
kl_rom_walk:
kl_rom_walk_patch:
rst 1,0
jr continue
mc_jump_restore:
mc_jump_restore_patch:
rst 1,0
continue:
push hl
call patch_jumpblocks
pop hl
ret
cas_in_close:
cas_out_open:
scf
ret
patch_jumpblocks:
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 cas out open
ld (#bc8c), a
ld hl, cas_out_open
ld (#bc8d), 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
; 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
filesize:
dw 0
Couldn't check if it actually runs, but feel free to find this out :)))
Quote from: ikonsgr on 18:58, 09 February 19
-Titus the fox (***Program Load Failed***)
-Strider 2 (start ok, but then gives a message about rewind tape etc and the crashes...)
Golden Axe & UN squadron although they load ok, when they try to load the 1st level they freeze,so maybe the files are damaged or has some kind of copy protection (i'm also hearing the tape relay that clicks so maybe i need to disable some other tape routines too...).
Some of the above nonworking games are crashing or reset,most probable because they overwrite my code, but there are some that give a "***Program LOAD failed***" message, so maybe i will be able to find a way to run these too in the future.
"***Program LOAD failed***" is a message from MC BOOT PROGRAM when called loader returns wit carry clear. The version I modified and posted should work correctly with these titles.
Check out these games with the loader I posted - it should increase the success ratio :)
QuoteAnyway, it seems that even as it is, a considerable number of games can be load and run directly from PC. Also all BASIC files and single binary files can be loaded directly too.
I suppose the interesting part, would be if this method works on a CPC 464 too, as it would be a rather cheap and easy solution for fast loading games and programs! ;-) Unfortunately i don't have any CPC 464 to verify it.... ::)
It should work on cpc464, but you'll still need to load the program from tape :)
Thank you very much for your interest and time Docent! I'll try your code and let you know of the results.
Btw, there is no need to have the code on tape or disk, i use a very small basic program (5-6 lines in all,takes ~1 minute to write it) , that transfers the code directly from PC to amstrad in 2 seconds,by just pressing a button on the pc program i developed, it's similar to the "cold start" function i already had!
So, the only thing you actually need to have on tape or disk, is only this small basic program! ;)
Ok,i just have the first results.
Running the code as it is, was giving me "broken in" errors on loading Basic files, so after a quick check ,i saw that you hadn't set zero flag for "cas in direct" and "cas in open" exit conditions (except setting carry flag, you also need to set zero flag too). So after adding LD A,2 and DEC A (i haven't find any more elegant way to set zero flag ::)), in exit conditions of those two routines, and setting header to &A755, basic programs and games that use BASIC loaders, were load and execute fine!
But, unfortunately any game with binary loader didn't seem to work at all. I've tried 4-5 games (of those that executed fine using my code), and all crash or reset after initial loading . So, most probable something is wrong with the MCBOOT PROGRAM, MC START routines or the patches of them.
Btw, this is the small program i'm using to transfer the code to amstrad:
10 OUT &FBD1,0:OUT &FBD1,1
20 FOR I=0 TO 287 (this changes according to code size)
30 WHILE INP(&FBD1)=1:WEND
40 POKE &A9B0+I,INP(&FBD0)
50 NEXT i
100 CALL &A9B0 (enable jump block patches)
As you can see, it's so small that you can even write it "on the fly" without saving at all!
Also, in many game loading cases, code is not damaged and remains even after reset, so it can be used for loading next game by just giving the last call to enable jumpblock patches! ;)
Docent, having a look at your code, i noticed that instead of jump to HL after the return of loading routine in MC BOOT PROGRAM , you instead, jump to MC START program routine.
I apply this modification to my code,and guess what, all the games that needed the method of :"memory loadaddr-1:load"":call &execaddr" (like terramex, eagle's nest, xenon) now they run normally using the run" command!
I have also manage to run 2-3 more games of the non working list, so what remains, is most probable games that overwrite my code, although some big games like UN SQUADRON and Golden AXE are initially loaded succesfully, but when they try to load the level file, they seem to stop. I'm also hearing the tape relay clicking,so maybe these were tape versions that converted to discs and thus having problems with my modified loading routines.
In any case, out of the 45 games tested, 33 were able to run succesfully, so now we are up to a hit ratio of ~75%!
Quote from: ikonsgr on 18:49, 10 February 19
Docent, having a look at your code, i noticed that instead of jump to HL after the return of loading routine in MC BOOT PROGRAM , you instead, jump to MC START program routine.
I apply this modification to my code,and guess what, all the games that needed the method of :"memory loadaddr-1:load"":call &execaddr" (like terramex, eagle's nest, xenon) now they run normally using the run" command!
I have also manage to run 2-3 more games of the non working list, so what remains, is most probable games that overwrite my code, although some big games like UN SQUADRON and Golden AXE are initially loaded succesfully, but when they try to load the level file, they seem to stop. I'm also hearing the tape relay clicking,so maybe these were tape versions that converted to discs and thus having problems with my modified loading routines.
In any case, out of the 45 games tested, 33 were able to run succesfully, so now we are up to a hit ratio of ~75%!
Hi,
I had a look at my code and fixed a couple of bugs:
- I fixed return flags - now all routines return correct error codes/flags just like firmware functions
- removed cas out open patch - it is not needed, see explanation below
- added more tests for various conditions - stream opened, patches applied etc
- the cas in open patch did not have a test against missing file and didn't return proper error code - added one, it assumes that pc side will return zero file length if a filename isincorrect of missing
The reason why some of programs crashed (especially those with openout in the loader) is that you put the code into disk buffer area, which is used and initialized depending on operations. One of such operations is "openout" basic command. It initializes this buffer, overwriting the code inside the buffer. This is the reason why you needed the patch for cas out open. Btw: also other operations like "cat" will destroy the code in the buffer.
I moved all the patch code to #be80, patching routines to #ab80 and buffer to #a9b0 and added needed relocation. Now the cas out open patch is not needed anymore.
The code still requires testing as I don't have the hardware to test - please check this version out! It should run with most games now.
LIMIT &FFFF
header equ #a9b0
org #be80
; nolist
;write"DIRECT.BIN"
start:
ld hl, patch_end
ld de, patch_jumpblocks
ld bc, patch_jumpblocks_end-patch_jumpblocks
ldir
jp apply_patches
; data trasfer
byte_check:
ld a,#FB
in a,(#D1)
dec a
jr z, byte_check
ret
load_data:
; hl - address
; de - count
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
load_completed
ret
send_data:
; hl - address
; d - count
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
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
; 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,0
mc_boot_continue:
call apply_patches
mc_boot_start_addr:
ld hl, 0
push hl
call jumphl
pop hl
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,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,0
jr apply_patches
kl_rom_walk:
kl_rom_walk_patch:
rst 1,0
jr apply_patches
mc_jump_restore:
mc_jump_restore_patch:
rst 1,0
apply_patches:
xor a
ld (patches_applied), a
jp patch_jumpblocks
stream_opened:
db 0
filesize:
dw 0
patch_end:
org #ab80
patch_jumpblocks:
ld a, (patches_applied)
or a
ret nz
ld a, #c3 ; jump opcode
push hl
;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
; 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
pop hl
ld a, 1
ld (patches_applied), a
ret
patches_applied:
db 0
patch_jumpblocks_end
Quote from: ikonsgr on 12:34, 10 February 19
Ok,i just have the first results.
Running the code as it is, was giving me "broken in" errors on loading Basic files, so after a quick check ,i saw that you hadn't set zero flag for "cas in direct" and "cas in open" exit conditions (except setting carry flag, you also need to set zero flag too). So after adding LD A,2 and DEC A (i haven't find any more elegant way to set zero flag ::)), in exit conditions of those two routines, and setting header to &A755, basic programs and games that use BASIC loaders, were load and execute fine!
Yeah, I missed that - I fixed it in the latest code. I used a different code to set carry and clear zero
xor a
inc a
scf
Quote
But, unfortunately any game with binary loader didn't seem to work at all. I've tried 4-5 games (of those that executed fine using my code), and all crash or reset after initial loading . So, most probable something is wrong with the MCBOOT PROGRAM, MC START routines or the patches of them.
Btw, this is the small program i'm using to transfer the code to amstrad:
10 OUT &FBD1,0:OUT &FBD1,1
20 FOR I=0 TO 287 (this changes according to code size)
30 WHILE INP(&FBD1)=1:WEND
40 POKE &A9B0+I,INP(&FBD0)
50 NEXT i
100 CALL &A9B0 (enable jump block patches)
As you can see, it's so small that you can even write it "on the fly" without saving at all!
Also, in many game loading cases, code is not damaged and remains even after reset, so it can be used for loading next game by just giving the last call to enable jumpblock patches! ;)
Nice! unfortunately I don't have the hardware to connect to my cpc and test...
Btw: you could modify this to first receive file length as two bytes, then the start/loading address (2 bytes) and load them as variables for loop and start address for poke and a call. Then you'll not need to modify the basic code anymore :)
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.
- 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.
- 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.
- 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.
So, using your code which is much more flexible, elegant and smaller, i apply the above modifications and manage to get exactly the same results with my code!
And here is the modded code of yours that seemed to worked ok:
LIMIT &FFFF
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_header:
; hl - address
; de - count
call byte_check
ld a ,#FB
in a,(#D0)
ld (hl), a
ld (bc), a
inc hl
inc bc
dec de
ld a,d
or e
jr z, load_completed2
jr load_header
load_completed2
ret
load_data:
; hl - address
; de - count
.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
load_completed
ret
send_data:
; hl - address
; d - count
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
ret
; patched routines
cas_in_open:
; send file name
ld d, b
call send_data
; receive file header
ld hl, header_full
ld bc, header
ld de, 69
call load_header
ld de, 59
call load_data
; 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_full
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
LD C, &FF
jr c, mc_start_program
rst 0
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
call jumphl
rst 0
;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
cas_out_open:
scf
ret
patch_jumpblocks:
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 cas out open
ld (#bc8c), a
ld hl, cas_out_open
ld (#bc8d), 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,&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)
filesize:
dw 0
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.... :)
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:
Indeed i'm using winape assembler. Thanks again Docent,i will surely give it a try and let you know!
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... ::)
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.
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"!
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.... ::)
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.
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 :)
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.
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...
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 ! ;)
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 :)