As per boz's suggestion, here is a small tutorial on parameter passing between C and Assembler. Just wrote it in 20 minutes so apologies if it doesn't make sense. Please ask away if you have questions:
Code: Select all
Parameter Passing from C to ASM Using Belboz's Jagdev Environment Toolset
-------------------------------------------------------------------------
This took me a little while to figure out so it makes sense to pass on
what I have learnt.
When writing Jaguar code that has both C and Assembler using this environment
you will be using SMAC to compile the assembler and VBCC to compile the
code. Using these tools are not covered here but it is important to understand
that this tutorial relates to this specific setup. So lets begin...
Lets say that you have written an optimised 68000 assembler routine to display
a string on screen but your core code is in C so you want to be able to pass
the X and Y coordinate to display at as well as the memory address of the
string to display.
Although not necessary to compile, it is good practise to have a forward
decleration of this function in your C code before you use it. It also helps
the C compiler to ensure that correct variables are passed to the routine in
question. The forward decleration below shows what this may look like:
extern void foo_str(unsigned long int x, unsigned short int y, char *str);
Remember, this function does not exist in C code and is just a 68000 routine
in an assembly (.s) file.
Using this function in C is, as I am sure you know, a simple task. For example,
lets call it from func1():
void func1(void) {
char *string = "Hello World!\0";
foo_str(10, 12, string);
}
Thats the C end all sorted, no black magic or dark arts, just simple C coding.
Now, the fun stuff, reading in those parameters. But first, lets understand
where the passed parameters, X, Y and string, have gone. Just before the
routine was called these parameters were placed on the stack at the top of RAM
and this is where we need to get them from. Each parameter, regardless of
whether it is a byte (8-Bit), short (16-Bit) or long (32-bit) are stored in
long words (32-bit) on the stack. I want to avoid all the mechanics of stack
management in this tutorial, as thankfully much of it is managed for us. The
key information to know is that we will be using the A7 register as this has
the base address of the stack. Each parameter on the stack has a byte index,
so combining the A7 address with the parameter index we can obtain our
parameters.
All parameter indexes start at $4 (hex) with each parameter being stored four
further bytes (log word) later. So with our example in mind, the parameter
indexes are:
Parameter Index
x $4
y $8
str $c
The index increases by 4 bytes for each parameter passed, for example if we
had a function passing six parameters we would have the following indexes:
Parameter Index
param1 $4
param2 $8
param3 $c
param4 $10
param5 $14
param6 $18
Now that we know what indexes we can find our parameters at we need to
combine this with the A7 stack register. So, in our code, if we want to
get the contents of the first parameter and place them into register d0
we would type:
move.l ($4,A7),d0
Its that simple. Well, not quite;)
You will have noticed earlier that our second parameter was a short integer
and if we try to access it from index $8 we will get zero. Because the stack
is made up purely of long integers we need to add to the index to obtain
short integers. Parameter 2's base index is $8, we want the short integer
stored there so we need to add $2 to the base index. Therefore, to load
our second parameter into d1, we would type;
move.w ($a,A7),d1
Our third parameter, is an address to a string of characters. All addresses
are long integers so we can read directly from the parameter index, as such;
move.l ($c,A7),a0
So, our 68000 routine may look something like this;
_foo_str:
move.l ($4,A7),d0 ; Get X Parameter
move.w ($a,A7),d1 ; Get Y Parameter
move.l ($c,A7),a0 ; get String Address
; .... Do Some Processing with this Data ...
rts
It is VERY IMPORTANT to remember to preceed your function label with an
underscore so that the Linker can correctly marry up C and Assembler
functions.
This is where the story should end, and when I first was playing around with
this I though so too. But I forgot one key issue. When calling assembler
routines from C, YOU MUST ensure that your assembler function saves all the
registers you use in your assembler function to the stack before processing
and return them after processing is completed.
THIS AFFECTS THE STACK INDEXES !!!!!!! ARGGGGHHH !!!!
But it is relatively straight forward to understand. In our example, code
above we used registers d0, d1 and a0 in our routine. First, lets ensure
these get pushed to the stack at the start of the function and popped off
at the end. Our code would look like this:
_foo_str:
movem.l d0-d1/a0,-(sp); Save registers
move.l ($4,A7),d0 ; Get X Parameter
move.w ($a,A7),d1 ; Get Y Parameter
move.l ($c,A7),a0 ; get String Address
; .... Do Some Processing with this Data ...
movem.l (sp)+,d0-d1/a0; Restore registers
rts
The problem you may have noticed is that if our passed C parameters are on
the stack and now are saved registers are also then the indexes we use to
access our parameters are wrong. If you did, then you are correct :)
To resolve this issue we need to add the number of bytes of stack space used
for saving our registers to the base parameter indexes. In this example, we
have saved (pushed) three registers to the stack, each register being a long
integers, so four bytes long each. Therefore we have increased the stack by
12 bytes (or $c in hex).
To make sure we can access our parameters we need to add the stack increase
to the base indexes, so our code would now look like this;
_foo_str:
movem.l d0-d1/a0,-(sp); Save registers
move.l ($10,A7),d0 ; Get X Parameter ($4 + $c)
move.w ($16,A7),d1 ; Get Y Parameter ($a + $c)
move.l ($18,A7),a0 ; get String Address ($c + $c)
; .... Do Some Processing with this Data ...
movem.l (sp)+,d0-d1/a0; Restore registers
rts
This covers the basics of parameter passing in C. I have not covered
return addresses here and will probably do so soon.
Hopefully, this kinda makes some sense. Am happy to answer any questions
you may have....
Code: Select all
/*
x X Pixel Position to Display Sprite on Screen
y Y Pixel Position to Display Sprite on Screen
width Width of Sprite
height Height of Sprite
data Address of Sprite Data
wflag Width Flag for Blitter Routine (e.g WID16)
trans Transparent color 0 flag
*/
#define TRANS 1 /* Transparent or Solid Color Sprites (color=black) */
#define SOLID 0
extern void sprite(unsigned short int x, unsigned short int y,
unsigned short int width, unsigned short int height,
char *data, unsigned int wflag, unsigned int trans);
Code: Select all
void func1(void) {
sprite(140, 112, 48, 28, dk01, WID48, SOLID); /* Display Donkey Kong */
}
Code: Select all
;---------------------------------------------------------------------------------------------------
; Procedure: Display a Sprite
; Registers: d3 - Height and Width of Sprite
; d4 - Scratch Register
; d5 - X,Y Screen Location
; d6 - Scratch Register
;---------------------------------------------------------------------------------------------------
_sprite:
movem.l d3-d6,-(sp) ; Save registers
.wait:
move.l B_CMD,d6 ; Wait for blitter to finish
ror.w #1,d6 ; Check if blitter is idle
bcc.b .wait ; Bit was clear -> busy
move.l _activebuf,d6 ; Obtain active buffer flag
bne .buf1
move.l #(_videobuffers + (BMP_WIDTH*BMP_HEIGHT)),d6; Set address for buf1 if buf0 active
bra .setaddr
.buf1:
move.l #_videobuffers,d6 ; Set address for buf0 if buf1 active
.setaddr:
move.l d6,A1_BASE ; Point to destination address
move.l #PIXEL8|XADDPIX|WID256|PITCH1,A1_FLAGS ; Set destination flags, 256=size of vid buf
move.l ($28,a7),d4 ; Get address generation width from stack
or.l #PIXEL8|XADDPIX|PITCH1,d4 ; Add additional source data flags
move.l d4,A2_FLAGS ; Set source data flags
move.w ($16,a7),d5 ; Get screen x value from stack
swap d5 ; Swap register
move.w ($1a,a7),d5 ; Get screen y value from stack
move.l d5,A1_PIXEL ; Set pixel destination x,y position
move.w ($22,a7),d3 ; Get sprite height from stack
swap d3
move.w ($1e,a7),d3 ; Get sprite width from stack
move.l d3,B_COUNT ; Set height and width counters
moveq #0,d6 ; Zero register
move.l d6,A2_PIXEL ; Set pixel data x,y position to 0,0
move.w #1,d6 ; Set single line step
swap d6 ; In upper word of long
neg.w d3 ; Negate pixel data width
move.w d3,d6 ; Set pixel data width in low word
move.l d6,A1_STEP ; Set destination stepping
move.l d6,A2_STEP ; Set source stepping
move.l ($24,a7),d6 ; Load sprite data address
move.l d6,A2_BASE ; Set source data address to sprite address
move.l ($2c,A7),d4
bne .transparent ; Get solid/transparent then start the blitter
move.l #SRCEN|DSTEN|UPDA1|UPDA2|LFU_REPLACE,B_CMD
jmp .exit
.transparent:
move.l #SRCEN|DSTEN|UPDA1|UPDA2|LFU_REPLACE|BKGWREN|DCOMPEN,B_CMD
.exit:
movem.l (sp)+,d3-d6 ; Restore registers
rts
Enjoy