1 #include <stdint.h>
2 #include <stdbool.h>
3 
4 #include "emulator.h"
5 #include "portability.h"
6 #include "dbvz.h"
7 
8 
9 bool ads7846PenIrqEnabled;
10 
11 static const uint16_t ads7846DockResistorValues[EMU_PORT_END] = {0xFFF/*none*/, 0x1EB/*USB cradle*/, 0x000/*serial cradle, unknown*/, 0x000/*USB peripheral, unknown*/, 0x000/*serial peripheral, unknown*/};
12 
13 static uint8_t  ads7846BitsToNextControl;
14 static uint8_t  ads7846ControlByte;
15 static uint16_t ads7846OutputValue;
16 static bool     ads7846ChipSelect;
17 
18 
ads7846RangeMap(float oldMin,float oldMax,float value,float newMin,float newMax)19 static float ads7846RangeMap(float oldMin, float oldMax, float value, float newMin, float newMax){
20    return (value - oldMin) / (oldMax - oldMin) * (newMax - newMin) + newMin;
21 }
22 
ads7846GetAdcBit(void)23 static bool ads7846GetAdcBit(void){
24    bool bit = !!(ads7846OutputValue & 0x8000);
25    ads7846OutputValue <<= 1;
26    return bit;
27 }
28 
ads7846Reset(void)29 void ads7846Reset(void){
30    ads7846BitsToNextControl = 0;
31    ads7846ControlByte = 0x00;
32    ads7846PenIrqEnabled = true;
33    ads7846OutputValue = 0x0000;
34    ads7846ChipSelect = true;
35 #if !defined(EMU_NO_SAFETY)
36    m5XXRefreshTouchState();
37 #endif
38 }
39 
ads7846StateSize(void)40 uint32_t ads7846StateSize(void){
41    uint32_t size = 0;
42 
43    size += sizeof(uint8_t) * 4;
44    size += sizeof(uint16_t);
45 
46    return size;
47 }
48 
ads7846SaveState(uint8_t * data)49 void ads7846SaveState(uint8_t* data){
50    uint32_t offset = 0;
51 
52    writeStateValue8(data + offset, ads7846PenIrqEnabled);
53    offset += sizeof(uint8_t);
54    writeStateValue8(data + offset, ads7846BitsToNextControl);
55    offset += sizeof(uint8_t);
56    writeStateValue8(data + offset, ads7846ControlByte);
57    offset += sizeof(uint8_t);
58    writeStateValue16(data + offset, ads7846OutputValue);
59    offset += sizeof(uint16_t);
60    writeStateValue8(data + offset, ads7846ChipSelect);
61    offset += sizeof(uint8_t);
62 }
63 
ads7846LoadState(uint8_t * data)64 void ads7846LoadState(uint8_t* data){
65    uint32_t offset = 0;
66 
67    ads7846PenIrqEnabled = readStateValue8(data + offset);
68    offset += sizeof(uint8_t);
69    ads7846BitsToNextControl = readStateValue8(data + offset);
70    offset += sizeof(uint8_t);
71    ads7846ControlByte = readStateValue8(data + offset);
72    offset += sizeof(uint8_t);
73    ads7846OutputValue = readStateValue16(data + offset);
74    offset += sizeof(uint16_t);
75    ads7846ChipSelect = readStateValue8(data + offset);
76    offset += sizeof(uint8_t);
77 }
78 
ads7846SetChipSelect(bool value)79 void ads7846SetChipSelect(bool value){
80    //reset the chip when disabled, chip is active when chip select is low
81    if(value && !ads7846ChipSelect){
82       ads7846BitsToNextControl = 0;
83       ads7846ControlByte = 0x00;
84       ads7846PenIrqEnabled = true;
85       ads7846OutputValue = 0x0000;
86 #if !defined(EMU_NO_SAFETY)
87       m5XXRefreshTouchState();
88 #endif
89    }
90    ads7846ChipSelect = value;
91 }
92 
ads7846ExchangeBit(bool bitIn)93 bool ads7846ExchangeBit(bool bitIn){
94    //chip data out is high when off
95    if(ads7846ChipSelect)
96       return true;
97 
98    if(ads7846BitsToNextControl > 0)
99       ads7846BitsToNextControl--;
100 
101    if(ads7846BitsToNextControl == 0){
102       //check for control bit
103       //a new control byte can be sent while receiving data
104       //this is valid behavior as long as the start of the last control byte was 16 or more clock cycles ago
105       if(bitIn){
106          ads7846ControlByte = 0x01;
107          ads7846BitsToNextControl = 15;
108       }
109       return ads7846GetAdcBit();
110    }
111    else if(ads7846BitsToNextControl >= 8){
112       ads7846ControlByte <<= 1;
113       ads7846ControlByte |= bitIn;
114    }
115    else if(ads7846BitsToNextControl == 6){
116       //control byte and busy cycle finished, get output value
117       bool bitMode = !!(ads7846ControlByte & 0x08);
118       bool differentialMode = !(ads7846ControlByte & 0x04);
119       uint8_t channel = ads7846ControlByte >> 4 & 0x07;
120       uint8_t powerSave = ads7846ControlByte & 0x03;
121 
122       //debugLog("Accessed ADS7846 Ch:%d, %d bits, %s Mode, Power Save:%d, PC:0x%08X.\n", channel, bitMode ? 8 : 12, differentialMode ? "Diff" : "Normal", ads7846ControlByte & 0x03, flx68000GetPc());
123 
124       //reference disabled currently isnt emulated, I dont know what the proper behavior for that would be
125 
126 #if !defined(EMU_NO_SAFETY)
127       //trigger fake IRQs
128       ads7846OverridePenState(!(channel == 1 || channel == 3 || channel == 4 || channel == 5));
129 #endif
130 
131       if(powerSave != 2){
132          //ADC enabled, get analog value
133          if(differentialMode){
134             switch(channel){
135                case 0:
136                   //temperature 0, wrong mode
137                   ads7846OutputValue = 0xFFF;
138                   break;
139 
140                case 1:
141                   //touchscreen y
142                   if(palmInput.touchscreenTouched)
143                      ads7846OutputValue = ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenY, 0x0EE, 0xEE4);
144                   else
145                      ads7846OutputValue = 0xFEF;//y is almost fully on when dorment
146                   break;
147 
148                case 2:
149                   //battery, wrong mode
150                   ads7846OutputValue = 0xFFF;
151                   break;
152 
153                case 3:
154                   //touchscreen x relative to y
155                   if(palmInput.touchscreenTouched)
156                       ads7846OutputValue = ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenX, 0x093, 0x600) + ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenY, 0x000, 0x280);
157                   else
158                      ads7846OutputValue = 0x000;
159                   break;
160 
161                case 4:
162                   //touchscreen y relative to x
163                   if(palmInput.touchscreenTouched)
164                       ads7846OutputValue = ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenY, 0x9AF, 0xF3F) + ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenX, 0x000, 0x150);
165                   else
166                      ads7846OutputValue = 0xFFF;
167                   break;
168 
169                case 5:
170                   //touchscreen x
171                   if(palmInput.touchscreenTouched)
172                      ads7846OutputValue = ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenX, 0x0FD, 0xF47);
173                   else
174                      ads7846OutputValue = 0x309;
175                   break;
176 
177                case 6:
178                   //dock, wrong mode
179                   ads7846OutputValue = 0xFFF;
180                   break;
181 
182                case 7:
183                   //temperature 1, wrong mode, usualy 0xDFF/0xBFF, sometimes 0xFFF
184                   ads7846OutputValue = 0xDFF;
185                   break;
186             }
187          }
188          else{
189             if(!palmInput.touchscreenTouched){
190                switch(channel){
191                   case 0:
192                      //temperature 0, room temperature
193                      ads7846OutputValue = 0x3E2;
194                      break;
195 
196                   case 1:
197                      //touchscreen y
198                      if(palmInput.touchscreenTouched)
199                         ads7846OutputValue = ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenY, 0x0EE, 0xEE4);
200                      else
201                         ads7846OutputValue = 0xFFF;//y is almost fully on when dorment
202                      break;
203 
204                   case 2:
205                      //battery
206                      //ads7846OutputValue = 0x600;//5%
207                      //ads7846OutputValue = 0x61C;//30%
208                      //ads7846OutputValue = 0x63C;//40%
209                      //ads7846OutputValue = 0x65C;//60%
210                      //ads7846OutputValue = 0x67C;//80%
211                      //ads7846OutputValue = 0x68C;//100%
212                      //ads7846OutputValue = 0x69C;//100%
213                      ads7846OutputValue = ads7846RangeMap(0, 100, palmMisc.batteryLevel, 0x5FD, 0x68C);
214                      break;
215 
216                   case 3:
217                      //touchscreen x relative to y
218                      if(palmInput.touchscreenTouched)
219                         ads7846OutputValue = ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenX, 0x093, 0x600) + ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenY, 0x000, 0x280);
220                      else
221                         ads7846OutputValue = 0x000;
222                      break;
223 
224                   case 4:
225                      //touchscreen y relative to x
226                      if(palmInput.touchscreenTouched)
227                         ads7846OutputValue = ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenY, 0x9AF, 0xF3F) + ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenX, 0x000, 0x150);
228                      else
229                         ads7846OutputValue = 0xFFF;
230                      break;
231 
232                   case 5:
233                      //touchscreen x
234                      if(palmInput.touchscreenTouched)
235                         ads7846OutputValue = ads7846RangeMap(0.0, 1.0, 1.0 - palmInput.touchscreenX, 0x0FD, 0xF47);
236                      else
237                         ads7846OutputValue = 0x3FB;
238                      break;
239 
240                   case 6:
241                      //dock
242                      ads7846OutputValue = ads7846DockResistorValues[palmMisc.dataPort];
243                      break;
244 
245                   case 7:
246                      //temperature 1, room temperature
247                      ads7846OutputValue = 0x4A1;
248                      break;
249                }
250             }
251             else{
252                //crosses lines with REF+(unverified)
253                ads7846OutputValue = 0xF80;
254             }
255          }
256       }
257       else{
258          //ADC disabled, return invalid data
259          if((channel == 3 || channel == 5) && !palmInput.touchscreenTouched)
260             ads7846OutputValue = 0x000;
261          else
262             ads7846OutputValue = 0xFFF;
263       }
264 
265       //move to output position
266       ads7846OutputValue <<= 4;
267 
268       //if 8 bit conversion, clear extra bits and shorten conversion by 4 bits
269       if(bitMode){
270          ads7846OutputValue &= 0xFF00;
271          ads7846BitsToNextControl -= 4;
272       }
273 
274       ads7846PenIrqEnabled = !(powerSave & 0x01);
275 #if !defined(EMU_NO_SAFETY)
276       m5XXRefreshTouchState();
277 #endif
278    }
279 
280    return ads7846GetAdcBit();
281 }
282