帕斯卡三角形的汇编程序在第 4 行之外打印乱码输出

问题描述 投票:0回答:1

我正在使用 Irvine32 库开发一个汇编程序,根据用户输入(范围从 1 到 13 行)打印帕斯卡三角形。虽然程序正确显示前几行,但从第 5 行开始,输出变成乱码,并且未按预期显示二项式系数。


; ---------------------------------------------------------------------------------
; 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

预期行为:

当我输入1到5之间的数字时,程序会以适当的间距正确显示帕斯卡三角形的相应行。例如,输入 4 会产生:

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

实际行为:

但是,当我输入大于 4 的数字(例如 5 或 13)时,输出会变成乱码,例如 13 会导致:

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!

问题详情:

第 0-4 行: 正确显示前六行,没有适当的间距。

第 5-13 行:

  • 输出开始连接不带空格的数字(例如,“Row 1: 11”而不是“第 1 行:1 1”)。
  • 除了第 5 行之外,输出还包含 随机符号和格式不正确的数字。

我尝试过的:

  • 寄存器保存:确保所有必要的寄存器(EAX、 EDX、ESI、EDI)保留在程序开始处,并且 退出前恢复以防止意外的副作用。
  • 使用 WriteDec: 将任何自定义数字打印例程替换为 来自 Irvine32 库的内置 WriteDec 过程来处理 可靠的十进制数字打印。
  • 显式空格字符:更改了空格字符打印 MOV DL, ' ' 到 MOV DL, 32 以确保使用正确的 ASCII 值。

问题:

  • 为什么第 5 行以外的二项式系数与串联数字和随机符号一起打印不正确?
  • 较高行的数字之间如何打印空格是否存在问题?
  • 是否有任何被忽视的寄存器管理问题或代码的其他方面可能导致此行为?

附加信息:

  • 汇编器和环境:在 32 位 Windows 环境中将 MASM 与 Irvine32 库结合使用。

  • 之前的尝试:遵循之前的建议来保留额外的寄存器并消除自定义输出程序,但问题仍然存在。

任何有关解决这些输出问题的帮助或见解将不胜感激!

assembly x86 masm pascals-triangle output-formatting
1个回答
0
投票

感谢 Peter Cordes 提供的宝贵反馈!

您对

WriteChar
程序的正确寄存器使用的观察正是我解决我的帕斯卡三角程序所面临的问题所需要的。

问题回顾:

最初,我的程序正确显示帕斯卡三角形直到第 5 行。但是,从第 6 行开始,输出因串联数字和随机符号而变得乱码。这主要是由于二项式系数之间的空格字符处理不正确。

根本原因:

正如您所指出的,Irvine32 库中的

WriteChar
过程期望字符位于
AL
寄存器中,而不是
DL
中。在我的原始代码中,我将空格字符移动到
DL
,这导致了错误的字符打印。

已实施的解决方案:

  1. 更正注册

    WriteChar

    • 原代码:

      ; Print Space
      MOV DL, 32                  ; ASCII space character (decimal 32)
      CALL WriteChar               ; Print space
      
    • 更新代码:

      ; Print Space
      MOV AL, 32                  ; ASCII space character (decimal 32)
      CALL WriteChar               ; Print space
      
    • 说明:
      通过将空格字符 (

      32
      ) 移入
      AL
      寄存器而不是
      DL
      WriteChar
      程序可以正确打印空格,确保二项式系数之间的正确分离。

  2. 已验证的注册保存:

    • 确保在程序开始时正确保存所有必要的寄存器(
      EAX
      EDX
      ESI
      EDI
      )并在退出前恢复。这可以防止过程调用(如
      WriteDec
      WriteChar
      )产生意外的副作用。

更新输出:

实施更改后,程序现在可以正确显示帕斯卡三角形,并且数字之间有适当的间距。以下是输入

13
行时的输出示例:

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!

要点:

  • 了解图书馆程序:
    彻底理解像

    WriteChar
    这样的库程序如何期望它们的参数是至关重要的。将数据放错到错误的寄存器中可能会导致意外的行为。

  • 注册管理:
    正确保存和恢复寄存器对于维护汇编语言中过程调用的数据完整性至关重要。

最后注意事项:

感谢 Peter 的指导,该程序现在可以按预期运行,准确地显示帕斯卡三角形,并且数字之间的间距适当。如果有人有进一步的建议或改进,我很乐意听到他们!

© www.soinside.com 2019 - 2024. All rights reserved.