📄 start.asm
字号:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; stripped-down startup code
; - no #pragma startup/#pragma exit/atexit()...
; - no freeing of unused memory for TINY memory model (using INT 21h AH=4Ah)
; - no exit code - no heap
; - TINY memory model only - no NULL pointer checking
; - no interrupt handling - no DOS version checking
;
; Thanks, John Fine, for helping with the invalid entry point problem.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SEGMENT _TEXT PUBLIC CLASS=CODE
resb 100h
GLOBAL __code
__code:
..start:
mov ax,cs
mov ds,ax
cmp ax,0040h ; bootstrap or .COM file?
jbe boot ; xxx - is there a better test for DOS?
inc byte [__dos]
boot:
mov es,ax
mov ss,ax
mov sp,stack
xor ax,ax ; the stack is in the BSS,
mov di,__bss ; so don't push/pop until
mov cx,__end ; the BSS is zeroed
sub cx,di
rep stosb
; check for 32-bit CPU and get memory sizes
call cpu_is_32bit
call get_convmem_size
call get_extmem_size
; xxx - set conv_mem (far pointer) and ext_mem (linear adr)
; xxx - adjust extmem_size and ext_mem if XMS present
; xxx - adjust conv_mem_size and conv_mem based on __end
EXTERN _main
call _main ; call C code
exit:
mov ax,0E07h ; *** BEEEP ***
int 10h
mov ah,0 ; await key pressed
int 16h
mov al,[__dos]
or al,al
jne dos_exit
int 19h ; re-start the boot process
dos_exit:
mov ax,4C01h
int 21h
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: cpu_is_32bit
; action: checks for 32-bit CPU
; in: (nothing)
; out: __got_32bit_cpu set
; modifies: (nothing)
; minimum CPU: 8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cpu_is_32bit:
push bx
push ax
pushf
pushf
pop bx ; old FLAGS -> BX
mov ax,bx
xor ah,70h ; try changing b14 (NT)...
push ax ; ... or b13:b12 (IOPL)
popf
pushf
pop ax ; new FLAGS -> AX
popf
xor al,al ; zero AL
xor ah,bh ; 32-bit CPU if we changed NT...
and ah,70h ; ...or IOPL
mov [__got_32bit_cpu],ah
pop ax
pop bx
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: get_convmem_size
; action: gets conventional memory size using BIOS calls
; in: (nothing)
; out: __conv_mem_size set
; modifies: (nothing)
; minimum CPU: 8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
get_convmem_size:
push dx
push ax
int 12h
xor dx,dx ; move to DX multiplies by 256
mov dl,ah
mov ah,al
xor al,al
shl ax,1 ; multiply by 4 (x1024 total)
rcl dx,1
shl ax,1
rcl dx,1
mov [__conv_mem_size + 0],ax
mov [__conv_mem_size + 2],dx
pop ax
pop dx
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: get_extmem_size
; action: gets extended memory size using BIOS calls
; in: (nothing)
; out: (nothing)
; modifies: maybe the high 16 bits of some registers
; minimum CPU: 8088 (386+ for INT 15h AX=E820h)
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
get_extmem_size:
push bp
mov bp,sp
push es
push di
push si
push dx
push cx
push bx
push ax
; got a 32-bit CPU or not?
mov al,[__got_32bit_cpu]
or al,al
je mem3 ; can't use INT 15h AX=E820h
; try INT 15h AX=E820h
push ds
pop es
mov di,buffer_e820
xor esi,esi
mov edx,534D4150h ; "SMAP"
xor ebx,ebx
mem1:
mov ecx,20h
mov eax,0000E820h
int 15h
jc mem3
cmp dword [es:di + 16],1 ; type 1 memory (available to OS)
jne mem2
cmp dword [es:di],100000h ; check only extended memory
jb mem2
add esi,[es:di + 8]
mem2:
or ebx,ebx
jne mem1
mov eax,esi
mov edx,esi
shr edx,16
jmp short mem7 ; DX:AX=RAM top
mem3:
; try INT 15h AX=E801h
mov ax,0E801h
int 15h
jc mem4
mov dx,bx ; BX=ext mem >=16M, in 64K
; move to DX multiples by 64K
xor bx,bx
mov bl,ah ; AX=ext mem 1M-16M, in K
mov ah,al ; multiply by 256
xor al,al
shl ax,1 ; multiply by 4 (x1024 total)
rcl bx,1
shl ax,1
rcl bx,1
add dx,bx
jmp short mem7 ; DX:AX=RAM top
mem4:
; try INT 15h AH=88h
mov ax,8855h
int 15h ; CY=error is not reliable...
cmp ax,8855h ; ...so make sure AL is modified
jne mem5
mov ax,88AAh
int 15h ; AX=ext mem size, in K
cmp ax,88AAh
je mem6
mem5:
xor dx,dx ; move to DX multiplies by 256
mov dl,ah
mov ah,al
xor al,al
shl ax,1 ; multiply by 4 (x1024 total)
rcl dx,1
shl ax,1
rcl dx,1
jmp short mem7 ; DX:AX=RAM top
mem6:
xor ax,ax
xor dx,dx
mem7:
mov [__ext_mem_size + 0],ax
mov [__ext_mem_size + 2],dx
pop ax
pop bx
pop cx
pop dx
pop si
pop di
pop es
pop bp
ret
; xxx - restore VCPI code
; xxx - make sure XMS memory alloc works when EMM386 loaded
; alternate code to enter pmode; should work with Bochs
; xxx - uncomment this, use it if INT 15h AH=89h fails
%if 0
unhand EQU 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: kbd
; action: waits until 8042 keyboard controller ready
; to accept a command or data byte
; modifies: AL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
kbd0:
jmp short $+2 ; a delay (probably not effective nor necessary)
in al,60h ; read and discard data/status from 8042
kbd:
jmp short $+2 ; delay
in al,64h
test al,1 ; output buffer (data _from_ keyboard) full?
jnz kbd0 ; yes, read and discard
test al,2 ; input buffer (data _to_ keyboard) empty?
jnz kbd ; no, loop
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: set_a20_at
; action: enables/disables A20 at 8042 output port,
; using D0h (read output port) and D1h
; (write output port) command bytes to
; toggle bit b1 of the output port
; in: AH=0 to disable A20, AH != 0 to enable A20
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
set_a20_at:
push si
push ax
pushf
; Yay, feedback! Chase told me it works better if I shut off interrupts:
cli
call kbd
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 ah,ah
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -