Page 1 of 1

tap8k. simple, small and fast loader

Posted: Sat Dec 09, 2017 9:16 pm
by antoniovillena
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

Re: tap8k. simple, small and fast loader

Posted: Sun Dec 10, 2017 5:30 am
by RMartins
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 ?

Re: tap8k. simple, small and fast loader

Posted: Sun Dec 10, 2017 2:19 pm
by antoniovillena
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 ?

Re: tap8k. simple, small and fast loader

Posted: Sun Dec 10, 2017 3:08 pm
by antoniovillena
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

Re: tap8k. simple, small and fast loader

Posted: Sun Dec 10, 2017 3:16 pm
by RMartins
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.

Re: tap8k. simple, small and fast loader

Posted: Sun Dec 10, 2017 3:18 pm
by Ast A. Moore
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.

Re: tap8k. simple, small and fast loader

Posted: Sun Dec 10, 2017 3:34 pm
by RMartins
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 :)

Re: tap8k. simple, small and fast loader

Posted: Sun Dec 10, 2017 3:51 pm
by Seven.FFF
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 ;)

Re: tap8k. simple, small and fast loader

Posted: Mon Dec 11, 2017 1:31 am
by antoniovillena
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).

Re: tap8k. simple, small and fast loader

Posted: Mon Dec 11, 2017 1:32 am
by antoniovillena
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?

Re: tap8k. simple, small and fast loader

Posted: Mon Dec 11, 2017 9:22 am
by Ast A. Moore
antoniovillena wrote: 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?
zasm, for instance.

Re: tap8k. simple, small and fast loader

Posted: Mon Dec 11, 2017 12:00 pm
by antoniovillena
Ast A. Moore wrote: Mon Dec 11, 2017 9:22 am zasm, for instance.
I have Windows and I can't try.

Re: tap8k. simple, small and fast loader

Posted: Fri Mar 02, 2018 5:38 pm
by antoniovillena
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

Re: tap8k. simple, small and fast loader

Posted: Sat Mar 03, 2018 11:59 am
by Nomad
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.

Re: tap8k. simple, small and fast loader

Posted: Sun Mar 04, 2018 10:59 am
by antoniovillena
Nomad wrote: Sat Mar 03, 2018 11:59 am I am just concerned if it will work on real hardware.
Yes. It works on real hardware.

Re: tap8k. simple, small and fast loader

Posted: Fri Mar 16, 2018 11:59 pm
by Rorschak
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:

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