From b8b356b8b3cc77a56969c7b7d5e6fb6c6a2a39e5 Mon Sep 17 00:00:00 2001 From: TQ Hirsch Date: Tue, 26 Sep 2023 20:56:04 +0200 Subject: [PATCH] Added new trace infrastructure --- src/main/kotlin/Main.kt | 11 +- src/main/kotlin/com/thequux/mcpdp/core/CPU.kt | 64 ++++++----- .../DebugTools.kt => debug/Disassembler.kt} | 54 ++++------ .../thequux/mcpdp/debug/LoggingCollector.kt | 15 +++ .../com/thequux/mcpdp/debug/TraceRecord.kt | 36 +++++++ .../kotlin/com/thequux/mcpdp/debug/Tracer.kt | 101 ++++++++++++++++++ .../com/thequux/mcpdp/ext/bit/bitext.kt | 100 ++++++++--------- .../com/thequux/mcpdp/ext/bit/formatting.kt | 6 +- 8 files changed, 275 insertions(+), 112 deletions(-) rename src/main/kotlin/com/thequux/mcpdp/{util/DebugTools.kt => debug/Disassembler.kt} (90%) create mode 100644 src/main/kotlin/com/thequux/mcpdp/debug/LoggingCollector.kt create mode 100644 src/main/kotlin/com/thequux/mcpdp/debug/TraceRecord.kt create mode 100644 src/main/kotlin/com/thequux/mcpdp/debug/Tracer.kt diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index c197c39..7f6ef05 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -1,5 +1,8 @@ import ch.qos.logback.classic.LoggerContext import com.thequux.mcpdp.core.CPU +import com.thequux.mcpdp.debug.LoggingCollector +import com.thequux.mcpdp.debug.NullTracer +import com.thequux.mcpdp.debug.Tracer import com.thequux.mcpdp.loadAbs import com.thequux.mcpdp.peripheral.DL11 import com.thequux.mcpdp.peripheral.MemBus @@ -10,10 +13,14 @@ fun main(args: Array) { val tb = org.jline.terminal.TerminalBuilder.terminal() var mbus = MemBus(32768) - var cpu = CPU(mbus) + val tracer = Tracer() + val loggingCollector = LoggingCollector() + tracer.addCollector(loggingCollector) + var cpu = CPU(mbus, if (CPU.debugMode) tracer else NullTracer()) val console = DL11(mbus.unibus, tb.input(), tb.output()).apply { mount(mbus.unibus) } try { + tb.enterRawMode() console.start() @@ -21,7 +28,7 @@ fun main(args: Array) { cpu.runState = CPU.RunState.RUNNING cpu.pc = 0x80u val start = System.nanoTime() - val ninsn = cpu.run(200000000) + val ninsn = cpu.run(600000000) val end = System.nanoTime() System.err.println("Halted at 0${cpu.pc.toString(8)}") diff --git a/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt b/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt index 8ff021e..8ed5540 100644 --- a/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt +++ b/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt @@ -2,7 +2,9 @@ package com.thequux.mcpdp.core import com.thequux.mcpdp.ext.bit.* import com.thequux.mcpdp.peripheral.MemBus -import com.thequux.mcpdp.util.Disassembler +import com.thequux.mcpdp.debug.Disassembler +import com.thequux.mcpdp.debug.ITracer +import com.thequux.mcpdp.debug.NullTracer import com.thequux.mcpdp.util.ProgrammerError import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -15,9 +17,14 @@ import java.lang.StringBuilder /// Exxx: ROM /// xxxx: rest @OptIn(ExperimentalUnsignedTypes::class) -class CPU(val mbus: MemBus) { +class CPU(val mbus: MemBus, val tracer: ITracer = NullTracer()) { companion object { + val PADDR_REG_BIT: Int = 23 + val PADDR_ISPACE_BIT: Int = 22 + val PADDR_DST_BIT: Int = 24 + val PADDR_ARG_BIT: Int = 25 + val debugMode = System.getProperty("jdp11.itrace").toBoolean() private val insnTable: Array Unit> = Array(256) {{throw InvalidOpcodeException()}} @@ -536,9 +543,9 @@ class CPU(val mbus: MemBus) { for (i in 0x90..0x9F) insnTable[i] = { opcode -> // MOVB val src = opc_srcb(opcode) val dst = opc_dstb(opcode) - var dstv = op_loadb(src) + val dstv = op_loadb(src) - var dstw = if (is_paddr_reg(dst)) { + if (is_paddr_reg(dst)) { op_storw(dst, dstv.toUShort() sex 8) dstv.toUShort() sex 8 } else { @@ -595,14 +602,13 @@ class CPU(val mbus: MemBus) { N = res < 0 Z = res == 0 V = ((srcv bit 31) xor (dstv bit 15)) and ((srcv bit 15) == (res bit 31)) - C = (dst.toInt() + src.inv().inc().toInt()) >= 0x1_0000 + C = (dst.toInt() + src.inv().inc().toInt()) < 0x1_0000 } // SUB // insnTable[0x0E] = // TODO: check this // insnTable[0x0F] = // TODO: check this } } - private var control_reg: UShort = 0u val logger: Logger = LoggerFactory.getLogger(this.javaClass) private val flagStr: String @@ -618,7 +624,6 @@ class CPU(val mbus: MemBus) { private val general_registers = Array(2) { UShortArray(6) } private val shadow_r6 = UShortArray(4) // val core = PagingUnit(mbus) - private val dasm = Disassembler(core) var runState: RunState = RunState.HALTED var psw: UShort @@ -662,7 +667,7 @@ class CPU(val mbus: MemBus) { set(value) { registers[7] = value } - private var sp: UShort + var sp: UShort get() = registers[6] set(value) { registers[6] = value @@ -708,8 +713,6 @@ class CPU(val mbus: MemBus) { private var Z: Boolean = false private var V: Boolean = false private var T: Boolean = false - private val PADDR_REG_BIT: Int = 23 - private val PADDR_ISPACE_BIT: Int = 22 init { val regs = Registers() @@ -724,21 +727,23 @@ class CPU(val mbus: MemBus) { private fun is_paddr_reg(addr: UInt): Boolean = addr bit PADDR_REG_BIT private fun is_paddr_ispace(addr: UInt): Boolean = addr bit PADDR_ISPACE_BIT - private fun op_resolve(operand: Int, byte_mode: Boolean = false): UInt { + private fun op_resolve(operand: Int, src: Boolean, byte_mode: Boolean = false): UInt { val mode = operand shr 3 val reg = operand and 0x7 val is_pc = reg == 7 val increment = if (byte_mode && !is_pc) 1U else 2U - return 0x80_FFFFu and when (mode and 0x7) { + val decoded = 0x80_FFFFu and when (mode and 0x7) { 0 -> reg.toUInt() bis PADDR_REG_BIT 1 -> registers[reg].toUInt() 2 -> { val addr = registers[reg] + if (reg == 7) tracer.noteArgument(op_loadw(addr.toUInt() bis PADDR_ISPACE_BIT, false)) registers[reg] = (addr + increment).toUShort() addr.toUInt().bit(PADDR_ISPACE_BIT, is_pc) } 3 -> { val addr = registers[reg] + if (reg == 7) tracer.noteArgument(op_loadw(addr.toUInt() bis PADDR_ISPACE_BIT, false)) registers[reg] = (addr + 2U).toUShort() core.getw(addr, dspace = !is_pc).toUInt() } @@ -752,16 +757,19 @@ class CPU(val mbus: MemBus) { } 6 -> { val idx = core.getw(pc, dspace = false) + tracer.noteArgument(idx) pc = (pc + 2u).toUShort() (idx + registers[reg]) and 0xFFFFu } 7 -> { val idx = core.getw(pc, dspace = false) + tracer.noteArgument(idx) pc = (pc+2u).toUShort() core.getw((idx+registers[reg]).toUShort()).toUInt() } else -> throw InvalidOpcodeException() // unreachable } + return decoded.bit(PADDR_DST_BIT, !src) bis PADDR_ARG_BIT } private fun op_storb(spec: UInt, value: UByte) { @@ -776,11 +784,13 @@ class CPU(val mbus: MemBus) { private fun op_loadb(spec: UInt): UByte { val addr = (spec and 0xFFFFu).toUShort() - return if (is_paddr_reg(spec)) { + val value = if (is_paddr_reg(spec)) { (registers[addr.toInt()] and 0xFFu).toUByte() } else { core.getb(addr) } + tracer.noteReference(spec, value.toUShort()) + return value } private fun op_storw(spec: UInt, value: UShort, dspace: Boolean = true) { @@ -795,17 +805,20 @@ class CPU(val mbus: MemBus) { private fun op_loadw(spec: UInt, dspace: Boolean=true): UShort { val addr = (spec and 0xFFFFu).toUShort() - return if (is_paddr_reg(spec)) { + val value = if (is_paddr_reg(spec)) { registers[addr.toInt()] } else { core.getw(addr, dspace) } + if (spec bit PADDR_ARG_BIT) + tracer.noteReference(spec, value) + return value } - private fun opc_dst(opcode: Int): UInt = op_resolve(opcode and 0x3F) - private fun opc_dstb(opcode: Int): UInt = op_resolve(opcode and 0x3F, byte_mode = true) - private fun opc_src(opcode: Int): UInt = op_resolve(opcode shr 6 and 0x3F) - private fun opc_srcb(opcode: Int): UInt = op_resolve(opcode shr 6 and 0x3F, byte_mode = true) + private fun opc_dst(opcode: Int): UInt = op_resolve(opcode and 0x3F, false) + private fun opc_dstb(opcode: Int): UInt = op_resolve(opcode and 0x3F, false, byte_mode = true) + private fun opc_src(opcode: Int): UInt = op_resolve(opcode shr 6 and 0x3F, true) + private fun opc_srcb(opcode: Int): UInt = op_resolve(opcode shr 6 and 0x3F, true, byte_mode = true) private fun stack_pop(): UShort = core.getw(sp).also { sp = (sp + 2u).toUShort() } private fun stack_push(value: UShort) { @@ -821,12 +834,15 @@ class CPU(val mbus: MemBus) { /// Internal step; evaluates one opcode, but does not handle errors, interrupts, or updating system registers fun step_int() { - if (debugMode) { - logger.debug("${pc.toString(8).padStart(6, '0')}: ${dasm.dasm_at(pc)}") + try { + tracer.noteBegin(this) + val opcode = core.getw(pc).toInt() + tracer.noteOpcode(opcode.toUShort()) + pc = (pc + 2u).toUShort() + this.(insnTable[opcode shr 8])(opcode) + } finally { + tracer.noteEnd() } - val opcode = core.getw(pc).toInt() - pc = (pc + 2u).toUShort() - this.(insnTable[opcode shr 8])(opcode) } private fun debugFlags() { @@ -907,7 +923,7 @@ class CPU(val mbus: MemBus) { } fun trap(vector: UShort) { - logger.info("Trap to {}", vector.toString(8)) +// logger.info("Trap to {}", vector.toString(8)) val old_psw = psw // update PSW first so that this gets pushed to the psw = core.getw((vector + 2u).toUShort()) diff --git a/src/main/kotlin/com/thequux/mcpdp/util/DebugTools.kt b/src/main/kotlin/com/thequux/mcpdp/debug/Disassembler.kt similarity index 90% rename from src/main/kotlin/com/thequux/mcpdp/util/DebugTools.kt rename to src/main/kotlin/com/thequux/mcpdp/debug/Disassembler.kt index bebeb85..25157e2 100644 --- a/src/main/kotlin/com/thequux/mcpdp/util/DebugTools.kt +++ b/src/main/kotlin/com/thequux/mcpdp/debug/Disassembler.kt @@ -1,37 +1,28 @@ -package com.thequux.mcpdp.util +package com.thequux.mcpdp.debug -import com.thequux.mcpdp.core.CPU import com.thequux.mcpdp.core.InvalidOpcodeException -import com.thequux.mcpdp.core.VAddressSpace -import com.thequux.mcpdp.ext.bit.* +import com.thequux.mcpdp.ext.bit.bit -object DebugTools { +@OptIn(ExperimentalUnsignedTypes::class) +class Disassembler { - -} - -class NullDisassembler() { - fun dasm_at(loc: UShort): String = "" -} -class Disassembler(val core: VAddressSpace) { - - var vpc: UShort = 0u + var istream: UShortArray = UShortArray(0) + var vpc: Int = 0 var opc: UShort = 0u var opcode: Int = 0 private fun fmt(opcode: String, vararg args: String): String { val res = StringBuilder() - var tpc = opc var bytes = 0 - while (tpc != vpc) { + for (word in istream) { + if (bytes != 0) + res.append('.') bytes += 2 - res.append(core.getw(tpc, dspace = false).toString(16).padStart(4, '0')) - res.append('.') - tpc = tpc.inc().inc() + res.append(word.toString(8).padStart(6, '0')) } while (bytes < 6) { - res.append("----.") + res.append(".------") bytes += 2 } res.append('\t') @@ -56,11 +47,7 @@ class Disassembler(val core: VAddressSpace) { } } - private fun getw(): UShort { - val ret = core.getw(vpc, dspace = false) - vpc = vpc.inc().inc() - return ret - } + private fun getw(): UShort = istream[vpc++] private fun arg_at(offset: Int): String { val rv = opcode shr offset and 0x3F val reg = rv and 0x7 @@ -72,7 +59,7 @@ class Disassembler(val core: VAddressSpace) { 2 -> if (reg == 7) "$ind#${getw().toString(8)}" else "$ind($rn)+" 4 -> "$ind-($rn)" 6 -> if (reg == 7) { - ind + (getw() + vpc).toShort().toString(8) + ind + (getw() + opc + (vpc * 2).toUInt()).toShort().toString(8) } else { "$ind${getw().toString(8)}($rn)" } @@ -86,22 +73,22 @@ class Disassembler(val core: VAddressSpace) { private fun br_rel(opc: String): String { val rel = (opcode and 0xFF) - (opcode and 0x80 shl 1) - val dst = (vpc.toInt() + rel.shl(1)).toUShort() + val dst = (this.opc.toInt() + (vpc * 2) + rel.shl(1)).toUShort() return fmt(opc, dst.toString(8)) } private fun rel6(): String { val rel = (opcode and 0x3F) - (opcode and 0x20 shl 1) - val dst = vpc.toInt() + rel + val dst = opc.toInt() + vpc*2 + rel return dst.toUShort().toString(8) } - fun dasm_at(loc: UShort): String { - vpc = loc + fun dasm_at(loc: UShort, istream: UShortArray): String { opc = loc + this.istream = istream + opcode = istream[0].toInt() - opcode = core.getw(vpc, dspace = false).toInt() - vpc = (vpc + 2u).toUShort() + vpc = 1 try { return when (opcode and 0xF000) { 0x0, 0x8000 -> when (opcode and 0xFF00) { @@ -225,7 +212,8 @@ class Disassembler(val core: VAddressSpace) { 2 -> { throw InvalidOpcodeException() } // MTPD // TODO - else -> throw InvalidOpcodeException()// Reserved + else -> throw InvalidOpcodeException() +// Reserved } else -> throw InvalidOpcodeException() diff --git a/src/main/kotlin/com/thequux/mcpdp/debug/LoggingCollector.kt b/src/main/kotlin/com/thequux/mcpdp/debug/LoggingCollector.kt new file mode 100644 index 0000000..39a1d39 --- /dev/null +++ b/src/main/kotlin/com/thequux/mcpdp/debug/LoggingCollector.kt @@ -0,0 +1,15 @@ +package com.thequux.mcpdp.debug + +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +class LoggingCollector(val logger: Logger = LoggerFactory.getLogger("ITrace")): Collector { + + init { + logger.debug("PC SP PSW |SRC DST |ISTREAM INSN") + } + private val disassembler = Disassembler() + override fun invoke(record: TraceRecord) { + logger.debug(record.report(disassembler)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/thequux/mcpdp/debug/TraceRecord.kt b/src/main/kotlin/com/thequux/mcpdp/debug/TraceRecord.kt new file mode 100644 index 0000000..99f7a1a --- /dev/null +++ b/src/main/kotlin/com/thequux/mcpdp/debug/TraceRecord.kt @@ -0,0 +1,36 @@ +@file:OptIn(ExperimentalUnsignedTypes::class) + +package com.thequux.mcpdp.debug + +import com.thequux.mcpdp.ext.bit.toOctal + +data class TraceRecord( + val pc: UShort, + val sp: UShort, + val psw: UShort, + val opc: UShort, + val args: UShortArray, + val src: Pair? = null, + val dst: Pair? = null, +) { + + @OptIn(ExperimentalUnsignedTypes::class) + companion object { + fun default(): TraceRecord = TraceRecord(0u, 0u, 0u, 0u, UShortArray(0)) + } + + private fun formatRef(ref: Pair?): String { + return if (ref == null) { + " ".repeat(15) + } else { + "${ref.second.toOctal()}@${ref.first.toOctal(8)}" + } + } + + fun report(disassembler: Disassembler = Disassembler()): String { + return "${pc.toOctal()} ${sp.toOctal()} ${psw.toOctal()}|" + + "${formatRef(src)} ${formatRef(dst)}|" + + disassembler.dasm_at(pc, args) + } +} + diff --git a/src/main/kotlin/com/thequux/mcpdp/debug/Tracer.kt b/src/main/kotlin/com/thequux/mcpdp/debug/Tracer.kt new file mode 100644 index 0000000..275feaa --- /dev/null +++ b/src/main/kotlin/com/thequux/mcpdp/debug/Tracer.kt @@ -0,0 +1,101 @@ +@file:OptIn(ExperimentalUnsignedTypes::class) + +package com.thequux.mcpdp.debug + +import ch.qos.logback.classic.LoggerContext +import com.thequux.mcpdp.core.CPU +import com.thequux.mcpdp.ext.bit.bit +import com.thequux.mcpdp.ext.bit.toOctal +import org.slf4j.LoggerFactory +import java.util.Arrays + +typealias Collector = (TraceRecord) -> Unit + +interface ITracer { + fun noteBegin(cpu: CPU) + fun noteOpcode(opcode: UShort) + fun noteArgument(arg: UShort) + fun noteEnd() + abstract fun noteReference(spec: UInt, value: UShort) +} + +/// An implementation of Tracer that does nothing, without need for a null check. +/// This should get JITed out, so should have no runtime overhead +class NullTracer: ITracer { + override fun noteBegin(cpu: CPU) {} + + override fun noteOpcode(opcode: UShort) {} + + override fun noteArgument(arg: UShort) {} + + override fun noteReference(spec: UInt, value: UShort) {} + + override fun noteEnd() {} +} +class Tracer : ITracer { + private val logger = LoggerFactory.getLogger(this.javaClass) + private var src: Pair? = null + private var dst: Pair? = null + private var inInsn: Boolean = false + + private var pc: UShort = 0u + private var psw: UShort = 0u + private var sp: UShort = 0u + private var opcode: UShort = 0u + private var opargs: UShortArray = UShortArray(3) + private var oparg_ct: Int = 0 + + private val collectors: HashSet<(TraceRecord) -> Unit> = HashSet() + + override fun noteBegin(cpu: CPU) { + if (inInsn) { + logger.warn("Began collection a new insn before committing the previous one. PC=${pc.toOctal()}") + } + pc = cpu.pc + psw = cpu.psw + sp = cpu.sp + src = null + dst = null + oparg_ct = 0 + inInsn = true + } + + override fun noteOpcode(opcode: UShort) { + this.opcode = opcode + noteArgument(opcode) + } + + override fun noteArgument(arg: UShort) { + opargs[oparg_ct++] = arg + } + + override fun noteReference(spec: UInt, value: UShort) { + val ref = (spec and 0xFFFFu) to value + if (spec bit CPU.PADDR_DST_BIT) { + dst = ref + } else { + src = ref + } + } + + override fun noteEnd() { + val args = UShortArray(oparg_ct) {opargs[it]} + oparg_ct = 0 + val record = TraceRecord( + pc, sp, psw, opcode, args, + src, dst, + ) + for (coll in collectors) { + coll(record) + } + inInsn = false + } + + fun addCollector(c: Collector) { + collectors.add(c) + } + + fun removeCollector(c: Collector) { + collectors.remove(c) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/thequux/mcpdp/ext/bit/bitext.kt b/src/main/kotlin/com/thequux/mcpdp/ext/bit/bitext.kt index b44b03a..359e369 100644 --- a/src/main/kotlin/com/thequux/mcpdp/ext/bit/bitext.kt +++ b/src/main/kotlin/com/thequux/mcpdp/ext/bit/bitext.kt @@ -1,4 +1,4 @@ -@file:Suppress("NOTHING_TO_INLINE") +@file:Suppress("NOTHING_TO_/*inline*/") package com.thequux.mcpdp.ext.bit @@ -6,82 +6,82 @@ import kotlin.experimental.and import kotlin.math.max import kotlin.math.min -inline infix fun Byte.shr(qty: Int): Byte = this.toInt().shr(min(qty, 7)).toByte() -inline infix fun Byte.shl(qty: Int): Byte = if (qty > 7) 0 else this.toInt().shl(qty).toByte() -inline infix fun Byte.bit(n: Int): Boolean = this.toInt() shr n and 1 != 0 -inline fun Byte.bit(n: Int, v: Boolean): Byte = if (v) this bis n else this bic n -inline infix fun Byte.bis(n: Int): Byte = this.toInt().or(1 shl n).toByte() -inline infix fun Byte.bic(n: Int): Byte = this.toInt().and(1.shl(n).inv()).toByte() -inline infix fun Byte.sex(n: Int): Byte { +/*inline*/ infix fun Byte.shr(qty: Int): Byte = this.toInt().shr(min(qty, 7)).toByte() +/*inline*/ infix fun Byte.shl(qty: Int): Byte = if (qty > 7) 0 else this.toInt().shl(qty).toByte() +/*inline*/ infix fun Byte.bit(n: Int): Boolean = this.toInt() shr n and 1 != 0 +/*inline*/ fun Byte.bit(n: Int, v: Boolean): Byte = if (v) this bis n else this bic n +/*inline*/ infix fun Byte.bis(n: Int): Byte = this.toInt().or(1 shl n).toByte() +/*inline*/ infix fun Byte.bic(n: Int): Byte = this.toInt().and(1.shl(n).inv()).toByte() +/*inline*/ infix fun Byte.sex(n: Int): Byte { val sign = 1.toByte() shl n+1 return ((this and sign.dec()) - (this and sign)).toByte() } -inline infix fun UByte.shr(qty: Int): UByte = this.toUInt().shr(min(qty, 7)).toUByte() -inline infix fun UByte.shl(qty: Int): UByte = if (qty > 7) 0U else this.toInt().shl(qty).toUByte() -inline infix fun UByte.bit(n: Int): Boolean = this.toUInt() shr n and 1U != 0U -inline fun UByte.bit(n: Int, v: Boolean): UByte = if (v) this bis n else this bic n -inline infix fun UByte.bis(n: Int): UByte = this.toUInt().or(1U shl n).toUByte() -inline infix fun UByte.bic(n: Int): UByte = this.toUInt().and(1U.shl(n).inv()).toUByte() -inline infix fun UByte.sex(n: Int): UByte { +/*inline*/ infix fun UByte.shr(qty: Int): UByte = this.toUInt().shr(min(qty, 7)).toUByte() +/*inline*/ infix fun UByte.shl(qty: Int): UByte = if (qty > 7) 0U else this.toInt().shl(qty).toUByte() +/*inline*/ infix fun UByte.bit(n: Int): Boolean = this.toUInt() shr n and 1U != 0U +/*inline*/ fun UByte.bit(n: Int, v: Boolean): UByte = if (v) this bis n else this bic n +/*inline*/ infix fun UByte.bis(n: Int): UByte = this.toUInt().or(1U shl n).toUByte() +/*inline*/ infix fun UByte.bic(n: Int): UByte = this.toUInt().and(1U.shl(n).inv()).toUByte() +/*inline*/ infix fun UByte.sex(n: Int): UByte { val sign = 1.toUByte() shl n+1 return ((this and sign.dec()) - (this and sign)).toUByte() } -inline infix fun Short.shr(qty: Int): Short = this.toInt().shr(min(qty, 15)).toShort() -inline infix fun Short.shl(qty: Int): Short = if (qty > 15) 0 else this.toInt().shl(qty).toShort() -inline infix fun Short.bit(n: Int): Boolean = this.toInt() shr n and 1 != 0 -inline fun Short.bit(n: Int, v: Boolean): Short = if (v) this bis n else this bic n -inline infix fun Short.bis(n: Int): Short = this.toInt().or(1 shl n).toShort() -inline infix fun Short.bic(n: Int): Short = this.toInt().and(1.shl(n).inv()).toShort() -inline infix fun Short.sex(n: Int): Short { +/*inline*/ infix fun Short.shr(qty: Int): Short = this.toInt().shr(min(qty, 15)).toShort() +/*inline*/ infix fun Short.shl(qty: Int): Short = if (qty > 15) 0 else this.toInt().shl(qty).toShort() +/*inline*/ infix fun Short.bit(n: Int): Boolean = this.toInt() shr n and 1 != 0 +/*inline*/ fun Short.bit(n: Int, v: Boolean): Short = if (v) this bis n else this bic n +/*inline*/ infix fun Short.bis(n: Int): Short = this.toInt().or(1 shl n).toShort() +/*inline*/ infix fun Short.bic(n: Int): Short = this.toInt().and(1.shl(n).inv()).toShort() +/*inline*/ infix fun Short.sex(n: Int): Short { val sign = 1.toShort() shl n+1 return ((this and sign.dec()) - (this and sign)).toShort() } -inline infix fun UShort.shr(qty: Int): UShort = this.toUInt().shr(min(qty, 15)).toUShort() -inline infix fun UShort.shl(qty: Int): UShort = if (qty > 15) 0U else this.toInt().shl(qty).toUShort() -inline infix fun UShort.bit(n: Int): Boolean = this.toUInt() shr n and 1U != 0U -inline fun UShort.bit(n: Int, v: Boolean): UShort = if (v) this bis n else this bic n -inline infix fun UShort.bis(n: Int): UShort = this.toUInt().or(1U shl n).toUShort() -inline infix fun UShort.bic(n: Int): UShort = this.toUInt().and(1U.shl(n).inv()).toUShort() -inline infix fun UShort.sex(n: Int): UShort { +/*inline*/ infix fun UShort.shr(qty: Int): UShort = this.toUInt().shr(min(qty, 15)).toUShort() +/*inline*/ infix fun UShort.shl(qty: Int): UShort = if (qty > 15) 0U else this.toInt().shl(qty).toUShort() +/*inline*/ infix fun UShort.bit(n: Int): Boolean = this.toUInt() shr n and 1U != 0U +/*inline*/ fun UShort.bit(n: Int, v: Boolean): UShort = if (v) this bis n else this bic n +/*inline*/ infix fun UShort.bis(n: Int): UShort = this.toUInt().or(1U shl n).toUShort() +/*inline*/ infix fun UShort.bic(n: Int): UShort = this.toUInt().and(1U.shl(n).inv()).toUShort() +/*inline*/ infix fun UShort.sex(n: Int): UShort { val sign = 1.toUShort() shl (n-1) return (this - (this and sign shl 1)).toUShort() } -inline fun UShort.maskSet(v: UShort, mask: UShort) = this and mask.inv() or (v and mask) +/*inline*/ fun UShort.maskSet(v: UShort, mask: UShort) = this and mask.inv() or (v and mask) -inline infix fun Int.bit(n: Int): Boolean = this shr n and 1 != 0 -inline fun Int.bit(n: Int, v: Boolean): Int = if (v) this bis n else this bic n -inline infix fun Int.bis(n: Int): Int = this.or(1 shl n) -inline infix fun Int.bic(n: Int): Int = this and (1 shl n).inv() -inline infix fun Int.sex(n: Int): Int { +/*inline*/ infix fun Int.bit(n: Int): Boolean = this shr n and 1 != 0 +/*inline*/ fun Int.bit(n: Int, v: Boolean): Int = if (v) this bis n else this bic n +/*inline*/ infix fun Int.bis(n: Int): Int = this.or(1 shl n) +/*inline*/ infix fun Int.bic(n: Int): Int = this and (1 shl n).inv() +/*inline*/ infix fun Int.sex(n: Int): Int { val sign = 1 shl n+1 return ((this and sign.dec()) - (this and sign)) } -inline infix fun UInt.bit(n: Int): Boolean = this shr n and 1U != 0U -inline fun UInt.bit(n: Int, v: Boolean): UInt = if (v) this bis n else this bic n -inline infix fun UInt.bis(n: Int): UInt = this.or(1U shl n) -inline infix fun UInt.bic(n: Int): UInt = this.and(1U.shl(n).inv()) -inline infix fun UInt.sex(n: Int): UInt { +/*inline*/ infix fun UInt.bit(n: Int): Boolean = this shr n and 1U != 0U +/*inline*/ fun UInt.bit(n: Int, v: Boolean): UInt = if (v) this bis n else this bic n +/*inline*/ infix fun UInt.bis(n: Int): UInt = this.or(1U shl n) +/*inline*/ infix fun UInt.bic(n: Int): UInt = this.and(1U.shl(n).inv()) +/*inline*/ infix fun UInt.sex(n: Int): UInt { val sign = 1U shl n+1 return ((this and sign.dec()) - (this and sign)) } -inline infix fun Long.bit(n: Int): Boolean = this shr n and 1L != 0L -inline fun Long.bit(n: Int, v: Boolean): Long = if (v) this bis n else this bic n -inline infix fun Long.bis(n: Int): Long = this.or(1L shl n) -inline infix fun Long.bic(n: Int): Long = this and (1L shl n).inv() -inline infix fun Long.sex(n: Int): Long { +/*inline*/ infix fun Long.bit(n: Int): Boolean = this shr n and 1L != 0L +/*inline*/ fun Long.bit(n: Int, v: Boolean): Long = if (v) this bis n else this bic n +/*inline*/ infix fun Long.bis(n: Int): Long = this.or(1L shl n) +/*inline*/ infix fun Long.bic(n: Int): Long = this and (1L shl n).inv() +/*inline*/ infix fun Long.sex(n: Int): Long { val sign = 1.toLong() shl n+1 return (this and sign.dec()) - (this and sign) } -inline infix fun ULong.bit(n: Int): Boolean = this shr n and 1UL != 0UL -inline fun ULong.bit(n: Int, v: Boolean): ULong = if (v) this bis n else this bic n -inline infix fun ULong.bis(n: Int): ULong = this.or(1UL shl n) -inline infix fun ULong.bic(n: Int): ULong = this.and(1UL.shl(n).inv()) -inline infix fun ULong.sex(n: Int): ULong { +/*inline*/ infix fun ULong.bit(n: Int): Boolean = this shr n and 1UL != 0UL +/*inline*/ fun ULong.bit(n: Int, v: Boolean): ULong = if (v) this bis n else this bic n +/*inline*/ infix fun ULong.bis(n: Int): ULong = this.or(1UL shl n) +/*inline*/ infix fun ULong.bic(n: Int): ULong = this.and(1UL.shl(n).inv()) +/*inline*/ infix fun ULong.sex(n: Int): ULong { val sign = 1UL shl n+1 return ((this and (sign.dec())) - (this and sign)) } diff --git a/src/main/kotlin/com/thequux/mcpdp/ext/bit/formatting.kt b/src/main/kotlin/com/thequux/mcpdp/ext/bit/formatting.kt index 6db9c09..19b46b9 100644 --- a/src/main/kotlin/com/thequux/mcpdp/ext/bit/formatting.kt +++ b/src/main/kotlin/com/thequux/mcpdp/ext/bit/formatting.kt @@ -1,5 +1,5 @@ package com.thequux.mcpdp.ext.bit -fun UShort.toOctal(): String = this.toString(8).padStart(6,'0') -fun UByte.toOctal(): String = this.toString(8).padStart(3, '0') -fun UInt.toOctal(): String = this.toString(8).padStart(11, '0') +fun UShort.toOctal(len: Int = 6): String = this.toString(8).padStart(len,'0') +fun UByte.toOctal(len: Int = 3): String = this.toString(8).padStart(len, '0') +fun UInt.toOctal(len: Int = 11): String = this.toString(8).padStart(len, '0')