Introduce old redir program
[lcapit-junk-code.git] / pet-projects / emu8086 / src / Processor.java
blob238e5e1f3f5ba540379ffb32fdb1ea1c6a9863d7
2 /**
3 * Emulates a 8086 processor.
4 *
5 * @author Luiz Fernando N. Capitulino
6 */
7 public class Processor {
8 /**
9 * System RAM
11 private RAMMemory ram;
13 /**
14 * Processor status (general purpose registers and flags)
16 private ProcStatus registers;
18 /**
19 * Halted flag
21 public boolean halted;
23 /**
24 * Read RAM location pointed to by IP register and increment it.
26 * @return RAM memory contents pointed to by IP
27 * @throws InvalidMemException
29 private int read_current_ip() throws InvalidMemException
31 int ip = registers.getRegister(Reg8086.REG_IP);
32 int value = readRAM(ip);
33 registers.setRegister(Reg8086.REG_IP, ++ip);
35 return value;
38 /**
39 * Make a 16-bit value from two 8-bit values.
41 * @param low 8-bit low
42 * @param high 8-bit high
43 * @return New 16-bit value
45 private int make_16bit(int low, int high)
47 int value = low;
48 value |= ((high & 0xFF) << 8);
49 return value;
53 * Processor control instructions
56 /**
57 * Halt
59 private void inst_hlt()
61 halted = true;
64 /**
65 * CLC
67 private void inst_clc()
69 registers.unsetFlags(RegFlags.FLAG_C);
72 /**
73 * CMC
75 private void inst_cmc()
77 if (registers.getFlag(RegFlags.FLAG_C))
78 registers.unsetFlags(RegFlags.FLAG_C);
79 else
80 registers.setFlags(RegFlags.FLAG_C);
83 /**
84 * STC
86 private void inst_stc()
88 registers.setFlags(RegFlags.FLAG_C);
91 /**
92 * CLD
94 private void inst_cld()
96 registers.unsetFlags(RegFlags.FLAG_D);
99 /**
100 * STD
102 private void inst_std()
104 registers.setFlags(RegFlags.FLAG_D);
108 * CLI
110 private void inst_cli()
112 registers.unsetFlags(RegFlags.FLAG_I);
116 * STI
118 private void inst_sti()
120 registers.setFlags(RegFlags.FLAG_I);
124 * Processor control instruction dispatcher.
126 * @param instruction current instruction
127 * @return true if the instructions was executed, false otherwise
129 private boolean processor_control(short instruction)
131 switch (instruction)
133 case Inst8086.CLC:
134 inst_clc();
135 break;
136 case Inst8086.CMC:
137 inst_cmc();
138 break;
139 case Inst8086.STC:
140 inst_stc();
141 break;
142 case Inst8086.CLD:
143 inst_cld();
144 break;
145 case Inst8086.STD:
146 inst_std();
147 break;
148 case Inst8086.CLI:
149 inst_cli();
150 break;
151 case Inst8086.STI:
152 inst_sti();
153 break;
154 case Inst8086.HLT:
155 inst_hlt();
156 break;
157 default:
158 return false;
161 return true;
165 * Data transfer instructions
169 * Move a immediate to the specified register
171 * @param xvalue 16-bit value
172 * @param hvalue 8-bit value high
173 * @param lvalue 8-bit value low
174 * @param xreg 16-bit register
175 * @param hreg 8-bit register high
176 * @param lreg 8-bit register low
177 * @param word true if it's a word operation
178 * @throws InvalidRegisterException
180 private void mov_to_register(int xvalue, int hvalue, int lvalue,
181 Reg8086 xreg, Reg8086 hreg, Reg8086 lreg,
182 boolean word) throws InvalidRegisterException
184 if (word)
185 registers.setRegister_X(xreg, xvalue);
186 else
187 registers.setRegister_L(lreg, lvalue);
191 * MOV: immediate to register.
193 * @param instruction current instruction
194 * @return true if the instructions was executed, false otherwise
195 * @throws InvalidMemException
196 * @throws InvalidRegisterException
198 private boolean mov_immediate_to_register(short instruction) throws InvalidMemException, InvalidRegisterException
200 short word_mask = 0x8;
201 short imm_reg_mask = 0xF0;
203 if (!((instruction & imm_reg_mask) == Inst8086.MOV_IR))
204 return false;
206 // low 8-bit data
207 int low = read_current_ip();
209 boolean word = false;
210 int high = 0;
211 int xvalue = 0;
212 if ((instruction & word_mask) == word_mask) {
213 // high 8-bit data
214 word = true;
215 high = read_current_ip();
216 xvalue = make_16bit(low, high);
219 short reg_mask = 0x7;
220 short reg = (short) (instruction & reg_mask);
221 switch (reg) {
222 case 0x0: // AX
223 mov_to_register(xvalue, high, low, Reg8086.REG_AX,
224 Reg8086.REG_AH, Reg8086.REG_AL, word);
225 break;
226 case 0x1: // CX
227 mov_to_register(xvalue, high, low, Reg8086.REG_CX,
228 Reg8086.REG_CH, Reg8086.REG_CL, word);
229 break;
230 case 0x2:// DX
231 mov_to_register(xvalue, high, low, Reg8086.REG_DX,
232 Reg8086.REG_DH, Reg8086.REG_DL, word);
233 break;
234 case 0x3: // BX
235 mov_to_register(xvalue, high, low, Reg8086.REG_BX,
236 Reg8086.REG_BH, Reg8086.REG_BL, word);
237 break;
238 case 0x4: // SP if word, AH otherwise
239 if (word) {
240 registers.setRegister(Reg8086.REG_SP, xvalue);
241 } else {
242 registers.setRegister(Reg8086.REG_AH, low);
243 registers.setRegister(Reg8086.REG_AX, low);
245 break;
246 case 0x5: // BP if word, CH otherwise
247 if (word) {
248 registers.setRegister(Reg8086.REG_BP, xvalue);
249 } else {
250 registers.setRegister(Reg8086.REG_CH, low);
251 registers.setRegister(Reg8086.REG_CX, low);
253 break;
254 case 0x6: // SI if word, DH otherwise
255 if (word) {
256 registers.setRegister(Reg8086.REG_SI, xvalue);
257 } else {
258 registers.setRegister(Reg8086.REG_DH, low);
259 registers.setRegister(Reg8086.REG_DX, low);
261 break;
262 case 0x7: // DI if word, BH otherwise
263 if (word) {
264 registers.setRegister(Reg8086.REG_DI, xvalue);
265 } else {
266 registers.setRegister(Reg8086.REG_BH, low);
267 registers.setRegister(Reg8086.REG_BX, low);
269 break;
270 default:
271 throw new InvalidRegisterException((int) reg);
274 return true;
278 * MOV: accumulator to memory.
280 * @param instruction current instruction
281 * @return true if the instructions was executed, false otherwise
282 * @throws InvalidMemException
284 private boolean mov_accumulator_to_memory(short instruction) throws InvalidMemException
286 short acct_mem_mask = 0xFE;
288 if (!((instruction & acct_mem_mask) == Inst8086.MOV_AC))
289 return false;
291 int addr_low = read_current_ip();
292 int addr_high = read_current_ip();
293 ram.write(addr_low, (short) registers.getRegister(Reg8086.REG_AL));
295 short word_mask = 0x1;
296 if ((instruction & word_mask) == 1) {
297 if (addr_high == 0) {
298 // FIXME: shouldn't GAS generate this address?
299 addr_high = addr_low + 1;
301 ram.write(addr_high, (short) registers.getRegister(Reg8086.REG_AH));
304 return true;
308 * MOV: memory to accumulator.
310 * @param instruction current instruction
311 * @return true if the instructions was executed, false otherwise
312 * @throws InvalidMemException
314 private boolean mov_memory_to_accumulator(short instruction) throws InvalidMemException
316 short mem_acct_mask = 0xFE;
318 if (!((instruction & mem_acct_mask) == Inst8086.MOV_CA))
319 return false;
321 int addr_low = read_current_ip();
322 short value_low = ram.read(addr_low);
323 registers.setRegister(Reg8086.REG_AL, value_low);
324 registers.setRegister(Reg8086.REG_AX, value_low);
326 int addr_high = read_current_ip();
327 short word_mask = 0x1;
328 if ((instruction & word_mask) == 1) {
329 if (addr_high == 0) {
330 // FIXME: shouldn't GAS generate this address?
331 addr_high = addr_low + 1;
333 short value_high = ram.read(addr_high);
334 registers.setRegister(Reg8086.REG_AH, value_high);
335 int value = make_16bit(value_low, value_high);
336 registers.setRegister(Reg8086.REG_AX, value);
339 return true;
343 * MOV: Memory/Register to/from Register
345 * @param instruction current instruction
346 * @return true if the instructions was executed, false otherwise
347 * @throws InvalidMemException
348 * @throws UnkInstructionException
349 * @throws InvalidRegisterException
351 private boolean mov_mr_to_from_register(short instruction) throws InvalidMemException, UnkInstructionException, InvalidRegisterException
353 short mr_r_mask = 0xFC;
355 if (!((instruction & mr_r_mask) == Inst8086.MOV_RM_R))
356 return false;
358 /* Get direction bit */
359 short direction_mask = 0x2;
360 boolean to_register = false;
361 if ((instruction & direction_mask) == direction_mask)
362 to_register = true;
364 /* Get word bit */
365 short word_mask = 0x1;
366 boolean word = false;
367 if ((instruction & word_mask) == word_mask)
368 word = true;
370 /* read second byte instruction */
371 int sec_byte = read_current_ip();
373 /* Get mod bits */
374 short mod_mask = 0xC0;
375 int mod = ((sec_byte & mod_mask) >> 6);
377 /* Get Register bits */
378 short reg_mask = 0x38;
379 int reg_value = ((sec_byte & reg_mask) >> 3);
380 Reg8086 reg = Reg8086.convert_reg(reg_value, word);
382 /* Get r/m bits */
383 int rm_mask = 0x7;
384 int rm = sec_byte & rm_mask;
386 switch (mod) {
387 case 0x3: // r/m is a register
388 if (to_register == false) {
389 int value = registers.getRegister(reg);
390 Reg8086 reg2 = Reg8086.convert_reg(rm, word);
391 if (reg2 == Reg8086.REG_AH ||
392 reg2 == Reg8086.REG_BH ||
393 reg2 == Reg8086.REG_CH ||
394 reg2 == Reg8086.REG_DH) {
395 registers.setRegister_H(reg2, value);
396 return true;
398 if (reg2 == Reg8086.REG_AL ||
399 reg2 == Reg8086.REG_BL ||
400 reg2 == Reg8086.REG_CL ||
401 reg2 == Reg8086.REG_DL) {
402 registers.setRegister_L(reg2, value);
403 return true;
405 if (reg2 == Reg8086.REG_AX ||
406 reg2 == Reg8086.REG_BX ||
407 reg2 == Reg8086.REG_CX ||
408 reg2 == Reg8086.REG_DX) {
409 registers.setRegister_X(reg2, value);
410 return true;
413 break;
416 throw new UnkInstructionException("type of mov");
420 * Data Transfer instruction dispatcher.
422 * @param instruction current instruction
423 * @return true if the instructions was executed, false otherwise
424 * @throws InvalidMemException
425 * @throws InvalidRegisterException
426 * @throws UnkInstructionException
428 private boolean data_transfer(short instruction) throws InvalidMemException, InvalidRegisterException, UnkInstructionException
430 if (mov_immediate_to_register(instruction))
431 return true;
433 if (mov_accumulator_to_memory(instruction))
434 return true;
436 if (mov_memory_to_accumulator(instruction))
437 return true;
439 if (mov_mr_to_from_register(instruction))
440 return true;
442 return false;
446 * INC instruction
448 * @param instruction
449 * @return true if the instructions was executed, false otherwise
450 * @throws InvalidRegisterException
452 private boolean inc_reg(short instruction) throws InvalidRegisterException
454 short inc_r_mask = 0xF8;
456 if (!((instruction & inc_r_mask) == Inst8086.INC_REG))
457 return false;
459 // Get register
460 int reg_code = instruction & 0x7;
461 Reg8086 reg = Reg8086.convert_reg(reg_code, true);
463 // increment
464 int value = registers.getRegister(reg);
465 registers.setRegister_X(reg, ++value);
467 return true;
471 * DEC instruction
473 * @param instruction
474 * @return true if the instructions was executed, false otherwise
475 * @throws InvalidRegisterException
477 private boolean dec_reg(short instruction) throws InvalidRegisterException
479 short dec_r_mask = 0xF8;
481 if (!((instruction & dec_r_mask) == Inst8086.DEC_REG))
482 return false;
484 // Get register
485 int reg_code = instruction & 0x7;
486 Reg8086 reg = Reg8086.convert_reg(reg_code, true);
488 // decrement
489 int value = registers.getRegister(reg);
490 registers.setRegister_X(reg, --value);
492 return true;
496 * ADD instruction
498 * @param instruction
499 * @return true if the instructions was executed, false otherwise
500 * @throws InvalidMemException
501 * @throws UnkInstructionException
502 * @throws InvalidRegisterException
504 private boolean add(short instruction) throws InvalidMemException, UnkInstructionException, InvalidRegisterException
506 short add_r_mask = 0xFC;
508 if (!((instruction & add_r_mask) == Inst8086.ADD_REG))
509 return false;
511 // Get direction
512 boolean to_register = false;
513 int reg_mask = 0x2;
514 if ((instruction & reg_mask) == reg_mask)
515 to_register = true;
517 // Get word
518 boolean word = true;
519 int word_mask = 0x1;
520 if ((instruction & word_mask) != word_mask)
521 throw new UnkInstructionException("add type");
523 /* read second byte instruction */
524 int sec_byte_inst = read_current_ip();
526 // XXX: Only accepts registers for now
527 int mod_mask = 0xC0;
528 if ((sec_byte_inst & mod_mask) != mod_mask)
529 throw new UnkInstructionException("add type");
531 // Get source
532 int reg_sec_mask = 0x38;
533 int reg_source = ((sec_byte_inst & reg_sec_mask) >> 3);
534 Reg8086 reg = Reg8086.convert_reg(reg_source, word);
536 // Get target
537 int rm_mask = 0x3;
538 int reg_rm = sec_byte_inst & rm_mask;
539 Reg8086 reg_target = Reg8086.convert_reg(reg_rm, word);
541 // Add them!
542 int result = registers.getRegister(reg) +
543 registers.getRegister(reg_target);
544 registers.setRegister_X(reg_target, result);
546 return true;
550 * SUB instruction
552 * @param instruction
553 * @return true if the instructions was executed, false otherwise
554 * @throws InvalidMemException
555 * @throws UnkInstructionException
556 * @throws InvalidRegisterException
558 private boolean sub(short instruction) throws InvalidMemException, UnkInstructionException, InvalidRegisterException
560 short sub_r_mask = 0xFC;
562 if (!((instruction & sub_r_mask) == Inst8086.SUB_REG))
563 return false;
565 // Get direction
566 boolean to_register = false;
567 int reg_mask = 0x2;
568 if ((instruction & reg_mask) == reg_mask)
569 to_register = true;
571 // Get word
572 boolean word = true;
573 int word_mask = 0x1;
574 if ((instruction & word_mask) != word_mask)
575 throw new UnkInstructionException("add type");
577 /* read second byte instruction */
578 int sec_byte_inst = read_current_ip();
580 // XXX: Only accepts registers for now
581 int mod_mask = 0xC0;
582 if ((sec_byte_inst & mod_mask) != mod_mask)
583 throw new UnkInstructionException("add type");
585 // Get source
586 int reg_sec_mask = 0x38;
587 int reg_source = ((sec_byte_inst & reg_sec_mask) >> 3);
588 Reg8086 reg = Reg8086.convert_reg(reg_source, word);
590 // Get target
591 int rm_mask = 0x3;
592 int reg_rm = sec_byte_inst & rm_mask;
593 Reg8086 reg_target = Reg8086.convert_reg(reg_rm, word);
595 // Add them!
596 int result = registers.getRegister(reg) -
597 registers.getRegister(reg_target);
598 registers.setRegister_X(reg_target, result);
600 return true;
604 * Arithmetic instruction dispatcher.
606 * @param instruction current instruction
607 * @return true if the instructions was executed, false otherwise
608 * @throws InvalidRegisterException
609 * @throws UnkInstructionException
610 * @throws InvalidMemException
612 private boolean arithmetic(short instruction) throws InvalidRegisterException, InvalidMemException, UnkInstructionException
614 if (inc_reg(instruction))
615 return true;
617 if (dec_reg(instruction))
618 return true;
620 if (add(instruction))
621 return true;
623 if (sub(instruction))
624 return true;
626 return false;
630 * Execute specified instruction.
632 * @param instruction instruction to be executed
633 * @throws UnkInstructionException, InvalidMemException
634 * @throws InvalidRegisterException
636 private void executeInst(short instruction) throws UnkInstructionException, UnkInstructionException, InvalidMemException, InvalidRegisterException
638 if (data_transfer(instruction))
639 return;
641 if (processor_control(instruction))
642 return;
644 if (arithmetic(instruction))
645 return;
647 throw new UnkInstructionException(instruction);
651 * Execute next instruction.
652 * @throws UnkInstructionException, InvalidMemException
653 * @throws InvalidRegisterException
655 public void executeNext() throws UnkInstructionException, UnkInstructionException, InvalidMemException, InvalidRegisterException
657 executeInst((short) read_current_ip());
661 * Write all memory contents to stdout.
663 public void dumpRAM()
665 ram.dump();
669 * Write one byte into the specified RAM address.
671 * @param addr RAM address to write to
672 * @param value new contents for the specified address
673 * @throws InvalidMemException
675 public void writeRAM(int addr, short value) throws InvalidMemException
677 ram.write(addr, value);
681 * Read one byte from the specified RAM address.
683 * @param addr RAM address to read from
684 * @return address contents
685 * @throws InvalidMemException
687 public short readRAM(int addr) throws InvalidMemException
689 return ram.read(addr);
693 * Get Processor RAM memory.
695 * @return processor RAM memory
697 public RAMMemory getRAM()
699 return ram;
703 * Set the Processor RAM memory object
705 * @param new_ram new ram memory object
707 public void setRAM(RAMMemory new_ram)
709 ram = new_ram;
713 * Get Processor status.
715 * @return processor status
717 public ProcStatus getStatus()
719 return registers;
723 * Set Processor status
725 * @param new_status new processor status
727 public void setStatus(ProcStatus new_status)
729 registers = new_status;
733 * Constructor.
735 public Processor()
737 ram = new RAMMemory(100);
738 registers = new ProcStatus();
739 halted = false;