- ZXDAAD128
This project is a DAAD interpreter created from scratch with Boriel's ZXBasic for Spectrum 128k and compatible systems and enabling the use of the extra banks to make bigger adventures and showing bitmap images without the requirement of disk.
DAAD is a multi-machine and multi-graphics adventure writer, enabling you to target a broad range of 8-bit and 16-bit systems.
You can see the classic interpreters here.
ZXDAAD128 requires to use the frontend of DAAD Reborn Compiler with a specific backend supplied with this project in order to distribute parts of the database around other banks. The Messages (MTX), System Messages (STX), Location Descriptions (LTX), Object Descriptions (OTX) and the character set will be allocated on other banks if they do not fit on the space available on bank 0. All the other database data will always be on Bank 0.
ZXDAAD128 supports compressed bitmap graphics with the DCP tool, instead of the traditional vector graphics.
The tool uses the ZX0 compressor by Einar Saukas to compress full SCR files and the images will also be allocated on the banks of RAM by the compiler backend on the TAPE
subtarget. On PLUS3
, they will be loaded from disk.
In order to show pictures, first you must use the CondAct PICTURE
to prepare the image for showing. On PLUS3
subtarget, it will load the image on buffer and on the TAPE
subtarget it will simply check that it exists on RAM. Please, refer to the original DAAD documentation, since the interpreter imitates its behabiour.
Then you must use the condAct DISPLAY
to show the buffered image on screen, also described on the original DAAD documentation.
Be aware that the image will be shown always on full screen, there is no provision of clipping and positioning the image according to the active window.
Supports loading and saving on tape or disk with +3DOS and 42 or 32 characters per line.
ZXDAAD128 also emulates, to some extent, Maluva DAAD extension which adds new functionalities to the classic interpreters. These are the extended condActs currently supported:
XPICTURE
: Loads and shows a compressed DCP bitmap image. This function does not support the standard Maluva's SC2DAAD format. Instead, it will execute thePICTURE
andDISPLAY
condacts on sequence and report error with the standard Maluva method of error reporting, please refer to its wiki for more information.XMESSAGE
: Use of extra texts. The maximum size allowed is 64Kb, divided into 4 blocks of 16Kb maximum, which will also be distributed around the memory banks by the compiler's backend on both targets.XUNDONE
: To cancel the status "done".XLOAD
/XSAVE
: To load/save your gameplay. They work like the traditionalLOAD
/SAVE
condActs on this interpreter.
- The interpreter is very big!
- The images are always displayed from the upper left corner of the screen. They do not ajust themselves to the dimensions and position of the active window, which is the standard behaviour.
- CondActs
GFX
not implemented. - Maluva's condActs
XPART
,XBEEP
.XSPLITSCR
,XNEXTCLS
,XNEXTRST
&XSPEED
not supported.
- English
- Spanish
These are the essential requirements to create adventures with this interpreter:
- DAAD Reborn Compiler, more precisely, the frontend program
DRF.exe
, version 0.27 or upper. It can be obtained here. - PHP, which can be obtained here, please follow its installation instructions for your OS.
If you want to compile the interpreter, you will also need:
And for DCP:
- Any C99 standard compiler.
To create your own adventure you need a text source file (.DSF), and the source file must be encoded with Windows-1252 or ISO-8859-1 charsets.
There are empty templates in several languages to start your adventure:
To learn more about how to create your own adventure, your can:
- Read the excellent Wiki pages of Natalia's Msx2Daad project, to know more about DAAD.
- Also you can follow this great DAAD Tutorial for beginners writted by the author of the DRC compiler.
From now on, you have to paths to follow:
- Use DAAD Ready, please refer to the relevant section.
- Doing it yourself manually with the instructions detailed on the next sections.
DAAD Ready will avoid you all the hassle of setting up a build framework from the ground up, but it is recommended to skim a bit the following sections in order to know some useful options of the intepreter to customize your adventure.
If you want to do your own build, all the required files are on the /DRC
directory.
After you have the DSF
file of your adventure, you must compile it with the frontend program DRF
as described on its documentation with the ZX target and the desired subtarget, like so:
drf zx mygame.dsf
drf zx plus3 mygame.dsf
However, instead of generating the DDB
file with the standard drb
program, you must use the program drb128
located on the /DRC
directory of this distribution.
This is not an executable file but a php script, so you need to run it with a php interpreter. Again, please refer to the DRC wiki for more information.
You could execute it with something like this on the command line:
php drb128 -i scr\ TAPE EN EN 42 mygame.json AD8X6.CHR
These are the command line parameters that drb128
needs:
php drb128 [options] <subtarget> <lang.> <lang. interpreter> <characters per line> <input file> <character set>
The meaning of these parameters are the following:
- subtarget : The subtarget machine. Valid values:
TAPE
orPLUS3
. - lang. : The game language, should be
EN
,ES
,DE
,FR
orPT
(English, Spanish, German, French or Portuguese). - lang. interpreter: The languaje employed by the interpreter. Valid values:
EN
orES
(English, Spanish) - characters per line: Number of characters per line. Valid values:
42
or32
. - inputfile : Path to the json file generated by
DRF
. - charsetfile : Path to a binary file with the embedded charset.
And these are the optional parameters:
- -v : Verbose output
- -3 : Prepend +3 header to the resulting files
- -c : Forced classic mode
- -d : Forced debug mode
- -b : Use best fit algorithm when assigning the memory banks (first fit by default).
- -o [output file] : Path & file name of the output files. If absent, same path & file name of json input file would be used.
- -i [image path] : The path for the images to load on RAM. If not defined, no images will be loaded. Only for TAPE target, since on PLUS3, they will be loaded from disk.
- -k [char. id] : The ASCII code of the character which will be used as a cursor. By default, the character "_" will be used (code 95).
- -p [palette file] : Path to a JSON file which define the colors of the 16 values used on the interpreter. Please refer to the file
DRC/palette.json
for a sample. - -t [Token file] : Path to a file with alternative tokens for text compression.
- -x [bank number] : Number of bank to exclude from allocation.
The character set file must be a 2048 bytes file, 8 bytes per character, 256 characters. Please take in mind that if you are using a 42 lines interpreter, the character set must be 6x8, so despite each of your characters has 8 bits per scanline to define, make sure you don't use the two rightmost ones (and even the third rightmost one if you want to have some space between characters).
Two character sets, AD8x8.CHR
and AD6x8.CHR
, are included for your own use.
By default, the backend will generate one or more files with the same name and path of the input file, but instead the extension .ADn
, being n a number.
This number is the memory bank where the contents of the corresponding file should be loaded.
So in the previous example, on the path of mygame.json
, you would get files like mygame.AD0
, mygame.AD1
, and so on...
With the -o
optional parameter, you can define a different name and path for the output files.
The file with extension .AD0
should be loaded with the bank 0 active at the address 0x6000 and jump to address 0x6002 in order to run. The other files (if any), should be loaded at 0xC000 with the corresponding bank of the extension active. However, there are two scripts on the distribution, DAADMaker128
and DAADMakerPlus3
, which can do the job for you. They will be explained on the following sections.
The backend program will put the interpreter and the main code of the database on Bank 0 always, but it will allocate the Messages (MTX), System Messages (STX), Location Descriptions (LTX), Object Descriptions (OTX), the character set, the extra messages and compressed images on other banks if necessary.
The default policy of allocation is first fit, that is, it will allocate a data block on the first bank where there is enough space avaliable to fit. This policy will generate the lower number of files.
However, if there are too much blocks (i.e. images), it becomes inefficient. On that case, you can use the -b
parameter, which will enable the best fit allocation policy instead. This means that the block will be allocated on the bank with less space where it can fit.
The backend compiler includes the tokens included in the default databases for both English and Spanish, in order to compress the texts.
If you need to get the better tokens for a given title you can use DRT by Jose Manuel Ferrer, a nice tokenizer for DAAD texts which will get the best compression. In order to use them, you must use the option -t
to provide an alternative tokens file.
If you want images on memory for the TAPE
subtarget, you must provide a path to a directory with the image files with the option -i
. The program will scan the directory for files with name like 012.DCP
, a three digit number from 0 to 255 (included) as the filename and with DCP extension.
On PLUS3
, the interpreter will load the image files from the disk instead, so the compressed image files must be present on the disk.
To show an image, you should do a call to the condAct PICTURE
with the number of the file as parameter (i.e. PICTURE 12
for image of file 012.DCP
) to preload it, and then use DISPLAY
to show it on screen. Please refer to those two condActs on the original DAAD manual. The CondAct XPICTURE
will do both of operations, while behaving like the Maluva equivalent on the error reporting mechanism.
Depending of your configuration on DRB128
of the target, characters per line and languaje, the application will use one of the 8 interpreters available, each one compiled with the corresponding configuration.
This is the characteristics of each configuration:
Target | Chars. per line | Language | Disk save | Banks reserved | Source of images |
---|---|---|---|---|---|
TAPE | 42 | English | No | None | RAM |
TAPE | 42 | Spanish | No | None | RAM |
TAPE | 32 | English | No | None | RAM |
TAPE | 32 | Spanish | No | None | RAM |
PLUS3 | 42 | English | Yes | Most of bank 6 and bank 7 | Disk |
PLUS3 | 42 | Spanish | Yes | Most of bank 6 and bank 7 | Disk |
PLUS3 | 32 | English | Yes | Most of bank 6 and bank 7 | Disk |
PLUS3 | 32 | Spanish | Yes | Most of bank 6 and bank 7 | Disk |
The versions with disk support require some RAM for buffers and control, so some banks will not be available. Take that into consideration when developing adventures for these versions.
With this tool you can compress SCR files (dumps of the Spectrum video RAM) in order to allocate then on the banks of RAM or load them from disk on the +3 interpreters.
The use is quite simple:
dcp [-f] [-m] [-l=num_lines] [-o=output] input
You must supply the path of a SCR file, and a file with the same name but with extension DCP will be created on the same path (if the -o
parameter is not used).
These are the options of this program:
- -f : This parameter will force the overwriting of the destination file if it already exists (instead of giving an error if that is the case).
- -m : This parameter will force mirroring mode, that is, the right side of the image will be discarded to save space, and the interpreter will draw the left side mirrored in its place.
- -o=OUTPUT : Use this parameter to set the path and the filename of the compressed output file.
- -l=NUM_LINES : The number of lines of the image to include. The rest, will be discarded.
If you provide an already symmetric image, the program will detect it and will enable mirroring mode by itself.
DAADMaker128
will help you create a .TAP
file from the bank files created with drc128
with a loader.
You can also add an optional loading screen in .SCR format. If you don't provide one the game won't have a loading screen but will work anyway.
This is a PHP script, so like with drb128
, you need to have PHP installed.
php daadmaker128 [options] <bank0.AD0> [bank1.AD1] ...
The bank files to include on the .TAP
file should be stated on the command line in the correct order and do not forget any file or you will get unexplained resets.
There are also the following optional parameteres to customize the loader:
- -t [TAP file] : Name of the tape file. Otherwise, the name of the first bank file will be used.
- -s [SCR file] : SCR file which will be used as loading screen.
- -n Name : Name of the program for the loader. That is, the name of the file that will appear when the loading process begins.
This php script is the equivalent of the previous one, but instead it will create two files to make a self-bootable disk for +3.
It works in the same way as DAADMaker128
:
php daadmakerPlus3 [options] <bank0.AD0> [bank1.AD1] ...
Again, the bank files should be stated on the command line in the correct order and do not forget any file.
Also, you have the following options:
- -d [path] : Destination of the loader and data filer to include on a disk image.
- -s [SCR file] : SCR file which will be used as loading screen.
This program will generate two files: a daad.bin
file (the binary data to load) and a disk
file (the loader).
After that, you can create a disk image file with those two files, including also the compressed image files needed on the adventure.
For that, you can use the program MKP3FS
from taptools. You can find compiled versions here.
ZXDAAD128 is already included on DAAD Ready, however it may have an older version.
If you want to use the bleeding edge version of the interpreter, a patch is included on the \DAAD-Ready
directory of this project.
Just copy the contents of this directory onto your DAAD Ready directory to overwrite the older files and you are ready to go!
In order to compile, Boriel's ZXBasic and a Python 3.x interpreter is required.
This is an example of the command line options to compile the interpreter for tape & english:
zxbc -o MY_INTERPRETER.BIN --optimize 4 --org 0x6002 -H 2048 -D LANG_EN ZXDAAD128.bas
Check ZXBasic documentation or the command line --help
of the compiler to know the meaning of the command line options.
Most of these options have optimal values. You can alter some of them, but be aware of the following:
- The output file must have
.BIN
extension for the backend. - The ORG address must be always 0x6002, this is imperative.
- Use the maximum level of optimization possible (4 for now).
- The heap will allocate the objects data, temporary text buffers, ramsave data and disc save data. If you get a "Game error 9" message, then you need to allocate more ram for the heap.
There also are symbols you can define with the -D
option in order to customize your interpreter:
- Language:
LANG_ES
: Use the Spanish language.LANG_EN
: Use the English language (default).
- Disk drive support:
PLUS3
: Use +3DOS.TAPE
: No disk support (default).
- Number of characters per line:
FONT32
: 32 characters per lineFONT42
: 42 characters per line (default).
The script DRB128.php
is generated by the Python script build_drb128.py
and the template file drb128_template.php
.
It will try to find the following files on the same path:
- ZXD128_TAPE_ES_C42.BIN
- ZXD128_TAPE_EN_C42.BIN
- ZXD128_TAPE_ES_C32.BIN
- ZXD128_TAPE_EN_C32.BIN
- ZXD128_PLUS3_ES_C42.BIN
- ZXD128_PLUS3_EN_C42.BIN
- ZXD128_PLUS3_ES_C32.BIN
- ZXD128_PLUS3_EN_C32.BIN
And it will replace the appropiate placeholders on the template. The naming of the files is clear enough to know which compilation options the script expects on every binary.
The other two scripts are build on the same way that DRB128. Fittingly, they are called build_DAADMaker128.py
and build_DAADMakerPlus3.py
and the template files DAADMaker128_template.php
and DAADMakerPlus3_template.php
.
These two scripts will try to find the file loader.bin
for DAADMaker128 and loaderplus3.bin
for DAADMakerPlus3, which contains the binary of the loaders. These loaders are assembled from the .asm
files with the same names on the directory src\asm
with Sjasmplus.
If you want to compile the DCP program, any standard C99 compiler will serve. You can tinker with the Makefile for your own needs.
The whole build process is done on the batch file src/build.bat
, so you can peruse it to understand the whole process.
The batch expects to have python, zxbc (Boriel´s zxbasic compiler) and sjasmplus (assembler) executables on the path.
For the moment, there is only a windows BAT file.
The original DAAD interpreter had a simple extension system to add extra functionality to the final adventure. ZXDAAD128 supports an emulation of this mechanism and is extended with support for inserting custom data in one of the banks.
The idea is to include a block of assembly language and call it from a DAAD program.
The only real free memory in the DAAD system is the area used for the database, so it seemed logical to include assembler blobs in this section. On ZXDAAD128, you can also reserve one of the banks to do this. More on the relevant section.
The database is fairly simple in layout, it consists of a header to all the data structures of the database, and after these pointers come the data tables generated by the compiler.
The first is the CTL section. Now because this is actually empty and always at the same address (after the pointers) it is the only really useful address/place for any non relocatable code.
If you include machine code anywhere else in the database you will not know its address until compile time, and as you make changes to the database its position may change, so you would have to re-assemble and compile again.
Thus the only code you should include elsewhere in the database is relocatable code/data. Machine code is just a series of numbers like any piece of data so the next problem is to get your code inserted in the database.
The compiler provides several pre-processor commands to achieve this.
- #incbin filespec: Will include a memory image file at the current position in the database.
- #defb expression {{{expression} expression}: Will include the indicated byte value(s) in the database at the current address.
- #defw expression {{{expression} expression}: Will include the indicated word value(s) in the database at the current address.
- #hex hexpair{{whitespace}{hexpair}....}: Will include direct hex data in the db. This is similar to the HEX statement of PDS.
- #dbaddr symbol: Will give symbol the current address in the database. This can be used for CALL or #defw commands.
NOTE: You aren't limited to only assembly language. You can generate new data tables as well with these commands.
In order to call an assembler routine you have various mechanisms:
These two condacts have an external vector. Which can be set using two special forms of the #incbin directive:
- #extern {filespec}
- #sfx {filespec}
If you include the filename then it will cause the given file to be included in the database (as if the command was followed by #incbin).
The vector in the interpreter is set on database load to the address of the routine. Now any EXTERN or SFX commands will be directed to your routine.
Be careful with condact EXTERN
, since it is also used with the Maluva emulation, and that one will take precedence. If a MALUVA function is recognized, it will take precedence over the call to the EXTERN code.
On ZXDAAD128, EXTERN takes always two parameters after de CondAct:
EXTERN A B
The parámeter A is the parámeter that it will be passed on to the MALUVA function, and the second is the code of the actual Maluva function to run.
In order to run your own code, B must be always bigger than 11 and not equals to 255 (at the moment of writting this document), because those values are reserved for Maluva. It is recommended to use bigger values in order to avoid conflicts with future extensions of Maluva.
When executed, your routine will be supplied with the following information on the registers:
- A - Value of first parameter (may be indirected, in which case you will get the contents of the flag in A).
- HL - Points at the Flag given by the first parameter (or the flag given by the contents of the flag if is indirected).
- DE - Points at the Object location given by the first parameter.
- IX - Address of Flag 0, 256 bytes after this are the objects.
- BC - points at the second parameter of the call.
The BC register must be preserved/updated,that is, you should advance the BC pointer and leave it pointing at the last inline parameter if you wish to include more parameters or data after de CondAct.
e.g.
EXTERN 0 6
#defw Anaddress
Could be accessed with the following routine:
LD A,(BC)
PUSH AF
INC BC
LD A,(BC)
LD E,A
INC BC
LD A,(BC)
LD D,A ; DE = Anaddress
POP AF ; A = 6
The rest of the registers can be thrased.
It is declared on DRC with the following directive:
- #int {filespec}
Any routine placed on this vector will be called 50 times a second for the entire period that the game is running.
Thus there is no command in the DAAD language to call the routine.
All registers currently active are stored on the stack to save the context, so be sure to save also the alternate registers should you use them too.
This is a special CondAct which transfers execution to the given routine.
Although flexible it can't pass any parameters directly. You need to place data in Flags before the call or use #defb/w after to include static parmeters and these can be extracted using the BC pointer.
The information on the registers is the same as EXTERN/SFX, except the BC register already points at the next CondAct or any following data, so you must leave it pointing to the first non parameter byte afterwards.
All the previous mechanism were already there in DAAD for the Spectrum, and it was designed for the 48K model alone.
Thus, custom code could only be inserted on bank 0. This can be enough for small routines but is clearly a problem when you want to include something bigger, like a music player.
To solve this, ZXDAAD128 allows to reserve one of the banks for inserting your own code. For that, you must use the option -x n
with DRB128, with n
being the number of the bank to reserve. A non sensible value for n
will give an error, so remember that banks 2 & 5 are not available, and 6 & 7 too for the Plus3 target.
When you have your custom code assembled, the resultant binary file should have extension *.ADn
, with n
being the number of the excluded bank. This file can be included among all other output bank files from DRB128 in order to be packed with any of the DAADMaker scripts.
Now, for jumping into this code, you can call the 'false CondAct' JumpToBank. This false CondAct is not recognized by DRC, so on your code you should do this to call it:
EXTERN n 100
#defb p
Where n
will be the bank to jump, which should be the same number as the bank reserved, otherwise ugly things will ensue.
The byte p
included after de CondAct is a parameter value that will be passed to the code on bank n
on the A register and pointer to the start of the flags/objects array will be set on IX.
This extern command will change the current active bank to n
and do a CALL $C000
, so the startup code is expected there.
On return, a boolean result is expected on the A register, with 0 being the false value. This value will set the result of the CondAct using the Maluva error reporting method.
You will need to have knowledge on Z80 assembler to make extensions. With the previous instructions, it should be almost enough to handle this task.
However, a handicap of this interpreter is that the database's addresses varies depending of the options of said interpreter. This is intentional in order to have the maximum memory available in each case. This could be cumbersome, so to ease things, on the /asm
directory, there is a series of ASM files with equates of the labels and addresses for each option:
- ZXD128_TAPE_ES_C42_LABELS.ASM
- ZXD128_TAPE_EN_C42_LABELS.ASM
- ZXD128_TAPE_ES_C32_LABELS.ASM
- ZXD128_TAPE_EN_C32_LABELS.ASM
- ZXD128_PLUS3_ES_C42_LABELS.ASM
- ZXD128_PLUS3_EN_C42_LABELS.ASM
- ZXD128_PLUS3_ES_C32_LABELS.ASM
- ZXD128_PLUS3_EN_C32_LABELS.ASM
- The interpreter ZXDAAD128, DAADMaker128 and DAADMakerPlus3 is (c) Cronomantic 2022. Is distributed under the GPLv3 license.
- The graphic compressor DCP is based on RCS & ZX0 which are created by Einar Saukas and is distributed under a 'BSD-3' License.
- DRB128 is a fork from Uto's original DRB, (c) Uto 2018. Is distributed under the GPLv3 license.
- Boriel's ZXBasic and its libraries have been used to build the interpreter. They are distributed under the GPLv3 license and the MIT License respectively.
- To Boriel for his wonderful compiler.
- This work has been made posible thanks to the continuous help of Uto and his marvelous DAAD Ready.
- Thanks to NataliaPC for their Msx2Daad, a great source of inspiration.
- Plus3 loader has been made thanks to the help of Sergio thEpOpE.
- Thanks to Dwalin for his help on testing the interpreter.