mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-15 15:36:05 +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
|
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
|
0x00 - No operation
|
||||||
0x01 - Skip (arg 1) bytes
|
0x01 - Skip (arg 1) bytes
|
||||||
0x02 - Read (arg 1) bytes and store to core memory pointer (arg 2)
|
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")
|
//// 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)
|
//// appear (e.g. getting garbage values when it fucking shouldn't)
|
||||||
|
|
||||||
if (destroyed) { throw DanglingPointerException("The pointer is already destroyed ($this)") }
|
// 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 (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 {
|
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