├── CPRINT.C ├── CPRINT.H ├── DISKIO.H ├── DRIVER.C ├── DRIVER.H ├── HEADER.ASM ├── INTEGER.H ├── LICENSE.TXT ├── MAKEFILE ├── SD.C ├── SD.H ├── SDMM.C ├── STANDARD.H ├── doc ├── adapter.png ├── board.png ├── module.png ├── module_schema.png ├── schematic.png └── teaser.png ├── driver ├── NILQUAD.SYS └── SD.SYS ├── eagle ├── sd_adapter.lbr ├── sdadapter.brd ├── sdadapter.sch ├── sddrive.brd └── sddrive.sch └── readme.md /CPRINT.C: -------------------------------------------------------------------------------- 1 | /* cprint.c */ 2 | /* */ 3 | /* This file contains simple ASCII output routines. These are used */ 4 | /* by the device driver for debugging, and to issue informational */ 5 | /* messages (e.g. while loading). In general the C run time library */ 6 | /* functions probably aren't safe for use withing the context of a */ 7 | /* device driver and should be avoided. */ 8 | /* */ 9 | /* All these routines do their output thru the routine outchr, which */ 10 | /* is defined in DRIVER.ASM. It calls the BIOS INT 10 "dumb TTY" */ 11 | /* output function directly and does not use MSDOS at all. */ 12 | /* */ 13 | /* Copyright (C) 1994 by Robert Armstrong */ 14 | /* */ 15 | /* This program is free software; you can redistribute it and/or modify */ 16 | /* it under the terms of the GNU General Public License as published by */ 17 | /* the Free Software Foundation; either version 2 of the License, or */ 18 | /* (at your option) any later version. */ 19 | /* */ 20 | /* This program is distributed in the hope that it will be useful, but */ 21 | /* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */ 22 | /* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ 23 | /* Public License for more details. */ 24 | /* */ 25 | /* You should have received a copy of the GNU General Public License */ 26 | /* along with this program; if not, visit the website of the Free */ 27 | /* Software Foundation, Inc., www.gnu.org. */ 28 | 29 | #include /* NULL, etc... */ 30 | #include /* used only for MK_FP ! */ 31 | #include /* needed for variable argument lists */ 32 | 33 | /* outchr - print a single ASCII character */ 34 | void outchr (char ch) 35 | { 36 | _DI = _SI = 0; 37 | _AL = ch; _AH = 0xE; _BX = 0; 38 | asm INT 0x10; 39 | } 40 | 41 | 42 | /* outstr - print an ASCIZ string */ 43 | void outstr (char *p) 44 | { 45 | while (*p != '\0') 46 | outchr (*p++); 47 | } 48 | 49 | /* outdec - print a signed decimal integer */ 50 | void outdec (int val) 51 | { 52 | if (val < 0) 53 | {outchr('-'); val = -val;} 54 | if (val > 9) 55 | {outdec( val/10 ); val %= 10;} 56 | outchr('0' + val); 57 | } 58 | 59 | /* outhex - print a n digit hex number with leading zeros */ 60 | void outhex (unsigned val, int ndigits) 61 | { 62 | if (ndigits > 1) 63 | outhex (val >> 4, ndigits-1); 64 | val &= 0xf; 65 | if (val > 9) 66 | outchr('A'+val-10); 67 | else 68 | outchr('0'+val); 69 | } 70 | 71 | /* outhex - print a n digit hex number with leading zeros */ 72 | void outlhex (unsigned long lval) 73 | { 74 | int i; 75 | for (i=3;i>=0;i--) 76 | outhex(((unsigned char *)&lval)[i],2); 77 | } 78 | 79 | 80 | /* outcrlf - print a carriage return, line feed pair */ 81 | void outcrlf (void) 82 | { 83 | outchr ('\r'); outchr ('\n'); 84 | } 85 | 86 | /* cprintf */ 87 | /* This routine provides a simple emulation for the printf() function */ 88 | /* using the "safe" console output routines. Only a few escape seq- */ 89 | /* uences are allowed: %d, %x and %s. A width modifier (e.g. %2x) is */ 90 | /* recognized only for %x, and then may only be a single decimal digit. */ 91 | void cdprintf (char near *msg, ...) 92 | { 93 | va_list ap; char *str; int size, ival; unsigned uval; unsigned long luval; 94 | va_start (ap, msg); 95 | 96 | while (*msg != '\0') { 97 | /*outhex((unsigned) msg, 4); outchr('='); outhex(*msg, 2); outchr(' ');*/ 98 | if (*msg == '%') { 99 | ++msg; size = 0; 100 | if ((*msg >= '0') && (*msg <= '9')) 101 | {size = *msg - '0'; ++msg;} 102 | if (*msg == 'c') { 103 | ival = va_arg(ap, int); outchr(ival&0xff); ++msg; 104 | } else if (*msg == 'd') { 105 | ival = va_arg(ap, int); outdec (ival); ++msg; 106 | } else if (*msg == 'x') { 107 | uval = va_arg(ap, unsigned); ++msg; 108 | outhex (uval, (size > 0) ? size : 4); 109 | } else if (*msg == 'L') { 110 | luval = va_arg(ap, unsigned long); ++msg; 111 | outlhex (luval); 112 | } else if (*msg == 's') { 113 | str = va_arg(ap, char *); outstr (str); ++msg; 114 | } 115 | } else if (*msg == '\n') { 116 | outchr('\r'); outchr('\n'); ++msg; 117 | } else { 118 | outchr(*msg); ++msg; 119 | } 120 | } 121 | 122 | va_end (ap); 123 | } 124 | -------------------------------------------------------------------------------- /CPRINT.H: -------------------------------------------------------------------------------- 1 | /* */ 2 | /* This file contains simple ASCII output routines. These are used */ 3 | /* by the device driver for debugging, and to issue informational */ 4 | /* messages (e.g. while loading). In general the C run time library */ 5 | /* functions probably aren't safe for use withing the context of a */ 6 | /* device driver and should be avoided. */ 7 | /* */ 8 | /* All these routines do their output thru the routine outchr, which */ 9 | /* is defined in DRIVER.ASM. It calls the BIOS INT 10 "dumb TTY" */ 10 | /* output function directly and does not use MSDOS at all. */ 11 | /* */ 12 | /* Copyright (C) 1994 by Robert Armstrong */ 13 | /* */ 14 | /* This program is free software; you can redistribute it and/or modify */ 15 | /* it under the terms of the GNU General Public License as published by */ 16 | /* the Free Software Foundation; either version 2 of the License, or */ 17 | /* (at your option) any later version. */ 18 | /* */ 19 | /* This program is distributed in the hope that it will be useful, but */ 20 | /* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */ 21 | /* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ 22 | /* Public License for more details. */ 23 | /* */ 24 | /* You should have received a copy of the GNU General Public License */ 25 | /* along with this program; if not, visit the website of the Free */ 26 | /* Software Foundation, Inc., www.gnu.org. */ 27 | 28 | #ifndef _CPRINT_H 29 | #define _CPRINT_H 30 | void outchr (char ch); 31 | void outstr (char *p); 32 | void outdec (int val); 33 | void outhex (unsigned val, int ndigits); 34 | void outcrlf (void); 35 | void cdprintf (char near *msg, ...); 36 | #endif 37 | -------------------------------------------------------------------------------- /DISKIO.H: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------- 2 | / Low level disk interface modlue include file (C)ChaN, 2013 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | #include "integer.h" 13 | 14 | 15 | /* Status of Disk Functions */ 16 | typedef BYTE DSTATUS; 17 | 18 | /* Results of Disk Functions */ 19 | typedef enum { 20 | RES_OK = 0, /* 0: Successful */ 21 | RES_ERROR, /* 1: R/W Error */ 22 | RES_WRPRT, /* 2: Write Protected */ 23 | RES_NOTRDY, /* 3: Not Ready */ 24 | RES_PARERR /* 4: Invalid Parameter */ 25 | } DRESULT; 26 | 27 | void setportbase(BYTE val); /* set the port base */ 28 | 29 | /*---------------------------------------*/ 30 | /* Prototypes for disk control functions */ 31 | #define DOSFAR far 32 | 33 | DSTATUS disk_initialize (BYTE pdrv); 34 | DSTATUS disk_status (BYTE pdrv); 35 | DRESULT disk_result (BYTE pdrv); 36 | DRESULT disk_read (BYTE pdrv, BYTE DOSFAR * buff, DWORD sector, UINT count); 37 | DRESULT disk_write (BYTE pdrv, const BYTE DOSFAR * buff, DWORD sector, UINT count); 38 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void DOSFAR * buff); 39 | 40 | 41 | /* Disk Status Bits (DSTATUS) */ 42 | #define STA_NOINIT 0x01 /* Drive not initialized */ 43 | #define STA_NODISK 0x02 /* No medium in the drive */ 44 | #define STA_PROTECT 0x04 /* Write protected */ 45 | 46 | 47 | /* Command code for disk_ioctrl fucntion */ 48 | 49 | /* Generic command (used by FatFs) */ 50 | #define CTRL_SYNC 0 /* Flush disk cache (for write functions) */ 51 | #define GET_SECTOR_COUNT 1 /* Get media size (for only f_mkfs()) */ 52 | //#define GET_SECTOR_SIZE 2 /* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */ 53 | #define GET_BLOCK_SIZE 3 /* Get erase block size (for only f_mkfs()) */ 54 | #define CTRL_ERASE_SECTOR 4 /* Force erased a block of sectors (for only _USE_ERASE) */ 55 | 56 | /* Generic command (not used by FatFs) */ 57 | //#define CTRL_POWER 5 /* Get/Set power status */ 58 | //#define CTRL_LOCK 6 /* Lock/Unlock media removal */ 59 | //#define CTRL_EJECT 7 /* Eject media */ 60 | //#define CTRL_FORMAT 8 /* Create physical format on the media */ 61 | 62 | /* MMC/SDC specific ioctl command */ 63 | #define MMC_GET_TYPE 10 /* Get card type */ 64 | #define MMC_GET_CSD 11 /* Get CSD */ 65 | #define MMC_GET_CID 12 /* Get CID */ 66 | #define MMC_GET_OCR 13 /* Get OCR */ 67 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 68 | 69 | /* ATA/CF specific ioctl command */ 70 | #define ATA_GET_REV 20 /* Get F/W revision */ 71 | #define ATA_GET_MODEL 21 /* Get model name */ 72 | #define ATA_GET_SN 22 /* Get serial number */ 73 | 74 | 75 | /* MMC card type flags (MMC_GET_TYPE) */ 76 | #define CT_MMC 0x01 /* MMC ver 3 */ 77 | #define CT_SD1 0x02 /* SD ver 1 */ 78 | #define CT_SD2 0x04 /* SD ver 2 */ 79 | #define CT_SDC (CT_SD1|CT_SD2) /* SD */ 80 | #define CT_BLOCK 0x08 /* Block addressing */ 81 | 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /DRIVER.C: -------------------------------------------------------------------------------- 1 | /* driver.c - MSDOS device driver functions */ 2 | /* */ 3 | /* Copyright (C) 1994 by Robert Armstrong */ 4 | /* */ 5 | /* This program is free software; you can redistribute it and/or modify */ 6 | /* it under the terms of the GNU General Public License as published by */ 7 | /* the Free Software Foundation; either version 2 of the License, or */ 8 | /* (at your option) any later version. */ 9 | /* */ 10 | /* This program is distributed in the hope that it will be useful, but */ 11 | /* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */ 12 | /* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ 13 | /* Public License for more details. */ 14 | /* */ 15 | /* You should have received a copy of the GNU General Public License */ 16 | /* along with this program; if not, visit the website of the Free */ 17 | /* Software Foundation, Inc., www.gnu.org. */ 18 | 19 | 20 | #include /* NULL, etc... */ 21 | #include /* used only for MK_FP ! */ 22 | #include "standard.h" /* definitions for this project */ 23 | #include "sd.h" /* SD card glue */ 24 | #include "diskio.h" /* SD card library header */ 25 | #include "driver.h" /* MSDOS device driver interface */ 26 | #include "cprint.h" /* Console printing */ 27 | 28 | 29 | #define FORM_FACTOR 8 /* DOS form factor code used for SD */ 30 | 31 | 32 | /* Forward declarations for routines that need it... */ 33 | PRIVATE BOOLEAN parse_options (char far *); 34 | PUBLIC void Initialize (rh_init_t far *); 35 | PUBLIC void far SDDriver (rh_t far *); 36 | 37 | 38 | /* These data structures are exported by the HEADER.ASM module... */ 39 | extern devhdr_t header; /* MSDOS device header for our driver */ 40 | extern bpb_t bpb; /* BIOS Parameter block " " " */ 41 | extern bpbtbl_t bpbtbl; /* BPB table (one for each drive unit) */ 42 | 43 | /* This double word is actually a far pointer to the entry point of */ 44 | /* this module. It's called by the assembly interface whenver DOS needs */ 45 | /* driver action. The second word of the far pointer contains the seg- */ 46 | /* ment address, which is actually computed and written by the assembly */ 47 | /* code. */ 48 | PUBLIC WORD c_driver[2] = {(WORD) &SDDriver, 0}; 49 | 50 | extern unsigned char com_flag; 51 | 52 | /* Local data for this module... */ 53 | BOOLEAN Debug = FALSE; /* TRUE to enable debug (verbose) mode */ 54 | BOOLEAN InitNeeded = TRUE; /* TRUE if we need to (re) initialize */ 55 | WORD RebootVector[2]; /* previous INT 19H vector contents */ 56 | extern DWORD partitionoffset; 57 | 58 | extern BYTE sd_card_check; 59 | extern BYTE portbase; 60 | 61 | BYTE partition_number; 62 | 63 | /* fmemcpy */ 64 | /* This function is equivalent to the C RTL _fmemcpy routine. We have */ 65 | /* to define it here because BPC is unable to generate inline code for */ 66 | /* _fmemcpy (although it can generate equivalent code for memcpy!). */ 67 | void fmemcpy (void far *dst, void far *src, WORD n) 68 | { 69 | _asm { 70 | les di,dword ptr dst 71 | mov dx,word ptr src+2 72 | mov si,word ptr src 73 | mov cx,n 74 | push ds 75 | mov ds,dx 76 | rep movsb 77 | pop ds 78 | } 79 | } 80 | 81 | /* fmemset */ 82 | /* This is the equivalent to _fmemset. It is here for exactly the */ 83 | /* same reasons as fmemcpy !!! */ 84 | void fmemset (void far *dst, BYTE c, WORD n) 85 | { 86 | _asm { 87 | mov al,c 88 | les di,dword ptr dst 89 | mov cx,n 90 | rep stosb 91 | } 92 | } 93 | 94 | 95 | /* Media Check */ 96 | /* DOS calls this function to determine if the tape in the drive has */ 97 | /* been changed. The SD hardware can't determine this (like many */ 98 | /* older 360K floppy drives), and so we always return the "Don't Know" */ 99 | /* response. This works well enough... */ 100 | PUBLIC void MediaCheck (rh_media_check_t far *rh) 101 | { 102 | if (Debug) cdprintf("SD: media check: unit=%d\n", rh->rh.unit); 103 | rh->media_status = SDMediaCheck(rh->rh.unit) ? -1 : 0; 104 | rh->rh.status = DONE; 105 | } 106 | 107 | 108 | /* Build BPB */ 109 | /* DOS uses this function to build the BIOS parameter block for the */ 110 | /* specified drive. For diskettes, which support different densities */ 111 | /* and formats, the driver actually has to read the BPB from the boot */ 112 | /* sector on the disk. */ 113 | PUBLIC void BuildBPB (rh_get_bpb_t far *rh) 114 | { 115 | if (Debug) 116 | cdprintf("SD: build BPB: unit=%d\n", rh->rh.unit); 117 | rh->bpb = &bpb; 118 | rh->rh.status = DONE; 119 | } 120 | 121 | 122 | /* Get Parameters */ 123 | /* This routine implements the Get Parameters subfunction of the DOS */ 124 | /* Generic IOCTL call. It gets a pointer to the device paramters block, */ 125 | /* which it then fills in. We do NOT create the track/sector map that */ 126 | /* is defined by recent DOS manuals to be at the end of the block - it */ 127 | /* never seems to be used... */ 128 | PUBLIC void GetParameters (device_params_t far *dp) 129 | { 130 | dp->form_factor = FORM_FACTOR; dp->attributes = 0; dp->media_type = 0; 131 | dp->cylinders = bpb.total_sectors / (bpb.track_size * bpb.head_count); 132 | fmemcpy(&(dp->bpb), &bpb, sizeof(bpb_t)); 133 | } 134 | 135 | /* dos_error */ 136 | /* This routine will translate a SD error code into an appropriate */ 137 | /* DOS error code. This driver never retries on any error condition. */ 138 | /* For actual tape read/write errors it's pointless because the drive */ 139 | /* will have already tried several times before reporting the failure. */ 140 | /* All the other errors (e.g. write lock, communications failures, etc) */ 141 | /* are not likely to succeed without user intervention, so we go thru */ 142 | /* the usual DOS "Abort, Retry or Ignore" dialog. Communications errors */ 143 | /* are a special situation. In these cases we also set global flag to */ 144 | /* force a controller initialization before the next operation. */ 145 | int dos_error (int status) 146 | { 147 | switch (status) { 148 | case RES_OK: return 0; 149 | case RES_WRPRT: return WRITE_PROTECT; 150 | case RES_NOTRDY: InitNeeded= TRUE; return NOT_READY; 151 | case RES_ERROR: InitNeeded= TRUE; return BAD_SECTOR; 152 | case RES_PARERR: return CRC_ERROR; 153 | 154 | default: 155 | cdprintf("SD: unknown drive error - status = 0x%2x\n", status); 156 | return GENERAL_FAILURE; 157 | } 158 | } 159 | 160 | 161 | /* drive_init */ 162 | /* This routine should be called before every I/O function. If the */ 163 | /* last I/O operation resulted in a protocol error, then this routine */ 164 | /* will re-initialize the drive and the communications. If the drive */ 165 | /* still won't talk to us, then it will set a general failure code in */ 166 | /* the request header and return FALSE. */ 167 | BOOLEAN drive_init (rh_t far *rh) 168 | { 169 | if (!InitNeeded) return TRUE; 170 | if (!SDInitialize(rh->unit, partition_number, &bpb)) { 171 | if (Debug) cdprintf("SD: drive failed to initialize\n"); 172 | rh->status = DONE | ERROR | GENERAL_FAILURE; 173 | return FALSE; 174 | } 175 | InitNeeded = FALSE; 176 | if (Debug) cdprintf("SD: drive initialized\n"); 177 | return TRUE; 178 | } 179 | 180 | 181 | /* Read Data */ 182 | PUBLIC void ReadBlock (rh_io_t far *rh) 183 | { 184 | WORD lbn, count; int status; BYTE far *dta; 185 | WORD sendct; 186 | if (Debug) 187 | cdprintf("SD: read block: unit=%d, start=%d, count=%d, dta=%4x:%4x\n", 188 | rh->rh.unit, rh->start, rh->count, FP_SEG(rh->dta), FP_OFF(rh->dta)); 189 | if (!drive_init ((rh_t far *) rh)) return; 190 | count = rh->count, lbn = rh->start, dta = rh->dta; 191 | while (count > 0) { 192 | sendct = (count > 16) ? 16 : count; 193 | status = SDRead(rh->rh.unit, lbn, dta, sendct); 194 | if (status != RES_OK) { 195 | if (Debug) cdprintf("SD: read error - status=%d\n", status); 196 | fmemset(dta, 0, BLOCKSIZE); 197 | rh->rh.status = DONE | ERROR | dos_error(status); 198 | return; 199 | } 200 | lbn += sendct; 201 | count -= sendct; 202 | dta += (sendct*BLOCKSIZE); 203 | } 204 | rh->rh.status = DONE; 205 | } 206 | 207 | 208 | /* Write Data */ 209 | /* Write Data with Verification */ 210 | PUBLIC void WriteBlock (rh_io_t far *rh, BOOLEAN verify) 211 | { 212 | WORD lbn, count; int status; BYTE far *dta; 213 | WORD sendct; 214 | if (Debug) 215 | cdprintf("SD: write block: unit=%d, start=%d, count=%d, dta=%4x:%4x\n", 216 | rh->rh.unit, rh->start, rh->count, FP_SEG(rh->dta), FP_OFF(rh->dta)); 217 | if (!drive_init ((rh_t far *) rh)) return; 218 | count = rh->count, lbn = rh->start, dta = rh->dta; 219 | while (count > 0) { 220 | sendct = (count > 16) ? 16 : count; 221 | status = SDWrite(rh->rh.unit, lbn, dta, sendct); 222 | if (status != RES_OK) { 223 | if (Debug) cdprintf("SD: write error - status=%d\n", status); 224 | rh->rh.status = DONE | ERROR | dos_error(status); 225 | return; 226 | } 227 | lbn += sendct; 228 | count -= sendct; 229 | dta += (sendct*BLOCKSIZE); 230 | } 231 | rh->rh.status = DONE; 232 | } 233 | 234 | 235 | /* Generic IOCTL */ 236 | /* The generic IOCTL functions are used by DOS programs to determine */ 237 | /* the device geometry, and especially by the FORMAT program to init- */ 238 | /* ialize new media. The DOS format program requires us to implement */ 239 | /* these three generic IOCTL functions: */ 240 | 241 | PUBLIC void GenericIOCTL (rh_generic_ioctl_t far *rh) 242 | { 243 | if (Debug) 244 | cdprintf("SD: generic IOCTL: unit=%d, major=0x%2x, minor=0x%2x, data=%4x:%4x\n", 245 | rh->rh.unit, rh->major, rh->minor, FP_SEG(rh->packet), FP_OFF(rh->packet)); 246 | 247 | if (rh->major == DISK_DEVICE) 248 | switch (rh->minor) { 249 | case GET_PARAMETERS: GetParameters ((device_params_t far *) rh->packet); 250 | rh->rh.status = DONE; 251 | return; 252 | case GET_ACCESS: ((access_flag_t far *) (rh->packet))->allowed = 1; 253 | rh->rh.status = DONE; 254 | return; 255 | case SET_MEDIA_ID: 256 | case SET_ACCESS: 257 | case SET_PARAMETERS: 258 | case FORMAT_TRACK: 259 | rh->rh.status = DONE; 260 | return; 261 | case GET_MEDIA_ID: 262 | default: ; 263 | } 264 | //cdprintf("SD: unimplemented IOCTL - unit=%d, major=0x%2x, minor=0x%2x\n",rh->rh.unit, rh->major, rh->minor); 265 | rh->rh.status = DONE | ERROR | UNKNOWN_COMMAND; 266 | } 267 | 268 | 269 | /* Generic IOCTL Query */ 270 | /* DOS programs can use this function to determine which generic */ 271 | /* IOCTL functions a device supports. The query IOCTL driver call will */ 272 | /* succeed for any function the driver supports, and fail for others. */ 273 | /* Nothing actually happens - just a test for success or failure. */ 274 | PUBLIC void IOCTLQuery (rh_generic_ioctl_t far *rh) 275 | { 276 | if (Debug) 277 | cdprintf("SD: generic IOCTL query: unit=%d, major=0x%2x, minor=0x%2x\n", 278 | rh->rh.unit, rh->major, rh->minor); 279 | if (rh->major == DISK_DEVICE) 280 | switch (rh->minor) { 281 | case GET_ACCESS: 282 | case SET_ACCESS: 283 | case SET_MEDIA_ID: 284 | case GET_PARAMETERS: 285 | case SET_PARAMETERS: 286 | case FORMAT_TRACK: rh->rh.status = DONE; 287 | return; 288 | default: 289 | break; 290 | } 291 | rh->rh.status = DONE | ERROR | UNKNOWN_COMMAND; 292 | } 293 | 294 | 295 | /* SDDriver */ 296 | /* This procedure is called by the DRIVER.ASM interface module when */ 297 | /* MSDOS calls the driver INTERRUPT routine, and the C code is expected */ 298 | /* to define it. Note that the STRATEGY call is handled completely */ 299 | /* inside DRIVER.ASM and the address of the request header block is */ 300 | /* passed to the C interrupt routine as a parameter. */ 301 | PUBLIC void far SDDriver (rh_t far *rh) 302 | { 303 | /* 304 | if (Debug) 305 | cdprintf("SD: request at %4x:%4x, command=%d, length=%d\n", 306 | FP_SEG(rh), FP_OFF(rh), rh->command, rh->length); 307 | */ 308 | switch (rh->command) { 309 | case INITIALIZATION: Initialize ((rh_init_t far *) rh); break; 310 | case MEDIA_CHECK: MediaCheck ((rh_media_check_t far *) rh); break; 311 | case GET_BPB: BuildBPB ((rh_get_bpb_t far *) rh); break; 312 | case INPUT: ReadBlock ((rh_io_t far *) rh); break; 313 | case OUTPUT: WriteBlock ((rh_io_t far *) rh, FALSE); break; 314 | case OUTPUT_VERIFY: WriteBlock ((rh_io_t far *) rh, TRUE); break; 315 | case GENERIC_IOCTL: GenericIOCTL ((rh_generic_ioctl_t far *) rh); break; 316 | case IOCTL_QUERY: IOCTLQuery ((rh_generic_ioctl_t far *) rh); break; 317 | 318 | case GET_LOGICAL: 319 | case SET_LOGICAL: rh->status = DONE; break; 320 | 321 | default: 322 | cdprintf("SD: unimplemented driver request - command=%d, length=%d\n", 323 | rh->command, rh->length); 324 | rh->status = DONE | ERROR | UNKNOWN_COMMAND; 325 | } 326 | } 327 | 328 | 329 | PUBLIC void Shutdown (void) 330 | { 331 | long i; 332 | cdprintf("SD: Shutdown\n"); 333 | for (i=0; i <100000; ++i); 334 | /* SDClose(); */ 335 | JMPVECTOR(RebootVector); 336 | } 337 | 338 | 339 | /* WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! */ 340 | /* */ 341 | /* All code following this point in the file is discarded after the */ 342 | /* driver initialization. Make absolutely sure that no routine above */ 343 | /* this line calls any routine below it!! */ 344 | /* */ 345 | /* WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! */ 346 | 347 | 348 | /* Driver Initialization */ 349 | /* DOS calls this function immediately after the driver is loaded and */ 350 | /* expects it to perform whatever initialization is required. Since */ 351 | /* this function can never be called again, it's customary to discard */ 352 | /* the memory allocated to this routine and any others that are used */ 353 | /* only at initialization. This allows us to economize a little on the */ 354 | /* amount of memory used. */ 355 | /* */ 356 | /* This routine's basic function is to initialize the serial port, go */ 357 | /* and make contact with the SD card, and then return a table of BPBs to */ 358 | /* DOS. If we can't communicate with the drive, then the entire driver */ 359 | /* is unloaded from memory. */ 360 | PUBLIC void Initialize (rh_init_t far *rh) 361 | { 362 | WORD brkadr, reboot[2]; int status, i; 363 | 364 | /* The version number is sneakily stored in the device header! */ 365 | cdprintf("SD Card Parallel Port Driver v.1.1.0 - https://github.com/nilseuropa/sdpp\n"); 366 | cdprintf("\n"); 367 | /* Parse the options from the CONFIG.SYS file, if any... */ 368 | if (!parse_options((char far *) rh->bpbtbl)) { 369 | cdprintf("SD: bad options in CONFIG.SYS\n"); 370 | goto unload2; 371 | } 372 | 373 | /* Calculate the size of this driver by using the address of this */ 374 | /* routine. Note that C will return an offset for &Initialize which */ 375 | /* is relative to the _TEXT segment. We have to adjust this by adding */ 376 | /* the offset from DGROUP. See HEADER.ASM for a memory layout. */ 377 | brkadr = ((WORD) &Initialize) + ((_CS - _DS) << 4); 378 | rh->brkadr = MK_FP(_DS, brkadr); 379 | if (Debug) 380 | cdprintf("SD: CS=%4x, DS=%4x, SS=%4x, SP=%4x, break=%4x\n", 381 | _CS, _DS, _SS, _SP, brkadr); 382 | 383 | /* Try to make contact with the drive... */ 384 | if (Debug) cdprintf("SD: initializing drive\n"); 385 | if (!SDInitialize(rh->rh.unit, partition_number, &bpb)) { 386 | cdprintf("SD: adapter or card not found\n"); 387 | cdprintf("SD: try another port \n"); 388 | goto unload1; 389 | } 390 | cdprintf("SD: rh = %4x:%4x\n", FP_SEG(rh), FP_OFF(rh)); 391 | 392 | reboot[0] = ((WORD) &reboot) + ((_CS - _DS) << 4); 393 | reboot[1] = _CS; 394 | GETVECTOR(0x19, RebootVector); 395 | SETVECTOR(0x19, reboot); 396 | if (Debug) 397 | cdprintf("SD: reboot vector = %4x:%4x, old vector = %4x, %4x\n", 398 | reboot[1], reboot[0], RebootVector[1], RebootVector[0]); 399 | 400 | /* All is well. Tell DOS how many units and the BPBs... */ 401 | cdprintf("SD initialized on DOS drive %c\n", 402 | rh->drive+'A'); 403 | rh->nunits = 1; rh->bpbtbl = &bpbtbl; 404 | rh->rh.status = DONE; 405 | 406 | if (Debug) 407 | { 408 | cdprintf("SD: BPB data:\n"); 409 | cdprintf("Sector Size: %d ", bpb.sector_size); 410 | cdprintf("Allocation unit: %d\n", bpb.allocation_unit); 411 | cdprintf("Reserved sectors: %d ", bpb.reserved_sectors); 412 | cdprintf("Fat Count: %d\n", bpb.fat_count); 413 | cdprintf("Directory size: %d ", bpb.directory_size); 414 | cdprintf("Total sectors: %d\n", bpb.total_sectors); 415 | cdprintf("Media descriptor: %x ", bpb.media_descriptor); 416 | cdprintf("Fat sectors: %d\n", bpb.fat_sectors); 417 | cdprintf("Track size: %d ", bpb.track_size); 418 | cdprintf("Head count: %d\n", bpb.head_count); 419 | cdprintf("Hidden sectors: %d ", bpb.hidden_sectors); 420 | cdprintf("Sector Ct 32 hex: %L\n", bpb.sector_count); 421 | cdprintf("Partition offset: %L\n", partition_offset); 422 | } 423 | return; 424 | 425 | /* We get here if there are any errors in initialization. In that */ 426 | /* case we can unload this driver completely from memory by setting */ 427 | /* (1) the break address to the starting address, (2) the number of */ 428 | /* units to 0, and (3) the error flag. */ 429 | unload1: 430 | { }; 431 | unload2: 432 | rh->brkadr = MK_FP(_DS, 0); rh->nunits = 0; rh->rh.status = ERROR; 433 | } 434 | 435 | /* iseol - return TRUE if ch is any end of line character */ 436 | PRIVATE BOOLEAN iseol (char ch) 437 | { return ch=='\0' || ch=='\r' || ch=='\n'; } 438 | 439 | /* spanwhite - skip any white space characters in the string */ 440 | PRIVATE char far *spanwhite (char far *p) 441 | { while (*p==' ' || *p=='\t') ++p; return p; } 442 | 443 | /* option_value */ 444 | /* This routine will parse the "=nnn" part of an option. It should */ 445 | /* be called with a text pointer to what we expect to be the '=' char- */ 446 | /* acter. If all is well, it will return the binary value of the arg- */ 447 | /* ument and a pointer to the first non-numeric character. If there is */ 448 | /* a syntax error, then it will return NULL. */ 449 | PRIVATE char far *option_value (char far *p, WORD *v) 450 | { 451 | BOOLEAN null = TRUE; 452 | if (*p++ != '=') return NULL; 453 | for (*v=0; *p>='0' && *p<='9'; ++p) 454 | *v = (*v * 10) + (*p - '0'), null = FALSE; 455 | return null ? NULL : p; 456 | } 457 | 458 | /* parse_options */ 459 | /* This routine will parse our line from CONFIG.SYS and extract the */ 460 | /* driver options from it. The routine returns TRUE if it parsed the */ 461 | /* line successfully, and FALSE if there are any problems. The pointer */ 462 | /* to CONFIG.SYS that DOS gives us actually points at the first char- */ 463 | /* acter after "DEVICE=", so we have to first skip over our own file */ 464 | /* name by searching for a blank. All the option values are stored in */ 465 | /* global variables (e.g. DrivePort, DriveBaud, etc). */ 466 | PRIVATE BOOLEAN parse_options (char far *p) 467 | { 468 | WORD temp; 469 | while (*p!=' ' && *p!='\t' && !iseol(*p)) ++p; 470 | p = spanwhite(p); 471 | while (!iseol(*p)) { 472 | p = spanwhite(p); 473 | if (*p++ != '/') return FALSE; 474 | switch (*p++) { 475 | case 'd', 'D': 476 | Debug = TRUE; 477 | break; 478 | case 'k', 'K': 479 | sd_card_check = 1; 480 | break; 481 | case 'p', 'P': 482 | if ((p=option_value(p,&temp)) == NULL) return FALSE; 483 | if ((temp < 1) || (temp > 4)) 484 | cdprintf("SD: Invalid partition number %x\n",temp); 485 | else 486 | partition_number = temp; 487 | break; 488 | case 'b', 'B': 489 | if ((p=option_value(p,&temp)) == NULL) return FALSE; 490 | if ((temp < 1) || (temp > 5)) 491 | cdprintf("SD: Invalid port base index %x\n",temp); 492 | else 493 | portbase = temp; 494 | break; 495 | default: 496 | return FALSE; 497 | } 498 | p = spanwhite(p); 499 | } 500 | return TRUE; 501 | } 502 | -------------------------------------------------------------------------------- /DRIVER.H: -------------------------------------------------------------------------------- 1 | /* driver.h - MSDOS commands for a device driver.... */ 2 | /* */ 3 | /* Copyright (C) 1994 by Robert Armstrong */ 4 | /* */ 5 | /* This program is free software; you can redistribute it and/or modify */ 6 | /* it under the terms of the GNU General Public License as published by */ 7 | /* the Free Software Foundation; either version 2 of the License, or */ 8 | /* (at your option) any later version. */ 9 | /* */ 10 | /* This program is distributed in the hope that it will be useful, but */ 11 | /* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */ 12 | /* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ 13 | /* Public License for more details. */ 14 | /* */ 15 | /* You should have received a copy of the GNU General Public License */ 16 | /* along with this program; if not, visit the website of the Free */ 17 | /* Software Foundation, Inc., www.gnu.org. */ 18 | 19 | #ifndef _DRIVER_H 20 | #define _DRIVER_H 21 | 22 | 23 | /* NOTE: B implies block device, C implies a character device. */ 24 | /* n.n+ implies DOS version n.n or later only! */ 25 | #define INITIALIZATION 0 /* (BC) initialize driver */ 26 | #define MEDIA_CHECK 1 /* (B) query media changed */ 27 | #define GET_BPB 2 /* (B) BIOS parameter block */ 28 | #define IOCTL_INPUT 3 /* (BC) read IO control data */ 29 | #define INPUT 4 /* (BC) read data */ 30 | #define ND_INPUT 5 /* (C) non-destructive input */ 31 | #define INPUT_STATUS 6 /* (C) query data available */ 32 | #define INPUT_FLUSH 7 /* (C) flush input buffers */ 33 | #define OUTPUT 8 /* (BC) write data */ 34 | #define OUTPUT_VERIFY 9 /* (BC) write data with verify */ 35 | #define OUTPUT_STATUS 10 /* (C) query output busy */ 36 | #define OUTPUT_FLUSH 11 /* (C) flush output buffers */ 37 | #define IOCTL_OUTPUT 12 /* (BC) write IO control data */ 38 | #define DEVICE_OPEN 13 /* (BC) (3.0+) device is to be opened */ 39 | #define DEVICE_CLOSE 14 /* (BC) (3.0+) " " " " closed */ 40 | #define REMOVABLE_MEDIA 15 /* (B) (3.0+) query removable media */ 41 | #define OUTPUT_BUSY 16 /* (C) (3.0+) output data until busy */ 42 | #define GENERIC_IOCTL 19 /* (B) (3.2+) */ 43 | #define GET_LOGICAL 23 /* (BC) (3.2+) */ 44 | #define SET_LOGICAL 24 /* (BC) (3.2+) */ 45 | #define IOCTL_QUERY 25 /* (BC) (5.0+) */ 46 | 47 | /* Flag bits for the request header status word... */ 48 | #define ERROR 0x8000 /* indicates any error condition */ 49 | #define BUSY 0x0200 /* prevents further operations */ 50 | #define DONE 0x0100 /* set on request completion */ 51 | 52 | /* Error codes that may be returned by a device driver... */ 53 | #define WRITE_PROTECT 0x00 /* diskette is write protected */ 54 | #define UNKNOWN_UNIT 0x01 /* unit number does not exist */ 55 | #define NOT_READY 0x02 /* device is not ready */ 56 | #define UNKNOWN_COMMAND 0x03 /* unknown command code */ 57 | #define CRC_ERROR 0x04 /* data check (CRC) error */ 58 | #define HDR_LEN_ERROR 0x05 /* bad request header length */ 59 | #define SEEK_ERROR 0x06 /* seek failure */ 60 | #define UNKNOWN_MEDIA 0x07 /* unknown media (e.g. wrong density) */ 61 | #define BAD_SECTOR 0x08 /* sector not found */ 62 | #define NO_PAPER 0x09 /* printer out of paper */ 63 | #define WRITE_FAULT 0x0A /* write failure */ 64 | #define READ_FAULT 0x0B /* read failure */ 65 | #define GENERAL_FAILURE 0x0C /* general failure */ 66 | #define MEDIA_CHANGED 0x0F /* invalid diskette change */ 67 | 68 | /* Values associated with function 19 - Generic IOCTL... */ 69 | #define SERIAL_DEVICE 0x01 /* device category: any serial device */ 70 | #define CONSOLE_DEVICE 0x03 /* " " : console (display) */ 71 | #define PARALLEL_DEVICE 0x05 /* " " : parallel printer */ 72 | #define DISK_DEVICE 0x08 /* " " : any disk (block) */ 73 | #define SET_PARAMETERS 0x40 /* disk device: set device parameters */ 74 | #define GET_PARAMETERS 0x60 /* " " : get " " " */ 75 | #define WRITE_TRACK 0x41 /* " " : write one track */ 76 | #define READ_TRACK 0x61 /* " " : read " " */ 77 | #define FORMAT_TRACK 0x42 /* " " : format " " */ 78 | #define VERIFY_TRACK 0x62 /* " " : verify " " */ 79 | #define SET_MEDIA_ID 0x46 /* " " : set media id byte */ 80 | #define GET_MEDIA_ID 0x66 /* " " : get " " " */ 81 | #define SENSE_MEDIA 0x68 /* " " : sense media type */ 82 | #define SET_ACCESS 0x67 /* " " : set access allowed flag */ 83 | #define GET_ACCESS 0x47 /* " " : get " " " */ 84 | 85 | struct _devhdr { /* Device header structure... */ 86 | struct _devhdr far *next; /* address of the next device */ 87 | WORD attribute; /* device attribute word */ 88 | void (near *strtgy) (void); /* strategy routine address */ 89 | void (near *intrpt) (void); /* interrupt " " */ 90 | BYTE name[8]; /* device name (blank filled!) */ 91 | }; 92 | typedef struct _devhdr devhdr_t; 93 | 94 | struct _bpb { /* BIOS Parameter block structure... */ 95 | WORD sector_size; /* sector size, in bytes */ 96 | BYTE allocation_unit; /* allocation unit size */ 97 | WORD reserved_sectors; /* number of reserved (boot) sectors */ 98 | BYTE fat_count; /* number of FATs on disk */ 99 | WORD directory_size; /* root directory size, in files */ 100 | WORD total_sectors; /* device size, in sectors */ 101 | BYTE media_descriptor; /* media descriptor code from the BIOS */ 102 | WORD fat_sectors; /* number of sectors per FAT */ 103 | WORD track_size; /* track size, in sectors */ 104 | WORD head_count; /* number of heads */ 105 | LONG hidden_sectors; /* offset of this hard disk partition */ 106 | /* The following device size is used only for disks > 32Mb. In that */ 107 | /* case, the total_sectors field should be zero! */ 108 | LONG sector_count; /* device size, in sectors */ 109 | }; 110 | typedef struct _bpb bpb_t; 111 | 112 | struct _rhfixed { /* Fixed preamble for every request... */ 113 | BYTE length; /* length of the header, in bytes */ 114 | BYTE unit; /* physical unit number requested */ 115 | BYTE command; /* device driver command code */ 116 | WORD status; /* status returned by the driver */ 117 | BYTE reserved[8]; /* reserved (unused) bytes */ 118 | }; 119 | typedef struct _rhfixed rh_t; 120 | 121 | /* NOTE: count is in _bytes_ for character type device drivers, and */ 122 | /* in _sectors_ for block type drivers!! */ 123 | 124 | typedef near * (bpbtbl_t[]); 125 | struct _rh_init { /* INITIALIZATION(0) */ 126 | rh_t rh; /* fixed portion of the header */ 127 | BYTE nunits; /* number of units supported by driver */ 128 | BYTE far *brkadr; /* break address (memory used) */ 129 | bpbtbl_t far *bpbtbl; /* pointer to array of BPBs */ 130 | BYTE drive; /* first available drive number */ 131 | }; 132 | typedef struct _rh_init rh_init_t; 133 | 134 | struct _rh_media_check { /* MEDIA_CHECK(1) */ 135 | rh_t rh; /* fixed portion of the request */ 136 | BYTE media_type; /* media descriptor byte from BIOS */ 137 | BYTE media_status; /* new media status flags */ 138 | BYTE far *volume_id; /* pointer to volume ID string */ 139 | }; 140 | typedef struct _rh_media_check rh_media_check_t; 141 | 142 | struct _rh_get_bpb { /* GET_BPB(2) */ 143 | rh_t rh; /* fixed portion of the request */ 144 | BYTE media_type; /* media descriptor byte from BIOS */ 145 | BYTE far *dta; /* address for data transfer */ 146 | bpb_t far *bpb; /* pointer to the BPB */ 147 | }; 148 | typedef struct _rh_get_bpb rh_get_bpb_t; 149 | 150 | struct _rh_ioctl { /* IOCTL_INPUT(3), IOCTL_OUTPUT(12) */ 151 | rh_t rh; /* fixed portion of the request */ 152 | BYTE media_type; /* media descriptor byte from BIOS */ 153 | BYTE far *dta; /* address for data transfer */ 154 | WORD count; /* transfer count (bytes or sectors) */ 155 | WORD start; /* starting sector number */ 156 | }; 157 | typedef struct _rh_ioctl rh_ioctl_t; 158 | 159 | struct _rh_io { /* INPUT(4),OUTPUT(8),OUTPUT_VERIFY(9) */ 160 | rh_t rh; /* fixed portion of the request */ 161 | BYTE media_type; /* media descriptor byte from BIOS */ 162 | BYTE far *dta; /* address for data transfer */ 163 | WORD count; /* transfer count (bytes or sectors) */ 164 | WORD start; /* starting sector number */ 165 | BYTE far *volume_id; /* address of volume ID string */ 166 | }; 167 | typedef struct _rh_io rh_io_t; 168 | 169 | struct _rh_ndinput { /* ND_INPUT(5) */ 170 | rh_t rh; /* fixed portion of the request */ 171 | BYTE ch; /* next input character (returned) */ 172 | }; 173 | typedef struct _rh_ndinput rh_ndinput_t; 174 | 175 | /* INPUT_STATUS(6) has only a request header... */ 176 | /* INPUT_FLUSH(7) " " " " " ... */ 177 | /* OUTPUT_STATUS(10) " " " " " ... */ 178 | /* OUTPUT_FLUSH(11) " " " " " ... */ 179 | /* DEVICE_OPEN(13) " " " " " ... */ 180 | /* DEVICE_CLOSE(14) " " " " " ... */ 181 | /* REMOVABLE_MEDIA(15) " " " " " ... */ 182 | 183 | struct _rh_output_busy { /* OUTPUT_BUSY(16) */ 184 | rh_t rh; /* fixed portion of the request */ 185 | BYTE media_type; /* media descriptor byte from BIOS */ 186 | BYTE far *dta; /* address for data transfer */ 187 | WORD count; /* transfer count (bytes or sectors) */ 188 | }; 189 | typedef struct _rh_output_busy rh_output_busy_t; 190 | 191 | struct _rh_generic_ioctl { /* GENERIC_IOCTL(19), IOCTL_QUERY(25) */ 192 | rh_t rh; /* fixed portion of the request */ 193 | BYTE major, minor; /* function code - major and minor */ 194 | WORD si, di; /* caller's SI and DI registers */ 195 | BYTE far *packet; /* address of IOCTL data packet */ 196 | }; 197 | typedef struct _rh_generic_ioctl rh_generic_ioctl_t; 198 | 199 | struct _rh_logical { /* GET_LOGICAL(23), SET_LOGICAL(24) */ 200 | rh_t rh; /* fixed portion of the request */ 201 | BYTE io; /* unit code or last device */ 202 | BYTE command; /* command code */ 203 | WORD status; /* resulting status */ 204 | LONG reserved; /* reserved (unused) */ 205 | }; 206 | typedef struct _rh_logical rh_logical_t; 207 | 208 | struct _device_params { /* Generic IOCTL, Get/Set Parameters */ 209 | BYTE special; /* special functions and flags */ 210 | BYTE form_factor; /* device (not media!) form factor */ 211 | WORD attributes; /* physical drive attributes */ 212 | WORD cylinders; /* number of cylinders */ 213 | BYTE media_type; /* media type (not the media id!) */ 214 | bpb_t bpb; /* the entire BIOS parameter block! */ 215 | WORD layout[]; /* track layout map */ 216 | }; 217 | typedef struct _device_params device_params_t; 218 | 219 | struct _access_flag { /* Generic IOCTL Get/Set access allowed */ 220 | BYTE special; /* special functions and flags */ 221 | BYTE allowed; /* non-zero if access is allowed */ 222 | }; 223 | typedef struct _access_flag access_flag_t; 224 | 225 | struct _media_type { /* Generic IOCTL Get media type */ 226 | BYTE default_media; /* 1 for default media type */ 227 | BYTE form_factor; /* media (not device!) form factor */ 228 | }; 229 | typedef struct _media_type media_type_t; 230 | 231 | #endif 232 | -------------------------------------------------------------------------------- /HEADER.ASM: -------------------------------------------------------------------------------- 1 | PAGE 60, 132 2 | TITLE HEADER - Interface for C Device Drivers 3 | SUBTTL Bob Armstrong [23-Jul-94] 4 | 5 | 6 | 7 | ; header.asm - MSDOS device driver header... 8 | ; 9 | ; Copyright (C) 1994 by Robert Armstrong 10 | ; 11 | ; This program is free software; you can redistribute it and/or modify 12 | ; it under the terms of the GNU General Public License as published by 13 | ; the Free Software Foundation; either version 2 of the License, or 14 | ; (at your option) any later version. 15 | ; 16 | ; This program is distributed in the hope that it will be useful, but 17 | ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- 18 | ; ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 19 | ; Public License for more details. 20 | ; 21 | ; You should have received a copy of the GNU General Public License 22 | ; along with this program; if not, visit the website of the Free 23 | ; Software Foundation, Inc., www.gnu.org. 24 | 25 | 26 | ; This module receives the DOS device driver calls (there are only two!) 27 | ; and sets up an environment suitable for executing C code. The original 28 | ; DOS device driver request is then handed off to the C code. 29 | ; 30 | ; There are two requirements that drive the memory layout of a device 31 | ; driver: (1) DOS requires the device header to be the first thing in the 32 | ; memory image, and (2) it is desirable to free the memory allocated to 33 | ; startup code once the driver has been initialized. These requirements 34 | ; force a memory layout like this diagram: 35 | ; 36 | ; DS, SS -> +----------------------+ <- driver load address 37 | ; | | 38 | ; | (device header) | 39 | ; | | 40 | ; \ _DATA \ 41 | ; | | 42 | ; |----------------------| 43 | ; | | 44 | ; \ _BSS \ 45 | ; | | 46 | ; | (stack) | <- C language stack 47 | ; | | 48 | ; CS -> |----------------------| 49 | ; | | 50 | ; \ _TEXT \ 51 | ; | | <- driver break address 52 | ; | | 53 | ; \ (startup code) \ 54 | ; | | 55 | ; +----------------------+ 56 | ; 57 | ; This is very similar to the TINY (.COM file) model _except_ that the 58 | ; code is at the end of the memory image rather than the start. This 59 | ; turns out to be a major problem because the code generated by TURBO 60 | ; assumes that the CS register contains the start of the _TEXT segment 61 | ; and NOT the DGROUP. This is contrary to the documentation in the 62 | ; Borland manual and _I_ think it's a bug. Note that this bug is 63 | ; asymptomatic in .COM files because the _TEXT segment is normally the 64 | ; first thing in the DGROUP. 65 | ; 66 | ; To get around this problem we use the SMALL model (i.e. CS != DS). 67 | ; Trouble is, when this driver is loaded the only thing we know is the 68 | ; address of the start of the driver and that's in the CS register. 69 | ; This means that we have to calculate an appropriate CS value to use 70 | ; in the C code. 71 | ; 72 | ; Another unrelated issue is the stack size. The stack DOS uses when 73 | ; a driver is called has room for about 20 PUSHes. This isn't enough 74 | ; for any serious C code, so we only use the DOS stack to save all the 75 | ; registers. After that we switch to our own stack, which is located 76 | ; in the _BSS segment. Of course we have to put the DOS stack pointer 77 | ; back before returning. 78 | ; 79 | ; In order to ensure that the device header appears first in the load 80 | ; image it must be defined in this module. We also define the BIOS 81 | ; Parameter Block (BPB) and the table of BPB pointers in this module. 82 | ; These things are unfortunate because those parts of this file must be 83 | ; modified for each different driver. 84 | ; 85 | ; The only interface between this module and the C code is a single 86 | ; far pointer which must be exported by the C code. This pointer would 87 | ; be declared as: 88 | ; 89 | ; void (far *c_driver) (rh_t far *); 90 | ; 91 | ; The offset part (first word!) of this pointer must be initialized at 92 | ; link time with the offset (relative to _TEXT) of the routine that 93 | ; will handle DOS driver calls. The segment part of this pointer is 94 | ; filled in by this module with the CS value we compute before the 95 | ; first call. The C driver routine is passed a far pointer to the 96 | ; DOS request header. Everything else is up to the C code. 97 | ; 98 | ; Bob Armstrong [22-July-1994] 99 | 100 | .8086 101 | 102 | ; This is the size of the new stack we create for the C code... 103 | STACKSIZE EQU 256 ; use whatever seems appropriate... 104 | 105 | 106 | ; This DGROUP, and the order the segments appear in this module, will 107 | ; force the load order to be as described above! 108 | DGROUP GROUP _DATA, _BSS, _TEXT 109 | 110 | 111 | ; The _DATA segment (initialized data) for this module contains the 112 | ; MSDOS device header and the BIOS parameter blocks... 113 | _DATA SEGMENT WORD PUBLIC 'DATA' 114 | PUBLIC _header, _bpb, _bpbtbl 115 | 116 | ; Header attribute bits for block devices: 117 | ; 118 | ; 0002H (B1) - 32 bit sector addresses 119 | ; 0040H (B6) - Generic IOCTL, Get/Set logical device 120 | ; 0080H (B7) - IOCTL Query 121 | ; 0800H (B11) - Open/Close device, Removable media 122 | ; 2000H (B13) - IBM format 123 | ; 4000H (B14) - IOCTL read/write 124 | ; 8000H (B15) - zero for block device! 125 | 126 | ; The DOS device header... 127 | _header DD -1 ; link to the next device 128 | DW 00C0H ; block device, non-IBM format, generic IOCTL 129 | DW DGROUP:STRATEGY ; address of the strategy routine 130 | DW DGROUP:INTERRUPT; " " " interrupt " 131 | DB 1 ; number of drives 132 | DB 'SDCDv10' ; DOS doesn't really use these bytes 133 | 134 | ; The geometry (sectors/track, tracks/cylinder) defined in the BPB is rather 135 | ; arbitrary in the case of the TU58, but there are things to watch out for. 136 | ; First, the DOS FORMAT program has a bug which causes it to crash or worse 137 | ; yet, exit silently without doing anything for devices with large numbers of 138 | ; sectors per track. Experiments show that 64 sectors/track is safe (at least 139 | ; for DOS v5) but 128 is not. Second, the DRIVER.C module must calculate the 140 | ; number of cylinders by cylinders = device_size / (sectors * heads). This 141 | ; value must always come out to an integer. 142 | 143 | ; The BIOS Parameter Block... 144 | _bpb DW 512 ; sector size 145 | DB 1 ; cluster size 146 | DW 1 ; number of reserved sectors 147 | DB 2 ; number of FAT copies 148 | DW 48 ; number of files in root directory 149 | DW 512 ; total number of sectors 150 | DB 0F0H ; media descriptor 151 | DW 2 ; number of sectors per FAT 152 | DW 64 ; sectors per track 153 | DW 2 ; number of heads 154 | DD 0 ; number of hidden sectors 155 | DD 0 ; large sector count 156 | 157 | ; This table contains one BPB pointer for each physical drive... 158 | _bpbtbl DW DGROUP:_bpb ; the BPB for unit 0 159 | ; DW DGROUP:_bpb ; " " " " 1 160 | 161 | ; This doubleword points to the entry point of the C driver code... 162 | EXTRN _c_driver:WORD 163 | 164 | _DATA ENDS 165 | 166 | 167 | ; The _BSS segment contains uninitialized static data... 168 | _BSS SEGMENT WORD PUBLIC 'BSS' 169 | PUBLIC _STACK 170 | 171 | ; Local variables for this module... 172 | RHPTR DW 2 DUP (?) ; offset and segment of the request header 173 | OLDSTK DW 2 DUP (?) ; original stack offset and segment 174 | 175 | ; This is the stack while the C code is running... 176 | DB STACKSIZE DUP (?) 177 | _STACK LABEL WORD ; the address of a stack is at its _top_ ! 178 | 179 | _BSS ENDS 180 | 181 | 182 | ; WARNING! The _TEXT segment must be paragraph aligned! 183 | _TEXT SEGMENT PARA PUBLIC 'CODE' 184 | ASSUME CS:DGROUP, DS:DGROUP, SS:DGROUP, ES:NOTHING 185 | 186 | 187 | ; These words give the offsets of the _TEXT segment and the stack, both 188 | ; relative to the DGROUP. Note that you can't define use "DGROUP:_TEXT" 189 | ; as this generates a segment relocation record in the .EXE file which 190 | ; will prevent you from converting to a .COM file. The way its written 191 | ; works, but assumes that it is the first thing in this module. 192 | CSOFF DW DGROUP:CSOFF 193 | NEWSTK DW DGROUP:_STACK 194 | 195 | 196 | ; This is the entry for the STRATEGY procedure. DOS calls this routine 197 | ; with the address of the request header, but all the work is expected 198 | ; to occur in the INTERRUPT procedure. All we do here is to save the 199 | ; address of the request for later processing. Since this function is 200 | ; trivial, we never call any C code... 201 | PUBLIC STRATEGY 202 | STRATEGY PROC FAR 203 | MOV CS:RHPTR,BX ; save the request header offset 204 | MOV CS:RHPTR+2,ES ; and its segment 205 | RET ; that's all there is to do 206 | STRATEGY ENDP 207 | 208 | 209 | ; And now the INTERRUPT routine. This routine has to save all the 210 | ; registers, switch to the new stack, set up the segment registers, 211 | ; and call the C language driver routine while passing it the address 212 | ; of the request header (saved by the strategy routine). 213 | PUBLIC INTERRUPT 214 | INTERRUPT PROC FAR 215 | PUSH DS ES AX BX CX DX DI SI BP 216 | 217 | CLI ; interrupts OFF while the stack is unsafe! 218 | MOV CS:OLDSTK+2,SS ; save the current stack pointer 219 | MOV CS:OLDSTK,SP ; ... 220 | MOV BX,CS ; then setup the new stack 221 | MOV SS,BX ; ... 222 | MOV SP,NEWSTK ; ... 223 | STI ; interrupts are safe once again 224 | MOV AX,CSOFF ; compute the correct code segment address 225 | SHR AX,4 ; ... for the _TEXT segment 226 | ADD AX,BX ; ... 227 | MOV _c_driver+2,AX ; fix the pointer appropriately 228 | MOV DS,BX ; setup DS for the C code 229 | CLD ; the C code will assume this state 230 | 231 | PUSH CS:RHPTR+2 ; pass the request header 232 | PUSH CS:RHPTR ; address as a parameter 233 | CALL DWORD PTR _c_driver; call the C entry point 234 | 235 | CLI ; interrupts OFF once again 236 | MOV SS,CS:OLDSTK+2 ; and restore the original stack 237 | MOV SP,CS:OLDSTK ; ... 238 | STI ; ... 239 | 240 | POP BP SI DI DX CX BX AX ES DS 241 | RET 242 | INTERRUPT ENDP 243 | 244 | 245 | _TEXT ENDS 246 | 247 | END 248 | -------------------------------------------------------------------------------- /INTEGER.H: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------*/ 2 | /* Integer type definitions for FatFs module */ 3 | /*-------------------------------------------*/ 4 | 5 | #ifndef _INTEGER 6 | #define _INTEGER 7 | 8 | #ifdef _WIN32 /* FatFs development platform */ 9 | 10 | #include 11 | #include 12 | 13 | #else /* Embedded platform */ 14 | 15 | /* These types must be 16-bit, 32-bit or larger integer */ 16 | typedef int INT; 17 | typedef unsigned int UINT; 18 | 19 | /* These types must be 8-bit integer */ 20 | typedef char CHAR; 21 | typedef unsigned char UCHAR; 22 | typedef unsigned char BYTE; 23 | 24 | /* These types must be 16-bit integer */ 25 | typedef short SHORT; 26 | typedef unsigned short USHORT; 27 | typedef unsigned short WORD; 28 | typedef unsigned short WCHAR; 29 | 30 | /* These types must be 32-bit integer */ 31 | #ifndef DEFINEDLONG 32 | #define DEFINEDLONG 33 | typedef long LONG; 34 | #endif 35 | typedef unsigned long ULONG; 36 | typedef unsigned long DWORD; 37 | 38 | #endif 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /MAKEFILE: -------------------------------------------------------------------------------- 1 | # Makefile for SDPP driver [2024] 2 | 3 | CC=bcc -c -ms -Z -O -Ol -Oe 4 | ASM=tasm -mx 5 | 6 | .c.obj: 7 | $(CC) $< 8 | 9 | .asm.obj: 10 | $(ASM) $* 11 | 12 | sd.sys: header.obj cprint.obj sd.obj sdmm.obj driver.obj 13 | tlink -t -m -s -n header cprint sd sdmm driver, sd.sys 14 | 15 | sd.com: header.obj cprint.obj sd.obj sdmm.obj driver.obj 16 | tlink -t -m -s -n header cprint sd sdmm driver, sd.sys 17 | rename sd.sys sd.com 18 | 19 | clean: 20 | del *.obj 21 | del sd.sys 22 | 23 | driver.obj: cprint.c sdmm.c driver.c cprint.c cprint.h standard.h driver.h sd.h 24 | sd.obj: sd.c sd.h standard.h driver.h 25 | sdmm.obj: sdmm.c diskio.h integer.h 26 | header.obj: header.asm 27 | -------------------------------------------------------------------------------- /SD.C: -------------------------------------------------------------------------------- 1 | /* sd.c */ 2 | /* */ 3 | /* Copyright (C) 1994 by Robert Armstrong */ 4 | /* */ 5 | /* This program is free software; you can redistribute it and/or modify */ 6 | /* it under the terms of the GNU General Public License as published by */ 7 | /* the Free Software Foundation; either version 2 of the License, or */ 8 | /* (at your option) any later version. */ 9 | /* */ 10 | /* This program is distributed in the hope that it will be useful, but */ 11 | /* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */ 12 | /* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ 13 | /* Public License for more details. */ 14 | /* */ 15 | /* You should have received a copy of the GNU General Public License */ 16 | /* along with this program; if not, visit the website of the Free */ 17 | /* Software Foundation, Inc., www.gnu.org. */ 18 | /* */ 19 | /* */ 20 | /* The functions provided are: */ 21 | /* */ 22 | /* SDInitialize - establish two way communications with the drive */ 23 | /* SDRead - read one 512 byte logical block from the tape */ 24 | /* SDWrite - write one 512 byte logical block to the tape */ 25 | /* SDMediaCheck - see if card detect has changed */ 26 | /* */ 27 | /* Normally the SDInitialize routine would be called */ 28 | /* during the DOS device driver initialization, and then the SDRead and */ 29 | /* */ 30 | 31 | #include /* needed for NULL, etc */ 32 | #include /* memset, memcopy, etc */ 33 | #include "standard.h" /* all definitions for this project */ 34 | #include "sd.h" /* device protocol and data defintions */ 35 | #include "diskio.h" /* stuff from sdmm.c module */ 36 | #include "driver.h" 37 | #include "cprint.h" 38 | 39 | DWORD partition_offset = 0; 40 | 41 | /* FatFs refers the members in the FAT structures as byte array instead of 42 | / structure member because the structure is not binary compatible between 43 | / different platforms */ 44 | 45 | #define BS_jmpBoot 0 /* Jump instruction (3) */ 46 | #define BS_OEMName 3 /* OEM name (8) */ 47 | #define BPB_BytsPerSec 11 /* Sector size [byte] (2) */ 48 | #define BPB_SecPerClus 13 /* Cluster size [sector] (1) */ 49 | #define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (2) */ 50 | #define BPB_NumFATs 16 /* Number of FAT copies (1) */ 51 | #define BPB_RootEntCnt 17 /* Number of root directory entries for FAT12/16 (2) */ 52 | #define BPB_TotSec16 19 /* Volume size [sector] (2) */ 53 | #define BPB_Media 21 /* Media descriptor (1) */ 54 | #define BPB_FATSz16 22 /* FAT size [sector] (2) */ 55 | #define BPB_SecPerTrk 24 /* Track size [sector] (2) */ 56 | #define BPB_NumHeads 26 /* Number of heads (2) */ 57 | #define BPB_HiddSec 28 /* Number of special hidden sectors (4) */ 58 | #define BPB_TotSec32 32 /* Volume size [sector] (4) */ 59 | #define BS_DrvNum 36 /* Physical drive number (2) */ 60 | #define BS_BootSig 38 /* Extended boot signature (1) */ 61 | #define BS_VolID 39 /* Volume serial number (4) */ 62 | #define BS_VolLab 43 /* Volume label (8) */ 63 | #define BS_FilSysType 54 /* File system type (1) */ 64 | #define BPB_FATSz32 36 /* FAT size [sector] (4) */ 65 | #define BPB_ExtFlags 40 /* Extended flags (2) */ 66 | #define BPB_FSVer 42 /* File system version (2) */ 67 | #define BPB_RootClus 44 /* Root directory first cluster (4) */ 68 | #define BPB_FSInfo 48 /* Offset of FSINFO sector (2) */ 69 | #define BPB_BkBootSec 50 /* Offset of backup boot sector (2) */ 70 | #define BS_DrvNum32 64 /* Physical drive number (2) */ 71 | #define BS_BootSig32 66 /* Extended boot signature (1) */ 72 | #define BS_VolID32 67 /* Volume serial number (4) */ 73 | #define BS_VolLab32 71 /* Volume label (8) */ 74 | #define BS_FilSysType32 82 /* File system type (1) */ 75 | #define FSI_LeadSig 0 /* FSI: Leading signature (4) */ 76 | #define FSI_StrucSig 484 /* FSI: Structure signature (4) */ 77 | #define FSI_Free_Count 488 /* FSI: Number of free clusters (4) */ 78 | #define FSI_Nxt_Free 492 /* FSI: Last allocated cluster (4) */ 79 | #define MBR_Table 446 /* MBR: Partition table offset (2) */ 80 | #define SZ_PTE 16 /* MBR: Size of a partition table entry */ 81 | #define BS_55AA 510 /* Boot sector signature (2) */ 82 | 83 | 84 | BYTE local_buffer[BLOCKSIZE]; 85 | 86 | #define LD_WORD(x) *((WORD *)(BYTE *)(x)) 87 | #define LD_DWORD(x) *((DWORD *)(BYTE *)(x)) 88 | 89 | /*-----------------------------------------------------------------------*/ 90 | /* Load a sector and check if it is an FAT boot sector */ 91 | /*-----------------------------------------------------------------------*/ 92 | 93 | static 94 | BYTE check_fs (BYTE unit, 95 | /* 0:FAT boor sector, 1:Valid boor sector but not FAT, */ 96 | /* 2:Not a boot sector, 3:Disk error */ 97 | DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ 98 | ) 99 | { 100 | if (disk_read (unit, local_buffer, sect, 1) != RES_OK) 101 | return 3; 102 | if (LD_WORD(&local_buffer[BS_55AA]) != 0xAA55) /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */ 103 | return 2; 104 | if ((LD_DWORD(&local_buffer[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ 105 | return 0; 106 | if ((LD_DWORD(&local_buffer[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ 107 | return 0; 108 | return 1; 109 | } 110 | 111 | 112 | /*-----------------------------------------------------------------------*/ 113 | /* Find logical drive and check if the volume is mounted */ 114 | /*-----------------------------------------------------------------------*/ 115 | 116 | static 117 | int find_volume ( 118 | BYTE unit, 119 | BYTE partno, 120 | bpb_t *bpb 121 | ) 122 | { 123 | BYTE fmt; 124 | DSTATUS stat; 125 | WORD secsize; 126 | DWORD bsect; 127 | 128 | stat = disk_status(unit); 129 | if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ 130 | return 0; /* The file system object is valid */ 131 | } 132 | 133 | /* The file system object is not valid. */ 134 | /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */ 135 | 136 | stat = disk_initialize(unit); /* Initialize the physical drive */ 137 | if (stat & STA_NOINIT) /* Check if the initialization succeeded */ 138 | return -1; /* Failed to initialize due to no medium or hard error */ 139 | 140 | /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */ 141 | bsect = 0; 142 | fmt = check_fs(unit, bsect); /* Load sector 0 and check if it is an FAT boot sector as SFD */ 143 | if (fmt == 1 || (!fmt && (partno))) { /* Not an FAT boot sector or forced partition number */ 144 | UINT i; 145 | DWORD br[4]; 146 | 147 | for (i = 0; i < 4; i++) { /* Get partition offset */ 148 | BYTE *pt = &local_buffer[MBR_Table + i * SZ_PTE]; 149 | br[i] = pt[4] ? LD_DWORD(&pt[8]) : 0; 150 | } 151 | i = partno; /* Partition number: 0:auto, 1-4:forced */ 152 | if (i) i--; 153 | do { /* Find an FAT volume */ 154 | bsect = br[i]; 155 | fmt = bsect ? check_fs(unit, bsect) : 2; /* Check the partition */ 156 | } while (!partno && fmt && ++i < 4); 157 | } 158 | if (fmt == 3) return -2; /* An error occured in the disk I/O layer */ 159 | if (fmt) return -3; /* No FAT volume is found */ 160 | 161 | /* An FAT volume is found. Following code initializes the file system object */ 162 | 163 | secsize = LD_WORD(local_buffer + BPB_BytsPerSec); 164 | if (secsize != BLOCKSIZE) 165 | return -3; 166 | 167 | bpb->sector_size = BLOCKSIZE; 168 | bpb->allocation_unit = local_buffer[BPB_SecPerClus]; /* Number of sectors per cluster */ 169 | bpb->reserved_sectors = LD_WORD(local_buffer+BPB_RsvdSecCnt); /* Number of reserved sectors */ 170 | if (!bpb->reserved_sectors) return -3; /* (Must not be 0) */ 171 | bpb->fat_count = local_buffer[BPB_NumFATs]; /* Number of FAT copies */ 172 | if (bpb->fat_count == 0) 173 | bpb->fat_count = 2; 174 | bpb->directory_size = LD_WORD(local_buffer+BPB_RootEntCnt); 175 | bpb->total_sectors = LD_WORD(local_buffer+BPB_TotSec16); 176 | if (!bpb->total_sectors) 177 | bpb->sector_count = LD_DWORD(local_buffer+BPB_TotSec32); 178 | else 179 | bpb->sector_count = bpb->total_sectors; 180 | bpb->media_descriptor = local_buffer[BPB_Media]; 181 | bpb->fat_sectors = LD_WORD(local_buffer+BPB_FATSz16); /* Number of sectors per FAT */ 182 | bpb->track_size = LD_WORD(local_buffer+BPB_SecPerTrk); /* Number of sectors per cluster */ 183 | bpb->head_count = LD_WORD(local_buffer+BPB_NumHeads); /* Number of sectors per cluster */ 184 | bpb->hidden_sectors = 1; 185 | 186 | partition_offset = bsect; 187 | return 0; 188 | } 189 | 190 | 191 | /* SDInitialize */ 192 | PUBLIC BOOLEAN SDInitialize (BYTE unit, BYTE partno, bpb_t *bpb) 193 | { 194 | if (find_volume(unit,partno,bpb) < 0) 195 | return FALSE; 196 | return TRUE; 197 | } 198 | 199 | /* SDMediaCheck */ 200 | PUBLIC BOOLEAN SDMediaCheck (BYTE unit) 201 | { 202 | return (disk_result(unit) == RES_OK) ? FALSE : TRUE; 203 | } 204 | 205 | /* SDRead */ 206 | /* IMPORTANT! Blocks are always 512 bytes! Never more, never less. */ 207 | /* */ 208 | /* INPUTS: */ 209 | /* unit - selects tape drive 0 (left) or 1 (right) */ 210 | /* lbn - logical block number to be read [0..511] */ 211 | /* buffer - address of 512 bytes to receive the data read */ 212 | /* */ 213 | /* RETURNS: operation status as reported by the TU58 */ 214 | /* */ 215 | PUBLIC int SDRead (WORD unit, WORD lbn, BYTE far *buffer, WORD count) 216 | { 217 | return disk_read (unit, buffer, lbn + partition_offset, count); 218 | } 219 | 220 | 221 | /* SDWrite */ 222 | /* IMPORTANT! Blocks are always 512 bytes! Never more, never less. */ 223 | /* */ 224 | /* INPUTS: */ 225 | /* unit - selects tape drive 0 (left) or 1 (right) */ 226 | /* lbn - logical block number to be read [0..511] */ 227 | /* buffer - address of 512 bytes containing the data to write */ 228 | /* verify - TRUE to ask the TU58 for a verification pass */ 229 | /* */ 230 | /* RETURNS: operation status as reported by the TU58 */ 231 | /* */ 232 | PUBLIC int SDWrite (WORD unit, WORD lbn, BYTE far *buffer, WORD count) 233 | { 234 | return disk_write (unit, buffer, lbn + partition_offset, count); 235 | } 236 | 237 | -------------------------------------------------------------------------------- /SD.H: -------------------------------------------------------------------------------- 1 | /* sd.h - SD card driver glue */ 2 | 3 | #ifndef _SD_H 4 | #define _SD_H 5 | 6 | #include "integer.h" 7 | #include "driver.h" 8 | 9 | #define BLOCKSIZE 512 10 | 11 | extern DWORD partition_offset; 12 | 13 | /* SDInitialize - establish two way communications with the drive */ 14 | BOOLEAN SDInitialize (BYTE unit, BYTE partno, bpb_t *bpb); 15 | /* SDRead - read one 512 byte logical block from the tape */ 16 | int SDRead (WORD, WORD, BYTE far *, WORD count); 17 | /* SDWrite - write one 512 byte logical block to the tape */ 18 | int SDWrite (WORD, WORD, BYTE far *, WORD count); 19 | /* SDMediaCheck - check if media changed */ 20 | BOOLEAN SDMediaCheck (BYTE unit); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /SDMM.C: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------/ 2 | / Foolproof MMCv3/SDv1/SDv2 (in SPI mode) control module 3 | /-------------------------------------------------------------------------/ 4 | / 5 | / Copyright (C) 2013, ChaN, all right reserved. 6 | / Modified in 2024 by nilseuropa. 7 | / 8 | / * This software is a free software and there is NO WARRANTY. 9 | / * No restriction on use. You can use, modify and redistribute it for 10 | / personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. 11 | / * Redistributions of source code must retain the above copyright notice. 12 | / 13 | /-------------------------------------------------------------------------/ 14 | Features and Limitations: 15 | 16 | * Easy to Port Bit-banging SPI 17 | It uses only four GPIO pins. No interrupt, no SPI is needed. 18 | 19 | * Platform Independent 20 | You need to modify only a few macros to control GPIO ports. 21 | 22 | * Low Speed 23 | The data transfer rate will be several times slower than hardware SPI. 24 | 25 | * No Media Change Detection 26 | Application program needs to perform f_mount() after media change. 27 | 28 | * Outport D3-D7 powers the SD card 29 | 30 | /-------------------------------------------------------------------------*/ 31 | 32 | #include 33 | 34 | #include "diskio.h" /* Common include file for FatFs and disk I/O layer */ 35 | #include "cprint.h" 36 | 37 | /*-------------------------------------------------------------------------*/ 38 | /* Platform dependent macros and functions needed to be modified */ 39 | /*-------------------------------------------------------------------------*/ 40 | 41 | #define outportbyte(a,b) outp(a,b) 42 | #define inportbyte(a) inp(a) 43 | 44 | static WORD portbases[5] = {0x3BC,0x378,0x278,0x3E8,0x2E8}; 45 | 46 | BYTE sd_card_check = 0; 47 | BYTE portbase = 2; 48 | 49 | WORD OUTPORT=0x378; 50 | WORD STATUSPORT=0x379; 51 | WORD CONTROLPORT=0x37A; 52 | 53 | /* Potential power outputs are the control DIOs, those are inverted lines */ 54 | #define VAR_INIT() {STATUSPORT=OUTPORT+1; CONTROLPORT=OUTPORT+2; outportbyte(CONTROLPORT,0x00); } 55 | 56 | /* STATUSPORT channels */ 57 | 58 | /* MISOPIN (SD card DAT0/DO pin 7) is PPORT SELECT (DB-25 pin 13) */ 59 | #define MISOPIN (0x01 << 4) 60 | /* Card Detect (N/A) is PPORT BUSY (DB-25 pin 11) */ 61 | #define CDDETECTPIN (0x01 << 7) 62 | 63 | /* OUTPORT channels */ 64 | 65 | /* MOSI (SD card CMD/DI pin 2) is PPORT D0 (DB-25 pin 2) */ 66 | #define MOSIPIN (0x01 << 0) 67 | /* CLOCK (SD card CLK/SCLK pin 5) is PPORT D1 (DB-25 pin 3) */ 68 | #define CLOCKPIN (0x01 << 1) 69 | /* Chip Select (SD card CAT3/CS pin 1) is PPORT D2 (DB-25 pin 4) */ 70 | #define CSPIN (0x01 << 2) 71 | /* Connect ground to one of PPORT pins 18-25 */ 72 | 73 | /* Data channels as Power outputs */ 74 | 75 | /* PWRPINs are PPORT D3-D7 (DB-25 pin 5-9) */ 76 | #define PWRPIN1 (0x01 << 3) 77 | #define PWRPIN2 (0x01 << 4) 78 | #define PWRPIN3 (0x01 << 5) 79 | #define PWRPIN4 (0x01 << 6) 80 | #define PWRPIN5 (0x01 << 7) 81 | 82 | #define POWER 0XF8 // Data Power Pins mask 83 | 84 | #if 1 85 | #define TOUTCHR(x) 86 | #define TOUTHEX(x) 87 | #define TOUTWORD(x) 88 | #else 89 | #define TOUTCHR(x) toutchr(x) 90 | #define TOUTHEX(x) touthex(x) 91 | #define TOUTWORD(x) toutword(x) 92 | 93 | static BYTE toutchr (unsigned char ch) 94 | { 95 | _DI = _SI = 0; 96 | _AL = ch; _AH = 0xE; _BX = 0; 97 | asm INT 0x10; 98 | return 0; 99 | } 100 | 101 | static BYTE touthex(unsigned char c) 102 | { 103 | char d = c >> 4; 104 | toutchr(d > 9 ? d+('A'-10) : d+'0'); 105 | d = c & 0x0F; 106 | toutchr(d > 9 ? d+('A'-10) : d+'0'); 107 | return 0; 108 | } 109 | 110 | static BYTE toutword(WORD x) 111 | { 112 | touthex(x >> 8); 113 | touthex(x); 114 | return 0; 115 | } 116 | 117 | #endif 118 | 119 | void setportbase(BYTE val) 120 | { 121 | if ((val >= 1) && (val <= (sizeof(portbases)/sizeof(portbases[0])) )) 122 | OUTPORT = portbases[val-1]; 123 | VAR_INIT(); 124 | } 125 | 126 | #define DO_INIT() 127 | #define DI_INIT() 128 | #define CK_INIT() 129 | #define CS_INIT() 130 | 131 | const int bit_delay_val = 10; 132 | 133 | #define BITDLY() 134 | 135 | #define DO (inportbyte(STATUSPORT) & MISOPIN) 136 | #define WDO (TOUTCHR(RDO ? '1 ' : '0')+RDO) 137 | #define CDDETECT() (inportbyte(STATUSPORT) & CDDETECTPIN) 138 | 139 | #define CLOCKBITHIGHMOSIHIGH() outportbyte(OUTPORT,POWER|MOSIPIN|CLOCKPIN) 140 | #define CLOCKBITHIGHMOSILOW() outportbyte(OUTPORT,POWER|CLOCKPIN) 141 | #define CLOCKBITLOWMOSIHIGH() outportbyte(OUTPORT,POWER|MOSIPIN) 142 | #define CLOCKBITLOWMOSILOW() outportbyte(OUTPORT,POWER) 143 | #define CLOCKBITHIGHMOSIHIGHNOCS() outportbyte(OUTPORT,POWER|MOSIPIN|CLOCKPIN|CSPIN) 144 | #define CLOCKBITLOWMOSIHIGHNOCS() outportbyte(OUTPORT,POWER|MOSIPIN|CSPIN) 145 | #define CS_L() outportbyte(OUTPORT,POWER|MOSIPIN) 146 | #define CS_H() outportbyte(OUTPORT,POWER|MOSIPIN|CSPIN) 147 | #define CK_L() 148 | #define CONTROL_POWER_ON() outportbyte(CONTROLPORT,0x00) // unused 149 | 150 | #define ADJ_VAL 1 151 | 152 | static 153 | void dly_us (UINT n) 154 | { 155 | _CX = n; 156 | loopit: 157 | _asm { 158 | loop loopit 159 | } 160 | } 161 | 162 | #define NOSHIFT 163 | 164 | #ifdef NOSHIFT 165 | 166 | DWORD dwordlshift(DWORD d, int n) 167 | { 168 | int i; 169 | WORD a = ((WORD *)d)[0]; 170 | WORD b = ((WORD *)d)[1]; 171 | DWORD r; 172 | 173 | for (i=0;i>= 1; 196 | a |= (b & 0x1) ? 0x8000 : 0; 197 | b >>= 1; 198 | } 199 | ((WORD *)r)[0] = a; 200 | ((WORD *)r)[1] = b; 201 | return r; 202 | } 203 | 204 | #define DWORDRSHIFT(d,n) dwordrshift(d,n) 205 | 206 | #else 207 | 208 | #define DWORDLSHIFT(d,n) ((d) << (n)) 209 | #define DWORDRSHIFT(d,n) ((d) >> (n)) 210 | 211 | #endif 212 | 213 | /*-------------------------------------------------------------------------- 214 | 215 | Module Private Functions 216 | 217 | ---------------------------------------------------------------------------*/ 218 | 219 | /* MMC/SD command (SPI mode) */ 220 | #define CMD0 (0) /* GO_IDLE_STATE */ 221 | #define CMD1 (1) /* SEND_OP_COND */ 222 | #define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */ 223 | #define CMD8 (8) /* SEND_IF_COND */ 224 | #define CMD9 (9) /* SEND_CSD */ 225 | #define CMD10 (10) /* SEND_CID */ 226 | #define CMD12 (12) /* STOP_TRANSMISSION */ 227 | #define CMD13 (13) /* SEND_STATUS */ 228 | #define ACMD13 (0x80+13) /* SD_STATUS (SDC) */ 229 | #define CMD16 (16) /* SET_BLOCKLEN */ 230 | #define CMD17 (17) /* READ_SINGLE_BLOCK */ 231 | #define CMD18 (18) /* READ_MULTIPLE_BLOCK */ 232 | #define CMD23 (23) /* SET_BLOCK_COUNT */ 233 | #define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */ 234 | #define CMD24 (24) /* WRITE_BLOCK */ 235 | #define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */ 236 | #define CMD32 (32) /* ERASE_ER_BLK_START */ 237 | #define CMD33 (33) /* ERASE_ER_BLK_END */ 238 | #define CMD38 (38) /* ERASE */ 239 | #define CMD55 (55) /* APP_CMD */ 240 | #define CMD58 (58) /* READ_OCR */ 241 | 242 | 243 | static 244 | DSTATUS Stat = STA_NOINIT; /* Disk status */ 245 | 246 | static 247 | BYTE CardType; /* b0:MMC, b1:SDv1, b2:SDv2, b3:Block addressing */ 248 | 249 | 250 | 251 | /*-----------------------------------------------------------------------*/ 252 | /* Transmit bytes to the card (bitbanging) */ 253 | /*-----------------------------------------------------------------------*/ 254 | 255 | static 256 | void xmit_mmc ( 257 | const BYTE DOSFAR * buff, /* Data to be sent */ 258 | UINT bc /* Number of bytes to send */ 259 | ) 260 | { 261 | BYTE d; 262 | 263 | do { 264 | d = *buff++; /* Get a byte to be sent */ 265 | if (d & 0x80) 266 | { CLOCKBITLOWMOSIHIGH(); BITDLY(); CLOCKBITHIGHMOSIHIGH(); BITDLY(); 267 | } else 268 | { CLOCKBITLOWMOSILOW(); BITDLY(); CLOCKBITHIGHMOSILOW(); BITDLY(); 269 | } 270 | if (d & 0x40) 271 | { CLOCKBITLOWMOSIHIGH(); BITDLY(); CLOCKBITHIGHMOSIHIGH(); BITDLY(); 272 | } else 273 | { CLOCKBITLOWMOSILOW(); BITDLY(); CLOCKBITHIGHMOSILOW(); BITDLY(); 274 | } 275 | if (d & 0x20) 276 | { CLOCKBITLOWMOSIHIGH(); BITDLY(); CLOCKBITHIGHMOSIHIGH(); BITDLY(); 277 | } else 278 | { CLOCKBITLOWMOSILOW(); BITDLY(); CLOCKBITHIGHMOSILOW(); BITDLY(); 279 | } 280 | if (d & 0x10) 281 | { CLOCKBITLOWMOSIHIGH(); BITDLY(); CLOCKBITHIGHMOSIHIGH(); BITDLY(); 282 | } else 283 | { CLOCKBITLOWMOSILOW(); BITDLY(); CLOCKBITHIGHMOSILOW(); BITDLY(); 284 | } 285 | if (d & 0x08) 286 | { CLOCKBITLOWMOSIHIGH(); BITDLY(); CLOCKBITHIGHMOSIHIGH(); BITDLY(); 287 | } else 288 | { CLOCKBITLOWMOSILOW(); BITDLY(); CLOCKBITHIGHMOSILOW(); BITDLY(); 289 | } 290 | if (d & 0x04) 291 | { CLOCKBITLOWMOSIHIGH(); BITDLY(); CLOCKBITHIGHMOSIHIGH(); BITDLY(); 292 | } else 293 | { CLOCKBITLOWMOSILOW(); BITDLY(); CLOCKBITHIGHMOSILOW(); BITDLY(); 294 | } 295 | if (d & 0x02) 296 | { CLOCKBITLOWMOSIHIGH(); BITDLY(); CLOCKBITHIGHMOSIHIGH(); BITDLY(); 297 | } else 298 | { CLOCKBITLOWMOSILOW(); BITDLY(); CLOCKBITHIGHMOSILOW(); BITDLY(); 299 | } 300 | if (d & 0x01) 301 | { CLOCKBITLOWMOSIHIGH(); BITDLY(); CLOCKBITHIGHMOSIHIGH(); BITDLY(); 302 | } else 303 | { CLOCKBITLOWMOSILOW(); BITDLY(); CLOCKBITHIGHMOSILOW(); BITDLY(); 304 | } 305 | } while (--bc); 306 | CLOCKBITLOWMOSIHIGH(); 307 | } 308 | 309 | 310 | 311 | /*-----------------------------------------------------------------------*/ 312 | /* Receive bytes from the card (bitbanging) */ 313 | /*-----------------------------------------------------------------------*/ 314 | 315 | static 316 | void rcvr_mmc ( 317 | BYTE DOSFAR *buff, /* Pointer to read buffer */ 318 | UINT bc /* Number of bytes to receive */ 319 | ) 320 | { 321 | BYTE r; 322 | 323 | do { 324 | CLOCKBITHIGHMOSIHIGH(); BITDLY(); 325 | r = 0; if (DO) r++; /* bit7 */ 326 | CLOCKBITLOWMOSIHIGH(); BITDLY(); 327 | 328 | CLOCKBITHIGHMOSIHIGH(); BITDLY(); 329 | r <<= 1; if (DO) r++; /* bit6 */ 330 | CLOCKBITLOWMOSIHIGH(); BITDLY(); 331 | 332 | CLOCKBITHIGHMOSIHIGH(); BITDLY(); 333 | r <<= 1; if (DO) r++; /* bit5 */ 334 | CLOCKBITLOWMOSIHIGH(); BITDLY(); 335 | 336 | CLOCKBITHIGHMOSIHIGH(); BITDLY(); 337 | r <<= 1; if (DO) r++; /* bit4 */ 338 | CLOCKBITLOWMOSIHIGH(); BITDLY(); 339 | 340 | CLOCKBITHIGHMOSIHIGH(); BITDLY(); 341 | r <<= 1; if (DO) r++; /* bit3 */ 342 | CLOCKBITLOWMOSIHIGH(); BITDLY(); 343 | 344 | CLOCKBITHIGHMOSIHIGH(); BITDLY(); 345 | r <<= 1; if (DO) r++; /* bit2 */ 346 | CLOCKBITLOWMOSIHIGH(); BITDLY(); 347 | 348 | CLOCKBITHIGHMOSIHIGH(); BITDLY(); 349 | r <<= 1; if (DO) r++; /* bit1 */ 350 | CLOCKBITLOWMOSIHIGH(); BITDLY(); 351 | 352 | CLOCKBITHIGHMOSIHIGH(); BITDLY(); 353 | r <<= 1; if (DO) r++; /* bit0 */ 354 | CLOCKBITLOWMOSIHIGH(); BITDLY(); 355 | 356 | *buff++ = r; /* Store a received byte */ 357 | } while (--bc); 358 | } 359 | 360 | /*-----------------------------------------------------------------------*/ 361 | /* Receive bytes from the card (bitbanging) */ 362 | /*-----------------------------------------------------------------------*/ 363 | 364 | static 365 | void dummy_rcvr_mmc (void) 366 | { 367 | int i; 368 | CLOCKBITLOWMOSIHIGHNOCS(); BITDLY(); 369 | for (i=0;i<8;i++) 370 | { 371 | CLOCKBITHIGHMOSIHIGHNOCS(); BITDLY(); 372 | CLOCKBITLOWMOSIHIGHNOCS(); BITDLY(); 373 | } 374 | } 375 | 376 | /*-----------------------------------------------------------------------*/ 377 | /* Wait for card ready */ 378 | /*-----------------------------------------------------------------------*/ 379 | 380 | static 381 | int wait_ready (void) /* 1:OK, 0:Timeout */ 382 | { 383 | BYTE d; 384 | UINT tmr; 385 | 386 | 387 | for (tmr = 5000; tmr; tmr--) { /* Wait for ready in timeout of 500ms */ 388 | rcvr_mmc(&d, 1); 389 | if (d == 0xFF) break; 390 | dly_us(100); 391 | } 392 | 393 | return tmr ? 1 : 0; 394 | } 395 | 396 | 397 | 398 | /*-----------------------------------------------------------------------*/ 399 | /* Deselect the card and release SPI bus */ 400 | /*-----------------------------------------------------------------------*/ 401 | 402 | static 403 | void deselect (void) 404 | { 405 | BYTE d; 406 | 407 | CS_H(); 408 | dummy_rcvr_mmc(); /* Dummy clock (force DO hi-z for multiple slave SPI) */ 409 | } 410 | 411 | 412 | 413 | /*-----------------------------------------------------------------------*/ 414 | /* Select the card and wait for ready */ 415 | /*-----------------------------------------------------------------------*/ 416 | 417 | static 418 | int select (void) /* 1:OK, 0:Timeout */ 419 | { 420 | BYTE d; 421 | 422 | CS_L(); 423 | rcvr_mmc(&d, 1); /* Dummy clock (force DO enabled) */ 424 | 425 | if (wait_ready()) return 1; /* OK */ 426 | deselect(); 427 | return 0; /* Failed */ 428 | } 429 | 430 | 431 | 432 | /*-----------------------------------------------------------------------*/ 433 | /* Receive a data packet from the card */ 434 | /*-----------------------------------------------------------------------*/ 435 | 436 | static 437 | int rcvr_datablock ( /* 1:OK, 0:Failed */ 438 | BYTE DOSFAR *buff, /* Data buffer to store received data */ 439 | UINT btr /* Byte count */ 440 | ) 441 | { 442 | BYTE d[2]; 443 | UINT tmr; 444 | 445 | 446 | for (tmr = 1000; tmr; tmr--) { /* Wait for data packet in timeout of 100ms */ 447 | rcvr_mmc(d, 1); 448 | if (d[0] != 0xFF) break; 449 | dly_us(100); 450 | } 451 | if (d[0] != 0xFE) { 452 | return 0; /* If not valid data token, return with error */ 453 | } 454 | 455 | rcvr_mmc(buff, btr); /* Receive the data block into buffer */ 456 | rcvr_mmc(d, 2); /* Discard CRC */ 457 | 458 | return 1; /* Return with success */ 459 | } 460 | 461 | 462 | 463 | /*-----------------------------------------------------------------------*/ 464 | /* Send a data packet to the card */ 465 | /*-----------------------------------------------------------------------*/ 466 | 467 | static 468 | int xmit_datablock ( /* 1:OK, 0:Failed */ 469 | const BYTE DOSFAR *buff, /* 512 byte data block to be transmitted */ 470 | BYTE token /* Data/Stop token */ 471 | ) 472 | { 473 | BYTE d[2]; 474 | 475 | 476 | if (!wait_ready()) return 0; 477 | 478 | d[0] = token; 479 | xmit_mmc(d, 1); /* Xmit a token */ 480 | if (token != 0xFD) { /* Is it data token? */ 481 | xmit_mmc(buff, 512); /* Xmit the 512 byte data block to MMC */ 482 | rcvr_mmc(d, 2); /* Xmit dummy CRC (0xFF,0xFF) */ 483 | rcvr_mmc(d, 1); /* Receive data response */ 484 | if ((d[0] & 0x1F) != 0x05) /* If not accepted, return with error */ 485 | { 486 | return 0; 487 | } 488 | } 489 | 490 | return 1; 491 | } 492 | 493 | 494 | 495 | /*-----------------------------------------------------------------------*/ 496 | /* Send a command packet to the card */ 497 | /*-----------------------------------------------------------------------*/ 498 | 499 | static 500 | BYTE send_cmd ( /* Returns command response (bit7==1:Send failed)*/ 501 | BYTE cmd, /* Command byte */ 502 | DWORD arg /* Argument */ 503 | ) 504 | { 505 | BYTE n, d, buf[6]; 506 | 507 | 508 | if (cmd & 0x80) { /* ACMD is the command sequense of CMD55-CMD */ 509 | cmd &= 0x7F; 510 | n = send_cmd(CMD55, 0); 511 | if (n > 1) return n; 512 | } 513 | 514 | /* Select the card and wait for ready except to stop multiple block read */ 515 | if (cmd != CMD12) { 516 | deselect(); 517 | if (!select()) return 0xFF; 518 | } 519 | 520 | /* Send a command packet */ 521 | buf[0] = 0x40 | cmd; /* Start + Command index */ 522 | #ifdef NOSHIFT 523 | buf[1] = ((BYTE *)&arg)[3]; /* Argument[31..24] */ 524 | buf[2] = ((BYTE *)&arg)[2]; /* Argument[23..16] */ 525 | buf[3] = ((BYTE *)&arg)[1]; /* Argument[15..8] */ 526 | buf[4] = ((BYTE *)&arg)[0]; /* Argument[7..0] */ 527 | #else 528 | buf[1] = (BYTE)(arg >> 24); /* Argument[31..24] */ 529 | buf[2] = (BYTE)(arg >> 16); /* Argument[23..16] */ 530 | buf[3] = (BYTE)(arg >> 8); /* Argument[15..8] */ 531 | buf[4] = (BYTE)arg; /* Argument[7..0] */ 532 | #endif 533 | n = 0x01; /* Dummy CRC + Stop */ 534 | if (cmd == CMD0) n = 0x95; /* (valid CRC for CMD0(0)) */ 535 | if (cmd == CMD8) n = 0x87; /* (valid CRC for CMD8(0x1AA)) */ 536 | buf[5] = n; 537 | TOUTCHR('L'); 538 | TOUTHEX(buf[0]); 539 | TOUTHEX(buf[1]); 540 | TOUTHEX(buf[2]); 541 | TOUTHEX(buf[3]); 542 | TOUTHEX(buf[4]); 543 | TOUTHEX(buf[5]); 544 | xmit_mmc(buf, 6); 545 | 546 | /* Receive command response */ 547 | if (cmd == CMD12) rcvr_mmc(&d, 1); /* Skip a stuff byte when stop reading */ 548 | n = 10; /* Wait for a valid response in timeout of 10 attempts */ 549 | do 550 | { 551 | rcvr_mmc(&d, 1); 552 | } 553 | while ((d & 0x80) && (--n)); 554 | TOUTCHR('P'); 555 | TOUTHEX(d); 556 | return d; /* Return with the response value */ 557 | } 558 | 559 | 560 | 561 | /*-------------------------------------------------------------------------- 562 | 563 | Public Functions 564 | 565 | ---------------------------------------------------------------------------*/ 566 | 567 | 568 | /*-----------------------------------------------------------------------*/ 569 | /* Get Disk Status */ 570 | /*-----------------------------------------------------------------------*/ 571 | 572 | DSTATUS disk_status ( 573 | BYTE drv /* Drive number (always 0) */ 574 | ) 575 | { 576 | if (drv) return STA_NOINIT; 577 | if ((sd_card_check) && (CDDETECT())) 578 | { 579 | Stat = STA_NOINIT; 580 | return STA_NOINIT; 581 | } 582 | return Stat; 583 | } 584 | 585 | DRESULT disk_result ( 586 | BYTE drv /* Drive number (always 0) */ 587 | ) 588 | { 589 | if (drv) return RES_NOTRDY; 590 | if ((sd_card_check) && (CDDETECT())) 591 | { 592 | Stat = STA_NOINIT; 593 | return RES_NOTRDY; 594 | } 595 | return RES_OK; 596 | } 597 | 598 | 599 | /*-----------------------------------------------------------------------*/ 600 | /* Initialize Disk Drive */ 601 | /*-----------------------------------------------------------------------*/ 602 | 603 | DSTATUS disk_initialize ( 604 | BYTE drv /* Physical drive nmuber (0) */ 605 | ) 606 | { 607 | BYTE n, ty, cmd, buf[4]; 608 | UINT tmr; 609 | DSTATUS s; 610 | 611 | setportbase(portbase); 612 | 613 | if (drv) return RES_NOTRDY; 614 | if ((sd_card_check) && (CDDETECT())) 615 | return RES_NOTRDY; 616 | 617 | /* MEMO: nn_INIT is defined as an empty function */ 618 | 619 | CS_INIT(); CS_H(); /* Initialize port pin tied to CS */ 620 | dly_us(10000); /* 10ms. time for SD card to power up */ 621 | CS_INIT(); CS_H(); /* Initialize port pin tied to CS */ 622 | CK_INIT(); CK_L(); /* Initialize port pin tied to SCLK */ 623 | DI_INIT(); /* Initialize port pin tied to DI */ 624 | DO_INIT(); /* Initialize port pin tied to DO */ 625 | 626 | CLOCKBITLOWMOSILOW(); /* Turn power on */ 627 | 628 | for (n = 10; n; n--) dummy_rcvr_mmc(); /* Apply 80 dummy clocks and the card gets ready to receive command */ 629 | 630 | ty = 0; 631 | if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ 632 | if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */ 633 | rcvr_mmc(buf, 4); /* Get trailing return value of R7 resp */ 634 | if (buf[2] == 0x01 && buf[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ 635 | for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state (ACMD41 with HCS bit) */ 636 | if (send_cmd(ACMD41, 1UL << 30) == 0) break; 637 | dly_us(1000); 638 | } 639 | if (tmr && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */ 640 | rcvr_mmc(buf, 4); 641 | ty = (buf[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */ 642 | } 643 | } 644 | } else { /* SDv1 or MMCv3 */ 645 | if (send_cmd(ACMD41, 0) <= 1) { 646 | ty = CT_SD1; cmd = ACMD41; /* SDv1 */ 647 | } else { 648 | ty = CT_MMC; cmd = CMD1; /* MMCv3 */ 649 | } 650 | for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state */ 651 | if (send_cmd(cmd, 0) == 0) break; 652 | dly_us(1000); 653 | } 654 | if (!tmr || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */ 655 | ty = 0; 656 | } 657 | } 658 | CardType = ty; 659 | s = ty ? 0 : STA_NOINIT; 660 | Stat = s; 661 | 662 | deselect(); 663 | 664 | return s; 665 | } 666 | 667 | 668 | 669 | /*-----------------------------------------------------------------------*/ 670 | /* Read Sector(s) */ 671 | /*-----------------------------------------------------------------------*/ 672 | 673 | DRESULT disk_read ( 674 | BYTE drv, /* Physical drive nmuber (0) */ 675 | BYTE DOSFAR *buff, /* Pointer to the data buffer to store read data */ 676 | DWORD sector, /* Start sector number (LBA) */ 677 | UINT count /* Sector count (1..128) */ 678 | ) 679 | { 680 | DRESULT dr = disk_result(drv); 681 | if (dr != RES_OK) return dr; 682 | 683 | if (!(CardType & CT_BLOCK)) sector = DWORDLSHIFT(sector,9); /* Convert LBA to byte address if needed */ 684 | 685 | if (count == 1) { /* Single block read */ 686 | if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */ 687 | && rcvr_datablock(buff, 512)) 688 | count = 0; 689 | } 690 | else { /* Multiple block read */ 691 | if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */ 692 | do { 693 | if (!rcvr_datablock(buff, 512)) break; 694 | buff += 512; 695 | } while (--count); 696 | send_cmd(CMD12, 0); /* STOP_TRANSMISSION */ 697 | } 698 | } 699 | deselect(); 700 | 701 | return count ? RES_ERROR : RES_OK; 702 | } 703 | 704 | 705 | 706 | /*-----------------------------------------------------------------------*/ 707 | /* Write Sector(s) */ 708 | /*-----------------------------------------------------------------------*/ 709 | 710 | DRESULT disk_write ( 711 | BYTE drv, /* Physical drive nmuber (0) */ 712 | const BYTE DOSFAR *buff, /* Pointer to the data to be written */ 713 | DWORD sector, /* Start sector number (LBA) */ 714 | UINT count /* Sector count (1..128) */ 715 | ) 716 | { 717 | DRESULT dr = disk_result(drv); 718 | if (dr != RES_OK) return dr; 719 | 720 | if (!(CardType & CT_BLOCK)) sector = DWORDLSHIFT(sector,9); /* Convert LBA to byte address if needed */ 721 | 722 | if (count == 1) { /* Single block write */ 723 | if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ 724 | && xmit_datablock(buff, 0xFE)) 725 | count = 0; 726 | } 727 | else { /* Multiple block write */ 728 | if (CardType & CT_SDC) send_cmd(ACMD23, count); 729 | if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ 730 | do { 731 | if (!xmit_datablock(buff, 0xFC)) break; 732 | buff += 512; 733 | } while (--count); 734 | if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */ 735 | count = 1; 736 | if (!wait_ready()) count = 1; /* Wait for card to write */ 737 | } 738 | } 739 | deselect(); 740 | 741 | return count ? RES_ERROR : RES_OK; 742 | } 743 | 744 | 745 | /*-----------------------------------------------------------------------*/ 746 | /* Miscellaneous Functions */ 747 | /*-----------------------------------------------------------------------*/ 748 | 749 | DRESULT disk_ioctl ( 750 | BYTE drv, /* Physical drive nmuber (0) */ 751 | BYTE ctrl, /* Control code */ 752 | void DOSFAR *buff /* Buffer to send/receive control data */ 753 | ) 754 | { 755 | DRESULT res; 756 | BYTE n, csd[16]; 757 | DWORD cs; 758 | DRESULT dr = disk_result(drv); 759 | if (dr != RES_OK) return dr; 760 | 761 | res = RES_ERROR; 762 | switch (ctrl) { 763 | case CTRL_SYNC : /* Make sure that no pending write process */ 764 | if (select()) res = RES_OK; 765 | break; 766 | 767 | case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ 768 | if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { 769 | if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ 770 | cs = csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1; 771 | *(DWORD DOSFAR *)buff = DWORDLSHIFT(cs,10); 772 | } else { /* SDC ver 1.XX or MMC */ 773 | n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; 774 | cs = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1; 775 | *(DWORD DOSFAR *)buff = DWORDLSHIFT(cs,n-9); 776 | } 777 | res = RES_OK; 778 | } 779 | break; 780 | 781 | case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */ 782 | *(DWORD DOSFAR *)buff = 128; 783 | res = RES_OK; 784 | break; 785 | 786 | default: 787 | res = RES_PARERR; 788 | } 789 | 790 | deselect(); 791 | 792 | return res; 793 | } 794 | -------------------------------------------------------------------------------- /STANDARD.H: -------------------------------------------------------------------------------- 1 | /* standard.h - standard, project wide, declarations */ 2 | /* */ 3 | /* Copyright (C) 1994 by Robert Armstrong */ 4 | /* */ 5 | /* This program is free software; you can redistribute it and/or modify */ 6 | /* it under the terms of the GNU General Public License as published by */ 7 | /* the Free Software Foundation; either version 2 of the License, or */ 8 | /* (at your option) any later version. */ 9 | /* */ 10 | /* This program is distributed in the hope that it will be useful, but */ 11 | /* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */ 12 | /* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ 13 | /* Public License for more details. */ 14 | /* */ 15 | /* You should have received a copy of the GNU General Public License */ 16 | /* along with this program; if not, visit the website of the Free */ 17 | /* Software Foundation, Inc., www.gnu.org. */ 18 | 19 | 20 | /* Datatypes of a known size ... */ 21 | typedef unsigned char BYTE; /* 8 bits, unsigned */ 22 | typedef unsigned short WORD; /* 16 " " */ 23 | #ifndef DEFINEDLONG 24 | #define DEFINEDLONG 25 | typedef unsigned long LONG; /* 32 " " */ 26 | #endif 27 | 28 | 29 | /* The BOOLEAN type and associated constants ... */ 30 | typedef unsigned char BOOLEAN;/* any TRUE/FALSE result */ 31 | #define FALSE (0) /* logical falsity */ 32 | #define TRUE (~FALSE)/* if it's not FALSE, it's ... */ 33 | 34 | 35 | /* This magic declaration modifier will make the symbol defined be */ 36 | /* invisible to the linker. For example, "PRIVATE int foo;" or even */ 37 | /* "PRIVATE void fred () { ...}". This is used extensively to restrict */ 38 | /* the scope of a global declaration to the current module. */ 39 | #define PRIVATE static /* this works for most compilers! */ 40 | 41 | 42 | /* And this magic declaration modifier guarantees that the symbol so */ 43 | /* defined will be visible to other modules. It is the opposite of */ 44 | /* "PRIVATE". In C this is the default condition anyway, so this macro */ 45 | /* mostly serves as documentation. */ 46 | #define PUBLIC /* this is the way things are anyway */ 47 | 48 | 49 | /* Extract the high and low bytes of a 16 bit value... */ 50 | #define HIGH(x) (((x) >> 8) & 0xFF) 51 | #define LOW(x) ((x) & 0xFF) 52 | 53 | /* Test a value for odd/even... */ 54 | #define ODD(x) (((x) & 1) != 0) 55 | 56 | 57 | /* These macros provide access to specific 8088 family hardware ins- */ 58 | /* tructions. From a performance viewpoint, it is highly desirable that */ 59 | /* these macros not be implemented by subroutine calls, but rather that */ 60 | /* they expand directly to inline assembler instructions. */ 61 | 62 | #ifdef __TURBOC__ 63 | /* These macros turn the interrupt system on and off. They should */ 64 | /* generate the STI (0xFB) and CLI (0xFA) instructions... */ 65 | #define INT_ON asm sti 66 | #define INT_OFF asm cli 67 | 68 | /* These macros read and write a bytes from an I/O port. They should */ 69 | /* generate the 0xE5 (IN AL,DX) or 0xEE (OUT DX,AL) instructions... */ 70 | #define INBYTE(p,v) { _DX = (p); asm IN AL,DX; v = _AL; } 71 | #define OUTBYTE(p,v) { _DX = (p); _AL = (v); asm OUT DX,AL; } 72 | 73 | /* These macros will set or clear specific bit(s) in an I/O port. */ 74 | /* They work by first reading the port, seting or clearing the bit(s), */ 75 | /* and then writing the port. Needless to say, they can only be used */ 76 | /* on ports that can safely be both read and written! */ 77 | #define IOSETBIT(p,m) { _DX = (p); _BL = (m); \ 78 | asm IN AL,DX; asm OR AL,BL; asm OUT DX,AL; } 79 | #define IOCLRBIT(p,m) { _DX = (p); _BL = ~(m); \ 80 | asm IN AL,DX; asm AND AL,BL; asm OUT DX,AL; } 81 | 82 | /* These macros use the DOS INT 21H functions to get and set the */ 83 | /* contents of interrupt vectors. They are used to save and restore */ 84 | /* the serial port interrupt service routine... */ 85 | #define GETVECTOR(v,a) { _AL = (v); _AH = 0x35; asm INT 0x21; \ 86 | asm MOV WORD PTR a, BX; asm MOV WORD PTR a+2, ES; } 87 | #define SETVECTOR(v,a) { _AL = (v); _AH = 0x25; asm PUSH DS; \ 88 | asm LDS DX, DWORD PTR a; \ 89 | asm INT 0x21; asm POP DS; } 90 | #define JMPVECTOR(a) { asm JMP DWORD PTR a; } 91 | 92 | 93 | /* Compute dst += src with end-around carry, just like the TU58 does... */ 94 | #define ADC(dst,src) { asm MOV AX, dst; asm ADD AX, src; \ 95 | asm ADC AX, 0; asm MOV dst, AX; } 96 | 97 | #endif /* ifdef __TURBOC__ */ 98 | 99 | 100 | /* Debugging output routines... */ 101 | void outchr (char); 102 | void outstr (char *); 103 | void outdec (int); 104 | void outhex (unsigned, int); 105 | void outcrlf (void); 106 | void cprintf (char near *, ...); 107 | -------------------------------------------------------------------------------- /doc/adapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilseuropa/sdpp/e022dde3fd2cdbc802a9eaa7fce568da4dec132a/doc/adapter.png -------------------------------------------------------------------------------- /doc/board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilseuropa/sdpp/e022dde3fd2cdbc802a9eaa7fce568da4dec132a/doc/board.png -------------------------------------------------------------------------------- /doc/module.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilseuropa/sdpp/e022dde3fd2cdbc802a9eaa7fce568da4dec132a/doc/module.png -------------------------------------------------------------------------------- /doc/module_schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilseuropa/sdpp/e022dde3fd2cdbc802a9eaa7fce568da4dec132a/doc/module_schema.png -------------------------------------------------------------------------------- /doc/schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilseuropa/sdpp/e022dde3fd2cdbc802a9eaa7fce568da4dec132a/doc/schematic.png -------------------------------------------------------------------------------- /doc/teaser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilseuropa/sdpp/e022dde3fd2cdbc802a9eaa7fce568da4dec132a/doc/teaser.png -------------------------------------------------------------------------------- /driver/NILQUAD.SYS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilseuropa/sdpp/e022dde3fd2cdbc802a9eaa7fce568da4dec132a/driver/NILQUAD.SYS -------------------------------------------------------------------------------- /driver/SD.SYS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilseuropa/sdpp/e022dde3fd2cdbc802a9eaa7fce568da4dec132a/driver/SD.SYS -------------------------------------------------------------------------------- /eagle/sd_adapter.lbr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | SD Card 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /eagle/sdadapter.brd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | SDPP Module Adapter 176 | github.com/nilseuropa/sdpp 177 | !!! WARNING !!! 178 | Populate barrier diodes 179 | only if your port is 180 | IEEE1284 compliant. 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | <h3>SparkFun Electronics' preferred foot prints</h3> 191 | In this library you'll find non-functional items- supply symbols, logos, notations, frame blocks, etc.<br><br> 192 | We've spent an enormous amount of time creating and checking these footprints and parts, but it is the end user's responsibility to ensure correctness and suitablity for a given componet or application. If you enjoy using this library, please buy one of our products at www.sparkfun.com. 193 | <br><br> 194 | <b>Licensing:</b> Creative Commons ShareAlike 4.0 International - https://creativecommons.org/licenses/by-sa/4.0/ 195 | <br><br> 196 | You are welcome to use this library for commercial purposes. For attribution, we ask that when you begin to sell your device using our footprint, you email us with a link to the product being sold. We want bragging rights that we helped (in a very small part) to create your 8th world wonder. We would like the opportunity to feature your device on our homepage. 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | <b>SUB-D Connectors</b><p> 239 | Harting<br> 240 | NorComp<br> 241 | <p> 242 | PREFIX :<br> 243 | H = High density<br> 244 | F = Female<br> 245 | M = Male<p> 246 | NUMBER: Number of pins<p> 247 | SUFFIX :<br> 248 | H = Horizontal<br> 249 | V = Vertical <br> 250 | P = Shield pin on housing<br> 251 | B = No mounting holes<br> 252 | S = Pins individually placeable (Single)<br> 253 | D = Direct mounting <p> 254 | <author>Created by librarian@cadsoft.de</author> 255 | 256 | 257 | <b>SUB-D</b> 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | >NAME 308 | >VALUE 309 | 1 310 | Board 311 | F25D 312 | 13 313 | 25 314 | 14 315 | Board 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | SUB-D 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | <b>Diodes</b><p> 356 | Based on the following sources: 357 | <ul> 358 | <li>Motorola : www.onsemi.com 359 | <li>Fairchild : www.fairchildsemi.com 360 | <li>Philips : www.semiconductors.com 361 | <li>Vishay : www.vishay.de 362 | </ul> 363 | <author>Created by librarian@cadsoft.de</author> 364 | 365 | 366 | <b>Molded plasitc,JEDEC SOD-123/Mini SMA</b><p> 367 | Source: Comchip CGRM4001-G.pdf 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | >NAME 381 | >VALUE 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | Molded plasitc,JEDEC SOD-123/Mini SMA 390 | Source: Comchip CGRM4001-G.pdf 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | <b>Jacks</b><p> 399 | Power Connectors<br> 400 | Phone Connectors<br> 401 | Cinch Connectors<br> 402 | <author>Created by librarian@cadsoft.de</author> 403 | 404 | 405 | <b>DC POWER JACK</b> Pad shape changed to LONG 2007.07.26<p> 406 | Source: DCJ0303.pdf 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | >NAME 437 | >VALUE 438 | 439 | 440 | 441 | 442 | DC POWER JACK Pad shape changed to LONG 2007.07.26 443 | Source: DCJ0303.pdf 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | <b>SparkFun 2 Layer Design Rule Checks - STANDARD/TIGHT/FAB-LIMIT</b> 486 | <p> 487 | These rules have been curated by SparkFuns DFM commitee. After doing much research, communicating with our multiple fab houses, and getting quotes of various designs, we have compiled three DRU files. 488 | <p> 489 | <b>STANDARD:</b> This is more of a "best case scenario" set of limitations. If your design has the space, and/or you have the time to work within these parameters, please do. Larger trace width and clearance makes for easier visual inspection of the PCB while troubleshooting (useful in production and to the end user). It also allows for better ability to hack a trace (if you are crazy enough to scrape away the mask and solder to a trace). Another thing to keep in mind is that more metal is just more robust. 490 | <p> 491 | <b>TIGHT:</b> This is where cost comes into play. We have found that most fab houses begin to add extra charges when you go smaller than these specs. In some cases, going to less than 15 mil trace can increase the cost by 10%. (This is why we have set the min drill on this DRU to 15 mil) Same story for traces thinner than 7 mil. To avoid those extra charges, then stay within the rules of this DRU. 492 | <p> 493 | <b>FAB-LIMIT:</b> These set of rules are at the very limit of most fab houses capabilities. You will pay more for these specs, and it should be used on designs that have a darned good reason to need 4 mil vias and 4 mil traces. 494 | <p> 495 | **NOTE Clearance, Distance, Sizes, and Restring are all set to different limits in each of these three DRU files. Please compare the files within the CAM job editor window of eagle to see all the numbers. 496 | <p> 497 | ***NOTE, Please set your Net Classes to default (0mil for all settings), so that it won't effect the DRC when you run it with these settings. 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | Since Version 6.2.2 text objects can contain more than one line, 855 | which will not be processed correctly with this version. 856 | 857 | 858 | Since Version 8.2, EAGLE supports online libraries. The ids 859 | of those online libraries will not be understood (or retained) 860 | with this version. 861 | 862 | 863 | Since Version 8.3, EAGLE supports URNs for individual library 864 | assets (packages, symbols, and devices). The URNs of those assets 865 | will not be understood (or retained) with this version. 866 | 867 | 868 | Since Version 8.3, EAGLE supports the association of 3D packages 869 | with devices in libraries, schematics, and board files. Those 3D 870 | packages will not be understood (or retained) with this version. 871 | 872 | 873 | 874 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SD Card to Parallel Port Adapter(s) and MS-DOS Driver 2 | 3 | "A long forgotten solution that gets updated every decade or so. [ 1994 - 2014 - **2024** ]" 4 | *It's either the update rate is doubling - and the next update will come in 2029 - or it is coming in every year that ends with a four...* Anyway, I wanted to have a single repository to contain all information to keep this neat solution afloat. This is it. 5 | 6 | ![](doc/teaser.png) 7 | 8 | ## Parallel port power 9 | Current version of the MS-DOS driver supports [powering the SD card](https://www.transkommunikation.ch/dateien/schaltungen/diverse_schaltungen/computer_circuits/Get%20Power%20From%20Pc%20Parallel%20Port.pdf) via the parallel port. A modern micro SD card [takes around 25mA]( 10 | https://goughlui.com/2021/02/27/experiment-microsd-card-power-consumption-spi-performance/) of current on 3.3V in average. There are five data channels that are utilized as power outputs: 11 | 12 | | Adapter | DB-25 Pin | Parallel Port | 13 | | -------- | ------- | ------- | 14 | | VCC | 5 | Data 3 | 15 | | VCC | 6 | Data 4 | 16 | | VCC | 7 | Data 5 | 17 | | VCC | 8 | Data 6 | 18 | | VCC | 9 | Data 7 | 19 | 20 | *IEEE 1284 Level II interface drivers must be able to source 14 mA current (at least +2.4V 21 | voltage) and also sink 14 mA current (output voltage lower than 0.4V). The output impedance 22 | in normal operation range is defined to be 50+/-5 ohms.* 23 | 24 | |Port type|Normal|UM82C11-C|IEEE 1284| 25 | | -------- | -------- | ------- | ------- | 26 | Data output (>2.4V)| 2.6 mA| 2 mA| 14 mA| 27 | Data line sink (<0.4V)| 24 mA| 24 mA| 14 mA| 28 | Control output (>2.4 V)| 0.5 mA| 1.5 mA| ?| 29 | Signal lines (short circuit)| 1 mA| ?| ?| 30 | Control line sink (<0.4V)| 7 mA| 7 mA| 14 mA| 31 | 32 | :warning: **WARNING, using this feature can potentially harm your port driver if it is not IEEE1284 compliant!** 33 | 34 | As this standard came alive in 1994, rule of thumb is that most computers built after it are probably compliant, and everything before that is a mystery that you have to solve for the sake of your machine. 35 | The power feature is totally optional, and potentially dangerous if your machine doesn't support it. 36 | 37 | # Hardware options 38 | 39 | 1. Hook up an SD breakout board to a DB25 and be done with it *( see Wiring )* 40 | 2. Etch my simple adapter board design for the breakout module 41 | 3. Build my SD Drive design *( contributions are welcome )* 42 | 43 | ## SD Breakout board 44 | In principle any SD card connector breakout board can be used that has 3.3/5V level shifters and a voltage regulator - like this one: 45 | 46 | ![](doc/adapter.png) 47 | 48 | :warning: **Do not use breakout boards without level shifters! You will most likely damage your card.** 49 | 50 | ### Wiring 51 | | SD Adapter | Hint | DB-25 Pin | Parallel Port | 52 | | -------- | ------- | ------- | ------- | 53 | | CS |Chip select| 4 | Data 2 | 54 | | SCK |Clock| 3 | Data 1 | 55 | | MOSI |Card serial in| 2 | Data 0 | 56 | | MISO |Card serial out| 13 | Select | 57 | | VCC |**+5V DC**| | | 58 | | GND |Ground| 25 | Ground | 59 | | **Optional** | | | 60 | | CD |Card detect| 11 | BUSY| 61 | 62 | ## Breakout Adapter 63 | 64 | ### Schematic diagram 65 | Disregard the barrier diodes in case you want to build a fool-proof circuit. 66 | ![](doc/module_schema.png) 67 | 68 | ### Etch it yourself at home board 69 | This is an easy to fabricate adapter board for the micro SD card connector breakout module depicted above. It is available through at least 5 vendors. *( The one I have came from Reichelt. )* 70 | 71 | ![](doc/module.png) 72 | 73 | **Double check that your port is IEEE1284 compliant.** 74 | The circuit also provides an auxiliary power input through barrel jack connector. In case you are unsure or your port is unable to handle the load **DO NOT POPULATE** the barrier diodes! 75 | 76 | ## SD Drive *( the fancy )* 77 | Or a more integrated version, designed to power the SD card through an IEEE1284 compliant port. 78 | 79 | ### Schematic diagram 80 | 81 | Each channel is fed through a super low forward drop *(0.31V)* barrier diode to protect the port in case the push-pull outputs are operated by another software than the MS-DOS driver in this repository. The sum of the channels is buffered by a 100uF capacitor, and the supply voltage for the SD card is provided by a linear regulator. With the correct port type, this provides around 100mA on the 3.3V rail that can handle the microsecond long spikes that occur during flash write cycles. 82 | 83 | ![](doc/schematic.png) 84 | 85 | 86 | ### Board layout 87 | 88 | **Double check that your port is IEEE1284 compliant.** 89 | The circuit also provides an auxiliary power input through a USB-C connector. In case you are unsure or your port is unable to handle the load **DO NOT POPULATE** the barrier diodes! 90 | 91 | ![](doc/board.png) 92 | 93 | #### Bill of materials 94 | ``` 95 | Part Value Package Type 96 | 97 | C1 1.0uF 0603-CAP 98 | C2 1.0uF 0603-CAP 99 | C3 0.1uF 0603-CAP 100 | C4 0.1uF 0603-CAP 101 | C5 100uF PANASONIC_C 102 | D1 CGRM4001-G SOD-123_MINI-SMA RB521ASA 103 | D2 CGRM4001-G SOD-123_MINI-SMA RB521ASA 104 | D3 CGRM4001-G SOD-123_MINI-SMA RB521ASA 105 | D4 CGRM4001-G SOD-123_MINI-SMA RB521ASA 106 | D5 CGRM4001-G SOD-123_MINI-SMARB521ASA 107 | J1 1-2295018-2 TE_1-2295018-2 USB-C 108 | JP2 USD_CONNECTOR MICRO-SD-SOCKET-PP 4UCON-15882 109 | R1 100k 0603-RES 110 | R2 100k 0603-RES 111 | R3 100k 0603-RES 112 | R4 100k 0603-RES 113 | U1 IC1 SO14 TXB0104PWRSOIC14 114 | U2 3.3V SOT23-5 V_REG_AP2112K-3.3V 115 | X1 SUB-D DB25 F25D 116 | ``` 117 | 118 | :exclamation: This printed circuit board is yet to be tested. If you have built one, please let me know. 119 | 120 | **** 121 | 122 | # MS-DOS Device Driver 123 | Original device driver is written in 1994 by **Dan Marks** and it is based on TU58 by **Robert Armstrong**. Foolproof MMCv3/SDv1/SDv2 control module from Chan Fat FS has been added by [profdc9](https://forum.vcfed.org/index.php?threads/ms-dos-driver-for-an-sd-card-connected-via-a-parallel-port.41516/) and posted on Vintage Computer Federation forum in 2014. 124 | 125 | The driver is made available under the GNU General Public License version 2 and incorporates modified code from ELM Chan Fat FS: http://elm-chan.org/fsw/ff/00index_e.html. 126 | 127 | #### Compilation 128 | If you would like to compile the driver from source you'll need **Borland C++ 3.1** with the included **Turbo Assembler 3.1** installed on your DOS machine. 129 | 130 | ### Binaries 131 | 132 | :floppy_disk: [Download](driver/SD.SYS) the latest pre-compiled version of `SD.SYS` 133 | 134 | :floppy_disk: [Download](driver/NILQUAD.SYS) a modified version compiled for Nilquader's NC100 SD adapter `NILQUAD.SYS` *(MISO on BUSY)* 135 | 136 | **** 137 | 138 | ## How to use it 139 | 140 | * Create <=32MB partition(s) on a uSD card 141 | * Format to FAT16 for best results 142 | * Find out the port base of your parallel port 143 | * Make sure that you are powering the adapter correctly 144 | * Install the [driver](driver/SD.SYS) :arrow_up: on DOS and enjoy. *(see configuration below)* 145 | 146 | #### Prepare the card on Linux 147 | 1. Open a terminal 148 | 2. Find your block device with `lsblk` 149 | 3. `sudo umount /dev/` -- unmount if mounted 150 | 4. Partition with `sudo fdisk /dev/` -- or use *gparted* 151 | * `p` lists, `d` deletes, `n` for new partition 152 | * new partition --> `p` for primary 153 | * enter last sector `+32M` 154 | * `t` to change partition type to FAT16 (`L` to list avaiable formats ) 155 | * `w` to write changes to partition table 156 | 5. Format `sudo mkdosfs -F 16 /dev/` 157 | 158 | 159 | #### Prepare the card on Windows 160 | 161 | 1. Open a command line and start `diskpart` 162 | 2. `list disk` -- find the disk number of your card 163 | 3. `select disk #` -- select your sd card 164 | 4. `list part` -- look for existing partitions 165 | 5. `select part #` -- select the partition to work with 166 | 6. `delete part #` -- to get rid of anything that is not FAT16 167 | 7. `create part primary size=32` -- create a 32MB partition 168 | 8. `exit` -- leave diskpart 169 | 9. `format X: /FS:FAT /Q` -- format partition to FAT16 (replace X) 170 | 171 | [Source](https://aesmit.org/2020/05/24/format-sd-card-fat16-on-windows-10/) 172 | 173 | #### Prepare the card on Mac 174 | 1. Open Terminal (Applications > Utilities) 175 | 2. `hdiutil create -size 32m -fs "MS-DOS FAT16" -volname "MSDOS32MB" mydisk.dmg` -- create a blank 32MB FAT16 disk image 176 | 3. `hdiutil attach mydisk.dmg` -- mount image 177 | 4. Copy your files 178 | 5. `hdiutil detach /Volumes/MSDOS32MB` -- unmout image 179 | 6. `hdiutil convert mydisk.dmg -format UDTO -o mydisk.iso` -- convert to raw format (mydisk.iso.cdr) 180 | 7. Use `diskutil list` to find your physical media 181 | 8. Unmount it if mounted `diskutil unmountDisk /dev/disk#` 182 | 9. `sudo dd if=mydisk.iso.cdr of=/dev/rdisk2 bs=1m` -- write your image to the partition 183 | 10. `diskutil eject /dev/disk#` -- eject card 184 | 185 | ### DOS 186 | Put the following line in your `config.sys` file in order to install and load the driver: 187 | 188 | `DEVICE=SD.SYS /p= /b=` 189 | 190 | 191 | ``` 192 | /d = debugging mode (displays copious debugging messsages) 193 | /k = use card detect signal to inform DOS that card is attached 194 | /p = partition number (1-4) to partition in MBR to use. Default: first available. 195 | /b = port base index of parallel port, one of 196 | 1=0x3BC, 2=0x378, 3=0x278, 4=0x3E8, 5=0x2E8 197 | Default: 0x378 198 | ``` 199 | #### Notes 200 | You can have multiple copies of the driver loaded if there are multiple partitions on your SD card you want to use simultaneously. 201 | 202 | **** 203 | --------------------------------------------------------------------------------