syscall implementation

1,274 views
Skip to first unread message

Hossam Fouad

unread,
Mar 19, 2017, 1:35:21 PM3/19/17
to RISC-V ISA Dev
what is the syscall assembly implementation to print character on screen ?












Michael Clark

unread,
Mar 19, 2017, 7:58:55 PM3/19/17
to Hossam Fouad, RISC-V ISA Dev

On 20 Mar 2017, at 2:35 AM, Hossam Fouad <hossam....@gmail.com> wrote:

what is the syscall assembly implementation to print character on screen ?

RISC-V doesn’t have a concept of a screen but if you running an operating system such as linux, you can use the write (2) system call to output characters to the console (which might be a serial port or a pseudo tty). RISC-V operating systems typically use the ecall instruction to make system calls. Note that the calls in this example are specific to riscv-linux.

.section .text
.globl _start
_start:

        li a0, 0                    # stdout
1:      auipc a1, %pcrel_hi(msg)    # load msg(hi)
        addi a1, a1, %pcrel_lo(1b)  # load msg(lo)
        li a2, 12                   # length
        li a3, 0
        li a7, 64                   # _NR_sys_write
        ecall                       # system call

        li a0, 0
        li a1, 0
        li a2, 0
        li a3, 0
        li a7, 93                   # _NR_sys_exit
        ecall                       # system call

loop:
        j loop

.section .rodata
msg:
        .string "Hello World\n"

Alternatively if you are on bare metal hardware you may need to write characters to a UART to see them on the screen. Note the MMIO base address of the UART may be different on your system.

.section .text
.globl _start
_start:

.equ UART_BASE, 0x40003000
.equ REG_RBR, 0
.equ REG_TBR, 0
.equ REG_IIR, 2
.equ IIR_TX_RDY, 2
.equ IIR_RX_RDY, 4

1:      auipc a0, %pcrel_hi(msg)    # load msg(hi)
        addi a0, a0, %pcrel_lo(1b)  # load msg(lo)
2:      jal ra, puts
3:      j 3b                        # busy loop

puts:
        li a2, UART_BASE
1:      lbu a1, (a0)
        beqz a1, 3f
2:      lbu a3, REG_IIR(a2)
        andi a3, a3, IIR_TX_RDY
        beqz a3, 2b
        sb a1, REG_TBR(a2)
        addi a0, a0, 1
        j 1b
3:      ret

.section .rodata
msg:
        .string "Hello World\n"


BTW The question is probably more appropriate for sw-dev.

Krishanu Singh

unread,
Dec 20, 2018, 9:37:31 AM12/20/18
to RISC-V ISA Dev, hossam....@gmail.com
Hello Michael !

Can you please explain the second part(The bare metal one) code step by step?

Thanks!

Luke Kenneth Casson Leighton

unread,
Dec 21, 2018, 9:54:51 AM12/21/18
to krishanu...@gmail.com, RISC-V ISA Dev, hossam....@gmail.com
On Thu, Dec 20, 2018 at 9:37 AM Krishanu Singh
<krishanu...@gmail.com> wrote:

> Hello Michael !
>
> Can you please explain the second part(The bare metal one) code step by step?

it means, you have implemented a memory-mapped peripheral (a UART), and to
output a character you write to the "output this character onto the
UART TX line please"
memory address of that peripheral.

the implementation of that memory-mapped peripheral then does exactly that.
you can find a verilog implementation of a memory-mapped UART peripheral with
a google search, very easily. here are some in scala;
https://github.com/sifive/sifive-blocks/tree/master/src/main/scala/devices/uart

here is another:
https://opencores.org/projects/uart16550

that one is wishbone-compliant, so the wishbone memory controller takes care
of the memory mapping.

this is absolutely standard practice, almost 100% of peripherals are
done this way,
today. so if you look at the source code of any linux kernel driver you will
find it does exactly what that assembly listing does.

l.
Reply all
Reply to author
Forward
0 new messages