]> Shamusworld >> Repos - virtualjaguar/blob - src/memtrack.cpp
Added Memory Track support. One small step towards full CD-ROM support.
[virtualjaguar] / src / memtrack.cpp
1 //
2 // Memory Track cartridge emulation
3 //
4 // by James Hammons
5 // (C) 2016 Underground Software
6 //
7 // The Memory Track is just a large(-ish) EEPROM, holding 128K. We emulate the
8 // Atmel part, since it seems to be easier to deal with than the AMD part. The
9 // way it works is the 68K checks in its R/W functions to see if the MT is
10 // inserted, and, if so, call the R/W functions here. It also checks to see if
11 // the ROM width was changed to 32-bit; if not, then it reads the normal ROM in
12 // the ROM space like usual.
13 //
14 // The Atmel part reads/writes a single byte into a long space. So we have to
15 // adjust for that when reading from/writing to the NVRAM.
16 //
17 // JLH = James Hammons <jlhamm@acm.org>
18 //
19 // Who  When        What
20 // ---  ----------  -----------------------------------------------------------
21 // JLH  06/12/2016  Created this file ;-)
22 //
23
24 #include "memtrack.h"
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <log.h>
29 #include <settings.h>
30
31
32 #define MEMTRACK_FILENAME       "memtrack.eeprom"
33 //#define DEBUG_MEMTRACK
34
35 enum { MT_NONE, MT_PROD_ID, MT_RESET, MT_WRITE_ENABLE };
36 enum { MT_IDLE, MT_PHASE1, MT_PHASE2 };
37
38 uint8_t mtMem[0x20000];
39 uint8_t mtCommand = MT_NONE;
40 uint8_t mtState = MT_IDLE;
41 bool haveMT = false;
42 char mtFilename[MAX_PATH];
43
44 // Private function prototypes
45 void MTWriteFile(void);
46 void MTStateMachine(uint8_t reg, uint16_t data);
47
48
49 void MTInit(void)
50 {
51         sprintf(mtFilename, "%s%s", vjs.EEPROMPath, MEMTRACK_FILENAME);
52         FILE * fp = fopen(mtFilename, "rb");
53
54         if (fp)
55         {
56                 size_t ignored = fread(mtMem, 1, 0x20000, fp);
57                 fclose(fp);
58                 WriteLog("MT: Loaded NVRAM from %s\n", mtFilename);
59                 haveMT = true;
60         }
61         else
62                 WriteLog("MT: Could not open file \"%s\"!\n", mtFilename);
63 }
64
65
66 void MTReset(void)
67 {
68         if (!haveMT)
69                 memset(mtMem, 0xFF, 0x20000);
70 }
71
72
73 void MTDone(void)
74 {
75         MTWriteFile();
76         WriteLog("MT: Done.\n");
77 }
78
79
80 void MTWriteFile(void)
81 {
82         if (!haveMT)
83                 return;
84
85         FILE * fp = fopen(mtFilename, "wb");
86
87         if (fp)
88         {
89                 fwrite(mtMem, 1, 0x20000, fp);
90                 fclose(fp);
91         }
92         else
93                 WriteLog("MT: Could not create file \"%s\"!", mtFilename);
94 }
95
96
97 //
98 // This is crappy, there doesn't seem to be a word interface to the NVRAM. But
99 // we'll keep this as a placeholder for now.
100 //
101 uint16_t MTReadWord(uint32_t addr)
102 {
103         uint32_t value = MTReadLong(addr);
104
105         if ((addr & 0x03) == 0)
106                 value >>= 16;
107         else if ((addr & 0x03) == 2)
108                 value &= 0xFFFF;
109
110 #ifdef DEBUG_MEMTRACK
111 WriteLog("MT: Reading word @ $%06X: $%04X\n", addr, value);
112 #endif
113
114         return (uint16_t)value;
115 }
116
117
118 uint32_t MTReadLong(uint32_t addr)
119 {
120         uint32_t value = 0;
121
122         if (mtCommand == MT_PROD_ID)
123         {
124                 if (addr == 0x800000)
125                         value = 0x1F;
126                 else if (addr == 0x800004)
127                         value = 0xD5;
128         }
129         else
130         {
131                 value = (uint32_t)mtMem[(addr & 0x7FFFC) >> 2];
132         }
133
134         // We do this because we're not sure how the real thing behaves; but it
135         // seems reasonable on its face to do it this way. :-P So we turn off write
136         // mode when reading the NVRAM.
137         if (mtCommand == MT_WRITE_ENABLE)
138                 mtCommand = MT_NONE;
139
140 #ifdef DEBUG_MEMTRACK
141 WriteLog("MT: Reading long @ $%06X: $%08X\n", addr, value << 16);
142 #endif
143         return value << 16;
144 }
145
146
147 void MTWriteWord(uint32_t addr, uint16_t data)
148 {
149         // We don't care about writes to long offsets + 2
150         if ((addr & 0x3) == 2)
151                 return;
152
153 #ifdef DEBUG_MEMTRACK
154 WriteLog("MT: Writing word @ $%06X: $%04X (Writing is %sabled)\n", addr, data, (mtCommand == MT_WRITE_ENABLE ? "en" : "dis"));
155 #endif
156
157         // Write to the NVRAM if it's enabled...
158         if (mtCommand == MT_WRITE_ENABLE)
159         {
160                 mtMem[(addr & 0x7FFFC) >> 2] = (uint8_t)(data & 0xFF);
161                 return;
162         }
163
164         switch (addr)
165         {
166         case (0x800000 + (4 * 0x5555)):         // $815554
167                 MTStateMachine(0, data);
168                 break;
169         case (0x800000 + (4 * 0x2AAA)):         // $80AAA8
170                 MTStateMachine(1, data);
171                 break;
172         }
173 }
174
175
176 void MTWriteLong(uint32_t addr, uint32_t data)
177 {
178         // Strip off lower 3 bits of the passed in address
179         addr &= 0xFFFFFC;
180
181         MTWriteWord(addr + 0, data & 0xFFFF);
182         MTWriteWord(addr + 2, data >> 16);
183 }
184
185
186 void MTStateMachine(uint8_t reg, uint16_t data)
187 {
188 #ifdef DEBUG_MEMTRACK
189 WriteLog("MTStateMachine: reg = %u, data = $%02X, current state = %u\n", reg, data, mtState);
190 #endif
191         switch (mtState)
192         {
193         case MT_IDLE:
194                 if ((reg == 0) && (data == 0xAA))
195                         mtState = MT_PHASE1;
196
197                 break;
198         case MT_PHASE1:
199                 if ((reg == 1) && (data == 0x55))
200                         mtState = MT_PHASE2;
201                 else
202                         mtState = MT_IDLE;
203
204                 break;
205         case MT_PHASE2:
206                 if (reg == 0)
207                 {
208                         if (data == 0x90)               // Product ID
209                                 mtCommand = MT_PROD_ID;
210                         else if (data == 0xF0)  // Reset
211                                 mtCommand = MT_NONE;
212                         else if (data == 0xA0)  // Write enagle
213                                 mtCommand = MT_WRITE_ENABLE;
214                         else
215                                 mtCommand = MT_NONE;
216                 }
217
218                 mtState = MT_IDLE;
219                 break;
220         }
221 #ifdef DEBUG_MEMTRACK
222 WriteLog("                state = %u, cmd = %u\n", mtState, mtCommand);
223 #endif
224 }
225