831 lines
28 KiB
Plaintext
831 lines
28 KiB
Plaintext
|
." $Header$
|
||
|
.RP
|
||
|
.TL
|
||
|
Back end table for the Intel 8080 micro-processor
|
||
|
.AU
|
||
|
Gerard Buskermolen
|
||
|
.AB
|
||
|
A back end is a part of the Amsterdam Compiler Kit (ACK).
|
||
|
It translates EM, a family of intermediate languages, into the
|
||
|
assembly language of some target machine, here the Intel 8080 and Intel 8085 microprocessors.
|
||
|
.AE
|
||
|
.NH1
|
||
|
INTRODUCTION
|
||
|
.PP
|
||
|
To simplify the task of producing portable (cross) compilers and
|
||
|
interpreters, the Vrije Universiteit designed an integrated collection
|
||
|
of programs, the Amsterdam Compiler Kit (ACK).
|
||
|
It is based on the old UNCOL-idea ([4]) which attempts to solve the problem
|
||
|
of making a compiler for each of
|
||
|
.B N
|
||
|
languages on
|
||
|
.B M
|
||
|
different machines without having to write
|
||
|
.B N\ *\ M
|
||
|
programs.
|
||
|
.sp 1
|
||
|
The UNCOL approach is to write
|
||
|
.B N
|
||
|
"front ends", each of which translates one source language into
|
||
|
a common intermediate language, UNCOL (UNiversal Computer Oriented
|
||
|
Language), and
|
||
|
.B M
|
||
|
"back ends", each of which translates programs in UNCOL into a
|
||
|
specific machine language.
|
||
|
Under these conditions, only
|
||
|
.B N\ +\ M
|
||
|
programs should be written to provide all
|
||
|
.B N
|
||
|
languages on all
|
||
|
.B M
|
||
|
machines, instead of
|
||
|
.B N\ *\ M
|
||
|
programs.
|
||
|
.sp 1
|
||
|
The intermediate language for the Amsterdam Compiler Kit is the machine
|
||
|
language for a simple stack machine called EM (Encoding Machine).
|
||
|
So a back end for the Intel 8080 micro translates EM code into
|
||
|
8080 assembly language.
|
||
|
.sp 1
|
||
|
The back end is a single program that is driven by a machine dependent
|
||
|
driving table.
|
||
|
This driving table, or back end table,
|
||
|
defines the mapping from EM code to the machine's assembly language.
|
||
|
.NH 1
|
||
|
THE 8080 MICRO PROCESSOR
|
||
|
.PP
|
||
|
This back end table can be used without modification for the Intel 8085
|
||
|
processor.
|
||
|
Except for two additional instructions, the 8085 instruction set
|
||
|
is identical and fully compatible with the 8080 instruction set.
|
||
|
So everywhere in this document '8080' can be read as '8080 and 8085'.
|
||
|
.NH 2
|
||
|
Registers
|
||
|
.PP
|
||
|
The 8080 processor has an 8 bit accumulator,
|
||
|
six general purpose 8-bit registers,
|
||
|
a 16 bit programcounter and a 16 bit stackpointer.
|
||
|
Assembler programs can refer the accumulator by A and
|
||
|
the general purpose registers by B, C, D, E, H and L. (*)
|
||
|
.FS
|
||
|
* In this document 8080 registers and mnemonics are referenced by capitals, for the sake of clarity.
|
||
|
Nevertheless the assembler expects small letters.
|
||
|
.FE
|
||
|
Several instructions address registers in groups of two, thus creating
|
||
|
16 bit registers:
|
||
|
.DS
|
||
|
Registers referenced: Symbolic reference:
|
||
|
B and C B
|
||
|
D and E D
|
||
|
H and L H
|
||
|
.DE
|
||
|
The first named register, contains the high order byte
|
||
|
(H and L stand for High and Low).
|
||
|
.br
|
||
|
The instruction determines how the processor interprets the reference.
|
||
|
For example, ADD B is an 8 bit operation, adding the contents of
|
||
|
register B to accumulator A. By contrast PUSH B is a 16 bit operation
|
||
|
pushing B and C onto the stack.
|
||
|
.sp 1
|
||
|
There are no index registers.
|
||
|
.sp 1
|
||
|
.NH 2
|
||
|
Flip-flops
|
||
|
.PP
|
||
|
The 8080 microprocessor provides five flip-flops used as condition flags
|
||
|
(S, Z, P, C, AC) and one interrupt enable flip-flop IE.
|
||
|
.br
|
||
|
The sign bit S is set (cleared) by certain instructions when the most significant
|
||
|
bit of the result of an operation equals one (zero).
|
||
|
.br
|
||
|
The zero bit Z is set (cleared) by certain operations when the
|
||
|
8-bit result of an operation equals (does not equal) zero.
|
||
|
.br
|
||
|
The parity bit P is set (cleared) if the 8-bit result of an
|
||
|
operation includes an even (odd) number of ones.
|
||
|
.br
|
||
|
C is the normal carry bit.
|
||
|
.br
|
||
|
AC is an auxiliary carry that indicates whether there has been a carry
|
||
|
out of bit 3 of the accumulator.
|
||
|
This auxiliary carry is used only by the DAA instruction, which
|
||
|
adjusts the 8-bit value in the accumulator to form two 4-bit
|
||
|
binary coded decimal digits.
|
||
|
Needless to say this instruction is not used in the back-end.
|
||
|
.sp 1
|
||
|
The interrupt enable flip-flop IE is set and cleared under
|
||
|
program control using the instructions EI (Enable Interrupt) and
|
||
|
DI (Disable Interrupt).
|
||
|
It is automatically cleared when the CPU is reset and when
|
||
|
an interrupt occurs, disabling further interrupts until IE = 1 again.
|
||
|
.NH 2
|
||
|
Addressing modes
|
||
|
.NH 3
|
||
|
Implied addressing
|
||
|
.PP
|
||
|
The addressing mode of some instructions is implied by the instruction itself.
|
||
|
For example, the RAL (rotate accumulator left) instruction deals only with
|
||
|
the accumulator, and PCHL loads the programcounter with the contents
|
||
|
of register-pair HL.
|
||
|
.NH 3
|
||
|
Register addressing
|
||
|
.PP
|
||
|
|
||
|
With each intruction using register addressing,
|
||
|
only one register is specified (except for the MOV instruction),
|
||
|
although in many of them the accumulator is implied as
|
||
|
second operand.
|
||
|
Examples are CMP E, which compares register E with the accumulator,
|
||
|
and DCR B, which decrements register B.
|
||
|
.br
|
||
|
A few instructions deal with 16 bit register-pairs:
|
||
|
examples are DCX B, which decrements register-pair BC and the
|
||
|
PUSH and POP instructions.
|
||
|
.NH 3
|
||
|
Register indirect addressing
|
||
|
.PP
|
||
|
Each instruction that may refer to an 8 bit register, may
|
||
|
refer also to a memory location. In this case the letter M
|
||
|
(for Memory) has to be used instead of a register.
|
||
|
It indicates the memory location pointed to by H and L,
|
||
|
so ADD M adds the contents of the memory location specified
|
||
|
by H and L to the contents of the accumulator.
|
||
|
.br
|
||
|
The register-pairs BC and DE can also be used for indirect addressing,
|
||
|
but only to load or store the accumulator.
|
||
|
For example, STAX B stores the contents of the accumulator
|
||
|
into the memory location addressed by register-pair BC.
|
||
|
.NH 3
|
||
|
Immediate addressing
|
||
|
.PP
|
||
|
The immediate value can be an 8 bit value, as in ADI 10 which
|
||
|
adds 10 to the accumulator, or a 16 bit value, as in
|
||
|
LXI H,1000, which loads 1000 in the register-pair HL.
|
||
|
.NH 3
|
||
|
Direct addressing
|
||
|
.PP
|
||
|
Jump instructions include a 16 bit address as part of the instruction.
|
||
|
.br
|
||
|
The instruction SHLD 1234 stores the contents of register
|
||
|
pair HL on memory locations 1234 and 1235.
|
||
|
The high order byte is stored at the highest address.
|
||
|
.NH 1
|
||
|
THE 8080 BACK END TABLE
|
||
|
.PP
|
||
|
The back end table is designed as described in [5].
|
||
|
So for an overall design of a back end table I refer to this document.
|
||
|
.br
|
||
|
This section deals with problems encountered in writing the
|
||
|
8080 back-end table.
|
||
|
Some remarks are made about particular parts
|
||
|
of the table that might not seem clear at first sight.
|
||
|
.NH 2
|
||
|
Constant definitions
|
||
|
.PP
|
||
|
Word size (EM_WSIZE) and pointer size (EM_PSIZE) are both
|
||
|
defined as two bytes.
|
||
|
The hole between AB and LB (EM_BSIZE) is four bytes: only the
|
||
|
return address and the localbase are saved.
|
||
|
.NH 2
|
||
|
Registers and their properties
|
||
|
.PP
|
||
|
All properties have the default size of two bytes, because one-byte
|
||
|
registers also cover two bytes when put on the real stack.
|
||
|
.sp 1
|
||
|
The next considerations led to the choise of register-pair BC
|
||
|
as localbase.
|
||
|
Though saving the localbase in memory would leave one more register-pair
|
||
|
available as scratch register, it would slow down instructions
|
||
|
as 'lol' and 'stl' too much.
|
||
|
So a register-pair should be sacrificed as localbase.
|
||
|
Because a back-end without a free register-pair HL is completely
|
||
|
broken-winged, the only reasonable choises are BC and DE.
|
||
|
Though the choise between them might seem arbitrary at first sight,
|
||
|
there is a difference between register-pairs BC and DE:
|
||
|
the instruction XCHG exchanges the contents of register-pairs DE and
|
||
|
HL.
|
||
|
When DE and HL are both heavily used on the fake-stack, this instruction
|
||
|
is very usefull.
|
||
|
Since it won't be usefull too often to exchange HL with the localbase
|
||
|
and since an instruction exchanging BC and HL does not exist, BC is
|
||
|
chosen as localbase.
|
||
|
.sp 1
|
||
|
Many of the register properties are never mentioned in the
|
||
|
PATTERNS part of the table.
|
||
|
They are only needed to define the INSTRUCTIONS correctly.
|
||
|
.sp 1
|
||
|
The properties really used in the PATTERNS part are:
|
||
|
.IP areg: 24
|
||
|
the accumulator only
|
||
|
.IP reg:
|
||
|
any of the registers A, D, E, H or L. Of course the registers B and C which are
|
||
|
used as localbase don't possess this property.
|
||
|
When there is a single register on the fake-stack, its value
|
||
|
is always considered non-negative.
|
||
|
.IP dereg:
|
||
|
register-pair DE only
|
||
|
.IP hlreg:
|
||
|
register-pair HL only
|
||
|
.IP hl_or_de:
|
||
|
register-pairs HL and DE both have this property
|
||
|
.IP localbase:
|
||
|
used only once (i.e. in the EM-instruction 'str 0')
|
||
|
.PP
|
||
|
.sp 1
|
||
|
The stackpointer SP and the processor status word PSW have to be
|
||
|
defined explicitely because they are needed in some instructions
|
||
|
(i.e. SP in LXI, DCX and INX and PSW in PUSH and POP).
|
||
|
.br
|
||
|
It doesn't matter that the processor status word is not just register A
|
||
|
but includes the condition flags.
|
||
|
.NH 2
|
||
|
Tokens
|
||
|
.PP
|
||
|
The tokens 'm' and 'const1' are used in the INSTRUCTIONS- and MOVES parts only.
|
||
|
They will never be on the fake-stack.
|
||
|
.sp 1
|
||
|
The token 'label' reflects addresses known at assembly time.
|
||
|
It is used to take full profit of the instructions LHLD
|
||
|
(Load HL Direct) and SHLD (Store HL Direct).
|
||
|
.sp 1
|
||
|
Compared with many other back-end tables, there are only a small number of
|
||
|
different tokens (four).
|
||
|
Reasons are the limited addressing modes of the 8080 microprocessor,
|
||
|
no index registers etc.
|
||
|
.br
|
||
|
For example to translate the EM-instruction
|
||
|
.DS
|
||
|
lol 10
|
||
|
.DE
|
||
|
the next 8080 instructions are generated:
|
||
|
.DS L
|
||
|
LXI H,10 /* load registers pair HL with value 10 */
|
||
|
DAD B /* add localbase (BC) to HL */
|
||
|
MOV E,M /* load E with byte pointed to by HL */
|
||
|
INX H /* increment HL */
|
||
|
MOV D,M /* load D with next byte */
|
||
|
.DE
|
||
|
Of course, instead of emitting code immmediately, it could be postponed
|
||
|
by placing something like a {LOCAL,10} on the fake-stack, but some day the above
|
||
|
mentioned code will have to be generated, so a LOCAL-token is
|
||
|
hardly usefull.
|
||
|
.br
|
||
|
See also the comment on the load instructions.
|
||
|
.NH 2
|
||
|
Sets
|
||
|
.PP
|
||
|
Only 'src1or2' is used in the PATTERNS.
|
||
|
.NH 2
|
||
|
Instructions
|
||
|
.PP
|
||
|
Each instruction indicates whether or not the condition flags
|
||
|
are affected, but this information will never have any influence
|
||
|
because there are no tests in the PATTERNS part of the table.
|
||
|
.sp 1
|
||
|
For each instruction a cost vector indicates the number of bytes
|
||
|
the instruction occupies and the number of time periods it takes
|
||
|
to execute the instruction.
|
||
|
The length of a time period depends on the clock frequency
|
||
|
and may range from 480 nanoseconds to 2 microseconds on a
|
||
|
8080 system and from 320 nanoseconds to 2 microseconds
|
||
|
on a 8085 system.
|
||
|
.sp 1
|
||
|
In the TOKENS-part the cost of token 'm' is defined as (0,3).
|
||
|
In fact it usually takes 3 extra time periods when this register indirect mode
|
||
|
is used instead of register mode, but since the costs are not completely
|
||
|
orthogonal this results in small deficiencies for the DCR, INR and MOV
|
||
|
instructions.
|
||
|
Although it is not particularly usefull these deficiencies are
|
||
|
corrected in the INSTRUCTIONS part, by treating the register indirect
|
||
|
mode seperately.
|
||
|
.sp 1
|
||
|
The costs of the conditional call and return instructions really
|
||
|
depend on whether or not the call resp. return is actually made.
|
||
|
Unimportant.
|
||
|
.sp 1
|
||
|
Instructions not used in this table have been commented out.
|
||
|
Of course many of them are used in the library routines.
|
||
|
.NH 2
|
||
|
Moves
|
||
|
.PP
|
||
|
This section is supposed to be straight-forward.
|
||
|
.NH 2
|
||
|
Tests
|
||
|
.PP
|
||
|
The TESTS section is only included to refrain
|
||
|
.B cgg
|
||
|
from complaining.
|
||
|
.NH 2
|
||
|
Stackingrules
|
||
|
.PP
|
||
|
When, for example, the token {const2,10} has to be stacked while
|
||
|
no free register-pair is available, the next code is generated:
|
||
|
.DS
|
||
|
PUSH H
|
||
|
LXI H,10
|
||
|
XTHL
|
||
|
.DE
|
||
|
The last instruction exchanges the contents of HL with the value
|
||
|
on top of the stack, giving HL its original value again.
|
||
|
.NH 2
|
||
|
Coercions
|
||
|
.PP
|
||
|
The coercion to unstack register A, is somewhat tricky,
|
||
|
but unfortunately just popping PSW leaves the high-order byte in
|
||
|
the accumulator.
|
||
|
.sp 1
|
||
|
The cheapest way to coerce HL to DE (or DE to HL) is by using
|
||
|
the XCHG instruction, but it is not possible to explain
|
||
|
.B cgg
|
||
|
this instruction in fact exchanges the contents of these
|
||
|
register-pairs.
|
||
|
Before the coercion is carried out other appearances of DE and HL
|
||
|
on the fake-stack will be moved to the real stack, because in
|
||
|
the INSTRUCTION-part is told that XCHG destroyes the contents
|
||
|
of both DE and HL.
|
||
|
.br
|
||
|
The coercion transposing one register-pair to another one by
|
||
|
emitting two MOV-instructions, will be used only if
|
||
|
one of the register-pairs is the localbase.
|
||
|
.NH 2
|
||
|
Patterns
|
||
|
.PP
|
||
|
As a general habit I have allocated (uses ...) all registers
|
||
|
that should be free to generate the code, although it is not
|
||
|
always necessary.
|
||
|
For example in the code rule
|
||
|
.DS
|
||
|
pat loe
|
||
|
uses hlreg
|
||
|
gen lhld {label,$1} yields hl
|
||
|
.DE
|
||
|
the 'uses'-clause could have been omitted because
|
||
|
.B cgg
|
||
|
knows that LHLD destroyes register-pair HL.
|
||
|
.sp 1
|
||
|
Since there is only one register with property 'hlreg',
|
||
|
there is no difference between 'uses hlreg' (allocate a
|
||
|
register with property 'hlreg') and 'kills hlreg' (remove
|
||
|
all registers with property 'hlreg' from the fake-stack).
|
||
|
The same applies for the property 'dereg'.
|
||
|
.br
|
||
|
As a consequence 'kills' is rarely used in this back-end table.
|
||
|
.NH 3
|
||
|
Group 1: Load instructions
|
||
|
.PP
|
||
|
When a local variable must be squared, there will probably be EM-code like:
|
||
|
.DS
|
||
|
lol 10
|
||
|
lol 10
|
||
|
mli 2
|
||
|
.DE
|
||
|
When the code for the first 'lol 10' has been executed, DE contains the
|
||
|
wanted value.
|
||
|
To refrain
|
||
|
.B cgg
|
||
|
from emitting the code for 'lol 10' again, an extra
|
||
|
pattern is included in the table for cases like this.
|
||
|
.br
|
||
|
The same applies for two consecutive 'loe'-s or 'lil'-s.
|
||
|
.sp 1
|
||
|
A bit tricky is 'lof'.
|
||
|
It expects either DE or HL on the fake-stack, moves {const2,$1}
|
||
|
into the other one, and eventually adds them.
|
||
|
The 'kills' part is necessary here because if DE was on the fake-stack,
|
||
|
.B cgg
|
||
|
doesn't see that the contents of DE is destroyed by the code
|
||
|
(in fact 'kills dereg' would have been sufficient: because of the
|
||
|
DAD instruction
|
||
|
.B cgg
|
||
|
knows that HL is destroyed).
|
||
|
.sp 1
|
||
|
By lookahead,
|
||
|
.B cgg
|
||
|
can make a clever choise between the first and
|
||
|
second code rule of 'loi 4'.
|
||
|
The same applies for several other instructions.
|
||
|
.NH 3
|
||
|
Group 2: Store instructions
|
||
|
.PP
|
||
|
A similar idea as with the two consecutive identical load instructions
|
||
|
in Group 1, applies for a store instruction followed by a corresponding load instruction.
|
||
|
.NH 3
|
||
|
Groups 3 and 4: Signed and unsigned integer arithmetic
|
||
|
.PP
|
||
|
Since the 8080 instruction set doesn't provide multiply and
|
||
|
divide instructions, special routines are made to accomplish these tasks.
|
||
|
.sp 1
|
||
|
Instead of providing four slighty differing routines for 16 bit signed or
|
||
|
unsigned division, yielding the quotient or the remainder,
|
||
|
the routines are merged.
|
||
|
This saves space and assembly time
|
||
|
when several variants are used in a particular program,
|
||
|
at the cost of a little speed.
|
||
|
.br
|
||
|
When the routine is called, bit 7 of register A indicates whether
|
||
|
the operands should be considered as signed or as unsigned integers,
|
||
|
and bit 0 of register A indicates whether the quotient or the
|
||
|
remainder has to be delivered.
|
||
|
.br
|
||
|
The same applies for 32 bit division.
|
||
|
.sp 1
|
||
|
The routine doing the 16 bit unsigned multiplication could
|
||
|
have been used for 16 bit signed multiplication too.
|
||
|
Nevertheless a special 16 bit signed multiplication routine is
|
||
|
provided, because this one will usually be much faster.
|
||
|
.NH 3
|
||
|
Group 5: Floating point arithmetic
|
||
|
.PP
|
||
|
Floating points are not implemented.
|
||
|
.br
|
||
|
Whenever an EM-instruction involving floating points is offered
|
||
|
to the code-generator, it generates the code 'call eunimpl',
|
||
|
which traps with trap number 63.
|
||
|
Some of the Pascal and C library routines output floating point
|
||
|
EM-instructions, so code has to be generated for them.
|
||
|
Of course this doesn't imply the code will ever be executed.
|
||
|
.NH 3
|
||
|
Group 12: Compare instructions
|
||
|
.PP
|
||
|
The code for 'cmu 2', with its 4 labels, is terrible.
|
||
|
But it is the best I could find.
|
||
|
.NH 3
|
||
|
Group 9: Logical instructions
|
||
|
.PP
|
||
|
I have tried to merge both variants of the instructions 'and 2', 'ior 2' and 'xor 2',
|
||
|
as in
|
||
|
.DS
|
||
|
pat and $1==2
|
||
|
with hl_or_de hl_or_de
|
||
|
uses reusing %1, reusing %2, hl_or_de, areg
|
||
|
gen mov a,%1.2
|
||
|
ana %2.2
|
||
|
mov %a.2,a
|
||
|
mov a,%1.1
|
||
|
ana %2.1
|
||
|
mov %a.1,a yields %a
|
||
|
.DE
|
||
|
but the current version of
|
||
|
.B cgg
|
||
|
doesn't approve this.
|
||
|
.br
|
||
|
In any case
|
||
|
.B cgg
|
||
|
chooses either DE or HL to store the result, using lookahead.
|
||
|
.NH 3
|
||
|
Group 14: Procedure call instructions
|
||
|
.PP
|
||
|
There is an 8 bytes function return area, called '.fra'.
|
||
|
If only 2 bytes have to be returned, register-pair DE is used.
|
||
|
.NH 1
|
||
|
LIBRARY ROUTINES
|
||
|
.PP
|
||
|
Most of the library routines start with saving the return address
|
||
|
and the localbase, so that the parameters are on the top of the stack
|
||
|
and the registers B and C are available as scratch registers.
|
||
|
Since register-pair HL is needed to accomplish these tasks,
|
||
|
and also to restore everything just before the routine returns,
|
||
|
it is not possible to transfer data between the routines and the
|
||
|
surrounding world through register H or L.
|
||
|
Only registers A, D and E can be used for this.
|
||
|
.sp
|
||
|
When a routine returns 2 bytes, they are usually returned in
|
||
|
registers-pair DE.
|
||
|
When it returns more than 2 bytes they are pushed onto the stack.
|
||
|
.br
|
||
|
|
||
|
It would have been possible to let the 32 bit arithmetic routines
|
||
|
return 2 bytes in DE and the remaining 2 bytes on the stack
|
||
|
(this often would have saved some space and execution time),
|
||
|
but I don't consider that as well-structured programming.
|
||
|
.NH 1
|
||
|
TRAPS
|
||
|
.PP
|
||
|
Whenever a trap, for example trying to divide by zero,
|
||
|
occurs in a program that originally was written in C or Pascal,
|
||
|
a special trap handler is called.
|
||
|
.br
|
||
|
This trap handler wants to write an appropriate error message on the
|
||
|
monitor.
|
||
|
It tries to read the message from a file (e.g. etc/pc_rt_errors in the
|
||
|
EM home directory for Pascal programs), but since the 8080 back-end
|
||
|
doesn't know about files, we are in trouble.
|
||
|
This problem is solved, as far as possible, by including the 'open'-monitor call in the mon-routine.
|
||
|
It returns with file descriptor -1.
|
||
|
The trap handler reacts by generating another trap, with the original
|
||
|
trap number.
|
||
|
But this time, instead of calling the C- or Pascal trap handler again,
|
||
|
the next message is printed on the monitor:
|
||
|
.DS L
|
||
|
trap number <TN>
|
||
|
line <LN> of file <FN>
|
||
|
|
||
|
where <TN> is the trap number (decimal)
|
||
|
<LN> is the line number (decimal)
|
||
|
<FN> is the filename of the original program
|
||
|
.DE
|
||
|
.sp 1
|
||
|
Trap numbers are subdivided as follows:
|
||
|
.IP 1-27: 20
|
||
|
EM-machine error, as described in [3]
|
||
|
.IP 63:
|
||
|
an unimplemented EM-instruction is used
|
||
|
.IP 64-127:
|
||
|
generated by compilers, runtime systems, etc.
|
||
|
.IP 128-252:
|
||
|
generated by user programs
|
||
|
.NH 1
|
||
|
IMPLEMENTATION
|
||
|
.PP
|
||
|
It will not be possible to run the entire Amsterdam Compiler Kit on a
|
||
|
8080-based computer system.
|
||
|
One has to write a program on another
|
||
|
system, a system where the compiler kit runs on.
|
||
|
This program may be a mixture of high-level languages, such as
|
||
|
C or Pascal, EM and 8080 assembly code.
|
||
|
The program should be compiled using the compiler kit, producing 8080 machine code.
|
||
|
This code should come available to the 8080 machine
|
||
|
for example by downloading or
|
||
|
by storing it in ROM (Read Only Memory).
|
||
|
.sp 1
|
||
|
Depending on the characteristics of the particular 8080 based system, some
|
||
|
adaptions have to be made:
|
||
|
.IP 1) 10
|
||
|
In 'head_em': the base address, which is the address where the first
|
||
|
8080 instruction will be stored, and the initial value of the
|
||
|
stackpointer are set to 0x1000 and 0x8000 respectivally.
|
||
|
.br
|
||
|
Other systems require other values.
|
||
|
.IP 2)
|
||
|
In 'head_em': before calling "_m_a_i_n", the environment
|
||
|
pointer, argument vector and argument count will have to be pushed
|
||
|
onto the stack.
|
||
|
Since this back-end is tested on a system without any knowledge
|
||
|
of these things, dummies are pushed now.
|
||
|
.IP 3)
|
||
|
In 'tail_em': proper routines "putchar" and "getchar" should
|
||
|
be provided.
|
||
|
They should write resp. read a character on/from the monitor.
|
||
|
Maybe some conversions will have to be made.
|
||
|
.IP 4)
|
||
|
In 'head_em': an application program returns control to the monitor by
|
||
|
jumping to address 0xFB52.
|
||
|
If this is not the right way on your system, change it.
|
||
|
.IP 5)
|
||
|
In 'tail_em': the current version of the 8080 back-end has very limited I/O
|
||
|
capabilities, because it was tested on a system that
|
||
|
had no knowlegde of files.
|
||
|
So the implementation of the EM-instruction 'mon' is very simple;
|
||
|
it can only do the following things:
|
||
|
.RS
|
||
|
.IP Monitor\ call\ 1: 40
|
||
|
Exit
|
||
|
.IP Monitor\ call\ 3:
|
||
|
read, always reads from the monitor.
|
||
|
.br
|
||
|
echos the read character.
|
||
|
.br
|
||
|
ignores file descriptor.
|
||
|
.IP Monitor\ call\ 4:
|
||
|
write, always writes on the monitor.
|
||
|
.br
|
||
|
ignores file descriptor.
|
||
|
.IP Monitor\ call\ 5:
|
||
|
open file, returns file descriptor -1.
|
||
|
.br
|
||
|
(compare chapter about TRAPS)
|
||
|
.IP Monitor\ call\ 6:
|
||
|
close file, returns error code = 0.
|
||
|
.IP Monitor\ call\ 54:
|
||
|
io-control, returns error code = 0.
|
||
|
.RE
|
||
|
.sp
|
||
|
If the system should do file-handling the routine ".mon"
|
||
|
should be extended thoroughly.
|
||
|
.NH 1
|
||
|
INTEL 8080 VERSUS ZILOG Z80 AND INTEL 8086
|
||
|
.NH 2
|
||
|
Introduction
|
||
|
.PP
|
||
|
At about the same time I develloped the back end
|
||
|
for the Intel 8080 and Intel 8085,
|
||
|
Frans van Haarlem did the same job for the Zilog z80 microprocessor.
|
||
|
Since the z80 processor is an extension of the 8080,
|
||
|
any machine code offered to a 8080 processor can be offered
|
||
|
to a z80 too.
|
||
|
The assembly languages are quite different however.
|
||
|
.br
|
||
|
During the devellopments of the back ends we have used
|
||
|
two micro-computers, both equiped with a z80 microprocessor.
|
||
|
Of course the output of the 8080 back end is assembled by an
|
||
|
8080 assembler. This should assure I have never used any of
|
||
|
the features that are potentially available in the z80 processor,
|
||
|
but are not part of a true 8080 processor.
|
||
|
.sp 1
|
||
|
As a final job, I have
|
||
|
investigated the differences between the 8080 and z80 processors
|
||
|
and their influence on the back ends.
|
||
|
I have tried to measure this influence by examining the length of
|
||
|
the generated code.
|
||
|
I have also involved the 8086 micro-processor in this measurements.
|
||
|
.NH 2
|
||
|
Differences between the 8080 and z80 processors
|
||
|
.PP
|
||
|
Except for some features that are less important concerning back ends,
|
||
|
there are two points where the z80 improves the 8080:
|
||
|
.IP First, 18
|
||
|
the z80 has two additional index registers, IX and IY.
|
||
|
They are used as in
|
||
|
.DS
|
||
|
LD B,(IX+10)
|
||
|
.DE
|
||
|
The offset, here 10, should fit in one byte.
|
||
|
.IP Second,
|
||
|
the z80 has several additional instructions.
|
||
|
The most important ones are:
|
||
|
.RS
|
||
|
.IP 1) 8
|
||
|
The 8080 can only load or store register-pair HL direct
|
||
|
(using LHLD or SHLD).
|
||
|
The z80 can handle BC, DE and SP too.
|
||
|
.IP 2)
|
||
|
Instructions are included to ease block movements.
|
||
|
.IP 3)
|
||
|
There is a 16 bit subtract instruction.
|
||
|
.IP 4)
|
||
|
While the 8080 can only rotate the accumulator, the z80
|
||
|
can rotate and shift each 8 bit register.
|
||
|
.IP 5)
|
||
|
Special routines are included to jump to near locations, saving 1 byte.
|
||
|
.RE
|
||
|
.NH 2
|
||
|
Consequences for the 8080 and z80 back end
|
||
|
.PP
|
||
|
The most striking difference between the 8080 and z80 back ends
|
||
|
is the choise of the localbase.
|
||
|
The writer of the z80 back end chose index register IY as localbase,
|
||
|
because this results in the cheapest coding of EM-instructions
|
||
|
like 'lol' and 'stl'.
|
||
|
.br
|
||
|
The z80 instructions that load local 10, for example
|
||
|
.DS
|
||
|
LD E,(IY+10)
|
||
|
LD D,(IY+11)
|
||
|
.DE
|
||
|
occupy 6 bytes and take 38 time periods to execute.
|
||
|
The five corresponding 8080 instructions loading a local
|
||
|
occupy 7 bytes and take 41 time periods.
|
||
|
Although the profit of the z80 might be not world-shocking,
|
||
|
it should be noted that as a side effect it may save some
|
||
|
pushing and popping since register pair HL is not used.
|
||
|
.sp 1
|
||
|
The choise of IY as localbase has its drawbacks too.
|
||
|
The root of the problem is that it is not possible to add
|
||
|
IY to HL.
|
||
|
For the EM-instruction
|
||
|
.DS
|
||
|
lal 20
|
||
|
.DE
|
||
|
the z80 back end generates code like
|
||
|
.DS
|
||
|
LD BC,20
|
||
|
PUSH IY
|
||
|
POP HL
|
||
|
ADD HL,BC
|
||
|
.DE
|
||
|
leaving the wanted address in HL.
|
||
|
.br
|
||
|
This annoying push and pop instructions are also needed in some
|
||
|
other instructions, for instance in 'lol' when the offset
|
||
|
doesn't fit in one byte.
|
||
|
.sp 1
|
||
|
Beside the choise of the localbase, I think there is no
|
||
|
fundamental difference between the 8080 and z80 back ends,
|
||
|
except of course that the z80 back end has register pair BC
|
||
|
and, less important, index register IX available as scratch registers.
|
||
|
.sp 1
|
||
|
Most of the PATTERNS in the 8080 and z80 tables are more or less
|
||
|
a direct translation of each other.
|
||
|
.NH 2
|
||
|
What did I do?
|
||
|
.PP
|
||
|
To get an idea of the quality of the code generated by
|
||
|
the 8080, z80 and 8086 back ends I have gathered
|
||
|
some C programs and some Pascal programs.
|
||
|
Then I produced 8080, z80 and 8086 code for them.
|
||
|
Investigating the assembler listing I found the
|
||
|
lengths of the different parts of the generated code.
|
||
|
.br
|
||
|
I have checked two areas:
|
||
|
.IP 1) 8
|
||
|
the entire text part
|
||
|
.IP 2)
|
||
|
the text part without any library routine, so only the plain user program
|
||
|
.LP
|
||
|
I have to admit that neither one of them is really honest.
|
||
|
When the entire text part is checked, the result is disturbed
|
||
|
because not always the same library routines are loaded.
|
||
|
And when only the user program itself is considered, the result is
|
||
|
disturbed too.
|
||
|
For example the 8086 has a multiply instruction,
|
||
|
so the EM-instruction 'mli 2' is translated in the main program,
|
||
|
but the 8080 and z80 call a library routine that is not counted.
|
||
|
Also the 8080 uses library routines at some places where the
|
||
|
z80 does not.
|
||
|
.sp 1
|
||
|
But nevertheless I think the measurements will give an idea
|
||
|
about the code produced by the three back ends.
|
||
|
.NH 2
|
||
|
The results
|
||
|
.PP
|
||
|
The table below should be read as follows.
|
||
|
For all programs I have computed the ratio of the code-lengths
|
||
|
of the 8080, z80 and 8086.
|
||
|
The averages of all Pascal/C programs are listed in the table,
|
||
|
standarized to '100' for the 8080.
|
||
|
So the listed '107' indicates that the lengths
|
||
|
of the text parts of the z80 programs that originally were Pascal programs,
|
||
|
averaged 7 percent larger than in the corresponding 8080 programs.
|
||
|
.DS C
|
||
|
--------------------------------------------------
|
||
|
| | 8080 | z80 | 8086 |
|
||
|
--------------------------------------------------
|
||
|
| C, text part | 100 | 103 | 65 |
|
||
|
| Pascal, text part | 100 | 107 | 55 |
|
||
|
| C, user program | 100 | 110 | 71 |
|
||
|
| Pascal, user program | 100 | 118 | 67 |
|
||
|
--------------------------------------------------
|
||
|
.DE
|
||
|
.TE
|
||
|
The most striking thing in this table is that the z80 back end appears
|
||
|
to produce larger code than the 8080 back end.
|
||
|
The reason is that the current z80 back end table is
|
||
|
not very elaborate yet.
|
||
|
For instance it doesn't look for any EM-pattern longer than one.
|
||
|
So the table shows that the preparations in the 8080 back end table
|
||
|
to produce faster code (like recognizing special EM-patterns
|
||
|
and permitting one byte registers on the fake-stack)
|
||
|
was not just for fun, but really improved the generated code
|
||
|
significantly.
|
||
|
.sp 1
|
||
|
The table shows that the 8080 table is relativelly better
|
||
|
when only the plain user program is considered instead of the entire text part.
|
||
|
This is not very surprising since the 8080 back end sometimes
|
||
|
uses library routines where the z80 and especially the 8086 don't.
|
||
|
.sp 1
|
||
|
The difference between the 8080 and z80 on the one hand and the 8086
|
||
|
on the other is very big.
|
||
|
But of course it was not equal game:
|
||
|
the 8086 is a 16 bit processor that is much more advanced than the
|
||
|
8080 or z80 and the 8086 back end is known to produce
|
||
|
very good code.
|
||
|
.bp
|
||
|
.B REFERENCES
|
||
|
.sp 2
|
||
|
.IP [1] 10
|
||
|
8080/8085 Assembly Language Programming Manual,
|
||
|
.br
|
||
|
Intel Corporation (1977,1978)
|
||
|
.IP [2]
|
||
|
Andrew S. Tanenbaum, Hans van Staveren, E.G. Keizer and Johan W. Stevenson,
|
||
|
.br
|
||
|
A practical tool kit for making portable compilers,
|
||
|
.br
|
||
|
Informatica report 74, Vrije Universiteit, Amsterdam, 1983.
|
||
|
.sp
|
||
|
An overview on the Amsterdam Compiler Kit.
|
||
|
.IP [3]
|
||
|
Tanenbaum, A.S., Stevenson, J.W., Keizer, E.G., and van Staveren, H.
|
||
|
.br
|
||
|
Desciption of an experimental machine architecture for use with block
|
||
|
structured languages,
|
||
|
.br
|
||
|
Informatica report 81, Vrije Universiteit, Amsterdam, 1983.
|
||
|
.sp
|
||
|
The defining document for EM.
|
||
|
.IP [4]
|
||
|
Steel, T.B., Jr.
|
||
|
.br
|
||
|
UNCOL: The myth and the Fact. in Ann. Rev. Auto. Prog.
|
||
|
.br
|
||
|
Goodman, R. (ed.), vol. 2, (1960), p325-344.
|
||
|
.sp
|
||
|
An introduction to the UNCOL idea by its originator.
|
||
|
.IP [5]
|
||
|
van Staveren, Hans
|
||
|
.br
|
||
|
The table driven code generator from the Amsterdam Compiler Kit
|
||
|
(Second Revised Edition),
|
||
|
.br
|
||
|
Vrije Universiteit, Amsterdam.
|
||
|
.sp
|
||
|
The defining document for writing a back end table.
|
||
|
.IP [6]
|
||
|
Voors, Jan
|
||
|
.br
|
||
|
A back end for the Zilog z8000 micro,
|
||
|
.br
|
||
|
Vrije Universiteit, Amsterdam.
|
||
|
.sp
|
||
|
A document like this one, but for the z8000.
|