0

The is leaving me baffled, and I really can't figure out why I am getting this problem. I am to create a program in c that outputs an unsigned value just before the C flag is set, using assembly to accumulate a variable with addcc, and send back the value to main in c. I believe the code I have for c is correct:

unsigned int func();

int main(void){
    printf("The max value before the C flag is set is: %u\n", func());
}

Now the problem comes in with the assembly...

.global func
func: save %sp, -128, %sp
addcc %g0, 1, %g0
clr %l0

loop:
bcs away
mov %l0, %i0
addcc %i0, 1, %l0
ba loop
nop

away:
ret
restore

What this should be doing is accumulating %l0, when the C flag is set passing the value back to %i0 and returning it. When I run this, I get 0. This doen's make sense to me, as I believe I should be getting a much larger number. Any help would be appreciated.

Dillon Burton
  • 363
  • 6
  • 16
  • The C flag indicates a carry? Then I'd guess it is set on incrementing when the value is `UINT_MAX`. Now, `UINT_MAX + 1` is 0. – Daniel Fischer Apr 02 '13 at 21:30
  • That's exactly what I assumed, the only problem is I have no idea how I could return the number before it resets. I can use: bcs (Branch when the c flag is set) or bcc (branch when the c flag is cleared) – Dillon Burton Apr 02 '13 at 21:32
  • Decrement the value by one after the flag is set (or is that cheating?). – Daniel Fischer Apr 02 '13 at 21:33
  • subcc? I attempted that, I get -1. – Dillon Burton Apr 02 '13 at 21:33
  • No I don't think it would be considered cheating. I though for a moment that it wasn't looping for some reason, debugged, and yes it is looping so it must be resetting. Subtracting one takes me to -1 though! Frustrating! – Dillon Burton Apr 02 '13 at 21:35
  • But you return and print an `unsigned int` in C, so it can't be `-1` (well, the bit-pattern of `UINT_MAX` usually is the same as that of the `int` -1). – Daniel Fischer Apr 02 '13 at 21:35

1 Answers1

3

The problem is that branches on Sparc are delayed -- the next instruction after the branch will be executed before the branch actually executes. So when you have:

bcs away
mov %l0, %i0

when the C flag is set and this branch is taken, the mov will still execute, overwriting the value in %i0 (the value before the last add that set C) with %l0 (the value after the add -- 0).

If you stick a nop in here:

bcs away
nop
mov %l0, %i0

it should give you the value you want.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • Awesome worked great! That never crossed my mind but it makes perfect sense. Just some clarification, in c, would this be the correct way to print an unsigned number? – Dillon Burton Apr 02 '13 at 21:41
  • May I comment "What the four-letter-word?" – Daniel Fischer Apr 02 '13 at 21:42
  • @DillonBurton: Yes, `%u` prints an `unsigned int` – Chris Dodd Apr 02 '13 at 23:58
  • @DanielFischer: [Branch delay slots](http://en.wikipedia.org/wiki/Delay_slot) are a common misfeature of early RISC designs. – Chris Dodd Apr 03 '13 at 00:05
  • @Chris Dodd: Or simply use `bcs,a away` to _annul_ the delay slot (not execute it if the branch is taken), http://stackoverflow.com/questions/604119/what-is-anulled-branch-different-from-regular-branch-instructions – FrankH. Apr 03 '13 at 08:40
  • @Chris Thanks. Surprisingly, the idea makes some sense. – Daniel Fischer Apr 03 '13 at 13:19
  • @FrankH.: except the annul flag has the reverse sense -- it skips the instruction if the branch is *not* taken, which is the reverse of what you want (and will cause an infinite loop here). You could rearrange the code to make the branch delay slots useful, or to make annuled slots useful, but that's beside the point. – Chris Dodd Apr 04 '13 at 17:05