ecall

The one instruction that asks the simulator for something, the service number in a7 that says what, and the registers each service reads and answers in. Printing, reading, the clock and ending a program.

Every program so far has left its answer in a register or in memory. To print a line, read what you typed or ask what the time is, a RISC-V program asks the environment, and the instruction it asks with is ecall.

The three steps

ecall takes no operands at all. Everything about the request is in the registers:

  1. put the service number in a7, which says what you want,
  2. put the arguments in a0, and in a1 and a2 for the services that take more,
  3. run ecall.

Anything the service answers with comes back in a0. There is one ecall instruction and about thirty services behind it, and which number means what is the environment's choice, not the CPU's: RARS decided that 4 prints a string, and a RISC-V chip in a router knows nothing about it. Linux on RISC-V asks the same way, with its own number in a7, and RARS took some of its numbers: 64 is a write and 93 is an exit on both.

a7 is the eighth argument register the rest of the time, and a0 is both the first argument and the first return value, which is the same double duty a subroutine call gives them.

Printing

The console panel below the editor shows Hello, world! and then 42. Service 4 walks the string from a0 until it reads a zero byte, which is why .asciz and not .ascii. Service 1 reads a0 as a signed 32 bit number, so li a0, -1 prints -1.

\n inside a string is a newline, and '\n' as a character literal is the same byte, which is 10. Nothing prints a newline for you: service 4 prints exactly the bytes you gave it.

li a7, 10 and ecall is service 10, exit. Without it the program carries on into whatever follows, which is why every program on this page ends with those two lines.

Printing a number in another base

The console reads 0x000000ff 00000000000000000000000000000101 4294967295. All three of those pad to the full width of a word, so 255 comes out as eight hex digits and 5 as thirty two binary ones, and service 36 prints the same bits service 1 would have printed as -1.

Try changing li a0, 255 to li a0, -1 and running again: service 34 prints 0xffffffff, which is what the registers panel shows for that register.

Reading

Press Run and the program stops at the ecall with the prompt in the console and waits: type a number in the box under it and press Enter, and the run carries on inside that one instruction.

The reading services are:

  • 5 reads a line and parses it as a decimal number into a0. A line that is not a number ends the run.
  • 12 reads one character into a0.
  • 8 reads a whole line into the buffer at a0, up to a1 characters, and keeps the newline. The buffer is yours, and .space is how you reserve it.

The answer lands in a0, and the add t0, a0, a0 above takes it out of there before the next li a0 of a printing service overwrites it. The service number is safe in a7 whatever you do with a0, which is where MIPS is more awkward: there both live in $v0 and the answer is destroyed by the line that asks the next question.

The clock

The console shows 500 ms of program time. Service 30 counts from the start of the run here, where RARS counts from 1 January 1970, and a program that measures how long something took subtracts two readings, which works the same either way. It answers in two registers, the low word in a0 and the high word in a1.

Service 32 waits for a0 milliseconds of program time. The wait costs no instructions, so a program that idles on the keyboard never reaches the Playground's two million, and the editor stays responsive so Stop still answers. In a testcase both of them run on a virtual clock that starts at zero and only moves through the program's own waits, which is why the number above is exactly 500 and not 503.

The whole table

servicewhat it doesreadsanswers
1print a signed integera0
2print a floatf12
3print a doublef12
4print a null terminated stringa0 = its address
5read an integera0
6read a floatf0
7read a doublef0
8read a line into a buffera0 = buffer, a1 = how many charactersthe string
9ask for heap memorya0 = how many bytesa0 = the address
10end the program
11print one charactera0
12read one charactera0
30milliseconds since the run starteda0, a1
32wait that many millisecondsa0
34print an integer in hexadecimala0
35print an integer in binarya0
36print an integer as unsigneda0
41a random integera0 = which generatora0
42a random integer under a limita0 = which generator, a1 = the limita0
43a random floata0f0
44a random doublea0f0
93end the program with a codea0
50-60RARS's dialog boxessee the documentation page

The same table with a paragraph on each service is on the RISC-V ecall documentation page.

Service 9 hands out memory from the heap, which starts at 0x10040000, and it never gives any back: there is no free, and a program that asks in a loop runs out. Services 50 to 60 are RARS's pop up dialogs, and here they read from and write to the console like everything else, since this editor has one place for input and one for output.

The numbers are close to MIPS's and they are not the same list. Ending with a code is 93 here and 17 on MIPS, and the four file services are 1024, 63, 64 and 57 where MIPS has 13 to 16. A program moved between the two courses needs its ecall numbers checked one at a time, and the register the number goes in changed from $v0 to a7.

ecall with a number nothing answers to ends the run with invalid or unimplemented syscall service: 99, naming the number. The four file services are the ones this editor does not have: it has no file system, so open, read, write and close stop the program with Handler openFile is not implemented.

Your turn

Print The answer is 42 and end the program, with nothing else in the output. The string is written for you and the number is not part of it, so it takes two services.

Show solution

The second one reads a number and prints its square, with nothing else in the output. The test types 9, so the console reads 81.

Show solution