home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
drdobbs
/
1989
/
09
/
green.lst
< prev
next >
Wrap
File List
|
1989-07-27
|
18KB
|
507 lines
_80386 PROTECTED MODE AND MULTITASKING_
by Tom Green
[LISTIN╟ ONE]
/************************************************************************/
/* 386.H - structures etc. for the 80386 */
/* By Tom Green */
/************************************************************************/
/* all of these structures are processor dependant, so */
/* you must set code generation for byte alignment */
/* generic descriptor - data, code, system, TSS */
typedef struct descriptor{
unsigned int limit_lo;
unsigned int base_lo;
unsigned char base_mid;
unsigned char type_dpl;
unsigned char limit_hi;
unsigned char base_hi;
}descriptor;
/* call, task, interrupt, trap gate */
typedef struct gate{
unsigned int offset_lo;
unsigned int selector;
unsigned char count;
unsigned char type_dpl;
unsigned int offset_hi;
}gate;
/* this is the layout for a task state segment (TSS) */
/* the fill fields of structures are not used by the 80386 */
/* but must be there */
typedef struct tss{
unsigned int back_link; /* selector for last task */
unsigned int fill1;
unsigned long esp0; /* stack pointer privilege level 0 */
unsigned int ss0; /* stack segment privilege level 0 */
unsigned int fill2;
unsigned long esp1; /* stack pointer privilege level 1 */
unsigned int ss1; /* stack segment privilege level 1 */
unsigned int fill3;
unsigned long esp2; /* stack pointer privilege level 2 */
unsigned int ss2; /* stack segment privilege level 2 */
unsigned int fill4;
unsigned long cr3; /* control register 3, page table */
unsigned long eip; /* instruction pointer */
unsigned long eflags;
unsigned long eax;
unsigned long ecx;
unsigned long edx;
unsigned long ebx;è unsigned long esp;
unsigned long ebp;
unsigned long esi;
unsigned long edi;
unsigned int es;
unsigned int fill5;
unsigned int cs;
unsigned int fill6;
unsigned int ss;
unsigned int fill7;
unsigned int ds;
unsigned int fill8;
unsigned int fs;
unsigned int fill9;
unsigned int gs;
unsigned int filla;
unsigned int ldt;
unsigned int fillb;
unsigned int tbit; /* exception on task switch bit */
unsigned int iomap;
}tss;
#define TSS_SIZE (sizeof(tss))
#define DESCRIPTOR_SIZE (sizeof(descriptor))
#define GATE_SIZE (sizeof(gate))
#define DPL(x) (x<<5)
#define TYPE_CODE_DESCR 0x18
#define TYPE_DATA_DESCR 0x10
#define TYPE_TSS_DESCR 0x09
#define TYPE_CALL_GATE 0x0c
#define TYPE_TASK_GATE 0x05
#define TYPE_INTERRUPT_GATE 0x0e
#define TYPE_TRAP_GATE 0x0f
#define SEG_WRITABLE 0x02
#define SEG_READABLE 0x02
#define SEG_EXPAND_DOWN 0x04
#define SEG_CONFORMING 0x04
#define SEG_ACCESSED 0x01
#define SEG_TASK_BUSY_BIT 0x02
#define SEG_PRESENT_BIT 0x80
#define SEG_GRANULARITY_BIT 0x80
#define SEG_DEFAULT_BIT 0x40
#define SELECTOR_MASK 0xfff8
[LISTING TWO]
/************************************************************************/
/* TASK.C - this code creates and sets up the Global Descriptor */
/* Table and Task State Segments. the code switches to protected */
/* mode, runs tasks, and returns to real mode. */
/* Compile with Turbo C 2.0 */
/* By Tom Green */
/************************************************************************/
#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "386.h"
/* selectors for entries in our GDT */
#define CODE_SELECTOR 0x08
#define DATA_SELECTOR 0x10
#define TASK_1_SELECTOR 0x18
#define TASK_2_SELECTOR 0x20
#define MAIN_TASK_SELECTOR 0x28
#define VID_MEM_SELECTOR 0x30
/* physical address of video ram, mono and color */
#define COLOR_VID_MEM 0xb8000L
#define MONO_VID_MEM 0xb0000L
/* video modes returned by BIOS call */
#define MONO_MODE 0x07
#define BW_80_MODE 0x02
#define COLOR_80_MODE 0x03
/* pointer to a function */
typedef void (func_ptr)(void);
/* extern stuff in mode.asm */
void protected_mode(unsigned long gdt_ptr,unsigned int cseg,unsigned int dseg);
unsigned int load_task_register(unsigned int tss_selector);
void real_mode(unsigned int dseg);
void jump_to_task(unsigned int tss_selector);
/* prototypes for local functions */
void task1(void);
void task2(void);
void init_tss(tss *t,unsigned int cs,unsigned int ds,unsigned char *sp,
func_ptr ip);
void init_gdt_descriptor(descriptor *descr,unsigned long base,unsigned long
limit,unsigned char type);
void print(unsigned int x,unsigned int y,char *s);
void vid_mem_putchar(unsigned int x,unsigned int y,char c);
/* this array of descriptors will be our Global Descriptor Table */
descriptor gdt[10];
/* these are the TSS's for our tasks */ètss main_tss;
tss task_1_tss;
tss task_2_tss;
/* seperate stacks for each task */
unsigned char task_1_stack[1024];
unsigned char task_2_stack[1024];
/* global y location for protected mode screen writes */
/* using descriptor for video ram */
unsigned int y=0;
void main(void)
{
unsigned long base;
unsigned char type;
union REGS r;
/* setup code and data descriptors in GDT */
/* code GDT entry 1 */
/* turn code segment into 20 (and 32) bit physical base address */
base=((unsigned long)_CS)<<4;
/* set descriptor type for a readable code segment */
type=TYPE_CODE_DESCR | SEG_PRESENT_BIT | SEG_READABLE;
init_gdt_descriptor(&gdt[1],base,0xffffL,type);
/* data GDT entry 2 */
/* turn data segment into 20 (and 32) bit physical base address */
base=((unsigned long)_DS)<<4;
/* set descriptor type for a writeable data segment */
type=TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE;
init_gdt_descriptor(&gdt[2],base,0xffffL,type);
/* set up TSS's for tasks here */
/* set descriptor type for a TSS */
type=TYPE_TSS_DESCR | SEG_PRESENT_BIT;
/* put a descriptor for each TSS in the GDT */
/* TSS GDT entry 3, TSS for task1 */
/* turn segment:offset of task1 TSS into physical base address */
base=(((unsigned long)_DS)<<4)+(unsigned int)&task_1_tss;
init_gdt_descriptor(&gdt[3],base,(unsigned long)TSS_SIZE-1,type);
/* TSS GDT entry 4, TSS for task2 */
/* turn segment:offset of task2 TSS into physical base address */
base=(((unsigned long)_DS)<<4)+(unsigned int)&task_2_tss;
init_gdt_descriptor(&gdt[4],base,(unsigned long)TSS_SIZE-1,type);
/* TSS GDT entry 5, TSS for main starting task */è /* turn segment:offset of main TSS into physical base address */
base=(((unsigned long)_DS)<<4)+(unsigned int)&main_tss;
init_gdt_descriptor(&gdt[5],base,(unsigned long)TSS_SIZE-1,type);
/* init the TSS with starting values for each task */
/* task 1 */
init_tss(&task_1_tss,CODE_SELECTOR,DATA_SELECTOR,task_1_stack+
sizeof(task_1_stack),task1);
/* task 2 */
init_tss(&task_2_tss,CODE_SELECTOR,DATA_SELECTOR,task_2_stack+
sizeof(task_2_stack),task2);
/* video ram descriptor GDT entry 6 */
/* set descriptor for a writeable data segment */
type=TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE;
r.h.ah=15; /* get video mode BIOS */
int86(0x10,&r,&r);
/* check if mono mode */
if(r.h.al==MONO_MODE)
init_gdt_descriptor(&gdt[6],MONO_VID_MEM,3999,type);
/* check if color mode */
else if(r.h.al==BW_80_MODE || r.h.al==COLOR_80_MODE)
init_gdt_descriptor(&gdt[6],COLOR_VID_MEM,3999,type);
else{
printf("\nThis video mode is not supported.");
exit(1);
}
/* we are now ready to enter protected mode */
clrscr();
cprintf("\nPress return to enter protected mode.");
getchar();
/* turn segment:offset of GDT into 20 bit physical address */
base=(((unsigned long)_DS)<<4)+(unsigned int)&gdt;
/* this puts us in protected mode */
protected_mode(base,CODE_SELECTOR,DATA_SELECTOR);
/* this loads the task register for the first task */
load_task_register(MAIN_TASK_SELECTOR);
y=3; /* this is line we will start printing on in protected mode */
/* using our descriptor to write to video ram */
print(0,y++,"Entered protected mode in main task");
/* this jumps to first task (which will jump back here eventually) */
jump_to_task(TASK_1_SELECTOR);
print(0,y++,"Returned to main task, leaving protected mode");è
/* return us to real mode */
real_mode(DATA_SELECTOR);
gotoxy(1,22);
cprintf("Returned to real mode. Press return to exit to DOS");
getchar();
clrscr();
}
/* code for task 1 */
void task1(void)
{
while(1){
print(0,y++,"Hello from task1");
jump_to_task(TASK_2_SELECTOR);
/* return to original task (in main()) after several task switches */
if(y>18)
jump_to_task(MAIN_TASK_SELECTOR);
}
}
/* code for task 2 */
void task2(void)
{
while(1){
print(0,y++,"Hello from task2");
jump_to_task(TASK_1_SELECTOR);
}
}
/* this initializes a TSS */
/* inits segment registers, eip, and stack stuff to starting values */
void init_tss(tss *t,unsigned int cs,unsigned int ds,unsigned char *sp,
func_ptr ip)
{
t->cs=cs; /* code selector */
t->ds=ds; /* set these to the data selector */
t->es=ds;
t->ss=ds;
t->fs=ds;
t->gs=ds;
t->eip=(unsigned int)ip; /* address of first instruction to execute */
t->esp=(unsigned int)sp; /* offset of stack in data */
t->ebp=(unsigned int)sp;
}
/* this initializes a descriptor in the Global Decsriptor Table */
/* sets up the base, limit, type, and granularity */
void init_gdt_descriptor(descriptor *descr,unsigned long base,unsigned long
limit,unsigned char type)è{
descr->base_lo=(unsigned int)base;
descr->base_mid=(unsigned char)(base >> 16);
descr->type_dpl=type;
/* if limit > 0xfffffL then we have to set granularity bit and shift */
if(limit > 0xfffffL){
limit = limit >> 12;
descr->limit_hi=((unsigned char)(limit >> 16) & 0xff) |
SEG_GRANULARITY_BIT;
}
else
descr->limit_hi=((unsigned char)(limit >> 16) & 0xff);
descr->limit_lo=(unsigned int)limit;
descr->base_hi=(unsigned char)(base >> 24);
}
/* this routine prints a string using vid_mem_putchar */
void print(unsigned int x,unsigned int y,char *s)
{
while(*s)
vid_mem_putchar(x++,y,*s++);
}
/* this routine writes a character directly to video ram */
/* uses the selector for the descriptor we set up in main */
/* for video ram */
void vid_mem_putchar(unsigned int x,unsigned int y,char c)
{
register unsigned int offset;
char far *vid_ptr;
offset=(y*160) + (x*2);
/* make our far pointer use our special video ram descriptor */
/* yes, we can even use far pointers with selectors */
vid_ptr=MK_FP(VID_MEM_SELECTOR,offset);
*vid_ptr++=c; /* write character */
*vid_ptr=0x07; /* write attribute byte */
}
[LISTING THREE]
;*****************************************************************
; MODE.ASM
; Routines for switching to protected and real mode. Also includes
; routines for loading task register and jumping to a task.
; Assemble with Turbo TASM 1.0
; By Tom Green
;*****************************************************************
.MODEL SMALL
.386P
.DATA
;this is where we stuff address of GDT that is passed
gdtptr LABEL PWORD
dw 50h ;size in bytes of GDT, enough for 10 entries
dd ? ;this is where we will put physical address of GDT
;this is where we will store the address of a task (the selector) that
;we will jump to in jump_to_task
new_task LABEL DWORD
dw 00h
new_select LABEL WORD
dw 00h
;this is where we store address to jump to when we enter protected mode
;in protected_mode
;offset of code where we will jump
p_mode LABEL DWORD
dw OFFSET protect
;put selector of protected mode code segment here
p_mode_select LABEL WORD
dw 0
.CODE
PUBLIC _real_mode,_protected_mode,_jump_to_task
PUBLIC _load_task_register
;*****************************************************************
; void protected_mode(unsigned long gdt_ptr,unsigned int cseg,
; unsigned int dseg) - puts 386 in protected mode and loads segment
; registers with code and data selectors passed (cs and ds
; parameters). pass this routine a 32 bit physical address of the
; Global Descriptor Table (gdt_ptr parameter). Turns interrupts
; off while we run in protected mode.
;*****************************************************************
_protected_mode PROC NEAR
push bp
mov bp,sp
mov ax,[bp+4] ;get low word of address of GDT
mov dx,[bp+6] ;get high word of address of GDT
mov WORD PTR gdtptr+4,dx ;store high word of address of GDT
mov WORD PTR gdtptr+2,ax ;store low word of address of GDTè mov ax,[bp+8] ;get selector for code descriptor
mov dx,[bp+10] ;get selector for data descriptor
mov p_mode_select,ax ;put code selector in our jmp pointer
mov eax,0 ;prepare to zero out eflags
push eax
popfd ;zero out eflags
lgdt PWORD PTR gdtptr ;load gdt register with limit and ptr
mov eax,cr0
or eax,1
mov cr0,eax ;turn protected mode on
jmp DWORD PTR p_mode ;this will jump to protect through ptr
;(cs will be loaded with code selector)
protect:
;we are now running in protected mode, and we will load segment registers
;with selectors that look like our code is still in real mode
mov ss,dx ;load segment registers with data selector
mov ds,dx
mov es,dx
mov fs,dx
mov gs,dx
mov ax,0
lldt ax ;make sure ldt register has 0
pop bp
ret
_protected_mode ENDP
;*****************************************************************
; void load_task_register(unsigned int tss_selector) -
; loads task register with TSS selector
;*****************************************************************
_load_task_register PROC NEAR
push bp
mov bp,sp
ltr [bp+4] ;load task register with selector for current task
pop bp
ret
_load_task_register ENDP
;*****************************************************************
; void real_mode(unsigned int dseg) -
; returns 386 to real mode. pass this routine the selector for
; a 64k data segment so we can return to real mode. this
; routine assumes we are executing from a 64k data segment.
; (80386 segment registers must have selector of segment with
; 64k limit to return to real mode)
;*****************************************************************
_real_mode PROC NEAR
push bp
mov bp,sp
mov ax,[bp+4] ;get selector for data segment
mov ds,ax ;now make sure all segment registers
mov es,ax ;contain selector to 64k segment
mov fs,ax ;must have this to return to real mode
mov gs,ax
mov ss,axè mov eax,cr0
and eax,07ffffffeh
mov cr0,eax ;protected mode off
jmp FAR PTR flush ;flush queue and set cs for real mode
;now cs will be loaded with correct
;segment for real mode
flush:
mov ax,DGROUP ;restore data seg registers for real mode
mov ds,ax
mov ss,ax
mov es,ax
sti ;interrupts back on for real mode
pop bp
ret
_real_mode ENDP
;*****************************************************************
; void jump_to_task(unsigned int tss_selector) -
; jumps to 386 TSS task. pass this routine the selector of the
; TSS of the task you want to jump to.
;*****************************************************************
_jump_to_task PROC NEAR
push bp
mov bp,sp
mov ax,[bp+4] ;get selector of new task
mov new_select,ax ;store it in pointer
jmp DWORD PTR new_task ;jump to task through selector:offset ptr
pop bp
ret
_jump_to_task ENDP
END