Class: PSX::CDROM

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

Overview

Minimal CD-ROM controller stub.

Enough to convince the BIOS that there’s a CD subsystem present with no disc inserted, so it proceeds to the shell screen. Handles the commands the BIOS issues during boot: GetStat, Init, GetID, Setmode, Test, etc.

We don’t model timing accurately. Responses are queued and delivered one per tick (called from the emulator’s frame loop), respecting that the previous IRQ must be acknowledged and the previous response FIFO drained before the next response shows up.

Constant Summary collapse

STAT_INDEX_MASK =

Status register (0x1F801800 read) bits

0x03
STAT_ADPCM_BUSY =
0x04
STAT_PARAMETER_FIFO_EMPTY =
0x08
STAT_PARAMETER_FIFO_NOT_FULL =
0x10
STAT_RESPONSE_FIFO_NOT_EMPTY =
0x20
STAT_DATA_FIFO_NOT_EMPTY =
0x40
STAT_BUSY =
0x80
DEFAULT_STAT =

“stat” byte returned in command responses. Bit 4 = shell open (we say closed); bit 0 = error. We default to 0x02 (motor on, no error, no disc seek done) for no-disc.

0x02

Instance Method Summary collapse

Constructor Details

#initialize(interrupts:) ⇒ CDROM

Returns a new instance of CDROM.



29
30
31
32
# File 'lib/psx/cdrom.rb', line 29

def initialize(interrupts:)
  @interrupts = interrupts
  reset
end

Instance Method Details

#read8(reg) ⇒ Object

— Bus interface ——————————————————



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/psx/cdrom.rb', line 46

def read8(reg)
  case reg & 3
  when 0 then status
  when 1
    # Response FIFO
    v = @response.shift || 0
    v & 0xFF
  when 2
    # Data FIFO (no data, just return 0)
    0
  when 3
    case @index
    when 0, 2 then @irq_enable | 0xE0
    when 1, 3 then @irq_flags | 0xE0
    end
  end
end

#resetObject



34
35
36
37
38
39
40
41
42
# File 'lib/psx/cdrom.rb', line 34

def reset
  @index = 0
  @parameters = []
  @response  = []
  @irq_enable = 0
  @irq_flags  = 0          # bits 0-2 = pending INT type (0..7)
  @pending = []            # [[int_type, [bytes...]], ...]
  @stat = DEFAULT_STAT
end

#tickObject

Advance one step in the response queue. Call this periodically (e.g. once per VBlank). One delivery per call: a response only appears once the previous one has been acknowledged.



99
100
101
102
103
104
105
106
107
108
# File 'lib/psx/cdrom.rb', line 99

def tick
  return if @pending.empty?
  return if @irq_flags != 0           # previous IRQ not yet acknowledged
  return if !@response.empty?         # previous response not yet drained

  type, data = @pending.shift
  @response.concat(data)
  @irq_flags = type & 0x07
  @interrupts.request(Interrupts::IRQ_CDROM) if (@irq_enable & @irq_flags) != 0
end

#write8(reg, value) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/psx/cdrom.rb', line 64

def write8(reg, value)
  v = value & 0xFF
  case reg & 3
  when 0
    @index = v & 3
  when 1
    case @index
    when 0 then execute_command(v)
    # other indices: audio map / right CD audio routing — ignore
    end
  when 2
    case @index
    when 0
      @parameters.push(v) if @parameters.size < 16
    when 1
      @irq_enable = v & 0x1F
    # 2/3 = audio routing — ignore
    end
  when 3
    case @index
    when 0
      # Request register: bit 7 BFRD enables data buffer read; bit 5 SMEN.
      # Both unused by our stub.
    when 1
      # Ack IRQ flags and optionally reset parameter FIFO (bit 6).
      @irq_flags &= ~(v & 0x1F)
      @parameters.clear if (v & 0x40) != 0
    # 2/3 = audio volume apply — ignore
    end
  end
end