SHDesigns: Embedded Systems Design, Consulting and Developer Resources Page hits:

Softools WinIDE Tips

Using Assembly language:

1. Lables with #asm blocks are not visible by the current C file even if they are declared global. The only way around this is to use a separate asm file.

Use a double colon in the _asm names to make them global:

#extern near int asmFunc(void)
#asm
_asmFunc::
 ....
 ret

Note: the above will be visiable as "near asmFunc" in C a near labels have a '_' prepended. Similarly, far functions will need a '$' before the name:

#extern int asmFunc(void)
#asm
$asmFunc::
 ....
 ret

 

2. Use bare functions to define #asm functions, they are better that #asm blocks as they provide a prototype, i.e.

void my_asmfunc(void)
{
#asm
   ... code ...
#endasm
}

The only thing the 'C' function adds is a lret at the end. Also, the my_asmfunc will be visible to 'C' programs.

Use near for code that needs to be in root memory:

near void my_near_asmfunc(void)
{
#asm

 ........
#endasm
}

This will only add the return after the asm code and a .cseg at the start.

Using 'C' function definitions to wrap asm code handles the name mangling between C and asm code. C code will have a '$' prepended to far functions and an '_' prepended to near functions.

3. Accessing local variables and parameters.

Use the #pragma offset_labels on
Then you will be able to access local and parameters via ix+.name (you will need to preserve the IX register):

#pragma offset_labels on 
int near asmfunc(int param1, long param2) // near will have asm name of _asmfunc
{
  int lvar;
 #asm
  ld  hl,(ix+.param1) ; you could also use _HL=param1 before the asm block
  ld  de,(ix+.lvar) ; access local varaiable
  add  hl,de
 #endasm
  return _HL;  // return what the asm block left in hl
}

4. Global vars:

Lobal variables will need an underscore before the name:

int globvar;

#asm
   ld hl,(_globvar)
#endasm

5. Static Variables

Static variables can not be used in inline ASM code as the compiler uses an internal name that is unknown to the ASM blocks. You can get around this somewhat by using the register aliases shown in section 6.

6. Passing Regs to ASM code

The compiler can be used to pass regs to Inline ASM code. The variables _HL, _DE, _BC are aliases for the registers.

Example:

unsigned offset;

void add_index_offset()
{
 // example of a static that will not be visible to ASM code
static int index;
 _DE=offset;
 _HL=index; // do _HL last as it is likely used to load others
#asm
 xor a ; clear carry
 adc hl,de
; HL is now index+offset
#endasm
 return _HL;
}

If you write a function with an ASM block, you can use "return _HL" to return what the ASM code leaves in HL. This will actually not generate any code, but it will get rid of the warning of "function must return a value".

7. Getting rid of "variable not used" and "Function must return a value" warnings.

int my_asm_func(int param1, int param2)
{
#asm
 ld hl,(ix+.param1)
 ld de,(ix+.param2)
...........
#endasm
}

The above will generate 3 warnings. The compiler will warn about param1 and param2 not being used within the function and will complain about no return value.

You can turn off the warnings by surrounding the function with "#pragma warn" statements. But there is a better way:

int my_asm_func(int param1_notused, int param2_notused)
{
#asm
 ld hl,(ix+.param1_notused)
 ld de,(ix+.param2_notused)
...........
#endasm
 return _HL;
}

Including "notused" in a parameter name will get rid of the warnings. The "return _HL;" will tell the compiler to return what the ASM block left in HL (no code is generated as functions normally return int's in HL.)

 


Additional Information: Rabbit Libraries Home Page - SHDesigns Home Page