Machine Code Made Easy - Part 1
Author
Publisher
Description
From Fred 6
Machine Code Made Easy - Part 1
Hello everyone out there in Coupé‚ land. This is (hopefully!) the
first in a series on machine code for beginners. No, please,
don’t all groan at once - m/c has its uses - it’s not just for
swotty egg-headed squares. Just think of all the games you’ll
write and all the money you’ll make after reading this article!
Now, firstly you’ll need an assembler. This is a program with
which the machine code can be entered. I can ..recommend the one
from LERM although any old one’ll do. Got one? Right. Now to
lesson one…..
REGISTERS:-
The first thing we’ll deal with are the registers.
You could compare these with variables from BASIC, and there are
8 main ones. They are named after letters: A;B;C;D;E;F;H;L and
each can contain a number between 0 and 255
To allocate a number to one of these registers we use the
command: LD A,100
This stands for LoaD A with 100 and is like the BASIC: LET A=100
The contents of one register can be copied into another by using : LD A,B
This will load A with whatever is in B and is like : LET A=B in BASIC
Although each register can only hold a number 0 to 255,
they can be used in pairs to hold a number 0 to 65535. The
pairings used are: B&C ; D&E ; H&L ; A&F
The F register is special and is used to store the results
of tests via FLAGS (F for flag, geddit?) and so shouldn’t be
tampered with. A flag is just a binary digit which indicates
whether a calculation has produced a carry or a zero and things
like that. Don’t be concerned with flags for the moment.
2 other registers we will use are IX and IY. These are known
as INDEXES and can hold a number 0 to 6..5535.
To use a number which is stored in memory we use brackets
around the ADDRESS of the number. ie, where about in memory it
is. The address must be between 0 and 65535 and the number is
always between 0 and 255. For example, to load the ACCUMULATOR
(another name for the A register) with the contents of 30000 we
use : LD A,(30000)
Similarly, to load A with the contents of the address held
in the HL register pair use : LD A,(..HL)
Another two very useful commands are DEC and INC. These
DECrement (take away 1 from) or INCrement (add 1 to) any
register or register pair. So, if we have a program:
LD A,100 | ; put 100 into A |
LD B,A | ; put A into B |
INC A | ; add 1 to A |
DEC B | ; take away 1 from B |
Our end result is that A=101 an..d B=99
Let us now look at a program the way it would appear on an
assembler:
ORG 50000 | ; ORG means ORiGin-the start address | |
DEMO1 | LD HL,TEXT1 | ; DEMO1 and TEXT1 are LABELS, they |
; represent numbers and addresses | ||
LD B,16 | ; B=16 | |
LOOP1 | LD A,(HL) | ; LOOP1 is another label. A becomes |
; the.. contents of the address in HL | ||
RST 16 | ; See next page | |
INC HL | ; HL becomes HL+1 | |
DJNZ LOOP1 | ; See next page | |
RET | ; End the routine and RETurn | |
TEXT1 | DM “This is a | ; DM isn’t an instruction, it just |
demo!!!” | ; tells the assembler to POKE the | |
; message into memory |
PLEASE NOTE: On some assemblers DM is written as DEFM
There are two new instructions in this one. RST 16 jumps to
a ROM routine which prints the character currently held in the A
register. (NB. The character is stored as an ASCII code. Refer
to your manual for explanation)
The other new command, DJNZ, is more complex. Firstly, it
does a DEC B and decrements the B register. If this produces a
non-zero number (ie B is now not 0) then the zero flag (in the F
register, remember) is not se..t and so the program jumps back to
LOOP1. If the B register IS now 0 then the zero flag is set and so
the CPU goes on with the next instruction in the program
which in this case is RET. RET simply means RETurn and will,
in our case, stick us back into BASIC.
To get this up and running, assemble it (see your assembler for details),
save it and reload it to address 50000.
To run a m/c routine we can use the BASIC command CALL 50000
where 50000 is our start ad..dress.
Now, let’s get onto something more complicated. (Great,huh?)
THE STACK:-
The stack is a part of memory where numbers can be
stored temporarily using simple commands. It grows downwards and
its current memory location is held in another 16-bit register
called SP for Stack Pointer. (A 16-bit register can hold a value
0 to 65535, while an 8-bit one can only hold a value 0 to 255)
The two main commands for using the stack are PUSH and POP.
To store a value held in HL, we can use: PUSH HL
This puts the value to the position pointed to by SP and then lets SP=SP-2
To retrieve this value we can use use: POP HL . This lets
SP=SP+2, takes the value in the address pointed to by SP and
sticks it into HL. There is no reason why this value couldn’t be
POPped off into BC or IX etc. and you should note that the value
stays in memory - just under SP.
The stack is also used when a subroutine is called. The
command to jump to a subroutine is CALL xxxx, where xxxx is the
start address of the routine. The computer has to know where to RETurn
to when the subroutine ends, so the address of the
command after the CALL is PUSHed onto the stack. When the CPU
comes across a RET command it POPs a value off the stack and
jumps to this address.
Jumping from place to place also uses certain commands:
JP xxxx ; JumPs to address xxxx without affecting the stack
JR xx ; Jumps Relative to current address without affecting
the stack.
This means that JR -100 will jump back 100 addresses,
and JR +100 will jump forward 100 addresses. The jump MUST be in
the range -127 to +128.
Another important feature of jumping is that we can qualify the jump.
In simple terms this means that we can JP, JR or CALL depending or
the state of certain flags.
For example, CALL NZ means call if not zero. ie, the zero
flag is NOT set. CALL Z would do the opposite and call if the
zero flag IS set. All the flags can be used in this way,although
a beginner would probably only use the CARRY and ZERO flags.
A jump can also be made to the address in HL. The command is
JP HL or JP (HL). Don’t be confused by the brackets - they
shouldn’t really be there.
We will end today by looking at some simple arithmetical
commands:-
ADD A,xx ; xx is added to the A register. The result is
stored in A. xx could be an 8-bit number or an 8-bit register.
SUB xx ; xx is subtacted from the A register. The result is
stored in A and xx could be an 8-bit number or an 8-bit register
CP xx ; This is a sneaky one. CP stands for ComPare and
the A register is compared to xx. In fact, xx is subtracted from
A, but the result isn’t stored anywhere. xx can be as above.
In all three of the above commands, flags are affected. If, during
an addition or subtraction (including CP), the result
crosses 0 or 255 then the CARRY flag is set.
eg. if A=100 and we did an ADD A,200 the carry would be set.
if A=100 and we did a SUB 200 or a CP 200 carry would
also be set.
BUT if A=100 and we did an ADD A,50 or a SUB 50 or even a
CP 50 then carry would be reset.
If the final result of any of these commands is 0, then the zero flag is set.
eg. If A=100 and we do a SUB 100 or a CP 100 then zero would be set.
PLEASE NOTE although the final result may be less than 0 or
more than 255 the A register always contains a number 0 to 255. So,
in effect any values above/below 255/0 are “wrapped around” so that they
will fit into this range
Finally, here’s a short proggie. Try to see how it works.
ORG 32768 | ; Start address 32768 | |
DEMO2 | LD B,96 | ; B set to 96 |
LD A,32 | ; A set to 32 | |
LOOP2 | RST 16 | ; A printed on screen |
ADD A,2 | ; A=A+2 | |
CP B | ; Does A=B? | |
JR NZ,LOOP2 | ; If not then go back to LOOP2 | |
LD HL,TEXT2 | ; HL set to start of text | |
LD B,20 | ; B set to 20 (20 letters to print) | |
LOOP3 | LD A,(HL) | ; A set to letter value |
RST 16 | ; letter printed | |
INC HL | ; HL moved to next letter | |
DJNZ | LOOP3 | ; Go round loop 20 times |
RET | ; All done | |
TEXT | DM “HOWZABOUTIS,THEN!!!!” | ; Stupid message |