├── .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 | 
8 |
9 | Each channel is shown in a certain color:
10 |
11 | Channel 1 | red |
12 | Channel 2 | green |
13 | Channel 3 | blue |
14 |
15 |
16 |
17 | ## Controls
18 |
19 |
20 |
21 | SPACE |
22 | Toggle playback. |
23 |
24 |
25 | 1 /2 /3 |
26 | Toggle channels. |
27 |
28 |
29 | LEFT /RIGHT |
30 |
31 | Move through the song.
32 | Hold SHIFT to move faster.
33 | Hold CTRL to move frame by frame.
34 | |
35 |
36 |
37 | UP /DOWN |
38 |
39 | Scroll up/down.
40 | |
41 |
42 |
43 | BACKSPACE |
44 | Jump to the beginning of the song. |
45 |
46 |
47 | + /- |
48 | Time zoom. |
49 |
50 |
51 | PAGE DOWN /PAGE UP |
52 | Pitch zoom. |
53 |
54 |
55 | TAB |
56 | Toggle SID engine. |
57 |
58 |
59 | B |
60 | Toggle bars. |
61 |
62 |
63 | F |
64 | Toggle filter. |
65 |
66 |
67 | M |
68 | Toggle chip model. |
69 |
70 |
71 | W /S /A /D |
72 | Change bar offset/width. |
73 |
74 |
75 | < /> |
76 | Switch sub tune. |
77 |
78 |
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 |
--------------------------------------------------------------------------------