mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
1563 lines
52 KiB
Plaintext
1563 lines
52 KiB
Plaintext
let _fromCharCode = b => String.fromCharCode((b >= 0) ? b : 256 + b)
|
|
function btostr(bytes) {
|
|
let s = ""
|
|
for (let i = 0; i < bytes.length; i++) {
|
|
s += _fromCharCode(bytes[i])
|
|
}
|
|
return s
|
|
}
|
|
// define exceptions
|
|
function InterruptedException(m) {
|
|
this.message = m;
|
|
this.stack = (new Error()).stack;
|
|
};
|
|
InterruptedException.prototype = Object.create(Error.prototype);
|
|
InterruptedException.prototype.name = 'InterruptedException';
|
|
InterruptedException.prototype.constructor = InterruptedException;
|
|
function IllegalAccessException(m) {
|
|
this.message = m;
|
|
this.stack = (new Error()).stack;
|
|
};
|
|
IllegalAccessException.prototype = Object.create(Error.prototype);
|
|
IllegalAccessException.prototype.name = 'IllegalAccessException';
|
|
IllegalAccessException.prototype.constructor = IllegalAccessException;
|
|
class SIG {
|
|
constructor(name, number) {
|
|
this.name = "SIG" + name;
|
|
this.number = number|0;
|
|
}
|
|
}
|
|
const SIGTERM = new SIG("TERM",15);
|
|
const SIGSEGV = new SIG("SEGV",11);
|
|
|
|
class PmemFSdir {
|
|
constructor(targetpath) {
|
|
if (targetpath === undefined) {
|
|
this.data = "" // the values is not used by anything :p
|
|
return
|
|
}
|
|
|
|
if (typeof targetpath === 'string' || targetpath instanceof String) {
|
|
this.target = targetpath
|
|
}
|
|
else {
|
|
throw Error("Invalid type for directory")
|
|
}
|
|
}
|
|
}
|
|
class PmemFSfile {
|
|
constructor(bytes) {
|
|
if (bytes === undefined) {
|
|
this.data = ""
|
|
return
|
|
}
|
|
|
|
// string representation (preferable)
|
|
if (typeof bytes === 'string' || bytes instanceof String) {
|
|
this.data = bytes
|
|
}
|
|
// Javascript array OR JVM byte[]
|
|
else if (Array.isArray(bytes) || bytes.toString().startsWith("[B")) {
|
|
this.bdata = bytes[i]
|
|
}
|
|
else {
|
|
throw Error("Invalid type for directory")
|
|
}
|
|
}
|
|
|
|
dataAsString() {
|
|
if (this.data !== undefined) return this.data
|
|
this.data = ""
|
|
for (let i = 0; i < bytes.length; i++) {
|
|
this.data += String.fromCharCode(bytes[i])
|
|
}
|
|
return this.data
|
|
}
|
|
|
|
dataAsBytes() {
|
|
if (this.bdata !== undefined) return this.bdata
|
|
this.bdata = new Int8Array(this.data.length)
|
|
for (let i = 0; i < this.data.length; i++) {
|
|
let p = this.data.charCodeAt(i)
|
|
this.bdata[i] = (p > 127) ? p - 255 : p
|
|
}
|
|
return this.bdata
|
|
}
|
|
}
|
|
|
|
function generateRandomHashStr(len) {
|
|
let cs = 'qwfpgarstdzxcvbjluyhneiokmQWFPGARSTDZXCVBJLUYHNEIOKM';
|
|
let s = '';
|
|
for (let i = 0; i < len; i++) {
|
|
s += cs[(Math.random()*cs.length)|0];
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// define TVDOS
|
|
const _TVDOS = {};
|
|
_TVDOS.VERSION = "1.0";
|
|
_TVDOS.DRIVES = {}; // Object where key-value pair is <drive-letter> : [serial-port, drive-number]
|
|
_TVDOS.DRIVEFS = {}; // filesystem driver for the drive letter
|
|
_TVDOS.DRIVEINFO = {};
|
|
// actually figure out the drive letter association
|
|
// Drive A is always the device we're currently on
|
|
_TVDOS.DRIVES["A"] = _BIOS.FIRST_BOOTABLE_PORT
|
|
_TVDOS.DRIVEFS["A"] = "SERIAL"
|
|
_TVDOS.DRIVEINFO["A"] = {
|
|
name: com.sendMessageGetBytes(_BIOS.FIRST_BOOTABLE_PORT[0], "DEVNAM\x17").init(),
|
|
type: com.sendMessageGetBytes(_BIOS.FIRST_BOOTABLE_PORT[0], "DEVTYP\x17").substring(0,4)
|
|
}
|
|
_TVDOS.TMPFS = {
|
|
".": new PmemFSdir("TMP/"),
|
|
"..": new PmemFSdir("TMP/"),
|
|
};
|
|
|
|
// probe filesystem devices
|
|
let devnameToDriver = {"PRNT":"LP","STOR":"SERIAL","COMM":"COM","HTTP":"NET","TMP":"PMEMFS"}
|
|
let currentDriveLetter = ["A","B","C","D"]
|
|
for (let portNo = 1; portNo < 4; portNo++) {
|
|
if (com.areYouThere(portNo)) {
|
|
com.sendMessage(portNo, "DEVRST\x17")
|
|
sys.spin()
|
|
let devname = com.sendMessageGetBytes(portNo, "DEVTYP\x17").substring(0,4)
|
|
let driver = devnameToDriver[devname]
|
|
// serial.println(`port: ${portNo}, devname: ${devname} ${com.sendMessageGetBytes(portNo, "DEVNAM\x17")}`)
|
|
if (driver) {
|
|
let dlet = currentDriveLetter[portNo]
|
|
_TVDOS.DRIVEFS[dlet] = driver
|
|
_TVDOS.DRIVES[dlet] = [portNo, 1]
|
|
_TVDOS.DRIVEINFO[dlet] = {
|
|
name: com.sendMessageGetBytes(portNo, "DEVNAM\x17").init(),
|
|
type: devname
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_TVDOS.DRV = {}
|
|
|
|
|
|
_TVDOS.getPath = function() {
|
|
return [''].concat(_TVDOS.variables.PATH.split(';'));
|
|
};
|
|
// initial values
|
|
_TVDOS.variables = {
|
|
DOSDIR: "\\tvdos",
|
|
LANG: "EN",
|
|
KEYBOARD: "us_qwerty",
|
|
PATH: "\\tvdos\\bin;\\home",
|
|
PATHEXT: ".com;.bat;.app;.js",
|
|
HELPPATH: "\\tvdos\\help",
|
|
OS_NAME: "TSVM Disk Operating System",
|
|
OS_VERSION: _TVDOS.VERSION
|
|
};
|
|
Object.freeze(_TVDOS);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
_TVDOS.DRV.FS = {}
|
|
|
|
class TVDOSFileDescriptor {
|
|
|
|
constructor(path0, driverID) {
|
|
if (path0.startsWith("$")) {
|
|
let path1 = path0.substring(3)
|
|
let slashPos = path1.indexOf("/")
|
|
let devName = path1.substring(0, (slashPos < 0) ? path1.length : slashPos)
|
|
|
|
if (!files.reservedNames.includes(devName)) {
|
|
throw Error(`${devName} is not a valid device file`)
|
|
}
|
|
|
|
this._driveLetter = undefined
|
|
this._path = path0
|
|
this._driverID = `DEV${devName}`
|
|
this._driver = _TVDOS.DRV.FS[`DEV${devName}`] // can't just put `driverID` here
|
|
}
|
|
else {
|
|
let p = path0.replaceAll("/", "\\")
|
|
this._driveLetter = p[0]
|
|
p = p.substring(2) // detaches A:
|
|
// remove trailing slashes
|
|
while (p.endsWith("\\")) {
|
|
p = p.substring(0, p.length - 1)
|
|
}
|
|
// remove initial slashes...
|
|
while (p.startsWith("\\")) {
|
|
p = p.substring(1)
|
|
}
|
|
// then add our own
|
|
p = "\\" + p
|
|
|
|
this._path = p
|
|
|
|
if (driverID == undefined) driverID = "UNDEFINED"
|
|
|
|
this._driverID = driverID
|
|
this._driver = _TVDOS.DRV.FS[driverID]
|
|
// serial.println(`TVDOSFileDescriptor input path: ${path0}, p = ${p}, driveLetter = ${this._driveLetter}, path = ${this._path}, driver = ${driverID}`)
|
|
}
|
|
}
|
|
|
|
get size() {
|
|
return this.driver.getFileLen(this)
|
|
}
|
|
|
|
get path() {
|
|
return this._path
|
|
}
|
|
|
|
get driverID() {
|
|
return this._driverID
|
|
}
|
|
|
|
get driver() {
|
|
return this._driver
|
|
}
|
|
|
|
get driveLetter() {
|
|
return this._driveLetter
|
|
}
|
|
|
|
get fullPath() {
|
|
return `${this._driveLetter}:${this._path}`
|
|
}
|
|
|
|
/** reads the file bytewise and puts it to the specified memory address
|
|
* @param count optional -- how many bytes to read
|
|
* @param offset optional -- how many bytes to skip initially
|
|
*/
|
|
pread(ptr, count, offset) {
|
|
this.driver.pread(this, ptr, count, offset)
|
|
}
|
|
/** @return bytewise (actually SIGNED Int8) contents of the file in JS array */
|
|
bread() {
|
|
return this.driver.bread(this)
|
|
}
|
|
/** @return textwise contents of the file in JS string */
|
|
sread() {
|
|
return this.driver.sread(this)
|
|
}
|
|
|
|
/** writes the bytes stored in the memory[ptr .. ptr+count-1] to file[offset .. offset+count-1]
|
|
* - @param offset is optional
|
|
*/
|
|
pwrite(ptr, count, offset) {
|
|
this.driver.pwrite(this, ptr, count, offset)
|
|
}
|
|
/** @param bytes bytewise contents to write, in JS array */
|
|
bwrite(bytes) {
|
|
this.driver.bwrite(this, bytes)
|
|
}
|
|
/** @param string stringwise contents to write, in JS array */
|
|
swrite(string) {
|
|
this.driver.swrite(this, string)
|
|
}
|
|
|
|
/**
|
|
* @param ptr where the byes are (offsets from the base ptr can be coded as `baseptr + offset`)
|
|
* @param count how many bytes to append
|
|
*/
|
|
pappend(ptr, count) {
|
|
this.driver.pappend(this, ptr, count)
|
|
}
|
|
bappend(bytes) {
|
|
this.driver.bappend(this, bytes)
|
|
}
|
|
sappend(string) {
|
|
this.driver.sappend(this, string)
|
|
}
|
|
|
|
flush() {
|
|
this.driver.flush(this)
|
|
}
|
|
|
|
close() {
|
|
this.driver.close(this)
|
|
}
|
|
|
|
get isDirectory() {
|
|
return this.driver.isDirectory(this)
|
|
}
|
|
|
|
get name() {
|
|
return this.path.split("\\").last()
|
|
}
|
|
get extension() {
|
|
let fname = this.name
|
|
let dotpos = fname.lastIndexOf('.')
|
|
if (dotpos < 0) return ''
|
|
else return fname.substring(dotpos+1)
|
|
}
|
|
get parentPath() {
|
|
// return this.path.split("\\").init().join("\\")
|
|
let li = this.path.lastIndexOf("\\")
|
|
return this.path.substring(0, li)
|
|
}
|
|
|
|
list() {
|
|
return (!this.isDirectory) ? undefined : this.driver.listFiles(this)
|
|
}
|
|
/** When the file does not exist, mkfile() will be called; if you want to make a directory, use mkdir() */
|
|
touch() {
|
|
return this.driver.touch(this)
|
|
}
|
|
/** Creates the directory named by this abstract pathname, including any necessary but nonexistent parent directories */
|
|
mkDir() {
|
|
return this.driver.mkDir(this)
|
|
}
|
|
|
|
mkFile() {
|
|
return this.driver.mkFile(this)
|
|
}
|
|
|
|
remove() {
|
|
return this.driver.remove(this)
|
|
}
|
|
|
|
get exists() {
|
|
return this.driver.exists(this)
|
|
}
|
|
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
_TVDOS.DRV.FS.SERIAL = {}
|
|
// TODO \dev\null zero full random lp fb0..fb3 mem pmem0..pmem7 com1..com4
|
|
|
|
|
|
|
|
_TVDOS.DRV.FS.SERIAL._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
|
|
}
|
|
|
|
_TVDOS.DRV.FS.SERIAL._openr = (fd) => {
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
_TVDOS.DRV.FS.SERIAL._flush(port[0]);_TVDOS.DRV.FS.SERIAL._close(port[0])
|
|
com.sendMessage(port[0], "OPENR"+'"'+fd.path+'",'+port[1])
|
|
return com.getStatusCode(port[0])
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL._openw = (fd) => {
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
_TVDOS.DRV.FS.SERIAL._flush(port[0]);_TVDOS.DRV.FS.SERIAL._close(port[0])
|
|
com.sendMessage(port[0], "OPENW"+'"'+fd.path+'",'+port[1])
|
|
return com.getStatusCode(port[0])
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL._opena = (fd) => {
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
_TVDOS.DRV.FS.SERIAL._flush(port[0]);_TVDOS.DRV.FS.SERIAL._close(port[0])
|
|
com.sendMessage(port[0], "OPENA"+'"'+fd.path+'",'+port[1])
|
|
return com.getStatusCode(port[0])
|
|
}
|
|
|
|
|
|
_TVDOS.DRV.FS.SERIAL._close = (portNo) => {
|
|
com.sendMessage(portNo, "CLOSE")
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL._flush = (portNo) => {
|
|
com.sendMessage(portNo, "FLUSH")
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.close = (fd) => {
|
|
let portNo = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(portNo[0], "CLOSE")
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.flush = (fd) => {
|
|
let portNo = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(portNo[0], "FLUSH")
|
|
}
|
|
|
|
_TVDOS.DRV.FS.SERIAL.getFileLen = (fd) => {
|
|
if (129 == _TVDOS.DRV.FS.SERIAL._openr(fd)) throw Error(`No such file: ${fd.fullPath}`)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(port[0], "GETLEN")
|
|
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 Number(com.pullMessage(port[0]))
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.pread = (fd, ptr, count, offset) => {
|
|
if (129 == _TVDOS.DRV.FS.SERIAL._openr(fd)) throw Error(`No such file: ${fd.fullPath}`)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(port[0], "READ")
|
|
let 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)
|
|
}
|
|
dma.comToRam(port[0], offset || 0, ptr, count || 0)
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.sread = (fd) => {
|
|
if (129 == _TVDOS.DRV.FS.SERIAL._openr(fd)) throw Error(`No such file: ${fd.fullPath}`)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(port[0], "READ")
|
|
let 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])
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.bread = (fd) => {
|
|
let str = _TVDOS.DRV.FS.SERIAL.sread(fd)
|
|
let bytes = new Int8Array(str.length)
|
|
for (let i = 0; i < str.length; i++) {
|
|
// let p = str.charCodeAt(i)
|
|
// bytes[i] = (p > 127) ? p - 255 : p
|
|
bytes[i] = str.charCodeAt(i)
|
|
}
|
|
return bytes
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.pwrite = (fd, ptr, count, offset) => {
|
|
if (offset) throw Error(`Write offset not supported on Serial device such as disk drives`)
|
|
|
|
let rrrr = _TVDOS.DRV.FS.SERIAL._openw(fd); if (rrrr != 0) throw Error("Writing a file failed with "+rrrr)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
dma.ramToCom(ptr, port[0], count)
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.swrite = (fd, string) => {
|
|
let rrrr = _TVDOS.DRV.FS.SERIAL._openw(fd); if (rrrr != 0) throw Error("Writing a file failed with "+rrrr)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(port[0], "WRITE"+string.length)
|
|
let 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)
|
|
_TVDOS.DRV.FS.SERIAL._flush(port[0]);_TVDOS.DRV.FS.SERIAL._close(port[0])
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.bwrite = (fd, bytes) => { // pwrite replaces DMA.ramToCom
|
|
let string = btostr(bytes) // no spreading: has length limit
|
|
_TVDOS.DRV.FS.SERIAL.swrite(fd, string)
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.pappend = (fd, ptr, count) => {
|
|
let rrrr = _TVDOS.DRV.FS.SERIAL._opena(fd); if (rrrr != 0) throw Error("Appending to a file failed with "+rrrr)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
dma.ramToCom(ptr, port[0], count)
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.bappend = (fd, bytes) => { // pwrite replaces DMA.ramToCom
|
|
let string = btostr(bytes) // no spreading: has length limit
|
|
_TVDOS.DRV.FS.SERIAL.sappend(fd, string)
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.sappend = (fd, string) => {
|
|
let rrrr = _TVDOS.DRV.FS.SERIAL._opena(fd); if (rrrr != 0) throw Error("Appending to a file failed with "+rrrr)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(port[0], "WRITE"+string.length)
|
|
let response = com.getStatusCode(port[0])
|
|
if (135 == response) {
|
|
throw Error("File not opened")
|
|
}
|
|
if (response < 0 || response >= 128) {
|
|
throw Error("Appending to a file failed with "+response)
|
|
}
|
|
com.sendMessage(port[0], string)
|
|
_TVDOS.DRV.FS.SERIAL._flush(port[0]);_TVDOS.DRV.FS.SERIAL._close(port[0])
|
|
}
|
|
|
|
_TVDOS.DRV.FS.SERIAL.isDirectory = (fd) => {
|
|
if (129 == _TVDOS.DRV.FS.SERIAL._openr(fd)) return false // file not exists
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(port[0], "LISTFILES")
|
|
let response = com.getStatusCode(port[0])
|
|
return (response === 0)
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.listFiles = (fd) => {
|
|
if (129 == _TVDOS.DRV.FS.SERIAL._openr(fd)) throw Error(`No such file: ${fd.fullPath}`)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(port[0], "LISTFILES")
|
|
let response = com.getStatusCode(port[0])
|
|
if (response !== 0) return undefined
|
|
let rawStr = com.pullMessage(port[0]) // {\x11 | \x12} <name> [ \x1E {\x11 | \x12} <name> ] \x17
|
|
let parentPath = fd.fullPath
|
|
return rawStr.substring(0, rawStr.length).split('\x1E').map(it => new TVDOSFileDescriptor(`${parentPath}/${it.substring(1)}`, "SERIAL"))
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.touch = (fd) => {
|
|
let rrrr = _TVDOS.DRV.FS.SERIAL._openw(fd); if (rrrr != 0) throw Error("Touching a file failed with "+rrrr)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(port[0], "TOUCH")
|
|
let response = com.getStatusCode(port[0])
|
|
return (response === 0)
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.mkDir = (fd) => {
|
|
let rrrr = _TVDOS.DRV.FS.SERIAL._openw(fd); if (rrrr != 0) throw Error("Mkdir a file failed with "+rrrr)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(port[0], "MKDIR")
|
|
let 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)
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.mkFile = (fd) => {
|
|
let rrrr = _TVDOS.DRV.FS.SERIAL._openw(fd); if (rrrr != 0) throw Error("Mkfile failed with "+rrrr)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(port[0], "MKFILE")
|
|
let response = com.getStatusCode(port[0])
|
|
return (response === 0)
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.remove = (fd) => {
|
|
let rrrr = _TVDOS.DRV.FS.SERIAL._openw(fd); if (rrrr != 0) throw Error("Writing a file failed with "+rrrr)
|
|
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
com.sendMessage(port[0], "DELETE")
|
|
let response = com.getStatusCode(port[0])
|
|
return response
|
|
}
|
|
_TVDOS.DRV.FS.SERIAL.exists = (fd) => {
|
|
return (0 == _TVDOS.DRV.FS.SERIAL._openr(fd))
|
|
}
|
|
|
|
Object.freeze(_TVDOS.DRV.FS.SERIAL)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
_TVDOS.DRV.FS.DEVRND = {}
|
|
|
|
_TVDOS.DRV.FS.DEVRND._mkrndbytes = (length) => {
|
|
let bytes = new Int8Array(length)
|
|
let timeNow = sys.currentTimeInMills()
|
|
let state = 12345678910 + timeNow
|
|
|
|
for (let i = 0; i < length; i++) {
|
|
let x = state
|
|
x ^= x << 13
|
|
x ^= x >> 7
|
|
x ^= x << 17
|
|
state = x
|
|
|
|
bytes[i] = state & 255
|
|
}
|
|
|
|
return bytes
|
|
}
|
|
|
|
_TVDOS.DRV.FS.DEVRND.pread = (fd, ptr, count, offset) => {
|
|
let bytes = _TVDOS.DRV.FS.DEVRND._mkrndbytes(count + offset)
|
|
for (let i = 0; i < count; i++) {
|
|
sys.poke(ptr + i, bytes[offset + i])
|
|
}
|
|
|
|
}
|
|
|
|
_TVDOS.DRV.FS.DEVRND.pwrite = (fd, ptr, count, offset) => {}
|
|
_TVDOS.DRV.FS.DEVRND.bwrite = (fd, bytes) => {}
|
|
_TVDOS.DRV.FS.DEVRND.swrite = (fd, string) => {}
|
|
|
|
_TVDOS.DRV.FS.DEVRND.flush = () => {}
|
|
_TVDOS.DRV.FS.DEVRND.close = () => {}
|
|
_TVDOS.DRV.FS.DEVRND.isDirectory = () => false
|
|
_TVDOS.DRV.FS.DEVRND.listFiles = () => undefined
|
|
_TVDOS.DRV.FS.DEVRND.touch = () => {}
|
|
_TVDOS.DRV.FS.DEVRND.mkDir = () => {}
|
|
_TVDOS.DRV.FS.DEVRND.mkFile = () => {}
|
|
_TVDOS.DRV.FS.DEVRND.remove = () => {}
|
|
_TVDOS.DRV.FS.DEVRND.exists = () => true
|
|
|
|
Object.freeze(_TVDOS.DRV.FS.DEVRND)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
_TVDOS.DRV.FS.UNDEFINED = {}
|
|
|
|
_TVDOS.DRV.FS.UNDEFINED.pread = (fd, ptr, count, offset) => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.bread = (fd) => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.sread = (fd) => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.pwrite = (fd, ptr, count, offset) => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.bwrite = (fd, bytes) => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.swrite = (fd, string) => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.pappend = (fd, ptr, count, offset) => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.bappend = (fd, bytes) => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.sappend = (fd, string) => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.flush = () => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.close = () => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.isDirectory = () => false
|
|
_TVDOS.DRV.FS.UNDEFINED.listFiles = () => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.touch = () => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.mkDir = () => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.mkFile = () => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.remove = () => { throw Error("File does not exist") }
|
|
_TVDOS.DRV.FS.UNDEFINED.exists = () => false
|
|
|
|
Object.freeze(_TVDOS.DRV.FS.UNDEFINED)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
_TVDOS.DRV.FS.DEVNUL = {}
|
|
|
|
_TVDOS.DRV.FS.DEVNUL.pread = (fd, ptr, count, offset) => {
|
|
for (let i = 0; i < count; i++) {
|
|
sys.poke(ptr + i, -1)
|
|
}
|
|
}
|
|
_TVDOS.DRV.FS.DEVNUL.bread = (fd) => { return [] }
|
|
_TVDOS.DRV.FS.DEVNUL.sread = (fd) => { return "" }
|
|
|
|
_TVDOS.DRV.FS.DEVNUL.pwrite = (fd, ptr, count, offset) => {}
|
|
_TVDOS.DRV.FS.DEVNUL.bwrite = (fd, bytes) => {}
|
|
_TVDOS.DRV.FS.DEVNUL.swrite = (fd, string) => {}
|
|
|
|
_TVDOS.DRV.FS.DEVNUL.flush = () => {}
|
|
_TVDOS.DRV.FS.DEVNUL.close = () => {}
|
|
_TVDOS.DRV.FS.DEVNUL.isDirectory = () => false
|
|
_TVDOS.DRV.FS.DEVNUL.listFiles = () => undefined
|
|
_TVDOS.DRV.FS.DEVNUL.touch = () => {}
|
|
_TVDOS.DRV.FS.DEVNUL.mkDir = () => {}
|
|
_TVDOS.DRV.FS.DEVNUL.mkFile = () => {}
|
|
_TVDOS.DRV.FS.DEVNUL.remove = () => {}
|
|
_TVDOS.DRV.FS.DEVNUL.exists = () => true
|
|
|
|
Object.freeze(_TVDOS.DRV.FS.DEVNUL)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
_TVDOS.DRV.FS.DEVZERO = {}
|
|
|
|
_TVDOS.DRV.FS.DEVZERO.pread = (fd, ptr, count, offset) => {
|
|
for (let i = 0; i < count; i++) {
|
|
sys.poke(ptr + i, 0)
|
|
}
|
|
}
|
|
|
|
_TVDOS.DRV.FS.DEVZERO.pwrite = (fd, ptr, count, offset) => {}
|
|
_TVDOS.DRV.FS.DEVZERO.bwrite = (fd, bytes) => {}
|
|
_TVDOS.DRV.FS.DEVZERO.swrite = (fd, string) => {}
|
|
|
|
_TVDOS.DRV.FS.DEVZERO.flush = () => {}
|
|
_TVDOS.DRV.FS.DEVZERO.close = () => {}
|
|
_TVDOS.DRV.FS.DEVZERO.isDirectory = () => false
|
|
_TVDOS.DRV.FS.DEVZERO.listFiles = () => undefined
|
|
_TVDOS.DRV.FS.DEVZERO.touch = () => {}
|
|
_TVDOS.DRV.FS.DEVZERO.mkDir = () => {}
|
|
_TVDOS.DRV.FS.DEVZERO.mkFile = () => {}
|
|
_TVDOS.DRV.FS.DEVZERO.remove = () => {}
|
|
_TVDOS.DRV.FS.DEVZERO.exists = () => true
|
|
|
|
Object.freeze(_TVDOS.DRV.FS.DEVZERO)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
_TVDOS.DRV.FS.DEVCON = {}
|
|
|
|
_TVDOS.DRV.FS.DEVCON.pread = (fd, ptr, count, offset) => {
|
|
let mem = graphics.getGpuMemBase()
|
|
let consize = Math.min(count, grapihcs.getTermDimension().sum())
|
|
for (let i = 0; i < consize; i++) {
|
|
sys.poke(ptr + i, sys.peek(mem - 5122 - i))
|
|
}
|
|
}
|
|
_TVDOS.DRV.FS.DEVCON.bread = (fd) => {
|
|
let mem = graphics.getGpuMemBase()
|
|
let consize = Math.min(count, grapihcs.getTermDimension().sum())
|
|
let r = new Int8Array(consize)
|
|
for (let i = 0; i < consize; i++) {
|
|
// let p = sys.peek(mem - 5122 - i)
|
|
// r[i] = (p > 127) ? p - 255 : p
|
|
r[i] = sys.peek(mem - 5122 - i)
|
|
}
|
|
return r
|
|
}
|
|
_TVDOS.DRV.FS.DEVCON.sread = (fd) => {
|
|
let mem = graphics.getGpuMemBase()
|
|
let consize = Math.min(count, grapihcs.getTermDimension().sum())
|
|
let r = ''
|
|
for (let i = 0; i < consize; i++) {
|
|
r += String.fromCharCode(sys.peek(mem - 5122 - i))
|
|
}
|
|
return r
|
|
}
|
|
|
|
_TVDOS.DRV.FS.DEVCON.pwrite = (fd, ptr, count, offset) => {}
|
|
_TVDOS.DRV.FS.DEVCON.bwrite = (fd, bytes) => {
|
|
let string = btostr(bytes) // no spreading: has length limit
|
|
_TVDOS.DRV.FS.DEVZERO.swrite(fd, string)
|
|
}
|
|
_TVDOS.DRV.FS.DEVCON.swrite = (fd, string) => {
|
|
print(string)
|
|
}
|
|
|
|
_TVDOS.DRV.FS.DEVCON.flush = () => {}
|
|
_TVDOS.DRV.FS.DEVCON.close = () => {}
|
|
_TVDOS.DRV.FS.DEVCON.isDirectory = () => false
|
|
_TVDOS.DRV.FS.DEVCON.listFiles = () => undefined
|
|
_TVDOS.DRV.FS.DEVCON.touch = () => {}
|
|
_TVDOS.DRV.FS.DEVCON.mkDir = () => {}
|
|
_TVDOS.DRV.FS.DEVCON.mkFile = () => {}
|
|
_TVDOS.DRV.FS.DEVCON.remove = () => {}
|
|
_TVDOS.DRV.FS.DEVCON.exists = () => true
|
|
|
|
Object.freeze(_TVDOS.DRV.FS.DEVCON)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
let mkvtcontext = (index, VMEM) => {
|
|
return { // VT0 is reserved for symlink to physical terminal
|
|
isActive: (index === 0 || index === "0"),
|
|
buffer: (index === 0 || index === "0") ? undefined : VMEM
|
|
// TODO: getCursorYX, setCursorYX, setCursorX, setCursorY, getTextForeBack, setTextFore, setTextBack
|
|
// all read/write form the VMEM
|
|
}
|
|
}
|
|
|
|
// byte-to-byte copy of the 512 KB of VRAM
|
|
function mkdevvt(index) {
|
|
|
|
// VT0 is reserved for symlink to the physical terminal
|
|
if (isNaN(index)) return false // check for non-numerics and undefined
|
|
if (index !== index || index <= 0) return false // check for NaN, 0 and negative values
|
|
|
|
// check if the device already exists
|
|
if (_TVDOS.DRV.FS['DEVVT'+index] != undefined) return false
|
|
|
|
let VDEV = {}
|
|
let VMEM = new Int8Array(512 * 1024)
|
|
|
|
VDEV.pread = (fd, ptr, count, offset) => {
|
|
for (let i = 0; i < (count || 0); i++) {
|
|
sys.poke(ptr + i, VMEM[i + (offset || 0)])
|
|
}
|
|
}
|
|
VDEV.bread = (fd) => {
|
|
return VMEM.slice()
|
|
}
|
|
VDEV.sread = (fd) => {
|
|
let r = ''
|
|
for (let i = 0; i < VMEM.length; i++) {
|
|
let c = VMEM[i]
|
|
r += String.fromCharCode((c < 0) ? 256 + c : c)
|
|
}
|
|
return r
|
|
}
|
|
|
|
|
|
VDEV.pwrite = (fd, ptr, count, offset) => {
|
|
for (let i = 0; i < (count || 0); i++) {
|
|
VMEM[i + (offset || 0)] = sys.peek(ptr + i)
|
|
}
|
|
}
|
|
VDEV.bwrite = (fd, bytes) => {
|
|
if (bytes.length == VMEM.length && bytes instanceof Int8Array) {
|
|
VMEM = bytes.slice()
|
|
}
|
|
else {
|
|
for (let i = 0; i < bytes.length; i++) {
|
|
VMEM[i] = bytes[i]
|
|
}
|
|
}
|
|
}
|
|
VDEV.swrite = (fd, string) => {
|
|
for (let i = 0; i < string.length; i++) {
|
|
VMEM[i] = string.charCodeAt(i)
|
|
}
|
|
}
|
|
|
|
|
|
VDEV.flush = () => {}
|
|
VDEV.close = () => {}
|
|
VDEV.isDirectory = () => false
|
|
VDEV.listFiles = () => undefined
|
|
VDEV.touch = () => {}
|
|
VDEV.mkDir = () => {}
|
|
VDEV.mkFile = () => {}
|
|
VDEV.remove = () => {}
|
|
VDEV.exists = () => true
|
|
|
|
_TVDOS.VT_CONTEXTS[index] = mkvtcontext(index, VMEM)
|
|
_TVDOS.DRV.FS['DEVVT'+index] = VDEV
|
|
|
|
Object.freeze(_TVDOS.DRV.FS['DEVVT'+index])
|
|
|
|
return true
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// physical video terminal
|
|
_TVDOS.DRV.FS.DEVPT = {}
|
|
|
|
_TVDOS.DRV.FS.DEVPT.pread = (fd, ptr, count, offset) => {
|
|
let mem = graphics.getGpuMemBase()
|
|
for (let i = 0; i < (count || 0); i++) {
|
|
sys.poke(ptr + i, sys.peek(mem - i))
|
|
}
|
|
}
|
|
_TVDOS.DRV.FS.DEVPT.bread = (fd) => {
|
|
let mem = graphics.getGpuMemBase()
|
|
let r = new Int8Array(512 * 1024)
|
|
for (let i = 0; i < r.length; i++) {
|
|
r[i] = sys.peek(mem - i)
|
|
}
|
|
}
|
|
_TVDOS.DRV.FS.DEVPT.sread = (fd) => {
|
|
let mem = graphics.getGpuMemBase()
|
|
let r = ''
|
|
for (let i = 0; i < 512 * 1024; i++) {
|
|
let c = sys.peek(mem - i)
|
|
r[i] = String.fromCharCode((c < 0) ? 256 + c : c)
|
|
}
|
|
}
|
|
|
|
|
|
_TVDOS.DRV.FS.DEVPT.pwrite = (fd, ptr, count, offset) => {
|
|
let mem = graphics.getGpuMemBase()
|
|
for (let i = 0; i < (count || 0); i++) {
|
|
sys.poke(mem - i, sys.peek(ptr + i))
|
|
}
|
|
}
|
|
_TVDOS.DRV.FS.DEVPT.bwrite = (fd, bytes) => {
|
|
let mem = graphics.getGpuMemBase()
|
|
for (let i = 0; i < bytes.length; i++) {
|
|
sys.poke(mem - i, bytes[i])
|
|
}
|
|
}
|
|
_TVDOS.DRV.FS.DEVPT.swrite = (fd, string) => {
|
|
let mem = graphics.getGpuMemBase()
|
|
for (let i = 0; i < string.length; i++) {
|
|
sys.poke(mem - i, string.charCodeAt(i))
|
|
}
|
|
}
|
|
|
|
|
|
_TVDOS.DRV.FS.DEVPT.flush = () => {}
|
|
_TVDOS.DRV.FS.DEVPT.close = () => {}
|
|
_TVDOS.DRV.FS.DEVPT.isDirectory = () => false
|
|
_TVDOS.DRV.FS.DEVPT.listFiles = () => undefined
|
|
_TVDOS.DRV.FS.DEVPT.touch = () => {}
|
|
_TVDOS.DRV.FS.DEVPT.mkDir = () => {}
|
|
_TVDOS.DRV.FS.DEVPT.mkFile = () => {}
|
|
_TVDOS.DRV.FS.DEVPT.remove = () => {}
|
|
_TVDOS.DRV.FS.DEVPT.exists = () => true
|
|
|
|
mkvtcontext(0)
|
|
|
|
Object.freeze(_TVDOS.DRV.FS.DEVPT)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
_TVDOS.DRV.FS.DEVFBIPF = {}
|
|
|
|
_TVDOS.DRV.FS.DEVFBIPF.pwrite = (fd, infilePtr, count, _2) => {
|
|
let decodefun = ([graphics.decodeIpf1, graphics.decodeIpf2])[sys.peek(infilePtr + 13)]
|
|
let width = sys.peek(infilePtr+8) | (sys.peek(infilePtr+9) << 8)
|
|
let height = sys.peek(infilePtr+10) | (sys.peek(infilePtr+11) << 8)
|
|
let hasAlpha = (sys.peek(infilePtr+12) != 0)
|
|
let ipfType = sys.peek(infilePtr+13)
|
|
let imgLen = sys.peek(infilePtr+24) | (sys.peek(infilePtr+25) << 8) | (sys.peek(infilePtr+26) << 16) | (sys.peek(infilePtr+27) << 24)
|
|
|
|
let ipfbuf = sys.malloc(imgLen)
|
|
gzip.decompFromTo(infilePtr + 28, count - 28, ipfbuf) // should return FBUF_SIZE
|
|
|
|
graphics.setGraphicsMode(4)
|
|
decodefun(ipfbuf, -1048577, -1310721, width, height, hasAlpha)
|
|
|
|
sys.free(ipfbuf)
|
|
}
|
|
_TVDOS.DRV.FS.DEVFBIPF.bwrite = (fd, bytes) => {
|
|
// TODO pread the file
|
|
let fp = sys.malloc(bytes.length)
|
|
bytes.forEach((it,i) => {
|
|
sys.poke(fp + i, it)
|
|
})
|
|
_TVDOS.DRV.FS.DEVFBIPF.pwrite(fd, fp)
|
|
sys.free(fp)
|
|
}
|
|
|
|
// _TVDOS.DRV.FS.DEVFBIPF will be write-only: no way to select IPF1/2 and/or transparency
|
|
|
|
_TVDOS.DRV.FS.DEVFBIPF.flush = () => {}
|
|
_TVDOS.DRV.FS.DEVFBIPF.close = () => {}
|
|
_TVDOS.DRV.FS.DEVFBIPF.isDirectory = () => false
|
|
_TVDOS.DRV.FS.DEVFBIPF.listFiles = () => undefined
|
|
_TVDOS.DRV.FS.DEVFBIPF.touch = () => {}
|
|
_TVDOS.DRV.FS.DEVFBIPF.mkDir = () => {}
|
|
_TVDOS.DRV.FS.DEVFBIPF.mkFile = () => {}
|
|
_TVDOS.DRV.FS.DEVFBIPF.remove = () => {}
|
|
_TVDOS.DRV.FS.DEVFBIPF.exists = () => true
|
|
|
|
Object.freeze(_TVDOS.DRV.FS.DEVFBIPF)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
_TVDOS.DRV.FS.DEVTMP = {}
|
|
|
|
_TVDOS.DRV.FS.DEVTMP.sread = (fd) => {
|
|
if (_TVDOS.TMPFS[fd.path] === undefined) throw Error(`No such file: ${fd.fullPath}`)
|
|
return _TVDOS.TMPFS[fd.path].dataAsString()
|
|
}
|
|
_TVDOS.DRV.FS.DEVTMP.bread = (fd) => {
|
|
if (_TVDOS.TMPFS[fd.path] === undefined) throw Error(`No such file: ${fd.fullPath}`)
|
|
return _TVDOS.TMPFS[fd.path].dataAsBytes()
|
|
}
|
|
_TVDOS.DRV.FS.DEVTMP.pread = (fd, ptr, count, offset) => {
|
|
if (_TVDOS.TMPFS[fd.path] === undefined) throw Error(`No such file: ${fd.fullPath}`)
|
|
let str = _TVDOS.TMPFS[fd.path].dataAsString()
|
|
for (let i = 0; i < count - (offset || 0); i++) {
|
|
sys.poke(ptr + i, String.charCodeAt(i + (offset || 0)))
|
|
}
|
|
}
|
|
|
|
_TVDOS.DRV.FS.DEVTMP.swrite = (fd, str) => {
|
|
_TVDOS.TMPFS[fd.path] = new PmemFSfile(str)
|
|
}
|
|
_TVDOS.DRV.FS.DEVTMP.bwrite = (fd, bytes) => {
|
|
_TVDOS.TMPFS[fd.path] = new PmemFSfile(bytes)
|
|
}
|
|
_TVDOS.DRV.FS.DEVTMP.pwrite = (fd, ptr, count, offset) => {
|
|
let appendstr = ""
|
|
for (let i = 0; i < count; i++) {
|
|
appendstr += String.fromCharCode(sys.peek(ptr + i))
|
|
}
|
|
let oldstr = (_TVDOS.TMPFS[fd.path] === undefined) ? "" : _TVDOS.TMPFS[fd.path].dataAsString().substring(0, offset || 0)
|
|
_TVDOS.TMPFS[fd.path] = new PmemFSfile(oldstr + appendstr)
|
|
}
|
|
|
|
_TVDOS.DRV.FS.DEVTMP.flush = (fd) => {}
|
|
_TVDOS.DRV.FS.DEVTMP.close = (fd) => {}
|
|
_TVDOS.DRV.FS.DEVTMP.isDirectory = (fd) => (_TVDOS.TMPFS[fd.path] != undefined && _TVDOS.TMPFS[fd.path] instanceof PmemFSdir)
|
|
_TVDOS.DRV.FS.DEVTMP.listFiles = (fd) => {
|
|
Object.keys(_TVDOS.TMPFS).filter(it => it.startsWith(fd.path)).map(it => new TVDOSFileDescriptor(`$:/TMP/${it}`))
|
|
}
|
|
_TVDOS.DRV.FS.DEVTMP.touch = (fd) => {}
|
|
_TVDOS.DRV.FS.DEVTMP.mkDir = (fd) => {
|
|
_TVDOS.TMPFS[fd.path] = new PmemFSdir(fd.path)
|
|
return true
|
|
}
|
|
_TVDOS.DRV.FS.DEVTMP.mkFile = (fd) => {
|
|
_TVDOS.TMPFS[fd.path] = new PmemFSfile("")
|
|
return true
|
|
}
|
|
_TVDOS.DRV.FS.DEVTMP.remove = (fd) => {
|
|
let path = _TVDOS.TMPFS[fd.path]
|
|
delete _TVDOS.TMPFS[fd.path]
|
|
|
|
// delete all the keys starting with _TVDOS.TMPFS[fd.path]
|
|
if (_TVDOS.DRV.FS.DEVTMP.isDirectory(fd)) {
|
|
Object.keys(_TVDOS.TMPFS).filter(it=>it.startsWith(path)).forEach(childs=>{
|
|
delete _TVDOS.TMPFS[childs]
|
|
})
|
|
}
|
|
|
|
return true
|
|
}
|
|
_TVDOS.DRV.FS.DEVTMP.exists = (fd) => (_TVDOS.TMPFS[fd.path] !== undefined)
|
|
|
|
Object.freeze(_TVDOS.DRV.FS.DEVTMP)
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
_TVDOS.DRV.FS.NET = {}
|
|
|
|
|
|
_TVDOS.DRV.FS.NET.sread = (fd) => {
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
let url = fd.path.substring(fd.path.indexOf("\\")+1).replaceAll("\\","/")
|
|
|
|
// serial.println("NETFILE GET " + url)
|
|
com.sendMessage(port[0], "GET " + url)
|
|
com.waitUntilReady(port[0])
|
|
|
|
if (com.getStatusCode(port[0]) == 0)
|
|
return com.pullMessage(port[0])
|
|
else
|
|
return null
|
|
}
|
|
|
|
_TVDOS.DRV.FS.NET.flush = () => {}
|
|
_TVDOS.DRV.FS.NET.close = () => {}
|
|
_TVDOS.DRV.FS.NET.isDirectory = () => false
|
|
_TVDOS.DRV.FS.NET.listFiles = () => undefined
|
|
_TVDOS.DRV.FS.NET.touch = () => {}
|
|
_TVDOS.DRV.FS.NET.mkDir = () => {}
|
|
_TVDOS.DRV.FS.NET.mkFile = () => {}
|
|
_TVDOS.DRV.FS.NET.remove = () => {}
|
|
_TVDOS.DRV.FS.NET.exists = (fd) => {
|
|
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
|
let url = fd.path.substring(fd.path.indexOf("\\")+1).replaceAll("\\","/")
|
|
|
|
com.sendMessage(port[0], "GET " + url)
|
|
com.waitUntilReady(port[0])
|
|
|
|
return (0 == com.getStatusCode(port[0]))
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// Legacy Serial filesystem, !!pending for removal!!
|
|
|
|
|
|
/*const 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 disk status code (0 for successful operation)
|
|
// 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]);
|
|
return com.getStatusCode(port[0]);
|
|
};
|
|
filesystem.getFileLen = (driveLetter) => {
|
|
var port = filesystem._toPorts(driveLetter);
|
|
com.sendMessage(port[0], "GETLEN");
|
|
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 Number(com.pullMessage(port[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.readAllBytes = (driveLetter) => {
|
|
var str = filesystem.readAll(driveLetter);
|
|
var bytes = new Uint8Array(str.length);
|
|
for (let i = 0; i < str.length; i++) {
|
|
bytes[i] = str.charCodeAt(i);
|
|
}
|
|
return bytes;
|
|
};
|
|
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.writeBytes = (driveLetter, bytes) => {
|
|
var string = btostr(bytes); // no spreading: has length limit
|
|
filesystem.write(driveLetter, string);
|
|
};
|
|
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);
|
|
};
|
|
filesystem.remove = (driveLetter) => {
|
|
var port = filesystem._toPorts(driveLetter);
|
|
com.sendMessage(port[0], "DELETE");
|
|
var response = com.getStatusCode(port[0]);
|
|
return (response === 0);
|
|
};
|
|
Object.freeze(filesystem);*/
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
const files = {}
|
|
|
|
files.reservedNames = ["AUX", // unused
|
|
"COM1","COM2","COM3","COM4", // serial ports
|
|
"CON", // the tty
|
|
"FB1","FB2", // raw framebuffer, 256 colours, typ. 560x448
|
|
"FBIPF", // framebuffer that accepts IPF-formatted bytes, reduced colours, typ. 560x448
|
|
"HFB1","HFB2","HFB3","HFB4", // half-dimension framebuffers, 256 colours, typ. 280x224
|
|
"LPT1","LPT2","LPT3","LPT4", // reserved for "parallel ports"; unused
|
|
"MEM", // /dev/mem
|
|
"NUL", // /dev/null
|
|
"PMEM0","PMEM1","PMEM2","PMEM3","PMEM4","PMEM5","PMEM6","PMEM7", // /dev/mem for peripherals
|
|
"PRN", // a printer
|
|
"RND", // /dev/urandom
|
|
"TMP", // tmp file that resides in the Program Memory
|
|
"XFB", // raw framebuffer, 4096 colours, typ. 560x448. Memory layout follows the gpu's (0..250779: RG-plane, 250880..262143: gap, 262144..513023: BA-plane)
|
|
"ZERO"] // /dev/zero
|
|
|
|
/** This function only creates a file descriptor; will not actually interact with the drives yet. */
|
|
files.open = (fullPath) => {
|
|
if (fullPath == undefined) throw Error("path is undefined")
|
|
|
|
if (files.reservedNames.includes(fullPath.toUpperCase())) {
|
|
return new TVDOSFileDescriptor(fullPath.toUpperCase())
|
|
}
|
|
|
|
if (fullPath[2] != '/' && fullPath[2] != '\\') throw Error("Expected full path with drive letter, got " + fullPath)
|
|
let driveLetter = fullPath[0].toUpperCase()
|
|
let driver = _TVDOS.DRIVEFS[driveLetter]
|
|
|
|
// special device files ($:\NUL, $:\CON, etc)
|
|
if ("$" == driveLetter) {
|
|
return new TVDOSFileDescriptor(fullPath.toUpperCase())
|
|
}
|
|
|
|
return new TVDOSFileDescriptor(fullPath, driver)
|
|
}
|
|
|
|
|
|
Object.freeze(files)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
const input = {};
|
|
const inputwork = {};
|
|
|
|
inputwork.keymap = [];
|
|
|
|
input.changeKeyLayout = function(name) {
|
|
let f = files.open(`A:/tvdos/${name.toLowerCase()}.key`)
|
|
let res0 = f.sread().trim()
|
|
if (res0.length == 0 && inputwork.keymap.length == 0) throw new Error(`I/O Error on ${f.fullPath}`)
|
|
try {
|
|
inputwork.keymap = eval(res0)
|
|
}
|
|
catch (e) {
|
|
printerrln(e)
|
|
return -1
|
|
}
|
|
}
|
|
|
|
// load initial key layout
|
|
input.changeKeyLayout(_TVDOS.variables.KEYBOARD || "us_qwerty");
|
|
|
|
// states to run the keyboard input
|
|
inputwork.stroboTime = 4294967296*0;
|
|
inputwork.stroboDelays = [0,250000000,0,25000000,0]; // 250ms, 25ms
|
|
inputwork.stroboStatus = 0; // 0: first key, 1: waiting for initial delay, 2: repeating key, 3: waiting for repeat delay
|
|
inputwork.oldKeys = [];
|
|
inputwork.oldMouse = [];
|
|
inputwork.repeatCount = 0;
|
|
//inputwork.keyChanged = false;
|
|
|
|
/**
|
|
* callback: takes one argument of object which
|
|
* [ <eventname>, args ]
|
|
* where:
|
|
* "key_down", <key symbol string>, <repeat count>, keycode0, keycode1 .. keycode7
|
|
* "key_change", <key symbol string (what went up)>, 0, keycode0, keycode1 .. keycode7 (remaining keys that are held down)
|
|
* "mouse_down", pos-x, pos-y, 1 // yes there's only one mouse button :p
|
|
* "mouse_up", pos-x, pos-y, 0
|
|
* "mouse_move", pos-x, pos-y, <button down?>, oldpos-x, oldpos-y
|
|
*/
|
|
input.withEvent = function(callback) {
|
|
|
|
// TODO mouse event
|
|
function arrayEq(a,b) {
|
|
for (let i = 0; i < a.length; ++i) {
|
|
if (a[i] !== b[i]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function arrayDiff(a,b) {
|
|
return a.filter(x => !b.includes(x));
|
|
}
|
|
|
|
function keysToStr(keys) {
|
|
//let shiftin = keys.includes(59) || keys.includes(60);
|
|
let headkey = keys.head();
|
|
//return (inputwork.keymap[headkey] == undefined) ? undefined : (shiftin) ? (inputwork.keymap[headkey][1] || inputwork.keymap[headkey][0]) : inputwork.keymap[headkey][0];
|
|
return (inputwork.keymap[headkey] == undefined) ? undefined : inputwork.keymap[headkey];
|
|
}
|
|
|
|
sys.poke(-40, 255);
|
|
let keys = [sys.peek(-41),sys.peek(-42),sys.peek(-43),sys.peek(-44),sys.peek(-45),sys.peek(-46),sys.peek(-47),sys.peek(-48)];
|
|
let mouse = [sys.peek(-33) | (sys.peek(-34) << 8), sys.peek(-35) | (sys.peek(-36) << 8), sys.peek(-37)];
|
|
let keyChanged = !arrayEq(keys, inputwork.oldKeys)
|
|
let keyDiff = arrayDiff(keys, inputwork.oldKeys)
|
|
|
|
// FIXME does not work with QMK's "LT(1,KC_ENT)" or something similar
|
|
//if (keys.sum() > 0) serial.println(`! captured=${keys.join()}`)
|
|
|
|
if (inputwork.stroboStatus % 2 == 0 && keys[0] != 0) {
|
|
inputwork.stroboStatus += 1;
|
|
inputwork.stroboTime = sys.nanoTime();
|
|
inputwork.repeatCount += 1;
|
|
|
|
let shiftin = keys.includes(59) || keys.includes(60);
|
|
let keysym0 = keysToStr(keys);
|
|
let newKeysym0 = keysToStr(keyDiff);
|
|
let keysym = (keysym0 === undefined) ? undefined : (shiftin && keysym0[1]) ? keysym0[1] : keysym0[0];
|
|
let newKeysym = (newKeysym0 === undefined) ? undefined : (shiftin && newKeysym0[1]) ? newKeysym0[1] : newKeysym0[0];
|
|
|
|
|
|
//serial.println(`keys=${keys.join()}; oldkeys=${inputwork.oldKeys}; keyDiff=${keyDiff}`)
|
|
//serial.println(`keysym=${keysym}; newkeysym=${newKeysym}`)
|
|
|
|
if (!keyChanged) {
|
|
callback(["key_down", keysym, inputwork.repeatCount].concat(keys));
|
|
}
|
|
else if (newKeysym !== undefined) {
|
|
callback(["key_down", newKeysym, inputwork.repeatCount].concat(keys));
|
|
}
|
|
|
|
|
|
inputwork.oldKeys = keys; // don't put this outside of if-cascade
|
|
}
|
|
else if (keyChanged || keys[0] == 0) {
|
|
inputwork.stroboStatus = 0;
|
|
inputwork.repeatCount = 0;
|
|
|
|
if (keys[0] == 0) {
|
|
inputwork.keyChanged = false;
|
|
sys.sleep(25)
|
|
}
|
|
|
|
if (keyChanged) {
|
|
//callback(["key_change", keysToStr(arrayDiff(keys, inputwork.oldKeys)), inputwork.repeatCount].concat(keys));
|
|
|
|
//serial.println(`$ oldkeys=${inputwork.oldKeys}; newkeys=${keys}`)
|
|
//serial.println(`$ keydiff=${arrayDiff(keys, inputwork.oldKeys)}; newkeysym=${keysToStr(arrayDiff(keys, inputwork.oldKeys))}`)
|
|
|
|
//inputwork.keyChanged = keysToStr(arrayDiff(keys, inputwork.oldKeys));
|
|
}
|
|
}
|
|
else if (inputwork.stroboStatus % 2 == 1 && sys.nanoTime() - inputwork.stroboTime < inputwork.stroboDelays[inputwork.stroboStatus]) {
|
|
sys.sleep(25);
|
|
}
|
|
else {
|
|
inputwork.stroboStatus += 1;
|
|
if (inputwork.stroboStatus >= 4) {
|
|
inputwork.stroboStatus = 2;
|
|
}
|
|
}
|
|
|
|
inputwork.oldMouse = mouse;
|
|
|
|
};
|
|
|
|
Object.freeze(input);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
const unicode = {};
|
|
unicode.utf8toCodepoints = function(utf8text) {
|
|
let UTF8_ACCEPT = 0
|
|
let UTF8D = [
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
|
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
|
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
|
|
0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
|
|
12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
|
|
12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
|
|
12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
|
|
12,36,12,12,12,12,12,12,12,12,12,12,
|
|
]
|
|
|
|
let state = UTF8_ACCEPT
|
|
let codep = 0
|
|
let codepoints = []
|
|
|
|
for (let i=0; i < utf8text.length; i++) {
|
|
let byte = utf8text.charCodeAt(i)
|
|
let type = UTF8D[byte]
|
|
codep = (state != UTF8_ACCEPT) ?
|
|
(byte & 0x3f) | (codep << 6) : (0xff >> type) & (byte)
|
|
state = UTF8D[256 + state + type]
|
|
if (state == UTF8_ACCEPT)
|
|
codepoints.push(codep)
|
|
}
|
|
return codepoints
|
|
}
|
|
|
|
// array of array: [predicate_for_coderange: (Codepoint) -> Boolean , printing_function: (Codepoint) -> Unit_that_does_screen_drawing]
|
|
// Usage: unicode.uniprint.push[(c) => 0xAC00 <= c && c <= 0xD7A3, printHalfRowHangul]
|
|
unicode.uniprint = [];
|
|
unicode.uniprint.push([c => (0 <= c && c <= 255), c => { sys.print(String.fromCodePoint(c)) }]);
|
|
// @return [predicate, printing function]
|
|
unicode.getUniprint = (c) => {
|
|
for (let k = 0; k < unicode.uniprint.length; k++) {
|
|
if (unicode.uniprint[k][0](c))
|
|
return unicode.uniprint[k]
|
|
}}
|
|
|
|
unicode.print = (str) => {
|
|
if ((typeof str === 'string' || str instanceof String) && str.length > 0) {
|
|
|
|
let cp = unicode.utf8toCodepoints(str)
|
|
cp.forEach(c => {
|
|
let q = unicode.getUniprint(c)
|
|
if (q == undefined || !q[0](c)) {
|
|
con.addch(4)
|
|
con.curs_right()
|
|
}
|
|
else {
|
|
q[1](c)
|
|
}
|
|
})
|
|
}
|
|
else {
|
|
sys.print(str)
|
|
}
|
|
}
|
|
|
|
unicode.println = (str) => {
|
|
unicode.print(str+'\n\n')
|
|
}
|
|
|
|
unicode.strlen = (str) => {
|
|
// Convert string to an array of codepoints using spread operator
|
|
// This correctly handles surrogate pairs and counts each codepoint as one
|
|
return unicode.utf8toCodepoints(str).length
|
|
}
|
|
|
|
unicode.visualStrlen = (str) => {
|
|
function isTripleWidth(c) {
|
|
return (0xAC00 <= c && c <= 0xD7FF) && [1,4,8,10,13].includes(((c - 0xAC00) / 588)|0)
|
|
}
|
|
|
|
function isDoubleWidth(c) {
|
|
return (0x3000 <= c && c <= 0x303f) || (0x3100 <= c && c <= 0x312f) || (0x3200 <= c && c <= 0x33ff) ||
|
|
(0xAC00 <= c && c <= 0xD7FF) || (0xFE30 <= c && c <= 0xFE4F) || (0xFF00 <= c && c <= 0xff60)
|
|
}
|
|
|
|
// Convert string to an array of codepoints using spread operator
|
|
// This correctly handles surrogate pairs and counts each codepoint as one
|
|
return unicode.utf8toCodepoints(str).reduce((acc, c) => acc + (isTripleWidth(c) ? 3 : isDoubleWidth(c) ? 2 : 1), 0)
|
|
}
|
|
|
|
Object.freeze(unicode);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// patch con to use VTs
|
|
/*con.move = function(y, x) {
|
|
let activeVT = _TVDOS.ACTIVE_VT || 0 // 0 is physical terminal
|
|
let vt = _TVDOS.VT_CONTEXTS[activeVT]
|
|
|
|
vt.setCursorYX(y|0, x|0)
|
|
};
|
|
|
|
con.getyx = function() {
|
|
let activeVT = _TVDOS.ACTIVE_VT || 0 // 0 is physical terminal
|
|
let vt = _TVDOS.VT_CONTEXTS[activeVT]
|
|
|
|
return vt.getCursorYX()
|
|
};*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
let checkTerm = `if (sys.peek(-49)&1) throw new InterruptedException();`
|
|
let injectIntChk = (s, n) => {
|
|
// primitive way of injecting a code; will replace a JS string that matches the regex...
|
|
let k = s
|
|
.replace(/while *\([^\n]+\) *{/, "$& "+n+"();")
|
|
.replace(/for *\([^\n]+\) *{/, "$& "+n+"();")
|
|
.replace(/do *{/, "$& "+n+"();");
|
|
//serial.println(k);
|
|
return k;
|
|
}
|
|
|
|
var _G = {} // The Glorious Global Table
|
|
|
|
const moduleCache = {}
|
|
|
|
// install other stuffs
|
|
// js source-based impl
|
|
var require = (absdir) => {
|
|
let moduleFile = files.open(absdir)
|
|
if (!moduleFile.exists) throw Error("No such file: " + absdir)
|
|
let moduleScript = moduleFile.sread()
|
|
var intchkFunName = `tvdosSIGTERM_${generateRandomHashStr(16)}`;
|
|
return eval(`var ${intchkFunName} = function(){ ${checkTerm} };let exports = {}; ${injectIntChk(moduleScript, intchkFunName)}; Object.freeze(exports)`)
|
|
}
|
|
|
|
// COCC-based impl helper functions
|
|
let loadImgToMem = (str) => {
|
|
let intent = str.charCodeAt(4) // 2 for executable, 3 for shared
|
|
let addrToLoad = (str.charCodeAt(5) << 16) | (str.charCodeAt(6) << 8) | (str.charCodeAt(7))
|
|
const imageSize = (str.charCodeAt(9) << 16) | (str.charCodeAt(10) << 8) | (str.charCodeAt(11))
|
|
|
|
if (addrToLoad == 0)
|
|
addrToLoad = sys.malloc(imageSize + 4)
|
|
else
|
|
sys.forceAlloc(addrToLoad, imageSize + 4)
|
|
|
|
dma.strToRam(str, addrToLoad, 0, str.length)
|
|
|
|
// write magic 0xA5 to the beginning of the image area
|
|
sys.poke(addrToLoad, 0xA5)
|
|
|
|
return addrToLoad
|
|
}
|
|
let requireFromMemory = (ptr) => {
|
|
if (moduleCache[ptr]) return moduleCache[ptr]
|
|
|
|
const codeStr = sys.toObjectCode(ptr)
|
|
|
|
// expose args through a wrapper -- just like calling a syscall
|
|
const wrapper = new Function("require", "module", "exports", "args", `
|
|
${codeStr}
|
|
if (typeof module.exports === 'function') {
|
|
return module.exports(...args)
|
|
}
|
|
return module.exports
|
|
`)
|
|
|
|
const moduleRunner = (...args) => {
|
|
const exports = {}
|
|
const module = { exports }
|
|
return wrapper((id) => requireFromMemory(id), module, exports, args);
|
|
}
|
|
|
|
moduleCache[moduleId] = moduleRunner
|
|
return moduleRunner
|
|
}
|
|
// COCC-based impl
|
|
/*var require = (absdir) => {
|
|
let moduleFile = files.open(absdir)
|
|
if (!moduleFile.exists) throw Error("No such file: " + absdir)
|
|
let moduleScript = moduleFile.sread()
|
|
let moduleName = absdir.split("\\").last().substringBeforeLast(".")
|
|
|
|
// load the "string" into memory
|
|
let ptr = loadImgToMem(moduleScript)
|
|
let intent = sys.peek(ptr + 4)
|
|
|
|
// if it's a shared library, put it into the global table
|
|
if (3 == intent) {
|
|
// create the table if it's not there
|
|
if (!_G.SO)
|
|
_G.SO = {}
|
|
|
|
_G.SO[moduleName] = ptr
|
|
}
|
|
|
|
requireFromMemory(ptr)
|
|
}*/
|
|
|
|
|
|
var GL = require("A:/tvdos/include/gl.mjs")
|
|
|
|
|
|
// @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'
|
|
// @return status returned by the program
|
|
var execApp = (cmdsrc, args, appname) => {
|
|
appname = (appname) ? `_${appname}` : "_appStub"
|
|
var intchkFunName = `tvdosSIGTERM_${generateRandomHashStr(16)}`;
|
|
var execAppPrg = eval(
|
|
`var ${intchkFunName} = function(){ ${checkTerm} };` +
|
|
`var ${appname}=function(exec_args){${injectIntChk(cmdsrc, intchkFunName)}\n};` +
|
|
`${appname}`); // making 'exec_args' a app-level global
|
|
|
|
execAppPrg(args);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Load HSDPA driver
|
|
try {
|
|
let hsdpadrvFile = files.open("A:/tvdos/HSDPADRV.SYS")
|
|
if (hsdpadrvFile.exists) {
|
|
eval(hsdpadrvFile.sread())
|
|
}
|
|
} catch (e) {
|
|
serial.println("Warning: Could not load HSDPA driver: " + e.message)
|
|
}
|
|
|
|
// Boot script
|
|
serial.println(`TVDOS.SYS initialised on VM ${sys.getVmId()}, running boot script...`);
|
|
|
|
let cmdfile = files.open("A:/tvdos/bin/command.js")
|
|
eval(`var _AUTOEXEC=function(exec_args){${cmdfile.sread()}\n};` +
|
|
`_AUTOEXEC`)(["", "-c", "\\AUTOEXEC.BAT"])
|