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

📄 loadera.asm

📁 boot loader bing--boot.asm for fdd and loader c++ -startup.asm
💻 ASM
字号:
; I tried dis-assembling the INT 15h AH=89h function in my BIOS.
; It doesn't appear to support 32-bit pmode.

SEGMENT _TEXT PUBLIC CLASS=CODE

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			_intr
; action:		replaces buggy Turbo C 2.0 intr() function
; in:			interrupt number and pointer to REGPACK on stack
; out:			(nothing)
; modifies:		REGPACK
; minimum CPU:		8088
; notes:		C proto: void intr(int intnum, struct REGPACK *preg);
;			Turbo C 2.0 intr() does not appear to load BP
;			register correctly
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

%if 1

GLOBAL _intr
_intr:
	push bp
		mov bp,sp

; save old registers
		pushf
		push es
		push ds
		push di
		push si
		push bp
		push dx
		push cx
		push bx
		push ax

; set interrupt vector, using self-modifying code
			mov bx,word [bp + 4]
			mov byte [cs:INT_NUM],bl

; load new registers
			mov bx,word [bp + 6]
			push bx
			push ds

			mov ax,word [bx + 0]	; [preg + 0]
			push word [bx + 2]	; [preg + 2]; BX
			mov cx,word [bx + 4]	; [preg + 4]
			mov dx,word [bx + 6]	; [preg + 6]
			mov bp,word [bx + 8]	; [preg + 8]
			mov si,word [bx + 10]	; [preg + 10]
			mov di,word [bx + 12]	; [preg + 12]
			push word [bx + 14]	; [preg + 14]; DS
			mov es,word [bx + 16]	; [preg + 16]
			push word [bx + 18]	; [preg + 18]; FLAGS

			popf
			pop ds
			pop bx

; perform interrupt (CDh = opcode for INT instruction)
			db 0CDh
INT_NUM:
			db 10h

; save new registers
			push bx
			push ds
			pushf

			mov bx,sp
			mov ds,[ss:bx + 6]
			mov bx,[ss:bx + 8]

			pop word [bx + 18]	; [preg + 18]; FLAGS
			mov word [bx + 16],es	; [preg + 16]
			pop word [bx + 14]	; [preg + 14]; DS
			mov word [bx + 12],di	; [preg + 12]
			mov word [bx + 10],si	; [preg + 10]
			mov word [bx + 8],bp	; [preg + 8]
			mov word [bx + 6],dx	; [preg + 6]
			mov word [bx + 4],cx	; [preg + 4]
			pop word [bx + 2]	; [preg + 2]; BX
			mov word [bx + 0],ax	; [preg + 0]

			add sp,4

; restore old registers
		pop ax
		pop bx
		pop cx
		pop dx
		pop bp
		pop si
		pop di
		pop ds
		pop es
		popf
	pop bp
	ret

%endif

%if 1

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			verify_a20
; action:		verifies A20 gate is enabled (ON)
; in:			(nothing)
; out (A20 enabled):	ZF=0
; out (A20 NOT enabled):ZF=1
; modifies:		(nothing)
; minimum CPU:		286+
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

verify_a20:
	push ax
	push ds
	push es
		xor ax,ax
		mov ds,ax
		dec ax
		mov es,ax

		mov ax,[es:10h]		; read word at FFFF:0010 (1 meg)
		not ax			; 1's complement
		push word [0]		; save word at 0000:0000 (0)
			mov [0],ax	; word at 0 = ~(word at 1 meg)
			mov ax,[0]	; read it back
			cmp ax,[es:10h]	; fail if word at 0 == word at 1 meg
		pop word [0]
	pop es
	pop ds
	pop ax
	ret		; if ZF=1, the A20 gate is NOT enabled

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			empty_8042
; action:		waits until 8042 keyboard controller ready
;			to accept a command or data byte
; in:			(nothing)
; out:			(nothing)
; modifies:		AL
; minimum CPU:		286+
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

empty:
	jmp short $+2	; a delay (probably not effective nor necessary)
	in al,60h	; read and discard data/status from 8042
empty_8042:
	jmp short $+2	; delay
	in al,64h
	test al,1	; output buffer (data _from_ keyboard) full?
	jnz empty	; yes, read and discard
	test al,2	; input buffer (data _to_ keyboard) empty?
	jnz empty_8042	; no, loop
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			enable_a20_at
; action:		enables A20 by setting b1 of 8042 output port
; in:			(nothing)
; out:			(nothing)
; modifies:		AX
; minimum CPU:		286+
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

enable_a20_at:
	pushf
		cli
		call empty_8042
		mov al,0D0h	; 8042 command byte to read output port
		out 64h,al
await:
		in al,64h
		test al,1	; output buffer (data _from_ keyboard) full?
		jz await	; no, loop

		in al,60h	; read output port
		or al,2		; OR with 2 to enable gate
		mov ah,al

		call empty_8042
		mov al,0D1h	; 8042 command byte to write output port
		out 64h,al

		call empty_8042
		mov al,ah	; the value to write
		out 60h,al

		call empty_8042
	popf
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			enable_a20_vectra
; action:		enables A20 with 8042 command byte DFh
; in:			(nothing)
; out:			(nothing)
; modifies:		AX
; minimum CPU:		286+
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

enable_a20_vectra:
	pushf
		cli
		call empty_8042
		mov al,0DFh
		out 64h,al
		call empty_8042
	popf
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			enable_a20_ps2
; action:		enables A20 gate using INT 15h AX=2401h
; in:			(nothing)
; out:			(nothing)
; modifies:		(nothing)
; minimum CPU:		286+
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

enable_a20_ps2:
	push ax
		mov ax,2401h
		int 15h
	pop ax
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			enable_a20
; action:		enables A20 gate and verifies that it's on
; in:			(nothing)
; out (success):	AX=0
; out (failure):	AX=1
; modifies:		AX
; minimum CPU:		286+
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

enable_a20:
	call enable_a20_at	; try 'AT' method
	call verify_a20		; did it work?
	jne enable_a20_1
	call enable_a20_vectra	; no; try 'Vectra' method
	call verify_a20		; did _that_ work?
	jne enable_a20_1
	call enable_a20_ps2	; no; try 'PS/2' method
	call verify_a20		; last chance: did it work?
enable_a20_1:
	mov ax,0
	jne enable_a20_2
	mov ax,1
enable_a20_2:
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			_enable_pmode
; action:		enables 32-bit pmode and jumps to loaded kernel
; in:			A20 flag and 32-bit physical entry point on stack
; out (error):		AX=1 if 32-bit CPU not present
; out (error):		AX=2 if A20 could not be enabled
; out (success):	(does not return if successful)
; modifies:		AX
; minimum CPU:		386+
; notes:		C prototype:
;			extern int enable_pmode(int enable_a20, long entry);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

GLOBAL _enable_pmode
_enable_pmode:
	push bp
		mov bp,sp

; paranoia
EXTERN __got_32bit_cpu
		mov al,[__got_32bit_cpu]
		or al,al
		mov ax,1
		je pmode_err

; turn on A20 gate if necessary
		test word [bp + 4],0FFFFh
		je pmode_a20
		call enable_a20
		or ax,ax
		mov ax,2
		jne pmode_err
pmode_a20:
; stash the entry point. This is self-modifying code,
; but it's the only way I can see of jumping to the kernel
; with all of the data segment registers already loaded
		mov eax,[bp + 6]
		mov [cs:entry + 2],eax

; in gdt_ptr: convert GDT offset to GDT linear address
		xor eax,eax
		mov ax,ds
		shl eax,4
		add [gdt_ptr + 2],eax

; interrupts off
		push dword 0
		popfd

; convert SS:SP to ESP
		xor ebx,ebx
		mov bx,sp
		add ebx,eax
		mov esp,ebx

; load GDT and set PE bit
		lgdt [gdt_ptr]
		mov ebx,cr0
		inc bx
		mov cr0,ebx

; load data segment registers
		mov ax,DATA_SEL
		mov ds,ax
		mov ss,ax
		mov es,ax
		mov fs,ax
		mov gs,ax

; BING magic value. GRUB/Multiboot uses 2BADB002h
		mov eax,0B1C6B002h

; 2-byte JMP at [entry+0]:	66 EA
; 4-byte offset at [entry+2]:	00 00 00 00
; 2-byte selector at [entry+6]:	08 00	(=CODE_SEL)
entry:
		jmp CODE_SEL:dword 0
pmode_err:
	pop bp
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SEGMENT _DATA PUBLIC CLASS=DATA

gdt:
; our NULL (0th) descriptor; INT 15h AH=87h 1st descriptor
	dw 0			; limit 15:0
	dw 0			; base 15:0
	db 0			; base 23:16
	db 0			; type
	db 0			; limit 19:16, flags
	db 0			; base 31:24

	dw 0
	dw 0
	db 0
	db 0
	db 0
	db 0

; src and dst descriptors used by INT 15h AH=87h
gdt2:
	dw 0FFFFh
	dw 0
	db 0
	db 93h
	db 0CFh
	db 0
gdt3:
	dw 0FFFFh
	dw 0
	db 0
	db 93h
	db 0CFh
	db 0

; two more descriptors used by INT 15h AH=87h
	dw 0
	dw 0
	db 0
	db 0
	db 0
	db 0

	dw 0
	dw 0
	db 0
	db 0
	db 0
	db 0

CODE_SEL	equ	$-gdt
	dw 0FFFFh
	dw 0
	db 0
	db 9Ah			; present, ring 0, code, non-conforming, readable
	db 0CFh			; page-granular, 32-bit
	db 0

DATA_SEL	equ	$-gdt
	dw 0FFFFh
	dw 0
	db 0
	db 92h			; present, ring 0, data, expand-up, writable
	db 0CFh			; page-granular, 32-bit
	db 0
gdt_end:

gdt_ptr:
	dw gdt_end - gdt - 1	; GDT limit
;;	dd gdt			; linear, physical adr of GDT
	dw gdt, 0		; old TLINK chokes; use 16-bit address here


%else

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			_enable_pmode
; action:		enables 32-bit pmode and jumps to loaded kernel
; in:			A20 flag and 32-bit physical entry point on stack
; out (error):		AX=1 if 32-bit CPU not present
; out (error):		AX=2 if A20 could not be enabled
; out (success):	(does not return if successful)
; modifies:		AX
; minimum CPU:		386+
; notes:		C prototype:
;			extern int enable_pmode(int enable_a20, long entry);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

GLOBAL _enable_pmode
_enable_pmode:
	push bp
		mov bp,sp

; paranoia
EXTERN __got_32bit_cpu
		mov al,[__got_32bit_cpu]
		or al,al
		mov ax,1
		je pmode_err

; stash the entry point. This is self-modifying code,
; but it's the only way I can see of jumping to the kernel
; with all of the data segment registers already loaded
		mov eax,[bp + 6]
		mov [cs:entry + 1],eax

; in gdt_ptr: convert GDT offset to GDT linear address
		xor eax,eax
		mov ax,ds
		shl eax,4
		add [gdt1 + 2],eax

; convert SS:SP to ESP
		xor ebx,ebx
		mov bx,sp
		add ebx,eax
		mov esp,ebx

; call INT 15h AH=89h to switch to pmode
		mov ah,89h
		mov bx,2820h	; move IRQs 0-15 to INTs 20h-2Fh
		push ds
		pop es
		mov di,gdt
		int 15h
		jc pmode_err

[BITS 32]

; BING magic value. GRUB/Multiboot uses 2BADB002h
		mov eax,0B1C6B002h

; 1-byte JMP at [entry+0]:	EA
; 4-byte offset at [entry+1]:	00 00 00 00
; 2-byte selector at [entry+5]:	08 00	(CODE_SEL)

entry:
		jmp CODE_SEL:0
[BITS 16]

pmode_err:
	pop bp
	ret

SEGMENT _DATA PUBLIC CLASS=DATA

gdt:
; NULL descriptor
	dw 0			; limit 15:0
	dw 0			; base 15:0
	db 0			; base 23:16
	db 0			; type
	db 0			; limit 19:16, flags
	db 0			; base 31:24

gdt1:
; pseudo-descriptor for the GDT itself
	dw gdt_end - gdt	; limit 15:0
; xxx - TLINK chokes on 32-bit address
;	dd gdt			; base 31:0
	dw gdt			; base 15:0
	dw 0			; base 31:16
	dw 0

; IDT pseudo-descriptor
	dw 0
	dd 0
	dw 0

; data descriptor for DS
	dw 0FFFFh
	dw 0
	db 0
	db 92h			; present, ring 0, data, expand-up, writable
	db 0CFh			; page-granular, 32-bit
	db 0

; data descriptor for ES
	dw 0FFFFh
	dw 0
	db 0
	db 92h
	db 0CFh
	db 0

; data descriptor for SS
	dw 0FFFFh
	dw 0
	db 0
	db 92h
	db 0CFh
	db 0

; code descriptor for CS
CODE_SEL	equ	$-gdt
	dw 0FFFFh
	dw 0
	db 0
	db 9Ah			; present, ring 0, code, non-conforming, readable
	db 0CFh			; page-granular, 32-bit
	db 0

; used by INT 15h AH=89h
	dd 0
	dd 0
gdt_end:

%endif

; OK, I'm not 100% sure what's going on here... had to fiddle
; about until things started working. Looks like you need to
; 1. name the segments _TEXT and _DATA
; 2. assign them to classes CODE and DATA
; 3. use a GROUP statement: (NO, DON'T DO THIS)

GROUP DGROUP _TEXT _DATA

⌨️ 快捷键说明

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