mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-10 05:01:50 +09:00
HSDPA and driver implementation
This commit is contained in:
271
assets/disk0/tvdos/HSDPADRV.SYS
Normal file
271
assets/disk0/tvdos/HSDPADRV.SYS
Normal file
@@ -0,0 +1,271 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// High Speed Disk Peripheral Adapter (HSDPA) Driver for TVDOS
|
||||
// This driver treats each disk from HSDPA as a single large file
|
||||
// Created by Claude on 2025-08-16
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add TAPE device names to reserved names
|
||||
files.reservedNames.push("TAPE0", "TAPE1", "TAPE2", "TAPE3")
|
||||
|
||||
// HSDPA peripheral addresses for slot 4
|
||||
// MMIO area (for registers): -524289 to -655360 (backwards!)
|
||||
// Memory Space area (for buffer): -4194305 to -5242880 (backwards!)
|
||||
const HSDPA_SLOT4_MMIO_START = -524289 // MMIO area start
|
||||
const HSDPA_REG_DISK1_STATUS = HSDPA_SLOT4_MMIO_START - 0
|
||||
const HSDPA_REG_DISK2_STATUS = HSDPA_SLOT4_MMIO_START - 3
|
||||
const HSDPA_REG_DISK3_STATUS = HSDPA_SLOT4_MMIO_START - 6
|
||||
const HSDPA_REG_DISK4_STATUS = HSDPA_SLOT4_MMIO_START - 9
|
||||
const HSDPA_REG_DISK_CONTROL = HSDPA_SLOT4_MMIO_START - 12
|
||||
const HSDPA_REG_ACTIVE_DISK = HSDPA_SLOT4_MMIO_START - 20
|
||||
const HSDPA_REG_SEQ_IO_CONTROL = HSDPA_SLOT4_MMIO_START - 256
|
||||
const HSDPA_REG_SEQ_IO_OPCODE = HSDPA_SLOT4_MMIO_START - 258
|
||||
const HSDPA_REG_SEQ_IO_ARG1 = HSDPA_SLOT4_MMIO_START - 259
|
||||
const HSDPA_REG_SEQ_IO_ARG2 = HSDPA_SLOT4_MMIO_START - 262
|
||||
|
||||
// Sequential I/O opcodes
|
||||
const HSDPA_OPCODE_NOP = 0x00
|
||||
const HSDPA_OPCODE_SKIP = 0x01
|
||||
const HSDPA_OPCODE_READ = 0x02
|
||||
const HSDPA_OPCODE_WRITE = 0x03
|
||||
const HSDPA_OPCODE_REWIND = 0xF0
|
||||
const HSDPA_OPCODE_TERMINATE = 0xFF
|
||||
|
||||
// Helper functions for HSDPA MMIO access
|
||||
const hsdpa = {}
|
||||
|
||||
hsdpa.setActiveDisk = (diskNumber) => {
|
||||
sys.poke(HSDPA_REG_ACTIVE_DISK, diskNumber) // 1-based
|
||||
}
|
||||
|
||||
hsdpa.getActiveDisk = () => {
|
||||
return sys.peek(HSDPA_REG_ACTIVE_DISK)
|
||||
}
|
||||
|
||||
hsdpa.enableSequentialIO = () => {
|
||||
sys.poke(HSDPA_REG_SEQ_IO_CONTROL, 0x01)
|
||||
}
|
||||
|
||||
hsdpa.disableSequentialIO = () => {
|
||||
sys.poke(HSDPA_REG_SEQ_IO_CONTROL, 0x00)
|
||||
}
|
||||
|
||||
hsdpa.rewind = () => {
|
||||
sys.poke(HSDPA_REG_SEQ_IO_OPCODE, HSDPA_OPCODE_REWIND)
|
||||
}
|
||||
|
||||
hsdpa.skip = (bytes) => {
|
||||
// Write arg1 (3 bytes, little endian) - using backwards addressing
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1, bytes & 0xFF) // LSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 1, (bytes >> 8) & 0xFF) // MSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 2, (bytes >> 16) & 0xFF) // MSB2
|
||||
// Execute skip operation
|
||||
sys.poke(HSDPA_REG_SEQ_IO_OPCODE, HSDPA_OPCODE_SKIP)
|
||||
}
|
||||
|
||||
hsdpa.readToMemory = (bytes, vmMemoryPointer) => {
|
||||
serial.println(`HSDPA: readToMemory(${bytes}, ${vmMemoryPointer})`)
|
||||
// Write arg1 (bytes to read) - using backwards addressing
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1, bytes & 0xFF) // LSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 1, (bytes >> 8) & 0xFF) // MSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 2, (bytes >> 16) & 0xFF) // MSB2
|
||||
// Write arg2 (VM memory pointer) - using backwards addressing
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG2, vmMemoryPointer & 0xFF) // LSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG2 - 1, (vmMemoryPointer >> 8) & 0xFF) // MSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG2 - 2, (vmMemoryPointer >> 16) & 0xFF) // MSB2
|
||||
// Execute read operation
|
||||
serial.println(`HSDPA: Executing read opcode...`)
|
||||
sys.poke(HSDPA_REG_SEQ_IO_OPCODE, HSDPA_OPCODE_READ)
|
||||
serial.println(`HSDPA: Read opcode executed`)
|
||||
}
|
||||
|
||||
hsdpa.writeFromMemory = (bytes, vmMemoryPointer) => {
|
||||
// Write arg1 (bytes to write) - using backwards addressing
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1, bytes & 0xFF) // LSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 1, (bytes >> 8) & 0xFF) // MSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 2, (bytes >> 16) & 0xFF) // MSB2
|
||||
// Write arg2 (VM memory pointer) - using backwards addressing
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG2, vmMemoryPointer & 0xFF) // LSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG2 - 1, (vmMemoryPointer >> 8) & 0xFF) // MSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG2 - 2, (vmMemoryPointer >> 16) & 0xFF) // MSB2
|
||||
// Execute write operation
|
||||
sys.poke(HSDPA_REG_SEQ_IO_OPCODE, HSDPA_OPCODE_WRITE)
|
||||
}
|
||||
|
||||
hsdpa.terminate = () => {
|
||||
sys.poke(HSDPA_REG_SEQ_IO_OPCODE, HSDPA_OPCODE_TERMINATE)
|
||||
}
|
||||
|
||||
// Helper to get disk number from device name (TAPE0 -> 1, TAPE1 -> 2, etc.)
|
||||
hsdpa.getHsdpaDiskNumber = (deviceName) => {
|
||||
if (deviceName.startsWith("TAPE")) {
|
||||
let diskIndex = parseInt(deviceName.substring(4))
|
||||
return diskIndex + 1 // HSDPA uses 1-based disk numbers
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// State tracking for open files
|
||||
hsdpa.openFiles = {}
|
||||
|
||||
// Create TAPE device drivers
|
||||
for (let tapeIndex = 0; tapeIndex < 4; tapeIndex++) {
|
||||
let deviceName = `DEVTAPE${tapeIndex}`
|
||||
|
||||
_TVDOS.DRV.FS[deviceName] = {}
|
||||
|
||||
let driver = _TVDOS.DRV.FS[deviceName]
|
||||
|
||||
// Get file size - for HSDPA tapes, we don't know the size ahead of time
|
||||
// So we return a very large number to indicate it's available
|
||||
driver.getFileLen = (fd) => {
|
||||
return 0x7FFFFFFF // Return max positive 32-bit integer
|
||||
}
|
||||
|
||||
// Sequential read from tape
|
||||
driver.pread = (fd, ptr, count, offset) => {
|
||||
let diskNumber = hsdpa.getHsdpaDiskNumber(fd._driverID.substring(3)) // Remove "DEV" prefix
|
||||
|
||||
// Set active disk
|
||||
hsdpa.setActiveDisk(diskNumber)
|
||||
hsdpa.enableSequentialIO()
|
||||
|
||||
try {
|
||||
// If offset specified, rewind and skip to position
|
||||
if (offset && offset > 0) {
|
||||
hsdpa.rewind()
|
||||
hsdpa.skip(offset)
|
||||
}
|
||||
|
||||
// Read data to VM memory
|
||||
hsdpa.readToMemory(count, ptr)
|
||||
} catch (e) {
|
||||
console.error("HSDPA read error:", e)
|
||||
// Fill with zeros on error
|
||||
for (let i = 0; i < count; i++) {
|
||||
sys.poke(ptr + i, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Binary read - reads entire file into byte array
|
||||
driver.bread = (fd) => {
|
||||
let diskNumber = hsdpa.getHsdpaDiskNumber(fd._driverID.substring(3))
|
||||
|
||||
// For simplicity, read a reasonable chunk size
|
||||
let chunkSize = 65536 // 64KB
|
||||
let tempPtr = sys.malloc(chunkSize)
|
||||
|
||||
try {
|
||||
hsdpa.setActiveDisk(diskNumber)
|
||||
hsdpa.enableSequentialIO()
|
||||
hsdpa.rewind()
|
||||
hsdpa.readToMemory(chunkSize, tempPtr)
|
||||
|
||||
// Copy to JavaScript array
|
||||
let result = new Int8Array(chunkSize)
|
||||
for (let i = 0; i < chunkSize; i++) {
|
||||
result[i] = sys.peek(tempPtr + i)
|
||||
}
|
||||
|
||||
return result
|
||||
} finally {
|
||||
sys.free(tempPtr)
|
||||
}
|
||||
}
|
||||
|
||||
// String read - reads file as string
|
||||
driver.sread = (fd) => {
|
||||
let bytes = driver.bread(fd)
|
||||
let result = ""
|
||||
for (let i = 0; i < bytes.length && bytes[i] !== 0; i++) {
|
||||
result += String.fromCharCode(bytes[i] & 0xFF)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Write operations (not supported for tape devices)
|
||||
driver.pwrite = (fd, ptr, count, offset) => {
|
||||
throw Error("Write operations not supported on HSDPA tape devices")
|
||||
}
|
||||
|
||||
driver.bwrite = (fd, bytes) => {
|
||||
throw Error("Write operations not supported on HSDPA tape devices")
|
||||
}
|
||||
|
||||
driver.swrite = (fd, string) => {
|
||||
throw Error("Write operations not supported on HSDPA tape devices")
|
||||
}
|
||||
|
||||
driver.pappend = (fd, ptr, count) => {
|
||||
throw Error("Append operations not supported on HSDPA tape devices")
|
||||
}
|
||||
|
||||
driver.bappend = (fd, bytes) => {
|
||||
throw Error("Append operations not supported on HSDPA tape devices")
|
||||
}
|
||||
|
||||
driver.sappend = (fd, string) => {
|
||||
throw Error("Append operations not supported on HSDPA tape devices")
|
||||
}
|
||||
|
||||
// File operations
|
||||
driver.flush = (fd) => {
|
||||
// No-op for read-only tape
|
||||
}
|
||||
|
||||
driver.close = (fd) => {
|
||||
let diskNumber = hsdpa.getHsdpaDiskNumber(fd._driverID.substring(3))
|
||||
hsdpa.setActiveDisk(diskNumber)
|
||||
hsdpa.terminate()
|
||||
hsdpa.disableSequentialIO()
|
||||
|
||||
// Remove from open files tracking
|
||||
delete hsdpa.openFiles[fd._driverID]
|
||||
}
|
||||
|
||||
// Directory operations (tapes are single files, not directories)
|
||||
driver.isDirectory = (fd) => {
|
||||
return false
|
||||
}
|
||||
|
||||
driver.listFiles = (fd) => {
|
||||
return undefined // Not a directory
|
||||
}
|
||||
|
||||
// File management operations (not supported)
|
||||
driver.touch = (fd) => {
|
||||
throw Error("Touch operation not supported on HSDPA tape devices")
|
||||
}
|
||||
|
||||
driver.mkDir = (fd) => {
|
||||
throw Error("Directory creation not supported on HSDPA tape devices")
|
||||
}
|
||||
|
||||
driver.mkFile = (fd) => {
|
||||
throw Error("File creation not supported on HSDPA tape devices")
|
||||
}
|
||||
|
||||
driver.remove = (fd) => {
|
||||
throw Error("File removal not supported on HSDPA tape devices")
|
||||
}
|
||||
|
||||
// Check if tape exists (always true if disk is attached)
|
||||
driver.exists = (fd) => {
|
||||
let diskNumber = hsdpa.getHsdpaDiskNumber(fd._driverID.substring(3))
|
||||
|
||||
// Try to set active disk and check if it responds
|
||||
try {
|
||||
hsdpa.setActiveDisk(diskNumber)
|
||||
// If we can read the active disk register back, the disk exists
|
||||
return hsdpa.getActiveDisk() === diskNumber
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Freeze the driver object
|
||||
Object.freeze(driver)
|
||||
}
|
||||
|
||||
// Print initialization message
|
||||
serial.println("HSDPA Driver loaded: TAPE0-TAPE3 devices available at $:/TAPE0/, $:/TAPE1/, $:/TAPE2/, $:/TAPE3/")
|
||||
@@ -1388,7 +1388,7 @@ Object.freeze(unicode);
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// patch con to use VTs
|
||||
con.move = function(y, x) {
|
||||
/*con.move = function(y, x) {
|
||||
let activeVT = _TVDOS.ACTIVE_VT || 0 // 0 is physical terminal
|
||||
let vt = _TVDOS.VT_CONTEXTS[activeVT]
|
||||
|
||||
@@ -1400,7 +1400,7 @@ con.getyx = function() {
|
||||
let vt = _TVDOS.VT_CONTEXTS[activeVT]
|
||||
|
||||
return vt.getCursorYX()
|
||||
};
|
||||
};*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1516,6 +1516,16 @@ var execApp = (cmdsrc, args, appname) => {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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...`);
|
||||
|
||||
|
||||
@@ -17,24 +17,40 @@ graphics.clearPixels(255)
|
||||
graphics.clearPixels2(240)
|
||||
|
||||
|
||||
let seqread = require("seqread")
|
||||
seqread.prepare(fullFilePath.full)
|
||||
let seqreadserial = require("seqread")
|
||||
let seqreadtape = require("seqreadtape")
|
||||
let seqread = undefined
|
||||
let fullFilePathStr = fullFilePath.full
|
||||
|
||||
// select seqread driver to use
|
||||
if (fullFilePathStr.startsWith('$:/TAPE') || fullFilePathStr.startsWith('$:\\TAPE')) {
|
||||
seqread = seqreadtape
|
||||
seqread.seek(0)
|
||||
}
|
||||
else {
|
||||
seqread = seqreadserial
|
||||
}
|
||||
|
||||
seqread.prepare(fullFilePathStr)
|
||||
|
||||
|
||||
|
||||
let magic = seqread.readBytes(8)
|
||||
let magicMatching = true
|
||||
|
||||
let actualMagic = []
|
||||
|
||||
// check if magic number matches
|
||||
MAGIC.forEach((b,i) => {
|
||||
let testb = sys.peek(magic + i) & 255 // for some reason this must be located here
|
||||
actualMagic.push(testb)
|
||||
if (testb != b) {
|
||||
magicMatching = false
|
||||
}
|
||||
})
|
||||
sys.free(magic)
|
||||
if (!magicMatching) {
|
||||
println("Not a movie file (MAGIC mismatch)")
|
||||
println("Not a movie file (MAGIC mismatch) -- got " + actualMagic.join())
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
263
assets/disk0/tvdos/include/seqreadtape.mjs
Normal file
263
assets/disk0/tvdos/include/seqreadtape.mjs
Normal file
@@ -0,0 +1,263 @@
|
||||
// Sequential reader for HSDPA TAPE devices
|
||||
// Unlike seqread.mjs which is limited to 4096 bytes per read due to serial communication,
|
||||
// this module can read larger chunks efficiently from HSDPA devices.
|
||||
|
||||
let readCount = 0
|
||||
let currentTapeDevice = undefined
|
||||
let fileDescriptor = undefined
|
||||
let fileHeader = new Uint8Array(65536) // Much larger header buffer than serial version
|
||||
|
||||
// HSDPA peripheral addresses for slot 4
|
||||
// MMIO area (for registers): -524289 to -655360 (backwards!)
|
||||
const HSDPA_SLOT4_MMIO_START = -524289 // MMIO area start
|
||||
const HSDPA_REG_ACTIVE_DISK = HSDPA_SLOT4_MMIO_START - 20
|
||||
const HSDPA_REG_SEQ_IO_CONTROL = HSDPA_SLOT4_MMIO_START - 256
|
||||
const HSDPA_REG_SEQ_IO_OPCODE = HSDPA_SLOT4_MMIO_START - 258
|
||||
const HSDPA_REG_SEQ_IO_ARG1 = HSDPA_SLOT4_MMIO_START - 259
|
||||
const HSDPA_REG_SEQ_IO_ARG2 = HSDPA_SLOT4_MMIO_START - 262
|
||||
|
||||
// Sequential I/O opcodes
|
||||
const HSDPA_OPCODE_NOP = 0x00
|
||||
const HSDPA_OPCODE_SKIP = 0x01
|
||||
const HSDPA_OPCODE_READ = 0x02
|
||||
const HSDPA_OPCODE_WRITE = 0x03
|
||||
const HSDPA_OPCODE_REWIND = 0xF0
|
||||
const HSDPA_OPCODE_TERMINATE = 0xFF
|
||||
|
||||
// Helper functions for HSDPA MMIO access
|
||||
function hsdpaSetActiveDisk(diskNumber) {
|
||||
sys.poke(HSDPA_REG_ACTIVE_DISK, diskNumber) // 1-based
|
||||
}
|
||||
|
||||
function hsdpaEnableSequentialIO() {
|
||||
sys.poke(HSDPA_REG_SEQ_IO_CONTROL, 0x01)
|
||||
}
|
||||
|
||||
function hsdpaDisableSequentialIO() {
|
||||
sys.poke(HSDPA_REG_SEQ_IO_CONTROL, 0x00)
|
||||
}
|
||||
|
||||
function hsdpaRewind() {
|
||||
sys.poke(HSDPA_REG_SEQ_IO_OPCODE, HSDPA_OPCODE_REWIND)
|
||||
}
|
||||
|
||||
function hsdpaSkip(bytes) {
|
||||
// Write arg1 (3 bytes, little endian) - using backwards addressing
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1, bytes & 0xFF) // LSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 1, (bytes >> 8) & 0xFF) // MSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 2, (bytes >> 16) & 0xFF) // MSB2
|
||||
// Execute skip operation
|
||||
sys.poke(HSDPA_REG_SEQ_IO_OPCODE, HSDPA_OPCODE_SKIP)
|
||||
}
|
||||
|
||||
function hsdpaReadToMemory(bytes, vmMemoryPointer) {
|
||||
// Write arg1 (bytes to read) - using backwards addressing
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1, bytes & 0xFF) // LSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 1, (bytes >> 8) & 0xFF) // MSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 2, (bytes >> 16) & 0xFF) // MSB2
|
||||
// Write arg2 (VM memory pointer) - using backwards addressing
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG2, vmMemoryPointer & 0xFF) // LSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG2 - 1, (vmMemoryPointer >> 8) & 0xFF) // MSB
|
||||
sys.poke(HSDPA_REG_SEQ_IO_ARG2 - 2, (vmMemoryPointer >> 16) & 0xFF) // MSB2
|
||||
// Execute read operation
|
||||
sys.poke(HSDPA_REG_SEQ_IO_OPCODE, HSDPA_OPCODE_READ)
|
||||
}
|
||||
|
||||
function hsdpaTerminate() {
|
||||
sys.poke(HSDPA_REG_SEQ_IO_OPCODE, HSDPA_OPCODE_TERMINATE)
|
||||
}
|
||||
|
||||
function prepare(fullPath) {
|
||||
// Parse tape device path like "$:/TAPE0/"
|
||||
if (!fullPath.startsWith("$:/TAPE") && !fullPath.startsWith("$:\\TAPE")) {
|
||||
throw Error("seqreadtape: Expected TAPE device path like $:\\TAPE0, got '" + fullPath + "'")
|
||||
}
|
||||
|
||||
readCount = 0
|
||||
|
||||
// Extract tape number from path (TAPE0 -> 0, TAPE1 -> 1, etc.)
|
||||
let tapeMatch = fullPath.match(/\$:[/\\]TAPE([0-9]+)/)
|
||||
if (!tapeMatch) {
|
||||
throw Error("seqreadtape: Invalid TAPE device path format: " + fullPath)
|
||||
}
|
||||
|
||||
let tapeNumber = parseInt(tapeMatch[1])
|
||||
if (tapeNumber < 0 || tapeNumber > 3) {
|
||||
throw Error("seqreadtape: TAPE device number must be 0-3, got " + tapeNumber)
|
||||
}
|
||||
|
||||
currentTapeDevice = tapeNumber
|
||||
|
||||
try {
|
||||
// Open the tape device using TVDOS file system
|
||||
fileDescriptor = files.open(fullPath)
|
||||
if (!fileDescriptor.exists) {
|
||||
throw Error(`seqreadtape: TAPE device ${tapeNumber} not available or no file attached`)
|
||||
}
|
||||
|
||||
// Initialize HSDPA for sequential reading
|
||||
hsdpaSetActiveDisk(tapeNumber + 1) // HSDPA uses 1-based disk numbers
|
||||
hsdpaEnableSequentialIO()
|
||||
hsdpaRewind()
|
||||
|
||||
// Read file header (much larger than serial version)
|
||||
let headerPtr = sys.malloc(fileHeader.length)
|
||||
try {
|
||||
hsdpaReadToMemory(fileHeader.length, headerPtr)
|
||||
|
||||
// Copy header to our buffer
|
||||
for (let i = 0; i < fileHeader.length; i++) {
|
||||
fileHeader[i] = sys.peek(headerPtr + i) & 0xFF
|
||||
}
|
||||
} finally {
|
||||
sys.free(headerPtr)
|
||||
}
|
||||
|
||||
// Reset position for actual reading
|
||||
hsdpaRewind()
|
||||
readCount = 0
|
||||
|
||||
return 0
|
||||
|
||||
} catch (e) {
|
||||
throw Error("seqreadtape: Failed to prepare TAPE device: " + e.message)
|
||||
}
|
||||
}
|
||||
|
||||
function readBytes(length, ptrToDecode) {
|
||||
if (length <= 0) return
|
||||
|
||||
let ptr = (ptrToDecode === undefined) ? sys.malloc(length) : ptrToDecode
|
||||
|
||||
try {
|
||||
// Skip to current read position if needed
|
||||
if (readCount > 0) {
|
||||
hsdpaRewind()
|
||||
if (readCount > 0) {
|
||||
hsdpaSkip(readCount)
|
||||
}
|
||||
}
|
||||
|
||||
// Read directly using HSDPA - no 4096 byte limitation!
|
||||
hsdpaReadToMemory(length, ptr)
|
||||
|
||||
readCount += length
|
||||
|
||||
return ptr
|
||||
|
||||
} catch (e) {
|
||||
if (ptrToDecode === undefined) {
|
||||
sys.free(ptr)
|
||||
}
|
||||
throw Error("seqreadtape: Failed to read bytes: " + e.message)
|
||||
}
|
||||
}
|
||||
|
||||
function readInt() {
|
||||
let b = readBytes(4)
|
||||
let i = (sys.peek(b) & 0xFF) |
|
||||
((sys.peek(b+1) & 0xFF) << 8) |
|
||||
((sys.peek(b+2) & 0xFF) << 16) |
|
||||
((sys.peek(b+3) & 0xFF) << 24)
|
||||
sys.free(b)
|
||||
return i
|
||||
}
|
||||
|
||||
function readShort() {
|
||||
let b = readBytes(2)
|
||||
let i = (sys.peek(b) & 0xFF) | ((sys.peek(b+1) & 0xFF) << 8)
|
||||
sys.free(b)
|
||||
return i
|
||||
}
|
||||
|
||||
function readOneByte() {
|
||||
let b = readBytes(1)
|
||||
let i = sys.peek(b) & 0xFF
|
||||
sys.free(b)
|
||||
return i
|
||||
}
|
||||
|
||||
function readFourCC() {
|
||||
let b = readBytes(4)
|
||||
let a = [sys.peek(b) & 0xFF, sys.peek(b+1) & 0xFF, sys.peek(b+2) & 0xFF, sys.peek(b+3) & 0xFF]
|
||||
sys.free(b)
|
||||
let s = String.fromCharCode.apply(null, a)
|
||||
if (s.length != 4) {
|
||||
throw Error(`seqreadtape: FourCC is not 4 characters long (${s}; ${a.map(it=>"0x"+it.toString(16).padStart(2,'0')).join()})`)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
function readString(length) {
|
||||
let b = readBytes(length)
|
||||
let s = ""
|
||||
for (let k = 0; k < length; k++) {
|
||||
s += String.fromCharCode(sys.peek(b + k) & 0xFF)
|
||||
}
|
||||
sys.free(b)
|
||||
return s
|
||||
}
|
||||
|
||||
function skip(n) {
|
||||
if (n <= 0) return
|
||||
|
||||
// For HSDPA, we can skip efficiently without reading
|
||||
hsdpaSkip(n)
|
||||
readCount += n
|
||||
}
|
||||
|
||||
function getReadCount() {
|
||||
return readCount
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (fileDescriptor) {
|
||||
try {
|
||||
hsdpaTerminate()
|
||||
hsdpaDisableSequentialIO()
|
||||
fileDescriptor.close()
|
||||
} catch (e) {
|
||||
// Ignore close errors
|
||||
}
|
||||
fileDescriptor = undefined
|
||||
currentTapeDevice = undefined
|
||||
readCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentTapeDevice() {
|
||||
return currentTapeDevice
|
||||
}
|
||||
|
||||
function isReady() {
|
||||
return fileDescriptor !== undefined && currentTapeDevice !== undefined
|
||||
}
|
||||
|
||||
function seek(position) {
|
||||
// Seek to absolute position
|
||||
hsdpaRewind()
|
||||
if (position > 0) {
|
||||
hsdpaSkip(position)
|
||||
}
|
||||
readCount = position
|
||||
}
|
||||
|
||||
function rewind() { seek(0) }
|
||||
|
||||
exports = {
|
||||
fileHeader,
|
||||
prepare,
|
||||
readBytes,
|
||||
readInt,
|
||||
readShort,
|
||||
readFourCC,
|
||||
readOneByte,
|
||||
readString,
|
||||
skip,
|
||||
getReadCount,
|
||||
close,
|
||||
getCurrentTapeDevice,
|
||||
isReady,
|
||||
// Enhanced functions
|
||||
seek
|
||||
}
|
||||
Reference in New Issue
Block a user