CS217 Precept 6: SPARC Assembly

The goal of this precept is to give you a chance to write some SPARC assembly code. We will write four code fragments: scalar add, array add, structure print,and cat.

Scalar add

The first fragment adds one to a global variable and prints out the new value. To allocate space for an initialized global variable, we must: We want to be able to run our code fragment, so we will enclose it with the goop needed to set-up the function, _main: And to end the function _main: Once we have all the surrounding goop, the code to read the current value of _a, the global variable, is easy. We need to: Finally, we want to print out the new value of _a. To print _a's value using printf, we need to: Now that we have been through all the pieces, here is the full code:
	.seg "data"
	.align 4
	.global _a
_a:	.word 10
	

	.seg "text"
	.global _main
	.align 4
	.proc 4
_main:	save %sp, -96, %sp	! allocate stack space, advance register window

	set _a, %l0
	ld [%l0], %l1           ! load _a into %l1
	add %l1, 1, %l1		! _a = _a + 1
	st %l1, [%l0]           ! write value back into _a

	set str, %o0
	ld [%l0], %o1
	call _printf; nop       ! printf("%d\n", _a);

	ret; restore		

	.global str
str:	.byte 37		! %
	.byte 100		! d
	.byte 10		! \n
	.byte 0			


Array add

The second fragment adds up the values in an initialized global array and prints the final result. The directives needed to set up the array are very similar those used to set up the global variable in the previous example. The only difference is that we have one .word directive for each element in the array.
	.seg "data"
	.align 4
	.global _b
_b:	.word 0
	.word 1
	.word 2
	.word 3
	.word 4
We need the same goop to set up _main.
	.seg "text"
	.global _main
	.align 4
	.proc 4
_main:	save %sp, -96, %sp	! allocate stack space, advance register window
The main part of the function executes the equivalent of a "for" loop:
	for (i=0; i < 5; i++)
	   total = total + b[i]
To do this, we need: The following code initializes these registers:
	set 0, %l0		! i = 0
	set _b, %l2		! base address
	set 0, %l3		! total
The next sequence of code does the work of the loop. Notice that to calculate the address of b[i], we add the base address (_b) to i*4. Why times 4?
L1:	
	cmp %l0, 5		! i < 5
	bge L2
	nop
	sll %l0, 2, %l4
	ld [%l4+%l2], %l4	! %l4 = a[i]
	add %l3, %l4, %l3	! total = total + a[i]
	add %l0, 1, %l0		! i++
	ba L1
	nop
Another way to do this eliminates the need to multiply by four each iteration, by storing the value i*4 in a register.
L1:	
	cmp %l0, 20		! i*4 < 5*4
	bge L2
	nop
	ld [%l4+%l2], %l4	! %l4 = a[i]
	add %l3, %l4, %l3	! total = total + a[i]
	add %l0, 4, %l0		! %l0 = i*4
	ba L1
	nop
Here's the full text of the example. Notice that we print out the value of total at the end of the function using a code sequence that is very similar to our earlier example.
	.seg "text"
	.global _main
	.align 4
	.proc 4
_main:	save %sp, -96, %sp	! allocate stack space, advance register window
	set 0, %l0		! i = 0
	set 5, %l1		! upper bound	
	set _b, %l2		! base address
	set 0, %l3		! total
	
L1:	
	cmp %l0, %l1		! i < n
	bge L2
	nop
	sll %l0, 2, %l4
	ld [%l4+%l2], %l4	! %l4 = a[i]
	add %l3, %l4, %l3	! total = total + a[i]
	add %l0, 1, %l0		! i++
	ba L1
	nop
L2:	
	set str, %o0
	mov %l3, %o1
	call _printf            ! printf("%d\n", total)
	nop

	ret; restore            ! return to caller.

Structure Print

Our third fragment prints out the values in an initialized structure, called _c. The following contains the directives set up the structure and the instructions to set up the function, _main.
	.align 4
	.global _c
_c:	.word 25 		! first field is an integer
	.ascii "d"              ! second field is a character
	.align 4                ! realign to a 4-byte boundary
	.word 27                ! third field is an integer

	.seg "text"
	.global _main
	.align 4
	.proc 4
_main:	save %sp, -96, %sp	! allocate stack space, advance register window

To print the three fields using printf, we need to: and then call printf.
	set _c, %l0
	set str1, %o0
	ld [%l0], %o1		! first field of c
	ldsb [%l0+4], %o2       ! second field of c
	ld [%l0+8], %o3		! third field of c
	call _printf
	nop

	.global str1
str2:	.asciz "%d \t %c \t %d\n"


Here's the full text:
	.align 4
	.global _c
_c:	.word 25 		! first field is an integer
	.ascii "d"              ! second field is a character
	.align 4                ! realign to a 4-byte boundary
	.word 27                ! third field is an integer

	.seg "text"
	.global _main
	.align 4
	.proc 4
_main:	save %sp, -96, %sp	! allocate stack space, advance register window

	set _c, %l0
	set str1, %o0
	ld [%l0], %o1		! first field of c
	ldsb [%l0+4], %o2       ! second field of c
	ld [%l0+8], %o3		! third field of c
	call _printf
	nop

	ret; restore            ! return to caller.

	.global str1
str2:	.asciz "%d \t %c \t %d\n"



cat

Our final example implements a very simple version of the unix command, cat. It reads one character at a time from stdin and write ones character at a time to stdout. To implement this, we need to Here's the code:
	.seg "data"
	.global _buf     	! allocate a buffer to hold a character
_buf:	.byte 0
	
	.seg "text"
	.align 4
	.proc 4
	.global _main
	
_main:	save %sp, -96, %sp

Loop:		
	set 0, %o0		! read from stdin
	set _buf, %o1    	! store char in _buf
	set 1, %o2      	! read one character
	call _read
	nop

	cmp %o0, %g0            ! have we hit the EOF?
	be END
	nop

	set 1, %o0		! write to stdout
	set _buf, %o1		! get char from _buf
	set 1, %o2		! write one character
	call _write
	nop

	ba Loop
	nop

END:	ret
	restore