var/list/hex_digit_mask = list("0"=1,"1"=2,"2"=4,"3"=8,"4"=16,"5"=32,"6"=64,"7"=128,"8"=256,"9"=512,"A"=1024,"B"=2048,"C"=4096,"D"=8192,"E"=16384,"F"=32768) var/list/hex_digit_values = list("0" = 0, "1" = 1, "2" = 2, "3" = 3, "4" = 4, "5" = 5, "6" = 6, "7" = 7, "8" = 8, "9" = 9, "A" = 10, "B" = 11, "C" = 12, "D" = 13, "E" = 14, "F" = 15) /* * Toy implementation of MC14500b industrial control unit, a 1970s 1-bit CMOS processor used to replace ladder logic. It's a little baby PLC. * * Instructions: Word size in this setup is one byte, one nibble is the instruction and the other is the operand. In that order. A0 is opcode A operand 0. * 0 and F are NOPs * 1 (LD) loads an input value into the accumulator, RR * 2 (LDC) acts like 1, but with the complement of the input * 3 (AND) sets RR to the logical AND of RR and the input. * 4 (ANDC) sets RR to the logical AND of RR and the complement of the input * 5 (OR) sets RR to the logical OR of RR and the input * 6 (ORC) sets RR to the logical OR of RR and the complement of the input * 7 (XNOR) essentially equates RR and the input, with RR set to the result of the test. * 8 (STO) store RR in either a RAM addess (High 8 bits) or one of 8 outputs (Low 8 bits). * 9 (STOC) store complement of RR in the same was as STO. * A (IEN) sets IEN to the input * B (OEN) sets OEN to the input * C (JMP) will adjust the program counter by up to 32 addresses, forward or backward from the current instruction. Arguments 8+ will subtract 7 and then jump 4x that value forward, less than that will jump back 4x (that value + 1) * D (RTN) skip next instruction. For some reason. * E (SKZ) skip next instruction if RR is zero. */ #define INSTRUCTIONS_PER_PROCESS 32 #define MAX_ROM_SIZE 128 /obj/item/mechanics/mc14500 name = "Control Unit" icon = 'icons/obj/networked.dmi' icon_state = "genericsmall0" var/ROM = "" var/ioPins = 1 //Bitfield. Low byte is IO, high byte is internal memory flags, lowest bit is read as complement of RR var/RR = 0 //Result register. It's the accumulator. Look, motorola picked these names. var/IEN = 0 //Input ENable flag var/OEN = 0 //Output ENable flag var/running = 0 var/program_counter = 0 var/tmp/datum/mechanicsMessage/lastSignal = null //var/tmp/list/sourceCode = list() var/dbgmode = 0 New() ..() verbs -= /obj/item/mechanics/verb/setvalue mechanics.addInput("input 1", "fire1") mechanics.addInput("input 2", "fire2") mechanics.addInput("input 3", "fire3") mechanics.addInput("input 4", "fire4") mechanics.addInput("input 5", "fire5") mechanics.addInput("input 6", "fire6") mechanics.addInput("input 7", "fire7") //mechanics.addInput("input 8", "fire8") process() if (..() || !running || !level) return . = length(ROM) if (. < 2 || . % 2) //Too short or an odd length and we're out of here. running = 0 src.icon_state = "genericsmall0" return updateUsrDialog() SPAWN_DBG (0) for (var/i = INSTRUCTIONS_PER_PROCESS, i > 0, i--) if (!running || !level) break program_counter %= . switch (interpret_instruction(copytext(ROM, program_counter+1, program_counter+2), copytext(ROM, program_counter+2, program_counter+3))) if (-1) running = 0 src.icon_state = "genericsmall0" break if (1) i -= 5 program_counter += 2 sleep(1) attack_hand(mob/user as mob) if (!istype(src.loc, /turf/)) return if (!src.level) return ..() if (user.machine == src) user << output("[src.running]&[RR ? 1 : 0]&[IEN]&[OEN]", "mcu14500b.browser:update_indicators") user << output("[ioPins]", "mcu14500b.browser:update_mem_lights") return . = user_interface(user) if (.) user.Browse(., "window=mcu14500b") onclose(user, "mcu14500b") proc/user_interface(mob/user as mob) if (!user || user.stat || (iscarbon(user) && get_dist(user, src) > 1)) return user.machine = src . = {"Industrial Control Unit
[running ? " ACTIVE " : "INACTIVE"]

RRInput ENableOutput ENable


INPUT STATUS "} for (var/bit = 7, bit >= 0, bit--) . += "" . += "
[bit]
RAM STATUS" for (var/bit = 15, bit >= 8, bit--) . += "" . += {"
[bit-8]
"} //WIP verb/goofy_rom_debug() set src in view(1) set name = "\[Set ROM\]" set desc = "Configure the ROM by thinking really hard at the floating-gate transistors inside. Really, really hard." set category = "Local" if (!isliving(usr)) return if (usr.stat) return if (!mechanics.allowChange(usr)) boutput(usr, "[MECHFAILSTRING]") return . = adminscrub(strip_html(input(usr, "What should the ROM be set to? This better be hexadecimal and an even number of characters!!", "Terrible debug ROM panel", src.ROM) as text)) if (usr.stat || get_dist(usr, src) > 2) return if (!mechanics.allowChange(usr)) boutput(usr, "[MECHFAILSTRING]") return . = uppertext(copytext(ckey(.), 1, 1+MAX_ROM_SIZE)) if (length(.)%2 || !is_hex(.)) boutput(usr, "Invalid ROM values. Great job, knucklehead!!") ROM = . verb/goofy_power_debug() set src in view(1) set name = "\[Toggle Active\]" set desc = "Toggle whether this is on or not. Doing stuff." set category = "Local" if (!isliving(usr)) return if (usr.stat) return if (!mechanics.allowChange(usr)) boutput(usr, "[MECHFAILSTRING]") return src.running = !src.running && src.level IEN = 0 OEN = 0 RR = 0 program_counter = 0 src.ioPins = 1 //All zero except the !RR section. src.icon_state = "genericsmall[src.running ? 1 : 0]" desc = {"* Instructions: Word size in this setup is one byte, one nibble is the instruction and the other is the operand. In that order. A0 is opcode A operand 0.
* 0 and F are NOPs
* 1 (LD) loads an input value into the accumulator, RR. Input 0 is !RR.
* 2 (LDC) acts like 1, but with the complement of the input
* 3 (AND) sets RR to the logical AND of RR and the input.
* 4 (ANDC) sets RR to the logical AND of RR and the complement of the input
* 5 (OR) sets RR to the logical OR of RR and the input
* 6 (ORC) sets RR to the logical OR of RR and the complement of the input
* 7 (XNOR) essentially equates RR and the input, with RR set to the result of the test.
* 8 (STO) store RR in either a RAM addess (High 8 bits) or one of 8 outputs (Low 8 bits).
* 9 (STOC) store complement of RR in the same was as STO.
* A (IEN) sets IEN to the input. IEN is Input ENable.
* B (OEN) sets OEN to the input. OEN is Output ENable.
* C (JMP) will adjust the program counter by up to 32 addresses, forward or backward from the current instruction. Arguments 8+ will subtract 7 and then jump 4x that value forward, less than that will jump back 4x (that value + 1)
* D (RTN) skip next instruction. For some reason.
* E (SKZ) skip next instruction if RR is zero.
Output signals have the value \"PIN:VALUE\" i.e \"2:1\" to output true on pin 2. You can filter this with OR gate triggers, ok.
Example program: \"30A0B01181\" Will AND RR with 0 on the first iteration (As IEN is zero) and AND it with !RR on subsequent loops (Both set it to zero), load !RR (1) into IEN and OEN, then load input 1 and send it to output 1. This will repeat without end."} proc/interpret_instruction(instruction, argument) if (src.dbgmode) boutput(world, "\[[instruction], [argument]] RR=[RR] IEN=[IEN] OEN=[OEN]") . = argument argument = hex_digit_mask["[argument]"] if (!argument) return -1 switch (instruction) if ("0","F")//NOP return 0 if ("1") //LD, RR = (DATA & IEN) RR = (IEN && (ioPins & argument)) ioPins = (ioPins & 65534 & (~argument | 65280)) | !RR if ("2") //LDC, RR = !(DATA & IEN) RR = !(IEN && (ioPins & argument)) ioPins = (ioPins & 65534 & (~argument | 65280)) | !RR if ("3") //AND, RR = RR & (DATA & IEN) RR = RR && (IEN && (ioPins & argument)) ioPins = (ioPins & 65534 & (~argument | 65280)) | !RR if ("4") //ANDC, RR = RR & !(DATA & IEN) RR = RR && !(IEN && (ioPins & argument)) ioPins = (ioPins & 65534 & (~argument | 65280)) | !RR if ("5") //OR, RR = RR | (DATA & IEN) RR = RR || (IEN && (ioPins & argument)) ioPins = (ioPins & 65534 & (~argument | 65280)) | !RR if ("6") //ORC, RR = RR | !(DATA & IEN). Waugh. RR = RR || !(IEN && (ioPins & argument)) ioPins = (ioPins & 65534 & (~argument | 65280)) | !RR if ("7") //XNOR, RR = RR == (DATA & IEN) RR = (RR && 1) == ((IEN && (ioPins & argument)) && 1) //The &&1 is so we can compare them both as booleans instead of tripping over two bitfields with ones in different places ioPins = (ioPins & 65534 & (~argument | 65280)) | !RR if ("8") //STO, DATA = RR if (argument > 128) if (OEN) ioPins = RR ? (ioPins | argument) : (ioPins & ~argument) return 0 else if (OEN) if (lastSignal) lastSignal.signal = "[.]:[RR ? 1 : 0]" mechanics.fireOutgoing(lastSignal) lastSignal = null if (src.dbgmode) boutput(world, "OUTe: [.]:[RR ? 1 : 0]") else mechanics.fireOutgoing(mechanics.newSignal("[.]:[RR ? 1 : 0]")) if (src.dbgmode) boutput(world, "OUT: [.]:[RR ? 1 : 0]") return 1 if ("9") //STOC, DATA = !RR if (argument > 128) if (OEN) ioPins = RR ? (ioPins & ~argument) : (ioPins | argument) return 0 else if (OEN) if (lastSignal) lastSignal.signal = "[.]:[RR ? 0 : 1]" mechanics.fireOutgoing(lastSignal) lastSignal = null if (src.dbgmode) boutput(world, "OUTe: [.]:[RR ? 0 : 1]") else mechanics.fireOutgoing(mechanics.newSignal("[.]:[RR ? 0 : 1]")) if (src.dbgmode) boutput(world, "OUT: [.]:[RR ? 0 : 1]") return 1 if ("A") //IEN, IEN = DATA IEN = (ioPins & argument) if ("B") //OEN, OEN = DATA OEN = (ioPins & argument) if ("C") //JMP . = hex_digit_values[.] if (!isnum(.)) return -1 if (. > 7) program_counter += (4 * (. - 7)) - 2 else program_counter -= (4 * (. + 1)) + 2 . = length(ROM) if (program_counter < 0) program_counter = . + (program_counter % .) return 0 if ("D") //RTN, skip next instruction program_counter += 2 if ("E") //SKZ, skip next instruction if !RR if (!RR) program_counter += 2 else return -1 return 0 proc fire1(var/datum/mechanicsMessage/anInput) if(level == 2) return if (anInput && anInput.isTrue()) ioPins |= 2 else ioPins &= ~2 lastSignal = anInput fire2(var/datum/mechanicsMessage/anInput) if(level == 2) return if (anInput && anInput.isTrue()) ioPins |= 4 else ioPins &= ~4 lastSignal = anInput fire3(var/datum/mechanicsMessage/anInput) if(level == 2) return if (anInput && anInput.isTrue()) ioPins |= 8 else ioPins &= ~8 lastSignal = anInput fire4(var/datum/mechanicsMessage/anInput) if(level == 2) return if (anInput && anInput.isTrue()) ioPins |= 16 else ioPins &= ~16 lastSignal = anInput fire5(var/datum/mechanicsMessage/anInput) if(level == 2) return if (anInput && anInput.isTrue()) ioPins |= 32 else ioPins &= ~32 lastSignal = anInput fire6(var/datum/mechanicsMessage/anInput) if(level == 2) return if (anInput && anInput.isTrue()) ioPins |= 64 else ioPins &= ~64 lastSignal = anInput fire7(var/datum/mechanicsMessage/anInput) if(level == 2) return if (anInput && anInput.isTrue()) ioPins |= 128 else ioPins &= ~128 lastSignal = anInput #undef INSTRUCTIONS_PER_PROCESS