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,312 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ # The CPU's bus:
6
+ #
7
+ # $0000-$0001 - the 7501's port (CPUPort), over RAM
8
+ # $0002-$7FFF - RAM
9
+ # $8000-$BFFF - the low ROM bank, or RAM
10
+ # $C000-$FCFF - the high ROM bank, or RAM; $FC00-$FCFF is always the
11
+ # KERNAL's while ROM is selected
12
+ # $FD00-$FD0F - the 6551 ACIA (Plus/4 only)
13
+ # $FD10-$FD1F - the user port's 6529 (Plus/4 only)
14
+ # $FD30-$FD3F - the keyboard's 6529 (KeyboardPort)
15
+ # $FDD0-$FDDF - the ROM bank latch
16
+ # $FF00-$FF1F - the TED
17
+ # $FF3E/$FF3F - a write selects ROM or RAM at $8000-$FFFF
18
+ # $FF40-$FFFF - the high ROM bank, or RAM
19
+ #
20
+ # A write to $FF3E selects ROM and one to $FF3F RAM, everywhere in
21
+ # $8000-$FFFF but $FD00-$FF3F, which holds I/O either way. ROM is
22
+ # selected at power-on and reset. A write to ROM goes to the RAM under
23
+ # it. The ROM bank latch takes the address written to, not the byte:
24
+ # bits 0-1 pick the low bank and bits 2-3 the high bank, 0 for BASIC
25
+ # and the KERNAL, 1 for the Plus/4's function ROMs, 2 and 3 for the two
26
+ # cartridges.
27
+ #
28
+ # RAM smaller than 64K repeats through the 64K, as the C16's 16K does
29
+ # without A14 and A15. Where nothing answers, a read finds the last byte
30
+ # on the bus.
31
+ class Bus
32
+ include Addressable
33
+
34
+ RAM_POWER_ON = 0x00
35
+
36
+ # RAM through a size it repeats at.
37
+ class MirroredRAM
38
+ def initialize(ram, mask)
39
+ @ram = ram
40
+ @mask = mask
41
+ end
42
+
43
+ def peek(addr) = @ram.peek(addr & @mask)
44
+
45
+ def poke(addr, value)
46
+ @ram.poke(addr & @mask, value)
47
+ end
48
+ end
49
+
50
+ # The last byte the CPU moved, which an empty spot reads back.
51
+ class OpenBus
52
+ def initialize(bus)
53
+ @bus = bus
54
+ end
55
+
56
+ def peek(_addr) = @bus.data
57
+ def poke(_addr, _value); end
58
+ end
59
+
60
+ # Page 0: the port at $00 and $01, RAM above it.
61
+ class ZeroPage
62
+ def initialize(port, ram)
63
+ @port = port
64
+ @ram = ram
65
+ end
66
+
67
+ def peek(addr) = addr < 2 ? @port.peek(addr) : @ram.peek(addr)
68
+
69
+ def poke(addr, value)
70
+ return @port.poke(addr, value) if addr < 2
71
+
72
+ @ram.poke(addr, value)
73
+ end
74
+ end
75
+
76
+ # Page $FD: the ACIA, the user port, the keyboard port and the ROM
77
+ # bank latch, by the address's bits 4-7. +acia+ and +user_port+ are
78
+ # nil where the machine has none.
79
+ class IOPage
80
+ def initialize(keyboard_port:, acia: nil, user_port: nil)
81
+ @bus = nil
82
+ @acia = acia
83
+ @user_port = user_port
84
+ @keyboard_port = keyboard_port
85
+ end
86
+
87
+ # The bus whose last byte an empty spot reads and whose bank latch
88
+ # a write to $FDD0-$FDDF sets.
89
+ def connect(bus)
90
+ @bus = bus
91
+ end
92
+
93
+ def peek(addr)
94
+ case addr & 0xf0
95
+ when 0x00 then @acia ? @acia.peek(addr) : @bus.data
96
+ when 0x10 then @user_port ? @user_port.peek(addr) : @bus.data
97
+ when 0x30 then @keyboard_port.peek(addr)
98
+ else @bus.data
99
+ end
100
+ end
101
+
102
+ def poke(addr, value)
103
+ case addr & 0xf0
104
+ when 0x00 then @acia&.poke(addr, value)
105
+ when 0x10 then @user_port&.poke(addr, value)
106
+ when 0x30 then @keyboard_port.poke(addr, value)
107
+ when 0xd0 then @bus.select_banks(addr)
108
+ end
109
+ end
110
+ end
111
+
112
+ # VICE's debug cartridge register at $FDCF, a spot in the I/O page
113
+ # that nothing answers (plus4world forum post 31417). A write there
114
+ # calls the handler with the byte, the exit code the VICE testprogs
115
+ # report. Every other access falls through to the I/O page.
116
+ class DebugRegister
117
+ ADDRESS = 0xfdcf
118
+
119
+ def initialize(io, &handler)
120
+ @io = io
121
+ @handler = handler
122
+ end
123
+
124
+ def peek(addr) = @io.peek(addr)
125
+
126
+ def poke(addr, value)
127
+ return @handler.call(value) if addr == ADDRESS
128
+
129
+ @io.poke(addr, value)
130
+ end
131
+ end
132
+
133
+ # Page $FE with a 1551's TCBM interface plugged in: the 6523 TIA
134
+ # that talks to drive 9 at $FEC0-$FEDF and drive 8's at $FEE0-$FEFF.
135
+ # The rest of the page is open.
136
+ class TCBMPage
137
+ def initialize(bus, drive8:, drive9:)
138
+ @bus = bus
139
+ @drive8 = drive8
140
+ @drive9 = drive9
141
+ end
142
+
143
+ def peek(addr)
144
+ return @bus.data if addr < 0xfec0
145
+
146
+ addr < 0xfee0 ? @drive9.peek(addr) : @drive8.peek(addr)
147
+ end
148
+
149
+ def poke(addr, value)
150
+ return if addr < 0xfec0
151
+
152
+ addr < 0xfee0 ? @drive9.poke(addr, value) : @drive8.poke(addr, value)
153
+ end
154
+ end
155
+
156
+ # Page $FF: the TED in $FF00-$FF3F, its two phantom registers, and
157
+ # ROM or RAM above.
158
+ class TopPage
159
+ def initialize(bus, ted)
160
+ @bus = bus
161
+ @ted = ted
162
+ end
163
+
164
+ def peek(addr)
165
+ return @ted.peek(addr) if addr < 0xff40
166
+
167
+ @bus.top_memory.peek(addr)
168
+ end
169
+
170
+ def poke(addr, value)
171
+ return @bus.ram_view.poke(addr, value) if addr >= 0xff40
172
+
173
+ case addr
174
+ when 0xff3e then @bus.rom_selected = true
175
+ when 0xff3f then @bus.rom_selected = false
176
+ else @ted.poke(addr, value)
177
+ end
178
+ end
179
+ end
180
+
181
+ attr_reader :ram, :ram_view, :data, :low_bank, :high_bank
182
+
183
+ # What the top page reads above $FF3F: the high ROM bank or RAM.
184
+ attr_reader :top_memory
185
+
186
+ # The CPU sees ROM at $8000-$FFFF.
187
+ attr_reader :rom_selected
188
+
189
+ # +io+ is the IOPage, +model+ a Model profile, whose KERNAL it maps
190
+ # and whose function ROMs it maps where the model has them, and
191
+ # +ram_kb+ the RAM fitted.
192
+ def initialize(ted:, port:, io:, model: Model::PLUS4, ram_kb: model.ram_kb)
193
+ addressable_at(0, length: 2**16)
194
+ size = ram_kb * 1024
195
+ @ram = Memory.new(Array.new(size, RAM_POWER_ON), length: size, start: 0)
196
+ @ram_view = MirroredRAM.new(@ram, size - 1)
197
+ @ted = ted
198
+ @open_bus = OpenBus.new(self)
199
+ @low_roms = [ROM.load("plus4/basic.rom", 0x8000), nil, nil, nil]
200
+ @high_roms = [ROM.load(model.kernal, 0xc000), nil, nil, nil]
201
+ if model.plus4_parts
202
+ @low_roms[1] = ROM.load("plus4/function-low.rom", 0x8000)
203
+ @high_roms[1] = ROM.load("plus4/function-high.rom", 0xc000)
204
+ end
205
+ @zero_page = ZeroPage.new(port, @ram_view)
206
+ @io_page = io
207
+ io.connect(self)
208
+ @top_page = TopPage.new(self, ted)
209
+ @tcbm_page = @open_bus
210
+ @debug_register = nil
211
+ @data = 0
212
+ @read_pages = Array.new(256, @open_bus)
213
+ @write_pages = Array.new(256, @ram_view)
214
+ @low_bank = @high_bank = 0
215
+ @rom_selected = true
216
+ @top_memory = @ram_view
217
+ map_writes
218
+ map_reads
219
+ end
220
+
221
+ def power_on!
222
+ @ram.clear!(Array.new(@ram.length, RAM_POWER_ON))
223
+ reset!
224
+ end
225
+
226
+ # The RESET line selects ROM and the first bank of each.
227
+ def reset!
228
+ @low_bank = @high_bank = 0
229
+ self.rom_selected = true
230
+ end
231
+
232
+ def rom_selected=(selected)
233
+ @rom_selected = selected
234
+ @ted.rom_selected = selected
235
+ map_reads
236
+ end
237
+
238
+ # Whether the CPU reads the KERNAL at $C000-$FFFF: ROM selected, with
239
+ # the KERNAL's bank, high bank 0.
240
+ def kernal_selected? = @rom_selected && @high_bank.zero?
241
+
242
+ # A write to the ROM bank latch at +addr+.
243
+ def select_banks(addr)
244
+ @low_bank = addr & 0x03
245
+ @high_bank = (addr >> 2) & 0x03
246
+ map_reads
247
+ end
248
+
249
+ # Calls the block with each byte written to $FDCF (DebugRegister).
250
+ def install_debug_register(&)
251
+ @debug_register = DebugRegister.new(@io_page, &)
252
+ map_writes
253
+ end
254
+
255
+ # Plugs in a 1551's TCBM interface, the TIAs at $FEC0-$FEFF.
256
+ def plug_tcbm(drive8:, drive9:)
257
+ @tcbm_page = TCBMPage.new(self, drive8:, drive9:)
258
+ map_writes
259
+ map_reads
260
+ end
261
+
262
+ # Puts a cartridge's ROM chips in the slots of cartridge 1: a chip at
263
+ # $8000 in low bank 2 and one at $C000 in high bank 2, each repeated
264
+ # through the bank's 16K.
265
+ def attach_cartridge(chips)
266
+ chips.each do |chip|
267
+ data = chip.data * (0x4000 / chip.data.length)
268
+ if chip.address < 0xc000
269
+ @low_roms[2] = ROM.new(data, length: 0x4000, start: 0x8000)
270
+ else
271
+ @high_roms[2] = ROM.new(data, length: 0x4000, start: 0xc000)
272
+ end
273
+ end
274
+ map_reads
275
+ end
276
+
277
+ def peek(addr)
278
+ @data = @read_pages[addr >> 8].peek(addr)
279
+ end
280
+
281
+ def poke(addr, value)
282
+ @data = value
283
+ @write_pages[addr >> 8].poke(addr, value)
284
+ end
285
+
286
+ private
287
+
288
+ def map_writes
289
+ @write_pages[0x00] = @zero_page
290
+ @write_pages[0xfd] = @debug_register || @io_page
291
+ @write_pages[0xfe] = @tcbm_page
292
+ @write_pages[0xff] = @top_page
293
+ end
294
+
295
+ def map_reads
296
+ @read_pages.fill(@ram_view, 0x00, 0xfd)
297
+ @read_pages[0x00] = @zero_page
298
+ map_roms if @rom_selected
299
+ @top_memory = @rom_selected ? @high_roms[@high_bank] || @open_bus : @ram_view
300
+ @read_pages[0xfd] = @io_page
301
+ @read_pages[0xfe] = @tcbm_page
302
+ @read_pages[0xff] = @top_page
303
+ end
304
+
305
+ def map_roms
306
+ @read_pages.fill(@low_roms[@low_bank] || @open_bus, 0x80, 0x40)
307
+ @read_pages.fill(@high_roms[@high_bank] || @open_bus, 0xc0, 0x3c)
308
+ @read_pages[0xfc] = @high_roms[0]
309
+ end
310
+ end
311
+ end
312
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ # The 7501 or 8501, a 6510 core without an NMI pin, on the Plus4::Bus.
6
+ class CPU
7
+ include Badline::Chips::CPU::Core
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ # The 7501's I/O port at $00 (direction) and $01 (data). Its seven
6
+ # pins carry the serial bus and the cassette port, and none of them
7
+ # banks memory:
8
+ #
9
+ # - P0, P1 and P2 drive the serial bus's DATA, CLK and ATN through
10
+ # inverters, so a 1 pulls the line low, and P1 is the cassette write
11
+ # line too.
12
+ # - P3 runs the cassette motor while it is low.
13
+ # - P4 reads the cassette read line, P6 the serial CLK line and P7 the
14
+ # DATA line, 1 while the line is high.
15
+ # - P5 has no pin and reads 1.
16
+ #
17
+ # An output pin reads back what it drives. A pin left as an input reads
18
+ # what drives it from outside, and the serial outputs, P0-P2, float high
19
+ # there, so they pull their lines, as an output of 1 does.
20
+ class CPUPort
21
+ DATA_OUT = 0x01
22
+ CLK_OUT = 0x02
23
+ ATN_OUT = 0x04
24
+ MOTOR = 0x08
25
+ TAPE_READ = 0x10
26
+ CLK_IN = 0x40
27
+ DATA_IN = 0x80
28
+
29
+ attr_reader :ddr, :data
30
+
31
+ def initialize(serial_bus:, datasette:)
32
+ @serial_bus = serial_bus
33
+ @datasette = datasette
34
+ @ddr = 0
35
+ @data = 0
36
+ end
37
+
38
+ def reset!
39
+ @ddr = 0
40
+ @data = 0
41
+ changed
42
+ end
43
+
44
+ def peek(addr)
45
+ return @ddr if addr.zero?
46
+
47
+ (@data & @ddr) | (inputs & ~@ddr & 0xff)
48
+ end
49
+
50
+ def poke(addr, value)
51
+ if addr.zero?
52
+ @ddr = value
53
+ else
54
+ @data = value
55
+ end
56
+ changed
57
+ end
58
+
59
+ # The pins' levels as they leave the chip, inputs floating high.
60
+ def levels = (@data & @ddr) | (~@ddr & 0xff)
61
+
62
+ # The serial bus lines the port pulls low, as Drives::SerialBus host
63
+ # lines.
64
+ def serial_lines
65
+ out = levels
66
+ lines = 0
67
+ lines |= Drives::SerialBus::HOST_ATN_OUT if out.anybits?(ATN_OUT)
68
+ lines |= Drives::SerialBus::HOST_CLK_OUT if out.anybits?(CLK_OUT)
69
+ lines |= Drives::SerialBus::HOST_DATA_OUT if out.anybits?(DATA_OUT)
70
+ lines
71
+ end
72
+
73
+ private
74
+
75
+ def inputs
76
+ value = 0x2f
77
+ value |= TAPE_READ if @datasette.read_high?
78
+ low = @serial_bus.low_lines
79
+ value |= CLK_IN unless low.anybits?(Drives::SerialBus::CLK)
80
+ value |= DATA_IN unless low.anybits?(Drives::SerialBus::DATA)
81
+ value
82
+ end
83
+
84
+ def changed
85
+ lines = serial_lines
86
+ @serial_bus.host_lines = lines unless lines == @serial_bus.host_lines
87
+ out = levels
88
+ @datasette.motor = out.nobits?(MOTOR)
89
+ @datasette.write_line = out.anybits?(CLK_OUT)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ # The keyboard: the 6529 at $FD30-$FD3F, whose eight outputs drive the
6
+ # key matrix's lines, and the TED's K0-K7 inputs, which $FF08 latches
7
+ # (Chips::TED#keyboard_port=). The KERNAL's scan ($DB11) writes each
8
+ # line's mask to $FD30 and $FF08 and reads the keys back from $FF08,
9
+ # and its key table at $E026 puts each key at line times 8 plus input
10
+ # bit.
11
+ #
12
+ # The 6529 is a quasi-bidirectional latch: a read returns what was
13
+ # written to it.
14
+ class KeyboardPort
15
+ # One row of keys per 6529 output, in K0-K7 order, after the
16
+ # KERNAL's key table. HELP is the KERNAL's F7, and the two SHIFT keys
17
+ # share one place.
18
+ MATRIX = [
19
+ %i[delete return £ help f1 f2 f3 @],
20
+ %i[3 w a 4 z s e lshift],
21
+ %i[5 r d 6 c f t x],
22
+ %i[7 y g 8 b h u v],
23
+ %i[9 i j 0 m k o n],
24
+ %i[crsr_down p l crsr_up . : - ,],
25
+ %i[crsr_left * ; crsr_right esc = + /],
26
+ %i[1 clr_home control 2 space cbm q run_stop]
27
+ ].freeze
28
+
29
+ attr_reader :keyboard, :output
30
+
31
+ def initialize(keyboard)
32
+ @keyboard = keyboard
33
+ @output = 0xff
34
+ end
35
+
36
+ def reset!
37
+ @output = 0xff
38
+ end
39
+
40
+ def peek(_addr) = @output
41
+
42
+ def poke(_addr, value)
43
+ @output = value
44
+ end
45
+
46
+ # The K0-K7 inputs the TED latches on a write of +_value+ to $FF08:
47
+ # high but where a held key joins a line the 6529 pulls low.
48
+ def latch(_value) = @keyboard.scan(@output, 0xff).last
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ # The 264 series machines badline builds by name, after xplus4's
6
+ # -model names, with the C116 as another name for the C16: one board
7
+ # design with its RAM, its video standard and KERNAL, and whether the
8
+ # Plus/4's own parts are fitted (the 3-plus-1 function ROMs, the 6551
9
+ # ACIA and the user port's 6529).
10
+ module Model
11
+ Profile = Data.define(:name, :ram_kb, :ted, :kernal, :plus4_parts)
12
+
13
+ PLUS4 = Profile.new(name: "plus4", ram_kb: 64, ted: Chips::TED::PAL, kernal: "plus4/kernal-pal.rom",
14
+ plus4_parts: true)
15
+ PLUS4_NTSC = Profile.new(name: "plus4ntsc", ram_kb: 64, ted: Chips::TED::NTSC,
16
+ kernal: "plus4/kernal-ntsc.rom", plus4_parts: true)
17
+ C16 = Profile.new(name: "c16", ram_kb: 16, ted: Chips::TED::PAL, kernal: "plus4/kernal-pal.rom",
18
+ plus4_parts: false)
19
+ C16_NTSC = Profile.new(name: "c16ntsc", ram_kb: 16, ted: Chips::TED::NTSC, kernal: "plus4/kernal-ntsc.rom",
20
+ plus4_parts: false)
21
+
22
+ ALL = [PLUS4, PLUS4_NTSC, C16, C16_NTSC].freeze
23
+
24
+ # Other names a model goes by.
25
+ ALIASES = { "c116" => "c16" }.freeze
26
+
27
+ # The RAM sizes a machine can have, in K.
28
+ RAM_SIZES = [16, 32, 64].freeze
29
+
30
+ def self.named(name)
31
+ model = ALL.find { |candidate| candidate.name == ALIASES.fetch(name, name) }
32
+ raise ArgumentError, "no Plus/4 model named #{name}" if model.nil?
33
+
34
+ model
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Plus4
5
+ # The Plus/4's user port 6529 at $FD10-$FD1F, a quasi-bidirectional
6
+ # latch: a line reads low where the latch or the outside pulls it. With
7
+ # nothing plugged into the user port, the outside pulls only bit 2, the
8
+ # cassette sense switch, low while a key is down on the deck. The
9
+ # KERNAL's IRQ reads it there ($CFBF).
10
+ class UserPort
11
+ TAPE_SENSE = 0x04
12
+
13
+ attr_reader :output
14
+
15
+ def initialize(datasette)
16
+ @datasette = datasette
17
+ @output = 0xff
18
+ end
19
+
20
+ def reset!
21
+ @output = 0xff
22
+ end
23
+
24
+ def peek(_addr) = @datasette.sense_low? ? @output & ~TAPE_SENSE : @output
25
+
26
+ def poke(_addr, value)
27
+ @output = value
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/ted"
4
+ require "badline/chips/tia"
5
+ require "badline/plus4/model"
6
+ require "badline/plus4/cpu"
7
+ require "badline/plus4/cpu_port"
8
+ require "badline/plus4/keyboard_port"
9
+ require "badline/plus4/user_port"
10
+ require "badline/plus4/acia"
11
+ require "badline/plus4/bus"
12
+
13
+ module Badline
14
+ # The Commodore 264 series: the Plus/4, the C16 and the C116. A 7501 on
15
+ # the TED's clock, which runs it once or twice a TED cycle (Chips::TED's
16
+ # Beam), the TED and, on the Plus/4, a 6551 ACIA and the user port's
17
+ # 6529. Only the TED pulls IRQ, and nothing pulls NMI, which the 7501
18
+ # doesn't have.
19
+ #
20
+ # It runs headless only: the TED paints no picture yet. Device 8
21
+ # serves disks through the KERNAL traps (C64::KernalTraps), and
22
+ # cartridges go in cartridge 1's slots.
23
+ class Plus4
24
+ include IntegerHelper
25
+ include Machine::KeyboardBuffer
26
+ include Machine::Clocking
27
+ include C64::KernalTraps
28
+
29
+ # The cycle by which the KERNAL has booted and BASIC printed READY,
30
+ # with a margin, in TED cycles. READY is up by about 116,000 on a PAL
31
+ # Plus/4, 126,000 on an NTSC one and sooner on a C16.
32
+ INIT_THRESHOLD = 200_000
33
+
34
+ attr_reader :model, :bus, :cpu, :ted, :port, :keyboard, :keyboard_port, :acia, :user_port, :datasette, :tias,
35
+ :iec_bus, :cycles, :ram_kb
36
+
37
+ def family = :plus4
38
+
39
+ def init_threshold = INIT_THRESHOLD
40
+
41
+ def ram = @bus.ram
42
+
43
+ # Reads and writes memory as the CPU sees it, through the bus.
44
+ def peek(addr) = @bus.peek(addr)
45
+
46
+ def poke(addr, value) = @bus.poke(addr, value)
47
+
48
+ # +model+ names one of Model::ALL, or c116 for the C16, and +ram+ the
49
+ # RAM in K, one of Model::RAM_SIZES, when not the model's own. +tcbm+
50
+ # plugs in a 1551's TCBM interface, the TIAs at $FEC0-$FEFF, with no
51
+ # drive answering on the other side.
52
+ def initialize(model: "plus4", ram: nil, tcbm: false, debug: false)
53
+ @model = Model.named(model)
54
+ @ram_kb = ram || @model.ram_kb
55
+ raise ArgumentError, "no Plus/4 RAM size of #{@ram_kb}K" unless Model::RAM_SIZES.include?(@ram_kb)
56
+
57
+ @ted = Chips::TED.new(@model.ted)
58
+ @keyboard = Input::Keyboard.new(matrix: KeyboardPort::MATRIX)
59
+ @keyboard_port = KeyboardPort.new(@keyboard)
60
+ @ted.keyboard_port = @keyboard_port
61
+ @iec_bus = Drives::SerialBus.new
62
+ @datasette = Drives::Datasette.new(level: true)
63
+ @port = CPUPort.new(serial_bus: @iec_bus, datasette: @datasette)
64
+ plus4_parts = @model.plus4_parts
65
+ @acia = plus4_parts ? ACIA.new : nil
66
+ @user_port = plus4_parts ? UserPort.new(@datasette) : nil
67
+ io = Bus::IOPage.new(keyboard_port: @keyboard_port, acia: @acia, user_port: @user_port)
68
+ @bus = Bus.new(ted: @ted, port: @port, io:, model: @model, ram_kb: @ram_kb)
69
+ @tias = tcbm ? [Chips::TIA.new, Chips::TIA.new] : []
70
+ @bus.plug_tcbm(drive8: @tias[0], drive9: @tias[1]) if tcbm
71
+ @cpu = CPU.new(@bus, debug:)
72
+ @cycles = 0
73
+ @init_handlers = []
74
+ @pending_keys = nil
75
+ @dos = nil
76
+ @serial_trap = nil
77
+ @save_trap = nil
78
+ @capture_output = nil
79
+ end
80
+
81
+ # One TED cycle: the TED moves on and decides the CPU's clock, then the
82
+ # CPU runs its cycles in it, none while the TED holds the bus.
83
+ def cycle!
84
+ handle_init if @cycles == INIT_THRESHOLD
85
+ feed_keyboard if @pending_keys
86
+
87
+ ted = @ted
88
+ ted.cycle!
89
+ @datasette.cycle!
90
+ @cpu.irq = ted.irq?
91
+ slots = ted.cpu_slots
92
+ if slots.zero?
93
+ @cpu.stall!
94
+ else
95
+ clock_cpu(ted)
96
+ clock_cpu(ted) if slots == 2
97
+ end
98
+
99
+ @cycles += 1
100
+ end
101
+
102
+ # The RESET line: the CPU, the port, the 6529s, the ACIA, the bank
103
+ # latch and ROM selection. The TED has no reset pin.
104
+ def reset!
105
+ @port.reset!
106
+ @keyboard_port.reset!
107
+ @user_port&.reset!
108
+ @dos&.reset!
109
+ @acia&.reset!
110
+ @tias.each(&:reset!)
111
+ @bus.reset!
112
+ @cpu.reset!
113
+ end
114
+
115
+ # Switches the machine off and on: RAM and the TED start from their
116
+ # power-on state, and the RES line resets everything else.
117
+ def power_cycle!
118
+ @bus.power_on!
119
+ @ted.power_on!
120
+ reset!
121
+ end
122
+
123
+ # Calls the block with the exit code a VICE testprog writes to $FDCF.
124
+ def install_debug_register(&) = @bus.install_debug_register(&)
125
+
126
+ # Puts a cartridge's ROM chips in cartridge 1's slots (Bus#attach_cartridge)
127
+ # and switches the machine off and on, as a cartridge goes in with the
128
+ # power off. The KERNAL starts it at reset when it signs itself with CBM
129
+ # at $8007.
130
+ def attach_cartridge(chips)
131
+ @bus.attach_cartridge(chips)
132
+ power_cycle!
133
+ end
134
+
135
+ # Stores a PRG's bytes from its load address on, into RAM, and returns
136
+ # that address.
137
+ def load_prg(data)
138
+ load_addr = uint16(data[0], data[1])
139
+ bytes = data[2..] || []
140
+ bytes = bytes[0, 0x10000 - load_addr] if load_addr + bytes.length > 0x10000
141
+ bytes.each_with_index { |byte, i| @bus.ram_view.poke(load_addr + i, byte) }
142
+ load_addr
143
+ end
144
+
145
+ private
146
+
147
+ # The 264 KERNAL's keyboard buffer.
148
+ def keyboard_buffer = Machine::KeyboardBuffer::PLUS4_LAYOUT
149
+
150
+ # The 264 KERNAL, which the traps stand in for.
151
+ def trap_layout = KernalTrap::PLUS4_LAYOUT
152
+
153
+ def trap_bus = @bus
154
+
155
+ # Device 8, which the serial traps answer for, with no true drive on
156
+ # the bus to take it.
157
+ def serial_trap_device = KernalTrap::Routine::DEVICE
158
+
159
+ # BA stops the CPU at a read, and a write goes ahead.
160
+ def clock_cpu(ted)
161
+ ted.ba_low? && !@cpu.pending_write? ? @cpu.stall! : @cpu.cycle!
162
+ end
163
+ end
164
+ end