Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Relocatable Z80 code (relative CALLs)

687 views
Skip to first unread message

Douglas Beattie Jr.

unread,
Mar 13, 2000, 3:00:00 AM3/13/00
to
;Relative Call implementation, toward a goal of 100% relocatable code.
;Note how the code in memory is similar to the Z80 JR, but with 16-bit
; displacement.

RST1 EQU 08h

rCALL MACRO destination
RST RST1
DW &destination&-$-2 ;i.e. -($+2)
.ENDM


ORG 3000h

;when used, the above macro produces the following code:
;
XAMPL rCALL noop
XOR A
LD B,A
LD C,A
LD D,A
LD E,A
HALT
noop: NOP
RET

;when used, the above macro produces the following code:
;
; A 9 ORG 3000h
; A 13 XAMPL rCALL noop
;3000 CF A+ 13 RST RST1
;3001 06 00 A+ 13 DW noop-$-2 ;i.e. -($+2)
;3003 AF A 14 XOR A
;3004 47 A 15 LD B,A
;3005 4F A 16 LD C,A
;3006 57 A 17 LD D,A
;3007 5F A 18 LD E,A
;3008 76 A 19 HALT
;3009 00 A 20 noop: NOP
;300A C9 A 21 RET


;Z80 Relocatable Subroutine Call -- METHOD 1
;advantages:
; can be used in ROM
;disadvantage:
; destroys both DE and HL
; more than 8 bytes; overlaps next RST vector
; (unless RST 38h is used.)
;

ORG RST1

POP HL ;get return PC
LD E,(HL)
INC HL ;bump return address
LD D,(HL)
INC HL ; to point to continuing code
EX DE,HL ;return address to DE
ADD HL,DE ;calculate subroutine address
PUSH DE ;save return address
JP (HL) ;jump to subroutine.


;Z80 Relocatable Subroutine Call -- METHOD 2
;advantage:
; uses only alternate registers DE' and HL'
; can be used in ROM
;disadvantage:
; more than 8 bytes; overlaps next RST vector
;
ORG RST1

EXX
POP HL ;get return PC
LD E,(HL)
INC HL ;bump return address
LD D,(HL)
INC HL ; to point to continuing code
EX DE,HL ;return address to DE
ADD HL,DE ;calculate subroutine address
PUSH DE ;save return address
PUSH HL ;push subroutine address
EXX
RET ; jump to subroutine.

END

;Zilog Macro Assembler. Version 2.10

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

Is there an even more efficient method of
the "Z80 Relocatable Subroutine Call" ?

--
Douglas Beattie Jr. http://www2.whidbey.net/~beattidp/

Douglas Beattie Jr.

unread,
Mar 15, 2000, 3:00:00 AM3/15/00
to
(Here's another one...)

;Z80 Relocatable Subroutine Call -- METHOD 3
;Advantages:
; CP/M compatible -- uses only A and HL.
; Only 10 instructions.
; Size of macro remains 3-bytes,
; ultimate code size remains the same.
ORG RST1

POP HL ;get return PC

INC HL ;bump return address

INC HL ; to point to continuing code

PUSH HL ;save return address
DEC HL ;get MSB
LD A,(HL) ; to reg A
DEC HL ;get LSB
LD L,(HL) ; to reg L
LD H,A ;and MSB to H


JP (HL) ;jump to subroutine.

--

Matthew Phillips

unread,
Mar 15, 2000, 3:00:00 AM3/15/00
to
"Douglas Beattie Jr." <beat...@whidbey.net> writes:

> (Here's another one...)
>
> ;Z80 Relocatable Subroutine Call -- METHOD 3
> ;Advantages:
> ; CP/M compatible -- uses only A and HL.
> ; Only 10 instructions.
> ; Size of macro remains 3-bytes,
> ; ultimate code size remains the same.
> ORG RST1
>
> POP HL ;get return PC
> INC HL ;bump return address
> INC HL ; to point to continuing code
> PUSH HL ;save return address
> DEC HL ;get MSB
> LD A,(HL) ; to reg A
> DEC HL ;get LSB
> LD L,(HL) ; to reg L
> LD H,A ;and MSB to H
> JP (HL) ;jump to subroutine.

I don't follow that one - in waht way is it relocatable? It looks to me
to be equivalent to a plain CALL.

Matthew Phillips
---------------------------------------------------------
WACCI on WWW - http://users.ox.ac.uk/~chri0264/wowww.html
The UK's biggest CPC user club
---------------------------------------------------------

Douglas Beattie Jr.

unread,
Mar 15, 2000, 3:00:00 AM3/15/00
to
You're right, Matthew... I confess: it was about 1:15 AM when I posted.
...it looked perfect the other night!

(anyone have a fix for method 3? -- looks like I need a workaround..)

Douglas Beattie Jr.

unread,
Mar 15, 2000, 3:00:00 AM3/15/00
to
Here are some more cool solutions sent to me by Shawn Sijnstra, using
8 bytes or less in zero page.

(..hope you don't mind me sharing these, Shawn..)

Shawn Sijnstra wrote:
>
> If you were willing to lose HL in the rCall macro, the following
> should work. If you want to preserve HL in the actual macro, that
> can be done too with a slightly longer rCALL macro that pushes HL,
> then loads HL,offset then EX (SP),HL. Naturally the RST1 routine
> would have to be modified. If you wanted to preserve HL in there
> as well, it would take more than 8 bytes.
>
> -----------------------------------
> RST1 EQU 08H
>
> ; Macro destroys HL but so does RST1 so it shouldn't matter
> ;
> rCALL MACRO destination
> LD HL,&destination&-$-3 ;i.e. -($+3)
>
> RST1
>
> ; Relocatable subroutine call -- another method
> ; Advantages: 8 bytes, preserves DE
> ; Disadvantages:
> ; call is longer, requires more stack, requires HL=offset
>
> ORG RST1
> ex de,hl ;de has offset, hl has DE
> ex (sp),hl ;(SP) has DE, hl has return address
> ex de,hl ;hl has offset, de has return
> add hl,de ;HL has offset+return (i.e. call destination)
> ex de,hl ;de has call dest, hl has ret
> ex (sp),hl ;hl has de, (SP) has ret
> ex de,hl ;de has de, hl has call dest
> jp (HL)
>
> ;Alternately if DE has the offset and HL is preserved.
> ; requires DE=offset and only 7 bytes (but more stack)
>
> ORG RST1
>
> ex (SP),hl ;(sp) has hl, hl has ret
> ex de,hl ;de has ret, hl has offset
> add hl,de ;hl has offset+ret
> ex de,hl ;hl has ret, de has offset+ret
> ex (SP),hl ;(sp) has ret, hl has hl
> push de ;put offset+ret on stack
> ret ; "JP (DE)"
>
> Cheers,
> Shawn

Douglas Beattie Jr.

unread,
Mar 15, 2000, 3:00:00 AM3/15/00
to
A 89 ;Z80 Relocatable Subroutine Call -- METHOD 3 RETHINKED
A 90 ;advantage:
A 91 ; CP/M compatible -- uses only A and HL
A 92 ; Size of macro remains 3-bytes,
A 93 ; ultimate code size remains the same.
A 94 ;disadvantage:
A 95 ; a whopping 17 bytes of code, but still maybe faster
A 96 ; than using EX (SP),HL instructions.
A 97 ORG 0008h
A 98
0008 E1 A 99 POP HL ;get return PC
0009 7E A 100 LD A,(HL) ;get LSB
000A 23 A 101 INC HL
000B 08 A 102 EX AF,AF'
000C 7E A 103 LD A,(HL) ;get MSB
000D 23 A 104 INC HL
000E E5 A 105 PUSH HL ;save return address
000F 08 A 106 EX AF,AF' ; use LSB, add to L
0010 85 A 107 ADD A,L ; possibly set C-fl in F
0011 6F A 108 LD L,A ; plug in LSB
0012 7C A 109 LD A,H ; save H to A, C-fl still in F
0013 08 A 110 EX AF,AF' ; select AF' (MSB)
0014 67 A 111 LD H,A ;now put MSB of offset into H
0015 08 A 112 EX AF,AF' ; select AF (saved H)
0016 8C A 113 ADC A,H ; add with Cy-fl
0017 67 A 114 LD H,A ;MSB destination result to H
0018 E9 A 115 JP (HL) ; jump relative to subroutine

;Zilog Macro Assembler. Version 2.10 15-Mar-00 12:43:20

I remember how they said the 6809 was great for "Position-Independant
Code," which can be loaded anywhere in memory. And I wondered if the
Z80 was even capable of such a feat, albeit somewhat slower.. (If you
up the clock by 2_Mhz, there's probably sufficient compensation. i.e.
4_MHz Z80 code using absolute calls may be slower than 6_Mhz Z80 using
a relative call scheme).

Perhaps Position-Independant Code on a Z80 is more trouble
than it's worth -- What do you think?

Paul R. Santa-Maria

unread,
Mar 16, 2000, 3:00:00 AM3/16/00
to
Douglas Beattie Jr. <beat...@whidbey.net> wrote in article
<38CFFC...@whidbey.net>...

> Perhaps Position-Independant Code on a Z80 is more trouble
> than it's worth -- What do you think?

Position-independent (PI) code shines in a multitasking operating system
where the code really can be loaded anywhere. But on an eight-bit
single-tasking microcomputer? It is not needed.

Paul R. Santa-Maria
Ann Arbor, Michigan USA
pau...@ameritech.net

Shawn Sijnstra

unread,
Mar 16, 2000, 3:00:00 AM3/16/00
to

"Douglas Beattie Jr." wrote:

> Perhaps Position-Independant Code on a Z80 is more trouble
> than it's worth -- What do you think?

It's a shame that the z280 is no longer in production. It could do
EVERYTHING relative to the PC with 16 bit offsets.

Cheers,
Shawn

Shawn Sijnstra

unread,
Mar 16, 2000, 3:00:00 AM3/16/00
to Douglas Beattie Jr.
You can preserve all registers as well as having the short call routine
but a really long RST routine if you do the following (assuming my brain
is still functioning):
-----------------------------------
RST1 EQU 08H

rCALL MACRO destination

RST RST1

DW &destination&-$-2 ;i.e. -($+2)

.ENDM


; Relocatable subroutine call -- another method

; Advantages: preserves all registers and has a short call
; Disadvantages: requires stack, very long routine (18 bytes)

ORG RST1
ex (sp),hl ;(SP) has hl, hl has return address
dec sp
dec sp
push de ;stack is hl;de;
ld e,(hl)
inc hl
ld d,(hl)
inc hl ;hl has actual return address, de has offset


ex de,hl ;hl has offset, de has return
add hl,de ;HL has offset+return (i.e. call destination)

ex (sp),hl ;hl has DE, stack is hl;call+return
inc sp
inc sp
ex de,hl ;hl has return, de has DE
ex (sp),hl ;hl has HL, stack is return, call+return
dec sp
dec sp
ret


Cheers,
Shawn

Tim Mann

unread,
Mar 16, 2000, 3:00:00 AM3/16/00
to Shawn Sijnstra
In Shawn's sequence, if you get an interrupt between the first "inc sp"
and the last "dec sp", it will trash stuff on the stack that you need to
keep and blow you out of the water. This could be worked around by
adding a di/ei pair, but only if you know that your system does not use
the Z-80 nonmaskable interrupt feature.

I was thinking about writing a sequence that uses ix for safely addressing
inside the stack; this should be doable but will be even longer and slower.
I haven't sat down and written the code as yet.

--Tim

> From: Shawn Sijnstra <S.Sij...@unsw.edu.au>
> Date: Thu, 16 Mar 2000 12:27:40 +1100
> To: "Douglas Beattie Jr." <beat...@whidbey.net>
> Newsgroups: comp.os.cpm
> Subject: Re: Relocatable Z80 code (relative CALLs)
> -----


Tim Mann <ma...@pa.dec.com> Compaq Systems Research Center
http://www.research.compaq.com/SRC/personal/Tim_Mann/


Herbert Oppmann

unread,
Mar 17, 2000, 3:00:00 AM3/17/00
to
In comp.os.cpm Douglas Beattie wrote:

> ;Relative Call implementation, toward a goal of 100% relocatable code.

[some methods removed]

> Is there an even more efficient method of
> the "Z80 Relocatable Subroutine Call" ?

Depends on what type of efficiency you are aimed:
code size or execution time?

For the latter, I have another idea:

Keep (variable) base address of code in an index register (e.g. IX)
all the time.

Macro rCALL corresponds to:
LD HL,offset of destination from base
RST xx

At the restart location:
ADD HL,IX ; detour
JMP HL

Advantage:
- does not overlap into next RST space
- should be faster (I didn't count the cycles, but it seems obvious)
- uses only register HL

Disadvantage:
- needs one index register all the time
- rCALL macro is one byte longer than normal CALL
--
Andrea Christ-Oppmann & Herbert Oppmann
Tel +49 911 3849547, Duesseldorfer Strasse 65
90425 Nuernberg, Germany

bma...@iglou.com

unread,
Mar 17, 2000, 3:00:00 AM3/17/00
to
On he...@memotech.franken.de(HerbertOppmann) said:
>For the latter, I have another idea:
>Keep (variable) base address of code in an index register (e.g. IX)
>all the time.
>LD HL,offset of destination from base
>RST xx
>At the restart location:
>ADD HL,IX ; detour
>JMP HL

I need to find my Z80 programming book. Are you sure ADD HL,IX is a
valid instruction?

Charles E. Bortle, Jr.

unread,
Mar 17, 2000, 3:00:00 AM3/17/00
to
Hello All,

> I need to find my Z80 programming book. Are you sure ADD HL,IX is a
> valid instruction?

No, that is not a valid instruciton, according to the Mostek Programming
Manula For The Z80.

You could do this:

;PUSH DE
PUSH IX
POP DE
ADD HL, DE
;POP DE

--
Charles cbr...@ix.netcom.com
"For God So Loved The World, That He Gave His
Only Begotten Son, That Whosoever Believeth
In Him Should Not Perish, But Have Everlasting
Life"John3:16 * http://pw2.netcom.com/~cbrtjr/wrdthing.html *
<bma...@iglou.com> wrote in message news:38d2...@news.iglou.com...

Douglas Beattie Jr.

unread,
Mar 17, 2000, 3:00:00 AM3/17/00
to
Herbert Oppmann wrote:
>
> Keep (variable) base address of code in an index register (e.g. IX)
> all the time.

> Macro rCALL corresponds to:


> LD HL,offset of destination from base
> RST xx

..... okay; that looks good.

> At the restart location:
> ADD HL,IX ; detour
> JMP HL

..... hmmm. can't do that. IX only works as a destination for ADD.
IX and IY do most things exactly like HL (same opcode, prefixed DD/FD)
but IX/IY can't use HL as the operand, or vice-versa.

But you have the right idea; we've all completely overlooked the
index registers.. How-about:

POP IY
DEC SP
DEC SP
ADD IY,DE
JP (IY)

; 8 bytes, right.?

Herbert Oppmann

unread,
Mar 18, 2000, 3:00:00 AM3/18/00
to
beat...@whidbey.net wrote:

> > ADD HL,IX ; detour


> ..... hmmm. can't do that.

Ooops. Too much x86 assembly, I guess.

There is still a possibility for my idea to operate with a base and
offset to calculate the destination:

Macro rCALL corresponds to:
10 LD DE,offset of destination from base
11 RST xx
21 cycles (on a Z80)

At the restart location:
15 PUSH IX ; save base address
15 ADD IX,DE ; calculate call destination
23 EX (SP),IX ; put destination on stack, restore base
10 RET ; jump to destination
63 cycles

Uses 7 bytes. But is not as fast as I intended it.


Your idea (using the return address for calculting the destination
address):

> But you have the right idea; we've all completely overlooked the
> index registers.. How-about:
>
> POP IY
> DEC SP
> DEC SP
> ADD IY,DE
> JP (IY)
>
> ; 8 bytes, right.?

I'm arguing strongly against DEC SP. If between the POP IY and the
ADD IY,DE there is an interrupt, the original return address is
trashed. You would have to disable interrupts and also the non-
maskable interrupts, which can be done - if at all - only in a
very specific way for a particular machine. Your code would be
highly non-portable.

Your solution could look like:

Macro rCALL corresponds to:
10 LD DE,offset from here to destination
11 RST xx
21 cycles

At the restart location:
14 POP IY ; get return address
15 PUSH IY ; restore return address
15 ADD IY,DE ; calculate destination address
8 JP (IY) ; go!
52 cycles

Same number of bytes, only slightly more cycles that 2 x DEC SP.


--
Andrea Christ-Oppmann & Herbert Oppmann

Duesseldorfer Str. 65, 90425 Nuernberg, Germany

Lee Hart

unread,
Mar 19, 2000, 3:00:00 AM3/19/00
to
In comp.os.cpm Douglas Beattie wrote:
> Relative Call implementation, toward a goal of 100% relocatable code.

>> Is there an even more efficient method of


>> the "Z80 Relocatable Subroutine Call" ?

I'm not sure why you want the code itself to be relocatable once in
memory. Are you loading it from disk and just want the freedom to load
it wherever there happens to be free memory at the moment? If so, why
not store the code on disk as a .REL file instead of a .COM file?

This is a format used by Microsoft's M80 (and other) relocatable
assemblers. A .REL file is no bigger than a .COM file, but can be
relocated during loading. You don't have to use Microsoft's L80; a
fairly small loader program can do it.

For example, I spent a lot of time working with Alan Bomberger's
Write-Hand-Man. This was a very clever package to add Sidekick-like
pop-up utilities to CP/M (little programs like a calculator, notepad,
rolodex, etc.). It created a little 1.5k hole somewhere in high memory.
When you hit a certain key, no matter what program was running at the
time, the TSR (terminate and stay resident) part of WHM was a .REL file
loader that loaded the particular utility you requested from disk, and
relocated it to run in this 1.5k hole. When finished, the program
exited, leaving everything else in memory exactly as-is, so the
interrupted program resumed unaffected.
--
Lee A. Hart Ring the bells that you can ring
4209 France Ave. N. Forget the perfect offering
Robbinsdale, MN 55422 USA There is a crack in everything
phone (612) 533-3226 That's how the light gets in
leea...@earthlink.net Leonard Cohen

Douglas Beattie Jr.

unread,
Mar 19, 2000, 3:00:00 AM3/19/00
to
Actually, Lee, I had an ulterior motive.

I'm researching different ways to implement a Z80 FORTH compiler for
CP/M-80, which compiles relocatable code, and also produces modules of
headerless, position-independant code for a (ROMmable) target.

Also take for example a program which sits high in memory, but has a
heap above it. If a larger heap is needed during execution, the program
could theoretically be moved downward in memory by (say..) 600 bytes,
while it is running..

It's true, though... there is an advantage to using .REL files. Doesn't
the Z-System also run .ZRL files right from the command line? It's no
wonder that CP/M didn't do that in the first place; CP/M was first, and
CP/M ASM was an absolute assembler -- REL files came later, didn't they?

What format does CP/M RMAC use for its intermediate relocatable format,
and are these also executable (assuming a proper loader)?

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

John Elliott

unread,
Mar 19, 2000, 3:00:00 AM3/19/00
to
"Douglas Beattie Jr." <beat...@whidbey.net> wrote:
>What format does CP/M RMAC use for its intermediate relocatable format,

REL, the same as M80.

>and are these also executable (assuming a proper loader)?

Not directly; if you link them with the Digital Research LINK (rather than
Microsoft L80) you can generate:

* PRL files (relocatable on 256-byte boundaries) which can be loaded
directly by the CP/M 3 CCP;
* RSX files (relocatable on 256-byte boundaries) which can then be attached
to a .COM file and relocated automatically at load by the CP/M 3 CCP;
* SPR files (relocatable on 256-byte boundaries) which GENCPM uses to create
CPM3.SYS.

The PRL format has a 256-byte header, but the only important value is
the length of the code, which is at bytes 1 and 2. Thus it's fairly
easy to write a loader which goes in the rest of the header, works
out where it is, and relocates the file. I believe the Z4 "*.C4M" files
do this; certainly I've done it to PRL files to make them work on
the Sinclair Spectrum (where the address of the code is passed in BC).

CP/M-86 Plus extended the RSXs to behave like DLLs - programs would only
contain a reference to the RSX, and the loader would load the RSX from a
separate file at runtime.

------------- http://www.seasip.demon.co.uk/index.html --------------------
John Elliott |BLOODNOK: "But why have you got such a long face?"
|SEAGOON: "Heavy dentures, Sir!" - The Goon Show
:-------------------------------------------------------------------------)

Bruce Morgen

unread,
Mar 20, 2000, 3:00:00 AM3/20/00
to
"Douglas Beattie Jr." <beat...@whidbey.net> wrote:

>Actually, Lee, I had an ulterior motive.
>
>I'm researching different ways to implement a Z80 FORTH compiler for
>CP/M-80, which compiles relocatable code, and also produces modules of
>headerless, position-independant code for a (ROMmable) target.
>
>Also take for example a program which sits high in memory, but has a
>heap above it. If a larger heap is needed during execution, the program
>could theoretically be moved downward in memory by (say..) 600 bytes,
>while it is running..
>
>It's true, though... there is an advantage to using .REL files. Doesn't
>the Z-System also run .ZRL files right from the command line?

No, the .ZRL files are
used for loadable
system segments, not
programs executed from
the command line. You
need a specialized
loader like JetLDR or
NZCOM to make use of
.ZRLs. Z-System does
support executables
that aren't ORGed
absolutely through the
"Type 4" concept, where
the command processor
works together with
special relocator code
in the executable to
load and run the
program as high in the
TPA as possible.

>It's no
>wonder that CP/M didn't do that in the first place; CP/M was first, and
>CP/M ASM was an absolute assembler -- REL files came later, didn't they?
>

Actually, the MS .REL
format was around in the
'70s, but DRI chose not
to use it until RMAC was
released.

>What format does CP/M RMAC use for its intermediate relocatable format,

>and are these also executable (assuming a proper loader)?

RMAC uses .REL files just
like M80, but it doesn't
support some M80 features,
most notably named commons,
as required by Z-System
.ZRLS. Named commons were
implemented to support
assembler subroutines for
MS FORTRAN and/or COBOL, as
I recall.

__________________________________________________
http://come.to/realization
http://www.atman.net/realization
http://www.users.uniserve.com/~samuel/brucemrg.htm
http://www.users.uniserve.com/~samuel/brucsong.htm

0 new messages