This project was done for the ‘Computer Organization and Assembly Language’ class, with Prof. Borin from Unicamp.
Below you’ll find an IAS Computer simulator, written entirely in ARM assembly. The ias_engine.s file is the core that simulates the IAS computer. Interface.s is the simulator interface that lets the user run the whole program, run a single instruction, print the contents of registers and so on. Ias_state.s is the memory map of the IAS program, so your IAS code goes there. Main.s ties it all together.
IAS_ENGINE.S
.global step_instruction
.text
step_instruction:
push {r4-r6, lr}
@--check for PC overflow
ldr r0, =PC
ldr r0, [r0]
cmp r0, #1024
blt noOverflow
mov r0, #2
pop {r4-r6, pc}
noOverflow:
and r3, r0, #1
lsr r0, r0, #1
mov r1, #5
mul r2, r0, r1
ldr r1, =IAS_MEM
add r1, r1, r2
cmp r3, #1
beq rightInstruction
@--fetch instruction on the left
ldrb r5, [r1]
add r1, r1, #2
ldrb r0, [r1], #-1
lsr r0, r0, #4
ldrb r1, [r1]
lsl r1, r1, #4
orr r6, r0, r1
b opcodeAddressReady
@--fetch instruction on the right
rightInstruction:
add r1, r1, #2
ldrb r0, [r1], #1
and r2, r0, #15
lsl r2, r2, #4
ldrb r0, [r1]
lsr r0, r0, #4
orr r5, r0, r2
ldrb r0, [r1], #1
and r2, r0, #15
lsl r2, r2, #8
ldrb r0, [r1]
orr r6, r0, r2
@--r5=opcode r6=address, ready to go
opcodeAddressReady:
@--validate memory address
cmp r6, #1024
blt memoryOK
mov r0, #2
pop {r4-r6, pc}
memoryOK:
ldr r0, =IAS_MEM
mov r1, #5
mul r2, r1, r6
add r0, r0, r2 @--r0=physical address
@--Switch - calculate offset and branch
cmp r5, #0
blt invalidOpcode
cmp r5, #34
bge invalidOpcode
ldr r1, =instructionVector
ldr r1, [r1, r5, lsl #2]
bx r1
@--LOAD M(X) opcode==1
loadM:
bl loadMemory
ldr r1, =AC
str r0, [r1]
b instructionProcessed
@--LOAD -M(X) opcode==2
loadNegative:
bl loadMemory
mov r2, #-1
mul r3, r2, r0
ldr r1, =AC
str r3, [r1]
b instructionProcessed
@--LOAD |M(X)| opcode==3
loadModulus:
bl loadMemory
cmp r0, #0
movlt r2, #-1
mullt r3, r2, r0
movge r3, r0
ldr r1, =AC
str r3, [r1]
b instructionProcessed
@--ADD M(X) opcode==5
add:
bl loadMemory
ldr r1, =AC
ldr r2, [r1]
add r0, r0, r2
str r0, [r1]
b instructionProcessed
@--SUB M(X) opcode==6
sub:
bl loadMemory
ldr r1, =AC
ldr r2, [r1]
sub r2, r2, r0
str r2, [r1]
b instructionProcessed
@--ADD |M(X)| opcode==7
addModulus:
bl loadMemory
cmp r0, #0
movlt r2, #-1
mullt r3, r2, r0
movge r3, r0
ldr r1, =AC
ldr r2, [r1]
add r3, r3, r2
str r3, [r1]
b instructionProcessed
@--SUB |M(X)| opcode==7
subModulus:
bl loadMemory
cmp r0, #0
movlt r2, #-1
mullt r3, r2, r0
movge r3, r0
ldr r1, =AC
ldr r2, [r1]
sub r2, r2, r3
str r2, [r1]
b instructionProcessed
@--LOAD MQ, M(X) opcode==9
loadMQM:
bl loadMemory
ldr r1, =MQ
str r0, [r1]
b instructionProcessed
@--LOAD MQ opcode==10
loadMQ:
ldr r0, =MQ
ldr r0, [r0]
ldr r1, =AC
str r0, [r1]
b instructionProcessed
@--MUL M(X) opcode==11
mul:
bl loadMemory
ldr r1, =MQ
ldr r2, [r1]
mul r3, r2, r0
str r3, [r1]
mov r0, #0
ldr r1, =AC
str r0, [r1]
b instructionProcessed
@--DIV M(X) opcode==12
div:
bl loadMemory
ldr r1, =AC
ldr r1, [r1]
mov r2, #0
divisionLoop:
cmp r1, r0
blt endDivision
add r2, r2, #1
sub r1, r1, r0
b divisionLoop
endDivision:
ldr r0, =AC
str r1, [r0]
ldr r0, =MQ
str r2, [r0]
b instructionProcessed
@--JUMP M(0:19) opcode==13
jumpLeft:
lsl r6, r6, #1
ldr r1, =PC
str r6, [r1]
b noPCincrement
@--JUMP M(20:39( opcode==14
jumpRight:
lsl r6, r6, #1
add r6, r6, #1
ldr r1, =PC
str r6, [r1]
b noPCincrement
@--JUMP+ M(0:19) opcode==15
jumpPlusLeft:
ldr r1, =AC
ldr r1, [r1]
cmp r1, #0
bge jumpLeft
b instructionProcessed
@--JUMP+ (20:39) opcode==16
jumpPlusRight:
ldr r1, =AC
ldr r1, [r1]
cmp r1, #0
bge jumpRight
b instructionProcessed
@--LSH opcode==20
lsh:
ldr r0, =AC
ldr r1, [r0]
lsl r1, r1, #1
str r1, [r0]
b instructionProcessed
@--RSH opcode==21
rsh:
ldr r0, =AC
ldr r1, [r0]
lsr r1, r1, #1
str r1, [r0]
b instructionProcessed
@--STOR M(8:19) opcode==18
storLeft:
ldr r1, =AC
ldr r1, [r1]
and r3, r1, #15
and r2, r1, #4080
lsr r2, r2, #4
strb r2, [r0, #1]!
add r0, r0, #1
ldrb r1, [r0]
and r1, r1, #15
lsl r3, r3, #4
orr r1, r1, r3
strb r1, [r0]
b instructionProcessed
@--STOR M(28:39) opcode==19
storRight:
ldr r1, =AC
ldr r1, [r1]
and r2, r1, #255
lsr r1, r1, #8
and r1, r1, #15
ldrb r3, [r0, #3]!
orr r3, r3, r1
strb r3, [r0]
strb r2, [r0, #1]
b instructionProcessed
@STOR M(X) opcode==33
stor:
ldr r1, =AC
ldrb r2, [r1, #3]!
strb r2, [r0, #1]!
ldrb r2, [r1, #-1]!
strb r2, [r0, #1]!
ldrb r2, [r1, #-1]!
strb r2, [r0, #1]!
ldrb r2, [r1, #-1]!
strb r2, [r0, #1]!
b instructionProcessed
@-- invalid opcode, return 1
invalidOpcode:
mov r0, #1
pop {r4-r6, pc}
instructionProcessed:
@--increment PC
ldr r0, =PC
ldr r1, [r0]
add r1, r1, #1
str r1, [r0]
@--return without incrementing PC
noPCincrement:
mov r0, #0
pop {r4-r6, pc}
loadMemory:
add r0, r0, #1
mov r1, #0
ldrb r2, [r0], #1
lsl r2, r2, #24
orr r1, r1, r2
ldrb r2, [r0], #1
lsl r2, r2, #16
orr r1, r1, r2
ldrb r2, [r0], #1
lsl r2, r2, #8
orr r1, r1, r2
ldrb r2, [r0]
orr r1, r1, r2
mov r0, r1
bx lr
.data
instructionVector:
.word invalidOpcode
.word loadM
.word loadNegative
.word loadModulus
.word invalidOpcode
.word add
.word sub
.word addModulus
.word subModulus
.word loadMQM
.word loadMQ
.word mul
.word div
.word jumpLeft
.word jumpRight
.word jumpPlusLeft
.word jumpPlusRight
.word invalidOpcode
.word storLeft
.word storRight
.word lsh
.word rsh
.word invalidOpcode
.word invalidOpcode
.word invalidOpcode
.word invalidOpcode
.word invalidOpcode
.word invalidOpcode
.word invalidOpcode
.word invalidOpcode
.word invalidOpcode
.word invalidOpcode
.word invalidOpcode
.word stor
INTERFACE.S
.global get_cmd
.global my_strlen
.global my_itoah
.global my_itoa
.global my_strcmp
.global my_atoi
.global my_ahtoi
.text
@-- int get_cmd(int* po1, int* op2)
get_cmd:
push {r4-r8, lr}
@-- r0 = ¶m1 r1=¶m2
mov r5, r0
mov r6, r1
@--reset buffers
ldr r0, =buffer1
mov r1, #0
strb r1, [r0]
bl get_line
mov r4, r0
@--check for "exit" command
ldr r1, =string_exit
bl my_strcmp
cmp r0, #0
moveq r0, #0
beq return_cmd
@--check for "si" command
mov r0, r4
ldr r1, =string_si
bl my_strcmp
cmp r0, #0
moveq r0, #1
beq return_cmd
@--check for "c" command
mov r0, r4
ldr r1, =string_c
bl my_strcmp
cmp r0, #0
moveq r0, #3
beq return_cmd
@--check for "regs" command
mov r0, r4
ldr r1, =string_regs
bl my_strcmp
cmp r0, #0
moveq r0, #6
beq return_cmd
@--check for sn
ldr r3, =buffer1
mov r0, r4
ldrb r1, [r0], #1
strb r1, [r3], #1
ldrb r1, [r0], #1
strb r1, [r3], #1
mov r1, #0
strb r1, [r3]
mov r7, r0
ldr r0, =string_sn
ldr r1, =buffer1
bl my_strcmp
cmp r0, #0
bne not_sn
@--read parameter
add r7, r7, #1
ldr r0, =buffer1
readNumber:
ldrb r1, [r7], #1
cmp r1, #0
beq endNumber
strb r1, [r0], #1
b readNumber
endNumber:
mov r1, #0
strb r1, [r0]
ldr r0, =buffer1
bl my_atoi
str r0, [r5]
mov r0, #2
b return_cmd
not_sn:
@--check for p
ldr r3, =buffer1
mov r0, r4
ldrb r1, [r0], #1
strb r1, [r3], #1
mov r1, #0
strb r1, [r3]
mov r7, r0
ldr r0, =string_p
ldr r1, =buffer1
bl my_strcmp
cmp r0, #0
bne not_p
@--read parameter
add r7, r7, #2
ldrb r0, [r7]
cmp r0, #120
moveq r8, #1
movne r8, #0
addeq r7, r7, #1
subne r7, r7, #1
ldr r0, =buffer1
readNumber2:
ldrb r1, [r7], #1
cmp r1, #0
beq endNumber2
strb r1, [r0], #1
b readNumber2
endNumber2:
mov r1, #0
strb r1, [r0]
ldr r0, =buffer1
cmp r8, #1
bleq my_ahtoi
blne my_atoi
str r0, [r5]
mov r0, #5
b return_cmd
not_p:
@--check for stw
ldr r3, =buffer1
mov r0, r4
ldrb r1, [r0], #1
strb r1, [r3], #1
ldrb r1, [r0], #1
strb r1, [r3], #1
ldrb r1, [r0], #1
strb r1, [r3], #1
mov r1, #0
strb r1, [r3]
mov r7, r0
ldr r0, =string_stw
ldr r1, =buffer1
bl my_strcmp
cmp r0, #0
bne not_stw
@--get first parameter
add r7, r7, #2
ldrb r0, [r7]
cmp r0, #120
moveq r8, #1
movne r8, #0
addeq r7, r7, #1
subne r7, r7, #1
ldr r0, =buffer1
readNumber3:
ldrb r1, [r7], #1
cmp r1, #32
beq endNumber3
strb r1, [r0], #1
b readNumber3
endNumber3:
mov r1, #0
strb r1, [r0]
ldr r0, =buffer1
cmp r8, #1
bleq my_ahtoi
blne my_atoi
str r0, [r5]
@get second parameter
add r7, r7, #1
ldrb r0, [r7]
cmp r0, #120
moveq r8, #1
movne r8, #0
addeq r7, r7, #1
subne r7, r7, #1
ldr r0, =buffer1
readNumber4:
ldrb r1, [r7], #1
cmp r1, #0
beq endNumber4
strb r1, [r0], #1
b readNumber4
endNumber4:
mov r1, #0
strb r1, [r0]
ldr r0, =buffer1
cmp r8, #1
bleq my_ahtoi
blne my_atoi
str r0, [r6]
mov r0, #4
b return_cmd
not_stw:
@--default return=0
mov r0, #0
return_cmd:
pop {r4-r8, pc}
@-- char* get_line()
get_line:
push {r4, r7, lr}
ldr r4, =readBuffer
mov r0, #0
strb r0, [r4]
@--read one line from stdin
loop:
mov r0, #0
mov r1, r4
mov r2, #1
mov r7, #3
svc #0
ldrb r0, [r4]
cmp r0, #10
beq exitEndLine
cmp r0, #0
beq exitEndFile
cmp r0, #255
beq exitEndFile
add r4, r4, #1
b loop
exitEndLine:
exitEndFile:
mov r0, #0
strb r0, [r4]
ldr r0, =readBuffer
pop {r4, r7, pc}
@-- int divideBy10(int x) r0=result, r1=remainder
divideBy10:
mov r1, r0
movw r2, #:lower16:429496730
movt r2, #:upper16:429496730
smull r3, r0, r1, r2
mov r3, #10
mul r2, r0, r3
sub r1, r1, r2
bx lr
@--int divideBy16(int x) r0=result, r1=remainder
divideBy16:
mov r3, r0
lsr r0, r0, #4
mov r1, #16
mul r2, r0, r1
sub r1, r3, r2
bx lr
@--int my_strcmp(char* str1, char* str2)
my_strcmp:
ldrb r2, [r0], #1
ldrb r3, [r1], #1
cmp r2, r3
beq equalChars
movlt r0, #-1
movgt r0, #1
b endComp
equalChars:
cmp r2, #0
bne my_strcmp
mov r0, #0
endComp:
bx lr
@-- int my_strlen(char* str)
my_strlen:
push {lr}
mov r2, r0
head:
ldrb r1, [r0], #1
cmp r1, #0
bne head
sub r0, r0, r2
sub r0, r0, #1
pop {pc}
@-- int my_atoi(const char* str)
my_atoi:
push {r4,r5}
mov r5, #1
mov r3, #0
mov r2, #10
ldrb r1, [r0]
cmp r1, #45
bne start
add r0, r0, #1
mov r5, #-1
start:
ldrb r1, [r0], #1
cmp r1, #0
beq end
mov r4, r3
mul r3, r4, r2
sub r1, r1, #48
add r3, r3, r1
b start
end:
mul r0, r3, r5
pop {r4,r5}
bx lr
@--int my_ahtoi(const char* str)
my_ahtoi:
push {r4,r5}
mov r5, #1
mov r3, #0
mov r2, #16
ldrb r1, [r0]
cmp r1, #45
bne start2
add r0, r0, #1
mov r5, #-1
start2:
ldrb r1, [r0], #1
cmp r1, #0
beq end2
mov r4, r3
mul r3, r4, r2
cmp r1, #80
blt notLowerCase
sub r1, r1, #97
add r1, r1, #10
b continue
notLowerCase:
cmp r1, #60
blt notLetter
sub r1, r1, #65
add r1, r1, #10
b continue
notLetter:
sub r1, r1, #48
continue:
add r3, r3, r1
b start2
end2:
mul r0, r3, r5
pop {r4,r5}
bx lr
@--void my_itoa(int v, char* buf)
my_itoa:
push {r4,r5,lr}
cmp r0, #0
bge notNegative
mov r2, r0
mov r3, #-1
mul r0, r2, r3
mov r2, #45
strb r2, [r1], #1
notNegative:
mov r4, #0
mov r5, r1
cmp r0, #0
bne start3
add r0, r0, #48
strb r0, [r5], #1
mov r0, #0
start3:
cmp r0, #0
beq end3
bl divideBy10
add r4, r4, #1
push {r1}
b start3
end3:
output:
cmp r4, #0
beq endOutput
pop {r0}
add r0, r0, #48
strb r0, [r5], #1
sub r4, r4, #1
b output
endOutput:
mov r0, #0
strb r0, [r5]
pop {r4,r5,pc}
@--void my_itoah(int v, char* buf)
my_itoah:
push {r4,r5,lr}
cmp r0, #0
bge notNegative2
mov r2, r0
mov r3, #-1
mul r0, r2, r3
mov r2, #45
strb r2, [r1], #1
notNegative2:
mov r4, #0
mov r5, r1
cmp r0, #0
bne start4
add r0, r0, #48
strb r0, [r5], #1
mov r0, #0
start4:
cmp r0, #0
beq end4
bl divideBy16
add r4, r4, #1
push {r1}
b start4
end4:
output2:
cmp r4, #0
beq endOutput2
pop {r0}
cmp r0, #10
addlt r0, r0, #48
addge r0, r0, #55
strb r0, [r5], #1
sub r4, r4, #1
b output2
endOutput2:
mov r0, #0
strb r0, [r5]
pop {r4, r5, pc}
.data
readBuffer: .space 1200
buffer1: .space 400
string_exit: .asciz "exit"
string_si: .asciz "si"
string_sn: .asciz "sn"
string_c: .asciz "c"
string_stw: .asciz "stw"
string_p: .asciz "p"
string_regs: .asciz "regs"
MAIN.S
.global main
.text
main:
push {r4-r8, lr}
mov r6, #0
mainLoop:
ldr r0, =param1
ldr r1, =param2
bl get_cmd
mov r4, r0
@--return code==0 "exit"
cmp r4, #0
beq exitSimulator
@--return code==1 "si"
cmp r4, #1
bne not_si
bl step_instruction
cmp r0, #0
bne exitSimulator
not_si:
@--return code==2 "sn"
cmp r4, #2
bne not_sn
ldr r8, =param1
ldr r8, [r8]
executionLoop:
cmp r8,#0
ble endExecutionLoop
bl step_instruction
cmp r0, #0
bne exitSimulator
sub r8, r8, #1
b executionLoop
endExecutionLoop:
not_sn:
@--return code==3 "c"
cmp r4, #3
bne not_c
infiniteLoop:
bl step_instruction
cmp r0, #0
beq infiniteLoop
bne exitSimulator
not_c:
@--return code==4 "stw"
cmp r4, #4
bne not_stw
ldr r0, =param2 @valor
ldr r1, =param1 @endereco
ldr r1, [r1]
mov r2, #5
mul r3, r1, r2
ldr r1, =IAS_MEM
add r1, r1, r3
add r1, r1, #1
add r0, r0, #3
ldrb r2, [r0], #-1
cmp r2, #0
strneb r2, [r1], #1
addeq r1, r1, #1
ldrb r2, [r0], #-1
cmp r2, #0
strneb r2, [r1], #1
addeq r1, r1, #1
ldrb r2, [r0], #-1
cmp r2, #0
strneb r2, [r1], #1
addeq r1, r1, #1
ldrb r2, [r0]
strb r2, [r1]
not_stw:
@--return code==5 "p"
cmp r4, #5
bne not_p
@--print newline if not first run
cmp r6, #0
beq firstRun2
ldr r1, =buffer2
mov r2, #10
strb r2, [r1]
mov r0, #1
mov r2, #1
mov r7, #4
svc #0
firstRun2:
add r6, r6, #1
@--print value at memory address
ldr r0, =param1
ldr r0, [r0]
mov r2, #5
mul r3, r0, r2
ldr r1, =IAS_MEM
add r0, r1, r3
bl printMemory
not_p:
@-- return code==6 "regs"
cmp r4, #6
bne not_regs
@--print newline if not first run
cmp r6, #0
beq firstRun
ldr r1, =buffer2
mov r2, #10
strb r2, [r1]
mov r0, #1
mov r2, #1
mov r7, #4
svc #0
firstRun:
add r6, r6, #1
@--print AC
mov r0, #1
ldr r1, =string_AC
mov r2, #4
mov r7, #4
svc #0
ldr r0, =AC
ldr r0, [r0]
mov r1, #1
bl printHex
@--print MQ
mov r0, #1
mov r2, #4
ldr r1, =string_MQ
mov r7, #4
svc #0
ldr r0, =MQ
ldr r0, [r0]
mov r1, #1
bl printHex
@--print PC
mov r0, #1
ldr r1, =string_PC
mov r2, #4
mov r7, #4
svc #0
ldr r0, =PC
ldr r0, [r0]
tst r0, #1
moveq r5, #69
movne r5, #68
lsr r0, r0, #1
mov r1, #0
bl printHex
ldr r0, =buffer2
mov r1, #47
strb r1, [r0], #1
strb r5, [r0]
mov r0, #1
ldr r1, =buffer2
mov r2, #2
mov r7, #4
svc #0
not_regs:
b mainLoop
exitSimulator:
@--output
ldr r1, =buffer2
mov r0, #0
strb r0, [r1]
mov r0, #1
mov r2, #1
mov r7, #4
svc #0
mov r0, #0
pop {r4-r8, pc}
printMemory:
push {r4-r7,lr}
mov r4, r0
ldr r6, =buffer3
mov r2, #48
strb r2, [r6], #1
mov r2, #120
strb r2, [r6], #1
mov r5, #0
processBytes:
cmp r5, #5
bge endProcess
ldrb r0, [r4], #1
cmp r0, #16
movlt r2, #48
strltb r2, [r6], #1
ldr r1, =buffer2
bl my_itoah
ldr r1, =buffer2
transferBytes:
ldrb r0, [r1], #1
cmp r0, #0
beq endTransfer
strb r0, [r6], #1
b transferBytes
endTransfer:
add r5, r5, #1
b processBytes
endProcess:
mov r0, #1
ldr r1, =buffer3
mov r2, #12
mov r7, #4
svc #0
pop {r4-r7, pc}
printHex:
push {r7,r8, lr}
mov r8, r1
ldr r1, =buffer2
bl my_itoah
ldr r0, =buffer2
bl my_strlen
ldr r1, =buffer3
mov r2, #48
strb r2, [r1], #1
mov r2, #120
strb r2, [r1], #1
mov r3, #48
addZeroes:
cmp r0, #10
bge endZeroes
strb r3, [r1], #1
add r0, r0, #1
b addZeroes
endZeroes:
ldr r0, =buffer2
copyNumber:
ldrb r2, [r0], #1
cmp r2, #0
beq copyDone
strb r2, [r1], #1
b copyNumber
copyDone:
cmp r8, #1
moveq r2, #10
streqb r2, [r1]
moveq r2, #13
movne r2, #12
mov r0, #1
ldr r1, =buffer3
mov r7, #4
svc #0
pop {r7,r8, pc}
.data
param1: .space 4
param2: .space 4
buffer2: .space 400
buffer3: .space 400
string_AC: .ascii "AC: "
string_MQ: .ascii "MQ: "
string_PC: .ascii "PC: "
IAS_STATE.S
@ This is a macro definition to help setting entries in the
@ IAS memory map
.macro mm_entry byte1 byte2 byte3 byte4 byte5
.byte 0xbyte1
.byte 0xbyte2
.byte 0xbyte3
.byte 0xbyte4
.byte 0xbyte5
.endm
@ This .data section defines the architectural state
@ of the IAS machine
.data
.align 4
.globl IAS_MEM
.globl PC
.globl AC
.globl MQ
PC:
.word 0
AC:
.word 0
MQ:
.word 0
IAS_MEM:
mm_entry 09 00 50 B0 06
mm_entry 00 00 00 00 00
mm_entry 00 00 00 00 00
mm_entry 00 00 00 00 00
mm_entry 00 00 00 00 00
mm_entry 00 00 00 00 04
mm_entry 00 00 00 00 05
mm_entry 00 00 00 00 00
mm_entry 00 00 00 00 03
mm_entry 00 00 00 00 0A
mm_entry 00 00 00 00 00
mm_entry 00 00 00 00 00
.fill 1012, 5, 0