-2

I'm trying to print an equilateral triangle made of * in NASM based off of user input that can be from numbers 01 to 99. So if the user inputs 99 the bottom row of the triangle will have 99 stars, then the row above that would have 97 stars, then above that row 95 stars, etc. but it is only making a rectangle with the same width and length. How can I change my code so that it will print an equilateral triangle and not a rectangle? Thanks for the help.

section .data

star: db '*', 1                    
starLen1:  equ $-star  

;endl
newLineMsg: db 0xA, 0xD
newLineLen: equ $-newLineMsg

section .bss

TriangleSize resb 1               ;Holds width of triangle  
TriangleSize2 resb 1 
spacebewteenvalues resb 1   ;take space bwteen values   
loopcounter1 resb 2           ;hold count for first loop    
loopcounter2 resb 2          ;hold count for 2nd loop    
answer2 resb 2               ;hold first digital after times 10 for second input 
answer3 resb 2               ;hold value after plus 2nd digit

section .text
global _start

_start:

mov eax,3           
mov ebx,0         
mov ecx,TriangleSize        
mov edx,1      
int 80h     

mov eax,3           
mov ebx,0         
mov ecx,TriangleSize2        
mov edx,1      
int 80h     

;sub ascii from each digit
sub [TriangleSize], byte '0'
sub [TriangleSize2], byte '0'

;multiply first digit by 10
mov al, [TriangleSize] 
mov bl, 10
mul bl

;move into variable
mov [answer2], ax

;add 2nd digit
mov al, [answer2]
add al, [TriangleSize2]

;move both digit into variable
mov [answer3], al

;convert to decimal
add [answer3], byte '0'
    
;reset loop
mov [loopcounter1], byte '0'
mov [loopcounter2], byte '1'
    
;Start to cout *
jmp TriFunction1


;outputs first row
 TriFunction1:

;move counter into reigster
mov al, [loopcounter1]

;compare row length then jump to next row
cmp al, [answer3]
je CoutNewline           ;endl

;cout *
mov eax,4           
mov ebx,1          
mov ecx,star      
mov edx,1     
int 80h 

;inc the loop counter
add [loopcounter1], byte 1

;jump back to beginning
jmp TriFunction1


;goes to next row
TriFunction2:

;move 2nd loop counter into register
mov al, [loopcounter2]

;compare 
cmp al, [answer3]

je end      ;when rectangle has finished drawing go back to main

add [loopcounter2], byte 1

;output the next row of *
jmp TriFunction1
       
;endl
 CoutNewline:

;out \n
mov edx, newLineLen
mov ecx, newLineMsg
mov ebx, 1
mov eax, 4
int 0x80

;reset loop
mov [loopcounter1], word '0'

;check for next row
jmp TriFunction2


end:
mov eax,1            ; The system call for exit (sys_exit)
mov ebx,0            ; Exit with return code of 0 (no error)
int 80h;
  
;takes space between user input
takespacebetweennumbers:
mov eax,3            
mov ebx,0            
mov ecx,spacebewteenvalues      
mov edx,1     
int 80h 
ret            ;return back 
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
Noelle472
  • 1
  • 3
  • `mov [loopcounter1], byte '0'` will store **character** zero '0'=0x30=48. I think you should store **binary** zero 0 instead. – vitsoft Oct 22 '22 at 20:46
  • @vitsoft This code works but prints out a rectangle. I need to change it to print out a triangle – Noelle472 Oct 22 '22 at 20:57
  • Loop counters should normally be integers in registers, not ASCII digits in memory. That's slower and more complicated. You also don't need two different `read` system calls to read up to 2 bytes. Just read as many as there are, up to some buffer size, and loop over the digits - [NASM Assembly convert input to integer?](https://stackoverflow.com/q/19309749). There's also no need for any use of 16-bit registers in this 32-bit code, just bytes and dwords would be more efficient. – Peter Cordes Oct 23 '22 at 15:28
  • 1
    Also, that won't be an equilateral triangle. If the base width equals the height, the two sloped sides will be somewhat longer. https://www.omnicalculator.com/math/equilateral-triangle - the base to height ratio of an equilateral triangle is 3:4, according to some googling. So don't call it that, although IDK a better name for it; maybe a "centered triangle"? – Peter Cordes Oct 23 '22 at 15:37
  • Also, Linux newline is just `\n` aka `0xa`. Maybe you copied some DOS code that had a `0xa, 0xd`, although that's backwards for DOS, the standard DOS newline is \r\n. – Peter Cordes Oct 23 '22 at 15:40
  • 2-digit input can be *much* simpler if you don't care about the single digit case. Like `movzx` / `and` / `rol` (swap) / `aad` to do `high*10 + low`. Also, looping can be much simpler using counters in registers, like in https://godbolt.org/z/EYcznGjj3 where I just used EDX as the loop counter, printing a right-triangle because I got bored after playing with the input part. I just filled a buffer the length of one line, and incremented a pointer / decremented a length. But even nested loops with two `dec esi/jnz` or `dec edi/jnz` loops separate from the syscalls can be much simpler. – Peter Cordes Oct 23 '22 at 18:14
  • @PeterCordes It's called a right scalene triangle. If OP wants to draw an equilateral triangle as stated, it's trivially bounded to fail by a factor of `sqrt(2)`. If it's only drawing half of the triangle, then its height needs to be `sqrt(3)` of its half base (the length of the last line) and not roughly the half. Though their current relative error is just 11%. An equilateral triangle has a ratio height to base equal to `sqrt(3)/2` which is the square root of 3:4. Oh but they want to draw centered lines, not all shifted to the left. So it's just an isosceles triangle. – Margaret Bloom Oct 23 '22 at 19:12

1 Answers1

4

Try to draw the expected output and then analyze how to achieve the result. It's always better to divide a complex task into small steps.

I replaced the tail of your code from the line ;move both digit into variable:

;move both digit into variable
mov [answer3], al

; AL is now binary size of the triangle side (00..99). Examples:
; AL=07  Spaces Asterixes
;   *       3     1
;  ***      2     3
; *****     1     5
;*******    0     7

; AL=08  Spaces Asterixes
;   **     3      2
;  ****    2      4
; ******   1      6
;********  0      8

SECTION .data
Space     DB ' '
Asterix   DB '*'
NewLine   DB 10
SECTION .bss
Spaces    RESB 1
Asterixes RESB 1
Rows      RESB 1
SECTION .text
; Calculate Spaces and Asterixes from triangle Size in AL.
   CMP AL,1
   JNA end
   DEC AL
   SHR AL,1           ; AL=(Size-1)/2, CF=1 if Size was even.
   MOV [Spaces],AL
   MOV AH,1
   ADC AH,0           ; Start the 1st row with 1 or 2 asterixes. 
   MOV [Asterixes],AH
   INC AL
   MOV [Rows],AL
NextLine:
   CALL PrintSpaces
   CALL PrintAsterixes
   CALL PrintNewLine
   DEC BYTE [Spaces]
   ADD BYTE [Asterixes],2
   DEC BYTE [Rows]
   JNZ NextLine
end:
   mov eax,1            ; The system call for exit (sys_exit)
   mov ebx,0            ; Exit with return code of 0 (no error)
   int 80h;

PrintSpaces:
   LEA ECX,[Space]            ; Address of a character to print.
   MOVZX EBP,BYTE [Spaces]    ; How many times.
   JMP Print
PrintAsterixes:
   LEA ECX,[Asterix]          ; Address of a character to print.
   MOVZX EBP,BYTE [Asterixes] ; How many times.
   JMP Print
PrintNewLine:
   LEA ECX,[NewLine]          ; Address of a character to print.
   MOV EBP,1                  ; How many times.
Print:
   TEST EBP,EBP               ; How many times to repeat the character.
   JZ No
   MOV EBX,1                  ; Standard output handle.
   MOV EDX,1                  ; Print one character addressed by ECX.
NextChar:
   MOV EAX,4                  ; Invoke kernel function sys_write.
   INT 80h
   DEC EBP
   JNZ NextChar               ; Repeat EBP times.
No:RET

Modification which will draw a diamond shape instead of triangle:

; AL is now binary size of the diand width side (00..99). Examples:
; AL=07  Spaces Asterixes
;   *       3     1
;  ***      2     3
; *****     1     5
;*******    0     7
; *****     1     5
;  ***      2     3
;   *       3     1

; AL=08  Spaces Asterixes
;   **     3      2
;  ****    2      4
; ******   1      6
;********  0      8
; ******   1      6
;  ****    2      4
;   **     3      2

SECTION .text
; Calculate Spaces and Asterixes from triangle Size in AL.
   CMP AL,1
   JNA end
   DEC AL
   SHR AL,1   ; AL=(Size-1)/2, CF=1 if Size was even.
   MOV [Spaces],AL
   MOV AH,1
   ADC AH,0
   MOV [Asterixes],AH
   INC AL
   MOV [Rows],AL
UpperLines:                 ; The upper half of the diamond.
   CALL PrintSpaces
   CALL PrintAsterixes
   CALL PrintNewLine
   DEC BYTE [Spaces]
   ADD BYTE [Asterixes],2
   DEC BYTE [Rows]
   JNZ UpperLines
   SUB BYTE [Asterixes],2   ; Rollback the middle line of the diamond.
   INC BYTE [Spaces]
LowerLines:                 ; The lower half of the diamond.
   SUB BYTE [Asterixes],2
   INC BYTE [Spaces]
   CALL PrintSpaces
   CALL PrintAsterixes
   CALL PrintNewLine
   CMP BYTE [Asterixes],2
   JNB LowerLines
end:
vitsoft
  • 5,515
  • 1
  • 18
  • 31
  • are u able to make a diamond with this same code – Noelle472 Oct 24 '22 at 02:31
  • @Noelle472 That should be easy: when the base line of triangle is reached (`[Rows]==0`), instead of jump to `end` reverse modifications of `Spaces, Asterixes, Rows` and keep printing them until `[Asterixes]<1`. This will draw the second triangle upside down below the first one. – vitsoft Oct 24 '22 at 06:22
  • Any chance u can provide the code for that like you did for triangle? I'm lost in what you mean. – Noelle472 Oct 25 '22 at 01:44
  • @Noelle472 When you're lost, start with simpler examples, such as HelloWorld, and keep extending them gradually until you'll find yourself. Coding in asembly is exciting! – vitsoft Oct 25 '22 at 07:28