badline 0.1.0 → 0.2.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.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +185 -0
  3. data/CLAUDE.md +217 -0
  4. data/CODE_OF_CONDUCT.md +92 -0
  5. data/CONTRIBUTING.md +51 -0
  6. data/README.md +224 -31
  7. data/doc/pinned-behaviour.md +1117 -0
  8. data/exe/badline +22 -2
  9. data/exe/badline-sid +34 -0
  10. data/lib/badline/address_bus.rb +96 -25
  11. data/lib/badline/audio/aiff.rb +36 -0
  12. data/lib/badline/audio/bare_player.rb +129 -0
  13. data/lib/badline/audio/cli.rb +133 -0
  14. data/lib/badline/audio/console.rb +75 -0
  15. data/lib/badline/audio/jukebox.rb +82 -0
  16. data/lib/badline/audio/machine_player.rb +57 -0
  17. data/lib/badline/audio/options.rb +137 -0
  18. data/lib/badline/audio/pcm_writer.rb +57 -0
  19. data/lib/badline/audio/playback.rb +108 -0
  20. data/lib/badline/audio/renderer.rb +85 -0
  21. data/lib/badline/audio/sdl_sink.rb +102 -0
  22. data/lib/badline/audio/wav.rb +23 -0
  23. data/lib/badline/audio.rb +14 -0
  24. data/lib/badline/cartridge/action_replay.rb +111 -0
  25. data/lib/badline/cartridge/atomic_power.rb +24 -0
  26. data/lib/badline/cartridge/bank.rb +81 -0
  27. data/lib/badline/cartridge/comal80.rb +48 -0
  28. data/lib/badline/cartridge/dinamic.rb +34 -0
  29. data/lib/badline/cartridge/easy_flash.rb +122 -0
  30. data/lib/badline/cartridge/epyx_fastload.rb +71 -0
  31. data/lib/badline/cartridge/final_cartridge3.rb +77 -0
  32. data/lib/badline/cartridge/flash.rb +265 -0
  33. data/lib/badline/cartridge/freezer.rb +31 -0
  34. data/lib/badline/cartridge/fun_play.rb +38 -0
  35. data/lib/badline/cartridge/g_mod2.rb +62 -0
  36. data/lib/badline/cartridge/game_system.rb +40 -0
  37. data/lib/badline/cartridge/kcs_power.rb +72 -0
  38. data/lib/badline/cartridge/mach5.rb +34 -0
  39. data/lib/badline/cartridge/magic_desk.rb +6 -3
  40. data/lib/badline/cartridge/ocean.rb +11 -12
  41. data/lib/badline/cartridge/pagefox.rb +56 -0
  42. data/lib/badline/cartridge/retro_replay.rb +276 -0
  43. data/lib/badline/cartridge/rex_utility.rb +33 -0
  44. data/lib/badline/cartridge/rgcd.rb +48 -0
  45. data/lib/badline/cartridge/simons_basic.rb +40 -0
  46. data/lib/badline/cartridge/standard.rb +3 -19
  47. data/lib/badline/cartridge/super_games.rb +36 -0
  48. data/lib/badline/cartridge/westermann.rb +34 -0
  49. data/lib/badline/cartridge/zaxxon.rb +50 -0
  50. data/lib/badline/cartridge.rb +132 -16
  51. data/lib/badline/cia/interrupt_register.rb +102 -0
  52. data/lib/badline/cia/serial.rb +219 -0
  53. data/lib/badline/cia/timer.rb +43 -31
  54. data/lib/badline/cia.rb +138 -88
  55. data/lib/badline/color_memory.rb +14 -5
  56. data/lib/badline/computer.rb +97 -12
  57. data/lib/badline/control_ports.rb +54 -9
  58. data/lib/badline/cpu/addressing.rb +131 -0
  59. data/lib/badline/cpu/microcode.rb +169 -0
  60. data/lib/badline/cpu/operations.rb +88 -0
  61. data/lib/badline/cpu/stack_operations.rb +82 -0
  62. data/lib/badline/cpu.rb +109 -168
  63. data/lib/badline/cycleable.rb +3 -0
  64. data/lib/badline/datasette.rb +81 -0
  65. data/lib/badline/debug_register.rb +28 -0
  66. data/lib/badline/gui/application.rb +79 -18
  67. data/lib/badline/gui/gamepads.rb +97 -0
  68. data/lib/badline/gui/joy_map.rb +15 -7
  69. data/lib/badline/gui.rb +1 -0
  70. data/lib/badline/input/mouse1351.rb +41 -0
  71. data/lib/badline/input/paddles.rb +36 -0
  72. data/lib/badline/input.rb +4 -0
  73. data/lib/badline/instruction_set/arithmetic.rb +16 -21
  74. data/lib/badline/instruction_set/bitwise.rb +17 -22
  75. data/lib/badline/instruction_set/branch.rb +20 -19
  76. data/lib/badline/instruction_set/flag.rb +7 -7
  77. data/lib/badline/instruction_set/illegal.rb +14 -22
  78. data/lib/badline/instruction_set/inc_dec.rb +8 -10
  79. data/lib/badline/instruction_set/stack.rb +5 -57
  80. data/lib/badline/instruction_set/transfer.rb +9 -9
  81. data/lib/badline/instruction_set.rb +9 -31
  82. data/lib/badline/interrupts.rb +104 -0
  83. data/lib/badline/joystick.rb +9 -9
  84. data/lib/badline/kernal_trap/channel.rb +51 -0
  85. data/lib/badline/kernal_trap/drive/status.rb +49 -0
  86. data/lib/badline/kernal_trap/drive.rb +277 -0
  87. data/lib/badline/kernal_trap/file.rb +5 -25
  88. data/lib/badline/kernal_trap/load.rb +86 -24
  89. data/lib/badline/kernal_trap/routine.rb +51 -0
  90. data/lib/badline/kernal_trap/save.rb +46 -14
  91. data/lib/badline/kernal_trap/serial.rb +171 -0
  92. data/lib/badline/kernal_trap.rb +4 -0
  93. data/lib/badline/keyboard.rb +40 -16
  94. data/lib/badline/media.rb +48 -10
  95. data/lib/badline/memory.rb +5 -0
  96. data/lib/badline/roms/eapi/LICENSE.md +15 -0
  97. data/lib/badline/roms/eapi/README +41 -0
  98. data/lib/badline/roms/eapi/eapi-am29f040-14 +0 -0
  99. data/lib/badline/sid/decimator.rb +46 -0
  100. data/lib/badline/sid/envelope.rb +232 -0
  101. data/lib/badline/sid/filter.rb +213 -0
  102. data/lib/badline/sid/voice.rb +61 -0
  103. data/lib/badline/sid/waveform/combined.rb +126 -0
  104. data/lib/badline/sid/waveform/fast_forward.rb +105 -0
  105. data/lib/badline/sid/waveform/noise_writeback.rb +82 -0
  106. data/lib/badline/sid/waveform.rb +289 -0
  107. data/lib/badline/sid.rb +274 -7
  108. data/lib/badline/status.rb +251 -23
  109. data/lib/badline/storage/crt_file.rb +18 -2
  110. data/lib/badline/storage/d64_image.rb +6 -0
  111. data/lib/badline/storage/d71_image.rb +4 -0
  112. data/lib/badline/storage/d81_image.rb +4 -0
  113. data/lib/badline/storage/disk_image.rb +99 -13
  114. data/lib/badline/storage/host_directory.rb +30 -4
  115. data/lib/badline/storage/sid_file.rb +274 -0
  116. data/lib/badline/storage/song_lengths.rb +65 -0
  117. data/lib/badline/storage/t64.rb +67 -0
  118. data/lib/badline/storage/tap.rb +68 -0
  119. data/lib/badline/storage.rb +19 -0
  120. data/lib/badline/time_of_day.rb +105 -52
  121. data/lib/badline/version.rb +1 -1
  122. data/lib/badline/vic/bank.rb +14 -6
  123. data/lib/badline/vic/border_mask.rb +76 -0
  124. data/lib/badline/vic/collisions.rb +104 -0
  125. data/lib/badline/vic/color_patches.rb +33 -0
  126. data/lib/badline/vic/display_state.rb +92 -29
  127. data/lib/badline/vic/graphics_mode.rb +78 -53
  128. data/lib/badline/vic/graphics_shifter.rb +140 -0
  129. data/lib/badline/vic/register_log.rb +63 -0
  130. data/lib/badline/vic/registers.rb +3 -0
  131. data/lib/badline/vic/sequencer.rb +163 -137
  132. data/lib/badline/vic/sequencer_output.rb +116 -0
  133. data/lib/badline/vic/sprite/internal_bus.rb +49 -0
  134. data/lib/badline/vic/sprite/shifter.rb +129 -0
  135. data/lib/badline/vic/sprite.rb +275 -65
  136. data/lib/badline/vic/sprites.rb +220 -58
  137. data/lib/badline/vic.rb +389 -55
  138. data/lib/badline.rb +5 -0
  139. data/vendor/.gitkeep +0 -0
  140. metadata +104 -4
data/README.md CHANGED
@@ -1,40 +1,233 @@
1
- ![Build](https://github.com/elektronaut/badline/workflows/Build/badge.svg)
2
- [![Code Climate](https://codeclimate.com/github/elektronaut/badline/badges/gpa.svg)](https://codeclimate.com/github/elektronaut/badline)
3
- [![Code Climate](https://codeclimate.com/github/elektronaut/badline/badges/coverage.svg)](https://codeclimate.com/github/elektronaut/badline)
1
+ [![Version](https://img.shields.io/gem/v/badline.svg?style=flat)](https://rubygems.org/gems/badline)
2
+ [![Build](https://github.com/elektronaut/badline/actions/workflows/build.yml/badge.svg)](https://github.com/elektronaut/badline/actions/workflows/build.yml)
4
3
 
5
4
  # Badline
6
5
 
7
- Badline is a Commodore 64 emulator in written in Ruby. It is cycle accurate,
8
- utilizing Fibers to emulate cycles.
6
+ Badline is a Commodore 64 emulator written in Ruby. It emulates a PAL
7
+ machine one clock cycle at a time, stepping the 6510, the VIC-II, both
8
+ CIAs and the SID together, so raster timing, bad lines and sprite DMA
9
+ are modelled at the cycle level.
9
10
 
10
- Currently the memory map and 6510 CPU is working.
11
+ It runs programs, disk and tape images, cartridges and SID tunes, and
12
+ the SDL2 front end supports the keyboard, joysticks, game controllers,
13
+ paddles and a 1351 mouse. There is no live sound yet, and emulation runs
14
+ slower than a real C64. See [What's emulated](#whats-emulated) for the
15
+ details.
11
16
 
12
- ## TODO
17
+ ## Requirements
13
18
 
14
- - VIC-II emulation
15
- - CIA 1/2
16
- - C1541 emulation
17
- - SID emulation?
19
+ Ruby 4.0 or newer, and SDL2:
20
+
21
+ ```sh
22
+ brew install sdl2 # macOS
23
+ apt install libsdl2-dev # Debian/Ubuntu
24
+ ```
25
+
26
+ ## Installation
27
+
28
+ ```sh
29
+ gem install badline
30
+ ```
31
+
32
+ Or add `gem "badline"` to your Gemfile and run `bundle install`.
33
+
34
+ ## Usage
35
+
36
+ Run `badline` with no arguments to boot to the BASIC prompt, or give it
37
+ something to load:
38
+
39
+ ```sh
40
+ badline # READY.
41
+ badline game.prg # Load and run a program
42
+ badline game.d64 # Mount a disk image as device 8 and load it
43
+ badline game.tap # Insert a tape and load it
44
+ badline game.crt # Attach a cartridge
45
+ badline tune.sid # Play a SID tune
46
+ badline ~/c64 # Mount a directory as device 8
47
+ ```
48
+
49
+ Programs, disk and tape images and SID tunes start automatically, and
50
+ a cartridge starts itself. A mounted directory waits for you to `LOAD`
51
+ from it. `--no-autostart` attaches the media and stops at `READY.`, so
52
+ you can type the `LOAD` yourself. `--song N` picks a subtune of a
53
+ `.sid` file, `--sid 8580` fits the newer SID, and `--disable-jit` runs
54
+ without YJIT, which is otherwise switched on at startup.
55
+ `badline --help` lists the options.
56
+
57
+ ## Media
58
+
59
+ | Format | Handling |
60
+ |--------|----------|
61
+ | `.prg`, `.p00` | Loaded into memory after boot. A program at the BASIC start (`$0801`) is `RUN`, anything else is left for you to `SYS` |
62
+ | `.d64`, `.d71`, `.d81` | Mounted read-only as device 8, then `LOAD"*",8,1` and `RUN` |
63
+ | `.t64` | Mounted read-only as device 8 and loaded like a disk image. The files load by name, and no tape is involved |
64
+ | `.tap` | Inserted in the datasette with PLAY pressed, then `LOAD` and `RUN`. It loads at the speed of a real tape |
65
+ | `.crt` | The hardware types listed under [Cartridges](#whats-emulated). Other types are rejected |
66
+ | `.sid` | PSID and RSID tunes, started through a small driver after boot |
67
+ | A directory | Mounted read-write as device 8. It serves the `.prg` and `.p00` files in it and the contents of any `.t64`, and `SAVE` writes a new `.prg` |
68
+
69
+ There is no 1541. Device 8 works by trapping the KERNAL's `LOAD` and
70
+ `SAVE` routines and its serial bus primitives, so files open by name
71
+ through `OPEN` and `CHRIN` as well. The command channel answers `I`,
72
+ `B-P` and `U1` block reads, which covers loaders that read blocks
73
+ directly. Loaders that upload their own code to the drive with `M-W` and
74
+ `M-E`, and copy protection that reads raw GCR, won't work.
75
+
76
+ ## Playing and rendering SID tunes
77
+
78
+ `badline-sid` plays a `.sid` tune on the host's audio device, or with
79
+ `--output` (or `-o`) renders it to a 16-bit PCM file instead. The
80
+ output extension picks the format, `.wav` or `.aiff`.
81
+
82
+ ```sh
83
+ badline-sid tune.sid # play, length from HVSC
84
+ badline-sid -s 3 tune.sid # play the third subtune
85
+ badline-sid --seconds 180 tune.sid -o out.aiff
86
+ badline-sid -s 3 --rate 48000 tune.sid -o out.wav
87
+ badline-sid --sid 8580 tune.sid
88
+ badline-sid --filter-chunk 1 tune.sid -o out.wav # exact filter, slower
89
+ ```
90
+
91
+ Both modes take the same options. `--song` (or `-s`) picks the subtune,
92
+ counting from 1 as HVSC does, and defaults to the tune's own start
93
+ song. Playback asks the device for 44.1 kHz and takes whatever rate it
94
+ offers, unless `--rate` says otherwise. Ctrl-C stops it.
95
+
96
+ Played on a terminal, `badline-sid` shows the tune's name, author and
97
+ release, the song number and the time played against the song's
98
+ length. `n` or → skips to the next song, `p` or ← goes back one, space
99
+ pauses and `q` quits. `--no-tui`, or output that isn't a terminal,
100
+ gives plain progress output instead.
101
+
102
+ A `.sid` file doesn't store its length, so `badline-sid` looks the
103
+ tune up by MD5 in HVSC's `Songlengths.md5`. It finds the database
104
+ through `--songlengths`, in a `DOCUMENTS` directory in any of the
105
+ tune's parent directories (the layout of an HVSC collection), or under
106
+ `$HVSC_BASE/DOCUMENTS`. Without a database or `--seconds` it runs for
107
+ 60 seconds.
108
+
109
+ PSID tunes run on a CPU and RAM with only the SID clocked, at about
110
+ twice real time, so they play smoothly. RSID tunes set up their own
111
+ interrupts, so they boot a full C64 first and run at about half real
112
+ time. They render fine but stutter when played, and `badline-sid` says
113
+ so when it falls behind. The filter steps four cycles at a time;
114
+ `--filter-chunk 1` steps it every cycle, which is exact and takes about
115
+ twice as long. `badline-sid --help` lists the options.
116
+
117
+ ## Input
118
+
119
+ Keys map by their unshifted symbol, and Shift gives the C64's shifted
120
+ character, not the host's: Shift-2 types `"`. These keys have no
121
+ same-named host key:
122
+
123
+ | C64 | Host |
124
+ |-----|------|
125
+ | `RUN/STOP` | `Escape` |
126
+ | `CLR/HOME` | `Home` |
127
+ | `INST/DEL` | `Backspace` |
128
+ | `CRSR ⇔` / `CRSR ⇕` | `Right` / `Down` (add Shift for left and up) |
129
+ | `←` / `↑` | `Left` / `Up` |
130
+ | `CTRL` | `Left Ctrl` |
131
+ | `C=` | `Left Alt` |
132
+ | `@` | `\` |
133
+ | `:` | `'` |
134
+ | `£` | `End` |
135
+ | `+` / `*` | Keypad `+` / Keypad `*` |
136
+ | `RESTORE` | Not mapped |
137
+
138
+ `Tab` steps through the input modes and `Shift-Tab` steps back. The
139
+ window title shows the current mode:
140
+
141
+ | Mode | What the host drives |
142
+ |------|----------------------|
143
+ | (none) | The keyboard |
144
+ | `[JOY]` | Arrows and Space are joystick 2, `WASD` and Left Shift joystick 1 |
145
+ | `[MOUSE 1]` / `[MOUSE 2]` | A 1351 mouse in control port 1 or 2 |
146
+ | `[PADDLE 1]` / `[PADDLE 2]` | A pair of paddles in control port 1 or 2 |
147
+
148
+ The mouse and paddle modes capture the host mouse until you `Tab` out of
149
+ them. Moving it moves the 1351 or turns the two paddle knobs, and the
150
+ left and right buttons are the 1351's buttons, or the fire buttons of
151
+ paddles A and B. Games differ in which port they read, which is why each
152
+ device has a mode per port.
153
+
154
+ Game controllers work in every mode. The first one is joystick 2 and
155
+ the second is joystick 1. The D-pad and left stick steer, the face and
156
+ shoulder buttons fire, and controllers can be connected or removed while
157
+ the emulator runs.
158
+
159
+ Control port 1's fire line is also the VIC-II's light pen input, so
160
+ joystick 1's fire button and the 1351's left button in port 1 latch the
161
+ light pen registers.
162
+
163
+ ## What's emulated
164
+
165
+ - **6510**: every opcode, documented and undocumented, with per-cycle
166
+ bus behaviour checked against the
167
+ [65x02 single step tests](https://github.com/SingleStepTests/65x02).
168
+ `JAM` opcodes halt the CPU until reset.
169
+ - **Memory**: banking through the 6510 port, including the cartridge
170
+ `EXROM`/`GAME` lines and Ultimax mode.
171
+ - **VIC-II** (PAL 6569): the five standard graphics modes and the
172
+ invalid ones, sprites with multicolour, expansion, priority and
173
+ pixel-level collisions, raster interrupts, bad lines, sprite DMA, the
174
+ border, VIC banks and the light pen.
175
+ - **CIA 1 and 2**: timers, time-of-day clocks with alarms, the serial
176
+ shift register, interrupts, the keyboard matrix with its ghost keys,
177
+ the control ports and the paddle multiplexer.
178
+ - **SID**: the 6581 and the 8580, with oscillators, ring modulation and
179
+ sync, the envelope generator including the ADSR delay bug, the filter,
180
+ and the RC network on the board that removes the DC offset from the
181
+ output. The machine has a 6581 unless a `.sid` tune asks for an 8580
182
+ in its header, and `--sid 6581` or `--sid 8580` overrides either.
183
+ - **Datasette**: `.tap` playback into CIA 1's FLAG line, with the motor
184
+ and sense lines on the 6510 port.
185
+ - **Cartridges**: standard 8K, 16K and Ultimax, Simons' BASIC, Ocean,
186
+ Fun Play / Power Play, Super Games, Epyx FastLoad, Westermann Learning,
187
+ Rex Utility, C64 Game System / System 3, Dinamic, Zaxxon / Super Zaxxon,
188
+ Magic Desk, Comal-80, EasyFlash, Mach 5, Pagefox, RGCD and GMod2, and
189
+ the freezers Action Replay (v4.2 to v6), Atomic Power / Nordic Power,
190
+ Retro Replay / Nordic Replay, Final Cartridge III / III+ and the KCS
191
+ Power Cartridge.
192
+ EasyFlash and GMod2 flash takes writes through the chip's command set
193
+ (program, sector and chip erase, autoselect), so games and EAPI can
194
+ save to it. An EasyFlash image's EAPI is swapped for a bundled copy of
195
+ the Am29F040 EAPI on attach, as VICE does. The writes stay in memory
196
+ and are lost when the emulator quits: the `.crt` file is never
197
+ overwritten. The Retro Replay's flash
198
+ works the same way in flash mode, which the flash jumper enables:
199
+ `Media.attach(computer, path, cartridge: { flash_jumper: true })`, with
200
+ `bank_jumper: true` to run from the second 64K of a 128K image. The
201
+ GMod2 EEPROM and the Retro Replay clock port aren't there.
202
+
203
+ Known gaps:
204
+
205
+ - No live audio in the emulator window. The SID is emulated, but the
206
+ whole machine runs below real time. `badline-sid` plays `.sid` tunes
207
+ on their own.
208
+ - No drive emulation, so fast loaders and anything else that runs code
209
+ on the drive won't work (see [Media](#media)). Disk images are
210
+ read-only.
211
+ - No NTSC machine, no REU, and no `RESTORE` key.
212
+ - The emulator window has no freeze button yet, so a freezer cartridge
213
+ runs its menu but can't freeze a program.
214
+
215
+ ## Contributing
216
+
217
+ Bug reports and pull requests are welcome on
218
+ [GitHub](https://github.com/elektronaut/badline).
219
+ [CONTRIBUTING.md](CONTRIBUTING.md) covers running the tests and the
220
+ commit format, and the project has a
221
+ [code of conduct](CODE_OF_CONDUCT.md).
18
222
 
19
223
  ## License
20
224
 
21
- Copyright 2016 Inge Jørgensen
22
-
23
- Permission is hereby granted, free of charge, to any person obtaining
24
- a copy of this software and associated documentation files (the
25
- "Software"), to deal in the Software without restriction, including
26
- without limitation the rights to use, copy, modify, merge, publish,
27
- distribute, sublicense, and/or sell copies of the Software, and to
28
- permit persons to whom the Software is furnished to do so, subject to
29
- the following conditions:
30
-
31
- The above copyright notice and this permission notice shall be
32
- included in all copies or substantial portions of the Software.
33
-
34
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
36
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
37
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
38
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
39
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
40
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
225
+ Released under the [MIT License](MIT-LICENSE).
226
+
227
+ Badline bundles one piece of third-party software:
228
+ `lib/badline/roms/eapi/eapi-am29f040-14`, the EasyFlash flash driver
229
+ (EAPI) for the Am29F040, © 2009–2010 Thomas 'skoe' Giesel, assembled
230
+ unaltered from [its upstream source](https://gitlab.com/easyflash/eapi).
231
+ It isn't part of badline and is distributed under the zlib licence; see
232
+ [its README](lib/badline/roms/eapi/README) and
233
+ [LICENSE.md](lib/badline/roms/eapi/LICENSE.md).