TITLE MICRO DECISION ROM SOURCE REV 2.2A JULY 6, 1983 ; ;**************************************************************************** ; ; MICRO-DECISION ; CP/M 2.2 ROM REV. 2.2A ; COPYRIGHT 1982, 1983 ; MORROW DESIGNS, INC. ; 7/06/83 ; ;**************************************************************************** ; ; SPECIAL VERSION OF ROM 2.2 ; WRITE PRECOMP IS TURNED OFF ; TO FIX TIMING PROBLEM IN PRECOMP CIRCUIT.\ ; ;**************************************************************************** ; ; REVCMPT EQU 13H ; Rom rev compatability level REV equ 22h ; Rom Rev. ; ROM EQU 0 ; .Z80 ASEG ; ; ORG ROM ; JP START ; POWER ON JUMP JP MESG ; PRINT A MESSAGE POINTED TO BY DE JP HOME ; HOME HEAD JP RDHST ; READ PHYSICAL SECTOR JP WRTHST ; WRITE PHYSICAL SECTOR JP DISCIO ; DIRECT I/O READ OR WRITE JP RDIO ; I/O READ LOOP JP WRIO ; I/O WRITE LOOP JP BTER ; PRINT BOOT ERROR MESSAGE DW ROMDATX ; POINTER TO ROM VERSION OF DATX JP CENTOUT ; CENTRONICS DRIVER JP RDLSEC ; READ A LOGICAL SECTOR JP WRTLSEC ; WRITE A LOGICAL SECTOR ; ; ; Module name: Microtest ; ; Function: This module tests the RAM and ROM of the Micro Decision ; prior to booting. The module is entered any time the MD ; is RESET. Upon entry, a determination is made as to whether ; a power-on RESET ( cold reset ), or a " warm reset " is ; taking place. If it is a cold reset, than the RAM and ROM ; is tested, prior to booting, and if it is a warm reset, ; the diagnostics are skipped, and the system is booted. ; As the tests progress, the active test is displayed on ; the terminal. Upon completion of a test, a message is ; printed to indicate the passed test. If the test fails, ; then a message is printed, and the machine halts. ; ; ; Rev: 00 David Block 8/18/82 ; Rev: 10 DB 3/18/83 - Added firmware diagnostics ; ; system equates ; diskbuf equ 0fc00h ; start of disk sector buffer memtop equ 0ffffh ; top of ram port equ memtop-1 ; port to test stored here by diagnose bgnchar equ memtop-2 ; starting character for barber-pole test mempass equ memtop-1 ; number of memory passes errors equ memtop-2 ; number of memory errors made romctl equ 0f6h ; rom enable/disable port s1data equ 0fch ; console port data s1stat equ 0fdh ; console port status s2data equ 0feh ; printer port data s2stat equ 0ffh ; printer port status cdata equ 0f4h ; centronics data port cstat equ 0f5h ; centronics status port cr equ 0dh ; carriage return lf equ 0ah ; line feed bell equ 07h ; bell wr equ 0 ; for building test pattern.. rd equ 40h ; ..for memory diagnostics ramchk equ 0d3h ; set to checksum for ram romchk equ 48h ; set to checksum for rom ; ; start: im 0 ; set interrupt mode 0 ld sp,00h ; assume that ram is ok ld iy,romdaty ; init. iy call initctc ; initialize counter-timer chip call inituart ; initialize UARTS ld de,signon ; point to signon message call clrmsg ; and print signon call romtst ; check rom call move ; move memtest call bufchk ; check disk buffer ; ram space call stblk ; test rest of ram if ; necessary, then boot ; ; warm: in a,(s1data) ; make sure no characters are waiting in a,(0f5h) ; check if diagnostics required bit 5,a call z,diagnose ; call diagnostics test ld de,bootmsg ; point to boot msg call mesg ; and print it loop: call ciny ; get a key ; cp cr ; is it a cr? ; jr nz,loop ; if not, loop ld de,crlf ; point to lf call mesg jp boot ; and boot ; end of main ; ; Subroutine: INITUART ; ; Function: This routine initializes both UARTS. They are set ; for 8 bits, no parity, x1 clock rate, and 2 stop bits. ; In addition, DTR, and RTS, are programmed to be on. ; inituart: ld a,80h ; reset uarts out (s1stat),a out (s2stat),a out (s1stat),a out (s2stat),a ld a,40h out (s1stat),a out (s2stat),a ; end of reset sequence ld a,0ceh ; 8 bits, no par, x16, 2 stop out (s1stat),a out (s2stat),a ld a,037h ; Tx,Rx - on, DTR,RTS - on, ER reset out (s1stat),a out (s2stat),a ret ; end of inituart ; ; ;*************************************************** ;This must stay at location 07fh in the ROM; ORG ROM+07FH ; DW SKINT ;INTERUPT RETURN ADDRESS ; FOR WAIT ROUTINE. ;**************************************************** ; ; ; Subroutine: INITCTC ; ; Function: This routine initializes the counter timer chip to provide ; baud rate clocks for the USARTS. The definition of the use of ; the channels is as follows: ; 0 - motor time out delay ; 1 - Baud rate for serial port 1 ; ( set for 9600 baud) ; 2 - Baud rate for serial port 2 ; ( set for 1200 baud) ; initctc: ld a,3eh ;set mode 3, channel 0 out (0f3h),a ;used for motor time out ld a,0FFh ;set to maximum delay out (0f0h),a ld a,0FFH out (0f0h),a ld a,7eh ;set mode 3, channel 1 out (0f3h),a ;set to 9600 baud ld a,0Dh ;this is Console port out (0f1h),a ld a,00h out (0f1h),a ld a,0beh ;set mode 3, channel 2 out (0f3h),a ;set to 1200 baud ld a,068h ;this is printer port out (0f2h),a ld a,00h out (0f2h),a ; channel two set to 1200 baud ret ; ; ; Subroutine: ROMTST ; ; Function: This routine initializes the hl, and bc register ; pairs and calls chksum. It is used to test the system ; integrity. If the test passed, then a message is printed ; and the diagnostics continue. If the test failed, then ; execution will be passed to the memerr routine. ; Memerr will print a message indicating the problem found, ; and then abort the boot process. ; romtst: ; perform a checksum on the rom ld hl,0 ; starting address in rom ld bc,0ffdh ; number of bytes to check call chksum ; compute checksum of rom ret z ; return if checksum was o.k ld de,rommsg ; point to rommsg call mesg ; print ROM error message jr memerr ; print it, and halt ; end of romtst ; ; Subroutine: MEMERR ; ; Function: When a bad memory location is detected during ; the memory test, execution comes here. This routine ; prints an error message, and halts the processor. ; memerr: in a,(0f5h) ; check if diagnostics selected bit 5,a jp z,warm ; if diagnostics selected, go to diag mode ld de,rerr ; point to fatal error message call mesg halt ; halt processor ; end of memerr ; ; Subroutine BUFCHK ; ; Function: This routine calculates a checksum on the disk ; buffer RAM at location FC00h to FFFFh as an indication ; of system integrity. If the test passes, then the routine ; will return, and the accumulator will be equal to zero. ; If a bad checksum is computed, then execution will be ; passed to memerr. ; bufchk: ; set up pointers to test top of RAM ld hl,diskbuf ; low address ld bc,3f8h ; # of bytes to test call chksum ; test diskbuf area ret z ; return if O.K. ld de,rambad ; point to rammsg call mesg ; print ram bad message jr memerr ; jmp to error ; end of bufchk ; ; Subroutine: Chksum ; ; Function: This routine performs a checksum on a given block of ; memory. The memory may be ROM or RAM, but must be set ; such that the correct checksum is zero. The start of ; the block to be tested should be addressed by the HL ; register pair, and the number of bytes to check should ; be in the BC register pair. If the checksum is correct, ; then the accumulator will be zero upon return. ; If the accumulator is non-zero, then the memory is bad. ; The test used is a parity test, by column. The parity ; is computed by xor'ing all the bytes together. The last ; byte in the memory being tested is chosen to cause ; correct parity to yield a 0 after the xor's. ; chksum: xor a ; initialize checksum push af ; because it's popped later chk: pop af ; get current check byte value xor (hl) ; calculate new check byte inc hl ; next location to test dec bc ; decrement byte count push af ; save check byte ld a,b ; test if bc=0 or c ; b or c =0 ==> done jr nz,chk ; if not done, loop pop af ; get completed check byte ret ; and return it ; end of chksum ; ; ; Subroutine: MOVE ; ; Function: This routine moves the memory test program to Š; the disk buffer area, and pads the disk buffer ram ; such that when a checksum is computed on the buffer ; area, the result will be zero. ; move: ; set up to move prog to ram ld hl,block ; from.... ld de,diskbuf ; to.... ld bc,endblk-stblk ; how many... ldir ; move it!!!! ; ld bc,3f7h-endblk+stblk ; number of bytes to pad with push de ; save this pop hl ; get it for source of ldir inc de ; dest for ldir ld (hl), 0ffh ; initial padd ldir ; walk up and fill ld (hl),ramchk ; set checksum ret ; ; ;THE I/O PORTS ARE: FDCSTAT EQU 0FAH FDCDATA EQU 0FBH MOTOR EQU 0F7H MTRCHK EQU 0F5H TC EQU 0F7H ; WRALL EQU 0 ;WRITE ALLOCATTED WRDIR EQU 1 ;WRITE DIRECTROY WRUAL EQU 2 ;WRITE UNALLOCATTED ; ; THE FOLLOWING ARE BITS WITHIN THE DISKDEF BYTE. DEN EQU 6 DSB EQÕ 2 VD EQU 7 TK80 EQU 5 SIZMSK EQU 18H DENMSK EQU 40H ; ; THE FOLLOWING ARE BITS WITHIN THE FLAG BYTE: DFLAG. HSTACT EQU 0 ;HOST ACTIVE FLAG HSTWRT EQU 1 ;HOST WRITTEN FLAG RSFLAG EQU 2 ;READ SECTOR FLAG ŠREADOP EQU 3 ;READ OPERATION FLAG ; ; THE FOLLOWING VALUES ARE OFFSETS WITHIN THE RAMDATY AREA. SEKDSK EQU 0 ;SEEK DISK SEKTRK EQU 1 ;SEEK TRACK SEKSEC EQU 2 ;SEEK SECTOR ; SEKHST EQU 3 ;SEKSEC CONVERTED TO HOST ; UNACNT EQU 4 ;UNALLOC REC COUNT UNADSK EQU 5 ;UNALLOC DISK UNATRK EQU 6 ;UNALLOC TRACK UNASEC EQU 7 ;UNALLOC SECTOR UNAMAX EQU 8 ;SECTORS PER ALLOC. BLOCK SECTRK EQU 9 ;SECTORS PER TRACK ; WRTYPE EQU 10 ;WRITE TYPE DFLAG EQU 11 ;FLAG BYTE TRSEC EQU 12 ;TEMP STORAGE ; VMSGP EQU 13 ;POINTER TO VIRT DRIVE MESG. VDRVP EQU 15 ; " " " " IN MESG. ; CDSK EQU 17 ;CURRENT DRIVE PDSK EQU 18 ; VDSK EQU 19 ;CURRENT VIRTUAL DRIVE ; DMAADR EQU 20 ;DMA ADDR. ; CONOUT EQU 22 ;POINTER TO CONOUT ADDRESS CONIN EQU 24 ;POINTER TO CONIN ADDRESS ; MTAB EQU 26 ;POINTER TO MTAB TROFF EQU 7 ;OFFSET TO TRACK IN MTAB ; ; ; THE FOLLOWING ARE OFFSETS WITHIN THE RAMDATX AREA. HSTDSK EQU 0 ;HOST DISK HSTTRK EQU 1 ;HOST TRACK HSTSEC EQU 2 ;HOST SECTOR ; RETRY EQU 4 ;RETRY COUNT ; SECSIZ EQU 12 ;TWO BYTES WHICH DESCRIBE SECTOR SIZE SECCNT EQU 3 ;SECTOR COUNT ; IOADD EQU 10 ;ADDRESS OF ACTUAL I/O CODE ; HSTBUF EQU 5 ;POINTER TO DATA BUFFER ; ERFLAG EQU 7 ;ERROR FLAG ; STADD EQU 14 ;7 BYTE BUFFER FOR FDC STATUS ; PHYTRK EQU 8 PHYHD EQU 9 ; CMDCNT EQU 21 ;LENGTH OF COMMAND FOLLOWED BY THE COMMAND BYTES. ŠEOT EQU CMDCNT+7 GPL EQU CMDCNT+8 N EQU CMDCNT+6 DTL EQU CMDCNT+9 R EQU CMDCNT+5 CY EQU CMDCNT+3 FDCCMD EQU CMDCNT+1 HD EQU CMDCNT+4 EOTOF EQU 5 ; ; RDCMD EQU 6 ;READ COMMAND WRCMD EQU 5 ;WRITE COMMAND SICMD EQU 8 ;SENSE INT. COMMAND SPCMD EQU 3 ;SPECIFY COMMAND SKCMD EQU 7 ;ROOT OF SEEK & HOME COMMAND SEKBT EQU 8 ;SEEK BIT FOR COMMAND HMBT EQU 0 ;HOME BIT FOR COMMAND ; STCNT EQU 7 ;NUMBER OF BYTES RETURNED BY FDC ; SKERR EQU 2 ;SEEK ERROR CODE WPERR EQU 1 ;WRITE PROTECT CODE NTRDY EQU 6 ;NOT READY CODE ; DOUT EQU 0 ; ASC0 EQU 30H ;ASCII 0 ASCA EQU 41H ASCI EQU 49H ASCR EQU 52H ; MRQ EQU 7 EXB EQU 5 ; MTRMSK EQU 3 ;MOTOR CONTROL BIT MASK MTRMSK1 EQU 7 ; PREC EQU 2 ;PRECOMP BIT MERR EQU 40H ;MASTER ERROR MASK EXEC EQU 20H ;MASK FOR EXEC BIT RDRDY EQU 0F0H ;STATUS FOR READ BYTE WRRDY EQU 0B0H ;STATUS FOR WRITE BYTE ; PR40 EQU 19 ;PRECOMP BOUND FOR 40 TRACKS PR80 EQU 39 ;PRECOMP BOUND FOR 80 TRACKS ; ; ; BOOT CONSTANTS DSKBUF EQU 0FC00H BOOTBF EQU DSKBUF+200H RAMDAT EQU DSKBUF+300H ; BOOT: call xfrdata ; transfer data tables to RAM ; ; CALL HMOK ; CALL DISCIO ;LOAD BOOT ; LD A,(IX+ERFLAG) ;GET ERROR BYTE OR A ;SEE IF O.K. ; JP Z,BOOTBF ;JMP TO BOOT IF NO ERROR ; BTER: LD DE,BTERR ;BOOT ERROR MESG. JP MEMERR ; xfrdata: LD HL,ROMDATX ;GET READY TO MOVE DATA AREAS LD DE,RAMDAT ;INTO RAM LD BC,DATLNG Š; LDIR ;DO MOVE ; LD IX,RAMDAT ;SET IX & IY LD IY,RAMDAT+IYOFF ret ; ;Following are output routines for both serial ports. When SEROUT is called, ; it expects the character to output to be in the C register, and the B reg. ; is to contain the port address for the serial port to be used. ; ser2out: ld b,s2stat ;use port 2 jr serout ; use general purpose output routine ; CNOUT: ld b,s1stat ; use console port for I/O ; serout: ld a,b ; switch port and char. registers ld b,c ; now B has character to output ld c,a ; now, C has port to use ; chkstatO: ; check output status IN A,(C) ;CONSOLE OUTPUT ROUTINE BIT 0,A ;SEE IF RDY JR Z,chkstatO ;JMP IF NOT RDY ; LD A,B ;OUTPUT CHAR. dec c ; point to data port OUT (C),A RET ; ;General purpose serial input routines ; ser2in: ld b,s2stat ;point to 2nd status port jr serin ;use general purpose input routine ; CNIN: ld b,s1stat ;set up for 1st status port serin: ld c,b ;move port to use to C reg chkstatI: ; check input status IN A,(C) ;serial port INPUT ROUTINE BIT 1,A ;SEE IF RDY JR Z,chkstatI ;JMP IF NOT RDY ; dec c IN A,(C) ;GET CHAR. AND 7FH ;MASK OFF PARITY RET ; ; Centronics port driver ; This routine outputs the character in register C to the Centronics port. ; After the character is output, the routine will monitor the ACK Bit from ; the port, and if no acknowledge is found within 1ms, the routine returns ; with the Acc. non zero. If the character is acknowledged, then the Acc. ; will be 0 upon return. ; acktime equ 064 ; centout: in a,(cstat) ; check printer RDY line bit 4,a jr nz,centout ; loop until printer ready ; ld a,c out (cdata),a ; output char to Cent. data port ld a,80h ; send strobe to printer out (cstat),a xor a out (cstat),a ; strobe done ; ld hl,acktime ; load timeout value for ACK ; acklp: in a,(cstat) ; check ACK bit bit 3,a jr z,ackok ; if cleared, return success flag dec hl ; if not cleared, update timeer ld a,l or h ; see if hl=0 => timeout jr nz,acklp ; if not timeout, keep looking dec a ; if timeout, set Acc. to 0ffh ret ackok: xor a ; Acknowledged, so set for printer ret ; ok, then return ; CLRMSG: LD B,50 ;LINE COUNT CLRLP: LD A,LF CALL OUTCN DJNZ CLRLP ; MESG: LD A,(DE) OR A RET Z CALL OUTCN INC DE JR MESG ; OUTCN: LD C,A PUSH BC PUSH DE Š PUSH HL LD L,(IY+CONOUT) ;GET CONOUT ADDR IN HL LD H,(IY+CONOUT+1) CALL CNTJP POP HL POP DE POP BC RET ; CNTJP: JP (HL) ; ; RDLSEC: LD (IY+UNACNT),0 ;CLEAR UNACNT ; SET READOP,(IY+DFLAG) ;SET FOR READ OP ; SET RSFLAG,(IY+DFLAG) ;FORCE READ ; ;TREAT AS UNALLOC LD (IY+WRTYPE),WRUAL ; JP RWOPER ; ; WRTLSEC: RES READOP,(IY+DFLAG) ;SET TO WRITE ; LD (IY+WRTYPE),C ;SAVE WRITE TYPE ; LD A,C ;SEE IF UNALLOC CP WRUAL JR NZ,CHKUNA ;JMP IF NOT ; LD A,(IY+UNAMAX) ;A=INIT UNALLOC SEC COUNT LD (IY+UNACNT),A ;INIT. UNALLOC COUNT. ; LD A,(IY+SEKDSK) ;UNADSK=SEKDSK LD (IY+UNADSK),A ; LD A,(IY+SEKTRK) ;UNATRK=SEKTRK LD (IY+UNATRK),A ; LD A,(IY+SEKSEC) ;UNASEC=SEKSEC LD (IY+UNASEC),A ; CHKUNA: LD A,(IY+UNACNT) ;ANY UNALLOC LEFT? OR A JR Z,ALLOC ;JMP IF NOT ; ; MORE UNALLOC REMAINS. ; DEC (IY+UNACNT) ;DEC UNALLOC COUNT ; LD A,(IY+SEKDSK) ;SAME DISK ? CP (IY+UNADSK) ; JR NZ,ALLOC ;JMP IF NOT ; Š LD A,(IY+SEKTRK) ;SAME TRACK ? CP (IY+UNATRK) JR NZ,ALLOC ;JMP IF NOT ; LD A,(IY+SEKSEC) ;SAME SECTOR ? CP (IY+UNASEC) JR NZ,ALLOC ;JMP IF NOT ; ; SECTOR IS UNALLOC. ; INC (IY+UNASEC) ;INC NEXT UNALLOC SECTOR ; LD A,(IY+SECTRK) ;CHECK FOR END OF TRACK CP (IY+UNASEC) JR NC,NOOVF ;JMP IF NOT END OF TRACK ; INC (IY+UNATRK) ;INC TRACK LD (IY+UNASEC),1 ;SECTOR 1 ; NOOVF: RES RSFLAG,(IY+DFLAG) ;NO READ NEEDED. JR RWOPER ; ALLOC: LD (IY+UNACNT),0 ;CLEAR UNALLOC COUNT ; SET RSFLAG,(IY+DFLAG) ;READ NEEDED ; ; RWOPER: LD (IX+ERFLAG),0 ;CLEAR ERROR FLAG ; LD A,(IY+SEKDSK) ;GET DRIVE CALL GDSK ;GET POINTER TO DISKDEF INC HL ; LD A,(HL) ;A=DSKDEF1 ; LD E,(IY+SEKSEC) ;GET SECTOR DEC E ;DEC SECTOR LD D,E ;SAVE SECTOR ; AND SIZMSK ;MASK OUT SIZE BITS ; ; LD B,0 ;128 BYTE MASK CP 0 ;SEE IF 128 JR Z,S128 ;JMP IF 128 ; LD B,1 ;256 BYTE MASK CP 8 ;SEE IF 256 JR Z,S256 ;JMP IF 256 ; LD B,3 ;512 BYTE MASK CP 10H ;SEE IF 512 JR Z,S512 ;JMP IF 512 ; LD B,7 ;1024 BYTE MASK ; S1024: SRA E ;CALC PHYSICAL SECTOR S512: SRA E S256: SRA E S128: INC E LD (IY+SEKHST),E ;SAVE PHYSICAL SECTOR ; ; LD A,B ;A=MASK AND D ;MASK SECTOR LD (IY+TRSEC),A ;SAVE MASKED SECTOR ; BIT HSTACT,(IY+DFLAG) ;HOST ACTIVE ? SET HSTACT,(IY+DFLAG) ;SET HOST ACTIVE JR Z,FILHST ;FILL IF NOT ACTIVE ; LD A,(IY+SEKDSK) ;SAME DISK ? CP (IX+HSTDSK) JR NZ,NOMTCH ;JMP IF NOT ; LD A,(IY+SEKTRK) ;SAME TRACK ? CP (IX+HSTTRK) JR NZ,NOMTCH ;JMP IF NOT ; LD A,(IY+SEKHST) ;SAME SECTOR ? CP (IX+HSTSEC) JR Z,MATCH ;JMP IF SAME ; NOMTCH: XOR A ;A=NO ERROR STATUS BIT HSTWRT,(IY+DFLAG) ;HOST WRITTEN ? CALL NZ,WRTHST ;WRITE HOST IF NEEDED OR A ;CHECK STATUS RET NZ ;RETURN IF ERROR ; FILHST: LD A,(IY+SEKDSK) ;SET HOST FOR XFER Š LD (IX+HSTDSK),A ; LD A,(IY+SEKTRK) LD (IX+HSTTRK),A ; LD A,(IY+SEKHST) LD (IX+HSTSEC),A ; XOR A ;A=NO ERROR STATUS BIT RSFLAG,(IY+DFLAG) ;NEED TO READ ? CALL NZ,RDHST ;READ OR A ;CHECK STATUS RET NZ ;RETURN IF ERROR ; RES HSTWRT,(IY+DFLAG) ;NO PENDING WRITE ; MATCH: LD L,(IY+TRSEC) ;GET MASKED SECTOR LD H,0 ; ADD HL,HL ;CALC 2**HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ; LD E,(IX+HSTBUF) ;DE=> HOST BUFFER LD D,(IX+HSTBUF+1) ; ADD HL,DE ;HL=> SECTOR IN BUFFER ; LD E,(IY+DMAADR) ;DE=DMA ADDR. LD D,(IY+DMAADR+1) ; LD BC,128 ;BC=SECTOR LENGTH ; BIT READOP,(IY+DFLAG) ;READ OR WRITE ? LD A,0 RET NZ ;RETURN IF READ ; SET HSTWRT,(IY+DFLAG) ;SET WRITE FLAG EX DE,HL ;REVERSE DIRECTION ; RET ; ; RDHST: LD HL,RDIO ;GET READ ADDRESS LD (IX+IOADD),L ;PUT ADDRESS IN RAMDATX LD (IX+IOADD+1),H ; LD C,RDCMD ;C=READ COMMADN JR IOHST ;GO DO I/O ; ; WRTHST: LD HL,WRIO ;GET WRITE ADDRESS LD (IX+IOADD),L ;PUT ADDRESS IN RAMDATX LD (IX+IOADD+1),H ; LD C,WRCMD ;C=WRITE COMMAND ; ŠIOHST: LD A,(IX+HSTDSK) ;GET HOST DRIVE CALL GDSK ;GET POINTER TO DSKDEF INC HL ; LD A,(HL) ;A=DSKDEF1 AND DENMSK ;MASK OFF DENSITY BIT OR C ;OR INTO COMMAND LD (IX+FDCCMD),A ;PUT IN COMMAND BUFFER ; LD (IX+CMDCNT),9 ;SET COMMAND COUNT ; LD A,SIZMSK ;GET SECTOR SIZE BITS AND (HL) RRCA ;RIGHT JUSTIFY RRCA RRCA LD (IX+N),A ;SAVE IN COMMAND BUFFER ; LD B,80H ;B=DTL FOR 128 LD DE,80H ;DE=SIZE BYTES FOR 128 CP 0 ;SEE IF 128 JR Z,STSIZ ;JMP IF 128 ; LD B,0FFH ;B=DTL FOR 256,512 & 1024 LD DE,0 ;DE=SIZE BYTES FOR 256 CP 1 ;SEE IF 256 JR Z,STSIZ ;JMP IF 256 ; LD D,1 ;DE=SIZE BYTES FOR 512 CP 2 ;SEE IF 512 JR Z,STSIZ ; LD D,3 ;DE=SIZE BYTES FOR 1024 ; STSIZ: LD (IX+DTL),B ;SET DTL ; BIT 7,(IX+SECSIZ+1) ;SEE IF NON-STANDARD BLOCK SIZE JR NZ,NSTD ;JMP IF NON-STANDARD ; LD (IX+SECSIZ),E ;SET SIZE BYTES LD (IX+SECSIZ+1),D ; NSTD: XOR A ;ASSUME SIDE 0 & CLEAR CY ; BIT DSB,(HL) ;SEE IF DOUBLE SIDED ; LD B,(IX+HSTTRK) ;GET HOST TRACK LD C,(IX+HSTSEC) ;GET HOST SECTOR ; JR Z,SSIDE ;JMP IF SINGLE SIDED ; RR B ;B=TRACK CY=HEAD ; RLA ;GET HEAD BIT IN A ; SSIDE: LD (IX+HD),A ;SET HEAD RLCA RLCA LD (IX+PHYHD),A ; LD (IX+PHYTRK),B ;SET TRACK LD (IX+CY),B ; LD (IX+R),C ;SET SECTOR ; LD A,3 ;MASK OUT PHYSICAL DRIVE ADDRESS AND (HL) OR (IX+PHYHD) ;OR IN HEAD BIT LD (IX+FDCCMD+1),A ;PUT IN COMMAND BUFFER ; LD DE,EOTOF ;OFFSET TO EOT & GPL IN MTAB ADD HL,DE ;ADD TO MTAB POINTER ; LD A,(HL) ;SET EOT LD (IX+EOT),A ; INC HL ;SET GPL LD A,(HL) LD (IX+GPL),A ; ; DISCIO: LD B,(IX+RETRY) ;B=RETRY COUNT LD D,3 ; DLOP: PUSH BC ;SAVE RETRY COUNT PUSH DE ;SAVE VFO PNTR. ; LD A,SEKBT ;C=SEEK COMMAND CALL SELSK ;SELCT DRIVE AND SEEK ; ; PUSH IY ;SAVE IY ; CALL STRTIO ;START FDC ; CALL DIO ;GO DO I/O ; POP IY ;RESTORE IY ; LD B,STCNT ;SET UP AND READ PUSH IX ;FDC STATUS BYTES POP HL ;INTO STATUS MEMORY. ; LD DE,STADD ADD HL,DE ;HL=STATUS MEMORY ; GSTLP: CALL INFDC ;WAIT FOR FDC INI ;GET BYTE JR NZ,GSTLP ; SKST: POP DE ;RESTORE VFO PNTR. POP BC ;RESTORE RETRY COUNT ; LD A,(IX+STADD) ;A=STATUS0 AND MERR ;CHECK FOR ERROR JR Z,DRET1 ;RETURN IF NO ERROR ; CALL ECODE ;CALC. ERROR CODE ; LD A,WPERR ;SEE IF WRITE PROTECT ERROR CP C JR Z,NRTRY ; LD A,NTRDY CP C CALL Z,CNT ; LD A,SKERR ;SEE IF WRONG TRACK CP C CALL Z,HOMED ;HOME IF WRONG TRACK ; LD A,11 CP B CALL Z,HMOK ; DJNZ DLOP ;RETRY LOOP ; NRTRY: LD A,(IX+ERFLAG) ; check ERFLAG CP 0FEH ; FEh => no error handling JR Z,DRET ; CALL EDSP ; JR C,DRET ;RET IF 'A' (ABORT) ; OR A ;SEE IF 'R' (RETRY) JP NZ,DISCIO ;TRY AGAIN DRET1: LD C,A ;else, 'I' (IGNORE) ; DRET: LD (IX+ERFLAG),C ;UPDATE ERROR FLAG LD A,C ; RET ; STRTIO: LD C,FDCDATA ;SET UP TO XFER FDC COMMAND ; PUSH IX ;CALC. ADDR. OF COMMAND LIST POP HL ;AND PUT IN HL LD DE,CMDCNT ;OFFSET TO COMMAND LIST ADD HL,DE ; LD B,(HL) ;B=COMMAND LIST LENGTH Š INC HL ;INC COMMAND LIST PNTR. ; CALL RDY ;MAKE SURE FDC IS READY ; STATLP: CALL OUTFDC ;WAIT FOR FDC OUTI ;WRITE COMMAND BYTE JR NZ,STATLP ;LOOP 'TIL DONE ; LD B,(IX+SECSIZ) ;SET UP REGS. FOR I/O LD E,(IX+SECSIZ+1) RES 7,E ;CLEAR NON-STANDARD BIT LD D,(IX+SECCNT) DEC D ;D=SECTOR COUNT -1 ; LD L,(IX+IOADD) ;HL=IO ADDR. LD H,(IX+IOADD+1) PUSH HL ;SAVE ON STACK POP IY ;IY=I/O ADDR. ; LD L,(IX+HSTBUF) ;HL=DMA ADDR. LD H,(IX+HSTBUF+1) ; RET ; DILP: BIT MRQ,A ;SEE IF MASTER REQ. RET NZ ; DIO: IN A,(FDCSTAT) BIT EXB,A ;SEE IF EXECUTING JR Z,DILP ; PUSH DE ;SAVE DE LD DE,DOUT ;LOAD TIME OUT COUNT JP (IY) ;JMP TO I/O ; EDSP: LD A,C RLCA LD E,A LD D,0 LD HL,ETAB-2 ADD HL,DE LD E,(HL) INC HL LD D,(HL) ; PUSH BC ;SAVE STATUS PUSH DE ;SAVE DE Š LD DE,DMESG ;BASIC ERR MESG. CALL MESG ; LD A,ASCA ;GET DRIVE IN ASCII ADD A,(IX+HSTDSK) CALL OUTCN ;DISPLAY DRIVE ; LD DE,COLN CALL MESG ; POP DE ;RESTORE ; CALL MESG ld a,0ffh ;see if error message desired cp (IX+ERFLAG) scf ;simulate ABORT selection CALL nz,ERSP ; POP BC ;C=STATUS RET ; ; VIRTM: CALL MESG ;PRINT MESG. PLOP: CALL CINY ;GET RESPONSE ; CP 0DH ;SEE IF CR JR NZ,PLOP ;LOOP IF NOT ; DCRLF: LD DE,CRLF JP MESG ;PRINT CR & LF ; ERSP: LD DE,RESM CALL MESG ERLP: CALL CINY AND 0DFH ;FORCE UPPER CASE ; LD E,A CP ASCA ;SEE IF "A" SCF JR Z,ERET ; CP ASCR ;SEE IF "R" JR Z,ERET ; SUB ASCI ;SEE IF "I" JR NZ,ERLP ;TRY AGAIN ; ERET: PUSH AF LD A,E CALL OUTCN LD DE,CRLF CALL MESG POP AF RET ; ; ŠCINY: PUSH BC PUSH HL LD L,(IY+CONIN) ;HL=CONIN ADDR. LD H,(IY+CONIN+1) ; CALL CNTJP ; POP HL POP BC ; RET ; RLOOP1: AND EXEC ;SEE IF STILL EXECUTING JR Z,EXDN ;JMP IF DONE ; IN A,(FDCSTAT) ;GET STATUS CP RDRDY ;SEE IF BYTE READY JR Z,RDSC ;JMP IF 1ST BYTE READY ; DEC DE ;DEC TIME OUT COUNT LD A,E ;SEE IF TIMED OUT OR D JR Z,TMOUT ;JMP IF TIMED OUT ; RDIO: IN A,(FDCSTAT) ;GET STATUS CP RDRDY ;SEE IF BYTE READY JR NZ,RLOOP1 ;LOOP IF BYTE NOT READY ; RDSC: INI ;XFER 1ST BYTE ; POP DE ;GET DE PUSH DE ;SAVE DE ; RLOOP3: LD A,E ;SEE IF LAST PART OF LAST SECTOR OR D JR NZ,RLOOP2 ;JMP IF NOT LAST PART DEC B ;DEC BYTE COUNT IF LAST ; RLOOP2: EI ;ENABLE INTS. HALT ;WAIT FOR BYTE IN A,(FDCSTAT) ;GET STATUS AND EXEC ;SEE IF STILL EXECUTING JR Z,EXDN ;JMP IF NOT ; INI ;XFER BYTE JR NZ,RLOOP2 ;LOOP IF MORE BYTES ; DEC E ;DEC MSB OF SECTOR LENGTH JP P,RLOOP3 ;JMP IF NOT END ; POP DE ;RESTORE SECTOR SIZE & COUNT DEC D ;DEC SECTOR COUNT PUSH DE JP P,RLOOP3 ;JMP IF NOT END ; EI ;ENABLE INTS. HALT ;WAIT FOR LAST BYTE Š; IN A,(TC) ;STOP FDC INI ;XFER LAST BYTE ; EXDN: POP DE RET ; TMOUT: POP DE POP HL POP IY PUSH IY PUSH BC LD C,MOTOR ;INIT C REG TO MOTOR LD A,(IY+PDSK) ;A = PHYSICAL DISK LD B,A SET 3,A OUT (C),A ;PULSE READY LINE ; TMLP: IN A,(FDCSTAT) ;GET STATUS OF FDC BIT EXB,A ;SEE IF EXECUTING JR NZ,TMLP ;LOOP IF EXECUTING OUT (C),B ;ELSE PULSE READY LINE POP BC ;RESTORE REGISTERS JP (HL) ;EXECUTION DONE ; WLOOP1: AND EXEC ;SEE IF STILL EXECUTING JR Z,EXDN ;JMP IF DONE ; IN A,(FDCSTAT) ;GET STATUS CP WRRDY ;SEE IF BYTE READY JR Z,WRSC ;JMP IF 1ST BYTE READY ; DEC DE ;DEC TIME OUT COUNT LD A,E ;SEE IF TIMED OUT OR D JR Z,TMOUT ;JMP IF TIMED OUT ; ; WRIO: IN A,(FDCSTAT) ;GET STATUS CP WRRDY ;SEE IF BYTE READY JR NZ,WLOOP1 ;LOOP IF BYTE NOT READY ; WRSC: OUTI ;XFER 1ST BYTE ; POP DE ;GET DE PUSH DE ;SAVE DE ; WLOOP3: LD A,E ;SEE IF LAST PART OF LAST SECTOR OR D JR NZ,WLOOP2 ;JMP IF NOT LAST PART DEC B ;DEC BYTE COUNT IF LAST ; WLOOP2: EI ;ENABLE INTS. HALT ;WAIT FOR BYTE IN A,(FDCSTAT) ;GET STATUS AND EXEC ;SEE IF STILL EXECUTING JR Z,EXDN ;JMP IF NOT ; OUTI ;XFER BYTE JR NZ,WLOOP2 ;LOOP IF MORE BYTES ; DEC E ;DEC MSB OF SECTOR LENGTH JP P,WLOOP3 ;JMP IF NOT END Š; POP DE ;RESTORE SECTOR SIZE & COUNT DEC D ;DEC SECTOR COUNT PUSH DE JP P,WLOOP3 ;JMP IF NOT END ; EI ;ENABLE INTS. HALT ;WAIT FOR LAST BYTE ; IN A,(TC) ;STOP FDC OUTI ;XFER LAST BYTE ; POP DE RET ; CNT: DEC D RET NZ LD B,1 RET ; HOME: BIT HSTWRT,(IY+DFLAG) ;CLEAR HOST ACTIVE FLAG JR NZ,HMSK ;UNLESS WRITE IS PENDING. RES HSTACT,(IY+DFLAG) ; HMSK: LD A,(IX+HSTDSK) PUSH AF ; LD A,(IY+SEKDSK) LD (IX+HSTDSK),A ; CALL HMOK ; POP AF LD (IX+HSTDSK),A ; RET ; HOMED: CALL CNT ; HMOK: LD A,HMBT ;SET BIT FOR HOME COMMAND ; SELSK: PUSH BC ;SAVE RETRY PUSH DE ; LD C,A ; LD A,(IX+HSTDSK) ;GET NEW DRIVE ; PUSH AF ;SAVE NEW DRIVE ; CALL GDSK ;GET POINTER TO DSKDEF ; LD A,(HL) ;GET MOTOR CONTROL BIT AND MTRMSK ;MASK OFF MOTOR BIT BIT 2,A JR Z,MTOK RRCA MTOK: LD E,A ;E=MOTOR CONTROL BIT ; Š INC HL ;HL=>TO DSKDEF1 ; POP AF ;A=NEW DRIVE ; CP (IY+CDSK) ;SEE IF DRIVE CHANGED JR Z,SAME ;JMP IF NO CHANGE LD (IY+CDSK),A ;UPDATE CURRENT DRIVE ; BIT VD,(HL) ;SEE IF VIRTUAL PUSH BC JR Z,NVIRT ;JMP IF NOT VIRTUAL ; CP (IY+VDSK) ;NEW DRIVE = VDSK ? LD (IY+VDSK),A ;UPDTAE VDSK TO NEW DRIVE PUSH AF ;SAVE NEW DRIVE PUSH DE PUSH HL ; CALL NZ,VIRT ;CALL IF NEW DRIVE <> VDSK ; POP HL ;RESTORE REGS. POP DE POP AF ; NVIRT: CALL SPECFY ;SET NEW DRIVE PARAMETERS POP BC ; SAME: BIT DEN,(HL) ;CHECK DENSITY ; JR Z,SINGLE ;JMP IF SINGLE ; DOUBLE: LD A,PR40 ;40 TRACK PRECOMP BOUND BIT TK80,(HL) ;SEE IF 80 TRACK JR Z,TRK40 ;JMP IF 40 TRACK ; LD A,PR80 ;80 TRACK PRECOMP BOUND ; TRK40: CP (IX+PHYTRK) ;SEE IF PRECOMP JR NC,SINGLE ;JMP IF NO PRECOMP ; ;********************************************************************* ; SET PREC,E ;TURN ON PRECOMP ;********************************************************************* ; SINGLE: ; IN A,(MTRCHK) ;SEE IF MOTOR ON AND MTRMSK ;MASK MOTOR BITS LD D,A ;SAVE MOTOR BITS AND E ;NZ IF MOTOR ON, CY=0 ; PUSH AF ;SAVE FLAGS ; LD A,(HL) ;GET MOTOR BITS AND MTRMSK LD B,A INC B LD A,8 MLOOP: RLCA DJNZ MLOOP OR D OR E ;TURN ON MOTORS OUT (MOTOR),A Š LD (IY+PDSK),A ;SAVE MOTOR BYTE ; CALL GTRK ;GET CURRENT TRACK LD A,(IX+PHYTRK) ;A=NEW TRACK ; BIT 3,C ;SEE IF HOME JR Z,SK0 ;JMP IF HOME ; CP D ;NEW TRK = OLD TRK ? JR Z,VSKP ;JMP IF TRKS. SAME ; SK0: CALL SEEK ; POP AF ;GET FLAGS SCF ;CY=1 FOR HEAD SETTLE PUSH AF ;SAVE FLAGS ; VSKP: POP AF ;RESTORE FLAGS ; INC HL ;POINTER TO MOTOR WAIT ; CALL WAIT ;WAIT FOR MOTOR AND/OR HEAD ; POP DE POP BC ;RESTORE RETRY ; RET ; ; VIRT: INC A ;CONVERT TO ASCII SET 6,A ; LD E,(IY+VDRVP) LD D,(IY+VDRVP+1) ;DE=PNTR TO VDRV LD (DE),A ;PUT VDRV IN VMSG. ; LD E,(IY+VMSGP) ;DE=PNTR TO VMSG LD D,(IY+VMSGP+1) ; JP VIRTM ; GDSK: PUSH IY ;GET IY INTO HL POP HL ;CALC. PIONTER TO MTAB FOR DRIVE LD DE,MTAB ADD HL,DE ; LD D,A RLCA ;MULT. BY 9 RLCA RLCA ADD A,D LD E,A LD D,0 ADD HL,DE ;HL=MTAB FOR DRIVE ; Š RET ; GTRK: PUSH HL ;SAVE MTAB POINTER LD DE,TROFF ;OFFSET TO TRACK ADD HL,DE ;ADD TO HL ; LD D,(HL) ;D=CURRENT TRACK ; POP HL ;RESTORE HL RET ; ; WAIT ROUTINE EXPECTS HL TO POINT TO MOTOR DELAY TIME IN MTAB. ; THE Z AND THE CY FLAGS DETERMINE IF MOTOR AND/OR HEAD SETTLE ; DELAYS ARE NEEDED. ; IF Z=1 THEN MOTOR DELAY IS NEEDED. ; IF CY=1 THEN HEAD SETTLE DELAY IS NEEDED. ; ; DELAY TIME = 4MS. TIMES THE VALUES IN MTAB. ; WAIT: LD B,(IY+PDSK) ;GET MOTOR BYTE LD C,MOTOR ;GET MOTOR PORT ; LD D,(HL) ;HL=>WAIT TIME LD E,0 INC HL ;HL=>SETTLE TIME ; JR NC,WAIT1 ;JMP IF NO SETTLE DELAY ; IM 2 ;CHANGE INT. MODE LD A,ROM SHR 8 AND 0FFH ;SET INT TABLE ADDRESS FOR ROM+7F LD I,A ; JR NZ,WAIT2 ;JMP IF NO MOTOR DELAY ; EI ;ENABLE INTS. ; WLOP1: DEC DE ;WAIT LOOP. DEC COUNT LD A,D ;CHECK IF COUNT IS 0 OR E JR Z,WLOP2 ;JMP IF COUNT = 0 ; LD C,MOTOR ;GET MOTOR PORT & TIME PAD OUT (C),B ;KEEP MOTORS RUNNING JR WLOP1 ;LOOP ; WAIT2: LD D,(HL) ;GET HEAD SETTLE DELAY EI WLOP2: OUT (C),B ;KEEP MOTORS RUNNING JR WLOP2 ;JMP SELF ; SKINT: IM 0 ;SET INT MODE BACK TO 0 INC SP ;FIX STACK INC SP ; CALL SINTR ;CLEAR INT. ; LD A,D ;COMPARE SETTLE TIME TO WHATS LEFT CP (HL) ;OF MOTOR TIME. JR NC,WLOP3 ;JMP IF MOTOR BIGGER ; LD D,(HL) ;DE=SETTLE TIME JR WLOP3 ; WAIT1: RET NZ ;RETURN IF NO MOTOR DELAY NEEDED ; WLOP3: OUT (C),B ;KEEP MOTORS RUNNING OUT (C),B ;TIME PAD NOP ; " " ; DEC DE ;DEC COUNT LD A,D OR E ;SEE IF COUNT = 0 JP NZ,WLOP3 ;JMP IF DONE ; RET NRDY: CALL SINT ;SENSE INT. TO CLEAR SEEK FLAGS ; RDY: IN A,(FDCSTAT) ;SEE IF ANY SEEK FLAGS ARE SET AND 0FH JR NZ,NRDY ;JUMP IF ANY SEEK FLAGS ARE SET ; IN A,(FDCSTAT) ;MAKE SURE FDC IS READY BIT 4,A JR NZ,RDY RET ; OUTFDC: IN A,(FDCSTAT) ;WAIT FOR FDC TO SIGNAL AND 0C0H ;THAT IT IS READY TO CP 80H ;ACCEPT INPUT JR NZ,OUTFDC ;LOOP IF NOT READY RET ;RETURN WHEN READY ; INFDC: IN A,(FDCSTAT) ;WAIT FOR FDC TO SIGNAL AND 0C0H ;THAT IT IS READY TO CP 0C0H ;RETURN OUTPUT. JR NZ,INFDC ;LOOP IF NOT READY RET ;RETURN WHEN READY ; SEEK: CALL OUTFDC ;WAIT FOR FDC ; LD A,SKCMD ;ROOT OF SEEK & HOME COMMAND OR C ;OR IN BIT TO GET HOME OR SEEK OUT (FDCDATA),A ;OUTPUT COMMAND ; CALL OUTFDC ;WAIT FOR FDC LD A,MTRMSK ;GET MASK FOR DS AND (HL) ;MASK OFF DS ; BIT 3,C ;SEE IF HOME JR Z,HD0 ;JMP IF HOME ; OR (IX+PHYHD) ;OR IN HEAD BIT HD0: OUT (FDCDATA),A ;OUTPUT HD AND DS ; BIT 3,C ;SEE IF HOME RET Z ;RET IF HOME ; CALL OUTFDC ;WAIT FOR FDC Š LD A,(IX+PHYTRK) ;GET TRACK OUT (FDCDATA),A ;OUTPUT TRACK ; RET ; SPECFY: PUSH HL ;SAVE HL INC HL ;MOVE HL TO SPECIFY PARAMETERS INC HL ;FOR CURRENT DRIVE. INC HL ; CALL OUTFDC ;WAIT FOR FDC LD A,SPCMD ;SPECIFY COMMAND OUT (FDCDATA),A ; TMPBC EQU (200H OR FDCDATA) ; LD BC,TMPBC ;PARAM. COUNT SPLP: CALL OUTFDC OUTI JR NZ,SPLP ; POP HL RET ; SINT: CALL OUTFDC LD A,SICMD ;SENSE INT. STAT. COMMAND OUT (FDCDATA),A ; Š CALL INFDC IN A,(FDCDATA) ; AND 0C0H ;CHECK FOR INVALID COMMAND CP 80H RET Z ;RETURN IF INVALID ; CALL INFDC IN A,(FDCDATA) ;GET PRESENT TRACK ; RET ; SINTR: CALL SINT ; PUSH HL ;SAVE HL & DE PUSH DE ; LD DE,TROFF-2 ;OFFSET TO CURRENT TRACK ADD HL,DE ;HL=>CURRENT TRACK LD (HL),A ;UPDATE CURRENT TRACK ; POP DE ;RESTORE REGS. POP HL ; RET ; ;THIS ROUTINE USES THE STATUS THAT HAS BEEN STORED IN STADD ;THROUGH STADD+2 TO COMPUTE AN ERROR CODE. POSSIBLE ERROR ;CODES ARE: ; ; WRITE PROTECT 1 ; SEEK ERROR 2 ; DATA CRC 3 ; ID CRC 4 ; UNREADABLE 5 ; SINGLE SIDED 6 ; SYNC 7 ; DRIVE NOT RDY 8 ; ; WP EQU 1 BCE EQU 1 WC EQU 4 DD EQU 5 EN EQU 7 ND EQU 2 MA EQU 0 CM EQU 6 MD EQU 0 SS EQU 3 ORE EQU 4 NR EQU 4 DER EQU 5 ; ECODE: LD C,1 ;ERROR ROOT ; BIT WP,(IX+STADD+1) ;SEE IF WP RET NZ ; Š INC C BIT BCE,(IX+STADD+2) ;SEE IF SEEK ERROR RET NZ BIT WC,(IX+STADD+2) RET NZ ; INC C BIT DD,(IX+STADD+2) ;SEE IF DATA CRC RET NZ ; INC C BIT DER,(IX+STADD+1) ;SEE IF ID CRC RET NZ ; INC C BIT EN,(IX+STADD+1) ;SEE IF UNREADABLE RET NZ BIT ND,(IX+STADD+1) RET NZ BIT MA,(IX+STADD+1) RET NZ BIT CM,(IX+STADD+2) RET NZ BIT MD,(IX+STADD+2) RET NZ ; INC C BIT SS,(IX+STADD) ;SEE IF SINGLE SIDED ERROR RET NZ ; INC C BIT ORE,(IX+STADD+1) ;SEE IF SYNC RET NZ ; INC C ;MUST BE DRIVE NOT READY BIT NR,(IX+STADD) ;SEE IF NOT READY RET NZ ; INC C ;UNKNOWN ERROR RET ; ; ; DS (ROM+7FCH-$),0FFH ; ;****************************************************************** ;This area must remain at locations 7fc-7ff ; DB REVCMPT db rev ; DS 2,0FFH ; ; ; ; ; Subroutine: DIAGNOSE ; ; This routine is used to test the various functional blocks of the hardware. ; It displays a menu, from which you can select the test to be performed. ; diagnose: ld de,diagmsg ; print diagnostics signon msg call mesg ; and menu inlp: call ciny ; get test to run cp '1' ; adjust and check if in range jp m,inlp ; between 1, and... cp 3ah ; ... 9 jp p,inlp push af call outcn ; output test number ld de,crlf ; if valid #, send cr/lf call mesg pop af sub '0' ; A = test number (1-9) cp 4 ; if bpole test, set up port jp p,notbp ; don't set up port if not bpole test ld (port),a ; set up port to use for bpole test notbp: dec a ; set base offset of 0 rlca ; calculate offset in dispatch table ld c,a xor a ld b,a ; bc = offset in dispatch table ld hl,dsptch ; hl = base of dispatch table add hl,bc ; hl = address of vector for selected ; diagnostic test ld e,(hl) inc hl ld d,(hl) ex de,hl call cntjp ; to indirect jump jr diagnose ; then return to menu ; dsptch: dw bpole ; test 1 = bpole on serial port 1 dw bpole ; 2 = bpole on serial port 2 dw bpole ; 3 = bpole on centronics port dw loopbk ; 4 = loop back test on serial port 2 dw mtest ; 5 = memory test dw rwtest ; 6 = floppy r/w test dw seektest ; 7 = floppy seek test dw vfotest ; 8 = vfo test dw diagboot ; boot system ; ; Subroutine: BPOLE ; ; This will print a barber-pole test pattern on the ports. The port ; to test is stored in location 'PORT'. 1 => serial port 1 ; 2 => serial port 2 ; 3 => centronics port ; bpole: ld a,20h ; initialize barberpole test ld (bgnchar),a ; a = character to print prntlp: ld a,05fh ; b = number of characters before cr/lf ld b,a ld a,(bgnchar) ; get character to start with outlp: call testout ; output the character dec b ; adjust character count jr z,shift ; if end, change start for next line inc a ; else, next character jp p,outlp ; send next character ld a,20h ; jump over control codes jr outlp ; then continue sending shift: call keystat ; see if a key has been typed ret nz ; if so, then end test ld a,cr ; send out cr, lf call testout ; output the character ld a,lf call testout ld a,(bgnchar) ; change starting character inc a jp m,bpole ; if 80h, then re-start ld (bgnchar),a jr prntlp ; then print next line ; ; Subroutine: TESTOUT ; ; Output a character to a port, where port saved in memory location 'PORT' ; On entry, A = character to output. This routine calls the appropriate ; output handler. ; testout: push af push bc ld c,a ld a,(port) ; get port to test cp 1 ; see if port 1 jr nz,port2 ; if not, check if port 2 call cnout ; if 1, test port 1 jr exitout ; restore, and return port2: cp 2 ; see if test for port 2 jr nz,cent ; no? then centronics call ser2out ; yes => use 2nd serial port jr exitout ; then restore and return cent: call centout ; must be centronics test exitout: pop bc pop af ; restore registers before return ret ; ; ; keystat: in a,(s1stat) ; read console status bit 1,a ; see if key hit ret ; ; Subroutine: LOOPBK ; ; Perform a loop-back test on serial port 2. Test ends when a key is pressed. ; loopbk: in a,(s2data) ; clear any character in buffer xor a ; starting character for test is null looptst: ld d,a ; character to look for ld c,a ; character to send in C call ser2out ; output char in C call ser2in ; get char from port 2 to acc. cp d ; did we get what we sent? jr nz,looperr ; jump if not call keystat ; else, was console key hit? jr nz,loopgd ld a,d ; get next char to send inc a jp p,looptst ; and send it loopgd: ld de,passed ; point to success mesg jr msgout ; and send message looperr: ld de,failed ; point to fail message msgout: call mesg ; print message ld de,loopmsg call mesg ret ; ; Subroutine: MTEST ; ; Perform memory test continually, until a key is hit on keyboard. ; mtest: ld de,tstmsg ; print memory test msg call mesg call clrerr ; clear error count and pass count memlp: ld hl,0 ; lowest address to test ld bc,diskbuf-1 ; highest address to test call memtest ; test memory or a ; test flags jr z,noerr ; if no errors, increment pass call incerr ; else, increment error count noerr: call prntpass ; print pass info call keystat ; see if key pressed jr z,memlp ; keep testing if no key ret ; else, return to test menu ; ; clrerr: xor a ; clear errors and pass ld (mempass),a ld (errors),a ret ; ; incerr: ld a,(errors) ; increment error count inc a ld (errors),a ret ; prntpass: ; print pass info for test ld de,passmsg ; point to pass message call mesg ; print it ld a,(mempass) ; get number of passes made inc a ; increment by one ld (mempass),a ; save it call outbyte ; print it ld de,coln call mesg ld a,(errors) ; get number of errors found call outbyte ; print them ld de,errnum ; and a message call mesg ret ; ; ; ;RWTEST writes a worst case data pattern to track 39 of a selected drive, ; afterwhich it goes into a loop reading the data continually until a key ; is pressed on the keyboard. If CRC errors occur during the ID, or DATA ; areas, the error count is incremented. After each pass, the pass number, ; and the total number of errors detected since starting the test, is printed. ; ; rwtest: call setup ;set up tables, get drive to test ld (IX+HSTTRK),39 ; set to test track 39 ld (IX+SECCNT),5 ; set to read all five sectors call bldbuf ; build buffer of worst case data call clrerr ; clear error count rwloop: call wrtbuf ; write data to disk call rdbuf ; read data from disk call keystat ; check if key hit jr z,rwloop ; if no key hit, r/w again ret ; ;SETUP moves the ramdatx and ramdaty ares to ram, and selects the drive to ; be used for the test. ; setup: call xfrdata ; move ramdatx and ramdaty ld de,drvmsg ; prompt for drive to use call mesg ; print prompt drvlp: call ciny ; get drive and 0dfh ; force upper case sub 41h ; test for valid drive jr c,drvlp cp 4 jr nc,drvlp ld (IX+HSTDSK),a ; drive ok, so set up to use it push af ld de,insrt ; tell them to insert a disk call mesg pop af ; get back drive add a,41h ; turn to ascii call outcn ; tell which drive gets the diskette ld de,rtrnmsg ; point to rest of message call mesg call ciny ; get a character ld de,crlf ; output a cr and lf call mesg ret ; ;BLDBUF build a buffer of worst case data. ; pat1 equ 0aa5fh ; worst case pattern 1 pat2 equ 6db6h ; worst case pattern 2 tstbuf equ 8000h ; use 8000h as start of dma address ; bldbuf: ld bc,pat1 ; get first worst case pattern ld hl,tstbuf ; point to test buf push hl ; save testbuf ld de,tstbuf+2 ; point to dest. byte ld (hl),c ; save 1st byte of worst case pattern inc hl ld (hl),b ; save 2nd byte of pattern dec hl ; point to start for copy ld bc,800h ; copy 2k bytes ldir ; replicate pattern ; ld bc,pat2 ; rest gets 2nd pattern ld (hl),c inc hl ld (hl),b ; 2nd pattern set up dec hl ; start for move ld bc,0c00h ; 3k left to fill ldir ; fill it ; pop bc ;restore pointer to buffer area ld (IX+HSTBUF),c ;set dma addr. for disk I/O ld (IX+HSTBUF+1),b ;set dma address ret ; ; ;WRTBUF sets up the error flag for disk I/O, and writes the buffer to disk. ; wrtbuf: ld de,wrtmsg ; print Writing... call mesg ld (IX+ERFLAG),0FFh ; set type of error handling call wrthst ; write the data call tsterr ; see if any errors, if so inc errors ret ; else return to menu ; ;RDBUF does disk reads of a worst case pattern from track 39 of the selected ; drive. If CRC errors occur, they are logged, and a record is printed out ; after each pass of the read. ; rdbuf: ld de,rdmsg ; print reading... call mesg ld (IX+ERFLAG),0ffh ; set type of error handling call rdhst ; read the data call tsterr ; see if any errors, if so inc errors call prntpass ret ; else return to menu ; ; tsterr: ld a,(IX+ERFLAG) ; check returned error status cp 0 ; see if no errors call nz,incerr ; else, increment error count ret ; ;SEEKTEST performs a 'butterfly' seektest continually until a key is ;pressed on the keyboard. ; seektest: call setup ; get disk to test, and select it ld b,0 ; starting outer track ld c,39 ; starting inside track ld (IX+HSTBUF),0 ; set DMA address for command ld (IX+HSTBUF+1),80h ; at 8000h (arbitrary address) ld (IX+SECCNT),1 ; only 1 sector needs to be read call clrerr ; set error count to 0 seeklp: push bc ; save seek ranges call tstseek ; seek the two tracks pop bc ; get ranges back call nxtrng ; calculate next range call keystat ; check for key input jr z,seeklp ; if no key hit, test again ret ; ; NXTRNG takes the b-c registers and adjusts them for the next track to seek ; to. For the butterfly pattern, the b register is incremented, and the c ; register is decremented. If the c register is decremented past zero, then ; it is time to change the direction for the seek. This is done by swapping ; the bc registers, and then re-adjusting them again. ; nxtrng: inc b ; increment track dec c ; decrement track jp m,swapbc ; if underflow, then it's time to swap ret ; otherwise, adjustment is done ; swapbc: ld a,b ; swap b-c pair, and readjust ld b,c ld c,a push bc ; save range call prntpass ; print pass info only after complete ; pass has been made. inc # of passes pop bc ; get back range values jr nxtrng ; swap done, so re-adjust bc ; ; ;TSTSEEK seeks two tracks, keeping track of errors during the seek. If ;any errors occurred, then the error count is incremented. The tracks ;seeked are in the b-c pair upon entry. ; tstseek: push bc ; save tracks being seeked ld (IX+HSTTRK),b ; set first track to seek ld (IX+ERFLAG),0ffh ; set for error reporting/ no handling call rdhst ; read the sector call tsterr ; increment error count if necessary pop bc ; get other track to seek ld (IX+HSTTRK),c ; set other track to seek ld (IX+ERFLAG),0ffh ; set type of error handling call rdhst ; read the other sector call tsterr ; inc error count if necessary ret ; ; ;VFOTEST tries to read track zero continually until a key is hit on the ;keyboard. It is used to check the VFO to see if it's in range. When ;performing this test, error handling is completely turned off. ; vfotest: call xfrdata ; move ramdatx, ramdaty xor a ld (IX+HSTDSK),a ; select drive 'A' ld (IX+HSTTRK),a ; track zero ld (IX+SECCNT),1 ; set for one sector ld (IX+HSTBUF),0 ; set DMA address ld (IX+HSTBUF+1),80h vfoadj: ld (IX+ERFLAG),0feh ; turn off error handling call rdhst ; do a read call keystat ; look for a key jr z,vfoadj ; keep reading until a key is hit ret ; ; ; diagboot: pop af ; bump a subroutine level ret ; ; ; This section of code is run at FC00h!!!! ; ; block: .phase diskbuf ; stblk: in a,(romctl) ; turn off rom ld a,(00) ; get value at loc. 0 cp 0c3h ; test if it's jp op code out (romctl),a ; turn on rom... jr nz,test ; if jp then warm boot jp warm ; and boot!!!! ; else, start testing system test: ld de,tstmsg ; point to test msg. call mesg ; print it ld hl,0 ; low addr to test ld bc,diskbuf-1 ; highest to test call memtest ; test memory or a ; test flags jp nz,memerr ; if memfail, then jump ld de,romok ; point to memory ok message call mesg ; and print it jp warm ; if ok, then go ; ; Subroutine: MEMTEST ; ; Function: This routine performs a memory test on a block of memory. ; The block of memory may be of any size, and is determined ; by the values passed upon entry. Š; When called, register pair HL points to the start of the ; block to be tested, and register pair BC points to the last ; location to be tested. When an error is encountered, the ; routine will return a non-zero value in the accumulator. ; When no errors are found, the routine will return with ; the accumulator equal to zero. ; memtest: in a,(romctl) ; turn off ROM ld (smem),hl ; save start pointer ld hl,patlst ; point to test table testlp: ld d,(hl) ; get data to test inc hl bit 7,(hl) ; check if end of table jr nz,pass ; if so, then passed ld e,(hl) ; get offset(0,1,2)& rd/wr bit 6,e ; test if read or write res 6,e ; reset the bit inc hl push hl ; save table pointer jr z,fill ; 0==> fill memory call check ; 1==> check memory jr over fill: ld ix,wrmem ; calculate start call testm ; test memory over: pop hl ; restore table pointer jr testlp ; loop while not done pass: xor a ; set accumulator to pass out (romctl),a ; turn ROM back on ret ; return success check: ld ix,rdmem ; set for read testm: ld hl,(smem) ; get where to start LD A,E ; get offset from start ADD A,L ; compute actual starting LD L,A ; address ; hl<==hl+offset LD A,H ; ADC A,0 LD H,A ; lp: call indjp ; indirect jump via ix ; INC HL ; skip two bytes INC HL INC HL LD A,B CP H RET C jr NZ,lp ; if top not reached, keep going LD A,C CP L RET C jr lp ; indjp: jp (ix) ; perform jump to wr or rd ; wrmem: LD (HL),D ; write test value ret ; rdmem: ld a,(hl) ; get test value cp d jr nz,err ret ; ERR: out (romctl),a ; made error, so turn on ROM in a,(0f5h) ; see if in diagnostics mode bit 5,a call z,outhl ; output full error msg if in ; diagnostics mode pop af pop af pop af LD A,0FFH ret ; ; Subroutine: OUTHL ; ; This routine prints out the memory location at which the error occured, ; followed by the data read from RAM, and what it expected to read. ; outhl: push de ld de,readmsg ; point to message call mesg ; print it in a,(romctl) ; get ROM out of the way ld a,(hl) ; get value read out (romctl),a ; turn ROM back on call outbyte ; output it in hex ld de,expctmsg ; point to message call mesg ; print it pop de ; get back expected value ld a,d call outbyte ; print expected value in hex ld de,rammsg ; point to ram error mesg call mesg ; print it, ld a,h ; get high byte of address call outbyte ; print it ld a,l ; get low byte of address call outbyte ; print it ret ; ; Subroutine: OUTBYTE ; ; This will print the hex value of the accumulator. ; outbyte: push af ; save value rrca ; get upper nibble rrca rrca rrca and 0fh ; mask off the rest call outasc ; print ascii hex digit pop af ; get value back and 0fh ; mask call outasc ; print ascii hex digit ret ; ; Subroutine: OUTASC ; ; This routine outputs the lower nibble of the accumulator as a hex digit. ; outasc: cp 0ah ; see if digit or alpha jp m,num ; jump if digit add a,07h ; adjust for alpha num: add a,30h ; convert to ascii ld c,a call outcn ; print it ret ; ; ; ; ; ; ; ; MEMORY PATTERN TEST TABLE ; ; format is: ; 1st byte -- value to use (0-ffh) ; 2nd byte -- bit 6 r/w flag 0 => write ; 1 => read ; bits [0,1] offset (0,1,2) ; ; patlst: db 0ffh,wr or 0 db 00,wr or 1 db 0ffh,wr or 2 db 00,rd or 1 db 0ffh,rd or 2 db 0ffh,rd or 0 db 00,wr or 0 db 0ffh,wr or 1 db 00,wr or 2 db 00,rd or 2 db 00,rd or 0 db 0ffh,rd or 1 db 0aah,wr or 0 db 55h,wr or 2 db 0aah,wr or 1 db 0aah,rd or 1 db 55h,wr or 1 db 0aah,rd or 0 db 55h,wr or 0 db 55h,rd or 2 db 55h,rd or 1 db 55h,rd or 0 db 00,80h ; msb=1 ==> end of pattern list ; SMEM equ $ endblk: ; .dephase ; ; ----------------- Messages --------------- SKM: DC 'Wrong track.' DB 0 ; WPM: DC 'Write protected.' DB 0 ; CRM: DC 'Data error.' DB 0 ; URM: DC 'Not found.' DB 0 ; SYM: DC 'Lost data.' DB 0 ; NRM: DC 'Drive not ready.' DB 0 ; SSM EQU NRM ; UNM: DC 'Unknown error.' DB 0 ; DMESG: DW 0A0DH DC 'Disk error on drive ' DB 0 ; ; RESM: DW 0A0DH DC 'Type R to try again, A to abort, or I to ignore: ' DB 0 ; BTERR: DB 0AH DB 'Error on CP/M system diskette.',0 RERR: DW 0A0DH DC 'Push reset to try again. ' DB 0 ; ; signon: db cr,'Micro-Decision -- ROM Rev. ' db ((rev and 0f0h) shr 4)+asc0,'.',(rev and 0fh)+asc0,'A',cr,lf db 'Copyright 1982, 1983 Morrow Designs, Inc.',cr,lf,lf,0 ; tstmsg: db 'Testing memory -- please stand by: ',0 romok: db 'Memory O.K.',cr,lf,lf,0 bootmsg: db 'Insert CP/M system diskette in Drive A' rtrnmsg: db ' and press [RETURN] ',bell,0 rommsg: db bell,bell,'ROM Memory error',0 rambad: db bell,bell,'RAM Memory error',0 readmsg: db bell,bell,cr,lf,'Read ',0 expctmsg: db ' expected ',0 rammsg: db ' at RAM location ',0 diagmsg: db cr,lf,'Diagnostics Menu',cr,lf,lf db '1) Port 1 \',cr,lf db '2) Port 2 > Barber-Pole pattern test',cr,lf db '3) Centronics port /',cr,lf db '4) Loop back on port 2',cr,lf db '5) RAM test',cr,lf db '6) FDC R/W',cr,lf db '7) FDC seek test',cr,lf db '8) VFO test',cr,lf db '9) Boot',cr,lf,lf db 'Enter #:',0 passmsg: db cr,lf,'End of pass ',0 errnum: db ' errors so far.',0 loopmsg: db 'loop test.',0 failed: db cr,lf,'Failed ',0 passed: db cr,lf,'Passed ',0 drvmsg: db 'Enter drive to test (A-D):',0 insrt: db cr,lf,'Insert a formatted diskette in drive ',0 wrtmsg: db cr,lf,'Writing...',0 rdmsg: db 'reading',0 CRLF: DW 0A0DH DB 0 ; ; COLN: DC ': ' DB 0 Š; ;*********************--DATA TABLES--************************ ; ; ROMDATX: RHSTDV: DB 0 RHSTTK: DB 0 RHSTSC: DB 1 RSECCT: DB 1 RRETRY: DB 20 RHSTBF: DW BOOTBF RERFLG: DB 0 RPHYTK: DB 0 RPHYHD: DB 0 RIOADD: DW RDIO RSECSZ: DW 0 RSTADD: DW 0 DW 0 DW 0 DB 0 RCMDCT: DB 9 RCMDBF: DB 46H DB 0 DB 0 DB 0 DB 1 DB 3 DB 5 DB 28 DB 0FFH ; IYOFF EQU $-ROMDATX ; ROMDATY: RSEKDK: DB 0 RSEKTK: DB 0 RSEKSC: DB 0 ; RSEKHT: DB 0 ; RUNACT: DB 0 RUNADK: DB 0 RUNATK: DB 0 RUNASC: DB 0 RUNAMX: DB 0 RSECTK: DB 0 ; RWRTYP: DB 0 RDFLAG: DB 0 RTRSEC: DB 0 ; RVMSGP: DW 0 RVDRVP: DW 0 ; ŠRCDSK: DB 0FFH RPDSK: DB 0 RVDSK: DB 0 ; RDMADR: DW 0 ROUTP: DW CNOUT RINP: DW CNIN ; ; MTAB contains one 9 byte entry for each logical drive. ; The bytes of each entry are defined as follows: ; ; Byte 0 DSKDEF0: ; Bit 0-2 Motor control bit ; Bit 3-4 Double sided mode: ; 00=Even tracks on side 0, ; Odd tracks on side 1. ; 01=1st 40 (or 80) tracks ; on side 0, remaining ; tracks on side 1. ; 10=Both sides are treated ; as a single track with ; twice as many sectors. ; Bit 5-7 Unused. ; ; Byte 1 DSKDEF1: ; Bit 0-1 Physical drive address. ; Bit 2 Double sided drive if = 1. ; Bit 3-4 Sector size: ; 00=128 ; 01=256 ; 10=512 ; 11=1024. ; Bit 5 Tracks: 0=40; 1=80. ; Bit 6 Density: 0=single; 1=double. ; Bit 7 Virtual drive: 1=virtual. ; ; Byte 2 Motor on wait time in increments of 4 ms. ; ; Byte 3 Head settle time (after seek) in increments ; of 4 ms. ; ; Byte 4-5 The two parameter bytes for the FDC specify ; command: Byte 4 = SRT/HUT ; Byte 5 = HLT/ND ; ND must be 1. ; ; Byte 6 EOT byte for FDC read or write commands. ; ; Byte 7 GPL byte for FDC read or write commands. ; ; Byte 8 Current track. ; RMTAB: ; Drive 1 parameter table DB 1 Š DB 0D8H DB 88 DB 5 DB 6FH DB 3 DB 5 DB 28 DB 0FFH ; Drive 2 parameter table DB 2 Š DB 59H DB 88 DB 5 DB 6FH DB 3 DB 5 DB 28 DB 0FFH ; Drive 3 parameter table DB 4 Š DB 5AH DB 88 DB 5 DB 6FH DB 3 DB 5 DB 28 DB 0FFH ; Drive 4 parameter table DB 4 Š DB 5BH DB 88 DB 5 DB 6FH DB 3 DB 5 DB 28 DB 0FFH ; DATLNG EQU $-ROMDATX ; ; ETAB: DW WPM DW SKM DW CRM DW CRM DW URM DW SSM DW SYM DW NRM DW UNM ; ; ÄB romchë ;ROÍ CHECË SUÍ BYTE ; DS (ROM+0FFFH-$),0FFH ; END