📄 pm10.asm
字号:
; pm10.asm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; pm10.asm - protected-mode demo code
; Christopher Giese <geezer[AT]execpc.com>
;
; Release date 9/28/98. Distribute freely. ABSOLUTELY NO WARRANTY.
; Assemble with NASM: nasm -o pm10.com pm10.asm
;
; Demonstrates:
; - Two Ring 3 tasks, preemptively multitasked with timer interrupt.
; - Ring 3 code calling Ring 0 via software interrupt (syscall)
; Notes:
; - Code to return to real mode removed for clarity.
; Put it back in if you like.
; Fixes/changes:
; - Byte 6 of descriptors (flags/limit 19:16) changed from
; 0xFC to 0xCF
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[SECTION .text]
[ORG 0x100]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 16-bit real mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[BITS 16]
; point code/data descriptors to CS<<4 (=DS<<4 for .COM file)
xor ebx,ebx
mov bx,cs ; EBX=segment
shl ebx,4 ; EBX=segment << 4
lea eax,[ebx] ; =linear address of segment base
mov [gdt2 + 2],ax
mov [gdt3 + 2],ax
mov [gdt4 + 2],ax
mov [gdt5 + 2],ax
shr eax,16
mov [gdt2 + 4],al
mov [gdt3 + 4],al
mov [gdt4 + 4],al
mov [gdt5 + 4],al
mov [gdt2 + 7],ah
mov [gdt3 + 7],ah
mov [gdt4 + 7],ah
mov [gdt5 + 7],ah
; point tss descriptor to tss
lea eax,[ebx + tss] ; EAX=linear address of tss
mov [gdt6 + 2],ax
shr eax,16
mov [gdt6 + 4],al
mov [gdt6 + 7],ah
; point gdtr to the gdt, idtr to the idt
lea eax,[ebx + gdt] ; EAX=linear address of gdt
mov [gdtr + 2],eax
lea eax,[ebx + idt] ; EAX=linear address of idt
mov [idtr + 2],eax
; clear NT bit (so iret does normal iret, instead of task-switch),
; set IOPL=00, and set IF=0 (disable interrupts)
push dword 0
popfd
; load GDT and IDT for full protected mode
lgdt [gdtr]
lidt [idtr]
; set PE [protected mode enable] bit and go
mov eax,cr0
or al,1
mov cr0,eax
jmp SYS_CODE_SEL:do_pm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32-bit protected mode, ring 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[BITS 32]
do_pm: mov ax,SYS_DATA_SEL
mov ds,ax
mov ss,ax
nop
mov es,ax
mov fs,ax
mov gs,ax
; load task register. We don't use the x86 task switch mechanism, but
; we still need the TSS to specify the locations of the system-mode
; (Ring 0) and user-mode (Ring 3) stacks
mov ax,USER_TSS
ltr ax
; print starting msg
lea esi,[st_msg]
call wrstr
; set up scheduler (36 timer interrupts=2 seconds)
; load ECX with 65536 to test-run this code for exactly one hour
mov ecx,36
lea ebx,[regsA] ; point to user regs
; SAVE KERNEL REGS
sched: push ebx
push ecx
; save current ESP in TSS
mov [tss_esp0],esp
; LOAD USER REGS
lea esp,[ebx]
; pop EAX, EBX, ECX, EDX, EBP, ESI, EDI...
popa
; ...DS, ES, FS, GS...
pop ds
pop es
pop fs
pop gs
; ...EIP, CS, EFLAGS, ESP, SS (jumps to user task)
iret
; fault/exception/interrupt (except int 0x1F) brings us back here
; *** CAUTION ***: a fault in Ring 0 will not stack SS and ESP.
; Faults other than hardware interrupts may stack an extra error code.
; Either of these situations will mess up the stack.
; user EIP, CS, EFLAGS, ESP, SS left on stack -- complete the stack frame
fault:
fault2: push gs
push fs
push es
push ds
pusha
; reset 8259 interrupt controller
mov al,0x20
out 0x20,al
; SAVE USER REGS
mov ax,ss
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
lea esi,[esp]
mov edi,[esp + 72] ; saved kernel EBX -> user regs
mov ecx,17 ; 17 dwords worth (68 bytes)
rep movsd
add esp,68
; LOAD KERNEL REGS
pop ecx
pop ebx
; reschedule
cmp ebx,regsA
je next
lea ebx,[regsA]
jmp again
next: lea ebx,[regsB]
again: loop sched
; print ending msg
lea esi,[end_msg]
call wrstr
jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32-bit protected mode, ring 3 (task A)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
taskA: lea esi,[hi_msgA]
int 0x1F ; wrstr syscall
mov ecx,0x7FFFF ; delay
loop $
jmp taskA
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32-bit protected mode, ring 3 (task B)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
taskB: lea esi,[hi_msgB]
int 0x1F ; wrstr syscall
mov ecx,0x7FFFF ; delay
loop $
jmp taskB
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; fault handlers (EIP -> offending instruction)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
isr0: mov bl,0
jmp fault ; zero divide
isr5: mov bl,5
jmp fault ; BOUND
isr6: mov bl,6
jmp fault ; invalid opcode
isr7: mov bl,7
jmp fault ; coprocessor not available
isr10: mov bl,0x10
jmp fault
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; aborts (EIP -> ???)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
isr9: mov bl,9 ; coprocessor segment overrun
jmp fault
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; fault handlers w/ error code (EIP -> offending instruction)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
isr0A: mov bl,0x0A
jmp fault2 ; bad TSS
isr0B: mov bl,0x0B
jmp fault2 ; segment not present
isr0C: mov bl,0x0C
jmp fault2 ; stack fault
isr0D: mov bl,0x0D
jmp fault2 ; GPF
isr0E: mov bl,0x0E
jmp fault2 ; page fault
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; aborts w/ error code (EIP = garbage)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
isr8: mov bl,8 ; double fault
jmp fault2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; traps (EIP -> beyond offending instruction)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
isr1: mov bl,1
jmp fault ; debug (XXX - may be fault or trap)
isr2: mov bl,2
jmp fault ; non-maskable interrupt
isr4: mov bl,4
jmp fault ; INTO
isr3: mov bl,3
jmp fault ; INT3
isr0F: mov bl,0x0F
jmp fault ; coprocessor error
isr11: mov bl,0x11
jmp fault ; alignment check
isr12: mov bl,0x12
jmp fault
isr13: mov bl,0x13
jmp fault
isr14: mov bl,0x14
jmp fault
isr15: mov bl,0x15
jmp fault
isr16: mov bl,0x16
jmp fault
isr17: mov bl,0x17
jmp fault
isr18: mov bl,0x18
jmp fault
isr19: mov bl,0x19
jmp fault
isr1A: mov bl,0x1A
jmp fault
isr1B: mov bl,0x1B
jmp fault
isr1C: mov bl,0x1C
jmp fault
isr1D: mov bl,0x1D
jmp fault
isr1E: mov bl,0x1E
jmp fault
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; character-output video routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
wrch: push gs
push ecx
push ebx
push eax
mov ax,LINEAR_SEL
mov gs,ax
; (Y * 80 + X) * 2 --> EAX
movzx eax,byte [CsrY]
mov cl,80
mul cl
add al,[CsrX]
adc ah,0
shl eax,1
; EAX + 0xB8000 --> EBX; store char
lea ebx,[eax + 0xB8000]
pop eax
push eax
mov [gs:ebx],al
; advance cursor
mov cx,[CsrX]
inc cl
cmp cl,80 ; cursor off right side of screen?
jb wrch2
xor cl,cl ; yes, wrap to left side...
inc ch ; ...and down one line
cmp ch,25 ; cursor off bottom of screen?
jb wrch2
xor ch,ch ; yes, wrap to top left corner (no scroll)
wrch2: mov [CsrX],cx
pop eax
pop ebx
pop ecx
pop gs
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; string-output video routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
wrstr: push esi
push eax
cld
jmp wrstr2
wrstr1: call wrch
wrstr2: lodsb
or al,al
jne wrstr1
pop eax
pop esi
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; interrupt 0x1F service routine
; The interrupt gate pointing to this function is a Ring 3 gate
; so this code can be called from Ring 3. Other interrupts/
; exceptions have Ring 0 gates, and cause GPF (interrupt 0x0D)
; instead, when called from Ring 3.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
isr1F: pusha
push gs
push fs
push es
push ds
; though possibly called from Ring 3, this code runs at Ring 0,
; and can use SYS_DATA_SEL and LINEAR_SEL
mov ax,SYS_DATA_SEL
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
call wrstr
pop ds
pop es
pop fs
pop gs
popa
iret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CsrX: db 0
CsrY: db 0
st_msg: db "(Scheduler starts.) ", 0
hi_msgA:db "Hello from task A. ", 0
hi_msgB:db "Greetings from task B. ", 0
end_msg:db "(Scheduler done.) ", 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 16-bit limit/32-bit linear base address of GDT and IDT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
gdtr: dw gdt_end - gdt - 1 ; GDT limit
dd gdt ; linear, physical address of GDT
idtr: dw idt_end - idt - 1 ; IDT limit
dd idt ; linear, physical address of IDT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; global descriptor table (GDT)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; null descriptor
gdt: 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
; linear data segment descriptor
LINEAR_SEL equ $-gdt
dw 0xFFFF ; limit 0xFFFFF
dw 0 ; base for this one is always 0
db 0
db 0x92 ; present, ring 0, data, expand-up, writable
db 0xCF ; page-granular, 32-bit
db 0
; code segment descriptor
SYS_CODE_SEL equ $-gdt
gdt2: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0x9A ; present, ring 0, code, non-conforming, readable
db 0xCF
db 0
; data segment descriptor
SYS_DATA_SEL equ $-gdt
gdt3: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0x92 ; present, ring 0, data, expand-up, writable
db 0xCF
db 0
; code segment descriptor
USER_CODE_SEL equ $-gdt+3
gdt4: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0xFA ; present, ring 3, code, non-conforming, readable
db 0xCF
db 0
; data segment descriptor
USER_DATA_SEL equ $-gdt+3
gdt5: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0xF2 ; present, ring 3, data, expand-up, writable
db 0xCF
db 0
; user TSS
USER_TSS equ $-gdt
gdt6: dw 103
dw 0 ; set to tss
db 0
db 0xE9 ; present, ring 3, 32-bit available TSS
db 0
db 0
gdt_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; interrupt descriptor table (IDT)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32 reserved interrupts:
idt: dw isr0 ; entry point 15:0
dw SYS_CODE_SEL ; selector
db 0 ; word count
db 0x8E ; type (32-bit Ring 0 interrupt gate)
dw 0 ; entry point 31:16 (XXX - isr0 >> 16)
dw isr1
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr2
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr3
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr4
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr5
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr6
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr7
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr8
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr9
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr0A
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr0B
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr0C
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr0D
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr0E
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr0F
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr10
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr11
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr12
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr13
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr14
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr15
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr16
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr17
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr18
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr19
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr1A
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr1B
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr1C
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr1D
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr1E
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw isr1F
dw SYS_CODE_SEL
db 0
db 0xEE ; Ring 3 interrupt gate
dw 0
idt_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; task state segment
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
tss: dw 0, 0 ; back link
tss_esp0:
dd 0 ; ESP0
dw SYS_DATA_SEL, 0 ; SS0, reserved
dd 0 ; ESP1
dw 0, 0 ; SS1, reserved
dd 0 ; ESP2
dw 0, 0 ; SS2, reserved
dd 0 ; CR3
dd 0, 0 ; EIP, EFLAGS (EFLAGS=0x200 for ints)
dd 0, 0, 0, 0 ; EAX, ECX, EDX, EBX
dd 0, 0, 0, 0 ; ESP, EBP, ESI, EDI
dw 0, 0 ; ES, reserved
dw 0, 0 ; CS, reserved
dw 0, 0 ; SS, reserved
dw 0, 0 ; DS, reserved
dw 0, 0 ; FS, reserved
dw 0, 0 ; GS, reserved
dw 0, 0 ; LDT, reserved
dw 0, 0 ; debug, IO perm. bitmap
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; taskA data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
times 63 dd 0
stackA: dd 0
; regs popped by popa (ESP is popped and discarded)
regsA: dd 0, 0, 0, 0, 0, 0, 0, 0 ; EDI, ESI, EBP, EBX, EDX, ECX, EDX
; regs popped by pop ds, etc.
regsA1: dw USER_DATA_SEL, 0 ; DS
dw USER_DATA_SEL, 0 ; ES
dw USER_DATA_SEL, 0 ; FS
dw USER_DATA_SEL, 0 ; GS
; regs popped by iret
regsA2: dd taskA ; EIP
dw USER_CODE_SEL, 0 ; CS
dd 0x200 ; EFLAGS (0x200 enables ints)
dd stackA ; ESP
dw USER_DATA_SEL, 0 ; SS
regsA3:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; taskB data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
times 63 dd 0
stackB: dd 0
regsB: dd 0, 0, 0, 0, 0, 0, 0, 0
regsB1: dw USER_DATA_SEL, 0
dw USER_DATA_SEL, 0
dw USER_DATA_SEL, 0
dw USER_DATA_SEL, 0
regsB2: dd taskB
dw USER_CODE_SEL, 0
dd 0x200
dd stackB
dw USER_DATA_SEL, 0
regsB3:
end:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -