Implement timers
This commit is contained in:
parent
6c7544dffb
commit
67093196ea
|
@ -1,8 +1,14 @@
|
||||||
open Bytes
|
open Bytes
|
||||||
open Printf
|
open Printf
|
||||||
|
|
||||||
(** http://bgb.bircd.org/pandocs.htm#memorymap
|
(** @see http://bgb.bircd.org/pandocs.htm#memorymap
|
||||||
http://imrannazar.com/GameBoy-Emulation-in-JavaScript:-Memory *)
|
@see http://imrannazar.com/GameBoy-Emulation-in-JavaScript:-Memory *)
|
||||||
|
|
||||||
|
(** Common addresses *)
|
||||||
|
let gDIV = 0xFF04 (** divider register *)
|
||||||
|
let gTIMA = 0xFF05 (** timer counter *)
|
||||||
|
let gTMA = 0xFF06 (** timer modulo *)
|
||||||
|
let gTAC = 0xFF07 (** timer control *)
|
||||||
|
|
||||||
type map = {
|
type map = {
|
||||||
rom_bank_00 : bytes; (* cartridge, 16KB *)
|
rom_bank_00 : bytes; (* cartridge, 16KB *)
|
||||||
|
@ -15,8 +21,14 @@ type map = {
|
||||||
interrupt : bytes; (* Interrupt Enable Register *)
|
interrupt : bytes; (* Interrupt Enable Register *)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type t = {
|
||||||
|
map : map;
|
||||||
|
timer_div : Timer.t;
|
||||||
|
tima : Timer.t;
|
||||||
|
}
|
||||||
|
|
||||||
let init (cartridge: Cartridge.t) =
|
let init (cartridge: Cartridge.t) =
|
||||||
{
|
let map = {
|
||||||
rom_bank_00 = sub cartridge.full_rom 0 0x4000;
|
rom_bank_00 = sub cartridge.full_rom 0 0x4000;
|
||||||
rom_bank_01 = create 0x4000;
|
rom_bank_01 = create 0x4000;
|
||||||
vram = create 0x2000;
|
vram = create 0x2000;
|
||||||
|
@ -25,7 +37,19 @@ let init (cartridge: Cartridge.t) =
|
||||||
io = create 0x0080;
|
io = create 0x0080;
|
||||||
hram = create 0x2000;
|
hram = create 0x2000;
|
||||||
interrupt = create 1
|
interrupt = create 1
|
||||||
}
|
} in
|
||||||
|
|
||||||
|
(** Init register values
|
||||||
|
@see http://bgb.bircd.org/pandocs.htm#powerupsequence *)
|
||||||
|
let zero = char_of_int 0 in
|
||||||
|
set map.io 0x05 zero; (* TIMA, 0xFF05 *)
|
||||||
|
set map.io 0x06 zero; (* TMA, 0xFF06 *)
|
||||||
|
set map.io 0x07 zero; (* TAC, 0xFF07 *)
|
||||||
|
|
||||||
|
let timer_div = Timer.create 16384 true in
|
||||||
|
let tima = Timer.create_tima 0 in
|
||||||
|
|
||||||
|
{ map; timer_div; tima; }
|
||||||
|
|
||||||
let get_mem_bank mem addr =
|
let get_mem_bank mem addr =
|
||||||
match addr with
|
match addr with
|
||||||
|
@ -50,3 +74,28 @@ let get mem addr =
|
||||||
let set mem addr c =
|
let set mem addr c =
|
||||||
let m, x = get_mem_bank mem addr in
|
let m, x = get_mem_bank mem addr in
|
||||||
set m x c
|
set m x c
|
||||||
|
|
||||||
|
(** Increment byte in memory, wrap value in case of overflow *)
|
||||||
|
let inc mem addr =
|
||||||
|
let m, x = get_mem_bank mem addr in
|
||||||
|
let value = Bytes.get m x |> int_of_char in
|
||||||
|
let inc_value = value + 1 in
|
||||||
|
let overflow = inc_value > 0xFF in
|
||||||
|
let c = inc_value mod 0x01FF |> char_of_int in
|
||||||
|
Bytes.set m x c;
|
||||||
|
overflow
|
||||||
|
|
||||||
|
|
||||||
|
let update_timers mem cycles =
|
||||||
|
let should_inc_div = Timer.update mem.timer_div cycles in
|
||||||
|
if should_inc_div then ignore (inc mem.map gDIV);
|
||||||
|
|
||||||
|
let should_inc_tima = Timer.update mem.tima cycles in
|
||||||
|
if should_inc_tima then begin
|
||||||
|
let overflow = inc mem.map gTIMA in
|
||||||
|
if overflow then begin
|
||||||
|
let tma = get mem.map gTMA in
|
||||||
|
set mem.map gTIMA tma
|
||||||
|
(* TODO: INT 50 - Timer interupt *)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
11
src/oboy.ml
11
src/oboy.ml
|
@ -3,16 +3,19 @@ open Printf
|
||||||
let fps = 60
|
let fps = 60
|
||||||
let cycles_per_frame = Cpu.frequence / fps
|
let cycles_per_frame = Cpu.frequence / fps
|
||||||
|
|
||||||
let rec run (cpu: Cpu.t) (mem: Memory.map) (screen: Screen.t) =
|
let rec run (cpu: Cpu.t) (mem: Memory.t) (screen: Screen.t) =
|
||||||
printf "\n";
|
|
||||||
let start = Unix.gettimeofday () in
|
let start = Unix.gettimeofday () in
|
||||||
printf "start %f\n" start;
|
printf "start %f\n" start;
|
||||||
|
|
||||||
let rec run_for cpu mem cycles_remaining =
|
let rec run_for cpu (mem: Memory.t) cycles_remaining =
|
||||||
if cycles_remaining > 0 then
|
if cycles_remaining > 0 then
|
||||||
begin
|
begin
|
||||||
let inst, cycles = Cpu.run cpu mem in
|
printf "\n";
|
||||||
|
let inst, cycles = Cpu.run cpu mem.map in
|
||||||
printf "[Instruction] %s\n" inst;
|
printf "[Instruction] %s\n" inst;
|
||||||
|
|
||||||
|
Memory.update_timers mem cycles;
|
||||||
|
|
||||||
run_for cpu mem (cycles_remaining - cycles)
|
run_for cpu mem (cycles_remaining - cycles)
|
||||||
end
|
end
|
||||||
in
|
in
|
||||||
|
|
38
src/timer.ml
Normal file
38
src/timer.ml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
|
||||||
|
let frequence = 4194304
|
||||||
|
|
||||||
|
type t = {
|
||||||
|
clock_rate : int;
|
||||||
|
period : int;
|
||||||
|
mutable enabled : bool;
|
||||||
|
mutable cycles_left : int;
|
||||||
|
}
|
||||||
|
|
||||||
|
let create clock_rate enabled =
|
||||||
|
let period = frequence / clock_rate in
|
||||||
|
{
|
||||||
|
clock_rate;
|
||||||
|
period;
|
||||||
|
enabled;
|
||||||
|
cycles_left = period;
|
||||||
|
}
|
||||||
|
|
||||||
|
(** TIMA - Timer counter *)
|
||||||
|
let create_tima tac =
|
||||||
|
let enabled = Bit.is_set tac 2 in
|
||||||
|
let clock_select = tac land 0b00000011 in
|
||||||
|
let clock_rate = match clock_select with
|
||||||
|
| 0 -> 4096
|
||||||
|
| 1 -> 262144
|
||||||
|
| 2 -> 65536
|
||||||
|
| 3 -> 16384
|
||||||
|
| _ -> failwith "Unreachable clock rate code."
|
||||||
|
in
|
||||||
|
create clock_rate enabled
|
||||||
|
|
||||||
|
let update timer cycles =
|
||||||
|
let remain = timer.cycles_left - cycles in
|
||||||
|
let should_inc = remain <= 0 in
|
||||||
|
if should_inc then
|
||||||
|
timer.cycles_left <- remain + timer.period;
|
||||||
|
should_inc
|
Loading…
Reference in a new issue