Implement timers

This commit is contained in:
Fabien Freling 2016-01-23 13:16:18 +01:00
parent 6c7544dffb
commit 67093196ea
3 changed files with 102 additions and 12 deletions

View file

@ -1,22 +1,34 @@
open Bytes
open Printf
(** http://bgb.bircd.org/pandocs.htm#memorymap
http://imrannazar.com/GameBoy-Emulation-in-JavaScript:-Memory *)
(** @see http://bgb.bircd.org/pandocs.htm#memorymap
@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 = {
rom_bank_00 : bytes; (* cartridge, 16KB *)
rom_bank_01 : bytes; (* additional bank, 16KB *)
vram : bytes; (* Video RAM, 8KB *)
vram : bytes; (* Video RAM, 8KB *)
wram_bank_0 : bytes; (* work RAM, 4KB *)
wram_bank_1 : bytes; (* work RAM, 4KB *)
io : bytes; (* I/O ports *)
hram : bytes; (* High RAM, 8KB *)
interrupt : bytes; (* Interrupt Enable Register *)
io : bytes; (* I/O ports *)
hram : bytes; (* High RAM, 8KB *)
interrupt : bytes; (* Interrupt Enable Register *)
}
type t = {
map : map;
timer_div : Timer.t;
tima : Timer.t;
}
let init (cartridge: Cartridge.t) =
{
let map = {
rom_bank_00 = sub cartridge.full_rom 0 0x4000;
rom_bank_01 = create 0x4000;
vram = create 0x2000;
@ -25,7 +37,19 @@ let init (cartridge: Cartridge.t) =
io = create 0x0080;
hram = create 0x2000;
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 =
match addr with
@ -50,3 +74,28 @@ let get mem addr =
let set mem addr c =
let m, x = get_mem_bank mem addr in
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

View file

@ -3,16 +3,19 @@ open Printf
let fps = 60
let cycles_per_frame = Cpu.frequence / fps
let rec run (cpu: Cpu.t) (mem: Memory.map) (screen: Screen.t) =
printf "\n";
let rec run (cpu: Cpu.t) (mem: Memory.t) (screen: Screen.t) =
let start = Unix.gettimeofday () in
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
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;
Memory.update_timers mem cycles;
run_for cpu mem (cycles_remaining - cycles)
end
in

38
src/timer.ml Normal file
View 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