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,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ class Cartridge
6
+ # EasyFlash: two Am29F040B flash chips, one for ROML and one for ROMH,
7
+ # each 64 banks of 8K, and 256 bytes of RAM at $DF00. $DE00 selects the
8
+ # bank. $DE02 sets the memory configuration: bit 2 hands the GAME line
9
+ # to bit 0 (otherwise the boot jumper holds it low), bit 1 pulls EXROM
10
+ # low and bit 7 drives the LED.
11
+ #
12
+ # The flash takes writes in Ultimax mode, at $8000 for ROML and $E000
13
+ # for ROMH, which is how EAPI programs and erases it. The writes stay in
14
+ # memory; #save_crt writes the flash out as a new image.
15
+ #
16
+ # An image built for EasyFlash carries an EAPI, the flash driver, at $B800
17
+ # in bank 0 of ROMH. On attach, as in VICE, the bundled Am29F040 EAPI
18
+ # (roms/eapi) takes its place, so the image drives the flash emulated here.
19
+ class EasyFlash < Cartridge
20
+ BANKS = 64
21
+ EAPI_OFFSET = 0x1800
22
+
23
+ def self.eapi
24
+ @eapi ||= ROM.read("eapi/eapi-am29f040-14").drop(2).freeze
25
+ end
26
+
27
+ attr_reader :low_flash, :high_flash
28
+
29
+ def clock=(clock)
30
+ super
31
+ [@low_flash, @high_flash].each { |flash| flash.clock = clock }
32
+ end
33
+
34
+ def readable_io_pages
35
+ [0xdf]
36
+ end
37
+
38
+ def peek(addr)
39
+ @io_ram[addr & 0xff]
40
+ end
41
+
42
+ def poke(addr, value)
43
+ if addr >= 0xdf00
44
+ @io_ram[addr & 0xff] = value
45
+ elsif addr.nobits?(0x02)
46
+ @bank = value & 0x3f
47
+ select_bank
48
+ else
49
+ @control = value & 0x87
50
+ apply_control
51
+ changed!
52
+ end
53
+ end
54
+
55
+ def led?
56
+ @control.anybits?(0x80)
57
+ end
58
+
59
+ # Reset clears both registers, and leaves the flash and the RAM.
60
+ def reset
61
+ @bank = 0
62
+ @control = 0
63
+ apply_control
64
+ select_bank
65
+ end
66
+
67
+ # Writes every bank that isn't blank to a CRT image at path.
68
+ def save_crt(path)
69
+ chips = (0...BANKS).flat_map do |number|
70
+ [[@low_flash, 0x8000], [@high_flash, 0xa000]].filter_map do |flash, address|
71
+ data = flash.data[number * BANK_SIZE, BANK_SIZE]
72
+ Storage::CRTFile::Chip.new(chip_type: 2, bank: number, address:, data:) if data.any?(0..0xfe)
73
+ end
74
+ end
75
+ image = Storage::CRTFile::Image.new(hardware_type: 32, subtype: 0, exrom: 1, game: 0, name:, chips:)
76
+ Storage::CRTFile.write(path, image)
77
+ end
78
+
79
+ private
80
+
81
+ def select_bank
82
+ map_bank
83
+ changed!
84
+ end
85
+
86
+ def map_bank
87
+ @roml = @low_flash.window(@bank * BANK_SIZE)
88
+ @romh = @high_flash.window(@bank * BANK_SIZE)
89
+ end
90
+
91
+ def apply_control
92
+ game = @control.nobits?(0x04) || @control.anybits?(0x01)
93
+ exrom = @control.anybits?(0x02)
94
+ @game = game ? 0 : 1
95
+ @exrom = exrom ? 0 : 1
96
+ end
97
+
98
+ def install_chips(chips)
99
+ low = Array.new(BANKS * BANK_SIZE, 0xff)
100
+ high = Array.new(BANKS * BANK_SIZE, 0xff)
101
+ chips.each do |chip|
102
+ offset = (chip.bank & 0x3f) * BANK_SIZE
103
+ if chip.address >= ROMH_START
104
+ place(high, offset, chip.data)
105
+ else
106
+ place(low, offset, chip.data)
107
+ place(high, offset, chip.data[BANK_SIZE..]) if chip.data.length > BANK_SIZE
108
+ end
109
+ end
110
+ install_eapi(high)
111
+ @low_flash = Flash.new(low)
112
+ @high_flash = Flash.new(high)
113
+ [@low_flash, @high_flash].each { |flash| flash.on_change { select_bank } }
114
+ @io_ram = Array.new(0x100, 0xff)
115
+ reset
116
+ end
117
+
118
+ def install_eapi(high)
119
+ return unless high[EAPI_OFFSET, 4] == "eapi".bytes
120
+
121
+ eapi = EasyFlash.eapi
122
+ high[EAPI_OFFSET, eapi.length] = eapi
123
+ end
124
+
125
+ def place(flash_data, offset, bytes)
126
+ bytes = bytes.first(BANK_SIZE)
127
+ flash_data[offset, bytes.length] = bytes
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ class Cartridge
6
+ # Epyx FastLoad: 8K. A capacitor holds the ROM in for 512 cycles after
7
+ # it was last discharged, by reset, a read through ROML or a read of
8
+ # I/O 1; once it charges, $8000-$9FFF reads RAM. I/O 2 reads the last
9
+ # 256 bytes of the ROM.
10
+ class EpyxFastload < Cartridge
11
+ DISCHARGED_CYCLES = 512
12
+
13
+ class Window
14
+ def initialize(cartridge, rom)
15
+ @cartridge = cartridge
16
+ @rom = rom
17
+ end
18
+
19
+ def peek(addr)
20
+ @cartridge.rom_visible? ? @rom.peek(addr) : @cartridge.ram_peek(addr)
21
+ end
22
+ end
23
+
24
+ def clock=(clock)
25
+ super
26
+ discharge!
27
+ end
28
+
29
+ def readable_io_pages
30
+ [0xde, 0xdf]
31
+ end
32
+
33
+ def peek(addr)
34
+ return @rom.peek(addr) if addr >= 0xdf00
35
+
36
+ discharge!
37
+ open_bus(addr)
38
+ end
39
+
40
+ # True while the capacitor holds the ROM in, discharging it again.
41
+ def rom_visible?
42
+ return true unless @clock
43
+ return false if @clock.call - @discharged_at >= DISCHARGED_CYCLES
44
+
45
+ discharge!
46
+ true
47
+ end
48
+
49
+ def ram_peek(addr)
50
+ @ram.peek(addr)
51
+ end
52
+
53
+ # Reset discharges the capacitor, so the ROM shows.
54
+ def reset
55
+ discharge!
56
+ end
57
+
58
+ private
59
+
60
+ def discharge!
61
+ @discharged_at = @clock ? @clock.call : 0
62
+ end
63
+
64
+ def install_chips(chips)
65
+ @rom = banks_from(chips).first.compact.first || EMPTY_BANK
66
+ @roml = Window.new(self, @rom)
67
+ @discharged_at = 0
68
+ self.mode = :rom8k
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ class Cartridge
6
+ # The Final Cartridge III: four 16K banks, or sixteen on the III+. I/O 1
7
+ # and I/O 2 show the last two pages of the selected ROML bank. A write
8
+ # to $DFFF sets the register:
9
+ #
10
+ # bits 0-1 bank (0-3 on the III+)
11
+ # bit 4 EXROM level
12
+ # bit 5 GAME level
13
+ # bit 6 NMI line, active low
14
+ # bit 7 hides the register until the next freeze
15
+ #
16
+ # Clearing bit 6 pulls NMI without freezing, and setting it lets go of
17
+ # NMI after a freeze. A freeze keeps the bank and switches to Ultimax,
18
+ # which the VIC doesn't see until the register sets it again.
19
+ class FinalCartridge3 < Cartridge
20
+ include Freezer
21
+
22
+ REGISTER = 0xdfff
23
+
24
+ def readable_io_pages
25
+ [0xde, 0xdf]
26
+ end
27
+
28
+ def peek(addr)
29
+ @io_bank.peek(addr)
30
+ end
31
+
32
+ def poke(addr, value)
33
+ return unless addr == REGISTER && @register_visible
34
+
35
+ @register_visible = value.nobits?(0x80)
36
+ self.nmi = value.nobits?(0x40)
37
+ select(value & @bank_mask)
38
+ @exrom = (value >> 4) & 0x01
39
+ @game = (value >> 5) & 0x01
40
+ @phi1_ultimax = ultimax?
41
+ changed!
42
+ end
43
+
44
+ def phi1_ultimax?
45
+ @phi1_ultimax
46
+ end
47
+
48
+ def reset
49
+ @register_visible = true
50
+ @phi1_ultimax = false
51
+ self.nmi = false
52
+ select(0)
53
+ select_mode(:rom16k)
54
+ end
55
+
56
+ def freeze!
57
+ @register_visible = true
58
+ @phi1_ultimax = false
59
+ select_mode(:ultimax)
60
+ end
61
+
62
+ private
63
+
64
+ def windows = [*@roml_banks, *@romh_banks, EMPTY_BANK]
65
+
66
+ def select(number)
67
+ @io_bank = @roml = bank(@roml_banks, number)
68
+ @romh = bank(@romh_banks, number)
69
+ end
70
+
71
+ def install_chips(chips)
72
+ @roml_banks, @romh_banks = banks_from(chips)
73
+ @bank_mask = @roml_banks.length > 4 ? 0x0f : 0x03
74
+ reset
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,279 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ class Cartridge
6
+ # An AMD 29F0x0 flash chip, driven through the JEDEC command set in the
7
+ # AMD datasheets. Each command opens with two unlock cycles, $AA to the
8
+ # first unlock address and $55 to the second, and a third write picks
9
+ # the command:
10
+ #
11
+ # $90 autoselect: offset 0 reads the manufacturer, 1 the device and 2
12
+ # the sector protection, until a reset
13
+ # $F0 reset to reading the array, which a lone $F0 also does
14
+ # $A0 byte program: the next write clears bits of the array, which
15
+ # only an erase sets again
16
+ # $80 erase, after two more unlock cycles: $10 to the first unlock
17
+ # address erases the chip, and $30 to a sector erases the sector.
18
+ # Further sectors can follow within the 50 us window.
19
+ #
20
+ # While a program or erase runs, a read returns status: DQ7 the
21
+ # complement of the programmed bit (0 while erasing), DQ6 toggling on
22
+ # every read, DQ5 set when a program failed and DQ3 set once an erase
23
+ # has left its window. $B0 suspends a sector erase and $30 resumes it.
24
+ #
25
+ # The cartridge reads the array through windows, and the chip swaps
26
+ # them for status windows while it is busy or in autoselect.
27
+ class Flash
28
+ Model = Data.define(:size, :sector_size, :unlock1, :unlock2, :unlock_mask, :manufacturer, :device)
29
+
30
+ AM29F040 = Model.new(size: 0x80000, sector_size: 0x10000, unlock1: 0x5555, unlock2: 0x2aaa,
31
+ unlock_mask: 0x7fff, manufacturer: 0x01, device: 0xa4)
32
+ AM29F040B = Model.new(size: 0x80000, sector_size: 0x10000, unlock1: 0x555, unlock2: 0x2aa,
33
+ unlock_mask: 0x7ff, manufacturer: 0x01, device: 0xa4)
34
+ AM29F010 = Model.new(size: 0x20000, sector_size: 0x4000, unlock1: 0x5555, unlock2: 0x2aaa,
35
+ unlock_mask: 0x7fff, manufacturer: 0x01, device: 0x20)
36
+
37
+ # The datasheet's typical times, in cycles at 1 MHz.
38
+ PROGRAM_CYCLES = 7
39
+ ERASE_WINDOW_CYCLES = 50
40
+ SECTOR_ERASE_CYCLES = 1_000_000
41
+ CHIP_ERASE_CYCLES = 8_000_000
42
+
43
+ # The array, read directly while the chip reads it.
44
+ class Window
45
+ def initialize(flash, base)
46
+ @flash = flash
47
+ @data = flash.data
48
+ @base = base
49
+ end
50
+
51
+ def peek(addr)
52
+ @data[@base | (addr & 0x1fff)]
53
+ end
54
+ alias [] peek
55
+
56
+ def poke(addr, value)
57
+ @flash.write(@base | (addr & 0x1fff), value)
58
+ end
59
+ alias []= poke
60
+ end
61
+
62
+ # Reads through the chip, for its status and autoselect codes.
63
+ class StatusWindow < Window
64
+ def peek(addr)
65
+ @flash.read(@base | (addr & 0x1fff))
66
+ end
67
+ alias [] peek
68
+ end
69
+
70
+ # The command states, which a snapshot names.
71
+ STATES = %i[read program_setup autoselect programming erase_window erasing suspended chip_erase
72
+ program_error].freeze
73
+
74
+ attr_reader :data, :state
75
+ attr_writer :clock
76
+
77
+ def initialize(data, model: AM29F040B, clock: -> { 0 })
78
+ @model = model
79
+ @data = data + ([0xff] * (model.size - data.length))
80
+ @clock = clock
81
+ @windows = {}
82
+ @status_windows = {}
83
+ @on_change = nil
84
+ @state = :read
85
+ @base_state = :read
86
+ @cycle = 0
87
+ @toggle = 0
88
+ end
89
+
90
+ # Called when the chip starts or stops answering reads from the array.
91
+ def on_change(&block)
92
+ @on_change = block
93
+ end
94
+
95
+ def window(base)
96
+ if array_mode?
97
+ @windows[base] ||= Window.new(self, base)
98
+ else
99
+ @status_windows[base] ||= StatusWindow.new(self, base)
100
+ end
101
+ end
102
+
103
+ # Hands out new windows at the bases given, as #window would have.
104
+ def rebuild_windows(bases, status_bases)
105
+ @windows = {}
106
+ @status_windows = {}
107
+ bases.each { |base| @windows[base] = Window.new(self, base) }
108
+ status_bases.each { |base| @status_windows[base] = StatusWindow.new(self, base) }
109
+ end
110
+
111
+ def array_mode?
112
+ @state == :read || @state == :program_setup
113
+ end
114
+
115
+ def read(offset)
116
+ settle
117
+ case @state
118
+ when :read then @data[offset]
119
+ when :autoselect then autoselect_code(offset)
120
+ when :suspended then suspended_read(offset)
121
+ else status
122
+ end
123
+ end
124
+
125
+ def write(offset, value)
126
+ settle
127
+ case @state
128
+ when :program_setup then program(offset, value)
129
+ when :erase_window then extend_erase(offset, value)
130
+ when :erasing then suspend if value == 0xb0
131
+ when :suspended then resume if value == 0x30
132
+ when :program_error then enter(:read, base: :read) if value == 0xf0
133
+ when :programming, :chip_erase then nil
134
+ else command(offset, value)
135
+ end
136
+ end
137
+
138
+ private
139
+
140
+ def command(offset, value)
141
+ if value == 0xf0
142
+ @cycle = 0
143
+ return enter(:read, base: :read)
144
+ end
145
+
146
+ address = offset & @model.unlock_mask
147
+ @cycle = case @cycle
148
+ when 0, 3 then address == @model.unlock1 && value == 0xaa ? @cycle + 1 : 0
149
+ when 1, 4 then address == @model.unlock2 && value == 0x55 ? @cycle + 1 : 0
150
+ when 2 then command_cycle(address, value)
151
+ when 5 then erase_command(offset, address, value)
152
+ end
153
+ end
154
+
155
+ def command_cycle(address, value)
156
+ return 0 unless address == @model.unlock1
157
+
158
+ case value
159
+ when 0x90 then enter(:autoselect, base: :autoselect)
160
+ when 0xa0 then @state = :program_setup
161
+ when 0x80 then return 3
162
+ end
163
+ 0
164
+ end
165
+
166
+ def erase_command(offset, address, value)
167
+ if value == 0x10 && address == @model.unlock1
168
+ start_busy(:chip_erase, CHIP_ERASE_CYCLES)
169
+ elsif value == 0x30
170
+ @erasing_sectors = [sector(offset)]
171
+ start_busy(:erase_window, ERASE_WINDOW_CYCLES)
172
+ end
173
+ 0
174
+ end
175
+
176
+ def program(offset, value)
177
+ @programmed = value
178
+ @data[offset] &= value
179
+ if @data[offset] == value
180
+ start_busy(:programming, PROGRAM_CYCLES)
181
+ else
182
+ enter(:program_error)
183
+ end
184
+ end
185
+
186
+ def extend_erase(offset, value)
187
+ case value
188
+ when 0x30
189
+ @erasing_sectors |= [sector(offset)]
190
+ @done_at = now + ERASE_WINDOW_CYCLES
191
+ when 0xb0
192
+ @remaining = @erasing_sectors.length * SECTOR_ERASE_CYCLES
193
+ enter(:suspended)
194
+ else
195
+ @erasing_sectors = nil
196
+ enter(@base_state)
197
+ end
198
+ end
199
+
200
+ def suspend
201
+ @remaining = @done_at - now
202
+ enter(:suspended)
203
+ end
204
+
205
+ def resume
206
+ start_busy(:erasing, @remaining)
207
+ end
208
+
209
+ # Moves a timed operation on once the clock has passed its end.
210
+ def settle
211
+ return unless @done_at && now >= @done_at
212
+
213
+ case @state
214
+ when :erase_window
215
+ start_busy(:erasing, @done_at - now + (@erasing_sectors.length * SECTOR_ERASE_CYCLES))
216
+ settle
217
+ when :erasing
218
+ @erasing_sectors.each { |s| @data.fill(0xff, s * @model.sector_size, @model.sector_size) }
219
+ @erasing_sectors = nil
220
+ finish
221
+ when :chip_erase
222
+ @data.fill(0xff)
223
+ finish
224
+ else
225
+ finish
226
+ end
227
+ end
228
+
229
+ def finish
230
+ @done_at = nil
231
+ enter(@base_state)
232
+ end
233
+
234
+ def start_busy(state, cycles)
235
+ @done_at = now + cycles
236
+ @programmed = 0xff unless state == :programming
237
+ enter(state)
238
+ end
239
+
240
+ def enter(state, base: @base_state)
241
+ was_array = array_mode?
242
+ @state = state
243
+ @base_state = base
244
+ @done_at = nil unless %i[programming erase_window erasing chip_erase].include?(state)
245
+ @on_change&.call if was_array != array_mode?
246
+ end
247
+
248
+ def status
249
+ @toggle ^= 0x40
250
+ value = (~@programmed & 0x80) | @toggle
251
+ value |= 0x20 if @state == :program_error
252
+ value |= 0x08 if @state == :erasing || @state == :chip_erase
253
+ value
254
+ end
255
+
256
+ def suspended_read(offset)
257
+ @erasing_sectors.include?(sector(offset)) ? 0x80 : @data[offset]
258
+ end
259
+
260
+ def autoselect_code(offset)
261
+ case offset & 0x43
262
+ when 0 then @model.manufacturer
263
+ when 1 then @model.device
264
+ when 2 then 0x00
265
+ else @data[offset]
266
+ end
267
+ end
268
+
269
+ def sector(offset)
270
+ offset / @model.sector_size
271
+ end
272
+
273
+ def now
274
+ @clock.call
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ class Cartridge
6
+ # The freeze button of a freezer cartridge. A press pulls NMI, and the
7
+ # computer calls #freeze! once the CPU has pushed the return address and
8
+ # status of the interrupt, so the vector fetch that follows reads the
9
+ # cartridge ROM in Ultimax mode. The cartridge lets go of NMI when its
10
+ # software acknowledges the freeze.
11
+ module Freezer
12
+ def freezer? = true
13
+
14
+ def press_button
15
+ @button = true
16
+ self.nmi = true if freeze_allowed?
17
+ end
18
+
19
+ def release_button
20
+ @button = false
21
+ end
22
+
23
+ private
24
+
25
+ def freeze_allowed?
26
+ true
27
+ end
28
+
29
+ def button_pressed?
30
+ @button == true
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ class Cartridge
6
+ # Fun Play / Power Play: up to 16 8K banks. A write to I/O 1 selects the
7
+ # bank from bits 3-5 and 0, and either 8K mode ($00 in bits 7, 6, 2 and 1)
8
+ # or no ROM ($86 in those bits).
9
+ class FunPlay < Cartridge
10
+ def poke(addr, value)
11
+ return if addr > 0xdeff
12
+
13
+ @roml = bank(@banks, self.class.bank_number(value))
14
+ case value & 0xc6
15
+ when 0x00 then self.mode = :rom8k
16
+ when 0x86 then self.mode = :off
17
+ end
18
+ changed!
19
+ end
20
+
21
+ # The CRT file numbers each bank by the register value that selects it.
22
+ def reset
23
+ poke(0xde00, 0)
24
+ end
25
+
26
+ def self.bank_number(value)
27
+ ((value >> 3) & 0x07) | ((value & 0x01) << 3)
28
+ end
29
+
30
+ private
31
+
32
+ def install_chips(chips)
33
+ @banks = []
34
+ chips.each { |chip| @banks[self.class.bank_number(chip.bank)] = rom_bank(chip.data) }
35
+ reset
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class C64
5
+ class Cartridge
6
+ # GMod2: an Am29F040 flash chip of 64 8K banks at ROML. A write to I/O 1
7
+ # selects the bank from bits 0-5. Bit 6 selects the EEPROM, which
8
+ # switches the ROM out, and bits 6 and 7 together enable flash writes:
9
+ # the cartridge then asserts Ultimax on write cycles only, so reads see
10
+ # the C64's memory and writes to $E000-$FFFF reach the flash. The EEPROM
11
+ # isn't emulated.
12
+ class GMod2 < Cartridge
13
+ BANKS = 64
14
+
15
+ attr_reader :flash
16
+
17
+ def clock=(clock)
18
+ super
19
+ @flash.clock = clock
20
+ end
21
+
22
+ def poke(addr, value)
23
+ return if addr > 0xdeff
24
+
25
+ @bank = value & 0x3f
26
+ @flash_writes = value.allbits?(0xc0)
27
+ self.mode = value.anybits?(0x40) ? :off : :rom8k
28
+ select_bank
29
+ end
30
+
31
+ def romh_writes
32
+ @roml if @flash_writes
33
+ end
34
+
35
+ # Reset selects the first bank in 8K mode, and leaves the flash as it
36
+ # is.
37
+ def reset
38
+ @bank = 0
39
+ @flash_writes = false
40
+ self.mode = :rom8k
41
+ select_bank
42
+ end
43
+
44
+ private
45
+
46
+ def select_bank
47
+ @roml = @flash.window(@bank * BANK_SIZE)
48
+ changed!
49
+ end
50
+
51
+ def install_chips(chips)
52
+ data = Array.new(BANKS * BANK_SIZE, 0xff)
53
+ chips.each do |chip|
54
+ bytes = chip.data.first(BANK_SIZE)
55
+ data[(chip.bank & 0x3f) * BANK_SIZE, bytes.length] = bytes
56
+ end
57
+ @flash = Flash.new(data, model: Flash::AM29F040)
58
+ @flash.on_change { select_bank }
59
+ reset
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end