badline 0.12.0 → 0.13.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 (516) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/CLAUDE.md +59 -31
  4. data/README.md +1 -1
  5. data/doc/library.md +9 -9
  6. data/doc/pinned-behaviour.md +105 -105
  7. data/doc/snapshots.md +1 -4
  8. data/lib/badline/audio/bare_player.rb +4 -4
  9. data/lib/badline/audio/cli.rb +6 -6
  10. data/lib/badline/audio/jukebox.rb +2 -2
  11. data/lib/badline/audio/machine_player.rb +2 -2
  12. data/lib/badline/{media → audio}/queue.rb +1 -1
  13. data/lib/badline/audio/queued_tune.rb +6 -6
  14. data/lib/badline/audio/renderer.rb +5 -5
  15. data/lib/badline/audio/stereo.rb +1 -1
  16. data/lib/badline/c128/{bus_state.rb → bus/saved_state.rb} +6 -6
  17. data/lib/badline/c128/bus.rb +30 -51
  18. data/lib/badline/c128/color_lines.rb +2 -2
  19. data/lib/badline/c128/cpu.rb +1 -1
  20. data/lib/badline/c128/drives.rb +10 -11
  21. data/lib/badline/c128/fast_serial.rb +5 -5
  22. data/lib/badline/c128/mmu_pages.rb +1 -1
  23. data/lib/badline/c128/modes.rb +2 -2
  24. data/lib/badline/c128/saved_state.rb +21 -47
  25. data/lib/badline/c128/z80.rb +2 -2
  26. data/lib/badline/c128.rb +18 -72
  27. data/lib/badline/{computer → c64}/attachments.rb +16 -47
  28. data/lib/badline/c64/banked_ram/banking.rb +32 -0
  29. data/lib/badline/c64/banked_ram/plus256k.rb +56 -0
  30. data/lib/badline/c64/banked_ram/plus60k.rb +45 -0
  31. data/lib/badline/c64/banked_ram/unexpanded.rb +30 -0
  32. data/lib/badline/c64/banked_ram.rb +23 -0
  33. data/lib/badline/c64/bus/extra_sids.rb +32 -0
  34. data/lib/badline/c64/bus/fittings.rb +40 -0
  35. data/lib/badline/c64/bus/pla.rb +149 -0
  36. data/lib/badline/c64/bus/roms.rb +41 -0
  37. data/lib/badline/c64/bus/saved_state.rb +68 -0
  38. data/lib/badline/c64/bus/sid_slots.rb +44 -0
  39. data/lib/badline/c64/bus.rb +218 -0
  40. data/lib/badline/c64/cartridge/action_replay.rb +128 -0
  41. data/lib/badline/c64/cartridge/atomic_power.rb +26 -0
  42. data/lib/badline/c64/cartridge/bank.rb +83 -0
  43. data/lib/badline/c64/cartridge/comal80.rb +52 -0
  44. data/lib/badline/c64/cartridge/dinamic.rb +36 -0
  45. data/lib/badline/c64/cartridge/easy_flash.rb +147 -0
  46. data/lib/badline/c64/cartridge/epyx_fastload.rb +81 -0
  47. data/lib/badline/c64/cartridge/final_cartridge3.rb +91 -0
  48. data/lib/badline/c64/cartridge/flash/saved_state.rb +40 -0
  49. data/lib/badline/c64/cartridge/flash.rb +279 -0
  50. data/lib/badline/c64/cartridge/freezer.rb +35 -0
  51. data/lib/badline/c64/cartridge/fun_play.rb +40 -0
  52. data/lib/badline/c64/cartridge/g_mod2.rb +76 -0
  53. data/lib/badline/c64/cartridge/game_system.rb +42 -0
  54. data/lib/badline/c64/cartridge/geo_ram.rb +81 -0
  55. data/lib/badline/c64/cartridge/kcs_power.rb +77 -0
  56. data/lib/badline/c64/cartridge/mach5.rb +34 -0
  57. data/lib/badline/c64/cartridge/magic_desk.rb +32 -0
  58. data/lib/badline/c64/cartridge/ocean.rb +37 -0
  59. data/lib/badline/c64/cartridge/pagefox.rb +70 -0
  60. data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +47 -0
  61. data/lib/badline/c64/cartridge/retro_replay.rb +286 -0
  62. data/lib/badline/c64/cartridge/rex_utility.rb +31 -0
  63. data/lib/badline/c64/cartridge/rgcd.rb +60 -0
  64. data/lib/badline/c64/cartridge/saved_state.rb +63 -0
  65. data/lib/badline/c64/cartridge/simons_basic.rb +37 -0
  66. data/lib/badline/c64/cartridge/standard.rb +17 -0
  67. data/lib/badline/c64/cartridge/super_games.rb +50 -0
  68. data/lib/badline/c64/cartridge/westermann.rb +32 -0
  69. data/lib/badline/c64/cartridge/zaxxon.rb +68 -0
  70. data/lib/badline/c64/cartridge.rb +258 -0
  71. data/lib/badline/c64/color_memory.rb +27 -0
  72. data/lib/badline/c64/common.rb +34 -0
  73. data/lib/badline/c64/kernal_traps.rb +89 -0
  74. data/lib/badline/c64/reu/dma/saved_state.rb +52 -0
  75. data/lib/badline/c64/reu/dma.rb +275 -0
  76. data/lib/badline/c64/reu/ram.rb +109 -0
  77. data/lib/badline/c64/reu/saved_state.rb +55 -0
  78. data/lib/badline/c64/reu/trigger.rb +34 -0
  79. data/lib/badline/c64/reu.rb +291 -0
  80. data/lib/badline/{computer → c64}/saved_state.rb +13 -18
  81. data/lib/badline/{computer → c64}/true_drives.rb +20 -31
  82. data/lib/badline/{computer.rb → c64.rb} +22 -80
  83. data/lib/badline/chips/cia/fast_forward.rb +62 -0
  84. data/lib/badline/chips/cia/interrupt_register.rb +145 -0
  85. data/lib/badline/chips/cia/saved_state.rb +49 -0
  86. data/lib/badline/chips/cia/serial/saved_state.rb +45 -0
  87. data/lib/badline/chips/cia/serial.rb +250 -0
  88. data/lib/badline/chips/cia/time_of_day.rb +232 -0
  89. data/lib/badline/chips/cia/timer.rb +208 -0
  90. data/lib/badline/chips/cia.rb +281 -0
  91. data/lib/badline/chips/cpu/addressing.rb +133 -0
  92. data/lib/badline/chips/cpu/boundary.rb +56 -0
  93. data/lib/badline/chips/cpu/core.rb +167 -0
  94. data/lib/badline/chips/cpu/instruction.rb +338 -0
  95. data/lib/badline/chips/cpu/instruction_set/arithmetic.rb +118 -0
  96. data/lib/badline/chips/cpu/instruction_set/bitwise.rb +130 -0
  97. data/lib/badline/chips/cpu/instruction_set/branch.rb +83 -0
  98. data/lib/badline/chips/cpu/instruction_set/flag.rb +67 -0
  99. data/lib/badline/chips/cpu/instruction_set/illegal.rb +274 -0
  100. data/lib/badline/chips/cpu/instruction_set/inc_dec.rb +73 -0
  101. data/lib/badline/chips/cpu/instruction_set/stack.rb +56 -0
  102. data/lib/badline/chips/cpu/instruction_set/transfer.rb +141 -0
  103. data/lib/badline/chips/cpu/instruction_set.rb +59 -0
  104. data/lib/badline/chips/cpu/interrupts.rb +108 -0
  105. data/lib/badline/chips/cpu/microcode.rb +179 -0
  106. data/lib/badline/chips/cpu/operations.rb +90 -0
  107. data/lib/badline/chips/cpu/port.rb +116 -0
  108. data/lib/badline/chips/cpu/saved_state.rb +117 -0
  109. data/lib/badline/chips/cpu/stack_operations.rb +84 -0
  110. data/lib/badline/chips/cpu/traps.rb +23 -0
  111. data/lib/badline/chips/cpu.rb +12 -0
  112. data/lib/badline/chips/sid/catch_up.rb +122 -0
  113. data/lib/badline/chips/sid/decimator.rb +66 -0
  114. data/lib/badline/chips/sid/envelope/saved_state.rb +70 -0
  115. data/lib/badline/chips/sid/envelope.rb +241 -0
  116. data/lib/badline/chips/sid/filter/saved_state.rb +42 -0
  117. data/lib/badline/chips/sid/filter.rb +241 -0
  118. data/lib/badline/chips/sid/saved_state.rb +52 -0
  119. data/lib/badline/chips/sid/voice.rb +79 -0
  120. data/lib/badline/chips/sid/waveform/combined.rb +128 -0
  121. data/lib/badline/chips/sid/waveform/fast_forward.rb +107 -0
  122. data/lib/badline/chips/sid/waveform/noise_writeback.rb +124 -0
  123. data/lib/badline/chips/sid/waveform/saved_state.rb +68 -0
  124. data/lib/badline/chips/sid/waveform.rb +307 -0
  125. data/lib/badline/chips/sid.rb +265 -0
  126. data/lib/badline/{c128 → chips}/vdc/memory.rb +1 -1
  127. data/lib/badline/{c128 → chips}/vdc/painter.rb +1 -1
  128. data/lib/badline/{c128 → chips}/vdc/raster.rb +1 -1
  129. data/lib/badline/chips/vdc/saved_state.rb +36 -0
  130. data/lib/badline/{c128 → chips}/vdc/window.rb +1 -1
  131. data/lib/badline/{c128 → chips}/vdc.rb +7 -31
  132. data/lib/badline/chips/via/control_lines.rb +124 -0
  133. data/lib/badline/chips/via/fast_forward.rb +55 -0
  134. data/lib/badline/chips/via/interrupt_register.rb +69 -0
  135. data/lib/badline/chips/via/saved_state.rb +44 -0
  136. data/lib/badline/chips/via/shift_register.rb +182 -0
  137. data/lib/badline/chips/via/timer1.rb +133 -0
  138. data/lib/badline/chips/via/timer2.rb +135 -0
  139. data/lib/badline/chips/via.rb +268 -0
  140. data/lib/badline/chips/vic/bank.rb +135 -0
  141. data/lib/badline/chips/vic/border_mask.rb +88 -0
  142. data/lib/badline/chips/vic/collisions.rb +117 -0
  143. data/lib/badline/chips/vic/color_patches.rb +49 -0
  144. data/lib/badline/chips/vic/display_state.rb +187 -0
  145. data/lib/badline/chips/vic/graphics_mode.rb +166 -0
  146. data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +40 -0
  147. data/lib/badline/chips/vic/graphics_shifter.rb +187 -0
  148. data/lib/badline/chips/vic/register_log.rb +83 -0
  149. data/lib/badline/chips/vic/registers.rb +197 -0
  150. data/lib/badline/chips/vic/saved_state.rb +82 -0
  151. data/lib/badline/chips/vic/sequencer/saved_state.rb +91 -0
  152. data/lib/badline/chips/vic/sequencer.rb +277 -0
  153. data/lib/badline/chips/vic/sequencer_output.rb +118 -0
  154. data/lib/badline/chips/vic/sprite/internal_bus.rb +70 -0
  155. data/lib/badline/chips/vic/sprite/saved_state.rb +66 -0
  156. data/lib/badline/chips/vic/sprite/shifter.rb +148 -0
  157. data/lib/badline/chips/vic/sprite/timing.rb +58 -0
  158. data/lib/badline/chips/vic/sprite.rb +339 -0
  159. data/lib/badline/chips/vic/sprites/saved_state.rb +59 -0
  160. data/lib/badline/chips/vic/sprites.rb +284 -0
  161. data/lib/badline/chips/vic.rb +783 -0
  162. data/lib/badline/chips/vic_i/painter/saved_state.rb +40 -0
  163. data/lib/badline/{vic20/vic → chips/vic_i}/painter.rb +6 -30
  164. data/lib/badline/{vic20/vic → chips/vic_i}/saved_state.rb +2 -2
  165. data/lib/badline/chips/vic_i/sound/output.rb +85 -0
  166. data/lib/badline/chips/vic_i/sound.rb +235 -0
  167. data/lib/badline/{vic20/vic → chips/vic_i}/text_window.rb +2 -2
  168. data/lib/badline/{vic20/vic.rb → chips/vic_i.rb} +5 -5
  169. data/lib/badline/chips/z80/arithmetic.rb +119 -0
  170. data/lib/badline/chips/z80/bit_page.rb +57 -0
  171. data/lib/badline/chips/z80/block_io.rb +60 -0
  172. data/lib/badline/chips/z80/block_transfers.rb +61 -0
  173. data/lib/badline/chips/z80/bus_cycles.rb +113 -0
  174. data/lib/badline/chips/z80/core.rb +115 -0
  175. data/lib/badline/chips/z80/exchanges.rb +52 -0
  176. data/lib/badline/chips/z80/extended_page.rb +91 -0
  177. data/lib/badline/chips/z80/flags.rb +39 -0
  178. data/lib/badline/chips/z80/interrupt_acceptance.rb +48 -0
  179. data/lib/badline/chips/z80/jumps.rb +84 -0
  180. data/lib/badline/chips/z80/loads.rb +100 -0
  181. data/lib/badline/chips/z80/main_page.rb +136 -0
  182. data/lib/badline/chips/z80/registers.rb +122 -0
  183. data/lib/badline/chips/z80/saved_state.rb +71 -0
  184. data/lib/badline/chips/z80/shifts.rb +66 -0
  185. data/lib/badline/chips/z80.rb +13 -0
  186. data/lib/badline/core.rb +27 -29
  187. data/lib/badline/drives/datasette/saved_state.rb +37 -0
  188. data/lib/badline/drives/datasette.rb +140 -0
  189. data/lib/badline/drives/engine/core.rb +149 -0
  190. data/lib/badline/drives/engine/fast_serial.rb +75 -0
  191. data/lib/badline/drives/engine/idle.rb +127 -0
  192. data/lib/badline/drives/engine/idle_cpu.rb +30 -0
  193. data/lib/badline/drives/engine/orbit.rb +81 -0
  194. data/lib/badline/drives/engine/serial_lines.rb +75 -0
  195. data/lib/badline/drives/engine/sleep.rb +114 -0
  196. data/lib/badline/drives/engine/vias.rb +86 -0
  197. data/lib/badline/drives/engine/watch.rb +152 -0
  198. data/lib/badline/drives/engine.rb +11 -0
  199. data/lib/badline/drives/model1541/bus.rb +88 -0
  200. data/lib/badline/drives/model1541/byte_ready.rb +50 -0
  201. data/lib/badline/drives/model1541/cpu.rb +13 -0
  202. data/lib/badline/drives/model1541/disk/saved_state.rb +88 -0
  203. data/lib/badline/drives/model1541/disk.rb +317 -0
  204. data/lib/badline/drives/model1541/disk_via.rb +38 -0
  205. data/lib/badline/drives/model1541/gcr.rb +59 -0
  206. data/lib/badline/drives/model1541/mechanism/saved_state.rb +72 -0
  207. data/lib/badline/drives/model1541/mechanism.rb +315 -0
  208. data/lib/badline/drives/model1541/rotation.rb +123 -0
  209. data/lib/badline/drives/model1541/saved_state.rb +44 -0
  210. data/lib/badline/drives/model1541/sector_reader.rb +78 -0
  211. data/lib/badline/drives/model1541/serial_port.rb +20 -0
  212. data/lib/badline/drives/model1541/track.rb +100 -0
  213. data/lib/badline/drives/model1541.rb +81 -0
  214. data/lib/badline/drives/model1571/bus.rb +113 -0
  215. data/lib/badline/drives/model1571/cpu.rb +13 -0
  216. data/lib/badline/drives/model1571/saved_state.rb +47 -0
  217. data/lib/badline/drives/model1571/serial_port.rb +39 -0
  218. data/lib/badline/drives/model1571/serial_via.rb +27 -0
  219. data/lib/badline/drives/model1571/wd1770.rb +122 -0
  220. data/lib/badline/drives/model1571.rb +142 -0
  221. data/lib/badline/drives/model1581/bus.rb +139 -0
  222. data/lib/badline/drives/model1581/cpu.rb +13 -0
  223. data/lib/badline/drives/model1581/disk/saved_state.rb +89 -0
  224. data/lib/badline/drives/model1581/disk.rb +137 -0
  225. data/lib/badline/drives/model1581/mechanism.rb +119 -0
  226. data/lib/badline/drives/model1581/saved_state.rb +42 -0
  227. data/lib/badline/drives/model1581/serial_port.rb +54 -0
  228. data/lib/badline/drives/model1581/slot.rb +79 -0
  229. data/lib/badline/drives/model1581/track.rb +102 -0
  230. data/lib/badline/drives/model1581/track_bytes.rb +77 -0
  231. data/lib/badline/drives/model1581/wd1772/registers.rb +91 -0
  232. data/lib/badline/drives/model1581/wd1772/saved_state.rb +68 -0
  233. data/lib/badline/drives/model1581/wd1772/sector_writes.rb +75 -0
  234. data/lib/badline/drives/model1581/wd1772/sectors.rb +152 -0
  235. data/lib/badline/drives/model1581/wd1772/stepping.rb +111 -0
  236. data/lib/badline/drives/model1581/wd1772/tracks.rb +127 -0
  237. data/lib/badline/drives/model1581/wd1772/turn.rb +75 -0
  238. data/lib/badline/drives/model1581/wd1772.rb +218 -0
  239. data/lib/badline/drives/model1581.rb +206 -0
  240. data/lib/badline/drives/serial_bus.rb +161 -0
  241. data/lib/badline/frontend/boot.rb +10 -61
  242. data/lib/badline/frontend/machine_window.rb +3 -1
  243. data/lib/badline/frontend/player/filter_view.rb +1 -1
  244. data/lib/badline/frontend/player/player_controller.rb +2 -2
  245. data/lib/badline/frontend/player/player_screen.rb +1 -0
  246. data/lib/badline/frontend/player/theme.rb +7 -7
  247. data/lib/badline/frontend/timeline.rb +3 -3
  248. data/lib/badline/input/control_ports.rb +78 -0
  249. data/lib/badline/input/joystick.rb +27 -0
  250. data/lib/badline/input/keyboard.rb +114 -0
  251. data/lib/badline/input.rb +3 -0
  252. data/lib/badline/kernal_trap/{drive → dos}/block_commands.rb +1 -1
  253. data/lib/badline/kernal_trap/dos/channel.rb +183 -0
  254. data/lib/badline/kernal_trap/{drive → dos}/channels.rb +1 -1
  255. data/lib/badline/kernal_trap/{drive → dos}/memory.rb +1 -1
  256. data/lib/badline/kernal_trap/{drive → dos}/parameters.rb +1 -1
  257. data/lib/badline/kernal_trap/{drive → dos}/status.rb +1 -1
  258. data/lib/badline/kernal_trap/{drive → dos}/write_file.rb +1 -1
  259. data/lib/badline/kernal_trap/{drive → dos}/writes.rb +1 -1
  260. data/lib/badline/kernal_trap/{drive.rb → dos.rb} +11 -10
  261. data/lib/badline/kernal_trap/{file.rb → file_routine.rb} +1 -1
  262. data/lib/badline/kernal_trap/load.rb +6 -6
  263. data/lib/badline/kernal_trap/save.rb +5 -5
  264. data/lib/badline/kernal_trap/serial.rb +7 -7
  265. data/lib/badline/kernal_trap.rb +2 -3
  266. data/lib/badline/machine/clocking.rb +50 -0
  267. data/lib/badline/machine.rb +63 -7
  268. data/lib/badline/media/boot_disk.rb +2 -2
  269. data/lib/badline/media/disk_list.rb +3 -3
  270. data/lib/badline/media/disk_set.rb +1 -1
  271. data/lib/badline/media/extensions.rb +33 -0
  272. data/lib/badline/media/program.rb +28 -0
  273. data/lib/badline/media/true_drive.rb +4 -3
  274. data/lib/badline/media/tune.rb +35 -0
  275. data/lib/badline/media/vic20_cartridge.rb +1 -7
  276. data/lib/badline/media/vic20_media.rb +38 -80
  277. data/lib/badline/media.rb +84 -81
  278. data/lib/badline/model.rb +1 -1
  279. data/lib/badline/options/event.rb +3 -1
  280. data/lib/badline/roms/README +6 -6
  281. data/lib/badline/sdl.rb +1 -0
  282. data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
  283. data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
  284. data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
  285. data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
  286. data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
  287. data/lib/badline/sid_tune.rb +6 -0
  288. data/lib/badline/snapshot/c128_setup.rb +43 -0
  289. data/lib/badline/snapshot/c64_setup.rb +79 -0
  290. data/lib/badline/snapshot/image.rb +5 -25
  291. data/lib/badline/snapshot/machine_state.rb +10 -2
  292. data/lib/badline/snapshot/setup.rb +22 -66
  293. data/lib/badline/snapshot/state.rb +8 -17
  294. data/lib/badline/snapshot/storage_setup.rb +68 -0
  295. data/lib/badline/snapshot/vic20_setup.rb +37 -0
  296. data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
  297. data/lib/badline/snapshot/vice/restore.rb +1 -1
  298. data/lib/badline/snapshot/vice/reu1764.rb +1 -1
  299. data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
  300. data/lib/badline/snapshot/vice.rb +7 -5
  301. data/lib/badline/snapshot.rb +13 -12
  302. data/lib/badline/storage/d64_image.rb +15 -9
  303. data/lib/badline/storage/d71_image.rb +0 -2
  304. data/lib/badline/storage/d81_image.rb +0 -2
  305. data/lib/badline/storage/disk_image/writing.rb +2 -2
  306. data/lib/badline/storage/disk_image.rb +3 -6
  307. data/lib/badline/storage/g64_image.rb +2 -2
  308. data/lib/badline/storage/host_directory.rb +0 -5
  309. data/lib/badline/storage/t64.rb +2 -8
  310. data/lib/badline/storage/unavailable.rb +1 -1
  311. data/lib/badline/storage.rb +0 -37
  312. data/lib/badline/support/c128_checkpoint.rb +39 -0
  313. data/lib/badline/support/checkpoint.rb +108 -0
  314. data/lib/badline/support/vic20_checkpoint.rb +38 -0
  315. data/lib/badline/support.rb +5 -0
  316. data/lib/badline/version.rb +1 -1
  317. data/lib/badline/vic20/attachments.rb +11 -81
  318. data/lib/badline/vic20/bus/saved_state.rb +35 -0
  319. data/lib/badline/vic20/bus.rb +3 -25
  320. data/lib/badline/vic20/cpu.rb +1 -1
  321. data/lib/badline/vic20/port_wiring.rb +4 -4
  322. data/lib/badline/vic20/saved_state.rb +16 -60
  323. data/lib/badline/vic20/user_via_ports.rb +2 -2
  324. data/lib/badline/vic20.rb +20 -51
  325. metadata +250 -221
  326. data/lib/badline/address_bus/extra_sids.rb +0 -30
  327. data/lib/badline/address_bus/fittings.rb +0 -38
  328. data/lib/badline/address_bus/pla.rb +0 -147
  329. data/lib/badline/address_bus/roms.rb +0 -39
  330. data/lib/badline/address_bus/saved_state.rb +0 -72
  331. data/lib/badline/address_bus/sid_slots.rb +0 -42
  332. data/lib/badline/address_bus.rb +0 -235
  333. data/lib/badline/cartridge/action_replay.rb +0 -126
  334. data/lib/badline/cartridge/atomic_power.rb +0 -24
  335. data/lib/badline/cartridge/bank.rb +0 -81
  336. data/lib/badline/cartridge/comal80.rb +0 -50
  337. data/lib/badline/cartridge/dinamic.rb +0 -34
  338. data/lib/badline/cartridge/easy_flash.rb +0 -145
  339. data/lib/badline/cartridge/epyx_fastload.rb +0 -79
  340. data/lib/badline/cartridge/final_cartridge3.rb +0 -89
  341. data/lib/badline/cartridge/flash.rb +0 -306
  342. data/lib/badline/cartridge/freezer.rb +0 -33
  343. data/lib/badline/cartridge/fun_play.rb +0 -38
  344. data/lib/badline/cartridge/g_mod2.rb +0 -74
  345. data/lib/badline/cartridge/game_system.rb +0 -40
  346. data/lib/badline/cartridge/geo_ram.rb +0 -79
  347. data/lib/badline/cartridge/kcs_power.rb +0 -75
  348. data/lib/badline/cartridge/mach5.rb +0 -32
  349. data/lib/badline/cartridge/magic_desk.rb +0 -30
  350. data/lib/badline/cartridge/ocean.rb +0 -35
  351. data/lib/badline/cartridge/pagefox.rb +0 -68
  352. data/lib/badline/cartridge/retro_replay.rb +0 -320
  353. data/lib/badline/cartridge/rex_utility.rb +0 -29
  354. data/lib/badline/cartridge/rgcd.rb +0 -58
  355. data/lib/badline/cartridge/simons_basic.rb +0 -35
  356. data/lib/badline/cartridge/standard.rb +0 -15
  357. data/lib/badline/cartridge/super_games.rb +0 -48
  358. data/lib/badline/cartridge/westermann.rb +0 -30
  359. data/lib/badline/cartridge/zaxxon.rb +0 -66
  360. data/lib/badline/cartridge.rb +0 -303
  361. data/lib/badline/checkpoint.rb +0 -98
  362. data/lib/badline/cia/fast_forward.rb +0 -60
  363. data/lib/badline/cia/interrupt_register.rb +0 -143
  364. data/lib/badline/cia/serial.rb +0 -275
  365. data/lib/badline/cia/state.rb +0 -47
  366. data/lib/badline/cia/timer.rb +0 -206
  367. data/lib/badline/cia.rb +0 -279
  368. data/lib/badline/color_memory.rb +0 -25
  369. data/lib/badline/computer/kernal_traps.rb +0 -29
  370. data/lib/badline/control_ports.rb +0 -76
  371. data/lib/badline/cpu/addressing.rb +0 -131
  372. data/lib/badline/cpu/boundary.rb +0 -54
  373. data/lib/badline/cpu/core.rb +0 -165
  374. data/lib/badline/cpu/microcode.rb +0 -177
  375. data/lib/badline/cpu/operations.rb +0 -88
  376. data/lib/badline/cpu/saved_state.rb +0 -115
  377. data/lib/badline/cpu/stack_operations.rb +0 -82
  378. data/lib/badline/cpu.rb +0 -10
  379. data/lib/badline/cycleable.rb +0 -38
  380. data/lib/badline/datasette.rb +0 -161
  381. data/lib/badline/drive/core.rb +0 -147
  382. data/lib/badline/drive/fast_serial.rb +0 -73
  383. data/lib/badline/drive/idle.rb +0 -125
  384. data/lib/badline/drive/idle_cpu.rb +0 -28
  385. data/lib/badline/drive/orbit.rb +0 -79
  386. data/lib/badline/drive/serial_lines.rb +0 -73
  387. data/lib/badline/drive/sleep.rb +0 -112
  388. data/lib/badline/drive/vias.rb +0 -84
  389. data/lib/badline/drive/watch.rb +0 -150
  390. data/lib/badline/drive.rb +0 -11
  391. data/lib/badline/drive1541/bus.rb +0 -86
  392. data/lib/badline/drive1541/byte_ready.rb +0 -48
  393. data/lib/badline/drive1541/cpu.rb +0 -11
  394. data/lib/badline/drive1541/disk.rb +0 -318
  395. data/lib/badline/drive1541/disk_state.rb +0 -94
  396. data/lib/badline/drive1541/disk_via.rb +0 -36
  397. data/lib/badline/drive1541/gcr.rb +0 -57
  398. data/lib/badline/drive1541/mechanism.rb +0 -313
  399. data/lib/badline/drive1541/mechanism_state.rb +0 -68
  400. data/lib/badline/drive1541/rotation.rb +0 -121
  401. data/lib/badline/drive1541/sector_reader.rb +0 -76
  402. data/lib/badline/drive1541/serial_port.rb +0 -18
  403. data/lib/badline/drive1541/track.rb +0 -98
  404. data/lib/badline/drive1541.rb +0 -111
  405. data/lib/badline/drive1571/bus.rb +0 -111
  406. data/lib/badline/drive1571/cpu.rb +0 -11
  407. data/lib/badline/drive1571/serial_port.rb +0 -37
  408. data/lib/badline/drive1571/serial_via.rb +0 -25
  409. data/lib/badline/drive1571/wd1770.rb +0 -120
  410. data/lib/badline/drive1571.rb +0 -175
  411. data/lib/badline/drive1581/bus.rb +0 -137
  412. data/lib/badline/drive1581/cpu.rb +0 -11
  413. data/lib/badline/drive1581/disk.rb +0 -135
  414. data/lib/badline/drive1581/disk_state.rb +0 -87
  415. data/lib/badline/drive1581/mechanism.rb +0 -117
  416. data/lib/badline/drive1581/serial_port.rb +0 -52
  417. data/lib/badline/drive1581/slot.rb +0 -78
  418. data/lib/badline/drive1581/track.rb +0 -100
  419. data/lib/badline/drive1581/track_bytes.rb +0 -75
  420. data/lib/badline/drive1581/wd1772/registers.rb +0 -89
  421. data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
  422. data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
  423. data/lib/badline/drive1581/wd1772/state.rb +0 -66
  424. data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
  425. data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
  426. data/lib/badline/drive1581/wd1772/turn.rb +0 -73
  427. data/lib/badline/drive1581/wd1772.rb +0 -216
  428. data/lib/badline/drive1581.rb +0 -233
  429. data/lib/badline/iec_bus.rb +0 -159
  430. data/lib/badline/instruction.rb +0 -334
  431. data/lib/badline/instruction_set/arithmetic.rb +0 -114
  432. data/lib/badline/instruction_set/bitwise.rb +0 -126
  433. data/lib/badline/instruction_set/branch.rb +0 -79
  434. data/lib/badline/instruction_set/flag.rb +0 -63
  435. data/lib/badline/instruction_set/illegal.rb +0 -270
  436. data/lib/badline/instruction_set/inc_dec.rb +0 -69
  437. data/lib/badline/instruction_set/stack.rb +0 -52
  438. data/lib/badline/instruction_set/transfer.rb +0 -137
  439. data/lib/badline/instruction_set.rb +0 -55
  440. data/lib/badline/interrupts.rb +0 -104
  441. data/lib/badline/joystick.rb +0 -25
  442. data/lib/badline/kernal_trap/channel.rb +0 -181
  443. data/lib/badline/keyboard.rb +0 -112
  444. data/lib/badline/ram_expansion/banking.rb +0 -30
  445. data/lib/badline/ram_expansion/plus256k.rb +0 -54
  446. data/lib/badline/ram_expansion/plus60k.rb +0 -43
  447. data/lib/badline/ram_expansion/unexpanded.rb +0 -28
  448. data/lib/badline/ram_expansion.rb +0 -21
  449. data/lib/badline/reu/dma/saved_state.rb +0 -50
  450. data/lib/badline/reu/dma.rb +0 -273
  451. data/lib/badline/reu/ram.rb +0 -107
  452. data/lib/badline/reu/saved_state.rb +0 -80
  453. data/lib/badline/reu/trigger.rb +0 -32
  454. data/lib/badline/reu.rb +0 -289
  455. data/lib/badline/sid/catch_up.rb +0 -120
  456. data/lib/badline/sid/decimator.rb +0 -64
  457. data/lib/badline/sid/envelope/state.rb +0 -68
  458. data/lib/badline/sid/envelope.rb +0 -239
  459. data/lib/badline/sid/filter.rb +0 -263
  460. data/lib/badline/sid/voice.rb +0 -77
  461. data/lib/badline/sid/waveform/combined.rb +0 -126
  462. data/lib/badline/sid/waveform/fast_forward.rb +0 -105
  463. data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
  464. data/lib/badline/sid/waveform/state.rb +0 -66
  465. data/lib/badline/sid/waveform.rb +0 -305
  466. data/lib/badline/sid.rb +0 -303
  467. data/lib/badline/time_of_day.rb +0 -228
  468. data/lib/badline/traps.rb +0 -19
  469. data/lib/badline/via/control_lines.rb +0 -122
  470. data/lib/badline/via/fast_forward.rb +0 -53
  471. data/lib/badline/via/interrupt_register.rb +0 -67
  472. data/lib/badline/via/shift_register.rb +0 -180
  473. data/lib/badline/via/state.rb +0 -42
  474. data/lib/badline/via/timer1.rb +0 -131
  475. data/lib/badline/via/timer2.rb +0 -133
  476. data/lib/badline/via.rb +0 -266
  477. data/lib/badline/vic/bank.rb +0 -133
  478. data/lib/badline/vic/border_mask.rb +0 -86
  479. data/lib/badline/vic/collisions.rb +0 -115
  480. data/lib/badline/vic/color_patches.rb +0 -47
  481. data/lib/badline/vic/display_state.rb +0 -185
  482. data/lib/badline/vic/graphics_mode.rb +0 -164
  483. data/lib/badline/vic/graphics_shifter.rb +0 -207
  484. data/lib/badline/vic/register_log.rb +0 -81
  485. data/lib/badline/vic/registers.rb +0 -195
  486. data/lib/badline/vic/sequencer.rb +0 -275
  487. data/lib/badline/vic/sequencer_output.rb +0 -116
  488. data/lib/badline/vic/sequencer_state.rb +0 -89
  489. data/lib/badline/vic/sprite/internal_bus.rb +0 -68
  490. data/lib/badline/vic/sprite/saved_state.rb +0 -64
  491. data/lib/badline/vic/sprite/shifter.rb +0 -146
  492. data/lib/badline/vic/sprite/timing.rb +0 -56
  493. data/lib/badline/vic/sprite.rb +0 -337
  494. data/lib/badline/vic/sprites.rb +0 -282
  495. data/lib/badline/vic/sprites_state.rb +0 -57
  496. data/lib/badline/vic.rb +0 -852
  497. data/lib/badline/vic20/sound/output.rb +0 -83
  498. data/lib/badline/vic20/sound.rb +0 -233
  499. data/lib/badline/z80/arithmetic.rb +0 -117
  500. data/lib/badline/z80/bit_page.rb +0 -55
  501. data/lib/badline/z80/block_io.rb +0 -58
  502. data/lib/badline/z80/block_transfers.rb +0 -59
  503. data/lib/badline/z80/bus_cycles.rb +0 -111
  504. data/lib/badline/z80/core.rb +0 -113
  505. data/lib/badline/z80/exchanges.rb +0 -50
  506. data/lib/badline/z80/extended_page.rb +0 -89
  507. data/lib/badline/z80/flags.rb +0 -37
  508. data/lib/badline/z80/interrupt_acceptance.rb +0 -46
  509. data/lib/badline/z80/jumps.rb +0 -82
  510. data/lib/badline/z80/loads.rb +0 -98
  511. data/lib/badline/z80/main_page.rb +0 -134
  512. data/lib/badline/z80/registers.rb +0 -120
  513. data/lib/badline/z80/saved_state.rb +0 -69
  514. data/lib/badline/z80/shifts.rb +0 -64
  515. data/lib/badline/z80.rb +0 -11
  516. /data/lib/badline/snapshot/{fields.rb → vice/fields.rb} +0 -0
@@ -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,179 @@
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
+ def self.write_mask(plan)
121
+ plan.map { |step| WRITE_STEPS.include?(step) }.freeze
122
+ end
123
+
124
+ def self.build(instruction)
125
+ plan = plan_for(instruction)
126
+ new(instruction.name, plan, write_mask(plan),
127
+ instruction.boundary_cycle?)
128
+ end
129
+
130
+ def self.plan_for(instruction)
131
+ case instruction.name
132
+ when :jmp
133
+ if instruction.addressing_mode == :indirect
134
+ JMP_INDIRECT_PLAN
135
+ else
136
+ JMP_ABSOLUTE_PLAN
137
+ end
138
+ when *BRANCHES then BRANCH_PLAN
139
+ else SPECIAL_PLANS[instruction.name] || addressed_plan(instruction)
140
+ end
141
+ end
142
+
143
+ def self.addressed_plan(instruction)
144
+ mode = instruction.addressing_mode
145
+ case mode
146
+ when :implied then IMPLIED_PLAN
147
+ when :accumulator then ACCUMULATOR_PLAN
148
+ when :immediate then IMMEDIATE_PLAN
149
+ else
150
+ [:op_fetch, *ADDRESSING_STEPS.fetch(mode),
151
+ *OPERATION_STEPS.fetch(operation_kind(instruction.name))].freeze
152
+ end
153
+ end
154
+
155
+ def self.operation_kind(name)
156
+ return :write if STORES.include?(name)
157
+ return :rmw if READ_MODIFY_WRITES.include?(name)
158
+
159
+ :read
160
+ end
161
+
162
+ private_class_method :build, :plan_for, :addressed_plan, :operation_kind
163
+ end
164
+
165
+ FETCH_WRITES = Microcode.write_mask(FETCH_PLAN)
166
+ INTERRUPT_WRITES = Microcode.write_mask(INTERRUPT_PLAN)
167
+ JAMMED_WRITES = Microcode.write_mask(JAMMED_PLAN)
168
+ MICROCODE = Microcode.table
169
+
170
+ # The instruction methods, in a fixed order a snapshot names them by.
171
+ OPERATIONS = MICROCODE.map(&:operation).uniq.freeze
172
+
173
+ # The plans a snapshot names that aren't an opcode's.
174
+ FETCH = -1
175
+ INTERRUPT = -2
176
+ JAMMED = -3
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ # Steps that read or write an instruction's operand once its address is
7
+ # known, plus the branch and JAM steps.
8
+ module Operations
9
+ private
10
+
11
+ def op_mem_read
12
+ send(@operation, @address, @memory.peek(@address))
13
+ end_instruction
14
+ end
15
+
16
+ def op_mem_write
17
+ send(@operation, @address, nil)
18
+ end_instruction
19
+ end
20
+
21
+ # Read-modify-write instructions (ASL, INC and the like) take three
22
+ # cycles here: read the value and compute the result, write the
23
+ # original value back, then write the result. The instruction method
24
+ # hands the result over through #write_modified.
25
+ def op_rmw_read
26
+ @value = @memory.peek(@address)
27
+ send(@operation, @address, @value)
28
+ end
29
+
30
+ def op_rmw_write_original
31
+ @memory.poke(@address, @value)
32
+ end
33
+
34
+ def op_rmw_write_result
35
+ @memory.poke(@address, @rmw_result)
36
+ end_instruction
37
+ end
38
+
39
+ def op_relative
40
+ offset = @memory.peek(@program_counter)
41
+ @program_counter = (@program_counter + 1) & 0xffff
42
+ @address = (@program_counter + signed_int8(offset)) & 0xffff
43
+ @branch_taken = false
44
+ send(@operation, @address, nil)
45
+ if @branch_taken
46
+ # A taken branch within the same page doesn't poll on its last cycle.
47
+ @skip_poll = true if same_page?
48
+ else
49
+ end_instruction
50
+ end
51
+ end
52
+
53
+ def op_branch
54
+ @memory.peek(@program_counter)
55
+ return unless same_page?
56
+
57
+ @program_counter = @address
58
+ end_instruction
59
+ end
60
+
61
+ # A branch to another page spends an extra cycle reading from the target's low byte on the old page.
62
+ def op_branch_fixup
63
+ @memory.peek((@program_counter & 0xff00) | (@address & 0xff))
64
+ @program_counter = @address
65
+ end_instruction
66
+ end
67
+
68
+ def same_page?
69
+ @address >> 8 == @program_counter >> 8
70
+ end
71
+
72
+ # Runs once for each address in JAM_ADDRESSES. By the time a step runs, @index already points at the next one.
73
+ def op_jam
74
+ position = @index - 2
75
+ @memory.peek(JAM_ADDRESSES[position - 1])
76
+ return unless position == JAM_ADDRESSES.length
77
+
78
+ @plan = JAMMED_PLAN
79
+ @writes = JAMMED_WRITES
80
+ @index = 0
81
+ end
82
+
83
+ def op_jammed
84
+ @index = 0
85
+ @memory.peek(0xffff)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ # The I/O port of the 6510 and the 8502 at $00 (direction) and $01
7
+ # (data). An input pin reads its pull-up, P4 reads the datasette's sense
8
+ # line, and a pin without a pull-up holds the charge last driven onto
9
+ # it. P5 drives the datasette motor through an inverter: low runs it.
10
+ #
11
+ # The 6510 has pull-ups on P0-P2 and P4 and leaves P3, P6 and P7
12
+ # floating. The 8502 adds a CAPS LOCK pin on P6, high while the key is
13
+ # up.
14
+ class Port
15
+ TAPE_SENSE = 0b0001_0000
16
+
17
+ # The direction and output registers, and the charge on the floating
18
+ # pins.
19
+ attr_reader :ddr, :data, :floating
20
+
21
+ # The port as the banking logic reads it (PortStatus).
22
+ attr_reader :status
23
+
24
+ # Whether CAPS LOCK is down, holding its pin low.
25
+ attr_reader :caps_lock
26
+
27
+ # +pullups+ and +floating+ are the masks of the pins with a pull-up and
28
+ # of the floating ones, and +caps_lock+ the CAPS LOCK pin's, 0 where
29
+ # there is none.
30
+ def initialize(datasette, pullups:, floating:, caps_lock: 0)
31
+ @datasette = datasette
32
+ @pullups = pullups
33
+ @floating_mask = floating
34
+ @caps_lock_pin = caps_lock
35
+ @caps_lock = false
36
+ @ddr = 0x00
37
+ @data = 0x00
38
+ @floating = 0x00
39
+ @change_handler = nil
40
+ @status = PortStatus.new(%i[basic kernal io tape_out tape_switch tape_motor], value: input_value)
41
+ end
42
+
43
+ # Calls the block after every write to the port, for the bus to remap
44
+ # its banks.
45
+ def on_change(&handler)
46
+ @change_handler = handler
47
+ end
48
+
49
+ # What a read of $01 sees.
50
+ def value = @status.value
51
+
52
+ def write_ddr(value)
53
+ @ddr = value
54
+ update
55
+ end
56
+
57
+ def write_data(value)
58
+ @data = value
59
+ update
60
+ end
61
+
62
+ # The RES line clears the direction and output registers. The floating
63
+ # pins keep their charge.
64
+ def reset!
65
+ @ddr = 0x00
66
+ @data = 0x00
67
+ update
68
+ end
69
+
70
+ # Sets the registers and the charge, as a snapshot restores them, and
71
+ # drives the motor and calls the change handler as a write does.
72
+ def restore(ddr, data, floating)
73
+ load(ddr, data, floating)
74
+ update
75
+ end
76
+
77
+ # Sets the registers and the charge, as a snapshot loads them, without
78
+ # driving the motor or calling the change handler.
79
+ def load(ddr, data, floating)
80
+ @ddr = ddr
81
+ @data = data
82
+ @floating = floating
83
+ refresh
84
+ end
85
+
86
+ # Holds CAPS LOCK down, or lets it up.
87
+ def caps_lock=(down)
88
+ @caps_lock = down
89
+ refresh
90
+ end
91
+
92
+ # Reads the inputs again, after the datasette's sense line changes.
93
+ def refresh
94
+ @status.value = input_value
95
+ end
96
+
97
+ private
98
+
99
+ def update
100
+ driven = @ddr & @floating_mask
101
+ @floating = (@floating & ~driven) | (@data & driven)
102
+ refresh
103
+ @datasette.motor = !@status.tape_motor?
104
+ @change_handler&.call
105
+ end
106
+
107
+ def input_value
108
+ input = @pullups | (@floating & @floating_mask)
109
+ input |= @caps_lock_pin unless @caps_lock
110
+ input &= ~TAPE_SENSE if @datasette.sense_low?
111
+ (@data & @ddr) | (input & ~@ddr & 0xff)
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ # The CPU's state for a snapshot: the registers, the interrupt lines and
7
+ # their pipeline, and the step of the instruction in flight, named by
8
+ # where its plan comes from.
9
+ module SavedState
10
+ # Writes everything the CPU holds, down to the step of the instruction
11
+ # it is in, for load_state to put back. The interrupt lines, BA stalls
12
+ # and SO are state too; the traps and the debug flag belong to the host.
13
+ def save_state(out)
14
+ out.marker("CPU")
15
+ out.int(@program_counter).int(@stack_pointer).int(@a).int(@x).int(@y).int(@status.value)
16
+ [@irq, @nmi, @irq_sample, @irq_pending, @nmi_sample, @nmi_pending, @skip_poll, @boundary_irq,
17
+ @boundary_nmi, @boundary_crossed, @brk, @pending_write, @so_high, @optional_dummy,
18
+ @branch_taken].each { |flag| out.boolean(flag) }
19
+ out.optional_int(@interrupt).optional_int(@stalled_at).int(@cycles).int(@instructions)
20
+ out.int(plan_source).int(@index).int(OPERATIONS.index(@operation) || -1)
21
+ [@address, @dummy_address, @value, @rmw_result, @pointer].each { |register| out.optional_int(register) }
22
+ end
23
+
24
+ def load_state(input)
25
+ input.marker("CPU")
26
+ @program_counter = input.int
27
+ @stack_pointer = input.int
28
+ @a = input.int
29
+ @x = input.int
30
+ @y = input.int
31
+ @status.value = input.int
32
+ load_flags(input)
33
+ @interrupt = input.optional_int
34
+ @stalled_at = input.optional_int
35
+ @cycles = input.int
36
+ @instructions = input.int
37
+ load_plan(input.int)
38
+ @index = input.int
39
+ operation = input.int
40
+ @operation = operation.negative? ? nil : OPERATIONS.fetch(operation)
41
+ load_working_registers(input)
42
+ end
43
+
44
+ # Drops the instruction or interrupt sequence in flight and the
45
+ # interrupt lines' pipeline, so the next cycle fetches an opcode at the
46
+ # program counter, and sets the cycle count. A snapshot taken between
47
+ # instructions, as VICE's are, restores through here.
48
+ def resume(cycles)
49
+ @irq_sample = @irq_pending = false
50
+ @nmi_sample = @nmi_pending = false
51
+ @skip_poll = false
52
+ @interrupt = nil
53
+ @brk = false
54
+ @stalled_at = nil
55
+ @cycles = cycles
56
+ end_sequence
57
+ @pending_write = false
58
+ end
59
+
60
+ private
61
+
62
+ # Where the plan running comes from: an opcode's microcode, or FETCH,
63
+ # INTERRUPT or JAMMED below it.
64
+ def plan_source
65
+ return FETCH if @plan.equal?(FETCH_PLAN)
66
+ return INTERRUPT if @plan.equal?(INTERRUPT_PLAN)
67
+ return JAMMED if @plan.equal?(JAMMED_PLAN)
68
+
69
+ MICROCODE.index { |micro| micro.plan.equal?(@plan) && micro.operation == @operation } ||
70
+ MICROCODE.index { |micro| micro.plan.equal?(@plan) }
71
+ end
72
+
73
+ def load_plan(source)
74
+ case source
75
+ when FETCH then use_plan(FETCH_PLAN, FETCH_WRITES)
76
+ when INTERRUPT then use_plan(INTERRUPT_PLAN, INTERRUPT_WRITES)
77
+ when JAMMED then use_plan(JAMMED_PLAN, JAMMED_WRITES)
78
+ else
79
+ micro = MICROCODE.fetch(source)
80
+ use_plan(micro.plan, micro.writes)
81
+ end
82
+ end
83
+
84
+ def use_plan(plan, writes)
85
+ @plan = plan
86
+ @writes = writes
87
+ end
88
+
89
+ def load_flags(input)
90
+ @irq = input.boolean?
91
+ @nmi = input.boolean?
92
+ @irq_sample = input.boolean?
93
+ @irq_pending = input.boolean?
94
+ @nmi_sample = input.boolean?
95
+ @nmi_pending = input.boolean?
96
+ @skip_poll = input.boolean?
97
+ @boundary_irq = input.boolean?
98
+ @boundary_nmi = input.boolean?
99
+ @boundary_crossed = input.boolean?
100
+ @brk = input.boolean?
101
+ @pending_write = input.boolean?
102
+ @so_high = input.boolean?
103
+ @optional_dummy = input.boolean?
104
+ @branch_taken = input.boolean?
105
+ end
106
+
107
+ def load_working_registers(input)
108
+ @address = input.optional_int
109
+ @dummy_address = input.optional_int
110
+ @value = input.optional_int
111
+ @rmw_result = input.optional_int
112
+ @pointer = input.optional_int
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ # Steps that use the stack: PHA, PHP, PLA, PLP, JSR, RTS and RTI.
7
+ module StackOperations
8
+ private
9
+
10
+ def op_push_exec
11
+ send(@operation, nil, nil)
12
+ end_instruction
13
+ end
14
+
15
+ def op_pull_dummy
16
+ @memory.peek(stack_address)
17
+ end
18
+
19
+ def op_pull_exec
20
+ send(@operation, nil, nil)
21
+ end_instruction
22
+ end
23
+
24
+ def op_pull_low
25
+ @stack_pointer = (@stack_pointer + 1) & 0xff
26
+ @address = @memory.peek(stack_address)
27
+ end
28
+
29
+ def op_pull_high
30
+ @stack_pointer = (@stack_pointer + 1) & 0xff
31
+ @address |= @memory.peek(stack_address) << 8
32
+ end
33
+
34
+ def op_rts_fixup
35
+ @memory.peek(@address)
36
+ @program_counter = (@address + 1) & 0xffff
37
+ end_instruction
38
+ end
39
+
40
+ def op_rti_pull_status
41
+ @stack_pointer = (@stack_pointer + 1) & 0xff
42
+ @status.value = @memory.peek(stack_address)
43
+ @status.break = false
44
+ end
45
+
46
+ def op_rti_pull_high
47
+ @stack_pointer = (@stack_pointer + 1) & 0xff
48
+ @program_counter = @address | (@memory.peek(stack_address) << 8)
49
+ end_instruction
50
+ end
51
+
52
+ def op_jsr_low
53
+ @address = @memory.peek(@program_counter)
54
+ @program_counter = (@program_counter + 2) & 0xffff
55
+ end
56
+
57
+ def op_jsr_dummy
58
+ @memory.peek(stack_address)
59
+ end
60
+
61
+ def op_jsr_push_high
62
+ @memory.poke(stack_address, high_byte(return_address))
63
+ end
64
+
65
+ def op_jsr_push_low
66
+ @memory.poke(stack_address(-1), low_byte(return_address))
67
+ @stack_pointer = (@stack_pointer - 2) & 0xff
68
+ end
69
+
70
+ # JSR reads its target's high byte after pushing the return address.
71
+ # A JSR running from the stack page can overwrite its own operand
72
+ # with that push, and then jumps to the overwritten address.
73
+ def op_jsr_high
74
+ @program_counter = @address | (@memory.peek(return_address) << 8)
75
+ end_instruction
76
+ end
77
+
78
+ def return_address
79
+ (@program_counter - 1) & 0xffff
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end