Baremetal Aarch64: Pt 3, Hello World Ultimate
Last time around I said I was gonna do input but then
totally forgot about it
so today's gonna
be a short one on getting input via UART into the
machine and then making it dance like a little
monkey for us.
UART revisited
The UART interface, like we covered, is dead simple. As with our output code, our input code is going to be fully blocking on the CPU until the UART flags change. Unlike the output, this means that we're going to be eating CPU time until the user provides with input, which could take a while. Cheap, nonblocking IO would take much more of a stack than we have going for us.
Our function will return a single character from input,
and take no arguments. We will need to check if the
RXFE flag is set (that is, that the receive stack
is empty) on the UART, and wait until it's cleared.
Thankfully, the UART is buffered, so we don't need to time our reads from the data register to the exact moment the user writes something.
// our input function // takes 0 arg on stack, returns 1 // trashes 2 registers _ugetc: str xzr, [sp, -8]! stp x0, x1, [sp, -16]! adrp x0, UART_BASE add x0, x0, :lo12:UART_BASE ldr w0, [x0] // now x0 has the UART_BASE location add x0, x0, 0x18 // UART_FLAG address _ugetc_loop1: ldr x1, [x0] and x1, x1, 0x10 cbnz x1, _ugetc_loop1 sub x0, x0, 0x18 ldr x0, [x0] str x0, [sp, 16] ldp x0, x1, [sp, 16]! ret
We reserved a place on the stack for the return argument, then stored the return there, while popping everything else.
Some fun code to play around with in the main code path:
PLEASE_WRITE: .asciz "Please input a key and I'll do my best to repeat it and tell you if it's odd or even: " 3: ldr x2, =EXAMPLE_STRING str x2, [sp, -8]! bl _uputs // and Hello World, finally! ldr x2, =PLEASE_WRITE str x2, [sp, -8]! bl _uputs _parity_loop: bl _ugetc ldr x2, [sp] and x2, x2, 0xff sub x2, x2, 0x40 cbz x2, 4f add x2, x2, 0x40 bl _uputc sub x2, x2, 0x30 and x2, x2, 0x1 cbz x2, _is_even _is_odd: mov x2, 0x4f b _parity_loop_end _is_even: mov x2, 0x45 _parity_loop_end: str x2, [sp, -8]! bl _uputc b _parity_loop 4: add x2, x2, 0x40 str x2, [sp, -8]! bl _uputc b .
This program tests your input whether it's even or odd,
prints E or O depending on the case, and halts when it
encounters 0x40—that is, @.
To get the input working correctly, I had to pipe it to a serial device, and use PuTTY to link to the other side. If your input isn't working, it's probably due to that. My own experience here is that the code is somehow extremely sluggish; there's up to a full second of delay between my input and the device's response, and I'm not sure if that's a quirk of using virtual COM ports and PuTTY to interface with QEMU or a failing of my code. I might want to write some sort of device driver for input later on to alleviate that and avoid serial TTY, so we'll see.