LPE-Z380 card v2.0 user's manual -------------------------------- * Hardware by Leonardo Padial, * EPROM v3.0 by Nestor Soriano, , and Daniel Zorita, * User's manual by Nestor Soriano This is version 1.0 of the user's manual, and it's for EPROM version 3.0 (c) 2000 Padial / Zorita / Soriano Z80 and Z380 are trademarks of Zilog Inc. INDEX ----- 1. DESCRIPTION OF THE LPE-Z380 CARD 1.1. LPE-Z380 OVERVIEW 1.2. HARDWARE DESCRIPTION. JUMPERS SETTING 1.3. LPE-Z380 ADDRESS SPACE MAP. MEMORY SPACES 1.3.1. THE MAIN RAM 1.3.2. THE BIPORT RAM 1.3.3. THE BOOT EPROM 2. LPE-Z380 PROGRAMMING REFERENCE 2.1. BIPORT MEMORY MAP 2.1.1. ROM HEADER 2.1.2. INTERCHANGE VARIABLES AREA 2.1.3. INFORMATION AREA 2.1.4. CALLs CAPTOR CODE 2.1.5. SERVER CAPTOR CODE 2.1.6. SERVER RESERVED AREA 2.1.7. BLOCK INTERCHANGE AREA 2.1.8. USER AREA 2.2. BASE FRAME MEMORY MAP 2.2.1. SYSTEM JUMP TABLE AREA 2.2.2. SAR JUMP TABLE 2.2.3. CALL NAME AREA 2.2.4. USER PROGRAM AREA 2.2.5. USER RESERVED AREA 2.2.6. TRAPPING HANDLING ROUTINE 2.2.7. ZBIOS EXTERNAL CALL CAPTOR 2.2.8. ZBIOS CODE 2.2.9. CALLs HANDLER 2.2.10. SAR CODE 2.2.11. HOOKS AREA 2.3. ZBIOS DESCRIPTION 2.3.1. CALLING PROCEDURE 2.3.2. ZBIOS ROUTINES LIST 2.4. THE HARDWARE SERVER 2.4.1. HARDWARE SERVER USING PROCEDURE 2.4.2. SAR DESCRIPTION 2.5. SYSTEM BASIC EXPANDED STATEMENTS (CALLs) 3. Z380 PROCESSOR OVERVIEW 3.1. EXTENDED REGISTER SET 3.2. WORD/LONG WORD MODE. DECODER DIRECTIVES 3.3. NATIVE AND EXTENDED MODE 4. CODE EXAMPLES 1. DESCRIPTION OF THE LPE-Z380 CARD 1.1. LPE-Z380 OVERVIEW The LPE-Z380 is an expansion card for MSX computers which contains a Zilog 80380 (or just Z380) processor witch a selectable clock speed of 3.57 Mhz or 14.318 MHz (4 x 3.57 Mhz). There is also a SIMM socket in the card for connecting the RAM memory to be used by the Z380. Various RAM sizes are supported by the card, from 1 to 128 MByte, and this memory is treated in a linear way by the Z380 processor. There are three ways in which LPE-Z380 can be used: - In the called "cartridge mode", the LPE-Z380 card is just connected to any MSX expansion slot, and it becomes a standard MSX peripheral. This means that in this mode, the MSX Z80 processor and the Z380 processor work independently of each other, so real multitasking is possible with the appropriate synchronization. In this mode, the Z380 clock frequency can be selected at 3.57 MHz or at 14.318 MHz. Normally, the 14.318 MHz speed should be selected. - The Z380 can replace the Z80 processor of the MSX. As in the cartridge mode, the LPE-Z380 card must be inserted in a expansion slot; but in the procesor replace mode, a pair of pins of the MSX Z80 must be cut and a special jumper on the card must be set appropriately. In this mode, Z380 runs at 3.57 MHz due to the MSX hardware limtations, but the effective speed of the processor is double of the Z80 because Z380 executes binary instructions in a half of the time when compared with Z80. Also, in this mode the addressing space of the Z380 is limited to 64K to maintain compatibility with original Z80 space. - When connected to a 32-bit slot expander, the LPE-Z380 card is the core of the modular MSX. The Z380 processor is 100% binary compatible with Z80, so if you are familiar with MSX assembler programming you can immediately start to make basic applications for this card after reading this manual, only a Z80 assembler is needed. On the other hand, new Z380 features (4 sets of 32 bit registers, new instructions -such 16 bit logic and more flexible register load-, linear memory...) makes programming a much easier task compared with Z80. However LPE-Z380 card presents an important limitation: when working in cartridge mode, it is not possible to directly access to MSX hardware resources (memory, ports, peripherals) from a Z380 programs; the only way to cmmunicate Z380 and MSX is the biport memory, which can be accessed by both Z380 and MSX Z80. If Z380 wants to access any MSX resource, actually MSX must do it, and then place the result (if any) in the biport memory so Z380 can recover it. To achieve this, a mechanism named "hardware server" is provided: Z380 program places a piece of Z80 code in the biport memory, then MSX executes it and places the result also in biport; there is an easy and standarized way to do it. This is explained forward with detail. This manual describes only the cartridge mode, which is nowadays the most practical way of using the LPE-Z380 card. Note: when MSX boots, LPE-Z380 boot program will show a graphic logo for approximately two seconds. The following keys are available in that moment: ESC: Makes logo to immediately disappear. SPACE: Holds logo in screen while pressed. Z: Shows the setup menu. Keep it pressed until logo disappears. In MSX2 and higher machines, holding "1" key pressed before logo appears will cause the MSX1 logo (SCREEN 2) to appear instead of the MSX2 (SCREEN 8) one. 1.2. HARDWARE DESCRIPTION. JUMPERS SETTING This section contains only an overview description of the LPE-Z380 card at hardware level, and the basic information about jumpers and SIMM memory module needed in order to connect the LPE-Z380 card to a MSX. For a more detailed description, contact Leonardo Padial. Figure 1 shows a simplified scheme of the LPE-Z380 card. Only the basic parts are shown. Please be sure to read all the information about memory modules and jumpers before connecting the card to your MSX. Also, when connecting the card to a MSX expansion slot, note that the side where the described parts are placed is the front part. Figure 1 ---------------------------- | -- ---- ---- | 1: Socket for the SIMM RAM module | || | | | | 4 5 | 2: Main EPROM | || | | | | ---- -- | 3: Secondary EPROM (optional) | || | | | | | | | 4: The Z380 processor | || ---- ---- ---- | 5: Speed/work mode jumpers | || 2 3 | 6: Memory size jumpers | || | 7,8: CPLD (programmable FLASH type) | || 10 | ----- | Contains F0-F2 internal ports | || 7 | | | and RAM refresh adapter, also | || | ----- | some bus controlling | || | 6 ----- | 9: The 1KByte biport memory | || | 8 | | | 10: Five auxiliar outputs | || ----- | 11: External primary slots outputs | || | | -- ----- | | 1 9 | | | | ----- | | 11 --- | ---------------------------- |||||||||||||||||||||| * Socket for RAM module Here you must connect a RAM module (not supplied with the card) which the Z380 processor will use to store data and program. It must be a 72 contact SIMM type, with 60 ns access time, and with a size of 1, 2, 4, 8, 16, 32, 64 or 128 MByte. * Main EPROM Contains the boot program for the Z380, which sets up a small system area with a basic BIOS (called ZBIOS) in the Z380 memory, sends a boot program to the MSX and leaves the Z380 ready to accept commands from the MSX. Detailed information about ZBIOS is provided forward. * Secondary EPROM At boot, all the Z380 addressing space is configured as byte-wide memory. This means that every memory position has a length of 8 bits, and the boot program is read from the main EPROM only. However, this state can be changed so a word-wide memory (length of memory positions is 16 bits) is assumed (a new EPROM code is needed for this). In this case, the main EPROM contains the low bytes of each memory position to be read by Z380, and the secondary EPROM contains the high bytes. Differences between byte-wide memory and word-wide memory are explained forward with detail (actually, when setting RAM to 16-bit mode, a hybrid mode is obtained: even memory positions have a size of 16 bits, and odd memory positions have a size of 8 bits). * Speed / work mode jumpers With these jumpers you select the working mode of the LPE-Z380 card: 3.57 MHz/14.318 MHz cartridge mode, or MSX processor replace mode. The way for setting the jumpers is as follows: .... 1234 ..-- 3 & 4 connected: Cartridge mode, 14.318 MHz .--. 2 & 3 connected: Cartridge or Replace mode, 3.57 MHz --.. 1 & 2 connected: Modular mode, 14.318 MHz The card is supplied in 14.318 MHz cartridge mode, and it is recommended to leave it in this state. Remember that an internal hardware modification is needed in your MSX to use the processor replace mode. Contact Leonardo Padial for more details about this. * Memory size jumpers After connecting a new memory module, you must set these jumpers appropriately according to the memory type and size, else the LPE-Z380 card will not work. Note that a bad jumpers configuration will only cause the card to be ignored by the MSX, but neither the memory module, the card nor the MSX can result damaged. There is a few types of memory modules which will not work with the LPE-Z380 card, but this does not mean that the module is damaged. Table 1 shows the jumpers configuration for the most common SIMM modules types. For other modules, request information to Leonardo Padial. Table 1 .. A18 <--- Jumpers with its names .. A20 .. A22 .. A24 .. A19 .. A21 .. A23 .. A25 Mem. size Mem. type Jumpers to set --------- --------- -------------- 1 MByte 256K*32, 2 chips type 256K*16 bits A18 & A19 2 MByte 512K*32, 4 chips type 256K*16 bits A18 & A19 4 MByte 1M*32, 8 chips type 1M*4 bits A20 & A21 8 MByte 2M*32, 16 chips type 1M*4 bits A20 & A21 16 MByte 4M*32, 8 chips type 4M*4 bits A22 & A23 32 MByte 8M*32, 16 chips type 4M*4 bits A22 & A23 64 MByte, 16M*32, 32 chips type 16M*1 bits A24 & A25 128 MByte, 64M*32, 16 chips type 16M*4 bits A24 & A25 1.3. LPE-Z380 ADDRESS SPACE MAP. MEMORY SPACES The Z380 processor has a 32-bit wide address bus. This means that the memory addressing space has a size of 4 GByte (or 4GWord, if memory is word-wide). In other words, memory addresses go from &H00000000 to &HFFFFFFFF. This addressing space is divided in four 1 G wide pages in the following way: Page 0: &H00000000 to &H3FFFFFFF Page 1: &H40000000 to &H7FFFFFFF Page 2: &H80000000 to &HBFFFFFFF Page 3: &HC0000000 to &HFFFFFFFF The LPE-Z380 card connects its three memory spaces (main RAM, biport RAM and EPROM) to this address space in the way explained below. 1.3.1. THE MAIN RAM The main RAM (the one contained in the SIMM module) is connected twice: it is connected in page 0 as byte-wide memory; and it is connected in page 3 as hybrid-wide memory (even addresses are word-wide and odd addresses are byte-wide). Beware: it is actually the same memory, but accessible from two different address spaces. Note that physically speaking, this memory is always word-wide. This means that if you have for example a 16 MByte memory module, you can use addresses from &H00000000 to &H007FFFFF. This is 8 M addresses, but every one contains one word (two bytes), so memory size is 8 MWord = 16 MByte. However you can't access the total capacity of this memory on the LPE-Z380, because full word-wide memory management is not supported. When accessing page 0, only the lower byte of every memory position is accessed, so a classical byte-wide memory is emulated. Any Z380 code must be stored and executed in page 0. In this mode, yo can access 50% of total memory size. When accessing memory through page 3, odd addresses work as in page 0 (only the low byte is accessible), but even address are fully accessible. In other words, even addresses are word-wide and odd addresses are byte-wide; this mode is called hybrid-wide mode along this manual. You can access 75% of the total memory size in this mode. You can't run programs here unless your assember assumes a hybrid-memory model for creating the code, which is very unlikely to happen; normally, accessing memory from page 3 should be done only for massive data storage. See figures 2a and 2b for a graphic representation of byte-wide and hybrid-wide memories. Figure 2a: memory size on page 0 (byte-wide) 1 byte Memory size = N bytes <----> ------ ------ ------ ------ ------ | | | | | | | | ... | | ------ ------ ------ ------ ------ 0 1 2 3 (N/2)-1 Total accessible capacity = (N/2) * 1 byte = (N/2) bytes Figure 2b: memory size on page 3 (hybrid-wide) 1 byte <----> ------------- ------ ------------- ------ ------------- ------ | : | | | | : | | | | : | ... | | ------------- ------ ------------- ------ ------------- ------ 0 1 2 3 4 (N/2)-1 Even addresses are word-wide, odd addresses are byte-wide. Total accessible capacity = (N/4) * 1 byte + (N/4) * 2 bytes = (75%)*N bytes Some simple examples will help you to understand the difference between behavior of byte-wide and word-wide memories: - Example 1: writing and reading 2 bytes in byte-wide memory LD HL,(#1234) LD (&H00001000),HL ;Accessing memory through page 0, any address the result will be: (&H00001000) <-- &H34 (&H00001001) <-- &H12 and now when reading: LD HL,(&H00001000) the result will be: L <-- (&H00001000) H <-- (&H00001001) that is, HL <- &H1234. This behavior is same as in the classical MSX byte-wide memory. - Example 2: writing/reading 2 bytes in a word-wide address LD HL,&H1234 LD (&HC0001000),HL ;Accessing memory through page 3, even address the result will be: (&H00001000) <-- &H1234 and now when reading: LD HL,(&HC0001000) the result will be: HL <-- (&H00001000) that is, HL <-- &H1234. The difference with example 1 is that now we used only one memory address to store the 16 bit data. - Example 3: writing 1 byte in word-wide memory LD A,&H12 LD (&HC0001000),A ;Accessing memory through page 3, even address the result will be: (&H00001000) <-- &H1212 When writing 1 byte in a word-wide address, the same byte is wrote in both the high part and the low part of the memory position. - Example 4: writing 2 bytes in word-wide memory, and reading through byte-wide addressing space LD HL,&H1234 LD (&HC0001000),HL ;Accessing memory through page 3, even address LD A,&H56 LD (&HC0001001),A ;Next address (odd) LD HL,(&H00001000) ;Accessing memory through page 0 the result will be: L <-- Low(&H00001000) H <-- (&H00001001) that is, HL <-- &H5634. - Example 5: writing 1 byte in byte-wide memory, and reading thorugh word-wide memory space LD A,&H12 LD (&H00001000),A ;Accessing memory through page 0, even address LD A,&H34 LD (&H00001001),A ;Next address (odd) LD HL,(&HC0001000) ;Accessing memory through page 3, even address the result will be: HL <-- &H1212 because actually, the entire memory position is always wrote, but in byte-wide mode, only the low part is visible. Due to different memory sizes, if you want to perform memory block transfers from page 3 to page 3, or between page 0 and page 3, you can't use LDIR nor LDIRW instructions. You must use a LDIW + LDI loop; this is the method used by ZBIOS in the PUT and GET routines. ZBIOS, which is the code thorugh which MSX can send various basic commands to Z380 (these commands allows the information exchange between MSX and Z380, and also to start the execution of a Z380 program and perform some configuration of the LPE-Z380), is placed at the end of the base frame (the first 64 KByte address space of main RAM) and is executed in page 0. Also, hardware server auxiliary routines (SAR), BASIC expanded statements (CALLs) handler and some hooks are placed here. Refer to section "Base frame memory map" for more details about this. 1.3.2. THE BIPORT RAM The biport RAM is the only way through which MSX and Z380 can communicate, because this is the only memory space that can be accessed by both MSX and Z380 processor. It has a size of 1 KByte. MSX see this memory in the addresses range &H4000-&H43FF of the slot where LPE-Z380 card is connected (actually also in the ranges &H0000-&H03FF, &H8000-&H83FF and &HC000-&HC3FF, but code and data placed here is prepared for run only on page 1). Z380 see this memory in page 1, at addresses &H40000000 to &H400003FF. This memory contains the BASIC expanded statements (CALLs) captor, which transfers control to the CALLs manager placed in Z380 memory; variables space used by ZBIOS to manage input/output parameters; information about Z380 slot, version number and memory size; the hardware server captor, which executes the Z80 code provided by Z380 when it need to access any MSX hardware resource; space for data block interchange between MSX and Z380; and optionally, space reserved to the user. Of course, this memory space is always byte-wide. Refer to section "Biport memory map" for more details about this memory space. 1.3.3. THE BOOT EPROM The boot EPROM is useless after system boot (all the useful code stored here is copied to main RAM and biport RAM at startup and from this moment EPROM is not used anymore), however it is left in the Z380 addressing space so it can be read by the user. It is 32 KByte long and it is placed in page 2, that is, at addresses &H80000000 to &H80007FFF. It is always byte-wide memory unless a new EPROM is used and a secondary EPROM is placed (then, both EPROMs build up a 32 KWord memory space). Note that actually it is possible to set a different memory configuration (that is, to set each memory space in a different page) by changing the value of the Z380 internal port &HF0. However, the configuration explained above is the one set up by EPROM when system boots, and should never be modified, otherwise ZBIOS and the code placed in biport will not work and LPE-Z380 card will become unusable until a reset is made. 2. LPE-Z380 PROGRAMMING REFERENCE In this chapter a basic reference of LPE-Z380 built-in programming resources is provided, so after reading it you can start making programs for the card. Note that this chapter treats LPE-Z380 card features, and NOT Z380 processor features. Some Z380 processor features are treated forward in "Z380 processor overview" chapter. First, detailed map and contents description of biport RAM and base frame of main RAM is provided. Next, detailed listing and description of ZBIOS routines can be found. Then the hardware server mechanism is explained, and detailed listing and description of SAR (Server Auxiliary Routines) is provided. How to use hooks to add new ZBIOS commands and BASIC expanded statement is detailed; and finally, how to use the built-in BASIC expanded statements is explained. 2.1. BIPORT MEMORY MAP Biport RAM, which is the only memory space visible by both the MSX and the Z380 processor, is 1KByte long and it is placed at addresses &H4000-&H43FF of the LPE-Z380 slot (when looking from the MSX) and in addresses &H40000000-&H4000003FF of Z380 main RAM (when looking from Z380). Figure 3 shows the contents map of this memory space. Figure 3 - biport RAM map ----------------- &H4000 (addresses when looking from MSX) | ROM header | |---------------| | Interchange | &H4010 | variables | | area | |---------------| | Information | &H4030 | area | |---------------| | CALLs | (STATEMENT) | captor code | |---------------| | Server | (S_JMP+1) | captor code | |---------------| --- | | (S_CODE) | | Server | | Size can be controlled with | reserved area | | SETSSPC command (0 at startup) | | | |---------------| --- | | (BLKDIR) | | Block : interchange : area | | | | |---------------| --- | | (USRDIR) | | User reserved | | Size can be controlled | area | | with BCLEAR command (0 at startup) | | | ----------------- &H43FF --- Along this chapter, some features of the hardware server and its behavior are exposed, but "The hardware server" chapter must be referred in order to obtain a detailed description of this mechanism and how to use it. 2.1.1. ROM HEADER This is a standard MSX ROM header which makes MSX to see LPE-Z380 card as if it were a 1K ROM cartridge. This allows the system to execute a initialization program at boot (the MSX boot program is suplied by Z380 boot program, both are placed in EPROM), and also to maintain code for the BASIC expanded statements (CALLs) captor in the biport memory. * STATEMENT (&H4004, 2 bytes) Indicates the start address of the CALLs captor code, which just stores the BASIC pointer in variable BASPNT on variables area and transfers control to CALLs handler in Z380 RAM. This is explained later with more detail. 2.1.2. INTERCHANGE VARIABLES AREA This is a set of variables which are used to share parameters and/or results when executing ZBIOS commands, and it is also used by SAR (Server Auxiliary Routines) as temporary location to share input/output parameters when accessing MSX resources. This is true for ZCOMM to BLKLON; variables from TRAPFDIR to BASPNT are system variables and are not used to share parameters. Below is a general description of the purpose of each variable when using ZBIOS commands. Of course, description for each individual command (in "ZBIOS description" chapter) must be referred to obtain more details. * ZCOMM (&H4010, 1 byte) Used to order Z380 to execute a ZBIOS command when sending the order from a MSX program. ZBIOS commands can also be accessed by the Z380 itself by setting the command to be called in the A register, and then doing a CALL 6. Refer to "ZBIOS description" chapter for detailed procedure of ZBIOS calls. * ZDIR_3 (&H4011, 4 bytes) Used to specify an address in the Z380 memory for ZBIOS commands EXE, PEEK, POKE, PUT and GET. * ZVAL (&H4015, 2 bytes) Used to read or write a value in ZBIOS commands PEEK and POKE, also used to specify a command parameter in other commands. * ZDIR_M (&H4017, 2 bytes) Used to specify a MSX memory address for ZBIOS commands PUT and GET. Also used in other commands. * BLON (&H4019, 2 bytes) Used to specify the block length in ZBIOS commands PUT and GET. The maximum value is (BLKLON). * TLON (&H401B, 2 bytes) Not used by ZBIOS itself, but by the CALL code for PUT and GET and by SAR (Server Auxiliary Routines). It specifies the total block length when transferring blocks with size bigger than (BLKLON). * BLKDIR (&H401D, 2 bytes) Start address of the block interchange area. It is same as (S_C_DIR) at startup, so server reserved area does not exist at all. * BLKLON (&H401F, 2 bytes) Size of the block interchange area. It is just (USRDIR) - (BLKDIR), or &H4400 - (BLKDIR) when (USRDIR) is 0. It is rounded to the nearest lower multiple of 3 to avoid problems when transferring blocks from/to hybrid-wide memory. * USRDIR (&H4021, 2 bytes) Start address of the biport user reserved area. When it is 0, it means that there is not user reserved area at all (this is the default state at system startup). * TRAPFDIR (&H4023, 2 bytes) Z380 address for jumping when an unknown Z380 instruction is fetched from memory. Since it is a 2 byte adrress, code to be jumped must be placed in the base frame. This variable is set up tp the default value &H0003 when a user program is about to being executed (jumping to this address of main RAM, the Z380 program is terminated immediately. Refer to "Base frame memory map" section for more details). * TRAPIDIR (&H4025, 2 bytes) Z380 address for jumping when an unknown Z380 instruction is supplied by an interrupt in mode 0. Since it is a 2 byte adrress, code to be jumped must be placed in the base frame. Currently Z380 card does not support mode 0 interrupts, but this address is supplied to prevent future expansions, and to support manual setting of the interrupt trapping flag. This variable is set up tp the default value &H0003 when a user program is about to being executed. * INTFLG (&H4027, 1 byte) Z380 must set this address to &HFF when generating an interupt to MSX, so MSX interrupt handling routine can distinguish a VDP interrupt from a Z380 interrupt by looking at this address. MSX must set this flag again to 0 to indicate that interrupt has been recognized. Interrupts mechanisms between MSX and LPE-Z30 are not explained in this version of user's manual. * SERVSPC (&H4028, 2 bytes) Contains the size of the server reserved area, which is 0 at system startup. To create, destroy or modify the server reserved area, the ZBIOS command SETSSPC must be used. * BASPNT (&H402A, 2 bytes) Used to store BASIC text pointer when handling a CALL command. CALL captor sets it appropriately before transferring control to CALL handler, and CALL handler must keep it up to date when doing the CALL parameters management. * Addresses &H402C to &H402F are reserved for future expansion and should not be modified. They contains always 0. 2.1.3. INFORMATION AREA This area contains various information about the LPE-Z380 card. This area is set up at boot, and it is intended to be read only from that moment (except for S_ACTION). * ZSTRING (&H4030, 9 bytes) Contains the ASCII string "LPE-Z380"+0. To search for the Z380 card, search for this string at address &H4030 of all the slots in the system. * ZVER_C (&H4039, 2 bytes) LPE-Z380 card version, one byte for primary version and another for secondary version, in BCD format. * ZVER_R (&H403B, 2 bytes) LPE-Z380 EPROM version, one byte for primary version and another for secondary version, in BCD format. * ZSLOT (&H403D, 1 byte) Slot where LPE-Z380 is placed. * ZSUB (&H403E, 1 byte) Subslot where LPE-Z380 is placed. When slot is not expanded, it is 255. * ZMEM (&H403F, 1 byte) Z380 main memory size, in Megabytes. When it is 0, size is 512 KBytes. Note that it is phisical size, so accessible size is actually 50% or 75% of this value (when accessing in byte-wide mode or in hybrid-wide mode, respectively). Also, remember that the amount of accessible memory addresses is always 50% of this value (see figures 2a and 2b in "The main RAM" section). * S_C_DIR (&H4040, 2 bytes) Start of the biport area where Z80 code to be executed must be placed when using the hardware server capability. It is same as (BLKDIR) when no server reserved area is set up (this is the default state at system startup). * S_JMP (&H4042, 3 bytes) This is a jump to the server captor. Immediately after sending to Z380 the order for executing a user program (using ZBIOS command EXE, see "ZBIOS description" section), MSX must perform a CALL S_JMP in order to pass to hardware server mode; actually, the Z380 use program will not start until the hardware server is ready to be used. * S_ACTION (&H4045, 1 byte) This variable is used to control the hardware server mechanism. It may have three different values: 0, 1 or 2. - A value of 0 means that MSX is not running the hardware server. - A value of 2 means that MSX is running the hardware server captor: it will remain waiting in an infinite loop until Z380 changes the value of S_ACTION to 0 (then the hardware server finishes and the MSX program that did the CALL S_JMP will take control again), or to 1. This is the initial state when server captor is activated by MSX with a CALL S_JMP. - A value of 1 set by Z380 makes MSX to execute the Z80 code placed in biport, at the address indicated by (S_C_DIR). When this code finishes (with RET), S_ACTION will be set again to 2 by MSX, and it will go again to the server captor mode. All other values will act as 2, but they are reserved for future expansion and should never be used. * S_ERROR (&H4046, 3 bytes) When system boots, the Z380 initialization program sets a jump to this address (via interslot call with RST 30) in the BASIC error hook (&HFFB1). S_ERROR contains a jump to a short routine in biport which just sets 0 in S_ACTION. This enables Z380 to realize that a BASIC error occurred when handling CALL parameters, and therefore the hardware server finished because MSX jumped to BASIC interpreter error handling routine instead of returning to the server captor. Normally the Z380 program should terminate itself immediately when it detects that S_ACTION has been set to 0. 2.1.4. CALLs CAPTOR CODE Z380 built-in code contains some predefined BASIC expanded statements (CALLs), and the capability of adding new user ones. The main code for handling these CALLs is placed in Z380 main RAM, but a small code is needed to be placed in biport (pointed by STATEMENT in the ROM header) so MSX will automatically execute it, in order to transfer control to the Z380 main handler code, when a CALL is made. This control transfer code is called the CALLs captor code, and performs the following operations: - Save the BASIC text execution pointer in variable BASPNT. - Execute ZBIOS command 10, which is the Z380 CALLs handler. - Jump to hardware server mode. - When Z380 code finishes, server is freed and captor execution continues. Then, the BASIC text pointer is restored from BASPNT. - ZVAL is examined to check if the CALL was actually processed. If (ZVAL)=1, it means that the statement was a Z380 built-in or user CALL, then carry is reset and control returns to BASIC program. If (ZVAL)=0, then the CALL was not a Z380 one, therefore carry is set and MSX executes the CALL handler routine of the other slots. At the end of this code is placed the destination for S_ERROR code, that is called when an error occurs while handling CALL parameters. 2.1.5. SERVER CAPTOR CODE This code, which is the destination of the jump placed at S_JMP, is the main control code for the hardware server mechanism. It will act depending of the contents of variable S_ACTION, as explained in previous section. It is mandatory for the MSX program to call the hardware server (via CALL S_JMP) immediatley after starting the execution of a Z380 user program via ZBIOS command EXE; otherwise, the Z380 program execution will never start. The hardware server captor terminates itself (RETurning to the caller) when it detects a value of 0 in S_ACTION; this value is set by ZBIOS when the Z380 user program finishes. If hardware server executes any of the BASIC interpreter auxiliary routines for handling CALL parameters (for example GETBYT or FRMVEVL) and an error occurs (usually Syntax error or Type mismatch), the BASIC error handler takes control of MSX execution, "killing" the server captor; therefore, S_ACTION would remain indefinitely with value 1, and probably the Z380 user program would hang, waiting for a value of 2 which would never be placed. To avoid this, the MSX H_ERR hook at &HFFB1 is patched at system startup, so it points to a small routine in biport which sets S_ACTION to 0. Z380 program must detect this (a value of 0 in S_ACTION when 1 or 2 is expected), and usually terminate itself with JP 3 when this happens (because if it were a normal MSX program, it anyaway had been automatically terminated). 2.1.6. SERVER RESERVED AREA This is the place where the Z80 code to be executed by MSX must be placed when using the hardware server mechanism. For example, if a Z380 program wants to read a value from MSX port &H98, it must place the following code in this area (whose start address is indicated by variable S_C_DIR): IN A,(&H98) LD (&H4011),A RET Then it must set S_ACTION to 1 so server captor starts the execution of the code, wait until S_ACTION is set again to 2 by server captor (which means that code execution finished) and recover the result from &H40000011 (remember that biport memory is &H4000-&H43FF for MSX, and &H40000000-&H400003FF for Z380). Usually, variables area (ZCOMM to BLKLON) is used for temporary storage of parameters and results when using the hardware server, however a user reserved area at the end of biport could also be used. Note that the default size for this area after system startup is 0, this means that actually the Z80 code is placed in the block interchange area. This is not a problem unless you use ZBIOS command PUT and GET: these commands use the block interchange area for temporary data storage, so they overwrite any previously existing code placed here. For this reason, if you want to use simultaneously the hardware server and the PUT/GET commands, or if you want to use the block interchange area as temporary data storage area for the Z80 code, you must previously reserve area for the Z80 code, that is, set the server reserved area size to a value of at least the size of the code. Use the ZBIOS command SETSSPC for this. ZBIOS command PUTCODE can be used to easily copy a Z80 code to server reserved area. See "ZBIOS description" chapter for more details. 2.1.7. BLOCK INTERCHANGE AREA This area is used by ZBIOS routines PUT and GET to perform data block interchange between MSX and Z380 memory. When there is no user reserved area nor server reserved area, its size is about 900 bytes. There is a variable in the variables area of biport RAM which indicates the size of this zone (see section "Interchange variables area" on the biport RAM description chapter); this variable is automatically updated when user area or server reserved area are created, erased or modified by calling ZBIOS commands BCLEAR or SETSSPC, respectively. However note that this size is always rounded to the nearest lower multiple of 3, so one or two bytes can remain unused at the end of this area. For example if there is a difference of 452 bytes between the start of the user area and the start of the block transfer area, the size of this one is actually set to 450. This rounding is done to ensure that big memory transfers (those whose block size is bigger than the whole block interchange area, so multiple transfers are needed) will work correctly even when source or destination (or both) of the transfer is in hybrid-wide memory (memory transfers can't work when the last byte to be transferred is the low byte of a word-wide memory position, for example when the start address is even and the block size is 4 bytes, see figure 2b. In this case, an extra byte is transferred; this problem will never appear if block size is multiple of 3. See PUT and GET explanation on "ZBIOS description" chapter). 2.1.8. USER AREA The user area, placed at the end of the biport RAM space, is not used at all by any of the LPE-Z380 routines, so user can use it freely and safely. It can be useful, for example, to share data with MSX by using user routines instead of ZBIOS, or for temporary parameters/results storage when using hardware server mechanism. This area does not exist after system startup, and must be explicity created by the user with the ZBIOS command BCLEAR. This command allows also to modify the area size, and even to destroy it. Refer to "ZBIOS description" chapter for details of this command. Note that the bigger the user area is, the smaller the block interchange area becomes, so big block transfers become somewhat slower. Also, note that of course the start of the user area can't be set to an address smaller that the start address of the block interchange area. If this is done, the server captor, the CALLs captor or the information area may be corrupted by the user data and LPE-Z380 can become unusable until a reset is made. Therefore, be careful when using BCLEAR command, and before using it, check where the block interchange area starts (look at the BLKDIR variable). It is always recommended to make user area as smaller as possible. 2.2. BASE FRAME MEMORY MAP A frame is a 64K main memory block which starts in a 64K boundary, that is, which covers addresses range &Hxxxx0000 to &HxxxxFFFF. The base frame is the frame 0, that is, addresses &H00000000 to &H0000FFFF. Contents of the Z380 main RAM is user defined and not set up by the LPE-Z380 built-in code, except for the base frame. A small part (about 4K in current version of EPROM) of this frame contains ZBIOS code, SAR code, and some jumps and hooks, so it can't be modified by the user; the rest remains free for the user (in fact, when Z380 is running in native mode, user programs -actually any programs- can run only in the base frame; refer to "Z380 processor overview" chapter for details about Z380 running modes). Figure 4 shows the map of the main RAM base frame. 256 bytes at the start, and about 4KBytes at the end, are reserved to the system. Figure 4 - base frame map --------------------- | JP TRAPPING | &H00000000 --------------------- | JP TERM | &H00000003 --------------------- | JP ZBIOS | &H00000006 --------------------- | DW ENDUPA | &H00000009 --------------------- | Unused | &H0000000B --------------------- | | &H00000010 | SAR jump table | | | --------------------- | Space for CALL | &H00000052 | name (CALLNAM) | --------------------- | | &H00000062 : Reserved for : : future expansion : | | --------------------- | | &H00000100 | | | UPA | | (User Program | | Area) | : : | | | | | ^ | | | | | Stack | --------------------- | | ENDUPA | User Reserved Area| | | --------------------- | Trapping handling | TRAPPING | routine | --------------------- | ZBIOS external | TERM | call captor | --------------------- | | ZBIOS | ZBIOS code | | | --------------------- | | | CALLs | | handler | | | --------------------- | | | SAR code | | | --------------------- | Hooks | &H0000FFF0 | | --------------------- &H0000FFFF 2.2.1. SYSTEM JUMP TABLE AREA 11 bytes at the start of the base frame are filled with a system jump table, as explained below: * JP TRAPPING: This jump is placed in address 0, so it will be called automatically by Z380 processor when an unknown opcode is fetched from memory in run time. Then the trapping management routine, which will redirect execution to the user trapping routine (if defined), is called. Trapping procedure will be explained forward. * JP TERM: Placed in address 3, Z380 user programs must jump here in order to terminate themselves (or do RET if the original stack is preserved, so the result is same). Actually, when doing a JP 3 control is transferred to the ZBIOS external call captor, which waits for external (coming from MSX) ZBIOS calls requests in an infinite loop (actually, one of these ZBIOS calls must be used to start the execution of a Z380 user program). This is explained forward with more detail. * JP ZBIOS: Besides of the MSX, the Z380 user program can also execute ZBIOS routines by placing the command number in A register and then calling address 6. Of course it is a nonsense to use ZBIOS commands such PEEK, POKE or EXE from a Z380 user program, buth other commands such BCLEAR, SETCALL or PUT/GET may be useful no matter of who is the caller (MSX or Z380). Refer to "ZBIOS description" section for more details. * DW ENDUPA: These two bytes indicate where the UPA (User Program Area) ends (the last usable byte is (ENDUPA)-1). After system startup this value is the same of the start address of trapping handling routine (so there is no user reserved area at all), but it can be modified in order to reserve permanent user space (in a similar way of the memory reservation performed by TSRs at the end of the TPA in MSX). See section "User reserved area" for details about how to reserve memory at the end of the UPA. 2.2.2. SAR JUMP TABLE As it was briefly explained in previous chapter, a hardware server mechanism is used when it is needed to access MSX hardware resources (memory, ports) from a Z380 user program. Basically, this mechanism consist on setting Z80 code on biport and forcing MSX to execute this code and place the results (usually a byte read from a port or MSX memory address) of the hardware access in biport (or the opposite: forcing MSX to read input data from biport and then execute code; usually the input data is wrote to a port or MSX memory address). User program can use its own Z80 code for the hardware server, as long as the usage rules which are explained in "The hardware server" chapter are followed. However, in order to make programming work easier, a set of built-in routines which do the most usual hardware access operations (such byte/block read/write in memory/ports) are available in the base frame. These routines are called SAR (Server Access Routines). For example, routine RDBYT returns in A the contents of the MSX memory address passed in HL. Of course, these routines use the hardware server with their own Z80 code. The way of accessing SAR is through a jump table which starts at address &H00000010 in base frame. Refer to "Hardware server" chapter for a complete list and description of SAR. 2.2.3. CALL NAME AREA This 16 byte area is filled with the contents of PROCNM of MSX memory, which contains the name of the expanded statement, when a CALL is made from BASIC. CALL handler determines whether the statement is a Z380 one or not, by comparing the contents of this buffer with an internal table, and any user CALLs handling code must do same. Refer to "Hooks area" chapter for details about how user CALLs can be created and installed. The space from the end of the CALL name area to &H000000FF is reserved for future expansion and should never be used. 2.2.4. USER PROGRAM AREA This area, whose first address is &H00000100 and whose last address is ENDUPA-1, is the area where the user program is placed and executed. Its contents is undefined unless you load code or data here by using the ZBIOS command PUT. Note that although this area starts in address &H00000100, it is not mandatory for your program to be placed exactly in this address. In fact, you can place your program anywhere in this area, since its execution will start when the ZBIOS command EXE is called, and this command has the program starting address as input parameter. However it is recommended to place your code as low as possible in UPA, to prevent the existence of user reserved areas (see next section). Of course you can also place your program anywhere else in the main RAM (from address &H00010000 onwards), since as said before, the contents of main RAM is completely user defined out of the base frame; however, to make code executable out of the base frame you need an assembler program specifically designed for Z380. In fact, if you have only a Z80 assembler (which is very likely to happen if we assume that you are a MSX user), you can't generate code prepared to run beyond the &HFFFF boundary. For this reason, the free area on the base frame is called "User Program Area". Note also that it is phisically impossible for Z380 processor to execute any code placed out of the base frame unless it is set to extended mode; Z380 remains always in native mode after system startup, so extended mode must be set by the user if desired. Refer to chapter "Z380 processor overview" for more details about Z380 running modes. The UPA is intended to be, as in the case of the MSX TPA, a "transient" area. This means that after the user program finishes, the next time a user program is loaded it will use the same area, erasing any previous memory contents (this behavior is same as the MSX TPA). If you want to create a "permanent" area to store any code or data, you can reserve space at the end of the UPA, by decreasing the value of ENDUPA at address &H00000009 (this method is similar to the one used by MSX TSRs to reserve memory at the end of the TPA). This is explained with more detail soon. Finally, note that the stack pointer is set to the last UPA address when a user program is about to being executed (actually, the ZBIOS captor sets the stack here when a JP 3 is done; see "ZBIOS external call captor" section), so stack causes the effective length of the UPA to be decreased. If this is a problem for you, just set the stack pointer to any address of main RAM beyond the base frame. You can always do this because contrarywise to the program counter, the Z380 stack pointer can be set to any 32 bit value regardless of the Z380 running mode. 2.2.5. USER RESERVED AREA As said before, UPA is a "transient" area, so code or data placed here is not guaranteed to remain unchanged after the user program termination. But it may occur that you want to place any data in the Z380 memory permanently, so no one apart from you can remove or modify it; for example to install a new user ZBIOS command or BASIC expanded statement. It is possible to achieve this by creating a "user reserved area" at the end of the base frame, but note that this decreases UPA size. The procedure to do this is as follows. Let's suppose you want to reserve RBYTES bytes. Then you must follow these steps: - Read the value of ENDUPA at address &H00000009. - Save this value in a variable, for example OLDUPA. - Decrease ENDUPA by RBYTES, call the result NEWUPA. - Store NEWUPA at address &H00000009. - Also, store NEWUPA in a variable, for example MYUPA. - All done: your RBYTES long reserved area starts at address NEWUPA. When you don't need the reserved area any longer, you should destroy it so UPA size is not decreased vainly. Assuming that you saved OLDUPA and MYUPA values when you created the reserved area, do the following in order to destroy your user reserved area: - Read the value of ENDUPA at address &H00000009. - Compare it with MYUPA. If it is not same, it means that other program(s) has also reserved memory after you did it, so you can't remove your reserved area at all. See figure 5. - Once ENDUPA = MYUPA condition is checked, store OLDUPA at address &H00000009. - All done: your reserved area no longer exists. Figure 5 - more than one user reserved area : : | UPA | ----------------- | 3rd program | ENDUPA | reserved area | <- Can be removed ----------------- | 2nd program | <- Can't be removed until | reserved area | 3rd program reserved area is removed ----------------- | 1st program | <- Can't be removed until | reserved area | 2nd program reserved area is removed ----------------- | | TRAPPING : : There is not any user reserved area after system startup, that is, ENDUPA = TRAPPING. In this state, UPA has its maximum size (about 60K in current version of EPROM). NOTE: LPE-Z380 built-in code does not provide any mechanism to reserve memory outside of the base frame. Any standarized external program is needed for this (similar to MEMMAN for MSX). 2.2.6. TRAPPING HANDLING ROUTINE Z380 processor provides an undefined opcodes trapping mechanism. When an undefined opcode (that is, bytes not corresponding to any Z380 machine code instruction) is fetched from memory in run time, the memory address where the unknown opcode starts is pushed on the stack, certain bit of a special control register is set, and a JP 0 is automatically done. The code placed at the base frame by LPE-Z380 supports the trapping feature. In address 0 there is a jump to a trapping handling routine. This routine, placed after the user reserved area (or after UPA), first checks for the control bit mentioned above to be set. If not, there is no trapping condition at all, and execution resumes at address 3 (so program terminates). If there is actually a trapping condition, the biport variable TRAPFDIR (which contains the starting address of a user defined trapping handling routine) is read, and execution jumps to the read address. When a user program is about to be executed, TRAPFDIR is set to &H0003 by ZBIOS code, so a trapping condition causes the program termination. The user program itself must set TRAPFDIR to the starting address of the user's trapping handling routine, if there is any. Note that TRAPFDIR is a 2 byte variable, so the user's trapping routine must be placed in the base frame. A trapping condition is also generated when an undefined opcode is supplied by an interrupting device in interrupt mode 0, then a different control bit is set. Although LPE-Z380 card does not support interrupt mode 0, an extra variable is provided for defining an user handling routine for this type of trapping: TRAPIDIR. This is to prevent future expansions of the card, and also to provide a manual trapping mechanism (for example, yo can set manually the control bit for IM0 trappings -refer to Z380 user's manual for details about how to do it- and then do a JP 0). TRAPIDIR is a 2 byte address as well as TRAFDIR. 2.2.7. ZBIOS EXTERNAL CALL CAPTOR When Z380 processor is not executing a ZBIOS command nor a user program, it executes the ZBIOS external call captor, placed after the trapping handling routine, in an infinite loop. This captor just waits for ZBIOS execution requests from the MSX (by checking that ZCOMM variable on biport has a value other than 0), and when a request is detected, it calls ZBIOS requested command and starts again to wait for a new command request. Note that the execution of a Z380 user program is nothing but a call to the ZBIOS command EXE, so when the program finishes, the ZBIOS captor must take control again. For this reason, the way of finishing a user program is a JP 3, which is the entry point of the ZBIOS captor (also a RET can be used if the original stack pointer is preserved, then the effect is the same of a JP 3). There is a way of making Z380 to do something useful apart from checking ZCOMM variable while no ZBIOS request is made: the H_WAIT hook, placed at address &H0000FFF8. In fact, actually the code of the ZBIOS captor does the following: (JP 3 jumps here) - Set the stack to the end of UPA - Set ZCOMM to 0 - Repeat in an infinite loop: - CALL H_WAIT - If ZCOMM<>0, call ZBIOS (PUSH &H00000003 : JP 6) So if you set in the hook (which initially contains only RETs) a jump to any routine (placed for example in a user reserved area), this routine will be called continuously when Z380 has nothing to do. The hook is 8 bytes long, which is more than enough for placing a jump to anywhere in main RAM. The hook will be called with the Z380 set to long word mode, and the called routine must preserve register HL. There is also available a hook which is called when a ZBIOS command is about to be executed, and another one which is called when a BASIC expanded statement execution (CALL) is requested. See "Hooks area" section. 2.2.8. ZBIOS CODE As said before, there is two ways to execute a ZBIOS command: from the MSX (by setting variables at biport and then writing command number in ZCOMM variable, so command execution request is recignized by ZBIOS captor), and from a Z380 user program (by setting variables at biport, setting the command number in A and doing CALL 6). In both cases the same ZBIOS code, placed after the ZBIOS captor, is executed (actually the jump on address 6 points directly to ZBIOS code, and the ZBIOS captor just sets the command number in A and does a CALL 6, with &H00000003 in the stack). Calling ZBIOS from a Z380 program is of course a nonsense when the called command is PEEK, POKE, IN0, OUT0 or EXE (the effect of executing ZBIOS command EXE from Z380 is the one of a CALL to the specified address); but commands like PUT, GET (when source and/or destination is hybrid-wide memory) or BCLEAR are useful regardless of the calling point (MSX program or Z380 program). See "ZBIOS description" for more details. When an undefined ZBIOS command execution is requested (that is, when ZCOMM or A contain a number not matching with any ZBIOS command), ZBIOS just does a RET; nothing special happens nor any error is returned. In current EPROM version, valid ZBIOS command identifiers are those in the range 1-14 (see "ZBIOS description" chapter). Values 15-63 are reserved for future system expansion; if you want to create new ZBIOS commands by patching the jump in address 6 as explained above, use values in the range 64-255, which is available for user expansions. Value 0 is used to indicate that ZBIOS captor is running and no ZBIOS command is being executed. User ZBIOS commands can be added by using the H_COMM hook, and normally the code for these new commands should be placed in a user reserved area in the base frame. Refer to "Hooks area" section for more details. 2.2.9. CALLS HANDLER This is the code executed when a CALL command is executed from BASIC, via execution of the ZBIOS command 10 performed by the server captor in biport. The BASIC text pointer is placed in BASPNT variable before the calls handler takes control. The first operation done by the CALLs handler is to copy the contents of PROCNM on MSX memory (that is, the command name) to CALLNAM. Then, hook H_CALL is called so first user CALL handler is executed (if there is any installed). User code for CALLs handling must recognize the name at CALLNAM (ZBIOS command COMPCALL can be used to do it easily; see "ZBIOS description" chapter) and, if it is an own command, it must be processed (BASPNT must be kept up to date) and RET must be done after setting (ZVAL) to 1. If not an own command, (ZVAL) must be left to 0 and execution must jump to the old H_CALL hook contents (saved while installation), so other existing user CALLs can be handled. When execution returns from H_CALL, system CALLs handler checks (ZVAL) value. If it is 1, it means that command was already processed by any of the user handling routines, and it returns. If not, it compares the command name with an internal table (internal commands are explained in "System BASIC expanded statements (CALLs)" section), and if any of them matches, it is processed and handler returns with (ZVAL)=1; else it returns with (ZVAL)=0. Finally, the CALL captor on biport takes control again; it restores BASIC text pointer from BASPNT and sets or resets carry flag depending of the value of (ZVAL) before returning control to BASIC call handler. Refer to section "Hooks area" for more details about how user CALLs must be created and installed. 2.2.10. SAR CODE As explained in "SAR jump table" section, various built-in routines are provided (Server Auxiliary Routines) for an easy use of the hardware server capability. User can therefore choose between the use of the hardware server with its own Z80 routines, or to just use SAR, and then there is no need of even learning how the hardware server works. The SAR main code, together with its Z80 routines, is placed after the CALLs handler. The way of accessing these routines is through a jump table which starts at address &H00000010. Detailed list and description of SAR use is provided forward in "SAR description" chapter. 2.2.11. HOOKS AREA After ZBIOS code, the hooks area is placed. By modifying these hooks (which contain only RET after system startup so they do nothing) you can take some control on the ZBIOS behavior without having to set up a whole new ZBIOS code, and you can create new BASIC expanded statements and load them on Z380 RAM. Currently there is three 8-byte long hooks (this size is more than enough to place a jump to anywhere in main RAM) available: * H_CALL at &H0000FFE8 This hook is called by the CALLs handler after setting the command name in CALLNAM area. The way of adding user CALLs for Z380 is to put the code for the new CALLs management into Z380 main RAM (an user reserved area at the end of the base frame should be created for this) and to place a jump to this code into H_CALL, which contains just RETs after system startup. Below is detailed the procedure for creating and installing user CALLs management code. An example is provided in the "Code examples" section. * To install code: - Copy the old contents of H_CALL into the code itself. - Copy the code into Z380 memory (normally, an user reserved area should be created for this, using the procedure described in "User reserved area" section). - Put a jump to the code in H_CALL. * The code must do the following (registers input and output state is undefined): - Check the command name which is placed at CALLNAM. ZBIOS command COMPCALL can be used to do it easily. - If the command is not recognized, set (ZVAL) to 0 and jump to the old H_CALL (which must have been saved when installing code) with the original stack pointer. - If the command is recognized, process it (ZBIOS command CALLPAR is very useful for processing parameters) keeping BASPNT always up to date, set (ZVAL) to 1 and RETurn with the original stack pointer. If your only method to process parameters is the use of CALLPAR command, you don't need to worry about BASPNT, because it is always updated automatically. When calling BASIC interpreter routines for CALL parameter management (such GETBYT or FRMEVL) using SAR instead of CALLPAR, mind that you must do JP 3 if you detect a value of 0 in S_ACTION, because this means that a BASIC error occurred and therefore hardware server was terminated and BASIC interpreter took control of execution on MSX. CALLPAR will automatically do a JP 3 when a BASIC error is detected. Note that H_CALL is called immediately after setting command name into CALLNAM and before any command name checking, this means that you can install your own versions of built-in CALLs (ZEXE, ZPEEK, ZPOKE, etc), although it is normally not recommended to do. * H_COMM at &H0000FFF0 This hook is called at the start of the ZBIOS code, that is, the point where execution jumps when a CALL 6 is done or when the ZBIOS captor recognizes an external ZBIOS command request. It is called with Z380 in long word mode and containing the requested command in A register, and any code executed from here must jump to the old contents of the hook (saved when code is installed, usually in a user reserved area in base frame) with the original stack pointer (no register preservation is needed apart from A, as explained next) to finish its execution. If contents of A register is preserved when returning to the hook caller, the ZBIOS captor execution will continue after the instruction which called the hook, so the requested system ZBIOS command will be executed. But if A=0 when returning, the ZBIOS captor will terminate immediately with a JP 3. Modifying this hook is the way to implement new ZBIOS user commands. The procedure is as follows: recognize the command number in A and, if it matches with your user command number, process it, set A=0 and finish. Else, keep A unmodified and finish. In both cases, jump to the old contents of the hook to finish, with the original stack pointer (do not use RET to finish even if you process the command, because maybe other installed ZBIOS extensions do global processing regardless of the command number). For example, let's suppose you want to implement a new command with code 200 (remember that only range 64-255 is reserved for the user). Then you place in &HFFF0 a jump to a code (which should be placed in a user reserved area at the end of UPA) that does the following: - If A<>200, JP FINISH. - Execute code for new command 200. - Set A=0 and JP FINISH. - FINISH: Jump to the old hook contents. * H_WAIT at &H0000FFF8 As explained in previous chapter, this hook is continuously called while Z380 is doing nothing useful, that is, while no ZBIOS command nor user program is being executed and the ZBIOS captor is waiting for an external ZBIOS command execution request. The complete execution sequence of ZBIOS captor is as follows: (JP 3 jumps here) - Set the stack to the end of UPA - Set ZCOMM to 0 - Repeat in an infinite loop: - CALL H_WAIT - If ZCOMM<>0, call ZBIOS (PUSH &H00000003 : JP 6) This hook is useful when you want to perform with Z380 any long task without completely losing control over the processor (that is, having the possibility of interrupt the task with any other user program). Then H_WAIT must contain a jump to a routine (which should be placed in a user reserved area after UPA) that performs one step of the task and then returns. This hook will be called with Z380 in long word mode. The called routine must preserve the original contents of HL register (both normal and extended portions). 2.3. ZBIOS DESCRIPTION In this section all the ZBIOS commands behavior and input/output parameters are explained. The "Interchange variables area" zone on biport is used for these parameters, see "Biport memory map" chapter for the concrete location of each one; here they will be referred by their names. 2.3.1. CALLING PROCEDURE To execute a ZBIOS command from a MSX program, enable LPE-Z380 slot on Z80 page 1 and follow this sequence: - Check that Z380 is ready to execute ZBIOS commands, that is, check that (ZCOMM) value is 0. If not 0, it means thet Z380 is already executing a ZBIOS command, so you must wait. - Set biport input parameters appropriately for the command. - Put the number of the command to be executed in (ZCOMM), so ZBIOS captor transfers control to ZBIOS code. - If the command is EXE, jump to hardware server mode via CALL S_JMP. The Z380 program execution will not start until hardware server is not ready. - (Only if command is not EXE): Now ZBIOS command is being executed by Z380. If necessary, wait until the command finishes (until (ZCOMM) is 0 again) and recover the result from the appropriate variables, or from the block interchange area. - (If command is EXE): When Z380 program finishes, hardware server terminates and MSX program continues after the CALL S_JMP. Execution of a ZBIOS command from a Z380 user program can be done in any moment by following these steps: - Set biport input parameters appropriately for the command. - Set command number in A register. - Do a CALL 6. - When ZBIOS command finishes, control will return to your program. If necessary, recover the result from the appropriate variables, or from the block interchange area. Examples of ZBIOS command execution are provided in "Code examples" chapter. 2.3.2. ZBIOS ROUTINES LIST The following is the list of ZBIOS commands with their command number. Information about modified registers is only useful when calling the command from a Z380 user program, of course. For the concrete location on biport of the referred input/output parameters, see "Biport memory map" chapter. User comands can be added by using H_COMM hook; refer to "Hooks area" section for details about this. Note that besides of the mentioned registers, all of the ZBIOS routines will return will IX modified; concretely, they will return with IX pointing to the start of biport memory, that is, IX=&H40000000. Also, they will always return with processor in word mode. Some of these commands are useful from a MSX program, other are useful only from a Z380 user program, and others are useful from both environments. "Z" is placed after command name if it is a command intended to be called from a Z380 program, "M" if it is a command intended to be called from MSX, and "M, Z" is placed if the command is useful from both environments. * Command 1: PEEK (M) Reads a byte or a word from Z380 main memory. Input: (ZDIR_3) = Address to be read Output: (ZVAL) = Data read Registers: A, DE, HL When specified address is word-wide, a 16 bit value will be returned in ZVAL. When address is byte-wide, a 8 bit value will be returned in the low part of ZVAL, and the high part will be always 0. * Command 2: POKE (M) Writes a byte or a word in Z380 main memory. Input: (ZDIR_3) = Address to be read (ZVAL) = Data to be written Registers: A, DE If the specified address is word-wide, the whole contents of ZVAL will be written. If it is byte-wide, only the low byte will be written, and the high byte will just be ignored. For example, if (ZDIR)=&H00000100 and (ZVAL)=&H1234, the result of the command execution will be: (&H00000100) <- &H34. * Command 3: EXE (M) Executes a Z380 user program. Input: (ZDIR_3) = Starting address of program in main RAM The following actions are performed before actually transferring control to the specified address so it is guaranteed that any old Z80 code will work correctly: - Registers bank 0 is selected - The extended portion of all registers in bank 0 is set to 0 - Word mode is set - Trapping addresses are set to &H0003, so by default, trapping condition will cause the program to terminate - Wait until hardware server is ready, then execute program Also, if called from MSX, since the caller is actually the ZBIOS captor, stack pointer is set to the end of UPA and return address &H00000003 is pushed on the stack. When executing this command from a Z380 program, the effect will be simply the one of a CALL to the specified address (as long as the called code finishes with RET instead of JP 3, of course), which is not very useful. Note that Z380 can't execute any code placed beyond the &H0000FFFF boundary when it is running in native mode; in this case, when an EXE command is requested, only the lower 16 bits of (ZDIR_3) will be used and the rest will be ignored and assumed to be zero. For example if (ZDIR_3)=&H12345678 and Z380 is running in native mode, the actual called address will be &H00005678. See "Z380 processor overview" chapter for more details about Z380 running modes. When the called program wants to terminate, there is two ways to do it: by RET instruction (only if the stack pointer has the same value as when the program started), or by JP 3 (no matter where the stack pointer is pointing at). Both methods are actually same, because a RET with the original stack pointer will fetch &H00000003 from the stack as return address. IMPORTANT NOTE: It is mandatory for MSX to jump to hardware server mode immediately after setting value 3 into ZCOMM, else the Z380 program will never been executed. That is, although for execute the other ZBIOS commands from the MSX it is enough with LD A,command LD (ZCOMM),A ;Optionally, wait until (ZCOMM) is 0 again for command EXE, however, the procedure is LD A,3 LD (ZCOMM),A CALL S_JMP and execution will continue after the CALL S_JMP when the Z380 program finishes and the server is freed. * Command 4: GET (M, Z) Copies a block of data from main RAM to the block interchange area in biport RAM. Input: (ZDIR_3) = Source address in main RAM (ZBLON) = Block length, maximum allowed is (BLKLON) Output: Data copied from main RAM to (BLKDIR) in biport RAM (ZDIR_3) = (ZDIR_3) + (ZBLON) Registers: AF, BC, DE, HL, DE', HL' Both byte-wide and hybrid-wide mode for source memory are supported; when it is hybrid-wide, word-wide memory addresses will be copied usign two byte-wide memory positions in biport RAM. See figure 6 (note that in word-wide addresses, low byte is stored first, so word will be transferred with the same order to byte-wide memory). Figure 6 - transferring 8 bytes from hybrid-wide RAM to byte-wide biport Contents of source ------------- ------ ------------- ------ ------------- | 1234 | | 56 | | 789A | | BC | | DEF0 | ------------- ------ ------------- ------ ------------- 0 1 2 3 4 Contents of destination after copy ------ ------ ------ ------ ------ ------ ------ ------ | 34 | | 12 | | 56 | | 9A | | 78 | | BC | | F0 | | DE | ------ ------ ------ ------ ------ ------ ------ ------ 0 1 2 3 4 5 6 7 Please note that it is not possible to read only one byte of the word-wide memory positions. For this reason, the block lengths in which the last byte to be read is the low byte of a word-wide position are not allowed (for example, returning to figure 6, it is allowed to copy 6 or 8 bytes, but not 7). These forbidden block lengths are those which matches with the formula (3*N)+1 if source address is even, and (3*N)+2 if the source is odd, for any integer value of N. Remember that this is valid only when source memory is hybrid-wide (source addresses beyond &HC0000000 boundary); any length is allowed when source memory is byte-wide. When a forbidden block length is specified in (ZBLON), this value is automatically incremented by one, so the amount of bytes actually copied to block interchange area will be (ZBLON)+1, altough (ZBLON) value will remain unchanged. There is no danger of overlapping the user reserved area with this extra byte when (ZBLON) equals (BLKLON), since value in BLKLON is always a multiple of 3, so in this case the extra byte copy will never be needed. Since (ZDIR_3) is increased by the block length after the copy, it is easy to copy big blocks (larger than (BLKLON)) with a loop of GETs. There is a detailed sample in "Code examples" chapter. Although this command is mainly for using from MSX, combined with PUT it may be useful for a Z380 program in order to do byte-wide memory to hybrid-wide memory transfers, and vice versa, using biport as intermediate buffer. * Command 5: PUT (M, Z) Copies a block of data from the block interchange area in biport RAM to main RAM. Input: (ZDIR_3) = Destination address in main RAM (ZBLON) = Block length, maximum allowed is (BLKLON) Output: Data copied from (BLKDIR) in biport RAM to main RAM (ZDIR_3) = (ZDIR_3) + (ZBLON) Registers: AF, BC, DE, HL, DE', HL' Both byte-wide and hybrid-wide mode for destination memory are supported; when it is hybrid-wide, word-wide memory addresses will be filles with the contents of two consecutive byte-wide memory positions in biport RAM. See figure 7 (note that in word-wide addresses, low byte is stored first, so word will be filled with the same order when reading from byte-wide memory). Figure 7 - transferring 8 bytes from byte-wide biport to hybrid-wide RAM Contents of source ------ ------ ------ ------ ------ ------ ------ ------ | 12 | | 34 | | 56 | | 78 | | 9A | | BC | | DE | | F0 | ------ ------ ------ ------ ------ ------ ------ ------ 0 1 2 3 4 5 6 7 Contents of destination after copy ------------- ------ ------------- ------ ------------- | 3412 | | 56 | | 9A78 | | BC | | F0DE | ------------- ------ ------------- ------ ------------- 0 1 2 3 4 Please note that it is not possible to write only one byte of the word-wide memory positions. For this reason, the block lengths in which the last byte to be wrote is the low byte of a word-wide position are not allowed (for example, returning to figure 7, it is allowed to copy 6 or 8 bytes, but not 7). These forbidden block lengths are those which matches with the formula (3*N)+1 if source address is even, and (3*N)+2 if the source is odd, for any integer value of N. Remember that this is valid only when destination memory is hybrid-wide (source addresses beyond &HC0000000 boundary); any length is allowed when source memory is byte-wide. When a forbidden block length is specified in (ZBLON), this value is automatically incremented by one, so the amount of bytes actually copied from block interchange area will be (ZBLON)+1, altough (ZBLON) value will remain unchanged; that is, the high byte of the last destination address (which is word-wide) will be filled with an aleatory byte -which is placed after useful data (the copied block) in biport RAM; see figure 8. When (ZBLON) = (BLKLON), this extra byte copy is never needed, since (BLKLON) is always a multiple of 3. Figure 8 - transferring 4 bytes from biport to an even address of hybrid-wide RAM (so it is forbidden length) Contents of source ------ ------ ------ ------ ------ | 12 | | 34 | | 56 | | 78 | | xx | (xx = unknown data) ------ ------ ------ ------ ------ 0 1 2 3 4 Contents of destination after copy (actually 5 bytes are copied) ------------- ------ ------------- | 3412 | | 56 | | xx78 | ------------- ------ ------------- 0 1 2 Since (ZDIR_3) is increased by the block length after the copy, it is easy to copy big blocks (larger than (BLKLON)) with a loop of PUTs. There is a detailed sample in "Code examples" chapter. Although this command is mainly for using from MSX, combined with GET it may be useful for a Z380 program in order to do byte-wide memory to hybrid-wide memory transfers, and vice versa, using biport as intermediate buffer. * Command 6: OUT0 (M) Writes a data in a Z380 internal port. Input: (ZDIR_3) = Port number (1 byte) (ZVAL) = Data to write (2 bytes) Registers: A, C, HL This command is intended to be used only by advanced users. Please be very careful when using it, because writing wrong data could result in a damage on the card. Normal users should never need to change Z380 internal data ports at all. * Command 7: IN0 (M) Reads a data from a Z380 internal port. Input: (ZDIR_3) = Port number (1 byte) Output: (ZVAL) = Data read (2 bytes) Registers: A, C, HL This command is intended to be used only by advanced users. Normal users should never need to request information about Z380 internal ports. * Command 8: BCLEAR (M, Z) Sets the starting address of user reserved area in biport. Input: (ZDIR_M) = New starting address of user reserved area in biport Minimum allowed is (BLKDIR)+3 Maximum allowed is &H43FF If 0, user reserved area is removed Output: (USRDIR) = New starting address of user reserved area in biport (BLKLON) = (USRDIR) - (BLKDIR), rounded to the nearest lower multiple of 3 Registers: AF, DE, HL With this command you can create, modify or destroy a user reserved area in biport; the user reserved area will not be modified at all by Z380 system code, so you can use it freely and safely. This area starts in the address you specify in (ZDIR_M), which is set to (USRDIR), and ends at the last address of biport RAM, that is, &H43FF. If you specify 0 as starting address, the user reserved area will be destroyed, and (USRDIR) will be set with value 0; this is the default state (the one after system startup). Besides of setting (USRDIR) appropriately, this command also updates (BLKLON), so it contains the new size of the block interchange area (modifying user reserved area size implies also the block interchange area size to be modified; see figure 3 in "Biport memory map" section). Remember that this size is rounded to the nearest lower multiple of 3 to avoid problems when performing block transfers to/from hybrid-wide memory. The lower allowed address for the start of user reserved area is (BLKDIR)+3, so in this case the block interchange area has its smaller possible size (3 bytes). If you set an address below this one, no error will be returned, but system will become unstable and probably you will lose control over the card after a new ZBIOS call is made, so a reset will be needed. You can specify an address in the range &H0000-&H03FF as well as in the range &H4000-&H43FF in (ZDIR_M), since actually only the 10 lower bits of the specified address will be taken from (ZDIR_M), and the rest will be set appropriately by ZBIOS code. * Command 9: ZMODE (M) Gets information about Z380 current running mode, or sets Z380 to extended mode. Input: (ZVAL) = 0 to get current Z380 running mode (ZVAL) = &HFF to set Z380 to extended mode Output (when getting mode): (ZVAL) = 0 if Z380 is currently running in native mode (ZVAL) = &HFF if Z380 is currently running in extended mode Registers: AF, BC, HL Use this command to get the Z380 current running mode or to set it to extended mode from the MSX (you can use also this command from a Z380 program, but it is easier to use the new Z380 instructions to do this). Remember that once extended mode is set, native mode can't be restored unless a reset is made (see "Z380 processor overview" chapter for more details about Z380 running modes). * Command 10: CALLHAND This is an internal system command and must never be used by the user programs. It is used by CALLs captor in biport for transferring control to CALLs handling code in Z380 RAM. It acts like EXE, but using always the CALLs handler address. Hardware server mode is automatically set by CALLs captor after calling this command, so CALL name and parameters processing is possible. * Command 11: SETSSPC (Z) Set the size of the server reserved area. Input: (ZVAL) = New size of the server reserved area (2 bytes) With 0 destroys the user reserved area With -1 (&HFFFF) sets the maximum possible size (then the size of the block interchange area is 3 bytes) Output: (BLKDIR) = (BLKDIR) + (ZVAL) (SERVSPC)= (ZVAL) (BLKLON) = (USRDIR) - (BLKDIR), rounded to the nearest lower multiple of 3 Registers: AF, BC, DE, HL As explained in previous chapter, the default biport state after system startup implies that when using the hardware server capability, the address where the Z80 code is placed and the starting address of the block interchange area are the same. This is normally not a problem if you do not need to use the block interchange area while a Z80 code is being executed. However in some cases this state is undesirable. For example let's suppose that you put a Z80 code to be frequently used by hardware server. If after using this code you use the PUT or GET commands, the previous Z80 code is overwritten with the transferred data, so before using it again you must copy it again to biport. Another example is the use of the block interchange area as temporary data storage area for the code in biport. To solve this problem, the starting address of the block interchange area can be set to a higher address in biport. Then the space remaining between the address where the Z80 code is placed and the new start address of the block interchange area is called the server reserved area, and any Z80 code placed here will remain unmodified until a new code is placed by the user, manually or using the ZBIOS command PUTCODE. The SETSSPC command sets up the user reserved area with the specified size (or destroys it if a size of 0 is specified), and updates appropriately all biport variables referring to block interchange area. To make the server reserved area as big as possible (then the block interchange area becomes as small as possible, that is, just 3 bytes long), use value -1 (&HFFFF) as size. See figure 3 to understand where the server reserved area and the block interchange area are exactly placed in biport. * Command 12: PUTCODE (Z) Copies a Z80 code to server reserved area in biport. Z80 code must be stored in byte-wide memory. Input: (ZDIR_3) = Address on Z380 RAM where Z80 code to be copied is placed (format: 2 bytes with length, followed by raw code) Registers: AF, BC, DE, HL Before using the hardware server capability, the Z80 code to be executed must be copied to the server reserved area. This can of course be done by just reading the starting address of the server reserved area from S_C_DIR variable on biport, and then copying the code to that address with a LDIR. However ZBIOS command PUTCODE is provided to do this task somewhat easier. The way of using this command is as follows: store the Z80 code preceeded by a word indicating its length, put the address of this length word in (ZDIR_3), and execute PUTCODE command. For example, returning to the example in which MSX port &H98 is read, to copy it to biport using PUTCODE do the following: LD HL,Z80COD LD (ZDIR_3),HL LD A,12 ;PUTCODE identifier CALL 6 ;ZBIOS entry ... Z80COD: DW END - START START: IN A,(&H98) LD (&H4011),A RET END: ; * Command 13: COMPCALL (Z) Compares the CALL command name in CALLNAM (&H00000052) with a user command names table. Input: (ZDIR_3) = Address of a table with user command names, in the following format: db NAME1,0 db NAME2,0 ... db LASTNAME,0,0 Output: (ZVAL+1) = If the command in CALLNAM matches with any of the commands in the user table: order number of the command in the table, starting with 1. If none of the user commands matches with the one in CALLNAM: 0. Registers: AF, BC, DE, HL This ZBIOS command is provided to help in the management of user BASIC expanded statements (CALLs). It compares the name of the executed CALL, which is assumed to be stored in CALLNAM, with a table of command names provided by the user. The format of this table is as follows: the names, all uppercased, are separed by a 0 character; and the last name is followed by two 0 characters. If the executed command is one of the user table commands, its table order number is returned in (ZVAL+1); otherwise, 0 is returned. The maximum number of command names in a table is 255. For example, let's suppose a user code which manages commands COMONE, COMTWO and COMTHREE. Then, the way of recognizing the command name is as follows: LD HL,TABLE LD (ZDIR_3),HL LD A,13 ;COMPCALL number CALL 6 ;ZBIOS entry ... TABLE: DB "COMONE",0 DB "COMTWO",0 DB "COMTHREE",0 DB 0 ;End of table mark The result will be (ZVAL+1)=1, 2 or 3 when the executed command is COMONE, COMTWO or COMTHREE, respectively. If none of these commands was actually executed, (ZVAL+1)=0. * Command 14: CALLPAR (Z) Extracts a parameter of the executed BASIC expanded statement and updates the BASIC text pointer, BASPNT. Input: (BASPNT) = Current BASIC text pointer (not used on operation 8) (ZVAL) = Requested operation: 1: Extract "(" character 2: Extract ")" character 3: Extract "," character 4: Extract 1 byte integer (uses GETBYT) 5: Extract 2 byte integer (uses FRMQNT) 6: Obtain the storage address for a variable (uses PTRGET) 7: Evaluate an expression in text (uses FRMVEL) 8: Register a string (uses FRESTR) Output: (BASPNT) updated (pointing to next item in BASIC text) (Not modified for operation 8) (ZVAL) depends on the requested operation: For 4 and 5: Extracted value, always 2 bytes (High byte is always 0 for operation 4) For 6: Variable storage address on MSX memory For 7: Type of the extracted value (2, 3, 4 or 8) (the extracted value is stored in DAC, address &HF7F6 of MSX memory) For 8: MSX address where string descriptor is stored In case of BASIC error, does not return (terminates with JP 3). Registers: AF, BC, DE, HL, AF', DE', HL', IX' Corrupts (ZDIR_3), (ZDIR_M), (ZBLON) and (ZTLON) This ZBIOS command is provided to help in the management of user BASIC expanded statements (CALLs). Usually, a user CALL will have the form CALL NAME(param1,param2,...,paramN), where parameters are either input numeric or string data, or BASIC variable names for output data storage. The way of processing these parameters is the use of the BASIC interpreter built-in routines GETBYT, FRMQNT, PTRGET, FRMEVL and FRESTR. A MSX program can call these routines directly, but from a Z380 CALLs handler, it is necessary to use the hardware server fot this. CALLPAR command makes this work somewhat easier. When user CALL handler is executed, the first operation is to recognize the command name (normally COMPCALL should be used). After this, the command processing may start; note that the initial state is the BASIC text pointer (stored at BASPNT) pointing after the command name, that is, the initial "(". Parameters must be extracted sequentially, minding that after every CALLPAR execution the BASIC text pointer (BASPNT) is updated so it points to the next item (except for operation 8, this is explained forward). Also, mind that in case of a BASIC error when extracting any parameter (usually Syntax error or Illegal function call), the whole handler program will be automatically terminated via JP 3. Operations 1, 2 and 3 just extract one character from text and check that it matches with the requested character. If so, nothing special happens apart from the update of BASPNT, which now points to the next item on text; else, a Syntax error is generated and program is terminated via JP 3. Operations 4 and 5 extracts the 1-byte or 2-byte value from text, respectively. Normally, input parameters for a CALL are integer numbers, so it is enough to use these operations to extract it; otherwise, operation 7 must be used, so any data type can be extracted. Operation 6 assumes that parameter is a variable name and obtains its storage address in MSX memory (note that SUBFLG is set to 0 before calling PTRGET so it is assumed to be a single variable rather than an array). When you want to set this variable to any value, just write the value on the obtained MSX memory address using the hardware server (manually or via SAR). If variable is of integer type, the raw integer value can be directly wrote on the obtained address; else, data type conversion may be needed of course. Operation 7 evaluates any kind of expression in BASIC text, and returns its type (2 for integer, 3 for string, 4/8 for simple/double precision real), which of course remains also stored in VALTYP on MSX memory. The result of the expression itself is not returned, and remains into DAC (&HF7F6) in MSX memory. You must then use the hardware server to read it (manually or via SAR). Operation 8 is the only one which does not use BASPNT, its only input parameters are VALTYP and DAC in MSX memory. Normally, it is used immediately after operation 7 when the extracted parameter is a string. For example, let's suppose a command such CALL ADDINT(int1,int2,var%). This command adds the integer values "int1" (whis is supposed to be 1 byte long) and "int2" (which is supposed to be 2 byte long) and stores result in variable "var" (which is supposed to be of integer type). Then the code for this command handling must perform the following operations sequence after recognizing the command name: - Extract initial "(" with operation 1. - Extract "int1" with operation 4. - Extract "," after "int1" with operation 3. - Extract "int2" with operation 5. - Extract "," after "int2" with operation 3. - Extract "var" address with operation 6, call it "add". - Extract final ")" with operation 2. - Make operation res=int1+int2. - Store "res" value into "add" on MSX memory, using the hardware server. A detailed example is provided in "Code examples" chapter. Remember that if you want to perform the parameters management manually (callig BASIC interpreter routines via hardware server instead of using CALLPAR) you must always keep BASPNT up to date. For example, to extract a 1-byte parameter: LD HL,(BASPNT) ;Current BASIC text pointer CALL EXEMS2 ;SAR routine (see "SAR description" section) DW GETBYT ;Calls GETBYT on BASIC interpreter LD (BASPNT),HL ;Updates pointer For details about BASIC expanded statements management and BASIC interpreter routines, refer to chapter 2 of MSX2 Technical Handbook, available at http://konamiman.msx.tni.nl * Commands 15-63: Reserved for future system expansion. * Commands 64-255: Available for user commands implementation (see "Hooks area" section to know how to do this). 2.4. THE HARDWARE SERVER On LPE-Z380 card the Z380 processor is actually not connected to the MSX slot bus: the only MSX part which the Z380 can see is the shared biport memory. This means that Z380 can't access to MSX hardware resources, that is, memory and ports (and therefore any integrated or external peripheral: VDP, disk, sound devices...) For this reason a software mechanism is implemented in LPE-Z380 that makes possible to access MSX hardware resources from a Z380 user program: the hardware server. It basically consists on the following: after requesting a Z380 user program execution, MSX jumps to hardware server mode (refer to EXE command description on "ZBIOS description" chapter for details about this), in which it waits for hardware access requests from Z380. These requests have the form of a Z80 code, containing the requested hardware access operation (usually a LD or IN/OUT), which is placed in biport memory; MSX executes this code, places any output result in biport so Z380 can recover it, and returns to the waiting state for accepting new requests. When Z380 program finishes, the hardare server is freed so MSX can continue the execution of the previous program. There is two ways of accessing hardware server from a Z380 program. In the "low-level" way, the user places its own Z80 code in biport, forces MSX to execute it and recovers any existing output result when MSX finishes that Z80 code execution. The procedure for doing this is explained in the next section. The "high-level" way is the use of SAR (Server Auxiliary Routines), which are a set of built-in routines for doing the most usual hardware access operations in an easy way: using these routines, you must not worry about the hardware server utilization procedure nor about any Z80 code (of course these routines use their own Z80 codes). For example a simple CALL MRBYTE will return in A the contents of the MSX memory address indicated in HL. All SAR are listed and described in "SAR description" section. SAR is the easier way to access MSX hardware from a Z380 program, however for some applications it may not be the optimal way, basically for two reasons: - All SAR use the hardware server in a syncrhonized way: after the routine is called, the Z380 program execution will not continue until the Z80 code execution finishes. This is of course necessary when an output result is needed, but in the case of slow routines which do not generate any output result (for example sending a large data block from MSX memory to a MSX port), Z380 could continue its execution (as far as it does not need other MSX hardware access of course) while MSX is executing the server Z80 code, so global program speed is improved. This is not possible with SAR. - All SAR use an own Z80 code, and this code is copied to biport every time the routine is called. Actually, if the same hardware access operation is made several times, the appropriate Z80 code may be left in biport and used as many times as necessary, so program speed is also slightly improved; but again, this is not possible with SAR. So summarising, if execution speed is not a critical factor in your Z380 program and if you do not need any sophisticated hardware access algorythm, use always SAR to access MSX hardware, since it will make your program development easier. Otherwise, use the hardware server mechanism "manually" with your own Z80 code, in the way explained in the next chapter. 2.4.1. HARDWARE SERVER USING PROCEDURE To use the hardware server mechanism manually, with an own Z80 code, the following procedure must be followed: 1) Check the value of variable S_ACTION (&H40000045) on biport: - A value of 2 means that the server is ready to accept new Z80 code, so you can proceed to next step. - A value of 1 indicates that the server is busy with the execution of a previous Z80 code, so it can't be used yet. Wait until S_ACTION value changes to 2 or to 0. - A value of 0 means that the server has been freed, so it can't be used anymore in the current Z380 program. Normally this indicates an error condition, so Z380 program should immediately terminate itself via JP 3. 2) Copy the Z80 code to the server reserved area. There is two different ways to do this: - Obtain the server reserved area starting address from S_C_DIR variable (&H40000040), and copy the Z80 code to this area with a LDIR. - Or use the ZBIOS command PUTCODE (see "ZBIOS description" chapter for details). 3) Set S_ACTION variable to 1, so MSX starts the execution of your Z80 code. Now MSX is executing the Z80 code. If you do not expect any output results, your Z380 program can continue its normal execution, since Z80 and Z380 executions are independent of each other. Otherwise, proceed to next step. 4) Wait until S_ACTION takes a value of 2 again, this means that the Z80 code execution finished (again, if you read a 0, you should terminate program). 5) Assuming that the Z80 code put any output data in any biport address, recover it and all is done: the hardware server is ready to accept new requests. As example, let's use again the code that reads a byte from MSX port &H98. The complete procedure to use it is the following one (a Z80 assembler is assumed; refer to "Z380 processor overview" chapter for details about DDIR instructions): ZDIR_3: EQU &H0011 S_C_DIR: EQU &H0040 S_ACTION: EQU &H0045 PUTCODE: EQU 12 ZBIOS: EQU &H0006 ;1) Wait until the server is ready SWAIT: DDIR IW LD A,(S_ACTION) DW &H4000 OR A JP Z,3 ;If 0: terminate CP 1 JR Z,SWAIT ;If 1: wait ;2) Copy Z80 code to server reserved area using PUTCODE DDIR LW ;We assume that Z80 code is placed in base frame, LD HL,CODE ;so we load HLz with 0 (CODE is a 16 bit address) DDIR IW,LW LD (ZDIR_3),HL DW &H4000 LD A,PUTCODE CALL ZBIOS ;3) Start code execution LD A,1 DDIR IW LD (S_ACTION),A DW &H4000 ;4) Wait until execution finishes SWEXE: DDIR IW LD A,(S_ACTION) DW &H4000 ;This time we do not expect 0 CP 1 ;because our code can't generate BASIC error. JR Z,SWEXE ;If 1: wait ;5) Recover result and process it DDIR IW LD A,(&H0011) DW &H4000 JP PROCESS ;--- This is the Z80 code CODE: DW END - START START: IN A,(&H98) LD (&H4011),A RET END: ; The Z80 code must be of course relocatable, and finish itself with RET. Z80 registers input state is undefined, except for the index registers. IX will contain the starting address of the server reserved area, so the code has a reference of where himself is placed. IY will contain the starting address of the block interchange area, so the code knows where to put any temporary data (assuming of course that a server reserved area has been previously created via SETSSPC command, otherwise IY will point also to the code itself. Refer to SETSSPC command description on "ZBIOS description" chapter, and look also to "Biport memory map" chapter). Note that biport memory is very slow, so it your Z80 code execution speed is critical (for example when using the PCM A/D converter of Turbo-R to digitize sound), it is better to copy the Z80 code to MSX memory (SAR may be used for this, see next chapter), say that we use address "ADD", and then use a simple JP ADD as server Z80 code. If a value of 0 is read from S_ACTION, it means that MSX is not running the hardware server, and there is no way to recapture it from the current Z380 program. Normally this situation is reached when calling any BASIC interpreter routine (GETBYT, FRMEVL, etc) in order to handle input parameters of a BASIC expanded statement (CALL): if any of these routines generates a BASIC error (so MSX loses control over the hardware server), S_ACTION is automatically set to 0, and the Z380 program (the CALL handler) should then terminate itself immediately via JP 3, as if it were a normal MSX program for CALL handling (see CALLPAR command description on "ZBIOS description" chapter for more details). Of course a Z380 program can free the server itself by setting S_ACTION to 0 manually, but this is normally not recommended to do. Normally, variables area (ZDIR_3 to ZTLON) shall be used by Z380 program and Z80 code to share input and output data. However, a user reserved area at the end of biport could also be used of course (see "Biport memory map" section and BCLEAR command description on "ZBIOS description" chapter). 2.4.2. SAR DESCRIPTION In this section all the Server Auxiliary Routines (SAR) are listed and described. SAR allows you to access MSX hardware in an easy way, without having to worry about the hardware server mechanism behavior nor to use any specific Z80 code. Refer to "The hardware server" section for some remarks about the limitations of using SAR instead of your own Z80 code. SAR are accessible thorough a jump table which starts at address &H00000010. There are 11 routines, and each one has two versions. In the first version, input parameters are passed thorugh registers, as usual in any kind of routines. In the second version, an inline format is used to pass input parameters to the routine, that is, these parameters must follow the routine call instruction via DB or DW. 11 second version jumps follow the 11 first version jumps. When accessing MSX memory via SAR, note that no slot nor segment switching is performed, so the current slot/segment for the appropriate Z80 page is accessed. Therefore, when accessing MSX addresses &H4000 to &H7FFF, it is actually biport memory what you will access, because the LPE-Z380 slot is always connected on MSX page 1 (so MSX can run the hardware server) when a Z380 user program is being executed. To access other slots, use MSX BIOS routine RDSLT/WRSLT through EXEMSX or EXEMS2. Below is the SAR list and description, together with their location on the jump table. Note that all of these routines modifies AF, BC, DE, HL and IX (they return results or are corrupted); some routines modifies also other registers, this is detailed in the "Modifies:" field. Exceptions to this rule are indicated in the "Preserves:" field. SAR uses variables area (ZDIR_3 to ZTLON) for temporary data storage; the "Variables modified:" field indicates the variables which are corrupted for each routine. - Registers input parameters versions: * MRBYTE (&H0010) Reads a byte from MSX memory. Input: HL = Address to be read Output: A = Read byte Modifies: AF, BC, DE, IX Preserves: HL Variables modified: ZVAL * MRWORD (&H0013) Reads a word (2 bytes) from MSX memory. Input: HL = Address to be read Output: DE = Read word Modifies: AF, BC, DE, IX Preserves: HL Variables modified: ZVAL * MWBYTE (&H0016) Writes a byte to MSX memory. Input: HL = Address to be written A = Byte to write Output: none Modifies: AF, BC, DE, IX Preserves: HL Variables modified: none * MWWORD (&H0019) Writes a word (2 bytes) to MSX memory. Input: HL = Address to be written DE = Word to write Output: none Modifies: AF, BC, DE, IX Preserves: HL Variables modified: none * MRPORT (&H001C) Reads a byte from a MSX port. Input: C = Port to be read Output: A = Read byte Modifies: AF, B, DE, HL, IX Preserves: C Variables modified: ZVAL * MWPORT (&H001F) Writes a byte to a MSX port. Input: C = Port to be written A = Byte to write Output: none Modifies: AF, B, DE, HL, IX Preserves: C Variables modified: none * EXEMSX (&H0022) Makes MSX to execute a routine placed on its memory. Input: AF, BC, DE, HL, IX, IY (low word): Passed to the called routine HLz = Address of the routine Output: AF, BC, DE, HL, IX, IY (low word): Returned from the called routine BCz, DEz, IXz, IYz: Set to 0 Modifies: AF, BC, DE, HL, IX, IY Preserves: HLz Variables modified: ZDIR_3, ZVAL, ZDIR_M, ZBLON, ZTLON * ZTOM (&H0025) Transfers a data block from Z380 memory to MSX memory. Hybrid-wide memory is supported. Input: HL = Z380 memory source address (32 bits) DE = MSX memory destination address (16 bits) BC = Block length Output: HL = HL+BC DE = DE+BC Modifies: AF, BC, DE, HL, IX Variables modified: ZDIR_3, ZVAL, ZDIR_M, ZBLON, ZTLON * MTOZ (&H0028) Transfers a data block from MSX memory to Z380 memory. Hybrid-wide memory is supported. Input: HL = MSX memory source address (16 bits) DE = Z380 memory destination address (32 bits) BC = Block length Output: HL = HL+BC DE = DE+BC Modifies: AF, BC, DE, HL, IX Variables modified: ZDIR_3, ZVAL, ZDIR_M, ZBLON, ZTLON * MTOP (&H002B) Transfers a data block from MSX memory to a MSX port. Input: HL = MSX memory source address C = Destination MSX port DE = Block length Output: HL = HL+DE Modifies: AF, BC, DE, HL, IX Variables modified: ZDIR_M * PTOM (&H002E) Transfers a data block from a MSX port to MSX memory. Input: C = Source MSX port HL = MSX memory destination address DE = Block length Output: HL = HL+DE Modifies: AF, BC, DE, HL, IX Variables modified: ZDIR_M - Inline input parameters versions: * MRBYT2 (&H0031) Reads a byte from MSX memory. Usage: CALL MRBYT2 DW Address Output: A = Read byte Modifies: AF, BC, DE, HL, IX Variables modified: ZVAL * MRWOR2 (&H0034) Reads a word (2 bytes) from MSX memory. Usage: CALL MRWOR2 DW Address Output: DE = Read word Modifies: AF, BC, DE, HL, IX Variables modified: ZVAL * MWBYT2 (&H0037) Writes a byte to MSX memory. Input: A = Byte to write Usage: CALL MWBYT2 DW Address Output: none Modifies: AF, BC, DE, HL, IX Variables modified: none * MWWOR2 (&H003A) Writes a word (2 bytes) to MSX memory. Input: DE = Word to write Usage: CALL MWWOR2 DW Address Output: none Modifies: AF, BC, DE, HL, IX, DE' Variables modified: none * MRPOR2 (&H003D) Reads a byte from a MSX port. Usage: CALL MRPOR2 DB Port Output: A = Read byte Modifies: AF, BC, DE, HL, IX Variables modified: ZVAL * MWPOR2 (&H0040) Writes a byte to a MSX port. Input: A = Byte to write Usage: CALL MWPOR2 DB Port Output: none Modifies: AF, BC, DE, HL, IX Variables modified: none * EXEMS2 (&H0043) Makes MSX to execute a routine placed on its memory. Input: AF, BC, DE, HL, IX, IY (low word): Passed to the called routine Usage: CALL EXEMS2 DW Routine address Output: AF, BC, DE, HL, IX, IY (low word): Returned from the called routine BCz, DEz, IXz, IYz: Set to 0 HLz corrupted Modifies: AF, BC, DE, HL, IX, IY, AF', DE', HL', IX' Variables modified: ZDIR_3, ZVAL, ZDIR_M, ZBLON, ZTLON * ZTOM2 (&H0046) Transfers a data block from Z380 memory to MSX memory. Hybrid-wide memory is supported. Usage: CALL ZTOM2 DW Z380 source address (low word) DW Z380 source address (high word) DW MSX destination DW Block length Output: none Modifies: AF, BC, DE, HL, IX, HL', BC', DE' Variables modified: ZDIR_3, ZVAL, ZDIR_M, ZBLON, ZTLON * MTOZ2 (&H0049) Transfers a data block from MSX memory to Z380 memory. Hybrid-wide memory is supported. Usage: CALL MTOZ2 DW MSX source address DW Z380 destination address (low word) DW Z380 destination address (high word) DW Block length Output: none Modifies: AF, BC, DE, HL, IX, HL', DE', BC' Variables modified: ZDIR_3, ZVAL, ZDIR_M, ZBLON, ZTLON * MTOP2 (&H0049) Transfers a data block from MSX memory to a MSX port. Usage: CALL MTOP2 DW MSX source address DB MSX destination port DW Block length Output: none Modifies: AF, BC, DE, HL, IX, HL' Variables modified: ZDIR_M * PTOM2 (&H004F) Transfers a data block from a MSX port to MSX memory. Usage: CALL MTOP2 DB MSX source port DW MSX destination address DW Block length Output: none Modifies: AF, BC, DE, HL, IX, HL' Variables modified: ZDIR_M 2.5. SYSTEM BASIC EXPANDED STATEMENTS (CALLs) As explained in previous chapters, from the MSX view point the LPE-Z380 card is simply a RAM cartridge with 1KByte size. This allows the use of some BASIC expanded statements (CALLs) to execute some ZBIOS commands and to access biport RAM; the main handling code is placed in Z380 RAM and a short CALL captor is placed in biport (this was explained in previous sections). In this section, format and parametrs for all of Z380 built-in CALLs are explained; user CALLs can be added by using the procedure explained in "Hooks area" section. Note that MSX will jump to hardware server mode while the CALLs handling code on Z380 is running, this means that BASIC program execution will stop until the command execution finishes. In the case of ZEXE command this can be avoid: it is enough to free the hardware server from the executed Z380 program (setting S_ACTION to 0) and BASIC program execution will continue while Z380 program is running. However this is not recommended to do, because once freed, the hardware server can't be captured again so Z380 program has no access to MSX hardware resources; besides if a furter CALL execution is attempted while Z380 program stills running, BASIC program execution will stop again and then there is no way of making it continue, apart from waiting until the Z380 program finishes. Below is the list and description of all the available system CALLs. "var%" means that an integer variable must be used. _BPEEK (address, var%) Reads value from the specified address on biport memory to the specified variable. Address can be specified in the range &H4000-&H43FF or &H0000-&H03FF. _BPOKE (address, value) Writes the specified value in the specified address on biport memory. Address can be specified in the range &H4000-&H43FF or &H0000-&H03FF. _ZPEEK (address 31-16, address 15-0, var%) Reads value from the specified address in main RAM to the specified variable, by executing the ZBIOS command PEEK. Address is specified within two values, the first one contains two high order bytes of the address and the second one contains the two low order bytes; for example, to read data from address &H12345678, do _ZPEEK(&H1234,&H5678,var%). Of course, when reading data from a word-wide address, the read value will be 16 bits long (in the range -32768..32767); and when reading from a byte-wide address, it will be 8 bits long (in the range 0..255). _ZPOKE (address 31-16, address 15-0, value) Writes the specified value to the specified address in main RAM, by executing the ZBIOS command POKE. Address is specified within two values, the first one contains the two high order bytes of the address and the second one contains the two low order bytes; for example, to write data into address &H12345678, do _ZPOKE(&H1234,&H5678,value%). Value can be any 16 bit number (in the range -32768..32767), but note that when writing to a byte-wide address, actually only the low byte of this value will be wrote. For example when doing _BPOKE(0,&H100,&H1234), the actual value wrote to address &H100 will be &H34, and no error will be generated. Of course the full value is wrote when the address is word-wide, so &H1234 will be wrote to address &H100 with _BPOKE(&HC000,&H100,&H1234). _ZGET (source 31-16, source 15-0, destination, length) Transfers a memory block from the specified source address in Z380 main RAM, to the specified destination address in MSX memory, by using the ZBIOS command GET. Main RAM source address is specified within two values, the first one contains two high order bytes of the address and the second one contains the two low order bytes; see the example for _ZPEEK or _ZPOKE. Refer to chapter "ZBIOS description" to know how the transfer will work when the source memory is hybrid-wide (beyond &HC0000000 boundary). Note that if if a forbidden length is specified, an extra byte will be copied to biport as explained in GET command description, but this extra byte will not be transferred to MSX memory. There is no limit on the block length that can be specified (apart from the size of the available MSX memory in BASIC environment, of course). When the specified size is bigger than the biport's block interchange area size, successive ZBIOS command GET executions will be performed until the complete block is transferred. _ZPUT (source, destination 31-16, destination 15-0, length) Transfers a memory block from the specified source address in MSX memory, to the specified destination address in Z380 main RAM, by using the ZBIOS command PUT. Main RAM destination address is specified within two values, the first one contains two high order bytes of the address and the second one contains the two low order bytes; see the example for _ZPEEK or _ZPOKE. Refer to chapter "ZBIOS description" to know how the transfer will work when the destination memory is hybrid-wide (beyond &HC0000000 boundary). Note that if if a forbidden length is specified, an extra byte will be copied to main RAM as explained in GET command description, since it is not possible to access only the low byte of a word-wide memory position. See figure 8 in "ZBIOS description" chapter. There is no limit on the block length that can be specified (apart from the physical size of the MSX memory). When the specified size is bigger than the block interchange area size, successive ZBIOS command PUT executions will be performed until the complete block is transferred. _ZEXE (address 31-16, address 15-0) Executes the user program placed at the specified address of the Z380 main RAM, by executing the ZBIOS command EXE. Address is specified within two values, the first one contains two high order bytes of the address and the second one contains the two low order bytes; for example, to execute a program from address &H00123456, do _EXE(&H0012,&H3456). Normally, user programs will be placed in the base frame, so the actual use of this command will be _ZEXE(0,address). Remember that it is impossible for Z380 to execute any code out of the base frame when it is running in native mode; refer to "Z380 processor overview" chapter for details about Z380 running modes. _IN0 (port, var%) Reads the specified Z380 internal port value (note that it is a 16 bits value) to the specified variable, by executing the ZBIOS command IN0. This command is intended to be used only by advanced users. Normal users should never need to request information about Z380 internal ports. _OUT0 (port, value) Writes the specified 16 bit value to the specified Z380 internal port, by executing the ZBIOS command OUT0. This command is intended to be used only by advanced users. Please be very careful when using it, because writing wrong data could result in a damage on the card. Normal users should never need to change Z380 internal data ports at all. _BCLEAR (address) Sets the start of the user reserved area in biport memory, by executing the ZBIOS command BCLEAR. Address must be specified in the range &H4000-&H43FF; if 0 is specified, the current user area is destroyed. Refer to "ZBIOS description" and "Biport memory map" sections for details about user memory reservation on biport. 3. Z380 PROCESSOR OVERVIEW As said at the start of this manual, Z380 processor is 100% compatible with Z80, so after reading previous chapters which describes the LPE-Z380 card structure and BIOS, you can start already to make programs for the card if you have Z80 assembler programming knowledge (and a Z80 assembler of course). However, knowledge about Z380 processor new features is desirable in order to make really powerful programs, and also to make programming work easier. This section introduces the main features added to Z380 processor with regard to Z80; these are extended register set (4 sets of 32-bit registers are available), data manipulation modes (word and long word), decoder directives, and running modes (native and extended). Please note that this is only an overview. For a complete and detailed description of Z380 features and instructions, please download Z380 User's Manual from Zilog home page, www.zilog.com. Also, please note that unless otherwise stated, a byte-wide memory is assumed in all the examples. 3.1. EXTENDED REGISTER SET Z80 register set consist of 10 x 16-bit registers (BC, DE, HL, IX, IY, BC', DE', HL', SP, PC), 4 x 8-bit registers (A, A', I, R), and two flag registers (F, F'). In Z380, this register set has been expanded in three ways: - All 16-bit registers becomes 32-bit registers, by the addition of an extended 16-bit part to each of the original 16-bit register. An extended 16-bit part is added also to 8-bit register I. - Alternate index registers are added (IX', IY'), and they are of course 32-bit registers. - There is FOUR sets of main and indexed registers. That is: AF, BC, DE, HL, IX, IY, AF', BC', DE', HL', IX' and IY' exist 4 times. So summarising, Z380 register set consists of: 42 x 32-bit registers (4 sets of BC, DE, HL, IX, IY, BC', DE', HL', IX' and IY', plus SP and PC), 9 x 8-bit registers (4 sets of A and A', plus R), 8 flag registers (4 sets of F and F'), and a 24-bit register (I). See the complete Z380 register set at a glance in figure 9. Figure 9 - Z380 register sets (This is same as Figure 2-1 of Z380 User's Manual) < 8bits > ----------------- ----------------- (4 x this set) | A | F | (4 x this set) | A' | F' | ----------------+-------+-------| ----------------+-------+-------| | BCz | B | C | | BCz' | B' | C' | |---------------+-------+-------| |---------------+-------+-------| | DEz | D | E | | DEz' | D' | E' | |---------------+-------+-------| |---------------+-------+-------| | HLz | H | L | | HLz' | H' | L' | |---------------+-------+-------| |---------------+-------+-------| | IXz | IXh | IXl | | IXz' | IXh' | IXl' | |---------------+-------+-------| |---------------+-------+-------| | IYz | IYh | IYl | | IYz' | IYh' | IYl' | --------------------------------- --------------------------------- --------- --------------------------------- | R | | SPz | SP | ----------------+-------- ----------------+---------------- | Iz | I | | PCz | PC | ------------------------- --------------------------------- Note that as in the case of the Z80, only one register bank (primary or alternate) can be accessed at the same time, and exchange instructions are needed in order to access the other one (however, in Z380 new exchange instructions have been added, so in addition to EXX, EX AF,AF' and EX DE,Hl, now there is also available EX HL,HL', EX B,B', EX BC,IX, etc...). On the other hand, also only one of the 4 availabe register sets is visible in each moment. Of course there is new Z380 instructions to select which bank will be the visible one. Extended portion of 32-bit registers can't be directly accessed as in the case of the normal portion (that is, you can't do for example LD HLz,value). To access this extended portion you must either use the SWAP instruction, or perform 32-bit data loading. The SWAP instructions causes the low word and the high word (the extended portion) of a 32-bit register to be exchanged. For example if DE=&H1234ABCD, a SWAP DE instruction will cause that DE=&HABCD1234, so now you can access to the data previously stored in the extended portion by just accessing to D or E registers. 32-bit data manipulation is described in next section. Table 2 contains all the SWAP instructions together with their opcodes, so you can use them to access the full contents of 32-bit registers. Use macros or DBs to program these instructions with a Z80 assembler. Table 2 - Z380 SWAP instructions with their opcodes (hexadecimal values) (Taken from Z380 User's Manual, page B-14) SWAP BC: ED, 0E SWAP DE: ED, 1E SWAP HL: ED, 3E SWAP IX: DD, 3E SWAP IY: FD, 3E 3.2. WORD/LONG WORD MODE. DECODER DIRECTIVES As said before, Z80 16-bit registers have been expanded to a 32-bit size in Z380. This means that these registers can be loaded with any 32-bit data, but it is also possible to load them with a 16-bit data, so only the Z80 compatible part (the low word) of the register is accessed and the extended part remains unmodified. This is useful to manage 16-bit data and of course necessary to maintain compatibility with old Z80 code. To achieve this, Z380 processor provides two data manipulation modes: the word mode and the long word mode. In the word mode all the data manipulation operations which concern 32-bit registers will be assumed to be 16-bit operations (so only the low word of the register is accessed), while in long word mode these operations will manage 32-bit quantities (so the full register is accessed). This will become clear with the following example: let's take the load instruction LD BC,(HL). In word mode, the low word of BC will be loaded with the contents of (BC) and (BC+1), just as in the Z80, and the extended part BCz will remain unmodified. In long word mode, however, the whole register will be loaded with (BC), (BC+1), (BC+2) and (BC+3). There is two ways to set data manipulation mode. The first one is the permanent way: with SETC LW (sets long word mode) and RESC LW (sets word mode) instructions, you set the Z380 to the appropriate mode, so all the subsequent operations will be performed in this mode. For example: setc lw ld bc,(hl) ;Long word operation ld (ix+1),de ;Long word operation: (IX+1)..(IX+4) <- full DE push iy ;Long word operation: 4 bytes are PUSHed resc lw ld iy,de ;Word operation: IY <- DE, IYz unchanged ex de,bc ;Word operation: DE <-> BC, DEz and BCz unchanged pop iy ;Word operation: 2 bytes are POPed There is also a way to execute a single instruction in a given mode without changing the current mode (the one set with SETC LW or RESC LW instruction): using decoder directives. The decoder directives are instructions which makes the processor to actually do nothing, but they sets the mode -word or long word- in which the immediately following instruction (and only this one) will be executed, regardless of the current permanent state. Decoder directives for word and long word mode control are DDIR W and DDIR LW respectively. Look at the following example: setc lw ld bc,(hl) ;Long word operation ld (ix+1),de ;Long word operation ddir w ;<--- Next operation is word sized ld iy,de ;Word operation ex de,bc ;Long word operation ex ix,ix' ;Long word operation Until now we spoke only about loading data from memory to register (or vice versa) when memory location is pointed by another register, and about register data exchange. But of course registers can be loaded also with 32-bit immediate values, or with memory data using an immediate 32-bit memory address. There is no new instructions to do it; simply the old 16-bit load instructions are used and the immediate data is expanded. For example: ld hl,&H1234 <--- opcodes &H21, &H34, &H12 ld hl,&HABCD1234 <--- opcodes &H21, &H34, &H12, &HCD, &HAB It is not difficult to realize that this method alone can't work properly. If Z380 fetches these opcodes from memory, he can't guess if it is either a LD HL,&H1234 followed by a CALL (whose opcode is &HCD), or actually a LD HL,&HABCD1234, or a LD HL,&HCD1234 followed by XOR E (whose opcode is &HAB). To solve this problem we must use again the decoder directives. In fact, when an instruction manages an immediate data which is expanded with regard to the original Z80 data size, directive DDIR IB (if data has been expanded by one byte) or DDIR IW (if data has been expanded by 2 bytes) must preceed the instruction. Examples: ld hl,&h1234 ;Same as Z80 ddir ib ld hl,&HCD1234 ;Expanded by 1 byte ddir iw ld de,(&HABCD1234) ;Expanded by 2 bytes If you are using a Z80 assembler which do not accept immediate values beyond the &HFFFF boundary, you must write these expanded instructions in the following way: ld hl,&h1234 ddir ib ld hl,&H1234 ;Equals to LD HL,&HCD1234 db &HCD ddir iw ld de,(&H1234) ;Equals to LD DE,(&HABCD1234) dw &HABCD Note that using &HCD as expanded data byte has the same effect of using &H00CD as expanded data word, since both 16-bit ad 32-bit loads are allowed, but 24-bit load is not available. Not only data load instructions can be expanded by extra immediate byte or word; also all the instructions which manage immediate data can do same. For example relative jumps and indexed memory access instructions can be expanded to manage 16-bit or 24-bit displacements; and of course JP and CALL can manage 32-bit addresses with this method (only in extended mode, this is explained forward). This means that you can use instructions such the following ones: jr &H123456 ld a,(ix+&H123456) jp &H12345678 However note that JR instructions have a different opcode depending of the size of the displacement (8-bit, 16-bit or 24-bit). Please refer to Z380 User's Manual for more details. Both uses of decoder directives previously explained can be mixed in one single DDIR instruction. That is, decoder directives can be used to, at the same time, select word/long word mode and indicate an immediate data expansion. For example: ddir ib,w ld (&HCD1234),bc will cause the low word of BC to be stored in addresses &HCD1234 and &HCD1235. At the end of this section there is a complete list with all the available decoder directives, together with their opcodes, so you can access all the Z380 memory even if you use only Z80 instructions (use macros or DBs to program decoder directives with your Z80 assembler). Note that word mode implies that always ONLY a word will be transferred, and long word mode implies that always a FULL 32-bit data will be transferred. This means that when executing the following instruction: ddir lw ld hl,&H1234 the whole HL register will be loaded with &H00001234, since it is strictly a long word operation even if only a 16-bit value was specified (the remaining 16 bits are assumed to be 0). Also, when you execute this instruction: ddir iw,w ld hl,&HABCD1234 the low word of HL will be loaded with &H1234, but the extended portion HLz will remain unmodified, since even if you specified a 32-bit value, it is strictly a word operation, so only the low 16 bits of the data are considered, and the rest is ignored. Table 3 is a list of all the Z380 decoder directives together with their opcodes, so with them you can access all the Z380 memory addressing space even if you use only Z80 instructions. Use macros or DBs to program these decoder directives with a Z80 assembler. Also, opcodes for SETC LW and RESC LW are shown. Table 3 - Z380 decoder directives and W/LW mode setting instructions with their opcodes (hexadecimal values) (Taken from Z380 User's Manual, page B-4) DDIR IB: DD, C3 DDIR IB,LW: FD, C1 DDIR IB,W: DD, C1 DDIR IW: FD, C3 DDIR IW,LW: FD, C2 DDIR IW,W: DD, C2 DDIR LW: FD, C0 DDIR W: DD, C0 SETC LW: DD, F7 RESC LW: DD, FF 3.3. NATIVE AND EXTENDED MODE Es explained in previous section, Z380 can manage 32-bit registers as if it were 16-bit registers, by setting the word mode; this means that unless decoder directives are used, registers works in the same way as in the Z80 processor. However this is not enough to make Z380 processor to 100% seem a Z80 when executing old Z80 code. For example the following code: ld hl,&HFFFF inc hl ld a,(hl) would cause, in a Z80 processor, the A register to be load with the contents of memory address 0; but in a Z380, it will cause the address &H10000 to be accessed instead (assuming word mode, and 0 as previous contents of HLz). For this reason, two running modes are implemented in Z380: native mode and extended mode. When in native and word mode, which is the processor status after a reset, a Z80 program will work 100% correctly because whatever he does, Z380 processor acts exactly as if it were a Z80. The difference between native and extended mode is the address calculations: in native mode, all the address calculations are done modulo 65536; and in extended mode, calculations are done using all the 32 bits. For example, in native mode, program counter and stack pointer manage only 16-bit quantities. This means that program will continue in address 0 after it reaches address &H0000FFFF; and PUSHed data will be stored in address &H0000FFFF if SP had a previous value of 0. Extended mode accepts executable code anywhere in its memory space, and of course program will continue in address &H00010000 after &H0000FFFF. When doing a CALL, a 16-bit return address will be pushed on the stack in native mode; in extended mode, a 32-bit address will be pushed. The same applies to address calculations when using JR and LD (IX+n) instructions: LD A,(IX+1) with IX=&H0000FFFF will actually access address 0 in native mode, and address &H00010000 in extended mode. Also, when INCreasing a register which may contain an address (such HL, IX, IY), operation is done on the 16 low bits of the register only, and the extended part remains unchanged. So &H0000FFFF+1 is 0 in native mode, and &H00010000 in extended mode. However, note that even in native mode, all the Z380 instructions can be used, and the extended portion of the registers still available by using SWAP instructions or decoder directives (so you can access all the Z380 memory for data storage even in native mode). The difference with extended mode fall on the addresses calculation only. So you can for example load HL with &H1234FFFF, and if you do INC HL, the result will be &H12340000 in native mode and &H12350000 in extended mode. This is not true for PC only (in native mode, PCz value is always 0). Also stack pointer can be set to any 32-bit value regardless of the running mode. Extended mode can be set by using the SETC XM instruction (opcodes: &HFD, &HF7). However native mode can't be restored once extended mode has been set; only a reset will make Z380 to return to native mode. The boot program of LPE-Z380 card leaves Z380 in native mode after startup. Anyway, since LPE-Z380 can't execute MSX programs so only code specifically designed for the card can run, it is recommended to consider Z380 a 32-bit processor from the beginning, and therefore to make all programs prepared to run on extended mode, even if the executable code is placed always in the base frame due to the limitation introduced by the fact that a Z80 assembler is used. It may be actually a little difficult to completely understand differences between native mode and extended mode if only a theorical explanation is read. Please take a look at table 4, which contains some examples of how the Z380 processor acts when executing address-related instructions, depending on the running mode. Table 4 - examples of instructions which acts different in native and extended mode - All instructions which use 16 bit or 24 bit immediate data are supposed to be preceded by DDIR IB or DDIR IW, respectively. - Examples with HL are also valid with any 16 bit registers (BC, DE, IX, IY, SP). Examples with IX are also valid with IY and SP. - Examples with CPI/CPDR are also valid with CPD/CPIR. - Please refer to Z380 User's Manual for a complete description of each instruction and a complete list of instructions affected by native/extended mode. Operation What happens is What happens in native mode extended mode ----------------------------------------------------------------------- JP #12345678 PC is loaded with PC is loaded with #00005678 #12345678 PC=#0000FFF0 Jump to #00000013 Jump to #00010013 JR #20 PC=#0000ABCD PUSH #ABD1 PUSH #0000+PUSH #ABD1 CALL #1234 #00001234 is called #00001234 is called POP #ABD1 to PC POP #0000+#ABD1 to PC PC=#0000ABCD PUSH #ABD3 PUSH #0000+PUSH #ABD3 CALL #12345678 #00005678 is called #12345678 is called POP #ABD3 to PC POP #0000+#ABD3 to PC PC=#1234ABCD Impossible PUSH #1234+PUSH #ABD2 CALL #123456 (PCz is always 0) #123456 is called POP #1234+#ABD2 to PC IX=#0034FFFF A <- (#00340000) A <- (#00350000) LD A,(IX+1) HL=#0001FFFF HL <- #00010000 HL <- #00020000 INC HL HL=#00020000 HL <- #0002FFFF HL <- #0001FFFF DEC HL HL=#0001FFFF HL <- #00010000 HL <- #00030000 DE=#00010001 ADD HL,DE HL=#00050000 HL <- #0005FFFF HL <- #0003FFFF DE=#00010001 SUB HL,DE HL=#000AFFFF F <- A-(HL) F <- A-(HL) BC=#00000000 HL <- #000A0000 HL <- #000B0000 CPI BC <- #0000FFFF BC <- #0000FFFF (same as nat.) HL=#000A0000 HL <- #000AFFFF (1st iteration) HL <- #0009FFFF BC=#12345678 Stops when Stops when CPDR BC=#12340000 BC=#12340000 (same as native) 4. CODE EXAMPLES In this section some coding examples are provided, so you can achieve a better understanding about the concepts and techniques explained along this manual. Examples for Z380 programming, ZBIOS calling, hooks use, and user BASIC CALLs creation are included. * Example 1: Searching for LPE-Z380 card If you work in BASIC environment you can just start to use CALLs for accessing LPE-Z380 resources; however when working in machine code, the first thing you must do is to find out what is the slot where LPE-Z380 is plugged, so you can connect biport memory in page 1 and then access it. This can be done with List 1. ;--- List 1: Searching for the LPE-Z380 slot ; Output: (LPEZSLOT) = LPE-Z380 slot, and this slot enabled ; in page 1, if found ; (LPEZSLOT) = &HFF, and slot 3 or 3-3 enabled in page 1, ; if not found ENASLT: equ &H0024 EXPTBL: equ &HFCC1 SLTATR: equ &HFCC9 ZSEARCH: ld a,&HFF ld (CUR_SLOT),a ld (LPEZSLOT),a ZSLOOP: call NEXT_SLOT ;Loop to scan all slots cp &HFF ret z ld e,a ld c,a res 0,c res 1,c sla a sla a sla a sla a or c and &B00111111 ld c,a ld b,0 ld hl,SLTATR+1 ;Check the slot only if it has expanded add hl,bc ;statements (actually if it had when booting, bit 5,(hl) ;this is always the case of Z380) jr z,ZSLOOP ENABLE: ld a,e ld h,&H40 ;Enable slot to check call ENASLT ZTEST: ld hl,&H4030 ;Check for "LPE-Z380"+0 string ld de,Z380TXT ;on address &H4030 ZTLOOP: ld a,(de) cp (hl) jr nz,ZSLOOP or a jr z,ZFOUND inc hl inc de jr ZTLOOP ZFOUND: ld a,(CUR_SLOT) ld (LPEZSLOT),a ret ;--- Routine NEXT_SLOT: ; Every time it is called, returns the number of the next available slot, ; starting with 0 or 0-0. ; When there is no more slots available, returns &HFF. ; Initialize CUR_SLOT with &HFF before calling it for the first time! ; Modifies AF, BC, HL. NEXT_SLOT: ld a,(CUR_SLOT) cp #FF jr nz,NEXT0 ;Initialize? ld a,(EXPTBL) and %10000000 ld (CUR_SLOT),a ret NEXT0: ld a,(CUR_SLOT) ;We reached slot 3 or 3-3 the last time? cp %10001111 ;Then, return &HFF. jr z,FINISH cp %00000011 jr z,FINISH bit 7,a jr nz,SLTEXP SLTPR: and %00000011 ;Increase primary slot number inc a ;if necessary ld c,a ld b,0 ld hl,EXPTBL add hl,bc ld a,(hl) and %10000000 or c ld (CUR_SLOT),a ret SLTEXP: ld c,a ;Increase expanded slot number and %00001100 ;if necessary cp %00001100 ld a,c jr z,SLTPRIM add %00000100 ld (CUR_SLOT),a ret FINISH: ld a,&HFF ret CUR_SLOT: db &HFF ;Current slot number ;--- Data LPEZSLOT: DB &HFF Z380TXT: DB "LPE-Z380",0 * Example 2: Executing ZBIOS commands This is a simple example of how a ZBIOS command must be executed from MSX or from Z380. List 2 executes PEEK command from a MSX program, following the sequence described in previous chapters: wait until Z380 is free, set up input parameters in biport variables area, execute command, wait until command finishes, and recover results. List 3 executes SETCALL command from a Z380 program, in this case it is not necessary to wait for anything. Remember that if the ZBIOS command to be executed is EXE, it is mandatory for MSX to jump to the hardware server captor via CALL S_JMP. See EXE command description on "ZBIOS description" chapter for more details. Note that List 2 first enables LPE-Z380 slot in page 1; however, from Example 3 onwards, the LPE-Z380 slot will be assumed to be already enabled in page 1 in all the lists. ;--- List 2: Executing a ZBIOS command from MSX. ; PEEK command is executed and address &H0001ABCD is read. PEEK: equ 1 ENASLT: equ &H0024 ZCOMM: equ &H4010 ZDIR_3: equ &H4011 ZVAL: equ &H4015 ZDIR_H: equ &H0001 ;Address, high word ZDIR_L: equ &HABCD ;Address, low word ZPEEK: ld a,(LPEZSLOT) ;Enables LPE-Z380 slot in page 1 ld h,&H40 ;(LPEZSLOT is set up by List 1) call ENASLT ZWAIT1: ld a,(ZCOMM) ;First we must wait until Z380 is free or a ;(until the current ZBIOS command or jr nz,ZWAIT1 ;user program finishes) ld hl,ZDIR_L ;We set address to read ld (ZDIR_3),hl ld hl,ZDIR_H ld (ZDIR_3+2),hl ld a,PEEK ;Now we order ZBIOS captor to execute ld (ZCOMM),a ;PEEK command ZWAIT2: ld a,(ZCOMM) ;Now we must wait until command execution or a ;finishes jr nz,ZWAIT2 ld a,(ZVAL) ;All done: we recover the data from ZVAL jp PROCESS ;and we do whatever we want with it ;Note: we read a 8-bit address in this example. You should use LD HL,(ZVAL) ;to recover data if you read a 16-bit address, of course. ;--- List 3: Executing a ZBIOS command from a Z380 program. ; BCLEAR is called to reserve SIZE bytes at the end of biport. ; The code is prepared to be assembled with a Z80 assembler ; (it is assumed that DDIR is previously defined as a macro). BCLEAR: equ 8 SIZE: equ 64 ;Example value ZDIR_M: equ &H0017 ZBIOS: equ &H0006 NOCALL: ld hl,&H4400-SIZE ddir iw,w ;* ld (ZDIR_M),hl ;* This equals to LD (&H40000017),HL dw &H4000 ;* (in word mode) ld a,BCLEAR ;Just calls ZBIOS entry call ZBIOS ret ;Note: address &H0006 will be effectively called even if extended mode ;is set, because CALL has not been expanded with DDIR IB or DDIR IW, so only ;two bytes are fetched after CALL opcode as destination address and the ;remaining 16 bits are assumed to be 0. * Example 3: Transferring big data blocks between MSX and Z380 main RAM As explained in the "ZBIOS description" chapter, the maximum size of a memory block transfer between biport RAM and Z380 main RAM by using ZBIOS commands PUT and GET is determined by the size of the block interchange area, that is (BLKLON). To transfer a larger block, it must be divided into several sub-blocks of (BLKLON) size, and then each one must be transferred with a PUT commad execution; this example shows the correct way to do it. Note that it is intended for being used from a MSX program; from a Z380 program it is enough to use ZTOM and MTOZ routines (se "SAR description" section"). List 4 transfers a block of an arbitrary length from MSX memory to Z380 main RAM memory, while List 5 does the same in the opposite sense. This code is very close to the one that actually use the Server Auxiliary Routines ZTOM and MTOZ, which as you know can transfer blocks of any length. Note that these routines work correctly even if the block length is actually smaller than (BLKLON); in this case only one PUT or GET execution will be performed. Therefore it is advisable to use always BIGPUT and BIGGET to transfer memory blocks, without actually care about the length. ;--- List 4: Transferring large block from MSX memory to Z380 memory ; by using ZBIOS command PUT ; (transfers 4KByte from MSX &HA000 to Z380 &H0002B000). ; Note that PUT command automatically updates ZDIR_3 ; after its execution. ; Note: it is MSX code, not Z380 code. PUT: equ 5 ZCOMM: equ &H4010 ZDIR_3: equ &H4011 ZDIR_M: equ &H4017 ZBLON: equ &H4019 ;Current sub-block length (maximum: (BLKLON)) ZTLON: equ &H401B ;Total block length BLKDIR: equ &H401D ;Block interchange area start address BLKLON: equ &H401F ;Block interchange area length SOURCE: equ &HA000 ;Source MSX address DEST_H: equ &H0002 ;Destination Z380 RAM address, low word DEST_L: equ &HB000 ;Destination Z380 RAM address, high word LENGTH: equ &H1000 ;Block length LIST4: ld hl,SOURCE ;First set up parameters ld (ZDIR_M),hl ld hl,DEST_L ld (ZDIR_3),hl ld hl,DEST_H ld (ZDIR_3+2),hl ld hl,LENGTH ld (ZTLON),hl BIGPUT: ld hl,(ZTLON) ;Finishes if no more data to transfer ld a,h or l ret z call CALC_L ;Returns BC=current sub-block length ld (ZBLON),bc ;Transfers current sub-block from MSX ld hl,(ZDIR_M) ;source memory to biport memory ld de,(BLKDIR) ldir ld (ZDIR_M),hl ;Updates pointer for next step ld a,PUT ;Executes PUT: sub-block block is call DO_380 ;transferred from biport to Z380 main RAM jr BIGPUT ;Start again to transfer more sub-blocks ;--- List 5: Transferring large block from Z380 memory to MSX memory ; by using ZBIOS command GET ; (transfers 4KByte from Z380 &H0002B000 to MSX &HA000). ; Note that GET command automatically updates ZDIR_3 ; after its execution. ; Note: it is MSX code, not Z380 code. GET: equ 4 ZCOMM: equ &H4010 ZDIR_3: equ &H4011 ZDIR_M: equ &H4017 ZBLON: equ &H4019 ;Current sub-block length (maximum: (BLKLON)) ZTLON: equ &H401B ;Total block length BLKDIR: equ &H401D ;Block interchange area start address BLKLON: equ &H401F ;Block interchange area length DESTIN: equ &HA000 ;Destination MSX address SOUR_H: equ &H0002 ;Source Z380 RAM address, low word SOUR_L: equ &HB000 ;Source Z380 RAM address, high word LENGTH: equ &H1000 ;Block length LIST5: ld hl,DESTIN ;First set up parameters ld (ZDIR_M),hl ld hl,SOUR_L ld (ZDIR_3),hl ld hl,SOUR_H ld (ZDIR_3+2),hl ld hl,LENGTH ld (ZTLON),hl BIGGET: ld hl,(ZTLON) ;Finishes if no more data to transfer ld a,h or l ret z call CALC_L ;Returns BC=current sub-block length ld (ZBLON),bc ;Executes GET: Transfers sub-block from ld a,GET ;Z380 RAM to block interchange area in biport call DO_380 ld de,(ZDIR_M) ;Now we transfer sub-block from biport ld hl,(BLKDIR) ;to MSX memory ldir ld (ZDIR_M),de ;Updates pointer for next step jr BIGGET ;Start again to transfer more sub-blocks ;--- Common routines for List 4 and List 5 --- ;--- This routine calculates the size of the next sub-block to be ; transferred: ; - Returns BC = (BLKLON) if the next sub-block is NOT the last ; one to be transferred. ; - Returns BC = Total block length MOD (BLKLON) if the next ; sub-block is the last one to be transferred. ; Also, decreases (ZTLON) by BC bytes; if the next sub-block is ; the last one, sets (ZTLON) to 0. CALC_L: ld hl,(ZTLON) ld bc,(BLKLON) or a sbc hl,bc jr nc,NOLON0 ld hl,0 ld bc,(ZTLON) NOLON0: ld (ZTLON),hl ret ;--- This routine executes the ZBIOS command specified in A ; (see List 2 for details) DO_380: call WAITZ ld (ZCOMM),a call WAITZ ret WAITZ: ex af,af' WAITZ2: ld a,(ZCOMM) or a jr nz,WAITZ2 ex af,af' ret * Example 4: Executing a transient user program This is an example of a Z380 transient program load an execution. Remember that a transient program is just load in UPA and executed, without any memory reservation. It is also an example of SAR use to manage keyboard input and screen output. List 6 loads an executes a program which acts like MSX-DOS command TYPE /P: it shows the contents of a text file on the screen, making a pause and asking for a key pressing when the screen is full; program will end when an EOF character (&H1A) is found. For simplicity we suppose that the file has been previously loaded on Z380 RAM (for example using list 4 together with disk access routines); the concrete address is specified by TXT_H and TXT_L constants. ;--- List 6: Load + execution of a Z380 user program. ; Shows a text file previously load on Z380 RAM, ; with pause for key pressing when the screen is full. ; It ends when a EOF character is found. ; Specify the text address within TEXT_L and TEXT_H. ;*** CONSTANTS DEFINITION *** ;--- Biport variables used ZCOMM: equ &H0010 ;Here, biport addresses are indicated ZDIR_3: equ &H0011 ;with a default of &H40000000 for Z380, ZVAL: equ &H0015 ;and with a default of &H4000 for MSX ZDIR_M: equ &H0017 ZTLON: equ &H001B S_JMP: equ &H0042 ;--- SAR used EXEMS2: equ &H0043 ;--- MSX BIOS routines used CALSLT: equ #001C ;Call routine on other slot CHGET: equ #009F ;Wait for a key pressing CHPUT: equ #00A2 ;Print character CLS: equ #00C3 ;Clear screen KILBUF: equ #0156 ;Clear keyboard buffer ;--- Other data & macros TEXT_H: equ &H0001 ;We suppose text placed at &H00010000 TEXT_L: equ &H0000 EOF: equ &H1A bios: macro @r ;This macro executes a MSX-BIOS routine ld iy,0 ;using CALSLT, so it works from both BASIC ld ix,@r ;and DOS environment. call EXEMS2 dw CALSLT endm ;*** THIS IS THE MSX CODE *** ;--- Loads Z380 program in Z380 RAM, address &H00000100 org &H100 ld hl,&H0100 ld (ZDIR_3+&H4000),hl ld hl,&H0000 ld (ZDIR_3+2+&H4000),hl ld hl,ZCODE ld (ZDIR_M+&H4000),hl ld hl,ZC_END-ZCODE ld (ZTLON+&H4000),hl call BIGPUT ;See List 4 ;--- Executes program AND JUMPS TO HARDWARE SERVER ld hl,&H0100 ld (ZDIR_3+&H4000),hl ld hl,&H0000 ld (ZDIR_3+2+&H4000),hl ld a,EXE ld (ZCOMM+&H4000),a call S_JMP+&H4000 ;Jump to hardware server is mandatory!! ;--- Program finished, we can continue doing anything jp ANYTHING ;*** THIS IS THE Z380 PROGRAM *** ZCODE: equ &H0100 xor a ;First clears screen. bios CLS ;CLS needs Z=0 at input. setc lw ddir iw ld hl,TEXT_L ;Text address dw TEXT_H ld b,21 ;Number of lines per screen LOOP: ld a,(hl) ;Read character and print it. push hl push bc bios CHPUT setc lw pop bc cp 10 ;If end of line found, process it. call z,NXLIN cp EOF ;If EOF found, terminate. jp z,3 setc lw pop hl ;Update pointer and repeat loop. inc hl jr LOOP ;--- End of line processing NXLIN: djnz NRET ;If not end of screen, just decrease B. bios KILBUF setc lw ld hl,PRESS LOOP2: ld a,(hl) ;Prints "Press any key". or a jr z,WKEY push hl bios CHPUT setc lw pop hl inc hl jr LOOP2 WKEY: bios CHGET ;Wait for a key pressing. xor a ;Clear screen and start again main loop. bios CLS ld b,21 xor a NRET: ret PRESS: db 10,"- Press any key... - ",0 ZC_END: ; Note that we use CALSLT to execute MSX BIOS routines, this enables to execute this program from both DOS and BASIC environments. If we were sure that MSX is working in BASIC environment while our Z380 program is running, we could call BIOS routines directly (for example CALL EXEMS2 : dw CLS). Normally, the Z380 program should know the MSX state in which it runs (or obtain it, normally reading the slot selection register and its associated system variables). To print "Press any key" message we use a loop in which we print each character individually. There is another way to do same: copy the whole text string to MSX memory, and then print by it using DOS function STROUT (9), via CALL 5 or CALL &HF37D. But in this case we must know what parts of MSX memory are available for temporary data storage. Normally, all TPA on DOS environment (except page 1 of course) will be available, but in BASIC environment care must be taken because an existing BASIC program could be overwritten (this is specially true when processing a CALL). This program could be optimized by using the hardware server manually instead of SAR, in the following way: while MSX is executing the code for printing the current character, the Z380 main program is already reading the next character from memory. This makes program a little more complex of course, but program speed is improved. * Example 5: Installing an user BASIC expanded statement This example shows how a user CALL must be developped and installed. It also shows how to reserve memory at the end of UPA. The name of the command is DIVIDE, and it performs an unsigned integer division using the Z380 command DIVUW. Its syntax is as follows: CALL DIVIDE(dividend_h, dividend_l, divisor, q%, r%, o%) dividend_h: High word of the 32-bit dividend. dividend_l: Low word of the 32-bit dividend. divisor: 16-bit divisor. q%: Integer variable where quotient will be put. r%: Integer variable where rest will be put. o%: integer variable which will be set to -1 if there is overflow (quotient is greater than 65535 or dividend is 0), else it will be set to 0. List 7 contains the installer and the CALL management code itself. The installer will reserve memory at the end of UPA using the procedure explained in the "User reserved area" section on "Base frame memory map" chapter, copy the CALL management code there, and update H_CALL hook. It is all Z380 code, and it is assumed that it was previously load on Z380 memory (address &H00000100); refer to example 4 to know how to do this. ;--- List 7: Example of user CALL and its installation. ; Usage: CALL DIVIDE(dividend_h, dividend_l, divisor, q%, r%, o%) ; Example: CALL DIVIDE (0,103,2,a%,b%,c%) ; Returns a%=51, b%=1, c%=0 ;*** CONSTANTS DEFINITION *** ;--- Used biport variables ZDIR_3: equ &H0011 ZVAL: equ &H0015 ;--- Used SAR MWWORD: equ &H0019 ;--- Other base frame addresses ZBIOS: equ &H0006 MAXDIR: equ &H0009 H_CALL: equ &HFFE8 ;*** CODE INSTALLER *** org &H0100 ;--- First reserves space at the end of the base frame INST: resc lw ld hl,(MAXDIR) subw hl,DIVEND - DIVST ;CALL handler size ld (MAXDIR),hl ;--- Builds the new H_CALL hook ld (NEW_H+1),hl ;--- Stores the old hook contents on the CALL manager itself setc lw ld hl,H_CALL ld de,OLD_H ld bc,8 ldirw ;We can use it here because BC is multiple of 4 ;--- Sets ups CHKNAM, it is a fixed memory reference ; in the main code, which is relocatable resc lw ld hl,CALDIV - DIVST ld de,(MAXDIR) add hl,de ld (CHKNAM+1),hl ;--- Copies the CALL handler to the previously reserved area setc lw ld hl,DIVST ld de,0 ddir w ld de,(MAXDIR) ld bc,DIVEND - DIVST ldir ;--- Sets up the new hook ld hl,NEWHOOK ld de,H_CALL ld bc,8 ldirw ;We can use it here because BC is multiple of 4 ;--- All done: installer terminates, and from now on ; CALL DIVIDE is available. jp 3 ;--- New H_CALL hook NEW_H: jp 0 ;<--- It is set up with the new user reserved area ds 5 ; address before copying it to H_CALL ;*** CALL HANDLER CODE *** ;It must be relocatable. References to fixed addresses must be set up ;by the installer (here, only for CHKNAM). DIVST: setc lw ddir iw ld ix,0 ;Makes IX point to biport dw #4000 ;--- Checks the command name. If not DIVIDE, sets (ZVAL)=0 ; and jumpt to the old hook contents. CHKNAM: ld hl,0 ;<--- Set up when installing ld (ix+ZDIR_3),hl ld a,COMPCALL ;We use ZBIOS to check name call ZBIOS ld a,(ix+ZVAL+1) or a jr nz,OKDIV NODIV: xor a ;Continues here if command is not DIVIDE ld (ix+ZVAL),a OLD_H: ds 8 ;<--- Set up with the old hook when installing CALDIV: db "DIVIDE",0,0 ;Command name(s) (must be uppercased) OKDIV: ;Continues from here if command is DIVIDE ;--- Now extracts all parameters and stores it in the stack. ; ZBIOS is used for this. resc lw ld (ix+ZVAL),1 ;Extracts "(" ld a,CALLPAR call ZBIOS ld (ix+ZVAL),5 ;Extracts dividend_h ld a,CALLPAR call ZBIOS ld hl,(ix+ZVAL) push hl ld (ix+ZVAL),3 ;Extracts "," ld a,CALLPAR call ZBIOS ld (ix+ZVAL),5 ;Extracts dividend_l ld a,CALLPAR call ZBIOS ld hl,(ix+ZVAL) push hl ld (ix+ZVAL),3 ;Extracts "," ld a,CALLPAR call ZBIOS ld (ix+ZVAL),5 ;Extracts divisor ld a,CALLPAR call ZBIOS ld hl,(ix+ZVAL) push hl ld (ix+ZVAL),3 ;Extracts "," ld a,CALLPAR call ZBIOS ld (ix+ZVAL),6 ;Extracts q% address ld a,CALLPAR call ZBIOS ld hl,(ix+ZVAL) push hl ld (ix+ZVAL),3 ;Extracts "," ld a,CALLPAR call ZBIOS ld (ix+ZVAL),6 ;Extracts r% address ld a,CALLPAR call ZBIOS ld hl,(ix+ZVAL) push hl ld (ix+ZVAL),3 ;Extracts "," ld a,CALLPAR call ZBIOS ld (ix+ZVAL),6 ;Extracts o% address ld a,CALLPAR call ZBIOS ld hl,(ix+ZVAL) push hl ld (ix+ZVAL),2 ;Extracts ")" ld a,CALLPAR call ZBIOS sub sp,6 ;Reserves other 6 bytes on the stack ;Now the stack contents is as follows: ;(SP) = reserved for qoutient ;(SP+2) = reserved for rest ;(SP+4) = reserved for overflow ;(SP+6) = o% address ;(SP+8) = r% address ;(SP+10)= q% address ;(SP+12)= divisor (16 bit) ;(SP+14)= dividend (32 bit) ;--- Recovers dividend to HL, divisor to DE, and divides ddir lw ld hl,(sp+14) ;Recovers dividend (4 byte) ld de,(sp+12) ;Recovers divisor (2 byte) divuw de ;HL(15..0) = HL(31..0) \ DE(15..0) ;HL(31..16) = Rest ;--- Stores results temporarily on the stack ld (sp),hl ;Stores quotient swap hl ld (sp+2),hl ;Stores rest push af pop hl ;Bit 2 of F is set if overflow bit 2,l ld hl,0 jr z,NOVF cplw hl NOVF: ld (sp+4),hl ;Stores overflow (-1) or not (0) condition. ;--- Save results into variables ld de,(sp) ;Save quotient on q% ld hl,(sp+10) call MWWORD ld de,(sp+2) ;Save rest on r% ld hl,(sp+8) call MWWORD ld de,(sp+4) ;Save overflow on o% ld hl,(sp+6) call MWWORD ;--- All done: restores stack, sets (ZVAL)=1 and terminates. ld a,1 ddir iw ld (ZVAL),a dw #4000 add sp,18 ;Restores initial stack pointer ret DIVEND: ; Look how the ZBIOS command CALLPAR is used to easily extract all parameters. Since variables q%, r% and o% are integer, to set them to any value it is enough to write the raw value into the address obtained for each one; the easiest way to do this is by using SAR. Also, look how COMPCALL is used to easily check the command name. It is not strictly mandatory to install this code in base frame; you can actually use any Z380 RAM location starting at &H00010000. However if you do this, note that other transient or resident programs may overwrite your code if they use the same address for code or data storage. To avoid this, an external memory management mechanism is needed; Z380 EPROM code does not provide any mechanism to reserve memory beyond the base frame. If it is a new ZBIOS command what you want to install instead of a new CALL, the procedure is almost same as the one shown in list 7; only the following is different: - When installing, use of course H_COMM instead of H_CALL. - Check the command number in A instead of the command name in CALLNAM. - If command number does not match, restore original contents of A instead of setting ZVAL to 0, before jumping to the old hook. - Use biport variables area for input/output parameters, instead of extracting them with CALLPAR. - When all process is done, set A to 0 instead of setting ZVAL to 1, before returning with the original stack pointer. Refer to "Hooks area" section for more information about how to create user ZBIOS commands.