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/
;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.
--
> (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
---------------------------------------------------------
(anyone have a fix for method 3? -- looks like I need a workaround..)
--
Douglas Beattie Jr. http://www2.whidbey.net/~beattidp/
(..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
;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?
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
> 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
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
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/
> ;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
I need to find my Z80 programming book. Are you sure ADD HL,IX is a
valid instruction?
> 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...
> 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.?
> > 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
>> 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
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)?
--
Douglas Beattie Jr. http://www2.whidbey.net/~beattidp/
** ** ** ** ** ** ** ** ** ** ** ** ** **
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
:-------------------------------------------------------------------------)
>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