mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
HSDPA and driver implementation
This commit is contained in:
@@ -9,13 +9,13 @@ import net.torvald.tsvm.VM
|
||||
*
|
||||
* Created by minjaesong on 2025-05-06.
|
||||
*/
|
||||
class HSDPA(val vm: VM, val baudRate: Long = 133_333_333L): PeriBase("hsdpa") {
|
||||
open class HSDPA(val vm: VM, val baudRate: Long = 133_333_333L): PeriBase("hsdpa") {
|
||||
|
||||
companion object {
|
||||
const val BUFFER_SIZE = 1048576 // 1MB buffer size
|
||||
const val MAX_DISKS = 4
|
||||
|
||||
// MMIO register offsets
|
||||
// MMIO register offsets (relative to peripheral base)
|
||||
const val REG_DISK1_STATUS = 0
|
||||
const val REG_DISK2_STATUS = 3
|
||||
const val REG_DISK3_STATUS = 6
|
||||
@@ -37,6 +37,7 @@ class HSDPA(val vm: VM, val baudRate: Long = 133_333_333L): PeriBase("hsdpa") {
|
||||
const val OPCODE_SKIP = 0x01
|
||||
const val OPCODE_READ = 0x02
|
||||
const val OPCODE_WRITE = 0x03
|
||||
const val OPCODE_REWIND = 0xF0
|
||||
const val OPCODE_TERMINATE = 0xFF
|
||||
}
|
||||
|
||||
@@ -52,16 +53,25 @@ class HSDPA(val vm: VM, val baudRate: Long = 133_333_333L): PeriBase("hsdpa") {
|
||||
|
||||
// Currently active disk (0-based index, -1 means no disk selected)
|
||||
private var activeDisk = -1
|
||||
|
||||
// Sequential I/O state
|
||||
protected var sequentialIOActive = false
|
||||
protected var sequentialIOPosition = 0L
|
||||
|
||||
override fun peek(addr: Long): Byte? {
|
||||
return if (addr in 0L until BUFFER_SIZE)
|
||||
// Memory Space area - for buffer access
|
||||
return if (addr in 0L until BUFFER_SIZE) {
|
||||
buffer[addr.toInt()]
|
||||
else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun poke(addr: Long, byte: Byte) {
|
||||
if (addr in 0L until BUFFER_SIZE)
|
||||
// Memory Space area - for buffer access
|
||||
if (addr in 0L until BUFFER_SIZE) {
|
||||
buffer[addr.toInt()] = byte
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
@@ -96,7 +106,14 @@ class HSDPA(val vm: VM, val baudRate: Long = 133_333_333L): PeriBase("hsdpa") {
|
||||
(activeDisk + 1).toByte() // 1-based in register
|
||||
}
|
||||
REG_SEQ_IO_CONTROL -> {
|
||||
TODO()
|
||||
// Return sequential I/O control flags
|
||||
var flags = 0
|
||||
if (sequentialIOActive) flags = flags or 0x01
|
||||
flags.toByte()
|
||||
}
|
||||
REG_SEQ_IO_CONTROL + 1 -> {
|
||||
// Second byte of control flags (currently unused)
|
||||
0
|
||||
}
|
||||
REG_SEQ_IO_OPCODE -> {
|
||||
opcodeBuf.toByte()
|
||||
@@ -118,6 +135,7 @@ class HSDPA(val vm: VM, val baudRate: Long = 133_333_333L): PeriBase("hsdpa") {
|
||||
*/
|
||||
override fun mmio_write(addr: Long, value: Byte) {
|
||||
val address = addr.toInt()
|
||||
println("HSDPA: mmio_write(addr=$addr, value=0x${(value.toInt() and 0xFF).toString(16)})")
|
||||
when (address) {
|
||||
in REG_DISK1_STATUS..REG_DISK4_STATUS+2 -> {
|
||||
val diskIndex = (address - REG_DISK1_STATUS) / 3
|
||||
@@ -136,17 +154,34 @@ class HSDPA(val vm: VM, val baudRate: Long = 133_333_333L): PeriBase("hsdpa") {
|
||||
setActiveDisk(value.toInt() - 1) // 1-based in register
|
||||
}
|
||||
REG_SEQ_IO_CONTROL -> {
|
||||
|
||||
// Set sequential I/O control flags
|
||||
sequentialIOActive = (value.toInt() and 0x01) != 0
|
||||
}
|
||||
REG_SEQ_IO_CONTROL + 1 -> {
|
||||
// Second byte of control flags (currently unused)
|
||||
}
|
||||
REG_SEQ_IO_OPCODE -> {
|
||||
opcodeBuf = value.toUint()
|
||||
println("HSDPA: Writing opcode 0x${value.toUint().toString(16)} to register")
|
||||
handleSequentialIOOpcode(value.toUint())
|
||||
}
|
||||
in REG_SEQ_IO_ARG1..REG_SEQ_IO_ARG1+2 -> {
|
||||
arg1 = arg1 or (value.toUint() shl (address - REG_SEQ_IO_ARG1) * 8)
|
||||
val byteOffset = (address - REG_SEQ_IO_ARG1)
|
||||
if (byteOffset == 0) {
|
||||
// Reset arg1 when writing to LSB
|
||||
arg1 = value.toUint()
|
||||
} else {
|
||||
arg1 = arg1 or (value.toUint() shl (byteOffset * 8))
|
||||
}
|
||||
}
|
||||
in REG_SEQ_IO_ARG2..REG_SEQ_IO_ARG2+2 -> {
|
||||
arg2 = arg2 or (value.toUint() shl (address - REG_SEQ_IO_ARG2) * 8)
|
||||
val byteOffset = (address - REG_SEQ_IO_ARG2)
|
||||
if (byteOffset == 0) {
|
||||
// Reset arg2 when writing to LSB
|
||||
arg2 = value.toUint()
|
||||
} else {
|
||||
arg2 = arg2 or (value.toUint() shl (byteOffset * 8))
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
@@ -317,29 +352,73 @@ class HSDPA(val vm: VM, val baudRate: Long = 133_333_333L): PeriBase("hsdpa") {
|
||||
* Handles sequential I/O opcodes
|
||||
* @param opcode Opcode to handle
|
||||
*/
|
||||
private fun handleSequentialIOOpcode(opcode: Int) {
|
||||
protected open fun handleSequentialIOOpcode(opcode: Int) {
|
||||
println("HSDPA: handleSequentialIOOpcode(0x${opcode.toString(16)})")
|
||||
when (opcode) {
|
||||
OPCODE_NOP -> {
|
||||
// No operation
|
||||
println("HSDPA: NOP")
|
||||
}
|
||||
OPCODE_SKIP -> {
|
||||
// Skip bytes
|
||||
// Implementation depends on VM memory access
|
||||
// Skip arg1 bytes in the active disk
|
||||
println("HSDPA: SKIP $arg1 bytes, activeDisk=$activeDisk")
|
||||
if (activeDisk in 0 until MAX_DISKS) {
|
||||
sequentialIOSkip(arg1)
|
||||
}
|
||||
}
|
||||
OPCODE_READ -> {
|
||||
// Read bytes and store to core memory
|
||||
// Implementation depends on VM memory access
|
||||
// Read arg1 bytes and store to core memory at pointer arg2
|
||||
println("HSDPA: READ $arg1 bytes to pointer $arg2, activeDisk=$activeDisk")
|
||||
println("HSDPA: arg1 = 0x${arg1.toString(16)}, arg2 = 0x${arg2.toString(16)}")
|
||||
if (activeDisk in 0 until MAX_DISKS) {
|
||||
sequentialIORead(arg1, arg2)
|
||||
}
|
||||
}
|
||||
OPCODE_WRITE -> {
|
||||
// Write bytes from core memory
|
||||
// Implementation depends on VM memory access
|
||||
// Write arg1 bytes from core memory at pointer arg2
|
||||
if (activeDisk in 0 until MAX_DISKS) {
|
||||
sequentialIOWrite(arg1, arg2)
|
||||
}
|
||||
}
|
||||
OPCODE_REWIND -> {
|
||||
// Rewind to starting point
|
||||
println("HSDPA: REWIND to position 0")
|
||||
sequentialIOPosition = 0L
|
||||
}
|
||||
OPCODE_TERMINATE -> {
|
||||
// Terminate sequential I/O session
|
||||
// Clear buffer or reset state as needed
|
||||
sequentialIOActive = false
|
||||
sequentialIOPosition = 0L
|
||||
// Clear the buffer
|
||||
buffer.fill(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip bytes in sequential I/O mode
|
||||
*/
|
||||
protected open fun sequentialIOSkip(bytes: Int) {
|
||||
sequentialIOPosition += bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Read bytes from disk to VM memory in sequential I/O mode
|
||||
*/
|
||||
protected open fun sequentialIORead(bytes: Int, vmMemoryPointer: Int) {
|
||||
// Default implementation - subclasses should override
|
||||
// For now, just advance the position
|
||||
sequentialIOPosition += bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Write bytes from VM memory to disk in sequential I/O mode
|
||||
*/
|
||||
protected open fun sequentialIOWrite(bytes: Int, vmMemoryPointer: Int) {
|
||||
// Default implementation - subclasses should override
|
||||
// For now, just advance the position
|
||||
sequentialIOPosition += bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a disk to a specific port
|
||||
|
||||
217
tsvm_core/src/net/torvald/tsvm/peripheral/HostFileHSDPA.kt
Normal file
217
tsvm_core/src/net/torvald/tsvm/peripheral/HostFileHSDPA.kt
Normal file
@@ -0,0 +1,217 @@
|
||||
package net.torvald.tsvm.peripheral
|
||||
|
||||
import net.torvald.tsvm.VM
|
||||
import java.io.File
|
||||
import java.io.RandomAccessFile
|
||||
|
||||
/**
|
||||
* Host File High Speed Disk Peripheral Adapter (HostFileHSDPA)
|
||||
*
|
||||
* A testing version of HSDPA that uses actual files on the host computer as disk sources.
|
||||
* Each disk corresponds to a single file on the host filesystem.
|
||||
*
|
||||
* Created by Claude on 2025-08-16.
|
||||
*/
|
||||
class HostFileHSDPA : HSDPA {
|
||||
|
||||
// Primary constructor for Java reflection compatibility
|
||||
constructor(vm: VM, hostFilePaths: Array<String>, baudRate: java.lang.Long) : super(vm, baudRate.toLong()) {
|
||||
initializeHostFiles(hostFilePaths.toList())
|
||||
}
|
||||
|
||||
// Secondary constructor for Kotlin usage
|
||||
constructor(vm: VM, hostFilePaths: List<String> = emptyList(), baudRate: Long = 133_333_333L) : super(vm, baudRate) {
|
||||
initializeHostFiles(hostFilePaths)
|
||||
}
|
||||
|
||||
// Host files for each disk slot
|
||||
private val hostFiles = Array<RandomAccessFile?>(MAX_DISKS) { null }
|
||||
private val hostFilePaths = Array<String?>(MAX_DISKS) { null }
|
||||
|
||||
private fun initializeHostFiles(hostFilePathsList: List<String>) {
|
||||
if (hostFilePathsList.isNotEmpty()) {
|
||||
for (i in 0 until minOf(hostFilePathsList.size, MAX_DISKS)) {
|
||||
val file = File(hostFilePathsList[i])
|
||||
if (file.exists() && file.isFile) {
|
||||
this.hostFiles[i] = RandomAccessFile(file, "r")
|
||||
this.hostFilePaths[i] = hostFilePathsList[i]
|
||||
println("HostFileHSDPA: Attached file '${hostFilePathsList[i]}' to disk $i")
|
||||
} else {
|
||||
println("HostFileHSDPA: Warning - file '${hostFilePathsList[i]}' does not exist or is not a file")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a host file to a disk slot
|
||||
* @param diskIndex Disk slot index (0-3)
|
||||
* @param filePath Path to the host file
|
||||
*/
|
||||
fun attachHostFile(diskIndex: Int, filePath: String) {
|
||||
if (diskIndex < 0 || diskIndex >= MAX_DISKS) return
|
||||
|
||||
try {
|
||||
// Close existing file if any
|
||||
hostFiles[diskIndex]?.close()
|
||||
|
||||
// Open new file
|
||||
val file = File(filePath)
|
||||
if (file.exists() && file.isFile) {
|
||||
hostFiles[diskIndex] = RandomAccessFile(file, "r")
|
||||
hostFilePaths[diskIndex] = filePath
|
||||
println("HSDPA: Attached file '$filePath' to disk $diskIndex")
|
||||
} else {
|
||||
println("HSDPA: Warning - file '$filePath' does not exist or is not a file")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println("HSDPA: Error attaching file '$filePath' to disk $diskIndex: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches a host file from a disk slot
|
||||
* @param diskIndex Disk slot index (0-3)
|
||||
*/
|
||||
fun detachHostFile(diskIndex: Int) {
|
||||
if (diskIndex < 0 || diskIndex >= MAX_DISKS) return
|
||||
|
||||
try {
|
||||
hostFiles[diskIndex]?.close()
|
||||
hostFiles[diskIndex] = null
|
||||
hostFilePaths[diskIndex] = null
|
||||
println("HSDPA: Detached file from disk $diskIndex")
|
||||
} catch (e: Exception) {
|
||||
println("HSDPA: Error detaching file from disk $diskIndex: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the file attached to a disk slot
|
||||
* @param diskIndex Disk slot index (0-3)
|
||||
* @return File size in bytes, or 0 if no file attached
|
||||
*/
|
||||
fun getAttachedFileSize(diskIndex: Int): Long {
|
||||
if (diskIndex < 0 || diskIndex >= MAX_DISKS) return 0L
|
||||
|
||||
return try {
|
||||
hostFiles[diskIndex]?.length() ?: 0L
|
||||
} catch (e: Exception) {
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
override fun sequentialIOSkip(bytes: Int) {
|
||||
sequentialIOPosition += bytes
|
||||
// Clamp position to file bounds if needed
|
||||
val activeDiskIndex = getActiveDiskIndex()
|
||||
if (activeDiskIndex >= 0) {
|
||||
val fileSize = getAttachedFileSize(activeDiskIndex)
|
||||
if (sequentialIOPosition > fileSize) {
|
||||
sequentialIOPosition = fileSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun sequentialIORead(bytes: Int, vmMemoryPointer: Int) {
|
||||
println("HostFileHSDPA: sequentialIORead($bytes, $vmMemoryPointer)")
|
||||
val activeDiskIndex = getActiveDiskIndex()
|
||||
println("HostFileHSDPA: activeDiskIndex = $activeDiskIndex")
|
||||
if (activeDiskIndex < 0 || hostFiles[activeDiskIndex] == null) {
|
||||
// No file attached, just advance position
|
||||
println("HostFileHSDPA: No file attached, advancing position")
|
||||
sequentialIOPosition += bytes
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val file = hostFiles[activeDiskIndex]!!
|
||||
println("HostFileHSDPA: Seeking to position $sequentialIOPosition")
|
||||
file.seek(sequentialIOPosition)
|
||||
|
||||
// Read data into a temporary buffer
|
||||
val readBuffer = ByteArray(bytes)
|
||||
val bytesRead = file.read(readBuffer)
|
||||
println("HostFileHSDPA: Read $bytesRead bytes from file")
|
||||
|
||||
if (bytesRead > 0) {
|
||||
// Log first few bytes for debugging
|
||||
val firstBytes = readBuffer.take(8).map { (it.toInt() and 0xFF).toString(16).padStart(2, '0') }.joinToString(" ")
|
||||
println("HostFileHSDPA: First bytes: $firstBytes")
|
||||
|
||||
// Copy data to VM memory
|
||||
for (i in 0 until bytesRead) {
|
||||
vm.poke(vmMemoryPointer + i.toLong(), readBuffer[i])
|
||||
}
|
||||
sequentialIOPosition += bytesRead
|
||||
println("HostFileHSDPA: Copied $bytesRead bytes to VM memory at $vmMemoryPointer")
|
||||
}
|
||||
|
||||
// Fill remaining bytes with zeros if we read less than requested
|
||||
if (bytesRead < bytes) {
|
||||
for (i in bytesRead until bytes) {
|
||||
vm.poke(vmMemoryPointer + i.toLong(), 0)
|
||||
}
|
||||
sequentialIOPosition += (bytes - bytesRead)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
println("HSDPA: Error reading from file: ${e.message}")
|
||||
// Just advance position on error
|
||||
sequentialIOPosition += bytes
|
||||
}
|
||||
}
|
||||
|
||||
override fun sequentialIOWrite(bytes: Int, vmMemoryPointer: Int) {
|
||||
val activeDiskIndex = getActiveDiskIndex()
|
||||
if (activeDiskIndex < 0 || hostFiles[activeDiskIndex] == null) {
|
||||
// No file attached, just advance position
|
||||
sequentialIOPosition += bytes
|
||||
return
|
||||
}
|
||||
|
||||
// For now, we only support read-only access to host files
|
||||
// In a full implementation, we would write to the file here
|
||||
println("HSDPA: Write operation not supported in read-only mode")
|
||||
sequentialIOPosition += bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently active disk index
|
||||
* @return Active disk index (0-3), or -1 if no disk is active
|
||||
*/
|
||||
private fun getActiveDiskIndex(): Int {
|
||||
// Read the active disk register
|
||||
val activeReg = mmio_read(REG_ACTIVE_DISK.toLong())?.toInt() ?: 0
|
||||
return if (activeReg > 0) activeReg - 1 else -1
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
super.dispose()
|
||||
|
||||
// Close all open files
|
||||
for (i in 0 until MAX_DISKS) {
|
||||
try {
|
||||
hostFiles[i]?.close()
|
||||
} catch (e: Exception) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about attached files
|
||||
* @return Array of file info strings
|
||||
*/
|
||||
fun getAttachedFilesInfo(): Array<String> {
|
||||
return Array(MAX_DISKS) { i ->
|
||||
val path = hostFilePaths[i]
|
||||
if (path != null) {
|
||||
val size = getAttachedFileSize(i)
|
||||
"Disk $i: $path (${size} bytes)"
|
||||
} else {
|
||||
"Disk $i: No file attached"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user