我无法在具有直接内存访问的 8086 程序集中使用中点算法绘制圆
I am unable to draw a circle using midpoint algorithm in 8086 Assembly with Direct Memory Access
我有一个作业,我应该让形状移动并用颜色改变形状。首先,我没有成功地画出一个圆的八分圆。假设在 DMA 模式下使用 TASM 使用 Intel 8086 汇编语言。 (模式 19)
我在想,如果我能完成一个圆圈,我可以为它制作动画并改变形状。搞不懂是算法错了还是代码错了
.model small
.stack 256
.code
startaddr dw 0a000h ;start of video memory
color db 3
xc dw 160
yc dw 100
r dw 50 ; radius
x dw 0
y dw 50 ;radius
pk dw 1
temp dw 1
plot macro r1, r2, r3 ;x,y,color
mov ax, r2
mov bx, 320
mul bx
add ax, r1
mov di, ax
mov al, r3
mov es:[di], al
endm
start:
mov ax, yc
add y, ax
mov ah,00
mov al, 13h
int 10h ;switch to 320x200 mode
mov es, startaddr
mov dx, y
mov ax, xc
add x, ax
plot x, dx, color
mov bx, r
mov pk, bx
sub pk, 1
neg pk
cmp pk, 0
jge pk1
drawc:
mov bx, x
sub bx, xc
mov ax, y
sub ax, yc
mov temp, ax
cmp bx, temp
jge keypress
mov dx, y
plot x, dx, color
peekay:
cmp pk, 0
jg pk1
mov ax, x
mov bx, 2
mul bx
add ax, pk
mov pk, ax
inc x ;x+1
jmp drawc
pk1:
dec y
mov ax, x
sub ax, y
mov bx, 2
mul bx
add ax, pk
mov pk, ax
inc x
jmp drawc
keypress:
mov ah,00
int 16h ;await keypress
mov ah,00
mov al,03
int 10h
mov ah,4ch
mov al,00 ;terminate program
int 21h
end start
Output
你目前的代码很难理解,因为它的注释太少了。当您编写汇编时,重要的是自由地注释,解释代码应该做什么,因为语言本身不是很有表现力。当然,我知道每条指令的含义,但作为一个人,我很难跟踪所有已注册的值及其在整个过程中的流动。我也不知道您的代码在 高级 .
上应该做什么
更糟糕的是,当我尝试阅读代码时,您的标签名称对我来说也毫无意义。什么是 peekay
? pk1
?我会 猜测 drawc
是 DrawCircle
,但为什么不这样称呼它呢?那么你甚至不需要在那里发表评论,因为从名字上就很明显了。
至于你的实际问题,从输出来看你已经成功画了一条线。但这并不是你真正想要画的。你想使用 midpoint algorithm to draw a circle. And you're in luck, because that Wikipedia article has sample code implementing this algorithm in C。如果您不熟悉汇编,并且正在为这个问题而苦苦挣扎,我的建议是先用 C 语言编写代码并确保您的算法有效。 然后,你可以将工作的C代码翻译成汇编。一旦您对汇编更加熟悉,就可以开始跳过步骤并直接用汇编编写,将 C 风格的算法翻译成您头脑中的汇编指令。至少,这就是我所做的,而且我仍然每当我遇到困难时都会回到 C。
所以让我们从维基百科窃取一些 C 代码:
void DrawCircle(int x0, int y0, int radius)
{
int x = radius;
int y = 0;
int err = 0;
while (x >= y)
{
PlotPixel(x0 + x, y0 + y);
PlotPixel(x0 + y, y0 + x);
PlotPixel(x0 - y, y0 + x);
PlotPixel(x0 - x, y0 + y);
PlotPixel(x0 - x, y0 - y);
PlotPixel(x0 - y, y0 - x);
PlotPixel(x0 + y, y0 - x);
PlotPixel(x0 + x, y0 - y);
if (err <= 0)
{
y += 1;
err += 2*y + 1;
}
if (err > 0)
{
x -= 1;
err -= 2*x + 1;
}
}
}
通常情况下,发明、编写和测试算法是最困难的部分,但我们只是通过窃取 建立在他人工作的基础上来绕过这一步。现在,我们只需要 PlotPixel
函数。不过这很简单——您的汇编代码已经包含了该部分,因为您已成功绘制一条线!
为了让我们达成一致,最简单的方法是调用 BIOS 中断 10h,函数 0Ch,它在图形模式下绘制一个像素。 DX
包含点的x坐标,CX
包含y坐标,AL
包含颜色属性,BH
包含视频页面。为简单起见,我们假设视频页面为 0(默认值)。这可以包含在一个简单的宏中,如下所示:
PlotPixel MACRO x, y, color, videoPage
mov cx, x
mov dx, y
mov bh, videoPage
mov ax, (color | (0Ch << 4)) ; AL == color, AH == function 0Ch
int 10h
ENDM
第二阶段是将 C 函数翻译成汇编。由于对 PlotPixel
函数的重复调用以及循环结构,此翻译不会是一个简单的练习。我们将以 长 代码清单结束。我们还有另一个问题:没有足够的寄存器来保存我们所有的临时值!当然,这在通用寄存器数量非常有限的 x86 上很常见,所以我们将做我们总是必须做的事情:使用堆栈。它速度较慢,但它有效。 (这段代码无论如何都不会很快。)这是我想出的:
; Draws a circle of the specified radius at the specified location
; using the midpoint algorithm.
;
; Parameters: DX == center, x
; CX == center, y
; BX == radius
; AL == color
; Clobbers: <none>
; Returns: <none>
DrawCircle:
push bp
mov bp, sp
push dx ; xCenter [bp - 2]
push cx ; yCenter [bp - 4]
push bx ; x [bp - 6]
push 0 ; y [bp - 8]
push 0 ; err [bp - 10]
; Prepare to plot pixels:
mov ah, 0Ch ; AH == function 0Ch (plot pixel in graphics mode)
xor bx, bx ; BH == video page 0
DrawLoop:
mov dx, WORD [bp - 6]
cmp dx, WORD [bp - 8]
jl Finished ; (x < y) ? we're finished drawing : keep drawing
; Plot pixels:
mov cx, WORD [bp - 2]
mov dx, WORD [bp - 4]
add cx, WORD [bp - 6] ; CX = xCenter + x
add dx, WORD [bp - 8] ; DX = yCenter + y
int 10h
mov cx, WORD [bp - 2]
sub cx, WORD [bp - 6] ; CX = xCenter - x
int 10h
mov dx, WORD [bp - 4]
sub dx, WORD [bp - 8] ; DX = yCenter - y
int 10h
mov cx, WORD [bp - 2]
add cx, WORD [bp - 6] ; CX = xCenter + x
int 10h
mov cx, WORD [bp - 2]
mov dx, WORD [bp - 4]
add cx, WORD [bp - 8] ; CX = xCenter + y
add dx, WORD [bp - 6] ; DX = yCenter + x
int 10h
mov cx, WORD [bp - 2]
sub cx, WORD [bp - 8] ; CX = xCenter - y
int 10h
mov dx, WORD [bp - 4]
sub dx, WORD [bp - 6] ; DX = yCenter - x
int 10h
mov cx, WORD [bp - 2]
add cx, WORD [bp - 8] ; CX = xCenter + y
int 10h
; Update state values and check error:
mov dx, WORD [bp - 8]
inc dx
mov WORD [bp - 8], dx
add dx, dx ; DX *= 2
inc dx
add dx, WORD [bp - 10]
mov WORD [bp - 10], dx
sub dx, WORD [bp - 6]
add dx, dx ; DX *= 2
js DrawLoop ; DX < 0 ? keep looping : fall through and check error
inc dx
dec cx
mov WORD [bp - 6], cx
add cx, cx ; CX *= 2
neg cx
inc cx ; CX = (1 - CX)
add WORD [bp - 10], cx
jmp DrawLoop ; keep looping
Finished:
pop bx ; (clean up the stack; no need to save this value)
pop bx ; (clean up the stack; no need to save this value)
pop bx
pop cx
pop dx
pop bp
ret
END DrawCircle
您会看到,在函数的顶部,我在堆栈上分配了 space 用于我们的临时值。如内联注释中所述,我们将使用 BP
寄存器的偏移量访问它们中的每一个。*
函数的大部分由主绘图循环组成,DrawLoop
。在顶部,我们进行比较以查看是否应该继续循环。然后我们开始认真绘制像素,进行必要的操作,就像维基百科的 C 代码中显示的那样。绘图后,我们进行更多操作,将结果存储回堆栈中的临时值,然后 运行 再进行几次比较,看看我们是否应该继续循环(同样,大致类似于 if
原始 C 代码中的块)。最后,一旦我们完成,我会在返回前清理堆栈。
请注意,我 "inlined" 来自 PlotPixel
宏的代码。这使我可以在顶部设置 AH
和 BH
寄存器,并将它们重新用于所有调用。这稍微缩短了代码,也加快了速度。并行结构使其具有足够的可读性(至少在我看来是这样)。
这里没有什么特别棘手的事情,除了我的一些算术操作。显而易见的是,将一个寄存器加到它本身与将它乘以 2 是一样的。我通过取反原始值然后将其递增 1 从 1 中减去一个寄存器。这些在代码中有注释。我认为唯一值得指出的另一件事是我使用 test reg, reg
进行简单比较,而不是 cmp reg, 0
。前者更短更快。
只需设置您的视频模式,将参数放入适当的寄存器,然后调用它!
当然有加速此功能的方法,但它们是以严重牺牲可读性和可理解性为代价的。在继续阅读之前,请确保您首先了解这里发生的事情!
此代码中的大瓶颈有两个:
使用堆栈。
可能有一种更有创意的方法来编写代码,以更优化地使用寄存器,根据需要调整值以避免尽可能多地占用内存。但这对我虚弱的头脑来说太多了,无法跟踪。它不应该非常慢——毕竟所有这些值都将适合缓存。
使用 BIOS 像素绘制功能。
这个 非常 慢,即使在现代机器上也是如此。它可以很好地在屏幕上绘制一个简单的圆圈,尤其是在虚拟化硬件上,但对于多个圆圈的复杂输出来说速度还不够快。要解决这个问题,您将不得不求助于视频内存的原始位旋转,这是不可移植的。我想这就是您所说的 "DMA mode" 的意思。如果这样做,您将限制您的代码只在具有符合标准规范的 VGA 硬件的系统上使用 运行ning。你也输了 resolution/mode-independence.
进行更改非常简单。我刚刚添加了一个 PlotPixel
函数来完成繁重的工作,并更改了 DrawCircle
中的代码以调用此函数而不是 BIOS 中断(并删除了前导 mov ah, 0Ch
和 xor bx, bx
行):
; Plots a pixel by writing a BYTE-sized color value directly to memory,
; based on the formula: 0A000h + (Y * 320) + X
;
; Parameters: DX == x-coordinate
; CX == y-coordinate
; AL == color
; Clobbers: CX
; Returns: <none>
PlotPixel:
push di
mov di, 0A000h
mov es, di ; ES == video memory offset
mov di, cx
add cx, cx
add cx, cx
add di, cx
shl di, 6 ; Y *= 320
add di, dx ; Y += X
mov BYTE es:[di], al ; write the color byte to memory at (X, Y)
pop di
ret
END PlotPixel
我想你已经理解这部分了,但之所以能这样工作是因为在模式 19 (13h) 中,有 320×200 像素和 256 种颜色。因为 256 == 28,每个像素的颜色值正好存储在 1 个字节(8 位)中。因此,如果从显存的开头(地址A000h)开始,像素是线性存储的,可以直接写入它们的颜色值。写入像素 (x, y) 的公式为:A000h + (y * 320) + x
.
正如您在原始代码中所做的那样,您可以通过将 ES
的设置提升到调用方并将 ES == 0A000h
作为 PlotPixel
函数的前提条件来进一步改进.但是我确实对您的原始代码进行了重大更改,用左移和一些加法替换了缓慢的乘法 (MUL
)。您原来的基于乘法的代码也有一个溢出错误,我的重写修复了这个错误。
您可以通过一次写入多个字节来进一步加快速度——在 8086 上,这将是写入一个 WORD(两个字节)。这将需要 "inlining" 直接在 DrawCircle
函数中使用像素绘图代码,并进行一些创造性的寄存器分配以确保您要绘制的第一个像素位于 AL
中,并且第二个像素在 AH
中。我会把它留作练习。
* 我喜欢使用宏将这些偏移量转换为常量,然后在整个函数中使用该符号名称,而不是对数值进行硬编码。这不仅使代码更具可读性,而且如果您决定更改推送参数的顺序或推送的参数数量,还可以更轻松地进行更改。我没有在这里这样做,因为我不知道 TASM 的正确语法是什么,并且在调试这段代码时它咬了我。
说起来,我写了代码在 NASM 中并尝试即时翻译成 TASM,这是我不太熟悉的一种语法。对不起,如果有任何小的语法问题你必须先解决才能 assemble 它!
我有一个作业,我应该让形状移动并用颜色改变形状。首先,我没有成功地画出一个圆的八分圆。假设在 DMA 模式下使用 TASM 使用 Intel 8086 汇编语言。 (模式 19) 我在想,如果我能完成一个圆圈,我可以为它制作动画并改变形状。搞不懂是算法错了还是代码错了
.model small
.stack 256
.code
startaddr dw 0a000h ;start of video memory
color db 3
xc dw 160
yc dw 100
r dw 50 ; radius
x dw 0
y dw 50 ;radius
pk dw 1
temp dw 1
plot macro r1, r2, r3 ;x,y,color
mov ax, r2
mov bx, 320
mul bx
add ax, r1
mov di, ax
mov al, r3
mov es:[di], al
endm
start:
mov ax, yc
add y, ax
mov ah,00
mov al, 13h
int 10h ;switch to 320x200 mode
mov es, startaddr
mov dx, y
mov ax, xc
add x, ax
plot x, dx, color
mov bx, r
mov pk, bx
sub pk, 1
neg pk
cmp pk, 0
jge pk1
drawc:
mov bx, x
sub bx, xc
mov ax, y
sub ax, yc
mov temp, ax
cmp bx, temp
jge keypress
mov dx, y
plot x, dx, color
peekay:
cmp pk, 0
jg pk1
mov ax, x
mov bx, 2
mul bx
add ax, pk
mov pk, ax
inc x ;x+1
jmp drawc
pk1:
dec y
mov ax, x
sub ax, y
mov bx, 2
mul bx
add ax, pk
mov pk, ax
inc x
jmp drawc
keypress:
mov ah,00
int 16h ;await keypress
mov ah,00
mov al,03
int 10h
mov ah,4ch
mov al,00 ;terminate program
int 21h
end start
Output
你目前的代码很难理解,因为它的注释太少了。当您编写汇编时,重要的是自由地注释,解释代码应该做什么,因为语言本身不是很有表现力。当然,我知道每条指令的含义,但作为一个人,我很难跟踪所有已注册的值及其在整个过程中的流动。我也不知道您的代码在 高级 .
上应该做什么更糟糕的是,当我尝试阅读代码时,您的标签名称对我来说也毫无意义。什么是 peekay
? pk1
?我会 猜测 drawc
是 DrawCircle
,但为什么不这样称呼它呢?那么你甚至不需要在那里发表评论,因为从名字上就很明显了。
至于你的实际问题,从输出来看你已经成功画了一条线。但这并不是你真正想要画的。你想使用 midpoint algorithm to draw a circle. And you're in luck, because that Wikipedia article has sample code implementing this algorithm in C。如果您不熟悉汇编,并且正在为这个问题而苦苦挣扎,我的建议是先用 C 语言编写代码并确保您的算法有效。 然后,你可以将工作的C代码翻译成汇编。一旦您对汇编更加熟悉,就可以开始跳过步骤并直接用汇编编写,将 C 风格的算法翻译成您头脑中的汇编指令。至少,这就是我所做的,而且我仍然每当我遇到困难时都会回到 C。
所以让我们从维基百科窃取一些 C 代码:
void DrawCircle(int x0, int y0, int radius)
{
int x = radius;
int y = 0;
int err = 0;
while (x >= y)
{
PlotPixel(x0 + x, y0 + y);
PlotPixel(x0 + y, y0 + x);
PlotPixel(x0 - y, y0 + x);
PlotPixel(x0 - x, y0 + y);
PlotPixel(x0 - x, y0 - y);
PlotPixel(x0 - y, y0 - x);
PlotPixel(x0 + y, y0 - x);
PlotPixel(x0 + x, y0 - y);
if (err <= 0)
{
y += 1;
err += 2*y + 1;
}
if (err > 0)
{
x -= 1;
err -= 2*x + 1;
}
}
}
通常情况下,发明、编写和测试算法是最困难的部分,但我们只是通过窃取 建立在他人工作的基础上来绕过这一步。现在,我们只需要 PlotPixel
函数。不过这很简单——您的汇编代码已经包含了该部分,因为您已成功绘制一条线!
为了让我们达成一致,最简单的方法是调用 BIOS 中断 10h,函数 0Ch,它在图形模式下绘制一个像素。 DX
包含点的x坐标,CX
包含y坐标,AL
包含颜色属性,BH
包含视频页面。为简单起见,我们假设视频页面为 0(默认值)。这可以包含在一个简单的宏中,如下所示:
PlotPixel MACRO x, y, color, videoPage
mov cx, x
mov dx, y
mov bh, videoPage
mov ax, (color | (0Ch << 4)) ; AL == color, AH == function 0Ch
int 10h
ENDM
第二阶段是将 C 函数翻译成汇编。由于对 PlotPixel
函数的重复调用以及循环结构,此翻译不会是一个简单的练习。我们将以 长 代码清单结束。我们还有另一个问题:没有足够的寄存器来保存我们所有的临时值!当然,这在通用寄存器数量非常有限的 x86 上很常见,所以我们将做我们总是必须做的事情:使用堆栈。它速度较慢,但它有效。 (这段代码无论如何都不会很快。)这是我想出的:
; Draws a circle of the specified radius at the specified location
; using the midpoint algorithm.
;
; Parameters: DX == center, x
; CX == center, y
; BX == radius
; AL == color
; Clobbers: <none>
; Returns: <none>
DrawCircle:
push bp
mov bp, sp
push dx ; xCenter [bp - 2]
push cx ; yCenter [bp - 4]
push bx ; x [bp - 6]
push 0 ; y [bp - 8]
push 0 ; err [bp - 10]
; Prepare to plot pixels:
mov ah, 0Ch ; AH == function 0Ch (plot pixel in graphics mode)
xor bx, bx ; BH == video page 0
DrawLoop:
mov dx, WORD [bp - 6]
cmp dx, WORD [bp - 8]
jl Finished ; (x < y) ? we're finished drawing : keep drawing
; Plot pixels:
mov cx, WORD [bp - 2]
mov dx, WORD [bp - 4]
add cx, WORD [bp - 6] ; CX = xCenter + x
add dx, WORD [bp - 8] ; DX = yCenter + y
int 10h
mov cx, WORD [bp - 2]
sub cx, WORD [bp - 6] ; CX = xCenter - x
int 10h
mov dx, WORD [bp - 4]
sub dx, WORD [bp - 8] ; DX = yCenter - y
int 10h
mov cx, WORD [bp - 2]
add cx, WORD [bp - 6] ; CX = xCenter + x
int 10h
mov cx, WORD [bp - 2]
mov dx, WORD [bp - 4]
add cx, WORD [bp - 8] ; CX = xCenter + y
add dx, WORD [bp - 6] ; DX = yCenter + x
int 10h
mov cx, WORD [bp - 2]
sub cx, WORD [bp - 8] ; CX = xCenter - y
int 10h
mov dx, WORD [bp - 4]
sub dx, WORD [bp - 6] ; DX = yCenter - x
int 10h
mov cx, WORD [bp - 2]
add cx, WORD [bp - 8] ; CX = xCenter + y
int 10h
; Update state values and check error:
mov dx, WORD [bp - 8]
inc dx
mov WORD [bp - 8], dx
add dx, dx ; DX *= 2
inc dx
add dx, WORD [bp - 10]
mov WORD [bp - 10], dx
sub dx, WORD [bp - 6]
add dx, dx ; DX *= 2
js DrawLoop ; DX < 0 ? keep looping : fall through and check error
inc dx
dec cx
mov WORD [bp - 6], cx
add cx, cx ; CX *= 2
neg cx
inc cx ; CX = (1 - CX)
add WORD [bp - 10], cx
jmp DrawLoop ; keep looping
Finished:
pop bx ; (clean up the stack; no need to save this value)
pop bx ; (clean up the stack; no need to save this value)
pop bx
pop cx
pop dx
pop bp
ret
END DrawCircle
您会看到,在函数的顶部,我在堆栈上分配了 space 用于我们的临时值。如内联注释中所述,我们将使用 BP
寄存器的偏移量访问它们中的每一个。*
函数的大部分由主绘图循环组成,DrawLoop
。在顶部,我们进行比较以查看是否应该继续循环。然后我们开始认真绘制像素,进行必要的操作,就像维基百科的 C 代码中显示的那样。绘图后,我们进行更多操作,将结果存储回堆栈中的临时值,然后 运行 再进行几次比较,看看我们是否应该继续循环(同样,大致类似于 if
原始 C 代码中的块)。最后,一旦我们完成,我会在返回前清理堆栈。
请注意,我 "inlined" 来自 PlotPixel
宏的代码。这使我可以在顶部设置 AH
和 BH
寄存器,并将它们重新用于所有调用。这稍微缩短了代码,也加快了速度。并行结构使其具有足够的可读性(至少在我看来是这样)。
这里没有什么特别棘手的事情,除了我的一些算术操作。显而易见的是,将一个寄存器加到它本身与将它乘以 2 是一样的。我通过取反原始值然后将其递增 1 从 1 中减去一个寄存器。这些在代码中有注释。我认为唯一值得指出的另一件事是我使用 test reg, reg
进行简单比较,而不是 cmp reg, 0
。前者更短更快。
只需设置您的视频模式,将参数放入适当的寄存器,然后调用它!
当然有加速此功能的方法,但它们是以严重牺牲可读性和可理解性为代价的。在继续阅读之前,请确保您首先了解这里发生的事情!
此代码中的大瓶颈有两个:
使用堆栈。
可能有一种更有创意的方法来编写代码,以更优化地使用寄存器,根据需要调整值以避免尽可能多地占用内存。但这对我虚弱的头脑来说太多了,无法跟踪。它不应该非常慢——毕竟所有这些值都将适合缓存。
使用 BIOS 像素绘制功能。
这个 非常 慢,即使在现代机器上也是如此。它可以很好地在屏幕上绘制一个简单的圆圈,尤其是在虚拟化硬件上,但对于多个圆圈的复杂输出来说速度还不够快。要解决这个问题,您将不得不求助于视频内存的原始位旋转,这是不可移植的。我想这就是您所说的 "DMA mode" 的意思。如果这样做,您将限制您的代码只在具有符合标准规范的 VGA 硬件的系统上使用 运行ning。你也输了 resolution/mode-independence.
进行更改非常简单。我刚刚添加了一个
PlotPixel
函数来完成繁重的工作,并更改了DrawCircle
中的代码以调用此函数而不是 BIOS 中断(并删除了前导mov ah, 0Ch
和xor bx, bx
行):; Plots a pixel by writing a BYTE-sized color value directly to memory, ; based on the formula: 0A000h + (Y * 320) + X ; ; Parameters: DX == x-coordinate ; CX == y-coordinate ; AL == color ; Clobbers: CX ; Returns: <none> PlotPixel: push di mov di, 0A000h mov es, di ; ES == video memory offset mov di, cx add cx, cx add cx, cx add di, cx shl di, 6 ; Y *= 320 add di, dx ; Y += X mov BYTE es:[di], al ; write the color byte to memory at (X, Y) pop di ret END PlotPixel
我想你已经理解这部分了,但之所以能这样工作是因为在模式 19 (13h) 中,有 320×200 像素和 256 种颜色。因为 256 == 28,每个像素的颜色值正好存储在 1 个字节(8 位)中。因此,如果从显存的开头(地址A000h)开始,像素是线性存储的,可以直接写入它们的颜色值。写入像素 (x, y) 的公式为:
A000h + (y * 320) + x
.正如您在原始代码中所做的那样,您可以通过将
ES
的设置提升到调用方并将ES == 0A000h
作为PlotPixel
函数的前提条件来进一步改进.但是我确实对您的原始代码进行了重大更改,用左移和一些加法替换了缓慢的乘法 (MUL
)。您原来的基于乘法的代码也有一个溢出错误,我的重写修复了这个错误。您可以通过一次写入多个字节来进一步加快速度——在 8086 上,这将是写入一个 WORD(两个字节)。这将需要 "inlining" 直接在
DrawCircle
函数中使用像素绘图代码,并进行一些创造性的寄存器分配以确保您要绘制的第一个像素位于AL
中,并且第二个像素在AH
中。我会把它留作练习。
* 我喜欢使用宏将这些偏移量转换为常量,然后在整个函数中使用该符号名称,而不是对数值进行硬编码。这不仅使代码更具可读性,而且如果您决定更改推送参数的顺序或推送的参数数量,还可以更轻松地进行更改。我没有在这里这样做,因为我不知道 TASM 的正确语法是什么,并且在调试这段代码时它咬了我。
说起来,我写了代码在 NASM 中并尝试即时翻译成 TASM,这是我不太熟悉的一种语法。对不起,如果有任何小的语法问题你必须先解决才能 assemble 它!