Problem porting C code to ASM

Here you can ask questions or provide insights about how to use efficiently 6502 assembly code on the Oric.
User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Problem porting C code to ASM

Post by jbperin »

Hi all,

For performance purpose, I'm currently porting some C code to assembly language and I'm facing a problem that I can't understand.
Perhaps someone can help me.
I use OSDK, the code is written in C and I use asm(" ...") to locally replace C code with assembly.

I've got the following variables defined in zero page in a .s file:

Code: Select all

_dda3StartValue     .dsb 1
_dda3EndValue       .dsb 1
_dda3NbVal            .dsb 1
_dda3Increment     .dsb 1
I make these variable available in the C code by using the following definition:

Code: Select all

extern unsigned char   dda3StartValue;
extern unsigned char   dda3EndValue;
extern unsigned char   dda3NbVal;
extern signed char     dda3Increment;
My program works well when I write in C:

Code: Select all

        if (dda3EndValue > dda3StartValue) {
            dda3NbVal                = dda3EndValue-dda3StartValue;
            dda3Increment            = 1;
        } else {
            dda3NbVal                = dda3StartValue-dda3EndValue;
            dda3Increment            = -1;
        }
But its behaviour is very different when I replace by the following assembly lines:

Code: Select all

.(
lda _dda3StartValue:
cmp _dda3EndValue:
bcs else:
            lda _dda3EndValue: sec: sbc _dda3StartValue: sta _dda3NbVal:
            lda #1: sta _dda3Increment:
            jmp endif
else
            lda _dda3StartValue: sec: sbc _dda3EndValue: sta _dda3NbVal:
            lda #$FF: sta _dda3Increment:
endif
.)
Can anyone tells me what's the difference ?

What's even more strange is that I used the same translation for the same algo for just a different variable dda1 instead of dda3 and it works nice.
Is it possible that the bcs comes to cross a page and causes the issue. I really can't understand the phenomena.

NB: I know there's a possible optim in the else by avoiding the re-read _dda3StartValue .. it is not the topic here .. I just need to understand what make the behaviour different between the C and the ASM.

Thanks
User avatar
iss
Wing Commander
Posts: 1641
Joined: Sat Apr 03, 2010 5:43 pm
Location: Bulgaria
Contact:

Re: Problem porting C code to ASM

Post by iss »

What about to swap the variables ;) :

Code: Select all

...
lda  _dda3EndValue
cmp _dda3StartValue
bcs else
...
Additionally:
- bcs will be taken when > and =;
- after it you have C-flag set, no sec needed.
- is it possible to not use cmp at all (I asking myself).
User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Re: Problem porting C code to ASM

Post by jbperin »

I realize it is a bug in the compiler when mixing C code with inlined assembly language.

In fact, my C code portion to optimize is located between two C code portion

Code: Select all

if (condition) {
  Some_C_code_here;
} else {
  Some_other_C_code_here;
}

C_code_to_optimize;

if (other condition) {
  Some_More_Code_here;
}
and when I replace C_code_to_optimize by asm ("the corresponding assembly code")
the compiler does no longer care about the code when computing the jump to go over the else

Here's the branching that is done by the compiler:

Code: Select all

if (condition) {
  Some_C_code_here;
  JMP LabelENDIF
} else {
  Some_other_C_code_here;
}

asm ("the corresponding assembly code");

LabelENDIF
if (other condition) {
  Some_More_C_code_here;
}
So the assembly is bypassed

The problem can be avoided by inserting a junk C instruction in the midst to prevent the compiler from misbranching.

Code: Select all

if (condition) {
  Some_C_code_here;
  JMP LabelENDIF
} else {
  Some_other_C_code_here;
}
LabelENDIF

C_junk_instruction = 0;
asm ("the corresponding assembly code");

if (other condition) {
  Some_More_C_code_here;
}
:? I must admit I found it was hard to figure out ..
User avatar
iss
Wing Commander
Posts: 1641
Joined: Sat Apr 03, 2010 5:43 pm
Location: Bulgaria
Contact:

Re: Problem porting C code to ASM

Post by iss »

Can you attach real code to check exactly what happens.
Here I mean code which can be compiled and NOT your REAL code. :)
User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Re: Problem porting C code to ASM

Post by jbperin »

Here's the code in which I can switch between C or asm version by using the __USE_ASM_CODE__ macro definition:

Code: Select all

        if          (dda1NbVal > dda1NbStep) {
            dda1CurrentError     = dda1NbVal;
            dda1StepFunction     = &dda1Step1;
        } else if   (dda1NbVal < dda1NbStep) {
            dda1CurrentError     = dda1NbStep;
            dda1StepFunction     = &dda1Step2;
        } else {
            dda1CurrentError     = dda1EndValue;
            dda1StepFunction     = &dda1Step0;
        }

#ifndef __USE_ASM_CODE__
        if (dda3EndValue > dda3StartValue) {
            dda3NbVal                = dda3EndValue-dda3StartValue;
            dda3Increment            = 1;
        } else {
            dda3NbVal                = dda3StartValue-dda3EndValue;
            dda3Increment            = -1;
        }
#else
        asm (
            ":break2:.(:"
            "lda _dda3StartValue:"
            "cmp _dda3EndValue:"
            "bcs else3:"
            "lda _dda3EndValue: sec: sbc _dda3StartValue: sta _dda3NbVal:"
            "lda #1: sta _dda3Increment:"
            "jmp endif3:else3:"
            "lda _dda3StartValue: sec: sbc _dda3EndValue: sta _dda3NbVal:"
            "lda #$FF: sta _dda3Increment:"
            ":endif3:.):"
        );
#endif
        if (dda3NbVal > dda3NbStep) {
            dda3CurrentError     = dda3NbVal;
            dda3StepFunction     = &dda3Step1;
        } else if   (dda3NbVal < dda3NbStep) {
            dda3CurrentError     = dda3NbStep;
            dda3StepFunction     = &dda3Step2;
        } else {
            dda3CurrentError     = dda3EndValue;
            dda3StepFunction     = &dda3Step0;
        }
When compiled without the __USE_ASM_CODE__ defined, the C code is used and this is what it gives in the linked.s

Code: Select all

	lda _dda1NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda _dda1NbStep : sta tmp1 :
	lda tmp1 : sta tmp1 : lda #0 : sta tmp1+1 :
	lda tmp1 : cmp tmp0 : lda tmp1+1 : sbc tmp0+1 : bvc *+4 : eor #$80 : bmi *+5 : jmp Lmain232 : : :
	lda _dda1NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda1CurrentError :
	lda #<(_dda1Step1) : sta _dda1StepFunction : lda #>(_dda1Step1) : sta _dda1StepFunction+1 :
	jmp Lmain233 :
Lmain232
	lda _dda1NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda _dda1NbStep : sta tmp1 :
	lda tmp1 : sta tmp1 : lda #0 : sta tmp1+1 :
	lda tmp0 : cmp tmp1 : lda tmp0+1 : sbc tmp1+1 : bvc *+4 : eor #$80 : bmi *+5 : jmp Lmain234 : :
	lda _dda1NbStep : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda1CurrentError :
	lda #<(_dda1Step2) : sta _dda1StepFunction : lda #>(_dda1Step2) : sta _dda1StepFunction+1 :
	jmp Lmain235 :
Lmain234
	lda _dda1EndValue : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda1CurrentError :
	lda #<(_dda1Step0) : sta _dda1StepFunction : lda #>(_dda1Step0) : sta _dda1StepFunction+1 :
Lmain235
Lmain233
	lda _dda3EndValue : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda _dda3StartValue : sta tmp1 :
	lda tmp1 : sta tmp1 : lda #0 : sta tmp1+1 :
	lda tmp1 : cmp tmp0 : lda tmp1+1 : sbc tmp0+1 : bvc *+4 : eor #$80 : bmi *+5 : jmp Lmain236 : : :
	lda _dda3EndValue : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda _dda3StartValue : sta tmp1 :
	lda tmp1 : sta tmp1 : lda #0 : sta tmp1+1 :
	sec : lda tmp0 : sbc tmp1 : sta tmp0 : lda tmp0+1 : sbc tmp1+1 : sta tmp0+1 :
	lda tmp0 : sta _dda3NbVal :
	lda #<(1) : sta _dda3Increment :
	jmp Lmain237 :
Lmain236
	lda _dda3StartValue : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda _dda3EndValue : sta tmp1 :
	lda tmp1 : sta tmp1 : lda #0 : sta tmp1+1 :
	sec : lda tmp0 : sbc tmp1 : sta tmp0 : lda tmp0+1 : sbc tmp1+1 : sta tmp0+1 :
	lda tmp0 : sta _dda3NbVal :
	lda #<(255) : sta _dda3Increment :
Lmain237
	lda _dda3NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda _dda3NbStep : sta tmp1 :
	lda tmp1 : sta tmp1 : lda #0 : sta tmp1+1 :
	lda tmp1 : cmp tmp0 : lda tmp1+1 : sbc tmp0+1 : bvc *+4 : eor #$80 : bmi *+5 : jmp Lmain238 : : :
	lda _dda3NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda3CurrentError :
	lda #<(_dda3Step1) : sta _dda3StepFunction : lda #>(_dda3Step1) : sta _dda3StepFunction+1 :
	jmp Lmain239 :
Lmain238
	lda _dda3NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda _dda3NbStep : sta tmp1 :
	lda tmp1 : sta tmp1 : lda #0 : sta tmp1+1 :
	lda tmp0 : cmp tmp1 : lda tmp0+1 : sbc tmp1+1 : bvc *+4 : eor #$80 : bmi *+5 : jmp Lmain240 : :
	lda _dda3NbStep : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda3CurrentError :
	lda #<(_dda3Step2) : sta _dda3StepFunction : lda #>(_dda3Step2) : sta _dda3StepFunction+1 :
	jmp Lmain241 :
Lmain240
	lda _dda3EndValue : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda3CurrentError :
	lda #<(_dda3Step0) : sta _dda3StepFunction : lda #>(_dda3Step0) : sta _dda3StepFunction+1 :
Lmain241
Now, if I define the macro, the generated code in linked.s is:

Code: Select all

	lda _dda1NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda _dda1NbStep : sta tmp1 :
	lda tmp1 : sta tmp1 : lda #0 : sta tmp1+1 :
	lda tmp1 : cmp tmp0 : lda tmp1+1 : sbc tmp0+1 : bvc *+4 : eor #$80 : bmi *+5 : jmp Lmain232 : : :
	lda _dda1NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda1CurrentError :
	lda #<(_dda1Step1) : sta _dda1StepFunction : lda #>(_dda1Step1) : sta _dda1StepFunction+1 :
	jmp Lmain233 :
Lmain232
	lda _dda1NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda _dda1NbStep : sta tmp1 :
	lda tmp1 : sta tmp1 : lda #0 : sta tmp1+1 :
	lda tmp0 : cmp tmp1 : lda tmp0+1 : sbc tmp1+1 : bvc *+4 : eor #$80 : bmi *+5 : jmp Lmain234 : :
	lda _dda1NbStep : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda1CurrentError :
	lda #<(_dda1Step2) : sta _dda1StepFunction : lda #>(_dda1Step2) : sta _dda1StepFunction+1 :
	jmp Lmain235 :
Lmain234
	lda _dda1EndValue : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda1CurrentError :
	lda #<(_dda1Step0) : sta _dda1StepFunction : lda #>(_dda1Step0) : sta _dda1StepFunction+1 :
:break2:.(:lda _dda3StartValue:cmp _dda3EndValue:bcs else3:lda _dda3EndValue: sec: sbc _dda3StartValue: sta _dda3NbVal:lda #1: sta _dda3Increment:jmp endif3:else3:lda _dda3StartValue: sec: sbc _dda3EndValue: sta _dda3NbVal:lda #$FF: sta _dda3Increment::endif3:.):
Lmain235
Lmain233
	lda _dda3NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda _dda3NbStep : sta tmp1 :
	lda tmp1 : sta tmp1 : lda #0 : sta tmp1+1 :
	lda tmp1 : cmp tmp0 : lda tmp1+1 : sbc tmp0+1 : bvc *+4 : eor #$80 : bmi *+5 : jmp Lmain236 : : :
	lda _dda3NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda3CurrentError :
	lda #<(_dda3Step1) : sta _dda3StepFunction : lda #>(_dda3Step1) : sta _dda3StepFunction+1 :
	jmp Lmain237 :
Lmain236
	lda _dda3NbVal : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda _dda3NbStep : sta tmp1 :
	lda tmp1 : sta tmp1 : lda #0 : sta tmp1+1 :
	lda tmp0 : cmp tmp1 : lda tmp0+1 : sbc tmp1+1 : bvc *+4 : eor #$80 : bmi *+5 : jmp Lmain238 : :
	lda _dda3NbStep : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda3CurrentError :
	lda #<(_dda3Step2) : sta _dda3StepFunction : lda #>(_dda3Step2) : sta _dda3StepFunction+1 :
	jmp Lmain239 :
Lmain238
	lda _dda3EndValue : sta tmp0 :
	lda tmp0 : sta tmp0 : lda #0 : sta tmp0+1 :
	lda tmp0 : sta _dda3CurrentError :
	lda #<(_dda3Step0) : sta _dda3StepFunction : lda #>(_dda3Step0) : sta _dda3StepFunction+1 :
Lmain239
Look at where is located the label Lmain233 (which corresponds to the EndIf label). In the first case it is before the C code to optimize. In the other case it is after the optimized assembly code.

The inlined code that follows a else condition is bypassed .
User avatar
Dbug
Site Admin
Posts: 4444
Joined: Fri Jan 06, 2006 10:00 pm
Location: Oslo, Norway
Contact:

Re: Problem porting C code to ASM

Post by Dbug »

Which version of the OSDK are you using?
OSDK Version 1.18
Upgraded the compiler to version 1.39 with a fix for inline assembler directives not being generated at the proper location in the code
https://osdk.org/index.php?page=download
User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Re: Problem porting C code to ASM

Post by jbperin »

Dbug wrote: Wed Oct 06, 2021 8:48 pm Which version of the OSDK are you using?
I use OSDK 1.19

Code: Select all

Assembling the first part
Building the program Main at adress $C000 [OSDK 1.19]

Code: Select all

OSDK=C:\Users\...\osdk_1_19
User avatar
Dbug
Site Admin
Posts: 4444
Joined: Fri Jan 06, 2006 10:00 pm
Location: Oslo, Norway
Contact:

Re: Problem porting C code to ASM

Post by Dbug »

Just to check: Does the generated code changes if you enclose the asm block in its own set of { } ?
User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Re: Problem porting C code to ASM

Post by jbperin »

Yes :-)

It works nice if I surround the inlined code with {}.

Thank you for the trick .. I think it has to be done for any inlined code that immediately follows a closing bracket'}' from either a loop (for, while) or a conditionnal (if else).
Post Reply