Class: Badline::CIA

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Addressable
Defined in:
lib/badline/cia.rb,
lib/badline/cia/timer.rb

Overview

CIA (Complex Interface Adapter) chip

Defined Under Namespace

Classes: Timer

Instance Attribute Summary collapse

Attributes included from Addressable

#end, #length

Instance Method Summary collapse

Methods included from Addressable

#[], #[]=, #addressable_at, #in_range?, #peek16, #poke16, #range

Methods included from IntegerHelper

#bcd, #bcd_to_i, #format16, #format8, #high_byte, #low_byte, #signed_int8, #uint16

Constructor Details

#initialize(start: 0, peripheral: nil) ⇒ CIA

Returns a new instance of CIA.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/badline/cia.rb', line 23

def initialize(start: 0, peripheral: nil)
  addressable_at(start, length: 2**8)

  @peripheral = peripheral
  @data_port_a = 0xff
  @data_port_b = 0xff
  @data_dir_a = 0xff
  @data_dir_b = 0x0
  @irq_pending = 0
  @serial_data = 0x0
  @tod = TimeOfDay.new
  @interrupt_control = Status.new([:timer_a, :timer_b, :alarm, :serial,
                                   :flag, 0, 0, 0])
  @interrupt_status = Status.new([:timer_a, :timer_b, :alarm, :serial,
                                  :flag, 0, 0, :interrupt])
  @control_a = Status.new(%i[start output out_mode run_mode load
                             in_mode serial_mode clock_frequency])
  @control_b = Status.new(%i[start output out_mode run_mode load
                             in_cnt in_timer_a alarm])
  @ta = Timer.new(@control_a)
  @tb = Timer.new(@control_b)
end

Instance Attribute Details

#control_aObject (readonly)

Returns the value of attribute control_a.



12
13
14
# File 'lib/badline/cia.rb', line 12

def control_a
  @control_a
end

#control_bObject (readonly)

Returns the value of attribute control_b.



12
13
14
# File 'lib/badline/cia.rb', line 12

def control_b
  @control_b
end

#interrupt_controlObject (readonly)

Returns the value of attribute interrupt_control.



12
13
14
# File 'lib/badline/cia.rb', line 12

def interrupt_control
  @interrupt_control
end

#interrupt_statusObject (readonly)

Returns the value of attribute interrupt_status.



12
13
14
# File 'lib/badline/cia.rb', line 12

def interrupt_status
  @interrupt_status
end

#peripheralObject (readonly)

Returns the value of attribute peripheral.



12
13
14
# File 'lib/badline/cia.rb', line 12

def peripheral
  @peripheral
end

#startObject (readonly)

Returns the value of attribute start.



12
13
14
# File 'lib/badline/cia.rb', line 12

def start
  @start
end

Instance Method Details

#cycle!Object



54
55
56
57
58
59
60
61
# File 'lib/badline/cia.rb', line 54

def cycle!
  if @irq_pending.positive?
    @irq_pending -= 1
    interrupt_status.interrupt = true if @irq_pending.zero?
  end
  update_timers
  @tod.cycle! { trigger_alarm }
end

#interrupt!(delay = 1) ⇒ Object



46
47
48
# File 'lib/badline/cia.rb', line 46

def interrupt!(delay = 1)
  @irq_pending = @irq_pending.positive? ? [@irq_pending, delay].min : delay
end

#interrupted?Boolean

Returns:

  • (Boolean)


50
51
52
# File 'lib/badline/cia.rb', line 50

def interrupted?
  interrupt_status.value.anybits?(0x80)
end

#peek(addr) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/badline/cia.rb', line 80

def peek(addr)
  case index(addr) & 0x0f
  when 0x00 then read_port_a
  when 0x01 then read_port_b
  when 0x02 then @data_dir_a
  when 0x03 then @data_dir_b
  when 0x04 then low_byte(@ta.counter)
  when 0x05 then high_byte(@ta.counter)
  when 0x06 then low_byte(@tb.counter)
  when 0x07 then high_byte(@tb.counter)
  when 0x08 then @tod.tenths
  when 0x09 then @tod.seconds
  when 0x0a then @tod.minutes
  when 0x0b then @tod.hours
  when 0x0c then @serial_data
  when 0x0d
    value = interrupt_status.value
    interrupt_status.value = 0x0 # Burn after reading
    @irq_pending = 0
    value
  when 0x0e then control_a.value
  when 0x0f then control_b.value
  end
end

#poke(addr, value) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/badline/cia.rb', line 105

def poke(addr, value)
  case index(addr) & 0x0f
  when 0x00 then @data_port_a = value
  when 0x01 then @data_port_b = value
  when 0x02 then @data_dir_a = value
  when 0x03 then @data_dir_b = value
  when 0x04 then @ta.write_latch_low(value)
  when 0x05 then @ta.write_latch_high(value)
  when 0x06 then @tb.write_latch_low(value)
  when 0x07 then @tb.write_latch_high(value)
  when 0x08 then @tod.write(:tenths, value, alarm: control_b.alarm?)
  when 0x09 then @tod.write(:seconds, value, alarm: control_b.alarm?)
  when 0x0a then @tod.write(:minutes, value, alarm: control_b.alarm?)
  when 0x0b then @tod.write_hours(value, alarm: control_b.alarm?)
  when 0x0c
    # TODO: Serial
  when 0x0d then write_interrupt_control(value)
  when 0x0e then @ta.write_control(value)
  when 0x0f then @tb.write_control(value)
  end
end

#port_a_linesObject

Port A as driven by the data/direction registers alone, without peripheral pulldown. Cheap path for the VIC bank lookup.



70
71
72
# File 'lib/badline/cia.rb', line 70

def port_a_lines
  driven_lines(@data_port_a, @data_dir_a)
end

#read_port_aObject



63
64
65
66
# File 'lib/badline/cia.rb', line 63

def read_port_a
  pulldown = peripheral ? peripheral.read_a(@data_port_a, @data_port_b) : 0xff
  driven_lines(@data_port_a, @data_dir_a) & pulldown
end

#read_port_bObject



74
75
76
77
78
# File 'lib/badline/cia.rb', line 74

def read_port_b
  pulldown = peripheral ? peripheral.read_b(@data_port_a, @data_port_b) : 0xff
  value = driven_lines(@data_port_b, @data_dir_b) & pulldown
  apply_timer_output(value)
end