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