badline 0.12.1 → 0.14.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 +66 -1
- data/CLAUDE.md +86 -38
- data/CONTRIBUTING.md +4 -3
- data/README.md +9 -6
- data/doc/library.md +9 -9
- data/doc/media.md +2 -1
- data/doc/pinned-behaviour.md +134 -105
- data/doc/snapshot-format.md +1047 -0
- data/doc/snapshots.md +73 -43
- data/lib/badline/audio/bare_player.rb +53 -20
- data/lib/badline/audio/cli.rb +6 -6
- data/lib/badline/audio/jukebox.rb +2 -2
- data/lib/badline/audio/machine_player.rb +16 -10
- data/lib/badline/{media → audio}/queue.rb +1 -1
- data/lib/badline/audio/queued_tune.rb +6 -6
- data/lib/badline/audio/renderer.rb +33 -22
- data/lib/badline/audio/stereo.rb +1 -1
- data/lib/badline/c128/banks.rb +6 -3
- data/lib/badline/c128/bus/keyed_state.rb +45 -0
- data/lib/badline/c128/bus.rb +35 -20
- data/lib/badline/c128/color_lines.rb +2 -2
- data/lib/badline/c128/cpu.rb +1 -1
- data/lib/badline/c128/drives.rb +14 -34
- data/lib/badline/c128/fast_serial.rb +5 -5
- data/lib/badline/c128/keyed_modules.rb +117 -0
- data/lib/badline/c128/keyed_state.rb +91 -0
- data/lib/badline/c128/keys.rb +9 -0
- data/lib/badline/c128/mmu.rb +9 -8
- data/lib/badline/c128/mmu_pages.rb +21 -1
- data/lib/badline/c128/modes.rb +21 -11
- data/lib/badline/c128/z80.rb +2 -2
- data/lib/badline/c128/z80_turns.rb +1 -1
- data/lib/badline/c128.rb +21 -16
- data/lib/badline/c64/attachments.rb +42 -0
- data/lib/badline/c64/banked_ram/banking.rb +32 -0
- data/lib/badline/c64/banked_ram/plus256k.rb +64 -0
- data/lib/badline/c64/banked_ram/plus60k.rb +47 -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/debug_register.rb +32 -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/keyed_state.rb +41 -0
- data/lib/badline/c64/bus/pla.rb +149 -0
- data/lib/badline/c64/bus/roms.rb +41 -0
- data/lib/badline/c64/bus/sid_slots.rb +44 -0
- data/lib/badline/c64/bus.rb +219 -0
- data/lib/badline/c64/cartridge/action_replay.rb +115 -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 +132 -0
- data/lib/badline/c64/cartridge/epyx_fastload.rb +73 -0
- data/lib/badline/c64/cartridge/final_cartridge3.rb +79 -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 +64 -0
- data/lib/badline/c64/cartridge/game_system.rb +42 -0
- data/lib/badline/c64/cartridge/geo_ram.rb +65 -0
- data/lib/badline/c64/cartridge/kcs_power.rb +69 -0
- data/lib/badline/c64/cartridge/keyed_state.rb +393 -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 +60 -0
- data/lib/badline/c64/cartridge/retro_replay.rb +278 -0
- data/lib/badline/c64/cartridge/rex_utility.rb +31 -0
- data/lib/badline/c64/cartridge/rgcd.rb +50 -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 +40 -0
- data/lib/badline/c64/cartridge/westermann.rb +32 -0
- data/lib/badline/c64/cartridge/zaxxon.rb +52 -0
- data/lib/badline/c64/cartridge.rb +232 -0
- data/lib/badline/c64/color_memory.rb +27 -0
- data/lib/badline/{computer → c64}/common.rb +2 -2
- data/lib/badline/{computer → c64}/kernal_traps.rb +39 -36
- data/lib/badline/c64/keyed_modules.rb +111 -0
- data/lib/badline/c64/keyed_state.rb +68 -0
- data/lib/badline/c64/reu/dma.rb +271 -0
- data/lib/badline/c64/reu/keyed_state.rb +140 -0
- data/lib/badline/c64/reu/ram.rb +94 -0
- data/lib/badline/c64/reu/trigger.rb +34 -0
- data/lib/badline/c64/reu.rb +289 -0
- data/lib/badline/c64/true_drives.rb +120 -0
- data/lib/badline/{computer.rb → c64.rb} +24 -15
- data/lib/badline/chips/cia/fast_forward.rb +62 -0
- data/lib/badline/chips/cia/interrupt_register.rb +128 -0
- data/lib/badline/chips/cia/keyed_parts.rb +213 -0
- data/lib/badline/chips/cia/serial.rb +246 -0
- data/lib/badline/chips/cia/time_of_day.rb +210 -0
- data/lib/badline/chips/cia/timer.rb +188 -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 +183 -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/keyed_state.rb +177 -0
- data/lib/badline/chips/cpu/microcode.rb +195 -0
- data/lib/badline/chips/cpu/operations.rb +90 -0
- data/lib/badline/chips/cpu/port.rb +133 -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 +17 -0
- data/lib/badline/chips/sid/catch_up.rb +122 -0
- data/lib/badline/chips/sid/decimator.rb +68 -0
- data/lib/badline/chips/sid/envelope.rb +236 -0
- data/lib/badline/chips/sid/filter.rb +227 -0
- data/lib/badline/chips/sid/keyed_parts.rb +286 -0
- data/lib/badline/chips/sid/voice.rb +69 -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.rb +305 -0
- data/lib/badline/chips/sid.rb +264 -0
- data/lib/badline/chips/ted/beam.rb +119 -0
- data/lib/badline/chips/ted/timers.rb +66 -0
- data/lib/badline/chips/ted.rb +143 -0
- data/lib/badline/chips/tia.rb +49 -0
- data/lib/badline/chips/vdc/keyed_parts.rb +105 -0
- data/lib/badline/{c128 → chips}/vdc/memory.rb +1 -1
- data/lib/badline/{c128 → chips}/vdc/painter.rb +1 -13
- data/lib/badline/{c128 → chips}/vdc/raster.rb +1 -17
- data/lib/badline/{c128 → chips}/vdc/window.rb +1 -10
- data/lib/badline/{c128 → chips}/vdc.rb +6 -31
- data/lib/badline/chips/via/control_lines.rb +109 -0
- data/lib/badline/chips/via/fast_forward.rb +55 -0
- data/lib/badline/chips/via/interrupt_register.rb +60 -0
- data/lib/badline/chips/via/keyed_parts.rb +163 -0
- data/lib/badline/chips/via/shift_register.rb +168 -0
- data/lib/badline/chips/via/timer1.rb +120 -0
- data/lib/badline/chips/via/timer2.rb +117 -0
- data/lib/badline/chips/via.rb +267 -0
- data/lib/badline/chips/vic/bank.rb +135 -0
- data/lib/badline/chips/vic/border_mask.rb +78 -0
- data/lib/badline/chips/vic/collisions.rb +106 -0
- data/lib/badline/chips/vic/color_patches.rb +38 -0
- data/lib/badline/chips/vic/display_state.rb +168 -0
- data/lib/badline/chips/vic/graphics_mode.rb +166 -0
- data/lib/badline/chips/vic/graphics_shifter.rb +183 -0
- data/lib/badline/chips/vic/keyed_parts.rb +228 -0
- data/lib/badline/chips/vic/keyed_sprites.rb +225 -0
- data/lib/badline/chips/vic/keyed_state.rb +102 -0
- data/lib/badline/chips/vic/register_log.rb +65 -0
- data/lib/badline/chips/vic/registers.rb +188 -0
- data/lib/badline/chips/vic/sequencer.rb +275 -0
- data/lib/badline/chips/vic/sequencer_output.rb +118 -0
- data/lib/badline/chips/vic/sprite/internal_bus.rb +59 -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 +337 -0
- data/lib/badline/chips/vic/sprites.rb +281 -0
- data/lib/badline/chips/vic.rb +787 -0
- data/lib/badline/chips/vic_i/keyed_parts.rb +142 -0
- data/lib/badline/{vic20/vic → chips/vic_i}/painter.rb +2 -30
- data/lib/badline/chips/vic_i/sound/output.rb +85 -0
- data/lib/badline/chips/vic_i/sound.rb +227 -0
- data/lib/badline/{vic20/vic → chips/vic_i}/text_window.rb +2 -2
- data/lib/badline/{vic20/vic.rb → chips/vic_i.rb} +6 -6
- 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/keyed_state.rb +73 -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/shifts.rb +66 -0
- data/lib/badline/chips/z80.rb +13 -0
- data/lib/badline/chips.rb +10 -0
- data/lib/badline/core.rb +13 -32
- data/lib/badline/drives/datasette/keyed_state.rb +38 -0
- data/lib/badline/drives/datasette.rb +173 -0
- data/lib/badline/drives/engine/core.rb +152 -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 +72 -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 +75 -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.rb +318 -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/keyed_state.rb +251 -0
- data/lib/badline/drives/model1541/mechanism.rb +313 -0
- data/lib/badline/drives/model1541/rotation.rb +123 -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 +80 -0
- data/lib/badline/drives/model1571/bus.rb +100 -0
- data/lib/badline/drives/model1571/cpu.rb +13 -0
- data/lib/badline/drives/model1571/keyed_state.rb +108 -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 +108 -0
- data/lib/badline/drives/model1571.rb +141 -0
- data/lib/badline/drives/model1581/bus.rb +126 -0
- data/lib/badline/drives/model1581/cpu.rb +13 -0
- data/lib/badline/drives/model1581/disk.rb +133 -0
- data/lib/badline/drives/model1581/keyed_state.rb +280 -0
- data/lib/badline/drives/model1581/mechanism.rb +98 -0
- data/lib/badline/drives/model1581/serial_port.rb +54 -0
- data/lib/badline/drives/model1581/slot.rb +61 -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/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 +216 -0
- data/lib/badline/drives/model1581.rb +206 -0
- data/lib/badline/drives/serial_bus.rb +161 -0
- data/lib/badline/drives.rb +8 -0
- data/lib/badline/frontend/app.rb +3 -3
- data/lib/badline/frontend/boot.rb +4 -4
- data/lib/badline/frontend/controls.rb +19 -3
- data/lib/badline/frontend/drive_led.rb +44 -21
- data/lib/badline/frontend/machine_window.rb +3 -1
- data/lib/badline/frontend/menu/menu_dialogs.rb +2 -2
- 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/snapshots.rb +12 -1
- data/lib/badline/frontend/timeline.rb +30 -12
- 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/matrix_typist.rb +98 -0
- data/lib/badline/input.rb +4 -0
- data/lib/badline/kernal_trap/chrout.rb +45 -0
- data/lib/badline/kernal_trap/{drive → dos}/block_commands.rb +1 -1
- data/lib/badline/kernal_trap/dos/channel.rb +156 -0
- data/lib/badline/kernal_trap/{drive → dos}/channels.rb +1 -26
- data/lib/badline/kernal_trap/{drive → dos}/memory.rb +1 -10
- data/lib/badline/kernal_trap/{drive → dos}/parameters.rb +1 -1
- data/lib/badline/kernal_trap/{drive → dos}/status.rb +1 -9
- data/lib/badline/kernal_trap/{drive → dos}/write_file.rb +1 -19
- data/lib/badline/kernal_trap/{drive → dos}/writes.rb +1 -1
- data/lib/badline/kernal_trap/{drive.rb → dos.rb} +9 -27
- data/lib/badline/kernal_trap/file_routine.rb +4 -4
- data/lib/badline/kernal_trap/keyed_state.rb +150 -0
- data/lib/badline/kernal_trap/layout.rb +75 -11
- data/lib/badline/kernal_trap/load.rb +19 -21
- data/lib/badline/kernal_trap/routine.rb +12 -0
- data/lib/badline/kernal_trap/save.rb +12 -22
- data/lib/badline/kernal_trap/serial.rb +8 -24
- data/lib/badline/kernal_trap.rb +3 -2
- data/lib/badline/machine/clocking.rb +1 -1
- data/lib/badline/machine/keyboard_buffer.rb +49 -0
- data/lib/badline/machine/settling.rb +29 -0
- data/lib/badline/machine.rb +10 -7
- data/lib/badline/media/boot_disk.rb +14 -2
- data/lib/badline/media/extensions.rb +10 -0
- data/lib/badline/media/true_drive.rb +1 -1
- data/lib/badline/media/tune.rb +2 -2
- data/lib/badline/media.rb +3 -3
- data/lib/badline/memory.rb +3 -6
- data/lib/badline/model.rb +1 -1
- data/lib/badline/options/family.rb +5 -2
- data/lib/badline/options/help.rb +42 -16
- data/lib/badline/options/table.rb +2 -0
- data/lib/badline/options/validation.rb +2 -0
- data/lib/badline/options.rb +6 -1
- data/lib/badline/plus4/acia.rb +45 -0
- data/lib/badline/plus4/bus.rb +312 -0
- data/lib/badline/plus4/cpu.rb +10 -0
- data/lib/badline/plus4/cpu_port.rb +93 -0
- data/lib/badline/plus4/keyboard_port.rb +51 -0
- data/lib/badline/plus4/model.rb +38 -0
- data/lib/badline/plus4/user_port.rb +31 -0
- data/lib/badline/plus4.rb +164 -0
- data/lib/badline/roms/README +44 -6
- data/lib/badline/roms/plus4/basic.rom +0 -0
- data/lib/badline/roms/plus4/function-high.rom +0 -0
- data/lib/badline/roms/plus4/function-low.rom +0 -0
- data/lib/badline/roms/plus4/kernal-ntsc.rom +0 -0
- data/lib/badline/roms/plus4/kernal-pal.rom +0 -0
- 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 +32 -0
- data/lib/badline/snapshot/c64_setup.rb +83 -0
- data/lib/badline/snapshot/container.rb +4 -0
- data/lib/badline/snapshot/field_doc/markdown.rb +112 -0
- data/lib/badline/snapshot/field_doc.rb +109 -0
- data/lib/badline/snapshot/image.rb +132 -46
- data/lib/badline/snapshot/keyed/cursor.rb +40 -0
- data/lib/badline/snapshot/keyed/modules.rb +66 -0
- data/lib/badline/snapshot/keyed/reader.rb +188 -0
- data/lib/badline/snapshot/keyed/recorder.rb +88 -0
- data/lib/badline/snapshot/keyed/report.rb +26 -0
- data/lib/badline/snapshot/keyed/writer.rb +55 -0
- data/lib/badline/snapshot/keyed.rb +64 -0
- data/lib/badline/snapshot/machine_state.rb +17 -64
- data/lib/badline/snapshot/storage_setup.rb +63 -0
- data/lib/badline/snapshot/vic20_setup.rb +28 -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 +3 -3
- data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
- data/lib/badline/snapshot/vice.rb +13 -35
- data/lib/badline/snapshot.rb +44 -22
- data/lib/badline/storage/crt_file.rb +3 -1
- data/lib/badline/storage/d64_image.rb +0 -2
- data/lib/badline/storage/d71_image.rb +0 -2
- data/lib/badline/storage/d81_image.rb +0 -2
- data/lib/badline/storage/disk_image.rb +3 -6
- data/lib/badline/storage/g64_image.rb +10 -5
- data/lib/badline/storage/host_directory.rb +0 -5
- data/lib/badline/storage/t64.rb +2 -8
- data/lib/badline/storage/tap.rb +43 -15
- 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 +8 -5
- data/lib/badline/vic20/bus/keyed_state.rb +52 -0
- data/lib/badline/vic20/bus.rb +2 -25
- data/lib/badline/vic20/cpu.rb +1 -1
- data/lib/badline/vic20/keyed_modules.rb +73 -0
- data/lib/badline/vic20/keyed_state.rb +45 -0
- data/lib/badline/vic20/port_wiring.rb +4 -4
- data/lib/badline/vic20/user_via_ports.rb +2 -2
- data/lib/badline/vic20.rb +22 -17
- data/lib/badline.rb +1 -0
- metadata +271 -228
- data/lib/badline/address_bus/extra_sids.rb +0 -30
- data/lib/badline/address_bus/fittings.rb +0 -38
- data/lib/badline/address_bus/pla.rb +0 -147
- data/lib/badline/address_bus/roms.rb +0 -39
- data/lib/badline/address_bus/saved_state.rb +0 -66
- data/lib/badline/address_bus/sid_slots.rb +0 -42
- data/lib/badline/address_bus.rb +0 -216
- data/lib/badline/c128/bus_state.rb +0 -53
- data/lib/badline/c128/saved_state.rb +0 -143
- 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/chrout_trap.rb +0 -43
- 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/attachments.rb +0 -88
- data/lib/badline/computer/saved_state.rb +0 -117
- data/lib/badline/computer/true_drives.rb +0 -69
- data/lib/badline/control_ports.rb +0 -76
- data/lib/badline/cpu/addressing.rb +0 -131
- data/lib/badline/cpu/boundary.rb +0 -54
- data/lib/badline/cpu/core.rb +0 -165
- data/lib/badline/cpu/microcode.rb +0 -177
- data/lib/badline/cpu/operations.rb +0 -88
- data/lib/badline/cpu/saved_state.rb +0 -115
- data/lib/badline/cpu/stack_operations.rb +0 -82
- data/lib/badline/cpu.rb +0 -10
- data/lib/badline/cpu_port.rb +0 -112
- data/lib/badline/datasette.rb +0 -161
- data/lib/badline/debug_register.rb +0 -28
- data/lib/badline/drive/core.rb +0 -147
- data/lib/badline/drive/fast_serial.rb +0 -73
- data/lib/badline/drive/idle.rb +0 -125
- data/lib/badline/drive/idle_cpu.rb +0 -28
- data/lib/badline/drive/orbit.rb +0 -79
- data/lib/badline/drive/serial_lines.rb +0 -73
- data/lib/badline/drive/sleep.rb +0 -112
- data/lib/badline/drive/vias.rb +0 -84
- data/lib/badline/drive/watch.rb +0 -150
- data/lib/badline/drive.rb +0 -11
- data/lib/badline/drive1541/bus.rb +0 -86
- data/lib/badline/drive1541/byte_ready.rb +0 -48
- data/lib/badline/drive1541/cpu.rb +0 -11
- data/lib/badline/drive1541/disk.rb +0 -315
- data/lib/badline/drive1541/disk_state.rb +0 -86
- data/lib/badline/drive1541/disk_via.rb +0 -36
- data/lib/badline/drive1541/gcr.rb +0 -57
- data/lib/badline/drive1541/mechanism.rb +0 -313
- data/lib/badline/drive1541/mechanism_state.rb +0 -68
- data/lib/badline/drive1541/rotation.rb +0 -121
- data/lib/badline/drive1541/sector_reader.rb +0 -76
- data/lib/badline/drive1541/serial_port.rb +0 -18
- data/lib/badline/drive1541/track.rb +0 -98
- data/lib/badline/drive1541.rb +0 -111
- data/lib/badline/drive1571/bus.rb +0 -111
- data/lib/badline/drive1571/cpu.rb +0 -11
- data/lib/badline/drive1571/serial_port.rb +0 -37
- data/lib/badline/drive1571/serial_via.rb +0 -25
- data/lib/badline/drive1571/wd1770.rb +0 -120
- data/lib/badline/drive1571.rb +0 -175
- data/lib/badline/drive1581/bus.rb +0 -137
- data/lib/badline/drive1581/cpu.rb +0 -11
- data/lib/badline/drive1581/disk.rb +0 -135
- data/lib/badline/drive1581/disk_state.rb +0 -87
- data/lib/badline/drive1581/mechanism.rb +0 -117
- data/lib/badline/drive1581/serial_port.rb +0 -52
- data/lib/badline/drive1581/slot.rb +0 -78
- data/lib/badline/drive1581/track.rb +0 -100
- data/lib/badline/drive1581/track_bytes.rb +0 -75
- data/lib/badline/drive1581/wd1772/registers.rb +0 -89
- data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
- data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
- data/lib/badline/drive1581/wd1772/state.rb +0 -66
- data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
- data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
- data/lib/badline/drive1581/wd1772/turn.rb +0 -73
- data/lib/badline/drive1581/wd1772.rb +0 -216
- data/lib/badline/drive1581.rb +0 -233
- data/lib/badline/iec_bus.rb +0 -159
- data/lib/badline/instruction.rb +0 -334
- data/lib/badline/instruction_set/arithmetic.rb +0 -114
- data/lib/badline/instruction_set/bitwise.rb +0 -126
- data/lib/badline/instruction_set/branch.rb +0 -79
- data/lib/badline/instruction_set/flag.rb +0 -63
- data/lib/badline/instruction_set/illegal.rb +0 -270
- data/lib/badline/instruction_set/inc_dec.rb +0 -69
- data/lib/badline/instruction_set/stack.rb +0 -52
- data/lib/badline/instruction_set/transfer.rb +0 -137
- data/lib/badline/instruction_set.rb +0 -55
- data/lib/badline/interrupts.rb +0 -104
- data/lib/badline/joystick.rb +0 -25
- data/lib/badline/kernal_trap/channel.rb +0 -181
- data/lib/badline/keyboard.rb +0 -112
- data/lib/badline/keyboard_buffer.rb +0 -33
- 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/snapshot/setup.rb +0 -75
- data/lib/badline/snapshot/state.rb +0 -189
- data/lib/badline/time_of_day.rb +0 -228
- data/lib/badline/traps.rb +0 -19
- data/lib/badline/via/control_lines.rb +0 -122
- data/lib/badline/via/fast_forward.rb +0 -53
- data/lib/badline/via/interrupt_register.rb +0 -67
- data/lib/badline/via/shift_register.rb +0 -180
- data/lib/badline/via/state.rb +0 -42
- data/lib/badline/via/timer1.rb +0 -131
- data/lib/badline/via/timer2.rb +0 -133
- data/lib/badline/via.rb +0 -266
- data/lib/badline/vic/bank.rb +0 -133
- data/lib/badline/vic/border_mask.rb +0 -86
- data/lib/badline/vic/collisions.rb +0 -115
- data/lib/badline/vic/color_patches.rb +0 -47
- data/lib/badline/vic/display_state.rb +0 -185
- data/lib/badline/vic/graphics_mode.rb +0 -164
- data/lib/badline/vic/graphics_shifter.rb +0 -207
- data/lib/badline/vic/register_log.rb +0 -81
- data/lib/badline/vic/registers.rb +0 -195
- data/lib/badline/vic/sequencer.rb +0 -275
- data/lib/badline/vic/sequencer_output.rb +0 -116
- data/lib/badline/vic/sequencer_state.rb +0 -89
- data/lib/badline/vic/sprite/internal_bus.rb +0 -68
- data/lib/badline/vic/sprite/saved_state.rb +0 -64
- data/lib/badline/vic/sprite/shifter.rb +0 -146
- data/lib/badline/vic/sprite/timing.rb +0 -56
- data/lib/badline/vic/sprite.rb +0 -337
- data/lib/badline/vic/sprites.rb +0 -282
- data/lib/badline/vic/sprites_state.rb +0 -57
- data/lib/badline/vic.rb +0 -853
- data/lib/badline/vic20/saved_state.rb +0 -124
- data/lib/badline/vic20/sound/output.rb +0 -83
- data/lib/badline/vic20/sound.rb +0 -233
- data/lib/badline/vic20/vic/saved_state.rb +0 -55
- 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
|
@@ -20,6 +20,7 @@ module Badline
|
|
|
20
20
|
def initialize(computer, options)
|
|
21
21
|
@computer = computer
|
|
22
22
|
@at_finish = options.save_snapshot
|
|
23
|
+
@vice = options.vice?
|
|
23
24
|
@autosave = options.frames.zero?
|
|
24
25
|
@autosave_at = AUTOSAVE_FRAMES
|
|
25
26
|
end
|
|
@@ -128,11 +129,21 @@ module Badline
|
|
|
128
129
|
end
|
|
129
130
|
|
|
130
131
|
def finish
|
|
131
|
-
|
|
132
|
+
return if @at_finish.empty?
|
|
133
|
+
|
|
134
|
+
@vice ? export(@at_finish) : save(@at_finish)
|
|
132
135
|
end
|
|
133
136
|
|
|
134
137
|
private
|
|
135
138
|
|
|
139
|
+
# Saves the machine with VICE's modules too, and warns when it can't.
|
|
140
|
+
def export(path)
|
|
141
|
+
@computer.export_snapshot(path)
|
|
142
|
+
puts "Saved #{path} for badline and VICE"
|
|
143
|
+
rescue Snapshot::FormatError, SystemCallError => e
|
|
144
|
+
warn "badline: #{path}: #{e.message}"
|
|
145
|
+
end
|
|
146
|
+
|
|
136
147
|
def restore_from(path)
|
|
137
148
|
@computer = Snapshot.load(path) { |line| puts line }
|
|
138
149
|
puts "Restored #{name_of(path)} from #{path}"
|
|
@@ -21,10 +21,13 @@ module Badline
|
|
|
21
21
|
@failed = false
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
# The C128's two locking keys, outside its matrix.
|
|
25
|
+
LOCKS = %w[caps_lock forty_eighty].freeze
|
|
26
|
+
|
|
27
|
+
# What's wrong with the events for the family's machine, the first
|
|
28
|
+
# key or menu page that names nothing, or an empty string.
|
|
29
|
+
def self.error(events, family)
|
|
30
|
+
event = events.find { |candidate| candidate.action == "key" && !key?(candidate.argument, family) }
|
|
28
31
|
return "no such key: #{event.argument}" unless event.nil?
|
|
29
32
|
|
|
30
33
|
event = events.find { |candidate| candidate.action == "menu" && !page?(candidate.argument) }
|
|
@@ -35,19 +38,24 @@ module Badline
|
|
|
35
38
|
# sound. An empty one is its first.
|
|
36
39
|
def self.page?(name) = name.empty? || PauseMenu::PAGES.any? { |page| page.to_s == name }
|
|
37
40
|
|
|
38
|
-
def self.key?(name
|
|
41
|
+
def self.key?(name, family)
|
|
42
|
+
name == "restore" || (family == :c128 && LOCKS.include?(name)) ||
|
|
43
|
+
!keyboard_key(name, family).nil? || !joystick_direction(name).nil?
|
|
44
|
+
end
|
|
39
45
|
|
|
40
|
-
# The
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
# The family's Keyboard key for a name such as space, return or a,
|
|
47
|
+
# or on the C128 esc or keypad1.
|
|
48
|
+
def self.keyboard_key(name, family)
|
|
49
|
+
matrix = family == :c128 ? C128::KEYBOARD_MATRIX : Input::Keyboard::C64_MATRIX
|
|
50
|
+
key = matrix.flatten.find { |candidate| candidate.to_s == name }
|
|
51
|
+
key.nil? ? Input::Keyboard::COMBINATIONS.keys.find { |candidate| candidate.to_s == name } : key
|
|
44
52
|
end
|
|
45
53
|
|
|
46
54
|
# The direction of joy1-NAME or joy2-NAME, such as up or fire.
|
|
47
55
|
def self.joystick_direction(name)
|
|
48
56
|
return nil unless name.start_with?("joy1-") || name.start_with?("joy2-")
|
|
49
57
|
|
|
50
|
-
Joystick::DIRECTIONS.keys.find { |direction| direction.to_s == name[5, name.size - 5] }
|
|
58
|
+
Input::Joystick::DIRECTIONS.keys.find { |direction| direction.to_s == name[5, name.size - 5] }
|
|
51
59
|
end
|
|
52
60
|
|
|
53
61
|
# The screenshot's file name, with the frame's number in place of %d,
|
|
@@ -130,6 +138,8 @@ module Badline
|
|
|
130
138
|
|
|
131
139
|
def press(computer, event)
|
|
132
140
|
name = event.argument
|
|
141
|
+
return lock(computer, name) if LOCKS.include?(name)
|
|
142
|
+
|
|
133
143
|
if name == "restore"
|
|
134
144
|
computer.press_restore
|
|
135
145
|
return hold(event)
|
|
@@ -137,13 +147,21 @@ module Badline
|
|
|
137
147
|
|
|
138
148
|
direction = Timeline.joystick_direction(name)
|
|
139
149
|
if direction.nil?
|
|
140
|
-
computer.keyboard.press(Timeline.keyboard_key(name))
|
|
150
|
+
computer.keyboard.press(Timeline.keyboard_key(name, computer.family))
|
|
141
151
|
else
|
|
142
152
|
joystick(computer, name).press(direction)
|
|
143
153
|
end
|
|
144
154
|
hold(event)
|
|
145
155
|
end
|
|
146
156
|
|
|
157
|
+
# CAPS LOCK and 40/80 DISPLAY lock: a push puts one down if it was up,
|
|
158
|
+
# and up if it was down.
|
|
159
|
+
def lock(computer, name)
|
|
160
|
+
return unless computer.family == :c128
|
|
161
|
+
|
|
162
|
+
name == "caps_lock" ? computer.push_caps_lock : computer.push_display_key
|
|
163
|
+
end
|
|
164
|
+
|
|
147
165
|
# The VIC-20 and the C128 have no cartridge with a button yet.
|
|
148
166
|
def freeze(computer, event)
|
|
149
167
|
return unless computer.family == :c64
|
|
@@ -164,7 +182,7 @@ module Badline
|
|
|
164
182
|
|
|
165
183
|
direction = Timeline.joystick_direction(name)
|
|
166
184
|
if direction.nil?
|
|
167
|
-
computer.keyboard.release(Timeline.keyboard_key(name))
|
|
185
|
+
computer.keyboard.release(Timeline.keyboard_key(name, computer.family))
|
|
168
186
|
else
|
|
169
187
|
joystick(computer, name).release(direction)
|
|
170
188
|
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Input
|
|
5
|
+
# CIA 1's control ports and the keyboard matrix that spans them.
|
|
6
|
+
#
|
|
7
|
+
# Port A carries the matrix rows together with joystick 2, port B the matrix
|
|
8
|
+
# columns together with joystick 1. Every switch is active low, so the
|
|
9
|
+
# joysticks are wired-AND onto the lines *before* the matrix settles: a stick
|
|
10
|
+
# held in a direction selects rows and columns of its own, which is where
|
|
11
|
+
# hardware's phantom keypresses come from. Paddles and a 1351 mouse have
|
|
12
|
+
# buttons on those same lines.
|
|
13
|
+
#
|
|
14
|
+
# The ports also carry SID's POTX/POTY lines, which reach them through analog
|
|
15
|
+
# switches closed by CIA 1 PA6 (port 1) and PA7 (port 2). Close both and the
|
|
16
|
+
# pots sit in parallel, so SID charges through the lower of the two
|
|
17
|
+
# resistances; close neither and the lines float high. Reading a pot asks the
|
|
18
|
+
# CIA for the current select lines, since a program sets them and then goes
|
|
19
|
+
# straight to SID without touching the ports again.
|
|
20
|
+
#
|
|
21
|
+
# A matrix may have rows beyond port A's eight. Their select lines come from
|
|
22
|
+
# outside the CIA and are pushed into +extra_rows+, bit 0 for the ninth row
|
|
23
|
+
# upwards, active low like port A; idle, every line is high and none of
|
|
24
|
+
# those rows is selected. They settle with port A's lines as rows 8 and up,
|
|
25
|
+
# so their keys reach port B, but port A reads only its own eight.
|
|
26
|
+
class ControlPorts
|
|
27
|
+
PORT1_POTS = 0x40
|
|
28
|
+
PORT2_POTS = 0x80
|
|
29
|
+
FIRE = 0x10
|
|
30
|
+
|
|
31
|
+
attr_reader :keyboard, :joystick1, :joystick2
|
|
32
|
+
attr_accessor :device1, :device2, :port_a_source, :extra_rows
|
|
33
|
+
|
|
34
|
+
def initialize(keyboard:, joystick1:, joystick2:)
|
|
35
|
+
@keyboard = keyboard
|
|
36
|
+
@joystick1 = joystick1
|
|
37
|
+
@joystick2 = joystick2
|
|
38
|
+
@device1 = nil
|
|
39
|
+
@device2 = nil
|
|
40
|
+
@port_a_source = nil
|
|
41
|
+
@extra_rows = 0xff
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def read_a(port_a, port_b) = scan(port_a, port_b).first & 0xff
|
|
45
|
+
|
|
46
|
+
def read_b(port_a, port_b) = scan(port_a, port_b).last
|
|
47
|
+
|
|
48
|
+
# Control port 1 pin 6 is both the port B fire line and the VIC's light
|
|
49
|
+
# pen input, so joystick 1's button and a 1351's left button latch
|
|
50
|
+
# $D013/$D014. The CIA samples this every cycle, so it skips the matrix and
|
|
51
|
+
# asks only what the port's own devices drive.
|
|
52
|
+
def port_b4_high? = port_bits(@joystick1, @device1).anybits?(FIRE)
|
|
53
|
+
|
|
54
|
+
def pot_x = selected_devices.map(&:pot_x).min || 0xff
|
|
55
|
+
|
|
56
|
+
def pot_y = selected_devices.map(&:pot_y).min || 0xff
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def selected_devices
|
|
61
|
+
lines = @port_a_source&.port_a_lines || 0x00
|
|
62
|
+
devices = []
|
|
63
|
+
devices << @device1 if @device1 && lines.anybits?(PORT1_POTS)
|
|
64
|
+
devices << @device2 if @device2 && lines.anybits?(PORT2_POTS)
|
|
65
|
+
devices
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def scan(port_a, port_b)
|
|
69
|
+
keyboard.scan((port_a & port_bits(@joystick2, @device2)) | (@extra_rows << 8),
|
|
70
|
+
port_b & port_bits(@joystick1, @device1))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def port_bits(joystick, device)
|
|
74
|
+
device ? joystick.port_bits & device.port_bits : joystick.port_bits
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Input
|
|
5
|
+
class Joystick
|
|
6
|
+
DIRECTIONS = { up: 0, down: 1, left: 2, right: 3, fire: 4 }.freeze
|
|
7
|
+
|
|
8
|
+
# Kept up to date as switches move, since the CIA samples the fire line
|
|
9
|
+
# every cycle to drive the light pen input.
|
|
10
|
+
attr_reader :port_bits
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@port_bits = 0xff
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def press(direction)
|
|
17
|
+
bit = DIRECTIONS[direction]
|
|
18
|
+
@port_bits &= ~(1 << bit) if bit
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def release(direction)
|
|
22
|
+
bit = DIRECTIONS[direction]
|
|
23
|
+
@port_bits |= 1 << bit if bit
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Input
|
|
5
|
+
# The key matrix spanning CIA 1's two ports: rows on port A, columns on
|
|
6
|
+
# port B. Row lines beyond the eighth sit above port A's bits in +rows+.
|
|
7
|
+
#
|
|
8
|
+
# A pressed key shorts its row line to its column line, so whichever side is
|
|
9
|
+
# pulled low drags the other down with it. Scanning works in either
|
|
10
|
+
# direction, and chains of keys sharing a line propagate the pulldown -- the
|
|
11
|
+
# ghost keys real hardware reports.
|
|
12
|
+
class Keyboard
|
|
13
|
+
include IntegerHelper
|
|
14
|
+
|
|
15
|
+
# Keys the C64 types as a combination: cursor up and cursor left are the
|
|
16
|
+
# shifted cursor keys. Pressing one holds both of its matrix keys, while
|
|
17
|
+
# a shift key held on its own stays down when it is released.
|
|
18
|
+
COMBINATIONS = {
|
|
19
|
+
cursor_up: %i[lshift cursor_v],
|
|
20
|
+
cursor_left: %i[lshift cursor_h]
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
# The C64's wiring: one row of keys per port A line, in port B column
|
|
24
|
+
# order.
|
|
25
|
+
C64_MATRIX = [
|
|
26
|
+
%i[delete return cursor_h f7 f1 f3 f5 cursor_v],
|
|
27
|
+
%i[3 w a 4 z s e lshift],
|
|
28
|
+
%i[5 r d 6 c f t x],
|
|
29
|
+
%i[7 y g 8 b h u v],
|
|
30
|
+
%i[9 i j 0 m k o n],
|
|
31
|
+
%i[+ p l - . : @ ,],
|
|
32
|
+
%i[£ * ; clr_home rshift = up /],
|
|
33
|
+
%i[1 left control 2 space cbm q run_stop]
|
|
34
|
+
].freeze
|
|
35
|
+
|
|
36
|
+
attr_reader :keys, :matrix
|
|
37
|
+
|
|
38
|
+
def initialize(matrix: C64_MATRIX)
|
|
39
|
+
@keys = []
|
|
40
|
+
@row_masks = nil
|
|
41
|
+
@matrix = matrix
|
|
42
|
+
@connected = true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Whether the machine has a keyboard. The C64GS has none: no key goes
|
|
46
|
+
# down on it.
|
|
47
|
+
def connected? = @connected
|
|
48
|
+
|
|
49
|
+
def connected=(connected)
|
|
50
|
+
@connected = connected
|
|
51
|
+
@keys.clear unless connected
|
|
52
|
+
@row_masks = nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def press(key)
|
|
56
|
+
return unless @connected && valid_key?(key)
|
|
57
|
+
|
|
58
|
+
@keys << key
|
|
59
|
+
@row_masks = nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def release(key)
|
|
63
|
+
@keys.reject! { |k| k == key }
|
|
64
|
+
@row_masks = nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Settles the row (port A) and column (port B) lines against each other
|
|
68
|
+
# until no key contact pulls a line low any more, and returns both.
|
|
69
|
+
def scan(rows, cols)
|
|
70
|
+
return [rows, cols] if keys.empty?
|
|
71
|
+
|
|
72
|
+
loop do
|
|
73
|
+
settled_rows = rows
|
|
74
|
+
settled_cols = cols
|
|
75
|
+
|
|
76
|
+
row_masks.each_with_index do |mask, row|
|
|
77
|
+
bit = 1 << row
|
|
78
|
+
next if mask.zero? || (rows.allbits?(bit) && cols.allbits?(mask))
|
|
79
|
+
|
|
80
|
+
rows &= ~bit
|
|
81
|
+
cols &= ~mask
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
return [rows, cols] if rows == settled_rows && cols == settled_cols
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def read_a(port_a, port_b) = scan(port_a, port_b).first
|
|
89
|
+
|
|
90
|
+
def read_b(port_a, port_b) = scan(port_a, port_b).last
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
# Columns held down per row, as a bit mask of port B lines.
|
|
95
|
+
def row_masks
|
|
96
|
+
@row_masks ||= begin
|
|
97
|
+
pressed = held
|
|
98
|
+
matrix.map do |row|
|
|
99
|
+
row.each_with_index.sum { |key, column| pressed.include?(key) ? 1 << column : 0 }
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# The matrix keys held down, with each combination expanded.
|
|
105
|
+
def held
|
|
106
|
+
keys.flat_map { |key| COMBINATIONS[key] || [key] }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def valid_key?(key)
|
|
110
|
+
COMBINATIONS.key?(key) || matrix.flatten.include?(key)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Input
|
|
5
|
+
# Types PETSCII codes on a Keyboard's matrix, one key at a time, for
|
|
6
|
+
# software that scans the keys itself instead of reading a KERNAL's
|
|
7
|
+
# keyboard buffer: each code's keys go down for HOLD cycles, then up for
|
|
8
|
+
# GAP cycles. Codes it has no key for are skipped.
|
|
9
|
+
class MatrixTypist
|
|
10
|
+
HOLD = 100_000
|
|
11
|
+
GAP = 40_000
|
|
12
|
+
|
|
13
|
+
# The keys typed without SHIFT, by PETSCII code.
|
|
14
|
+
PLAIN = {
|
|
15
|
+
0x0d => :return, 0x20 => :space, 0x2a => :*, 0x2b => :+, 0x2c => :",", 0x2d => :-, 0x2e => :".",
|
|
16
|
+
0x2f => :/, 0x3a => :":", 0x3b => :";", 0x3d => :"=", 0x40 => :"@"
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
# The keys typed with SHIFT, by PETSCII code.
|
|
20
|
+
SHIFTED = {
|
|
21
|
+
0x21 => :"1", 0x22 => :"2", 0x23 => :"3", 0x24 => :"4", 0x25 => :"5", 0x26 => :"6", 0x27 => :"7",
|
|
22
|
+
0x28 => :"8", 0x29 => :"9", 0x3c => :",", 0x3e => :".", 0x3f => :/, 0x5b => :":", 0x5d => :";"
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
DIGITS = %i[0 1 2 3 4 5 6 7 8 9].freeze
|
|
26
|
+
LETTERS = %i[a b c d e f g h i j k l m n o p q r s t u v w x y z].freeze
|
|
27
|
+
|
|
28
|
+
# The keys that type the PETSCII code, SHIFT first, or none.
|
|
29
|
+
def self.keys(code)
|
|
30
|
+
return [DIGITS[code - 0x30]] if code.between?(0x30, 0x39)
|
|
31
|
+
return [LETTERS[code - 0x41]] if code.between?(0x41, 0x5a)
|
|
32
|
+
return [:lshift, LETTERS[code - 0xc1]] if code.between?(0xc1, 0xda)
|
|
33
|
+
return [PLAIN[code]] if PLAIN.key?(code)
|
|
34
|
+
return [:lshift, SHIFTED[code]] if SHIFTED.key?(code)
|
|
35
|
+
|
|
36
|
+
[]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def initialize(keyboard)
|
|
40
|
+
@keyboard = keyboard
|
|
41
|
+
@held = []
|
|
42
|
+
@code = 0
|
|
43
|
+
@wait = 0
|
|
44
|
+
@typing = false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Whether it is part way through codes: from the first key down until
|
|
48
|
+
# the last one is up and its gap has passed.
|
|
49
|
+
def typing? = @typing
|
|
50
|
+
|
|
51
|
+
# A cycle of typing +codes+, taking each code off its front as its keys
|
|
52
|
+
# go down.
|
|
53
|
+
def cycle!(codes)
|
|
54
|
+
@wait -= 1 if @wait.positive?
|
|
55
|
+
return unless @wait.zero?
|
|
56
|
+
|
|
57
|
+
if @held.empty?
|
|
58
|
+
press(codes)
|
|
59
|
+
else
|
|
60
|
+
@held.each { |key| @keyboard.release(key) }
|
|
61
|
+
@held = []
|
|
62
|
+
@wait = GAP
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Where it is in its typing, in a keyed snapshot module under `prefix`:
|
|
67
|
+
# the code whose keys are down, if any, the cycles left before they
|
|
68
|
+
# go up or the next code's go down, and whether it is part way
|
|
69
|
+
# through. The keys go down again on load.
|
|
70
|
+
def save_fields(out, prefix)
|
|
71
|
+
out.optional_int("#{prefix}code", @held.empty? ? nil : @code)
|
|
72
|
+
out.int("#{prefix}wait", @wait).bool("#{prefix}typing", @typing)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def load_fields(input, prefix)
|
|
76
|
+
@held.each { |key| @keyboard.release(key) }
|
|
77
|
+
code = input.optional_int("#{prefix}code")
|
|
78
|
+
@code = code || 0
|
|
79
|
+
@held = code.nil? ? [] : MatrixTypist.keys(code)
|
|
80
|
+
@held.each { |key| @keyboard.press(key) }
|
|
81
|
+
@wait = input.int("#{prefix}wait", @wait)
|
|
82
|
+
@typing = input.bool("#{prefix}typing", @typing)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def press(codes)
|
|
88
|
+
@typing = !codes.empty?
|
|
89
|
+
return unless @typing
|
|
90
|
+
|
|
91
|
+
@code = codes.shift
|
|
92
|
+
@held = MatrixTypist.keys(@code)
|
|
93
|
+
@held.each { |key| @keyboard.press(key) }
|
|
94
|
+
@wait = @held.empty? ? 1 : HOLD
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
data/lib/badline/input.rb
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module KernalTrap
|
|
5
|
+
# Observe-only PC trap on the KERNAL CHROUT routine ($FFD2). Records each
|
|
6
|
+
# character written while the layout finds the KERNAL mapped in, then lets execution fall
|
|
7
|
+
# through to the ROM, so the screen output is unaffected. Used for headless
|
|
8
|
+
# capture of program output.
|
|
9
|
+
class Chrout
|
|
10
|
+
ADDRESS = 0xffd2
|
|
11
|
+
|
|
12
|
+
attr_reader :output
|
|
13
|
+
|
|
14
|
+
# The KERNAL layout whose mapping the trap checks.
|
|
15
|
+
attr_writer :layout
|
|
16
|
+
|
|
17
|
+
def initialize(cpu:, bus:, layout:)
|
|
18
|
+
@cpu = cpu
|
|
19
|
+
@bus = bus
|
|
20
|
+
@layout = layout
|
|
21
|
+
@output = +""
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call
|
|
25
|
+
@output << ascii(@cpu.a) if @layout.kernal_mapped?(@bus)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def inspect
|
|
29
|
+
"#<#{self.class.name} output=#{@output.inspect}>"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def ascii(byte)
|
|
35
|
+
case byte
|
|
36
|
+
when 0x0d then "\n"
|
|
37
|
+
when 0x41..0x5a then (byte + 0x20).chr
|
|
38
|
+
when 0xc1..0xda then (byte - 0x80).chr
|
|
39
|
+
when 0x20..0x7e then byte.chr
|
|
40
|
+
else ""
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module KernalTrap
|
|
5
|
+
class DOS
|
|
6
|
+
# One open channel on the virtual drive, holding a file, a block buffer
|
|
7
|
+
# or the command channel's status message. It gives out one byte at a
|
|
8
|
+
# time and flags the last one, so the bus can raise EOI with it. A
|
|
9
|
+
# channel cut short by a read error never flags one, and holds the
|
|
10
|
+
# error for the drive to report once its bytes run out.
|
|
11
|
+
class Channel
|
|
12
|
+
attr_accessor :buffer
|
|
13
|
+
attr_reader :error, :pointer
|
|
14
|
+
attr_writer :lead
|
|
15
|
+
|
|
16
|
+
def initialize(bytes = [], error: nil, writable: false, base: 0)
|
|
17
|
+
@base = base
|
|
18
|
+
replace(bytes)
|
|
19
|
+
@error = error
|
|
20
|
+
@writable = writable
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# A block buffer opened with "#", which reads and writes one of the
|
|
24
|
+
# drive's buffers in its RAM, so M-R and M-W reach the same bytes. It
|
|
25
|
+
# hands out the whole block unless a B-R sets a shorter end, and
|
|
26
|
+
# flags that byte as the last. The DOS opens it with the pointer at 1
|
|
27
|
+
# and the buffer's number as the byte to send, so the first read
|
|
28
|
+
# answers with the number and the next one moves on to the third
|
|
29
|
+
# byte.
|
|
30
|
+
def self.buffer(memory, number)
|
|
31
|
+
new(memory.ram, writable: true, base: Memory.buffer_address(number)).tap do |channel|
|
|
32
|
+
channel.buffer = number
|
|
33
|
+
channel.rewind(BLOCK_SIZE)
|
|
34
|
+
channel.pointer = 1
|
|
35
|
+
channel.lead = number
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# A file's bytes, up to the first block its chain can't read. The
|
|
40
|
+
# drive reads a block ahead before sending the last byte of the one
|
|
41
|
+
# before, so a bad block holds that byte back too, and a bad first
|
|
42
|
+
# block or a missing file leaves nothing to send.
|
|
43
|
+
def self.for_file(storage, name, type)
|
|
44
|
+
data = storage.read_file(name, type:)
|
|
45
|
+
return new([], error: [FILE_NOT_FOUND]) unless data
|
|
46
|
+
|
|
47
|
+
failure = storage.respond_to?(:read_error) && storage.read_error(name, type:)
|
|
48
|
+
return new(data) unless failure
|
|
49
|
+
|
|
50
|
+
new(data[0, failure[:offset] - 1] || [], error: failure.values_at(:error, :track, :sector))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The file an open names. The drive keeps the first block of the file
|
|
54
|
+
# a LOAD (secondary address 0) opens at $7E and $026F of its memory,
|
|
55
|
+
# and a LOAD of a name starting with "*" reopens that file rather than
|
|
56
|
+
# the first one on the disk. Loaders write the block there with M-W
|
|
57
|
+
# to load a file the directory doesn't list. A LOAD of "$" reads the
|
|
58
|
+
# directory as a BASIC program, which a pattern after a colon narrows
|
|
59
|
+
# to the files it names.
|
|
60
|
+
def self.for_name(storage, memory, secondary, name, type)
|
|
61
|
+
return new(storage.directory.bytes(name)) if listing?(storage, secondary, name)
|
|
62
|
+
|
|
63
|
+
file = Storage.parse_name(name).first
|
|
64
|
+
return for_file(storage, file, type) unless secondary.zero? && storage.respond_to?(:first_block)
|
|
65
|
+
|
|
66
|
+
last = memory.last_program if name.start_with?("*")
|
|
67
|
+
return at_block(storage, *last) if last
|
|
68
|
+
|
|
69
|
+
block = storage.first_block(file, type:)
|
|
70
|
+
memory.last_program = block if block
|
|
71
|
+
for_file(storage, file, type)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.listing?(storage, secondary, name)
|
|
75
|
+
secondary.zero? && name.start_with?("$") && storage.respond_to?(:directory)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# The file whose chain starts at the block.
|
|
79
|
+
def self.at_block(storage, track, sector)
|
|
80
|
+
data = storage.read_file_at(track, sector)
|
|
81
|
+
data ? new(data) : new([], error: [ILLEGAL_TRACK_OR_SECTOR, track, sector])
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def replace(bytes, length = bytes.length)
|
|
85
|
+
@bytes = bytes
|
|
86
|
+
rewind(length)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def rewind(length)
|
|
90
|
+
@length = length
|
|
91
|
+
self.pointer = 0
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def pointer=(position)
|
|
95
|
+
@pointer = position
|
|
96
|
+
@lead = nil
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# A block read into the channel. A buffer channel takes it into its
|
|
100
|
+
# buffer, a file channel hands it out in place of the file.
|
|
101
|
+
def load(block, length)
|
|
102
|
+
return replace(block.dup, length) unless @writable
|
|
103
|
+
|
|
104
|
+
@bytes[@base, block.length] = block
|
|
105
|
+
rewind(length)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def writable? = @writable
|
|
109
|
+
|
|
110
|
+
# A buffer channel's block, as a block write puts it on the disk.
|
|
111
|
+
def block = @bytes[@base, BLOCK_SIZE]
|
|
112
|
+
|
|
113
|
+
def exhausted?
|
|
114
|
+
@pointer >= @length
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# A file that holds nothing but its error never opened.
|
|
118
|
+
def failed?
|
|
119
|
+
exhausted? && !@error.nil?
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Each byte lands at the pointer, which wraps within the block.
|
|
123
|
+
def write(bytes)
|
|
124
|
+
@lead = nil
|
|
125
|
+
bytes.each do |byte|
|
|
126
|
+
@bytes[@base + @pointer] = byte
|
|
127
|
+
@pointer = (@pointer + 1) & 0xff
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def read
|
|
132
|
+
return read_buffer if @writable
|
|
133
|
+
return if exhausted?
|
|
134
|
+
|
|
135
|
+
byte = @lead || @bytes[@base + @pointer]
|
|
136
|
+
@lead = nil
|
|
137
|
+
@pointer += 1
|
|
138
|
+
[byte, exhausted? && !@error]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
private
|
|
142
|
+
|
|
143
|
+
# A buffer never runs out. Past its last byte, which goes out with
|
|
144
|
+
# EOI, the pointer wraps back to the second, and a pointer set past
|
|
145
|
+
# the end runs on around the block to it.
|
|
146
|
+
def read_buffer
|
|
147
|
+
byte = @lead || @bytes[@base + @pointer]
|
|
148
|
+
@lead = nil
|
|
149
|
+
last = @pointer == @length - 1
|
|
150
|
+
@pointer = last ? 1 : (@pointer + 1) & 0xff
|
|
151
|
+
[byte, last]
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|