Add CPU.handle_interrupts

This commit is contained in:
Fabien Freling 2016-03-01 23:15:56 +01:00
parent c6106203eb
commit b4c81fa3f9
3 changed files with 50 additions and 13 deletions

View file

@ -306,7 +306,7 @@ let run cpu (mem: Memory.map) =
| '\xFB' -> let inst = sprintf "EI" in | '\xFB' -> let inst = sprintf "EI" in
(* enable interrupts *) (* enable interrupts *)
Interrupt.ime := true; Interrupt.gIME := true;
inst, 4 inst, 4
| x -> eprintf "opcode 0x%02X\n" (int_of_char x); | x -> eprintf "opcode 0x%02X\n" (int_of_char x);
@ -323,7 +323,7 @@ let run cpu (mem: Memory.map) =
* Interrupt handlers * Interrupt handlers
* *
************************************************) ************************************************)
let handle_interrupt cpu mem flag = let handle_interrupt cpu mmap flag =
let int_vector = match flag with let int_vector = match flag with
| Interrupt.V_Blank -> 0x40 | Interrupt.V_Blank -> 0x40
| Interrupt.LCD_Stat -> 0x48 | Interrupt.LCD_Stat -> 0x48
@ -331,25 +331,34 @@ let handle_interrupt cpu mem flag =
| Interrupt.Serial -> 0x58 | Interrupt.Serial -> 0x58
| Interrupt.Joypad -> 0x60 in | Interrupt.Joypad -> 0x60 in
(* TODO: reset IF flag *) Interrupt.reset_IF_flag flag mmap;
Interrupt.ime := false; Interrupt.gIME := false;
push_pc_stack cpu mem; push_pc_stack cpu mmap;
cpu.reg.pc <- int_vector 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_ = let handle_interrupts cpu mmap =
if not !Interrupt.ime then if not !Interrupt.gIME then
() (* Interrupt Master Enable flag set to false, nothing to do *) () (* 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 (* N.B.: flags are precomputed once but an interrupt could be requested
during that time *) during that time *)
let ie = Interrupt.get_IE mmap in
let if_ = Interrupt.get_IF mmap in
let interrupts = ie land if_ in let interrupts = ie land if_ in
let flags = Interrupt.get_flags interrupts in let flags = Interrupt.get_flags interrupts in
match flags with match flags with
| [] -> () (* No interrupt *) | [] -> () (* No interrupt *)
| _ -> List.iter (fun x -> handle_interrupt cpu mem x) flags | _ -> List.iter (fun x -> handle_interrupt cpu mmap x) flags
) end

View file

@ -6,7 +6,11 @@
* LICENSE file at the top level directory of this source tree. * 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 = type t =
| V_Blank | V_Blank
@ -15,6 +19,14 @@ type t =
| Serial | Serial
| Joypad | 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. *) (** Return the list of interrupt flags sorted by priority. *)
let get_flags byte = let get_flags byte =
@ -38,3 +50,17 @@ let get_flags byte =
| _ -> List.rev accu in | _ -> List.rev accu in
get_flag byte 0 [] 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

View file

@ -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 let inst, cycles = Cpu.run cpu mem.map in
printf "[Instruction] %s\n" inst; printf "[Instruction] %s\n" inst;
Cpu.handle_interrupts cpu mem.map;
Memory.update_timers mem cycles; Memory.update_timers mem cycles;
run_for cpu mem (cycles_remaining - cycles) run_for cpu mem (cycles_remaining - cycles)