.xlist INCLUDE MON.180 .list ;***************************************************************************** ;* * ;* PROGRAM TO CONTROL THE TRACK POSITION, ETC. OF 5 1/4 INCH * ;* FLOPPY DRIVES TO MAKE HEAD ALIGNMENT POSSIBLE. * ;* COPYRIGHT 1989 RONALD E. JACOBS * ;* * ;* latest work done 03/30/89 * ;* * ;***************************************************************************** if alignment ;ASSEMBLE ALIGNMENT CODE? ;************************************************************************ ;* * ;* the following is a user setable equate * ;* * ;************************************************************************ diradfg equ 0FFh ;0 for ADM20; 0FFh for ADM31 cursor addressing stack equ 417eh ;stack space, RAM useD here(IM2 USES THIS ADDR) cr equ 0Dh ;carriage return lf equ 0Ah ;line feed bs equ 08h ;backspace align: ld sp,stack ;starting address of stack ;**************************************************************** ;* * ;* ROUTINE DISPLA * ;* * ;* SCROLLS WHATEVER IS ON THE SCREEN OFF AND DISPLAYS * ;* THE COMMANDS AND OTHER INFORMATION ON THE SCREEN. * ;* * ;**************************************************************** ld hl,mv2ram ;source of bytes to move from ROM to RAM ld de,stack ;destination of bytes to put into RAM ld bc,endmv2-mv2ram ;number of bytes to move ldir ;move bytes into RAM displa: ld A,25;B,25 ;number of times to send Line Feed call lnfeeds ;send line feed characters to screen ld de,screen ;start addr of string call outstr ;print the string ld A,14d;b,14d ;prepare for lnfeeds: b=number of line feeds call lnfeeds ;send line feed characters to screen call cylstr ;print the maximum track number after MAX TRK call denstr ;print "FM" or "MFM" after DENSITY call trkstr ;print the track number after TRACK call sidstr ;print "0" or "1" after SIDE call secstr ;print sector number after seCtor call motstr ;print "OFF" or "ON" after MOTOR call drvstr ;print the drive number (0 to 4) after DRIVE comand: ld bc,4725h ;B=column address w/offset,C=row addr w/offset call curadd ;move cursor to 'ST3' ld a,(st3) ;get status register 3 from memory ld ix,st3done jp lbyte ;convert the a register to ascii and print st3done:ld a,(bufchr) ;0 if chars not from buffer, else char. number ld bc,2829h ;B=column address w/offset,C=row addr w/offset add a,b ;move column address to current buffer charactr ld b,a ;store column address in BC for call to curadd call curadd ;move cursor to 'Command:' call inchr ;get character from keyboard or buffer ;**************************************************************** ;* * ;* SEE IF CHARACTER JUST RECEIVED FROM CONSOLE WAS * ;* A DIGIT. IF SO, CHECK FOR A SECOND DIGIT. * ;* IF NOT CHECK TO SEE IF CHARACTER IS A VALID LETTER. * ;* * ;**************************************************************** ld h,a ;save character that was typed at console ld a,(maxtrh) ;get maximum track number high digit in ASCII ld b,a ;B=max track number high digit inc b ;B=max track number high digit+1 ld a,h ;retrieve character that was typed at console cp '0' jp c,whatchr ;if typed character < 0 cp b ;b=high digit+1 of maximum track number jp c,track ;if typed character < max. high track digit+1 ;**************************************************************** ;* * ;* DETERMINE IF A VALID LETTER COMMAND WAS TYPED * ;* AND TAKE APPROPRIATE ACTION * ;* * ;**************************************************************** whatchr:cp 03h ;03h= ^C jp z,exit cp 06h ;06h= ^F jp z,format cp 12h ;12h= ^R jp z,rptbuf cp 14h ;14h= ^T jp z,maxtrk cp ';' jp z,filbuf cp '/' jp z,setbuf and 0DFh ;convert character to upper case cp 'A' jp z,currev cp 'C' jp z,setsec ;set sector number cp 'D' jp z,data cp 'F' jp z,density cp 'H' jp z,header cp 'I' jp z,stepi cp 'O' jp z,stepo cp 'R' jp z,recal cp 'S' jp z,sidsel cp 'M' jp z,motor cp 'L' jp z,seldrv cp 'Z' jp z,sleep jp comand ;character typed wasn't valid command ;************************************************************************ ;* * ;* FILBUF ROUTINE * ;* * ;* Fill the buffer for batch execution of commands. * ;* Generally Registers are used this way: * ;* BC:pointer to character in buffer at the cursor. * ;* D:Count of characters up to the cursor. * ;* E:Count of total number of characters in buffer. * ;* HL:pointer to next unused character location in buffer. * ;* * ;************************************************************************ filbuf: ld de,bscolon ;2 character string:backspace and colon call outstr ;send the string to the CRT to make "Command;" ;** Initialize counters and pointers. ** ld hl,cmdbuf ;point to start of command buffer ld b,h ld c,l ;BC also points to start of command buffer ld e,0 ;zero the buffer character count ld d,e ;zero the count of characters up to the cursor LD (HL),E ;STORE A NULL AS END OF BUFFER MARKER ;** Enter here from RPTBUF routine: ** initins:xor a ;zero A: 00=insert, 0FFh=overwrite ld (insert),a ;save insert in ram loadbuf:call inchr ;get a character from the keyboard ;** See if the character just received was a carriage return. ** cp cr ;is it a carriage return? jP z,fildone ;if it was a carriage return ;** See if the character just received was ^V (toggle insert/overwrite). ** cp 16h ;is it ^V? jr nz,isctlg ;if not ^V ld a,(insert) ;get insert/overwrite selection byte from ram cpl ;toggle the insert byte (00 OR 0FFh) ld (insert),a ;save insert in ram jr loadbuf ;** See if the character just received was a ^G (to delete next buffer char) ** isctlg: cp 07h ;is it ^G? jr nz,isbs ;if not ^G LD A,(BC) ;GET THE CHARACTER FROM UNDER THE CURSOR OR A ;SEE IF CHARACTER UNDER CURSOR IS 00 (NULL) jR z,LOADBUF ;IF NO CHARACTER IS UNDER CURSOR TO DELETE DEC E ;DEC. COUNT OF TOTAL NUMBER OF CHARS IN BUFFER ;** SEE IF CURSOR IS AT NULL (WHICH MARKS END OF BUFFER) ** PUSH BC ;SAVE POINTER TO CHARACTER IN BUFFER AT CURSOR movbufl:INC BC ;POINT TO NEXT CHARACTER AFTER THE CURSOR LD A,(BC) ;A=NEXT CHARACTER AFTER THE CURSOR DEC BC ;POINT TO LOCATION OF THE CURSOR LD (BC),A ;SAVE CHARACTER AT NEXT LOWER MEMORY ADDRESS OR A ;SEE IF CHARACTER UNDER CURSOR IS 00 (NULL) jR z,MVBFL1 ;IF NO CHARACTER IS UNDER CURSOR TO DELETE INC BC ;POINT TO LOCATION WHERE CURSOR WILL BE AFTER.. CALL OUTCHRA ; ...SENDING THE CHARACTER TO THE CRT jr movbufl MVBFL1: LD A,' ' ;SPACE CHARACTER CALL OUTCHRA ;WRITE A SPACE OVER THE LAST CHARACTER ON CRT POP BC ;GET POINTER TO CHAR. WHERE CURSOR SHOULD BE CALL BSCURSR DEC HL ;CHAR WAS DELETED SO POINT TO PREVIOUS BUF LOC JR LOADBUF ;** BACKSPACE THE CURSOR FROM THE END OF THE BUFFER TO WHERE IT BELONGS ** BSCURSR:PUSH HL ;SAVE POINTER TO NEXT UNUSED CHAR LOC IN BUFFER BSCUR: CALL CPBCHL ;COMPARE BC WITH HL:RETURNS Z IF =; NZ IF NOT = JR Z,Bsdone ;IF BC EQUALS HL(IF CURSOR BACK WHERE IT BEGAN) LD A,BS ;PREPARE TO SEND BACKSPACE CHARACTER TO THE CRT CALL OUTCHRA ;BACKSPACE THE CURSOR ON THE CRT DEC HL ;HL IS POINTING TO LOCATION WHERE CURSOR IS NOW jr bscur BSDONE: POP HL ;GET POINTER TO NEXT UNUSED CHAR LOC IN BUFFER RET ;** COMPARE BC WITH HL: RETURN Z IF EQUAL: RETURN NZ IF UNEQUAL ** CPBCHL: LD A,B ;COMPARE BC WITH HL: FIRST COMPARE B WITH H CP H RET NZ ;IF BC IS NOT EQUAL TO HL RETURN NZ LD A,C ; NOW COMPARE C WITH L CP L RET ;RETURN Z IF BC=HL; RETURN NZ IF BC NOT = HL ;** See if the character just received was a back space. ** isbs: cp bs ;is it a back space? jr z,isbs1 cp 13h ;13h = ^S jr nz,isctld ;if not a ^S go see if it is ^D isbs1: inc d dec d ;see if D was zero jr z,loadbuf ;if there are no characters to back space to dec d ;count of chars. up to the new cursor location dec bc ;dec. pointer to buffer location at the cursor ld a,bs ;put a back space in A, in case it was ^S call outchra ;backspace the cursor on the CRT jr loadbuf ;go get another character ;** see if the character just received was a forward space. ** isctld: cp 04h ;04h = ^D jr nz,ldbuf ;if not ^D ld a,d ;count of characters up to the cursor cp e ;compare with total count of characters jr z,loadbuf ;if there are no characters to foward space to ld a,(bc) ;get the character at cursor from the buffer call outchra inc d ;count of chars. up to the new cursor location inc bc ;inc. pointer to buffer location at the cursor jr loadbuf ;go get another character ;** End up here if character other then CR, ^V, back space or forward space.** ldbuf: call outchra ;send the character to the CRT ex af,af' ld a,(bc) ;get character that is currently under cursor ex af,af' ;now af' has character that was under cursor ld (BC),a ;store new character into buffer at cursor inc bc ;increment pointer to buffer char. at cursor inc d ;increment count of characters up to cursor ld a,(insert) or a ;0 means insert, non zero means overwrite jr nz,owrite ;if overwriting is on ;** If insert is on. (REGISTER A WILL BE ZERO)** inc hl ;point to next storage location in buffer LD (HL),A ;STORE A NULL INDICATING END OF BUFFER inc e ;increment total count of characters in buffer ;**Shift all characters after the inserted character right one position.** movbuf: PUSH BC ;SAVE CURSOR LOCATION PRIOR TO SHIFTING RIGHT movbufr:CALL CPBCHL ;SEE IF REGISTER PAIRS BC AND HL ARE EQUAL... jr Z,MVBFDON ;IF BC EQUALS HL ld a,(bc) ;get character that is currently under cursor ex af,af' call outchra ;print the character previously under cursor ld (bc),a ;save the character previously under cursor inc bc ;increment pointer to buffer char. at cursor JR MOVBUFR MVBFDON:POP BC ;GET BACK CURSOR LOCATION FROM BEFORE SHIFTING CALL BSCURSR ;MOVE CURSOR FROM END OF BUF TO PROPER POSITION JP LOADBUF ;**If overwriting is on.** owrite: ld a,e ;get total count of characters in buffer sub d ;subtract count of characters up to the cursor jp nc,loadbuf ;if cursor is before end of buffer inc hl ;point to next storage location in buffer inc e ;increment count of characters in buffer XOR A ;ZERO REGISTER A LD (HL),A ;STORE ZERO AS END OF BUFFER MARKER jp loadbuf fildone:ld a,e ld (buftot),a ;total count of characters in buffer(stays same ;************************************************************************ ;* * ;* SETBUF ROUTINE * ;* * ;* Enter this routine when '/' is read from the batch command * ;* buffer. (Also entered if '/' is read from the keyboard, but * ;* in that case it won't have any effect apparent to the user.) * ;* Also enter after repeating and editing buffer to the CRT. * ;* 1. Set the buffer count to its' original value. * ;* 2. Reset the buffer pointer to start of buffer. * ;* 3. Some of what is done in SETBUF is also used by FILBUF: * ;* FILBUF loads register E into BUFTOT. SETBUF uses the * ;* value in BUFTOT to set BUFCNT. (FILBUF also * ;* needs to set BUFCNT.) * ;* If it wasn't done in SETBUF, BUFPTR could be loaded at * ;* the beginning of FILBUF. * ;************************************************************************ setbuf: ld a,(buftot) ;maximum count of characters possible in buffer inc a ;count is predecremented in subroutne inchr: ld (bufcnt),a ;store buffer count in memory xor a ;zero register A to reset number gotten ld (bufchr),a ;store number of characters gotten from buffer ld hl,cmdbuf ;start of command buffer ld (bufptr),hl ;store pointer in memory ; ld bc,2829h ;B=column address w/offset,C=row addr w/offset ; call curadd ;move cursor to 'Command:' jp comand ;************************************************************************ ;* * ;* RPTBUF ROUTINE * ;* * ;* Retype the buffer command line. Make editing of it possible. * ;* Execute the command line after a Carriage Return is typed. * ;* * ;************************************************************************ rptbuf: ld de,bscolon ;2 character string:backspace and colon call outstr ;send the string to the CRT ld hl,cmdbuf ;start of command buffer ld a,(buftot) ;maximum count of characters possible in buffer ld d,a ;D = count of characters (to pass to LOADBUF:) ld e,0 ;E will=character count (to pass to LOADBUF:) bufcrt: ld a,d cp e ;see if D=E jp z,initins ;if all characters have been reprinted on CRT ld a,(hl) ;get a character from the buffer call outchra ;send the character from the buffer to the crt inc hl ;point to next character in buffer ld b,h ;BC <-- HL ld c,l inc e ;increment pointer to character in buffer jr bufcrt ;************************************************************************ ;* * ;* CURREV ROUTINE * ;* * ;* Toggles a flag bit which the cursor addressing * ;* routine uses to reverse or not reverse the order * ;* of transmission of row and column to the terminal. * ;* * ;************************************************************************ currev: ld a,(curflg) ;will be 00 or 0FFh;will be 0000$0000 or 0000$1000h cpl ;toggle the flag byte ld (curflg),a ;store in memory jp displa ;repaint the screen then fall thru to comand ;**************************************************************** ;* * ;* EXIT ROUTINE * ;* * ;* 1. MOVE CURSOR TO SCREEN BOTTOM LEFT * ;* 2. RETURN TO MONITOR if there or warm boot if not. * ;* * ;**************************************************************** exit: ld bc,2029h ;b=column address w/offset,c=row addr w/offset call curadd ;position the cursor for exit if monitor jp (iy) ;jump to monitor program (iy loaded by monstep) else jp 0 ;warmboot endif ;**************************************************************** ;* * ;* MOTOR ROUTINE * ;* * ;**************************************************************** motor: ld a,(motogl) ;motor toggle flag cpl ;toggle motor flag between 00 and FFh ld (motogl),a ;store the toggled flag call motstr jp comand motstr: ld bc,2B27h ;B=COLUMN ADDRESS W/OFFSET,C=ROW ADDR W/OFFSET call curadd ;CURSOR ADDRESS SUBROUTINE ld a,(motogl) ;MOTOR TOGGLE FLAG or a ;0=MOTOR OFF FF=MOTOR ON ld de,offmsg ;START OF OFF MESSAGE STRING jr z,chgmot ld de,onmsg ;START OF ON MESSAGE STRING chgmot: call outstr ;print the string ret ;**************************************************************** ;* * ;* RECAL ROUTINE * ;* * ;* RECALIBRATE THE MICRODECISION CONTROLLED DRIVE BY: * ;* 1. GIVING THE NEC 765 RECALIBRATE COMMAND (WHICH * ;* STEPS OUT A MAXIMUM OF 77 TRACKS UNTIL THE * ;* TRACK 0 LINE GOES TRUE. * ;* 2. TERMINATE THE COMMAND BY GIVING THE NEC 765 * ;* SENSE INTERRUPT STATUS COMMAND. * ;* * ;**************************************************************** recal: call cmdnew ;is data port ready to receive new command? ld a,07h ;NEC 765 recalibrate command code out (0FBh),a ;send recalibrate command to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte? ld a,(motdrv) ;bits 0 (=US0) and 1 (=US1) select the drive out (0FBh),a ;send second command byte to NEC 765 data port im0 ei halt ;wait for an interrupt from FDC call sensint ;perform NEC 765 sense interrupt status command call pcn ;get pointer to present drive's PCN in hl ld a,(hl) ;present drive's present track number call mkasci ;convert the hex track number to ascii chars call trkstr ;print the new track number on crt jp comand ;**************************************************************** ;* * ;* SELDRV ROUTINE * ;* * ;**************************************************************** seldrv: ld bc,2B28h ;B=column address w/offset,C=row addr w/offset call curadd ;move cursor to drive number position drvchr: call inchr ;get actual drive number from terminal ld e,a ;save a copy of the character in E and 11111100b ;valid character is 0-3; mask last 2 bits cp '0' ;is remaining character a '0' ? jr nz,drvchr ;character wasn't valid; try again selpnt: ld a,e ;get a copy of the character and 00001111b ;convert ASCII digit to binary ld (motdrv),a ;store drive number call outchre ;print the character that is in register E ;*TEST FOR MICRODECISION DRIVES WHICH HAVE NEVER BEEN CALIBRATED BY FSTEP call motrun ;so hardware selects the new drive call pcn ;get pointer to present drive's PCN in hl ld a,(hl) ;present drive's present track number cp 0FFh ;will be 0ffh if fstep has never moved drive hd jr nz,caldon ;if drive has already been calibrated ld hl,digit1 ;hl points to storage in memory for tens digit ld (hl),'?' ;store a '?' in tens digit inc hl ;hl points to storage in memory for ones digit ld (hl),'?' ;store a '?' in ones digit jr printxx ;return for next command caldon: call mkasci ;convert the hex track number to ascii chars printxx:call trkstr ;print the new drive's track number on the CRT jp comand ;return for next command drvstr: ld bc,2B28h ;B=column address w/offset,C=row addr w/offset call curadd ;move cursor to drive letter position ld a,(motdrv) ;get the binary drive number or 30h ;convert binary drive number to ASCII call outchra ;print the number on the screen ret ;**************************************************************** ;* * ;* SIDSEL ROUTINE * ;* * ;* TOGGLE THE CHOSEN SIDE, DISPLAY CHOICE ON CRT, * ;* STORE CHOICE FOR USE BY MOTRUN SUBROUTINE * ;* * ;**************************************************************** sidsel: ld a,(sidbit) ;current SIDE is coded into bit 2 xor 00000100b ;toggle bit 2 ld (sidbit),a ;save into memory call sidstr jp comand sidstr: ld bc,2B25h ;B=column address w/offset,c=row addr w/offset call curadd ;cursor address subroutine ld a,(sidbit) bit 2,a ;current side is coded into bit 2 ld a,'0' ;be ready to print a '0' jr z,chgsid ;if selected side is 0 inc a ;change '0' to '1' and be ready to print a '1' chgsid: call outchra ;print the character that is in register a ret ;**************************************************************** ;* * ;* SETSEC ROUTINE * ;* Get the sector number, display choice on the CRT. * ;* * ;**************************************************************** setsec: ld bc,2B26h ;B=column address w/offset,c=row addr w/offset call curadd ;cursor address subroutine call inchr ;get a character from the keyboard or buffer call valdig ;see if A register has ASCII 0 through 9 jr c,setsec ;valdig returns with carry set if not 0 thru 9 sub '0' ;make the sector number hexidecimal ld (sector),a ;store selected sector number in memory call secstr ;print the sector number on the CRT jp comand secstr: ld bc,2B26h ;B=column address w/offset,c=row addr w/offset call curadd ;cursor address subroutine ld a,(sector) ;get the sector number (in hex) from memory or '0' ;make the sector number an ASCII digit call outchra ;print the character that is in register a ret ;**************************************************************** ;* * ;* DENSITY ROUTINE * ;* * ;* TOGGLE THE DENSITY SELECTION, DISPLAY CHOICE ON CRT. * ;* * ;**************************************************************** density:ld a,(denbit) ;current density is coded into bit 6 ld e,01000000b xor e ;toggle bit 6 in register A ld (denbit),a ;save new byte into memory call denstr jp comand denstr: ld bc,2B23h ;B=column address w/offset,C=row addr w/offset call curadd ;cursor address subroutine ld a,(denbit) bit 6,A ;current density is coded into bit 6 ld de,denmfm ;point to string "MFM" to print jr nz,chgden ;if selected density was FM ld de,denfm ;was "MFM", point to string "FM" to print chgden: call outstr ;print the string that DE points to ret ;**************************************************************** ;* * ;* HEADER ROUTINE * ;* * ;* Get I.D. information from a sector header. * ;* * ;**************************************************************** header: call cmdnew ;is data port ready to receive a new command? ld a,(denbit) ;density bit is 6: 0=FM, 1=MFM or 00001010b ;merge the READ ID command with the density bit out (0FBh),a ;send READ ID command to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte ld a,(sidbit) ;get side select bit ld e,a ;save side select bit in register E ld a,(motdrv) ;bit 0=US0, BIT 1=US1 for drive selection or e ;merge head select bit with drive select bits out (0FBh),a ;byte 2 of READ ID command call indata ;get the 7 result bytes of the READ ID command jp headout ;print the 7 result bytes on the CRT ;**************************************************************** ;* * ;* DATA ROUTINE * ;* * ;* Read undeleted data from selected drive, track, head, * ;* and sector. * ;* * ;**************************************************************** data: ld bc,3926h ;B=column address w/offset,C=row addr w/offset call curadd ;cursor address subroutine call cmdnew ;is data port ready to receive a new command ld a,0;(MT)<<<<<<<<;MT is multi-track flag (bit 7) ld e,a ;E contains multi-track flag (MT) ld a,0;(SK)<<<<<<<<;SK is skip deleted data address mark (bit 5) or e ;merge SK and MT bits ld e,a ;E contains SK and MT bits ld a,(denbit) ;density is coded into bit 6: 0=FM, 1=MFM or e ;merge SK and MT bits with density bit or 00000110b ;NEC 765 read data command code out (0FBh),a ;send read data command to NEC 765 data port ;*The following 6 lines are identical with those in the format command ;*and could therefore be a subroutine. call cmdrdy ;is data port ready to receive a command byte? ld a,(sidbit) ;bit 2: 0=side 1, 1=side 1 ld e,a ;save side select bit in register E ld a,(motdrv) ;bit 0=US0, BIT 1=US1 for drive selection or e ;merge head select bit with drive select bits out (0FBh),a ;byte 2 of read data command call cmdrdy ;is data port ready to receive a command byte? ld a,(curtrb) ;C: current track number out (0FBh),a ;send command byte to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte? ld a,(sidbit) ;get side bit (in bit 2) rrca ;shift side bit into bit 0 rrca ;H: head number out (0FBh),a ;send command byte to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte? ld a,(sector) ;R: sector number to begin reading at out (0FBh),a ;send command byte to NEC 765 data port ;*the following 3 lines are the same as used in the format command call cmdrdy ;is data port ready to receive a command byte? ld a,(SECSIZE) ;N: sector size code (03=1024 selected) out (0FBh),a ;send command byte to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte? ld a,0FFh ;EOT: end of track (last sector to read) out (0FBh),a ;send command byte to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte? ld a,1Ch ;GPL: gap length out (0FBh),a ;send command byte to NEC 765 data port ;** Calculate BPS before outputting last byte of read data command. ** im0 LD H,0 LD L,128d ;HL=SMALLEST SECTOR SIZE (SECSIZE=N=0 FOR FM) LD A,(DENBIT) ;DENSITY IS CODED INTO BIT 6: 0=FM 1=MFM OR A ;SEE IF ZERO OR 1 LD A,(SECSIZE) ;GET SECTOR SIZE CODE (N) JR Z,CALCBP1 ;IF DENSITY IS FM (SINGLE) CALCBPS:ADD HL,HL ;IF DENSITY IS BOUBLE, DOUBLE 128 TO MAKE 256 DEC A ;MFM DOES NOT USE SIZE CODE 0;BEGINS AT 256 BPS CALCBP1:or a ;test for size code N=0 JR Z,bps2bc ADD HL,HL ;DOUBLE THE CALCULATED BYTES PER SECTOR DEC A JR CALCBP1 bps2bc: LD B,H LD C,L ;BC <-- SECTOR SIZE IN BYTES (FROM HL) ;** output the last byte of the read data command. ** calcdtl:call cmdrdy ;is data port ready to receive a command byte? LD A,(SECSIZE) ;GET SECTOR SIZE CODE (N) or a ;test to see if sector size code is 0 ld a,128d ;if secsize=0 out 128d to 0FBh else out 0FFh jr z,outdtl ;if sector size code is zero ld a,0FFh ;DTL: data length use 0FFh if N not equal 0 outdtl: out (0FBh),a ;send command byte to NEC 765 data port ;** Loop while waiting for 765/8272 to begin executing the read command. ** waitexe:ld hl,4C00h ;HL points to sector buffer in RAM ld de,8000h ;load time out count waitex1:in a,(0FAh) ;get status byte from NEC765/INTEL8272 bit 5,a ;see if execution has begun(non-DMA gone high?) jr nz,RDIO ;if execution has begun dilp: bit 7,a ;see if master req. jr z,waitex1 ;if Request for Master (RQM) low(not requested) dataerr:ld de,readerr ;address of 'Data read not begun' message call outstr ;send the error message to the CRT jp get7 ;jmp to i/o ;** This code runs while read data command is executing but awaiting 1st byte** rloop1: and 020h ;see if still executing jr z,dataerr ;jmp if done in a,(0FAh) ;get status byte from NEC765/INTEL8272 cp 0F0h ;see if a byte is ready jr z,inbyte ;jmp if 1st byte ready dec de ;decrement time out count ld a,e ;see if timed out or d jr z,dataerr ;if timed out rdio: in a,(0FAh) ;get status byte from NEC765/INTEL8272 cp 0F0h ;see if a byte is ready jr nz,rloop1 ;loop if byte is not ready ;** This code runs while NEC765 is getting bytes from the disk sector. ** inbyte: dec bc ;dec count of bytes to get from sector ld a,b ;see if count has reached zero or c jr Nz,in1byte ;if more than one byte is left to get in a,(0F7h) ;input terminal count if one byte is left in1byte:in a,(0FBh) ;get a byte from the sector ld (hl),a ;put the byte from the sector into memory inc hl ;increment memory storage pointer jr z,get7 ;if all bytes have been read from the sector rloop2: ei halt ;wait for a byte from the sector in a,(0FAh) ;get status byte from the NEC765 and 020h ;see if still executing jr nz,inbyte ;if still executing get7: call indata ;read the 7 result bytes ST0,ST1,ST2,C,H,R,N call headout ;print the 7 result bytes on the CRT jp comand ;**************************************************************** ;* * ;* STEPI ROUTINE * ;* * ;* INCREMENT THE ASCII REPRESNTATION OF THE CURRENT TRACK * ;* NUMBER BY ONE (IF NOT ALREADY HIGHEST TRACK). PRINT IT * ;* ON THE CRT. * ;* * ;**************************************************************** ;*microdecision step in routine stepi: ld a,(maxtrb) ;A= highest allowed track number ld b,a ;B= highest allowed track number call pcn ;get pointer to present drive's PCN in HL ld a,(hl) ;A= present drive's present track number cp b ;is current track < highest allowed track jp nc,comand ;don't step in if now =or>highest allowed track inc a ;increment track number by 1 jr step ;**************************************************************** ;* * ;* STEPO ROUTINE * ;* * ;* DECREMENT THE ASCII REPRESNTATION OF THE CURRENT TRACK * ;* NUMBER BY ONE (IF NOT ALREADY LOWEST TRACK). PRINT IT * ;* ON THE CRT. * ;* * ;**************************************************************** stepo: call pcn ;get pointer to present drive's PCN in hl ld a,(hl) ;present drive's present track number cp 0 ;lowest allowed track number jp z,comand ;don't step out if already at track 0 dec a ;decrement track number by 1 step: ld (curtrb),a ;save the track number call mkasci ;convert track number to ascii characters call trkstr ;print the new track number on crt jp mdseek ;go do the seek ;**************************************************************** ;* * ;* SLEEP ROUTINE * ;* Delay for selected time. * ;* Enter up to four digits followed by "M" (for * ;* milliseconds). * ;* See the table in the data/storage section at the end * ;* of this program for an idea of how multipliers and the * ;* ones, tens, hundreds, and thousands digits are stored * ;* in memory. * ;**************************************************************** sleep: call outchra ;display the 'Z' (caused entry) on the screen ld a,0 ;initialize count of digits from keybord/buffer ld (digcnt),a ;initialize count of digits from keybord/buffer getzdig:call inchr ;get a character from buffer or terminal cp 'm' ;follow the entered digits with (M)illiseconds jr z,zdelay ;if lower case 'm' entered go perform the delay cp 'M' ;check for uppercase M jr z,zdelay ;go calculate and do the delay if uppercase M call valdig ;see if A register has ASCII 0 through 9 jr c,getzdig ;valdig returns with carry set if not 0 thru 9 call outchra ;display the character on the CRT and 0Fh ;change character from ASCII to a number ld e,a ;save the number for storage into memory ld hl,zdigit ;pointer to most recently received character ld c,4 ;count of decimal digits to shift in HL regstrs shift: ld a,(hl) ;get a character that was stored in memory ld (hl),e ;replace previously received char into memory ld e,a ;save the character that was stored in memory inc hl ;point to the next memory location inc hl inc hl dec c ;decrement count of decimal digits to shift jr nz,shift ;if all 4 digits haven't been shifted yet ld a,(digcnt) ;get count of digits from the keyboard/buffer inc a ;increment count of digits that were entered cp 4 ;maximum number of digits that may be entered ld (digcnt),a ;save count of digits from the keyboard/buffer jr nz,getzdig ;go get another character zdelay: ld c,4 ;count of digits to multiply by exx ld hl,0000 ;initialize the total exx ldnmbr: dec hl ;point to multiplier (1,10,100, or 1000) ld d,(hl) ;load low byte of multiplier into E dec hl ;point to high byte of multiplier ld e,(hl) ;load high byte of multiplier into D dec hl ;point to entered digit ld a,(hl) ;get entered decimal digit from memory ld (hl),0 ;replace entered digits w/0 for next sleep use dlayadd: and a ;test if decimal number is zero jr z,nxtnmbr ;if zero push de ;save multiplier (1,10,100, or 1000) exx ;save hl pointer; get total pop de ;get multiplier add hl,de ;add 1,10,100, or 1000 one time dec a ;subtract 1 from entered digit exx ;get pointer jr dlayadd nxtnmbr: dec c ;decrement count of decimal digits to add up jr nz,ldnmbr ;if entered digits are remaining exx dly1ms: ld de,135d ;(10)count for generating 1 millisecond delay ld a,l ;(4) or h ;(4)see if hl has decremented to 0 yet jr z,dlydone ;(10)if there are no milliseconds left to delay dlaylp: dec de ;takes(6)clock cycles (4000 cycles/millisecond) ld a,d ;(4) or e ;(4)see if de has decremented to 0 yet jr nz,dlaylp ;this instruction takes (7/12) clock cycles dec hl ;(6)decrement count of milliseconds remaining jp dly1ms ;(10) dlydone:ld a,(bufcnt) ;count of chars left to get from buffer, if any or a ;see if any characters left to get from buffer jr nz,dlyret ld a,(digcnt) ;count of digits entered in sleep routine inc a ;blank out the 'Z' also ld b,a ;number of times to send string ld de,bsspbs ;address of string backspace, space, backspace call multstr dlyret: jp comand ;**************************************************************** ;* * ;* SUBROUTINE MKASCI * ;* * ;* 1. ON ENTRY, A CONTAINS THE HEX TRACK NUMBER THAT WAS * ;* STORED BY NEC 765 RESULT COMMAND. * ;* 2. CONVERT IT TO TWO ASCII DIGITS AND STORE THEM FOR * ;* USE BY THE TRKCRT ROUTINE (WHICH PRINTS THEM TO THE * ;* CRT). * ;* 3. Enter at HEXASCI with A containing a hex number to * ;* convert to a pair of ASCII digits and HL containing * ;* a pointer to a sequential pair of addresses in which * ;* to store the resulting ASCII bytes. * ;* * ;**************************************************************** mkasci: ld hl,digit1 ;hl points to storage in memory for tens digit ld b,10 ;prepare to subtract 10s from the hex number call asc inc hl ;HL points to storage in memory for ones digit add a,b ;add the 10 that was subtracted to make neg. # ld b,1 ;prepare to subtract 1s from the hex number asc: ld (hl),'0' ;initialize the stored ASCII digit to '0' ascsub: sub b ;find the number of 10s or 1s in the hex number ret c ;if less then 10 or 1 remains in the hex number inc (hl) ;bump the stored ASCII digit higher jr ascsub ;**************************************************************** ;* * ;* TRACK ROUTINE * ;* * ;* This routine is entered when a digit (within allowable * ;* range of track number) is typed when the program is * ;* waiting for a command. This routine stores that digit, * ;* gets another digit (range check is performed), displays * ;* the digits on the screen, and sends the head to the * ;* selected track. * ;* * ;**************************************************************** track: ld (digit1),a ;save the first digit (in ASCII) ld bc,2B24h ;B=column address w/offset,C=row addr w/offset call curadd ;move cursor to track number position ld a,(digit1) call outchra ;print the first digit (from register a) get2nd: call inchr ;get the 2nd digit of track number ld (digit2),a ;save the second character call valdig ;see if A register has ASCII 0 through 9 jr c,get2nd ;valdig returns with carry set if not 0 thru 9 ld hl,digit1 ;HL points to memory storage of hi ASCII digit call calctr ;calculate current track in binary (HL points) ld a,(maxtrb) ;maximum allowed track number in binary sub (hl) ;HL points to current track in binary jr c,get2nd ;2nd digit was too large dec hl ;HL points to track number low digit in ASCII ld a,(hl) ;A=track number low digit in ASCII call outchra ;print the second digit (from register A) jp mdseek ;go do the seek ;************************************************************************ ;* * ;* MAXTRK ROUTINE * ;* * ;* Get two digits from the console. These two digits are the * ;* maximum track number beyond which the drive is not allowed * ;* to step. Save these digits in ASCII in MAXTRH and MAXTRL. * ;* * ;************************************************************************ maxtrk: ld bc,2B22h ;B=column address w/offset,C=row addr w/offset call curadd ;cursor address subroutine ld hl,maxtrh ;point to memory storage for max track hi digit ;*NOTE: registers HL must be preserved across these calls* call maxtr1 inc hl call maxtr1 dec hl ;point to DIGIT1 (needed by CALCTR) call calctr ;calculate and store maximum track in binary jp comand maxtr1: call inchr ;get 1st digit of the maximum track number ld (hl),a ;save the first ASCII digit (DIGIT1) call valdig ;see if A register has ASCII 0 through 9 jr c,maxtr1 ;valdig returns with carry set if not 0 thru 9 call outchra ;print the first digit (from register a) ret cylstr: ld bc,2B22h ;B=column address w/offset,C=row addr w/offset call curadd ;cursor address subroutine ld a,(maxtrh) call outchra ;print the first digit (from register a) ld a,(maxtrl) call outchra ;print the second digit (from register a) ret ;************************************************************************ ;* * ;* FORMAT ROUTINE * ;* Formats a microdecision drive track. * ;* * ;************************************************************************ format: ld bc,3136h ;B=column address w/offset,C=row addr w/offset call curadd ;cursor address subroutine ;;;;;;; ld a,0 ;initialize count value ;;;;;;; XOR A ;initialize count value to zero ;;;;;;; ld (chrns),a ;count of sector information requests call cmdnew ;is data port ready to receive a new command ld a,(denbit) ;density is coded into bit 6: 0=FM, 1=MFM or 00001101b ;NEC 765 format a track command code out (0FBh),a ;send format a track cmd to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte ld a,(sidbit) ld e,a ;save side select bit in register E ld a,(motdrv) ;bit 0=US0, BIT 1=US1 for drive selection or e ;merge head select bit with drive select bits out (0FBh),a ;byte 2 of format a track command call cmdrdy ;is data port ready to receive a command byte? ld a,(SECSIZE) ;N: bytes per sector (03=1024 selected) out (0FBh),a ;send command byte to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte? ld a,(SPT) ;SC: sectors per track out (0FBh),a ;send command byte to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte ld a,(GPL3) ;GPL: gap three out (0FBh),a ;send command byte to NEC 765 data port im0 call cmdrdy ;is data port ready to receive a command byte? LD A,(SECTOR) ;USE USER SET SECTOR NUMBER FOR SECTOR COUNT LD B,A ;SAVE COUNT IN REGISTER B LD A,01;(STRTSEC)<<<<<<<<<<<<<<< invalid command-never started jr z,sensint ;if invalid command or not done, start over ret ;************************************************************************ ;* * ;* SUBROUTINE PCN * ;* * ;* Returns a pointer in HL to the present drive's present cylinder * ;* number. The table is filled by storing the results of the * ;* NEC 765 sense interrupt status command (RESULT SUBROUTINE). * ;* * ;************************************************************************ pcn: ld hl,pcntbl-1 ;table of each of 4 drives present cylinder #s ld a,(motdrv) ;present drives number inc a nextdr: inc hl ;develop a pointer (in HL) to byte for present dec a ; cylinder number for present drive jp nz,nextdr ret ;************************************************************************ ;* * ;* SUBROUTINE CMDNEW * ;* * ;* Reads MicroDecision port 0FAh to get the status of the NEC 765 * ;* controller chip. This is a check to see if the chip is ready * ;* for the first byte of new command. * ;* * ;************************************************************************ cmdnew: call mddly ;delay 12.5 uS cmdnew1:in a,(0FAh) ;read NEC 765 status port and 11110000b ;mask out some bits cp 10000000b ;is data port ready for a new command? jr nz,cmdnew1 ;loop until ready ret ;************************************************************************ ;* * ;* SUBROUTINE CMDRDY * ;* * ;* Reads MicroDecision port 0FAh to get the status of the NEC 765 * ;* controller chip. This is a check to see if the chip is ready * ;* for the next byte of a command which is already in progress. * ;* * ;************************************************************************ cmdrdy: call mddly ;delay 12.5 uS cmdrdy1:in a,(0FAh) ;read NEC 765 status port and 11010000b ;mask out some bits cp 10010000b ;is data port ready for more bytes of command? jr nz,cmdrdy1 ;loop until ready ret ;************************************************************************ ;* * ;* SUBROUTINE RDRDY * ;* * ;* Reads MicroDecision port 0FAh to get the status of the NEC 765 * ;* controller chip. This is a check to see if the chip is ready * ;* to deliver the next byte of the result. * ;* * ;************************************************************************ rdrdy: call mddly ;delay 12.5 uS rdrdy1: in a,(0FAh) ;read NEC 765 status port anä 11100000b ;savå data input/output,request for master bits cp 11000000b ;is data port ready to send data? jr nz,rdrdy1 ;loop until ready ret ;************************************************************************ ;* * ;* SUBROUTINE MDDLY * ;* * ;* DELAYS 12 uS. 12uS IS THE MINIMUM TIME REQUIRED BETWEEN BYTES * ;* BEING OUTPUT TO THE NEC 765. * ;* 12.5uS = (50 CYCLES)/(4 MHZ) * ;* * ;************************************************************************ mddly: ex (sp),ix ;23 clock cycles to execute this instruction ex (sp),ix ;23 more clock cycles ret ; 1 clock cycle for ret (3 cycles for call) ;************************************************************************ ;* * ;* SUBROUTINE MULTSTR * ;* * ;* Send string of characters pointed to by registers DE to the * ;* CRT the number of times contained in B. * ;* * ;************************************************************************ multstr:push de ;save a copy of the string address call outstr pop de dec b ;decrement number of times to send string jr nz,multstr ret ;************************************************************************ ;* * ;* SUBROUTINE OUTSTR * ;* * ;* Enter with DE pointing to a string to send to the CRT. The * ;* string must be terminated with a '$' character. * ;* * ;************************************************************************ outstr: ld a,(de) ;get a character of the string to be printed cp '$' ;end of string marker ret z ;if end of string has been reached call outchra ;if character was not '$' then print it inc de ;point to the next character in the string jr outstr ;go do the next character ;**************************************************************** ;* * ;* SUBROUTINE LNFEEDS and CHRFEED * ;* * ;* To send linefeeds enter at lnfeeds with register A * ;* containing number of times to send a line feed * ;* character (0Ah) to the screen. Enter at chrfeed with * ;* register E containing character to send and register A * ;* containing number of times to send it. * ;* * ;**************************************************************** lnfeeds:ld e,lf ;character to send (ASCII line feed) chrfeed:or a ;test to see if count has reached 0 ret z ;if count has reached 0 call outchre ;send line feed to screen dec a ;count <-- count-1 jr chrfeed ;**************************************************************** ;* * ;* OUTPUT A CHARACTER TO THE CRT * ;* * ;* enter at OUTCHRA if character to send is in register A * ;* enter at OUTCHRE if character to send is in register E * ;* * ;* All registers are preserved with use of either entry * ;* point. * ;* * ;**************************************************************** outchra:push de ld e,a ;routine entered with character to print in A call outchre pop de ret outchre:push af outchr: in a,(status) ;Get communications port status and wrbit ;test if ready to send a character jr z,outchr ;loop if not ready ld a,e ;get the character to print from register E out (wrreg),a ;Output to transmitter buffer pop af ret ;**************************************************************** ;* * ;* ASCII character strings. * ;* * ;**************************************************************** screen: db cr db 'uDecision Investigation Tool rev. 2.6' db CR,LF db 'Copyright 1989 Ronald E. Jacobs (Berkeley, CA)' db lf,cr db 'max ^Trk step In/Out ST0' db CR,LF db 'F density read Headers ST1' db CR,LF db 'NN track cursor Addressing ST2' db CR,LF db 'Side Recalibrate ST3' db CR,LF db 'seCtor read Data' db cr,lf db 'Motor ^Format a track' db CR,LF db 'L drive ^C exit' db cr,lf db 'Command:' db '$' denFM: db 'FM $' denMFM: db 'MFM$' OFFmsg: db 'OFF$' ONmsg: db 'ON $' bscolon:db bs,';$' ;used by filbuf to put a ';' after Command: bsspbs: db bs,' ',bs,'$' ;used to blank the character before the cursor readerr:db 'Data read not begun$';error message for sector data reads ;************************************************************************ ;* * ;* The following bytes are moved into ram when fstep is entered. * ;* They are here because fstep is operating from ROM but needs * ;* these bytes in RAM so that they can be changed dynamically. * ;* * ;************************************************************************ MV2RAM: .phase stack denbit: db 01000000b ;0100$0000 =MFM 0000$0000 =FM sidbit: db 00000000b ;0000$0100 =drive side 1 0000$0000 = side 0 curflg: db diradfg ;0FFh for row-then-column,00 for col-then-row;0000$1000 cursor direct address sequence order flag motogl: db 0 ;motor toggle flag SECSIZE:db 3 ;SECTOR SIZE CODE (N) (3=1024 bytes per sector) SPT: DB 05h ;SECTORS PER TRACK GPL3: DB 05Fh ;GAP THREE LENGTH ;*The order of the next 3 bytes must not be changed (for subroutine CALCTR)* digit1: db '?' ;makes ASCII track number (tens digit) digit2: db '?' ;initially not known (='??') (ones digit) curtrb: db 99 ;current track number in binary motdrv: db 0 ;drive #(filled by routine seldrv) ;*The order of the next 3 bytes must not be changed (for subroutine CALCTR)* maxtrh: db '3' ;maximum high decimal track digit in ASCII maxtrl: db '9' ;maximum low decimal track digit in ASCII maxtrb: db 39 ;maximum track number in binary pcntbl: ;0FFh=FSTEP has never accessed this drive pcn0: db 0FFh ;drive 0 present cylinder number pcn1: db 0FFh ;drive 1 present cylinder number pcn2: db 0FFh ;drive 2 present cylinder number pcn3: db 0FFh ;drive 3 present cylinder number sector: db 05 ;sector number to read data from ;;;;;;;chrns: db 1 ;used by FORMAT:count of CHRNs requested insert: db 0FFh ;FF=insert;00=overwrite for cmd buffer editing bufcnt: db 0 ;count of chars remaining in batch cmd buffer bufchr: db 0 ;number of characters gotten so far from buffer bufptr: dw cmdbuf ;pointer to char. in command buffer(0FEh long) buftot: db 0 ;total number of characters entered in buffer ;** The order of the following 8 lines of ds and dw must not change for SLEEP** zdigit: db 0 ;most recently recieved digit in sleep command dw 0001d ; 1s multiplier db 0 ;digit storage(10s when all digits entered) dw 0010d ; 10s multiplier db 0 ;digit storage (100s) dw 0100d ; 100s multiplier db 0 ;digit storage (1000s)(least recently received) dw 1000d ;1000s multiplier digcnt: db 0 ;count of entered digits in sleep command ;************************************************************************ ;* * ;* The following equates define addresses of uninitialized RAM * ;* which this program will use when it runs. * ;* * ;************************************************************************ st3 equ $ ;1 byte uninitialized storage status reg. 3 datbuf equ st3+1 ;7 bytes uninitialized storage(ST0,1,2,C,H,R,N) st0 equ datbuf ;status register 0 st1 equ st0+1 ;status register 1 st2 equ st1+1 ;status register 2 cmdbuf equ datbuf+7 ;255 bytes uninitialized storage for batch cmd .dephase ENDMV2: ;END OF ASSEMBLED CODE ENDIF ;END OF "IF ALIGNMENT" CONDITIONAL ;************************************************************************ ;* * ;* INITIALIZATION STRINGS FOR MD2 AND MD3 UARTS AND MD3 TIMER * ;* * ;* Everything after this point may be overwritten after * ;* the program begins operating. * ;* * ;************************************************************************ IF MD3 init_tab: db 0f3h ;CTC channel 0, mode 3 db 3eh db 0f0h ;CTC channel 0 set to max delay db 0ffh db 0f0h db 0ffh db 0f3h ;CTC channel 1, mode 3 db 7eh db 0f1h ;CTC channel 1 set to 9600 baud db 0dh db 0f1h db 0 db 0f3h ;CTC channel 2, mode 3 db 0beh db 0f2h ;CTC channel 2 set to 1200 baud db 68h db 0f2h db 0 db s1stat ;reset both UARTS db 80h db s2stat db 80h db s1stat db 80h db s2stat db 80h db s1stat db 40h db s2stat db 40h ;end of reset sequence db s1stat ;set both UARTS db 0ceh ;8 bits, no parity, x16, 2 stop db s2stat db 0ceh db s1stat ;Tx, Rx - on, DTR, RTS - on, ER reset db 37h db s2stat db 37h init_len equ ($-init_tab)/2 ENDIF ;ENDIF OF IF MD3 ;************************************************************************ ;* * ;* The following macro calls cause the variable following the % * ;* sign to be printed on the CRT when this program is assembled. * ;* * ;************************************************************************ endall equ $-1 ;ending address of all code and data codelen equ $-serinit ;total code length .radix 16 ;change radix to base 16 for following macros if2 ;if2 assembles only on 2nd pass of 2 pass mode else ;give the following information only on first pass oncrt 'Start of all code SERINIT',%serinit if monitor oncrt 'Monitor program re-entry address STRT ',%strt endif if alignment oncrt 'Alignment program start address ALIGN ',%align endif oncrt 'Ending address of all code and data ENDALL ',%endall oncrt 'Total code length is ',%codelen endif .radix 10 ;return to default radix end