mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
working proof-of-concept file read fom disk
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
var ba = com.sendMessageGetBytes(0, "DEVNAM"+String.fromCharCode(0x17));
|
||||
function getStatusMessage(portNo) {
|
||||
return com.sendMessageGetBytes(portNo, "DEVSTU"+String.fromCharCode(0x17));
|
||||
}
|
||||
|
||||
let ba = com.sendMessageGetBytes(0, "DEVNAM"+String.fromCharCode(0x17));
|
||||
serial.println(ba);
|
||||
|
||||
ba = com.pullMessage(0)
|
||||
@@ -10,15 +14,23 @@ serial.println("# END OF MSG");
|
||||
ba = com.sendMessageGetBytes(1, "DEVNAM"+String.fromCharCode(0x17));
|
||||
serial.println(ba);
|
||||
|
||||
ba = com.sendMessageGetBytes(1, "DEVSTU"+String.fromCharCode(0x17));
|
||||
serial.println(ba);
|
||||
serial.println(getStatusMessage(1));
|
||||
|
||||
ba = com.sendMessageGetBytes(1, "LIST");
|
||||
ba = com.pullMessage(1);
|
||||
println(ba);
|
||||
|
||||
ba = com.sendMessageGetBytes(1, "DEVSTU"+String.fromCharCode(0x17));
|
||||
serial.println(ba);
|
||||
serial.println(getStatusMessage(1));
|
||||
|
||||
com.sendMessage(1, "OPENR\"basic.js\"");
|
||||
|
||||
println("Status code: "+com.getStatusCode(1));
|
||||
|
||||
com.sendMessage(1, "READ");
|
||||
println("Status code: "+com.getStatusCode(1));
|
||||
let source = com.pullMessage(1);
|
||||
println(source);
|
||||
|
||||
eval(source);
|
||||
|
||||
serial.println("k bye")
|
||||
@@ -85,9 +85,12 @@ Description: opens the file for writing
|
||||
|
||||
Description: opens the file for appending (a variant of write)
|
||||
|
||||
WRITE
|
||||
WRITE<bytes to write>
|
||||
|
||||
Description: puts the device into WRITE mode. Any subsequent bytes will be interpreted as-is for writing
|
||||
Description: puts the device into WRITE mode and sets internal counter. Any subsequent messages will be interpreted
|
||||
as-is, get written directly to the file, and will decrement the internal counter accordingly.
|
||||
The number of bytes are required because of a nature of block-transfer, where every message is always in
|
||||
the length of the block size.
|
||||
|
||||
FLUSH
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@ class Base64Delegate {
|
||||
return Base64Coder.decode(inputstr)
|
||||
}
|
||||
|
||||
fun atostr(inputstr: String): String {
|
||||
return Base64Coder.decode(inputstr).toString(VM.CHARSET)
|
||||
}
|
||||
|
||||
fun btoa(inputbytes: ByteArray): String {
|
||||
val sb = StringBuilder()
|
||||
sb.append(Base64Coder.encode(inputbytes))
|
||||
|
||||
@@ -39,6 +39,7 @@ object SerialHelper {
|
||||
}
|
||||
|
||||
// Returns what's on the RX buffer after sendMessage()
|
||||
// i.e won't (actually shouldn't) clobber the device's message compose buffer (see TestDiskDrive)
|
||||
fun fetchResponse(vm: VM, portNo: Int): ByteArray {
|
||||
val incomingMsg = ByteArray(BLOCK_SIZE)
|
||||
|
||||
@@ -89,6 +90,7 @@ object SerialHelper {
|
||||
while (!checkIfDeviceIsReady(vm, portNo)) { Thread.sleep(SLEEP_TIME) }
|
||||
}
|
||||
|
||||
fun getStatusCode(vm: VM, portNo: Int) = vm.getIO().mmio_read(4080L + portNo)
|
||||
|
||||
private fun checkIfDeviceIsThere(vm: VM, portNo: Int) =
|
||||
(vm.getIO().mmio_read(4092L + portNo)!! and 1.toByte()) == 1.toByte()
|
||||
@@ -138,4 +140,5 @@ class SerialHelperDelegate(val vm: VM) {
|
||||
fun fetchResponse(portNo: Int) = SerialHelper.fetchResponse(vm, portNo).toString(VM.CHARSET)
|
||||
fun getDeviceStatus(portNo: Int) = SerialHelper.getDeviceStatus(vm, portNo)
|
||||
fun waitUntilReady(portNo: Int) = SerialHelper.waitUntilReady(vm, portNo)
|
||||
fun getStatusCode(portNo: Int) = SerialHelper.getStatusCode(vm, portNo)
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package net.torvald.tsvm.peripheral
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolean) {
|
||||
|
||||
protected var recipient: BlockTransferInterface? = null
|
||||
|
||||
open @Volatile var ready = true
|
||||
open @Volatile var busy = false
|
||||
@Volatile var ready = true
|
||||
@Volatile var busy = false
|
||||
|
||||
@Volatile var statusCode = 0
|
||||
|
||||
protected var sendmode = false; private set
|
||||
open var blockSize = 0
|
||||
@Volatile var blockSize = 0
|
||||
|
||||
open fun attachDevice(device: BlockTransferInterface?) {
|
||||
recipient = device
|
||||
@@ -49,7 +49,9 @@ abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolea
|
||||
recipient?.startSend()
|
||||
}
|
||||
|
||||
/** A method called by the sender so it can ACTUALLY write its thing onto me. */
|
||||
/** A method called by the sender so it can ACTUALLY write its thing onto me.
|
||||
*
|
||||
* @param inputData received message, usually 4096 bytes long and null-padded */
|
||||
abstract fun writeoutImpl(inputData: ByteArray)
|
||||
/** The actual implementation; must be called by a sender class */
|
||||
fun writeout(inputData: ByteArray) {
|
||||
@@ -64,6 +66,8 @@ abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolea
|
||||
open fun doYouHaveNext(): Boolean = recipient?.hasNext() ?: false
|
||||
open fun yourBlockSize(): Int = recipient?.blockSize ?: 0
|
||||
|
||||
fun getYourStatusCode() = recipient?.statusCode ?: 0
|
||||
|
||||
/** @param sendmode TRUE for send, FALSE for receive */
|
||||
open fun setMode(sendmode: Boolean) {
|
||||
this.sendmode = sendmode
|
||||
@@ -82,5 +86,14 @@ abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolea
|
||||
const val BAD_NEWS = 0x15.toByte()
|
||||
const val UNIT_SEP = 0x1F.toByte()
|
||||
const val END_OF_SEND_BLOCK = 0x17.toByte()
|
||||
|
||||
fun trimNull(ba: ByteArray): ByteArray {
|
||||
var cnt = BLOCK_SIZE - 1
|
||||
while (cnt >= 0) {
|
||||
if (ba[cnt] != 0.toByte()) break
|
||||
cnt -= 1
|
||||
}
|
||||
return ba.sliceArray(0..cnt)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,16 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
|
||||
in 72..79 -> systemUptime.ushr((adi - 72) * 8).and(255).toByte()
|
||||
in 80..87 -> rtc.ushr((adi - 80) * 8).and(255).toByte()
|
||||
|
||||
4076L -> blockTransferPorts[0].statusCode.toByte()
|
||||
4077L -> blockTransferPorts[1].statusCode.toByte()
|
||||
4078L -> blockTransferPorts[2].statusCode.toByte()
|
||||
4079L -> blockTransferPorts[3].statusCode.toByte()
|
||||
|
||||
4080L -> blockTransferPorts[0].getYourStatusCode().toByte()
|
||||
4081L -> blockTransferPorts[1].getYourStatusCode().toByte()
|
||||
4082L -> blockTransferPorts[2].getYourStatusCode().toByte()
|
||||
4083L -> blockTransferPorts[3].getYourStatusCode().toByte()
|
||||
|
||||
4084L -> (blockTransferPorts[0].yourBlockSize().toByte())
|
||||
4085L -> (blockTransferPorts[0].doYouHaveNext().toInt().shl(7) or blockTransferPorts[0].yourBlockSize().ushr(8).and(15)).toByte()
|
||||
4086L -> (blockTransferPorts[1].yourBlockSize().toByte())
|
||||
@@ -136,6 +146,11 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
|
||||
RTClatched = byte.and(0b10).isNonZero()
|
||||
}
|
||||
|
||||
4076L -> blockTransferPorts[0].statusCode = bi
|
||||
4077L -> blockTransferPorts[1].statusCode = bi
|
||||
4078L -> blockTransferPorts[2].statusCode = bi
|
||||
4079L -> blockTransferPorts[3].statusCode = bi
|
||||
|
||||
4084L -> blockTransferPorts[0].blockSize = blockTransferPorts[0].blockSize.and(0xFF00) or byte.toInt().and(255)
|
||||
4085L -> {
|
||||
blockTransferPorts[0].hasNext = (byte < 0)
|
||||
|
||||
@@ -13,6 +13,7 @@ class TestDiskDrive(private val driveNum: Int) : BlockTransferInterface(false, t
|
||||
const val STATE_CODE_ILLEGAL_COMMAND = 128
|
||||
const val STATE_CODE_FILE_NOT_FOUND = 129
|
||||
const val STATE_CODE_FILE_ALREADY_OPENED = 130
|
||||
const val STATE_CODE_OPERATION_NOT_PERMITTED = 131
|
||||
const val STATE_CODE_SYSTEM_IO_ERROR = 192
|
||||
|
||||
|
||||
@@ -24,6 +25,7 @@ class TestDiskDrive(private val driveNum: Int) : BlockTransferInterface(false, t
|
||||
errorMsgs[STATE_CODE_FILE_NOT_FOUND] = "FILE NOT FOUND"
|
||||
errorMsgs[STATE_CODE_FILE_ALREADY_OPENED] = "FILE ALREADY OPENED"
|
||||
errorMsgs[STATE_CODE_SYSTEM_IO_ERROR] = "IO ERROR ON SIMULATED DRIVE"
|
||||
errorMsgs[STATE_CODE_OPERATION_NOT_PERMITTED] = "OPERATION NOT PERMITTED"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +58,6 @@ class TestDiskDrive(private val driveNum: Int) : BlockTransferInterface(false, t
|
||||
private var fileOpen = false
|
||||
private var file = File(rootPath.toURI())
|
||||
//private var readModeLength = -1 // always 4096
|
||||
private var stateCode = STATE_CODE_STANDBY
|
||||
private var writeMode = false
|
||||
private var writeModeLength = -1
|
||||
|
||||
@@ -66,6 +67,8 @@ class TestDiskDrive(private val driveNum: Int) : BlockTransferInterface(false, t
|
||||
|
||||
|
||||
init {
|
||||
statusCode = STATE_CODE_STANDBY
|
||||
|
||||
if (!rootPath.exists()) {
|
||||
rootPath.mkdirs()
|
||||
}
|
||||
@@ -112,23 +115,27 @@ class TestDiskDrive(private val driveNum: Int) : BlockTransferInterface(false, t
|
||||
* Disk drive must create desired side effects in accordance with the input message.
|
||||
*/
|
||||
override fun writeoutImpl(inputData: ByteArray) {
|
||||
val inputString = inputData.toString(VM.CHARSET)
|
||||
if (writeMode) {
|
||||
|
||||
}
|
||||
else {
|
||||
val inputString = trimNull(inputData).toString(VM.CHARSET)
|
||||
|
||||
if (inputString.startsWith("DEVRST\u0017")) {
|
||||
//readModeLength = -1
|
||||
fileOpen = false
|
||||
file = File(rootPath.toURI())
|
||||
blockSendCount = 0
|
||||
stateCode = STATE_CODE_STANDBY
|
||||
statusCode = STATE_CODE_STANDBY
|
||||
writeMode = false
|
||||
writeModeLength = -1
|
||||
}
|
||||
else if (inputString.startsWith("DEVSTU\u0017")) {
|
||||
if (stateCode < 128) {
|
||||
recipient?.writeout(composePositiveAns("${stateCode.toChar()}", errorMsgs[stateCode]))
|
||||
if (statusCode < 128) {
|
||||
recipient?.writeout(composePositiveAns("${statusCode.toChar()}", errorMsgs[statusCode]))
|
||||
}
|
||||
else {
|
||||
recipient?.writeout(composeNegativeAns("${stateCode.toChar()}", errorMsgs[stateCode]))
|
||||
recipient?.writeout(composeNegativeAns("${statusCode.toChar()}", errorMsgs[statusCode]))
|
||||
}
|
||||
}
|
||||
else if (inputString.startsWith("DEVTYP\u0017"))
|
||||
@@ -137,41 +144,55 @@ class TestDiskDrive(private val driveNum: Int) : BlockTransferInterface(false, t
|
||||
recipient?.writeout(composePositiveAns("Testtec Virtual Disk Drive"))
|
||||
else if (inputString.startsWith("OPENR\"") || inputString.startsWith("OPENW\"") || inputString.startsWith("OPENA\"")) {
|
||||
if (fileOpen) {
|
||||
stateCode = STATE_CODE_FILE_ALREADY_OPENED
|
||||
statusCode = STATE_CODE_FILE_ALREADY_OPENED
|
||||
return
|
||||
}
|
||||
|
||||
println("[TestDiskDrive] msg: $inputString, lastIndex: ${inputString.lastIndex}")
|
||||
|
||||
val openMode = inputString[4]
|
||||
println("[TestDiskDrive] open mode: $openMode")
|
||||
// split inputstring into path and optional drive-number
|
||||
|
||||
val prop = inputString.subSequence(6, inputString.length).split(",\"")
|
||||
|
||||
if (prop.size == 0 || prop.size > 2) {
|
||||
stateCode = STATE_CODE_ILLEGAL_COMMAND
|
||||
// get position of latest delimeter (comma)
|
||||
var commaIndex = inputString.lastIndex
|
||||
while (commaIndex > 6) {
|
||||
if (inputString[commaIndex] == ',') break; commaIndex -= 1
|
||||
}
|
||||
// sanity check if path is actually enclosed with double-quote
|
||||
if (commaIndex != 6 && inputString[commaIndex - 1] != '"') {
|
||||
statusCode = STATE_CODE_ILLEGAL_COMMAND
|
||||
return
|
||||
}
|
||||
val filePath = sanitisePath(prop[0])
|
||||
val driveNum = if (prop.size != 2) 0 else prop[1].toInt()
|
||||
val pathStr = inputString.substring(6, if (commaIndex == 6) inputString.lastIndex else commaIndex - 1)
|
||||
val driveNum =
|
||||
if (commaIndex == 6) null else inputString.substring(commaIndex + 1, inputString.length).toInt()
|
||||
val filePath = sanitisePath(pathStr)
|
||||
|
||||
// TODO driveNum is for disk drives that may have two or more slots built; for testing purposes we'll ignore it
|
||||
|
||||
file = File(rootPath, filePath)
|
||||
println("[TestDiskDrive] file path: ${file.canonicalPath}, drive num: $driveNum")
|
||||
|
||||
if (openMode == 'R' && !file.exists()) {
|
||||
stateCode = STATE_CODE_FILE_NOT_FOUND
|
||||
println("! file not found")
|
||||
statusCode = STATE_CODE_FILE_NOT_FOUND
|
||||
return
|
||||
}
|
||||
|
||||
statusCode = STATE_CODE_STANDBY
|
||||
fileOpen = true
|
||||
blockSendCount = 0
|
||||
}
|
||||
else if (inputString.startsWith("LIST")) {
|
||||
// temporary behaviour to ignore any arguments
|
||||
resetBuf()
|
||||
messageComposeBuffer.write(getReadableLs().toByteArray(VM.CHARSET))
|
||||
stateCode = STATE_CODE_STANDBY
|
||||
statusCode = STATE_CODE_STANDBY
|
||||
}
|
||||
else if (inputString.startsWith("CLOSE")) {
|
||||
fileOpen = false
|
||||
stateCode = STATE_CODE_STANDBY
|
||||
statusCode = STATE_CODE_STANDBY
|
||||
}
|
||||
else if (inputString.startsWith("READ")) {
|
||||
//readModeLength = inputString.substring(4 until inputString.length).toInt()
|
||||
@@ -180,10 +201,15 @@ class TestDiskDrive(private val driveNum: Int) : BlockTransferInterface(false, t
|
||||
if (file.isFile) {
|
||||
try {
|
||||
messageComposeBuffer.write(file.readBytes())
|
||||
stateCode = STATE_CODE_STANDBY
|
||||
statusCode = STATE_CODE_STANDBY
|
||||
}
|
||||
catch (e: IOException) {
|
||||
stateCode = STATE_CODE_SYSTEM_IO_ERROR
|
||||
statusCode = STATE_CODE_SYSTEM_IO_ERROR
|
||||
}
|
||||
}
|
||||
else {
|
||||
statusCode = STATE_CODE_OPERATION_NOT_PERMITTED
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,9 @@ MMIO
|
||||
72..79 RO: System uptime in nanoseconds
|
||||
80..87 RO: RTC in microseconds
|
||||
|
||||
4076..4079 RW: 8-bit status code for the port
|
||||
4080..4083 RO: 8-bit status code for connected device
|
||||
|
||||
4084..4091 RO: Block transfer status
|
||||
0b nnnnnnnn a000 mmmm
|
||||
|
||||
|
||||
Reference in New Issue
Block a user