git-ified DLX simulator
Getty Ritter
11 years ago
| 1 | from sys import stdin, argv | |
| 2 | ||
| 3 | result = [] | |
| 4 | ARITH = { | |
| 5 | 'add': 0x20, | |
| 6 | 'and': 0x24, | |
| 7 | 'or': 0x25, | |
| 8 | 'seq': 0x28, | |
| 9 | 'sle': 0x2c, | |
| 10 | 'sll': 0x04, | |
| 11 | 'slt': 0x2a, | |
| 12 | 'sne': 0x29, | |
| 13 | 'sra': 0x07, | |
| 14 | 'srl': 0x06, | |
| 15 | 'sub': 0x22, | |
| 16 | 'xor': 0x26, | |
| 17 | 'nul': 0x00 | |
| 18 | } | |
| 19 | ||
| 20 | OP = { | |
| 21 | 'addi': 0x08, | |
| 22 | 'andi': 0x0c, | |
| 23 | 'beqz': 0x04, | |
| 24 | 'bnez': 0x05, | |
| 25 | 'j': 0x02, | |
| 26 | 'jal': 0x03, | |
| 27 | 'jalr': 0x13, | |
| 28 | 'jr': 0x12, | |
| 29 | 'lhi': 0x0f, | |
| 30 | 'lw': 0x23, | |
| 31 | 'ori': 0x0d, | |
| 32 | 'seqi': 0x18, | |
| 33 | 'slei': 0x1c, | |
| 34 | 'slli': 0x14, | |
| 35 | 'slti': 0x1a, | |
| 36 | 'snei': 0x19, | |
| 37 | 'srai': 0x17, | |
| 38 | 'srli': 0x16, | |
| 39 | 'subi': 0x0a, | |
| 40 | 'sw': 0x2b, | |
| 41 | 'xori': 0x0e, | |
| 42 | 'halt': 0x30, | |
| 43 | 'pln': 0x31 | |
| 44 | } | |
| 45 | ||
| 46 | BYTE = 0xff | |
| 47 | ||
| 48 | for line in stdin.readlines(): | |
| 49 | op, *rest = line.split() | |
| 50 | if op in ARITH: | |
| 51 | opcode = 0 | |
| 52 | funct = ARITH[op] | |
| 53 | type = 'R' | |
| 54 | else: | |
| 55 | opcode = OP[op] | |
| 56 | if op in ('j', 'jal'): | |
| 57 | type = 'J' | |
| 58 | else: | |
| 59 | type = 'I' | |
| 60 | ||
| 61 | if type == 'R': | |
| 62 | rs, rt, rd = map(int, rest) | |
| 63 | result.append(opcode << 26 | | |
| 64 | rs << 21 | | |
| 65 | rt << 16 | | |
| 66 | rd << 11 | | |
| 67 | funct) | |
| 68 | elif type == 'I': | |
| 69 | rs, rt, immed = map(int, rest) | |
| 70 | result.append(opcode << 26 | | |
| 71 | rs << 21 | | |
| 72 | rt << 16 | | |
| 73 | (immed & 0xffff)) | |
| 74 | else: | |
| 75 | addr = int(rest[0]) | |
| 76 | result.append(opcode << 26 | | |
| 77 | (addr & 0x3ffffff)) | |
| 78 | ||
| 79 | with open(argv[1], 'wb') as f: | |
| 80 | for instr in result: | |
| 81 | f.write(bytes([instr >> 0x18, | |
| 82 | (instr >> 0x10) & BYTE, | |
| 83 | (instr >> 0x08) & BYTE, | |
| 84 | instr & BYTE])) |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
| 1 | ori 1 1 5 | |
| 2 | sw 0 1 0 | |
| 3 | subi 1 1 1 | |
| 4 | pln 1 0 0 | |
| 5 | bnez 1 0 -3 | |
| 6 | lw 1 2 0 | |
| 7 | pln 2 0 0 | |
| 8 | bnez 1 0 -2 | |
| 9 | halt 0 0 0 |
Binary diff not shown
| 1 | ori 1 1 5 | |
| 2 | ori 2 2 1 | |
| 3 | sw 0 1 0 | |
| 4 | subi 1 1 1 | |
| 5 | pln 1 0 0 | |
| 6 | bnez 1 0 -4 | |
| 7 | ori 0 2 5 | |
| 8 | lw 1 2 0 | |
| 9 | subi 2 2 1 | |
| 10 | pln 2 0 0 | |
| 11 | bnez 1 0 -3 | |
| 12 | pln 2 0 0 | |
| 13 | halt 0 0 0 |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
| 1 | import instr; | |
| 2 | ||
| 3 | import std.array; | |
| 4 | import std.stdio; | |
| 5 | import std.string; | |
| 6 | ||
| 7 | class CPU { | |
| 8 | public: | |
| 9 | uint[32] registers; | |
| 10 | uint[4096] memory; | |
| 11 | uint[] program; | |
| 12 | uint pc, cycles; | |
| 13 | bool running; | |
| 14 | ||
| 15 | this(uint[] p) { | |
| 16 | pc = 0; | |
| 17 | registers[] = 0; | |
| 18 | memory[] = 0; | |
| 19 | running = true; | |
| 20 | ||
| 21 | program = p; | |
| 22 | } | |
| 23 | ||
| 24 | void tick() { | |
| 25 | if (pc < program.length) { | |
| 26 | Instr i = Instr.decode(program[pc]); | |
| 27 | i.compute(this); | |
| 28 | } else { | |
| 29 | running = false; | |
| 30 | } | |
| 31 | pc++; | |
| 32 | } | |
| 33 | ||
| 34 | void run() { | |
| 35 | while (running) { | |
| 36 | tick(); | |
| 37 | } | |
| 38 | } | |
| 39 | ||
| 40 | /* Stop the program. */ | |
| 41 | void halt() { | |
| 42 | writefln("HALTING PROGRAM."); | |
| 43 | running = false; | |
| 44 | } | |
| 45 | ||
| 46 | /* Print all the registers at this state. */ | |
| 47 | void print_registers() { | |
| 48 | writef(" registers(["); | |
| 49 | for (uint i = 0; i < 32; i++) { | |
| 50 | if (i != 0) | |
| 51 | writef(", "); | |
| 52 | ||
| 53 | writef("%x", registers[i]); | |
| 54 | } | |
| 55 | writefln("])"); | |
| 56 | } | |
| 57 | ||
| 58 | /* Print the value of a register. */ | |
| 59 | void print_line(uint x) { | |
| 60 | printf("PROGRAM OUTPUT: %d\n", x); | |
| 61 | } | |
| 62 | } | |
| 63 | ||
| 64 | void main(string[] args) { | |
| 65 | if (args.length < 2) { | |
| 66 | writefln("Usage: %s [file]", args[0]); | |
| 67 | return; | |
| 68 | } | |
| 69 | writefln("Opening %s", args[1]); | |
| 70 | auto f = File(args[1], "r"); | |
| 71 | uint psize = cast(uint)f.size >> 2; | |
| 72 | uint[] program; | |
| 73 | program.length = psize; | |
| 74 | uint i = 0; | |
| 75 | ||
| 76 | foreach(ubyte[] buffer; f.byChunk(4)) { | |
| 77 | uint word = buffer[3] | | |
| 78 | buffer[2] << 0x08 | | |
| 79 | buffer[1] << 0x10 | | |
| 80 | buffer[0] << 0x18; | |
| 81 | program[i] = word; | |
| 82 | i ++; | |
| 83 | } | |
| 84 | CPU cpu = new CPU(program); | |
| 85 | writefln("Executing program..."); | |
| 86 | (new CPU(program)).run(); | |
| 87 | writefln(""); | |
| 88 | } |
| 1 | module instr; | |
| 2 | ||
| 3 | /* Reference on the DLX instruction set---mostly a subset | |
| 4 | of MIPS with slightly different semantics---can be | |
| 5 | found at | |
| 6 | http://www.csee.umbc.edu/courses/undergraduate/411/spring96/dlx.html | |
| 7 | */ | |
| 8 | ||
| 9 | import std.string; | |
| 10 | import std.stdio; | |
| 11 | import cpu; | |
| 12 | ||
| 13 | /* the full list of DLX opcodes; the only one missing is | |
| 14 | the arithmetic opcode, which is always 0x00. */ | |
| 15 | enum Opcode { | |
| 16 | addi = 0x08, | |
| 17 | andi = 0x0c, | |
| 18 | beqz = 0x04, | |
| 19 | bnez = 0x05, | |
| 20 | j = 0x02, | |
| 21 | jal = 0x03, | |
| 22 | jalr = 0x13, | |
| 23 | jr = 0x12, | |
| 24 | lhi = 0x0f, | |
| 25 | lw = 0x23, | |
| 26 | ori = 0x0d, | |
| 27 | seqi = 0x18, | |
| 28 | slei = 0x1c, | |
| 29 | slli = 0x14, | |
| 30 | slti = 0x1a, | |
| 31 | snei = 0x19, | |
| 32 | srai = 0x17, | |
| 33 | srli = 0x16, | |
| 34 | subi = 0x0a, | |
| 35 | sw = 0x2b, | |
| 36 | xori = 0x0e, | |
| 37 | halt = 0x30, /* These two are not part of the instruction */ | |
| 38 | pln = 0x31, /* set but were added to make debugging easier. */ | |
| 39 | } | |
| 40 | ||
| 41 | /* These are the arithmetic 'opcodes', which are found to | |
| 42 | the far right in an arithmetic instruction. */ | |
| 43 | enum Func { | |
| 44 | add = 0x20, | |
| 45 | and = 0x24, | |
| 46 | or = 0x25, | |
| 47 | seq = 0x28, | |
| 48 | sle = 0x2c, | |
| 49 | sll = 0x04, | |
| 50 | slt = 0x2a, | |
| 51 | sne = 0x29, | |
| 52 | sra = 0x07, | |
| 53 | srl = 0x06, | |
| 54 | sub = 0x22, | |
| 55 | xor = 0x26, | |
| 56 | nul = 0x00, | |
| 57 | } | |
| 58 | ||
| 59 | /* The masks to extract the individual portions of | |
| 60 | the bytecode instructions. */ | |
| 61 | enum Masks { | |
| 62 | opcode = 0b11111100000000000000000000000000, | |
| 63 | rs = 0b00000011111000000000000000000000, | |
| 64 | rt = 0b00000000000111110000000000000000, | |
| 65 | rd = 0b00000000000000001111100000000000, | |
| 66 | shamt = 0b00000000000000000000011111000000, | |
| 67 | funct = 0b00000000000000000000000000111111, | |
| 68 | immed = 0b00000000000000001111111111111111, | |
| 69 | addr = 0b00000011111111111111111111111111, | |
| 70 | } | |
| 71 | ||
| 72 | /* A set of related functions to extract and shift | |
| 73 | * the relevant pieces of an instruction. */ | |
| 74 | ubyte extract_opcode(uint instr) { | |
| 75 | return (Masks.opcode & instr) >> 26; | |
| 76 | } | |
| 77 | ||
| 78 | ubyte extract_rs(uint instr) { | |
| 79 | return (Masks.rs & instr) >> 21; | |
| 80 | } | |
| 81 | ||
| 82 | ubyte extract_rt(uint instr) { | |
| 83 | return (Masks.rt & instr) >> 16; | |
| 84 | } | |
| 85 | ||
| 86 | ubyte extract_rd(uint instr) { | |
| 87 | return (Masks.rd & instr) >> 11; | |
| 88 | } | |
| 89 | ||
| 90 | ubyte extract_shamt(uint instr) { | |
| 91 | return (Masks.shamt & instr) >> 6; | |
| 92 | } | |
| 93 | ||
| 94 | ubyte extract_funct(uint instr) { | |
| 95 | return (Masks.funct & instr); | |
| 96 | } | |
| 97 | ||
| 98 | ushort extract_immed(uint instr) { | |
| 99 | return (Masks.immed & instr); | |
| 100 | } | |
| 101 | ||
| 102 | uint extract_addr(uint instr) { | |
| 103 | return (Masks.addr & instr); | |
| 104 | } | |
| 105 | ||
| 106 | /* Ensure the sign still exists.*/ | |
| 107 | short extend(ushort x) { | |
| 108 | return cast(short)x; | |
| 109 | } | |
| 110 | ||
| 111 | int addr_extend(uint address) { | |
| 112 | uint na = address & 0x1fffffff; | |
| 113 | na |= (address & 0x20000000) << 6; | |
| 114 | return cast(int)na; | |
| 115 | } | |
| 116 | ||
| 117 | ||
| 118 | /* The structs representing the parts of instructions; | |
| 119 | one for each of the three types. */ | |
| 120 | struct R_Type { | |
| 121 | ubyte opcode, rs, rt, rd, shamt, funct; | |
| 122 | }; | |
| 123 | ||
| 124 | struct I_Type { | |
| 125 | ubyte opcode, rs, rt; | |
| 126 | ushort immediate; | |
| 127 | }; | |
| 128 | ||
| 129 | struct J_Type { | |
| 130 | ubyte opcode; | |
| 131 | uint address; | |
| 132 | }; | |
| 133 | ||
| 134 | /* An Instr consists of a single flag to tell the type of the | |
| 135 | * instruction, and a payload that is a union whose initialized | |
| 136 | * member corresponds to the type. Each member of the payload | |
| 137 | * is a struct mirroring the constituents of that MIPS | |
| 138 | * instruction type. */ | |
| 139 | struct Instr { | |
| 140 | static r_string = "R(op:0x%x, rs:0x%x, rt:0x%x, rd:0x%x, shamt:0x%x, funct:0x%x)"; | |
| 141 | static i_string = "I(op:0x%x, rs:0x%x, rt:0x%x, immediate:0x%x)"; | |
| 142 | static j_string = "J(op:0x%x, addr:0x%x)"; | |
| 143 | ||
| 144 | enum Type : short { R, I, J }; | |
| 145 | Type type; | |
| 146 | ||
| 147 | union { | |
| 148 | R_Type r_instr; | |
| 149 | I_Type i_instr; | |
| 150 | J_Type j_instr; | |
| 151 | }; | |
| 152 | ||
| 153 | /* Unless specified otherwise, an instruction takes 2 cycles to | |
| 154 | complete. */ | |
| 155 | uint time = 2u; | |
| 156 | ||
| 157 | static Instr decode(uint raw_instruction) { | |
| 158 | ubyte op = extract_opcode(raw_instruction); | |
| 159 | Instr result; | |
| 160 | ||
| 161 | writefln("opcode: %02x\n", op); | |
| 162 | if (op == 0x00) { | |
| 163 | result.type = Type.R; | |
| 164 | with (result.r_instr) { | |
| 165 | opcode = op; | |
| 166 | rs = extract_rs(raw_instruction); | |
| 167 | rt = extract_rt(raw_instruction); | |
| 168 | rd = extract_rd(raw_instruction); | |
| 169 | shamt = extract_shamt(raw_instruction); | |
| 170 | funct = extract_funct(raw_instruction); | |
| 171 | } | |
| 172 | ||
| 173 | } else if (op == Opcode.j || op == Opcode.jal) { | |
| 174 | result.type = Type.J; | |
| 175 | with (result.j_instr) { | |
| 176 | opcode = op; | |
| 177 | address = extract_addr(raw_instruction); | |
| 178 | } | |
| 179 | ||
| 180 | } else { | |
| 181 | result.type = Type.I; | |
| 182 | with (result.i_instr) { | |
| 183 | opcode = op; | |
| 184 | rs = extract_rs(raw_instruction); | |
| 185 | rt = extract_rt(raw_instruction); | |
| 186 | immediate = extract_immed(raw_instruction); | |
| 187 | } | |
| 188 | } | |
| 189 | ||
| 190 | return result; | |
| 191 | } | |
| 192 | ||
| 193 | /* Perform the action indicated by this instruction. Has | |
| 194 | access to the CPU (which also contains memory to be | |
| 195 | used. */ | |
| 196 | uint compute(CPU cpu) { | |
| 197 | writefln("Executing %s\n", this.toString()); | |
| 198 | switch (type) { | |
| 199 | case Type.R: | |
| 200 | with(r_instr) { | |
| 201 | uint vs = cpu.registers[rs]; | |
| 202 | uint vt = cpu.registers[rt]; | |
| 203 | switch (funct) { | |
| 204 | case Func.add: | |
| 205 | cpu.registers[rd] = vs + vt; | |
| 206 | break; | |
| 207 | case Func.and: | |
| 208 | cpu.registers[rd] = vs & vt; | |
| 209 | break; | |
| 210 | case Func.or: | |
| 211 | cpu.registers[rd] = vs | vt; | |
| 212 | break; | |
| 213 | case Func.seq: | |
| 214 | cpu.registers[rd] = (vs == vt ? 1u : 0u); | |
| 215 | break; | |
| 216 | case Func.sle: | |
| 217 | cpu.registers[rd] = (vs <= vt ? 1u : 0u); | |
| 218 | break; | |
| 219 | case Func.sll: | |
| 220 | cpu.registers[rd] = vs << (vt % 8); | |
| 221 | break; | |
| 222 | case Func.slt: | |
| 223 | cpu.registers[rd] = (vs < vt ? 1u : 0u); | |
| 224 | break; | |
| 225 | case Func.sne: | |
| 226 | cpu.registers[rd] = (vs != vt ? 1u : 0u); | |
| 227 | break; | |
| 228 | case Func.sra: | |
| 229 | cpu.registers[rd] = vs >> (vt % 8); | |
| 230 | break; | |
| 231 | case Func.srl: | |
| 232 | cpu.registers[rd] = vs >> (vt % 8); | |
| 233 | break; | |
| 234 | case Func.sub: | |
| 235 | cpu.registers[rd] = vs - vt; | |
| 236 | break; | |
| 237 | case Func.xor: | |
| 238 | cpu.registers[rd] = rs ^ rt; | |
| 239 | break; | |
| 240 | default: | |
| 241 | throw new Exception("Unknown funct: %d".format(funct)); | |
| 242 | } | |
| 243 | return cpu.registers[rd]; | |
| 244 | } | |
| 245 | break; | |
| 246 | ||
| 247 | case Type.J: | |
| 248 | switch (j_instr.opcode) { | |
| 249 | case Opcode.j: | |
| 250 | cpu.pc += addr_extend(j_instr.address) - 1; | |
| 251 | break; | |
| 252 | case Opcode.jal: | |
| 253 | cpu.registers[31] = cpu.pc + 4; | |
| 254 | cpu.pc += addr_extend(j_instr.address) - 1; | |
| 255 | break; | |
| 256 | default: | |
| 257 | throw new Exception("Unknown opcode for J-type: %d".format(j_instr.opcode)); | |
| 258 | } | |
| 259 | break; | |
| 260 | ||
| 261 | case Type.I: | |
| 262 | with (i_instr) { | |
| 263 | uint vs = cpu.registers[rs]; | |
| 264 | uint eimm = extend(immediate); | |
| 265 | switch (opcode) { | |
| 266 | case Opcode.addi: | |
| 267 | cpu.registers[rt] = vs + eimm; | |
| 268 | break; | |
| 269 | case Opcode.andi: | |
| 270 | cpu.registers[rt] = vs & eimm; | |
| 271 | break; | |
| 272 | case Opcode.beqz: | |
| 273 | cpu.pc += (vs == 0 ? eimm : 0); | |
| 274 | break; | |
| 275 | case Opcode.bnez: | |
| 276 | cpu.pc += (vs != 0 ? eimm : 0); | |
| 277 | break; | |
| 278 | case Opcode.jalr: | |
| 279 | cpu.registers[31] = cpu.pc + 4; | |
| 280 | cpu.pc = vs - 1; | |
| 281 | break; | |
| 282 | case Opcode.jr: | |
| 283 | cpu.pc = vs - 1; | |
| 284 | break; | |
| 285 | case Opcode.lhi: | |
| 286 | cpu.registers[rt] = immediate << 16; | |
| 287 | break; | |
| 288 | case Opcode.lw: | |
| 289 | cpu.registers[rt] = cpu.memory[vs + eimm]; | |
| 290 | break; | |
| 291 | case Opcode.ori: | |
| 292 | cpu.registers[rt] = vs | eimm; | |
| 293 | break; | |
| 294 | case Opcode.seqi: | |
| 295 | cpu.registers[rt] = (vs == eimm ? 1 : 0); | |
| 296 | break; | |
| 297 | case Opcode.slei: | |
| 298 | cpu.registers[rt] = (vs <= eimm ? 1 : 0); | |
| 299 | break; | |
| 300 | case Opcode.slli: | |
| 301 | cpu.registers[rt] = vs << (immediate % 8); | |
| 302 | break; | |
| 303 | case Opcode.slti: | |
| 304 | cpu.registers[rt] = (vs < eimm ? 1 : 0); | |
| 305 | break; | |
| 306 | case Opcode.snei: | |
| 307 | cpu.registers[rt] = (vs != eimm ? 1 : 0); | |
| 308 | break; | |
| 309 | case Opcode.srai: | |
| 310 | cpu.registers[rt] = vs >> (immediate % 8); | |
| 311 | break; | |
| 312 | case Opcode.srli: | |
| 313 | cpu.registers[rt] = vs >> (immediate % 8); | |
| 314 | break; | |
| 315 | case Opcode.subi: | |
| 316 | cpu.registers[rt] = vs - eimm; | |
| 317 | break; | |
| 318 | case Opcode.sw: | |
| 319 | cpu.memory[vs + eimm] = cpu.registers[rt]; | |
| 320 | break; | |
| 321 | case Opcode.xori: | |
| 322 | cpu.registers[rt] = vs ^ immediate; | |
| 323 | break; | |
| 324 | case Opcode.pln: | |
| 325 | cpu.print_line(vs); | |
| 326 | break; | |
| 327 | case Opcode.halt: | |
| 328 | cpu.halt(); | |
| 329 | break; | |
| 330 | default: | |
| 331 | throw new Exception("Unknown opcode in %s".format(this.toString())); | |
| 332 | } | |
| 333 | return cpu.registers[rt]; | |
| 334 | } | |
| 335 | break; | |
| 336 | default: | |
| 337 | throw new Exception("Unknown type: %d".format(type)); | |
| 338 | break; | |
| 339 | } | |
| 340 | return 0u; | |
| 341 | } | |
| 342 | ||
| 343 | string toString() { | |
| 344 | switch(type) { | |
| 345 | case Type.R: | |
| 346 | with(r_instr) { | |
| 347 | return r_string.format(opcode, rs, rt, rd, shamt, funct); | |
| 348 | } | |
| 349 | case Type.I: | |
| 350 | with (i_instr) { | |
| 351 | return i_string.format(opcode, rs, rt, immediate); | |
| 352 | } | |
| 353 | case Type.J: | |
| 354 | with (j_instr) { | |
| 355 | return j_string.format(opcode, address); | |
| 356 | } | |
| 357 | default: | |
| 358 | return "UNKNOWN"; | |
| 359 | } | |
| 360 | } | |
| 361 | } |