⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 loader.asm

📁 自己编写的一个简单的操作系统
💻 ASM
字号:
;=============================================================================
;       Kitnix   ver 0.01
;					Write by Kit
;					2008-10-31
;
;		Loader.asm
;			Show the menu
;			Control the computer
;			Open the user file
;			Restart or Shutdown
;=============================================================================


;==============================================================================
;%define  _BOOT_DEBUG_

org 0100h
BaseOfStack			equ 0100h
BaseOfUserFile		equ 08000h
OffsetOfUserFile	equ	0100h

;==============================================================================		
	jmp LABEL_START
%include "fat12hdr.inc"
	

;==============================================================================	
LABEL_START:
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, BaseOfStack

again:
	mov ax, RootDirSectors				; 还原 wRootDirSizeForLoop
	mov [wRootDirSizeForLoop], ax		; Root Directory 占用的扇区数, 在循环中会递减至零.

	mov ax, BootMessage
	mov bp, ax
	mov ax, ds
	mov es, ax
	mov cx, 086H
	mov ax, 01301h
	mov bx, 000Bh
	mov dx, 0300H
	int 10H

	mov ah, 00H			; ┓
	int 16H				; ┣	键盘读入 + 回显 
	mov ah, 0EH			; ┃
	int 10H				; ┛

n1:
	cmp al, '1'
	jnz n2

	mov bx, FirstFileName
	mov [choice], bx
	jmp Find

n2:
	cmp al, '2'
	jnz n3

	mov bx, SecondFileName
	mov [choice], bx
	jmp Find

n3:
	cmp al, '3'
	jnz n4

	mov	ax, 0600h		; AH = 6,  AL = 0h
	mov	bx, 0700h		; 黑底白字(BL = 07h)
	mov	cx, 0			; 左上角: (0, 0)
	mov	dx, 0184fh		; 右下角: (80, 50)
	int	10h				; int 10h
	jmp again


n4:
	cmp al, '4'
	jnz n5

	jmp 0ffffh:0000h		; 重启

n5:
	cmp al, '5'
	jnz again
   
%ifdef	_BOOT_DEBUG_
	mov	ax, 4c00h		
	int	21h

%else
	; 关机实现
	mov ax,5301h	;Function 5301h: APM Connect real-mode interface
    xor bx,bx		;Device ID: 0000h (=system BIOS)
    int 15h			;Call interrupt: 15h

    mov ax,530eh	;Function 530Eh: APM Driver version
    mov cx,0102h	;Driver version: APM v1.2
    int 15h			;Call interrupt: 15h

    mov ax,5307h	;Function 5307h: APM Set system power state
    mov bl,01h		;Device ID: 0001h (=All devices)
    mov cx,0003h	;Power State: 0003h (=Off)
    int 15h			 ;Call interrupt: 15h
%endif


;----------------------------------------------------------------------------
; 函数名: Find
;----------------------------------------------------------------------------
; 作用:
;	在软盘中寻找 用户文件
Find:
	xor	ah, ah	; ┓
	xor	dl, dl	; ┣ 软驱复位
	int	13h		; ┛
	mov dh, 0
	call DispStr

	; 下面在 A 盘的根目录寻找 用户文件
	mov	word [wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
	cmp	word [wRootDirSizeForLoop], 0	; ┓
	jz	LABEL_NO_LOADERBIN				; ┣ 判断根目录区是不是已经读完
	dec	word [wRootDirSizeForLoop]		; ┛ 如果读完表示没有找到 用户文件
	mov	ax, BaseOfUserFile
	mov	es, ax							; es <- BaseOfUserFile
	mov	bx, OffsetOfUserFile				; bx <- OffsetOfUserFile	于是, es:bx = BaseOfUserFile:OffsetOfUserFile
	mov	ax, [wSectorNo]					; ax <- Root Directory 中的某 Sector 号
	mov	cl, 1
	call	ReadSector

	mov	si, [choice]						; ds:si -> 用户文件名
	mov	di, OffsetOfUserFile				; es:di -> BaseOfUserFile:0100 = BaseOfUserFile*10h+100
	cld
	mov	dx, 10h
LABEL_SEARCH_FOR_LOADERBIN:
	cmp	dx, 0										; ┓循环次数控制,
	jz	LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR			; ┣如果已经读完了一个 Sector,
	dec	dx											; ┛就跳到下一个 Sector
	mov	cx, 11
LABEL_CMP_FILENAME:
	cmp	cx, 0
	jz	LABEL_FILENAME_FOUND						; 如果比较了 11 个字符都相等, 表示找到
dec	cx
	lodsb											; ds:si -> al
	cmp	al, byte [es:di]
	jz	LABEL_GO_ON
	jmp	LABEL_DIFFERENT								; 只要发现不一样的字符就表明本 DirectoryEntry 不是我们要找的 用户文件
LABEL_GO_ON:
	inc	di
	jmp	LABEL_CMP_FILENAME							; 继续循环

LABEL_DIFFERENT:
	and	di, 0FFE0h									; else ┓	di &= E0 为了让它指向本条目开头
	add	di, 20h										;      ┃
	mov	si, [choice]								;      ┣ di += 20h  下一个目录条目
	jmp	LABEL_SEARCH_FOR_LOADERBIN					;	   ┛

LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
	add	word [wSectorNo], 1
	jmp	LABEL_SEARCH_IN_ROOT_DIR_BEGIN

LABEL_NO_LOADERBIN:
	mov	dh, 2				; "NO USER FILE."
	call	DispStr			; 显示字符串
	jmp again

LABEL_FILENAME_FOUND:			; 找到 用户文件 后便来到这里继续
	mov	ax, RootDirSectors
	and	di, 0FFE0h				; di -> 当前条目的开始
	add	di, 01Ah				; di -> 首 Sector
	mov	cx, word [es:di]
	push	cx					; 保存此 Sector 在 FAT 中的序号
	add	cx, ax
	add	cx, DeltaSectorNo		; 这句完成时 cl 里面变成 用户文件 的起始扇区号 (从 0 开始数的序号)
	mov	ax, BaseOfUserFile
	mov	es, ax					; es <- BaseOfUserFile
	mov	bx, OffsetOfUserFile		; bx <- OffsetOfUserFile	于是, es:bx = BaseOfUserFile:OffsetOfUserFile = BaseOfUserFile * 10h + OffsetOfUserFile
	mov	ax, cx					; ax <- Sector 号

LABEL_GOON_LOADING_FILE:
	push	ax					; ┓
	push	bx					; ┃
	mov	ah, 0Eh					; ┃ 每读一个扇区就在 "Loading  " 后面打一个点, 形成这样的效果:
	mov	al, '.'					; ┃
	mov	bl, 0Fh					; ┃ Loading ......
	int	10h						; ┃
	pop	bx						; ┃
	pop	ax						; ┛

	mov	cl, 1
	call	ReadSector
	pop	ax						; 取出此 Sector 在 FAT 中的序号
	call	GetFATEntry
	cmp	ax, 0FFFh
	jz	LABEL_FILE_LOADED
	push	ax					; 保存 Sector 在 FAT 中的序号
	mov	dx, RootDirSectors
	add	ax, dx
	add	ax, DeltaSectorNo
	add	bx, [BPB_BytsPerSec]
	jmp	LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:

	mov	 dh, 1					; "Ready."
	call DispStr				; 显示字符串

; *****************************************************************************************************
	jmp	BaseOfUserFile:OffsetOfUserFile	; 这一句正式跳转到已加载到内存中的 用户文件的开始处
						; 开始执行 用户文件 的代码
						; Find 的使命到此结束
; *****************************************************************************************************
	ret


;----------------------------------------------------------------------------
; 函数名: DispStr
;----------------------------------------------------------------------------
; 作用:
;	显示一个字符串
DispStr:
	mov ax, MessageLength
	mul dh
	add ax, LoadMessage
	mov bp, ax
	mov ax, ds
	mov es, ax
	mov cx, MessageLength
	mov ax, 01301h
	mov bx, 000Bh
	or dh, dh
	jz prn
	mov dh, 1
prn:
	add dh, 10
	xor dl, dl
	int 10H
	ret

;----------------------------------------------------------------------------
; 函数名: ReadSector
;----------------------------------------------------------------------------
; 作用:
;	从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中
ReadSector:
	; -----------------------------------------------------------------------
	; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)
	; -----------------------------------------------------------------------
	; 设扇区号为 x
	;                           ┌ 柱面号 = y >> 1
	;       x           ┌ 商 y ┤
	; -------------- => ┤      └ 磁头号 = y & 1
	;  每磁道扇区数     │
	;                   └ 余 z => 起始扇区号 = z + 1
	
	push bp
	mov bp, sp
	sub	esp, 2					; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]

	mov	byte [bp-2], cl
	push bx						; 保存 bx
	mov	bl, [BPB_SecPerTrk]		; bl: 除数
	div	bl						; y 在 al 中, z 在 ah 中
	inc	ah						; z ++
	mov	cl, ah					; cl <- 起始扇区号
	mov	dh, al					; dh <- y
	shr	al, 1					; y >> 1 (其实是 y/BPB_NumHeads, 这里BPB_NumHeads=2)
	mov	ch, al					; ch <- 柱面号
	and	dh, 1					; dh & 1 = 磁头号
	pop	bx						; 恢复 bx
	; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 
	
	mov	dl, [BS_DrvNum]			; 驱动器号 (0 表示 A 盘)
.GoOnReading:
	mov	ah, 2					; 读
	mov	al, byte [bp-2]			; 读 al 个扇区
	int	13h
	jc	.GoOnReading			; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止

	add	esp, 2
	pop	bp

	ret


;----------------------------------------------------------------------------
; 函数名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
;	找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中
;	需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx
GetFATEntry:
	push	es
	push	bx
	push	ax
	mov	ax, BaseOfUserFile	; ┓
	sub	ax, 0100h			; ┣ 在 BaseOfUserFile 后面留出 4K 空间用于存放 FAT
	mov	es, ax				; ┛
	pop	ax
	mov	byte [bOdd], 0
	mov	bx, 3
	mul	bx					; dx:ax = ax * 3
	mov	bx, 2
	div	bx					; dx:ax / 2  ==>  ax <- 商, dx <- 余数
	cmp	dx, 0
	jz	LABEL_EVEN
	mov	byte [bOdd], 1
LABEL_EVEN:;偶数
	xor	dx, dx				; 现在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面来计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)
	mov	bx, [BPB_BytsPerSec]
	div	bx					; dx:ax / BPB_BytsPerSec  ==>	ax <- 商   (FATEntry 所在的扇区相对于 FAT 来说的扇区号)
							;				dx <- 余数 (FATEntry 在扇区内的偏移)。
	push	dx
	mov	bx, 0				; bx <- 0	于是, es:bx = (BaseOfUserFile - 100):00 = (BaseOfUserFile - 100) * 10h
	add	ax, SectorNoOfFAT1	; 此句执行之后的 ax 就是 FATEntry 所在的扇区号
	mov	cl, 2
	call	ReadSector		; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界发生错误, 因为一个 FATEntry 可能跨越两个扇区
	pop	dx
	add	bx, dx
	mov	ax, [es:bx]
	cmp	byte [bOdd], 1
	jnz	LABEL_EVEN_2
	shr	ax, 4
LABEL_EVEN_2:
	and	ax, 0FFFh

LABEL_GET_FAT_ENRY_OK:

	pop	bx
	pop	es
	ret



;============================================================================
;字符串
;----------------------------------------------------------------------------
BootMessage:	db "Hello, this is Kit's World!", 13, 10
				db "1. First Program", 13, 10
				db "2. Second Program", 13, 10
				db "3. Clean Screen", 13, 10
				db "4. Restart", 13, 10
				db "5. Shutdown", 13, 10
				db "Please input your choose: "
FirstFileName   db "FIRST   BIN", 0				; 用户文件名1
SecondFileName  db "SECOND  BIN", 0				; 用户文件名2
LoadMessage:	db "Loading     "
SuccessMessage	db "File Ready  "
FailMessage		db "NO USER FILE"

;============================================================================
;变量
;----------------------------------------------------------------------------
wRootDirSizeForLoop	dw	0
wSectorNo			dw	0		; 要读取的扇区号
bOdd				db	0		; 奇数还是偶数
choice				dw  0
MessageLength		equ 12

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -