badline 0.12.1 → 0.14.0

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 (565) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -1
  3. data/CLAUDE.md +86 -38
  4. data/CONTRIBUTING.md +4 -3
  5. data/README.md +9 -6
  6. data/doc/library.md +9 -9
  7. data/doc/media.md +2 -1
  8. data/doc/pinned-behaviour.md +134 -105
  9. data/doc/snapshot-format.md +1047 -0
  10. data/doc/snapshots.md +73 -43
  11. data/lib/badline/audio/bare_player.rb +53 -20
  12. data/lib/badline/audio/cli.rb +6 -6
  13. data/lib/badline/audio/jukebox.rb +2 -2
  14. data/lib/badline/audio/machine_player.rb +16 -10
  15. data/lib/badline/{media → audio}/queue.rb +1 -1
  16. data/lib/badline/audio/queued_tune.rb +6 -6
  17. data/lib/badline/audio/renderer.rb +33 -22
  18. data/lib/badline/audio/stereo.rb +1 -1
  19. data/lib/badline/c128/banks.rb +6 -3
  20. data/lib/badline/c128/bus/keyed_state.rb +45 -0
  21. data/lib/badline/c128/bus.rb +35 -20
  22. data/lib/badline/c128/color_lines.rb +2 -2
  23. data/lib/badline/c128/cpu.rb +1 -1
  24. data/lib/badline/c128/drives.rb +14 -34
  25. data/lib/badline/c128/fast_serial.rb +5 -5
  26. data/lib/badline/c128/keyed_modules.rb +117 -0
  27. data/lib/badline/c128/keyed_state.rb +91 -0
  28. data/lib/badline/c128/keys.rb +9 -0
  29. data/lib/badline/c128/mmu.rb +9 -8
  30. data/lib/badline/c128/mmu_pages.rb +21 -1
  31. data/lib/badline/c128/modes.rb +21 -11
  32. data/lib/badline/c128/z80.rb +2 -2
  33. data/lib/badline/c128/z80_turns.rb +1 -1
  34. data/lib/badline/c128.rb +21 -16
  35. data/lib/badline/c64/attachments.rb +42 -0
  36. data/lib/badline/c64/banked_ram/banking.rb +32 -0
  37. data/lib/badline/c64/banked_ram/plus256k.rb +64 -0
  38. data/lib/badline/c64/banked_ram/plus60k.rb +47 -0
  39. data/lib/badline/c64/banked_ram/unexpanded.rb +30 -0
  40. data/lib/badline/c64/banked_ram.rb +23 -0
  41. data/lib/badline/c64/bus/debug_register.rb +32 -0
  42. data/lib/badline/c64/bus/extra_sids.rb +32 -0
  43. data/lib/badline/c64/bus/fittings.rb +40 -0
  44. data/lib/badline/c64/bus/keyed_state.rb +41 -0
  45. data/lib/badline/c64/bus/pla.rb +149 -0
  46. data/lib/badline/c64/bus/roms.rb +41 -0
  47. data/lib/badline/c64/bus/sid_slots.rb +44 -0
  48. data/lib/badline/c64/bus.rb +219 -0
  49. data/lib/badline/c64/cartridge/action_replay.rb +115 -0
  50. data/lib/badline/c64/cartridge/atomic_power.rb +26 -0
  51. data/lib/badline/c64/cartridge/bank.rb +83 -0
  52. data/lib/badline/c64/cartridge/comal80.rb +52 -0
  53. data/lib/badline/c64/cartridge/dinamic.rb +36 -0
  54. data/lib/badline/c64/cartridge/easy_flash.rb +132 -0
  55. data/lib/badline/c64/cartridge/epyx_fastload.rb +73 -0
  56. data/lib/badline/c64/cartridge/final_cartridge3.rb +79 -0
  57. data/lib/badline/c64/cartridge/flash.rb +279 -0
  58. data/lib/badline/c64/cartridge/freezer.rb +35 -0
  59. data/lib/badline/c64/cartridge/fun_play.rb +40 -0
  60. data/lib/badline/c64/cartridge/g_mod2.rb +64 -0
  61. data/lib/badline/c64/cartridge/game_system.rb +42 -0
  62. data/lib/badline/c64/cartridge/geo_ram.rb +65 -0
  63. data/lib/badline/c64/cartridge/kcs_power.rb +69 -0
  64. data/lib/badline/c64/cartridge/keyed_state.rb +393 -0
  65. data/lib/badline/c64/cartridge/mach5.rb +34 -0
  66. data/lib/badline/c64/cartridge/magic_desk.rb +32 -0
  67. data/lib/badline/c64/cartridge/ocean.rb +37 -0
  68. data/lib/badline/c64/cartridge/pagefox.rb +60 -0
  69. data/lib/badline/c64/cartridge/retro_replay.rb +278 -0
  70. data/lib/badline/c64/cartridge/rex_utility.rb +31 -0
  71. data/lib/badline/c64/cartridge/rgcd.rb +50 -0
  72. data/lib/badline/c64/cartridge/simons_basic.rb +37 -0
  73. data/lib/badline/c64/cartridge/standard.rb +17 -0
  74. data/lib/badline/c64/cartridge/super_games.rb +40 -0
  75. data/lib/badline/c64/cartridge/westermann.rb +32 -0
  76. data/lib/badline/c64/cartridge/zaxxon.rb +52 -0
  77. data/lib/badline/c64/cartridge.rb +232 -0
  78. data/lib/badline/c64/color_memory.rb +27 -0
  79. data/lib/badline/{computer → c64}/common.rb +2 -2
  80. data/lib/badline/{computer → c64}/kernal_traps.rb +39 -36
  81. data/lib/badline/c64/keyed_modules.rb +111 -0
  82. data/lib/badline/c64/keyed_state.rb +68 -0
  83. data/lib/badline/c64/reu/dma.rb +271 -0
  84. data/lib/badline/c64/reu/keyed_state.rb +140 -0
  85. data/lib/badline/c64/reu/ram.rb +94 -0
  86. data/lib/badline/c64/reu/trigger.rb +34 -0
  87. data/lib/badline/c64/reu.rb +289 -0
  88. data/lib/badline/c64/true_drives.rb +120 -0
  89. data/lib/badline/{computer.rb → c64.rb} +24 -15
  90. data/lib/badline/chips/cia/fast_forward.rb +62 -0
  91. data/lib/badline/chips/cia/interrupt_register.rb +128 -0
  92. data/lib/badline/chips/cia/keyed_parts.rb +213 -0
  93. data/lib/badline/chips/cia/serial.rb +246 -0
  94. data/lib/badline/chips/cia/time_of_day.rb +210 -0
  95. data/lib/badline/chips/cia/timer.rb +188 -0
  96. data/lib/badline/chips/cia.rb +281 -0
  97. data/lib/badline/chips/cpu/addressing.rb +133 -0
  98. data/lib/badline/chips/cpu/boundary.rb +56 -0
  99. data/lib/badline/chips/cpu/core.rb +183 -0
  100. data/lib/badline/chips/cpu/instruction.rb +338 -0
  101. data/lib/badline/chips/cpu/instruction_set/arithmetic.rb +118 -0
  102. data/lib/badline/chips/cpu/instruction_set/bitwise.rb +130 -0
  103. data/lib/badline/chips/cpu/instruction_set/branch.rb +83 -0
  104. data/lib/badline/chips/cpu/instruction_set/flag.rb +67 -0
  105. data/lib/badline/chips/cpu/instruction_set/illegal.rb +274 -0
  106. data/lib/badline/chips/cpu/instruction_set/inc_dec.rb +73 -0
  107. data/lib/badline/chips/cpu/instruction_set/stack.rb +56 -0
  108. data/lib/badline/chips/cpu/instruction_set/transfer.rb +141 -0
  109. data/lib/badline/chips/cpu/instruction_set.rb +59 -0
  110. data/lib/badline/chips/cpu/interrupts.rb +108 -0
  111. data/lib/badline/chips/cpu/keyed_state.rb +177 -0
  112. data/lib/badline/chips/cpu/microcode.rb +195 -0
  113. data/lib/badline/chips/cpu/operations.rb +90 -0
  114. data/lib/badline/chips/cpu/port.rb +133 -0
  115. data/lib/badline/chips/cpu/stack_operations.rb +84 -0
  116. data/lib/badline/chips/cpu/traps.rb +23 -0
  117. data/lib/badline/chips/cpu.rb +17 -0
  118. data/lib/badline/chips/sid/catch_up.rb +122 -0
  119. data/lib/badline/chips/sid/decimator.rb +68 -0
  120. data/lib/badline/chips/sid/envelope.rb +236 -0
  121. data/lib/badline/chips/sid/filter.rb +227 -0
  122. data/lib/badline/chips/sid/keyed_parts.rb +286 -0
  123. data/lib/badline/chips/sid/voice.rb +69 -0
  124. data/lib/badline/chips/sid/waveform/combined.rb +128 -0
  125. data/lib/badline/chips/sid/waveform/fast_forward.rb +107 -0
  126. data/lib/badline/chips/sid/waveform/noise_writeback.rb +124 -0
  127. data/lib/badline/chips/sid/waveform.rb +305 -0
  128. data/lib/badline/chips/sid.rb +264 -0
  129. data/lib/badline/chips/ted/beam.rb +119 -0
  130. data/lib/badline/chips/ted/timers.rb +66 -0
  131. data/lib/badline/chips/ted.rb +143 -0
  132. data/lib/badline/chips/tia.rb +49 -0
  133. data/lib/badline/chips/vdc/keyed_parts.rb +105 -0
  134. data/lib/badline/{c128 → chips}/vdc/memory.rb +1 -1
  135. data/lib/badline/{c128 → chips}/vdc/painter.rb +1 -13
  136. data/lib/badline/{c128 → chips}/vdc/raster.rb +1 -17
  137. data/lib/badline/{c128 → chips}/vdc/window.rb +1 -10
  138. data/lib/badline/{c128 → chips}/vdc.rb +6 -31
  139. data/lib/badline/chips/via/control_lines.rb +109 -0
  140. data/lib/badline/chips/via/fast_forward.rb +55 -0
  141. data/lib/badline/chips/via/interrupt_register.rb +60 -0
  142. data/lib/badline/chips/via/keyed_parts.rb +163 -0
  143. data/lib/badline/chips/via/shift_register.rb +168 -0
  144. data/lib/badline/chips/via/timer1.rb +120 -0
  145. data/lib/badline/chips/via/timer2.rb +117 -0
  146. data/lib/badline/chips/via.rb +267 -0
  147. data/lib/badline/chips/vic/bank.rb +135 -0
  148. data/lib/badline/chips/vic/border_mask.rb +78 -0
  149. data/lib/badline/chips/vic/collisions.rb +106 -0
  150. data/lib/badline/chips/vic/color_patches.rb +38 -0
  151. data/lib/badline/chips/vic/display_state.rb +168 -0
  152. data/lib/badline/chips/vic/graphics_mode.rb +166 -0
  153. data/lib/badline/chips/vic/graphics_shifter.rb +183 -0
  154. data/lib/badline/chips/vic/keyed_parts.rb +228 -0
  155. data/lib/badline/chips/vic/keyed_sprites.rb +225 -0
  156. data/lib/badline/chips/vic/keyed_state.rb +102 -0
  157. data/lib/badline/chips/vic/register_log.rb +65 -0
  158. data/lib/badline/chips/vic/registers.rb +188 -0
  159. data/lib/badline/chips/vic/sequencer.rb +275 -0
  160. data/lib/badline/chips/vic/sequencer_output.rb +118 -0
  161. data/lib/badline/chips/vic/sprite/internal_bus.rb +59 -0
  162. data/lib/badline/chips/vic/sprite/shifter.rb +148 -0
  163. data/lib/badline/chips/vic/sprite/timing.rb +58 -0
  164. data/lib/badline/chips/vic/sprite.rb +337 -0
  165. data/lib/badline/chips/vic/sprites.rb +281 -0
  166. data/lib/badline/chips/vic.rb +787 -0
  167. data/lib/badline/chips/vic_i/keyed_parts.rb +142 -0
  168. data/lib/badline/{vic20/vic → chips/vic_i}/painter.rb +2 -30
  169. data/lib/badline/chips/vic_i/sound/output.rb +85 -0
  170. data/lib/badline/chips/vic_i/sound.rb +227 -0
  171. data/lib/badline/{vic20/vic → chips/vic_i}/text_window.rb +2 -2
  172. data/lib/badline/{vic20/vic.rb → chips/vic_i.rb} +6 -6
  173. data/lib/badline/chips/z80/arithmetic.rb +119 -0
  174. data/lib/badline/chips/z80/bit_page.rb +57 -0
  175. data/lib/badline/chips/z80/block_io.rb +60 -0
  176. data/lib/badline/chips/z80/block_transfers.rb +61 -0
  177. data/lib/badline/chips/z80/bus_cycles.rb +113 -0
  178. data/lib/badline/chips/z80/core.rb +115 -0
  179. data/lib/badline/chips/z80/exchanges.rb +52 -0
  180. data/lib/badline/chips/z80/extended_page.rb +91 -0
  181. data/lib/badline/chips/z80/flags.rb +39 -0
  182. data/lib/badline/chips/z80/interrupt_acceptance.rb +48 -0
  183. data/lib/badline/chips/z80/jumps.rb +84 -0
  184. data/lib/badline/chips/z80/keyed_state.rb +73 -0
  185. data/lib/badline/chips/z80/loads.rb +100 -0
  186. data/lib/badline/chips/z80/main_page.rb +136 -0
  187. data/lib/badline/chips/z80/registers.rb +122 -0
  188. data/lib/badline/chips/z80/shifts.rb +66 -0
  189. data/lib/badline/chips/z80.rb +13 -0
  190. data/lib/badline/chips.rb +10 -0
  191. data/lib/badline/core.rb +13 -32
  192. data/lib/badline/drives/datasette/keyed_state.rb +38 -0
  193. data/lib/badline/drives/datasette.rb +173 -0
  194. data/lib/badline/drives/engine/core.rb +152 -0
  195. data/lib/badline/drives/engine/fast_serial.rb +75 -0
  196. data/lib/badline/drives/engine/idle.rb +127 -0
  197. data/lib/badline/drives/engine/idle_cpu.rb +30 -0
  198. data/lib/badline/drives/engine/orbit.rb +81 -0
  199. data/lib/badline/drives/engine/serial_lines.rb +72 -0
  200. data/lib/badline/drives/engine/sleep.rb +114 -0
  201. data/lib/badline/drives/engine/vias.rb +86 -0
  202. data/lib/badline/drives/engine/watch.rb +152 -0
  203. data/lib/badline/drives/engine.rb +11 -0
  204. data/lib/badline/drives/model1541/bus.rb +75 -0
  205. data/lib/badline/drives/model1541/byte_ready.rb +50 -0
  206. data/lib/badline/drives/model1541/cpu.rb +13 -0
  207. data/lib/badline/drives/model1541/disk.rb +318 -0
  208. data/lib/badline/drives/model1541/disk_via.rb +38 -0
  209. data/lib/badline/drives/model1541/gcr.rb +59 -0
  210. data/lib/badline/drives/model1541/keyed_state.rb +251 -0
  211. data/lib/badline/drives/model1541/mechanism.rb +313 -0
  212. data/lib/badline/drives/model1541/rotation.rb +123 -0
  213. data/lib/badline/drives/model1541/sector_reader.rb +78 -0
  214. data/lib/badline/drives/model1541/serial_port.rb +20 -0
  215. data/lib/badline/drives/model1541/track.rb +100 -0
  216. data/lib/badline/drives/model1541.rb +80 -0
  217. data/lib/badline/drives/model1571/bus.rb +100 -0
  218. data/lib/badline/drives/model1571/cpu.rb +13 -0
  219. data/lib/badline/drives/model1571/keyed_state.rb +108 -0
  220. data/lib/badline/drives/model1571/serial_port.rb +39 -0
  221. data/lib/badline/drives/model1571/serial_via.rb +27 -0
  222. data/lib/badline/drives/model1571/wd1770.rb +108 -0
  223. data/lib/badline/drives/model1571.rb +141 -0
  224. data/lib/badline/drives/model1581/bus.rb +126 -0
  225. data/lib/badline/drives/model1581/cpu.rb +13 -0
  226. data/lib/badline/drives/model1581/disk.rb +133 -0
  227. data/lib/badline/drives/model1581/keyed_state.rb +280 -0
  228. data/lib/badline/drives/model1581/mechanism.rb +98 -0
  229. data/lib/badline/drives/model1581/serial_port.rb +54 -0
  230. data/lib/badline/drives/model1581/slot.rb +61 -0
  231. data/lib/badline/drives/model1581/track.rb +102 -0
  232. data/lib/badline/drives/model1581/track_bytes.rb +77 -0
  233. data/lib/badline/drives/model1581/wd1772/registers.rb +91 -0
  234. data/lib/badline/drives/model1581/wd1772/sector_writes.rb +75 -0
  235. data/lib/badline/drives/model1581/wd1772/sectors.rb +152 -0
  236. data/lib/badline/drives/model1581/wd1772/stepping.rb +111 -0
  237. data/lib/badline/drives/model1581/wd1772/tracks.rb +127 -0
  238. data/lib/badline/drives/model1581/wd1772/turn.rb +75 -0
  239. data/lib/badline/drives/model1581/wd1772.rb +216 -0
  240. data/lib/badline/drives/model1581.rb +206 -0
  241. data/lib/badline/drives/serial_bus.rb +161 -0
  242. data/lib/badline/drives.rb +8 -0
  243. data/lib/badline/frontend/app.rb +3 -3
  244. data/lib/badline/frontend/boot.rb +4 -4
  245. data/lib/badline/frontend/controls.rb +19 -3
  246. data/lib/badline/frontend/drive_led.rb +44 -21
  247. data/lib/badline/frontend/machine_window.rb +3 -1
  248. data/lib/badline/frontend/menu/menu_dialogs.rb +2 -2
  249. data/lib/badline/frontend/player/filter_view.rb +1 -1
  250. data/lib/badline/frontend/player/player_controller.rb +2 -2
  251. data/lib/badline/frontend/player/player_screen.rb +1 -0
  252. data/lib/badline/frontend/player/theme.rb +7 -7
  253. data/lib/badline/frontend/snapshots.rb +12 -1
  254. data/lib/badline/frontend/timeline.rb +30 -12
  255. data/lib/badline/input/control_ports.rb +78 -0
  256. data/lib/badline/input/joystick.rb +27 -0
  257. data/lib/badline/input/keyboard.rb +114 -0
  258. data/lib/badline/input/matrix_typist.rb +98 -0
  259. data/lib/badline/input.rb +4 -0
  260. data/lib/badline/kernal_trap/chrout.rb +45 -0
  261. data/lib/badline/kernal_trap/{drive → dos}/block_commands.rb +1 -1
  262. data/lib/badline/kernal_trap/dos/channel.rb +156 -0
  263. data/lib/badline/kernal_trap/{drive → dos}/channels.rb +1 -26
  264. data/lib/badline/kernal_trap/{drive → dos}/memory.rb +1 -10
  265. data/lib/badline/kernal_trap/{drive → dos}/parameters.rb +1 -1
  266. data/lib/badline/kernal_trap/{drive → dos}/status.rb +1 -9
  267. data/lib/badline/kernal_trap/{drive → dos}/write_file.rb +1 -19
  268. data/lib/badline/kernal_trap/{drive → dos}/writes.rb +1 -1
  269. data/lib/badline/kernal_trap/{drive.rb → dos.rb} +9 -27
  270. data/lib/badline/kernal_trap/file_routine.rb +4 -4
  271. data/lib/badline/kernal_trap/keyed_state.rb +150 -0
  272. data/lib/badline/kernal_trap/layout.rb +75 -11
  273. data/lib/badline/kernal_trap/load.rb +19 -21
  274. data/lib/badline/kernal_trap/routine.rb +12 -0
  275. data/lib/badline/kernal_trap/save.rb +12 -22
  276. data/lib/badline/kernal_trap/serial.rb +8 -24
  277. data/lib/badline/kernal_trap.rb +3 -2
  278. data/lib/badline/machine/clocking.rb +1 -1
  279. data/lib/badline/machine/keyboard_buffer.rb +49 -0
  280. data/lib/badline/machine/settling.rb +29 -0
  281. data/lib/badline/machine.rb +10 -7
  282. data/lib/badline/media/boot_disk.rb +14 -2
  283. data/lib/badline/media/extensions.rb +10 -0
  284. data/lib/badline/media/true_drive.rb +1 -1
  285. data/lib/badline/media/tune.rb +2 -2
  286. data/lib/badline/media.rb +3 -3
  287. data/lib/badline/memory.rb +3 -6
  288. data/lib/badline/model.rb +1 -1
  289. data/lib/badline/options/family.rb +5 -2
  290. data/lib/badline/options/help.rb +42 -16
  291. data/lib/badline/options/table.rb +2 -0
  292. data/lib/badline/options/validation.rb +2 -0
  293. data/lib/badline/options.rb +6 -1
  294. data/lib/badline/plus4/acia.rb +45 -0
  295. data/lib/badline/plus4/bus.rb +312 -0
  296. data/lib/badline/plus4/cpu.rb +10 -0
  297. data/lib/badline/plus4/cpu_port.rb +93 -0
  298. data/lib/badline/plus4/keyboard_port.rb +51 -0
  299. data/lib/badline/plus4/model.rb +38 -0
  300. data/lib/badline/plus4/user_port.rb +31 -0
  301. data/lib/badline/plus4.rb +164 -0
  302. data/lib/badline/roms/README +44 -6
  303. data/lib/badline/roms/plus4/basic.rom +0 -0
  304. data/lib/badline/roms/plus4/function-high.rom +0 -0
  305. data/lib/badline/roms/plus4/function-low.rom +0 -0
  306. data/lib/badline/roms/plus4/kernal-ntsc.rom +0 -0
  307. data/lib/badline/roms/plus4/kernal-pal.rom +0 -0
  308. data/lib/badline/sdl.rb +1 -0
  309. data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
  310. data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
  311. data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
  312. data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
  313. data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
  314. data/lib/badline/sid_tune.rb +6 -0
  315. data/lib/badline/snapshot/c128_setup.rb +32 -0
  316. data/lib/badline/snapshot/c64_setup.rb +83 -0
  317. data/lib/badline/snapshot/container.rb +4 -0
  318. data/lib/badline/snapshot/field_doc/markdown.rb +112 -0
  319. data/lib/badline/snapshot/field_doc.rb +109 -0
  320. data/lib/badline/snapshot/image.rb +132 -46
  321. data/lib/badline/snapshot/keyed/cursor.rb +40 -0
  322. data/lib/badline/snapshot/keyed/modules.rb +66 -0
  323. data/lib/badline/snapshot/keyed/reader.rb +188 -0
  324. data/lib/badline/snapshot/keyed/recorder.rb +88 -0
  325. data/lib/badline/snapshot/keyed/report.rb +26 -0
  326. data/lib/badline/snapshot/keyed/writer.rb +55 -0
  327. data/lib/badline/snapshot/keyed.rb +64 -0
  328. data/lib/badline/snapshot/machine_state.rb +17 -64
  329. data/lib/badline/snapshot/storage_setup.rb +63 -0
  330. data/lib/badline/snapshot/vic20_setup.rb +28 -0
  331. data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
  332. data/lib/badline/snapshot/vice/restore.rb +1 -1
  333. data/lib/badline/snapshot/vice/reu1764.rb +3 -3
  334. data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
  335. data/lib/badline/snapshot/vice.rb +13 -35
  336. data/lib/badline/snapshot.rb +44 -22
  337. data/lib/badline/storage/crt_file.rb +3 -1
  338. data/lib/badline/storage/d64_image.rb +0 -2
  339. data/lib/badline/storage/d71_image.rb +0 -2
  340. data/lib/badline/storage/d81_image.rb +0 -2
  341. data/lib/badline/storage/disk_image.rb +3 -6
  342. data/lib/badline/storage/g64_image.rb +10 -5
  343. data/lib/badline/storage/host_directory.rb +0 -5
  344. data/lib/badline/storage/t64.rb +2 -8
  345. data/lib/badline/storage/tap.rb +43 -15
  346. data/lib/badline/storage/unavailable.rb +1 -1
  347. data/lib/badline/storage.rb +0 -37
  348. data/lib/badline/support/c128_checkpoint.rb +39 -0
  349. data/lib/badline/support/checkpoint.rb +108 -0
  350. data/lib/badline/support/vic20_checkpoint.rb +38 -0
  351. data/lib/badline/support.rb +5 -0
  352. data/lib/badline/version.rb +1 -1
  353. data/lib/badline/vic20/attachments.rb +8 -5
  354. data/lib/badline/vic20/bus/keyed_state.rb +52 -0
  355. data/lib/badline/vic20/bus.rb +2 -25
  356. data/lib/badline/vic20/cpu.rb +1 -1
  357. data/lib/badline/vic20/keyed_modules.rb +73 -0
  358. data/lib/badline/vic20/keyed_state.rb +45 -0
  359. data/lib/badline/vic20/port_wiring.rb +4 -4
  360. data/lib/badline/vic20/user_via_ports.rb +2 -2
  361. data/lib/badline/vic20.rb +22 -17
  362. data/lib/badline.rb +1 -0
  363. metadata +271 -228
  364. data/lib/badline/address_bus/extra_sids.rb +0 -30
  365. data/lib/badline/address_bus/fittings.rb +0 -38
  366. data/lib/badline/address_bus/pla.rb +0 -147
  367. data/lib/badline/address_bus/roms.rb +0 -39
  368. data/lib/badline/address_bus/saved_state.rb +0 -66
  369. data/lib/badline/address_bus/sid_slots.rb +0 -42
  370. data/lib/badline/address_bus.rb +0 -216
  371. data/lib/badline/c128/bus_state.rb +0 -53
  372. data/lib/badline/c128/saved_state.rb +0 -143
  373. data/lib/badline/cartridge/action_replay.rb +0 -126
  374. data/lib/badline/cartridge/atomic_power.rb +0 -24
  375. data/lib/badline/cartridge/bank.rb +0 -81
  376. data/lib/badline/cartridge/comal80.rb +0 -50
  377. data/lib/badline/cartridge/dinamic.rb +0 -34
  378. data/lib/badline/cartridge/easy_flash.rb +0 -145
  379. data/lib/badline/cartridge/epyx_fastload.rb +0 -79
  380. data/lib/badline/cartridge/final_cartridge3.rb +0 -89
  381. data/lib/badline/cartridge/flash.rb +0 -306
  382. data/lib/badline/cartridge/freezer.rb +0 -33
  383. data/lib/badline/cartridge/fun_play.rb +0 -38
  384. data/lib/badline/cartridge/g_mod2.rb +0 -74
  385. data/lib/badline/cartridge/game_system.rb +0 -40
  386. data/lib/badline/cartridge/geo_ram.rb +0 -79
  387. data/lib/badline/cartridge/kcs_power.rb +0 -75
  388. data/lib/badline/cartridge/mach5.rb +0 -32
  389. data/lib/badline/cartridge/magic_desk.rb +0 -30
  390. data/lib/badline/cartridge/ocean.rb +0 -35
  391. data/lib/badline/cartridge/pagefox.rb +0 -68
  392. data/lib/badline/cartridge/retro_replay.rb +0 -320
  393. data/lib/badline/cartridge/rex_utility.rb +0 -29
  394. data/lib/badline/cartridge/rgcd.rb +0 -58
  395. data/lib/badline/cartridge/simons_basic.rb +0 -35
  396. data/lib/badline/cartridge/standard.rb +0 -15
  397. data/lib/badline/cartridge/super_games.rb +0 -48
  398. data/lib/badline/cartridge/westermann.rb +0 -30
  399. data/lib/badline/cartridge/zaxxon.rb +0 -66
  400. data/lib/badline/cartridge.rb +0 -303
  401. data/lib/badline/checkpoint.rb +0 -98
  402. data/lib/badline/chrout_trap.rb +0 -43
  403. data/lib/badline/cia/fast_forward.rb +0 -60
  404. data/lib/badline/cia/interrupt_register.rb +0 -143
  405. data/lib/badline/cia/serial.rb +0 -275
  406. data/lib/badline/cia/state.rb +0 -47
  407. data/lib/badline/cia/timer.rb +0 -206
  408. data/lib/badline/cia.rb +0 -279
  409. data/lib/badline/color_memory.rb +0 -25
  410. data/lib/badline/computer/attachments.rb +0 -88
  411. data/lib/badline/computer/saved_state.rb +0 -117
  412. data/lib/badline/computer/true_drives.rb +0 -69
  413. data/lib/badline/control_ports.rb +0 -76
  414. data/lib/badline/cpu/addressing.rb +0 -131
  415. data/lib/badline/cpu/boundary.rb +0 -54
  416. data/lib/badline/cpu/core.rb +0 -165
  417. data/lib/badline/cpu/microcode.rb +0 -177
  418. data/lib/badline/cpu/operations.rb +0 -88
  419. data/lib/badline/cpu/saved_state.rb +0 -115
  420. data/lib/badline/cpu/stack_operations.rb +0 -82
  421. data/lib/badline/cpu.rb +0 -10
  422. data/lib/badline/cpu_port.rb +0 -112
  423. data/lib/badline/datasette.rb +0 -161
  424. data/lib/badline/debug_register.rb +0 -28
  425. data/lib/badline/drive/core.rb +0 -147
  426. data/lib/badline/drive/fast_serial.rb +0 -73
  427. data/lib/badline/drive/idle.rb +0 -125
  428. data/lib/badline/drive/idle_cpu.rb +0 -28
  429. data/lib/badline/drive/orbit.rb +0 -79
  430. data/lib/badline/drive/serial_lines.rb +0 -73
  431. data/lib/badline/drive/sleep.rb +0 -112
  432. data/lib/badline/drive/vias.rb +0 -84
  433. data/lib/badline/drive/watch.rb +0 -150
  434. data/lib/badline/drive.rb +0 -11
  435. data/lib/badline/drive1541/bus.rb +0 -86
  436. data/lib/badline/drive1541/byte_ready.rb +0 -48
  437. data/lib/badline/drive1541/cpu.rb +0 -11
  438. data/lib/badline/drive1541/disk.rb +0 -315
  439. data/lib/badline/drive1541/disk_state.rb +0 -86
  440. data/lib/badline/drive1541/disk_via.rb +0 -36
  441. data/lib/badline/drive1541/gcr.rb +0 -57
  442. data/lib/badline/drive1541/mechanism.rb +0 -313
  443. data/lib/badline/drive1541/mechanism_state.rb +0 -68
  444. data/lib/badline/drive1541/rotation.rb +0 -121
  445. data/lib/badline/drive1541/sector_reader.rb +0 -76
  446. data/lib/badline/drive1541/serial_port.rb +0 -18
  447. data/lib/badline/drive1541/track.rb +0 -98
  448. data/lib/badline/drive1541.rb +0 -111
  449. data/lib/badline/drive1571/bus.rb +0 -111
  450. data/lib/badline/drive1571/cpu.rb +0 -11
  451. data/lib/badline/drive1571/serial_port.rb +0 -37
  452. data/lib/badline/drive1571/serial_via.rb +0 -25
  453. data/lib/badline/drive1571/wd1770.rb +0 -120
  454. data/lib/badline/drive1571.rb +0 -175
  455. data/lib/badline/drive1581/bus.rb +0 -137
  456. data/lib/badline/drive1581/cpu.rb +0 -11
  457. data/lib/badline/drive1581/disk.rb +0 -135
  458. data/lib/badline/drive1581/disk_state.rb +0 -87
  459. data/lib/badline/drive1581/mechanism.rb +0 -117
  460. data/lib/badline/drive1581/serial_port.rb +0 -52
  461. data/lib/badline/drive1581/slot.rb +0 -78
  462. data/lib/badline/drive1581/track.rb +0 -100
  463. data/lib/badline/drive1581/track_bytes.rb +0 -75
  464. data/lib/badline/drive1581/wd1772/registers.rb +0 -89
  465. data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
  466. data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
  467. data/lib/badline/drive1581/wd1772/state.rb +0 -66
  468. data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
  469. data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
  470. data/lib/badline/drive1581/wd1772/turn.rb +0 -73
  471. data/lib/badline/drive1581/wd1772.rb +0 -216
  472. data/lib/badline/drive1581.rb +0 -233
  473. data/lib/badline/iec_bus.rb +0 -159
  474. data/lib/badline/instruction.rb +0 -334
  475. data/lib/badline/instruction_set/arithmetic.rb +0 -114
  476. data/lib/badline/instruction_set/bitwise.rb +0 -126
  477. data/lib/badline/instruction_set/branch.rb +0 -79
  478. data/lib/badline/instruction_set/flag.rb +0 -63
  479. data/lib/badline/instruction_set/illegal.rb +0 -270
  480. data/lib/badline/instruction_set/inc_dec.rb +0 -69
  481. data/lib/badline/instruction_set/stack.rb +0 -52
  482. data/lib/badline/instruction_set/transfer.rb +0 -137
  483. data/lib/badline/instruction_set.rb +0 -55
  484. data/lib/badline/interrupts.rb +0 -104
  485. data/lib/badline/joystick.rb +0 -25
  486. data/lib/badline/kernal_trap/channel.rb +0 -181
  487. data/lib/badline/keyboard.rb +0 -112
  488. data/lib/badline/keyboard_buffer.rb +0 -33
  489. data/lib/badline/ram_expansion/banking.rb +0 -30
  490. data/lib/badline/ram_expansion/plus256k.rb +0 -54
  491. data/lib/badline/ram_expansion/plus60k.rb +0 -43
  492. data/lib/badline/ram_expansion/unexpanded.rb +0 -28
  493. data/lib/badline/ram_expansion.rb +0 -21
  494. data/lib/badline/reu/dma/saved_state.rb +0 -50
  495. data/lib/badline/reu/dma.rb +0 -273
  496. data/lib/badline/reu/ram.rb +0 -107
  497. data/lib/badline/reu/saved_state.rb +0 -80
  498. data/lib/badline/reu/trigger.rb +0 -32
  499. data/lib/badline/reu.rb +0 -289
  500. data/lib/badline/sid/catch_up.rb +0 -120
  501. data/lib/badline/sid/decimator.rb +0 -64
  502. data/lib/badline/sid/envelope/state.rb +0 -68
  503. data/lib/badline/sid/envelope.rb +0 -239
  504. data/lib/badline/sid/filter.rb +0 -263
  505. data/lib/badline/sid/voice.rb +0 -77
  506. data/lib/badline/sid/waveform/combined.rb +0 -126
  507. data/lib/badline/sid/waveform/fast_forward.rb +0 -105
  508. data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
  509. data/lib/badline/sid/waveform/state.rb +0 -66
  510. data/lib/badline/sid/waveform.rb +0 -305
  511. data/lib/badline/sid.rb +0 -303
  512. data/lib/badline/snapshot/setup.rb +0 -75
  513. data/lib/badline/snapshot/state.rb +0 -189
  514. data/lib/badline/time_of_day.rb +0 -228
  515. data/lib/badline/traps.rb +0 -19
  516. data/lib/badline/via/control_lines.rb +0 -122
  517. data/lib/badline/via/fast_forward.rb +0 -53
  518. data/lib/badline/via/interrupt_register.rb +0 -67
  519. data/lib/badline/via/shift_register.rb +0 -180
  520. data/lib/badline/via/state.rb +0 -42
  521. data/lib/badline/via/timer1.rb +0 -131
  522. data/lib/badline/via/timer2.rb +0 -133
  523. data/lib/badline/via.rb +0 -266
  524. data/lib/badline/vic/bank.rb +0 -133
  525. data/lib/badline/vic/border_mask.rb +0 -86
  526. data/lib/badline/vic/collisions.rb +0 -115
  527. data/lib/badline/vic/color_patches.rb +0 -47
  528. data/lib/badline/vic/display_state.rb +0 -185
  529. data/lib/badline/vic/graphics_mode.rb +0 -164
  530. data/lib/badline/vic/graphics_shifter.rb +0 -207
  531. data/lib/badline/vic/register_log.rb +0 -81
  532. data/lib/badline/vic/registers.rb +0 -195
  533. data/lib/badline/vic/sequencer.rb +0 -275
  534. data/lib/badline/vic/sequencer_output.rb +0 -116
  535. data/lib/badline/vic/sequencer_state.rb +0 -89
  536. data/lib/badline/vic/sprite/internal_bus.rb +0 -68
  537. data/lib/badline/vic/sprite/saved_state.rb +0 -64
  538. data/lib/badline/vic/sprite/shifter.rb +0 -146
  539. data/lib/badline/vic/sprite/timing.rb +0 -56
  540. data/lib/badline/vic/sprite.rb +0 -337
  541. data/lib/badline/vic/sprites.rb +0 -282
  542. data/lib/badline/vic/sprites_state.rb +0 -57
  543. data/lib/badline/vic.rb +0 -853
  544. data/lib/badline/vic20/saved_state.rb +0 -124
  545. data/lib/badline/vic20/sound/output.rb +0 -83
  546. data/lib/badline/vic20/sound.rb +0 -233
  547. data/lib/badline/vic20/vic/saved_state.rb +0 -55
  548. data/lib/badline/z80/arithmetic.rb +0 -117
  549. data/lib/badline/z80/bit_page.rb +0 -55
  550. data/lib/badline/z80/block_io.rb +0 -58
  551. data/lib/badline/z80/block_transfers.rb +0 -59
  552. data/lib/badline/z80/bus_cycles.rb +0 -111
  553. data/lib/badline/z80/core.rb +0 -113
  554. data/lib/badline/z80/exchanges.rb +0 -50
  555. data/lib/badline/z80/extended_page.rb +0 -89
  556. data/lib/badline/z80/flags.rb +0 -37
  557. data/lib/badline/z80/interrupt_acceptance.rb +0 -46
  558. data/lib/badline/z80/jumps.rb +0 -82
  559. data/lib/badline/z80/loads.rb +0 -98
  560. data/lib/badline/z80/main_page.rb +0 -134
  561. data/lib/badline/z80/registers.rb +0 -120
  562. data/lib/badline/z80/saved_state.rb +0 -69
  563. data/lib/badline/z80/shifts.rb +0 -64
  564. data/lib/badline/z80.rb +0 -11
  565. /data/lib/badline/snapshot/{fields.rb → vice/fields.rb} +0 -0
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CIA
6
+ class Timer
7
+ attr_accessor :counter, :latch
8
+ attr_reader :control, :underflowed
9
+
10
+ def initialize(control)
11
+ @control = control
12
+ @counter = @latch = 0xffff
13
+ @pipe = 0
14
+ @load_delay = 0
15
+ @reload = false
16
+ @underflowed = false
17
+ @oneshot_linger = 0
18
+ @toggle = true
19
+ settle
20
+ end
21
+
22
+ # The level this timer drives on its port B pin: a square wave that
23
+ # flips on each underflow, or a single high tick when one happens.
24
+ def output?
25
+ control.out_mode? ? @toggle : @underflowed
26
+ end
27
+
28
+ # Whether the timer is stopped with nothing in its pipeline, so a
29
+ # cycle changes nothing.
30
+ def idle? = @empty && !started?
31
+
32
+ # Whether the timer counts ø2 in continuous mode with nothing pending
33
+ # but the reload after an underflow, so fast_forward can run it. A
34
+ # latch below 2 is left out.
35
+ def free_running?
36
+ started? && !@control.run_mode? && @control.value.nobits?(0x20) && @latch >= 2 && @pipe == 0b11 &&
37
+ (@load_delay | @oneshot_linger).zero? && (@settled || @reload)
38
+ end
39
+
40
+ # Runs a free-running timer +cycles+ cycles at once, as cycle! would:
41
+ # an underflow every latch + 1 cycles, each one followed by the
42
+ # reload. Returns the underflows.
43
+ def fast_forward(cycles)
44
+ return 0 if cycles.zero?
45
+ return after_underflow(cycles, 0) unless @settled
46
+
47
+ if cycles < @counter
48
+ @counter -= cycles
49
+ return 0
50
+ end
51
+ after_underflow(cycles - @counter, 1)
52
+ end
53
+
54
+ def cycle!(feed)
55
+ feed &&= @control.value & 0x01 != 0
56
+ if @settled
57
+ return @counter -= 1 if feed && @counter > 1
58
+ elsif @empty
59
+ enter if feed
60
+ return
61
+ end
62
+ run_tick(feed)
63
+ end
64
+
65
+ def run_tick(feed)
66
+ @underflowed = false
67
+ @oneshot_linger -= 1 if @oneshot_linger.positive?
68
+ tick(feed)
69
+ settle
70
+ end
71
+
72
+ def write_control(value)
73
+ @toggle = true if value.anybits?(0x01) && !started?
74
+ @oneshot_linger = 2 if control.run_mode? && value.nobits?(0x08)
75
+ control.value = value & ~0x10
76
+ @load_delay = 3 if value.anybits?(0x10)
77
+ settle
78
+ end
79
+
80
+ def write_latch_low(value)
81
+ @latch = (@latch & 0xff00) | value
82
+ end
83
+
84
+ def write_latch_high(value)
85
+ @latch = (value << 8) | (@latch & 0xff)
86
+ @counter = @latch unless started?
87
+ end
88
+
89
+ # Whether the timer's PB output toggle is high, and its count pipeline
90
+ # as two bits, the newer stage high.
91
+ attr_reader :toggle, :pipe
92
+
93
+ # Sets the control register without its load strobe, the toggle and
94
+ # the count pipeline, as a VICE snapshot gives them.
95
+ def restore_pipeline(control, toggle, pipe)
96
+ @control.value = control & ~0x10
97
+ @toggle = toggle
98
+ @pipe = pipe
99
+ settle
100
+ end
101
+
102
+ private
103
+
104
+ def started?
105
+ @control.value & 0x01 != 0
106
+ end
107
+
108
+ # Where fast_forward lands +cycles+ after an underflow, with
109
+ # +underflows+ underflows counted so far.
110
+ def after_underflow(cycles, underflows)
111
+ periods, rest = cycles.divmod(@latch + 1)
112
+ underflows += periods
113
+ @toggle = !@toggle if underflows.odd?
114
+ @underflowed = rest.zero?
115
+ @reload = rest.zero?
116
+ @counter = @latch - (rest.zero? ? 0 : rest - 1)
117
+ settle
118
+ underflows
119
+ end
120
+
121
+ def enter
122
+ @pipe = 0b10
123
+ @empty = false
124
+ end
125
+
126
+ # With no load, reload or one-shot linger pending, a full pipe only
127
+ # counts and an empty one only waits to be fed.
128
+ def settle
129
+ quiet = (@load_delay | @oneshot_linger).zero? && !@reload
130
+ @settled = quiet && @pipe == 0b11
131
+ @empty = quiet && @pipe.zero?
132
+ end
133
+
134
+ def tick(feed)
135
+ counting = @pipe.anybits?(0b01)
136
+ @pipe = (@pipe >> 1) | (feed ? 0b10 : 0)
137
+ loading = @reload || @load_delay == 1
138
+ @reload = false
139
+
140
+ if loading
141
+ reload
142
+ elsif premature_underflow?
143
+ underflow
144
+ elsif counting
145
+ count
146
+ end
147
+
148
+ forced_load
149
+ end
150
+
151
+ def forced_load
152
+ return unless @load_delay.positive?
153
+
154
+ @load_delay -= 1
155
+ @counter = @latch if @load_delay == 1
156
+ end
157
+
158
+ # the final pipeline stage, before any pending load lands
159
+ def premature_underflow?
160
+ @counter.zero? && @pipe.anybits?(0b01) && started?
161
+ end
162
+
163
+ # A load consumes its tick, so the counter never decrements on it
164
+ def reload
165
+ @counter = @latch
166
+ # A zero latch underflows again on the reload tick while running
167
+ underflow if @counter.zero? && started? && @pipe.anybits?(0b01)
168
+ end
169
+
170
+ def count
171
+ @counter -= 1 if @counter.positive?
172
+ underflow if @counter.zero? && @pipe.anybits?(0b01)
173
+ end
174
+
175
+ def underflow
176
+ @underflowed = true
177
+ @reload = true
178
+ @counter = @latch
179
+ @toggle = !@toggle
180
+ return unless control.run_mode? || @oneshot_linger.positive?
181
+
182
+ control.start = false
183
+ @pipe = 0
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,281 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/cia/time_of_day"
4
+ require "badline/chips/cia/interrupt_register"
5
+ require "badline/chips/cia/serial"
6
+ require "badline/chips/cia/timer"
7
+ require "badline/chips/cia/fast_forward"
8
+ require "badline/chips/cia/keyed_parts"
9
+
10
+ module Badline
11
+ module Chips
12
+ # CIA (Complex Interface Adapter) chip
13
+ class CIA
14
+ include Addressable
15
+ include FastForward
16
+
17
+ # The original NMOS 6526 of the breadbin C64, and the HMOS 6526A of the
18
+ # C64C, which differs from it only in its interrupt control register.
19
+ MODELS = %i[mos6526 mos6526a].freeze
20
+
21
+ attr_reader :start, :control_a, :control_b, :serial, :model
22
+
23
+ # What's on the ports outside the chip. It pulls port lines low through
24
+ # read_a and read_b, and on CIA 1 drives PB4 through port_b4_high?.
25
+ attr_accessor :peripheral
26
+
27
+ # Timer A and timer B.
28
+ def timers = [@ta, @tb]
29
+
30
+ # Port A and B's data and direction registers.
31
+ def port_registers = [@data_port_a, @data_port_b, @data_dir_a, @data_dir_b]
32
+
33
+ def interrupt_status = @icr.status
34
+
35
+ def interrupt_control = @icr.mask
36
+
37
+ def interrupt!(delay = 1) = @icr.assert!(delay)
38
+
39
+ def time_of_day = @tod
40
+
41
+ def timer_a = @ta.counter
42
+
43
+ def timer_a=(value)
44
+ @ta.counter = value
45
+ end
46
+
47
+ def timer_a_latch = @ta.latch
48
+
49
+ def timer_a_latch=(value)
50
+ @ta.latch = value
51
+ end
52
+
53
+ def timer_b = @tb.counter
54
+
55
+ def timer_b=(value)
56
+ @tb.counter = value
57
+ end
58
+
59
+ def timer_b_latch = @tb.latch
60
+
61
+ def timer_b_latch=(value)
62
+ @tb.latch = value
63
+ end
64
+
65
+ # +tod_hz+ is the rate of the pulses on the TOD pin, the region's mains
66
+ # unless a machine feeds it otherwise, and 0 for a pin held still.
67
+ def initialize(start: 0, peripheral: nil, model: :mos6526, region: Region::PAL, tod_hz: nil)
68
+ raise ArgumentError, "unknown CIA model #{model}" unless MODELS.include?(model)
69
+
70
+ addressable_at(start, length: 2**8)
71
+
72
+ @model = model
73
+ @clock_hz = region.clock_hz
74
+ @mains_hz = tod_hz || region.mains_hz
75
+
76
+ @peripheral = peripheral
77
+ @port_b4_handler = nil
78
+ @port_a_handler = nil
79
+ @port_b4_high = true
80
+ reset!
81
+ end
82
+
83
+ # The RES line: ports back to inputs, timers stopped with their latches
84
+ # full, interrupts masked and the TOD clock stopped at 1:00:00.0. The
85
+ # PB4 level carries over and is resampled on the next cycle.
86
+ def reset!
87
+ @data_port_a = 0x00
88
+ @data_port_b = 0x00
89
+ @data_dir_a = 0x00
90
+ @data_dir_b = 0x00
91
+ @port_b4_driven_high = true
92
+ @cnt_high = true
93
+ @cnt_rise = false
94
+ @tod = TimeOfDay.new(clock_hz: @clock_hz, mains_hz: @mains_hz)
95
+ @icr = InterruptRegister.new(@model)
96
+ @icr_status = @icr.status
97
+ @control_a = ControlRegister.new(%i[start output out_mode run_mode load
98
+ in_mode serial_mode clock_frequency])
99
+ @control_b = ControlRegister.new(%i[start output out_mode run_mode load
100
+ in_cnt in_timer_a alarm])
101
+ @ta = Timer.new(@control_a)
102
+ @tb = Timer.new(@control_b)
103
+ @serial = Serial.new(@control_a)
104
+ @port_a_handler&.call
105
+ end
106
+
107
+ # Register a change handler on the PB4 line. On CIA 1, this feeds the
108
+ # light pen input.
109
+ def on_port_b4_change(&handler)
110
+ @port_b4_handler = handler
111
+ end
112
+
113
+ # Register a handler for writes that can move port A's lines: to the
114
+ # data register or DDR, and the reset. On CIA 2, this tells the drives
115
+ # on the serial bus that the C64 may have moved a line.
116
+ def on_port_a_write(&handler)
117
+ @port_a_handler = handler
118
+ end
119
+
120
+ # A falling edge on the FLAG pin. On CIA 1 the datasette's tape read
121
+ # line drives it, on CIA 2 the serial bus SRQ.
122
+ def flag! = raise_interrupt(:flag)
123
+
124
+ def interrupted? = @icr_status.value >= 0x80
125
+
126
+ def cycle!
127
+ @icr.cycle! unless @icr.quiet
128
+ refresh_port_b4 if @port_b4_handler
129
+ level = @serial.cnt
130
+ level == @cnt_high ? @cnt_rise = false : cnt_edge(level)
131
+ underflowed = update_timers
132
+ @serial.cycle!(underflowed) { trigger_serial } if underflowed || !@serial.idle
133
+ @tod.cycle! { trigger_alarm }
134
+ end
135
+
136
+ def read_port_a
137
+ lines = driven_lines(@data_port_a, @data_dir_a)
138
+ return lines unless peripheral
139
+
140
+ lines & peripheral.read_a(lines, driven_lines(@data_port_b, @data_dir_b))
141
+ end
142
+
143
+ # Port A as driven by the data/direction registers alone, without
144
+ # peripheral pulldown. Cheap path for the VIC bank lookup.
145
+ def port_a_lines = driven_lines(@data_port_a, @data_dir_a)
146
+
147
+ # Port B as driven by the data/direction registers alone.
148
+ def port_b_lines = driven_lines(@data_port_b, @data_dir_b)
149
+
150
+ def read_port_b
151
+ lines = driven_lines(@data_port_b, @data_dir_b)
152
+ lines &= peripheral.read_b(driven_lines(@data_port_a, @data_dir_a), lines) if peripheral
153
+ apply_timer_output(lines)
154
+ end
155
+
156
+ def peek(addr)
157
+ case offset_of(addr) & 0x0f
158
+ when 0x00 then read_port_a
159
+ when 0x01 then read_port_b
160
+ when 0x02 then @data_dir_a
161
+ when 0x03 then @data_dir_b
162
+ when 0x04 then low_byte(@ta.counter)
163
+ when 0x05 then high_byte(@ta.counter)
164
+ when 0x06 then low_byte(@tb.counter)
165
+ when 0x07 then high_byte(@tb.counter)
166
+ when 0x08 then @tod.tenths
167
+ when 0x09 then @tod.seconds
168
+ when 0x0a then @tod.minutes
169
+ when 0x0b then @tod.hours
170
+ when 0x0c then @serial.data
171
+ when 0x0d then @icr.read
172
+ when 0x0e then control_a.value
173
+ when 0x0f then control_b.value
174
+ end
175
+ end
176
+
177
+ def poke(addr, value)
178
+ case offset_of(addr) & 0x0f
179
+ when 0x00 then update_port_a { @data_port_a = value }
180
+ when 0x01 then update_port_b { @data_port_b = value }
181
+ when 0x02 then update_port_a { @data_dir_a = value }
182
+ when 0x03 then update_port_b { @data_dir_b = value }
183
+ when 0x04 then @ta.write_latch_low(value)
184
+ when 0x05 then @ta.write_latch_high(value)
185
+ when 0x06 then @tb.write_latch_low(value)
186
+ when 0x07 then @tb.write_latch_high(value)
187
+ when 0x08 then @tod.write(:tenths, value, alarm: control_b.alarm?)
188
+ when 0x09 then @tod.write(:seconds, value, alarm: control_b.alarm?)
189
+ when 0x0a then @tod.write(:minutes, value, alarm: control_b.alarm?)
190
+ when 0x0b then @tod.write_hours(value, alarm: control_b.alarm?)
191
+ when 0x0c then @serial.write(value)
192
+ when 0x0d then @icr.write(value)
193
+ when 0x0e then write_control_a(value)
194
+ when 0x0f then @tb.write_control(value)
195
+ end
196
+ end
197
+
198
+ private
199
+
200
+ def update_port_a
201
+ yield
202
+ @port_a_handler&.call
203
+ end
204
+
205
+ def update_port_b
206
+ yield
207
+ @port_b4_driven_high = driven_lines(@data_port_b, @data_dir_b).anybits?(0x10)
208
+ refresh_port_b4 if @port_b4_handler
209
+ end
210
+
211
+ # PB4 is also control port 1's fire line, and a peripheral pulls it low
212
+ # without any register write, so the level is resampled every cycle rather
213
+ # than only after a poke.
214
+ def refresh_port_b4
215
+ high = @port_b4_driven_high && (peripheral.nil? || peripheral.port_b4_high?)
216
+ return if high == @port_b4_high
217
+
218
+ @port_b4_high = high
219
+ @port_b4_handler.call(high)
220
+ end
221
+
222
+ def driven_lines(register, direction)
223
+ # Output bits are driven from the data register; input bits float high.
224
+ # External peripherals can still pull any line low (wired-AND).
225
+ (register & direction) | (~direction & 0xff)
226
+ end
227
+
228
+ def apply_timer_output(value)
229
+ value = with_bit(value, 6, @ta.output?) if control_a.output?
230
+ value = with_bit(value, 7, @tb.output?) if control_b.output?
231
+ value
232
+ end
233
+
234
+ def with_bit(value, bit, set)
235
+ set ? value | (1 << bit) : value & ~(1 << bit)
236
+ end
237
+
238
+ def trigger_alarm = raise_interrupt(:alarm)
239
+ def trigger_serial = raise_interrupt(:serial)
240
+
241
+ def raise_interrupt(source) = @icr.flag(source)
242
+
243
+ # CNT is sampled once a cycle. When the serial port drives it from this
244
+ # cycle's timer A underflow, the new level is picked up on the next one.
245
+ def cnt_edge(level)
246
+ @cnt_high = level
247
+ @cnt_rise = level
248
+ @serial.rising_edge! { trigger_serial } if level
249
+ end
250
+
251
+ # Returns whether timer A underflowed, which clocks the serial port.
252
+ #
253
+ # CRB bits 6-5 pick timer B's source: ø2, CNT edges, timer A
254
+ # underflows, or timer A underflows gated by the CNT level. Every source
255
+ # drives the same count-enable line, so a cascaded underflow goes through
256
+ # the input pipeline exactly as a CNT edge does.
257
+ def update_timers
258
+ @ta.cycle!(@control_a.value & 0x20 == 0x20 ? @cnt_rise : true)
259
+ underflowed = @ta.underflowed
260
+ @tb.cycle!(
261
+ case @control_b.value & 0x60
262
+ when 0x00 then true
263
+ when 0x20 then @cnt_rise
264
+ when 0x40 then underflowed
265
+ else underflowed && @cnt_high
266
+ end
267
+ )
268
+ @icr.flag(:timer_a) if underflowed
269
+ @icr.timer_b_underflow! if @tb.underflowed
270
+ underflowed
271
+ end
272
+
273
+ def write_control_a(value)
274
+ was_output = control_a.serial_mode?
275
+ @ta.write_control(value)
276
+ @serial.reset! { trigger_serial } if control_a.serial_mode? != was_output
277
+ @tod.fifty_hz = control_a.clock_frequency?
278
+ end
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ # Steps that read an instruction's operand bytes and work out the
7
+ # address it operates on, which they leave in @address. Also the
8
+ # single-step implied, accumulator and immediate instructions, and JMP.
9
+ module Addressing
10
+ private
11
+
12
+ def op_implied_dummy
13
+ @memory.peek(@program_counter)
14
+ end
15
+
16
+ def op_implied_exec
17
+ @memory.peek(@program_counter)
18
+ send(@operation, nil, nil)
19
+ end_instruction
20
+ end
21
+
22
+ def op_accumulator_exec
23
+ @memory.peek(@program_counter)
24
+ send(@operation, :accumulator, @a)
25
+ end_instruction
26
+ end
27
+
28
+ def op_immediate_read
29
+ value = @memory.peek(@program_counter)
30
+ @program_counter = (@program_counter + 1) & 0xffff
31
+ send(@operation, nil, value)
32
+ end_instruction
33
+ end
34
+
35
+ def op_zp
36
+ @address = @memory.peek(@program_counter)
37
+ @program_counter = (@program_counter + 1) & 0xffff
38
+ end
39
+
40
+ def op_zp_x
41
+ @memory.peek(@address)
42
+ @address = (@address + @x) & 0xff
43
+ end
44
+
45
+ def op_zp_y
46
+ @memory.peek(@address)
47
+ @address = (@address + @y) & 0xff
48
+ end
49
+
50
+ def op_abs_low
51
+ @address = @memory.peek(@program_counter)
52
+ @program_counter = (@program_counter + 1) & 0xffff
53
+ end
54
+
55
+ def op_abs_high
56
+ @address |= @memory.peek(@program_counter) << 8
57
+ @program_counter = (@program_counter + 1) & 0xffff
58
+ end
59
+
60
+ def op_abs_high_x
61
+ high = @memory.peek(@program_counter)
62
+ @program_counter = (@program_counter + 1) & 0xffff
63
+ index_address(high, @x)
64
+ end
65
+
66
+ def op_abs_high_y
67
+ high = @memory.peek(@program_counter)
68
+ @program_counter = (@program_counter + 1) & 0xffff
69
+ index_address(high, @y)
70
+ end
71
+
72
+ def op_idx_dummy
73
+ @memory.peek(@dummy_address)
74
+ end
75
+
76
+ def op_zp_pointer
77
+ @pointer = @memory.peek(@program_counter)
78
+ @program_counter = (@program_counter + 1) & 0xffff
79
+ end
80
+
81
+ def op_pointer_x
82
+ @memory.peek(@pointer)
83
+ @pointer = (@pointer + @x) & 0xff
84
+ end
85
+
86
+ def op_pointer_low
87
+ @address = @memory.peek(@pointer)
88
+ end
89
+
90
+ def op_pointer_high
91
+ @address |= @memory.peek((@pointer + 1) & 0xff) << 8
92
+ end
93
+
94
+ def op_pointer_high_y
95
+ index_address(@memory.peek((@pointer + 1) & 0xff), @y)
96
+ end
97
+
98
+ def op_jmp_high
99
+ @program_counter = @address | (@memory.peek(@program_counter) << 8)
100
+ end_instruction
101
+ end
102
+
103
+ def op_indirect_low
104
+ @pointer = @address
105
+ @address = @memory.peek(@pointer)
106
+ end
107
+
108
+ # The pointer's high byte never increments, so JMP ($30FF) reads its
109
+ # target's high byte from $3000, not $3100.
110
+ def op_indirect_high
111
+ high = @memory.peek((@pointer & 0xff00) | ((@pointer + 1) & 0xff))
112
+ @program_counter = @address | (high << 8)
113
+ end_instruction
114
+ end
115
+
116
+ # Adds an index register to a base address. The 6502 adds to the low
117
+ # byte first and spends the next cycle reading from that address,
118
+ # before the carry reaches the high byte. Read instructions skip that
119
+ # cycle when there is no carry.
120
+ def index_address(high, index)
121
+ low = @address
122
+ @address = (((high << 8) | low) + index) & 0xffff
123
+ @boundary_crossed = (low + index) > 0xff
124
+ if @optional_dummy && !@boundary_crossed
125
+ @index += 1
126
+ else
127
+ @dummy_address = (high << 8) | ((low + index) & 0xff)
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ # The steps at an instruction boundary: the opcode fetch that starts
7
+ # an instruction, and the ends of one.
8
+ module Boundary
9
+ private
10
+
11
+ # The first cycle of every instruction. Starts an interrupt if one was
12
+ # pending when the previous instruction ended. Otherwise reads the
13
+ # opcode and switches to its plan.
14
+ def op_fetch
15
+ return start_interrupt if @boundary_nmi || @boundary_irq
16
+
17
+ run_traps
18
+ opcode = @memory.peek(@program_counter)
19
+ log(opcode) if @debug
20
+ micro = MICROCODE[opcode]
21
+ @operation = micro.operation
22
+ @plan = micro.plan
23
+ @writes = micro.writes
24
+ @optional_dummy = micro.optional_dummy
25
+ @index = 1
26
+ @boundary_crossed = false
27
+ @program_counter = (@program_counter + 1) & 0xffff
28
+ end
29
+
30
+ def end_instruction
31
+ @instructions += 1
32
+ end_sequence
33
+ end
34
+
35
+ # Makes the next cycle an opcode fetch, and saves the pending interrupt
36
+ # flags for op_fetch. The next cycle polls before op_fetch runs, which
37
+ # updates the live flags.
38
+ def end_sequence
39
+ @plan = FETCH_PLAN
40
+ @writes = FETCH_WRITES
41
+ @index = 0
42
+ @boundary_irq = @irq_pending
43
+ @boundary_nmi = @nmi_pending
44
+ end
45
+
46
+ def log(opcode)
47
+ instruction = Instruction.find(opcode)
48
+ puts(
49
+ "#{@cycles}: PC: #{format16(@program_counter)} - " \
50
+ "#{instruction.name.upcase} #{instruction.addressing_mode}"
51
+ )
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end