| 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 
|---|
| 24 | AtariVox::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 
|---|
| 43 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 
|---|
| 61 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 
|---|
| 79 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 
|---|
| 120 | void AtariVox::reset() | 
|---|
| 121 | { | 
|---|
| 122 | myLastDataWriteCycle = 0; | 
|---|
| 123 | SaveKey::reset(); | 
|---|
| 124 | } | 
|---|
| 125 |  | 
|---|