渲染出的曼德布罗特集合与老师展示的不一致,我找不到差异在哪儿
我们在DOSBox里使用8086软件包,做一个小型的汇编语言练习。任务是在DOSBox的屏幕上显示曼德尔布罗集合的一个近似,按下面的指令实现:
我们不允许使用栈,使用定点小数,比例尺为64。屏幕上的每个格子(25行 x 80列)被赋予一个复数点 c = c_x + i * c_y,使得 c_x = -144 + col * 3,其中 0 <= col <= 79,并且 c_y = -72 + row * 6,其中 0 <= row <= 24。
对于屏幕上的每个像元,我们递归计算
z_{n+1} = z_{n} ^ 2 + c
可以简化为
x_new = x ^ 2 / 64 + y ^ 2 / 64 + c_x
和
y_new = x * y / 32 + c_y
并且对于每个点需要执行最多16次迭代。
我们的逃逸条件是 x^2/64 + y^2/64 > 4*64。
- 如果在1-3次迭代内逃逸,则输出012Eh。
- 如果在4-7次迭代内逃逸,则输出042Ah。
- 如果在8-15次迭代内逃逸,则输出0EDBh。
- 如果在16次迭代后仍未逃逸,则输出0ADBh。
现在讲师还附上了一张应该的样子图片:

这是我的结果:

你可以看到它有点不一样,我也不明白为什么。我相信我的实现是正确的,但我无法调试,因为我尝试在DOSBox里使用CV调试器,但我不知道怎么用,而且也在自学中,但仍然学不会。
我尝试对几个点手动运行,似乎也是正确的。
以下是我的完整汇编代码(请对我不过分挑剔优化之类的,毕竟是第二次作业):
.model small
.stack 100h
.data
; We store our variables in memory because the 8086 processor
; does not have enough registers to hold everything at once.
c_x dw 0
c_y dw 0
x dw 0
y dw 0
x_sqr dw 0
y_sqr dw 0
row_cnt dw 0
col_cnt dw 0
iter dw 0
.code
START:
; clearing the dos screen
mov ax, 0003h
int 10h
; writing to the data segment our screen coordinates for the horizontal axis and setting di=0
mov ax, @data
mov ds, ax
mov ax, 0B800h
mov es, ax
mov di, 0
; setting up the row loop
mov c_y, -72
mov row_cnt, 25
ROW_LOOP:
; setting up the colum loop
mov c_x, -144
mov col_cnt, 80
COL_LOOP:
; initial values of x,y from z_0
mov x, 0
mov y, 0
mov iter, 1
MANDEL_LOOP:
; calculating x^2/64
mov ax, x
imul x ; DX:AX = x * x
mov bx, 64
idiv bx ; AX = (DX:AX) / 64
mov x_sqr, ax
; calculating y^2/64
mov ax, y
imul y ; DX:AX = y * y
mov bx, 64
idiv bx ; AX = (DX:AX) / 64
mov y_sqr, ax
; escape condition
mov bx, x_sqr
add bx, y_sqr
cmp bx, 256
jg DRAW_PIXEL
; compare iter to see the iter num of the iterated calc of z_n
cmp iter, 16
je DRAW_PIXEL
; calc saving y_new = (xy) / 32 + c_y
mov ax, x
imul y ; DX:AX = x * y
mov bx, 32
idiv bx ; AX = (DX:AX) / 32
add ax, c_y
mov y, ax
; calc saving x_new = x_sqr - y_sqr + c_x
mov ax, x_sqr
sub ax, y_sqr
add ax, c_x
mov x, ax
inc iter
jmp MANDEL_LOOP
WRITE_VRAM:
; printing to the screen the char
mov es:[di], ax
add di, 2 ; move forward 2 bytes in video memory
; moving to next colum
add c_x, 3
dec col_cnt
jz COL_DONE
jmp COL_LOOP
COL_DONE:
; moving to next row
add c_y, 6
dec row_cnt
jz ROW_DONE
jmp ROW_LOOP
ROW_DONE:
jmp EXIT
DRAW_PIXEL:
; assigning the printed char based of iter value
cmp iter, 3
jle COL_1_3
cmp iter, 7
jle COL_4_7
cmp iter, 15
jle COL_8_15
; if it didnt jump, iter is 16
mov ax, 0ADBh
jmp WRITE_VRAM
COL_1_3:
mov ax, 012Eh
jmp WRITE_VRAM
COL_4_7:
mov ax, 042Ah
jmp WRITE_VRAM
COL_8_15:
mov ax, 0EDBh
jmp WRITE_VRAM
EXIT:
; moving the command line to the bottom and exiting the program
mov ah, 02h
mov bh, 00h
mov dh, 18h
mov dl, 00h
int 10h
mov ah, 04Ch
int 21h
END START
解决方案
; escape condition mov bx, x_sqr add bx, y_sqr cmp bx, 256 jg DRAW_PIXEL
- 你需要一个额外的
jc,因为加法可能会产生进位。 - 你需要一个
ja,因为和可能超过32767。无论如何它始终是一个正数。
我把你的程序改写成一个在DOSBox上可以运行的.COM可执行文件。我为了与我最近关于Mandelbrot的 codegolf答案 的思路保持相似,调整了运算的顺序。我探索了多种提高计算精度的方法(包括四舍五入到最近值以及推迟进行简化运算),但最终似乎都没什么影响!通过调整迭代计数器(以及它对颜色分配器的影响)的尝试终于得到一个不错的结果。虽然它仍然不是直接拷贝你老师图片的版本,但我也没指望会完全一样(也许你也不应该指望)。
ORG 256
mov ax, 0003h
int 10h
mov ax, 0B800h
mov es, ax
xor di, di
; setting up the row loop -72 To +72 Step 6
mov word [c_y], -72 ; [-1.125,+1.125]
mov word [row_cnt], 25
ROW_LOOP:
; setting up the column loop -144 To +93 Step 3
mov word [c_x], -144 ; [-2.250,+1.453]
mov word [col_cnt], 80
COL_LOOP:
; initial values of x,y from z_0
xor ax, ax
mov [iter], ax
mov [x], ax
mov [y], ax
mov [xx], ax
mov [yy], ax
mov cx, 64
MANDEL_LOOP:
; calc saving y_new = (2xy) + c_y
mov ax, [x]
imul word [y]
add ax, ax
adc dx, dx
idiv cx
add ax, [c_y]
mov [y], ax
; calc saving x_new = x^2 - y^2 + c_x
mov ax, [xx]
sub ax, [yy]
add ax, [c_x]
mov [x], ax
; calculating x^2 + y^2
mov ax, [y]
imul ax
idiv cx
mov [yy], ax
mov ax, [x]
imul ax
idiv cx
mov [xx], ax
add ax, [yy]
; escape condition
jc DRAW_PIXEL
cmp ax, 4*64
ja DRAW_PIXEL
; compare iter to see the iter num of the iterated calc of z_n
inc word [iter]
cmp word [iter], 16
jb MANDEL_LOOP
DRAW_PIXEL:
; assigning the printed char based of iter value
mov ax, 092Eh
cmp word [iter], 2
jbe WRITE_VRAM
mov ax, 042Ah
cmp word [iter], 6
jbe WRITE_VRAM
mov ax, 0EDBh
cmp word [iter], 14
jbe WRITE_VRAM
mov ah, 0Ah
WRITE_VRAM:
stosw
; moving to next colum
add word [c_x], 3
dec word [col_cnt]
jz COL_DONE
jmp COL_LOOP
COL_DONE:
; moving to next row
add word [c_y], 6
dec word [row_cnt]
jz ROW_DONE
jmp ROW_LOOP
ROW_DONE:
EXIT:
; moving the command line to the bottom and exiting the program
mov ah, 02h
mov bh, 00h
mov dh, 18h
mov dl, 00h
int 10h
mov ah, 00h
int 16h
mov ah, 4Ch
int 21h
c_x: dw 0
c_y: dw 0
x: dw 0
y: dw 0
xx: dw 0
yy: dw 0
row_cnt: dw 0
col_cnt: dw 0
iter: dw 0
