jsos

college code for operating system fundamentals in js

git clone https://9o.is/git/jsos.git

commit b0354fa7a1147a450f0643042512f92b690b15a3
parent 1a4469efd985b98ec8d6984129feb326219e6f90
Author: Jul <jul@9o.is>
Date:   Mon, 10 Dec 2012 21:44:41 -0500

Implemented file system.

Diffstat:
Mglobals.js | 4+++-
Mindex.html | 176++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mscripts/host/cpu.js | 2++
Mscripts/host/disk.js | 141++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Ascripts/host/interface.js | 38++++++++++++++++++++++++++++++++++++++
Mscripts/os/console.js | 6++++++
Mscripts/os/deviceDriverFilesystem.js | 544++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mscripts/os/memoryManager.js | 48+++++++++++++++++++++++++++++++++++++++---------
Mscripts/os/pcb.js | 10++++++++++
Mscripts/os/scheduler.js | 121++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mscripts/os/shell.js | 132++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mscripts/utils.js | 37+++++++++++++++++++++++++++++++++++++
Astyles/julios-tabs.css | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Mstyles/julios.css | 25+++++++++----------------
14 files changed, 1125 insertions(+), 212 deletions(-)

diff --git a/globals.js b/globals.js @@ -79,9 +79,11 @@ var krnKeyboardDriver = null; var krnFilesystemDriver = null; // memory size -var TOTAL_PAGES = 4; +var TOTAL_PAGES = 3; +var SWAPPABLE = 1; var MAX_MEMORY_PROGRAM = 256; //bytes var TOTAL_MEMORY = TOTAL_PAGES * MAX_MEMORY_PROGRAM; //bytes +var TOTAL_PAGES_AND_SWAPS = TOTAL_PAGES + SWAPPABLE; // pcb states var PCB_STATE_RESIDENT = "resident"; diff --git a/index.html b/index.html @@ -8,6 +8,7 @@ <link rel="stylesheet" href="styles/1140.css" type="text/css" media="screen" /> <link rel="stylesheet" href="styles/julios.css" type="text/css" media="screen" /> <link rel="stylesheet" href="styles/julios-buttons.css" type="text/css" media="screen" /> + <link rel="stylesheet" href="styles/julios-tabs.css" type="text/css" media="screen" /> <title> JuliOS 0.3 - a Browser-based virtual Operating System @@ -22,6 +23,7 @@ <script type="text/javascript" src="scripts/host/cpu.js"></script> <script type="text/javascript" src="scripts/host/memory.js"></script> <script type="text/javascript" src="scripts/host/disk.js"></script> + <script type="text/javascript" src="scripts/host/interface.js"></script> <!-- Virtual OS Routines: Make sure Kernel code is last, since it needs those above it. --> <script type="text/javascript" src="scripts/os/interrupt.js"></script> @@ -46,7 +48,7 @@ <body> <div class="container"> <div class="row"> - <div class="threecol" id="buttons"> + <div class="fourcol" id="buttons"> <button type="button" id = "btnStartOS" class="btnicon28" @@ -65,82 +67,118 @@ onclick="simBtnReset_click(this)"> <i class="icon28-reset"></i></button> </div> - <div class="fourcol" id="processes"> - <h4>Processes</h4> - <table cellspacing="7" id="processesTable"> - <tr> - <th>pid</th> - <th>state</th> - <th>base</th> - <th>limit</th> - <th>PC</th> - <th>ACC</th> - <th>X</th> - <th>Y</th> - <th>Z</th> - <th>P</th> - </tr> - </table> - </div> - <div class="onecol"> - <h4>Registers</h4> - <table> - <tr> - <th align="right">PC:</th> - <td id="pc_register"></td> - </tr> - <tr> - <th align="right">ACC:</th> - <td id="acc_register"></td> - </tr> - <tr> - <th align="right">X:</th> - <td id="x_register"></td> - </tr> - <tr> - <th align="right">Y:</th> - <td id="y_register"></td> - </tr> - <tr> - <th align="right">Z:</th> - <td id="z_register"></td> - </tr> - </table> - </div> - <div class="fourcol last"> - <textarea id="taProgramInput" - placeholder="Input Program hex here"></textarea> - </div> + <div class="eightcol last"></div> </div> <div class="row"> - <div class="tencol"> + <div class="eightcol"> <canvas id="display" tabindex="0" - width="950" - height="450"></canvas> - </div> - <div class="twocol last"> - <table> - <tr><th>CPU Utilization</th></tr> - <tr><td id="cpu_utilization">0%</td></tr> - <tr><th>CPU Throughput</th></tr> - <tr><td id="cpu_throughput">0%</td></tr> - </table> + width="750" + height="630"></canvas> </div> - </div> + <div class="fourcol last"> + <div id="tabContainer"> + <div class="tabs"> + <ul> + <li id="tabHeader_1">CPU</li> + <li id="tabHeader_2">Memory</li> + <li id="tabHeader_3">Disk</li> + <li id="tabHeader_4">Log</li> + <li id="tabHeader_5">Load</li> + </ul> + </div> + <div class="tabscontent"> + <div class="tabpage" id="tabpage_1"> - <div class="row"> - <div class="eightcol"> - <textarea id="taLog" - placeholder="No Log Output"></textarea> - </div> + <div style="float:left"> + <b><span id="cpu_utilization">Utilization: 0%</span></b><br> + <canvas id="cpu_utilization_bar" width="160" + height="7"></canvas><br> + <canvas id="cpu_utilization_graph" width="160" + height="100"></canvas> + </div> + + <div style="float:right"> + <b><span id="cpu_throughput">Throughput: 0%</span></b><br> + <canvas id="cpu_throughput_bar" width="160" + height="7"></canvas><br> + <canvas id="cpu_throughput_graph" width="160" + height="100"></canvas> + </div> + + <div style="float:left"> + <b><div id="cpu_turnaround">Turn Around: 0%</div></b> + <canvas id="cpu_turnaround_graph" width="160" + height="100"></canvas> + </div> - <div class="fourcol last" id="memoryDump"> - <table cellspacing="10" id="memoryDumpTable"> - <tr><th>Empty Memory Dump</th></tr> - </table> - </div> + <div style="float:right"> + <b><div id="cpu_wait">Wait: 0%</div></b> + <canvas id="cpu_wait_graph" width="160" + height="100"></canvas> + </div> + + <div style="clear:both"></div> + + <div> + <h4>Registers</h4> + <table> + <tr> + <th align="right">PC:</th> + <td id="pc_register"></td> + </tr> + <tr> + <th align="right">ACC:</th> + <td id="acc_register"></td> + </tr> + <tr> + <th align="right">X:</th> + <td id="x_register"></td> + </tr> + <tr> + <th align="right">Y:</th> + <td id="y_register"></td> + </tr> + <tr> + <th align="right">Z:</th> + <td id="z_register"></td> + </tr> + </table> + </div> + + <div> + <h4>Processes</h4> + <table cellspacing="7" id="processesTable"> + </table> + </div> + + </div> + + <div class="tabpage" id="tabpage_2"> + <div> + <table cellspacing="10" id="memoryDumpTable"> + <tr><th>Empty Memory Dump</th></tr> + </table> + </div> + </div> + + <div class="tabpage" id="tabpage_3"> + <p>Disk info goes here...</p> + </div> + + <div class="tabpage" id="tabpage_4"> + <textarea id="taLog" + placeholder="No Log Output"></textarea> + </div> + + <div class="tabpage" id="tabpage_5"> + <textarea id="taProgramInput" + placeholder="Input Program hex here"></textarea> + </div> + + </div> + </div> </div> <div class="row"> diff --git a/scripts/host/cpu.js b/scripts/host/cpu.js @@ -34,6 +34,8 @@ function cpu() { krnTrace("CPU cycle"); this.execute(); this.output(); + + _KernelCPUScheduler.running.bursts += 1; } /* diff --git a/scripts/host/disk.js b/scripts/host/disk.js @@ -18,96 +18,135 @@ * * Total storage available is 16,384 bytes with * 4 tracks with 8 sectors with 4 blocks with 64 bytes. + * + * Write and Read functions handle data as array, not string. */ -function disk() { - var TRACKS = 4; - var SECTORS = 8; - var BLOCKS = 8; - var BYTES = 64; +function disk() { + + this.TRACKS = 4; + this.SECTORS = 8; + this.BLOCKS = 8; + this.BYTES = 64; - var init = function() { + this.init = function() { // TODO do I need this? }; /* * Reads a single byte from a byte. */ - var readByte = function(tsb,byte) { - if(invalidDiskByte(byte)) - return; //TODO throw bsod? - return read(tsb).substring(byte, byte+1); - } + this.readByte = function(tsb,byte) { + if(this.invalidTSB(tsb) || this.invalidDiskByte(byte)) + return undefined; + + var data = this.read(tsb); + if(data == undefined) + return data; + return data[byte-1]; + }; /* * Reads data from a block. */ - var read = function(tsb) { - return sessionStorage.getItem(tsb.toString()); + this.read = function(tsb) { + if(this.invalidTSB(tsb)) + return []; + + var data = localStorage.getItem(tsb.toString()); + if(data == undefined) return []; + return data.split(","); }; /* * Writes a single byte into a block. */ - var writeByte = function(tsb, byte, data) { - if(invalidDiskByte(byte) || invalidDiskData(data)) - return; //TODO throw bsod? - - var read = read(tsb); - var input = read.substring(0, byte) + data + - read.substring(byte+1, read.length); - write(tsb, input); + this.writeByte = function(tsb, byte, data) { + if(this.invalidTSB(tsb) || !isByte(data)) + return false; + + var read = this.read(tsb); + read[byte-1] = data; + localStorage.setItem(tsb.toString(), read); + return true; }; /* * Writes a sequence of bytes into a block and returns * any data that didn't fit. + * @param clear erase everything in the block before writing. */ - var write = function(tsb, data) { - data = data.toString(); - - function writeData(data) { - sessionStorage.setItem(tsb.toString(), data); - } + this.write = function(tsb, data, clear) { + if(this.invalidTSB(tsb) || this.invalidDiskData(data)) + return false; - if(data.length > BYTES) { - writeData(data.substring(0, BYTES)); - return data.substring(BYTES, data.length); + if(clear) { + localStorage.setItem(tsb.toString(), ""); + localStorage.setItem(tsb.toString(), data.slice(0,this.BYTES)); } else { - writeData(data.substring(0, BYTES)); - return undefined; + localStorage.setItem(tsb.toString(), this.read(tsb.toString()).concat(data.slice(0,this.BYTES))); } + + if(data.length > this.BYTES) + return data.slice(this.BYTES, data.length); + else + return []; }; /* * Checks if byte is a valid location in storage. */ - var invalidDiskByte = function(byte) { - return !isByte(byte) || byte >= BYTES; - } + this.invalidDiskByte = function(byte) { + return !isByte(byte) || byte > this.BYTES; + }; /* * Checks if data is valid and can be stored. */ - var invalidDiskData = function(data) { - data = data.toString(); - for(i=0; i<data.length; i++) - if(!isByte(data[i])) return false; - return true; - } + this.invalidDiskData = function(data) { + for(i=0; i<data.length; i++) { + var dataHex = parseInt(data[i], 16); + if(!isByte(dataHex)) { return true; } + } + return false; + }; + + /* + * Checks if tsb object has invalid + * track, sector, block values. + */ + this.invalidTSB = function(tsb) { + if(tsb == undefined) return true; + + var t = tsb.track; + var s = tsb.sector; + var b = tsb.block; + + if(t > this.TRACKS || t < 1 || + s > this.SECTORS || s < 1 || + b > this.BLOCKS || b < 1) return true; + + return false; + }; + + this.emptyBlock = function() { + var data = []; + for(i=0; i<this.BYTES; i++) + data.push(0); + return data; + }; }; /* * Holds a track, sector, block value. */ function tsb(t,s,b) { - var track = t.toString(); - var sector = s.toString(); - var block = b.toString(); - - var toString = function() { - return - track.toString() + - sector.toString() + - block.toString(); - } + this.track = t; + this.sector = s; + this.block = b; + + this.toString = function() { + return this.track.toString()+ + this.sector.toString()+ + this.block.toString(); + }; }; diff --git a/scripts/host/interface.js b/scripts/host/interface.js @@ -0,0 +1,38 @@ +window.onload=function() { + + // get tab container + var container = document.getElementById("tabContainer"); + // set current tab + var navitem = container.querySelector(".tabs ul li"); + //store which tab we are on + var ident = navitem.id.split("_")[1]; + navitem.parentNode.setAttribute("data-current",ident); + //set current tab with class of activetabheader + navitem.setAttribute("class","tabActiveHeader"); + + //hide two tab contents we don't need + var pages = container.querySelectorAll(".tabpage"); + for (var i = 1; i < pages.length; i++) { + pages[i].style.display="none"; + } + + //this adds click event to tabs + var tabs = container.querySelectorAll(".tabs ul li"); + for (var i = 0; i < tabs.length; i++) { + tabs[i].onclick=displayPage; + } +} + +// on click of one of tabs +function displayPage() { + var current = this.parentNode.getAttribute("data-current"); + //remove class of activetabheader and hide old contents + document.getElementById("tabHeader_" + current).removeAttribute("class"); + document.getElementById("tabpage_" + current).style.display="none"; + + var ident = this.id.split("_")[1]; + //add class of activetabheader to new active tab and show contents + this.setAttribute("class","tabActiveHeader"); + document.getElementById("tabpage_" + ident).style.display="block"; + this.parentNode.setAttribute("data-current",ident); +} diff --git a/scripts/os/console.js b/scripts/os/console.js @@ -35,6 +35,7 @@ function Console() { this.resetScroll = consoleResetScroll; this.scroll = consoleScroll; this.refresh = consoleRefresh; + this.setColor = consoleSetColor; } function consoleInit() { @@ -270,3 +271,8 @@ function consoleTaskbarRefresh() { this.taskbarDateTime(); this.taskbarStatus(this.statusText); } + +/* Sets fill style color. Usually to change text color. */ +function consoleSetColor(color) { + DRAWING_CONTEXT.strokeStyle = color; +} diff --git a/scripts/os/deviceDriverFilesystem.js b/scripts/os/deviceDriverFilesystem.js @@ -6,51 +6,286 @@ The Kernel Filesystem Device Driver. - Encoding format for each block: - status , track , sector , block + Header format for each block: + status , track , sector , block (1 byte each) - total: 4 bytes - data: 60 bytes + header: 4 bytes + payload: 60 bytes Status represents type and availability flag bits: - - One Byte - ----------------------------------- - |X|X|X|X|X|X| type | availability | - ----------------------------------- + + ---------------------------------------- + |X|X|X|X|X| type | type | availability | + ---------------------------------------- + + file types: 0=text, 1=directory, 2=swap + availability: 0=available 1=unavailable ---------------------------------- */ DeviceDriverFilesystem.prototype = new DeviceDriver; // "Inherit" from prototype function DeviceDriverFilesystem() { + + /* TSB of root directory. */ + this.rootDir = undefined; - /* Max number of first status bit for each block. */ - var STATUS_BIT_SIZE = 4; + /* TSB of current working directory. */ + this.cd = undefined; - /* - * Maps existing directory names to disk location by TSB. - */ - var directory = {}; - - // TODO var is hidden scope but not rest? this.driverEntry = function() { + + // set all tsb's that dont exist to zeros + //this.init(); + var rootTSB = new tsb(1,1,1); + + // init if it hasn't been before + if(_Disk.readByte(rootTSB,1) == undefined) + this.initBlockDirectory(rootTSB); + this.rootDir = new Directory(rootTSB); + this.cd = this.rootDir; + this.status = "loaded"; }; - var isr = function() { + this.isr = function() { //TODO }; /* - * Formats entire filesystem by overriding directories. + * Formats entire filesystem by setting it to intial use. */ - var format = function() { - var data = pad("0", _Disk.BYTES); - for(i=0; i<directories.length; i++) { - //_Disk.write(directories[i]) - + this.format = function() { + for(t=1; t<=_Disk.TRACKS; t++) + for(s=1; s<=_Disk.SECTORS; s++) + for(b=1; b<=_Disk.BLOCKS; b++) { + var ts = new tsb(t,s,b); + if(_Disk.read(ts).length == 0) + _Disk.write(ts,[0], true); + else { + var file = new File(ts); + if(file.type() != file.TYPE_SWAP) + _Disk.write(ts,[0], true); + } + } + + return true; + }; + + this.allDirectories = function(dir) { + var dirs = [dir]; + var directories = dir.directories(); + while(directories.length != 0) { + for(i=0; i<directories.length; i++) { + dirs.concat(this.allDirectories(directories[i])); + } + } + return dirs; + }; + + this.initBlockTextFile = function(ts) { + // type text & unavailable -- next block (0,0,0) & GS=1D + var value = ["1","0","0","0","1d"]; + _Disk.write(ts,value, true); + }; + + this.initBlockDirectory = function(ts) { + // type dir & unavailable -- next block (0,0,0) & GS=1D + var value = ["3","0","0","0","1d"]; + _Disk.write(ts,value, true); + }; + + this.initBlockData = function(ts, type) { + // type set & unavailable -- next block (0,0,0) + var value = [type,"0","0","0"]; + _Disk.write(ts,value, true); + }; + + this.getAvailableBlock = function() { + for(t=1; t<=_Disk.TRACKS; t++) + for(s=1; s<=_Disk.SECTORS; s++) + for(b=1; b<=_Disk.BLOCKS; b++) { + var ts = new tsb(t,s,b); + var f = new File(ts); + if(f.available() || _Disk.readByte(ts,1) == undefined) + return ts; + } + return undefined; + }; + + this.createTextFile = function(fn) { + fn = fn.trim(); + fn = this.rename(fn); + var tsb = this.getAvailableBlock(); + if(tsb == undefined) return false; + + this.initBlockTextFile(tsb); + var textFile = new TextFile(tsb); + textFile.setName(fn); + return this.cd.addTextFile(textFile); + }; + + this.createDirectory = function(fn) { + fn = fn.trim(); + fn = this.rename(fn); + var tsb = this.getAvailableBlock(); + if(tsb == undefined) return false; + + this.initBlockDirectory(tsb); + var dir = new Directory(tsb); + dir.setName(fn); + return this.cd.addDirectory(dir); + }; + + this.writeTextFile = function(fn, text) { + fn = fn.trim(); + var file = this.cd.getTextFile(fn); + if(file == undefined) return false; + return file.setText(text); + }; + + this.readTextFile = function(fn) { + fn = fn.trim(); + var file = this.cd.getTextFile(fn); + if(file == undefined) + return undefined; + return file.text(); + }; + + this.deleteFile = function(fn) { + fn = fn.trim(); + return this.cd.deleteFile(fn); + }; + + this.ls = function() { + return this.cd.directories().concat(this.cd.textFiles()); + }; + + this.changeDirectory = function(fn) { + fn = fn.trim(); + + if(fn=="..") { + if(this.cd.parentDirectory() != undefined) { + this.cd = this.cd.parentDirectory(); + return true; + } else + return false; + } + if(fn==".") { + return true; + } + + var dir = this.cd.getDirectory(fn); + if(dir != undefined) { + this.cd = dir; + return true; + } + return false; + }; + + // rename if existing filename in this current directory + this.rename = function(name,suffix) { + if(suffix==undefined) suffix=0; + + var dirs = this.cd.directories(); + var txtFiles = this.cd.textFiles(); + + for(var i = 0; i < dirs.length; i++) { + var name1 = dirs[i].name(); + if(name1 == name) + return this.rename(name+pad("",suffix+1), suffix+1); + } + for(var i = 0; i < txtFiles.length; i++) { + if(txtFiles[i].name() == name) + return this.rename(name+pad("",suffix+1), suffix+1); + } + + return name; + }; +}; + +function File(TSB) { + + this.TYPE_TEXT = 0; + this.TYPE_DIR = 1; + this.TYPE_SWAP = 2; + + /* Header size of file. */ + this.HEADER_SIZE = 4; + + this.tsb = TSB; + this.all_tsb = [TSB]; + + /* + * Gets the data of this file from disk. + */ + this.data = function() { + var data = _Disk.read(this.tsb); + var header = data.slice(0, this.HEADER_SIZE); + while(this.next(header) != undefined) { + var tsb = this.next(header); + var extData = _Disk.read(tsb); + header = extData.slice(0, this.HEADER_SIZE); + extData = extData.slice(this.HEADER_SIZE, extData.length); + data = data.concat(extData); } + return data; + }; + + /* + * Write data to this file. + * @param clear erase existing data + */ + this.write = function(data, clear) { + if(clear) { + // set all TSB's to available + for(i=1; i<this.all_tsb.length; i++) + _Disk.write(this.all_tsb[i], [0], true); + this.all_tsb = this.all_tsb.slice(0,1); + } else { + data = this.data().concat(data); + } + + return this.writeData(data, clear); + } + + this.writeData = function(data,clear) { + var ts = this.tsb; + while(data.length > 0) { + data = _Disk.write(ts, data, clear); + + // get extra block if data does not fit + if(data.length > 0) { + var ts1 = krnFilesystemDriver.getAvailableBlock(); + if(ts1 == undefined) return false; + this.all_tsb.push(ts1); + this.setNext(ts, ts1); + ts = ts1; + data = [this.data()[0],"0","0","0"].concat(data); + } + } + return true; + } + + /* + * Get the next block if the data in this block + * continues to another one. + */ + this.next = function(data) { + var track = data[1]; + var sector = data[2]; + var block = data[3]; + + if(track==0 || sector==0 || block==0 || + track==undefined || sector==undefined || block==undefined) + return undefined; + return new tsb(track, sector, block); + }; + + this.setNext = function(tsb, tsbNext) { + return _Disk.writeByte(tsb,2, tsbNext.track) && + _Disk.writeByte(tsb,3, tsbNext.sector) && + _Disk.writeByte(tsb,4, tsbNext.block); }; /* @@ -58,44 +293,265 @@ function DeviceDriverFilesystem() { * write) of the block. * If true, block is available, else it's occupied. */ - var availablity = function(data) { - var byte = data.substring(0,1); - var bit = byte.toString(2).substring(7,8); - return bit == 1 ? true : false; + this.available = function() { + var byte = parseInt(this.data()[0]); + var bit = pad(byte.toString(2),8).substring(7,8); + return bit == 0 ? true : false; }; /* - * Get the type (if file or directory) of the block. + * Get the type of the block. * If true, it's a file, else it's a directory. */ - var type = function krnFsType(data) { - var byte = data.substring(0,1); - var bit = byte.toString(2).substring(6,7); - return bit == 1 ? true : false; + this.type = function() { + var data = this.data(); + var byte = parseInt(data.slice(0,1)[0]); + var typeBinary = pad(byte.toString(2),8).substring(5,7); + var type = parseInt(typeBinary, 2); + return type; }; /* - * Get the next block if the data in this block - * continues to the next. + * Returns the data excluding the header. */ - var next = function(data) { - var track = data.substring(1,2); - var sector = data.substring(2,3); - var block = data.substring(3,4); + this.payload = function() { + var data = this.data(); + return data.slice(this.HEADER_SIZE, data.length); + }; +}; - if(track==0 && sector==0 && block==0) +/* + * A directory in a filesystem. + * Header format for each directory is the name of the + * directory. Name can be any length so header is not a + * fixed size. Header and payload is separated by a + * "group separator" (hex=1D). + * + * Payload format is a list of TSB's (3 bytes each) that + * point to other directories and files. + * The first TSB is the parent directory. + */ +Directory.prototype = new File(); +function Directory(TSB) { + File.call(this, TSB); + + this.parentDirectory = function() { + if(this.tsb.toString() != "111") { // not root directory + var ts = this.data().slice(this.gs()+1, this.gs()+4); + return new Directory(new tsb(ts[0],ts[1],ts[2])); + } else return undefined; - return new tsb(track, sector, block); + }; + + this.gs = function(payload) { + var data = payload ? this.payload() : this.data(); + + for(i=0; i<data.length; i++) + if(data[i] == "1d") + return i; + }; + + this.name = function() { + var hex_name = this.data().slice(this.HEADER_SIZE, this.gs(false)); + var name = ""; + + for(i=0; i<hex_name.length; i++) + name += hex2ASCII(hex_name[i]); + + return name; + }; + + this.files = function() { + var data = this.payload(); + data = data.slice(this.gs(true)+1, data.length); + + var files = []; + for(i=0; i<data.length; i+=3) { + var ts = new tsb(data[i], data[i+1], data[i+2]); + if(ts != undefined) + files.push(new File(ts)); + } + + return files; + }; + + this.directories = function() { + var dirs = []; + var files = this.files(); + for(var i = 0; i < files.length; i++) { + if(files[i].type() == this.TYPE_DIR) + dirs.push(new Directory(files[i].tsb)); + } + + if(this.tsb.toString() != "111") { // not root directory + return dirs.slice(1, dirs.length); + } else + return dirs; + }; + + this.getDirectory = function(fn) { + var directories = this.directories(); + for(var i=0; i<directories.length; i++) + if(directories[i].name() == fn) + return directories[i]; + return undefined; + }; + + this.textFiles = function() { + var textFiles = []; + var files = this.files(); + for(i=0; i<files.length; i++) + if(files[i].type() == this.TYPE_TEXT) + textFiles.push(new TextFile(files[i].tsb)); + + return textFiles; + }; + + this.getTextFile = function(fn) { + var textFiles = this.textFiles(); + for(var i=0; i < textFiles.length; i++) + if(textFiles[i].name() == fn) + return textFiles[i]; + return undefined; + }; + + this.addDirectory = function(dir) { + dir.write(dir.data().concat([this.tsb.track,this.tsb.sector,this.tsb.block]), true); + var tsb = dir.tsb; + var data = this.data().concat([tsb.track, tsb.sector, tsb.block]); + return this.write(data, true); + }; + + this.addTextFile = function(textFile) { + var tsb = textFile.tsb; + var data = this.data().concat([tsb.track, tsb.sector, tsb.block]); + return this.write(data, true); + }; + + this.deleteFile = function(name) { + var file = undefined; + var directories = this.directories(); + var textFiles = this.textFiles(); + + // find from directories + for(var i = 0; i < directories.length; i++) { + if(directories[i].name() == name) { + file = directories[i]; + directories.splice[i,1]; + } + } + + // find from text files + for(var i = 0; i < textFiles.length; i++) { + if(textFiles[i].name() == name) { + file = textFiles[i]; + textFiles.splice[i,1]; + } + } + + if(file == undefined) + return false; + else { + _Disk.write(file.tsb,[0], true); + return this.deleteTSB(file.tsb); + } + }; + + this.deleteTSB = function(tsb) { + var data = this.data(); + var header = data.slice(0, this.gs(false)+1); + var payload = data.slice(this.gs(false)+1, data.length); + + for(var i = 0; i < payload.length; i+=3) { + if(tsb.track == payload[i] && + tsb.sector == payload[i+1] && + tsb.block == payload[i+2]) { + payload[i] = 0; + payload[i+1] = 0; + payload[i+2] = 0; + return this.write(header.concat(payload), true); + } + } + return false; + }; + + this.setName = function(name) { + var data = this.data(); + data = data.slice(0, this.HEADER_SIZE).concat( + ascii2hex(name)).concat( + data.slice(this.gs(false), data.length)); + + return this.write(data, true); }; }; -function krnFsDispatch(params) { - -} +/* + * A file in a filesystem. + * Header format for each file is the name of the + * file. Name can be any length so header is not a + * fixed size. Header and payload is separated by a + * "group separator" (hex=1D). + * + * Payload format is the ascii hex data of a file. + */ +TextFile.prototype = new File(); +function TextFile(TSB) { + File.call(this, TSB); + + this.gs = function(payload) { + var data = payload ? this.payload() : this.data(); + + for(i=0; i<data.length; i++) + if(data[i] == "1d") + return i; + }; + this.name = function() { + var hex_name = this.data().slice(this.HEADER_SIZE, this.gs(false)); + var name = ""; + for(i=0; i<hex_name.length; i++) + name += hex2ASCII(hex_name[i]); + return name; + }; + + this.text = function() { + var data = this.payload(); + data = data.slice(this.gs(true)+1, data.length); + + var text = ""; + for(var i=0; i < data.length; i++) { + text += hex2ASCII(data[i]); + } + return text; + }; + this.setName = function(name) { + var data = this.data(); + data = data.slice(0, this.HEADER_SIZE).concat( + ascii2hex(name)).concat( + data.slice(this.gs(false), data.length)); + return this.write(data, true); + }; + this.setText = function(text) { + var data = this.data().slice(0, this.gs(false)+1).concat(ascii2hex(text)); + return this.write(data,true); + }; + }; +/* + * A file in a filesystem. + * Header format for each file is the name of the + * file. Name can be any length so header is not a + * fixed size. Header and payload is separated by a + * "group separator" (hex=1D). + * + * Payload format is the ascii hex data of a file. + */ +Swap.prototype = new File(); +function Swap(TSB) { + File.call(this, TSB); +}; diff --git a/scripts/os/memoryManager.js b/scripts/os/memoryManager.js @@ -3,6 +3,9 @@ */ function MemoryManager() { this.processes = new Array(); // array of pcb's + + /* Points to the location of a swapped process. */ + this.swap = {}; /* * Loads a hex program into memory. @@ -10,17 +13,26 @@ function MemoryManager() { */ this.load = function(hex, priority) { var pid = this.getAvailablePage(); - if(pid < 0) return pid; + if(pid < 0) { + return pid; + } + var page = pid % TOTAL_PAGES; var bytes = this.hex2Dec(hex); - var base = pid * MAX_MEMORY_PROGRAM; - var limit = (pid+1) * MAX_MEMORY_PROGRAM - 1; + var base = page * MAX_MEMORY_PROGRAM; + var limit = (page+1) * MAX_MEMORY_PROGRAM - 1; var process = new pcb(pid, PCB_STATE_RESIDENT, base, limit, base, priority); - this.processes[pid] = process; + this.processes[page] = process; _Memory.allocateBytes(bytes, base); this.output(); - +/* + if(this.processes.length <= TOTAL_PAGES_AND_SWAPS) { + this.swap.pid = pid; + this.swap.tsb = krnFilesystemDriver.getAvailableBlock(); + this.rollOut(); + } +*/ return pid; } @@ -31,13 +43,31 @@ function MemoryManager() { this.getAvailablePage = function() { for(var i=0; i<TOTAL_PAGES; i++) { var p = this.processes[i]; - if(p == undefined || p.state == PCB_STATE_TERMINATED) + if(p == undefined) return i; + if(p.state == PCB_STATE_TERMINATED) + return p.PID+TOTAL_PAGES; } return -1; } /* + * Swaps an entire process by "rolling out" or transfering + * a process to swap files in the disk drive. + */ + this.rollOut = function() { + + } + + /* + * Swaps an entire process by "rolling in" or transfering + * a process to memory from swap files in the disk drive. + */ + this.rollIn = function() { + + } + + /* * Converts an array of hexadecimals to an array of decimals. */ this.hex2Dec = function(hex) { @@ -61,9 +91,9 @@ function MemoryManager() { html += '<tr>'; html += '<td>'+p.PID+'</td>'; html += '<td>'+p.state+'</td>'; - html += '<td>'+pad((p.base-(p.PID*MAX_MEMORY_PROGRAM)).toString(16),4).toUpperCase()+'</td>'; - html += '<td>'+pad((p.limit-(p.PID*MAX_MEMORY_PROGRAM)).toString(16),4).toUpperCase()+'</td>'; - html += '<td>'+pad((p.PC-(p.PID*MAX_MEMORY_PROGRAM)).toString(16),4).toUpperCase()+'</td>'; + html += '<td>'+pad((p.base-((p.PID%TOTAL_PAGES)*MAX_MEMORY_PROGRAM)).toString(16),4).toUpperCase()+'</td>'; + html += '<td>'+pad((p.limit-((p.PID%TOTAL_PAGES)*MAX_MEMORY_PROGRAM)).toString(16),4).toUpperCase()+'</td>'; + html += '<td>'+pad((p.PC-((p.PID%TOTAL_PAGES)*MAX_MEMORY_PROGRAM)).toString(16),4).toUpperCase()+'</td>'; html += '<td>'+pad(p.ACC.toString(16),2).toUpperCase()+'</td>'; html += '<td>'+pad(p.X.toString(16),2).toUpperCase()+'</td>'; html += '<td>'+pad(p.Y.toString(16),2).toUpperCase()+'</td>'; diff --git a/scripts/os/pcb.js b/scripts/os/pcb.js @@ -12,6 +12,9 @@ function pcb(pid, state, base, limit, pc, priority) { this.Y = 0; this.Z = 0; this.priority = priority; + this.arrival = undefined; + this.end = undefined; + this.bursts = 0; /* * Updates process control block values. @@ -41,6 +44,13 @@ function pcb(pid, state, base, limit, pc, priority) { this.X, this.Y, this.Z); + + if(newState == PCB_STATE_TERMINATED) + this.end = _OSclock; + + if(this.arrival == undefined && + newState == PCB_STATE_READY) + this.arrival = _OSclock; } /* diff --git a/scripts/os/scheduler.js b/scripts/os/scheduler.js @@ -15,20 +15,28 @@ function scheduler() { */ this.running = undefined; + /* + * History of all the processes that have been scheduled. + */ + this.history = []; + /* The amount of clock pulses when the CPU was executing. */ this.busyTime = 0; - /* The amount of completed processes. */ - this.completedProcesses = 0; - this.run = function() { this.busyTime++; // if ready queue is not empty, dequeue if(this.processTerminated()) { + + if(this.running != undefined) + this.addHistory(this.running); + var p = _KernelReadyQueue.pop(); - while(p.state == PCB_STATE_TERMINATED) + while(p.state == PCB_STATE_TERMINATED) { + this.addHistory(p); p = _KernelReadyQueue.pop(); + } this.setRunningProcess(p); krnTrace("Adding PID "+p.PID+" by context switch."); } @@ -96,18 +104,117 @@ function scheduler() { * CPU Throughput */ this.throughput = function() { - var n = this.completedProcesses / this.busyTime; + var n = this.history.length / this.busyTime; return Math.round(n*100); } + /* + * Avg Turnaround + * TODO + */ + this.turnAround = function() { + var total = 0; + var length = this.history.length; + for(i=0; i<length; i++) { + var p = this.history[i]; + total += (p.end - p.arrival); + } + + return total / length; + } + + /* + * Avg Wait + * TODO + */ + this.wait = function() { + var total = 0; + var length = this.history.length; + for(i=0; i<length; i++) { + var p = this.history[i]; + total += (p.end - p.arrival - p.bursts); + } + + return total / length; + } + + this.addHistory = function(p) { + this.history.push({ + pid: p.PID, + arrival: p.arrival, + end: p.end, + bursts: p.bursts + }); + } + //TODO: would prefer to measure portion of performance // instead of the whole OSclock since the OS is mostly // always idle. this.output = function() { document.getElementById("cpu_utilization").innerHTML = - this.utilization()+"%"; + "Utilization: "+this.utilization()+"%"; document.getElementById("cpu_throughput").innerHTML = - this.throughput()+"%"; + "Throughput: "+this.throughput()+"%"; + + document.getElementById("cpu_turnaround").innerHTML = + "Turn Around: "+this.turnAround(); + + document.getElementById("cpu_wait").innerHTML = + "Wait: "+this.wait(); + + var canvas = document.getElementById('cpu_utilization_bar'); + var context = canvas.getContext('2d'); + + context.fillStyle = '#8ED6FF'; + context.fillRect(0, 0, canvas.width, canvas.height); + + context.fillStyle = '#ffb78e'; + context.fillRect(0, 0, (canvas.width * this.utilization())/100, canvas.height); + + canvas = document.getElementById('cpu_throughput_bar'); + context = canvas.getContext('2d'); + + context.fillStyle = '#8ED6FF'; + context.fillRect(0, 0, canvas.width, canvas.height); + + context.fillStyle = '#ffb78e'; + context.fillRect(0, 0, (canvas.width * this.throughput())/100, canvas.height); + + canvas = document.getElementById('cpu_turnaround_graph'); + context = canvas.getContext('2d'); + + context.fillStyle = '#eee'; + context.fillRect(0, 0, canvas.width, canvas.height); + + canvas = document.getElementById('cpu_utilization_graph'); + context = canvas.getContext('2d'); + + context.fillStyle = '#eee'; + context.fillRect(0, 0, canvas.width, canvas.height); + + canvas = document.getElementById('cpu_throughput_graph'); + context = canvas.getContext('2d'); + + context.fillStyle = '#eee'; + context.fillRect(0, 0, canvas.width, canvas.height); + + canvas = document.getElementById('cpu_wait_graph'); + context = canvas.getContext('2d'); + + context.fillStyle = '#eee'; + context.fillRect(0, 0, canvas.width, canvas.height); + + canvas = document.getElementById('cpu_turnaround_graph'); + context = canvas.getContext('2d'); + + context.fillStyle = '#eee'; + context.fillRect(0, 0, canvas.width, canvas.height); + + canvas = document.getElementById('cpu_wait_graph'); + context = canvas.getContext('2d'); + + context.fillStyle = '#eee'; + context.fillRect(0, 0, canvas.width, canvas.height); } } diff --git a/scripts/os/shell.js b/scripts/os/shell.js @@ -159,11 +159,25 @@ function shellInit() sc.function = shellKill; this.commandList[this.commandList.length] = sc; - // create + // change directory + sc = new ShellCommand(); + sc.command = "cd"; + sc.description = "- Changes the current working directory."; + sc.function = shellCD; + this.commandList[this.commandList.length] = sc; + + // create directory + sc = new ShellCommand(); + sc.command = "mkdir"; + sc.description = "- Creates a new directory in the current directory."; + sc.function = shellCreateDirectory; + this.commandList[this.commandList.length] = sc; + + // create text file sc = new ShellCommand(); sc.command = "create"; - sc.description = "- Creates a new file in the filesystem."; - sc.function = shellCreate; + sc.description = "- Creates a new text file in the current directory."; + sc.function = shellCreateFile; this.commandList[this.commandList.length] = sc; // read @@ -690,50 +704,138 @@ function shellKill(args) { } /* - * Creates a new file. + * Changes the current working directory. */ -function shellCreate(args) { - var filename = args[0]; - // TODO +function shellCD(args) { + var fn = args[0]; + + if(fn == undefined) { + _StdOut.putText("Usage: cd <directory name>"); + return; + } + + if(!krnFilesystemDriver.changeDirectory(fn)) + _StdOut.putText(fn+" is not a directory"); +} + +/* + * Creates a new directory. + */ +function shellCreateDirectory(args) { + var fn = args[0]; + + if(fn == undefined) { + _StdOut.putText("Usage: mkdir <file name>"); + return; + } + + if(krnFilesystemDriver.createDirectory(fn)) + _StdOut.putText(fn+" created"); + else + _StdOut.putText(fn+" not created"); +} + +/* + * Creates a new text file. + */ +function shellCreateFile(args) { + var fn = args[0]; + + if(fn == undefined) { + _StdOut.putText("Usage: create <file name>"); + return; + } + + if(krnFilesystemDriver.createTextFile(fn)) + _StdOut.putText(fn+" created"); + else + _StdOut.putText(fn+" not created"); } /* * Reads a file. */ function shellRead(args) { - var filename = args[0]; - // TODO + var fn = args[0]; + + if(fn == undefined) { + _StdOut.putText("Usage: read <file name>"); + return; + } + + var text = krnFilesystemDriver.readTextFile(fn); + if(text == undefined) + _StdOut.putText(fn+" is not a text file."); + else { + _StdOut.putText(text); + } } /* * Writes data to a file. */ function shellWrite(args) { - var filename = args[0]; + var fn = args[0]; var data = args[1]; - // TODO + for(var i=2; i<args.length; i++) + data += " "+args[i]; + + if(fn == undefined || data == undefined) { + _StdOut.putText("Usage: write <file name> <data>"); + return; + } + + if(krnFilesystemDriver.writeTextFile(fn,data)) + _StdOut.putText(fn+" modified"); + else + _StdOut.putText(fn+" not modified"); } /* * Deletes a file. */ function shellDelete(args) { - var filename = args[0]; - // TODO + var fn = args[0]; + + if(fn == undefined) { + _StdOut.putText("Usage: delete <file name>"); + return; + } + + if(krnFilesystemDriver.deleteFile(fn)) + _StdOut.putText(fn+" deleted"); + else + _StdOut.putText(fn+" not deleted"); } /* * Formats the entire filesystem. */ function shellFormat(args) { - // TODO + if(krnFilesystemDriver.format()) + _StdOut.putText("Format succeeded"); + else + _StdOut.putText("Format FAILED"); } /* * Lists the files currently stored on disk. */ function shellLs(args) { - // TODO + var files = krnFilesystemDriver.ls(); + for(var i=0; i < files.length; i++) { + var file = files[i]; + var name = file.name(); + var type = file.type(); + var type_dir = file.TYPE_DIR; + + if(type == type_dir) + _StdOut.putText("d - "+name); + else + _StdOut.putText("t - "+name); + + _Console.advanceLine(); + } } /* diff --git a/scripts/utils.js b/scripts/utils.js @@ -47,6 +47,20 @@ function isValidHex(str) { } /* + * Matches hex byte. ie. FF or 8D etc + */ +function isValidHexByte(str) { + if(str.length > 2 || str.length < 1) return false; + for (var i in str) { + var ch = str[i]; + if ("abcedfABCDEF0123456789".indexOf(ch) < 0) { + return false; + } + } + return true; +} + +/* * Padding string hex values * ie. If size is 4 and hex string is 'a8' * result will be '00a8'. @@ -87,6 +101,29 @@ function hex2ASCII(num) { } /* + * Converts ASCII character to a number. + */ +function ascii2hex(pStr) { + var tempstr = []; + for (a=0; a<pStr.length; a++) { + tempstr[a] = pStr.charCodeAt(a).toString(16); + } + return tempstr; +} + +/* + * Converts a string to array. Each array element is + * a string character. + */ +function string2Array(string) { + string = string.toString(); + var arr = []; + for(i=0; i<string.length; i++) + arr[i] = string.substring(i,i+1); + return arr; +} + +/* * Checks if the value is an integer. */ function isInt(value){ diff --git a/styles/julios-tabs.css b/styles/julios-tabs.css @@ -0,0 +1,53 @@ +.tabs{ + height: 30px; +} + +.tabs > ul{ + font-size: 1em; + list-style:none; +} + +.tabs > ul > li{ + margin:0 2px 0 0; + padding:7px 10px; + display:block; + float:left; + cursor: default; + -ms-user-select: none; + -o-user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + border-radius:4px 4px 0 0; + -moz-border-radius:4px 4px 0 0; + -webkit-border-radius:4px 4px 0 0; + -o-border-radius:4px 4px 0 0; + -ms-moz-border-radius:4px 4px 0 0; + background-image: -moz-radial-gradient(50% 10%, circle, #ffffff 0%, #c1c1c1 30px); + background-image: -webkit-radial-gradient(50% 10%, circle, #ffffff 0%, #c1c1c1 30px); + background-image: -o-radial-gradient(50% 10%, circle, #ffffff 0%, #c1c1c1 30px); + background-image: -ms-radial-gradient(50% 10%, circle, #ffffff 0%, #c1c1c1 30px); +} + +.tabs > ul > li:hover{ + background-image: -moz-radial-gradient(50% 10%, circle, #fcfcfc 0%, #b9b9b9 30px); + background-image: -webkit-radial-gradient(50% 10%, circle, #fcfcfc 0%, #b9b9b9 30px); + background-image: -o-radial-gradient(50% 10%, circle, #fcfcfc 0%, #b9b9b9 30px); + background-image: -ms-radial-gradient(50% 10%, circle, #fcfcfc 0%, #b9b9b9 30px); +} + +.tabs > ul > li.tabActiveHeader{ + background: #fff; + background: -moz-linear-gradient(top, #FFFFFF 0%, #F3F3F3 10%, #F3F3F3 50%, #FFFFFF 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#FFFFFF), color-stop(10%,#F3F3F3), color-stop(50%,#F3F3F3), color-stop(100%,#FFFFFF)); + background: -o-gradient(linear, left top, left bottom, color-stop(0%,#FFFFFF), color-stop(10%,#F3F3F3), color-stop(50%,#F3F3F3), color-stop(100%,#FFFFFF)); + background: -ms-gradient(linear, left top, left bottom, color-stop(0%,#FFFFFF), color-stop(10%,#F3F3F3), color-stop(50%,#F3F3F3), color-stop(100%,#FFFFFF)); +} + +.tabpage { + padding-top: 15px; +} + +.tabpage > div { + margin-top: 20px; +} diff --git a/styles/julios.css b/styles/julios.css @@ -19,30 +19,23 @@ body { font-size: 10pt; } -#taProgramInput { - height: 100px; - width: 99%; -} - -#taLog { - height: 150px; - width: 100%; +#tabContainer { + margin-top: 20px; } -#memoryDump { +.tabpage { overflow-y:auto; - height:150px; + height:570px; } -#processes { - overflow-y:auto; - overflow-x:hidden; - height:100px; -} +.tabpage > textarea { + height:550px; + width: 98%; +} #buttons { text-align: center; - margin-top: 20px; + margin: 20px 0; } .footer {