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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/CLAUDE.md +59 -31
- data/README.md +1 -1
- data/doc/library.md +9 -9
- data/doc/pinned-behaviour.md +105 -105
- data/doc/snapshots.md +1 -4
- data/lib/badline/audio/bare_player.rb +3 -3
- data/lib/badline/audio/cli.rb +6 -6
- data/lib/badline/audio/jukebox.rb +2 -2
- data/lib/badline/audio/machine_player.rb +2 -2
- data/lib/badline/{media → audio}/queue.rb +1 -1
- data/lib/badline/audio/queued_tune.rb +6 -6
- data/lib/badline/audio/renderer.rb +5 -5
- data/lib/badline/audio/stereo.rb +1 -1
- data/lib/badline/c128/{bus_state.rb → bus/saved_state.rb} +1 -1
- data/lib/badline/c128/bus.rb +18 -17
- data/lib/badline/c128/color_lines.rb +2 -2
- data/lib/badline/c128/cpu.rb +1 -1
- data/lib/badline/c128/drives.rb +10 -11
- data/lib/badline/c128/fast_serial.rb +5 -5
- data/lib/badline/c128/mmu_pages.rb +1 -1
- data/lib/badline/c128/modes.rb +2 -2
- data/lib/badline/c128/saved_state.rb +21 -47
- data/lib/badline/c128/z80.rb +2 -2
- data/lib/badline/c128.rb +14 -9
- data/lib/badline/{computer → c64}/attachments.rb +4 -6
- data/lib/badline/c64/banked_ram/banking.rb +32 -0
- data/lib/badline/c64/banked_ram/plus256k.rb +56 -0
- data/lib/badline/c64/banked_ram/plus60k.rb +45 -0
- data/lib/badline/c64/banked_ram/unexpanded.rb +30 -0
- data/lib/badline/c64/banked_ram.rb +23 -0
- data/lib/badline/c64/bus/extra_sids.rb +32 -0
- data/lib/badline/c64/bus/fittings.rb +40 -0
- data/lib/badline/c64/bus/pla.rb +149 -0
- data/lib/badline/c64/bus/roms.rb +41 -0
- data/lib/badline/c64/bus/saved_state.rb +68 -0
- data/lib/badline/c64/bus/sid_slots.rb +44 -0
- data/lib/badline/c64/bus.rb +218 -0
- data/lib/badline/c64/cartridge/action_replay.rb +128 -0
- data/lib/badline/c64/cartridge/atomic_power.rb +26 -0
- data/lib/badline/c64/cartridge/bank.rb +83 -0
- data/lib/badline/c64/cartridge/comal80.rb +52 -0
- data/lib/badline/c64/cartridge/dinamic.rb +36 -0
- data/lib/badline/c64/cartridge/easy_flash.rb +147 -0
- data/lib/badline/c64/cartridge/epyx_fastload.rb +81 -0
- data/lib/badline/c64/cartridge/final_cartridge3.rb +91 -0
- data/lib/badline/c64/cartridge/flash/saved_state.rb +40 -0
- data/lib/badline/c64/cartridge/flash.rb +279 -0
- data/lib/badline/c64/cartridge/freezer.rb +35 -0
- data/lib/badline/c64/cartridge/fun_play.rb +40 -0
- data/lib/badline/c64/cartridge/g_mod2.rb +76 -0
- data/lib/badline/c64/cartridge/game_system.rb +42 -0
- data/lib/badline/c64/cartridge/geo_ram.rb +81 -0
- data/lib/badline/c64/cartridge/kcs_power.rb +77 -0
- data/lib/badline/c64/cartridge/mach5.rb +34 -0
- data/lib/badline/c64/cartridge/magic_desk.rb +32 -0
- data/lib/badline/c64/cartridge/ocean.rb +37 -0
- data/lib/badline/c64/cartridge/pagefox.rb +70 -0
- data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +47 -0
- data/lib/badline/c64/cartridge/retro_replay.rb +286 -0
- data/lib/badline/c64/cartridge/rex_utility.rb +31 -0
- data/lib/badline/c64/cartridge/rgcd.rb +60 -0
- data/lib/badline/c64/cartridge/saved_state.rb +63 -0
- data/lib/badline/c64/cartridge/simons_basic.rb +37 -0
- data/lib/badline/c64/cartridge/standard.rb +17 -0
- data/lib/badline/c64/cartridge/super_games.rb +50 -0
- data/lib/badline/c64/cartridge/westermann.rb +32 -0
- data/lib/badline/c64/cartridge/zaxxon.rb +68 -0
- data/lib/badline/c64/cartridge.rb +258 -0
- data/lib/badline/c64/color_memory.rb +27 -0
- data/lib/badline/{computer → c64}/common.rb +2 -2
- data/lib/badline/{computer → c64}/kernal_traps.rb +18 -18
- data/lib/badline/c64/reu/dma/saved_state.rb +52 -0
- data/lib/badline/c64/reu/dma.rb +275 -0
- data/lib/badline/c64/reu/ram.rb +109 -0
- data/lib/badline/c64/reu/saved_state.rb +55 -0
- data/lib/badline/c64/reu/trigger.rb +34 -0
- data/lib/badline/c64/reu.rb +291 -0
- data/lib/badline/{computer → c64}/saved_state.rb +13 -18
- data/lib/badline/{computer → c64}/true_drives.rb +11 -11
- data/lib/badline/{computer.rb → c64.rb} +18 -13
- data/lib/badline/chips/cia/fast_forward.rb +62 -0
- data/lib/badline/chips/cia/interrupt_register.rb +145 -0
- data/lib/badline/chips/cia/saved_state.rb +49 -0
- data/lib/badline/chips/cia/serial/saved_state.rb +45 -0
- data/lib/badline/chips/cia/serial.rb +250 -0
- data/lib/badline/chips/cia/time_of_day.rb +232 -0
- data/lib/badline/chips/cia/timer.rb +208 -0
- data/lib/badline/chips/cia.rb +281 -0
- data/lib/badline/chips/cpu/addressing.rb +133 -0
- data/lib/badline/chips/cpu/boundary.rb +56 -0
- data/lib/badline/chips/cpu/core.rb +167 -0
- data/lib/badline/chips/cpu/instruction.rb +338 -0
- data/lib/badline/chips/cpu/instruction_set/arithmetic.rb +118 -0
- data/lib/badline/chips/cpu/instruction_set/bitwise.rb +130 -0
- data/lib/badline/chips/cpu/instruction_set/branch.rb +83 -0
- data/lib/badline/chips/cpu/instruction_set/flag.rb +67 -0
- data/lib/badline/chips/cpu/instruction_set/illegal.rb +274 -0
- data/lib/badline/chips/cpu/instruction_set/inc_dec.rb +73 -0
- data/lib/badline/chips/cpu/instruction_set/stack.rb +56 -0
- data/lib/badline/chips/cpu/instruction_set/transfer.rb +141 -0
- data/lib/badline/chips/cpu/instruction_set.rb +59 -0
- data/lib/badline/chips/cpu/interrupts.rb +108 -0
- data/lib/badline/chips/cpu/microcode.rb +179 -0
- data/lib/badline/chips/cpu/operations.rb +90 -0
- data/lib/badline/chips/cpu/port.rb +116 -0
- data/lib/badline/chips/cpu/saved_state.rb +117 -0
- data/lib/badline/chips/cpu/stack_operations.rb +84 -0
- data/lib/badline/chips/cpu/traps.rb +23 -0
- data/lib/badline/chips/cpu.rb +12 -0
- data/lib/badline/chips/sid/catch_up.rb +122 -0
- data/lib/badline/chips/sid/decimator.rb +66 -0
- data/lib/badline/chips/sid/envelope/saved_state.rb +70 -0
- data/lib/badline/chips/sid/envelope.rb +241 -0
- data/lib/badline/chips/sid/filter/saved_state.rb +42 -0
- data/lib/badline/chips/sid/filter.rb +241 -0
- data/lib/badline/chips/sid/saved_state.rb +52 -0
- data/lib/badline/chips/sid/voice.rb +79 -0
- data/lib/badline/chips/sid/waveform/combined.rb +128 -0
- data/lib/badline/chips/sid/waveform/fast_forward.rb +107 -0
- data/lib/badline/chips/sid/waveform/noise_writeback.rb +124 -0
- data/lib/badline/chips/sid/waveform/saved_state.rb +68 -0
- data/lib/badline/chips/sid/waveform.rb +307 -0
- data/lib/badline/chips/sid.rb +265 -0
- data/lib/badline/{c128 → chips}/vdc/memory.rb +1 -1
- data/lib/badline/{c128 → chips}/vdc/painter.rb +1 -1
- data/lib/badline/{c128 → chips}/vdc/raster.rb +1 -1
- data/lib/badline/chips/vdc/saved_state.rb +36 -0
- data/lib/badline/{c128 → chips}/vdc/window.rb +1 -1
- data/lib/badline/{c128 → chips}/vdc.rb +7 -31
- data/lib/badline/chips/via/control_lines.rb +124 -0
- data/lib/badline/chips/via/fast_forward.rb +55 -0
- data/lib/badline/chips/via/interrupt_register.rb +69 -0
- data/lib/badline/chips/via/saved_state.rb +44 -0
- data/lib/badline/chips/via/shift_register.rb +182 -0
- data/lib/badline/chips/via/timer1.rb +133 -0
- data/lib/badline/chips/via/timer2.rb +135 -0
- data/lib/badline/chips/via.rb +268 -0
- data/lib/badline/chips/vic/bank.rb +135 -0
- data/lib/badline/chips/vic/border_mask.rb +88 -0
- data/lib/badline/chips/vic/collisions.rb +117 -0
- data/lib/badline/chips/vic/color_patches.rb +49 -0
- data/lib/badline/chips/vic/display_state.rb +187 -0
- data/lib/badline/chips/vic/graphics_mode.rb +166 -0
- data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +40 -0
- data/lib/badline/chips/vic/graphics_shifter.rb +187 -0
- data/lib/badline/chips/vic/register_log.rb +83 -0
- data/lib/badline/chips/vic/registers.rb +197 -0
- data/lib/badline/chips/vic/saved_state.rb +82 -0
- data/lib/badline/chips/vic/sequencer/saved_state.rb +91 -0
- data/lib/badline/chips/vic/sequencer.rb +277 -0
- data/lib/badline/chips/vic/sequencer_output.rb +118 -0
- data/lib/badline/chips/vic/sprite/internal_bus.rb +70 -0
- data/lib/badline/chips/vic/sprite/saved_state.rb +66 -0
- data/lib/badline/chips/vic/sprite/shifter.rb +148 -0
- data/lib/badline/chips/vic/sprite/timing.rb +58 -0
- data/lib/badline/chips/vic/sprite.rb +339 -0
- data/lib/badline/chips/vic/sprites/saved_state.rb +59 -0
- data/lib/badline/chips/vic/sprites.rb +284 -0
- data/lib/badline/chips/vic.rb +783 -0
- data/lib/badline/chips/vic_i/painter/saved_state.rb +40 -0
- data/lib/badline/{vic20/vic → chips/vic_i}/painter.rb +6 -30
- data/lib/badline/{vic20/vic → chips/vic_i}/saved_state.rb +2 -2
- data/lib/badline/chips/vic_i/sound/output.rb +85 -0
- data/lib/badline/chips/vic_i/sound.rb +235 -0
- data/lib/badline/{vic20/vic → chips/vic_i}/text_window.rb +2 -2
- data/lib/badline/{vic20/vic.rb → chips/vic_i.rb} +5 -5
- data/lib/badline/chips/z80/arithmetic.rb +119 -0
- data/lib/badline/chips/z80/bit_page.rb +57 -0
- data/lib/badline/chips/z80/block_io.rb +60 -0
- data/lib/badline/chips/z80/block_transfers.rb +61 -0
- data/lib/badline/chips/z80/bus_cycles.rb +113 -0
- data/lib/badline/chips/z80/core.rb +115 -0
- data/lib/badline/chips/z80/exchanges.rb +52 -0
- data/lib/badline/chips/z80/extended_page.rb +91 -0
- data/lib/badline/chips/z80/flags.rb +39 -0
- data/lib/badline/chips/z80/interrupt_acceptance.rb +48 -0
- data/lib/badline/chips/z80/jumps.rb +84 -0
- data/lib/badline/chips/z80/loads.rb +100 -0
- data/lib/badline/chips/z80/main_page.rb +136 -0
- data/lib/badline/chips/z80/registers.rb +122 -0
- data/lib/badline/chips/z80/saved_state.rb +71 -0
- data/lib/badline/chips/z80/shifts.rb +66 -0
- data/lib/badline/chips/z80.rb +13 -0
- data/lib/badline/core.rb +26 -28
- data/lib/badline/drives/datasette/saved_state.rb +37 -0
- data/lib/badline/drives/datasette.rb +140 -0
- data/lib/badline/drives/engine/core.rb +149 -0
- data/lib/badline/drives/engine/fast_serial.rb +75 -0
- data/lib/badline/drives/engine/idle.rb +127 -0
- data/lib/badline/drives/engine/idle_cpu.rb +30 -0
- data/lib/badline/drives/engine/orbit.rb +81 -0
- data/lib/badline/drives/engine/serial_lines.rb +75 -0
- data/lib/badline/drives/engine/sleep.rb +114 -0
- data/lib/badline/drives/engine/vias.rb +86 -0
- data/lib/badline/drives/engine/watch.rb +152 -0
- data/lib/badline/drives/engine.rb +11 -0
- data/lib/badline/drives/model1541/bus.rb +88 -0
- data/lib/badline/drives/model1541/byte_ready.rb +50 -0
- data/lib/badline/drives/model1541/cpu.rb +13 -0
- data/lib/badline/drives/model1541/disk/saved_state.rb +88 -0
- data/lib/badline/drives/model1541/disk.rb +317 -0
- data/lib/badline/drives/model1541/disk_via.rb +38 -0
- data/lib/badline/drives/model1541/gcr.rb +59 -0
- data/lib/badline/drives/model1541/mechanism/saved_state.rb +72 -0
- data/lib/badline/drives/model1541/mechanism.rb +315 -0
- data/lib/badline/drives/model1541/rotation.rb +123 -0
- data/lib/badline/drives/model1541/saved_state.rb +44 -0
- data/lib/badline/drives/model1541/sector_reader.rb +78 -0
- data/lib/badline/drives/model1541/serial_port.rb +20 -0
- data/lib/badline/drives/model1541/track.rb +100 -0
- data/lib/badline/drives/model1541.rb +81 -0
- data/lib/badline/drives/model1571/bus.rb +113 -0
- data/lib/badline/drives/model1571/cpu.rb +13 -0
- data/lib/badline/drives/model1571/saved_state.rb +47 -0
- data/lib/badline/drives/model1571/serial_port.rb +39 -0
- data/lib/badline/drives/model1571/serial_via.rb +27 -0
- data/lib/badline/drives/model1571/wd1770.rb +122 -0
- data/lib/badline/drives/model1571.rb +142 -0
- data/lib/badline/drives/model1581/bus.rb +139 -0
- data/lib/badline/drives/model1581/cpu.rb +13 -0
- data/lib/badline/drives/model1581/disk/saved_state.rb +89 -0
- data/lib/badline/drives/model1581/disk.rb +137 -0
- data/lib/badline/drives/model1581/mechanism.rb +119 -0
- data/lib/badline/drives/model1581/saved_state.rb +42 -0
- data/lib/badline/drives/model1581/serial_port.rb +54 -0
- data/lib/badline/drives/model1581/slot.rb +79 -0
- data/lib/badline/drives/model1581/track.rb +102 -0
- data/lib/badline/drives/model1581/track_bytes.rb +77 -0
- data/lib/badline/drives/model1581/wd1772/registers.rb +91 -0
- data/lib/badline/drives/model1581/wd1772/saved_state.rb +68 -0
- data/lib/badline/drives/model1581/wd1772/sector_writes.rb +75 -0
- data/lib/badline/drives/model1581/wd1772/sectors.rb +152 -0
- data/lib/badline/drives/model1581/wd1772/stepping.rb +111 -0
- data/lib/badline/drives/model1581/wd1772/tracks.rb +127 -0
- data/lib/badline/drives/model1581/wd1772/turn.rb +75 -0
- data/lib/badline/drives/model1581/wd1772.rb +218 -0
- data/lib/badline/drives/model1581.rb +206 -0
- data/lib/badline/drives/serial_bus.rb +161 -0
- data/lib/badline/frontend/boot.rb +3 -3
- data/lib/badline/frontend/machine_window.rb +3 -1
- data/lib/badline/frontend/player/filter_view.rb +1 -1
- data/lib/badline/frontend/player/player_controller.rb +2 -2
- data/lib/badline/frontend/player/player_screen.rb +1 -0
- data/lib/badline/frontend/player/theme.rb +7 -7
- data/lib/badline/frontend/timeline.rb +3 -3
- data/lib/badline/input/control_ports.rb +78 -0
- data/lib/badline/input/joystick.rb +27 -0
- data/lib/badline/input/keyboard.rb +114 -0
- data/lib/badline/input.rb +3 -0
- data/lib/badline/kernal_trap/{drive → dos}/block_commands.rb +1 -1
- data/lib/badline/kernal_trap/dos/channel.rb +183 -0
- data/lib/badline/kernal_trap/{drive → dos}/channels.rb +1 -1
- data/lib/badline/kernal_trap/{drive → dos}/memory.rb +1 -1
- data/lib/badline/kernal_trap/{drive → dos}/parameters.rb +1 -1
- data/lib/badline/kernal_trap/{drive → dos}/status.rb +1 -1
- data/lib/badline/kernal_trap/{drive → dos}/write_file.rb +1 -1
- data/lib/badline/kernal_trap/{drive → dos}/writes.rb +1 -1
- data/lib/badline/kernal_trap/{drive.rb → dos.rb} +11 -10
- data/lib/badline/kernal_trap/load.rb +5 -5
- data/lib/badline/kernal_trap/save.rb +4 -4
- data/lib/badline/kernal_trap/serial.rb +7 -7
- data/lib/badline/kernal_trap.rb +1 -2
- data/lib/badline/machine/clocking.rb +1 -1
- data/lib/badline/machine.rb +5 -5
- data/lib/badline/media/true_drive.rb +1 -1
- data/lib/badline/media/tune.rb +2 -2
- data/lib/badline/media.rb +3 -3
- data/lib/badline/model.rb +1 -1
- data/lib/badline/roms/README +6 -6
- data/lib/badline/sdl.rb +1 -0
- data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
- data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
- data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
- data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
- data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
- data/lib/badline/sid_tune.rb +6 -0
- data/lib/badline/snapshot/c128_setup.rb +43 -0
- data/lib/badline/snapshot/c64_setup.rb +79 -0
- data/lib/badline/snapshot/image.rb +5 -25
- data/lib/badline/snapshot/machine_state.rb +1 -1
- data/lib/badline/snapshot/setup.rb +22 -66
- data/lib/badline/snapshot/state.rb +8 -18
- data/lib/badline/snapshot/storage_setup.rb +68 -0
- data/lib/badline/snapshot/vic20_setup.rb +37 -0
- data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
- data/lib/badline/snapshot/vice/restore.rb +1 -1
- data/lib/badline/snapshot/vice/reu1764.rb +1 -1
- data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
- data/lib/badline/snapshot/vice.rb +7 -5
- data/lib/badline/snapshot.rb +3 -4
- data/lib/badline/storage/d64_image.rb +0 -2
- data/lib/badline/storage/d71_image.rb +0 -2
- data/lib/badline/storage/d81_image.rb +0 -2
- data/lib/badline/storage/disk_image.rb +3 -6
- data/lib/badline/storage/g64_image.rb +2 -2
- data/lib/badline/storage/host_directory.rb +0 -5
- data/lib/badline/storage/t64.rb +2 -8
- data/lib/badline/storage/unavailable.rb +1 -1
- data/lib/badline/storage.rb +0 -37
- data/lib/badline/support/c128_checkpoint.rb +39 -0
- data/lib/badline/support/checkpoint.rb +108 -0
- data/lib/badline/support/vic20_checkpoint.rb +38 -0
- data/lib/badline/support.rb +5 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +5 -5
- data/lib/badline/vic20/bus/saved_state.rb +35 -0
- data/lib/badline/vic20/bus.rb +3 -25
- data/lib/badline/vic20/cpu.rb +1 -1
- data/lib/badline/vic20/port_wiring.rb +4 -4
- data/lib/badline/vic20/saved_state.rb +16 -30
- data/lib/badline/vic20/user_via_ports.rb +2 -2
- data/lib/badline/vic20.rb +19 -14
- metadata +245 -221
- data/lib/badline/address_bus/extra_sids.rb +0 -30
- data/lib/badline/address_bus/fittings.rb +0 -38
- data/lib/badline/address_bus/pla.rb +0 -147
- data/lib/badline/address_bus/roms.rb +0 -39
- data/lib/badline/address_bus/saved_state.rb +0 -66
- data/lib/badline/address_bus/sid_slots.rb +0 -42
- data/lib/badline/address_bus.rb +0 -216
- data/lib/badline/cartridge/action_replay.rb +0 -126
- data/lib/badline/cartridge/atomic_power.rb +0 -24
- data/lib/badline/cartridge/bank.rb +0 -81
- data/lib/badline/cartridge/comal80.rb +0 -50
- data/lib/badline/cartridge/dinamic.rb +0 -34
- data/lib/badline/cartridge/easy_flash.rb +0 -145
- data/lib/badline/cartridge/epyx_fastload.rb +0 -79
- data/lib/badline/cartridge/final_cartridge3.rb +0 -89
- data/lib/badline/cartridge/flash.rb +0 -306
- data/lib/badline/cartridge/freezer.rb +0 -33
- data/lib/badline/cartridge/fun_play.rb +0 -38
- data/lib/badline/cartridge/g_mod2.rb +0 -74
- data/lib/badline/cartridge/game_system.rb +0 -40
- data/lib/badline/cartridge/geo_ram.rb +0 -79
- data/lib/badline/cartridge/kcs_power.rb +0 -75
- data/lib/badline/cartridge/mach5.rb +0 -32
- data/lib/badline/cartridge/magic_desk.rb +0 -30
- data/lib/badline/cartridge/ocean.rb +0 -35
- data/lib/badline/cartridge/pagefox.rb +0 -68
- data/lib/badline/cartridge/retro_replay.rb +0 -320
- data/lib/badline/cartridge/rex_utility.rb +0 -29
- data/lib/badline/cartridge/rgcd.rb +0 -58
- data/lib/badline/cartridge/simons_basic.rb +0 -35
- data/lib/badline/cartridge/standard.rb +0 -15
- data/lib/badline/cartridge/super_games.rb +0 -48
- data/lib/badline/cartridge/westermann.rb +0 -30
- data/lib/badline/cartridge/zaxxon.rb +0 -66
- data/lib/badline/cartridge.rb +0 -303
- data/lib/badline/checkpoint.rb +0 -98
- data/lib/badline/cia/fast_forward.rb +0 -60
- data/lib/badline/cia/interrupt_register.rb +0 -143
- data/lib/badline/cia/serial.rb +0 -275
- data/lib/badline/cia/state.rb +0 -47
- data/lib/badline/cia/timer.rb +0 -206
- data/lib/badline/cia.rb +0 -279
- data/lib/badline/color_memory.rb +0 -25
- data/lib/badline/control_ports.rb +0 -76
- data/lib/badline/cpu/addressing.rb +0 -131
- data/lib/badline/cpu/boundary.rb +0 -54
- data/lib/badline/cpu/core.rb +0 -165
- data/lib/badline/cpu/microcode.rb +0 -177
- data/lib/badline/cpu/operations.rb +0 -88
- data/lib/badline/cpu/saved_state.rb +0 -115
- data/lib/badline/cpu/stack_operations.rb +0 -82
- data/lib/badline/cpu.rb +0 -10
- data/lib/badline/cpu_port.rb +0 -112
- data/lib/badline/datasette.rb +0 -161
- data/lib/badline/drive/core.rb +0 -147
- data/lib/badline/drive/fast_serial.rb +0 -73
- data/lib/badline/drive/idle.rb +0 -125
- data/lib/badline/drive/idle_cpu.rb +0 -28
- data/lib/badline/drive/orbit.rb +0 -79
- data/lib/badline/drive/serial_lines.rb +0 -73
- data/lib/badline/drive/sleep.rb +0 -112
- data/lib/badline/drive/vias.rb +0 -84
- data/lib/badline/drive/watch.rb +0 -150
- data/lib/badline/drive.rb +0 -11
- data/lib/badline/drive1541/bus.rb +0 -86
- data/lib/badline/drive1541/byte_ready.rb +0 -48
- data/lib/badline/drive1541/cpu.rb +0 -11
- data/lib/badline/drive1541/disk.rb +0 -315
- data/lib/badline/drive1541/disk_state.rb +0 -86
- data/lib/badline/drive1541/disk_via.rb +0 -36
- data/lib/badline/drive1541/gcr.rb +0 -57
- data/lib/badline/drive1541/mechanism.rb +0 -313
- data/lib/badline/drive1541/mechanism_state.rb +0 -68
- data/lib/badline/drive1541/rotation.rb +0 -121
- data/lib/badline/drive1541/sector_reader.rb +0 -76
- data/lib/badline/drive1541/serial_port.rb +0 -18
- data/lib/badline/drive1541/track.rb +0 -98
- data/lib/badline/drive1541.rb +0 -111
- data/lib/badline/drive1571/bus.rb +0 -111
- data/lib/badline/drive1571/cpu.rb +0 -11
- data/lib/badline/drive1571/serial_port.rb +0 -37
- data/lib/badline/drive1571/serial_via.rb +0 -25
- data/lib/badline/drive1571/wd1770.rb +0 -120
- data/lib/badline/drive1571.rb +0 -175
- data/lib/badline/drive1581/bus.rb +0 -137
- data/lib/badline/drive1581/cpu.rb +0 -11
- data/lib/badline/drive1581/disk.rb +0 -135
- data/lib/badline/drive1581/disk_state.rb +0 -87
- data/lib/badline/drive1581/mechanism.rb +0 -117
- data/lib/badline/drive1581/serial_port.rb +0 -52
- data/lib/badline/drive1581/slot.rb +0 -78
- data/lib/badline/drive1581/track.rb +0 -100
- data/lib/badline/drive1581/track_bytes.rb +0 -75
- data/lib/badline/drive1581/wd1772/registers.rb +0 -89
- data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
- data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
- data/lib/badline/drive1581/wd1772/state.rb +0 -66
- data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
- data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
- data/lib/badline/drive1581/wd1772/turn.rb +0 -73
- data/lib/badline/drive1581/wd1772.rb +0 -216
- data/lib/badline/drive1581.rb +0 -233
- data/lib/badline/iec_bus.rb +0 -159
- data/lib/badline/instruction.rb +0 -334
- data/lib/badline/instruction_set/arithmetic.rb +0 -114
- data/lib/badline/instruction_set/bitwise.rb +0 -126
- data/lib/badline/instruction_set/branch.rb +0 -79
- data/lib/badline/instruction_set/flag.rb +0 -63
- data/lib/badline/instruction_set/illegal.rb +0 -270
- data/lib/badline/instruction_set/inc_dec.rb +0 -69
- data/lib/badline/instruction_set/stack.rb +0 -52
- data/lib/badline/instruction_set/transfer.rb +0 -137
- data/lib/badline/instruction_set.rb +0 -55
- data/lib/badline/interrupts.rb +0 -104
- data/lib/badline/joystick.rb +0 -25
- data/lib/badline/kernal_trap/channel.rb +0 -181
- data/lib/badline/keyboard.rb +0 -112
- data/lib/badline/ram_expansion/banking.rb +0 -30
- data/lib/badline/ram_expansion/plus256k.rb +0 -54
- data/lib/badline/ram_expansion/plus60k.rb +0 -43
- data/lib/badline/ram_expansion/unexpanded.rb +0 -28
- data/lib/badline/ram_expansion.rb +0 -21
- data/lib/badline/reu/dma/saved_state.rb +0 -50
- data/lib/badline/reu/dma.rb +0 -273
- data/lib/badline/reu/ram.rb +0 -107
- data/lib/badline/reu/saved_state.rb +0 -80
- data/lib/badline/reu/trigger.rb +0 -32
- data/lib/badline/reu.rb +0 -289
- data/lib/badline/sid/catch_up.rb +0 -120
- data/lib/badline/sid/decimator.rb +0 -64
- data/lib/badline/sid/envelope/state.rb +0 -68
- data/lib/badline/sid/envelope.rb +0 -239
- data/lib/badline/sid/filter.rb +0 -263
- data/lib/badline/sid/voice.rb +0 -77
- data/lib/badline/sid/waveform/combined.rb +0 -126
- data/lib/badline/sid/waveform/fast_forward.rb +0 -105
- data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
- data/lib/badline/sid/waveform/state.rb +0 -66
- data/lib/badline/sid/waveform.rb +0 -305
- data/lib/badline/sid.rb +0 -303
- data/lib/badline/time_of_day.rb +0 -228
- data/lib/badline/traps.rb +0 -19
- data/lib/badline/via/control_lines.rb +0 -122
- data/lib/badline/via/fast_forward.rb +0 -53
- data/lib/badline/via/interrupt_register.rb +0 -67
- data/lib/badline/via/shift_register.rb +0 -180
- data/lib/badline/via/state.rb +0 -42
- data/lib/badline/via/timer1.rb +0 -131
- data/lib/badline/via/timer2.rb +0 -133
- data/lib/badline/via.rb +0 -266
- data/lib/badline/vic/bank.rb +0 -133
- data/lib/badline/vic/border_mask.rb +0 -86
- data/lib/badline/vic/collisions.rb +0 -115
- data/lib/badline/vic/color_patches.rb +0 -47
- data/lib/badline/vic/display_state.rb +0 -185
- data/lib/badline/vic/graphics_mode.rb +0 -164
- data/lib/badline/vic/graphics_shifter.rb +0 -207
- data/lib/badline/vic/register_log.rb +0 -81
- data/lib/badline/vic/registers.rb +0 -195
- data/lib/badline/vic/sequencer.rb +0 -275
- data/lib/badline/vic/sequencer_output.rb +0 -116
- data/lib/badline/vic/sequencer_state.rb +0 -89
- data/lib/badline/vic/sprite/internal_bus.rb +0 -68
- data/lib/badline/vic/sprite/saved_state.rb +0 -64
- data/lib/badline/vic/sprite/shifter.rb +0 -146
- data/lib/badline/vic/sprite/timing.rb +0 -56
- data/lib/badline/vic/sprite.rb +0 -337
- data/lib/badline/vic/sprites.rb +0 -282
- data/lib/badline/vic/sprites_state.rb +0 -57
- data/lib/badline/vic.rb +0 -853
- data/lib/badline/vic20/sound/output.rb +0 -83
- data/lib/badline/vic20/sound.rb +0 -233
- data/lib/badline/z80/arithmetic.rb +0 -117
- data/lib/badline/z80/bit_page.rb +0 -55
- data/lib/badline/z80/block_io.rb +0 -58
- data/lib/badline/z80/block_transfers.rb +0 -59
- data/lib/badline/z80/bus_cycles.rb +0 -111
- data/lib/badline/z80/core.rb +0 -113
- data/lib/badline/z80/exchanges.rb +0 -50
- data/lib/badline/z80/extended_page.rb +0 -89
- data/lib/badline/z80/flags.rb +0 -37
- data/lib/badline/z80/interrupt_acceptance.rb +0 -46
- data/lib/badline/z80/jumps.rb +0 -82
- data/lib/badline/z80/loads.rb +0 -98
- data/lib/badline/z80/main_page.rb +0 -134
- data/lib/badline/z80/registers.rb +0 -120
- data/lib/badline/z80/saved_state.rb +0 -69
- data/lib/badline/z80/shifts.rb +0 -64
- data/lib/badline/z80.rb +0 -11
- /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
|