initial commit

This commit is contained in:
minjaesong
2020-04-07 07:02:15 +09:00
commit da7e2e7c77
69 changed files with 1197 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
package net.torvald.tsvm;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
public class AppLoader {
public static String appTitle = "Totally Simple Virtual Machine";
public static LwjglApplicationConfiguration appConfig;
public static void main(String[] args) {
ShaderProgram.pedantic = false;
appConfig = new LwjglApplicationConfiguration();
appConfig.foregroundFPS = 60;
appConfig.backgroundFPS = 60;
appConfig.vSyncEnabled = false;
appConfig.useGL30 = true;
appConfig.resizable = false;
appConfig.title = appTitle;
appConfig.forceExit = true;
appConfig.width = 560;
appConfig.height = 448;
new LwjglApplication(new VMGUI(appConfig), appConfig);
}
public static ShaderProgram loadShaderFromFile(String vert, String frag) {
ShaderProgram s = new ShaderProgram(Gdx.files.internal(vert), Gdx.files.internal(frag));
if (s.getLog().toLowerCase().contains("error")) {
throw new Error(String.format("Shader program loaded with %s, %s failed:\n%s", vert, frag, s.getLog()));
}
return s;
}
public static ShaderProgram loadShaderInline(String vert, String frag) {
ShaderProgram s = new ShaderProgram(vert, frag);
if (s.getLog().toLowerCase().contains("error")) {
throw new Error(String.format("Shader program loaded with %s, %s failed:\n%s", vert, frag, s.getLog()));
}
return s;
}
}

View File

@@ -0,0 +1,141 @@
package net.torvald
import sun.misc.Unsafe
import java.io.PrintStream
/**
* Further read:
* - http://www.docjar.com/docs/api/sun/misc/Unsafe.html
*
* Created by minjaesong on 2019-06-21.
*/
internal object UnsafeHelper {
val unsafe: Unsafe
init {
val unsafeConstructor = Unsafe::class.java.getDeclaredConstructor()
unsafeConstructor.isAccessible = true
unsafe = unsafeConstructor.newInstance()
}
/**
* A factory method to allocate a memory of given size and return its starting address as a pointer.
*/
fun allocate(size: Long): UnsafePtr {
val ptr = unsafe.allocateMemory(size)
return UnsafePtr(ptr, size)
}
fun memcpy(src: UnsafePtr, fromIndex: Long, dest: UnsafePtr, toIndex: Long, copyLength: Long) =
unsafe.copyMemory(src.ptr + fromIndex, dest.ptr + toIndex, copyLength)
fun memcpy(srcAddress: Long, destAddress: Long, copyLength: Long) =
unsafe.copyMemory(srcAddress, destAddress, copyLength)
fun memcpyRaw(srcObj: Any?, srcPos: Long, destObj: Any?, destPos: Long, len: Long) =
unsafe.copyMemory(srcObj, srcPos, destObj, destPos, len)
/**
* The array object in JVM is stored in this memory map:
*
* 0 w 2w *
* | Some identifier | Other identifier | the actual data ... |
*
* (where w = 4 for 32-bit JVM and 8 for 64-bit JVM. If Compressed-OOP is involved, things may get complicated)
*
* @return offset from the array's base memory address (aka pointer) that the actual data begins.
*/
fun getArrayOffset(obj: Any) = unsafe.arrayBaseOffset(obj.javaClass)
}
/**
* To allocate a memory, use UnsafeHelper.allocate(long)
*
* All the getFloat/Int/whatever methods will follow the endianness of your system,
* e.g. it'll be Little Endian on x86, Big Endian on PPC, User-defined on ARM; therefore these functions should not be
* used when the portability matters (e.g. Savefile). In such situations, do byte-wise operations will be needed.
*
* Use of hashCode() is forbidden, use the pointer instead.
*/
internal class UnsafePtr(pointer: Long, allocSize: Long) {
var destroyed = false
private set
var ptr: Long = pointer
private set
var size: Long = allocSize
private set
fun realloc(newSize: Long) {
ptr = UnsafeHelper.unsafe.reallocateMemory(ptr, newSize)
size = newSize
}
fun destroy() {
if (!destroyed) {
UnsafeHelper.unsafe.freeMemory(ptr)
println("[UnsafePtr] Destroying pointer $this; called from:")
printStackTrace(this)
destroyed = true
}
}
private inline fun checkNullPtr(index: Long) { // ignore what IDEA says and do inline this
// commenting out because of the suspected (or minor?) performance impact.
// 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)
assert(!destroyed) { throw NullPointerException("The pointer is already destroyed ($this)") }
// OOB Check: debugging purposes only -- comment out for the production
//if (index !in 0 until size) throw IndexOutOfBoundsException("Index: $index; alloc size: $size")
}
operator fun get(index: Long): Byte {
checkNullPtr(index)
return UnsafeHelper.unsafe.getByte(ptr + index)
}
operator fun set(index: Long, value: Byte) {
checkNullPtr(index)
UnsafeHelper.unsafe.putByte(ptr + index, value)
}
// NOTE: get/set multibyte values are NOT BYTE-ALIGNED!
fun getFloat(index: Long): Float {
checkNullPtr(index)
return UnsafeHelper.unsafe.getFloat(ptr + index)
}
fun getInt(index: Long): Int {
checkNullPtr(index)
return UnsafeHelper.unsafe.getInt(ptr + index)
}
fun setFloat(index: Long, value: Float) {
checkNullPtr(index)
UnsafeHelper.unsafe.putFloat(ptr + index, value)
}
fun setInt(index: Long, value: Int) {
checkNullPtr(index)
UnsafeHelper.unsafe.putInt(ptr + index, value)
}
fun fillWith(byte: Byte) {
UnsafeHelper.unsafe.setMemory(ptr, size, byte)
}
override fun toString() = "0x${ptr.toString(16)} with size $size"
override fun equals(other: Any?) = this.ptr == (other as UnsafePtr).ptr && this.size == other.size
inline fun printStackTrace(obj: Any) = printStackTrace(obj, System.out) // because of Java
fun printStackTrace(obj: Any, out: PrintStream = System.out) {
Thread.currentThread().stackTrace.forEach {
out.println("[${obj.javaClass.simpleName}] ... $it")
}
}
}

104
src/net/torvald/tsvm/VM.kt Normal file
View File

@@ -0,0 +1,104 @@
package net.torvald.tsvm
import net.torvald.UnsafeHelper
import net.torvald.UnsafePtr
import net.torvald.tsvm.peripheral.IOSpace
import net.torvald.tsvm.peripheral.PeriBase
/**
* 1 byte = 2 pixels
*
* 560x448@4bpp = 125 440 bytes
* 560x448@8bpp = 250 880 bytes
*
* -> 262144 bytes (256 kB)
*
* [USER AREA | HW AREA]
* Number of pheripherals = 8, of which the computer itself is considered as a peri.
*
* HW AREA = [Peripherals | MMIO | INTVEC]
*
* User area: 8 MB, hardware area: 8 MB
*
* 8192 kB
* User Space
* 1024 kB
* Peripheral #8
* 1024 kB
* Peripheral #7
* ...
* 1024 kB
* MMIO and Interrupt Vectors
* 128 kB
* MMIO for Peri #8
* 128 kB
* MMIO for Peri #7
* ...
* 128 kB
* MMIO for the computer
* 130816 bytes
* MMIO for Ports, etc.
* 256 bytes
* Vectors for 64 interrupts
*
*
*/
class VM(
_memsize: Int
) {
val memsize = minOf(USER_SPACE_SIZE, _memsize.toLong())
internal val usermem = UnsafeHelper.allocate(memsize)
val peripheralTable = Array(8) { PeripheralEntry() }
init {
peripheralTable[0] = PeripheralEntry(
"io",
IOSpace(),
HW_RESERVE_SIZE,
MMIO_SIZE.toInt() - 256,
64
)
}
fun findPeribyType(searchTerm: String): Int? {
for (i in 0..7) {
if (peripheralTable[i].type == searchTerm) return i
}
return null
}
fun dispose() {
usermem.destroy()
peripheralTable.forEach { it.peripheral?.dispose() }
}
/*
NOTE: re-fill peripheralTable whenever the VM cold-boots!
you are absolutely not supposed to hot-swap peripheral cards when the computer is on
*/
companion object {
val MMIO_SIZE = 128.kB()
val HW_RESERVE_SIZE = 1024.kB()
val USER_SPACE_SIZE = 8192.kB()
const val PERITYPE_GRAPHICS = "gpu"
}
}
data class PeripheralEntry(
val type: String = "null",
val peripheral: PeriBase? = null,
val memsize: Long = 0,
val mmioSize: Int = 0,
val interruptCount: Int = 0 // max: 4
)
fun Int.kB() = this * 1024L

View File

@@ -0,0 +1,119 @@
package net.torvald.tsvm
import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.tsvm.peripheral.GraphicsAdapter
import kotlin.math.roundToInt
class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() {
val vm = VM(8192)
lateinit var gpu: GraphicsAdapter
lateinit var batch: SpriteBatch
lateinit var camera: OrthographicCamera
override fun create() {
super.create()
gpu = GraphicsAdapter()
vm.peripheralTable[1] = PeripheralEntry(
VM.PERITYPE_GRAPHICS,
gpu,
256.kB(),
16,
0
)
batch = SpriteBatch()
camera = OrthographicCamera(appConfig.width.toFloat(), appConfig.height.toFloat())
camera.setToOrtho(false)
camera.update()
batch.projectionMatrix = camera.combined
Gdx.gl20.glViewport(0, 0, appConfig.width, appConfig.height)
}
private var updateAkku = 0.0
private var updateRate = 1f / 60f
override fun render() {
Gdx.graphics.setTitle("${AppLoader.appTitle} $EMDASH F: ${Gdx.graphics.framesPerSecond}")
super.render()
val dt = Gdx.graphics.rawDeltaTime
updateAkku += dt
var i = 0L
while (updateAkku >= updateRate) {
updateGame(updateRate)
updateAkku -= updateRate
i += 1
}
renderGame()
}
private fun updateGame(delta: Float) {
paintTestPalette()
}
private fun paintTestPalette() {
for (y in 0 until 360) {
for (x in 0 until GraphicsAdapter.WIDTH) {
val palnum = 20 * (y / (360 / 12)) + (x / (GraphicsAdapter.WIDTH / 20))
gpu.poke(y.toLong() * GraphicsAdapter.WIDTH + x, palnum.toByte())
}
}
for (y in 360 until GraphicsAdapter.HEIGHT) {
for (x in 0 until GraphicsAdapter.WIDTH) {
val palnum = 240 + (x / (GraphicsAdapter.WIDTH / 16))
gpu.poke(y.toLong() * GraphicsAdapter.WIDTH + x, palnum.toByte())
}
}
gpu.poke(262142L, Math.random().times(255.0).toByte())
gpu.poke(262143L, Math.random().times(255.0).toByte())
/*
local vm = require("rawmemoryaccess")
local w = 560
local h = 448
local peripheral_slot = 1
for y = 0, 359 do
for x = 0, w - 1 do
palnum = 20 * (y / (360 / 12)) + (x / (w / 20))
vm.poke(-(y * w + x + 1) - 1048576 * peripheral_slot, palnum)
end
end
for y = 360, h - 1 do
for x = 0, w - 1 do
palnum = 240 + (x / (w / 16))
vm.poke(-(y * w + x + 1) - 1048576 * peripheral_slot, palnum)
end
end
vm.poke(-262143 - 1048576 * peripheral_slot, math.floor(math.random() * 255.0))
vm.poke(-262144 - 1048576 * peripheral_slot, math.floor(math.random() * 255.0))
*/
}
private fun renderGame() {
gpu.render(batch, 0f, 0f)
}
override fun dispose() {
super.dispose()
}
}
const val EMDASH = 0x2014.toChar()

View File

@@ -0,0 +1,66 @@
package net.torvald.tsvm.firmware
import net.torvald.UnsafePtr
import net.torvald.tsvm.VM
import net.torvald.tsvm.kB
import net.torvald.tsvm.peripheral.PeriBase
import org.luaj.vm2.LuaTable
import org.luaj.vm2.LuaValue
import org.luaj.vm2.lib.OneArgFunction
import org.luaj.vm2.lib.TwoArgFunction
internal class Firmware(val vm: VM) {
val t = LuaTable()
fun errorIllegalAccess(addr: Long) {
}
private fun translateAddr(addr: LuaValue): Pair<Any?, Long> {
val addr = addr.checklong()
return when (addr) {
// DO note that numbers in Lua are double precision floats (ignore Lua 5.3 for now)
in 0..8192.kB() - 1 -> vm.usermem to addr
in -1024.kB()..-1 -> vm.peripheralTable[0].peripheral to (-addr - 1)
in -2048.kB()..-1024.kB()-1 -> vm.peripheralTable[1].peripheral to (-addr - 1 - 1024.kB())
in -3072.kB()..-2048.kB()-1 -> vm.peripheralTable[2].peripheral to (-addr - 1 - 2048.kB())
in -4096.kB()..-3072.kB()-1 -> vm.peripheralTable[3].peripheral to (-addr - 1 - 3072.kB())
in -5120.kB()..-4096.kB()-1 -> vm.peripheralTable[4].peripheral to (-addr - 1 - 4096.kB())
in -6144.kB()..-5120.kB()-1 -> vm.peripheralTable[5].peripheral to (-addr - 1 - 5120.kB())
in -7168.kB()..-6144.kB()-1 -> vm.peripheralTable[6].peripheral to (-addr - 1 - 6144.kB())
in -8192.kB()..-7168.kB()-1 -> vm.peripheralTable[7].peripheral to (-addr - 1 - 7168.kB())
else -> null to addr
}
}
init {
t["poke"] = object : TwoArgFunction() {
override fun call(addr: LuaValue, value: LuaValue): LuaValue {
val (memspace, offset) = translateAddr(addr)
if (memspace == null)
errorIllegalAccess(addr.checklong())
else if (memspace is UnsafePtr)
memspace.set(offset, value.checkint().toByte())
else
(memspace as PeriBase).poke(offset, value.checkint().toByte())
return LuaValue.NIL
}
}
t["peek"] = object : OneArgFunction() {
override fun call(addr: LuaValue): LuaValue {
val (memspace, offset) = translateAddr(addr)
return if (memspace == null)
LuaValue.NIL
else if (memspace is UnsafePtr)
memspace.get(offset).toLuaValue()
else
(memspace as PeriBase).peek(offset)?.toLuaValue() ?: LuaValue.NIL
}
}
}
fun Byte.toLuaValue() = LuaValue.valueOf(this.toInt())
}

View File

@@ -0,0 +1,427 @@
package net.torvald.tsvm.peripheral
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.tsvm.AppLoader
import net.torvald.tsvm.VM
import net.torvald.tsvm.kB
class GraphicsAdapter : PeriBase {
internal val framebuffer = Pixmap(WIDTH, HEIGHT, Pixmap.Format.RGBA8888)
private var rendertex = Texture(1, 1, Pixmap.Format.RGBA8888)
private val paletteOfFloats = FloatArray(1024) {
val rgba = DEFAULT_PALETTE[it / 4]
val channel = it % 4
rgba.shr((3 - channel) * 8).and(255) / 255f
}
private val paletteShader = AppLoader.loadShaderInline(DRAW_SHADER_VERT, DRAW_SHADER_FRAG)
private var textmodeBlinkCursor = true
private var graphicsUseSprites = false
private var lastUsedColour = (-1).toByte()
init {
framebuffer.blending = Pixmap.Blending.None
framebuffer.setColor(-1)
framebuffer.fill()
for (k in 0..1023) {
if (k != 0 && k % 4 == 0)
println()
print(paletteOfFloats[k])
print(" ")
}
}
override fun peek(addr: Long): Byte? {
val adi = addr.toInt()
return when (addr) {
in 0 until 250880 -> framebuffer.getPixel(adi % WIDTH, adi / WIDTH).toByte()
in 261632 until 262144 -> peekPalette(adi - 261632)
in 0 until VM.HW_RESERVE_SIZE -> peek(addr % VRAM_SIZE) // HW mirroring
else -> null
}
}
override fun poke(addr: Long, byte: Byte) {
val adi = addr.toInt()
val bi = byte.toInt().and(255)
when (addr) {
in 0 until 250880 -> {
lastUsedColour = byte
framebuffer.drawPixel(adi % WIDTH, adi / WIDTH, bi.shl(24))
}
in 261632 until 262144 -> pokePalette(adi - 261632, byte)
in 0 until VM.HW_RESERVE_SIZE -> poke(addr % VRAM_SIZE, byte) // HW mirroring
}
}
override fun mmio_read(addr: Long): Byte? {
return when (addr) {
0L -> (WIDTH % 256).toByte()
1L -> (WIDTH / 256).toByte()
2L -> (HEIGHT % 256).toByte()
3L -> (HEIGHT / 256).toByte()
4L -> 70
5L -> 32
6L -> textmodeBlinkCursor.toInt().toByte()
7L -> graphicsUseSprites.toInt().toByte()
8L -> lastUsedColour
in 0 until VM.MMIO_SIZE -> -1
else -> null
}
}
override fun mmio_write(addr: Long, byte: Byte) {
TODO("Not yet implemented")
}
override fun dispose() {
framebuffer.dispose()
rendertex.dispose()
}
fun render(batch: SpriteBatch, x: Float, y: Float) {
rendertex.dispose()
rendertex = Texture(framebuffer)
batch.begin()
batch.color = Color.WHITE
batch.shader = paletteShader
paletteShader.setUniform4fv("pal", paletteOfFloats, 0, paletteOfFloats.size)
// must be done every time the shader is "actually loaded"
// try this: if above line precedes 'batch.shader = paletteShader', it won't work
batch.draw(rendertex, x, y)
batch.end()
batch.shader = null
}
private fun peekPalette(offset: Int): Byte {
val highvalue = paletteOfFloats[offset * 2] // R, B
val lowvalue = paletteOfFloats[offset * 2 + 1] // G, A
return (highvalue.div(15f).toInt().shl(4) or lowvalue.div(15f).toInt()).toByte()
}
private fun pokePalette(offset: Int, byte: Byte) {
val highvalue = byte.toInt().and(0xF0).ushr(4) / 15f
val lowvalue = byte.toInt().and(0x0F) / 15f
paletteOfFloats[offset * 2] = highvalue
paletteOfFloats[offset * 2 + 1] = lowvalue
}
private fun Boolean.toInt() = if (this) 1 else 0
companion object {
const val WIDTH = 560
const val HEIGHT = 448
val VRAM_SIZE = 256.kB()
val DRAW_SHADER_FRAG = """
#version 120
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform vec4 pal[256];
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
void main(void) {
gl_FragColor = pal[int(texture2D(u_texture, v_texCoords).r * 255.0)];
//gl_FragColor = vec4(texture2D(u_texture, v_texCoords).rrr, 1.0);
}
""".trimIndent()
val DRAW_SHADER_VERT = """
#version 120
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec4 v_color;
varying vec2 v_texCoords;
void main() {
v_color = a_color;
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
""".trimIndent()
val DEFAULT_PALETTE = intArrayOf( // 0b rrrrrrrr gggggggg bbbbbbbb aaaaaaaa
255,
17663,
35071,
48127,
65535,
2228479,
2245887,
2263295,
2276351,
2293759,
4456703,
4474111,
4491519,
4504575,
4521983,
6684927,
6702335,
6719743,
6732799,
6750207,
10027263,
10044671,
10062079,
10075135,
10092543,
12255487,
12272895,
12290303,
12303359,
12320767,
14483711,
14501119,
14518527,
14531583,
14548991,
16711935,
16729343,
16746751,
16759807,
16777215,
855638271,
855655679,
855673087,
855686143,
855703551,
857866495,
857883903,
857901311,
857914367,
857931775,
860094719,
860112127,
860129535,
860142591,
860159999,
862322943,
862340351,
862357759,
862370815,
862388223,
865665279,
865682687,
865700095,
865713151,
865730559,
867893503,
867910911,
867928319,
867941375,
867958783,
870121727,
870139135,
870156543,
870169599,
870187007,
872349951,
872367359,
872384767,
872397823,
872415231,
1711276287,
1711293695,
1711311103,
1711324159,
1711341567,
1713504511,
1713521919,
1713539327,
1713552383,
1713569791,
1715732735,
1715750143,
1715767551,
1715780607,
1715798015,
1717960959,
1717978367,
1717995775,
1718008831,
1718026239,
1721303295,
1721320703,
1721338111,
1721351167,
1721368575,
1723531519,
1723548927,
1723566335,
1723579391,
1723596799,
1725759743,
1725777151,
1725794559,
1725807615,
1725825023,
1727987967,
1728005375,
1728022783,
1728035839,
1728053247,
-1728052993,
-1728035585,
-1728018177,
-1728005121,
-1727987713,
-1725824769,
-1725807361,
-1725789953,
-1725776897,
-1725759489,
-1723596545,
-1723579137,
-1723561729,
-1723548673,
-1723531265,
-1721368321,
-1721350913,
-1721333505,
-1721320449,
-1721303041,
-1718025985,
-1718008577,
-1717991169,
-1717978113,
-1717960705,
-1715797761,
-1715780353,
-1715762945,
-1715749889,
-1715732481,
-1713569537,
-1713552129,
-1713534721,
-1713521665,
-1713504257,
-1711341313,
-1711323905,
-1711306497,
-1711293441,
-1711276033,
-872414977,
-872397569,
-872380161,
-872367105,
-872349697,
-870186753,
-870169345,
-870151937,
-870138881,
-870121473,
-867958529,
-867941121,
-867923713,
-867910657,
-867893249,
-865730305,
-865712897,
-865695489,
-865682433,
-865665025,
-862387969,
-862370561,
-862353153,
-862340097,
-862322689,
-860159745,
-860142337,
-860124929,
-860111873,
-860094465,
-857931521,
-857914113,
-857896705,
-857883649,
-857866241,
-855703297,
-855685889,
-855668481,
-855655425,
-855638017,
-16776961,
-16759553,
-16742145,
-16729089,
-16711681,
-14548737,
-14531329,
-14513921,
-14500865,
-14483457,
-12320513,
-12303105,
-12285697,
-12272641,
-12255233,
-10092289,
-10074881,
-10057473,
-10044417,
-10027009,
-6749953,
-6732545,
-6715137,
-6702081,
-6684673,
-4521729,
-4504321,
-4486913,
-4473857,
-4456449,
-2293505,
-2276097,
-2258689,
-2245633,
-2228225,
-65281,
-47873,
-30465,
-17409,
-1,
255,
286331391,
572662527,
858993663,
1145324799,
1431655935,
1717987071,
2004318207,
-2004317953,
-1717986817,
-1431655681,
-1145324545,
-858993409,
-572662273,
-286331137,
0
)
}
}

View File

@@ -0,0 +1,23 @@
package net.torvald.tsvm.peripheral
class IOSpace : PeriBase {
override fun peek(addr: Long): Byte? {
TODO("Not yet implemented")
}
override fun poke(addr: Long, byte: Byte) {
TODO("Not yet implemented")
}
override fun mmio_read(addr: Long): Byte? {
TODO("Not yet implemented")
}
override fun mmio_write(addr: Long, byte: Byte) {
TODO("Not yet implemented")
}
override fun dispose() {
TODO("Not yet implemented")
}
}

View File

@@ -0,0 +1,19 @@
package net.torvald.tsvm.peripheral
interface PeriBase {
/**
* Addr is not an offset; they can be "wired" into any other "chip" in the card other than its RAM
*/
fun peek(addr: Long): Byte?
/**
* Addr is not an offset; they can be "wired" into any other "chip" in the card other than its RAM
*/
fun poke(addr: Long, byte: Byte)
fun mmio_read(addr: Long): Byte?
fun mmio_write(addr: Long, byte: Byte)
fun dispose()
}