anim.cc
//***********************************************************************
// MODULE : ANIM - Animator Device Driver for the RCOS OS simulator *
// AUTHOR : Ron Chernich *
// PURPOSE: Module to perform all screen animation operations and re- *
// draw of switched displays. *
// HISTORY: *
// 14-MAY-93 First (MSC/C++ 7.00) version *
// 03-FEB-94 Pre-empt message added *
// 18-AUG-94 Disk Animation title messages added *
// 25-AUG-94 Animator given responsibility for all display classes *
// 16-MAR-94 Creation/deletion of TTY Radio Btns made dynamic *
// 22-MAR-95 (Happy Birthday, Ron!) No of terminals made dynamic *
//***********************************************************************
#include "anim.hh"
//////////////
// This define sets the minimum time between message changes in DOS
// clock ticks of 18.2 mS
//
#define MIN_DISY 42 // about three quarters of a second
////////////////
// Defined in the main module, this reference lets us display messages
// in the Supervisor Title Bar and create/destroy control buttons..
//
extern App Rcos;
extern char *pszInitl;
extern char *pszTitle;
/////////////
// These arrays set dithered backgrounds for the display window..
//
UINT16 nWinFgColor[4] = { _Green, _LightBlue, _Yellow, _LightRed };
UINT16 nWinBgColor[4] = { _DarkGrey, _White, _BrightWhite, _Yellow };
/////////////
// ..and here are the messages for display..
//
static char *pszInRdy =
{ "Low Level Scheduler moves Process P%d to the Ready Queue" };
static char *pszRdyRun =
{ "Process P%d context dispatched to the CPU" };
static char *pszRunBlk =
{ "Process P%d Blocks on an external event" };
static char *pszBlkRdy =
{ "Event Process P%d was Blocked on completes - Process now Ready." };
static char *pszRunRdy =
{ "Quantum for Process P%d expires - Process moved back to Ready." };
static char *pszForked =
{ "Process P0 Forks creating Process P%d." };
static char *pszRdySus =
{ "Ready Process P%d suspended by operator." };
static char *pszSusRdy =
{ "Suspended Ready Process P%d moves back to Ready." };
static char *pszBlkSus =
{ "Blocked Process P%d suspended by operator." };
static char *pszSusBlk =
{ "Suspended Blocked Process P%d moves back to Blocked." };
static char *pszSusBlkSus =
{ "Event Suspended Process P%d was blocked on completes." };
static char *pszHaltOk =
{ "Process P%d Terminated normally." };
static char *pszHaltBad =
{ "Process P%d Terminated on Illegal p-code." };
static char *pszPreEmpt =
{ "Running Process pre-empted by P%d (higher priority)." };
//
// These strings relate to the disk drive animation..
//
static char *pszEnQueue =
{ "A new Transfer Request arrives on the device queue.." };
static char *pszSeek =
{ "Seek %s to Track %d commences.." };
static char *pszOnTrack =
{ "On track - scanning for sector %02d.." };
static char *pszDeQueue =
{ "Data %s user buffer - Interrupt issued." };
static char *pszIdle =
{ "Disk Unit %c: now Idle." };
/////////////////////////////////////////////////////////////////////////
// Disk animators are not instantiated until actually needed..
//
Animate::Animate (UINT16 uId, UINT16 uCls, Knl *pK, UINT16 nTerm) :
port(uId, uCls, pK), nCurWin(NIL)
{
nTermRb = nTerm;
pDev = new TTyAnim(pK, nTerm);
for (int idx = 0; idx < MAX_DRIVES; idx++)
pDiskAnim[idx] = NULL;
}
///////////////
// Kill off any disk animators..
//
Animate::~Animate (void)
{
if (pDev)
delete pDev;
for (int idx = 0; idx < MAX_DRIVES; idx++)
if (pDiskAnim[idx])
delete pDiskAnim[idx];
}
///////////////////////
// As the user pressed buttons, different components of the OS come into
// "view" in the main animation window. This member registers the new
// change and performs any clean-up required by the old one. If changing
// from the Devices screen, we must destroy the Radio Buttons for the TTYs
// to inhibit redirection of keyboard.
//
void Animate::SetCurWin (INT16 nWin, BOOL bReTitle)
{
if ((CPU_DISP <= (AniWin)nWin) && (NIL > (AniWin)nWin)) {
Mickey.AutoPointer(WIN_X1, WIN_Y1, WIN_X2, WIN_Y2);
if (bReTitle)
Rcos.AppTitle(pszTitle);
if ((nCurWin == TTY_DISP) && ((AniWin)nWin != TTY_DISP)) {
pDev->DeActivate();
for (UINT16 i = 0; i < nTermRb; i++) {
rect r = pDev->GetTTyPos(i);
if (r.lr.x && r.lr.y)
Rcos.AppDestroy(RC_RadioBtn, RB_TTY_BASE + i);
}
}
GfxPattern(GFX_HalfTone);
GfxTextColorBg(nWinBgColor[nWin]);
GfxRect(WIN_X1, WIN_Y1+1, WIN_X2, WIN_Y2, GFX_Fill, nWinFgColor[nWin]);
GfxPattern(GFX_Solid);
switch ((AniWin)nWin) {
case CPU_DISP:
Cpu.Paint();
break;
case MEM_DISP:
break;
case DSK_DISP: {
for (int idx = 0; idx < MAX_DRIVES; idx++)
if (NULL != pDiskAnim[idx])
pDiskAnim[idx]->Paint();
}
break;
case TTY_DISP:
pDev->Paint();
if (nCurWin != TTY_DISP) {
pDev->Activate();
for (UINT16 i = 0; i < nTermRb; i++) {
rect r = pDev->GetTTyPos(i);
if (r.lr.x && r.lr.y) {
char szLab[8];
sprintf(szLab, "TTY &%d", i+1);
Rcos.AppCreate(RC_RadioBtn, RB_TTY_BASE + i, szLab,
r.ul.x + 8, r.ul.y + r.lr.y + 8, FALSE);
}
}
}
break;
}
Mickey.ShowPointer();
nCurWin = (AniWin)nWin;
}
}
//////////////////
// Provided the appropriate window is visible, perform graphix actions
// as dictated by the message to depict some internal change.
// We suspend timing while this is taking place so that it takes no
// time at all, at all..
//
void Animate::RxPort (PMSG pM)
{
if (0 == nCurWin) {
MSG msg;
MSG_ANIP pcby;
MSG_ANIQ aniq;
Clock.Stop();
switch (pM->wMsgType & ~MM_Sync) {
case ANI_REFRESH:
msg = message(ID_ANIM, ANI_GET_QUS, 0, &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.PaintAllQ(aniq);
break;
case ANI_UPDAT_PCB:
msg = message(ID_ANIM, ANI_GET_PCB, 0, &pcby);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.PaintBox(&pcby);
break;
case ANI_IN_RDY:
PutTitle(pszInRdy, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, READY_Q+(INPUT_Q<<8), &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.RefreshQ(INPUT_Q, &aniq.arr[0][0]);
Cpu.Anim(INPUT_Q, READY_Q);
Cpu.RefreshQ(READY_Q, &aniq.arr[1][0]);
break;
case ANI_RDY_RUN:
PutTitle(pszRdyRun, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, READY_Q, &aniq);
pTx->SendMsg(ID_Kernel, &msg);
msg = message(ID_ANIM, ANI_GET_PCB, 0, &pcby);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.RefreshQ(READY_Q, &aniq.arr[1][0]);
Cpu.Anim(READY_Q, CPROC_BOX);
Cpu.PaintBox(&pcby);
break;
case ANI_RUN_RDY:
PutTitle(pszRunRdy, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, READY_Q, &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.Anim(CPROC_BOX, READY_Q);
Cpu.RefreshQ(READY_Q, &aniq.arr[1][0]);
Cpu.PaintBox(NULL);
break;
case ANI_RUN_BLK:
PutTitle(pszRunBlk, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, BLOKED_Q, &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.Anim(CPROC_BOX, BLOKED_Q);
Cpu.RefreshQ(BLOKED_Q, &aniq.arr[1][0]);
Cpu.PaintBox(NULL);
break;
case ANI_BLK_RDY:
PutTitle(pszBlkRdy, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, BLOKED_Q+(READY_Q<<8), &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.RefreshQ(BLOKED_Q, &aniq.arr[1][0]);
Cpu.Anim(BLOKED_Q, READY_Q);
Cpu.RefreshQ(READY_Q, &aniq.arr[0][0]);
break;
case ANI_BLK_SBLK:
PutTitle(pszSusBlk, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, BLOKED_Q+(SUSBLK_Q<<8), &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.RefreshQ(BLOKED_Q, &aniq.arr[1][0]);
Cpu.Anim(BLOKED_Q, SUSBLK_Q);
Cpu.RefreshQ(SUSBLK_Q, &aniq.arr[0][0]);
break;
case ANI_SBLK_BLK:
PutTitle(pszSusBlk, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, SUSBLK_Q+(BLOKED_Q<<8), &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.RefreshQ(SUSBLK_Q, &aniq.arr[1][0]);
Cpu.Anim(SUSBLK_Q, BLOKED_Q);
Cpu.RefreshQ(BLOKED_Q, &aniq.arr[0][0]);
break;
case ANI_RDY_SRDY:
PutTitle(pszRdySus, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, READY_Q+(SUSRDY_Q<<8), &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.RefreshQ(READY_Q, &aniq.arr[1][0]);
Cpu.Anim(READY_Q, SUSRDY_Q);
Cpu.RefreshQ(SUSRDY_Q, &aniq.arr[0][0]);
break;
case ANI_SBLK_SRDY:
PutTitle(pszSusBlkSus, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, SUSBLK_Q+(SUSRDY_Q<<8), &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.RefreshQ(SUSBLK_Q, &aniq.arr[1][0]);
Cpu.Anim(SUSBLK_Q, SUSRDY_Q);
Cpu.RefreshQ(SUSRDY_Q, &aniq.arr[0][0]);
break;
case ANI_SRDY_RDY:
PutTitle(pszSusRdy, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, SUSRDY_Q+(READY_Q<<8), &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.RefreshQ(SUSRDY_Q, &aniq.arr[1][0]);
Cpu.Anim(SUSRDY_Q, READY_Q);
Cpu.RefreshQ(READY_Q, &aniq.arr[0][0]);
break;
case ANI_FORKS:
PutTitle(pszForked, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, INPUT_Q, &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.RefreshQ(INPUT_Q, &aniq.arr[1][0]);
break;
case ANI_HALT_OK:
PutTitle(pszHaltOk, pM->wParam);
break;
case ANI_HALT_BAD:
PutTitle(pszHaltBad, pM->wParam);
break;
case ANI_PREEMPT:
PutTitle(pszPreEmpt, pM->wParam);
msg = message(ID_ANIM, ANI_GET_QUS, READY_Q, &aniq);
pTx->SendMsg(ID_Kernel, &msg);
Cpu.Anim(CPROC_BOX, READY_Q);
Cpu.RefreshQ(READY_Q, &aniq.arr[1][0]);
Cpu.PaintBox(NULL);
break;
}
Clock.Start();
}
else if (2 == nCurWin) {
Clock.Stop();
switch (pM->wMsgType & ~MM_Sync) {
case ANI_ONTRACK:
PutTitle(pszOnTrack, pM->wParam);
break;
case ANI_CHAN_ENQ:
if (pDiskAnim[pM->wParam])
pDiskAnim[pM->wParam]->RefreshTQ((TransQ*)pM->pBody, 0);
PutTitle(pszEnQueue);
break;
case ANI_SPIN:
if (pDiskAnim[pM->wParam])
pDiskAnim[pM->wParam]->DiskSpin(*(UINT16*)pM->pBody);
break;
case ANI_BEGIN_SEEK: {
char str[8];
UINT16 nVal = *(UINT16*)pM->pBody;
strcpy(str, (nVal & 0x8000) ? "out" : "in");
PutTitle(pszSeek, nVal & 0x7fff, str);
}
break;
case ANI_SEEK:
if (pDiskAnim[pM->wParam])
pDiskAnim[pM->wParam]->DiskSeek(*(UINT16*)pM->pBody);
break;
case ANI_RWOP: {
char str[16];
strcpy(str, (pM->wParam) ? "written from" : "read into");
PutTitle(pszDeQueue, 0, str);
if (pDiskAnim[pM->wParam])
pDiskAnim[pM->wParam]->RefreshTQ((TransQ*)pM->pBody, 1);
TransQ *pTq = (TransQ*)pM->pBody;
if (1 == pTq->GetLen())
PutTitle(pszIdle, pM->wParam + 'A');
}
break;
}
Clock.Start();
}
else if ((pM->wMsgType & ~MM_Sync) == ANI_DCREAT) {
if (NULL == pDiskAnim[pM->wParam])
pDiskAnim[pM->wParam] = new DiskAnim(pM->wParam, *(UINT16*)pM->pBody);
}
}
///////////////
// Prepare a title message from the passed template and parameter(s)
// and display it on the Application Title Bar. Make sure at least
// MIN_DISY 18mS ticks have elapsed since the last message (gives
// adequate time to read the damn things..)
//
void Animate::PutTitle (char *psz1, UINT16 uNr, char *psz2)
{
char sz[80];
static UINT32 uPrevTock = 0;
if (psz2)
sprintf(sz, psz1, psz2, uNr);
else
sprintf(sz, psz1, uNr);
while (Clock.GetTocks() - uPrevTock < MIN_DISY)
NULL;
Rcos.AppTitle(sz);
uPrevTock = Clock.GetTocks();
}
/********************************** eof **********************************/