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
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model1581
|
|
6
|
+
# The drive CPU's bus, as the 74LS139 decodes A13-A15 (1581 Service
|
|
7
|
+
# Manual, memory map and schematic sheet 1):
|
|
8
|
+
#
|
|
9
|
+
# $0000-$1FFF - 8 KB of RAM
|
|
10
|
+
# $2000-$3FFF - nothing: open bus
|
|
11
|
+
# $4000-$5FFF - the 8520, its sixteen registers mirrored
|
|
12
|
+
# $6000-$7FFF - the WD1772, its four registers mirrored
|
|
13
|
+
# $8000-$FFFF - the DOS ROM
|
|
14
|
+
#
|
|
15
|
+
# Writes to the ROM and to open bus go nowhere, and a read from open
|
|
16
|
+
# bus finds the last byte on the data bus, as on the 1541. A write to
|
|
17
|
+
# either of the 8520's ports or their direction registers moves the
|
|
18
|
+
# drive's lines (Drives::Model1581#ports_written).
|
|
19
|
+
#
|
|
20
|
+
# While Idle records a pass of the DOS's idle loop, the bus watches it
|
|
21
|
+
# (watch!): the RAM it reads and writes, and whether it touched the
|
|
22
|
+
# 8520 or the WD1772, either of which makes the pass volatile. While
|
|
23
|
+
# Idle looks for an orbit, the bus guards the drive (guard!), and any
|
|
24
|
+
# access to either chip taints it.
|
|
25
|
+
class Bus
|
|
26
|
+
include Addressable
|
|
27
|
+
|
|
28
|
+
attr_reader :ram, :rom, :cia, :fdc, :data, :volatile, :touched, :tainted, :touched_timers
|
|
29
|
+
|
|
30
|
+
def initialize(rom:, cia:, fdc:, drive:)
|
|
31
|
+
addressable_at(0, length: 2**16)
|
|
32
|
+
@ram = Memory.new([], length: 0x2000, start: 0)
|
|
33
|
+
@rom = rom
|
|
34
|
+
@cia = cia
|
|
35
|
+
@fdc = fdc
|
|
36
|
+
@drive = drive
|
|
37
|
+
@data = 0
|
|
38
|
+
@recording = @guarding = @watching = false
|
|
39
|
+
@volatile = @tainted = false
|
|
40
|
+
@touched = {}
|
|
41
|
+
@touched_timers = 0
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def peek(addr)
|
|
45
|
+
return @data = @rom.peek(addr) if addr >= 0x8000
|
|
46
|
+
return @data = chip_peek(addr) if addr >= 0x2000
|
|
47
|
+
|
|
48
|
+
touch(addr) if @recording
|
|
49
|
+
@data = @ram.peek(addr)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def poke(addr, value)
|
|
53
|
+
@data = value
|
|
54
|
+
return if addr >= 0x8000
|
|
55
|
+
return chip_poke(addr, value) if addr >= 0x2000
|
|
56
|
+
|
|
57
|
+
touch(addr) if @recording
|
|
58
|
+
@ram.poke(addr, value)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Starts watching: see volatile and touched.
|
|
62
|
+
def watch!
|
|
63
|
+
@recording = true
|
|
64
|
+
@volatile = false
|
|
65
|
+
@touched = {}
|
|
66
|
+
watching!
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def unwatch!
|
|
70
|
+
@recording = false
|
|
71
|
+
watching!
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Starts guarding: see tainted.
|
|
75
|
+
def guard!
|
|
76
|
+
@guarding = true
|
|
77
|
+
@tainted = false
|
|
78
|
+
watching!
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def chip_peek(addr)
|
|
84
|
+
watch_chip if @watching
|
|
85
|
+
if addr >= 0x6000
|
|
86
|
+
@fdc.peek(addr & 0x03)
|
|
87
|
+
elsif addr >= 0x4000
|
|
88
|
+
@cia.peek(0x4000 | (addr & 0x0f))
|
|
89
|
+
else
|
|
90
|
+
@data
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def chip_poke(addr, value)
|
|
95
|
+
watch_chip if @watching
|
|
96
|
+
if addr >= 0x6000
|
|
97
|
+
@fdc.poke(addr & 0x03, value)
|
|
98
|
+
elsif addr >= 0x4000
|
|
99
|
+
@cia.poke(0x4000 | (addr & 0x0f), value)
|
|
100
|
+
@drive.ports_written if addr.nobits?(0x0c)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def touch(addr)
|
|
105
|
+
@touched[addr] = @ram.peek(addr) unless @touched.key?(addr)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def watching!
|
|
109
|
+
@watching = @recording || @guarding
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def watch_chip
|
|
113
|
+
if @recording
|
|
114
|
+
@volatile = true
|
|
115
|
+
@recording = false
|
|
116
|
+
end
|
|
117
|
+
if @guarding
|
|
118
|
+
@tainted = true
|
|
119
|
+
@guarding = false
|
|
120
|
+
end
|
|
121
|
+
watching!
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model1581
|
|
6
|
+
# A 3.5" double-sided disk as the WD1772 sees it: a Track on each side
|
|
7
|
+
# of 80 cylinders, or none where it was never formatted.
|
|
8
|
+
#
|
|
9
|
+
# A .d81 image holds the 1581 DOS's logical blocks in order, 40 of 256
|
|
10
|
+
# bytes on each logical track 1 to 80 (1581 Service Manual, format
|
|
11
|
+
# organization). Logical track t is cylinder t - 1, and the DOS puts
|
|
12
|
+
# its sectors 0 to 19 on side 0 and 20 to 39 on side 1, two to each
|
|
13
|
+
# 512-byte physical sector, numbered from 1. So physical sector s on
|
|
14
|
+
# side h of cylinder c holds logical sectors 20h + 2(s - 1) and the one
|
|
15
|
+
# after it, and every cylinder of the image is a standard Track
|
|
16
|
+
# (Track.standard) on both sides. A block the image's error table
|
|
17
|
+
# marks 20 or 21 loses its sector's ID field, 27 spoils the ID's CRC,
|
|
18
|
+
# 22 loses the data field and 23 spoils its CRC.
|
|
19
|
+
#
|
|
20
|
+
# The WD1772 writes into the tracks, and flush stores the standard
|
|
21
|
+
# sectors of each one written back in the image, which writes its host
|
|
22
|
+
# file. A track formatted another way stays on the disk alone. The disk
|
|
23
|
+
# is write-protected when the image won't take writes.
|
|
24
|
+
class Disk
|
|
25
|
+
CYLINDERS = 80
|
|
26
|
+
SIDES = 2
|
|
27
|
+
|
|
28
|
+
# Logical blocks a side of a cylinder holds.
|
|
29
|
+
SIDE_BLOCKS = 20
|
|
30
|
+
|
|
31
|
+
# A logical block's bytes, and how many a physical sector holds.
|
|
32
|
+
BLOCK_SIZE = 256
|
|
33
|
+
BLOCKS = Track::SECTOR_SIZE / BLOCK_SIZE
|
|
34
|
+
|
|
35
|
+
# A disk from the .d81 image at +path+. `read_only` opens the image
|
|
36
|
+
# write-protected.
|
|
37
|
+
def self.open(path, read_only: false)
|
|
38
|
+
new(Storage::D81Image.new(path, read_only:)).opened(path, read_only)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
attr_reader :image
|
|
42
|
+
|
|
43
|
+
def initialize(image = nil)
|
|
44
|
+
@image = image
|
|
45
|
+
@tracks = Array.new(CYLINDERS * SIDES)
|
|
46
|
+
@written = {}
|
|
47
|
+
@changed = {}
|
|
48
|
+
@path = nil
|
|
49
|
+
@read_only = false
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The Track on +side+ of +cylinder+, or nil where there is none: past
|
|
53
|
+
# the image's cylinders, or on a disk without an image that the head
|
|
54
|
+
# never formatted.
|
|
55
|
+
def track(cylinder, side)
|
|
56
|
+
index = (cylinder * SIDES) + side
|
|
57
|
+
return @tracks[index] if @tracks[index] || !@image || cylinder >= CYLINDERS
|
|
58
|
+
|
|
59
|
+
@tracks[index] = image_track(cylinder, side)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# A write track laid +track+ down on +side+ of +cylinder+, or a write
|
|
63
|
+
# sector changed it.
|
|
64
|
+
def write(cylinder, side, track)
|
|
65
|
+
return if cylinder >= CYLINDERS
|
|
66
|
+
|
|
67
|
+
index = (cylinder * SIDES) + side
|
|
68
|
+
@tracks[index] = track
|
|
69
|
+
@written[index] = true
|
|
70
|
+
@changed[index] = true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Whether the write-protect hole is open: the disk's image won't take
|
|
74
|
+
# writes, or a disk without an image was opened read-only.
|
|
75
|
+
def write_protected? = @image.nil? ? @read_only : !@image.writable?
|
|
76
|
+
|
|
77
|
+
# Whether any track was written since the last flush.
|
|
78
|
+
def written? = !@written.empty?
|
|
79
|
+
|
|
80
|
+
# Stores the standard sectors of each track written since the last
|
|
81
|
+
# flush in the image, all in one write to its host file.
|
|
82
|
+
def flush
|
|
83
|
+
written = @written.keys.sort
|
|
84
|
+
@written.clear
|
|
85
|
+
return if @image.nil? || written.empty?
|
|
86
|
+
|
|
87
|
+
@image.store_blocks(written.flat_map { |index| track_blocks(index / SIDES, index % SIDES) })
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def image_track(cylinder, side)
|
|
93
|
+
track = Track.standard(cylinder, side) { |sector| physical_block(cylinder, side, sector) }
|
|
94
|
+
track.sectors.each { |sector| mark_errors(cylinder, side, sector) }
|
|
95
|
+
track.sectors.reject! { |sector| sector.id.nil? }
|
|
96
|
+
track
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The logical track and first sector physical sector +sector+ holds.
|
|
100
|
+
def logical(cylinder, side, sector) = [cylinder + 1, (side * SIDE_BLOCKS) + ((sector - 1) * BLOCKS)]
|
|
101
|
+
|
|
102
|
+
def physical_block(cylinder, side, sector)
|
|
103
|
+
track, first = logical(cylinder, side, sector)
|
|
104
|
+
Array.new(BLOCKS) { |i| @image.read_block(track, first + i) || [] }.flatten
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def mark_errors(cylinder, side, sector)
|
|
108
|
+
track, first = logical(cylinder, side, sector.id.bytes[2])
|
|
109
|
+
errors = Array.new(BLOCKS) { |i| @image.block_error(track, first + i) }
|
|
110
|
+
sector.id.good = false if errors.include?(27)
|
|
111
|
+
sector.data.good = false if errors.include?(23)
|
|
112
|
+
sector.data = nil if errors.include?(22)
|
|
113
|
+
sector.id = nil if errors.include?(20) || errors.include?(21)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# [track, sector, data, nil] for DiskImage#store_blocks, for each
|
|
117
|
+
# logical block a standard sector of the track holds.
|
|
118
|
+
def track_blocks(cylinder, side)
|
|
119
|
+
track = @tracks[(cylinder * SIDES) + side]
|
|
120
|
+
Track::SECTORS.times.flat_map do |index|
|
|
121
|
+
data = track.sector([cylinder, side, index + 1, Track::SIZE_CODE])&.data
|
|
122
|
+
next [] unless data
|
|
123
|
+
|
|
124
|
+
logical_track, first = logical(cylinder, side, index + 1)
|
|
125
|
+
Array.new(BLOCKS) do |i|
|
|
126
|
+
[logical_track, first + i, data.bytes[i * BLOCK_SIZE, BLOCK_SIZE], nil]
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model1581
|
|
6
|
+
class WD1772
|
|
7
|
+
PHASE_NAMES = %w[idle spin_up stepped settled search read_data write_gate write_data read_track track_index
|
|
8
|
+
write_track motor_idle index_interrupt settle_search read_address write_track_start
|
|
9
|
+
starting].freeze
|
|
10
|
+
MODE_NAMES = %w[read write address verify_track].freeze
|
|
11
|
+
|
|
12
|
+
# The controller in a keyed snapshot module (BL-D8-FDC): its own
|
|
13
|
+
# cycle count, the step of the command running and what the search
|
|
14
|
+
# is for by name, the registers and lines, the command's counters,
|
|
15
|
+
# and when its next step, its deadline and the next ID field fall,
|
|
16
|
+
# in its cycles from now, each absent when it never does.
|
|
17
|
+
def save_fields(out)
|
|
18
|
+
out.int("now", @now).text("phase", PHASE_NAMES.fetch(@phase)).text("mode", MODE_NAMES.fetch(@mode))
|
|
19
|
+
[["due_in", @due], ["deadline_in", @deadline], ["id_in", @id_at]].each do |key, at|
|
|
20
|
+
out.int(key, at - @now) unless at == NEVER
|
|
21
|
+
end
|
|
22
|
+
out.int("command", @command).int("track", @track).int("sector", @sector).int("data", @data)
|
|
23
|
+
out.int("status", @status).int("steps", @steps).int("count", @count).int("after_spin_up", @after_spin_up)
|
|
24
|
+
out.int("found", @found).int("index", @index).int("crc", @crc).int("write_id", @write_id).int("size", @size)
|
|
25
|
+
save_flag_fields(out)
|
|
26
|
+
out.bytes("bytes", @bytes).ints("syncs", @syncs.map { |sync| sync ? 1 : 0 })
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def load_fields(input)
|
|
30
|
+
@now = input.int("now", @now)
|
|
31
|
+
@phase = PHASE_NAMES.index(input.choice("phase", PHASE_NAMES, "idle")).to_i
|
|
32
|
+
@mode = MODE_NAMES.index(input.choice("mode", MODE_NAMES, "read")).to_i
|
|
33
|
+
@due = at_fields(input, "due_in")
|
|
34
|
+
@deadline = at_fields(input, "deadline_in")
|
|
35
|
+
@id_at = at_fields(input, "id_in")
|
|
36
|
+
load_register_fields(input)
|
|
37
|
+
load_flag_fields(input)
|
|
38
|
+
@bytes = input.bytes("bytes", "".b).bytes
|
|
39
|
+
@syncs = input.ints("syncs", []).map { |sync| sync == 1 }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def at_fields(input, key)
|
|
45
|
+
cycles = input.optional_int(key)
|
|
46
|
+
cycles.nil? ? NEVER : @now + cycles
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def load_register_fields(input)
|
|
50
|
+
@command = input.byte("command", @command)
|
|
51
|
+
@track = input.byte("track", @track)
|
|
52
|
+
@sector = input.byte("sector", @sector)
|
|
53
|
+
@data = input.byte("data", @data)
|
|
54
|
+
@status = input.byte("status", @status)
|
|
55
|
+
@steps = input.int("steps", @steps)
|
|
56
|
+
@count = input.int("count", @count)
|
|
57
|
+
@after_spin_up = input.int("after_spin_up", @after_spin_up)
|
|
58
|
+
@found = input.int("found", @found)
|
|
59
|
+
@index = input.int("index", @index)
|
|
60
|
+
@crc = input.int("crc", @crc)
|
|
61
|
+
@write_id = input.int("write_id", @write_id)
|
|
62
|
+
@size = input.int("size", @size)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def save_flag_fields(out)
|
|
66
|
+
out.bool("type1", @type1).bool("drq", @drq).bool("intrq", @intrq).bool("motor_on", @motor_on)
|
|
67
|
+
out.bool("inward", @inward).bool("crc_good", @crc_good).bool("gate_open", @gate_open).bool("late", @late)
|
|
68
|
+
out.bool("settling", @settling)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def load_flag_fields(input)
|
|
72
|
+
@type1 = input.bool("type1", @type1)
|
|
73
|
+
@drq = input.bool("drq", @drq)
|
|
74
|
+
@intrq = input.bool("intrq", @intrq)
|
|
75
|
+
@motor_on = input.bool("motor_on", @motor_on)
|
|
76
|
+
@inward = input.bool("inward", @inward)
|
|
77
|
+
@crc_good = input.bool("crc_good", @crc_good)
|
|
78
|
+
@gate_open = input.bool("gate_open", @gate_open)
|
|
79
|
+
@late = input.bool("late", @late)
|
|
80
|
+
@settling = input.bool("settling", @settling)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class Mechanism
|
|
85
|
+
# The head, the motor and the disk-change switch in a keyed snapshot
|
|
86
|
+
# module (BL-D8-MECH), with the disk's angle as it stands `now`, on
|
|
87
|
+
# the controller's clock.
|
|
88
|
+
def save_fields(out, now)
|
|
89
|
+
out.int("cylinder", @cylinder).int("side", @side).int("angle", angle(now)).bool("motor", @motor)
|
|
90
|
+
out.bool("changed", @changed)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Puts `disk` under the head without flushing the one there, as a
|
|
94
|
+
# snapshot load replaces it.
|
|
95
|
+
def load_fields(input, disk, now)
|
|
96
|
+
@disk = disk
|
|
97
|
+
@cylinder = input.int("cylinder", @cylinder)
|
|
98
|
+
@side = input.masked("side", @side, 1)
|
|
99
|
+
@angle = input.int("angle", @angle)
|
|
100
|
+
@since = now
|
|
101
|
+
@motor = input.bool("motor", @motor)
|
|
102
|
+
@changed = input.bool("changed", @changed)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
class Disk
|
|
107
|
+
# A disk's fields in a keyed snapshot module (BL-D8-DISK): the image
|
|
108
|
+
# file's path and how it was opened, the image as last flushed,
|
|
109
|
+
# embedded, each track the head wrote since the disk went in, sector
|
|
110
|
+
# by sector as the WD1772 sees them, and the tracks written since the
|
|
111
|
+
# last flush.
|
|
112
|
+
module KeyedState
|
|
113
|
+
# The image's path, expanded, and whether it was opened read-only,
|
|
114
|
+
# for a disk from Disk.open. Nil for a disk made another way.
|
|
115
|
+
attr_reader :path, :read_only
|
|
116
|
+
|
|
117
|
+
# Notes the image's path and how it was opened.
|
|
118
|
+
def opened(path, read_only)
|
|
119
|
+
@path = File.expand_path(path)
|
|
120
|
+
@read_only = read_only
|
|
121
|
+
self
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def save_fields(out)
|
|
125
|
+
path = @path
|
|
126
|
+
out.text("path", path) if path
|
|
127
|
+
out.bool("read_only", @read_only == true)
|
|
128
|
+
image = @image
|
|
129
|
+
out.text("image.path", image.path.to_s).bytes("image", image.contents) if image
|
|
130
|
+
changed = @changed.keys.sort
|
|
131
|
+
out.ints("changed", changed)
|
|
132
|
+
changed.each { |index| save_track_fields(out, "track#{index}.", @tracks[index]) }
|
|
133
|
+
out.ints("written", @written.keys)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def load_track_fields(input)
|
|
137
|
+
@changed.clear
|
|
138
|
+
input.ints("changed", []).each do |index|
|
|
139
|
+
@tracks[index] = load_track_sectors(input, "track#{index}.")
|
|
140
|
+
@changed[index] = true
|
|
141
|
+
end
|
|
142
|
+
@written.clear
|
|
143
|
+
input.ints("written", []).each { |index| @written[index] = true }
|
|
144
|
+
self
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
def save_track_fields(out, prefix, track)
|
|
150
|
+
out.int("#{prefix}sectors", track.sectors.length)
|
|
151
|
+
track.sectors.each_with_index do |sector, number|
|
|
152
|
+
save_field_fields(out, "#{prefix}sector#{number}.id.", sector.id)
|
|
153
|
+
data = sector.data
|
|
154
|
+
save_field_fields(out, "#{prefix}sector#{number}.data.", data) if data
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def save_field_fields(out, prefix, field)
|
|
159
|
+
out.int("#{prefix}mark", field.mark).bytes("#{prefix}bytes", field.bytes)
|
|
160
|
+
out.bool("#{prefix}good", field.good).bool("#{prefix}deleted", field.deleted)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def load_track_sectors(input, prefix)
|
|
164
|
+
Track.new(Array.new(input.int("#{prefix}sectors", 0)) do |number|
|
|
165
|
+
id = load_field_fields(input, "#{prefix}sector#{number}.id.")
|
|
166
|
+
data = input.optional_int("#{prefix}sector#{number}.data.mark")
|
|
167
|
+
Track::Sector.new(id, data.nil? ? nil : load_field_fields(input, "#{prefix}sector#{number}.data."))
|
|
168
|
+
end)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def load_field_fields(input, prefix)
|
|
172
|
+
Track::Field.new(input.int("#{prefix}mark", 0), input.bytes("#{prefix}bytes", "".b).bytes,
|
|
173
|
+
input.bool("#{prefix}good", true), input.bool("#{prefix}deleted", false))
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
include KeyedState
|
|
178
|
+
|
|
179
|
+
# The disk a keyed snapshot module describes: its embedded image,
|
|
180
|
+
# with the tracks the module holds laid over it. The image takes
|
|
181
|
+
# writes back to its file only while that file is there and the
|
|
182
|
+
# reader isn't `detached`. With no image, the disk is the tracks
|
|
183
|
+
# alone.
|
|
184
|
+
def self.from_fields(input, detached: false)
|
|
185
|
+
path = input.text("path", "")
|
|
186
|
+
read_only = input.bool("read_only", false)
|
|
187
|
+
bytes = input.bytes("image", "".b).bytes
|
|
188
|
+
image_path = input.text("image.path", "")
|
|
189
|
+
disk = bytes.empty? ? Disk.new : Disk.new(image_from(image_path, bytes, read_only || detached))
|
|
190
|
+
disk.opened(path, read_only || bytes.empty?) unless path.empty?
|
|
191
|
+
disk.load_track_fields(input)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def self.image_from(path, bytes, read_only)
|
|
195
|
+
Storage::D81Image.new(File.file?(path) ? path : "", read_only:, bytes:)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
class Bus
|
|
200
|
+
# The drive's RAM and the byte last on its data bus, in a keyed
|
|
201
|
+
# snapshot module (BL-D8-RAM).
|
|
202
|
+
def save_fields(out)
|
|
203
|
+
out.int("data", @data).bytes("ram", @ram.read(0, @ram.length))
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def load_fields(input)
|
|
207
|
+
@data = input.byte("data", @data)
|
|
208
|
+
ram = @ram.read(0, @ram.length)
|
|
209
|
+
@ram.write(0, input.bytes_into("ram", ram))
|
|
210
|
+
@watching = false
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# The drive's own fields in a keyed snapshot module (BL-D8): its
|
|
215
|
+
# model, its clock phase and cycle count, the ATN level it last saw
|
|
216
|
+
# and the host's lines as the serial port latched them. The fast
|
|
217
|
+
# serial direction and the ports' lines come from the 8520 once it
|
|
218
|
+
# is loaded.
|
|
219
|
+
module KeyedState
|
|
220
|
+
def save_fields(out)
|
|
221
|
+
settle!
|
|
222
|
+
out.text("model", model_name).int("phase", @phase).int("cycles", @cycles).bool("atn", @atn)
|
|
223
|
+
@serial_port.save_fields(out, "serial.")
|
|
224
|
+
out
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def load_fields(input)
|
|
228
|
+
settle!
|
|
229
|
+
input.choice("model", [model_name], model_name)
|
|
230
|
+
@phase = input.int("phase", @phase)
|
|
231
|
+
@cycles = input.int("cycles", @cycles)
|
|
232
|
+
@atn = input.bool("atn", @atn)
|
|
233
|
+
@serial_port.load_fields(input, "serial.")
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def restored!
|
|
237
|
+
@fast_serial_out = @cia.port_b_lines.anybits?(FAST_SERIAL_OUT)
|
|
238
|
+
ports_written
|
|
239
|
+
push_fast_output(@fast_serial_out ? fast_pins : 0)
|
|
240
|
+
forget_orbits
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# The drive as modules of its own, as the 1541's, with the 8520 and
|
|
244
|
+
# the WD1772 in place of the VIAs.
|
|
245
|
+
def save_modules(sections, name)
|
|
246
|
+
sections << Snapshot::Keyed.section(name) { |out| save_fields(out) }
|
|
247
|
+
sections << Snapshot::Keyed.section("#{name}-RAM") { |out| @bus.save_fields(out) }
|
|
248
|
+
sections << Snapshot::Keyed.section("#{name}-CIA") { |out| @cia.save_fields(out) }
|
|
249
|
+
sections << Snapshot::Keyed.section("#{name}-CPU") { |out| @cpu.save_fields(out) }
|
|
250
|
+
sections << Snapshot::Keyed.section("#{name}-FDC") { |out| @fdc.save_fields(out) }
|
|
251
|
+
now = @fdc.now
|
|
252
|
+
sections << Snapshot::Keyed.section("#{name}-MECH") { |out| @mechanism.save_fields(out, now) }
|
|
253
|
+
disk = @mechanism.disk
|
|
254
|
+
sections << Snapshot::Keyed.section("#{name}-DISK") { |out| disk.save_fields(out) } if disk
|
|
255
|
+
sections
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# The drive from its modules, `input` being its own. The mechanism
|
|
259
|
+
# takes the disk's angle on the WD1772's clock, so it loads after
|
|
260
|
+
# the controller.
|
|
261
|
+
def load_modules(modules, input, name)
|
|
262
|
+
load_fields(input)
|
|
263
|
+
@bus.load_fields(modules.required("#{name}-RAM"))
|
|
264
|
+
@cia.load_fields(modules.required("#{name}-CIA"))
|
|
265
|
+
@cpu.load_fields(modules.required("#{name}-CPU"))
|
|
266
|
+
@fdc.load_fields(modules.required("#{name}-FDC"))
|
|
267
|
+
disk = modules.optional("#{name}-DISK")
|
|
268
|
+
mechanism = modules.required("#{name}-MECH")
|
|
269
|
+
@mechanism.load_fields(mechanism, disk.nil? ? nil : Disk.from_fields(disk, detached: modules.detached?),
|
|
270
|
+
@fdc.now)
|
|
271
|
+
@cia.restored!
|
|
272
|
+
@cpu.restored!
|
|
273
|
+
restored!
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
include KeyedState
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model1581
|
|
6
|
+
# The 1581's 3.5" mechanism: the spindle motor, the head on one of
|
|
7
|
+
# its cylinders and sides, the index sensor, and the lines it reports
|
|
8
|
+
# on the drive connector (1581 Service Manual, schematic sheet 2).
|
|
9
|
+
#
|
|
10
|
+
# The motor runs while /MOTOR is low and turns a disk at 300 rpm, a
|
|
11
|
+
# turn every REVOLUTION drive cycles at 2 MHz. The index sensor pulses
|
|
12
|
+
# for INDEX_PULSE cycles at the start of each turn, which is where a
|
|
13
|
+
# Track's bytes start. /RDY is low while the motor turns a disk. The
|
|
14
|
+
# WD1772 steps the head, and /TR00 is low while it sits on cylinder 0.
|
|
15
|
+
# /WPRT is low while the disk's write-protect hole is open, or without
|
|
16
|
+
# a disk.
|
|
17
|
+
#
|
|
18
|
+
# /DISK CHNG goes low as a disk comes out, and at power-on, and goes
|
|
19
|
+
# high again at a step pulse with a disk in, as on any Shugart-style
|
|
20
|
+
# drive with a disk change line. The DOS steps in and out after
|
|
21
|
+
# spinning the motor up to find out whether a disk is in.
|
|
22
|
+
#
|
|
23
|
+
# Times are the WD1772's cycle count (WD1772#now), which the drive
|
|
24
|
+
# passes in: the angle under the head follows from how long the disk
|
|
25
|
+
# has turned.
|
|
26
|
+
class Mechanism
|
|
27
|
+
REVOLUTION = 400_000
|
|
28
|
+
INDEX_PULSE = 4_000
|
|
29
|
+
LAST_CYLINDER = 83
|
|
30
|
+
|
|
31
|
+
attr_reader :disk, :cylinder
|
|
32
|
+
attr_accessor :side
|
|
33
|
+
|
|
34
|
+
def initialize
|
|
35
|
+
@disk = nil
|
|
36
|
+
@cylinder = 0
|
|
37
|
+
@side = 0
|
|
38
|
+
@motor = false
|
|
39
|
+
@changed = true
|
|
40
|
+
@angle = 0
|
|
41
|
+
@since = 0
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Puts a Disk in, or with nil takes it out. Whatever was in is
|
|
45
|
+
# flushed first, and the disk change line goes low.
|
|
46
|
+
def insert(disk, now)
|
|
47
|
+
@angle = angle(now)
|
|
48
|
+
@since = now
|
|
49
|
+
flush
|
|
50
|
+
@disk = disk
|
|
51
|
+
@changed = true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def flush = @disk&.flush
|
|
55
|
+
|
|
56
|
+
def motor_on? = @motor
|
|
57
|
+
|
|
58
|
+
# Turns the motor on or off at +now+.
|
|
59
|
+
def motor(on, now)
|
|
60
|
+
@angle = angle(now)
|
|
61
|
+
@since = now
|
|
62
|
+
@motor = on
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Whether a disk turns.
|
|
66
|
+
def spinning? = @motor && !@disk.nil?
|
|
67
|
+
|
|
68
|
+
# How far into its turn the disk is at +now+, in cycles from the
|
|
69
|
+
# index.
|
|
70
|
+
def angle(now) = spinning? ? (@angle + now - @since) % REVOLUTION : @angle
|
|
71
|
+
|
|
72
|
+
def index?(now) = spinning? && angle(now) < INDEX_PULSE
|
|
73
|
+
|
|
74
|
+
def ready? = spinning?
|
|
75
|
+
|
|
76
|
+
def track0? = @cylinder.zero?
|
|
77
|
+
|
|
78
|
+
def disk_changed? = @changed
|
|
79
|
+
|
|
80
|
+
def write_protected? = @disk.nil? || @disk.write_protected?
|
|
81
|
+
|
|
82
|
+
# A step pulse, towards the hub with +inward+.
|
|
83
|
+
def step(inward)
|
|
84
|
+
@cylinder = (@cylinder + (inward ? 1 : -1)).clamp(0, LAST_CYLINDER)
|
|
85
|
+
@changed = false if @disk
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# The Track under the head, or nil.
|
|
89
|
+
def track = @disk&.track(@cylinder, @side)
|
|
90
|
+
|
|
91
|
+
# Stores a track the WD1772 laid down under the head.
|
|
92
|
+
def write(track)
|
|
93
|
+
@disk&.write(@cylinder, @side, track)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|