From 67093196eaa34a13f9338ab1eb7e41b3dcd46212 Mon Sep 17 00:00:00 2001 From: Fabien Freling Date: Sat, 23 Jan 2016 13:16:18 +0100 Subject: [PATCH] Implement timers --- src/memory.ml | 65 ++++++++++++++++++++++++++++++++++++++++++++------- src/oboy.ml | 11 +++++---- src/timer.ml | 38 ++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 src/timer.ml diff --git a/src/memory.ml b/src/memory.ml index f6a5943..5ae0af1 100644 --- a/src/memory.ml +++ b/src/memory.ml @@ -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 diff --git a/src/oboy.ml b/src/oboy.ml index 107e23d..11a9bbb 100644 --- a/src/oboy.ml +++ b/src/oboy.ml @@ -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 diff --git a/src/timer.ml b/src/timer.ml new file mode 100644 index 0000000..50fbea0 --- /dev/null +++ b/src/timer.ml @@ -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