3 * Emulates a 8086 processor.
5 * @author Luiz Fernando N. Capitulino
7 public class Processor
{
11 private RAMMemory ram
;
14 * Processor status (general purpose registers and flags)
16 private ProcStatus registers
;
21 public boolean halted
;
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
);
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
)
48 value
|= ((high
& 0xFF) << 8);
53 * Processor control instructions
59 private void inst_hlt()
67 private void inst_clc()
69 registers
.unsetFlags(RegFlags
.FLAG_C
);
75 private void inst_cmc()
77 if (registers
.getFlag(RegFlags
.FLAG_C
))
78 registers
.unsetFlags(RegFlags
.FLAG_C
);
80 registers
.setFlags(RegFlags
.FLAG_C
);
86 private void inst_stc()
88 registers
.setFlags(RegFlags
.FLAG_C
);
94 private void inst_cld()
96 registers
.unsetFlags(RegFlags
.FLAG_D
);
102 private void inst_std()
104 registers
.setFlags(RegFlags
.FLAG_D
);
110 private void inst_cli()
112 registers
.unsetFlags(RegFlags
.FLAG_I
);
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
)
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
185 registers
.setRegister_X(xreg
, xvalue
);
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
))
207 int low
= read_current_ip();
209 boolean word
= false;
212 if ((instruction
& word_mask
) == word_mask
) {
215 high
= read_current_ip();
216 xvalue
= make_16bit(low
, high
);
219 short reg_mask
= 0x7;
220 short reg
= (short) (instruction
& reg_mask
);
223 mov_to_register(xvalue
, high
, low
, Reg8086
.REG_AX
,
224 Reg8086
.REG_AH
, Reg8086
.REG_AL
, word
);
227 mov_to_register(xvalue
, high
, low
, Reg8086
.REG_CX
,
228 Reg8086
.REG_CH
, Reg8086
.REG_CL
, word
);
231 mov_to_register(xvalue
, high
, low
, Reg8086
.REG_DX
,
232 Reg8086
.REG_DH
, Reg8086
.REG_DL
, word
);
235 mov_to_register(xvalue
, high
, low
, Reg8086
.REG_BX
,
236 Reg8086
.REG_BH
, Reg8086
.REG_BL
, word
);
238 case 0x4: // SP if word, AH otherwise
240 registers
.setRegister(Reg8086
.REG_SP
, xvalue
);
242 registers
.setRegister(Reg8086
.REG_AH
, low
);
243 registers
.setRegister(Reg8086
.REG_AX
, low
);
246 case 0x5: // BP if word, CH otherwise
248 registers
.setRegister(Reg8086
.REG_BP
, xvalue
);
250 registers
.setRegister(Reg8086
.REG_CH
, low
);
251 registers
.setRegister(Reg8086
.REG_CX
, low
);
254 case 0x6: // SI if word, DH otherwise
256 registers
.setRegister(Reg8086
.REG_SI
, xvalue
);
258 registers
.setRegister(Reg8086
.REG_DH
, low
);
259 registers
.setRegister(Reg8086
.REG_DX
, low
);
262 case 0x7: // DI if word, BH otherwise
264 registers
.setRegister(Reg8086
.REG_DI
, xvalue
);
266 registers
.setRegister(Reg8086
.REG_BH
, low
);
267 registers
.setRegister(Reg8086
.REG_BX
, low
);
271 throw new InvalidRegisterException((int) reg
);
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
))
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
));
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
))
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
);
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
))
358 /* Get direction bit */
359 short direction_mask
= 0x2;
360 boolean to_register
= false;
361 if ((instruction
& direction_mask
) == direction_mask
)
365 short word_mask
= 0x1;
366 boolean word
= false;
367 if ((instruction
& word_mask
) == word_mask
)
370 /* read second byte instruction */
371 int sec_byte
= read_current_ip();
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
);
384 int rm
= sec_byte
& rm_mask
;
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
);
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
);
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
);
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
))
433 if (mov_accumulator_to_memory(instruction
))
436 if (mov_memory_to_accumulator(instruction
))
439 if (mov_mr_to_from_register(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
))
460 int reg_code
= instruction
& 0x7;
461 Reg8086 reg
= Reg8086
.convert_reg(reg_code
, true);
464 int value
= registers
.getRegister(reg
);
465 registers
.setRegister_X(reg
, ++value
);
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
))
485 int reg_code
= instruction
& 0x7;
486 Reg8086 reg
= Reg8086
.convert_reg(reg_code
, true);
489 int value
= registers
.getRegister(reg
);
490 registers
.setRegister_X(reg
, --value
);
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
))
512 boolean to_register
= false;
514 if ((instruction
& reg_mask
) == reg_mask
)
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
528 if ((sec_byte_inst
& mod_mask
) != mod_mask
)
529 throw new UnkInstructionException("add type");
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
);
538 int reg_rm
= sec_byte_inst
& rm_mask
;
539 Reg8086 reg_target
= Reg8086
.convert_reg(reg_rm
, word
);
542 int result
= registers
.getRegister(reg
) +
543 registers
.getRegister(reg_target
);
544 registers
.setRegister_X(reg_target
, result
);
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
))
566 boolean to_register
= false;
568 if ((instruction
& reg_mask
) == reg_mask
)
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
582 if ((sec_byte_inst
& mod_mask
) != mod_mask
)
583 throw new UnkInstructionException("add type");
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
);
592 int reg_rm
= sec_byte_inst
& rm_mask
;
593 Reg8086 reg_target
= Reg8086
.convert_reg(reg_rm
, word
);
596 int result
= registers
.getRegister(reg
) -
597 registers
.getRegister(reg_target
);
598 registers
.setRegister_X(reg_target
, result
);
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
))
617 if (dec_reg(instruction
))
620 if (add(instruction
))
623 if (sub(instruction
))
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
))
641 if (processor_control(instruction
))
644 if (arithmetic(instruction
))
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()
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()
703 * Set the Processor RAM memory object
705 * @param new_ram new ram memory object
707 public void setRAM(RAMMemory new_ram
)
713 * Get Processor status.
715 * @return processor status
717 public ProcStatus
getStatus()
723 * Set Processor status
725 * @param new_status new processor status
727 public void setStatus(ProcStatus new_status
)
729 registers
= new_status
;
737 ram
= new RAMMemory(100);
738 registers
= new ProcStatus();