I'm asked to write a RISC-V assembly code to generate a symmetric diamond made up of asterisks which has a middle row width of N. N is the odd number taken as input from the user.
Example: Inputting 5 into the terminal should yield the result:
*
***
*****
***
*
Due to my relative inexperience in working with RISC-V as I am a beginner, I couldn't fully accomplish this.
What I have written and tested in RARS is this:
# CONST
.eqv SYSTEM_EXIT, 93
.eqv SYSTEM_READ, 63
.eqv SYSTEM_WRITE, 64
.eqv STDIN, 0
.eqv STDOUT, 1
# DATA
.data
space:
.asciz " "
asterisk:
.asciz "*"
newline:
.asciz "\n"
in_buffer:
.space 16
# TEXT
.text
.global _start
_start:
li a0, STDIN
la a1, in_buffer
li a2, 16
li a7, SYSTEM_READ
ecall
la a1, in_buffer
li t0, 0
li t1, 10
convert_input:
lbu t2, 0(a1)
beqz t2, input_done
beq t2, t1, input_done
sb t2, 0(a1)
addi t2, t2, -48
mul t0, t0, t1
add t0, t0, t2
addi a1, a1, 1
j convert_input
input_done:
andi t3, t0, 1
beqz t3, not_odd
li t4, 0
li t5, 0
diamond_outer_loop:
bge t4, t0, end_pattern
li t6, 0
print_spaces_before_asterisks:
blt t5, t0, check_asterisks
j next_row
check_asterisks:
bge t5, t6, print_asterisks
j inc_space
print_asterisks:
li a0, STDOUT
li a2, 1
la a1, asterisk
li a7, SYSTEM_WRITE
ecall
addi t6, t6, 2
j next_asterisk
inc_space:
li a0, STDOUT
li a2, 1
la a1, space
li a7, SYSTEM_WRITE
ecall
addi t5, t5, 1
j print_spaces_before_asterisks
next_asterisk:
li a0, STDOUT
li a2, 1
la a1, newline
li a7, SYSTEM_WRITE
ecall
addi t4, t4, 1
addi t5, t5, -1
j diamond_outer_loop
next_row:
li a0, STDOUT
li a2, 1
la a1, newline
li a7, SYSTEM_WRITE
ecall
addi t5, t5, 2
j diamond_outer_loop
not_odd:
addi t0, t0, -1
end_pattern:
li a0, 0
li a7, SYSTEM_EXIT
ecall
What this program yields as a result for example input of 5 is:
*
*
*
*
*
It correctly changes size according to the input number (7 lines if the input 7 for example) but the number of stars is always 1 and is formatted incorrectly.
First and foremost you need a working algorithm. If you have it already (i.e. in your head or in C or a language you know well), then you need to debug this. The way to debug is to single step and observe what it is doing at each instruction. Since the output is wrong immediately (it should print some spaces first), you have something to debug right away.
It is most helpful to have an algorithm using C or a language you know well — before doing the assembly version. (In other words, you don't have to think in assembly language to develop an algorithm.) Once you have the algorithm, take it to assembly language as literally as possible. Assembly language is so much easier if you already know what the code should do (the variables and the control flow that you need).
And if you transcribe a working C code to assembly language, then you'll know that any bugs are assembly translation problems not algorithmic problems, and those are much easier to debug. (Debugging a broken algorithm is hard in assembly.)
When you have an algorithm in C or other language, you have something you can compare with during debugging, which will tell you exactly where your assembly code is going wrong. Single step each, both the C and the assembly, and you'll see where the assembly is going wrong.
You may be thinking that you don't need a C version, but let's note that it will only be a 3-5 of lines of code, and it is practically mandatory for you to make progress with this.
I suggest getting it working in C, then take it in C to "if-goto-label" style.
For example, a nested for-loop like this (for printing a simple square matrix):
Can be transformed, still in C, into if-goto-label:
This is an accurate transcription of the control structures used in that simple nested for loop example. This transformation will run the same as the for-loop version.
Translating controls structures properly takes some work as assembly language will allow many useless or wrong things. :But rest assured that each control flow construct (if-then-else, for/while, sequential statements, nested statements) have a simple pattern in if-goto-label. Follow the proper pattern and your control flow in assembly will run the same as the C version.
(It is easy here to try to optimize something during translation to assembly, but resist that urge, use the simple patterns even if they appear optimizable. My preference is to apply optimization to the C if-goto-label code first, then if that works, take that to assembly.)