📄 dd.c
字号:
/*****************************************************************************
- dd_write() should do the Right Thing when write
ends in the middle of a block
>>>- testing
Turbo C 2.0, Turbo C 3.0, DJGPP
486 system, Pentium system
plain DOS, DOS w/ SMARTDRV, Windows DOS box
1.44 meg disks, 1.68 meg disks
random things: initial offset, whole middle blocks, terminal offset
>>>- faster?
later:
- need zero-fill if reading beyond end of disk?
- support hard drive (entire drive and partitions)
*****************************************************************************/
#include <stdlib.h> /* atol(), malloc(), free() */
#include <string.h> /* NULL, movedata(), memset(), strlen(), memicmp() */
#include <ctype.h> /* isalpha(), toupper(), tolower() */
#include <stdio.h> /* printf() */
#include <fcntl.h> /* O_nnn */
#include <bios.h> /* _DISK_..., biosdisk() */
#include <dos.h> /* union REGS, int86(), peek(), poke(), pokeb() */
#include <io.h> /* open(), lseek(), tell(), read(), write(), close() */
#if defined(__DJGPP__)
#include <unistd.h> /* all of the io.h functions except tell() */
#include <sys/farptr.h> /* _far...() */
#include <dpmi.h> /* __dpmi... */
#include <go32.h> /* _dos_ds */
/* the nice thing about standards is... */
#define to_linear(S,O) (((unsigned long)(S) << 4) + O)
#define peek(S,O) _farpeekw(_dos_ds, to_linear(S,O))
#define poke(S,O,V) _farpokew(_dos_ds, to_linear(S,O), V)
#define pokeb(S,O,V) _farpokeb(_dos_ds, to_linear(S,O), V)
#define my_movedata(SS,SO,DS,DO,C) \
movedata(_dos_ds, to_linear(SS,SO), \
_dos_ds, to_linear(DS,DO), C)
#define dos_int(N,R) __dpmi_int(N,R)
typedef __dpmi_regs regs_t;
#elif defined(__TURBOC__)
#define my_movedata(SS,SO,DS,DO,C) \
movedata(SS,SO,DS,DO,C)
#define dos_int(N,R) int86(N,R,R)
typedef union REGS regs_t;
#else
#error Not DJGPP, not Turbo C. Sorry.
#endif
#if 0
#define DEBUG(X) X
#else
#define DEBUG(X)
#endif
#define BUFLEN 8192
#define false 0
#define true 1
typedef unsigned char bool;
typedef struct
{
unsigned is_device : 1;
unsigned needs_fill : 1;
unsigned needs_flush : 1;
unsigned long size;
/* read/write position */
unsigned short curr_blk, curr_off;
/* block buffer */
unsigned short buf_size;/* max number of bytes in buffer */
unsigned char *buf; /* base address of buffer */
/* used if input/output is a regular file */
int handle;
/* use if input/output is a block device */
unsigned char int13_dev;
unsigned short cyls, sects, heads, bps;
} file_t;
static const char *_usage =
"\n"
"dd for DOS, version 0.3 http://www.execpc.com/~geezer/os\n"
"Performs low-level byte-wise copying between files and floppies.\n"
"Usage:\n"
" dd count=... skip=... seek=... if=... of=... probe\n"
"count=number of bytes to copy (default: size of input)\n"
"skip =number of input bytes to skip (default: 0)\n"
"seek =number of output bytes to skip (default: 0)\n"
"if =input file or drive\n"
"of =output file or drive\n"
" Examples:\n"
" dd count=512 if=boot.bin of=a: (install bootloader)\n"
" dd skip=512 if=boot.bin of=a:\\loader.bin\n"
" dd skip=18K count=18K if=a: of=d:t1 (copy 2nd track of 1.44M disk)\n"
" dd count=5k if=a: of=c:save (backup boot sector and 1st FAT)\n"
" dd skip=100 count=100 if=foo of=bar (pick out second 100 bytes of file)\n"
" dd if=image of=b: (RAWRITE)\n"
" dd if=a: of=c:\\tmp\\image.bin (RAWREAD, DSKIMG)\n"
" dd if=a: of=b: (DISKCOPY)\n"
"Use 'probe' for unusual floppy sizes such as 1.68 meg\n";
static bool _probe_floppy, _changed_fpt;
static unsigned short _old_fpt_seg, _old_fpt_off;
/*////////////////////////////////////////////////////////////////////////////
PROBING DISK GEOMETRY
////////////////////////////////////////////////////////////////////////////*/
/*****************************************************************************
*****************************************************************************/
static void new_fpt(void)
{
unsigned short new_fpt_seg;
regs_t regs;
if(_changed_fpt)
return;
/* save old FPT */
_old_fpt_off = peek(0, 0x1E * 4 + 0);
_old_fpt_seg = peek(0, 0x1E * 4 + 2);
/* alloc 1 paragraph (16 bytes) DOS memory */
regs.h.ah = 0x48;
regs.x.bx = 1;
dos_int(0x21, ®s);
new_fpt_seg = regs.x.ax;
/* copy old FPT to new */
my_movedata(_old_fpt_seg, _old_fpt_off,
new_fpt_seg, 0,
11);
/* set sectors-per-track to inflated value */
pokeb(new_fpt_seg, 4, 96);
/* install new FPT */
poke(0, 0x1E * 4 + 0, 0);
poke(0, 0x1E * 4 + 2, new_fpt_seg);
_changed_fpt = true;
}
/*****************************************************************************
*****************************************************************************/
static void old_fpt(void)
{
if(!_changed_fpt)
return;
poke(0, 0x1E * 4 + 0, _old_fpt_off);
poke(0, 0x1E * 4 + 2, _old_fpt_seg);
}
/*****************************************************************************
*****************************************************************************/
static int dd_get_geom(file_t *file)
{
unsigned short temp;
union REGS regs;
/* first do INT 13h AH=08h to get number of drives */
regs.h.ah = 0x08;
if(file->int13_dev >= 0x80)
regs.h.dl = 0x80;
else
regs.h.dl = 0;
int86(0x13, ®s, ®s);
if(regs.x.cflag)
{
printf("error getting drive count in dd_get_geom\n");
return -1;
}
if((file->int13_dev & 0x7F) >= regs.h.dl)
{
printf("error: no such drive in dd_get_geom\n");
return -1;
}
temp = regs.h.cl;
temp <<= 2;
temp |= regs.h.ch;
file->bps = 512;
file->cyls = temp + 1;
file->heads = regs.h.dh + 1;
/* hard disk: use the sectors-per-track returned by INT 13h AH=08h */
if(file->int13_dev >= 0x80)
file->sects = regs.h.cl & 0x3F;
/* floppy disk, no probe: use sectors-per-track returned by INT 13h AH=08h */
else if(!_probe_floppy)
file->sects = regs.h.cl & 0x3F;
/* floppy disk, probed geometry. Use this for non-standard floppy sizes
like 1.68 meg (21 sectors per track), since INT 13h AH=08h is unreliable
in this case. */
else
{
unsigned short sects, delta, old_err;
/* NOTE: 'try' is a C++ reserved word */
unsigned char try, buffer[512];
/* install new floppy parameter table */
new_fpt();
printf("probing floppy geometry; please wait...\n");
AGAIN:
delta = sects = 16;
do
{
delta >>= 1;
DEBUG(printf("sects=%d, delta=%d\n", sects, delta);)
old_err = 0;
/* The floppy drive is easily confused by this probing.
Try each read up to 3 times before giving up. */
for(try = 3; try != 0; try--)
{
/* *** WARNING, WARNING -- biosdisk() doesn't return what
Turbo C online help says it returns. It returns the INT 13h
error value only, not the number of sectors moved. */
temp = biosdisk(_DISK_READ, file->int13_dev,
0/*head*/, 0/*track*/,
sects, 1/*nsects*/, buffer);
if(temp == 6)
{
DEBUG(printf("disk change, "
"restarting...\n");)
goto AGAIN;
}
else if(temp == 0)
{
sects += delta;
break;
}
DEBUG(printf("\terror 0x%X\n", temp);)
(void)biosdisk(_DISK_RESET, file->int13_dev,
0, 0, 0, 0, 0);
/* don't decrement sects more than once */
if(old_err == 0)
sects -= delta;
old_err = temp;
}
} while(delta != 0);
/* no, do this when the program exits
old_fpt(); */
(void)biosdisk(_DISK_RESET, file->int13_dev, 0, 0, 0, 0, 0);
if(temp != 0)
sects--;
file->sects = sects;
}
printf("CHS=%u:%u:%u\n", file->cyls, file->heads, file->sects);
return 0;
}
/*****************************************************************************
*****************************************************************************/
static int dd_rwdisk(file_t *file, int cmd, unsigned long lba,
unsigned short nsects, unsigned char *buf)
{
unsigned short cyl, head, sect;
unsigned char try;
int err;
if(nsects == 0)
return 0;
if(lba * file->bps >= file->size)
{
printf("error: access beyond end of disk in dd_rwdisk\n");
return -1;
}
/* convert to CHS */
sect = lba % file->sects + 1;
lba /= file->sects;
head = lba % file->heads;
cyl = lba / file->heads;
for(try = 3; try != 0; try--)
{
DEBUG(printf("dd_rwdisk: CHS=%u:%u:%u\n", cyl, head, sect);)
err = biosdisk(cmd, file->int13_dev, head, cyl, sect,
nsects, buf);
if(err == 0 || err == 0x11)
return 0;
DEBUG(printf("disk error 0x%X in dd_rwdisk\n", err);)
}
printf("disk error 0x%X in dd_rwdisk\n", err);
return -1;
}
/*////////////////////////////////////////////////////////////////////////////
INPUT
////////////////////////////////////////////////////////////////////////////*/
/*****************************************************************************
*****************************************************************************/
static int dd_fill(file_t *file)
{
int temp;
if(!file->needs_fill)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -