Machine Code Calls¶
HUNTER is designed to be extremely versatile and flexible in its use. For some applications it is not possible to perform every function using pure Basic routines, usually for reasons of speed. It is possible to write machine code routines in these situations.
Also covered in this section is some general guidelines in the control of HUNTER by means of PEEK and POKE to specified memory addresses.
It should be emphasised that the use of features described here is not recommended for users unfamiliar with computing at machine code level.
The Machine Code¶
HUNTER uses the CMOS NSC-800 microprocessor, which provides an instruction set entirely compatible with the popular Z80 microprocessor. For that reason it is not proposed to go into detail about the facilities offered by machine code and users are referred to any of the standard books which cover the Z80.
It is not recommended, however, that the stack pointer register (SP) is used in a fashion incompatible with supporting interrupts. Standard use in, the form of subroutine CALLs, PUSHs and POPs, however, is supported.
To execute a machine code program the function:-
is used. This will pass control from Basic to the address. A machine code RET will return the user to Basic.
Passing Simple Parameters¶
It is possible to pass information both from Basic to the ‘CALLed’ program and return the information back to Basic.
To set up outgoing data the ARG reserved word is used, format as shown. The variable will be equated to the value in brackets.
A = ARG (5)
will cause the NSC800 internal registers C and E to be set up as follows:-
The pair are set up as a 16 bit integer, hence:-
when CALL is executed.
These registers are used for compatibility with the CP/M style system call structure.
To return data to Basic the program:-
1000 X = CALL(addr)
causes the value of variable X to take on the value contained in register A.
Passing Multiple Variables¶
To pass more than one integer between a CALLed program and Basic the machine code linkage stack is used.
This is controlled in Basic by PUSH and POP. In the routine:-
1000 PUSH X,Y 1010 A = CALL (addr) 1020 Y = POP(1) 1030 X = POP(1)
The integer values of X and Y are first placed on the stack and made available to the called routine, and then removed by the POP instruction back into their own variables.
When CALL is executed the register pair HL is used as an address pointer to the bottom of the stack on which the variables are placed
In other words HL is addressing the low byte of the most recently PUSHed variable. To access others then HL is incremented. These values may either be used by the called routine or modified and used by Basic. It is not necessary to preserve the contents of HL.
Section Memory Locations and Port Allocations, contain list’s of available addresses of HUNTER parameters and system call addresses. The parameters may be written or read by means of PEEK and POKE operations. System calls may be used for functions such as the CLOCK or communications parameters.
By subtle use of the DEFSEG statement, to address the required page, many places in RAM can be used for machine code implants. However, the only place in common RAM available (whatever DEFSEG is set to) is the serial communications buffer. Since the communications are active continuously on receive, take care not to let programs be corrupted. 50 hex bytes are available for small user programs at decimal address 62981 (see Memory Locations).
Machine Code In Basic¶
The recommended method of storing machine code from BASIC is to store it in a dummy string array, a method for which is outlined below.
First calculate the length of the machine code and dimension a string array with the required number of single byte elements using the formula:
where N=number of bytes needed for machine code and D=number of single byte elements required in dimension.
To reserve 10 bytes of memory, the required dimension is
After dimensioning, all the bytes will be set to zero.
The start address of the reserved memory must be found. This is done as follows:
Assuming the array name is MC$, the statement
AD=VARPTP(MC$)-1will return the address of the first reserved byte in AD.
To actually put the machine code into the array, a series of POKE statements are used, i.e:
POKE AD,30,7, 14, 1,205,5,0,201
The machine code must be given as decimal numbers.
Finally, to execute the machine code, all that is required is
where X is a dummy variable and AD is the start address of the machine code as obtained in above.
It is vitally important that DEFSEG is set to 0 when the CALL is executed. If DESEG is altered in the program, DEFSEG=0 must be executed before the CALL is executed. It is also important that no attempt be made to use the array which is holding the machine code in the Basic program.
Paging In Machine Code¶
In some applications the user may wish to access other pages from a machine code program. However, this is not a trivial matter and extreme care must be taken when using this facility. The user is recommended to read File Structure And Memory Organisation for full details on the HUNTER memory organisation before proceeding.
In order to use the HUNTER paging, the machine code to switch the page must be located in the common block of ram starting at 49152 (C000H). If the machine code is not in this block of ram the HUNTER will not be able to access the machine code program once the page has been switched. In order to ensure the machine code is located above 49152 (C000H) the array used to hold the machine code is the last array dimensioned. Check the start address of the array using VARPTR. If the address returned is still below 49152, use a dummy array to push the start address of the array to above 49152.
The paging control is accessed via an ‘OUT (E0H),A’ command, with the accumulator containing the required page number with bit 7 set to 1.
i.e: to switch to page R0, the accumulator should contain 80H (128 decimal).
Available RAM in R0¶
Basic source in HUNTER can extend for 54k, though there must be space for the symbol table.
The following diagram, shows how program and arrays are stored in lower memory, simple variables and array definitions start at D700 and work down. The gap in the middle is available and unused, the amount being displayed using FRE. The limits are contained in memory locations ALIM and DEFLIM. The address contained at these locations indicates the position of the free space.
It should be remembered that these locations are allocated dynamically and are entirely dependent upon the program and store. Also, if CLEAR or a new program line is inserted then the data storage is removed, so DEFLIM will point to the top of memory and ALIM to the top of the program. Hence, the program should be RUN before using these values. Clearly, if Basic is only used to start a large machine code program then there will be no variables and only a small amount of Basic source.
The system calls are all defined in CP/M Interface. They should be used by CALLing location 5 with the relevant value in C for CP/M compatibility. NO guarantee is made of the contents of undefined registers on exit and in general all register contents will be destroyed.
The machine code stack pointer is initialised at switch on. It is not recommended that it should be moved as the system can use a considerable amount of stack space. Under no circumstances should routines be written which preclude the use of interrupts: V24 I/O uses them liberally.
The key, as detailed in section ON/OFF KEY, is software controlled. The keyboard should be scanned periodically in any user written software if manual power OFF is required.
It is not proposed to go into software techniques, meaning of object code, etc., here as the NSC800 is totally software compatible to the Z80. A list of the machine code instructions is presented in NSC800 Machine Code. Users are referred to any tutorial book on the Z80 for detailed programming information.
Of interest in machine code loops, etc., is the execution time of the program. The number of cycles used by each instruction is detailed in NSC800 Machine Code. The cycle time in HUNTER is 250ns.
If serial data is received, then the routines will be considerably slower due to interrupts.