1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include "MediaFactory.hxx"
19#include "System.hxx"
20#include "OSystem.hxx"
21#include "AtariVox.hxx"
22
23// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
24AtariVox::AtariVox(Jack jack, const Event& event, const System& system,
25 const string& portname, const string& eepromfile,
26 onMessageCallback callback)
27 : SaveKey(jack, event, system, eepromfile, callback, Controller::Type::AtariVox),
28 myShiftCount(0),
29 myShiftRegister(0),
30 myLastDataWriteCycle(0)
31{
32 mySerialPort = MediaFactory::createSerialPort();
33 if(mySerialPort->openPort(portname))
34 myAboutString = " (using serial port \'" + portname + "\')";
35 else
36 myAboutString = " (invalid serial port \'" + portname + "\')";
37
38 setPin(DigitalPin::Three, true);
39 setPin(DigitalPin::Four, true);
40}
41
42// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
43bool AtariVox::read(DigitalPin pin)
44{
45 // We need to override the Controller::read() method, since the timing
46 // of the actual read is important for the EEPROM (we can't just read
47 // 60 times per second in the ::update() method)
48 switch(pin)
49 {
50 // Pin 2: SpeakJet READY
51 case DigitalPin::Two:
52 // For now, we just assume the device is always ready
53 return setPin(pin, true);
54
55 default:
56 return SaveKey::read(pin);
57 }
58}
59
60// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
61void AtariVox::write(DigitalPin pin, bool value)
62{
63 // Change the pin state based on value
64 switch(pin)
65 {
66 // Pin 1: SpeakJet DATA
67 // output serial data to the speakjet
68 case DigitalPin::One:
69 setPin(pin, value);
70 clockDataIn(value);
71 break;
72
73 default:
74 SaveKey::write(pin, value);
75 }
76}
77
78// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
79void AtariVox::clockDataIn(bool value)
80{
81 if(value && (myShiftCount == 0))
82 return;
83
84 // If this is the first write this frame, or if it's been a long time
85 // since the last write, start a new data byte.
86 uInt64 cycle = mySystem.cycles();
87 if((cycle < myLastDataWriteCycle) || (cycle > myLastDataWriteCycle + 1000))
88 {
89 myShiftRegister = 0;
90 myShiftCount = 0;
91 }
92
93 // If this is the first write this frame, or if it's been 62 cycles
94 // since the last write, shift this bit into the current byte.
95 if((cycle < myLastDataWriteCycle) || (cycle >= myLastDataWriteCycle + 62))
96 {
97 myShiftRegister >>= 1;
98 myShiftRegister |= (value << 15);
99 if(++myShiftCount == 10)
100 {
101 myShiftCount = 0;
102 myShiftRegister >>= 6;
103 if(!(myShiftRegister & (1<<9)))
104 cerr << "AtariVox: bad start bit" << endl;
105 else if((myShiftRegister & 1))
106 cerr << "AtariVox: bad stop bit" << endl;
107 else
108 {
109 uInt8 data = ((myShiftRegister >> 1) & 0xff);
110 mySerialPort->writeByte(data);
111 }
112 myShiftRegister = 0;
113 }
114 }
115
116 myLastDataWriteCycle = cycle;
117}
118
119// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
120void AtariVox::reset()
121{
122 myLastDataWriteCycle = 0;
123 SaveKey::reset();
124}
125