
**********************************************************************
files: Mcode_Parameters.TXT
       http://www.users.globalnet.co.uk/~jg27paw4/pourri/hex_tool.zip
       http://www.users.globalnet.co.uk/~jg27paw4/pourri/z80times.zip
**********************************************************************


** Passing Parameters to Machine Code
   ==================================

There are several methods for passing parameters from a BASIC program to a machine code program. There are a multitude of variations on most of these methods, as the USR function can be used with most ZX BASIC commands.

1. POKE the parameters directly into the m/code prior to the USR call.

2. RANDOMIZE pppp: prior to the USR call will store value pppp in SEED at 23670/1 from where the m/code can retrieve it.

   These two options restrict pppp to a 16-bit integer.

3. LET a=numexp+USR aaaa: puts numexp (which can be any numeric expression) on the calculator stack.

4. LET a$=(strexp+STR$ USR aaa)(TO 0): puts a pointer to strexp (which can be any string expression) on the calculator stack (a$ is set to the null string).

5. DEF FN a(param1,param2...paramN)=USR aaaa: stores the parameters (which can be any combination of numeric & string expressions) in the area pointed to by DEFADD at 23563/4 (starting at the first character after the opening '(' of DEF FN a(...)).

   DEF FN parameter entries are separated by ',' and terminated by ')', and their form is:
   strings (8 bytes): 'a' '$' $0e $00 loclo lochi lenlo lenhi
                      where  loc & len are location & length
   numbers (7 bytes): 'a' $0e ee  mm mm mm mm (for FP numbers)
                      'a' $0e 00 $00 lo hi 00 (+ve small integers)
                      'a' $0e 00 $ff lo hi 00 (-ve small integers)
                      where ee & mm are exponent & mantissa

6. PRINT USR ssss,param1,param2...paramN,aaaa;: calls a prefatory routine at ssss which calls SCANNING to evaluate the parameters (which can be any combination of numeric & string expressions) and put them on the calculator stack, thence calling the routine at aaaa which can then retrieve them.

ssss:   ld   hl,14
        add  hl,sp
        ld   sp,hl           ;undo USR
GETARG: rst  $20
        call $24fb           ;SCANNING
        rst  $18
        cp   ","
        jr   z,GETARG
        call $2da2           ;FP_TO_BC
        push bc              ;BC=aaaa
        pop  hl
        jp   (hl)            ;JP aaaa

7a.LET a=numexp1=numexp2+USR address: puts numexp1 & numexp2 (which can be any numeric expressions) on the calculator stack.
   which can be extended for any number of parameters by nesting them; eg.:
7b.LET a=numexp1=(numexp2=(numexp3+USR address)): puts numexp1, numexp2 & numexp3 on the calculator stack.


Parameters can be transferred to & from the calculator stack using the ROM routines referenced below: FETCHS is for long integers >65535, floating point numbers and string pointers; FPTOBC is for regular integers <=65535, and FPTOA is for small integers <=255. Using STORES/FETCHS for strings, BC holds the length and DE the location, A is zero for array elements or string slices and non-zero for simple strings; for numbers AEDCB holds the 5-byte floating point form.

STORES: equ  $2ab6           ;STK_STORE to calculator stack: AEDCB
FETCHS: equ  $2bf1           ;STK_FETCH from calculator stack: AEDCB
STAKA:  equ  $2d28           ;STACK_A   to calculator stack: A
STAKBC: equ  $2d2b           ;STACK_BC  to calculator stack: BC
FPTOBC: equ  $2da2           ;FP_TO_BC  from calculator stack: BC
FPTOA:  equ  $2dd5           ;FP_TO_A   from calculator stack: A


**********************************************************************


** Assembler Listings with OpCodes in a Distinct Column
   ====================================================

The Phipps disassembler puts the opcodes in a distinct column.

[code]
28B2 FDCB01F6 SET  6,(IY+1)
28B6 DF       RST  18
28B7 CD8D2C   CALL 2C8D
28BA D28A1C   JP   NC,1C8A
28BD E5       PUSH HL
28BE E61F     AND  1F
28C0 4F       LD   C,A
28C1 E7       RST  20
28C2 E5       PUSH HL
28C3 FE28     CP   28
28C5 2828     JR   Z,+40 28EF
[/code]

Unfortunately it only accepts a start address, not a stop address, and if hard copy is selected so that it can be saved to a file there's nothing on the screen to show how far it's got.

The WinZ80 disassembler also puts the opcodes in a distinct column.

[code]
  start:
0000 f3       DI
0001 af       XOR  A
0002 11ffff   LD   DE,ffff
0005 c3cb11   JP   11cb,start_new
  error_1:
0008 2a5d5c   LD   HL,(5c5d)
000b 225f5c   LD   (5c5f),HL
000e 1843     JR   0053,error_2
  print_a_1:
0010 c3f215   JP   15f2,print_a_2
[/code]

As can be seen, it also knows about the routine names in the ROM. It accepts both start and end addresses for a disassembly, and always saves to a file. The debugger command is "dump start,end,file".

   ====================================================

My [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/hex_tool.zip]Hex Tools[/url] programs includes a Hex Dumper which will send a memory dump to screen or printer for a selected address range, with a print width from one to sixteen bytes.

So, for example, entering:
Start/End as hex/dec? [hd] "h"
Start? (hex) "28B2"
End? (hex) "2950"
Print address? [hdn] "n"
Bytes per line? [1-16] 1
Bytes as hex/dec? [hd] "h"
Checksum? [hdn] "n"
Screen or Printer? [sp] "s"
Print hex bytes spaced? [yn] "n"
will print Look_Vars like this:

[img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/hex_dump.gif[/img]

The PRINT at line 100 could be changed to append to a string or poke memory instead of printing. Fuse lets you send printer output to a text file, as does WinZ80 and some other emulators as well I expect.


**********************************************************************


** Timing Calculator Extension to Z80 OpCodes Spreadsheet
   ======================================================

I've set up an extra Excel spreadsheet which references Einar's v.helpful Z80TABLE spreadsheet and allows a list of Z80 assembler commands to be typed in and their timings added up. The [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/z80times.zip]Z80TIMES[/url] ZIP file includes Einar's spreadsheet with names added for the 42 columns which contain "documented instructions" entries in black, so that it's easier for them to be referenced in the other spreadsheet.

I don't use Excel much, so there might be a simpler way to do this, but what I did was, for each command line, set up a row of 42 cells each with a formula of the form:

[code]=IFERROR(IF(nextref=0,NA(),nextref),VLOOKUP(cmndref,z80named.xls!columnname,2,FALSE))[/code]

Evaluated right to left, the cells in a row return errors until a match is found in Z80TABLE for the command in question in a column with a non-zero time in the second cell, after which the value is passed along to the left-hand cell to be displayed. (I haven't included alternative timings for conditional commands.) The commands have to be typed with the same spacing as they appear in the original spreadsheet, although the case of the letters doesn't matter. The 41 precursor cells I've just coloured green on green so they don't show (other than as a green rectangle, I suppose), but they could be collapsed with the Hide function.

I've just put in one row with a command from each sheet, just to show that they're all being referenced correctly; it's easy enough to paste in extra rows for longer command sequences. I had to save the file as XLSX format as it wouldn't do the external lookup otherwise.

You might get a message: "[b]Security Warning[/b] Automatic update of links has been disabled" when loading the file (I generally have automatic update of everything disabled, which I find makes for a much quieter life). Also, the external file references include a full path name from my J drive. I tried removing it with Find & Replace, but the next time I loaded the file it had come back again, so I don't know what's up with that.


**********************************************************************


** BASIC Commands Which Can Include A USR Call
   ===========================================

Which is almost all of them. I checked the list of commands and functions and the only ones which won't accept either "USR n" or "STR$ USR n" are the commands CLS, CONTINUE, COPY, NEW, REM, RETURN, STOP and the functions which take no argument: INKEY$, PI, RND (although INKEY$#USR n would work).


**********************************************************************


** Machine code optimisation
   =========================

This might be of interest:
[url]http://www.worldofspectrum.org/forums/showpost.php?p=652495&postcount=13[/url]

Although Fuse and ZXSpin record T-states I could never make head nor tail of the results they gave, nor could I get much sense out of ZXSpin's profiler even though I use its assembler & monitor/debugger a lot, although there is an explanation of it somewhere, either in Emulators or Development. Check those forums for other threads on timing & profiling.

Mark Woodmass' SpecEmu has a good debugger and can track timings. I used to use that a lot for timing, then I found some routines were giving significantly different timing results when re-executing exactly the same section of code, so that feature has to be used with some caution.

One emulator which has a monitor/debugger which returns consistent timings is WinZ80 (although I expect that there are others that I've not tried), but I think that the version on WoS is unregistered shareware and thus isn't practical for regular use. The registered version has to be installed off floppy disk as the registration code is hidden on the disk somewhere, although Gerton Lunter has made the full version freely available now anyway, so it should be around.

Einar Saukas put all the Z80 timings into an Excel spreadsheet (from the Zaks book, I think). I added an extension so that two sets of commands could be typed in to compare timings. I use it when I'm considering alternative methods of writing a bit of code. The files are here:
[url]http://www.users.globalnet.co.uk/~jg27paw4/pourri/z80times.zip[/url]
Note that commands don't take specific values; displacements use 'd', one-byte values use 'n', two-byte values use 'nn'; eg. "ld l,(ix+d)", "call nz,nn"; as the forms have to match those used in the reference spreadsheet.

=========================

... and using IX/IY is slower than everything else - although useful at times even so.

Also remember that if you're timing a repeating instruction such as LDIR, CPIR, etc, the time needs to be multiplied by the BC value to get the total time (although CPIR & CPDR will finish early if they get a match).

Also remember that conditional instructions take a slightly different time depending upon whether they're true or false; this also applies to the final repetition of a repeating instruction, when the repeating condition is no longer true. Einar's spreadsheet has separate columns for the true & false times; my extension always looks up the true value.

=========================

I calculate by hand if I'm timing some code that I've already written. I note the frames+Tstates at the start and the frames+Tstates at the end and subtract one from t'other. If I'm timing some code that I've not already written (ie. it's still in the planning stage) then I use the spreadsheet as previously mentioned.

=========================

T-(time)-cycles are the time taken for the processor's clock to 'tick' once. M-(machine)-cycles are the time taken for the processor to execute an action. So the time taken to execute a machine code command will be the sum of the times for each of the processor actions required to complete it. To find the time in seconds (or a fraction thereof), divide T by the CPU speed (3.54mHz for the Spectrum). Comparing the data flow diagrams in Zaks' "Programming the Z80" will show why the same M-cycles can sum to different T-cycles.


**********************************************************************