git-ified DLX simulator
Getty Ritter
10 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 | } |