Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libgambatte/src/sound/duty_unit.cpp
2 views
1
/***************************************************************************
2
* Copyright (C) 2007 by Sindre Aamås *
3
* [email protected] *
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 version 2 as *
7
* published by the Free Software Foundation. *
8
* *
9
* This program is distributed in the hope that it will be useful, *
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12
* GNU General Public License version 2 for more details. *
13
* *
14
* You should have received a copy of the GNU General Public License *
15
* version 2 along with this program; if not, write to the *
16
* Free Software Foundation, Inc., *
17
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18
***************************************************************************/
19
#include "duty_unit.h"
20
#include <algorithm>
21
22
static inline bool toOutState(const unsigned duty, const unsigned pos) {
23
static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E };
24
25
return duties[duty] >> pos & 1;
26
}
27
28
static inline unsigned toPeriod(const unsigned freq) {
29
return (2048 - freq) << 1;
30
}
31
32
namespace gambatte {
33
34
void DutyUnit::updatePos(const unsigned long cc) {
35
if (cc >= nextPosUpdate) {
36
const unsigned long inc = (cc - nextPosUpdate) / period + 1;
37
nextPosUpdate += period * inc;
38
pos += inc;
39
pos &= 7;
40
}
41
}
42
43
void DutyUnit::setDuty(const unsigned nr1) {
44
duty = nr1 >> 6;
45
high = toOutState(duty, pos);
46
}
47
48
void DutyUnit::setCounter() {
49
static const unsigned char nextStateDistance[4 * 8] = {
50
6, 5, 4, 3, 2, 1, 0, 0,
51
0, 5, 4, 3, 2, 1, 0, 1,
52
0, 3, 2, 1, 0, 3, 2, 1,
53
0, 5, 4, 3, 2, 1, 0, 1
54
};
55
56
if (enableEvents && nextPosUpdate != COUNTER_DISABLED)
57
counter = nextPosUpdate + period * nextStateDistance[(duty * 8) | pos];
58
else
59
counter = COUNTER_DISABLED;
60
}
61
62
void DutyUnit::setFreq(const unsigned newFreq, const unsigned long cc) {
63
updatePos(cc);
64
period = toPeriod(newFreq);
65
setCounter();
66
}
67
68
void DutyUnit::event() {
69
unsigned inc = period << duty;
70
71
if (duty == 3)
72
inc -= period * 2;
73
74
if (!(high ^= true))
75
inc = period * 8 - inc;
76
77
counter += inc;
78
}
79
80
void DutyUnit::nr1Change(const unsigned newNr1, const unsigned long cc) {
81
updatePos(cc);
82
setDuty(newNr1);
83
setCounter();
84
}
85
86
void DutyUnit::nr3Change(const unsigned newNr3, const unsigned long cc) {
87
setFreq((getFreq() & 0x700) | newNr3, cc);
88
}
89
90
void DutyUnit::nr4Change(const unsigned newNr4, const unsigned long cc) {
91
setFreq((newNr4 << 8 & 0x700) | (getFreq() & 0xFF), cc);
92
93
if (newNr4 & 0x80) {
94
nextPosUpdate = (cc & ~1) + period;
95
setCounter();
96
}
97
}
98
99
DutyUnit::DutyUnit() :
100
nextPosUpdate(COUNTER_DISABLED),
101
period(4096),
102
pos(0),
103
duty(0),
104
high(false),
105
enableEvents(true)
106
{}
107
108
void DutyUnit::reset() {
109
pos = 0;
110
high = toOutState(duty, pos);
111
nextPosUpdate = COUNTER_DISABLED;
112
setCounter();
113
}
114
115
void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned long cc) {
116
nextPosUpdate = std::max(dstate.nextPosUpdate, cc);
117
pos = dstate.pos & 7;
118
setDuty(nr1);
119
period = toPeriod((nr4 << 8 & 0x700) | dstate.nr3);
120
enableEvents = true;
121
setCounter();
122
}
123
124
void DutyUnit::resetCounters(const unsigned long oldCc) {
125
if (nextPosUpdate == COUNTER_DISABLED)
126
return;
127
128
updatePos(oldCc);
129
nextPosUpdate -= COUNTER_MAX;
130
SoundUnit::resetCounters(oldCc);
131
}
132
133
void DutyUnit::killCounter() {
134
enableEvents = false;
135
setCounter();
136
}
137
138
void DutyUnit::reviveCounter(const unsigned long cc) {
139
updatePos(cc);
140
high = toOutState(duty, pos);
141
enableEvents = true;
142
setCounter();
143
}
144
145
SYNCFUNC(DutyUnit)
146
{
147
NSS(counter);
148
NSS(nextPosUpdate);
149
NSS(period);
150
NSS(pos);
151
NSS(duty);
152
NSS(high);
153
NSS(enableEvents);
154
}
155
156
}
157
158