// Mini OS, Semaphores Demo and MMU Test added -- 2004 March
24
// Minimal March 25 Homework implementation
// Additional MMU comments added
in LoadProgram
import Files;
import Semaphores;
import Hardware;
import Dangerous;
// ==============================================================
// Global data, used as indicated..
// Semaphore Demo data..
final int Qsize = 4;
int[32] DemoFrame; // stack frame for second process
char[Qsize] queue; // the buffer
int fx=0, tx=0; // fill and take buffer indexes
Semaphore fill, take; // their semaphores
// MMU Test data..
int[32] PageTable;
int[40] AppPIB; // used for PIB + MMU
// Interrupt handler process stack frames..
int[16] MMUFrame; // stack frame for MMU interrupt
int[16] ClockFrame; // stack frame for Clock interrupt
int[16] DiskFrame; // stack frame for Disk interrupt
int[16] KeyIntFrame; // stack frame for KeyIn interrupt
int[16] PrintFrame; // stack frame for Print interrupt
int[16] Sema4Frame; // stack frame for Signal/Wait interrupts
int[80] SysCallFrame; // stack frame for SysCall interrupt;
// it's extra big because the SysCall handler calls lots
of
// nested subroutines, which might collide with
the next frame
// Data used by the file system..
int[16] ActiveFiles; // the 4 files you can have open, 4 ints each
final int Directory = 1536; // start of disk dir
// watch for possible collision between this and main()
stack
final int BuffersAt = Directory+256; // 256-byte buffers for files
1-4
// Memory allocator data..
int HeapTop = 16382; // allocates down from here..
final int HeapBottom = BuffersAt+1024; // .. to here
// Keyboard and Printer data..
int KeyFillPtr = BuffersAt, KeyTakePtr = BuffersAt,
KeyShowPtr = BuffersAt; // initialize them to empty buffer
int GotRetns = 0; // the number of CRs in KeyIn buffer
boolean PrintReady = true; // =false between '\r' and interrupt
// Timer data..
int theTicks; // used by clock interrupt for Delay
// Process manager data..
int ReadyList = 0; // list of waiting processes
int CurProcess = 0; // whatever is currently running
int[8] MainPIB; // to make main() look like another process
// User-level file data..
FileId Kin, Cout; // KeyIn and ScreenOut, always open
String KeyString; // used by ReadStr() to buffer a line
final int[] MyName = { ORD('M')*256+ORD('i'), ORD('n')*256+ORD('i'),
ORD('O')*256+ORD('S') }; // "MiniOS", the name of
this file
// Video data, used within the system code..
int Vrow = 1, Vcol = 2; // current display V and H
final int TextColor = DoText+white;
final int TextBgnd = FillRect+minRed+minGreen+minBlue;
// dark gray fill
final int[] FontBits = { 0, 0, 0, 0, 0, 0, 0, 190, 0,
0, // sp
0, 14, 0, 14, 0,
40, 124, 40, 124, 40,
76, 146, 511, 146, 100, 66, 37, 146, 328,
132,
96, 150, 137, 118, 208, 0, 0, 14, 0, 0,
0, 124, 130, 257, 0,
0, 257, 130, 124, 0,
8, 42, 28, 42, 8,
16, 16, 124, 16, 16,
0, 512, 448, 192, 0,
16, 16, 16, 16, 16,
0, 192, 192, 0, 0,
0, 384, 96, 24, 6,
56, 68, 130, 68, 56,
0, 4, 254, 0, 0,
// 0
132, 194, 162, 146, 140, 68, 130, 146, 146,
108,
48, 40, 36, 254, 32,
78, 138, 138, 138, 114,
120, 148, 146, 146, 96, 2, 2, 226, 26,
6,
108, 146, 146, 146, 108, 12, 146, 146, 82, 60,
0, 204, 204, 0, 0,
0, 716, 460, 0, 0,
0, 16, 40, 68, 130,
40, 40, 40, 40, 40,
0, 130, 68, 40, 16,
4, 2, 162, 18, 12,
120, 132, 306, 330, 60, 224, 56, 38, 56,
224, // @A
130, 254, 146, 146, 108, 124, 130, 130, 130,
68,
130, 254, 130, 68, 56, 254, 146,
146, 130, 130,
254, 18, 18, 18, 2,
124, 130, 130, 146, 244,
254, 16, 16, 16, 254, 0, 130,
254, 130, 0,
96, 128, 128, 128, 126, 254, 16, 40, 68,
130,
254, 128, 128, 128, 128, 254, 4, 24, 4, 254,
254, 4, 24, 32, 254,
124, 130, 130, 130, 124,
254, 18, 18, 18, 12,
124, 130, 130, 386, 636, // P
254, 18, 50, 82, 140, 76,
146, 146, 146, 100,
2, 2, 254, 2, 2,
126, 128, 128, 128, 126,
14, 48, 192, 48, 14,
30, 224, 24, 224, 30,
198, 40, 16, 40, 198, 6, 8,
240, 8, 6,
194, 162, 146, 138, 134, 0, 0, 511, 257, 0,
0, 6, 24, 96, 384,
0, 257, 511, 0, 0,
4, 2, 1, 2, 4,
128, 128, 128, 128, 128,
0, 2, 4, 0, 0,
64, 168, 168, 168, 240, // `a
254, 136, 136, 136, 112, 112, 136, 136, 136,
136,
112, 136, 136, 136, 254, 112, 168, 168, 168,
48,
16, 252, 18, 2, 4,
112, 648, 648, 648, 504,
254, 8, 8, 8, 240,
0, 136, 250, 128, 0,
512, 520, 506, 0, 0,
254, 16, 48, 72, 128,
0, 2, 254, 0, 0,
248, 8, 240, 8, 240,
248, 16, 8, 8, 240,
112, 136, 136, 136, 112,
1016, 136, 136, 136, 112, 112, 136, 136, 136, 1016,
// p
248, 16, 8, 8, 16,
144, 168, 168, 168, 72,
0, 8, 126, 136, 128,
120, 128, 128, 128, 248,
8, 48, 192, 48, 8,
56, 192, 48, 192, 56,
136, 80, 32, 80, 136, 536,
608, 384, 96, 24,
136, 200, 168, 152, 136, 0, 16, 238, 257, 0,
0, 0, 511, 0, 0,
0, 257, 238, 16, 0,
4, 2, 2, 4, 2 };
// ==============================================================
// Video driver subroutines, for use by the system only..
// This sets a simple rectangular clipping region
// for concurrent programs sharing the screen by tiling..
void Clipper(int T, int L, int B, int R) {
Video.top = T; // set clip region for this process
Video.left = L;
Video.bottom = B;
Video.right = R;
Video.datum = ClipRect;} // ~Clipper
// Erase next line, used when an output string has \r in it..
void ScreenLn() { // start new line, and clear it..
Vcol = 2;
Vrow = Vrow+12;
if (Vrow>469) Vrow = 1;
Video.left = 0;
Video.right = 640;
Video.top = Vrow-1;
Video.bottom = Vrow+12;
Video.datum = TextBgnd;} // ~ScreenLn
// Erase previous char, used when an output string has \b in it..
void ScreenBS() {
if (Vcol<8) return; // nothing to back up over
Video.right = Vcol;
Vcol = Vcol-6;
Video.left = Vcol;
Video.top = Vrow;
Video.bottom = Vrow+12;
Video.datum = TextBgnd;} // ~ScreenBS
// Display one character..
void ScreenChar(char ch) { // display one character on current
line
if (ORD(ch)==13) ScreenLn();
if (ORD(ch)==8) ScreenBS();
if (ch<' ' || ch>'~' || Vcol>633) return; // not printable
Video.address = ORD(ch)*10-320+&FontBits; // 5 ints
x 2 bytes =10
Video.left = Vcol;
Video.right = Vcol+5;
Vcol = Vcol+6;
Video.top = Vrow;
Video.bottom = Vrow+12;
Video.datum = TextColor;} // ~ScreenChar
// File-oriented write to screen, called by SysCall(7)
// when the file type is -1 (Screen/KeyIn); it assumes the
data
// is like String arrays, 1 char per 2-byte array element..
void WriteScr(int nby, int DataPtr) {
while (nby>0) {
ScreenChar(CHR(mmPEEK(DataPtr)));
DataPtr = DataPtr+2;
nby = nby-2;}} // ~WriteScr
// File-oriented read from keyin, called by SysCall(6)
// when the file type is -1 (Screen/KeyIn); it packs data
in
// 1 char per 2-bytes, like String arrays, omitting final
return.
// Special-case: if buffer has only one element (2 bytes),
// then a single return character is returned in it.
int ReadLine(int nby, int DataPtr) {
// we want nby bytes (but not more than 254) in array DataPtr
int got = 0; // the for-loop fill index
int ch; // temp to hold current char
boolean sho; // =true if need to display this char
if (nby>254) nby = 254; // request is too big; trim it
if (((KeyFillPtr-KeyTakePtr)&256)+KeyFillPtr-KeyTakePtr<nby
&& GotRetns==0) { // not
enough chars to satisfy..
while (KeyShowPtr != KeyFillPtr) { // echo what
we have so far..
ch = mmPEEK(KeyShowPtr);
KeyShowPtr = ((KeyShowPtr+2)&254)+BuffersAt;
ScreenChar(CHR(ch));}
return -1;}
while (got<nby) { // OK, we can do it this time..
ch = mmPEEK(KeyTakePtr); // get a character
from buffer
sho = KeyShowPtr == KeyTakePtr; // false while
working on the
// keystrokes that were already
echoed, then true after that.
KeyTakePtr = ((KeyTakePtr+2)&254)+BuffersAt;
// advance to next
if (sho) { // once it's caught up, keep it up..
ScreenChar(CHR(ch));
KeyShowPtr = KeyTakePtr;}
if (ch==13) { // keep returns count current..
GotRetns = GotRetns-1;
if (nby>2) break;} // exit if got
end-of-line return
else if (ch==8) if (nby>2) if (got>0) { // backspace..
DataPtr = DataPtr-2; // back up
over the previous character
got = got-2;
continue;} // don't insert it into
buffer
mmPOKE(DataPtr,ch); // '\r' isn't included unless
asking for 1 byte
DataPtr = DataPtr+2;
got = got+2;} // '\r' will be last char in 1-byte
request
return got;} // ~ReadLine
// ==============================================================
// Disk file I/O, for use by the system only..
// Read or write one sector: flop =true if floppy; wrt =true to
write;
// get/put nby bytes at sector number sec, memory buffer
at dPtr;
// return the actual number of bytes transferred.
// Not interrupt-driven, it just busy-waits until completion.
// Called from various places inside the disk driver..
int RWsector(boolean flop, boolean wrt, int sec, int nby, int dPtr)
{
int timo = 65533; // hopefully long enough to wait
if (flop && Enables.datum & FlopInBit ==0) return
0; // floppy not in
if (flop) sec = ~sec; // hardware wants these negatives..
if (wrt) nby = -nby;
Disk.sector = sec;
Disk.address = dPtr;
Disk.count = nby;
while (Enables.datum&DiskPendBit == 0 && timo
!=0) // wait until done..
timo = timo - timo/timo; // a little slower
than just timo-1
Enables.datum = IntOnBit; // turn DiskPendBit back off
nby = nby-Disk.count;
if (wrt) nby = -nby;
return nby;} // ~RWsector
// Write nby bytes at address DataPtr to file index filix;
// called by WriteFile from SysCall(7) for disk files.
// Does not properly map data through MMU!
int WrtDskFile(boolean flop, int filix, int nby, int DataPtr) {
int sect = ActiveFiles[filix+1]; // get current file position
int offs = ActiveFiles[filix+2];
int BufPtr = filix*64+BuffersAt;
int eofAt = ActiveFiles[filix+3]; // how many bytes left
in this file
int got = 0; // the # of bytes transferred
if (nby>eofAt) if (eofAt>=0) nby = eofAt; // can't write
past eof
if (ActiveFiles[filix]+4==0) { // newly opened flop, or
was reading..
ActiveFiles[filix] = -5; // note this is 'write'
if (offs>0) sect = sect-1;} // gotta rewrite
this sector
if (offs>0) { // partial buffer to fill..
while (offs<256 && nby>0) { // transfer
2 bytes at a time
POKE(BufPtr+offs, PEEK(DataPtr));
offs = offs+2;
DataPtr = DataPtr+2;
nby = nby-2;
got = got+2;
eofAt = eofAt-2;}
offs = offs & 255; // =0 when buffer is
full
if (offs==0) { // full: write sector out
offs = RWsector(flop, true, sect,
256, BufPtr);
sect = sect+1;
offs = 0;}}
// ASSERT: offs==0 || nby==0
if (nby>255) { // write some whole sectors, directly from
user buffer..
offs = RWsector(flop, true, sect, nby&(-256),
DataPtr);
nby = nby-offs;
got = got+offs;
DataPtr = DataPtr+offs;
eofAt = eofAt-offs;
sect = offs/256+sect;
offs = 0;}
if (nby>0) { // buffer the rest (less than one sector)..
offs = RWsector(flop, false, sect, 256, BufPtr);
if (offs<256) nby = 0; // oops, something
went wrong, kill it
offs = 0;
while (nby>0) {
POKE(BufPtr+offs, PEEK(DataPtr));
offs = offs+2;
DataPtr = DataPtr+2;
nby = nby-2;
got = got+2;
eofAt = eofAt-2;}
sect = sect+1;}
ActiveFiles[filix+1] = sect; // update file position info
ActiveFiles[filix+2] = offs;
ActiveFiles[filix+3] = eofAt;
// ScreenChar('`');
return got;} // ~WrtDskFile
// Read nby bytes to address DataPtr from file index filix;
// called by ReadFile from SysCall(6)..
// Now properly maps addresses through MMU
-- MAYBE
int RdDiskFile(boolean flop, int filix, int nby, int DataPtr) {
int sect = ActiveFiles[filix+1]; // get current file position
int offs = ActiveFiles[filix+2];
int BufPtr = filix*64+BuffersAt;
int eofAt = ActiveFiles[filix+3]; // how many bytes left
in this file
int got = 0; // the # of bytes transferred
if (nby>eofAt) if (eofAt>=0) nby = eofAt; // can't read
past eof
if (offs>0) { // partial sector in buffer..
while (offs<256 && nby>0) { // transfer
2 bytes at a time
mmPOKE(DataPtr,
PEEK(BufPtr+offs));
offs = offs+2;
DataPtr = DataPtr+2;
nby = nby-2;
got = got+2;
eofAt = eofAt-2;}
offs = offs & 255; // =0 when buffer is
empty
ActiveFiles[filix+2] = offs; // update file
position info
ActiveFiles[filix+3] = eofAt;
if (offs==0) { // empty, advance sector pointer
too
sect = sect+1;
ActiveFiles[filix+1] = sect;}}
if (false) { // get some whole
sectors, directly to user buffer..
offs = RWsector(flop, false, sect, nby&(-256),
DataPtr);
nby = nby-offs;
got = got+offs;
DataPtr = DataPtr+offs;
eofAt = eofAt-offs;
sect = offs/256+sect;
ActiveFiles[filix+1] = sect;}
while (nby>0) { // buffer the
rest (less than one sector)..
offs = RWsector(flop, false, sect, 256, BufPtr);
if (offs<256) if
(offs<nby) nby = offs; // maybe early eof?
offs = 0;
while (nby>0 && offs<256) {
mmPOKE(DataPtr,
PEEK(BufPtr+offs));
offs = offs+2;
DataPtr = DataPtr+2;
eofAt = eofAt-2;
if (eofAt+1==0) eofAt = 0; // don't
cross 0
nby = nby-2;
got = got+2;}
offs = offs & 255;
// =0 when buffer is empty
if (offs==0) sect = sect+1;
ActiveFiles[filix+2] = offs; // update file
position info
ActiveFiles[filix+3] = eofAt;
ActiveFiles[filix+1] = sect;}
return got;} // ~RdDiskFile
// Look for an unused file index, called by Open only..
int FindAvailFile() {
if (ActiveFiles[4]==0) return 4;
if (ActiveFiles[8]==0) return 8;
if (ActiveFiles[12]==0) return 12;
return 0;} // ~FindAvailFile
// 2 functions to pack and unpack bytes,
// between char[] arrays (2 bytes for each character)
// and disk file format (byte for byte)
//
// These are called by the file manager to convert between String
// file names and the packed form of the directory.
// You could use them elsewhere, but they take integer pointers,
// which requires using the (unsafe) unary-& operator.
// Pack nby bytes from char[] array frm to int[] array too
// bytes are 1/word in char[], 2/word in int[]
void PackBytes(int nby, int frm, int too) {
int temp;
while (nby>0) {
temp = mmPEEK(frm)*256;
nby = nby-2;
frm = frm+2;
if (nby>=0) temp = temp + (mmPEEK(frm)&255);
mmPOKE(too,temp);
too = too+2;
frm = frm+2;}} // ~PackBytes
// Unpack nby bytes from int[] array frm to char[] array too
// bytes are 1/word in char[], 2/word in int[]
void unPackBy(int nby, int frm, int too) {
int temp;
while (nby>0) {
mmPOKE(too,mmPEEK(frm-1)&255);
mmPOKE(too+2,mmPEEK(frm)&255);
nby = nby-2;
frm = frm+2;
too = too+4;}} // ~unPackBy
// Compare the String name at NamePtr to dirName (in the directory),
// return true if they are equal (not case-sensitive);
// NamePtr points to 1st byte of user String,
// dirName points to a name in directory, max 27 chars;
// Called by OpenFile to find an existing file with this name..
boolean EqualName(int dirName, int NamePtr) {
int ix = mmPEEK(NamePtr-2)/2; // get String length
int chd, chn;
if (ix>27) return false; // NamePtr is too long, cannot
be equal
if ((mmPEEK(dirName+3+ix)&255)!=0) return false; //
too short
while (ix>0) {
ix = ix-1;
chd = mmPEEK(dirName+3+ix)&255;
if (chd<91) if (chd>64) chd = chd+32;
chn = mmPEEK(NamePtr+ix+ix);
if (chn<91) if (chn>64) chn = chn+32;
if (chd != chn) return false;} // unequal
return true;} // ~EqualName
// Store file type fity into file index fid,
// then set start sector and eof from directory entry # fino;
// return a FileID value.
// Called by OpenFile() on its way back to SysCall(4)
int SetActiveFile(int fid, int fino, int fity) {
if (fity>0) ActiveFiles[fid] = fity;
ActiveFiles[fid+1] = mmPEEK(Directory+fino*32);
ActiveFiles[fid+2] = 0;
ActiveFiles[fid+3] = mmPEEK(Directory+fino*32+2);
return fid/4+1;} // ~SetActiveFile
// Open any known named file or file type
int OpenFile(int NamePtr) { // called by SysCall(4)
char two = CHR(mmPEEK(NamePtr+2)&223); // 2nd char of
name, if needed
int fi = FindAvailFile(); // might not be needed, but cheap
call
int ix = -1; // = default device name, KeyIn/ScreenOut
int nx;
if (mmPEEK(NamePtr)==ORD('.')) { // device name..
if (two == 'K') fi = 0; // KeyIn/ScreenOut are
always FileId =1
else if (two == 'S') fi = 0;
else if (fi==0) return 0; // FindAvailFile()
failed, can't open
else if (two == 'P') ix = -2; // printer
else if (two == 'F') ix = -4; // floppy
else if (two == 'H') ix = -3; // hard-sectored
hard disk
else return 0; // dunno; can't open
it
ActiveFiles[fi] = ix; // set the file type
if (ix+4 == 0) { // floppy, read the directory..
ix = RWsector(true, false, 0, 256,
Directory);
// you should verify file name,
etc...
return SetActiveFile(fi,1,0);} //
we just open file # 1
else if (ix+3 == 0) { // hard disk, absolute
sector..
ActiveFiles[fi+1] = 0; // sector
0
ActiveFiles[fi+2] = 0;
ActiveFiles[fi+3] = -1;} // no size
limit
return fi/4+1;}
else { // named HD file..
if (mmPEEK(NamePtr-2)>54) return 0; // name
too long
if (RWsector(false, false, 0, 256, Directory)<256)
return 0; // read failed
ix = mmPEEK(Directory+12); // # files on this
disk
while (ix>0) // try each file..
if (EqualName(Directory+ix*32,NamePtr))
return SetActiveFile(fi,ix,ix);
// found it
else ix = ix-1;
ix = mmPEEK(Directory+12)+1; // not there, add
another file
if (ix>mmPEEK(Directory+14)) return 0; // oops,
too many
nx = mmPEEK(NamePtr-2)/2; // length of file
name, in chars
PackBytes(nx, NamePtr, Directory+ix*32+4); //
insert name
mmPOKE(Directory+ix*32+4+nx,0); // null terminated
mmPOKE(Directory+ix*32,mmPEEK(Directory+10));
// set start to next sec
mmPOKE(Directory+ix*32+2,-1); // new file, assume
65K
if (RWsector(false, true, 0, 256, Directory)==256)
// rewrite dir
return SetActiveFile(fi,ix,ix);}
// fail if dir rewrite fails
return 0;} // ~OpenFile
// Close the file indexed by FileId theFile..
void CloseFile(int theFile) { // called by SysCall(5)
int fi = theFile*4-4; // point to the indexed ActiveFiles
int BufPtr = fi*64+BuffersAt; // point to its buffer, in
case needed
int ix;
if (fi<0) return; // not an open file (null FileId)
if (theFile>4) return; // not an open file (bogus FileId)
ix = ActiveFiles[fi]; // the file # on disk, or device #
if (ix>0) { // close hard disk file..
ActiveFiles[fi] = 0;
if (RWsector(false, false, 0, 256, Directory)<256)
return;
if (mmPEEK(Directory+12)>=ix) return; // not
closing new file
if (ActiveFiles[fi+3]+1==0) return; // didn't
write anything
mmPOKE(Directory+12,ix); // update # files
mmPOKE(Directory+ix*32+2,~ActiveFiles[fi+3]);
// actual file size
ix = ActiveFiles[fi+1]; // this is next sector
to write
if (ActiveFiles[fi+2]>0) // rewrite partial
buffer
if (RWsector(false, true, ix, 256,
BufPtr)==256)
ix = ix+1; // successful
write, update sector #
mmPOKE(Directory+10,ix); // that is sector for
next file to open
ix = RWsector(false, true, 0, 256, Directory);
// rewrite dir
return;}
else if (ix+5==0) if (ActiveFiles[fi+2]>0) // writing to
floppy..
ix = RWsector(true, true, ActiveFiles[fi+1],
256, BufPtr);
if (fi>0) ActiveFiles[fi] = 0; // leave K/S open
} // ~CloseFile
// Read a block into array DataPtr from open FileId theFile..
int ReadFile(int theFile, int DataPtr) { // called by SysCall(6)
int fi = theFile*4-4; // point to the indexed ActiveFiles
int who, len;
if (fi<0) return 0; // not an open file (null FileId)
if (theFile>4) return 0; // not an open file (bogus FileId)
who = ActiveFiles[fi];
if (who==0) return 0; // not an open file (stale fileId)
if (DataPtr==0) return 0; // no array to read into
len = mmPEEK(DataPtr-2); // the size of the array
if (who+1==0) return ReadLine(len, DataPtr); // Keyboard
input
if (who+4==0) return RdDiskFile(true, fi, len, DataPtr);
// Floppy in
if (who>0) { // named HD file
if (ActiveFiles[fi+3]+1==0) return 0; // new
file, nothing to read
who = -3;}
if (who+3==0 || who>0) // Hard disk sector or file input..
return RdDiskFile(false, fi, len, DataPtr);
return 0; // ignore the rest
} // ~ReadFile
// Write a block from array DataPtr to open FileId theFile..
int WriteFile(int theFile, int DataPtr) { // called by SysCall(7)
int fi = theFile*4-4; // point to the indexed ActiveFiles
int who, len;
if (fi<0) return 0; // not an open file (null FileId)
if (theFile>4) return 0; // not an open file (bogus FileId)
who = ActiveFiles[fi];
if (who==0) return 0; // not an open file (stale fileId)
if (DataPtr==0) return 0; // no array to write from
len = mmPEEK(DataPtr-2); // the size of the array
if (who+1==0) { // Screen output..
WriteScr(len, DataPtr);
return len;}
if (who != -2) // Floppy/hard disk output..
return WrtDskFile(who<-3, fi, len, DataPtr);
return 0; // ignore the rest
} // ~WriteFile
// ==============================================================
// Process management subroutine,
// moves CurProcess to Blok2, and ReadyList to CurProcess
int SwapOut(int SemSP, int Blok2) {
int going = CurProcess;
int comin = ReadyList;
if (Blok2==0) Blok2 = &ReadyList; // Blok2=0 just swaps
if (comin !=0) if (going !=0) { // can't do it if nothing
there
ReadyList = PEEK(comin); // unlinked top of
ReadyList
CurProcess = comin; // and make it current
POKE(going+6,SemSP); // save current process's
SP in its PIB
POKE(going, PEEK(Blok2)); // link it to rest
of its list
POKE(Blok2,going); // move (formerly) CurProcess
to its list
MMU.address = PEEK(comin+4);
// set MMU to current
return PEEK(comin+6);} // exit to it on caller's
CoCall
return SemSP;} // ~SwapOut
// ==============================================================
// Interrupt drivers..
// These are called ONCE to start up their respective processes.
// After that, they do all their work when interrupts hit..
void DoSema4() {
// These 2 interrupts happen for Signal and Wait respectively
int SemSP = 0; // maps to Sema4Frame[3]
int aPtr, temp;
Sema4Frame[6] = &Sema4Frame[9]; // initial
SP
Sema4Frame[8] = &Sema4Frame[0]; // initial
FP
Sema4Frame[0] = PEEK((&SemSP)-6); // copy my globals
ptr
Sema4Frame[1] = 0;
// halt if it returns (it shouldn't)
SemSP = FORK(&Sema4Frame[6]); // returns twice:
if (SemSP==0) { // returned in caller's process..
aPtr = &Sema4Frame[3]; // set 2 interrupt
vector items..
iVector[6] = aPtr;
iVector[7] = aPtr;
return;}
// otherwise returned in new process (first)..
POKE(SemSP-6,0); // clear caller's return value
while (true) { // this is the interrupt handler loop..
COCALL(&SemSP); // wait for interrupt
aPtr = SignalWait.address; // the semaphore
(not on stack)
if (mmPEEK(mmPEEK(SemSP)-2)&1 ==0) { //
caller's opcode, =0 if Signal
temp = ReadyList;
// get previous ready list,
ReadyList = mmPEEK(aPtr); // replace
it with process from sem,
mmPOKE(aPtr,mmPEEK(ReadyList));
// (delete top of sem queue)
mmPOKE(ReadyList, temp);} // ..link
ready list to previous
else SemSP = SwapOut(SemSP,aPtr);} // otherwise
it's a Wait
} // ~DoSema4
void DoDisk() {
// This interrupt is used for switching context at the end
of
// disk I/O, if you switched out to make use
of the wait time.
// Right now it's set up but unused.
int SemSP = 0; // maps to DiskFrame[3]
int unused;
int temp;
DiskFrame[6] = &DiskFrame[9]; // initial SP
DiskFrame[8] = &DiskFrame[0]; // initial FP
DiskFrame[0] = PEEK((&SemSP)-6); // copy my globals
ptr
DiskFrame[1] = 0;
// halt if it returns (it shouldn't)
SemSP = FORK(&DiskFrame[6]); // returns twice:
if (SemSP==0) { // returned in caller's process..
iVector[2] = &DiskFrame[3]; // set interrupt
vector
return;}
// otherwise returned in new process (first)..
POKE(SemSP-6,0); // clear caller's return value
while (true) { // this is the interrupt handler loop..
COCALL(&SemSP); // wait for interrupt
// YOUR CODE GOES HERE
} // do nothing
} // ~DoDisk
void DoKeyInts() {
// This interrupt grabs a keystroke when it is available,
// and saves it in a buffer for when the program
needs it.
int SemSP = 0; // maps to KeyIntFrame[3]
int theKey;
int temp;
KeyIntFrame[6] = &KeyIntFrame[9]; // initial SP
KeyIntFrame[8] = &KeyIntFrame[0]; // initial FP
KeyIntFrame[0] = PEEK((&SemSP)-6); // copy my
globals ptr
KeyIntFrame[1] = 0;
// halt if it returns
SemSP = FORK(&KeyIntFrame[6]); // create a new process
(2 returns)
if (SemSP==0) { // final return in initial process
iVector[3] = &KeyIntFrame[3]; // set interrupt
vector
return;}
// otherwise returned in new process (first)..
POKE(SemSP-6,0); // clear caller's return value
while (true) { // this is the interrupt handler loop..
COCALL(&SemSP); // wait for interrupt
theKey = KeyIn.datum & 127; // got a keystroke,
save it..
mmPOKE(KeyFillPtr, theKey); // ..in the keyboard
buffer
if (theKey==13) GotRetns = GotRetns+1; // count
returns (=lines)
KeyFillPtr = ((KeyFillPtr+2)&254)+BuffersAt;
// advance ptr
if (KeyFillPtr==KeyTakePtr) { // buffer o'flow,
discard oldest
if (mmPEEK(KeyTakePtr)==13) GotRetns
= GotRetns-1;
KeyTakePtr = ((KeyTakePtr+2)&254)+BuffersAt;
if (KeyFillPtr==KeyShowPtr) // discard
oldest show also..
KeyShowPtr = KeyTakePtr;}
} // ~while
} // ~DoKeyInts
void DoPrint() {
// This interrupt happens after line output completion
// Right now it's set up but unused.
int SemSP = 0; // maps to PrintFrame[3]
int unused;
int temp;
PrintFrame[6] = &PrintFrame[9]; // initial SP
PrintFrame[8] = &PrintFrame[0]; // initial FP
PrintFrame[0] = PEEK((&SemSP)-6); // copy my globals
ptr
PrintFrame[1] = 0;
// halt if it returns (it shouldn't)
SemSP = FORK(&PrintFrame[6]); // returns twice:
if (SemSP==0) { // returned in caller's process..
iVector[4] = &PrintFrame[3]; // set interrupt
vector
return;}
// otherwise returned in new process (first)..
POKE(SemSP-6,0); // clear caller's return value
while (true) { // this is the interrupt handler loop..
COCALL(&SemSP); // wait for interrupt
PrintReady = true;} // do nothing
} // ~DoPrint
void DoMemMgr() {
// This interrupt happens on a memory manager page fault
// Right now ...
int SemSP = 0; // maps to MMUFrame[3]
int page;
int temp;
MMUFrame[6] = &MMUFrame[9]; // initial SP
MMUFrame[8] = &MMUFrame[0]; // initial FP
MMUFrame[0] = PEEK((&SemSP)-6); // copy my globals
ptr
MMUFrame[1] = 0;
// halt if it returns (it shouldn't)
SemSP = FORK(&MMUFrame[6]); // returns twice:
if (SemSP==0) { // returned in caller's process..
iVector[0] = &MMUFrame[3]; // set interrupt
vector
return;}
// otherwise returned in new process (first)..
POKE(SemSP-6,0); // clear caller's return value
while (true) { // this is the interrupt handler loop..
iVector[0] = &MMUFrame[3]; // OK, put it
back
COCALL(&SemSP); // wait for interrupt
iVector[0] = 0; // so it crashes "Semaphor=0"
if still NG
page = ((MMU.fault >> 10)
& 63);
temp = PEEK(CurProcess+4)-1;
// address of MMU for this process
if ((PEEK(temp+page) &
255) == 126) { // unmapped..
POKE(temp+page,
PEEK(temp+page)-126+page+0xA0); // turn it on
if (MMU.PC
!= 0) { // stack faulted, restore saved values..
mmPOKE(SemSP,MMU.PC);
mmPOKE(SemSP-2,MMU.FP);
mmPOKE(SemSP-4,MMU.CB);}}
else mmPOKE(SemSP,0);
// otherwise kill it, PC=0
} // do something..
} // ~DoMemMgr
void DoClock() {
// This interrupt happens 8x each second, for timing purposes;
// we use it to maintain theTicks.
int SemSP = 0; // maps to ClockFrame[3]
int prior; // holds previous Date.seconds reading, updated
int temp;
ClockFrame[6] = &ClockFrame[9]; // initial SP
ClockFrame[8] = &ClockFrame[0]; // initial FP
ClockFrame[0] = PEEK((&SemSP)-6); // copy my globals
ptr
ClockFrame[1] = 0;
// halt if it returns (it shouldn't)
SemSP = FORK(&ClockFrame[6]); // returns twice:
if (SemSP==0) { // returned in caller's process..
iVector[1] = &ClockFrame[3]; // set interrupt
vector
return;}
// otherwise returned in new process (first)..
POKE(SemSP-6,0); // clear caller's return value
while (true) { // this is the interrupt handler loop..
COCALL(&SemSP); // wait for interrupt
temp = Date.seconds;
if (temp != prior) { // ticks management, got
a new second..
prior = temp;
theTicks = prior*8;}
else theTicks = theTicks+1;
// YOUR CODE GOES HERE
} } // ~DoClock
void DoSysCall() {
// this interrupt happens when the program executes SysCall
int SemSP = 0; // maps to SysCallFrame[3]
int aPtr;
int temp;
SysCallFrame[6] = &SysCallFrame[9]; // initial
SP
SysCallFrame[8] = &SysCallFrame[0]; // initial
FP
SysCallFrame[0] = PEEK((&SemSP)-6); // copy my
globals ptr
SysCallFrame[1] = 0;
// halt if it returns
SemSP = FORK(&SysCallFrame[6]); // create a new process
(2 returns)
if (SemSP==0) { // final return, into initial process
iVector[5] = &SysCallFrame[3]; // ready,
set interrupt vector
return;}
// otherwise returned in new process (first)..
POKE(SemSP-6,0); // clear caller's return value
while (true) { // this is the interrupt handler loop..
iVector[5] = &SysCallFrame[3]; // restore
double-fault crasher
COCALL(&SemSP); // wait for interrupt
iVector[5] = 0; // so it crashes "Semaphor=0"
on programming error
temp = mmPEEK(SemSP-8); // get a parameter (the
last pushed)
switch(mmPEEK(SemSP-6)) { // get opcode
case 0:
// exit from program
SemSP = SwapOut(SemSP,0); // Batch:
let main take care of it
break;
case 1:
// array bounds check
temp = mmPEEK(0xFABC); //
non-existing memory, to crash
mmPOKE(SemSP,0); // clear
caller's PC
break;
case 2:
// memory allocator
temp = temp+4; // # bytes wanted
if (HeapBottom+temp<HeapTop)
{ // do we have enough available?
aPtr = HeapTop-temp;
// yup, allocate it..
HeapTop = aPtr;
temp = temp-4;
mmPOKE(aPtr+2,temp);
// record the size
mmPOKE(aPtr,0);
// initially zero references
aPtr = aPtr+4;} // the
front of the data proper
else aPtr = 0; // otherwise return
null
mmPOKE(SemSP-8,aPtr); // return
pointer, or null
break;
case 3:
// memory de-allocator request
if (temp == 0) continue; // ignore
null (initial assignment)
temp = temp-4; // the pointer's
ref count is here
if (mmPEEK(temp) & -2 == 0)
{
mmPOKE(temp,0); // take
count down to 0 for viewing
} // dispose it (You
get to write this part)
else if (mmPEEK(temp) & -16
== 0) { // not permanent..
mmPOKE(temp,mmPEEK(temp)-1);}
// decrement reference count
break; // caller pops the pointer
case 4:
// open file
mmPOKE(SemSP-8, OpenFile(temp));
// OpenFile does the work
break;
case 5:
// close file
CloseFile(temp); // CloseFile does
the work
break;
case 6:
// read file
temp = ReadFile(mmPEEK(SemSP-10),
temp); // ReadFile does it
if (temp<0) // the device is
busy, so..
mmPOKE(SemSP,mmPEEK(SemSP)-1);
// back up PC to try again
// better: block this
process until it's unbusy,
// but we
don't have processes implemented yet
else mmPOKE(SemSP-10, temp); //
the return value (how many bytes)
break;
case 7:
// write file
temp = WriteFile(mmPEEK(SemSP-10),
temp);
if (temp<0) mmPOKE(SemSP,mmPEEK(SemSP)-1);
// back up PC
else mmPOKE(SemSP-10,
temp);
break;
case 8:
// seek file
// this is left to the student as
an exercise
break;
case 9:
// get file info
mmPOKE(SemSP-8, 0); // info? what
info? return 0
break;
case 13:
// wait for event (yield to other process)
SemSP = SwapOut(SemSP,0);
break;
case 14:
// ticks delay
if (temp<=0) { // asking for
a delay (>0 is target time)
temp = theTicks-temp;
mmPOKE(SemSP-8,temp);}
if (temp>theTicks)
mmPOKE(SemSP,mmPEEK(SemSP)-1);
// back up PC to wait some more
break;
case 17:
// set MMU
temp = mmPEEK(SemSP-8);
// new MMU setting
POKE(CurProcess+4,temp);
// save in PIB
MMU.address
= temp; // make it happen
break;
}} // ~while ~switch // otherwise do nothing
} // ~DoSysCall
// ==============================================================
// User-level system code..
// Look to see if HD is formatted, and if not, build directory
void HDformat(boolean forced) { // if forced=true, build it regardless
int ix;
int[128] Buff; // one sector, for the 7-file directory.
FileId HD = Open(".H"); // all I/O is at user level!
ix = Read(HD,Buff); // reads sector 0 into Buff
Close(HD);
if (!forced) if (ix==256) if (Buff[0]==ORD('H')*256+ORD('a'))
if (Buff[1]==ORD('r')*256+ORD('d'))
if (Buff[2]==ORD('D')*256+ORD('i'))
if (Buff[3]==ORD('s')*256+ORD('k')) return;
// it seems OK
ix = 128; // no, set up my 1-sector directory..
while (ix>0) {ix = ix-1; Buff[ix] = 0;} // clear the buffer,
Buff[0] = ORD('H')*256+ORD('a'); // then insert signature,
Buff[1] = ORD('r')*256+ORD('d');
Buff[2] = ORD('D')*256+ORD('i');
Buff[3] = ORD('s')*256+ORD('k');
Buff[4] = 0; // no boot sector
Buff[5] = 4; // next unused sector
Buff[6] = 0; // no files yet
Buff[7] = 7; // directory size (could be 31 in 4 sectors)
HD = Open(".H"); // OK, rewrite the directory
ix = Write(HD,Buff); // no error checking!
Close(HD);} // ~HDformat
// Read one line (up to \r) from keyboard, return it as String
String ReadStr() { // return a pointer to an input line
int ix = 0, nx = 0;
String theStr;
int theLen;
theLen = Read(Kin, KeyString)/2; // get a line
theStr = new char[theLen]; // allocate as many bytes as
we got
while (ix<theLen) { // copy them over to new String array..
theStr[ix] = KeyString[ix];
ix = ix+1;}
return theStr;} // ~ReadStr
// Display output String,
// a cleaner interface than needing to deal with returned
int..
void PrintStr(String str) {int ix = Write(Cout, str);}
// Display output character..
void PrintChar(char ch) { // uses SysCall, for safety
int nx;
char[1] cc; cc[0] = ch;
nx = Write(Cout, cc);}
// PrintNum, a string-free number display (uses PrintChar)
void PrintNum(int theNum) {
if (theNum==32768) { // special-case, it has no positive..
PrintChar('-');
PrintChar('3');
theNum = 2768;}
else if (theNum<0) { // negative number, show the sign
PrintChar('-');
theNum = -theNum;}
if (theNum>9) { // recursive call to do leftmost digits
first
PrintNum(theNum/10);
theNum = theNum%10;}
PrintChar(CHR(theNum+48));} // ~PrintNum
// Halt if stack overrun. Called by main() or a deep subroutine
// called from main(), in case I counted wrong in space allocation..
void BombCheck() {
int here; // a local address to test
if (&here>Directory-100) { // oops, this is a crasher...
PrintStr("** Stack Collision **");
here = SysOp0(0);}} // ~BombCheck
// This is the startup code that get the OS running.
// Call it once, before anything else..
void MiniOS() {
MMU.address = 0; // turn MMU off
BombCheck(); // verify that we have enough stack space to
do this
POKE(Directory-2,256); // make it look like array
POKE(Directory-4,-16);
DoSysCall(); // initialize each type of interrupt..
DoKeyInts();
DoDisk();
DoPrint();
DoClock();
DoSema4();
DoMemMgr();
MainPIB[0] = 0; // create a (fake) PIB for current process..
MainPIB[2] = 0; // my MMU initially
disabled
MainPIB[4] = 0;
CurProcess = &MainPIB;
Enables.datum = IntOnBit; // enables interrupts (reset turned
'em off)
HDformat(false); // check for hard drive & initialize
Kin = Open(".K"); // open default KeyIn & ScreenOut
files..
Cout = Open(".S");
KeyString = new char[120]; // used by ReadStr()
do PrintChar('\r'); while (Vrow>2); // clear screen (interrupt-safe)
} // ~MiniOS
// Some String operators...
// Return true if str1==str2 (not case sensitive)..
boolean StrEqual(String str1, String str2) {
int ix = LENGTH(str2);
char ch1, ch2;
if (LENGTH(str1) != ix) return false;
while (ix>0) {
ix = ix-1;
ch1 = str1[ix];
if (ch1>='A') if (ch1<='Z') ch1 = CHR(ORD(ch1)+32);
ch2 = str2[ix];
if (ch2>='A') if (ch2<='Z') ch2 = CHR(ORD(ch2)+32);
if (ch1 != ch2) return false;}
return true;} // ~StrEqual
// Parse out word wdno from aStr..
String GetWord(String aStr, int wdno) {
String res;
boolean inwd = false;
int here = 0, bgn = -1, end = -1;
while (here<LENGTH(aStr)) {
if ((aStr[here]>' ') != inwd) {
inwd = !inwd;
if (inwd) {
if (wdno==0) bgn = here;
wdno = wdno-1;}
else if (wdno<0) break;}
here = here+1;}
if (wdno<0) end = here;
if (bgn<end) {
res = new char[end-bgn];
here=0;
while (bgn<end) {
res[here] = aStr[bgn];
here = here+1;
bgn = bgn+1;}
return res;}
else return "";} // ~GetWord
// Some date extraction routines..
// These extract date parts from ints; they do not access the I/O
int YearOf(int datehours) {return (datehours>>1)/4383;}
// returns 4 for 2004
int HourOf(int datehours) {return (datehours-24000)%24;}
int JulianOf(int datehours) {return (datehours-YearOf(datehours)*8766)/24;}
int MonthDayOf(int datehours) { // computes both month and day..
final int[] mons = {31,28,31,30,31,30,31,31,30,31,30,31};
int yr = YearOf(datehours); // so we know if it's leapyear
int dy = JulianOf(datehours);
int mo = 0;
while (dy>=mons[mo]) { // count off the months..
dy = dy-mons[mo];
mo = mo+1;
if (mo==2) dy = dy-1;}
return mo*100+dy+101;} // ~MonthDayOf
int MonthOf(int datehours) {return MonthDayOf(datehours)/100;}
int DayOf(int datehours) {return MonthDayOf(datehours)%100;}
// This cheats a little, by reaching directly into the directory,
// to extract the name of the recently-opened floppy disk
file (fno=0),
// or the name of HD file #fno.
String GetFileName(int fno) { // return String name of the file
#fno
int ix = 0;
String theStr;
char[28] theName;
int[] DirAry; // will point to Directory
int theLen = 27;
FileId fi;
if (fno>0) { // HD file name..
POKE(&DirAry,Directory);
fi = Open(".H"); // opens the HD directory
ix = Read(fi,DirAry);
fno = fno-1;}
else fi = Open(".F"); // opens the floppy directory
unPackBy(28, Directory+fno*32+36, &theName); // get
name, padded by nulls
while (theLen>=0) {
if (theName[theLen]>' ') break;
theLen = theLen-1;}
theStr = new char[theLen+1]; // allocate as many bytes as
we got
ix = 0;
while (ix<=theLen) { // copy them over to new String
array..
theStr[ix] = theName[ix];
ix = ix+1;}
Close(fi);
return theStr;} // ~GetFileName
// Returns true if current floppy is "MiniOS"
// Used to skip user interaction after flop is changed,
// and to force DH reformat if not.
boolean MyFlopName() {
int nx = LENGTH(MyName); // = "MiniOS" packed as int[]
int ix = 0;
FileId fi = Open(".F"); // opens the floppy directory
while (ix<nx) { // compare it to "MiniOS"
if (MyName[ix] != PEEK(Directory+36+ix*2)) nx
= -1; // unequal (exit)
ix = ix+1;}
Close(fi);
return nx>0;} // ~MyFlopName
// If current floppy is "MiniOS", asks for new;
// returns true if current floppy is still "MiniOS"
boolean FlopSwap() {
if (MyFlopName()) {
PrintStr("Mount floppy, then click\r");
while (Mouse.datum>=0) ;
while (Mouse.datum<0) ;}
return MyFlopName();} // ~FlopSwap
// Show HD directory..
void ShowDir() {
String str;
int fno = 0;
while (true) {
fno = fno+1;
str = GetFileName(fno);
if (LENGTH(str)==0) return;
PrintStr("\r");
PrintNum(fno);
PrintStr(" ");
PrintNum(PEEK(Directory+fno*32));
PrintStr(" ");
PrintNum(PEEK(Directory+fno*32+2));
PrintStr(" ");
PrintStr(str);}} // ~ShowDir
// Copy one (program) file from floppy to hard disk..
void CopyFlop() {
String str;
FileId fi, fo;
int[128] buffer;
int nby, totby = 0;
BombCheck();
if (FlopSwap()) HDformat(true); // erase hard drive
str = GetFileName(0); // the name on disk will be same as
on floppy
PrintStr("Copying file '");
PrintStr(str);
PrintStr("' ");
fi = Open(".F"); // open floppy source
fo = Open(str); // open destination file, same name
nby = (Read(fi, buffer)+1)/2; // read a block
totby = buffer[0]; // get the byte count
PrintNum(totby);
PrintStr(" bytes ");
while (totby>0 && nby>0) {
while (nby<128) {buffer[nby] = 0; nby = nby+1;}
// fill out partial
if (nby*2 > Write(fo, buffer)) { // write it
PrintStr("Write Error ");
break;}
PrintChar('.'); // one dot every block
totby = totby-nby*2; // count bytes down
nby = (Read(fi, buffer)+1)/2;} // read next
block
Close(fi);
Close(fo);
PrintStr(" Done.\r");} // ~CopyFlop
// Load a program file, rewritten for Mar25
assignment..
void LoadProgram(String prog) {
final int maxCode = 1024; // punt:
only read programs < than this
String str;
FileId fi;
int CodeBase, Stack;
int[] DiskBlk = new int[maxCode];
// a low-memory disk buffer
int nx = 0xC0C1, ix = 32;
BombCheck();
while (ix>0) { // setup working MMU
= writeable real
ix = ix-1;
PageTable[ix] = ix*514+nx;
// ix counts down -1 for every
two bytes,
// so
the page # is actually ix*2 on the left (=ix*2*256), plus
ix*2+1 on the right;
// the
+1 part is built into nx (=0xC0C1). nx also adds on an offset
to start the table
// in
the top quadrant (+0x40) and a write enable bit (0x80).
Later, when we reach
// the
middle (ix==16), we remove the +0x40 offset for the final
(lower) quadrant.
if (ix>23)
AppPIB[ix+8]
= ix*514+0xA0A1; // user MMU will be middle 64K
else AppPIB[ix+8] = 126*257;
// except stack is mostly disabled
// the
user MMU table is embedded in the same array as the PIB, offset +16 bytes.
// each
page is offset +32 to get the middle two quadrants of memory
if (ix==16) nx = 0x8081;}
//
AppPIB[8] = 0x80A1; // user PIB is
in page 0 = real page 0
// Note
that page 1 is still offset +32 (i.e. at hex 0xA1)
fi = Open(prog);
nx = Read(fi,DiskBlk); // read the
whole program if < 2K
Close(fi);
ix = DiskBlk[0];
PrintStr("Got "); PrintNum(ix); PrintStr(" bytes...");
if (nx<ix || ix<30) {
if (nx<ix) PrintStr("Program
is too big");
else PrintStr("Could not open code
file");
return;}
PageTable[31] = 0xDFFF; // point my
page 126 to user page 63
// Page
63 is still the (writeable) I/O page, =255 =0xFF, we only change page 62
// (the
left byte) to be user page 63, which is actual page 95, bits: W101.1111,
=0xDF
nx = SysOp1(17, &PageTable); //
tell system to turn MMU on
nx = maxCode;
ix = 0xFC00;
while (nx>0) {
nx = nx-1;
ix = ix-2;
mmPOKE(ix, DiskBlk[nx]);
// mmPOKE here, so the MMU page table
is used
// actually,
it's used anyway, because this executes in user mode
if ((nx & 511)==0)
{
PageTable[31]
= PageTable[31] - 0x100; // move it down 1 page
// subtract 1 from the left byte, which
is the mobile page, now = user 62
ix = 0xFC00;}}
CodeBase = 2-maxCode*2; // nominal
start of code
Stack = 1922; // start stack in page
1 (1026)
ix = Stack-3072; // now points to
user page 1 (still my page 126)
PageTable[31] = 0xA1FF; // my page
126 now points to user page 1
POKE(ix-2, 0x2009); // SysCall(0)
for quit
POKE(ix+2, Stack-2); // return address
is that SysCall
POKE(ix, Stack); // globals ptr, =FP
POKE(ix+4, CodeBase);
POKE(ix+6, Stack); // initial
FP
POKE(ix+8, DiskBlk[1]+CodeBase); //
initial PC
AppPIB[3] = Stack+8; // initial SP,
where Process Mgr can find it
AppPIB[2] = 16+&AppPIB; // user
MMU, so Process Mgr can find it
AppPIB[0] = ReadyList; // link to
front of ReadyList
ReadyList = &AppPIB;
PrintStr(" Success!\r");} // ~LoadProgram
// Start new thread...
void TakenShow() { // a thread to take chars from queue & display
them
int temp; // maps to DemoFrame[7] (allow 4 ints
for PIB)
DemoFrame[10] = &DemoFrame[13]; // initial SP
DemoFrame[12] = &DemoFrame[4]; // initial FP
DemoFrame[4] = PEEK((&temp)-6); // copy my globals
ptr
DemoFrame[5] = 0;
// halt if it returns (it shouldn't)
temp = FORK(&DemoFrame[10]); // returns twice:
if (temp==0) return; // returned in caller's process
// otherwise returned in new process (first)..
POKE(temp-6,0); // clear caller's return value
POKE(CurProcess+6,temp); // save caller's SP in its PIB
POKE(CurProcess,ReadyList); // move caller to ready list..
ReadyList = CurProcess;
CurProcess = &DemoFrame; // make me current
while (true) { // OK, process the data..
Wait(take);
PrintChar(queue[tx]);
Signal(fill);
tx = (tx+1)%Qsize;}
} // ~TakenShow
void DoSemDemo() { // Demonstrate Semaphores..
int ix = Qsize;
char[1] GetKey;
while (ix>0) { // initialize this semaphore; take is already
OK
Signal(fill);
ix = ix-1;}
TakenShow(); // go fork off a thread to do the other end
while (true) { // OK, process the data..
Wait(fill);
ix = Read(Kin, GetKey); // get a keystroke
ix = ORD(GetKey[0]);
queue[fx] = CHR(ix^((ix>>1)&32)); // [capitalize
letters]
Signal(take);
fx = (fx+1)%Qsize;}
} // ~DoSemDemo
// The main program..
void main() {
int here = 0;
String str, prog;
MiniOS(); // start up operating system
while (true) {
here = 0;
PrintStr("\rQuit, Copy (Flop->HD), <filename>\r>
");
str = ReadStr();
if (StrEqual(str,"Quit")) break;
else if (StrEqual(str,"Demo")) DoSemDemo();
while (true) {
prog = GetWord(str,here);
here = here+1;
if (LENGTH(prog)==0) break;
if (StrEqual(prog,"Copy")) CopyFlop();
else if (StrEqual(prog,"Dir")) ShowDir();
else {
LoadProgram(prog); //
load it and start it up as a process
if (SysOp0(13)==0) ;}}}
// wait for termination
PrintStr("\rBye!");} // ~main