.xlist INCLUDE MON.180 .list ;***************************************************************************** ;* * ;* Program to control the head position, etc., of floppy drives to make * ;* head alignment possible. Also reads sector IDs and reads and writes * ;* sectors. * ;* * ;* Copyright 1989 Ronald E. Jacobs * ;* * ;* (latest work done 05/29/89) * ;* * ;***************************************************************************** if alignment ;assemble this alignment code? ;************************************************************************ ;* * ;* the following is a user setable equate * ;* * ;************************************************************************ diradfg equ 0 ;1 for ADM20; 0 for ADM31 cursor addressing stack equ 2140h ;stack space, RAM use here cr equ 0Dh ;carriage return lf equ 0Ah ;line feed bs equ 08h ;backspace ;**************************************************************** ;* * ;* ROUTINE DISPLA * ;* * ;* SCROLLS WHATEVER IS ON THE SCREEN OFF AND DISPLAYS * ;* THE COMMANDS AND OTHER INFORMATION ON THE SCREEN. * ;* * ;**************************************************************** align: ld sp,stack ;starting address of stack 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 ld hl,maxtrk ;point to max track memory storage for byt2crt ld d,08d ;number of times to call byt2rt out2crt:call byt2crt ;display a byte in decimal on the CRT dec d ;decrement number of times to call byt2crt jr nz,out2crt ;if byt2crt not called required number of times call fgpstr ;print the format gap in hexicecimal call rgpstr ;print the read gap in hexidecimal call bufstr ;print the sector buffer pointer on CRT 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 drvstr ;print the drive number(0 to 4) after DRIVE call hltstr ;print the head load time after HEAD LOAD call hutstr ;print the head unload time after UNLOAD call srtstr ;print the step rate time after STEP RATE comand: IM0 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 call hexcrt ;convert A register to ASCII and send to CRT ;;;;;;; ld ix,st3done ;;;;;;; jp lbyte ;convert the a register to ascii and print ;;;;;;;st3done: ld c,28h ;C=row address-1 with offset ld hl,(bufchr) ;# of chars in cmd buffer display up to cursor ld de,0008h ;column address+offset of Command: add hl,de ;HL=# of cols from start of cmd row to cursor ld de,80d ;number of columns in a row or a ;clear the carry flag subline:inc c ;increment the row address sbc hl,de ;find # of rows (# of 80s) in total of chars jr nc,subline ;loop until 1 too many rows is subtracted add hl,de ;add back the extra row that was subtracted ld a,20h ;20h=cursor addressing offset add a,l ;A=column address with offset ld b,a ;B=column address with/offset 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 jr c,whatchr ;if typed character wasn't a digit (wasn't 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 ld hl,vectabl ;table of addresses of routines ld e,'A' ;first letter of alphabet ld d,26d ;try all of the letters of the alphabet findltr:cp e ;see if char from keyboard was current letter jr z,gotltr ;if char was current letter of the alphabet inc hl ;point to next entry in table inc hl inc e ;try the next letter of the alphabet dec d ;decrement count of letters left to try jp z,comand ;if all letters have been tried (was no match) jr findltr ;see if the next letter is a match gotltr: ld e,(hl) ;get byte of address to jump to inc hl ;point to other byte of address to jump to ld d,(hl) ;get the other byte of address to jump to ex de,hl ;put the address to jump to in HL jp (hl) vectabl:dw currev ;A dw bufaddr ;B specify the sector buffer address dw getcyl ;C get & display 2 digits on CRT, store binary dw data ;D read a sector of data dw id ;E read a sector ID field dw density ;F dw setrgpl ;G set read gap length dw gethead ;H get & display 2 digits on CRT, store binary dw stepi ;I dw specify ;J set head load, head unload, or step rate time dw comand ;K IS UNASSIGNED dw seldrv ;L dw motor ;M dw getsecz ;N get & display 2 digits on CRT, store binary dw stepo ;O dw pause ;P dw comand ;Q IS UNASSIGNED dw getrecd ;R get & display 2 digits on CRT, store binary dw sidsel ;S dw setfgpl ;T set format gap length dw getsec1 ;U get & display 2 digits on CRT, store binary dw getsecp ;V get & display 2 digits on CRT, store binary dw write ;W write a sector of data dw comand ;X IS UNASSIGNED dw recal ;Y dw sleep ;Z getcyl: ld hl,cylndr ;point to cylinder memory storage structure jr rget2 ;if 'C' get & display 2 digits on CRT,store binary gethead:ld hl,head ;point to head memory storage structure jr rget2 ;if 'H' get & display 2 digits on CRT,store binary getsecz:ld hl,secsize ;point to number memory storage structure jr rget2 ;if 'N' get & display 2 digits on CRT,store binary getrecd:ld hl,record ;point to record memory storage structure jr rget2 ;if 'R' get & display 2 digits on CRT,store binary getsec1:ld hl,strtsec ;point to strtsec memory storage structure jr rget2 ;if 'U' get & display 2 digits on CRT,store binary getsecp:ld hl,secpt ;point to secpt memory storage structure rget2: jp get2dig ;if 'V'get&display 2 digits on CRT,store binary ;************************************************************************ ;* * ;* 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 control characters from cursor to end of 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 (hl),0 ;store a null as end of buffer marker ;** Enter here from RPTBUF routine: ** initins:xor a ;zero A: 00=insert, 0FFh=overwrite jr savins ;save insert on (overwrite off) in ram loadbuf:call inchr ;get a character from the keyboard ;** Don't accept either '^R' or ';' in the buffer string.* cp 12h ;12h is ^R jr z,loadbuf ;if ^R, don't do anything with it, ignore it. cp ';' jr z,loadbuf ;if ';', don't do anything with it, ignore it. ;** 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) savins: 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 ld d,0 ;use D=count of control characters after cursor ;** 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 outctl ; ...sending the character to the crt jr movbufl mvbfl1: pop bc ;get pointer to char. where cursor should be dec hl ;char was deleted so point to previous buf loc call bscursr jr loadbuf ;** Backspace the cursor from the end of the buffer to where it belongs. ** bscursr:LD A,2 ;send 2 space characters to write over last... LD E,' ' ; ...char(s)(in case deleted char=cntl char) CALL CHRFEED ;write spaces over the last characters on CRT ;;;;;;; CALL OUTCHRA ;write spaces over the last characterS on CRT push hl ;save pointer to next unused char loc in buffer ld e,d ;D=number of control characters after cursor ld d,0 ;DE=number of control characters after cursor add hl,de ;add the number of control characters to HL inc hl ;compensate for the two spaces output above inc hl 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: push hl ;save entry HL or a ;clear carry flag sbc hl,bc ;compare BC with HL pop hl ;retrieve entry HL 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: push hl ;save pointer to next unused char loc in buffer ld hl,cmdbuf ;pointer to start of buffer call cpbchl ;compare BC with HL:returns Z if =; NZ if not = pop hl ;get pointer to next unused char loc in buffer jr z,loadbuf ;if there are no characters to back space to dec bc ;dec. pointer to buffer location at the cursor ld a,(bc) ;get character from under cursor cp 20h ;ASCII control characters are 0 through 1Fh ld a,bs ;put a back space in A call c,outchra ;if char was a control char, backspace twice call outchra ;backspace the cursor on the CRT loadbf1:jr loadbuf ;go get another character ;** see if the character just received was a forward space. ** isctld: cp 04h ;04h = ^D jr nz,iscr ;if not ^D ld a,(bc) ;get the character at cursor from the buffer cp 0 ;see if already at end of buffer jr z,loadbf1 ;if there are no characters to forward space to call outctl inc bc ;inc. pointer to buffer location at the cursor jr loadbf1 ;go get another character ;*Send a character to the CRT. Preceed it with ^ if it's a control character.* outctl: cp 20h ;ASCII control characters are 0 through 1Fh ld e,'^' ;send a caret to the crt call c,outchre ;if keyboard character is a control character ld e,a jr nc,bufchra ;if keyboard character not a control character set 6,e ;convert the control char. to a printing char. inc d ;increment count of control characters bufchra:jp outchre ;send the char to the CRT: use outchre's return ;** See if the character just received was a carriage return. ** iscr: cp cr ;is it a carriage return? jr z,setbuf ;if it was a carriage return ;** End up here if character other then CR, ^V, back space or forward space.** ldbuf: push af ;save a copy of the character from keyboard call outctl ;output the char & keep count of control chard pop af ;retrieve the copy of character from keyboard ld d,0 ;initialize D=count of cntrl chars after cursor 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 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 inc bc ;increment pointer to buffer char. at cursor ld (hl),a ;store a null indicating end of 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 outctl ;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: call cpbchl ;compare BC with HL:returns Z if =; NZ if not = inc bc ;increment pointer to buffer char. at cursor jp nz,rdisply ;if cursor is before end of buffer inc hl ;point to next storage location in buffer ld (hl),0 ;store zero as end of buffer marker rdisply:push bc rdsply: ld a,(bc) ;A=next character after the cursor or a ;see if character under cursor is 00 (null) jr z,mvbfdon ;if all characters have been redisplayed call outctl ;send the character(+ '^' if control)to the crt inc bc ;next character between cursor and buffer end jr rdsply ;************************************************************************ ;* * ;* 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. * ;* * ;************************************************************************ setbuf: call resbuf ;set BUFACTV=0 and BUFCHR counter to 0 dec (hl) ;MAKE BUFACTV=0FFh, indicating buffer is active ;;;;;;; ld a,0FFh ;0FFh indicates that buffer is in use ;;;;;;; ld (bufactv),a ;BUFACTV= 0FFh if active, 0 if not active ld hl,cmdbuf ;start of command buffer ld (bufptr),hl ;store pointer in memory jp comand resbuf: xor a ;zero register A ld hl,bufchr+1 ;point to high byte of BUFCHR ld (hl),a ;zero high byte of buffer displayed character counter dec hl ;point to low byte of BUFCHR(# of chars + '^'s) ld (hl),a ;zero low byte of BUFCHR (BUFCHR is a word) dec hl ;HL points to buffer active flag (BUFACTV) ld (hl),a ;0 indicates buffer is inactive ret ;************************************************************************ ;* * ;* 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 bufcrt: ld b,h ;BC <-- HL ld c,l ld a,(hl) ;get a character from the buffer or a ;end of string in buffer is marked with 00 jp z,initins ;if all characters have been reprinted on CRT call outctl ;send the character(+ '^' if control)to the crt inc hl ;point to next character in buffer jr bufcrt ;************************************************************************ ;* * ;* CURREV ROUTINE * ;* * ;* Toggles a BIT 0, which the cursor addressing * ;* routine uses to reverse or not reverse the order * ;* of transmission of row and column to the terminal. * ;* * ;************************************************************************ currev: ld hl,curflg inc (hl) ;toggle bit 0 jp displa ;repaint the screen then fall thru to comand ;**************************************************************** ;* * ;* SUBROUTINE INNYBLS * ;* * ;* Input up to four hex digits from the keyboard. Enter * ;* with number of nybbles to get in B (1-4). Returns * ;* result in HL. Example: * ;* enter with b=4 * ;* enter BA98 at keyboard. * ;* returns BA98 in HL. (BA IN H, 98 IN L) * ;* * ;**************************************************************** innybls:ld hl,00 ;initialize HL. HL will hold up to four nybbles innyb: call inchr ;get a character from the keyboard ld e,a ;save a copy of the character sub '0' ;make binary from ASCII jr c,innyb ;if character was less then '0' cp 9+1 ;binary 10d jr c,is0to9 ;if character is '0'-'9' ld a,e ;get a fresh copy of the character and 11011111b ;convert character to upper case cp 'A' jr c,innyb ;if less then 'A' cp 'G' jr nc,innyb ;if greater then 'F' call outchra ;display the A-F on the CRT sub 'A'-10d ;convert to 0Ah through 0Fh jr savnyb ;save the nybble is0to9: call outchre ;display the 0-9 on the CRT savnyb: add hl,hl ;on first pass HL is zero as initialized above add hl,hl ;on successive passes each nybble is shifted add hl,hl ; into next higher nybble add hl,hl or l ;add nybble from register A to register L ld l,a ;save in register L dec b ;decrement count of nybbles to get jr nz,innyb ;if haven't gotten all four nybbles ret ;**************************************************************** ;* * ;* BUFADDR ROUTINE * ;* * ;* Input four hex digits from the keyboard which specify * ;* a new sector buffer starting address. * ;* * ;**************************************************************** bufaddr:ld bc,2A28h ;b=column address w/offset,c=row addr w/offset call curadd ;move cursor to 'buffer' ld b,4 ;count of nybbles (characters) to get from KBD call innybls ;returns binary number in HL ld (secbuf),hl ;save the new address in RAM jp comand bufstr: ld bc,2A28h ;b=column address w/offset,c=row addr w/offset call curadd ;move the cursor to 'buffer' ld hl,secbuf+1 ;point to high byte of buffer address call bufstr1 ;convert high byte to ASCII and send to CRT dec hl ;point to low byte of buffer address bufstr1:ld a,(hl) ;get the byte to send to the CRT call hexcrt ;convert the byte to ASCII and send to CRT ret ;**************************************************************** ;* * ;* SETFGPL ROUTINE * ;* * ;* Input two hex digits from the keyboard which specify * ;* a new format gap length. * ;* * ;**************************************************************** setfgpl:ld bc,4B26h ;b=column address w/offset,c=row addr w/offset call curadd ;move cursor to FORMAT GAP ld b,2 ;count of nybbles (characters) to get from KBD call innybls ;returns binary number in hl ld a,l ;put the two nybbles into register A ld (fgpl),A ;save the new address in RAM jp comand fgpstr: ld bc,4B26h ;b=column address w/offset,c=row addr w/offset call curadd ;move the cursor to FORMAT GAP ld a,(FGPL) ;get the byte to send to the CRT call hexcrt ;convert the byte to ASCII and send to CRT ret ;**************************************************************** ;* * ;* SETRGPL ROUTINE * ;* * ;* Input two hex digits from the keyboard which specify * ;* a new read gap length. * ;* * ;**************************************************************** setrgpl:ld bc,4B27h ;b=column address w/offset,c=row addr w/offset call curadd ;move cursor to READ GAP ld b,2 ;count of nybbles (characters) to get from KBD call innybls ;returns binary number in HL ld a,l ;put the two nybbles into register A ld (rgpl),a ;save the new address in RAM jp comand rgpstr: ld bc,4B27h ;b=column address w/offset,c=row addr w/offset call curadd ;move the cursor to READ GAP ld a,(rgpl) ;get the byte to send to the CRT call hexcrt ;convert the byte to ASCII and send to CRT ret ;**************************************************************** ;* * ;* 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 (FDCDATA),a;(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 (FDCDATA),a;(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 ;************************************************************************ ;* * ;* SPECIFY ROUTINE * ;* Allows specification of head load time, step rate, and head * ;* unload time for a drive. * ;* * ;************************************************************************ specify:ld bc,4223h ;B=column address w/offset,C=row addr w/offset call curadd ;move cursor to left of "head unload" position ;;;;;;; ld b,4Ah ;prepare column address for displaying new data call inchr ;get a character from the keyboard and 11011111b ;convert the character to upper case ;;;;;;; ld hl,hlt ;HL points to Head Load Time (hlt) in RAM ;;;;;;; dec c ;decrement row address cp 'L' jr z,hload ;set head load time ;;;;;;; inc hl ;HL points to head unload time (hut) in RAM ;;;;;;; inc c ;increment row address cp 'U' ;head unload time jr z,uload ;set head unload time ;;;;;;; inc hl ;HL points to step rate time (srt) in RAM ;;;;;;; inc c ;increment row address cp 'S' jr nz,specify ;if not an 'S' then try again with another char ;** Set the step rate time. ** srate: call srtstr ;display step rate time on CRT call getupdn ;returns with carry set if a -, cleared if a + inc (hl) ;increment srt so if it was a - we'll be ready jr c,srange ;if character entered at keyboard was a - dec (hl) ;character was a + so decrement the already... dec (hl) ; ...incremented srt srange: ld a,(hl) ;A=step rate time cp 0FFh ;allowed range is 0 to 0Fh jr nz,srt10 ;if not 0FFh ld (hl),0Fh ;if it was 0FFh, make it 0Fh jr SRATE;dispsrt ;will be jumping if it was 0FFh srt10: cp 10h ;10h is out of range jr nz,SRATE;dispsrt ;if not 10h ld (hl),0 ;if it was 10h, make it 0 ;;;;;;dispsrt:call srtstr ;display step rate time on CRT jr srate ;get another '+' or '-' or carriage return ;** Enter with HL pointing to Step Rate Time. ** srtstr: ld hl,srt ;HL points to step rate time (srt) in RAM push hl ;save pointer to step rate time ld bc,4A24h ;B=column address w/offset,C=row addr w/offset ld e,(hl) ;d=step rate time ld d,0 ;de=step rate time ld h,d ld l,10h ;HL=10h OR A;xor a ;clear the carry flag sbc hl,de ;subtract srt from 10h call clkmult ;double HL (SRT times 2) if 4 MHz clock pop hl ;retrieve pointer to step rate time ret ;** Set the head load time. ** hload: call hltstr ;display head load time on CRT call getupdn ;returns with carry set if a -, cleared if a + inc (hl) ;increment hut so if it was a + we'll be ready jr nc,lrange ;if character entered at keyboard was a + dec (hl) ;character was a - so decrement the already... dec (hl) ; ...incremented hut lrange: ld a,(hl) ;A=head load time cp 00 ;allowed range is 1 to 07Fh jr nz,hltFF ;if not 0 ld (hl),07Fh ;if it was 0, make it 07Fh jr HLOAD;times2 hltFF: cp 080h jr nz,HLOAD;times2 ;if not 080h ld (hl),1 ;if it was 080h, make it 1 ;times2: call hltstr ;display head load time on CRT jr hload ;get another '+' or '-' or carriage return hltstr: ld hl,hlt ;HL points to Head Load Time (hlt) in RAM push hl ;save pointer to head load time (HLT) ld bc,4A22h ;B=column address w/offset,C=row addr w/offset ld l,(hl) ;L=head load time ld h,0 ;HL=head load time add hl,hl ;head load time times 2 call clkmult ;double HL again (to times 4) if 4 MHz clock pop hl ;retrieve pointer to head load time (HLT) ret ;** Set the head unload time. ** uload: call hutstr ;display head unload time on screen call getupdn ;returns with carry set if a -, cleared if a + inc (hl) ;increment hut so if it was a + we'll be ready jr nc,urange ;if character entered at keyboard was a + dec (hl) ;character was a - so decrement the already... dec (hl) ; ...incremented hut urange: ld a,(hl) ;A=head unload time cp 0 ;allowed range is 1 to 0Fh jr nz,hut0F ;if not 0 ld (hl),0Fh ;if it was 0, make it 0Fh jr uload hut0F: cp 10h jr nz,uload ;if not 10h ld (hl),1 ;if it was 10h, make it 1 jr uload ;get another '+' or '-' or carriage return hutstr: LD HL,HUT ;HL points to head unload time (hut) in RAM push hl ;save pointer to head unload time (HUT) ld bc,4A23h ;B=column address w/offset,C=row addr w/offset ld l,(hl) ;L=head unload time ld h,0 ;HL=head unload time add hl,hl ;head unload time times 2 add hl,hl ;times 4 (head unload time is in add hl,hl ;times 8 16 (or 32) mS increments) add hl,hl ;times 16 call clkmult ;double HL again (to times 32) if 4 MHz clock pop hl ;retrieve pointer to head unload time (HUT) ret ;************************************************************************ ;* * ;* SUBROUTINE CLKMULT * ;* Enter with HL pair containing a binary numeric value. HL * ;* will be returned unchanged if NEC765 clock rate is 8 MHz. HL * ;* will be returned doubled if clock rate is 4 Mhz. * ;* * ;************************************************************************ clkmult:call curadd ;move cursor to left of SRT,HUT, or HLT value ld a,(clock) ;clock=1 for 8 MHz, 2 for 4 MHz clkmlt1:dec a ;no passes for 8 MHz, 1 pass for 4 MHz jp z,wordcrt ;print word on CRT, use wordcrt's return add hl,hl ;do this 0 times for 8 MHz, once for 4 MHz jr clkmlt1 ;************************************************************************ ;* * ;* SUBROUTINE GETUPDN * ;* Get a '+', '-', or carriage return from the keyboard. If any * ;* other character is entered loop until one of these three is * ;* received. If * ;* '+' return with carry cleared. * ;* '-' return with carry set. * ;* CR send the NEC765 specify command then jp to Comand. * ;* * ;************************************************************************ getupdn: ;;;;;;; call curadd ;move cursor to head step rate time position call inchr ;get a character from the keyboard cp '-' ;see if a '-' was entered at keyboard scf ;if a '-' then return with carry set ret z ;if it was a '-' cp '+' ret z ;if a '+' then returns with carry cleared cp cr ;if carriage return... jr nz,getupdn ;loop if input char not a +, -, or carriage ret ;** Issue the NEC765 specify command. ** call cmdnew ;is data port ready to receive new command? ld a,03h ;NEC 765 specify command code out (FDCDATA),a;(0FBh),a ;send specify command to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte? ld hl,srt ;HL points to step rate time ld a,(hl) ;A=step rate time rlca ;shift step rate time into high nybble rlca rlca rlca dec hl ;HL points to head unload time or (hl) ;high A nybble=step rate;low nybble=head unload out (FDCDATA),a;(0FBh),a ;send second command byte to NEC 765 data port call cmdrdy ;is data port ready to receive a command byte? dec hl ;HL points to head load time ld a,(hl) ;A=head load time rlca ;shift HLT left 1 bit dec hl ;HL points to nondma byte or (hl) ;set bit 0 to 0 if DMA mode;1 if NON-DMA mode out (FDCDATA),a;(0FBh),a ;send second command byte to NEC 765 data port jp comand ;**************************************************************** ;* * ;* SELDRV ROUTINE * ;* * ;**************************************************************** seldrv: ld bc,2C25h ;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 call byt2crt ;display new track number as ASCII pair on CRT jp comand ;return for next command drvstr: ld bc,2C25h ;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 ld hl,head ;address of selected head byte in RAM ld (hl),0 ;store head in RAM (will be zero if sidbit=0) jr z,selend ;if head zero is correct inc (hl) ;increment the head number to 01 selend: call sidstr ;display the new side on the CRT call byt2crt ;display the new head (HL points to it) on CRT jp comand sidstr: ld bc,2C23h ;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 ;**************************************************************** ;* * ;* ID ROUTINE * ;* * ;* Get I.D. information for a sector. * ;* * ;**************************************************************** id: 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 (FDCDATA),a;(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 (FDCDATA),a;(0FBh),a ;byte 2 of READ ID command CALL GET7 ;get 7 result bytes and print on the CRT ;;;;;;; call indata ;get the 7 result bytes of the READ ID command ;;;;;;; jp headout ;print the 7 result bytes on the CRT ;**************************************************************** ;* * ;* WRITE ROUTINE * ;* * ;* Write undeleted data to selected drive, track, head, * ;* and sector. * ;* * ;**************************************************************** write: ld e,0 ;E=skip data flag (SK): must be 0 for write cmd ld d,00000101b ;NEC765 write data command code call rwcmd ;issue the write data command jr wrio ;** This code runs while write data command is executing but awaiting ** ;** first byte request. ** wloop1: and 020h ;see if still executing jp z,dataerr ;jmp if done in a,(FDCSTAT);0FAH) ;get status byte from NEC765/INTEL8272 cp 0B0h ;see if a byte is requested yet jr z,outbyte ;jmp if 1st byte is requested dec de ;decrement time out count ld a,e ;see if timed out or d jp z,dataerr ;if timed out wrio: in a,(FDCSTAT);0FAH) ;get status byte from NEC765/INTEL8272 cp 0B0h ;see if a byte is requested yet jr nz,wloop1 ;loop if byte is not ready ;** This code runs while NEC765 is getting bytes from the disk sector. ** outbyte:dec bc ;dec count of bytes to get from buffer ld a,b ;see if count has reached zero or c jr nz,outbyt1 ;if more than one byte is left to get in a,(FDCTC);0F7h) ;input terminal count if one byte is left outbyt1:ld a,(hl) ;get a byte from the buffer inc hl ;increment memory storage pointer out (FDCDATA),a;(0FBh),a ;write a byte to the sector jr z,get7 ;if all bytes have been written to the sector ei halt ;wait for NEC765 to request a byte from buffer in a,(FDCSTAT);0FAH) ;get status byte from the NEC765 and 020h ;see if still executing jr nz,outbyte ;if still executing jp get7 ;if not still executing ;**************************************************************** ;* * ;* DATA ROUTINE * ;* * ;* Read undeleted data from selected drive, track, head, * ;* and sector. * ;* * ;**************************************************************** data: ld bc,2027h ;B=column address w/offset,C=row addr w/offset call curadd ;cursor address subroutine ld a,0;(SK)<<<<<<<<;SK is skip deleted data address mark (bit 5) ld e,a ;E contains skip data flag (SK) ld d,00000110b ;NEC 765 read data command code call rwcmd ;issue the read data command jr rdio ;** This code runs while read data command is executing but awaiting 1st byte** rloop1: and 020h ;see if still executing jp z,dataerr ;jmp if done in a,(FDCSTAT);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 jp z,dataerr ;if timed out rdio: in a,(FDCSTAT);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,(FDCTC);0F7h) ;input terminal count if one byte is left in1byte:in a,(FDCDATA);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 ei halt ;wait for a byte from the sector in a,(FDCSTAT);0FAh) ;get status byte from the NEC765 and 020h ;see if still executing jr nz,inbyte ;if still executing ;************************************************************************ ;* SUBROUTINE GET7 * ;************************************************************************ get7: call indata ;read the 7 result bytes ST0,ST1,ST2,C,H,R,N JP HEADOUT;jpcall headout ;print the 7 result bytes on the CRT ;NOTE: HEADOUT JUMPS TO COMAND ;;;;;;; jp comand ;************************************************************************ ;* * ;* SUBROUTINE RWCMD * ;* Issues the NEC765 read data OR write data commands. Enter with * ;* register E containing SK (skip data) bit and D containing the * ;* command code (read data or write data command code). * ;* Returns sector size in bytes in BC and pointer to the data * ;* buffer in HL. * ;************************************************************************ rwcmd: LD HL,FDCCMDS ;POINT TO BUFFER FOR SERIES OF BYTES TO OUTPUT PUSH HL ;SAVE POINTER TO FDCCMDS BYTES ;;;;;;; call cmdnew ;is data port ready to receive a new command ld a,0;(MT)<<<<<<<<;MT is multi-track flag (bit 7) 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 d ;NEC 765 (read or write) data command code ;;;;;;; out (FDCDATA),a;(0FBh),a ;send read data command to NEC 765 data port LD (HL),A ;SAVE THE COMMAND BYTE IN THE BUFFER INC HL ;POINT TO NEXT LOCATION IN FDC COMMAND BUFFER ;*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 (FDCDATA),a;(0FBh),a ;byte 2 of read data command LD (HL),A ;SAVE THE COMMAND BYTE IN THE BUFFER INC HL ;POINT TO NEXT LOCATION IN FDC COMMAND BUFFER ;;;;;;; call cmdrdy ;is data port ready to receive a command byte? ld a,(cylndr) ;C: cylinder number ;;;;;;; out (FDCDATA),a;(0FBh),a ;send command byte to NEC 765 data port LD (HL),A ;SAVE THE COMMAND BYTE IN THE BUFFER INC HL ;POINT TO NEXT LOCATION IN FDC COMMAND BUFFER ;;;;;;; call cmdrdy ;is data port ready to receive a command byte? ld a,(head) ;H: get head byte ;;;;;;; out (FDCDATA),a;(0FBh),a ;send command byte to NEC 765 data port LD (HL),A ;SAVE THE COMMAND BYTE IN THE BUFFER INC HL ;POINT TO NEXT LOCATION IN FDC COMMAND BUFFER ;;;;;;; call cmdrdy ;is data port ready to receive a command byte? ld a,(record) ;R: sector number to begin reading at ;;;;;;; out (FDCDATA),a;(0FBh),a ;send command byte to NEC 765 data port LD (HL),A ;SAVE THE COMMAND BYTE IN THE BUFFER INC HL ;POINT TO NEXT LOCATION IN FDC COMMAND BUFFER ;*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 (FDCDATA),a;(0FBh),a ;send command byte to NEC 765 data port LD (HL),A ;SAVE THE COMMAND BYTE IN THE BUFFER INC HL ;POINT TO NEXT LOCATION IN FDC COMMAND BUFFER ;;;;;;; call cmdrdy ;is data port ready to receive a command byte? ld a,0FFh ;EOT: end of track (last sector to read) ;;;;;;; out (FDCDATA),a;(0FBh),a ;send command byte to NEC 765 data port LD (HL),A ;SAVE THE COMMAND BYTE IN THE BUFFER INC HL ;POINT TO NEXT LOCATION IN FDC COMMAND BUFFER ;;;;;;; call cmdrdy ;is data port ready to receive a command byte? ld a,(rgpl) ;read data gap length ;;;;;;; out (FDCDATA),a;(0FBh),a ;send command byte to NEC 765 data port LD (HL),A ;SAVE THE COMMAND BYTE IN THE BUFFER POP HL ;RETRIEVE POINTER TO FDCCMDS BYTES LD B,8 ;COUNT OF 8 BYTES TO OUTPUT TO FDC DATA PORT LD C,FDCDATA ;NEC 765 data port CALL CMDNEW ;is data port ready to receive a new command OUTRW: OUTI ;SEND A BYTE FROM FDCCMDS BUFFER TO DATA PORT CALL CMDRDY ;is data port ready to receive a command byte? JR NZ,OUTRW ;** Calculate BPS before outputting last byte of read data command. ** ;;;;;;; im0 LD HL,128d ;HL=smallest sector size (secsize=N=0 for FM) ;;;;;;; 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) ld hl,(secbuf) ;HL points to start of sector buffer in RAM push hl ;save the pointer for this sector's buffer add hl,bc ;HL points to 1st byte for next sector's buffer ld (secbuf),hl ;save new sector buffer pointer pop hl ;retrieve the pointer for this sectors buffer ;** output the last byte of the read or write 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 FDCDATA 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 (FDCDATA),a;(0FBh),a ;send command byte to NEC 765 data port ;** Loop while waiting for 765/8272 to begin executing the read command. ** ld de,8000h ;load time out count waitex1:in a,(FDCSTAT);0FAh) ;get status byte from NEC765/INTEL8272 bit 5,a ;see if execution has begun(non-DMA gone high?) ret nz ;if execution has begun dilp: bit 7,a ;set if NEC765 is requesting service by Z80 jr z,waitex1 ;if Request for Master (RQM) low(not requested) DATAERR:in a,(FDCSTAT);0FAh) ;get status byte from NEC765/INTEL8272 BIT 6,A ;1 IF FROM NEC765 TO Z80; 0 IF FROM Z80 TO 765 jp NZ,get7 ;IF FROM 765 TO Z80 jmp to GET 7 result bytes ld de,readerr ;address of 'CAN'T READ DATA' message CALL OUTSTR ;ELSE FROM Z80 TO NEC765:SEND 'CAN'T READ' MSG CALL INCHR ;WAIT FOR ANY CHARACTER FROM THE KEYBOARD ld b,15d ;number of times to send string ld de,bsspbs ;address of string backspace, space, backspace call multstr 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 BUFACTV 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,bufactv ;RAM addr:0FFh=buffer active,0= buffer inactive ld e,(hl) ;save state of buffer (active or inactive) ld (hl),0 ;temporarily indicate buffer is inactive call inchr ;go wait for a character & keep motor running cp 03h ;see if ^C was typed ld (hl),e ;restore state of buffer (active or inactive) jr nz,endpaus ;if character wasn't ^C ;**If ^C typed, terminate command string execution.** ld hl,nulbyt ;point to a byte which happens to be 00 ld (bufptr),hl ;bufptr points to null byte(means end of buffer 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 outifbf ;if buffer active don't send character to crt 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 outifbf ;if buffer active don't send character to 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,(bufactv) ;0 IF BUFFER IS NOT ACTIVE, 0FFh IF IS ACTIVE;count of chars left to get from buffer, if any or a ;see if CHARACTERS ARE FROM COMMAND BUFFER;any characters left to get from buffer jr nz,dlyret ;IF CHARACTERS ARE FROM COMMAND BUFFER ;;;;;;; LD BC,2829h ;B=column address w/offset,C=row addr w/offset ;;;;;;; CALL CURADD ;move cursor to 'Command:' ;;;;;;; LD E,' ' ;SEND A SPACE CHARACTER... ;;;;;;; LD A,6 ; ...6 TIMES ;;;;;;; CALL CHRFEED ;CLEAR THE "Z9999" OFF OF THE CRT 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: * ;* 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 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. * ;* 4. Enter at BYTDISP to convert a binary number in HL to * ;* to three ASCII digits (000-999) and send them to the * ;* CRT without moving the cursor. * ;* 5. BC is preserved if entered at BYTDISP. * ;* * ;**************************************************************** byt2crt:ld a,(hl) ;get the byte to be converted from memory inc hl ;point to row addr w/offset ld c,(hl) ;C=row address w/offset inc hl ;point to B=column address w/offset ld b,(hl) ;B=column address w/offset call curadd ;move cursor to selected column-row position inc hl ;HL points to next 3 byte structure cp 0FFh ;if FF then never calibrated or something jr z,is0FFh push hl ;save the address of the next 3 byte group ld l,a ;get the one byte binary value ld h,0 ;zero the high byte since it's a 1 byte value nulbyt equ $-1 ;RAM loc. nulbyt=0 used as dummy buffer end call wordcrt ;turn it into 3 decimal digits and display them pop hl ;get back the address of the next 3 byte group ret wordcrt:push bc ;if entered here, registers BC are preserved ld e,' ' ;initialize the ASCII representation to ' ' ld c,100d ;prepare to subtract 100s from the hex number ld b,0 ;make BC=100d call asc ;find and display the number of hundreds in HL add hl,bc ;add the 100 that was subtracted to make neg. # ld c,10d ;prepare to subtract 10s from the hex number call asc ;find and display the number of tens in HL add hl,bc ;add the 10 that was subtracted to make neg. # ld e,'0' ;at least have a '0' in the ones position ld c,1 ;prepare to subtract 1s from the hex number call asc ;find and display the number of ones in HL pop bc ret ;routine exits here unless is0FFh is executing asc: xor a ;set the carry to 0 and zero register A ld a,e ;copy the ' ' or '0' into A ascsub: sbc hl,bc ;find number of 100s,10s,or 1s in the hex numbr jr c,ascret ;if less then 10 or 1 remains in the hex number inc a ;increment the ASCII representation jr ascsub ascret: cp ' ' ;see if still a ' ' jp z,outchra ;when suppressing leading 0; use outchra's ret set 4,e ;change register E from ' '(20h) to '0' (30h) or e ;set bit 4 in register A (make it 30h-39h) jp outchra ;print the 100s, 10s, or 1s digit that is in E ;** 0FFh indicates uncertainty, as a drive that hasn't been recalibrated.** is0FFh: ld a,' ' call outchra ld a,'?' ;send ' ??' to the CRT call outchra jp outchra ;use outchra's return ;**************************************************************** ;* * ;* 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 bc,2B22h ;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 ;************************************************************************ ;* * ;* 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 inc b ;get2dig displays 2 digits, BYT2CRT shows 3 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 (FDCDATA),a;(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) ;side bit is coded into bit 2 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 (FDCDATA),a;(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 (FDCDATA),a;(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 (FDCDATA),a;(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 (FDCDATA),a;(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 formatted stored in reg. C ld a,0E5h ;D: filler byte out (FDCDATA),a;(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) ;get the current cylinder number (0-39) ei halt ;wait for an interrupt out (FDCDATA),a;(0FBh),A ;send C information to NEC 765 data port ld a,(head) ;head (usually is 0 or 1) ei halt ;wait for an interrupt out (FDCDATA),a;(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 (FDCDATA),a;(0FBh),A ;send R information to NEC 765 data port ld a,(secsize) ;sector size code ei halt ;wait for an interrupt out (FDCDATA),a;(0FBh),A ;send N information to NEC 765 data port inc c ;number of sector 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 LD C,FDCDATA ;NEC765 FLOPPY DISK CONTROLLER DATA PORT indat9: calì rdrdù ;is NEC 765 ready to send another result byte ;;;;;;; in a,(FDCDATA);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 INI jr nz,indat9 ;if more result bytes remain to be read ret ;************************************************************************ ;* * ;* SUBROUTINE HEXCRT * ;* * ;* Convert a byte to ASCII and send the two characters to the CRT. * ;* Enter with the byte to be converted in register A. * ;* * ;************************************************************************ hexcrt: ld e,a ;save a copy of the byte into register E rrca ;get the high nybble first rrca rrca rrca call conv ;convert the high nybble to an ASCII character ld a,e ;get the copy of the byte, now do low nybble conv: and 0fh ;convert the nybble to an ASCII character add a,090h daa adc a,040h daa call outchra ;send ASCII representation of the nybble to CRT 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 call hexcrt 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 call bufstr ;print the end of buffer address on the CRT 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 rrca ;see if bit 0 is 0 or 1 jr nc,colout rowout: ld a,b ;output column address call outchra ;print the character that is in reg A jr nc,dcadon colout: ld a,c ;output row address call outchra ;print the character that is in reg A jr nc,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 to see if characters are comming from buffer. * ;* 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 * ;* BUFACTV (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 (FDCDATA),a;(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 (FDCDATA),a;(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,(bufactv) ;0 if inactive, 0FFh if active or a ;see if buffer count is active jr z,charin ;if buffer is inactive ld hl,(bufptr) ;HL points to current byte in buffer ld a,(hl) ;get current byte from buffer PUSH HL ;SAVE POINTER TO CURRENT BYTE IN BUFFER LD HL,(BUFCHR) ;HL=NUMBER OF CHARACTERS+'^'s IN BUFFER STRING INC HL ;ADD 1 TO NUMBER OF CHARACTERS CP 20h ;control characters are 0-1Fh JR NC,SAVECNT ;IF NOT A CONTROL CHARACTER INC HL ;ADD 1 MORE(FOR THE '^')IF IT WAS A CNTRL CHAR SAVECNT:LD (BUFCHR),HL ;SAVE NUMBER OF CHARS='^'s in memory POP HL ;RETRIEVE POINTER TO CURRENT BYTE IN BUFFER or a ;see if current byte is 0 jr z,bufres ;end of buffer marked by 00 call motrun in a,(status) ;read status register and rdbit ld a,(hl) ;get another copy of current byte from buffer inc hl ;HL points to current byte+1 in buffer ld (bufptr),hl ;save pointer to next byte in buffer jr z,getst3 ;nothing from kbd, so use character from buffer in a,(rdreg) ;read character to clear out I/O chip ;* BUFRES executes if character entered at keyboard to terminate buffer command bufres: CALL RESBUF ;RESET BUFfer ACTiVe flag & BUFCHR counter to 0 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 hl,cmdbuf ;point to start of buffer ld e,' ' ;prepare to send a blank space wipelin:ld a,(hl) ;get a character from the buffer cp 0 ;see if end of buffer has been reached jr z,retdumy ;if end of buffer let comand reposition cursor cp 20h ;control characters are 0-1Fh call c,outchre ;if char in buffer is a control character call outchre ;send a space inc hl ;point to next character in buffer jr wipelin retdumy:ld a,' ' ;dummy character to return to caller jp getst3 ;exit batch command, now get character from kbd 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,(FDCDATA);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) is 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 (FDCDATA),a;(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 (FDCDATA),a;(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 (FDCDATA),a;(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 (FDCDATA),a;(0FBh),a ;issue the command ld bc,4325h ;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,(FDCDATA);a,(0FBh) ;read status 0 ld (ST0),a ;save status register 0 information call hexcrt ;convert the A register to ASCII and print ;;;;;;; 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,(FDCDATA);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,(FDCSTAT);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: PUSH AF ;SAVE THE STATUS FLAGS call mddly ;delay 12.5 uS cmdrdy1:in a,(FDCSTAT);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 POP AF ;RETRIEVE THE STATUS FLAGS 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,(FDCSTAT);f0FAh) ;read NEC 765 status port LD (FDCST),A ;SAVE FLOPPY DISK CONTROLLER STATUS IN MEMORY 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 OUTIFBF * ;* If characters are not comming from the buffer, output the * ;* character in register A to the CRT. If characters are comming * ;* from the buffer don't output any character to the CRT. * ;* * ;* All registers are preserved wether or not character is sent. * ;************************************************************************ outifbf:push de ;save the DE register pair ld d,a ;save the character which might be output ld a,(bufactv) ;0 if buffer is not active, 0FFh if is active or a ;see if characters are from command buffer ld a,d ;retrieve the character which might be output pop de ;retrieve the DE register pair ret nz ;ret if characters are from command buffer jp outchra ;else buffer inactive so send character to CRT ;************************************************************************ ;* * ;* 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 is terminated by setting bit 7 in the last character. * ;* * ;* However, if a space character (' ') with bit 7 set is found, * ;* a space character is sent to the CRT for the number of times * ;* in the next byte in the string. * ;* * ;************************************************************************ outstr: ld a,(de) ;get a character of the string to be printed bit 7,a ;bit 7 is set on the last character res 7,a ;clear bit 7 (the parity bit) jr z,notset ;if character wasn't last or multiple space cp ' ' ;' 'with bit 7 set means multiple ' ' outputs jp nz,outchra ;if bit 7 was set AND NOT ' ',use outchra's ret inc de ;point to number of times to send space ld a,(de) ;A=number of times to send space push de ld e,' ' call chrfeed pop de jr donext notset: call outchra ;if char was'nt last, print it & return here donext: 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. * ;* * ;**************************************************************** ;*NOTE:0A0h=ASCII space char(020h)with bit 7 set & means send multiple spaces.* 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', 0A0h,8, 'Cylinder', 0A0h,7, 'head Load' db cr,lf, 0A0h,5 db 'Side', 0A0H,12, 'Head', 0A0H,10, 'Unload' db cr,lf, 0A0H,4 db 'Motor', 0A0H,10, 'Record', 0A0H,7, 'Step rate' db cr,lf, 0A0H,4 db 'drive', 0A0H,10, 'Number', 0A0H,6,'ST0', 0A0H,4,'ST3' db cr,lf db 'max track', 0A0H,9,'1st sec', 0A0H,8, 'frmT gpl' db cr,lf, 0A0H,18 db 'sec/trk', 0A0H,8, 'read Gpl' db cr,lf, 0A0H,3 db 'Buffer', 0A0H,9, 'density' db cr,lf db 'Command', 10000000b or '>' denfm: db ' F', 10000000b or 'M' denmfm: db 'MF', 10000000b or 'M' offmsg: db 'OF', 10000000b or 'F' onmsg: db ' O', 10000000b or 'N' bscolon:db bs, 10000000b or ';';used by filbuf to put a ';' after Command: bsspbs: db bs,' ', 10000000b or bs;used to blank the character before the cursor readerr:db 'CAN''T READ DAT', 10000000b or 'A';sector data read err messg ;************************************************************************ ;* * ;* 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 ;** 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. ** head: db 00h ;initialize to head zero dw 3A23h ;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. ** record: db 05h dw 3A24h ;record 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 ;secsize column addr w/offset,row addr w/offset 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) ;** The following five lines must remain in this order. ** 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 denbit: db 01000000b ;0100$0000 =MFM 0000$0000 =FM sidbit: db 00000000b ;0000$0100 =drive side 1 0000$0000 = side 0 curflg: db diradfg ;bit 0 determines order row-column transmited motogl: db 0 ;motor toggle flag insert: db 0FFh ;FF=insert;00=overwrite for cmd buffer editing ;** BUFACTV and BUFCHR must remain in the following order. ** bufactv:db 00 ;0=buffer inactive, 0FFh=buffer active bufchr: dw 0000 ;displayable chars including '^' gotten so far secbuf: dw 2400h ;RAM buffer for sector reads and writes clock: db 02 ;NEC765 clock speed 1 for 8 MHz, 2 FOR 4 MHz ;** The following four lines must remain in this order. ** nondma db 01 ;NEC765 in DMA mode=0;nonDMA mode=1(1 FOR uDec) hlt: db 02 ;Head Load Time range 01-7Fh steps 4mS @ 4MHz hut: db 01h ;Head Unload Time range 16-240mS steps16mS@4Mhz srt: db 0Eh ;Step Rate Time range F-0 steps 2mS @ 4MHz ;** 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. * ;* * ;************************************************************************ bufptr equ $ ;pointer to char. in command buffer datbuf: equ bufptr+2 ;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 st3 equ st2+1 ;1 byte uninitialized storage status reg. 3 ;4 BYTES RESERVED HERE FOR CYL.HEAD REC.NMBR FDCST equ st3+5 ;NEC765/INTEL 8272 MAIN STATUS REGISTER FDCCMDS equ fdcst+1 ;10 byte BUFFER:command BYTES TO OUTPUT TO FDC cmdbuf equ fdccmds+10d ;uninitialized storage for batch command .dephase endmv2: ;end of assembled code endif ;end of "if alignment" conditional ;************************************************************************ ;* * ;* INITIALIZATION data FOR MD2 AND MD3 UARTS AND MD3 TIMER * ;* * ;************************************************************************ if MD3 inittab: 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 initlen equ ($-inittab)/2 endif ;endif of if MD3 IF KPRO ;* Initialize keyboard port. Output the following bytes to port 7. * inittab:db 018h ;RESET SIO db 004h ;SELECT REGISTER 4 db 044h ;16X BAUD RATE, 1 STOP BIT, NO PARITY db 003h ;SELECT REGISTER 3 db 0C1h ;8 BITS RX, RX ENABLED db 005h ;SELECT REGISTER 5 db 0E8h ;8 BIT TX, TX ENABLED NO RTS, NO DTR db 001h ;SELECT REGISTER 1 db 000h ;NO INTERRUPTS initend: initlen equ initend-inittab ;* Initialize serial printer port. Output the following bytes to port 4. * db 018h ;RESET SIO db 004h ;SELECT REGISTER 4 db 04Ch ;16X BAUD RATE, 2 STOP BIT, NO PARITY db 003h ;SELECT REGISTER 3 db 0C1h ;8 BITS RX, RX ENABLED db 005h ;SELECT REGISTER 5 db 068h ;8 BIT TX, TX ENABLED NO RTS, NO DTR db 001h ;SELECT REGISTER 1 db 000h ;NO INTERRUPTS endif ;IF KPRO ;************************************************************************ ;* * ;* 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