;//--------------------------------------------------------------- ;// showtss.s ;// ;// This is a modification of the 'twotasks.s' demo discussed ;// in class, and it was studied during an in-class exercise. ;// ;// (The program demonstrated task-switching by the processor ;// between two tasks; both tasks ran using privilege-level 0 ;// and one of the two tasks used a Local Descriptor Table.) ;// ;// assemble with: $ as86 showtss.s -b showtss.b ;// install using: $ dd if=showtss.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 ;// revised on: 29 FEB 2004 -- added a display of tasks' TSSs ;//--------------------------------------------------------------- .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 show_task_state_info ;<--- NEW PROCEDURE ADDED 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 sel_vs EQU 0x0030 ; vram-segment selector <-- added ;----------------------------------------------------------------- .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 ;----------------------------------------------------------------- ;----------------------------------------------------------------- return_address: .LONG 0 ; to store exit-address myTSS1: .SPACE 0x2C ; task-state segment #1 myTSS2: .SPACE 0x2C ; task-state segment #2 ;================================================================= ;====== THE FOLLOWING PROCEDURES ARE EXECUTED BY TASK #1 ======= ;================================================================= ;----------------------------------------------------------------- prepare_for_the_demo: ; initialize the Task-State Segment for Task #2 lea di, myTSS2 mov word 14[di], #draw ; initial-IP mov word 16[di], #0x0000 ; initial-FLAGS mov word 26[di], #stack2 ; initial-SP mov word 34[di], #sel_es ; initial-ES mov word 36[di], #sel_cs ; initial-CS mov word 38[di], #sel_ss ; initial-SS mov word 40[di], #sel_ss ; initial-DS mov word 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 #0x0037 ; GDT's segment-limit ; <-- changed 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 ;----------------------------------------------------------------- ;================================================================= ;=== BELOW IS ADDITIONAL CODE FOR A NEW PROCEDURE IN TASK #1 === ;================================================================= ;----------------------------------------------------------------- fields: .ASCII "LINK SP0 SS0 SP1 SS1 SP2 SS2" ; array of names .ASCII " IP FL AX CX DX BX SP BP SI DI" .ASCII " ES CS SS DSLDTR" count: .WORD ( * - fields )/4 ; count of names hexlst: .ASCII "0123456789ABCDEF" ; table of digits outbuf: .ASCII " nnn=xxxx " ; output buffer outlen: .WORD * - outbuf ; output length ;----------------------------------------------------------------- ax2hex: ; converts value in AX to a string of hex digits at DS:DI pusha ; preserve cpu registers mov dx, ax ; copy AX value to DX mov bx, #hexlst ; BX = xlat-table offset mov cx, #4 ; number of nybbles/word .L0: rol dx, #4 ; get next nybble in DL mov al, dl ; nybble's byte to AL and al, #0x0F ; isolate bits of nybble xlat ; convert to hex numeral mov [di], al ; copy numeral to buffer inc di ; advance buffer-pointer loop .L0 ; do rest of the nybbles popa ; restore cpu registers ret ; back to calling routine ;----------------------------------------------------------------- ;----------------------------------------------------------------- buf2screen: ; draws output-buffer to screen at address in ES:DI lea si, outbuf ; point DS:SI to string cld ; do forward processing mov ah, #0x50 ; load color-attributes mov cx, outlen ; setup string's length .L1: lodsb ; fetch next character stosw ; store char and colors loop .L1 ; again for other chars ret ; back to calling routine ;----------------------------------------------------------------- show_task_state_info: ; This procedure draws TSS1 and TSS2 onto the screen mov ax, #sel_vs ; address video memory mov es, ax ; with ES register mov ax, #sel_ss ; address this segment mov ds, ax ; with DS register xor dx, dx ; start counting from 0 .L2: ; move next TSS field-name into the output-buffer imul si, dx, #4 ; compute name's offset mov eax, fields[si] ; get the name into EAX mov outbuf, eax ; copy name into buffer ; compute offset of next field in the TSS imul bx, dx, #2 ; compute field's offset ; draw next field from TSS#1 on the screen mov ax, myTSS1[bx] ; get field-value in AX lea di, outbuf+5 ; point to buffer-offset call ax2hex ; convert to hex string imul di, dx, #160 ; screen-row's offset add di, #60 ; plus column's offset call buf2screen ; draw the output-buffer ; draw next field from TSS#2 on the screen mov ax, myTSS2[bx] ; get field-value in AX lea di, outbuf+5 ; point to buffer-offset call ax2hex ; convert to hex string imul di, dx, #160 ; screen-row's offset add di, #120 ; plus column's offset call buf2screen ; draw the output-buffer ; update the loop-counter and check for exit-condition inc dx ; increment field-counter cmp dx, count ; final field was shown? jb .L2 ; no, show another field ret ; back to 'main' routine ;----------------------------------------------------------------- END