; LAST UPDATED ON: 02 DECEMBER 83 -- Ver 2.2i (Release is N) ; REASON FOR UPDATE: Fixed bug with 4 M-DRIVE/Hs llo ; 10 OCT 83, Ver 2.2M Changed DISK3 for new firmware. Change M-DRIVE/H ; to be consistent with other CompuPro OS's llo ; 10 JUN 83, Ver 2.2i Add DISK3 driver as per initial specs. aep ; 25 MAY 83, Ver 2.2h Add format structure to sector xlate table. Move I/O ; initialization to the boot loader to achieve invariance ; against CBIOS size variations. aep ; 03 MAY 83, Ver 2.2g Continued debug and streamline of DISK2 driver with ; addition for multiple drives. BIOS split to permit ; boot from floppy of hard disk drivers in addition to ; full I/O compliment (loaded by file in "STARTUP.SUB" ; sequence.) aep ; 24 MAR 83, Ver 2.2f Update M-Drive/H with message, restructure hard disk ; driver for faster algorythm including relocation of up ; to one track's worth of "bad media" sectors. aep ; 02 JAN 83, Ver 2.2e General debug of M-DRIVE/H. aep ; 16 NOV 82, Ver 2.2e Add M-DRIVE/H driver. aep ; 10 OCT 82, Ver 2.2d Add DISK2 controller software to match existing ; but without the crazy sector relocation algorythm. aep ; 15 JUL 82, Ver 2.2L Modify to accomodate use of boot switch again, ; eliminate bit banger. aep ; 16 MAY 82, Ver 2.2LXmMN Restructure code (massive overhaul) to permit ; interrupt handler overlay. aep ; 25 MAR 82, Ver 2.2LXM Add mini-drives, M-BOOT, fix SPECIFY, ; full I/O byte implimentation. aep ; ; PROGRAM NAME: HMX1BIOS.ASM -- Customized BIOS for CP/M-80 version 2.2, ; developed for use with CompuPro Systems Components. ; ; ========================== Copyright 1983, CompuPro Corporation. ; || || ; || HMX1BIOS.ASM || ; || || ; ========================== ; ; This product is a copyright program product of CompuPro and is ; supplied for use with the CompuPro Computer Systems. ; ; CONSTANTS: VERS EQU 22 ;CP/M version number CBIOSV EQU 'N' ;CBIOS revision level (2.2x) (CompuPro level) ; ; LIBRARY CONSTANTS: MACLIB COMPUPRO ;Disk and Serial/Parallel interface constants MACLIB ASCII ;Mnemonics for common ASCII, other special characters MACLIB ACTIVE ;Flags directing construction for the various ;CompuPro products to "customize" the BIOS MACLIB CPMDISK ;CP/M disk defaults, CBIOS offsets, BDOS functions MACLIB BOOTSCPM ;CP/M cold/warm boot routines for each of the ;possible controller types ; ; Footnote #1. PAGE ; PROGRAM: ; ; The next statement produces a harmless error message if MAC is used instead. ASEG ;Used Digital Research RMAC assembler and ORG BIOS ;LINK linker to assemble this code ; JMP CBOOT ;+00h Cold boot LOADVEC EQU BIOS+1 ;Base vector address of interrupt or other code loaded ;by CP/M to extend the CBIOS beyond what the boot track ;can hold -- value stored here at end of cold boot. JMP WBOOT ;+03h Warm boot CIS: JMP CONST ;+06h Console status (input) CI: JMP CONIN ;+09h Console input CO: JMP CONOUT ;+0Ch Console output JMP LISTOUT ;+0Fh List output JMP PUNCH ;+12h Punch output JMP READER ;+15h Reader input JMP HOME ;+18h Set track to zero JMP SELDSK ;+1Bh Select disk unit JMP SETTRK ;+1Eh Set track JMP SETSEC ;+21h Set sector JMP SETDMA ;+24h Set Disk Memory Address JMP READ ;+27h Read from disk JMP WRITE ;+2Ah Write onto disk JMP LISTST ;+2Dh List status (output) JMP SECTRN ;+30h Translate sector number JMP SETNUM ;+33h Set number of sectors to R/W (dangerous) JMP SETXAD ;+36h Set extended address (unblocked data only) ; ; Footnote #2. ; ;**************************************************************** ;* DISK LOGICAL TO PHYSICAL SECTOR TRANSLATION TABLES * ;**************************************************************** if FLOPPY8 ; GPL1, GPL2, Physical sectors/track X8TABLE:DW XLT8S0 ;Single 128 DB 07h, 1Bh, 26, 0 ;Format information "header" DW XLT8D1 ;Double 256 DB 0Eh, 36h, 26, 0 DW XLT8D2 ;Double 512 DB 1Bh, 54h, 15, 0 DW XLT8D3 ;Double 1024 DB 35h, 74h, 08, 0 ;--------------------------------------- XLT8S0: DB 0,6,12,18,24,4,10,16,22,2,8,14,20 DB 1,7,13,19,25,5,11,17,23,3,9,15,21 ;--------------------------------------- XLT8D1: DB 0, 1,18,19,36,37, 2, 3,20,21,38,39 DB 4, 5,22,23,40,41, 6, 7,24,25,42,43 DB 8, 9,26,27,44,45,10,11,28,29,46,47 DB 12,13,30,31,48,49,14,15,32,33,50,51 DB 16,17,34,35 ;--------------------------------------- XLT8D2: DB 0, 1, 2, 3,16,17,18,19 DB 32,33,34,35,48,49,50,51 DB 4, 5, 6, 7,20,21,22,23 DB 36,37,38,39,52,53,54,55 DB 8, 9,10,11,24,25,26,27 DB 40,41,42,43,56,57,58,59 DB 12,13,14,15,28,29,30,31 DB 44,45,46,47 ;--------------------------------------- XLT8D3: DB 0, 1, 2, 3, 4, 5, 6, 7 DB 24,25,26,27,28,29,30,31 DB 48,49,50,51,52,53,54,55 DB 8, 9,10,11,12,13,14,15 DB 32,33,34,35,36,37,38,39 DB 56,57,58,59,60,61,62,63 DB 16,17,18,19,20,21,22,23 DB 40,41,42,43,44,45,46,47 endif ;======================================= if FLOPPY5 ; GPL1, GPL2, Physical sectors/track X5TABLE:DW XLT5S0 ;Single 128 DB 08h, 10h, 16, 0 ;Format information ; DB 07h, 09h, 18, 0 ;Optional replacement (tight) DW XLT5D1 ;Double 256 DB 10h, 19h, 16, 0 ; DB 07h, 09h, 18, 0 ;Optional replacement (tight) DW XLT5D2 ;Double 512 DB 2Ah, 50h, 08, 0 DW XLT5D3 ;Double 1024 ; DB 2Ah, 50h, 04, 0 ;Optional replacement (loose) DB 20h, 32h, 05, 0 ;--------------------------------------- XLT5S0: DB 0, 5,10,15, 4, 9,14, 3 ; 0, 5,10,15, 2, 7,12,17 DB 8,13, 2, 7,12, 1, 6,11 ; 4, 9,14, 1, 6,11,16, 3 DB 00,00 ; 8,13 ;--------------------------------------- XLT5D1: DB 0, 1,10,11,20,21,30,31 ; 0, 1,10,11,20,21,30,31 DB 8, 9,18,19,28,29 ; 4, 5,14,15,24,25,34,35 DB 6, 7,16,17,26,27 ; 8, 9,18,19,28,29 DB 4, 5,14,15,24,25 ; 2, 3,12,13,22,23,32,33 DB 2, 3,12,13,22,23,00,00,00,00 ; 6, 7,16,17,26,27 ;--------------------------------------- XLT5D2: DB 0, 1, 2, 3,16,17,18,19 DB 12,13,14,15,28,29,30,31 DB 8, 9,10,11,24,25,26,27 DB 4, 5, 6, 7,20,21,22,23 DB 00,00,00,00,00,00,00,00 ;Additional space for other allocations ;--------------------------------------- XLT5D3: DB 0, 1, 2, 3, 4, 5, 6, 7 DB 16,17,18,19,20,21,22,23 DB 32,33,34,35,36,37,38,39 DB 8, 9,10,11,12,13,14,15 DB 24,25,26,27,28,29,30,31 endif PAGE ;**************************************************************** ;* Macro for generating the Disk Parameter Blocks. * ;**************************************************************** ; ; Control Blocks (DPB's in CP/M-eze) for disk drives: ; ; MAKEDPB Name, Type, Heads, Sectors/Track, Records(128 bytes)/sector, ; Reserved cylinders, Ending cylinder number (starting at 1), ; Allocation block size (in bytes from 1024 to 16*1024) ; Directory entries, # of directory entries to be checked ; (and optional),0 will force 16K extents for CP/M 1.4 compatibility ; MAKEDPB MACRO ?NM,TY,HDS,SPT,SIR,?F,?L,BLS,NDIR,NCKS,K16 BSF SET 3 ;;Init block shift factor EXTMSK SET 0 ;;Init extent mask ACCU SET BLS/1024 REPT 6 ;;6 bit shift max if ACCU = 1 EXITM endif BSF SET BSF+1 ;;Bump block shift factor EXTMSK SET 1+(EXTMSK*2);;Shift in a bit from right ACCU SET ACCU/2 ;;Shift 1 bit right until in zero position ENDM SIB SET BLS/128 ;;Sectors in block DSMC SET ((((?L*HDS)-(?F))*SPT*SIR)+SIB-1)/SIB ;;Maximum data blocks if DSMC > 256 EXTMSK SET EXTMSK/2 ;;Shift out one for double byte allocation endif if not NUL K16 ;;optional [0] in last position EXTMSK SET K16 ;;forces 16K extent mask endif DIRBLK SET 8000h ;;Init allocation block for directory ADIR SET BLS/32 ;;32 bytes/dir entry ACCU SET NDIR ;;Set accumulator to number of dir entries REPT 15 ;;16 Bits of directory alloc max ACCU SET ACCU-(ADIR) ;;32 bytes per directory entry if ACCU <= 0 ;;Done when all accounted for EXITM endif ;;Subtract # of entries in one block DIRBLK SET (DIRBLK/2)+8000h;;Shift in one bit from the left ENDM if (TY > 40h) and (TY < 80h) DIRBLK SET 0FFFFh ;;Reserve all dir blocks for hard disks endif ;; ;;---- Disk Parameter Block Generated ---- ;; ;; Disk type definition blocks for each particular mode. ;; The produced format of these areas are as follows: ;; ;; 8 bit = disk type code (not standard CP/M) ;; 16 bit = Sectors per track (real start of DPB) ;; 8 bit = Block shift factor (block size in 128 byte segments) ;; 8 bit = Block Shift mask ;; 8 bit = Extent mask (a somewhat magical number best left alone) ;; 16 bit = Disk size in blocks (less 1 -- for expansion purposes) ;; 16 bit = Directory size. (less 1 in case size = 64K) ;; 16 bit = Allocation for directory. (vector to bitmap for this drive) ;; 16 bit = Check size. (# of 128 byte directory segments for checksums) ;; 16 bit = Offset to first track. (# of reserved cylinders to this drive) DB TY ;;Disk type identifier DPB&?NM DW SPT * SIR ;;Sectors/track DB BSF, SIB-1, EXTMSK ;;Block size factor, records/blk, mask DW DSMC-1, NDIR-1 ;;Disk size (blocks), Directory entries DB high DIRBLK, low DIRBLK ;;Directory allocation reserved blocks DW (NCKS+3)/4, ?F ;;Checksum vector size, reserved tracks ?NM&CSVZ EQU (NCKS+3)/4 ;;Checksum vector size ?NM&ALVZ EQU (DSMC+7)/8 ;;Max allocation vector size (bytes = blocks / 8 bits) ENDM ; DPB8TBL: ;8 inch floppy disk DPB table MAKEDPB F8S0, 00h, 1,26,1, 2,77, 1024, 64, 64 MAKEDPB F8S1, 01h, 2,26,1, 2*2,77, 1024, 128,128 MAKEDPB F8D2, 02h, 1,26,2, 2,77, 2048, 128,128, 0 ;16K extents MAKEDPB F8D3, 03h, 2,26,2, 2*2,77, 2048, 256,256 MAKEDPB F8D4, 04h, 1,15,4, 2,77, 2048, 128,128 MAKEDPB F8D5, 05h, 2,15,4, 2*2,77, 2048, 256,256 MAKEDPB F8D6, 06h, 1, 8,8, 2,77, 2048, 128,128 MAKEDPB F8D7, 07h, 2, 8,8, 2*2,77, 2048, 256,256 FD8CSVZ EQU F8D7CSVZ ;Max checksum vector size (4 dir entries / record) FD8ALVZ EQU F8D7ALVZ ;Max allocation vector size (bytes = blocks / 8 bits) DPBFD8 EQU DPBF8D7 ;DPB with largest size alloc, checksum vectors ; if FLOPPY5 DPB5TBL: ;5 1/4 inch floppy disk DPB table ; TYPE base = 20h, then 77 tracks ; TYPE base = 28h, then 40 tracks ; TYPE base = 30h, then 80 tracks MAKEDPB F5S0, BIAS5+0, 1,16,1, 2,TRK5, 1024, 64, 64 MAKEDPB F5S1, BIAS5+1, 2,16,1, 2*2,TRK5, 1024, 128,128 MAKEDPB F5D2, BIAS5+2, 1,16,2, 2,TRK5, 2048, 128,128, 0 ;16K extents MAKEDPB F5D3, BIAS5+3, 2,16,2, 2*2,TRK5, 2048, 256,256 MAKEDPB F5D4, BIAS5+4, 1, 8,4, 2,TRK5, 2048, 128,128 MAKEDPB F5D5, BIAS5+5, 2, 8,4, 2*2,TRK5, 2048, 256,256 MAKEDPB F5D6, BIAS5+6, 1, 5,8, 2,TRK5, 2048, 128,128 MAKEDPB F5D7, BIAS5+7, 2, 5,8, 2*2,TRK5, 2048, 256,256 FD5CSVZ EQU F5D7CSVZ ;Max checksum vector size (4 dir entries / record) FD5ALVZ EQU F5D7ALVZ ;Max allocation vector size (bytes = blocks / 8 bits) DPBFD5 EQU DPBF5D7 ;DPB with largest size alloc, checksum vectors endif ; if DISK2 ;Disk 2 hard disk controller active. if D2M10 ;Memorex (Fujitsu) 10 Mbyte drive MAKEDPB D2N1, 43h, 4,11,8, 4*1,123, 4096, 1024,0 ;First 5M MAKEDPB D2N2, 43h, 4,11,8, 4*123,243, 4096, 1024,0 ;Second 5M+ endif if D2M20 ;Memorex (Fujitsu) 20 Mbyte drive MAKEDPB D2N1, 43h, 8,11,8, 8*1, 94, 4096, 1024,0 ;First 8M MAKEDPB D2N2, 43h, 8,11,8, 8*94,187, 4096, 1024,0 ;Second 8M MAKEDPB D2N3, 43h, 8,11,8, 8*187,243, 4096, 1024,0 ;Third 4M+ endif if D2F20B ;Fujitsu "BE" 20 Mbyte drive MAKEDPB D2N1, 43h, 4,22,8, 4*1, 94, 4096, 1024,0 ;First 8M MAKEDPB D2N2, 43h, 4,22,8, 4*94,187, 4096, 1024,0 ;Second 8M MAKEDPB D2N3, 43h, 4,22,8, 4*187,243, 4096, 1024,0 ;Third 4M+ endif if D2M26 ;Shugart 26 Mbyte drive MAKEDPB D2N1, 43h, 8,16,8, 8*1, 57, 4096, 1024,0 ;First 8M MAKEDPB D2N2, 43h, 8,16,8, 8*57,113, 4096, 1024,0 ;Second 8M MAKEDPB D2N3, 43h, 8,16,8, 8*113,169, 4096, 1024,0 ;Third 8M MAKEDPB D2N4, 43h, 8,16,8, 8*169,202, 4096, 1024,0 ;Fourth 2M+ endif if D2F40B ;Fujitsu "BE" 40 Mbyte drive MAKEDPB D2N1, 43h, 8,22,8, 8*1, 47, 4096, 1024,0 ;First 8M MAKEDPB D2N2, 43h, 8,22,8, 8*47, 93, 4096, 1024,0 ;Second 8M MAKEDPB D2N3, 43h, 8,22,8, 8*93,139, 4096, 1024,0 ;Third 8M MAKEDPB D2N4, 43h, 8,22,8, 8*139,185, 4096, 1024,0 ;Fourth 8M MAKEDPB D2N5, 43h, 8,22,8, 8*185,241, 4096, 1024,0 ;Fifth 8M endif endif ; if DISK3 ;Disk 3 hard disk controller active. if D3ST506 ;Seagate 5 Mbyte drive (ST506) MAKEDPB D3N1, 53h, 4,9,8, 1,153, 4096, 1024,0 ;First 5M endif if D3M20 ; 20 Mbyte drive MAKEDPB D3N1, 53h, 8,9,8, 1, 57, 4096, 1024,0 ;First 8M MAKEDPB D3N2, 53h, 8,9,8, 57,113, 4096, 1024,0 ;Second 8M MAKEDPB D3N3, 53h, 8,9,8, 113,153, 4096, 1024,0 ;Third 4M+ endif if D3Q540 ; 40 Mbyte Quantum drive MAKEDPB D3N1, 53h, 8,9,8, 2, 72, 4096, 1024,0 ;First 5M MAKEDPB D3N2, 53h, 8,9,8, 2+(8*75),180, 4096, 1024,0 ;Second 7.4M MAKEDPB D3N3, 53h, 8,9,8, 2+(8*180),285, 4096, 1024,0 ;Third 7.4M MAKEDPB D3N4, 53h, 8,9,8, 2+(8*285),390, 4096, 1024,0 ;Fourth 7.4M MAKEDPB D3N5, 53h, 8,9,8, 2+(8*390),492, 4096, 1024,0 ;Fourth 7.3M MAKEDPB D3N6, 53h, 8,9,8, 2+(8*492),509, 2048, 256,0 ;Sixth 1.2M endif endif ; ; Memory Drives: ; if XMDRIVE ;M-drive using 8088 extended addressing MAKEDPB MEM, MEMTYPE, 1,239,1, 0,32, 2048, 128,0, 0 ;16K ext endif ; if HMDRIVE ;M-DRIVE/H dynamic RAM disk drive HM$FTRK EQU 4 ;32 Sectors/Board = reserved parity area = tracks 0-3 HM$DIRS EQU ((HM$NUM+1)/2)*128 ;Directory entries to allocate if HM$NUM gt 4 MAKEDPB HMD, HMDTYPE, 1,8*HM$NUM,1, HM$FTRK,256, 2048, HM$DIRS,0 else MAKEDPB HMD, HMDTYPE, 1,8*HM$NUM,1, HM$FTRK,256, 2048, HM$DIRS,0 endif endif ; PAGE ;************************************************************************ ;* Macro for generating Control Headers for disk drives (DPH's): * ;************************************************************************ ; ; Set all letter drives to not in use status. IRPC ?X,ABCDEFGHIJKLMNOP DRV&?X SET 0 ALVZ&?X SET 0 CSVZ&?X SET 0 ENDM ; ; The produced format of these Disk Parameter Headers are as follows: ; 8 bits = Mask for drive selection (drive dependent and not std CP/M) ; 16 bits = -> translation table. (real start of DPH) ; 48 bits = Work area for CP/M. ; 16 bits = -> DIRBUF. ; 16 bits = -> Parameter block. ; 16 bits = -> check vector. ; 16 bits = -> allocation vector. ; ; To generate a DPH, use the format: ; MAKEDPH Drive letter, Physical drive selection mask, ; Initial Sector translation table for that drive, ; Initial DPB call letter name (see first entry of MAKEDPB). ; MAKEDPH MACRO ?DR,?MASK,?XLT,?DPBN ;; ?DR = disk name [A,B,C,...,P] ;; ?MASK = disk selection mask for port control ;; ?XLT = sector translation table address ;; ?DPBN = code name for largest corresponding DPB DRV&?DR SET $ DB ?MASK ;;Address of this drive letter's DPH DPH&?DR DW ?XLT ;;Sector translation table DW 0000h, 0000h, 0000h ;;Scratchpad for CP/M DW DIRBUF, DPB&?DPBN ;;Dir buffer, disk parameter block DW CSV&?DR, ALV&?DR ;;Checksum vector, Allocation vector start CSVZ&?DR SET ?DPBN&CSVZ ;;Set checksum vector reserve size ALVZ&?DR SET ?DPBN&ALVZ ;;Set allocation vector reserve size ENDM ; if not (DISK2 or DISK3) ;=1 ; Floppy disks are only permanent storage. if not (FLOPPY5 and BOOT5X) ;=2 MAKEDPH A, 00h, XLT8D3, FD8 ;Drive A: 8 inch floppy MAKEDPH B, 01h, XLT8D3, FD8 ;Drive B: 8 inch floppy if FPY8X4 ;If 4 drives are present ;=3 MAKEDPH C, 02h, XLT8D3, FD8 ;Drive C: 8 inch floppy MAKEDPH D, 03h, XLT8D3, FD8 ;Drive D: 8 inch floppy endif ;=3 ; if FLOPPY5 ;=3 MAKEDPH E, 00h, XLT5D3, FD5 ;Drive E: 5 inch floppy MAKEDPH F, 01h, XLT5D3, FD5 ;Drive F: 5 inch floppy if FPY5X4 ;If 4 drives are present ;=4 MAKEDPH G, 02h, XLT5D3, FD5 ;Drive G: 5 inch floppy MAKEDPH H, 03h, XLT5D3, FD5 ;Drive H: 5 inch floppy endif ;=4 endif ;=3 ; else ;Booting from 5 1/4 inch disks ;=2 ; MAKEDPH A, 00h, XLT5D3, FD5 ;Drive A: 5 inch floppy MAKEDPH B, 01h, XLT5D3, FD5 ;Drive B: 5 inch floppy if FPY5X4 ;If 4 drives are present ;=3 MAKEDPH C, 02h, XLT5D3, FD5 ;Drive C: 5 inch floppy MAKEDPH D, 03h, XLT5D3, FD5 ;Drive D: 5 inch floppy endif ;=3 ; if FLOPPY8 ;=3 MAKEDPH E, 00h, XLT8D3, FD8 ;Drive E: 8 inch floppy MAKEDPH F, 01h, XLT8D3, FD8 ;Drive F: 8 inch floppy if FPY8X4 ;If 4 drives are present ;=4 MAKEDPH G, 02h, XLT8D3, FD8 ;Drive G: 8 inch floppy MAKEDPH H, 03h, XLT8D3, FD8 ;Drive H: 8 inch floppy endif ;=4 endif ;=3 endif ;=2 ; else ;=1 ; Hard Disks exist, and should be used for drive A: if D2M10 ;If Memorex 10 megabyte drive ;=2 D2UNIT0 EQU 'A' - 'A' ;Unit #0 base drive ID select MAKEDPH A, 10h, 00, D2N1 ;Drive A: 8 inch hard disk, select #0, 8M MAKEDPH B, 10h, 00, D2N2 ;Drive B: 8 inch hard disk, select #0, 2M+ if DISK2X ;If second drive present ;=3 D2UNIT1 EQU 'C' - 'A' ;Unit #1 base drive ID select MAKEDPH C, 21h, 00, D2N1 ;Drive C: 8 inch hard disk, select #1, 8M MAKEDPH D, 21h, 00, D2N2 ;Drive D: 8 inch hard disk, select #1, 2M+ endif ;=3 if DISK2Y ;If third drive present ;=3 D2UNIT2 EQU 'E' - 'A' ;Unit #2 base drive ID select MAKEDPH E, 42h, 00, D2N1 ;Drive E: 8 inch hard disk, select #2, 8M MAKEDPH F, 42h, 00, D2N2 ;Drive F: 8 inch hard disk, select #2, 2M+ endif ;=3 if DISK2Z ;If fourth drive present ;=3 D2UNIT3 EQU 'G' - 'A' ;Unit #3 base drive ID select MAKEDPH G, 83h, 00, D2N1 ;Drive G: 8 inch hard disk, select #3, 8M MAKEDPH H, 83h, 00, D2N2 ;Drive H: 8 inch hard disk, select #3, 2M+ endif ;=3 endif ;=2 ; if (D2M20 or D2F20B) ;If Memorex 20 megabyte drive ;=2 ; or Fujitsu "BE" 20 megabyte drive D2UNIT0 EQU 'A' - 'A' ;Unit #0 base drive ID select MAKEDPH A, 10h, 00, D2N1 ;Drive A: 8 inch hard disk, select #0, 8M MAKEDPH B, 10h, 00, D2N2 ;Drive B: 8 inch hard disk, select #0, 8M MAKEDPH C, 10h, 00, D2N3 ;Drive C: 8 inch hard disk, select #0, 4M+ if DISK2X ;If second drive present ;=3 D2UNIT1 EQU 'D' - 'A' ;Unit #1 base drive ID select MAKEDPH D, 21h, 00, D2N1 ;Drive D: 8 inch hard disk, select #1, 8M MAKEDPH E, 21h, 00, D2N2 ;Drive E: 8 inch hard disk, select #1, 8M MAKEDPH F, 21h, 00, D2N3 ;Drive F: 8 inch hard disk, select #1, 4M+ endif ;=3 if DISK2Y ;If third drive present ;=3 D2UNIT2 EQU 'G' - 'A' ;Unit #2 base drive ID select MAKEDPH G, 42h, 00, D2N1 ;Drive G: 8 inch hard disk, select #2, 8M MAKEDPH H, 42h, 00, D2N2 ;Drive H: 8 inch hard disk, select #2, 8M MAKEDPH I, 42h, 00, D2N3 ;Drive I: 8 inch hard disk, select #2, 4M+ endif ;=3 if DISK2Z ;If fourth drive present ;=3 D2UNIT3 EQU 'J' - 'A' ;Unit #3 base drive ID select MAKEDPH J, 83h, 00, D2N1 ;Drive J: 8 inch hard disk, select #3, 8M MAKEDPH K, 83h, 00, D2N2 ;Drive K: 8 inch hard disk, select #3, 8M MAKEDPH L, 83h, 00, D2N3 ;Drive L: 8 inch hard disk, select #3, 4M+ endif ;=3 endif ;=2 ; if D2M26 ;If Shugart 26 megabyte drive ;=2 D2UNIT0 EQU 'A' - 'A' ;Unit #0 base drive ID select MAKEDPH A, 10h, 00, D2N1 ;Drive A: 14 inch hard disk, select #0, 8M MAKEDPH B, 10h, 00, D2N2 ;Drive B: 14 inch hard disk, select #0, 8M MAKEDPH C, 10h, 00, D2N3 ;Drive C: 14 inch hard disk, select #0, 8M MAKEDPH D, 10h, 00, D2N4 ;Drive D: 14 inch hard disk, select #0, 2M+ if DISK2X ;If second drive present ;=3 D2UNIT1 EQU 'E' - 'A' ;Unit #1 base drive ID select MAKEDPH E, 21h, 00, D2N1 ;Drive E: 14 inch hard disk, select #1, 8M MAKEDPH F, 21h, 00, D2N2 ;Drive F: 14 inch hard disk, select #1, 8M MAKEDPH G, 21h, 00, D2N3 ;Drive G: 14 inch hard disk, select #1, 8M MAKEDPH H, 21h, 00, D2N4 ;Drive H: 14 inch hard disk, select #1, 2M+ endif ;=3 if DISK2Y ;If third drive present ;=3 D2UNIT2 EQU 'I' - 'A' ;Unit #2 base drive ID select MAKEDPH I, 42h, 00, D2N1 ;Drive I: 14 inch hard disk, select #2, 8M MAKEDPH J, 42h, 00, D2N2 ;Drive J: 14 inch hard disk, select #2, 8M MAKEDPH K, 42h, 00, D2N3 ;Drive K: 14 inch hard disk, select #2, 8M MAKEDPH L, 42h, 00, D2N4 ;Drive L: 14 inch hard disk, select #2, 2M+ endif ;=3 endif ;=2 ; if D2F40B ;If Fujitsu "BE" 40 megabyte drive ;=2 D2UNIT0 EQU 'A' - 'A' ;Unit #0 base drive ID select MAKEDPH A, 10h, 00, D2N1 ;Drive A: 8 inch hard disk, select #0, 8M MAKEDPH B, 10h, 00, D2N2 ;Drive B: 8 inch hard disk, select #0, 8M MAKEDPH C, 10h, 00, D2N3 ;Drive C: 8 inch hard disk, select #0, 8M MAKEDPH D, 10h, 00, D2N4 ;Drive D: 8 inch hard disk, select #0, 8M MAKEDPH E, 10h, 00, D2N5 ;Drive E: 8 inch hard disk, select #0, 8M if DISK2X ;If second drive present ;=3 D2UNIT1 EQU 'G' - 'A' ;Unit #1 base drive ID select MAKEDPH G, 21h, 00, D2N1 ;Drive G: 8 inch hard disk, select #1, 8M MAKEDPH H, 21h, 00, D2N2 ;Drive H: 8 inch hard disk, select #1, 8M MAKEDPH I, 21h, 00, D2N3 ;Drive I: 8 inch hard disk, select #1, 8M MAKEDPH J, 21h, 00, D2N4 ;Drive J: 8 inch hard disk, select #1, 8M MAKEDPH K, 21h, 00, D2N5 ;Drive K: 8 inch hard disk, select #1, 8M endif ;=3 endif ;=2 ; if DISK3 if D3ST506 MAKEDPH A, 00h, 00, D3N1 ;Drive G: 5 inch hard disk, select #0, 5M if DISK3X MAKEDPH B, 01h, 00, D3N1 ;Drive G: 5 inch hard disk, select #0, 5M endif endif if D3Q540 MAKEDPH A, 00h, 00, D3N1 ;Drive G: 5 inch hard disk, select #0, 5M MAKEDPH B, 00h, 00, D3N2 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH C, 00h, 00, D3N3 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH D, 00h, 00, D3N4 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH E, 00h, 00, D3N5 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH F, 00h, 00, D3N6 ;Drive G: 5 inch hard disk, select #0, 1.2M if DISK3X MAKEDPH G, 01h, 00, D3N1 ;Drive G: 5 inch hard disk, select #0, 5M MAKEDPH H, 01h, 00, D3N2 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH I, 01h, 00, D3N3 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH J, 01h, 00, D3N4 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH K, 01h, 00, D3N5 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH L, 01h, 00, D3N6 ;Drive G: 5 inch hard disk, select #0, 1.2M endif endif endif ; if FLOPPY8 ;=2 if ((DRVI eq 0) and (DRVJ eq 0)) ;=3 MAKEDPH I, 00h, XLT8D3, FD8 ;Drive I: 8 inch floppy MAKEDPH J, 01h, XLT8D3, FD8 ;Drive J: 8 inch floppy if FPY8X4 ;If 4 drives are present ;=4 MAKEDPH K, 02h, XLT8D3, FD8 ;Drive K: 8 inch floppy MAKEDPH L, 03h, XLT8D3, FD8 ;Drive L: 8 inch floppy endif ;=4 else ;=3 MAKEDPH N, 00h, XLT8D3, FD8 ;Drive N: 8 inch floppy MAKEDPH O, 01h, XLT8D3, FD8 ;Drive O: 8 inch floppy endif ;=3 endif ;=2 ; if FLOPPY5 ;=2 if ((DRVK eq 0) and (DRVL eq 0)) ;=3 MAKEDPH K, 00h, XLT5D3, FD5 ;Drive K: 5 1/4 inch floppy MAKEDPH L, 01h, XLT5D3, FD5 ;Drive L: 5 1/4 inch floppy else ;=3 if ((DRVN eq 0) and (DRVO eq 0)) ;=4 MAKEDPH N, 00h, XLT5D3, FD5 ;Drive N: 5 1/4 inch floppy MAKEDPH O, 01h, XLT5D3, FD5 ;Drive O: 5 1/4 inch floppy endif ;=4 endif ;=3 endif ;=2 endif ;=1 ; ; Memory drives. if HMDRIVE ;M-DRIVE/H present if XMDRIVE ;If extended memory drive, then can't use "M:" if (DRVH eq 0) ;If drive "H:" not already specified MAKEDPH H, 00h, 00, HMD ;Drive H: M-DRIVE/H memory drive else ;Use "N:" instead if so MAKEDPH N, 00h, 00, HMD ;Drive N: M-DRIVE/H memory drive endif else ;Use "M:" if this is the only memory drive MAKEDPH M, 00h, 00, HMD ;Drive M: M-DRIVE/H memory drive endif endif ; if XMDRIVE MAKEDPH M, 00h, 00, MEM ;Drive M: M-DRIVE using 8088 endif PAGE ;---------------------------------------- ; ; HOME ROUTINE: ; ; Return disk to home. This routine sets the track number to zero. ; The current host disk buffer is flushed to the disk, and made inactive. ; HOME: CALL FLUSH ;Flush host buffer if incomplete write CALL HSTOFF ;Clear host active flag LHLD SEKDSK ;Move desired drive, type back to active SHLD ACTDSK LDA SAVSEC ;Restore last desired sector to active status STA ACTSEC LDA SEKGPL ;Restore associated Gap Length for floppies STA ACTGPL LHLD DMAADR ;Move selected transfer address to SHLD BUFADR ;the internal buffer address pointer XRA A ;Clear the extended DMA byte (always bank 0) STA DMAADE STA BUFADE ;and extended buffer DMA address LXI B,0 ;Init to track 0 (theoretical position) ; ;---------------------------------------- ; ; SET TRACK ROUTINE: ; ; Set track number. The track number is saved for later use during ; a disk transfer operation. ; ;Entry: B,C = track number. ; SETTRK: MOV L,C! MOV H,B ;Put track number in "H,L" SHLD ACTTRK ;Save as active track number SHLD SEKTRK ;And as desired (blocking/deblocking) RET ; ;---------------------------------------- ; ; SECTOR TRANSLATION ROUTINE: ; ; Translate sector number from logical to physical. ; ;Entry: D,E = 0, then no translation required, ; else: D,E = translation table address and ; B,C = sector number to translate. ; ;Exit: H,L = translated sector number. ; SECTRN: MOV L,C! MOV H,B ;Offset (or record #) in H,L MOV A,E! ORA D ;See if translation required RZ ;Done if not DAD D ;Add base to offset MOV L,M! MVI H,0 ;Translation is always 1 byte for floppies RET ; ;---------------------------------------- ; ; SET SECTOR ROUTINE: ; ; Set the sector for later use in the disk transfer. No ; actual disk operations are perfomed. ; ;Entry: B,C = sector number (sometimes "B" contains an invalid number ; if less than 256 sectors used for selected drive). ; SETSEC: MOV A,C STA ACTSEC ;Sector to seek STA SAVSEC ;Special save area for blocking/deblocking RET ; ;---------------------------------------- ; ; SET NUMBER OF CONTINUOUS SECTORS TO USE: ; ; Set the number of sectors to be used during a single transfer. This ; is a potentially dangerous value to alter in that the READ and WRITE ; routines will currently only use this value correctly if the disk ; is marked as "non-blocked", or having 128 byte sectors. Unless you ; really have a need to alter this value, leave it at "1". ; ;Entry: C = number of sectors to use in the next disk transfer. ; SETNUM: MOV A,C STA NUMSEC RET ; ;---------------------------------------- ; ; SET DIRECT MEMORY ACCESS (Lower 2 bytes): ; ; Set Direct Memory Address (DMA) for subsequent disk read or ; write routines. This is the place the actual requested 128 ; byte record (CP/M 1.4 sector) goes. ; ;Entry: B,C = Disk memory address. ; SETDMA: MOV H,B! MOV L,C SHLD DMAADR ;Save as Direct Memory Access start byte SHLD BUFADR ;And as desired (blocking/deblocking) RET ; ;---------------------------------------- ; ; SET DIRECT MEMORY ACCESS (Upper byte of 24 bit address): ; ; Set extended bank address of DMA as above, currently inactive due ; to the potential havoc it wreaks if not used very, very carefully. ; ;Entry: C = extended bank address byte. ; SETXAD: MOV A,C ;Get extended address byte from "C" STA DMAADE ;Save in memory RET ; ;---------------------------------------- ; ; Create disk drive "existance" table to the actual drives presesnt. This ; makes a list of the relative DBH's for individual logical drives which occupy ; absolute letter identifications. Those logical drives with no associated ; physical counterpart have their pointers set to zero, indicating an invalid ; or non-existant drive. ; DSKTBL: IRPC ?D,ABCDEFGHIJKLMNOP DW DRV$&?D ENDM ; ; SELECT DISK DRIVE: ; ; Select the disk drive for subsequent disk transfers and return the ; appropriate DPB address. This routine diverges from the normal CP/M ; implementation of just saving the disk selection value until the ; transfer is performed. This divergence is required because floppy ; disks are a removable media and come in more than on format. This ; routine determines the correct format and initializes the DPH with ; the appropriate values in agreement with the format type. ; ;Entry: C = Disk selection value. ; D,E and 1 = 0, ==> Must determine disk type, ; else = 1, ==> Drive type has been determined. ; ;Exit: H,L = 0, If drive not selectable, ; H,L = DPH address if drive is selectable, and is initialized for the ; appropriate floppy disk format if D,E = 0, ; else the DPH pointed to contains data about the last disk accessed, ; (the following are not required by CP/M, but are by the FORMAT utility) ; D,E = DPB address if floppy disk was selected and initialized, ; B,C = Base address of sector translate, format info if floppy selected. ; SELDSK: LXI H,0 ;Indicate not found or drive not active MOV A,C! CPI 16! RNC ;Done if invalid drive specified ADI 'A' ;Add in ASCII bias to drive ID STA NRDYM2 ;Set drive letter into "not ready" message MOV B,H ;Clear upper byte of offset word LXI H,DSKTBL ;Base of disk DPH lookup table DAD B! DAD B ;Add offset times 2 MOV A,M! INX H ;Get low order byte of DPH pointer MOV H,M! MOV L,A ;Get word DPH pointer in "H,L" ORA H! JZ SELINV ;Check for valid drive, done if not ; MOV A,M ;Get disk selection mask STA ACTDSK ;Set into ACTDSK (make active disk) STA SEKDSK ;And desired (seek to) INX H ;Point to true DPH PUSH H ;Save DPH address LXI B,5*2 ;Add offset within DPH to its DPB pointer DAD B ;Result in "H,L" = DPH(DPB) MOV C,M! INX H ;Put start of DBP in "B,C" MOV B,M! DCX B ;Point to disk type for this DPH LDAX B ;Get it STA ACTTYP ;Save active disk type STA SEKTYP ;And desired POP H ;Restore DPH address CPI D2$TYPE ;See if fixed media (starting hard disk type) RNC ;Done if hard disk or memory drive ; LXI B,X8TABLE ;Show format parameter table base if FLOPPY5 CPI FD5TYPE ;See if 5 1/4 inch disk type JC SEL8XLT ;Use that format table if so LXI B,X5TABLE ;5 1/4 format parameter table base SEL8XLT:CALL FIX58 ;Patch controller ports endif MOV A,E! ANI 1 ;Mask "Force selected" bit, set status RNZ ;Done if drive previously selected ; PUSH B! PUSH H ;Save XLATE, DPH address again CALL TREAD ;Determine disk type POP D! POP B ;Restore DPH, XLATE address LXI H,0 ;Indicate drive not active RNZ ;If disk type determined, fix DPH to match ; FIX$DPH:PUSH B! PUSH D ;Save base of track info, Drive's DPH address LXI D,DPB8TBL+1 ;Get base of DPB's to get correct one if FLOPPY5 CPI FD5TYPE ;See if 5 1/4 inch disk type JC SEL8DPB ;Use that format table if so LXI D,DPB5TBL+1 ;Get, save base of DPB's to get correct one endif SEL8DPB:ANI 07h! MOV L,A ;Get disk type for DPB locate in "H,L" DAD H! DAD H ;times 4 DAD H! DAD H ;times 4 = 16 bytes/DPB XCHG! DAD D! XCHG ;Add base of DPB's to get correct one in "D,E" ANI 06h! MOV L,A! ADD A ;Remove sided bit (offset 2X), times 2 = 4X ADD L! MOV L,A ;Plus 2X = 6X, offset to table in "H,L" DAD B ;Add "Sector translation" base to offset POP B! PUSH B ;Recover, save again DPH base address MOV A,M! STAX B ;Set translation table address into DPH INX H! INX B ; as word value MOV A,M! STAX B ;At start of DPH INX H! MOV A,M ;Point to associated Gap 3 Length, get it STA SEKGPL ;Save for read/write operations STA ACTGPL LXI H,(5*2)-1 ;Offset from present DPH address to DPB pointer DAD B ;Move current pointer to DPH [DPB] pointer MOV M,E! INX H ;Set DPB address into DPH MOV M,D POP H! POP B ;Restore DPH start address, format table base RET SELINV: STA CDISK ;Put zero in A so drive A: is selected RET ; upon selection of nonexistent drive ; ; TREAD - Determine floppy disk type. ; ;Exit: Zbit set = no error and ; A = disk type (8n + (0-7)) if 8 inch. ; (8n + 20h + (0-7)) if 5 1/4 inch. ; n = 0-3 depending on the number of cylinders/drive. ; TREAD: CALL GTREADY ;Ask for drive status RNZ ;Abort if not ready LDA TEMPBF ;Get status byte ANI FD2SIDE ;Mask TS (two sided) bit RRC! RRC! RRC MOV B,A ;Save sided flag in "B" LXI H,SEKTYP ;Make into drive type with other info MOV A,M! ANI 38h ;Mask to select drive size bits ORA B! MOV M,A LXI H,RECAL ;Home to track 0 MVI B,LRECAL ;No status bytes for command CALL MOVETO ;Process command, (Always reports success) MVI A,2 ;Seek to track two (only done if recal worked) CALL DOSEEK! RNZ ;Abort if error (not reported if not tried) LXI H,DRID MVI M,FD$DRID ;Get disk ID (format info) TRD2: PUSH H LXI B,(DRIDL*256)+7 ;Command length + 7 bytes of status CALL EXEC ;Process command POP H JZ TRD3 ;Get proper drive type if successful MOV A,M! XRI FD$MFM ;Compliment MFM/FM bit MOV M,A! ANI FD$MFM JNZ TRD2 ;Loop for MFM if read not valid ORI 0FFh ;Abort if both FM and MFM tried and failed RET ; TRD3: LDA TEMPBF+6 ;Get number of bytes ADD A! MOV B,A ;times 2 in "B" LDA SEKTYP! ORA B ;Combine N with sided flag STA SEKTYP ;Save disk type CMP A ;Set zero flag (show no error) RET ; PAGE ;---------------------------------------- ; ; READ SECTOR ROUTINE: ; ; Read a CP/M 128 byte sector (also known as a "record"). ; ;Exit: A = 0, Z bit set for successful read operation. ; A = non-zero value if unsucessful read operation. ; READ: CALL CHKBKD ;Check for blocked drive MVI A,FD$RDAT ;Read from single density floppy JNC FINAL ;If non-blocked transfer XRA A ;Set flag to force a read CALL FILL ;Fill buffer with data POP H ;Host record address POP D ;User DMA PUSH PSW ;Save error status byte CALL MOVDTA ;Move 128 bytes POP PSW RZ ;Done if no error ; ; HSTOFF - Turn off host active to prevent write to host. ; Used if error on fill to write buffer and if HOME command issued. ; HSTOFF: XRA A ;Clear host active flag STA HSTACT INR A ;Indicate error condition RET ; ; Check Blocked Disk transfer. ; ;Exit: Carry bit set if blocked device. ; Carry bit clear if unblocked device (logical and physical ; sectors are both 128 bytes in length). ; CHKBKD: LDA SEKTYP ;Get type of device in use if (XMDRIVE or HMDRIVE) CPI MEMTYPE ;If M-drive RNC ;(not blocked) endif if (DISK2 or DISK3) CPI D2$TYPE ;If Hard Disk CMC! RC ;Always blocked if so endif ANI 06h ;See if 128 byte sector 8 or 5 inch RZ ;Not blocked device if so if FLOPPY5 LDA SEKTYP ;Get seek type again CPI FD5TYPE! CMC! RC ;Blocked if 5 1/4 inch, check track if 8" endif LDA SEKTRK ;See if track 0, single density possible ADI 0FFh! RC ;Blocked sectors if not track 0 XRA A ;Force type 0, reset blocked select bit STA ACTTYP ;Use as actual type in use, non-blocked set RET ; ;---------------------------------------- ; ; WRITE SECTOR ROUTINE: ; ; Write the selected 128 byte CP/M sector. ; ;Entry: C = 0, write to a previously allocated block. ; C = 1, write to the directory. ; C = 2, write to the first sector of unallocated data block. ; ;Exit: A = 0, Z bit set for successful write operation. ; A = non-zero value if unsucessful write operation. ; WRITE: CALL CHKBKD ;Check for blocked drive MVI A,FD$WRT ;Write to single density floppy JNC FINAL ;If non-blocked transfer XRA A ;Set for forced pre-read MOV B,C ;Save original write type in "B" DCR C ;Write type in "C" DCR C ;See if write to first of unallocated JNZ WRIT2 ;Forced read if not CMA ;No physical read from disk required WRIT2: PUSH B ;Save write type CALL FILL ;If "A"=0, then a physical read is required POP D ;Host record address POP H ;DMA address PUSH PSW ;Save error status CALL MOVDTA ;Move 128 bytes into host buffer POP PSW ;Recover error status and byte POP B ;Recover write type JNZ HSTOFF ;Abort if any errors occurred INR A ;Make status byte a "1" STA HSTWRT ;HSTWRT = 1 (active for next query) ANA B ;"B" = 1 if write to directory RZ ;Done if not ; ; FLUSH - Write out active host buffer onto disk. ; FLUSH: LXI H,HSTWRT ;See if host write flag is active MOV A,M! ORA A! RZ ;Done if host buffer already on disk DCR M ;Mark inactive for next query LHLD HSTDSK ;Move disk and type SHLD ACTDSK LHLD HSTTRK SHLD ACTTRK LHLD HSTSEC SHLD ACTSEC LXI H,HSTBUF ;Set DMA xfer from host buffer address SHLD BUFADR MVI A,FD$WRT+FD$MFM ;Write double density JMP FINAL ;From host buffer ; ; FILL - fill host buffer with approprite host sector. ; ;Entry: A = 0 means read physical sector required if not in buffer. ; A = 0FFh means read not required. ; ;Exit: On exit the stack will contain the following values: ; POP x = host record address. ; POP y = caller's buffer address. ; A = 0 and Z-bit set if no errors occurred. ; FILL: STA RDFLAG ;Save read flag LXI H,HSTBUF ;Set DMA xfer to host buffer address SHLD BUFADR LDA SEKTYP ;Get disk type if (DISK2 or DISK3) CPI D2$TYPE ;See if hard disk JNC FILL1 ;Use sector block size (log base 2) if so endif ;For both 8 inch and 5 1/4 inch floppies, RRC ;Put "N" field In the least significant bits FILL1: ANI 3 ;Strip to get log base 2 of sector size MOV B,A ;B = log base 2 (sector size) - 7 XCHG ;Host buffer base in D,E LXI H,128 ;128 byte records LDA SAVSEC ;Get logical sector (desired) FILL2: XCHG RRC ;Divide sector # by 2's to get physical JNC FILL3 ;If low bit not set DAD D ;Add bias to offset FILL3: XCHG DAD H ;Double significance of offset for next bit ANI 7Fh ;Mask to get physical sector DCR B ;Bump log count for bit shifts JNZ FILL2 ;If not all bits checked STA SEKSEC ;Physical sec # = desired / (sec size/128) LHLD DMAADR ;Get DMA address from user XTHL ;Set return parameters PUSH D ;Save host buffer pointer to 128 byte sector PUSH H ;Set return address of user LXI H,HSTACT ;Get current Host active flag MOV A,M! ORA A ;and it's status MVI M,1 ;which always becomes 1 (active) JZ FILL6 ;If host buffer was inactive, do a forced read LXI H,HSTSEC-1 ;Compare host values with desired LXI D,SEKSEC-1 MVI C,SEKTYP-SEKSEC+1+1 FILL4: INX H! INX D ;Bump pointers for comparison XRA A ;Clear error byte to zero value DCR C! RZ ;Bump comparison count, done if all bytes match LDAX D! CMP M ;See if current pair match JZ FILL4 ;Loop until all checked or mis-match CALL FLUSH ;Flush host buffer to disk if mis-match RNZ ;Abort if error FILL6: LHLD SEKDSK ;Move disk and type SHLD HSTDSK ;For new host physical read from disk SHLD ACTDSK LHLD SEKTRK SHLD HSTTRK SHLD ACTTRK LHLD SEKSEC SHLD HSTSEC SHLD ACTSEC LDA RDFLAG ;See if read required INR A! RZ ;Done if not ; RDMFM: MVI A,FD$RDAT+FD$MFM ;Read double density ; ; FINAL -- Perform final transfer processing, actual data ; moved from/to memory to/from storage device. ; ;Entry: A = Command, either READ or WRITE. ; ;Exit: A = 0, Z-bit set if no errors occured during transfer with ; a possibility of "MRTRY" retries at the transfer. ; A = non-zero, Z-bit reset if errors occurred on all "MRTRY" ; efforts at a transfer. ; ; --- Footnote #3. --- ; FINAL: LXI H,CIOPB ;Put the initial command in it's buffer MOV M,A! XCHG ;And save pointer in "D,E" LHLD ACTTRK ;Get track (head, cylinder) number in "H,L" LDA ACTSEC! MOV C,A ;Get active sector number in "C" LDA ACTTYP ;Get drive type in "A" if (XMDRIVE or HMDRIVE) CPI MEMTYPE ;See if either M-DRIVE JNC MEMFNL ;Special finish if so endif XCHG ;Track in "D,E" and pointer in "H,L" if (DISK2 or DISK3) CPI D2$TYPE ;See if hard disk type for drive JNC HARDFNL ;Use it's controls if so endif INX H ;Point to next buffer specification (drive) if FLOPPY5 MOV B,A ;Save type CALL FIX58 ;Set ports for 8 or 5 inch floppies MOV A,B endif ANI 07h ;Remove mini-floppy select RAR ;Get N (sector size), reset carry if 1 sided MOV B,A ;Save in "B" JNC FPYFNL ;Done if single sided XRA A ;Clear carry MOV A,E ;Convert to head and cylinder (CY becomes head) RAR ;(divide by 2) MOV E,A FPYFNL: MVI A,0 RAL ;Set head number bit from CY MOV D,A ;Move to head field RLC! RLC ;And head bit in drive # field MOV M,A ;Save temporary value of head LDA ACTDSK ;Get drive # ORA M ;Combine head with drive MOV M,A ;Set drive (0 0 0 0 0 HDS DS1 DS0) INX H! MOV M,E ;+2 = Set cylinder INX H! MOV M,D ;+3 = Set head INX H! MOV A,C! INR A ;+4 = Set sector (+1 for controller) MOV M,A ;Set beginning sector INX H! MOV A,B! MOV M,A ;+5 = Set N field INX H! LDA NUMSEC ;+6 = Ending sector, calculated by N*2 ADD C! MOV M,A ;Set EOT INX H! LDA ACTGPL ;+7 = Set Gap length MOV M,A ;Set GPL field INX H! MVI M,0FFh ;+8 = Set DTL to double density DCR B! JP FNL0 ;Use it if not single type (0) MVI M,80h ;Set DTL to 128 bytes FNL0: MVI E,MRTRY ;Set retry count in "E" FNL1: CALL GTREADY ;Ask for drive status RNZ ;Abort if still not ready DCR E! MOV A,E! RM ;Bump retry count, done if negative status LDA CIOPB+2 ;Get cylinder number CALL DOSEEK ;Seek to proper track JNZ FNL1 ;Retry if seek error LXI H,BUFADE ;Load blocking/deblocking buffer as DMA address MVI B,3 ;24 bits (3 bytes) FNL2: MOV A,M ;Get DMA address to controller DMAPT: OUT FD8PORT+FDMA DCX H! DCR B ;Data is backward in memory JNZ FNL2 ;Loop until all 3 bytes sent LXI H,CIOPB ;Point to Command Input/Output Parameter Block LXI B,(CIOPL SHL 8)+7 ;Set command length, get 7 status bytes CALL EXEC1 ;perform operation SUI 40h ;Remove "abnormal termination of command" bit ; -- This bit is set because at the end of the last sector read or written, ; there was no "Terminal Count" flag issued from an external DMA controller. ; On the DISK 1 this signal is grounded, and although the FDC indicates this ; error, if the "End of Track Encountered" error bit is also set and no others ; are, this represents a successful read or write by the DISK 1. JNZ FNL1 ;Try again if any other error LDA TEMPBF+1 ;Get second result status byte SUI FD$EOC ;Remove sector past end of cylinder bit RZ ;Done if no other errors ANI FD$NWRT ;See if write protect error JZ FNL1 ;Loop if not write error ; ; WRTERR - Get drive write protect status, ask to unprotect if necessary. ; WRTERR: CALL GTDSTS ;Get drive ready status MOV A,M ;Recover full status byte ANI FD$WRTP ;Select write protect status bit LXI H,WRTPMSG ;Write protected disk detected message CNZ PRINT ;Send to console if needs to be changed GTWRT1: CALL CIS ;See if character at console RNZ ;Abort if so CALL GTDSTS ;Get drive status JNZ GTWRT1 ;Loop if drive now not ready MOV A,M ;Recover full status byte ANI FD$WRTP ;Select write protect status bit JNZ GTWRT1 ;If drive still protected, check again JMP FNL1 ;Loop if not protected ; ; GTREADY - Wait until selected drive is ready or console driven abort ; (any char typed). ; ;Exit: Z-bit set if drive is ready, or was made ready after request. ; Z-bit reset if console abort -- drive not ready. ; GTREADY:MVI A,80h ;Turn on drives FDCMOT: OUT FD8PORT+FDON ;Serial port used as "motor on" control bit CALL GTDSTS ;Ask for drive status, ignore first reading CNZ GTDSTS ;Ask again if first reading didn't show ready RZ ;Done if ready LXI H,NRDYM1 ;Show not ready CALL PRINT GTRDY1: CALL GTDSTS ;Get drive status RZ ;Done if now ready CALL CIS ;See if character at console (abort) JZ GTRDY1 ;Loop until console abort or drive ready RET ; ; DOSEEK -- Seek to specified Track: ; ;Entry: A = Track number to seek to. ; DOSEEK: STA DSEKC+2 ;Save cylinder # LXI H,DSEKC ;Seek command pointer MVI B,DSEKL ;Seek command with no status bytes recieved ; ; MOVETO -- Move head according to command: ; ;Entry: H,L = address of command buffer. ; B = length of command buffer. ; ;Exit: Z-bit set if no error. ; MOVETO: CALL EXECP ;Set up seek command in controller SEEKI: CALL INTREQ ;Wait for interrupt service request MVI A,FD$RSTS ;Send Request for interrupt status command FDCD0: OUT FD8PORT+FDCD MVI C,2 ;2 status bytes recieved CALL GCMPS ;Get status SUI FDC$SKE! RZ ;Verify seek end, no other errors -- done if so LDA ACTDSK ;See if disk sampled matches XRA M! ANI 3 ;One in status byte JNZ SEEKI ;Loop until so INR A ;Show error RET ; ; GTDSTS - Get drive ready status of ACTDSK: ; ;Exit: Z-bit set if ready. ; GTDSTS: LXI H,DSTS ;Ask for drive status MVI B,DSTSL ;Command length and 1 status byte CALL EXECP ;Perform command INR C ;Get 1 status byte CALL GCMPS CMA ;Flip polarity of status byte ANI FD$RDY ;Mask ready bit RET ; ; EXEC, execute normal floppy disk controller command sequence: ; EXECP, execute floppy disk controller command sequence with no ; status check afterwards ("C" = 0): ; ;Entry: H,L = Pointer to first byte of command buffer, ; B = number of bytes to output to controller in sequence, ; C = number of bytes to input to status buffer. ; ;Exit: If C <> 0 then see GCMPS. ; H,L = 1 past last byte of command buffer if no status bytes. ; C = 0 on exit always. ; EXECP: MVI C,0 ;No status bytes for this execution EXEC: INX H ;Point to drive # byte LDA ACTDSK ;Set drive into command buffer MOV M,A ;at second byte DCX H ;Point to start of command sequence EXEC1: CALL GETRQM ;Wait for request for master service MOV A,M ;Get command byte FDCD1: OUT FD8PORT+FDCD ;Send to controller INX H! DCR B ;Point to next JNZ EXEC1 ;Loop if more bytes MOV A,C ;# of status bytes ORA A! RZ ;Done if no status bytes ; ; Command Phase completed, now wait for Execution Phase completion indication. CALL INTREQ ;Wait for interrupt service request ; ; Get result status byte(s) into TEMPBF buffer. ; ;Entry: C = number of Result Phase status bytes to read. ; ;Exit: H,L = TEMPBF = pointer to status bytes read in. ; A = first status byte anded with 0F8h with flags set accordingly. ; GCMPS: LXI H,TEMPBF ;Set status buffer address PUSH H ;Save status buffer start pointer GCMPS2: CALL GETRQM ;Wait for request for master service FDCD2: IN FD8PORT+FDCD ;Read result status byte MOV M,A! INX H ;In status buffer, point to next location DCR C ;Decrement status byte counter JNZ GCMPS2 ;Wait until all done POP H ;Point to first status byte MOV A,M ;Get first status byte ANI not (FD$HDS + FD$DRVS) ;Get completion status less drive, head bits RET ; ; Poll for RQM status bit, done when master requests service. GETRQM: FDCS1: IN FD8PORT+FDCS ;Get main status byte ORA A ;See if request for master service bit set JP GETRQM ;Loop if not ready ; ANI FD$DIO ;Mask data direction (DIO) bit RET ; ; Wait for Interrupt Service Request from FDC (8272). INTREQ: INTS1: IN FD8PORT+INTS ;Get interrupt status byte ORA A ;Sample interrupt service request bit JP INTREQ ;Wait for interrupt if not set RET ; ; Self modifying code to fix ports for 8 or 5 inch disk controller. ; if FLOPPY5 FIX58: CPI FD5TYPE ;See if 8 inch drive selected MVI A,FD8PORT JC FIXPORT FIX5: MVI A,FD5PORT ;Use 5 1/4 inch controller if not FIXPORT:STA FDCS1+1 ;Fix control/status ports to correspond INR A STA FDCD0+1 ;Data ports STA FDCD1+1 STA FDCD2+1 INR A STA DMAPT+1 ;DMA port as output STA INTS1+1 ;Status as input INR A STA FDCMOT+1 ;Motor on control port (bit banger unused) RET endif ; PAGE ;**************************************************************** ;* FINAL PROCESSING FOR HARD DISK DRIVES (DISK 2,3) * ;**************************************************************** ; ; Routines for driving the DISK2 and DISK3 controllers, and drives ; corresponding SELECTOR CHANNEL boards if DISK2 in conjunction ; with one to four hard disk drives. ; HARDFNL: if (DISK2 and DISK3) CPI D3$TYPE ;See if DISK3 driver JC D2$FNL ;Finish with DISK2 if not endif if DISK3 D3$FNL: LXI H,D3CIOPB+2 ;Point to DISK3 command parameter block LDA ACTDSK! MOV M,A ;Setup active disk as 0-3 selection of drive LDA CIOPB ;Get original command CMA! ANI 1 ;Correct for DISK3 read/write control bit INX H! MOV M,A ;Save Read/Write command type INX H! MOV M,C ;Save lower half of sector count INX H! XRA A! MOV M,A ;Upper half is zero INX H! MOV M,E ;Save track lower half INX H! MOV M,D ;and upper track half INX H! MVI M,1 ;Transfer 1 sector INX H! MOV M,A ;Upper half is zero XCHG! LHLD BUFADR ;Get local DMA address XCHG! INX H! MOV M,E ;Lower byte saved INX H! MOV M,D ;Middle byte saved LDA BUFADE ;Get upper byte INX H! MOV M,A ;Upper byte saved to complete command load ; Linkage remains fixed to loop back to start of D3CIOPB MVI A,D3$XFER ;Set up for data Input/Output transfer D3EXEC: LXI H,D3CIOPB ;Point to parameter block in use D3EXEC0:MOV M,A ;Put command in first byte of parameter block INX H! MVI M,0 ;Clear the status byte ; Begin command execution, wait for result status. MVI A,D3$ATTN ;Issue "attention" command to DISK3 to start OUT D3$PORT ;Disk controller operation D3EXEC1:MOV A,M! ORA A ;See if command execution has completed JZ D3EXEC1 ;End status check if byte now non-zero D3EXEC2:INR A! RZ ;See if result was 0FFh (valid completion) ADI '0'-1 ;Add ASCII bias for error type STA D3MSG2 PUSH B! PUSH D! PUSH H LXI H,D3MSG ;Show error message and type CALL PRINT CALL CI ;Wait for operator to respond POP H! POP D! POP B ORI 0FFh ;Show error condition on return RET ; endif ; PAGE ;******************************************************** ;* FINAL PROCESSING FOR DISK 2 HARD DISK DRIVES * ;******************************************************** ; if DISK2 D2$FNL: MOV A,E ;Get low order portion of track XCHG ;Put desired track number in "H,L" DAD H! DAD H ;Shift left 4 bits to put cylinder in "H" DAD H! DAD H ; if (D2M??) ;16 heads ; ANI 0000$1111b ;Select head value (0-15) ; endif if (D2M20 or D2F40B or D2M26) ;8 heads ANI 0000$0111b ;Select head value (0-7) DAD H ;Shift left (6 bits) again for proper cylinder in "H" endif if (D2M10 or D2F20B) ;4 heads ANI 0000$0011b ;Select head value (0-3) DAD H! DAD H ;Shift left again for proper cylinder in "H" endif MOV L,A ;Put head value in "L" XCHG ;Return both to "D,E" - command pointer "H,L" MOV A,M! ANI 1 ;Select Read/Write bit from floppy command MVI M,D2$READ ;Replace with hard disk read JZ D2FNLX ;if was a floppy read command MVI M,D2$WRT ;Use write command instead if not D2FNLX: MVI L,MRTRY ;Load max retry count D2FNLP: DCR L! MOV A,L! RM ;Bump retry count, done if neg (0FFh) status CALL D2SELECT ;(Re-)select appropriate drive, verify ready RNZ ;Abort if not PUSH H ;Save retry counter on stack CALL D2XFER ;Seek to proper cylinder, perform data transfer POP H ;Recover retry counter RZ ;Done if no errors ADD A ;See if timeout error (header not found) JP D2FNLP ;Loop in case of soft error (not timeout) ; ; D2RELOCATE -- Search relocation table for matching sector ID, load ; corresponding relocated sector values if found and try transfer ; again using alternate sector. ; D2RELOC:RRC ;Restore error byte ANI D2$OVR ;See if overrun was cause of timeout JNZ D2FNLP ;Treat as soft error if so LXI H,(RELTBL0 - 2) - (3 * D2$SCNT) ; Point to base of relocation table(s), pre-comp for 2 indexes PUSH D ;Save head, cylinder specification LXI D,(3 * D2$SCNT) ;Point to next disk unit's faulty sector table D2REL1: DAD D ;Add unit table size until correct one found DCR B ;Bump unit number count JP D2REL1 ;Loop until pointer at this unit's table POP D ;Recover head, cylinder specification ; ; Loop entry points -- if no match found, skip other tests for match of ; current head, sector and advance to next 3 byte entry in table. D2REL5: INX H ;Skip head storage byte D2REL4: INX H ;Skip sector storage byte D2REL3: MVI A,D2$SCNT-1 ;See if all available spare sectors checked INR B! CMP B ;Bump count of table entries, see if past max RC ;Irrecoverable error if no entries match MOV A,M! INX H! CMP D ;See if CYLINDER matches table JNZ D2REL5 ;Skip head, sector check if not MOV A,M! INX H! CMP E ;See if HEAD number matches table JNZ D2REL4 ;Skip sector check if not MOV A,M! INX H! CMP C ;See if SECTOR matches table JNZ D2REL3 ;See if scan next triplet if not ; ; Place new values in the registers to correspond with the relocated sector, ; and continue with the Disk 2 read/write operation using the "spare" storage. D2XRELS:MOV C,B ;Corresponding sector number counted in "B" LXI D,2 ;Relocated sectors at cylinder 0, head #2 JMP D2FNLX ;Continue with execution using new values ; ; D2SELECT -- Select appropriate DISK 2 hard disk drive. ; ;Entry: ACTDSK set to current active drive to get ready status. ; ;Exit: C,D,E unaltered, ; B = Lower nibble of drive select (0-3) from ACTDSK value, ; Zero bit set if drive is ready. ; D2SELECT:CALL D2READY ;See if drive is ready, ignore first if not CNZ D2READY ;Reset drive fault bit, again check drive ready RZ ;Return valid if so PUSH B! PUSH D! PUSH H ;Save desired positions LXI H,D2$MSG ;Show unit not ready CALL PRINT D2SEL1: CALL CIS ;See if character from console JNZ D2SEL2 ;Return with zero bit reset (error) if so CALL D2READY ;See if drive is ready JNZ D2SEL1 ;Keep trying until so or console abort D2SEL2: POP H! POP D! POP B ;Recover desired positions RET ; ; Routine to get the drive ready status of the active hard disk. D2READY:LDA ACTDSK ;Get unit select (upper and lower nibbles set) ANI 3! MOV B,A ;Select lower nibble in range 0-3, save in "B" ORI (D2$STRB + D2$RST) ;Add in drive strobe, fault clear bits OUT D2$CNTL ;Set drive select register ADI '0'-(D2$STRB+D2$RST);Add bias and save as drive ASCII ID STA D2$MSGX ;in error message LDA ACTDSK ;Get unit select (upper and lower nibbles set) ANI 1111$0000b ;Use only upper nibble single select OUT D2$DATA ;Set up DISK 2 with drive select IN D2$STAT ;Get status byte ANI (D2$ATTN + D2$NRDY) ;Strip out attention, drive ready bits XRI D2$ATTN ;Flip status of attention bit RET ;Return with drive ready status ; ; D2XFER - DISK 2 Data Transfer (to/from Hard Disk) Operation. ; ; -- Two part transfer (with seek to home position if cylinder unknown). ; First the command sequence is given to seek correct cylinder, and for drives ; having automatic settling time delays, the concurrent loading of the ; controller to execute the actual data transfer. For drives not having an ; automatic settling time delay, the No-op command is issued after a seek, ; which allows between 1 and 2 extra revolutions of the disk to allow the ; heads to settle. ; ;Entry: CIOPB = command, (read or write) ; D = Desired cylinder to seek, ; E = Desired head of cylinder, ; C = Desired sector of track, ; B = Actual disk unit in use. ; ;Exit: B,C,D,E unaltered, ; H,L = Pointer to current cylinder table storage, ; Zero bit set if no errors occurred. ; D2XFER: LXI H,HD2CYL ;Base address of last known cylinder positions PUSH B ;Get this drive's MOV C,B! MVI B,0 ;Offset for last cylinder position in "B,C" DAD B! POP B ;Add offset for desired drive unit number (0-3) MOV A,M ;Get last position for active unit CPI -1 ;Minus one indicates unknown -- seek home CZ D2HOME ;Execute sequence if "forced" home ;Current cylinder again (after home) in "A" SUB D ;Get the difference of desired and current CNZ D2SEEK ;If current not at desired cylinder, seek to it ; ; DISK 2 Transfer data to/from hard disk. PUSH H ;Save cylinder pointer MVI A,D2$STRB ;Add drive select command ORA B ;to unit desired OUT D2$CNTL ;Output to DISK 2 control register LDA ACTDSK ;Get active drive number ANI 1111$0000b ;Mask single select bits (high nibble) ORA E! OUT D2$DATA ;Select drive, head MVI A,D2$CYL ;Select cylinder number storage OUT D2$CNTL MOV A,D! OUT D2$DATA ;Get desired Cylinder number MVI A,D2$HEAD ;Select head number storage OUT D2$CNTL MOV A,E! OUT D2$DATA ;Get the desired Head number MVI A,D2$SEC ;Select sector storage OUT D2$CNTL MOV A,C! OUT D2$DATA ;Load desired sector ; ; Set up selector channel to correspond, with desired DMA value ready. IN SELCHAN ;Initialize selector channel to accept data LXI H,BUFADE ;Get extended DMA address byte MOV A,M! DCX H ;from storage, point to middle byte OUT SELCHAN ;Upper byte Loaded first MOV A,M! DCX H ;Load with remaining DMA mid, low bytes OUT SELCHAN MOV A,M ;For full 24 bit DMA transfer address OUT SELCHAN LDA CIOPB+0 ;Get hard disk Read/Write command CPI D2$READ ;See if read operation PUSH PSW ;Save Read/Write operation type MVI A,SELBYT+00h ;Upper bit not set if write operation JNZ D2XFER4 MVI A,SELBYT+80h ;Set up selector channel to correspond D2XFER4:OUT SELCHAN ;Last byte of 4 in sequence POP PSW! ORA B ;Get read/write command that begins transfer OUT D2$CNTL ;Send to controller to begin execution XTHL! XTHL ;Let the state machine start it's sequence ; Loop until command completed. D2XFER5:IN D2$STAT ;Get status of controller ORA A ;See if command still in process JM D2XFER5 ;Loop until transfer complete ANI (D2$TOUT + D2$CRC + D2$OVR + D2$NRDY + D2SEKD + D2$WRTF) ; Select Timeout, CRC error, Overrun, Not Ready, Seek Done, Write Fault* bits XRI D2$WRTF ;Flip status of write fault bit for final POP H ;Recover table pointer for cylinder updates PUSH PSW ;Save error status MVI A,D2$STRB ;Reset interrupt status bit to inactive state OUT D2$CNTL MVI A,0001$0000b ;Reselect Unit #0 to execute command OUT D2$DATA POP PSW! RZ ;Done if no errors from transfer operation MVI M,0FFh ;Show drive at unknown position, force home RET ;Return with error type set ; ; D2SEEK -- DISK 2 Execute seek to desired cylinder position. ; ;Entry: H,L = Pointer to current cylinder table storage, ; D = Desired cylinder to seek, ; E = Desired head of cylinder, ; C = Desired sector of track, ; B = Actual disk unit in use. ; ;Exit: B,C,D,E,H,L unaltered, return when desired cylinder has been reached. ; Storage for unit positioner [H,L] updated, indicating new location. ; D2SEEK: PUSH D ;Save head, cylinder numbers MVI E,D2$SOU ;Seek outward control byte JNC D2SEEKO ;If seek outward MVI E,D2$SIN ;Seek inward control byte CMA! INR A ;Two's compliment count to make positive D2SEEKO:MOV D,A ;Move number of tracks MOV A,B! ORA E ;Get selected drive, add in step direction OUT D2$CNTL ;Send to DISK2 XTHL! XTHL ;Allow the direction bit to settle D2STEP1:IN D2$DATA ;Activate a step if (D2M10 or D2M20 or D2F20B or D2F40B) ;All steps accumulated by drive DCR D ;Bump step counter JNZ D2STEP1 ;If not all steps sent POP D ;Recover desired cylinder MOV M,D ;Set current storage = desired else ;Steps are issued individually D2SEEK2:IN D2$STAT ;Get drive status ANI D2$SEKD ;See if seek complete JNZ D2SEEK2 ;Wait until so DCR D ;Bump step counter JNZ D2STEP1 ;Loop for another if not all steps sent if D2M26 ;Shugart drive requires computed settling time MVI A,D2$TIME ;Issue a NO-OP command to get settling delay ORA B ;Add in selected unit control bits OUT D2$CNTL ;Send to controller, use next instructions for ; command settling delay (no interrupt active) POP D ;Recover desired cylinder MOV M,D ;Set current storage = desired D2SKDLY:IN D2$STAT ;Get controller status byte ANI D2$TOUT ;Mask timeout bit (1-2 index pulse delay) JZ D2SKDLY ;Loop for settling time (approx.) till timeout else ;No extra delay necessary POP D ;Recover desired cylinder MOV M,D ;Set current storage = desired endif endif RET ; ; D2HOME -- DISK 2 Execute seek to Home position (cylinder #0). ; This routine is called when the actual position of a drive unit (as measured ; by it's cylinder number) is in doubt. It returns the positioner to the ; special "known" place at the outermost cylinder. ; ;Entry: H,L = Pointer to current cylinder table storage, ; B = Actual disk unit in use. ; ;Exit: B,C,D,E,H,L unaltered, return when cylinder 0 has been reached, ; A = 0, and storage for unit (H,L pointer) reset, indicating proper ; location of positioner. ; D2HOME: INR M ;Mark current with proper "home" value (zero) PUSH B! MOV C,M ;Save sector, use 256 count in "C" D2HOME0:IN D2$STAT ;Get drive status ANI D2$CYL0 ;See if cylinder 0 has been reached JZ D2HOME3 ;Done with seek operation if so MOV A,B ;Get active drive unit ORI (D2$SOU + D2$RST) ;Add in step out command, reset fault bit OUT D2$CNTL ;Send to DISK2 D2HOME1:IN D2$DATA ;Cause a step to be activated DCR C ;Bump step counter if (D2M10 or D2M20 or D2F20B or D2F40B) ; Fujitsu drives accept all pulses at once JNZ D2HOME1 ;Loop if not done with steps endif D2HOME2:IN D2$STAT ;Get drive status ANI D2$SEKD ;See if seek complete JNZ D2HOME2 ;Wait until so if D2M26 ;Shugart drives require separate pulses JMP D2HOME0 ;Loop if not done with steps endif D2HOME3:POP B ;Recover desired sector, active unit MOV A,M ;Get current cylinder again (after home) in "A" RET endif ; PAGE ;**************************************************************** ;* FINAL PROCESSING FOR BOTH TYPES OF MEMORY DRIVES * ;**************************************************************** ; ; First is M-DRIVE/H which may optionally utilize "parity byte" ; software error checking stored at its lowest value "tracks". ; if (XMDRIVE or HMDRIVE) MEMFNL:;** MOV A,C ;Get pseudo sector (4K bank) ;** MOV B,H ! MOV C,L ;Save requested cylinder *** DAD H! DAD H! DAD H ;Pseudo cylinder (128 byte block in 4K bank) DAD H! DAD H! DAD H ;Multiply by 128 to get offset in bank DAD H ;32 Pseudo cylinders max (32 * 128 = 4K) XCHG ;MDRIVE "track" address in "D,E" LHLD DMAADR ;Direct memory address in "H,L" if (XMDRIVE and HMDRIVE) ;If both MDRIVE's are active JZ MEMXFNL ;Use 8088 mapped memory if exactly "MEMTYPE" endif ; if HMDRIVE ;If M-DRIVE/H is active ; ----- For M-DRIVE/H ----- ;Entry: D,E = Translated "Track" in range of (4-511)*128, ; A = "Sector" in range of 0-"8n-1", where "n" is # of M-DRIVE/H boards. ;** B,C = Actual track ; ;** Rearranged so that "sector" info is actually the high order address byte - ; "D" becomes the low order track address byte, and since "D" may hold ; only a "1" or "0" in it's MSB, use it to identify the "high or low" ; sector being addressed by rotating that bit to the high order position. ; ; Lines commented out with a ";**" are a slightly more efficient way of keeping ; track of parity bytes. The line below a ";**" keeps parity the way other » CompuPrï BIOS'ó dï tï maintaiî compatibilitù across OS's. HMD$FNL:;** PUSH B ;Save "track" for parity PUSÈ PSW¡ OUÔ HM$CNTÌ ;Savå "sector¢ foò parity¬ higè "track¢ byte MOV A,D! OUT HM$CNTL ;Low order byte of "track" MOV A,E! OUT HM$CNTL ;Set "record" 1 or 0 ID in final byte setup ;** LXI B,5A80h ;Start parity ("B"), 128 Byte operation ("C") LXI B,8080H LDA CIOPB ;See if read operation requested CPI FD$RDAT JNZ HMD$WRT ;Do write if not HMD$RD: IN HM$DATA ;Get data byte from "drive" MOV M,A! INX H ;In memory, point to next location ADD B! MOV B,A ;Accumulate parity byte DCR C ;Until all 128 bytes done JNZ HMD$RD ;If done, point to stored disk parity byte POP PSW! OUT HM$CNTL ;Set High order byte of track ;** POP D ;** MOV A,E MOV A,D ;** RLC ;Move high bit into low order position OUT HM$CNTL ;Set Low order byte of track (single bit) ;** MOV A,D! ;** OUT HM$CNTL ;Use old byte of "track" to find single byte MOV A,E! OUT HM$CNTL ;Use old byte of "track" to find single byte IN HM$DATA! SUB B ;Get it and see if parity matches RET ;Return with error status ; ; Write operation to MDRIVE/H. HMD$WRT:MOV A,M! INX H ;Get byte from memory, point to next OUT HM$DATA ;Xmit to "drive" ADD B! MOV B,A ;Accumulate parity byte DCR C ;Until 128 bytes done JNZ HMD$WRT ;If done, point to storage for parity byte POP PSW! OUT HM$CNTL ;Set High order byte of track ;** POP D ;Recover selected track MOV A,D ;** MOV A,E! ;** RLC ;Move high bit into low order position OUT HM$CNTL ;Set Low order byte of track (single bit) MOV A,E! OUT HM$CNTL ;** MOV A,D! ;** OUT HM$CNTL ;Use old byte of "track" to find single byte MOV A,B! OUT HM$DATA ;Save it to memory "drive" reserved area XRA A! RET ;Show valid write operation endif ; ; ---- Memory Mapped Drive using 8088 as "DMA controller" ---- ;Entry: D,E = "Track" in range of 0-32, ; C = "Sector" in range of 0-"n", where "n" is 4K memory block number. ; if XMDRIVE MEMXFNL: if XMBOOT ADI 18 ;Add offset to get past host bank (64K=16*4K) else ; and boot + (2*4K) ADI 16 ;Add offset to get past host bank (64K=16*4K) endif MOV C,A ;And save MVI B,128/2 ;Word count for sector (64 * 16 bits) LDA CIOPB ;See if read operation CPI FD$RDAT MOV A,C ;Get pseudo sector (4K bank) JZ OUTOF ;Do a read from external memory operation if so ;---- JMP INTO ;Do a write to external memory operation if not endif endif ; ; MOVDTA - Move data (128 bytes) in memory. ; ;Entry: H,L = Source address, ; D,E = Destination address, ; A = Source / Destination extended address byte (direction dependent). ; if I8088 INTO: XCHG STA ES88+1 ;Set 8088 DS register to destination bank addr XRA A! STA DS88+1 ;Set 8088 ES register to current source address JMP TRN88 ; MOVDTA: XCHG ;Make "D,E" = source, "H,L" = destination MVI B,128/2 ;128 bytes to move (64 words) XRA A OUTOF: STA DS88+1 ;Set 8088 DS register (Data Segment) to source XRA A! STA ES88+1 ;Set 8088 ES register (Extra Segment) to dest TRN88: SHLD DI88 ;8088 Destination Index XCHG! SHLD SI88 ;8088 Source Index MOV A,B! STA CX88 ;Get # of words to move in CX reg storage XRA A! STA CX88+1 ;Clear high byte of 8088 CX reg storage STA DS88 ;Clear low byte of 8088 DS reg storage STA ES88 ;Clear low byte of 8088 ES reg storage IN SWAPP! XRA A ;Switch to 8088 CPU, show no errors for M-DRIVE RET ; ; The following is 8086/8088 code which is used for moving ; data via the dual processor's 8088. ; LOOP88: DB 0E4h,SWAPP ;INB BANKP ;Wait for transfer DB 0FCh ;CLD ;Set direction DB 0B8h ;MOV AX,#BXADR ;Set AX to current bank DW 0 ;Always 0 for dual processor DB 08Eh,0D8h ;MOV DS,AX ;Set DS to current bank DB 08Bh,00Eh ;MOV CX,[CX88] ;Set CX (word count) DW CX88 ;Auto decrement on MOVS DB 0C4h,03Eh ;LES DI,[DI88] ;Set DI and ES (destination) DW DI88 ;Auto increment on MOVS DB 0C5h,036h ;LDS SI,[SI88] ;Set SI and DS (source) DW SI88 ;Auto increment on MOVS DB 0F2h ;REP ;Repeat next op until CX=0 DB 0A5h ;MOVS word ;Move source word to dest DB 0EBh ;JMP LOP88 ;Loop when block moved DB -($-LOOP88+1) ; else if z80 MOVDTA: LXI B,128 ;128 bytes to move DB 0EDH,0B0H ;LDIR ;Perform move RET else MOVDTA: MVI B,128 ;128 bytes to move MOVE1: MOV A,M! STAX D ;Get source byte, save in destination INX H! INX D ;Point to next in each DCR B ;Byte count JNZ MOVE1 ;Loop until done RET endif endif PAGE ; ; PRINT -- Print message terminated by zero byte: ;Entry: H,L = message pointer, terminated by NULL character. ;Exit: H,L = zero byte + 1. ; PRINT: PUSH H ;Save string pointer MVI C,CR ;Xmit a carriage return CALL CO ;Output to the console MVI C,LF ;Followed by a line feed POP H ;Recover string pointer PRINT1: PUSH H ;Save string pointer CALL CO ;Output to the console POP H ;Recover string pointer MOV A,M! INX H ;Get a character, point to next ORA A! RZ ;If zero (NULL), then terminate MOV C,A JMP PRINT1 ;Loop until "NULL" encountered in string ; ; Drive error messages altered to indicate the drive (unit) in question. ; WRTPMSG:DB 'Write Error - Unprotect Disk, then Re-' NRDYM1: DB 'Load Drive ' NRDYM2: DB 'x:',CR,LF,0 ; if DISK2 D2$MSG: DB 'DISK2 Unit #' D2$MSGX: DB 'x Not Ready',0 endif ; if DISK3 D3MSG: DB 'DISK3 Error #' D3MSG2: DB 'x',0 endif ; PAGE ;************************************************ ;* INITIALIZED VARIABLES / CONSTANTS * ;************************************************ if I8088 ; ; Initial values here are set up for start of XMBOOT (if activated). ; ; 01000H = Point to place to save CP/M block (destination segment). ; CCP/16 = Point to start of CCP (source segment). ; 1600H/2 = Word length of CP/M as move count (to save to high mem). ; ; DESTINATION ADDRESS (Segment and Index register words): DI88: DW 0000h ;Destination Index ES88: DW 1000h ;Extended Segment ; ; SOURCE ADDRESS (Segment and Index register words): SI88: DW 0000h ;Source Index DS88: DW CCP/16 ;Data Segment ; ; LENGTH (words -- 2 bytes each): CX88: DW 1600h/2 ;Amount to move (Words) endif ; ; BIOS blocking / deblocking flags. ; HSTACT: DB 0 ;Host active flag HSTWRT: DB 0 ;Host written (must follow host active flag) RDFLAG: DB 0 ;Read flag ; ; Floppy Disk commands followed by data storage bytes. ; DSEKC: DB FD$SEEK,0,0 ;Seek track command DSEKL: EQU $-DSEKC ; DSTS: DB FD$DSTS,0 ;Get drive status command DSTSL: EQU $-DSTS ; RECAL: DB FD$RECA,0 ;Recalibrate (seek to home track) command LRECAL: EQU $-RECAL ; DRID: DB FD$DRID,0 ;Read disk ID command to get sector size DRIDL: EQU $-DRID ; ; Disk 2 track location storage (not retained by controller). ; if DISK2 HD2CYL: DB -1,-1,-1,-1 ;Show all 4 possible drives in unknown starting endif ;positions (force home operation). PAGE ;**************************************** ;* WARM AND COLD BOOT ROUTINES * ;**************************************** ; ; BOOT CP/M FROM SPECIFIED DISK: ; ; The CBOOT entry point gets control from the cold start loader and ; is responsible for the basic system initialization. This includes ; outputting a sign-on message and initializing the following page ; zero locations: ; ; 0,1,2: Set to the warmstart jump vector. ; 3: Set to the initial IOBYTE value. ; 4: Default and logged on drive. ; 5,6,7: Set to a jump to BDOS. ; ;Exit: Register C must contain the selected drive, which is ; zero to select the "A:" drive. The exit address is to ; the CCP routine. ; ; The WBOOT entry point gets control when a warm start occurs, ; a ^C from the console, a jump to BDOS (function 0), or a jump ; to location zero. The WBOOT routine reads the CCP and BDOS ; from the appropriate disk sectors. WBOOT must also re-initialize ; locations 0,1,2 and 5,6,7. The WBOOT routine exits with the "C" ; register set to the appropriate drive selection value. The exit ; address is to the CCP. ; WBOOT: LXI SP,100h ;Temp stack for boot LXI H,CCP+3 ;The "+3" allows warm boot and go to PUSH H ;CP/M with no buffer execution ; ; CCP can be loaded with name of program to execute if vector ; to just "CCP". This is used at end of cold boot to load any extra ; routines to the CBIOS (such as interrupt handlers), and may be a ; SUBMIT file which does many things. ; if XMBOOT ; This routine requires that you have ram at the base of the next ; memory segment - 010000h. It must be at least 8K in size to accomodate both ; the CCP and BDOS. If you have memory above 64K, this is a good use for it - ; it considerably speeds up the warm boot process and makes more CBIOS ram ; available for bitmaps and such. It also permits you to run after initial ; boot with "systemless" diskettes (even allows you to use a single side, ; single density diskette where the warm boot disk would normally reside). ; ;NOTE: Having M-Drive active is not necessary, if both are used then ; the M-drive size is reduced by only 8K. ; LXI H,0 SHLD SI88 ;Set both index pointers to 0 SHLD DI88 MVI H,10h ;Point to saved CP/M block SHLD DS88 ;and use as source (Start of 2nd memory bank) LXI H,CCP/16 ;Point to start of CCP SHLD ES88 ;and use as destination address LXI H,1600h/2 ;Word length of CP/M SHLD CX88 ;as counter IN SWAPP ;Perform block move using 8088 else CALL LOADCPM ;Boot CP/M from floppy or hard disk endif GOCPM: LXI B,80h ;Set default data transfer address CALL SETDMA MVI A,0C3h ;Store jumps in low memory STA 0 ;CBIOS vector STA 5 ;BDOS vector LXI H,BIOS+3 ;Warm boot vector address SHLD 1 LXI H,X$BDOS ;BDOS vector address SHLD 6 LDA CDISK ;Get active warm boot disk # MOV C,A ;Put in "C" for CP/M RET ; if not XMBOOT ;If re-boot is always from a disk (of some type) -- BOOTCPM ;Boot CP/M from disk, exit only if load sucessful. endif ; PAGE ;************************************************ ;* CONSOLE,READER,PUNCH,LIST ROUTINES * ;************************************************ ; ; CONSOLE STATUS INPUT ROUTINE: ; ;Exit: A = 0 (zero), means no character currently ready to read. ; A = FFh (255), means character currently ready to read. ; IOBYTE selects device to use as follows: ; 0 = TTY:, 1 = CRT:, 2 = BAT:, 3 = UC1: ; USER 6 xxx xxx USER 7 ; ----- If CRT, secondary select done using IOCNTL byte: ; 0 = USER 0, 1 = SysSup 1, 2 = IF1P0, 3 = Custom. ; ----- If BAT, secondary select done using READER of IOBYTE: ; 0 = USER 0 1 = USER 1, 2 = USER 2, 3 = USER 3 ; CONST: LDA IOBYTE ;Get I/O Byte (0=TTY,1=CRT,2=BAT,3=UC1) ANI 3 ;Select console bits JNZ CONST1 ;If not TTY, check for others ; ; T T Y -- Used by all six logical device vectors. TTYST: MVI A,6 ;TTY is always Interfacer 3,4 USER 6 IF3STS: OUT IF3UX ;Select mux IN IF3US ;Get TTY status IFXST: ANI UDAV ;Mask data available XCRTST: RZ ORI 0FFh ;Show ready RET ; CONST1: DCR A ;See if CRT selected JNZ CONST2 ;Check for UC1 or BATCH if not ; ; C R T -- Video Display Terminal. CRTST: LDA IOCNTL ;Get I/O control select byte ANI 00$0000$11b ;Check on CRT select bits JZ IF3STS ;If zero, CRT is Interfacer 3,4 USER 0 DCR A ;See if System Support I UART JNZ CRTST2 ;Try devices 2,3 if not IN SS1US ;Get console status JMP IFXST ;If I/O control was a "1", then use SS1 board ; CRTST2: DCR A ;See if UART 0 of IF 1,2 JNZ CRTST3 ;Use device 3 if not IN IF1US0 ;switch selects Interfacer I,II port at 00h JMP IFXST ;All CompuPro input status masks are identical ; ; Custom CRT routine, initially set for Interfacer I,II UART 1. CRTST3: IN IF1US1 ;BUILD YOUR OWN CUSTOM CRT DRIVER HERE ANI UDAV ;Select status bit(s) XRI 0 ;Optional compliment of data JMP XCRTST ;Universal return from status check ; ; More checks on regular console devices. CONST2: DCR A ;See if UC1 selected ; ; U C 1 -- Optional user console device. UC1ST: MVI A,7 ;Get optional user 7 console status JNZ IF3STS ;Complete status mask if not BATCH selected ; ; B A T -- BATCH Mode (use READER select bits to chose USER 0-3). LDA IOBYTE RRC! RRC ;Put READER select bits in lower 2 bits ANI 3 ;To select low user # JMP IF3STS ;And get IF3 status ; ;**************************************** ;* CONSOLE DATA INPUT ROUTINE: * ;**************************************** ; ; Read the next character into the A register, clearing ; the high order bit. If no character currently ready to ; read then wait for a character to arrive before returning. ; ; IOBYTE selects device to use as follows: ; 0 = TTY:, 1 = CRT:, 2 = BAT:, 3 = UC1: ; USER 6 xxx xxx USER 7 ;----- If CRT, secondary select done using IOCNTL byte: ; 0 = USER 0, 1 = SysSup 1, 2 = IF1P0, 3 = Custom. ;----- If BAT, secondary select done using READER of IOBYTE: ; 0 = USER 0 1 = USER 1, 2 = USER 2, 3 = USER 3 ; ;Exit: A = character read from terminal. ; CONIN: LDA IOBYTE ;Get I/O Byte (0=TTY,1=CRT,2=BAT,3=UC1) ANI 3 ;Select console bits JNZ CONIN1 ;If not TTY, check for others ; ; T T Y -- Used by all six logical device vecotrs. TTYIN: MVI A,6 ;TTY is always Interfacer 3,4 USER 6 IF3IN: OUT IF3UX ;Select mux IF3IN2: IN IF3US ;Get TTY status ANI UDAV ;Mask data available JZ IF3IN2 ;Loop until ready IN IF3UD ;Get data ANI 7Fh ;Strip out parity RET ; CONIN1: DCR A ;See if CRT JNZ CONIN2 ;Try UC1 or BATCH if not ; ; C R T -- Video Display Terminal. CRTIN: LDA IOCNTL ;Get I/O control select byte ANI 00$0000$11b ;Check on CRT select bits JZ IF3IN ;If zero, CRT is Interfacer 3,4 USER 0 DCR A ;See if System Support I UART JNZ CRTIN2 ;Try devices 2,3 if not CRTIN1: IN SS1US ;Get console status ANI UDAV JZ CRTIN1 ;Loop if data not available IN SS1UD ;Get data ANI 7Fh ;Strip out parity RET ; CRTIN2: DCR A ;See if Interfacer I,II UART 0 JNZ CRTIN3 ;Use alternate CRT device 3 if not CRTIN2X:IN IF1US0 ;switch selects Interfacer I,II ANI UDAV JZ CRTIN2X ;Loop if data not available IN IF1UD0 ;Get data ANI 7Fh ;Strip out parity RET ; ; Custom CRT routine, initially set for Interfacer I,II UART 1. CRTIN3: IN IF1US1 ;BUILD YOUR OWN CUSTOM CRT INPUT ROUTINE ANI UDAV ;Strip out console status bits XRI 0 ;Flip status bits as necessary JZ CRTIN3 ;Loop if data not available IN IF1UD1 ;Get data ANI 7Fh ;Strip out parity RET ; ; Check for other console devices. CONIN2: DCR A ;See if UC1 selected ; ; U C 1 -- Optional user console device. UC1CI: MVI A,7 ;Get optional user 7 console status JNZ IF3IN ;Complete data input if not BATCH selected ; ; B A T -- BATCH Mode (use READER select bits to chose USER 0-3). LDA IOBYTE RRC! RRC ;Put READER select bits in lower 2 bits ANI 3 ;To select low user # JMP IF3IN ;And get IF3 data input ; ;******************************************************** ;* READER LOGICAL DEVICE DATA INPUT ROUTINE: * ;******************************************************** ; ; Read the next character from the currently assigned ; reader device into the A register, no parity bit is stripped. ; ; IOBYTE selects device to use as follows: ; 0 = TTY:, 1 = PTP:, 2 = UP1:, 3 = UP2: ; USER 6 USER 1 USER 2 USER 3 ; ;Exit: A = character read from the reader device. ; READER: LDA IOBYTE ;Get I/O BYTE RRC! RRC ;Move select to lower two bits ANI 3 ;Mask select bits (0=TTY,1=PTR,2=UR1,3=UR2) JNZ IF3RI ;Else get USER 1-3 as reader inputs MVI A,6 ;TTY is USER 6 IF3RI: OUT IF3UX ;Select mux IF3RI2: IN IF3US ;Get TTY status ANI UDAV ;Mask data available JZ IF3RI2 ;Loop until ready IN IF3UD ;Get data RET ; ;******************************************************** ;* SEND CHARACTER TO PUNCH OUTPUT LOGICAL DEVICE: * ;******************************************************** ; ; Send a character (8 bits) to the selected punch device. ; ; IOBYTE selects device to use as follows: ; 0 = TTY:, 1 = PTP:, 2 = UP1:, 3 = UP2: ; USER 6 USER 1 USER 2 USER 3 ; ;Entry: C = ASCII character or byte to output. ; PUNCH: LDA IOBYTE ;Get I/O BYTE ANI 00$11$00$00b ;Mask out punch device select bits JZ TTYOUT ;Use TTY if zero PUNCH2: RRC! RRC! RRC! RRC ;Move select to lower two bits ANI 3 ;Mask select bits JMP IF3OUT ;Select USER for Interfacer 3,4 ; ;**************************************** ;* CONSOLE DATA OUTPUT ROUTINE: * ;**************************************** ; ; Send a character to the console. If the console is not ready ; to output a character, wait until it is, then do transmission. ; ; IOBYTE selects device to use as follows: ; 0 = TTY:, 1 = CRT:, 2 = BAT:, 3 = UC1: ; USER 6 xxx xxx USER 7 ;----- If CRT, secondary select done using IOCNTL byte: ; 0 = USER 0, 1 = SysSup 1, 2 = IF1-P0, 3 = Custom. ;----- If BAT, secondary select done using PUNCH of IOBYTE: ; 0 = USER 0 1 = USER 1, 2 = USER 2, 3 = USER 3 ; ;Entry: C = ASCII character to output to console. ; CONOUT: LDA IOBYTE ;Get I/O Byte (0=TTY,1=CRT,2=BAT,3=UC1) ANI 3 ;Mask select bits JNZ CONOUT1 ;If not TTY, check for others ; ; T T Y -- Used by all six logical device vectors. TTYOUT: MVI A,6 ;TTY is always Interfacer 3,4 USER 6 IF3OUT: OUT IF3UX ;Select mux IF3OUT2:IN IF3US ;Get TTY status ANI IF3TMSK ;Mask TBE, DSR bits XRI IF3FMSK ;Flip status of both JNZ IF3OUT2 ;Loop until ready MOV A,C OUT IF3UD ;Xmit data RET ; CONOUT1:DCR A ;See if CRT JNZ CONOUT2 ;Try UC1 or BATCH if not ; ; C R T -- Video Display Terminal. CRTOUT: LDA IOCNTL ;Get I/O control select byte ANI 00$0000$11b ;Check on CRT select bits JZ IF3OUT ;If zero, CRT is Interfacer 3,4 USER 0 DCR A ;See if System Support I UART JNZ CRTOUT2 ;Try devices 2,3 if not CRTOUT1:IN SS1US ;Get console status XRI SS1FMSK ;Flip TBE status bit ANI SS1TMSK ;Mask data xmit flags JNZ CRTOUT1 ;Loop if xmit buffer not empty MOV A,C ;Get char to xmit OUT SS1UD ;Send it on it's way RET ; CRTOUT2:DCR A ;See if device 3 selected JNZ CRTOUT3 ;Use custom CRT routine if so CRTO2LP:IN IF1US0 ;Switch selects Interfacer I,II ANI IF1TMSK ;Mask data xmit flags XRI IF1FMSK ;Flip TBE status bit JNZ CRTO2LP ;Loop if xmit buffer not empty MOV A,C ;Get char to xmit OUT IF1UD0 ;Send it on it's way RET ; ; Custom CRT routine, initially set for Interfacer I,II UART 1. CRTOUT3:IN IF1US1 ;BUILD YOUR OWN CUSTOM CRT OUTPUT ROUTINE ANI IF1TMSK ;Mask data xmit flags XRI IF1FMSK ;Flip TBE status bit JNZ CRTOUT3 ;Loop if xmit buffer not empty MOV A,C ;Get char to xmit OUT IF1UD1 ;Send it on it's way RET ; ; Check for other console devices. CONOUT2:DCR A ;See if UC1 selected ; ; U C 1 -- Optional user console device. UC1CO: MVI A,7 ;Get USER 7 console status if UC1 selected JNZ IF3OUT ;Complete data input if so ; ; B A T -- BATCH Mode (use PUNCH select bits to chose USER 0-3). LDA IOBYTE ;Get I/O byte value again for PUNCH select JMP PUNCH2 ;And use their value to select the USER number ; ;******************************************************** ;* OUTPUT CHARACTER TO LIST LOGICAL DEVICE: * ;******************************************************** ; ; Send a character to the list device. If the list device is not ; ready to receive a character wait until the device is ready. ; ; IOBYTE selects device to use as follows: ; 0 = TTY:, 1 = CRT:, 2 = LPT:, 3 = UL1: ; USER 6 xxx xxx USER 5. ;----- If CRT, secondary select done using IOCNTL byte: ; 0 = USER 0, 1 = SysSup 1, 2 = IF1-P0, 3 = Custom. ;----- If LPT, secondary select done using IOCNTL byte: ; 0 = USER 4 1 = IF1-P1, 2,3 = Custom. ; ;Entry: C = ASCII character to be output. ; LISTOUT:LDA IOBYTE ;Get IOBYTE status RLC! RLC ;Move select to lower two bits ANI 3 ;Mask out select bits JZ TTYOUT ;Use TTY if zero DCR A ;See if CRT output JZ CRTOUT ;Use it if so DCR A ;See if line printer JNZ UL1OUT ;User list device if not ; ; L P T -- Line printer list output. LDA IOCNTL ;Get I/O control switch byte ANI 11$0000$00b ;Mask out LPT control bits JM LPTCUST ;If custom, use routine JZ LPTUSR4 ;If control value was zero, USER 4 ; LPTIF1: IN IF1US1 ;Get status ANI IF1TMSK ;Mask ready bits XRI IF1FMSK ;Flip TBE bit JNZ LPTIF1 ;Loop until ready MOV A,C ;Get char OUT IF1UD1 ;Xmit it RET ; LPTUSR4:MVI A,4 ;USER 4 as list device if so OUT IF3UX ;Select mux in Interfacer 3,4 LDA IOCNTL ;Get I/O control byte ADD A! ADD A ;Shift left 2 bits to mask USER 4 routine JMP LSTSPCL ;Check for special list protocol ; ; U L 1 -- User device list output. UL1OUT: MVI A,5 ;USER 5 is special list device OUT IF3UX ;Select mux in Interfacer 3,4 LDA IOCNTL ;Get I/O control byte LSTSPCL:ANI 00$11$00$00b ;Mask out USER 5 output routine select bits JZ IF3OUT2 ;Normal transmit if zero ADD A! ADD A ;Shift left 2 bits JM ETX$ACK ;Use ETX/ACK protocol if higher bit set ; ; XON/XOFF (DC1/DC3) software protocol in use. DC1$DC3:IN IF3US ;Get status ANI UDAV JZ IF3OUT2 ;List if no char ready on input IN IF3UD ;Get character ANI 7FH ;Strip out parity bit CPI XOFF ;^S? (XOFF) JNZ IF3OUT2 ;Continue if not DC1X2: IN IF3US ;Get status ANI UDAV JZ DC1X2 ;Wait for data from printer IN IF3UD ;Get it ANI 7FH CPI XON ;^Q? (XON) JNZ DC1X2 ;Loop until found JMP IF3OUT2 ;Xmit data using usual Interfacer 3,4 output ; ; --- Footnote #4. --- ; ; --- ETX/ACK software protocol in use. ETX$ACK:MOV A,C ;Get character to transmit CPI LF ;See if line feed (always last character if in ;an ESCAPE sequence) JNZ IF3OUT2 ;Transmit data if not CALL IF3OUT2 ;Transmit Line Feed and return for more MVI C,ETX ;Show end of line (End of Transmission, ETX) CALL IF3OUT2 ;Transmit and return to wait for ACK ACKX2: IN IF3US ;Get status ANI UDAV JZ ACKX2 ;Wait for data from printer IN IF3UD ;Get it ANI 7FH ;Strip out parity bit CPI ACK ;See if ACKnowledge of buffer empty JNZ ACKX2 ;Loop until found RET ; ; Custom list routine for LPT selection. LPTCUST:IN IF1US2 ;Get status ANI IF1TMSK ;Mask ready bits XRI IF1FMSK ;Flip TBE bit JNZ LPTCUST ;Loop until ready MOV A,C ;Get char OUT IF1UD2 ;Xmit it RET ; ;******************************************************** ;* LIST DEVICE OUTPUT READY STATUS ROUTINE: * ;******************************************************** ; ; Return the ready status for the assigned list device. ; ; IOBYTE selects device to use as follows: ; 0 = TTY:, 1 = CRT:, 2 = LPT:, 3 = UL1: ; USER 6 xxx xxx USER 5. ;----- If CRT, secondary select done using IOCNTL byte: ; 0 = USER 0, 1 = SysSup 1, 2 = IF1-P0, 3 = Custom. ;----- If LPT, secondary select done using IOCNTL byte: ; 0 = USER 4 1 = IF1-P1, 2,3 = Custom. ; ;Exit: A = 0 (zero), list device is not ready. ; A = FFh (255), list device is ready. ; LISTST: LDA IOBYTE ;Get IOBYTE status RLC ;if LPT or UL1, carry set JC LPTLOS ;See which if either selected RLC ;if CRT, carry is set, else TTY MVI A,6 ;TTY select USER 6 on Interfacer 3,4 JNC IF3LOS ; ; C R T -- List status. CRTLOS: LDA IOCNTL ;Get I/O control select byte ANI 00$0000$11b ;Check on CRT select bits JZ IF3LOS ;If zero, CRT is Interfacer 3,4 USER 0 DCR A ;See if System Support I UART JNZ CRTLOS2 ;Try devices 2,3 if not IN SS1US ;Get System Support I console status XRI SS1FMSK ;Flip selected status values ANI SS1TMSK ;Mask ready bit(s) (zero bit set if ready) DCR A ;Make previous zero into 0FFh RNZ ;If ready XRA A ;Show not ready RET ; CRTLOS2:DCR A ;See if Interfacer I or II, UART 0 MVI A,0FFh ;Show ready on custom routine if not RNZ ;Use custom routine if not IN IF1US0 ;alternate if Interfacer I or II selected JMP IF1LOS ;Use common Interfacer I output status ; LPTLOS: RLC ;carry set if UL1, reset for LPT MVI A,5 ;USER 5 for UL1 on Interfacer 3,4 JC IF3LOS ; user list device selected ; ; L P T -- Line printer list status. LDA IOCNTL ;Get I/O control switch byte ANI 11$0000$00b ;Mask out LPT control bits JM CRTLOS2 ;If custom, show ready MVI A,4 ;USER 4 of Interfacer 3,4 JZ IF3LOS ;if control value was zero ; IN IF1US1 ;Get alternate status port IF1LOS: XRI IF1FMSK ;Flip TBE bit ANI IF1TMSK ;Mask ready bits RZ ;Done if not ready ORI 0FFh ;Show ready RET ; ; U L 1 -- User device list status. IF3LOS: OUT IF3UX ;Select mux in Interfacer 3,4 IN IF3US ;Get status XRI IF3TMSK ;Flip status of both ANI IF3FMSK ;Mask TBE, DSR bits (zero bit set if ready) DCR A! RNZ ;Make previous zero into 0FFh, if ready XRA A ;Show not ready RET ; PAGE REUSE SET $ ;Nominal Start of re-usable space ;************************************************ ;* COLD BOOT INITIALIZATION ROUTINES * ;************************************************ ; if XMBOOT ;If memory disk used for "re-boot", then re-use space BOOTCPM ;Boot CP/M from disk, exit only if load sucessful. endif ; CBOOT: LXI SP,100h ;Temp stack for cold boot ; ; "Size" the M-Drive/H by counting active boards in use. if (HMDRIVE and HMSIZER) LXI H,-((512-4)/2) ;Init the disk size (blocks 2K) LXI B,30F8h ;"B" = "0", "C" = board count-1 HMDTEST:MOV A,B! STA HM$MSG ;Put ASCII board count in Sign-on INR B ;Bump count for next loop LXI D,(512-4)/2 ;Size of each board in 2K blocks DAD D ;Add in more blocks for new board MOV A,C! ADI 8 ;Bump sector count by 8 (1 board) STA DPBHMD! MOV C,A ;Save sector count in Disk Parameter Block CPI 64 ;See if maximum count reached JZ HMDZEND ;Done if so PUSH H ;Save the current disk size in blocks CALL HMDTRD ;Read a sector into the directory buffer XRA A ;Clear command (for write) CALL HMDTWR ;Write it back out again CALL HMDTRD ;Read back, should now report no errors POP H ;Recover current disk size JZ HMDTEST ;Test for next board if no errors MOV A,C! CPI 0 ;See if no boards present JZ HMDZERO ;If no boards zero dskmsk CPI 31 ;See if 2 megabytes or more JNC HMD4K ;Set DSM only if so, rest of DPB is set CPI 8 ;See if only 512K JNZ HMD1 ;If not 512k, don't change EXM XRA A! STA DPBHMD+4 ;Set EXM to 0 if only one board HMD1: XCHG ;Save DSM in "D,E" LXI H,128-1 SHLD DPBHMD+7 ;Set directory count to 128 MVI A,0C0H STA DPBHMD+9 ;Set number of reserved blocks to match XCHG ;Put DSM back in "H,L" HMD4K: DCX H! SHLD DPBHMD+5 ;Save corrected disk size allocation JMP HMDZEND ;Skip no m-drive section HMDZERO: XRA A ;Want 0 in the dskmsk table if XMDRIVE ;If extended memory drive present if (DRVH eq 0) ; then M-DRIVE/H is H:, if nothing else there STA DSKTBL+('H'-'A')*2 !STA DSKTBL+('H'-'A')*2+1 else ;If H: is used, it is N: STA DSKTBL+('N'-'A')*2 !STA DSKTBL+('N'-'A')*2+1 endif else ;If no other mdrive, M: is used STA DSKTBL+('M'-'A')*2 !STA DSKTBL+('M'-'A')*2+1 endif HMDZEND: ;Was done if all 8 boards present endif ; ; Print the corrected sign-on message to the default console. LXI H,SIGNON ;Show outside world that we're alive CALL PRINT ;Output Banner ; Set up 8088 for moving data. if I8088 MVI A,0EAh ;8088 jump instruction STA 400h ;Place jump at 400h LXI H,LOOP88 ;to block move routine SHLD 401h LXI H,0 SHLD 403h IN SWAPP ;Execute jump to LOP88 else LXI H,0 endif SHLD LOADVEC ;Make cold boot a warm boot, showing no overlay ; if DISK3 CALL D3INIT ;Initialize the DISK3 controller endif ;(before a boot if done there) ; ; Perform the cold boot from disk operation to bring in CP/M. CALL LOADCPM ;Get CP/M into memory ; ; If "extended memory" drive is used for warm boots, save CP/M image there. if XMBOOT IN SWAPP ;Perform block move using 8088 to save CP/M endif ;in high memory (locations preset) ; ; Load up the sector relocation tables for the hard disk systems. if DISK2 ;Hard disk unit active (#0) LXI H,RELTBL0 ;Point to relocation table storage MVI C,D2UNIT0 ;Get Unit #0 ID CALL D2INIT ;Read in table to initialize memory if DISK2X ;Second unit active LXI H,RELTBL1 ;Point to relocation table storage MVI C,D2UNIT1 ;Get Unit #1 ID CALL D2INIT ;Read in table to initialize memory endif if DISK2Y ;Third unit active LXI H,RELTBL2 ;Point to relocation table storage MVI C,D2UNIT2 ;Get Unit #2 ID CALL D2INIT ;Read in table to initialize memory endif if DISK2Z ;Fourth unit active LXI H,RELTBL3 ;Point to relocation table storage MVI C,D2UNIT3 ;Get Unit #3 ID CALL D2INIT ;Read in table to initialize memory endif endif ; ; "Size" the extended memory drive which uses main memory and the 8088. if XMDRIVE LXI H,TEMPBF ;Read the first word into temporary LXI D,0 ;Use base of each memory block (4K): D,E=0 LXI B,0100h ;1 word xfer ("B") and compare with 0 ("C") MVI A,16 ;Look for beginning page (start at 16*4K) LOOK: CALL LOOK1 ;Get byte at first location and put "C" byte CALL LOOK1 ;Put original back and get back stored value INR A ;Point to next 4K block of memory JZ MEMMAX ;Done if at maximum (leave very top 4K alone) INR C! DCR C ;See if sent byte still 0 (mem bank exists) JZ LOOK ;If this page exists look for another MEMMAX: if XMBOOT SUI 19 ;Adjust page number to include boot area else SUI 17 ;Adjust page number endif MOV E,A ;Save sectors/track in D,E (# of tracks fixed) LXI H,DPBMEM ;Point to M-drive DPB MOV M,E! INX H ;Set the # of sectors/track MOV M,D! INX H XCHG! CPI 65 ;Check for different sized mdrives JNC LMDRIVE ;Most of DPB is correct if large drive (>448K) XCHG! DCR M! INX H ;Change block size to 1K (BSH) MOV A,M! ORA A ;And mask to 1K (BLM), clear carry for high bit RAR! MOV M,A ;Shift right 1 bit in mask, save again XCHG! MVI A,63 ;Change size of directory count to 64 if small STA DPBMEM+7 DAD H ;Sectors * 2 for 1K blocks LMDRIVE:DAD H ;* 2 (2K blocks per (16K/4) 4K "sector") DCX H ;less 1 for DSM (disk size in blocks) SHLD DPBMEM+5 ;Save it endif ; ; If the length of a command line (in characters) is placed at location ; CCP+7, and the command line itself is placed starting at location CCP+8, ; CP/M will execute this command line after a boot (cold or warm) and execution ; is transferred to CCP+0. If execution is transferred to CCP+3, this command ; line is ignored. Only one line (as would be seen on screen) is allowed, but ; it may be a "SUBMIT STARTUP" or whatever to perform a sequence of operations. ; ; This command line is best installed using 2nd generation SYSGENing by ; using DDT to alter the actual CCP (which, after a SYSGEN, exists at location ; 1600h for CompuPro systems). BE CAREFUL. There are some quirks that won't ; allow some commands to execute properly (and since Digital Research didn't ; document all of this, no help can expected from them or CompuPro). ; XRA A ;Always set the default disk to drive A: on STA CDISK ;cold boot entry to CP/M (or SUBMIT won't work) CALL GOCPM ;Set up vectors in Page 0 JMP CCP ;Go to CP/M and execute anything in CCP buffer ; ;----- cold boot subroutines ----- ; ; DISK2 sector relocation table initialization routine. ; ;Entry: H,L = Base address of unit's relocation table, ; C = Unit select byte (by specifying the outer logical drive). ; if DISK2 D2INIT: PUSH H ;Save DMA specification CALL SELDSK ;Set the current active drive specification POP B ;Recover DMA for setup CALL SETDMA ;Save DMA address of table MVI C,(D2$SCNT-1) * 8 ;Get relocation table from last sector on drive CALL SETSEC ;Set for first active record (8 / 1K sector) CALL HOME ;Restore pointers and register storage JMP READ ; and read sector at Cylinder 0, Head 0 endif ; ; Initialize the DISK3 controller. if DISK3 D3INIT: LXI H,D3CIOPB ;Start of normal CIOPB SHLD D3DIOPB+D3$LINK ;Put link in default IOPB SHLD D3CIOPB+D3$LINK ;Put link in normal IOPB XRA A ;Clear A STA D3DIOPB+D3$LNK3 ;Set default dma address STA D3CIOPB+D3$LNK3 ; STA D3CIOPB+D3$DMA3 ;Set extended address of DMA to 0 MVI M,D3$NOOP ! INX H ;First command is NOOP,Point to status MOV M,A ! INX H ;Clear status byte, point at drive MOV M,A ! DCX H ;Set drive to 0, point back at status MVI A,D3RESET ;Reset DISK3 controller (Select controller #1) OUT D3$PORT MVI A,D3$ATTN OUT D3$PORT ;Send attention to controller LXI B,0FFFH ;Wait only this many times incase no DISK3 D3INIT3:MOV A,M ! ORA A ;Wait until status non-zero JNZ D3INIT4 ;Exit timeout loop if status non-zero DCX B ;Count this wait loop MOV A,B ! ORA C ;See if zero reached JNZ D3INIT3 ;Try again if not timed out LXI H,NOD3 ;If timed out ... JMP PRINT ; ....print message and return ; D3INIT4:MVI C,NDISK3-1 ;Get number of active drives D3INIT1:PUSH B ;Save current drive MOV A,C ;Get current drive number STA D3$DRVS+D3CIOPB ;Set current drive number MVI A,D3$HOME ;Home drive first thing CALL D3EXEC ;Execute home command LXI H,100H ;Read specify sector into TPA SHLD D3$DMA+D3CIOPB ;Set as dma address in DISK3 iopb LXI H,D3CIOPB+3 ;Get start of DISK3 arguments XRA A ;Clear A MVI M,1 ! INX H ;Set as read command MOV M,A ! INX H ;Want to start reading at sector 0 MOV M,A ! INX H ;High byte of sector MOV M,A ! INX H ;Want to start reading at track 0 MOV M,A ! INX H ;High byte of track MVI M,2 ! INX H ;Want to read 2 sectors MOV M,A ! INX H ;High byte of sector count MVI A,D3$XFER ;Do DISK3 read/write sector command CALL D3EXEC ;Execute command MVI C,8 ;Now compare 1st 8 bytes against signon message LXI H,SIGNON ;Get start of "CompuPro" in signon LXI D,100H ;Get begining of 8 flag bytes SGNCMP: LDAX D ;Get first byte of what is on disk CMP M ;See if it matches signon JNZ NOFMT ;If no match, disk is not formatted right INX D ! INX H ;Move both pointers up DCR C ;Count this byte JNZ SGNCMP ;Do 8 bytes LXI H,100H+16 ;Point to specify block SHLD D3$DMA+D3CIOPB ;Put address of specify in iopb MVI A,D3$SPEC ;DISK3 specify command CALL D3EXEC ;Tell disk3 to perform specify LXI H,100H+1024 ;Point to start of bad map SHLD D3$DMA+D3CIOPB ;Put address of badmap in iopb MVI A,D3$MAP ;Set badmap command CALL D3EXEC ;Execute command to set the relocation map D3INIT2:POP B DCR C ;Count this drive JP D3INIT1 ;Do for all drives LXI H,D3CIOPB+3 ;Point at ARG1 MVI M,0 ! INX H ;Use mode 0 MVI M,MRTRY ! INX H ;Set number of retries MVI M,4 ! INX H ;Max of 4 drives on one disk3 MVI A,D3$GLBL ;Disk3 set globals command JMP D3EXEC ;Execute globals command and return with status ; NOFMT: POP B ! PUSH B ;Recover/save drive number MOV A,C ! ADI '0' ;Add ascii bias to drive number STA FMTDRV ;Set ascii drive letter in message LXI H,FMTMSG ;Show not formatted message CALL PRINT JMP D3INIT2 ;Go do any remaining drives ; FMTMSG: DB 'Hard disk unit ' FMTDRV: DB 'X not formatted properly.',CR,LF,NULL NOD3: DB 'No DISK3.',CR,LF,NULL endif ; ; Routine to get a word from high memory, exchange the first byte with ; the contents of register "C", and return the word to high memory. "C" is ; usually 00h on first call to this routine, and its storage in high memory ; is checked by the second call. If storage exists at this high block, the ; second call will return with "C" still containing a 0; if not the S-100 ; (IEEE 696) bus will return with 0FFh, indicating where the top of active ; memory is. ; if XMDRIVE LOOK1: PUSH PSW CALL OUTOF ;Read word from external XCHG ;Exchange source, dest (fix) MOV A,M ;Get first byte from external MOV M,C ;Set up byte to send back to external MOV C,A ;Put recovered byte in "C" POP PSW! PUSH PSW ;Recover outside extended address CALL INTO ;Send "fixed" word back to external POP PSW ;Recover outside extended address RET endif ; ; Similar routine to size the number of boards belonging to an M-Drive/H. if (HMDRIVE and HMSIZER) HMDTRD: MVI A,FD$RDAT ;Floppy "read data" command works for M-Drive HMDTWR: STA CIOPB ;Store command for Read/Write process LXI H,DIRBUF ;Use the directory buffer area for storage PUSH B ;Save sector, message value MOV A,C ;Sector to test LXI B,4 ;Actual track number for parity LXI D,4*128 ;"Translated" track number 4 (directory) CALL HMD$FNL ;Perform Read/Write operation POP B ;Recover sector and the ASCII board number RET ;Return with parity error status endif ; PAGE ; Sign on message -- must be terminated by a "NULL" byte. SIGNON: DB 'CompuPro Systems ' DB MSIZE/10+'0',MSIZE mod 10 + '0' DB 'K CP/M ' DB VERS/10+'0','.',VERS mod 10 + '0' DB CBIOSV ;Cbios version letter if I8088 DB 'x' ;Show 8088 code active endif if Z80 db 'Z' ;Show Z80 code active endif if XMDRIVE DB 'M' ;Show M-DRIVE using extended memory and 8088 included endif DB CR,LF ;New line for other specifications if FLOPPY5 DB '5" Disk1' ;Show 5 1/4 inch drives included if (HMDRIVE or DISK2 or DISK3) DB ', ' ;Separator endif endif if HMDRIVE HM$MSG: DB HM$NUM+'0',' M-Drive/H' ;Show active M-DRIVE/H boards included if (DISK2 or DISK3) DB ', ' ;Separator endif endif if DISK2 DB ' Disk2/' ;Hard disk (Disk 2) separator ID if D2M10 DB 'M10' ;Show Hard Disk with 10 Mbytes included endif if D2M20 DB 'M20' ;Show Hard Disk with 20 Mbytes included endif if D2F20B DB 'F20' ;Show Hard Disk with 20 Mbytes included endif if D2M26 DB 'M26' ;Show Hard Disk with 26 Mbytes included endif if D2F40B DB 'F40' ;Show Hard Disk with 40 Mbytes included endif if DISK3 DB ', ' ;Separator endif endif if DISK3 DB ' Disk3/' ;Hard disk (Disk 3) separator ID if D3ST506 DB 'M5' ;Show Hard Disk with 5 Mbytes included endif if D3Q540 DB 'Q540' ;Show hard Disk with 40 Mbytes included endif endif DB NULL ;End of sign-on message ; ENDCODE EQU $ ;End of active code ;-------------------------------- if (BOOTSEC * 128) lt (ENDCODE - BIOS) ;If disk storage area insufficeint --:ERROR:-- Assembly time BOOT SPACE SHORTAGE SPACE$OVER EQU (ENDCODE - BIOS)-(BOOTSEC * 128) else SPACE$LEFT EQU (BOOTSEC * 128)-(ENDCODE - BIOS) ;Amount of boot space left endif ; ;******************************** ;* CP/M DISK WORK SPACE: * ;******************************** ORG REUSE ;Space recovered from code used only at cold boot time ; ; Allocation vectors (reserved space). IRPC ?Y,ABCDEFGHIJKLMNOP ALV$&?Y DS ALVZ&?Y ;;Specific drive allocation vector size ENDM ; ; Checksum vectors (reserved space for disk change identification). IRPC ?Z,ABCDEFGHIJKLMNOP CSV$&?Z DS CSVZ&?Z ;;Specific drive checksum vector size ENDM ; ;------------------------------- if DISK2 ;Disk 2 controller storage containing RELTBL0 DS D2$SCNT * 3 ;Sector relocation tables for respective units if DISK2X RELTBL1 DS D2$SCNT * 3 ;Unit 1 endif if DISK2Y RELTBL2 DS D2$SCNT * 3 ;Unit 2 endif if DISK2Z RELTBL3 DS D2$SCNT * 3 ;Unit 3 endif endif ENDWORK EQU $ ;End of workspace below block buffers ;------------------------------- ORG 0FB00h ;Place directory, host buffer near final block of RAM ; if (ENDWORK gt $) ;Overflow of storage space requirement --:ERROR:-- Assembly time STORAGE SPACE SHORTAGE NEED$SPACE EQU (ENDWORK - $) else SPARE$RAM EQU ($ - ENDWORK) endif ; HSTBUF DS HSTSIZ ;Host buffer (sector blocking/deblocking) DIRBUF DS 128 ;Directory buffer ; ; Disk access information. ; This area is organized into the following groups: ; 1) Sector number. ; 2,3) Track number. ; 4) Disk drive. ; 5) Drive type. ; Each of these groups has three cells for the current disk request, ; ACTual disk transfer, and active host disk. ; SAVSEC DS 2 ;Special storage for record in use NUMSEC DS 1 ;Number of sectors ; ; User (desired, seek to) Data Buffer Address, Parameters. ; DMAADR DS 2 ;Lower 16 bits (least, middle) DMAADE DS 1 ;Extended address ; SEKSEC DS 1 ;Current sector request SEKGPL DS 1 ;Current sector gap length SEKTRK DS 2 ;Current track request SEKDSK DS 1 ;Current disk number request SEKTYP DS 1 ;Current disk's type ; ; (Active) Physical Data Buffer Address, Parameters. ; BUFADR DS 2 ;Lower 16 bits (least, middle) BUFADE DS 1 ;Extended address ; ACTSEC DS 1 ;Actual transfer operation ACTGPL DS 1 ;Actual sector gap length used ACTTRK DS 2 ;Actual transfer operation ACTDSK DS 1 ;Actual transfer operation ACTTYP DS 1 ;Actual disk's type ; ; Host Disk Physical Parameters. ; HSTSEC DS 1 ;Active host disk HSTGPL DS 1 ;Host sector gap length HSTTRK DS 2 ;Active host disk HSTDSK DS 1 ;Active host disk HSTTYP DS 1 ;Active disk's type ; ; Floppy Disk "Command Input/Output Parameter Block" for controller ops. ; CIOPL EQU 9 CIOPB DS CIOPL ;Disk command buffer TEMPBF DS 8 ;Result status cells ;------------------------------- if DISK3 ; Command, Status, Drive, Read/Write flag D3CIOPB:DS 4 DS 2 ;Sector DS 2 ;Track DS 2 ;Number of sectors to xfer DS 2 ;Data DMA address DS 1 ;In current bank DS 2 ;Link returns to start of this IOPB DS 1 ;In current bank as well endif ; ORG ENDCODE ;Show end of booted code address to user END ;Das Ende