badline 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/CLAUDE.md +59 -31
- data/README.md +1 -1
- data/doc/library.md +9 -9
- data/doc/pinned-behaviour.md +105 -105
- data/doc/snapshots.md +1 -4
- data/lib/badline/audio/bare_player.rb +4 -4
- data/lib/badline/audio/cli.rb +6 -6
- data/lib/badline/audio/jukebox.rb +2 -2
- data/lib/badline/audio/machine_player.rb +2 -2
- data/lib/badline/{media → audio}/queue.rb +1 -1
- data/lib/badline/audio/queued_tune.rb +6 -6
- data/lib/badline/audio/renderer.rb +5 -5
- data/lib/badline/audio/stereo.rb +1 -1
- data/lib/badline/c128/{bus_state.rb → bus/saved_state.rb} +6 -6
- data/lib/badline/c128/bus.rb +30 -51
- data/lib/badline/c128/color_lines.rb +2 -2
- data/lib/badline/c128/cpu.rb +1 -1
- data/lib/badline/c128/drives.rb +10 -11
- data/lib/badline/c128/fast_serial.rb +5 -5
- data/lib/badline/c128/mmu_pages.rb +1 -1
- data/lib/badline/c128/modes.rb +2 -2
- data/lib/badline/c128/saved_state.rb +21 -47
- data/lib/badline/c128/z80.rb +2 -2
- data/lib/badline/c128.rb +18 -72
- data/lib/badline/{computer → c64}/attachments.rb +16 -47
- data/lib/badline/c64/banked_ram/banking.rb +32 -0
- data/lib/badline/c64/banked_ram/plus256k.rb +56 -0
- data/lib/badline/c64/banked_ram/plus60k.rb +45 -0
- data/lib/badline/c64/banked_ram/unexpanded.rb +30 -0
- data/lib/badline/c64/banked_ram.rb +23 -0
- data/lib/badline/c64/bus/extra_sids.rb +32 -0
- data/lib/badline/c64/bus/fittings.rb +40 -0
- data/lib/badline/c64/bus/pla.rb +149 -0
- data/lib/badline/c64/bus/roms.rb +41 -0
- data/lib/badline/c64/bus/saved_state.rb +68 -0
- data/lib/badline/c64/bus/sid_slots.rb +44 -0
- data/lib/badline/c64/bus.rb +218 -0
- data/lib/badline/c64/cartridge/action_replay.rb +128 -0
- data/lib/badline/c64/cartridge/atomic_power.rb +26 -0
- data/lib/badline/c64/cartridge/bank.rb +83 -0
- data/lib/badline/c64/cartridge/comal80.rb +52 -0
- data/lib/badline/c64/cartridge/dinamic.rb +36 -0
- data/lib/badline/c64/cartridge/easy_flash.rb +147 -0
- data/lib/badline/c64/cartridge/epyx_fastload.rb +81 -0
- data/lib/badline/c64/cartridge/final_cartridge3.rb +91 -0
- data/lib/badline/c64/cartridge/flash/saved_state.rb +40 -0
- data/lib/badline/c64/cartridge/flash.rb +279 -0
- data/lib/badline/c64/cartridge/freezer.rb +35 -0
- data/lib/badline/c64/cartridge/fun_play.rb +40 -0
- data/lib/badline/c64/cartridge/g_mod2.rb +76 -0
- data/lib/badline/c64/cartridge/game_system.rb +42 -0
- data/lib/badline/c64/cartridge/geo_ram.rb +81 -0
- data/lib/badline/c64/cartridge/kcs_power.rb +77 -0
- data/lib/badline/c64/cartridge/mach5.rb +34 -0
- data/lib/badline/c64/cartridge/magic_desk.rb +32 -0
- data/lib/badline/c64/cartridge/ocean.rb +37 -0
- data/lib/badline/c64/cartridge/pagefox.rb +70 -0
- data/lib/badline/c64/cartridge/retro_replay/saved_state.rb +47 -0
- data/lib/badline/c64/cartridge/retro_replay.rb +286 -0
- data/lib/badline/c64/cartridge/rex_utility.rb +31 -0
- data/lib/badline/c64/cartridge/rgcd.rb +60 -0
- data/lib/badline/c64/cartridge/saved_state.rb +63 -0
- data/lib/badline/c64/cartridge/simons_basic.rb +37 -0
- data/lib/badline/c64/cartridge/standard.rb +17 -0
- data/lib/badline/c64/cartridge/super_games.rb +50 -0
- data/lib/badline/c64/cartridge/westermann.rb +32 -0
- data/lib/badline/c64/cartridge/zaxxon.rb +68 -0
- data/lib/badline/c64/cartridge.rb +258 -0
- data/lib/badline/c64/color_memory.rb +27 -0
- data/lib/badline/c64/common.rb +34 -0
- data/lib/badline/c64/kernal_traps.rb +89 -0
- data/lib/badline/c64/reu/dma/saved_state.rb +52 -0
- data/lib/badline/c64/reu/dma.rb +275 -0
- data/lib/badline/c64/reu/ram.rb +109 -0
- data/lib/badline/c64/reu/saved_state.rb +55 -0
- data/lib/badline/c64/reu/trigger.rb +34 -0
- data/lib/badline/c64/reu.rb +291 -0
- data/lib/badline/{computer → c64}/saved_state.rb +13 -18
- data/lib/badline/{computer → c64}/true_drives.rb +20 -31
- data/lib/badline/{computer.rb → c64.rb} +22 -80
- data/lib/badline/chips/cia/fast_forward.rb +62 -0
- data/lib/badline/chips/cia/interrupt_register.rb +145 -0
- data/lib/badline/chips/cia/saved_state.rb +49 -0
- data/lib/badline/chips/cia/serial/saved_state.rb +45 -0
- data/lib/badline/chips/cia/serial.rb +250 -0
- data/lib/badline/chips/cia/time_of_day.rb +232 -0
- data/lib/badline/chips/cia/timer.rb +208 -0
- data/lib/badline/chips/cia.rb +281 -0
- data/lib/badline/chips/cpu/addressing.rb +133 -0
- data/lib/badline/chips/cpu/boundary.rb +56 -0
- data/lib/badline/chips/cpu/core.rb +167 -0
- data/lib/badline/chips/cpu/instruction.rb +338 -0
- data/lib/badline/chips/cpu/instruction_set/arithmetic.rb +118 -0
- data/lib/badline/chips/cpu/instruction_set/bitwise.rb +130 -0
- data/lib/badline/chips/cpu/instruction_set/branch.rb +83 -0
- data/lib/badline/chips/cpu/instruction_set/flag.rb +67 -0
- data/lib/badline/chips/cpu/instruction_set/illegal.rb +274 -0
- data/lib/badline/chips/cpu/instruction_set/inc_dec.rb +73 -0
- data/lib/badline/chips/cpu/instruction_set/stack.rb +56 -0
- data/lib/badline/chips/cpu/instruction_set/transfer.rb +141 -0
- data/lib/badline/chips/cpu/instruction_set.rb +59 -0
- data/lib/badline/chips/cpu/interrupts.rb +108 -0
- data/lib/badline/chips/cpu/microcode.rb +179 -0
- data/lib/badline/chips/cpu/operations.rb +90 -0
- data/lib/badline/chips/cpu/port.rb +116 -0
- data/lib/badline/chips/cpu/saved_state.rb +117 -0
- data/lib/badline/chips/cpu/stack_operations.rb +84 -0
- data/lib/badline/chips/cpu/traps.rb +23 -0
- data/lib/badline/chips/cpu.rb +12 -0
- data/lib/badline/chips/sid/catch_up.rb +122 -0
- data/lib/badline/chips/sid/decimator.rb +66 -0
- data/lib/badline/chips/sid/envelope/saved_state.rb +70 -0
- data/lib/badline/chips/sid/envelope.rb +241 -0
- data/lib/badline/chips/sid/filter/saved_state.rb +42 -0
- data/lib/badline/chips/sid/filter.rb +241 -0
- data/lib/badline/chips/sid/saved_state.rb +52 -0
- data/lib/badline/chips/sid/voice.rb +79 -0
- data/lib/badline/chips/sid/waveform/combined.rb +128 -0
- data/lib/badline/chips/sid/waveform/fast_forward.rb +107 -0
- data/lib/badline/chips/sid/waveform/noise_writeback.rb +124 -0
- data/lib/badline/chips/sid/waveform/saved_state.rb +68 -0
- data/lib/badline/chips/sid/waveform.rb +307 -0
- data/lib/badline/chips/sid.rb +265 -0
- data/lib/badline/{c128 → chips}/vdc/memory.rb +1 -1
- data/lib/badline/{c128 → chips}/vdc/painter.rb +1 -1
- data/lib/badline/{c128 → chips}/vdc/raster.rb +1 -1
- data/lib/badline/chips/vdc/saved_state.rb +36 -0
- data/lib/badline/{c128 → chips}/vdc/window.rb +1 -1
- data/lib/badline/{c128 → chips}/vdc.rb +7 -31
- data/lib/badline/chips/via/control_lines.rb +124 -0
- data/lib/badline/chips/via/fast_forward.rb +55 -0
- data/lib/badline/chips/via/interrupt_register.rb +69 -0
- data/lib/badline/chips/via/saved_state.rb +44 -0
- data/lib/badline/chips/via/shift_register.rb +182 -0
- data/lib/badline/chips/via/timer1.rb +133 -0
- data/lib/badline/chips/via/timer2.rb +135 -0
- data/lib/badline/chips/via.rb +268 -0
- data/lib/badline/chips/vic/bank.rb +135 -0
- data/lib/badline/chips/vic/border_mask.rb +88 -0
- data/lib/badline/chips/vic/collisions.rb +117 -0
- data/lib/badline/chips/vic/color_patches.rb +49 -0
- data/lib/badline/chips/vic/display_state.rb +187 -0
- data/lib/badline/chips/vic/graphics_mode.rb +166 -0
- data/lib/badline/chips/vic/graphics_shifter/saved_state.rb +40 -0
- data/lib/badline/chips/vic/graphics_shifter.rb +187 -0
- data/lib/badline/chips/vic/register_log.rb +83 -0
- data/lib/badline/chips/vic/registers.rb +197 -0
- data/lib/badline/chips/vic/saved_state.rb +82 -0
- data/lib/badline/chips/vic/sequencer/saved_state.rb +91 -0
- data/lib/badline/chips/vic/sequencer.rb +277 -0
- data/lib/badline/chips/vic/sequencer_output.rb +118 -0
- data/lib/badline/chips/vic/sprite/internal_bus.rb +70 -0
- data/lib/badline/chips/vic/sprite/saved_state.rb +66 -0
- data/lib/badline/chips/vic/sprite/shifter.rb +148 -0
- data/lib/badline/chips/vic/sprite/timing.rb +58 -0
- data/lib/badline/chips/vic/sprite.rb +339 -0
- data/lib/badline/chips/vic/sprites/saved_state.rb +59 -0
- data/lib/badline/chips/vic/sprites.rb +284 -0
- data/lib/badline/chips/vic.rb +783 -0
- data/lib/badline/chips/vic_i/painter/saved_state.rb +40 -0
- data/lib/badline/{vic20/vic → chips/vic_i}/painter.rb +6 -30
- data/lib/badline/{vic20/vic → chips/vic_i}/saved_state.rb +2 -2
- data/lib/badline/chips/vic_i/sound/output.rb +85 -0
- data/lib/badline/chips/vic_i/sound.rb +235 -0
- data/lib/badline/{vic20/vic → chips/vic_i}/text_window.rb +2 -2
- data/lib/badline/{vic20/vic.rb → chips/vic_i.rb} +5 -5
- data/lib/badline/chips/z80/arithmetic.rb +119 -0
- data/lib/badline/chips/z80/bit_page.rb +57 -0
- data/lib/badline/chips/z80/block_io.rb +60 -0
- data/lib/badline/chips/z80/block_transfers.rb +61 -0
- data/lib/badline/chips/z80/bus_cycles.rb +113 -0
- data/lib/badline/chips/z80/core.rb +115 -0
- data/lib/badline/chips/z80/exchanges.rb +52 -0
- data/lib/badline/chips/z80/extended_page.rb +91 -0
- data/lib/badline/chips/z80/flags.rb +39 -0
- data/lib/badline/chips/z80/interrupt_acceptance.rb +48 -0
- data/lib/badline/chips/z80/jumps.rb +84 -0
- data/lib/badline/chips/z80/loads.rb +100 -0
- data/lib/badline/chips/z80/main_page.rb +136 -0
- data/lib/badline/chips/z80/registers.rb +122 -0
- data/lib/badline/chips/z80/saved_state.rb +71 -0
- data/lib/badline/chips/z80/shifts.rb +66 -0
- data/lib/badline/chips/z80.rb +13 -0
- data/lib/badline/core.rb +27 -29
- data/lib/badline/drives/datasette/saved_state.rb +37 -0
- data/lib/badline/drives/datasette.rb +140 -0
- data/lib/badline/drives/engine/core.rb +149 -0
- data/lib/badline/drives/engine/fast_serial.rb +75 -0
- data/lib/badline/drives/engine/idle.rb +127 -0
- data/lib/badline/drives/engine/idle_cpu.rb +30 -0
- data/lib/badline/drives/engine/orbit.rb +81 -0
- data/lib/badline/drives/engine/serial_lines.rb +75 -0
- data/lib/badline/drives/engine/sleep.rb +114 -0
- data/lib/badline/drives/engine/vias.rb +86 -0
- data/lib/badline/drives/engine/watch.rb +152 -0
- data/lib/badline/drives/engine.rb +11 -0
- data/lib/badline/drives/model1541/bus.rb +88 -0
- data/lib/badline/drives/model1541/byte_ready.rb +50 -0
- data/lib/badline/drives/model1541/cpu.rb +13 -0
- data/lib/badline/drives/model1541/disk/saved_state.rb +88 -0
- data/lib/badline/drives/model1541/disk.rb +317 -0
- data/lib/badline/drives/model1541/disk_via.rb +38 -0
- data/lib/badline/drives/model1541/gcr.rb +59 -0
- data/lib/badline/drives/model1541/mechanism/saved_state.rb +72 -0
- data/lib/badline/drives/model1541/mechanism.rb +315 -0
- data/lib/badline/drives/model1541/rotation.rb +123 -0
- data/lib/badline/drives/model1541/saved_state.rb +44 -0
- data/lib/badline/drives/model1541/sector_reader.rb +78 -0
- data/lib/badline/drives/model1541/serial_port.rb +20 -0
- data/lib/badline/drives/model1541/track.rb +100 -0
- data/lib/badline/drives/model1541.rb +81 -0
- data/lib/badline/drives/model1571/bus.rb +113 -0
- data/lib/badline/drives/model1571/cpu.rb +13 -0
- data/lib/badline/drives/model1571/saved_state.rb +47 -0
- data/lib/badline/drives/model1571/serial_port.rb +39 -0
- data/lib/badline/drives/model1571/serial_via.rb +27 -0
- data/lib/badline/drives/model1571/wd1770.rb +122 -0
- data/lib/badline/drives/model1571.rb +142 -0
- data/lib/badline/drives/model1581/bus.rb +139 -0
- data/lib/badline/drives/model1581/cpu.rb +13 -0
- data/lib/badline/drives/model1581/disk/saved_state.rb +89 -0
- data/lib/badline/drives/model1581/disk.rb +137 -0
- data/lib/badline/drives/model1581/mechanism.rb +119 -0
- data/lib/badline/drives/model1581/saved_state.rb +42 -0
- data/lib/badline/drives/model1581/serial_port.rb +54 -0
- data/lib/badline/drives/model1581/slot.rb +79 -0
- data/lib/badline/drives/model1581/track.rb +102 -0
- data/lib/badline/drives/model1581/track_bytes.rb +77 -0
- data/lib/badline/drives/model1581/wd1772/registers.rb +91 -0
- data/lib/badline/drives/model1581/wd1772/saved_state.rb +68 -0
- data/lib/badline/drives/model1581/wd1772/sector_writes.rb +75 -0
- data/lib/badline/drives/model1581/wd1772/sectors.rb +152 -0
- data/lib/badline/drives/model1581/wd1772/stepping.rb +111 -0
- data/lib/badline/drives/model1581/wd1772/tracks.rb +127 -0
- data/lib/badline/drives/model1581/wd1772/turn.rb +75 -0
- data/lib/badline/drives/model1581/wd1772.rb +218 -0
- data/lib/badline/drives/model1581.rb +206 -0
- data/lib/badline/drives/serial_bus.rb +161 -0
- data/lib/badline/frontend/boot.rb +10 -61
- data/lib/badline/frontend/machine_window.rb +3 -1
- data/lib/badline/frontend/player/filter_view.rb +1 -1
- data/lib/badline/frontend/player/player_controller.rb +2 -2
- data/lib/badline/frontend/player/player_screen.rb +1 -0
- data/lib/badline/frontend/player/theme.rb +7 -7
- data/lib/badline/frontend/timeline.rb +3 -3
- data/lib/badline/input/control_ports.rb +78 -0
- data/lib/badline/input/joystick.rb +27 -0
- data/lib/badline/input/keyboard.rb +114 -0
- data/lib/badline/input.rb +3 -0
- data/lib/badline/kernal_trap/{drive → dos}/block_commands.rb +1 -1
- data/lib/badline/kernal_trap/dos/channel.rb +183 -0
- data/lib/badline/kernal_trap/{drive → dos}/channels.rb +1 -1
- data/lib/badline/kernal_trap/{drive → dos}/memory.rb +1 -1
- data/lib/badline/kernal_trap/{drive → dos}/parameters.rb +1 -1
- data/lib/badline/kernal_trap/{drive → dos}/status.rb +1 -1
- data/lib/badline/kernal_trap/{drive → dos}/write_file.rb +1 -1
- data/lib/badline/kernal_trap/{drive → dos}/writes.rb +1 -1
- data/lib/badline/kernal_trap/{drive.rb → dos.rb} +11 -10
- data/lib/badline/kernal_trap/{file.rb → file_routine.rb} +1 -1
- data/lib/badline/kernal_trap/load.rb +6 -6
- data/lib/badline/kernal_trap/save.rb +5 -5
- data/lib/badline/kernal_trap/serial.rb +7 -7
- data/lib/badline/kernal_trap.rb +2 -3
- data/lib/badline/machine/clocking.rb +50 -0
- data/lib/badline/machine.rb +63 -7
- data/lib/badline/media/boot_disk.rb +2 -2
- data/lib/badline/media/disk_list.rb +3 -3
- data/lib/badline/media/disk_set.rb +1 -1
- data/lib/badline/media/extensions.rb +33 -0
- data/lib/badline/media/program.rb +28 -0
- data/lib/badline/media/true_drive.rb +4 -3
- data/lib/badline/media/tune.rb +35 -0
- data/lib/badline/media/vic20_cartridge.rb +1 -7
- data/lib/badline/media/vic20_media.rb +38 -80
- data/lib/badline/media.rb +84 -81
- data/lib/badline/model.rb +1 -1
- data/lib/badline/options/event.rb +3 -1
- data/lib/badline/roms/README +6 -6
- data/lib/badline/sdl.rb +1 -0
- data/lib/badline/{storage/sid_file.rb → sid_tune/file.rb} +3 -3
- data/lib/badline/{storage → sid_tune}/hvsc.rb +7 -7
- data/lib/badline/{storage → sid_tune}/song_lengths.rb +2 -2
- data/lib/badline/{storage → sid_tune}/stil/credit.rb +1 -1
- data/lib/badline/{storage → sid_tune}/stil.rb +3 -3
- data/lib/badline/sid_tune.rb +6 -0
- data/lib/badline/snapshot/c128_setup.rb +43 -0
- data/lib/badline/snapshot/c64_setup.rb +79 -0
- data/lib/badline/snapshot/image.rb +5 -25
- data/lib/badline/snapshot/machine_state.rb +10 -2
- data/lib/badline/snapshot/setup.rb +22 -66
- data/lib/badline/snapshot/state.rb +8 -17
- data/lib/badline/snapshot/storage_setup.rb +68 -0
- data/lib/badline/snapshot/vic20_setup.rb +37 -0
- data/lib/badline/snapshot/vice/c64_mem.rb +1 -1
- data/lib/badline/snapshot/vice/restore.rb +1 -1
- data/lib/badline/snapshot/vice/reu1764.rb +1 -1
- data/lib/badline/snapshot/vice/reu_fields.rb +40 -0
- data/lib/badline/snapshot/vice.rb +7 -5
- data/lib/badline/snapshot.rb +13 -12
- data/lib/badline/storage/d64_image.rb +15 -9
- data/lib/badline/storage/d71_image.rb +0 -2
- data/lib/badline/storage/d81_image.rb +0 -2
- data/lib/badline/storage/disk_image/writing.rb +2 -2
- data/lib/badline/storage/disk_image.rb +3 -6
- data/lib/badline/storage/g64_image.rb +2 -2
- data/lib/badline/storage/host_directory.rb +0 -5
- data/lib/badline/storage/t64.rb +2 -8
- data/lib/badline/storage/unavailable.rb +1 -1
- data/lib/badline/storage.rb +0 -37
- data/lib/badline/support/c128_checkpoint.rb +39 -0
- data/lib/badline/support/checkpoint.rb +108 -0
- data/lib/badline/support/vic20_checkpoint.rb +38 -0
- data/lib/badline/support.rb +5 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic20/attachments.rb +11 -81
- data/lib/badline/vic20/bus/saved_state.rb +35 -0
- data/lib/badline/vic20/bus.rb +3 -25
- data/lib/badline/vic20/cpu.rb +1 -1
- data/lib/badline/vic20/port_wiring.rb +4 -4
- data/lib/badline/vic20/saved_state.rb +16 -60
- data/lib/badline/vic20/user_via_ports.rb +2 -2
- data/lib/badline/vic20.rb +20 -51
- metadata +250 -221
- data/lib/badline/address_bus/extra_sids.rb +0 -30
- data/lib/badline/address_bus/fittings.rb +0 -38
- data/lib/badline/address_bus/pla.rb +0 -147
- data/lib/badline/address_bus/roms.rb +0 -39
- data/lib/badline/address_bus/saved_state.rb +0 -72
- data/lib/badline/address_bus/sid_slots.rb +0 -42
- data/lib/badline/address_bus.rb +0 -235
- data/lib/badline/cartridge/action_replay.rb +0 -126
- data/lib/badline/cartridge/atomic_power.rb +0 -24
- data/lib/badline/cartridge/bank.rb +0 -81
- data/lib/badline/cartridge/comal80.rb +0 -50
- data/lib/badline/cartridge/dinamic.rb +0 -34
- data/lib/badline/cartridge/easy_flash.rb +0 -145
- data/lib/badline/cartridge/epyx_fastload.rb +0 -79
- data/lib/badline/cartridge/final_cartridge3.rb +0 -89
- data/lib/badline/cartridge/flash.rb +0 -306
- data/lib/badline/cartridge/freezer.rb +0 -33
- data/lib/badline/cartridge/fun_play.rb +0 -38
- data/lib/badline/cartridge/g_mod2.rb +0 -74
- data/lib/badline/cartridge/game_system.rb +0 -40
- data/lib/badline/cartridge/geo_ram.rb +0 -79
- data/lib/badline/cartridge/kcs_power.rb +0 -75
- data/lib/badline/cartridge/mach5.rb +0 -32
- data/lib/badline/cartridge/magic_desk.rb +0 -30
- data/lib/badline/cartridge/ocean.rb +0 -35
- data/lib/badline/cartridge/pagefox.rb +0 -68
- data/lib/badline/cartridge/retro_replay.rb +0 -320
- data/lib/badline/cartridge/rex_utility.rb +0 -29
- data/lib/badline/cartridge/rgcd.rb +0 -58
- data/lib/badline/cartridge/simons_basic.rb +0 -35
- data/lib/badline/cartridge/standard.rb +0 -15
- data/lib/badline/cartridge/super_games.rb +0 -48
- data/lib/badline/cartridge/westermann.rb +0 -30
- data/lib/badline/cartridge/zaxxon.rb +0 -66
- data/lib/badline/cartridge.rb +0 -303
- data/lib/badline/checkpoint.rb +0 -98
- data/lib/badline/cia/fast_forward.rb +0 -60
- data/lib/badline/cia/interrupt_register.rb +0 -143
- data/lib/badline/cia/serial.rb +0 -275
- data/lib/badline/cia/state.rb +0 -47
- data/lib/badline/cia/timer.rb +0 -206
- data/lib/badline/cia.rb +0 -279
- data/lib/badline/color_memory.rb +0 -25
- data/lib/badline/computer/kernal_traps.rb +0 -29
- data/lib/badline/control_ports.rb +0 -76
- data/lib/badline/cpu/addressing.rb +0 -131
- data/lib/badline/cpu/boundary.rb +0 -54
- data/lib/badline/cpu/core.rb +0 -165
- data/lib/badline/cpu/microcode.rb +0 -177
- data/lib/badline/cpu/operations.rb +0 -88
- data/lib/badline/cpu/saved_state.rb +0 -115
- data/lib/badline/cpu/stack_operations.rb +0 -82
- data/lib/badline/cpu.rb +0 -10
- data/lib/badline/cycleable.rb +0 -38
- data/lib/badline/datasette.rb +0 -161
- data/lib/badline/drive/core.rb +0 -147
- data/lib/badline/drive/fast_serial.rb +0 -73
- data/lib/badline/drive/idle.rb +0 -125
- data/lib/badline/drive/idle_cpu.rb +0 -28
- data/lib/badline/drive/orbit.rb +0 -79
- data/lib/badline/drive/serial_lines.rb +0 -73
- data/lib/badline/drive/sleep.rb +0 -112
- data/lib/badline/drive/vias.rb +0 -84
- data/lib/badline/drive/watch.rb +0 -150
- data/lib/badline/drive.rb +0 -11
- data/lib/badline/drive1541/bus.rb +0 -86
- data/lib/badline/drive1541/byte_ready.rb +0 -48
- data/lib/badline/drive1541/cpu.rb +0 -11
- data/lib/badline/drive1541/disk.rb +0 -318
- data/lib/badline/drive1541/disk_state.rb +0 -94
- data/lib/badline/drive1541/disk_via.rb +0 -36
- data/lib/badline/drive1541/gcr.rb +0 -57
- data/lib/badline/drive1541/mechanism.rb +0 -313
- data/lib/badline/drive1541/mechanism_state.rb +0 -68
- data/lib/badline/drive1541/rotation.rb +0 -121
- data/lib/badline/drive1541/sector_reader.rb +0 -76
- data/lib/badline/drive1541/serial_port.rb +0 -18
- data/lib/badline/drive1541/track.rb +0 -98
- data/lib/badline/drive1541.rb +0 -111
- data/lib/badline/drive1571/bus.rb +0 -111
- data/lib/badline/drive1571/cpu.rb +0 -11
- data/lib/badline/drive1571/serial_port.rb +0 -37
- data/lib/badline/drive1571/serial_via.rb +0 -25
- data/lib/badline/drive1571/wd1770.rb +0 -120
- data/lib/badline/drive1571.rb +0 -175
- data/lib/badline/drive1581/bus.rb +0 -137
- data/lib/badline/drive1581/cpu.rb +0 -11
- data/lib/badline/drive1581/disk.rb +0 -135
- data/lib/badline/drive1581/disk_state.rb +0 -87
- data/lib/badline/drive1581/mechanism.rb +0 -117
- data/lib/badline/drive1581/serial_port.rb +0 -52
- data/lib/badline/drive1581/slot.rb +0 -78
- data/lib/badline/drive1581/track.rb +0 -100
- data/lib/badline/drive1581/track_bytes.rb +0 -75
- data/lib/badline/drive1581/wd1772/registers.rb +0 -89
- data/lib/badline/drive1581/wd1772/sector_writes.rb +0 -73
- data/lib/badline/drive1581/wd1772/sectors.rb +0 -150
- data/lib/badline/drive1581/wd1772/state.rb +0 -66
- data/lib/badline/drive1581/wd1772/stepping.rb +0 -109
- data/lib/badline/drive1581/wd1772/tracks.rb +0 -125
- data/lib/badline/drive1581/wd1772/turn.rb +0 -73
- data/lib/badline/drive1581/wd1772.rb +0 -216
- data/lib/badline/drive1581.rb +0 -233
- data/lib/badline/iec_bus.rb +0 -159
- data/lib/badline/instruction.rb +0 -334
- data/lib/badline/instruction_set/arithmetic.rb +0 -114
- data/lib/badline/instruction_set/bitwise.rb +0 -126
- data/lib/badline/instruction_set/branch.rb +0 -79
- data/lib/badline/instruction_set/flag.rb +0 -63
- data/lib/badline/instruction_set/illegal.rb +0 -270
- data/lib/badline/instruction_set/inc_dec.rb +0 -69
- data/lib/badline/instruction_set/stack.rb +0 -52
- data/lib/badline/instruction_set/transfer.rb +0 -137
- data/lib/badline/instruction_set.rb +0 -55
- data/lib/badline/interrupts.rb +0 -104
- data/lib/badline/joystick.rb +0 -25
- data/lib/badline/kernal_trap/channel.rb +0 -181
- data/lib/badline/keyboard.rb +0 -112
- data/lib/badline/ram_expansion/banking.rb +0 -30
- data/lib/badline/ram_expansion/plus256k.rb +0 -54
- data/lib/badline/ram_expansion/plus60k.rb +0 -43
- data/lib/badline/ram_expansion/unexpanded.rb +0 -28
- data/lib/badline/ram_expansion.rb +0 -21
- data/lib/badline/reu/dma/saved_state.rb +0 -50
- data/lib/badline/reu/dma.rb +0 -273
- data/lib/badline/reu/ram.rb +0 -107
- data/lib/badline/reu/saved_state.rb +0 -80
- data/lib/badline/reu/trigger.rb +0 -32
- data/lib/badline/reu.rb +0 -289
- data/lib/badline/sid/catch_up.rb +0 -120
- data/lib/badline/sid/decimator.rb +0 -64
- data/lib/badline/sid/envelope/state.rb +0 -68
- data/lib/badline/sid/envelope.rb +0 -239
- data/lib/badline/sid/filter.rb +0 -263
- data/lib/badline/sid/voice.rb +0 -77
- data/lib/badline/sid/waveform/combined.rb +0 -126
- data/lib/badline/sid/waveform/fast_forward.rb +0 -105
- data/lib/badline/sid/waveform/noise_writeback.rb +0 -122
- data/lib/badline/sid/waveform/state.rb +0 -66
- data/lib/badline/sid/waveform.rb +0 -305
- data/lib/badline/sid.rb +0 -303
- data/lib/badline/time_of_day.rb +0 -228
- data/lib/badline/traps.rb +0 -19
- data/lib/badline/via/control_lines.rb +0 -122
- data/lib/badline/via/fast_forward.rb +0 -53
- data/lib/badline/via/interrupt_register.rb +0 -67
- data/lib/badline/via/shift_register.rb +0 -180
- data/lib/badline/via/state.rb +0 -42
- data/lib/badline/via/timer1.rb +0 -131
- data/lib/badline/via/timer2.rb +0 -133
- data/lib/badline/via.rb +0 -266
- data/lib/badline/vic/bank.rb +0 -133
- data/lib/badline/vic/border_mask.rb +0 -86
- data/lib/badline/vic/collisions.rb +0 -115
- data/lib/badline/vic/color_patches.rb +0 -47
- data/lib/badline/vic/display_state.rb +0 -185
- data/lib/badline/vic/graphics_mode.rb +0 -164
- data/lib/badline/vic/graphics_shifter.rb +0 -207
- data/lib/badline/vic/register_log.rb +0 -81
- data/lib/badline/vic/registers.rb +0 -195
- data/lib/badline/vic/sequencer.rb +0 -275
- data/lib/badline/vic/sequencer_output.rb +0 -116
- data/lib/badline/vic/sequencer_state.rb +0 -89
- data/lib/badline/vic/sprite/internal_bus.rb +0 -68
- data/lib/badline/vic/sprite/saved_state.rb +0 -64
- data/lib/badline/vic/sprite/shifter.rb +0 -146
- data/lib/badline/vic/sprite/timing.rb +0 -56
- data/lib/badline/vic/sprite.rb +0 -337
- data/lib/badline/vic/sprites.rb +0 -282
- data/lib/badline/vic/sprites_state.rb +0 -57
- data/lib/badline/vic.rb +0 -852
- data/lib/badline/vic20/sound/output.rb +0 -83
- data/lib/badline/vic20/sound.rb +0 -233
- data/lib/badline/z80/arithmetic.rb +0 -117
- data/lib/badline/z80/bit_page.rb +0 -55
- data/lib/badline/z80/block_io.rb +0 -58
- data/lib/badline/z80/block_transfers.rb +0 -59
- data/lib/badline/z80/bus_cycles.rb +0 -111
- data/lib/badline/z80/core.rb +0 -113
- data/lib/badline/z80/exchanges.rb +0 -50
- data/lib/badline/z80/extended_page.rb +0 -89
- data/lib/badline/z80/flags.rb +0 -37
- data/lib/badline/z80/interrupt_acceptance.rb +0 -46
- data/lib/badline/z80/jumps.rb +0 -82
- data/lib/badline/z80/loads.rb +0 -98
- data/lib/badline/z80/main_page.rb +0 -134
- data/lib/badline/z80/registers.rb +0 -120
- data/lib/badline/z80/saved_state.rb +0 -69
- data/lib/badline/z80/shifts.rb +0 -64
- data/lib/badline/z80.rb +0 -11
- /data/lib/badline/snapshot/{fields.rb → vice/fields.rb} +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/chips/sid/filter/saved_state"
|
|
4
|
+
|
|
5
|
+
module Badline
|
|
6
|
+
module Chips
|
|
7
|
+
class SID
|
|
8
|
+
# The SID's multimode filter: a state-variable design integrating low-,
|
|
9
|
+
# band- and high-pass in parallel, with the mixer and master volume on
|
|
10
|
+
# its output, and the C64 board's own RC network after that.
|
|
11
|
+
#
|
|
12
|
+
# Voices are routed through it or around it per the RES/FILT register,
|
|
13
|
+
# and the outputs selected by MODE/VOL are summed back together with the
|
|
14
|
+
# unfiltered ones.
|
|
15
|
+
class Filter
|
|
16
|
+
include SavedState
|
|
17
|
+
|
|
18
|
+
# Cutoff frequency in Hz at each chip's breakpoints. The 6581 curve was
|
|
19
|
+
# sampled from a real chip by reSID and bends sharply around $300; the
|
|
20
|
+
# 8580's is close to a straight line from 0 to 12.5 kHz. The curve
|
|
21
|
+
# between breakpoints is interpolated linearly; reSID fits a spline, so
|
|
22
|
+
# the two differ by a few Hz mid-segment.
|
|
23
|
+
CUTOFF_POINTS = {
|
|
24
|
+
mos6581: [[0, 220], [128, 230], [256, 250], [384, 300],
|
|
25
|
+
[512, 420], [640, 780], [768, 1600], [832, 2300],
|
|
26
|
+
[896, 3200], [960, 4300], [1024, 5000], [1088, 5400],
|
|
27
|
+
[1152, 5800], [1216, 6000], [1280, 6200], [1344, 6400],
|
|
28
|
+
[1408, 6600], [1472, 6800], [1536, 7000], [1600, 7200],
|
|
29
|
+
[1664, 7400], [1728, 7600], [1792, 7800], [1856, 8000],
|
|
30
|
+
[1920, 8200], [1984, 8400], [2047, 8600]].freeze,
|
|
31
|
+
mos8580: [[0, 0], [128, 800], [256, 1600], [384, 2500],
|
|
32
|
+
[512, 3300], [640, 4100], [768, 4800], [896, 5600],
|
|
33
|
+
[1024, 6500], [1152, 7500], [1280, 8400], [1408, 9200],
|
|
34
|
+
[1536, 9800], [1664, 10_500], [1792, 11_000],
|
|
35
|
+
[1920, 11_700], [2047, 12_500]].freeze
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
# 2^20 / 1 MHz, so one cycle of integration is a plain shift.
|
|
39
|
+
SCALE = (2**20) / 1_000_000.0
|
|
40
|
+
|
|
41
|
+
# The integrator goes unstable above ~16 kHz at a one-cycle step.
|
|
42
|
+
W0_MAX = (2 * Math::PI * 16_000 * SCALE).to_i
|
|
43
|
+
|
|
44
|
+
# A step of several cycles scales w0 by their count. reSID keeps such
|
|
45
|
+
# a step stable by capping the cutoff at 4 kHz over 8 cycles; this is
|
|
46
|
+
# the same bound on the product, so a single cycle is never capped.
|
|
47
|
+
W0_STEP_MAX = (2 * Math::PI * 4_000 * 8 * SCALE).to_i
|
|
48
|
+
|
|
49
|
+
def self.build_w0(points)
|
|
50
|
+
table = Array.new(2048)
|
|
51
|
+
points.each_cons(2) do |(fc0, hz0), (fc1, hz1)|
|
|
52
|
+
(fc0..fc1).each do |register|
|
|
53
|
+
hz = hz0 + (((hz1 - hz0) * (register - fc0)).to_f / (fc1 - fc0))
|
|
54
|
+
table[register] = [(2 * Math::PI * hz * SCALE).to_i, W0_MAX].min
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
table.freeze
|
|
58
|
+
end
|
|
59
|
+
private_class_method :build_w0
|
|
60
|
+
|
|
61
|
+
W0 = CUTOFF_POINTS.transform_values { |points| build_w0(points) }.freeze
|
|
62
|
+
|
|
63
|
+
# 1024/Q, with Q sweeping from 0.707 up to 1.707 across the register.
|
|
64
|
+
RESONANCE = (0x0..0xf).map { |res| (1024.0 / (0.707 + (res / 15.0))).to_i }.freeze
|
|
65
|
+
|
|
66
|
+
# The mixer's own input DC offset, derived by reSID from the 6581's
|
|
67
|
+
# zero-volume and full-volume output levels. The 8580 has none.
|
|
68
|
+
MIXER_DC = { mos6581: -((0xfff * 0xff) / 18) >> 7, mos8580: 0 }.freeze
|
|
69
|
+
|
|
70
|
+
# Fractional bits carried in the integrators. At low cutoffs a cycle's
|
|
71
|
+
# step is only a few units, which truncating to whole units would
|
|
72
|
+
# swallow, and always toward minus infinity.
|
|
73
|
+
FRACTION = 12
|
|
74
|
+
|
|
75
|
+
# The RC network between the SID's audio pin and the C64's output jack:
|
|
76
|
+
# a 10kΩ/1000pF low-pass at ~16 kHz, then a 1kΩ/10µF high-pass at
|
|
77
|
+
# ~16 Hz that strips the 6581's DC offset off the mix.
|
|
78
|
+
class External
|
|
79
|
+
W0_LOWPASS = (100_000 * SCALE).round
|
|
80
|
+
W0_HIGHPASS = (100 * SCALE).round
|
|
81
|
+
|
|
82
|
+
# The high-pass step is W0_HIGHPASS/2^20 of the gap, so in whole
|
|
83
|
+
# units it stalls on any gap under ~10k, as reSID's does. Sixteen
|
|
84
|
+
# fractional bits bring that under one unit.
|
|
85
|
+
FRACTION = 16
|
|
86
|
+
|
|
87
|
+
# Its input arrives with the filter's fractional bits still on.
|
|
88
|
+
INPUT_SHIFT = FRACTION - Filter::FRACTION
|
|
89
|
+
|
|
90
|
+
attr_reader :output
|
|
91
|
+
|
|
92
|
+
def initialize
|
|
93
|
+
@lowpass = 0
|
|
94
|
+
@highpass = 0
|
|
95
|
+
@output = 0
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def save_state(out)
|
|
99
|
+
out.int(@lowpass).int(@highpass).int(@output)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def load_state(input)
|
|
103
|
+
@lowpass = input.int
|
|
104
|
+
@highpass = input.int
|
|
105
|
+
@output = input.int
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Takes the input in 1/2^Filter::FRACTION units, and reads the
|
|
109
|
+
# output out in whole ones. A step of several cycles moves each pole
|
|
110
|
+
# at most onto its input.
|
|
111
|
+
def cycle!(input, cycles = 1)
|
|
112
|
+
w0_lowpass = [(W0_LOWPASS >> 8) * cycles, 1 << 12].min
|
|
113
|
+
w0_highpass = [W0_HIGHPASS * cycles, 1 << 20].min
|
|
114
|
+
delta_lowpass = (w0_lowpass * ((input << INPUT_SHIFT) - @lowpass)) >> 12
|
|
115
|
+
delta_highpass = (w0_highpass * (@lowpass - @highpass)) >> 20
|
|
116
|
+
@output = (@lowpass - @highpass) >> FRACTION
|
|
117
|
+
@lowpass += delta_lowpass
|
|
118
|
+
@highpass += delta_highpass
|
|
119
|
+
@output
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
attr_reader :model, :cutoff, :routing, :mode, :volume
|
|
124
|
+
|
|
125
|
+
def initialize(model: :mos6581)
|
|
126
|
+
@model = model
|
|
127
|
+
@w0_table = W0.fetch(model)
|
|
128
|
+
@mixer_dc = MIXER_DC.fetch(model)
|
|
129
|
+
@external = External.new
|
|
130
|
+
reset
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The integrators and the registers carry over; the cutoff maps
|
|
134
|
+
# through the new model's curve.
|
|
135
|
+
def model=(model)
|
|
136
|
+
@model = model
|
|
137
|
+
@w0_table = W0.fetch(model)
|
|
138
|
+
@mixer_dc = MIXER_DC.fetch(model)
|
|
139
|
+
@w0 = @w0_table[@cutoff]
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def reset
|
|
143
|
+
@cutoff = 0x000
|
|
144
|
+
@routing = 0x0
|
|
145
|
+
@mode = 0x0
|
|
146
|
+
@volume = 0x0
|
|
147
|
+
@voice3_off = false
|
|
148
|
+
@w0 = @w0_table[0]
|
|
149
|
+
@resonance = RESONANCE[0]
|
|
150
|
+
@lowpass = @bandpass = @highpass = 0
|
|
151
|
+
@input = @unfiltered = 0
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def write(reg, value)
|
|
155
|
+
case reg
|
|
156
|
+
when 0x15 then self.cutoff = (@cutoff & 0x7f8) | (value & 0x07)
|
|
157
|
+
when 0x16 then self.cutoff = (value << 3) | (@cutoff & 0x007)
|
|
158
|
+
when 0x17 then write_resonance(value)
|
|
159
|
+
when 0x18 then write_mode_volume(value)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def cutoff=(value)
|
|
164
|
+
@cutoff = value & 0x7ff
|
|
165
|
+
@w0 = @w0_table[@cutoff]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def voice3_off? = @voice3_off
|
|
169
|
+
|
|
170
|
+
def lowpass = @lowpass >> FRACTION
|
|
171
|
+
def bandpass = @bandpass >> FRACTION
|
|
172
|
+
def highpass = @highpass >> FRACTION
|
|
173
|
+
|
|
174
|
+
# Integrates `cycles` cycles in one step, from the voices' output at
|
|
175
|
+
# the end of them.
|
|
176
|
+
def cycle!(voices, cycles = 1)
|
|
177
|
+
route(voices)
|
|
178
|
+
w0 = @w0 * cycles
|
|
179
|
+
w0 = W0_STEP_MAX if w0 > W0_STEP_MAX
|
|
180
|
+
@bandpass -= (w0 * @highpass) >> 20
|
|
181
|
+
@lowpass -= (w0 * @bandpass) >> 20
|
|
182
|
+
@highpass = ((@bandpass * @resonance) >> 10) - @lowpass - (@input << FRACTION)
|
|
183
|
+
@external.cycle!(fine_mix, cycles)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# The SID's own audio pin, before the board's RC network.
|
|
187
|
+
def mix = fine_mix >> FRACTION
|
|
188
|
+
|
|
189
|
+
def output = @external.output
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
193
|
+
def write_resonance(value)
|
|
194
|
+
@routing = value & 0x0f
|
|
195
|
+
@resonance = RESONANCE[(value >> 4) & 0x0f]
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def write_mode_volume(value)
|
|
199
|
+
@volume = value & 0x0f
|
|
200
|
+
@mode = (value >> 4) & 0x07
|
|
201
|
+
@voice3_off = value.anybits?(0x80)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Unrolled over the three voices, like SID#clock!. Voice 3 is silenced
|
|
205
|
+
# by MODE/VOL bit 7 only while it bypasses the filter.
|
|
206
|
+
def route(voices)
|
|
207
|
+
voice1, voice2, voice3 = voices
|
|
208
|
+
@input = 0
|
|
209
|
+
@unfiltered = 0
|
|
210
|
+
route_voice(voice1.output >> 7, 0x1)
|
|
211
|
+
route_voice(voice2.output >> 7, 0x2)
|
|
212
|
+
if @routing & 0x4 != 0
|
|
213
|
+
@input += voice3.output >> 7
|
|
214
|
+
elsif !@voice3_off
|
|
215
|
+
@unfiltered += voice3.output >> 7
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def route_voice(output, bit)
|
|
220
|
+
if @routing & bit == bit
|
|
221
|
+
@input += output
|
|
222
|
+
else
|
|
223
|
+
@unfiltered += output
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# The mix with the integrators' fractional bits kept, so the RC
|
|
228
|
+
# network isn't fed the filter's output floored to whole units.
|
|
229
|
+
def fine_mix = (((@unfiltered + @mixer_dc) << FRACTION) + filtered) * @volume
|
|
230
|
+
|
|
231
|
+
def filtered
|
|
232
|
+
value = 0
|
|
233
|
+
value += @lowpass if @mode & 0x1 != 0
|
|
234
|
+
value += @bandpass if @mode & 0x2 != 0
|
|
235
|
+
value += @highpass if @mode & 0x4 != 0
|
|
236
|
+
value
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class SID
|
|
6
|
+
# The SID's state for a snapshot.
|
|
7
|
+
module SavedState
|
|
8
|
+
# How far the recording has got into the sample it is averaging, for
|
|
9
|
+
# a host that carries on recording from a saved state: it calls
|
|
10
|
+
# #record, then #load_recording.
|
|
11
|
+
def save_recording(out)
|
|
12
|
+
decimator = @decimator
|
|
13
|
+
raise ArgumentError, "the SID isn't recording" if decimator.nil?
|
|
14
|
+
|
|
15
|
+
out.marker("RECORDING")
|
|
16
|
+
decimator.save_state(out)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def load_recording(input)
|
|
20
|
+
decimator = @decimator
|
|
21
|
+
raise ArgumentError, "the SID isn't recording" if decimator.nil?
|
|
22
|
+
|
|
23
|
+
input.marker("RECORDING")
|
|
24
|
+
decimator.load_state(input)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# The registers, the data bus, the voices and the filter, and the span
|
|
28
|
+
# the DSP has yet to catch up on, as it stands: saving leaves the SID
|
|
29
|
+
# as it was. Whether it synthesizes or records is the host's.
|
|
30
|
+
def save_state(out)
|
|
31
|
+
out.marker("SID")
|
|
32
|
+
@registers.save_state(out)
|
|
33
|
+
out.int(@bus_value).int(@bus_ttl).int(@pending_cycles).ints(@deferred_writes)
|
|
34
|
+
@voices.each { |voice| voice.save_state(out) }
|
|
35
|
+
@filter.save_state(out)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def load_state(input)
|
|
39
|
+
input.marker("SID")
|
|
40
|
+
@registers.load_state(input)
|
|
41
|
+
@bus_value = input.int
|
|
42
|
+
@bus_ttl = input.int
|
|
43
|
+
@pending_cycles = input.int
|
|
44
|
+
input.ints_into(@deferred_writes)
|
|
45
|
+
@voices.each { |voice| voice.load_state(input) }
|
|
46
|
+
@filter.load_state(input)
|
|
47
|
+
update_span_rules
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class SID
|
|
6
|
+
# One of the three voices: a waveform generator amplitude-modulated by
|
|
7
|
+
# its envelope.
|
|
8
|
+
#
|
|
9
|
+
# The 6581 waveform DAC swings around a midpoint well above ground, so
|
|
10
|
+
# even a silent voice carries a large DC offset into the mixer. The
|
|
11
|
+
# 8580's is centred and carries none.
|
|
12
|
+
class Voice
|
|
13
|
+
WAVE_ZERO = { mos6581: 0x380, mos8580: 0x800 }.freeze
|
|
14
|
+
DC_OFFSET = { mos6581: 0x800 * 0xff, mos8580: 0 }.freeze
|
|
15
|
+
|
|
16
|
+
attr_reader :waveform, :envelope
|
|
17
|
+
|
|
18
|
+
# Each voice hard-syncs and ring-modulates against the previous one,
|
|
19
|
+
# wrapping from the first back round to the last.
|
|
20
|
+
def self.linked(count, model:)
|
|
21
|
+
voices = Array.new(count) { new(model:) }
|
|
22
|
+
waveforms = voices.map(&:waveform)
|
|
23
|
+
waveforms.each_with_index do |waveform, i|
|
|
24
|
+
waveform.sync_source = waveforms[i - 1]
|
|
25
|
+
waveform.sync_dest = waveforms[(i + 1) % count]
|
|
26
|
+
end
|
|
27
|
+
voices
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def initialize(model: :mos6581)
|
|
31
|
+
@waveform = Waveform.new(model:)
|
|
32
|
+
@envelope = Envelope.new
|
|
33
|
+
@wave_zero = WAVE_ZERO.fetch(model)
|
|
34
|
+
@dc_offset = DC_OFFSET.fetch(model)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def model=(model)
|
|
38
|
+
@waveform.model = model
|
|
39
|
+
@wave_zero = WAVE_ZERO.fetch(model)
|
|
40
|
+
@dc_offset = DC_OFFSET.fetch(model)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def reset!
|
|
44
|
+
@waveform.reset!
|
|
45
|
+
@envelope = Envelope.new
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def save_state(out)
|
|
49
|
+
@waveform.save_state(out)
|
|
50
|
+
@envelope.save_state(out)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def load_state(input)
|
|
54
|
+
@waveform.load_state(input)
|
|
55
|
+
@envelope.load_state(input)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def control=(value)
|
|
59
|
+
@waveform.control = value
|
|
60
|
+
@envelope.control = value
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def cycle!
|
|
64
|
+
@envelope.cycle!
|
|
65
|
+
@waveform.cycle!
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def fast_forward(cycles)
|
|
69
|
+
@envelope.fast_forward(cycles)
|
|
70
|
+
@waveform.fast_forward(cycles)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def output
|
|
74
|
+
((@waveform.output - @wave_zero) * @envelope.output) + @dc_offset
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class SID
|
|
6
|
+
class Waveform
|
|
7
|
+
# The shapes the chip puts out when more than one waveform is selected.
|
|
8
|
+
#
|
|
9
|
+
# Selecting several shapers connects their outputs to the same twelve
|
|
10
|
+
# DAC lines. Each line settles somewhere between the levels the
|
|
11
|
+
# shapers drive it to, and couples to the lines around it through the
|
|
12
|
+
# resistances between them, so a bit reads high only when enough of
|
|
13
|
+
# its own drive and its neighbours' is high. Modelled here as a
|
|
14
|
+
# threshold on a weighted sum: each selected shaper adds its bit times
|
|
15
|
+
# its strength, a high pulse adds its strength to every line, and the
|
|
16
|
+
# neighbours contribute with one weight per distance below and one per
|
|
17
|
+
# distance above.
|
|
18
|
+
#
|
|
19
|
+
# A low pulse grounds every line.
|
|
20
|
+
#
|
|
21
|
+
# The parameters come from bin/sidwavefit, fitted against OSC3 dumps of
|
|
22
|
+
# real chips in SID/resid-test (oscsample0/1-6581/8580wf30/50/60/70).
|
|
23
|
+
# The tables are built from them once per chip model.
|
|
24
|
+
module Combined
|
|
25
|
+
BITS = 12
|
|
26
|
+
|
|
27
|
+
# Per chip and selection (triangle $1, sawtooth $2, pulse $4): the
|
|
28
|
+
# triangle's strength next to a sawtooth (the sawtooth, or a lone
|
|
29
|
+
# triangle, has strength 1), the pulse's strength, the threshold as
|
|
30
|
+
# a fraction of the full drive, then the weights of the lines one to
|
|
31
|
+
# eleven bits below and one to eleven bits above.
|
|
32
|
+
PARAMETERS = {
|
|
33
|
+
mos6581: {
|
|
34
|
+
0x3 => [0.522, 6.4087, 0.9062,
|
|
35
|
+
0.5304, 0.278, 0.1473, 0.0773, 0.0, 0.0184, 0.0113, 0.0251, 0.0594, 0.2001, 0.2001,
|
|
36
|
+
0.4103, 0.1756, 0.0687, 0.0282, 0.0115, 0.0047, 0.0031, 0.2001, 0.2001, 0.2001, 0.2001],
|
|
37
|
+
0x5 => [5.871, 0.0, 0.7485,
|
|
38
|
+
0.6522, 0.5913, 0.5404, 0.4828, 0.428, 0.361, 0.2955, 0.2453, 0.1558, 0.0501, 0.0016,
|
|
39
|
+
0.7106, 0.6667, 0.6426, 0.6376, 0.5761, 0.6639, 0.7193, 0.4728, 0.4306, 0.392, 0.357],
|
|
40
|
+
0x6 => [3.8329, 4.7751, 0.9565,
|
|
41
|
+
0.815, 0.8302, 0.8204, 0.8107, 0.8511, 0.8416, 0.8322, 0.8728, 0.8636, 0.8545, 0.8954,
|
|
42
|
+
0.8071, 0.7979, 0.7465, 0.7771, 0.7643, 0.7572, 0.7054, 0.4585, 0.4159, 0.3773, 0.3421],
|
|
43
|
+
0x7 => [5.5703, 0.2543, 0.9575,
|
|
44
|
+
0.7177, 0.515, 0.3696, 0.2653, 0.1904, 0.1367, 0.0606, 0.0953, 0.0506, 0.0363, 0.2261,
|
|
45
|
+
0.6347, 0.286, 0.203, 0.0817, 0.0438, 0.1235, 0.2001, 0.2001, 0.2001, 0.2001, 0.2001]
|
|
46
|
+
},
|
|
47
|
+
mos8580: {
|
|
48
|
+
0x3 => [0.2223, 7.7741, 0.8869,
|
|
49
|
+
0.4626, 0.2025, 0.0911, 0.041, 0.0185, 0.0, 0.0008, 0.0, 0.0, 0.0127, 0.0063,
|
|
50
|
+
0.5633, 0.311, 0.2099, 0.0881, 0.0255, 0.0, 0.0431, 0.2002, 0.2002, 0.2002, 0.2002],
|
|
51
|
+
0x5 => [3.1022, 1.1282, 0.9157,
|
|
52
|
+
0.2731, 0.2748, 0.0031, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
|
53
|
+
0.6228, 0.7375, 0.6094, 0.6667, 0.4381, 0.4714, 0.3648, 0.2669, 0.2264, 0.2001, 0.2001],
|
|
54
|
+
0x6 => [0.9477, 0.0012, 0.7837,
|
|
55
|
+
0.2565, 0.2289, 0.1095, 0.0805, 0.0282, 0.0281, 0.0, 0.0, 0.0, 0.0, 0.0063,
|
|
56
|
+
0.3382, 0.3131, 0.3129, 0.2898, 0.233, 0.1762, 0.2135, 0.2002, 0.2002, 0.2002, 0.2002],
|
|
57
|
+
0x7 => [1.219, 1.1111, 0.9473,
|
|
58
|
+
0.3829, 0.1466, 0.0577, 0.0215, 0.0082, 0.0032, 0.0, 0.0, 0.0, 0.0, 0.0,
|
|
59
|
+
0.7273, 0.6845, 0.566, 0.4807, 0.6, 0.1954, 0.4651, 0.2194, 0.2002, 0.2002, 0.2002]
|
|
60
|
+
}
|
|
61
|
+
}.freeze
|
|
62
|
+
|
|
63
|
+
# A compiled parameter set: the weight matrix, and per line the sum
|
|
64
|
+
# of its weights and the threshold against the full drive.
|
|
65
|
+
class Network
|
|
66
|
+
attr_reader :triangle_strength, :pulse_strength
|
|
67
|
+
|
|
68
|
+
def initialize(params)
|
|
69
|
+
@triangle_strength, @pulse_strength, threshold = params[0, 3]
|
|
70
|
+
below = [1.0] + params[3, BITS - 1]
|
|
71
|
+
above = [1.0] + params[3 + BITS - 1, BITS - 1]
|
|
72
|
+
@weights = Array.new(BITS) do |i|
|
|
73
|
+
Array.new(BITS) { |j| j <= i ? below[i - j] : above[j - i] }.freeze
|
|
74
|
+
end.freeze
|
|
75
|
+
@norms = @weights.map(&:sum).freeze
|
|
76
|
+
@threshold = threshold
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# `drives` holds [bits, strength] for each selected shaper; a high
|
|
80
|
+
# pulse is passed separately, as it drives every line alike.
|
|
81
|
+
def shape(drives, pulse)
|
|
82
|
+
total = drives.sum { |_, strength| strength }
|
|
83
|
+
total += @pulse_strength if pulse
|
|
84
|
+
out = 0
|
|
85
|
+
BITS.times do |i|
|
|
86
|
+
level = pulse ? @pulse_strength * @norms[i] : 0.0
|
|
87
|
+
weights = @weights[i]
|
|
88
|
+
drives.each do |bits, strength|
|
|
89
|
+
BITS.times { |j| level += weights[j] * strength if bits[j] == 1 }
|
|
90
|
+
end
|
|
91
|
+
out |= 1 << i if level >= @threshold * @norms[i] * total
|
|
92
|
+
end
|
|
93
|
+
out
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
module_function
|
|
98
|
+
|
|
99
|
+
# The pulse-high shapes of the four combinations without noise.
|
|
100
|
+
def tables(model)
|
|
101
|
+
@tables ||= {}
|
|
102
|
+
@tables[model] ||= PARAMETERS.fetch(model).to_h do |selected, params|
|
|
103
|
+
[selected, table(selected, params)]
|
|
104
|
+
end.freeze
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# One combination's shapes, indexed by the phase (the sawtooth), or
|
|
108
|
+
# for triangle and pulse by the triangle. A triangle combined with a
|
|
109
|
+
# sawtooth is never folded, so it is the phase shifted left one bit.
|
|
110
|
+
def table(selected, params)
|
|
111
|
+
network = Network.new(params)
|
|
112
|
+
Array.new(1 << BITS) do |index|
|
|
113
|
+
triangle = selected == 0x5 ? index : (index << 1) & 0xfff
|
|
114
|
+
network.shape(drives(selected, network, index, triangle), selected.anybits?(0x4))
|
|
115
|
+
end.freeze
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def drives(selected, network, saw, triangle)
|
|
119
|
+
drives = []
|
|
120
|
+
drives << [saw, 1.0] if selected.anybits?(0x2)
|
|
121
|
+
drives << [triangle, selected.anybits?(0x2) ? network.triangle_strength : 1.0] if selected.anybits?(0x1)
|
|
122
|
+
drives
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Chips
|
|
5
|
+
class SID
|
|
6
|
+
class Waveform
|
|
7
|
+
# Running an oscillator many cycles in one step, for the catch-ups that
|
|
8
|
+
# fast-forward the voices between whole cycles.
|
|
9
|
+
module FastForward
|
|
10
|
+
# Runs `cycles` cycles as #cycle! would, for an oscillator that is not
|
|
11
|
+
# feeding back. What those cycles latch onto the output is left to the
|
|
12
|
+
# whole cycle that follows, which latches over it.
|
|
13
|
+
def fast_forward(cycles)
|
|
14
|
+
drain_floating(cycles) if @selected.zero?
|
|
15
|
+
if @test
|
|
16
|
+
bleed_shift_register(cycles)
|
|
17
|
+
else
|
|
18
|
+
while @shift_pipeline != 0 && cycles != 0
|
|
19
|
+
advance
|
|
20
|
+
cycles -= 1
|
|
21
|
+
end
|
|
22
|
+
skip_ahead(cycles) if cycles != 0 && @frequency != 0
|
|
23
|
+
compare_pulse(((@accumulator - @frequency) & 0xffffff) >> 12)
|
|
24
|
+
end
|
|
25
|
+
@stale = true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# The cycle on which the accumulator MSB next rises, counting the next
|
|
29
|
+
# one as 1, or nil if it never will. The test bit leaves the last
|
|
30
|
+
# rise standing for as long as it is held.
|
|
31
|
+
def cycles_to_msb_rise
|
|
32
|
+
return (@msb_rising ? 1 : nil) if @test
|
|
33
|
+
return if @frequency.zero?
|
|
34
|
+
|
|
35
|
+
distance = (MSB - @accumulator) & 0xffffff
|
|
36
|
+
distance = 0x1000000 if distance.zero?
|
|
37
|
+
(distance + @frequency - 1) / @frequency
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# A combined waveform with sawtooth (on the 6581) or noise in the mix
|
|
41
|
+
# writes back into the oscillator every cycle, so it cannot be
|
|
42
|
+
# fast-forwarded. Triangle and pulse alone write nothing back.
|
|
43
|
+
def feedback?
|
|
44
|
+
combined_waveform? && (@selected.anybits?(0x8) || (@topbit_feedback && @selected.anybits?(0x2)))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Whether a catch-up has to run this oscillator a cycle at a time.
|
|
48
|
+
# While synthesizing, every combined waveform does, so the filter
|
|
49
|
+
# integrates it a cycle at a time.
|
|
50
|
+
def stepped?(synthesizing) = synthesizing ? combined_waveform? : feedback?
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def combined_waveform? = !@test && combined?(@selected)
|
|
55
|
+
|
|
56
|
+
# Advances an accumulator with no shift in flight. Each rise of bit 19
|
|
57
|
+
# shifts the LFSR two cycles on, so a rise in the last two cycles
|
|
58
|
+
# leaves its shift pending for the cycles after.
|
|
59
|
+
def skip_ahead(cycles)
|
|
60
|
+
previous = @accumulator
|
|
61
|
+
@accumulator += @frequency * cycles
|
|
62
|
+
rises = bit19_rises(previous, @accumulator)
|
|
63
|
+
if rises != 0
|
|
64
|
+
@shift_pipeline = pipeline_after(cycles)
|
|
65
|
+
rises -= 1 if @shift_pipeline != 0
|
|
66
|
+
rises.times { shift_noise }
|
|
67
|
+
end
|
|
68
|
+
@accumulator &= 0xffffff
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Bit 19 rises at most once a cycle, on each odd multiple of 2^19,
|
|
72
|
+
# and at most once in any eight.
|
|
73
|
+
def bit19_rises(from, to) = ((to + 0x80000) >> 20) - ((from + 0x80000) >> 20)
|
|
74
|
+
|
|
75
|
+
# How far a rise on one of the last two cycles has come.
|
|
76
|
+
def pipeline_after(cycles)
|
|
77
|
+
last = @accumulator - @frequency
|
|
78
|
+
return 2 if bit19_rises(last, @accumulator) != 0
|
|
79
|
+
return 1 if cycles > 1 && bit19_rises(last - @frequency, last) != 0
|
|
80
|
+
|
|
81
|
+
0
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def drain_floating(cycles)
|
|
85
|
+
return if @floating_ttl.zero?
|
|
86
|
+
|
|
87
|
+
@floating_ttl -= cycles
|
|
88
|
+
return unless @floating_ttl <= 0
|
|
89
|
+
|
|
90
|
+
@floating_ttl = 0
|
|
91
|
+
@floating = 0x000
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def bleed_shift_register(cycles = 1)
|
|
95
|
+
return if @shift_register_reset.zero?
|
|
96
|
+
|
|
97
|
+
@shift_register_reset -= cycles
|
|
98
|
+
return unless @shift_register_reset <= 0
|
|
99
|
+
|
|
100
|
+
@shift_register_reset = 0
|
|
101
|
+
@shift_register = SHIFT_REGISTER_RESET
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|