Assembly 8086汇编:将变量区域放在代码段的开头

Assembly 8086汇编:将变量区域放在代码段的开头,assembly,x86,x86-16,osdev,dosbox,Assembly,X86,X86 16,Osdev,Dosbox,我有一个代码段,位于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 rout

我有一个代码段,位于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(和我在最后一个问题中提到的)我强烈建议使用调试器。该评论是:

我建议调试引导加载程序。它有一个命令行调试器,可以理解实模式寻址,并可用于在指令执行时监视指令、设置断点、显示寄存器、检查内存等

如果希望看到在代码顶部放置
center:db 25
时发生的情况,可以使用NDISASM转储已编码的指令。假设您的第二阶段仍然是
dt.bin
,您可以使用此命令获得该二进制文件的反汇编输出:

ndisasm -b16 dt.bin
该命令对dt.bin进行反汇编,假设它是16位指令。如果您将
center:db 25
放在
dt.asm
顶部的代码之前,并将dt.bin组合起来,然后运行上面的命令,您将得到如下输出:

这不是你期望的代码!25的数值在哪里?输出为十六进制。那个25位小数是0x19。如果您检查第一条指令解码
198CC88E
,您将看到
19
是第一个字节。这一个字节将指令解码变成了完全的无意义

如果将
center:db 25
更改为
center:db 30
,会发生什么情况?如果您尝试组装它并再次使用上面的ndisam命令,您应该会看到:


30十进制是0x1e。正如Jester指出的那样,0x1e本身就是一条指令
push-ds
。这个单字节指令做了一些无用的事情,但之后的所有指令都是您所期望的。使用NASM的
-f bin
将数据输出到这样的二进制文件时,将数据置于代码上方可能会导致异常问题。因此,最好将数据放在代码后面,这样不会干扰指令解码。

可能您未显示的加载程序仍将控制权转移到
0x5678:0x1234
,该加载程序将包含您的数据,而不是要执行的代码。请注意,
30
恰好是
PUSH-DS
的操作码,它仍然可以让您的代码正确执行。PS:学习使用调试器。明白了,再次感谢!您知道您可以直接在Windows下运行DOSBox。
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
00000000  1E                push ds
00000001  8CC8              mov ax,cs
00000003  8ED8              mov ds,ax
00000005  8EC0              mov es,ax
00000007  E82600            call word 0x30