问题
Shell 任务如何实现?系统调用 uint ReadKey() 如何实现?
实现流程
关键问题
当 Shell 请求用户输入时,用户是否正在输入字符?
当多个任务请求用户输入时,哪个任务应该获得输入的字符?
解决方案
当一个任务调用 ReadKey 函数,却没有得到用户输入的字符时,操作系统会把当前任务放入 gKeyWait 队列中;等用户输入字符时,就把 gKeyWait 队列中所有的任务调度回就绪队列,并且这些任务都可以得到用户输入的字符。
解决方案实现
Shell 任务的实现
syscall.c
uint ReadKey()
{
uint ret = 0;
SysCall(2, 0, &ret, 0);
return ret;
}
ihandler.c
void SysCallHandler(uint type, uint cmd, uint param1, uint param2) // __cdcel__
{
switch(type)
{
case 0:
TaskCallHandler(cmd, param1, param2);
break;
case 1:
MutexCallHandler(cmd, param1, param2);
break;
case 2:
KeyCallHandler(cmd, param1, param2);
default:
break;
}
}
void KeyboardHandler()
{
byte sc = ReadPort(0x60);
PutScanCode(sc);
NotifyKeyCode();
SendEOI(MASTER_EOI_PORT);
}
keyboard.c
static Queue gKeyWait = {0};
void KeyCallHandler(uint cmd, uint param1, uint param2)
{
if(param1)
{
uint kc = FetchKeyCode();
if(kc)
{
uint* p = (uint*)param1;
*p = kc;
NotifyAll(kc);
}
else
{
Event* evt = CreateEvent(KeyEvent, (uint)&gKeyWait, param1, 0);
EventSchedule(WAIT, evt);
}
}
}
static void NotifyAll(uint kc)
{
Event* evt = CreateEvent(KeyEvent, (uint)&gKeyWait, kc, 0);
EventSchedule(NOTIFY, evt);
}
void NotifyKeyCode()
{
uint kc = FetchKeyCode();
if(kc)
{
NotifyAll(kc);
}
}
void KeyBoardModInit()
{
Queue_Init(&gKeyWait);
gKCBuff.max = 2;
}
task.c
static void KeyEventSchedule(uint action, Event* event)
{
Queue* wait = (Queue*)event->id;
if(action == NOTIFY)
{
ListNode* pos = NULL;
uint kc = event->param1;
List_ForEach((List*)wait, pos)
{
TaskNode* pt = (TaskNode*)pos;
if(pt)
{
uint* p = (uint*)pt->task.event->param1;
*p = kc;
}
}
WaitingToReady(wait);
}
else if(action == WAIT)
{
WaitEvent(wait, event);
}
}
shell.c
static uint IsKeyDown(uint kc)
{
return !!(kc & 0xFF000000);
}
static char GetChar(uint kc)
{
return (char)kc;
}
static byte GetKeyCode(uint kc)
{
return (byte)(kc >> 8);
}
void Shell()
{
SetPrintPos(0, 9);
PrintString("L.J.OS >> ");
while(1)
{
uint key = ReadKey();
if(IsKeyDown(key))
{
char ch = GetChar(key);
byte vc = GetKeyCode(key);
if(ch)
{
PrintChar(ch);
}
}
}
}
app.c
void AppMain()
{
RegApp("Shell", Shell, 255);
}
运行结果如下如所示
Shell 任务可以正确的获得用户键入的字符,并显示在屏幕上。
思考
如何实现 Shell 任务的用户接口功能?