Z88DK - Z88 Application Building

Writing z88 application DORs is not fun, in fact almost impossible first time, then once you find a setup that works you constantly recycle the DOR for each application. I've been there, done that, given up, asked for help, got it and then recycled it, and here it is again in the kit!

One of the original aims of the Z88dk was to be able to create applications simply and easily without having to go into the messy bits of how to construct the DOR - simply give it your parameters and away it goes and creates it without you having to know whats going on.

Well, I've finally implemented it, hopefully in a non messy way within the limits the system, so read on, and find out how easy it is to create applications with the z88dk.

How to do it

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 <grin>, 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!)

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!

Extra Features

As  I  said  above,  the default application type is bad and to let OZ redraw the screen, this isn't particularly friendly as you can imagine so  you  can  turn  off  the  redraw  screen  flag by #define APP_TYPE yourself,  this  does  however leave you with a bit of a problem - you won't be able to redraw your screen, so, to this end, I've provided an facility  for  you to be able to do it - simply have a function called redrawscreen which is defined thus:

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 - NB it won't automatically turn off OZ preservation of the screen. This __APPFUNC__ call  could  be applied to other functions to enable your code to take over  error  handling for example, however that isn't supported at the moment.

For an example of this see the examples/app/dstar.c demo.

Menus and Topics

[Or  whatever  the  name  is!]  In  a fit of conscience and against my better   principles(!)   I've   implemented   topics.   They're  fully implemented  except for help pages (maybe coming soon!). This is where things get a little complicated, so listen up!

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], it's hotkey by #define TOP[num]_[item]KEY,  code  by #define TOPIC(...)CODE and its attribute (see dev notes and dor,h) by #define TOPIC(...)ATTR. But thats getting confusing,  so  have  a look at the dstar.c example in examples/app to see what I mean.

Each  topic  and each command on a topic may have a help page attached to  it, have a look at useless.c to see how it's done - remember don't specify  so  much help that it goes over the 8k limit or all hell will break loose (I'm sure you can live with that limitation!)

As  an aside, topics also have an attribute byte, which can be defined by  #define  TOPIC[num]ATTR.  All  attributes can be left out in which case they default to 0.

In order to handle menu selections you should define a function thus:

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

Where code is the command code that has been selected. If you don't do this then you can't react to your menus!

Bad, but not so bad Applications

 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!

Good Applications

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.

[Untested  feature!!]  If  you're  feeling particularly cocky, 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!!

Changing the origin of the application

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 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.]

Using the Near Heap

It is possible to use the near heap routines from an application, however, please define your heapsize thusly (or strange things will happen!):
#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 preprocessoe). 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?

Sending/receiving mail

I've  now  implemented  functions to allow passing and sending of mail between functions, the two functions are called: readmail and sendmail and  both have the prototype (char *type, far char *info, int length). Don't  worry  about the far char * type - a near pointer (eg for local variables)  is  automatically  extended  to  far  when  needed. For an example  of  the usage of readmail look at examples/app/viewer.c which is  a  simple  text  viewer  which accepts the NAME type so to use it, select a file from the filer and switch to to the Textview popdown.

NB.  To  save  heartache  from  typos (I'm a fine one to talk!) in the z88.h  header file I've defined the two OZ supported mail types, these are  FILEMAIL  and DATEMAIL, use these instead of typing the character string yourself!

The appmake Program

This  is a quick knock up of a program which will generate bank images from  an  application binary - it creates the ROM header and the front DOR  as  appropriate,  call it from the frontend using the -create-app flag  at  the same time as compile time. Internally it callocs 64k and then  loads  the appropriate addresses with the necessary information. It obtains information about the application from the z88_crt0.map and z88_crt0.sym files and as such should not be run alone.

The  algorithm  used  to  generate  a  Card  ID is based on the time() function,  I  don't  know whether this will be sufficient to guarantee exclusive-ness, but hopefully it will be sufficient - does anyone have a  better algorithm for thus - rand() is useless since it isn't random -  i.e. given the same seed it will of course generate the same random number - (It seem that picking up ROM values via the r register on the Z80 is more random!)

A few precautionary notes

Due  to  a  small  feechure  in  z80asm  which causes DEFVARS -1 to be incorrectly  resolved  when linking object files, all assembling takes place  at one time, this means that the -a flag is ineffective, and is equivalent  to  -c  when  the  -create-app  flag is supplied. So, as a result  don't  compile  to  object  files then link - you'll trash the lower memory of the Z88! [Been there, done that!]

In Summary

To create an application supply the following command line to zcc:
zcc -create-app -make-app [-reqpag=] [-zorg=] [libraries] [files]
After of course including dor.h and application.h in your application. The  two  flags  -create-app  and  -make-app  are  needed  so that the compiler  doesn't  become  to  specifically Z88 orientated - other Z80 machines  might  require  different  flags to the compiler in order to create  a  different  type  of  executable,  this way thinks are quite transparent and the zcc.cfg file doesn't become even more overloaded!!

If  you  wish  to  use  graphic  functions then you have to supply the application  graphics  library  which  is  -lgfxapp  -  this pages the graphics  page  into  the 16384 segment instead of the 49152 segment - I've  taken  the  needed routines from standard.lib and placed them in gfx[app].lib  due  to  a  label  clash  with  some of the standard.lib routines.


z88dk /z88_application.html
Last Updated 20/1/2002 dom