Generating Huge SNAs with Z88DK

Discuss game and other programming topics not specifically covered in another forum

Moderator: Programming Moderators

Alcoholics Anonymous
Posts: 463
Joined: Mon May 29, 2017 7:00 pm

Generating Huge SNAs with Z88DK

Postby Alcoholics Anonymous » Sat May 26, 2018 2:33 am

It turns out that NextOS and a variety of emulators are not too concerned about the filesize of sna snapshots as long as they contain everything expected. Kev wrote a terminator demo a short while ago that took advantage of this by appending data to his sna so that he could hold all the data of his program in a single sna file.

So I've added automatic generation of big snas that contain all of the next's memory in one file to z88dk. These files can automatically load the next's memory from the sna file before starting (just the bank / page space and not divmmc memory or resources).

A short review of relevant command line options before an example:

-subtype=sna -Cz"--clean --fullsize --pages" -create-app

This will generate either a 48k or 128k sna depending on whether the program has stuff in the 128k memory banks. "--clean" means the sna generator will remove binaires produced by the compile as it uses them to generate the output. Any remaining binary files are your responsibility to load into memory. Things placed into the next memory that cannot normally be stored in the sna (ie 16k banks 8 and above) will be output as separate binary files. "--fullsize" says those binary files should be padded to a full 8k or 16k. "--pages" indicates contents of memory should be output as 8k pages.

-subtype=sna -Cz"--128 --clean --fullsize --pages" -create-app

As above but this time force a 128k snapshot. NextOS does treat 48k and 128k snas differently. In particular 48k snapshots have port 7ffd locked when started.

-subtype=sna -Cz"--ext --clean --fullsize --pages" -create-app

This is the new big sna type. The contents of BANK and PAGE space will be added to the end of the sna. You can append even more data to the sna if you add a single byte "255" preceeding the appended data. This appended data will be ignored by the loader. "--fullsize --pages" will only affect the binary files produced for other bankspaces like divmmc memory in DIV that will not be in the sna.

An example consisting of two source files:

zzz.asm

Code: Select all

SECTION BANK_5

defs 2048,0xaa

SECTION PAGE_30

defs 128, 0
defs 128, 30
defs 128, 0xff

SECTION PAGE_41

defs 128, 0
defs 128, 41
defs 128, 0xff

SECTION PAGE_125

defs 128, 0
defs 128, 125
defs 128, 0xff

SECTION DIV_1

defm "DIV1", 0
This asm file places stuff in various memory banks. BANK_5 corresponds to the ula screen so when the program starts you should see some vertical stripes in the display. DIV_1 is page 1 of divmmc memory. This is not made part of the sna but is included to make sure it is still output in a separate bin file.

zzz.c

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <arch/zxn/esxdos.h>
#include <arch/zxn.h>
#include <errno.h>

unsigned char _z_sna_filename[13];

void print_page(unsigned char page)
{
   ZXN_WRITE_MMU3(page);
   
   printf("Page %u\n\n", page);
   
   for (unsigned char *p = 0x6000; p != 0x6180; ++p)
      printf("%02x", *p);
   
   printf("\n\n");
   
   ZXN_WRITE_MMU3(11);
}

int main(void)
{
   unsigned char fin;
   
   printf("Filename: %s\n\n", _z_sna_filename);
   
   fin = esxdos_f_open(_z_sna_filename, ESXDOS_MODE_R | ESXDOS_MODE_OPEN_EXIST);
   
   if (errno == 0)
   {
      extended_sna_load(fin);
      esxdos_f_close(fin);
   }
   
   if (errno)
   {
      printf("Error: %u\n", errno);
      exit(1);
   }
   
   print_page(30);
   print_page(41);
   print_page(125);
   
   return 0;
}
This C file invokes the loader and then prints the contents of the loaded memory banks.


The compile line is:

zcc +zxn -vn -startup=0 -clib=sdcc_iy -SO3 --max-allocs-per-node200000 zzz.c zzz.asm -o zzz -subtype=sna -Cz"--ext --fullsize --clean" -create-app

The output is a single 153k sna file containing everything.


The loading process could have been hidden in z88dk's crt but I decided against hiding it for a few reasons which may be clear when the loading process is described.

1.
The loader needs to know the name of the sna file in order to open itself. If your program declares 13 bytes of space at location "__z_sna_filename", the sna generator will write the name of the sna file into that space as the sna is created. In this example the output filename is "zzz.sna" and this is written into the sna. Notice that the filename must be 8.3. If you do not provide this space, nothing happens. If someone renames the sna file, it all breaks.

2.
The program opens the sna file. If you did not provide space at "__z_sna_filename" you can instead use a constant name like:

fin = esxdos_f_open("zzz.sna", ESXDOS_MODE_R | ESXDOS_MODE_OPEN_EXIST);

You just have to make sure the name of your sna on disk matches.

3.
extended_sna_load() is called to load the memory banks contained in the sna. Care is taken to make sure the file is closed when this is finished. If all is well 0 is returned otherwise -1 is returned and errno is set. The pages appended to the sna are encoded as {page,8k} where page is a single byte identifying page number. If page=255, the sna load process is stopped. At this point you could read more data yourself or get a file pointer position so that your program can seek the sna while running to load extra data on demand. This is not done here and instead the file is just closed after the sna load process ends.

4.
The rest of the program just verifies that the data was loaded properly.


The example is in C because that's much faster to write but of course it can all be done from assembler too (the asm loader function is "asm_extended_sna_load" ( https://github.com/z88dk/z88dk/blob/mas ... a_load.asm )

From asm you'd just call it like this:

Code: Select all

EXTERN asm_extended_sna_load

ld l,handle
call asm_extended_sna_load

; carry set if error
A couple of caveats. The loader uses mmu2 (0x4000-0x5fff, normally the ula screen) to map pages in and load into. This means the loader itself cannot be located in this region of memory. This is almost always ok as you'd have to place the program org quite low for there to be a problem and normally the main bank's program org is around 32768. The other is the 13 bytes at "__z_sna_filename" are assumed to be in the main bank but this is a good assumption for sna.
Last edited by Alcoholics Anonymous on Mon May 28, 2018 2:20 am, edited 1 time in total.

User avatar
MrKWatkins
Posts: 39
Joined: Tue May 30, 2017 8:37 pm

Re: Generating Huge SNAs with Z88DK

Postby MrKWatkins » Sat May 26, 2018 12:00 pm

This looks really good! I'll have a play when I get a chance.

Alcoholics Anonymous
Posts: 463
Joined: Mon May 29, 2017 7:00 pm

Re: Generating Huge SNAs with Z88DK

Postby Alcoholics Anonymous » Sun May 27, 2018 6:12 am

NextOS 1.98E introduces an .snx file type that is exactly the same as .sna but when the program is launched, the sna file is kept open on file handle 0 so that the program is started with an open handle on its executable. This way it doesn't have to know its filename to open its executable.

This snx type is not going to work on emulators without the emulators being changed nor will it work with other operating systems (eg esxdos). zesarux will be able to load these types in full emulation mode because it executes NextOS and NextOS implements the behaviour.

There is not much changes from the example I gave above; the only difference is no open has to occur and there's no need to know the filename:

zzz.asm

Code: Select all

SECTION BANK_5

defs 2048,0xaa

SECTION PAGE_30

defs 128, 0
defs 128, 30
defs 128, 0xff

SECTION PAGE_41

defs 128, 0
defs 128, 41
defs 128, 0xff

SECTION PAGE_125

defs 128, 0
defs 128, 125
defs 128, 0xff

SECTION DIV_1

defm "DIV1", 0
(unchanged)

zzz.c

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <arch/zxn/esxdos.h>
#include <arch/zxn.h>
#include <errno.h>

void print_page(unsigned char page)
{
   ZXN_WRITE_MMU3(page);
   
   printf("Page %u\n\n", page);
   
   for (unsigned char *p = 0x6000; p != 0x6180; ++p)
      printf("%02x", *p);
   
   printf("\n\n");
   
   ZXN_WRITE_MMU3(11);
}

int main(void)
{
   extended_sna_load(0);
   esxdos_f_close(0);

   if (errno)
   {
      printf("Error: %u\n", errno);
      exit(1);
   }
   
   print_page(30);
   print_page(41);
   print_page(125);
   
   return 0;
}
Compile line:

zcc +zxn -vn -startup=0 -clib=sdcc_iy -SO3 --max-allocs-per-node200000 zzz.c zzz.asm -o zzz -subtype=snx -Cz"--fullsize --clean" -create-app

I missed tonight's deadline for the nightly build so this will be in tomorrow's instead.

Alcoholics Anonymous
Posts: 463
Joined: Mon May 29, 2017 7:00 pm

Re: Generating Huge SNAs with Z88DK

Postby Alcoholics Anonymous » Thu May 31, 2018 4:45 pm

This is now documented by an example inside z88dk:
https://github.com/z88dk/z88dk/tree/mas ... xn/big-sna


Who is online

Users browsing this forum: No registered users and 2 guests