Page 1 of 1

Multitasking Howto

Posted: Thu Jul 13, 2006 4:31 am
by Jeko
I readed this tutorial, but i don't understood what is kstackend, kstacks[d][KSTACKTOP] and stacks[d][USTACKTOP].

Posted: Thu Jul 13, 2006 5:04 am
by spix
kstacks[d][KSTACKTOP] and stacks[d][USTACKTOP].
kstacks is an array of kernel stacks for processes, KSTACKTOP is the top of the stack. stacks is an array of user stacks, and USTACKTOP is the top of the stack.

A user process (one that is running in ring 3) uses the User Stack when executing in ring 3 and the kernel stack during interrupts (I think)

the kstacks and stacks method shown is just one way of storing the physical memory of the stacks. (you would seem to be limited by whatever the max value of "d" is)

There is another tutorial located here http://hosted.cjmovie.net/TutMultitask.htm that might be a bit easier to understand.

Hope that helps[/url]

Posted: Thu Jul 13, 2006 5:23 am
by Jeko
thank you for the link.

Posted: Mon Jul 17, 2006 3:11 am
by Jeko
thank you, but i need something about ring0 and ring3 multitasking.

Posted: Mon Jul 17, 2006 4:18 am
by Dex
Heres a demo to do it in asm.
NOTE: Sorry for post this instead of link, lost the link.

Code: Select all

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	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: