Machine Code Made Easy - Part 5
Description
ROM Routines
If you were tuned into last issue's column, you'll know how ROM0 and ROM1 can be paged into memory. And if you don't know... tuf! Well, as I'm sure you all know, there are ROM routines which we can use in our own machine code programs. This has 2 uses:-
1- To save us from writing long and complicated routines of our own. (ie, let us be lazy)
2- To let us see how Dr. Andy Wright (genius extrodinaire) solved some (very!) tough problems. (ie, to pinch his ideas)
Again, I will reiterate that budding coders should get SAMCO's SAM Coupé Technical Manual. It retails around £17 (I think) but is certainly more than worth its weight in gold if you are doing any coding. Anyway, the manual gives you a full list of routines and entry conditions, as well as floating point calculator codes (see later).
Meanwhile, I'll give you a rough idea of some of the more useful routines and how to use them:
Well, basically there is a jump table in ROM0 at 0100H which contains instructions to run certain subroutines. It is worth noting that before one of the addresses in the table is called, you should switch in ROM0, which expects page 0 to be in section 4000-7FFF. Your simple routine would be something like:
IN a,(LMPR) ; LMPR=250 dec. LD (LMPRSTORE),a ; store original status LD a,%00111111 ; switch in ROM0 and necessary RAM OUT (LMPR),a ; . ; . ; rest of routine, including CALL to . ; jump table . ; LD a,(LMPRSTORE) ; reset LMPR's status OUT (LMPR),a ;
The important thing to remember is that you MUST set every parameter and entry condition the routine needs. If anything goes wrong you should return to BASIC, but you can never be too careful when using other people's routines. However, you don't have to worry about switching in ROM1 as well - if the routine uses it the jump table will sort it all out for you, including the restoration of HMPR.
So, what about the routines themselves, eh? Well here's a sample of the ones you are most likely to use ( most of them graphical), with all the entry conditions:
JPLOT Plot pixel. B= y coordinate (0 at top, 191 at bottom),
0139H C= x coordinate. [If mode=3, and fatpix=0, then HL= x
coordinate (0-511)]
JDRAW Draw line from current coords, going C pixels
013CH horizontally (or HL if fatpix=0 & mode=3), and B pixels
vertically. E= 1 to go right, FF to go left. D= 1 to go
down, FF to go up. Colour, inverse etc are all governed
by syatem variables as is the screen to use.
JDRAWTO Like JDRAW, except that the line is drawn to C,B (or
013FH HL, as above).
JCIRCLE Draw circle at C,B radius A. Colours etc come from
0142H temporary colour SVARs.
JFILL Modes 3,4 only. Fills a pattern into the space from
01455H C,B, with the address of the 16*16 pixel pattern held
in DE (0 for solid fill of current pen). A=0 to use 6k
scratchpad immediately after the 24k screen. A<>0 to
use previous scratchpad for new colour/pattern.
Pattern data for DE has 8 bytes for each row, makes 128
bytes in total.
JCLSBL If A=0 clear entire screen.
0143H If A<>0 clear upper screen.
JGETINT Unstack rounded number from FPCS (see later) and put it
0121H in HL. BC=HL. A=L
JSTKFETCH Unstack top number of FPCS into AEDCB.
0124H
JSTKSTORE Stack AEDCB onto FPCS.
0127H
JBUFFET Unstack the details of a string from the FPCS, copy
012AH text to a buffer in the system variables page. On exit,
DE points to the start of the buffer, BC holds the
string length. A=C.
Phew! And that's just a sample! There are plenty more, with more
detailed descriptions then those listed above, in the Advanced
Technical Manual.
And after that heavy dose of information...
The Floating Point Calculator
Ha ha! This one's a nightmare! zillions of control codes with a second stack and 2 different ways of storing numbers. And you thought assembly language was tough!.
Anyway, the floating point calcultor (FPC for short and typist's cramp avoidment) is the bit of the ROM that does all the SAM's complicated maths work, and most of the string functions too just for good measure.
It has its own stack where numbers and string information are stored as 5-byte long codes. These are stored in three different ways, as is described below:-
1. Integers between -65535 and +65535 are stored as - 0, sign (0 for positive, FF for negative), least significant byte, most significant byte, 0.
2. Other numbers between around 1E38 and 1.7E-39 are held in the same way as with the Spectrum : ie, as 2 raised to a power (called the exponent) times another bit called the mantissa. The first byte is the exponent+80H, the other four are the mantissa. The exponent can be negative of course, but you should note than this scheme does not allow some numbers like 1/10 being stored acurately, just like 1/3 cannot be represented accurately in decimal.
3. Strings are held in the 5-byte code thus - 16k page of text, offset of text start within page (8000H - BFFFH, LSB/MSB), string length (LSB/MSB).
The calculator itself is used in a very simple way. Making sure ROM0 is switched in, do a RST08. Follow this by the list of control codes to command it. The codes themselves are like a sort of mini-language, but it is not necessary to learn them - they are all listed in the ATM. Because there are so many, I will give you a selection with their 'labels'. Please note - N2 refers to the top number on the FPCS, N1 is the number below it.
Hex Label Function
00 MULT N1*N2
01 ADDN N1+N2
03 SUBN N1-N2
05 DIVN N1/N2
06 SWOP Swop round top two numbers or strings
23 STKBREG Stack whatever B was on entry as a 5-byte integer
25 DUP Duplicate top FPCS entry
26 ONELIT Stack next byte on FPCS as a 5-byte number.
27 FIVELIT Stack next 5 bytes.
30 TRUNC Chop off any part after the decimal point.
33 EXIT Finish using calculator.
34 EXIT2 Finish using calculator and do a RET.
E0 STKHALF Stack 0.5
E1 STKZERO Stack zero
E2 STK16k Surprise, surprise - stack 16384
E6 STKFONE Stack five byte version of 1
E9 STKONE Stack integer version of 1
EC STKTEN Guess
F0 STKHALFPI Stack PI/2
And there's more... but I'll save that for the next time.
To round off today, though I'll show you how to use these codes in any proggies you might have. For example, let's use the calculator to work out what the circumference of a circle is, when we know the diameter.
CALL ROM_IN ; Routine to do ROM switching LD a,(DIAMETER) LD b,a RST #08 DB STKBREG ; Stack on the diameter DB STKHALFPI ; Stack PI/2 DB ONELIT,2 ; Stack 2 DB MULT ; Multiply to get PI on the FPCS DB MULT ; Circumference= diameter*PI DB STKHALF ; We'll round off by adding a half DB ADDN ; and chopping off fractions DB TRUNC ; Making sure the number is stored as DB RESTACK ; an integer DB EXIT ; Leave calculator CALL JGETINT ; ROM routine listed above that gets ; the top value in the FPCS and, ; providing its an integer, puts it ; into HL, BC and the LSB into A. LD (CIRCUM),HL ; Store result CALL ROM_OUT ; Restore LMPR RET ; Go bye-bye
All the labels named above are listed earlier on; you will need to EQU them elsewhere. ROM_IN and ROM_OUT were subroutines I gave you in the last column, but I'm sure you can write your own if you want.