tap8k. simple, small and fast loader
-
- Drutt
- Posts: 17
- Joined: Sat Dec 09, 2017 9:07 pm
tap8k. simple, small and fast loader
I did this tool that can be useful. It generates a .TAP with only the basic block from a binary file (assembled with .org 32768). It's good for small games or tests (because you don't have loading screen).
https://github.com/antoniovillena/zx7b/ ... er/tap8k.c
https://github.com/antoniovillena/zx7b/ ... /tap8k.exe
https://github.com/antoniovillena/zx7b/ ... er/tap8k.c
https://github.com/antoniovillena/zx7b/ ... /tap8k.exe
Re: tap8k. simple, small and fast loader
Pasmo already generates .TAP files, and you can simply concatenate a binary output with a pre-defined basic loader.TAP.
What specific case does it solve ?
Looking at the code, it seems, it does just that, add a basic loader.
Am I missing something ?
What specific case does it solve ?
Looking at the code, it seems, it does just that, add a basic loader.
Am I missing something ?
-
- Drutt
- Posts: 17
- Joined: Sat Dec 09, 2017 9:07 pm
Re: tap8k. simple, small and fast loader
Write a simple TAP using Pasmo and let's compare. I am sure with tap8k the loading time (and filesize) is almost half.
-
- Drutt
- Posts: 17
- Joined: Sat Dec 09, 2017 9:07 pm
Re: tap8k. simple, small and fast loader
I took a more detailed look at the code, and found a few things
You have a bug here, whenever the input filename has an extension shorter then 3 chars
This will fix it:
It's also good practice to check if malloc returns something != NULL, but I leave that to you
Previous line is a potential bug, because it will do a buffer overrun if the file is larger than 0x10000 or 65536 (actually a little less, 24 bytes will do).
Overrun happens, if it copies up to the max (0x10000) buffer size, while starting at a larger offset then zero (mem+23).
We can assume the files should be shorter, since the block or header length fields will not allow it, but a user can throw any file at you.
A buffer overrun will surely mess with other application variables on top, and it's nice to not blow up .
This will only work if we compile the code in a little endian CPU architecture.
When outputting to files, it's advisable to handle byte ordering, and not rely on type of CPU architecture of host CPU matching the one of the target computer.
Although if building for Windows (.exe), you might not have considered this could be built in other OS/CPU architecture.
On another note, since an int is 4 bytes, it's also initializing the flag and first header byte to zero, meaning respectively an header block of a program. (sneaky coding ).
Apparently here is another bug, since according to TAP format https://faqwiki.zxnet.co.uk/wiki/TAP_format
at position mem+14 ( 2 +1 +1 + 10), is the position of the header Length of data block which should be 2 bytes less than the actual next code Block Length (mem+21) since it does not include the block flag or checksum bytes.
Most emulators (eventually ZX ROM too) will probably only consider the next Block Length to determine the program size, but the Length of data block in header and actual code Block Length will be inconsistent if left like this.
The reminder of the code is a bit hard to follow due to "Magic Numbers" , that I presume is a BASIC program.
I would suggest, that you add some comments and refactor parts of the code into functions (calc_checksum, initHeader, writeBlock, etc..), so that it's easier to maintain.
You have a bug here, whenever the input filename has an extension shorter then 3 chars
Code: Select all
output_name= (char *)malloc(strlen(argv[1])+1);
ext[0]= 0;
strcpy(output_name, argv[1]);
strcat(output_name, ".TAP");
Code: Select all
ext[0]= 0;
output_name= (char *)malloc(strlen(argv[1])+4+1); // strlen(argv[1]) + strlen((".TAP") + 1;
strcpy(output_name, argv[1]);
strcat(output_name, ".TAP");
Code: Select all
unsigned char mem[0x10000]
...
size= fread(mem+23, 1, 0x10000, fi);
Overrun happens, if it copies up to the max (0x10000) buffer size, while starting at a larger offset then zero (mem+23).
We can assume the files should be shorter, since the block or header length fields will not allow it, but a user can throw any file at you.
A buffer overrun will surely mess with other application variables on top, and it's nice to not blow up .
Code: Select all
*(int*)(mem)= 19;
When outputting to files, it's advisable to handle byte ordering, and not rely on type of CPU architecture of host CPU matching the one of the target computer.
Although if building for Windows (.exe), you might not have considered this could be built in other OS/CPU architecture.
On another note, since an int is 4 bytes, it's also initializing the flag and first header byte to zero, meaning respectively an header block of a program. (sneaky coding ).
Code: Select all
*(short*)(mem+18)= *(short*)(mem+14)= *(short*)(mem+21)= size+20;
at position mem+14 ( 2 +1 +1 + 10), is the position of the header Length of data block which should be 2 bytes less than the actual next code Block Length (mem+21) since it does not include the block flag or checksum bytes.
Most emulators (eventually ZX ROM too) will probably only consider the next Block Length to determine the program size, but the Length of data block in header and actual code Block Length will be inconsistent if left like this.
The reminder of the code is a bit hard to follow due to "Magic Numbers" , that I presume is a BASIC program.
Code: Select all
*(int*)(mem+3)= 0x8e117d06;
*(int*)(mem+7)= 0x0e37c0fd;
*(int*)(mem+11)= 0x2196398f;
*(int*)(mem+15)= 0xb8edda6d;
*(int*)(mem+19)= 0xe9f9eb13;
- Ast A. Moore
- Rick Dangerous
- Posts: 2641
- Joined: Mon Nov 13, 2017 3:16 pm
Re: tap8k. simple, small and fast loader
I’m also quite confused. Is it compressing the data or simply puts machine code inside the BASIC loader? Because you can do both with many assemblers already.
Every man should plant a tree, build a house, and write a ZX Spectrum game.
Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
Re: tap8k. simple, small and fast loader
There is another sneaky bug, that lurks in the code.
It never initializes mem+16, the Program Header first parameter or start LINE.
Since code is using malloc and not calloc, it only works because by chance those 2 bytes are set to zero.
NOTE: Including windows NT / XP and after, windows is known to init/clear memory of applications before start.
If software is compiled on other systems, usually we get some nasty surprises, if the system does not init memory to zeros like windows does.
This is a very good windows feature, to hide software problems
It never initializes mem+16, the Program Header first parameter or start LINE.
Since code is using malloc and not calloc, it only works because by chance those 2 bytes are set to zero.
NOTE: Including windows NT / XP and after, windows is known to init/clear memory of applications before start.
If software is compiled on other systems, usually we get some nasty surprises, if the system does not init memory to zeros like windows does.
This is a very good windows feature, to hide software problems
Re: tap8k. simple, small and fast loader
Nice Antonio, thanks!
I like reading about subtle bugs in c++. I am guilty myself of introducing them every time I change other people’s code
I like reading about subtle bugs in c++. I am guilty myself of introducing them every time I change other people’s code
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
-
- Drutt
- Posts: 17
- Joined: Sat Dec 09, 2017 9:07 pm
Re: tap8k. simple, small and fast loader
The hidden assembly code is:RMartins wrote: ↑Sun Dec 10, 2017 3:16 pmCode: Select all
*(int*)(mem+3)= 0x8e117d06; *(int*)(mem+7)= 0x0e37c0fd; *(int*)(mem+11)= 0x2196398f; *(int*)(mem+15)= 0xb8edda6d; *(int*)(mem+19)= 0xe9f9eb13;
Code: Select all
l5ccb: ld b, $7d
ld de, $fd8e
defb $c0, $37, $0e, $8f, $39, $96 ; paolo ferraris method, in basic jump to $5ccb
l5cd6: ld hl, $da6d
lddr
inc de
ex de, hl
ld sp, hl
jp (hl)
Last edited by antoniovillena on Mon Dec 11, 2017 1:34 am, edited 1 time in total.
-
- Drutt
- Posts: 17
- Joined: Sat Dec 09, 2017 9:07 pm
Re: tap8k. simple, small and fast loader
puts machine code inside BASIC loader. What assembler do that?Ast A. Moore wrote: ↑Sun Dec 10, 2017 3:18 pm I’m also quite confused. Is it compressing the data or simply puts machine code inside the BASIC loader? Because you can do both with many assemblers already.
- Ast A. Moore
- Rick Dangerous
- Posts: 2641
- Joined: Mon Nov 13, 2017 3:16 pm
Re: tap8k. simple, small and fast loader
zasm, for instance.antoniovillena wrote: ↑Mon Dec 11, 2017 1:32 amputs machine code inside BASIC loader. What assembler do that?Ast A. Moore wrote: ↑Sun Dec 10, 2017 3:18 pm I’m also quite confused. Is it compressing the data or simply puts machine code inside the BASIC loader? Because you can do both with many assemblers already.
Every man should plant a tree, build a house, and write a ZX Spectrum game.
Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
-
- Drutt
- Posts: 17
- Joined: Sat Dec 09, 2017 9:07 pm
Re: tap8k. simple, small and fast loader
I have Windows and I can't try.
-
- Drutt
- Posts: 17
- Joined: Sat Dec 09, 2017 9:07 pm
Re: tap8k. simple, small and fast loader
I have released a version that includes shrinkler compressor, developped here.
https://github.com/antoniovillena/zx7b/ ... /shr8k.asm
So from a input binary file assembled with org $8000 you will have a compressed TAP file like this:
https://github.com/antoniovillena/zx7b/ ... r/demo.tap
https://github.com/antoniovillena/zx7b/ ... /shr8k.asm
So from a input binary file assembled with org $8000 you will have a compressed TAP file like this:
https://github.com/antoniovillena/zx7b/ ... r/demo.tap
Re: tap8k. simple, small and fast loader
Has it been tested on real hardware (48k,128k,+3) Just curious because a lot of these loaders that perform seemingly fine in emulation have issues on the real thing.
I must confess I never really thought about a better loader than what zeus/pasmo gives you but fair play there appears to be scope for improvement over what the assemblers can give us. I am just concerned if it will work on real hardware.
I must confess I never really thought about a better loader than what zeus/pasmo gives you but fair play there appears to be scope for improvement over what the assemblers can give us. I am just concerned if it will work on real hardware.
-
- Drutt
- Posts: 17
- Joined: Sat Dec 09, 2017 9:07 pm
Re: tap8k. simple, small and fast loader
The program fails if you have an interface that moves the basic. A spectrum 48K with a betadisk interface moves the basic away from 23755. You should probably check PEEK 23635+256*PEEK 23636 in order to locate the start of your code.
If you need to know where your code is executing, i use this little trick:
If you need to know where your code is executing, i use this little trick:
Code: Select all
LocateProgram: XOR A ; SET Z FLAG
INC A ; RESET Z FLAG
CALL $1FC6 ; THIS CALL TO THE ROM CONTAINS THESE INSTRUCTIONS. POP HL, RET Z, JP (HL).
Mycode: ... ; AT THIS POINT, HL CONTAINS THE ADDRESS OF THIS INSTRUCTION