1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 *   Mupen64plus - dd_controller.c                                         *
3 *   Mupen64Plus homepage: http://code.google.com/p/mupen64plus/           *
4 *   Copyright (C) 2015 LuigiBlood                                         *
5 *                                                                         *
6 *   This program is free software; you can redistribute it and/or modify  *
7 *   it under the terms of the GNU General Public License as published by  *
8 *   the Free Software Foundation; either version 2 of the License, or     *
9 *   (at your option) any later version.                                   *
10 *                                                                         *
11 *   This program is distributed in the hope that it will be useful,       *
12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14 *   GNU General Public License for more details.                          *
15 *                                                                         *
16 *   You should have received a copy of the GNU General Public License     *
17 *   along with this program; if not, write to the                         *
18 *   Free Software Foundation, Inc.,                                       *
19 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
20 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21 
22 #include "dd_controller.h"
23 #include "dd_rom.h"
24 #include "dd_disk.h"
25 
26 #include <string.h>
27 #include <time.h>
28 
29 #define M64P_CORE_PROTOTYPES 1
30 #include "api/m64p_config.h"
31 #include "api/m64p_types.h"
32 #include "main/main.h"
33 #include "main/device.h"
34 #include "memory/memory.h"
35 #include "r4300/cp0.h"
36 #include "r4300/cp0_private.h"
37 #include "r4300/interrupt.h"
38 #include "r4300/r4300_core.h"
39 #include "si/pif.h"
40 #include "si/si_controller.h"
41 
42 extern int dd_bm_mode_read;
43 extern int CUR_BLOCK;
44 int dd_bm_reset_hold;
45 struct tm* timeinfo;
46 
byte2bcd(int n)47 static unsigned char byte2bcd(int n)
48 {
49     n %= 100;
50     return ((n / 10) << 4) | (n % 10);
51 }
52 
init_dd(struct dd_controller * dd,struct r4300_core * r4300,uint8_t * dd_disk,size_t dd_disk_size)53 void init_dd(struct dd_controller* dd,
54                 struct r4300_core* r4300,
55                 uint8_t* dd_disk,
56                 size_t dd_disk_size)
57 {
58     dd->r4300 = r4300;
59 
60     init_dd_disk(&dd->disk, dd_disk, dd_disk_size);
61 }
62 
poweron_dd(struct dd_controller * dd)63 void poweron_dd(struct dd_controller* dd)
64 {
65     memset(dd->regs, 0, ASIC_REGS_COUNT*sizeof(uint32_t));
66     memset(dd->c2_buf, 0, 0x400);
67     memset(dd->sec_buf, 0, 0x100);
68     memset(dd->mseq_buf, 0, 0x40);
69 
70     dd_bm_reset_hold = 0;
71 
72     dd->regs[ASIC_CMD_STATUS] =
73         (ConfigGetParamBool(g_CoreConfig, "64DD") == 1) ? 0x01400000 : 0xffffffff;
74     dd->regs[ASIC_ID_REG] = 0x00030000;
75 }
76 
read_dd_regs(void * opaque,uint32_t address,uint32_t * value)77 int read_dd_regs(void* opaque, uint32_t address, uint32_t* value)
78 {
79    int Cur_Sector;
80    struct dd_controller* dd = (struct dd_controller*)opaque;
81    uint32_t reg = dd_reg(address);
82 
83    uint32_t offset = address & 0x00000fff;
84 
85    *value = 0x00000000;
86 
87    if (reg < ASIC_REGS_COUNT)
88       *value = dd->regs[reg];
89 
90    Cur_Sector = dd->regs[ASIC_CUR_SECTOR] >> 16;
91    if (Cur_Sector >= 0x5A)
92       Cur_Sector -= 0x5A;
93 
94    if ((reg == ASIC_CMD_STATUS) && (dd->regs[ASIC_CMD_STATUS] & 0x04000000) && (85 < Cur_Sector))
95    {
96       dd->regs[ASIC_CMD_STATUS] &= ~0x04000000;
97       cp0_update_count();
98       g_cp0_regs[CP0_CAUSE_REG] &= ~0x00000800;
99       check_interrupt();
100       dd_update_bm(dd);
101    }
102 
103 #if 0
104    /* BUFFERS */
105    switch (address & 0x00000c00)
106    {
107       case 0x000:
108          /* C2 BUFFER */
109          *value = dd->c2_buf[offset/4];
110          break;
111       case 0x400:
112          /* SECTOR BUFFER */
113          offset -= 0x400;
114          *value = dd->sec_buf[offset/4];
115          break;
116    }
117 
118    if ((address & 0x00000f00) == 0x500)
119    {
120       /* REGS */
121       if (reg < ASIC_REGS_COUNT)
122          *value = dd->regs[reg];
123 
124       if (((address & 0x00000FFF) >= 0x580) || ((address & 0x00000FFF) < 0x5C0))
125       {
126          /* MSEQ */
127          offset -= 0x580;
128          *value = dd->mseq_buf[offset/4];
129       }
130    }
131 #endif
132    return 0;
133 }
134 
write_dd_regs(void * opaque,uint32_t address,uint32_t value,uint32_t mask)135 int write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask)
136 {
137    uint8_t year, month, hour, day, min, sec;
138    struct dd_controller* dd = (struct dd_controller*)opaque;
139    uint32_t reg = dd_reg(address);
140 
141    value &= 0xffff0000;
142 
143    if (!ConfigGetParamBool(g_CoreConfig, "64DD"))
144       return 0;
145 
146    switch (reg)
147    {
148       case ASIC_DATA:
149          dd->regs[ASIC_DATA] = value;
150          break;
151 
152       case ASIC_BM_STATUS_CTL:
153          /* SET SECTOR */
154          dd->regs[ASIC_CUR_SECTOR] = value & 0x00FF0000;
155          CUR_BLOCK = ((dd->regs[ASIC_CUR_SECTOR] >> 16) < 0x5A) ? 0 : 1;
156 
157          if (value & 0x01000000)
158          {
159             /* MECHA INT RESET */
160             dd->regs[ASIC_CMD_STATUS] &= ~0x02000000;
161          }
162 
163          if (value & 0x02000000)
164          {
165             /* BLOCK TRANSFER */
166             dd->regs[ASIC_BM_STATUS_CTL] |= 0x01000000;
167          }
168 
169          if (value & 0x10000000)
170          {
171             /* BM RESET */
172             dd_bm_reset_hold = 1;
173          }
174 
175          if (!(value & 0x10000000) && dd_bm_reset_hold)
176          {
177             /* BM RESET */
178             dd_bm_reset_hold = 0;
179             dd->regs[ASIC_CMD_STATUS] &= ~0x5C000000;
180             dd->regs[ASIC_BM_STATUS_CTL] = 0x00000000;
181             CUR_BLOCK = 0;
182             dd->regs[ASIC_CUR_SECTOR] = 0;
183          }
184 
185          if ((dd->regs[ASIC_CMD_STATUS] & 0x06000000) == 0)
186          {
187             g_cp0_regs[CP0_CAUSE_REG] &= ~0x00000800;
188             cp0_update_count();
189             check_interrupt();
190          }
191 
192          if (value & 0x80000000)
193          {
194             /* BM START */
195             dd->regs[ASIC_BM_STATUS_CTL] |= 0x80000000;
196             dd_update_bm(dd);
197          }
198 
199          break;
200 
201       case ASIC_CMD_STATUS:
202          /* ASIC Commands */
203          timeinfo = (struct tm*)af_rtc_get_time(&g_dev.si.pif.af_rtc);
204 
205          switch (value >> 16)
206          {
207             case 0x01:
208                /* SEEK READ TRACK */
209                dd->regs[ASIC_CUR_TK] = dd->regs[ASIC_DATA] | 0x60000000;
210                dd->regs[ASIC_CMD_STATUS] &= ~0x00180000;
211                dd_bm_mode_read = 1;
212                dd_set_zone_and_track_offset(dd);
213                break;
214 
215             case 0x02:
216                /* SEEK WRITE TRACK */
217                dd->regs[ASIC_CUR_TK] = dd->regs[ASIC_DATA] | 0x60000000;
218                dd->regs[ASIC_CMD_STATUS] &= ~0x00180000;
219                dd_bm_mode_read = 0;
220                dd_set_zone_and_track_offset(dd);
221                break;
222 
223             case 0x08:
224                /* CLEAR DISK CHANGE FLAG */
225                dd->regs[ASIC_CMD_STATUS] &= ~0x00010000;
226                break;
227 
228             case 0x09:
229                /* CLEAR RESET FLAG */
230                dd->regs[ASIC_CMD_STATUS] &= ~0x00400000;
231                break;
232 
233             case 0x12:
234                /* Get Year/Month */
235 
236                /* Put time in DATA as BCD */
237                year = (uint8_t)byte2bcd(timeinfo->tm_year);
238                month = (uint8_t)byte2bcd((timeinfo->tm_mon + 1));
239 
240                dd->regs[ASIC_DATA] = (year << 24) | (month << 16);
241                break;
242 
243             case 0x13:
244                /* Get Day/Hour */
245 
246                /* Put time in DATA as BCD */
247                hour = (uint8_t)byte2bcd(timeinfo->tm_hour);
248                day = (uint8_t)byte2bcd(timeinfo->tm_mday);
249 
250                dd->regs[ASIC_DATA] = (day << 24) | (hour << 16);
251                break;
252             case 0x14:
253                /* Get Min/Sec */
254 
255                /* Put time in DATA as BCD */
256                min = (uint8_t)byte2bcd(timeinfo->tm_min);
257                sec = (uint8_t)byte2bcd(timeinfo->tm_sec);
258 
259                dd->regs[ASIC_DATA] = (min << 24) | (sec << 16);
260                break;
261 
262             case 0x1b:
263                /* Feature Inquiry */
264                dd->regs[ASIC_DATA] = 0x00000000;
265                break;
266          }
267 
268          dd->regs[ASIC_CMD_STATUS] |= 0x02000000;
269          cp0_update_count();
270          g_cp0_regs[CP0_CAUSE_REG] |= 0x00000800;
271          check_interrupt();
272 #if 0
273          add_interrupt_event(CART_INT, 1000);
274 #endif
275          break;
276 
277       case ASIC_HARD_RESET:
278          dd->regs[ASIC_CMD_STATUS] |= 0x00400000;
279          break;
280 
281       case ASIC_HOST_SECBYTE:
282          dd->regs[ASIC_HOST_SECBYTE] = value;
283          break;
284    }
285 
286    return 0;
287 }
288 
dd_end_of_dma_event(struct dd_controller * dd)289 int dd_end_of_dma_event(struct dd_controller* dd)
290 {
291 #if 0
292     Insert clear CART INT here or something
293     dd_update_bm(dd);
294 
295     if ((dd->regs[ASIC_CMD_STATUS] & 0x04000000) == 0)
296         return 1;
297 #endif
298 
299     return 0;
300 }
301 
dd_pi_test()302 void dd_pi_test()
303 {
304     g_cp0_regs[CP0_CAUSE_REG] &= ~0x00000800;
305     cp0_update_count();
306     check_interrupt();
307 }
308