; Disassembly of the program T0BOOT as supplied by Intel on PDS CP/M ; ; Modifications: ; ; 5 Feb 84...Added code to allow one processor to boot completely ; before allowing other processor to boot. ; 26 Feb 84...Added code to request disk semaphore before issuing ; recal commands to drives 1,2,3. This prevents processor ; B from sending recal commands when processor A is reading ; the diskette. ; This code is stored on track 0, sector 35 (cp/m sector) through ; sector 64. (ie. side 1 except the first two sectors of side 1) ; On boot, side 1 is read completely into memory starting at 8000H ; and then execution is transferred to location 8100H, the entry ; point to this program. ; It is also stored in the bubble device at the equivalent location ; and is read by the boot prom to the same memory locations. RIM MACRO DB 20H ENDM ORG 8100H FALSE EQU 0 TRUE EQU 0FFH L0001 EQU 01H SDFLAG EQU 10H CCP EQU 0DC00H CBOOT EQU 0F200H ; Ascii equates CR EQU 0DH LF EQU 0AH ESC EQU 1BH READD EQU 4 ; Read command FDCMAS EQU 0B0H ;* FDC MASTER STATUS REG */ FDCIO EQU 0B1H ;* FDC DATA I/O REG */ FDSK EQU 0FH ;* FDC SEEK COMMAND */ FDCWRT EQU 05H ;* FDC WRITE COMMAND */ FDCRD EQU 06H ;* FDC READ COMMAND */ FDCFMT EQU 0DH ;* FDC FORMAT COMMAND */ FDCSIN EQU 08H ;* FDC SENSE INTERRUPT COMMAND */ FDCSEN EQU 06H ;* FDC SENSE DRIVE STATUS COMMAND */ FDCTRM EQU 0D0H ;* FDC TERMINAL COUNT PORT */ NONDMA EQU 010H ;* FDC NON-DMA BIT RD EQU 0D0H ;* FDC READY TO BE READ FROM WRT EQU 090H ;* FDC READY TO BE WRITTEN TO DELAY EQU 040H ;* DELAY COUNT FDCRCL EQU 7 ; FDC recalibrate command DEFDSK EQU 000CH ; Address of default disk device ; as stored by boot rom BPFLAG EQU 000DH ; Address of two bytes that flag ; presence of bubble devices ; Hardware equates MAXDRV EQU 6 ; Four floppys and two bubbles MAXFLP EQU 4 ; Maximum number of floppys BUBFLG EQU 10H ; Address of bubble present flag ; 0 => bubble present KBDSTS EQU 0C2H ; Keyboard status port KBDDTA EQU 0C0H ; Keyboard data port KBDRDY EQU 20H ; Keyboard ready bit CRTRDY EQU 80H ; CRT ready bit DSKSTS EQU 0C1H ; Disk status byte DSKRDY EQU 8 ; Disk ready bit SEMI EQU 0C1H ; Semaphore input port SEMO EQU 0C3H ; Semaphore output port FLOPPY EQU 1 ; Floppy semaphore bit B0 EQU 2 ; Bubble 0 semaphore bit B1 EQU 4 ; Bubble 1 semaphore bit PRNSTS EQU 0E0H ; Printer status port PRNCTL EQU 0E1H ; Control port for printer PRNDTA EQU 0E2H ; Printer data port UARTDT EQU 90H ; Usart data port UARTST EQU UARTDT+1 ; Usart status port TXRDY EQU 01H ; Transmitter ready bit RXRDY EQU 02H ; Receiver ready bit J1 EQU 40H ; Port number for J1 multimodule J3 EQU 60H ; Port number for J3 multimodule T0BOOT: ;8100 LXI SP,USTACK ;8898H MVI A,0 OUT 0F0H ; Disable boot ROM CALL SYNCH ; Force processor B to pause ; Move flags set by boot prom to memory to be used by CP/M LDA BB0FLG ; Move bubble 0 present flag STA BPFLAG LDA BB1FLG ; Move bubble 1 present flag STA BPFLAG+1 LDA BOOTDV ; Move cold boot device number STA DEFDSK TRYAGN: MVI C,80H ; Ask permission to open a file CALL CO ;81CDH MVI C,8FH ; for write, disk number 15 CALL CO ;81CDH MVI C,0 ; file number 0 CALL CO ;81CDH L812B: ;812B ; The next few instructions probably result from the PL/M statement: ; DO WHILE VAR:=CI AND 80H = 0 ; ( ) I believe parentheses should be here. ; Otherwise 80H=0 will always be false. ; ; 5 Feb 84...Fixed code (effectively added parentheses) CALL CI ; Get reply from CRT cpu STA TEMP ; Save either 0C0H (denied) or ; 0C1H (granted) ANI 80H ; Mask lower bits JZ L812B ; Wait if this was not reply L8143: ;8143 LDA TEMP ;8899H ANI 1 ; Look at lower bit JZ TRYAGN ; Wait for permission L8153: ;8153 LHLD BOOTDV ; Get cold boot device number MOV C,L ; Ready to pass it LXI D,CPMIOP ; Point to IOPB CALL DEVIO ;84E8H LXI H,3CH ; Interrupt 7.5 vector (keyboard ; reset interrupt) MVI M,0C7H ; Plug in RST0 INX H ; Point to 3DH MVI M,0C9H ; Plug in RET LXI H,SDFLAG ; Single drive flag MVI M,TRUE ; Start with true ; LDA BB1FLG ;9002H ; LXI H,BB0FLG ;9001H ; ORA M ; Check for either bubble present ; RAR ; JC L81BD ; Jump if either present MVI C,FLOPPY ; Grab semaphore for disk drive CALL SETSEM LXI H,TEMP ; Temporary variable MVI M,1 ; Initialize to disk 1 L817A: ;817A MVI A,3 ; Loop 3 times LXI H,TEMP ;8899H CMP M JC L81BD ;81BDH LDA TEMP ; Get drive number STA RCLCMD+1 ; Store in second byte of RECAL command L8189: ;8189 CALL RECAL ; Issue RECALIBRATE command CALL RECAL ; twice LDA STABUF+1 ; Get Status Register 0 CPI 0 ; Is it zero ? JNZ L819A ; If not, jump JMP L8189 L819A: ;819A LDA STABUF+1 ; Get Status Register 0 again ANI 80H ; Look at bit 7 only CPI 0 ; Is it zero ? JZ L81A7 ; Jump if yes JMP L8189 ;8189H L81A7: ;81A7 LDA STABUF+1 ; Get Status Register 0 again ANI 10H ; Look at recal error bit CPI 0 ; Is it zero (no fault) ? JNZ L81B6 ;81B6H LXI H,SDFLAG ; Point to single drive flag MVI M,FALSE ; Set to false L81B6: ;81B6 LXI H,TEMP ; Bump loop counter and check for INR M ; overflow JNZ L817A ; Jump if no overflow L81BD: ;81BD MVI A,1 ; Reset disk semaphore OUT SEMO MVI C,1BH ; Reset 7.5 f/f, mask all but 7.5 CALL PLMSIM ;8881H CALL CBOOTE ;81CAH L81C5: ;81C5 EI HLT REBOOT: ;81C7 JMP T0BOOT ;8100H CBOOTE: ;81CA MVI C,0C0H ; Close file 0 so that other processor CALL CO ; can open it and boot JMP CBOOT ; Cold boot entry point into BIOS CO: ;81CD IN 0C2H ANI 80H JZ CO ;81CDH MOV A,C OUT 0C0H RET CI: ;81D8 IN 0C2H ANI 20H JZ CI ;81D8H IN 0C0H RET PRMSG: ;81E2 ; Print string of characters pointed to by HL, terminated by zero MOV A,M MOV C,A ANA A RZ CALL CO ;81CDH INX H JMP PRMSG ;81E2H CONIN: ;81ED JMP CI ;81D8H CONOUT: ;81F0 JMP CO ;81CDH SYNCH: ;8229 RIM ; Which processor ? RLC RNC ; Return if A, let A boot first XRA A ; Else, pause to get out of synch SYNC05: ;822D PUSH PSW XRA A SYNC10: ;822F DCR A JNZ SYNC10 ;822FH POP PSW DCR A JNZ SYNC05 ;822DH RET DB 8 MOV D,L BUBSEM: ;823B DB B0 ; Bubble 0 semaphore DB B1 ; Bubble 1 semaphore ASCII: ;823D ; Converts binary to ascii, storing result in memory. ; Inputs: DE pointer to memory for results ; C base to be used for conversion to ascii ; STACK value to be converted ; Output: Ascii equivalent of number on stack in memory LXI H,PTR+1 ;88C6H MOV M,D ; Save address for ascii values DCX H ; be plugged into error message MOV M,E DCX H MOV M,C ; Save base for ascii conversion DCX H POP D ; Get return address POP B ; Get value to be converted MOV M,B ; Save value to be converted DCX H MOV M,C ; (low byte) PUSH D ; Restore return address LXI H,INDEX ; Initialize loop counter MVI M,1 ; to 1 ASCI05: ;8251 MVI A,4 ; Do loop 4 times LXI H,INDEX ;88CBH CMP M JC ASCI15 ; Jump if loop counter bigger than 4 LXI H,ASCTMP ; Place to build each character MVI M,'0' ; Offset from '0' in ascii LHLD ASCVAL ; Get binary value to convert XCHG ; Put value in DE LHLD BASE ; Get radix for conversion MVI H,0 ; Zero upper 8 bits CALL DDIVH ; Divide DE by BASE XCHG ; Quotient to HL SHLD QUOTMP ; Store quotient XCHG ; Quotient back to DE CALL BCTDE ; Multiply BC by DE => HL LXI D,ASCVAL ; Point to value CALL MDEMHL ; ((DE))-(HL) => HL SHLD ASCDIG ; Save result (value-value/base*base) MVI A,9 CALL AMHL ; 9 - (HL) JNC ASCI10 ; Jump if numeric (0-9) LXI H,ASCTMP ; Else, alpha (A-F) MVI M,'7' ; Offset from '7' in ascii ASCI10: ;8289 LDA ASCTMP ;88CCH LXI D,ASCDIG ;88C9H CALL MDEPA ; Offset into ascii (one digit) XCHG ; Ascii char into DE MVI A,4 ; Max number of digits (hexadecimal) INX H SUB M MOV C,A MVI B,0 LHLD PTR ; Memory location for results DAD B MOV M,E LHLD QUOTMP ;88C7H SHLD ASCVAL ;88C2H LXI H,INDEX ;88CBH INR M JNZ ASCI05 ;8251H ASCI15: ;82AC LXI H,INDEX ;88CBH MVI M,0 ASCI20: ;82B1 MVI A,2 LXI H,INDEX ;88CBH CMP M JC ASCI35 ;82E3H LHLD INDEX ;88CBH MVI H,0 XCHG LHLD PTR ;88C5H DAD D MOV A,M CPI '0' ;30H JNZ ASCI25 ;82D9H LHLD INDEX ;88CBH MVI H,0 XCHG LHLD PTR ;88C5H DAD D MVI M,' ' ;20H JMP ASCI30 ;82DCH ASCI25: ;82D9 JMP ASCI35 ;82E3H ASCI30: ;82DC LXI H,INDEX ;88CBH INR M JNZ ASCI20 ;82B1H ASCI35: ;82E3 RET DERROR: ;82E4 ; Reports disk or bubble error with status bytes on console. Prompts ; user for appropriate action (retry, cancel, abort). LHLD SIOPB ; Get device number (0-5) MVI H,0 ; Make it a byte PUSH H ; Pass it on stack LXI D,DNUM ; Address of disk number in error message MVI C,10H CALL ASCII ;823DH LXI H,DERIDX ; Initialize a loop counter MVI M,1 ; with 1 DERR05: ;82F7 LDA STABUF ; Byte count of result from FDC LXI H,DERIDX ; Loop til all result bytes converted CMP M ; and stored in error message JC DERR10 ; Jump if done LHLD DERIDX ; Else, get loop counter MVI H,0 LXI B,STABUF ; Point to result buffer DAD B ; Index into result bytes MOV C,M ; Retrieve next result byte MVI B,0 PUSH B ; Push onto stack LDA DERIDX ; Get loop counter DCR A ; minus 1 RLC ; (minus 1) * 2 RLC ; (minus 1) * 4 MOV C,A MVI B,0 LXI H,STAT ; Point to status bytes in error message DAD B ; Index into point to store ascii of XCHG ; status byte MVI C,10H ; Base for conversion CALL ASCII ; Convert result bytes to ascii in message LXI H,DERIDX ; Bump loop index INR M JNZ DERR05 ; Loop if not overflowed DERR10: ;8328 RET BBDRIO: ; This routine is the bubble device driver. ; It is called after the SIOPB has been filled. LDA SIOPB ; Get disk number SUI MAXFLP ; 0 => J1 bubble, 1 => J3 bubble STA BUBBLE ; Save bubble number MOV C,A MVI B,0 LXI H,BPFLAG ; Point to bubble present flag DAD B ; Point to bubble 0 or 1 flag MOV A,M ; Retrieve flag RAR ; Is bubble present ? JC BBDR05 ; Yes, ok CALL DNRABT ; No, show drive not ready and abort BBDR05: LXI H,SIOPB ; Point to disk number LDA LASTB ; Get last bubble accessed CMP M ; Same one requested again ? JZ BBDR10 ; If yes, skip port address change LDA SIOPB ; No, get current bubble (0 or 1) STA LASTB ; Save it for next time through MOV C,A ; Get ready to pass it CALL STUFF ; Go modify port numbers in code ; to new bubble port number BBDR10: LHLD BUBBLE ; Bubble number (0 or 1) MVI H,0 ; Make upper nybble 0 LXI B,BUBSEM ;823BH DAD B ; Index into semaphore table MOV A,M ; Get appropriate semaphore STA BUBBLE ; Save it MOV C,A ; Ready to pass CALL SETSEM ; Request J1 or J3 LXI H,1 ; Start loop counter at 1 SHLD I ; and run (sector count) times BBDR15: ;FAA5 LDA SIOPB+2 ; Get sector count from IOPB LXI H,I ; Loop counter CALL AMMHL ; Subtract ((HL)) from (A) JC BBDR35 ; Finished with loop CALL LDBMC ;878AH LDA SIOPB+1 ; Get command from IOPB CPI READD ; Is it read command ? JNZ BBDR20 ;838DH LHLD SIOPB+5 ; Yes, get buffer address MOV B,H ; Put in BC for call MOV C,L CALL BBREAD ;87F8H JMP BBDR25 ;8395H BBDR20: LHLD SIOPB+5 ; Get buffer address MOV B,H ; Put in BC for call to write MOV C,L CALL BBWRIT ;87DAH BBDR25: CALL BBERR ;87C2H LDA STABUF+1 ; Check error code CPI 0 ; Is it zero ? (ie. no error) JZ BBDR30 ; Jump if no error MVI A,1 ; Else, return error (non-zero) RET BBDR30: CALL SECCNT ; Increment to next sector LXI D,1 ; Increment loop counter by 1 LHLD I DAD D SHLD I JNC BBDR15 BBDR35: LDA BUBBLE ; Get bubble number INR A ; Increment by 1 OUT SEMO ; Turn off semaphore MVI A,0 ; Return success code RET ; The following routine is the PL/M routine called DKDRIO. DKDRIO: ;83BC LXI H,LPFLAG ; Assume disk is ready (motor on) MVI M,1 IN DSKSTS ; Now check if disk ready ANI DSKRDY CPI 0 JZ DKDR05 ; Jump if ready LXI H,LPFLAG ; Else, reset LPFLAG MVI M,0 DKDR05: ;83CF MVI C,FLOPPY ; Disk semaphore CALL SETSEM ; Go set semaphore for disk LDA LPFLAG ; Was disk ready CPI 0 JNZ DKDR10 ; Branch around if ready CALL LPDLY ; Else, 200ms delay for disk ; to come up to speed DKDR10: ;83DF LDA SIOPB ; RC$DR = SIOPB(DRIVE) STA RCLCMD+1 ;88B1H ; DO I = 1 TO SIOPB(SECTOR$CNT) LXI H,1 SHLD I ;88C0H DKDR15: ;83EB LDA SIOPB+2 ;88A3H LXI H,I ;88C0H CALL AMMHL ;8875H JC DKDR45 ;84A7H ; SK$CYL, WR$CYL = SIOPB (TRACK$ADDR) LDA SIOPB+3 ;88A4H STA SKIOPB+2 ;88B4H STA WRIOPB+2 ;88B7H ; IF SIOPB (SECTOR$ADDR) > 16 THEN DO; MVI A,10H LXI H,SIOPB+4 ;88A5H CMP M JNC DKDR20 ;8424H ; WR$SEC = SIOPB (SECTOR$ADDR) - 16 LDA SIOPB+4 ;88A5H SUI 10H STA WRIOPB+4 ;88B9H ; WR$HD = 1 LXI H,WRIOPB+3 ;88B8H MVI M,1 ; SK$DR, WR$DR = SIOPB(DRIVE) OR 4 LDA SIOPB ;88A1H ORI 4 STA SKIOPB+1 ;88B3H STA WRIOPB+1 ;88B6H JMP DKDR25 ;8438H ; END ; ELSE DO; DKDR20: ;8424 ; WR$REC = SIOPB (SECTOR$ADDR) LDA SIOPB+4 ;88A5H STA WRIOPB+4 ;88B9H ; WR$HD = 0 LXI H,WRIOPB+3 ;88B8H MVI M,0 ; SK$DR, WR$DR = SIOPB (DRIVE) LDA SIOPB ;88A1H STA SKIOPB+1 ;88B3H STA WRIOPB+1 ;88B6H ;END DKDR25: ;8438 CALL ISEEK ;8622H LDA SIOPB+1 ; Get command byte CPI READD ; Is it read command ? JNZ DKDR30 ; Jump if not read LXI H,WRIOPB ;88B5H MVI M,46H ; Read command, MFM LHLD SIOPB+5 ; Get DMA address MOV B,H ; and put into BC MOV C,L CALL FDREAD ;8639H JMP DKDR35 ;8460H DKDR30: ;8453 LXI H,WRIOPB ;88B5H MVI M,45H ; Write command, MFM LHLD SIOPB+5 ; Get DMA address MOV B,H ; and put into BC MOV C,L CALL FDWRT ;867DH DKDR35: ;8460 LDA STABUF+1 ;88A9H ANI 0C0H CPI 0 JZ DKDR40 ;8497H LDA STABUF ;88A8H STA X ;88CFH LDA STABUF+1 ;88A9H STA Y ;88D0H LDA STABUF+2 ;88AAH STA Z ;88D1H CALL RECAL ;8611H CALL RECAL ;8611H LDA X ;88CFH STA STABUF ;88A8H LDA Y ;88D0H STA STABUF+1 ;88A9H LDA Z ;88D1H STA STABUF+2 ;88AAH MVI A,1 RET DKDR40: ;8497 CALL SECCNT ; Increment sector count LXI D,1 ; Increment loop index LHLD I ;88C0H DAD D SHLD I ;88C0H JNC DKDR15 ; Make sure no overflow of loop index DKDR45: ;84A7 MVI A,1 ; Reset disk semaphore OUT SEMO MVI A,0 ; Return success code RET IOPB: ; Like the old days ! DB 80H DS 1 ; Instruction DB 1 ; Initialize number of sectors DS 1 ; Track start address DS 1 ; Sector start address DS 2 ; DMA address RETRY: ;84B5 DS 1 ; Retry counter variable SECCNT: ;84B6 ; Increment sector counter. If at end of track, increment ; track number and set sector number to one. LXI H,SIOPB+4 ; Point to sector address in IOPB MOV A,M ; Fetch it CPI 32 ; Is it last sector on track ? JNZ SEC05 ; No, jump MVI M,1 ; Yes, start next track with sector=1 DCX H ; Point to track number INR M ; Increment it JMP SEC10 ;84C7H SEC05: ;84C6 INR M ; Bump up to next sector SEC10: ;84C7 LHLD SIOPB+5 ; Get buffer pointer LXI B,256 ; Increment by sector size DAD B SHLD SIOPB+5 ; Replace it RET PLMDEV: ; PL/M entry point with all parameters normally in IOPB passed ; in registers and on the stack ; CALL PLMDEV (instruction, drive number, sector*128+track, DMA); LXI H,IOPB+6 ; Point to end of IOPB MOV M,D ; High byte of buffer address DCX H MOV M,E ; Low byte of buffer address DCX H MOV M,C ; Sector address DCX H MOV M,B ; Track address DCX H ; Skip over number of sectors DCX H ; Point to instruction POP D ; Get return address POP B ; Get drive number MOV A,C ; Drive number POP B ; Get instruction from stack MOV M,C ; Save instruction PUSH D ; Push return address back on stack MOV C,A ; Drive number in C LXI D,IOPB ; Point to beginning of IOPB DEVIO: ;84E8 ; Device I/O...disk or bubble depending on value in C ; Arrive here with DE pointing to IOPB and C containing the ; disk device number (0-5). LXI H,SIOPB ;88A1H MOV M,C ; Save disk number MVI B,6 ; Byte count DEV05: ;84EE INX H ; Increment destination pointer INX D ; Increment source pointer LDAX D ; Source byte MOV M,A ; Destination byte DCR B ; Count down JNZ DEV05 ; Loop til 6 bytes transferred DI DEV10: ;84F7 MVI A,10 ; Initialize retry counter STA RETRY ;84B5H DEV15: ;84FC LDA SIOPB ; Get device number CPI 4 ; Floppy or bubble ? JNC DEV20 ; Branch if bubble CALL DKDRIO ;83BCH JMP DEV25 ;850DH DEV20: ;850A CALL BBDRIO ;8329H DEV25: ;850D ANA A ; Check return code for error JZ DEV35 ; Jump if no error to RET LDA RETRY ; Else, check error count down DCR A ; Decrement it STA RETRY ; Replace it JNZ DEV15 ; If not to zero, try again CALL DERROR ; Report error and take action LXI H,ERRMSG ; Print error message and give user CALL PRMSG ; option of retry, ignore, cancel MVI A,7 OUT 0C2H DEV30: ;8528 CALL CONIN ; Wait for his response MOV C,A CALL CONOUT ; Echo character CPI 'I' ; Ignore ? JZ DEV35 ; Yes, jump to RET CPI 'R' ; Retry ? JZ DEV10 ; Start operation again CPI 'C' ; Cancel ? JZ ABORT ; Abort and warm boot LXI H,DELCHR ; Anything else, erase char CALL PRMSG ;81E2H JMP DEV30 ; and wait for valid choice DEV35: ;8547 EI RET DELCHR: ;8549 DB 8 DB ' ' ;20H DB 8 DB 0 ERRMSG: ;854D DB CR,LF,'DISK ERROR',CR DB LF,'DRIVE' DNUM: ;8560 DS 4 ; Room to plug in drive number DB ' STATUS=' STAT: ;856D DB ' ',CR,LF,LF DB ' I - IGNORE',CR,LF DB ' R - RETRY',CR,LF DB ' C - CANCEL',CR,LF,CR,LF DB ' ENTER RESPONSE - ' DB 0 ;*********************************************************************** ; ; LPDLY ; THIS ROUTINE DELAYS 200MS ALLOWING TIME FOR THE ; DISK(S) TO SPIN UP TO SPEED. ; ; CALLED BY: DKDRIO ; ;*********************************************************************** LPDLY: MVI C,200 ;OUTER LOOP COUNT LPD1: DCR B ;INNER LOOP (1MS) JNZ LPD1 DCR C ;OUTER LOOP JNZ LPD1 RET SETSEM: ; Entered with 1 or 2 or 4 in C register, corresponding ; to disk semaphore, multimodule J1/J2 semaphore, or ; multimodule J3/J4 semaphore. ; NOTE: The semaphore hardware uses negative logic. ; (ie. 1 = not set) IN SEMI ; Get other processor's semaphore ANA C ; Mask all but semaphore requested JZ SETSEM ; Wait for device available MOV A,C ; Get device requested ANI 6 ; Make sure LSBit is zero for setting ; semaphore (negative logic) OUT SEMO ; Set semaphore for this processor IN SEMI ; Read again from other processor ANA C ; Verify other processor didn't ; request them at same time RNZ ; Return if not ; Here if other processor has already requested the device MOV A,C ; Else get original request ORI 1 ; OR in bit to cause bit set (semaphore ; clear) in 8255A OUT SEMO ; Request again XRA A ; Zero flag STA LPFLAG ;0FFEAH RIM ; Am I processor A ? RLC JNC SETSEM ; Loop if processor A NOP ; NOPs to get out of sync with NOP ; other processor JMP SETSEM ; Loop if processor B ;**************************************** ; ; WAIT FOR INTERRUPT 6.5 FROM FDC ; WTFR65: ;WAIT FOR INTR6.5 RIM ANI 20H JZ WTFR65 RET ;******************************************************* ;******************************************************* ; INTEL 8272 FLEXIBLE DISK CONTROLLER I/O SUBROUTINES ; ; ;******************************************************* ; SENSE INTERRUPT STATUS ; ; CALLING SEQUENCE ; CALL SNSIS ; NORMAL RETURN,CARRY=0 (NC),ST0 & PCN IN STABUF(1) ; and STABUF(2) and count in STABUF(0) ; ERROR RETURN, CARRY=1 (C) ; SNSIS: LXI B,SNSIC ;SENSE INTERRUPT STATUS IOPB MVI E,1 ;NO. OF BYTES CALL CMND ;ISSUE COMMAND JMP RSULT ;GET RESULTS SNSIC: DB 08H ;************************************************************ ; RECALIBRATE COMMAND ; ; CALLING SEQUENCE ; CALL RECAL ; NORMAL RETURN, CARRY=0 (NC) ; ERROR RETURN, CARRY=1 (C) ; RECAL: ;8611 LXI B,RCLCMD ; Recal command IOPB MVI E,2 ; Length of command string CALL CMND ; Issue command JNC ISEK05 ; If no error CALL RSULT ; Else, get result JMP RECAL ; and try again ;************************************************************ ; ISEEK COMMAND ; ; CALLING SEQUENCE ; CALL SEEK ; NORMAL RETURN, CARRY=0 (NC) ; ERROR RETURN, CARRY=1 (C) ; ISEEK: LXI B,SKIOPB ; Seek command IOPB MVI E,3 ; Number of bytes CALL CMND ; Issue command JNC ISEK05 ; Jump if no error CALL RSULT ; Else, get result bytes JMP ISEEK ; and try again ISEK05: ;FD6D CALL WTFR65 ; Wait for interrupt 6.5 JMP SNSIS ; Sense interrupt status ;************************************************************ ; FDREAD COMMAND ; ; CALLING SEQUENCE ; CALL READ ; NORMAL RETURN, CARRY=0 (NC) ; ERROR RETURN, CARRY=1 (C) ; ; INPUT: BC POINT TO USER BUFFER FDREAD: PUSH B ;SAVE USER BUFFER POINTER RD00: LXI B, WRIOPB ;READ COMMAND STRING MVI E, 9 ;# OF BYTES RD10: CALL CMNDS ;GO ISSUE COMMAND JC RSULTP ;JMP ON ERROR (NOTE SPECIAL ENTRY POINT ; INTO RSULT THAT WILL CLEAN UP STACK JZ RD12 ;GOOD SO DO READ CALL SNSIS ;SENSE INTERRUPT STATUS JMP RD00 ;TRY THE COMMAND AGAIN ; ; START READ IN DATA ; RD12: POP H ;READ BUFFER ADDR MVI E,0 ;INITIALIZE BYTE COUNT MVI D,0D0H ;LOAD SUBTRACT VALUES LXI B,0 ;COUNTERS RD15: IN FDCMAS ;GET STATUS THIS LOOP CHECKS TO ; MAKE SURE THE DRIVE IS ON ; AND THE DISK IS LOADED RLC ;CHECK READY BIT JC RD20 ;JIF READY TO READ DCR C JNZ RD15 DCR B JNZ RD15 ;IF IT GETS HERE THERE IS A DISK PROBLEM JMP DKOFF ; DRIVE MAY BE OFF OR NO DISK IS LOADED RD20: IN FDCMAS ;STATUS IN EACH BYTE SUB D ;SUBTRACT D0 ; BORROW BIT = NOT READY ; ZERO = ERROR ; ANYTHING ELSE = READY JC RD20 ;JIF NOT READY JZ RD30 ;JIF ERROR ; RD25: IN FDCIO ;INPUT THE BYTE MOV M, A ;STUFF IN MEMORY INX H ;BUMP POINTER DCR E ;-1 FROM DATA CNTR JNZ RD20 ;LOOP HERE ; RD30: OUT FDCTRM ;ISSUE TERMINATE COUNT ; JMP RSULT ;GET FINAL STATUS AND RETURN ;************************************************************ ; FDWRT A RECORD ; ; CALLING SEQUENCE ; CALL WRITE ; NORMAL RETURN, CARRY=0 (NC) ; ERROR RETURN, CARRY=1 (C) ; ; INPUT: BC POINTS TO USER BUFFER FDWRT: PUSH B ;SAVE USER BUFFER POINTER WR00: LXI B,WRIOPB ;WRITE COMMAND STRING MVI E, 9 ;# OF BYTES WR10: CALL CMNDS ;ISSUE TO CONTROLLER JC RSULTP ;JUMP ON ERROR, NOTE SPECIAL ENTRY POINT ; INTO RSULT THAT WILL CLEAN UP STACK JZ WR12 ;JIF OKAY CALL SNSIS ;CHECK INTERRUPT BITS JMP WR00 ;RETRY THE COMMAND ; ; START OUTPUTTING ; WR12: POP H ;WRITE BUFR ADDR MVI E,0 ;INITIAL BYTE COUNT MVI D,0B0H ;LOAD SUBTRACT VALUES LXI B,0 ;SET COUNTER WR15: ;THIS LOOP MAKES SURE THE DRIVE IS ON ; AND THE DISK IS LOADED IN FDCMAS RLC ;CHECK READY BIT JC WR20 ;JIF READY DCR C JNZ WR15 ;THESE TWO COUNTER ENSURE THE ; DISK HAS ENOUGH TIME TO MAKE ; TWO FULL REVOLUTIONS DCR B JNZ WR15 ;IF IT GETS TO HERE THE DRIVE IS OFF ; OR THE DISK IS NOT LOADED DKOFF: OUT FDCMAS ;THE ONLY WAY TO GET OUT OF THIS STATE ; IS TO WRITE THE MAIN STATUS PORT WHICH ; IS READ-ONLY. SPECIAL DECODING DROPS ; READY SIGNAL. THEN RESULT IS CALLED TO ; GET THE BAD STATUS OUT FDCMAS OUT FDCMAS MVI A,43H DKOFF1: DCR A ;LOOP TO DELAY CHECKING MRQ JNZ DKOFF1 IN FDCMAS ;GET STATUS RLC ;SHIFT OUT MRQ BIT JNC DKOFF ;IF NC - IT DID NOT SET SO TRY IT AGAIN CALL RSULT ;GET THE RESULTS CALL LPDLY ; WAIT AND THEN MAKE SURE SEEK ; INTERRUPT IS CLEAR CALL SNSIS ; DNRABT: ;FDFD ; Display "drive not ready" and abort LXI H,DNRMSG CALL PRMSG ABORT: XRA A STA ABTFLG MVI A,7 OUT KBDSTS JMP REBOOT DNRMSG: DB CR,LF,'DRIVE NOT READY' DB CR,LF,0 WR20: IN FDCMAS ;GET STATUS SUB D ;SUBTRACT B0H ; BORROW BIT = NOT READY ; ZERO = READY ; ANYTHING ELSE = ERROR JC WR20 ;JIF NOT READY JNZ WR30 ;JIF ERROR ; WR25: MOV A, M ;FETCH DATA OUT FDCIO ;SEND TO FDC INX H ;BUMP PNTR DCR E ;-1 CNTR JNZ WR20 ;IF NZ - MORE TO GO ; WR30: OUT FDCTRM ;SEND TC ; JMP RSULT ;READ IN FINAL STATUS AND RETURN ;************************************************************ ; COMMAND PHASE ROUTINE ; ; CALLING SEQUENCE ; BC=ADR(IOPB) ; E=# OF BYTES IN COMMAND ; ; CALL CMNDS ;COMMAND SERIAL OPERATION ; ; CALL CMND ;UNCHECKED COMMAND OUTPUT ; ; ERROR RETURN, CARRY=1 (C) ; BUSY RETURN, ZERO FLAG=0 (NZ). CARRY=0 (NC) ; REGS BC, E PRESERVED FOR WAIT LOOPING ; NORMAL RETURN, ZERO FLAG=1 (Z). CARRY=0 (NC) ; ; NOTE: THE 8272 FDC IS EITHER IN THE READ/WRITE MODE OR ; THE SEEK MODE, AND THESE TWO MODES ARE MUTUALLY ; EXCLUSIVE. ; ; ; COMMAND SERIAL OPERATIONS ; I.E. COMMANDS THAT OPERATE IN THE READ/WRITE MODE ; OF THE 8272 AND/OR COMMANDS THAT MUST CHECK ; FOR FDC BUSY AND FOR ANY FDD SEEKING ; E.G. READ DATA, WRITE DATA, ; READ A TRACK, FORMAT A TRACK ; CMNDS: ;86FE IN FDCMAS ;GET MAIN FDC STATUS ANI 1FH ;FDC BUSY OR ANY SEEKS COMPLETED RNZ ;YES, RETURN W/ZERO FLAG=0, CARRY=0 ; ; COMMANDS THAT DO NOT CHECK FOR FDC BUSY OR ANY FDD SEEKING ; E.G. SENSE INTERRUPT STATUS ; CMND: CALL RDYC ;IS FDC READY FOR COMMAND RC ; RETURN ON ERROR LDAX B ;YES, A=BYTE FROM IOPB OUT FDCIO ;SEND BYTE TO FDC DATA PORT INX B ;BUMP POINTER DCR E ;DONE? JNZ CMND ;NO, CONTINUE RET ;NORMAL RETURN, CARRY=0,ZERO FLAG=1 ;************************************************************ ; RESULT PHASE ROUTINE ; ; CALLING SEQUENCE ; CALL RSULT ; ; NORMAL RETURN, CARRY=0 ; C=NO. OF BYTES FOUND ; RESULT BYTES STORED IN STA$BUF(1-N) ; WITH THE NUMBER OF RESULT BYTES IN STA$BUF(0) ; RSULTP: POP H ;CLEAN UP STACK ON WRITE OR READ ERROR RSULT: LXI H,STABUF+1;HL=ADR (RESULT BUFFER) MVI C,0 ;INITIALIZE BYTE COUNT RSU10: MVI A,DELAY ;ALLOW 8272 TIME RSU15: DCR A ; TO CHANGE JNZ RSU15 ; FDC STATUS RSU17: IN FDCMAS ;A=FDC STATUS RLC ; READY ? JNC RSU17 ;JIF NO RLC ;MORE RESULT BYTES JC RSU20 ;JIF YES LXI H,STABUF;SAVE COUNT OF RESULT BYTES MOV M,C RET ;RETURN RSU20: IN FDCIO ;GET RESULT BYTE FROM FLOPPY MOV M,A ;STORE BYTE IN MEMORY INX H ;BUMP POINTER INR C ;BUMP COUNT JMP RSU10 ;GO BACK AND CHECK FOR MORE BYTES ;************************************************************ ; READY FOR COMMAND SUBROUTINE ; ; CALLING SEQUENCE ; CALL RDYC ; ; NORMAL RETURN, CARRY=0 ; ERROR RETURN, CARRY=1 ; RDYC: MVI A,DELAY ;ALLOW 8272 TIME RYO10: DCR A ; TO CHANGE JNZ RYO10 ; FDC STATUS RYO20: IN FDCMAS ;GET FDC STATUS RLC ;CHECK MSbit JNC RYO20 ;NO, WAIT UNTIL IT IS RLC ;YES, IS DIO = INPUT ? RET ; INDICATE ERROR IF CARRY=1 PRTTAB: ;8741 DW J101+1 DW J102+1 DW J103+1 DW J104+1 DW J105+1 DW J106+1 DW J107+1 DW J108+1 DW J109+1 DW J110+1 DW J111+1 NUM1S EQU ($-PRTTAB)/2 ; Number of entries PRT05: DW J101X+1 DW J102X+1 DW J103X+1 DW J104X+1 NUM0S EQU ($-PRT05)/2 ; Number of entries BUBADR: DB J1+1 ; Port number for J1 multimodule DB J3+1 ; Port number for J3 multimodule STUFF: MOV A,C ; Drive number SUI MAXFLP ; 0 => J1, 1 => J3 LXI H,BUBADR ; Point to legal bubble module addrs MVI B,0 MOV C,A DAD B MOV A,M ; Retrieve legal port address LXI H,PRTTAB ; Point to table of address in code MVI C,NUM1S ; Number of entries in table STUF05: ;FEAB MOV E,M ; Build address from table INX H MOV D,M INX H STAX D ; Plug in legal address DCR C ; Counter JNZ STUF05 ; Loop until done MOV C,A ; Get port number RRC ; Check for 0 in LSBit RNC ; Return if so MOV A,C ; Else, make 0 LSBit ANI 0F0H MVI C,NUM0S JMP STUF05 ; Bubble IOPB processed by LDBMC BBIOPB: ;8785 DB 4 ; FSA channel 1, 4 pages to be Xferred DB 10H ; per write/read, 64 bytes/page DB 40H ; Enable byte DS 2 ; Bubble address LDBMC: ;878A ; Calculate bubble page # from track and sector numbers already stored ; in SIOPB. Then the calculated values for the transfer to/from the ; bubble are sent to the Bubble Memory Controller (BMC). LXI H,SIOPB+3 ; Point to track address MOV C,M ; Retrieve track address INX H ; Point to sector address MOV B,M ; Get it, too MOV A,C ; Starting track number ANI 0EH ; Mask it to 14 or less and even ; (0 - 15 legal) RRC ; Divide by 2 PUSH PSW ; Save it MOV A,C ; Track number again ANI 1 ; Even or odd ? RRC ; Put even/odd flag into high bit MOV C,A ; Save it for now MOV A,B ; Get starting sector number DCR A ; Sector number - 1 (0-63 legal) RLC ; Shift up by 2 bits RLC ; (sector - 1) * 4 ORA C ; OR in high bit even/odd flag LXI H,BBIOPB+3 ;0FEC2H MOV M,A ; Save calculated value INX H ; Point to next location POP PSW ; Get track number/2 MOV M,A ; Save it MVI A,0BH ; Set RAC to Block Length LSByte J111: OUT J1+1 ;41H NOP NOP NOP LXI H,BBIOPB ; Send 5 bytes of bubble IOPB MVI B,5 LDBM05: ;87B2 MOV A,M ; Get next byte to send J104X: OUT J1 ; Send it INX H ; Point to next byte DCR B ; Decrement counter JNZ LDBM05 ; Loop for more MVI A,1DH ; FIFO reset command to BMC J110: OUT J1+1 NOP NOP NOP RET BBERR: ;87C2 ; Read error status from BMC and save it in STABUF with count of ; one as first byte of STABUF. LXI H,STABUF ; Point to buffer MVI A,1 ; Store count of 1 MOV M,A INX H ; Point to where status byte goes J101: IN J1+1 ; Get BMC status ANI 34H ; Mask all but Op Fail, Timing Error, MOV M,A ; and Uncorrectible Error and save it ANI 30H ; Look at Op Fail, Timing Error only CPI 30H ; Is that it ? JNZ BBER05 ; Jump if not Op Fail AND Timing Error MVI A,1FH ; Else, issue software reset to BMC J102: OUT J1+1 BBER05: ;FF13 RET BBWRIT: ;87DA ; Send out bytes from memory pointed to by BC to the bubble controller PUSH B ; Move BC to HL POP H LXI B,8180H ; Status codes to compare later MVI A,13H ; Issue "Write Bubble Data" command J103: OUT J1+1 BBWR05: ;87E3 J104: IN J1+1 ; Read status from BMC ANA B ; Check for Busy and FIFO ready CMP B ; Is that it ? JNZ BBWR10 ; Jump if not both MOV A,M ; Else, Busy=1 and FIFO rdy=1 ==> J101X: OUT J1 ; BMC ready to receive data INX H ; Point to next data byte JMP BBWR05 ; Loop for more BBWR10: ;87F1 J105: IN J1+1 ; Get BMC status again ANA C ; Look at Busy only JNZ BBWR05 ; Loop if busy RET ; Else, done and return BBREAD: ;87F8 ; Read data from Bubble Memory Controller til no data available. Store ; that data in system memory pointed to by BC. PUSH B ; Move BC to HL POP H LXI B,8180H ; Mask and compare values for BMC status MVI A,12H ; Issue "Read Bubble Data" command J106: OUT J1+1 BBRE05: ;8801 J107: IN J1+1 ; Get bubble status ANA B ; Look at Busy and FIFO Ready only CMP B ; Is it both ? JNZ BBRE10 ; Jump if not J102X: IN J1 ; Else, data is ready, get it MOV M,A ; Store to memory INX H ; Bump up pointer JMP BBRE05 ; Loop for more BBRE10: ;880F J108: IN J1+1 ; Get BMC status ANA C ; Look at Busy only JNZ BBRE05 ; Loop if busy only BBRE15: ;8815 J109: IN J1+1 ; Get status again RRC ; FIFO ready bit to CY RNC ; Return if FIFO no ready, ie done J103X: IN J1 ; Else, get remaining data MOV M,A ; Store to memory INX H ; Bump pointer JMP BBRE15 ; Loop til done MDEPA: ;8820 ; Add the 16 bit value in memory pointed to by DE to the 16 bit value ; with MSByte = 0 and LSByte = (A). ((DE))+(A) => HL XCHG MOV E,A MVI D,0 XCHG LDAX D ADD L MOV L,A INX D LDAX D ADC H MOV H,A RET DDIVH: ;882D ; Divide contents of DE by contents of HL. Quotient returned in DE. ??? MOV B,H MOV C,L LXI H,0 ;0 MVI A,10H DDVH05: ;8834 PUSH PSW DAD H XCHG SUB A DAD H XCHG ADC L SUB C MOV L,A MOV A,H SBB B MOV H,A INX D JNC DDVH10 ;8846H DAD B DCX D DDVH10: ;8846 POP PSW DCR A JNZ DDVH05 ;8834H RET MOV B,H MOV C,L BCTDE: ;884E ; Multiply the contents of BC by the contents of DE. (BC)*(DE) => HL LXI H,0 ;0 MVI A,10H BTD05: ;8853 DAD H XCHG DAD H XCHG JNC BTD10 ;885BH DAD B BTD10: ;885B DCR A JNZ BTD05 ;8853H RET AMHL: ;8860 ; Subtract the contents of HL from the contents of A (treated as ; low 8 bits of 16 bit value with upper 8 bits zeros). Returns ; difference in HL. MOV E,A MVI D,0 MOV A,E SUB L MOV L,A MOV A,D SBB H MOV H,A RET MOV L,A MVI H,0 MDEMHL: ;886D ; Subtract the contents of HL from the 16 bit value in the memory ; location pointed to by DE. ((DE))-(HL) => HL LDAX D SUB L MOV L,A INX D LDAX D SBB H MOV H,A RET AMMHL: ;8875 ; Subtract the 16 bit value pointed to by HL from the contents of ; register A treated as 16 bits with the high byte zero. ; (A) - ((HL)) -> HL MOV E,A MVI D,0 MOV A,E SUB M MOV E,A MOV A,D INX H SBB M MOV D,A XCHG RET PLMSIM: ;8881 MOV A,C SIM RET DS 20 ; Stack USTACK: ;8898 ABTFLG: DS 1 TEMP: DS 1 CPMIOP: ; IOPB that defines the loading of CP/M itself DB 0 ; Reserved DB 4 ; Command DB 36 ; Sector count DB 1 ; Track address DB 1 ; Sector address DW CCP ; DMA address SIOPB: DB 0 DB 4 ; Command DB 24H ; Sector count (256 bytes/sector) DB 2 ; Track address DB 5 ; Sector address DW 0 ; DMA address STABUF: DS 8 ; Buffer for status bytes from FDC RCLCMD: DB 7 ; Recalibrate command for FDC DB 0 ; Drive number for recal command SKIOPB: DB 0FH ; Seek command DB 0 ; Drive number DB 2 ; Seek cylinder number WRIOPB: ; Write/Read IOPB DB 46H ; Command (Read, MFM) DB 0 ; Drive number DB 2 ; Cylinder number DB 0 ; Head number DB 4 ; Start sector number DB 1 ; Number of data bytes/sector (1 ==> 256) DB 10H ; Last sector number of track DB 20H ; Gap length DB 0FFH ; 0FFH ==> ignore LPFLAG: DB 1 LASTB: DB 4 I: DS 2 ASCVAL: DS 2 ; Value to be converted by ASCII BASE: DS 1 ; Base used for ascii conversion PTR: DS 2 ; Pointer to ascii string QUOTMP: DS 2 ; Intermediate quotient in ASCII ASCDIG: DS 2 ; Variable for digit at a time in ASCII INDEX: DS 1 ; Index for loop ASCTMP: DS 1 ; Temporary for ASCII DERIDX: DS 1 ; Index of loop in DERROR BUBBLE: DS 1 X: DS 1 Y: DS 1 Z: DS 1 ORG 9000H BOOTDV: DS 1 ; 0 ==> boot from diskette ; 4 ==> boot from bubble 0 BB0FLG: DS 1 ; True ==> bubble 0 exists BB1FLG: DS 1 ; True ==> bubble 1 exists END