badline 0.12.1 → 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 (505) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -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 +3 -3
  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} +1 -1
  17. data/lib/badline/c128/bus.rb +18 -17
  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 +14 -9
  27. data/lib/badline/{computer → c64}/attachments.rb +4 -6
  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/{computer → c64}/common.rb +2 -2
  73. data/lib/badline/{computer → c64}/kernal_traps.rb +18 -18
  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 +11 -11
  82. data/lib/badline/{computer.rb → c64.rb} +18 -13
  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 +26 -28
  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 +3 -3
  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/load.rb +5 -5
  262. data/lib/badline/kernal_trap/save.rb +4 -4
  263. data/lib/badline/kernal_trap/serial.rb +7 -7
  264. data/lib/badline/kernal_trap.rb +1 -2
  265. data/lib/badline/machine/clocking.rb +1 -1
  266. data/lib/badline/machine.rb +5 -5
  267. data/lib/badline/media/true_drive.rb +1 -1
  268. data/lib/badline/media/tune.rb +2 -2
  269. data/lib/badline/media.rb +3 -3
  270. data/lib/badline/model.rb +1 -1
  271. data/lib/badline/roms/README +6 -6
  272. data/lib/badline/sdl.rb +1 -0
  273. data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
  274. data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
  275. data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
  276. data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
  277. data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
  278. data/lib/badline/sid_tune.rb +6 -0
  279. data/lib/badline/snapshot/c128_setup.rb +43 -0
  280. data/lib/badline/snapshot/c64_setup.rb +79 -0
  281. data/lib/badline/snapshot/image.rb +5 -25
  282. data/lib/badline/snapshot/machine_state.rb +1 -1
  283. data/lib/badline/snapshot/setup.rb +22 -66
  284. data/lib/badline/snapshot/state.rb +8 -18
  285. data/lib/badline/snapshot/storage_setup.rb +68 -0
  286. data/lib/badline/snapshot/vic20_setup.rb +37 -0
  287. data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
  288. data/lib/badline/snapshot/vice/restore.rb +1 -1
  289. data/lib/badline/snapshot/vice/reu1764.rb +1 -1
  290. data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
  291. data/lib/badline/snapshot/vice.rb +7 -5
  292. data/lib/badline/snapshot.rb +3 -4
  293. data/lib/badline/storage/d64_image.rb +0 -2
  294. data/lib/badline/storage/d71_image.rb +0 -2
  295. data/lib/badline/storage/d81_image.rb +0 -2
  296. data/lib/badline/storage/disk_image.rb +3 -6
  297. data/lib/badline/storage/g64_image.rb +2 -2
  298. data/lib/badline/storage/host_directory.rb +0 -5
  299. data/lib/badline/storage/t64.rb +2 -8
  300. data/lib/badline/storage/unavailable.rb +1 -1
  301. data/lib/badline/storage.rb +0 -37
  302. data/lib/badline/support/c128_checkpoint.rb +39 -0
  303. data/lib/badline/support/checkpoint.rb +108 -0
  304. data/lib/badline/support/vic20_checkpoint.rb +38 -0
  305. data/lib/badline/support.rb +5 -0
  306. data/lib/badline/version.rb +1 -1
  307. data/lib/badline/vic20/attachments.rb +5 -5
  308. data/lib/badline/vic20/bus/saved_state.rb +35 -0
  309. data/lib/badline/vic20/bus.rb +3 -25
  310. data/lib/badline/vic20/cpu.rb +1 -1
  311. data/lib/badline/vic20/port_wiring.rb +4 -4
  312. data/lib/badline/vic20/saved_state.rb +16 -30
  313. data/lib/badline/vic20/user_via_ports.rb +2 -2
  314. data/lib/badline/vic20.rb +19 -14
  315. metadata +245 -221
  316. data/lib/badline/address_bus/extra_sids.rb +0 -30
  317. data/lib/badline/address_bus/fittings.rb +0 -38
  318. data/lib/badline/address_bus/pla.rb +0 -147
  319. data/lib/badline/address_bus/roms.rb +0 -39
  320. data/lib/badline/address_bus/saved_state.rb +0 -66
  321. data/lib/badline/address_bus/sid_slots.rb +0 -42
  322. data/lib/badline/address_bus.rb +0 -216
  323. data/lib/badline/cartridge/action_replay.rb +0 -126
  324. data/lib/badline/cartridge/atomic_power.rb +0 -24
  325. data/lib/badline/cartridge/bank.rb +0 -81
  326. data/lib/badline/cartridge/comal80.rb +0 -50
  327. data/lib/badline/cartridge/dinamic.rb +0 -34
  328. data/lib/badline/cartridge/easy_flash.rb +0 -145
  329. data/lib/badline/cartridge/epyx_fastload.rb +0 -79
  330. data/lib/badline/cartridge/final_cartridge3.rb +0 -89
  331. data/lib/badline/cartridge/flash.rb +0 -306
  332. data/lib/badline/cartridge/freezer.rb +0 -33
  333. data/lib/badline/cartridge/fun_play.rb +0 -38
  334. data/lib/badline/cartridge/g_mod2.rb +0 -74
  335. data/lib/badline/cartridge/game_system.rb +0 -40
  336. data/lib/badline/cartridge/geo_ram.rb +0 -79
  337. data/lib/badline/cartridge/kcs_power.rb +0 -75
  338. data/lib/badline/cartridge/mach5.rb +0 -32
  339. data/lib/badline/cartridge/magic_desk.rb +0 -30
  340. data/lib/badline/cartridge/ocean.rb +0 -35
  341. data/lib/badline/cartridge/pagefox.rb +0 -68
  342. data/lib/badline/cartridge/retro_replay.rb +0 -320
  343. data/lib/badline/cartridge/rex_utility.rb +0 -29
  344. data/lib/badline/cartridge/rgcd.rb +0 -58
  345. data/lib/badline/cartridge/simons_basic.rb +0 -35
  346. data/lib/badline/cartridge/standard.rb +0 -15
  347. data/lib/badline/cartridge/super_games.rb +0 -48
  348. data/lib/badline/cartridge/westermann.rb +0 -30
  349. data/lib/badline/cartridge/zaxxon.rb +0 -66
  350. data/lib/badline/cartridge.rb +0 -303
  351. data/lib/badline/checkpoint.rb +0 -98
  352. data/lib/badline/cia/fast_forward.rb +0 -60
  353. data/lib/badline/cia/interrupt_register.rb +0 -143
  354. data/lib/badline/cia/serial.rb +0 -275
  355. data/lib/badline/cia/state.rb +0 -47
  356. data/lib/badline/cia/timer.rb +0 -206
  357. data/lib/badline/cia.rb +0 -279
  358. data/lib/badline/color_memory.rb +0 -25
  359. data/lib/badline/control_ports.rb +0 -76
  360. data/lib/badline/cpu/addressing.rb +0 -131
  361. data/lib/badline/cpu/boundary.rb +0 -54
  362. data/lib/badline/cpu/core.rb +0 -165
  363. data/lib/badline/cpu/microcode.rb +0 -177
  364. data/lib/badline/cpu/operations.rb +0 -88
  365. data/lib/badline/cpu/saved_state.rb +0 -115
  366. data/lib/badline/cpu/stack_operations.rb +0 -82
  367. data/lib/badline/cpu.rb +0 -10
  368. data/lib/badline/cpu_port.rb +0 -112
  369. data/lib/badline/datasette.rb +0 -161
  370. data/lib/badline/drive/core.rb +0 -147
  371. data/lib/badline/drive/fast_serial.rb +0 -73
  372. data/lib/badline/drive/idle.rb +0 -125
  373. data/lib/badline/drive/idle_cpu.rb +0 -28
  374. data/lib/badline/drive/orbit.rb +0 -79
  375. data/lib/badline/drive/serial_lines.rb +0 -73
  376. data/lib/badline/drive/sleep.rb +0 -112
  377. data/lib/badline/drive/vias.rb +0 -84
  378. data/lib/badline/drive/watch.rb +0 -150
  379. data/lib/badline/drive.rb +0 -11
  380. data/lib/badline/drive1541/bus.rb +0 -86
  381. data/lib/badline/drive1541/byte_ready.rb +0 -48
  382. data/lib/badline/drive1541/cpu.rb +0 -11
  383. data/lib/badline/drive1541/disk.rb +0 -315
  384. data/lib/badline/drive1541/disk_state.rb +0 -86
  385. data/lib/badline/drive1541/disk_via.rb +0 -36
  386. data/lib/badline/drive1541/gcr.rb +0 -57
  387. data/lib/badline/drive1541/mechanism.rb +0 -313
  388. data/lib/badline/drive1541/mechanism_state.rb +0 -68
  389. data/lib/badline/drive1541/rotation.rb +0 -121
  390. data/lib/badline/drive1541/sector_reader.rb +0 -76
  391. data/lib/badline/drive1541/serial_port.rb +0 -18
  392. data/lib/badline/drive1541/track.rb +0 -98
  393. data/lib/badline/drive1541.rb +0 -111
  394. data/lib/badline/drive1571/bus.rb +0 -111
  395. data/lib/badline/drive1571/cpu.rb +0 -11
  396. data/lib/badline/drive1571/serial_port.rb +0 -37
  397. data/lib/badline/drive1571/serial_via.rb +0 -25
  398. data/lib/badline/drive1571/wd1770.rb +0 -120
  399. data/lib/badline/drive1571.rb +0 -175
  400. data/lib/badline/drive1581/bus.rb +0 -137
  401. data/lib/badline/drive1581/cpu.rb +0 -11
  402. data/lib/badline/drive1581/disk.rb +0 -135
  403. data/lib/badline/drive1581/disk_state.rb +0 -87
  404. data/lib/badline/drive1581/mechanism.rb +0 -117
  405. data/lib/badline/drive1581/serial_port.rb +0 -52
  406. data/lib/badline/drive1581/slot.rb +0 -78
  407. data/lib/badline/drive1581/track.rb +0 -100
  408. data/lib/badline/drive1581/track_bytes.rb +0 -75
  409. data/lib/badline/drive1581/wd1772/registers.rb +0 -89
  410. data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
  411. data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
  412. data/lib/badline/drive1581/wd1772/state.rb +0 -66
  413. data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
  414. data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
  415. data/lib/badline/drive1581/wd1772/turn.rb +0 -73
  416. data/lib/badline/drive1581/wd1772.rb +0 -216
  417. data/lib/badline/drive1581.rb +0 -233
  418. data/lib/badline/iec_bus.rb +0 -159
  419. data/lib/badline/instruction.rb +0 -334
  420. data/lib/badline/instruction_set/arithmetic.rb +0 -114
  421. data/lib/badline/instruction_set/bitwise.rb +0 -126
  422. data/lib/badline/instruction_set/branch.rb +0 -79
  423. data/lib/badline/instruction_set/flag.rb +0 -63
  424. data/lib/badline/instruction_set/illegal.rb +0 -270
  425. data/lib/badline/instruction_set/inc_dec.rb +0 -69
  426. data/lib/badline/instruction_set/stack.rb +0 -52
  427. data/lib/badline/instruction_set/transfer.rb +0 -137
  428. data/lib/badline/instruction_set.rb +0 -55
  429. data/lib/badline/interrupts.rb +0 -104
  430. data/lib/badline/joystick.rb +0 -25
  431. data/lib/badline/kernal_trap/channel.rb +0 -181
  432. data/lib/badline/keyboard.rb +0 -112
  433. data/lib/badline/ram_expansion/banking.rb +0 -30
  434. data/lib/badline/ram_expansion/plus256k.rb +0 -54
  435. data/lib/badline/ram_expansion/plus60k.rb +0 -43
  436. data/lib/badline/ram_expansion/unexpanded.rb +0 -28
  437. data/lib/badline/ram_expansion.rb +0 -21
  438. data/lib/badline/reu/dma/saved_state.rb +0 -50
  439. data/lib/badline/reu/dma.rb +0 -273
  440. data/lib/badline/reu/ram.rb +0 -107
  441. data/lib/badline/reu/saved_state.rb +0 -80
  442. data/lib/badline/reu/trigger.rb +0 -32
  443. data/lib/badline/reu.rb +0 -289
  444. data/lib/badline/sid/catch_up.rb +0 -120
  445. data/lib/badline/sid/decimator.rb +0 -64
  446. data/lib/badline/sid/envelope/state.rb +0 -68
  447. data/lib/badline/sid/envelope.rb +0 -239
  448. data/lib/badline/sid/filter.rb +0 -263
  449. data/lib/badline/sid/voice.rb +0 -77
  450. data/lib/badline/sid/waveform/combined.rb +0 -126
  451. data/lib/badline/sid/waveform/fast_forward.rb +0 -105
  452. data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
  453. data/lib/badline/sid/waveform/state.rb +0 -66
  454. data/lib/badline/sid/waveform.rb +0 -305
  455. data/lib/badline/sid.rb +0 -303
  456. data/lib/badline/time_of_day.rb +0 -228
  457. data/lib/badline/traps.rb +0 -19
  458. data/lib/badline/via/control_lines.rb +0 -122
  459. data/lib/badline/via/fast_forward.rb +0 -53
  460. data/lib/badline/via/interrupt_register.rb +0 -67
  461. data/lib/badline/via/shift_register.rb +0 -180
  462. data/lib/badline/via/state.rb +0 -42
  463. data/lib/badline/via/timer1.rb +0 -131
  464. data/lib/badline/via/timer2.rb +0 -133
  465. data/lib/badline/via.rb +0 -266
  466. data/lib/badline/vic/bank.rb +0 -133
  467. data/lib/badline/vic/border_mask.rb +0 -86
  468. data/lib/badline/vic/collisions.rb +0 -115
  469. data/lib/badline/vic/color_patches.rb +0 -47
  470. data/lib/badline/vic/display_state.rb +0 -185
  471. data/lib/badline/vic/graphics_mode.rb +0 -164
  472. data/lib/badline/vic/graphics_shifter.rb +0 -207
  473. data/lib/badline/vic/register_log.rb +0 -81
  474. data/lib/badline/vic/registers.rb +0 -195
  475. data/lib/badline/vic/sequencer.rb +0 -275
  476. data/lib/badline/vic/sequencer_output.rb +0 -116
  477. data/lib/badline/vic/sequencer_state.rb +0 -89
  478. data/lib/badline/vic/sprite/internal_bus.rb +0 -68
  479. data/lib/badline/vic/sprite/saved_state.rb +0 -64
  480. data/lib/badline/vic/sprite/shifter.rb +0 -146
  481. data/lib/badline/vic/sprite/timing.rb +0 -56
  482. data/lib/badline/vic/sprite.rb +0 -337
  483. data/lib/badline/vic/sprites.rb +0 -282
  484. data/lib/badline/vic/sprites_state.rb +0 -57
  485. data/lib/badline/vic.rb +0 -853
  486. data/lib/badline/vic20/sound/output.rb +0 -83
  487. data/lib/badline/vic20/sound.rb +0 -233
  488. data/lib/badline/z80/arithmetic.rb +0 -117
  489. data/lib/badline/z80/bit_page.rb +0 -55
  490. data/lib/badline/z80/block_io.rb +0 -58
  491. data/lib/badline/z80/block_transfers.rb +0 -59
  492. data/lib/badline/z80/bus_cycles.rb +0 -111
  493. data/lib/badline/z80/core.rb +0 -113
  494. data/lib/badline/z80/exchanges.rb +0 -50
  495. data/lib/badline/z80/extended_page.rb +0 -89
  496. data/lib/badline/z80/flags.rb +0 -37
  497. data/lib/badline/z80/interrupt_acceptance.rb +0 -46
  498. data/lib/badline/z80/jumps.rb +0 -82
  499. data/lib/badline/z80/loads.rb +0 -98
  500. data/lib/badline/z80/main_page.rb +0 -134
  501. data/lib/badline/z80/registers.rb +0 -120
  502. data/lib/badline/z80/saved_state.rb +0 -69
  503. data/lib/badline/z80/shifts.rb +0 -64
  504. data/lib/badline/z80.rb +0 -11
  505. /data/lib/badline/snapshot/{fields.rb → vice/fields.rb} +0 -0
@@ -0,0 +1,281 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/cia/interrupt_register"
4
+ require "badline/chips/cia/serial"
5
+ require "badline/chips/cia/timer"
6
+ require "badline/chips/cia/saved_state"
7
+ require "badline/chips/cia/fast_forward"
8
+
9
+ module Badline
10
+ module Chips
11
+ # CIA (Complex Interface Adapter) chip
12
+ class CIA
13
+ include Addressable
14
+ include SavedState
15
+ include FastForward
16
+
17
+ # The original NMOS 6526 of the breadbin C64, and the HMOS 6526A of the
18
+ # C64C, which differs from it only in its interrupt control register.
19
+ MODELS = %i[mos6526 mos6526a].freeze
20
+
21
+ attr_reader :start, :control_a, :control_b, :serial, :model
22
+
23
+ # What's on the ports outside the chip. It pulls port lines low through
24
+ # read_a and read_b, and on CIA 1 drives PB4 through port_b4_high?.
25
+ attr_accessor :peripheral
26
+
27
+ # Timer A and timer B.
28
+ def timers = [@ta, @tb]
29
+
30
+ # Port A and B's data and direction registers.
31
+ def port_registers = [@data_port_a, @data_port_b, @data_dir_a, @data_dir_b]
32
+
33
+ def interrupt_status = @icr.status
34
+
35
+ def interrupt_control = @icr.mask
36
+
37
+ def interrupt!(delay = 1) = @icr.assert!(delay)
38
+
39
+ def time_of_day = @tod
40
+
41
+ def timer_a = @ta.counter
42
+
43
+ def timer_a=(value)
44
+ @ta.counter = value
45
+ end
46
+
47
+ def timer_a_latch = @ta.latch
48
+
49
+ def timer_a_latch=(value)
50
+ @ta.latch = value
51
+ end
52
+
53
+ def timer_b = @tb.counter
54
+
55
+ def timer_b=(value)
56
+ @tb.counter = value
57
+ end
58
+
59
+ def timer_b_latch = @tb.latch
60
+
61
+ def timer_b_latch=(value)
62
+ @tb.latch = value
63
+ end
64
+
65
+ # +tod_hz+ is the rate of the pulses on the TOD pin, the region's mains
66
+ # unless a machine feeds it otherwise, and 0 for a pin held still.
67
+ def initialize(start: 0, peripheral: nil, model: :mos6526, region: Region::PAL, tod_hz: nil)
68
+ raise ArgumentError, "unknown CIA model #{model}" unless MODELS.include?(model)
69
+
70
+ addressable_at(start, length: 2**8)
71
+
72
+ @model = model
73
+ @clock_hz = region.clock_hz
74
+ @mains_hz = tod_hz || region.mains_hz
75
+
76
+ @peripheral = peripheral
77
+ @port_b4_handler = nil
78
+ @port_a_handler = nil
79
+ @port_b4_high = true
80
+ reset!
81
+ end
82
+
83
+ # The RES line: ports back to inputs, timers stopped with their latches
84
+ # full, interrupts masked and the TOD clock stopped at 1:00:00.0. The
85
+ # PB4 level carries over and is resampled on the next cycle.
86
+ def reset!
87
+ @data_port_a = 0x00
88
+ @data_port_b = 0x00
89
+ @data_dir_a = 0x00
90
+ @data_dir_b = 0x00
91
+ @port_b4_driven_high = true
92
+ @cnt_high = true
93
+ @cnt_rise = false
94
+ @tod = TimeOfDay.new(clock_hz: @clock_hz, mains_hz: @mains_hz)
95
+ @icr = InterruptRegister.new(@model)
96
+ @icr_status = @icr.status
97
+ @control_a = ControlRegister.new(%i[start output out_mode run_mode load
98
+ in_mode serial_mode clock_frequency])
99
+ @control_b = ControlRegister.new(%i[start output out_mode run_mode load
100
+ in_cnt in_timer_a alarm])
101
+ @ta = Timer.new(@control_a)
102
+ @tb = Timer.new(@control_b)
103
+ @serial = Serial.new(@control_a)
104
+ @port_a_handler&.call
105
+ end
106
+
107
+ # Register a change handler on the PB4 line. On CIA 1, this feeds the
108
+ # light pen input.
109
+ def on_port_b4_change(&handler)
110
+ @port_b4_handler = handler
111
+ end
112
+
113
+ # Register a handler for writes that can move port A's lines: to the
114
+ # data register or DDR, and the reset. On CIA 2, this tells the drives
115
+ # on the serial bus that the C64 may have moved a line.
116
+ def on_port_a_write(&handler)
117
+ @port_a_handler = handler
118
+ end
119
+
120
+ # A falling edge on the FLAG pin. On CIA 1 the datasette's tape read
121
+ # line drives it, on CIA 2 the serial bus SRQ.
122
+ def flag! = raise_interrupt(:flag)
123
+
124
+ def interrupted? = @icr_status.value >= 0x80
125
+
126
+ def cycle!
127
+ @icr.cycle! unless @icr.quiet
128
+ refresh_port_b4 if @port_b4_handler
129
+ level = @serial.cnt
130
+ level == @cnt_high ? @cnt_rise = false : cnt_edge(level)
131
+ underflowed = update_timers
132
+ @serial.cycle!(underflowed) { trigger_serial } if underflowed || !@serial.idle
133
+ @tod.cycle! { trigger_alarm }
134
+ end
135
+
136
+ def read_port_a
137
+ lines = driven_lines(@data_port_a, @data_dir_a)
138
+ return lines unless peripheral
139
+
140
+ lines & peripheral.read_a(lines, driven_lines(@data_port_b, @data_dir_b))
141
+ end
142
+
143
+ # Port A as driven by the data/direction registers alone, without
144
+ # peripheral pulldown. Cheap path for the VIC bank lookup.
145
+ def port_a_lines = driven_lines(@data_port_a, @data_dir_a)
146
+
147
+ # Port B as driven by the data/direction registers alone.
148
+ def port_b_lines = driven_lines(@data_port_b, @data_dir_b)
149
+
150
+ def read_port_b
151
+ lines = driven_lines(@data_port_b, @data_dir_b)
152
+ lines &= peripheral.read_b(driven_lines(@data_port_a, @data_dir_a), lines) if peripheral
153
+ apply_timer_output(lines)
154
+ end
155
+
156
+ def peek(addr)
157
+ case offset_of(addr) & 0x0f
158
+ when 0x00 then read_port_a
159
+ when 0x01 then read_port_b
160
+ when 0x02 then @data_dir_a
161
+ when 0x03 then @data_dir_b
162
+ when 0x04 then low_byte(@ta.counter)
163
+ when 0x05 then high_byte(@ta.counter)
164
+ when 0x06 then low_byte(@tb.counter)
165
+ when 0x07 then high_byte(@tb.counter)
166
+ when 0x08 then @tod.tenths
167
+ when 0x09 then @tod.seconds
168
+ when 0x0a then @tod.minutes
169
+ when 0x0b then @tod.hours
170
+ when 0x0c then @serial.data
171
+ when 0x0d then @icr.read
172
+ when 0x0e then control_a.value
173
+ when 0x0f then control_b.value
174
+ end
175
+ end
176
+
177
+ def poke(addr, value)
178
+ case offset_of(addr) & 0x0f
179
+ when 0x00 then update_port_a { @data_port_a = value }
180
+ when 0x01 then update_port_b { @data_port_b = value }
181
+ when 0x02 then update_port_a { @data_dir_a = value }
182
+ when 0x03 then update_port_b { @data_dir_b = value }
183
+ when 0x04 then @ta.write_latch_low(value)
184
+ when 0x05 then @ta.write_latch_high(value)
185
+ when 0x06 then @tb.write_latch_low(value)
186
+ when 0x07 then @tb.write_latch_high(value)
187
+ when 0x08 then @tod.write(:tenths, value, alarm: control_b.alarm?)
188
+ when 0x09 then @tod.write(:seconds, value, alarm: control_b.alarm?)
189
+ when 0x0a then @tod.write(:minutes, value, alarm: control_b.alarm?)
190
+ when 0x0b then @tod.write_hours(value, alarm: control_b.alarm?)
191
+ when 0x0c then @serial.write(value)
192
+ when 0x0d then @icr.write(value)
193
+ when 0x0e then write_control_a(value)
194
+ when 0x0f then @tb.write_control(value)
195
+ end
196
+ end
197
+
198
+ private
199
+
200
+ def update_port_a
201
+ yield
202
+ @port_a_handler&.call
203
+ end
204
+
205
+ def update_port_b
206
+ yield
207
+ @port_b4_driven_high = driven_lines(@data_port_b, @data_dir_b).anybits?(0x10)
208
+ refresh_port_b4 if @port_b4_handler
209
+ end
210
+
211
+ # PB4 is also control port 1's fire line, and a peripheral pulls it low
212
+ # without any register write, so the level is resampled every cycle rather
213
+ # than only after a poke.
214
+ def refresh_port_b4
215
+ high = @port_b4_driven_high && (peripheral.nil? || peripheral.port_b4_high?)
216
+ return if high == @port_b4_high
217
+
218
+ @port_b4_high = high
219
+ @port_b4_handler.call(high)
220
+ end
221
+
222
+ def driven_lines(register, direction)
223
+ # Output bits are driven from the data register; input bits float high.
224
+ # External peripherals can still pull any line low (wired-AND).
225
+ (register & direction) | (~direction & 0xff)
226
+ end
227
+
228
+ def apply_timer_output(value)
229
+ value = with_bit(value, 6, @ta.output?) if control_a.output?
230
+ value = with_bit(value, 7, @tb.output?) if control_b.output?
231
+ value
232
+ end
233
+
234
+ def with_bit(value, bit, set)
235
+ set ? value | (1 << bit) : value & ~(1 << bit)
236
+ end
237
+
238
+ def trigger_alarm = raise_interrupt(:alarm)
239
+ def trigger_serial = raise_interrupt(:serial)
240
+
241
+ def raise_interrupt(source) = @icr.flag(source)
242
+
243
+ # CNT is sampled once a cycle. When the serial port drives it from this
244
+ # cycle's timer A underflow, the new level is picked up on the next one.
245
+ def cnt_edge(level)
246
+ @cnt_high = level
247
+ @cnt_rise = level
248
+ @serial.rising_edge! { trigger_serial } if level
249
+ end
250
+
251
+ # Returns whether timer A underflowed, which clocks the serial port.
252
+ #
253
+ # CRB bits 6-5 pick timer B's source: ø2, CNT edges, timer A
254
+ # underflows, or timer A underflows gated by the CNT level. Every source
255
+ # drives the same count-enable line, so a cascaded underflow goes through
256
+ # the input pipeline exactly as a CNT edge does.
257
+ def update_timers
258
+ @ta.cycle!(@control_a.value & 0x20 == 0x20 ? @cnt_rise : true)
259
+ underflowed = @ta.underflowed
260
+ @tb.cycle!(
261
+ case @control_b.value & 0x60
262
+ when 0x00 then true
263
+ when 0x20 then @cnt_rise
264
+ when 0x40 then underflowed
265
+ else underflowed && @cnt_high
266
+ end
267
+ )
268
+ @icr.flag(:timer_a) if underflowed
269
+ @icr.timer_b_underflow! if @tb.underflowed
270
+ underflowed
271
+ end
272
+
273
+ def write_control_a(value)
274
+ was_output = control_a.serial_mode?
275
+ @ta.write_control(value)
276
+ @serial.reset! { trigger_serial } if control_a.serial_mode? != was_output
277
+ @tod.fifty_hz = control_a.clock_frequency?
278
+ end
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ # Steps that read an instruction's operand bytes and work out the
7
+ # address it operates on, which they leave in @address. Also the
8
+ # single-step implied, accumulator and immediate instructions, and JMP.
9
+ module Addressing
10
+ private
11
+
12
+ def op_implied_dummy
13
+ @memory.peek(@program_counter)
14
+ end
15
+
16
+ def op_implied_exec
17
+ @memory.peek(@program_counter)
18
+ send(@operation, nil, nil)
19
+ end_instruction
20
+ end
21
+
22
+ def op_accumulator_exec
23
+ @memory.peek(@program_counter)
24
+ send(@operation, :accumulator, @a)
25
+ end_instruction
26
+ end
27
+
28
+ def op_immediate_read
29
+ value = @memory.peek(@program_counter)
30
+ @program_counter = (@program_counter + 1) & 0xffff
31
+ send(@operation, nil, value)
32
+ end_instruction
33
+ end
34
+
35
+ def op_zp
36
+ @address = @memory.peek(@program_counter)
37
+ @program_counter = (@program_counter + 1) & 0xffff
38
+ end
39
+
40
+ def op_zp_x
41
+ @memory.peek(@address)
42
+ @address = (@address + @x) & 0xff
43
+ end
44
+
45
+ def op_zp_y
46
+ @memory.peek(@address)
47
+ @address = (@address + @y) & 0xff
48
+ end
49
+
50
+ def op_abs_low
51
+ @address = @memory.peek(@program_counter)
52
+ @program_counter = (@program_counter + 1) & 0xffff
53
+ end
54
+
55
+ def op_abs_high
56
+ @address |= @memory.peek(@program_counter) << 8
57
+ @program_counter = (@program_counter + 1) & 0xffff
58
+ end
59
+
60
+ def op_abs_high_x
61
+ high = @memory.peek(@program_counter)
62
+ @program_counter = (@program_counter + 1) & 0xffff
63
+ index_address(high, @x)
64
+ end
65
+
66
+ def op_abs_high_y
67
+ high = @memory.peek(@program_counter)
68
+ @program_counter = (@program_counter + 1) & 0xffff
69
+ index_address(high, @y)
70
+ end
71
+
72
+ def op_idx_dummy
73
+ @memory.peek(@dummy_address)
74
+ end
75
+
76
+ def op_zp_pointer
77
+ @pointer = @memory.peek(@program_counter)
78
+ @program_counter = (@program_counter + 1) & 0xffff
79
+ end
80
+
81
+ def op_pointer_x
82
+ @memory.peek(@pointer)
83
+ @pointer = (@pointer + @x) & 0xff
84
+ end
85
+
86
+ def op_pointer_low
87
+ @address = @memory.peek(@pointer)
88
+ end
89
+
90
+ def op_pointer_high
91
+ @address |= @memory.peek((@pointer + 1) & 0xff) << 8
92
+ end
93
+
94
+ def op_pointer_high_y
95
+ index_address(@memory.peek((@pointer + 1) & 0xff), @y)
96
+ end
97
+
98
+ def op_jmp_high
99
+ @program_counter = @address | (@memory.peek(@program_counter) << 8)
100
+ end_instruction
101
+ end
102
+
103
+ def op_indirect_low
104
+ @pointer = @address
105
+ @address = @memory.peek(@pointer)
106
+ end
107
+
108
+ # The pointer's high byte never increments, so JMP ($30FF) reads its
109
+ # target's high byte from $3000, not $3100.
110
+ def op_indirect_high
111
+ high = @memory.peek((@pointer & 0xff00) | ((@pointer + 1) & 0xff))
112
+ @program_counter = @address | (high << 8)
113
+ end_instruction
114
+ end
115
+
116
+ # Adds an index register to a base address. The 6502 adds to the low
117
+ # byte first and spends the next cycle reading from that address,
118
+ # before the carry reaches the high byte. Read instructions skip that
119
+ # cycle when there is no carry.
120
+ def index_address(high, index)
121
+ low = @address
122
+ @address = (((high << 8) | low) + index) & 0xffff
123
+ @boundary_crossed = (low + index) > 0xff
124
+ if @optional_dummy && !@boundary_crossed
125
+ @index += 1
126
+ else
127
+ @dummy_address = (high << 8) | ((low + index) & 0xff)
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Chips
5
+ class CPU
6
+ # The steps at an instruction boundary: the opcode fetch that starts
7
+ # an instruction, and the ends of one.
8
+ module Boundary
9
+ private
10
+
11
+ # The first cycle of every instruction. Starts an interrupt if one was
12
+ # pending when the previous instruction ended. Otherwise reads the
13
+ # opcode and switches to its plan.
14
+ def op_fetch
15
+ return start_interrupt if @boundary_nmi || @boundary_irq
16
+
17
+ run_traps
18
+ opcode = @memory.peek(@program_counter)
19
+ log(opcode) if @debug
20
+ micro = MICROCODE[opcode]
21
+ @operation = micro.operation
22
+ @plan = micro.plan
23
+ @writes = micro.writes
24
+ @optional_dummy = micro.optional_dummy
25
+ @index = 1
26
+ @boundary_crossed = false
27
+ @program_counter = (@program_counter + 1) & 0xffff
28
+ end
29
+
30
+ def end_instruction
31
+ @instructions += 1
32
+ end_sequence
33
+ end
34
+
35
+ # Makes the next cycle an opcode fetch, and saves the pending interrupt
36
+ # flags for op_fetch. The next cycle polls before op_fetch runs, which
37
+ # updates the live flags.
38
+ def end_sequence
39
+ @plan = FETCH_PLAN
40
+ @writes = FETCH_WRITES
41
+ @index = 0
42
+ @boundary_irq = @irq_pending
43
+ @boundary_nmi = @nmi_pending
44
+ end
45
+
46
+ def log(opcode)
47
+ instruction = Instruction.find(opcode)
48
+ puts(
49
+ "#{@cycles}: PC: #{format16(@program_counter)} - " \
50
+ "#{instruction.name.upcase} #{instruction.addressing_mode}"
51
+ )
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/chips/cpu/microcode"
4
+ require "badline/chips/cpu/addressing"
5
+ require "badline/chips/cpu/operations"
6
+ require "badline/chips/cpu/stack_operations"
7
+ require "badline/chips/cpu/boundary"
8
+ require "badline/chips/cpu/saved_state"
9
+
10
+ module Badline
11
+ module Chips
12
+ class CPU
13
+ STATUS_FLAGS = [:carry, :zero, :interrupt, :decimal, :break, 1, :overflow, :negative].freeze
14
+
15
+ # The 6502 core, on whatever bus +memory+ is. The C64's 6510 (CPU) and
16
+ # the 1541's 6502 (Drives::Model1541::CPU) each include it.
17
+ module Core
18
+ include IntegerHelper
19
+ include InstructionSet
20
+ include Interrupts
21
+ include Traps
22
+ include Addressing
23
+ include Operations
24
+ include StackOperations
25
+ include Boundary
26
+ include SavedState
27
+
28
+ attr_reader :memory, :instructions, :boundary_crossed, :cycles, :pending_write
29
+ attr_accessor :program_counter, :stack_pointer, :status, :a, :x, :y, :nmi, :irq
30
+
31
+ alias pending_write? pending_write
32
+
33
+ # +ane_constant+ is ANE's magic constant, which varies from chip to
34
+ # chip. The default is the C64 6510's.
35
+ def initialize(memory, debug: false, ane_constant: 0xef)
36
+ @debug = debug
37
+ @ane_constant = ane_constant
38
+ @memory = memory
39
+ @status = CPUStatus.new(STATUS_FLAGS, value: 0b00100000)
40
+ reset_registers
41
+
42
+ @nmi = @irq = false
43
+ @irq_sample = @irq_pending = false
44
+ @nmi_sample = @nmi_pending = false
45
+ @skip_poll = false
46
+ @boundary_crossed = false
47
+ @interrupt = nil
48
+ @brk = false
49
+ @pending_write = false
50
+ @stalled_at = nil
51
+ @so_high = true
52
+
53
+ @cycles = 0
54
+ @instructions = 0
55
+ @traps = nil
56
+ end_sequence
57
+ end
58
+
59
+ def reset!
60
+ status.interrupt = true
61
+ reset_registers
62
+ @nmi = false
63
+ @irq_sample = @irq_pending = false
64
+ @nmi_sample = @nmi_pending = false
65
+ @skip_poll = false
66
+ @pending_write = false
67
+ end_sequence
68
+ end
69
+
70
+ def p
71
+ status.value
72
+ end
73
+
74
+ def p=(new_value)
75
+ status.value = new_value
76
+ end
77
+
78
+ def cycle!
79
+ poll # the interrupt lines
80
+
81
+ # Run the next step
82
+ index = @index
83
+ @index = index + 1
84
+ send(@plan[index])
85
+ @cycles += 1
86
+
87
+ # Record if the next step writes to memory
88
+ @pending_write = @writes[@index]
89
+ nil
90
+ end
91
+
92
+ # Called instead of #cycle! on a cycle the VIC holds the CPU through
93
+ # BA. Records which cycle the CPU was stalled before, and keeps
94
+ # sampling the interrupt lines.
95
+ def stall!
96
+ @stalled_at = @cycles
97
+ sample_while_stalled
98
+ end
99
+
100
+ # The 6502's SO (set overflow) pin, which the 6510 leaves out, so the
101
+ # C64 never drives it. A falling edge sets V and a held level does
102
+ # nothing more.
103
+ def so=(high)
104
+ @status.overflow = true if @so_high && !high
105
+ @so_high = high
106
+ end
107
+
108
+ # Pulses SO: pulls it low, which sets V, and releases it.
109
+ def so!
110
+ @status.overflow = true
111
+ @so_high = true
112
+ end
113
+
114
+ def jammed?
115
+ @plan.equal?(JAMMED_PLAN)
116
+ end
117
+
118
+ # Runs to the end of the instruction, or until the CPU jams.
119
+ def step!
120
+ cycle!
121
+ cycle! until @plan.equal?(FETCH_PLAN) || jammed?
122
+ nil
123
+ end
124
+
125
+ # Whether the next cycle fetches an opcode.
126
+ def boundary? = @index.zero? && @plan.equal?(FETCH_PLAN)
127
+
128
+ def trapped? = !@traps.nil?
129
+
130
+ def inspect
131
+ "Cycles: #{@cycles}, PC: #{format16(program_counter)}, " \
132
+ "SP: #{format8(stack_pointer)}, A: #{format8(a)}, X: #{format8(x)}, " \
133
+ "Y: #{format8(y)}, P: #{format8(p)}"
134
+ end
135
+
136
+ private
137
+
138
+ # True when the CPU was stalled right before this cycle.
139
+ def stalled_before_this_cycle?
140
+ @stalled_at == @cycles
141
+ end
142
+
143
+ # True when the CPU was stalled right before the cycle that preceded
144
+ # this one.
145
+ def stalled_before_previous_cycle?
146
+ @stalled_at == @cycles - 1
147
+ end
148
+
149
+ def write_byte(addr, value)
150
+ @memory.poke(addr, value)
151
+ end
152
+
153
+ def stack_address(offset = 0)
154
+ 0x0100 | ((@stack_pointer + offset) & 0xff)
155
+ end
156
+
157
+ def reset_registers
158
+ # The program counter is initialized from the reset vector
159
+ @program_counter = @memory.peek16(0xfffc)
160
+ # Stack pointer starts at 0x01ff and grows down
161
+ @stack_pointer = 0xff
162
+ @a = @x = @y = 0x0
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end