Reputation: 27
I am new to assembly and trying to write a version of the "echo" built-in but only operating on 1 byte at a time.
I have the following which works the way I want, except it overflows more than 1 byte on both read and write even though I explicitly say 1 byte in x2 on both syscalls. What am I doing wrong?
Example run:
sh-4.2$ ./echo1b
f
f
o
o
b
b
bar
bar
bazbazbaz
bazbazbaz
q
sh-4.2$
Here is the code:
.data
temp: .byte 1
.text
.globl _start
_start:
/* read one byte from stdin, store to temp */
mov x0, #0x0
adr x1, temp
mov x2, #0x1
mov x8, #0x3F
svc #0x0
/* write newline to stdout */
mov x0, #0x1
mov x1, #0xA
mov x2, #0x1
mov x8, #0x40
svc #0x0
/* if byte in temp is "q", exit */
mov x5, #0x71
ldr x1, temp
cmp x1, x5
beq exit
/* otherwise, write it to stdout and repeat */
mov x0, #0x1
adr x1, temp
mov x2, #0x1
mov x8, #0x40
svc #0x0
b _start
exit:
/* exit cleanly */
eor x0, x0, x0
eor x1, x1, x1
eor x2, x2, x2
mov x8, #0x5D
svc #0x0
Upvotes: 1
Views: 1102
Reputation: 2493
There are several issues in your code:
x1
when calling sys_write
as you did with temp
temp
with the newline character, you had to use ldrb w1, [x0]
instead of ldr x1, temp
where x0
points to temp
. The latter would read 4 bytes while it's not guaranteed that the upper three bytes are zero.I also improved some parts of your code:
cmp
can be used with a 12 bit immediate so there's no need to put 0x71
in a register.sys_write
call before _start
avoids the unconditional jump.sys_exit
uses only x0
as parameter so there's no need to set x1
and x2
to zero.Here's the final code, tested on Raspbian 4.19 (debian based):
.data
temp: .byte 1
newline: .byte 0x0A
.text
.globl _start
loop:
// 4: Otherwise, write it to stdout and repeat
mov x0, #0x1 // int fd
adr x1, temp // void* buf
mov x2, #0x1 // size_t count
mov x8, #0x40 // sys_write
svc #0x0
_start:
// 1: Read one byte from stdin and store to temp (including newline)
mov x0, #0x0 // int fd
adr x1, temp // void* buf
mov x2, #0x1 // size_t count
mov x8, #0x3F // sys_read
svc #0x0
// 2: If byte in temp is 'q', exit
adr x0, temp
ldrb w1, [x0] // instead of temp
cmp x1, #0x71
bne loop
// 5: Exit cleanly
eor x0, x0, x0 // int status
mov x8, #0x5D // sys_exit
svc #0x0
Edit after comment: To flush stdin at exit, you could add this lines before step 5:
// 5: Flush stdin (read until newline)
flush:
mov x0, #0x0 // int fd
adr x1, temp // void* buf
mov x2, #0x1 // size_t count
mov x8, #0x3F // sys_read
svc #0x0
adr x0, temp
ldrb w1, [x0]
cmp x1, #0x0A
bne flush // loop until x0 == 0x0A
Upvotes: 5