OSDN Git Service

[General] Convert sourcecode's CRLF format: DOS(WINDOWS) to Unix, to apply patches...
[csp-qt/common_source_project-fm7.git] / source / src / vm / tf20.cpp
index 1119203..a6cb59c 100644 (file)
-/*\r
-       Skelton for retropc emulator\r
-\r
-       Author : Takeda.Toshiya\r
-       Date   : 2008.02.28 -\r
-\r
-       [ EPSON TF-20 ]\r
-*/\r
-\r
-#include "tf20.h"\r
-#include "disk.h"\r
-\r
-/*\r
-       This is based on vfloppy 1.4 by:\r
-\r
-       Justin Mitchell (madmitch@discordia.org.uk) and friends.\r
-       Fred Jan Kraan (fjkraan@xs4all.nl)\r
-*/\r
-\r
-#define PHASE_IDLE     0\r
-#define PHASE_SELECT   1\r
-#define PHASE_FUNC     2\r
-#define PHASE_HEAD     3\r
-#define PHASE_DATA     4\r
-#define PHASE_EXEC     5\r
-#define PHASE_RESULT   6\r
-#define PHASE_END      7\r
-\r
-#define DID_FIRST      0x31\r
-#define DID_SECOND     0x32\r
-#define DS_SEL         0x05\r
-\r
-#define NUL            0x00\r
-#define SOH            0x01\r
-#define STX            0x02\r
-#define ETX            0x03\r
-#define EOT            0x04\r
-#define ENQ            0x05\r
-#define ACK            0x06\r
-#define NAK            0x15\r
-#define US             0x31\r
-\r
-#define FNC_RESET_P    0x0d\r
-#define FNC_RESET_M    0x0e\r
-#define FNC_READ       0x77\r
-#define FNC_WRITE      0x78\r
-#define FNC_WRITEHST   0x79\r
-#define FNC_COPY       0x7a\r
-#define FNC_FORMAT     0x7c\r
-\r
-#define ERR_SUCCESS    0x00\r
-#define ERR_READ       0xfa\r
-#define ERR_WRITE      0xfb\r
-#define ERR_DRIVE      0xfc\r
-#define ERR_PROTECTED  0xfd\r
-#define ERR_UNKNOWN    0xfe\r
-\r
-void TF20::initialize()\r
-{\r
-       // initialize d88 handler\r
-       for(int i = 0; i < MAX_DRIVE; i++) {\r
-               disk[i] = new DISK();\r
-       }\r
-}\r
-\r
-void TF20::release()\r
-{\r
-       for(int i = 0; i < MAX_DRIVE; i++) {\r
-               if(disk[i]) {\r
-                       delete disk[i];\r
-               }\r
-       }\r
-}\r
-\r
-void TF20::reset()\r
-{\r
-       phase = PHASE_IDLE;\r
-       buflen = 0;\r
-}\r
-\r
-#define REPLY(val) d_sio->write_signal(did_sio, val, 0xff)\r
-\r
-void TF20::write_signal(int id, uint32 data, uint32 mask)\r
-{\r
-       switch(phase) {\r
-       case PHASE_IDLE:\r
-               if(data == EOT) {\r
-                       break;\r
-               }\r
-               if(data == ENQ) {\r
-                       REPLY(ACK);\r
-                       break;\r
-               }\r
-               if(data != DID_FIRST) {\r
-                       REPLY(NAK);\r
-                       break;\r
-               }\r
-               phase = PHASE_SELECT;\r
-               buflen = 0;\r
-               break;\r
-               \r
-       case PHASE_SELECT:\r
-               bufr[buflen++] = data;\r
-               if(buflen < 3) {\r
-                       break;\r
-               }\r
-               if(bufr[0] != DID_FIRST && bufr[0] != DID_SECOND) {\r
-                       phase = PHASE_IDLE;\r
-                       break;\r
-               }\r
-               if(bufr[2] != DS_SEL) {\r
-                       REPLY(NAK);\r
-                       phase = PHASE_IDLE;\r
-                       break;\r
-               }\r
-               REPLY(ACK);\r
-               phase = PHASE_FUNC;\r
-               break;\r
-               \r
-       case PHASE_FUNC:\r
-               if(data == EOT) {\r
-                       phase = PHASE_IDLE;\r
-                       break;\r
-               }\r
-               if(data == ENQ) {\r
-                       REPLY(ACK);\r
-                       break;\r
-               }\r
-               if(data != SOH) {\r
-                       REPLY(NAK);\r
-                       phase = PHASE_IDLE;\r
-                       break;\r
-               }\r
-               phase = PHASE_HEAD;\r
-               buflen = 0;\r
-               break;\r
-               \r
-       case PHASE_HEAD:\r
-               bufr[buflen++] = data;\r
-               if(buflen < 6) {\r
-                       break;\r
-               }\r
-               REPLY(ACK);\r
-               phase = PHASE_DATA;\r
-               break;\r
-               \r
-       case PHASE_DATA:\r
-               bufr[buflen++] = data;\r
-               if(buflen < (6 + bufr[4] + 4)) {\r
-                       break;\r
-               }\r
-               if(bufr[6] != STX) {\r
-                       REPLY(NAK);\r
-                       phase = PHASE_IDLE;\r
-                       break;\r
-               }\r
-               REPLY(ACK);\r
-               phase = PHASE_EXEC;\r
-               break;\r
-               \r
-       case PHASE_EXEC:\r
-               if(data != EOT) {\r
-                       REPLY(NAK);\r
-                       phase = PHASE_IDLE;\r
-                       break;\r
-               }\r
-               if(!process_cmd()) {\r
-                       REPLY(NAK);\r
-                       phase = PHASE_IDLE;\r
-                       break;\r
-               }\r
-               for(int i = 0; i < 7; i++) {\r
-                       REPLY(bufs[i]);\r
-               }\r
-               phase = PHASE_RESULT;\r
-               break;\r
-               \r
-       case PHASE_RESULT:\r
-               if(data == ENQ) {\r
-                       REPLY(ACK);\r
-                       phase = PHASE_FUNC;\r
-                       break;\r
-               }\r
-               if(data != ACK) {\r
-                       phase = PHASE_FUNC;\r
-                       break;\r
-               }\r
-               for(int i = 7; i < buflen; i++) {\r
-                       REPLY(bufs[i]);\r
-               }\r
-               phase = PHASE_END;\r
-               break;\r
-               \r
-       case PHASE_END:\r
-               if(data == ENQ) {\r
-                       REPLY(ACK);\r
-               } else if(data == ACK) {\r
-                       REPLY(EOT);\r
-               }\r
-               phase = PHASE_FUNC;\r
-               break;\r
-       }\r
-}\r
-\r
-#define SET_HEAD(size) { \\r
-       bufs[0] = 1; \\r
-       bufs[1] = 1; \\r
-       bufs[2] = bufr[2]; \\r
-       bufs[3] = bufr[1]; \\r
-       bufs[4] = bufr[3]; \\r
-       bufs[5] = size; \\r
-       uint8 sum = 0; \\r
-       for(int s = 0; s < 6; s++) \\r
-               sum += bufs[s]; \\r
-       bufs[6] = 256 - sum; \\r
-       bufs[7] = 2; \\r
-       buflen = 8; \\r
-}\r
-#define SET_DATA(v) bufs[buflen++] = (v)\r
-#define SET_CODE(v) { \\r
-       bufs[buflen++] = (v); \\r
-       bufs[buflen++] = 3; \\r
-       uint8 sum = 0; \\r
-       for(int s = 7; s < buflen; s++) \\r
-               sum += bufs[s]; \\r
-       bufs[buflen++] = 256 - sum; \\r
-}\r
-\r
-bool TF20::process_cmd()\r
-{\r
-       int drv, trk, sec, dst;\r
-       uint8 *sctr, *sctw;\r
-       \r
-       switch(bufr[3]) {\r
-       case FNC_RESET_P:\r
-       case FNC_RESET_M:\r
-               SET_HEAD(0);\r
-               SET_CODE(ERR_SUCCESS);\r
-               return true;\r
-               \r
-       case FNC_READ:\r
-               drv = (bufr[1] == DID_FIRST) ? 0 : (bufr[1] == DID_SECOND) ? 2 : 4;\r
-               drv += bufr[7] - 1;\r
-               trk = bufr[8];\r
-               sec = bufr[9];\r
-               if(!disk_inserted(drv)) {\r
-                       // drive error\r
-                       SET_HEAD(0x80);\r
-                       for(int i = 0; i < 128; i++) {\r
-                               SET_DATA(0xff);\r
-                       }\r
-                       SET_CODE(ERR_DRIVE);\r
-                       return true;\r
-               }\r
-               if((sctr = get_sector(drv, trk, sec)) == NULL) {\r
-                       // read error\r
-                       SET_HEAD(0x80);\r
-                       for(int i = 0; i < 128; i++) {\r
-                               SET_DATA(0xff);\r
-                       }\r
-                       SET_CODE(ERR_READ);\r
-                       return true;\r
-               }\r
-               SET_HEAD(0x80);\r
-               for(int i = 0; i < 128; i++) {\r
-                       SET_DATA(sctr[i]);\r
-               }\r
-               SET_CODE(ERR_SUCCESS);\r
-               return true;\r
-               \r
-       case FNC_WRITE:\r
-               drv = (bufr[1] == DID_FIRST) ? 0 : (bufr[1] == DID_SECOND) ? 2 : 4;\r
-               drv += bufr[7] - 1;\r
-               trk = bufr[8];\r
-               sec = bufr[9];\r
-               if(!disk_inserted(drv)) {\r
-                       // drive error\r
-                       SET_HEAD(0);\r
-                       SET_CODE(ERR_DRIVE);\r
-                       return true;\r
-               }\r
-               if(disk_protected(drv)) {\r
-                       // write protect\r
-                       SET_HEAD(0);\r
-                       SET_CODE(ERR_PROTECTED);\r
-                       return true;\r
-               }\r
-               if((sctw = get_sector(drv, trk, sec)) == NULL) {\r
-                       // write error\r
-                       SET_HEAD(0);\r
-                       SET_CODE(ERR_WRITE);\r
-                       return true;\r
-               }\r
-               // dont care write type\r
-               for(int i = 0; i < 128; i++) {\r
-                       sctw[i] = bufr[11 + i];\r
-               }\r
-               SET_HEAD(0);\r
-               SET_CODE(ERR_SUCCESS);\r
-               return true;\r
-               \r
-       case FNC_WRITEHST:\r
-               SET_HEAD(0);\r
-               SET_CODE(ERR_SUCCESS);\r
-               return true;\r
-               \r
-       case FNC_COPY:\r
-               drv = (bufr[1] == DID_FIRST) ? 0 : (bufr[1] == DID_SECOND) ? 2 : 4;\r
-               drv += bufr[7] - 1;\r
-               dst = (drv & ~1) | (~drv & 1);\r
-               if(!disk_inserted(drv)) {\r
-                       // drive error\r
-                       SET_HEAD(0);\r
-                       SET_CODE(ERR_DRIVE);\r
-                       return true;\r
-               }\r
-               if(!disk_inserted(dst)) {\r
-                       // drive error\r
-                       SET_HEAD(0);\r
-                       SET_CODE(ERR_DRIVE);\r
-                       return true;\r
-               }\r
-               if(disk_protected(dst)) {\r
-                       // write protect\r
-                       SET_HEAD(0);\r
-                       SET_CODE(ERR_PROTECTED);\r
-                       return true;\r
-               }\r
-               for(trk = 0; trk < 40; trk++) {\r
-                       for(sec = 1; sec <= 64; sec++) {\r
-                               if((sctr = get_sector(drv, trk, sec)) == NULL) {\r
-                                       // read error\r
-                                       SET_HEAD(0);\r
-                                       SET_CODE(ERR_READ);\r
-                                       return true;\r
-                               }\r
-                               if((sctw = get_sector(dst, trk, sec)) == NULL) {\r
-                                       // write error\r
-                                       SET_HEAD(0);\r
-                                       SET_CODE(ERR_WRITE);\r
-                                       return true;\r
-                               }\r
-                               memcpy(sctw, sctr, 128);\r
-                       }\r
-                       SET_HEAD(2);\r
-                       SET_DATA(trk == 39 ? 0xff : 0);         // high-order\r
-                       SET_DATA(trk == 39 ? 0xff : trk);       // low-order\r
-                       SET_CODE(ERR_SUCCESS);\r
-               }\r
-               return true;\r
-               \r
-       case FNC_FORMAT:\r
-               drv = (bufr[1] == DID_FIRST) ? 0 : (bufr[1] == DID_SECOND) ? 2 : 4;\r
-               drv += bufr[7] - 1;\r
-               if(!disk_inserted(drv)) {\r
-                       // drive error\r
-                       SET_HEAD(0);\r
-                       SET_CODE(ERR_DRIVE);\r
-                       return true;\r
-               }\r
-               if(disk_protected(drv)) {\r
-                       // write protect\r
-                       SET_HEAD(0);\r
-                       SET_CODE(ERR_PROTECTED);\r
-                       return true;\r
-               }\r
-               for(trk = 0; trk < 40; trk++) {\r
-                       for(sec = 1; sec <= 64; sec++) {\r
-                               if((sctw = get_sector(drv, trk, sec)) == NULL) {\r
-                                       // write error\r
-                                       SET_HEAD(0);\r
-                                       SET_CODE(ERR_WRITE);\r
-                                       return true;\r
-                               }\r
-                               memset(sctw, 0xe5, 128);\r
-                       }\r
-                       SET_HEAD(2);\r
-                       SET_DATA(trk == 39 ? 0xff : 0);         // high-order\r
-                       SET_DATA(trk == 39 ? 0xff : trk);       // low-order\r
-                       SET_CODE(ERR_SUCCESS);\r
-               }\r
-               return true;\r
-       }\r
-       // unknown command\r
-       return false;\r
-}\r
-\r
-bool TF20::disk_protected(int drv)\r
-{\r
-       if(drv < MAX_DRIVE) {\r
-               return disk[drv]->write_protected;\r
-       }\r
-       return false;\r
-}\r
-\r
-uint8* TF20::get_sector(int drv, int trk, int sec)\r
-{\r
-       // logical : trk = 0-39, sec = 1-64, secsize = 128bytes\r
-       int total = trk * 64 + sec - 1;\r
-       int half = total & 1;\r
-       total >>= 1;\r
-       int phys_sec = total & 15;\r
-       total >>= 4;\r
-       int phys_side = total & 1;\r
-       int phys_trk = total >> 1;\r
-       \r
-       if(!disk_inserted(drv)) {\r
-               return NULL;\r
-       }\r
-       if(!disk[drv]->get_sector(phys_trk, phys_side, phys_sec)) {\r
-               return NULL;\r
-       }\r
-       return disk[drv]->sector + (half ? 128 : 0);\r
-}\r
-\r
-// ----------------------------------------------------------------------------\r
-// user interface\r
-// ----------------------------------------------------------------------------\r
-\r
-void TF20::open_disk(int drv, _TCHAR path[], int offset)\r
-{\r
-       if(drv < MAX_DRIVE) {\r
-               disk[drv]->open(path, offset);\r
-       }\r
-}\r
-\r
-void TF20::close_disk(int drv)\r
-{\r
-       if(drv < MAX_DRIVE) {\r
-               disk[drv]->close();\r
-       }\r
-}\r
-\r
-bool TF20::disk_inserted(int drv)\r
-{\r
-       if(drv < MAX_DRIVE) {\r
-               return disk[drv]->inserted;\r
-       }\r
-       return false;\r
-}\r
-\r
+/*
+       Skelton for retropc emulator
+
+       Author : Takeda.Toshiya
+       Date   : 2008.02.28 -
+
+       [ EPSON TF-20 ]
+*/
+
+#include "tf20.h"
+#include "disk.h"
+
+/*
+       This is based on vfloppy 1.4 by:
+
+       Justin Mitchell (madmitch@discordia.org.uk) and friends.
+       Fred Jan Kraan (fjkraan@xs4all.nl)
+*/
+
+#define PHASE_IDLE     0
+#define PHASE_SELECT   1
+#define PHASE_FUNC     2
+#define PHASE_HEAD     3
+#define PHASE_DATA     4
+#define PHASE_EXEC     5
+#define PHASE_RESULT   6
+#define PHASE_END      7
+
+#define DID_FIRST      0x31
+#define DID_SECOND     0x32
+#define DS_SEL         0x05
+
+#define NUL            0x00
+#define SOH            0x01
+#define STX            0x02
+#define ETX            0x03
+#define EOT            0x04
+#define ENQ            0x05
+#define ACK            0x06
+#define NAK            0x15
+#define US             0x31
+
+#define FNC_RESET_P    0x0d
+#define FNC_RESET_M    0x0e
+#define FNC_READ       0x77
+#define FNC_WRITE      0x78
+#define FNC_WRITEHST   0x79
+#define FNC_COPY       0x7a
+#define FNC_FORMAT     0x7c
+
+#define ERR_SUCCESS    0x00
+#define ERR_READ       0xfa
+#define ERR_WRITE      0xfb
+#define ERR_DRIVE      0xfc
+#define ERR_PROTECTED  0xfd
+#define ERR_UNKNOWN    0xfe
+
+void TF20::initialize()
+{
+       // initialize d88 handler
+       for(int i = 0; i < MAX_DRIVE; i++) {
+               disk[i] = new DISK();
+       }
+}
+
+void TF20::release()
+{
+       for(int i = 0; i < MAX_DRIVE; i++) {
+               if(disk[i]) {
+                       disk[i]->close();
+                       delete disk[i];
+               }
+       }
+}
+
+void TF20::reset()
+{
+       phase = PHASE_IDLE;
+       buflen = 0;
+}
+
+#define REPLY(val) d_sio->write_signal(did_sio, val, 0xff)
+
+void TF20::write_signal(int id, uint32 data, uint32 mask)
+{
+       switch(phase) {
+       case PHASE_IDLE:
+               if(data == EOT) {
+                       break;
+               }
+               if(data == ENQ) {
+                       REPLY(ACK);
+                       break;
+               }
+               if(data != DID_FIRST) {
+                       REPLY(NAK);
+                       break;
+               }
+               phase = PHASE_SELECT;
+               buflen = 0;
+               break;
+               
+       case PHASE_SELECT:
+               bufr[buflen++] = data;
+               if(buflen < 3) {
+                       break;
+               }
+               if(bufr[0] != DID_FIRST && bufr[0] != DID_SECOND) {
+                       phase = PHASE_IDLE;
+                       break;
+               }
+               if(bufr[2] != DS_SEL) {
+                       REPLY(NAK);
+                       phase = PHASE_IDLE;
+                       break;
+               }
+               REPLY(ACK);
+               phase = PHASE_FUNC;
+               break;
+               
+       case PHASE_FUNC:
+               if(data == EOT) {
+                       phase = PHASE_IDLE;
+                       break;
+               }
+               if(data == ENQ) {
+                       REPLY(ACK);
+                       break;
+               }
+               if(data != SOH) {
+                       REPLY(NAK);
+                       phase = PHASE_IDLE;
+                       break;
+               }
+               phase = PHASE_HEAD;
+               buflen = 0;
+               break;
+               
+       case PHASE_HEAD:
+               bufr[buflen++] = data;
+               if(buflen < 6) {
+                       break;
+               }
+               REPLY(ACK);
+               phase = PHASE_DATA;
+               break;
+               
+       case PHASE_DATA:
+               bufr[buflen++] = data;
+               if(buflen < (6 + bufr[4] + 4)) {
+                       break;
+               }
+               if(bufr[6] != STX) {
+                       REPLY(NAK);
+                       phase = PHASE_IDLE;
+                       break;
+               }
+               REPLY(ACK);
+               phase = PHASE_EXEC;
+               break;
+               
+       case PHASE_EXEC:
+               if(data != EOT) {
+                       REPLY(NAK);
+                       phase = PHASE_IDLE;
+                       break;
+               }
+               if(!process_cmd()) {
+                       REPLY(NAK);
+                       phase = PHASE_IDLE;
+                       break;
+               }
+               for(int i = 0; i < 7; i++) {
+                       REPLY(bufs[i]);
+               }
+               phase = PHASE_RESULT;
+               break;
+               
+       case PHASE_RESULT:
+               if(data == ENQ) {
+                       REPLY(ACK);
+                       phase = PHASE_FUNC;
+                       break;
+               }
+               if(data != ACK) {
+                       phase = PHASE_FUNC;
+                       break;
+               }
+               for(int i = 7; i < buflen; i++) {
+                       REPLY(bufs[i]);
+               }
+               phase = PHASE_END;
+               break;
+               
+       case PHASE_END:
+               if(data == ENQ) {
+                       REPLY(ACK);
+               } else if(data == ACK) {
+                       REPLY(EOT);
+               }
+               phase = PHASE_FUNC;
+               break;
+       }
+}
+
+#define SET_HEAD(size) { \
+       bufs[0] = 1; \
+       bufs[1] = 1; \
+       bufs[2] = bufr[2]; \
+       bufs[3] = bufr[1]; \
+       bufs[4] = bufr[3]; \
+       bufs[5] = size; \
+       uint8 sum = 0; \
+       for(int s = 0; s < 6; s++) \
+               sum += bufs[s]; \
+       bufs[6] = 256 - sum; \
+       bufs[7] = 2; \
+       buflen = 8; \
+}
+#define SET_DATA(v) bufs[buflen++] = (v)
+#define SET_CODE(v) { \
+       bufs[buflen++] = (v); \
+       bufs[buflen++] = 3; \
+       uint8 sum = 0; \
+       for(int s = 7; s < buflen; s++) \
+               sum += bufs[s]; \
+       bufs[buflen++] = 256 - sum; \
+}
+
+bool TF20::process_cmd()
+{
+       int drv, trk, sec, dst;
+       uint8 *sctr, *sctw;
+       
+       switch(bufr[3]) {
+       case FNC_RESET_P:
+       case FNC_RESET_M:
+               SET_HEAD(0);
+               SET_CODE(ERR_SUCCESS);
+               return true;
+               
+       case FNC_READ:
+               drv = (bufr[1] == DID_FIRST) ? 0 : (bufr[1] == DID_SECOND) ? 2 : 4;
+               drv += bufr[7] - 1;
+               trk = bufr[8];
+               sec = bufr[9];
+               if(!disk_inserted(drv)) {
+                       // drive error
+                       SET_HEAD(0x80);
+                       for(int i = 0; i < 128; i++) {
+                               SET_DATA(0xff);
+                       }
+                       SET_CODE(ERR_DRIVE);
+                       return true;
+               }
+               if((sctr = get_sector(drv, trk, sec)) == NULL) {
+                       // read error
+                       SET_HEAD(0x80);
+                       for(int i = 0; i < 128; i++) {
+                               SET_DATA(0xff);
+                       }
+                       SET_CODE(ERR_READ);
+                       return true;
+               }
+               SET_HEAD(0x80);
+               for(int i = 0; i < 128; i++) {
+                       SET_DATA(sctr[i]);
+               }
+               SET_CODE(ERR_SUCCESS);
+               return true;
+               
+       case FNC_WRITE:
+               drv = (bufr[1] == DID_FIRST) ? 0 : (bufr[1] == DID_SECOND) ? 2 : 4;
+               drv += bufr[7] - 1;
+               trk = bufr[8];
+               sec = bufr[9];
+               if(!disk_inserted(drv)) {
+                       // drive error
+                       SET_HEAD(0);
+                       SET_CODE(ERR_DRIVE);
+                       return true;
+               }
+               if(disk_protected(drv)) {
+                       // write protect
+                       SET_HEAD(0);
+                       SET_CODE(ERR_PROTECTED);
+                       return true;
+               }
+               if((sctw = get_sector(drv, trk, sec)) == NULL) {
+                       // write error
+                       SET_HEAD(0);
+                       SET_CODE(ERR_WRITE);
+                       return true;
+               }
+               // dont care write type
+               for(int i = 0; i < 128; i++) {
+                       sctw[i] = bufr[11 + i];
+               }
+               SET_HEAD(0);
+               SET_CODE(ERR_SUCCESS);
+               return true;
+               
+       case FNC_WRITEHST:
+               SET_HEAD(0);
+               SET_CODE(ERR_SUCCESS);
+               return true;
+               
+       case FNC_COPY:
+               drv = (bufr[1] == DID_FIRST) ? 0 : (bufr[1] == DID_SECOND) ? 2 : 4;
+               drv += bufr[7] - 1;
+               dst = (drv & ~1) | (~drv & 1);
+               if(!disk_inserted(drv)) {
+                       // drive error
+                       SET_HEAD(0);
+                       SET_CODE(ERR_DRIVE);
+                       return true;
+               }
+               if(!disk_inserted(dst)) {
+                       // drive error
+                       SET_HEAD(0);
+                       SET_CODE(ERR_DRIVE);
+                       return true;
+               }
+               if(disk_protected(dst)) {
+                       // write protect
+                       SET_HEAD(0);
+                       SET_CODE(ERR_PROTECTED);
+                       return true;
+               }
+               for(trk = 0; trk < 40; trk++) {
+                       for(sec = 1; sec <= 64; sec++) {
+                               if((sctr = get_sector(drv, trk, sec)) == NULL) {
+                                       // read error
+                                       SET_HEAD(0);
+                                       SET_CODE(ERR_READ);
+                                       return true;
+                               }
+                               if((sctw = get_sector(dst, trk, sec)) == NULL) {
+                                       // write error
+                                       SET_HEAD(0);
+                                       SET_CODE(ERR_WRITE);
+                                       return true;
+                               }
+                               memcpy(sctw, sctr, 128);
+                       }
+                       SET_HEAD(2);
+                       SET_DATA(trk == 39 ? 0xff : 0);         // high-order
+                       SET_DATA(trk == 39 ? 0xff : trk);       // low-order
+                       SET_CODE(ERR_SUCCESS);
+               }
+               return true;
+               
+       case FNC_FORMAT:
+               drv = (bufr[1] == DID_FIRST) ? 0 : (bufr[1] == DID_SECOND) ? 2 : 4;
+               drv += bufr[7] - 1;
+               if(!disk_inserted(drv)) {
+                       // drive error
+                       SET_HEAD(0);
+                       SET_CODE(ERR_DRIVE);
+                       return true;
+               }
+               if(disk_protected(drv)) {
+                       // write protect
+                       SET_HEAD(0);
+                       SET_CODE(ERR_PROTECTED);
+                       return true;
+               }
+               for(trk = 0; trk < 40; trk++) {
+                       for(sec = 1; sec <= 64; sec++) {
+                               if((sctw = get_sector(drv, trk, sec)) == NULL) {
+                                       // write error
+                                       SET_HEAD(0);
+                                       SET_CODE(ERR_WRITE);
+                                       return true;
+                               }
+                               memset(sctw, 0xe5, 128);
+                       }
+                       SET_HEAD(2);
+                       SET_DATA(trk == 39 ? 0xff : 0);         // high-order
+                       SET_DATA(trk == 39 ? 0xff : trk);       // low-order
+                       SET_CODE(ERR_SUCCESS);
+               }
+               return true;
+       }
+       // unknown command
+       return false;
+}
+
+bool TF20::disk_protected(int drv)
+{
+       if(drv < MAX_DRIVE) {
+               return disk[drv]->write_protected;
+       }
+       return false;
+}
+
+uint8* TF20::get_sector(int drv, int trk, int sec)
+{
+       // logical : trk = 0-39, sec = 1-64, secsize = 128bytes
+       int total = trk * 64 + sec - 1;
+       int half = total & 1;
+       total >>= 1;
+       int phys_sec = total & 15;
+       total >>= 4;
+       int phys_side = total & 1;
+       int phys_trk = total >> 1;
+       
+       if(!disk_inserted(drv)) {
+               return NULL;
+       }
+       if(!disk[drv]->get_sector(phys_trk, phys_side, phys_sec)) {
+               return NULL;
+       }
+       return disk[drv]->sector + (half ? 128 : 0);
+}
+
+// ----------------------------------------------------------------------------
+// user interface
+// ----------------------------------------------------------------------------
+
+void TF20::open_disk(int drv, _TCHAR path[], int offset)
+{
+       if(drv < MAX_DRIVE) {
+               disk[drv]->open(path, offset);
+       }
+}
+
+void TF20::close_disk(int drv)
+{
+       if(drv < MAX_DRIVE) {
+               disk[drv]->close();
+       }
+}
+
+bool TF20::disk_inserted(int drv)
+{
+       if(drv < MAX_DRIVE) {
+               return disk[drv]->inserted;
+       }
+       return false;
+}
+