Added new trace infrastructure

This commit is contained in:
2023-09-26 20:56:04 +02:00
parent d994f7f1ea
commit b8b356b8b3
8 changed files with 275 additions and 112 deletions

View File

@@ -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<String>) {
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<String>) {
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)}")

View File

@@ -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<CPU.(Int) -> 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<UShortArray>(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())

View File

@@ -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()

View File

@@ -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))
}
}

View File

@@ -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<UInt, UShort>? = null,
val dst: Pair<UInt, UShort>? = null,
) {
@OptIn(ExperimentalUnsignedTypes::class)
companion object {
fun default(): TraceRecord = TraceRecord(0u, 0u, 0u, 0u, UShortArray(0))
}
private fun formatRef(ref: Pair<UInt, UShort>?): 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)
}
}

View File

@@ -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<UInt, UShort>? = null
private var dst: Pair<UInt, UShort>? = 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)
}
}

View File

@@ -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))
}

View File

@@ -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')