working proof-of-concept file read fom disk

This commit is contained in:
minjaesong
2020-10-28 23:07:12 +09:00
parent 664a4ca758
commit 29584b7070
8 changed files with 161 additions and 82 deletions

View File

@@ -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")

View File

@@ -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

View File

@@ -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))

View File

@@ -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)
}

View File

@@ -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)
}
}
}

View File

@@ -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)

View File

@@ -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,78 +115,101 @@ 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) {
if (inputString.startsWith("DEVRST\u0017")) {
//readModeLength = -1
fileOpen = false
file = File(rootPath.toURI())
blockSendCount = 0
stateCode = STATE_CODE_STANDBY
writeMode = false
writeModeLength = -1
}
else if (inputString.startsWith("DEVSTU\u0017")) {
if (stateCode < 128) {
recipient?.writeout(composePositiveAns("${stateCode.toChar()}", errorMsgs[stateCode]))
else {
val inputString = trimNull(inputData).toString(VM.CHARSET)
if (inputString.startsWith("DEVRST\u0017")) {
//readModeLength = -1
fileOpen = false
file = File(rootPath.toURI())
blockSendCount = 0
statusCode = STATE_CODE_STANDBY
writeMode = false
writeModeLength = -1
}
else {
recipient?.writeout(composeNegativeAns("${stateCode.toChar()}", errorMsgs[stateCode]))
}
}
else if (inputString.startsWith("DEVTYP\u0017"))
recipient?.writeout(composePositiveAns("STOR"))
else if (inputString.startsWith("DEVNAM\u0017"))
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
return
}
val openMode = inputString[4]
val prop = inputString.subSequence(6, inputString.length).split(",\"")
if (prop.size == 0 || prop.size > 2) {
stateCode = STATE_CODE_ILLEGAL_COMMAND
return
}
val filePath = sanitisePath(prop[0])
val driveNum = if (prop.size != 2) 0 else prop[1].toInt()
// 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)
if (openMode == 'R' && !file.exists()) {
stateCode = STATE_CODE_FILE_NOT_FOUND
return
}
fileOpen = true
}
else if (inputString.startsWith("LIST")) {
// temporary behaviour to ignore any arguments
resetBuf()
messageComposeBuffer.write(getReadableLs().toByteArray(VM.CHARSET))
stateCode = STATE_CODE_STANDBY
}
else if (inputString.startsWith("CLOSE")) {
fileOpen = false
stateCode = STATE_CODE_STANDBY
}
else if (inputString.startsWith("READ")) {
//readModeLength = inputString.substring(4 until inputString.length).toInt()
resetBuf()
if (file.isFile) {
try {
messageComposeBuffer.write(file.readBytes())
stateCode = STATE_CODE_STANDBY
else if (inputString.startsWith("DEVSTU\u0017")) {
if (statusCode < 128) {
recipient?.writeout(composePositiveAns("${statusCode.toChar()}", errorMsgs[statusCode]))
}
catch (e: IOException) {
stateCode = STATE_CODE_SYSTEM_IO_ERROR
else {
recipient?.writeout(composeNegativeAns("${statusCode.toChar()}", errorMsgs[statusCode]))
}
}
else if (inputString.startsWith("DEVTYP\u0017"))
recipient?.writeout(composePositiveAns("STOR"))
else if (inputString.startsWith("DEVNAM\u0017"))
recipient?.writeout(composePositiveAns("Testtec Virtual Disk Drive"))
else if (inputString.startsWith("OPENR\"") || inputString.startsWith("OPENW\"") || inputString.startsWith("OPENA\"")) {
if (fileOpen) {
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
// 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 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()) {
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))
statusCode = STATE_CODE_STANDBY
}
else if (inputString.startsWith("CLOSE")) {
fileOpen = false
statusCode = STATE_CODE_STANDBY
}
else if (inputString.startsWith("READ")) {
//readModeLength = inputString.substring(4 until inputString.length).toInt()
resetBuf()
if (file.isFile) {
try {
messageComposeBuffer.write(file.readBytes())
statusCode = STATE_CODE_STANDBY
}
catch (e: IOException) {
statusCode = STATE_CODE_SYSTEM_IO_ERROR
}
}
else {
statusCode = STATE_CODE_OPERATION_NOT_PERMITTED
return
}
}
}

View File

@@ -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