RosettaCodeData/Task/Subleq/ARM-Assembly/subleq.arm

150 lines
4.5 KiB
Plaintext

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@ ARM SUBLEQ for Linux @@@
@@@ Word size is 32 bits. The program is @@@
@@@ given 8 MB (2 Mwords) to run in. @@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.text
.global _start
@@@ Linux syscalls
.equ exit, 1
.equ read, 3
.equ write, 4
.equ open, 5
_start: pop {r6} @ Retrieve amount of arguments
cmp r6,#2 @ There should be exactly 2 (incl program)
ldrne r1,=usage @ Otherwise, print usage and stop
bne die
pop {r0,r1} @ Retrieve filename
mov r0,r1
mov r1,#0 @ Try to open the file in read mode
mov r2,#0
mov r7,#open
swi #0
movs r5,r0 @ File handle in R5
ldrmi r1,=efile @ If the file can't be opened, error
bmi die
ldr r8,=prog @ R8 = pointer into program
mov r6,#0 @ At the beginning, there is no data
rdnum: bl fchar @ Skip past whitespace
cmp r0,#32
bls rdnum
mov r9,#0 @ R9 = current number being read
subs r10,r0,#'- @ R10 is zero if number is negative
bleq fchar @ And get next character
1: sub r0,r0,#'0 @ Subtract ASCII 0
cmp r0,#9
ldrhi r1,=echar
bhi die @ Invalid digit = error
mov r1,#10
mla r0,r9,r1,r0 @ Multiply accumulator by 10 and add digit
mov r9,r0
bl fchar @ Get next character
cmp r0,#32 @ If it isn't whitespace...
bhi 1b @ ...then it's the next digit
tst r10,r10 @ If the number should be negative,
rsbeq r9,r9,#0 @ ...then negate it
str r9,[r8],#4 @ Store the number
b rdnum @ And get the next number.
setup: ldr r0,=prog @ Zero out the rest of program memory
sub r0,r8,r0 @ Zero to 8-word (32-byte) boundary
orr r0,r0,#31 @ Find address of last byte within
add r0,r0,r8 @ current 31-byte block
mov r1,#0 @ R1 = zero to write
1: str r1,[r8],#4 @ Write zeroes,
cmp r0,r8 @ until boundary reached.
blo 1b
mov r0,#0 @ 8 words of zeroes in r0-r7
umull r2,r3,r0,r1 @ A trick to produce 2 zero words in one
umull r4,r5,r0,r1 @ go: 0*0 = 0, long multiplication
umull r6,r7,r0,r1 @ results in 2 words.
ldr r9,=mem_end
2: stmia r8!,{r0-r7} @ Write 8 zero words at a time
cmp r8,r9 @ Are we at mem_end yet?
blo 2b @ If not, keep going
ldr r8,=prog @ R8 = IP, starts at beginning
ldr r6,=prog @ R6 = base address for memory
mov r12,#0xFFFF @ 0x1FFFFF = address mask
movt r12,#0x1F
instr: ldmia r8!,{r9-r11} @ R9, R10, R11 = A, B, C
cmp r9,#-1 @ If A=-1, get character
beq rchar
cmp r10,#-1 @ Otherwise, if B=-1, write character
beq wchar
and r9,r9,r12 @ Keep addresses within 2 Mwords
and r10,r10,r12
ldr r0,[r6,r9,lsl #2] @ Grab [A] and [B]
ldr r1,[r6,r10,lsl #2]
subs r1,r1,r0 @ Subtract
str r1,[r6,r10,lsl #2] @ Store back in [B]
cmpmi r0,r0 @ Set zero flag if negative
bne instr @ If result is positive, next instruction
lsls r8,r11,#2 @ Otherwise, C becomes the new IP
add r8,r8,r6
bpl instr @ If result is positive, keep going
mov r0,#0 @ Otherwise, we exit
mov r7,#exit
swi #0
@@@ Read character into [B]
rchar: mov r0,#0 @ STDIN
and r10,r10,r12 @ Address of B
add r10,r6,r10,lsl #2 @ Kept in R10 out of harm's way
mov r1,r10
mov r2,#1 @ Read one character
mov r7,#read
swi #0
cmp r0,#1 @ We should have received 1 byte
movne r1,#-1 @ If not, write -1
ldreqb r1,[r10] @ Otherwise, blank out the top 3 bytes
str r1,[r10]
b instr
@@@ Write character in [A]
wchar: mov r0,#1 @ STDIN
and r1,r9,r12 @ Address of [A]
add r1,r6,r1,lsl #2
mov r2,#1 @ Write one character
mov r7,#write
swi #0
b instr
@@@ Read character from file into R0. Tries to read more
@@@ if the buffer is empty (as given by R6). Buffer in R11.
fchar: tst r6,r6 @ Any bytes in the buffer?
ldrneb r0,[r11],#1 @ If so, return next character from buffer
subne r6,r6,#1
bxne lr
mov r12,lr @ Save link register
mov r0,r5 @ If not, read from file into buffer
ldr r1,=fbuf
mov r2,#0x400000
mov r7,#read
swi #0
movs r6,r0 @ Amount of bytes in r6
beq setup @ If no more bytes, start the program
ldr r11,=fbuf @ Otherwise, R11 = start of buffer
mov lr,r12
b fchar
@@@ Write a zero-terminated string, in [r1], to stdout.
print: push {lr}
mov r2,r1
1: ldrb r0,[r2],#1 @ Get character and advance pointer
tst r0,r0 @ Zero yet?
bne 1b @ If not, keep scanning
sub r2,r2,r1 @ If so, calculate length
mov r0,#1 @ STDOUT
mov r7,#write @ Write to STDOUT
swi #0
pop {pc}
@@@ Print error message in [r1], then end.
die: bl print
mov r0,#255
mov r7,#exit
swi #0
usage: .asciz "Usage: subleq <filename>\n"
efile: .asciz "Cannot open file\n"
echar: .asciz "Invalid number in file\n"
@@@ Memory
.bss
.align 4
prog: .space 0x400000 @ Lower half of program memory
fbuf: .space 0x400000 @ File buffer and top half of program memory
mem_end = .