1
0
mirror of synced 2026-01-12 00:02:46 +00:00
Matthieu Bucchianeri 2024-10-13 00:29:32 -07:00
parent 1c6e15242a
commit 05a093cb6f
14 changed files with 2851 additions and 0 deletions

123
XTMax/Apps/SDPP/CPRINT.C Normal file
View File

@ -0,0 +1,123 @@
/* cprint.c */
/* */
/* This file contains simple ASCII output routines. These are used */
/* by the device driver for debugging, and to issue informational */
/* messages (e.g. while loading). In general the C run time library */
/* functions probably aren't safe for use withing the context of a */
/* device driver and should be avoided. */
/* */
/* All these routines do their output thru the routine outchr, which */
/* is defined in DRIVER.ASM. It calls the BIOS INT 10 "dumb TTY" */
/* output function directly and does not use MSDOS at all. */
/* */
/* Copyright (C) 1994 by Robert Armstrong */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */
/* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */
/* Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, visit the website of the Free */
/* Software Foundation, Inc., www.gnu.org. */
#include <stdio.h> /* NULL, etc... */
#include <dos.h> /* used only for MK_FP ! */
#include <stdarg.h> /* needed for variable argument lists */
/* outchr - print a single ASCII character */
void outchr (char ch)
{
_DI = _SI = 0;
_AL = ch; _AH = 0xE; _BX = 0;
asm INT 0x10;
}
/* outstr - print an ASCIZ string */
void outstr (char *p)
{
while (*p != '\0')
outchr (*p++);
}
/* outdec - print a signed decimal integer */
void outdec (int val)
{
if (val < 0)
{outchr('-'); val = -val;}
if (val > 9)
{outdec( val/10 ); val %= 10;}
outchr('0' + val);
}
/* outhex - print a n digit hex number with leading zeros */
void outhex (unsigned val, int ndigits)
{
if (ndigits > 1)
outhex (val >> 4, ndigits-1);
val &= 0xf;
if (val > 9)
outchr('A'+val-10);
else
outchr('0'+val);
}
/* outhex - print a n digit hex number with leading zeros */
void outlhex (unsigned long lval)
{
int i;
for (i=3;i>=0;i--)
outhex(((unsigned char *)&lval)[i],2);
}
/* outcrlf - print a carriage return, line feed pair */
void outcrlf (void)
{
outchr ('\r'); outchr ('\n');
}
/* cprintf */
/* This routine provides a simple emulation for the printf() function */
/* using the "safe" console output routines. Only a few escape seq- */
/* uences are allowed: %d, %x and %s. A width modifier (e.g. %2x) is */
/* recognized only for %x, and then may only be a single decimal digit. */
void cdprintf (char near *msg, ...)
{
va_list ap; char *str; int size, ival; unsigned uval; unsigned long luval;
va_start (ap, msg);
while (*msg != '\0') {
/*outhex((unsigned) msg, 4); outchr('='); outhex(*msg, 2); outchr(' ');*/
if (*msg == '%') {
++msg; size = 0;
if ((*msg >= '0') && (*msg <= '9'))
{size = *msg - '0'; ++msg;}
if (*msg == 'c') {
ival = va_arg(ap, int); outchr(ival&0xff); ++msg;
} else if (*msg == 'd') {
ival = va_arg(ap, int); outdec (ival); ++msg;
} else if (*msg == 'x') {
uval = va_arg(ap, unsigned); ++msg;
outhex (uval, (size > 0) ? size : 4);
} else if (*msg == 'L') {
luval = va_arg(ap, unsigned long); ++msg;
outlhex (luval);
} else if (*msg == 's') {
str = va_arg(ap, char *); outstr (str); ++msg;
}
} else if (*msg == '\n') {
outchr('\r'); outchr('\n'); ++msg;
} else {
outchr(*msg); ++msg;
}
}
va_end (ap);
}

36
XTMax/Apps/SDPP/CPRINT.H Normal file
View File

@ -0,0 +1,36 @@
/* */
/* This file contains simple ASCII output routines. These are used */
/* by the device driver for debugging, and to issue informational */
/* messages (e.g. while loading). In general the C run time library */
/* functions probably aren't safe for use withing the context of a */
/* device driver and should be avoided. */
/* */
/* All these routines do their output thru the routine outchr, which */
/* is defined in DRIVER.ASM. It calls the BIOS INT 10 "dumb TTY" */
/* output function directly and does not use MSDOS at all. */
/* */
/* Copyright (C) 1994 by Robert Armstrong */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */
/* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */
/* Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, visit the website of the Free */
/* Software Foundation, Inc., www.gnu.org. */
#ifndef _CPRINT_H
#define _CPRINT_H
void outchr (char ch);
void outstr (char *p);
void outdec (int val);
void outhex (unsigned val, int ndigits);
void outcrlf (void);
void cdprintf (char near *msg, ...);
#endif

87
XTMax/Apps/SDPP/DISKIO.H Normal file
View File

@ -0,0 +1,87 @@
/*-----------------------------------------------------------------------
/ Low level disk interface modlue include file (C)ChaN, 2013
/-----------------------------------------------------------------------*/
#ifndef _DISKIO_DEFINED
#define _DISKIO_DEFINED
#ifdef __cplusplus
extern "C" {
#endif
#include "integer.h"
/* Status of Disk Functions */
typedef BYTE DSTATUS;
/* Results of Disk Functions */
typedef enum {
RES_OK = 0, /* 0: Successful */
RES_ERROR, /* 1: R/W Error */
RES_WRPRT, /* 2: Write Protected */
RES_NOTRDY, /* 3: Not Ready */
RES_PARERR /* 4: Invalid Parameter */
} DRESULT;
void setportbase(BYTE val); /* set the port base */
/*---------------------------------------*/
/* Prototypes for disk control functions */
#define DOSFAR far
DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_result (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE DOSFAR * buff, DWORD sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE DOSFAR * buff, DWORD sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void DOSFAR * buff);
/* Disk Status Bits (DSTATUS) */
#define STA_NOINIT 0x01 /* Drive not initialized */
#define STA_NODISK 0x02 /* No medium in the drive */
#define STA_PROTECT 0x04 /* Write protected */
/* Command code for disk_ioctrl fucntion */
/* Generic command (used by FatFs) */
#define CTRL_SYNC 0 /* Flush disk cache (for write functions) */
#define GET_SECTOR_COUNT 1 /* Get media size (for only f_mkfs()) */
//#define GET_SECTOR_SIZE 2 /* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */
#define GET_BLOCK_SIZE 3 /* Get erase block size (for only f_mkfs()) */
#define CTRL_ERASE_SECTOR 4 /* Force erased a block of sectors (for only _USE_ERASE) */
/* Generic command (not used by FatFs) */
//#define CTRL_POWER 5 /* Get/Set power status */
//#define CTRL_LOCK 6 /* Lock/Unlock media removal */
//#define CTRL_EJECT 7 /* Eject media */
//#define CTRL_FORMAT 8 /* Create physical format on the media */
/* MMC/SDC specific ioctl command */
#define MMC_GET_TYPE 10 /* Get card type */
#define MMC_GET_CSD 11 /* Get CSD */
#define MMC_GET_CID 12 /* Get CID */
#define MMC_GET_OCR 13 /* Get OCR */
#define MMC_GET_SDSTAT 14 /* Get SD status */
/* ATA/CF specific ioctl command */
#define ATA_GET_REV 20 /* Get F/W revision */
#define ATA_GET_MODEL 21 /* Get model name */
#define ATA_GET_SN 22 /* Get serial number */
/* MMC card type flags (MMC_GET_TYPE) */
#define CT_MMC 0x01 /* MMC ver 3 */
#define CT_SD1 0x02 /* SD ver 1 */
#define CT_SD2 0x04 /* SD ver 2 */
#define CT_SDC (CT_SD1|CT_SD2) /* SD */
#define CT_BLOCK 0x08 /* Block addressing */
#ifdef __cplusplus
}
#endif
#endif

508
XTMax/Apps/SDPP/DRIVER.C Normal file
View File

@ -0,0 +1,508 @@
/* driver.c - MSDOS device driver functions */
/* */
/* Copyright (C) 1994 by Robert Armstrong */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */
/* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */
/* Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, visit the website of the Free */
/* Software Foundation, Inc., www.gnu.org. */
#include <stdio.h> /* NULL, etc... */
#include <dos.h> /* used only for MK_FP ! */
#include "standard.h" /* definitions for this project */
#include "sd.h" /* SD card glue */
#include "diskio.h" /* SD card library header */
#include "driver.h" /* MSDOS device driver interface */
#include "cprint.h" /* Console printing */
#define FORM_FACTOR 8 /* DOS form factor code used for SD */
/* Forward declarations for routines that need it... */
PRIVATE BOOLEAN parse_options (char far *);
PUBLIC void Initialize (rh_init_t far *);
PUBLIC void far SDDriver (rh_t far *);
/* These data structures are exported by the HEADER.ASM module... */
extern devhdr_t header; /* MSDOS device header for our driver */
extern bpb_t bpb; /* BIOS Parameter block " " " */
extern bpbtbl_t bpbtbl; /* BPB table (one for each drive unit) */
/* This double word is actually a far pointer to the entry point of */
/* this module. It's called by the assembly interface whenver DOS needs */
/* driver action. The second word of the far pointer contains the seg- */
/* ment address, which is actually computed and written by the assembly */
/* code. */
PUBLIC WORD c_driver[2] = {(WORD) &SDDriver, 0};
extern unsigned char com_flag;
/* Local data for this module... */
BOOLEAN Debug = FALSE; /* TRUE to enable debug (verbose) mode */
BOOLEAN InitNeeded = TRUE; /* TRUE if we need to (re) initialize */
WORD RebootVector[2]; /* previous INT 19H vector contents */
extern DWORD partitionoffset;
extern BYTE sd_card_check;
extern BYTE portbase;
BYTE partition_number;
/* fmemcpy */
/* This function is equivalent to the C RTL _fmemcpy routine. We have */
/* to define it here because BPC is unable to generate inline code for */
/* _fmemcpy (although it can generate equivalent code for memcpy!). */
void fmemcpy (void far *dst, void far *src, WORD n)
{
_asm {
les di,dword ptr dst
mov dx,word ptr src+2
mov si,word ptr src
mov cx,n
push ds
mov ds,dx
rep movsb
pop ds
}
}
/* fmemset */
/* This is the equivalent to _fmemset. It is here for exactly the */
/* same reasons as fmemcpy !!! */
void fmemset (void far *dst, BYTE c, WORD n)
{
_asm {
mov al,c
les di,dword ptr dst
mov cx,n
rep stosb
}
}
/* Media Check */
/* DOS calls this function to determine if the tape in the drive has */
/* been changed. The SD hardware can't determine this (like many */
/* older 360K floppy drives), and so we always return the "Don't Know" */
/* response. This works well enough... */
PUBLIC void MediaCheck (rh_media_check_t far *rh)
{
if (Debug) cdprintf("SD: media check: unit=%d\n", rh->rh.unit);
rh->media_status = SDMediaCheck(rh->rh.unit) ? -1 : 0;
rh->rh.status = DONE;
}
/* Build BPB */
/* DOS uses this function to build the BIOS parameter block for the */
/* specified drive. For diskettes, which support different densities */
/* and formats, the driver actually has to read the BPB from the boot */
/* sector on the disk. */
PUBLIC void BuildBPB (rh_get_bpb_t far *rh)
{
if (Debug)
cdprintf("SD: build BPB: unit=%d\n", rh->rh.unit);
rh->bpb = &bpb;
rh->rh.status = DONE;
}
/* Get Parameters */
/* This routine implements the Get Parameters subfunction of the DOS */
/* Generic IOCTL call. It gets a pointer to the device paramters block, */
/* which it then fills in. We do NOT create the track/sector map that */
/* is defined by recent DOS manuals to be at the end of the block - it */
/* never seems to be used... */
PUBLIC void GetParameters (device_params_t far *dp)
{
dp->form_factor = FORM_FACTOR; dp->attributes = 0; dp->media_type = 0;
dp->cylinders = bpb.total_sectors / (bpb.track_size * bpb.head_count);
fmemcpy(&(dp->bpb), &bpb, sizeof(bpb_t));
}
/* dos_error */
/* This routine will translate a SD error code into an appropriate */
/* DOS error code. This driver never retries on any error condition. */
/* For actual tape read/write errors it's pointless because the drive */
/* will have already tried several times before reporting the failure. */
/* All the other errors (e.g. write lock, communications failures, etc) */
/* are not likely to succeed without user intervention, so we go thru */
/* the usual DOS "Abort, Retry or Ignore" dialog. Communications errors */
/* are a special situation. In these cases we also set global flag to */
/* force a controller initialization before the next operation. */
int dos_error (int status)
{
switch (status) {
case RES_OK: return 0;
case RES_WRPRT: return WRITE_PROTECT;
case RES_NOTRDY: InitNeeded= TRUE; return NOT_READY;
case RES_ERROR: InitNeeded= TRUE; return BAD_SECTOR;
case RES_PARERR: return CRC_ERROR;
default:
cdprintf("SD: unknown drive error - status = 0x%2x\n", status);
return GENERAL_FAILURE;
}
}
/* drive_init */
/* This routine should be called before every I/O function. If the */
/* last I/O operation resulted in a protocol error, then this routine */
/* will re-initialize the drive and the communications. If the drive */
/* still won't talk to us, then it will set a general failure code in */
/* the request header and return FALSE. */
BOOLEAN drive_init (rh_t far *rh)
{
if (!InitNeeded) return TRUE;
if (!SDInitialize(rh->unit, partition_number, &bpb)) {
if (Debug) cdprintf("SD: drive failed to initialize\n");
rh->status = DONE | ERROR | GENERAL_FAILURE;
return FALSE;
}
InitNeeded = FALSE;
if (Debug) cdprintf("SD: drive initialized\n");
return TRUE;
}
/* Read Data */
PUBLIC void ReadBlock (rh_io_t far *rh)
{
DWORD lbn;
WORD count; int status; BYTE far *dta;
WORD sendct;
if (Debug)
cdprintf("SD: read block: unit=%d, start=%d, count=%d, dta=%4x:%4x\n",
rh->rh.unit, rh->start, rh->count, FP_SEG(rh->dta), FP_OFF(rh->dta));
if (!drive_init ((rh_t far *) rh)) return;
count = rh->count, lbn = rh->start, dta = rh->dta;
lbn = (rh->start == 0xFFFF) ? rh->longstart : rh->start;
while (count > 0) {
sendct = (count > 16) ? 16 : count;
status = SDRead(rh->rh.unit, lbn, dta, sendct);
if (status != RES_OK) {
if (Debug) cdprintf("SD: read error - status=%d\n", status);
fmemset(dta, 0, BLOCKSIZE);
rh->rh.status = DONE | ERROR | dos_error(status);
return;
}
lbn += sendct;
count -= sendct;
dta += (sendct*BLOCKSIZE);
}
rh->rh.status = DONE;
}
/* Write Data */
/* Write Data with Verification */
PUBLIC void WriteBlock (rh_io_t far *rh, BOOLEAN verify)
{
DWORD lbn;
WORD count; int status; BYTE far *dta;
WORD sendct;
if (Debug)
cdprintf("SD: write block: unit=%d, start=%d, count=%d, dta=%4x:%4x\n",
rh->rh.unit, rh->start, rh->count, FP_SEG(rh->dta), FP_OFF(rh->dta));
if (!drive_init ((rh_t far *) rh)) return;
count = rh->count, dta = rh->dta;
lbn = (rh->start == 0xFFFF) ? rh->longstart : rh->start;
while (count > 0) {
sendct = (count > 16) ? 16 : count;
status = SDWrite(rh->rh.unit, lbn, dta, sendct);
if (status != RES_OK) {
if (Debug) cdprintf("SD: write error - status=%d\n", status);
rh->rh.status = DONE | ERROR | dos_error(status);
return;
}
lbn += sendct;
count -= sendct;
dta += (sendct*BLOCKSIZE);
}
rh->rh.status = DONE;
}
/* Generic IOCTL */
/* The generic IOCTL functions are used by DOS programs to determine */
/* the device geometry, and especially by the FORMAT program to init- */
/* ialize new media. The DOS format program requires us to implement */
/* these three generic IOCTL functions: */
PUBLIC void GenericIOCTL (rh_generic_ioctl_t far *rh)
{
if (Debug)
cdprintf("SD: generic IOCTL: unit=%d, major=0x%2x, minor=0x%2x, data=%4x:%4x\n",
rh->rh.unit, rh->major, rh->minor, FP_SEG(rh->packet), FP_OFF(rh->packet));
if (rh->major == DISK_DEVICE)
switch (rh->minor) {
case GET_PARAMETERS: GetParameters ((device_params_t far *) rh->packet);
rh->rh.status = DONE;
return;
case GET_ACCESS: ((access_flag_t far *) (rh->packet))->allowed = 1;
rh->rh.status = DONE;
return;
case SET_MEDIA_ID:
case SET_ACCESS:
case SET_PARAMETERS:
case FORMAT_TRACK:
rh->rh.status = DONE;
return;
case GET_MEDIA_ID:
default: ;
}
cdprintf("SD: unimplemented IOCTL - unit=%d, major=0x%2x, minor=0x%2x\n",
rh->rh.unit, rh->major, rh->minor);
rh->rh.status = DONE | ERROR | UNKNOWN_COMMAND;
}
/* Generic IOCTL Query */
/* DOS programs can use this function to determine which generic */
/* IOCTL functions a device supports. The query IOCTL driver call will */
/* succeed for any function the driver supports, and fail for others. */
/* Nothing actually happens - just a test for success or failure. */
PUBLIC void IOCTLQuery (rh_generic_ioctl_t far *rh)
{
if (Debug)
cdprintf("SD: generic IOCTL query: unit=%d, major=0x%2x, minor=0x%2x\n",
rh->rh.unit, rh->major, rh->minor);
if (rh->major == DISK_DEVICE)
switch (rh->minor) {
case GET_ACCESS:
case SET_ACCESS:
case SET_MEDIA_ID:
case GET_PARAMETERS:
case SET_PARAMETERS:
case FORMAT_TRACK: rh->rh.status = DONE;
return;
default:
break;
}
rh->rh.status = DONE | ERROR | UNKNOWN_COMMAND;
}
/* SDDriver */
/* This procedure is called by the DRIVER.ASM interface module when */
/* MSDOS calls the driver INTERRUPT routine, and the C code is expected */
/* to define it. Note that the STRATEGY call is handled completely */
/* inside DRIVER.ASM and the address of the request header block is */
/* passed to the C interrupt routine as a parameter. */
PUBLIC void far SDDriver (rh_t far *rh)
{
/*
if (Debug)
cdprintf("SD: request at %4x:%4x, command=%d, length=%d\n",
FP_SEG(rh), FP_OFF(rh), rh->command, rh->length);
*/
switch (rh->command) {
case INITIALIZATION: Initialize ((rh_init_t far *) rh); break;
case MEDIA_CHECK: MediaCheck ((rh_media_check_t far *) rh); break;
case GET_BPB: BuildBPB ((rh_get_bpb_t far *) rh); break;
case INPUT: ReadBlock ((rh_io_t far *) rh); break;
case OUTPUT: WriteBlock ((rh_io_t far *) rh, FALSE); break;
case OUTPUT_VERIFY: WriteBlock ((rh_io_t far *) rh, TRUE); break;
case GENERIC_IOCTL: GenericIOCTL ((rh_generic_ioctl_t far *) rh); break;
case IOCTL_QUERY: IOCTLQuery ((rh_generic_ioctl_t far *) rh); break;
case GET_LOGICAL:
case SET_LOGICAL: rh->status = DONE; break;
default:
cdprintf("SD: unimplemented driver request - command=%d, length=%d\n",
rh->command, rh->length);
rh->status = DONE | ERROR | UNKNOWN_COMMAND;
}
}
PUBLIC void Shutdown (void)
{
long i;
cdprintf("SD: Shutdown\n");
for (i=0; i <100000; ++i);
/* SDClose(); */
JMPVECTOR(RebootVector);
}
/* WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! */
/* */
/* All code following this point in the file is discarded after the */
/* driver initialization. Make absolutely sure that no routine above */
/* this line calls any routine below it!! */
/* */
/* WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! */
/* Driver Initialization */
/* DOS calls this function immediately after the driver is loaded and */
/* expects it to perform whatever initialization is required. Since */
/* this function can never be called again, it's customary to discard */
/* the memory allocated to this routine and any others that are used */
/* only at initialization. This allows us to economize a little on the */
/* amount of memory used. */
/* */
/* This routine's basic function is to initialize the serial port, go */
/* and make contact with the SD card, and then return a table of BPBs to */
/* DOS. If we can't communicate with the drive, then the entire driver */
/* is unloaded from memory. */
PUBLIC void Initialize (rh_init_t far *rh)
{
WORD brkadr, reboot[2]; int status, i;
/* The version number is sneakily stored in the device header! */
cdprintf("SD pport device driver V%c.%c (C) 2014 by Dan Marks\n based on TU58 by Robert Armstrong\n",
header.name[6], header.name[7]);
/* Parse the options from the CONFIG.SYS file, if any... */
if (!parse_options((char far *) rh->bpbtbl)) {
cdprintf("SD: bad options in CONFIG.SYS\n");
goto unload2;
}
/* Calculate the size of this driver by using the address of this */
/* routine. Note that C will return an offset for &Initialize which */
/* is relative to the _TEXT segment. We have to adjust this by adding */
/* the offset from DGROUP. See HEADER.ASM for a memory layout. */
brkadr = ((WORD) &Initialize) + ((_CS - _DS) << 4);
rh->brkadr = MK_FP(_DS, brkadr);
if (Debug)
cdprintf("SD: CS=%4x, DS=%4x, SS=%4x, SP=%4x, break=%4x\n",
_CS, _DS, _SS, _SP, brkadr);
/* Try to make contact with the drive... */
if (Debug) cdprintf("SD: initializing drive\n");
if (!SDInitialize(rh->rh.unit, partition_number, &bpb)) {
cdprintf("SD: drive not connected or not powered\n");
goto unload1;
}
cdprintf("SD: rh = %4x:%4x\n", FP_SEG(rh), FP_OFF(rh));
reboot[0] = ((WORD) &reboot) + ((_CS - _DS) << 4);
reboot[1] = _CS;
GETVECTOR(0x19, RebootVector);
SETVECTOR(0x19, reboot);
if (Debug)
cdprintf("SD: reboot vector = %4x:%4x, old vector = %4x, %4x\n",
reboot[1], reboot[0], RebootVector[1], RebootVector[0]);
/* All is well. Tell DOS how many units and the BPBs... */
cdprintf("SD initialized on DOS drive %c\n",
rh->drive+'A');
rh->nunits = 1; rh->bpbtbl = &bpbtbl;
rh->rh.status = DONE;
if (Debug)
{
cdprintf("SD: BPB data:\n");
cdprintf("Sector Size: %d ", bpb.sector_size);
cdprintf("Allocation unit: %d\n", bpb.allocation_unit);
cdprintf("Reserved sectors: %d ", bpb.reserved_sectors);
cdprintf("Fat Count: %d\n", bpb.fat_count);
cdprintf("Directory size: %d ", bpb.directory_size);
cdprintf("Total sectors: %d\n", bpb.total_sectors);
cdprintf("Media descriptor: %x ", bpb.media_descriptor);
cdprintf("Fat sectors: %d\n", bpb.fat_sectors);
cdprintf("Track size: %d ", bpb.track_size);
cdprintf("Head count: %d\n", bpb.head_count);
cdprintf("Hidden sectors: %d ", bpb.hidden_sectors);
cdprintf("Sector Ct 32 hex: %L\n", bpb.sector_count);
cdprintf("Partition offset: %L\n", partition_offset);
}
return;
/* We get here if there are any errors in initialization. In that */
/* case we can unload this driver completely from memory by setting */
/* (1) the break address to the starting address, (2) the number of */
/* units to 0, and (3) the error flag. */
unload1:
{ };
unload2:
rh->brkadr = MK_FP(_DS, 0); rh->nunits = 0; rh->rh.status = ERROR;
}
/* iseol - return TRUE if ch is any end of line character */
PRIVATE BOOLEAN iseol (char ch)
{ return ch=='\0' || ch=='\r' || ch=='\n'; }
/* spanwhite - skip any white space characters in the string */
PRIVATE char far *spanwhite (char far *p)
{ while (*p==' ' || *p=='\t') ++p; return p; }
/* option_value */
/* This routine will parse the "=nnn" part of an option. It should */
/* be called with a text pointer to what we expect to be the '=' char- */
/* acter. If all is well, it will return the binary value of the arg- */
/* ument and a pointer to the first non-numeric character. If there is */
/* a syntax error, then it will return NULL. */
PRIVATE char far *option_value (char far *p, WORD *v)
{
BOOLEAN null = TRUE;
if (*p++ != '=') return NULL;
for (*v=0; *p>='0' && *p<='9'; ++p)
*v = (*v * 10) + (*p - '0'), null = FALSE;
return null ? NULL : p;
}
/* parse_options */
/* This routine will parse our line from CONFIG.SYS and extract the */
/* driver options from it. The routine returns TRUE if it parsed the */
/* line successfully, and FALSE if there are any problems. The pointer */
/* to CONFIG.SYS that DOS gives us actually points at the first char- */
/* acter after "DEVICE=", so we have to first skip over our own file */
/* name by searching for a blank. All the option values are stored in */
/* global variables (e.g. DrivePort, DriveBaud, etc). */
PRIVATE BOOLEAN parse_options (char far *p)
{
WORD temp;
while (*p!=' ' && *p!='\t' && !iseol(*p)) ++p;
p = spanwhite(p);
while (!iseol(*p)) {
p = spanwhite(p);
if (*p++ != '/') return FALSE;
switch (*p++) {
case 'd', 'D':
Debug = TRUE;
break;
case 'k', 'K':
sd_card_check = 1;
break;
case 'p', 'P':
if ((p=option_value(p,&temp)) == NULL) return FALSE;
if ((temp < 1) || (temp > 4))
cdprintf("SD: Invalid partition number %x\n",temp);
else
partition_number = temp;
break;
case 'b', 'B':
if ((p=option_value(p,&temp)) == NULL) return FALSE;
if ((temp < 1) || (temp > 5))
cdprintf("SD: Invalid port base index %x\n",temp);
else
portbase = temp;
break;
default:
return FALSE;
}
p = spanwhite(p);
}
return TRUE;
}

232
XTMax/Apps/SDPP/DRIVER.H Normal file
View File

@ -0,0 +1,232 @@
/* driver.h - MSDOS commands for a device driver.... */
/* */
/* Copyright (C) 1994 by Robert Armstrong */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */
/* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */
/* Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, visit the website of the Free */
/* Software Foundation, Inc., www.gnu.org. */
#ifndef _DRIVER_H
#define _DRIVER_H
/* NOTE: B implies block device, C implies a character device. */
/* n.n+ implies DOS version n.n or later only! */
#define INITIALIZATION 0 /* (BC) initialize driver */
#define MEDIA_CHECK 1 /* (B) query media changed */
#define GET_BPB 2 /* (B) BIOS parameter block */
#define IOCTL_INPUT 3 /* (BC) read IO control data */
#define INPUT 4 /* (BC) read data */
#define ND_INPUT 5 /* (C) non-destructive input */
#define INPUT_STATUS 6 /* (C) query data available */
#define INPUT_FLUSH 7 /* (C) flush input buffers */
#define OUTPUT 8 /* (BC) write data */
#define OUTPUT_VERIFY 9 /* (BC) write data with verify */
#define OUTPUT_STATUS 10 /* (C) query output busy */
#define OUTPUT_FLUSH 11 /* (C) flush output buffers */
#define IOCTL_OUTPUT 12 /* (BC) write IO control data */
#define DEVICE_OPEN 13 /* (BC) (3.0+) device is to be opened */
#define DEVICE_CLOSE 14 /* (BC) (3.0+) " " " " closed */
#define REMOVABLE_MEDIA 15 /* (B) (3.0+) query removable media */
#define OUTPUT_BUSY 16 /* (C) (3.0+) output data until busy */
#define GENERIC_IOCTL 19 /* (B) (3.2+) */
#define GET_LOGICAL 23 /* (BC) (3.2+) */
#define SET_LOGICAL 24 /* (BC) (3.2+) */
#define IOCTL_QUERY 25 /* (BC) (5.0+) */
/* Flag bits for the request header status word... */
#define ERROR 0x8000 /* indicates any error condition */
#define BUSY 0x0200 /* prevents further operations */
#define DONE 0x0100 /* set on request completion */
/* Error codes that may be returned by a device driver... */
#define WRITE_PROTECT 0x00 /* diskette is write protected */
#define UNKNOWN_UNIT 0x01 /* unit number does not exist */
#define NOT_READY 0x02 /* device is not ready */
#define UNKNOWN_COMMAND 0x03 /* unknown command code */
#define CRC_ERROR 0x04 /* data check (CRC) error */
#define HDR_LEN_ERROR 0x05 /* bad request header length */
#define SEEK_ERROR 0x06 /* seek failure */
#define UNKNOWN_MEDIA 0x07 /* unknown media (e.g. wrong density) */
#define BAD_SECTOR 0x08 /* sector not found */
#define NO_PAPER 0x09 /* printer out of paper */
#define WRITE_FAULT 0x0A /* write failure */
#define READ_FAULT 0x0B /* read failure */
#define GENERAL_FAILURE 0x0C /* general failure */
#define MEDIA_CHANGED 0x0F /* invalid diskette change */
/* Values associated with function 19 - Generic IOCTL... */
#define SERIAL_DEVICE 0x01 /* device category: any serial device */
#define CONSOLE_DEVICE 0x03 /* " " : console (display) */
#define PARALLEL_DEVICE 0x05 /* " " : parallel printer */
#define DISK_DEVICE 0x08 /* " " : any disk (block) */
#define SET_PARAMETERS 0x40 /* disk device: set device parameters */
#define GET_PARAMETERS 0x60 /* " " : get " " " */
#define WRITE_TRACK 0x41 /* " " : write one track */
#define READ_TRACK 0x61 /* " " : read " " */
#define FORMAT_TRACK 0x42 /* " " : format " " */
#define VERIFY_TRACK 0x62 /* " " : verify " " */
#define SET_MEDIA_ID 0x46 /* " " : set media id byte */
#define GET_MEDIA_ID 0x66 /* " " : get " " " */
#define SENSE_MEDIA 0x68 /* " " : sense media type */
#define SET_ACCESS 0x67 /* " " : set access allowed flag */
#define GET_ACCESS 0x47 /* " " : get " " " */
struct _devhdr { /* Device header structure... */
struct _devhdr far *next; /* address of the next device */
WORD attribute; /* device attribute word */
void (near *strtgy) (void); /* strategy routine address */
void (near *intrpt) (void); /* interrupt " " */
BYTE name[8]; /* device name (blank filled!) */
};
typedef struct _devhdr devhdr_t;
struct _bpb { /* BIOS Parameter block structure... */
WORD sector_size; /* sector size, in bytes */
BYTE allocation_unit; /* allocation unit size */
WORD reserved_sectors; /* number of reserved (boot) sectors */
BYTE fat_count; /* number of FATs on disk */
WORD directory_size; /* root directory size, in files */
WORD total_sectors; /* device size, in sectors */
BYTE media_descriptor; /* media descriptor code from the BIOS */
WORD fat_sectors; /* number of sectors per FAT */
WORD track_size; /* track size, in sectors */
WORD head_count; /* number of heads */
LONG hidden_sectors; /* offset of this hard disk partition */
/* The following device size is used only for disks > 32Mb. In that */
/* case, the total_sectors field should be zero! */
LONG sector_count; /* device size, in sectors */
};
typedef struct _bpb bpb_t;
struct _rhfixed { /* Fixed preamble for every request... */
BYTE length; /* length of the header, in bytes */
BYTE unit; /* physical unit number requested */
BYTE command; /* device driver command code */
WORD status; /* status returned by the driver */
BYTE reserved[8]; /* reserved (unused) bytes */
};
typedef struct _rhfixed rh_t;
/* NOTE: count is in _bytes_ for character type device drivers, and */
/* in _sectors_ for block type drivers!! */
typedef near * (bpbtbl_t[]);
struct _rh_init { /* INITIALIZATION(0) */
rh_t rh; /* fixed portion of the header */
BYTE nunits; /* number of units supported by driver */
BYTE far *brkadr; /* break address (memory used) */
bpbtbl_t far *bpbtbl; /* pointer to array of BPBs */
BYTE drive; /* first available drive number */
};
typedef struct _rh_init rh_init_t;
struct _rh_media_check { /* MEDIA_CHECK(1) */
rh_t rh; /* fixed portion of the request */
BYTE media_type; /* media descriptor byte from BIOS */
BYTE media_status; /* new media status flags */
BYTE far *volume_id; /* pointer to volume ID string */
};
typedef struct _rh_media_check rh_media_check_t;
struct _rh_get_bpb { /* GET_BPB(2) */
rh_t rh; /* fixed portion of the request */
BYTE media_type; /* media descriptor byte from BIOS */
BYTE far *dta; /* address for data transfer */
bpb_t far *bpb; /* pointer to the BPB */
};
typedef struct _rh_get_bpb rh_get_bpb_t;
struct _rh_ioctl { /* IOCTL_INPUT(3), IOCTL_OUTPUT(12) */
rh_t rh; /* fixed portion of the request */
BYTE media_type; /* media descriptor byte from BIOS */
BYTE far *dta; /* address for data transfer */
WORD count; /* transfer count (bytes or sectors) */
WORD start; /* starting sector number */
};
typedef struct _rh_ioctl rh_ioctl_t;
struct _rh_io { /* INPUT(4),OUTPUT(8),OUTPUT_VERIFY(9) */
rh_t rh; /* fixed portion of the request */
BYTE media_type; /* media descriptor byte from BIOS */
BYTE far *dta; /* address for data transfer */
WORD count; /* transfer count (bytes or sectors) */
WORD start; /* starting sector number */
BYTE far *volume_id; /* address of volume ID string */
DWORD longstart; /* long start for lba */
};
typedef struct _rh_io rh_io_t;
struct _rh_ndinput { /* ND_INPUT(5) */
rh_t rh; /* fixed portion of the request */
BYTE ch; /* next input character (returned) */
};
typedef struct _rh_ndinput rh_ndinput_t;
/* INPUT_STATUS(6) has only a request header... */
/* INPUT_FLUSH(7) " " " " " ... */
/* OUTPUT_STATUS(10) " " " " " ... */
/* OUTPUT_FLUSH(11) " " " " " ... */
/* DEVICE_OPEN(13) " " " " " ... */
/* DEVICE_CLOSE(14) " " " " " ... */
/* REMOVABLE_MEDIA(15) " " " " " ... */
struct _rh_output_busy { /* OUTPUT_BUSY(16) */
rh_t rh; /* fixed portion of the request */
BYTE media_type; /* media descriptor byte from BIOS */
BYTE far *dta; /* address for data transfer */
WORD count; /* transfer count (bytes or sectors) */
};
typedef struct _rh_output_busy rh_output_busy_t;
struct _rh_generic_ioctl { /* GENERIC_IOCTL(19), IOCTL_QUERY(25) */
rh_t rh; /* fixed portion of the request */
BYTE major, minor; /* function code - major and minor */
WORD si, di; /* caller's SI and DI registers */
BYTE far *packet; /* address of IOCTL data packet */
};
typedef struct _rh_generic_ioctl rh_generic_ioctl_t;
struct _rh_logical { /* GET_LOGICAL(23), SET_LOGICAL(24) */
rh_t rh; /* fixed portion of the request */
BYTE io; /* unit code or last device */
BYTE command; /* command code */
WORD status; /* resulting status */
LONG reserved; /* reserved (unused) */
};
typedef struct _rh_logical rh_logical_t;
struct _device_params { /* Generic IOCTL, Get/Set Parameters */
BYTE special; /* special functions and flags */
BYTE form_factor; /* device (not media!) form factor */
WORD attributes; /* physical drive attributes */
WORD cylinders; /* number of cylinders */
BYTE media_type; /* media type (not the media id!) */
bpb_t bpb; /* the entire BIOS parameter block! */
WORD layout[]; /* track layout map */
};
typedef struct _device_params device_params_t;
struct _access_flag { /* Generic IOCTL Get/Set access allowed */
BYTE special; /* special functions and flags */
BYTE allowed; /* non-zero if access is allowed */
};
typedef struct _access_flag access_flag_t;
struct _media_type { /* Generic IOCTL Get media type */
BYTE default_media; /* 1 for default media type */
BYTE form_factor; /* media (not device!) form factor */
};
typedef struct _media_type media_type_t;
#endif

247
XTMax/Apps/SDPP/HEADER.ASM Normal file
View File

@ -0,0 +1,247 @@
PAGE 60, 132
TITLE HEADER - Interface for C Device Drivers
SUBTTL Bob Armstrong [23-Jul-94]
; header.asm - MSDOS device driver header...
;
; Copyright (C) 1994 by Robert Armstrong
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful, but
; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT-
; ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
; Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, visit the website of the Free
; Software Foundation, Inc., www.gnu.org.
; This module receives the DOS device driver calls (there are only two!)
; and sets up an environment suitable for executing C code. The original
; DOS device driver request is then handed off to the C code.
;
; There are two requirements that drive the memory layout of a device
; driver: (1) DOS requires the device header to be the first thing in the
; memory image, and (2) it is desirable to free the memory allocated to
; startup code once the driver has been initialized. These requirements
; force a memory layout like this diagram:
;
; DS, SS -> +----------------------+ <- driver load address
; | |
; | (device header) |
; | |
; \ _DATA \
; | |
; |----------------------|
; | |
; \ _BSS \
; | |
; | (stack) | <- C language stack
; | |
; CS -> |----------------------|
; | |
; \ _TEXT \
; | | <- driver break address
; | |
; \ (startup code) \
; | |
; +----------------------+
;
; This is very similar to the TINY (.COM file) model _except_ that the
; code is at the end of the memory image rather than the start. This
; turns out to be a major problem because the code generated by TURBO
; assumes that the CS register contains the start of the _TEXT segment
; and NOT the DGROUP. This is contrary to the documentation in the
; Borland manual and _I_ think it's a bug. Note that this bug is
; asymptomatic in .COM files because the _TEXT segment is normally the
; first thing in the DGROUP.
;
; To get around this problem we use the SMALL model (i.e. CS != DS).
; Trouble is, when this driver is loaded the only thing we know is the
; address of the start of the driver and that's in the CS register.
; This means that we have to calculate an appropriate CS value to use
; in the C code.
;
; Another unrelated issue is the stack size. The stack DOS uses when
; a driver is called has room for about 20 PUSHes. This isn't enough
; for any serious C code, so we only use the DOS stack to save all the
; registers. After that we switch to our own stack, which is located
; in the _BSS segment. Of course we have to put the DOS stack pointer
; back before returning.
;
; In order to ensure that the device header appears first in the load
; image it must be defined in this module. We also define the BIOS
; Parameter Block (BPB) and the table of BPB pointers in this module.
; These things are unfortunate because those parts of this file must be
; modified for each different driver.
;
; The only interface between this module and the C code is a single
; far pointer which must be exported by the C code. This pointer would
; be declared as:
;
; void (far *c_driver) (rh_t far *);
;
; The offset part (first word!) of this pointer must be initialized at
; link time with the offset (relative to _TEXT) of the routine that
; will handle DOS driver calls. The segment part of this pointer is
; filled in by this module with the CS value we compute before the
; first call. The C driver routine is passed a far pointer to the
; DOS request header. Everything else is up to the C code.
;
; Bob Armstrong [22-July-1994]
.8086
; This is the size of the new stack we create for the C code...
STACKSIZE EQU 256 ; use whatever seems appropriate...
; This DGROUP, and the order the segments appear in this module, will
; force the load order to be as described above!
DGROUP GROUP _DATA, _BSS, _TEXT
; The _DATA segment (initialized data) for this module contains the
; MSDOS device header and the BIOS parameter blocks...
_DATA SEGMENT WORD PUBLIC 'DATA'
PUBLIC _header, _bpb, _bpbtbl
; Header attribute bits for block devices:
;
; 0002H (B1) - 32 bit sector addresses
; 0040H (B6) - Generic IOCTL, Get/Set logical device
; 0080H (B7) - IOCTL Query
; 0800H (B11) - Open/Close device, Removable media
; 2000H (B13) - IBM format
; 4000H (B14) - IOCTL read/write
; 8000H (B15) - zero for block device!
; The DOS device header...
_header DD -1 ; link to the next device
DW 00C0H ; block device, non-IBM format, generic IOCTL
DW DGROUP:STRATEGY ; address of the strategy routine
DW DGROUP:INTERRUPT; " " " interrupt "
DB 1 ; number of drives
DB 'SDCDv11' ; DOS doesn't really use these bytes
; The geometry (sectors/track, tracks/cylinder) defined in the BPB is rather
; arbitrary in the case of the TU58, but there are things to watch out for.
; First, the DOS FORMAT program has a bug which causes it to crash or worse
; yet, exit silently without doing anything for devices with large numbers of
; sectors per track. Experiments show that 64 sectors/track is safe (at least
; for DOS v5) but 128 is not. Second, the DRIVER.C module must calculate the
; number of cylinders by cylinders = device_size / (sectors * heads). This
; value must always come out to an integer.
; The BIOS Parameter Block...
_bpb DW 512 ; sector size
DB 1 ; cluster size
DW 1 ; number of reserved sectors
DB 2 ; number of FAT copies
DW 48 ; number of files in root directory
DW 512 ; total number of sectors
DB 0F0H ; media descriptor
DW 2 ; number of sectors per FAT
DW 64 ; sectors per track
DW 2 ; number of heads
DD 0 ; number of hidden sectors
DD 0 ; large sector count
; This table contains one BPB pointer for each physical drive...
_bpbtbl DW DGROUP:_bpb ; the BPB for unit 0
; DW DGROUP:_bpb ; " " " " 1
; This doubleword points to the entry point of the C driver code...
EXTRN _c_driver:WORD
_DATA ENDS
; The _BSS segment contains uninitialized static data...
_BSS SEGMENT WORD PUBLIC 'BSS'
PUBLIC _STACK
; Local variables for this module...
RHPTR DW 2 DUP (?) ; offset and segment of the request header
OLDSTK DW 2 DUP (?) ; original stack offset and segment
; This is the stack while the C code is running...
DB STACKSIZE DUP (?)
_STACK LABEL WORD ; the address of a stack is at its _top_ !
_BSS ENDS
; WARNING! The _TEXT segment must be paragraph aligned!
_TEXT SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:DGROUP, DS:DGROUP, SS:DGROUP, ES:NOTHING
; These words give the offsets of the _TEXT segment and the stack, both
; relative to the DGROUP. Note that you can't define use "DGROUP:_TEXT"
; as this generates a segment relocation record in the .EXE file which
; will prevent you from converting to a .COM file. The way its written
; works, but assumes that it is the first thing in this module.
CSOFF DW DGROUP:CSOFF
NEWSTK DW DGROUP:_STACK
; This is the entry for the STRATEGY procedure. DOS calls this routine
; with the address of the request header, but all the work is expected
; to occur in the INTERRUPT procedure. All we do here is to save the
; address of the request for later processing. Since this function is
; trivial, we never call any C code...
PUBLIC STRATEGY
STRATEGY PROC FAR
MOV CS:RHPTR,BX ; save the request header offset
MOV CS:RHPTR+2,ES ; and its segment
RET ; that's all there is to do
STRATEGY ENDP
; And now the INTERRUPT routine. This routine has to save all the
; registers, switch to the new stack, set up the segment registers,
; and call the C language driver routine while passing it the address
; of the request header (saved by the strategy routine).
PUBLIC INTERRUPT
INTERRUPT PROC FAR
PUSH DS ES AX BX CX DX DI SI BP
CLI ; interrupts OFF while the stack is unsafe!
MOV CS:OLDSTK+2,SS ; save the current stack pointer
MOV CS:OLDSTK,SP ; ...
MOV BX,CS ; then setup the new stack
MOV SS,BX ; ...
MOV SP,NEWSTK ; ...
STI ; interrupts are safe once again
MOV AX,CSOFF ; compute the correct code segment address
SHR AX,4 ; ... for the _TEXT segment
ADD AX,BX ; ...
MOV _c_driver+2,AX ; fix the pointer appropriately
MOV DS,BX ; setup DS for the C code
CLD ; the C code will assume this state
PUSH CS:RHPTR+2 ; pass the request header
PUSH CS:RHPTR ; address as a parameter
CALL DWORD PTR _c_driver; call the C entry point
CLI ; interrupts OFF once again
MOV SS,CS:OLDSTK+2 ; and restore the original stack
MOV SP,CS:OLDSTK ; ...
STI ; ...
POP BP SI DI DX CX BX AX ES DS
RET
INTERRUPT ENDP
_TEXT ENDS
END

40
XTMax/Apps/SDPP/INTEGER.H Normal file
View File

@ -0,0 +1,40 @@
/*-------------------------------------------*/
/* Integer type definitions for FatFs module */
/*-------------------------------------------*/
#ifndef _INTEGER
#define _INTEGER
#ifdef _WIN32 /* FatFs development platform */
#include <windows.h>
#include <tchar.h>
#else /* Embedded platform */
/* These types must be 16-bit, 32-bit or larger integer */
typedef int INT;
typedef unsigned int UINT;
/* These types must be 8-bit integer */
typedef char CHAR;
typedef unsigned char UCHAR;
typedef unsigned char BYTE;
/* These types must be 16-bit integer */
typedef short SHORT;
typedef unsigned short USHORT;
typedef unsigned short WORD;
typedef unsigned short WCHAR;
/* These types must be 32-bit integer */
#ifndef DEFINEDLONG
#define DEFINEDLONG
typedef long LONG;
#endif
typedef unsigned long ULONG;
typedef unsigned long DWORD;
#endif
#endif

340
XTMax/Apps/SDPP/LICENSE.TXT Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

26
XTMax/Apps/SDPP/MAKEFILE Normal file
View File

@ -0,0 +1,26 @@
# Makefile for the TU58 Device Driver project - RLA [12-Aug-94]
CC=bcc -c -ms -Z -O -Ol -Oe
ASM=tasm -mx
.c.obj:
$(CC) $<
.asm.obj:
$(ASM) $*
sd.sys: header.obj cprint.obj sd.obj sdmm.obj driver.obj
tlink -t -m -s -n header cprint sd sdmm driver, sd.sys
sd.com: header.obj cprint.obj sd.obj sdmm.obj driver.obj
tlink -t -m -s -n header cprint sd sdmm driver, sd.sys
rename sd.sys sd.com
clean:
del *.obj
del sd.sys
driver.obj: cprint.c sdmm.c driver.c cprint.c cprint.h standard.h driver.h sd.h
sd.obj: sd.c sd.h standard.h driver.h
sdmm.obj: sdmm.c diskio.h integer.h
header.obj: header.asm

View File

@ -0,0 +1,72 @@
SD card driver for parallel port
I wrote this to simplify data transfer between my IBM PC and my laptop, because my laptop does
not have a 360k floppy drive but does have an SD card slot.
WARNING: I take **no responsibility** for any damage to your computer, parallel port, or SD
card, or any data. You use this driver at your own risk. It is highly recommended you use
an expendable parallel port card with your expendable SD card, and your expendable data.
It is recommended that you use a level converter IC with between your 5 volt parallel port
outputs and the SD card 3.3 volt inputs. This project is intended as a fun hack for hobbyists
and enthusiasts and not for serious work.
This driver is made available under the GNU General Public License version 2. It incorporates
modified code from ELM Chan Fat FS (http://elm-chan.org/fsw/ff/00index_e.html).
Usage:
In your config.sys file
DEVICE=SD.SYS /d /k /p=<partition #> /b=<port base index>
Loads and installs the SD card driver.
/d = debugging mode (displays copious debugging messsages)
/k = use card detect signal to inform dos that card is attached
/p = partition number (1-4) to partition in MBR to use. Default: first available.
/b = port base index of parallel port, one of
1=0x3BC, 2=0x378, 3=0x278, 4=0x3E8, 5=0x2E8
Default: 0x378
For best results, format your SD card with a FAT16 partition which is less than 32 MB in size.
NOTE: Many versions of DOS don't know how to handle FAT32, and many can't have FAT16 with a
partition size greater than 32 MB. Therefore, if you want to play with this, make your parition on
the card FAT16 and less than 32 MB. This assures the best compatibility. You can have multiple copies of the
driver loaded if there are multiple partitions on your SD card you want to use simultaneously.
I have used Adafruit's microSD adapter
(http://www.adafruit.com/products/254?gclid=CLH7l4iEkrwCFQPNOgod7BkAQA)
if you want a relatively simple way to interface your PC parallel port to
the SD card. The adapter provides the 3.3 volts needed to power the SD card, as well
as a the level shifting between the 5 volt parallel port output and the 3.3 volt input.
If you directly connect a 5 volt output to a 3.3 volt input, you risk latching up the
3.3 volt input and damaging the card or computer from exceesive current.
Some have used series resistors instead of the level converters, but I found this
to not be that reliable and still may have this problem. Also, some SD cards MISO/DO
outputs are unable to drive a TTL input of some parallel ports, so you may need to add
a buffer between the two as well. I have found quite a bit of variability in the drive
current required for the inputs of various parallel ports.
The driver uses the very slow serial peripheral interface (SPI) mode of the SD card. The
speed, which depends on your PC speed, could be as slow as 10 kilobytes/second. This is
not a replacement for your hard drive. Your parallel port should be configured for standard
mode (not bidirectional) if applicable.
The connections between the parallel port and the SD card are as follows:
Parallel port SD card
PIN 25 signal GND GND (Vss)
+3.3V Vdd (power)
PIN 2 signal D0 CMD / MOSI / DI (SPI data in)
PIN 3 signal D1 SCLK / CLK (SPI clock)
PIN 4 signal D2 DAT3 / CS (SPI chip select)
PIN 13 signal SELECT DAT0 / MISO / DO (SPI data out)
PIN 11 signal BUSY Card detect (if you SD card slot has one)
For similar setups, look up parallel port to JTAG adapters which are used for in circuit
programming and debugging.
Good luck and be careful!

236
XTMax/Apps/SDPP/SD.C Normal file
View File

@ -0,0 +1,236 @@
/* sd.c */
/* */
/* Copyright (C) 1994 by Robert Armstrong */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */
/* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */
/* Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, visit the website of the Free */
/* Software Foundation, Inc., www.gnu.org. */
/* */
/* */
/* The functions provided are: */
/* */
/* SDInitialize - establish two way communications with the drive */
/* SDRead - read one 512 byte logical block from the tape */
/* SDWrite - write one 512 byte logical block to the tape */
/* SDMediaCheck - see if card detect has changed */
/* */
/* Normally the SDInitialize routine would be called */
/* during the DOS device driver initialization, and then the SDRead and */
/* */
#include <stdio.h> /* needed for NULL, etc */
#include <mem.h> /* memset, memcopy, etc */
#include "standard.h" /* all definitions for this project */
#include "sd.h" /* device protocol and data defintions */
#include "diskio.h" /* stuff from sdmm.c module */
#include "driver.h"
#include "cprint.h"
DWORD partition_offset = 0;
/* FatFs refers the members in the FAT structures as byte array instead of
/ structure member because the structure is not binary compatible between
/ different platforms */
#define BS_jmpBoot 0 /* Jump instruction (3) */
#define BS_OEMName 3 /* OEM name (8) */
#define BPB_BytsPerSec 11 /* Sector size [byte] (2) */
#define BPB_SecPerClus 13 /* Cluster size [sector] (1) */
#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (2) */
#define BPB_NumFATs 16 /* Number of FAT copies (1) */
#define BPB_RootEntCnt 17 /* Number of root directory entries for FAT12/16 (2) */
#define BPB_TotSec16 19 /* Volume size [sector] (2) */
#define BPB_Media 21 /* Media descriptor (1) */
#define BPB_FATSz16 22 /* FAT size [sector] (2) */
#define BPB_SecPerTrk 24 /* Track size [sector] (2) */
#define BPB_NumHeads 26 /* Number of heads (2) */
#define BPB_HiddSec 28 /* Number of special hidden sectors (4) */
#define BPB_TotSec32 32 /* Volume size [sector] (4) */
#define BS_DrvNum 36 /* Physical drive number (2) */
#define BS_BootSig 38 /* Extended boot signature (1) */
#define BS_VolID 39 /* Volume serial number (4) */
#define BS_VolLab 43 /* Volume label (8) */
#define BS_FilSysType 54 /* File system type (1) */
#define BPB_FATSz32 36 /* FAT size [sector] (4) */
#define BPB_ExtFlags 40 /* Extended flags (2) */
#define BPB_FSVer 42 /* File system version (2) */
#define BPB_RootClus 44 /* Root directory first cluster (4) */
#define BPB_FSInfo 48 /* Offset of FSINFO sector (2) */
#define BPB_BkBootSec 50 /* Offset of backup boot sector (2) */
#define BS_DrvNum32 64 /* Physical drive number (2) */
#define BS_BootSig32 66 /* Extended boot signature (1) */
#define BS_VolID32 67 /* Volume serial number (4) */
#define BS_VolLab32 71 /* Volume label (8) */
#define BS_FilSysType32 82 /* File system type (1) */
#define FSI_LeadSig 0 /* FSI: Leading signature (4) */
#define FSI_StrucSig 484 /* FSI: Structure signature (4) */
#define FSI_Free_Count 488 /* FSI: Number of free clusters (4) */
#define FSI_Nxt_Free 492 /* FSI: Last allocated cluster (4) */
#define MBR_Table 446 /* MBR: Partition table offset (2) */
#define SZ_PTE 16 /* MBR: Size of a partition table entry */
#define BS_55AA 510 /* Boot sector signature (2) */
BYTE local_buffer[BLOCKSIZE];
#define LD_WORD(x) *((WORD *)(BYTE *)(x))
#define LD_DWORD(x) *((DWORD *)(BYTE *)(x))
/*-----------------------------------------------------------------------*/
/* Load a sector and check if it is an FAT boot sector */
/*-----------------------------------------------------------------------*/
static
BYTE check_fs (BYTE unit,
/* 0:FAT boor sector, 1:Valid boor sector but not FAT, */
/* 2:Not a boot sector, 3:Disk error */
DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */
)
{
if (disk_read (unit, local_buffer, sect, 1) != RES_OK)
return 3;
if (LD_WORD(&local_buffer[BS_55AA]) != 0xAA55) /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */
return 2;
if ((LD_DWORD(&local_buffer[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */
return 0;
if ((LD_DWORD(&local_buffer[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */
return 0;
return 1;
}
/*-----------------------------------------------------------------------*/
/* Find logical drive and check if the volume is mounted */
/*-----------------------------------------------------------------------*/
static
int find_volume (
BYTE unit,
BYTE partno,
bpb_t *bpb
)
{
BYTE fmt;
DSTATUS stat;
WORD secsize;
DWORD bsect;
stat = disk_status(unit);
if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */
return 0; /* The file system object is valid */
}
/* The file system object is not valid. */
/* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */
stat = disk_initialize(unit); /* Initialize the physical drive */
if (stat & STA_NOINIT) /* Check if the initialization succeeded */
return -1; /* Failed to initialize due to no medium or hard error */
/* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */
bsect = 0;
fmt = check_fs(unit, bsect); /* Load sector 0 and check if it is an FAT boot sector as SFD */
if (fmt == 1 || (!fmt && (partno))) { /* Not an FAT boot sector or forced partition number */
UINT i;
DWORD br[4];
for (i = 0; i < 4; i++) { /* Get partition offset */
BYTE *pt = &local_buffer[MBR_Table + i * SZ_PTE];
br[i] = pt[4] ? LD_DWORD(&pt[8]) : 0;
}
i = partno; /* Partition number: 0:auto, 1-4:forced */
if (i) i--;
do { /* Find an FAT volume */
bsect = br[i];
fmt = bsect ? check_fs(unit, bsect) : 2; /* Check the partition */
} while (!partno && fmt && ++i < 4);
}
if (fmt == 3) return -2; /* An error occured in the disk I/O layer */
if (fmt) return -3; /* No FAT volume is found */
/* An FAT volume is found. Following code initializes the file system object */
secsize = LD_WORD(local_buffer + BPB_BytsPerSec);
if (secsize != BLOCKSIZE)
return -3;
bpb->sector_size = BLOCKSIZE;
bpb->allocation_unit = local_buffer[BPB_SecPerClus]; /* Number of sectors per cluster */
bpb->reserved_sectors = LD_WORD(local_buffer+BPB_RsvdSecCnt); /* Number of reserved sectors */
if (!bpb->reserved_sectors) return -3; /* (Must not be 0) */
bpb->fat_count = local_buffer[BPB_NumFATs]; /* Number of FAT copies */
if (bpb->fat_count == 0)
bpb->fat_count = 2;
bpb->directory_size = LD_WORD(local_buffer+BPB_RootEntCnt);
bpb->total_sectors = LD_WORD(local_buffer+BPB_TotSec16);
if (!bpb->total_sectors)
bpb->sector_count = LD_DWORD(local_buffer+BPB_TotSec32);
else
bpb->sector_count = bpb->total_sectors;
bpb->media_descriptor = local_buffer[BPB_Media];
bpb->fat_sectors = LD_WORD(local_buffer+BPB_FATSz16); /* Number of sectors per FAT */
bpb->track_size = LD_WORD(local_buffer+BPB_SecPerTrk); /* Number of sectors per cluster */
bpb->head_count = LD_WORD(local_buffer+BPB_NumHeads); /* Number of sectors per cluster */
bpb->hidden_sectors = 1;
partition_offset = bsect;
return 0;
}
/* SDInitialize */
PUBLIC BOOLEAN SDInitialize (BYTE unit, BYTE partno, bpb_t *bpb)
{
if (find_volume(unit,partno,bpb) < 0)
return FALSE;
return TRUE;
}
/* SDMediaCheck */
PUBLIC BOOLEAN SDMediaCheck (BYTE unit)
{
return (disk_result(unit) == RES_OK) ? FALSE : TRUE;
}
/* SDRead */
/* IMPORTANT! Blocks are always 512 bytes! Never more, never less. */
/* */
/* INPUTS: */
/* unit - selects tape drive 0 (left) or 1 (right) */
/* lbn - logical block number to be read [0..511] */
/* buffer - address of 512 bytes to receive the data read */
/* */
/* RETURNS: operation status as reported by the TU58 */
/* */
PUBLIC int SDRead (WORD unit, DWORD lbn, BYTE far *buffer, WORD count)
{
return disk_read (unit, buffer, lbn + partition_offset, count);
}
/* SDWrite */
/* IMPORTANT! Blocks are always 512 bytes! Never more, never less. */
/* */
/* INPUTS: */
/* unit - selects tape drive 0 (left) or 1 (right) */
/* lbn - logical block number to be read [0..511] */
/* buffer - address of 512 bytes containing the data to write */
/* verify - TRUE to ask the TU58 for a verification pass */
/* */
/* RETURNS: operation status as reported by the TU58 */
/* */
PUBLIC int SDWrite (WORD unit, DWORD lbn, BYTE far *buffer, WORD count)
{
return disk_write (unit, buffer, lbn + partition_offset, count);
}

22
XTMax/Apps/SDPP/SD.H Normal file
View File

@ -0,0 +1,22 @@
/* sd.h - SD card driver glue */
#ifndef _SD_H
#define _SD_H
#include "integer.h"
#include "driver.h"
#define BLOCKSIZE 512
extern DWORD partition_offset;
/* SDInitialize - establish two way communications with the drive */
BOOLEAN SDInitialize (BYTE unit, BYTE partno, bpb_t *bpb);
/* SDRead - read one 512 byte logical block from the tape */
int SDRead (WORD, DWORD, BYTE far *, WORD count);
/* SDWrite - write one 512 byte logical block to the tape */
int SDWrite (WORD, DWORD, BYTE far *, WORD count);
/* SDMediaCheck - check if media changed */
BOOLEAN SDMediaCheck (BYTE unit);
#endif

776
XTMax/Apps/SDPP/SDMM.C Normal file
View File

@ -0,0 +1,776 @@
/*------------------------------------------------------------------------/
/ Foolproof MMCv3/SDv1/SDv2 (in SPI mode) control module
/-------------------------------------------------------------------------/
/
/ Copyright (C) 2013, ChaN, all right reserved.
/
/ * This software is a free software and there is NO WARRANTY.
/ * No restriction on use. You can use, modify and redistribute it for
/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
/ * Redistributions of source code must retain the above copyright notice.
/
/-------------------------------------------------------------------------/
Features and Limitations:
* Easy to Port Bit-banging SPI
It uses only four GPIO pins. No interrupt, no SPI is needed.
* Platform Independent
You need to modify only a few macros to control GPIO ports.
* Low Speed
The data transfer rate will be several times slower than hardware SPI.
* No Media Change Detection
Application program needs to perform f_mount() after media change.
/-------------------------------------------------------------------------*/
#include <conio.h>
#include "diskio.h" /* Common include file for FatFs and disk I/O layer */
#include "cprint.h"
/*-------------------------------------------------------------------------*/
/* Platform dependent macros and functions needed to be modified */
/*-------------------------------------------------------------------------*/
#define outportbyte(a,b) outp(a,b)
#define inportbyte(a) inp(a)
static WORD portbases[5] = {0x3BC,0x378,0x278,0x3E8,0x2E8};
BYTE sd_card_check = 0;
BYTE portbase = 2;
WORD OUTPORT=0x378;
WORD STATUSPORT=0x379;
WORD CONTROLPORT=0x37A;
#define VAR_INIT() {STATUSPORT=OUTPORT+1; CONTROLPORT=OUTPORT+2; outportbyte(CONTROLPORT,inportbyte(CONTROLPORT) & 0xCF); }
/* MISOPIN (SD card DAT0/DO pin 7) is PPORT SELECT (DB-25 pin 13) */
#define MISOPIN (0x01 << 4)
/* Card Detect (N/A) is PPORT BUSY (DB-25 pin 11) */
#define CDDETECTPIN (0x01 << 7)
/* Do not interface 5 to 3.3 volts directly! Use level converter... */
/* MOSI (SD card CMD/DI pin 2) is PPORT D0 (DB-25 pin 2) */
#define MOSIPIN (0x01 << 0)
/* CLOCK (SD card CLK/SCLK pin 5) is PPORT D1 (DB-25 pin 3) */
#define CLOCKPIN (0x01 << 1)
/* Card Select (SD card CAT3/CS pin 1) is PPORT D2 (DB-25 pin 4) */
#define CSPIN (0x01 << 2)
/* Connect ground to one of PPORT pins 18-25 */
#if 1
#define TOUTCHR(x)
#define TOUTHEX(x)
#define TOUTWORD(x)
#else
#define TOUTCHR(x) toutchr(x)
#define TOUTHEX(x) touthex(x)
#define TOUTWORD(x) toutword(x)
static BYTE toutchr (unsigned char ch)
{
_DI = _SI = 0;
_AL = ch; _AH = 0xE; _BX = 0;
asm INT 0x10;
return 0;
}
static BYTE touthex(unsigned char c)
{
char d = c >> 4;
toutchr(d > 9 ? d+('A'-10) : d+'0');
d = c & 0x0F;
toutchr(d > 9 ? d+('A'-10) : d+'0');
return 0;
}
static BYTE toutword(WORD x)
{
touthex(x >> 8);
touthex(x);
return 0;
}
#endif
void setportbase(BYTE val)
{
if ((val >= 1) && (val <= (sizeof(portbases)/sizeof(portbases[0])) ))
OUTPORT = portbases[val-1];
VAR_INIT();
}
#define DO_INIT()
#define DI_INIT()
#define CK_INIT()
#define CS_INIT()
const int bit_delay_val = 10;
#define BITDLY()
#define DO(statusport) (inportbyte((statusport)) & MISOPIN)
#define CDDETECT(statusport) (inportbyte((statusport)) & CDDETECTPIN)
#define CLOCKBITHIGHMOSIHIGH(outport) outportbyte((outport),MOSIPIN|CLOCKPIN)
#define CLOCKBITHIGHMOSILOW(outport) outportbyte((outport),CLOCKPIN)
#define CLOCKBITLOWMOSIHIGH(outport) outportbyte((outport),MOSIPIN)
#define CLOCKBITLOWMOSILOW(outport) outportbyte((outport),0)
#define CLOCKBITHIGHMOSIHIGHNOCS(outport) outportbyte((outport),MOSIPIN|CLOCKPIN|CSPIN)
#define CLOCKBITLOWMOSIHIGHNOCS(outport) outportbyte((outport),MOSIPIN|CSPIN)
#define CS_L(outport) outportbyte((outport),MOSIPIN)
#define CS_H(outport) outportbyte((outport),MOSIPIN|CSPIN)
#define CK_L(outport)
#define ADJ_VAL 1
static
void dly_us (UINT n)
{
_CX = n;
loopit:
_asm {
loop loopit
}
}
#define NOSHIFT
#ifdef NOSHIFT
DWORD dwordlshift(DWORD d, int n)
{
int i;
WORD a = ((WORD *)d)[0];
WORD b = ((WORD *)d)[1];
DWORD r;
for (i=0;i<n;i++)
{
b <<= 1;
b |= (a & 0x8000) ? 1 : 0;
a <<= 1;
}
((WORD *)r)[0] = a;
((WORD *)r)[1] = b;
return r;
}
#define DWORDLSHIFT(d,n) dwordlshift(d,n)
DWORD dwordrshift(DWORD d, int n)
{
int i;
WORD a = ((WORD *)d)[0];
WORD b = ((WORD *)d)[1];
DWORD r;
for (i=0;i<n;i++)
{
a >>= 1;
a |= (b & 0x1) ? 0x8000 : 0;
b >>= 1;
}
((WORD *)r)[0] = a;
((WORD *)r)[1] = b;
return r;
}
#define DWORDRSHIFT(d,n) dwordrshift(d,n)
#else
#define DWORDLSHIFT(d,n) ((d) << (n))
#define DWORDRSHIFT(d,n) ((d) >> (n))
#endif
/*--------------------------------------------------------------------------
Module Private Functions
---------------------------------------------------------------------------*/
/* MMC/SD command (SPI mode) */
#define CMD0 (0) /* GO_IDLE_STATE */
#define CMD1 (1) /* SEND_OP_COND */
#define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
#define CMD8 (8) /* SEND_IF_COND */
#define CMD9 (9) /* SEND_CSD */
#define CMD10 (10) /* SEND_CID */
#define CMD12 (12) /* STOP_TRANSMISSION */
#define CMD13 (13) /* SEND_STATUS */
#define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
#define CMD16 (16) /* SET_BLOCKLEN */
#define CMD17 (17) /* READ_SINGLE_BLOCK */
#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
#define CMD23 (23) /* SET_BLOCK_COUNT */
#define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (24) /* WRITE_BLOCK */
#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
#define CMD32 (32) /* ERASE_ER_BLK_START */
#define CMD33 (33) /* ERASE_ER_BLK_END */
#define CMD38 (38) /* ERASE */
#define CMD55 (55) /* APP_CMD */
#define CMD58 (58) /* READ_OCR */
static
DSTATUS Stat = STA_NOINIT; /* Disk status */
static
BYTE CardType; /* b0:MMC, b1:SDv1, b2:SDv2, b3:Block addressing */
/*-----------------------------------------------------------------------*/
/* Transmit bytes to the card (bitbanging) */
/*-----------------------------------------------------------------------*/
static
void xmit_mmc (
const BYTE DOSFAR * buff, /* Data to be sent */
UINT bc /* Number of bytes to send */
)
{
BYTE d;
WORD outport = OUTPORT;
do {
d = *buff++; /* Get a byte to be sent */
if (d & 0x80)
{ CLOCKBITLOWMOSIHIGH(outport); BITDLY(); CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
} else
{ CLOCKBITLOWMOSILOW(outport); BITDLY(); CLOCKBITHIGHMOSILOW(outport); BITDLY();
}
if (d & 0x40)
{ CLOCKBITLOWMOSIHIGH(outport); BITDLY(); CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
} else
{ CLOCKBITLOWMOSILOW(outport); BITDLY(); CLOCKBITHIGHMOSILOW(outport); BITDLY();
}
if (d & 0x20)
{ CLOCKBITLOWMOSIHIGH(outport); BITDLY(); CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
} else
{ CLOCKBITLOWMOSILOW(outport); BITDLY(); CLOCKBITHIGHMOSILOW(outport); BITDLY();
}
if (d & 0x10)
{ CLOCKBITLOWMOSIHIGH(outport); BITDLY(); CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
} else
{ CLOCKBITLOWMOSILOW(outport); BITDLY(); CLOCKBITHIGHMOSILOW(outport); BITDLY();
}
if (d & 0x08)
{ CLOCKBITLOWMOSIHIGH(outport); BITDLY(); CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
} else
{ CLOCKBITLOWMOSILOW(outport); BITDLY(); CLOCKBITHIGHMOSILOW(outport); BITDLY();
}
if (d & 0x04)
{ CLOCKBITLOWMOSIHIGH(outport); BITDLY(); CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
} else
{ CLOCKBITLOWMOSILOW(outport); BITDLY(); CLOCKBITHIGHMOSILOW(outport); BITDLY();
}
if (d & 0x02)
{ CLOCKBITLOWMOSIHIGH(outport); BITDLY(); CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
} else
{ CLOCKBITLOWMOSILOW(outport); BITDLY(); CLOCKBITHIGHMOSILOW(outport); BITDLY();
}
if (d & 0x01)
{ CLOCKBITLOWMOSIHIGH(outport); BITDLY(); CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
} else
{ CLOCKBITLOWMOSILOW(outport); BITDLY(); CLOCKBITHIGHMOSILOW(outport); BITDLY();
}
} while (--bc);
CLOCKBITLOWMOSIHIGH(outport);
}
/*-----------------------------------------------------------------------*/
/* Receive bytes from the card (bitbanging) */
/*-----------------------------------------------------------------------*/
static
void rcvr_mmc (
BYTE DOSFAR *buff, /* Pointer to read buffer */
UINT bc /* Number of bytes to receive */
)
{
BYTE r;
WORD outport = OUTPORT;
WORD statusport = STATUSPORT;
do {
CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
r = 0; if (DO(statusport)) r++; /* bit7 */
CLOCKBITLOWMOSIHIGH(outport); BITDLY();
CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
r <<= 1; if (DO(statusport)) r++; /* bit6 */
CLOCKBITLOWMOSIHIGH(outport); BITDLY();
CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
r <<= 1; if (DO(statusport)) r++; /* bit5 */
CLOCKBITLOWMOSIHIGH(outport); BITDLY();
CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
r <<= 1; if (DO(statusport)) r++; /* bit4 */
CLOCKBITLOWMOSIHIGH(outport); BITDLY();
CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
r <<= 1; if (DO(statusport)) r++; /* bit3 */
CLOCKBITLOWMOSIHIGH(outport); BITDLY();
CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
r <<= 1; if (DO(statusport)) r++; /* bit2 */
CLOCKBITLOWMOSIHIGH(outport); BITDLY();
CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
r <<= 1; if (DO(statusport)) r++; /* bit1 */
CLOCKBITLOWMOSIHIGH(outport); BITDLY();
CLOCKBITHIGHMOSIHIGH(outport); BITDLY();
r <<= 1; if (DO(statusport)) r++; /* bit0 */
CLOCKBITLOWMOSIHIGH(outport); BITDLY();
*buff++ = r; /* Store a received byte */
} while (--bc);
}
/*-----------------------------------------------------------------------*/
/* Receive bytes from the card (bitbanging) */
/*-----------------------------------------------------------------------*/
static
void dummy_rcvr_mmc (void)
{
int i;
WORD outport = OUTPORT;
CLOCKBITLOWMOSIHIGHNOCS(outport); BITDLY();
for (i=0;i<8;i++)
{
CLOCKBITHIGHMOSIHIGHNOCS(outport); BITDLY();
CLOCKBITLOWMOSIHIGHNOCS(outport); BITDLY();
}
}
/*-----------------------------------------------------------------------*/
/* Wait for card ready */
/*-----------------------------------------------------------------------*/
static
int wait_ready (void) /* 1:OK, 0:Timeout */
{
BYTE d;
UINT tmr;
for (tmr = 5000; tmr; tmr--) { /* Wait for ready in timeout of 500ms */
rcvr_mmc(&d, 1);
if (d == 0xFF) break;
dly_us(100);
}
return tmr ? 1 : 0;
}
/*-----------------------------------------------------------------------*/
/* Deselect the card and release SPI bus */
/*-----------------------------------------------------------------------*/
static
void deselect (void)
{
BYTE d;
CS_H(OUTPORT);
dummy_rcvr_mmc(); /* Dummy clock (force DO hi-z for multiple slave SPI) */
}
/*-----------------------------------------------------------------------*/
/* Select the card and wait for ready */
/*-----------------------------------------------------------------------*/
static
int select (void) /* 1:OK, 0:Timeout */
{
BYTE d;
CS_L(OUTPORT);
rcvr_mmc(&d, 1); /* Dummy clock (force DO enabled) */
if (wait_ready()) return 1; /* OK */
deselect();
return 0; /* Failed */
}
/*-----------------------------------------------------------------------*/
/* Receive a data packet from the card */
/*-----------------------------------------------------------------------*/
static
int rcvr_datablock ( /* 1:OK, 0:Failed */
BYTE DOSFAR *buff, /* Data buffer to store received data */
UINT btr /* Byte count */
)
{
BYTE d[2];
UINT tmr;
for (tmr = 1000; tmr; tmr--) { /* Wait for data packet in timeout of 100ms */
rcvr_mmc(d, 1);
if (d[0] != 0xFF) break;
dly_us(100);
}
if (d[0] != 0xFE) {
return 0; /* If not valid data token, return with error */
}
rcvr_mmc(buff, btr); /* Receive the data block into buffer */
rcvr_mmc(d, 2); /* Discard CRC */
return 1; /* Return with success */
}
/*-----------------------------------------------------------------------*/
/* Send a data packet to the card */
/*-----------------------------------------------------------------------*/
static
int xmit_datablock ( /* 1:OK, 0:Failed */
const BYTE DOSFAR *buff, /* 512 byte data block to be transmitted */
BYTE token /* Data/Stop token */
)
{
BYTE d[2];
if (!wait_ready()) return 0;
d[0] = token;
xmit_mmc(d, 1); /* Xmit a token */
if (token != 0xFD) { /* Is it data token? */
xmit_mmc(buff, 512); /* Xmit the 512 byte data block to MMC */
rcvr_mmc(d, 2); /* Xmit dummy CRC (0xFF,0xFF) */
rcvr_mmc(d, 1); /* Receive data response */
if ((d[0] & 0x1F) != 0x05) /* If not accepted, return with error */
{
return 0;
}
}
return 1;
}
/*-----------------------------------------------------------------------*/
/* Send a command packet to the card */
/*-----------------------------------------------------------------------*/
static
BYTE send_cmd ( /* Returns command response (bit7==1:Send failed)*/
BYTE cmd, /* Command byte */
DWORD arg /* Argument */
)
{
BYTE n, d, buf[6];
if (cmd & 0x80) { /* ACMD<n> is the command sequense of CMD55-CMD<n> */
cmd &= 0x7F;
n = send_cmd(CMD55, 0);
if (n > 1) return n;
}
/* Select the card and wait for ready except to stop multiple block read */
if (cmd != CMD12) {
deselect();
if (!select()) return 0xFF;
}
/* Send a command packet */
buf[0] = 0x40 | cmd; /* Start + Command index */
#ifdef NOSHIFT
buf[1] = ((BYTE *)&arg)[3]; /* Argument[31..24] */
buf[2] = ((BYTE *)&arg)[2]; /* Argument[23..16] */
buf[3] = ((BYTE *)&arg)[1]; /* Argument[15..8] */
buf[4] = ((BYTE *)&arg)[0]; /* Argument[7..0] */
#else
buf[1] = (BYTE)(arg >> 24); /* Argument[31..24] */
buf[2] = (BYTE)(arg >> 16); /* Argument[23..16] */
buf[3] = (BYTE)(arg >> 8); /* Argument[15..8] */
buf[4] = (BYTE)arg; /* Argument[7..0] */
#endif
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0) n = 0x95; /* (valid CRC for CMD0(0)) */
if (cmd == CMD8) n = 0x87; /* (valid CRC for CMD8(0x1AA)) */
buf[5] = n;
TOUTCHR('L');
TOUTHEX(buf[0]);
TOUTHEX(buf[1]);
TOUTHEX(buf[2]);
TOUTHEX(buf[3]);
TOUTHEX(buf[4]);
TOUTHEX(buf[5]);
xmit_mmc(buf, 6);
/* Receive command response */
if (cmd == CMD12) rcvr_mmc(&d, 1); /* Skip a stuff byte when stop reading */
n = 10; /* Wait for a valid response in timeout of 10 attempts */
do
{
rcvr_mmc(&d, 1);
}
while ((d & 0x80) && (--n));
TOUTCHR('P');
TOUTHEX(d);
return d; /* Return with the response value */
}
/*--------------------------------------------------------------------------
Public Functions
---------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* Get Disk Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE drv /* Drive number (always 0) */
)
{
if (drv) return STA_NOINIT;
if ((sd_card_check) && (CDDETECT(STATUSPORT)))
{
Stat = STA_NOINIT;
return STA_NOINIT;
}
return Stat;
}
DRESULT disk_result (
BYTE drv /* Drive number (always 0) */
)
{
if (drv) return RES_NOTRDY;
if ((sd_card_check) && (CDDETECT(STATUSPORT)))
{
Stat = STA_NOINIT;
return RES_NOTRDY;
}
return RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE drv /* Physical drive nmuber (0) */
)
{
BYTE n, ty, cmd, buf[4];
UINT tmr;
DSTATUS s;
setportbase(portbase);
if (drv) return RES_NOTRDY;
if ((sd_card_check) && (CDDETECT(STATUSPORT)))
return RES_NOTRDY;
ty = 0;
for (n = 5; n; n--) {
CS_INIT(); CS_H(OUTPORT); /* Initialize port pin tied to CS */
dly_us(10000); /* 10ms. time for SD card to power up */
CS_INIT(); CS_H(OUTPORT); /* Initialize port pin tied to CS */
CK_INIT(); CK_L(OUTPORT); /* Initialize port pin tied to SCLK */
DI_INIT(); /* Initialize port pin tied to DI */
DO_INIT(); /* Initialize port pin tied to DO */
for (tmr = 10; tmr; tmr--) dummy_rcvr_mmc(); /* Apply 80 dummy clocks and the card gets ready to receive command */
if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */
if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */
rcvr_mmc(buf, 4); /* Get trailing return value of R7 resp */
if (buf[2] == 0x01 && buf[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */
for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state (ACMD41 with HCS bit) */
if (send_cmd(ACMD41, 1UL << 30) == 0) break;
dly_us(1000);
}
if (tmr && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
rcvr_mmc(buf, 4);
ty = (buf[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */
}
}
} else { /* SDv1 or MMCv3 */
if (send_cmd(ACMD41, 0) <= 1) {
ty = CT_SD1; cmd = ACMD41; /* SDv1 */
} else {
ty = CT_MMC; cmd = CMD1; /* MMCv3 */
}
for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state */
if (send_cmd(cmd, 0) == 0) break;
dly_us(1000);
}
if (!tmr || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */
ty = 0;
}
break;
}
}
CardType = ty;
s = ty ? 0 : STA_NOINIT;
Stat = s;
deselect();
return s;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE drv, /* Physical drive nmuber (0) */
BYTE DOSFAR *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
UINT count /* Sector count (1..128) */
)
{
DRESULT dr = disk_result(drv);
if (dr != RES_OK) return dr;
if (!(CardType & CT_BLOCK)) sector = DWORDLSHIFT(sector,9); /* Convert LBA to byte address if needed */
if (count == 1) { /* Single block read */
if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */
&& rcvr_datablock(buff, 512))
count = 0;
}
else { /* Multiple block read */
if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */
do {
if (!rcvr_datablock(buff, 512)) break;
buff += 512;
} while (--count);
send_cmd(CMD12, 0); /* STOP_TRANSMISSION */
}
}
deselect();
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0) */
const BYTE DOSFAR *buff, /* Pointer to the data to be written */
DWORD sector, /* Start sector number (LBA) */
UINT count /* Sector count (1..128) */
)
{
DRESULT dr = disk_result(drv);
if (dr != RES_OK) return dr;
if (!(CardType & CT_BLOCK)) sector = DWORDLSHIFT(sector,9); /* Convert LBA to byte address if needed */
if (count == 1) { /* Single block write */
if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */
&& xmit_datablock(buff, 0xFE))
count = 0;
}
else { /* Multiple block write */
if (CardType & CT_SDC) send_cmd(ACMD23, count);
if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
do {
if (!xmit_datablock(buff, 0xFC)) break;
buff += 512;
} while (--count);
if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
count = 1;
if (!wait_ready()) count = 1; /* Wait for card to write */
}
}
deselect();
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE drv, /* Physical drive nmuber (0) */
BYTE ctrl, /* Control code */
void DOSFAR *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
BYTE n, csd[16];
DWORD cs;
DRESULT dr = disk_result(drv);
if (dr != RES_OK) return dr;
res = RES_ERROR;
switch (ctrl) {
case CTRL_SYNC : /* Make sure that no pending write process */
if (select()) res = RES_OK;
break;
case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
cs = csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1;
*(DWORD DOSFAR *)buff = DWORDLSHIFT(cs,10);
} else { /* SDC ver 1.XX or MMC */
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
cs = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
*(DWORD DOSFAR *)buff = DWORDLSHIFT(cs,n-9);
}
res = RES_OK;
}
break;
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
*(DWORD DOSFAR *)buff = 128;
res = RES_OK;
break;
default:
res = RES_PARERR;
}
deselect();
return res;
}

106
XTMax/Apps/SDPP/STANDARD.H Normal file
View File

@ -0,0 +1,106 @@
/* standard.h - standard, project wide, declarations */
/* */
/* Copyright (C) 1994 by Robert Armstrong */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT- */
/* ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */
/* Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, visit the website of the Free */
/* Software Foundation, Inc., www.gnu.org. */
/* Datatypes of a known size ... */
typedef unsigned char BYTE; /* 8 bits, unsigned */
typedef unsigned short WORD; /* 16 " " */
#ifndef DEFINEDLONG
#define DEFINEDLONG
typedef unsigned long LONG; /* 32 " " */
#endif
/* The BOOLEAN type and associated constants ... */
typedef unsigned char BOOLEAN;/* any TRUE/FALSE result */
#define FALSE (0) /* logical falsity */
#define TRUE (~FALSE)/* if it's not FALSE, it's ... */
/* This magic declaration modifier will make the symbol defined be */
/* invisible to the linker. For example, "PRIVATE int foo;" or even */
/* "PRIVATE void fred () { ...}". This is used extensively to restrict */
/* the scope of a global declaration to the current module. */
#define PRIVATE static /* this works for most compilers! */
/* And this magic declaration modifier guarantees that the symbol so */
/* defined will be visible to other modules. It is the opposite of */
/* "PRIVATE". In C this is the default condition anyway, so this macro */
/* mostly serves as documentation. */
#define PUBLIC /* this is the way things are anyway */
/* Extract the high and low bytes of a 16 bit value... */
#define HIGH(x) (((x) >> 8) & 0xFF)
#define LOW(x) ((x) & 0xFF)
/* Test a value for odd/even... */
#define ODD(x) (((x) & 1) != 0)
/* These macros provide access to specific 8088 family hardware ins- */
/* tructions. From a performance viewpoint, it is highly desirable that */
/* these macros not be implemented by subroutine calls, but rather that */
/* they expand directly to inline assembler instructions. */
#ifdef __TURBOC__
/* These macros turn the interrupt system on and off. They should */
/* generate the STI (0xFB) and CLI (0xFA) instructions... */
#define INT_ON asm sti
#define INT_OFF asm cli
/* These macros read and write a bytes from an I/O port. They should */
/* generate the 0xE5 (IN AL,DX) or 0xEE (OUT DX,AL) instructions... */
#define INBYTE(p,v) { _DX = (p); asm IN AL,DX; v = _AL; }
#define OUTBYTE(p,v) { _DX = (p); _AL = (v); asm OUT DX,AL; }
/* These macros will set or clear specific bit(s) in an I/O port. */
/* They work by first reading the port, seting or clearing the bit(s), */
/* and then writing the port. Needless to say, they can only be used */
/* on ports that can safely be both read and written! */
#define IOSETBIT(p,m) { _DX = (p); _BL = (m); \
asm IN AL,DX; asm OR AL,BL; asm OUT DX,AL; }
#define IOCLRBIT(p,m) { _DX = (p); _BL = ~(m); \
asm IN AL,DX; asm AND AL,BL; asm OUT DX,AL; }
/* These macros use the DOS INT 21H functions to get and set the */
/* contents of interrupt vectors. They are used to save and restore */
/* the serial port interrupt service routine... */
#define GETVECTOR(v,a) { _AL = (v); _AH = 0x35; asm INT 0x21; \
asm MOV WORD PTR a, BX; asm MOV WORD PTR a+2, ES; }
#define SETVECTOR(v,a) { _AL = (v); _AH = 0x25; asm PUSH DS; \
asm LDS DX, DWORD PTR a; \
asm INT 0x21; asm POP DS; }
#define JMPVECTOR(a) { asm JMP DWORD PTR a; }
/* Compute dst += src with end-around carry, just like the TU58 does... */
#define ADC(dst,src) { asm MOV AX, dst; asm ADD AX, src; \
asm ADC AX, 0; asm MOV dst, AX; }
#endif /* ifdef __TURBOC__ */
/* Debugging output routines... */
void outchr (char);
void outstr (char *);
void outdec (int);
void outhex (unsigned, int);
void outcrlf (void);
void cprintf (char near *, ...);