mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-10 15:04:03 +09:00
TEVD disk drive wip
This commit is contained in:
BIN
assets/DOSBOOT.tevd
Normal file
BIN
assets/DOSBOOT.tevd
Normal file
Binary file not shown.
BIN
assets/DOSBOOT.tevd.original
Normal file
BIN
assets/DOSBOOT.tevd.original
Normal file
Binary file not shown.
@@ -1,11 +1,13 @@
|
|||||||
package net.torvald.tsvm.peripheral
|
package net.torvald.tsvm.peripheral
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolean) {
|
abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolean) {
|
||||||
|
|
||||||
protected var recipient: BlockTransferInterface? = null
|
protected var recipient: BlockTransferInterface? = null
|
||||||
|
|
||||||
@Volatile var ready = true
|
@Volatile val ready = AtomicBoolean(true)
|
||||||
@Volatile var busy = false
|
@Volatile val busy = AtomicBoolean(false)
|
||||||
|
|
||||||
@Volatile var statusCode = 0
|
@Volatile var statusCode = 0
|
||||||
|
|
||||||
@@ -17,8 +19,8 @@ abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolea
|
|||||||
device?.recipient = this
|
device?.recipient = this
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun areYouReady(): Boolean = recipient?.ready ?: false
|
open fun areYouReady(): Boolean = recipient?.ready?.get() ?: false
|
||||||
open fun areYouBusy(): Boolean = recipient?.busy ?: false
|
open fun areYouBusy(): Boolean = recipient?.busy?.get() ?: false
|
||||||
|
|
||||||
/** Writes a thing to the recipient.
|
/** Writes a thing to the recipient.
|
||||||
* A method exposed to outside of the box
|
* A method exposed to outside of the box
|
||||||
@@ -27,16 +29,16 @@ abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolea
|
|||||||
/** The actual implementation */
|
/** The actual implementation */
|
||||||
fun startSend() {
|
fun startSend() {
|
||||||
//if (areYouReady()) {
|
//if (areYouReady()) {
|
||||||
busy = true
|
busy.setRelease(true)
|
||||||
ready = false
|
ready.setRelease(false)
|
||||||
|
|
||||||
recipient?.let {
|
recipient?.let {
|
||||||
this.blockSize = startSendImpl(it)
|
this.blockSize = startSendImpl(it)
|
||||||
//println("[BlockTransferInterface.startSend()] recipients blocksize = ${this.blockSize}")
|
//println("[BlockTransferInterface.startSend()] recipients blocksize = ${this.blockSize}")
|
||||||
}
|
}
|
||||||
|
|
||||||
busy = false
|
busy.setRelease(false)
|
||||||
ready = true
|
ready.setRelease(true)
|
||||||
//}
|
//}
|
||||||
//else {
|
//else {
|
||||||
// throw IOException("${this.javaClass.canonicalName}: Device '${recipient?.javaClass?.canonicalName}' is not ready to receive")
|
// throw IOException("${this.javaClass.canonicalName}: Device '${recipient?.javaClass?.canonicalName}' is not ready to receive")
|
||||||
@@ -55,12 +57,12 @@ abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolea
|
|||||||
abstract fun writeoutImpl(inputData: ByteArray)
|
abstract fun writeoutImpl(inputData: ByteArray)
|
||||||
/** The actual implementation; must be called by a sender class */
|
/** The actual implementation; must be called by a sender class */
|
||||||
fun writeout(inputData: ByteArray) {
|
fun writeout(inputData: ByteArray) {
|
||||||
busy = true
|
busy.setRelease(true)
|
||||||
ready = false
|
ready.setRelease(false)
|
||||||
blockSize = minOf(inputData.size, BLOCK_SIZE)
|
blockSize = minOf(inputData.size, BLOCK_SIZE)
|
||||||
writeoutImpl(inputData)
|
writeoutImpl(inputData)
|
||||||
busy = false
|
busy.setRelease(false)
|
||||||
ready = true
|
ready.setRelease(true)
|
||||||
}
|
}
|
||||||
abstract fun hasNext(): Boolean
|
abstract fun hasNext(): Boolean
|
||||||
open fun doYouHaveNext(): Boolean = recipient?.hasNext() ?: false
|
open fun doYouHaveNext(): Boolean = recipient?.hasNext() ?: false
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class TestDiskDrive(private val vm: VM, private val driveNum: Int, theRootPath:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val DBGPRN = true
|
private val DBGPRN = false
|
||||||
|
|
||||||
private fun printdbg(msg: Any) {
|
private fun printdbg(msg: Any) {
|
||||||
if (DBGPRN) println("[TestDiskDrive] $msg")
|
if (DBGPRN) println("[TestDiskDrive] $msg")
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.VDUtil
|
|||||||
import net.torvald.tsvm.VM
|
import net.torvald.tsvm.VM
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2022-12-15.
|
* Created by minjaesong on 2022-12-15.
|
||||||
*/
|
*/
|
||||||
class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val diskUUIDstr: String) : BlockTransferInterface(false, true) {
|
class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val theTevdPath: String, val diskUUIDstr: String) : BlockTransferInterface(false, true) {
|
||||||
|
|
||||||
|
|
||||||
private val DBGPRN = true
|
private val DBGPRN = true
|
||||||
@@ -35,6 +36,7 @@ class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val dis
|
|||||||
private var blockSendBuffer = ByteArray(1)
|
private var blockSendBuffer = ByteArray(1)
|
||||||
private var blockSendCount = 0
|
private var blockSendCount = 0
|
||||||
|
|
||||||
|
private val hasChanges = AtomicBoolean(false)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
statusCode = TestDiskDrive.STATE_CODE_STANDBY
|
statusCode = TestDiskDrive.STATE_CODE_STANDBY
|
||||||
@@ -42,12 +44,28 @@ class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val dis
|
|||||||
if (!tevdPath.exists()) {
|
if (!tevdPath.exists()) {
|
||||||
throw FileNotFoundException("Disk file '${theTevdPath}' not found")
|
throw FileNotFoundException("Disk file '${theTevdPath}' not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Thread {
|
||||||
|
while (vm.isRunning) {
|
||||||
|
if (hasChanges.compareAndExchangeAcquire(true, false)) {
|
||||||
|
printdbg("Disk has changes, committing... $theTevdPath")
|
||||||
|
commit()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printdbg("Disk has no changes, skipping... $theTevdPath")
|
||||||
|
}
|
||||||
|
Thread.sleep(1000L * COMMIT_INTERVAL)
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
it.start()
|
||||||
|
vm.contexts.add(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** How often the changes in DOM (disk object model) should be saved to the physical drive when there are changes. Seconds. */
|
/** How often the changes in DOM (disk object model) should be saved to the physical drive when there are changes. Seconds. */
|
||||||
const val COMMIT_INTERVAL = 30
|
const val COMMIT_INTERVAL = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
fun commit() {
|
fun commit() {
|
||||||
@@ -116,6 +134,8 @@ class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val dis
|
|||||||
|
|
||||||
writeMode = false
|
writeMode = false
|
||||||
appendMode = false
|
appendMode = false
|
||||||
|
|
||||||
|
hasChanges.getAndSet(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -142,7 +162,7 @@ class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val dis
|
|||||||
else if (inputString.startsWith("DEVTYP\u0017"))
|
else if (inputString.startsWith("DEVTYP\u0017"))
|
||||||
recipient?.writeout(TestDiskDrive.composePositiveAns("STOR"))
|
recipient?.writeout(TestDiskDrive.composePositiveAns("STOR"))
|
||||||
else if (inputString.startsWith("DEVNAM\u0017"))
|
else if (inputString.startsWith("DEVNAM\u0017"))
|
||||||
recipient?.writeout(TestDiskDrive.composePositiveAns("Testtec Virtual Disk Drive"))
|
recipient?.writeout(TestDiskDrive.composePositiveAns("Generic Disk Drive"))
|
||||||
else if (inputString.startsWith("OPENR\"") || inputString.startsWith("OPENW\"") || inputString.startsWith("OPENA\"")) {
|
else if (inputString.startsWith("OPENR\"") || inputString.startsWith("OPENW\"") || inputString.startsWith("OPENA\"")) {
|
||||||
if (fileOpen) {
|
if (fileOpen) {
|
||||||
|
|
||||||
@@ -181,6 +201,11 @@ class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val dis
|
|||||||
statusCode = TestDiskDrive.STATE_CODE_NO_SUCH_FILE_EXISTS
|
statusCode = TestDiskDrive.STATE_CODE_NO_SUCH_FILE_EXISTS
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
else if (DOM.isReadOnly && (openMode == 'W' || openMode == 'A')) {
|
||||||
|
printdbg("! disk is read-only")
|
||||||
|
statusCode = TestDiskDrive.STATE_CODE_READ_ONLY
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
statusCode = TestDiskDrive.STATE_CODE_STANDBY
|
statusCode = TestDiskDrive.STATE_CODE_STANDBY
|
||||||
fileOpen = true
|
fileOpen = true
|
||||||
@@ -198,6 +223,7 @@ class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val dis
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
file.delete()
|
file.delete()
|
||||||
|
hasChanges.getAndSet(true)
|
||||||
}
|
}
|
||||||
catch (e: SecurityException) {
|
catch (e: SecurityException) {
|
||||||
statusCode = TestDiskDrive.STATE_CODE_SYSTEM_SECURITY_ERROR
|
statusCode = TestDiskDrive.STATE_CODE_SYSTEM_SECURITY_ERROR
|
||||||
@@ -322,6 +348,7 @@ class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val dis
|
|||||||
try {
|
try {
|
||||||
val status = file.mkdir()
|
val status = file.mkdir()
|
||||||
statusCode = if (status) 0 else 1
|
statusCode = if (status) 0 else 1
|
||||||
|
if (status) hasChanges.getAndSet(true)
|
||||||
}
|
}
|
||||||
catch (e: SecurityException) {
|
catch (e: SecurityException) {
|
||||||
statusCode = TestDiskDrive.STATE_CODE_SYSTEM_SECURITY_ERROR
|
statusCode = TestDiskDrive.STATE_CODE_SYSTEM_SECURITY_ERROR
|
||||||
@@ -339,6 +366,7 @@ class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val dis
|
|||||||
try {
|
try {
|
||||||
val f1 = file.createNewFile()
|
val f1 = file.createNewFile()
|
||||||
statusCode = if (f1) TestDiskDrive.STATE_CODE_STANDBY else TestDiskDrive.STATE_CODE_OPERATION_FAILED
|
statusCode = if (f1) TestDiskDrive.STATE_CODE_STANDBY else TestDiskDrive.STATE_CODE_OPERATION_FAILED
|
||||||
|
if (f1) hasChanges.getAndSet(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
catch (e: IOException) {
|
catch (e: IOException) {
|
||||||
@@ -360,6 +388,7 @@ class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val dis
|
|||||||
try {
|
try {
|
||||||
val f1 = file.setLastModified(vm.worldInterface.currentTimeInMills())
|
val f1 = file.setLastModified(vm.worldInterface.currentTimeInMills())
|
||||||
statusCode = if (f1) TestDiskDrive.STATE_CODE_STANDBY else TestDiskDrive.STATE_CODE_OPERATION_FAILED
|
statusCode = if (f1) TestDiskDrive.STATE_CODE_STANDBY else TestDiskDrive.STATE_CODE_OPERATION_FAILED
|
||||||
|
if (f1) hasChanges.getAndSet(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
catch (e: IOException) {
|
catch (e: IOException) {
|
||||||
@@ -427,6 +456,9 @@ class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val dis
|
|||||||
|
|
||||||
if (file.isFile) sb.append(file.name)
|
if (file.isFile) sb.append(file.name)
|
||||||
else {
|
else {
|
||||||
|
var filesCount = 0
|
||||||
|
var dirsCount = 0
|
||||||
|
|
||||||
sb.append("Current directory: ")
|
sb.append("Current directory: ")
|
||||||
sb.append(if (isRoot) "(root)" else file.path)
|
sb.append(if (isRoot) "(root)" else file.path)
|
||||||
sb.append('\n')
|
sb.append('\n')
|
||||||
@@ -442,16 +474,27 @@ class TevdDiskDrive(private val vm: VM, private val theTevdPath: String, val dis
|
|||||||
if (it.isDirectory) {
|
if (it.isDirectory) {
|
||||||
sb.append("/")
|
sb.append("/")
|
||||||
filenameLen += 1
|
filenameLen += 1
|
||||||
|
|
||||||
|
dirsCount += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append(" ".repeat(40 - filenameLen))
|
sb.append(" ".repeat(40 - filenameLen))
|
||||||
|
|
||||||
if (it.isFile) {
|
if (it.isFile) {
|
||||||
sb.append("${it.length()} B")
|
sb.append("${it.length()} B")
|
||||||
|
|
||||||
|
filesCount += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append('\n')
|
sb.append('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sb.append("\n")
|
||||||
|
sb.append("\n$filesCount Files, $dirsCount, Directories")
|
||||||
|
sb.append("\nDisk used ${DOM.usedBytes} bytes")
|
||||||
|
sb.append("\n${DOM.capacity - DOM.usedBytes} bytes free")
|
||||||
|
if (DOM.isReadOnly)
|
||||||
|
sb.append("\nThe disk is read-only!")
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (sb.last() == '\n') sb.substring(0, sb.lastIndex) else sb.toString()
|
return if (sb.last() == '\n') sb.substring(0, sb.lastIndex) else sb.toString()
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ class TevdFileDescriptor(val DOM: VirtualDisk, _pathstr: String) {
|
|||||||
|
|
||||||
|
|
||||||
val isFile: Boolean
|
val isFile: Boolean
|
||||||
get() = TODO()
|
get() = entryID.let { if (it == null) false else VDUtil.isFileFollowSymlink(DOM, it) }
|
||||||
|
|
||||||
val isDirectory: Boolean
|
val isDirectory: Boolean
|
||||||
get() = TODO()
|
get() = entryID.let { if (it == null) false else VDUtil.isDirectoryFollowSymlink(DOM, it) }
|
||||||
|
|
||||||
|
|
||||||
private var fileContent: EntryFile? = null
|
private var fileContent: EntryFile? = null
|
||||||
@@ -63,6 +63,7 @@ class TevdFileDescriptor(val DOM: VirtualDisk, _pathstr: String) {
|
|||||||
|
|
||||||
fun delete() {
|
fun delete() {
|
||||||
fileContent = null
|
fileContent = null
|
||||||
|
VDUtil.deleteFile(DOM, vdPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun length(): Long {
|
fun length(): Long {
|
||||||
|
|||||||
Reference in New Issue
Block a user