badline 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +185 -0
  3. data/CLAUDE.md +217 -0
  4. data/CODE_OF_CONDUCT.md +92 -0
  5. data/CONTRIBUTING.md +51 -0
  6. data/README.md +224 -31
  7. data/doc/pinned-behaviour.md +1117 -0
  8. data/exe/badline +22 -2
  9. data/exe/badline-sid +34 -0
  10. data/lib/badline/address_bus.rb +96 -25
  11. data/lib/badline/audio/aiff.rb +36 -0
  12. data/lib/badline/audio/bare_player.rb +129 -0
  13. data/lib/badline/audio/cli.rb +133 -0
  14. data/lib/badline/audio/console.rb +75 -0
  15. data/lib/badline/audio/jukebox.rb +82 -0
  16. data/lib/badline/audio/machine_player.rb +57 -0
  17. data/lib/badline/audio/options.rb +137 -0
  18. data/lib/badline/audio/pcm_writer.rb +57 -0
  19. data/lib/badline/audio/playback.rb +108 -0
  20. data/lib/badline/audio/renderer.rb +85 -0
  21. data/lib/badline/audio/sdl_sink.rb +102 -0
  22. data/lib/badline/audio/wav.rb +23 -0
  23. data/lib/badline/audio.rb +14 -0
  24. data/lib/badline/cartridge/action_replay.rb +111 -0
  25. data/lib/badline/cartridge/atomic_power.rb +24 -0
  26. data/lib/badline/cartridge/bank.rb +81 -0
  27. data/lib/badline/cartridge/comal80.rb +48 -0
  28. data/lib/badline/cartridge/dinamic.rb +34 -0
  29. data/lib/badline/cartridge/easy_flash.rb +122 -0
  30. data/lib/badline/cartridge/epyx_fastload.rb +71 -0
  31. data/lib/badline/cartridge/final_cartridge3.rb +77 -0
  32. data/lib/badline/cartridge/flash.rb +265 -0
  33. data/lib/badline/cartridge/freezer.rb +31 -0
  34. data/lib/badline/cartridge/fun_play.rb +38 -0
  35. data/lib/badline/cartridge/g_mod2.rb +62 -0
  36. data/lib/badline/cartridge/game_system.rb +40 -0
  37. data/lib/badline/cartridge/kcs_power.rb +72 -0
  38. data/lib/badline/cartridge/mach5.rb +34 -0
  39. data/lib/badline/cartridge/magic_desk.rb +6 -3
  40. data/lib/badline/cartridge/ocean.rb +11 -12
  41. data/lib/badline/cartridge/pagefox.rb +56 -0
  42. data/lib/badline/cartridge/retro_replay.rb +276 -0
  43. data/lib/badline/cartridge/rex_utility.rb +33 -0
  44. data/lib/badline/cartridge/rgcd.rb +48 -0
  45. data/lib/badline/cartridge/simons_basic.rb +40 -0
  46. data/lib/badline/cartridge/standard.rb +3 -19
  47. data/lib/badline/cartridge/super_games.rb +36 -0
  48. data/lib/badline/cartridge/westermann.rb +34 -0
  49. data/lib/badline/cartridge/zaxxon.rb +50 -0
  50. data/lib/badline/cartridge.rb +132 -16
  51. data/lib/badline/cia/interrupt_register.rb +102 -0
  52. data/lib/badline/cia/serial.rb +219 -0
  53. data/lib/badline/cia/timer.rb +43 -31
  54. data/lib/badline/cia.rb +138 -88
  55. data/lib/badline/color_memory.rb +14 -5
  56. data/lib/badline/computer.rb +97 -12
  57. data/lib/badline/control_ports.rb +54 -9
  58. data/lib/badline/cpu/addressing.rb +131 -0
  59. data/lib/badline/cpu/microcode.rb +169 -0
  60. data/lib/badline/cpu/operations.rb +88 -0
  61. data/lib/badline/cpu/stack_operations.rb +82 -0
  62. data/lib/badline/cpu.rb +109 -168
  63. data/lib/badline/cycleable.rb +3 -0
  64. data/lib/badline/datasette.rb +81 -0
  65. data/lib/badline/debug_register.rb +28 -0
  66. data/lib/badline/gui/application.rb +79 -18
  67. data/lib/badline/gui/gamepads.rb +97 -0
  68. data/lib/badline/gui/joy_map.rb +15 -7
  69. data/lib/badline/gui.rb +1 -0
  70. data/lib/badline/input/mouse1351.rb +41 -0
  71. data/lib/badline/input/paddles.rb +36 -0
  72. data/lib/badline/input.rb +4 -0
  73. data/lib/badline/instruction_set/arithmetic.rb +16 -21
  74. data/lib/badline/instruction_set/bitwise.rb +17 -22
  75. data/lib/badline/instruction_set/branch.rb +20 -19
  76. data/lib/badline/instruction_set/flag.rb +7 -7
  77. data/lib/badline/instruction_set/illegal.rb +14 -22
  78. data/lib/badline/instruction_set/inc_dec.rb +8 -10
  79. data/lib/badline/instruction_set/stack.rb +5 -57
  80. data/lib/badline/instruction_set/transfer.rb +9 -9
  81. data/lib/badline/instruction_set.rb +9 -31
  82. data/lib/badline/interrupts.rb +104 -0
  83. data/lib/badline/joystick.rb +9 -9
  84. data/lib/badline/kernal_trap/channel.rb +51 -0
  85. data/lib/badline/kernal_trap/drive/status.rb +49 -0
  86. data/lib/badline/kernal_trap/drive.rb +277 -0
  87. data/lib/badline/kernal_trap/file.rb +5 -25
  88. data/lib/badline/kernal_trap/load.rb +86 -24
  89. data/lib/badline/kernal_trap/routine.rb +51 -0
  90. data/lib/badline/kernal_trap/save.rb +46 -14
  91. data/lib/badline/kernal_trap/serial.rb +171 -0
  92. data/lib/badline/kernal_trap.rb +4 -0
  93. data/lib/badline/keyboard.rb +40 -16
  94. data/lib/badline/media.rb +48 -10
  95. data/lib/badline/memory.rb +5 -0
  96. data/lib/badline/roms/eapi/LICENSE.md +15 -0
  97. data/lib/badline/roms/eapi/README +41 -0
  98. data/lib/badline/roms/eapi/eapi-am29f040-14 +0 -0
  99. data/lib/badline/sid/decimator.rb +46 -0
  100. data/lib/badline/sid/envelope.rb +232 -0
  101. data/lib/badline/sid/filter.rb +213 -0
  102. data/lib/badline/sid/voice.rb +61 -0
  103. data/lib/badline/sid/waveform/combined.rb +126 -0
  104. data/lib/badline/sid/waveform/fast_forward.rb +105 -0
  105. data/lib/badline/sid/waveform/noise_writeback.rb +82 -0
  106. data/lib/badline/sid/waveform.rb +289 -0
  107. data/lib/badline/sid.rb +274 -7
  108. data/lib/badline/status.rb +251 -23
  109. data/lib/badline/storage/crt_file.rb +18 -2
  110. data/lib/badline/storage/d64_image.rb +6 -0
  111. data/lib/badline/storage/d71_image.rb +4 -0
  112. data/lib/badline/storage/d81_image.rb +4 -0
  113. data/lib/badline/storage/disk_image.rb +99 -13
  114. data/lib/badline/storage/host_directory.rb +30 -4
  115. data/lib/badline/storage/sid_file.rb +274 -0
  116. data/lib/badline/storage/song_lengths.rb +65 -0
  117. data/lib/badline/storage/t64.rb +67 -0
  118. data/lib/badline/storage/tap.rb +68 -0
  119. data/lib/badline/storage.rb +19 -0
  120. data/lib/badline/time_of_day.rb +105 -52
  121. data/lib/badline/version.rb +1 -1
  122. data/lib/badline/vic/bank.rb +14 -6
  123. data/lib/badline/vic/border_mask.rb +76 -0
  124. data/lib/badline/vic/collisions.rb +104 -0
  125. data/lib/badline/vic/color_patches.rb +33 -0
  126. data/lib/badline/vic/display_state.rb +92 -29
  127. data/lib/badline/vic/graphics_mode.rb +78 -53
  128. data/lib/badline/vic/graphics_shifter.rb +140 -0
  129. data/lib/badline/vic/register_log.rb +63 -0
  130. data/lib/badline/vic/registers.rb +3 -0
  131. data/lib/badline/vic/sequencer.rb +163 -137
  132. data/lib/badline/vic/sequencer_output.rb +116 -0
  133. data/lib/badline/vic/sprite/internal_bus.rb +49 -0
  134. data/lib/badline/vic/sprite/shifter.rb +129 -0
  135. data/lib/badline/vic/sprite.rb +275 -65
  136. data/lib/badline/vic/sprites.rb +220 -58
  137. data/lib/badline/vic.rb +389 -55
  138. data/lib/badline.rb +5 -0
  139. data/vendor/.gitkeep +0 -0
  140. metadata +104 -4
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class SID
5
+ class Waveform
6
+ # Running an oscillator many cycles in one step, for the catch-ups that
7
+ # fast-forward the voices between whole cycles.
8
+ module FastForward
9
+ # Runs `cycles` cycles as #cycle! would, for an oscillator that is not
10
+ # feeding back. What those cycles latch onto the output is left to the
11
+ # whole cycle that follows, which latches over it.
12
+ def fast_forward(cycles)
13
+ drain_floating(cycles) if @selected.zero?
14
+ if @test
15
+ bleed_shift_register(cycles)
16
+ else
17
+ while @shift_pipeline.positive? && cycles.positive?
18
+ advance
19
+ cycles -= 1
20
+ end
21
+ skip_ahead(cycles) if cycles.positive?
22
+ compare_pulse(((@accumulator - @frequency) & 0xffffff) >> 12)
23
+ end
24
+ @stale = true
25
+ end
26
+
27
+ # The cycle on which the accumulator MSB next rises, counting the next
28
+ # one as 1, or nil if it never will. The test bit leaves the last
29
+ # rise standing for as long as it is held.
30
+ def cycles_to_msb_rise
31
+ return (@msb_rising ? 1 : nil) if @test
32
+ return if @frequency.zero?
33
+
34
+ distance = (MSB - @accumulator) & 0xffffff
35
+ distance = 0x1000000 if distance.zero?
36
+ (distance + @frequency - 1) / @frequency
37
+ end
38
+
39
+ # A combined waveform with sawtooth (on the 6581) or noise in the mix
40
+ # writes back into the oscillator every cycle, so it cannot be
41
+ # fast-forwarded. Triangle and pulse alone write nothing back.
42
+ def feedback?
43
+ combined_waveform? && (@selected.anybits?(0x8) || (@topbit_feedback && @selected.anybits?(0x2)))
44
+ end
45
+
46
+ # Whether a catch-up has to run this oscillator a cycle at a time.
47
+ # While synthesizing, every combined waveform does, so the filter
48
+ # integrates it a cycle at a time.
49
+ def stepped?(synthesizing) = synthesizing ? combined_waveform? : feedback?
50
+
51
+ private
52
+
53
+ def combined_waveform? = !@test && combined?(@selected)
54
+
55
+ # Advances an accumulator with no shift in flight. Each rise of bit 19
56
+ # shifts the LFSR two cycles on, so a rise in the last two cycles
57
+ # leaves its shift pending for the cycles after.
58
+ def skip_ahead(cycles)
59
+ previous = @accumulator
60
+ @accumulator += @frequency * cycles
61
+ rises = bit19_rises(previous, @accumulator)
62
+ if rises.positive?
63
+ @shift_pipeline = pipeline_after(cycles)
64
+ rises -= 1 if @shift_pipeline.positive?
65
+ rises.times { shift_noise }
66
+ end
67
+ @accumulator &= 0xffffff
68
+ end
69
+
70
+ # Bit 19 rises at most once a cycle, on each odd multiple of 2^19,
71
+ # and at most once in any eight.
72
+ def bit19_rises(from, to) = ((to + 0x80000) >> 20) - ((from + 0x80000) >> 20)
73
+
74
+ # How far a rise on one of the last two cycles has come.
75
+ def pipeline_after(cycles)
76
+ last = @accumulator - @frequency
77
+ return 2 if bit19_rises(last, @accumulator).positive?
78
+ return 1 if cycles > 1 && bit19_rises(last - @frequency, last).positive?
79
+
80
+ 0
81
+ end
82
+
83
+ def drain_floating(cycles)
84
+ return unless @floating_ttl.positive?
85
+
86
+ @floating_ttl -= cycles
87
+ return if @floating_ttl.positive?
88
+
89
+ @floating_ttl = 0
90
+ @floating = 0x000
91
+ end
92
+
93
+ def bleed_shift_register(cycles = 1)
94
+ return unless @shift_register_reset.positive?
95
+
96
+ @shift_register_reset -= cycles
97
+ return if @shift_register_reset.positive?
98
+
99
+ @shift_register_reset = 0
100
+ @shift_register = SHIFT_REGISTER_RESET
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class SID
5
+ class Waveform
6
+ # Noise combined with other waveforms: the output lines write back into
7
+ # the LFSR, and what they write is not always what OSC3 reads.
8
+ module NoiseWriteback
9
+ # The LFSR bits the noise shaper reads, paired with the output bit each
10
+ # one drives (Dag Lem's diagram in SID/noise-reset_new). A combined
11
+ # waveform drives the same lines, so a bit held low there is written
12
+ # back into the register.
13
+ NOISE_TAPS = [[0x100000, 0x800], [0x040000, 0x400], [0x004000, 0x200],
14
+ [0x000800, 0x100], [0x000200, 0x080], [0x000020, 0x040],
15
+ [0x000004, 0x020], [0x000001, 0x010]].freeze
16
+
17
+ # Pulse+noise with the pulse high: the noise lines next to the four
18
+ # below them, which nothing drives, are pulled low. The OSC3 read and
19
+ # the LFSR writeback see the same lines against different thresholds,
20
+ # so they lose a different number of bits. The 8580's pair is the one
21
+ # that reproduces SID/wf12nsr's pulse+noise row (it reads $f8, and the
22
+ # writeback leaves $fc behind), and SID/wb_testsuite's C->9 and C->E
23
+ # rows need that same $fc written at release. On the 6581 the read is
24
+ # the $fc of SID/wf12nsr's readme (VICE bug #1037), and the writeback
25
+ # pulls nothing: wb_testsuite's 8/9/A/B->C rows run pulse+noise and
26
+ # leave the register alone.
27
+ PULSE_NOISE_READ = { mos6581: 0xfc0, mos8580: 0xf80 }.freeze
28
+ PULSE_NOISE_WRITE = { mos6581: 0xff0, mos8580: 0xfc0 }.freeze
29
+
30
+ # What a 6581 writes back when the test bit falls with only pulse+noise
31
+ # common to the old waveform and the new one (SID/wb_testsuite's D->C,
32
+ # E->C, F->C and D->E rows).
33
+ PULSE_NOISE_RELEASE = 0xf80
34
+
35
+ private
36
+
37
+ # What the lines write into the LFSR, against a lower threshold than
38
+ # OSC3 reads them by: a line left high between two low ones reads high
39
+ # (SID/noisewriteback's noise_writeback_test2) but is written low
40
+ # (SID/wf12nsr's noise+triangle and noise+sawtooth rows).
41
+ def write_back(selected, value)
42
+ return value & ((value << 1) | (value >> 1)) unless selected == 0xc
43
+ return value if value.zero?
44
+
45
+ noise & @pulse_noise_write
46
+ end
47
+
48
+ def write_shift_register(value)
49
+ NOISE_TAPS.each { |bit, line| @shift_register &= ~bit if value.nobits?(line) }
50
+ end
51
+
52
+ # Releasing the test bit finishes the shift it interrupted: the old
53
+ # waveform's output may be written back first, then a bit clocks in
54
+ # over the forced-high bit 22. On the 6581, a change that keeps only
55
+ # pulse+noise of the old waveform writes the lowest noise lines low.
56
+ def release_test(previous)
57
+ if @topbit_feedback && previous != 0xc && (previous & @selected) == 0xc
58
+ write_shift_register(noise & PULSE_NOISE_RELEASE)
59
+ elsif release_writes_back?(previous, @selected)
60
+ write_shift_register(write_back(previous, shape(previous)))
61
+ end
62
+ shift_noise(1)
63
+ end
64
+
65
+ # Which waveform changes write the old output back as the test bit
66
+ # falls (SID/wb_testsuite, after libresidfp's do_writeback). Noise has
67
+ # to have been combined before and still be selected after. Dropping
68
+ # to noise alone writes nothing back unless all four were selected,
69
+ # nor does changing to pulse+noise or from pulse+noise to
70
+ # sawtooth+noise, nor, on the 6581, trading triangle for sawtooth or
71
+ # back.
72
+ def release_writes_back?(previous, selected)
73
+ return false if previous <= 0x8 || selected < 0x8
74
+ return previous == 0xf if selected == 0x8
75
+ return false if selected == 0xc || (previous == 0xc && selected == 0xa)
76
+
77
+ !(@topbit_feedback && [previous & 0x3, selected & 0x3].sort == [0x1, 0x2])
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,289 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/sid/waveform/combined"
4
+ require "badline/sid/waveform/fast_forward"
5
+ require "badline/sid/waveform/noise_writeback"
6
+
7
+ module Badline
8
+ class SID
9
+ # Waveform generator: a 24-bit phase accumulator and the shapers that
10
+ # read it.
11
+ #
12
+ # The accumulator advances by the 16-bit frequency every cycle, so every
13
+ # waveform is a pure function of the phase. Bit 23 drives hard sync and
14
+ # the triangle fold, bit 19 clocks the noise LFSR.
15
+ #
16
+ # Selecting more than one waveform shorts the shapers onto the lines the
17
+ # oscillator itself reads back, so a combined waveform is not only a
18
+ # shape: a low top bit reaches the accumulator MSB through the sawtooth
19
+ # switch, and noise in the mix is written back into the LFSR. The shape
20
+ # itself comes from Combined's model of the lines pulling on each other.
21
+ #
22
+ # The pulse comparator's output reaches the lines a cycle after the
23
+ # accumulator it compared.
24
+ #
25
+ # With nothing selected the DAC input floats, holding the last value
26
+ # driven onto it until the charge drains away.
27
+ class Waveform
28
+ include FastForward
29
+ include NoiseWriteback
30
+
31
+ # All bits high at power up, odd ones stored inverted (SID/oscinit).
32
+ POWER_ON_ACCUMULATOR = 0x555555
33
+
34
+ # Holding the test bit bleeds every LFSR bit high; the power-on state is
35
+ # one shift further on, which is what releasing the bit leaves behind.
36
+ SHIFT_REGISTER_RESET = 0x7fffff
37
+ NOISE_SEED = 0x7ffffe
38
+
39
+ # Cycles the test bit needs to bleed the LFSR high. SID/bitfade's
40
+ # delaynoise measures ~$950000 on a real 8580. Nothing measures the
41
+ # 6581: SID/resid-test's oscsample dumps show it holding its bits
42
+ # through the ~$8800 cycles between runs, and the slow reset
43
+ # (SID/noise-reset_old) allows it a second.
44
+ SHIFT_REGISTER_RESET_DELAY = { mos6581: 0x80000, mos8580: 0x950000 }.freeze
45
+
46
+ # Cycles the floating DAC input holds its charge (SID/osc3-wave0).
47
+ FLOATING_OUTPUT_TTL = 0x4000
48
+
49
+ MSB = 0x800000
50
+
51
+ attr_accessor :sync_source, :sync_dest
52
+ attr_reader :accumulator, :shift_register, :frequency, :pulse_width, :pulse
53
+
54
+ def initialize(model: :mos6581)
55
+ @topbit_feedback = model != :mos8580
56
+ @tri_saw_delay = model == :mos8580
57
+ @pulse_noise_read = PULSE_NOISE_READ.fetch(model)
58
+ @pulse_noise_write = PULSE_NOISE_WRITE.fetch(model)
59
+ @combined = Combined.tables(model)
60
+ @shift_register_reset_delay = SHIFT_REGISTER_RESET_DELAY.fetch(model)
61
+ @accumulator = POWER_ON_ACCUMULATOR
62
+ @sync_source = @sync_dest = self
63
+ reset!
64
+ end
65
+
66
+ # The RES line clears the registers and reseeds the LFSR, but leaves
67
+ # the accumulator alone (SID/oscinit).
68
+ def reset!
69
+ @delayed_sawtooth = @delayed_triangle = 0x000
70
+ @shift_register = NOISE_SEED
71
+ @shift_register_reset = 0
72
+ @shift_pipeline = 0
73
+ @frequency = 0x0000
74
+ @pulse_width = 0x000
75
+ @selected = 0x0
76
+ @test = false
77
+ @ring = false
78
+ @sync = false
79
+ @msb_rising = false
80
+ @floating = 0x000
81
+ @floating_ttl = 0
82
+ @pulse = 0xfff
83
+ @output = 0x000
84
+ @stale = true
85
+ end
86
+
87
+ def sync? = @sync
88
+ def msb_rising? = @msb_rising
89
+
90
+ def frequency_low=(value)
91
+ @frequency = (@frequency & 0xff00) | value
92
+ end
93
+
94
+ def frequency_high=(value)
95
+ @frequency = (value << 8) | (@frequency & 0x00ff)
96
+ end
97
+
98
+ def pulse_width_low=(value)
99
+ @pulse_width = (@pulse_width & 0xf00) | value
100
+ @stale = true
101
+ end
102
+
103
+ def pulse_width_high=(value)
104
+ @pulse_width = ((value & 0x0f) << 8) | (@pulse_width & 0x0ff)
105
+ @stale = true
106
+ end
107
+
108
+ # The test bit clears the accumulator and stalls the LFSR half way
109
+ # through a shift, holding bit 22 high while its bits bleed up.
110
+ def control=(value)
111
+ previous = @selected
112
+ @selected = (value >> 4) & 0x0f
113
+ @ring = value.anybits?(0x04)
114
+ @sync = value.anybits?(0x02)
115
+ test = value.anybits?(0x08)
116
+ if test
117
+ @accumulator = 0x000000
118
+ @pulse = 0xfff
119
+ @shift_pipeline = 0
120
+ @shift_register_reset = @shift_register_reset_delay unless @test
121
+ elsif @test
122
+ release_test(previous)
123
+ end
124
+ @test = test
125
+ @stale = true
126
+ end
127
+
128
+ def cycle!
129
+ latch_output
130
+ compare_pulse
131
+ @test ? bleed_shift_register : advance
132
+ @stale = true
133
+ end
134
+
135
+ # Hard sync resets the next voice's accumulator when ours wraps. A
136
+ # source that is itself synced on the cycle its own MSB rises does not
137
+ # pass the sync on.
138
+ def synchronize!
139
+ return unless @msb_rising && @sync_dest.sync? &&
140
+ !(@sync && @sync_source.msb_rising?)
141
+
142
+ @sync_dest.reset_accumulator
143
+ end
144
+
145
+ def reset_accumulator
146
+ @accumulator = 0x000000
147
+ @stale = true
148
+ end
149
+
150
+ # Held for the rest of the cycle once computed, so the filter, OSC3 and
151
+ # the oscillator's own feedback all see one value per cycle.
152
+ def output
153
+ return @output unless @stale
154
+
155
+ @stale = false
156
+ @output = @selected.zero? ? @floating : shape(@selected)
157
+ end
158
+
159
+ # What OSC3 reads. The 8580 delays the triangle and sawtooth shapers
160
+ # by half a cycle, which OSC3 latches as a whole cycle late; pulse and
161
+ # noise still reach the lines on time.
162
+ def osc3
163
+ return output unless @tri_saw_delay && @selected.anybits?(0x3)
164
+
165
+ shape(@selected, @delayed_sawtooth, @delayed_triangle)
166
+ end
167
+
168
+ def sawtooth = @accumulator >> 12
169
+
170
+ # The upper half of the ramp is folded back down, shifted left one bit
171
+ # to keep the full 12-bit range.
172
+ def triangle(selected = @selected)
173
+ folded = inverted?(selected) ? @accumulator ^ 0xffffff : @accumulator
174
+ (folded >> 11) & 0xfff
175
+ end
176
+
177
+ # Eight taps off the 23-bit LFSR, gathered into a 12-bit sample.
178
+ def noise
179
+ ((@shift_register & 0x100000) >> 9) |
180
+ ((@shift_register & 0x040000) >> 8) |
181
+ ((@shift_register & 0x004000) >> 5) |
182
+ ((@shift_register & 0x000800) >> 3) |
183
+ ((@shift_register & 0x000200) >> 2) |
184
+ ((@shift_register & 0x000020) << 1) |
185
+ ((@shift_register & 0x000004) << 3) |
186
+ ((@shift_register & 0x000001) << 4)
187
+ end
188
+
189
+ private
190
+
191
+ def shape(selected, saw = sawtooth, tri = triangle(selected))
192
+ case selected
193
+ when 0x1 then tri
194
+ when 0x2 then saw
195
+ when 0x4 then @pulse
196
+ when 0x8 then noise
197
+ else combined(selected, saw, tri)
198
+ end
199
+ end
200
+
201
+ # A low pulse grounds every line. Noise with pulse alone loses its
202
+ # lowest lines; noise with triangle or sawtooth is ANDed over the rest
203
+ # of the mix.
204
+ def combined(selected, saw, tri)
205
+ return 0x000 if selected.anybits?(0x4) && @pulse.zero?
206
+ return noise & @pulse_noise_read if selected == 0xc
207
+
208
+ rest = selected & 0x7
209
+ value = case rest
210
+ when 0x1 then tri
211
+ when 0x2 then saw
212
+ when 0x4 then 0xfff
213
+ else @combined[rest][rest == 0x5 ? tri : saw]
214
+ end
215
+ selected.anybits?(0x8) ? value & noise : value
216
+ end
217
+
218
+ def compare_pulse(phase = @accumulator >> 12) = (@pulse = @test || phase >= @pulse_width ? 0xfff : 0x000)
219
+
220
+ # More than one bit set in the selection.
221
+ def combined?(selected) = selected.anybits?(selected - 1)
222
+
223
+ # The one point in the cycle where the waveform selector drives the DAC:
224
+ # either it latches a fresh value, or nothing drives the line and the
225
+ # charge already on it drains a little further.
226
+ def latch_output
227
+ value = output
228
+ if @tri_saw_delay
229
+ @delayed_sawtooth = sawtooth
230
+ @delayed_triangle = triangle
231
+ end
232
+ if @selected.zero?
233
+ drain_floating(1)
234
+ return
235
+ end
236
+
237
+ @floating = value
238
+ @floating_ttl = FLOATING_OUTPUT_TTL
239
+ feed_back(value) if combined?(@selected) && !@test
240
+ end
241
+
242
+ # A zero on the shared output lines travels back into the oscillator:
243
+ # through the sawtooth switch it clears the accumulator MSB on the next
244
+ # cycle, and with noise selected it lands in the LFSR, where a bit
245
+ # pulled low can never come back. The 8580 buffers the top bit behind a
246
+ # flip-flop before the sawtooth switch, so only the LFSR sees it. In the
247
+ # first phase of a shift the register bits float, so nothing is pulled
248
+ # down: the output is latched instead, for the second phase to write.
249
+ def feed_back(value)
250
+ @accumulator &= ~MSB if @topbit_feedback && @selected.anybits?(0x2) && value.nobits?(0x800)
251
+ write_shift_register(write_back(@selected, value)) if @selected.anybits?(0x8) && @shift_pipeline != 1
252
+ end
253
+
254
+ # A rise of bit 19 starts a shift that takes two more cycles
255
+ # (SID/noisewriteback's noise_writeback_test2, after libresidfp's shift
256
+ # pipeline). The first phase latches the output, and the second writes
257
+ # it over the taps, by the test bit release rule with the waveform left
258
+ # alone, before the register shifts.
259
+ def advance
260
+ previous = @accumulator
261
+ @accumulator = (previous + @frequency) & 0xffffff
262
+ @msb_rising = previous.nobits?(MSB) && @accumulator.anybits?(MSB)
263
+ if previous.nobits?(0x080000) && @accumulator.anybits?(0x080000)
264
+ @shift_pipeline = 2
265
+ elsif @shift_pipeline.positive? && (@shift_pipeline -= 1).zero?
266
+ write_shift_register(write_back(@selected, @output)) if release_writes_back?(@selected, @selected)
267
+ shift_noise
268
+ end
269
+ end
270
+
271
+ # Bit 0 feeds back bits 22 and 17, with the test bit forcing bit 22 high.
272
+ def shift_noise(forced = 0)
273
+ bit0 = ((@shift_register >> 22) | forced) ^ (@shift_register >> 17)
274
+ @shift_register = ((@shift_register << 1) & 0x7fffff) | (bit0 & 0x1)
275
+ end
276
+
277
+ # TriXOR = !Saw & ((!V3 & Ring) ^ bit23), where V3 is the modulating
278
+ # voice's MSB (SID/ringmod).
279
+ def inverted?(selected)
280
+ return false if selected.anybits?(0x2)
281
+
282
+ msb = @accumulator.anybits?(MSB)
283
+ return msb unless @ring
284
+
285
+ msb != @sync_source.accumulator.nobits?(MSB)
286
+ end
287
+ end
288
+ end
289
+ end