UrForth: "STRLITERAL" now respects the optimiser
[urasm.git] / src / libfdc / dskfs_trdos.c
blob5f1b10f69a9e3102f80f6af175bab76f57910c7c
1 /*
2 * WD1793/uPD765 emulator
3 * Copyright (c) 2009-..., SAM style
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is furnished
10 * to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
17 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
18 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
20 * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 * changes by Ketmar // Invisible Vector
24 * Understanding is not required. Only obedience.
25 * git commit: 84b4cb3f749ff3cb954a202b3c65d7b53ee08273
27 #include "dskfs_all.h"
28 #include "dskfs_trdos.h"
29 #include "dskldr.h"
32 //**************************************************************************
34 // TR-DOS low level API
36 //**************************************************************************
38 //==========================================================================
40 // flpIsDiskTRDOS
42 //==========================================================================
43 int flpIsDiskTRDOS (Floppy *flp) {
44 if (!flp || !flp->insert) return 0;
45 // trdos
46 uint8_t fbuf[0x100];
47 if (flpGetSectorData(flp, 0, 15, fbuf, 0x100) != 0) return 0;
48 // at least 16 sectors
49 if (flpGetSectorData(flp, 0, 9, fbuf, 0x100) != 0) return 0;
50 return (fbuf[0xe7] == 0x10u);
54 //==========================================================================
56 // flpFormatTRDTrack
58 // build a single track 16x256 (TRDOS), sector data @bpos (4K)
60 //==========================================================================
61 int flpFormatTRDTrack (Floppy *flp, int tr, const void *bpos, size_t bpossize) {
62 if (!flp || bpossize < 0 || tr < 0 || tr > 255) return FLPERR_SHIT;
63 FDCSector lst[FLP_TRDOS_SECTORS_PER_TRACK];
64 memset(lst, 0, sizeof(lst));
65 const uint8_t *ppos = (const uint8_t *)bpos;
66 if (!ppos) bpossize = 0;
67 for (unsigned f = 0; f < FLP_TRDOS_SECTORS_PER_TRACK; ++f) {
68 FDCSector *sc = &lst[f];
69 sc->sec = (uint8_t)(f+1u);
70 sc->type = 0xfbU;
71 sc->crc = 0xffffU;
72 sc->trk = ((tr&0xfe)>>1);
73 sc->head = (tr&0x01 ? 1 : 0);
74 sc->sz = 1; // 256 bytes
75 if (bpossize > 0) {
76 if (bpossize >= 256) {
77 memcpy(sc->data, ppos, 256);
78 ppos += 256;
79 bpossize -= 256;
80 } else {
81 memcpy(sc->data, ppos, bpossize);
82 memset(sc->data, 0, 256-bpossize);
83 bpossize = 0;
85 } else {
86 memset(sc->data, 0, 256);
89 return flpFormatTracks(flp, tr, lst, FLP_TRDOS_SECTORS_PER_TRACK, FLP_FORMAT_TRACK_CALC_CRC);
93 //==========================================================================
95 // flpFormatTRD
97 // format whole disk as 2x84x16x256 and init as TRDOS
99 //==========================================================================
100 int flpFormatTRD (Floppy *flp) {
101 if (!flp) return FLPERR_SHIT;
102 flp->protect = 0;
103 flp->doubleSide = 1;
104 flp->trk80 = 1;
105 flp->insert = 0;
106 flp->changed = 0;
107 for (int i = 1; i < 168; ++i) {
108 int res = flpFormatTRDTrack(flp, i, NULL, 0);
109 if (res != 0) return res;
111 // first track
112 const uint8_t trd_8e0[32] = {
113 0x00,0x00,0x01,0x16,0x00,0xf0,0x09,0x10,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,
114 0x20,0x20,0x20,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,
116 uint8_t buf[0x1000];
117 memset(buf, 0, 0x1000);
118 memcpy(buf+0x8e0, trd_8e0, 0x20);
119 int res = flpFormatTRDTrack(flp, 0, buf, sizeof(buf));
120 if (res == FLPERR_OK) flp->insert = 1;
121 return res;
126 //**************************************************************************
128 // TR-DOS utilities
130 //**************************************************************************
132 //==========================================================================
134 // flpCreateFile
136 //==========================================================================
137 int flpCreateFile (Floppy *flp, TRFile *dsc) {
138 if (flpDetectDiskType(flp) != FLP_DISK_TYPE_TRDOS) return FLPERR_SHIT;
139 uint8_t fbuf[0x100];
140 uint8_t files;
141 uint16_t freesec;
142 if (flpGetSectorData(flp, 0, 9, fbuf, 256) != FLPERR_OK) return FLPERR_SHIT;
143 dsc->sec = fbuf[0xe1];
144 dsc->trk = fbuf[0xe2];
145 files = fbuf[0xe4];
146 if (files > 127) return FLPERR_MANYFILES;
147 ++files;
148 fbuf[0xe4] = files;
149 freesec = fbuf[0xe5]+(fbuf[0xe6]<<8);
150 if (freesec < dsc->slen) return FLPERR_NOSPACE;
151 freesec -= dsc->slen;
152 fbuf[0xe5] = (freesec&0xff);
153 fbuf[0xe6] = ((freesec&0xff00)>>8);
154 fbuf[0xe1] += (dsc->slen&0x0f);
155 fbuf[0xe2] += ((dsc->slen&0xf0)>>4);
156 if (fbuf[0xe1] > 0x0f) {
157 fbuf[0xe1] -= 0x10;
158 ++fbuf[0xe2];
160 if (flpPutSectorData(flp, 0, 9, fbuf, 256) != FLPERR_OK) return FLPERR_SHIT;
161 freesec = ((files&0xf0)>>4)+1;
162 if (flpGetSectorData(flp, 0, freesec, fbuf, 256) != FLPERR_OK) return FLPERR_SHIT;
163 memmove(fbuf+(((files-1)&0x0f)<<4), dsc, 16);
164 flpPutSectorData(flp, 0, freesec, fbuf, 256);
165 flp->changed = 1;
166 return FLPERR_OK;
170 //==========================================================================
172 // flpGetCatalogEntry
174 //==========================================================================
175 int flpGetCatalogEntry (Floppy *flp, TRFile *dst, int num) {
176 uint8_t fbuf[0x100];
177 int sec, pos;
178 if (flpDetectDiskType(flp) != FLP_DISK_TYPE_TRDOS) return FLPERR_SHIT;
179 if (num < 0 || num > 127) return FLPERR_MANYFILES;
180 sec = ((num&0xf0)>>4); // sector
181 pos = ((num&0x0f)<<4); // file number inside sector
182 if (flpGetSectorData(flp, 0, sec+1, fbuf, 256) != FLPERR_OK) return FLPERR_NOSPACE;
183 if (dst != NULL) memmove(dst, fbuf+pos, 16);
184 return FLPERR_OK;
188 //==========================================================================
190 // flpGetTRCatalog
192 //==========================================================================
193 int flpGetTRCatalog (Floppy *flp, TRFile *dst) {
194 int cnt = 0;
195 if (flpDetectDiskType(flp) == FLP_DISK_TYPE_TRDOS) {
196 uint8_t *dpt = (uint8_t *)dst;
197 uint8_t fbuf[0x100];
198 for (int sc = 1; sc < 9; ++sc) {
199 if (flpGetSectorData(flp, 0, sc, fbuf, 256) != FLPERR_OK) break;
200 const uint8_t *ptr = fbuf;
201 unsigned fc;
202 for (fc = 0; fc < 16; ++fc) {
203 if (*ptr == 0) break;
204 if (dpt) {
205 memmove(dpt, ptr, 16);
206 dpt += 16;
208 ptr += 16;
209 ++cnt;
211 if (fc < 16) break;
214 return cnt;
219 //**************************************************************************
221 // 'boot' utilities
223 //**************************************************************************
225 //==========================================================================
227 // flpHasBoot
229 //==========================================================================
230 int flpHasBoot (Floppy *flp) {
231 if (flp != NULL && flpDetectDiskType(flp) == FLP_DISK_TYPE_TRDOS) {
232 TRFile cat[128];
233 int catSize = flpGetTRCatalog(flp, cat);
234 for (int i = 0; i < catSize; ++i) {
235 if (memcmp(cat[i].name, "boot B", 9) == 0) return i;
238 return FLPERR_SHIT;
242 //==========================================================================
244 // flpSetBoot
246 //==========================================================================
247 int flpSetBoot (Floppy *flp, FILE *fl, int replace) {
248 if (flp != NULL && flpDetectDiskType(flp) == FLP_DISK_TYPE_TRDOS) {
249 int idx = flpHasBoot(flp);
250 if (idx >= 0) {
251 if (replace) return FLPERR_SHIT; //TODO
252 return FLPERR_OK;
254 return dskLoadHoBeta(flp, fl);
256 return FLPERR_SHIT;
260 //==========================================================================
262 // flpFindFirstBasic
264 //==========================================================================
265 int flpFindFirstBasic (Floppy *flp, TRFile *dst, int ffirst) {
266 if (flp == NULL || flpDetectDiskType(flp) != FLP_DISK_TYPE_TRDOS) return FLPERR_SHIT;
267 if (!flp->insert) return FLPERR_SHIT;
268 if (ffirst < 0) ffirst = 0;
269 TRFile fcb;
270 for (; ffirst < FLP_TRDOS_CATALOG_MAX; ++ffirst) {
271 if (flpGetCatalogEntry(flp, &fcb, ffirst) != 0) break;
272 if (fcb.name[0] == 0) break; // end of directory
273 if (fcb.name[0] == 1) continue; // deleted
274 if (fcb.ext != 'B') continue;
275 int nameok = 1;
276 int seennspc = 0;
277 for (unsigned f = 0; f < 8; ++f) {
278 const uint8_t ch = (unsigned)(fcb.name[f]&0xffU);
279 if (ch < 32 || ch == '"') { nameok = 0; break; }
280 if (ch != 32) seennspc = 1;
282 if (!nameok || !seennspc) continue;
283 if (dst) memcpy(dst, &fcb, sizeof(TRFile));
284 return ffirst;
286 return FLPERR_SHIT;
290 //==========================================================================
292 // flpHasAnyNonBootBasic
294 //==========================================================================
295 int flpHasAnyNonBootBasic (Floppy *flp, TRFile *dst) {
296 if (flp == NULL || flpDetectDiskType(flp) != FLP_DISK_TYPE_TRDOS) return 0;
297 if (!flp->insert) return 0;
298 // check if we have any non-boot basic file
299 TRFile fcb;
300 int fidx = 0;
301 for (;;) {
302 fidx = flpFindFirstBasic(flp, &fcb, fidx);
303 if (fidx < 0) break;
304 if (memcmp(fcb.name, "boot ", 8) == 0) { ++fidx; continue; }
305 if (dst) memcpy(dst, &fcb, sizeof(TRFile));
306 return 1;
308 return 0;
312 //==========================================================================
314 // flpSetBootSimple
316 //==========================================================================
317 int flpSetBootSimple (Floppy *flp) {
318 if (flp == NULL || flpDetectDiskType(flp) != FLP_DISK_TYPE_TRDOS) return FLPERR_SHIT;
319 if (!flp->insert) return FLPERR_SHIT;
320 // check if we have any non-boot basic file
321 TRFile fcb;
322 int fidx = 0;
323 int seenBasic = 0;
324 for (;;) {
325 fidx = flpFindFirstBasic(flp, &fcb, fidx);
326 if (fidx < 0) break;
327 if (memcmp(fcb.name, "boot ", 8) == 0) return FLPERR_SHIT; // nothing to do
328 if (seenBasic) return FLPERR_SHIT; // too many basic files
329 seenBasic = 1;
330 ++fidx;
332 if (!seenBasic) return FLPERR_SHIT;
334 // create simple autorun boot:
335 // 10 RANDOMIZE USR VAL "15619":REM LOAD "file"
337 uint8_t secBuf[256];
338 memset(secBuf, 0, sizeof(secBuf));
339 size_t scpos = 0;
340 // line number
341 secBuf[scpos++] = 0;
342 secBuf[scpos++] = 10;
343 // line size (will be fixed later)
344 secBuf[scpos++] = 0;
345 secBuf[scpos++] = 0;
346 // line data
347 secBuf[scpos++] = 0xf9U; // RANDOMIZE
348 secBuf[scpos++] = 0xc0U; // USR
349 secBuf[scpos++] = 0xb0U; // VAL
350 secBuf[scpos++] = '"';
351 secBuf[scpos++] = '1';
352 secBuf[scpos++] = '5';
353 secBuf[scpos++] = '6';
354 secBuf[scpos++] = '1';
355 secBuf[scpos++] = '9';
356 secBuf[scpos++] = '"';
357 secBuf[scpos++] = ':';
358 secBuf[scpos++] = 0xeaU; // REM
359 secBuf[scpos++] = ':';
360 secBuf[scpos++] = 0xefU; // LOAD
361 secBuf[scpos++] = '"';
362 // put name
363 size_t nlen = 8;
364 while (nlen > 0 && fcb.name[nlen-1] == ' ') --nlen;
365 if (nlen == 0) return FLPERR_SHIT; // just in case
366 memcpy(secBuf+scpos, fcb.name, nlen); scpos += nlen;
367 secBuf[scpos++] = '"';
368 // line end
369 secBuf[scpos++] = 13;
370 // fix line size
371 secBuf[2] = (scpos-4)&0xffU;
372 secBuf[3] = ((scpos-4)>>8)&0xffU;
373 // TR-DOS signature
374 secBuf[scpos++] = 0x80;
375 secBuf[scpos++] = 0xaa;
376 // start line
377 secBuf[scpos++] = 10;
378 secBuf[scpos++] = 0;
380 memcpy(fcb.name, "boot ", 8);
381 fcb.ext = 'B';
382 // last 4 bytes are not in length
383 fcb.lst = (scpos-4)&0xff;
384 fcb.hst = ((scpos-4)>>8)&0xff;
385 fcb.llen = (scpos-4)&0xff;
386 fcb.hlen = ((scpos-4)>>8)&0xff;
387 fcb.slen = 1; // one sector
388 fcb.sec = fcb.trk = 0; // will be set in `flpCreateFile()`
390 if (flpCreateFile(flp, &fcb) != FLPERR_OK) return FLPERR_SHIT;
391 if (flpPutSectorData(flp, fcb.trk, fcb.sec+1, secBuf, 256) != FLPERR_OK) return FLPERR_SHIT;
393 flp->changed = 1;
394 return FLPERR_OK;
398 //==========================================================================
400 // flpRemoveBoot
402 // TODO
404 //==========================================================================
405 int flpRemoveBoot (Floppy *flp) {
406 return FLPERR_SHIT; //TODO