CPCWiki forum

General Category => Programming => Topic started by: zhulien on 15:42, 26 August 21

Title: Can you code a Z80 program with zero absolute addresses?
Post by: zhulien on 15:42, 26 August 21
here is a challenge for you... Can you code a Z80 program with zero absolute addresses?

Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: roudoudou on 15:53, 26 August 21
YES  8)
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: zhulien on 15:55, 26 August 21
other than something simple like an ret or a maths operation followed by ret?
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: Animalgril987 on 17:39, 26 August 21
Certainly. Some game loaders do it, so that their code is relocatable (because HIMEM) will be different for the different CPCs.
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: BSC on 19:09, 26 August 21
Quote from: roudoudou on 15:53, 26 August 21
YES  8)


+1
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: norecess464 on 19:10, 26 August 21
If my memory is still good, I think DAMS assembler (ADAM in UK: https://www.cpc-power.com/index.php?page=detail&num=4248 (https://www.cpc-power.com/index.php?page=detail&num=4248)) is entirely relocatable. It's considered as a big program for the Amstrad CPC.
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: Zik on 22:03, 26 August 21
Quote from: zhulien on 15:42, 26 August 21Can you code a Z80 program with zero absolute addresses?
Absolutely!
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: zhulien on 12:20, 27 August 21
how does DAMS achieve this?


The way i thought of doing it, perhaps is too complicated an approach, was to have a GUID in the code, search all memory for the GUID, from that GUID calculate where it was found and we can now calculate where we are actually executing then to update jump tables based on an offset (where we assembled the jump table from vs where we are currently executing) and that all calls are jumps via the recalculated jump tables. 


Is there a better way?
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: roudoudou on 12:25, 27 August 21

"easy" way: no call, no jump, no memory variable, only relative jumps
or use a relocation table, patch the code, run the code anywhere
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: m_dr_m on 12:54, 27 August 21
Yep, DAMS does it via a relocation table. No calls in such a big tool would be rather challenging (especially considering the small footprint of dams). Well, Orgams's z80 emulation for step by step debugging doesn't use calls, to be both fast and transparent.
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: andycadley on 13:48, 27 August 21

Traditionally with something like:

CALL pchl ; store PC in HL


....


pchl: POP HL
JP (HL)
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: eto on 19:28, 27 August 21
Quote from: andycadley on 13:48, 27 August 21CALL pchl ; store PC in HL

isn't pchl then absolute?
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: scruss on 20:13, 27 August 21
I'm told that Richard Russell's Z80 floating point library (used on the Z88 and later versions of BBC BASIC for Z80 machines) is relocatable, fully re-entrant and uses no storage outside the stack. I'm pretty sure it's using a jump block though, as using JR throughout would be complex.
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: andycadley on 21:05, 27 August 21
Quote from: eto on 19:28, 27 August 21
isn't pchl then absolute?
Well it depends on how strict you want to be about it. You could write the instructions to a fixed location (e.g. the screen or one of the RST points) first. Or pass dummy parameters from BASIC, writing the PCHL routine to (IX) via a series of direct instructions and then doing a CALL (IX) by the combination PUSH IX; RET


Got to be said though that writing fully relocatable code in Z80 is enormously hard work. The CPU just isn't really designed for it. You really want Register Indirect addressing on absolutely every type of instruction as well as something like LEA to calculate addresses.
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: GUNHED on 04:52, 28 August 21
Quote from: zhulien on 15:42, 26 August 21
here is a challenge for you... Can you code a Z80 program with zero absolute addresses?
Under FutureOS quite some apps start at &0000.  :)
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: eto on 11:03, 28 August 21
Quote from: andycadley on 21:05, 27 August 21Well it depends on how strict you want to be about it.

Well... I think if the question has at its core the restriction "zero absolute addresses". Once you soften this, you answer a totally different question.
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: zhulien on 11:25, 28 August 21

Since we cannot use an absolute address, we cannot know where our code or even labels are - we can search for our code. An example is here, we found our code id 'AGUID' (it could be a real guid if you want longer code). After having our one known found absolute address, we can now modify it to put a call to a nearby function that get's the PC. Having got the PC we can for example find the absolute address to a table of relative function offsets. Finally we then go to our actual code logic with IY having the absolute address of the table of relative function offsets. I have coded a relocatable call, but... of course in this example it breaks the rule to call it 3 times as it itself should not be an absolute address.


I think it is possible to continue with the route I have shown as an example, but instead of having a relocatable call, basically jump relative to the offsets in the table that is known. Yes, the code will be spaghetti code, and of course there is no recursion easily and no subroutines in the normal sense, the functions would essentially be using looked up JRs to make an equivalent to a call, and continue their flow with other looked up JRs rather than returning. There are definitely a lot better ways of doing things and even by having a single known service function such as the relocatable_call demonstrated, it could be easily possible to make everything else relocatable.


Any imrpovements?  Can we make a fully self-relocating code that runs from any memory location without a separate loader?



ORG #4000


JR FINDCODE


CODE_ID: TEXT "AGUID"
PCISHERE: JR GOTPC


GETPC: POP HL ; GET PC
PUSH HL
RET


GOTPC: LD BC, TAB_ADDRESSES - PCISHERE + 2
ADD HL, BC
PUSH HL
POP IY ; IY = ADDRESS OF TAB_ADDRESSES


JR ENTRY






; FIND SOME GUID
FINDCODE: LD BC, 0 ; WE WILL LATER OVERWRITE THE GUID TO BE A CALL
FINDLOOP: PUSH BC ; TO GETPC WHICH IS A KNOWN NUMBER OF BYTES FROM
POP HL ; THE CODE_ID
LD A, (HL)
CP 'A'
JR NZ, FINDNEXT


INC HL
LD A, (HL)
CP 'G'
JR NZ, FINDNEXT


INC HL
LD A, (HL)
CP 'U'
JR NZ, FINDNEXT


INC HL
LD A, (HL)
CP 'I'
JR NZ, FINDNEXT


INC HL
LD A, (HL)
CP 'D'
JR NZ, FINDNEXT
JR Z, FOUND


FINDNEXT: INC BC
JR FINDLOOP


; NOW WE KNOW THE ADDRESS OF THE CODE_ID
; LETS MODIFY IT TO BECOME A CALL TO GETPC
; BEFORE JUMPING TO IT
FOUND: PUSH BC ; BC = ADDRESS OF CODE_ID
PUSH BC
PUSH BC
POP HL ; HL = ADDRESS OF CODE_ID


; GETPC IS 7 BYTES BEYOND CODE_ID SO ADD 7 TO HL
LD BC, 7
ADD HL, BC


PUSH HL
POP DE ; DE = ADDRESS OF GETPC


POP HL ; HL = ADDRESS OF CODE_ID AGAIN


LD A, #CD ; A BECOMES CALL
LD (HL), A
INC HL


LD A, E ; G BECOMES PART OF GETPC ADDRESS
LD (HL), A
INC HL


LD A, D ; U BECOMES PART OF GETPC ADDRESS
LD (HL), A
INC HL


LD A, 0 ; I BECOMES NOP
LD (HL), A
INC HL


LD A, 0 ; D BECOMES NOP
LD (HL), A


POP BC ; BC = GETPC


JR CODE_ID ; JUMP TO CODE ID, WHICH THEN CALLS GETPC AND JUMPS TO
; GOTPC


TAB_ADDRESSES:
DW FUNC0-TAB_ADDRESSES, FUNC1-TAB_ADDRESSES, FUNC2-TAB_ADDRESSES


ENTRY: LD A,0 ; ENTRY POINT TO OUR APPLICATION
CALL RELOCATABLE_CALL ; RELOCATABLE CALL TO FUNCTION 0
HALT ; SHOULD HAVE A 255 IN A HERE


FUNC0: LD A,1 ; RELOCATABLE CALL TO FUNCTION 1
CALL RELOCATABLE_CALL ;
RET


FUNC1: LD A,2 ; RELOCATABLE CALL TO FUNCTION 2
CALL RELOCATABLE_CALL ;
RET


FUNC2: LD A,255 ; WE JUST WANT TO RETURN A RESULT OF 255
RET


RELOCATABLE_CALL:
; PUT CODE TO PRESERVE REGISTERS HERE
LD H, 0
LD L, A
ADD HL, HL ; GET THE ENTRY NUMBER


PUSH IY
POP BC ; BC = TABLE


ADD HL, BC ; HL = FUNCTION OFFSET TO READ


LD C, (HL) ; BC = 2 BYTE RELATIVE ADDRESS FROM THE TABLE
INC HL
LD B, (HL)


PUSH IY
POP HL
ADD HL, BC ; ADD THE TABLE TO RELATIVE ADDRESS TO GET AN ABSOLUTE ADDRESS


; PUT CODE TO RESTORE REGISTERS HERE


PUSH HL ; CALL THE ADDRESS
RET




Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: Animalgril987 on 13:12, 28 August 21
Why not:
  POP HL
  PUSH HL    ; HL now has copy of return address
  DEC HL       ; HL points to high byte of called address
  LD D, (HL)
  DEC HL      ;  HL now points to high byte of called address
  LD E, (HL)


and DE holds the entry address of your routine.
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: roudoudou on 14:31, 28 August 21
Quote from: Animalgril987 on 13:12, 28 August 21
Why not:
  POP HL
  PUSH HL    ; HL now has copy of return address
  DEC HL       ; HL points to high byte of called address
  LD D, (HL)
  DEC HL      ;  HL now points to high byte of called address
  LD E, (HL)


and DE holds the entry address of your routine.
you assume there is a CALL before your routine but AMSDOS has his own ways  ;D

Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: Animalgril987 on 14:58, 28 August 21
Quote from: roudoudou on 14:31, 28 August 21Quote from: Animalgril987 on Today at 13:12:53
Why not:
  POP HL
  PUSH HL    ; HL now has copy of return address
  DEC HL       ; HL points to high byte of called address
  LD D, (HL)
  DEC HL      ;  HL now points to high byte of called address
  LD E, (HL)


 and DE holds the entry address of your routine.
you assume there is a CALL before your routine but AMSDOS has his own ways
Very true.
Also a typo: 3rd comment should say LOW byte, not high.
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: andycadley on 12:03, 29 August 21
Quote from: roudoudou on 14:31, 28 August 21
you assume there is a CALL before your routine but AMSDOS has his own ways  ;D
Yes, this sort of approach will fail if you've been called via an indirection block of some kind, as the stacked return address will actually be the location of the indirection rather than the code it eventually jumps to.
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: andycadley on 12:10, 29 August 21
Quote from: eto on 11:03, 28 August 21
Well... I think if the question has at its core the restriction "zero absolute addresses". Once you soften this, you answer a totally different question.
Well it depends on whether you interpret that as strictly no absolute addressing or more as a requirement that the code automatically relocates itself (either initially or on every call).


Writing truly address independent code on the Z80 (i.e. where you can't just inline modify an absolute addressed instruction) is particularly difficult. Not impossible, but you definitely have to jump through some hoops to accomplish it.


Cheating slightly and using the fact that passing parameters from BASIC will reserve some scratch memory pointed to by IX isn't really using absolute addressing but it certainly feels like skirting around the limitations a bit.
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: zhulien on 15:17, 30 August 21
To write a relocator, I would do the following:


- assemble the application, eg: at 0000
- assemble the application again at a different address which changes the high and low byte of each call, eg: 0101
- write a file of addresses for use with a loader


Then write the loader, allocate the application ram, allocate the address file (if not processed as it is read), change each address then free up the address file in ram.


If a program can just run from where ever it is loaded, or self-relocate (that bit would at least need to run where ever it is loaded), then that has a useful purpose.


Are there any better ways?  If crunched of course the process would be a bit different.  Are there any crunchers/relocators that do so 'in place'?  eg: FFFCCC (F = free ram, C = crunched app) --> UUUUUF (U = unchrunched app)


Does anyone not use a mass storage device on CPC these days that makes crunching even necessary?  I think the Z80 cycles and complexity are not worth it given storage for CPC is now plentiful... how to fill up so many gigabytes of data with a Z80 anyway?
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: FloppySoftware on 07:10, 23 September 21
I use CP/M PRL files to achieve this. They have a relocatable bitmap based on page offset.


You can generate PRL files with DR LINK, ZASM or my PRL program. There are other tools to do this.


You can write a PRL loader, I did it for my SamaruX project.
Title: Re: Can you code a Z80 program with zero absolute addresses?
Post by: krusty_benediction on 08:31, 23 September 21
Never tested, but winape assembler proposes these directives:
relocate_end    Mark the end of a relocatable section of code.
relocate_start    Mark the start of a relocatable section of code.
relocate_table [byte|word] [base_address]    Generate a relocation table. By default a table of word sized offsets is generated, override this by specifying byte for small code sections. The base_address specifies the relative origin for the values in the table.

and these variables

relocate_count    The number of entries in the relocation table.
relocate_size    The size of the relocation table, assuming the table is using word entries.
I guess other assemblers have such functionalities too.I would say they generate a table of addresses you have to patch by adding them the loading address of the program.
No need to do the things by yourself
 
Powered by SMFPacks Menu Editor Mod