2

I have been trying to build a home-brew B cross-compiler for my Ti-89 for a few months now, and am finally at the point at which I would like to generate assembly which my calculator executes. The language is 'B' in the sense that there is only 1 type which is the integer/pointer. I don't understand how to implement function pointers on this platform. I was considering implementing function pointers in the following ways:

  • Storing the address of the first instruction using an immediate value
  • Deriving the address of the first instruction using the program counter and an offset
  • Storing just the offset and calculating the address of the first instruction when the jump is taken.

The first method definitely won't work since assembly programs on the Ti-89 are copied into RAM before being executed. I would have no way of knowing at compile time where the function would be located. Also, according to the Ti-89/Ti-92 Plus Developer Guide on page 24, "ASM programs may move during heap garbage collect. Pointers to overriding system code would become invalid." This would imply that the second method would not necessarily work either, since it is possible that heap garbage collection happens after the address of the first instruction is calculated. Assuming what it says about the program moving is correct, this could cause the entire function to move. The third method will work in specific cases, but the problem is I don't necessarily know where in the assembly program the function will be called. Thus I would need a different offset for each place the function could be called, and I have no way of calculating that offset as far as I know.

TIGCC allows for function pointers, so how the heck do they implement them? Is there something I'm missing?

Links:

2.71828-asy
  • 171
  • 1
  • 5

2 Answers2

3

If you want to get the address of a function in the same compilation unit, you should use PC relative addressing. You can use lea (load effective address) to store the address in any address register (a0-a6. Technically, a7 works as well, but don't do that. a6 is also often reserved as frame pointer):

lea   myfunc(PC),a2

This can be called using jsr (a2) (jump to subroutine), or in some cases just using jmp (a2) (essentially a tail jump, in case the parameters on the stack already match the function parameters expected by myfunc).

Alternatively, you can use pea (push effective address) to push the address on the stack:

pea   myfunc(PC)

This could be either a function parameter, or if this stays at the top of the stack, you will jump to that address using the next rts. E.g., if you have a function that would end like this:

    lea  myfunc(PC),a2
; some other code (not changing a2, not messing with the stack)
    bsr  foo   ; local subroutine
    jsr  (a2)
    rts

You could also write:

    pea  myfunc(PC)
; some other code (not messing with the stack)
    bra  foo  ; tail call to local subroutine foo
              ; on return this will jump to myfunc
              ; when myfunc ends, it returns to the caller of this function
chtz
  • 17,329
  • 4
  • 26
  • 56
  • This breaks if the executable moves during execution like it says in the documentation doesn't it? I reinterpreted it to mean that the stored executable can move, but not the copy in RAM that is running. – 2.71828-asy Feb 24 '19 at 22:33
  • If the executable moves "during execution" you have much bigger problems. It's been a while that I programmed for a TI-92+, but that should never happen. The big advantage of PC-relative addressing is that the kernel does not need to calculate any absolute addresses before launching. – chtz Feb 25 '19 at 07:50
  • This works nicely for jumps to subroutines not further away than +- 32767 bytes, it would even work for "moving code" (which I don't think is happening on the TI) if you can make sure it doesn't move between the load of the register and the actual jump. – tofro Feb 26 '19 at 07:35
  • I've only ever had problems with moving code if I have to copy a section of code to another section of RAM and run it from there, which is really only something I do if I'm writing an interrupt handler and I'm trying to improve performance. – puppydrum64 Jun 29 '23 at 16:53
0

I followed the suggestion of @lurker. The assembly seems to just push the address onto the stack without much more info. From testing, if my function was named my_func then the assembly looks like move.l #my_func,-4(%fp) which is just pushing the function pointer. I'm guessing the assembler converts it to pushing the immediate relative the PC, and not just the actual address within the binary. This would mean that I read the documentation I referenced earlier wrong. What I think it was trying to say is that you cannot refer to the addresses within the binary in memory, but you can refer to addresses within the temporary copy it puts in RAM. This would make more sense since the operating system wouldn't really be running in the background while the assembly program runs anyways. I have to assume that the garbage collection is something that can only happen after the program executes.

2.71828-asy
  • 171
  • 1
  • 5
  • Presumably you compiled with optimization disabled, otherwise it wouldn't need to store the function pointer on the stack (depending on what you did with it). Anyway no, it probably is using a `mov`-immediate with the absolute address (filled in by the linker and/or fixed up a runtime). What does TIGCC do for access to static data, e.g. passing the address of `static int foo` to an `extern void bar(int*)` function? – Peter Cordes Feb 24 '19 at 02:07
  • @Peter Cordes Yes, compiling optimization level `-O0`. When I test that, the compiler reserves space at the end of the executable for the variable, and loads the pointer with `pea foo`, which is just 68k for push the address of foo onto the stack. I will disassemble the binary to make sure it's not using PC indirect addressing. – 2.71828-asy Feb 24 '19 at 05:39
  • If you can use an *immediate address* you either have a relocating loader or your assembler converts everything into PC-relative. Both is possible. – tofro Feb 27 '19 at 09:20