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,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ module InstructionSet
7
+ module Transfer
8
+ # +begin_src ruby
9
+ # Load accumulator with memory.
10
+ #
11
+ # Opcodes:
12
+ # $A1 - indirect_x - 6 cycles
13
+ # $A5 - zeropage - 3 cycles
14
+ # $A9 - immediate - 2 cycles
15
+ # $AD - absolute - 4 cycles
16
+ # $B1 - indirect_y - 5+ cycles
17
+ # $B5 - zeropage_x - 4 cycles
18
+ # $B9 - absolute_y - 4+ cycles
19
+ # $BD - absolute_x - 4+ cycles
20
+ def lda(_addr, value)
21
+ @a = value
22
+ update_number_flags(@a)
23
+ end
24
+
25
+ # Load X register with memory.
26
+ #
27
+ # Opcodes:
28
+ # $A2 - immediate - 2 cycles
29
+ # $A6 - zeropage - 3 cycles
30
+ # $AE - absolute - 4 cycles
31
+ # $B6 - zeropage_y - 4 cycles
32
+ # $BE - absolute_y - 4+ cycles
33
+ def ldx(_addr, value)
34
+ @x = value
35
+ update_number_flags(@x)
36
+ end
37
+
38
+ # Load Y register with memory.
39
+ #
40
+ # Opcodes:
41
+ # $A0 - immediate - 2 cycles
42
+ # $A4 - zeropage - 3 cycles
43
+ # $AC - absolute - 4 cycles
44
+ # $B4 - zeropage_x - 4 cycles
45
+ # $BC - absolute_x - 4+ cycles
46
+ def ldy(_addr, value)
47
+ @y = value
48
+ update_number_flags(@y)
49
+ end
50
+
51
+ # Store accumulator in memory.
52
+ #
53
+ # Opcodes:
54
+ # $81 - indirect_x - 6 cycles
55
+ # $85 - zeropage - 3 cycles
56
+ # $8D - absolute - 4 cycles
57
+ # $91 - indirect_y - 6 cycles
58
+ # $95 - zeropage_x - 4 cycles
59
+ # $99 - absolute_y - 5 cycles
60
+ # $9D - absolute_x - 5 cycles
61
+ def sta(addr, _value)
62
+ write_byte(addr, @a)
63
+ end
64
+
65
+ # Store X register in memory.
66
+ #
67
+ # Opcodes:
68
+ # $86 - zeropage - 3 cycles
69
+ # $8E - absolute - 4 cycles
70
+ # $96 - zeropage_y - 4 cycles
71
+ def stx(addr, _value)
72
+ write_byte(addr, @x)
73
+ end
74
+
75
+ # Store Y register in memory.
76
+ #
77
+ # Opcodes:
78
+ # $84 - zeropage - 3 cycles
79
+ # $8C - absolute - 4 cycles
80
+ # $94 - zeropage_x - 4 cycles
81
+ def sty(addr, _value)
82
+ write_byte(addr, @y)
83
+ end
84
+
85
+ # Transfer accumulator to X register.
86
+ #
87
+ # Opcodes:
88
+ # $AA - implied - 2 cycles
89
+ def tax(_addr, _value)
90
+ @x = a
91
+ update_number_flags(@x)
92
+ end
93
+
94
+ # Transfer accumulator to Y register.
95
+ #
96
+ # Opcodes:
97
+ # $A8 - implied - 2 cycles
98
+ def tay(_addr, _value)
99
+ @y = a
100
+ update_number_flags(@y)
101
+ end
102
+
103
+ # Transfer stack pointer to X register.
104
+ #
105
+ # Opcodes:
106
+ # $BA - implied - 2 cycles
107
+ def tsx(_addr, _value)
108
+ @x = stack_pointer
109
+ update_number_flags(@x)
110
+ end
111
+
112
+ # Transfer X register to accumulator.
113
+ #
114
+ # Opcodes:
115
+ # $8A - implied - 2 cycles
116
+ def txa(_addr, _operand)
117
+ @a = x
118
+ update_number_flags(@a)
119
+ end
120
+
121
+ # Transfer X register to stack pointer.
122
+ #
123
+ # Opcodes:
124
+ # $9A - implied - 2 cycles
125
+ def txs(_addr, _operand)
126
+ @stack_pointer = x
127
+ end
128
+
129
+ # Transfer Y register to accumulator.
130
+ #
131
+ # Opcodes:
132
+ # $98 - implied - 2 cycles
133
+ def tya(_addr, _operand)
134
+ @a = y
135
+ update_number_flags(@a)
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/cpu/instruction_set/arithmetic"
4
+ require "badline/chips/cpu/instruction_set/bitwise"
5
+ require "badline/chips/cpu/instruction_set/branch"
6
+ require "badline/chips/cpu/instruction_set/inc_dec"
7
+ require "badline/chips/cpu/instruction_set/flag"
8
+ require "badline/chips/cpu/instruction_set/illegal"
9
+ require "badline/chips/cpu/instruction_set/stack"
10
+ require "badline/chips/cpu/instruction_set/transfer"
11
+
12
+ module Badline
13
+ module Chips
14
+ class CPU
15
+ # http://www.6502.org/tutorials/6502opcodes.html
16
+ # http://www.e-tradition.net/bytes/6502/6502_instruction_set.html
17
+ module InstructionSet
18
+ include InstructionSet::Arithmetic
19
+ include InstructionSet::Bitwise
20
+ include InstructionSet::Branch
21
+ include InstructionSet::IncDec
22
+ include InstructionSet::Flag
23
+ include InstructionSet::Illegal
24
+ include InstructionSet::Stack
25
+ include InstructionSet::Transfer
26
+
27
+ # No operation. The illegal forms with an operand still read it, in
28
+ # the steps before this is called.
29
+ #
30
+ # Opcodes:
31
+ # $EA - implied - 2 cycles
32
+ # $04, $44, $64 - zeropage - 3 cycles (illegal)
33
+ # $14, $34, $54, $74, $D4, $F4 - zeropage_x - 4 cycles (illegal)
34
+ # $0C - absolute - 4 cycles (illegal)
35
+ # $1C, $3C, $5C, $7C, $DC, $FC - absolute_x - 4+ cycles (illegal)
36
+ # $1A, $3A, $5A, $7A, $DA, $FA - implied - 2 cycles (illegal)
37
+ def nop(_addr, _value); end
38
+
39
+ private
40
+
41
+ def update_number_flags(value)
42
+ status.zero = value.zero?
43
+ status.negative = value & 0x80 != 0
44
+ value
45
+ end
46
+
47
+ # Stores a read-modify-write result. For memory, the write happens in
48
+ # the steps that follow (see Operations#op_rmw_read).
49
+ def write_modified(addr, result)
50
+ if addr == :accumulator
51
+ @a = result
52
+ else
53
+ @rmw_result = result
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ module Interrupts
7
+ BREAK_MASK = 0x10
8
+
9
+ private
10
+
11
+ # Samples the IRQ line and the NMI latch once per cycle. An instruction
12
+ # boundary commits the sample taken on its second-to-last cycle.
13
+ def poll
14
+ if @skip_poll
15
+ @skip_poll = false
16
+ return
17
+ end
18
+ @irq_pending = @irq_sample
19
+ @nmi_pending = @nmi_sample
20
+ @irq_sample = @irq && !@status.interrupt?
21
+ @nmi_sample = @nmi
22
+ end
23
+
24
+ # A cycle the VIC stalls through BA keeps sampling the lines, OR-ed with
25
+ # the sample it already holds, but the boundary pipeline doesn't advance
26
+ # and a skipped poll stays owed to the next real cycle.
27
+ def sample_while_stalled
28
+ @irq_sample ||= @irq && !interrupt_disabled_after_stall?
29
+ @nmi_sample = true if @nmi
30
+ end
31
+
32
+ # A stalled CLI or SEI execute cycle masks with the I it is about to
33
+ # set. Every other step masks with the current I, PLP included: its
34
+ # pulled I only arrives with the stalled stack read.
35
+ def interrupt_disabled_after_stall?
36
+ return @status.interrupt? unless @plan.equal?(CPU::IMPLIED_PLAN)
37
+
38
+ case @operation
39
+ when :cli then false
40
+ when :sei then true
41
+ else @status.interrupt?
42
+ end
43
+ end
44
+
45
+ # Runs in the opcode fetch cycle in place of the fetch, with no bus
46
+ # access, and switches to the interrupt plan.
47
+ def start_interrupt
48
+ @interrupt = @boundary_nmi ? 0xfffa : 0xfffe
49
+ @brk = false
50
+ @plan = CPU::INTERRUPT_PLAN
51
+ @writes = CPU::INTERRUPT_WRITES
52
+ @index = 1
53
+ end
54
+
55
+ # BRK reads the byte after its opcode and ignores it, and selects the
56
+ # IRQ vector.
57
+ def op_brk_dummy
58
+ @memory.peek(@program_counter)
59
+ @interrupt = 0xfffe
60
+ @brk = true
61
+ end
62
+
63
+ def op_int_push_pch
64
+ @address = @brk ? (@program_counter + 1) & 0xffff : @program_counter
65
+ @memory.poke(stack_address, high_byte(@address))
66
+ @stack_pointer = (@stack_pointer - 1) & 0xff
67
+ end
68
+
69
+ def op_int_push_pcl
70
+ @memory.poke(stack_address, low_byte(@address))
71
+ @stack_pointer = (@stack_pointer - 1) & 0xff
72
+ end
73
+
74
+ def op_int_push_status
75
+ value = @brk ? @status.value | BREAK_MASK : @status.value & ~BREAK_MASK
76
+ @memory.poke(stack_address, value)
77
+ @stack_pointer = (@stack_pointer - 1) & 0xff
78
+ @status.interrupt = true
79
+ # An NMI that arrives before cycle 4 of a BRK or IRQ takes it over:
80
+ # the sequence finishes with the NMI vector.
81
+ @interrupt = 0xfffa if @interrupt == 0xfffe && @nmi_pending
82
+ @nmi = false if @interrupt == 0xfffa
83
+ end
84
+
85
+ def op_int_vector_low
86
+ @address = @memory.peek(@interrupt)
87
+ end
88
+
89
+ # Clears any interrupt that is pending, so the handler's first
90
+ # instruction always runs before the next interrupt. This applies to
91
+ # BRK too.
92
+ def op_int_vector_high
93
+ high = @memory.peek((@interrupt + 1) & 0xffff)
94
+ @program_counter = @address | (high << 8)
95
+ @irq_sample = @irq_pending = false
96
+ @nmi_sample = @nmi_pending = false
97
+ @interrupt = nil
98
+ if @brk
99
+ @status.break = false
100
+ end_instruction
101
+ else
102
+ end_sequence
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ # Where a CPU caught inside an instruction is, as a snapshot names it:
7
+ # an opcode's instruction, an IRQ or NMI sequence, or jammed.
8
+ POSITIONS = %w[boundary op interrupt jam].freeze
9
+
10
+ # The CPU's fields in a keyed snapshot module (Snapshot::Keyed): the
11
+ # registers, the interrupt lines' pipeline, the counts and, inside an
12
+ # instruction, where it is in hardware terms (pos.*), rather than as
13
+ # the microcode lays it out.
14
+ module KeyedState
15
+ def save_fields(out)
16
+ out.int("pc", @program_counter).int("sp", @stack_pointer).int("a", @a).int("x", @x).int("y", @y)
17
+ out.int("p", @status.value).bool("nmi", @nmi).bool("so_high", @so_high)
18
+ out.bool("irq.sample", @irq_sample).bool("irq.pending", @irq_pending).bool("irq.boundary", @boundary_irq)
19
+ out.bool("nmi.sample", @nmi_sample).bool("nmi.pending", @nmi_pending).bool("nmi.boundary", @boundary_nmi)
20
+ out.bool("skip_poll", @skip_poll).optional_int("stalled", stalled_ago)
21
+ out.int("cycles", @cycles).int("instructions", @instructions)
22
+ save_position(out) unless boundary?
23
+ out
24
+ end
25
+
26
+ def load_fields(input)
27
+ @program_counter = input.word("pc", @program_counter)
28
+ @stack_pointer = input.byte("sp", @stack_pointer)
29
+ @a = input.byte("a", @a)
30
+ @x = input.byte("x", @x)
31
+ @y = input.byte("y", @y)
32
+ @status.value = input.byte("p", @status.value)
33
+ load_lines(input)
34
+ @cycles = input.int("cycles", @cycles)
35
+ @instructions = input.int("instructions", @instructions)
36
+ stalled = input.optional_int("stalled")
37
+ @stalled_at = stalled.nil? ? nil : @cycles - stalled
38
+ load_position(input)
39
+ end
40
+
41
+ # What the fields leave to be worked out: whether the next step
42
+ # writes.
43
+ def restored!
44
+ @pending_write = @writes[@index]
45
+ end
46
+
47
+ private
48
+
49
+ # Cycles since the CPU last stalled, when that was this cycle or the
50
+ # one before, the only ones an instruction looks back at.
51
+ def stalled_ago
52
+ stalled_at = @stalled_at
53
+ return nil if stalled_at.nil?
54
+
55
+ ago = @cycles - stalled_at
56
+ ago.between?(0, 1) ? ago : nil
57
+ end
58
+
59
+ def load_lines(input)
60
+ @nmi = input.bool("nmi", @nmi)
61
+ @so_high = input.bool("so_high", @so_high)
62
+ @irq_sample = input.bool("irq.sample", @irq_sample)
63
+ @irq_pending = input.bool("irq.pending", @irq_pending)
64
+ @boundary_irq = input.bool("irq.boundary", @boundary_irq)
65
+ @nmi_sample = input.bool("nmi.sample", @nmi_sample)
66
+ @nmi_pending = input.bool("nmi.pending", @nmi_pending)
67
+ @boundary_nmi = input.bool("nmi.boundary", @boundary_nmi)
68
+ @skip_poll = input.bool("skip_poll", @skip_poll)
69
+ end
70
+
71
+ def save_position(out)
72
+ source = plan_source
73
+ if source == JAMMED
74
+ out.text("pos.seq", "jam")
75
+ return
76
+ end
77
+
78
+ out.text("pos.seq", source == INTERRUPT ? "interrupt" : "op")
79
+ out.int("pos.op", source) unless source == INTERRUPT
80
+ out.int("pos.t", position_t(source)).bool("pos.crossed", @boundary_crossed)
81
+ out.bool("pos.branch_taken", @branch_taken == true).bool("pos.brk", @brk)
82
+ out.optional_int("pos.vector", @interrupt)
83
+ out.optional_int("pos.address", @address).optional_int("pos.dummy_address", @dummy_address)
84
+ out.optional_int("pos.value", @value).optional_int("pos.rmw", @rmw_result)
85
+ out.optional_int("pos.pointer", @pointer)
86
+ end
87
+
88
+ # The cycles of the instruction or sequence run so far, counting the
89
+ # opcode fetch as 1 and leaving out stalled cycles.
90
+ def position_t(source)
91
+ return @index if source == INTERRUPT
92
+
93
+ MICROCODE.fetch(source).t_at(@index, @boundary_crossed)
94
+ end
95
+
96
+ def load_position(input)
97
+ case input.choice("pos.seq", POSITIONS, "boundary")
98
+ when "op" then load_instruction(input)
99
+ when "interrupt" then load_interrupt(input)
100
+ when "jam" then load_jam
101
+ else load_boundary
102
+ end
103
+ end
104
+
105
+ def load_instruction(input)
106
+ micro = MICROCODE.fetch(input.byte("pos.op", 0xea))
107
+ use_plan(micro.plan, micro.writes)
108
+ @operation = micro.operation
109
+ @optional_dummy = micro.optional_dummy
110
+ @boundary_crossed = input.bool("pos.crossed", false)
111
+ @index = micro.index_at(input.int("pos.t", 1), @boundary_crossed).clamp(1, micro.plan.length - 1)
112
+ load_latches(input)
113
+ end
114
+
115
+ def load_interrupt(input)
116
+ use_plan(INTERRUPT_PLAN, INTERRUPT_WRITES)
117
+ @boundary_crossed = input.bool("pos.crossed", false)
118
+ @index = input.int("pos.t", 1).clamp(1, INTERRUPT_PLAN.length - 1)
119
+ load_latches(input)
120
+ end
121
+
122
+ def load_jam
123
+ use_plan(JAMMED_PLAN, JAMMED_WRITES)
124
+ @index = 0
125
+ clear_latches
126
+ end
127
+
128
+ def load_boundary
129
+ use_plan(FETCH_PLAN, FETCH_WRITES)
130
+ @index = 0
131
+ clear_latches
132
+ end
133
+
134
+ def load_latches(input)
135
+ @branch_taken = input.bool("pos.branch_taken", false)
136
+ @brk = input.bool("pos.brk", false)
137
+ @interrupt = input.optional_int("pos.vector")
138
+ @address = input.optional_int("pos.address")
139
+ @dummy_address = input.optional_int("pos.dummy_address")
140
+ @value = input.optional_int("pos.value")
141
+ @rmw_result = input.optional_int("pos.rmw")
142
+ @pointer = input.optional_int("pos.pointer")
143
+ end
144
+
145
+ # The working registers no step reads before the next instruction
146
+ # sets them.
147
+ def clear_latches
148
+ @boundary_crossed = false
149
+ @branch_taken = false
150
+ @brk = false
151
+ @interrupt = nil
152
+ @address = nil
153
+ @dummy_address = nil
154
+ @value = nil
155
+ @rmw_result = nil
156
+ @pointer = nil
157
+ end
158
+
159
+ # Where the plan running comes from: an opcode's microcode, or FETCH,
160
+ # INTERRUPT or JAMMED below it.
161
+ def plan_source
162
+ return FETCH if @plan.equal?(FETCH_PLAN)
163
+ return INTERRUPT if @plan.equal?(INTERRUPT_PLAN)
164
+ return JAMMED if @plan.equal?(JAMMED_PLAN)
165
+
166
+ MICROCODE.index { |micro| micro.plan.equal?(@plan) && micro.operation == @operation } ||
167
+ MICROCODE.index { |micro| micro.plan.equal?(@plan) }
168
+ end
169
+
170
+ def use_plan(plan, writes)
171
+ @plan = plan
172
+ @writes = writes
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ class InvalidOpcodeError < StandardError; end
7
+
8
+ # Steps that write to memory. The CPU keeps running through BA on
9
+ # cycles that write, so C64#cycle! asks before each cycle whether
10
+ # the next step is one of these.
11
+ WRITE_STEPS = %i[
12
+ op_mem_write op_rmw_write_original op_rmw_write_result op_push_exec op_jsr_push_high op_jsr_push_low
13
+ op_int_push_pch op_int_push_pcl op_int_push_status
14
+ ].freeze
15
+
16
+ # The steps each addressing mode takes to work out the address an
17
+ # instruction operates on. They run after the opcode fetch.
18
+ ADDRESSING_STEPS = {
19
+ zeropage: %i[op_zp],
20
+ zeropage_x: %i[op_zp op_zp_x],
21
+ zeropage_y: %i[op_zp op_zp_y],
22
+ absolute: %i[op_abs_low op_abs_high],
23
+ absolute_x: %i[op_abs_low op_abs_high_x op_idx_dummy],
24
+ absolute_y: %i[op_abs_low op_abs_high_y op_idx_dummy],
25
+ indirect_x: %i[op_zp_pointer op_pointer_x op_pointer_low op_pointer_high],
26
+ indirect_y: %i[op_zp_pointer op_pointer_low op_pointer_high_y op_idx_dummy]
27
+ }.freeze
28
+
29
+ # The steps that read or write that address, once it is known.
30
+ OPERATION_STEPS = {
31
+ read: %i[op_mem_read],
32
+ write: %i[op_mem_write],
33
+ rmw: %i[op_rmw_read op_rmw_write_original op_rmw_write_result]
34
+ }.freeze
35
+
36
+ STORES = %i[sta stx sty sax sha shx shy tas].freeze
37
+ READ_MODIFY_WRITES = %i[asl lsr rol ror inc dec slo sre rla rra isc dcp].freeze
38
+ BRANCHES = %i[bcc bcs beq bmi bne bpl bvc bvs].freeze
39
+
40
+ # The addresses a 6502 reads after a JAM opcode, before it settles into
41
+ # JAMMED_PLAN.
42
+ JAM_ADDRESSES = [0xffff, 0xfffe, 0xfffe].freeze
43
+
44
+ # The plan between instructions: the next cycle fetches an opcode.
45
+ FETCH_PLAN = %i[op_fetch].freeze
46
+
47
+ IMPLIED_PLAN = %i[op_fetch op_implied_exec].freeze
48
+ ACCUMULATOR_PLAN = %i[op_fetch op_accumulator_exec].freeze
49
+ IMMEDIATE_PLAN = %i[op_fetch op_immediate_read].freeze
50
+
51
+ # $10, $30, $50, $70, $90, $B0, $D0, $F0 - relative - 2+ cycles.
52
+ # A branch that is not taken takes 2 cycles. A taken branch takes 3,
53
+ # or 4 when the target is on another page.
54
+ BRANCH_PLAN = %i[op_fetch op_relative op_branch op_branch_fixup].freeze
55
+
56
+ # $4C - absolute - 3 cycles
57
+ JMP_ABSOLUTE_PLAN = %i[op_fetch op_abs_low op_jmp_high].freeze
58
+
59
+ # $6C - indirect - 5 cycles
60
+ JMP_INDIRECT_PLAN = %i[op_fetch op_abs_low op_abs_high op_indirect_low op_indirect_high].freeze
61
+
62
+ # $20 - absolute - 6 cycles
63
+ JSR_PLAN = %i[op_fetch op_jsr_low op_jsr_dummy op_jsr_push_high op_jsr_push_low op_jsr_high].freeze
64
+
65
+ # $60 - implied - 6 cycles
66
+ RTS_PLAN = %i[op_fetch op_implied_dummy op_pull_dummy op_pull_low op_pull_high op_rts_fixup].freeze
67
+
68
+ # $40 - implied - 6 cycles
69
+ RTI_PLAN = %i[op_fetch op_implied_dummy op_pull_dummy op_rti_pull_status op_pull_low op_rti_pull_high].freeze
70
+
71
+ # $08, $48 - implied - 3 cycles
72
+ PUSH_PLAN = %i[op_fetch op_implied_dummy op_push_exec].freeze
73
+
74
+ # $28, $68 - implied - 4 cycles
75
+ PULL_PLAN = %i[op_fetch op_implied_dummy op_pull_dummy op_pull_exec].freeze
76
+
77
+ # $02, $12, $22, $32, $42, $52, $62, $72, $92, $B2, $D2, $F2 - illegal
78
+ JAM_PLAN = [:op_fetch, :op_implied_dummy, *Array.new(JAM_ADDRESSES.length, :op_jam)].freeze
79
+
80
+ # A jammed CPU reads $FFFF on every cycle until reset. It never reaches
81
+ # an instruction boundary, so a pending IRQ or NMI never starts.
82
+ JAMMED_PLAN = %i[op_jammed].freeze
83
+
84
+ # BRK and hardware interrupts push the return address and status, then
85
+ # read the new PC from a vector. Only the second step differs: BRK's
86
+ # selects the IRQ vector and sets the break flag it pushes.
87
+ #
88
+ # $00 - implied - 7 cycles (BRK)
89
+ INTERRUPT_PLAN = %i[
90
+ op_fetch op_implied_dummy op_int_push_pch op_int_push_pcl op_int_push_status
91
+ op_int_vector_low op_int_vector_high
92
+ ].freeze
93
+ BRK_PLAN = %i[
94
+ op_fetch op_brk_dummy op_int_push_pch op_int_push_pcl op_int_push_status
95
+ op_int_vector_low op_int_vector_high
96
+ ].freeze
97
+
98
+ SPECIAL_PLANS = {
99
+ brk: BRK_PLAN, jam: JAM_PLAN, jsr: JSR_PLAN, rts: RTS_PLAN,
100
+ rti: RTI_PLAN, pha: PUSH_PLAN, php: PUSH_PLAN,
101
+ pla: PULL_PLAN, plp: PULL_PLAN
102
+ }.freeze
103
+
104
+ # Everything the CPU needs to run one opcode:
105
+ # - +operation+: the instruction method to call, such as :lda
106
+ # - +plan+: the step to run on each cycle
107
+ # - +writes+: which of those steps write to memory
108
+ # - +optional_dummy+: true for indexed reads, which skip the extra
109
+ # cycle unless indexing crosses a page
110
+ Microcode = Struct.new(:operation, :plan, :writes, :optional_dummy) do
111
+ def self.table
112
+ Array.new(256) do |opcode|
113
+ instruction = Instruction.find(opcode)
114
+ raise InvalidOpcodeError, format("$%02x", opcode) unless instruction
115
+
116
+ build(instruction)
117
+ end.freeze
118
+ end
119
+
120
+ # The plan step that follows `cycles` cycles of the instruction, counting
121
+ # the opcode fetch as 1: one further on once an indexed read has
122
+ # skipped its dummy read, which it does when the index add didn't
123
+ # cross a page.
124
+ def index_at(cycles, crossed) = skips_dummy?(cycles, crossed) ? cycles + 1 : cycles
125
+
126
+ # The cycles run before plan step `index`, as index_at counts them.
127
+ def t_at(index, crossed) = skips_dummy?(index - 1, crossed) ? index - 1 : index
128
+
129
+ def skips_dummy?(cycles, crossed)
130
+ return false unless optional_dummy && !crossed
131
+
132
+ dummy = plan.index(:op_idx_dummy)
133
+ !dummy.nil? && cycles >= dummy
134
+ end
135
+
136
+ def self.write_mask(plan)
137
+ plan.map { |step| WRITE_STEPS.include?(step) }.freeze
138
+ end
139
+
140
+ def self.build(instruction)
141
+ plan = plan_for(instruction)
142
+ new(instruction.name, plan, write_mask(plan),
143
+ instruction.boundary_cycle?)
144
+ end
145
+
146
+ def self.plan_for(instruction)
147
+ case instruction.name
148
+ when :jmp
149
+ if instruction.addressing_mode == :indirect
150
+ JMP_INDIRECT_PLAN
151
+ else
152
+ JMP_ABSOLUTE_PLAN
153
+ end
154
+ when *BRANCHES then BRANCH_PLAN
155
+ else SPECIAL_PLANS[instruction.name] || addressed_plan(instruction)
156
+ end
157
+ end
158
+
159
+ def self.addressed_plan(instruction)
160
+ mode = instruction.addressing_mode
161
+ case mode
162
+ when :implied then IMPLIED_PLAN
163
+ when :accumulator then ACCUMULATOR_PLAN
164
+ when :immediate then IMMEDIATE_PLAN
165
+ else
166
+ [:op_fetch, *ADDRESSING_STEPS.fetch(mode),
167
+ *OPERATION_STEPS.fetch(operation_kind(instruction.name))].freeze
168
+ end
169
+ end
170
+
171
+ def self.operation_kind(name)
172
+ return :write if STORES.include?(name)
173
+ return :rmw if READ_MODIFY_WRITES.include?(name)
174
+
175
+ :read
176
+ end
177
+
178
+ private_class_method :build, :plan_for, :addressed_plan, :operation_kind
179
+ end
180
+
181
+ FETCH_WRITES = Microcode.write_mask(FETCH_PLAN)
182
+ INTERRUPT_WRITES = Microcode.write_mask(INTERRUPT_PLAN)
183
+ JAMMED_WRITES = Microcode.write_mask(JAMMED_PLAN)
184
+ MICROCODE = Microcode.table
185
+
186
+ # The instruction methods, in a fixed order a snapshot names them by.
187
+ OPERATIONS = MICROCODE.map(&:operation).uniq.freeze
188
+
189
+ # The plans a snapshot names that aren't an opcode's.
190
+ FETCH = -1
191
+ INTERRUPT = -2
192
+ JAMMED = -3
193
+ end
194
+ end
195
+ end