自制操作系统

bochs调试 WONDOWS 10

基本配置

1.启动双击bochsdbg.exe  

2.启动配置

常用调试命令

断点操作

b [address] 设置断点,其中 [address] 可以是物理地址(如 pb 0x7c00)、虚拟地址(如 vb 0:0x7c00)或线性地址(如 lb 0x7c00)。

物理地址

pb 0x7c00

虚拟地址

vb 0:0x7c00

线性地址

lb 0x7c00

info break 查询已经设置的所有断点。

程序执行控制

c 或 cont 或 continue 继续执行程序,直到遇到下一个断点。

继续执行,直到遇到下一个断点

c


s 或 step 或 si 或 stepi 单步执行指令。如果带参数 [count],则执行指定数量的指令,如 step 3 将执行接下来的三条指令。

执行1个命令

s

执行3个命令

step 3

寄存器查询

  • 段寄存器查询

sreg

  • 普通寄存器查询

reg 

  •  寄存器查询

dump_cpu

info [registers] 或 i r 显示所有寄存器的当前值

查询cup 寄存器的值

info cup 

  • 内存操作

x /fmt [address] [size] 查看内存区域的内容。/fmt 可以是 /x(十六进制)、/o(八进制)、/d(十进制)或 /a(ASCII)。
mwrite [address] [data] 向内存地址写入数据。

  • 其他命令

q 或 quit 或 exit 停止调试并退出 Bochs。
help 或 h 显示帮助信息,可以跟具体命令获取详细帮助,如 help x。

  • 反汇编

disassemble [start] [end] 或 dis 反汇编指定范围内的机器码。

  • 断点管理

delete 或 del 或 d [breakpoint number] 删除指定的断点。

寄存器

段寄存器

cs:ip   代码段

ds 数据段

 mov ax, [bx] ;等价于:mov ax, [ds:bx]

ss :sp 堆栈段  bp 

附加段:ES、FS、GS

数据访问:AX,BX.CX,SI,DI

写入磁盘命令

dd if=mbr.bin of=.vhd bs=512 count=1 seek=2

参数解析

  • if=mbr.bin: 指定输入文件为mbr.bin,通常这是一个包含主引导记录(MBR)的二进制文件。
  • of=.vhd: 指定输出文件为.vhd,这是Virtual Hard Disk(虚拟硬盘)的文件格式,常用于虚拟机环境。
  • bs=512: 设置每次读取或写入的数据块大小为512字节,这通常是磁盘扇区的标准大小。
  • count=1: 只读取并写入一个数据块。
  • seek=2: 在输出文件中跳过前两个512字节的块,从第三个512字节块开始写入数据。

中断

 int 10

  •  AH = 00h:设置显示模式,如文本模式或图形模式。
  • AH = 01h:光标位置,设置光标在屏幕上的位置。
  • AH = 02h:写入字符,将一个字符写入当前光标位置。
  • AH = 03h:读取字符,从当前光标位置读取一个字符。
  • AH = 04h:写入字符串,将一个字符串写入当前光标位置。
  • AH = 05h:读取字符串,从当前光标位置读取一个字符串。
  • AH = 06h:清屏,清除屏幕显示。
  • AH = 07h:设置光标形状。
  • AH = 08h:设置光标可见性。
  • AH = 09h:设置字符属性。
  • AH = 0Ah:设置光标起始位置。
  • AH = 0Bh:设置光标结束位置。
  • AH = 0Ch:设置字符颜色。
  • AH = 0Dh:设置页面颜色。
  • AH = 0Eh:在当前光标位置输出一个字符。
  • AH = 0Fh:读取光标位置。

    mov ax, 0600h ;AH=06h,AL=00H 调用06h功能,清屏
    mov bx, 0700h
    mov cx,0
    mov dx,184fh ;(80,25) 

int 0x80

  • 设置 EAX 寄存器: 在发起系统调用之前,EAX 寄存器必须被设置为一个代表所需系统调用编号的值。每个系统调用都有一个唯一的编号,例如,write 系统调用的编号通常是 4。
  • 设置其他寄存器: 其他寄存器(如 EBX, ECX, EDX 等)用于传递给系统调用的参数。具体哪些参数应该放在哪个寄存器中,取决于具体的系统调用。
  • 执行 int 0x80: 执行 int 0x80 指令后,处理器会跳转到内核中预先设定的中断处理程序。这个处理程序会读取 EAX 寄存器中的系统调用编号,然后根据编号执行相应的系统调用服务。
  • 返回结果: 系统调用完成后,结果通常会被存储在 EAX 寄存器中,错误码则可能存储在其他寄存器中,如 EFLAGS。 

section .data
    message db 'Hello, World!',0

section .text
    global _start

_start:
    ; 设置系统调用编号(write = 4)
    mov eax, 4

    ; 设置第一个参数(文件描述符,标准输出 = 1)
    mov ebx, 1

    ; 设置第二个参数(要写的字符串的地址)
    mov ecx, message

    ; 设置第三个参数(要写入的字节数)
    mov edx, 13

    ; 发起系统调用
    int 0x80

    ; 结束程序
    mov eax, 1      ; 系统调用编号(exit = 1)
    xor ebx, ebx    ; exit code = 0
    int 0x80

lba读写

lba支持的磁盘有:ide、SATA、SCSI、SSD、USB

lba的寄存器有:data寄存器,device寄存器,command寄存器

LBA28方式使用28位来描述一个扇区地址,最大支持128GB的硬磁盘容量。

data寄存器0x1F0已经读取或写入的数据,大小为两个字节(16位数据)
每次读取1个word,反复循环,直到读完所有数据
features寄存器0x1F1读取时的错误信息
写入时的额外参数
sector count寄存器0x1F2指定读取或写入的扇区数
LBA low寄存器0x1F3lba地址的低8位
LBA mid寄存器0x1F4lba地址的中8位
LBA high寄存器0x1F5lba地址的高8位
device寄存器0x1F6lba地址的前4位(占用device寄存器的低4位)
主盘值为0(占用device寄存器的第5位)
第6位值为1
LBA模式为1,CHS模式为0(占用device寄存器的第7位)
第8位值为1
command寄存器0x1F7读取,写入的命令,返回磁盘状态
1 读取扇区:0x20 写入扇区:0x30
磁盘识别:0xEC

 mbr与ide磁盘的读写

;能够将第二个扇区里面的内容加载进入内存
;mbr.asm loader.asm kernal.asm
;0         1                       3
;将loader放入0x900
[bits 16]
LOADER_BASE_ADDR    equ 0x900
LOADER_START_SECTOR    equ 0x2    ;表示已LBA方式,我们的loader存在第2块扇区


SECTION MBR vstart=0x7c00
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
    mov sp,0x7c00
    mov ax,0xb800
    mov gs,ax
    
;利用0x06的功能,调用10号中断,
; AH = 0x06 
; AL = 0 表示全部都要清楚
; BH = 上卷行的属性
;(CL,CH) 左上角 x,y
;(DL,DH) 右下角

    mov ax, 0600h
    mov bx, 0700h
    mov cx,0
    mov dx,184fh ;(80,25)
    
    int 10h
;输出当前我们在MBR
    mov byte [gs:0x00], '1'
    mov byte [gs:0x01], 0xA4
    
    mov byte [gs:0x02], ' '
    mov byte [gs:0x03], 0xA4
    
    mov byte [gs:0x04], ' '
    mov byte [gs:0x05], 0xA4
    
    
    mov byte [gs:0x06], 'B'
    mov byte [gs:0x07], 0xA4
    
    mov byte [gs:0x08], 'R'
    mov byte [gs:0x09], 0xA4
    
    mov eax,LOADER_START_SECTOR ;LBA 读入的扇区
    mov bx,LOADER_BASE_ADDR        ;写入的地址
    mov cx,1                    ;等待读入的扇区数
    call rd_disk
    
    jmp LOADER_BASE_ADDR        ;调到实际的物理内存

rd_disk:
    ;eax LBA的扇区号
    ;bx 数据写入的内存地址
    ;cx 读入的扇区数
    
    mov esi,eax        ;备份eax
    mov di,cx        ;备份cx
    
;读一个扇区
    mov dx, 0x1f2
    mov al, cl
    out dx, al
    
    

;将LBA的地址存入0x1f3,0x1f6
    
    ;7-0位写入0x1f3
    mov eax,esi
    mov dx, 0x1f3
    out dx,al
    
    ;15-8位写给1f4
    mov cl,8
    shr eax,cl
    mov dx,0x1f4
    out dx,al
    
    ;23-16位写给1f5
    shr eax,cl
    mov dx,0x1f5
    out dx,al
    ;divice寄存器设置
    shr eax,cl
    and al,0x0f
    or al,0xe0    ;设置7-4位为1110,此时才是lBA模式
    mov dx,0x1f6
    out dx,al
    
    ;设置commnd寄存器 向0x1f7写入读命令
    mov dx,0x1f7
    mov al,0x20
    out dx,al
    
    ;检测硬盘状态
    .not_ready:
    nop
    in al,dx
    and al,0x88; 4位为1,表示可以传输,7位为1表示硬盘忙
    cmp al,0x08
    jnz .not_ready
    
    ;data寄存器设置 读数据
    mov ax,di
    mov dx, 256
    mul dx
    mov cx,ax
    mov dx,0x1f0
    
    .go_on:
        in ax,dx
        mov [bx],ax
        add bx,2
        loop .go_on
        ret    
    times 510 - ($-$$)    db 0
    dw 0xaa55

loader

;能够将第9个扇区里面的内容加载进入内存
;mbr.asm loader.asm kernal.asm
;0         1                       3
;将kernal放入0x1500

KERNAL_BASE_ADDR equ 0x1500
KERNAL_START_SECTOR equ 0x9
LOADER_BASE_ADDR equ 0x900
section loader vstart=LOADER_BASE_ADDR
mov ax,0xb800;指定文本模式显示缓存区
mov es,ax

mov byte [es:0x00],'o'
mov byte [es:0x01],0x07
mov byte [es:0x02],'k'
mov byte [es:0x03],0x06

    mov eax,KERNAL_START_SECTOR ;LBA 读入的扇区
    mov bx,KERNAL_BASE_ADDR        ;写入的地址
    mov cx,1                    ;等待读入的扇区数
    call rd_disk
    
    jmp KERNAL_BASE_ADDR        ;调到实际的物理内存

rd_disk:
    ;eax LBA的扇区号
    ;bx 数据写入的内存地址
    ;cx 读入的扇区数
    
    mov esi,eax        ;备份eax
    mov di,cx        ;备份cx
    
;读一个扇区
    mov dx, 0x1f2
    mov al, cl
    out dx, al
    
    

;将LBA的地址存入0x1f3,0x1f6
    
    ;7-0位写入0x1f3
    mov eax,esi
    mov dx, 0x1f3
    out dx,al
    
    ;15-8位写给1f4
    mov cl,8
    shr eax,cl
    mov dx,0x1f4
    out dx,al
    
    ;23-16位写给1f5
    shr eax,cl
    mov dx,0x1f5
    out dx,al
    ;divice寄存器设置
    shr eax,cl
    and al,0x0f
    or al,0xe0    ;设置7-4位为1110,此时才是lBA模式
    mov dx,0x1f6
    out dx,al
    
    ;设置commnd寄存器 向0x1f7写入读命令
    mov dx,0x1f7
    mov al,0x20
    out dx,al
    
    ;检测硬盘状态
    .not_ready:
    nop
    in al,dx
    and al,0x88; 4位为1,表示可以传输,7位为1表示硬盘忙
    cmp al,0x08
    jnz .not_ready
    
    ;data寄存器设置 读数据
    mov ax,di
    mov dx, 256
    mul dx
    mov cx,ax
    mov dx,0x1f0
    
    .go_on:
        in ax,dx
        mov [bx],ax
        add bx,2
        loop .go_on
        ret    

 kernal

[bits 16]
KERNAL_BASE_ADDR equ 0x1500
section kernal vstart=KERNAL_BASE_ADDR

mov ax,0xb800 ;指向文本模式的显示缓冲区
mov es,ax

mov byte [es:0x00],'z'
mov byte [es:0x01],0xA4
mov byte [es:0x02],'h'
mov byte [es:0x03],0xA4
jmp $
times 512 - ($-$$)    db 0

c与nasm联合编程  

小试牛刀

1.gcc 编译命令

-o指定目标文件

-m32 32系统架构

-c 生成可链接文件:.o文件

gcc -o test.c.o -m32 -c test.c

test.c 

 void myprint(char *msg,int len);
int chooseNUM(int a,int b)
{
    if(a>b)
    {
        myprint("a is big\n",9);
    }
    else
    {
        myprint("b is big\n",9);
    }
}

2.nasm 编译命令 

-f 生成elf类型的文件

-o 生成目标文件 

 nasm -f elf -o nasm.c.asm.o test.c.asm

test.c.asm 

 extern chooseNUM
[section .data]
num1 dd 22
num2 dd 33
[section .text]
global _start
global myprint
_start:
    push num1
    push num2
    call chooseNUM
    add esp,4
    mov ebx,0
    mov eax,1;sys_exit
    int 0x80
myprint:
    mov edx,[esp+8]
    mov ecx,[esp+4]
    mov ebx,1
    mov eax,4
    int 0x80;sys_write
    ret

3.ld 链接 

-s 精简

-m 指定系统架构

-o 生成目标问价

-e 指定入口函数:default:_start

ld -s  -m elf_i386 -o cAndNasm test.c.o test.c.asm.o

4.执行

 联合编程成功 

开始开发

1.改造test.c

  void myprint();
int DISPLAY()
{
    myprint();
    while(1)
    {
        
    }
}

2.改造test.c.asm

 extern DISPLAY
[bits 16]
[section .text]
global _start
global myprint
_start:
    call DISPLAY
myprint:
    mov ax,0xb800
    mov es,ax
    mov byte [es:0x00],'A'
    mov byte [es:0x01],0x07
    ret

3.生成elf的.o文件

#-f efl 生成elf格式的问价

nasm -f elf -o testc.asm.o test.c.asm

#-m32 指定架构系统 -c 生成.o文件
gcc -m32 -c -o testc.o test.c

4.链接文件

#-s 精简 -m 指定系统架构 -Ttext 指定程序入口

ld -s -m elf_i386 -Ttext 0x1500 -o kernal testc.asm.o testc.o

5.objcopy 生成纯二进制文件

-O(大写) 生成文件类型 :binary

 objcopy -O binary kernal kernal.bin

6.完成 dd 写入硬盘

安全模式

GDT

GDTR 

nasm代码 


;----------------------------------------------------------------------------
DA_DR        EQU    90h    ; 存在的只读数据段类型值
DA_DRW        EQU    92h    ; 存在的可读写数据段属性值
DA_DRWA        EQU    93h    ; 存在的已访问可读写数据段类型值
DA_C        EQU    98h    ; 存在的只执行代码段属性值
DA_CR        EQU    9Ah    ; 存在的可执行可读代码段属性值
DA_CCO        EQU    9Ch    ; 存在的只执行一致代码段属性值
DA_CCOR        EQU    9Eh    ; 存在的可执行可读一致代码段属性值
;----------------------------------------------------------------------------
DA_32        EQU    4000h    ; 32 位段

DA_DPL0        EQU      00h    ; DPL = 0
DA_DPL1        EQU      20h    ; DPL = 1
DA_DPL2        EQU      40h    ; DPL = 2
DA_DPL3        EQU      60h    ; DPL = 3
;----------------------------------------------------------------------------
%macro Descriptor 3
    dw    %2 & 0FFFFh                ; 段界限 1                (2 字节)
    dw    %1 & 0FFFFh                ; 段基址 1                (2 字节)
    db    (%1 >> 16) & 0FFh            ; 段基址 2                (1 字节)
    dw    ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)    ; 属性 1 + 段界限 2 + 属性 2        (2 字节)
    db    (%1 >> 24) & 0FFh            ; 段基址 3                (1 字节)
%endmacro ;

org 07c00h
    jmp    LABEL_BEGIN

[SECTION .gdt]
; GDT
;                                         段基址,      段界限     , 属性
LABEL_GDT:        Descriptor           0,                0, 0             ; 空描述符
LABEL_DESC_CODE32:    Descriptor           0, SegCode32Len - 1, DA_C + DA_32    ; 非一致代码段, 32
LABEL_DESC_VIDEO:    Descriptor     0B8000h,           0ffffh, DA_DRW        ; 显存首地址
; GDT 结束

GdtLen        equ    $ - LABEL_GDT    ; GDT长度
GdtPtr        dw    GdtLen - 1    ; GDT界限
        dd    0        ; GDT基地址

; GDT 选择子
SelectorCode32        equ    LABEL_DESC_CODE32    - LABEL_GDT
SelectorVideo        equ    LABEL_DESC_VIDEO    - LABEL_GDT
; END of [SECTION .gdt]

[SECTION .s16]
[BITS    16]
LABEL_BEGIN:
    mov    ax, cs
    mov    ds, ax
    mov    es, ax
    mov    ss, ax
    mov    sp, 0100h

    ; 回写初始化 32 位代码段描述符
    xor    eax, eax  
    mov    ax, cs
    shl    eax, 4
    add    eax, LABEL_SEG_CODE32
    mov    word [LABEL_DESC_CODE32 + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_CODE32 + 4], al
    mov    byte [LABEL_DESC_CODE32 + 7], ah

    ; 为加载 GDTR 作准备
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_GDT        ; eax <- gdt 基地址
    mov    dword [GdtPtr + 2], eax    ; [GdtPtr + 2] <- gdt 基地址

    ; 加载 GDTR
    lgdt    [GdtPtr]

    ; 关中断
    cli

    ; 打开地址线A20
    in    al, 92h
    or    al, 00000010b
    out    92h, al

    ; 准备切换到保护模式
    mov    eax, cr0
    or    eax, 1
    mov    cr0, eax

    ; 真正进入保护模式
    jmp    dword SelectorCode32:0    ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处
; END of [SECTION .s16]


[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS    32]

LABEL_SEG_CODE32:
    mov    ax, SelectorVideo
    mov    gs, ax            ; 视频段选择子(目的)

    mov    edi, (80 * 3 + 0) * 2    ; 屏幕第 3 行, 第 0 列。
    mov    ah, 0Ch            ; 0000: 黑底    1100: 红字
    mov    al, 'D'
    mov    [gs:edi], ax

    ; 到此停止
    jmp    $

SegCode32Len    equ    $ - LABEL_SEG_CODE32
; END of [SECTION .s32]
times 361    db 0
dw 0xaa55

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值