• 【日拱一卒行而不辍20220921】自制操作系统


    8086内存安排

    8086实模式的寻址范围只有1MB。其中:

    系统硬件使用的存储器地址被安排在高端,地址从0xA0000H(684KB)开始的384KB中,其中有用于显示的视频缓冲区;

    内存低端安排了中断向量表和BIOS数据区;

    剩下的从0x00500H开始的到0xA0000H总共不到640KB的内存是操作系统和应用程序所能够使用的,应用程序不能够使用这600KB以外的内存,这就是著名的“640KB限制”。

     彩显缓冲区

    从上图可以看出,0xB0000H开始到0xB8000H为单色字符模式视频缓冲区;0xB8000H到C0000为彩色字符模式视频缓冲区。

    内存地址空间从B8000H~BFFFFH 共32KB空间([B800:0000]到[B800:8FFF]),称为80x25彩色字符模式显示缓冲区,向这个地址写入的数据会立即出现在显示器上。

    窗口大小为160x25个字节(4000个字节),每两个字节为一个字符的参数:

    偶数地址存放字符的ascii码,奇数地址存放字符属性。

    一行可以存放80个字符一共25行,共可以存放80x25个字符2000个字符。每个字符可以有FF=2^8=256种属性。

    第7位:代表闪烁效果
    第6、5、4位:代表背景色的rgb=red green blue 红绿蓝
    第3位:高亮
    第2、1、0 位:字符颜色的rgb
    对应颜色设置成1就变成对应颜色
    0010 0100 绿底红字 十六进制=0x24
    0100 0001 红底蓝字 十六进制=0x41
    0001 0010 蓝底绿字 十六进制=0x12

    对于昨天的第二扇区的代码,直接把数据放到了0xB8000H开始的彩色字符模式视频缓冲区内。

     昨天的运行效果如下。

     根据如下代码,可以看出每行用160字节可以显示80个字符。

    单色缓冲区

    将把上节中的es由0xB800改为0xB000后,qemu没有进行显示。所以后续还是用es=0xB800H吧。

    int13/02H中断

    根据int13/02H调用格式:

    AH = 02h
    AL = number of sectors to read (must be nonzero)
    CH = low eight bits of cylinder number
    CL = sector number 1-63 (bits 0-5)
    high two bits of cylinder (bits 6-7, hard disk only)
    DH = head number
    DL = drive number (bit 7 set for hard disk)
    ES:BX -> data buffer

    下述代码将第二扇区复制进了内存es:bx=[new:0]位置

    1. mov ah, 0x02
    2. mov al, 1
    3. mov ch, 0
    4. mov cl, 2
    5. mov dh, 0
    6. mov bx, new
    7. mov es, bx
    8. xor bx, bx
    9. int 0x13

    下述代码令cs:ip=[new:0]

            jmp new:0

    通过上述两部操作,把第二扇区的代码复制到了[new:0]的位置,同时也让指令指针指向了此位置,从而可以继续运行代码。

    去掉冗余代码,最精简的可以使用第二扇区代的代码如下。

    1. [BITS 16]
    2. org 0x7C00
    3. start:
    4. mov ah, 0x02
    5. mov al, 1
    6. mov ch, 0
    7. mov cl, 2
    8. mov dh, 0
    9. mov bx, new
    10. mov es, bx
    11. xor bx, bx
    12. int 0x13
    13. jmp new:0
    14. data:
    15. new equ 0x0500
    16. times 510-($-$$) db 0
    17. dw 0xaa55
    18. sect2:
    19. mov ax, 0xB800
    20. mov es, ax
    21. mov byte [es:0x0000], '0'
    22. mov byte [es:0x0001], 0x48
    23. mov byte [es:0x00A0], '1'
    24. mov byte [es:0x00A1], 0x48
    25. mov byte [es:0x0140], '2'
    26. mov byte [es:0x0141], 0x48
    27. mov byte [es:0x00A0], '1'
    28. mov byte [es:0x00A1], 0x48
    29. mov byte [es:0x00A0], '1'
    30. mov byte [es:0x00A1], 0x48
    31. mov byte [es:420], 'H'
    32. mov byte [es:421], 0x48
    33. mov byte [es:422], 'E'
    34. mov byte [es:423], 0x68
    35. mov byte [es:424], 'L'
    36. mov byte [es:425], 0x28
    37. mov byte [es:426], 'L'
    38. mov byte [es:427], 0x38
    39. mov byte [es:428], 'O'
    40. mov byte [es:429], 0x18
    41. mov byte [es:430], '!'
    42. mov byte [es:431], 0x58
    43. hlt

    GDT

    全局描述符表Global Descriptor Table,表中每个元素8个字节,每个元素表示一个段(代码段,数据段,栈段)的信息,且GDT在进入保护模式之前必须存在,所以它必须位于1MB以下。

    GDTR中存放的是GDT在内存中的基地址和其表长界限。

     LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。

    LDTR记录局部描述符表的起始位置,与GDTR不同,LDTR的内容是一个段选择子。

    由于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它,这个描述符就存储在GDT中,对应这个表述符也会有一个选择子,LDTR装载的就是这样一个选择子。

    LDTR可以在程序中随时改变,通过使用lldt指令。如上图,如果装载的是Selector 2则LDTR指向的是表LDT2。

    由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表LDT则可以将每个进程的程序段CS、数据段DS、堆栈段SS封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问。

    如下代码为建立一个GDT的示例,首先定义一段连续内存。本示例构建了3个元素CODE_DESC、DATA_DESC、VIDEO_DESC,并预留了60个元素。在lgdt命令中将GDT_LIMIT和GDT_BASE赋给了寄存器GDTR。

    其中:DD 的意思是define double(4-bytes),与之相似的还有db(byte)dw(word,2-bytes)

    1. ; gdt
    2. GDT_BASE:
    3. dd 0x00000000
    4. dd 0x00000000
    5. CODE_DESC:
    6. dd DESC_CODE_LOW_32
    7. dd DESC_CODE_HIGH_32
    8. DATA_DESC:
    9. dd DESC_DATA_LOW_32
    10. dd DESC_DATA_HIGH_32
    11. VIDEO_DESC:
    12. dd DESC_VIDEO_LOW_32
    13. dd DESC_VIDEO_HIGH_32
    14. ; reserve 60 gdt entries space
    15. times 60 dq 0
    16. GDT_SIZE equ $ - GDT_BASE
    17. GDT_LIMIT equ GDT_SIZE - 1
    18. gdt_ptr:
    19. dw GDT_LIMIT
    20. dd GDT_BASE
    21. ; enable A20
    22. in al, 0x92
    23. or al, 0000_0010b
    24. out 0x92, al
    25. ; load GDT
    26. lgdt [gdt_ptr]
    27. ; open protection mode - set cr0 bit 0
    28. mov eax, cr0
    29. or eax, 0x00000001
    30. mov cr0, eax
    31. ; refresh pipeline
    32. jmp dword SELECTOR_CODE:protection_mode_entry

    其中,SELECTOR_CODE等的定义如下:

    1. ;*************************** segment selector *********************************;
    2. RPL0 equ 00b
    3. RPL1 equ 01b
    4. RPL2 equ 10b
    5. RPL3 equ 11b
    6. TI_GDT equ 000b
    7. TI_LDT equ 100b
    8. SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0
    9. SELECTOR_DATA equ (0x0002 << 3) + TI_GDT + RPL0
    10. SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0

    jmp dword SELECTOR_CODE:protection_mode_entry

    执行这一句会把SELECTOR_CODE装入CS,并跳转到SELECTOR_CODE:protection_mode_entry处。

    LDT

    LDT示例代码如下所示

    1. ; LDT
    2. [SECTION .ldt]
    3. ALIGN 32
    4. LABEL_LDT:
    5. ; 段基址 段界限 属性
    6. LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32
    7. LABEL_LDT_DESC_CODEB: Descriptor 0, CodeBLen - 1, DA_C + DA_32 ; Code, 32
    8. LDTLen equ $ - LABEL_LDT ;LDT表长度
    9. ; LDT 选择子
    10. SelectorLDTCodeA equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL
    11. SelectorLDTCodeB equ LABEL_LDT_DESC_CODEB - LABEL_LDT + SA_TIL
    12. ; END of [SECTION .ldt]
    13. ; Load LDT
    14. mov ax, SelectorLDT
    15. lldt ax ;加载LDT在GDT中的描述符
  • 相关阅读:
    2023电赛E题视觉部分
    模糊控制算法实例matlab程序
    vs工程配置
    P01914005张怡安(信息论作业)
    《最新出炉》系列入门篇-Python+Playwright自动化测试-47-自动滚动到元素出现的位置
    Liquibase使用SQL语句执行数据库变更
    Vue 组件间的通信方式
    HttpServlet学习中的常见问题(个人珍藏笔记)
    思维训练1
    AI-数学-高中-45函数单调性与导数
  • 原文地址:https://blog.csdn.net/bear_miao/article/details/126965462