;+ ; ; PROGRAM TO FORMAT DISKS FOR 35.477 COMPUTER HARDWARE DESIGN LAB #5. ; ; LOAD THIS PROGRAM USING NIMAL'S LOADER AND THEN "S 1000". ; ; DOUG SHOAF AND JOHN WILSON LAB #5 UVAX CONVERSION, SPRING 1992. ; ;- .PSECT ABS,LONG ;ADDRESSES ARE ABSOLUTE .DSABL GBL ;UNDEFINED SYMBOLS CAUSE ERROR ; ; DISK GEOMETRY. ; ; DISKS ARE SINGLE-SIDED, SINGLE-DENSITY, 5.25" 35-TRACK DRIVES (SHUGART SA400 ; OR SIMILAR). THIS IS A VERY OLD FORMAT, MOST 5.25" DRIVES SINCE THE SA400L ; (UPGRADED SA400) HAVE 40 OR 80 TRACKS, AND USE DOUBLE OR QUAD DENSITY, ; WHICH IS A DIFFERENT DATA FORMAT (MFM) AT TWICE OR FOUR TIMES THE BIT RATE. ; ; OUR DATA RATE IS 125,000 BITS PER SECOND AND THE DISK TURNS AT 300 RPM. ; NTRKS= 35 ;NUMBER OF TRACKS PER DISK (40 ON TM100-1) NSECS= 16 ;NUMBER OF SECTORS PER TRACK SECLEN= 128 ;SECTORS ARE 128 BYTES ; ; FORMAT CONSTANTS: ; TOTAL= 3125 ;TOTAL BYTES PER TRACK INCLUDING FORMAT ;=7.5*/RPM, BIT RATE IS 125KHZ, 5.25"=300 RPM GAP4B= 34 ;FF'S BETWEEN PHYSICAL INDEX HOLE AND INDEX MARK (FC) SWIM= 0 ;1 TO ACTUALLY WRITE SOFTWARE INDEX MARK (FC) ;(NORMALLY USED BUT WD1771 DOESN'T REQUIRE IT) GAP1= 26 ;FF'S BETWEEN INDEX AND FIRST ID RECORD ADDRESS MARK ; ID RECORD COMES NEXT: TRACK (0-34), SIDE (0), SECTOR (1-16), LENGTH CODE (0) GAP2= 17 ;00'S BETWEEN ID RECORD AND DATA RECORD ADDRESS MARK ; DATA RECORD COMES NEXT: SECLEN BYTES OF DATA (BLANK DISKS USE SECLEN*E5) GAP3= 15 ;FF'S BETWEEN DATA RECORD AND NEXT ID RECORD GAP4A= TOTAL-+GAP1+>> ;FF'S BETWEEN LAST SECTOR AND NEXT PHYSICAL INDEX ; ; DEVICE REGISTERS. ; FDCSR= ^X20001E78 ;COMMAND/STATUS REGISTER FDCSRH= FDCSR+1 ;HIGH BYTE OF CSR FDTRK= FDCSR+2 ;WD 1771 TRACK REGISTER (BYTE) FDSEC= FDCSR+4 ;WD 1771 SECTOR REGISTER (BYTE) FDDAT= FDCSR+6 ;WD 1771 DATA REGISTER (BYTE) ; VEC$A= ^X200+^O260 ;INTR "A" VECTOR VEC$B= ^X200+^O264 ;INTR "B" VECTOR ; ; INTERNAL PROCESSOR REGISTER NUMBERS, ACCESSED WITH MTPR/MFPR INSTRUCTIONS. ; PR$_SCBB=^X11 ;SYS CTRL BLK BASE (INIT TO 0) PR$_IPL=^X12 ;INTR PRIO LEVEL (INIT TO ^X13, FDC INTS AT LEVEL ^X14) ; ; THE NEXT FOUR PROCESSOR REGISTERS DON'T EXIST ON ALL VAXEN BUT KA640 HAS 'EM. ; THEY ARE USED TO ACCESS THE CONSOLE TERMINAL UART. ; RXCS= ^X20 ;CONSOLE UART RX CSR (BIT 7 = READY) RXDB= ^X21 ;CONSOLE UART RX DATA BUF (LOW BYTE = CHAR) TXCS= ^X22 ;CONSOLE UART TX CSR (BIT 7 = READY) TXDB= ^X23 ;CONSOLE UART TX DATA BUF (LOW BYTE = CHAR) ; ; ASCII CHARACTERS. ; CTRLC= ^O3 ;^C BEL= ^O7 ;BELL BS= ^O10 ;BACKSPACE LF= ^O12 ;LINE FEED CR= ^O15 ;CARRIAGE RETURN ; .= ^X1000 ;STARTING ADDRESS ; ; INITIALIZE FOR FORMATTING ; START: MOVL #STACK,SP ;;SET UP STACK CLRB @#FDCSRH ;;REMOVE FIFO FROM DATA PATH, DISABLE INTS MOVL #INTA,@#VEC$A ;;SET INTR "A" VECTOR (ALWAYS THE SAME) MTPR #0,#PR$_SCBB ;;SET SCB BASE TO 0 MTPR #^X13,#PR$_IPL ;;SET PRIORITY TO ^X13 TO ALLOW DISK INTS MOVL #BANNER,R0 ;IDENTIFY THE PROGRAM JSB @#PRINT ;(WHAT DO YOU *THINK* IT DOES?!) NXTDSK: ; FORMAT NEXT DISK JSB @#RECAL ;SEEK TRACK 0 MOVL #PROMPT,R0 ;PROMPT JSB @#PRINT 10$: JSB @#GETC ;TRY TO GET A CHARACTER FROM THE KEYBOARD BCS 10$ ;LOOP IF NONE CMPB R0,#CR ;CR? BEQL 20$ ;YES, SKIP CMPB R0,#CTRLC ;^C? BNEQ 10$ ;IGNORE IF NOT HALT ;YES, HALT BRB NXTDSK ;RESTART IF ONTINUED FROM CONSOLE 20$: CLRL R9 ;INIT CURRENT TRACK NUMBER MOVL #RULER,R0 ;RULER JSB @#PRINT ; NEXT: ; IF THEY TYPE ^C THEN ABORT JSB @#GETC ;SEE IF THEY TYPED A CHARACTER BCS NXTTRK ;NOPE, SKIP CMPB R0,#CTRLC ;^C? BNEQ NEXT ;NO, GET NEXT CHAR MOVL #BREAK,R0 ;POINT AT MSG BRW ABORT ;GO ABORT NXTTRK: ; OTHERWISE FORMAT NEXT TRACK MOVL #DOT,R0 ;PRINT DOT JSB @#PRINT JSB @#SEEK ;SEEK TO CURRENT TRACK MOVL #2,R10 ;RETRY COUNT 10$: ; INITIALIZE DATA FOR WRITE-TRACK COMMAND ; THE MOVC INSTRUCTIONS ALL LEAVE R3 POINTING TO THE BYTE FOLLOWING ; THE LAST BYTE WRITTEN, SO WE WILL USE R3 AS THE POINTER SO THAT ; IT UPDATES AUTOMATICALLY. MOVC TRASHES R0-R2, R4-R5 IN OTHER WAYS. MOVL #BUFFER,R3 ;POINT WITH R3 MOVC5 #0,(R3),#^XFF,#GAP4B,(R3) ;WRITE 2ND PART OF GAP 4 .IF NE SWIM ;WRITE SOFTWARE INDEX MARK IF SELECTED MOVC5 #0,(R3),#0,#6,(R3) ;6 00'S MOVB #^XFC,(R3)+ ;INDEX MARK .ENDC ; NE SWIM MOVC5 #0,(R3),#^XFF,#GAP1,(R3) ;WRITE GAP 1 MOVL #1,R6 ;INIT CURRENT SECTOR NUMBER 20$: ; WRITE DATA FOR EACH SECTOR MOVC5 #0,(R3),#0,#6,(R3) ;6 00'S (SYNC) MOVB #^XFE,(R3)+ ;ID ADDRESS MARK MOVB R9,(R3)+ ;TRACK NUMBER CLRB (R3)+ ;HEAD NUMBER (ALWAYS 0 FOR SS) MOVB R6,(R3)+ ;SECTOR NUMBER .IF EQ SECLEN-128 CLRB (R3)+ ;LENGTH CODE (0=128 BYTES) .ENDC .IF EQ SECLEN-256 MOVB #1,(R3)+ ;LENGTH CODE (1=256 BYTES) .ENDC .IF EQ SECLEN-512 MOVB #2,(R3)+ ;LENGTH CODE (2=512 BYTES) .ENDC .IF EQ SECLEN-1024 MOVB #3,(R3)+ ;LENGTH CODE (3=1024 BYTES) .ENDC MOVB #^XF7,(R3)+ ;HEADER CRC (ACTUALLY WRITES 2 BYTES) MOVC5 #0,(R3),#0,#GAP2,(R3) ;GAP 2 (00'S BETWEEN HEADER AND DATA) MOVB #^XF8,(R3)+ ;DATA ADDRESS MARK ; DATA VALUES MUST BE LESS THAN F7 (IBM 3740 FORMAT USES E5), ; SINCE OTHER VALUES ARE CONVERTED TO MARKS 'N STUFF BY THE 1771 MOVL R3,R7 ;SAVE ADDRESS OF DATA FIELD MOVC5 #0,(R3),#^A' ',#SECLEN,(R3) ;INIT DATA TO BLANKS MOVL R3,R8 ;SAVE R3 (TRASHED BY MOVC'S BELOW) ; PUT A MESSAGE IN THE DATA FIELD SAYING WHICH SECTOR IT IS MOVC3 #6,@#TRACK,(R7) ;"TRACK " (NEXT ADDRESS IS IN R3) MOVZBL R9,R0 ;GET CURRENT TRACK JSB @#PUTN ;CONVERT TO DECIMAL MOVC3 #9,@#SECTOR,(R3) ;", SECTOR " MOVL R6,R0 ;GET CURRENT SECTOR JSB @#PUTN ;CONVERT TO DECIMAL MOVL R8,R3 ;RESTORE R3 TO POINT AFTER DATA FIELD MOVB #^XF7,(R3)+ ;DATA CRC (ACTUALLY WRITES 2 BYTES) MOVC5 #0,(R3),#^XFF,#GAP3,(R3) ;GAP 3 (FF'S BETWEEN DATA AND ; NEXT HEADER) INCL R6 ;BUMP TO NEXT SECTOR NUMBER CMPL R6,#NSECS ;DONE ALL SECTORS? BLEQU 20$ ;LOOP IF NOT MOVC5 #0,(R3),#^XFF,#GAP4A+,(R3) ;GAP 4 PLUS 10% TO ALLOW ; FOR VARIATIONS IN MOTOR SPEED ; WRITE THE TRACK TO THE DISK MOVL #INTB$W,@#VEC$B ;SET INTR VECTOR TO "WRITE DATA" ROUTINE MOVL #BUFFER,@#POINTR ;SET DMA ADDRESS MOVL #TOTAL+-,@#COUNTR ;SET DMA COUNT ;(-NSECS/2 SINCE EACH SECTOR HAS 2 F7'S WHICH ;ARE 2 BYTES ON THE DISK BUT WE WRITE ONLY 1 ;BYTE TO THE 1771) CLRB @#DONE ;NOT DONE YET MOVW #^XF8F4,@#FDCSR ;FIFO=1,FFD=1,FIFO-RESET=1,ENA=1,ENB=1,WRT TRK 30$: TSTB @#DONE ;HAS "DONE" INT HAPPENED? BEQL 30$ ;SPIN UNTIL IT DOES BITB #^XE4,@#FDCSR ;ERRORS? BEQL 35$ ;NO BRW WTERR ;YES 35$: ; NOW READ EVERY SECTOR TO MAKE SURE THEY'RE ALL READABLE MOVL #INTB$R,@#VEC$B ;RESET INTR "B" VECTOR FOR READING CLRL R6 ;SECTOR NUMBER FROM SECTAB 40$: MOVL #5,R7 ;RETRY COUNT MOVB L^SECTAB(R6),R5 ;GET LOGICAL SECTOR NUMBER ADDB3 R5,#^A'A'-1,@#NUM+1 ;GET LETTER REPRESENTING SECTOR MOVL #NUM,R0 ;PRINT NUMBER OF SECTOR WE'RE READING JSB @#PRINT 50$: ; TRY TO READ NEXT SECTOR MOVL #BUFFER,@#POINTR ;BUFFER ADDRESS MOVL #SECLEN,@#COUNTR ;LENGTH CLRB @#DONE ;NOT DONE YET MOVB R5,@#FDSEC ;SET SECTOR REGISTER MOVW #^XB88C,@#FDCSR ;FIFO=1,FFD=0,ENA=1,ENB=1,FIFO-RESET=1,READ 60$: TSTB @#DONE ;DONE? BEQL 60$ ;SPIN UNTIL SO BITB #^X9C,@#FDCSR ;ERRORS? BEQL 90$ ;NO ; ERROR READING, RETRY MOVL #QUES,R0 ;REPLACE DOT WITH QUESTION MARK JSB @#PRINT DECL R7 ;FINISHED RETRYING? BEQL 80$ ;YES, HARD ERROR MOVB #^XD0,@#FDCSR ;ABORT (FORCE INTERRUPT) MOVL #RUBS,R0 ;HACK -- SEND A COUPLE OF RUBOUTS JSB @#PRINT ;(THIS GUARANTEES A >=16 USEC DELAY) TSTB @#FDCSR ;GET STATUS (CLEAR INTRQ) JSB @#RECAL ;RECALIBRATE DRIVE (SEEK TRACK 0) JSB @#SEEK ;STEP BACK OUT TO OUR TRACK BRB 50$ ;TRY AGAIN 80$: DECL R10 ;RETRIED WRITING YET? BEQL RSERR ;YES, GIVE UP BRW 10$ ;NO, LOOP 90$: ; READ SECTOR OK INCL R6 ;SECTOR +1 CMPL R6,#NSECS ;DONE ALL? BGEQU 95$ ;YES BRW 40$ ;LOOP IF NOT 95$: ; TRACK FINISHED MOVL #STAR,R0 ;PRINT A STAR JSB @#PRINT ; SEE IF WE'RE DONE INCL R9 ;MOVE TO NEXT TRACK CMPL R9,#NTRKS ;DONE? BGEQU 100$ ;YES BRW NEXT ;NO, LOOP 100$: MOVL #HAPPY,R0 ;FORMAT SUCCESSFUL ABORT: JSB @#PRINT BRW NXTDSK ;AROUND FOR MORE ; WTERR: ; ERROR WRITING TRACK MOVL #WERR,R0 ;POINT AT MESSAGE BICB3 #^X1B,@#FDCSR,R3 ;GET ERROR FLAGS MOVL #WERTAB,R4 ;GET TABLE OF MEANINGS BRB ERROR ; RSERR: ; ERROR READING SECTOR MOVL #RERR,R0 ;POINT AT MESSAGE BICB3 #^X63,@#FDCSR,R3 ;GET ERROR FLAGS MOVL #RERTAB,R4 ;GET TABLE OF MEANINGS ;BRB ERROR ; ; HANDLE FDC ERRORS. ; R3 ERROR BITS FROM FDCSR ; R4 TABLE OF .LONG POINTERS TO MESSAGE FOR EACH POSSIBLE ; BIT OF R3 (STARTING WITH BIT 7) ; ERROR: JSB @#PRINT ;PRINT MESSAGE 10$: MOVL (R4)+,R0 ;GET NEXT POINTER TSTB R3 ;IS THIS THE ERROR? BLSS ABORT ;YES, PRINT MSG AND PUNT ASHL #1,R3,R3 ;LEFT 1 BRB 10$ ;KEEP LOOKING ; ; ROUTINE TO RECALIBRATE DRIVE. ; RECAL: MOVB #^X03,@#FDCSR ;RECAL COMMAND BRB POLL ;GO POLL UNTIL IT'S DONE ; ; ROUTINE TO SEEK TO CURRENT TRACK. ; SEEK: MOVB R9,@#FDDAT ;LOAD TRACK NUMBER MOVB #^X13,@#FDCSR ;SEEK COMMAND POLL: ; BRIEF DELAY TO GIVE THE "BUSY" BIT TIME TO TURN ON MOVL #20,R0 ;WAIT LOOP COUNT 10$: SOBGTR R0,10$ ;SIT 'N SPIN ; WAIT FOR THE "BUSY" BIT TO TURN OFF 20$: MOVB @#FDCSR,R0 ;READ STATUS BLBS R0,20$ ;WAIT UNTIL LOW BIT CLEARS RSB ; ; INTR A ("OPERATION COMPLETE") INTERRUPT SERVICE ROUTINE. ; .ALIGN LONG ;LOW 2 BITS OF ADDRESS MUST BE 0 INTA: CLRB @#FDCSRH ;;DISABLE ALL FURTHER DISK INTS MOVB #1,@#DONE ;;SET "DONE" FLAG REI ; ; INTR B ("FIFO READY") ISR FOR READING FROM FIFO. ; .ALIGN LONG INTB$R: MOVL R0,-(SP) ;;SAVE R0 MOVL @#POINTR,R0 ;;FETCH POINTER MOVB @#FDDAT,(R0)+ ;;READ A BYTE MOVL R0,@#POINTR ;;POINT AT NEXT BYTE MOVL (SP)+,R0 ;;RESTORE DECL @#COUNTR ;;DECREMENT COUNT BEQL NOMORE ;;0, NO MORE INTS ; BITB #2,@#FDCSRH ;;"FIFO OR" BIT SET? ; BNEQ INTB$R ;;YES, READ ANOTHER BYTE REI ; ; INTR B ("FIFO READY") ISR FOR WRITING TO FIFO. ; .ALIGN LONG INTB$W: MOVL R0,-(SP) ;;SAVE R0 MOVL @#POINTR,R0 ;;FETCH POINTER MOVB (R0)+,@#FDDAT ;;WRITE A BYTE MOVL R0,@#POINTR ;;POINT AT TO NEXT BYTE MOVL (SP)+,R0 ;;RESTORE DECL @#COUNTR ;;DECREMENT COUNT BEQL NOMORE ;;0, NO MORE INTS BITB #1,@#FDCSRH ;;"FIFO IR" BIT SET? BNEQ INTB$W ;;YES, WRITE ANOTHER BYTE REI ; NOMORE: ; BYTE COUNT EXHAUSTED, NO MORE INTS NEEDED (FROM INTB$R OR INTB$W) BICB #8,@#FDCSRH ;;CLEAR "INTR ENB" BIT REI ; ; READ A CHARACTER IF ONE IS AVAILABLE. ; OTHERWISE RETURN C=1. ; GETC: MFPR #RXCS,R0 ;GET STATUS TSTB R0 ;IS A CHAR READY (C CLEARED)? BGEQ 10$ ;NO MFPR #RXDB,R0 ;YES, GET THE CHAR MOVZBL R0,R0 ;CLEAR HIGH 24 BITS RSB 10$: BISPSW #1 ;SET C RSB ; ; PRINT .ASCIZ STRING AT (R0). ; PRINT: MOVZBL (R0)+,R1 ;GET NEXT CHAR BEQL 20$ ;END OF STRING, RETURN 10$: MFPR #TXCS,R2 ;GET STATUS TSTB R2 ;READY? BGEQ 10$ ;LOOP IF NOT MTPR R1,#TXDB ;WRITE THE CHAR BRB PRINT ;LOOP 20$: RSB ; ; CONVERT NUMBER IN R0 TO A 1- OR 2-DIGIT DECIMAL NUMBER AT (R3)+. ; PUTN: CLRL R1 ;ZERO-EXTEND EDIV #10,R0,R0,R1 ;DIVIDE BY 10, R0=QUOTIENT, R1=REMAINDER BEQL 10$ ;R0=0 ADDB3 #^A'0',R0,(R3)+ ;FIRST DIGIT IF NON-ZERO 10$: ADDB3 #^A'0',R1,(R3)+ ;SECOND DIGIT REGARDLESS RSB ; BANNER: .BYTE CR,LF .ASCIZ /35.477 CHD LAB 5 DISK FORMATTING PROGRAM/ ; PROMPT: .ASCIZ /PRESS TO FORMAT NEXT DISK, ^C TO HALT: / ; RULER: .BYTE CR,LF .ASCII /[....!..../ ;HARD-CODED FOR 35 TRACKS. SORRY. .ASCII /1....!..../ .ASCII /2....!..../ .ASCIZ /3...]/ ; ; SECTOR INTERLEAVE TABLE. THIS ENABLES US TO READ SECTORS BACK FOR ; VERIFICATION JUST AS THEY'RE PASSING UNDER THE HEAD, MUCH FASTER ; THAN READING THEM 1..NSECS. ; SECTAB: .BYTE 5,8,11,14,1,4,7,10 ;3:1 INTERLEAVE, SKEW=5 .BYTE 13,16,3,6,9,12,15,2 ;HARD-CODED FOR 16 SECTORS. SORRY. ; HAPPY: .ASCIZ /FORMAT SUCCESSFUL/ BREAK: .ASCIZ /? FORMAT ABORTED/ WERR: .ASCIZ /? WRITE ERROR - / RERR: .ASCIZ /? READ ERROR - / ; TRACK: .ASCII /TRACK / ;MESSAGE TO GO IN EACH DATA FIELD SECTOR: .ASCII /, SECTOR / ; DOT: .ASCIZ /./ ;PRINT A DOT FOR EACH TRACK BEGUN QUES: .ASCIZ /?/ ;OVERPRINT A ? WHILE RETRYING READS NUM: .ASCIZ /#/ ;SECTOR NUMBER BEING READ STAR: .ASCIZ /*/ ;OVERPRINT A STAR FOR EACH TRACK FINISHED ; WERTAB: .LONG NOTRDY,WRTPRT,WRTFLT,0,0,LSTDAT RERTAB: .LONG NOTRDY,0,0,RNTFND,CRCERR,LSTDAT ; NOTRDY: .ASCIZ /NOT RDY/ WRTPRT: .ASCIZ /WRT PRT/ WRTFLT: .ASCIZ /WRT FLT/ RNTFND: .ASCIZ /REC NOT FND/ CRCERR: .ASCIZ /CRC ERR/ LSTDAT: .ASCIZ /LST DAT/ ; RUBS: .BYTE ^O177,^O177,0 ;RUBOUTS FOR DELAY ; POINTR: .LONG ;POINTER TO NEXT BYTE TO READ OR WRITE COUNTR: .LONG ;COUNTER OF BYTES REMAINING TO READ OR WRITE DONE: .BYTE ;FLAG - NON-ZERO => DONE WITH OPERATION ; .BLKL ^X100 ;1KB STACK IS WAY MORE THAN ENOUGH STACK= . ;INITIAL VALUE OF SP ; BUFFER: .BLKB TOTAL+ ;WRITE TRACK/READ SECTOR BUFFER ; .END START ;(START ADDRESS WON'T WORK WITH NIMAL'S LOADER)