; ;IO 10/20/82 ;************************************************************* ; CDISK EQU 4 ; global constant BASE EQU $ ; " " ; MACLIB Z80 ; $-M ; ; IF NOT MMPM ; IF NOT XMLDR ; ; ; THIS PROGRAM IS LOADED AT LOCATION ; 0 BY THE BOOTSTRAP PROGRAM, AND RUN. ; IT'S PURPOSE IS TO LOAD AND RUN THE MAIN ; CP/M DISK OPERATING SYSTEM AT THE TOP OF ; THE MEMORY IN USE. ;************************************************************* ; BPROM EQU 0F800H ;DELTA PRODUCTS MONITOR BDCOM EQU 0F8H BDSTAT EQU 0F8H BTRACK EQU 0F9H BSECT EQU 0FAH BDDATA EQU 0FBH BDDENS EQU 0FDH BWAIT EQU 0FCH BSELECT EQU 0FCH SEEKCMD EQU 0001$0000B HEADLOAD EQU 0000$1000B READCMD EQU 1000$1000B SEEKRATE EQU 0000$0011B ; ;************************************************************* ; ORG BASE ; ; LXI SP,100H LXI H,BRLOOP ; MUST NOT BE DISTURBED MVI C,2 ; C IS SECTOR COUNTER LDED LOADADR ; WHERE TO BEGIN LOADING THIS SYSTEM EXX ; ALTERNATE PAIR LHLD SECTTK0 ; GET SECT TRK 0 IN L, SECT TRK 1 IN H LDED BSECTS ; TOTAL SECTS TO READ IN E, CODE IN D LXI B,0080H ; B = TRACK C IS A MASK EXX RBLK: MOV A,C ; C SERVES TO SET SINGLE DENSITY OUT BDDENS ; SELECT SINGLE DENSITY XRA A ; SELECT TRACK 0 OUT BDDATA ; TRACK TO SEEK BSEEK: MVI A,SEEKCMD + HEADLOAD + SEEKRATE OUT BDCOM ; SEND COMMAND IN WAIT ; BREAD: MOV A,C ; NOW C IS SECTOR # OUT BSECT ; MVI A,READCMD ; OUT BDCOM ; ; ; HL MAINTAINS LOOP, DE = DMA, B = SECT BRLOOP: IN WAIT ; THIS SPECIAL PORT WILL HANG CPU ORA A ; UNTIL DATA IS AVAILABLE JP BRDONE ; HARDWARE SET MSB WHEN INT. IS VALID ; IN BDDATA STAX D ; D IS DMA ADDRESS INX D ; POINT TO NEW PCHL ; HL POINTS TO RLOOP ; BRDONE: IN BDSTAT ORA A ; IMPORTANT WE CLEAR FLAGS JNZ ERROR ; ; EXX ; ALTERNATE DATA DCR E ; TOTAL SECTORS WERE IN E JZ ENTERSYS ; ALL SECTORS READ, GO TO O.S. DCR L ; L = SECT TRK 0, H = SECT TRK 1 JZ NXTTRK ; FIRST TRK DONE EXX ; INR C ; POINT TO NEXT SECTOR JMP BREAD ; GET NEXT SECTOR ; NXTTRK: ; NOTE: ALTERNATE REGS ARE IN! MOV A,D ; SUI 0DDH ; JZ SSDD ; MOV A,C ; 80H ANA D ; D HAS CODE BYTE AND MSB SET = DBL SIDED RLC ; ROTATE TO TEST BIT 8 JNC SSDD ; MSB OF 'D' NOT SET DCR B ; ONLY GO THROUGH HERE ONCE RLC!RLC ; ROTATE WITHOUT HOLDING BIT SSDD: CMA ; INVERTING BUFFERS SO CMD MUST UPSIDEDOWN OUT BSELECT ; TRACK NO. IS LOW ENUF THAT STILL ON DRIVE 0 MOV D,L ; CLEAR D INR B ; MAYBE 0, 1 OR 2 MOV A,B ; MAYBE FF, 0, OR 1 OUT BDDATA ; TRACK REGISTER MOV B,A ; WHATEVER IT IS, SAVE IT MOV L,H ; EXX ; XRA A ; INR A ; MOV C,A ; RESET SECTOR COUNT TO 1 OUT BDDENS ; SELECT DOUBLE DENSITY, REDUNDANT SOMETIMES JMP BSEEK ; SEEK TO NEW TRACK ; ERROR: MOV B,A ; ERROR CODE IS IN A LXI H,0F803H ; RENTER PUSH H ; RETURN ADR JMP 0F812H ; REPORT ERROR ENTERSYS: INR A ; REG JUST HAPPENS TO BE EMPTY OUT 9 ; TURN OFF PROM, A JUST HAPPENS TO BE 0 DB 0C3H ; JMP INSTRUCTION ; ; *** FLAGS *** ; NOTE: this program must be full of data ; ; from 0000h to 7fh for hexloader in ORG 78H ; DSYSGEN to work properly, if you shorten ; ; something, fill with nop's IF MLDR ENTRYADR: DW 100H ; ENTER SYSTEM AT THIS ADDRESS LOADADR: DW 100H ; ELSE ENTRYADR: DW CBOOTE LOADADR: DW CCP ; LOAD SYSTEM AT THIS ADDRESS ENDIF ; SECTTK0: DB 25 ; MAX SECTORS TO READ TRACK 0 SECTTK1: DB 51 ; MAX SECTORS TO READ TRACK 1 ; BSECTS: DB 76 ; TOTAL NUMBER OF SECTORS TO READ DFLAG: DB 10H ; DOUBLE DENSITY FLAG ; ******************************************************************* ENDIF ; if xmldr ; BSPCHR EQU 08H ;STANDARD BACKSPACE CHARACTER PROM EQU 0F800H ;DELTA PRODUCTS PROM PROMSELECT EQU 9 ; prom control port ; CSTAT EQU 3 ;CONSOLE STATUS PORT CDATA EQU 2 ;CONSOLE DATA PORT ; LSTAT EQU 1 ;LIST STATUS PORT LDSR EQU 80H ;LIST PORT DATA SET READY BIT LDATA EQU 0 ;LIST DATA PORT ; BIAS EQU (MSIZE-20)*1024 ;BIAS FOR LARGER THAN 17K. CCP EQU BIAS+3400H ;START OF CPM. BDOS EQU CCP+806H ;BASE OF BDOS BIOS EQU CCP+1600H ;BASE OF BIOS ORG BIOS CPML EQU $-CCP ;LENGTH OF CPM SYSTEM-BIOS. NSECTS EQU (CPML/128+1) AND 0FEH ;NUMBER OF SECTORS IN CPM ; ******************************************************************* ENDIF ; if not mmpm IF MLDR OR XMLDR ORG 1700H BOOT: ; dummy address to eliminate assembly WBOOT: ; errors ENDIF IF MMPM ORG BASE ENDIF ; ; I/O JUMP VECTOR ; THIS IS WHERE CPM CALLS WHENEVER IT NEEDS ; TO DO ANY INPUT/OUTPUT OPERATION. ; USER PROGRAMS MAY USE THESE ENTRY POINTS ; ALSO, BUT NOTE THAT THE LOCATION OF THIS ; VECTOR CHANGES WITH THE MEMORY SIZE. ; IF MMPM JMP COMMONBASE JMP WARMSTART ELSE CBOOTE: JMP BOOT ;FROM COLD START LOADER. WBOOTE: JMP WBOOT ENDIF JMP CONST JMP CONIN IF MMPM JMP CONOUT ELSE COUTE: JMP CONOUT ENDIF JMP LIST JMP PUNCH JMP READER JMP HOME JMP SELDSK JMP SETTRK JMP SETSEC ;SET SECTOR NUMBER. JMP SETDMA ;SET DISK STARTING ADR. WREAD: JMP SREAD ;READ SELECTED RECORD JMP SWRITE ;WRITE SELECTED RECORD IF NOT MMPM JMP LISTST ;RETURN LIST STATUS JMP SECTRAN ;SECTOR TRANSLATE PFLG: DB 0 ;PRINTER SELECTION BYTE SKRATE: DB SEKRATE ;SEEK RATE ELSE JMP POLLPT0 ; LIST STATUS JMP SECTRAN ; SECTOR TRANSLATE JMP SELMEMORY ; SELECT MEMORY JMP POLLDEVICE ; POLL DEVICE JMP STARTCLOCK ; START CLOCK JMP STOPCLOCK ; STOP CLOCK JMP EXITREGION ; EXIT REGION JMP MAXCONSOLE ; MAXIMUM CONSOLE NUMBER JMP SYSTEMINIT ; SYSTEM INITIALIZATION DB 0 ; NO IDLE PROCEEDURE ; SKRATE: DB SEKRATE ; SEEK RATE NSECTS EQU 0FFH ; we don't do re-boots under m/pm CCP EQU 0FFH ; load adr for mldr PUNCH: ; READER: ; XRA A ; RET ; ENDIF ; ; IF NOT MMPM ; ;************************************************************* ;***************** BEGINNING OF I/O ROUTINES ***************** ;************************************************************* ; ; ; this routine call the prom jump entry pointed to by HL ; interrupts are disabled . ; IF DPROM ENTERPROM: DI SHLD CALLPROM ; this is a pchl type call DB 11$101$101B ; z80 store sp direct DB 01$110$011B ; second half DW TEMPSTACK ; stack LXI SP,PROMSTACK ; 8 levels for prom PUSH PSW ; save anything in A XRA A ; turn on prom OUT PROMSELECT ; write to control port POP PSW CALL $-$ CALLPROM EQU $-2 PUSH PSW ; save anything in A MVI A,1 ; remove prom OUT PROMSELECT ; write to control port POP PSW DB 11$101$101B ; load stack pointer direct DB 01$111$011B ; second half DW TEMPSTACK ; stack RET TEMPSTACK: DW 0 DS 16 ; 8 levels PROMSTACK: Promadr: Constatadr: dw Prom + 6 ; constat Coninadr: dw Prom + 9 ; conin Conoutadr: dw Prom +0Ch ; conout Pversion: db 0 ; prom version ; ENDIF ; ; CHECK CONSOLE INPUT STATUS. ; ; CONST: IF DPROM PUSH H LHLD Constatadr CALL ENTERPROM POP H RET ; ELSE ENDIF IF NOT DPROM IN CSTAT ANI 2 RZ ;ACC=0 = NO DATA CMA RET ENDIF ; ; READ A CHARACTER FROM CONSOLE. ; READER: CONIN: IF DBLOCK LDA HSTACT ; ORA A ; JZ CONIN1 ; XRA A ; STA HSTACT ; LDA HSTWRT ; ORA A ; MVI A,0 ; STA HSTWRT ; STA UNACNT ; JZ CONIN1 ; LDA NEWDISK ; STA UNADSK ; LDA BUFDSK ; CALL SELOK ; CALL AWRITE ; LDA UNADSK ; STA NEWDISK ; ENDIF ; ; CONIN1: IF SLIMLINE XRA A ; off motor OUT 0F8H+4 ; select port ENDIF ; IF DPROM PUSH H ; LHLD CONINADR ; LDA SHIFTLOCK ; MOV C,A ; CALL ENTERPROM ; POP H ; CPI 0FFH ; shift lock ? RNZ ; LDA SHIFTLOCK ; CMA ; STA SHIFTLOCK ; JMP CONIN ; ; SHIFTLOCK: DB 00H ; ELSE ENDIF IF NOT DPROM ; IN CSTAT ; ANI 2 ; JZ CONIN ; IN CDATA ; ANI 7FH ; RET ; ENDIF ; ; ; WRITE A CHARACTER TO THE CONSOLE DEVICE. ; PUNCH: CONOUT: MOV A,C ; ; IF DPROM PUSH H ; LHLD CONOUTADR ; CALL ENTERPROM ; POP H ; RET ; ; ELSE ENDIF IF NOT DPROM CONOTC: IN CSTAT ; RAR ; JNC CONOTC ; MOV A,C ; OUT CDATA ; RET ; ENDIF ; ; ;******************************************************* ;************** END OF I.O. ROUTINES ******************* ;******************************************************* ; ; ***************************************************************** ; BOOT ; The boot routine is exicuted only after a hardware reset ; is done, after the coldstart loader reads in the cp/m system ; the boot routine has been placed in the data space area to save ram ;**************************************************************** IF NOT MLDR IF NOT XMLDR BOOT: LXI SP,7CH ; ; IF SLIMLINE XRA A ; de-select drive OUT 0F8H+4 ; ENDIF ; XRA A ; OUT 9 ; on prom CALL PROM+(3*8) ; ninth table entry STA PVERSION ; prom version MVI A,1 ; turn off prom OUT 9 ; CALL BOOT1 ; go exicute rest of cold boot routine CALL CLEARCOM ; common data area to 0 ; GOCPM: MVI A,0C3H ;PUT JMP TO WBOOT STA 0 ;ADR AT ZERO. STA 5 ; LXI H,WBOOTE ; SHLD 1 ; LXI H,BDOS ;PUT JUMP TO BDOS SHLD 6 ;AT ADR 5,6,7. LXI H,80H ;SET DEFAULT DMA ADR. IF DBLOCK SHLD HSTDMA ; ELSE ; SHLD TRK ; ENDIF LDA CDISK ;PASS TO CCP IN C. MOV C,A ; JMP CCP ;JUMP TO CCP. ;****************************************************************** ; WARM-BOOT: READ ALL OF CPM BACK IN ; EXCEPT BIOS, THEN JUMP TO CCP. ;****************************************************************** ; WBOOT: IF DBLOCK LDA HSTACT ; some application pgms write to disk ORA A ; and do wmboot with out going to console JZ WBOOT1 ; (i.e. chaining operations ) LDA HSTWRT ; ORA A ; this routine flushes r/w buffer for them JZ WBOOT1 ; LDA BUFDSK ; CALL SELOK ; CALL AWRITE ; WBOOT1: LXI SP,80H ; LXI H,TEMP1 ; blocking storage LXI D,0 ; LXI B,18 ; CALL FILLIT ; ELSE ; LXI SP,80H ; ENDIF ; ; CALL CLEARCOM ; fill common data area to 0 LDA BOOTDISK ; warm boot from cold boot disk MOV C,A ; CALL SELDSK ; hl returns with addrs of correct table ; ; get sectors/track,and no. system tracks from drive block ; LXI D,10 ; DAD D ; SHLD INDEX1 ; LHLD $-$ ; INDEX1: EQU $-2 ; LXI D,NSECTPOS ; offset to nsects DAD D ; MOV D,M ; get nsects INX H ; point at sectk0 MOV E,M ; for hwboot INX H ; point at sectk1 MOV A,M ; STA SECTK1 ; LXI B,2 ; TRACK (B)=0, SECTOR (C)=2. LXI H,CCP ; GET STARTING ADDRESS. SHLD DMAADD ; ; ; read in ccp and bdos using prameters from drive block ; IF PRIAM or SHUGART or QUANTUM LDA BOOTDISK ; booting from hard disk ? CPI 5 ; convert to A = 0, B = 1, C = 2 D = 3 JC HWBOOT ; ENDIF ; RB1: ; MOV A,B ; select needs track STA TRK ; MOV A,C ; STA SECT ; PUSH H ; dma PUSH D ; sects PUSH B ; trk CALL SREAD ; do it RBLK1: ; POP B ; POP D ; POP H ; DCR D ; decrement sector count. JZ GOCPM ; all done when zero. PUSH D ; LXI D,128 ; bytes per sector DAD D ; SHLD DMAADD ; POP D ; INR C ; increment sector countR. DCR E ; e has sectors to read this trk JNZ RB1 ; read next sector STDWBT: ; LDA SECTK1 ; get sectors per track on tk 1 MOV E,A ; MVI C,1 ; otherwise reset sector =1 INR B ; increment track number JMP RB1 ; ; IF PRIAM OR SHUGART OR QUANTUM ; HWBOOT: PUSH D ; LHLD DMAADD ; lower dma address so that boot pgm LXI D,80H ; loads below ccp by 128 XRA A ; set zero flag so subtract will work DSBC DE ; SHLD DMAADD ; for this subtraction to work POP D ; INR D ; compensate for boot (nsects) LXI B,1 ; hard disk system begins at trk 0 JMP RB1 ; sect 1, not 2 ; ENDIF ; if priam or shugart ENDIF ; if mmpm ENDIF ; if mldr ENDIF ; if xmldr ;************************************************************ ; HOME HOME: LXI B,0 ; ;************************************************************** ; SET TRACK ; PARAMETERS; ; TRACK IN 'C' SETTRK: SBCD TRK ;SAVE TRACK NUMBER RET ;************************************************************* ; SET SECTOR ; PARAMETERS; ; SECTOR IN 'C' SETSEC: SBCD SECT RET ;RETURN FROM SETSEC. ;************************************************************ ; SET DMA ADDRESS PARAMETERS ; DMA IN BC ; ; SET DISK DMA ADDRESS. ; SETDMA: SBCD DMAADD ;PUT AT DMA ADR ADDRESS. RET ; ;************************************************************ ; ; this is a pointer to the cp/m drive referance number to lun address ; map table. DW DRIVMAP ; SECTRAN: ;TRANSLATE THE SECTOR GIVEN BY BC USING THE ;TRANSLATE TABLE GIVEN BY DE XCHG ; HL = trans table MOV A,H ; see if null table ORA L DAD B JZ TRANSEC ; no table CPI 1 ; this is our flag to tell us JZ HDTRANS ; to use our algorithm MOV L,M ; get translated sector MVI H,0 ; less than 256 sectors RET TRANSEC: INX H ; make relative to 1 RET ; IF PRIAM ; skew factor of 52 HDTRANS: MOV A,C ; get logical sector RAR ; divide by 2 JNC GOUT ADI 52 ; odd sector - add 52 GOUT: MVI H,0 MOV L,A INX H ; make relative to 1 RET ; ELSE HDTRANS: RET ENDIF ; ; ;************************************************************** ; ; DISK DRIVER SWAPPING ROUTINE ; ;************************************************************** IF NOT PRIAM IF NOT SHUGART IF NOT QUANTUM ; the default warm boot number BOOTDISK: DB 00 ; this is the drive that we will warm ; boot from ; this is the logical drive exchange ; table that exchanges logical drives ; A, B, ... F for phyical LUN numbers ; 0ffh is an invalid number ; indicating the drive does not exist ; duplicate entry at lun0 DRIVMAP: ; LUN # DRIVE # DB 00 ; A DB 01 ; B DB 02 ; C DB 03 ; D DB 04 ; E DB 0FFH ; F DB 0FFH ; G DB 0FFH ; H DB 0FFH ; I DB 0FFH ; J DB 0FFH ; K DB 0FFH ; L DB 0FFH ; M DB 0FFH ; N DB 0FFH ; O DB 0FFH ; P ENDIF ENDIF ENDIF IF PRIAM OR SHUGART OR QUANTUM ; the default warm boot number BOOTDISK: DB 00 ; this is the drive that we will warm ; boot from ; this is the logical drive exchange ; table that exchanges logical drives ; A, B, ... F for phyical LUN numbers ; 0ffh is an invalid number ; indicating the drive does not exist ; duplicate entry at lun0 DRIVMAP: ; LUN # DRIVE # DB 04 ; A hard disk DB 05 ; B hard disk DB 06 ; C hard disk DB 07 ; D hard disk DB 0FFH ; E DB 00 ; F floppy DB 01 ; G floppy DB 02 ; H DB 03 ; I DB 0FFH ; J DB 0FFH ; K DB 0FFH ; L DB 0FFH ; M DB 0FFH ; N DB 0FFH ; O DB 0FFH ; P ENDIF ; map cpm drive numbers A to P into lun address ; Enter reg C = cpm drive reference, Exit Acc = lun address MAPLUN: MVI A,15 ; new drive outside table ? CMP C MVI A,0FFH RC LXI H,DRIVMAP MVI B,0 DAD B MOV A,M ; get lun address RET ;*************************************************************** ; SELECT DISK ;PARAMETERS ; NEW DISK IN 'C' ; IF LOGGED IN BEFORE, ROUTINE WILL ; ONLY UPDATE 'NEWDISK'. DBLOCKING ; WILL MAKE THE CALL TO SELD1. ; SELD1 RESETS DENSITY IF NOT DONE ; ALREADY. ;************************************************************** ; SELDSK: IF MLDR OR XMLDR PUSH B ; CALL CLEARCOM ; common data area doesnt get init POP B ; by boot or warm boot so.... ENDIF ; DI CALL MAPLUN LXI H,0 ; error flag CPI 0FFH ; error from maplun? JNZ SELOK ; disk exists LDA BOOTDISK ; STA CDISK ; bad address EI RET SELOK: MOV C,A ; replace with lun STA NEWDISK ; remember new disk IF PRIAM or SHUGART or QUANTUM LDA OLDISK ; is old disk a flop CPI 04H ; floppy lun's are 0,1,2,3 JNC OLD$NOT$FLOP ENDIF ; save old disk (floppy) track in track table IF DUAL ;IF DUAL DRIVE, ANI 0FEH ;CLEAR OUT BIT 0. ENDIF IF PRIAM or SHUGART or QUANTUM ; this routine req because you could LDA LASTFLOP ; be on flop, go priam, come back to MOV E,A ; another flop which screws up track MVI D,0 ; register on 91 LXI H,TRTAB ; DAD D ; IN TRACK ; MOV M,A ; ; OLD$NOT$FLOP: ; is new disk a floppy ? LDA NEWDISK ; CPI 04H ; JNC NEW$NOT$FLOP ; ; move in floppy jump table overlay into cp/m jump table ; LXI D,JTOLAY ; distination (cpm jump table) LXI H,FDJT ; source (floppy jump overlay) LXI B,06H ; jump table overlay length LDIR ; z80 block move LDA NEWDISK ; ENDIF MOV C,A JMP FSELECT ; floppy version of select IF PRIAM or SHUGART or QUANTUM NEW$NOT$FLOP: ; move hard disk jump table overlay into cp/m jump table LXI D,JTOLAY ; distination (cpm jump table) LXI H,HDJT ; source (hard disk overlay) LXI B,06H ; jump table overlay length LDIR ; z80 block move ; LDA NEWDISK MOV C,A ; pass new disk in c to bldptr CALL MOVPARMS EI RET ENDIF ; BLDPTR: MOV L,C ;COMPUTE SEL. DRIVE HEADER VECTOR MVI H,0 ;AS 16 BIT QTY. DAD H ;*2 (BUILD 16-BYTE OFFSET) DAD H ;*4 DAD H ;*8 DAD H ;*16 LXI D,DPBASE ;TABLE BASE DAD D ;EFFECTIVE ADDRESS OF VECTOR RET ; MOVPARMS: CALL BLDPTR SETBPM: PUSH B PUSH H ;BUILD POINTER TO BLOCKING VALUES LXI D,10 ;IN BACK OF DISK PARAMETER BLOCKS DAD D ; MOV E,M ;POINT TO DISK PARAMETER BLOCK INX H ; MOV D,M ; XCHG ;BUILD POINTER TO LXI D,17 ;BLOCKING PARAMETERS DAD D ; LXI D,CPMSPT ;CPM SECTORS/TRACK LXI B,0BH ;BYTES TO MOVE LDIR ; POP H ; POP B ; RET ; ; CLEARCOM: LXI H,COMDATA ; LXI D,0 ; LXI B,COMLENGTH ; FILLIT: MOV M,E ; enter w/ hl > dest, e = fill char INX H ; bc block length DCX B ; MOV A,C ; ORA B ; RZ ; JMP FILLIT ; ; PMSG: MOV A,M ; get a character ORA A ; is it 0 RZ ; return MOV C,A CALL COUTE ; print INX H JMP PMSG ; IF DBLOCK SREAD: JMP BLREAD ; de-blocking read routine SWRITE: ; de-blocking write routine JMP BLWRT ; JTOLAY: AREAD: JMP READ ; this location modified by code above AWRITE: JMP WRITE ; same ELSE JTOLAY: SREAD: JMP READ ; floppy or hardisk SWRITE: JMP WRITE ; same ENDIF ; IF PRIAM or SHUGART or QUANTUM HEXOUT: JMP HEX3 HEX1: PUSH PSW RRC!RRC!RRC!RRC CALL HEX2 POP PSW HEX2: ANI 0FH ADI 90H DAA ACI 40H DAA MOV C,A JMP CONOUT HEX3: CALL HEX1 RET ENDIF ;************************************************************ ; ; END OF BASIC I.O. ROUTINES ; ;************************************************************ ;