Images

Advance C - PART III


Advance C
C is a general-purpose, block structuredprocedural,imperative computer programming language developed in1972 by Dennis Ritchie at the Bell Telephone Laboratoriesfor use with the Unix operating system. It has since spread to many other platforms. Although C was designed as a system implementation language, it is also widely used for applications. C has also greatly influenced many other popular languages,especially C++, which was originally designed as an extension to C.
Despite its popularity, C has been widely criticized. Such criticisms fall into two broad classes: desirable operations that are too hard to achieve using unadorned C, and undesirable operations that are too easy to accidentally invoke while using C. Putting this another way, the safe, effective use of C requires more programmer skillexperienceeffort, and attention to detail than is required for some other programming languages.
This Tutorial Covers a lot of Advance C concept(such as Graphic Under C, TSRs, Drivers, and Kernal basics). As said earlier, effective use of C requires more programmer skill, experience, effort, and attention to detail than is required for some other programming languages, so be careful while using examples given and/or doing some R&D based on the code samples provided.


TSR Programming with C
Device Driver Programming
Device driver” and “Driver” are interchangeably used in Programming world. Device drivers are the programs that control the functioning of peripherals. According to me, writing device driver is one of the easier things in programming. What all you need to know for device driver programming is good knowledge of hardware components. You may also need to know, how to access those hardware components through programs. In this chapter let’s see how to write our own device driver.
As I said earlier, device drivers are the programs that control the functioning of peripherals like keyboard, printer, etc. More specifically, they are the modules of an operating system. MS DOS device drivers are with .SYS extensions. Since drivers drive peripheral devices, they get loaded into the memory when we bootup the system. So obviously, they remain resident in memory, but they are not considered as normal TSRs.
As drivers are the modules of an Operating System, one has to modify the OS whenever he adds new device to his system. Fortunately the installable device drivers technology available with MS DOS gives more flexibility to the user. It avoids direct operations or modifications of Operating System. The user can simply install a new device in a system, copy the driver files to boot disk and edit the system configuration file. Thus it clearly avoids complexity.
Types of MS DOS device drivers
  1. Character device drivers
  2. Block device drivers
Character device drivers
Character device drivers correspond to single byte. That is, these device drivers controls peripheral devices that perform input and output one character (i.e., one byte) at a time. The example for such devices are terminalprinter etc.
Block device drivers
Block device drivers correspond to block rather than byte. Even though they can be used with other devices, they are usually written to control random access storage devices such as floppy drives.
Writing our own device driver
Writing device driver is not a tough job as one may think. But nowadays device driver programming is not needed as the peripheral device vendors provide powerful drivers along with their products. So I avoid indepth explanation about the device driver programming.
BUF160
BUF160 is a device driver for expanding the default keyboard buffer from 16 bytes to 160 bytes. 16 bytes restriction of default keyboard buffer might be strange to the people who are unnoticingly using keyboard buffer expansion program. If you don’t use any keyboard buffer expansion utility and if your keyboard buffer is still 16 bytes in size (i.e., it can hold only 16 character when you work under command prompt), you may try this BUF160. BUF160 is a good device driver. The recent version is 1.6a. It works by installing itself as the standard keyboard buffer in the BIOS. It can only do this if it is in the same segment as the BIOS, so you are advised to install it as the first device driver. While it installs itself into the BIOS, it also installs a device driver called KBUFFER. Anything written to KBUFFERends up in the keyboard buffer. I suggest you to look into the memory map found with Ralf Brown’s Interrupt List for understanding BIOS data area.
Source code
Following is the source code of BUF160. It is written in assembly. As the code is more clear, I don’t want to port it to Turbo C. I hope this real code will help you to understand the concepts behind device drivers. Refer the comment line for explanations.
title BUF160
page 58,132
;
; BUF160.ASM
;
;**********************************************************************
; Compilation flags
;**********************************************************************
TRANSFER
equ 1
;Enables keyboard buffer transfer v1.4
; procedure if enabled (1)
v1.4
USE286
equ 0
;Should we use 286 (and later)
v1.5
; CPU specific instructions?
v1.5
PRIVATESTACK
equ 1
;Use own stack?
v1.6
PROGNAME
equ 'BUF160'
VERSION
equ 'v1.6a, 29 January 1992'
;**********************************************************************
; General equates
;**********************************************************************
BUFSIZE
equ 160
;What is the size of the keyboard buffer
STACKSZ
equ 100h
;What is the size of the private buffer
SUCCESS
equ 0100h
ERROR equ 8100h
BUSY equ 0300h
CR
equ 13
;Carriage Return
LF
equ 10
;Line Feed
TERM equ '$'
;DOS printing terminator character
;**********************************************************************
; Data structures
;**********************************************************************
dqq struc
ofs dw
?
segw dw
?
;changed from 'seg' to keep MASM 5.0 happy v1.4
dqq ends
rqq struc
;Request header structure
len db
?
;length of request block (bytes)
unit db
?
;unit #
code db
?
;driver command code
status
dw
?
;status return
q1
dd
?
;8 reserved bytes
q2
dd
?
mdesc db
?
;donno
trans dd
?
count dw
?
rqq ends
;**********************************************************************
; Pointers to BIOS data segment, v1.4
;**********************************************************************
BIOS_DATA_SEG
equ 40H
;MASM had prob using BIOS_DATA in
calculations,
; so this typeless constant introduced. v1.6
BIOS_DATA SEGMENT AT BIOS_DATA_SEG
org 1AH
BUFFER_GET dw
?
;org 1ah
BUFFER_PUT dw
?
;org 1ch
org 80H
BUFFER_START
dw
?
;org 80h
BUFFER_END dw
?
;org 82h
BIOS_DATA ENDS
;**********************************************************************
; The actual program
;**********************************************************************
Cseg segment
byte
assume
cs:Cseg,ds:Cseg,es:Cseg,ss:Cseg
org 0
; no offset, it's a .SYS file
start equ $
; define start=CS:0000
IF USE286
;
v1.5
.286
%OUT Compiling 286 code ...
ELSE
%OUT Compiling generic 8086 code ...
ENDIF
IF PRIVATESTACK
%OUT Using private stack ...
ELSE
%OUT Not using private stack ...
ENDIF
IF TRANSFER
%OUT Including keyboard transfer code ...
ELSE
%OUT Not including keyboard transfer code ...
ENDIF
public
header
header
label near
dd
-1
;pointer to next device
dw
8000h
;type device
dw
Strat
;strategy entry point
dw
Intr
;interrupt entry point
db
'KBUFFER '
;device name
public
req
req dd
?
;store request header vector here
public
queue_start,queue_end
queue_start dw
BUFSIZE dup (0) ;our expanded keyboard buffer
queue_end equ $ - start
;calculate offset as typeless
constant
IF PRIVATESTACK
;
v1.6
stack_end db STACKSZ dup (0)
;use our own private data stack
stack_start equ $
oldss dw
0
oldsp dw
0
oldax dw
0
ENDIF
;*********************************************************************
; Strategy procedure
;
Save the pointer to the request header for Intr in the req area.
;
Enters with pointer in es:bx
;*********************************************************************
public
Strat
Strat proc far
mov cs:[req].ofs,bx
mov cs:[req].segw,es ;
v1.4
ret
Strat endp
;**********************************************************************
; The main interrupt (driver)
;
This is the actual driver. Processes the command contained in the
;
request header.
(Remember, req points to the request header.)
;**********************************************************************
public
Intr
ASSUME
ds:Cseg, es:NOTHING
;
v1.4
Intr proc far
IF PRIVATESTACK
;If using private stack, process
mov cs:oldax, ax
;
v1.6
cli
; turn ints off
mov ax, ss
mov cs:oldss, ax
mov cs:oldsp, sp
mov sp, offset stack_start
mov ax, cs
mov ss, ax
sti
; turn ints back on
mov ax, cs:oldax
ENDIF
push ds
;save everything in sight
push es
IF USE286
pusha
;
v1.5
ELSE
push ax
push bx
push cx
push dx
push di
push si
ENDIF
mov ax,cs
mov ds,ax
;DS=code segment
les bx,req
;point to request hdr
v1.4a
mov si,offset cmd_table
;our function table
mov cl,es:[bx].code
;get command
xor ch,ch
;clear msb
v1.4
shl cx,1
;*2 for word addresses
add si,cx
;add to table base
call word ptr [si]
;call our function
v1.4a
les bx,cs:req
;get back request hdr vector
mov es:[bx].status,ax ;return status
IF USE286
popa
;
v1.5
ELSE
pop si
;clean everything up
pop di
pop dx
pop cx
pop bx
pop ax
ENDIF
pop es
pop ds
IF PRIVATESTACK
mov ax, cs:oldss
;
v1.6
cli
; turn ints off
mov ss, ax
mov sp, cs:oldsp
mov ax, cs:oldax
sti
; turn ints on
ENDIF
ret
public
cmd_table
cmd_table:
;command routing table
dw
Cmd_Init
;0=initialization (we do that)
dw
Cmd_None
;1=media check (always SUCCESS)
dw
Cmd_None
;2=build BIOS param block (ditto)
dw
Cmd_None
;3=IO control input (ditto)
dw
Cmd_None
;4=input from device (ditto)
dw
Cmd_None
;5=nondest input no-wait (ditto)
dw
Cmd_None
;6=input status (ditto)
dw
Cmd_None
;7=flush input queue (ditto)
dw
Cmd_Output
;8=output to device (we do that)
dw
Cmd_Output
;9=output with verify (same thing)
dw
Cmd_Output_Status ;A=output status (we do that)
dw
Cmd_None
;B=flush output queue (always SUCCESS)
dw
Cmd_None
;C=IO control output (ditto)
;*********************************************************************
; Cmd_Output procedure
;*********************************************************************
public
Cmd_Output
Cmd_Output proc near
mov ax,BIOS_DATA
mov ds,ax
;BIOS data area
ASSUME
ds:BIOS_DATA
;keep MASM happy
v1.4
mov cx,es:[bx].count
les bx,es:[bx].trans
Output_Loop:
mov al,es:[bx]
inc bx
cli
mov di,BUFFER_PUT
;next free space
v1.4
call Buf_Wrap
;add 2, check for wraparound
cmp di,BUFFER_GET
;is the buffer full?
v1.4
sti
;ints back on
v1.4
je
Output_Error
;buffer is full, error
v1.4
xchg BUFFER_PUT,di
;save the old, get the new
v1.4
xor ah,ah
mov [di],ax
;
v1.4
loop Output_Loop
public
Cmd_None
;
v1.4
Cmd_None:
;share this code
v1.4
mov ax,SUCCESS
ret
Output_Error:
mov ax,ERROR
ret
Cmd_Output endp
;*********************************************************************
; Buf_Wrap procedure
;*********************************************************************
public
Buf_Wrap
Buf_Wrap
proc near
inc di
inc di
cmp di,BUFFER_END
;hit end yet?
v1.4
je
Wrap
;>=, wrap around
v1.4
ret
Wrap:
mov di,BUFFER_START
;force ptr to start
v1.4
ret
Buf_Wrap
endp
;*********************************************************************
; Cmd_Output_Status procedure
;*********************************************************************
public
Cmd_Output_Status
Cmd_Output_Status proc near
mov ax,BIOS_DATA
mov ds,ax
mov di,BUFFER_PUT
;ptr to next free space
v1.4
call Buf_Wrap
;wraparound if necessary
cmp di,BUFFER_GET
;same as next char to get?
v1.4
jne Cmd_None
;ok, return SUCCESS
v1.4a
mov ax,BUSY
ret
Cmd_Output_Status endp
public
last_code
last_code label near
;*********************************************************************
; Initialization (installation) procedure
;*********************************************************************
public
Cmd_Init
Cmd_Init
proc near
mov ax,cs
mov ds,ax
mov es,ax
;
v1.4a
ASSUME
ds:Cseg,es:Cseg
;
v1.4a
; Is our new keyboard buffer within reach of the near pointers in
;BIOS_DATA?
cmp ax,(0fffh+BIOS_DATA_SEG-queue_end/10h);
v1.6
ja
Init_Error
;No, too far away
mov dx,offset banner ;Yes, 'Buf160 loaded'
mov ah,9
;DOS display msg
int 21h
mov bx,0
;Initialize size of buf
v1.5
mov cx,BIOS_DATA
;PRESERVE THIS!
v1.4
mov ds,cx
;BIOS data area
ASSUME
ds:BIOS_DATA
;
v1.4
cli
;turn off ints
v1.6a
IF
TRANSFER
public
Transfer_Buffer
Transfer_Buffer:
mov si,BUFFER_GET
;next key to read
v1.4
mov dx,BUFFER_PUT
;next empty space
v1.4a
mov di,offset queue_start ;gonna stuff here
v1.4a
cld
;insure fwd
v1.4
Transfer_Loop:
cmp si,dx
;hit empty yet?
v1.4a
je
Transfer_Done
;yep, transfer complete
lodsw
;snarf the kbd word
stosw
;stuff in OUR buffer
v1.4a
inc bx
;increment counter
v1.5
inc bx
;increment counter
v1.5
cmp si,BUFFER_END
;hit kbd buffer's end yet?
v1.4
jne Transfer_Loop
; nope, keep going
mov si,BUFFER_START
;yep, wrap around to start
v1.4
jmp Transfer_Loop
; and keep going
public
Transfer_Done
Transfer_Done:
ENDIF
mov ax,cs
;Code Segment
sub ax,cx
; calculate difference b/w bios & this
IF USE286
shl ax,4
;
v1.5
ELSE
shl ax,1
;remainder * 16 (paras to bytes)
shl ax,1
shl ax,1
shl ax,1
ENDIF
mov cx,ax
;CX = driver starting offset
add ax,offset queue_start ;AX = queue_start offset
mov BUFFER_START,ax
;init BIOS buffer pointers
v1.4
mov BUFFER_GET,ax
;
v1.4
add ax,bx
;here'e next free space
mov BUFFER_PUT,ax
;tell BIOS
v1.4
mov ax,cx
;get back driver starting offset v1.4a
add ax,queue_end
;code start + queue end
v1.4a
mov BUFFER_END,ax
;tell BIOS
v1.4
sti
;restore ints
v1.6a
les bx,cs:[req]
;complete driver header
mov es:[bx].trans.ofs,offset last_code ;driver end
jmp short Stuff_Seg
;share code, return success v1.4a
public
Init_Error
ASSUME
ds:Cseg,es:Cseg
;
v1.4
Init_Error:
mov dx,offset msg_err ;'Buf160 too far...'
mov ah,9
;display msg
int 21h
les bx,cs:[req]
;complete driver header
v1.6
IF
0
;not sure if it works.
mov es:[bx].trans.ofs,0
ELSE
mov es:[bx].trans.ofs,offset last_code
ENDIF
Stuff_Seg:
;
v1.4a
mov es:[bx].trans.segw,cs ;
v1.4
mov ax,SUCCESS
ret
Cmd_Init
endp
public
banner, msg_err
banner
db
PROGNAME,' ',VERSION,' installed.',CR,LF
;v1.4
db
'Keyboard now has buffer of 160 characters.'
IF PRIVATESTACK
db
' Using private stack.'
ENDIF
db
CR,LF,CR,LF,TERM
msg_err
db
PROGNAME,' too far from BIOS data area.'
;v1.4
db
CR,LF,CR,LF,TERM
Intr endp
Cseg ends
end
Compiling BUF160
To compile with Turbo Assembler use:
tasm BUF160
tlink BUF160
exe2bin BUF160.exe BUF160.sys
To compile with Microsoft Assembler use:
masm BUF160
link BUF160
exe2bin BUF160.exe BUF160.sys
Installing BUF160
To install BUF160, insert the following line in your config.sys:
DEVICE=<path>BUF160.SYS

Operating System Development
Writing Your Own OS
Operating System is nothing but collection of programs for managing system resources like CPU, memory, storage device etc. Study of the Operating System is one of the vastest areas. This chapter does not deal with the details about Operating System. And in this chapter I would like to show you how OS can be written in Turbo C. However you may not be able to code your Operating System without depth knowledge of memory management, processor scheduling etc. So I strongly recommend you to go through a good Operating System book for indepth knowledge. According to me most of the people are not usingTurbo C to write OS, because Turbo C is 16bit. Also people mainly hangout with Assembly language for a better and tight code.
EZOS_86
EZOS_86 is a simple multitasking kernel written in Turbo C for x86 machines in 1996-97. Operating Systems are usually protected and licensed according to GNU’s General Public License and so this EZOS_86! So if you modify or rewrite this source code, you must acknowledge the author Scott A. Christensen and you are expected to keep the name of the revised OS as EZOS_86, but you can change the version. Regarding OS and other software, violation of copyright is treated as high offense. So beware of the licenses!
Notes
The author added following note:
EZOS_86 is a simple multitasking kernel for the x86 family. It is written in 100% C source (it uses Turbo C extensions to access the registers). If you need a tight, fast, hand-coded, assembly kernel, forget this one! The main emphasis here is to keep it simple: no linked lists, no dynamic allocation, no complicated task scheduling, no assembly language, etc. Yes, this can be embedded! The scheduler is very rudimentary. It is preemptive, but with a strictly prioritized order.
There is no protection from starvation; if a higher priority task spins the CPU, the lower priority tasks will never execute. Programs for embedded applications are often event driven and properly written will work fine. On the other hand, it wouldn't be that hard to change the scheduler to a round robin method if desired.
The scheduler always traverses the Task Control Block (TCB) array from the beginning (&tcb[0]). The first task encountered that is eligible to run is the one executed. At least one task MUST always be eligible to run; hence the "null" task, which is created as the lowest priority and NEVER, sleeps. The same task function can have multiple instances. For example you could call OsTaskCreate( ) three times and pass task0 as the function all three times. Of course you must specify a unique stack and tcb. The parameter passed to task0 can identify the particular instance of the function.
Reentrancy issues:
  • use the runtime library at your own risk (reason for direct video)
  • floating point is not reentrant; use semaphore protection or only do floating point in one task.
Semaphores:
  • clearing semaphore does not cause task switch; call OsSchedule( ) to yield. This can throttle throughput. One could have null task continuously scan TCBs for eligible task and yield.
  • OsSemClear( ) returns TRUE if higher priority task waiting on sem
  • multiple tasks can sleep on same semaphore
  • ok to clear semaphore from within interrupt routine
As written this code will run a demo on an IBM clones and even clean up upon exit returning nicely backs to DOS. It creates the file "out" to dump the stack contents. Interrupt routines use the current task's stack. Be careful not to exceed your allocated stack space; very strange results can occur. Compile it with Turbo C with optimization off.
Wishlist:
  • simple file functions to read/write directly to IDE HD with FAT16
  • multitasking capable floating point support
  • some sort of built in debugging capability (TBUG.ZIP looks like a good start)
  • runtime calculation of cpu utilization
  • _simplified_ malloc for embedded applications
Kernel Source Code
/*
* ezos_86.c
*
*
* All Rights Reserved
*
* This file is part of the EZOS_86 multitasking kernel.
*
*
* version description
* --------------------------------------------------------------
* 0.01.00 initial release
*
*/
#include <dos.h>
#include <stdio.h>
#include <conio.h>
#include <stdarg.h>
/*--------------------------------------------------------------*/
#define TRUE (0 == 0)
#define FALSE (0 != 0)
#define RUNNING 0
#define RUN_ASAP 1
#define SLEEPING 2
#define PENDING 3
#define SUSPENDED 4
#define KILLED 5
#define ALL_KILLED -2
#define NOT_STARTED -1
#define TICK_VECT 8
#define MAX_TASKS 10
#define STACKSIZE 1024
#define PENDING_SEM_REQUEST 0
#define PENDING_SEM_WAIT 1
#define TSK_ERR_ -1000
#define TSK_ERR_TIMEOUT (TSK_ERR_ - 0)
#define OS_INFINITE_WAIT -1L
#define OS_IMMEDIATE_RETURN 0L
#define OsEnable() enable()
#define OsDisable() disable()
#define ATTR ((unsigned int) (((BLACK<<4)|WHITE)<<8))
#define schedule() \
{ \
int si; \

static PTCB_REC pTCBsi; \
static PTCB_REC pTCBsc; \
\
if(killedTasks == numTasks) \
{ \
_SP = mainSP; \
_SS = mainSS; \
mainSleep = FALSE; \
curTask = ALL_KILLED; \
} \
else \
{ \
for(si = 0, pTCBsi = tcb; si < numTasks; si++, pTCBsi++) \
{ \
if(pTCBsi->taskStatus == RUNNING) \
break; \
if(pTCBsi->taskStatus == RUN_ASAP) \
{ \
pTCBsc = &tcb[curTask]; \
if(pTCBsc->taskStatus == RUNNING) \
pTCBsc->taskStatus = RUN_ASAP; \
pTCBsc->taskSP = _SP; \
pTCBsc->taskSS = _SS; \
pTCBsi->taskStatus = RUNNING; \
_SP = pTCBsi->taskSP; \
_SS = pTCBsi->taskSS; \
curTask = si; \
break; \
} \
} \
} \
}
/*--------------------------------------------------------------*/
typedef void (far cdecl *FUNCPTR)();
typedef struct
{
unsigned int r_bp;
unsigned int r_di;
unsigned int r_si;
unsigned int r_ds;
unsigned int r_es;
unsigned int r_dx;
unsigned int r_cx;
unsigned int r_bx;
unsigned int r_ax;

FUNCPTR taskStartAddr;
unsigned int r_flags;
FUNCPTR taskExitReturn;
void * pTaskParam;
} STACK_REC;
typedef struct
{
unsigned int taskStatus;
unsigned int taskSP;
unsigned int taskSS;
long ticks;
int semState;
int * pSem;
} TCB_REC, *PTCB_REC;
/*--------------------------------------------------------------*/
void far interrupt OsTickIsr(void);
int far interrupt OsSchedule(void);
void far OsTaskKill(void);
void OsTaskCreate(PTCB_REC, FUNCPTR, void *,
unsigned char far *, int);
long OsTranslateMilsToTicks(long);
void OsInstall(void);
void OsRun(void);
void OsDeinstall(void);
void OsSleep(long);
void OsSleepTicks(long);
int OsSemClear(int *);
void OsSemSet(int *);
int OsSemWait(int *, long);
int OsSemSetWait(int *, long);
int OsSemRequest(int *, long);
int OsDisableStat(void);
void dumpStack(FILE *, unsigned char *, int);
void tprintf(const char *, ...);
void tputs(const char *);
void sout(char *);
void incRow(void);
void far task0(void *);
void far task1(void *);
void far task2(void *);
void far taskNull(void *);
/*--------------------------------------------------------------*/
void (far interrupt *oldTickIsr)(void);
int numTasks = 0;
int killedTasks = 0;
int curTask = NOT_STARTED;
int mainSleep = TRUE;
unsigned int mainSP;
unsigned int mainSS;
TCB_REC tcb[MAX_TASKS];
unsigned int _stklen = (STACKSIZE * MAX_TASKS) + 1024;
int itick = 0;
unsigned int (far *screen)[80];
int row = 0;
int col = 0;
int tickSem = 1;
int goSem = 1;
int screenSem = 0;
/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
void main()
{
unsigned char stack0[STACKSIZE];
unsigned char stack1[STACKSIZE];
unsigned char stack2[STACKSIZE];
unsigned char stackNull[STACKSIZE];
FILE * f;
clrscr();
puts("\n\n EZOS_86 multitasking kernel");
puts(" Copyright (C) 1996-97 Scott A. Christensen");
delay(5000);
clrscr();
gotoxy(1, 24);
screen = MK_FP(0xB800, 0);
OsTaskCreate(&tcb[0], task0, (void *) 100, stack0, STACKSIZE);
OsTaskCreate(&tcb[1], task1, (void *) 101, stack1, STACKSIZE);
OsTaskCreate(&tcb[2], task2, (void *) 102, stack2, STACKSIZE);
OsTaskCreate(&tcb[3], taskNull, NULL, stackNull, STACKSIZE);
OsInstall();
OsRun();
OsDeinstall();
f = fopen("out", "wb");
dumpStack(f, stack0, STACKSIZE);
dumpStack(f, stack1, STACKSIZE);
dumpStack(f, stack2, STACKSIZE);
dumpStack(f, stackNull, STACKSIZE);
fclose(f);
puts("done, hit key to continue...");
getch();
}
/*--------------------------------------------------------------*/
void dumpStack(
FILE * f,
unsigned char * stack,
int size
)
{
int i;
char buf[80];
char string[80];
string[0] = 0;
for(i = 0; i < size; i++)
{
if(i % 16 == 0)
fprintf(f, "%04X:%04X ", FP_SEG(&stack[i]), FP_OFF(&stack[i]));
fprintf(f, "%02X ", stack[i]);
if(isalnum(stack[i]) || stack[i] == ' ')
{
buf[0] = stack[i];
buf[1] = 0;
strcat(string, buf);
}
else
strcat(string, ".");
if(i % 16 == 15)
{
fprintf(f, " %s\r\n", string);
string[0] = 0;
}
}
fprintf(f, "\r\n");
}
/*--------------------------------------------------------------*/
void OsInstall()
{
oldTickIsr = getvect(TICK_VECT);
setvect(TICK_VECT, OsTickIsr);
}
/*--------------------------------------------------------------*/
void OsRun()
{
while(mainSleep);
}
/*--------------------------------------------------------------*/
void OsDeinstall()
{
setvect(TICK_VECT, oldTickIsr);
}
/*--------------------------------------------------------------*/
void far interrupt OsTickIsr()
{
int i;
static PTCB_REC pTCBi;
switch(curTask)
{
case ALL_KILLED:
break;
case NOT_STARTED:
mainSP = _SP;
mainSS = _SS;
pTCBi = tcb;
pTCBi->taskStatus = RUNNING;
_SP = pTCBi->taskSP;
_SS = pTCBi->taskSS;
curTask = 0;
break;
default:
itick++;
for(i = 0, pTCBi = tcb; i < numTasks; i++, pTCBi++)
{
if((pTCBi->taskStatus == SLEEPING) ||
(pTCBi->taskStatus == PENDING))
if(pTCBi->ticks > 0L)
if(--(pTCBi->ticks) == 0L)
pTCBi->taskStatus = RUN_ASAP;
}
schedule();
break;
}
oldTickIsr();
}
/*--------------------------------------------------------------*/
int far interrupt OsSchedule()
{
OsDisable();
schedule();
return _AX; /* dummy value */
}
/*--------------------------------------------------------------*/
void far OsTaskKill()
{
OsDisable();
killedTasks++;
tcb[curTask].taskStatus = KILLED;
OsSchedule();
}
/*--------------------------------------------------------------*/
void OsTaskCreate(
PTCB_REC pTCB,
FUNCPTR func,
void * pTaskParam,
unsigned char far * pStack,
int stackSize
)
{
STACK_REC far * pStackRec;
int i;
for(i = 0; i < stackSize; i++)
pStack[i] = 0xFF;
pStackRec = (STACK_REC far *) (pStack + stackSize -
sizeof(STACK_REC));
pStackRec->r_bp = 0;
pStackRec->r_di = 0;
pStackRec->r_si = 0;
pStackRec->r_ds = _DS;
pStackRec->r_es = _DS;
pStackRec->r_dx = 0;
pStackRec->r_cx = 0;
pStackRec->r_bx = 0;
pStackRec->r_ax = 0;
pStackRec->taskStartAddr = func;
pStackRec->r_flags = 0x0200;
pStackRec->taskExitReturn = OsTaskKill;
pStackRec->pTaskParam = pTaskParam;
pTCB->taskStatus = RUN_ASAP;
pTCB->taskSP = FP_OFF(pStackRec);
pTCB->taskSS = FP_SEG(pStackRec);
numTasks++;
}
/*--------------------------------------------------------------*/
long OsTranslateMilsToTicks(
long mils
)
{
long x;
if(mils < 0L)
return -1L;
if(!mils)
return 0L;
x = ((mils * 91L) / 5000L) + 1L; /* 18.2 ticks per sec */
return x;
}
/*--------------------------------------------------------------*/
void OsSleep(

long mils
)
{
long ticks;
ticks = OsTranslateMilsToTicks(mils);
OsSleepTicks(ticks);
}
/*--------------------------------------------------------------*/
void OsSleepTicks(
long ticks
)
{
PTCB_REC pTCB;
if(ticks <= 0L)
return;
OsDisable();
pTCB = &tcb[curTask];
pTCB->taskStatus = SLEEPING;
pTCB->ticks = ticks;
OsSchedule();
}
/*--------------------------------------------------------------*/
int OsSemClear(
int * pSem
)
{
int i;
STACK_REC far * pStackRec;
int processedRequest;
PTCB_REC pTCB;
int higherEligible;
int intsEnabled;
intsEnabled = OsDisableStat();
if(!*pSem)
{
if(intsEnabled)
OsEnable();

return FALSE;
}
*pSem = 0;
processedRequest = FALSE;
higherEligible = FALSE;
for(i = 0, pTCB = tcb; i < numTasks; i++, pTCB++)
{
if((pTCB->taskStatus == PENDING) && (pTCB->pSem == pSem))
{
switch(pTCB->semState)
{
case PENDING_SEM_REQUEST:
if(processedRequest)
break;
processedRequest = TRUE;
*pSem = 1;
/* !!! no break here !!! */
case PENDING_SEM_WAIT:
pStackRec = MK_FP(pTCB->taskSS, pTCB->taskSP);
pStackRec->r_ax = 0;
pTCB->taskStatus = RUN_ASAP;
if(i < curTask)
higherEligible = TRUE;
break;
}
}
}
if(intsEnabled)
OsEnable();
return higherEligible;
}
/*--------------------------------------------------------------*/
void OsSemSet(
int * pSem
)
{
int intsEnabled;
intsEnabled = OsDisableStat();
*pSem = 1;

if(intsEnabled)
OsEnable();
}
/*--------------------------------------------------------------*/
int OsSemWait(
int * pSem,
long mils
)
{
long ticks;
PTCB_REC pTCB;
OsDisable();
if(!*pSem)
{
OsEnable();
return 0;
}
ticks = OsTranslateMilsToTicks(mils);
if(!ticks)
{
OsEnable();
return TSK_ERR_TIMEOUT;
}
pTCB = &tcb[curTask];
pTCB->taskStatus = PENDING;
pTCB->semState = PENDING_SEM_WAIT;
pTCB->pSem = pSem;
pTCB->ticks = ticks;
_AX = TSK_ERR_TIMEOUT;
return OsSchedule();
}
/*--------------------------------------------------------------*/
int OsSemSetWait(
int * pSem,

long mils
)
{
OsDisable();
OsSemSet(pSem);
return OsSemWait(pSem, mils);
}
/*--------------------------------------------------------------*/
int OsSemRequest(
int * pSem,
long mils
)
{
long ticks;
PTCB_REC pTCB;
OsDisable();
if(!*pSem)
{
*pSem = 1;
OsEnable();
return 0;
}
ticks = OsTranslateMilsToTicks(mils);
if(!ticks)
{
OsEnable();
return TSK_ERR_TIMEOUT;
}
pTCB = &tcb[curTask];
pTCB->taskStatus = PENDING;
pTCB->semState = PENDING_SEM_REQUEST;
pTCB->pSem = pSem;
pTCB->ticks = ticks;
_AX = TSK_ERR_TIMEOUT;

return OsSchedule();
}
/*--------------------------------------------------------------*/
int OsDisableStat()
{
unsigned int flags;
flags = _FLAGS;
OsDisable();
return flags & 0x0200;
}
/*--------------------------------------------------------------*/
void tprintf(
const char * format,
...
)
{
va_list argPtr;
char buf[100];
struct time t;
va_start(argPtr, format);
vsprintf(buf + 18, format, argPtr);
va_end(argPtr);
OsSemRequest(&screenSem, OS_INFINITE_WAIT);
gettime(&t);
sprintf(buf, "-T%02d(%02d:%02d:%02d.%02d)",
curTask, t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);
buf[17] = ' ';
sout(buf);
OsSemClear(&screenSem);
}
/*--------------------------------------------------------------*/
void tputs(

const char * string
)
{
struct time t;
char buf[100];
OsSemRequest(&screenSem, OS_INFINITE_WAIT);
gettime(&t);
sprintf(buf, "-T%02d(%02d:%02d:%02d.%02d) %s\n",
curTask, t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund, string);
sout(buf);
OsSemClear(&screenSem);
}
/*--------------------------------------------------------------*/
void sout(
char * p
)
{
while(*p)
{
switch(*p)
{
case '\r':
col = 0;
break;
case '\n':
col = 0;
incRow();
break;
case '\t':
sout(" ");
break;
default:
screen[row][col] = ATTR | ((unsigned int) *p);
if(++col > 79)
{
col = 0;

incRow();
}
break;
}
p++;
}
}
/*--------------------------------------------------------------*/
void incRow()
{
int r;
int c;
if(++row > 24)
{
for(r = 0; r < 24; r++)
for(c = 0; c < 80; c++)
screen[r][c] = screen[r + 1][c];
for(c = 0; c < 80; c++)
screen[24][c] = ATTR | ((unsigned int) ' ');
row = 24;
}
}
/*--------------------------------------------------------------*/
void far task0(
void * pTaskParam
)
{
int val = (int) pTaskParam;
int i;
long j;
int rc;
OsSemWait(&goSem, OS_INFINITE_WAIT);
tprintf("init val passed = %d\n", val);
for(i = 0; i < 7; i++)
{
rc = OsSemWait(&tickSem, 300L);
switch(rc)
{
case 0:

tputs("OsSemWait successful");
OsSleep(150L);
break;
case TSK_ERR_TIMEOUT:
tputs("OsSemWait failed, error = TSK_ERR_TIMEOUT");
break;
default:
tprintf("OsSemWait failed, error = %d\n", rc);
break;
}
OsSleep(100L);
}
}
/*--------------------------------------------------------------*/
void far task1(
void * pTaskParam
)
{
int val = (int) pTaskParam;
int i;
OsSemWait(&goSem, OS_INFINITE_WAIT);
tprintf("init val passed = %d\n", val);
for(i = 0; i < 3; i++)
{
OsSleep(500L);
tputs("");
}
tputs("clearing tickSem");
OsSemClear(&tickSem);
OsSleep(1000L);
tputs("");
}
/*--------------------------------------------------------------*/
void far task2(

void * pTaskParam
)
{
int val = (int) pTaskParam;
int i;
int j;
tprintf("init val passed = %d\n", val);
OsSleep(2000L);
OsSemClear(&goSem);
for(i = 0; i < 3; i++)
{
OsSleepTicks(18L);
tputs("");
}
}
/*--------------------------------------------------------------*/
void far taskNull(
void * pTaskParam
)
{
while(killedTasks != numTasks - 1);
}
Good Luck!
Because of the success of Linux, many people are hanging out with the creation of OS.
Writing an efficient and neat OS is considered to be tough task because you may need to know more OS fundamentals and hardware details. If you could be able to come out with a new OS, the World would really appreciate you! Good Luck!

0 comments: