Class: PSX::Timers

Inherits:
Object
  • Object
show all
Defined in:
lib/psx/timers.rb

Overview

Root Counters (Timers) Timer 0: Pixel clock / Dot clock Timer 1: Horizontal retrace Timer 2: System clock / 8

Defined Under Namespace

Classes: Timer

Constant Summary collapse

NUM_TIMERS =
3
MODE_SYNC_ENABLE =

Mode register bits

0x0001
MODE_SYNC_MODE =

Sync enable

0x0006
MODE_RESET_TARGET =

Sync mode (bits 1-2)

0x0008
MODE_IRQ_TARGET =

Reset counter on target

0x0010
MODE_IRQ_OVERFLOW =

IRQ when target reached

0x0020
MODE_IRQ_REPEAT =

IRQ on overflow

0x0040
MODE_IRQ_TOGGLE =

Repeat IRQ

0x0080
MODE_CLOCK_SOURCE =

Toggle IRQ bit

0x0300
MODE_IRQ_FLAG =

Clock source (bits 8-9)

0x0400
MODE_TARGET_REACHED =

IRQ flag (bit 10, read-only)

0x0800
MODE_OVERFLOW =

Target reached (bit 11)

0x1000

Instance Method Summary collapse

Constructor Details

#initialize(interrupts: nil) ⇒ Timers

Returns a new instance of Timers.



105
106
107
108
109
# File 'lib/psx/timers.rb', line 105

def initialize(interrupts: nil)
  @interrupts = interrupts
  @timers = Array.new(NUM_TIMERS) { Timer.new }
  @system_counter = 0
end

Instance Method Details

#hblankObject

Call on HBlank



171
172
173
# File 'lib/psx/timers.rb', line 171

def hblank
  # Timer 0 and 1 can sync to hblank
end

#read(offset) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/psx/timers.rb', line 111

def read(offset)
  timer_num = offset / 0x10
  reg = offset % 0x10

  return 0 if timer_num >= NUM_TIMERS

  timer = @timers[timer_num]
  case reg
  when 0x00 then timer.read_counter
  when 0x04 then timer.read_mode
  when 0x08 then timer.read_target
  else 0
  end
end

#tick(cycles = 1) ⇒ Object

Call this periodically to advance timers cycles: number of CPU cycles elapsed



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/psx/timers.rb', line 142

def tick(cycles = 1)
  @system_counter += cycles

  # Timer 2 runs at system clock / 8
  timer2_ticks = @system_counter / 8
  if timer2_ticks > 0
    if @timers[2].tick(timer2_ticks)
      @interrupts&.request(Interrupts::IRQ_TIMER2)
    end
    @system_counter %= 8
  end

  # Timer 0 and 1 are typically synced to GPU
  # For simplicity, run them at a fixed rate
  if @timers[0].tick(cycles)
    @interrupts&.request(Interrupts::IRQ_TIMER0)
  end

  if @timers[1].tick(cycles / 8)  # Slower for hblank timing
    @interrupts&.request(Interrupts::IRQ_TIMER1)
  end
end

#vblankObject

Call on VBlank to update timer 1



166
167
168
# File 'lib/psx/timers.rb', line 166

def vblank
  # Timer 1 often syncs to vblank
end

#write(offset, value) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/psx/timers.rb', line 126

def write(offset, value)
  timer_num = offset / 0x10
  reg = offset % 0x10

  return if timer_num >= NUM_TIMERS

  timer = @timers[timer_num]
  case reg
  when 0x00 then timer.write_counter(value)
  when 0x04 then timer.write_mode(value)
  when 0x08 then timer.write_target(value)
  end
end