badline 0.3.0 → 0.4.1
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/.claude/hooks/session-start.sh +57 -0
- data/.claude/settings.json +14 -0
- data/CHANGELOG.md +65 -0
- data/CLAUDE.md +45 -16
- data/CONTRIBUTING.md +95 -3
- data/README.md +111 -61
- data/SECURITY.md +9 -0
- data/doc/pinned-behaviour.md +275 -33
- data/exe/badline-ruby +59 -0
- data/lib/badline/address_bus.rb +22 -10
- data/lib/badline/audio/aiff.rb +8 -3
- data/lib/badline/audio/cli.rb +17 -12
- data/lib/badline/audio/console.rb +12 -58
- data/lib/badline/audio/playback.rb +3 -0
- data/lib/badline/audio/sdl_sink.rb +20 -52
- data/lib/badline/audio/terminal.rb +90 -0
- data/lib/badline/audio.rb +2 -2
- data/lib/badline/cartridge/geo_ram.rb +63 -0
- data/lib/badline/cartridge.rb +1 -0
- data/lib/badline/cia/interrupt_register.rb +34 -10
- data/lib/badline/cia.rb +15 -8
- data/lib/badline/computer.rb +28 -13
- data/lib/badline/gui/application.rb +33 -21
- data/lib/badline/gui/gamepads.rb +22 -22
- data/lib/badline/gui/joy_map.rb +1 -1
- data/lib/badline/gui/key_map.rb +6 -1
- data/lib/badline/gui/pane.rb +7 -6
- data/lib/badline/gui/screen_pane.rb +9 -13
- data/lib/badline/gui/window.rb +35 -20
- data/lib/badline/gui.rb +1 -1
- data/lib/badline/input/mouse1351.rb +21 -6
- data/lib/badline/input/paddles.rb +11 -6
- data/lib/badline/kernal_trap/channel.rb +3 -0
- data/lib/badline/kernal_trap/drive/block_commands.rb +33 -4
- data/lib/badline/kernal_trap/drive/status.rb +3 -0
- data/lib/badline/kernal_trap/drive/write_file.rb +41 -0
- data/lib/badline/kernal_trap/drive/writes.rb +92 -0
- data/lib/badline/kernal_trap/drive.rb +51 -51
- data/lib/badline/kernal_trap/file.rb +6 -9
- data/lib/badline/kernal_trap/save.rb +9 -4
- data/lib/badline/keyboard.rb +19 -3
- data/lib/badline/media.rb +42 -11
- data/lib/badline/options.rb +209 -0
- data/lib/badline/ram_expansion/banking.rb +30 -0
- data/lib/badline/ram_expansion/plus256k.rb +38 -0
- data/lib/badline/ram_expansion/plus60k.rb +31 -0
- data/lib/badline/ram_expansion/unexpanded.rb +22 -0
- data/lib/badline/ram_expansion.rb +21 -0
- data/lib/badline/region.rb +51 -0
- data/lib/badline/sdl/events.rb +47 -0
- data/lib/badline/sdl/functions.rb +73 -0
- data/lib/badline/sdl.rb +67 -0
- data/lib/badline/sid/waveform/noise_writeback.rb +32 -14
- data/lib/badline/sid/waveform.rb +1 -1
- data/lib/badline/sid.rb +3 -1
- data/lib/badline/storage/d64_image.rb +10 -0
- data/lib/badline/storage/d71_image.rb +12 -0
- data/lib/badline/storage/d81_image.rb +9 -0
- data/lib/badline/storage/disk_image/bam.rb +111 -0
- data/lib/badline/storage/disk_image/directory.rb +79 -0
- data/lib/badline/storage/disk_image/writing.rb +110 -0
- data/lib/badline/storage/disk_image.rb +27 -4
- data/lib/badline/storage/host_directory.rb +2 -1
- data/lib/badline/storage.rb +15 -0
- data/lib/badline/time_of_day.rb +4 -2
- data/lib/badline/version.rb +1 -1
- data/lib/badline/via/control_lines.rb +104 -0
- data/lib/badline/via/interrupt_register.rb +58 -0
- data/lib/badline/via/shift_register.rb +135 -0
- data/lib/badline/via/timer1.rb +82 -0
- data/lib/badline/via/timer2.rb +84 -0
- data/lib/badline/via.rb +262 -0
- data/lib/badline/vic/bank.rb +4 -3
- data/lib/badline/vic/color_patches.rb +6 -3
- data/lib/badline/vic/display_state.rb +4 -0
- data/lib/badline/vic/graphics_shifter.rb +42 -7
- data/lib/badline/vic/registers.rb +3 -0
- data/lib/badline/vic/sequencer.rb +18 -18
- data/lib/badline/vic/sequencer_output.rb +4 -4
- data/lib/badline/vic/sprite/internal_bus.rb +4 -0
- data/lib/badline/vic/sprite/shifter.rb +25 -3
- data/lib/badline/vic/sprite.rb +5 -4
- data/lib/badline/vic/sprites.rb +8 -3
- data/lib/badline/vic.rb +93 -44
- data/lib/badline.rb +4 -0
- data/packaging/homebrew/badline.rb +34 -0
- metadata +30 -20
- data/exe/badline +0 -78
- data/exe/badline-sid +0 -34
- data/lib/badline/audio/options.rb +0 -137
data/doc/pinned-behaviour.md
CHANGED
|
@@ -24,7 +24,9 @@ only catches the rows that happen to move.
|
|
|
24
24
|
- [VIC graphics pipeline](#vic-graphics-pipeline)
|
|
25
25
|
- [VIC phi1 bus](#vic-phi1-bus)
|
|
26
26
|
- [VIC light pen](#vic-light-pen)
|
|
27
|
+
- [VIC-II 8565](#vic-ii-8565)
|
|
27
28
|
- [CIA 6526 timer pipeline](#cia-6526-timer-pipeline)
|
|
29
|
+
- [CIA 6526A interrupt register](#cia-6526a-interrupt-register)
|
|
28
30
|
- [CIA serial shift register](#cia-serial-shift-register)
|
|
29
31
|
- [6510 I/O port](#6510-io-port)
|
|
30
32
|
- [SID oscillator](#sid-oscillator)
|
|
@@ -400,6 +402,14 @@ only catches the rows that happen to move.
|
|
|
400
402
|
- Spec guard: *#collide_upto* in
|
|
401
403
|
[`vic/sprites_spec.rb`](../spec/badline/vic/sprites_spec.rb). Its first
|
|
402
404
|
four examples each fail on a one-pixel change.
|
|
405
|
+
- $D01E and $D01F are read-only. A write, including the write-back of a
|
|
406
|
+
read-modify-write such as `LSR $D01E`, changes neither register.
|
|
407
|
+
- Pinned by `general/fuxxortest/ef2-inst4a`, which shifts each frame's
|
|
408
|
+
collision out of $D01E with `LSR`. Storing the shifted value back
|
|
409
|
+
leaves bit 0 set for the next frame, which reads as a collision one
|
|
410
|
+
pixel to the right of every real one.
|
|
411
|
+
- Spec guard: *with a collision register* in
|
|
412
|
+
[`vic/registers_spec.rb`](../spec/badline/vic/registers_spec.rb).
|
|
403
413
|
|
|
404
414
|
## VIC border and idle state
|
|
405
415
|
|
|
@@ -576,6 +586,31 @@ only catches the rows that happen to move.
|
|
|
576
586
|
cells take their colour from the halted opcode.
|
|
577
587
|
- Spec guard: *an FLI match in column 13* in
|
|
578
588
|
[`vic_spec.rb`](../spec/badline/vic_spec.rb).
|
|
589
|
+
- On the 6569, a match that opens display state in a g-access column, which
|
|
590
|
+
is a DMA delay out of idle state, moves that column's idle g-access from
|
|
591
|
+
`$3fff` (or `$39ff`) to `$38ff`, but only when YSCROLL is nonzero, so
|
|
592
|
+
only when the trigger line's low three bits are nonzero. The address wins over
|
|
593
|
+
ECM's `$39ff`. Nothing else enters the condition: not the column, BMM,
|
|
594
|
+
RC, VC or the previous fetch. The column is the one just before the
|
|
595
|
+
first display-state g-access. This rule is empirical and fitted to the
|
|
596
|
+
two tests below. It is the same condition the Denise emulator converged
|
|
597
|
+
on, and no hardware explanation for the YSCROLL gate is known. The
|
|
598
|
+
readme lists `$38ff` for every 6569 it measured. Among 8565s it lists
|
|
599
|
+
`$3807`, `$38c7`, `$38d7` and `$38ff`, varying from chip to chip, and no
|
|
600
|
+
testbench row pins one, so the 8565 keeps `$3fff` there.
|
|
601
|
+
`VIC::DMA_DELAY_IDLE_ADDRESS` holds the 6569's address.
|
|
602
|
+
- Pinned by `vsp-tester`, which triggers on raster `$32` with YSCROLL 2
|
|
603
|
+
and reads the address back through sprite collisions. It passes on
|
|
604
|
+
`$38ff`, `$3807`, `$38c7` or `$38d7`, and reports `$3fff` (exit `$ff`)
|
|
605
|
+
without this rule.
|
|
606
|
+
- Pinned by `colorfetchbug/main`, whose only idle trigger is on raster
|
|
607
|
+
`$30` with YSCROLL 0 and whose reference shows `$3fff` there. Without
|
|
608
|
+
the YSCROLL gate it fails by 7 px. `sequencer-bug` (YSCROLL 3) reads
|
|
609
|
+
`$38ff` and passes either way.
|
|
610
|
+
- Spec guard: *reads the idle byte at $38ff when YSCROLL is nonzero* and
|
|
611
|
+
*reads the idle byte at $3fff when YSCROLL is 0*, plus the 8565's
|
|
612
|
+
*reads the idle byte at $3fff when YSCROLL is nonzero*, in
|
|
613
|
+
[`vic_spec.rb`](../spec/badline/vic_spec.rb).
|
|
579
614
|
- These rows can't be read as pixel counts. The sweep became readable by
|
|
580
615
|
OCRing each reference PNG against `lib/badline/roms/character.rom` and
|
|
581
616
|
matching every display row back to its offset in screen memory, so a diff
|
|
@@ -734,12 +769,125 @@ VICE x64sc's `vicii_fetch_graphics` and `draw_graphics8` for the 6569.
|
|
|
734
769
|
reference, as `makeref` corrects it: the test fixes up the tail bytes of
|
|
735
770
|
pre-R03 dumps before comparing. All five pages match, the raster-read
|
|
736
771
|
pages included (see [VIC raster IRQ phase](#vic-raster-irq-phase)).
|
|
737
|
-
- The 6569's offset is 2 half-pixels and the 8565's is 1
|
|
738
|
-
|
|
739
|
-
badline models only the 6569, and `bin/testbench` skips `vicii-new` rows.
|
|
772
|
+
- The 6569's offset is 2 half-pixels and the 8565's is 1 (see
|
|
773
|
+
[VIC-II 8565](#vic-ii-8565)).
|
|
740
774
|
- Pinned by `lplatency`, `lp-trigger`, and the `fldscroll` tests, which sync
|
|
741
775
|
through the light pen instead of the double IRQ.
|
|
742
776
|
|
|
777
|
+
## VIC-II 8565
|
|
778
|
+
|
|
779
|
+
The 8565 (`VIC.new(model: :mos8565)`, `Computer.new(vic_model:
|
|
780
|
+
:mos8565)`), fitted to the C64C, runs every rule above except the ones
|
|
781
|
+
below. They follow VICE x64sc's model checks (`color_latency` clear), and
|
|
782
|
+
each is gated on the model and was knocked out: removing it fails the rows
|
|
783
|
+
named, all in `testbench-vicii-new`, and moves no 6569 row. Pixel counts
|
|
784
|
+
are the row's diff with the rule removed.
|
|
785
|
+
|
|
786
|
+
- **Grey dots.** Where the 6569 still shows a colour register's old value
|
|
787
|
+
on the first pixel after a write, the 8565 shows light grey
|
|
788
|
+
(`VIC::GREY_DOT`, `$f`), for the border, the background registers
|
|
789
|
+
(`VIC::ColorPatches`) and the sprite colours (`VIC#log_sprite_change`).
|
|
790
|
+
A write that leaves the value as it was shows its dot too.
|
|
791
|
+
- The background and border dot is pinned by `rmwtest` (1655 px), every
|
|
792
|
+
spritesplit row whose `$d021` staircase it crosses (60 px each) and
|
|
793
|
+
`vicii_reg_timing` (275 px, 282/289 for `-a5`/`-ff`). The sprite dot
|
|
794
|
+
by `ss-hires-color` and `ss-mc-color0`/`1`/`2` (44 px each) and
|
|
795
|
+
`vicii_reg_timing` (136 px). The same-value dot by `rmwtest` (775 px)
|
|
796
|
+
and `vicii_reg_timing` (22 px), whose read-modify-write instructions
|
|
797
|
+
store each value twice.
|
|
798
|
+
- Spec guard: the *on the 8565* group in
|
|
799
|
+
[`color_patches_spec.rb`](../spec/badline/vic/color_patches_spec.rb),
|
|
800
|
+
*shows a grey dot on the pixel before a new sprite color* in
|
|
801
|
+
[`sprites_spec.rb`](../spec/badline/vic/sprites_spec.rb) and *shows a
|
|
802
|
+
grey dot for a background write of the same color* in
|
|
803
|
+
[`vic_spec.rb`](../spec/badline/vic_spec.rb).
|
|
804
|
+
- **Sprite multicolour.** `$d01c` reaches the sprite sequencers a pixel
|
|
805
|
+
sooner, 6 pixels on instead of 7, and works on the multicolour
|
|
806
|
+
flip-flop (VICE `update_sprite_mc_bits_8565`). The X match leaves the
|
|
807
|
+
flip-flop set in hi-res as well as clear after the first multicolour
|
|
808
|
+
pixel. A change flips it only when it lands between the two halves of
|
|
809
|
+
an expanded pixel, where a switch to hi-res sets it instead. A hi-res
|
|
810
|
+
pixel with the flip-flop clear sets it and loads nothing, so the latch
|
|
811
|
+
holds its last pixel.
|
|
812
|
+
- The delay is pinned by `ss-hires-mc`, `ss-mc-hires` and their `-exp`
|
|
813
|
+
twins (528/88/1452/88 px at 7). The flip-flop after the X match by
|
|
814
|
+
`ss-hires-mc`, `ss-pri`, `ss-unexp-exp-hires` (168 px each) and
|
|
815
|
+
`ss-hires-mc-exp`, `ss-pri-exp`, `ss-exp-unexp-hires` (336 px each).
|
|
816
|
+
The flip on a change by `ss-hires-mc-exp` (1364 px) and
|
|
817
|
+
`ss-mc-hires-exp` (44 px), and the hi-res hold by `ss-mc-hires` and
|
|
818
|
+
`ss-mc-hires-exp` (44 px each).
|
|
819
|
+
- Spec guard: *reaches the multicolor flip-flop six pixels on* in
|
|
820
|
+
[`sprites_spec.rb`](../spec/badline/vic/sprites_spec.rb) and the *with
|
|
821
|
+
an 8565* group of *mid-line writes* in
|
|
822
|
+
[`sprite_spec.rb`](../spec/badline/vic/sprite_spec.rb).
|
|
823
|
+
- **The g-access addresses with the `$d011` of the column before**,
|
|
824
|
+
whole, instead of the 6569's hold of BMM and, on the character ROM, ECM.
|
|
825
|
+
- Pinned by `modesplit` (598 px), `vicii_reg_timing` (92, 99/106 px),
|
|
826
|
+
`fetchsplit` (180 px) and every videomode row but `videomode1` (3 to
|
|
827
|
+
16 px).
|
|
828
|
+
- Spec guard: *addresses with the $d011 of the column before* in
|
|
829
|
+
[`vic_spec.rb`](../spec/badline/vic_spec.rb).
|
|
830
|
+
- **ECM and BMM take hold at the next group's pixel 0**, rising or
|
|
831
|
+
falling, where the 6569 takes them at pixel 4 as they rise and 5 or 6 as
|
|
832
|
+
they fall. MCM keeps the 6569's timing, but an MCM that falls out of
|
|
833
|
+
ECM+MCM does so on time instead of a pixel late.
|
|
834
|
+
- The pixel 0 is pinned by `modesplit` (916 px), `vicii_reg_timing` (86,
|
|
835
|
+
100/114 px) and every videomode row (6 to 13 px). The on-time MCM by
|
|
836
|
+
`videomode1` and `videomode-z` (2 px each).
|
|
837
|
+
- Spec guard: the *on the 8565* group in
|
|
838
|
+
[`graphics_shifter_spec.rb`](../spec/badline/vic/graphics_shifter_spec.rb).
|
|
839
|
+
- **Out of an invalid mode into multicolour bitmap, a background pixel 0
|
|
840
|
+
is still black, and into hi-res text any pixel 0 is.** Into multicolour
|
|
841
|
+
text and ECM text it shows on time.
|
|
842
|
+
- The multicolour bitmap case is pinned by `videomode-v` and `videomode2`
|
|
843
|
+
(1 px each) and `modesplit` (92 → 124 px), whose `%00` and `%01` pairs
|
|
844
|
+
after an ECM+MCM or ECM+BMM+MCM split keep their first pixel black.
|
|
845
|
+
`videomode-y` shows a `%01` pair on time into multicolour text.
|
|
846
|
+
- The hi-res text case is empirical, pinned by the E+B row of
|
|
847
|
+
`vicii_reg_timing-a5` and `-ff` (pass and 7 px, 7 and 14 px without
|
|
848
|
+
it), which drops ECM+BMM back to text. All three `vicii_reg_timing`
|
|
849
|
+
references keep that pixel 0 black, a foreground pixel in `-a5` and
|
|
850
|
+
`-ff`. `$d021` is black there, so no reference tells a background
|
|
851
|
+
pixel 0 apart, and the rule blackens both.
|
|
852
|
+
- `videomode-w` shows ECM+BMM into ECM text on time (1 px if it goes
|
|
853
|
+
black), while `modesplit`'s section 1 keeps the same move black (48 of
|
|
854
|
+
its 92 px). The two references disagree about the same move, and the
|
|
855
|
+
videomode one is kept.
|
|
856
|
+
- Spec guard: *keeps a background pixel 0 black out of an invalid mode
|
|
857
|
+
into multicolour bitmap*, *keeps pixel 0 black out of an invalid mode
|
|
858
|
+
into hi-res text*, *shows pixel 0 on time out of an invalid mode into
|
|
859
|
+
multicolour text* and *shows pixel 0 on time out of an invalid mode into
|
|
860
|
+
ECM text* in
|
|
861
|
+
[`graphics_shifter_spec.rb`](../spec/badline/vic/graphics_shifter_spec.rb).
|
|
862
|
+
- **The light pen latches one extra half-pixel**, where the 6569 adds two.
|
|
863
|
+
- Pinned by `lp-trigger/test2new`, which measures the trigger delay and
|
|
864
|
+
fails with two.
|
|
865
|
+
- Spec guard: *adds one extra half-pixel on the 8565* in
|
|
866
|
+
[`vic_spec.rb`](../spec/badline/vic_spec.rb).
|
|
867
|
+
|
|
868
|
+
What the 8565 references don't settle, and so what stays as it is:
|
|
869
|
+
|
|
870
|
+
- The pixel 0 of a group that leaves ECM or BMM is not consistent across
|
|
871
|
+
the references. Moving every falling edge to pixel 1 passes `modesplit`
|
|
872
|
+
but fails every videomode row but `videomode1` and all three
|
|
873
|
+
`vicii_reg_timing` rows, and the same move out of hi-res bitmap into
|
|
874
|
+
text is the new mode in `vicii_reg_timing-a5` and the old one in `-ff`.
|
|
875
|
+
The videomode readme says these delays "may depend on the type of VICII,
|
|
876
|
+
and the temperature of the chip". So `modesplit` (92 px) and
|
|
877
|
+
`vicii_reg_timing-ff` (7 px, the hi-res bitmap into text row) stay FAIL.
|
|
878
|
+
No emulator in the testbench results passes `vicii_reg_timing-a5` or
|
|
879
|
+
`-ff` on the 8565, and only Hoxs64 passes `modesplit`, against the
|
|
880
|
+
`8565early` reference.
|
|
881
|
+
- `fetchsplit` (154 px) stays FAIL: its readme says its 8565 artefacts
|
|
882
|
+
differ from chip to chip and change as the machine warms up, and its
|
|
883
|
+
`M1` and `M7` captures differ. The differences sit in the first character
|
|
884
|
+
after a `$dd00` bank switch on some lines. Delaying every rising, or
|
|
885
|
+
every falling, bank bit by a cycle on the 8565 makes it 656 or 752 px.
|
|
886
|
+
- VICE's idle g-access also reads ECM from the `$d011` of the column
|
|
887
|
+
before on the 8565. No testprog tells it apart, so the 8565 keeps the
|
|
888
|
+
6569's idle access, without the 6569's `$38ff` read where a DMA delay
|
|
889
|
+
starts.
|
|
890
|
+
|
|
743
891
|
## CIA 6526 timer pipeline
|
|
744
892
|
|
|
745
893
|
- Start and stop go through two stages. A force load lands one tick after
|
|
@@ -771,13 +919,17 @@ VICE x64sc's `vicii_fetch_graphics` and `draw_graphics8` for the 6569.
|
|
|
771
919
|
`CIA/ciavarious/cia3` K/L, `cia3a` D/H, `cia4` X, `cia8` A/C/F/J/L
|
|
772
920
|
(the readme's old-versus-new CIA cells) and `CIA/cia-timer/cia-timer-oldcias`,
|
|
773
921
|
and ported from VICE's `CIA_IM_TBB`.
|
|
774
|
-
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
922
|
+
- Every rule in this section is the **6526**'s, the chip `CIA.new`
|
|
923
|
+
builds unless given `model: :mos6526a`, and the one `Lorenz.d81`
|
|
924
|
+
expects. That is all the `(*1)` cells of `cia1ta`/`cia1tb` measure, and
|
|
925
|
+
it is an ICR difference, not a counter one. Those cells read the ICR on
|
|
926
|
+
the very cycle the underflow flag rises, so the source bit is up while
|
|
927
|
+
IR is not: they read `$01`/`$02`, where a 6526A reads `$81`/`$82`.
|
|
928
|
+
Counter readback is identical on both revisions. `Lorenznew.d81` expects
|
|
929
|
+
the 6526A and must not be mixed into the `lorenz` chain. Its tests run
|
|
930
|
+
one at a time on a 6526A machine in `testbench-cia-new` instead. See
|
|
931
|
+
[CIA 6526A interrupt register](#cia-6526a-interrupt-register) for what
|
|
932
|
+
changes.
|
|
781
933
|
- Timer B's cascade decodes CRB, not CRA. Every count source (ø2, a CNT
|
|
782
934
|
edge, a cascaded timer A underflow) drives the same two-stage
|
|
783
935
|
count-enable pipeline, so a timer A underflow decrements timer B two
|
|
@@ -805,6 +957,65 @@ VICE x64sc's `vicii_fetch_graphics` and `draw_graphics8` for the 6569.
|
|
|
805
957
|
acknowledge* and *6526 timer B bug* groups guard the three ICR rules
|
|
806
958
|
above, on a bare CIA.
|
|
807
959
|
|
|
960
|
+
## CIA 6526A interrupt register
|
|
961
|
+
|
|
962
|
+
The 6526A (`CIA.new(model: :mos6526a)`, `Computer.new(cia_model:
|
|
963
|
+
:mos6526a)`), fitted to the C64C, differs from the 6526 only in its
|
|
964
|
+
interrupt control register. Its timers and shift register run the rules
|
|
965
|
+
above unchanged: every `testbench-cia-new` row passes on the 6526's
|
|
966
|
+
timer and serial code, the long counter sweeps `cia1tanew`, `cia1tbnew`,
|
|
967
|
+
`cia2tanew` and `cia2tbnew` included. VICE's `ciatimer.c` has no model
|
|
968
|
+
check either. Each rule below is gated on the model in
|
|
969
|
+
[`cia/interrupt_register.rb`](../lib/badline/cia/interrupt_register.rb),
|
|
970
|
+
and each was knocked out: removing it fails the rows named.
|
|
971
|
+
|
|
972
|
+
- **IR on the flag's cycle.** A source flag whose mask bit is set raises
|
|
973
|
+
IR on the cycle the flag rises, not a cycle later. The exception is a
|
|
974
|
+
flag that rises on the cycle after an ICR read: that one raises IR a
|
|
975
|
+
cycle later, as on the 6526 (VICE's `rdi + 1 == rclk`).
|
|
976
|
+
- The early IR is pinned by `CIA/irqdelay/irqdelay-new`,
|
|
977
|
+
`irqdelay-oneshot-new`, `irqdelay2-new`, `irqdelay-cia1-4-new` and
|
|
978
|
+
`irqdelay-cia1-oneshot-4-new`, `interrupts/branchquirk/branchquirk-new`
|
|
979
|
+
and `branchquirk-nminew`, `interrupts/cia-int/cia-int-irq-new` and
|
|
980
|
+
`cia-int-nmi-new`, `interrupts/irq-ackn-bug/cia1new` and `cia2new`,
|
|
981
|
+
`interrupts/irqnmi/irqnmi-new`, `CIA/CIA-AcountsB/cmp-b-counts-a-new`,
|
|
982
|
+
`CIA/cia-timer/cia-timer-newcias`, `CIA/timerbasics/timer_test1_new`
|
|
983
|
+
and `CIA/dd0dtest/dd0dtest`.
|
|
984
|
+
- The exception after a read is pinned by `cia-int-irq-new`,
|
|
985
|
+
`cia-int-nmi-new` and `dd0dtest` test 17, which fail when every flag
|
|
986
|
+
raises IR at once.
|
|
987
|
+
- **A read sees an IR about to rise.** A read on the cycle before IR is
|
|
988
|
+
due returns IR set, and cancels the assert as on the 6526. On the 6526,
|
|
989
|
+
only a read on the next cycle sees it.
|
|
990
|
+
- Pinned by `dd0dtest` test 17. The two reads of `inc $dd0d,x` land on
|
|
991
|
+
the cycles before and of timer A's flag, so the second sees `$81` and
|
|
992
|
+
the RMW writes `$81`/`$82` back, arming timer B, whose NMI handler
|
|
993
|
+
then reads `$82`. Without the rule the second read sees `$01`, timer B
|
|
994
|
+
stays masked and no NMI comes.
|
|
995
|
+
- **The acknowledge holds every bit.** A read releases the interrupt line
|
|
996
|
+
at once, but the bits it read, sources and IR alike, stay readable on
|
|
997
|
+
the next cycle, along with anything flagged since. They clear the cycle
|
|
998
|
+
after that. The 6526 holds only IR (see *Old-CIA acknowledge* above).
|
|
999
|
+
- Pinned by `dd0dtest` tests 18 and 19. Their `inc $dd0d,x` reads `$81`
|
|
1000
|
+
on the flag cycle and again on the next cycle, where a 6526 reads
|
|
1001
|
+
`$80`, so the RMW writes `$82` and arms timer B.
|
|
1002
|
+
- **A mask write raises IR a cycle later**, where the 6526 takes two.
|
|
1003
|
+
- Pinned by Lorenz `imrnew`.
|
|
1004
|
+
- **No timer B bug.** An underflow on the cycle after an ICR read keeps
|
|
1005
|
+
its flag.
|
|
1006
|
+
- Pinned by `CIA/ciavarious/cia3new`, `cia3anew`, `cia4new` and
|
|
1007
|
+
`cia8new` (the readme's old-versus-new cells) and
|
|
1008
|
+
`CIA/cia-timer/cia-timer-newcias`.
|
|
1009
|
+
- Two of VICE's model checks are left out, because no testprog tells
|
|
1010
|
+
them apart. VICE stops a mask write from raising IR on the cycle after
|
|
1011
|
+
an ICR read, and applies the 6526's mask cancel (*Old-CIA mask cancel*
|
|
1012
|
+
above) only to the 6526. Adding the first, or dropping the second for
|
|
1013
|
+
the 6526A, passes every `testbench-cia-new` row, so the 6526A keeps the
|
|
1014
|
+
6526's behaviour on both.
|
|
1015
|
+
- Spec guard: *the 6526A interrupt register* in
|
|
1016
|
+
[`cia_spec.rb`](../spec/badline/cia_spec.rb), one example per rule,
|
|
1017
|
+
each failing when its rule is knocked out.
|
|
1018
|
+
|
|
808
1019
|
## CIA serial shift register
|
|
809
1020
|
|
|
810
1021
|
- The register counts itself empty on the 15th timer A underflow, when the
|
|
@@ -870,6 +1081,15 @@ VICE x64sc's `vicii_fetch_graphics` and `draw_graphics8` for the 6569.
|
|
|
870
1081
|
before the KERNAL does.
|
|
871
1082
|
- Spec guard: *when powered on* in
|
|
872
1083
|
[`address_bus_spec.rb`](../spec/badline/address_bus_spec.rb).
|
|
1084
|
+
- A write to `$00` or `$01` goes to the port, and the RAM below takes the
|
|
1085
|
+
byte the VIC fetched in the phi1 half of the same cycle, as in VICE's
|
|
1086
|
+
`zero_store`. Only the VIC reads that RAM.
|
|
1087
|
+
- Pinned by `general/ram0001/test1`, which puts the byte in `$3fff` for
|
|
1088
|
+
the idle fetch, and `general/fuxxortest/ef2-inst4a`, which uses two
|
|
1089
|
+
sprite pointer fetches. Both read the RAM back through sprite
|
|
1090
|
+
collisions.
|
|
1091
|
+
- Spec guard: *when a program writes to the port* in
|
|
1092
|
+
[`address_bus_spec.rb`](../spec/badline/address_bus_spec.rb).
|
|
873
1093
|
|
|
874
1094
|
## SID oscillator
|
|
875
1095
|
|
|
@@ -914,6 +1134,15 @@ VICE x64sc's `vicii_fetch_graphics` and `draw_graphics8` for the 6569.
|
|
|
914
1134
|
[`sid_spec.rb`](../spec/badline/sid_spec.rb): a catch-up that
|
|
915
1135
|
fast-forwards across a rise carries the shift still in flight into the
|
|
916
1136
|
next span.
|
|
1137
|
+
- Setting the test bit onto noise combined with another waveform writes
|
|
1138
|
+
that combination's output back into the LFSR first, from the accumulator
|
|
1139
|
+
it had, before the bit clears it.
|
|
1140
|
+
- Pinned by `SID/noiselfsrinit`'s `simple` and `scan` (both chips). Their
|
|
1141
|
+
`$f8`/`$80` pairs set the test bit onto all four waveforms from noise
|
|
1142
|
+
alone, and zero the register only if this writes back. The release that
|
|
1143
|
+
follows goes to noise alone, which writes nothing back (below).
|
|
1144
|
+
- Spec guard: *as the test bit rises* in
|
|
1145
|
+
[`sid/waveform_spec.rb`](../spec/badline/sid/waveform_spec.rb).
|
|
917
1146
|
- The test bit does not clear the LFSR. It stalls it halfway through a
|
|
918
1147
|
shift with bit 22 forced high. While the bit is held, every bit bleeds up
|
|
919
1148
|
to `$7fffff`: after `$950000` cycles on the 8580 (`SID/bitfade`'s
|
|
@@ -933,28 +1162,33 @@ VICE x64sc's `vicii_fetch_graphics` and `draw_graphics8` for the 6569.
|
|
|
933
1162
|
that varies with the chip's temperature (`SID/wf12nsr`'s
|
|
934
1163
|
`quicktest.prg` on a 6581), which nothing scored depends on.
|
|
935
1164
|
- Spec guard: *bleeding through a held test bit* in
|
|
936
|
-
[`sid/waveform_spec.rb`](../spec/badline/sid/waveform_spec.rb).
|
|
1165
|
+
[`sid/waveform_spec.rb`](../spec/badline/sid/waveform_spec.rb).
|
|
1166
|
+
- Before the bit clocks in on release, the old waveform's output is
|
|
937
1167
|
written back only for some waveform changes. Noise has to have been
|
|
938
|
-
combined before the release and still be
|
|
939
|
-
noise alone writes nothing back
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
1168
|
+
combined before the release and still be combined after it, so a change
|
|
1169
|
+
to noise alone writes nothing back. A change to pulse+noise writes
|
|
1170
|
+
nothing back, nor does a change from pulse+noise to sawtooth+noise. On
|
|
1171
|
+
the 6581, trading triangle for sawtooth or back writes nothing back. The
|
|
1172
|
+
rule follows libresidfp's `do_writeback`. What is written is the
|
|
1173
|
+
writeback shape below, not the OSC3 read. Two more cases are not in that
|
|
1174
|
+
rule. On the 6581, a change that keeps only pulse+noise of the old
|
|
1175
|
+
waveform (`D`/`E`/`F`→`C`, `D`→`E`) writes the three lowest noise lines
|
|
1176
|
+
low (`$f8`). On the 8580, a change from pulse+noise to all four
|
|
1177
|
+
(`C`→`F`) writes every line low.
|
|
1178
|
+
- Pinned by `SID/wb_testsuite` (the `9`/`A`/`D`/`E`/`F`→`8` rows on both
|
|
948
1179
|
chips, and the 6581's `9`↔`A`, `9`/`A`→`C` and `D`→`A` rows) and by
|
|
949
1180
|
`SID/noisewriteback`'s `noise_writeback_test1`. The 6581's `$f8` case
|
|
950
1181
|
is pinned by its `D`→`C`, `D`→`E`, `E`→`C` and `F`→`C` rows, and
|
|
951
1182
|
dropping it fails all four. The 8580's `C`→`A` row pins the
|
|
952
1183
|
pulse+noise to sawtooth+noise exception: without it the release writes
|
|
953
|
-
the 8580's `$fc` and the row fails.
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1184
|
+
the 8580's `$fc` and the row fails. The 8580's `C`→`F` row pins the
|
|
1185
|
+
all-low case: writing the old waveform's `$fc` back makes the first read
|
|
1186
|
+
`$fc`, where the row expects the `$6c` that `F`→`F` reads. Both 8580
|
|
1187
|
+
samplings in `SID/wb_testsuite/samplings` read `$6c` for `C`→`F`.
|
|
1188
|
+
- `F`→`8` wrote back until the test bit's rise (above) took over
|
|
1189
|
+
zeroing the register for `SID/noiselfsrinit`. Every sampling in
|
|
1190
|
+
`SID/wb_testsuite/samplings`, both chips, reads `F`→`8` as a plain
|
|
1191
|
+
shift.
|
|
958
1192
|
- Spec guard: *as the test bit falls* in
|
|
959
1193
|
[`sid/waveform_spec.rb`](../spec/badline/sid/waveform_spec.rb).
|
|
960
1194
|
- A combined waveform shorts the shapers onto the lines the oscillator reads
|
|
@@ -998,11 +1232,15 @@ VICE x64sc's `vicii_fetch_graphics` and `draw_graphics8` for the 6569.
|
|
|
998
1232
|
fails wfc and the `C`→`9`/`C`→`E` rows; applying the lone-line rule to
|
|
999
1233
|
the read as well makes `noise_writeback_test2` read `$00` on both
|
|
1000
1234
|
chips.
|
|
1001
|
-
-
|
|
1002
|
-
never
|
|
1003
|
-
the
|
|
1004
|
-
|
|
1005
|
-
|
|
1235
|
+
- Can't pass: the 6581's pulse+noise row of `SID/wf12nsr`. The program
|
|
1236
|
+
never writes voice 3's pulse width, so the row depends on whatever width
|
|
1237
|
+
the machine it was recorded on held. The 6581 reference reads `$00`
|
|
1238
|
+
right after the release into pulse+noise at frequency `$ffff`, as if
|
|
1239
|
+
the pulse was still low and grounding every line. After power-on or
|
|
1240
|
+
reset the width is zero and the pulse is high. With the width set
|
|
1241
|
+
before the program runs, the row passes from `$1a0` up and fails from
|
|
1242
|
+
`$198` down. The 8580 program passes from a zero width. `SID/wf12nsr`'s `quicktest.prg` doesn't set the width
|
|
1243
|
+
either. The row stays failing.
|
|
1006
1244
|
- Spec guard: *writes a lone line of a noise combination back low*, *with
|
|
1007
1245
|
pulse+noise on the 8580* and *as the test bit falls* in
|
|
1008
1246
|
[`sid/waveform_spec.rb`](../spec/badline/sid/waveform_spec.rb).
|
|
@@ -1110,9 +1348,13 @@ VICE x64sc's `vicii_fetch_graphics` and `draw_graphics8` for the 6569.
|
|
|
1110
1348
|
- A tune's init and play routines must run with the ROMs banked to match
|
|
1111
1349
|
the address they live at, following libsidplayfp's iomap: `$37` below
|
|
1112
1350
|
`$a000`, `$36` under BASIC, `$34` in the `$d000` I/O window, `$35` under
|
|
1113
|
-
the KERNAL.
|
|
1114
|
-
|
|
1351
|
+
the KERNAL. A routine below `$a000` gets `$36` too when the tune's image
|
|
1352
|
+
reaches under BASIC, as VSID does, since it may call into that part.
|
|
1353
|
+
Restore `$01` afterwards so the caller's banking survives. This entry is
|
|
1354
|
+
the rule, and it should outlive whichever code carries it.
|
|
1115
1355
|
- Derived against six OneLoad64 tunes (Galway, Tel, Gray, Dunn, Cooksey).
|
|
1356
|
+
Green Beret's init sits at `$9fe3` and calls its own code at `$aaca`,
|
|
1357
|
+
which forces the `$36` below `$a000`.
|
|
1116
1358
|
`SIDFile#bank_for` is the one copy of the map: `Driver` wraps both calls
|
|
1117
1359
|
in a `$01` save/bank/restore, and `BarePlayer#dispatch` pokes it before
|
|
1118
1360
|
handing the CPU the stub.
|
data/exe/badline-ruby
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
lib = File.expand_path("../lib", __dir__)
|
|
5
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
|
+
|
|
7
|
+
require "badline/options"
|
|
8
|
+
|
|
9
|
+
begin
|
|
10
|
+
options = Badline::Options.parse(ARGV)
|
|
11
|
+
rescue Badline::Options::Error => e
|
|
12
|
+
warn "badline-ruby: #{e.message}"
|
|
13
|
+
warn "Try 'badline-ruby --help' for more information."
|
|
14
|
+
exit 1
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if options.help?
|
|
18
|
+
puts options.help
|
|
19
|
+
exit
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
jit_available = defined?(RubyVM::YJIT) && !RubyVM::YJIT.enabled?
|
|
23
|
+
RubyVM::YJIT.enable if options.jit? && jit_available
|
|
24
|
+
|
|
25
|
+
require "badline"
|
|
26
|
+
|
|
27
|
+
if options.headless?
|
|
28
|
+
begin
|
|
29
|
+
Badline::Audio::CLI.new(options, sink: Badline::Audio::SDLSink.method(:new),
|
|
30
|
+
console: Badline::Audio::Console.method(:new)).run
|
|
31
|
+
rescue Badline::Audio::CLI::Error => e
|
|
32
|
+
warn "badline-ruby: #{e.message}"
|
|
33
|
+
exit 1
|
|
34
|
+
rescue Interrupt
|
|
35
|
+
exit 130
|
|
36
|
+
end
|
|
37
|
+
exit
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
require "badline/gui"
|
|
42
|
+
rescue Badline::SDL::Error => e
|
|
43
|
+
warn "badline-ruby: can't open the window: #{e.message}. " \
|
|
44
|
+
"Install SDL2 with 'brew install sdl2' or 'apt install libsdl2-2.0-0'."
|
|
45
|
+
exit 1
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
begin
|
|
49
|
+
application = Badline::GUI::Application.new(media_path: options.media_path, autostart: options.autostart?,
|
|
50
|
+
song: options.song, sid_model: options.sid_model,
|
|
51
|
+
sound: options.sound?, disk: { read_only: options.read_only? })
|
|
52
|
+
rescue Badline::Storage::SIDFile::FormatError, Badline::Storage::T64::FormatError,
|
|
53
|
+
Badline::Storage::TAP::FormatError, Badline::Storage::CRTFile::FormatError,
|
|
54
|
+
Badline::Cartridge::UnsupportedTypeError => e
|
|
55
|
+
warn "badline-ruby: #{options.media_path}: #{e.message}"
|
|
56
|
+
exit 1
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
application.run
|
data/lib/badline/address_bus.rb
CHANGED
|
@@ -49,10 +49,14 @@ module Badline
|
|
|
49
49
|
|
|
50
50
|
attr_reader :io_port, :ram, :basic_rom, :character_rom, :kernal_rom,
|
|
51
51
|
:vic, :sid, :color_ram, :cia1, :cia2, :keyboard, :joystick1, :joystick2,
|
|
52
|
-
:control_ports, :cartridge, :ultimax, :phi1_ultimax, :datasette
|
|
52
|
+
:control_ports, :cartridge, :ultimax, :phi1_ultimax, :datasette, :region, :video_ram
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
# ram_expansion fits a +60K (:plus60k) or +256K (:plus256k).
|
|
55
|
+
def initialize(sid_model: :mos6581, cia_model: :mos6526, vic_model: :mos6569, region: Region::PAL,
|
|
56
|
+
ram_expansion: nil)
|
|
57
|
+
@region = region
|
|
55
58
|
@ram = Memory.new(RAM_POWER_ON, length: 2**16, start: 0)
|
|
59
|
+
@ram_expansion = RAMExpansion.build(ram_expansion, @ram) { update_overlays! }
|
|
56
60
|
@cartridge = nil
|
|
57
61
|
@debug_register = nil
|
|
58
62
|
|
|
@@ -64,9 +68,9 @@ module Badline
|
|
|
64
68
|
@joystick1 = Joystick.new
|
|
65
69
|
@joystick2 = Joystick.new
|
|
66
70
|
@control_ports = ControlPorts.new(keyboard: @keyboard, joystick1: @joystick1, joystick2: @joystick2)
|
|
67
|
-
@vic = VIC.new(self)
|
|
68
|
-
@cia1 = CIA.new(start: 0xdc00, peripheral: @control_ports)
|
|
69
|
-
@cia2 = CIA.new(start: 0xdd00)
|
|
71
|
+
@vic = VIC.new(self, model: vic_model, region:)
|
|
72
|
+
@cia1 = CIA.new(start: 0xdc00, peripheral: @control_ports, model: cia_model, region:)
|
|
73
|
+
@cia2 = CIA.new(start: 0xdd00, model: cia_model, region:)
|
|
70
74
|
@control_ports.port_a_source = @cia1
|
|
71
75
|
@cia1.on_port_b4_change { |high| @vic.lightpen_level(high) }
|
|
72
76
|
@sid = SID.new(model: sid_model, pots: @control_ports)
|
|
@@ -97,11 +101,14 @@ module Badline
|
|
|
97
101
|
|
|
98
102
|
def power_on!
|
|
99
103
|
@ram.clear!(RAM_POWER_ON)
|
|
104
|
+
@ram_expansion.power_on!
|
|
100
105
|
end
|
|
101
106
|
|
|
102
|
-
# The
|
|
103
|
-
# The floating bits keep
|
|
104
|
-
|
|
107
|
+
# The RES line clears the 6510 port's direction and output registers,
|
|
108
|
+
# and a RAM expansion's bank register. The port's floating bits keep
|
|
109
|
+
# their charge.
|
|
110
|
+
def reset!
|
|
111
|
+
@ram_expansion.reset!
|
|
105
112
|
@port_ddr = 0x00
|
|
106
113
|
@port_out = 0x00
|
|
107
114
|
update_port!
|
|
@@ -124,8 +131,12 @@ module Badline
|
|
|
124
131
|
@read_pages[addr >> 8].peek(addr)
|
|
125
132
|
end
|
|
126
133
|
|
|
134
|
+
# A write to $00 or $01 goes to the port, but the RAM below is still
|
|
135
|
+
# write-enabled, and takes whatever is left on the bus: the byte the VIC
|
|
136
|
+
# fetched in the phi1 half of the cycle.
|
|
127
137
|
def poke(addr, value)
|
|
128
138
|
if addr < 0x02
|
|
139
|
+
@ram.poke(addr, @vic.phi1_data)
|
|
129
140
|
addr.zero? ? @port_ddr = value : @port_out = value
|
|
130
141
|
update_port!
|
|
131
142
|
else
|
|
@@ -162,8 +173,8 @@ module Badline
|
|
|
162
173
|
def update_overlays!
|
|
163
174
|
@ultimax = @cartridge ? @cartridge.ultimax? : false
|
|
164
175
|
@phi1_ultimax = @cartridge ? @cartridge.phi1_ultimax? : false
|
|
165
|
-
@
|
|
166
|
-
@
|
|
176
|
+
@ram_expansion.map(@read_pages, @write_pages)
|
|
177
|
+
@video_ram = @ram_expansion.video_ram
|
|
167
178
|
|
|
168
179
|
@ultimax ? map_ultimax_pages : map_banked_pages
|
|
169
180
|
end
|
|
@@ -238,6 +249,7 @@ module Badline
|
|
|
238
249
|
}.each do |chip, pages|
|
|
239
250
|
pages.each { |p| @read_pages[p] = @write_pages[p] = chip }
|
|
240
251
|
end
|
|
252
|
+
@ram_expansion.map_io(@read_pages, @write_pages)
|
|
241
253
|
@read_pages[0xd7] = @write_pages[0xd7] = @debug_register if @debug_register
|
|
242
254
|
map_cartridge_io if @cartridge
|
|
243
255
|
end
|
data/lib/badline/audio/aiff.rb
CHANGED
|
@@ -25,11 +25,16 @@ module Badline
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# Sign bit, 15-bit biased exponent, then a 64-bit mantissa that keeps
|
|
28
|
-
# its leading one rather than hiding it.
|
|
28
|
+
# its leading one rather than hiding it, packed as two 32-bit halves.
|
|
29
29
|
def extended(value)
|
|
30
30
|
exponent = EXPONENT_BIAS + value.bit_length - 1
|
|
31
|
-
mantissa
|
|
32
|
-
|
|
31
|
+
[exponent, *mantissa(value, 64 - value.bit_length)].pack("nNN")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def mantissa(value, shift)
|
|
35
|
+
return [value << (shift - 32), 0] if shift >= 32
|
|
36
|
+
|
|
37
|
+
[value >> (32 - shift), (value & ((1 << (32 - shift)) - 1)) << shift]
|
|
33
38
|
end
|
|
34
39
|
end
|
|
35
40
|
end
|
data/lib/badline/audio/cli.rb
CHANGED
|
@@ -2,19 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Audio
|
|
5
|
-
# Runs `badline-
|
|
6
|
-
# its length, then plays it or renders it
|
|
7
|
-
# terminal, the console lets the listener step
|
|
5
|
+
# Runs `badline-ruby --headless` or `--audio-out` once its options have
|
|
6
|
+
# parsed: picks the song and its length, then plays it or renders it
|
|
7
|
+
# to a file. Played on a terminal, the console lets the listener step
|
|
8
|
+
# between songs.
|
|
8
9
|
class CLI
|
|
9
10
|
class Error < StandardError; end
|
|
10
11
|
|
|
11
|
-
# `sink
|
|
12
|
-
# for and whether it has to be exact
|
|
13
|
-
|
|
12
|
+
# Playing needs `sink`, which builds the audio device given the rate
|
|
13
|
+
# to ask for and whether it has to be exact, and, on a terminal,
|
|
14
|
+
# `console`, which builds the terminal to play on given the input and
|
|
15
|
+
# output. The front end hands them in, so the native badline can hand
|
|
16
|
+
# in its own: badline-ruby's are SDLSink and Console.
|
|
17
|
+
def initialize(options, sink: nil, console: nil, out: $stdout, input: $stdin)
|
|
14
18
|
@options = options
|
|
15
19
|
@out = out
|
|
16
20
|
@input = input
|
|
17
21
|
@sink = sink
|
|
22
|
+
@console = console
|
|
18
23
|
@lengths = {}
|
|
19
24
|
end
|
|
20
25
|
|
|
@@ -39,7 +44,7 @@ module Badline
|
|
|
39
44
|
def seconds = length(song)
|
|
40
45
|
|
|
41
46
|
def length(song)
|
|
42
|
-
@lengths[song] ||= @options.seconds || songlength(song) ||
|
|
47
|
+
@lengths[song] ||= @options.seconds || songlength(song) || @options.fallback_seconds
|
|
43
48
|
end
|
|
44
49
|
|
|
45
50
|
def sid_model = @options.sid_model || tune.sid_model
|
|
@@ -57,9 +62,9 @@ module Badline
|
|
|
57
62
|
def render
|
|
58
63
|
renderer = renderer(song, @options.rate)
|
|
59
64
|
@out.puts describe
|
|
60
|
-
@out.puts "Rendering #{seconds}s for the #{model_name} to #{@options.
|
|
65
|
+
@out.puts "Rendering #{seconds}s for the #{model_name} to #{@options.audio_out} at #{@options.rate} Hz..."
|
|
61
66
|
started = now
|
|
62
|
-
renderer.render(@options.
|
|
67
|
+
renderer.render(@options.audio_out) { |done| progress(done) }
|
|
63
68
|
report(now - started)
|
|
64
69
|
end
|
|
65
70
|
|
|
@@ -81,7 +86,7 @@ module Badline
|
|
|
81
86
|
end
|
|
82
87
|
|
|
83
88
|
def play_interactively(sink)
|
|
84
|
-
console =
|
|
89
|
+
console = @console.call(input: @input, output: @out)
|
|
85
90
|
jukebox = Jukebox.new(sink, console, songs: tune.songs, renderer: method(:renderer), length: method(:length))
|
|
86
91
|
console.session do
|
|
87
92
|
console.header([tune.name, tune.author, tune.released].reject(&:empty?) +
|
|
@@ -92,7 +97,7 @@ module Badline
|
|
|
92
97
|
|
|
93
98
|
def open_sink
|
|
94
99
|
@sink.call(rate: @options.rate, exact_rate: @options.rate_given?)
|
|
95
|
-
rescue
|
|
100
|
+
rescue Playback::DeviceError => e
|
|
96
101
|
raise Error, "can't open the audio device: #{e.message}"
|
|
97
102
|
end
|
|
98
103
|
|
|
@@ -124,7 +129,7 @@ module Badline
|
|
|
124
129
|
def report(elapsed)
|
|
125
130
|
@out.print "\r" unless @options.quiet?
|
|
126
131
|
@out.puts format("Wrote %<path>s in %<elapsed>.1fs (%<speed>.2fx real time).",
|
|
127
|
-
path: @options.
|
|
132
|
+
path: @options.audio_out, elapsed:, speed: seconds / elapsed)
|
|
128
133
|
end
|
|
129
134
|
|
|
130
135
|
def now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|