第三天 - 32位模式与C语言

32位模式与C语言

一、32位模式

1. 制作IPL(Initial Program Loader)

  • 启动区 - 磁盘最初的512个字节是启动区

  • IPL - 启动程序装载器,装在启动区内,用于加载真正的操作系统

汇编代码解析:

; ipl.nas
; hello-os
; TAB = 4

		ORG		0x7c00				; 指明程序的装载地址

; 以下的记述用于标准FAT12格式的软盘

		JMP		entry
		DB		0x90
		DB		"HARIBOTE"			; 启动区的名称可是任意的字符串
		DW		512					; 每个扇区(sector)的大小(必须为512字节)
		DB		1					; 簇(cluster)的大小(必须为1个扇区)
		DW		1					; FAT12的起始位置(一般从第一个扇区开始)
		DB		2					; FAT的个数(必须为2)
		DW		224					; 根目录的大小(一般设成224项)
		DW		2880				; 该磁盘的大小(必须是2880扇区)
		DB		0xf0				; 磁盘的种类(必须是0xf0)
		DW		9					; FAT的长度(必须是9扇区)
		DW		18					; 一个磁道(track)有几个扇区(必须是18)
		DW		2					; 磁头数(必须是2)
		DD		0					; 不使用分区,必须是0
		DD		2880				; 重写一次磁盘大小
		DB		0,0,0x29			; 意义不明固定
		DD		0xffffffff			; (可能是)卷标号码
		DB		"HARIBOTEOS "		; 磁盘的名称(11字节)
		DB		"FAT12   "			; 磁盘格式名称
		RESB	18					; 先空出18字节

; 程序主体

entry:
		MOV		AX,0				; 初始化寄存器
		MOV		SS,AX
		MOV		SP,0x7c00
		MOV		DS,AX

; 读光盘

		MOV		AX,0X0820
		MOV		ES,AX
		MOV		CH,0				; 柱面0
		MOV		DH,0				; 磁头0
		MOV		CL,2				; 扇区2

		MOV		AH,0x02				; AH=0x02 : 读盘
		MOV		AL,1				; 1个扇区
		MOV		EX,0
		MOV		DL,0x00				; A驱动器
		INT		0x13				; 调用磁盘BIOS
		JC		error

; 虽然读完了,但是因为暂时没有要做的事所以停止等待指令

fin:
		HLT							; 让CPU停止,等待指令
		JMP		fin					; 无限循环

error:
		MOV		SI,msg

putloop:
		MOV		AL,[SI]
		ADD		SI,1				; 给SI加1
		CMP		AL,0
		JE		fin
		MOV		AH,0x0e				; 显示一个文字
		MOV		BX,15				; 指定字符颜色
		INT		0x10				; 调用显卡BIOS
		JMP		putloop

msg:
		DB		0x0a, 0x0a			; 换行两次
		DB		"load error"
		DB		0x0a
		DB		0
		RESB	0x7dfe-$
		DB		0x55, 0xaa

读光盘代码分析 - 把第二扇区的内容加载到 ox0820的内存地址

  • 0x7c00 ~ 0x7dff 这一个扇区(512个字节)用于存储启动区代码
  • 之所以加载到0x08000的位置,指示因为 0x7e00 ~ 0x9fbff 的内存暂无用途,所以使用空闲的0x8000
MOV      AX,0x0820
MOV      ES, AX
MOV      CH,0              ; 柱面0
MOV      DH,0              ; 磁头0
MOV      CL,2              ; 扇区2

MOV      AH,0x02          ; AH=0x02 : 读盘
MOV      AL,1              ; 1个扇区
MOV      BX,0
MOV      DL,0x00          ; A驱动器
INT      0x13              ; 调用磁盘BIOS
JC       error
  • JC指令
    • jump if carry
    • 如果进位标志为1 (carry flag) 则跳转
  • INT 0x13中断命令

BIOS 磁盘读写内容

;磁盘读写,扇区校验,寻道
AH = 0x02 #读盘
AH = 0x03 #写盘
AH = 0x04 #校验
AH = 0x0c # 寻道
AL = 处理对象的扇区数(连续的扇区)
CH = 柱面号
CL = 扇区号
DH = 磁头号
DL = 驱动号
ES:BX = 缓冲地址  (校验及寻道时不使用)

返回值:
FLACS.CF = 0 : 没有错误 AH = 0
FLACS.CF = 1:有错误,错误值写入AH(与重置(reset)功能一样)
  • FLACS.CF 进位标志

    • 调用这个读盘函数后,如果没错,进位标志位0,否则为1,所以使用JC指令
  • 磁头

  • 扇区

    • 一个扇区512个字节
  • 含有IPL的启动区 是 柱面0,磁头0,扇区1 ( C0-H0S1),下一个要装载的扇区是C0-H0-S2

  • 缓冲区地址

    • 要将软盘的数据装载到内存中的位置
    • BX只能表示64KB的内存,SI是辅助寄存区,指定一个ES x 16 + BX 的内存地址
  • 但我们要指定内存的地址时,必须同时指定段寄存器,一般把ES作为段寄存器

    • 写MOV CX, [1234] == MOV CX, [ES:1234]
    • 所以做好ES预先设置为0
  • 进位标志

    • flag,常用于报告BIOS函数调用是否有错误,进位为0则正确,进位为1则错误

2. 读盘试错代码分析

;读磁盘

        MOV      AX,0x0820
        MOV      ES, AX
        MOV      CH,0              ; 柱面0
        MOV DH,0 ; 磁头0
        MOV CL,2 ; 扇区2

        MOV SI,0 ; 记录失败次数的寄存器
retry:
        MOV      AH,0x02          ; AH=0x02 : 读入磁盘
        MOV      AL,1              ; 要处理的扇区数为1
        MOV      BX,0
        MOV      DL,0x00          ; A驱动器
        INT      0x13              ; 调用磁盘BIOS
        JNC      fin               ; 没出错的话跳转到fin
        ADD      SI,1              ; 往SI加1
        CMP      SI,5              ; 比较SI与5
        JAE      error             ; SI >= 5时,跳转到error
        MOV      AH,0x00
        MOV      DL,0x00          ; A驱动器
        INT      0x13              ; 重置驱动器
        JMP      retry
  • JNC命令
    • 跳转指令 - Jump if not carry
    • 进位为0则跳转
  • JAE
    • jump if above or equal
    • 大于或等于则跳转
  • 在读盘出错后,查看是否超过错误次数,没有则调用AH,DL指令,操作进行系统复位,恢复软盘的状态,然后跳转到 retry: 标号处再次读盘

3. 读18扇区的代码

; 读磁盘

		MOV		AX,0x0820
		MOV		ES,AX
		MOV		CH,0			; 柱面0
		MOV		DH,0			; 磁头0
		MOV		CL,2			; 扇区2
readloop:
		MOV		SI,0			; 记录失败次数的寄存器
retry:
		MOV		AH,0x02			; AH=0x02 读盘
		MOV		AL,1			; 每次读取1个扇区
		MOV		BX,0
		MOV		DL,0x00			; A驱动
		INT		0x13			; BIOS
		JNC		next			; 没出错则跳转到 next
		ADD		SI,1			; SI +1
		CMP		SI,5			; SI与5比較
		JAE		error			; SI >= 5 跳转到error
		MOV		AH,0x00
		MOV		DL,0x00			;A驱动
		INT		0x13			; 重置驱动器
		JMP		retry
next:
		MOV		AX,ES			; 把内存地址后移一个扇区大小0x200
		ADD		AX,0x0020
		MOV		ES,AX			; ADD ES,0x020
		ADD		CL,1			; CL + 1
		CMP		CL,18			; 比较18
		JBE		readloop		; CL <= 18 跳转到readloop

  • JBE指令

    • jump if below or equal
    • 小于等于则跳转
  • next 操作

    • 读下一个扇区 CL+1
    • ES指定读入地址,加上512字节,512/16 == 0x20
  • 上述操作结束后,我们已经把18个磁盘的内容装载到0x8200 - 0xa3ff

4. 读入10个柱面

  • 读取柱面c0-H0-s1 到 C9-H1-S18
; 读磁盘

		MOV		AX,0x0820
		MOV		ES,AX
		MOV		CH,0			; 柱面 0
		MOV		DH,0			; 磁头 0
		MOV		CL,2			; 扇区 2
readloop:
		MOV		SI,0			; 记录失败次数的寄存器
retry:
		MOV		AH,0x02			; AH=0x02 : 读入磁盘
		MOV		AL,1			; 1 个扇区
		MOV		BX,0
		MOV		DL,0x00			; A 驱动器
		INT		0x13			; 调用磁盘BIOS
		JNC		next			; 没出错时跳转到next
		ADD		SI,1			; SI 加 1
		CMP		SI,5			; 比较 SI 与 5
		JAE		error			; SI >= 5 时,跳转到error
		MOV		AH,0x00
		MOV		DL,0x00			; A 驱动器
		INT		0x13			; 重置驱动器
		JMP		retry
next:
		MOV		AX,ES			; 把内存地址后移0x200
		ADD		AX,0x0020
		MOV		ES,AX			; 因为没有 ADD ES,0x020 指令,所以这里稍微绕个弯
		ADD		CL,1			; CL 加 1
		CMP		CL,18			; 比较 CL 与 18
		JBE		readloop		; 如果 CL <= 18,则跳转至readloop 
		MOV		CL,1
		ADD		DH,1			; 磁头+1
		CMP		DH,2			; 判断磁头数量
		JB		readloop		; 上下两个磁头,如果 DH < 2, 则跳转到readloop
		MOV		DH,0
		ADD		CH,1
		CMP		CH,CYLS			; 判断柱面数量
		JB		readloop		; 如果 CH < CYLS,则跳转至readloop
  • JB指令

    • jump if below
    • 小于则跳转
  • EQU指令

    • 相当于#define,用于声明常数,前面定义了CYLS
    • 声明CYLS为常数10
  • 这个程序就从软盘读取10个柱面 即 10 * 2 * 18 * 512, = 180KB内容装载到内存中,填满了0x8200 = 0x34fff

5. 开发操作系统

  • 前面完成了启动区的制作,编写一个简单的程序

    • ;haribote.nas 汇编文件
      
      fin:
          HLT
          JMP fin
      
  • 将文件保存到磁盘映像的操作

    • make 指令 先将磁盘映像文件写入磁盘

    • make img

    • 0x002600附近,磁盘的这个位置保存着文件名:haribote.sys,

    • 0x004200那里,可以看到“F4 EB FD”

    • 查看二进制代码

    • 在windows文件系统找到磁盘,将haribote.sys保存到磁盘

    • 使用工具将磁盘备份作为磁盘映像

总结

  • 一般向一个空软盘中保存文件时

    • 文件名写在0x002600以后的地方

    • 文件的内容写在0x004200以后的地方

  • 下一步工作

    • 就是将系统本身的内容写到名为haribote.sys文件中,将这个文件保存到磁盘映像中,然后在启动区执行这个文件

6,从启动区执行操作系统

  • 如何执行磁盘映像上,位于地址是0x004200中程序?

  • 程序从启动区开始,把磁盘的内容 (布娃娃系统的代码) 装载到内存地址0x8000处

  • 该文件内容在磁盘中地址是0x00420,那么在该haribote.sys文件内容在内存地址 0x8000+0x4200=0xc200

  • 给haribote.sys代码加上 org 0xc200,给 ipl.nas 加上 jmp 0xc200

  • haribote.nas 汇编文件 代码如下:

    • ;haribote.nas 汇编文件
      ; haribote-os
      ; TAB=4
      
      		ORG		0xc200			; 指示这个程序会装载到什么地方
      
      		MOV		AL,0x13			; VGA显卡,320x200x8位彩色
      		MOV		AH,0x00
      		INT		0x10
      fin:
      		HLT
      		JMP		fin
      
      
  • 结果暂无输出

    • image-20230314214218068

7. 调用BIOS中断,切换显示模式,实现显示全黑

  • 设置显卡模式

    • ; haribote-os
      ; TAB=4
      
              ORG      0xc200           ; 这个程序将要被装载到内存的什么地方呢?
      
              MOV      AL,0x13          ; VGA显卡,320x200x8位彩色
              MOV      AH,0x00          ; 功能00h,设置显示器模式
              INT      0x10
      fin:
              HLT
              JMP      fin
      
    • AH 设置为 Ox00

    • image-20230314215347455

阶段运行

haribote.nas 文件
; haribote-os
; TAB=4

        ORG      0xc200           ; 这个程序将要被装载到内存的什么地方呢?

        MOV      AL,0x13          ; VGA显卡,320x200x8位彩色
        MOV      AH,0x00          ; 功能00h,设置显示器模式
        INT      0x10
fin:
        HLT
        JMP      fin
ipl.nas 启动区文件
; haribote-ipl
; TAB=4

CYLS	EQU		10				; 要读取到什么程度

		ORG		0x7c00			; 启动装载程序

; 以下记述用于标准FAT12格式软盘

		JMP		entry
		DB		0x90
		DB		"HARIBOTE"		; 磁盘名称(可以是任意字符串)
		DW		512				; 每个扇区的大小(必须是512)
		DB		1				; 簇的大小(必须为一个扇区)
		DW		1				; FAT12的起始位置(一般从第一个扇区开始
		DB		2				; FAT的个数(必须为2)
		DW		224				; 根目录的大小(一般设成224项)
		DW		2880			; 该磁盘的大小(必须是2880扇区)
		DB		0xf0			; 该磁盘的种类(必须是0xf0
		DW		9				; FAt的长度(必须是9扇区)
		DW		18				; 一个磁道有几个扇区(必须是18)
		DW		2				; 磁头数(必须是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			; 初始化寄存器
		MOV		SS,AX
		MOV		SP,0x7c00
		MOV		DS,AX

; 读磁盘

		MOV		AX,0x0820
		MOV		ES,AX
		MOV		CH,0			; 柱面 0
		MOV		DH,0			; 磁头 0
		MOV		CL,2			; 扇区 2
readloop:
		MOV		SI,0			; 记录失败次数的寄存器
retry:
		MOV		AH,0x02			; AH=0x02 : 读入磁盘
		MOV		AL,1			; 1 个扇区
		MOV		BX,0
		MOV		DL,0x00			; A 驱动器
		INT		0x13			; 调用磁盘BIOS
		JNC		next			; 没出错时跳转到next
		ADD		SI,1			; SI 加 1
		CMP		SI,5			; 比较 SI 与 5
		JAE		error			; SI >= 5 时,跳转到error
		MOV		AH,0x00
		MOV		DL,0x00			; A 驱动器
		INT		0x13			; 重置驱动器
		JMP		retry
next:
		MOV		AX,ES			; 把内存地址后移0x200
		ADD		AX,0x0020
		MOV		ES,AX			; 因为没有 ADD ES,0x020 指令,所以这里稍微绕个弯
		ADD		CL,1			; CL 加 1
		CMP		CL,18			; 比较 CL 与 18
		JBE		readloop		; 如果 CL <= 18,则跳转至readloop 
		MOV		CL,1
		ADD		DH,1
		CMP		DH,2
		JB		readloop		; 如果 DH < 2, 则跳转到readloop
		MOV		DH,0
		ADD		CH,1
		CMP		CH,CYLS
		JB		readloop		; 如果 CH < CYLS,则跳转至readloop

; 因为看完了实行haribote.sys

		MOV		[0x0ff0],CH		; IPL读到什么地方结束
		JMP		0xc200

error:
		MOV		SI,msg
putloop:
		MOV		AL,[SI]
		ADD		SI,1			; SI 加 1
		CMP		AL,0
		JE		fin
		MOV		AH,0x0e			; 显示一个文字
		MOV		BX,15			; 指定字符颜色
		INT		0x10			; 调用显卡BIOS
		JMP		putloop
fin:
		HLT						; 让CPu停止,等待指令
		JMP		fin				; 无限循环
msg:
		DB		0x0a, 0x0a		; 换行两次
		DB		"load error"
		DB		0x0a			; 换行
		DB		0

		RESB	0x7dfe-$		; 重复0x00一直到0x7dfe

		DB		0x55, 0xaa
  • 为了把磁盘装载内容的结束地址告诉 haribote.sys, 在 JMP 0xc200之前,把 CYLS常量的值写入内存地址 0x0ff0
Makefile文件
TOOLPATH = ../z_tools/
MAKE     = $(TOOLPATH)make.exe -r
NASK     = $(TOOLPATH)nask.exe
EDIMG    = $(TOOLPATH)edimg.exe
IMGTOL   = $(TOOLPATH)imgtol.com
COPY     = copy
DEL      = del

# 默认

default :
	$(MAKE) img

# 文件生成规则

ipl10.bin : ipl10.nas Makefile
	$(NASK) ipl10.nas ipl10.bin ipl10.lst

haribote.sys : haribote.nas Makefile
	$(NASK) haribote.nas haribote.sys haribote.lst

haribote.img : ipl10.bin haribote.sys Makefile
	$(EDIMG)   imgin:../z_tools/fdimg0at.tek \
		wbinimg src:ipl10.bin len:512 from:0 to:0 \
		copy from:haribote.sys to:@: \
		imgout:haribote.img

# 命令

img :
	$(MAKE) haribote.img

run :
	$(MAKE) img
	$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin
	$(MAKE) -C ../z_tools/qemu

install :
	$(MAKE) img
	$(IMGTOL) w a: haribote.img

clean :
	-$(DEL) ipl10.bin
	-$(DEL) ipl10.lst
	-$(DEL) haribote.sys
	-$(DEL) haribote.lst

src_only :
	$(MAKE) clean
	-$(DEL) haribote.img
编译 和 运行
  • 双击 !cons_nt.bat,并在打开的命令行中输入 make run

  • 效果:画面一片黑色

    • image-20230314215958835

8. 为32位模式做准备

  • BIOS功能是用16位机器语言写的,在32位模式下不能用,需要BIOS功能做的事情放在开头完成

    • 如获取键盘状态(数字小键盘 NumLock是ON还是OFF等功能)
  • 32位模式的优点

    • 可以使用的内存容量远远大于1MB
    • CPU的自我保护功能(识别出可疑的机器语言并进行屏蔽,以免破坏系统)在16位下不能用,但32位下能用
  • 在设置完画面模式后,需要将画面模式的信息保存在内存中,便于后续支持各种不同的画面模式时使用

    • ; haribote-os
      ; TAB=4
      
      ; 有关BOOT_INFO
      CYLS     EQU      0x0ff0           ; 设定启动区
      LEDS     EQU      0x0ff1
      VMODE   EQU      0x0ff2           ; 关于颜色数目的信息。颜色的位数。
      SCRNX   EQU      0x0ff4           ; 分辨率的X(screen x)
      SCRNY   EQU      0x0ff6           ; 分辨率的Y(screen y)
      VRAM     EQU      0x0ff8           ; 图像缓冲区的开始地址
      
              ORG      0xc200           ; 这个程序将要被装载到内存的什么地方呢?
              MOV      AL,0x13          ; VGA显卡,320x200x8位彩色
              MOV      AH,0x00
              INT      0x10
              MOV      BYTE [VMODE],8  ; 记录画面模式
              MOV      WORD [SCRNX],320
              MOV      WORD [SCRNY],200
              MOV      DWORD [VRAM],0x000a0000
      
      ;用BIOS取得键盘上各种LED指示灯的状态
              MOV      AH,0x02
              INT      0x16              ; keyboard BIOS
              MOV      [LEDS], AL
      
      fin:
              HLT
              JMP      fin
      
  • [VRAM]内存地址保存的是显卡内存(video RAM),用于显示画面的内存,保存起来备用

    • 显存在内存也分了几个位置,不同位置的显存用于不同的画图模式,显存的内存地址在BIOS中断函数中有说明

9. 导入C语言

  • 开始由汇编转入C语言,并提供了汇编代码去调用C语言代码(后续讲解)
  • haribote.sys 改名位 asmhead.nas, 前半部分用汇编,后半部分用C语言编写

asmhead.nas 文件

; haribote-os boot asm
; TAB=4

BOTPAK	EQU		0x00280000		; bootpack装载处
DSKCAC	EQU		0x00100000		; 磁盘缓存的地方
DSKCAC0	EQU		0x00008000		; 磁盘高速缓存的场所(实时模式)

; 有关BOOT_INFO
CYLS	EQU		0x0ff0			; 设定启动区
LEDS	EQU		0x0ff1
VMODE	EQU		0x0ff2			; 关于颜色数目的信息,颜色的位数
SCRNX	EQU		0x0ff4			; 分辨率的X(screen x)
SCRNY	EQU		0x0ff6			; 分辨率的Y(screen y)
VRAM	EQU		0x0ff8			; 图像缓冲区的开始地址

		ORG		0xc200			; 这个程序要被装载到内存的什么地方呢?

; 画面模式设定

		MOV		AL,0x13			; VGA显卡,320x200x8位彩色
		MOV		AH,0x00
		INT		0x10
		MOV		BYTE [VMODE],8	; 记录画面模式(C语言参照)
		MOV		WORD [SCRNX],320
		MOV		WORD [SCRNY],200
		MOV		DWORD [VRAM],0x000a0000

; 用BIOS取得键盘各种LED指示灯的状态

		MOV		AH,0x02
		INT		0x16 			; keyboard BIOS
		MOV		[LEDS],AL

; 使PIC不授受一切中断
;	如果要初始PIC的话,要在AT兼容的规范中,
;	如果不把这家伙放在CLI面前,我偶尔会举起来
; 	稍后进行PIC的初始化

        MOV		AL,0xff
        OUT		0x21,AL
        NOP						; 如果连续OUT命令的话,可能会有不太好的机型
        OUT		0xa1,AL

        CLI						; 甚至禁止CPU层面插队

; cpu从1 mb以上的内存,a20gate设定

        CALL	waitkbdout
        MOV		AL,0xd1
        OUT		0x64,AL
        CALL	waitkbdout
        MOV		AL,0xdf			; enable A20
        OUT		0x60,AL
        CALL	waitkbdout

; 保护模式过渡

[INSTRSET "i486p"]				; 用于记述想要使用的486命令

        LGDT	[GDTR0]			; 暂定GDT设定
        MOV		EAX,CR0
        AND		EAX,0x7fffffff	; 使bit31为0(为子禁止寻乎)
		OR		EAX,0x00000001	; 使bit0为1(为子保护模式转移)
        MOV		CR0,EAX
        JMP		pipelineflush
pipelineflush:
        MOV		AX,1*8			; 可读区段32bit
        MOV		DS,AX
        MOV		ES,AX
        MOV		FS,AX
        MOV		GS,AX
        MOV		SS,AX

; bootpack的传送

        MOV		ESI,bootpack	; 传输源
        MOV		EDI,BOTPAK		; 传输目的地
        MOV		ECX,512*1024/4
        CALL	memcpy

; 顺便磁盘数据也向原来的位置传送

; 首先从引导扇区

        MOV		ESI,0x7c00		; 传输源
        MOV		EDI,DSKCAC		; 传输目的地
        MOV		ECX,512/4
        CALL	memcpy

; 剩下的全部

        MOV		ESI,DSKCAC0+512	; 传输源
        MOV		EDI,DSKCAC+512	; 传输目的地
        MOV		ECX,0
        MOV		CL,BYTE [CYLS]
        IMUL	ECX,512*18*2/4	; 从柱面数转换成字节数 /4
        SUB		ECX,512/4		; 通过IPL减去
        CALL	memcpy

; 我们已经完成了需要使用asmhead 进行的所有操作
; 放到bootpack中

; 启动bootpack

        MOV		EBX,BOTPAK
		MOV		ECX,[EBX+16]
		ADD		ECX,3			; ECX += 3;
		SHR		ECX,2			; ECX /= 4;
		JZ		skip			; 无需转移
		MOV		ESI,[EBX+20]	; 传输源
		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		; 如果AND的结果不为0,请跳转至waitkbdou
		RET

memcpy:
		MOV		EAX,[ESI]
		ADD		ESI,4
		MOV		[EDI],EAX
		ADD		EDI,4
		SUB		ECX,1
		JNZ		memcpy			; 如果减法不为0,则返回memcpy
		RET
; memcpy也可以用字符串指令编写,除非你忘记了地址大小写前缀

		ALIGNB	16
GDT0:
		RESB	8				; 空选择器
		DW		0xffff,0x0000,0x9200,0x00cf	; 读/定段32bit
		DW		0xffff,0x0000,0x9a28,0x0047	; 可执行段32bit(用于bootpack)

		DW		0
GDTR0:
		DW		8*3-1
		DD		GDT0

		ALIGNB	16
bootpack:

10 . 实现HLT

汇编程序:naskfunc.nas

; naskfunc
; TAB=4

[FORMAT "WCOFF"]				; 制作目标文件的模式
[BITS 32]						; 制作32位模式用的机器语言


; 制作目标文件的信息

[FILE "naskfunc.nas"]			; 源文件名信息

		GLOBAL	_io_hlt			; 程序中包含的函数名


; 以下是实际的函数

[SECTION .text]		; 目标文件中写了这些之后再写程序

_io_hlt:	; void io_hlt(void)
		HLT
		RET
  • 代码解析
    • 汇编先声明一个与函数名相同的标号,然后开始写代码
    • RET命令
      • 相当于 return,表示函数结束,返回
    • 汇编函数前需要添加下划线,用于与C语言函数链接

bootpack.c 文件 - C语言程序使用汇编程序的函数

  • /* 告诉 C 编译器,有一个函数在别的文件里 */
    void io_hlt(void);
    /* 是函数声明却不用{},而用;,这表示的意思是:函数在别的文件中,你自己找一下吧! */
    void HariMain(void)
    {
    
    fin:
        io_hlt();   /* 执行naskfunc.nas里的_io_hlt */
        goto fin;
    }
    
posted @   bingekong  阅读(36)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示