z88dk - The z88 Development Kit - z88 Application Generation


Home
News
Downloads
Documentation
Contact
Links
Introduction

One of the nastiest things about writing a z88 application is generating the DOR header - it's full of pointers around the place and generally looks quite nasty. In fact, usually once you've generated one, you just end up recycling it for numerous projects without actually think what it does.

z88dk offers complete DOR generation, including help texts so that generating a DOR is as simple as a few #define statements and including a couple of header files/


How to generate a DOR

For the creation of applications two header files are important: <dor.h> and <application.h> - both of these need to be included in order to construct the application DOR.

<dor.h> contains some constants for the application type and application.h uses these constants to construct the DOR, hence they should  be included in this order. However, it's not as simple as that - application.h needs some information from you such as application name etc - there are defaults, but I don't think you'd want to use those!

The information is given courtesy of #define in between the including of dor.h and application.h, you should define the following:

#define HELP1   "..."
#define HELP2 "..."
#define HELP3 "..."
#define HELP4 "..."
#define HELP5 "..."
#define HELP6 "..."

The information for the application help page, if HELP1 is not defined then the standard HELP page is used, if any of the others (HELP2 to HELP6) is not defined then "" is substituted.

#define APP_NAME "..."

The name of the application (as appears in the Index)

#define APP_KEY  '[KEY]'

The hotkey for the application.

#define APP_INFO "..."

The equivalent of *name from BASIC.

#define APP_TYPE [constants or'd together as from dor.def]

The application type, if you don't define this then the default type is BAD with OZ preserving the screen (i.e. very, very bad!)

#define APP_TYPE2 [constants]

The application type byte 2 - specifies whether caps lock should be on or not.

All of the constants above are in z80asm format - not C style, so insert Z88 control sequences in the form ".." &blah &".." and not \[blah] or you might get a bit of surprise!


Menus, Topics and Help

You are allowed up to 7 topics per application and 24 items within each topic (if you ever fit this many into an app please let me know!) you define a topic with:

#define TOPIC[num] "[name]". 

Each item under a topic is named via

#define TOPIC[num]_[item], 

its hotkey by:

#define TOP[num]_[item]KEY

its code by:

#define TOPIC(...)CODE

its attribute (see dev notes and dor. h) by:

#define TOPIC(...)ATTR. 

amd its help can be defined using:

#define TOPIC(..)HELP(1-7)

An example will probably clear up the confusion, here's the DOR #defines for examples/z88/useless.c:

#include <dor.h>

#define HELP1 "A pointless demo application made with z88dk"
#define HELP2 "Simply loops, points out menu selections and dies"
#define HELP3 "..eventually..see source for details"
#define HELP4 ""
#define HELP5 "v0.1 - 9.4.99 (djm)"
#define HELP6 ""

#define APP_INFO "Made by z88dk"
#define APP_KEY 'U'
#define APP_NAME "Useless"
#define APP_TYPE AT_Good

#define TOPIC1 "Action"
#define TOPIC1ATTR TP_Help
#define TOPIC1HELP1 "Some help for the topic"
#define TOPIC1HELP2 "And another line of help"
#define TOPIC1_1 "Eat"
#define TOPIC1_1KEY "AE"
#define TOPIC1_1CODE $81
#define TOPIC1_1ATTR MN_Help

#define TOPIC1_1HELP1 "Are you hungry?"

#define TOPIC1_2 "Drink"
#define TOPIC1_2KEY "AD"
#define TOPIC1_2CODE $82
#define TOPIC1_2ATTR MN_Help
#define TOPIC1_2HELP1 "Why yes, if you're offering, mine's a"
#define TOPIC1_2HELP2 "pint of something nice"

#define TOPIC1_3 "Sleep"
#define TOPIC1_3KEY "AS"
#define TOPIC1_3CODE $83
#define TOPIC1_3ATTR MN_Help
#define TOPIC1_3HELP1 "Yawn...."

#define TOPIC2 "Commands"
#define TOPIC2ATTR TP_Help
#define TOPIC2HELP1 "From this menu you can quit this"
#define TOPIC2HELP2 "Completely useless application"

#define TOPIC2_1 "Quit"
#define TOPIC2_1KEY "CQ"
#define TOPIC2_1CODE $84
#define TOPIC2_1ATTR MN_Help
#define TOPIC2_1HELP1 "Go on, leave me now!"


#include <application.h>

Application Types/Memory Management
If the amount of static variables used by your application is less than 8k (the minimum amount of bad memory that can be allocated) then give the flag -reqpag=num to the frontend where num is the number of 256 byte pages of bad memory required - the unused memory up to the 8k limit is given back to OZ on pre-emption. To find out what num is for your application look at the .opt/.asm file and search for DEFVARS and count the number of bytes there. Sorry, there is no foolproof way to do this automatically. For example the dstar.c program needs -reqpag=5 and rpn.c needs -reqpag=1. Remember that the startup code (maybe unnecessarily)  reserves a byte at $2000 so add 1 to your variable count before dividing by 256!

It should in theory be possible to write good applicatons with the kit - simply don't use any static variables, supply the flag -reqpag=0 to the frontend and of course set the the appropriate APP_TYPE.

I have placed all variables used by the startup code into safe workspace area to allow this facility - I reserve 50 bytes which is more than sufficient for our needs (we need 47 ATM) to allow for future expansion.

If you're feeling particularly lucky, you can write good apps that have static variables...as long as you don't have too many of them that is. You can specify the flag -safedata=[n bytes] where n is the amount of static data that you want, this space is taken from the safe workspace, which remember is also shared by the stack, mail and other such things. A suppose a safe limit would be in the region of 500 bytes or so, but experiment, if you don't have arrays of local variables and no recursion you might be able to get away with more, as the saying goes, your mileage may vary!

The default origin for an application is 49152 - this should give you plenty of space to do whatever you like, however, perhaps you want to slot it in elsewhere in an eprom - in this case use the -zorg= flag. NB. The application DOR created assumes that it is located in top bank (63) of an EPROM cartridge, so unless you have a large application don't relocate it to < 49152 [This is to ensure that the application entry point is in segment 3 - the DOR contains a dummy startup which then jumps to the real start address.]

Near Malloc routines
It is possible to to use the near heap routines with applications, however, you should should initialise the malloc pool as follows (otherwise strange things may happen!):
#include <malloc.h>

#define HPSIZE [size]
HEAPSIZE(HPSIZE)
#pragma set HEAPSIZE HPSIZE

#pragma set is a synonym for #define that gets passed through to the compiler (as opposed to being expanded out by the preprocessor). When the time comes to write the zcc_opt.def file the compiler looks for the #define HEAPSIZE divides by 256 (for the number of pages) adds it to 32 (mininum size of bad memory) and writes this value to the zcc_opt.def file, this allows the DOR to be created requesting the correct amount of bad memory.

However, this can be a bit wasteful to say the least, say you only had 50 bytes of static variables and an 8k heap, the application would request 16k of bad memory when in fact it only needs 8448 bytes (to the nearest 256 byte page), so in this case supply the parameter -reqpag=33 and only 8448 bytes will be requested - smart eh


Far Malloc Routines
The z88 application type supports the far memory allocation routines. These are detailed in a separate document.

Advanced Application Techniques

As mentioned above, the default application type is BAD to to let OZ redraw the screen. This isn't particularly friendly towards OZ (or those with little free memory within their z88) and so you can turn off the OZ screen redraw by setting #define APP_TYPE appropriately. This does, leave you with a bit of problem though, how do you redraw the screen? The solution is a function which you should implement called redrawscreen. It should be defined as follows:

void __APPFUNC__ redrawscreen() 
{
        /* Put some code here */
}

The __APPFUNC__ flag causes the flag DEFINED_[function] to be written to the zcc_opt.def file. This is then picked up by the startup code and appropriate code is generated to call this function.

To trap menu commands you should define a function called handlecmds, which should have the following prototype:

void __APPFUNC__ handlecmds(int cmd)
{
/* Put some code here */
}

The parameter cmd contains the key code that has just been selected. You should check this against the menu options that you have available.

Upon application quit it is possible to have one of your functions called, it should be defined as follows:

void __APPFUNC__ applicationquit()
{
/* Put some code here */
}

This allows you to release any resources that you are using and prevent memory leaks and similar things.


Writing z88 Packages
A package is always attached to a application eg. The Packege Package is attached to Installer and the TCP Package is attached to the application ZSock, thus building a package with z88dk is tied to building an application. As result this documentation is written with the assumption that you know how to write and
build applications with z88dk.

Constructing the Package DOR

The package DOR is the structure which informs the Package system about your package. Creating the DOR with z88dk is incredibly simple. Normally to create an application DOR you would do the following:

#include <dor.h>

/* Define application paramaters */

#include <application.h>

In a similar manner, to build a package DOR you do the following:

#include <dor.h>

/* Define package parameters */

#include <package.h>
/* Define table of pointers to functions (jump table)*/

/* Define application parameters */
#include <application.h>

The package parameters are as follows:

#define MAKE_PACKAGE 1

Say that you do want to create a package:

#define PACK_VERSION    $MMmm

The version of the package you are creating - MM=major, mm=minor

#define PACK_NAME       "[name]"

The name of the package (without the [] of course!)

#define PACK_BOOT

This should be set to either 0 or 1 depending on whether the package should autoboot or not (This is of course bypassed by holding down 'P' on reset)

#define PACKAGE_ID      $xx

The package number as allocated by Garry Lancaster

#define MAX_CALL_NUM $nn

The maximum LSB of a call to your package.

Setting up the Jump Table

The jump table can be easily defined using the following construct:

package_str blahfishcakeblah[] = {
        {Func1},
        {Func2},
        ....
        {FuncN}
};

Remember to prototype as external to the file Func1....FuncN

Finally

You have to supply 3 functions yourself to fully define the package, these are the pack_ayt, pack_bye and pack_dat functions.  Due to the complex entry and exit parameters of these functions they are best written in assembler. For more details about these calls please see Garry's documentation. These functions must either be prototyped (to allow correct linking).

At a later date these 3 functions may be writable in C if there's enough demand for the ability.



Last Modified by Dom on 12.10.2003