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