home *** CD-ROM | disk | FTP | other *** search
/ The AGA Experience 2 / agavol2.iso / software / utilities / emulation / frodo / src / 1541d64.c next >
C/C++ Source or Header  |  1996-01-29  |  21KB  |  965 lines

  1. /*
  2.  *  1541d64.c - 1541-Emulation in .d64-Datei
  3.  *
  4.  *  Copyright (C) 1994-1996 by Christian Bauer
  5.  */
  6.  
  7. /*
  8.  *  Anmerkungen:
  9.  *  ------------
  10.  *
  11.  *  Routinen:
  12.  *   - Die Schnittstelle zu den IEC-Routinen besteht in den Routinen
  13.  *     D64_Init, D64_Exit, D64_Open, D64_Close, D64_Read und D64_Write:
  14.  *       D64_Init bereitet die Emulation vor
  15.  *       D64_Exit beendet die Emulation
  16.  *       D64_Open öffnet einen Kanal
  17.  *       D64_Close schließt einen Kanal
  18.  *       D64_Read liest aus einem Kanal
  19.  *       D64_Write schreibt in einen Kanal
  20.  *
  21.  *  DriveData:
  22.  *   - lock enthält das FileHandle der .d64-Datei
  23.  *
  24.  *  Inkompatibilitäten/Verbesserungen:
  25.  *   - Nur Lesezugriffe möglich
  26.  *   - Keine Wildcards beim Directory-Lesen
  27.  *   - Nur 'I'-Befehl implementiert
  28.  */
  29.  
  30.  
  31. #include <exec/types.h>
  32. #include <exec/memory.h>
  33. #include <clib/exec_protos.h>
  34. #include <clib/dos_protos.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37.  
  38. #include "IEC.h"
  39. #include "1541d64.h"
  40. #include "Display.h"
  41. #define CATCOMP_NUMBERS 1
  42. #include "LocStrings.h"
  43.  
  44.  
  45. // Kanalmodi
  46. enum {
  47.   CHMOD_FREE,        // Kanal frei
  48.   CHMOD_COMMAND,    // Kommando-/Fehlerkanal
  49.   CHMOD_DIRECTORY,    // Directory wird gelesen
  50.   CHMOD_FILE,        // Sequentielle Datei ist geöffnet
  51.   CHMOD_DIRECT        // Direkter Pufferzugriff ('#')
  52. };
  53.  
  54. // Anzahl Tracks
  55. #define NUM_TRACKS    35
  56.  
  57.  
  58. // Aus Main.asm
  59. extern void ResetC64(void);
  60.  
  61.  
  62. // Prototypes
  63. int open_file(DriveData *drive, int channel, const char *filename);
  64. void convert_filename(const char *srcname, char *destname, int *filemode, int *filetype);
  65. BOOL find_file(DriveData *drive, const char *filename, int *track, int *sector);
  66. int open_file_ts(DriveData *drive, int channel, int track, int sector);
  67. int open_directory(DriveData *drive, const char *filename);
  68. int open_direct(DriveData *drive, int channel, const char *filename);
  69. void close_all_channels(DriveData *drive);
  70. void execute_command(DriveData *drive, const char *command);
  71. void block_read_cmd(DriveData *drive, char *command);
  72. void buffer_ptr_cmd(DriveData *drive, char *command);
  73. BOOL parse_bcmd(char *cmd, int *arg1, int *arg2, int *arg3, int *arg4);
  74. int alloc_buffer(DriveData *drive, int want);
  75. void free_buffer(DriveData *drive, int buf);
  76. BOOL read_sector(DriveData *drive, int track, int sector, char *buffer);
  77. int offset_from_ts(int track, int sector);
  78.  
  79.  
  80. /**
  81.  **  Emulation vorbereiten, .d64-Datei öffnen, prefs zeigt auf den
  82.  **    Preferences-String
  83.  **/
  84.  
  85. struct FileInfoBlock fib;
  86.  
  87. void D64_Init(DriveData *drive, char *prefs)
  88. {
  89.   BPTR lock;
  90.   int i;
  91.   ULONG magic;
  92.  
  93.   // Dateilänge prüfen
  94.   if (lock = Lock(prefs, ACCESS_READ)) {
  95.     Examine(lock, &fib);
  96.     UnLock(lock);
  97.  
  98.     if (fib.fib_Size < 174848)
  99.       return;
  100.   } else
  101.     return;
  102.  
  103.   if (drive->ram = malloc(2048)) {
  104.     drive->BAM = drive->ram + 0x700;
  105.  
  106.     if (drive->lock = Open(prefs, MODE_OLDFILE)) {
  107.  
  108.       // x64 Image?
  109.       Read(drive->lock, &magic, 4);
  110.       drive->image_header = (magic == 0x43154164 ? 64 : 0);
  111.  
  112.       // BAM lesen
  113.       read_sector(drive, 18, 0, (char *)drive->BAM);
  114.  
  115.       for (i=0; i<=14; i++) {
  116.         drive->chan_mode[i] = CHMOD_FREE;
  117.         drive->chan_buf[i] = NULL;
  118.       }
  119.  
  120.       drive->chan_mode[15] = CHMOD_COMMAND;
  121.       drive->cmd_length = 0;
  122.  
  123.       for (i=0; i<4; i++)
  124.         drive->buf_free[i] = TRUE;
  125.  
  126.       SetError(drive, ERR_STARTUP);
  127.     }
  128.   }
  129. }
  130.  
  131.  
  132. /**
  133.  **  Emulation beenden, .d64-Datei schließen
  134.  **/
  135.  
  136. void D64_Exit(DriveData *drive)
  137. {
  138.   if (drive->lock) {
  139.     close_all_channels(drive);
  140.  
  141.     Close(drive->lock);
  142.     drive->lock = NULL;
  143.   }
  144.  
  145.   if (drive->ram) {
  146.     free(drive->ram);
  147.     drive->ram = NULL;
  148.   }
  149. }
  150.  
  151.  
  152. /**
  153.  **  Kanal öffnen, filename ist Null-terminiert
  154.  **/
  155.  
  156. int D64_Open(DriveData *drive, int channel, char *filename)
  157. {
  158.   SetError(drive, ERR_OK);
  159.  
  160.   // Kanal 15: Dateiname als Befehl ausführen
  161.   if (channel == 15) {
  162.     execute_command(drive, filename);
  163.     return ST_OK;
  164.   }
  165.  
  166.   if (drive->chan_mode[channel] != CHMOD_FREE) {
  167.     SetError(drive, ERR_NOCHANNEL);
  168.     return ST_OK;
  169.   }
  170.  
  171.   if (filename[0] == '$')
  172.     if (channel)
  173.       return open_file_ts(drive, channel, 18, 0);
  174.     else
  175.       return open_directory(drive, filename);
  176.  
  177.   if (filename[0] == '#')
  178.     return open_direct(drive, channel, filename);
  179.  
  180.   return open_file(drive, channel, filename);
  181. }
  182.  
  183.  
  184. /*
  185.  *  Datei wird geöffnet
  186.  */
  187.  
  188. // Zugriffsmodi
  189. enum {
  190.   FMODE_READ, FMODE_WRITE, FMODE_APPEND
  191. };
  192.  
  193. // Dateitypen
  194. enum {
  195.   FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL
  196. };
  197.  
  198. int open_file(DriveData *drive, int channel, const char *filename)
  199. {
  200.   char plainname[256];
  201.   int filemode = FMODE_READ;
  202.   int filetype = FTYPE_PRG;
  203.   int track, sector;
  204.  
  205.   convert_filename(filename, plainname, &filemode, &filetype);
  206.  
  207.   // Bei Kanal 0 immer PRG lesen, bei Kanal 1 immer PRG schreiben
  208.   if (!channel) {
  209.     filemode = FMODE_READ;
  210.     filetype = FTYPE_PRG;
  211.   }
  212.   if (channel == 1) {
  213.     filemode = FMODE_WRITE;
  214.     filetype = FTYPE_PRG;
  215.   }
  216.  
  217.   // Nur Lesezugriffe erlaubt
  218.   if (filemode != FMODE_READ) {
  219.     SetError(drive, ERR_WRITEPROTECT);
  220.     return ST_OK;
  221.   }
  222.  
  223.   // Datei im Directory suchen und öffnen
  224.   if (find_file(drive, plainname, &track, §or))
  225.     return open_file_ts(drive, channel, track, sector);
  226.   else
  227.     SetError(drive, ERR_FILENOTFOUND);
  228.  
  229.   return ST_OK;
  230. }
  231.  
  232.  
  233. /*
  234.  *  Dateibezeichnung analysieren, Dateimodus und -typ ermitteln
  235.  */
  236.  
  237. void convert_filename(const char *srcname, char *destname, int *filemode, int *filetype)
  238. {
  239.   char *p, *q;
  240.   int i;
  241.  
  242.   // Nach ':' suchen, p zeigt auf erstes Zeichen hinter dem ':'
  243.   if (p = strchr(srcname, ':'))
  244.     p++;
  245.   else
  246.     p = srcname;
  247.  
  248.   // Reststring -> destname
  249.   strncpy(destname, p, NAMEBUF_LENGTH);
  250.  
  251.   // Nach ',' suchen
  252.   p = destname;
  253.   while (*p && (*p != ',')) p++;
  254.  
  255.   // Nach Modusparametern, getrennt durch ',' suchen
  256.   p = destname;
  257.   while (p = strchr(p, ',')) {
  258.  
  259.     // String hinter dem ersten ',' abschneiden
  260.     *p++ = 0;
  261.  
  262.     switch (*p) {
  263.       case 'P':
  264.         *filetype = FTYPE_PRG;
  265.         break;
  266.       case 'S':
  267.         *filetype = FTYPE_SEQ;
  268.         break;
  269.       case 'U':
  270.         *filetype = FTYPE_USR;
  271.         break;
  272.       case 'L':
  273.         *filetype = FTYPE_REL;
  274.         break;
  275.       case 'R':
  276.         *filemode = FMODE_READ;
  277.         break;
  278.       case 'W':
  279.         *filemode = FMODE_WRITE;
  280.         break;
  281.       case 'A':
  282.         *filemode = FMODE_APPEND;
  283.         break;
  284.     }
  285.   }
  286. }
  287.  
  288.  
  289. /*
  290.  *  Datei im Directory suchen, ersten Track und Sektor ermitteln
  291.  *  FALSE=nicht gefunden, TRUE=gefunden
  292.  */
  293.  
  294. BOOL find_file(DriveData *drive, const char *filename, int *track, int *sector)
  295. {
  296.   int i, j;
  297.   UBYTE *p, *q;
  298.   DirEntry *dir;
  299.  
  300.   // Alle Directory-Blöcke scannen
  301.   drive->dir.next_track = drive->BAM->dir_track;
  302.   drive->dir.next_sector = drive->BAM->dir_sector;
  303.  
  304.   while (drive->dir.next_track) {
  305.     if (!read_sector(drive, drive->dir.next_track, drive->dir.next_sector,
  306.                      (char *) &drive->dir))
  307.       return FALSE;
  308.  
  309.     // Alle 8 Einträge eines Blocks scannen
  310.     for (j=0; j<8; j++) {
  311.       dir = &drive->dir.entry[j];
  312.       *track = dir->track;
  313.       *sector = dir->sector;
  314.  
  315.       if (dir->type) {
  316.         p = filename;
  317.         q = dir->name;
  318.         for (i=0; i<16 && *p; i++, p++, q++) {
  319.           if (*p == '*')
  320.             return TRUE;
  321.           if (*p != *q) {
  322.             if (*p != '?') goto next;
  323.             if (*q == 0xa0) goto next;
  324.           }
  325.         }
  326.  
  327.         if (i == 16 || *q == 0xa0)
  328.           return TRUE;
  329.       }
  330.       next:
  331.     }
  332.   }
  333.  
  334.   return FALSE;
  335. }
  336.  
  337.  
  338. /*
  339.  *  Datei öffnen, Track und Sektor des ersten Blocks gegeben
  340.  */
  341. int open_file_ts(DriveData *drive, int channel, int track, int sector)
  342. {
  343.   if (drive->chan_buf[channel] = malloc(256)) {
  344.     drive->chan_mode[channel] = CHMOD_FILE;
  345.  
  346.     // Beim nächsten D64_Read-Aufruf wird der erste Block gelesen
  347.     drive->chan_buf[channel][0] = track;
  348.     drive->chan_buf[channel][1] = sector;
  349.     drive->buf_len[channel] = 0;
  350.   }
  351.  
  352.   return ST_OK;
  353. }
  354.  
  355.  
  356. /*
  357.  *  Directory als Basic-Programm vorbereiten (Kanal 0)
  358.  */
  359.  
  360. const char type_char_1[] = {'D', 'S', 'P', 'U', 'R'};
  361. const char type_char_2[] = {'E', 'E', 'R', 'S', 'E'};
  362. const char type_char_3[] = {'L', 'Q', 'G', 'R', 'L'};
  363.  
  364. int open_directory(DriveData *drive, const char *filename)
  365. {
  366.   int i, j, n, m;
  367.   char *p, *q;
  368.   DirEntry *dir;
  369.   UBYTE c;
  370.  
  371.   if (p = drive->buf_ptr[0] = drive->chan_buf[0] = malloc(8192)) {
  372.     drive->chan_mode[0] = CHMOD_DIRECTORY;
  373.  
  374.     // Directory-Titel erzeugen
  375.     *p++ = 0x01;    // Ladeadresse $0401 (aus PET-Zeiten :-)
  376.     *p++ = 0x04;
  377.     *p++ = 0x01;    // Dummy-Verkettung
  378.     *p++ = 0x01;
  379.     *p++ = 0;        // Laufwerksnummer (0) als Zeilennummer
  380.     *p++ = 0;
  381.     *p++ = 0x12;    // RVS ON
  382.     *p++ = '\"';
  383.  
  384.     q = drive->BAM->disk_name;
  385.     for (i=0; i<23; i++) {
  386.       if ((c = *q++) == 0xa0)
  387.         *p++ = ' ';        // 0xa0 durch Leerzeichen ersetzen
  388.       else
  389.         *p++ = c;
  390.     }
  391.     *(p-7) = '\"';
  392.     *p++ = 0;
  393.  
  394.     // Alle Directory-Blöcke scannen
  395.     drive->dir.next_track = drive->BAM->dir_track;
  396.     drive->dir.next_sector = drive->BAM->dir_sector;
  397.  
  398.     while (drive->dir.next_track) {
  399.       if (!read_sector(drive, drive->dir.next_track, drive->dir.next_sector,
  400.                        (char *) &drive->dir))
  401.         return ST_OK;
  402.  
  403.       // Alle 8 Einträge eines Blocks scannen
  404.       for (j=0; j<8; j++) {
  405.         dir = &drive->dir.entry[j];
  406.  
  407.         if (dir->type) {
  408.           *p++ = 0x01;    // Dummy-Verkettung
  409.           *p++ = 0x01;
  410.  
  411.           *p++ = dir->num_blocks_l;        // Zeilennummer
  412.           *p++ = dir->num_blocks_h;
  413.  
  414.           *p++ = ' ';
  415.           n = (dir->num_blocks_h << 8) + dir->num_blocks_l;
  416.           if (n<10) *p++ = ' ';
  417.           if (n<100) *p++ = ' ';
  418.  
  419.           *p++ = '\"';
  420.           q = dir->name;
  421.           m = 0;
  422.           for (i=0; i<16; i++) {
  423.             if ((c = *q++) == 0xa0) {
  424.               if (m)
  425.                 *p++ = ' ';        // Alle 0xa0 durch Leerzeichen ersetzen
  426.               else
  427.                 m = *p++ = '\"';    // Aber das erste durch einen '"'
  428.             } else
  429.               *p++ = c;
  430.           }
  431.           if (m)
  432.             *p++ = ' ';
  433.           else
  434.             *p++ = '\"';            // Kein 0xa0, dann ein Leerzeichen anhängen
  435.  
  436.           if (dir->type & 0x80)
  437.             *p++ = ' ';
  438.           else
  439.             *p++ = '*';
  440.  
  441.           *p++ = type_char_1[dir->type & 0x0f];
  442.           *p++ = type_char_2[dir->type & 0x0f];
  443.           *p++ = type_char_3[dir->type & 0x0f];
  444.  
  445.           if (dir->type & 0x40)
  446.             *p++ = '<';
  447.           else
  448.             *p++ = ' ';
  449.  
  450.           *p++ = ' ';
  451.           if (n >= 10) *p++ = ' ';
  452.           if (n >= 100) *p++ = ' ';
  453.           *p++ = 0;
  454.         }
  455.       }
  456.     }
  457.  
  458.     // Abschlußzeile
  459.     q = p;
  460.     for (i=0; i<29; i++)
  461.       *q++ = ' ';
  462.  
  463.     n = 0;
  464.     for (i=0; i<35; i++)
  465.       n += drive->BAM->bitmap[i*4];
  466.  
  467.     *p++ = 0x01;        // Dummy-Verkettung
  468.     *p++ = 0x01;
  469.     *p++ = n & 0xff;    // Anzahl freier Blöcke als Zeilennummer
  470.     *p++ = (n >> 8) & 0xff;
  471.  
  472.     *p++ = 'B';
  473.     *p++ = 'L';
  474.     *p++ = 'O';
  475.     *p++ = 'C';
  476.     *p++ = 'K';
  477.     *p++ = 'S';
  478.     *p++ = ' ';
  479.     *p++ = 'F';
  480.     *p++ = 'R';
  481.     *p++ = 'E';
  482.     *p++ = 'E';
  483.     *p++ = '.';
  484.  
  485.     p = q;
  486.     *p++ = 0;
  487.     *p++ = 0;
  488.     *p++ = 0;
  489.  
  490.     drive->buf_len[0] = p - drive->chan_buf[0];
  491.   }
  492.  
  493.   return ST_OK;
  494. }
  495.  
  496.  
  497. /*
  498.  *  Kanal für direkten Pufferzugriff öffnen
  499.  */
  500.  
  501. int open_direct(DriveData *drive, int channel, const char *filename)
  502. {
  503.   int buf = -1;
  504.  
  505.   if (filename[1] == 0)
  506.     buf = alloc_buffer(drive, -1);
  507.   else
  508.     if ((filename[1] >= '0') && (filename[1] <= '3') && (filename[2] == 0))
  509.       buf = alloc_buffer(drive, filename[1] - '0');
  510.  
  511.   if (buf == -1) {
  512.     SetError(drive, ERR_NOCHANNEL);
  513.     return ST_OK;
  514.   }
  515.  
  516.   // Die Puffer liegen im 1541-RAM ab $300 und belegen je 256 Byte
  517.   drive->chan_buf[channel] = drive->buf_ptr[channel] = drive->ram + 0x300 + (buf << 8);
  518.   drive->chan_mode[channel] = CHMOD_DIRECT;
  519.   drive->chan_buf_num[channel] = buf;
  520.  
  521.   // Tatsächliche Puffernummer im Puffer ablegen
  522.   *drive->chan_buf[channel] = buf + '0';
  523.   drive->buf_len[channel] = 1;
  524.  
  525.   return ST_OK;
  526. }
  527.  
  528.  
  529. /**
  530.  **  Kanal schließen
  531.  **/
  532.  
  533. int D64_Close(DriveData *drive, int channel)
  534. {
  535.   if (channel==15) {
  536.     close_all_channels(drive);
  537.     return ST_OK;
  538.   }
  539.  
  540.   switch (drive->chan_mode[channel]) {
  541.     case CHMOD_FREE:
  542.       break;
  543.  
  544.     case CHMOD_DIRECT:
  545.       free_buffer(drive, drive->chan_buf_num[channel]);
  546.       drive->chan_buf[channel] = NULL;
  547.       drive->chan_mode[channel] = CHMOD_FREE;
  548.       break;
  549.  
  550.     default:
  551.       free(drive->chan_buf[channel]);
  552.       drive->chan_buf[channel] = NULL;
  553.       drive->chan_mode[channel] = CHMOD_FREE;
  554.       break;
  555.   }
  556.  
  557.   return ST_OK;
  558. }
  559.  
  560.  
  561. /*
  562.  *  Alle Kanäle schließen
  563.  */
  564.  
  565. void close_all_channels(DriveData *drive)
  566. {
  567.   int i;
  568.  
  569.   for (i=0; i<15; i++) D64_Close(drive, i);
  570.  
  571.   drive->cmd_length = 0;
  572. }
  573.  
  574.  
  575. /**
  576.  **  Ein Byte aus Kanal lesen
  577.  **/
  578.  
  579. int D64_Read(DriveData *drive, int channel, char *data)
  580. {
  581.   switch (drive->chan_mode[channel]) {
  582.     case CHMOD_FREE:
  583.       return ST_READ_TIMEOUT;
  584.       break;
  585.  
  586.     case CHMOD_COMMAND:
  587.       *data = *drive->error_ptr++;
  588.       if (--drive->error_length)
  589.         return ST_OK;
  590.       else {
  591.         SetError(drive, ERR_OK);
  592.         return ST_EOF;
  593.       }
  594.       break;
  595.  
  596.     case CHMOD_FILE:
  597.       // Nächsten Block lesen, wenn notwendig
  598.       if (drive->chan_buf[channel][0] && !drive->buf_len[channel]) {
  599.         if (!read_sector(drive, drive->chan_buf[channel][0],
  600.                          drive->chan_buf[channel][1], drive->chan_buf[channel]))
  601.           return ST_READ_TIMEOUT;
  602.         drive->buf_ptr[channel] = drive->chan_buf[channel] + 2;
  603.  
  604.         // Blocklänge ermitteln
  605.         drive->buf_len[channel] = drive->chan_buf[channel][0] ? 254 : (UBYTE)drive->chan_buf[channel][1];
  606.       }
  607.  
  608.       if (drive->buf_len[channel] > 0) {
  609.         *data = *drive->buf_ptr[channel]++;
  610.         if (!--drive->buf_len[channel] && !drive->chan_buf[channel][0])
  611.           return ST_EOF;
  612.         else
  613.           return ST_OK;
  614.       } else
  615.         return ST_READ_TIMEOUT;
  616.       break;
  617.  
  618.     case CHMOD_DIRECTORY:
  619.     case CHMOD_DIRECT:
  620.       if (drive->buf_len[channel] > 0) {
  621.         *data = *drive->buf_ptr[channel]++;
  622.         if (--drive->buf_len[channel])
  623.           return ST_OK;
  624.         else
  625.           return ST_EOF;
  626.       } else
  627.         return ST_READ_TIMEOUT;
  628.       break;
  629.   }
  630. }
  631.  
  632.  
  633. /**
  634.  **  Ein Byte in Kanal schreiben
  635.  **/
  636.  
  637. int D64_Write(DriveData *drive, int channel, char data, char eof)
  638. {
  639.   // Kanal 15: Zeichen sammeln und bei EOF Befehl ausführen
  640.   if (channel == 15) {
  641.     if (drive->cmd_length >= 40)
  642.       return ST_TIMEOUT;
  643.  
  644.     drive->cmd_buffer[drive->cmd_length++] = data;
  645.  
  646.     if (eof < 0) {
  647.       drive->cmd_buffer[drive->cmd_length++] = 0;
  648.       drive->cmd_length = 0;
  649.       execute_command(drive, drive->cmd_buffer);
  650.     }
  651.     return ST_OK;
  652.   }
  653.  
  654.   if (drive->chan_mode[channel] == CHMOD_FREE)
  655.     SetError(drive, ERR_FILENOTOPEN);
  656.  
  657.   if (drive->chan_mode[channel] == CHMOD_DIRECTORY)
  658.     SetError(drive, ERR_WRITEFILEOPEN);
  659.  
  660.   return ST_TIMEOUT;
  661. }
  662.  
  663.  
  664. /*
  665.  *  Befehlsstring ausführen
  666.  */
  667.  
  668. void execute_command(DriveData *drive, const char *command)
  669. {
  670.   UWORD adr;
  671.   APTR args;
  672.  
  673.   switch (command[0]) {
  674.     case 'B':
  675.       if (command[1] != '-') {
  676.         SetError(drive, ERR_SYNTAX30);
  677.       } else {
  678.         switch (command[2]) {
  679.           case 'R':
  680.             block_read_cmd(drive, &command[3]);
  681.             break;
  682.  
  683.           case 'P':
  684.             buffer_ptr_cmd(drive, &command[3]);
  685.             break;
  686.  
  687.           case 'A':
  688.           case 'F':
  689.           case 'W':
  690.             SetError(drive, ERR_WRITEPROTECT);
  691.             break;
  692.  
  693.           case 'E':
  694.             args = "B-E";
  695.             if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
  696.               ResetC64();
  697.             SetError(drive, ERR_SYNTAX30);
  698.             break;
  699.  
  700.           default:
  701.             SetError(drive, ERR_SYNTAX30);
  702.             break;
  703.         }
  704.       }
  705.       break;
  706.  
  707.     case 'M':
  708.       if (command[1] != '-') {
  709.         SetError(drive, ERR_SYNTAX30);
  710.       } else {
  711.         switch (command[2]) {
  712.           case 'R':
  713.             adr = ((UBYTE)command[4] << 8) | ((UBYTE)command[3]);
  714.             drive->error_ptr = drive->ram + (adr & 0x07ff);
  715.             if (!(drive->error_length = (UBYTE)command[5]))
  716.               drive->error_length = 1;
  717.             break;
  718.  
  719.           case 'E':
  720.             args = "M-E";
  721.             if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
  722.               ResetC64();
  723.             SetError(drive, ERR_SYNTAX30);
  724.             break;
  725.  
  726.           case 'W':
  727.             args = "M-W";
  728.             if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
  729.               ResetC64();
  730.             SetError(drive, ERR_SYNTAX30);
  731.             break;
  732.  
  733.           default:
  734.             SetError(drive, ERR_SYNTAX30);
  735.             break;
  736.         }
  737.       }
  738.       break;
  739.  
  740.     case 'I':
  741.       close_all_channels(drive);
  742.       read_sector(drive, 18, 0, (char *)drive->BAM);
  743.       SetError(drive, ERR_OK);
  744.       break;
  745.  
  746.     case 'U':
  747.       switch (command[1] & 0x0f) {
  748.         case 1:        // U1/UA: Block-Read
  749.           block_read_cmd(drive, &command[2]);
  750.           break;
  751.  
  752.         case 2:        // U2/UB: Block-Write
  753.           SetError(drive, ERR_WRITEPROTECT);
  754.           break;
  755.  
  756.         case 10:    // U:/UJ: Reset
  757.           close_all_channels(drive);
  758.           read_sector(drive, 18, 0, (char *)drive->BAM);
  759.           SetError(drive, ERR_STARTUP);
  760.           break;
  761.  
  762.         default:
  763.           SetError(drive, ERR_SYNTAX30);
  764.           break;
  765.       }
  766.       break;
  767.  
  768.     case 'C':
  769.     case 'N':
  770.     case 'R':
  771.     case 'S':
  772.     case 'V':
  773.       SetError(drive, ERR_WRITEPROTECT);
  774.       break;
  775.  
  776.     default:
  777.       SetError(drive, ERR_SYNTAX30);
  778.       break;
  779.   }
  780. }
  781.  
  782.  
  783. /*
  784.  *  B-R-Befehl ausführen
  785.  */
  786.  
  787. void block_read_cmd(DriveData *drive, char *command)
  788. {
  789.   int channel, drvnum, track, sector;
  790.  
  791.   if (parse_bcmd(command, &channel, &drvnum, &track, §or)) {
  792.     if (drive->chan_mode[channel] == CHMOD_DIRECT) {
  793.       read_sector(drive, track, sector, drive->buf_ptr[channel] = drive->chan_buf[channel]);
  794.       drive->buf_len[channel] = 256;
  795.       SetError(drive, ERR_OK);
  796.     } else
  797.       SetError(drive, ERR_NOCHANNEL);
  798.   } else
  799.     SetError(drive, ERR_SYNTAX30);
  800. }
  801.  
  802.  
  803. /*
  804.  *  B-P-Befehl ausführen
  805.  */
  806.  
  807. void buffer_ptr_cmd(DriveData *drive, char *command)
  808. {
  809.   int channel, pointer, i;
  810.  
  811.   if (parse_bcmd(command, &channel, &pointer, &i, &i)) {
  812.     if (drive->chan_mode[channel] == CHMOD_DIRECT) {
  813.       drive->buf_ptr[channel] = drive->chan_buf[channel] + pointer;
  814.       drive->buf_len[channel] = 256 - pointer;
  815.       SetError(drive, ERR_OK);
  816.     } else
  817.       SetError(drive, ERR_NOCHANNEL);
  818.   } else
  819.     SetError(drive, ERR_SYNTAX30);
  820. }
  821.  
  822.  
  823. /*
  824.  *  Parameter der Block-Befehle auswerten
  825.  *  TRUE: OK, FALSE: Fehler
  826.  */
  827.  
  828. BOOL parse_bcmd(char *cmd, int *arg1, int *arg2, int *arg3, int *arg4)
  829. {
  830.   int i;
  831.  
  832.   if (*cmd == ':') cmd++;
  833.  
  834.   // Vier durch Leerzeichen, Cursor Right oder Komma getrennte Parameter lesen
  835.   while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
  836.   if (!*cmd) return FALSE;
  837.  
  838.   i = 0;
  839.   while (*cmd >= 0x30 && *cmd < 0x40) {
  840.     i *= 10;
  841.     i += *cmd++ & 0x0f;
  842.   }
  843.   *arg1 = i & 0xff;
  844.  
  845.   while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
  846.   if (!*cmd) return TRUE;
  847.  
  848.   i = 0;
  849.   while (*cmd >= 0x30 && *cmd < 0x40) {
  850.     i *= 10;
  851.     i += *cmd++ & 0x0f;
  852.   }
  853.   *arg2 = i & 0xff;
  854.  
  855.   while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
  856.   if (!*cmd) return TRUE;
  857.  
  858.   i = 0;
  859.   while (*cmd >= 0x30 && *cmd < 0x40) {
  860.     i *= 10;
  861.     i += *cmd++ & 0x0f;
  862.   }
  863.   *arg3 = i & 0xff;
  864.  
  865.   while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
  866.  
  867.   i = 0;
  868.   while (*cmd >= 0x30 && *cmd < 0x40) {
  869.     i *= 10;
  870.     i += *cmd++ & 0x0f;
  871.   }
  872.   *arg4 = i & 0xff;
  873.  
  874.   return TRUE;
  875. }
  876.  
  877.  
  878. /*
  879.  *  Einen Floppy-Puffer belegen
  880.  *  -> Gewünschte Puffernummer oder -1
  881.  *  <- Belegte Puffernummer oder -1
  882.  */
  883.  
  884. int alloc_buffer(DriveData *drive, int want)
  885. {
  886.   if (want == -1) {
  887.     for (want=3; want>=0; want--)
  888.       if (drive->buf_free[want]) {
  889.         drive->buf_free[want] = FALSE;
  890.         return want;
  891.       }
  892.     return -1;
  893.   }
  894.  
  895.   if (want < 4)
  896.     if (drive->buf_free[want]) {
  897.       drive->buf_free[want] = FALSE;
  898.       return want;
  899.     } else
  900.       return -1;
  901.   else
  902.     return -1;
  903. }
  904.  
  905.  
  906. /*
  907.  *  Einen Floppy-Puffer freigeben
  908.  */
  909.  
  910. void free_buffer(DriveData *drive, int buf)
  911. {
  912.   drive->buf_free[buf] = TRUE;
  913. }
  914.  
  915.  
  916. /*
  917.  *  Einen Sektor lesen (256 Bytes)
  918.  *  TRUE: Gelungen, FALSE: Fehler
  919.  */
  920.  
  921. BOOL read_sector(DriveData *drive, int track, int sector, char *buffer)
  922. {
  923.   int offset;
  924.  
  925.   // Track/Sektor-Angabe in Byteoffset in der Datei umwandeln
  926.   if ((offset = offset_from_ts(track, sector)) < 0) {
  927.     SetError(drive, ERR_ILLEGALTS);
  928.     return FALSE;
  929.   }
  930.  
  931.   Seek(drive->lock, offset + drive->image_header, OFFSET_BEGINNING);
  932.   Read(drive->lock, buffer, 256);
  933.   return TRUE;
  934. }
  935.  
  936.  
  937. /*
  938.  *  Track/Sektor in Offset umrechnen
  939.  */
  940.  
  941. const int num_sectors[36] = {
  942.   0,
  943.   21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21
  944.   19,19,19,19,19,19,19,
  945.   18,18,18,18,18,18,
  946.   17,17,17,17,17
  947. };
  948.  
  949. const int sector_offset[36] = {
  950.   0,
  951.   0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336,
  952.   357,376,395,414,433,452,471,
  953.   490,508,526,544,562,580,
  954.   598,615,632,649,666
  955. };
  956.  
  957. int offset_from_ts(int track, int sector)
  958. {
  959.   if ((track < 1) || (track > NUM_TRACKS) ||
  960.       (sector < 0) || (sector >= num_sectors[track]))
  961.     return -1;
  962.  
  963.   return (sector_offset[track] + sector) << 8;
  964. }
  965.