mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-04 14:30:19 +00:00
Factored out the Atari 2600 speaker and adjusted it to postfix underscores.
This commit is contained in:
parent
1b1a8d3e52
commit
3361d6b93a
@ -791,133 +791,3 @@ void Machine::synchronise()
|
||||
update_audio();
|
||||
}
|
||||
|
||||
Atari2600::Speaker::Speaker()
|
||||
{
|
||||
_poly4_counter[0] = _poly4_counter[1] = 0x00f;
|
||||
_poly5_counter[0] = _poly5_counter[1] = 0x01f;
|
||||
_poly9_counter[0] = _poly9_counter[1] = 0x1ff;
|
||||
}
|
||||
|
||||
Atari2600::Speaker::~Speaker()
|
||||
{
|
||||
}
|
||||
|
||||
void Atari2600::Speaker::set_volume(int channel, uint8_t volume)
|
||||
{
|
||||
enqueue([=]() {
|
||||
_volume[channel] = volume & 0xf;
|
||||
});
|
||||
}
|
||||
|
||||
void Atari2600::Speaker::set_divider(int channel, uint8_t divider)
|
||||
{
|
||||
enqueue([=]() {
|
||||
_divider[channel] = divider & 0x1f;
|
||||
_divider_counter[channel] = 0;
|
||||
});
|
||||
}
|
||||
|
||||
void Atari2600::Speaker::set_control(int channel, uint8_t control)
|
||||
{
|
||||
enqueue([=]() {
|
||||
_control[channel] = control & 0xf;
|
||||
});
|
||||
}
|
||||
|
||||
#define advance_poly4(c) _poly4_counter[channel] = (_poly4_counter[channel] >> 1) | (((_poly4_counter[channel] << 3) ^ (_poly4_counter[channel] << 2))&0x008)
|
||||
#define advance_poly5(c) _poly5_counter[channel] = (_poly5_counter[channel] >> 1) | (((_poly5_counter[channel] << 4) ^ (_poly5_counter[channel] << 2))&0x010)
|
||||
#define advance_poly9(c) _poly9_counter[channel] = (_poly9_counter[channel] >> 1) | (((_poly9_counter[channel] << 4) ^ (_poly9_counter[channel] << 8))&0x100)
|
||||
|
||||
void Atari2600::Speaker::get_samples(unsigned int number_of_samples, int16_t *target)
|
||||
{
|
||||
for(unsigned int c = 0; c < number_of_samples; c++)
|
||||
{
|
||||
target[c] = 0;
|
||||
for(int channel = 0; channel < 2; channel++)
|
||||
{
|
||||
_divider_counter[channel] ++;
|
||||
int level = 0;
|
||||
switch(_control[channel])
|
||||
{
|
||||
case 0x0: case 0xb: // constant 1
|
||||
level = 1;
|
||||
break;
|
||||
|
||||
case 0x4: case 0x5: // div2 tone
|
||||
level = (_divider_counter[channel] / (_divider[channel]+1))&1;
|
||||
break;
|
||||
|
||||
case 0xc: case 0xd: // div6 tone
|
||||
level = (_divider_counter[channel] / ((_divider[channel]+1)*3))&1;
|
||||
break;
|
||||
|
||||
case 0x6: case 0xa: // div31 tone
|
||||
level = (_divider_counter[channel] / (_divider[channel]+1))%30 <= 18;
|
||||
break;
|
||||
|
||||
case 0xe: // div93 tone
|
||||
level = (_divider_counter[channel] / ((_divider[channel]+1)*3))%30 <= 18;
|
||||
break;
|
||||
|
||||
case 0x1: // 4-bit poly
|
||||
level = _poly4_counter[channel]&1;
|
||||
if(_divider_counter[channel] == _divider[channel]+1)
|
||||
{
|
||||
_divider_counter[channel] = 0;
|
||||
advance_poly4(channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x2: // 4-bit poly div31
|
||||
level = _poly4_counter[channel]&1;
|
||||
if(_divider_counter[channel]%(30*(_divider[channel]+1)) == 18)
|
||||
{
|
||||
advance_poly4(channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x3: // 5/4-bit poly
|
||||
level = _output_state[channel];
|
||||
if(_divider_counter[channel] == _divider[channel]+1)
|
||||
{
|
||||
if(_poly5_counter[channel]&1)
|
||||
{
|
||||
_output_state[channel] = _poly4_counter[channel]&1;
|
||||
advance_poly4(channel);
|
||||
}
|
||||
advance_poly5(channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7: case 0x9: // 5-bit poly
|
||||
level = _poly5_counter[channel]&1;
|
||||
if(_divider_counter[channel] == _divider[channel]+1)
|
||||
{
|
||||
_divider_counter[channel] = 0;
|
||||
advance_poly5(channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xf: // 5-bit poly div6
|
||||
level = _poly5_counter[channel]&1;
|
||||
if(_divider_counter[channel] == (_divider[channel]+1)*3)
|
||||
{
|
||||
_divider_counter[channel] = 0;
|
||||
advance_poly5(channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x8: // 9-bit poly
|
||||
level = _poly9_counter[channel]&1;
|
||||
if(_divider_counter[channel] == _divider[channel]+1)
|
||||
{
|
||||
_divider_counter[channel] = 0;
|
||||
advance_poly9(channel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
target[c] += _volume[channel] * 1024 * level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "../../Processors/6502/CPU6502.hpp"
|
||||
#include "../../Components/6532/6532.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "Speaker.hpp"
|
||||
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "Atari2600Inputs.h"
|
||||
@ -23,56 +24,28 @@ namespace Atari2600 {
|
||||
const unsigned int number_of_upcoming_events = 6;
|
||||
const unsigned int number_of_recorded_counters = 7;
|
||||
|
||||
class Speaker: public ::Outputs::Filter<Speaker> {
|
||||
public:
|
||||
Speaker();
|
||||
~Speaker();
|
||||
|
||||
void set_volume(int channel, uint8_t volume);
|
||||
void set_divider(int channel, uint8_t divider);
|
||||
void set_control(int channel, uint8_t control);
|
||||
|
||||
void get_samples(unsigned int number_of_samples, int16_t *target);
|
||||
|
||||
private:
|
||||
uint8_t _volume[2];
|
||||
uint8_t _divider[2];
|
||||
uint8_t _control[2];
|
||||
|
||||
int _poly4_counter[2];
|
||||
int _poly5_counter[2];
|
||||
int _poly9_counter[2];
|
||||
int _output_state[2];
|
||||
|
||||
int _divider_counter[2];
|
||||
|
||||
int _pattern_periods[16];
|
||||
int _patterns[16][512];
|
||||
};
|
||||
|
||||
class PIA: public MOS::MOS6532<PIA> {
|
||||
public:
|
||||
inline uint8_t get_port_input(int port)
|
||||
{
|
||||
return _portValues[port];
|
||||
return port_values_[port];
|
||||
}
|
||||
|
||||
inline void update_port_input(int port, uint8_t mask, bool set)
|
||||
{
|
||||
if(set) _portValues[port] &= ~mask; else _portValues[port] |= mask;
|
||||
if(set) port_values_[port] &= ~mask; else port_values_[port] |= mask;
|
||||
set_port_did_change(port);
|
||||
}
|
||||
|
||||
PIA() :
|
||||
_portValues{0xff, 0xff}
|
||||
port_values_{0xff, 0xff}
|
||||
{}
|
||||
|
||||
private:
|
||||
uint8_t _portValues[2];
|
||||
uint8_t port_values_[2];
|
||||
|
||||
};
|
||||
|
||||
|
||||
class Machine:
|
||||
public CPU6502::Processor<Machine>,
|
||||
public CRTMachine::Machine,
|
||||
|
137
Machines/Atari2600/Speaker.cpp
Normal file
137
Machines/Atari2600/Speaker.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
//
|
||||
// Speaker.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/12/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Speaker.hpp"
|
||||
|
||||
using namespace Atari2600;
|
||||
|
||||
Atari2600::Speaker::Speaker() :
|
||||
poly4_counter_{0x00f, 0x00f},
|
||||
poly5_counter_{0x01f, 0x01f},
|
||||
poly9_counter_{0x1ff, 0x1ff}
|
||||
{}
|
||||
|
||||
void Atari2600::Speaker::set_volume(int channel, uint8_t volume)
|
||||
{
|
||||
enqueue([=]() {
|
||||
volume_[channel] = volume & 0xf;
|
||||
});
|
||||
}
|
||||
|
||||
void Atari2600::Speaker::set_divider(int channel, uint8_t divider)
|
||||
{
|
||||
enqueue([=]() {
|
||||
divider_[channel] = divider & 0x1f;
|
||||
divider_counter_[channel] = 0;
|
||||
});
|
||||
}
|
||||
|
||||
void Atari2600::Speaker::set_control(int channel, uint8_t control)
|
||||
{
|
||||
enqueue([=]() {
|
||||
control_[channel] = control & 0xf;
|
||||
});
|
||||
}
|
||||
|
||||
#define advance_poly4(c) poly4_counter_[channel] = (poly4_counter_[channel] >> 1) | (((poly4_counter_[channel] << 3) ^ (poly4_counter_[channel] << 2))&0x008)
|
||||
#define advance_poly5(c) poly5_counter_[channel] = (poly5_counter_[channel] >> 1) | (((poly5_counter_[channel] << 4) ^ (poly5_counter_[channel] << 2))&0x010)
|
||||
#define advance_poly9(c) poly9_counter_[channel] = (poly9_counter_[channel] >> 1) | (((poly9_counter_[channel] << 4) ^ (poly9_counter_[channel] << 8))&0x100)
|
||||
|
||||
void Atari2600::Speaker::get_samples(unsigned int number_of_samples, int16_t *target)
|
||||
{
|
||||
for(unsigned int c = 0; c < number_of_samples; c++)
|
||||
{
|
||||
target[c] = 0;
|
||||
for(int channel = 0; channel < 2; channel++)
|
||||
{
|
||||
divider_counter_[channel] ++;
|
||||
int level = 0;
|
||||
switch(control_[channel])
|
||||
{
|
||||
case 0x0: case 0xb: // constant 1
|
||||
level = 1;
|
||||
break;
|
||||
|
||||
case 0x4: case 0x5: // div2 tone
|
||||
level = (divider_counter_[channel] / (divider_[channel]+1))&1;
|
||||
break;
|
||||
|
||||
case 0xc: case 0xd: // div6 tone
|
||||
level = (divider_counter_[channel] / ((divider_[channel]+1)*3))&1;
|
||||
break;
|
||||
|
||||
case 0x6: case 0xa: // div31 tone
|
||||
level = (divider_counter_[channel] / (divider_[channel]+1))%30 <= 18;
|
||||
break;
|
||||
|
||||
case 0xe: // div93 tone
|
||||
level = (divider_counter_[channel] / ((divider_[channel]+1)*3))%30 <= 18;
|
||||
break;
|
||||
|
||||
case 0x1: // 4-bit poly
|
||||
level = poly4_counter_[channel]&1;
|
||||
if(divider_counter_[channel] == divider_[channel]+1)
|
||||
{
|
||||
divider_counter_[channel] = 0;
|
||||
advance_poly4(channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x2: // 4-bit poly div31
|
||||
level = poly4_counter_[channel]&1;
|
||||
if(divider_counter_[channel]%(30*(divider_[channel]+1)) == 18)
|
||||
{
|
||||
advance_poly4(channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x3: // 5/4-bit poly
|
||||
level = output_state_[channel];
|
||||
if(divider_counter_[channel] == divider_[channel]+1)
|
||||
{
|
||||
if(poly5_counter_[channel]&1)
|
||||
{
|
||||
output_state_[channel] = poly4_counter_[channel]&1;
|
||||
advance_poly4(channel);
|
||||
}
|
||||
advance_poly5(channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7: case 0x9: // 5-bit poly
|
||||
level = poly5_counter_[channel]&1;
|
||||
if(divider_counter_[channel] == divider_[channel]+1)
|
||||
{
|
||||
divider_counter_[channel] = 0;
|
||||
advance_poly5(channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xf: // 5-bit poly div6
|
||||
level = poly5_counter_[channel]&1;
|
||||
if(divider_counter_[channel] == (divider_[channel]+1)*3)
|
||||
{
|
||||
divider_counter_[channel] = 0;
|
||||
advance_poly5(channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x8: // 9-bit poly
|
||||
level = poly9_counter_[channel]&1;
|
||||
if(divider_counter_[channel] == divider_[channel]+1)
|
||||
{
|
||||
divider_counter_[channel] = 0;
|
||||
advance_poly9(channel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
target[c] += volume_[channel] * 1024 * level;
|
||||
}
|
||||
}
|
||||
}
|
41
Machines/Atari2600/Speaker.hpp
Normal file
41
Machines/Atari2600/Speaker.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// Speaker.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/12/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Atari2600_Speaker_hpp
|
||||
#define Atari2600_Speaker_hpp
|
||||
|
||||
#include "../../Outputs/Speaker.hpp"
|
||||
|
||||
namespace Atari2600 {
|
||||
|
||||
class Speaker: public ::Outputs::Filter<Speaker> {
|
||||
public:
|
||||
Speaker();
|
||||
|
||||
void set_volume(int channel, uint8_t volume);
|
||||
void set_divider(int channel, uint8_t divider);
|
||||
void set_control(int channel, uint8_t control);
|
||||
|
||||
void get_samples(unsigned int number_of_samples, int16_t *target);
|
||||
|
||||
private:
|
||||
uint8_t volume_[2];
|
||||
uint8_t divider_[2];
|
||||
uint8_t control_[2];
|
||||
|
||||
int poly4_counter_[2];
|
||||
int poly5_counter_[2];
|
||||
int poly9_counter_[2];
|
||||
int output_state_[2];
|
||||
|
||||
int divider_counter_[2];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* Speaker_hpp */
|
@ -385,6 +385,7 @@
|
||||
4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.cpp */; };
|
||||
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA525D1DF33323007E74F2 /* Tape.cpp */; };
|
||||
4BEA52631DF339D7007E74F2 /* Speaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA52611DF339D7007E74F2 /* Speaker.cpp */; };
|
||||
4BEA52661DF3472B007E74F2 /* Speaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA52641DF3472B007E74F2 /* Speaker.cpp */; };
|
||||
4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; };
|
||||
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; };
|
||||
4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; };
|
||||
@ -899,6 +900,8 @@
|
||||
4BEA52601DF3343A007E74F2 /* Interrupts.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Interrupts.hpp; path = Electron/Interrupts.hpp; sourceTree = "<group>"; };
|
||||
4BEA52611DF339D7007E74F2 /* Speaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Speaker.cpp; path = Electron/Speaker.cpp; sourceTree = "<group>"; };
|
||||
4BEA52621DF339D7007E74F2 /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Speaker.hpp; path = Electron/Speaker.hpp; sourceTree = "<group>"; };
|
||||
4BEA52641DF3472B007E74F2 /* Speaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Speaker.cpp; sourceTree = "<group>"; };
|
||||
4BEA52651DF3472B007E74F2 /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = "<group>"; };
|
||||
4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = "<group>"; };
|
||||
4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
|
||||
4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = "<group>"; };
|
||||
@ -1049,8 +1052,10 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B2E2D971C3A06EC00138695 /* Atari2600.cpp */,
|
||||
4B2E2D981C3A06EC00138695 /* Atari2600.hpp */,
|
||||
4BEA52641DF3472B007E74F2 /* Speaker.cpp */,
|
||||
4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */,
|
||||
4B2E2D981C3A06EC00138695 /* Atari2600.hpp */,
|
||||
4BEA52651DF3472B007E74F2 /* Speaker.hpp */,
|
||||
);
|
||||
path = Atari2600;
|
||||
sourceTree = "<group>";
|
||||
@ -2392,6 +2397,7 @@
|
||||
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */,
|
||||
4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */,
|
||||
4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */,
|
||||
4BEA52661DF3472B007E74F2 /* Speaker.cpp in Sources */,
|
||||
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */,
|
||||
4B4C83701D4F623200CD541F /* D64.cpp in Sources */,
|
||||
4B5073071DDD3B9400C48FBD /* ArrayBuilder.cpp in Sources */,
|
||||
|
Loading…
x
Reference in New Issue
Block a user