Key concepts in SPARC assembly related to our project ------------------------------------------------------------------------ Complete SPARC reference manual can be found in http://docs.sun.com/app/docs/doc/816-1681 ------------------------------------------------------------------------ - statement syntax mnemonic_opcode operands ! comments also C-style comments (i.e., /* */) are acceptable other statements, called "pseudo-ops", are available to control the assembler's actions or to specify data. In general, initialized global variables are in the ".data" section, uninitilized global variables are in the ".bss" section, and the code is in the ".text" section. Following is an example helloword.s (from hello.pasc) program: .section ".data" .align 4 $S1: .asciz "hello world\n" .skip 243 .section ".bss" .align 4 .section ".text" /* start of main program */ .align 4 .global main .type main, #function main: /* write $S1 */ set $S1, %o0 call lib_write_str nop /* main program exit */ set 0, %o0 call exit nop - Registers Sparc has 32 general purpose integer registers visible to the program at any given time. Of these, 8 registers are global registers and 24 registers are in a register window. A window consists of three groups of 8 registers, the out, local, and in registers. Register Group Mnemonic Register Address global %g0-%g7 r[0]-r[7] out %o0-%o7 r[8]-r[15] local %l0-%l7 r[16]-r[23] in %i0-%i7 r[24]-r[31] - SPARC assembly instructions: standard formats src_reg, src_reg, dest_reg src_reg, immediate, dest_reg ! -4096 <= immediate < 4096 synthetic instructions clr reg => or %g0, %g0, reg mov immediate, dest_reg => or %g0, immediate, dest_reg mov src_reg, dest_reg => or %g0, src_reg, dest_reg code example ...... mov 9, %l0 !initialize x sub %l0, 1, %o0 !(x - 1) into %o0 sub %l0, 7, %o1 !(x - 7) into %o1 call .mul nop !result in %o0 mov %o0, %l1 !store it in y, %l1 = 8*2 = 16 The arithmatic operations are performed on registers. To perform the operations on memory, you must load the memory into registers, perform the operation, and store the results back to memory. See the following example code segment that performs i = j+k+20: .section ".data" .align 4 .section ".bss" .align 4 A: .skip 256 I: .skip 4 J: .skip 4 K: .skip 4 B: .skip 1 .align 4 C: .skip 1 .align 4 $T1: .skip 4 .section ".text" /* start of main program */ .align 4 .global main .type main, #function main: /* I := J */ set J, %l7 ld [%l7], %l0 set I, %l7 st %l0, [%l7] /* $T1 := K */ set K, %l7 ld [%l7], %l0 set $T1, %l7 st %l0, [%l7] /* I := I + $T1 */ set I, %l7 ld [%l7], %o0 set $T1, %l7 ld [%l7], %o1 add %o0, %o1, %o2 set I, %l7 st %o2, [%l7] /* $T1 := 20 */ set 20, %l0 set $T1, %l7 st %l0, [%l7] /* I := I + $T1 */ set I, %l7 ld [%l7], %o0 set $T1, %l7 ld [%l7], %o1 add %o0, %o1, %o2 set I, %l7 st %o2, [%l7] /* main program exit */ set 0, %o0 call exit nop - SPARC Delayed Branching If a branch or CALL is done on a SPARC, the new address is loaded into the nPC, not the PC. Effect: The instruction following the branch is executed before the branch takes effect. The position immediately following any branch or call instruction is called the "delay slot", and the instruction in that position is the "delay instruction". Example: Addr Code ---- ---------------------- 1000 addcc %g0,%g0,%g0 1004 be where 1008 subcc %g0,123,%L1 100C st %L1,[%i0+%i1] ... 2000 where: add %L1,%G0,%L2 2004 ld [%i0+%i1],%L3 Effect: PC nPC What's happening ---- ---- ----------------------- 1000 1004 addcc executing, be being fetched 1004 1008 be executing, subcc being fetched 1008 2000 subcc executing, add being fetched 2000 2004 add executing, ld being fetched ***ALWAYS FILL THE DELAY SLOT!!*** What can be put into the delay slot? Some useful instruction that should be executed whether you branch or not. Some instruction that does useful work only when you branch (or when you don't branch), but doesn't do any harm if executed in the other case. When all else fails, a NOP instruction. What MUST NOT be put into the delay slot? Anything that sets the CC that the branch decision depends on. The branch instruction makes the decision on whether to branch or not right away but it doesn't actually do the branch until after the delay instruction. (Only the branch is delayed, not the decision.) Another branch instruction. (What happens if you do this is not even defined! The result is unpredictable!) A "set" instruction. This is really two instructions, not one, and only half of it will be in the delay slot. (The assembler will warn you about this.) Example 1: Loop: ... ... add %L1,%L2,%L1 !Add %L2 to the sum in %L1 add %L3,1,%L3 !Increment the counter ba Loop !Back to the Loop again nop !The delay slot Loop: ... ... add %L1,%L2,%L1 !Add %L2 to the sum in %L1 ba Loop !Back to the Loop again add %L3,1,%L3 !Increment the counter (delay slot) Example 2: Loop: set Array,%L4 !Put Array's address in %L4 ... subcc %L2,%L3,%G0 !Compare %L2 to %L3 bne Loop !Loop until they are equal ! ------------------- ! Another line has been finished. ! Increment the line counter in %G1 !-------------------- add %G1,1,%G1 ... The correct code is: Loop: set Array,%L4 !Put Array's address in %L4 ... subcc %L2,%L3,%G0 !Compare %L2 to %L3 bne Loop !Loop until they are equal nop add %G1,1,%G1 ... Without the "nop" in the delay slot, the "add" will be done every time through the loop (the add will be in the delay slot). Since we can't put the "subcc" n the delay slot (the branch depends on it), and we can't put the "set" in the delay slot either, we'll have to settle for a "nop". - Register window SPARC has overalpping register window (optimization for routine calls) w_{i-1} | | w_{i+1} | | outs locals |ins | |outs| locals ints | | | |outs locals |ins | | | | w_i | ^ | | overlaped register. save instruction decreases the window number. restore instruction increases the window number. Epilogue/prologue in a procedure: function: save %sp, -C, %sp ; perform function, leave return value, ; if any, in register %i0 upon exit ret ; jmpl %i7+8, %g0 restore ; restore %g0,%g0,%g0 To pass a parameter from a caller to callee: caller assign value to %o0 callee references value in %i0. If there is more than 6*4=24 to be passed to a procedure: use %o0 to %o5 for the 24 bytes and put the rest of the bytes in the stack. calling a procedure: mov 0, %o0 call exit nop - Activation record and stack: The stack pointer (%sp or %o6) must always point to a free block of 64 bytes. This area is used by the operating system (Solaris, SunOS, and Linux at least) to save the current local and in registers upon a system interupt, exception, or trap instruction. This can occur at any time. Other aspects of register relations with memory are programming convention. The typical, and recommended, layout of the stack is shown in the following: low addresses +-------------------------+ %sp --> | 16 words for storing | | LOCAL and IN registers | +-------------------------+ | one-word pointer to | | aggregate return value | +-------------------------+ | 6 words for callee | | to store register | | arguments | +-------------------------+ | outgoing parameters | | past the 6th, if any | | (additional parameters) | +-------------------------+ | local variables | | temporaries | | other space that the | | compiler needs | +-------------------------+ %fp --> high addresses First 24-byte arguments are in %o0..%o5 (reference in the routine as %i0..%i5 due to the register window. Whatever beyond 24 bytes need to store in the stack. Temporaries for a procedure as well as the local variables needs to be allocated on the stack to deal with recursive routine calls. To reference these variables, use the offset of fp. Example: test7.pasc PROGRAM hello IS var i, j, k: integer; procedure aa is var i, j, k: integer; begin i := j+k+20; end; BEGIN i := j + k + 20; END. Example: test7.s .section ".data" .align 4 .section ".bss" .align 4 I: .skip 4 J: .skip 4 K: .skip 4 $T1: .skip 4 .section ".text" /* start of main program */ .align 4 .global main .type main, #function main: /* I := J */ set J, %l7 ld [%l7], %l0 set I, %l7 st %l0, [%l7] /* $T1 := K */ set K, %l7 ld [%l7], %l0 set $T1, %l7 st %l0, [%l7] /* I := I + $T1 */ set I, %l7 ld [%l7], %o0 set $T1, %l7 ld [%l7], %o1 add %o0, %o1, %o2 set I, %l7 st %o2, [%l7] /* $T1 := 20 */ set 20, %l0 set $T1, %l7 st %l0, [%l7] /* I := I + $T1 */ set I, %l7 ld [%l7], %o0 set $T1, %l7 ld [%l7], %o1 add %o0, %o1, %o2 set I, %l7 st %o2, [%l7] /* main program exit */ set 0, %o0 call exit nop /* routine AA */ .align 4 .type AA, #function AA: save %sp, -112, %sp /* I := J */ ld [%fp-8], %l0 st %l0, [%fp-4] /* $T3 := K */ ld [%fp-12], %l0 st %l0, [%fp-16] /* I := I + $T3 */ ld [%fp-4], %o0 ld [%fp-16], %o1 add %o0, %o1, %o2 st %o2, [%fp-4] /* $T3 := 20 */ set 20, %l0 st %l0, [%fp-16] /* I := I + $T3 */ ld [%fp-4], %o0 ld [%fp-16], %o1 add %o0, %o1, %o2 st %o2, [%fp-4] ret restore