.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 04/04/89 * ;* * ;***************************************************************************** if alignment ;assemble this alignment code? ;************************************************************************ ;* * ;* the following is a user setable equate * ;* * ;************************************************************************ diradfg equ 0FFh ;0 for ADM20; 0FFh for ADM31 cursor addressing stack equ 1100h ;stack space, RAM use here 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 ;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 ;prepare for lnfeeds: b=number of line feeds call lnfeeds ;send line feed characters to screen ;**The order of the next 4 groups of 3 lines each must not be changed.** ld hl,maxtrk ;point to memory storage for max track call byt2crt ;print the maximum track number after MAX TRACK call byt2crt ;print the number after call byt2crt ;print the number after call byt2crt ;print the number after call byt2crt ;print the number after call byt2crt ;print the number after call byt2crt ;print the number after call sidstr ;print "0" or "1" after SIDE call denstr ;print "FM" or "MFM" after DENSITY call motstr ;print "OFF" or "ON" after MOTOR ;;;;;;; call secstr ;print sector number after seCtor call drvstr ;print the drive number (0 to 4) after DRIVE comand: ld bc,4B25h ;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. * ;* * ;**************************************************************** call valdig ;RETURNS NO CARRY IF CHARACTER IS A DIGIT jp c,whatchr ;if typed character WASN'T A DIGIT 0-9 ld e,a ;save character that was typed at console call calcten ;returns in reg D the binary value of 10*digit ld a,(maxtrk) ;get maximum track number high digit in ASCII cp d ;subtract tens binary from max track value ld a,e ;retreive character that was typed at console jp nc,track ;if typed digit < max. high track ;**************************************************************** ;* * ;* 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 (set maximum allowed cylinder number) ld hl,maxtrk ;point to max track memory storage structure jp z,get2dig ;get & display 2 digits on CRT, store binary cp ';' jp z,filbuf cp '/' jp z,setbuf and 0DFh ;convert character to upper case cp 'A' jp z,currev cp 'C' ld hl,CYLNDR ;point to CYLINDER memory storage structure jp z,get2dig ;get & display 2 digits on CRT, store binary ;;;;;;; cp 'C' ;;;;;;; jp z,get1dig ;set sector number cp 'D' jp z,data cp 'F' jp z,density cp 'H' jp z,header cp 'I' jp z,stepi cp 'L' jp z,seldrv cp 'M' jp z,motor cp 'O' jp z,stepo CP 'P' jp z,pause cp 'R' jp z,recal cp 'S' jp z,sidsel 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 on (overwrite off) 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 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 left edge below 'Command>'. * ;* 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,2A24h ;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 LD HL,CYLNDR ;POINT TO CYL. NO. FOR FORMAT/RD/WRITE COMMANDS LD A,(CURTRK) ;GET THE NEW TRACK NUMBER LD (HL),A ;SAVE NEW CYLINDER NUMBER INTO RAM call byt2crt ;display new CYLINDER NO. as ASCII pair on CRT ld hl,curtrk ;regs. hl point to current track structure call byt2crt ;display new track number as ASCII pair on CRT jp comand ;**************************************************************** ;* * ;* SELDRV ROUTINE * ;* * ;**************************************************************** seldrv: ld bc,2A25h ;B=column address w/offset,C=row addr w/offset call curadd ;move cursor to drive number position call pcn ;get pointer to present drive's PCN in hl ld a,(curtrk) ;get the current binary track ld (hl),a ;save the current drives binary track number 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. ** call motrun ;so hardware selects the new drive call pcn ;get pointer to present drive's PCN in hl ld a,(hl) ;get new drives binary track number LD HL,CYLNDR ;POINT TO CYL. NO. FOR FORMAT/RD/WRITE COMMANDS LD (HL),A ;SAVE NEW CYLINDER NUMBER INTO RAM LD (CURTRK),A ;SAVE THE TRACK NUMBER INTO RAM call byt2crt ;display new CYLINDER NO. as ASCII pair on CRT ld hl,curtrk ;point to address in memory for current track ;;;;;;; ld (hl),a ;save the track number call byt2crt ;display new track number as ASCII pair on CRT jp comand ;return for next command drvstr: ld bc,2A25h ;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,2A23h ;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 ;**************************************************************** ;* * ;* 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,3A28h ;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,(CYLNDR) ;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,(DATASEC) ;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,(rgpl) ;read data 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 double, 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,2000h;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 inputting 7 result bytes ;** 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,(maxtrk) ;A= highest allowed track number ld b,a ;B= highest allowed track number ld a,(curtrk) ;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: ld a,(curtrk) ;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 hl,curtrk ;point to address in memory for current track ld (hl),a ;save the track number LD (CYLNDR),A ;ALSO SAVE IN CYLNDR call byt2crt ;display new track number as ASCII pair on CRT LD HL,CYLNDR CALL BYT2CRT ;DISPLAY NEW CYLINDER NUMBER ON CRT jp seek ;go do the seek ;**************************************************************** ;* * ;* PAUSE ROUTINE * ;* Do nothing while waiting for operator to strike any * ;* key. Temporarily stores a 0 in bufcnt to make sure * ;* character input routine (inchr) gets a character from * ;* the keyboard and not the buffer. Calls inchr because * ;* inchr keeps the motor running while awaiting a * ;* character. * ;* * ;**************************************************************** pause: ld hl,bufcnt ;RAM addr:count of characters left in buffer ld e,(hl) ;save count of characters left in buffer xor a ;zero register A ld (hl),a ;temporarily zero count of characters in buffer call inchr ;go wait for a character & keep motor running CP 03h ;see if ^C was typed ld (hl),e ;restore count of characters left in buffer jr nz,endpaus ;if character wasn't ^C ;**If ^C typed, terminate command string execution.** ld a,1 ld (hl),a ;say 1 character is left in buffer ld a,' ' ;' ' is a "do nothing" command ld (bufptr),a ;make ' ' the only character left in the string endpaus:jp comand ;**************************************************************** ;* * ;* 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 BYT2CRT * ;* * ;* 1. ON ENTRY, HL points to THE START OF A 3 BYTE * ;* STRUCTURE IN MEMORY: * ;* DW CCRRh ;SCREEN ADDRESS COLUMN AND ROW * ;* DB XXh ;binary number(e.g. track) * ;* 2. The cursor is moved to the appropriate address on * ;* the CRT (as defined by the word CCRRh) and the * ;* ASCII decimal representation of the byte XXh is * ;* printed on the CRT. * ;* 3. On exit, HL points to the entry HL+3 so it is ready * ;* if the next 3 byte structure is to be displayed on * ;* the screen. * ;* * ;**************************************************************** byt2crt:ld a,(hl) ;get the byte to be converted from memory inc hl LD c,(HL) INC HL LD b,(HL) call curadd ;move cursor to track number position INC HL cp 0FFh ;if FF then never calibrated or something jr z,is0FFh ld b,10d ;prepare to subtract 10s from the hex number call asc call outchre ;print the tens character that is in register E add a,b ;add the 10 that was subtracted to make neg. # ld b,1 ;prepare to subtract 1s from the hex number call asc call outchre ;print the ones character that is in register E ret ;routine exits here unless is0FFh is executing asc: ld e,'0' ;initialize the ASCII representation 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 e ;increment the ASCII representation jr ascsub ;** 0FFh indicates uncertainty, as a drive that hasn't been recalibrated.** is0FFh: ld a,'?' ;send '??' to the CRT call outchra call outchra ret ;**************************************************************** ;* * ;* 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 (curtrh),a ;save the first digit (in ASCII) ld bc,2A22h ;B=column address w/offset,C=row addr w/offset call curadd ;move cursor to track number position call outchre ;print the first digit get2nd: call inchr ;get the 2nd digit of track number 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 e,a ;save the ASCII character just entered sub 30h ;make the ASCII char. into a binary ones number add d ;add the binary value of the tens digit ld b,a ;save the sum of the tens+ones ld a,(maxtrk) ;get the highest allowed track number cp b ;compare it with the proposed new track number jr c,get2nd ;if new track would be larger than max track ld a,b ;put the new binary track number in register A ld (curtrk),a ;save the new current track number call outchre ;print the second digit LD HL,CYLNDR ;prepare for BYT2CRT:pointer to cylinder number ld (hl),a ;store new track number in cylndr also call BYT2CRT ;display the new track number on the CRT jp seek ;go do the seek ;**************************************************************** ;* * ;* GET1DIG ROUTINE * ;* Get a digit and display it on the CRT. On entry, BC * ;* contains column and row (with offset) of the screen * ;* address at which the digit will be displayed. HL * ;* points to the location in memory in which to store the * ;* digit. * ;* * ;**************************************************************** get1dig:call curadd ;cursor address subroutine call get ;get valid digit, send it to CRT,return it in A sub 30h ;make the ones a binary value in register A ld (hl),a ;save the binary value in memory 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 ;************************************************************************ ;* * ;* GET2DIG ROUTINE * ;* * ;* 1. ON ENTRY, HL points to THE START OF A 3 BYTE STRUCTURE IN * ;* MEMORY: * ;* DB XXh ;binary number(e.g. track) * ;* DW CCRRh ;SCREEN ADDRESS COLUMN AND ROW * ;* 2. The cursor is moved to the appropriate address on the CRT * ;* (as defined by the word CCRRh) and two digits are then * ;* input from the terminal. They are displayed on the CRT as * ;* they are input. The two digits are converted to a binary * ;* byte and stored in the entry HL (XXh). * ;* * ;************************************************************************ get2DIG:inc hl ;point to row address byte w/offset LD c,(HL) ;prepare BC for call to CURADD INC HL ;point to column address byte w/offset LD b,(HL) ;prepare BC for call to CURADD dec hl dec hl ;point to binary value to be displayed call curadd ;cursor address subroutine ;*NOTE: registers HL must be preserved across these calls* call get call calcten ;returns in D binary value of ASCII tens digit call get sub 30H ;make the ones a binary value in register A add a,d ;A=binary value of the pair of ASCII digits ld (hl),a ;save the binary value in memory jp comand get: call inchr ;get a digit of the maximum track number call valdig ;see if A register has ASCII 0 through 9 jr c,get ;valdig returns with carry set if not 0 thru 9 call outchra ;print the digit on the CRT 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 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,(SECPT) ;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,(FGPL) ;format gap length (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,(secpt) ;USE sectors per track FOR SECTOR COUNT LD B,A ;SAVE COUNT IN REGISTER B LD A,(STRTSEC) ;BEGINNING SECTOR NUMBER LD C,A ;SECTOR NUMBER BEING FORMATED STORED IN REG. C ld a,0E5h ;D: filler byte out (0FBh),a ;send command byte to NEC 765 data port ;************************************************************************ ;* SUBROUTINE CHRN * ;* Gets the cylinder number, head, sector number, and bytes per * ;* sector that is needed as part of some of the NEC 765 commands, * ;* including the format a track command. * ;************************************************************************ chrn: ld a,(cYLNDR);urtrk) ;get the current cylinder number (0-39) ei halt ;wait for an interrupt out (0FBh),A ;send C information to NEC 765 data port ld a,(sidbit) ;side is determined by bit 2 rrca ;side is in bit 1 rrca ;side is in bit 0 ei halt ;wait for an interrupt out (0FBh),A ;send H information to NEC 765 data port ld a,c ;sector (Record) number being written ei halt ;wait for an interrupt out (0FBh),A ;send R information to NEC 765 data port ld a,(secsize) ;sector size code ei halt ;wait for an interrupt out (0FBh),A ;send N information to NEC 765 data port inc c ;number of sector header being written dec b ;decrement count of sectors remaining to write jr nz,chrn ;if have not supplied C,H,R,N for all sectors call indata ;read ST0, ST1, ST2, C, H, R, N jp comand ;************************************************************************ ;* * ;* SUBROUTINE INDATA * ;* * ;************************************************************************ indata: ld hl,datbuf ;7 memory bytes for temporary result storage ld b,7 ;there are 7 result bytes to be read indat9: calì rdrdù ;is NEC 765 ready to send another result byte in a,(0FBh) ;read a result byte ld (hl),a ;temporarily store this result byte inc hl ;point to next temporary storage location dec b ;one less result byte remains to be read jr nz,indat9 ;if more result bytes remain to be read ret ;************************************************************************ ;* * ;* SUBROUTINE HEADOUT * ;* Prints ST0, ST1, ST2, and CHRN (obtained from READ ID * ;* command and read data command) on the CRT. * ;* * ;************************************************************************ headout:ld bc,5B20h ;B=column address w/offset,C=row addr w/offset ld a,(datbuf+5) ;datbuf+5=record (sector) number byte of result add c ;add row number to cylinder number ld c,a ;prepare bc register pair for call to curadd call curadd ;move cursor to data display position ld hl,datbuf-1 ;7 result bytes are stored in datbuf ld b,7 ;7= count (number of bytes in result) indat: inc hl push bc push hl ld a,(hl) ;get a result byte frm temporary memory storage ld ix,indat1 jp lbyte ;convert the a register to ascii and print indat1: ld a,' ' ;ascii space call outchra ;print a space pop hl pop bc ;get count dec b ;decrement count jr nz,indat ;if all 7 bytes not yet printed jp comand ;done if all 7 bytes have been printed ;**************************************************************** ;* * ;* CURADD SUBROUTINE * ;* * ;* DIRECT CURSOR ADDRESSING SUBROUTINE: upon entry * ;* to CURADD reg. B should contain COLUMN address with * ;* offset. Reg. C should contain ROW address with offset. * ;* All registers are returned unchanged. * ;* * ;**************************************************************** curadd: PUSH Af ld a,1Bh ;esc character-TERMINAL DEPENDENT call outchra ;print the character in register A ld a,'=' ;= character-TERMINAL DEPENDENT call outchra ;output character in register a ld a,(curflg) ;flag for row-column or reversed addr or a ;see if 00 or 0FFh jr nz,colout rowout: ld a,b ;output column address call outchra ;print the character that is in reg A jr nz,DCAdon ;;;;;;; ret nz ;is column next or are we done ? colout: ld a,c ;output row address call outchra ;print the character that is in reg A jr nz,rowout ;is row next or are we done ? dcadon: POP Af ret ;************************************************************************ ;* * ;* SUBROUTINE VALDIG * ;* * ;* Enter with a character in A. This subroutine checks the * ;* character and returns with the carry cleared if the character * ;* is between ASCII 0 and 9 (30h through 39h). If A contained * ;* anything else this subroutine returns with the carry set. * ;* All registers are returned unchanged. * ;* * ;************************************************************************ valdig: cp '0' ;is the character is a digit (ASCII 0-9)? ret c ;return with carry set if char less than '0' cp '9'+1 ;this will set carry if character '9' or less ccf ;complement carry ret ;return with carry set if char greater than '9' ;************************************************************************ ;* * ;* SUBROUTINE CALCTEN * ;* * ;* Enter with a ASCII digit 0-9 in register A. CALCTEN returns * ;* in register D the binary value of that number. Changes * ;* registers A and D only. * ;* * ;************************************************************************ calcten:push bc sub '0' ;maximum high decimal track digit in binary add a,a ;A=2*high decimal track digit ld b,a ;B=2*high decimal track digit add a,a ;A=4*high decimal track digit add a,a ;A=8*high decimal track digit add a,b ;A=10*high decimal track digit ld d,a ;return the binary value in register D pop bc ret ;**************************************************************** ;* * ;* INCHR SUBROUTINE * ;* * ;* Checks the buffer to see if it has characters in it. * ;* Fetches a character from the buffer if there are any. * ;* Else fetches a character from the console. * ;* (Inchr will return a character from the console when * ;* bufcnt (memory location) is zero.) * ;* Returns character in register A, and since this routine * ;* is nearly always running, it keeps the motor running by * ;* calling MOTRUN. * ;* Registers BC, DE, and HL are preserved. * ;* * ;**************************************************************** ;*use microdecision sense drive status command to get status byte 3 (st3) and ;*to select the side inchr: push bc push de push hl call cmdnew ;is data port ready to receive a new command? ld a,04 ;sense drive status command out (0FBh),a ;issue the command call cmdrdy ;is data port ready to receive a command byte ld a,(sidbit) ;get head select bit (coded into bit 2) ld e,a ld a,(motdrv) ;bit 0=US0, BIT 1=US1 for drive selection or e ;merge head select bits with drive select bits out (0FBh),a ;byte 2 of drive status command ;*NOTE: reading the result is delayed until later in the INCHR routine in order ;*to keep side one selected (if it is) as long as possible ;** GET A CHARACTER FROM THE CONSOLE or from the batch command buffer ** ld a,(bufcnt) or a ;see if buffer count is 0, meaning buffer empty jr z,charin ;if buffer is empty dec a ;decrement count ld (bufcnt),a ;dec. count of chars left to get from buffer jr z,bufres ;if no characters left to get from buffer call motrun in a,(status) ;read status register and rdbit jr z,frombuf ;no character entered,get character from buffer ;* BUFRES executes if character entered at keyboard to terminate buffer command bufres: xor a ;zero register A;prepare to exit batch command ld (bufcnt),a ;reset count of chars left to get from buffer ld (bufchr),a ;reset count of characters gotten from buffer ld bc,2729h ;B=column address w/offset,C=row addr w/offset call curadd ;move cursor to 'Command;' ld e,'>' ;replace 'Command;' with 'Command>' call outchre ld a,(buftot) ;total number of chars originally in buffer ld e,' ' ;send a blank space buftot number of times call chrfeed ld a,' ' ;dummy character to return to caller jp getst3 ;exit batch command, now get character from kbd ;** Get a character from the buffer. ** frombuf:ld hl,(bufptr) ;get buffer pointer ld a,(hl) ;get a character from the buffer inc hl ;point to next character in the buffer ld (bufptr),hl ;save buffer pointer into memory ld hl,bufchr ;increment count of characters gotten from inc (hl) ; buffer so far jr getst3 charin: call motrun in a,(status) ;read status register and rdbit jr z,charin ;no character ready, loop in a,(rdreg) ;read character and 07Fh ;strip parity getst3: push af ;preserve the character and the flag call rdrdy ;is data port ready to send a result byte? in a,(0FBh) ;read status byte 3 ld (st3),a ;save status register 3 information pop af ;retrieve the character and the flag pop hl pop de pop bc ret ;**************************************************************** ;* * ;* SUBROUTINE MOTRUN * ;* * ;* KEEP MOTOR RUNNING IF MOTOR ON (MOTOGL=0FFh) SELECTED * ;* * ;* THIS ROUTINE RUNS ONCE BEFORE EACH CHECK FOR A * ;* CHARACTER FROM THE TERMINAL or the buffer. THIS MAKES * ;* THE MOTOR RUN. * ;* * ;**************************************************************** ;run the microdecision controlled motor motrun: ld c,00000001b ;motor on bit: prepared if motdrv is 0(drive 0) ld a,(motdrv) ;get the drive number (0-3) ld b,00010000b ;upper four bits will drive drive select lines jr drvtst ;see if the drive is drive 0 or not mddrv1: ld c,00000010b ;must be drive 1, 2,OR 3 (MD wants only 1 or 2) dec a ;try the next drive rlc b ;select next drive higher drive bit drvtst: or a ;see if correct drive has been reached jr nz,mddrv1 ;if haven't reached correct drive ld a,(motogl) ;see if motor has been selected to be on or a ; 0=off 1=on jp z,drivon ;if motor is not supposed to be on ld a,c ;A<--motor line;bit1=drive 0 motor;2=drive1,2,3 drivon: or b ;add in the drive select line to the motor line out (0F7h),a ;turn on selected microdecision drive (RV2 BDS) out (0F8h),a ;for rev. 1 microdecision boards ret ;************************************************************************ ;* * ;* SUBROUTINE SEEK * ;* * ;* STEPS THE DRIVE HEAD TO THE TRACK SPECIFIED IN BYTE CURTRK. * ;* THE SEEK COMMAND IS A THREE BYTE COMMAND. * ;* * ;************************************************************************ seek: call cmdnew ;is data port ready to receive a new command? ld a,0Fh ;NEC 765 seek command code out (0FBh),a ;send seek command to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte ld a,(sidbit) ;bit 2 is 0 for side 0, 1 for side 1 ld e,a ld a,(motdrv) ;bit 0=US0, BIT 1=US1 for drive selection or e ;merge head select bits with drive select bits out (0FBh),a ;byte 2 of seek command call cmdrdy ;is data port ready to receive a command byte ld a,(curtrk) ;byte 3 selects the cylinder number out (0FBh),a im0 ei halt ;wait for an interrupt call sensint ;sense interrupt status (to terminate command) jp comand ;************************************************************************ ;* * ;* SUBROUTINE SENSINT * ;* THIS ROUTINE PERFORMS THE SENSE INTERRUPT STATUS COMMAND OF * ;* THE NEC 765 DISK CONTROLLER. IT IS NECESSARY TO PERFORM THIS * ;* COMMAND FOLLOWING SEEK AND RECALIBRATE COMMANDS IN ORDER TO * ;* PROPERLY TERMINATE THEM. * ;* * ;************************************************************************ sensint:call cmdnew ;is data port ready to receive a new command? ld a,08 ;sense interrupt status command(clears FDD0-3) out (0FBh),a ;issue the command ld bc,4725h ;B=column address w/offset,C=row addr w/offset call curadd ;move cursor to ST0 position call rdrdy ;is data port ready to send a result byte? in a,(0FBh) ;read status 0 ld (ST0),a ;save status register 0 information ld ix,sen0 jp lbyte ;convert the a register to ascii and print sen0: call rdrdy ;is data port ready to send a result byte? in a,(0FBh) ;read present cylinder number ld (curtrk),a ;save the track number LD (CYLNDR),A ;ALSO SAVE THE TRACK NUMBER IN CYLNDR ld a,(st0) ;get status register 0 information read earlier and 11100000b ;save interrupt code (7,6)and seek end bits (5) cp 10000000b ;bit 7=1, 6=0 --> 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 'MONSTEP Rev. 2.6' db CR,LF db 'Copyright 1989 Ronald E. Jacobs (Berkeley, CA)' db lf,cr db 'cur track Cylinder head load' db CR,LF db ' side Head unload' db CR,LF db ' motor Record step rate' db CR,LF db ' drive Number' db CR,LF db 'max track 1st sec frmt GPL' db cr,lf db ' sec/trk read GPL' db CR,LF db ' density' 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 hasrun: db 0A5H ;this byte in RAM indicates program has run 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 ;** The following X pairs of source code lines must remain contiguous. ** ;** The following two lines (three bytes) must remain in this order. ** maxtrk: db 39d ;maximum track number in binary dw 2A26h ;maxtrk column addr w/offset, row addr w/offset ;** The following two lines (three bytes) must remain in this order. ** curtrk: db 0FFh ;current track number in binary dw 2A22h ;curtrk column addr w/offset, row addr w/offset ;** The following two lines (three bytes) must remain in this order. ** cylndr: db 0FFh ;current cylinder number in binary dw 3A22h ;cylndr column addr w/offset, row addr w/offset ;** The following two lines (three bytes) must remain in this order. ** secpt: db 05h dw 3A27h ;cylndr column addr w/offset, row addr w/offset ;** The following two lines (three bytes) must remain in this order. ** STRTsec:db 01h dw 3A26h ;cylndr column addr w/offset, row addr w/offset ;** The following two lines (three bytes) must remain in this order. ** datAsec :db 05h dw 3A24h ;cylndr column addr w/offset, row addr w/offset ;** The following two lines (three bytes) must remain in this order. ** SECSIZE:db 3 ;SECTOR SIZE CODE (N) (3=1024 bytes per sector) dw 3A25h ;cylndr column addr w/offset, row addr w/offset ;;;;;sector: db 05 ;sector number to read data from FGPL: DB 05Fh ;format a track gap length (gap 3) RGPL: DB 01Ch ;read data command gap length motdrv: db 0 ;drive #(filled by routine seldrv) 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 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 ;set CTC channel 2 baud rate db 0Dh ;use 68h for 1200 baud; 0Dh for 9600 baud 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