diff --git a/src/commonMain/kotlin/mtmc/asm/Assembler.kt b/src/commonMain/kotlin/mtmc/asm/Assembler.kt index 02d4588..31d1d18 100644 --- a/src/commonMain/kotlin/mtmc/asm/Assembler.kt +++ b/src/commonMain/kotlin/mtmc/asm/Assembler.kt @@ -400,7 +400,7 @@ class Assembler { ): Graphic { val graphic = Graphic(labelTokens, token.line) val filename = token.stringValue - val file = File(File(this.srcName).getParent(), filename) + val file = File(File(this.srcName).parent, filename) val index = graphics.size data.setValue(token, byteArrayOf(((index shr 8) and 0xFF).toByte(), (index and 0xFF).toByte())) @@ -732,7 +732,7 @@ class Assembler { val first = tokenizer!!.consume() tokens.add(first) while (tokenizer!!.more() && - first.line == tokenizer!!.currentToken().line + first.line == tokenizer!!.getCurrentToken().line ) { tokens.add(tokenizer!!.consume()) } diff --git a/src/commonMain/kotlin/mtmc/emulator/BufferedImage.kt b/src/commonMain/kotlin/mtmc/emulator/BufferedImage.kt new file mode 100644 index 0000000..85fbcd2 --- /dev/null +++ b/src/commonMain/kotlin/mtmc/emulator/BufferedImage.kt @@ -0,0 +1,28 @@ +package mtmc.emulator + +class BufferedImage(width: Int, height: Int, type: Int) { + fun getWidth(): Int { + TODO("Not yet implemented") + } + + fun getHeight(): Int { + TODO("Not yet implemented") + } + + fun getRGB(x: Int, y: Int): Int { + TODO("Not yet implemented") + } + + fun getType(): Int { + TODO("Not yet implemented") + } + + fun setRGB(x: Int, y: Int, intVal: Int) { + TODO("Not yet implemented") + } +} + +class Dimension( + val width: Int, + val height: Int +) \ No newline at end of file diff --git a/src/commonMain/kotlin/mtmc/emulator/Color.kt b/src/commonMain/kotlin/mtmc/emulator/Color.kt new file mode 100644 index 0000000..5c8057d --- /dev/null +++ b/src/commonMain/kotlin/mtmc/emulator/Color.kt @@ -0,0 +1,8 @@ +package mtmc.emulator + +class Color( + val r: Int, + val g: Int, + val b: Int, +) { +} \ No newline at end of file diff --git a/src/commonMain/kotlin/mtmc/emulator/MTMCClock.kt b/src/commonMain/kotlin/mtmc/emulator/MTMCClock.kt index 4b91a06..8c43118 100644 --- a/src/commonMain/kotlin/mtmc/emulator/MTMCClock.kt +++ b/src/commonMain/kotlin/mtmc/emulator/MTMCClock.kt @@ -2,6 +2,7 @@ package mtmc.emulator import mtmc.emulator.MonTanaMiniComputer.ComputerStatus import mtmc.util.currentTimeMillis +import mtmc.util.requestAnimationFrame import kotlin.math.max /** @@ -11,49 +12,43 @@ import kotlin.math.max class MTMCClock( private val computer: MonTanaMiniComputer ) { + var instructions: Long = 0 + var ips: Long = 0 + var expected: Long = 0 + var virtual: Long = 0 + + var startTime = currentTimeMillis() + + var speed: Long = 0 + + var instructionsToRun = 0.0 + fun run() { - var instructions: Long = 0 - var ips: Long = 0 - var expected: Long = 0 - var virtual: Long = 0 + requestAnimationFrame { handleFrame(it) } + } - var startTime = currentTimeMillis() - var deltaStart: Long - var delta: Long + fun handleFrame(time: Double) { + // figure out how many instructions to execute this 'time' duration + // maximize time so we don't hang if the emulator is too slow + val actualTime = max(time, 0.16) - var speed: Long = 0 - var pulse: Long - var ms: Long = 10 - - while (computer.getStatus() == ComputerStatus.EXECUTING) { + // assume 1Hz = 1 instruction/second + if (computer.getStatus() == ComputerStatus.EXECUTING) { speed = max(computer.speed, 0).toLong() - pulse = (if (speed <= 0) 1000000 else max(speed / 100, 1)) - ms = (if (pulse < 10) 1000 / speed else 10) - - deltaStart = currentTimeMillis() - delta = ms - (currentTimeMillis() - deltaStart) - - - /* We've lost more than a second. Recalibrate. */ - if ((expected - virtual) > pulse * 100) { - startTime = deltaStart - virtual = 0 + if (speed == 0L) { + speed = 1000000L } - - /* Throttles to every 10ms, but "catches up" if we're behind */ - if (delta > 0 && (expected - virtual) < pulse && speed != 0L) { - try { - Thread.sleep(delta) - } catch (e: InterruptedException) { - } - } + instructionsToRun += time * speed + val pulse: Long = instructionsToRun.toLong() + instructionsToRun -= pulse instructions += computer.pulse(pulse) - virtual += pulse - ips = (virtual * 1000) / max(1, currentTimeMillis() - startTime) - expected = (currentTimeMillis() - startTime) * speed / 1000 + virtual += instructions + ips = (instructions / time).toLong() + + requestAnimationFrame { handleFrame(it) } } //println("Executed " + instructions + " instructions at a rate of " + ips + " ips (speed = " + speed + ")") diff --git a/src/commonMain/kotlin/mtmc/emulator/MTMCDisplay.kt b/src/commonMain/kotlin/mtmc/emulator/MTMCDisplay.kt index 70d984d..04f49f1 100644 --- a/src/commonMain/kotlin/mtmc/emulator/MTMCDisplay.kt +++ b/src/commonMain/kotlin/mtmc/emulator/MTMCDisplay.kt @@ -1,22 +1,13 @@ package mtmc.emulator -import java.awt.Color -import java.awt.Dimension -import java.awt.RenderingHints -import java.awt.image.BufferedImage -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.io.IOException -import java.util.* -import javax.imageio.ImageIO import kotlin.math.min class MTMCDisplay(private val computer: MonTanaMiniComputer) { private val buffer = BufferedImage( COLS, ROWS, - BufferedImage.TYPE_INT_ARGB - ) + 1 + ) // BufferedImage.TYPE_INT_ARGB private var currentColor: DisplayColor? = null private var graphics: Array = arrayOf() private var byteArray: ByteArray = ByteArray(0) @@ -69,24 +60,25 @@ class MTMCDisplay(private val computer: MonTanaMiniComputer) { } private fun loadSplashScreen() { - try { - val bytes: ByteArray = Base64.getDecoder().decode(SPLASH_SCREEN) - val bais = ByteArrayInputStream(bytes) - var img: BufferedImage? = null - img = ImageIO.read(bais) - loadScaledImage(img) - } catch (e: IOException) { - e.printStackTrace() - } + /* try { + val bytes: ByteArray = Base64.getDecoder().decode(SPLASH_SCREEN) + val bais = ByteArrayInputStream(bytes) + var img: BufferedImage? = null + img = ImageIO.read(bais) + loadScaledImage(img) + } catch (e: IOException) { + e.printStackTrace() + }*/ } private fun loadImage(data: ByteArray): BufferedImage? { - try { - return ImageIO.read(ByteArrayInputStream(data)) - } catch (e: IOException) { - e.printStackTrace() - throw IllegalStateException(e) - } + /* try { + return ImageIO.read(ByteArrayInputStream(data)) + } catch (e: IOException) { + e.printStackTrace() + throw IllegalStateException(e) + }*/ + return null } fun loadGraphics(data: Array) { @@ -96,7 +88,7 @@ class MTMCDisplay(private val computer: MonTanaMiniComputer) { } graphics = Array(data.size) { index -> - loadImage(data[index]) ?: BufferedImage(160, 144, BufferedImage.TYPE_INT_RGB) + loadImage(data[index]) ?: BufferedImage(160, 144, 1) //BufferedImage.TYPE_INT_RGB) } } @@ -128,31 +120,31 @@ class MTMCDisplay(private val computer: MonTanaMiniComputer) { } fun drawLine(startCol: Short, startRow: Short, endCol: Short, endRow: Short) { - val graphics = buffer.getGraphics() - graphics.setColor(currentColor!!.javaColor) - graphics.drawLine(startCol.toInt(), startRow.toInt(), endCol.toInt(), endRow.toInt()) - graphics.dispose() +// val graphics = buffer.getGraphics() +// graphics.setColor(currentColor!!.javaColor) +// graphics.drawLine(startCol.toInt(), startRow.toInt(), endCol.toInt(), endRow.toInt()) +// graphics.dispose() } fun drawRectangle(startCol: Short, startRow: Short, width: Short, height: Short) { - val graphics = buffer.getGraphics() - graphics.setColor(currentColor!!.javaColor) - graphics.fillRect(startCol.toInt(), startRow.toInt(), width.toInt(), height.toInt()) - graphics.dispose() +// val graphics = buffer.getGraphics() +// graphics.setColor(currentColor!!.javaColor) +// graphics.fillRect(startCol.toInt(), startRow.toInt(), width.toInt(), height.toInt()) +// graphics.dispose() } fun drawImage(image: Int, x: Int, y: Int) { val graphic = graphics!![image] - val graphics = buffer.getGraphics() - graphics.drawImage(graphic, x, y, null) - graphics.dispose() +// val graphics = buffer.getGraphics() +// graphics.drawImage(graphic, x, y, null) +// graphics.dispose() } fun drawImage(image: Int, x: Int, y: Int, width: Int, height: Int) { val graphic = graphics!![image] - val graphics = buffer.getGraphics() - graphics.drawImage(graphic, x, y, width, height, null) - graphics.dispose() +// val graphics = buffer.getGraphics() +// graphics.drawImage(graphic, x, y, width, height, null) +// graphics.dispose() } fun drawImage( @@ -167,20 +159,20 @@ class MTMCDisplay(private val computer: MonTanaMiniComputer) { dh: Int ) { val graphic = graphics!![image] - val graphics = buffer.getGraphics() - graphics.drawImage(graphic, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null) - graphics.dispose() +// val graphics = buffer.getGraphics() +// graphics.drawImage(graphic, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null) +// graphics.dispose() } fun sync() { - val baos = ByteArrayOutputStream() - try { - buffer.flush() - ImageIO.write(buffer, "png", baos) - byteArray = baos.toByteArray() - } catch (e: IOException) { - throw RuntimeException(e) - } + /* val baos = ByteArrayOutputStream() + try { + buffer.flush() + ImageIO.write(buffer, "png", baos) + byteArray = baos.toByteArray() + } catch (e: IOException) { + throw RuntimeException(e) + }*/ computer.notifyOfDisplayUpdate() } @@ -240,21 +232,24 @@ class MTMCDisplay(private val computer: MonTanaMiniComputer) { fun scaleImage(original: BufferedImage, scaleDimensions: Dimension): BufferedImage { val resized = BufferedImage(scaleDimensions.width, scaleDimensions.height, original.getType()) - val g = resized.createGraphics() - g.setRenderingHint( - RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR - ) - g.drawImage( - original, 0, 0, scaleDimensions.width, scaleDimensions.height, 0, 0, original.getWidth(), - original.getHeight(), null - ) - g.dispose() + /* + val g = resized.createGraphics() + g.setRenderingHint( + RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR + ) + g.drawImage( + original, 0, 0, scaleDimensions.width, scaleDimensions.height, 0, 0, original.getWidth(), + original.getHeight(), null + ) + g.dispose() + */ return resized } fun convertImage(image: BufferedImage): BufferedImage { - val arbg = BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR) + val arbg = + BufferedImage(image.getWidth(), image.getHeight(), 2) //BufferedImage.TYPE_4BYTE_ABGR) for (x in 0.. 0) Thread.sleep(millis.toLong()) - } catch (e: InterruptedException) { - throw RuntimeException(e) - } +// } else if (syscallNumber == getValue("sleep").toShort()) { +// // sleep +// val millis = computer.getRegisterValue(Register.A0) +// try { +// if (millis > 0) Thread.sleep(millis.toLong()) +// } catch (e: InterruptedException) { +// throw RuntimeException(e) +// } } else if (syscallNumber == getValue("fbreset").toShort()) { // fbreset computer.display.reset() diff --git a/src/commonMain/kotlin/mtmc/os/fs/File.kt b/src/commonMain/kotlin/mtmc/os/fs/File.kt index 5791843..eb36031 100644 --- a/src/commonMain/kotlin/mtmc/os/fs/File.kt +++ b/src/commonMain/kotlin/mtmc/os/fs/File.kt @@ -20,8 +20,6 @@ class File( constructor(name: String) : this(null, name) - fun getParent(): File = parent ?: error("No parent") - fun getPath(): String = if (parent == null) { name } else { diff --git a/src/commonMain/kotlin/mtmc/os/fs/Path.kt b/src/commonMain/kotlin/mtmc/os/fs/Path.kt index d005d17..d783fde 100644 --- a/src/commonMain/kotlin/mtmc/os/fs/Path.kt +++ b/src/commonMain/kotlin/mtmc/os/fs/Path.kt @@ -12,4 +12,14 @@ class Path { fun resolve(filename: String): Path { TODO("Not yet implemented") } + + fun toAbsolutePath(): Path { + TODO("Not yet implemented") + } + + companion object { + fun of(string: String): Path { + TODO() + } + } } \ No newline at end of file diff --git a/src/commonMain/kotlin/mtmc/os/shell/Shell.kt b/src/commonMain/kotlin/mtmc/os/shell/Shell.kt index 4d8b057..04d6c4f 100644 --- a/src/commonMain/kotlin/mtmc/os/shell/Shell.kt +++ b/src/commonMain/kotlin/mtmc/os/shell/Shell.kt @@ -96,7 +96,7 @@ object Shell { return } } else { - cmd = identifier.stringValue() + cmd = identifier.stringValue } if (isCommand(cmd)) { COMMANDS[cmd.lowercase()]!!.exec(tokens, computer) @@ -106,7 +106,7 @@ object Shell { asm.addAll(tokens.tokens) val updatedAsm = Assembler.transformSyntheticInstructions(asm) val firstToken = updatedAsm.peekFirst() - val firstTokenStr = firstToken?.stringValue() ?: error("Unexpected null token") + val firstTokenStr = firstToken?.stringValue ?: error("Unexpected null token") if (!updatedAsm.isEmpty() && isInstruction(firstTokenStr)) { val assembler = Assembler() val result = assembler.assemble(command) diff --git a/src/commonMain/kotlin/mtmc/os/shell/builtins/GetCommand.kt b/src/commonMain/kotlin/mtmc/os/shell/builtins/GetCommand.kt index b5e36dc..2ca70f2 100644 --- a/src/commonMain/kotlin/mtmc/os/shell/builtins/GetCommand.kt +++ b/src/commonMain/kotlin/mtmc/os/shell/builtins/GetCommand.kt @@ -17,11 +17,11 @@ class GetCommand : ShellCommand() { if (memLocation == null) { val register = tokens.matchAndConsume(MTMCToken.TokenType.IDENTIFIER) if (register == null) usageException() - val reg = Register.toInteger(register!!.stringValue()) + val reg = Register.toInteger(register!!.stringValue) if (reg >= 0) { - computer.console.println(register.stringValue() + ": " + computer.getRegisterValue(reg)) + computer.console.println(register.stringValue + ": " + computer.getRegisterValue(reg)) } else { - throw IllegalArgumentException("Bad register: " + register.stringValue()) + throw IllegalArgumentException("Bad register: " + register.stringValue) } } else { if (memLocation.type === MTMCToken.TokenType.INTEGER || memLocation.type === MTMCToken.TokenType.BINARY || memLocation.type === MTMCToken.TokenType.HEX) { diff --git a/src/commonMain/kotlin/mtmc/os/shell/builtins/SetCommand.kt b/src/commonMain/kotlin/mtmc/os/shell/builtins/SetCommand.kt index 6dfffe8..71bbde3 100644 --- a/src/commonMain/kotlin/mtmc/os/shell/builtins/SetCommand.kt +++ b/src/commonMain/kotlin/mtmc/os/shell/builtins/SetCommand.kt @@ -23,11 +23,11 @@ class SetCommand : ShellCommand() { MTMCToken.TokenType.BINARY ) if (value == null) usageException() - val reg = Register.toInteger(register!!.stringValue()) + val reg = Register.toInteger(register!!.stringValue) if (reg >= 0) { computer.setRegisterValue(reg, value!!.intValue()) } else { - throw IllegalArgumentException("Bad register: " + register.stringValue()) + throw IllegalArgumentException("Bad register: " + register.stringValue) } } else { val value = tokens.matchAndConsume( @@ -42,7 +42,7 @@ class SetCommand : ShellCommand() { } else { computer.writeStringToMemory( memLocation.intValue(), - value!!.stringValue().encodeToByteArray() + value!!.stringValue.encodeToByteArray() ) } } diff --git a/src/commonMain/kotlin/mtmc/tokenizer/MTMCToken.kt b/src/commonMain/kotlin/mtmc/tokenizer/MTMCToken.kt index e3f4f2f..a6a2e55 100644 --- a/src/commonMain/kotlin/mtmc/tokenizer/MTMCToken.kt +++ b/src/commonMain/kotlin/mtmc/tokenizer/MTMCToken.kt @@ -12,10 +12,6 @@ data class MTMCToken( return stringValue } - fun stringValue(): String { - return stringValue - } - fun charValue(): Char { return stringValue.get(0) } diff --git a/src/commonMain/kotlin/mtmc/tokenizer/MTMCTokenizer.kt b/src/commonMain/kotlin/mtmc/tokenizer/MTMCTokenizer.kt index 0911056..495e96e 100644 --- a/src/commonMain/kotlin/mtmc/tokenizer/MTMCTokenizer.kt +++ b/src/commonMain/kotlin/mtmc/tokenizer/MTMCTokenizer.kt @@ -7,7 +7,7 @@ class MTMCTokenizer(var source: String, lineCommentStart: String) { var tokens: MutableList = MTMCScanner(source, lineCommentStart).tokenize() var currentToken: Int = 0 - fun currentToken(): MTMCToken { + fun getCurrentToken(): MTMCToken { return tokens.get(currentToken) } @@ -28,8 +28,8 @@ class MTMCTokenizer(var source: String, lineCommentStart: String) { } fun matchAndConsume(identifier: String?): Boolean { - if (currentToken().type == MTMCToken.TokenType.IDENTIFIER && - currentToken().stringValue == identifier + if (getCurrentToken().type == MTMCToken.TokenType.IDENTIFIER && + getCurrentToken().stringValue == identifier ) { return true } else { @@ -39,7 +39,7 @@ class MTMCTokenizer(var source: String, lineCommentStart: String) { fun match(vararg type: MTMCToken.TokenType?): Boolean { for (tokenType in type) { - if (currentToken().type == tokenType) { + if (getCurrentToken().type == tokenType) { return true } } @@ -51,7 +51,7 @@ class MTMCTokenizer(var source: String, lineCommentStart: String) { } fun more(): Boolean { - return currentToken().type != MTMCToken.TokenType.EOF + return getCurrentToken().type != MTMCToken.TokenType.EOF } fun previousToken(): MTMCToken? { @@ -83,7 +83,7 @@ class MTMCTokenizer(var source: String, lineCommentStart: String) { do { last = consume() sb.append(last.stringValue) - next = currentToken() + next = getCurrentToken() } while (more() && last.end == next.start) return sb.toString() } else { diff --git a/src/commonMain/kotlin/mtmc/util/PlatformSpecific.kt b/src/commonMain/kotlin/mtmc/util/PlatformSpecific.kt index eb0a634..a0e12a7 100644 --- a/src/commonMain/kotlin/mtmc/util/PlatformSpecific.kt +++ b/src/commonMain/kotlin/mtmc/util/PlatformSpecific.kt @@ -1,3 +1,6 @@ package mtmc.util -expect fun currentTimeMillis(): Long \ No newline at end of file +expect fun currentTimeMillis(): Long + +expect fun requestAnimationFrame(action: (Double) -> Unit) + diff --git a/src/jsMain/kotlin/mtmc/util/PlatformSpecific.js.kt b/src/jsMain/kotlin/mtmc/util/PlatformSpecific.js.kt index 8d0587b..43d6fbd 100644 --- a/src/jsMain/kotlin/mtmc/util/PlatformSpecific.js.kt +++ b/src/jsMain/kotlin/mtmc/util/PlatformSpecific.js.kt @@ -1,5 +1,9 @@ package mtmc.util +import kotlinx.browser.window import kotlin.js.Date actual fun currentTimeMillis(): Long = Date().getTime().toLong() +actual fun requestAnimationFrame(action: (Double) -> Unit) { + window.requestAnimationFrame(action) +} diff --git a/src/jvmMain/kotlin/mtmc/util/PlatformSpecific.jvm.kt b/src/jvmMain/kotlin/mtmc/util/PlatformSpecific.jvm.kt index 841f842..6592a46 100644 --- a/src/jvmMain/kotlin/mtmc/util/PlatformSpecific.jvm.kt +++ b/src/jvmMain/kotlin/mtmc/util/PlatformSpecific.jvm.kt @@ -1,3 +1,6 @@ package mtmc.util -actual fun currentTimeMillis(): Long = System.currentTimeMillis() \ No newline at end of file +actual fun currentTimeMillis(): Long = System.currentTimeMillis() +actual fun requestAnimationFrame(action: (Double) -> Unit) { + error("requestAnimationFrame is not supported on JVM") +}