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,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class C64
|
|
5
|
+
class Cartridge
|
|
6
|
+
# EasyFlash: two Am29F040B flash chips, one for ROML and one for ROMH,
|
|
7
|
+
# each 64 banks of 8K, and 256 bytes of RAM at $DF00. $DE00 selects the
|
|
8
|
+
# bank. $DE02 sets the memory configuration: bit 2 hands the GAME line
|
|
9
|
+
# to bit 0 (otherwise the boot jumper holds it low), bit 1 pulls EXROM
|
|
10
|
+
# low and bit 7 drives the LED.
|
|
11
|
+
#
|
|
12
|
+
# The flash takes writes in Ultimax mode, at $8000 for ROML and $E000
|
|
13
|
+
# for ROMH, which is how EAPI programs and erases it. The writes stay in
|
|
14
|
+
# memory; #save_crt writes the flash out as a new image.
|
|
15
|
+
#
|
|
16
|
+
# An image built for EasyFlash carries an EAPI, the flash driver, at $B800
|
|
17
|
+
# in bank 0 of ROMH. On attach, as in VICE, the bundled Am29F040 EAPI
|
|
18
|
+
# (roms/eapi) takes its place, so the image drives the flash emulated here.
|
|
19
|
+
class EasyFlash < Cartridge
|
|
20
|
+
BANKS = 64
|
|
21
|
+
EAPI_OFFSET = 0x1800
|
|
22
|
+
|
|
23
|
+
def self.eapi
|
|
24
|
+
@eapi ||= ROM.read("eapi/eapi-am29f040-14").drop(2).freeze
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
attr_reader :low_flash, :high_flash
|
|
28
|
+
|
|
29
|
+
def clock=(clock)
|
|
30
|
+
super
|
|
31
|
+
[@low_flash, @high_flash].each { |flash| flash.clock = clock }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def readable_io_pages
|
|
35
|
+
[0xdf]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def peek(addr)
|
|
39
|
+
@io_ram[addr & 0xff]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def poke(addr, value)
|
|
43
|
+
if addr >= 0xdf00
|
|
44
|
+
@io_ram[addr & 0xff] = value
|
|
45
|
+
elsif addr.nobits?(0x02)
|
|
46
|
+
@bank = value & 0x3f
|
|
47
|
+
select_bank
|
|
48
|
+
else
|
|
49
|
+
@control = value & 0x87
|
|
50
|
+
apply_control
|
|
51
|
+
changed!
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def led?
|
|
56
|
+
@control.anybits?(0x80)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Reset clears both registers, and leaves the flash and the RAM.
|
|
60
|
+
def reset
|
|
61
|
+
@bank = 0
|
|
62
|
+
@control = 0
|
|
63
|
+
apply_control
|
|
64
|
+
select_bank
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Writes every bank that isn't blank to a CRT image at path.
|
|
68
|
+
def save_crt(path)
|
|
69
|
+
chips = (0...BANKS).flat_map do |number|
|
|
70
|
+
[[@low_flash, 0x8000], [@high_flash, 0xa000]].filter_map do |flash, address|
|
|
71
|
+
data = flash.data[number * BANK_SIZE, BANK_SIZE]
|
|
72
|
+
Storage::CRTFile::Chip.new(chip_type: 2, bank: number, address:, data:) if data.any?(0..0xfe)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
image = Storage::CRTFile::Image.new(hardware_type: 32, subtype: 0, exrom: 1, game: 0, name:, chips:)
|
|
76
|
+
Storage::CRTFile.write(path, image)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def select_bank
|
|
82
|
+
map_bank
|
|
83
|
+
changed!
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def map_bank
|
|
87
|
+
@roml = @low_flash.window(@bank * BANK_SIZE)
|
|
88
|
+
@romh = @high_flash.window(@bank * BANK_SIZE)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def apply_control
|
|
92
|
+
game = @control.nobits?(0x04) || @control.anybits?(0x01)
|
|
93
|
+
exrom = @control.anybits?(0x02)
|
|
94
|
+
@game = game ? 0 : 1
|
|
95
|
+
@exrom = exrom ? 0 : 1
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def install_chips(chips)
|
|
99
|
+
low = Array.new(BANKS * BANK_SIZE, 0xff)
|
|
100
|
+
high = Array.new(BANKS * BANK_SIZE, 0xff)
|
|
101
|
+
chips.each do |chip|
|
|
102
|
+
offset = (chip.bank & 0x3f) * BANK_SIZE
|
|
103
|
+
if chip.address >= ROMH_START
|
|
104
|
+
place(high, offset, chip.data)
|
|
105
|
+
else
|
|
106
|
+
place(low, offset, chip.data)
|
|
107
|
+
place(high, offset, chip.data[BANK_SIZE..]) if chip.data.length > BANK_SIZE
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
install_eapi(high)
|
|
111
|
+
@low_flash = Flash.new(low)
|
|
112
|
+
@high_flash = Flash.new(high)
|
|
113
|
+
[@low_flash, @high_flash].each { |flash| flash.on_change { select_bank } }
|
|
114
|
+
@io_ram = Array.new(0x100, 0xff)
|
|
115
|
+
reset
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def install_eapi(high)
|
|
119
|
+
return unless high[EAPI_OFFSET, 4] == "eapi".bytes
|
|
120
|
+
|
|
121
|
+
eapi = EasyFlash.eapi
|
|
122
|
+
high[EAPI_OFFSET, eapi.length] = eapi
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def place(flash_data, offset, bytes)
|
|
126
|
+
bytes = bytes.first(BANK_SIZE)
|
|
127
|
+
flash_data[offset, bytes.length] = bytes
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class C64
|
|
5
|
+
class Cartridge
|
|
6
|
+
# Epyx FastLoad: 8K. A capacitor holds the ROM in for 512 cycles after
|
|
7
|
+
# it was last discharged, by reset, a read through ROML or a read of
|
|
8
|
+
# I/O 1; once it charges, $8000-$9FFF reads RAM. I/O 2 reads the last
|
|
9
|
+
# 256 bytes of the ROM.
|
|
10
|
+
class EpyxFastload < Cartridge
|
|
11
|
+
DISCHARGED_CYCLES = 512
|
|
12
|
+
|
|
13
|
+
class Window
|
|
14
|
+
def initialize(cartridge, rom)
|
|
15
|
+
@cartridge = cartridge
|
|
16
|
+
@rom = rom
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def peek(addr)
|
|
20
|
+
@cartridge.rom_visible? ? @rom.peek(addr) : @cartridge.ram_peek(addr)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def clock=(clock)
|
|
25
|
+
super
|
|
26
|
+
discharge!
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def readable_io_pages
|
|
30
|
+
[0xde, 0xdf]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def peek(addr)
|
|
34
|
+
return @rom.peek(addr) if addr >= 0xdf00
|
|
35
|
+
|
|
36
|
+
discharge!
|
|
37
|
+
open_bus(addr)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# True while the capacitor holds the ROM in, discharging it again.
|
|
41
|
+
def rom_visible?
|
|
42
|
+
return true unless @clock
|
|
43
|
+
return false if @clock.call - @discharged_at >= DISCHARGED_CYCLES
|
|
44
|
+
|
|
45
|
+
discharge!
|
|
46
|
+
true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def ram_peek(addr)
|
|
50
|
+
@ram.peek(addr)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Reset discharges the capacitor, so the ROM shows.
|
|
54
|
+
def reset
|
|
55
|
+
discharge!
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def discharge!
|
|
61
|
+
@discharged_at = @clock ? @clock.call : 0
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def install_chips(chips)
|
|
65
|
+
@rom = banks_from(chips).first.compact.first || EMPTY_BANK
|
|
66
|
+
@roml = Window.new(self, @rom)
|
|
67
|
+
@discharged_at = 0
|
|
68
|
+
self.mode = :rom8k
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class C64
|
|
5
|
+
class Cartridge
|
|
6
|
+
# The Final Cartridge III: four 16K banks, or sixteen on the III+. I/O 1
|
|
7
|
+
# and I/O 2 show the last two pages of the selected ROML bank. A write
|
|
8
|
+
# to $DFFF sets the register:
|
|
9
|
+
#
|
|
10
|
+
# bits 0-1 bank (0-3 on the III+)
|
|
11
|
+
# bit 4 EXROM level
|
|
12
|
+
# bit 5 GAME level
|
|
13
|
+
# bit 6 NMI line, active low
|
|
14
|
+
# bit 7 hides the register until the next freeze
|
|
15
|
+
#
|
|
16
|
+
# Clearing bit 6 pulls NMI without freezing, and setting it lets go of
|
|
17
|
+
# NMI after a freeze. A freeze keeps the bank and switches to Ultimax,
|
|
18
|
+
# which the VIC doesn't see until the register sets it again.
|
|
19
|
+
class FinalCartridge3 < Cartridge
|
|
20
|
+
include Freezer
|
|
21
|
+
|
|
22
|
+
REGISTER = 0xdfff
|
|
23
|
+
|
|
24
|
+
def readable_io_pages
|
|
25
|
+
[0xde, 0xdf]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def peek(addr)
|
|
29
|
+
@io_bank.peek(addr)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def poke(addr, value)
|
|
33
|
+
return unless addr == REGISTER && @register_visible
|
|
34
|
+
|
|
35
|
+
@register_visible = value.nobits?(0x80)
|
|
36
|
+
self.nmi = value.nobits?(0x40)
|
|
37
|
+
select(value & @bank_mask)
|
|
38
|
+
@exrom = (value >> 4) & 0x01
|
|
39
|
+
@game = (value >> 5) & 0x01
|
|
40
|
+
@phi1_ultimax = ultimax?
|
|
41
|
+
changed!
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def phi1_ultimax?
|
|
45
|
+
@phi1_ultimax
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def reset
|
|
49
|
+
@register_visible = true
|
|
50
|
+
@phi1_ultimax = false
|
|
51
|
+
self.nmi = false
|
|
52
|
+
select(0)
|
|
53
|
+
select_mode(:rom16k)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def freeze!
|
|
57
|
+
@register_visible = true
|
|
58
|
+
@phi1_ultimax = false
|
|
59
|
+
select_mode(:ultimax)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def windows = [*@roml_banks, *@romh_banks, EMPTY_BANK]
|
|
65
|
+
|
|
66
|
+
def select(number)
|
|
67
|
+
@io_bank = @roml = bank(@roml_banks, number)
|
|
68
|
+
@romh = bank(@romh_banks, number)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def install_chips(chips)
|
|
72
|
+
@roml_banks, @romh_banks = banks_from(chips)
|
|
73
|
+
@bank_mask = @roml_banks.length > 4 ? 0x0f : 0x03
|
|
74
|
+
reset
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class C64
|
|
5
|
+
class Cartridge
|
|
6
|
+
# An AMD 29F0x0 flash chip, driven through the JEDEC command set in the
|
|
7
|
+
# AMD datasheets. Each command opens with two unlock cycles, $AA to the
|
|
8
|
+
# first unlock address and $55 to the second, and a third write picks
|
|
9
|
+
# the command:
|
|
10
|
+
#
|
|
11
|
+
# $90 autoselect: offset 0 reads the manufacturer, 1 the device and 2
|
|
12
|
+
# the sector protection, until a reset
|
|
13
|
+
# $F0 reset to reading the array, which a lone $F0 also does
|
|
14
|
+
# $A0 byte program: the next write clears bits of the array, which
|
|
15
|
+
# only an erase sets again
|
|
16
|
+
# $80 erase, after two more unlock cycles: $10 to the first unlock
|
|
17
|
+
# address erases the chip, and $30 to a sector erases the sector.
|
|
18
|
+
# Further sectors can follow within the 50 us window.
|
|
19
|
+
#
|
|
20
|
+
# While a program or erase runs, a read returns status: DQ7 the
|
|
21
|
+
# complement of the programmed bit (0 while erasing), DQ6 toggling on
|
|
22
|
+
# every read, DQ5 set when a program failed and DQ3 set once an erase
|
|
23
|
+
# has left its window. $B0 suspends a sector erase and $30 resumes it.
|
|
24
|
+
#
|
|
25
|
+
# The cartridge reads the array through windows, and the chip swaps
|
|
26
|
+
# them for status windows while it is busy or in autoselect.
|
|
27
|
+
class Flash
|
|
28
|
+
Model = Data.define(:size, :sector_size, :unlock1, :unlock2, :unlock_mask, :manufacturer, :device)
|
|
29
|
+
|
|
30
|
+
AM29F040 = Model.new(size: 0x80000, sector_size: 0x10000, unlock1: 0x5555, unlock2: 0x2aaa,
|
|
31
|
+
unlock_mask: 0x7fff, manufacturer: 0x01, device: 0xa4)
|
|
32
|
+
AM29F040B = Model.new(size: 0x80000, sector_size: 0x10000, unlock1: 0x555, unlock2: 0x2aa,
|
|
33
|
+
unlock_mask: 0x7ff, manufacturer: 0x01, device: 0xa4)
|
|
34
|
+
AM29F010 = Model.new(size: 0x20000, sector_size: 0x4000, unlock1: 0x5555, unlock2: 0x2aaa,
|
|
35
|
+
unlock_mask: 0x7fff, manufacturer: 0x01, device: 0x20)
|
|
36
|
+
|
|
37
|
+
# The datasheet's typical times, in cycles at 1 MHz.
|
|
38
|
+
PROGRAM_CYCLES = 7
|
|
39
|
+
ERASE_WINDOW_CYCLES = 50
|
|
40
|
+
SECTOR_ERASE_CYCLES = 1_000_000
|
|
41
|
+
CHIP_ERASE_CYCLES = 8_000_000
|
|
42
|
+
|
|
43
|
+
# The array, read directly while the chip reads it.
|
|
44
|
+
class Window
|
|
45
|
+
def initialize(flash, base)
|
|
46
|
+
@flash = flash
|
|
47
|
+
@data = flash.data
|
|
48
|
+
@base = base
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def peek(addr)
|
|
52
|
+
@data[@base | (addr & 0x1fff)]
|
|
53
|
+
end
|
|
54
|
+
alias [] peek
|
|
55
|
+
|
|
56
|
+
def poke(addr, value)
|
|
57
|
+
@flash.write(@base | (addr & 0x1fff), value)
|
|
58
|
+
end
|
|
59
|
+
alias []= poke
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Reads through the chip, for its status and autoselect codes.
|
|
63
|
+
class StatusWindow < Window
|
|
64
|
+
def peek(addr)
|
|
65
|
+
@flash.read(@base | (addr & 0x1fff))
|
|
66
|
+
end
|
|
67
|
+
alias [] peek
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# The command states, which a snapshot names.
|
|
71
|
+
STATES = %i[read program_setup autoselect programming erase_window erasing suspended chip_erase
|
|
72
|
+
program_error].freeze
|
|
73
|
+
|
|
74
|
+
attr_reader :data, :state
|
|
75
|
+
attr_writer :clock
|
|
76
|
+
|
|
77
|
+
def initialize(data, model: AM29F040B, clock: -> { 0 })
|
|
78
|
+
@model = model
|
|
79
|
+
@data = data + ([0xff] * (model.size - data.length))
|
|
80
|
+
@clock = clock
|
|
81
|
+
@windows = {}
|
|
82
|
+
@status_windows = {}
|
|
83
|
+
@on_change = nil
|
|
84
|
+
@state = :read
|
|
85
|
+
@base_state = :read
|
|
86
|
+
@cycle = 0
|
|
87
|
+
@toggle = 0
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Called when the chip starts or stops answering reads from the array.
|
|
91
|
+
def on_change(&block)
|
|
92
|
+
@on_change = block
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def window(base)
|
|
96
|
+
if array_mode?
|
|
97
|
+
@windows[base] ||= Window.new(self, base)
|
|
98
|
+
else
|
|
99
|
+
@status_windows[base] ||= StatusWindow.new(self, base)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Hands out new windows at the bases given, as #window would have.
|
|
104
|
+
def rebuild_windows(bases, status_bases)
|
|
105
|
+
@windows = {}
|
|
106
|
+
@status_windows = {}
|
|
107
|
+
bases.each { |base| @windows[base] = Window.new(self, base) }
|
|
108
|
+
status_bases.each { |base| @status_windows[base] = StatusWindow.new(self, base) }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def array_mode?
|
|
112
|
+
@state == :read || @state == :program_setup
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def read(offset)
|
|
116
|
+
settle
|
|
117
|
+
case @state
|
|
118
|
+
when :read then @data[offset]
|
|
119
|
+
when :autoselect then autoselect_code(offset)
|
|
120
|
+
when :suspended then suspended_read(offset)
|
|
121
|
+
else status
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def write(offset, value)
|
|
126
|
+
settle
|
|
127
|
+
case @state
|
|
128
|
+
when :program_setup then program(offset, value)
|
|
129
|
+
when :erase_window then extend_erase(offset, value)
|
|
130
|
+
when :erasing then suspend if value == 0xb0
|
|
131
|
+
when :suspended then resume if value == 0x30
|
|
132
|
+
when :program_error then enter(:read, base: :read) if value == 0xf0
|
|
133
|
+
when :programming, :chip_erase then nil
|
|
134
|
+
else command(offset, value)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
def command(offset, value)
|
|
141
|
+
if value == 0xf0
|
|
142
|
+
@cycle = 0
|
|
143
|
+
return enter(:read, base: :read)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
address = offset & @model.unlock_mask
|
|
147
|
+
@cycle = case @cycle
|
|
148
|
+
when 0, 3 then address == @model.unlock1 && value == 0xaa ? @cycle + 1 : 0
|
|
149
|
+
when 1, 4 then address == @model.unlock2 && value == 0x55 ? @cycle + 1 : 0
|
|
150
|
+
when 2 then command_cycle(address, value)
|
|
151
|
+
when 5 then erase_command(offset, address, value)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def command_cycle(address, value)
|
|
156
|
+
return 0 unless address == @model.unlock1
|
|
157
|
+
|
|
158
|
+
case value
|
|
159
|
+
when 0x90 then enter(:autoselect, base: :autoselect)
|
|
160
|
+
when 0xa0 then @state = :program_setup
|
|
161
|
+
when 0x80 then return 3
|
|
162
|
+
end
|
|
163
|
+
0
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def erase_command(offset, address, value)
|
|
167
|
+
if value == 0x10 && address == @model.unlock1
|
|
168
|
+
start_busy(:chip_erase, CHIP_ERASE_CYCLES)
|
|
169
|
+
elsif value == 0x30
|
|
170
|
+
@erasing_sectors = [sector(offset)]
|
|
171
|
+
start_busy(:erase_window, ERASE_WINDOW_CYCLES)
|
|
172
|
+
end
|
|
173
|
+
0
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def program(offset, value)
|
|
177
|
+
@programmed = value
|
|
178
|
+
@data[offset] &= value
|
|
179
|
+
if @data[offset] == value
|
|
180
|
+
start_busy(:programming, PROGRAM_CYCLES)
|
|
181
|
+
else
|
|
182
|
+
enter(:program_error)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def extend_erase(offset, value)
|
|
187
|
+
case value
|
|
188
|
+
when 0x30
|
|
189
|
+
@erasing_sectors |= [sector(offset)]
|
|
190
|
+
@done_at = now + ERASE_WINDOW_CYCLES
|
|
191
|
+
when 0xb0
|
|
192
|
+
@remaining = @erasing_sectors.length * SECTOR_ERASE_CYCLES
|
|
193
|
+
enter(:suspended)
|
|
194
|
+
else
|
|
195
|
+
@erasing_sectors = nil
|
|
196
|
+
enter(@base_state)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def suspend
|
|
201
|
+
@remaining = @done_at - now
|
|
202
|
+
enter(:suspended)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def resume
|
|
206
|
+
start_busy(:erasing, @remaining)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Moves a timed operation on once the clock has passed its end.
|
|
210
|
+
def settle
|
|
211
|
+
return unless @done_at && now >= @done_at
|
|
212
|
+
|
|
213
|
+
case @state
|
|
214
|
+
when :erase_window
|
|
215
|
+
start_busy(:erasing, @done_at - now + (@erasing_sectors.length * SECTOR_ERASE_CYCLES))
|
|
216
|
+
settle
|
|
217
|
+
when :erasing
|
|
218
|
+
@erasing_sectors.each { |s| @data.fill(0xff, s * @model.sector_size, @model.sector_size) }
|
|
219
|
+
@erasing_sectors = nil
|
|
220
|
+
finish
|
|
221
|
+
when :chip_erase
|
|
222
|
+
@data.fill(0xff)
|
|
223
|
+
finish
|
|
224
|
+
else
|
|
225
|
+
finish
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def finish
|
|
230
|
+
@done_at = nil
|
|
231
|
+
enter(@base_state)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def start_busy(state, cycles)
|
|
235
|
+
@done_at = now + cycles
|
|
236
|
+
@programmed = 0xff unless state == :programming
|
|
237
|
+
enter(state)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def enter(state, base: @base_state)
|
|
241
|
+
was_array = array_mode?
|
|
242
|
+
@state = state
|
|
243
|
+
@base_state = base
|
|
244
|
+
@done_at = nil unless %i[programming erase_window erasing chip_erase].include?(state)
|
|
245
|
+
@on_change&.call if was_array != array_mode?
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def status
|
|
249
|
+
@toggle ^= 0x40
|
|
250
|
+
value = (~@programmed & 0x80) | @toggle
|
|
251
|
+
value |= 0x20 if @state == :program_error
|
|
252
|
+
value |= 0x08 if @state == :erasing || @state == :chip_erase
|
|
253
|
+
value
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def suspended_read(offset)
|
|
257
|
+
@erasing_sectors.include?(sector(offset)) ? 0x80 : @data[offset]
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def autoselect_code(offset)
|
|
261
|
+
case offset & 0x43
|
|
262
|
+
when 0 then @model.manufacturer
|
|
263
|
+
when 1 then @model.device
|
|
264
|
+
when 2 then 0x00
|
|
265
|
+
else @data[offset]
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def sector(offset)
|
|
270
|
+
offset / @model.sector_size
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def now
|
|
274
|
+
@clock.call
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class C64
|
|
5
|
+
class Cartridge
|
|
6
|
+
# The freeze button of a freezer cartridge. A press pulls NMI, and the
|
|
7
|
+
# computer calls #freeze! once the CPU has pushed the return address and
|
|
8
|
+
# status of the interrupt, so the vector fetch that follows reads the
|
|
9
|
+
# cartridge ROM in Ultimax mode. The cartridge lets go of NMI when its
|
|
10
|
+
# software acknowledges the freeze.
|
|
11
|
+
module Freezer
|
|
12
|
+
def freezer? = true
|
|
13
|
+
|
|
14
|
+
def press_button
|
|
15
|
+
@button = true
|
|
16
|
+
self.nmi = true if freeze_allowed?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def release_button
|
|
20
|
+
@button = false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def freeze_allowed?
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def button_pressed?
|
|
30
|
+
@button == true
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class C64
|
|
5
|
+
class Cartridge
|
|
6
|
+
# Fun Play / Power Play: up to 16 8K banks. A write to I/O 1 selects the
|
|
7
|
+
# bank from bits 3-5 and 0, and either 8K mode ($00 in bits 7, 6, 2 and 1)
|
|
8
|
+
# or no ROM ($86 in those bits).
|
|
9
|
+
class FunPlay < Cartridge
|
|
10
|
+
def poke(addr, value)
|
|
11
|
+
return if addr > 0xdeff
|
|
12
|
+
|
|
13
|
+
@roml = bank(@banks, self.class.bank_number(value))
|
|
14
|
+
case value & 0xc6
|
|
15
|
+
when 0x00 then self.mode = :rom8k
|
|
16
|
+
when 0x86 then self.mode = :off
|
|
17
|
+
end
|
|
18
|
+
changed!
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The CRT file numbers each bank by the register value that selects it.
|
|
22
|
+
def reset
|
|
23
|
+
poke(0xde00, 0)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.bank_number(value)
|
|
27
|
+
((value >> 3) & 0x07) | ((value & 0x01) << 3)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def install_chips(chips)
|
|
33
|
+
@banks = []
|
|
34
|
+
chips.each { |chip| @banks[self.class.bank_number(chip.bank)] = rom_bank(chip.data) }
|
|
35
|
+
reset
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class C64
|
|
5
|
+
class Cartridge
|
|
6
|
+
# GMod2: an Am29F040 flash chip of 64 8K banks at ROML. A write to I/O 1
|
|
7
|
+
# selects the bank from bits 0-5. Bit 6 selects the EEPROM, which
|
|
8
|
+
# switches the ROM out, and bits 6 and 7 together enable flash writes:
|
|
9
|
+
# the cartridge then asserts Ultimax on write cycles only, so reads see
|
|
10
|
+
# the C64's memory and writes to $E000-$FFFF reach the flash. The EEPROM
|
|
11
|
+
# isn't emulated.
|
|
12
|
+
class GMod2 < Cartridge
|
|
13
|
+
BANKS = 64
|
|
14
|
+
|
|
15
|
+
attr_reader :flash
|
|
16
|
+
|
|
17
|
+
def clock=(clock)
|
|
18
|
+
super
|
|
19
|
+
@flash.clock = clock
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def poke(addr, value)
|
|
23
|
+
return if addr > 0xdeff
|
|
24
|
+
|
|
25
|
+
@bank = value & 0x3f
|
|
26
|
+
@flash_writes = value.allbits?(0xc0)
|
|
27
|
+
self.mode = value.anybits?(0x40) ? :off : :rom8k
|
|
28
|
+
select_bank
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def romh_writes
|
|
32
|
+
@roml if @flash_writes
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Reset selects the first bank in 8K mode, and leaves the flash as it
|
|
36
|
+
# is.
|
|
37
|
+
def reset
|
|
38
|
+
@bank = 0
|
|
39
|
+
@flash_writes = false
|
|
40
|
+
self.mode = :rom8k
|
|
41
|
+
select_bank
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def select_bank
|
|
47
|
+
@roml = @flash.window(@bank * BANK_SIZE)
|
|
48
|
+
changed!
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def install_chips(chips)
|
|
52
|
+
data = Array.new(BANKS * BANK_SIZE, 0xff)
|
|
53
|
+
chips.each do |chip|
|
|
54
|
+
bytes = chip.data.first(BANK_SIZE)
|
|
55
|
+
data[(chip.bank & 0x3f) * BANK_SIZE, bytes.length] = bytes
|
|
56
|
+
end
|
|
57
|
+
@flash = Flash.new(data, model: Flash::AM29F040)
|
|
58
|
+
@flash.on_change { select_bank }
|
|
59
|
+
reset
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|