;//--------------------------------------------------------------- ;// twotasks.s ;// ;// This program demonstrates task-switching by the processor ;// between two tasks that both execute at privilege-level 0. ;// (Only one of the two tasks has a Local Descriptor Table.) ;// ;// assemble with: $ as86 twotasks.s -b twotasks.b ;// install using: $ dd if=twotasks.b of=/dev/fd0 seek=1 ;// ;// NOTE: This code begins executing with CS:IP = 1000:0002. ;// ;// programmer: ALLAN CRUSE ;// date begun: 17 FEB 2004 ;// completion: 19 FEB 2004 -- added use of an LDT by task #2 ;//--------------------------------------------------------------- .SECT .TEXT ;----------------------------------------------------------------- .WORD 0xABCD ; programming signature ;----------------------------------------------------------------- main: mov ax, cs ; address this segment mov ds, ax ; with DS register pop dword return_address ; store return-address mov ss, ax ; adjust SS register mov esp, #stack1 ; and set new stacktop call prepare_for_the_demo call enter_protected_mode call exec_taskswitch_demo call leave_protected_mode push dword return_address ; recover our exit-address retf ; exit to program launcher ;----------------------------------------------------------------- ; EQUATES for our various segment-descriptor selectors sel_ss EQU 0x0008 ; data-segment selector sel_cs EQU 0x0010 ; code-segment selector sel_es EQU 0x0004 ; vram-segment selector sel_t1 EQU 0x0018 ; task-segment selector sel_t2 EQU 0x0020 ; task-segment selector sel_ls EQU 0x0028 ; LDT-segment selector ;----------------------------------------------------------------- .ALIGN 8 theGDT: .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor .WORD 0xFFFF, 0x0000, 0x9201, 0x0000 ; data descriptor .WORD 0xFFFF, 0x0000, 0x9A01, 0x0000 ; code descriptor .WORD 0x002B, myTSS1, 0x8101, 0x0000 ; task descriptor .WORD 0x002B, myTSS2, 0x8101, 0x0000 ; task descriptor .WORD 0x0007, theLDT, 0x8201, 0x0000 ; LDT descriptor ;----------------------------------------------------------------- theLDT: .WORD 0x7FFF, 0x8000, 0x920B, 0x0000 ; vram descriptor ;----------------------------------------------------------------- myTSS1: .SPACE 0x2C ; task-state segment #1 myTSS2: .SPACE 0x2C ; task-state segment #2 ;----------------------------------------------------------------- ;----------------------------------------------------------------- return_address: .LONG 0 ; to store exit-address ;----------------------------------------------------------------- ;================================================================= ;====== THE FOLLOWING PROCEDURES ARE EXECUTED BY TASK #1 ======= ;================================================================= ;----------------------------------------------------------------- prepare_for_the_demo: ; initialize the Task-State Segment for Task #2 lea di, myTSS2 mov 14[di], #draw ; initial-IP mov 16[di], #0x0000 ; initial-FLAGS mov 26[di], #stack2 ; initial-SP mov 34[di], #sel_es ; initial-ES mov 36[di], #sel_cs ; initial-CS mov 38[di], #sel_ss ; initial-SS mov 40[di], #sel_ss ; initial-DS mov 42[di], #sel_ls ; static-LDTR ret ;----------------------------------------------------------------- enter_protected_mode: ; initialize the Global Descriptor Table Register push #0x0001 ; GDT base-address hiword push #theGDT ; GDT base-address loword push #0x002F ; GDT's segment-limit lgdt [esp] ; memory-image into GDTR add sp, #6 ; discard the three words ; we disable interrupts while executing in protected-node cli ; no device interrupts ; turn on PE-bit in Control Register 0 mov eax, cr0 ; get machine status bts eax, #0 ; set PE-bit to 1 mov cr0, eax ; enable protection ; reload CS, SS, DS with protected-mode selectors jmpf #pm, #sel_cs ; reload register CS pm: mov ax, #sel_ss mov ss, ax ; reload register SS mov ds, ax ; reload register DS ; also purge the invalid value left in register ES xor ax, ax ; nullify selector mov es, ax ; in ES register ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- exec_taskswitch_demo: ; establish Task #1 as the current task mov ax, #sel_t1 ; load TSS1-selector ltr ax ; into TR register ; perform an intertask call to Task #2 callf #0, #sel_t2 ; call to task #2 ret ; return to 'main' ;----------------------------------------------------------------- leave_protected_mode: ; insure segment-attributes are valid for real-mode mov ax, ss ; address 64KB r/w data mov ds, ax ; using DS register mov es, ax ; and ES register ; turn off PE-bit in Control Register 0 mov eax, cr0 ; get machine status btr eax, #0 ; reset PE-bit to 0 mov cr0, eax ; reenter real-mode ; load real-mode segment-addresses into CS, SS, DS, ES jmpf #rm, #0x1000 ; reload CS rm: mov ax, cs ; address this segment mov ss, ax ; with SS register mov ds, ax ; also DS register mov es, ax ; also ES register ; now we can once again handle device-interrupts sti ; interrupts allowed ret ; back to main routine ;----------------------------------------------------------------- ;================================================================= ;=========== STACK AREA ALLOCATED FOR USE BY TASK #1 =========== ;================================================================= .align 16 ; use paragraph alignment .space 512 ; storage for task1 stack stack1: ; labels the top-of-stack ;================================================================= ;=========== STACK AREA ALLOCATED FOR USE BY TASK #2 =========== ;================================================================= .align 16 ; use paragraph alignment .space 512 ; storage for task2 stack stack2: ; labels the top-of-stack ;================================================================= ;----------------------------------------------------------------- ;----------------------------------------------------------------- ;================================================================= ;======= THE FOLLOWING PROCEDURE IS EXECUTED BY TASK #2 ======== ;================================================================= ;----------------------------------------------------------------- msg: .ASCII " Hello from task #2 " ; message string len: .WORD * - msg ; message length att: .BYTE 0x1F ; message colors ;----------------------------------------------------------------- draw: ; ; This procedure will draw a message directly into video memory. ; It expects DS already holds a selector for this segment (where ; the message-string is defined) and ES already holds a selector ; for the video memory segment where the string will be written. ; mov si, #msg ; point DS:SI to string mov di, #320 ; point ES:DI to screen cld ; do forward processimg mov ah, att ; color-attributes in AH mov cx, len ; string's length in CX nxchr: lodsb ; fetch next character stosw ; store char and color loop nxchr ; process entire string ; here executing 'iret' will now trigger a task-switch iret ; back to calling task ;----------------------------------------------------------------- ;================================================================= ;----------------------------------------------------------------- END