// 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); 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 : [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) } // probe filesystem devices let devnameToDriver = {"PRNT":"LP","STOR":"SERIAL","COMM":"COM","HTTP":"NET"} 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;\\tbas;\\home", PATHEXT: ".com;.bat;.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 devName = path0.substring(3) 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("/", "\\") // oh well... while (p.endsWith("\\")) { p = p.substring(0, p.length - 1) } this._driveLetter = p[0] this._path = p.substring(2) // detaches $: this._driverID = driverID this._driver = _TVDOS.DRV.FS[driverID] // serial.println(`TVDOSFileDescriptor input path: ${path0}, p = ${p}, driveLetter = ${this._driveLetter}, path = ${this._path}`) } } 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 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 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 Uint8Array(str.length) for (let i = 0; i < str.length; i++) { 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 = String.fromCharCode.apply(null, 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 = String.fromCharCode.apply(null, 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} [ \x1E {\x11 | \x12} ] \x17 let fdsInDir = [] let parentPath = fd.fullPath rawStr.substring(0, rawStr.length).split('\x1E').forEach(it => { let filename = it.substring(1) let newfd = new TVDOSFileDescriptor(`${parentPath}/${filename}`, "SERIAL") fdsInDir.push(newfd) }) return fdsInDir } _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 Uint8Array(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.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 Uint8Array(consize) for (let i = 0; i < consize; i++) { 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 = String.fromCharCode.apply(null, 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) /////////////////////////////////////////////////////////////////////////////// _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.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 = String.fromCharCode.apply(null, 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 "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 (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 * [ , args ] * where: * "key_down", , , keycode0, keycode1 .. keycode7 * "key_change", , 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,