;+
;
; 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*<BIT RATE>/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-<GAP4B+<SWIM*7>+GAP1+<NSECS*<6+1+4+2+GAP2+1+SECLEN+2+GAP3>>>
;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 <C>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+<TOTAL/10>,(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+<TOTAL/10>-<NSECS/2>,@#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/<CR><LF>
;
PROMPT: .ASCIZ <CR><LF>/PRESS <RETURN> TO FORMAT NEXT DISK, ^C TO HALT: /
;
RULER: .BYTE CR,LF
.ASCII /[....!..../ ;HARD-CODED FOR 35 TRACKS. SORRY.
.ASCII /1....!..../
.ASCII /2....!..../
.ASCIZ /3...]/<CR><LF>
;
; 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 <CR><LF>/FORMAT SUCCESSFUL/<CR><LF>
BREAK: .ASCIZ <CR><LF><BEL>/? FORMAT ABORTED/<CR><LF>
WERR: .ASCIZ <CR><LF><BEL>/? WRITE ERROR - /
RERR: .ASCIZ <CR><LF><BEL>/? 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 <BS>/?/ ;OVERPRINT A ? WHILE RETRYING READS
NUM: .ASCIZ <BS>/#/ ;SECTOR NUMBER BEING READ
STAR: .ASCIZ <BS>/*/ ;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/<CR><LF>
WRTPRT: .ASCIZ /WRT PRT/<CR><LF>
WRTFLT: .ASCIZ /WRT FLT/<CR><LF>
RNTFND: .ASCIZ /REC NOT FND/<CR><LF>
CRCERR: .ASCIZ /CRC ERR/<CR><LF>
LSTDAT: .ASCIZ /LST DAT/<CR><LF>
;
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+<TOTAL/10> ;WRITE TRACK/READ SECTOR BUFFER
;
.END START ;(START ADDRESS WON'T WORK WITH NIMAL'S LOADER)