1 /*
2  *  Copyright (C) 2002-2021  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
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 for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 
20 #ifndef DOSBOX_ADLIB_H
21 #define DOSBOX_ADLIB_H
22 
23 #include "dosbox.h"
24 #include "mixer.h"
25 #include "inout.h"
26 #include "setup.h"
27 #include "pic.h"
28 #include "hardware.h"
29 
30 #include <cmath>
31 
32 namespace Adlib {
33 
34 class Timer {
35 	//Rounded down start time
36 	double start;
37 	//Time when you overflow
38 	double trigger;
39 	//Clock interval
40 	double clockInterval;
41 	//cycle interval
42 	double counterInterval;
43 	uint8_t counter;
44 	bool enabled;
45 	bool overflow;
46 	bool masked;
47 
48 public:
Timer(int16_t micros)49 	Timer(int16_t micros)
50 	        : start(0.0),
51 	          trigger(0.0),
52 	          clockInterval(micros * 0.001), // interval in milliseconds
53 	          counterInterval(0.0),
54 	          counter(0),
55 	          enabled(false),
56 	          overflow(false),
57 	          masked(false)
58 	{
59 		SetCounter(0);
60 	}
61 
62 	//Update returns with true if overflow
63 	//Properly syncs up the start/end to current time and changing intervals
Update(const double time)64 	bool Update(const double time)
65 	{
66 		if (enabled && (time >= trigger)) {
67 			// How far into the next cycle
68 			const double deltaTime = time - trigger;
69 			// Sync start to last cycle
70 			const auto counterMod = fmod(deltaTime, counterInterval);
71 			start = time - counterMod;
72 			trigger = start + counterInterval;
73 			//Only set the overflow flag when not masked
74 			if (!masked) {
75 				overflow = true;
76 			}
77 		}
78 		return overflow;
79 	}
80 
81 	//On a reset make sure the start is in sync with the next cycle
Reset()82 	void Reset() {
83 		overflow = false;
84 	}
85 
SetCounter(Bit8u val)86 	void SetCounter(Bit8u val) {
87 		counter = val;
88 		//Interval for next cycle
89 		counterInterval = (256 - counter) * clockInterval;
90 	}
91 
SetMask(bool set)92 	void SetMask(bool set) {
93 		masked = set;
94 		if (masked)
95 			overflow = false;
96 	}
97 
Stop()98 	void Stop( ) {
99 		enabled = false;
100 	}
101 
Start(const double time)102 	void Start(const double time)
103 	{
104 		// Only properly start when not running before
105 		if (!enabled) {
106 			enabled = true;
107 			overflow = false;
108 			//Sync start to the last clock interval
109 			const auto clockMod = fmod(time, clockInterval);
110 			start = time - clockMod;
111 			//Overflow trigger
112 			trigger = start + counterInterval;
113 		}
114 	}
115 };
116 
117 struct Chip {
118 	//Last selected register
119 	Timer timer0, timer1;
120 	//Check for it being a write to the timer
121 	bool Write( Bit32u addr, Bit8u val );
122 	//Read the current timer state, will use current double
123 	Bit8u Read( );
124 
125 	Chip();
126 };
127 
128 //The type of handler this is
129 typedef enum {
130 	MODE_OPL2,
131 	MODE_DUALOPL2,
132 	MODE_OPL3,
133 	MODE_OPL3GOLD
134 } Mode;
135 
136 class Handler {
137 public:
138 	//Write an address to a chip, returns the address the chip sets
139 	virtual Bit32u WriteAddr(io_port_t port, Bit8u val) = 0;
140 	//Write to a specific register in the chip
141 	virtual void WriteReg( Bit32u addr, Bit8u val ) = 0;
142 	//Generate a certain amount of samples
143 	virtual void Generate(mixer_channel_t &chan, uint16_t samples) = 0;
144 	//Initialize at a specific sample rate and mode
145 	virtual void Init(uint32_t rate) = 0;
146 	virtual ~Handler() = default;
147 };
148 
149 //The cache for 2 chips or an opl3
150 typedef Bit8u RegisterCache[512];
151 
152 //Internal class used for dro capturing
153 class Capture;
154 
155 class Module: public Module_base {
156 	IO_ReadHandleObject ReadHandler[3];
157 	IO_WriteHandleObject WriteHandler[3];
158 
159 	//Mode we're running in
160 	Mode mode;
161 	//Last selected address in the chip for the different modes
162 	union {
163 		Bit32u normal;
164 		Bit8u dual[2];
165 	} reg;
166 	struct {
167 		bool active;
168 		Bit8u index;
169 		Bit8u lvol;
170 		Bit8u rvol;
171 		bool mixer;
172 	} ctrl;
173 	void CacheWrite( Bit32u reg, Bit8u val );
174 	void DualWrite( Bit8u index, Bit8u reg, Bit8u val );
175 	void CtrlWrite( Bit8u val );
176 	uint8_t CtrlRead(void);
177 
178 public:
179 	static OPL_Mode oplmode;
180 	mixer_channel_t mixerChan;
181 	Bit32u lastUsed;				//Ticks when adlib was last used to turn of mixing after a few second
182 
183 	Handler* handler;				//Handler that will generate the sound
184 	RegisterCache cache;
185 	Capture* capture;
186 	Chip	chip[2];
187 
188 	//Handle port writes
189 	void PortWrite(io_port_t port, io_val_t value, io_width_t width);
190 	uint8_t PortRead(io_port_t port, io_width_t width);
191 	void Init(Mode m);
192 
193 	Module(Section *configuration);
194 	~Module() override;
195 
196 	Module(const Module&) = delete; // prevent copy
197 	Module& operator=(const Module&) = delete; // prevent assignment
198 };
199 
200 } // namespace Adlib
201 
202 #endif
203