vak*_*kus 15 assembly virtualbox nasm bootloader x86-16
我正在编写小型操作系统 - 用于练习.我从bootloader开始.
我想创建一个以16位实模式运行的小命令系统(现在).
我创建了重置驱动器的bootloader,然后在bootloader之后加载扇区.
问题是因为jmp
功能后没有任何实际发生.
我不想尝试在0x7E00加载下一个扇区(我不完全确定如何使用es:bx指向地址,这可能是一个问题,我相信它的地址:偏移),就在引导加载程序之后.
这是代码:
;
; SECTOR 0x0
;
;dl is number of harddrive where is bootloader
org 0x7C00
bits 16
;reset hard drive
xor ah,ah
int 0x13
;read sectors
clc
mov bx,0x7E00
mov es,bx
xor bx,bx
mov ah,0x02 ;function
mov al,0x1 ;sectors to read
mov ch,0x0 ;tracks
mov cl,0x1 ;sector
mov dh,0x0 ;head
int 0x13
;if not readed jmp to error
jc error
;jump to 0x7E00 - executed only if loaded
jmp 0x7E00
error:
mov si,MSGError
.loop:
lodsb
or al,al
jz .end
mov ah,0x0E
int 0x10
jmp .loop
.end:
hlt
MSGError db "Error while booting", 0x0
times 0x1FE - ($ - $$) db 0x0
db 0x55
db 0xAA
;
; SECTOR 0x1
;
jmp printtest
;definitions
MSGLoaded db "Execution successful", 0x0
;
; Print function
; si - message to pring (NEED TO BE FINISHED WITH 0x0)
printtest:
mov si,MSGLoaded
.loop:
lodsb
or al,al
jz .end
mov ah,0x0E
int 0x10
jmp .loop
.end:
hlt
times 0x400 - ($-$$) db 0x0
Run Code Online (Sandbox Code Playgroud)
我一直在使用VirtualBox测试此代码,但实际上没有发生任何事情,读取错误没有显示,以及应该打印的消息.
Mic*_*tch 49
这段代码的主要问题是:
第一个是在这段代码中:
mov bx,0x7E00
mov es,bx
xor bx,bx
Run Code Online (Sandbox Code Playgroud)
问题是要将磁盘扇区加载到0x0000:0x7E00
(ES:BX).此代码将ES:BX设置为0x7E00:0x0000
解析为0x7E000
((0x7E00 << 4)+ 0x0000)的物理地址.我认为目的是加载0x07E0
到ES中,这将产生0x7E00
((0x07E0 << 4)+ 0x0000)的物理地址.您可以在此处了解有关16:16内存寻址计算的更多信息.将段乘以16与将其向左移位4位相同.
代码中的第二个问题是:
mov ah,0x02 ;function
mov al,0x1 ;sectors to read
mov ch,0x0 ;tracks
mov cl,0x2 ;sector number
mov dh,0x0 ;head
int 0x13
Run Code Online (Sandbox Code Playgroud)
磁盘上第二个512块扇区的编号是2,而不是1.因此要修复上面的代码,需要相应地设置CL:
mov cl,0x2 ;sector number
Run Code Online (Sandbox Code Playgroud)
在应该解决的各种仿真器,虚拟机和真实物理硬件上运行代码的其他问题包括:
lodsb
,movsb
等等可以被设置或清除.如果方向标志设置不正确,可能会在错误的方向上调整SI/DI寄存器.使用STD
/ CLD
将其设置为您希望的方向(CLD =正向/ STD =向后).在这种情况下,代码假定向前移动,因此应该使用CLD
.有关详细信息,请参阅指令集参考要解析第一个和第二个项目,可以在引导加载程序的开头附近使用此代码:
xor ax,ax ; We want a segment of 0 for DS for this question
mov ds,ax ; Set AX to appropriate segment value for your situation
mov es,ax ; In this case we'll default to ES=DS
mov bx,0x8000 ; Stack segment can be any usable memory
cli ; Disable interrupts to circumvent bug on early 8088 CPUs
mov ss,bx ; This places it with the top of the stack @ 0x80000.
mov sp,ax ; Set SP=0 so the bottom of stack will be @ 0x8FFFF
sti ; Re-enable interrupts
cld ; Set the direction flag to be positive direction
Run Code Online (Sandbox Code Playgroud)
有几点需要注意.当改变的值SS(通过在此情况下的寄存器MOV
),所述处理器被假设关闭中断该指令并保持它们关断,直到经过了下面的指令.通常,如果更新SS后立即更新SP,则无需担心禁用中断.在早期的8088处理器中存在一个错误,但这并不值得尊重,因此如果您针对最广泛的环境,明确禁用和重新启用它们是一个安全的选择.如果你不打算在越野车8088上工作那么CLI
/STI
可以在上面的代码中删除说明.我在80年代中期在家用电脑上做的工作第一手了解这个错误.
第二点需要注意的是我如何设置堆栈.对于刚接触8088/8086 16位汇编的人来说,可以通过多种方式设置堆栈.在这种情况下,我将堆栈的顶部(内存中的最低部分)设置为0x8000
(SS).然后我将堆栈指针(SP)设置为0
.当您以16位实模式在堆栈上推送某些内容时,处理器首先将堆栈指针递减2,然后在该位置放置一个16位WORD.因此,对堆栈的第一次推送将是0x0000-2 = 0xFFFE(-2).那么你有一个SS:SP,看起来像0x8000:0xFFFE
.在这种情况下,堆栈从0x8000:0x0000
到0x8000:0xFFFF
.
处理在8086上运行的堆栈(不适用于80286,80386+处理器)时,最好将堆栈指针(SP)设置为偶数.在原始的8086上,如果将SP设置为奇数,则每次访问堆栈空间都会产生4个时钟周期的惩罚.由于8088具有8位数据总线,因此不存在这种损失,但是在8086上加载16位字需要4个时钟周期,而在8088上需要8个时钟周期(两个8位存储器读取).
最后,如果要显式设置CS:IP以便在JMP完成(到内核)时正确设置CS,则建议执行FAR JMP(请参阅影响段寄存器的操作/FAR跳转).在NASM语法中,它看起来像这样:JMP
jmp 0x07E0:0x0000
Run Code Online (Sandbox Code Playgroud)
有些(即MASM/MASM32)汇编程序没有直接支持对FAR Jmp进行编码,所以可以通过以下方式手动完成:
db 0x0ea ; Far Jump instruction
dw 0x0000 ; Offset
dw 0x07E0 ; Segment
Run Code Online (Sandbox Code Playgroud)
如果使用GNU汇编程序,它看起来像:
ljmpw $0x07E0,$0x0000
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
4475 次 |
最近记录: |