Writing all-assembly functions -- syntax, parameter passing, return value

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Writing all-assembly functions -- syntax, parameter passing, return value

Jump to solution
2,182 Views
tomlogic
Contributor III

I want to write an assembly version of this function:

 

 

uint16_t intel16_asm( uint16_t value);

 

Looking at the assembly generated for a C version of that function, it would appear that return address is passed in H:X, the parameter is on the stack (SP+1 holds MSB, SP+2 holds LSB) and the return is expected in H:X.  So, I came up with the following assembly to swap the byte order:

 

 

PSHX            ; push return addressPSHH LDHX 4, SP      ; load LSB of parameter to H, junk to XLDX 3, SP       ; load MSB of parameter to XRTS             ; return

 

 

So what syntax should I use to create this function?  Can I mark it as inline and have the compiler inline the assembly so it loads a 16-bit value into H:X and then does the PSHH PSHX PULH PULX sequence to swap the byte order?

 

 

I tried doing:

 

 

uint16_t intel16_asm( uint16_t value){ asm {  PSHH  PSHX  PULH  PULX  RTS }}

But then I get a warning because the function doesn't return.  Is it even safe to use RTS in the assembly?  If I take the RTS out, how do I indicate to the compiler that the return value is already loaded in H:X?  How do I tell the compiler not to push the return address for me?

 

If I want to write the 32-bit version of this function, I assume a 32-bit parameter is passed on the stack in a similar fashion?  How is a 32-bit return value passed out?

 

Finally, any recommended tutorials or references for learning HC08 assembly?  I did HC11 over 18 years ago, and don't remember anything about it.  I've been doing a lot of Rabbit assembly (originally based on Z80 instruction set) over the past 10 years, and so far the HC08 syntax has been very foreign to me.

 

-Tom

 

(HC08 with CodeWarrior 5.9.0)

Labels (1)
Tags (1)
0 Kudos
1 Solution
658 Views
tomlogic
Contributor III

Well, I finally figured out a workable solution, but it's really ugly.  Surely there's an easier way, maybe with just having some assembly outside of the C function?

 

 

#pragma MESSAGE DISABLE C1404  // Return expected#pragma NO_ENTRY#pragma NO_EXIT#pragma NO_FRAME#pragma NO_RETURNuint16_t intel16_asm( uint16_t value){ asm {  PSHH  PSHX  PULH  PULX  RTS }}#pragma MESSAGE DEFAULT C1404

 

 

My first assumption had been right (parameter 1 in H:X), but I second guessed myself and edited the message with the incorrect assumption that H:X had the return address (which wouldn't really make sense).

 

My confusion came from looking at the disassembly of another function without parameters.  It pushed H:X onto the stack right away, but maybe that was to make room for a return value...

 

 

View solution in original post

0 Kudos
5 Replies
659 Views
tomlogic
Contributor III

Well, I finally figured out a workable solution, but it's really ugly.  Surely there's an easier way, maybe with just having some assembly outside of the C function?

 

 

#pragma MESSAGE DISABLE C1404  // Return expected#pragma NO_ENTRY#pragma NO_EXIT#pragma NO_FRAME#pragma NO_RETURNuint16_t intel16_asm( uint16_t value){ asm {  PSHH  PSHX  PULH  PULX  RTS }}#pragma MESSAGE DEFAULT C1404

 

 

My first assumption had been right (parameter 1 in H:X), but I second guessed myself and edited the message with the incorrect assumption that H:X had the return address (which wouldn't really make sense).

 

My confusion came from looking at the disassembly of another function without parameters.  It pushed H:X onto the stack right away, but maybe that was to make room for a return value...

 

 

0 Kudos
658 Views
bigmac
Specialist III

Hello,

 

For the 16-bit case, the following appears to generate efficient code.

 

uint16_t intel16( uint16_t value){  (void)value;  // Avoids unused parameter compiler warning    __asm {    pulh    pulx    pshh    pshx  }  return value;}

 

Since the entry to the function already pushes the argument onto the stack from H:X, the idea is to pull off the stack, and replace to the stack in reverse order.  An interesting observation is that the compiler actually optimises away the function entry pushes, and the subsequent inline pulls, leaving the following resultant code for the function, that achieves the required result.

 

   pshh   pshx   ldhx  1,sp   ais   #2   rts

Regards,

Mac

 

0 Kudos
658 Views
CompilerGuru
NXP Employee
NXP Employee

Has been a while since I last did S08 asm programming, so just some hints.

Note that the calling convention is different for the S08 and for the orginal HC08. As the S08 support more addressing modes with LDHX/STHX, one 16 bit argument is passed in H/X. for the HC08 A and X is used.

Long returns (anything else > 2 bytes) are done with an additional in pointer argument to a buffer.

Check the compiler manual, should have the basic calling convention described in there.

I think using the pragmas is the best way, well the RTS could be done by the compiler, does not really matter.

An alternative would be a plain assembly implementation of the function.

Assembly code outside of function is not allowed, the compiler is not using the assembler but directly emits object files.

 

Daniel

 

0 Kudos
655 Views
tomlogic
Contributor III

Thanks for the info.  I am using an S08, not an HC08.  Here's what I came up with for a 32-bit swap:

 

 

#pragma NO_ENTRY#pragma NO_EXIT#pragma NO_FRAME#pragma NO_RETURNuint32_t swap32( uint32_t value){ /* Stack offset:  1: LSB of return address  2: MSB of return address  3: byte0 (LSB) param1  4: byte1 of param1  5: byte2 of param1  6: byte3 of param1  7: LSB of address of return value  8: MSB of address of return value */ asm {  LDHX 7, SP   ; H:X is index to return value  LDA 3, SP   ; load byte 0 of parameter  STA 3, X   ; save as byte 3 of return  LDA 4, SP   ; load byte 1 of parameter  STA 2, X   ; save as byte 2 of return  LDA 5, SP   ; load byte 2 of parameter  STA 1, X   ; save as byte 1 of return  LDA 6, SP   ; load byte 3 of parameter  STA 0, X   ; save as byte 0 of return  RTS }}

 

 

0 Kudos
655 Views
CompilerGuru
NXP Employee
NXP Employee

Looks reasonable.

In the comment, the MSB and LSB's are in the wrong order, S08's are big endian, therefore the MSB is at the lower address.

 

0 Kudos