Jump to content

Photo

GCC for the TI


511 replies to this topic

#501 jedimatt42 OFFLINE  

jedimatt42

    Stargunner

  • 1,890 posts
  • Location:Beaverton, OR

Posted Tue Mar 12, 2019 7:16 PM

For small stuff, I totally agree with Chue... 
 
 
void enableRom(int crubase) {
__asm__( 
    "li r12,%0\n\t"
    "sbo 0\n\t"
    : : "i" (crubase) 
  );
}
 
It's just ugly when you have large code:
 
https://github.com/j...dsrlnkraw.c#L60
 
I've called 'naked' assembly functions from C many times...  
 
enablerom.h 
  extern void enableRom(int crubase);
 enablerom.asm
    def enableRom

enableRom:
    mv r1,r12           ; r1 - int crubase
    sbo 0
    rt
 
-M@

#502 ralphb OFFLINE  

ralphb

    Dragonstomper

  • 635 posts
  • Location:Germany

Posted Wed Mar 13, 2019 12:24 AM

OK, it's a "naked" function I want to do.  What's a "clothed" function then?  :)   I know how painful inlining is.

 

But in case of naked functions, how are arguments mapped to registers or laid out on the stack?  Your example suggests they're just mapped to R1, R2, .., but I've seen stack fiddling with R10 in my generated code as well.

 

How can I access C variables, in particular char arrays?  How do I return a value?



#503 jedimatt42 OFFLINE  

jedimatt42

    Stargunner

  • 1,890 posts
  • Location:Beaverton, OR

Posted Wed Mar 13, 2019 12:54 PM

I think a 'clothed' function would be where you use inline assembly, and you use the mechanisms for declaring the inputs and outputs, and what registers you play with.

In a 'naked' function, you pass values through registers, and return a value through r1.

Usually what I see going on the stack, messing about with r10 at the beginning of a C function, is allocating space for the variables and structures and preservation space for some registers, and then near the end of the function the stack is popped and restored to the same place. If you don't need to allocate extra temporary space, you do not need to mess with r10
 
Compile a simple function like  
 

int add(int a, int b) {
   return a+b;
}

Take a look at the generated .s file... make sure it wasn't inlined...   You should see it only messes with r1, and r2...   it should generate something like a 2 instructions: adding r2 to r1, and return.
That exemplifies the calling convention... After that you just have to think about what your parameters are and where they live.
 
here, I'll use c parameter names that are named after the registers you'll recieve them in...
 

extern int naked(int r1, int* r2, char r3, char* r4);

4 values will be passed in registers...
 
r1 and r3 are values, if a variable is passed, the value of the variable will be put in the corresponding register. 
r3 will put that value in the high byte.
 
r2 and r4 are both 16 bit address parameters...  so if you have an int var1, you can pass it's address as &var1 when calling, and the address of var1 will be in the corresponding register.
 
If you pass a string literal, or a char username[20], as username, username is the address of that character array, so declare the parameter as a char*, and the address will be stuffed into the corresponding register when passed. 
 
If you return an int or address, declare the naked function's return type to be 'int' or 'int *' and store the desired return value or address in r1 before returning.

 

You can mess with R10, subtracting from it, to allocate temp space so you can store registers before messing with them, if for instance you need to store R11 for return somewhere. A function should always return with R10 at the same place it was when you entered the function, otherwise when returning the stack will be all offset incorrectly. 

 

The easiest approach to stack allocation is to require the calling functions in C to allocate any data or string buffer space, and to pass them as addresses to your assembly function. Then your function doesn't need to mess with the stack directly, it can mess with those chunks of data by address simply passed to you in positional registers. 

 

One thing to note though - and this is just theory - xop which isn't useful on the 4a, and crubit read/writes are the only thing you can't express as C.  writing to the sound chip... it's just a memory mapped device... writing to the VDP, again just memory mapped device... I've written DSRLNK in pure C with just an enableROM and disableROM helper as inline assembly - It worked on TIPI cause my DSR makes no assumptions about the console DSRLNK routine being used, but the TIFDC failed cause it expects remnants of the DSRLNK to be left behind in scratchpad... That could have been C, but it was easier to give in and borrow code from Tursi's libti99. 

 

 -M@

 

 

 

 

 



#504 Tursi OFFLINE  

Tursi

    Quadrunner

  • 5,572 posts
  • HarmlessLion
  • Location:BUR

Posted Wed Mar 13, 2019 3:38 PM

It's just ugly when you have large code:
https://github.com/j...dsrlnkraw.c#L60


Hey!!  :mad: 

;)
 

but the TIFDC failed cause it expects remnants of the DSRLNK to be left behind in scratchpad... That could have been C, but it was easier to give in and borrow code from Tursi's libti99.


The code you copy there is in turn just a port of the Editor/Assembler's version of DSRLNK. I never got my own working 100% either.


Edited by Tursi, Wed Mar 13, 2019 3:38 PM.


#505 jedimatt42 OFFLINE  

jedimatt42

    Stargunner

  • 1,890 posts
  • Location:Beaverton, OR

Posted Wed Mar 13, 2019 5:09 PM

Hey!!  :mad: 

;)
 


The code you copy there is in turn just a port of the Editor/Assembler's version of DSRLNK. I never got my own working 100% either.

 

 

Yes, that code I linked to started with your code that lived in libti99, and then I purposefully broke the card search feature so that the crubase is specified instead of enumerated... This allows me to have a copy routine from TIPI's "DSK1." to the TIFDC's "DSK1."  in the tool I'm building, where a path can be prefixed with the crubase...  Oh, I probably released the simple version called TIPIFM...  

 

But it isn't the assembly code that is ugly, but the extra \n\t and quotes and white space in general... I haven't found a style any better than your examples, Tursi...  I generally dislike languages inside languages.... Terraform with embeded JSON chunks,  javascript / reactjs with jsx pretending to be html but not quite, which javascript expressions swizzled in between, and if you forget it isn't actually html you get bit... SQL inside JAVA...   Any server pages technology...     

 

This assembly inside C is my idea of a vacation.. :)

 

----

 

Oh, I used another strategy in the TIPI messaging code and accessing it from C...  There I pretty much defined my own calling convention... wrote the routines and the sort you BLWP to (sort of, I didn't write a vector, just LWPI and BL myself) ... So parameters just have to be stuffed into well known addresses, in that case I used the first few registers in the GPLWS.  Then a little inline assembly to wrap the call...   

 

 https://github.com/j.../tipi_msg.c#L63

 

The function above that return a value. 

 

 

-M@



#506 Tursi OFFLINE  

Tursi

    Quadrunner

  • 5,572 posts
  • HarmlessLion
  • Location:BUR

Posted Wed Mar 13, 2019 7:46 PM

But it isn't the assembly code that is ugly, but the extra \n\t and quotes and white space in general... I haven't found a style any better than your examples, Tursi...


hehe, no need to defend. The only reason I didn't break it out into a separate module is because I did intend it as a temporary. But that's the brilliant part of releasing libti99 as public domain, I can do as much or as little as I like and not feel obligated* to make it better!

(* - that's a lie, I always feel obligated...)
 



#507 ralphb OFFLINE  

ralphb

    Dragonstomper

  • 635 posts
  • Location:Germany

Posted Thu Mar 14, 2019 1:10 AM

Thanks, Matt!  So I'll just pass everything instead of trying to access global variables, and everything is registers unless I have local variables, and I can use R0-R9 as I want?  Sounds good.



#508 jedimatt42 OFFLINE  

jedimatt42

    Stargunner

  • 1,890 posts
  • Location:Beaverton, OR

Posted Thu Mar 14, 2019 9:02 AM

Thanks, Matt!  So I'll just pass everything instead of trying to access global variables, and everything is registers unless I have local variables, and I can use R0-R9 as I want?  Sounds good.

 

Analyzing more of my generated code, (.s files) R9, R10, R11, R13, R14, R15 should be preserved... if mutated in your function, restore them before returning.  The stack is a good place to do that. 

 

These are listed in the gcc patch (I just read the patch code for the first time) as non-volatile registers.  So, R0 - R8 and R12 are volatile, can be mutated without expectations from the caller.

 

-M@



#509 TheMole OFFLINE  

TheMole

    Dragonstomper

  • 819 posts
  • Location:Belgium

Posted Fri Mar 15, 2019 3:00 AM

For reference, register usage is explained in this post: http://atariage.com/...e-ti/?p=2033241

Also worth looking at the next few posts, which explain the calling convention.

 

Do keep in mind there's been quite a few patches since, so there's a chance it has changed a bit in the meantime.


Edited by TheMole, Fri Mar 15, 2019 3:04 AM.


#510 Tursi OFFLINE  

Tursi

    Quadrunner

  • 5,572 posts
  • HarmlessLion
  • Location:BUR

Posted Sat Mar 16, 2019 12:21 AM

Just getting back into this again... but I'm running into a case here where code that uses "printf" (my own printf from libti99) is being compiled to use "puts", for which I have no implementation. ;) It seems to be recognizing that there are no additional arguments and trying to simplify it. I'm going to look for a switch (and probably just add puts), but in case anyone else runs into that, I had to look at the assembly to see what was going on. ;)

 

There's an addendum to this - it also changes the strings by removing any trailing carriage returns, since puts normally does that for you.


Edited by Tursi, Sat Mar 16, 2019 12:30 AM.


#511 chue OFFLINE  

chue

    Moonsweeper

  • 345 posts

Posted Sat Mar 16, 2019 3:21 AM

I'm going to look for a switch...

 

You could try the no-builtin compiler flag.  I've got a note in one of my makefiles that says:

 

# -fno-builtin
# Do not substitute (optimize) other functions for printf, e.g. puts or putchar etc.


#512 Tursi OFFLINE  

Tursi

    Quadrunner

  • 5,572 posts
  • HarmlessLion
  • Location:BUR

Posted Sat Mar 16, 2019 4:29 PM

That's the one I wanted... I actually never got around to looking for it, I just implemented puts instead. ;) But I'm sure I'll hit that again someday, and before now, I'd seen that switch but was never sure what it did...






0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users