tap8k. simple, small and fast loader

The place for codemasters or beginners to talk about programming any language for the Spectrum.
antoniovillena
Berk
Posts: 10
Joined: Sat Dec 09, 2017 9:07 pm

tap8k. simple, small and fast loader

Post by antoniovillena » Sat Dec 09, 2017 9:16 pm

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
2 x

User avatar
RMartins
Manic Miner
Posts: 331
Joined: Thu Nov 16, 2017 3:26 pm

Re: tap8k. simple, small and fast loader

Post by RMartins » Sun Dec 10, 2017 5:30 am

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 ?
0 x

antoniovillena
Berk
Posts: 10
Joined: Sat Dec 09, 2017 9:07 pm

Re: tap8k. simple, small and fast loader

Post by antoniovillena » Sun Dec 10, 2017 2:19 pm

Write a simple TAP using Pasmo and let's compare. I am sure with tap8k the loading time (and filesize) is almost half.
RMartins wrote:
Sun Dec 10, 2017 5:30 am
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 ?
0 x

antoniovillena
Berk
Posts: 10
Joined: Sat Dec 09, 2017 9:07 pm

Re: tap8k. simple, small and fast loader

Post by antoniovillena » Sun Dec 10, 2017 3:08 pm

I did. You can compare:
  • rect_tap8k.tap is 70 bytes, 10 seconds to load
  • rect_pasmo.tap is 146 bytes, 20 seconds to load
I can not attach the .zip but you can see at this image.

Image
0 x

User avatar
RMartins
Manic Miner
Posts: 331
Joined: Thu Nov 16, 2017 3:26 pm

Re: tap8k. simple, small and fast loader

Post by RMartins » Sun Dec 10, 2017 3:16 pm

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

Code: Select all

	output_name= (char *)malloc(strlen(argv[1])+1);
	ext[0]= 0;
	strcpy(output_name, argv[1]);
	strcat(output_name, ".TAP");
This will fix it:

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");
It's also good practice to check if malloc returns something != NULL, but I leave that to you :)

Code: Select all

	unsigned char mem[0x10000]
...
	size= fread(mem+23, 1, 0x10000, fi);
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 ;) .

Code: Select all

	*(int*)(mem)= 19;
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 :D ).

Code: Select all

	*(short*)(mem+18)= *(short*)(mem+14)= *(short*)(mem+21)= size+20;
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" :D, 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;
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.
0 x

User avatar
Ast A. Moore
Manic Miner
Posts: 765
Joined: Mon Nov 13, 2017 3:16 pm

Re: tap8k. simple, small and fast loader

Post by Ast A. Moore » 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.
0 x
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.

User avatar
RMartins
Manic Miner
Posts: 331
Joined: Thu Nov 16, 2017 3:26 pm

Re: tap8k. simple, small and fast loader

Post by RMartins » Sun Dec 10, 2017 3:34 pm

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 :)
0 x

User avatar
Seven.FFF
Manic Miner
Posts: 283
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: tap8k. simple, small and fast loader

Post by Seven.FFF » Sun Dec 10, 2017 3:51 pm

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 ;)
0 x
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
seven-fff.com/blog

antoniovillena
Berk
Posts: 10
Joined: Sat Dec 09, 2017 9:07 pm

Re: tap8k. simple, small and fast loader

Post by antoniovillena » Mon Dec 11, 2017 1:31 am

RMartins wrote:
Sun Dec 10, 2017 3:16 pm

Code: Select all

	*(int*)(mem+3)= 0x8e117d06;
	*(int*)(mem+7)= 0x0e37c0fd;
	*(int*)(mem+11)= 0x2196398f;
	*(int*)(mem+15)= 0xb8edda6d;
	*(int*)(mem+19)= 0xe9f9eb13;
The hidden assembly code is:

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)
Thank you for the suggestions. Please put the complete fixed code and I will update the file (with your credits).
Last edited by antoniovillena on Mon Dec 11, 2017 1:34 am, edited 1 time in total.
0 x

antoniovillena
Berk
Posts: 10
Joined: Sat Dec 09, 2017 9:07 pm

Re: tap8k. simple, small and fast loader

Post by antoniovillena » Mon Dec 11, 2017 1:32 am

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.
puts machine code inside BASIC loader. What assembler do that?
0 x

Post Reply