├── .gitignore ├── CMakeLists.txt ├── README.md ├── logs ├── moons.txt └── turrican2.txt ├── screenshot.png ├── sids ├── Alibi.sid ├── Ballfever.sid ├── Break-up.sid ├── Clarence.sid ├── Cyberworld.sid ├── Faceless.sid ├── GoatLight_tune_1.sid ├── Solomatic_Fly.sid ├── Zimxusaf_I.sid └── pdd_oot.sid └── src ├── cpu.cpp ├── cpu.hpp ├── fx.cpp ├── fx.hpp ├── main.cpp ├── record.cpp ├── record.hpp ├── resid-0.16 ├── AUTHORS ├── COPYING ├── INSTALL ├── NEWS ├── README ├── THANKS ├── TODO ├── VC_CC_SUPPORT.txt ├── envelope.cc ├── envelope.h ├── extfilt.cc ├── extfilt.h ├── filter.cc ├── filter.h ├── pot.cc ├── pot.h ├── sid.cc ├── sid.h ├── siddefs.h ├── siddefs.h.in ├── spline.h ├── version.cc ├── voice.cc ├── voice.h ├── wave.cc ├── wave.h ├── wave6581_PST.cc ├── wave6581_PS_.cc ├── wave6581_P_T.cc ├── wave6581__ST.cc ├── wave8580_PST.cc ├── wave8580_PS_.cc ├── wave8580_P_T.cc └── wave8580__ST.cc ├── sidengine.cpp └── sidengine.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | project(sid-monitor) 4 | 5 | add_compile_options(-std=c++17 -Wall -Ofast) 6 | #add_compile_options(-std=c++17 -Wall -Og -g) 7 | 8 | find_package(SDL2 REQUIRED) 9 | include_directories(${SDL2_INCLUDE_DIRS}) 10 | 11 | file(GLOB SRC 12 | "src/*.cpp" 13 | "src/resid-0.16/*.cc" 14 | ) 15 | 16 | add_executable(${PROJECT_NAME} ${SRC}) 17 | target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARIES}) 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SID Monitor 2 | =========== 3 | 4 | A SID player with piano-roll-like visualization. 5 | If you like it, you may also like [NSF monitor](https://github.com/2bt/nsf-monitor). 6 | 7 | ![image](screenshot.png) 8 | 9 | Each channel is shown in a certain color: 10 | 11 | 12 | 13 | 14 |
Channel 1red
Channel 2green
Channel 3blue
15 | 16 | 17 | ## Controls 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
SPACEToggle playback.
1/2/3Toggle channels.
LEFT/RIGHT 31 | Move through the song.
32 | Hold SHIFT to move faster.
33 | Hold CTRL to move frame by frame. 34 |
UP/DOWN 39 | Scroll up/down. 40 |
BACKSPACEJump to the beginning of the song.
+/-Time zoom.
PAGE DOWN/PAGE UPPitch zoom.
TABToggle SID engine.
BToggle bars.
FToggle filter.
MToggle chip model.
W/S/A/DChange bar offset/width.
</>Switch sub tune.
79 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/screenshot.png -------------------------------------------------------------------------------- /sids/Alibi.sid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/sids/Alibi.sid -------------------------------------------------------------------------------- /sids/Ballfever.sid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/sids/Ballfever.sid -------------------------------------------------------------------------------- /sids/Break-up.sid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/sids/Break-up.sid -------------------------------------------------------------------------------- /sids/Clarence.sid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/sids/Clarence.sid -------------------------------------------------------------------------------- /sids/Cyberworld.sid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/sids/Cyberworld.sid -------------------------------------------------------------------------------- /sids/Faceless.sid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/sids/Faceless.sid -------------------------------------------------------------------------------- /sids/GoatLight_tune_1.sid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/sids/GoatLight_tune_1.sid -------------------------------------------------------------------------------- /sids/Solomatic_Fly.sid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/sids/Solomatic_Fly.sid -------------------------------------------------------------------------------- /sids/Zimxusaf_I.sid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/sids/Zimxusaf_I.sid -------------------------------------------------------------------------------- /sids/pdd_oot.sid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/sids/pdd_oot.sid -------------------------------------------------------------------------------- /src/cpu.cpp: -------------------------------------------------------------------------------- 1 | // this code is based on kb's 6502 code in tinysid, i think. 2 | #include "cpu.hpp" 3 | #include 4 | 5 | enum { 6 | FLAG_C = 1, 7 | FLAG_Z = 2, 8 | FLAG_I = 4, 9 | FLAG_D = 8, 10 | FLAG_B = 16, 11 | FLAG_V = 64, 12 | FLAG_N = 128, 13 | }; 14 | 15 | enum { 16 | ADC, AND, ASL, BCC, BCS, BEQ, BIT, BMI, BNE, BPL, BRK, BVC, BVS, CLC, 17 | CLD, CLI, CLV, CMP, CPX, CPY, DEC, DEX, DEY, EOR, INC, INX, INY, JMP, 18 | JSR, LDA, LDX, LDY, LSR, NOP, ORA, PHA, PHP, PLA, PLP, ROL, ROR, RTI, 19 | RTS, SBC, SEC, SED, SEI, STA, STX, STY, TAX, TAY, TSX, TXA, TXS, TYA, 20 | SLO, AXS, LAX, SAX, RLA, RRA, 21 | XXX, 22 | }; 23 | 24 | enum { IMP, IMM, ABS, ABSX, ABSY, ZP, ZPX, ZPY, IND, INDX, INDY, ACC, REL}; 25 | 26 | const int OPCODE_TABLE[256] = { 27 | BRK, ORA, XXX, SLO, NOP, ORA, ASL, SLO, PHP, ORA, ASL, XXX, XXX, ORA, ASL, SLO, 28 | BPL, ORA, XXX, SLO, NOP, ORA, ASL, SLO, CLC, ORA, XXX, SLO, XXX, ORA, ASL, SLO, 29 | JSR, AND, XXX, RLA, BIT, AND, ROL, RLA, PLP, AND, ROL, XXX, BIT, AND, ROL, RLA, 30 | BMI, AND, XXX, RLA, NOP, AND, ROL, RLA, SEC, AND, XXX, RLA, NOP, AND, ROL, RLA, 31 | RTI, EOR, XXX, XXX, NOP, EOR, LSR, XXX, PHA, EOR, LSR, XXX, JMP, EOR, LSR, XXX, 32 | BVC, EOR, XXX, XXX, NOP, EOR, LSR, XXX, CLI, EOR, XXX, XXX, XXX, EOR, LSR, XXX, 33 | RTS, ADC, XXX, RRA, NOP, ADC, ROR, RRA, PLA, ADC, ROR, XXX, JMP, ADC, ROR, RRA, 34 | BVS, ADC, XXX, RRA, NOP, ADC, ROR, RRA, SEI, ADC, XXX, RRA, XXX, ADC, ROR, RRA, 35 | NOP, STA, NOP, SAX, STY, STA, STX, SAX, DEY, XXX, TXA, XXX, STY, STA, STX, SAX, 36 | BCC, STA, XXX, XXX, STY, STA, STX, SAX, TYA, STA, TXS, XXX, XXX, STA, XXX, XXX, 37 | LDY, LDA, LDX, LAX, LDY, LDA, LDX, LAX, TAY, LDA, TAX, XXX, LDY, LDA, LDX, LAX, 38 | BCS, LDA, XXX, LAX, LDY, LDA, LDX, LAX, CLV, LDA, TSX, XXX, LDY, LDA, LDX, LAX, 39 | CPY, CMP, NOP, XXX, CPY, CMP, DEC, XXX, INY, CMP, DEX, AXS, CPY, CMP, DEC, XXX, 40 | BNE, CMP, XXX, XXX, NOP, CMP, DEC, XXX, CLD, CMP, XXX, XXX, XXX, CMP, DEC, XXX, 41 | CPX, SBC, NOP, XXX, CPX, SBC, INC, XXX, INX, SBC, NOP, XXX, CPX, SBC, INC, XXX, 42 | BEQ, SBC, XXX, XXX, NOP, SBC, INC, XXX, SED, SBC, XXX, XXX, XXX, SBC, INC, XXX, 43 | }; 44 | 45 | const int MODE_TABLE[256] = { 46 | IMP, INDX, XXX, INDX, ZP, ZP, ZP, ZP, IMP, IMM, ACC, IMM, ABS, ABS, ABS, ABS, 47 | REL, INDY, XXX, INDY, ZPX, ZPX, ZPX, ZPX, IMP, ABSY, ACC, ABSY, ABSX, ABSX, ABSX, ABSX, 48 | ABS, INDX, XXX, INDX, ZP, ZP, ZP, ZP, IMP, IMM, ACC, IMM, ABS, ABS, ABS, ABS, 49 | REL, INDY, XXX, INDY, ZPX, ZPX, ZPX, ZPX, IMP, ABSY, ACC, ABSY, ABSX, ABSX, ABSX, ABSX, 50 | IMP, INDX, XXX, INDX, ZP, ZP, ZP, ZP, IMP, IMM, ACC, IMM, ABS, ABS, ABS, ABS, 51 | REL, INDY, XXX, INDY, ZPX, ZPX, ZPX, ZPX, IMP, ABSY, ACC, ABSY, ABSX, ABSX, ABSX, ABSX, 52 | IMP, INDX, XXX, INDX, ZP, ZP, ZP, ZP, IMP, IMM, ACC, IMM, IND, ABS, ABS, ABS, 53 | REL, INDY, XXX, INDY, ZPX, ZPX, ZPX, ZPX, IMP, ABSY, ACC, ABSY, ABSX, ABSX, ABSX, ABSX, 54 | IMM, INDX, IMM, INDX, ZP, ZP, ZP, ZP, IMP, IMM, ACC, IMM, ABS, ABS, ABS, ABS, 55 | REL, INDY, XXX, INDY, ZPX, ZPX, ZPY, ZPY, IMP, ABSY, ACC, ABSY, ABSX, ABSX, ABSX, ABSY, 56 | IMM, INDX, IMM, INDX, ZP, ZP, ZP, ZP, IMP, IMM, ACC, IMM, ABS, ABS, ABS, ABS, 57 | REL, INDY, XXX, INDY, ZPX, ZPX, ZPY, ZPY, IMP, ABSY, ACC, ABSY, ABSX, ABSX, ABSY, ABSY, 58 | IMM, INDX, IMM, INDX, ZP, ZP, ZP, ZP, IMP, IMM, ACC, IMM, ABS, ABS, ABS, ABS, 59 | REL, INDY, XXX, INDY, ZPX, ZPX, ZPX, ZPX, IMP, ABSY, ACC, ABSY, ABSX, ABSX, ABSX, ABSX, 60 | IMM, INDX, IMM, INDX, ZP, ZP, ZP, ZP, IMP, IMM, ACC, IMM, ABS, ABS, ABS, ABS, 61 | REL, INDY, XXX, INDY, ZPX, ZPX, ZPX, ZPX, IMP, ABSY, ACC, ABSY, ABSX, ABSX, ABSX, ABSX, 62 | }; 63 | 64 | 65 | uint8_t CPU::getaddr(int mode) { 66 | uint16_t ad, ad2; 67 | switch (mode) { 68 | case IMP: cycles += 2; return 0; 69 | case IMM: cycles += 2; return getmem(pc++); 70 | case ABS: 71 | cycles += 4; 72 | ad = getmem(pc++); 73 | ad |= getmem(pc++) << 8; 74 | return getmem(ad); 75 | case ABSX: 76 | cycles += 4; 77 | ad = getmem(pc++); 78 | ad |= 256 * getmem(pc++); 79 | ad2 = ad + x; 80 | if ((ad2 & 0xff00) != (ad & 0xff00)) cycles++; 81 | return getmem(ad2); 82 | case ABSY: 83 | cycles += 4; 84 | ad = getmem(pc++); 85 | ad |= 256 * getmem(pc++); 86 | ad2 = ad + y; 87 | if ((ad2 & 0xff00) != (ad & 0xff00)) cycles++; 88 | return getmem(ad2); 89 | case ZP: 90 | cycles += 3; 91 | ad = getmem(pc++); 92 | return getmem(ad); 93 | case ZPX: 94 | cycles += 4; 95 | ad = getmem(pc++); 96 | ad += x; 97 | return getmem(ad & 0xff); 98 | case ZPY: 99 | cycles += 4; 100 | ad = getmem(pc++); 101 | ad += y; 102 | return getmem(ad & 0xff); 103 | case INDX: 104 | cycles += 6; 105 | ad = getmem(pc++); 106 | ad += x; 107 | ad2 = getmem(ad & 0xff); 108 | ad++; 109 | ad2 |= getmem(ad & 0xff) << 8; 110 | return getmem(ad2); 111 | case INDY: 112 | cycles += 5; 113 | ad = getmem(pc++); 114 | ad2 = getmem(ad); 115 | ad2 |= getmem((ad + 1) & 0xff) << 8; 116 | ad = ad2 + y; 117 | if ((ad2 & 0xff00) != (ad & 0xff00)) cycles++; 118 | return getmem(ad); 119 | case ACC: cycles += 2; return a; 120 | } 121 | return 0; 122 | } 123 | 124 | void CPU::setaddr(int mode, uint8_t val) { 125 | uint16_t ad, ad2; 126 | switch (mode) { 127 | case ABS: 128 | cycles += 2; 129 | ad = getmem(pc - 2); 130 | ad |= 256 * getmem(pc - 1); 131 | setmem(ad, val); 132 | return; 133 | case ABSX: 134 | cycles += 3; 135 | ad = getmem(pc - 2); 136 | ad |= 256 * getmem(pc - 1); 137 | ad2 = ad + x; 138 | if ((ad2 & 0xff00) != (ad & 0xff00)) cycles--; 139 | setmem(ad2, val); 140 | return; 141 | case ZP: 142 | cycles += 2; 143 | ad = getmem(pc - 1); 144 | setmem(ad, val); 145 | return; 146 | case ZPX: 147 | cycles += 2; 148 | ad = getmem(pc - 1); 149 | ad += x; 150 | setmem(ad & 0xff, val); 151 | return; 152 | case ACC: a = val; return; 153 | } 154 | } 155 | 156 | void CPU::putaddr(int mode, uint8_t val) { 157 | uint16_t ad, ad2; 158 | switch (mode) { 159 | case ABS: 160 | cycles += 4; 161 | ad = getmem(pc++); 162 | ad |= getmem(pc++) << 8; 163 | setmem(ad, val); 164 | return; 165 | case ABSX: 166 | cycles += 4; 167 | ad = getmem(pc++); 168 | ad |= getmem(pc++) << 8; 169 | ad2 = ad + x; 170 | setmem(ad2, val); 171 | return; 172 | case ABSY: 173 | cycles += 4; 174 | ad = getmem(pc++); 175 | ad |= getmem(pc++) << 8; 176 | ad2 = ad + y; 177 | if ((ad2 & 0xff00) != (ad & 0xff00)) cycles++; 178 | setmem(ad2, val); 179 | return; 180 | case ZP: 181 | cycles += 3; 182 | ad = getmem(pc++); 183 | setmem(ad, val); 184 | return; 185 | case ZPX: 186 | cycles += 4; 187 | ad = getmem(pc++); 188 | ad += x; 189 | setmem(ad & 0xff, val); 190 | return; 191 | case ZPY: 192 | cycles += 4; 193 | ad = getmem(pc++); 194 | ad += y; 195 | setmem(ad & 0xff, val); 196 | return; 197 | case INDX: 198 | cycles += 6; 199 | ad = getmem(pc++); 200 | ad += x; 201 | ad2 = getmem(ad & 0xff); 202 | ad++; 203 | ad2 |= getmem(ad & 0xff) << 8; 204 | setmem(ad2, val); 205 | return; 206 | case INDY: 207 | cycles += 5; 208 | ad = getmem(pc++); 209 | ad2 = getmem(ad); 210 | ad2 |= getmem((ad + 1) & 0xff) << 8; 211 | ad = ad2 + y; 212 | setmem(ad, val); 213 | return; 214 | case ACC: 215 | cycles += 2; 216 | a = val; 217 | return; 218 | } 219 | } 220 | 221 | void CPU::setflags(int flag, bool cond) { 222 | if (cond) p |= flag; 223 | else p &= ~flag; 224 | } 225 | 226 | void CPU::push(uint8_t val) { 227 | setmem(0x100 + s, val); 228 | if (s) s--; 229 | } 230 | 231 | uint8_t CPU::pop() { 232 | if (s < 0xff) s++; 233 | return getmem(0x100 + s); 234 | } 235 | 236 | void CPU::branch(bool cond) { 237 | int8_t dist; 238 | dist = (int8_t)getaddr(IMM); 239 | uint16_t wval = pc + dist; 240 | if (cond) { 241 | cycles += ((pc & 0x100) != (wval & 0x100)) ? 2 : 1; 242 | pc = wval; 243 | } 244 | } 245 | 246 | void CPU::parse() { 247 | uint8_t opc = getmem(pc++); 248 | uint8_t bval; 249 | uint16_t wval; 250 | int addr = MODE_TABLE[opc]; 251 | int c; 252 | switch (OPCODE_TABLE[opc]) { 253 | case ADC: 254 | wval = (uint16_t)a + getaddr(addr) + ((p & FLAG_C) ? 1 : 0); 255 | setflags(FLAG_C, wval & 0x100); 256 | a = (uint8_t)wval; 257 | setflags(FLAG_Z, !a); 258 | setflags(FLAG_N, a & 0x80); 259 | setflags(FLAG_V, (!!(p & FLAG_C)) ^ (!!(p & FLAG_N))); 260 | break; 261 | case AND: 262 | bval = getaddr(addr); 263 | a &= bval; 264 | setflags(FLAG_Z, !a); 265 | setflags(FLAG_N, a & 0x80); 266 | break; 267 | case ASL: 268 | wval = getaddr(addr); 269 | wval <<= 1; 270 | setaddr(addr, (uint8_t)wval); 271 | setflags(FLAG_Z, !wval); 272 | setflags(FLAG_N, wval & 0x80); 273 | setflags(FLAG_C, wval & 0x100); 274 | break; 275 | case BCC: branch(!(p & FLAG_C)); break; 276 | case BCS: branch(p & FLAG_C); break; 277 | case BNE: branch(!(p & FLAG_Z)); break; 278 | case BEQ: branch(p & FLAG_Z); break; 279 | case BPL: branch(!(p & FLAG_N)); break; 280 | case BMI: branch(p & FLAG_N); break; 281 | case BVC: branch(!(p & FLAG_V)); break; 282 | case BVS: branch(p & FLAG_V); break; 283 | case BIT: 284 | bval = getaddr(addr); 285 | setflags(FLAG_Z, !(a & bval)); 286 | setflags(FLAG_N, bval & 0x80); 287 | setflags(FLAG_V, bval & 0x40); 288 | break; 289 | case BRK: 290 | pc = 0; // Just quit the emulation 291 | break; 292 | case CLC: setflags(FLAG_C, 0); break; 293 | case CLD: setflags(FLAG_D, 0); break; 294 | case CLI: setflags(FLAG_I, 0); break; 295 | case CLV: setflags(FLAG_V, 0); break; 296 | case CMP: 297 | bval = getaddr(addr); 298 | wval = (uint16_t)a - bval; 299 | setflags(FLAG_Z, !wval); 300 | setflags(FLAG_N, wval & 0x80); 301 | setflags(FLAG_C, a >= bval); 302 | break; 303 | case CPX: 304 | bval = getaddr(addr); 305 | wval = (uint16_t)x - bval; 306 | setflags(FLAG_Z, !wval); 307 | setflags(FLAG_N, wval & 0x80); 308 | setflags(FLAG_C, x >= bval); 309 | break; 310 | case CPY: 311 | bval = getaddr(addr); 312 | wval = (uint16_t)y - bval; 313 | setflags(FLAG_Z, !wval); 314 | setflags(FLAG_N, wval & 0x80); 315 | setflags(FLAG_C, y >= bval); 316 | break; 317 | case DEC: 318 | bval = getaddr(addr); 319 | bval--; 320 | setaddr(addr, bval); 321 | setflags(FLAG_Z, !bval); 322 | setflags(FLAG_N, bval & 0x80); 323 | break; 324 | case DEX: 325 | x--; 326 | setflags(FLAG_Z, !x); 327 | setflags(FLAG_N, x & 0x80); 328 | break; 329 | case DEY: 330 | y--; 331 | setflags(FLAG_Z, !y); 332 | setflags(FLAG_N, y & 0x80); 333 | break; 334 | case EOR: 335 | bval = getaddr(addr); 336 | a ^= bval; 337 | setflags(FLAG_Z, !a); 338 | setflags(FLAG_N, a & 0x80); 339 | break; 340 | case INC: 341 | bval = getaddr(addr); 342 | bval++; 343 | setaddr(addr, bval); 344 | setflags(FLAG_Z, !bval); 345 | setflags(FLAG_N, bval & 0x80); 346 | break; 347 | case INX: 348 | x++; 349 | setflags(FLAG_Z, !x); 350 | setflags(FLAG_N, x & 0x80); 351 | break; 352 | case INY: 353 | y++; 354 | setflags(FLAG_Z, !y); 355 | setflags(FLAG_N, y & 0x80); 356 | break; 357 | case JMP: 358 | wval = getmem(pc++); 359 | wval |= 256 * getmem(pc++); 360 | switch (addr) { 361 | case ABS: pc = wval; break; 362 | case IND: 363 | pc = getmem(wval); 364 | pc |= 256 * getmem(wval + 1); 365 | break; 366 | } 367 | break; 368 | case JSR: 369 | push((pc + 1) >> 8); 370 | push((pc + 1)); 371 | wval = getmem(pc++); 372 | wval |= 256 * getmem(pc++); 373 | pc = wval; 374 | break; 375 | case LDA: 376 | a = getaddr(addr); 377 | setflags(FLAG_Z, !a); 378 | setflags(FLAG_N, a & 0x80); 379 | break; 380 | case LDX: 381 | x = getaddr(addr); 382 | setflags(FLAG_Z, !x); 383 | setflags(FLAG_N, x & 0x80); 384 | break; 385 | case LDY: 386 | y = getaddr(addr); 387 | setflags(FLAG_Z, !y); 388 | setflags(FLAG_N, y & 0x80); 389 | break; 390 | case LSR: 391 | bval = getaddr(addr); 392 | wval = (uint8_t)bval; 393 | wval >>= 1; 394 | setaddr(addr, (uint8_t)wval); 395 | setflags(FLAG_Z, !wval); 396 | setflags(FLAG_N, wval & 0x80); 397 | setflags(FLAG_C, bval & 1); 398 | break; 399 | case NOP: 400 | bval = getaddr(addr); 401 | break; 402 | case ORA: 403 | bval = getaddr(addr); 404 | a |= bval; 405 | setflags(FLAG_Z, !a); 406 | setflags(FLAG_N, a & 0x80); 407 | break; 408 | case PHA: push(a); break; 409 | case PHP: push(p); break; 410 | case PLA: 411 | a = pop(); 412 | setflags(FLAG_Z, !a); 413 | setflags(FLAG_N, a & 0x80); 414 | break; 415 | case PLP: p = pop(); break; 416 | case ROL: 417 | bval = getaddr(addr); 418 | c = !!(p & FLAG_C); 419 | setflags(FLAG_C, bval & 0x80); 420 | bval <<= 1; 421 | bval |= c; 422 | setaddr(addr, bval); 423 | setflags(FLAG_N, bval & 0x80); 424 | setflags(FLAG_Z, !bval); 425 | break; 426 | case ROR: 427 | bval = getaddr(addr); 428 | c = !!(p & FLAG_C); 429 | setflags(FLAG_C, bval & 1); 430 | bval >>= 1; 431 | bval |= 128 * c; 432 | setaddr(addr, bval); 433 | setflags(FLAG_N, bval & 0x80); 434 | setflags(FLAG_Z, !bval); 435 | break; 436 | case RTI: // Treat RTI like RTS 437 | case RTS: 438 | wval = pop(); 439 | wval |= pop() << 8; 440 | pc = wval + 1; 441 | break; 442 | case SBC: 443 | bval = getaddr(addr) ^ 0xff; 444 | wval = (uint16_t)a + bval + ((p & FLAG_C) ? 1 : 0); 445 | setflags(FLAG_C, wval & 0x100); 446 | a = (uint8_t)wval; 447 | setflags(FLAG_Z, !a); 448 | setflags(FLAG_N, a > 127); 449 | setflags(FLAG_V, (!!(p & FLAG_C)) ^ (!!(p & FLAG_N))); 450 | break; 451 | case SEC: setflags(FLAG_C, 1); break; 452 | case SED: setflags(FLAG_D, 1); break; 453 | case SEI: setflags(FLAG_I, 1); break; 454 | case STA: putaddr(addr, a); break; 455 | case STX: putaddr(addr, x); break; 456 | case STY: putaddr(addr, y); break; 457 | case TAX: 458 | x = a; 459 | setflags(FLAG_Z, !x); 460 | setflags(FLAG_N, x & 0x80); 461 | break; 462 | case TAY: 463 | y = a; 464 | setflags(FLAG_Z, !y); 465 | setflags(FLAG_N, y & 0x80); 466 | break; 467 | case TSX: 468 | x = s; 469 | setflags(FLAG_Z, !x); 470 | setflags(FLAG_N, x & 0x80); 471 | break; 472 | case TXA: 473 | a = x; 474 | setflags(FLAG_Z, !a); 475 | setflags(FLAG_N, a & 0x80); 476 | break; 477 | case TXS: s = x; break; 478 | case TYA: 479 | a = y; 480 | setflags(FLAG_Z, !a); 481 | setflags(FLAG_N, a & 0x80); 482 | break; 483 | 484 | // secret opcodes 485 | case SLO: 486 | bval = getaddr(addr); 487 | setflags(FLAG_C, bval >> 7); 488 | bval <<= 1; 489 | setaddr(addr, bval); 490 | a |= bval; 491 | setflags(FLAG_Z, !a); 492 | setflags(FLAG_N, a & 0x80); 493 | break; 494 | case AXS: 495 | wval = x = (x & a) - getaddr(addr); 496 | setflags(FLAG_Z, !x); 497 | setflags(FLAG_N, x & 0x80); 498 | setflags(FLAG_C, wval & 0x100); 499 | break; 500 | case LAX: 501 | a = x = getaddr(addr); 502 | setflags(FLAG_Z, !a); 503 | setflags(FLAG_N, a & 0x80); 504 | break; 505 | case SAX: 506 | putaddr(addr, a & x); 507 | break; 508 | 509 | case RLA: 510 | bval = getaddr(addr); 511 | c = !!(p & FLAG_C); 512 | setflags(FLAG_C, bval & 0x80); 513 | bval <<= 1; 514 | bval |= c; 515 | setaddr(addr, bval); 516 | a &= bval; 517 | setflags(FLAG_Z, !a); 518 | setflags(FLAG_N, a & 0x80); 519 | break; 520 | 521 | case RRA: 522 | bval = getaddr(addr); 523 | c = !!(p & FLAG_C); 524 | setflags(FLAG_C, bval & 1); 525 | bval >>= 1; 526 | bval |= 128 * c; 527 | setaddr(addr, bval); 528 | wval = (uint16_t)a + bval + ((p & FLAG_C) ? 1 : 0); 529 | setflags(FLAG_C, wval & 0x100); 530 | a = (uint8_t)wval; 531 | setflags(FLAG_Z, !a); 532 | setflags(FLAG_N, a & 0x80); 533 | setflags(FLAG_V, (!!(p & FLAG_C)) ^ (!!(p & FLAG_N))); 534 | break; 535 | 536 | case XXX: 537 | default: 538 | printf("cpu: unknown opcode: %02x\n", opc); 539 | break; 540 | } 541 | } 542 | 543 | 544 | void CPU::jsr(uint16_t npc, uint8_t na) { 545 | a = na; 546 | x = 0; 547 | y = 0; 548 | p = 0; 549 | s = 255; 550 | pc = npc; 551 | push(0); 552 | push(0); 553 | while (pc) parse(); 554 | } 555 | -------------------------------------------------------------------------------- /src/cpu.hpp: -------------------------------------------------------------------------------- 1 | // this code is based on kb's 6502 code in tinysid, i think. 2 | #pragma once 3 | #include 4 | #include 5 | 6 | class CPU { 7 | public: 8 | void jsr(uint16_t npc, uint8_t na); 9 | virtual ~CPU() {} 10 | 11 | virtual uint8_t getmem(uint16_t addr) = 0; 12 | virtual void setmem(uint16_t addr, uint8_t value) = 0; 13 | 14 | private: 15 | uint8_t getaddr(int mode); 16 | void setaddr(int mode, uint8_t val); 17 | void putaddr(int mode, uint8_t val); 18 | void setflags(int flag, bool cond); 19 | void push(uint8_t val); 20 | uint8_t pop(); 21 | void branch(bool cond); 22 | void parse(); 23 | 24 | int cycles; 25 | uint16_t pc; 26 | uint8_t a, x, y, s, p; 27 | }; 28 | -------------------------------------------------------------------------------- /src/fx.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "fx.hpp" 5 | 6 | 7 | namespace fx { 8 | namespace { 9 | 10 | const uint8_t FONT[] = { 11 | 0b00000000, 0b00011000, 0b01101100, 0b01101100, 0b00110000, 0b00000000, 0b00111000, 0b01100000, 0b00011000, 0b01100000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000110, 12 | 0b00000000, 0b00111100, 0b01101100, 0b01101100, 0b01111100, 0b11000110, 0b01101100, 0b01100000, 0b00110000, 0b00110000, 0b01100110, 0b00110000, 0b00000000, 0b00000000, 0b00000000, 0b00001100, 13 | 0b00000000, 0b00111100, 0b01101100, 0b11111110, 0b11000000, 0b11001100, 0b00111000, 0b11000000, 0b01100000, 0b00011000, 0b00111100, 0b00110000, 0b00000000, 0b00000000, 0b00000000, 0b00011000, 14 | 0b00000000, 0b00111100, 0b00000000, 0b01101100, 0b01111000, 0b00011000, 0b01110110, 0b00000000, 0b01100000, 0b00011000, 0b11111111, 0b11111100, 0b00000000, 0b11111100, 0b00000000, 0b00110000, 15 | 0b00000000, 0b00011000, 0b00000000, 0b11111110, 0b00001100, 0b00110000, 0b11011100, 0b00000000, 0b01100000, 0b00011000, 0b00111100, 0b00110000, 0b00000000, 0b00000000, 0b00000000, 0b01100000, 16 | 0b00000000, 0b00011000, 0b00000000, 0b01101100, 0b11111000, 0b01100110, 0b11001100, 0b00000000, 0b00110000, 0b00110000, 0b01100110, 0b00110000, 0b00110000, 0b00000000, 0b00110000, 0b11000000, 17 | 0b00000000, 0b00000000, 0b00000000, 0b01101100, 0b00110000, 0b11000110, 0b01110110, 0b00000000, 0b00011000, 0b01100000, 0b00000000, 0b00000000, 0b00110000, 0b00000000, 0b00110000, 0b10000000, 18 | 0b00000000, 0b00011000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b01100000, 0b00000000, 0b00000000, 0b00000000, 19 | 20 | 0b00111000, 0b00011000, 0b01111100, 0b01111110, 0b00011100, 0b11111100, 0b00111100, 0b11111110, 0b01111100, 0b01111100, 0b00000000, 0b00000000, 0b00011000, 0b00000000, 0b01100000, 0b01111000, 21 | 0b01001100, 0b00111000, 0b11000110, 0b00001100, 0b00111100, 0b11000000, 0b01100000, 0b11000110, 0b11000110, 0b11000110, 0b00110000, 0b00110000, 0b00110000, 0b00000000, 0b00110000, 0b11001100, 22 | 0b11000110, 0b00011000, 0b00001110, 0b00011000, 0b01101100, 0b11111100, 0b11000000, 0b00001100, 0b11000110, 0b11000110, 0b00110000, 0b00110000, 0b01100000, 0b11111100, 0b00011000, 0b00001100, 23 | 0b11000110, 0b00011000, 0b00111100, 0b00111100, 0b11001100, 0b00000110, 0b11111100, 0b00011000, 0b01111100, 0b01111110, 0b00000000, 0b00000000, 0b11000000, 0b00000000, 0b00001100, 0b00011000, 24 | 0b11000110, 0b00011000, 0b01111000, 0b00000110, 0b11111110, 0b00000110, 0b11000110, 0b00110000, 0b11000110, 0b00000110, 0b00000000, 0b00000000, 0b01100000, 0b00000000, 0b00011000, 0b00110000, 25 | 0b01100100, 0b00011000, 0b11100000, 0b11000110, 0b00001100, 0b11000110, 0b11000110, 0b00110000, 0b11000110, 0b00001100, 0b00110000, 0b00110000, 0b00110000, 0b11111100, 0b00110000, 0b00000000, 26 | 0b00111000, 0b01111110, 0b11111110, 0b01111100, 0b00001100, 0b01111100, 0b01111100, 0b00110000, 0b01111100, 0b01111000, 0b00110000, 0b00110000, 0b00011000, 0b00000000, 0b01100000, 0b00110000, 27 | 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b01100000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 28 | 29 | 0b01111100, 0b00111000, 0b11111100, 0b00111100, 0b11111000, 0b11111110, 0b11111110, 0b00111110, 0b11000110, 0b01111110, 0b00011110, 0b11000110, 0b01100000, 0b11000110, 0b11000110, 0b01111100, 30 | 0b11000110, 0b01101100, 0b11000110, 0b01100110, 0b11001100, 0b11000000, 0b11000000, 0b01100000, 0b11000110, 0b00011000, 0b00000110, 0b11001100, 0b01100000, 0b11101110, 0b11100110, 0b11000110, 31 | 0b11011110, 0b11000110, 0b11000110, 0b11000000, 0b11000110, 0b11000000, 0b11000000, 0b11000000, 0b11000110, 0b00011000, 0b00000110, 0b11011000, 0b01100000, 0b11111110, 0b11110110, 0b11000110, 32 | 0b11011110, 0b11000110, 0b11111100, 0b11000000, 0b11000110, 0b11111100, 0b11111100, 0b11001110, 0b11111110, 0b00011000, 0b00000110, 0b11110000, 0b01100000, 0b11111110, 0b11111110, 0b11000110, 33 | 0b11011110, 0b11111110, 0b11000110, 0b11000000, 0b11000110, 0b11000000, 0b11000000, 0b11000110, 0b11000110, 0b00011000, 0b11000110, 0b11111000, 0b01100000, 0b11010110, 0b11011110, 0b11000110, 34 | 0b11000000, 0b11000110, 0b11000110, 0b01100110, 0b11001100, 0b11000000, 0b11000000, 0b01100110, 0b11000110, 0b00011000, 0b11000110, 0b11011100, 0b01100000, 0b11000110, 0b11001110, 0b11000110, 35 | 0b01111000, 0b11000110, 0b11111100, 0b00111100, 0b11111000, 0b11111110, 0b11000000, 0b00111110, 0b11000110, 0b01111110, 0b01111100, 0b11001110, 0b01111110, 0b11000110, 0b11000110, 0b01111100, 36 | 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 37 | 38 | 0b11111100, 0b01111100, 0b11111100, 0b01111000, 0b01111110, 0b11000110, 0b11000110, 0b11000110, 0b11000110, 0b01100110, 0b11111110, 0b01111000, 0b11000000, 0b01111000, 0b00010000, 0b00000000, 39 | 0b11000110, 0b11000110, 0b11000110, 0b11001100, 0b00011000, 0b11000110, 0b11000110, 0b11000110, 0b11101110, 0b01100110, 0b00001110, 0b01100000, 0b01100000, 0b00011000, 0b00111000, 0b00000000, 40 | 0b11000110, 0b11000110, 0b11000110, 0b11000000, 0b00011000, 0b11000110, 0b11000110, 0b11010110, 0b01111100, 0b01100110, 0b00011100, 0b01100000, 0b00110000, 0b00011000, 0b01101100, 0b00000000, 41 | 0b11000110, 0b11000110, 0b11001110, 0b01111100, 0b00011000, 0b11000110, 0b11101110, 0b11111110, 0b00111000, 0b00111100, 0b00111000, 0b01100000, 0b00011000, 0b00011000, 0b11000110, 0b00000000, 42 | 0b11111100, 0b11011110, 0b11111000, 0b00000110, 0b00011000, 0b11000110, 0b01111100, 0b11111110, 0b01111100, 0b00011000, 0b01110000, 0b01100000, 0b00001100, 0b00011000, 0b00000000, 0b00000000, 43 | 0b11000000, 0b11001100, 0b11011100, 0b11000110, 0b00011000, 0b11000110, 0b00111000, 0b11101110, 0b11101110, 0b00011000, 0b11100000, 0b01100000, 0b00000110, 0b00011000, 0b00000000, 0b00000000, 44 | 0b11000000, 0b01111010, 0b11001110, 0b01111100, 0b00011000, 0b01111100, 0b00010000, 0b11000110, 0b11000110, 0b00011000, 0b11111110, 0b01111000, 0b00000010, 0b01111000, 0b00000000, 0b00000000, 45 | 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11111111, 46 | 47 | 0b00110000, 0b00000000, 0b01100000, 0b00000000, 0b00000110, 0b00000000, 0b00001110, 0b00000000, 0b01100000, 0b00000000, 0b00000000, 0b01100000, 0b00011000, 0b00000000, 0b00000000, 0b00000000, 48 | 0b00110000, 0b00000000, 0b01100000, 0b00000000, 0b00000110, 0b00000000, 0b00011000, 0b00000000, 0b01100000, 0b00011000, 0b00000110, 0b01100000, 0b00011000, 0b00000000, 0b00000000, 0b00000000, 49 | 0b00011000, 0b00111100, 0b01111100, 0b00111110, 0b00111110, 0b00111100, 0b00011000, 0b00111110, 0b01100000, 0b00000000, 0b00000000, 0b01100010, 0b00011000, 0b01110110, 0b01111100, 0b00111100, 50 | 0b00000000, 0b01100110, 0b01100110, 0b01100000, 0b01100110, 0b01100110, 0b01111110, 0b01100110, 0b01111100, 0b00011000, 0b00000110, 0b01100100, 0b00011000, 0b01101011, 0b01100110, 0b01100110, 51 | 0b00000000, 0b01100110, 0b01100110, 0b01100000, 0b01100110, 0b01111110, 0b00011000, 0b01100110, 0b01100110, 0b00011000, 0b00000110, 0b01101000, 0b00011000, 0b01101011, 0b01100110, 0b01100110, 52 | 0b00000000, 0b01100110, 0b01100110, 0b01100000, 0b01100110, 0b01100000, 0b00011000, 0b00111110, 0b01100110, 0b00011000, 0b00000110, 0b01111100, 0b00011000, 0b01101011, 0b01100110, 0b01100110, 53 | 0b00000000, 0b00111011, 0b01111100, 0b00111110, 0b00111110, 0b00111110, 0b00011000, 0b00000110, 0b01100110, 0b00011000, 0b01100110, 0b01100110, 0b00011000, 0b01101011, 0b01100110, 0b00111100, 54 | 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00111100, 0b00000000, 0b00000000, 0b00111100, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 55 | 56 | 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00110000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00011100, 0b00011000, 0b11100000, 0b01110110, 0b00000000, 57 | 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00110000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00110000, 0b00011000, 0b00110000, 0b11011100, 0b00010000, 58 | 0b01111100, 0b00111110, 0b01101110, 0b00111100, 0b11111100, 0b01100110, 0b01100110, 0b01100011, 0b01100011, 0b01100110, 0b01111110, 0b00110000, 0b00011000, 0b00110000, 0b00000000, 0b00111000, 59 | 0b01100110, 0b01100110, 0b01110000, 0b01000000, 0b00110000, 0b01100110, 0b01100110, 0b01101011, 0b00110110, 0b01100110, 0b00001100, 0b11100000, 0b00000000, 0b00011100, 0b00000000, 0b01101100, 60 | 0b01100110, 0b01100110, 0b01100000, 0b00111100, 0b00110000, 0b01100110, 0b01100110, 0b01101011, 0b00011100, 0b00101100, 0b00011000, 0b00110000, 0b00011000, 0b00110000, 0b00000000, 0b11000110, 61 | 0b01111100, 0b00111110, 0b01100000, 0b00000110, 0b00110000, 0b01100110, 0b00100100, 0b01101011, 0b00110110, 0b00011000, 0b00110000, 0b00110000, 0b00011000, 0b00110000, 0b00000000, 0b11000110, 62 | 0b01100000, 0b00000110, 0b01100000, 0b01111100, 0b00011100, 0b00111100, 0b00011000, 0b00110110, 0b01100011, 0b00110000, 0b01111110, 0b00011100, 0b00011000, 0b11100000, 0b00000000, 0b11111110, 63 | 0b01100000, 0b00000110, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b01100000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 64 | }; 65 | 66 | 67 | SDL_Window* m_window; 68 | SDL_Renderer* m_renderer; 69 | SDL_Texture* m_font_tex; 70 | bool m_running; 71 | int m_screen_width = 800; 72 | int m_screen_height = 600; 73 | 74 | } // namespace 75 | 76 | 77 | int run(App& app) { 78 | SDL_Init(SDL_INIT_VIDEO); 79 | m_window = SDL_CreateWindow( 80 | app.title(), 81 | SDL_WINDOWPOS_UNDEFINED, 82 | SDL_WINDOWPOS_UNDEFINED, 83 | m_screen_width, m_screen_height, 84 | SDL_WINDOW_RESIZABLE); 85 | 86 | m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_PRESENTVSYNC); 87 | //SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_BLEND); 88 | SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_ADD); 89 | 90 | // font 91 | { 92 | std::vector data(16 * 6 * 8 * 8); 93 | for (int i = 0; i < (int) data.size(); ++i) { 94 | data[i] = (FONT[i / 8] & (1 << (7 - i % 8))) ? 0xffff : 0; 95 | } 96 | m_font_tex = SDL_CreateTexture(m_renderer, SDL_PIXELFORMAT_ARGB4444, 97 | SDL_TEXTUREACCESS_STATIC, 16 * 8, 6 * 8); 98 | SDL_UpdateTexture(m_font_tex, nullptr, data.data(), 2 * 16 * 8); 99 | SDL_SetTextureBlendMode(m_font_tex, SDL_BLENDMODE_BLEND); 100 | } 101 | 102 | app.init(); 103 | 104 | m_running = true; 105 | while (m_running) { 106 | SDL_Event e; 107 | while (SDL_PollEvent(&e)) { 108 | switch (e.type) { 109 | case SDL_QUIT: 110 | m_running = false; 111 | break; 112 | 113 | case SDL_KEYDOWN: 114 | if (e.key.keysym.scancode == SDL_SCANCODE_ESCAPE) m_running = false; 115 | else app.key(e.key.keysym.scancode); 116 | break; 117 | 118 | case SDL_TEXTINPUT: 119 | app.textinput(e.text.text); 120 | break; 121 | 122 | case SDL_WINDOWEVENT: 123 | if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { 124 | m_screen_width = e.window.data1; 125 | m_screen_height = e.window.data2; 126 | } 127 | break; 128 | 129 | 130 | default: break; 131 | } 132 | } 133 | 134 | app.update(); 135 | SDL_RenderPresent(m_renderer); 136 | } 137 | 138 | SDL_DestroyTexture(m_font_tex); 139 | SDL_DestroyRenderer(m_renderer); 140 | SDL_DestroyWindow(m_window); 141 | SDL_Quit(); 142 | return 0; 143 | } 144 | 145 | void clear() { 146 | SDL_RenderClear(m_renderer); 147 | } 148 | 149 | void set_color(int r, int g, int b, int a) { 150 | SDL_SetRenderDrawColor(m_renderer, r, g, b, a); 151 | } 152 | 153 | void draw_line(int x1, int y1, int x2, int y2) { 154 | SDL_RenderDrawLine(m_renderer, x1, y1, x2, y2); 155 | } 156 | 157 | void draw_rectangle(bool fill, int x, int y, int w, int h) { 158 | SDL_Rect r = { x, y, w, h }; 159 | if (fill) SDL_RenderFillRect(m_renderer, &r); 160 | else SDL_RenderDrawRect(m_renderer, &r); 161 | } 162 | 163 | void set_font_color(int r, int g, int b) { 164 | SDL_SetTextureColorMod(m_font_tex, r, g, b); 165 | } 166 | 167 | void put_char(int x, int y, char c) { 168 | if (c < 32) return; 169 | SDL_Rect src = { c % 16 * 8, (c - 32) / 16 * 8, 8, 8 }; 170 | SDL_Rect dst = { x, y, 16, 16 }; 171 | SDL_RenderCopy(m_renderer, m_font_tex, &src, &dst); 172 | } 173 | 174 | void print(int x, int y, const char* str) { 175 | while (*str) { 176 | put_char(x, y, *str); 177 | ++str; 178 | x += 16; 179 | } 180 | } 181 | 182 | void printf(int x, int y, const char* format, ...) { 183 | va_list args; 184 | va_start(args, format); 185 | char line[256]; 186 | vsnprintf(line, 256, format, args); 187 | va_end(args); 188 | print(x, y, line); 189 | } 190 | 191 | int screen_width() { return m_screen_width; } 192 | int screen_height() { return m_screen_height; } 193 | 194 | } // namespace 195 | -------------------------------------------------------------------------------- /src/fx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace fx { 4 | struct App { 5 | virtual const char* title() const { return "app"; } 6 | virtual void init() {} 7 | virtual void key(int code) {} 8 | virtual void textinput(char const* text) {} 9 | virtual void update() {} 10 | }; 11 | 12 | 13 | int run(App& App); 14 | void clear(); 15 | void set_color(int r, int g, int b, int a = 255); 16 | void draw_line(int x1, int y1, int x2, int y2); 17 | void draw_rectangle(bool fill, int x, int y, int w, int h); 18 | void set_font_color(int r, int g, int b); 19 | void put_char(int x, int y, char c); 20 | void print(int x, int y, const char* str); 21 | void printf(int x, int y, const char* format, ...); 22 | 23 | int screen_width(); 24 | int screen_height(); 25 | } 26 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #define SDL_MAIN_HANDLED 5 | #include 6 | #include "record.hpp" 7 | #include "fx.hpp" 8 | #include "sidengine.hpp" 9 | 10 | 11 | enum { 12 | MIXRATE = 44100, 13 | BUFFER_SIZE = 1024, 14 | CHANNEL_COUT = 3, 15 | }; 16 | 17 | 18 | 19 | std::array engines = { 20 | SidEngine::create_resid(), 21 | SidEngine::create_tinysid(), 22 | }; 23 | 24 | char const* filename; 25 | int framerate; 26 | int samples_per_frame; 27 | Record record; 28 | int frame; 29 | bool playing = false; 30 | bool chan_active[] = { 1, 1, 1 }; 31 | int engine_nr = 0; 32 | size_t render_time = 0; 33 | 34 | 35 | void tick() { 36 | const Uint8* ks = SDL_GetKeyboardState(nullptr); 37 | if (!ks[SDL_SCANCODE_LCTRL] && !ks[SDL_SCANCODE_RCTRL]) { 38 | bool shift = ks[SDL_SCANCODE_LSHIFT] | ks[SDL_SCANCODE_RSHIFT]; 39 | int speed = shift ? 5 : 1; 40 | if (ks[SDL_SCANCODE_LEFT]) frame -= speed; 41 | else if (ks[SDL_SCANCODE_RIGHT]) frame += speed; 42 | else if (playing) ++frame; 43 | frame = std::min(std::max(frame, 0), record.states.size()); 44 | } 45 | 46 | std::array reg = record.states[frame].reg; 47 | for (int c = 0; c < CHANNEL_COUT; c++) { 48 | for (int r = 0; r < 7; r++) { 49 | uint8_t& a = reg[c * 7 + r]; 50 | // mute 51 | if (!chan_active[c] && r == 4) a &= 0xf0; 52 | if (!chan_active[c] && r == 5) a = 0xff; 53 | if (!chan_active[c] && r == 6) a = 0x00; 54 | } 55 | } 56 | engines[engine_nr]->update_registers(reg.data()); 57 | } 58 | 59 | void audio_callback(void* u, Uint8* stream, int bytes) { 60 | auto then = std::chrono::steady_clock::now(); 61 | 62 | int16_t* buffer = (int16_t*) stream; 63 | int length = bytes / sizeof(int16_t); 64 | static int sample = 0; 65 | while (length > 0) { 66 | if (sample == 0) tick(); 67 | int l = std::min(samples_per_frame - sample, length); 68 | sample += l; 69 | if (sample == samples_per_frame) sample = 0; 70 | length -= l; 71 | if (playing) engines[engine_nr]->mix(buffer, l); 72 | else for (int i = 0; i < l; ++i) buffer[i] = 0; 73 | buffer += l; 74 | } 75 | 76 | // measure time 77 | auto now = std::chrono::steady_clock::now(); 78 | size_t nano = std::chrono::duration_cast(now - then).count(); 79 | static size_t nano_acc = 0; 80 | static size_t count = 0; 81 | nano_acc += nano; 82 | count += bytes / 2; 83 | if (count >= MIXRATE) { 84 | render_time = nano_acc / (count / float(MIXRATE)); 85 | nano_acc = 0; 86 | count = 0; 87 | //printf("%10lu\n", render_time); 88 | } 89 | } 90 | 91 | 92 | struct App : fx::App { 93 | 94 | int scale_x = 4; 95 | int scale_y = 7; 96 | int offset = 3; 97 | int bar = 48; 98 | bool show_bar = true; 99 | bool filter_active = true; 100 | int vert_pos = 16; 101 | SidEngine::ChipModel chip_model = SidEngine::MOS8580; 102 | 103 | void reload_song(int nr) { 104 | SDL_PauseAudio(1); 105 | frame = 0; 106 | playing = false; 107 | record.load(filename, nr); 108 | SDL_PauseAudio(0); 109 | } 110 | 111 | void init() override { 112 | engines[engine_nr]->set_chip_model(chip_model); 113 | SDL_AudioSpec spec = { 114 | MIXRATE, AUDIO_S16, 1, 0, 115 | BUFFER_SIZE, 0, 0, &audio_callback 116 | }; 117 | SDL_OpenAudio(&spec, nullptr); 118 | SDL_PauseAudio(0); 119 | } 120 | 121 | const char* title() const override { return "sid-monitor"; } 122 | 123 | void key(int code) override { 124 | const Uint8* ks = SDL_GetKeyboardState(nullptr); 125 | bool ctrl = ks[SDL_SCANCODE_LCTRL] | ks[SDL_SCANCODE_RCTRL]; 126 | 127 | switch (code) { 128 | case SDL_SCANCODE_SPACE: playing ^= 1; break; 129 | case SDL_SCANCODE_LEFT: if (ctrl) frame = std::max(0, frame - 1); break; 130 | case SDL_SCANCODE_RIGHT: if (ctrl) ++frame; break; 131 | case SDL_SCANCODE_BACKSPACE: frame = 0; break; 132 | 133 | case SDL_SCANCODE_PAGEDOWN: scale_y = std::max(2, scale_y - 1); break; 134 | case SDL_SCANCODE_PAGEUP: ++scale_y; break; 135 | 136 | case SDL_SCANCODE_B: show_bar ^= 1; break; 137 | case SDL_SCANCODE_W: ++bar; break; 138 | case SDL_SCANCODE_S: --bar; break; 139 | case SDL_SCANCODE_D: ++offset; break; 140 | case SDL_SCANCODE_A: --offset; break; 141 | 142 | case SDL_SCANCODE_1: chan_active[0] ^= 1; break; 143 | case SDL_SCANCODE_2: chan_active[1] ^= 1; break; 144 | case SDL_SCANCODE_3: chan_active[2] ^= 1; break; 145 | 146 | case SDL_SCANCODE_F: engines[engine_nr]->enable_filter(filter_active ^= 1); break; 147 | case SDL_SCANCODE_M: 148 | chip_model = chip_model == SidEngine::MOS6581 ? SidEngine::MOS8580 : SidEngine::MOS6581; 149 | engines[engine_nr]->set_chip_model(chip_model); 150 | break; 151 | 152 | case SDL_SCANCODE_TAB: 153 | engine_nr = (engine_nr + 1) % engines.size(); 154 | engines[engine_nr]->set_chip_model(chip_model); 155 | engines[engine_nr]->enable_filter(filter_active); 156 | break; 157 | 158 | case SDL_SCANCODE_COMMA: 159 | if (record.song_nr > 1) reload_song(record.song_nr - 1); 160 | break; 161 | case SDL_SCANCODE_PERIOD: 162 | if (record.song_nr < record.song_count) reload_song(record.song_nr + 1); 163 | break; 164 | 165 | default: break; 166 | } 167 | } 168 | 169 | void textinput(char const* text) override { 170 | if (text[0] == '+') ++scale_x; 171 | if (text[0] == '-') scale_x = std::max(1, scale_x - 1); 172 | } 173 | 174 | void update() override { 175 | fx::set_color(0, 0, 0); 176 | fx::clear(); 177 | fx::set_font_color(250, 250, 250); 178 | 179 | int f = frame; 180 | int frames_per_screen = fx::screen_width() / scale_x; 181 | int start_frame = std::max(0, f - frames_per_screen / 2); 182 | 183 | // bar 184 | if (show_bar) { 185 | fx::set_color(50, 50, 50); 186 | for (int t = -((start_frame - offset) % bar); t < frames_per_screen; t += bar) { 187 | fx::draw_rectangle(true, t * scale_x, 0, 1, fx::screen_height()); 188 | } 189 | } 190 | 191 | 192 | for (int p = -69; p < 40; ++p) { 193 | 194 | int c = "101201011010"[(p + 120) % 12] - '0'; 195 | c = 10 + c * 20; 196 | fx::set_color(c, c, c); 197 | 198 | int y = -(p + vert_pos) * scale_y + fx::screen_height() / 2; 199 | fx::draw_rectangle(true, 0, y, fx::screen_width(), 1 + scale_y - 2); 200 | 201 | } 202 | 203 | int active_chans = 0; 204 | for (int c = 0; c < CHANNEL_COUT; c++) active_chans += chan_active[c]; 205 | 206 | 207 | for (int n = start_frame; n < start_frame + frames_per_screen; ++n) { 208 | if (n >= (int) record.states.size()) break; 209 | auto const& state = record.states[n]; 210 | auto const& prev_state = record.states[n > 0 ? n - 1 : 0]; 211 | 212 | int x = (n - start_frame) * scale_x; 213 | 214 | for (int c = 0; c < CHANNEL_COUT; c++) { 215 | if (!chan_active[c]) continue; 216 | 217 | int freq = state.reg[c * 7 + 0] | (state.reg[c * 7 + 1] << 8); 218 | float real_freq = freq * 17734472.0f * (1.0f / (18 << 24)); 219 | float pitch = std::log2(real_freq / 440) * 12; 220 | 221 | bool gate = state.reg[c * 7 + 4] & 1; 222 | int wave = state.reg[c * 7 + 4] >> 4; 223 | int prev_wave = prev_state.reg[c * 7 + 4] >> 4; 224 | bool noise = wave & 8; 225 | 226 | float vol = (wave > 0 ? 1 : 0.2) * (1 + gate); 227 | 228 | int v = 255 * std::pow(vol / 2.0f, 0.8f); 229 | float q = noise ? 0.66 : 0.33; 230 | if (c == 0) fx::set_color(v, v*q, v*q); 231 | if (c == 1) fx::set_color(v*q, v, v*q); 232 | if (c == 2) fx::set_color(v*q, v*q, v); 233 | 234 | int y = -(pitch + vert_pos) * scale_y + fx::screen_height() / 2; 235 | fx::draw_rectangle(true, x, y, scale_x, scale_y - 1); 236 | 237 | // new note 238 | if (wave != 0 && prev_wave == 0) { 239 | fx::draw_rectangle(false, x - scale_x, y - scale_y, scale_x, scale_y * 3 - 1); 240 | } 241 | 242 | // pulse width 243 | if (active_chans == 1) { 244 | int pw = ((state.reg[c * 7 + 2] >> 4) & 0xf) | ((state.reg[c * 7 + 3] & 0xf) << 4); 245 | fx::set_color(c == 0 ? 50 : 0, c == 1 ? 50 : 0, c == 2 ? 50 : 0); 246 | fx::draw_rectangle(true, x, fx::screen_height() - pw, scale_x, pw); 247 | } 248 | } 249 | 250 | // filter 251 | if (filter_active) { 252 | int freq = state.reg[22]; 253 | fx::set_color(40, 40, 0); 254 | fx::draw_rectangle(true, x, fx::screen_height() - freq, scale_x, freq); 255 | } 256 | 257 | } 258 | 259 | 260 | // cursor 261 | fx::set_color(255, 255, 255); 262 | fx::draw_rectangle(true, (f - start_frame) * scale_x, 0, 1, fx::screen_height()); 263 | 264 | // state 265 | auto const& state = record.states[f]; 266 | for (int i = 0; i < (int) state.is_set.size(); ++i) { 267 | int c = i / 7; 268 | int r = i % 7; 269 | if (c < CHANNEL_COUT && !chan_active[c]) continue; 270 | if (state.is_set[i]) fx::set_font_color(250, 250, 250); 271 | else fx::set_font_color(150, 150, 150); 272 | fx::printf(r * 48 + 8, fx::screen_height() - (4 - c) * 24, "%02X", state.reg[i]); 273 | } 274 | fx::set_font_color(250, 250, 250); 275 | for (int c = 0; c < CHANNEL_COUT; ++c) { 276 | if (!chan_active[c]) continue; 277 | int y = fx::screen_height() - (4 - c) * 24; 278 | 279 | bool filter = (state.reg[23] & (1 << c)) > 0; 280 | fx::printf(7 * 48 + 8, y, "%c", ".*"[filter]); 281 | 282 | int freq = state.reg[c * 7 + 0] | (state.reg[c * 7 + 1] << 8); 283 | float real_freq = freq * 17734472.0f * (1.0f / (18 << 24)); 284 | int note = int(57.5f + std::log2(real_freq / 440) * 12); 285 | 286 | fx::printf(23 * 16 + 8, y, note > 0 ? "%c%c%d" : "...", 287 | "CCDDEFFGGAAB"[note % 12], 288 | "-#-#--#-#-#-"[note % 12], 289 | note / 12, 290 | note); 291 | 292 | int control = state.reg[c * 7 + 4]; 293 | int pw = ((state.reg[c * 7 + 2] >> 4) & 0xf) | ((state.reg[c * 7 + 3] & 0xf) << 4); 294 | fx::printf(27 * 16 + 8, y, "%c%c%c%c %c%c%c%c %02X", 295 | "N."[!(control & 0x80)], 296 | "P."[!(control & 0x40)], 297 | "S."[!(control & 0x20)], 298 | "T."[!(control & 0x10)], 299 | "T."[!(control & 0x08)], 300 | "R."[!(control & 0x04)], 301 | "S."[!(control & 0x02)], 302 | "G."[!(control & 0x01)], 303 | pw); 304 | } 305 | int filter = state.reg[24]; 306 | fx::printf(27 * 16 + 8, fx::screen_height() - 24, "%c%c%c", 307 | "H."[!(filter & 0x40)], 308 | "B."[!(filter & 0x20)], 309 | "L."[!(filter & 0x10)]); 310 | 311 | fx::printf(8, 8, "%s - %d/%d", record.song_name.c_str(), record.song_nr, record.song_count); 312 | fx::printf(8, 8 + 24, "%s", record.song_author.c_str()); 313 | fx::printf(8, 8 + 48, "%s", record.song_released.c_str()); 314 | 315 | fx::printf(fx::screen_width() - 8 - 16 * 14, 8 + 24 * 0, " speed: %4.gX", record.speed); 316 | fx::printf(fx::screen_width() - 8 - 16 * 14, 8 + 24 * 1, " time: %02d:%02d", frame / framerate / 60, frame / framerate % 60); 317 | fx::printf(fx::screen_width() - 8 - 16 * 14, 8 + 24 * 2, " pos: %6d", f); 318 | fx::printf(fx::screen_width() - 8 - 16 * 14, 8 + 24 * 3, " bar: %6d", bar); 319 | fx::printf(fx::screen_width() - 8 - 16 * 14, 8 + 24 * 4, "filter: %s", filter_active ? " on" : "off"); 320 | fx::printf(fx::screen_width() - 8 - 16 * 14, 8 + 24 * 5, " model: %s", chip_model == SidEngine::MOS6581 ? "6581" : "8580"); 321 | fx::printf(fx::screen_width() - 8 - 16 * 14, 8 + 24 * 6, "engine:%7s", engines[engine_nr]->name()); 322 | 323 | const Uint8* ks = SDL_GetKeyboardState(nullptr); 324 | vert_pos += ks[SDL_SCANCODE_DOWN] - ks[SDL_SCANCODE_UP]; 325 | 326 | } 327 | }; 328 | 329 | 330 | void usage(char const* exe) { 331 | printf("usage: %s sid-file [song-number]\n", exe); 332 | exit(1); 333 | } 334 | 335 | int main(int argc, char** argv) { 336 | int song_nr = -1; 337 | if (argc == 3) song_nr = atoi(argv[2]); 338 | else if (argc != 2) usage(argv[0]); 339 | filename = argv[1]; 340 | if (!record.load(filename, song_nr)) usage(argv[0]); 341 | 342 | framerate = 50 * record.speed; 343 | samples_per_frame = MIXRATE / framerate; 344 | 345 | App app; 346 | return fx::run(app); 347 | } 348 | -------------------------------------------------------------------------------- /src/record.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "record.hpp" 5 | #include "cpu.hpp" 6 | 7 | class MyCPU : public CPU { 8 | public: 9 | void jsr(uint16_t npc, uint8_t na, std::function f = nullptr) { 10 | callback = f; 11 | CPU::jsr(npc, na); 12 | } 13 | 14 | void setmem(uint16_t addr, uint8_t value) override { 15 | if (callback) callback(addr, value); 16 | ram[addr] = value; 17 | } 18 | uint8_t getmem(uint16_t addr) override { 19 | return ram[addr]; 20 | } 21 | 22 | std::array ram = {}; 23 | std::function callback; 24 | }; 25 | 26 | 27 | struct Header { 28 | uint8_t magic[4]; 29 | uint16_t version; 30 | uint16_t offset; 31 | uint16_t load_addr; 32 | uint16_t init_addr; 33 | uint16_t play_addr; 34 | uint16_t song_count; 35 | uint16_t start_song; 36 | uint32_t speed; 37 | char song_name[32]; 38 | char song_author[32]; 39 | char song_released[32]; 40 | uint16_t flags; 41 | uint8_t start_page; 42 | uint8_t page_length; 43 | uint8_t sid_addr_2; 44 | uint8_t sid_addr_3; 45 | } __attribute__((packed)); 46 | 47 | 48 | uint32_t swap(uint16_t v) { return __builtin_bswap16(v); } 49 | uint32_t swap(uint32_t v) { return __builtin_bswap32(v); } 50 | 51 | 52 | bool Record::load(const char* filename, int nr) { 53 | *this = {}; 54 | 55 | // log 56 | const char* dot = strrchr(filename, '.'); 57 | if (dot && strcmp(dot, ".txt") == 0) { 58 | song_name = filename; 59 | speed = 1; 60 | song_nr = 1; 61 | song_count = 1; 62 | 63 | std::ifstream file(filename); 64 | if (!file.is_open()) { 65 | printf("error: could not open file\n"); 66 | return false; 67 | } 68 | std::string line; 69 | while (std::getline(file, line)) { 70 | State s; 71 | char const* p = line.c_str(); 72 | for (size_t i = 0; i < s.reg.size(); ++i) { 73 | s.reg[i] = strtoul(p, (char**) &p, 16); 74 | s.is_set[i] = true; 75 | } 76 | states.emplace_back(s); 77 | } 78 | // for (State const& s : states) { 79 | // for (uint8_t x : s.reg) printf(" %02X", x); 80 | // printf("\n"); 81 | // } 82 | return true; 83 | } 84 | 85 | 86 | std::ifstream file(filename, std::ios::binary | std::ios::ate); 87 | if (!file.is_open()) { 88 | printf("error: could not open file\n"); 89 | return false; 90 | } 91 | auto pos = file.tellg(); 92 | std::vector data(pos); 93 | file.seekg(0, std::ios::beg); 94 | file.read((char*) data.data(), pos); 95 | 96 | 97 | 98 | Header* h = (Header*) data.data(); 99 | h->version = swap(h->version); 100 | h->offset = swap(h->offset); 101 | h->load_addr = swap(h->load_addr); 102 | h->init_addr = swap(h->init_addr); 103 | h->play_addr = swap(h->play_addr); 104 | h->song_count = swap(h->song_count); 105 | h->start_song = swap(h->start_song); 106 | h->speed = swap(h->speed); 107 | 108 | // ??? 109 | h->load_addr = data[h->offset] | (data[h->offset + 1] << 8); 110 | 111 | printf("magic: %.4s\n", h->magic); 112 | printf("version: %d\n", h->version); 113 | printf("offset: %04x\n", h->offset); 114 | printf("load addr: %04x\n", h->load_addr); 115 | printf("init addr: %04x\n", h->init_addr); 116 | printf("play addr: %04x\n", h->play_addr); 117 | printf("song count: %d\n", h->song_count); 118 | printf("start song: %d\n", h->start_song); 119 | printf("speed: %08x\n", h->speed); 120 | printf("song name: %.32s\n", h->song_name); 121 | printf("song author: %.32s\n", h->song_author); 122 | printf("copyright: %.32s\n", h->song_released); 123 | if (h->version > 1) { 124 | h->flags = swap(h->flags); 125 | printf("flags: %04x\n", h->flags); 126 | printf("start page: %d\n", h->start_page); 127 | printf("page length: %d\n", h->page_length); 128 | printf("sid 2 addr: %d\n", h->sid_addr_2); 129 | printf("sid 3 addr: %d\n", h->sid_addr_3); 130 | } 131 | 132 | song_name = h->song_name; 133 | song_author = h->song_author; 134 | song_released = h->song_released; 135 | 136 | song_nr = nr > 0 ? nr : h->start_song; 137 | song_count = h->song_count; 138 | 139 | MyCPU cpu; 140 | 141 | uint16_t j = h->load_addr; 142 | for (int i = 2 + h->offset; i < (int) data.size(); ++i) { 143 | cpu.ram[j++] = data[i]; 144 | } 145 | 146 | // init song 147 | cpu.jsr(h->init_addr, song_nr - 1); 148 | 149 | // check timer 150 | if (song_nr < 32 && ((h->speed >> (song_nr - 1)) & 1)) { 151 | int timer = (cpu.ram[0xdc05] << 8) | cpu.ram[0xdc04]; 152 | // speed = (19656 + timer / 2) / timer; 153 | // speed = 19656.0f / timer; 154 | speed = round(19656 * 2.0f / timer) / 2; 155 | } 156 | else { 157 | speed = 1; 158 | } 159 | 160 | // play song 161 | for (int m = 0; m < 60 * 50 * 10 * speed; ++m) { 162 | State s; 163 | cpu.jsr(h->play_addr, 0, [&s](uint16_t addr, uint8_t value) { 164 | if (addr >= 0xd400 && addr < 0xd400 + s.is_set.size()) { 165 | s.is_set[addr - 0xd400] = true; 166 | } 167 | }); 168 | for (size_t i = 0; i < s.reg.size(); ++i) s.reg[i] = cpu.ram[0xd400 + i]; 169 | states.emplace_back(s); 170 | } 171 | 172 | return true; 173 | } 174 | 175 | -------------------------------------------------------------------------------- /src/record.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | struct Record { 10 | 11 | struct State { 12 | std::array is_set = {}; 13 | std::array reg = {}; 14 | }; 15 | 16 | std::vector states; 17 | std::string song_name; 18 | std::string song_author; 19 | std::string song_released; 20 | int song_count; 21 | int song_nr; 22 | float speed; 23 | 24 | bool load(const char* filename, int song_nr = 0); 25 | }; 26 | -------------------------------------------------------------------------------- /src/resid-0.16/AUTHORS: -------------------------------------------------------------------------------- 1 | Authors of reSID. 2 | 3 | Dag Lem: Designed and programmed complete emulation engine. 4 | -------------------------------------------------------------------------------- /src/resid-0.16/COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /src/resid-0.16/INSTALL: -------------------------------------------------------------------------------- 1 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software 2 | Foundation, Inc. 3 | 4 | This file is free documentation; the Free Software Foundation gives 5 | unlimited permission to copy, distribute and modify it. 6 | 7 | Basic Installation 8 | ================== 9 | 10 | These are generic installation instructions. 11 | 12 | The `configure' shell script attempts to guess correct values for 13 | various system-dependent variables used during compilation. It uses 14 | those values to create a `Makefile' in each directory of the package. 15 | It may also create one or more `.h' files containing system-dependent 16 | definitions. Finally, it creates a shell script `config.status' that 17 | you can run in the future to recreate the current configuration, and a 18 | file `config.log' containing compiler output (useful mainly for 19 | debugging `configure'). 20 | 21 | It can also use an optional file (typically called `config.cache' 22 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 23 | the results of its tests to speed up reconfiguring. (Caching is 24 | disabled by default to prevent problems with accidental use of stale 25 | cache files.) 26 | 27 | If you need to do unusual things to compile the package, please try 28 | to figure out how `configure' could check whether to do them, and mail 29 | diffs or instructions to the address given in the `README' so they can 30 | be considered for the next release. If you are using the cache, and at 31 | some point `config.cache' contains results you don't want to keep, you 32 | may remove or edit it. 33 | 34 | The file `configure.ac' (or `configure.in') is used to create 35 | `configure' by a program called `autoconf'. You only need 36 | `configure.ac' if you want to change it or regenerate `configure' using 37 | a newer version of `autoconf'. 38 | 39 | The simplest way to compile this package is: 40 | 41 | 1. `cd' to the directory containing the package's source code and type 42 | `./configure' to configure the package for your system. If you're 43 | using `csh' on an old version of System V, you might need to type 44 | `sh ./configure' instead to prevent `csh' from trying to execute 45 | `configure' itself. 46 | 47 | Running `configure' takes awhile. While running, it prints some 48 | messages telling which features it is checking for. 49 | 50 | 2. Type `make' to compile the package. 51 | 52 | 3. Optionally, type `make check' to run any self-tests that come with 53 | the package. 54 | 55 | 4. Type `make install' to install the programs and any data files and 56 | documentation. 57 | 58 | 5. You can remove the program binaries and object files from the 59 | source code directory by typing `make clean'. To also remove the 60 | files that `configure' created (so you can compile the package for 61 | a different kind of computer), type `make distclean'. There is 62 | also a `make maintainer-clean' target, but that is intended mainly 63 | for the package's developers. If you use it, you may have to get 64 | all sorts of other programs in order to regenerate files that came 65 | with the distribution. 66 | 67 | Compilers and Options 68 | ===================== 69 | 70 | Some systems require unusual options for compilation or linking that 71 | the `configure' script does not know about. Run `./configure --help' 72 | for details on some of the pertinent environment variables. 73 | 74 | You can give `configure' initial values for configuration parameters 75 | by setting variables in the command line or in the environment. Here 76 | is an example: 77 | 78 | ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix 79 | 80 | *Note Defining Variables::, for more details. 81 | 82 | Compiling For Multiple Architectures 83 | ==================================== 84 | 85 | You can compile the package for more than one kind of computer at the 86 | same time, by placing the object files for each architecture in their 87 | own directory. To do this, you must use a version of `make' that 88 | supports the `VPATH' variable, such as GNU `make'. `cd' to the 89 | directory where you want the object files and executables to go and run 90 | the `configure' script. `configure' automatically checks for the 91 | source code in the directory that `configure' is in and in `..'. 92 | 93 | If you have to use a `make' that does not support the `VPATH' 94 | variable, you have to compile the package for one architecture at a 95 | time in the source code directory. After you have installed the 96 | package for one architecture, use `make distclean' before reconfiguring 97 | for another architecture. 98 | 99 | Installation Names 100 | ================== 101 | 102 | By default, `make install' will install the package's files in 103 | `/usr/local/bin', `/usr/local/man', etc. You can specify an 104 | installation prefix other than `/usr/local' by giving `configure' the 105 | option `--prefix=PATH'. 106 | 107 | You can specify separate installation prefixes for 108 | architecture-specific files and architecture-independent files. If you 109 | give `configure' the option `--exec-prefix=PATH', the package will use 110 | PATH as the prefix for installing programs and libraries. 111 | Documentation and other data files will still use the regular prefix. 112 | 113 | In addition, if you use an unusual directory layout you can give 114 | options like `--bindir=PATH' to specify different values for particular 115 | kinds of files. Run `configure --help' for a list of the directories 116 | you can set and what kinds of files go in them. 117 | 118 | If the package supports it, you can cause programs to be installed 119 | with an extra prefix or suffix on their names by giving `configure' the 120 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 121 | 122 | Optional Features 123 | ================= 124 | 125 | Some packages pay attention to `--enable-FEATURE' options to 126 | `configure', where FEATURE indicates an optional part of the package. 127 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 128 | is something like `gnu-as' or `x' (for the X Window System). The 129 | `README' should mention any `--enable-' and `--with-' options that the 130 | package recognizes. 131 | 132 | For packages that use the X Window System, `configure' can usually 133 | find the X include and library files automatically, but if it doesn't, 134 | you can use the `configure' options `--x-includes=DIR' and 135 | `--x-libraries=DIR' to specify their locations. 136 | 137 | Specifying the System Type 138 | ========================== 139 | 140 | There may be some features `configure' cannot figure out 141 | automatically, but needs to determine by the type of machine the package 142 | will run on. Usually, assuming the package is built to be run on the 143 | _same_ architectures, `configure' can figure that out, but if it prints 144 | a message saying it cannot guess the machine type, give it the 145 | `--build=TYPE' option. TYPE can either be a short name for the system 146 | type, such as `sun4', or a canonical name which has the form: 147 | 148 | CPU-COMPANY-SYSTEM 149 | 150 | where SYSTEM can have one of these forms: 151 | 152 | OS KERNEL-OS 153 | 154 | See the file `config.sub' for the possible values of each field. If 155 | `config.sub' isn't included in this package, then this package doesn't 156 | need to know the machine type. 157 | 158 | If you are _building_ compiler tools for cross-compiling, you should 159 | use the `--target=TYPE' option to select the type of system they will 160 | produce code for. 161 | 162 | If you want to _use_ a cross compiler, that generates code for a 163 | platform different from the build platform, you should specify the 164 | "host" platform (i.e., that on which the generated programs will 165 | eventually be run) with `--host=TYPE'. 166 | 167 | Sharing Defaults 168 | ================ 169 | 170 | If you want to set default values for `configure' scripts to share, 171 | you can create a site shell script called `config.site' that gives 172 | default values for variables like `CC', `cache_file', and `prefix'. 173 | `configure' looks for `PREFIX/share/config.site' if it exists, then 174 | `PREFIX/etc/config.site' if it exists. Or, you can set the 175 | `CONFIG_SITE' environment variable to the location of the site script. 176 | A warning: not all `configure' scripts look for a site script. 177 | 178 | Defining Variables 179 | ================== 180 | 181 | Variables not defined in a site shell script can be set in the 182 | environment passed to `configure'. However, some packages may run 183 | configure again during the build, and the customized values of these 184 | variables may be lost. In order to avoid this problem, you should set 185 | them in the `configure' command line, using `VAR=value'. For example: 186 | 187 | ./configure CC=/usr/local2/bin/gcc 188 | 189 | will cause the specified gcc to be used as the C compiler (unless it is 190 | overridden in the site shell script). 191 | 192 | `configure' Invocation 193 | ====================== 194 | 195 | `configure' recognizes the following options to control how it 196 | operates. 197 | 198 | `--help' 199 | `-h' 200 | Print a summary of the options to `configure', and exit. 201 | 202 | `--version' 203 | `-V' 204 | Print the version of Autoconf used to generate the `configure' 205 | script, and exit. 206 | 207 | `--cache-file=FILE' 208 | Enable the cache: use and save the results of the tests in FILE, 209 | traditionally `config.cache'. FILE defaults to `/dev/null' to 210 | disable caching. 211 | 212 | `--config-cache' 213 | `-C' 214 | Alias for `--cache-file=config.cache'. 215 | 216 | `--quiet' 217 | `--silent' 218 | `-q' 219 | Do not print messages saying which checks are being made. To 220 | suppress all normal output, redirect it to `/dev/null' (any error 221 | messages will still be shown). 222 | 223 | `--srcdir=DIR' 224 | Look for the package's source code in directory DIR. Usually 225 | `configure' can determine that directory automatically. 226 | 227 | `configure' also accepts some other, not widely useful, options. Run 228 | `configure --help' for more details. 229 | 230 | -------------------------------------------------------------------------------- /src/resid-0.16/NEWS: -------------------------------------------------------------------------------- 1 | Changes in reSID version 0.16 2 | ----------------------------- 3 | 4 | An off-by-one error in the emulation of the ADSR delay bug has been 5 | fixed in the fast version of the envelope clocking. 6 | 7 | An initialization bug in the Filter class which caused floating point 8 | exceptions on some platforms has been fixed. 9 | 10 | Missing fields have been added to SID::State for correct snapshots. 11 | 12 | By building shifted FIR tables with samples according to the sampling 13 | frequency, the resampling code dramatically reduces the computational 14 | effort in the filter convolutions, without any loss of accuracy. The 15 | filter convolutions are now also vectorizable on current hardware. The 16 | implementation builds on ideas by Laurent Ganier. 17 | 18 | The resampling code has been split into two functions, one using 19 | interpolation and a small set of shifted filter tables, and one using 20 | direct lookup and a large set of shifted filter tables. The accuracy 21 | is the same, the difference is that the direct lookup runs has the 22 | potential of running at almost twice the speed (depending on cache 23 | size and memory bandwidth) using approximately 16MB more memory. It is 24 | now possible to run high quality resampling in real time on quite 25 | modest hardware, provided that a vectorizing compiler is used. 26 | 27 | 28 | Changes in reSID version 0.15 29 | ----------------------------- 30 | 31 | An error in the emulation of the ADSR delay bug has been fixed. When 32 | emulation of the ADSR delay bug was introduced in reSID 0.2, the delay 33 | was one cycle too long. One cycle was subtracted from the delay in 34 | reSID 0.4, however unfortunately one rate counter period was added as 35 | well, thus increasing the error. At the time there was no method to 36 | fully synchronize the CPU with envelope 3, so the measurements relied 37 | on averaging. Because of pipelining in the envelope logic the effects 38 | of a write are delayed, and this caused the test code to miss the 39 | target by exactly one rate counter period on a real SID. The current 40 | test code does achieve full synchronization with envelope 3, so this 41 | time the delay should be 100% correct. There are still side effects 42 | caused by pipelining which are not implemented in reSID, however these 43 | effects are not controllable without full synchronization with the 44 | envelope, something which is hard to achieve with envelope 3, and 45 | impossible with envelope 1 and 2. 46 | 47 | The envelope state (ADSR) has been added to the SID state, and the 48 | volume setting is now restored from the SID state. 49 | 50 | Filter scaling and clipping has been added to avoid sample overflows 51 | in the resampling filter. 52 | 53 | 54 | Changes in reSID version 0.14 55 | ----------------------------- 56 | 57 | The SID external audio input is now emulated. This can be used e.g. to 58 | simulate the hack of connecting a resistor from EXT IN to GND to boost 59 | the sample volume on the MOS8580. Calling sid.input(-32768) makes the 60 | MOS8580 sound more or less like the MOS6581 with respect to samples. 61 | The interface could also be used to mix in an external audio signal, 62 | but note that to do this correctly you should really resample the 63 | audio signal to 1MHz first. 64 | 65 | The filter settings are now updated immediately when the chip model is 66 | changed. Earlier the filter cutoff frequency would not change until 67 | the FC registers were updated. 68 | 69 | A one cycle error in the fast version of the envelope clocking has 70 | been fixed. This bug was introduced in reSID 0.13 and could affect the 71 | ADSR delay emulation. 72 | 73 | The exponential counter period is now only loaded at the envelope 74 | counter values 255, 93, 54, 26, 14, 6, 0. The period can be different 75 | for the same envelope counter value, depending on whether the envelope 76 | has been rising (attack -> release) or sinking (decay/release). 77 | 78 | A bug in the fast version of the noise register shift routine has been 79 | corrected. This bug caused too low noise frequency in some cases. 80 | 81 | The filter cutoff frequency is limited to 16kHz to keep the filter stable. 82 | 83 | 84 | Changes in reSID version 0.13 85 | ----------------------------- 86 | 87 | The internal DC levels of the MOS6581 have been double checked and 88 | corrected. The reason for the asymmetric scaling of the voice output 89 | has been found; there is a DC offset from the waveform D/A converter 90 | in addition to the DC offset from the envelope multiplying D/A 91 | converter. No selected waveform (N=P=S=T=0) yields minimum wave output 92 | level again. 93 | 94 | A bug in the fast version of the envelope clocking has been corrected. 95 | This bug could incorrectly invoke the ADSR delay emulation. 96 | 97 | 98 | Changes in reSID version 0.12 99 | ----------------------------- 100 | 101 | A bug causing incorrect sample spacing in the new SAMPLE_FAST sample 102 | calculation has been corrected. 103 | 104 | Audio clipping has been added to guard against sample overflows. 105 | 106 | To support multi-channel sampling, sample interleaving has been added 107 | to the clock() interface. 108 | 109 | To support synchronization with an external timer, an interface for 110 | sample rate adjustment has been added. 111 | 112 | The internal DC levels have been corrected. No selected waveform 113 | (N=P=S=T=0) yields maximum wave output level. Furthermore, each voice 114 | in the MOS6581 independently contributes to the DC level in the mixer, 115 | and the mixer itself has a small DC offset as well. The MOS8580 has no 116 | DC offsets. 117 | 118 | The spline interpolation routine has been generalized to accept 119 | repeated points to introduce points of non-differentiability and 120 | discontinuity. 121 | 122 | A separate mapping from the FC registers to filter cutoff frequency 123 | has been included for the MOS8580, and the mapping for the MOS6581 has 124 | been refined. 125 | 126 | 127 | Changes in reSID version 0.11 128 | ----------------------------- 129 | 130 | A new clock() interface has been added. This function generates audio 131 | samples into a buffer, greatly simplifying the task of writing driver 132 | code for reSID. It also facilitates more advanced audio sample 133 | generation, as described below. 134 | 135 | Three clocking methods are available: clocking at output sample 136 | frequency, clocking at cycle frequency with linear sample 137 | interpolation, and clocking at cycle frequency with audio resampling. 138 | 139 | Clocking at output sample frequency is fast, and yields acceptable 140 | sound quality, except for the SID combined waveforms, which have a 141 | very high frequency content. 142 | 143 | Clocking at cycle frequency with linear sample interpolation is 144 | approximately five to ten times slower at 44.1kHz sampling frequency, 145 | but the sound quality is improved because of the linear sample 146 | interpolation, and because some sampling noise is removed by the SID 147 | external filter, which attenuates signals above 16kHz. 148 | 149 | Finally, clocking at cycle frequency with audio resampling has a work 150 | rate which is independent of the sampling frequency; it is rather 151 | inversely proportional to the percentage of the bandwidth allocated 152 | to the filter transition band. This implies that e.g. with the 153 | transition band starting at ~ 20kHz, it is faster to generate 48kHz 154 | than 44.1kHz samples. 155 | 156 | Audio resampling is the theoretically correct method for sample 157 | generation, and delivers SID sound quality previously unheard of. This 158 | should make connoisseurs nod in appreciation, and for some time to 159 | come it could possibly also make people tear their hair over having to 160 | buy state of the art hardware to handle the obscene workload in real 161 | time. By trading off passband bandwidth for speed, real time 162 | processing is possible on current hardware. A 60% passband bandwidth 163 | is within the reach of reasonably fast machines, while maximum sound 164 | quality at 90% passband bandwidth, requiring four times the processing 165 | power, is not. Yet. 166 | 167 | 168 | Changes in reSID version 0.10 169 | ----------------------------- 170 | 171 | Libtool is now used to build the library. 172 | 173 | To keep the filters stable it is necessary to clock them at above 174 | sample rate. The chip clocking code has been modified to only 175 | "overclock" the filters, not the whole chip. This yields a 176 | considerable speedup without noticeably lowering sound quality. Note 177 | that this is aimed at slow hardware, if possible the 1 cycle clock 178 | interface should be used to eliminate sampling noise. 179 | 180 | 181 | Changes in reSID version 0.9 182 | ---------------------------- 183 | 184 | The sum of the filter outputs is no longer weighted. 185 | 186 | 187 | Changes in reSID version 0.8 188 | ---------------------------- 189 | 190 | voice3off has no effect if voice 3 is routed through the filter. 191 | 192 | 193 | Changes in reSID version 0.7 194 | ---------------------------- 195 | 196 | The audio output filter in the C64, external to the SID chip, has been 197 | modeled. 198 | 199 | The mapping function between the FC register and filter cutoff frequency can 200 | now be specified with spline interpolation points. This facilitates 201 | interactive modification of the mapping function by graphical presentation of 202 | the interpolation curve. The implementation of this novel spline design is 203 | fast and general purpose, and should be well suited for use in other projects 204 | as well. 205 | 206 | Filtered output has been inverted compared to unfiltered output. 207 | 208 | Aging of the bus value obtained when reading write only registers has been 209 | partly implemented. 210 | 211 | To facilitate offline storage the complete state of SID can be read and 212 | written. 213 | 214 | 215 | Changes in reSID version 0.6 216 | ---------------------------- 217 | 218 | A special case in synchronization has been implemented. 219 | 220 | The Autoconf script is cleaned up to allow compilation in a separate directory. 221 | 222 | 223 | Changes in reSID version 0.5 224 | ---------------------------- 225 | 226 | Emulation of MOS8580 combined waveforms. 227 | 228 | Version string resid_version_string provided for e.g. Autoconf tests. 229 | The string has C linkage. 230 | 231 | 232 | Changes in reSID version 0.4 233 | ---------------------------- 234 | 235 | The implementation of the ADSR delay bug has been refined and should now be 236 | cycle exact. 237 | 238 | The patch for VICE has been removed since VICE 0.15 will include reSID support. 239 | 240 | 241 | Changes in reSID version 0.3 242 | ---------------------------- 243 | 244 | The reSID library has changed name from libmos6581.a to libresid.a 245 | 246 | The pulse+sawtooth combined waveform has been corrected. 247 | 248 | Pulse+test bit samples are implemented. 249 | 250 | The envelope rate periods have finally been exactly determined. 251 | 252 | A new SID bug, the ADSR boundary bug, has been discovered and implemented. 253 | This bug makes it possible to step from envelope level 0x00 to 0xff or from 254 | 0xff to 0x00 in one step. 255 | 256 | One-cycle optimized overloads of the clock() functions have been implemented 257 | to facilitate sampling at 1MHz. 258 | 259 | The code has been further optimized for speed. 260 | 261 | 262 | Changes in reSID version 0.2 263 | ---------------------------- 264 | 265 | The implementation of the Envelope Generator has been rewritten to handle 266 | the infamous ADSR delay bug. All known envelope related bugs have been 267 | corrected. 268 | 269 | The maximum filter resonance is lowered to keep the filter stable. 270 | 271 | The reSID API has been simplified. Reading write only registers is allowed. 272 | -------------------------------------------------------------------------------- /src/resid-0.16/README: -------------------------------------------------------------------------------- 1 | This is reSID, a reverse engineered software emulation of the MOS6581 SID 2 | (Sound Interface Device). This chip was used in the Commodore 64 computer. 3 | 4 | reSID is free software. See the file COPYING for copying permission. 5 | 6 | reSID is a C++ library containing a complete emulation of the SID chip. 7 | This library can be linked into programs emulating the MOS6510 MPU to 8 | play music made for the Commodore 64 computer. reSID has been successfully 9 | linked into VICE, a full-fledged Commodore 64 emulator, and SIDPLAY, a 10 | popular SID tune player. The VICE home page is: 11 | http://www.viceteam.org/ 12 | A patch for SIDPLAY can be found on the SIDPLAY home page: 13 | http://www.geocities.com/SiliconValley/Lakes/5147/ 14 | 15 | Various SID emulators exist, however reSID should still be of great 16 | interest to Commodore 64 nostalgics. The emulator engine is cycle-based, 17 | emulating the internal operations of the SID chip. SID's audio filter is 18 | modeled as an actual two-integrator-loop biquadratic filter circuit. 19 | The engine has been developed based on available information on SID, sampling 20 | of the OSC3 and ENV3 registers, filter theory, and meticulous testing. 21 | In short, a scientific approach has been taken to model the SID chip as 22 | accurately as possible. 23 | 24 | To our knowledge reSID is by far the most accurate SID emulator ever created. 25 | This comes at a price; what is considered a fairly fast CPU at the time of 26 | this writing is needed to run the emulator. 27 | -------------------------------------------------------------------------------- /src/resid-0.16/THANKS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bt/sid-monitor/6bff2f0548d6e0e8208e90b0913022a0a4cb8652/src/resid-0.16/THANKS -------------------------------------------------------------------------------- /src/resid-0.16/TODO: -------------------------------------------------------------------------------- 1 | * Determine the characteristics of the SID filter integrators. Spice 2 | may perhaps be used to simulate the filter circuit. 3 | 4 | * Write documentation. Possibly a paper describing how SID was reverse 5 | engineered. 6 | 7 | * Implement a SID tune player. A PSID player, VSID, is partly 8 | implemented in VICE. 9 | -------------------------------------------------------------------------------- /src/resid-0.16/VC_CC_SUPPORT.txt: -------------------------------------------------------------------------------- 1 | To gain support for cc file extentions in VC5/6 you must modify 2 | the existing Windows registry keys to be as follows: 3 | 4 | [HKEY_CURRENT_USER\Software\Microsoft\Devstudio\6.0\Build System\Components\Platforms\Win32 (x86)\Tools\32-bit C/C++ Compiler for 80x86] 5 | "Input_Spec"="*.c;*.cpp;*.cxx;*.cc" 6 | [HKEY_CURRENT_USER\Software\Microsoft\Devstudio\6.0\Text Editor\Tabs/Language Settings\C/C++] 7 | "FileExtensions"="cpp;cxx;c;h;hxx;hpp;inl;tlh;tli;rc;rc2;cc" 8 | -------------------------------------------------------------------------------- /src/resid-0.16/envelope.cc: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #define __ENVELOPE_CC__ 21 | #include "envelope.h" 22 | 23 | RESID_NAMESPACE_START 24 | 25 | // ---------------------------------------------------------------------------- 26 | // Constructor. 27 | // ---------------------------------------------------------------------------- 28 | EnvelopeGenerator::EnvelopeGenerator() 29 | { 30 | reset(); 31 | } 32 | 33 | // ---------------------------------------------------------------------------- 34 | // SID reset. 35 | // ---------------------------------------------------------------------------- 36 | void EnvelopeGenerator::reset() 37 | { 38 | envelope_counter = 0; 39 | 40 | attack = 0; 41 | decay = 0; 42 | sustain = 0; 43 | release = 0; 44 | 45 | gate = 0; 46 | 47 | rate_counter = 0; 48 | exponential_counter = 0; 49 | exponential_counter_period = 1; 50 | 51 | state = RELEASE; 52 | rate_period = rate_counter_period[release]; 53 | hold_zero = true; 54 | } 55 | 56 | 57 | // Rate counter periods are calculated from the Envelope Rates table in 58 | // the Programmer's Reference Guide. The rate counter period is the number of 59 | // cycles between each increment of the envelope counter. 60 | // The rates have been verified by sampling ENV3. 61 | // 62 | // The rate counter is a 16 bit register which is incremented each cycle. 63 | // When the counter reaches a specific comparison value, the envelope counter 64 | // is incremented (attack) or decremented (decay/release) and the 65 | // counter is zeroed. 66 | // 67 | // NB! Sampling ENV3 shows that the calculated values are not exact. 68 | // It may seem like most calculated values have been rounded (.5 is rounded 69 | // down) and 1 has beed added to the result. A possible explanation for this 70 | // is that the SID designers have used the calculated values directly 71 | // as rate counter comparison values, not considering a one cycle delay to 72 | // zero the counter. This would yield an actual period of comparison value + 1. 73 | // 74 | // The time of the first envelope count can not be exactly controlled, except 75 | // possibly by resetting the chip. Because of this we cannot do cycle exact 76 | // sampling and must devise another method to calculate the rate counter 77 | // periods. 78 | // 79 | // The exact rate counter periods can be determined e.g. by counting the number 80 | // of cycles from envelope level 1 to envelope level 129, and dividing the 81 | // number of cycles by 128. CIA1 timer A and B in linked mode can perform 82 | // the cycle count. This is the method used to find the rates below. 83 | // 84 | // To avoid the ADSR delay bug, sampling of ENV3 should be done using 85 | // sustain = release = 0. This ensures that the attack state will not lower 86 | // the current rate counter period. 87 | // 88 | // The ENV3 sampling code below yields a maximum timing error of 14 cycles. 89 | // lda #$01 90 | // l1: cmp $d41c 91 | // bne l1 92 | // ... 93 | // lda #$ff 94 | // l2: cmp $d41c 95 | // bne l2 96 | // 97 | // This yields a maximum error for the calculated rate period of 14/128 cycles. 98 | // The described method is thus sufficient for exact calculation of the rate 99 | // periods. 100 | // 101 | reg16 EnvelopeGenerator::rate_counter_period[] = { 102 | 9, // 2ms*1.0MHz/256 = 7.81 103 | 32, // 8ms*1.0MHz/256 = 31.25 104 | 63, // 16ms*1.0MHz/256 = 62.50 105 | 95, // 24ms*1.0MHz/256 = 93.75 106 | 149, // 38ms*1.0MHz/256 = 148.44 107 | 220, // 56ms*1.0MHz/256 = 218.75 108 | 267, // 68ms*1.0MHz/256 = 265.63 109 | 313, // 80ms*1.0MHz/256 = 312.50 110 | 392, // 100ms*1.0MHz/256 = 390.63 111 | 977, // 250ms*1.0MHz/256 = 976.56 112 | 1954, // 500ms*1.0MHz/256 = 1953.13 113 | 3126, // 800ms*1.0MHz/256 = 3125.00 114 | 3907, // 1 s*1.0MHz/256 = 3906.25 115 | 11720, // 3 s*1.0MHz/256 = 11718.75 116 | 19532, // 5 s*1.0MHz/256 = 19531.25 117 | 31251 // 8 s*1.0MHz/256 = 31250.00 118 | }; 119 | 120 | 121 | // For decay and release, the clock to the envelope counter is sequentially 122 | // divided by 1, 2, 4, 8, 16, 30, 1 to create a piece-wise linear approximation 123 | // of an exponential. The exponential counter period is loaded at the envelope 124 | // counter values 255, 93, 54, 26, 14, 6, 0. The period can be different for the 125 | // same envelope counter value, depending on whether the envelope has been 126 | // rising (attack -> release) or sinking (decay/release). 127 | // 128 | // Since it is not possible to reset the rate counter (the test bit has no 129 | // influence on the envelope generator whatsoever) a method must be devised to 130 | // do cycle exact sampling of ENV3 to do the investigation. This is possible 131 | // with knowledge of the rate period for A=0, found above. 132 | // 133 | // The CPU can be synchronized with ENV3 by first synchronizing with the rate 134 | // counter by setting A=0 and wait in a carefully timed loop for the envelope 135 | // counter _not_ to change for 9 cycles. We can then wait for a specific value 136 | // of ENV3 with another timed loop to fully synchronize with ENV3. 137 | // 138 | // At the first period when an exponential counter period larger than one 139 | // is used (decay or relase), one extra cycle is spent before the envelope is 140 | // decremented. The envelope output is then delayed one cycle until the state 141 | // is changed to attack. Now one cycle less will be spent before the envelope 142 | // is incremented, and the situation is normalized. 143 | // The delay is probably caused by the comparison with the exponential counter, 144 | // and does not seem to affect the rate counter. This has been verified by 145 | // timing 256 consecutive complete envelopes with A = D = R = 1, S = 0, using 146 | // CIA1 timer A and B in linked mode. If the rate counter is not affected the 147 | // period of each complete envelope is 148 | // (255 + 162*1 + 39*2 + 28*4 + 12*8 + 8*16 + 6*30)*32 = 756*32 = 32352 149 | // which corresponds exactly to the timed value divided by the number of 150 | // complete envelopes. 151 | // NB! This one cycle delay is not modeled. 152 | 153 | 154 | // From the sustain levels it follows that both the low and high 4 bits of the 155 | // envelope counter are compared to the 4-bit sustain value. 156 | // This has been verified by sampling ENV3. 157 | // 158 | reg8 EnvelopeGenerator::sustain_level[] = { 159 | 0x00, 160 | 0x11, 161 | 0x22, 162 | 0x33, 163 | 0x44, 164 | 0x55, 165 | 0x66, 166 | 0x77, 167 | 0x88, 168 | 0x99, 169 | 0xaa, 170 | 0xbb, 171 | 0xcc, 172 | 0xdd, 173 | 0xee, 174 | 0xff, 175 | }; 176 | 177 | 178 | // ---------------------------------------------------------------------------- 179 | // Register functions. 180 | // ---------------------------------------------------------------------------- 181 | void EnvelopeGenerator::writeCONTROL_REG(reg8 control) 182 | { 183 | reg8 gate_next = control & 0x01; 184 | 185 | // The rate counter is never reset, thus there will be a delay before the 186 | // envelope counter starts counting up (attack) or down (release). 187 | 188 | // Gate bit on: Start attack, decay, sustain. 189 | if (!gate && gate_next) { 190 | state = ATTACK; 191 | rate_period = rate_counter_period[attack]; 192 | 193 | // Switching to attack state unlocks the zero freeze. 194 | hold_zero = false; 195 | } 196 | // Gate bit off: Start release. 197 | else if (gate && !gate_next) { 198 | state = RELEASE; 199 | rate_period = rate_counter_period[release]; 200 | } 201 | 202 | gate = gate_next; 203 | } 204 | 205 | void EnvelopeGenerator::writeATTACK_DECAY(reg8 attack_decay) 206 | { 207 | attack = (attack_decay >> 4) & 0x0f; 208 | decay = attack_decay & 0x0f; 209 | if (state == ATTACK) { 210 | rate_period = rate_counter_period[attack]; 211 | } 212 | else if (state == DECAY_SUSTAIN) { 213 | rate_period = rate_counter_period[decay]; 214 | } 215 | } 216 | 217 | void EnvelopeGenerator::writeSUSTAIN_RELEASE(reg8 sustain_release) 218 | { 219 | sustain = (sustain_release >> 4) & 0x0f; 220 | release = sustain_release & 0x0f; 221 | if (state == RELEASE) { 222 | rate_period = rate_counter_period[release]; 223 | } 224 | } 225 | 226 | reg8 EnvelopeGenerator::readENV() 227 | { 228 | return output(); 229 | } 230 | 231 | RESID_NAMESPACE_STOP 232 | -------------------------------------------------------------------------------- /src/resid-0.16/envelope.h: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #ifndef __ENVELOPE_H__ 21 | #define __ENVELOPE_H__ 22 | 23 | #include "siddefs.h" 24 | 25 | RESID_NAMESPACE_START 26 | 27 | // ---------------------------------------------------------------------------- 28 | // A 15 bit counter is used to implement the envelope rates, in effect 29 | // dividing the clock to the envelope counter by the currently selected rate 30 | // period. 31 | // In addition, another counter is used to implement the exponential envelope 32 | // decay, in effect further dividing the clock to the envelope counter. 33 | // The period of this counter is set to 1, 2, 4, 8, 16, 30 at the envelope 34 | // counter values 255, 93, 54, 26, 14, 6, respectively. 35 | // ---------------------------------------------------------------------------- 36 | class EnvelopeGenerator 37 | { 38 | public: 39 | EnvelopeGenerator(); 40 | 41 | enum State { ATTACK, DECAY_SUSTAIN, RELEASE }; 42 | 43 | RESID_INLINE void clock(); 44 | RESID_INLINE void clock(cycle_count delta_t); 45 | void reset(); 46 | 47 | void writeCONTROL_REG(reg8); 48 | void writeATTACK_DECAY(reg8); 49 | void writeSUSTAIN_RELEASE(reg8); 50 | reg8 readENV(); 51 | 52 | // 8-bit envelope output. 53 | RESID_INLINE reg8 output(); 54 | 55 | protected: 56 | reg16 rate_counter; 57 | reg16 rate_period; 58 | reg8 exponential_counter; 59 | reg8 exponential_counter_period; 60 | reg8 envelope_counter; 61 | bool hold_zero; 62 | 63 | reg4 attack; 64 | reg4 decay; 65 | reg4 sustain; 66 | reg4 release; 67 | 68 | reg8 gate; 69 | 70 | State state; 71 | 72 | // Lookup table to convert from attack, decay, or release value to rate 73 | // counter period. 74 | static reg16 rate_counter_period[]; 75 | 76 | // The 16 selectable sustain levels. 77 | static reg8 sustain_level[]; 78 | 79 | friend class SID; 80 | }; 81 | 82 | 83 | // ---------------------------------------------------------------------------- 84 | // Inline functions. 85 | // The following functions are defined inline because they are called every 86 | // time a sample is calculated. 87 | // ---------------------------------------------------------------------------- 88 | 89 | #if RESID_INLINING || defined(__ENVELOPE_CC__) 90 | 91 | // ---------------------------------------------------------------------------- 92 | // SID clocking - 1 cycle. 93 | // ---------------------------------------------------------------------------- 94 | RESID_INLINE 95 | void EnvelopeGenerator::clock() 96 | { 97 | // Check for ADSR delay bug. 98 | // If the rate counter comparison value is set below the current value of the 99 | // rate counter, the counter will continue counting up until it wraps around 100 | // to zero at 2^15 = 0x8000, and then count rate_period - 1 before the 101 | // envelope can finally be stepped. 102 | // This has been verified by sampling ENV3. 103 | // 104 | if (++rate_counter & 0x8000) { 105 | ++rate_counter &= 0x7fff; 106 | } 107 | 108 | if (rate_counter != rate_period) { 109 | return; 110 | } 111 | 112 | rate_counter = 0; 113 | 114 | // The first envelope step in the attack state also resets the exponential 115 | // counter. This has been verified by sampling ENV3. 116 | // 117 | if (state == ATTACK || ++exponential_counter == exponential_counter_period) 118 | { 119 | exponential_counter = 0; 120 | 121 | // Check whether the envelope counter is frozen at zero. 122 | if (hold_zero) { 123 | return; 124 | } 125 | 126 | switch (state) { 127 | case ATTACK: 128 | // The envelope counter can flip from 0xff to 0x00 by changing state to 129 | // release, then to attack. The envelope counter is then frozen at 130 | // zero; to unlock this situation the state must be changed to release, 131 | // then to attack. This has been verified by sampling ENV3. 132 | // 133 | ++envelope_counter &= 0xff; 134 | if (envelope_counter == 0xff) { 135 | state = DECAY_SUSTAIN; 136 | rate_period = rate_counter_period[decay]; 137 | } 138 | break; 139 | case DECAY_SUSTAIN: 140 | if (envelope_counter != sustain_level[sustain]) { 141 | --envelope_counter; 142 | } 143 | break; 144 | case RELEASE: 145 | // The envelope counter can flip from 0x00 to 0xff by changing state to 146 | // attack, then to release. The envelope counter will then continue 147 | // counting down in the release state. 148 | // This has been verified by sampling ENV3. 149 | // NB! The operation below requires two's complement integer. 150 | // 151 | --envelope_counter &= 0xff; 152 | break; 153 | } 154 | 155 | // Check for change of exponential counter period. 156 | switch (envelope_counter) { 157 | case 0xff: 158 | exponential_counter_period = 1; 159 | break; 160 | case 0x5d: 161 | exponential_counter_period = 2; 162 | break; 163 | case 0x36: 164 | exponential_counter_period = 4; 165 | break; 166 | case 0x1a: 167 | exponential_counter_period = 8; 168 | break; 169 | case 0x0e: 170 | exponential_counter_period = 16; 171 | break; 172 | case 0x06: 173 | exponential_counter_period = 30; 174 | break; 175 | case 0x00: 176 | exponential_counter_period = 1; 177 | 178 | // When the envelope counter is changed to zero, it is frozen at zero. 179 | // This has been verified by sampling ENV3. 180 | hold_zero = true; 181 | break; 182 | } 183 | } 184 | } 185 | 186 | 187 | // ---------------------------------------------------------------------------- 188 | // SID clocking - delta_t cycles. 189 | // ---------------------------------------------------------------------------- 190 | RESID_INLINE 191 | void EnvelopeGenerator::clock(cycle_count delta_t) 192 | { 193 | // Check for ADSR delay bug. 194 | // If the rate counter comparison value is set below the current value of the 195 | // rate counter, the counter will continue counting up until it wraps around 196 | // to zero at 2^15 = 0x8000, and then count rate_period - 1 before the 197 | // envelope can finally be stepped. 198 | // This has been verified by sampling ENV3. 199 | // 200 | 201 | // NB! This requires two's complement integer. 202 | int rate_step = rate_period - rate_counter; 203 | if (rate_step <= 0) { 204 | rate_step += 0x7fff; 205 | } 206 | 207 | while (delta_t) { 208 | if (delta_t < rate_step) { 209 | rate_counter += delta_t; 210 | if (rate_counter & 0x8000) { 211 | ++rate_counter &= 0x7fff; 212 | } 213 | return; 214 | } 215 | 216 | rate_counter = 0; 217 | delta_t -= rate_step; 218 | 219 | // The first envelope step in the attack state also resets the exponential 220 | // counter. This has been verified by sampling ENV3. 221 | // 222 | if (state == ATTACK || ++exponential_counter == exponential_counter_period) 223 | { 224 | exponential_counter = 0; 225 | 226 | // Check whether the envelope counter is frozen at zero. 227 | if (hold_zero) { 228 | rate_step = rate_period; 229 | continue; 230 | } 231 | 232 | switch (state) { 233 | case ATTACK: 234 | // The envelope counter can flip from 0xff to 0x00 by changing state to 235 | // release, then to attack. The envelope counter is then frozen at 236 | // zero; to unlock this situation the state must be changed to release, 237 | // then to attack. This has been verified by sampling ENV3. 238 | // 239 | ++envelope_counter &= 0xff; 240 | if (envelope_counter == 0xff) { 241 | state = DECAY_SUSTAIN; 242 | rate_period = rate_counter_period[decay]; 243 | } 244 | break; 245 | case DECAY_SUSTAIN: 246 | if (envelope_counter != sustain_level[sustain]) { 247 | --envelope_counter; 248 | } 249 | break; 250 | case RELEASE: 251 | // The envelope counter can flip from 0x00 to 0xff by changing state to 252 | // attack, then to release. The envelope counter will then continue 253 | // counting down in the release state. 254 | // This has been verified by sampling ENV3. 255 | // NB! The operation below requires two's complement integer. 256 | // 257 | --envelope_counter &= 0xff; 258 | break; 259 | } 260 | 261 | // Check for change of exponential counter period. 262 | switch (envelope_counter) { 263 | case 0xff: 264 | exponential_counter_period = 1; 265 | break; 266 | case 0x5d: 267 | exponential_counter_period = 2; 268 | break; 269 | case 0x36: 270 | exponential_counter_period = 4; 271 | break; 272 | case 0x1a: 273 | exponential_counter_period = 8; 274 | break; 275 | case 0x0e: 276 | exponential_counter_period = 16; 277 | break; 278 | case 0x06: 279 | exponential_counter_period = 30; 280 | break; 281 | case 0x00: 282 | exponential_counter_period = 1; 283 | 284 | // When the envelope counter is changed to zero, it is frozen at zero. 285 | // This has been verified by sampling ENV3. 286 | hold_zero = true; 287 | break; 288 | } 289 | } 290 | 291 | rate_step = rate_period; 292 | } 293 | } 294 | 295 | 296 | // ---------------------------------------------------------------------------- 297 | // Read the envelope generator output. 298 | // ---------------------------------------------------------------------------- 299 | RESID_INLINE 300 | reg8 EnvelopeGenerator::output() 301 | { 302 | return envelope_counter; 303 | } 304 | 305 | #endif // RESID_INLINING || defined(__ENVELOPE_CC__) 306 | 307 | RESID_NAMESPACE_STOP 308 | 309 | #endif // not __ENVELOPE_H__ 310 | -------------------------------------------------------------------------------- /src/resid-0.16/extfilt.cc: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #define __EXTFILT_CC__ 21 | #include "extfilt.h" 22 | 23 | RESID_NAMESPACE_START 24 | 25 | // ---------------------------------------------------------------------------- 26 | // Constructor. 27 | // ---------------------------------------------------------------------------- 28 | ExternalFilter::ExternalFilter() 29 | { 30 | reset(); 31 | enable_filter(true); 32 | set_sampling_parameter(15915.6); 33 | set_chip_model(MOS6581); 34 | } 35 | 36 | 37 | // ---------------------------------------------------------------------------- 38 | // Enable filter. 39 | // ---------------------------------------------------------------------------- 40 | void ExternalFilter::enable_filter(bool enable) 41 | { 42 | enabled = enable; 43 | } 44 | 45 | 46 | // ---------------------------------------------------------------------------- 47 | // Setup of the external filter sampling parameters. 48 | // ---------------------------------------------------------------------------- 49 | void ExternalFilter::set_sampling_parameter(double pass_freq) 50 | { 51 | static const double pi = 3.1415926535897932385; 52 | 53 | // Low-pass: R = 10kOhm, C = 1000pF; w0l = 1/RC = 1/(1e4*1e-9) = 100000 54 | // High-pass: R = 1kOhm, C = 10uF; w0h = 1/RC = 1/(1e3*1e-5) = 100 55 | // Multiply with 1.048576 to facilitate division by 1 000 000 by right- 56 | // shifting 20 times (2 ^ 20 = 1048576). 57 | 58 | w0hp = 105; 59 | w0lp = (sound_sample) (pass_freq * (2.0 * pi * 1.048576)); 60 | if (w0lp > 104858) 61 | w0lp = 104858; 62 | } 63 | 64 | 65 | // ---------------------------------------------------------------------------- 66 | // Set chip model. 67 | // ---------------------------------------------------------------------------- 68 | void ExternalFilter::set_chip_model(chip_model model) 69 | { 70 | if (model == MOS6581) { 71 | // Maximum mixer DC output level; to be removed if the external 72 | // filter is turned off: ((wave DC + voice DC)*voices + mixer DC)*volume 73 | // See voice.cc and filter.cc for an explanation of the values. 74 | mixer_DC = ((((0x800 - 0x380) + 0x800)*0xff*3 - 0xfff*0xff/18) >> 7)*0x0f; 75 | } 76 | else { 77 | // No DC offsets in the MOS8580. 78 | mixer_DC = 0; 79 | } 80 | } 81 | 82 | 83 | // ---------------------------------------------------------------------------- 84 | // SID reset. 85 | // ---------------------------------------------------------------------------- 86 | void ExternalFilter::reset() 87 | { 88 | // State of filter. 89 | Vlp = 0; 90 | Vhp = 0; 91 | Vo = 0; 92 | } 93 | 94 | RESID_NAMESPACE_STOP 95 | -------------------------------------------------------------------------------- /src/resid-0.16/extfilt.h: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #ifndef __EXTFILT_H__ 21 | #define __EXTFILT_H__ 22 | 23 | #include "siddefs.h" 24 | 25 | RESID_NAMESPACE_START 26 | 27 | // ---------------------------------------------------------------------------- 28 | // The audio output stage in a Commodore 64 consists of two STC networks, 29 | // a low-pass filter with 3-dB frequency 16kHz followed by a high-pass 30 | // filter with 3-dB frequency 16Hz (the latter provided an audio equipment 31 | // input impedance of 1kOhm). 32 | // The STC networks are connected with a BJT supposedly meant to act as 33 | // a unity gain buffer, which is not really how it works. A more elaborate 34 | // model would include the BJT, however DC circuit analysis yields BJT 35 | // base-emitter and emitter-base impedances sufficiently low to produce 36 | // additional low-pass and high-pass 3dB-frequencies in the order of hundreds 37 | // of kHz. This calls for a sampling frequency of several MHz, which is far 38 | // too high for practical use. 39 | // ---------------------------------------------------------------------------- 40 | class ExternalFilter 41 | { 42 | public: 43 | ExternalFilter(); 44 | 45 | void enable_filter(bool enable); 46 | void set_sampling_parameter(double pass_freq); 47 | void set_chip_model(chip_model model); 48 | 49 | RESID_INLINE void clock(sound_sample Vi); 50 | RESID_INLINE void clock(cycle_count delta_t, sound_sample Vi); 51 | void reset(); 52 | 53 | // Audio output (20 bits). 54 | RESID_INLINE sound_sample output(); 55 | 56 | protected: 57 | // Filter enabled. 58 | bool enabled; 59 | 60 | // Maximum mixer DC offset. 61 | sound_sample mixer_DC; 62 | 63 | // State of filters. 64 | sound_sample Vlp; // lowpass 65 | sound_sample Vhp; // highpass 66 | sound_sample Vo; 67 | 68 | // Cutoff frequencies. 69 | sound_sample w0lp; 70 | sound_sample w0hp; 71 | 72 | friend class SID; 73 | }; 74 | 75 | 76 | // ---------------------------------------------------------------------------- 77 | // Inline functions. 78 | // The following functions are defined inline because they are called every 79 | // time a sample is calculated. 80 | // ---------------------------------------------------------------------------- 81 | 82 | #if RESID_INLINING || defined(__EXTFILT_CC__) 83 | 84 | // ---------------------------------------------------------------------------- 85 | // SID clocking - 1 cycle. 86 | // ---------------------------------------------------------------------------- 87 | RESID_INLINE 88 | void ExternalFilter::clock(sound_sample Vi) 89 | { 90 | // This is handy for testing. 91 | if (!enabled) { 92 | // Remove maximum DC level since there is no filter to do it. 93 | Vlp = Vhp = 0; 94 | Vo = Vi - mixer_DC; 95 | return; 96 | } 97 | 98 | // delta_t is converted to seconds given a 1MHz clock by dividing 99 | // with 1 000 000. 100 | 101 | // Calculate filter outputs. 102 | // Vo = Vlp - Vhp; 103 | // Vlp = Vlp + w0lp*(Vi - Vlp)*delta_t; 104 | // Vhp = Vhp + w0hp*(Vlp - Vhp)*delta_t; 105 | 106 | sound_sample dVlp = (w0lp >> 8)*(Vi - Vlp) >> 12; 107 | sound_sample dVhp = w0hp*(Vlp - Vhp) >> 20; 108 | Vo = Vlp - Vhp; 109 | Vlp += dVlp; 110 | Vhp += dVhp; 111 | } 112 | 113 | // ---------------------------------------------------------------------------- 114 | // SID clocking - delta_t cycles. 115 | // ---------------------------------------------------------------------------- 116 | RESID_INLINE 117 | void ExternalFilter::clock(cycle_count delta_t, 118 | sound_sample Vi) 119 | { 120 | // This is handy for testing. 121 | if (!enabled) { 122 | // Remove maximum DC level since there is no filter to do it. 123 | Vlp = Vhp = 0; 124 | Vo = Vi - mixer_DC; 125 | return; 126 | } 127 | 128 | // Maximum delta cycles for the external filter to work satisfactorily 129 | // is approximately 8. 130 | cycle_count delta_t_flt = 8; 131 | 132 | while (delta_t) { 133 | if (delta_t < delta_t_flt) { 134 | delta_t_flt = delta_t; 135 | } 136 | 137 | // delta_t is converted to seconds given a 1MHz clock by dividing 138 | // with 1 000 000. 139 | 140 | // Calculate filter outputs. 141 | // Vo = Vlp - Vhp; 142 | // Vlp = Vlp + w0lp*(Vi - Vlp)*delta_t; 143 | // Vhp = Vhp + w0hp*(Vlp - Vhp)*delta_t; 144 | 145 | sound_sample dVlp = (w0lp*delta_t_flt >> 8)*(Vi - Vlp) >> 12; 146 | sound_sample dVhp = w0hp*delta_t_flt*(Vlp - Vhp) >> 20; 147 | Vo = Vlp - Vhp; 148 | Vlp += dVlp; 149 | Vhp += dVhp; 150 | 151 | delta_t -= delta_t_flt; 152 | } 153 | } 154 | 155 | 156 | // ---------------------------------------------------------------------------- 157 | // Audio output (19.5 bits). 158 | // ---------------------------------------------------------------------------- 159 | RESID_INLINE 160 | sound_sample ExternalFilter::output() 161 | { 162 | return Vo; 163 | } 164 | 165 | #endif // RESID_INLINING || defined(__EXTFILT_CC__) 166 | 167 | RESID_NAMESPACE_STOP 168 | 169 | #endif // not __EXTFILT_H__ 170 | -------------------------------------------------------------------------------- /src/resid-0.16/filter.cc: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #define __FILTER_CC__ 21 | #include "filter.h" 22 | 23 | RESID_NAMESPACE_START 24 | 25 | // Maximum cutoff frequency is specified as 26 | // FCmax = 2.6e-5/C = 2.6e-5/2200e-12 = 11818. 27 | // 28 | // Measurements indicate a cutoff frequency range of approximately 29 | // 220Hz - 18kHz on a MOS6581 fitted with 470pF capacitors. The function 30 | // mapping FC to cutoff frequency has the shape of the tanh function, with 31 | // a discontinuity at FCHI = 0x80. 32 | // In contrast, the MOS8580 almost perfectly corresponds with the 33 | // specification of a linear mapping from 30Hz to 12kHz. 34 | // 35 | // The mappings have been measured by feeding the SID with an external 36 | // signal since the chip itself is incapable of generating waveforms of 37 | // higher fundamental frequency than 4kHz. It is best to use the bandpass 38 | // output at full resonance to pick out the cutoff frequency at any given 39 | // FC setting. 40 | // 41 | // The mapping function is specified with spline interpolation points and 42 | // the function values are retrieved via table lookup. 43 | // 44 | // NB! Cutoff frequency characteristics may vary, we have modeled two 45 | // particular Commodore 64s. 46 | 47 | fc_point Filter::f0_points_6581[] = 48 | { 49 | // FC f FCHI FCLO 50 | // ---------------------------- 51 | { 0, 220 }, // 0x00 - repeated end point 52 | { 0, 220 }, // 0x00 53 | { 128, 230 }, // 0x10 54 | { 256, 250 }, // 0x20 55 | { 384, 300 }, // 0x30 56 | { 512, 420 }, // 0x40 57 | { 640, 780 }, // 0x50 58 | { 768, 1600 }, // 0x60 59 | { 832, 2300 }, // 0x68 60 | { 896, 3200 }, // 0x70 61 | { 960, 4300 }, // 0x78 62 | { 992, 5000 }, // 0x7c 63 | { 1008, 5400 }, // 0x7e 64 | { 1016, 5700 }, // 0x7f 65 | { 1023, 6000 }, // 0x7f 0x07 66 | { 1023, 6000 }, // 0x7f 0x07 - discontinuity 67 | { 1024, 4600 }, // 0x80 - 68 | { 1024, 4600 }, // 0x80 69 | { 1032, 4800 }, // 0x81 70 | { 1056, 5300 }, // 0x84 71 | { 1088, 6000 }, // 0x88 72 | { 1120, 6600 }, // 0x8c 73 | { 1152, 7200 }, // 0x90 74 | { 1280, 9500 }, // 0xa0 75 | { 1408, 12000 }, // 0xb0 76 | { 1536, 14500 }, // 0xc0 77 | { 1664, 16000 }, // 0xd0 78 | { 1792, 17100 }, // 0xe0 79 | { 1920, 17700 }, // 0xf0 80 | { 2047, 18000 }, // 0xff 0x07 81 | { 2047, 18000 } // 0xff 0x07 - repeated end point 82 | }; 83 | 84 | fc_point Filter::f0_points_8580[] = 85 | { 86 | // FC f FCHI FCLO 87 | // ---------------------------- 88 | { 0, 0 }, // 0x00 - repeated end point 89 | { 0, 0 }, // 0x00 90 | { 128, 800 }, // 0x10 91 | { 256, 1600 }, // 0x20 92 | { 384, 2500 }, // 0x30 93 | { 512, 3300 }, // 0x40 94 | { 640, 4100 }, // 0x50 95 | { 768, 4800 }, // 0x60 96 | { 896, 5600 }, // 0x70 97 | { 1024, 6500 }, // 0x80 98 | { 1152, 7500 }, // 0x90 99 | { 1280, 8400 }, // 0xa0 100 | { 1408, 9200 }, // 0xb0 101 | { 1536, 9800 }, // 0xc0 102 | { 1664, 10500 }, // 0xd0 103 | { 1792, 11000 }, // 0xe0 104 | { 1920, 11700 }, // 0xf0 105 | { 2047, 12500 }, // 0xff 0x07 106 | { 2047, 12500 } // 0xff 0x07 - repeated end point 107 | }; 108 | 109 | 110 | // ---------------------------------------------------------------------------- 111 | // Constructor. 112 | // ---------------------------------------------------------------------------- 113 | Filter::Filter() 114 | { 115 | fc = 0; 116 | 117 | res = 0; 118 | 119 | filt = 0; 120 | 121 | voice3off = 0; 122 | 123 | hp_bp_lp = 0; 124 | 125 | vol = 0; 126 | 127 | // State of filter. 128 | Vhp = 0; 129 | Vbp = 0; 130 | Vlp = 0; 131 | Vnf = 0; 132 | 133 | enable_filter(true); 134 | 135 | // Create mappings from FC to cutoff frequency. 136 | interpolate(f0_points_6581, f0_points_6581 137 | + sizeof(f0_points_6581)/sizeof(*f0_points_6581) - 1, 138 | PointPlotter(f0_6581), 1.0); 139 | interpolate(f0_points_8580, f0_points_8580 140 | + sizeof(f0_points_8580)/sizeof(*f0_points_8580) - 1, 141 | PointPlotter(f0_8580), 1.0); 142 | 143 | set_chip_model(MOS6581); 144 | } 145 | 146 | 147 | // ---------------------------------------------------------------------------- 148 | // Enable filter. 149 | // ---------------------------------------------------------------------------- 150 | void Filter::enable_filter(bool enable) 151 | { 152 | enabled = enable; 153 | } 154 | 155 | 156 | // ---------------------------------------------------------------------------- 157 | // Set chip model. 158 | // ---------------------------------------------------------------------------- 159 | void Filter::set_chip_model(chip_model model) 160 | { 161 | if (model == MOS6581) { 162 | // The mixer has a small input DC offset. This is found as follows: 163 | // 164 | // The "zero" output level of the mixer measured on the SID audio 165 | // output pin is 5.50V at zero volume, and 5.44 at full 166 | // volume. This yields a DC offset of (5.44V - 5.50V) = -0.06V. 167 | // 168 | // The DC offset is thus -0.06V/1.05V ~ -1/18 of the dynamic range 169 | // of one voice. See voice.cc for measurement of the dynamic 170 | // range. 171 | 172 | mixer_DC = -0xfff*0xff/18 >> 7; 173 | 174 | f0 = f0_6581; 175 | f0_points = f0_points_6581; 176 | f0_count = sizeof(f0_points_6581)/sizeof(*f0_points_6581); 177 | } 178 | else { 179 | // No DC offsets in the MOS8580. 180 | mixer_DC = 0; 181 | 182 | f0 = f0_8580; 183 | f0_points = f0_points_8580; 184 | f0_count = sizeof(f0_points_8580)/sizeof(*f0_points_8580); 185 | } 186 | 187 | set_w0(); 188 | set_Q(); 189 | } 190 | 191 | 192 | // ---------------------------------------------------------------------------- 193 | // SID reset. 194 | // ---------------------------------------------------------------------------- 195 | void Filter::reset() 196 | { 197 | fc = 0; 198 | 199 | res = 0; 200 | 201 | filt = 0; 202 | 203 | voice3off = 0; 204 | 205 | hp_bp_lp = 0; 206 | 207 | vol = 0; 208 | 209 | // State of filter. 210 | Vhp = 0; 211 | Vbp = 0; 212 | Vlp = 0; 213 | Vnf = 0; 214 | 215 | set_w0(); 216 | set_Q(); 217 | } 218 | 219 | 220 | // ---------------------------------------------------------------------------- 221 | // Register functions. 222 | // ---------------------------------------------------------------------------- 223 | void Filter::writeFC_LO(reg8 fc_lo) 224 | { 225 | fc = fc & 0x7f8 | fc_lo & 0x007; 226 | set_w0(); 227 | } 228 | 229 | void Filter::writeFC_HI(reg8 fc_hi) 230 | { 231 | fc = (fc_hi << 3) & 0x7f8 | fc & 0x007; 232 | set_w0(); 233 | } 234 | 235 | void Filter::writeRES_FILT(reg8 res_filt) 236 | { 237 | res = (res_filt >> 4) & 0x0f; 238 | set_Q(); 239 | 240 | filt = res_filt & 0x0f; 241 | } 242 | 243 | void Filter::writeMODE_VOL(reg8 mode_vol) 244 | { 245 | voice3off = mode_vol & 0x80; 246 | 247 | hp_bp_lp = (mode_vol >> 4) & 0x07; 248 | 249 | vol = mode_vol & 0x0f; 250 | } 251 | 252 | // Set filter cutoff frequency. 253 | void Filter::set_w0() 254 | { 255 | const double pi = 3.1415926535897932385; 256 | 257 | // Multiply with 1.048576 to facilitate division by 1 000 000 by right- 258 | // shifting 20 times (2 ^ 20 = 1048576). 259 | w0 = static_cast(2*pi*f0[fc]*1.048576); 260 | 261 | // Limit f0 to 16kHz to keep 1 cycle filter stable. 262 | const sound_sample w0_max_1 = static_cast(2*pi*16000*1.048576); 263 | w0_ceil_1 = w0 <= w0_max_1 ? w0 : w0_max_1; 264 | 265 | // Limit f0 to 4kHz to keep delta_t cycle filter stable. 266 | const sound_sample w0_max_dt = static_cast(2*pi*4000*1.048576); 267 | w0_ceil_dt = w0 <= w0_max_dt ? w0 : w0_max_dt; 268 | } 269 | 270 | // Set filter resonance. 271 | void Filter::set_Q() 272 | { 273 | // Q is controlled linearly by res. Q has approximate range [0.707, 1.7]. 274 | // As resonance is increased, the filter must be clocked more often to keep 275 | // stable. 276 | 277 | // The coefficient 1024 is dispensed of later by right-shifting 10 times 278 | // (2 ^ 10 = 1024). 279 | _1024_div_Q = static_cast(1024.0/(0.707 + 1.0*res/0x0f)); 280 | } 281 | 282 | // ---------------------------------------------------------------------------- 283 | // Spline functions. 284 | // ---------------------------------------------------------------------------- 285 | 286 | // ---------------------------------------------------------------------------- 287 | // Return the array of spline interpolation points used to map the FC register 288 | // to filter cutoff frequency. 289 | // ---------------------------------------------------------------------------- 290 | void Filter::fc_default(const fc_point*& points, int& count) 291 | { 292 | points = f0_points; 293 | count = f0_count; 294 | } 295 | 296 | // ---------------------------------------------------------------------------- 297 | // Given an array of interpolation points p with n points, the following 298 | // statement will specify a new FC mapping: 299 | // interpolate(p, p + n - 1, filter.fc_plotter(), 1.0); 300 | // Note that the x range of the interpolation points *must* be [0, 2047], 301 | // and that additional end points *must* be present since the end points 302 | // are not interpolated. 303 | // ---------------------------------------------------------------------------- 304 | PointPlotter Filter::fc_plotter() 305 | { 306 | return PointPlotter(f0); 307 | } 308 | 309 | RESID_NAMESPACE_STOP 310 | -------------------------------------------------------------------------------- /src/resid-0.16/filter.h: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #ifndef __FILTER_H__ 21 | #define __FILTER_H__ 22 | 23 | #include "siddefs.h" 24 | #include "spline.h" 25 | 26 | RESID_NAMESPACE_START 27 | 28 | // ---------------------------------------------------------------------------- 29 | // The SID filter is modeled with a two-integrator-loop biquadratic filter, 30 | // which has been confirmed by Bob Yannes to be the actual circuit used in 31 | // the SID chip. 32 | // 33 | // Measurements show that excellent emulation of the SID filter is achieved, 34 | // except when high resonance is combined with high sustain levels. 35 | // In this case the SID op-amps are performing less than ideally and are 36 | // causing some peculiar behavior of the SID filter. This however seems to 37 | // have more effect on the overall amplitude than on the color of the sound. 38 | // 39 | // The theory for the filter circuit can be found in "Microelectric Circuits" 40 | // by Adel S. Sedra and Kenneth C. Smith. 41 | // The circuit is modeled based on the explanation found there except that 42 | // an additional inverter is used in the feedback from the bandpass output, 43 | // allowing the summer op-amp to operate in single-ended mode. This yields 44 | // inverted filter outputs with levels independent of Q, which corresponds with 45 | // the results obtained from a real SID. 46 | // 47 | // We have been able to model the summer and the two integrators of the circuit 48 | // to form components of an IIR filter. 49 | // Vhp is the output of the summer, Vbp is the output of the first integrator, 50 | // and Vlp is the output of the second integrator in the filter circuit. 51 | // 52 | // According to Bob Yannes, the active stages of the SID filter are not really 53 | // op-amps. Rather, simple NMOS inverters are used. By biasing an inverter 54 | // into its region of quasi-linear operation using a feedback resistor from 55 | // input to output, a MOS inverter can be made to act like an op-amp for 56 | // small signals centered around the switching threshold. 57 | // 58 | // Qualified guesses at SID filter schematics are depicted below. 59 | // 60 | // SID filter 61 | // ---------- 62 | // 63 | // ----------------------------------------------- 64 | // | | 65 | // | ---Rq-- | 66 | // | | | | 67 | // | --------------|--R-----[A>--|--R-----[A>--| 74 | // | | | | 75 | // vi -----R1-- | | | 76 | // 77 | // vhp vbp vlp 78 | // 79 | // 80 | // vi - input voltage 81 | // vhp - highpass output 82 | // vbp - bandpass output 83 | // vlp - lowpass output 84 | // [A> - op-amp 85 | // R1 - summer resistor 86 | // Rq - resistor array controlling resonance (4 resistors) 87 | // R - NMOS FET voltage controlled resistor controlling cutoff frequency 88 | // Rs - shunt resitor 89 | // C - capacitor 90 | // 91 | // 92 | // 93 | // SID integrator 94 | // -------------- 95 | // 96 | // V+ 97 | // 98 | // | 99 | // | 100 | // -----| 101 | // | | 102 | // | ||-- 103 | // -|| 104 | // ---C--- ||-> 105 | // | | | 106 | // |---Rs-----------|---- vo 107 | // | | 108 | // | ||-- 109 | // vi ---- -----|------------|| 110 | // | ^ | ||-> 111 | // |___| | | 112 | // ----- | | 113 | // | | | 114 | // |---R2-- | 115 | // | 116 | // R1 V- 117 | // | 118 | // | 119 | // 120 | // Vw 121 | // 122 | // ---------------------------------------------------------------------------- 123 | class Filter 124 | { 125 | public: 126 | Filter(); 127 | 128 | void enable_filter(bool enable); 129 | void set_chip_model(chip_model model); 130 | 131 | RESID_INLINE 132 | void clock(sound_sample voice1, sound_sample voice2, sound_sample voice3, 133 | sound_sample ext_in); 134 | RESID_INLINE 135 | void clock(cycle_count delta_t, 136 | sound_sample voice1, sound_sample voice2, sound_sample voice3, 137 | sound_sample ext_in); 138 | void reset(); 139 | 140 | // Write registers. 141 | void writeFC_LO(reg8); 142 | void writeFC_HI(reg8); 143 | void writeRES_FILT(reg8); 144 | void writeMODE_VOL(reg8); 145 | 146 | // SID audio output (16 bits). 147 | sound_sample output(); 148 | 149 | // Spline functions. 150 | void fc_default(const fc_point*& points, int& count); 151 | PointPlotter fc_plotter(); 152 | 153 | protected: 154 | void set_w0(); 155 | void set_Q(); 156 | 157 | // Filter enabled. 158 | bool enabled; 159 | 160 | // Filter cutoff frequency. 161 | reg12 fc; 162 | 163 | // Filter resonance. 164 | reg8 res; 165 | 166 | // Selects which inputs to route through filter. 167 | reg8 filt; 168 | 169 | // Switch voice 3 off. 170 | reg8 voice3off; 171 | 172 | // Highpass, bandpass, and lowpass filter modes. 173 | reg8 hp_bp_lp; 174 | 175 | // Output master volume. 176 | reg4 vol; 177 | 178 | // Mixer DC offset. 179 | sound_sample mixer_DC; 180 | 181 | // State of filter. 182 | sound_sample Vhp; // highpass 183 | sound_sample Vbp; // bandpass 184 | sound_sample Vlp; // lowpass 185 | sound_sample Vnf; // not filtered 186 | 187 | // Cutoff frequency, resonance. 188 | sound_sample w0, w0_ceil_1, w0_ceil_dt; 189 | sound_sample _1024_div_Q; 190 | 191 | // Cutoff frequency tables. 192 | // FC is an 11 bit register. 193 | sound_sample f0_6581[2048]; 194 | sound_sample f0_8580[2048]; 195 | sound_sample* f0; 196 | static fc_point f0_points_6581[]; 197 | static fc_point f0_points_8580[]; 198 | fc_point* f0_points; 199 | int f0_count; 200 | 201 | friend class SID; 202 | }; 203 | 204 | 205 | // ---------------------------------------------------------------------------- 206 | // Inline functions. 207 | // The following functions are defined inline because they are called every 208 | // time a sample is calculated. 209 | // ---------------------------------------------------------------------------- 210 | 211 | #if RESID_INLINING || defined(__FILTER_CC__) 212 | 213 | // ---------------------------------------------------------------------------- 214 | // SID clocking - 1 cycle. 215 | // ---------------------------------------------------------------------------- 216 | RESID_INLINE 217 | void Filter::clock(sound_sample voice1, 218 | sound_sample voice2, 219 | sound_sample voice3, 220 | sound_sample ext_in) 221 | { 222 | // Scale each voice down from 20 to 13 bits. 223 | voice1 >>= 7; 224 | voice2 >>= 7; 225 | 226 | // NB! Voice 3 is not silenced by voice3off if it is routed through 227 | // the filter. 228 | if (voice3off && !(filt & 0x04)) { 229 | voice3 = 0; 230 | } 231 | else { 232 | voice3 >>= 7; 233 | } 234 | 235 | ext_in >>= 7; 236 | 237 | // This is handy for testing. 238 | if (!enabled) { 239 | Vnf = voice1 + voice2 + voice3 + ext_in; 240 | Vhp = Vbp = Vlp = 0; 241 | return; 242 | } 243 | 244 | // Route voices into or around filter. 245 | // The code below is expanded to a switch for faster execution. 246 | // (filt1 ? Vi : Vnf) += voice1; 247 | // (filt2 ? Vi : Vnf) += voice2; 248 | // (filt3 ? Vi : Vnf) += voice3; 249 | 250 | sound_sample Vi; 251 | 252 | switch (filt) { 253 | default: 254 | case 0x0: 255 | Vi = 0; 256 | Vnf = voice1 + voice2 + voice3 + ext_in; 257 | break; 258 | case 0x1: 259 | Vi = voice1; 260 | Vnf = voice2 + voice3 + ext_in; 261 | break; 262 | case 0x2: 263 | Vi = voice2; 264 | Vnf = voice1 + voice3 + ext_in; 265 | break; 266 | case 0x3: 267 | Vi = voice1 + voice2; 268 | Vnf = voice3 + ext_in; 269 | break; 270 | case 0x4: 271 | Vi = voice3; 272 | Vnf = voice1 + voice2 + ext_in; 273 | break; 274 | case 0x5: 275 | Vi = voice1 + voice3; 276 | Vnf = voice2 + ext_in; 277 | break; 278 | case 0x6: 279 | Vi = voice2 + voice3; 280 | Vnf = voice1 + ext_in; 281 | break; 282 | case 0x7: 283 | Vi = voice1 + voice2 + voice3; 284 | Vnf = ext_in; 285 | break; 286 | case 0x8: 287 | Vi = ext_in; 288 | Vnf = voice1 + voice2 + voice3; 289 | break; 290 | case 0x9: 291 | Vi = voice1 + ext_in; 292 | Vnf = voice2 + voice3; 293 | break; 294 | case 0xa: 295 | Vi = voice2 + ext_in; 296 | Vnf = voice1 + voice3; 297 | break; 298 | case 0xb: 299 | Vi = voice1 + voice2 + ext_in; 300 | Vnf = voice3; 301 | break; 302 | case 0xc: 303 | Vi = voice3 + ext_in; 304 | Vnf = voice1 + voice2; 305 | break; 306 | case 0xd: 307 | Vi = voice1 + voice3 + ext_in; 308 | Vnf = voice2; 309 | break; 310 | case 0xe: 311 | Vi = voice2 + voice3 + ext_in; 312 | Vnf = voice1; 313 | break; 314 | case 0xf: 315 | Vi = voice1 + voice2 + voice3 + ext_in; 316 | Vnf = 0; 317 | break; 318 | } 319 | 320 | // delta_t = 1 is converted to seconds given a 1MHz clock by dividing 321 | // with 1 000 000. 322 | 323 | // Calculate filter outputs. 324 | // Vhp = Vbp/Q - Vlp - Vi; 325 | // dVbp = -w0*Vhp*dt; 326 | // dVlp = -w0*Vbp*dt; 327 | 328 | sound_sample dVbp = (w0_ceil_1*Vhp >> 20); 329 | sound_sample dVlp = (w0_ceil_1*Vbp >> 20); 330 | Vbp -= dVbp; 331 | Vlp -= dVlp; 332 | Vhp = (Vbp*_1024_div_Q >> 10) - Vlp - Vi; 333 | } 334 | 335 | // ---------------------------------------------------------------------------- 336 | // SID clocking - delta_t cycles. 337 | // ---------------------------------------------------------------------------- 338 | RESID_INLINE 339 | void Filter::clock(cycle_count delta_t, 340 | sound_sample voice1, 341 | sound_sample voice2, 342 | sound_sample voice3, 343 | sound_sample ext_in) 344 | { 345 | // Scale each voice down from 20 to 13 bits. 346 | voice1 >>= 7; 347 | voice2 >>= 7; 348 | 349 | // NB! Voice 3 is not silenced by voice3off if it is routed through 350 | // the filter. 351 | if (voice3off && !(filt & 0x04)) { 352 | voice3 = 0; 353 | } 354 | else { 355 | voice3 >>= 7; 356 | } 357 | 358 | ext_in >>= 7; 359 | 360 | // Enable filter on/off. 361 | // This is not really part of SID, but is useful for testing. 362 | // On slow CPUs it may be necessary to bypass the filter to lower the CPU 363 | // load. 364 | if (!enabled) { 365 | Vnf = voice1 + voice2 + voice3 + ext_in; 366 | Vhp = Vbp = Vlp = 0; 367 | return; 368 | } 369 | 370 | // Route voices into or around filter. 371 | // The code below is expanded to a switch for faster execution. 372 | // (filt1 ? Vi : Vnf) += voice1; 373 | // (filt2 ? Vi : Vnf) += voice2; 374 | // (filt3 ? Vi : Vnf) += voice3; 375 | 376 | sound_sample Vi; 377 | 378 | switch (filt) { 379 | default: 380 | case 0x0: 381 | Vi = 0; 382 | Vnf = voice1 + voice2 + voice3 + ext_in; 383 | break; 384 | case 0x1: 385 | Vi = voice1; 386 | Vnf = voice2 + voice3 + ext_in; 387 | break; 388 | case 0x2: 389 | Vi = voice2; 390 | Vnf = voice1 + voice3 + ext_in; 391 | break; 392 | case 0x3: 393 | Vi = voice1 + voice2; 394 | Vnf = voice3 + ext_in; 395 | break; 396 | case 0x4: 397 | Vi = voice3; 398 | Vnf = voice1 + voice2 + ext_in; 399 | break; 400 | case 0x5: 401 | Vi = voice1 + voice3; 402 | Vnf = voice2 + ext_in; 403 | break; 404 | case 0x6: 405 | Vi = voice2 + voice3; 406 | Vnf = voice1 + ext_in; 407 | break; 408 | case 0x7: 409 | Vi = voice1 + voice2 + voice3; 410 | Vnf = ext_in; 411 | break; 412 | case 0x8: 413 | Vi = ext_in; 414 | Vnf = voice1 + voice2 + voice3; 415 | break; 416 | case 0x9: 417 | Vi = voice1 + ext_in; 418 | Vnf = voice2 + voice3; 419 | break; 420 | case 0xa: 421 | Vi = voice2 + ext_in; 422 | Vnf = voice1 + voice3; 423 | break; 424 | case 0xb: 425 | Vi = voice1 + voice2 + ext_in; 426 | Vnf = voice3; 427 | break; 428 | case 0xc: 429 | Vi = voice3 + ext_in; 430 | Vnf = voice1 + voice2; 431 | break; 432 | case 0xd: 433 | Vi = voice1 + voice3 + ext_in; 434 | Vnf = voice2; 435 | break; 436 | case 0xe: 437 | Vi = voice2 + voice3 + ext_in; 438 | Vnf = voice1; 439 | break; 440 | case 0xf: 441 | Vi = voice1 + voice2 + voice3 + ext_in; 442 | Vnf = 0; 443 | break; 444 | } 445 | 446 | // Maximum delta cycles for the filter to work satisfactorily under current 447 | // cutoff frequency and resonance constraints is approximately 8. 448 | cycle_count delta_t_flt = 8; 449 | 450 | while (delta_t) { 451 | if (delta_t < delta_t_flt) { 452 | delta_t_flt = delta_t; 453 | } 454 | 455 | // delta_t is converted to seconds given a 1MHz clock by dividing 456 | // with 1 000 000. This is done in two operations to avoid integer 457 | // multiplication overflow. 458 | 459 | // Calculate filter outputs. 460 | // Vhp = Vbp/Q - Vlp - Vi; 461 | // dVbp = -w0*Vhp*dt; 462 | // dVlp = -w0*Vbp*dt; 463 | sound_sample w0_delta_t = w0_ceil_dt*delta_t_flt >> 6; 464 | 465 | sound_sample dVbp = (w0_delta_t*Vhp >> 14); 466 | sound_sample dVlp = (w0_delta_t*Vbp >> 14); 467 | Vbp -= dVbp; 468 | Vlp -= dVlp; 469 | Vhp = (Vbp*_1024_div_Q >> 10) - Vlp - Vi; 470 | 471 | delta_t -= delta_t_flt; 472 | } 473 | } 474 | 475 | 476 | // ---------------------------------------------------------------------------- 477 | // SID audio output (20 bits). 478 | // ---------------------------------------------------------------------------- 479 | RESID_INLINE 480 | sound_sample Filter::output() 481 | { 482 | // This is handy for testing. 483 | if (!enabled) { 484 | return (Vnf + mixer_DC)*static_cast(vol); 485 | } 486 | 487 | // Mix highpass, bandpass, and lowpass outputs. The sum is not 488 | // weighted, this can be confirmed by sampling sound output for 489 | // e.g. bandpass, lowpass, and bandpass+lowpass from a SID chip. 490 | 491 | // The code below is expanded to a switch for faster execution. 492 | // if (hp) Vf += Vhp; 493 | // if (bp) Vf += Vbp; 494 | // if (lp) Vf += Vlp; 495 | 496 | sound_sample Vf; 497 | 498 | switch (hp_bp_lp) { 499 | default: 500 | case 0x0: 501 | Vf = 0; 502 | break; 503 | case 0x1: 504 | Vf = Vlp; 505 | break; 506 | case 0x2: 507 | Vf = Vbp; 508 | break; 509 | case 0x3: 510 | Vf = Vlp + Vbp; 511 | break; 512 | case 0x4: 513 | Vf = Vhp; 514 | break; 515 | case 0x5: 516 | Vf = Vlp + Vhp; 517 | break; 518 | case 0x6: 519 | Vf = Vbp + Vhp; 520 | break; 521 | case 0x7: 522 | Vf = Vlp + Vbp + Vhp; 523 | break; 524 | } 525 | 526 | // Sum non-filtered and filtered output. 527 | // Multiply the sum with volume. 528 | return (Vnf + Vf + mixer_DC)*static_cast(vol); 529 | } 530 | 531 | #endif // RESID_INLINING || defined(__FILTER_CC__) 532 | 533 | RESID_NAMESPACE_STOP 534 | 535 | #endif // not __FILTER_H__ 536 | -------------------------------------------------------------------------------- /src/resid-0.16/pot.cc: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #include "pot.h" 21 | 22 | RESID_NAMESPACE_START 23 | 24 | reg8 Potentiometer::readPOT() 25 | { 26 | // NB! Not modeled. 27 | return 0xff; 28 | } 29 | 30 | RESID_NAMESPACE_STOP 31 | -------------------------------------------------------------------------------- /src/resid-0.16/pot.h: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #ifndef __POT_H__ 21 | #define __POT_H__ 22 | 23 | #include "siddefs.h" 24 | 25 | RESID_NAMESPACE_START 26 | 27 | class Potentiometer 28 | { 29 | public: 30 | reg8 readPOT(); 31 | }; 32 | 33 | RESID_NAMESPACE_STOP 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/resid-0.16/sid.h: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #ifndef __SID_H__ 21 | #define __SID_H__ 22 | 23 | #include "siddefs.h" 24 | #include "voice.h" 25 | #include "filter.h" 26 | #include "extfilt.h" 27 | #include "pot.h" 28 | 29 | RESID_NAMESPACE_START 30 | 31 | class SID 32 | { 33 | public: 34 | SID(); 35 | ~SID(); 36 | 37 | void set_chip_model(chip_model model); 38 | void enable_filter(bool enable); 39 | void enable_external_filter(bool enable); 40 | bool set_sampling_parameters(double clock_freq, sampling_method method, 41 | double sample_freq, double pass_freq = -1, 42 | double filter_scale = 0.97); 43 | void adjust_sampling_frequency(double sample_freq); 44 | 45 | void fc_default(const fc_point*& points, int& count); 46 | PointPlotter fc_plotter(); 47 | 48 | void clock(); 49 | void clock(cycle_count delta_t); 50 | int clock(cycle_count& delta_t, short* buf, int n, int interleave = 1); 51 | void reset(); 52 | 53 | // Read/write registers. 54 | reg8 read(reg8 offset); 55 | void write(reg8 offset, reg8 value); 56 | void mute(reg8 channel, bool enable); 57 | 58 | // Read/write state. 59 | class State 60 | { 61 | public: 62 | State(); 63 | 64 | char sid_register[0x20]; 65 | 66 | reg8 bus_value; 67 | cycle_count bus_value_ttl; 68 | 69 | reg24 accumulator[3]; 70 | reg24 shift_register[3]; 71 | reg16 rate_counter[3]; 72 | reg16 rate_counter_period[3]; 73 | reg16 exponential_counter[3]; 74 | reg16 exponential_counter_period[3]; 75 | reg8 envelope_counter[3]; 76 | EnvelopeGenerator::State envelope_state[3]; 77 | bool hold_zero[3]; 78 | }; 79 | 80 | State read_state(); 81 | void write_state(const State& state); 82 | 83 | // 16-bit input (EXT IN). 84 | void input(int sample); 85 | 86 | // 16-bit output (AUDIO OUT). 87 | int output(); 88 | // n-bit output. 89 | int output(int bits); 90 | 91 | protected: 92 | static double I0(double x); 93 | RESID_INLINE int clock_fast(cycle_count& delta_t, short* buf, int n, 94 | int interleave); 95 | RESID_INLINE int clock_interpolate(cycle_count& delta_t, short* buf, int n, 96 | int interleave); 97 | RESID_INLINE int clock_resample_interpolate(cycle_count& delta_t, short* buf, 98 | int n, int interleave); 99 | RESID_INLINE int clock_resample_fast(cycle_count& delta_t, short* buf, 100 | int n, int interleave); 101 | 102 | Voice voice[3]; 103 | Filter filter; 104 | ExternalFilter extfilt; 105 | Potentiometer potx; 106 | Potentiometer poty; 107 | 108 | reg8 bus_value; 109 | cycle_count bus_value_ttl; 110 | 111 | double clock_frequency; 112 | 113 | // External audio input. 114 | int ext_in; 115 | 116 | // Resampling constants. 117 | static const int FIR_N; 118 | static const int FIR_RES_INTERPOLATE; 119 | static const int FIR_RES_FAST; 120 | static const int FIR_SHIFT; 121 | static const int RINGSIZE; 122 | 123 | // Fixpoint constants. 124 | static const int FIXP_SHIFT; 125 | static const int FIXP_MASK; 126 | 127 | // Sampling variables. 128 | sampling_method sampling; 129 | cycle_count cycles_per_sample; 130 | cycle_count sample_offset; 131 | int sample_index; 132 | short sample_prev; 133 | int fir_N; 134 | int fir_RES; 135 | 136 | // Ring buffer with overflow for contiguous storage of RINGSIZE samples. 137 | short* sample; 138 | 139 | // FIR_RES filter tables (FIR_N*FIR_RES). 140 | short* fir; 141 | }; 142 | 143 | RESID_NAMESPACE_STOP 144 | 145 | #endif // not __SID_H__ 146 | -------------------------------------------------------------------------------- /src/resid-0.16/siddefs.h: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 1999 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #ifndef __SIDDEFS_H__ 21 | #define __SIDDEFS_H__ 22 | 23 | // Define bool, true, and false for C++ compilers that lack these keywords. 24 | #define RESID_HAVE_BOOL 1 25 | 26 | // Inlining on/off. 27 | #define RESID_INLINING 1 28 | #define RESID_INLINE inline 29 | 30 | // Support namespace 31 | 32 | #ifdef RESID_NAMESPACE 33 | # define RESID_NAMESPACE_START \ 34 | namespace RESID_NAMESPACE \ 35 | { 36 | # define RESID_NAMESPACE_STOP \ 37 | } 38 | #else 39 | # define RESID_NAMESPACE_START 40 | # define RESID_NAMESPACE_STOP 41 | #endif 42 | 43 | 44 | RESID_NAMESPACE_START 45 | 46 | #if !RESID_HAVE_BOOL 47 | typedef int bool; 48 | const bool true = 1; 49 | const bool false = 0; 50 | #endif 51 | 52 | // We could have used the smallest possible data type for each SID register, 53 | // however this would give a slower engine because of data type conversions. 54 | // An int is assumed to be at least 32 bits (necessary in the types reg24, 55 | // cycle_count, and sound_sample). GNU does not support 16-bit machines 56 | // (GNU Coding Standards: Portability between CPUs), so this should be 57 | // a valid assumption. 58 | 59 | typedef unsigned int reg4; 60 | typedef unsigned int reg8; 61 | typedef unsigned int reg12; 62 | typedef unsigned int reg16; 63 | typedef unsigned int reg24; 64 | 65 | typedef int cycle_count; 66 | typedef int sound_sample; 67 | typedef sound_sample fc_point[2]; 68 | 69 | enum chip_model { MOS6581, MOS8580 }; 70 | 71 | enum sampling_method { SAMPLE_FAST, SAMPLE_INTERPOLATE, 72 | SAMPLE_RESAMPLE_INTERPOLATE, SAMPLE_RESAMPLE_FAST }; 73 | 74 | extern "C" 75 | { 76 | #ifndef __VERSION_CC__ 77 | extern const char* resid_version_string; 78 | #else 79 | const char* resid_version_string = "0.16"; 80 | #endif 81 | } 82 | 83 | RESID_NAMESPACE_STOP 84 | 85 | #endif // not __SIDDEFS_H__ 86 | -------------------------------------------------------------------------------- /src/resid-0.16/siddefs.h.in: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 1999 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #ifndef __SIDDEFS_H__ 21 | #define __SIDDEFS_H__ 22 | 23 | // Define bool, true, and false for C++ compilers that lack these keywords. 24 | #define RESID_HAVE_BOOL @RESID_HAVE_BOOL@ 25 | 26 | // Inlining on/off. 27 | #define RESID_INLINING @RESID_INLINING@ 28 | #define RESID_INLINE @RESID_INLINE@ 29 | 30 | // Support namespace 31 | @RESID_NAMESPACE@ 32 | #ifdef RESID_NAMESPACE 33 | # define RESID_NAMESPACE_START \ 34 | namespace RESID_NAMESPACE \ 35 | { 36 | # define RESID_NAMESPACE_STOP \ 37 | } 38 | #else 39 | # define RESID_NAMESPACE_START 40 | # define RESID_NAMESPACE_STOP 41 | #endif 42 | 43 | 44 | RESID_NAMESPACE_START 45 | 46 | #if !RESID_HAVE_BOOL 47 | typedef int bool; 48 | const bool true = 1; 49 | const bool false = 0; 50 | #endif 51 | 52 | // We could have used the smallest possible data type for each SID register, 53 | // however this would give a slower engine because of data type conversions. 54 | // An int is assumed to be at least 32 bits (necessary in the types reg24, 55 | // cycle_count, and sound_sample). GNU does not support 16-bit machines 56 | // (GNU Coding Standards: Portability between CPUs), so this should be 57 | // a valid assumption. 58 | 59 | typedef unsigned int reg4; 60 | typedef unsigned int reg8; 61 | typedef unsigned int reg12; 62 | typedef unsigned int reg16; 63 | typedef unsigned int reg24; 64 | 65 | typedef int cycle_count; 66 | typedef int sound_sample; 67 | typedef sound_sample fc_point[2]; 68 | 69 | enum chip_model { MOS6581, MOS8580 }; 70 | 71 | enum sampling_method { SAMPLE_FAST, SAMPLE_INTERPOLATE, 72 | SAMPLE_RESAMPLE_INTERPOLATE, SAMPLE_RESAMPLE_FAST }; 73 | 74 | extern "C" 75 | { 76 | #ifndef __VERSION_CC__ 77 | extern const char* resid_version_string; 78 | #else 79 | const char* resid_version_string = "@VERSION@"; 80 | #endif 81 | } 82 | 83 | RESID_NAMESPACE_STOP 84 | 85 | #endif // not __SIDDEFS_H__ 86 | -------------------------------------------------------------------------------- /src/resid-0.16/spline.h: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #ifndef __SPLINE_H__ 21 | #define __SPLINE_H__ 22 | 23 | RESID_NAMESPACE_START 24 | 25 | // Our objective is to construct a smooth interpolating single-valued function 26 | // y = f(x). 27 | // 28 | // Catmull-Rom splines are widely used for interpolation, however these are 29 | // parametric curves [x(t) y(t) ...] and can not be used to directly calculate 30 | // y = f(x). 31 | // For a discussion of Catmull-Rom splines see Catmull, E., and R. Rom, 32 | // "A Class of Local Interpolating Splines", Computer Aided Geometric Design. 33 | // 34 | // Natural cubic splines are single-valued functions, and have been used in 35 | // several applications e.g. to specify gamma curves for image display. 36 | // These splines do not afford local control, and a set of linear equations 37 | // including all interpolation points must be solved before any point on the 38 | // curve can be calculated. The lack of local control makes the splines 39 | // more difficult to handle than e.g. Catmull-Rom splines, and real-time 40 | // interpolation of a stream of data points is not possible. 41 | // For a discussion of natural cubic splines, see e.g. Kreyszig, E., "Advanced 42 | // Engineering Mathematics". 43 | // 44 | // Our approach is to approximate the properties of Catmull-Rom splines for 45 | // piecewice cubic polynomials f(x) = ax^3 + bx^2 + cx + d as follows: 46 | // Each curve segment is specified by four interpolation points, 47 | // p0, p1, p2, p3. 48 | // The curve between p1 and p2 must interpolate both p1 and p2, and in addition 49 | // f'(p1.x) = k1 = (p2.y - p0.y)/(p2.x - p0.x) and 50 | // f'(p2.x) = k2 = (p3.y - p1.y)/(p3.x - p1.x). 51 | // 52 | // The constraints are expressed by the following system of linear equations 53 | // 54 | // [ 1 xi xi^2 xi^3 ] [ d ] [ yi ] 55 | // [ 1 2*xi 3*xi^2 ] * [ c ] = [ ki ] 56 | // [ 1 xj xj^2 xj^3 ] [ b ] [ yj ] 57 | // [ 1 2*xj 3*xj^2 ] [ a ] [ kj ] 58 | // 59 | // Solving using Gaussian elimination and back substitution, setting 60 | // dy = yj - yi, dx = xj - xi, we get 61 | // 62 | // a = ((ki + kj) - 2*dy/dx)/(dx*dx); 63 | // b = ((kj - ki)/dx - 3*(xi + xj)*a)/2; 64 | // c = ki - (3*xi*a + 2*b)*xi; 65 | // d = yi - ((xi*a + b)*xi + c)*xi; 66 | // 67 | // Having calculated the coefficients of the cubic polynomial we have the 68 | // choice of evaluation by brute force 69 | // 70 | // for (x = x1; x <= x2; x += res) { 71 | // y = ((a*x + b)*x + c)*x + d; 72 | // plot(x, y); 73 | // } 74 | // 75 | // or by forward differencing 76 | // 77 | // y = ((a*x1 + b)*x1 + c)*x1 + d; 78 | // dy = (3*a*(x1 + res) + 2*b)*x1*res + ((a*res + b)*res + c)*res; 79 | // d2y = (6*a*(x1 + res) + 2*b)*res*res; 80 | // d3y = 6*a*res*res*res; 81 | // 82 | // for (x = x1; x <= x2; x += res) { 83 | // plot(x, y); 84 | // y += dy; dy += d2y; d2y += d3y; 85 | // } 86 | // 87 | // See Foley, Van Dam, Feiner, Hughes, "Computer Graphics, Principles and 88 | // Practice" for a discussion of forward differencing. 89 | // 90 | // If we have a set of interpolation points p0, ..., pn, we may specify 91 | // curve segments between p0 and p1, and between pn-1 and pn by using the 92 | // following constraints: 93 | // f''(p0.x) = 0 and 94 | // f''(pn.x) = 0. 95 | // 96 | // Substituting the results for a and b in 97 | // 98 | // 2*b + 6*a*xi = 0 99 | // 100 | // we get 101 | // 102 | // ki = (3*dy/dx - kj)/2; 103 | // 104 | // or by substituting the results for a and b in 105 | // 106 | // 2*b + 6*a*xj = 0 107 | // 108 | // we get 109 | // 110 | // kj = (3*dy/dx - ki)/2; 111 | // 112 | // Finally, if we have only two interpolation points, the cubic polynomial 113 | // will degenerate to a straight line if we set 114 | // 115 | // ki = kj = dy/dx; 116 | // 117 | 118 | 119 | #if SPLINE_BRUTE_FORCE 120 | #define interpolate_segment interpolate_brute_force 121 | #else 122 | #define interpolate_segment interpolate_forward_difference 123 | #endif 124 | 125 | 126 | // ---------------------------------------------------------------------------- 127 | // Calculation of coefficients. 128 | // ---------------------------------------------------------------------------- 129 | inline 130 | void cubic_coefficients(double x1, double y1, double x2, double y2, 131 | double k1, double k2, 132 | double& a, double& b, double& c, double& d) 133 | { 134 | double dx = x2 - x1, dy = y2 - y1; 135 | 136 | a = ((k1 + k2) - 2*dy/dx)/(dx*dx); 137 | b = ((k2 - k1)/dx - 3*(x1 + x2)*a)/2; 138 | c = k1 - (3*x1*a + 2*b)*x1; 139 | d = y1 - ((x1*a + b)*x1 + c)*x1; 140 | } 141 | 142 | // ---------------------------------------------------------------------------- 143 | // Evaluation of cubic polynomial by brute force. 144 | // ---------------------------------------------------------------------------- 145 | template 146 | inline 147 | void interpolate_brute_force(double x1, double y1, double x2, double y2, 148 | double k1, double k2, 149 | PointPlotter plot, double res) 150 | { 151 | double a, b, c, d; 152 | cubic_coefficients(x1, y1, x2, y2, k1, k2, a, b, c, d); 153 | 154 | // Calculate each point. 155 | for (double x = x1; x <= x2; x += res) { 156 | double y = ((a*x + b)*x + c)*x + d; 157 | plot(x, y); 158 | } 159 | } 160 | 161 | // ---------------------------------------------------------------------------- 162 | // Evaluation of cubic polynomial by forward differencing. 163 | // ---------------------------------------------------------------------------- 164 | template 165 | inline 166 | void interpolate_forward_difference(double x1, double y1, double x2, double y2, 167 | double k1, double k2, 168 | PointPlotter plot, double res) 169 | { 170 | double a, b, c, d; 171 | cubic_coefficients(x1, y1, x2, y2, k1, k2, a, b, c, d); 172 | 173 | double y = ((a*x1 + b)*x1 + c)*x1 + d; 174 | double dy = (3*a*(x1 + res) + 2*b)*x1*res + ((a*res + b)*res + c)*res; 175 | double d2y = (6*a*(x1 + res) + 2*b)*res*res; 176 | double d3y = 6*a*res*res*res; 177 | 178 | // Calculate each point. 179 | for (double x = x1; x <= x2; x += res) { 180 | plot(x, y); 181 | y += dy; dy += d2y; d2y += d3y; 182 | } 183 | } 184 | 185 | template 186 | inline 187 | double x(PointIter p) 188 | { 189 | return (*p)[0]; 190 | } 191 | 192 | template 193 | inline 194 | double y(PointIter p) 195 | { 196 | return (*p)[1]; 197 | } 198 | 199 | // ---------------------------------------------------------------------------- 200 | // Evaluation of complete interpolating function. 201 | // Note that since each curve segment is controlled by four points, the 202 | // end points will not be interpolated. If extra control points are not 203 | // desirable, the end points can simply be repeated to ensure interpolation. 204 | // Note also that points of non-differentiability and discontinuity can be 205 | // introduced by repeating points. 206 | // ---------------------------------------------------------------------------- 207 | template 208 | inline 209 | void interpolate(PointIter p0, PointIter pn, PointPlotter plot, double res) 210 | { 211 | double k1, k2; 212 | 213 | // Set up points for first curve segment. 214 | PointIter p1 = p0; ++p1; 215 | PointIter p2 = p1; ++p2; 216 | PointIter p3 = p2; ++p3; 217 | 218 | // Draw each curve segment. 219 | for (; p2 != pn; ++p0, ++p1, ++p2, ++p3) { 220 | // p1 and p2 equal; single point. 221 | if (x(p1) == x(p2)) { 222 | continue; 223 | } 224 | // Both end points repeated; straight line. 225 | if (x(p0) == x(p1) && x(p2) == x(p3)) { 226 | k1 = k2 = (y(p2) - y(p1))/(x(p2) - x(p1)); 227 | } 228 | // p0 and p1 equal; use f''(x1) = 0. 229 | else if (x(p0) == x(p1)) { 230 | k2 = (y(p3) - y(p1))/(x(p3) - x(p1)); 231 | k1 = (3*(y(p2) - y(p1))/(x(p2) - x(p1)) - k2)/2; 232 | } 233 | // p2 and p3 equal; use f''(x2) = 0. 234 | else if (x(p2) == x(p3)) { 235 | k1 = (y(p2) - y(p0))/(x(p2) - x(p0)); 236 | k2 = (3*(y(p2) - y(p1))/(x(p2) - x(p1)) - k1)/2; 237 | } 238 | // Normal curve. 239 | else { 240 | k1 = (y(p2) - y(p0))/(x(p2) - x(p0)); 241 | k2 = (y(p3) - y(p1))/(x(p3) - x(p1)); 242 | } 243 | 244 | interpolate_segment(x(p1), y(p1), x(p2), y(p2), k1, k2, plot, res); 245 | } 246 | } 247 | 248 | // ---------------------------------------------------------------------------- 249 | // Class for plotting integers into an array. 250 | // ---------------------------------------------------------------------------- 251 | template 252 | class PointPlotter 253 | { 254 | protected: 255 | F* f; 256 | 257 | public: 258 | PointPlotter(F* arr) : f(arr) 259 | { 260 | } 261 | 262 | void operator ()(double x, double y) 263 | { 264 | // Clamp negative values to zero. 265 | if (y < 0) { 266 | y = 0; 267 | } 268 | 269 | f[F(x)] = F(y); 270 | } 271 | }; 272 | 273 | RESID_NAMESPACE_STOP 274 | 275 | #endif // not __SPLINE_H__ 276 | -------------------------------------------------------------------------------- /src/resid-0.16/version.cc: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #define __VERSION_CC__ 21 | #include "siddefs.h" 22 | -------------------------------------------------------------------------------- /src/resid-0.16/voice.cc: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #define __VOICE_CC__ 21 | #include "voice.h" 22 | 23 | RESID_NAMESPACE_START 24 | 25 | // ---------------------------------------------------------------------------- 26 | // Constructor. 27 | // ---------------------------------------------------------------------------- 28 | Voice::Voice() 29 | : muted(false) 30 | { 31 | set_chip_model(MOS6581); 32 | } 33 | 34 | // ---------------------------------------------------------------------------- 35 | // Set chip model. 36 | // ---------------------------------------------------------------------------- 37 | void Voice::set_chip_model(chip_model model) 38 | { 39 | wave.set_chip_model(model); 40 | 41 | if (model == MOS6581) { 42 | // The waveform D/A converter introduces a DC offset in the signal 43 | // to the envelope multiplying D/A converter. The "zero" level of 44 | // the waveform D/A converter can be found as follows: 45 | // 46 | // Measure the "zero" voltage of voice 3 on the SID audio output 47 | // pin, routing only voice 3 to the mixer ($d417 = $0b, $d418 = 48 | // $0f, all other registers zeroed). 49 | // 50 | // Then set the sustain level for voice 3 to maximum and search for 51 | // the waveform output value yielding the same voltage as found 52 | // above. This is done by trying out different waveform output 53 | // values until the correct value is found, e.g. with the following 54 | // program: 55 | // 56 | // lda #$08 57 | // sta $d412 58 | // lda #$0b 59 | // sta $d417 60 | // lda #$0f 61 | // sta $d418 62 | // lda #$f0 63 | // sta $d414 64 | // lda #$21 65 | // sta $d412 66 | // lda #$01 67 | // sta $d40e 68 | // 69 | // ldx #$00 70 | // lda #$38 ; Tweak this to find the "zero" level 71 | //l cmp $d41b 72 | // bne l 73 | // stx $d40e ; Stop frequency counter - freeze waveform output 74 | // brk 75 | // 76 | // The waveform output range is 0x000 to 0xfff, so the "zero" 77 | // level should ideally have been 0x800. In the measured chip, the 78 | // waveform output "zero" level was found to be 0x380 (i.e. $d41b 79 | // = 0x38) at 5.94V. 80 | 81 | wave_zero = 0x380; 82 | 83 | // The envelope multiplying D/A converter introduces another DC 84 | // offset. This is isolated by the following measurements: 85 | // 86 | // * The "zero" output level of the mixer at full volume is 5.44V. 87 | // * Routing one voice to the mixer at full volume yields 88 | // 6.75V at maximum voice output (wave = 0xfff, sustain = 0xf) 89 | // 5.94V at "zero" voice output (wave = any, sustain = 0x0) 90 | // 5.70V at minimum voice output (wave = 0x000, sustain = 0xf) 91 | // * The DC offset of one voice is (5.94V - 5.44V) = 0.50V 92 | // * The dynamic range of one voice is |6.75V - 5.70V| = 1.05V 93 | // * The DC offset is thus 0.50V/1.05V ~ 1/2 of the dynamic range. 94 | // 95 | // Note that by removing the DC offset, we get the following ranges for 96 | // one voice: 97 | // y > 0: (6.75V - 5.44V) - 0.50V = 0.81V 98 | // y < 0: (5.70V - 5.44V) - 0.50V = -0.24V 99 | // The scaling of the voice amplitude is not symmetric about y = 0; 100 | // this follows from the DC level in the waveform output. 101 | 102 | voice_DC = 0x800*0xff; 103 | } 104 | else { 105 | // No DC offsets in the MOS8580. 106 | wave_zero = 0x800; 107 | voice_DC = 0; 108 | } 109 | } 110 | 111 | // ---------------------------------------------------------------------------- 112 | // Set sync source. 113 | // ---------------------------------------------------------------------------- 114 | void Voice::set_sync_source(Voice* source) 115 | { 116 | wave.set_sync_source(&source->wave); 117 | } 118 | 119 | // ---------------------------------------------------------------------------- 120 | // Register functions. 121 | // ---------------------------------------------------------------------------- 122 | void Voice::writeCONTROL_REG(reg8 control) 123 | { 124 | wave.writeCONTROL_REG(control); 125 | envelope.writeCONTROL_REG(control); 126 | } 127 | 128 | // ---------------------------------------------------------------------------- 129 | // SID reset. 130 | // ---------------------------------------------------------------------------- 131 | void Voice::reset() 132 | { 133 | wave.reset(); 134 | envelope.reset(); 135 | } 136 | 137 | 138 | // ---------------------------------------------------------------------------- 139 | // Voice mute. 140 | // ---------------------------------------------------------------------------- 141 | void Voice::mute(bool enable) 142 | { 143 | // enable = true (means voice is muted) 144 | muted = enable; 145 | } 146 | 147 | RESID_NAMESPACE_STOP 148 | -------------------------------------------------------------------------------- /src/resid-0.16/voice.h: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #ifndef __VOICE_H__ 21 | #define __VOICE_H__ 22 | 23 | #include "siddefs.h" 24 | #include "wave.h" 25 | #include "envelope.h" 26 | 27 | RESID_NAMESPACE_START 28 | 29 | class Voice 30 | { 31 | public: 32 | Voice(); 33 | 34 | void set_chip_model(chip_model model); 35 | void set_sync_source(Voice*); 36 | void reset(); 37 | void mute(bool enable); 38 | 39 | void writeCONTROL_REG(reg8); 40 | 41 | // Amplitude modulated waveform output. 42 | // Range [-2048*255, 2047*255]. 43 | RESID_INLINE sound_sample output(); 44 | 45 | protected: 46 | WaveformGenerator wave; 47 | EnvelopeGenerator envelope; 48 | bool muted; 49 | 50 | // Waveform D/A zero level. 51 | sound_sample wave_zero; 52 | 53 | // Multiplying D/A DC offset. 54 | sound_sample voice_DC; 55 | 56 | friend class SID; 57 | }; 58 | 59 | 60 | // ---------------------------------------------------------------------------- 61 | // Inline functions. 62 | // The following function is defined inline because it is called every 63 | // time a sample is calculated. 64 | // ---------------------------------------------------------------------------- 65 | 66 | #if RESID_INLINING || defined(__VOICE_CC__) 67 | 68 | // ---------------------------------------------------------------------------- 69 | // Amplitude modulated waveform output. 70 | // Ideal range [-2048*255, 2047*255]. 71 | // ---------------------------------------------------------------------------- 72 | RESID_INLINE 73 | sound_sample Voice::output() 74 | { 75 | if (!muted) 76 | { // Multiply oscillator output with envelope output. 77 | return (wave.output() - wave_zero)*envelope.output() + voice_DC; 78 | } else { 79 | return 0; 80 | } 81 | } 82 | 83 | #endif // RESID_INLINING || defined(__VOICE_CC__) 84 | 85 | RESID_NAMESPACE_STOP 86 | 87 | #endif // not __VOICE_H__ 88 | -------------------------------------------------------------------------------- /src/resid-0.16/wave.cc: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #define __WAVE_CC__ 21 | #include "wave.h" 22 | 23 | RESID_NAMESPACE_START 24 | 25 | // ---------------------------------------------------------------------------- 26 | // Constructor. 27 | // ---------------------------------------------------------------------------- 28 | WaveformGenerator::WaveformGenerator() 29 | { 30 | sync_source = this; 31 | 32 | set_chip_model(MOS6581); 33 | 34 | reset(); 35 | } 36 | 37 | 38 | // ---------------------------------------------------------------------------- 39 | // Set sync source. 40 | // ---------------------------------------------------------------------------- 41 | void WaveformGenerator::set_sync_source(WaveformGenerator* source) 42 | { 43 | sync_source = source; 44 | source->sync_dest = this; 45 | } 46 | 47 | 48 | // ---------------------------------------------------------------------------- 49 | // Set chip model. 50 | // ---------------------------------------------------------------------------- 51 | void WaveformGenerator::set_chip_model(chip_model model) 52 | { 53 | if (model == MOS6581) { 54 | wave__ST = wave6581__ST; 55 | wave_P_T = wave6581_P_T; 56 | wave_PS_ = wave6581_PS_; 57 | wave_PST = wave6581_PST; 58 | } 59 | else { 60 | wave__ST = wave8580__ST; 61 | wave_P_T = wave8580_P_T; 62 | wave_PS_ = wave8580_PS_; 63 | wave_PST = wave8580_PST; 64 | } 65 | } 66 | 67 | 68 | // ---------------------------------------------------------------------------- 69 | // Register functions. 70 | // ---------------------------------------------------------------------------- 71 | void WaveformGenerator::writeFREQ_LO(reg8 freq_lo) 72 | { 73 | freq = freq & 0xff00 | freq_lo & 0x00ff; 74 | } 75 | 76 | void WaveformGenerator::writeFREQ_HI(reg8 freq_hi) 77 | { 78 | freq = (freq_hi << 8) & 0xff00 | freq & 0x00ff; 79 | } 80 | 81 | void WaveformGenerator::writePW_LO(reg8 pw_lo) 82 | { 83 | pw = pw & 0xf00 | pw_lo & 0x0ff; 84 | } 85 | 86 | void WaveformGenerator::writePW_HI(reg8 pw_hi) 87 | { 88 | pw = (pw_hi << 8) & 0xf00 | pw & 0x0ff; 89 | } 90 | 91 | void WaveformGenerator::writeCONTROL_REG(reg8 control) 92 | { 93 | waveform = (control >> 4) & 0x0f; 94 | ring_mod = control & 0x04; 95 | sync = control & 0x02; 96 | 97 | reg8 test_next = control & 0x08; 98 | 99 | // Test bit set. 100 | // The accumulator and the shift register are both cleared. 101 | // NB! The shift register is not really cleared immediately. It seems like 102 | // the individual bits in the shift register start to fade down towards 103 | // zero when test is set. All bits reach zero within approximately 104 | // $2000 - $4000 cycles. 105 | // This is not modeled. There should fortunately be little audible output 106 | // from this peculiar behavior. 107 | if (test_next) { 108 | accumulator = 0; 109 | shift_register = 0; 110 | } 111 | // Test bit cleared. 112 | // The accumulator starts counting, and the shift register is reset to 113 | // the value 0x7ffff8. 114 | // NB! The shift register will not actually be set to this exact value if the 115 | // shift register bits have not had time to fade to zero. 116 | // This is not modeled. 117 | else if (test) { 118 | shift_register = 0x7ffff8; 119 | } 120 | 121 | test = test_next; 122 | 123 | // The gate bit is handled by the EnvelopeGenerator. 124 | } 125 | 126 | reg8 WaveformGenerator::readOSC() 127 | { 128 | return output() >> 4; 129 | } 130 | 131 | // ---------------------------------------------------------------------------- 132 | // SID reset. 133 | // ---------------------------------------------------------------------------- 134 | void WaveformGenerator::reset() 135 | { 136 | accumulator = 0; 137 | shift_register = 0x7ffff8; 138 | freq = 0; 139 | pw = 0; 140 | 141 | test = 0; 142 | ring_mod = 0; 143 | sync = 0; 144 | 145 | msb_rising = false; 146 | } 147 | 148 | RESID_NAMESPACE_STOP 149 | -------------------------------------------------------------------------------- /src/resid-0.16/wave.h: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // This file is part of reSID, a MOS6581 SID emulator engine. 3 | // Copyright (C) 2004 Dag Lem 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program; if not, write to the Free Software 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | // --------------------------------------------------------------------------- 19 | 20 | #ifndef __WAVE_H__ 21 | #define __WAVE_H__ 22 | 23 | #include "siddefs.h" 24 | 25 | RESID_NAMESPACE_START 26 | 27 | // ---------------------------------------------------------------------------- 28 | // A 24 bit accumulator is the basis for waveform generation. FREQ is added to 29 | // the lower 16 bits of the accumulator each cycle. 30 | // The accumulator is set to zero when TEST is set, and starts counting 31 | // when TEST is cleared. 32 | // The noise waveform is taken from intermediate bits of a 23 bit shift 33 | // register. This register is clocked by bit 19 of the accumulator. 34 | // ---------------------------------------------------------------------------- 35 | class WaveformGenerator 36 | { 37 | public: 38 | WaveformGenerator(); 39 | 40 | void set_sync_source(WaveformGenerator*); 41 | void set_chip_model(chip_model model); 42 | 43 | RESID_INLINE void clock(); 44 | RESID_INLINE void clock(cycle_count delta_t); 45 | RESID_INLINE void synchronize(); 46 | void reset(); 47 | 48 | void writeFREQ_LO(reg8); 49 | void writeFREQ_HI(reg8); 50 | void writePW_LO(reg8); 51 | void writePW_HI(reg8); 52 | void writeCONTROL_REG(reg8); 53 | reg8 readOSC(); 54 | 55 | // 12-bit waveform output. 56 | RESID_INLINE reg12 output(); 57 | 58 | protected: 59 | const WaveformGenerator* sync_source; 60 | WaveformGenerator* sync_dest; 61 | 62 | // Tell whether the accumulator MSB was set high on this cycle. 63 | bool msb_rising; 64 | 65 | reg24 accumulator; 66 | reg24 shift_register; 67 | 68 | // Fout = (Fn*Fclk/16777216)Hz 69 | reg16 freq; 70 | // PWout = (PWn/40.95)% 71 | reg12 pw; 72 | 73 | // The control register right-shifted 4 bits; used for output function 74 | // table lookup. 75 | reg8 waveform; 76 | 77 | // The remaining control register bits. 78 | reg8 test; 79 | reg8 ring_mod; 80 | reg8 sync; 81 | // The gate bit is handled by the EnvelopeGenerator. 82 | 83 | // 16 possible combinations of waveforms. 84 | RESID_INLINE reg12 output____(); 85 | RESID_INLINE reg12 output___T(); 86 | RESID_INLINE reg12 output__S_(); 87 | RESID_INLINE reg12 output__ST(); 88 | RESID_INLINE reg12 output_P__(); 89 | RESID_INLINE reg12 output_P_T(); 90 | RESID_INLINE reg12 output_PS_(); 91 | RESID_INLINE reg12 output_PST(); 92 | RESID_INLINE reg12 outputN___(); 93 | RESID_INLINE reg12 outputN__T(); 94 | RESID_INLINE reg12 outputN_S_(); 95 | RESID_INLINE reg12 outputN_ST(); 96 | RESID_INLINE reg12 outputNP__(); 97 | RESID_INLINE reg12 outputNP_T(); 98 | RESID_INLINE reg12 outputNPS_(); 99 | RESID_INLINE reg12 outputNPST(); 100 | 101 | // Sample data for combinations of waveforms. 102 | static reg8 wave6581__ST[]; 103 | static reg8 wave6581_P_T[]; 104 | static reg8 wave6581_PS_[]; 105 | static reg8 wave6581_PST[]; 106 | 107 | static reg8 wave8580__ST[]; 108 | static reg8 wave8580_P_T[]; 109 | static reg8 wave8580_PS_[]; 110 | static reg8 wave8580_PST[]; 111 | 112 | reg8* wave__ST; 113 | reg8* wave_P_T; 114 | reg8* wave_PS_; 115 | reg8* wave_PST; 116 | 117 | friend class Voice; 118 | friend class SID; 119 | }; 120 | 121 | 122 | // ---------------------------------------------------------------------------- 123 | // Inline functions. 124 | // The following functions are defined inline because they are called every 125 | // time a sample is calculated. 126 | // ---------------------------------------------------------------------------- 127 | 128 | #if RESID_INLINING || defined(__WAVE_CC__) 129 | 130 | // ---------------------------------------------------------------------------- 131 | // SID clocking - 1 cycle. 132 | // ---------------------------------------------------------------------------- 133 | RESID_INLINE 134 | void WaveformGenerator::clock() 135 | { 136 | // No operation if test bit is set. 137 | if (test) { 138 | return; 139 | } 140 | 141 | reg24 accumulator_prev = accumulator; 142 | 143 | // Calculate new accumulator value; 144 | accumulator += freq; 145 | accumulator &= 0xffffff; 146 | 147 | // Check whether the MSB is set high. This is used for synchronization. 148 | msb_rising = !(accumulator_prev & 0x800000) && (accumulator & 0x800000); 149 | 150 | // Shift noise register once for each time accumulator bit 19 is set high. 151 | if (!(accumulator_prev & 0x080000) && (accumulator & 0x080000)) { 152 | reg24 bit0 = ((shift_register >> 22) ^ (shift_register >> 17)) & 0x1; 153 | shift_register <<= 1; 154 | shift_register &= 0x7fffff; 155 | shift_register |= bit0; 156 | } 157 | } 158 | 159 | // ---------------------------------------------------------------------------- 160 | // SID clocking - delta_t cycles. 161 | // ---------------------------------------------------------------------------- 162 | RESID_INLINE 163 | void WaveformGenerator::clock(cycle_count delta_t) 164 | { 165 | // No operation if test bit is set. 166 | if (test) { 167 | return; 168 | } 169 | 170 | reg24 accumulator_prev = accumulator; 171 | 172 | // Calculate new accumulator value; 173 | reg24 delta_accumulator = delta_t*freq; 174 | accumulator += delta_accumulator; 175 | accumulator &= 0xffffff; 176 | 177 | // Check whether the MSB is set high. This is used for synchronization. 178 | msb_rising = !(accumulator_prev & 0x800000) && (accumulator & 0x800000); 179 | 180 | // Shift noise register once for each time accumulator bit 19 is set high. 181 | // Bit 19 is set high each time 2^20 (0x100000) is added to the accumulator. 182 | reg24 shift_period = 0x100000; 183 | 184 | while (delta_accumulator) { 185 | if (delta_accumulator < shift_period) { 186 | shift_period = delta_accumulator; 187 | // Determine whether bit 19 is set on the last period. 188 | // NB! Requires two's complement integer. 189 | if (shift_period <= 0x080000) { 190 | // Check for flip from 0 to 1. 191 | if (((accumulator - shift_period) & 0x080000) || !(accumulator & 0x080000)) 192 | { 193 | break; 194 | } 195 | } 196 | else { 197 | // Check for flip from 0 (to 1 or via 1 to 0) or from 1 via 0 to 1. 198 | if (((accumulator - shift_period) & 0x080000) && !(accumulator & 0x080000)) 199 | { 200 | break; 201 | } 202 | } 203 | } 204 | 205 | // Shift the noise/random register. 206 | // NB! The shift is actually delayed 2 cycles, this is not modeled. 207 | reg24 bit0 = ((shift_register >> 22) ^ (shift_register >> 17)) & 0x1; 208 | shift_register <<= 1; 209 | shift_register &= 0x7fffff; 210 | shift_register |= bit0; 211 | 212 | delta_accumulator -= shift_period; 213 | } 214 | } 215 | 216 | 217 | // ---------------------------------------------------------------------------- 218 | // Synchronize oscillators. 219 | // This must be done after all the oscillators have been clock()'ed since the 220 | // oscillators operate in parallel. 221 | // Note that the oscillators must be clocked exactly on the cycle when the 222 | // MSB is set high for hard sync to operate correctly. See SID::clock(). 223 | // ---------------------------------------------------------------------------- 224 | RESID_INLINE 225 | void WaveformGenerator::synchronize() 226 | { 227 | // A special case occurs when a sync source is synced itself on the same 228 | // cycle as when its MSB is set high. In this case the destination will 229 | // not be synced. This has been verified by sampling OSC3. 230 | if (msb_rising && sync_dest->sync && !(sync && sync_source->msb_rising)) { 231 | sync_dest->accumulator = 0; 232 | } 233 | } 234 | 235 | 236 | // ---------------------------------------------------------------------------- 237 | // Output functions. 238 | // NB! The output from SID 8580 is delayed one cycle compared to SID 6581, 239 | // this is not modeled. 240 | // ---------------------------------------------------------------------------- 241 | 242 | // No waveform: 243 | // Zero output. 244 | // 245 | RESID_INLINE 246 | reg12 WaveformGenerator::output____() 247 | { 248 | return 0x000; 249 | } 250 | 251 | // Triangle: 252 | // The upper 12 bits of the accumulator are used. 253 | // The MSB is used to create the falling edge of the triangle by inverting 254 | // the lower 11 bits. The MSB is thrown away and the lower 11 bits are 255 | // left-shifted (half the resolution, full amplitude). 256 | // Ring modulation substitutes the MSB with MSB EOR sync_source MSB. 257 | // 258 | RESID_INLINE 259 | reg12 WaveformGenerator::output___T() 260 | { 261 | reg24 msb = (ring_mod ? accumulator ^ sync_source->accumulator : accumulator) 262 | & 0x800000; 263 | return ((msb ? ~accumulator : accumulator) >> 11) & 0xfff; 264 | } 265 | 266 | // Sawtooth: 267 | // The output is identical to the upper 12 bits of the accumulator. 268 | // 269 | RESID_INLINE 270 | reg12 WaveformGenerator::output__S_() 271 | { 272 | return accumulator >> 12; 273 | } 274 | 275 | // Pulse: 276 | // The upper 12 bits of the accumulator are used. 277 | // These bits are compared to the pulse width register by a 12 bit digital 278 | // comparator; output is either all one or all zero bits. 279 | // NB! The output is actually delayed one cycle after the compare. 280 | // This is not modeled. 281 | // 282 | // The test bit, when set to one, holds the pulse waveform output at 0xfff 283 | // regardless of the pulse width setting. 284 | // 285 | RESID_INLINE 286 | reg12 WaveformGenerator::output_P__() 287 | { 288 | return (test || (accumulator >> 12) >= pw) ? 0xfff : 0x000; 289 | } 290 | 291 | // Noise: 292 | // The noise output is taken from intermediate bits of a 23-bit shift register 293 | // which is clocked by bit 19 of the accumulator. 294 | // NB! The output is actually delayed 2 cycles after bit 19 is set high. 295 | // This is not modeled. 296 | // 297 | // Operation: Calculate EOR result, shift register, set bit 0 = result. 298 | // 299 | // ----------------------->--------------------- 300 | // | | 301 | // ----EOR---- | 302 | // | | | 303 | // 2 2 2 1 1 1 1 1 1 1 1 1 1 | 304 | // Register bits: 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 <--- 305 | // | | | | | | | | 306 | // OSC3 bits : 7 6 5 4 3 2 1 0 307 | // 308 | // Since waveform output is 12 bits the output is left-shifted 4 times. 309 | // 310 | RESID_INLINE 311 | reg12 WaveformGenerator::outputN___() 312 | { 313 | return 314 | ((shift_register & 0x400000) >> 11) | 315 | ((shift_register & 0x100000) >> 10) | 316 | ((shift_register & 0x010000) >> 7) | 317 | ((shift_register & 0x002000) >> 5) | 318 | ((shift_register & 0x000800) >> 4) | 319 | ((shift_register & 0x000080) >> 1) | 320 | ((shift_register & 0x000010) << 1) | 321 | ((shift_register & 0x000004) << 2); 322 | } 323 | 324 | // Combined waveforms: 325 | // By combining waveforms, the bits of each waveform are effectively short 326 | // circuited. A zero bit in one waveform will result in a zero output bit 327 | // (thus the infamous claim that the waveforms are AND'ed). 328 | // However, a zero bit in one waveform will also affect the neighboring bits 329 | // in the output. The reason for this has not been determined. 330 | // 331 | // Example: 332 | // 333 | // 1 1 334 | // Bit # 1 0 9 8 7 6 5 4 3 2 1 0 335 | // ----------------------- 336 | // Sawtooth 0 0 0 1 1 1 1 1 1 0 0 0 337 | // 338 | // Triangle 0 0 1 1 1 1 1 1 0 0 0 0 339 | // 340 | // AND 0 0 0 1 1 1 1 1 0 0 0 0 341 | // 342 | // Output 0 0 0 0 1 1 1 0 0 0 0 0 343 | // 344 | // 345 | // This behavior would be quite difficult to model exactly, since the SID 346 | // in this case does not act as a digital state machine. Tests show that minor 347 | // (1 bit) differences can actually occur in the output from otherwise 348 | // identical samples from OSC3 when waveforms are combined. To further 349 | // complicate the situation the output changes slightly with time (more 350 | // neighboring bits are successively set) when the 12-bit waveform 351 | // registers are kept unchanged. 352 | // 353 | // It is probably possible to come up with a valid model for the 354 | // behavior, however this would be far too slow for practical use since it 355 | // would have to be based on the mutual influence of individual bits. 356 | // 357 | // The output is instead approximated by using the upper bits of the 358 | // accumulator as an index to look up the combined output in a table 359 | // containing actual combined waveform samples from OSC3. 360 | // These samples are 8 bit, so 4 bits of waveform resolution is lost. 361 | // All OSC3 samples are taken with FREQ=0x1000, adding a 1 to the upper 12 362 | // bits of the accumulator each cycle for a sample period of 4096 cycles. 363 | // 364 | // Sawtooth+Triangle: 365 | // The sawtooth output is used to look up an OSC3 sample. 366 | // 367 | // Pulse+Triangle: 368 | // The triangle output is right-shifted and used to look up an OSC3 sample. 369 | // The sample is output if the pulse output is on. 370 | // The reason for using the triangle output as the index is to handle ring 371 | // modulation. Only the first half of the sample is used, which should be OK 372 | // since the triangle waveform has half the resolution of the accumulator. 373 | // 374 | // Pulse+Sawtooth: 375 | // The sawtooth output is used to look up an OSC3 sample. 376 | // The sample is output if the pulse output is on. 377 | // 378 | // Pulse+Sawtooth+Triangle: 379 | // The sawtooth output is used to look up an OSC3 sample. 380 | // The sample is output if the pulse output is on. 381 | // 382 | RESID_INLINE 383 | reg12 WaveformGenerator::output__ST() 384 | { 385 | return wave__ST[output__S_()] << 4; 386 | } 387 | 388 | RESID_INLINE 389 | reg12 WaveformGenerator::output_P_T() 390 | { 391 | return (wave_P_T[output___T() >> 1] << 4) & output_P__(); 392 | } 393 | 394 | RESID_INLINE 395 | reg12 WaveformGenerator::output_PS_() 396 | { 397 | return (wave_PS_[output__S_()] << 4) & output_P__(); 398 | } 399 | 400 | RESID_INLINE 401 | reg12 WaveformGenerator::output_PST() 402 | { 403 | return (wave_PST[output__S_()] << 4) & output_P__(); 404 | } 405 | 406 | // Combined waveforms including noise: 407 | // All waveform combinations including noise output zero after a few cycles. 408 | // NB! The effects of such combinations are not fully explored. It is claimed 409 | // that the shift register may be filled with zeroes and locked up, which 410 | // seems to be true. 411 | // We have not attempted to model this behavior, suffice to say that 412 | // there is very little audible output from waveform combinations including 413 | // noise. We hope that nobody is actually using it. 414 | // 415 | RESID_INLINE 416 | reg12 WaveformGenerator::outputN__T() 417 | { 418 | return 0; 419 | } 420 | 421 | RESID_INLINE 422 | reg12 WaveformGenerator::outputN_S_() 423 | { 424 | return 0; 425 | } 426 | 427 | RESID_INLINE 428 | reg12 WaveformGenerator::outputN_ST() 429 | { 430 | return 0; 431 | } 432 | 433 | RESID_INLINE 434 | reg12 WaveformGenerator::outputNP__() 435 | { 436 | return 0; 437 | } 438 | 439 | RESID_INLINE 440 | reg12 WaveformGenerator::outputNP_T() 441 | { 442 | return 0; 443 | } 444 | 445 | RESID_INLINE 446 | reg12 WaveformGenerator::outputNPS_() 447 | { 448 | return 0; 449 | } 450 | 451 | RESID_INLINE 452 | reg12 WaveformGenerator::outputNPST() 453 | { 454 | return 0; 455 | } 456 | 457 | // ---------------------------------------------------------------------------- 458 | // Select one of 16 possible combinations of waveforms. 459 | // ---------------------------------------------------------------------------- 460 | RESID_INLINE 461 | reg12 WaveformGenerator::output() 462 | { 463 | // It may seem cleaner to use an array of member functions to return 464 | // waveform output; however a switch with inline functions is faster. 465 | 466 | switch (waveform) { 467 | default: 468 | case 0x0: 469 | return output____(); 470 | case 0x1: 471 | return output___T(); 472 | case 0x2: 473 | return output__S_(); 474 | case 0x3: 475 | return output__ST(); 476 | case 0x4: 477 | return output_P__(); 478 | case 0x5: 479 | return output_P_T(); 480 | case 0x6: 481 | return output_PS_(); 482 | case 0x7: 483 | return output_PST(); 484 | case 0x8: 485 | return outputN___(); 486 | case 0x9: 487 | return outputN__T(); 488 | case 0xa: 489 | return outputN_S_(); 490 | case 0xb: 491 | return outputN_ST(); 492 | case 0xc: 493 | return outputNP__(); 494 | case 0xd: 495 | return outputNP_T(); 496 | case 0xe: 497 | return outputNPS_(); 498 | case 0xf: 499 | return outputNPST(); 500 | } 501 | } 502 | 503 | #endif // RESID_INLINING || defined(__WAVE_CC__) 504 | 505 | RESID_NAMESPACE_STOP 506 | 507 | #endif // not __WAVE_H__ 508 | -------------------------------------------------------------------------------- /src/sidengine.cpp: -------------------------------------------------------------------------------- 1 | #include "sidengine.hpp" 2 | #include "resid-0.16/sid.h" 3 | #include 4 | 5 | namespace { 6 | 7 | 8 | enum { MIXRATE = 44100 }; 9 | 10 | class ReSidEngine : public SidEngine { 11 | public: 12 | ReSidEngine() { 13 | m_sid.reset(); 14 | m_sid.set_sampling_parameters(985248, SAMPLE_RESAMPLE_INTERPOLATE, MIXRATE); 15 | } 16 | char const* name() const override { return "reSID"; } 17 | void enable_filter(bool e) override { 18 | m_sid.enable_filter(e); 19 | } 20 | void set_chip_model(ChipModel cm) override { 21 | m_sid.set_chip_model((chip_model)cm); 22 | } 23 | void update_registers(uint8_t const* regs) override { 24 | for (int i = 0; i < REGISTER_COUNT; ++i) m_sid.write(i, regs[i]); 25 | } 26 | void mix(int16_t* buffer, int length) override { 27 | int c = 999999999; 28 | m_sid.clock(c, buffer, length); 29 | } 30 | private: 31 | SID m_sid; 32 | }; 33 | 34 | 35 | class TinySidEngine : public SidEngine { 36 | public: 37 | char const* name() const override { return "TinySID"; } 38 | void enable_filter(bool e) override {} 39 | void set_chip_model(ChipModel cm) override {} 40 | void update_registers(uint8_t const* regs) override; 41 | void mix(int16_t* buffer, int length) override; 42 | 43 | private: 44 | 45 | enum { 46 | CHANNEL_COUNT = 3 47 | }; 48 | enum State { RELEASE, ATTACK, DECAY, SUSTAIN }; 49 | enum { 50 | F_GATE = 0x01, 51 | F_SYNC = 0x02, 52 | F_RING = 0x04, 53 | F_TRI = 0x10, 54 | F_SAW = 0x20, 55 | F_PULSE = 0x40, 56 | F_NOISE = 0x80, 57 | }; 58 | enum { 59 | T_LOW = 1, 60 | T_BAND = 2, 61 | T_HIGH = 4, 62 | }; 63 | 64 | struct Channel { 65 | State state; 66 | int adsr[4]; 67 | int flags; 68 | uint32_t next_pulsewidth; 69 | uint32_t pulsewidth; 70 | uint32_t freq; 71 | 72 | 73 | // internal things 74 | int level; 75 | uint32_t phase; 76 | uint32_t noise_phase; 77 | uint32_t shift = 0x7ffff8; 78 | int noise; 79 | bool filter; 80 | }; 81 | 82 | struct { 83 | uint8_t type; 84 | float resonance; 85 | float freq; 86 | 87 | float high; 88 | float band; 89 | float low; 90 | 91 | } m_filter; 92 | 93 | std::array m_channels; 94 | }; 95 | 96 | void TinySidEngine::update_registers(uint8_t const* regs) { 97 | for (int i = 0; i < CHANNEL_COUNT; ++i) { 98 | Channel& chan = m_channels[i]; 99 | uint8_t const* r = regs + (i * 7); 100 | 101 | static const std::array ATTACK_SPEEDS = { 102 | 168867, 47495, 24124, 15998, 10200, 6908, 5692, 4855, 103 | 3877, 1555, 777, 486, 389, 129, 77, 48, 104 | }; 105 | 106 | static const std::array RELEASE_SPEEDS = { 107 | 42660, 15468, 7857, 5210, 3322, 2250, 1853, 1581, 108 | 1262, 506, 253, 158, 126, 42, 25, 15, 109 | }; 110 | 111 | chan.freq = (r[0] | (r[1] << 8)) * (15872000 / MIXRATE); 112 | chan.next_pulsewidth = (r[2] | ((r[3] & 15) << 8)) << 16; 113 | chan.flags = r[4]; 114 | chan.adsr[0] = ATTACK_SPEEDS[r[5] >> 4]; 115 | chan.adsr[1] = RELEASE_SPEEDS[r[5] & 15]; 116 | chan.adsr[2] = (r[6] >> 4) * 0x111111; 117 | chan.adsr[3] = RELEASE_SPEEDS[r[6] & 15]; 118 | chan.filter = (regs[23] >> i) & 1; 119 | } 120 | 121 | // filter 122 | int ff = (regs[21] & 7) | (regs[22] << 3); 123 | m_filter.freq = ff * (21.5332031 / MIXRATE); 124 | m_filter.resonance = 1.2 - 0.04 * (regs[23] >> 4); 125 | m_filter.type = (regs[24] >> 4) & 7; 126 | } 127 | 128 | void TinySidEngine::mix(int16_t* buffer, int length) { 129 | for (int i = 0; i < length; ++i) { 130 | 131 | int out[2] = {}; 132 | 133 | for (int c = 0; c < CHANNEL_COUNT; ++c) { 134 | Channel& chan = m_channels[c]; 135 | Channel& prev_chan = m_channels[c == 0 ? CHANNEL_COUNT - 1 : c - 1]; 136 | 137 | // osc 138 | chan.phase += chan.freq; 139 | chan.phase &= 0xfffffff; 140 | 141 | // sync 142 | if (prev_chan.phase < prev_chan.freq) { 143 | if (chan.flags & F_SYNC) { 144 | chan.phase = prev_chan.phase * chan.freq / prev_chan.freq; 145 | } 146 | } 147 | 148 | // envelope 149 | bool gate = chan.flags & F_GATE; 150 | if (gate && chan.state == RELEASE) chan.state = ATTACK; 151 | if (!gate) chan.state = RELEASE; 152 | 153 | switch (chan.state) { 154 | case ATTACK: 155 | chan.level += chan.adsr[0]; 156 | if (chan.level >= 0xffffff) { 157 | chan.level = 0xffffff; 158 | chan.state = DECAY; 159 | } 160 | break; 161 | case DECAY: 162 | chan.level -= chan.adsr[1]; 163 | if (chan.level <= chan.adsr[2]) { 164 | chan.level = chan.adsr[2]; 165 | chan.state = SUSTAIN; 166 | } 167 | break; 168 | case SUSTAIN: 169 | if (chan.level != chan.adsr[2]) chan.state = ATTACK; 170 | break; 171 | case RELEASE: 172 | chan.level -= chan.adsr[3]; 173 | if (chan.level < 0) chan.level = 0; 174 | break; 175 | } 176 | 177 | 178 | // smooth pulsewith change 179 | if (chan.phase < chan.freq) { 180 | chan.pulsewidth = chan.next_pulsewidth; 181 | } 182 | 183 | // waveforms 184 | uint8_t tri = ((chan.phase < 0x8000000 ? chan.phase : ~chan.phase) >> 19) & 0xff; 185 | uint8_t saw = (chan.phase >> 20) & 0xff; 186 | uint8_t pulse = ((chan.phase > chan.pulsewidth) - 1) & 0xff; 187 | if (chan.noise_phase != chan.phase >> 23) { 188 | chan.noise_phase = chan.phase >> 23; 189 | uint32_t s = chan.shift; 190 | chan.shift = s = (s << 1) | (((s >> 22) ^ (s >> 17)) & 1); 191 | chan.noise = ((s & 0x400000) >> 11) | 192 | ((s & 0x100000) >> 10) | 193 | ((s & 0x010000) >> 7) | 194 | ((s & 0x002000) >> 5) | 195 | ((s & 0x000800) >> 4) | 196 | ((s & 0x000080) >> 1) | 197 | ((s & 0x000010) << 1) | 198 | ((s & 0x000004) << 2); 199 | } 200 | uint8_t noise = chan.noise; 201 | 202 | // ringmod 203 | if (chan.flags & F_RING && prev_chan.phase < 0x8000000) { 204 | tri = ~tri; 205 | } 206 | 207 | int v = 0xff; 208 | if (chan.flags & F_TRI) v &= tri; 209 | if (chan.flags & F_SAW) v &= saw; 210 | if (chan.flags & F_PULSE) v &= pulse; 211 | if (chan.flags & F_NOISE) v &= noise; 212 | 213 | v = ((v - 0x80) * chan.level) >> 18; 214 | 215 | out[chan.filter] += v; 216 | } 217 | 218 | 219 | // filter 220 | m_filter.high = out[1] - m_filter.band * m_filter.resonance - m_filter.low; 221 | m_filter.band += m_filter.freq * m_filter.high; 222 | m_filter.low += m_filter.freq * m_filter.band; 223 | int f = 0; 224 | if (m_filter.type & T_LOW) f += m_filter.low; 225 | if (m_filter.type & T_BAND) f += m_filter.band; 226 | if (m_filter.type & T_HIGH) f += m_filter.high; 227 | 228 | int sample = out[0] + f; 229 | buffer[i] = std::max(-32768, std::min(sample, 32767)); 230 | } 231 | } 232 | 233 | 234 | } // namespace 235 | 236 | 237 | SidEngine* SidEngine::create_resid() { return new ReSidEngine(); } 238 | SidEngine* SidEngine::create_tinysid() { return new TinySidEngine(); } 239 | -------------------------------------------------------------------------------- /src/sidengine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | class SidEngine { 7 | public: 8 | enum ChipModel { MOS6581, MOS8580 }; 9 | enum { REGISTER_COUNT = 25 }; 10 | virtual char const* name() const = 0; 11 | virtual void enable_filter(bool e) = 0; 12 | virtual void set_chip_model(ChipModel cm) = 0; 13 | virtual void update_registers(uint8_t const* regs) = 0; 14 | virtual void mix(int16_t* buffer, int length) = 0; 15 | 16 | static SidEngine* create_resid(); 17 | static SidEngine* create_tinysid(); 18 | }; 19 | --------------------------------------------------------------------------------