diff --git a/build.gradle.kts b/build.gradle.kts index 2066143..5ef9a49 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,6 +19,8 @@ dependencies { implementation("info.picocli:picocli:4.7.5") implementation("org.jline:jline-terminal:3.23.0") implementation("org.jline:jline-terminal-jansi:3.23.0") + implementation("ch.qos.logback:logback-classic:1.4.11") + // implementation("org.slf4j:slf4j-api") } @@ -32,4 +34,4 @@ kotlin { application { mainClass.set("MainKt") -} \ No newline at end of file +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..5a19f9d --- /dev/null +++ b/flake.lock @@ -0,0 +1,252 @@ +{ + "nodes": { + "devenv": { + "inputs": { + "flake-compat": "flake-compat", + "nix": "nix", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1692700763, + "narHash": "sha256-pWbQEVSjlDfrrBoHv5XFe+IdYVpJXY8C9Wz7rO98yqU=", + "owner": "cachix", + "repo": "devenv", + "rev": "6b84a85dd5cde41cbf992b6b2445bf25fe0abb21", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1676545802, + "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "owner": "domenkozar", + "repo": "nix", + "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1678875422, + "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1685801374, + "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1692794066, + "narHash": "sha256-H0aG8r16dj0x/Wz6wQhQxc9V7AsObOiHPaKxQgH6Y08=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "fc944919f743bb22379dddf18dcb72db6cff84aa", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1688056373, + "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs_2", + "systems": "systems_2" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..c732f68 --- /dev/null +++ b/flake.nix @@ -0,0 +1,41 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05"; + systems.url = "github:nix-systems/default"; + devenv.url = "github:cachix/devenv"; + }; + + nixConfig = { + extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="; + extra-substituters = "https://devenv.cachix.org"; + }; + + outputs = { self, nixpkgs, devenv, systems, ... } @ inputs: + let + forEachSystem = nixpkgs.lib.genAttrs (import systems); + in + { + devShells = forEachSystem + (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + default = devenv.lib.mkShell { + inherit inputs pkgs; + modules = [ + { + # https://devenv.sh/reference/options/ + packages = [ pkgs.hello ]; + languages.java.enable = true; + languages.java.gradle.enable = true; + + enterShell = '' + hello + ''; + } + ]; + }; + }); + }; +} diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 2f62983..6119d32 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -18,10 +18,15 @@ fun main(args: Array) { cpu.loadAbs(File(args[0])) cpu.runState = CPU.RunState.RUNNING cpu.pc = 0x80u - cpu.run() + val start = System.nanoTime() + val ninsn = cpu.run() + val end = System.nanoTime() + System.err.println("Halted at 0${cpu.pc.toString(8)}") + val time = (end-start).toDouble() / 1_000_000_000.0 + println("Executed ${ninsn} in ${time}s: ${ninsn/time} i/s") } finally { println("Exiting") console.stop() } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt b/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt index 71eaf29..b76e44f 100644 --- a/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt +++ b/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt @@ -93,6 +93,21 @@ class CPU(val mbus: MemBus) { set(value) { psw = (psw and 0xFFFu) or (value and 0xFu) } + private var pirq: UShort = 0u + set(value) { + val bitset = pirq shr 8 + var maxp: UShort = 0u + for (i in 7 downTo 1) { + if (bitset bit i) { + maxp = i.toUShort() shl 1 + break + } + } + field = value and 0xFE00u or maxp or (maxp shl 4) + } + + private var cpu_err: UShort = 0u + set(value) { field = value and 0xFCu } private var N: Boolean = false private var C: Boolean = false @@ -757,12 +772,15 @@ class CPU(val mbus: MemBus) { } fun step() { - try { if (runState == RunState.HALTED) return // TODO: handle PIRQ - for (i in (psw_priority + 1..<7).reversed()) { + for (i in 7 downTo (psw_priority + 1)) { + if (pirq bit 8+i) { + trap(0xA0u) + break + } val source = mbus.unibus.checkInterrupt(i) if (source != null) { // we might have been waiting for an interrupt @@ -784,10 +802,13 @@ class CPU(val mbus: MemBus) { } } - fun run() { + fun run(): Long { + var ninsn: Long = 0 while (runState == RunState.RUNNING) { + ninsn++ step() } + return ninsn } fun trap(vector: UShort) { @@ -799,14 +820,25 @@ class CPU(val mbus: MemBus) { private inner class Registers: PAddressSpace { override fun getw(addr: UInt): UShort = when (addr) { + 0x3FFF0u -> (mbus.size shr 6).toUShort() + 0x3FFF2u -> 0u // upper size + 0x3FFF4u -> 0x1170u // system ID + 0x3FFF6u -> cpu_err + 0x3FFFAu -> pirq // PIRQ + 0x3FFFCu -> 0x0u // stack limit 0x3FFFEu -> psw + else -> throw BusTimeoutError(addr) } override fun setw(addr: UInt, value: UShort) = when (addr) { + 0x3_FFF0u , 0x3_FFF2u , 0x3_FFF4u, 0x3_FFF8u -> {} // read-only registers + 0x3_FFF6u -> cpu_err = value + 0x3_FFFAu -> pirq = value + 0x3_FFFCu -> {} // stack limit 0x3_FFFEu -> psw = value else -> { - println("Bus error at ${addr.toString(16)}") + println("Bus error at ${addr.toString(16)}: ${value.toString(16)}") throw BusTimeoutError(addr) } } diff --git a/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt b/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt index 64205e8..37eb143 100644 --- a/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt +++ b/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt @@ -10,6 +10,7 @@ import com.thequux.mcpdp.ext.bit.bit import com.thequux.mcpdp.ext.bit.maskSet import java.io.InputStream import java.io.OutputStream +import java.io.InterruptedIOException import java.util.concurrent.Semaphore import java.util.concurrent.locks.Lock import java.util.concurrent.locks.LockSupport @@ -110,6 +111,8 @@ class DL11(private var istr: InputStream, private val ostr: OutputStream, val re break } } + } catch (_: InterruptedIOException) { + // Nothing to do; time to shut down } catch (_: InterruptedException) { // Nothing to do; time to shut down } @@ -124,4 +127,4 @@ class DL11(private var istr: InputStream, private val ostr: OutputStream, val re super.stop() reader?.interrupt() } -} \ No newline at end of file +}