Jumping into ARM assembly


Thanks ! I'll test that.
Is that code supposed to wait for an input before continuing or it's in a loop ?
 
I got
Error: internal_relocation (type: OFFSET_IMM) not fixed up
at line
ldr r1,evdata_ofs
 
Yeah, I don't really understand what it's doing with that PC offset business. I guess the data it's trying to access is out of range of the instruction trying to reference it, so it's storing that address in a word of memory closer to the instruction then loading that, and indirecly loading from that register. But the exact syntax is beyond me, I'm afraid. Maybe you can use code like you're using in your examples when you load the address of the string to print. I forget; do you use ADR to load the address? You might not even need an offset if you use that, I'm guessing.
 
Can't get it :/ .
Surprisingly, there's almost no tutorials on arm asm events management.
 
Okay, I've gone and looked at your last working example, and there you use the notation ldr rn, =label, so perhaps change 'ldr r1, evdata_ofs' to 'ldr r1, =evdata_ofs'.

I've no idea if that's the only thing you'll need to change. I should really set up a dev environment so I can test these things. Are you using GNU AS to assemble your code? I think I have a cross-compiler version of that around somewhere.
 
Looks like a step towards the right direction, thx.
The program now compiles and runs.
It needs at least one event on the inputs, then "enter" to quit !
 
Last edited:
I'm getting different codes for each pressed key (not the same as in tetet macro).
But the two first svc are waiting for "enter" to continue code execution. How to avoid that ?
 
I need to push a key for a long time, then enter to get a keycode.
Warning: it's an horrible piece of code, I use _disp_hex to display r0's content.

Code:
  .text
  .align 2
  ev_size=16
  evdata_len=ev_size*4

  .macro syswrite  @ print the content of r1, must be ascii, buffer length r2 must be set
  push {r0, r7}
  mov r7, #4  @ system call number, 4 is 'write'
  mov r0, #1  @ file descriptor 1 - stdout
  svc 0  @ call the Linux kernel and print the ascii text
  pop {r0, r7}
  .endm

  .macro syswritechar  @ print what r1 points at, must be ascii, buffer length r2 must be set
  push {r0, r2, r7}
  mov r2, #1
  mov r7, #4  @ system call number, 4 is 'write'
  mov r0, #1  @ file descriptor 1 - stdout
  svc 0  @ call the Linux kernel and print the ascii text
  pop {r0, r2, r7}
  .endm

  .macro newline
  ldr r1, =newline  @ point on the buffer to write, \n, a newline
  mov r2, #2  @ newline buffer length
  syswrite
  .endm

  .global _start
_start:

getkey:
  mov r2,#evdata_len
  ldr r1, =evdata_ofs
  sub r0, r0, r0
  @ Clear evdata to evdata_len of zeroes
  @ Set r0 to 'keyp' i.e. 0x7079656b (assuming I've got my endianness correct)
  mov r7, #3
  svc 0 @ Do sys_read
  cmp r0, #ev_size
  bge getkey_ok
  @ Set r0 to 'gpio' i.e. 0x6f697067
  svc 0 @ Do sys_read again, this time on gpio signals??
  cmp r0,#ev_size
  @ if lt, mov r0, #0  ; return - no key pressed   

getkey_ok:
  @ Got a valid key event
  ldrh r2, [r1, #8]
  ldrh r0, [r1, #10]
  ldr r3, [r1, #12]
  @ if r2==1 (#ev_key) and r3==1 (#ev_press), return key code in r0, else mov r0, #0 and return

  newline
_disp_hex:
  ldr r1, =asciicounter  @ point on =asciicounter
  mov r2, r0
  mov r3, #15
  mov r4, #8

_loop:
  mov r2, r2, ror #28
  and r5, r2, r3
  cmp r5, #9
  add r5, #48
  ble _num
  add r5, #7

_num:
  str r5, [r1]
  syswritechar
  subs r4, #1
  bne _loop

  newline

  mov r7, #1  @ exit
  svc 0  @ call the Linux kernel and exit

  .data
asciicounter:  .word 0  @ must be initialized
newline:  .asciz "\n"  @ \n is a newline

evdata:  .fill evdata_len,1,0
evdata_ofs:  .word evdata
 
Um, most of those comments in my example were for you to translate into real machine code. You're not acutally clearing the evdata area (although that's not important the first time round), and you're not setting r0 correctly before calling sys_read, so its behaviour is undefined. I just left them as comments to keep the example short and easier to understand at a high level, but machine code is all about the low level, I'm afraid.

Also, I'll just mention that your newline data seems to have reverted to being a .asciz, rather than a .ascii, and you've gone back to printing out that 0x00 terminator along with your newline character.
 
Rofl, ok I'll translate the comments !
Yes I reused the wrong version of previous code...
[doublepost=1451765776,1451759037][/doublepost]Ok, I now have a loop.
How do I "clear the evdata area" ?
 
I mean set it to 0. Create a loop that runs ev_size times, and each time set the evdataoff+number to 0. There's code to do this starting line 650 of tetet.s, however I'm having a little difficulty understanding words versus bytes in that code.

lsr#2 has the effect of multiplying by 4, right? And each time round the code takes 4 off this value. That makes sense in the context of the indirect store up next. But evdata_len is ev_size*4, and ev_size looks to my eye to be a value in bytes, so evdata_len is a value in quarter bytes? That's a very weird unit to use, and makes me think I don't understand what's going on.

I'll try again tomorrow. I've clearly read it wrongly, but it's too late to debug my brain right now.
 
Sorry Linux-SWAT, you're going the wrong way. It's not event handling what you're doing in your code. I hope the next code will help you:

Code:
.title "evlist"
.arm

.include "macro.mac"

evdatalen=0x20

.data

evdev: .ascii "/dev/input/event"
evnum: .byte 0xff,0
evnamelen=.-evdev-1
.align

.text

eviocgname: .word ioc_read | ((evdatalen-1) << 16) | 0x4506
evdevofs:   .word evdev
evdataofs:  .word evdata

.globl _start
_start:
ldr r4,evdevofs
mov r5,#'0'

ev_scan_loop:
mov r1,#o_rd
mov r0,r4
strb r5,[r4,#(evnum-evdev)]
  invoke sys_open
# r0=fd
subs r3,r0,#0
bpl 1f

exit 0xff

1:
mov r0,#console
mov r1,r4
mov r2,#(evnamelen)
  invoke sys_write

mov r0,#' '
bl printchar

ldr r1,eviocgname
mov r0,r3
ldr r2,evdataofs
  invoke sys_ioctl

mov r1,r2
mov r2,r0
mov r0,#console
  invoke sys_write

mov r0,r3
  invoke sys_close

mov r0,#'\n'
bl printchar

inc r5
b ev_scan_loop

printchar:
push {r0-r2,r7,lr}

mov r0,#console
mov r1,sp
mov r2,#1
  invoke sys_write

pop {r0-r2,r7,pc}

.bss

evdata: .fill evdatalen,1,0
.align

.end

It does the following:
1/opens all events found in "/dev/input/event?" from '0' to "file not found" error.
2/gets its name via EVIOCGNAME
3/prints out everything useful

It's simple and may help you to pick the right event to read. The main thing is that you should not read CONSOLE or STDIN. You have to read an event from the device list.

An event data structure is 16 bytes long: (linux/input.h, also EVIOCGNAME is defined here)
STRUC
[0] timestamp1 .word ? (sys/time.h, struct timeval)
[4] timestamp2 .word ?
[8] type .short ?
[a] code .short ?
[c] value .word ?
ENDS

Try out the following:
run "evlist" and "cat" the event associated to the matrix keypad.
Code:
cat /dev/input/event0 | hexdump

event "type"s: (macro.mac)
ev_key=1 keypad
ev_abs=3 nubs and touchscreen

event "code" holds the scan code of the key.

event "value"s:
ev_release=0
ev_press=1
ev_repeat=2

If you wish to read events you have to alter the "termios" (termios.h) structure (disable echo) and open the event with O_NONBLOCK flag added.


I'm going to put together something useful with event-based key reading and post it soon.

I hope it was helpful.
[doublepost=1451810096,1451805749][/doublepost]I'm back with another example, 'evkeys.s'.
I also uploaded a newer 'macro.mac' (macro.mac.txt) that contains structures for event handling and SIG actions as well.

It reads the 'keypad' event until 'q' key is pressed.

Please download and include the newer macro.mac.

Code:
.title "evlist"
.arm

.include "macro.mac"

ifpandora=1

.if ifpandora
.else
key_q=0x10   @ for alarm/zaurus
.endif

evdatalen=ev_size

.data

evdev: .ascii "/dev/input/event"
evnum: .byte 0xff,0
evnamelen=.-evdev-1
.align

.text

eviocgname: .word ioc_read | ((evdatalen-1) << 16) | 0x4506 
evdevofs:   .word evdev
evdataofs:  .word evdata

.globl _start
_start:
 ldr r4,evdevofs
 mov r5,#'0'

ev_scan_loop:
 mov r1,#o_rd | o_nonblock
 mov r0,r4
 strb r5,[r4,#(evnum-evdev)]
  invoke sys_open
@ r0=fd
 subs r3,r0,#0
bpl 1f

exit 0xff

txt_keypad:
.if ifpandora
 .ascii "keyp"   @ "keypad"
.else
 .ascii "matr"   @ "matrix-keypad"
.endif

1:
 ldr r1,eviocgname
 mov r0,r3
 ldr r2,evdataofs
  invoke sys_ioctl

 ldr r0,[r2]
 ldr r1,txt_keypad
 cmp r0,r1
beq 2f

 mov r0,r3
  invoke sys_close

 inc r5
b ev_scan_loop

2:
 print "keypad event found\nlet's read it..."

1:
 mov r0,r3
 ldr r1,evdataofs
 mov r2,#evdatalen
  invoke sys_read

 cmp r0,#ev_size
blt 1b

 ldrh r2,[r1,#ev_type]
 ldr r4,[r1,#ev_value]
 ldrh r0,[r1,#ev_code]

 cmp r2,#ev_key
  cmpeq r4,#ev_press
   cmpeq r0,#key_q
bne 1b

 print "\n'q' was pressed. bye\n"

 mov r0,r3
  invoke sys_close

exit 0

.bss

evdata: .fill evdatalen,1,0
.align

.end
 

Attachments

  • macro.mac.txt
    2.8 KB · Views: 261
we can split the code into three parts

1/opening the event devices
Code:
@ the usual init stuff goes here
.title "evlist"
.arm
.include "macro.mac"

ifpandora=1        @ i was writing this code on a zaurus. that's why it's here.

.if ifpandora
.else
key_q=0x10         @ 'q' key scancode for alarm/zaurus
.endif

evdatalen=ev_size  @ event size is 16 bytes

.data
 evdev: .ascii "/dev/input/event"
 evnum: .byte 0xff,0    @ we will update the 0xff byte with the event's number
 evnamelen=.-evdev-1
.align

.bss
evdata: .fill evdatalen,1,0    @ allocate 16 bytes in uninitialized data section .bss

.text
@ defined in linux/inluput.h
@ #define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len)   /* get device name */
 eviocgname: .word ioc_read | ((evdatalen-1) << 16) | 0x4506
 evdevofs: .word evdev     @ store evdev's offset for easier access
 evdataofs: .word evdata   @ store evdata's offset for easier access

.globl _start
_start:
 ldr r4,evdevofs  @ similar to "ldr r4,=evdev". if we won't store 
                  @ the address then GAS will do it using a literal pool
 mov r5,#'0'      @ the first device to open will be "/dev/input/event0"

ev_scan_loop:
 mov r1,#o_rd | o_nonblock
                @ open the device for reading without 
                @ waiting to fill the buffer. R0 will return 0 if no input.
 mov r0,r4      @ device name
 strb r5,[r4,#(evnum-evdev)]
        @ the string will look "/dev/input/event0" 
        @ after writing R5 to the one before last character. "evnum" is ASCIIZ
  invoke sys_open @ try to open it
 subs r3,r0,#0    @ store the device handle to R3 for later use.
                 @ note: a regular syscall trashes r0 and some times r1 and/or r2.
bpl 1f           @ branch if the file was opened successfully

exit 0xff        @ exit if we run out of devices

txt_keypad:
.if ifpandora
.ascii "keyp"    @ "keypad"
.else
.ascii "matr"    @ "matrix-keypad"
.endif

1:
now we finished opening the device.
lest move to part 2
Code:
@ get the event's name using a syscall EVIOCGNAME
 ldr r1,eviocgname
 mov r0,r3         @ use the opened event's handle
 ldr r2,evdataofs  @ store the result in evdata. we use the 
                   @ same memory block to store the name and to 
                   @ read the event data later.
  invoke sys_ioctl

 ldr r0,[r2]       @ compare if the name was 'keypad'. we compare the 
                   @ first four characters.
 ldr r1,txt_keypad
 cmp r0,r1
beq 2f

 mov r0,r3          @ if it was not 'keyp' then close the device and
  invoke sys_close

 inc r5             @ move to the next event, '0'..'1'..'2'.. and so on
b ev_scan_loop

2:
 print "keypad event found\nlet's read it..."
finished with part 2. now read the event in pt 3.

Code:
1:
 mov r0,r3            @ event handle is still stored in R3
 ldr r1,evdataofs
 mov r2,#evdatalen    @ read the event handle and store the data into evdata
  invoke sys_read     @ we read 16 bytes in one run

 cmp r0,#ev_size      @ if we got less then 16 bytes then do the read loop again
blt 1b

 ldrh r2,[r1,#ev_type]  @ read the event type,
 ldr r4,[r1,#ev_value]  @  value
 ldrh r0,[r1,#ev_code]  @  and code from the 16 byte long buffer. 
                        @ (we don't need the timestamp data)

cmp r2,#ev_key        @  if it was a KEY event
 cmpeq r4,#ev_press    @ with a PRESSED key
  cmpeq r0,#key_q       @ and the key was 'Q'
bne 1b                   @ then print out the following message. 
                          @ otherwise branch back to the read loop

 print "\n'q' was pressed. bye\n"

 mov r0,r3
  invoke sys_close    @ close the handle

exit 0           @ and exit
.end
 
I'd be tempted to put a game loop in part 3, right between
1:
and
mov r0,r3 @ event handle is still stored in R3

with things like
bl update_engine
bl update_screen
[doublepost=1451943011,1451930284][/doublepost]Another question, I would like to use a register as a 0/1 flags container.
What would be the best method to write and read ?
 
Use 0 and 1 - the LSB. Set it using 'ORR Rn, Rn, #1', read it using 'ANDS Rx, Rn, #1' (where the Rx value will be discarded). Then, if you later decide you need another boolean, you can use the next bit, and replace the immediate constants in those examples with #2, then #4, #8 and so on.
 
Ok I'll try that. I first though of ROR thingy but that sounds more easy.
So if the register is 1111 and I wat to check the first 1, I just have to ands 1000 ? How to read the cpsr, a bwhat ?
 
Last edited:
Back
Top