Add CPU.handle_interrupts
This commit is contained in:
parent
c6106203eb
commit
b4c81fa3f9
33
src/cpu.ml
33
src/cpu.ml
|
@ -306,7 +306,7 @@ let run cpu (mem: Memory.map) =
|
|||
|
||||
| '\xFB' -> let inst = sprintf "EI" in
|
||||
(* enable interrupts *)
|
||||
Interrupt.ime := true;
|
||||
Interrupt.gIME := true;
|
||||
inst, 4
|
||||
|
||||
| x -> eprintf "opcode 0x%02X\n" (int_of_char x);
|
||||
|
@ -323,7 +323,7 @@ let run cpu (mem: Memory.map) =
|
|||
* Interrupt handlers
|
||||
*
|
||||
************************************************)
|
||||
let handle_interrupt cpu mem flag =
|
||||
let handle_interrupt cpu mmap flag =
|
||||
let int_vector = match flag with
|
||||
| Interrupt.V_Blank -> 0x40
|
||||
| Interrupt.LCD_Stat -> 0x48
|
||||
|
@ -331,25 +331,34 @@ let handle_interrupt cpu mem flag =
|
|||
| Interrupt.Serial -> 0x58
|
||||
| Interrupt.Joypad -> 0x60 in
|
||||
|
||||
(* TODO: reset IF flag *)
|
||||
Interrupt.ime := false;
|
||||
Interrupt.reset_IF_flag flag mmap;
|
||||
Interrupt.gIME := false;
|
||||
|
||||
push_pc_stack cpu mem;
|
||||
cpu.reg.pc <- int_vector
|
||||
push_pc_stack cpu mmap;
|
||||
cpu.reg.pc <- int_vector;
|
||||
|
||||
(* TODO: run cpu until RETI is called *)
|
||||
(* run cpu until RETI is called *)
|
||||
let last_inst = ref "" in
|
||||
while !last_inst <> "RETI" do
|
||||
let inst, cycles = run cpu mmap in
|
||||
last_inst := inst
|
||||
done;
|
||||
|
||||
Interrupt.gIME := true
|
||||
|
||||
|
||||
|
||||
let handle_interrupts cpu mem ie if_ =
|
||||
if not !Interrupt.ime then
|
||||
let handle_interrupts cpu mmap =
|
||||
if not !Interrupt.gIME then
|
||||
() (* Interrupt Master Enable flag set to false, nothing to do *)
|
||||
else (
|
||||
else begin
|
||||
(* N.B.: flags are precomputed once but an interrupt could be requested
|
||||
during that time *)
|
||||
let ie = Interrupt.get_IE mmap in
|
||||
let if_ = Interrupt.get_IF mmap in
|
||||
let interrupts = ie land if_ in
|
||||
let flags = Interrupt.get_flags interrupts in
|
||||
match flags with
|
||||
| [] -> () (* No interrupt *)
|
||||
| _ -> List.iter (fun x -> handle_interrupt cpu mem x) flags
|
||||
)
|
||||
| _ -> List.iter (fun x -> handle_interrupt cpu mmap x) flags
|
||||
end
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
* LICENSE file at the top level directory of this source tree.
|
||||
*)
|
||||
|
||||
let ime = ref false;;
|
||||
(** Interrupt Master Enable flag *)
|
||||
let gIME = ref false
|
||||
|
||||
let ie_addr = 0xFFFF
|
||||
let if_addr = 0xFFF0
|
||||
|
||||
type t =
|
||||
| V_Blank
|
||||
|
@ -15,6 +19,14 @@ type t =
|
|||
| Serial
|
||||
| Joypad
|
||||
|
||||
|
||||
let get_flag_mask = function
|
||||
| V_Blank -> 0b00000001
|
||||
| LCD_Stat -> 0b00000010
|
||||
| Timer -> 0b00000100
|
||||
| Serial -> 0b00001000
|
||||
| Joypad -> 0b00010000
|
||||
|
||||
(** Return the list of interrupt flags sorted by priority. *)
|
||||
let get_flags byte =
|
||||
|
||||
|
@ -38,3 +50,17 @@ let get_flags byte =
|
|||
| _ -> List.rev accu in
|
||||
|
||||
get_flag byte 0 []
|
||||
|
||||
|
||||
(** Interrupt Enable *)
|
||||
let get_IE mem = Memory.get mem ie_addr |> int_of_char
|
||||
|
||||
(** Interrupt Flag *)
|
||||
let get_IF mem = Memory.get mem if_addr |> int_of_char
|
||||
|
||||
let reset_IF_flag flag mem =
|
||||
let if_reg = get_IF mem in
|
||||
let flag_mask = get_flag_mask flag in
|
||||
let new_if_reg = if_reg land (lnot flag_mask) |> char_of_int in
|
||||
Memory.set mem if_addr new_if_reg
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ let rec run (cpu: Cpu.t) (mem: Memory.t) (screen: Screen.t) =
|
|||
let inst, cycles = Cpu.run cpu mem.map in
|
||||
printf "[Instruction] %s\n" inst;
|
||||
|
||||
Cpu.handle_interrupts cpu mem.map;
|
||||
|
||||
Memory.update_timers mem cycles;
|
||||
|
||||
run_for cpu mem (cycles_remaining - cycles)
|
||||
|
|
Loading…
Reference in a new issue