The goal of this article is to introduce SPARC v8 architecture and SPARC assembly ; I hope it can also constitute a good introduction to RISC philosophy.
What is SPARC ?
The principles of RISC (Reduced Instructions Set Computer) are born in the
early 80's in two universities (Berkeley and Stanford) ; its philosophy is
the quest for simplicity and CPU speed. SPARC (Scalable Processor ARChitecture)
is a 32 bits RISC architecture created by Sun in 1987. It's an open
architecture, so that any manufaturer can make SPARC processors (like Philips,
VLSI, T.I., Fujitsu... already did). Its key features are :
- a load/store architecture : this means that only registers can be used
in data manipulation operations, and not memory locations. Memory is
organised in a linear address space of 2^32 bits which use "big-endian"
organisation (the MSB is stored first) ; a word is 32 bits wide (a 16 bits
data is a halfword).
- a large number of registers : from 2 to 32 sets of 24 general purpose
registers are available ; these 24 registers are local registers %l[0-7],
in registers %i[0-7] and out registers %o[0-7], all working in an
overlapping windows mechanism that will be explained later. The SPARC
architecture also provides 8 global registers %g[0-7], 32 registers for
floating-point operations (%f[0-31]) and some specific registers (%pc, %sp,
%psr, %y,...).
- a small set of simple instructions : to avoid translation from machine code
to microcode, SPARC instructions are directly implemented in hardware, and
therefore are very basic (mainly load/store, logical, arithmetic, branching).
All instructions are 4 bytes long, and most of them use 3 registers (source1,
source2, destination in that order). Assemblers also provide a set of
synthetic instructions, which are more "coder friendly", but does not really
exist for the processor (and therefore must be carefully used). These
synthetic instructions have most of the time less operands, so that the
corresponding real instructions often use %g0, a read-only register stuck
at 0 ; here are some aliases :
synthetic instruction | real opcode
nop sethi 0, %g0
ret jmpl %i7+8, %g0
mov reg_or_imm, reg or %g0, reg_or_imm, reg
cmp reg, reg_or_imm subcc reg, reg_or_imm, %g0
Enough with theory, let's see some code.
SPARC assembly basics
Let's start with an in-season "hello world" style program : '!' is used
for single line comments, /* .. */ is used for multiple lines comments).
!8 sub r1, r2 ; keep result and flags
subcc r1, r2, %g0 cmp r1, r2 ; discard result, keep flags
sub r1, 0, r2 mov r2, r1 ; keep result, discard flags
As last example, here's a simple anti-cracking method : a checksum on
our own code :
!8= 0 get next element
! (delay slot result is annulled)
mov %l2, %o0 ! store the result in sum (BDS)
ret ! Return to caller
restore ! Restore register windows (BDS)
endcksum: ! Tell the linker how big the
.size cksum,(.-cksum) ! procedure is.
!-------------------- MAIN -------------------------
.global main
main:
save %sp,-64,%sp ! allocate space for stack
set main, %o0 ! start address for cksum
sethi %hi(EndOfCRCZone),%o1 ! high part of end address
call cksum ! calculate cksum
or %o1,%lo(EndOfCRCZone),%o1 ! low part of end address (BDS)
- EndOfCRCZone
-
set 0x10954, %o1 ! load precalculated checksum
cmp %o0, %o1 ! is checksum correct ?
be End ! yes : exit
nop ! do nothing (BDS)
Error: ! no : display message
sethi %hi(.CRCError ),%o0 ! load higher part of string offset
call printf ! print the string
or %o0,%lo(.CRCError),%o0 ! add lower part of string offset (BDS)
- End
-
ret ! Return to caller
restore ! Restore register windows (BDS)
EndMain: ! Tell the linker how big the
.size main,(.-main) ! procedure is.
!8
|