mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
preliminary hsdpa impl
This commit is contained in:
@@ -888,7 +888,7 @@ NOTE: Sequential I/O will clobber the peripheral memory space.
|
||||
|
||||
256..257 RW: Sequential I/O control flags
|
||||
|
||||
258 RW: Opcode
|
||||
258 RW: Opcode. Writing a value to this memory will execute the operation
|
||||
0x00 - No operation
|
||||
0x01 - Skip (arg 1) bytes
|
||||
0x02 - Read (arg 1) bytes and store to core memory pointer (arg 2)
|
||||
|
||||
@@ -89,8 +89,8 @@ internal class UnsafePtr(pointer: Long, allocSize: Long, private val caller: Any
|
||||
//// You may break the glass and use this tool when some fucking incomprehensible bugs ("vittujen vitun bugit")
|
||||
//// appear (e.g. getting garbage values when it fucking shouldn't)
|
||||
|
||||
if (destroyed) { throw DanglingPointerException("The pointer is already destroyed ($this)") }
|
||||
if (index !in 0 until size) throw AddressOverflowException("Index: $index; alloc size: $size; pointer: ${this}\n${Thread.currentThread().stackTrace.joinToString("\n", limit=10) { " $it" }}")
|
||||
// if (destroyed) { throw DanglingPointerException("The pointer is already destroyed ($this)") }
|
||||
// if (index !in 0 until size) throw AddressOverflowException("Index: $index; alloc size: $size; pointer: ${this}\n${Thread.currentThread().stackTrace.joinToString("\n", limit=10) { " $it" }}")
|
||||
}
|
||||
|
||||
operator fun get(index: Long): Byte {
|
||||
|
||||
382
tsvm_core/src/net/torvald/tsvm/peripheral/HSDPA.kt
Normal file
382
tsvm_core/src/net/torvald/tsvm/peripheral/HSDPA.kt
Normal file
@@ -0,0 +1,382 @@
|
||||
package net.torvald.tsvm.peripheral
|
||||
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
|
||||
import net.torvald.tsvm.VM
|
||||
|
||||
|
||||
/**
|
||||
* High Speed Disk Peripheral Adapter (HSDPA)
|
||||
*
|
||||
* Created by minjaesong on 2025-05-06.
|
||||
*/
|
||||
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
|
||||
const val REG_DISK1_STATUS = 0
|
||||
const val REG_DISK2_STATUS = 3
|
||||
const val REG_DISK3_STATUS = 6
|
||||
const val REG_DISK4_STATUS = 9
|
||||
const val REG_DISK_CONTROL = 12
|
||||
const val REG_DISK_STATUS_CODE = 16
|
||||
const val REG_ACTIVE_DISK = 20
|
||||
|
||||
// Sequential I/O registers
|
||||
const val REG_SEQ_IO_CONTROL = 256
|
||||
const val REG_SEQ_IO_OPCODE = 258
|
||||
const val REG_SEQ_IO_ARG1 = 259
|
||||
const val REG_SEQ_IO_ARG2 = 262
|
||||
const val REG_SEQ_IO_ARG3 = 265
|
||||
const val REG_SEQ_IO_ARG4 = 268
|
||||
|
||||
// Sequential I/O opcodes
|
||||
const val OPCODE_NOP = 0x00
|
||||
const val OPCODE_SKIP = 0x01
|
||||
const val OPCODE_READ = 0x02
|
||||
const val OPCODE_WRITE = 0x03
|
||||
const val OPCODE_TERMINATE = 0xFF
|
||||
}
|
||||
|
||||
override val typestring = "hsdpa"
|
||||
|
||||
override fun getVM() = vm
|
||||
|
||||
// Buffer for block transfer
|
||||
private val buffer = ByteArray(BUFFER_SIZE)
|
||||
|
||||
// Disk interfaces
|
||||
private val diskInterfaces = Array(MAX_DISKS) { DiskInterface(baudRate.toInt()) }
|
||||
|
||||
// Currently active disk (0-based index, -1 means no disk selected)
|
||||
private var activeDisk = -1
|
||||
|
||||
override fun peek(addr: Long): Byte? {
|
||||
return if (addr in 0L until BUFFER_SIZE)
|
||||
buffer[addr.toInt()]
|
||||
else null
|
||||
}
|
||||
|
||||
override fun poke(addr: Long, byte: Byte) {
|
||||
if (addr in 0L until BUFFER_SIZE)
|
||||
buffer[addr.toInt()] = byte
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
|
||||
private var opcodeBuf = 0
|
||||
private var arg1 = 0
|
||||
private var arg2 = 0
|
||||
|
||||
/**
|
||||
* Reads a value from the MMIO register
|
||||
* @param address Register address
|
||||
* @return Value at the register
|
||||
*/
|
||||
override fun mmio_read(addr: Long): Byte? {
|
||||
val address = addr.toInt()
|
||||
return when (address) {
|
||||
in REG_DISK1_STATUS..REG_DISK4_STATUS+2 -> {
|
||||
val diskIndex = (address - REG_DISK1_STATUS) / 3
|
||||
val offset = (address - REG_DISK1_STATUS) % 3
|
||||
getDiskStatusRegister(diskIndex, offset)
|
||||
}
|
||||
in REG_DISK_CONTROL..REG_DISK_CONTROL+3 -> {
|
||||
val diskIndex = address - REG_DISK_CONTROL
|
||||
getDiskControlRegister(diskIndex)
|
||||
}
|
||||
in REG_DISK_STATUS_CODE..REG_DISK_STATUS_CODE+3 -> {
|
||||
val diskIndex = address - REG_DISK_STATUS_CODE
|
||||
getDiskStatusCodeRegister(diskIndex)
|
||||
}
|
||||
REG_ACTIVE_DISK -> {
|
||||
(activeDisk + 1).toByte() // 1-based in register
|
||||
}
|
||||
REG_SEQ_IO_CONTROL -> {
|
||||
TODO()
|
||||
}
|
||||
REG_SEQ_IO_OPCODE -> {
|
||||
opcodeBuf.toByte()
|
||||
}
|
||||
in REG_SEQ_IO_ARG1..REG_SEQ_IO_ARG1+2 -> {
|
||||
(arg1 ushr ((address - REG_SEQ_IO_ARG1) * 8)).toByte()
|
||||
}
|
||||
in REG_SEQ_IO_ARG2..REG_SEQ_IO_ARG2+2 -> {
|
||||
(arg2 ushr ((address - REG_SEQ_IO_ARG2) * 8)).toByte()
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a value to the MMIO register
|
||||
* @param address Register address
|
||||
* @param value Value to write
|
||||
*/
|
||||
override fun mmio_write(addr: Long, value: Byte) {
|
||||
val address = addr.toInt()
|
||||
when (address) {
|
||||
in REG_DISK1_STATUS..REG_DISK4_STATUS+2 -> {
|
||||
val diskIndex = (address - REG_DISK1_STATUS) / 3
|
||||
val offset = (address - REG_DISK1_STATUS) % 3
|
||||
setDiskStatusRegister(diskIndex, offset, value)
|
||||
}
|
||||
in REG_DISK_CONTROL..REG_DISK_CONTROL+3 -> {
|
||||
val diskIndex = address - REG_DISK_CONTROL
|
||||
setDiskControlRegister(diskIndex, value)
|
||||
}
|
||||
in REG_DISK_STATUS_CODE..REG_DISK_STATUS_CODE+3 -> {
|
||||
val diskIndex = address - REG_DISK_STATUS_CODE
|
||||
setDiskStatusCodeRegister(diskIndex, value)
|
||||
}
|
||||
REG_ACTIVE_DISK -> {
|
||||
setActiveDisk(value.toInt() - 1) // 1-based in register
|
||||
}
|
||||
REG_SEQ_IO_CONTROL -> {
|
||||
|
||||
}
|
||||
REG_SEQ_IO_OPCODE -> {
|
||||
opcodeBuf = value.toUint()
|
||||
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)
|
||||
}
|
||||
in REG_SEQ_IO_ARG2..REG_SEQ_IO_ARG2+2 -> {
|
||||
arg2 = arg2 or (value.toUint() shl (address - REG_SEQ_IO_ARG2) * 8)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the disk status register value
|
||||
* @param diskIndex Disk index (0-3)
|
||||
* @param offset Offset within the 3-byte status register (0-2)
|
||||
* @return Register value
|
||||
*/
|
||||
private fun getDiskStatusRegister(diskIndex: Int, offset: Int): Byte {
|
||||
if (diskIndex < 0 || diskIndex >= MAX_DISKS) return 0
|
||||
|
||||
val disk = diskInterfaces[diskIndex]
|
||||
|
||||
return when (offset) {
|
||||
0 -> (disk.yourBlockSize() and 0xFF).toByte()
|
||||
1 -> ((disk.yourBlockSize() shr 8) and 0xFF).toByte()
|
||||
2 -> {
|
||||
var value = 0
|
||||
if (disk.doYouHaveNext()) value = value or 0x10
|
||||
if (disk.yourBlockSize() == 0) value = value or 0x04
|
||||
value = value or ((disk.yourBlockSize() shr 16) and 0x0F)
|
||||
value.toByte()
|
||||
}
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the disk status register value
|
||||
* @param diskIndex Disk index (0-3)
|
||||
* @param offset Offset within the 3-byte status register (0-2)
|
||||
* @param value Value to set
|
||||
*/
|
||||
private fun setDiskStatusRegister(diskIndex: Int, offset: Int, value: Byte) {
|
||||
if (diskIndex < 0 || diskIndex >= MAX_DISKS) return
|
||||
|
||||
val disk = diskInterfaces[diskIndex]
|
||||
|
||||
when (offset) {
|
||||
0 -> {
|
||||
// Update LSB of block size
|
||||
val currentSize = disk.blockSize.get()
|
||||
val newSize = (currentSize and 0xFFFF00) or (value.toInt() and 0xFF)
|
||||
disk.blockSize.set(newSize)
|
||||
}
|
||||
1 -> {
|
||||
// Update middle byte of block size
|
||||
val currentSize = disk.blockSize.get()
|
||||
val newSize = (currentSize and 0xFF00FF) or ((value.toInt() and 0xFF) shl 8)
|
||||
disk.blockSize.set(newSize)
|
||||
}
|
||||
2 -> {
|
||||
// Update MSB and flags
|
||||
val currentSize = disk.blockSize.get()
|
||||
val hasNext = (value.toInt() and 0x10) != 0
|
||||
val isZero = (value.toInt() and 0x04) != 0
|
||||
val msb = value.toInt() and 0x0F
|
||||
|
||||
val newSize = if (isZero) {
|
||||
0
|
||||
} else {
|
||||
(currentSize and 0x00FFFF) or (msb shl 16)
|
||||
}
|
||||
|
||||
disk.blockSize.set(newSize)
|
||||
// Set hasNext flag
|
||||
disk.setHasNext(hasNext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the disk control register value
|
||||
* @param diskIndex Disk index (0-3)
|
||||
* @return Register value
|
||||
*/
|
||||
private fun getDiskControlRegister(diskIndex: Int): Byte {
|
||||
if (diskIndex < 0 || diskIndex >= MAX_DISKS) return 0
|
||||
|
||||
val disk = diskInterfaces[diskIndex]
|
||||
|
||||
var value = 0
|
||||
if (disk.getMode()) value = value or 0x08 // Send mode
|
||||
if (disk.busy.get()) value = value or 0x04 // Busy
|
||||
if (disk.ready.get()) value = value or 0x02 // Ready
|
||||
if (disk.cableConnected()) value = value or 0x01 // Connected
|
||||
|
||||
return value.toByte()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the disk control register value
|
||||
* @param diskIndex Disk index (0-3)
|
||||
* @param value Value to set
|
||||
*/
|
||||
private fun setDiskControlRegister(diskIndex: Int, value: Byte) {
|
||||
if (diskIndex < 0 || diskIndex >= MAX_DISKS) return
|
||||
|
||||
val disk = diskInterfaces[diskIndex]
|
||||
|
||||
val sendMode = (value.toInt() and 0x08) != 0
|
||||
val startTransfer = (value.toInt() and 0x04) != 0
|
||||
val readyToReceive = (value.toInt() and 0x02) != 0
|
||||
|
||||
// Set mode (send/receive)
|
||||
disk.setMode(sendMode)
|
||||
|
||||
// Set ready flag
|
||||
disk.ready.set(readyToReceive)
|
||||
|
||||
// Start transfer if requested
|
||||
if (startTransfer) {
|
||||
if (sendMode) {
|
||||
// Start sending
|
||||
disk.startSend()
|
||||
} else {
|
||||
// Start reading
|
||||
disk.startRead()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the disk status code register value
|
||||
* @param diskIndex Disk index (0-3)
|
||||
* @return Register value
|
||||
*/
|
||||
private fun getDiskStatusCodeRegister(diskIndex: Int): Byte {
|
||||
if (diskIndex < 0 || diskIndex >= MAX_DISKS) return 0
|
||||
|
||||
val disk = diskInterfaces[diskIndex]
|
||||
return disk.getYourStatusCode().toByte()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the disk status code register value
|
||||
* @param diskIndex Disk index (0-3)
|
||||
* @param value Value to set
|
||||
*/
|
||||
private fun setDiskStatusCodeRegister(diskIndex: Int, value: Byte) {
|
||||
if (diskIndex < 0 || diskIndex >= MAX_DISKS) return
|
||||
|
||||
val disk = diskInterfaces[diskIndex]
|
||||
disk.statusCode.set(value.toInt())
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the active disk
|
||||
* @param diskIndex Disk index (0-3), or -1 to deselect all
|
||||
*/
|
||||
private fun setActiveDisk(diskIndex: Int) {
|
||||
if (diskIndex < -1 || diskIndex >= MAX_DISKS) return
|
||||
|
||||
// Deselect all disks first
|
||||
for (i in 0 until MAX_DISKS) {
|
||||
if (i != diskIndex) {
|
||||
diskInterfaces[i].ready.set(false)
|
||||
}
|
||||
}
|
||||
|
||||
activeDisk = diskIndex
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles sequential I/O opcodes
|
||||
* @param opcode Opcode to handle
|
||||
*/
|
||||
private fun handleSequentialIOOpcode(opcode: Int) {
|
||||
when (opcode) {
|
||||
OPCODE_NOP -> {
|
||||
// No operation
|
||||
}
|
||||
OPCODE_SKIP -> {
|
||||
// Skip bytes
|
||||
// Implementation depends on VM memory access
|
||||
}
|
||||
OPCODE_READ -> {
|
||||
// Read bytes and store to core memory
|
||||
// Implementation depends on VM memory access
|
||||
}
|
||||
OPCODE_WRITE -> {
|
||||
// Write bytes from core memory
|
||||
// Implementation depends on VM memory access
|
||||
}
|
||||
OPCODE_TERMINATE -> {
|
||||
// Terminate sequential I/O session
|
||||
// Clear buffer or reset state as needed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a disk to a specific port
|
||||
* @param diskIndex Port index (0-3)
|
||||
* @param disk Disk to attach
|
||||
*/
|
||||
fun attachDisk(diskIndex: Int, disk: BlockTransferInterface?) {
|
||||
if (diskIndex < 0 || diskIndex >= MAX_DISKS) return
|
||||
|
||||
diskInterfaces[diskIndex].attachDevice(disk)
|
||||
}
|
||||
|
||||
/**
|
||||
* Disk interface implementation for HSDPA
|
||||
*/
|
||||
inner class DiskInterface(baudRate: Int) : BlockTransferInterface(true, true, baudRate) {
|
||||
private var hasNextFlag = false
|
||||
|
||||
override fun startSendImpl(recipient: BlockTransferInterface): Int {
|
||||
// Copy data from buffer to recipient
|
||||
val dataToSend = buffer.copyOf(blockSize.get())
|
||||
recipient.writeout(dataToSend)
|
||||
return dataToSend.size
|
||||
}
|
||||
|
||||
override fun writeoutImpl(inputData: ByteArray) {
|
||||
// Copy received data to buffer
|
||||
val bytesToCopy = minOf(inputData.size, buffer.size)
|
||||
inputData.copyInto(buffer, 0, 0, bytesToCopy)
|
||||
}
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return hasNextFlag
|
||||
}
|
||||
|
||||
fun setHasNext(hasNext: Boolean) {
|
||||
this.hasNextFlag = hasNext
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user