badline 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -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 +4 -4
- 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} +6 -6
- data/lib/badline/c128/bus.rb +30 -51
- 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 +18 -72
- data/lib/badline/{computer → c64}/attachments.rb +16 -47
- 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/c64/common.rb +34 -0
- data/lib/badline/c64/kernal_traps.rb +89 -0
- 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 +20 -31
- data/lib/badline/{computer.rb → c64.rb} +22 -80
- 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 +27 -29
- 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 +10 -61
- 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/{file.rb → file_routine.rb} +1 -1
- data/lib/badline/kernal_trap/load.rb +6 -6
- data/lib/badline/kernal_trap/save.rb +5 -5
- data/lib/badline/kernal_trap/serial.rb +7 -7
- data/lib/badline/kernal_trap.rb +2 -3
- data/lib/badline/machine/clocking.rb +50 -0
- data/lib/badline/machine.rb +63 -7
- data/lib/badline/media/boot_disk.rb +2 -2
- data/lib/badline/media/disk_list.rb +3 -3
- data/lib/badline/media/disk_set.rb +1 -1
- data/lib/badline/media/extensions.rb +33 -0
- data/lib/badline/media/program.rb +28 -0
- data/lib/badline/media/true_drive.rb +4 -3
- data/lib/badline/media/tune.rb +35 -0
- data/lib/badline/media/vic20_cartridge.rb +1 -7
- data/lib/badline/media/vic20_media.rb +38 -80
- data/lib/badline/media.rb +84 -81
- data/lib/badline/model.rb +1 -1
- data/lib/badline/options/event.rb +3 -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 +10 -2
- data/lib/badline/snapshot/setup.rb +22 -66
- data/lib/badline/snapshot/state.rb +8 -17
- 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 +13 -12
- data/lib/badline/storage/d64_image.rb +15 -9
- data/lib/badline/storage/d71_image.rb +0 -2
- data/lib/badline/storage/d81_image.rb +0 -2
- data/lib/badline/storage/disk_image/writing.rb +2 -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 +11 -81
- 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 -60
- data/lib/badline/vic20/user_via_ports.rb +2 -2
- data/lib/badline/vic20.rb +20 -51
- metadata +250 -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 -72
- data/lib/badline/address_bus/sid_slots.rb +0 -42
- data/lib/badline/address_bus.rb +0 -235
- 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/computer/kernal_traps.rb +0 -29
- 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/cycleable.rb +0 -38
- 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 -318
- data/lib/badline/drive1541/disk_state.rb +0 -94
- 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 -852
- 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,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class CPU
|
|
6
|
+
module Interrupts
|
|
7
|
+
BREAK_MASK = 0x10
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# Samples the IRQ line and the NMI latch once per cycle. An instruction
|
|
12
|
+
# boundary commits the sample taken on its second-to-last cycle.
|
|
13
|
+
def poll
|
|
14
|
+
if @skip_poll
|
|
15
|
+
@skip_poll = false
|
|
16
|
+
return
|
|
17
|
+
end
|
|
18
|
+
@irq_pending = @irq_sample
|
|
19
|
+
@nmi_pending = @nmi_sample
|
|
20
|
+
@irq_sample = @irq && !@status.interrupt?
|
|
21
|
+
@nmi_sample = @nmi
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# A cycle the VIC stalls through BA keeps sampling the lines, OR-ed with
|
|
25
|
+
# the sample it already holds, but the boundary pipeline doesn't advance
|
|
26
|
+
# and a skipped poll stays owed to the next real cycle.
|
|
27
|
+
def sample_while_stalled
|
|
28
|
+
@irq_sample ||= @irq && !interrupt_disabled_after_stall?
|
|
29
|
+
@nmi_sample = true if @nmi
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# A stalled CLI or SEI execute cycle masks with the I it is about to
|
|
33
|
+
# set. Every other step masks with the current I, PLP included: its
|
|
34
|
+
# pulled I only arrives with the stalled stack read.
|
|
35
|
+
def interrupt_disabled_after_stall?
|
|
36
|
+
return @status.interrupt? unless @plan.equal?(CPU::IMPLIED_PLAN)
|
|
37
|
+
|
|
38
|
+
case @operation
|
|
39
|
+
when :cli then false
|
|
40
|
+
when :sei then true
|
|
41
|
+
else @status.interrupt?
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Runs in the opcode fetch cycle in place of the fetch, with no bus
|
|
46
|
+
# access, and switches to the interrupt plan.
|
|
47
|
+
def start_interrupt
|
|
48
|
+
@interrupt = @boundary_nmi ? 0xfffa : 0xfffe
|
|
49
|
+
@brk = false
|
|
50
|
+
@plan = CPU::INTERRUPT_PLAN
|
|
51
|
+
@writes = CPU::INTERRUPT_WRITES
|
|
52
|
+
@index = 1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# BRK reads the byte after its opcode and ignores it, and selects the
|
|
56
|
+
# IRQ vector.
|
|
57
|
+
def op_brk_dummy
|
|
58
|
+
@memory.peek(@program_counter)
|
|
59
|
+
@interrupt = 0xfffe
|
|
60
|
+
@brk = true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def op_int_push_pch
|
|
64
|
+
@address = @brk ? (@program_counter + 1) & 0xffff : @program_counter
|
|
65
|
+
@memory.poke(stack_address, high_byte(@address))
|
|
66
|
+
@stack_pointer = (@stack_pointer - 1) & 0xff
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def op_int_push_pcl
|
|
70
|
+
@memory.poke(stack_address, low_byte(@address))
|
|
71
|
+
@stack_pointer = (@stack_pointer - 1) & 0xff
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def op_int_push_status
|
|
75
|
+
value = @brk ? @status.value | BREAK_MASK : @status.value & ~BREAK_MASK
|
|
76
|
+
@memory.poke(stack_address, value)
|
|
77
|
+
@stack_pointer = (@stack_pointer - 1) & 0xff
|
|
78
|
+
@status.interrupt = true
|
|
79
|
+
# An NMI that arrives before cycle 4 of a BRK or IRQ takes it over:
|
|
80
|
+
# the sequence finishes with the NMI vector.
|
|
81
|
+
@interrupt = 0xfffa if @interrupt == 0xfffe && @nmi_pending
|
|
82
|
+
@nmi = false if @interrupt == 0xfffa
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def op_int_vector_low
|
|
86
|
+
@address = @memory.peek(@interrupt)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Clears any interrupt that is pending, so the handler's first
|
|
90
|
+
# instruction always runs before the next interrupt. This applies to
|
|
91
|
+
# BRK too.
|
|
92
|
+
def op_int_vector_high
|
|
93
|
+
high = @memory.peek((@interrupt + 1) & 0xffff)
|
|
94
|
+
@program_counter = @address | (high << 8)
|
|
95
|
+
@irq_sample = @irq_pending = false
|
|
96
|
+
@nmi_sample = @nmi_pending = false
|
|
97
|
+
@interrupt = nil
|
|
98
|
+
if @brk
|
|
99
|
+
@status.break = false
|
|
100
|
+
end_instruction
|
|
101
|
+
else
|
|
102
|
+
end_sequence
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class CPU
|
|
6
|
+
class InvalidOpcodeError < StandardError; end
|
|
7
|
+
|
|
8
|
+
# Steps that write to memory. The CPU keeps running through BA on
|
|
9
|
+
# cycles that write, so C64#cycle! asks before each cycle whether
|
|
10
|
+
# the next step is one of these.
|
|
11
|
+
WRITE_STEPS = %i[
|
|
12
|
+
op_mem_write op_rmw_write_original op_rmw_write_result op_push_exec op_jsr_push_high op_jsr_push_low
|
|
13
|
+
op_int_push_pch op_int_push_pcl op_int_push_status
|
|
14
|
+
].freeze
|
|
15
|
+
|
|
16
|
+
# The steps each addressing mode takes to work out the address an
|
|
17
|
+
# instruction operates on. They run after the opcode fetch.
|
|
18
|
+
ADDRESSING_STEPS = {
|
|
19
|
+
zeropage: %i[op_zp],
|
|
20
|
+
zeropage_x: %i[op_zp op_zp_x],
|
|
21
|
+
zeropage_y: %i[op_zp op_zp_y],
|
|
22
|
+
absolute: %i[op_abs_low op_abs_high],
|
|
23
|
+
absolute_x: %i[op_abs_low op_abs_high_x op_idx_dummy],
|
|
24
|
+
absolute_y: %i[op_abs_low op_abs_high_y op_idx_dummy],
|
|
25
|
+
indirect_x: %i[op_zp_pointer op_pointer_x op_pointer_low op_pointer_high],
|
|
26
|
+
indirect_y: %i[op_zp_pointer op_pointer_low op_pointer_high_y op_idx_dummy]
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
# The steps that read or write that address, once it is known.
|
|
30
|
+
OPERATION_STEPS = {
|
|
31
|
+
read: %i[op_mem_read],
|
|
32
|
+
write: %i[op_mem_write],
|
|
33
|
+
rmw: %i[op_rmw_read op_rmw_write_original op_rmw_write_result]
|
|
34
|
+
}.freeze
|
|
35
|
+
|
|
36
|
+
STORES = %i[sta stx sty sax sha shx shy tas].freeze
|
|
37
|
+
READ_MODIFY_WRITES = %i[asl lsr rol ror inc dec slo sre rla rra isc dcp].freeze
|
|
38
|
+
BRANCHES = %i[bcc bcs beq bmi bne bpl bvc bvs].freeze
|
|
39
|
+
|
|
40
|
+
# The addresses a 6502 reads after a JAM opcode, before it settles into
|
|
41
|
+
# JAMMED_PLAN.
|
|
42
|
+
JAM_ADDRESSES = [0xffff, 0xfffe, 0xfffe].freeze
|
|
43
|
+
|
|
44
|
+
# The plan between instructions: the next cycle fetches an opcode.
|
|
45
|
+
FETCH_PLAN = %i[op_fetch].freeze
|
|
46
|
+
|
|
47
|
+
IMPLIED_PLAN = %i[op_fetch op_implied_exec].freeze
|
|
48
|
+
ACCUMULATOR_PLAN = %i[op_fetch op_accumulator_exec].freeze
|
|
49
|
+
IMMEDIATE_PLAN = %i[op_fetch op_immediate_read].freeze
|
|
50
|
+
|
|
51
|
+
# $10, $30, $50, $70, $90, $B0, $D0, $F0 - relative - 2+ cycles.
|
|
52
|
+
# A branch that is not taken takes 2 cycles. A taken branch takes 3,
|
|
53
|
+
# or 4 when the target is on another page.
|
|
54
|
+
BRANCH_PLAN = %i[op_fetch op_relative op_branch op_branch_fixup].freeze
|
|
55
|
+
|
|
56
|
+
# $4C - absolute - 3 cycles
|
|
57
|
+
JMP_ABSOLUTE_PLAN = %i[op_fetch op_abs_low op_jmp_high].freeze
|
|
58
|
+
|
|
59
|
+
# $6C - indirect - 5 cycles
|
|
60
|
+
JMP_INDIRECT_PLAN = %i[op_fetch op_abs_low op_abs_high op_indirect_low op_indirect_high].freeze
|
|
61
|
+
|
|
62
|
+
# $20 - absolute - 6 cycles
|
|
63
|
+
JSR_PLAN = %i[op_fetch op_jsr_low op_jsr_dummy op_jsr_push_high op_jsr_push_low op_jsr_high].freeze
|
|
64
|
+
|
|
65
|
+
# $60 - implied - 6 cycles
|
|
66
|
+
RTS_PLAN = %i[op_fetch op_implied_dummy op_pull_dummy op_pull_low op_pull_high op_rts_fixup].freeze
|
|
67
|
+
|
|
68
|
+
# $40 - implied - 6 cycles
|
|
69
|
+
RTI_PLAN = %i[op_fetch op_implied_dummy op_pull_dummy op_rti_pull_status op_pull_low op_rti_pull_high].freeze
|
|
70
|
+
|
|
71
|
+
# $08, $48 - implied - 3 cycles
|
|
72
|
+
PUSH_PLAN = %i[op_fetch op_implied_dummy op_push_exec].freeze
|
|
73
|
+
|
|
74
|
+
# $28, $68 - implied - 4 cycles
|
|
75
|
+
PULL_PLAN = %i[op_fetch op_implied_dummy op_pull_dummy op_pull_exec].freeze
|
|
76
|
+
|
|
77
|
+
# $02, $12, $22, $32, $42, $52, $62, $72, $92, $B2, $D2, $F2 - illegal
|
|
78
|
+
JAM_PLAN = [:op_fetch, :op_implied_dummy, *Array.new(JAM_ADDRESSES.length, :op_jam)].freeze
|
|
79
|
+
|
|
80
|
+
# A jammed CPU reads $FFFF on every cycle until reset. It never reaches
|
|
81
|
+
# an instruction boundary, so a pending IRQ or NMI never starts.
|
|
82
|
+
JAMMED_PLAN = %i[op_jammed].freeze
|
|
83
|
+
|
|
84
|
+
# BRK and hardware interrupts push the return address and status, then
|
|
85
|
+
# read the new PC from a vector. Only the second step differs: BRK's
|
|
86
|
+
# selects the IRQ vector and sets the break flag it pushes.
|
|
87
|
+
#
|
|
88
|
+
# $00 - implied - 7 cycles (BRK)
|
|
89
|
+
INTERRUPT_PLAN = %i[
|
|
90
|
+
op_fetch op_implied_dummy op_int_push_pch op_int_push_pcl op_int_push_status
|
|
91
|
+
op_int_vector_low op_int_vector_high
|
|
92
|
+
].freeze
|
|
93
|
+
BRK_PLAN = %i[
|
|
94
|
+
op_fetch op_brk_dummy op_int_push_pch op_int_push_pcl op_int_push_status
|
|
95
|
+
op_int_vector_low op_int_vector_high
|
|
96
|
+
].freeze
|
|
97
|
+
|
|
98
|
+
SPECIAL_PLANS = {
|
|
99
|
+
brk: BRK_PLAN, jam: JAM_PLAN, jsr: JSR_PLAN, rts: RTS_PLAN,
|
|
100
|
+
rti: RTI_PLAN, pha: PUSH_PLAN, php: PUSH_PLAN,
|
|
101
|
+
pla: PULL_PLAN, plp: PULL_PLAN
|
|
102
|
+
}.freeze
|
|
103
|
+
|
|
104
|
+
# Everything the CPU needs to run one opcode:
|
|
105
|
+
# - +operation+: the instruction method to call, such as :lda
|
|
106
|
+
# - +plan+: the step to run on each cycle
|
|
107
|
+
# - +writes+: which of those steps write to memory
|
|
108
|
+
# - +optional_dummy+: true for indexed reads, which skip the extra
|
|
109
|
+
# cycle unless indexing crosses a page
|
|
110
|
+
Microcode = Struct.new(:operation, :plan, :writes, :optional_dummy) do
|
|
111
|
+
def self.table
|
|
112
|
+
Array.new(256) do |opcode|
|
|
113
|
+
instruction = Instruction.find(opcode)
|
|
114
|
+
raise InvalidOpcodeError, format("$%02x", opcode) unless instruction
|
|
115
|
+
|
|
116
|
+
build(instruction)
|
|
117
|
+
end.freeze
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.write_mask(plan)
|
|
121
|
+
plan.map { |step| WRITE_STEPS.include?(step) }.freeze
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def self.build(instruction)
|
|
125
|
+
plan = plan_for(instruction)
|
|
126
|
+
new(instruction.name, plan, write_mask(plan),
|
|
127
|
+
instruction.boundary_cycle?)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def self.plan_for(instruction)
|
|
131
|
+
case instruction.name
|
|
132
|
+
when :jmp
|
|
133
|
+
if instruction.addressing_mode == :indirect
|
|
134
|
+
JMP_INDIRECT_PLAN
|
|
135
|
+
else
|
|
136
|
+
JMP_ABSOLUTE_PLAN
|
|
137
|
+
end
|
|
138
|
+
when *BRANCHES then BRANCH_PLAN
|
|
139
|
+
else SPECIAL_PLANS[instruction.name] || addressed_plan(instruction)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def self.addressed_plan(instruction)
|
|
144
|
+
mode = instruction.addressing_mode
|
|
145
|
+
case mode
|
|
146
|
+
when :implied then IMPLIED_PLAN
|
|
147
|
+
when :accumulator then ACCUMULATOR_PLAN
|
|
148
|
+
when :immediate then IMMEDIATE_PLAN
|
|
149
|
+
else
|
|
150
|
+
[:op_fetch, *ADDRESSING_STEPS.fetch(mode),
|
|
151
|
+
*OPERATION_STEPS.fetch(operation_kind(instruction.name))].freeze
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def self.operation_kind(name)
|
|
156
|
+
return :write if STORES.include?(name)
|
|
157
|
+
return :rmw if READ_MODIFY_WRITES.include?(name)
|
|
158
|
+
|
|
159
|
+
:read
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private_class_method :build, :plan_for, :addressed_plan, :operation_kind
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
FETCH_WRITES = Microcode.write_mask(FETCH_PLAN)
|
|
166
|
+
INTERRUPT_WRITES = Microcode.write_mask(INTERRUPT_PLAN)
|
|
167
|
+
JAMMED_WRITES = Microcode.write_mask(JAMMED_PLAN)
|
|
168
|
+
MICROCODE = Microcode.table
|
|
169
|
+
|
|
170
|
+
# The instruction methods, in a fixed order a snapshot names them by.
|
|
171
|
+
OPERATIONS = MICROCODE.map(&:operation).uniq.freeze
|
|
172
|
+
|
|
173
|
+
# The plans a snapshot names that aren't an opcode's.
|
|
174
|
+
FETCH = -1
|
|
175
|
+
INTERRUPT = -2
|
|
176
|
+
JAMMED = -3
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class CPU
|
|
6
|
+
# Steps that read or write an instruction's operand once its address is
|
|
7
|
+
# known, plus the branch and JAM steps.
|
|
8
|
+
module Operations
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def op_mem_read
|
|
12
|
+
send(@operation, @address, @memory.peek(@address))
|
|
13
|
+
end_instruction
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def op_mem_write
|
|
17
|
+
send(@operation, @address, nil)
|
|
18
|
+
end_instruction
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Read-modify-write instructions (ASL, INC and the like) take three
|
|
22
|
+
# cycles here: read the value and compute the result, write the
|
|
23
|
+
# original value back, then write the result. The instruction method
|
|
24
|
+
# hands the result over through #write_modified.
|
|
25
|
+
def op_rmw_read
|
|
26
|
+
@value = @memory.peek(@address)
|
|
27
|
+
send(@operation, @address, @value)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def op_rmw_write_original
|
|
31
|
+
@memory.poke(@address, @value)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def op_rmw_write_result
|
|
35
|
+
@memory.poke(@address, @rmw_result)
|
|
36
|
+
end_instruction
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def op_relative
|
|
40
|
+
offset = @memory.peek(@program_counter)
|
|
41
|
+
@program_counter = (@program_counter + 1) & 0xffff
|
|
42
|
+
@address = (@program_counter + signed_int8(offset)) & 0xffff
|
|
43
|
+
@branch_taken = false
|
|
44
|
+
send(@operation, @address, nil)
|
|
45
|
+
if @branch_taken
|
|
46
|
+
# A taken branch within the same page doesn't poll on its last cycle.
|
|
47
|
+
@skip_poll = true if same_page?
|
|
48
|
+
else
|
|
49
|
+
end_instruction
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def op_branch
|
|
54
|
+
@memory.peek(@program_counter)
|
|
55
|
+
return unless same_page?
|
|
56
|
+
|
|
57
|
+
@program_counter = @address
|
|
58
|
+
end_instruction
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# A branch to another page spends an extra cycle reading from the target's low byte on the old page.
|
|
62
|
+
def op_branch_fixup
|
|
63
|
+
@memory.peek((@program_counter & 0xff00) | (@address & 0xff))
|
|
64
|
+
@program_counter = @address
|
|
65
|
+
end_instruction
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def same_page?
|
|
69
|
+
@address >> 8 == @program_counter >> 8
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Runs once for each address in JAM_ADDRESSES. By the time a step runs, @index already points at the next one.
|
|
73
|
+
def op_jam
|
|
74
|
+
position = @index - 2
|
|
75
|
+
@memory.peek(JAM_ADDRESSES[position - 1])
|
|
76
|
+
return unless position == JAM_ADDRESSES.length
|
|
77
|
+
|
|
78
|
+
@plan = JAMMED_PLAN
|
|
79
|
+
@writes = JAMMED_WRITES
|
|
80
|
+
@index = 0
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def op_jammed
|
|
84
|
+
@index = 0
|
|
85
|
+
@memory.peek(0xffff)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class CPU
|
|
6
|
+
# The I/O port of the 6510 and the 8502 at $00 (direction) and $01
|
|
7
|
+
# (data). An input pin reads its pull-up, P4 reads the datasette's sense
|
|
8
|
+
# line, and a pin without a pull-up holds the charge last driven onto
|
|
9
|
+
# it. P5 drives the datasette motor through an inverter: low runs it.
|
|
10
|
+
#
|
|
11
|
+
# The 6510 has pull-ups on P0-P2 and P4 and leaves P3, P6 and P7
|
|
12
|
+
# floating. The 8502 adds a CAPS LOCK pin on P6, high while the key is
|
|
13
|
+
# up.
|
|
14
|
+
class Port
|
|
15
|
+
TAPE_SENSE = 0b0001_0000
|
|
16
|
+
|
|
17
|
+
# The direction and output registers, and the charge on the floating
|
|
18
|
+
# pins.
|
|
19
|
+
attr_reader :ddr, :data, :floating
|
|
20
|
+
|
|
21
|
+
# The port as the banking logic reads it (PortStatus).
|
|
22
|
+
attr_reader :status
|
|
23
|
+
|
|
24
|
+
# Whether CAPS LOCK is down, holding its pin low.
|
|
25
|
+
attr_reader :caps_lock
|
|
26
|
+
|
|
27
|
+
# +pullups+ and +floating+ are the masks of the pins with a pull-up and
|
|
28
|
+
# of the floating ones, and +caps_lock+ the CAPS LOCK pin's, 0 where
|
|
29
|
+
# there is none.
|
|
30
|
+
def initialize(datasette, pullups:, floating:, caps_lock: 0)
|
|
31
|
+
@datasette = datasette
|
|
32
|
+
@pullups = pullups
|
|
33
|
+
@floating_mask = floating
|
|
34
|
+
@caps_lock_pin = caps_lock
|
|
35
|
+
@caps_lock = false
|
|
36
|
+
@ddr = 0x00
|
|
37
|
+
@data = 0x00
|
|
38
|
+
@floating = 0x00
|
|
39
|
+
@change_handler = nil
|
|
40
|
+
@status = PortStatus.new(%i[basic kernal io tape_out tape_switch tape_motor], value: input_value)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Calls the block after every write to the port, for the bus to remap
|
|
44
|
+
# its banks.
|
|
45
|
+
def on_change(&handler)
|
|
46
|
+
@change_handler = handler
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# What a read of $01 sees.
|
|
50
|
+
def value = @status.value
|
|
51
|
+
|
|
52
|
+
def write_ddr(value)
|
|
53
|
+
@ddr = value
|
|
54
|
+
update
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def write_data(value)
|
|
58
|
+
@data = value
|
|
59
|
+
update
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The RES line clears the direction and output registers. The floating
|
|
63
|
+
# pins keep their charge.
|
|
64
|
+
def reset!
|
|
65
|
+
@ddr = 0x00
|
|
66
|
+
@data = 0x00
|
|
67
|
+
update
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Sets the registers and the charge, as a snapshot restores them, and
|
|
71
|
+
# drives the motor and calls the change handler as a write does.
|
|
72
|
+
def restore(ddr, data, floating)
|
|
73
|
+
load(ddr, data, floating)
|
|
74
|
+
update
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Sets the registers and the charge, as a snapshot loads them, without
|
|
78
|
+
# driving the motor or calling the change handler.
|
|
79
|
+
def load(ddr, data, floating)
|
|
80
|
+
@ddr = ddr
|
|
81
|
+
@data = data
|
|
82
|
+
@floating = floating
|
|
83
|
+
refresh
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Holds CAPS LOCK down, or lets it up.
|
|
87
|
+
def caps_lock=(down)
|
|
88
|
+
@caps_lock = down
|
|
89
|
+
refresh
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Reads the inputs again, after the datasette's sense line changes.
|
|
93
|
+
def refresh
|
|
94
|
+
@status.value = input_value
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def update
|
|
100
|
+
driven = @ddr & @floating_mask
|
|
101
|
+
@floating = (@floating & ~driven) | (@data & driven)
|
|
102
|
+
refresh
|
|
103
|
+
@datasette.motor = !@status.tape_motor?
|
|
104
|
+
@change_handler&.call
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def input_value
|
|
108
|
+
input = @pullups | (@floating & @floating_mask)
|
|
109
|
+
input |= @caps_lock_pin unless @caps_lock
|
|
110
|
+
input &= ~TAPE_SENSE if @datasette.sense_low?
|
|
111
|
+
(@data & @ddr) | (input & ~@ddr & 0xff)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class CPU
|
|
6
|
+
# The CPU's state for a snapshot: the registers, the interrupt lines and
|
|
7
|
+
# their pipeline, and the step of the instruction in flight, named by
|
|
8
|
+
# where its plan comes from.
|
|
9
|
+
module SavedState
|
|
10
|
+
# Writes everything the CPU holds, down to the step of the instruction
|
|
11
|
+
# it is in, for load_state to put back. The interrupt lines, BA stalls
|
|
12
|
+
# and SO are state too; the traps and the debug flag belong to the host.
|
|
13
|
+
def save_state(out)
|
|
14
|
+
out.marker("CPU")
|
|
15
|
+
out.int(@program_counter).int(@stack_pointer).int(@a).int(@x).int(@y).int(@status.value)
|
|
16
|
+
[@irq, @nmi, @irq_sample, @irq_pending, @nmi_sample, @nmi_pending, @skip_poll, @boundary_irq,
|
|
17
|
+
@boundary_nmi, @boundary_crossed, @brk, @pending_write, @so_high, @optional_dummy,
|
|
18
|
+
@branch_taken].each { |flag| out.boolean(flag) }
|
|
19
|
+
out.optional_int(@interrupt).optional_int(@stalled_at).int(@cycles).int(@instructions)
|
|
20
|
+
out.int(plan_source).int(@index).int(OPERATIONS.index(@operation) || -1)
|
|
21
|
+
[@address, @dummy_address, @value, @rmw_result, @pointer].each { |register| out.optional_int(register) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def load_state(input)
|
|
25
|
+
input.marker("CPU")
|
|
26
|
+
@program_counter = input.int
|
|
27
|
+
@stack_pointer = input.int
|
|
28
|
+
@a = input.int
|
|
29
|
+
@x = input.int
|
|
30
|
+
@y = input.int
|
|
31
|
+
@status.value = input.int
|
|
32
|
+
load_flags(input)
|
|
33
|
+
@interrupt = input.optional_int
|
|
34
|
+
@stalled_at = input.optional_int
|
|
35
|
+
@cycles = input.int
|
|
36
|
+
@instructions = input.int
|
|
37
|
+
load_plan(input.int)
|
|
38
|
+
@index = input.int
|
|
39
|
+
operation = input.int
|
|
40
|
+
@operation = operation.negative? ? nil : OPERATIONS.fetch(operation)
|
|
41
|
+
load_working_registers(input)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Drops the instruction or interrupt sequence in flight and the
|
|
45
|
+
# interrupt lines' pipeline, so the next cycle fetches an opcode at the
|
|
46
|
+
# program counter, and sets the cycle count. A snapshot taken between
|
|
47
|
+
# instructions, as VICE's are, restores through here.
|
|
48
|
+
def resume(cycles)
|
|
49
|
+
@irq_sample = @irq_pending = false
|
|
50
|
+
@nmi_sample = @nmi_pending = false
|
|
51
|
+
@skip_poll = false
|
|
52
|
+
@interrupt = nil
|
|
53
|
+
@brk = false
|
|
54
|
+
@stalled_at = nil
|
|
55
|
+
@cycles = cycles
|
|
56
|
+
end_sequence
|
|
57
|
+
@pending_write = false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
# Where the plan running comes from: an opcode's microcode, or FETCH,
|
|
63
|
+
# INTERRUPT or JAMMED below it.
|
|
64
|
+
def plan_source
|
|
65
|
+
return FETCH if @plan.equal?(FETCH_PLAN)
|
|
66
|
+
return INTERRUPT if @plan.equal?(INTERRUPT_PLAN)
|
|
67
|
+
return JAMMED if @plan.equal?(JAMMED_PLAN)
|
|
68
|
+
|
|
69
|
+
MICROCODE.index { |micro| micro.plan.equal?(@plan) && micro.operation == @operation } ||
|
|
70
|
+
MICROCODE.index { |micro| micro.plan.equal?(@plan) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def load_plan(source)
|
|
74
|
+
case source
|
|
75
|
+
when FETCH then use_plan(FETCH_PLAN, FETCH_WRITES)
|
|
76
|
+
when INTERRUPT then use_plan(INTERRUPT_PLAN, INTERRUPT_WRITES)
|
|
77
|
+
when JAMMED then use_plan(JAMMED_PLAN, JAMMED_WRITES)
|
|
78
|
+
else
|
|
79
|
+
micro = MICROCODE.fetch(source)
|
|
80
|
+
use_plan(micro.plan, micro.writes)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def use_plan(plan, writes)
|
|
85
|
+
@plan = plan
|
|
86
|
+
@writes = writes
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def load_flags(input)
|
|
90
|
+
@irq = input.boolean?
|
|
91
|
+
@nmi = input.boolean?
|
|
92
|
+
@irq_sample = input.boolean?
|
|
93
|
+
@irq_pending = input.boolean?
|
|
94
|
+
@nmi_sample = input.boolean?
|
|
95
|
+
@nmi_pending = input.boolean?
|
|
96
|
+
@skip_poll = input.boolean?
|
|
97
|
+
@boundary_irq = input.boolean?
|
|
98
|
+
@boundary_nmi = input.boolean?
|
|
99
|
+
@boundary_crossed = input.boolean?
|
|
100
|
+
@brk = input.boolean?
|
|
101
|
+
@pending_write = input.boolean?
|
|
102
|
+
@so_high = input.boolean?
|
|
103
|
+
@optional_dummy = input.boolean?
|
|
104
|
+
@branch_taken = input.boolean?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def load_working_registers(input)
|
|
108
|
+
@address = input.optional_int
|
|
109
|
+
@dummy_address = input.optional_int
|
|
110
|
+
@value = input.optional_int
|
|
111
|
+
@rmw_result = input.optional_int
|
|
112
|
+
@pointer = input.optional_int
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class CPU
|
|
6
|
+
# Steps that use the stack: PHA, PHP, PLA, PLP, JSR, RTS and RTI.
|
|
7
|
+
module StackOperations
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def op_push_exec
|
|
11
|
+
send(@operation, nil, nil)
|
|
12
|
+
end_instruction
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def op_pull_dummy
|
|
16
|
+
@memory.peek(stack_address)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def op_pull_exec
|
|
20
|
+
send(@operation, nil, nil)
|
|
21
|
+
end_instruction
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def op_pull_low
|
|
25
|
+
@stack_pointer = (@stack_pointer + 1) & 0xff
|
|
26
|
+
@address = @memory.peek(stack_address)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def op_pull_high
|
|
30
|
+
@stack_pointer = (@stack_pointer + 1) & 0xff
|
|
31
|
+
@address |= @memory.peek(stack_address) << 8
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def op_rts_fixup
|
|
35
|
+
@memory.peek(@address)
|
|
36
|
+
@program_counter = (@address + 1) & 0xffff
|
|
37
|
+
end_instruction
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def op_rti_pull_status
|
|
41
|
+
@stack_pointer = (@stack_pointer + 1) & 0xff
|
|
42
|
+
@status.value = @memory.peek(stack_address)
|
|
43
|
+
@status.break = false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def op_rti_pull_high
|
|
47
|
+
@stack_pointer = (@stack_pointer + 1) & 0xff
|
|
48
|
+
@program_counter = @address | (@memory.peek(stack_address) << 8)
|
|
49
|
+
end_instruction
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def op_jsr_low
|
|
53
|
+
@address = @memory.peek(@program_counter)
|
|
54
|
+
@program_counter = (@program_counter + 2) & 0xffff
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def op_jsr_dummy
|
|
58
|
+
@memory.peek(stack_address)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def op_jsr_push_high
|
|
62
|
+
@memory.poke(stack_address, high_byte(return_address))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def op_jsr_push_low
|
|
66
|
+
@memory.poke(stack_address(-1), low_byte(return_address))
|
|
67
|
+
@stack_pointer = (@stack_pointer - 2) & 0xff
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# JSR reads its target's high byte after pushing the return address.
|
|
71
|
+
# A JSR running from the stack page can overwrite its own operand
|
|
72
|
+
# with that push, and then jumps to the overwritten address.
|
|
73
|
+
def op_jsr_high
|
|
74
|
+
@program_counter = @address | (@memory.peek(return_address) << 8)
|
|
75
|
+
end_instruction
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def return_address
|
|
79
|
+
(@program_counter - 1) & 0xffff
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|