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