至此(day1-day4)代码详解(ai辅助整理)
至此(day1-day4)代码详解
ipl10.nas
; ==================== 第一阶段引导程序 ====================
; 功能:读取磁盘数据并跳转到第二阶段加载程序
; 编译参数:nask -o ipl10.bin ipl10.nasCYLS EQU 10 ; 预设读取柱面数(实际值由BIOS决定)ORG 0x7c00 ; BIOS加载引导程序的标准内存地址; ---------- FAT12文件系统头部信息(必须严格符合规范)----------JMP entryDB 0x90 ; 跳转指令的填充字节DB "HARIBOTE" ; 引导扇区名称(8字节)DW 512 ; 每扇区字节数(必须512)DB 1 ; 每簇扇区数(必须1)DW 1 ; FAT表起始扇区(从第1扇区开始)DB 2 ; FAT表数量(必须2)DW 224 ; 根目录最大文件数(标准FAT12值)DW 2880 ; 磁盘总扇区数(1.44MB软盘标准值)DB 0xf0 ; 磁盘类型标识(0xf0表示3.5寸软盘)DW 9 ; 每个FAT表长度(9扇区)DW 18 ; 每磁道扇区数(标准1.44MB软盘)DW 2 ; 磁头数量(双面)DD 0 ; 隐藏扇区数(无分区设为0)DD 2880 ; 磁盘总扇区数(与前面相同)DB 0,0,0x29 ; 扩展引导标记(固定值)DD 0xffffffff ; 卷序列号(随机生成)DB "HARIBOTEOS "; 卷标(11字节)DB "FAT12 " ; 文件系统类型(8字节)RESB 18 ; 保留空间(填充18字节); ---------- 程序主体 ----------
entry:; 初始化段寄存器MOV AX,0 ; 将AX清零MOV SS,AX ; 堆栈段寄存器MOV SP,0x7c00 ; 堆栈指针指向引导程序起始地址MOV DS,AX ; 数据段寄存器; 设置磁盘读取参数MOV AX,0x0820 ; 数据加载到内存0x8200处
; ES 是段寄存器(Extra Segment Register),在实模式下,x86 处理器使用段寄存器和偏移寄存器组合来形成内存地址。
; ES 通常用于附加数据段,与偏移寄存器 BX 配合使用,即 ES:BX 可以构成一个 20 位的内存地址。
; 在这段代码里,ES 用于指定磁盘读取数据要加载到的内存段地址,后续通过 ES:BX 确定数据加载的具体目标地址。
; 在后续的代码中,`MOV BX,0` 结合当前的 ES 寄存器值,确定了磁盘读取数据的起始目标地址;
; 并且在 `next` 标签处,通过修改 ES 的值来更新目标地址,以实现连续读取磁盘数据到不同内存位置的功能。MOV ES,AX ; ES:BX构成目标地址MOV CH,0 ; 起始柱面号0MOV DH,0 ; 磁头号0MOV CL,2 ; 起始扇区号2(跳过引导扇区)readloop:MOV SI,0 ; 错误计数器清零
retry:; 调用BIOS磁盘读取中断(INT 13h AH=02h)MOV AH,0x02 ; 功能号:读取扇区MOV AL,1 ; 读取1个扇区MOV BX,0 ; ES:BX=目标地址MOV DL,0x00 ; 驱动器号0(A盘)
; 调用BIOS磁盘读取中断(INT 13h AH=02h),以下是该中断使用的寄存器及返回值说明:
; 输入寄存器:
; AH = 0x02 表示读取扇区功能
; AL = 要读取的扇区数量,此处为1
; CH = 起始柱面号,此处为0
; CL = 起始扇区号,此处为2(跳过引导扇区)
; DH = 磁头号,此处为0
; DL = 驱动器号,此处为0x00(A盘)
; ES:BX = 数据加载的目标内存地址,此处ES = 0x0820,BX = 0,即目标地址为0x8200
;
; 返回寄存器:
; CF(进位标志):如果CF = 0,表示操作成功;如果CF = 1,表示操作失败
; AH = 返回状态码,0 表示成功,其他值表示错误
; AL = 实际读取的扇区数量INT 0x13JNC next ; 成功则继续,JNC 英文全称为 Jump if Not Carry; 错误处理(最多重试5次)ADD SI,1 ; 错误计数加1CMP SI,5 ; 超过5次则报错
; JAE 是 "Jump if Above or Equal" 的缩写,意思是如果高于或等于则跳转JAE errorMOV AH,0x00 ; 驱动器复位功能MOV DL,0x00 ; 驱动器号0
; 调用BIOS磁盘中断服务,此处AH = 0x00 表示驱动器复位功能,DL = 0x00 表示驱动器号为A盘,
; 该中断用于在磁盘读取失败时尝试复位驱动器,以便重新进行读取操作。INT 0x13JMP retry ; 重试读取next:; 准备读取下一个扇区MOV AX,ES ; 当前内存地址ADD AX,0x0020 ; 每次增加512字节(0x200)MOV ES,AX ; 更新目标地址ADD CL,1 ; 扇区号加1CMP CL,18 ; 是否读完1磁道(18扇区)JBE readloop ; 未读完继续; 切换磁头/柱面MOV CL,1 ; 扇区号重置为1ADD DH,1 ; 切换磁头(0->1)CMP DH,2JB readloop ; 磁头0/1切换MOV DH,0 ; 磁头重置为0ADD CH,1 ; 柱面号加1CMP CH,CYLS ; 是否读完预设柱面JB readloop ; 继续读取; ---------- 加载完成 ----------MOV [0x0ff0],CH ; 保存实际读取的柱面数到0x0ff0JMP 0xc200 ; 跳转到第二阶段加载程序(asmhead); ---------- 错误处理 ----------
error:MOV SI,msg ; 加载错误信息地址
putloop:
; LODSB 指令用于从 DS:SI 所指向的内存地址加载一个字节到 AL 寄存器中。
; 在此处,SI 寄存器已经在前面的代码中被赋值为 msg 标签的地址,即错误信息字符串的起始地址。
; 因此,当前加载的地址就是错误信息字符串在内存中的地址,由 DS 段寄存器和 SI 偏移地址共同确定。
; 随着每次执行 LODSB 指令,SI 寄存器的值会自动加 1,指向下一个字符的地址。LODSB ; 加载字符到ALCMP AL,0 ; 是否字符串结尾
; JE 是 "Jump if Equal" 的缩写,意思是如果相等则跳转。
; 此处将 AL 寄存器的值与 0 比较后,若相等(即字符串结束),则跳转到 fin 标签处,结束错误信息的显示。JE finMOV AH,0x0e ; BIOS显示字符功能MOV BX,15 ; 显示颜色(白色)INT 0x10 ; 调用BIOS视频中断服务,功能为显示字符。;在前面已经设置了AH = 0x0e(BIOS显示字符功能)和BX = 15(显示颜色为白色),通过该中断将AL寄存器中的字符显示在屏幕上。JMP putloop
fin:HLT ; 停止CPUJMP fin ; 进入死循环msg:DB 0x0a, 0x0a ; 两个换行符DB "load error"; 错误信息DB 0x0a ; 换行符DB 0 ; 字符串结束符RESB 0x7dfe-$ ; 填充至510字节DB 0x55, 0xaa ; 引导扇区结束标志(0x55AA)
;; 常用段寄存器使用场景:
; --------------------------------------------------
; 1. ES(附加段寄存器)使用场景:;MOV AX,0x0820 ;MOV ES,AX ; ◀ 与BX配合指定磁盘数据加载地址0x8200; BIOS中断INT13h要求目标地址必须通过ES:BX指定; 2. DS(数据段寄存器)使用场景:; MOV DS,AX ; ◀ 用于访问程序数据段(如msg字符串);MOV AL,[SI] ; ◀ 默认使用DS段访问内存; 3. SS(堆栈段寄存器)使用场景:;MOV SS,AX ; ◀ 设置堆栈段地址;MOV SP,0x7c00 ; ◀ SP与SS配合构成堆栈地址; 4. SI(源索引寄存器)典型用法:;MOV SI,msg ; ◀ 作为字符串/数组的偏移地址;LODSB ; ◀ 从DS:SI地址读取字节到AL; 5. DI(目标索引寄存器)典型用法:;MOV DI,0x8200 ; ◀ 作为磁盘数据加载地址;MOV BX,DI ; ◀ 与ES配合指定磁盘数据加载地址0x8200;MOV [DI],AL ; ◀ 默认使用DS段访问内存
;各段寄存器使用规则:;1. ES :用于需要额外数据段的场景(磁盘I/O、字符串操作)
;2. DS :默认数据访问段(变量/数组访问)
;3. SS :堆栈操作自动使用(PUSH/POP/CALL/RET指令)
;4. SI/DI :;- SI通常与DS配合(DS:SI)作为源地址;- DI通常与ES配合(ES:DI)作为目标地址;- 在ipl10.nas中SI被用作错误计数器是特殊用法
asmhead.nas
; ==================== 第二阶段加载程序 ====================
; 功能:设置保护模式、初始化硬件、加载操作系统核心
; 编译参数:nask -o asmhead.bin asmhead.nas; ---------- 内存地址常量 ----------
BOTPAK EQU 0x00280000 ; 操作系统核心加载地址
DSKCAC EQU 0x00100000 ; 磁盘缓存区(主)
DSKCAC0 EQU 0x00008000 ; 磁盘缓存区(临时); ---------- 引导信息结构体地址 ----------
CYLS EQU 0x0ff0 ; 柱面数存储地址(来自ipl)
LEDS EQU 0x0ff1 ; 键盘指示灯状态
VMODE EQU 0x0ff2 ; 视频模式(8位=256色)
SCRNX EQU 0x0ff4 ; 水平分辨率(X轴)
SCRNY EQU 0x0ff6 ; 垂直分辨率(Y轴)
VRAM EQU 0x0ff8 ; 显存起始地址ORG 0xc200 ; 程序加载地址(ipl读取的数据); ---------- 视频模式初始化 ----------MOV AL,0x13 ; 设置VGA模式13h(320x200x8位)MOV AH,0x00 ; 功能号:设置视频模式
; INT 0x10 是BIOS提供的视频服务中断。在前面的代码中,已经将AL寄存器设置为0x13,AH寄存器设置为0x00。
; AL = 0x13 表示要设置的VGA模式为13h,即320x200像素的8位彩色模式。
; AH = 0x00 是BIOS视频服务的功能号,代表设置视频模式。
; 当执行 INT 0x10 指令时,BIOS会根据AH和AL寄存器的值来设置相应的视频模式。INT 0x10 ; 调用BIOS视频中断; 保存视频参数到引导信息结构体
; BYTE、WORD、DWORD 用于指定操作数的大小,具体根据存储数据的实际需求和对应内存地址所代表的数据类型来确认。
; VMODE 存储颜色位数,8 位即可表示,所以用 BYTE(1 字节)。
MOV BYTE [VMODE],8 ; 记录颜色位数
; SCRNX 和 SCRNY 分别存储水平和垂直分辨率,通常用 16 位整数表示,所以用 WORD(2 字节)。
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
; VRAM 存储显存起始地址,32 位系统中地址用 32 位表示,所以用 DWORD(4 字节)。
MOV DWORD [VRAM],0x000a0000 ; 标准VGA显存地址; ---------- 获取键盘状态 ----------MOV AH,0x02 ; 功能号:获取键盘指示灯状态
; INT 0x16 是BIOS提供的键盘服务中断。在前面的代码中,已经将AH寄存器设置为0x02,代表功能号为获取键盘指示灯状态。
; 当执行 INT 0x16 指令时,BIOS会根据AH寄存器的值执行相应的操作,并将键盘指示灯的状态存储在AL寄存器中。INT 0x16 ; 调用键盘BIOS中断MOV [LEDS],AL ; 保存状态到引导信息; ---------- 禁用中断控制器 ----------
; IRQ(Interrupt Request)即中断请求,是外部设备向CPU发出的请求信号,用于告知CPU有紧急事件需要处理。
; PIC(Programmable Interrupt Controller)即可编程中断控制器,负责管理和分配来自外部设备的中断请求。
;主PIC的端口号是0x20 - 0x21,从PIC的端口号是0xA0 - 0xA1。
; NOP(No Operation)是一条空操作指令,执行该指令时CPU不进行任何实际操作,仅消耗一个时钟周期,常用于硬件延时等待。
; OUT指令用于将数据从CPU寄存器输出到指定的I/O端口。
; CLI(Clear Interrupt Flag)是一条汇编指令,用于清除CPU的中断标志位,禁止CPU响应外部中断请求。MOV AL,0xff ; 屏蔽所有IRQ,将AL寄存器设置为0xff,0xff的二进制表示为全1,意味着屏蔽所有中断请求OUT 0x21,AL ; 主PIC,将AL的值输出到主PIC的中断屏蔽寄存器(端口号0x21),从而屏蔽主PIC管理的所有中断NOP ; 延时等待(硬件要求),执行空操作以满足硬件的时序要求OUT 0xa1,AL ; 从PIC,将AL的值输出到从PIC的中断屏蔽寄存器(端口号0xa1),从而屏蔽从PIC管理的所有中断CLI ; 禁止CPU响应中断,清除CPU的中断标志位,使CPU不再响应外部中断请求; ---------- 启用A20地址线 ----------
; 在实模式下,CPU只能访问1MB的内存空间,这是因为地址线只有20根(A0 - A19)。
; 而A20地址线的作用是允许CPU访问超过1MB的内存空间,进入保护模式后通常需要启用它。
; 键盘控制器可以用来控制A20地址线的状态,下面的代码就是通过键盘控制器来启用A20地址线。; 调用waitkbdout函数,等待键盘控制器的输入缓冲区为空,即键盘控制器空闲。
; 因为在向键盘控制器发送新命令之前,需要确保它已经处理完之前的请求。CALL waitkbdout ; 等待键盘控制器空闲; 将命令码0xd1移动到AL寄存器中。0xd1这个命令的作用是告诉键盘控制器,接下来要向其输出端口写入数据。MOV AL,0xd1 ; 命令:写入输出端口; 通过OUT指令将AL寄存器中的命令码0xd1发送到键盘控制器的命令端口0x64。
; 0x64是键盘控制器的命令端口,用于接收各种控制命令。OUT 0x64,AL ; 发送到键盘控制器; 再次调用waitkbdout函数,等待键盘控制器处理完刚才发送的命令,确保其准备好接收下一个数据。CALL waitkbdout; 将值0xdf移动到AL寄存器中。0xdf这个值用于启用A20门信号,从而允许访问超过1MB的内存空间。MOV AL,0xdf ; 启用A20门信号; 通过OUT指令将AL寄存器中的值0xdf发送到键盘控制器的数据端口0x60。
; 0x60是键盘控制器的数据端口,用于发送和接收数据。OUT 0x60,AL; 最后一次调用waitkbdout函数,等待键盘控制器处理完启用A20门信号的操作。CALL waitkbdout; ---------- 切换到保护模式 ----------
[INSTRSET "i486p"] ; 启用486指令集; LGDT 是一条汇编指令,用于加载全局描述符表寄存器(GDTR)。; [GDTR0] 表示从内存地址 GDTR0 处读取数据,该数据包含全局描述符表(GDT)的界限和基地址。; 全局描述符表是保护模式下用于管理内存段的重要数据结构,通过加载 GDTR,CPU 可以知道 GDT 的位置和大小。LGDT [GDTR0] ; 加载全局描述符表; CR0 是控制寄存器 0,它包含了多个控制位,用于控制 CPU 的工作模式和状态。; 这里将 CR0 的值移动到 EAX 寄存器中,目的是为了后续对其进行修改。MOV EAX,CR0 ; 获取控制寄存器; 0x7fffffff 的二进制表示为除了最高位(第 31 位)为 0 外,其余位全为 1。; 通过 AND 操作,将 EAX 的第 31 位清零,即关闭分页功能。在切换到保护模式初期,通常先关闭分页。AND EAX,0x7fffffff ; 关闭分页(bit31=0); 0x00000001 的二进制表示为最低位(第 0 位)为 1,其余位为 0。; 通过 OR 操作,将 EAX 的第 0 位置为 1,这一位是保护模式标志位,置 1 表示开启保护模式。OR EAX,0x00000001 ; 开启保护模式(bit0=1); 将修改后的 EAX 的值写回到 CR0 寄存器中,从而更新 CPU 的控制状态,正式进入保护模式。MOV CR0,EAX ; 更新控制寄存器; 当切换到保护模式后,CPU 的指令流水线中可能还存在实模式下的指令。; 执行 JMP 指令跳转到 pipelineflush 标签处,目的是清空指令流水线,确保后续执行的是保护模式下的指令。JMP pipelineflush ; 清空指令流水线pipelineflush:; 设置段寄存器为数据段选择子(索引1); 在保护模式下,段寄存器存储的是段选择子,而不是段基址。; 段选择子是一个16位的值,用于在全局描述符表(GDT)中索引对应的描述符。; 每个描述符的大小为8字节,因此索引乘以8得到段选择子的值。; 这里选择索引为1的描述符,即GDT中的第1项(索引从0开始)。MOV AX,1*8 ; 选择子=1(GDT第1项); DS(Data Segment)是数据段寄存器,用于指向数据段。; 将段选择子加载到DS寄存器,这样后续的数据访问操作将基于该数据段。MOV DS,AX ; 数据段; ES(Extra Segment)是附加段寄存器,通常用于辅助数据操作。; 同样将段选择子加载到ES寄存器,使其与DS指向相同的数据段。MOV ES,AX ; 附加段; FS(Far Segment)和GS(General Segment)是额外的段寄存器,可用于各种特殊用途。; 这里也将它们设置为相同的段选择子,以确保数据访问的一致性。MOV FS,AX ; 额外段...MOV GS,AX; SS(Stack Segment)是堆栈段寄存器,用于指向堆栈段。; 将段选择子加载到SS寄存器,确定堆栈段的位置。MOV SS,AX; ---------- 复制操作系统核心 ----------; 复制bootpack到高端内存MOV ESI,bootpack ; 源地址(编译时确定)MOV EDI,BOTPAK ; 目标地址0x280000MOV ECX,512*1024/4 ; 复制512KB(每次4字节)CALL memcpy; 复制启动扇区到缓存区MOV ESI,0x7c00 ; 源地址(ipl自身)MOV EDI,DSKCAC ; 目标地址0x100000MOV ECX,512/4 ; 512字节CALL memcpy; 复制剩余磁盘数据MOV ESI,DSKCAC0+512 ; 源地址(临时缓存+512)MOV EDI,DSKCAC+512 ; 目标地址(主缓存+512)MOV ECX,0MOV CL,BYTE [CYLS] ; 获取实际柱面数IMUL ECX,512*18*2/4 ; 计算总扇区数(柱面*18扇区*2磁头)SUB ECX,512/4 ; 减去已复制的启动扇区CALL memcpy; ---------- 启动操作系统核心 ----------MOV EBX,BOTPAK ; 核心程序入口地址MOV ECX,[EBX+16] ; 获取代码段长度ADD ECX,3 ; 对齐到4字节边界SHR ECX,2 ; 转换为双字数JZ skip ; 无需复制则跳过MOV ESI,[EBX+20] ; 源地址(ELF结构中的偏移)ADD ESI,EBX ; 转换为绝对地址MOV EDI,[EBX+12] ; 目标地址(程序加载地址)CALL memcpy ; 执行复制
skip:MOV ESP,[EBX+12] ; 设置堆栈指针JMP DWORD 2*8:0x0000001b ; 跳转到核心入口; ---------- 辅助函数 ----------
waitkbdout: ; 等待键盘控制器就绪IN AL,0x64 ; 读取状态寄存器AND AL,0x02 ; 检查输入缓冲区状态JNZ waitkbdout ; 忙则继续等待RETmemcpy: ; 内存复制函数(4字节对齐)MOV EAX,[ESI] ; 读取4字节ADD ESI,4 ; 源地址+4MOV [EDI],EAX ; 写入4字节ADD EDI,4 ; 目标地址+4SUB ECX,1 ; 计数器减1JNZ memcpy ; 循环直到ECX=0RET; ---------- GDT定义 ----------ALIGNB 16 ; 16字节对齐
GDT0:RESB 8 ; 空描述符(必须存在); 数据段描述符(可读写,32位)DW 0xffff,0x0000,0x9200,0x00cf ; 代码段描述符(可执行,32位)DW 0xffff,0x0000,0x9a28,0x0047 DW 0 ; 填充字节GDTR0: ; GDT寄存器值DW 8*3-1 ; GDT界限(3个描述符*8字节-1)DD GDT0 ; GDT基地址ALIGNB 16 ; 对齐到16字节
; 目前这里为空,可能是因为操作系统核心程序的具体内容还未编写完成,
; 或者是在开发过程中暂时预留该入口,后续再填充具体的代码逻辑。
; 下面可以添加一些示例代码作为占位,实际开发时需要替换为真实的核心程序代码。
bootpack:; 示例:打印一条简单的消息到屏幕(伪代码,实际需要具体实现); MOV AL, 'H'; MOV AH, 0x0E ; BIOS 显示字符功能号; INT 0x10; 可以继续添加更多初始化或启动相关的代码RET ; 返回调用处,实际核心程序可能不会返回
bootpack.c
/* 硬件操作接口声明 */
void io_hlt(void); // 暂停CPU执行
void io_cli(void); // 清除中断标志
void io_out8(int port, int data); // 向指定端口输出8位数据
int io_load_eflags(void); // 读取EFLAGS寄存器
void io_store_eflags(int eflags); // 写入EFLAGS寄存器/* 图形相关函数声明 */
void init_palette(void); // 初始化调色板
void set_palette(int start, int end, unsigned char *rgb); // 设置调色板
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1); // 绘制矩形
void init_screen(char *vram, int x, int y); // 初始化屏幕布局
void putfont8(char *vram, int xsize, int x, int y, char c, char *font); // 显示8x16字体/* 颜色定义 */
#define COL8_000000 0 // 黑色
#define COL8_FF0000 1 // 亮红色
#define COL8_00FF00 2 // 亮绿色
#define COL8_FFFF00 3 // 亮黄色
#define COL8_0000FF 4 // 亮蓝色
#define COL8_FF00FF 5 // 亮紫色
#define COL8_00FFFF 6 // 亮青色
#define COL8_FFFFFF 7 // 白色
#define COL8_C6C6C6 8 // 亮灰色
#define COL8_840000 9 // 暗红色
#define COL8_008400 10 // 暗绿色
#define COL8_848400 11 // 暗黄色
#define COL8_000084 12 // 暗蓝色
#define COL8_840084 13 // 暗紫色
#define COL8_008484 14 // 暗青色
#define COL8_848484 15 // 暗灰色/* 引导信息结构体(由asmhead.nas传递)*/
struct BOOTINFO {char cyls; // 读取的柱面数char leds; // 键盘LED状态char vmode; // 视频模式char reserve; // 保留字段short scrnx; // 屏幕X分辨率short scrny; // 屏幕Y分辨率char *vram; // 显存起始地址
};/* 主函数 */
void HariMain(void)
{// 从内存0x0ff0获取引导信息// 就是那个asmhead.nas文件给引导结构体分配的地址空间struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;// 字母'A'的点阵数据(8x16像素)static char font_A[16] = {0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00};init_palette(); // 初始化调色板init_screen(binfo->vram, binfo->scrnx, binfo->scrny); // 绘制屏幕// 在(10,10)位置显示白色字母Aputfont8(binfo->vram, binfo->scrnx, 10, 10, COL8_FFFFFF, font_A);// 无限循环保持系统运行for (;;) {io_hlt();}
}/* 初始化调色板 */
void init_palette(void)
{// 16色RGB配置表(R, G, B 各占1字节)static unsigned char table_rgb[16 * 3] = {0x00, 0x00, 0x00, // 0:黑色0xff, 0x00, 0x00, // 1:亮红色0x00, 0xff, 0x00, // 2:亮绿色0xff, 0xff, 0x00, // 3:亮黄色0x00, 0x00, 0xff, // 4:亮蓝色0xff, 0x00, 0xff, // 5:亮紫色0x00, 0xff, 0xff, // 6:亮青色0xff, 0xff, 0xff, // 7:白色0xc6, 0xc6, 0xc6, // 8:亮灰色0x84, 0x00, 0x00, // 9:暗红色0x00, 0x84, 0x00, // 10:暗绿色0x84, 0x84, 0x00, // 11:暗黄色0x00, 0x00, 0x84, // 12:暗蓝色0x84, 0x00, 0x84, // 13:暗紫色0x00, 0x84, 0x84, // 14:暗青色0x84, 0x84, 0x84 // 15:暗灰色};set_palette(0, 15, table_rgb); // 设置0-15号颜色
}/* 设置VGA调色板 */
void set_palette(int start, int end, unsigned char *rgb)
{int i, eflags;eflags = io_load_eflags(); // 保存中断标志io_cli(); // 禁用中断// 设置调色板寄存器io_out8(0x03c8, start); // 起始颜色编号for (i = start; i <= end; i++) {// 写入RGB值(VGA寄存器需要0-63范围的值)io_out8(0x03c9, rgb[0] / 4); // 红色分量io_out8(0x03c9, rgb[1] / 4); // 绿色分量io_out8(0x03c9, rgb[2] / 4); // 蓝色分量rgb += 3; // 移动到下一个颜色}io_store_eflags(eflags); // 恢复中断标志
}/* 矩形填充函数 */
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{int x, y;// 遍历矩形区域for (y = y0; y <= y1; y++) {for (x = x0; x <= x1; x++) {vram[y * xsize + x] = c; // 计算显存地址并写入颜色}}
}/* 初始化屏幕布局 */
void init_screen(char *vram, int x, int y)
{// 绘制背景boxfill8(vram, x, COL8_008484, 0, 0, x-1, y-29);// 绘制状态栏边框boxfill8(vram, x, COL8_C6C6C6, 0, y-28, x-1, y-28);boxfill8(vram, x, COL8_FFFFFF, 0, y - 27, x - 1, y - 27);boxfill8(vram, x, COL8_C6C6C6, 0, y - 26, x - 1, y - 1);boxfill8(vram, x, COL8_FFFFFF, 3, y - 24, 59, y - 24);boxfill8(vram, x, COL8_FFFFFF, 2, y - 24, 2, y - 4);boxfill8(vram, x, COL8_848484, 3, y - 4, 59, y - 4);boxfill8(vram, x, COL8_848484, 59, y - 23, 59, y - 5);boxfill8(vram, x, COL8_000000, 2, y - 3, 59, y - 3);boxfill8(vram, x, COL8_000000, 60, y - 24, 60, y - 3);boxfill8(vram, x, COL8_848484, x - 47, y - 24, x - 4, y - 24);boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y - 4);boxfill8(vram, x, COL8_FFFFFF, x - 47, y - 3, x - 4, y - 3);boxfill8(vram, x, COL8_FFFFFF, x - 3, y - 24, x - 3, y - 3);return;
}/* 显示8x16点阵字体 */
// c 填的是颜色
void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{int i;char *p, d;for (i = 0; i < 16; i++) { // 遍历16行p = vram + (y + i) * xsize + x; // 计算当前行起始地址d = font[i]; // 获取点阵数据// 检查每个bit位是否置1if ((d & 0x80) != 0) p[0] = c; // 第1像素if ((d & 0x40) != 0) p[1] = c; // 第2像素if ((d & 0x20) != 0) { p[2] = c; }if ((d & 0x10) != 0) { p[3] = c; }if ((d & 0x08) != 0) { p[4] = c; }if ((d & 0x04) != 0) { p[5] = c; }if ((d & 0x02) != 0) { p[6] = c; }if ((d & 0x01) != 0) { p[7] = c; }}return;
}
主要功能模块说明:
- 硬件交互 :通过 io_ 系列函数实现端口操作和中断控制
- 颜色管理 :16色调色板配置,包含亮色/暗色系
- 显存操作 :
- boxfill8 实现矩形填充
- putfont8 实现字符显示(8像素宽 x 16像素高)
- 屏幕布局 : init_screen 创建窗口化界面效果
- 引导信息 :通过 BOOTINFO 结构体获取屏幕参数
该代码实现了操作系统的图形界面基础功能,包括:
- 调色板初始化
- 屏幕绘制
- 字符显示
- 硬件中断管理
- 显存直接操作
naskfunc.nas
; 操作系统底层硬件操作接口
; 编译参数:nask -o naskfunc.obj naskfunc.nas[FORMAT "WCOFF"] ; 生成Windows COFF格式目标文件
[INSTRSET "i486p"] ; 支持486保护模式指令集
[BITS 32] ; 生成32位代码
[FILE "naskfunc.nas"] ; 源文件名(调试信息用); 全局函数声明(供C语言调用)
GLOBAL _io_hlt, _io_cli, _io_sti, _io_stihlt
GLOBAL _io_in8, _io_in16, _io_in32
GLOBAL _io_out8, _io_out16, _io_out32
GLOBAL _io_load_eflags, _io_store_eflags[SECTION .text] ; 代码段开始; --------------------------------------------------
; void io_hlt(void);
; 功能:暂停CPU直到中断发生
_io_hlt:HLT ; 执行HLT指令RET; --------------------------------------------------
; void io_cli(void);
; 功能:禁用中断(Clear Interrupt)
_io_cli:CLI ; 清除中断标志RET; --------------------------------------------------
; void io_sti(void);
; 功能:启用中断(Set Interrupt)
_io_sti:STI ; 设置中断标志RET; --------------------------------------------------
; void io_stihlt(void);
; 功能:启用中断并暂停CPU
_io_stihlt:STI ; 先启用中断HLT ; 然后暂停CPURET; --------------------------------------------------
; int io_in8(int port);
; 功能:从指定端口读取8位数据
_io_in8:MOV EDX,[ESP+4] ; 从栈中获取端口号(参数1)MOV EAX,0 ; 清空EAX高位IN AL,DX ; 从端口DX读取8位到ALRET ; 返回值通过EAX传递; --------------------------------------------------
; int io_in16(int port);
; 功能:从指定端口读取16位数据
_io_in16:MOV EDX,[ESP+4] ; 获取端口号MOV EAX,0 ; 清空EAX高位IN AX,DX ; 读取16位到AXRET; --------------------------------------------------
; int io_in32(int port);
; 功能:从指定端口读取32位数据
_io_in32:MOV EDX,[ESP+4] ; 获取端口号IN EAX,DX ; 读取32位到EAXRET; --------------------------------------------------
; void io_out8(int port, int data);
; 功能:向指定端口写入8位数据
_io_out8:MOV EDX,[ESP+4] ; 获取端口号(参数1)MOV AL,[ESP+8] ; 获取数据(参数2,8位)OUT DX,AL ; 向端口DX写入ALRET; --------------------------------------------------
; void io_out16(int port, int data);
; 功能:向指定端口写入16位数据
_io_out16:MOV EDX,[ESP+4] ; 获取端口号MOV EAX,[ESP+8] ; 获取数据(16位)OUT DX,AX ; 写入AX到端口RET; --------------------------------------------------
; void io_out32(int port, int data);
; 功能:向指定端口写入32位数据
_io_out32:MOV EDX,[ESP+4] ; 获取端口号MOV EAX,[ESP+8] ; 获取数据(32位)OUT DX,EAX ; 写入EAX到端口RET; --------------------------------------------------
; int io_load_eflags(void);
; 功能:读取EFLAGS寄存器值
_io_load_eflags:PUSHFD ; 将EFLAGS压入栈POP EAX ; 弹出到EAX作为返回值RET; --------------------------------------------------
; void io_store_eflags(int eflags);
; 功能:设置EFLAGS寄存器值
_io_store_eflags:MOV EAX,[ESP+4] ; 获取参数值PUSH EAX ; 压入栈POPFD ; 弹出到EFLAGSRET
相关文章:
至此(day1-day4)代码详解(ai辅助整理)
至此(day1-day4)代码详解 ipl10.nas ; 第一阶段引导程序 ; 功能:读取磁盘数据并跳转到第二阶段加载程序 ; 编译参数:nask -o ipl10.bin ipl10.nasCYLS EQU 10 ; 预设读取柱面数(实际值由BIOS决定)ORG…...
STM32F103_LL库+寄存器学习笔记12.2 - 串口DMA高效收发实战2:进一步提高串口接收的效率
导言 通过优化代码算法,在串口空闲中断回调里不需要暂时关闭DMA接收,达到提高串口接收的效率。在IDLE接收中断里关闭DMA接收会导致接收过程中有数据丢失风险(关DMA的瞬间如果有数据到来,会丢帧!)。 回顾一…...
conda 设置env后,环境还是安装在c盘的解决方式:
1|设置 envs 文件夹权限 右键【envs】文件夹,选择【属性】 选择【安全】,点击【编辑】 选中【Users(用户名\Users)】,选中运行所有权限,如图所示 点击【确认】,确保修改被保存 2、环境变量path设置 选择【高级系统设置…...
设计模式 - 工厂模式
简单工厂模式 public class CoffeeFactory {public Coffee get(string coffeeType) {Coffee coffee null;if ("American".equals(coffeeType)) {coffee new AmericanCoffee();} else if ("Latte".equals(coffeeType)) {coffee new LatteCoffee();}retur…...
动态规划-LCR 090.打家劫舍II-力扣(LeetCode)
一、题目解析 本题与打家劫舍的最大区别在于房子不是线性分布的了,而是首尾相连的环形分布,即如果偷了第一间房子,那么最后一间房子就不能偷了,因为它们是相连的。 二、算法原理 在分析之前我们可以先讨论上面提到的第一间房子偷…...
2025 年暑假 LBE 大空间市场火爆程度预测:技术驱动与消费升级下的增长引擎
一、市场爆发的底层逻辑 根据 DeepSeek 行业报告显示,2025 年 LBE 大空间市场将呈现结构性爆发,核心驱动力来自三大技术突破: 空间计算能力跃迁:上海移动已开通全球最大规模商用 5G-A 3CC 网络,主城区及十大重点场景…...
【AI 大模型】盘古大模型简介 ( 创建空间 | 体验模型 | 部署模型 )
文章目录 一、盘古大模型简介1、创建空间2、体验模型3、部署模型 总结 : 盘古大模型 是 开发部署 盘古基础模型 , 或 在 盘古模型 基础上进行 微调训练 的 大模型 的平台 , 是 开发训练 大模型的平台 ; 不适合 中小企业 和 个人开发者 开发 大模型应用 ; 一、盘古大模型简介 1、…...
2025年护网行动蓝队防御全解析:构建智能动态防御体系
2025年,随着网络攻击手段的智能化、混合化升级,护网行动中的蓝队防御已从传统的被动防护转向“动态感知、智能研判、主动反制”的立体化模式。如何在攻防不对称的对抗中实现“看得见、防得住、溯得清”?本文将结合前沿技术与实战经验…...
【Java高阶面经:微服务篇】3.熔断机制深度优化:从抖动治理到微服务高可用架构实战
一、熔断抖动的本质剖析与核心成因 1.1 熔断机制的核心价值与抖动危害 熔断机制作为微服务弹性架构的核心组件,通过模拟电路断路器逻辑,在服务出现异常时自动阻断请求链,防止故障扩散引发雪崩。但频繁的“熔断-恢复-熔断”抖动会导致: 用户体验恶化:请求成功率波动大,响…...
HTML回顾
html全称:HyperText Markup Language(超文本标记语言) 注重标签语义,而不是默认效果 规则 块级元素包括: marquee、div等 行内元素包括: span、input等 规则1:块级元素中能写:行内元素、块级元素(几乎什么都能写) 规则2:行级元素中能写:行内元素,但不能写:块…...
Leetcode百题斩-字典树
208. Implement Trie (Prefix Tree)[medium] 做完了哈希,来看看数据结构,做做字典树。字典树在搜索方面的作用还是蛮大的,主要是能实现前缀联想以及正确性匹配相关的功能。 字典树又名前缀树,顾名思义就是维护字符串的前缀。这个…...
大数据Spark(五十九):Standalone集群部署
文章目录 Standalone集群部署 一、节点划分 二、搭建Standalone集群 1、将下载好的Spark安装包上传解压 2、配饰spark-env.sh 3、配置workers 4、将配置好的安装包发送到node2、node3节点上 5、启动Standalone集群 三、提交任务测试 Standalone集群部署 Standalone 模…...
Vue 3 ~ 3.5 版本useTemplateRef使用
注意,useTemplateRef版本要在 3.5 以后才可使用,版本低的 ref 替代问题也不大~ 2024 年 9 月 1 日发布的 组合式 API:辅助 | Vue.js,引入一个小小的新 API useTemplateRef(),它用于访问实际的 DOM 节点。 …...
使用F5-tts复刻音色
最近第一人称视角的视频很火,想试试看复刻一下电视剧中某个角色的音色。看了下字节的API,嗯。。。138元一个音色,还不包括合成语音的费用,算了还是看看开源项目吧。 随便搜了搜,发现了两个项目一个是openvoice&#x…...
使用亮数据代理IP+Python爬虫批量爬取招聘信息训练面试类AI智能体(附完整源码)
文章目录 一、为什么要用代理IP?(重要!)二、环境准备(5分钟搞定)三、爬虫核心代码解析(含反反爬技巧)四、数据清洗的3个关键步骤五、训练AI智能体的实战技巧六、法律风险防范(必须看!…...
[软件工程]第二章题目汇总
1 [单选题] 原型化模型是( )。 A、适用于客户需求被明确定义的情况 B、很难产生有意义产品的一种冒险模型 C、提供一个精确表述的形式化规格说明 D、适用于客户需求难以清楚定义的情况 2 [单选题] 下列关于增量模型的说法正确的是( &…...
Java EE进阶1:导读
1.发展历程 2.学习内容 前⾯的课程中,学习的是Java基础,JavaEE主要学习Java的应用,也就是学习Java在企业中是如何应用的 Java更多场景是业务开发,更狭义点可以理解为web开发.所以咱们的学习也是围绕着如何使用Java来做web开发 2.1 什么是Web开发? web(…...
Unity自定义shader打包SpriteAtlas图集问题
Unity打包图集还是有一些坑的,至于图集SpriteAtlas是什么请参考我之前写的文章:【Sprite Atlas】Unity新图集系统SpriteAtlas超详细使用教程_spriteatlas 使用-CSDN博客 问题: 今天碰到的问题是,shader绘制的时候,因…...
系统集成项目管理工程师学习笔记之启动过程组
第十章 启动过程组 制定项目章程 定义 制定项目章程是编写一份正式批准项目并授权项目经理在项目活动中使用组织资源的文件的过程。 正式批准的项目文件 作用 1、明确项目与组织战略目标之间的直接联系 2、确立项目的正式地位 3、展示组织对项目的承诺 本过程仅开展一…...
vscode 常用调试
一、文件执行 python script.py {"name": "Python 调试程序: 当前文件","type": "debugpy","request": "launch","program": "${file}","console": "integratedTerminal"…...
Java 07异常
异常 指的是程序在编译和执行的过程中,出现的非正常的情况; 当然语法错误并不属于错误异常体系 最大的Throwable; 分为两个:Error ExceptionError 严重级别问题 常见的 堆内存溢出 栈内存溢出Exception 分为两个子类 RuntimeException 运…...
2025年PMP 学习二十三 16章 高级项目管理
2025年PMP 学习二十三 16章 高级项目管理 文章目录 2025年PMP 学习二十三 16章 高级项目管理高级项目管理战略管理战略管理的组成要素:企业战略转化为战略行动的阶段: 组织战略类型战略组织类型组织级项目管理OPM(公司项目管理) 组…...
【Java高阶面经:微服务篇】1.微服务架构核心:服务注册与发现之AP vs CP选型全攻略
一、CAP理论在服务注册与发现中的落地实践 1.1 CAP三要素的技术权衡 要素AP模型实现CP模型实现一致性最终一致性(Eureka通过异步复制实现)强一致性(ZooKeeper通过ZAB协议保证)可用性服务节点可独立响应(支持分区存活)分区期间无法保证写操作(需多数节点可用)分区容错性…...
ISCC 2025决赛 wp
PWN Dilemma 64位程序没有开启PIE,并且过滤了execve,不能使用system这些的了,所以要考虑ORW来做 进入main函数分析,这里有两个函数一个func_1一个func_2。 这两个函数都有漏洞,以下是详细分析: 对于func…...
C++(5)switch语句 循环while
这是一个电影评分的程序 default 就是 如果上述的都没有执行 就统一的执行default的内容。 然后记得break ___________________________________ 循环 (while) while的使用方式 输出 0-9的while循环...
操作系统----软考中级软件工程师(自用学习笔记)
目录 1、计算机系统层次结构 2、程序顺序执行的特征 3、程序并发执行的特征 4、三态模型 5、同步与互斥 6、信号量机制 7、PV操作 8、死锁 9、进程资源图 10、死锁避免 11、线程 12、程序局部性原理 13、分页存储管理 14、单缓冲器 15、双缓冲区 16、磁盘调度算…...
利用Spring Boot和Redis构建高性能缓存系统
利用Spring Boot和Redis构建高性能缓存系统 引言 在现代Web应用中,缓存是提升系统性能的关键技术之一。Redis作为一种高性能的内存数据库,广泛应用于缓存场景。本文将介绍如何利用Spring Boot和Redis构建一个高性能的缓存系统,涵盖Redis的基…...
每日一题:1、虚拟IPv4地址转换为32位整数(JS)
题目背景 我们需要处理一种特殊的虚拟IPv4地址,这种地址由4个小节组成,每节之间用#分隔。与标准IPv4地址不同,虚拟IPv4地址的第一节范围是1~128,后三节的范围是0~255。我们需要将这种虚拟IPv4地址转换为一个唯一的32位整数。如果…...
[Vue]组件介绍和父子组件间传值
组件介绍 Vue3的 .vue文件中的主要部分分别分为三个:<template>、<script>、<style> <template>: 结构,相当于原html中的<head><body><footer>部分。原本的index.html现在只做一个容器࿰…...
Vue3 中使用 provide/inject 实现跨层级组件传值失败的原因及解决方案
1、基础用法 父组件: <script setup> import { ref, provide } from vue; import ChildComponent from ./ChildComponent.vue; const parentData ref(初始数据); // 提供数据 provide(parentData, parentData); </script>子组件: <sc…...
Git Hooks 和 自动生成 Commit Message
前言: 企业编程必须始终依赖流程,而不是个人。个人能力很重要,应该鼓励,但不能指望它,否则软件质量将不一致,没有可持续性。一旦顶级程序员跳槽,公司就会陷入困境。企业应该努力改进工作流程&am…...
【小明剑魔视频Viggle AI模仿的核心算法组成】
Viggle AI 作为一款先进的生成式视频AI工具,其核心技术栈融合了多项前沿算法。以下是深度解析其核心算法架构及实现原理: 一、核心算法组成 1. 运动控制生成(Motion Control Generation) 算法框架:基于扩散模型&…...
Linux学习心得问题整理(二)
day05 Linux基础入门 Linux语法解析 如何理解ssh远程连接?如何使用ssh使用远程连接服务? ssh进也称远程服务终端,常见连接方式可以包括windows和Linux两种方式 首先咱们使用windows窗口进行连接,这里就采用xshell连接工具来给大家做演示吧…...
百度网盘加速补丁v7.14.1.6使用指南|PC不限速下载实操教程
软件介绍 本加速补丁可突破百度网盘限速限制,无需会员、无次数限制,实测下载速度可达带宽峰值。 三步极速配置教程 1. 环境准备 → 卸载电脑原有百度网盘客户端(避免冲突) → 关闭杀毒软件/安全卫士(防止误删补丁&am…...
RocketMQ消息拉取模式详解
RocketMQ提供了两种消息拉取模式,Pull模式(主动拉取)和 Push模式(长轮询)。 一、消息拉取模式分类 1. Pull模式(主动拉取) 特点:消费者主动向Broker发送请求拉取消息实现类&#…...
C++23 容器从其他兼容范围的可构造性与可赋值性 (P1206R7)
文章目录 背景与动机提案内容与实现细节提案 P1206R7实现细节编译器支持 对开发者的影响提高灵活性简化代码向后兼容性 总结 C23标准引入了对容器构造和赋值的新特性,这些特性使得容器能够更灵活地从其他兼容范围初始化,并支持从范围赋值。这些改进由提案…...
深入解析 HTTP 中的 GET 请求与 POST 请求
在互联网的世界里,数据的传输与交互无时无刻不在发生。HTTP(超文本传输协议)作为 Web 应用的基石,承载着浏览器与服务器之间的通信重任。而 GET 请求和 POST 请求,作为 HTTP 协议中最为常用的两种请求方法,…...
华三(H3C)IRF堆叠心跳的LACP MAD、BFD MAD和ARP MAD差异
华三(H3C)IRF堆叠心跳的三种MAD(多主检测)机制——LACP MAD、BFD MAD和ARP MAD在实现原理、组网要求及适用场景上存在显著差异。以下是三者的对比分析: 一、核心区别对比 特性LACP MADBFD MADARP MAD检测原理扩展LAC…...
thread 的mutex优化
std::mutex mtx; int shared_data 0;void increment() {std::lock_guard<std::mutex> lock(mtx); // 自动加锁shared_data; // 临界区 } // 离开作用域时自动解锁std::lock_guard 在离开作用域时自动解锁的行为是基于 C 的 RAII (Resource Acquisition Is Initializa…...
深入解析前端 JSBridge:现代混合开发的通信基石与架构艺术
引言:被低估的通信革命 在移动互联网爆发式增长的十年间,Hybrid App(混合应用)始终占据着不可替代的地位。作为连接 Web 与 Native 的神经中枢,JSBridge 的设计质量直接决定了应用的性能上限与开发效率。本文将突破传…...
打破次元壁,VR 气象站开启气象学习新姿势
在教育领域,VR 气象站同样发挥着巨大的作用,为气象教学带来了全新的模式,打破了传统教学的次元壁,让学生们以全新的姿势学习气象知识。 在传统的气象教学中,学生们主要通过课本、图片和老师的讲解来学习气象知识。这…...
python八股文汇总(持续更新版)
python装饰器 一、装饰器是什么? 装饰器是Python中一种"化妆师",它能在不修改原函数代码的前提下,给函数动态添加新功能。 本质:一个接收函数作为参数,并返回新函数的工具。作用:像给手机贴膜…...
C#入门系列【基础类型大冒险】从0到1,解锁编程世界的“元素周期表”
C#入门系列【基础类型大冒险】从0到1,解锁编程世界的“元素周期表” 嘿,欢迎来到C#的奇妙世界!如果把编程比作建造一座大厦,那么基础类型就是我们手中的“砖块”和“水泥”。它们看似普通,却构成了所有复杂程序的基石…...
物流项目第四期(运费模板列表实现)
前三期: 物流项目第一期(登录业务)-CSDN博客 物流项目第二期(用户端登录与双token三验证)-CSDN博客 物流项目第三期(统一网关、工厂模式运用)-CSDN博客 模板列表 在后台系统中,…...
数据中心Overlay解决方案
文档围绕数据中心 Overlay 解决方案展开,指出数据中心向大集中、虚拟化、云业务演进,传统架构存在网络规划复杂、弹性不足、业务扩展受限等问题。Overlay 网络在物理网络上构建虚拟网络,实现名址分离、网络与物理解耦,支持业务灵活部署。方案采用VXLAN 技术(如 SDN 控制模…...
中级网络工程师知识点8
1.无线控制器:实现无线网络统一管理,无缝漫游 2.无线认证系统:实现用户使用用户名和密码认证登录,外来访客通过扫描二维码或者手机短信验证登录无线网络 3.POE交换机:实现无线AP的接入和供电 4.高密吸顶式AP&#x…...
【Linux笔记】——简单实习一个日志项目
🔥个人主页🔥:孤寂大仙V 🌈收录专栏🌈:Linux 🌹往期回顾🌹: 【Linux笔记】——线程同步信号量与环形队列生产者消费者模型的实现(PV操作) 🔖流水不争…...
BRIGHTONE : 520-On-Chain WOHOO Carnival
BRIGHTONE is launching the “520-On-Chain WooHoo Carnival,” and the very first blast of $WOOHOO goes live right on schedule—ushering in a new on-chain celebration of joy! At exactly 21:09 on May 20, the “520-On-Chain WooHoo Carnival” officially kicks…...
在Java项目中集成Deepseek大语言模型实践指南
1. 引言 随着人工智能技术的发展,大语言模型在各领域应用日益广泛。本文将详细介绍如何在Java项目中集成Deepseek大模型,实现智能文本生成、对话等功能。 2. 前期准备 准备Java Spring Boot项目环境确保Maven已配置注册Deepseek账号并获取API密钥 获取a…...
医疗影像中,DICOM点云、三角面片实体混合渲染(VR)
此文章,涉及到专业性比较强,所以,大部分的内容,基本上都是示例代码的形式出现。以下的技术路径,完全经过实践验证,并且效果很好,可以放心使用。 1 概述 在医学影像中,对DICOM的渲染…...