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
|
| '\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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue