mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-11 23:34:04 +09:00
moving everything neatly into the assets directory
This commit is contained in:
141
assets/disk0/tvdos/TVDOS.SYS
Normal file
141
assets/disk0/tvdos/TVDOS.SYS
Normal file
@@ -0,0 +1,141 @@
|
||||
// define TVDOS
|
||||
var _TVDOS = {};
|
||||
_TVDOS.VERSION = "1.0";
|
||||
_TVDOS.DRIVES = {}; // Object where key-value pair is <drive-letter> : [serial-port, drive-number]
|
||||
// actually figure out the drive letter association
|
||||
// Drive A is always the device we're currently on
|
||||
_TVDOS.DRIVES["A"] = _BIOS.FIRST_BOOTABLE_PORT;
|
||||
//TODO
|
||||
|
||||
|
||||
_TVDOS.getPath = function() {
|
||||
return _TVDOS.variables.PATH.split(';');
|
||||
};
|
||||
// initial values
|
||||
_TVDOS.variables = {
|
||||
DOSDIR: "\\tvdos",
|
||||
LANG: "EN",
|
||||
PATH: ";\\tvdos\\bin;\\tbas;\\home",
|
||||
PATHEXT: ".com;.bat;.js",
|
||||
HELPPATH: "\\tvdos\\help",
|
||||
OS_NAME: "Terrarum Virtual DOS",
|
||||
OS_VERSION: _TVDOS.VERSION
|
||||
};
|
||||
Object.freeze(_TVDOS);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var filesystem = {};
|
||||
filesystem._toPorts = (driveLetter) => {
|
||||
if (driveLetter.toUpperCase === undefined) {
|
||||
throw Error("'"+driveLetter+"' (type: "+typeof driveLetter+") is not a valid drive letter");
|
||||
}
|
||||
var port = _TVDOS.DRIVES[driveLetter.toUpperCase()];
|
||||
if (port === undefined) {
|
||||
throw Error("Drive letter '" + driveLetter.toUpperCase() + "' does not exist");
|
||||
}
|
||||
return port
|
||||
};
|
||||
filesystem._close = (portNo) => {
|
||||
com.sendMessage(portNo, "CLOSE");
|
||||
};
|
||||
filesystem._flush = (portNo) => {
|
||||
com.sendMessage(portNo, "FLUSH");
|
||||
};
|
||||
// @return true if operation committed successfully, false if:
|
||||
// - opening file with R-mode and target file does not exists
|
||||
// throws if:
|
||||
// - java.lang.NullPointerException if path is null
|
||||
// - Error if operation mode is not "R", "W" nor "A"
|
||||
filesystem.open = (driveLetter, path, operationMode) => {
|
||||
var port = filesystem._toPorts(driveLetter);
|
||||
|
||||
filesystem._flush(port[0]); filesystem._close(port[0]);
|
||||
|
||||
var mode = operationMode.toUpperCase();
|
||||
if (mode != "R" && mode != "W" && mode != "A") {
|
||||
throw Error("Unknown file opening mode: " + mode);
|
||||
}
|
||||
|
||||
com.sendMessage(port[0], "OPEN"+mode+'"'+path+'",'+port[1]);
|
||||
var response = com.getStatusCode(port[0]);
|
||||
return (response == 0);
|
||||
};
|
||||
// @return the entire contents of the file in String
|
||||
filesystem.readAll = (driveLetter) => {
|
||||
var port = filesystem._toPorts(driveLetter);
|
||||
com.sendMessage(port[0], "READ");
|
||||
var response = com.getStatusCode(port[0]);
|
||||
if (135 == response) {
|
||||
throw Error("File not opened");
|
||||
}
|
||||
if (response < 0 || response >= 128) {
|
||||
throw Error("Reading a file failed with "+response);
|
||||
}
|
||||
return com.pullMessage(port[0]);
|
||||
};
|
||||
filesystem.write = (driveLetter, string) => {
|
||||
var port = filesystem._toPorts(driveLetter);
|
||||
com.sendMessage(port[0], "WRITE"+string.length);
|
||||
var response = com.getStatusCode(port[0]);
|
||||
if (135 == response) {
|
||||
throw Error("File not opened");
|
||||
}
|
||||
if (response < 0 || response >= 128) {
|
||||
throw Error("Writing a file failed with "+response);
|
||||
}
|
||||
com.sendMessage(port[0], string);
|
||||
filesystem._flush(port[0]); filesystem._close(port[0]);
|
||||
};
|
||||
filesystem.isDirectory = (driveLetter) => {
|
||||
var port = filesystem._toPorts(driveLetter);
|
||||
com.sendMessage(port[0], "LISTFILES");
|
||||
var response = com.getStatusCode(port[0]);
|
||||
return (response === 0);
|
||||
};
|
||||
filesystem.mkDir = (driveLetter) => {
|
||||
var port = filesystem._toPorts(driveLetter);
|
||||
com.sendMessage(port[0], "MKDIR");
|
||||
var response = com.getStatusCode(port[0]);
|
||||
|
||||
if (response < 0 || response >= 128) {
|
||||
var status = com.getDeviceStatus(port[0]);
|
||||
throw Error("Creating a directory failed with ("+response+"): "+status.message+"\n");
|
||||
}
|
||||
return (response === 0); // possible status codes: 0 (success), 1 (fail)
|
||||
};
|
||||
filesystem.touch = (driveLetter) => {
|
||||
var port = filesystem._toPorts(driveLetter);
|
||||
com.sendMessage(port[0], "TOUCH");
|
||||
var response = com.getStatusCode(port[0]);
|
||||
return (response === 0);
|
||||
};
|
||||
filesystem.mkFile = (driveLetter) => {
|
||||
var port = filesystem._toPorts(driveLetter);
|
||||
com.sendMessage(port[0], "MKFILE");
|
||||
var response = com.getStatusCode(port[0]);
|
||||
return (response === 0);
|
||||
};
|
||||
Object.freeze(filesystem);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// install other stuffs
|
||||
filesystem.open("A", "tvdos/gl.js", "R");
|
||||
var GL = eval(filesystem.readAll("A"));
|
||||
|
||||
// @param cmdsrc JS source code
|
||||
// @param args arguments for the program, must be Array, and args[0] is always the name of the program, e.g.
|
||||
// for command line 'echo foo bar', args[0] must be 'echo'
|
||||
var execApp = (cmdsrc, args) => {
|
||||
var execAppPrg = eval("var _appStub=function(exec_args){"+cmdsrc+"};_appStub;"); // making 'exec_args' a app-level global
|
||||
return execAppPrg(args);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Boot script
|
||||
serial.println("TVDOS.SYS initialised, running boot script...");
|
||||
var _G = {};
|
||||
filesystem.open("A", "tvdos/bin/command.js", "R");
|
||||
execApp(filesystem.readAll("A"), ["", "/c", "\\AUTOEXEC.BAT"]);
|
||||
3
assets/disk0/tvdos/bin/basica.js
Normal file
3
assets/disk0/tvdos/bin/basica.js
Normal file
@@ -0,0 +1,3 @@
|
||||
// load a BASIC rom
|
||||
sys.mapRom(1);
|
||||
execApp(sys.romReadAll());
|
||||
454
assets/disk0/tvdos/bin/command.js
Normal file
454
assets/disk0/tvdos/bin/command.js
Normal file
@@ -0,0 +1,454 @@
|
||||
let PROMPT_TEXT = ">";
|
||||
let CURRENT_DRIVE = "A";
|
||||
let shell_pwd = [""];
|
||||
|
||||
let goInteractive = false;
|
||||
let goFancy = false;
|
||||
|
||||
let DEBUG_PRINT = true;
|
||||
|
||||
let errorlevel = 0;
|
||||
|
||||
const welcome_text = "TSVM Disk Operating System, version " + _TVDOS.VERSION;
|
||||
|
||||
function print_prompt_text() {
|
||||
if (goFancy) {
|
||||
con.color_pair(239,161);
|
||||
print(" "+CURRENT_DRIVE+":");
|
||||
con.color_pair(161,253);
|
||||
con.addch(16);
|
||||
con.color_pair(0,253);
|
||||
print(" \\"+shell_pwd.join("\\").substring(1)+" ");
|
||||
if (errorlevel != 0) {
|
||||
con.color_pair(166,253);
|
||||
print("["+errorlevel+"] ");
|
||||
}
|
||||
con.color_pair(253,255);
|
||||
con.addch(16);
|
||||
con.addch(32);
|
||||
con.color_pair(239,255);
|
||||
}
|
||||
else {
|
||||
if (errorlevel != 0)
|
||||
print(CURRENT_DRIVE + ":\\" + shell_pwd.join("\\") + " [" + errorlevel + "]" + PROMPT_TEXT);
|
||||
else
|
||||
print(CURRENT_DRIVE + ":\\" + shell_pwd.join("\\") + PROMPT_TEXT);
|
||||
}
|
||||
}
|
||||
|
||||
function greet() {
|
||||
if (goFancy) {
|
||||
con.color_pair(0,253);
|
||||
//print(welcome_text + " ".repeat(_fsh.scrwidth - welcome_text.length));
|
||||
print(welcome_text + " ".repeat(80 - welcome_text.length));
|
||||
con.color_pair(239,255);
|
||||
println();
|
||||
}
|
||||
else
|
||||
println(welcome_text);
|
||||
}
|
||||
|
||||
function trimStartRevSlash(s) {
|
||||
var cnt = 0;
|
||||
while (cnt < s.length) {
|
||||
var chr = s[cnt];
|
||||
|
||||
if (chr != '\\') break;
|
||||
|
||||
cnt += 1;
|
||||
}
|
||||
|
||||
return s.substring(cnt);
|
||||
}
|
||||
|
||||
let shell = {};
|
||||
shell.getPwd = function() { return shell_pwd; }
|
||||
shell.getCurrentDrive = function() { return CURRENT_DRIVE; }
|
||||
// example input: echo "the string" > subdir\test.txt
|
||||
shell.parse = function(input) {
|
||||
var tokens = [];
|
||||
var stringBuffer = "";
|
||||
var mode = "LITERAL"; // LITERAL, QUOTE, ESCAPE, LIMBO
|
||||
var i = 0
|
||||
while (i < input.length) {
|
||||
const c = input[i];
|
||||
/*digraph g {
|
||||
LITERAL -> QUOTE [label="\""]
|
||||
LITERAL -> LIMBO [label="space"]
|
||||
LITERAL -> LITERAL [label=else]
|
||||
|
||||
QUOTE -> LIMBO [label="\""]
|
||||
QUOTE -> ESCAPE [label="\\"]
|
||||
QUOTE -> QUOTE [label=else]
|
||||
|
||||
ESCAPE -> QUOTE
|
||||
|
||||
LIMBO -> LITERAL [label="not space"]
|
||||
LIMBO -> QUOTE [label="\""]
|
||||
LIMBO -> LIMBO [label="space"]
|
||||
}*/
|
||||
if ("LITERAL" == mode) {
|
||||
if (' ' == c) {
|
||||
tokens.push(stringBuffer); stringBuffer = "";
|
||||
mode = "LIMBO";
|
||||
}
|
||||
else if ('"' == c) {
|
||||
tokens.push(stringBuffer); stringBuffer = "";
|
||||
mode = "QUOTE";
|
||||
}
|
||||
else {
|
||||
stringBuffer += c;
|
||||
}
|
||||
}
|
||||
else if ("LIMBO" == mode) {
|
||||
if ('"' == c) {
|
||||
mode = "QUOTE";
|
||||
}
|
||||
else if (c != ' ') {
|
||||
mode = "LITERAL";
|
||||
stringBuffer += c;
|
||||
}
|
||||
}
|
||||
else if ("QUOTE" == mode) {
|
||||
if ('"' == c) {
|
||||
tokens.push(stringBuffer); stringBuffer = "";
|
||||
mode = "LIMBO";
|
||||
}
|
||||
else if ('^' == c) {
|
||||
mode = "ESCAPE";
|
||||
}
|
||||
else {
|
||||
stringBuffer += c;
|
||||
}
|
||||
}
|
||||
else if ("ESCAPE" == mode) {
|
||||
TODO();
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (stringBuffer.length > 0) {
|
||||
tokens.push(stringBuffer);
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
shell.resolvePathInput = function(input) {
|
||||
// replace slashes into revslashes
|
||||
var pathstr = input.replaceAll('/','\\\\');
|
||||
var startsWithSlash = input.startsWith('\\');
|
||||
var newPwd = [];
|
||||
|
||||
// split them into an array while filtering empty elements except for the root 'head'
|
||||
var ipwd = (startsWithSlash ? [""] : shell_pwd).concat(pathstr.split("\\").filter(function(it) { return (it.length > 0); }));
|
||||
|
||||
serial.println("command.js > resolvePathInput > ipwd = "+ipwd);
|
||||
serial.println("command.js > resolvePathInput > newPwd = "+newPwd);
|
||||
|
||||
// process dots
|
||||
ipwd.forEach(function(it) {
|
||||
serial.println("command.js > resolvePathInput > ipwd.forEach > it = "+it);
|
||||
if (it === ".." && newPwd[1] !== undefined) {
|
||||
newPwd.pop();
|
||||
}
|
||||
else if (it !== ".." && it !== ".") {
|
||||
newPwd.push(it);
|
||||
}
|
||||
serial.println("command.js > resolvePathInput > newPwd = "+newPwd);
|
||||
});
|
||||
|
||||
// construct new pathstr from pwd arr so it will be sanitised
|
||||
pathstr = newPwd.join('\\').substring(1);
|
||||
|
||||
return { string: pathstr, pwd: newPwd };
|
||||
}
|
||||
shell.coreutils = {
|
||||
/* Args follow this format:
|
||||
* <command-name> <1st arg> <2nd arg> ...
|
||||
* NOTE:
|
||||
* even if there's no 1st arg, length of args may not be 1, therefore don't:
|
||||
* if (args.length < 2)
|
||||
* but do instead:
|
||||
* if (args[1] === undefined)
|
||||
*/
|
||||
cd: function(args) {
|
||||
if (args[1] === undefined) {
|
||||
println(CURRENT_DRIVE+":"+shell_pwd.join("\\"));
|
||||
return
|
||||
}
|
||||
var path = shell.resolvePathInput(args[1])
|
||||
if (DEBUG_PRINT) serial.println("command.js > cd > pathstr = "+path.string);
|
||||
|
||||
// check if path is valid
|
||||
filesystem.open(CURRENT_DRIVE, path.string, 'R');
|
||||
var dirOpened = filesystem.isDirectory(CURRENT_DRIVE); // open a dir; if path is nonexistent, file won't actually be opened
|
||||
if (!dirOpened) { printerrln("CHDIR failed for '"+path.string+"'"); return; } // if file is not opened, FALSE will be returned
|
||||
|
||||
shell_pwd = path.pwd;
|
||||
},
|
||||
mkdir: function(args) {
|
||||
if (args[1] === undefined) {
|
||||
printerrln("Syntax error");
|
||||
return
|
||||
}
|
||||
var path = shell.resolvePathInput(args[1])
|
||||
if (DEBUG_PRINT) serial.println("command.js > mkdir > pathstr = "+path.string);
|
||||
|
||||
// check if path is valid
|
||||
var dirOpened = filesystem.open(CURRENT_DRIVE, path.string, 'W');
|
||||
var mkdird = filesystem.mkDir(CURRENT_DRIVE);
|
||||
if (!mkdird) { printerrln("MKDIR failed for '"+path.string+"'"); return; }
|
||||
},
|
||||
cls: function(args) {
|
||||
con.clear();
|
||||
},
|
||||
exit: function(args) {
|
||||
cmdExit = true;
|
||||
},
|
||||
ver: function(args) {
|
||||
println(welcome_text);
|
||||
},
|
||||
echo: function(args) {
|
||||
if (args[1] !== undefined) {
|
||||
args.forEach(function(it,i) { if (i > 0) print(it+" ") });
|
||||
}
|
||||
println();
|
||||
},
|
||||
rem: function(args) {
|
||||
return 0;
|
||||
},
|
||||
set: function(args) {
|
||||
// print all the env vars
|
||||
if (args[1] === undefined) {
|
||||
Object.entries(_TVDOS.variables).forEach(function(a) { println(a[0]+"="+a[1]); })
|
||||
}
|
||||
else {
|
||||
// parse key-value pair with splitter '='
|
||||
var key = undefined; var value = undefined;
|
||||
// if syntax "<key> = <value>" is used?
|
||||
if ('=' == args[2]) {
|
||||
key = args[1].toUpperCase(); value = args[3];
|
||||
}
|
||||
else if (args[2] === undefined) {
|
||||
var pair = args[1].split('=');
|
||||
key = pair[0].toUpperCase(); value = pair[1];
|
||||
}
|
||||
|
||||
if (key == undefined) throw SyntaxError("Input format must be 'key=value'");
|
||||
|
||||
// if value is undefined, show what envvar[key] has
|
||||
if (value === undefined) {
|
||||
if (_TVDOS.variables[key] === undefined)
|
||||
println("Environment variable '"+key+"' not found");
|
||||
else
|
||||
println(_TVDOS.variables[key])
|
||||
}
|
||||
else {
|
||||
// TODO parse %var_name% line
|
||||
_TVDOS.variables[key] = value;
|
||||
}
|
||||
}
|
||||
},
|
||||
dir: function(args) {
|
||||
var pathstr = (args[1] !== undefined) ? args[1] : "\\"+shell_pwd.join("\\");
|
||||
|
||||
// check if path is valid
|
||||
var pathOpened = filesystem.open(CURRENT_DRIVE, pathstr, 'R');
|
||||
if (!pathOpened) { printerrln("File not found"); return; }
|
||||
|
||||
var port = filesystem._toPorts(CURRENT_DRIVE)[0]
|
||||
com.sendMessage(port, "LIST");
|
||||
println(com.pullMessage(port));
|
||||
}
|
||||
};
|
||||
shell.coreutils.chdir = shell.coreutils.cd;
|
||||
Object.freeze(shell.coreutils);
|
||||
shell.execute = function(line) {
|
||||
if (0 == line.size) return;
|
||||
var tokens = shell.parse(line);
|
||||
var cmd = tokens[0];
|
||||
if (cmd === undefined || cmd === '') return 0;
|
||||
|
||||
// handle Ctrl-C
|
||||
if (con.hitterminate()) return 1;
|
||||
|
||||
if (shell.coreutils[cmd.toLowerCase()] !== undefined) {
|
||||
var retval = shell.coreutils[cmd.toLowerCase()](tokens);
|
||||
return retval|0; // return value of undefined will cast into 0
|
||||
}
|
||||
else {
|
||||
// search through PATH for execution
|
||||
|
||||
var fileExists = false;
|
||||
var searchDir = (cmd.startsWith("\\")) ? [""] : ["\\"+shell_pwd.join("\\")].concat(_TVDOS.getPath());
|
||||
|
||||
var pathExt = []; // it seems Nashorn does not like 'let' too much? this line gets ignored sometimes
|
||||
// fill pathExt using %PATHEXT% but also capitalise them
|
||||
if (cmd.split(".")[1] === undefined)
|
||||
_TVDOS.variables.PATHEXT.split(';').forEach(function(it) { pathExt.push(it); pathExt.push(it.toUpperCase()); });
|
||||
else
|
||||
pathExt.push(""); // final empty extension
|
||||
|
||||
searchLoop:
|
||||
for (var i = 0; i < searchDir.length; i++) {
|
||||
for (var j = 0; j < pathExt.length; j++) {
|
||||
var search = searchDir[i]; if (!search.endsWith('\\')) search += '\\';
|
||||
var path = trimStartRevSlash(search + cmd + pathExt[j]);
|
||||
|
||||
if (DEBUG_PRINT) {
|
||||
serial.println("[command.js > shell.execute] file search path: "+path);
|
||||
}
|
||||
|
||||
if (filesystem.open(CURRENT_DRIVE, path, "R")) {
|
||||
fileExists = true;
|
||||
break searchLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fileExists) {
|
||||
printerrln('Bad command or filename: "'+cmd+'"');
|
||||
return 127;
|
||||
}
|
||||
else {
|
||||
var programCode = filesystem.readAll(CURRENT_DRIVE);
|
||||
var extension = undefined;
|
||||
// get proper extension
|
||||
var dotSepTokens = cmd.split('.');
|
||||
if (dotSepTokens.length > 1) extension = dotSepTokens[dotSepTokens.length - 1].toUpperCase();
|
||||
|
||||
if ("BAT" == extension) {
|
||||
// parse and run as batch file
|
||||
var lines = programCode.split('\n').filter(function(it) { return it.length > 0; });
|
||||
lines.forEach(function(line) {
|
||||
shell.execute(line);
|
||||
});
|
||||
}
|
||||
else {
|
||||
return execApp(programCode, tokens)|0; // return value of undefined will cast into 0
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Object.freeze(shell);
|
||||
_G.shell = shell;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if (exec_args[1] !== undefined) {
|
||||
// only meaningful switches would be either /c or /k anyway
|
||||
var firstSwitch = exec_args[1].toLowerCase();
|
||||
|
||||
// command /c <commands>
|
||||
// ^[0] ^[1] ^[2]
|
||||
if ("/c" == firstSwitch) {
|
||||
if ("" == exec_args[2]) return 0; // no commands were given, just exit successfully
|
||||
return shell.execute(exec_args[2]);
|
||||
}
|
||||
else if ("/k" == firstSwitch) {
|
||||
if ("" == exec_args[2]) return 0; // no commands were given, just exit successfully
|
||||
shell.execute(exec_args[2]);
|
||||
goInteractive = true;
|
||||
}
|
||||
else if ("/fancy" == firstSwitch) {
|
||||
goFancy = true;
|
||||
goInteractive = true;
|
||||
}
|
||||
else {
|
||||
printerrln("Invalid switch: "+exec_args[1]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
goInteractive = true;
|
||||
}
|
||||
|
||||
let cmdExit = false;
|
||||
if (goInteractive) {
|
||||
con.reset_graphics();
|
||||
greet();
|
||||
|
||||
let cmdHistory = []; // zeroth element is the oldest
|
||||
let cmdHistoryScroll = 0; // 0 for outside-of-buffer, 1 for most recent
|
||||
while (!cmdExit) {
|
||||
con.reset_graphics();
|
||||
print_prompt_text();
|
||||
|
||||
var cmdbuf = "";
|
||||
|
||||
while (true) {
|
||||
var key = con.getch();
|
||||
|
||||
// printable chars
|
||||
if (key >= 32 && key <= 126) {
|
||||
var s = String.fromCharCode(key);
|
||||
cmdbuf += s;
|
||||
print(s);
|
||||
}
|
||||
// backspace
|
||||
else if (key === 8 && cmdbuf.length > 0) {
|
||||
cmdbuf = cmdbuf.substring(0, cmdbuf.length - 1);
|
||||
print(String.fromCharCode(key));
|
||||
}
|
||||
// enter
|
||||
else if (key === 10 || key === 13) {
|
||||
println();
|
||||
try {
|
||||
errorlevel = 0; // reset the number
|
||||
errorlevel = shell.execute(cmdbuf);
|
||||
if (isNaN(errorlevel)) errorlevel = 2;
|
||||
}
|
||||
catch (e) {
|
||||
printerrln("\n"+e);
|
||||
if (errorlevel === 0 || isNaN(errorlevel)) {
|
||||
errorlevel = 1; // generic failure
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (cmdbuf.trim().length > 0)
|
||||
cmdHistory.push(cmdbuf);
|
||||
|
||||
cmdHistoryScroll = 0;
|
||||
con.curs_set(1);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
// up arrow
|
||||
else if (key === 19 && cmdHistory.length > 0 && cmdHistoryScroll < cmdHistory.length) {
|
||||
cmdHistoryScroll += 1;
|
||||
|
||||
// back the cursor in order to type new cmd
|
||||
var x = 0;
|
||||
for (x = 0; x < cmdbuf.length; x++) print(String.fromCharCode(8));
|
||||
cmdbuf = cmdHistory[cmdHistory.length - cmdHistoryScroll];
|
||||
// re-type the new command
|
||||
print(cmdbuf);
|
||||
|
||||
}
|
||||
// down arrow
|
||||
else if (key === 20) {
|
||||
if (cmdHistoryScroll > 0) {
|
||||
// back the cursor in order to type new cmd
|
||||
var x = 0;
|
||||
for (x = 0; x < cmdbuf.length; x++) print(String.fromCharCode(8));
|
||||
cmdbuf = cmdHistory[cmdHistory.length - cmdHistoryScroll];
|
||||
// re-type the new command
|
||||
print(cmdbuf);
|
||||
|
||||
cmdHistoryScroll -= 1;
|
||||
}
|
||||
else {
|
||||
// back the cursor in order to type new cmd
|
||||
var x = 0;
|
||||
for (x = 0; x < cmdbuf.length; x++) print(String.fromCharCode(8));
|
||||
cmdbuf = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
1
assets/disk0/tvdos/bin/false.js
Normal file
1
assets/disk0/tvdos/bin/false.js
Normal file
@@ -0,0 +1 @@
|
||||
return 1;
|
||||
106
assets/disk0/tvdos/bin/flsh.js
Normal file
106
assets/disk0/tvdos/bin/flsh.js
Normal file
@@ -0,0 +1,106 @@
|
||||
let CURRENT_DRIVE = "A";
|
||||
|
||||
let shell_pwd = [""];
|
||||
|
||||
const welcome_text = "TSVM Disk Operating System, version " + _TVDOS.VERSION;
|
||||
|
||||
function print_prompt_text() {
|
||||
// oh-my-zsh-like prompt
|
||||
con.color_pair(239,161);
|
||||
print(" "+CURRENT_DRIVE+":");
|
||||
con.color_pair(161,253);
|
||||
con.addch(16);
|
||||
con.color_pair(0,253);
|
||||
print(" \\"+shell_pwd.join("\\")+" ");
|
||||
con.color_pair(253,255);
|
||||
con.addch(16);
|
||||
con.addch(32);
|
||||
con.color_pair(239,255);
|
||||
}
|
||||
|
||||
function greet() {
|
||||
con.color_pair(0,253);
|
||||
//print(welcome_text + " ".repeat(_fsh.scrwidth - welcome_text.length));
|
||||
print(welcome_text + " ".repeat(80 - welcome_text.length));
|
||||
con.color_pair(239,255);
|
||||
println();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
con.clear();
|
||||
|
||||
greet();
|
||||
|
||||
let cmdHistory = []; // zeroth element is the oldest
|
||||
let cmdHistoryScroll = 0; // 0 for outside-of-buffer, 1 for most recent
|
||||
while (true) {
|
||||
print_prompt_text();
|
||||
|
||||
let cmdbuf = "";
|
||||
|
||||
while (true) {
|
||||
let key = con.getch();
|
||||
|
||||
// printable chars
|
||||
if (key >= 32 && key <= 126) {
|
||||
let s = String.fromCharCode(key);
|
||||
cmdbuf += s;
|
||||
print(s);
|
||||
}
|
||||
// backspace
|
||||
else if (key === 8 && cmdbuf.length > 0) {
|
||||
cmdbuf = cmdbuf.substring(0, cmdbuf.length - 1);
|
||||
print(String.fromCharCode(key));
|
||||
}
|
||||
// enter
|
||||
else if (key === 10 || key === 13) {
|
||||
println();
|
||||
try {
|
||||
println("You entered: " + cmdbuf);
|
||||
}
|
||||
catch (e) {
|
||||
println(e);
|
||||
}
|
||||
finally {
|
||||
if (cmdbuf.trim().length > 0)
|
||||
cmdHistory.push(cmdbuf);
|
||||
|
||||
cmdHistoryScroll = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// up arrow
|
||||
else if (key === 19 && cmdHistory.length > 0 && cmdHistoryScroll < cmdHistory.length) {
|
||||
cmdHistoryScroll += 1;
|
||||
|
||||
// back the cursor in order to type new cmd
|
||||
let x = 0;
|
||||
for (x = 0; x < cmdbuf.length; x++) print(String.fromCharCode(8));
|
||||
cmdbuf = cmdHistory[cmdHistory.length - cmdHistoryScroll];
|
||||
// re-type the new command
|
||||
print(cmdbuf);
|
||||
|
||||
}
|
||||
// down arrow
|
||||
else if (key === 20) {
|
||||
if (cmdHistoryScroll > 0) {
|
||||
// back the cursor in order to type new cmd
|
||||
let x = 0;
|
||||
for (x = 0; x < cmdbuf.length; x++) print(String.fromCharCode(8));
|
||||
cmdbuf = cmdHistory[cmdHistory.length - cmdHistoryScroll];
|
||||
// re-type the new command
|
||||
print(cmdbuf);
|
||||
|
||||
cmdHistoryScroll -= 1;
|
||||
}
|
||||
else {
|
||||
// back the cursor in order to type new cmd
|
||||
let x = 0;
|
||||
for (x = 0; x < cmdbuf.length; x++) print(String.fromCharCode(8));
|
||||
cmdbuf = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
131
assets/disk0/tvdos/bin/fsh.js
Normal file
131
assets/disk0/tvdos/bin/fsh.js
Normal file
@@ -0,0 +1,131 @@
|
||||
graphics.setBackground(3,3,3);
|
||||
graphics.resetPalette();
|
||||
|
||||
function captureUserInput() {
|
||||
sys.poke(-40, 1);
|
||||
}
|
||||
|
||||
function getKeyPushed(keyOrder) {
|
||||
return sys.peek(-41 - keyOrder);
|
||||
}
|
||||
|
||||
var _fsh = {};
|
||||
_fsh.titlebarTex = new GL.Texture(2, 14, base64.atob("/u/+/v3+/f39/f39/f39/f39/P39/Pz8/Pv7+w=="));
|
||||
_fsh.scrdim = con.getmaxyx();
|
||||
_fsh.scrwidth = _fsh.scrdim[1];
|
||||
_fsh.scrheight = _fsh.scrdim[0];
|
||||
_fsh.brandName = "f\xb3Sh";
|
||||
_fsh.brandLogoTexSmall = new GL.Texture(24, 14, gzip.decomp(base64.atob(
|
||||
"H4sIAAAAAAAAAPv/Hy/4Qbz458+fIeILQQBIwoSh6qECuMVBukCmIJkDVQ+RQNgLE0MX/w+1lyhxqIUwTLJ/sQMAcIXsbVABAAA="
|
||||
)));
|
||||
_fsh.scrlayout = ["com.fsh.clock","com.fsh.calendar","com.fsh.apps_n_files"];
|
||||
|
||||
_fsh.drawTitlebar = function(titletext) {
|
||||
GL.drawTexPattern(_fsh.titlebarTex, 0, 0, 560, 14);
|
||||
if (titletext === undefined || titletext.length == 0) {
|
||||
con.move(1,1);
|
||||
print(" ".repeat(_fsh.scrwidth));
|
||||
GL.drawTexImageOver(_fsh.brandLogoTexSmall, 268, 0);
|
||||
}
|
||||
else {
|
||||
con.color_pair(240, 255);
|
||||
GL.drawTexPattern(_fsh.titlebarTex, 268, 0, 24, 14);
|
||||
con.move(1, 1 + (_fsh.scrwidth - titletext.length) / 2);
|
||||
print(titletext);
|
||||
}
|
||||
con.color_pair(254, 255);
|
||||
};
|
||||
|
||||
|
||||
_fsh.Widget = function(id, w, h) {
|
||||
this.identifier = id;
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
|
||||
if (!this.identifier) {
|
||||
this.identifier = "";
|
||||
}
|
||||
|
||||
//this.update = function() {};
|
||||
/**
|
||||
* Params charXoff and charYoff are ZERO-BASED!
|
||||
*/
|
||||
this.draw = function(charXoff, charYoff) {};
|
||||
}
|
||||
|
||||
_fsh.widgets = {}
|
||||
_fsh.registerNewWidget = function(widget) {
|
||||
_fsh.widgets[widget.identifier] = widget;
|
||||
}
|
||||
|
||||
var clockWidget = new _fsh.Widget("com.fsh.clock", _fsh.scrwidth - 8, 7);
|
||||
clockWidget.numberSheet = new GL.SpriteSheet(19, 22, new GL.Texture(190, 22, gzip.decomp(base64.atob(
|
||||
"H4sIAAAAAAAAAMWVW3LEMAgE739aHcFJJV5ZMD2I9ToVfcl4GBr80HF8r/FaR1ozMuIyoUu87lEXI0al5qVR5AebSwchSaNE6Nyo1Nw5HXF3SfPT4Bshl"+
|
||||
"EycA8RD96mLlHbuhTgOrfLnUDZspafbSQWk56WEGvQEtWaWwgb8iz7a8AOXhsraO/q9Qw2/GnXovfVN+q2wM/p/oddn2cjF239GX3y11+SWCtc6FTHC1v"+
|
||||
"TVPkDPWWn0w+DDz93UX9v9mF5KIsQ6OdN2KJoB4ui1bXXr0AMp0YfiQo//4XhpK8555dsNehAqVS5uhb5iHn3Kko769J59KmLBe/TSR7hcsd+hr+HnrwR"+
|
||||
"9uvRF9+D3MP14gN7lqx+8OuNT+uqt3NFX3SN9fTbeeHNq+C29pRWzX5+Rcm7SZyjOKJ/2hkSPqul4xN279DrSYvCrNu2NI7ZMp1ouBxK3KBVVnEeAUWbK"+
|
||||
"MUDn5DPsPxmUqHZQjGpy2hergM3EVBAAAA=="
|
||||
))));
|
||||
|
||||
clockWidget.clockColon = new GL.Texture(4, 3, base64.atob("7+/v7+/v7+/v7+/v"));
|
||||
clockWidget.monthNames = ["Spring", "Summer", "Autumn", "Winter"];
|
||||
clockWidget.dayNames = ["Mondag ", "Tysdag ", "Midtveke", "Torsdag ", "Fredag ", "Laurdag ", "Sundag ", "Verddag "];
|
||||
clockWidget.draw = function(charXoff, charYoff) {
|
||||
con.color_pair(254, 255);
|
||||
var xoff = charXoff * 7;
|
||||
var yoff = charYoff * 14 + 3;
|
||||
var timeInMinutes = ((sys.currentTimeInMills() / 60000)|0);
|
||||
var mins = timeInMinutes % 60;
|
||||
var hours = ((timeInMinutes / 60)|0) % 24;
|
||||
var ordinalDay = ((timeInMinutes / (60*24))|0) % 120;
|
||||
var visualDay = (ordinalDay % 30) + 1;
|
||||
var months = ((timeInMinutes / (60*24*30))|0) % 4;
|
||||
var dayName = ordinalDay % 7; // 0 for Mondag
|
||||
if (ordinalDay == 119) dayName = 7; // Verddag
|
||||
var years = ((timeInMinutes / (60*24*30*120))|0) + 125;
|
||||
// draw timepiece
|
||||
GL.drawSprite(clockWidget.numberSheet, (hours / 10)|0, 0, xoff, yoff);
|
||||
GL.drawSprite(clockWidget.numberSheet, hours % 10, 0, xoff + 24, yoff);
|
||||
GL.drawTexImage(clockWidget.clockColon, xoff + 48, yoff + 5);
|
||||
GL.drawTexImage(clockWidget.clockColon, xoff + 48, yoff + 14);
|
||||
GL.drawSprite(clockWidget.numberSheet, (mins / 10)|0, 0, xoff + 57, yoff);
|
||||
GL.drawSprite(clockWidget.numberSheet, mins % 10, 0, xoff + 81, yoff);
|
||||
// print month and date
|
||||
con.move(1 + charYoff, 17 + charXoff);
|
||||
print(clockWidget.monthNames[months]+" "+visualDay);
|
||||
// print year and dayname
|
||||
con.mvaddch(2 + charYoff, 17 + charXoff, 5);
|
||||
con.move(2 + charYoff, 18 + charXoff);
|
||||
print(years+" "+clockWidget.dayNames[dayName]);
|
||||
};
|
||||
|
||||
|
||||
// register widgets
|
||||
_fsh.registerNewWidget(clockWidget);
|
||||
|
||||
// screen init
|
||||
con.color_pair(254, 255);
|
||||
con.clear();
|
||||
con.curs_set(0);
|
||||
_fsh.drawTitlebar();
|
||||
|
||||
|
||||
// TEST
|
||||
con.move(2,1);
|
||||
print("Hit backspace to exit");
|
||||
while (true) {
|
||||
captureUserInput();
|
||||
if (getKeyPushed(0) == 67) break;
|
||||
|
||||
_fsh.widgets["com.fsh.clock"].draw(25, 2);
|
||||
}
|
||||
|
||||
con.move(3,1);
|
||||
con.color_pair(201,255);
|
||||
print("cya!");
|
||||
|
||||
let konsht = 3412341241;
|
||||
println(konsht);
|
||||
|
||||
let pppp = graphics.getCursorYX();
|
||||
println(pppp.toString());
|
||||
20
assets/disk0/tvdos/bin/hexdump.js
Normal file
20
assets/disk0/tvdos/bin/hexdump.js
Normal file
@@ -0,0 +1,20 @@
|
||||
if (exec_args[1] === undefined) {
|
||||
printerrln("Usage: hexdump <file>")
|
||||
return 1;
|
||||
}
|
||||
|
||||
let fileOpened = filesystem.open(_G.shell.getCurrentDrive(), _G.shell.resolvePathInput(exec_args[1]).string, "R");
|
||||
if (!fileOpened) {
|
||||
printerrln(_G.shell.resolvePathInput(exec_args[1]).string+": cannot open");
|
||||
return 1;
|
||||
}
|
||||
let fileContent = filesystem.readAll(_G.shell.getCurrentDrive());
|
||||
let visible = "";
|
||||
|
||||
for (let k = 0; k < fileContent.length; k++) {
|
||||
if (k > 0 && k % 16 == 0) visible += "\n";
|
||||
visible += `${fileContent.charCodeAt(k).toString(16).toUpperCase().padStart(2, '0')} `;
|
||||
}
|
||||
|
||||
println(visible);
|
||||
return 0;
|
||||
143
assets/disk0/tvdos/bin/less.js
Normal file
143
assets/disk0/tvdos/bin/less.js
Normal file
@@ -0,0 +1,143 @@
|
||||
if (exec_args[1] === undefined) {
|
||||
println('Missing filename ("less -?" for help)');
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*let help = "\n
|
||||
SUMMARY OF COMMANDS\n
|
||||
\n
|
||||
h H Display this help\n
|
||||
q Q Exit
|
||||
\n"*/
|
||||
|
||||
if (exec_args[1].startsWith("-?")) {
|
||||
println("less <filename>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let fileOpened = filesystem.open(_G.shell.getCurrentDrive(), _G.shell.resolvePathInput(exec_args[1]).string, "R");
|
||||
if (!fileOpened) {
|
||||
printerrln(_G.shell.resolvePathInput(exec_args[1]).string+": cannot open");
|
||||
return 1;
|
||||
}
|
||||
|
||||
let scroll = 0;
|
||||
let pan = -1;
|
||||
let termW = con.getmaxyx()[1];
|
||||
let termH = con.getmaxyx()[0] - 1;
|
||||
let buf = "";
|
||||
let fileContent = filesystem.readAll(_G.shell.getCurrentDrive());
|
||||
let key = -1;
|
||||
let panSize = termW >> 1;
|
||||
let scrollSize = termH >> 3;
|
||||
|
||||
// initialise some helper variables
|
||||
let lineToBytes = [0];
|
||||
let maxPan = 0;
|
||||
let maxPanCur = 0;
|
||||
for (let i = 0; i < fileContent.length; i++) {
|
||||
let char = fileContent.charCodeAt(i);
|
||||
maxPanCur += 1;
|
||||
if (char == 10 || char == 13) {
|
||||
lineToBytes.push(i + 1);
|
||||
if (maxPanCur > maxPan) maxPan = maxPanCur;
|
||||
maxPanCur = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let startAddr = -1;
|
||||
let paintCur = 0;
|
||||
let cy = 1;
|
||||
let cx = 1;
|
||||
let char = -1;
|
||||
|
||||
let numbuf = 0;
|
||||
|
||||
let resetKeyReadStatus = function() {
|
||||
numbuf = 0;
|
||||
}
|
||||
|
||||
let repaint = function() {
|
||||
con.move(1,1); con.clear();
|
||||
|
||||
startAddr = lineToBytes[scroll];
|
||||
cy = 1; cx = -pan; paintCur = 0;
|
||||
while (cy <= termH) {
|
||||
char = fileContent.charCodeAt(startAddr + paintCur);
|
||||
if (isNaN(char)) break;
|
||||
if (cy <= termH) {
|
||||
if (cx >= 0 && cx < termW) {
|
||||
con.move(cy, cx);
|
||||
if (char != 10 && char != 13)
|
||||
con.addch(char);
|
||||
}
|
||||
cx += 1;
|
||||
}
|
||||
if (char == 10 || char == 13) {
|
||||
cy += 1;
|
||||
cx = -pan;
|
||||
}
|
||||
paintCur += 1;
|
||||
}
|
||||
}
|
||||
|
||||
repaint();
|
||||
con.move(termH + 1, 1);
|
||||
print(":"+" ".repeat(termW - 2));
|
||||
con.move(termH + 1, 2);
|
||||
while (true) {
|
||||
// read a key
|
||||
key = con.getch();
|
||||
// do something with key read
|
||||
/*Q*/if (key == 113 || key == 81) break;
|
||||
/*R*/else if (key == 114 || key == 82) repaint();
|
||||
/*up*/else if (key == 19) {
|
||||
scroll -= scrollSize;
|
||||
if (scroll < 0) scroll = 0;
|
||||
repaint();
|
||||
}
|
||||
/*down*/else if (key == 20) {
|
||||
scroll += scrollSize;
|
||||
if (scroll > lineToBytes.length - termH) scroll = lineToBytes.length - termH;
|
||||
repaint();
|
||||
}
|
||||
/*left*/else if (key == 21 && pan > 0) {
|
||||
pan -= panSize;
|
||||
repaint();
|
||||
}
|
||||
/*right*/else if (key == 22 && pan < maxPan - termW) {
|
||||
pan += panSize;
|
||||
repaint();
|
||||
}
|
||||
/*0-9*/else if (key >= 48 && key <= 57) {
|
||||
print(String.fromCharCode(key));
|
||||
numbuf = (numbuf * 10) + (key - 48);
|
||||
}
|
||||
/*bksp*/else if (key == 8) {
|
||||
if (numbuf > 0) print(String.fromCharCode(key));
|
||||
numbuf = (numbuf / 10)|0;
|
||||
}
|
||||
/*u*/else if (key == 117) {
|
||||
scroll -= numbuf;
|
||||
if (scroll < 0) scroll = 0;
|
||||
repaint();
|
||||
}
|
||||
/*d*/else if (key == 100) {
|
||||
scroll += numbuf;
|
||||
if (scroll > lineToBytes.length - termH) scroll = lineToBytes.length - termH;
|
||||
repaint();
|
||||
}
|
||||
|
||||
|
||||
if (!(key >= 48 && key <= 57 || key == 8)) {
|
||||
resetKeyReadStatus();
|
||||
con.move(termH + 1, 1);
|
||||
print(":"+" ".repeat(termW - 2));
|
||||
con.move(termH + 1, 2);
|
||||
}
|
||||
|
||||
serial.println("numbuf = "+numbuf);
|
||||
}
|
||||
|
||||
con.move(termH + 1, 1);
|
||||
return 0;
|
||||
30
assets/disk0/tvdos/bin/touch.js
Normal file
30
assets/disk0/tvdos/bin/touch.js
Normal file
@@ -0,0 +1,30 @@
|
||||
if (exec_args[1] === undefined) {
|
||||
println("TOUCH - TVDOS file date and time setting utility");
|
||||
println()
|
||||
println("SYNOPSIS")
|
||||
println(" TOUCH [/C] path")
|
||||
println()
|
||||
println("/C = don't create files that do not already exist")
|
||||
return 1;
|
||||
}
|
||||
|
||||
let path = _G.shell.resolvePathInput(exec_args[2] || exec_args[1]).string;
|
||||
let driveLetter = _G.shell.getCurrentDrive();
|
||||
let noNewFile = (exec_args[1] == "/c" || exec_args[1] == "/C");
|
||||
let fileOpened = filesystem.open(driveLetter, path, "W");
|
||||
if (!fileOpened) {
|
||||
printerrln("TOUCH: Can't open "+driveLetter+":\\"+path+" due to IO error");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!noNewFile) {
|
||||
filesystem.mkFile(driveLetter);
|
||||
}
|
||||
|
||||
let touched = filesystem.touch(driveLetter);
|
||||
if (!touched) {
|
||||
printerrln("TOUCH: Can't touch "+driveLetter+":\\"+path+" due to IO error");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
1
assets/disk0/tvdos/bin/true.js
Normal file
1
assets/disk0/tvdos/bin/true.js
Normal file
@@ -0,0 +1 @@
|
||||
return 0;
|
||||
45
assets/disk0/tvdos/filesystem.md
Normal file
45
assets/disk0/tvdos/filesystem.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Syntax
|
||||
## Reserved directories
|
||||
* `$DEVICE<device_number>`
|
||||
|
||||
## Reserved files
|
||||
* `$DEVICE<device_number>/$BOOT` — associates to bootloader, exact filename depends on the filesystem the device uses
|
||||
|
||||
# Drivers
|
||||
Filesystem driver is just an executable that can do file I/O to one specific filesystem it supports.
|
||||
|
||||
Filesystem drivers, just as regular TVDOS drivers, resides in `<root>/TVDOS/DRIVERS/`
|
||||
|
||||
# Commands
|
||||
|
||||
## cp
|
||||
`[cp|copy] <source> <destination>`
|
||||
|
||||
Executes following command:
|
||||
```
|
||||
<filesystem>.fs cp <source> <dest>
|
||||
```
|
||||
|
||||
## mv
|
||||
`[mv|move] <from> <to>`
|
||||
|
||||
Executes following command:
|
||||
```
|
||||
<filesystem>.fs mv <from> <to>
|
||||
```
|
||||
|
||||
## touch
|
||||
`touch <path>`
|
||||
|
||||
Executes following command:
|
||||
```
|
||||
<filesystem>.fs touch <path>
|
||||
```
|
||||
|
||||
## format
|
||||
`format -f [tsvm|flat|tree] [ -b <path_to_bootloader> ] <device_number>`
|
||||
|
||||
Executes following command:
|
||||
```
|
||||
<filesystem>.fs format <device_number> [ <path_to_bootloader> ]
|
||||
```
|
||||
104
assets/disk0/tvdos/gl.js
Normal file
104
assets/disk0/tvdos/gl.js
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
TVDOS Graphics Library
|
||||
|
||||
Has no affiliation with OpenGL by Khronos Group
|
||||
*/
|
||||
|
||||
var GL = {};
|
||||
|
||||
// bytes should be able to handle both JSArray and Java ByteArray (toString = "[B")?
|
||||
GL.Texture = function(w, h, bytes) {
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
this.texData = bytes;
|
||||
|
||||
if (!Array.isArray(bytes) && !bytes.toString().startsWith("[B")) {
|
||||
throw "Texture data is not an instance of array";
|
||||
}
|
||||
};
|
||||
GL.SpriteSheet = function(tilew, tileh, tex) {
|
||||
this.tileWidth = tilew;
|
||||
this.tileHeight = tileh;
|
||||
this.texture = tex;
|
||||
|
||||
if (!tex instanceof GL.Texture) {
|
||||
throw "Texture is not an instance of GL.Texture";
|
||||
}
|
||||
|
||||
this.getOffX = function(x) { // THIS, or: GL.SpriteSheet.prototype.getOffX
|
||||
var tx = this.tileWidth * x;
|
||||
if (tx + this.tileWidth > this.texture.width) throw "Sprite x-offset of "+tx+" is greater than sprite width "+this.texture.width;
|
||||
return tx;
|
||||
};
|
||||
|
||||
this.getOffY = function(y) {
|
||||
var ty = this.tileHeight * y;
|
||||
if (ty + this.tileHeight > this.texture.height) throw "Sprite y-offset of "+ty+" is greater than sprite height "+this.texture.height;
|
||||
return ty;
|
||||
};
|
||||
};
|
||||
GL.drawTexPattern = function(texture, x, y, width, height) {
|
||||
for (var yy = 0; yy < height; yy++) {
|
||||
for (var xx = 0; xx < width; xx++) {
|
||||
var tx = xx % texture.width;
|
||||
var ty = yy % texture.height;
|
||||
var c = texture.texData[ty * texture.width + tx];
|
||||
graphics.plotPixel(x + xx, y + yy, c);
|
||||
}
|
||||
}
|
||||
};
|
||||
GL.drawTexPatternOver = function(texture, x, y, width, height) {
|
||||
for (var yy = 0; yy < height; yy++) {
|
||||
for (var xx = 0; xx < width; xx++) {
|
||||
var tx = xx % texture.width;
|
||||
var ty = yy % texture.height;
|
||||
var c = texture.texData[ty * texture.width + tx];
|
||||
if ((c & 255) != 255) {
|
||||
graphics.plotPixel(x + xx, y + yy, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/*
|
||||
* Draws a texture verbatim - color of 255 will be written to the screen buffer
|
||||
*/
|
||||
GL.drawTexImage = function(texture, x, y) {
|
||||
GL.drawTexPattern(texture, x, y, texture.width, texture.height);
|
||||
};
|
||||
/*
|
||||
* Draws texture with blitting - color of 255 will pass-thru what's already on the screen buffer
|
||||
*/
|
||||
GL.drawTexImageOver = function(texture, x, y) {
|
||||
for (var ty = 0; ty < texture.height; ty++) {
|
||||
for (var tx = 0; tx < texture.width; tx++) {
|
||||
var c = texture.texData[ty * texture.width + tx];
|
||||
if ((c & 255) != 255) {
|
||||
graphics.plotPixel(x + tx, y + ty, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
GL.drawSprite = function(sheet, xi, yi, x, y) {
|
||||
var offx = sheet.getOffX(xi);
|
||||
var offy = sheet.getOffY(yi);
|
||||
for (var ty = 0; ty < sheet.tileHeight; ty++) {
|
||||
for (var tx = 0; tx < sheet.tileWidth; tx++) {
|
||||
var c = sheet.texture.texData[(ty + offy) * sheet.texture.width + (tx + offx)];
|
||||
graphics.plotPixel(x + tx, y + ty, c);
|
||||
}
|
||||
}
|
||||
};
|
||||
GL.drawSpriteOver = function(sheet, xi, yi, x, y) {
|
||||
var offx = sheet.getOffX(xi);
|
||||
var offy = sheet.getOffY(yi);
|
||||
for (var ty = 0; ty < sheet.tileHeight; ty++) {
|
||||
for (var tx = 0; tx < sheet.tileWidth; tx++) {
|
||||
var c = sheet.texture.texData[(ty + offy) * sheet.texture.width + (tx + offx)];
|
||||
if ((c & 255) != 255) {
|
||||
graphics.plotPixel(x + tx, y + ty, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Object.freeze(GL); // this returns frozen 'GL'
|
||||
Reference in New Issue
Block a user