title: Why does this simple counting loop in assembly start behaving eratically when I add a random floating point instruction?
body: <p>I am working on a bare metal OS for the Raspberry Pi model B, which features an ARM1176-JZF-S processor. While working on implementing the sine function for a math library, I encountered something very strange, which I've whittled down to a small-ish minimum reproducible example. </p>
  
  <p>The following code counts up from zero to four, and prints out each number with spaces in between:</p>
  
  <pre><code>    mov     r4, #0              // Initialize counter to 0
  
  c_loop$:
      ldr     r0, =IntString      // Convert counter to a string
      mov     r1, r4
      bl      int_to_str
  
      ldr     r0, =IntString      // Print the string
      ldr     r1, =0x00000FF0     // (Green text on black background)
      bl      print
  
      ldr     r0, =Space          // Print a space
      ldr     r1, =0x00000FF0     // (Green text on black background)
      bl      print
  
      mov     r5, #0x1000000      // Pause for a beat
  c_pause$:
      subs    r5, #1
      bne     c_pause$
  
      add     r4, #1              // Increment counter
      cmp     r4, #5              // Repeat until counter = 5
      blt     c_loop$
  
  halt:                           // Wait forever
      b       halt
  </code></pre>
  
  <p>The functions <code>int_to_str</code> and <code>print</code> were both written by me, and work fine. To be clear, they are not printing to any kind of output stream; they just write pixels in the shape of numbers directly to a frame buffer, which I got from the GPU through the mailbox system. The label <code>IntString</code> is a space for me to store the conversion of the counter to a string so I can print it out, and the label <code>Space</code> points to a string that's just a single space. <strong>This code works as intended</strong> and I see the numbers displayed on the screen.</p>
  
  <p>Here's what's odd. Have a look at this floating-point operation:</p>
  
  <pre><code>    vadd.f32    s2, s0, s1      // What the heck is happening here?
  </code></pre>
  
  <p>When I add this into the loop right before the line where I increment the counter, I get different behavior entirely. Rather than printing "0, 1, 2, 3, 4", I now I see "0, 1, 0, 1, 0, 1, ..." repeating forever. Why is this happening? Why does the floating point instruction have any effect on this code at all?</p>
  
  <p><strong>Important additional info:</strong> A while ago, I was working on some code to draw a Mandelbrot fractal to the screen, using floating point arithmetic to do the calculations. Back then I believed that my Raspberry Pi had a Cortex A7 processor (which is what the newer models have) and I turned to the <a href="https://static.docs.arm.com/ddi0463/f/DDI0463F_cortex_a7_fpu_r0p5_trm.pdf" rel="nofollow noreferrer">Cortex A7 Floating-Point Unit Technical Reference Manual</a> which says that:</p>
  
  <blockquote>
    <p>To use the Cortex-A7 FPU in Secure state and Non-secure state, first define the NSACR and then define the CPACR and FPEXC registers to enable the Cortex-A7 FPU.</p>
  </blockquote>
  
  <p>It gave the following code snippet to accomplish this task:</p>
  
  <pre><code>    MRC     p15, 0, r0, c1, c1, 2
      ORR     r0, r0, #3&lt;&lt;10  // enable fpu
      MCR     p15, 0, r0, c1, c1, 2
  
      LDR     r0, =(0xF &lt;&lt; 20)
      MCR     p15, 0, r0, c1, c0, 2
  
      MOV     r3, #0x40000000
      VMSR    FPEXC, r3
  </code></pre>
  
  <p>For some reason, this worked, and my Mandelbrot fractal appeared. Anyway, <strong>this snippet is present the program I'm working on today</strong>, directly above the code shown. When I remove it, I get <em>different</em> unexpected behavior. The program prints "0, 0, 0, ..." -- an infinite series of just 0's instead of 0's and 1's.</p>
  
  <p><strong>More details:</strong> My best guess about what's going on here is that the s0 and s1 floating point registers initially contain garbage, and that adding them together can raise an exception. This would explain a detail I haven't mentioned yet, which is that the code <em>occasionally</em> works even with the floating point instruction included -- maybe one time in five.</p>
  
  <p>In order to test this theory, I tried setting all registers involved to zero right before the counting loop begins:</p>
  
  <pre><code>    mov     r0, #0
      vmov    s0, r0
      vmov    s1, r0
      vmov    s2, r0
  </code></pre>
  
  <p>And lo and behold, the loop worked again. However, as a further test, I decided to set both s0 and s1 to the maximum value a float can hold, reasoning that this should yield an overflow error and cause the unexpected behavior to return:</p>
  
  <pre><code>    ldr     r0, =0b01111111011111111111111111111111
      vmov    s0, r0
      vmov    s1, r0
      vmov    s2, r0
  </code></pre>
  
  <p>But this too leads to the correct counting behavior!</p>
  
  <p>I'm at a loss for what's going on here. What's causing this?</p>
  
  <p><strong>Update:</strong> I've just noticed an issue. The code I'm using to assemble .s files into .o files is this:</p>
  
  <pre><code>    arm-none-eabi-as -o $@ $&lt; -mfpu=vfpv4 -mcpu=cortex-a72 -mfloat-abi=hard
  </code></pre>
  
  <p>But this has two issues. One, the <code>vfpv4</code> is incorrect as the model B features VFPv2, and two, <code>cortex-a72</code> is incorrect as the model B features an ARM1176-JZF-S.</p>
  
  <p>Fixing the first of these two issues doesn't change any of the behavior mentioned above (I re-tried each example and got the same results). The second issue seems more serious, however, since the man page for <code>arm-none-eabi-as</code> doesn't list the model B's processor type as one of the options. I will investigate further and post an update once I know more.</p>