8086 程序集:将变量区放在代码段的开头
8086 Assembly: put variables area at beginning of code segment
我有一个代码段位于 boot.img 文件的第 37 扇区,在我的主引导记录中我将这段代码加载到内存 0x5678:0x1234,这是我的 asm 代码:
[BITS 16] ;Set code generation to 16 bit mode
ORG 0x1234 ;set addressing to begin at 0x5678:0x1234
mov ax, cs
mov ds, ax
mov es, ax
call cls ;call routine to clear screen
call dspmsg ;call routine to display message
startdt:
call date
call cvtmo
call cvtday
call cvtcent
call cvtyear
call dspdate
call time
call cvthrs
call cvtmin
call cvtsec
call dsptime
jmp startdt ;use infinite loop to halt?
cls:
mov ah,06h ;function 06h (Scroll Screen)
mov al, 0 ;scroll all lines
mov bh,0x0a ;Attribute (light green on black)
mov ch,0 ;Upper left row is zero
mov cl,0 ;Upper left column is zero
mov dh,24 ;Lower left row is 24
mov dl,79 ;Lower left column is 79
int 10H ;BIOS Interrupt 10h (video services)
ret
dspmsg:
mov ah,13h ;function 13h (Display String)
mov al,0 ;Write mode is zero
mov bh,0 ;Use video page of zero
mov bl,0x0c ;Attribute (light red on black)
mov cx,msglen ;Character string is 25 long
mov dh,3 ;position on row 3
mov dl,[center] ;and column 28
lea bp,[msg] ;load the offset address of string into BP
int 10H
ret
msg: db 'Pradox V 0.1 Jiansong He',10,13
msglen: equ $-msg
int 10H
ret
date:
;Get date from the system
mov ah,04h ;function 04h (get RTC date)
int 1Ah ;BIOS Interrupt 1Ah (Read Real Time Clock)
ret
;CH - Century
;CL - Year
;DH - Month
;DL - Day
cvtmo:
;Converts the system date from BCD to ASCII
mov bh,dh ;copy contents of month (dh) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld],bh
mov bh,dh
and bh,0fh
add bh,30h
mov [dtfld + 1],bh
ret
cvtday:
mov bh,dl ;copy contents of day (dl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 3],bh
mov bh,dl
and bh,0fh
add bh,30h
mov [dtfld + 4],bh
ret
cvtcent:
mov bh,ch ;copy contents of century (ch) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 6],bh
mov bh,ch
and bh,0fh
add bh,30h
mov [dtfld + 7],bh
ret
cvtyear:
mov bh,cl ;copy contents of year (cl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 8],bh
mov bh,cl
and bh,0fh
add bh,30h
mov [dtfld + 9],bh
ret
dtfld: db '00/00/0000'
dspdate:
;Display the system date
mov ah,13h ;function 13h (Display String)
mov al,0 ;Write mode is zero
mov bh,0 ;Use video page of zero
mov bl,0x0a;Attribute
mov cx,10 ;Character string is 10 long
mov dh,4 ;position on row 4
mov dl,[center] ;and column 28
push ds ;put ds register on stack
pop es ;pop it into es register
lea bp,[dtfld] ;load the offset address of string into BP
int 10H
ret
time:
;Get time from the system
mov ah,02h
int 1Ah
ret
;CH - Hours
;CL - Minutes
;DH - Seconds
cvthrs:
;Converts the system time from BCD to ASCII
mov bh,ch ;copy contents of hours (ch) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld],bh
mov bh,ch
and bh,0fh
add bh,30h
mov [tmfld + 1],bh
ret
cvtmin:
mov bh,cl ;copy contents of minutes (cl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld + 3],bh
mov bh,cl
and bh,0fh
add bh,30h
mov [tmfld + 4],bh
ret
cvtsec:
mov bh,dh ;copy contents of seconds (dh) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld + 6],bh
mov bh,dh
and bh,0fh
add bh,30h
mov [tmfld + 7],bh
ret
tmfld: db '00:00:00'
dsptime:
;Display the system time
mov ah,13h ;function 13h (Display String)
mov al,1 ;Write mode is zero
mov bh,0 ;Use video page of zero
mov bl,0x0a;Attribute
mov cx,8 ;Character string is 8 long
mov dh,5 ;position on row 5
mov dl,[center];and column 0
push ds ;put ds register on stack
pop es ;pop it into es register
lea bp,[tmfld] ;load the offset address of string into BP
int 10H
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;variables;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
center: db 25
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;end variables;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
我的模拟器是dosbox,操作系统是lubuntu 运行在Oracle VM virtual box上,我自己的操作系统是win8 x64
请注意文件末尾的变量字段,如果我将此文件放在顶部,就在我更新 DS 和 ES 寄存器之后,我遇到了一个问题:
cls 和 dspmsg 子例程将不起作用
但是,如果我将值更改为 center: db 30
或者只是将此字段放在我的代码末尾,它就会起作用。
有人可以解释为什么将变量字段放在我的代码之上并更改标签的值会影响程序的性能吗?这与我的段寄存器有关吗?
这是我的装载机:
;bit16 ; 16bit by default
org 0x7c00
jmp short start
nop
bsOEM db "OS423 v.0.1" ; OEM String
start:
;;load sector into memory & 5678h:1234h
mov bx, 0x5678 ;segmented address
mov es, bx ;move segemented address to es
mov bx,0x1234 ;base address to bx
mov ah, 02 ;function read sectors
mov al, 01 ;# of sectors to load
mov ch, 00 ;track to read
mov cl, 02 ;sector to read
mov dh, 00 ;head to read
mov dl, 00 ;drive number
int 0x13 ;call interrupt 13
jmp 0x5678:0x1234 ;jump to memory address
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;variables;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
msg: db 'Welcome to Pradox OS 0.1! Authored by Jiansong he', 10, 13, '$'
mlen equ $-msg
padding times 510-($-$$) db 0 ;to make MBR 512 bytes
bootSig db 0x55, 0xaa ;signature (optional)
正如 Jester 所提到的(以及我在上一个问题中提到的),我强烈建议使用调试器。该评论是:
I'd recommend BOCHS for debugging bootloaders. It has a command line debugger that understands real mode addressing and can be used to watch instructions as they are executed,set breakpoints, display registers, examine memory etc.
如果您想查看将 center: db 25
放在代码顶部时发生的情况,您可以使用 NDISASM 转储已编码的指令。假设您的第二阶段仍然是 dt.bin
,您可以使用此命令获取该二进制文件的 disassembled 输出:
ndisasm -b16 dt.bin
这条指令disassembled dt.bin
假设是16位指令。如果你将 center: db 25
放在 dt.asm
和 assembled dt.bin 和 运行 上面的命令顶部的代码之前,你会得到这样的东西输出:
00000000 198CC88E sbb [si-0x7138],cx
00000004 D88EC0E8 fmul dword [bp-0x1740]
00000008 2600E8 es add al,ch
0000000B 3400 xor al,0x0
0000000D E86600 call word 0x76
这不是您期望的代码!那个值 25 在哪里?输出为十六进制。小数点后 25 位是 0x19。如果你检查第一条指令解码 198CC88E
你会看到 19
是第一个字节。那个单字节把指令解码变成了完全的废话。
如果将 center: db 25
更改为 center: db 30
会怎样?如果你尝试 assemble 它并再次使用上面的 NDISASM 命令你应该看到这个:
00000000 1E push ds
00000001 8CC8 mov ax,cs
00000003 8ED8 mov ds,ax
00000005 8EC0 mov es,ax
00000007 E82600 call word 0x30
30 十进制为 0x1e。正如杰斯特指出的那样,0x1e 本身就是一条指令 push ds
。该单字节指令做了一些无用的事情,但之后的所有指令都如您所料。像这样使用 -f bin
和 NASM 输出到二进制文件时将数据放在代码上方可能会导致异常问题。出于这个原因,最好将数据放在不会干扰指令解码的代码之后。
我有一个代码段位于 boot.img 文件的第 37 扇区,在我的主引导记录中我将这段代码加载到内存 0x5678:0x1234,这是我的 asm 代码:
[BITS 16] ;Set code generation to 16 bit mode
ORG 0x1234 ;set addressing to begin at 0x5678:0x1234
mov ax, cs
mov ds, ax
mov es, ax
call cls ;call routine to clear screen
call dspmsg ;call routine to display message
startdt:
call date
call cvtmo
call cvtday
call cvtcent
call cvtyear
call dspdate
call time
call cvthrs
call cvtmin
call cvtsec
call dsptime
jmp startdt ;use infinite loop to halt?
cls:
mov ah,06h ;function 06h (Scroll Screen)
mov al, 0 ;scroll all lines
mov bh,0x0a ;Attribute (light green on black)
mov ch,0 ;Upper left row is zero
mov cl,0 ;Upper left column is zero
mov dh,24 ;Lower left row is 24
mov dl,79 ;Lower left column is 79
int 10H ;BIOS Interrupt 10h (video services)
ret
dspmsg:
mov ah,13h ;function 13h (Display String)
mov al,0 ;Write mode is zero
mov bh,0 ;Use video page of zero
mov bl,0x0c ;Attribute (light red on black)
mov cx,msglen ;Character string is 25 long
mov dh,3 ;position on row 3
mov dl,[center] ;and column 28
lea bp,[msg] ;load the offset address of string into BP
int 10H
ret
msg: db 'Pradox V 0.1 Jiansong He',10,13
msglen: equ $-msg
int 10H
ret
date:
;Get date from the system
mov ah,04h ;function 04h (get RTC date)
int 1Ah ;BIOS Interrupt 1Ah (Read Real Time Clock)
ret
;CH - Century
;CL - Year
;DH - Month
;DL - Day
cvtmo:
;Converts the system date from BCD to ASCII
mov bh,dh ;copy contents of month (dh) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld],bh
mov bh,dh
and bh,0fh
add bh,30h
mov [dtfld + 1],bh
ret
cvtday:
mov bh,dl ;copy contents of day (dl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 3],bh
mov bh,dl
and bh,0fh
add bh,30h
mov [dtfld + 4],bh
ret
cvtcent:
mov bh,ch ;copy contents of century (ch) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 6],bh
mov bh,ch
and bh,0fh
add bh,30h
mov [dtfld + 7],bh
ret
cvtyear:
mov bh,cl ;copy contents of year (cl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 8],bh
mov bh,cl
and bh,0fh
add bh,30h
mov [dtfld + 9],bh
ret
dtfld: db '00/00/0000'
dspdate:
;Display the system date
mov ah,13h ;function 13h (Display String)
mov al,0 ;Write mode is zero
mov bh,0 ;Use video page of zero
mov bl,0x0a;Attribute
mov cx,10 ;Character string is 10 long
mov dh,4 ;position on row 4
mov dl,[center] ;and column 28
push ds ;put ds register on stack
pop es ;pop it into es register
lea bp,[dtfld] ;load the offset address of string into BP
int 10H
ret
time:
;Get time from the system
mov ah,02h
int 1Ah
ret
;CH - Hours
;CL - Minutes
;DH - Seconds
cvthrs:
;Converts the system time from BCD to ASCII
mov bh,ch ;copy contents of hours (ch) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld],bh
mov bh,ch
and bh,0fh
add bh,30h
mov [tmfld + 1],bh
ret
cvtmin:
mov bh,cl ;copy contents of minutes (cl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld + 3],bh
mov bh,cl
and bh,0fh
add bh,30h
mov [tmfld + 4],bh
ret
cvtsec:
mov bh,dh ;copy contents of seconds (dh) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld + 6],bh
mov bh,dh
and bh,0fh
add bh,30h
mov [tmfld + 7],bh
ret
tmfld: db '00:00:00'
dsptime:
;Display the system time
mov ah,13h ;function 13h (Display String)
mov al,1 ;Write mode is zero
mov bh,0 ;Use video page of zero
mov bl,0x0a;Attribute
mov cx,8 ;Character string is 8 long
mov dh,5 ;position on row 5
mov dl,[center];and column 0
push ds ;put ds register on stack
pop es ;pop it into es register
lea bp,[tmfld] ;load the offset address of string into BP
int 10H
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;variables;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
center: db 25
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;end variables;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
我的模拟器是dosbox,操作系统是lubuntu 运行在Oracle VM virtual box上,我自己的操作系统是win8 x64
请注意文件末尾的变量字段,如果我将此文件放在顶部,就在我更新 DS 和 ES 寄存器之后,我遇到了一个问题:
cls 和 dspmsg 子例程将不起作用
但是,如果我将值更改为 center: db 30
或者只是将此字段放在我的代码末尾,它就会起作用。
有人可以解释为什么将变量字段放在我的代码之上并更改标签的值会影响程序的性能吗?这与我的段寄存器有关吗?
这是我的装载机:
;bit16 ; 16bit by default
org 0x7c00
jmp short start
nop
bsOEM db "OS423 v.0.1" ; OEM String
start:
;;load sector into memory & 5678h:1234h
mov bx, 0x5678 ;segmented address
mov es, bx ;move segemented address to es
mov bx,0x1234 ;base address to bx
mov ah, 02 ;function read sectors
mov al, 01 ;# of sectors to load
mov ch, 00 ;track to read
mov cl, 02 ;sector to read
mov dh, 00 ;head to read
mov dl, 00 ;drive number
int 0x13 ;call interrupt 13
jmp 0x5678:0x1234 ;jump to memory address
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;variables;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
msg: db 'Welcome to Pradox OS 0.1! Authored by Jiansong he', 10, 13, '$'
mlen equ $-msg
padding times 510-($-$$) db 0 ;to make MBR 512 bytes
bootSig db 0x55, 0xaa ;signature (optional)
正如 Jester 所提到的(以及我在上一个问题中提到的),我强烈建议使用调试器。该评论是:
I'd recommend BOCHS for debugging bootloaders. It has a command line debugger that understands real mode addressing and can be used to watch instructions as they are executed,set breakpoints, display registers, examine memory etc.
如果您想查看将 center: db 25
放在代码顶部时发生的情况,您可以使用 NDISASM 转储已编码的指令。假设您的第二阶段仍然是 dt.bin
,您可以使用此命令获取该二进制文件的 disassembled 输出:
ndisasm -b16 dt.bin
这条指令disassembled dt.bin
假设是16位指令。如果你将 center: db 25
放在 dt.asm
和 assembled dt.bin 和 运行 上面的命令顶部的代码之前,你会得到这样的东西输出:
00000000 198CC88E sbb [si-0x7138],cx 00000004 D88EC0E8 fmul dword [bp-0x1740] 00000008 2600E8 es add al,ch 0000000B 3400 xor al,0x0 0000000D E86600 call word 0x76
这不是您期望的代码!那个值 25 在哪里?输出为十六进制。小数点后 25 位是 0x19。如果你检查第一条指令解码 198CC88E
你会看到 19
是第一个字节。那个单字节把指令解码变成了完全的废话。
如果将 center: db 25
更改为 center: db 30
会怎样?如果你尝试 assemble 它并再次使用上面的 NDISASM 命令你应该看到这个:
00000000 1E push ds 00000001 8CC8 mov ax,cs 00000003 8ED8 mov ds,ax 00000005 8EC0 mov es,ax 00000007 E82600 call word 0x30
30 十进制为 0x1e。正如杰斯特指出的那样,0x1e 本身就是一条指令 push ds
。该单字节指令做了一些无用的事情,但之后的所有指令都如您所料。像这样使用 -f bin
和 NASM 输出到二进制文件时将数据放在代码上方可能会导致异常问题。出于这个原因,最好将数据放在不会干扰指令解码的代码之后。