Making real 256kb ROMs in SDCC

Page 2/3
1 | | 3

By salutte

Resident (53)

salutte's picture

23-05-2020, 13:08

I thought a lot (maybe too much) about having the linker insert automatic trampolines, but I found it surprisingly difficult on the edge cases, and also I found that it is hard for the developer to keep track then onto which segment is mapped into which page.

An example would be:

file strings.c

uint8_t len(const char *str) {
  ... // Returns the length of a string.
}

file other.c

const char *str = "hello";
uint8_t l = len(str); // error

There, if an automated trampoline is inserted, and both modules are on the same page, then the module that contains "hello" is evicted before the call to len. Thus I left the "manual" process of allocating modules into pages to at least make those errors more visible.

About the builtin functions, the k5link module does not support them, but the module_linker should be fine. It allocates them permanently into pageA (which generally should never change).

By Grauw

Ascended (8927)

Grauw's picture

23-05-2020, 13:32

A linker like this is really interesting.

By geijoenr

Expert (127)

geijoenr's picture

23-05-2020, 14:22

Right, In the case you point out above, the const data is placed within a code segment. If you are switching in and out only in one of the banks the problem is not solvable. The solution would be to switch in two banks so that you can have both pages visible at once, but that would require being able to adjust the re-locations in runtime.

If you ignore the const data, all other data will be placed in the DATA segment which is RAM. You can choose not to switch it.

Are there more corner cases you came across? I think accepting the limitation that you cannot use const data mixed within a code module is not a big deal given the benefits you get.

I mean, you can place all const data in a specific segment, and switch that data in one the banks, whereas code is switched in a different one. Of course local variables are on the stack, that also is not switched out.

I think it _must_ work.

By salutte

Resident (53)

salutte's picture

23-05-2020, 16:56

I must think. Forbidding code and data in the same segment is a great idea.

I thought then a possible model, that now becomes possible:
Page A -> Always fixed, will have stuff like builtin functions, and isr functions that should be called at any time.

All code could be stored twice, for two possible locations, one to be located in page B, and other in page C. Then a call from A can be intercepted and load the target in page B, then a call in page B can be intercepted and placed in page C. And from C to B again.

Then page D remains for pure data access. Dealing with page D access must be done manually, as in your model, but jumps between pages can be highjacked.

Function pointers would not work, but this IMHO is not critical, albeit there is a chance to make it work.

Previously, I could not get the trampoline to work because I could not find any right place to store the previously mapped segment to restore it on exit, but in this model, that we always alter the target segment before the call, this should work. SDCC replaces many calls for jumps (i.e. if the call is the last statement in a function, so it does not need to return twice), so we need to be very careful on how to hijack calls.

----- edit before sending -----

Nah... I still can not get the trampoline to work. When returning, it might go to the wrong module. Imagine the following setup:

STEP   # PATH                # PAGE_A # PAGE_B # PAGE_C # PAGE_D   
#################################################################
 1     #  A: CALL MOD1       # MAIN   #  -     #  -     # -     
 2     #  B: CALL MOD2       # MAIN   #  MOD1  #  -     # -     
 3     #  C: CALL MOD3       # MAIN   #  MOD1  #  MOD2  # -     
 4     #  B: RET             # MAIN   #  MOD3  #  MOD2  # -     
 5     #  C: RET             # MAIN   #  MOD3  #  MOD2  # -     
 6     #  B: ERROR           # MAIN   #  MOD3  #  MOD2  # -     

In step 6, we returned to module 1 which was on page B but was evicted. We can highjack the call path, but I don't see how we can highjack the return path to restore the modules, while keeping the stack intact to keep sdcc happy.

By geijoenr

Expert (127)

geijoenr's picture

23-05-2020, 17:37

I think I get what you are saying, but I am not sure I fully understand the problem. What I would do instead of using a plain trampoline is to transform any long jump into a construct that first switches the bank and then does a 'call'. The return path will always be through that construct, so that is possible to switch back. I think it would be necessary to have a stack of switched pages to keep track of nested calls.
(well not need to keep them separated, the construct can check the stack before doing a ret to see if needed to switch; only the stack will grow bigger than usual, and ram needs to be provisioned for that).

I am trying to finish something else, but as soon as I am done I will dive into this and try to do some experiments using your linker. Let's see if I can understand better the roadblocks.

By salutte

Resident (53)

salutte's picture

23-05-2020, 17:38

That'd be great if you can check it!

I think that another solution would be to modify sdcc to add a 3 byte far pointer type, but I'd need to get deeply into sdcc for this.

By samsaga2

Resident (56)

samsaga2's picture

23-05-2020, 20:27

I think that could be a great idea to create a separated project for the linker. It really needs split the code in multiple files. With a github project I can easily contribute with pull requests.

By salutte

Resident (53)

salutte's picture

23-05-2020, 22:35

samsaga2, I think it's a great idea to split the linker on its own repo. I'll work on that this weekend.

Thanks!

By geijoenr

Expert (127)

geijoenr's picture

24-05-2020, 13:23

Hi salutte,
I have found sdcc has some support to emit banked calls, but is pretty much broken. The problem is that unless you put all the code on a single module, the code generator cannot figure out if a symbol is banked and which bank it belongs to. So it basically emits banked calls for everything.

At the moment I can do something like the following:

#pragma bank 4
void function_a ( ) { }

the pragma puts the module in segment CODE_4

and in another module:

extern void function_a();
void main () {
   function_a();
}

and this generates something like:

   407B CD 00 00      [17]   58 	call	banked_call
   407E 00 A0                59 	.dw	_function_in_code1
   4080 00 00                60 	.dw	0 ; pending

and the "pending" part should contain the bank to be switched to.

By geijoenr

Expert (127)

geijoenr's picture

24-05-2020, 14:11

The trick here is to manage the linker to process a relocation like:

ld a, (_function_in_code1 >> 16)

where _function_in_code1 is a 24bit address. I think this can be done with the sdcc linker, but it doesn't work so far, even though it emits the symbols in 24 bits, the relocations are not processed properly at the moment.

Page 2/3
1 | | 3