--- /dev/null
+ TITLE 'compactflash disk handler'\r
+\r
+; CP/M-80 Version 3 -- Modular BIOS\r
+\r
+\r
+ ; Disk drive dispatching tables for linked BIOS\r
+\r
+ public cf0,cf1,cf2,cf3\r
+\r
+ ; Variables containing parameters passed by BDOS\r
+\r
+ extrn @xdph\r
+ extrn @adrv,@rdrv\r
+ extrn @trk,@sect,@cnt\r
+ extrn @dma,@dbnk,@cbnk\r
+\r
+ ; System Control Block variables\r
+\r
+ extrn @ermde ; BDOS error mode\r
+\r
+ ; Utility routines in standard BIOS\r
+\r
+ extrn ?wboot ; warm boot vector\r
+ extrn ?pmsg,pr.inln ; print message @<HL>, print inline message\r
+ extrn pr.crlf ; print <cr><lf>\r
+ extrn phex2\r
+ extrn pr.decl\r
+ extrn ?pderr ; print BIOS disk error header\r
+ extrn ?conin,?cono ; con in and out\r
+ extrn ?const ; get console status\r
+ extrn ?bnksl\r
+\r
+ extrn bnk2phy ;\r
+ extrn add_hla\r
+\r
+\r
+ ; Port Address Equates\r
+\r
+ include config.inc\r
+ include z180reg.inc\r
+\r
+ ; CP/M 3 Disk definition macros\r
+\r
+ maclib cpm3slr.lib\r
+\r
+ ; Z180 macro library instruction definitions (ignored by slr180)\r
+\r
+ include z180.lib\r
+\r
+DEBUG equ false ; not used\r
+MULTIIO equ true ; Multi I/O currently not fully implemented.\r
+\r
+ ; IDE Task File Register Definitions\r
+\r
+;IdeDOR equ IDEBASE+6 ; Digital Output Register\r
+IDEDat equ IDEBASE+0 ; Data Register\r
+IDEErr equ IDEBASE+1 ; Error Register\r
+IDEFeat equ IDEBASE+1 ; Feature Register\r
+IDESCnt equ IDEBASE+2 ; Sector Count\r
+IDESNum equ IDEBASE+3 ; Sector Number\r
+IDECLo equ IDEBASE+4 ; Cylinder Low\r
+IDECHi equ IDEBASE+5 ; Cylinder High\r
+IDESDH equ IDEBASE+6 ; Drive and Head\r
+IDECmd equ IDEBASE+7 ; Command / Status\r
+\r
+ ; IDE Hard disk commands:\r
+\r
+CmdHome equ 10h ; Recalibrate\r
+CmdRd equ 20h ; Read Sector\r
+CmdWr equ 30h ; Write Sector\r
+CmdInit equ 91h ; Initialize Drive Params\r
+CmdId equ 0ECh ; Read ID\r
+CmdSF equ 0EFh ; Set Feature\r
+\r
+ ; Partition Table Structures\r
+\r
+PART_TYPE equ 4\r
+PART_START equ 8\r
+PART_SIZE equ 12\r
+\r
+ ; Partition table id\r
+ ; (see http://www.win.tue.nl/~aeb/partitions/partition_types-1.html)\r
+\r
+PARTID1_FAT16 equ 00EH\r
+PARTID2_FAT16 equ 006H\r
+PARTID_CPM equ 052H\r
+\r
+MAXDISKS equ 4\r
+\r
+; parttabl fields\r
+PTAB_TYPE equ 0 ; 1 byte\r
+PTAB_START equ 1 ; 4 byte (28 bit, max 128 GiB)\r
+PTAB_SIZE equ 5 ; 4 byte (3 needed, 20 bit, max 512 MiB)\r
+PTAB_SPT equ 9 ; 1 byte\r
+PTAB_BSH equ 10 ; 1 byte\r
+\r
+PARTENTRY_SIZE equ 11\r
+\r
+\r
+ ; common control characters\r
+\r
+cr equ 13\r
+lf equ 10\r
+bell equ 7\r
+\r
+;-------------------------------------------------------------------------------\r
+\r
+ ; Macro: wait while device is busy\r
+\r
+WAITNOTBUSY macro\r
+ local wait\r
+wait: in a,(IdeCmd)\r
+ rla\r
+ jr c,wait\r
+ endm\r
+\r
+ ; Macro: wait while device is busy\r
+\r
+WAITREADY macro\r
+ local wait\r
+wait: in a,(IdeCmd)\r
+ xor 01000000b\r
+ and 11000000b\r
+ jr nz,wait\r
+ endm\r
+\r
+ ; Macro: wait for DRQ signal\r
+\r
+WAITDRQ macro\r
+ local wait\r
+wait: in a,(IdeCmd)\r
+ bit 3,a\r
+ jr z,wait\r
+ endm\r
+\r
+;-------------------------------------------------------------------------------\r
+\r
+ dseg\r
+\r
+ ; Extended Disk Parameter Headers (XPDHs)\r
+\r
+ ; dph translate$table, - disk parameter header\r
+ ; disk$parameter$block,\r
+ ; checksum$size, (optional)\r
+ ; alloc$size (optional)\r
+\r
+ dw cf$write\r
+ dw cf$read\r
+ dw cf$login\r
+ dw cf$init0\r
+ db 0,0 ; relative drive zero\r
+cf0: dph 0,dpbsimhd512,0\r
+\r
+ dw cf$write\r
+ dw cf$read\r
+ dw cf$login\r
+ dw cf$init1\r
+ db 1,0 ; relative drive one\r
+cf1: dph 0,dpbsimhd512,0\r
+\r
+ dw cf$write\r
+ dw cf$read\r
+ dw cf$login\r
+ dw cf$init2\r
+ db 2,0 ; relative drive zero\r
+cf2: dph 0,dpbsimhd512,0\r
+\r
+ dw cf$write\r
+ dw cf$read\r
+ dw cf$login\r
+ dw cf$init3\r
+ db 3,0 ; relative drive one\r
+cf3: dph 0,dpbsimhd512,0\r
+\r
+ cseg ; DPB must be resident\r
+\r
+ ; dpb physical$sector$size, - disk parameter block\r
+ ; physical$sectors$per$track,\r
+ ; number$tracks,\r
+ ; block$size,\r
+ ; number$dir$entries,\r
+ ; track$offset,\r
+ ; checksum$vec$size (optional)\r
+\r
+dpbsimhd512:\r
+ dpb 512,8,2048,4096,1024,6,8000h\r
+\r
+;-------------------------------------------------------------------------------\r
+\r
+ dseg ; rest is banked\r
+\r
+; Disk I/O routines for standardized BIOS interface\r
+\r
+; Initialization entry point.\r
+; called for first time initialization.\r
+\r
+cf$init0:\r
+ call pr.inln ;\r
+ db 'cfio: CompactFlash Memory Card driver'cr,lf,0\r
+\r
+ ld hl,parttbl ; Clear partition table\r
+ ld b,PARTENTRY_SIZE*MAXDISKS\r
+ini_clrtbl:\r
+ ld (hl),0\r
+ inc hl\r
+ djnz ini_clrtbl\r
+\r
+ call cf_init ; init ide interface / cf card\r
+ jr nz,pend\r
+\r
+ call ident_read ; identify drive\r
+ jr nz,pend\r
+\r
+ call prnt_info ; print device information\r
+ call ptab_read ; read the partition table\r
+\r
+ ld c,0 ; number of found disks (paritions)\r
+ jr nz,pend\r
+\r
+ ld hl,tmpsecbuf+512-1 ; Point to first byte of partition table\r
+ ld a,(hl) ; Test, if it has a valid MBR\r
+ cp 0AAH ;\r
+ jr nz,pend ;\r
+ dec hl\r
+ ld a,(hl) ;\r
+ cp 055H ;\r
+ jr nz,pend ;\r
+\r
+ ; Search for valid Partitions\r
+\r
+ ld hl,tmpsecbuf+512-2-64+PART_TYPE ; Point to partition type of first first partition table entry\r
+ ld de,parttbl ;\r
+ ld b,4 ; Max # of partition table entries\r
+ploop:\r
+ ld a,(hl) ; Get Partitiontype\r
+ cp PARTID_CPM ; Test for CP/M Partition\r
+ ld a,16 ; Offset to next entry\r
+ jr nz,nextp\r
+ push bc\r
+ ld a,(hl) ; (Re)get Partitiontype\r
+ ld (de),a ; Save paritition type\r
+ inc de\r
+ inc hl ; Point to partition start (lba)\r
+ inc hl\r
+ inc hl\r
+ inc hl\r
+ ld bc,8 ; Copy partition start and size\r
+ ldir\r
+ rept PARTENTRY_SIZE-8-1\r
+ inc de\r
+ endm\r
+ pop bc\r
+ inc c ; One more found\r
+ ld a,c\r
+ cp MAXDISKS\r
+ jr z,pend\r
+ ld a,4\r
+nextp:\r
+ call add_hla\r
+ djnz ploop\r
+pend:\r
+ ;TODO: variable disk format: sectors per track, ...\r
+\r
+ call prnt_ptab ; Print partition table info\r
+ ret\r
+\r
+\r
+cf$init1:\r
+cf$init2:\r
+cf$init3:\r
+ ret ; all initialization done by drive 0\r
+\r
+; Read ID from Hard Disk\r
+\r
+ident_read:\r
+ WAITREADY\r
+ ld a,0E0h ; assume unit 0,\r
+ out (IdeSDH),a ;\r
+ ld a,CmdId\r
+ out (IdeCmd),a ; command: read sector data\r
+ ld hl,tmpsecbuf\r
+ ld bc,IdeDat ; B = 0 (counter), C = I/O address\r
+ WAITDRQ ; wait for DRQ to become active\r
+ inir\r
+ inir ; read 512 data bytes (2 x 256)\r
+ WAITNOTBUSY\r
+ in a,(IdeCmd) ; check final drive status\r
+ and 10001001b ; Busy, DRQ, or Error?\r
+ ret z ; no: everything is ok\r
+ ld a,1 ; return with A=1 on error\r
+ ret\r
+\r
+\r
+; Read partition table\r
+\r
+ptab_read:\r
+ WAITREADY\r
+ ld a,0E0h ; assume unit 0, lba mode\r
+ out (IdeSDH),a ;\r
+ xor a ; sector 0 (lba)\r
+ out (IdeSNum),a ;\r
+ out (IdeCLo),a\r
+ out (IdeCHi),a ;\r
+ inc a ; one sector to read\r
+ out (IdeSCnt),a ; set sector count\r
+\r
+ ld a,CmdRd\r
+ out (IdeCmd),a ; command: read sector data\r
+ ld hl,tmpsecbuf\r
+ ld bc,IdeDat ; B = 0 (counter), C = I/O address\r
+ WAITDRQ ; wait for DRQ to become active\r
+ inir\r
+ inir ; read 512 data bytes (2 x 256)\r
+ WAITNOTBUSY\r
+ in a,(IdeCmd) ; check final drive status\r
+ and 10001001b ; Busy, DRQ, or Error?\r
+ ret z ; no: everything is ok\r
+ ld a,1 ; return with A=1 on error\r
+ ret\r
+\r
+cf_init:\r
+ WAITREADY\r
+ ld a,0E0h ; assume unit 0, lba mode\r
+ out (IdeSDH),a ;\r
+ ld a,1 ; Enable 8-bit data transfer.\r
+ out (IDEFeat),a\r
+ ld a,CmdSF\r
+ out (IdeCmd),a ; command: read sector data\r
+ WAITNOTBUSY\r
+ in a,(IdeCmd) ; check final drive status\r
+ and 10001001b ; Busy, DRQ, or Error?\r
+ ret z ; no: everything is ok\r
+ ld a,1 ; return with A=1 on error\r
+ ret\r
+\r
+\r
+pr_char_nlbl:\r
+ bit 0,b\r
+ jr z,pr_char\r
+ cp ' '\r
+ ret z\r
+ res 0,b\r
+ ; fall thru\r
+pr_char:\r
+ push hl\r
+ push de\r
+ push bc\r
+ ld c,a\r
+ call ?cono\r
+ pop bc\r
+ pop de\r
+ pop hl\r
+ ret\r
+\r
+; Print an id string\r
+; Remove leading and trailing spaces\r
+\r
+pr_id:\r
+ push hl ; Save string address\r
+ ld b,0\r
+ add hl,bc\r
+ dec hl ; Point to last char.\r
+ ld a,' '\r
+prn_el: ; Reduce string len by number of trailing spaces\r
+ dec hl\r
+ cpi\r
+ jr nz,prn_el1 ; No more spaces\r
+ jp po,prn_el2 ; No more characters\r
+ cpd\r
+ dec hl\r
+ jr nz,prn_el1\r
+ jp po,prn_el2\r
+ jr prn_el\r
+prn_el1:\r
+ inc c\r
+prn_el2:\r
+ pop hl ; Restore beginning of string\r
+ ld a,c\r
+ or a ; Test number of remaining chars\r
+ ret z ; Done, if string was spaces only\r
+\r
+ ld b,1 ; Flag, skip spaces\r
+prn_lp:\r
+ inc hl ;Text is low byte high byte format\r
+ ld a,(hl)\r
+ call pr_char_nlbl\r
+ dec c\r
+ ret z\r
+ dec hl\r
+prn_lp1:\r
+ ld a,(hl)\r
+ call pr_char_nlbl\r
+ dec c\r
+ ret z\r
+ inc hl\r
+ inc hl\r
+ jr prn_lp\r
+\r
+; Print divice information\r
+\r
+prnt_info:\r
+ call pr.inln\r
+ db ' Model: ',0\r
+ ld hl,tmpsecbuf + 27*2 ; Model number\r
+ ld c,20*2 ; max character count\r
+ call pr_id ;\r
+ call pr.inln\r
+ db ', S/N: ',0\r
+ ld hl,tmpsecbuf + 10*2 ; Serial number\r
+ ld c, 10*2\r
+ call pr_id\r
+ call pr.inln\r
+ db ', Rev: ',0\r
+ ld hl,tmpsecbuf + 23*2 ; Firmware revision\r
+ ld c, 4*2\r
+ call pr_id\r
+\r
+ call pr.inln\r
+ db cr,lf,' Size: ',0\r
+ ld hl,(tmpsecbuf+60*2) ;Total Sectors Addressable in LBA Mode\r
+ ld de,(tmpsecbuf+61*2) ;\r
+ push hl\r
+ push de\r
+ ld bc,1\r
+ call pr.decl\r
+ call pr.inln\r
+ db ' Sectors (',0\r
+ pop de\r
+ pop hl\r
+ srl d\r
+ rr e\r
+ rr h\r
+ rr l\r
+ ld bc,1\r
+ call pr.decl\r
+ call pr.inln\r
+ db ' KiB)',cr,lf,0\r
+ ret\r
+\r
+; Print partition table info\r
+\r
+prnt_ptab:\r
+ ld ix,parttbl\r
+ ld c,0\r
+prp_lp:\r
+ ld a,c\r
+ cp 4\r
+ ret z\r
+ ld a,(ix+PTAB_TYPE)\r
+ or a\r
+ ret z\r
+\r
+ push bc\r
+ call pr.inln\r
+ db ' ',0\r
+ ld a,(@adrv)\r
+ add a,c\r
+ add a,'A'\r
+ call pr_char\r
+ call pr.inln\r
+ db ': CP/M partition at: ',0\r
+ ld l,(ix+PTAB_START+0)\r
+ ld h,(ix+PTAB_START+1)\r
+ ld e,(ix+PTAB_START+2)\r
+ ld d,(ix+PTAB_START+3)\r
+ ld bc,1\r
+ call pr.decl\r
+ call pr.inln\r
+ db ', size: ',0\r
+ ld l,(ix+PTAB_SIZE+0)\r
+ ld h,(ix+PTAB_SIZE+1)\r
+ ld e,(ix+PTAB_SIZE+2)\r
+ ld d,(ix+PTAB_SIZE+3)\r
+ srl d\r
+ rr e\r
+ rr h\r
+ rr l\r
+ ld bc,1\r
+ call pr.decl\r
+ call pr.inln\r
+ db 'KiB',cr,lf,0\r
+ ld bc,PARTENTRY_SIZE\r
+ add ix,bc\r
+ pop bc\r
+ inc c\r
+ jr prp_lp\r
+\r
+;-------------------------------------------------------------------------------\r
+\r
+; This entry is called when a logical drive is about to\r
+; be logged into for the purpose of density determination.\r
+; It may adjust the parameters contained in the disk\r
+; parameter header pointed at by <DE>\r
+;\r
+; absolute drive number in @adrv (8 bits) +0\r
+; relative drive number in @rdrv (8 bits) +1\r
+\r
+cf$login:\r
+ xor a\r
+ ld (residual),a ; just in case\r
+\r
+ ld hl,parttbl\r
+ ld a,(@rdrv)\r
+ ld e,a\r
+ ld d,PARTENTRY_SIZE\r
+ mlt de\r
+ add hl,de\r
+ ld a,(hl)\r
+ or a\r
+ ret nz\r
+ ld hl,0\r
+ ld (@xdph),hl\r
+ ret ;\r
+\r
+; disk READ and WRITE entry points.\r
+; these entries are called with the following arguments:\r
+;\r
+; absolute drive number in @adrv (8 bits) +0\r
+; relative drive number in @rdrv (8 bits) +1\r
+; disk track address in @trk (16 bits) +2\r
+; disk sector address in @sect(16 bits) +4\r
+; multi sector count in @cnt (8 bits) +6\r
+; disk transfer address in @dma (16 bits) +7\r
+; disk transfer bank in @dbnk (8 bits) +9\r
+; pointer to XDPH in <DE>\r
+;\r
+; they transfer the appropriate data, perform retries\r
+; if necessary, then return an error code in <A>\r
+\r
+cf$read:\r
+ ld de,read$msg ; point at " Read "\r
+ ld bc,M_DIM1*256 + CmdRd ; Transfermode: i/o to memory++\r
+ jr rw$common\r
+cf$write:\r
+ ld de,write$msg ; point at " Write "\r
+ ld bc,0*256 + CmdWr ; Transfermode: memory++ to i/o\r
+rw$common:\r
+\r
+ if MULTIIO\r
+ ld hl,residual ; remainng sectors from last multi io?\r
+ ld a,(hl)\r
+ sub a,1\r
+ jr c,rwc_new_sectors\r
+\r
+ ld (hl),a\r
+ xor a\r
+ ret\r
+ endif\r
+\r
+rwc_new_sectors:\r
+ ld (operation$name),de ; save message for errors\r
+ in0 a,(dcntl)\r
+ and a,~(M_DMS1+M_DIM1+M_DIM0)\r
+ or b\r
+ out0 (dcntl),a\r
+\r
+ ld b,1 ; assume 1 sector to transfer\r
+ if MULTIIO\r
+ ld a,(@cnt)\r
+ or a\r
+ jr z,rwc_doit\r
+\r
+ ld b,a ; number of sectors to transfer\r
+ dec a ; save remaining\r
+ ld (hl),a\r
+ xor a ; reset multi sector count\r
+ ld (@cnt),a\r
+rwc_doit:\r
+ endif\r
+\r
+ ld iy,parttbl\r
+ ld a,(@rdrv)\r
+ ld e,a\r
+ ld d,PARTENTRY_SIZE\r
+ mlt de\r
+ add iy,de\r
+\r
+retry:\r
+ ld a,b\r
+ out (IdeSCnt),a ; set sector count\r
+\r
+; compute logical block number (lba) --> cf-controller\r
+\r
+ ; TODO: sectors per track from dpb\r
+ ; lba = track * 8 + sector\r
+\r
+ xor a\r
+ ld hl,(@trk)\r
+ add hl,hl\r
+ adc a,a ; *2\r
+ add hl,hl\r
+ adc a,a ; *4\r
+ add hl,hl\r
+ adc a,a ; *8\r
+ ld de,(@sect)\r
+ add hl,de\r
+ adc a,0\r
+\r
+ push hl ; check, if block# fits in partition\r
+ ld e,(iy+PTAB_SIZE+0)\r
+ ld d,(iy+PTAB_SIZE+1)\r
+ sbc hl,de\r
+ ld l,a\r
+ sbc a,(iy+PTAB_SIZE+2)\r
+ ld a,l\r
+ pop hl\r
+ jr c,lba_ok\r
+ ld a,1 ; block# >= partition size, return error\r
+ ret\r
+\r
+lba_ok:\r
+ WAITREADY\r
+ ld e,a ; add partition start\r
+ ld a,(iy+PTAB_START+0)\r
+ add a,l\r
+ out (IdeSNum),a\r
+ ld a,(iy+PTAB_START+1)\r
+ adc a,h\r
+ out (IdeCLo),a\r
+ ld a,(iy+PTAB_START+2)\r
+ adc a,e\r
+ out (IdeCHi),a\r
+ ld a,(iy+PTAB_START+3)\r
+ adc a,0\r
+ and 00FH\r
+ or 0E0H\r
+ out (IdeSDH),a\r
+\r
+ ld hl,(@dma)\r
+ ld a,(@dbnk)\r
+\r
+; compute pysical transfer address --> DMA\r
+\r
+ call bnk2phy ; phys. linear address\r
+ out0 mar1l,l\r
+ out0 mar1h,h\r
+ out0 mar1b,a\r
+ ld a,IdeDat\r
+ out0 iar1l,a\r
+ xor a\r
+ out0 iar1h,a\r
+ out0 iar1b,a\r
+ out0 bcr1l,a\r
+ ld a,c\r
+ out (IDECmd),a\r
+ push bc\r
+nxt_sec:\r
+ ld a,2\r
+ out0 bcr1h,a\r
+ WAITDRQ\r
+ ld a,M_DE1+M_NDWE0\r
+ out0 (dstat),a\r
+wait_dma:\r
+ in0 a,(dstat)\r
+ bit DE1,A\r
+ jr nz,wait_dma\r
+\r
+ WAITNOTBUSY\r
+ in a,(IdeCmd) ; check final drive status\r
+ bit 0,a ; any error?\r
+ jr nz,err_out\r
+ djnz nxt_sec\r
+err_out:\r
+ pop bc\r
+ ld e,a\r
+ and 10001001b ; Busy, DRQ, or Error?\r
+ ret z ; Return to BDOS if no error\r
+\r
+; suppress error message if BDOS is returning errors to application...\r
+\r
+ ld a,(@ermde)\r
+ cp 0ffh\r
+ jr z,hard$error\r
+\r
+ ; Had permanent error, print message like:\r
+ ; BIOS Err on d: T-nn, S-mm, <operation> <type>, Retry ?\r
+\r
+ call ?pderr ; print message header\r
+\r
+ ld hl,(operation$name)\r
+ call ?pmsg ; last function (read or write)\r
+\r
+ ld hl,msg$drq\r
+ bit 3,e\r
+ call nz,?pmsg\r
+\r
+ bit 0,e\r
+ jr z,prompt\r
+\r
+ in a,(IDEErr)\r
+ ld hl,error$table ; point at table of message addresses\r
+errm1:\r
+ ld e,(hl)\r
+ inc hl\r
+ ld d,(hl)\r
+ inc hl ; get next message address\r
+ add a,a\r
+ push af ; shift left and push residual bits with status\r
+ ex de,hl\r
+ call c,?pmsg\r
+ ex de,hl ; print message, saving table pointer\r
+ pop af\r
+ jr nz,errm1 ; if any more bits left, continue\r
+\r
+prompt:\r
+ call pr.inln\r
+ db ' Retry (Y/N) ? ',0\r
+\r
+ call u$conin$echo ; get operator response\r
+ cp 'Y'\r
+ jp z,retry ; Yes, then retry\r
+\r
+hard$error:\r
+ ; otherwise,\r
+ xor a\r
+ ld (residual),a\r
+\r
+ ld a,1 ; return hard error to BDOS\r
+ ret\r
+\r
+cancel: ; here to abort job\r
+ jp ?wboot ; leap directly to warmstart vector\r
+\r
+\r
+; get console input, echo it, and shift to upper case\r
+\r
+u$conin$echo:\r
+ push bc\r
+u$c0:\r
+ call ?const\r
+ or a\r
+ jr z,u$c1 ; see if any char already struck\r
+ call ?conin\r
+ jr u$c0 ; yes, eat it and try again\r
+u$c1:\r
+ call ?conin\r
+ push af\r
+ ld c,a\r
+ cp ' '-1\r
+ call nc,?cono\r
+ pop af\r
+ pop bc\r
+ cp 'a'\r
+ ret c\r
+ sub 'a'-'A' ; make upper case\r
+ ret\r
+\r
+ ; error message components\r
+\r
+operation$name:\r
+ dw read$msg\r
+read$msg:\r
+ db ', Read, ',0\r
+write$msg:\r
+ db ', Write, ',0\r
+msg$drq:\r
+ db 'DRQ, ',0\r
+error$table:\r
+ dw b7$msg\r
+ dw b6$msg\r
+ dw b5$msg\r
+ dw b4$msg\r
+ dw b3$msg\r
+ dw b2$msg\r
+ dw b1$msg\r
+ dw b0$msg\r
+\r
+b7$msg: db ' Bad Block detected,',0\r
+b6$msg: db ' Uncorrectable Data Error,',0\r
+b5$msg: db ' Media Changed,',0\r
+b4$msg: db ' Sector ID Not Found,',0\r
+b3$msg: db ' Media Change Requst,',0\r
+b2$msg: db ' Aborted Command,',0\r
+b1$msg: db ' Track 0 Not Found,',0\r
+b0$msg: db ' AM Not Found (or general error),',0\r
+\r
+\r
+residual:\r
+ db 0\r
+\r
+parttbl:\r
+ ds PARTENTRY_SIZE*MAXDISKS\r
+\r
+tmpsecbuf: ;temporary sector buffer\r
+ ds 512\r
+\r
+ end\r