assemblyx86masmpascals-triangleoutput-formatting

Assembly Program for Pascal's Triangle Prints Garbled Output Beyond Row 4


I'm developing an Assembly program using the Irvine32 library to print Pascal's Triangle based on user input (ranging from 1 to 13 rows). While the program correctly displays the first few rows, starting from Row 5 onwards, the output becomes garbled and doesn't display the binomial coefficients as intended.


; ---------------------------------------------------------------------------------
; Name: Pascal's Triangulator Program
;
; Description:
; This program prompts the user to enter the number of rows (1-13) they wish to
; print of Pascal's Triangle. It calculates and displays each row with numbers
; separated by spaces. The program includes input validation to ensure the number
; of rows entered is within the specified range.
;
; ---------------------------------------------------------------------------------

INCLUDE Irvine32.inc

; --------------------------------
; Constants
; --------------------------------
MAX_ROWS       EQU 13
MIN_ROWS       EQU 1

; --------------------------------
; Data Segment
; --------------------------------
.data
    ; Program Messages
    introMsg          BYTE "Pascal's Triangulator - Programmed by Cameron Brooks!", 0
    introDesc         BYTE "This program will print up to 13 rows of Pascal's Triangle, per your specification!", 0
    promptMsg         BYTE "Enter total number of rows to print [1...13]: ", 0
    farewellMsg       BYTE "Thank you for using Pascal's Triangulator. Goodbye!", 0
    invalidInputMsg   BYTE "Invalid input. Please enter a number between 1 and 13.", 0

    ; Row Formatting
    currentRowMsg     BYTE "Row ", 0
    rowNumberMsg      BYTE ": ", 0

    ; Variables
    userRows          DWORD ?    ; Stores user input
    currentRow        DWORD ?    ; Stores current row index
    kValue            DWORD ?
    binCoeff          DWORD ?

.code
main PROC
    ; Introduction
    CALL Introduction

    ; Prompt user for input
    MOV     EDX, OFFSET promptMsg
    CALL    WriteString

    ; Read integer input
    CALL    ReadInt
    MOV     [userRows], EAX          ; Store input in userRows

    ; Validate input (1 <= userRows <= 13)
    CMP     EAX, MIN_ROWS
    JL      InvalidInput
    CMP     EAX, MAX_ROWS
    JG      InvalidInput

    ; Initialize loop counter in EBX
    MOV     EBX, 0                   ; EBX = loop counter (starts at 0)

RowLoop:
    ; Compare loop counter with userRows
    CMP     EBX, [userRows]
    JGE     EndProgram               ; Exit loop if all rows are printed

    ; Print "Row X: "
    MOV     EDX, OFFSET currentRowMsg
    CALL    WriteString               ; Print "Row "

    ; Print row number (EBX)
    PUSH    EAX                       ; Preserve EAX
    MOV     EAX, EBX                  ; Load currentRow into EAX
    CALL    WriteDec                  ; Print row number
    POP     EAX                       ; Restore EAX

    ; Print ": "
    MOV     EDX, OFFSET rowNumberMsg
    CALL    WriteString               ; Print ": "

    ; Set currentRow to EBX
    MOV     [currentRow], EBX         ; Update currentRow

    ; Print the current row
    CALL    PrintPascalRow            ; Print the current row

    ; Print newline
    CALL    CrLf

    ; Increment loop counter
    INC     EBX                       ; EBX++

    ; Jump back to loop
    JMP     RowLoop                   ; Repeat the loop

InvalidInput:
    ; Handle invalid input
    MOV     EDX, OFFSET invalidInputMsg
    CALL    WriteString
    CALL    CrLf
    JMP     EndProgram

EndProgram:
    ; Farewell message
    MOV     EDX, OFFSET farewellMsg
    CALL    WriteString
    CALL    CrLf

    ; Exit program
    EXIT
main ENDP

; ---------------------------------------------------------------------------------
; Procedure: Introduction
; Description:
; Displays the program title and a brief description.
;
; Registers Modified:
;   - EAX, EDX
; ---------------------------------------------------------------------------------
Introduction PROC
    ; Display Introduction Message
    MOV     EDX, OFFSET introMsg
    CALL    WriteString
    CALL    CrLf

    ; Display Introduction Description
    MOV     EDX, OFFSET introDesc
    CALL    WriteString
    CALL    CrLf
    CALL    CrLf

    RET
Introduction ENDP

; ---------------------------------------------------------------------------------
; Procedure: PrintPascalRow
; Description:
; Prints all binomial coefficients for the current row in Pascal's Triangle.
;
; Registers Modified:
;   - EAX, EBX, ESI, EDI, EDX
; ---------------------------------------------------------------------------------
PrintPascalRow PROC
    ; Preserve EAX, EDX, ESI, and EDI to avoid disrupting the main loop
    PUSH    EAX                     ; Preserve EAX
    PUSH    EDX                     ; Preserve EDX
    PUSH    ESI                     ; Preserve ESI
    PUSH    EDI                     ; Preserve EDI

    MOV     ESI, 0                  ; Initialize k to 0

RowElementLoop:
    MOV     EBX, [currentRow]       ; EBX = n (current row index)
    CMP     ESI, EBX
    JG      EndRow                  ; If k > n, end row

    ; Set kValue
    MOV     [kValue], ESI           ; k = current element index
    CALL    nChooseK                ; binCoeff = nCk

    ; Print Binomial Coefficient
    MOV     EAX, [binCoeff]         ; Load bin_coeff into EAX
    CALL    WriteDec                ; Print bin_coeff

    ; Print Space
    MOV     DL, 32                  ; ASCII space character (decimal 32)
    CALL    WriteChar               ; Print space

    ; Increment k
    INC     ESI                     ; k++

    ; Loop back to print next element
    JMP     RowElementLoop

EndRow:
    ; Restore preserved registers
    POP     EDI                     ; Restore EDI
    POP     ESI                     ; Restore ESI
    POP     EDX                     ; Restore EDX
    POP     EAX                     ; Restore EAX
    RET
PrintPascalRow ENDP

; ---------------------------------------------------------------------------------
; Procedure: nChooseK
; Description:
;   Calculates the binomial coefficient "n Choose k" using the multiplicative formula.
;
; Parameters:
;   - n is stored in 'currentRow'.
;   - k is stored in 'kValue'.
;
; Preconditions:
;   - 'currentRow' and 'kValue' contain valid integers where 0 <= k <= n.
;
; Postconditions:
;   - 'binCoeff' contains the value of "n Choose k".
;
; Registers Modified:
;   - EAX, EBX, ECX, EDX, ESI, EDI, EFLAGS
; ---------------------------------------------------------------------------------
nChooseK PROC
    ; Save Callee-Saved Registers
    PUSH    EBX
    PUSH    ESI
    PUSH    EDI

    ; Retrieve n and k
    MOV     EAX, [currentRow]       ; EAX = n
    MOV     EBX, [kValue]           ; EBX = k

    ; Handle k = 0 or k = n
    CMP     EBX, 0
    JE      SetOne
    CMP     EBX, EAX
    JE      SetOne

    ; If k > n, set binCoeff to 0
    CMP     EBX, EAX
    JG      SetZero

    ; Calculate nCk using Multiplicative Formula
    MOV     ECX, 1                  ; Initialize counter i=1
    MOV     EAX, 1                  ; Initialize binCoeff=1

CalcLoop:
    CMP     ECX, EBX
    JG      StoreResult

    ; Calculate (n - i + 1)
    MOV     ESI, [currentRow]       ; ESI = n
    SUB     ESI, ECX                ; ESI = n - i
    ADD     ESI, 1                  ; ESI = n - i + 1

    ; Multiply binCoeff by (n - i + 1)
    MUL     ESI                     ; EAX = EAX * (n - i +1)

    ; Divide binCoeff by i
    MOV     EDI, ECX                ; EDI = i
    XOR     EDX, EDX                ; Clear EDX for DIV
    DIV     EDI                     ; EAX = EAX / i

    ; Increment i
    INC     ECX                     ; i++
    JMP     CalcLoop

StoreResult:
    MOV     [binCoeff], EAX         ; Store the result
    JMP     EndCalculation

SetOne:
    MOV     [binCoeff], 1           ; binCoeff =1
    JMP     EndCalculation

SetZero:
    XOR     EAX, EAX                ; EAX =0
    MOV     [binCoeff], EAX         ; binCoeff=0

EndCalculation:
    ; Restore Callee-Saved Registers
    POP     EDI
    POP     ESI
    POP     EBX

    RET
nChooseK ENDP

END main

Expected Behavior:

When I input a number between 1 and 5, the program correctly displays the corresponding rows of Pascal's Triangle with proper spacing. For example, entering 4 produces:

Row 1: 11 
Row 2: 121 
Row 3: 1331 
Row 4: 14641  
Thank you for using Pascal's Triangulator. Goodbye!

Actual Behavior:

However, when I enter a number greater than 4 (e.g., 5 or 13), the output becomes garbled, for example 13 results in:

Row 1: 11
Row 2: 121
Row 3: 1331
Row 4: 14641
Row 5: 1510
10
51
Row 6: 1615201561
Row 7: 172135#35#2171
Row 8: 12856870F568281
Row 9: 19       36$84T126~126~84T36$9   1
Row 10: 110
45-120x210╥252ⁿ210╥120x45-10
1
Row 11: 111
557165Ñ330J462╬462╬330J165Ñ55711
1
Row 12: 112
66B220▄495∩792924£792495∩220▄66B12
1
Thank you for using Pascal's Triangulator. Goodbye!

Problem Details:

Rows 0-4: Correctly display the first six rows without proper spacing.

Rows 5-13:

What I've Tried:

Questions:

Additional Information:

Any assistance or insights into resolving these output issues would be greatly appreciated!


Solution

  • Thank you, Peter Cordes, for your invaluable feedback!

    Your observation regarding the correct register usage for the WriteChar procedure was exactly what I needed to resolve the issues I was facing with my Pascal's Triangle program.

    Issue Recap:

    Initially, my program correctly displayed Pascal's Triangle up to Row 5. However, starting from Row 6 onwards, the output became garbled with concatenated numbers and random symbols. This was primarily due to incorrect handling of the space character between binomial coefficients.

    Root Cause:

    As you pointed out, the WriteChar procedure from the Irvine32 library expects the character to be in the AL register, not DL. In my original code, I was moving the space character into DL, which led to incorrect character printing.

    Solution Implemented:

    1. Corrected Register for WriteChar:

      • Original Code:

        ; Print Space
        MOV DL, 32                  ; ASCII space character (decimal 32)
        CALL WriteChar               ; Print space
        
      • Updated Code:

        ; Print Space
        MOV AL, 32                  ; ASCII space character (decimal 32)
        CALL WriteChar               ; Print space
        
      • Explanation:
        By moving the space character (32) into the AL register instead of DL, the WriteChar procedure correctly prints the space, ensuring proper separation between binomial coefficients.

    2. Verified Register Preservation:

      • Ensured that all necessary registers (EAX, EDX, ESI, EDI) are properly preserved at the beginning of procedures and restored before exiting. This prevents unintended side effects from procedure calls like WriteDec and WriteChar.

    Updated Output:

    After implementing the changes, the program now correctly displays Pascal's Triangle with proper spacing between numbers. Here's an example of the output when entering 13 rows:

    Pascal's Triangulator - Programmed by Cameron Brooks!
    This program will print up to 13 rows of Pascal's Triangle, per your specification!
    
    Enter total number of rows to print [1...13]: 13
    Row 0: 1 
    Row 1: 1 1 
    Row 2: 1 2 1 
    Row 3: 1 3 3 1 
    Row 4: 1 4 6 4 1 
    Row 5: 1 5 10 10 5 1 
    Row 6: 1 6 15 20 15 6 1 
    Row 7: 1 7 21 35 35 21 7 1 
    Row 8: 1 8 28 56 70 56 28 8 1 
    Row 9: 1 9 36 84 126 126 84 36 9 1 
    Row 10: 1 10 45 120 210 252 210 120 45 10 1 
    Row 11: 1 11 55 165 330 462 462 330 165 55 11 1 
    Row 12: 1 12 66 220 495 792 924 792 495 220 66 12 1 
    Thank you for using Pascal's Triangulator. Goodbye!
    

    Key Takeaways:

    Final Notes:

    Thanks to Peter's guidance, the program now functions as intended, accurately displaying Pascal's Triangle with proper spacing between numbers. If anyone has further suggestions or improvements, I'd be happy to hear them!