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,339 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/chips/vic/sprite/internal_bus"
|
|
4
|
+
require "badline/chips/vic/sprite/shifter"
|
|
5
|
+
require "badline/chips/vic/sprite/saved_state"
|
|
6
|
+
require "badline/chips/vic/sprite/timing"
|
|
7
|
+
|
|
8
|
+
module Badline
|
|
9
|
+
module Chips
|
|
10
|
+
class VIC
|
|
11
|
+
# = Sprite
|
|
12
|
+
#
|
|
13
|
+
# The DMA/display state machine and the pixel sequencer for one sprite.
|
|
14
|
+
# The sequencer is a 24-bit shift register clocked once per pixel: the X
|
|
15
|
+
# comparator starts it, the expansion flip-flop gates the shift, and the
|
|
16
|
+
# multicolor flip-flop gates a two-bit output latch that feeds the color
|
|
17
|
+
# mux. Registers written mid-sprite therefore take hold from the pixel
|
|
18
|
+
# they reach, rather than re-decoding the row.
|
|
19
|
+
class Sprite
|
|
20
|
+
X_OFFSET = 104
|
|
21
|
+
|
|
22
|
+
# MCBASE and MC are six-bit byte counters into the sprite's 64-byte
|
|
23
|
+
# block. A sprite ends when MCBASE reaches exactly 63, which is why a
|
|
24
|
+
# mid-line $d017 write that steps it past 63 keeps it running.
|
|
25
|
+
LAST_MCBASE = 63
|
|
26
|
+
MC_MASK = 0x3f
|
|
27
|
+
|
|
28
|
+
# 24 bits at two pixels each, plus slack for a mid-sprite expansion
|
|
29
|
+
# change stretching the tail.
|
|
30
|
+
MAX_SPAN = 64
|
|
31
|
+
|
|
32
|
+
# Latch codes are the multicolor bit pairs: 0 is transparent, 1 and 3
|
|
33
|
+
# are the shared colors, 2 the sprite's own.
|
|
34
|
+
SHARED0 = 1
|
|
35
|
+
OWN_COLOR = 2
|
|
36
|
+
SHARED1 = 3
|
|
37
|
+
|
|
38
|
+
# The s-accesses reload the shift register partway through the line,
|
|
39
|
+
# at raster pixel 459 for sprite 0 and 16 later for each sprite after
|
|
40
|
+
# it. A sprite still shifting there loses the rest of its row, and the
|
|
41
|
+
# comparator is ignored for the twelve pixels from it, running on into
|
|
42
|
+
# the next line from a reload near the end of this one (the spritescan
|
|
43
|
+
# dumps). Its output holds the last pixel for seven, through K+6
|
|
44
|
+
# (spritefetchbug).
|
|
45
|
+
RELOAD_X = 459
|
|
46
|
+
RELOAD_STEP = 16
|
|
47
|
+
RELOAD_DEAD = 12
|
|
48
|
+
RELOAD_HOLD = 7
|
|
49
|
+
|
|
50
|
+
# On the line of its last row, a sprite whose DMA ended at cycle 16
|
|
51
|
+
# loses its display at cycle 58, so no hit from this pixel on starts
|
|
52
|
+
# it (spritegap3). One already shifting runs on. A region whose
|
|
53
|
+
# display compare falls later moves it with the compare.
|
|
54
|
+
DISPLAY_OFF_X = 460
|
|
55
|
+
|
|
56
|
+
include Shifter
|
|
57
|
+
include SavedState
|
|
58
|
+
|
|
59
|
+
attr_reader :index, :leftmost, :span, :codes,
|
|
60
|
+
:reload_leftmost, :reload_span, :reload_codes
|
|
61
|
+
|
|
62
|
+
def initialize(index, registers, bank, bus, model: :mos6569)
|
|
63
|
+
@index = index
|
|
64
|
+
@new_chip = model == :mos8565
|
|
65
|
+
@registers = registers
|
|
66
|
+
@bank = bank
|
|
67
|
+
@bus = bus
|
|
68
|
+
@width = bus.columns * 8
|
|
69
|
+
@timing = bus.timing
|
|
70
|
+
@bit = 1 << index
|
|
71
|
+
@dma = false
|
|
72
|
+
@display_on = false
|
|
73
|
+
@mcbase = 0
|
|
74
|
+
@mc = 0
|
|
75
|
+
@exp_ff = true
|
|
76
|
+
@crunched_mc = nil
|
|
77
|
+
@bits = 0
|
|
78
|
+
@row_ready = false
|
|
79
|
+
@codes = Array.new(MAX_SPAN + RELOAD_HOLD, 0)
|
|
80
|
+
@leftmost = 0
|
|
81
|
+
@span = 0
|
|
82
|
+
@reload_codes = Array.new(MAX_SPAN + RELOAD_HOLD, 0)
|
|
83
|
+
@reload_leftmost = 0
|
|
84
|
+
@reload_span = 0
|
|
85
|
+
reload = @timing.reload_x(index)
|
|
86
|
+
@reload_x = reload % @width
|
|
87
|
+
@reload_next_line = reload < @width
|
|
88
|
+
@sr = 0
|
|
89
|
+
@latch = 0
|
|
90
|
+
@mc_flop = false
|
|
91
|
+
@xe_flop = false
|
|
92
|
+
@first_byte_lost = false
|
|
93
|
+
@blind = false
|
|
94
|
+
@show_from = 0
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def displaying? = @dma
|
|
98
|
+
|
|
99
|
+
# True when this line has a row for the sequencer: its own, the next
|
|
100
|
+
# one for a sprite that reloads late in the line, or the last one for
|
|
101
|
+
# a sprite that reloads at its start.
|
|
102
|
+
def rendering?
|
|
103
|
+
return true if @row_ready
|
|
104
|
+
|
|
105
|
+
@reload_next_line ? @dma && @display_on : !@prev_bits.nil?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def enabled? = @registers[0x15].anybits?(@bit)
|
|
109
|
+
def multicolor? = @registers[0x1c].anybits?(@bit)
|
|
110
|
+
def x_expanded? = @registers[0x1d].anybits?(@bit)
|
|
111
|
+
def y_expanded? = @registers[0x17].anybits?(@bit)
|
|
112
|
+
def priority? = @registers[0x1b].anybits?(@bit)
|
|
113
|
+
|
|
114
|
+
def x
|
|
115
|
+
msb = @registers[0x10].anybits?(@bit) ? 0x100 : 0
|
|
116
|
+
msb | @registers[index * 2]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# The Y comparator is eight bits wide, so the PAL lines above 255
|
|
120
|
+
# match the coordinates 0-55 a second time (spritey).
|
|
121
|
+
def y_match?(line) = (line & 0xff) == @registers[(index * 2) + 1]
|
|
122
|
+
|
|
123
|
+
def color = @registers[0x27 + index] & 0x0f
|
|
124
|
+
|
|
125
|
+
# Cycle 16: MCBASE takes MC, which the three s-accesses stepped past
|
|
126
|
+
# the row, but only while the expansion flip-flop is set.
|
|
127
|
+
def advance_mcbase
|
|
128
|
+
mc = @crunched_mc || ((@mc + 3) & MC_MASK)
|
|
129
|
+
@crunched_mc = nil
|
|
130
|
+
@mcbase = mc if @exp_ff && @dma
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The end-of-sprite compare, a column after MCBASE moves. MCBASE has
|
|
134
|
+
# to land on 63 exactly, so a crunched sprite steps over it and runs
|
|
135
|
+
# on through the rest of its block. Only the DMA stops here; the
|
|
136
|
+
# display waits for cycle 58.
|
|
137
|
+
def finish_mcbase
|
|
138
|
+
@dma = false if @mcbase == LAST_MCBASE
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Clearing MxYE sets the expansion flip-flop at once. Cleared in
|
|
142
|
+
# cycle 15, while the flip-flop is still reset, it also crunches MC:
|
|
143
|
+
# the bits step to a mix of MC and MCBASE (Bauer §3.8, VICE
|
|
144
|
+
# `d017_store`) that cycle 16 then hands to MCBASE.
|
|
145
|
+
def clear_y_expansion(crunch)
|
|
146
|
+
return if @exp_ff
|
|
147
|
+
|
|
148
|
+
@exp_ff = true
|
|
149
|
+
return unless crunch && @dma
|
|
150
|
+
|
|
151
|
+
mc = (@mc + 3) & MC_MASK
|
|
152
|
+
@crunched_mc = (0x2a & @mcbase & mc) | (0x15 & (@mcbase | mc))
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Cycle 56, after the second Y compare: MxYE inverts the expansion
|
|
156
|
+
# flip-flop of a sprite with DMA running.
|
|
157
|
+
def toggle_expansion
|
|
158
|
+
@exp_ff = !@exp_ff if @dma && y_expanded?
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Cycles 55 and 56: a Y/enable match starts the DMA and rewinds
|
|
162
|
+
# MCBASE, and returns true. Display is enabled separately in cycle 58,
|
|
163
|
+
# so the rows render from the following line on.
|
|
164
|
+
#
|
|
165
|
+
# On the VIC's side, BA falls at the sprite's own column
|
|
166
|
+
# (Timing#ba_column) — 55 for sprite 0 on the 6569, two later for each
|
|
167
|
+
# sprite after it, a column behind the window the CPU sees
|
|
168
|
+
# (VIC#layout_columns) — or two columns after
|
|
169
|
+
# this compare, whichever is later. AEC follows three columns on, and
|
|
170
|
+
# the first of the three s-accesses runs in the column it arrives in:
|
|
171
|
+
# a DMA starting on the second compare therefore loses that access for
|
|
172
|
+
# sprite 0, alone among the eight in following the compares
|
|
173
|
+
# immediately (spriteenable2).
|
|
174
|
+
def check_dma(line, column)
|
|
175
|
+
return false if @dma || !enabled? || !y_match?(line)
|
|
176
|
+
|
|
177
|
+
@first_byte_lost = column + 2 > @timing.ba_column(index)
|
|
178
|
+
@dma = true
|
|
179
|
+
@mcbase = 0
|
|
180
|
+
@exp_ff = true
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Cycle 58: MC is reloaded from MCBASE for the coming row, and a
|
|
184
|
+
# sprite with DMA running starts displaying only while MxE and Y still
|
|
185
|
+
# match — a write to either between the compares and here keeps the
|
|
186
|
+
# data fetch running invisibly. The display goes off only here, and
|
|
187
|
+
# only once the DMA has, so a DMA restarted on the line of its last
|
|
188
|
+
# row keeps a display that was already on (spriterestart).
|
|
189
|
+
def check_display(line)
|
|
190
|
+
@mc = @mcbase
|
|
191
|
+
if @dma
|
|
192
|
+
@display_on = true if enabled? && y_match?(line)
|
|
193
|
+
else
|
|
194
|
+
@display_on = false
|
|
195
|
+
end
|
|
196
|
+
blind_fetch if @blind && @display_on
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# The address of the s-access that falls in phi1, the middle of the
|
|
200
|
+
# three, or nil when the sprite's DMA is off and the VIC idles there.
|
|
201
|
+
def phi1_address
|
|
202
|
+
(pointer * 64) + ((@mc + 1) & MC_MASK) if @dma
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# The row fetched at the end of the previous line renders on this one.
|
|
206
|
+
# A lost first s-access reads back the $ff the CPU is still driving.
|
|
207
|
+
def start_line
|
|
208
|
+
@span = 0
|
|
209
|
+
@reload_span = 0
|
|
210
|
+
@prev_bits = (@bits if @row_ready && @dma)
|
|
211
|
+
@row_ready = false
|
|
212
|
+
@blind = !@dma && !@reload_next_line
|
|
213
|
+
@show_from = 0
|
|
214
|
+
lost = @first_byte_lost
|
|
215
|
+
@first_byte_lost = false
|
|
216
|
+
return unless @dma && @display_on
|
|
217
|
+
|
|
218
|
+
@bits = row_bits(@mc)
|
|
219
|
+
@bits |= 0xff << 16 if lost
|
|
220
|
+
@row_ready = true
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Replay the line through the pixel sequencer, filling the code buffer
|
|
224
|
+
# from the X match on. A log of mid-line register writes makes the
|
|
225
|
+
# comparator and the shift register see the values each pixel was
|
|
226
|
+
# drawn with.
|
|
227
|
+
#
|
|
228
|
+
# The row can be shown once before the reload and once after it, so a
|
|
229
|
+
# sprite moved past the beam can fire a second time on the same line
|
|
230
|
+
# (spritex). A match inside the dead pixels shows nothing.
|
|
231
|
+
def sequence(log = nil)
|
|
232
|
+
@span = 0
|
|
233
|
+
@reload_span = 0
|
|
234
|
+
reload_bits = reload_row
|
|
235
|
+
return unless @row_ready || @prev_bits || reload_bits
|
|
236
|
+
|
|
237
|
+
log = nil if log.nil? || log.empty?
|
|
238
|
+
@stop_x = @dma ? nil : @timing.display_off_x
|
|
239
|
+
comparator_hits(log).each { |start| sequence_hit(log, start, reload_bits) }
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# The color at a raster position, for a sprite sequenced with the
|
|
243
|
+
# registers as they stand.
|
|
244
|
+
def pixel(raster_x)
|
|
245
|
+
code = code_at(raster_x, @leftmost, @span, @codes) ||
|
|
246
|
+
code_at(raster_x, @reload_leftmost, @reload_span, @reload_codes)
|
|
247
|
+
return nil if code.nil? || code.zero?
|
|
248
|
+
|
|
249
|
+
palette(@registers, Array.new(4, 0))[code]
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Fill a four-entry lookup from latch code to color, against a view of
|
|
253
|
+
# the color registers.
|
|
254
|
+
def palette(view, into)
|
|
255
|
+
into[SHARED0] = view[0x25] & 0x0f
|
|
256
|
+
into[OWN_COLOR] = view[0x27 + index] & 0x0f
|
|
257
|
+
into[SHARED1] = view[0x26] & 0x0f
|
|
258
|
+
into
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
private
|
|
262
|
+
|
|
263
|
+
# On the line whose compare started the DMA, sprites 3-7 ran their
|
|
264
|
+
# s-accesses before it was on, so the shift register holds what the
|
|
265
|
+
# VIC saw on its bus instead of a row, and a hit from the display
|
|
266
|
+
# turning on shows it (sbsprf24).
|
|
267
|
+
def blind_fetch
|
|
268
|
+
@bits = @bus.row(index)
|
|
269
|
+
@row_ready = true
|
|
270
|
+
@show_from = @timing.display_off_x
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def code_at(raster_x, leftmost, span, codes)
|
|
274
|
+
dist = raster_x - leftmost
|
|
275
|
+
dist += @width if dist.negative?
|
|
276
|
+
codes[dist] if dist < span
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def sequence_hit(log, start, reload_bits)
|
|
280
|
+
return if (@stop_x && start >= @stop_x) || start < @show_from || start < @reload_x + RELOAD_DEAD - @width
|
|
281
|
+
|
|
282
|
+
if start < @reload_x
|
|
283
|
+
sequence_early(log, start)
|
|
284
|
+
elsif start >= @reload_x + RELOAD_DEAD
|
|
285
|
+
sequence_reloaded(log, start, reload_bits)
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def sequence_early(log, start)
|
|
290
|
+
bits = early_row
|
|
291
|
+
return unless bits && @span.zero?
|
|
292
|
+
|
|
293
|
+
@leftmost = start
|
|
294
|
+
cut = reload_cut(start)
|
|
295
|
+
@span = cut_at_reload(cut, run(log, start, @codes, bits, cut), @codes)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def sequence_reloaded(log, start, bits)
|
|
299
|
+
return unless bits && @reload_span.zero?
|
|
300
|
+
|
|
301
|
+
@reload_leftmost = start
|
|
302
|
+
cut = reload_cut(start)
|
|
303
|
+
@reload_span = cut_at_reload(cut, run(log, start, @reload_codes, bits, cut), @reload_codes)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# The row in the shift register ahead of the reload: this line's for
|
|
307
|
+
# sprites 0-2, and for the others the one their reload at the start of
|
|
308
|
+
# the last line brought in.
|
|
309
|
+
def early_row = @reload_next_line ? current_row : @prev_bits
|
|
310
|
+
|
|
311
|
+
def current_row = (@bits if @row_ready)
|
|
312
|
+
|
|
313
|
+
# The row the reload brings in. Sprites 0-2 reload late in the line
|
|
314
|
+
# with the next line's row, so one that fires after it shows that row
|
|
315
|
+
# a line early (spritegap); the others reload at the start of the line
|
|
316
|
+
# with the row it already has.
|
|
317
|
+
def reload_row
|
|
318
|
+
return current_row unless @reload_next_line
|
|
319
|
+
return unless @dma && @display_on
|
|
320
|
+
|
|
321
|
+
bits = row_bits(@mc)
|
|
322
|
+
bits |= 0xff << 16 if @first_byte_lost
|
|
323
|
+
bits
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# The three s-accesses step MC through the sprite's block, wrapping
|
|
327
|
+
# within it.
|
|
328
|
+
def row_bits(counter)
|
|
329
|
+
base = pointer * 64
|
|
330
|
+
(@bank.peek(base + counter) << 16) |
|
|
331
|
+
(@bank.peek(base + ((counter + 1) & MC_MASK)) << 8) |
|
|
332
|
+
@bank.peek(base + ((counter + 2) & MC_MASK))
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def pointer = @bank.peek(@registers.screen_base + 0x3f8 + index)
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class VIC
|
|
6
|
+
class Sprites
|
|
7
|
+
# The sprites' save_state and load_state.
|
|
8
|
+
module SavedState
|
|
9
|
+
# Whether any sprite's DMA runs, set after Sprite#restore_counters.
|
|
10
|
+
def update_any_dma
|
|
11
|
+
@any_dma = @sprites.any?(&:displaying?)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# The sprites, their shared bus, the collisions and the register log,
|
|
15
|
+
# then the line's coverage and the pixels carried into the next line,
|
|
16
|
+
# each as x, colour and priority.
|
|
17
|
+
def save_state(out)
|
|
18
|
+
out.boolean(@any_dma).boolean(@stopped_dma).boolean(@carried).int(@lo).int(@hi).int(@sequenced)
|
|
19
|
+
out.blob(@win_color).booleans(@win_priority).ints(@palette)
|
|
20
|
+
[@carry, @carry_next].each { |carry| carry.each { |pixels| save_carry(pixels, out) } }
|
|
21
|
+
@sprites.each { |sprite| sprite.save_state(out) }
|
|
22
|
+
@bus.save_state(out)
|
|
23
|
+
@collisions.save_state(out)
|
|
24
|
+
@log.save_state(out)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def load_state(input)
|
|
28
|
+
@any_dma = input.boolean?
|
|
29
|
+
@stopped_dma = input.boolean?
|
|
30
|
+
@carried = input.boolean?
|
|
31
|
+
@lo = input.int
|
|
32
|
+
@hi = input.int
|
|
33
|
+
@sequenced = input.int
|
|
34
|
+
input.blob_into(@win_color)
|
|
35
|
+
input.booleans_into(@win_priority)
|
|
36
|
+
input.ints_into(@palette)
|
|
37
|
+
[@carry, @carry_next].each { |carry| carry.each { |pixels| load_carry(pixels, input) } }
|
|
38
|
+
@sprites.each { |sprite| sprite.load_state(input) }
|
|
39
|
+
@bus.load_state(input)
|
|
40
|
+
@collisions.load_state(input)
|
|
41
|
+
@log.load_state(input)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def save_carry(pixels, out)
|
|
47
|
+
out.int(pixels.length / 3)
|
|
48
|
+
pixels.each_slice(3) { |x, color, priority| out.int(x).int(color).boolean(priority) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def load_carry(pixels, input)
|
|
52
|
+
pixels.clear
|
|
53
|
+
input.int.times { pixels.push(input.int, input.int, input.boolean?) }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/chips/vic/collisions"
|
|
4
|
+
require "badline/chips/vic/register_log"
|
|
5
|
+
require "badline/chips/vic/sprite"
|
|
6
|
+
require "badline/chips/vic/sprites/saved_state"
|
|
7
|
+
|
|
8
|
+
module Badline
|
|
9
|
+
module Chips
|
|
10
|
+
class VIC
|
|
11
|
+
class Sprites
|
|
12
|
+
include SavedState
|
|
13
|
+
|
|
14
|
+
# Pixels between the start of the cycle following a write and the
|
|
15
|
+
# point the new value shows. Each signal reaches the output on its own
|
|
16
|
+
# path: the colors feed the final mux, the sequencer inputs — X
|
|
17
|
+
# position, multicolor and expansion — sit furthest upstream, and the
|
|
18
|
+
# priority mux lands one pixel ahead of them.
|
|
19
|
+
COLOR_DELAY = 1
|
|
20
|
+
PRIORITY_DELAY = 6
|
|
21
|
+
SEQUENCER_DELAY = 7
|
|
22
|
+
|
|
23
|
+
# The beam position a CPU write in Bauer cycle 15 sees: the VIC has
|
|
24
|
+
# run column 13 and runs column 14, where MCBASE moves, next.
|
|
25
|
+
CRUNCH_X = 14 * 8
|
|
26
|
+
|
|
27
|
+
WRITE_DELAY = Array.new(2**6).tap do |delays|
|
|
28
|
+
(0x00..0x0e).step(2) { |reg| delays[reg] = SEQUENCER_DELAY }
|
|
29
|
+
[0x10, 0x1c, 0x1d].each { |reg| delays[reg] = SEQUENCER_DELAY }
|
|
30
|
+
delays[0x1b] = PRIORITY_DELAY
|
|
31
|
+
[0x25, 0x26, *0x27..0x2e].each { |reg| delays[reg] = COLOR_DELAY }
|
|
32
|
+
end.freeze
|
|
33
|
+
|
|
34
|
+
# The 8565's sprite sequencers take a multicolor change a pixel
|
|
35
|
+
# sooner than the 6569's.
|
|
36
|
+
WRITE_DELAY_8565 = WRITE_DELAY.dup.tap { |delays| delays[0x1c] = SEQUENCER_DELAY - 1 }.freeze
|
|
37
|
+
|
|
38
|
+
def initialize(registers, bank, width, model: :mos6569, region: Region::PAL)
|
|
39
|
+
@registers = registers
|
|
40
|
+
@write_delay = model == :mos8565 ? WRITE_DELAY_8565 : WRITE_DELAY
|
|
41
|
+
@bank = bank
|
|
42
|
+
@width = width
|
|
43
|
+
@bus = Sprite::InternalBus.new(bank, Sprite::Timing.new(region))
|
|
44
|
+
@sprites = Array.new(8) { |i| Sprite.new(i, registers, bank, @bus, model:) }
|
|
45
|
+
@collisions = Collisions.new(registers, width)
|
|
46
|
+
@hits = @collisions.hits
|
|
47
|
+
@win_color = Array.new(width, 0)
|
|
48
|
+
@win_priority = Array.new(width, false)
|
|
49
|
+
@palette = Array.new(4, 0)
|
|
50
|
+
@log = RegisterLog.new(registers)
|
|
51
|
+
@any_dma = false
|
|
52
|
+
@stopped_dma = false
|
|
53
|
+
@carry = Array.new(8) { [] }
|
|
54
|
+
@carry_next = Array.new(8) { [] }
|
|
55
|
+
@carried = false
|
|
56
|
+
@lo = width
|
|
57
|
+
@hi = 0
|
|
58
|
+
@sequenced = -1
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def [](index) = @sprites[index]
|
|
62
|
+
|
|
63
|
+
# Pixels a sprite drew past the end of the last line land at the start
|
|
64
|
+
# of this one, which is when the beam reaches them.
|
|
65
|
+
def start_line
|
|
66
|
+
@bus.start_line(@registers[0x15].nonzero?)
|
|
67
|
+
@log.clear
|
|
68
|
+
@sprites.each(&:start_line)
|
|
69
|
+
@collisions.start_line
|
|
70
|
+
@sequenced = -1
|
|
71
|
+
@carry, @carry_next = @carry_next, @carry
|
|
72
|
+
@carry_next.each(&:clear)
|
|
73
|
+
@carried = @carry.any?(&:any?)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Bauer cycle 16, two columns ahead like the compares: MCBASE takes
|
|
77
|
+
# MC for every sprite whose expansion flip-flop is set.
|
|
78
|
+
def advance_mcbase
|
|
79
|
+
@sprites.each(&:advance_mcbase) if @any_dma
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# The end-of-sprite compare, a column after MCBASE moves. It is the
|
|
83
|
+
# only point a sprite's DMA stops, so the cached flag is settled here
|
|
84
|
+
# and set again by the compare that starts one.
|
|
85
|
+
def finish_mcbase
|
|
86
|
+
@stopped_dma = false
|
|
87
|
+
return unless @any_dma
|
|
88
|
+
|
|
89
|
+
running = @sprites.count(&:displaying?)
|
|
90
|
+
@sprites.each(&:finish_mcbase)
|
|
91
|
+
still = @sprites.count(&:displaying?)
|
|
92
|
+
@any_dma = still.positive?
|
|
93
|
+
@stopped_dma = still < running
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# True when the last #finish_mcbase ended a DMA, so the BA columns
|
|
97
|
+
# have to be rebuilt without it.
|
|
98
|
+
def stopped_dma? = @stopped_dma
|
|
99
|
+
|
|
100
|
+
# Bauer cycle 56, after the second Y compare.
|
|
101
|
+
def toggle_expansion
|
|
102
|
+
@sprites.each(&:toggle_expansion) if @any_dma
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Run the Y/enable compare (Bauer cycles 55/56) for every sprite;
|
|
106
|
+
# returns true when any sprite turned its DMA on, so the BA columns
|
|
107
|
+
# can be rebuilt mid-line.
|
|
108
|
+
def check_dma(line, column)
|
|
109
|
+
return false if @registers[0x15].zero?
|
|
110
|
+
|
|
111
|
+
hit = @sprites.map { |sprite| sprite.check_dma(line, column) }.any?
|
|
112
|
+
@any_dma ||= hit
|
|
113
|
+
hit
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Not gated on the DMA flag, since this is where a display outlives its
|
|
117
|
+
# DMA. MC now points at the next row, which a sprite firing after its
|
|
118
|
+
# reload shows, so a line sequenced before this has to be redone.
|
|
119
|
+
def check_display(line)
|
|
120
|
+
@sprites.each { |sprite| sprite.check_display(line) }
|
|
121
|
+
@sequenced = -1
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# A CPU access to a VIC register, in the cycle after the VIC's
|
|
125
|
+
# `column`.
|
|
126
|
+
def bus_data(column, value) = @bus.access(column, value)
|
|
127
|
+
|
|
128
|
+
# Not gated on the DMA flag: a row fetched at the start of the line
|
|
129
|
+
# still renders when cycle 16 ends the sprite.
|
|
130
|
+
def active? = @carried || @sprites.any?(&:rendering?)
|
|
131
|
+
|
|
132
|
+
# Record a mid-line write at the pixel where it becomes visible.
|
|
133
|
+
def log_change(reg, old, value, beam_x)
|
|
134
|
+
clear_y_expansion(old & ~value, beam_x) if reg == 0x17
|
|
135
|
+
delay = @write_delay[reg]
|
|
136
|
+
return unless delay && active?
|
|
137
|
+
|
|
138
|
+
@log.log(beam_x + delay, reg, old, value)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Latch the collisions for every sprite pixel the beam has passed
|
|
142
|
+
# since the last fold.
|
|
143
|
+
def collide_upto(beam_x, mask)
|
|
144
|
+
upto = [beam_x, @width].min
|
|
145
|
+
return if upto <= @collisions.folded
|
|
146
|
+
|
|
147
|
+
sequence_line if active?
|
|
148
|
+
@collisions.fold(mask, upto, @lo, @hi)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def clear_collision(reg, beam_x) = @collisions.clear(reg, beam_x)
|
|
152
|
+
|
|
153
|
+
# End of line: fold in the pixels the beam reached since the last
|
|
154
|
+
# read, then paint the winners over the background. A line with no
|
|
155
|
+
# background to paint over — one in the vertical blank — still
|
|
156
|
+
# collides, so `colors` is nil there rather than absent.
|
|
157
|
+
def finish_line(colors, mask)
|
|
158
|
+
collide_upto(@width, mask)
|
|
159
|
+
colors ? apply(colors, mask) : clear_coverage
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
# A $d017 write that clears MxYE acts on the flip-flop at once, rather
|
|
165
|
+
# than from a column hook, and crunches in Bauer cycle 15.
|
|
166
|
+
def clear_y_expansion(cleared, beam_x)
|
|
167
|
+
crunch = beam_x == CRUNCH_X
|
|
168
|
+
@sprites.each do |sprite|
|
|
169
|
+
sprite.clear_y_expansion(crunch) if cleared.anybits?(1 << sprite.index)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Replay the line for every sprite, filling the per-pixel coverage
|
|
174
|
+
# that both the fold and the composite read. A write logged later in
|
|
175
|
+
# the line can only move pixels past it, so re-running after the log
|
|
176
|
+
# grows leaves everything already folded where it was.
|
|
177
|
+
def sequence_line
|
|
178
|
+
return if @sequenced == @log.length
|
|
179
|
+
|
|
180
|
+
clear_coverage
|
|
181
|
+
@log.prepare
|
|
182
|
+
@sprites.each do |sprite|
|
|
183
|
+
sprite.sequence(@log)
|
|
184
|
+
merge(sprite)
|
|
185
|
+
end
|
|
186
|
+
@sequenced = @log.length
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Write one sprite's pixels into the coverage, picking up the color and
|
|
190
|
+
# priority registers as they stood at each pixel. The first sprite to
|
|
191
|
+
# claim a pixel wins (lowest index has priority); later ones only add
|
|
192
|
+
# their bit to it.
|
|
193
|
+
def merge(sprite)
|
|
194
|
+
bit = 1 << sprite.index
|
|
195
|
+
@carry_next[sprite.index].clear
|
|
196
|
+
@carry[sprite.index].each_slice(3) do |x, color, priority|
|
|
197
|
+
track(x, 1)
|
|
198
|
+
merge_pixel(x, color, bit, priority)
|
|
199
|
+
end
|
|
200
|
+
merge_run(sprite, sprite.leftmost, sprite.span, sprite.codes)
|
|
201
|
+
merge_run(sprite, sprite.reload_leftmost, sprite.reload_span, sprite.reload_codes)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def merge_run(sprite, leftmost, span, codes)
|
|
205
|
+
return if span.zero?
|
|
206
|
+
|
|
207
|
+
track(leftmost, span)
|
|
208
|
+
log = @log.empty? ? nil : @log
|
|
209
|
+
log&.rewind
|
|
210
|
+
merge_span(sprite, log, leftmost, span, codes)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def merge_span(sprite, log, leftmost, span, codes)
|
|
214
|
+
bit = 1 << sprite.index
|
|
215
|
+
palette = sprite.palette(log || @registers, @palette)
|
|
216
|
+
priority = (log || @registers)[0x1b].anybits?(bit)
|
|
217
|
+
pos = leftmost
|
|
218
|
+
boundary = log ? 0 : Float::INFINITY
|
|
219
|
+
index = 0
|
|
220
|
+
while index < span
|
|
221
|
+
if pos >= boundary
|
|
222
|
+
log.advance(pos)
|
|
223
|
+
sprite.palette(log, palette)
|
|
224
|
+
priority = log[0x1b].anybits?(bit)
|
|
225
|
+
boundary = log.next_x
|
|
226
|
+
end
|
|
227
|
+
code = codes[index]
|
|
228
|
+
place(pos, palette[code], bit, priority) unless code.zero?
|
|
229
|
+
index += 1
|
|
230
|
+
pos += 1
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# A pixel past the end of the line belongs to the start of the next.
|
|
235
|
+
def place(pos, color, bit, priority)
|
|
236
|
+
if pos < @width
|
|
237
|
+
merge_pixel(pos, color, bit, priority)
|
|
238
|
+
else
|
|
239
|
+
@carry_next[bit.bit_length - 1].push(pos - @width, color, priority)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def track(left, span)
|
|
244
|
+
right = left + span
|
|
245
|
+
if right > @width
|
|
246
|
+
@lo = 0
|
|
247
|
+
@hi = @width
|
|
248
|
+
else
|
|
249
|
+
@lo = left if left < @lo
|
|
250
|
+
@hi = right if right > @hi
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def merge_pixel(pos, color, bit, priority)
|
|
255
|
+
bits = @hits[pos]
|
|
256
|
+
if bits.zero?
|
|
257
|
+
@win_color[pos] = color
|
|
258
|
+
@win_priority[pos] = priority
|
|
259
|
+
end
|
|
260
|
+
@hits[pos] = bits | bit
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def apply(colors, mask)
|
|
264
|
+
pos = @lo
|
|
265
|
+
while pos < @hi
|
|
266
|
+
unless @hits[pos].zero?
|
|
267
|
+
colors[pos] = @win_color[pos] unless @win_priority[pos] && mask[pos]
|
|
268
|
+
@hits[pos] = 0
|
|
269
|
+
end
|
|
270
|
+
pos += 1
|
|
271
|
+
end
|
|
272
|
+
@lo = @width
|
|
273
|
+
@hi = 0
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def clear_coverage
|
|
277
|
+
@hits.fill(0, @lo, @hi - @lo) if @hi > @lo
|
|
278
|
+
@lo = @width
|
|
279
|
+
@hi = 0
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|