From b4c81fa3f993f42e11dbbd54c7087f2c5f212c9a Mon Sep 17 00:00:00 2001 From: Fabien Freling Date: Tue, 1 Mar 2016 23:15:56 +0100 Subject: [PATCH] Add CPU.handle_interrupts --- src/cpu.ml | 33 +++++++++++++++++++++------------ src/interrupt.ml | 28 +++++++++++++++++++++++++++- src/oboy.ml | 2 ++ 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/cpu.ml b/src/cpu.ml index b51ee60..e305de3 100644 --- a/src/cpu.ml +++ b/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 diff --git a/src/interrupt.ml b/src/interrupt.ml index 3e28847..94f5b69 100644 --- a/src/interrupt.ml +++ b/src/interrupt.ml @@ -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 + diff --git a/src/oboy.ml b/src/oboy.ml index f333dbd..0d44ee3 100644 --- a/src/oboy.ml +++ b/src/oboy.ml @@ -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)