Debugging 错误:应为标量类型

Debugging 错误:应为标量类型,debugging,assembly,Debugging,Assembly,我想用汇编语言完成一个程序,希望它能输出[00h,0FFh]中的所有ASCII字符和十六进制ASCII的表格格式。但是发生了错误,我尝试了很多次,但都失败了。 它显示除了标量类型之外,,但我不知道如何处理它。 这是代码,“mov di,col*8”中存在错误: 另一个问题是我不知道如何输出s(十六进制ASCII)中的元素,你能给我一些建议吗?从语法上我猜这是MASM/TASM语法 在这种情况下,mov di,col*8是非常虚假的 col是内存“变量”(更准确地说,col是“汇编符号”,它的值是

我想用汇编语言完成一个程序,希望它能输出[00h,0FFh]中的所有ASCII字符和十六进制ASCII的表格格式。但是发生了错误,我尝试了很多次,但都失败了。 它显示除了标量类型之外,,但我不知道如何处理它。 这是代码,“mov di,col*8”中存在错误:


另一个问题是我不知道如何输出s(十六进制ASCII)中的元素,你能给我一些建议吗?

从语法上我猜这是MASM/TASM语法

在这种情况下,
mov di,col*8
是非常虚假的

col
是内存“变量”(更准确地说,
col
是“汇编符号”,它的值是在
col
标签之后定义的内存的第一个字节的内存地址,在您的情况下,它由
dw 0
指令=>定义,产生两个具有零值的字节)

然后将
mov di,col
组装成
mov r16,r/m16
的变体,或以适当的英特尔语法组装成
mov di,[col]
,其中
col
表示16位整数值为
col
内存地址的
OFFSET
部分。默认情况下,地址的段部分取自
ds
,如果您确实正确地提前设置了
ds
,则
mov
将到达
dw
保留的内存

mov di,col*8
是不明确的,您可以将其组合为
mov di,[col*8]
,这意味着从
(OFFSET col)*8
address=获取内存不是您想要的,或者将
mov di,[col]*8
作为算术运算,将从内存获取的值相乘,而x86 CPU没有这样的指令。您需要分别对值进行算术运算,如:

mov    di,[col]
shl    di,3      ; di = di * 8
mov
指令中的任何“类算术”运算都是与内存地址相关的算术运算,如
mov eax、[ebx+esi*8]
在32b模式下有效,这意味着首先计算要获取的内存地址。x86 CPU系列的
mov
指令中没有内置值本身的算术运算,除了
movsx/movzx
指令,它们对值进行符号/零扩展。我想不起还有什么例外。类似于
lodsb
的“string”指令系列在获取后调整指针值,但这不是调整值本身,而是调整指针

您可以利用内存地址算术CPU模块,使用
lea
指令为您计算一些具有值的算术,而不是使用真实内存偏移量进行计算


当然,在您的特定代码中,将值保存在某个寄存器中更为理想,只需执行
添加xxx,8
来更新它,使其始终处于预乘状态,并且不在内存中存储/读取任何内容。当您无法将所有内容都放入寄存器时,仅将内存用作最后手段


编辑:在进一步阅读你的源代码之后

您可能应该在许多较小的步骤中编写代码,在每一步之后保持代码运行并进行调试

目前,您正在将直接视频ram写入与
int21h
输出例程混合,这对您没有好处,请选择一个

当您想要显示所有扩展ASCII字符时,直接写入是唯一的选择(因为DOS将从显示中读取10、13和其他控制代码值,并为它们执行特殊操作,如移动光标(新行、制表符)、蜂鸣音等)

若你们想要256个符号,那个么16x16表就很有意义了

所以,也许您应该开始编写代码,在单行中显示前16个扩展ASCII码。比如:

code segment
assume cs:code, ds:code
main:
    jmp begin
; reserved for data in future

; top left corner of the table at [0, 4] position
TABLE_LEFT_TOP_OFFSET   equ     (4*80*2)
; the table will be 16x16, each element being 5x1
; total size = 80x16 = (will fit 80x25 text mode screen)
; element 5 chars as:
; symbol, space, first hex digit, second hex digit, space

begin:
    mov     ax,0B800h
    mov     es,ax       ; es = text mode VRAM segment

; display 16 symbols (single row) on proper positions
    mov     di,TABLE_LEFT_TOP_OFFSET ; di = starting VRAM adr
    xor     dl,dl   ; symbol value = 0
    mov     cx,16   ; loop counter
row_loop:
    ; show symbol + space
    mov     al,dl   ; symbol ASCII
    mov     ah,4h   ; colour attribute
    mov     es:[di],ax      ; write symbol
    mov     al,' '
    mov     es:[di+2],ax    ; write space after it
    ; advance loop for next symbol
    inc     dl
    add     di,5*2  ; move 5 chars forward
    dec     cx
    jnz     row_loop

    ; exit back to DOS
    xor     ah,ah   ; ah = 0 - wait for key service
    int     16h
    int     20h     ; terminate to DOS

code ends
end main
在您验证此功能是否按预期工作(显示一行16个符号)后,您可以扩展它。。。与在开始时添加文本模式设置一样,“清除屏幕”并确认您处于预期模式:

begin:
    mov     ax,3        ; ah = 0 (set gfx mode), al = 3
    int     10h         ; set VGA text mode 80x25 chars
编译+运行+验证它是否工作

然后可以添加16行循环以显示所有256个符号:

    ...
    xor     dl,dl   ; symbol value = 0
rows_loop:
    mov     cx,16   ; loop counter
    ...

。。。再次运行并验证它是否工作。。。然后可以添加十六进制显示。正如您所知,您将只执行0-255个值,您可以完全避免推送/弹出/循环,只需执行简单的两位数转换:

    ...
    mov     es:[di+2],ax    ; write space after it

    ; show hexadecimal value of symbol - first digit
    mov     al,dl
    shr     al,4    ; al = upper 4 bits of dl (first hex digit)
    add     al,'0'
    cmp     al,'9'
    jbe     first_hex_digit_ok
    add     al,('A'-'0'-10) ; adjust it to A-F letter if needed
first_hex_digit_ok:
    mov     es:[di+4],ax    ; write first hex digit
    ; show hexadecimal value of symbol - second digit
    mov     al,dl
    and     al,0Fh  ; al = lower 4 bits of dl (second hex digit)
    add     al,'0'
    cmp     al,'9'
    jbe     second_hex_digit_ok
    add     al,('A'-'0'-10) ; adjust it to A-F letter if needed
second_hex_digit_ok:
    mov     es:[di+6],ax    ; write second hex digit
    ; write space after that
    mov     al,' '
    mov     es:[di+8],ax    ; write space after it

    ; advance loop for next symbol
    inc     dl
    ...
调试+验证它是否工作(我实际上没有这样做,所以我只希望我的代码能工作)

然后,您可以查看生成的代码,您可能会注意到所有的写入操作都类似于连续执行
es:[di]=word value
,因此您可以使用
stosw
来代替
di+?
置换和
add di,5*2
,就像第一次尝试后优化的代码一样(加上我为十六进制值添加了修改后的颜色):

最后,您可能会注意到,行和行循环在行尾的情况下没有做任何特殊的事情(因为单行填满了行的全部80个字符,所以它“自动”前进到下一行),所以您可以完全摆脱
cx
计数器,只需执行单个256符号循环:

    ...
    xor     dl,dl   ; symbol value = 0
symbol_loop:
    ; show symbol + space
    ...

    ...
    stosw           ; write space after it
    ; advance loop for next symbol
    inc     dl
    jnz     symbol_loop ; until all 256 symbols are displayed
    ; exit back to DOS
    ...
就这样。我没有验证我的代码,所以如果出现一些错误,我很抱歉,但我试图让你展示我在编写asm代码时的想法。。。实际上,做六位数的步骤太大了,我可能只会先做第一位数,验证
+'0'
+'A'-'0'-10
是否按预期工作,然后我会添加第二位数。另外,由于我对代码在最终版本中的结局有着非常清晰的了解(由于我的x86 asm经验),我的许多寄存器使用选择,如写入偏移量的
es:di
,等等。。可能看起来非常幸运,导致以后“免费”编写更简单的代码。如果您是x86汇编的新手,希望在初始版本的代码之后找到更好的想法,并且毫不犹豫地覆盖更多,只需将开发步骤保持在足够小的范围内,以便一次只调试并验证很少的更改


使用当前代码,您将遇到另一个问题:

    mov byte ptr es:[di+2],s[0]
没有
mov mem,mem    ...
; display 16 symbols (single row) on proper positions
    mov     di,TABLE_LEFT_TOP_OFFSET ; di = starting VRAM adr
    xor     dl,dl   ; symbol value = 0
rows_loop:
    mov     cx,16   ; loop counter
row_loop:
    ; show symbol + space
    mov     al,dl   ; symbol ASCII
    mov     ah,4h   ; colour attribute for symbol
    stosw           ; write symbol
    mov     al,' '
    stosw           ; write space after it
    ; show hexadecimal value of symbol - first digit
    mov     ah,2h   ; colour attribute for hex value
    mov     al,dl
    shr     al,4    ; al = upper 4 bits of dl (first hex digit)
    add     al,'0'
    cmp     al,'9'
    jbe     first_hex_digit_ok
    add     al,('A'-'0'-10) ; adjust it to A-F letter if needed
first_hex_digit_ok:
    stosw           ; write first hex digit
    ; show hexadecimal value of symbol - second digit
    mov     al,dl
    and     al,0Fh  ; al = lower 4 bits of dl (second hex digit)
    add     al,'0'
    cmp     al,'9'
    jbe     second_hex_digit_ok
    add     al,('A'-'0'-10) ; adjust it to A-F letter if needed
second_hex_digit_ok:
    stosw           ; write second hex digit
    ; write space after that
    mov     al,' '
    stosw           ; write space after it
    ; advance loop for next symbol
    inc     dl
    dec     cx
    jnz     row_loop
    ; both dl and di are ready for next line
    ; so all is needed is just to loop until dl==0
    test    dl,dl   ; until all 256 symbols were displayed
    jnz     rows_loop
    ...
    ...
    xor     dl,dl   ; symbol value = 0
symbol_loop:
    ; show symbol + space
    ...

    ...
    stosw           ; write space after it
    ; advance loop for next symbol
    inc     dl
    jnz     symbol_loop ; until all 256 symbols are displayed
    ; exit back to DOS
    ...
    mov byte ptr es:[di+2],s[0]
    mov al,s[0]
    mov es:[di+2],al