965c031b5e3236cb16a46d78f6e63179413b0794
[picodrive.git] / Pico / cd / buffering.c
1 // Buffering handling
2 // (c) Copyright 2007, Grazvydas "notaz" Ignotas
3
4 #include "../PicoInt.h"
5
6 int PicoCDBuffers = 0;
7 static unsigned char *cd_buffer = NULL;
8 static int prev_lba = 0x80000000;
9
10 static int hits, reads;
11
12 //#define THREADED_CD_IO
13
14 /* threaded reader */
15 #ifdef THREADED_CD_IO
16 #include <pthread.h>
17 #define tioprintf printf
18
19 static pthread_t thr_thread = 0;
20 static pthread_cond_t  thr_cond  = PTHREAD_COND_INITIALIZER;
21 static pthread_mutex_t thr_mutex = PTHREAD_MUTEX_INITIALIZER;
22 static unsigned char *thr_buffer[2][2048 + 304] __attribute__((aligned(4)));
23 static int thr_lba_need;
24 static int thr_lba_have[2];
25
26 static void thr_read_lba(int slot, int lba)
27 {
28         int is_bin = Pico_mcd->TOC.Tracks[0].ftype == TYPE_BIN;
29         int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
30
31         pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
32         pm_read(thr_buffer[slot], 2048, Pico_mcd->TOC.Tracks[0].F);
33         thr_lba_have[slot] = lba;
34 }
35
36 static void *buffering_thread(void *arg)
37 {
38         int free_slot, lba;
39
40         elprintf(EL_STATUS, "CD I/O thread started.");
41
42         pthread_mutex_lock(&thr_mutex);
43
44         while (1)
45         {
46                 if (thr_lba_need < 0) goto wait;
47
48                 free_slot = -1;
49                 if (thr_lba_have[0] == -1) free_slot = 0;
50                 if (thr_lba_have[1] == -1) free_slot = 1;
51                 if (free_slot == -1) goto wait;
52
53                 lba = thr_lba_need;
54                 if (lba != thr_lba_have[free_slot^1]) {
55                         thr_read_lba(free_slot, lba);
56                         tioprintf("t done %i %i\n", lba, free_slot);
57                         continue;
58                 }
59                 lba++;
60                 if (lba != thr_lba_have[free_slot^1]) {
61                         thr_read_lba(free_slot, lba);
62                         tioprintf("t done %i %i\n", lba, free_slot);
63                         continue;
64                 }
65
66 wait:
67                 pthread_cond_wait(&thr_cond, &thr_mutex);
68                 tioprintf("t wake\n");
69         }
70
71         pthread_mutex_unlock(&thr_mutex);
72
73         return NULL;
74 }
75
76 static void threaded_read(void *dest, int lba)
77 {
78         int i, have = -1;
79         tioprintf("\n");
80
81         if (lba == thr_lba_have[0]) have = 0;
82         if (lba == thr_lba_have[1]) have = 1;
83         if (have != -1)
84         {
85                 tioprintf("r hit  %i %i\n", lba, have);
86                 memcpy32(dest, (int *)thr_buffer[have], 2048/4);
87                 if (lba != prev_lba) {
88                         thr_lba_have[have] = -1; // make free slot
89                         thr_lba_need = lba + 1;  // guess a sequential read..
90                         pthread_cond_signal(&thr_cond);
91                         sched_yield();
92                         prev_lba = lba;
93                 }
94                 return;
95         }
96
97         tioprintf("r miss %i\n", lba);
98         thr_lba_need = lba;
99         pthread_mutex_lock(&thr_mutex);
100         pthread_mutex_unlock(&thr_mutex);
101         if (lba == thr_lba_have[0]) have = 0;
102         if (lba == thr_lba_have[1]) have = 1;
103         if (have == -1)
104         {
105                 // try again..
106                 thr_lba_have[0] = thr_lba_have[1] = -1;
107                 for (i = 0; have == -1 && i < 10; i++)
108                 {
109                         tioprintf("r hard %i\n", lba);
110                         pthread_cond_signal(&thr_cond);
111                         sched_yield();
112                         pthread_mutex_lock(&thr_mutex);
113                         pthread_mutex_unlock(&thr_mutex);
114                         if (lba == thr_lba_have[0]) have = 0;
115                         if (lba == thr_lba_have[1]) have = 1;
116                 }
117         }
118
119         // we MUST have the data at this point..
120         if (have == -1) { tioprintf("BUG!\n"); exit(1); }
121         tioprintf("r reco %i %i\n", lba, have);
122         memcpy32(dest, (int *)thr_buffer[have], 2048/4);
123         thr_lba_have[have] = -1;
124         pthread_cond_signal(&thr_cond);
125
126         prev_lba = lba;
127         return;
128 }
129 #endif
130
131
132 void PicoCDBufferInit(void)
133 {
134         void *tmp;
135
136         prev_lba = 0x80000000;
137         hits = reads = 0;
138         cd_buffer = NULL;
139
140         if (PicoCDBuffers <= 1) {
141                 PicoCDBuffers = 0;
142                 goto no_buffering; /* buffering off */
143         }
144
145         /* try alloc'ing until we succeed */
146         while (PicoCDBuffers > 0)
147         {
148                 tmp = realloc(cd_buffer, PicoCDBuffers * 2048 + 304);
149                 if (tmp != NULL) break;
150                 PicoCDBuffers >>= 1;
151         }
152
153         if (PicoCDBuffers > 0) {
154                 cd_buffer = tmp;
155                 return;
156         }
157
158 no_buffering:;
159 #ifdef THREADED_CD_IO
160         thr_lba_need = thr_lba_have[0] = thr_lba_have[1] = -1;
161         if (thr_thread == 0)
162         {
163                 pthread_create(&thr_thread, NULL, buffering_thread, NULL);
164         }
165 #endif
166 }
167
168
169 void PicoCDBufferFree(void)
170 {
171 #ifdef THREADED_CD_IO
172         pthread_mutex_lock(&thr_mutex);
173         pthread_mutex_unlock(&thr_mutex);
174 #endif
175         if (cd_buffer) {
176                 free(cd_buffer);
177                 cd_buffer = NULL;
178         }
179         if (reads)
180                 elprintf(EL_STATUS, "CD buffer hits: %i/%i (%i%%)\n", hits, reads, hits * 100 / reads);
181 }
182
183
184 /* this is a try to fight slow SD access of GP2X */
185 PICO_INTERNAL void PicoCDBufferRead(void *dest, int lba)
186 {
187         int is_bin, offs, read_len, moved = 0;
188         reads++;
189
190         is_bin = Pico_mcd->TOC.Tracks[0].ftype == TYPE_BIN;
191
192         if (PicoCDBuffers <= 0)
193         {
194 #ifdef THREADED_CD_IO
195                 threaded_read(dest, lba);
196                 return;
197 #else
198                 /* no buffering */
199                 int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
200                 pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
201                 pm_read(dest, 2048, Pico_mcd->TOC.Tracks[0].F);
202                 return;
203 #endif
204         }
205
206         /* hit? */
207         offs = lba - prev_lba;
208         if (offs >= 0 && offs < PicoCDBuffers)
209         {
210                 hits++;
211                 if (offs == 0) dprintf("CD buffer seek to old %i -> %i\n", prev_lba, lba);
212                 memcpy32(dest, (int *)(cd_buffer + offs*2048), 2048/4);
213                 return;
214         }
215
216         if (prev_lba + PicoCDBuffers != lba)
217         {
218                 int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
219                 dprintf("CD buffer seek %i -> %i\n", prev_lba, lba);
220                 pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
221         }
222
223         dprintf("CD buffer miss %i -> %i\n", prev_lba, lba);
224
225         if (lba < prev_lba && prev_lba - lba < PicoCDBuffers)
226         {
227                 read_len = prev_lba - lba;
228                 dprintf("CD buffer move=%i, read_len=%i", PicoCDBuffers - read_len, read_len);
229                 memmove(cd_buffer + read_len*2048, cd_buffer, (PicoCDBuffers - read_len)*2048);
230                 moved = 1;
231         }
232         else
233         {
234                 read_len = PicoCDBuffers;
235         }
236
237         if (PicoMessage != NULL && read_len >= 512)
238         {
239                 PicoMessage("Buffering data...");
240         }
241
242         if (is_bin)
243         {
244                 int i = 0;
245 #if REDUCE_IO_CALLS
246                 int bufs = (read_len*2048) / (2048+304);
247                 pm_read(cd_buffer, bufs*(2048+304), Pico_mcd->TOC.Tracks[0].F);
248                 for (i = 1; i < bufs; i++)
249                         // should really use memmove here, but my memcpy32 implementation is also suitable here
250                         memcpy32((int *)(cd_buffer + i*2048), (int *)(cd_buffer + i*(2048+304)), 2048/4);
251 #endif
252                 for (; i < read_len - 1; i++)
253                 {
254                         pm_read(cd_buffer + i*2048, 2048 + 304, Pico_mcd->TOC.Tracks[0].F);
255                         // pm_seek(Pico_mcd->TOC.Tracks[0].F, 304, SEEK_CUR); // seeking is slower, in PSP case even more
256                 }
257                 // further data might be moved, do not overwrite
258                 pm_read(cd_buffer + i*2048, 2048, Pico_mcd->TOC.Tracks[0].F);
259                 pm_seek(Pico_mcd->TOC.Tracks[0].F, 304, SEEK_CUR);
260         }
261         else
262         {
263                 pm_read(cd_buffer, read_len*2048, Pico_mcd->TOC.Tracks[0].F);
264         }
265         memcpy32(dest, (int *) cd_buffer, 2048/4);
266         prev_lba = lba;
267
268         if (moved)
269         {
270                 /* file pointer must point to the same data in file, as would-be data after our buffer */
271                 int where_seek;
272                 lba += PicoCDBuffers;
273                 where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
274                 pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
275         }
276 }
277