.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 09/15/89) * ;* * ;***************************************************************************** if alignment ;assemble this alignment code? ;************************************************************************ ;* Equates: * ;* Set REV1BD EQU TRUE if this program is being assembled for a * ;* revision 1 MicroDecision board. Set REV1BD equ false if this * ;* program is being assembled for a revision 2 MicroDecision * ;* board. * ;* Set diradfg for your particular terminal cursor addressing. * ;* * ;************************************************************************ rev1bd equ false ;true to use STEP on Rev. 1 MicroDecision board diradfg equ 0 ;1 for ADM20; 0 for ADM31 cursor addressing stack equ 2040h ;stack space, RAM use here cr equ 0Dh ;carriage return lf equ 0Ah ;line feed bs equ 08h ;backspace ;************************************************************************ ;* * ;* 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 jr displa ;repaint the screen then fall thru to comand ;**************************************************************** ;* * ;* Scrolls whatever is on the screen off and displays * ;* current settings and other information on the screen. * ;* * ;**************************************************************** align: ;In MON>>>>>>>ld hl,stack ;starting address of stack<<<<<<<< ;In MON>>>>>>>ld sp,hl ;sp <-- stack ex de,hl ;DE=stack=destination of bytes to move into RAM ld hl,mv2ram ;source of bytes to move from ROM to RAM ld bc,endmv2-mv2ram;number of bytes to move ldir ;move bytes into RAM call getdat ;copy drive 0 data into current drive data area call resbuf ;set command buffer inactive (returns reg. A=0) inc hl ;point to first byte in command buffer ld (hl),a ;0 indicates byte is last byte in buffer displa: ld a,25d ;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 crtdata:ld hl,datdca ;point to max track memory storage for byt2crt ld d,ndatdca ;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 curdata ;move cursor to drive number position dw 2C25h ;column address w/offset then row addr w/offset ld a,(motdrv) ;get the binary drive number or 30h ;convert binary drive number to ASCII call outchra ;CRT <-- drive number 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 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: ld sp,stack ;starting address of stack ld bc,comand ;pushing comand addr onto stack allows using... push bc ; ...ret instruction to get back to 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:' LD HL,STRLOCK ;POINT TO BYTE ALLOWING ABORTION OF STRING CMDS INC (HL) ;SET BIT 0 TO 1(BLOCKS ABORTING STRING COMMANDS call inchr ;get character from keyboard or buffer INC (HL) ;SET BIT 0 TO 0(ALLOWS ABORTING STRING COMMANDS ;**************************************************************** ;* * ;* 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:ld hl,vectabl ;table of addresses of routines ld e,a ;reg E=character from keyboard ld d,nchars ; D=count of valid non-alphabetic characters ld bc,chartbl ; BC=address of non-alphabetic character list compare:ld a,(bc) ;get a character from the non-alphabetic list cp e ;compare list character with character from kbd jr z,gohl ;if characters match inc hl ;point to next routine address inc hl inc bc ;point to next non-alphabetic character in list dec d ;decrement count of characters left in list jr nz,compare ;if not all characters tried from list yet res 5,e ;convert character to upper case ld a,'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,gohl ;if char was current letter of the alphabet inc hl ;point to next entry in table inc hl inc a ;try the next letter of the alphabet dec d ;decrement count of letters left to try ret z ;if all letters tried & no match(RET to comand) jr findltr ;see if the next letter is a match gohl: 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) chartbl:db 03h ;^C db 06h ;^F db 12h ;^R db 14h ;^T db 17h ;^W db ';' ;; db '/' ;/ nchars equ $-chartbl ;************************************************************************ ;* * ;* RPTBUF ROUTINE * ;* * ;* Retype the buffer command line. Make editing of it possible. * ;* Execute the command line after a Carriage Return is typed. * ;* Be sure to jump from this routine to SAVINS: with register A=0. * ;* * ;************************************************************************ rptbuf: call ldcolon ;CRT <-- backspace and colon (to make Command;) 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 jr z,savins ;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 ;************************************************************************ ;* * ;* FILBUF ROUTINE * ;* * ;* Fill the buffer for batch execution of commands. * ;* Generally Registers are used this way: * ;* BC:count of columns to character at the cursor. * ;* D:Count of control characters from cursor to end of buffer. * ;* HL:pointer to next unused character location in buffer. * ;* * ;************************************************************************ filbuf: call ldcolon ;CRT <--backspace and colon(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 xor a ;zero A: 00=insert, 0FFh=overwrite ld (hl),a ;store a null as end of buffer marker 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) ;** Enter here from RPTBUF routine (will enter with register A=0). ** 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 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) call crtbs ;CRT <-- backspace character 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 return: 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 call c,crtbs ;if char was a control char, backspace twice call crtbs ;backspace the cursor on the CRT jpldbuf: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,jpldbuf ;if there are no characters to forward space to call outctl inc bc ;inc. pointer to buffer location at the cursor jr jpldbuf ;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 nz,ldbuf ;if not a carriage return ld hl,bufactv ;point to buffer active flag (0=inactve) inc (hl) ;set buffer active flag non 0: indicates active jr setbuf ;** 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 jR jpldbuf ;**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 jr 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: ld hl,bufactv ;HL points to buffer active flag (BUFACTV) ld b,(hl) ;get status of buffer active flag on entry call resbuf ;set BUFACTV=0 and BUFCHR counter to 0 ld (hl),b ;restore entry status of buffer active flag inc hl ;hl points to start of command buffer ld (bufptr),hl ;store pointer in memory ret ;(jp comand) ;************************************************************************ ;* * ;* SUBROUTINE RESBUF * ;* (Must return Register A=0.) * ;************************************************************************ resbuf: xor a ;zero register A ld hl,bufchr ;point to low byte of BUFCHR(# of chars + '^'s) ld (hl),a ;zero low byte of buffer displayed char counter inc hl ;point to high byte of BUFCHR ld (hl),a ;zero high byte of BUFCHR (BUFCHR is a word) inc hl ;HL points to buffer active flag (BUFACTV) ld (hl),a ;0 indicates buffer is inactive ret ;**************************************************************** ;* * ;* 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 djnz innyb ;dec count of nybbles, loop if haven't gotten 4 ret ;**************************************************************** ;* * ;* BUFADDR ROUTINE * ;* * ;* Input four hex digits from the keyboard which specify * ;* a new sector buffer starting address. * ;* * ;**************************************************************** bufaddr:call curdata ;move cursor to 'buffer' dw 2A28h ;column address w/offset then row addr w/offset 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 ret ;(jp comand) bufstr: call curdata ;move cursor to 'buffer' dw 2A28h ;column address w/offset THEN row addr w/offset 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 jr jphxcrt ;CRT<--byte converted to ASCII(use HEXCRT'S RET ;**************************************************************** ;* * ;* SETFGPL ROUTINE * ;* * ;* Input two hex digits from the keyboard which specify * ;* a new format gap length. * ;* * ;**************************************************************** setfgpl:call curdata ;move cursor to FORMAT GAP dw 4B26h ;column address w/offset then row addr w/offset 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 ret ;(jp comand) fgpstr: call curdata ;move cursor to FORMAT GAP dw 4B26h ;column address w/offset then row addr w/offset ld a,(fgpl) ;get the byte to send to the CRT jphxcrt:jp hexcrt ;convert the byte to ASCII and send to CRT ;**************************************************************** ;* * ;* SETRGPL ROUTINE * ;* * ;* Input two hex digits from the keyboard which specify * ;* a new read gap length. * ;* * ;**************************************************************** setrgpl:call curdata ;move cursor to READ GAP dw 4B27h ;column address w/offset THEN row addr w/offset 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 ret ;(jp comand) rgpstr: call curdata ;move cursor to READ GAP dw 4B27h ;column address w/offset THEN row addr w/offset ld a,(rgpl) ;get the byte to send to the CRT jr jphxcrt ;convert byte to ASCII, send to CRT(ret to cmd) ;**************************************************************** ;* * ;* EXIT ROUTINE * ;* * ;* 1. Move cursor to screen left edge below 'Command>'. * ;* 2. RETURN TO MONITOR if there or warm boot if not. * ;* * ;**************************************************************** exit: call curdata ;position the cursor for exit dw 2029h ;column address w/offset then row addr w/offset 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 (fall thru to MOTSTR:) motstr: call curdata ;cursor address subroutine dw 2A24h ;column address w/offset then row addr w/offset 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 ;if MOTOGL bit 0 was 0, show motor "OFF" ld de,onmsg ;start of "ON" message string chgmot: jp outstr ;print the string (use outstr's 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: ld a,07h ;NEC 765 recalibrate command code call cmdnew ;is data port ready to receive new command? ld a,(motdrv) ;bits 0 (=US0) and 1 (=US1) select the drive call fcmdout ;give floppy disk controller the command byte 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 call byt2crt ;display new cylinder no. as ASCII pair on CRT ld hl,curtrk ;regs. hl point to current track structure jp byt2crt ;CRT<-- track number as ASCII pair (ret to cmd) ;************************************************************************ ;* * ;* SPECIFY ROUTINE * ;* Allows specification of head load time, step rate, and head * ;* unload time for a drive. * ;* * ;************************************************************************ specify:call curdata ;move cursor to left of "head unload" position dw 4223h ;column address w/offset then row addr w/offset call inchr ;get a character from the keyboard and 11011111b ;convert the character to upper case cp 'L' jr z,hload ;set head load time cp 'U' ;head unload time jr z,uload ;set head unload time 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 ;will be jumping if it was 0FFh srt10: cp 10h ;10h is out of range jr nz,srate ;if not 10h ld (hl),0 ;if it was 10h, make it 0 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 ;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 hltFF: cp 080h jr nz,hload ;if not 080h ld (hl),1 ;if it was 080h, make it 1 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 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. ** pop hl ;take ret addr of call to getupdn off of stack drvspec:ld a,03h ;NEC 765 specify command code call cmdnew ;is data port ready to receive new command? 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 call fcmdout ;give floppy disk controller the 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 jp fcmdout ;give FDC the command byte (RET to COMAND) ;**************************************************************** ;* * ;* SELDRV ROUTINE * ;* * ;**************************************************************** seldrv: call curdata ;move cursor to drive number position dw 2C25h ;column address w/offset then row addr w/offset ;* Save data on the current drive.* call drvtbl ;RETs HL points to current drive table ex de,hl ; HL points to data w/DCA addrs savdat: ldi ;copy a byte of data into current drive's table inc hl ;skip past the column address byte inc hl ;skip past the row address byte jp pe,savdat ;if bytes of data remain to be moved ld c,ndrvdat ;number of data items not followed by DCA pairs ldir ;copy remaining data into current drive's table drvchr: call inchr ;get actual drive number from terminal cp '4' ;must be less then '4' jr nc,drvchr ;if '4' or greater cp '0' ;must be '0' or greater jr c,drvchr ;if less then '0' selpnt: and 00001111b ;convert ASCII digit to binary ld (motdrv),a ;store drive number ;** Get data on the new drive. ** call getdat ;copy current drives data into drive data table call drvspec ;specify new head load, unload, step rate times jp crtdata ;rewrite all data on CRT, fall thru to comand ;**SUBROUTINE GETDAT: copy current drives data into drive data table.** getdat: call drvtbl ;get pointer to current drive's table in hl getdatl:ldi ;copy a byte of data into current drive's table inc de ;skip past the column address byte inc de ;skip past the row address byte jp pe,getdatl ;if bytes of data remain to be moved ld c,ndrvdat ;number of data items not followed by DCA pairs ldir ;copy remaining data into current drive's table ret ;**************************************************************** ;* * ;* SIDSEL ROUTINE * ;* * ;* Toggle the chosen side, display choice on CRT, * ;* store choice for use by MOTRUN subroutine. * ;* * ;**************************************************************** sidsel: ld hl,sidbit ;point to side byte in memory ld a,00000100b ;current side is coded into bit 2 xor (hl) ;toggle bit 2 ld (hl),a ;save new side bit 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 byt2crt ;CRT<--new head(HL points to it)fall to SIDSTR sidstr: call curdata ;cursor address subroutine dw 2C23h ;column address w/offset then row addr w/offset ld a,(sidbit) bit 2,a ;current side is coded into bit 2 ld a,'0' ;be ready to print a '0' jr z,jpouta ;if selected side is 0 inc a ;change '0' to '1' and be ready to print a '1' jpouta: jp outchra ;CRT<--ASCII char (RET COMAND) ;**************************************************************** ;* * ;* DENSITY ROUTINE * ;* * ;* Toggle the density selection, display choice on CRT. * ;* * ;**************************************************************** density:ld hl,denbit ;point to density byte in memory ld a,01000000b ;current density is coded into bit 6 xor (hl) ;toggle bit 6 of denbit ld (hl),a ;save new DENBIT to memory, fall thru to denstr denstr: call curdata ;cursor address subroutine dw 3A28h ;column address w/offset then row addr w/offset 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: jp outstr ;CRT <-- string DE points to (use OUTSTR's ret) ;**************************************************************** ;* * ;* ID ROUTINE * ;* * ;* Get I.D. information for a sector. * ;* * ;**************************************************************** id: ld ix,id ;return here to retry if there is a disk error ld a,(denbit) ;density bit is 6: 0=FM, 1=MFM or 00001010b ;merge the READ ID command with the density bit call outsidr ;output byte 2(head and drive select)to FDCDATA ld bc,0000 ;bytes left to read (in case we go to diskerr:) ld hl,(secbuf) ;disk buffer address(in case we go to diskerr:) call rdrdy ;returns here if execution done before timeout jr rwidone ;CRT<-- 7 result bytes (ret comand) ;**************************************************************** ;* * ;* WDATA ROUTINE * ;* * ;* Write undeleted data to selected drive, track, head, * ;* and sector. * ;* * ;**************************************************************** wdata: ld ix,wdata ;return here to retry if there is a disk error ld e,0 ;E=skip data flag (SK): must be 0 for write cmd ld a,00000101b ;FDC 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 jr nz,wrstat ;if still executing ld a,'8' jr jpdsker wrstat: in a,(fdcstat) ;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 jr nz,wrio ;if not timed out ld a,'9' jr jpdsker wrio: in a,(fdcstat) ;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 writing bytes into 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 IF rev1bd ; If Revision 1 board in a,(fdc1tc) ;input terminal count if one byte is left(REV 1 ELSE ; IF Revision 2 board in a,(fdc2tc) ;input terminal count if one byte is left(REV 2 endif ; Endif outbyt1:ld a,(hl) ;get a byte from the buffer inc hl ;increment memory storage pointer out (fdcdata),a ;write a byte to the sector jr z,rwidone ;if all bytes have been written to the sector ei halt ;wait for NEC765 to request a byte from buffer in a,(fdcstat) ;get status byte from the NEC765 and 020h ;see if still executing jr nz,outbyte ;if still executing ld a,'A' jr jpdsker ;if not still executing ;**************************************************************** ;* * ;* RDATA ROUTINE * ;* * ;* Read undeleted data from selected drive, track, head, * ;* and sector. * ;* * ;**************************************************************** rdata: ld ix,rdata ;return here to retry if there is a disk error ld a,(SK) ;SK is skip deleted data address mark (bit 5) ld e,a ;E contains skip data flag (SK) ld a,00000110b ;FDC 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 jr z,exstop ;if (probably) header not found in a,(fdcstat) ;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,no1st ;if timed out rdio: in a,(fdcstat) ;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 IF rev1bd ; IF REVISION 1 board in a,(fdc1tc) ;input terminal count if one byte is left(REV 1 ELSE ; IF REVISION 2 board in a,(fdc2tc) ;input terminal count if one byte is left(rev 2 endif ; ENDIF in1byte:in a,(fdcdata) ;get a byte from the sector ld (hl),a ;put the byte from the sector into memory inc hl ;increment memory storage pointer rwidone:jr z,chkst1 ;if all bytes have been read from the sector ei halt ;wait for a byte from the sector in a,(fdcstat) ;get status byte from the NEC765 and 020h ;see if still executing jr nz,inbyte ;if still executing ld a,'7' jpdsker:jr diskerr ;CRT <-- error code+Abort-Retry-Ignore message ;************************************************************************ ;* SUBROUTINE CHKST1 * ;* When a command has completed enter here to get the 7 result * ;* bytes and display them on the CRT. Then check to see if status * ;* byte 1 is zero. If it isn't then there was some kind of error. * ;* If there was an error go to the Abort-Retry-Ignore routine. * ;* * ;************************************************************************ chkst1: ;;;;;;; exx ;save HL register with possible new buffer addr call indata ;CRT <-- 7 result bytes ;;;;;;; exx ;retrieve HL register with new buffer addr ld a,(st1) ;get status register 1 or a jr nz,crcerr ;if a bit was set go to abort-retry-ignore ld (secbuf),hl ;save new sector buffer pointer jp bufstr ;CRT <-- end of buffer address (RET TO COMAND) ;**************************************************************** ;* * ;* DISK I/O ERROR HANDLING * ;* * ;**************************************************************** exstop: ld a,'3' ;was executing command and stopped IF REV1BD ; IF REVISION 1 BOARD LD (errcode),a ;store byte in RAM for (maybe)later examination ADD hl,bc ;disk buffer pointer=dsk buf pointer+bytes left ;;;;;;; PUSH hl ;save the maybe new disk buffer pointer address jr GETRES ELSE ; IF REVISION 2 BOARD jr diskerr ENDIF ; ENDIF no1st: ld a,'4' ;was executing and didn't get first data byte diskerr:ld (errcode),a ;store byte in RAM for (maybe)later examination add hl,bc ;disk buffer pointer=dsk buf pointer+bytes left ;;;;;;; push hl ;save the maybe new disk buffer pointer address IF REV1BD ; IF REVISION 1 BOARD waitexc:in a,(0F5h) ;tell FDC drive is not ready (rev.1 boards) ELSE ; IF REVISION 2 BOARD call motrun ;returns A=motor on bit & drive select bits or 08h ;bit 3 set tells FDC drive is not ready out (0F7h),a ;turn on selected microdecision drive (RV2 BDS) waitexc: ENDIF ; ENDIF ;;;;;;; LD A,(ERRCODE) ;FOR TROUBLESHOOTING THIS PROGRAM ;;;;;;; CALL OUTCHRA ;FOR TROUBLESHOOTING THIS PROGRAM in a,(fdcstat) ;get FDC status bit 5,a ;see if FDC is still executing jr nz,waitexc ;loop if FDC is still executing GETRES: call motrun ;when done executing, make ready line true call indata ;CRT <--read 7 result bytes ST0,ST1,ST2,C,H,R,N ;;;;;;; pop hl ;retrieve the ?new? disk buffer pointer address crcerr: push hl ;save the ?new? disk buffer pointer address ld hl,errvctr ;address of a pointer to error handling routine jp gohl ;go execute code at the pointer address erroutn:pop hl ;retrieve the ?new? disk buffer pointer address ld sp,stack-2 ;leave just "comand" pushed onto stack call locmsg ;position cursor at start of error report space ld de,nrdymsg ;point to "Error ARI" message call outstr ;CRT <-- "Error ARI" getari: in a,(status) ;read status register and rdbit jr z,getari ;no character ready, loop in a,(rdreg) ;read character and 01011111b ;strip parity & convert to upper case cp 'A' ;A is for abort jr nz,retry ;if not an "A" call wipemsg ;blank out abort-retry-ignore message jp bufres ;end command buffer execution, erase cmnd line retry: cp 'R' ;R is for retry jr nz,ignore ;wasn't "R", go see if "I" was typed call wipemsg call motrun ;restart the drive motor jp (ix) ;retry a sector read or write command ignore: cp 'I' ;I is for ignore jr nz,getari ;not "A", "R", or "I" typed so get another char ld (secbuf),hl ;save new sector buffer pointer if "I"gnore call wipemsg jp bufstr ;CRT<--end of sector buf address(RET to COMAND) ;************************************************************************ ;* * ;* SUBROUTINE RWCMD * ;* Issues the NEC765 read data OR write data commands. Enter with * ;* register E containing SK (skip data) bit and A 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: call locmsg ;position cursor at beginning of message area ld hl,mt ;point to multi-track flag (bit 7) or (hl) ;merge MT& FDC data command code(read or write) ld hl,denbit or (hl) or e ;merge in SK bit call outsidr ;give FDC it's 1st(in reg A)& 2nd command bytes ld hl,fdccmds ;point to buffer for series of bytes to output push hl ;save pointer to fdccmds bytes ld a,(cylndr) ;C: cylinder number ld (hl),a ;put the command byte into the buffer inc hl ;point to next location in FDC command buffer ld a,(head) ;H: get head byte ld (hl),a ;put the command byte into the buffer inc hl ;point to next location in FDC command buffer ld a,(record) ;R: sector number to begin reading at ld (hl),a ;put the command byte into the buffer inc hl ;point to next location in FDC command buffer ld a,(secsize) ;N: sector size code (03=1024 selected) ld (hl),a ;put the command byte into the buffer inc hl ;point to next location in FDC command buffer ld (hl),0FFh ;buffer<--end of track(EOT)last sector to read inc hl ;point to next location in FDC command buffer ld a,(rgpl) ;read data gap length ld (hl),a ;put the command byte into the buffer pop hl ;retrieve pointer to FDCCMDS bytes ld b,6 ;count of 6 bytes to output to FDC data port ld c,fdcdata ;NEC 765 data port 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/write data command. ** ld hl,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 ;** output the last byte of the read or write data command. ** calcdtl: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 ;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) ;get status byte from NEC765/INTEL8272 bit 5,a ;see if execution has begun(non-DMA gone high?) jr z,waitex1 ;if execution has not begun ret ;if execution has begun ;** WIPEMSG is a subroutine to wipe over the "Error ARI" message. ** wipemsg:ld b,09d ;prepare for multstr: send string 9 times jr crtbspb ;CRT<-- backspace,space,backspace(ret to caller ;**************************************************************** ;* * ;* STEPI ROUTINE * ;* * ;* Increment the current track number by one (if not * ;* already highest track). Force the current cylinder * ;* number to be the same as the current track number. * ;* Convert them both to ASCII and print them 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 ret nc ;don't step in if now =or>highest allowed track inc a ;increment track number by 1 jr step ;**************************************************************** ;* * ;* STEPO ROUTINE * ;* * ;* Decrement the current track number by one (if not * ;* already highest track). Force the current cylinder * ;* number to be the same as the current track number. * ;* Convert them both to ASCII and print them on the CRT. * ;* * ;**************************************************************** stepo: ld a,(curtrk) ;present drive's present track number cp 0 ;lowest allowed track number ret z ;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) ret nz ;(jr nz,comand) 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 ret ;(jp comand) ;**************************************************************** ;* * ;* SLEEP ROUTINE * ;* Delay for selected time. * ;* Enter four digits. (Gives delay in 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 ;CRT <-- 'Z' that caused entry ld hl,0000 ;initialize ld de,1000D call getzdig ld de,100D call getzdig ld de,10D call getzdig ld de,1D call getzdig ;returns HL=the number entered dly1ms: call motrun ;keep drive motor running during the delay ld d,0EAh ;count for generating 1 millisecond delay ld a,l ;HL=count of milliseconds to delay or h ;see if hl has decremented to 0 yet jr z,dlydone ;if there are no milliseconds left to delay dlaylp: dec d ;dec count for generating 1 millisecond delay jr nz,dlaylp ;if haven't delayed 1 millisecond yet dec hl ;decrement count of milliseconds remaining jr dly1ms dlydone:ld a,(bufactv) ;get command buffer active flag (non-0=active) or a ;see if command buffer is active ret nz ;if command buffer is active ld b,5 ;number of times to send string crtbspb:ld de,bsspbs ;address of string backspace, space, backspace jp multstr ;erase "Zxxxx", return to comand ;**************************************************************** ;* * ;* SUBROUTINE GETZDIG * ;* * ;* Enter with DE=1, 10d, 100d, or 1000d. Get a digit * ;* from the keyboard or the buffer and multiply it by * ;* contents of DE. Return result in HL. * ;* Example: * ;* Enter with DE=1000 decimal. * ;* Type a '9' at the keyboard. * ;* Return with HL=9000 decimal. * ;**************************************************************** getzdig:call inchr ;get a character from the keyboard or buffer call valdig ;see if character is 0 through 9 jr c,getzdig ;valdig returns with carry set if not 0 thru 9 call outchra ;send character to crt sub '0' ;make the ASCII digit into a binary number addup: ret z ;this is the exit point from subroutine getzdig add hl,de dec a jr addup ;**************************************************************** ;* * ;* 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 ' ' jr z,jrouta ;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) jr jrouta ;print the 100s, 10s, or 1s digit that is in E ;** 0FFh indicates uncertainty, as a drive that hasn't been recalibrated.** is0FFh: call crtsp ;CRT <-- space character ld a,'?' ;send ' ??' to the CRT call outchra jrouta: 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: call curdata ;move cursor to track number position dw 2B22h ;column address w/offset then row addr w/offset 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 ;************************************************************************ ;* The following five labels are entered directly from the comand * ;* parser. * ;************************************************************************ getmax: ld hl,maxtrk ;point to max track memory storage structure db 0DDh ;ld hl instruction is 21h. DD 21 is LD IX,nnnn getcyl: ld hl,cylndr ;point to cylinder memory storage structure db 0DDh ;ld hl instruction is 21h. DD 21 is LD IX,nnnn gethead:ld hl,head ;point to head memory storage structure db 0DDh ;ld hl instruction is 21h. DD 21 is LD IX,nnnn getsecz:ld hl,secsize ;point to number memory storage structure db 0DDh ;ld hl instruction is 21h. DD 21 is LD IX,nnnn getrecd:ld hl,record ;point to record memory storage structure db 0DDh ;ld hl instruction is 21h. DD 21 is LD IX,nnnn getsec1:ld hl,strtsec ;point to strtsec memory storage structure db 0DDh ;ld hl instruction is 21h. DD 21 is LD IX,nnnn getsecp:ld hl,secpt ;point to secpt memory storage structure ;************************************************************************ ;* * ;* 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 ret ;(RET to 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 jp outchra ;print the digit on the CRT(use outchra's ret) ;************************************************************************ ;* * ;* FORMAT ROUTINE * ;* Formats a microdecision drive track. * ;* * ;************************************************************************ format: call locmsg ;position cursor at beginning of message area ld a,(denbit) ;density is coded into bit 6: 0=FM, 1=MFM or 00001101b ;NEC 765 format a track command code call outsidr ;output byte 2(head and drive select)to FDCDATA ld a,(secsize) ;N: bytes per sector (03=1024 selected) call fcmdout ;give floppy disk controller the command byte ld a,(secpt) ;SC: sectors per track ld b,a ;save count in register B call fcmdout ;give floppy disk controller the command byte ld a,(fgpl) ;format gap length (gap three) call fcmdout ;give floppy disk controller the command byte ld a,(strtsec) ;beginning sector number ld c,a ;sector number being formatted stored in reg. C ld a,(filbyte) ;byte to fill each sector with (e.g. E5h) call fcmdout ;give floppy disk controller the command byte ;************************************************************************ ;* 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 ;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 ;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 ;send R information to NEC 765 data port ld a,(secsize) ;sector size code ei halt ;wait for an interrupt out (fdcdata),a ;send N information to NEC 765 data port inc c ;number of sector being written djnz chrn ;dec count of secs to write;if done, fall thru ;************************************************************************ ;* * ;* SUBROUTINE INDATA * ;* * ;************************************************************************ indata: PUSH HL ;SAVE ENTRY HL 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 ini jr nz,indat9 ;if more result bytes remain to be read ;************************************************************************ ;* * ;* SUBROUTINE;;;;;;;HEADOUT * ;* Prints ST0, ST1, ST2, and CHRN (obtained from READ ID * ;* command and read data command) on the CRT. * ;* * ;************************************************************************ ld bc,5B21h ;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 call crtsp ;CRT <-- space character pop hl pop bc ;get count djnz indat ;dec count, loop if all 7 bytes not printed yet POP HL ;RETRIEVE ENTRY HL ret ;done: all 7 bytes have been printed ;************************************************************************ ;* * ;* 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 jp outchra ;CRT<--nybble in ASCII (ret to comand) ;**************************************************************** ;* SUBROUTINE LOCMSG * ;* Position cursor at the beginning of message space. * ;**************************************************************** locmsg: ld bc,2027h ;B=column address w/offset,C=row addr w/offset jr curadd ;put cursor at start of error report space ;**************************************************************** ;* * ;* CURDATA SUBROUTINE * ;* * ;* DIRECT CURSOR ADDRESSING SUBROUTINE: * ;* The call to this routine is followed by two bytes * ;* having the row and column addresses with offsets. * ;* This routine returns to the point following the two * ;* address bytes. * ;* * ;**************************************************************** curdata:pop hl ;pop return address pushed by CALL CURDATA ld c,(hl) ;get column address, which follows the call inc hl ;point to row address ld b,(hl) ;get row addr, which is 2 bytes after the call inc hl ;point to return address push hl ;save return address, fall thru to CURADD ;**************************************************************** ;* * ;* 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 ld a,04 ;sense drive status command call outsidr ;output byte 2(head and drive select)to FDCDATA ;*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, non-zero if active or a ;see if buffer 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 AF ;SAVE COPY OF THE CURRENT BYTE FROM BUFFER INC HL ;HL points to current byte+1 in buffer LD (BUFPTR),HL ;save pointer to next 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 or a ;see if current byte is 0 jr z,clrcbuf ;end of buffer marked by 00 call motrun LD A,(STRLOCK) ;A <-- BYTE CAN PROHIBIT ABORTING CMD STRING RRCA ;GET BIT 0 INTO CARRY FLAG JR NC,GETSTAT ;IF BUFFER COMMAND MAY NOT BE ABORTED FROM KBD in a,(status) ;read status register and rdbit ;was a char was entered at keyboard(clrs carry) jr z,GETSTAT;getst3 ;nothing from kbd, so use character from buffer in a,(rdreg) ;read character to clear out I/O chip clrcbuf:POP AF ;POP AND DISCARD CHARACTER FROM BUFFER call bufres ;reset command buffer, clear cmd line from CRT ld a,' ' ;dummy character to return to caller jr 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 GETSTAT:call rdrdy ;is data port ready to send a result byte? in a,(fdcdata) ;read status byte 3 ;;;;;;; ld (st3),a ;save status register 3 information pop af ;retrieve the character pop hl pop de pop bc ret ;************************************************************************ ;* * ;* SUBROUTINE BUFRES * ;* BUFRES executes if any character is entered from the keyboard * ;* when inchr is checking for a character from the command buffer. * ;* BUFRES also executes if the null at the end of a command string * ;* is reached. * ;* BUFRES terminates execution of the command buffer by reseting * ;* the buffer counter and setting the buffer active flag to 0. * ;* BUFRES also writes space characters over the command line * ;* that was displayed on the CRT. * ;* * ;************************************************************************ bufres: call curdata ;move cursor to 'Command;' dw 2729h ;column address w/offset then row addr w/offset call resbuf ;reset buffer counter & buffer active flag to 0 ;(RETURNS HL POINTING TO FIRST CHAR IN BUFFER-1 ld e,'>' ;replace 'Command;' with 'Command>' jr wipeout ;SEND THE '>' AND THEN SEND SPACES ;;;;;;; call outchre wipelin:inc hl ;point to next character in buffer ld a,(hl) ;get a character from the buffer cp 0 ;see if end of buffer has been reached ret z ;if command line all blanked out now cp 20h ;control characters are 0-1Fh call c,outchre ;if char in buffer is a control character WIPEOUT:call outchre ;send a space (BUT FIRST SEND THE '>') ld e,' ' ;prepare to send a blank space jr wipelin ;************************************************************************ ;* * ;* SUBROUTINE MOTRUN * ;* >>>> FOR MORROW MICRODECISION REVISION 2 BOARDS <<<< * ;* * ;* Keep motor running if MOTOR ON (MOTOGL=0FFh) is selected. * ;* Motor doesn't run if MOTOGL=00. Also selects the drive and * ;* and adds in write precompensation. * ;* * ;* This routine runs once before each check for a character * ;* from the terminal or the buffer. This makes the motor run. * ;* * ;************************************************************************ IFF REV1BD ; IF REVISION 2 BOARD<<<<<<<<<<<<<< ;run the microdecision controlled motor motrun: ld a,(precomp) ;get track to begin write precompensation on ld b,a ;save precompensation in register B ld a,(curtrk) ;get the current track number sub b ;subtract precomp from current track ld d,0 ;be ready if write precomp not required jr c,whatdrv ;if write precompensation not required ld d,00000100b ;precomp required:set write precompensation bit whatdrv: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(REV2 wants only 1 or 2) rlc b ;select next drive higher drive bit dec a ;try the next drive 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 jr z,drivon ;if motor is supposed to be off 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 or d ;add in the precompensation bit out (0F7h),a ;turn on selected microdecision drive (RV2 BDS) ret ENDIF ; Endif for Revision 2 board ;************************************************************************ ;* * ;* SUBROUTINE MOTRUN * ;* >>>> FOR MORROW MICRODECISION REVISION 1 BOARDS <<<< * ;* * ;* Keep motor running if MOTOR ON (MOTOGL=0FFh) is selected. * ;* Motor doesn't run if MOTOGL=00. Also selects the drive if the * ;* drive is the fourth drive. (Drives 0-2 are selected by the * ;* NEC 765.) Also adds in write precompensation. * ;* * ;* This routine runs once before each check for a character * ;* from the terminal or the buffer. This makes the motor run. * ;* * ;************************************************************************ IF REV1BD ; IF REVISION 1 BOARD<<<<<<<<<<<<<< ;run the microdecision controlled motor motrun: ld a,(precomp) ;get track to begin write precompensation on ld b,a ;save precompensation in register B ld a,(curtrk) ;get the current track number sub b ;subtract precomp from current track ld d,0 ;be ready if write precomp not required jr c,whatdrv ;if write precompensation not required ld d,00001000b ;set REVISION 1 board write precompensation bit whatdrv:ld c,00000001b ;motor on bit: prepared if motdrv is 0(drive 0) ld b,0 ;no drive is selected by port F8 except drive D ld a,(motdrv) ;get the drive number (0-3) cp 3 ;drive 3 (D) is a special case jr nz,drvtst ;if drive 0, 1, OR 2 ld c,00000100 ;if drive is 3 (D) bit 2 will run it's motor ld b,00000010 ;if drive is 3 (D) bit 1 will select it jr ismoton mddrv1: rlc c ;ready next drive's motor to be on dec a ;try the next drive drvtst: or a ;see if correct drive has been reached jr nz,mddrv1 ;if haven't reached correct drive ismoton:ld a,(motogl) ;see if motor has been selected to be on or a ; 0=off 1=on jr z,drivon ;if motor is supposed to be off ld a,c ;A<--motor line;bit1,2, (3)=drive 0,1, (2 & 3) drivon: or d ;add in the precompensation bit or b ;add in drive select bit for drive 3 (D) out (0F8h),a ;for rev. 1 microdecision boards ret ENDIF ; Endif for Revision 1 board ;************************************************************************ ;* * ;* SUBROUTINE SEEK * ;* * ;* Steps the drive head to the track specified in byte CURTRK. * ;* The seed command is a three byte command. * ;* * ;************************************************************************ seek: ld a,0Fh ;NEC 765 seek command code call outsidr ;output byte 2(head and drive select)to FDCDATA ld a,(curtrk) ;byte 3 selects the cylinder number call fcmdout ;give floppy disk controller the command byte ei halt ;wait for interrupt, then fall thru to SENSINT ;************************************************************************ ;* * ;* 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:ld a,08 ;sense interrupt status command(clears FDD0-3) call cmdnew ;is data port ready to receive a new 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) ;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) ;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 ret ;************************************************************************ ;* * ;* SUBROUTINE DRVTBL * ;* * ;* No register data required for entry. * ;* Returns: * ;* HL= pointer to the present drive's data table. * ;* DE= pointer to data followed by DCA address pairs. * ;* BC= number of data items followed by DCA address pairs. * ;* * ;************************************************************************ drvtbl: ld hl,drvdata-drvdlen ;table of data for each of 4 drives ld a,(motdrv) ;present drives number (0-3) inc a ;make drive number be 1-4 ld de,drvdlen ;length of a drive's data table nextdr: add hl,de ;hl points to start of a drive data structure dec a ;decrement drive number jr nz,nextdr ld de,datdca ;DE=pointer to data w/DCA address pairs ld bc,ndatdca ;number of data items followed by DCA pairs ret ;************************************************************************ ;* * ;* SUBROUTINE OUTSIDR * ;* * ;* Merge head select bits with drive select bits. Output the * ;* result to floppy disk controller data port. * ;* * ;************************************************************************ outsidr:call cmdnew ;is data port ready to receive a new command? ld a,(sidbit) ;get head select bit (coded into bit 2) ld hl,motdrv ;point to drive select byte:bit 0=US0,BIT 1=US1 or (hl) ;merge head select&drive select bits, FALL THRU ;************************************************************************ ;* * ;* SUBROUTINE FCMDOUT * ;* * ;* CHECK IF FLOPPY DISK CONTROLLER IS READY FOR A COMMAND BYTE. * ;* OUTPUT THE BYTE IN REGISTER A TO THE FLOPPY DISK CONTROLLER * ;* DATA PORT. * ;************************************************************************ fcmdout:call cmdrdy ;is FDC ready to recieve a command byte? out (fdcdata),a ;issue the command byte 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: push af ;SAVE THE BYTE TO OUTPUT TO THE FDC DATA PORT ld hl,0FFFFh cmdnew1:dec hl ;decrement timeout count ld a,l ;see if timeout count has reached 0 yet or h ld a,'0' jr z,cmderr ;if timedout in a,(fdcstat) ;read NEC 765 status port and 11110000b ;mask out some bits cp 10000000b ;is data port ready for a new command? jr z,rdynew ;if ready for a new command byte and 11000000b cp 11000000b ;DOES FDC WANT Z80 TO READ A BYTE? JR NZ,CMDNEW1 ; IF READ NOT WANTED IN A,(FDCDATA) ; IF READ WANTED JR CMDNEW1 rdynew: pop af out (fdcdata),a ;issue the command ret cmderr: pop hl jp diskerr ;************************************************************************ ;* * ;* 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 push hl IF REV1BD ld a,08h out (0F7h),a ;load VFO latch ENDIF ld hl,0FFFFh ;initialize timeout count cmdrdy1:dec hl ;decrement timeout count ld a,l or h ;see if timeout count has reached zero yet ld a,'1' ;error code jr z,cmderr ; in a,(fdcstat) ;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 hl 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: push hl ld hl,0FFFFh ;initialize timeout count rdrdy1: dec hl ;decrement timeout count ld a,l ;see if timeout count has reached 0 or h ; ld a,'5' ;error code jr z,cmderr ;if timedout in a,(fdcstat) ;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 pop hl ret ;************************************************************************ ;* * ;* 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 djnz multstr ;decrement number of times to send string ret ;************************************************************************ ;* * ;* SUBROUTINE OUTSTR * ;* * ;* Enter with DE pointing to a string to send to the CRT. * ;* Characters in the string are each shifted left one bit. The * ;* string is terminated by setting bit 0 in the last character. * ;* * ;* However, if a space character (' ') with bit 0 set is found, * ;* a space character is sent to the CRT for the number of times * ;* in the next byte in the string. * ;* * ;* Enter at LDCOLON to sent a backspace then colon string to the * ;* CRT. ;************************************************************************ ldcolon:ld de,bscolon ;2 character string:backspace and colon outstr: ld a,(de) ;get a character of the string to be printed srl a ;shift char right one bit and set bit 7 to 0 jr nc,notset ;if character wasn't last or multiple space cp ' ' ;' 'with bit 0 set means multiple ' ' outputs jr 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. * ;* * ;**************************************************************** crtbs: ld a,bs ;enter here to send a backspace to the CRT outchra:push de ld e,a ;routine entered with character to print in A call outchre pop de ret crtsp: ld e,' ' ;enter here to send a space character to CRT outchre:push af ld a,(display) ;get display on-off byte rra ;shift bit 0 into the carry flag jr c,outefin ;if display is unwanted 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 outefin:pop af ret ;**************************************************************** ;* * ;* ASCII character strings. * ;* Each byte is shifted left one bit. The last byte in * ;* each string has bit 0 set. However, the number of * ;* to send multiple spaces is not shifted. * ;* * ;**************************************************************** scr equ cr shl 1 ;shifted carriage return slf equ lf shl 1 ;shifted line feed ms equ (20h shl 1)+1 ;space character shifted left,bit 0 set mtlist shift macro string irpc chr,string db '&chr' shl 1 endm endm lastchr macro last1 db (last1 shl 1)+1 endm screen: db scr shift MONSTEP^^^ Rev.^^^ 1.0 db ms, 42d shift ST0^^^ ^^^ 1^^^ ^^^ 2^^^ ^^^ C^^^ ^^^ H^^^ ^^^ R^^^ ^^^ N db scr,slf shift Copyright^^^ 1989^^^ Ronald^^^ E.^^^ Jacobs^^^ (Berkeley^^^,^^^ CA) db slf,scr,ms, 4 shift track db ms, 8 shift Cylinder db ms, 7 shift head^^^ Load db scr,slf,ms, 5 shift Side db ms, 12 shift Head db ms, 10 shift Unload db scr,slf,ms, 4 shift Motor db ms,10 shift Record db ms,7 shift Step^^^ rate db scr,slf, ms,4 shift drive db ms,10 shift Number db scr,slf shift max^^^ track db ms, 9, shift 1st^^^ sec db ms,8 shift frmT^^^ gpl db scr,slf,ms,18 shift sec/trk db ms,8 shift read^^^ Gpl db scr,slf,ms,3 shift Buffer db ms,9 shift density db scr,slf shift Command lastchr '>' denfm: shift ^^^ F lastchr 'M' denmfm: shift MF lastchr 'M' offmsg: shift OF lastchr 'F' onmsg: shift ^^^ O lastchr 'N' bscolon:db bs shl 1 ;used by filbuf to put a ';' after Command: lastchr ';' bsspbs: db bs shl 1 ;used to blank the character before the cursor shift ^^^ db ((bs shl 1)+1) nrdymsg:shift Error^^^ AR lastchr 'I' list ;resume listing when assembling ;************************************************************************ ;* * ;* 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 STRLOCK:DB 0 ;bit 0=0 prohibits kbd aborting command string display:db 0 ;initialize to display on errvctr:dw erroutn ;location of disk error handling routine secbuf: dw 2400h ;RAM buffer for sector reads and writes curflg: db diradfg ;bit 0 determines order row-column transmited motdrv: db 0 ;drive # (filled by routine seldrv) ;** The lines in VECTABL must remain in this order. ** vectabl:dw exit ;^C dw format ;^F dw rptbuf ;^R dw getmax ;^T get & display 2 digits on CRT, store binary dw wdata ;^W write a sector of data dw filbuf ;; dw setbuf ;/ dw currev ;A dw bufaddr ;B specify the sector buffer address dw getcyl ;C get & display 2 digits on CRT, store binary dw rdata ;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 return ;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 return ;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 return ;W IS UNASSIGNED dw return ;X IS UNASSIGNED dw recal ;Y dw sleep ;Z ;** The following 4 sets of 22 lines each must remain in this order. ** drvdata: curtrk0:db 0FFh ;drive 0 track numbr:0FFh=drive never accessed maxtrk0:db 39d ; 0 highest physical track drive goes to cylndr0:db 0FFh ; 0 Cylinder number (C) head0: db 0 ; 0 Head number (H) record0:db 1 ; 0 Record number (R) secsiz0:db 3 ; 0 sector size number (N) secpt0: db 5 ; 0 sectors per track strsec0:db 1 ; 0 beginning sector number denbit0:db 01000000b ; 0 density bit nondma0:db 01 ; 0 FDC DMA mode=0;nonDMA mode=1(=uDec) hlt0: db 02 ; 0 head load time hut0: db 01 ; 0 head unload time srt0: db 0Dh ; 0 step rate time fgpl0: db 05Fh ; 0 format gap length rgpl0: db 01Ch ; 0 read data command gap length side0: db 0 ; 0 side number motor0: db 0 ; 0 motor on/off flag clock0: db 2 ; 0 FDC clock speed 1= 8 MHz, 2= 4 MHz filbyt0:db 0E5h ; 0 fill byte for formatting a track sk0: db 0 ; 0 skip data bit for sector read/write mt0: db 0 ; 0 multi-track bit for sector read/write prcomp0:db 0FFh ; 0 track to begin write precompensation drvdlen equ $-drvdata ;length of a drive's data table curtrk1:db 0FFh ;drive 1 track numbr:0FFh=drive never accessed maxtrk1:db 39d ; 1 highest physical track drive goes to cylndr1:db 0FFh ; 1 Cylinder number (C) head1: db 0 ; 1 Head number (H) record1:db 1 ; 1 Record number (R) secsiz1:db 3 ; 1 sector size number (N) secpt1: db 5 ; 1 sectors per track strsec1:db 1 ; 1 beginning sector number denbit1:db 01000000b ; 1 density bit nondma1:db 01 ; 1 FDC DMA mode=0;nonDMA mode=1(=uDec) hlt1: db 02 ; 1 head load time hut1: db 01 ; 1 head unload time srt1: db 0Dh ; 1 step rate time fgpl1: db 05Fh ; 1 format gap length rgpl1: db 01Ch ; 1 read data command gap length side1: db 0 ; 1 side number motor1: db 0 ; 1 motor on/off flag clock1: db 2 ; 1 FDC clock speed 1= 8 MHz, 2= 4 MHz filbyt1:db 0E5h ; 1 fill byte for formatting a track sk1: db 0 ; 1 skip data bit for sector read/write mt1: db 0 ; 1 multi-track bit for sector read/write prcomp1:db 0FFh ; 1 track to begin write precompensation curtrk2:db 0FFh ;drive 2 track numbr:0FFh=drive never accessed maxtrk2:db 39d ; 2 highest physical track drive goes to cylndr2:db 0FFh ; 2 Cylinder number (C) head2: db 0 ; 2 Head number (H) record2:db 1 ; 2 Record number (R) secsiz2:db 3 ; 2 sector size number (N) secpt2: db 5 ; 2 sectors per track strsec2:db 1 ; 2 beginning sector number denbit2:db 01000000b ; 2 density bit nondma2:db 01 ; 2 FDC DMA mode=0;nonDMA mode=1(=uDec) hlt2: db 02 ; 2 head load time hut2: db 01 ; 2 head unload time srt2: db 0Dh ; 2 step rate time fgpl2: db 05Fh ; 2 format gap length rgpl2: db 01Ch ; 2 read data command gap length side2: db 0 ; 2 side number motor2: db 0 ; 2 motor on/off flag clock2: db 2 ; 2 FDC clock speed 1= 8 MHz, 2= 4 MHz filbyt2:db 0E5h ; 2 fill byte for formatting a track sk2: db 0 ; 2 skip data bit for sector read/write mt2: db 0 ; 2 multi-track bit for sector read/write prcomp2:db 0FFh ; 2 track to begin write precompensation curtrk3:db 0FFh ;drive 3 track numbr:0FFh=drive never accessed maxtrk3:db 39d ; 3 highest physical track drive goes to cylndr3:db 0FFh ; 3 Cylinder number (C) head3: db 0 ; 3 Head number (H) record3:db 1 ; 3 Record number (R) secsiz3:db 3 ; 3 sector size number (N) secpt3: db 5 ; 3 sectors per track strsec3:db 1 ; 3 beginning sector number denbit3:db 01000000b ; 3 density bit nondma3:db 01 ; 3 FDC DMA mode=0;nonDMA mode=1(=uDec) hlt3: db 02 ; 3 head load time hut3: db 01 ; 3 head unload time srt3: db 0Dh ; 3 step rate time fgpl3: db 05Fh ; 3 format gap length rgpl3: db 01Ch ; 3 read data command gap length side3: db 0 ; 3 side number motor3: db 0 ; 3 motor on/off flag clock3: db 2 ; 3 FDC clock speed 1= 8 MHz, 2= 4 MHz filbyt3:db 0E5h ; 3 fill byte for formatting a track sk3: db 0 ; 3 skip data bit for sector read/write mt3: db 0 ; 3 multi-track bit for sector read/write prcomp3:db 0FFh ; 3 track to begin write precompensation ;** The following 8 pairs of source code lines must remain in this order. ** ;** When moved into RAM each of the following 8 labels will be followed by ** ;** two bytes, which are the column and then the row address where the ** ;** associated data byte will be displayed on the screen. ** datdca: ;start of data/column/row triplets table. ;** The following X pairs of source code lines must remain in this order. ** curtrk: db 0 ;current track number in binary db 22H ;column address with offset hasrun equ $ runchr equ 2Ah ;row address with offset db runchr maxtrk: db 0 ;maximum track number in binary dw 2A26h ; column/row address with offset cylndr: db 0 ;current cylinder number (C)in binary dw 3A22h ; column/row address with offset head: db 0 ;initialize to head zero (H) dw 3A23h ; column/row address with offset record: db 0 ;sector number (R) dw 3A24h ; column/row address with offset secsize:db 0 ;sector size code (N) (3=1024 bytes per sector) dw 3A25h ; column/row address with offset secpt: db 0 ;sectors per track dw 3A27h ; column/row address with offset strtsec:db 0 ;starting sector number dw 3A26h ; column/row address with offset ndatdca equ ($-datdca)/3 ;number of data items in preceeding table ;************************************************************************ ;* * ;* The following equates define addresses of uninitialized RAM * ;* which this program will use when it runs. * ;* * ;************************************************************************ ;**The following 16 lines must remain in this order.** ;**The following 16 lines must follow the data/column/row triplets above. ** DRVDAT EQU $ ;DRVDAT USED AS LABEL:MARKS START OF THIS TABLE denbit equ drvdat ;0100$0000 =MFM 0000$0000 =FM nondma equ denbit+1 ;NEC765 in DMA mode=0;nonDMA mode=1(1 FOR uDec) hlt equ nondma+1 ;Head Load Time range 01-7Fh steps 4mS @ 4MHz hut equ hlt+1 ;Head Unload Time range 16-240mS steps16mS@4Mhz srt equ hut+1 ;Step Rate Time range F-0 steps 2mS @ 4MHz fgpl equ srt+1 ;format a track gap length (gap 3) rgpl equ fgpl+1 ;read data command gap length sidbit equ rgpl+1 ;0000$0100 =drive side 1 0000$0000 = side 0 motogl equ sidbit+1 ;motor toggle: 0=OFF, NON-ZERO=ON clock equ motogl+1 ;NEC765 clock speed 1 for 8 MHz, 2 FOR 4 MHz filbyte equ clock+1 ;fill byte used in formatting a track sk equ filbyte+1 ;skip data bit for sector read/write commands mt equ sk+1 ;multi-track bit for sector read/write commands precomp equ mt+1 ;track to begin write precompensation on NDRVDAT EQU PRECOMP+1-DRVDAT;NUMBER OF DATA ITEMS ABOVE insert equ PRECOMP+1 ;FF=insert;00=overwrite for cmd buffer editing datbuf equ insert+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 st3 equ st2+1 ;1 byte uninitialized storage status reg. 3 ;4 bytes reserved here for cyl.head rec.number fdcst equ st3+5 ;NEC765/INTEL 8272 main status register fdccmds equ fdcst+1 ;10 byte buffer:command bytes to output to FDC errcode equ fdccmds+10 ;1 byte for the disk error code bufptr equ errcode+1 ;pointer to char. in command buffer ;** BUFCHR, BUFACTV, and CMDBUF must remain in the following order. ** bufchr equ bufptr+2 ;displayed chars including'^'gotten so far(2byt bufactv equ bufchr+2 ;0=buffer inactive, non-zero=buffer active cmdbuf equ bufactv+1 ;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 bits, 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 if rev1bd oncrt 'Assembled for a Revision ONE MicroDecision board.' else oncrt 'Assembled for a Revision TWO MicroDecision board.' endif 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