Title djdma (rev 3.0 5_Sept_84) ; ; Copyright 1984 ; Morrow Designs, Inc. ; San Leandro, Ca. .z80 ;written in z80 assembly language aseg ;absolute code segment org 0 ;starting address is zero ;---------------------------------------------------------------------- ; Table of Contents ;------------------ ; ; misc vectors, routines and tables ; entpoc - reset entry ; patc21 - vco test ; entirq - maskable interrupt vector ; mult - multiply de * b and return result in hl ; iplpat - handshake routine for bootstrapping ; wflag - wait for selected bit in the status reg to assert ; entnmi - non-maskable interrupt entry ; spt8s - 8" default soft sectored default sectors/track table ; spt5s - 5" default soft sectored number of sectors/track ; spt5h - 5" default hard sectored number of sectors/track ; rpoint - entry vector tables for sector r/w routines ; branch - vector table ; loadhl - load the hl register pair ; dsptch - general purpose dispatch routine ; sdelay - delay while counting sector/index holes ; dly1ms - delay 1 millisecond ; dlynms - delay n milliseconds ; cstart - start the controller ; cstop - stop the controller ; ; major entry routines ; poc - power on clear (reset) entry ; rexec - normal command completion re:entry point ; exec - normal entry point after an non-maskable_interrupt ; trmcer - termination by channel error ; trmhlt - termination by halt (haltc) or illegal command ; trmirq - termination by set interrupt request command (intrqc) ; main - main entry point to controller page 60 ; ; controller commands ; setcrc - set crc error retry count ; setlft - set head unload time-out ; trackz - set track size ; setdp - set the disk's parameters ; setmp - set the media's parameters ; logical - change logical drives ; setdma - set dma address ; setccw - set channel command word address ; setcaw - branch in channel ; haltc - halt controller ; intrqc - set interrupt request ; serout - output to serial port ; srenbl - seial input enable ; dumpm - dump controller memory ; loadm - load controller memory ; gomem - execute controller routine ; gstat - sense drive status ; wrsect - write a sector ; rdsect - read a sector ; wtrack - write a track ; rtrack - read a track ; ; disk read/write support subroutines ; prep - prepare selected disk for reading or writing ; cdisk - change drive parameter tables & decalibrate drive ; simake - get new drive constants table & do a test read ; sector - read/compare a sector header ; home - home the disk head(s) ; seek - seek routine ; stp - step the disk head once ; fsect - find a sector (soft sectored disk) ; setxfr - setup the dma address for disk transfer ; reads - read a sector (5" or 8" soft sectored) ; writes - write a sector (5" or 8" soft sectored) ; fsect5 - find a sector (hard sectored disk) ; read5 - read a sector (5" hard sectored) ; write5 - write a sector (5" hard sectored) ; ; channel read/write routines ; rdch - read a block from the host's memory ; wrch - write a block to the host's memory ; rbyte - read a byte from the host's memory ; wbyte - write a byte to the host's memory ; castor - store the channel address ; rcmd - setup for a read from the host's memory ; wcmd - setup for a write from the host's memory ; incio - read/write to channel & increment address ; restor - restore the channel command address page ; ; serial input/output routines ; hspin - hang until serial input is active ; spin - input a character from the serial port ; spout - output a charactor to the serial port ; ; internal monitor ; montr - monitor main line ; getc - get a character ; go - go execute a subroutine ; write - write to controller memory ; print - print the contents of controller memory ; cmdtbl - command table for monitor ; error - error and prompt strings for monitor ; ; disk sector/index synchronization routines ; hsync - hard sectored disk sector/index synchronization ; count - count sector/index holes ; drvoff - deselect the current drive and turn off the motor ; desel - deselect the current drive (but not the motor) ; motoff - turn off the motor on line ; ; other tables ; ctable - command table ; rtbl - disk read vector table ; wtbl - disk write vector table ; sect8 - drive constants tables ; drive8 - initial values for 8" drive parameter tables ; drive5 - initial values for 5" drive parameter tables ; const - initial values for drive's scratchpad area page ;---------------------------------------------------------------------- ; Equates ;-------- ; ; constants maxhs5 equ 10 ;max sectors on a 5" hard sectored disk hldly equ 50 ;head load delay time (50ms) modly equ 1000 ;motor on delay time (1 sec) ;addresses stack equ 1030h ;base of normal stack area stksav equ 132Eh ;stack save during reads/writes - deselect time monram equ 13C7h ;base of monitor ram area (extends to 13ef) ; drive parameter table flag definitions BaseIY equ 1340h ;base of the (iy area) drive parameter tables YFlag equ 0Eh ;drive configuration byte offset @ver equ 0 ; 1 = sector header has been verified @hrd equ 1 ; 1 = media is hard sectored @5in equ 2 ; 1 = drive size is five inch @moc equ 3 ; 1 = motor has on/off control @dbl equ 4 ; 1 = media is double density @nrc equ 5 ; 1 = drive doesn't have testable ready line @nhc equ 6 ; 1 = drive can't unload heads @hdl equ 7 ; 1 = heads are currently loaded ; system status flag definitions BaseIX equ 13F0h ;base of system status (ix area) & control area XFlag equ 0Eh ;system status byte offset @ser equ 0 ; 1 = serial_input_status = active @mon equ 1 ; 1 = monitor_status = active @mot equ 2 ; 1 = motor_status = active @sct equ 3 ; 1 = sector count is calibrated @hol equ 4 ; 1 = sector/index hole has been processed @fhf equ 5 ; 1 = first hole found ; I/O Port Definitions Pwrt_0_Ri equ 4000h ;Reset Irq Pwrt_1_DskDo equ 4001h ;* Hang the Cpu Until a Byte is Ready Pwrt_2_HAddr equ 4002h ;High Byte of Address Latch Pwrt_3_BStat equ 4003h ;Bus Status Register Pwrt_4_Dro3 equ 4004h ;Disk Output Control Latch 5.25" Pwrt_5_Dro2 equ 4005h ;* Disk Output Control Latch 8" Pwrt_6_Dro1 equ 4006h ;Misc Control signals Pwrt_7_Dro0 equ 4007h ;Byte Assembly Control Latch Prd_1_DiskDi equ 4001h ;* Hang the Cpu Until a Byte is Ready Prd_2_DatIStb equ 4002h ;Read Data From the Bus Prd_3_DStat equ 4003h ;Disk Input Status Latch Prd_4_BusRq equ 4004h ;Request the Bus Prd_5_BusRelse equ 4005h ;Release the Bus page ;====================================================================== ; Reset Entry ;============ ; -execution begins here after a reset or power_on_clear ; entpoc: jp poc ;---------------------------------------------------------------------- ; VCO Test ;--------- ; patc21: ld ix,baseix ld iy,(baseix) ld a,40h ;set mode to read disk data ld (Pwrt_7_Dro0),a ld a,64h ;single density 5 1/4" ld (Pwrt_6_Dro1),a call hspin ;wait for input to go active ld a,44h ;single density 8" ld (Pwrt_6_Dro1),a call hspin ;wait for input to go active ld a,04h ;double density 8" ld (Pwrt_6_Dro1),a call hspin ;wait for input to go active ld a,44h ;single density 8" ld (Pwrt_6_Dro1),a call hspin ;wait for input to go active ld a,64h ;single density 5 1/4" ld (Pwrt_6_Dro1),a jp hspin ;wait for input to go active ;====================================================================== ; Maskable Interrupt Entry ;========================= ; 1) execution begins here after any maskable interrupts (caused ; by 'data rq' still being hi when 'bus stb' is issued) ; entirq: jp 1000h ;branch to interrupt jump vector ;---------------------------------------------------------------------- ; multiply de * b and return result in hl ;---------------------------------------- ; mult: ld hl,0 ;result:= zero or a ;if (parameter eq null) ret z ; return (no param = Z_Flag set) ld b,a ;b:= multiplier multlp: add hl,de ;repeat add increment (de) to result (hl) djnz multlp ;until (multiplier eq zero) ret ;(note that Z_Flag is still cleared) page ;---------------------------------------------------------------------- ; handshake routine for bootstrapping ;------------------------------------ ; this is the handshake routine that is moved into the host computer's ; memory starting at location 38h. ; iplpat: ;this label is used to reference the following handshake routine. .phase 038h hndshk: ld hl,hflg ;hl:= pointer to flag ld (hl),0 ;flag:= 0 hlp1: ld a,(hl) ;repeat or a ; (get flag) jp z,hlp1 ;until (flag ne zero) cp 40h ;if (flag ne normal_completion) jp nz,hlp1 ; goto loop jp 80h ;goto cold boot loader hflg: db 0ffh ;this is a flag to the loader .dephase ;---------------------------------------------------------------------- ; wait for the selected bit in the status register to go high ;------------------------------------------------------------ ; 1) enter this routine with the de pair equal to the max.time delay ; and the l reg set as a mask for the appropriate bit in the status ; register ; 2) each iteration takes 13.75us ; wflag: dec de ;repeat ld a,d ; delay:= delay - 1 or e ; if (delay eq 0) ret z ; exit error (Z_Flag set) ld a,(Prd_3_DStat) ; a:= status register and l ; if (chosen_bit eq active) jr z,wflag ; ret ; exit normal (Z_Flag clear) ds 66h - $, 0ffh ;====================================================================== ; non-maskable interrupt entry ;============================= ; -execution begins here after an out 0efh instruction ; entnmi: exx ex af,af' ;save machine status pop de ;get the program counter ld hl,-count add hl,de ;test of pc is in the count routine ld hl,exec ;command processing routine push hl jr nc,swbank ;carry => pc in count routine push de ;first finish the count routine swbank: exx ex af,af' ;restore machine status ret page ;---------------------------------------------------------------------- ; sector size table ;------------------ ; 1) these tables are used by the read track command to 1) figure out ; the default number of sectors per track given a sector size/data ; density and 2) to determine the lowest numbered sector. ; ; 8" soft sectored formats spt8s: db 26d, 1 ;26 sectors per track (single density) db 26d, 1 ;26 " " " (double density) db 15d, 1 ;15 " " " (micronix standard) db 08d, 1 ;8 " " " (cpm system standard) ; 5" soft sectored format spt5s: db 05d, 1 ;5 sectors per track (micro-decision) ; 5" hard sectored format spt5h: db 10d, 0 ;10 sectors per track (north star) ;---------------------------------------------------------------------- ; entry vector tables for sector read and write routines ;------------------------------------------------------- ; rpoint: dw rzro ;read (soft sectored) routine entry points dw rone dw rtwo wpoint: dw wloop ;write (soft sectored) routine entry points dw wone dw wtwo ds 0a0h - $, 0ffh ;put this table at 0a0h for formatdj.com ;---------------------------------------------------------------------- ; vector table ;------------- ; branch: jp home ;home the disk heads jp seek ;seek to a track jp cdiska ;get the current disk parameter table jp hpatch ;get in sync with a disk jp imload ;re:initialize hpatch: bit 7,(ix+0ah) jp z,hsync res @sct,(ix+xflag) jp hsync ;---------------------------------------------------------------------- ; load the hl register pair ;-------------------------- ; loadhl: ld a,(hl) inc hl ld h,(hl) ld l,a ret page ;---------------------------------------------------------------------- ; general purpose dispatch routine ;--------------------------------- ; dsptch: jp (hl) ;goto address given in HL pair ;---------------------------------------------------------------------- ; delay while counting sector/index holes ;---------------------------------------- ; 1) the de register pair contains the delay count. a count of 22 hex ; is equal to 1 millisecond. ; 2) each iteration of the loop takes about 45.45us. 12us are spent ; here with the remaining 33.45us spent in the count routine. ; 3) see the table in the documentation section (currently at the end ; of this file) for a list of pre-calculated values of sdelay ; in milliseconds ; sdelay: push hl sdlp1: call count ;repeat dec de ; count any sector holes ld a,e ; time_delay:= time_delay - 1 or d jr nz,sdlp1 ;until (time_delay eq 0) pop hl ;recover the pointer ret ;---------------------------------------------------------------------- ; delay one millisecond ;---------------------- ; 1) this routine delays one millisecond before returning. ; 2) none of the registers are altered. ; dly1ms: push bc ld b,200d ;time delay:= 1 ms dssllp: ld a,a ;repeat nop djnz dssllp ;until (time delay eq 0) pop bc ret ;---------------------------------------------------------------------- ; delay n milliseconds ;--------------------- ; 1) this routine calls the dly1ms routine 'de' times. ; 2) if the de pair is zero then there is no delay. ; 3) register usage: ; a -> general purpose ; de -> initially the number of milliseconds, returned = 0 ; dlynms: ld a,d ;while (delay ne 0) or e ret z ; (return) d10lp: call dly1ms ; call delay 1 millisecond dec de ; delay_count:= delay_count - 1 jr dlynms page ;---------------------------------------------------------------------- ; start the controller ;--------------------- ; cstart: ld a,(1311h) ld (Pwrt_7_Dro0),a ;set up the controller clocks etc ld a,(1310h) ld (Pwrt_6_Dro1),a ;start the controller ret ;---------------------------------------------------------------------- ; stop the controller ;-------------------- ; cstop: push hl ;save hl pair ld hl,(1310h) ;control bytes for 4006 and 4007 ld h,0 ;turn off the clocks and write gate set 1,l ;set controller idle byte ld (Pwrt_6_Dro1),hl ;send command to controller pop hl ;restore hl pair ret ;exit without zero flag set (no error) page ;====================================================================== ; power on clear (reset) entry ;============================= ; ; primary register initialization poc: im 1 ;force restart 7 on interrupt ld (Pwrt_0_Ri),a ;reset the interrupt flip-flop ld sp,stack ;initialize the stack pointer ld hl,0ffffh ld (Pwrt_4_Dro3),hl ;initialize the drive ports ld a,0c3h ;initialize the interrupt vector ld (1000h),a ; (jump instruction) ld hl,0 ld (1001h),hl ; (to the reset entry for now) ; move read/write table and drive constants tables into ram ld hl,rtbl ld de,1290h ld bc,drive8-rtbl ldir ;load constants from rom ; move drive parameter tables into ram ld de,baseiy ;pointer to start of drive tables ld b,2 ;number of types of drives xor a ;start drive numbering with zero ex af,af' ;save the drive number ftloop: ld a,80h ;select bit for drive zero ld c,4 ;number of drives of a givin type tiloop: push hl ;save the rom pointer push bc ;save the two counts ld bc,0eh ;length of drive table minus two ldi ;move the 1st byte of the table ldi ;move the 2nd byte of the table push af ;save the select bit xor (hl) ;merge with basic drive pattern ld (de),a ;store pattern in table pop af ;recover the select bit rrca ;shift right for next drive inc de ;advance the ram pointer inc hl ;advance the rom pointer ex af,af' ;recover the drive number ld (de),a ;store the drive number inc a ;advance the drive number ex af,af' ;save the drive number inc de ;advance the ram pointer ldir ;load the rest of the table pop bc ;recover the counts dec c ;decrement the drive count jr z,discrd ;zero => rom pointer to advance pop hl ;recover the old rom pointer jr tiloop ;load the next drive table discrd: ex (sp),hl ;new rom pointer to tos pop hl ;discard the old rom pointer djnz ftloop ;test for both drive types loaded page ; move global constants table into ram ld de,baseix ;pointer to start of global constants ld bc,10h ;total number of global constants ldir ;initialize global constants from rom ; initialize command addresses ld a,0 ld hl,50h ;pointer to host command byte addr. ld (13c0h),hl ;current_command_pointer:= 50h ld (13c2h),a ld (13c4h),hl ;initial_command_pointer:= 50h ld (13c6h),a ; misc initialization ld ix,baseix ;initialize index registers ld iy,(baseix) ld a,6 ld (1310h),a ;initialize port 4006's memory image ; test for bootstrapping ld a,(Prd_3_DStat) ;if (terminal/shunt is present) and 2 jp nz,imload ld a,(Prd_5_BusRelse) ; set channel to cycle steal mode set @mon,(ix+xflag) ; monitor_status:= active res @ser,(ix+xflag) ; serial_input_status:= inactive jp main ; don't do boot-strap - goto main ; begin the bootstrap process imload: ld ix,baseix ;initialize index registers ld iy,(baseix) ld sp,stack ; wait one second for the bus to settle down. ld de,1000 ;delay:= 1 second call dlynms ;do delay ; move the first 38 loctions of main memory into controller ram xor a ld b,a ld c,a ;main_mem_start:= location 0 ex af,af' ;extended_page:= 0 ld de,38h ;byte_count:= 38h ld hl,stack ;controller_memory_start:= 1030h push hl push de ;(save byte_cnt & cntrl_memory_start) call rdch ;move main-memory -> controller-memory page ; zero the first 38 locations of main memory ld de,3813h ;d:=38(start addr), e:=13(len handshake) call wcmd ;set channel to write xor a ld c,a ;main_mem_start:= 0 mzload: call incio ;repeat write a zero dec d ; counter:= counter + 1 jr nz,mzload ;until (counter eq 0) ; move the handshake routine (hndshk) into main memory ld hl,iplpat ;cntlr_mem_start:= start handshake call cwrite ;move cntlr-mem -> main-memory ld a,(Prd_5_BusRelse) ;stop cycle stealing call rcmd ;set channel to read dec bc ;move main_mem_start back to point at flag ; wait for the main cpu to write a zero to the flag location wpzero: out (c),a ld a,(Prd_2_DatIStb) or a jr nz,wpzero ; restore the first 38 bytes of main memory ld c,a ;main_mem_start:= location 0 pop de ;byte_count:= 38h pop hl ;controller_memory_start:= 1030h call wrch ;move controller-memory -> main memory page bstart: ld hl,1320h ld b,5 ld (hl),80h ;lo_byte_dma_address:= 80h dpload: inc hl ;repeat ld (hl),0 ; set remainder of dma_address:=0 djnz dpload ; set track,side & sector = 0 ld de,4004h ;de:= 8" drive control port ld bc,104h ;b:=1(side sel), c:=4(drive select) ; bootstrap testdr: ld a,0ffh ld (de),a ;deselect the current drive ld (iy+1),a ;decalibrate the current drive ld a,b ;select the port address xor e ld e,a ld a,4 ;select the drive xor c ld c,a push bc ;(current start-sector and drive) push de ;(current port address call logcal ;set the logical drive number call cdisk ;install the new drive's parameters call prep ;prepare for a disk access pop de pop bc jr z,testdr ;if errors then rtn to selecting drive ld a,(iy+0ch) ld (1324h),a ;set sector:= lowest sector call rdsect ;read 1st sector on track 0 side 0 jr nz,bstart bit @hrd,(iy+yflag) call nz,hsync ld a,40h ;a:= bootstrap completion code ld bc,hflg ;bc:= pointer to main memory out (c),a ;main_memory:= operation complete res @mon,(ix+xflag) ;monitor_status:= inactive set @ser,(ix+xflag) ;serial_input_status:= active jp main ;goto start of main loop page ; normal command completion re:entry point ;----------------------------------------- ; rexec: push af ;save the status call count ;check for sector hole pop af ;recover the status or a ;set/reset the zero flag call nz,wbyte ;zero => no status to be reported ; normal entry point after an non-maskable_interrupt ;--------------------------------------------------- ; exec: ld a,(1310h) ;port 4006 control byte and 0feh ;strip off the interrupt bit ld (1310h),a ;restore the control byte set 1,a ;controller_stop_bit:= set ld (Pwrt_6_Dro1),a page ;---------------------------------------------------------------------- ; start of command lookup ;------------------------ ; 1) note that data overrun errors will cause the command processing ; to restart at relook. ; relook: ld sp,stack ;initialize the command stack pointer ld hl,relook ld (1001h),hl ;Irq_vector:= retry lookup command ei ;enable interrupts call rbyte ;read the next command from main memory ld a,(Prd_2_DatIStb) ;get the data from the channel ld hl,ctable-4 ;command table pointer ld de,4 ;entry length lookup: add hl,de ;repeat advance the table pointer push af ; save the new command ld a,(hl) ; command entry cp 80h ; compare with end of table jp z,trmhlt ; illegal command error exit pop af ; recover new command cp (hl) ; compare with table entry jr nz,lookup ;until (command found) ld de,rexec ;address of normal termination as return push de ;put return address on stack inc hl ;get address of the command ld e,(hl) ;(low byte) inc hl ld d,(hl) ;(high byte) push de ;put command execution address on stack inc hl ;pointer:= number of parameters byte ld e,(hl) ;length of command argument list ld d,0 ld hl,1323h ;hl:= pointer to parameter save area push hl ;save pointer in parameter save area call cread ;read the arguments from main memory call castor ;save the current channel address ld hl,trmcer ;interrupt_vector:= data_error ld (1001h),hl ;update the interrupt vector pop hl ;recover pointer to parameter save area ld c,(hl) ;c:= parameter #1 inc hl ld b,(hl) ;b:= parameter #2 inc hl ld a,(hl) ex af,af' ;a':= parameter #3 inc hl ld e,(hl) ;e:= parameter #4 inc hl ld d,(hl) ;d:= parameter #5 inc hl ;l:= parameter #6 call loadhl ;h:= parameter #7 ret ;execute the command page ;---------------------------------------------------------------------- ; command string termination entries ;----------------------------------- ; termination by channel error ;----------------------------- ; trmcer: ld a,91h ;data type channel error code ld sp,stack ;adjust the stack pointer ld (Pwrt_0_Ri),a ;clear the interrupt call cstop ;insure that the controller is stopped ei ;enable interrupts again ; termination by halt (haltc) or illegal command ;----------------------------------------------- ; trmhlt: ex af,af' ;save the completion code ld bc,(13c4h) ;initial command address - low word ld a,(13c6h) ;initial command address - extended page ex af,af' ;save the address - high page ; termination by set interrupt request command (intrqc) ;------------------------------------------------------ ; trmirq: push af ;save the completion code exx ;save the new address - low word ld bc,(13c0h) ;current command address - low word ld a,(13c2h) ;current command address - extended page exx ;save current command address - low word call castor ;update current command address exx ;recover old address - low word ex af,af' ;save old address - high page pop af ;recover completion code call wcmd ;set up channel for writing call incio ;write the status code call rcmd ;set up the channel for reading call incio ;do a dummy read for interrupts page ;---------------------------------------------------------------------- ; main loop ;---------- ; main: ld sp,stack ;initialize the stack pointer ld ix,baseix ;pointer to global constants ld iy,(baseix) ;pointer to current drive table ld hl,0 ld (stksav),hl ;set the drive select time out constant mnlp1: bit @mot,(ix+xflag) ;loop if (motor_status eq active) jr z,mskp1 call count ld hl,(stksav) ; time-out:= time-out - 1 dec hl ld (stksav),hl ld a,h ; if (time-out eq 0) or l call z,drvoff ; deselect drive mskp1: bit @ser,(ix+xflag) ; if (serial port is active) jr z,mskp2 call spin ; if (character present) jr nz,mnlp1 xor a ; if (brk_chr eq true) or c jr nz,mskp10 inc (ix+0fh) ; inc break count jr nz,mskp10 ; if (count eq max) res @ser,(ix+xflag) ; disable input mskp10: xor a ; extended page ex af,af' ld a,c ; get the character ld bc,3eh ; address for character call wcmd call incio ; write char into memory ld a,40h ; completion code call incio jr mnlp1 mskp2: bit @mon,(ix+xflag) ; else if (the monitor is active) call nz,montr ; call internal monitor jr main page ;====================================================================== ; set crc error retry count ;========================== ; 1) this constant determines the number of retries that will be ; made when a crc error (either in the sector header or the data ; field) is encountered in a sector read/write. ; 2) the default is 10 decimal. ; setcrc: ld (ix+0ch),c ;set the retry counter initial value xor a ;return_status:= none ret ;====================================================================== ; set head unload time-out ;========================= ; 1) this constant determines the number of revolutions made by a ; disk before it is deselected (see the subroutine count). ; 2) the default is 16 decimal. ; setlft: ld (ix+0dh),c ;set head unload counter xor a ;return_status:= none ret ;====================================================================== ; set track size ;=============== ; 1) this routine sets the maximum track number (actually the ; highest track number plus one). ; 2) notice that cdisk is used to set the iy register to point ; to the start of the drive's parameter table. cdisk will return ; with the zero flag cleared if the drive number was out of range. ; trackz: ld a,c ;drive value ld (1325h),a ;current_drive_number:= updated push bc ;save the new track size call cdisk ;iy:= base of current drive parameter table pop bc ;restore the track size ret nz ;if (drive eq non-existant) error rtn ld (iy+0),b ;load new track value ld a,40h ;completion code ret page ;====================================================================== ; set drive parameters ;===================== ; 1) this routine sets characteristics that are unique to a certain ; kind of drive. the parameters are ; 1) drive number (c reg & 1323h) ; 2) step rate (1324h) ; 3) step settle time (1325h) ; 4) head load delay (1326h) ; 5) motor on delay (1327h) ; 2) notice that cdiska is used to set the iy reg to the start of the ; chosen drive's parameter table. ; setdp: ld a,c ;a:= drive_number call cdiska ;select new dpt and set iy to its start ret nz ;if (drive eq non-existant) return error ld de,22h ;scale factor:= 1_m.s. ld a,(1324h) ;a:= step_rate call mult jr z,setsk1 ;if (parameter ne null) ld (iy+4),l ld (iy+5),h ; set the step rate setsk1: ld a,(1325h) ;a:= step_settle_time call mult jr z,setsk2 ;if (parameter ne null) ld (iy+0ah),l ld (iy+0bh),h ; set the step settle time setsk2: ld a,(1326h) ;a:= head_load_delay_time or a jr z,setsk3 ;if (parameter ne null) ld (iy+6),a ld (iy+7),0 ; set the head load delay time setsk3: ld de,10 ;scale factor:= 10_m.s. ld a,(1327h) ;a:= motor_on_delay_time call mult jr z,setsk4 ;if (parameter ne null) ld (iy+8),l ld (iy+9),h ; set the motor_on_delay_time setsk4: ld a,40h ;return status equal normal completion ret page ;====================================================================== ; set media parameters ;===================== ; 1) this command sets the parameters that are specific to the media ; in the drive. the parameters are ; 1) drive number (c reg) ; 2) starting track of write precompensation (b' reg) ; 3) highest track (a' reg) ; 4) first sector's number (e reg) ; 5) total number of sectors per track (d reg) ; setmp: ld a,c ;a:= drive_number exx call cdiska ;select new dpt and set iy to its start exx ret nz ;if (drive eq non-existant) return error ld (iy+0fh),b ;save the starting track of precomp. ex af,af' or a jr z,smpsk1 ;if (track number parameter ne 0) ld (iy),a ; save new highest track number smpsk1: ld a,e ;if (SPT/starting sector given) or d jr z,smpsk2 ld a,e and 00000001b ; (insure sector number = 0 ! 1) or 10000000b ; set msb to indicate user spec ld (iy+0ch),a ; save first sector's number ld (iy+0dh),d ; save number of sectors per track smpsk2: ld a,40h ;normal completion code ret page ;====================================================================== ; change logical drives ;====================== ; 1) this routine command changes the logical ordering of the 8" ; and 5" drives. this is done by changing the drive number that ; is kept for each drive in its drive parameter table (dpt). the ; dpt's for 8" drives come first in memory (starting at location ; baseiy) and are immediatly followed by the 5" dpt's. the drive ; number is the fourth byte of any given dpt (which are 10h bytes ; long). ; 2) the drive number of the first 8" drive is returned 'ored' with ; the status ; logcal: ld hl,baseiy+3 ;hl:= pntr to drive num in first dpt ld a,(hl) ;logical drive number for 1st 8" drive ex af,af' ;save ld de,10h ;table length ld b,8 ;drive count ld a,4 ;mask and c ;mask the data logcaw: ld (hl),a ;load the count inc a ;advance the count and 7 ;modulo 8 add hl,de ;advance the pointer djnz logcaw ;decrease the count ex af,af' ;recover original logical drive number or 40h ;merge completion code ret ;exit with no status report ;====================================================================== ; set dma address ;================ ; setdma: ld (1320h),bc ;initialize low order 16 bits of dma ex af,af' ld (1322h),a ;initialize high order 8 bits of dma xor a ;zero => no status reported ret ;====================================================================== ; set channel command word address ;================================= ; setccw: ld (13c4h),bc ;low order 16 bits of channel address ex af,af' ld (13c6h),a ;high order 8 bits of channel address xor a ret page ;====================================================================== ; branch in channel ;================== ; setcaw: call castor ;store the new channel address xor a ret ;====================================================================== ; halt controller ;================ ; haltc: ld a,40h ;normal completion code jp trmhlt ;restore the ccwa and idle ;====================================================================== ; set interrupt request ;====================== ; intrqc: ld a,(1310h) ;port 4006 control byte set 0,a ;interrupt_request_bit:= set ld (1310h),a ;restore the control byte set 1,a ;controller_stop_bit:= set ld (Pwrt_6_Dro1),a ;generate an interrupt request ld a,(13c2h) ;extended page byte ex af,af' ld bc,(13c0h) ;channel address - low word call incr3 ;do a 24 bit increment ld a,40h ;completion code jp trmirq ;update channel address & write code ;====================================================================== ; output to serial port ;====================== ; serout: ld a,c ;ascii character to accumulator call spout ;transmit the character ld a,40h ;completion code ret ;exit ;====================================================================== ; serial input enable ;==================== ; 1) parameter 1 is in the c register and is equal to: ; 0 - if the serial port is to be disabled ; 1 - if the serial port is to be enabled ; srenbl: res @ser,(ix+xflag) ;serial_input_enabled:= false bit @ser,c ;if (serial_input is to be enabled) jr z,sreskp set @ser,(ix+xflag) ; serial_input_enabled:= true sreskp: xor a ret ;return with no status report page ;====================================================================== ; dump controller memory ;======================= ; dumpm: call wcmd ;set up channel for writing call cwrite ;transfer data from controller to host xor a ret ;====================================================================== ; load controller memory ;======================= ; loadm: call cread ;transfer data from host to controller xor a ;set zero flag for no status report ret ;====================================================================== ; execute controller routine ;=========================== ; gomem: push bc ;put branch address on the stack ret ;branch to desired routine ;====================================================================== ; sense drive status ;=================== ; gstat: ld a,c ;drive number ld (1325h),a ;set up for cdisk routine call cdisk ;change drives if necessary jr nz,stpush ;illegal drive condition ld a,(iy+1) ;get current track value cp 0ffh jr nz,stotrk ld a,1 stotrk: ld (1323h),a ;jam 'new track register' to track 1 ld (1324h),a ;(insure we select side zero - msb = 0) call prep ;ready the drive for data transfer jr z,stpush ;zero => error drive access ld a,40h ;normal completion code stpush: push af ;save completion code ld a,(iy+yflag) ;drive configuration byte from dpt call wbyte ;send byte to the channel ld a,(1319h) ;sector length code call wbyte ld a,(Prd_3_DStat) ;status byte call wbyte pop af ;recover completion code ret page ;====================================================================== ; read/write a sector ;==================== ; ; begin 'write sector' command wrsect: ld c,8 ;offset into transfer function table jr rdsect+2 ; begin 'read sector' command rdsect: ld c,0 call cdisk ;change the drive if required ret nz ;non-existant drive error exit ld b,0ah ;retry count for header reads prloop: push bc ;save the parameters call prep ;select drive, load head, test header pop bc ;recover the parameters jr nz,prepok ;non-zero => no errors cp 8dh ;test for crc error in header read ret nz ;zero => crc error in sector header djnz prloop ;test for 10 retrys ret ;error exit prepok: ld a,(1324h) ;load the new sector ld b,a ;save the side bit and 7fh ;strip off the side select bit ld (1318h),a ;init sector number in sector image ld a,80h ;side bit mask and b rlca ;move side bit into bit 0 ld (1317h),a ;initialize side byte in sector image set @ver,(iy+yflag) ;set the no verify flag call wcmd ;assume a read sector command ld b,80h ;b:= read/write control bit xor a or c ;c not equal to zero for write jr z,loadch ;zero => disk read - channel write ld a,(Prd_3_DStat) and 40h ; test if disk is write protected ld b,a ; initialize read/write bit ld a,90h ; write protect error code ret nz ; write protect error exit page call rcmd ;prepare for channel read (disk write) loadch: ld a,(1310h) ;port 4006 control byte and 7fh ;strip off old read/write flag or b ;merge new read/write flag ld (1310h),a ;update port 4006 control byte ld d,0 ld e,c ;offset into function table (0 or 8) ld hl,1290h ;function table pointer add hl,de ;add offset for read or write bit @hrd,(iy+yflag) ;if (drive eq hard-sectored) jr z,fcloop ld de,2 ; offset vector to hard-sectored add hl,de fcloop: call loadhl ;get the dispatch address ld b,(ix+0ch) ;b:= retry_count for data field crc errors ld c,4 ;a:= retry_count for missing data id marks trloop: push bc ;repeat push hl ; save the pointer and count call dsptch ; execute the routine pop hl pop bc jr nz,tester ; if (there were no errors) call cstop ; stop the controller call dly1ms ; delay 1 millisecond ld a,40h ; a:= normal completion code ret ; normal exit tester: cp 93h ; if (error code = no data field id mark) jr nz,trcnt dec c ; if (retry_count - 1 ne 0) jr nz,trloop ; retry trcnt: cp 8Eh ; if (other error) or (no data id mark) ret nz ; error exit (zero flag cleared) djnz trloop ret ;error exit hard crc error page ;====================================================================== ; read/write track ;================= ; ; begin 'write track' command wtrack: ld c,8 ;offset into function table jr rtrack+2 ; begin 'read track' command rtrack: ld c,0 call cdisk ;change drives if necessary ret nz ;drive select error exit res @ver,(iy+yflag) ;clear the no verify bit ld b,0ah ;retry count for sector headers peloop: push bc ;save the parameters call prep ;find the current sector pop bc ;recover the parameters jr nz,clrvfy ;no error in prep routine cp 8dh ;header crc error ret nz ;error exit djnz peloop ;try again ret ;error exit clrvfy: set @ver,(iy+yflag) ;set the no verify bit ld hl,(1320h) ;low order word of dma address ld (13e0h),hl ;save in upper memory ld a,(1322h) ;extended page of dma address ld (13e2h),a ;save also in upper memory ld a,(1324h) ;get the side bit ld b,80h ;side bit mask and b ;strip off low order bits rlca ;shift to bit 0 of accumulator ld (1317h),a ;update side byte in header image bit 3,c ;test the read/write flag jr z,rwflag ;zero => read track command ld a,(Prd_3_DStat) ; get the drive status and 40h ; strip off write protect status ld b,a ; zero out b for read/write flag ld a,90h ; write protect error code ret nz ; write protect error exit rwflag: ld a,(1310h) ;port 4006 control byte and 7fh ;strip off old r/w flag or b ;merge new flag ld (1310h),a ;restore the control byte ld d,0 ld e,c ;de:= read/write offset into table (0 or 8) ld hl,1290h ;function table pointer add hl,de ;add offset for read or write bit @hrd,(iy+yflag) ;if (drive eq hard-sectored) jr z,fcode inc hl ; offset vector to hard-sectored inc hl fcode: call loadhl ;function_address -> hl page ld b,(iy+0dh) ;b:= sectors_per_track ld a,(iy+0ch) ;d:= 1st sector's number and 00000001b ;(mask off the 'active user spec flag') ld d,a inc b ;sector_count:= sectors_per_track + 1 ld c,b ;highest_sector+1:= sector_count ld a,d ;if (starting_sector is 0) or a jr nz,ttloop ld a,(ix+0ah) ld (1318h),a ; sector_reg:= current_sect_number dec c ; highest_sect+1:= highest_sect+1 - 1 ttloop: djnz tcont ;reduce the sector count ld hl,(13e0h) ; original dma address ld (1320h),hl ld a,(13e2h) ld (1322h),a ; restore the old dma address call cstop ; stop the controller call dly1ms ; delay 1 ms before returning ld a,40h ; completion code ret ; track operation complete tcont: ld a,(1318h) ;get the old sector value inc a cp c ;if (current_sector+1 eq last_sector+1) jr nz,loadse ld a,d ; current_sector:= first_sector loadse: ld (1318h),a ;update the sector value sub d ;adjust sector offset exx ;save the register bank ld e,a ;offset to e ld d,a ;count to d or a ;set the zero flag ld hl,(13e0h) ;base dma address ld a,(13e2h) ld bc,(132ah) ;sector length jr z,dmasto ;zero => no offset needed dmloop: add hl,bc adc a,0 ; take care of page overflow dec d ; reduce the sector count jr nz,dmloop dmasto: ld (1320h),hl ;load current dma pointer ld (1322h),a page ld hl,(1326h) ;get address of sector table ld a,(1328h) add hl,de ;(e still = offset, d now = 0) adc a,0 ;form offset into sector table ex af,af' ld b,h ld c,l ;set up channel address call rcmd call incio ;read the table entry ld a,(Prd_2_DatIStb) exx ;restore the registers inc a ;test the table entry jr z,ttloop ;skip current sector cp 81h jr z,ttloop+2 ;abort command at this point bit 3,e ;test the read/write flag call z,wcmd ;zero => read track command push bc push de push hl ;save the registers exx ex (sp),hl ;put sector table pointer on the stack push hl ;save the function pointer ex af,af' ;extended page of table pointer push af call dsptch ;read or write the sector jr nz,svstat ;zero => no error ld a,40h svstat: ex af,af' pop af ;recover page byte of table pointer pop hl ;recover function pointer pop bc ;sector table pointer ex af,af' ;recover the status call wcmd call incio ;report the status pop de pop bc ;recover the other registers jp ttloop ;transfer the next sector page ;---------------------------------------------------------------------- ; prepare the currently selected disk for reading or writing ;----------------------------------------------------------- ; ; select the drive prep: ld (ix+0bh),0 ;reset the index count ld c,(iy+yflag) ;c:= drive_configuration_byte ld b,(iy+2) ;b:= drive_pattern set 1,b ;clear the side select bit ld a,(1324h) ;load the sector/side byte rla ;side bit to the carry jr nc,pskp1 ;carry => side select to be active res 1,b ;select side 1 pskp1: ld de,4004h ;drive_control_port:= 5" drive (4004h) ld a,0ffh bit @5in,c jr nz,pskp3 ;if (drive-size .eq. 8") inc de ; drive_control_port:= 8" drive (4005h) ld a,(1323h) ; get the new track sub 2bh ; carry = 0 if track >= 43 sbc a,a ; ripple the carry through the acc. or 11111110b ; mask to the low write current bit pskp3: and b ;merge mask with drive pattern ld (iy+2),a ;update drive pattern byte or 00001100b ;turn off the step command ld (de),a ;select the drive ; start the drive (do the head-load/motor-on delays) ld e,(iy+6) ld d,(iy+7) ;de:= head_load_delay ld a,e or d ; if (head_load_delay eq 0) jr nz,pskp4 ; (set delay to default) ld de,hldly ; head_load_delay:=50ms pskp4: bit @hdl,c ;if (heads are currently unloaded) jr nz,pskp5 ; zero => head not loaded => read header res @ver,c ; clear the no verify bit pskp5: bit @nhc,c ;test for the head always loaded jr z,pskp6 ;zero => head can physically unload set @hdl,c ; set the head loaded bit page pskp6: bit @moc,c ;if (drive has motor control) jr z,pskp7 bit @mot,(ix+xflag) ; if (motor_status eq active) jr nz,pskp7 res @hdl,c ; clear the head loaded flag ld e,(iy+8) ld d,(iy+9) ; de:= motor_on_delay ld a,e or d ; if (motor_on_delay eq 0) jr nz,pskp7 ; (set delay to default) ld de,modly ; motor_on_delay:=1_sec pskp7: bit @hdl,c ;if (head_status eq not currently loaded) jr nz,pskp8 call dlynms ; delay for head load or motor on res @ver,c ; force a read sector header operation pskp8: set @hdl,c ;head_stauts:= loaded set @mot,(ix+xflag) ;motor_statue:= acitve ld de,8000h ;delay:= .45 sec ld l,10000000b ;mask:= drive ready flag bit @nrc,c ;if (drive has a ready line to test) call z,wflag ; test the ready line jr nz,pskp9 ; if (drive not ready) nrexit: ld a,82h ; drive not ready error code call desel ; deselect the drive cp a ; set zero flag to indicate error ret ; error exit pskp9: ld (iy+yflag),c ;update the drive configuration byte ; move the heads (if necessary) ld a,(iy+1) ;get the current track inc a ;test for the head(s) calibrated jr nz,pskp10 ;zero => heads not calibrated call home ; if (drive won't home) jp nz,nrexit ; goto error exit pskp10: ld a,(1323h) ;if (desired track ne current track) call seek ; move the heads to the desired track jp nc,pskp12 ; if (desired track out of bounds) cp a ; set the error flag ret ; error return pskp12: bit @ver,(iy+yflag) ;if (track_number doesn't have to be verified) ret nz ; exit without error page ; determine the media's density ld hl,12a0h ;hl:= offset to 8" ss bit @5in,(iy+yflag) ;if (drive = 5") jr z,pskp13 res @hrd,(iy+yflag) ; hard_sectored _flag:= false ld b,2 ; hole_count:= 2 plop1: ld de,640h ; repeat delay:= 22ms ld l,00010000b ; mask:= index call wflag ; if (flag is not active) ld hl,12c0h ; (hl:= offset to 5" ss) jr z,pskp13 ; break ld de,1b4h ; delay:= 6ms ld l,00010000b ; mask:= index plop2: call wflag ; repeat look for a hole jr nz,plop2 ; until (hole no longer active) djnz plop1 ; until (2 holes have passed) ld hl,12e0h ; hl:= offset to 5" hs res @sct,(ix+xflag) ; sector_count_calibrated:= false set @hrd,(iy+yflag) ; hard_sectored_flag:= true pskp13: ld de,0 ;sector image offset:= single density bit @5in,(iy+yflag) ;if (drive size is 5") or (trc numr ne 0) jr nz,pskp14 ; (skip if 5") ld a,(iy+1) or a jr z,pskp15 ; (skip on track zero) pskp14: bit @dbl,(iy+yflag) ; if (density currently equal double) jr z,pskp15 ld e,10h ; adjust entry to double density pskp15: add hl,de ld b,6 ;retries:= 6 simagl: call cstop ;repeat reset the disk controller push hl push de ; save the parameters push bc call simake ; create sector image and do test read call cstop ; stop the controller pop bc pop de pop hl jr nz,pskp16 ; zero => the test read is no good jp c,nrexit ; carry => hard sectored drive not ready ld a,10h xor l ; change entry into sector image table ld l,a ; update the hl register pair djnz simagl ;until (retries - 1 eq 0) ld a,84h ; media unreadable error code bmexit: cp a ; set the error flag ret ; error exit page ; set the density flag pskp16: bit @hrd,(iy+yflag) ;if (media is hard sectored) or (track ne 0) jr nz,pskp17 ; (skip on hard sectored) ld a,(iy+1) or a jr z,pskp18 ; (skip on track 0) pskp17: set @dbl,(iy+yflag) ; density_flag:= double ld a,(131ah) or a jr nz,pskp18 ; if (media is single density) res @dbl,(iy+yflag) ; density_flag:= single ; if media is soft sectored then verify the track number pskp18: bit @hrd,(iy+yflag) ;if (media is soft sectored) jr nz,pskp20 ld b,8 ; retry_count:= 8 rhloop: push ix ; repeat ld ix,sctrds ; sector_vector:= read header call sector ; read header into sector image pop ix ; if (read was ok) jr z,pskp19 ; break djnz rhloop ; until (retry_count - 1 eq 0) cp a ; set the error flag ret ; error exit pskp19: ld a,(1316h) ; if (current track ne desired track) cp (iy+1) jr z,pskp20 res @ver,(iy+yflag) ; heads calibrated flag:= false ld (iy+1),0ffh ; decalibrate head(s) ld a,87h ; seek error code cp a ; set the error flag ret ; error exit ; check that the sector length code is in bounds pskp20: ld a,(1319h) ;if (sector number is out of range) cp 4 jr c,pskp21 ld a,85h ; error:= sector number out of range cp a ret ; error_return page ; figure the starting sector and the number of sectors per track pskp21: bit 7,(iy+0ch) ;if (start_sector & spt ne specified) jr nz,pskp23 ld hl,spt5h ; hl:= 5" hard sectored tables bit @hrd,(iy+yflag) ; if (drive soft sectored) jr nz,pskp22 ld hl,spt5s ; hl:= 5" soft sectored spt bit @5in,(iy+yflag) jr nz,pskp22 ; if (drive size is 8") ld hl,spt8s ; hl:= 8" spt table ld a,(1319h) ; a:= sector size code sla a ; offset:= offset * 2 ld e,a ; adjust offset ld d,0 add hl,de ; calculate entry pskp22: ld a,(hl) ld (iy+0dh),a ; set the number of sectors_per_track inc hl ld a,(hl) ld (iy+0ch),a ; set the starting sector's number ; figure the number of bytes per sector pskp23: ld a,(1319h) ;accm:= sector length code ld hl,40h size: add hl,hl ;double previous value sub 1 jr nc,size ld (132ah),hl ;update the dma length registers call cstop ;stop the controller ret page ;---------------------------------------------------------------------- ; change drive parameter tables & decalibrate drive ;-------------------------------------------------- ; 1) entry a cdisk makes the drive whose number is stored at 1325h ; the current drive. entry at cdiska makes current the drive whose ; number is passed in the accumulator current. this entails ; -setting current drive parameter table (dpt) pointer (iy reg) ; -setting the dpt save location (13f0h) ; -resetting the head loaded bit on the new drive ; -setting the sector decalibrated flag ; 2) if the drive was within range (0 - 7) then the z-flag is returned ; cleared else its returned set and the accm is equal to 81h. ; cdisk: ld a,(1325h) ;load new drive number cdiska: cp (iy+3) ;if (new_drive_number eq current_drive_number) ret z ; normal return (zero flag set) ; find current drive's parameter table call desel ;deselect the current drive but not the motor ld b,8 ;b:= total available drives ld de,10h ;de:= drive table length ld iy,1330h ;iy:= drive table pointer cdloop: add iy,de ;repeat advance table pointer cp (iy+3) ; if (drive number eq parameter table) jr z,dfound ; goto drive found djnz cdloop ;until (all parameter tables have been checked) ; drive not found ld iy,(baseix) ;restore the original drive table pointer ld a,81h ;a:= illegal drive error code ret ;error return (zero flag cleared) ; drive found dfound: ld (baseix),iy ;update the drive table pointer ret ;normal return (zero flag set) page ;---------------------------------------------------------------------- ; get a new drive constants table table and do a test read ;--------------------------------------------------------- ; 1) this routine moves a drive constants table into the current ; drive constants area (@ 1310h), sets the starting track of ; write pre-compensation and then does a test read. ; 2) this program is only called by prep (around siloop). ; 3) if the test read was successful then the carry is returned ; cleared. ; ;move the current disk parameter table to 1310h simake: ld de,1310h ;target of image ld bc,10h ;length of image ldir ;move the data ld a,(iy+0fh) ;if (starting track of precomp is specified) or a jr z,siskip ld (1312h),a ; set starting track of pre-compensation siskip: bit @hrd,(iy+yflag) ;test for hard sectored media jr z,holeok ;if (disk is hard sectored) call hsync ; find a hole scf ; if (hole not found) ret z ; return error (carry set) holeok: ld b,14 ;delay past edge of hole djnz holeok+2 ;cycle ld hl,(1310h) ;control bytes for 4006 & 4007 set 1,l ;controller idle bit ld (Pwrt_6_Dro1),hl ;set controller parameters res 1,l ;clear controller idle bit ld (Pwrt_6_Dro1),hl ;start the disk controller ld l,1 ;controller id found bit ld de,0d80h ;time out delay for controller bit @hrd,(iy+yflag) ;test for hard sectored media jr z,siskp1 ;if (disk is hard sectored) ld d,0 ; shorten the delay siskp1: call wflag ;look for id bit until time-out ret page ;---------------------------------------------------------------------- ; read/compare a sector header ;----------------------------- ; 1) the bc register pair is preserved. ; 2) the alternate register set is preserved. ; 3) the ix register must be pointing to either: ; sctcmp - to compare the disk sector id to the sector ; image id block. ; sctrds - to read the disk sector id block into the ; sector image id block. ; 4) an error return clears the zero flag (non-zero value) ; sector: push bc ;save bc register pair ld a,(1311h) ld c,a ;4007 control byte to c ld b,4 ;header length to b ld hl,1316h ;pointer to start of sector header area ld de,4001h ;pointer to controller data port ex af,af' push af ;save the alternate accm/flag set ld a,9 ;alternate_accm:= retry_count (actually 8) ex af,af' exx push bc ;save the alternalte reg. set push de push hl sctrty: ex af,af' ;repeat dec a ; retry_count:= retry_count -1 jr z,scter1 ; if (retry_count .le. 0) ex af,af' ; save retry_count ld bc,0010h ; time_delay:= max ld de,4001h ; pointer:= disk_data ld hl,4003h ; pointer:= disk_status call cstop ; stop controller & set clock speed call cstart ; start the controller ld a,(iy+yflag) and 00000110b ; if drive is 5" soft sectored cp 00000100b ; (bit2=1=5"drive, bit1=1=hard_sectored) jr z,sctlp2 ; skip to 5" read header id byte ; read the header id byte from an 8" disk sctlp1: djnz sctsk1 ; repeat if (primary-timer != 0) bit 0,(hl) ; if (attn/err == active) jr nz,sctsk2 ; break dec c ; if (secondary-timer == 0) jr z,sctrty ; goto retry sctsk1: bit 0,(hl) ; if (attn/err == active) jr z,sctlp1 ; break sctsk2: nop ; this is a time pad ld a,(de) ; read disk data exx jp (ix) ; goto either sctcmp or sctrds ; read the header id byte from a 5" disk sctlp2: djnz sctsk3 ; repeat if (primary-timer != 0) bit 0,(hl) ; if (attn/err == active) jr nz,sctsk4 ; break dec c ; if (secondary-timer == 0) jr z,sctrty ; goto retry sctsk3: bit 0,(hl) ; if (attn/err == active) jr z,sctlp2 ; break sctsk4: ld b,5 sctlp3: djnz sctlp3 ; this is a time pad ld a,(de) ; read disk data exx jp (ix) ; goto either sctcmp or sctrds ;compare the track number through sector number to sector image sctclp: ld a,(de) ;repeat sctcmp: cp (hl) ; if (disk_data.ne.image(pointer) jp nz,scter2 ; goto error exit inc hl ; pointer:= pointer + 1 djnz sctclp ;until (counter.eq.0) jr sctcnt ;read the track number through sector number to sector image sctrlp: ld a,(de) ;repeat sctrds: ld (hl),a ; image(pointer):= disk_data inc hl ; pointer:= pointer + 1 djnz sctrlp ;until (counter.eq.0) ;read the crc bytes through first two id gap bytes and check the crc sctcnt: ld a,(de) ;read first crc data byte nop nop ld a,(de) ;read second crc data byte ld a,c and 0efh ;crc error control byte ld (Pwrt_7_Dro0),a ;issue the command ld a,(de) ;sync with data - 1st ff/4e ld a,c ;(c is the disk_control byte) ld (Pwrt_7_Dro0),a ;stop crc error captures ld a,(Prd_3_DStat) ;get the status byte cpl bit 0,a ;zero => no crc error ld a,(de) ;read the 2nd ff/4e jr nz,scter2 ;if (error .eq. true) goto error exit exx ;restore alternate register set pop hl pop de pop bc exx ld a,(de) ;read the 3rd ff/4e ex af,af' ;restore the alternate accm pop af ex af,af' pop bc ;restore bc register pair ld a,(de) ;read the 4th ff/4e ret ; error return for missing header id byte scter1: ld l,15h ;(error code = 88h = 15h + 73h) ; error return form sector header compare scter2: exx pop hl ;restore alternate register set pop de pop bc exx ex af,af' ;restore the alt_accm. pop af ex af,af' ld a,l ;generte the error code add a,73h ;(88=no-id-byte; 89-8c=compare errors; 8d=crc) sctsk5: or a ;error_flag:= set pop bc ret ;return page ;---------------------------------------------------------------------- ; home the disk heads ;-------------------- ; 1) this routine homes the disk head ; 2) the current track number is reset to zero and the track verified ; flag is set to false. ; 3) if the head is over track zero then the head is stepped out 4 times ; and then homed (insures that heads sitting at track -2 will home). ; 4) if a 5 1/4" drive fails to find track zero then a second attempt ; is made at a slower step rate. ; 5) the z-flag is set if track 0 was found else its cleared (error-94h). ; 6) after the settle delay the t-0 flag is rechecked before returning. ; 7) register usage ; a -> general purpose ; b -> step count ; de -> settle constant (see settle) and slow step rate ; hl -> disk status port and current step rate ; home: res @ver,(iy+yflag) ;sector header verified:= false ld (iy+1),0 ;current track:= 0 ld hl,4003h ;hl:= pointer to disk status port bit 5,(hl) ;if (track zero flag is true) jr z,homlp1 ld a,0 ; direction:= step_in (away from home) ld b,4 ; step_count:= 4 homlp0: call stp ; repeat step in djnz homlp0 ; until (4 steps in have been taken) homlp1: ld a,8 ;direction:= step_out (towards home) ld b,0 ;step-count:= 256 (default for 8" drives) bit @5in,(iy+yflag) jr z,homlp2 ;if (drive is 5") ld b,64h ; step_count:= 100 homlp2: call stp ;repeat get the drive pattern bit 5,(hl) ; if (track zero flag is active) jp z,homsk1 call settle ; do settle delay bit 5,(hl) ; if (track zero flag isn't true) jr z,homlp1 ; goto home the heads cp a ; else set the zero flag ret ; normal return homsk1: djnz homlp2 ;until (step_count .eq. 0) bit @5in,(iy+yflag) ;if (drive_size .eq. 5) and (step rate ne slow) jr z,nothom ld de,554h ; de:= slow_step_rate (40ms) push hl ld l,(iy+4) ld h,(iy+5) ; hl:= current_step_rate or a ; (clear the carry flag) sbc hl,de pop hl ld (iy+4),e ld (iy+5),d ; step_rate:= slow (40ms) jr nz,homlp1 ; try to home a again at a slower rate nothom: dec (iy+1) ;decalibrate & set error flag ld a,94h ;error_code:= drive won't home ret ;error return (zero flag cleared) page ;---------------------------------------------------------------------- ; seek routine ;------------- ; 1) this routine seeks to the track passed in the accm. ; 2) if the track number is in bounds then the head is moved over it and ; the carry flag is cleared else, the carry is set to indicate errors. ; 3) register usage: ; a - enter equal desired track, becomes current step pattern ; b - current_track and absolute number of steps ; seek: cp (iy+1) ;if (desired_track eq current_track) ret z ; exit res @ver,(iy+yflag) ;clear the no verify bit cp (iy+0) ; if (new track out of bounds) jr c,seksk1 ccf ; set the error flag ld a,83h ; improper track value error code ret ; error exit seksk1: ld b,(iy+1) ;b:= current track ld (iy+1),a ;current track:= updated sub b ;find the difference jr nc,seksk2 ;carry => must compliment cpl inc a seksk2: ld b,a ;c:= absolute difference sbc a,a ;ripple the carry throughout a (a:=0 ! a:=ff) and 8 ;isolate the direction bit or (iy+2) ;merge with direction bit seklp1: call stp ;repeat step the heads djnz seklp1 ;until (step_count eq 0) settle: ld e,(iy+0ah) ld d,(iy+0bh) ;de:= settling time call sdelay ;do settle delay ret page ;---------------------------------------------------------------------- ; step the disk head once ;------------------------ ; description: ; this routine steps the head once. the direction is passed ; in the a register (8=step_out, 0=step_in). ; notes: ; 1) none of the registers are altered in this routine (see sdelay and ; count for possible register alteration). ; stp: push de ;(save the de pair and the accm) push af and 8 ;remove all but the direction bit ld de,4005h ;bc:= 4005 (8"-drive control port) bit @5in,(iy+yflag) jr z,stpsk1 ;if (drive is 5") dec de ; bc:= 4004 (5"-drive control port) stpsk1: or (iy+2) ;combine direction bit with drive pattern ld (de),a ;start the step command xor 4 ;toggle the step bit ld (de),a ;finish the step command ld e,(iy+4) ld d,(iy+5) ;de:= step_delay call sdelay ;do step delay pop af pop de ;(restore the de pair and the accm) ret page ;====================================================================== ; find a sector header (soft sectored) ;===================================== ; 1) this routine is called by both the soft sectored read and write ; routines. ; 2) the hl pair is set to the start of wpoint or rpoint by the calling ; routine. these tables contain vectors used in the actual read/write ; routines. ; fsect: push hl ;save the read/write pointer call setxfr ;setup transfer parameters pop hl ;restore the pointer xor a ;clear the accumulator and flags or e ;set the flags for leftover bytes ld a,0a1h ;crc control byte code ex af,af' ;save for the end of the command ld e,d ;number of initial bytes to e ld d,0 add hl,de ;form the table entry (into rpoint/wpoint) add hl,de ld e,(hl) ;get dispatch address from table inc hl ld d,(hl) push de pop ix ;dispatch address to ix ld b,(iy+0dh) sla b ;retries:= (number of sectors per track) * 2 fsloop: push ix ;repeat ld ix,sctcmp ; sector_vector:= compare w/sector image call sector ; if (disk_image .eq. memory_image) pop ix ret z ; return cp 88h ; if (error .eq. no id byte found) jr z,fscnt ; retry cp 8bh ; if (error .eq. sector_number) jr z,fscnt ; retry cp 8dh ; if (error .eq. crc error) ret nz ; retry fscnt: djnz fsloop ;until (retry count .eq. zero) or a ;reset the error (zero) flag ret ;error exit page ;---------------------------------------------------------------------- ; setup the dma address for a disk transfer ;------------------------------------------ ; setxfr: call rcmd ;set up the channel to do reads ld bc,(1320h) ;get the dma address ld de,(132ah) ;get the dma byte count ld a,(1322h) ;get the extended page address ex af,af' ;save the accumulator ld hl,132ch ;data storage pointer ld a,2 ;page overflow value add a,c ;test for a two byte page ld a,0 jr nc,adjust ;carry => 1 or 2 bytes to page end sub c ;calculate the count push af ;save on the stack loop22: call incio ;do channel read and inc. dma addr. dec de ;decrement the dma count ld a,(Prd_2_DatIStb) ;get the channel data ld (hl),a ;put the data in the buffer inc hl ;advance the buffer pointer xor a ;clear the accumulator or c ;test for page boundary jr nz,loop22 ;zero => page boundary found pop af ;adjust the stack adjust: push af ;save the byte count for exit call incio ;do the read before the transfer dec bc ;adjust the dma address ld hl,1330h ;parameter buffer pointer xor a or e ;test for full page count jr nz,ptest ;zero => full page transfers sub c ;calculate left over bytes ld e,c ;save left over in e jr sdata ;go fill the parameter buffer ptest: xor a or c ;test for even page boundary jr z,sdata ;zero => full page transfers sub e ;difference between data & addr. jr c,sdata ;carry => no negation ld e,a ;leftover in e ld a,c ;starting amount in a neg ;2s compliment loopd: call store ;write the parameters xor a ;full page code sdata: dec d ;reduce the page count jp p,loopd ;more pages left ld a,e ;leftover bytes cp 3 ;test for less than three jr c,emark ;carry => leftover less than three call store ;leftover bytes parameters to buffer ld e,0 ;adjust e page emark: pop af ;recover the front-end count ld d,a ;save in d ld a,2 call store ;write an end mark in buffer ex af,af' ld a,3fh ;get ready for the zero flag ld b,a ;prevent b from overflowing ex af,af' cp e ;test for two bytes leftover call c,store ;store the parameters ex af,af' inc a ;the zero flag ex af,af' ld a,0a2h ;crc control code call store ;store the control codes inc a ;adjust the accumulator store: dec a ;adjust the byte count ex af,af' ld (hl),a ;extended page addr to buffer ex af,af' inc hl ;advance the pointer ld (hl),a ;byte count less one to buffer inc hl ;advance the pointer inc b ;advance the dma page byte ret nz ;zero => 65k overflow ex af,af' inc a ;advance the extended page addr. ex af,af' ret page ;---------------------------------------------------------------------- ; read a sector 5" or 8" soft sectored ;------------------------------------- ; 1) note the use of the flag register in this routine. before entering ; this body of code the z_flag is set to tell how many bytes are ; going to be transferred. in the first part of this routine the ; carry flag is added to the flag register to indicate the data ; density and consequently how many data address marks to read. ; 2) the alternate register set is preserved. ; 3) the alternate accm is modified. ; reads: ld hl,rpoint ;read table entry pointer call fsect ;locate current sector ret nz ; error exit - sector not found call wcmd ;prepare for channel write (disk read) call cstop ;reset the disk controller exx push bc ;save the alternate reg. set push de push hl ld b,00h ;b:= time delay ld de,4001h ;de:= read disk data location ld hl,4003h ;hl:= disk status exx ld a,(1311h) ;load the 4007 mode control byte set 2,a ;change to sync byte to data header ld (Pwrt_7_Dro0),a ;update port 4007 ld b,2dh ;delay:= 144us + 16us rdlp0: djnz rdlp0 ;wait (5 bytes in single density) ld (stksav),sp ;save the stack pointer ld sp,1330h ;transfer parameter pointer ld bc,(1320h) ;load the dma address pop hl ;byte count to h - extended page to l dec h ;adjust the count for the loop ld a,l ld (Pwrt_2_HAddr),a ;initialize the extended page register exx page ld a,(iy+yflag) and 00000110b ;if drive is 5" soft sectored cp 00000100b ;(bit2=1=5"drive, bit1=1=hard_sectored) ld a,(1310h) ld (Pwrt_6_Dro1),a ;(start the controller looking for a.m.) jr z,rdlp2 ;skip to the 5" read-the-address-mark routine ; find the address mark on an 8" disk rdlp1: djnz rdskp1 ;repeat exx ; if (time-out == .true.) ld a,93h ; accm:= error code or a ; error_flag:= set jp rdrtn ; exit rdskp1: bit 0,(hl) ; accm:= attn/err jr z,rdlp1 ;until (attn/err == active) nop ;(small time pad for single density) ld a,(de) ;read data exx ;restore the original reg set jp (ix) ;goto rtwo, rone or rzro ; find the address mark on a 5" disk rdlp2: djnz rdskp2 ;repeat exx ; if (time-out == .true.) ld a,93h ; accm:= error code or a ; error_flag:= set jp rdrtn ; exit rdskp2: bit 0,(hl) ; accm:= attn/err jr z,rdlp2 ;until (attn/err == active) ld b,5 rdlp3: djnz rdlp3 ;wait an extra 8us before reading the data ld a,(de) ;read data exx ;restore the original reg set jp (ix) ;goto rtwo, rone or rzro page rtwo: out (c),a ;write to the channel inc bc ;advance the dma address pointer ld a,(de) ;get the first/second data byte rone: out (c),a inc bc rloop: ld a,(de) ;read data from the disk rzro: out (c),a ;write data to the dma channel inc bc ;advance the dma address pointer dec h ;decrement the byte count jr nz,rloop ld a,(de) ;read next to last data byte pop hl ;get the new block count and page out (c),a ;write next to last byte to channel inc bc ;advance the counter dec h ;test for last block to transfer ld a,(de) ;read the last byte out (c),a ;last byte to the channel inc bc ;increment the dma address ld a,l ld (Pwrt_2_HAddr),a ;update the extended page register jp nz,rloop ;drop back to the block loop ex af,af' ;flag for extra bytes ld a,(de) ;read data/crc from disk jr z,lastr ;zero flag means no extra bytes out (c),a ;data to the channel inc bc pop af ;flag to indicate two extra bytes ld a,(de) ;disk data/crc byte jr z,lastr ;again - zero flag means no byte out (c),a ld a,(de) ;read first crc byte lastr: ld a,(de) ;read second crc byte ld a,(131bh) ;crc error mode byte ld (Pwrt_7_Dro0),a ld a,(de) ;get in sync with error flag ld a,(1311h) ;control byte for 4007 ld (Pwrt_7_Dro0),a ;turn off error mode ld a,(Prd_3_DStat) ;status byte cpl bit 0,a ;set zero flag if no crc error ld a,8eh ;data field crc error code rdrtn: ld ix,baseix ;restore the ix register ld sp,(stksav) ;restore the stack pointer exx pop hl ;restore the original reg. set pop de pop bc exx jp cstop ;turn off the controller page ;---------------------------------------------------------------------- ; write a sector (5" or 8" soft sectoring) ;----------------------------------------- ; ; 1) the de pair is set up in sector (called by fsect) to point ; to location 4001h. ; 2) the hl pair is also set up in sector. initially the hl reg. ; pair points to the start of the sector image. once the header ; has been successfully read the hl pair is left pointing to ; the density byte (131ah) which is 0 for single density and ; one for double density. ; writes: ld hl,wpoint ;write table entry pointer call fsect ;locate the current sector ret nz ;sector not found if zero flag not set ld a,(de) ;<5th ff/4e> ld (stksav),sp ;save the stack pointer ld sp,1330h ;transfer parameters pointer ld a,(hl) ;get the density byte rra ;save the density flag in the carry ld a,(de) ;<6th ff/4e> pop bc ;byte count (b) & extended page (c) dec b ;adjust for the prefetch of data ld hl,400bh ;page 40 & 4e field count for dbl den ld a,(de) ;<7th ff/4e> exx ;change register banks ld de,4001h ;dma data port ld hl,4002h ;extended page port ld bc,(1320h) ;get the dma address ld a,(de) ;<8th ff/4e> inc bc ;adjust for the prefetch of data exx ;change register banks again jr c,double ;if (media is single density) ld l,6 ; port 4006 - control port of 82s105 ld a,(de) ; <9th ff> ld a,(de) ; <10th ff> ld a,(131eh) ; control byte for 4006 or 2 ld (hl),a ; turn controller off inc l ld (hl),80h ; switch clocks and turn on write gate dec l and 0fdh ld (hl),a ; turn controller back on ld a,0aah ; half byte of zero ld l,0ch ; half-zero count loop1: ld (de),a ; write half a zero dec l jr nz,loop1 ; write the sync-byte ld l,2 ; hl now points to the extended page reg. ld a,81h ld (Pwrt_7_Dro0),a ; enable the crc generator ld a,0f5h ; first half of fb ld (de),a ld a,91h ld (Pwrt_7_Dro0),a ; change controller to normal data ld a,6fh ; second half of fb ld (de),a jp (ix) ; dispatch to write data routine ;else (media is double density) double: ld a,(de) ; <9th - 19th 4e> dec l jr nz,double ld a,(de) ; <20th 4e> ; set the write pre-compensation ld a,(1312h) ; get the starting track of write precomp cp (iy+1) ; compare with present track sbc a,a ; extend the carry to the accumulator and 10h ; mask off the write precompensation bit ld l,a ; save the result in l ld a,(de) ; <21th 4e> ld a,(131eh) ; get controller command byte and 0efh ; mask off old precompensation bit or l ; merge new precomp bit or 2 ; controller disable bit ld l,6 ; hl now points to port 4006 ld (hl),a ; set precomp. & turn off controller inc l ld (hl),80h ; change clocks and turn on write gate and 0fdh dec l ld (hl),a ; turn the controller back on ld a,0aah ; one half of a byte of zero ld l,16h loop2: ld (de),a ; write 22 half zeros dec l jr nz,loop2 ;write the sync-bytes ld (de),a ; 23rd half zero ld l,7 ; hl points to mode port again ld (de),a ; write 24th half zero ld (hl),81h ; enable the crc generator ex de,hl ld (hl),44h ; write first half of a1 ld (hl),89h ; write second half of a1 ld (hl),44h ld (hl),89h ld (hl),44h ld a,91h ; normal data mode byte ld (de),a ld (hl),89h ex de,hl ld l,2 ; hl now points to extended page reg. ld a,0fbh ; id byte for data record ld (de),a jp (ix) ; dispatch to write routine page wtwo: exx ld a,(132ch) ;get the first byte of data inc bc ;advance the dma address ld (de),a ;write to the disk ld a,(132dh) ;get the second byte of data jr wonep wone: exx ld a,(132ch) ;get the first byte of data wonep: inc bc ;advance the dma address ld (de),a ;write the second/first byte to the disk exx wloop: exx ld a,(hl) ;get data from the dma channel out (c),a ;start a new dma cycle inc bc ;increment the dma address ld (de),a ;write the data to the disk exx ;swap register banks for byte count djnz wloop pop bc ;get the new byte count & extended page exx ld a,(hl) ;get next to last data byte out (c),a ;start the last dma cycle ld (de),a ;write next to last data to disk inc bc ;advance dma address pointer across page exx ld (hl),c ;load extended page register w/new value dec b ;test for block transfers done exx ld a,(hl) ;get the last data byte out (c),a ;start first dma cycle of new block ld (de),a ;write the last data byte to the disk inc bc ;advance the dma address pointer jr nz,wloop+1 ;jump back to block transfer loop ex af,af' ;flag to indicate extra bytes to xfer jr z,done pop af ;flag to indicate two extra bytes ld a,(hl) ;channel data ld (de),a ;write to the disk out (c),a ;start next dma cycle jr z,done-1 ;exit if only one extra byte ld a,(hl) ;get last data byte from channel reg. out (c),a ;extra dma cycle for lost data intr ld (de),a ;write last byte to the disk pop af ;control byte for port 4007 done: ld (Pwrt_7_Dro0),a ;mode to write the crc data bytes ld (de),a ;first crc data byte ld (de),a ;second crc data byte ld a,91h ;normal data mode byte ld (Pwrt_7_Dro0),a xor a ld (de),a ;flush the registers with a zero ld ix,baseix ;restore the ix register ld sp,(stksav) ;restore the stack pointer ld a,8eh ;data field crc error code jp cstop ;turn off the controller page ;====================================================================== ; find a sector (hard sectored disk) ;=================================== ; 1) this routine does both the common initialization for the 5" hard ; sectored read and write routines as well as finding the desired ; sector on the disk. ; 2) if the disk is not turning or the desired sector is greater than ; the maximum number of sectors on a 5" hard sectored disk (maxhs5) ; then the carry is returned set. ; fsect5: call cstop ;stop the controller ; setup the alternate register set ld d,0 ;read routine check byte ld hl,4002h ;channel data/extended page pointer ld a,(1322h) ;extended page byte ld e,a ;initialize extended page register ld (hl),a ;initialize channel extended page ld bc,(1320h) ;initialize dma address pointer exx ;save in shadow register bank ; find the desired sector ld b,maxhs5 ;counter:= max number of sectors on a 5" disk fs5lp: push bc call hsync ;repeat pop bc ; if (disk not turining) jr z,fs5err ; break ld a,(1318h) ; if (desired sector eq current sector) cp (ix+0ah) jr z,fs5skp ; goto final initialization djnz fs5lp ;until (max sector count reached) fs5err: scf ;set the error flag ret ;exit error ; final initialization fs5skp: ld de,4001h ;channel data/extended page pointer ld c,0 ;write routine check register res @fhf,(ix+xflag) ret ;return with carry flag cleared page ;---------------------------------------------------------------------- ; read a 5" disk (hard sectored) ;------------------------------- ; read5: call fsect5 ;find the sector if drive is ready ret c ;error exit - diskette not turning ld b,2ch rstall: djnz rstall ;preamble delay call cstart ;start the controller ld b,0 ;single density byte count ld a,(131ah) ;get the density bit or a ;set the zero flag ex af,af' ;save the flag ; find the data field address mark ld a,(1310h) ld (Pwrt_6_Dro1),a ;start controller looking for an addr mark r5lp1: ld a,(Prd_3_DStat) ;repeat bit 4,a ; if (index hole has passed) jr nz,r5skp1 ld a,93h ; error_code:= missing addr mark or a ; set error flag ret ; error return r5skp1: bit 0,a ;until (address mark found) jr z,r5lp1 ex af,af' jr z,r5skp2 ;if (drive is double density) ld a,(de) ; read second data field address mark r5skp2: ex (sp),hl ;delay ex (sp),hl ;delay ex af,af' ;save the flags (specifically the density) r5loop: ld a,(de) ;repeat repeat read data from disk exx ; exchange register banks out (c),a ; transfer data to the dma channel xor d rlca ld d,a ; update the check byte inc bc ; advance the dma pointer ld a,b or c ; test for 65k page overflow jr nz,reaok inc e ; advance extended page pointer reaok: ld (hl),e ; update extended page register exx ; swap the register banks djnz r5loop ; until (block is transfered) xor a ; clear the zero flag ex af,af' ; swap the flags (to find out if its dd) jr nz,r5loop ;until (the whole sector has been read) ld a,(de) ;read the check byte exx ;get the check byte xor d ;calculate error check byte ld a,8eh ;data field crc error code jp cstop ;turn off the controller page ;---------------------------------------------------------------------- ; write a sector (5" hard sectored) ;---------------------------------- ; write5: call fsect5 ;find the sector if drive is ready ret c ;diskette not turning in drive ld b,11h ;single density preamble length ld a,(131ah) ;density byte rra ;density bit to the carry flag ld a,(1310h) set 1,a ;set controller to inactive ld (Pwrt_6_Dro1),a ld a,90h ;control byte for port 4007 ld (Pwrt_7_Dro0),a ;turn on write gate ld a,(1310h) jr nc,cstr ;carry => media is double density ; set write pre-comp and adjust preamble length if double density ld a,(1312h) ;get the starting track of write pre-comp ld l,a ld a,(iy+1) ;if (current track ge 22) cp l ld a,24h ; (a:= pre-comp + dd control byte) jr c,w5skp1 ; if (current track ge start wrt-pre-cmp) or 10h ; set write precompensation bit w5skp1: ld (Pwrt_6_Dro1),a ; setup the controller ld b,20h ; adjust the preamble length res 1,a ; write the preamble cstr: ld (Pwrt_6_Dro1),a ;start the controller xor a ;clear the accumulator zerow: ld (de),a ;write the preamble ex (sp),hl ;delay ex (sp),hl ;delay djnz zerow ld hl,4002h ;channel data/extended page pointer ld b,a ;single density byte count ld a,(131ah) ;get the density byte or a ;set the flags ld a,0fbh ;sync byte jr z,lasts ;non-zero => double density ld (de),a ; write the first sync byte ex (sp),hl ; delay ex (sp),hl ; delay lasts: ld (de),a ;write the last sync byte page ; write the data ex af,af' ;save the flags w5loop: exx ;exchange the register banks out (c),a ;start the data channel inc bc ;advance the dma address ld a,b or c ;test for 65k page overflow jr nz,weaok ;zero => advance extended page inc e weaok: ld (hl),e ;update the extended page register exx ;exchange register banks ld a,(hl) ;get the channel data ld (de),a ;write data to the disk xor c rlca ;update the check byte ld c,a djnz w5loop ;test first/second half of data done xor a ;set the zero flag ex af,af' ;exchange the accumulator jr nz,w5loop ;test for transfer done ld a,c ;get the data check byte ld (de),a ;write to the disk ex (sp),hl ;delay ex (sp),hl ;delay xor a ld (de),a ;write a trailing zero ex (sp),hl ;delay ex (sp),hl ;delay ld (de),a ;delay until previous byte written jp cstop ;stop controller and exit page ;====================================================================== ; commands for reading and writing to main memory ;================================================ ; read a block of the host's memory into the controller's memory ;--------------------------------------------------------------- ; rdch: call rcmd cread: ld a,e or d ;test for block transfered ret z call incio ;do a dma cycle ld a,(Prd_2_DatIStb) ;get the dma channel data ld (hl),a ;store data at pointer inc hl ;advance the pointer dec de ;decrement the data block count jr cread ; write a block of controller memory to the host's memory ;-------------------------------------------------------- ; wrch: call wcmd cwrite: ld a,e or d ;test for block transfered ret z ld a,(hl) ;get the memory data inc hl ;advance the pointer call incio ;do a dma cycle dec de ;decrement the block count jr cwrite ; read/write a byte from/to main memory and increment the channel address ;------------------------------------------------------------------------ ; rbyte: call rcmd ;set the channel up for reading jr rwbyte wbyte: call wcmd ;set the channel up for writing rwbyte: ex af,af' ;save the accumulator ld a,(13c2h) ;get the extended page ex af,af' ;restore the accumulator ld bc,(13c0h) ;load lower channel address call incio ;do the channel command & increment ; channel address store ;---------------------- ; castor: ex af,af' ld (13c2h),a ;save new extended page ld (13c0h),bc ;save new lower address ex af,af' ;recover the accumulator ret page ; setup for a read from the host's memory ;---------------------------------------- ; rcmd: push af ;save the accumulator ld a,0a1h ;s-100 status byte for reading push af ;save the status ld a,(1310h) ;memory image of 4006 cmd. port and 7fh ;force channel read/write bit to read jr excmd ; setup for a write into the host's memory ;----------------------------------------- ; wcmd: push af ;save the accumulator ld a,1 ;s-100 status byte for writing push af ld a,(1310h) or 80h ;force channel read/write bit to write excmd: ld (1310h),a ;update memory image of port 4006 set 1,a ;controller_stop_bit:= set ld (Pwrt_6_Dro1),a ;update command port 4006 pop af ;recover the status byte ld (Pwrt_3_BStat),a ;update the s-100 status register pop af ;recover the accumulator ret ; read/write to/from host memory and increment host address (in a & bc) ;---------------------------------------------------------------------- ; incio: ex af,af' ;get extended page value ld (Pwrt_2_HAddr),a ;initialize extended page register ex af,af' ;recover the data out (c),a ;read/write data to/from host memory (bc=addr) incr3: inc c ;advanve the lsb of dma address ret nz inc b ;advance the msb of dma address ret nz ex af,af' inc a ;advance the extended page value ex af,af' ret ; diagnostic routine - restore the channel command address ;--------------------------------------------------------- ; restor: ld bc,(13c4h) ;get the channel command address ex af,af' ;save the accumulator ld a,(13c6h) ;extended page of address jp castor+1 ;restore the channel command address page ;---------------------------------------------------------------------- ; input a character from the serial port ;--------------------------------------- ; 1) this routine returns non-zero if it didn't get a character. ; 2) notice the routine count is constantly called. ; 3) register usage ; a - general purpose/returned character ; b - bit cell timer ; c - bit count/returned character ; hl - pointer to disk status port ; ; entry point 1 - hang until a character is present hspin: call spin ;repeat jr nz,hspin ;until (character present) ret ; entry point 2 - get character (if present) and return spin: call count ;look for index/sector hole ld hl,4003h ;hl:= pointer to the disk status port bit 1,(hl) ;if (serial port not active) ret nz ; return ld bc,0a08h ;else (b:=half_bit_time, c:=bit_count) inlp1: djnz inlp1 ; wait half a bit time bit 1,(hl) ; if (serial port no longer active) jr nz,spin ; go back and count another hole inlp2: ld b,19h ;repeat (9600 baud cell time) inlp3: djnz inlp3 ; wait one cell time scf ; assume a 1 bit is present bit 1,(hl) ; if (bit is not a 1) jr nz,inskp1 ccf ; clear the bit to a zero inskp1: rra ; shift new bit into the accumulator dec c ; bit_count:= bit_count - 1 jr nz,inlp2 ;until (bit_count eq 0) ld c,a ;c:= character ret page ;---------------------------------------------------------------------- ; output a character to the serial port ;-------------------------------------- ; 3) register usage ; a - general purpose ; b - bit cell timer ; c - save of the character to output ; h - save of the character to output ; l - bit cell count ; ix - 13f0 => current drive scratch pad area ; spout: push af ld a,(1310h) set 1,a ;set the controller stop bit ld (ix+6h),a ;memory image of 4006 to 1310 pop af or 80h ;append a rest bit ld c,a ;save the character in c call count ;look for index/sector hole ld a,c ;restore the character and 7fh ;make character into 7 bit ascii ld h,a ;save ascii character in h ld a,8 ;start bit ld l,0bh ;total bit count of character oloop: add a,(ix+6) ;merge with port 4006 image ld (Pwrt_6_Dro1),a ;send bit to the serial port xor a ;clear the accumulator ld b,18h ;9600 baud cell delay time btime: djnz btime ;wait one cell time sra c ;get the next serial bit from c ccf ;compliment the carry rla rla rla rla ;rotate carry to serial bit dec l ;decrement the bit count jr nz,oloop ;0 => character done ld c,h ;restore the ascii character to c jp count page ;====================================================================== ; internal monitor ;================= ; montr: ld (monram),sp ;save the stack pointer mone2: ld sp,monram+15h ;stack_pointer:= start of monitor stack ld de,promt ;pointer:= start of prompt mone3: ld a,(de) ;repeat get next character of prompt string call spout ; print character inc de ; pointer:= pointer + 1 ld a,(de) ; get_next_character or a jr nz,mone3 ;until (character eq 0) ld de,0 ;argument:= 0 monlp1: call getc ;repeat jr c,moncmd ; if (character eq command) ld b,4 ; goto identify_a_command shift: sla e ; else rl d ; move new nibble into arg. reg. djnz shift add a,e ld e,a jr monlp1 ; identify a command ;------------------- ; 1) this routine checks if the input is a proper command (upper- ; case g (go-execute), p (print), or w (write)). ; 2) if the command is identified then control is transferred to ; the following routine (found) ; moncmd: ld b,3 ;counter:= max number of commands ld hl,cmdtbl ;pointer:= command_table_base monlp2: cp (hl) ;while (char ne command) inc hl ; pointer:= (start of vector) jr z,found ; goto execute_a_command inc hl inc hl ; pointer:= pointer+2 (start next entry) djnz monlp2 ;until (all commands checked) ld de,error ;pointer:= start of error prompt string jr mone3 ;goto monitor output prompt string ; execute a command ;------------------ ; 1) if a command was identified by cmd then the hl pair is left ; pointing to the execution vector in the command table (cmdtbl). ; found: ld a,(hl) ;move the vector into the hl pair inc hl ld h,(hl) ld l,a jp (hl) ;execute the command (go, write or print) page ;---------------------------------------------------------------------- ; go execute a routine ;--------------------- ; 1) this routine executes a routine in controller memory (execution ; address is sent in the de register pair). ; 2) both the normal and alternate register loaded from and saved to: ; monram+15h 13dc f - af 13e6 iy - iy ; 13dd a 13e7 iy ; 13de b - bc 13e8 f - af' ; 13df c 13e9 a ; 13e0 e - de 13ea c - bc' ; 13e1 d 13eb b ; 13e2 l - hl 13ec e - de' ; 13e3 h 13ed d ; 13e4 ix - ix 13ee l - hl' ; 13e5 ix 13ef h ; go: ld hl,sretrn ;sretrn is return address after cmnd finishes push hl ;put return address on stack (13da & 13db) push de ;put execution address on stack (13d8 & 13d9) ld sp,monram+15h ;stack_pointer:= start of register save area pop af ;setup registers pop bc pop de pop hl pop ix pop iy ex af,af' pop af ex af,af' exx pop bc pop de pop hl exx ld sp,monram+11h ;stack pointer to execution address ret ;execute the command sretrn: ld sp,monram+29h ;stack_pointer:= end of register save area exx ;restore registers push hl push de push bc exx ex af,af' push af ex af,af' push iy push ix push hl push de push bc push af jp mone2 ;goto the idle loop page ;---------------------------------------------------------------------- ; write to controller memory ;--------------------------- ; write: call format ;print newline followed by starting location wrloop: ld a,20h ;repeat call spout ; print a space call getc ; if (current character isn't hex) jr c,leave ; return rlca ; move current character into m.s.n rlca rlca rlca ld c,a ; save the character exx call getc ; get the l.s.n exx ; if (current character isn't hex) leave: jp c,mone2 ; return add a,c ; combine the two halves of the number ld (de),a ; store number at current location inc de ; pointer:= pointer + 1 ld a,e and 0fh jr nz,wrloop ;until (l.s.b of location is zero) jr write ;goto start a new line page ;---------------------------------------------------------------------- ; print the contents of controller memory ;---------------------------------------- ; print: call format ;repeat new line followed by starting address prnlp1: ld a,20h ; repeat call spout ; print a space ld a,(de) call putl ; print m.s.n of current location ld a,(de) call putr ; print l.s.n of current location inc de ld a,e and 0fh jr nz,prnlp1 ; until (the l.s.n of address is zero) call hspin ; wait for a character from serial port call spout ; echo the character to the serial port ld a,c cp 20h jr z,print ;until (input character is not a space) jp mone2 ;goto monitor format: ld a,0dh ;output a carriage return - line feed call spout ld a,0ah call spout ld a,d call putl ;print most-signifigant nibble of m.s.d ld a,d call putr ;print least-signifigant nibble of m.s.d ld a,e call putl ;print most-signifigant nibble of l.s.d ld a,e jr putr ;print least-signifigant nibble of l.s.d putl: rrca ;print the left half of the accm as ascii-hex rrca rrca rrca putr: and 0fh ;print the right half of the accm as ascii-hex add a,'0' cp ':' ;if (number is lt ascii-hex) jout: jp c,spout ; print it sub 0f9h ;else add offset (makes a-f print properly) jr jout ; print it page ;---------------------------------------------------------------------- ; get a character ;---------------- ; 1) this routine returns with a character in the accm. ; 2) if the character is a hex digit then it is translated into ; binary. ; 3) if the character is not hexadecimal then the carry is returned set ; getc: call hspin ;wait for a character from the serial port call spout ;echo the character to the serial port ld a,c cp '0' ;if (character lt the smallest number) ret c ; return cp 'g' ;if (character gt the largest alpha) ccf ; (adjust the return status) ret c ; return ld b,'0' ;offset:= number cp ':' ;if (character gt the largeset number) jr c,mhex cp 'a' ; if (character lt smallest alpha) ret c ; return ld b,37h ; offset:= alpha mhex: sub b ;convert ascii character into hex ret ;return ;---------------------------------------------------------------------- ; command table for monitor ;-------------------------- ; cmdtbl: db 'g' ;go execute a subroutine dw go db 'p' ;print controller memory dw print db 'w' ;write to controller memory dw write ;---------------------------------------------------------------------- ; error and prompt strings for monitor ;------------------------------------- ; error: db '?' ;question mark promt: db 0dh ;carriage return db 0ah ;line feed db ':' ;colon db 0 ;end of string page ;---------------------------------------------------------------------- ; hard sectored disk sector/index synchronization ;------------------------------------------------ ; general description: ; this routine keeps track of the sector counts for 5" hard ; disks. the program hangs, calling count looking for a sector/index ; hole or until a time-out (about 3.11 seconds) occurs. if current ; sector isn't known (decalibrated = bit-7 set) then a synchronization ; process takes place. ; initial synchronization with the disk occurs directly after ; counting 2 successive hole with a delay of 12ms or less between them ; (the program can get fooled by two counts greater than 24 ms). the ; sector following these two closely spaced holes is sector 0. ; notes: ; 1) each iteration takes about 47.45us. 14us here and 33.45 in count. ; 2) if the disk isn't turning then the z-flag is returned set. ; 3) the constant maxhs5 is the max. number of sectors on a 5" (hs) disk. ; 4) register usage: ; a - general purpose ; b - hole count ; de - time_delay ; hl - pointer to disk status register (4003h - setup in count) ; ; look for an index/sector hole hsync: ld de,0 ;time_delay:= max_delay call count ;while (hole ne found) jr nz,tsync inc de ; time_delay:= time_delay + 1 ld a,d or e ; if (time_delay eq 0) jr nz,hsync+2 ; (disk not turning) ret ; error exit w/zero flag set tsync: bit @sct,(ix+xflag) ;if (sector count eq uncalibrated) jr nz,sleave ; repeat isync: ld b,2 ; short_counts:= 2 ld de,0 ; time_delay:= max_delay hsloop: call count ; while (hole ne found) jr nz,hfound inc de ; time_delay:=time_dely+1 ld a,d ; or e ; if (time_delay eq 0) jr nz,hsloop ret ; exit error hfound: bit 1,d ; if (delay le 12.15ms) (or gt 24.3ms) jr nz,isync ; go reset short delay count ; if (only one short delay counted) djnz isync+2 ; go check for 2nd short count ; else ld (ix+0ah),0 ; sector_count:= 0 set @sct,(ix+xflag) ; sector_calibrated:= true sleave: ld a,maxhs5 ;if (index hole eq found) cp (ix+0ah) jr z,hsync ; go back & wait for sector zero ret ;exit without error page ;---------------------------------------------------------------------- ; count sector/index holes ;------------------------- ; 1) this routine returns with the zero flag cleared if a new hole ; was found. ; 2) if the maximum number of index holes has been counted then the ; drive is deselected. deselection is done by jumping to the ; deselect routine. this is done to avoide premature termination ; (see entnmi). ; 3) register usage: ; a -> general purpose ; hl -> pointer (4003h-4005h) (left pointing to 4003) ; count: ld hl,4003h ;hl:= pointer to status register bit 4,(hl) ;if (index/sector_bit eq inactive) jr nz,sihigh set @hol,(ix+xflag) ; hole_processed:= false ret ; exit (hole not found) sihigh: bit @hol,(ix+xflag) ;if (hole_processed eq true) ret z ; exit (hole found but already processed) res @hol,(ix+xflag) ;hole_processed:= true bit @hrd,(iy+yflag) ;if (media eq hard_sectored) jr z,icount inc (ix+0ah) ; sector_count:= sector_count + 1 ld a,maxhs5 ; (max_sectors = last sector number + 1) bit @fhf,(ix+xflag) ; if (first hole found is true) jr z,sidle inc a ; max_sectors:= max_sectors + 1 sidle: set @fhf,(ix+xflag) ; set first hole found to true sub (ix+0ah) ; if (sector ne 0) ret nz ; exit (hole found & not sect 0) ld (ix+0ah),a ; sector_count:= 0 icount: inc (ix+0bh) ;index_count:= index_count + 1 ld a,(ix+0dh) ;a:= max revs before drive deselect sub (ix+0bh) ;if (index_count eq max revs before deselect) jp z,drvoff ; deselect the drive (do not call) ret page ;---------------------------------------------------------------------- ; turn off the current drive ;--------------------------- ; 1) this routine sets the drive's status inactive and then turns ; the motor. ; drvoff: call desel ;set the drive status to inactive call motoff ;turn off the motor ret ;---------------------------------------------------------------------- ; deselect the drive but not the motor ;------------------------------------- ; 1) this routine set the status of the current drive to inactive. ; 2) to turn off the motor line you must call motoff. ; 3) register usage: ; hl -> pointer to drive control port ; desel: push af ;save the accm and the flags ld a,11111110b ;setup to deselect everything but motor ld hl,4004h bit @5in,(iy+yflag) ;if (drive_control_port ne 5") jr nz,deskp1 inc l ; (move pointer to 8" control port 4005h) deskp1º bit @mot,(ix+xflag) ;if (motor_staus eq inactive) jr nz,deskp2 ld a,11111111b ; setup to deselect everything deskp2: lÄ (hl),a ;deselect res @hdl,(iy+yflag) ;head_loaded:= false res @sct,(ix+xflag) ;sector_count_calibrated:= false pop af ;restore the accm and the flags ret ;---------------------------------------------------------------------- ; turn off the motor line ;------------------------ ; 1) register usage: ; hl -> pointer to drive control port (left pointing to 4003) motoff: ld hl,4004h ;hl:= 5" drive control port (4004h) moskp1: ld (hl),11111111b ;deselect the drive ld l,3 ;hl:= disk status port (4003h) res @mot,(ix+xflag) ;motor_status:= inactive ret page ;====================================================================== ; command table ;============== ; 1) the byte count at the end of each title line tells how many ; bytes the total command takes. this includes both the command ; code (always present) and the status (sometimes present). ; 2) the parameters are enumerated following each title line. ; 3) commands are grouped according to function. ; ;---------------------------------------------------------------------- ;initial setup commands: ;----------------------- ; ;set crc error retry count (2 bytes) ; 1 - retry count + 1 ; ctable: db 28h dw setcrc db 1 ;unload head timeout (2 bytes) ; 1 - count ; db 2fh dw setlft db 1 ;set track size (4 bytes) ; 1 - drive ; 2 - highest track ; db 2dh dw trackz db 2 page ;set drive parameters ; 1 - drive ; 2 - step rate ; 3 - step settle time ; 4 - head load delay time ; 5 - motor on delay time ; db 30h dw setdp db 5 ;set media parameters ; 1 - drive ; 2 - starting track of write pre-compensation ; 3 - highest track number ; 4 - first sector's number ; 5 - total number of sectors per track ; db 31h dw setmp db 5 ;change logical drives (3 bytes) ; 1 - mask ; db 2eh dw logcal db 1 ;set dma address (4 bytes) ; 1 - new dma address low byte ; 2 - " " " med byte ; 3 - " " " high byte ; db 23h dw setdma db 3 ;set channel command word address (4 bytes) ; 1 - new channel command address low byte ; 2 - " " " " med byte ; 3 - " " " " high byte ; db 27h dw setccw db 3 page ;---------------------------------------------------------------------- ; disk access commands ;--------------------- ; ;sense drive status (6 bytes) ; 1 - drive ; db 22h dw gstat db 1 ;read sector (5 bytes) ; 1 - track ; 2 - side/sector ; 3 - drive ; db 20h dw rdsect db 3 ;write sector (5 bytes) ; 1 - track ; 2 - side/sector ; 3 - drive ; db 21h dw wrsect db 3 ;read track (5 bytes) ; 1 - track ; 2 - side ; 3 - drive ; 4 - sector table low byte ; 5 - " " med byte ; 6 - " " high byte ; db 29h dw rtrack db 6 ;write track (8 bytes) ; 1 - track ; 2 - side ; 3 - drive ; 4 - sector table low byte ; 5 - " " med byte ; 6 - " " high byte ; db 2ah dw wtrack db 6 page ;---------------------------------------------------------------------- ; controlling process execution ;------------------------------ ; ;branch in channel (4 bytes) ; 1 - new channel address low byte ; 2 - " " " med byte ; 3 - " " " high byte ; db 26h dw setcaw db 3 ;halt controller (2 bytes) ; 0 - no parameters ; db 25h dw haltc db 0 ;set interrupt request (2 bytes) ; 0 - no parameters ; db 24h dw intrqc db 0 page ;---------------------------------------------------------------------- ; other commands ;--------------- ; ;output to serial port (3 bytes) ; 1 - ascii character ; db 2bh dw serout db 1 ;serial input enable (2 bytes) ; 1 - mask ; db 2ch dw srenbl db 1 ;dump controller memory (8 bytes) ; 1 - main memory lo byte ; 2 - " " med byte ; 3 - " " high byte ; 4 - number of bytes to transfer low cnt ; 5 - " " " " " high cnt ; 6 - address in controller memory low byte ; 7 - " " " " high byte ; db 0a0h dw dumpm db 7 ;load controller memory (8 bytes) ; 1 - main memory lo byte ; 2 - " " med byte ; 3 - " " high byte ; 4 - number of bytes to transfer low cnt ; 5 - " " " " " high cnt ; 6 - address in controller memory low byte ; 7 - " " " " high byte ; db 0a1h dw loadm db 7 ;execute controller routine (4 bytes) ; 1 - execution address low byte ; 2 - " " high byte ; 3 - " " status ; db 0a2h dw gomem db 2 ; db 80h ;end of table page ;====================================================================== ; tables that are moved into the controller's ram ;================================================ ; vector table (loaded into memory starting at location 1290h) ;============================================================= ; rtbl: dw reads ;vector to soft sectored read a sector routine dw read5 ;vector to hard sectored read a sector routine dw 0 ;spare dw 0 ;spare wtbl: dw writes ;vector to soft sectored write a sector routine dw write5 ;vector to hard sectored write a sector routine dw 0 ;spare dw 0 ;spare ; 8" soft sectored drive constants ;================================= ; 8" drive single density [addr = 12a0] ;-------------------------------------- sect8: db 44h ;port 4006 control byte db 73h ;port 4007 control byte db 43 ;default starting track of write pre-compensation db 0 ;not used db 0 ;not used db 0 ;not used db 0 ;track number db 0 ;side number db 0 ;sector number db 0 ;sector length code db 0 ;single density id byte db 63h ;port 4007 crc control byte db 0 ;not used db 0 ;not used db 44h ;port 4006 write control byte db 0h ;not used ; 8" drive double density [addr = 12b0] ;-------------------------------------- db 4 ;port 4006 control byte db 70h ;port 4007 control byte db 43 ;default starting track of write pre-compensation db 0 ;not used db 0 ;not used db 0 ;not used db 0 ;track number db 0 ;side number db 0 ;sector number db 0 ;sector length code db 1 ;double density id byte db 61h ;port 4007 crc control byte db 0 ;not used db 0 ;not used db 4h ;port 4006 write control byte db 0h ;not used page ; 5" soft sectored drive constants ;================================= ; 5" drive single density (soft sectored) [addr = 12c0] ;------------------------------------------------------ db 64h ;port 4006 control byte db 7bh ;port 4007 control byte db 17 ;default starting track of write pre-compensation db 0 ;not used db 0 ;not used db 0 ;not used db 0 ;track number db 0 ;side number db 0 ;sector number db 0 ;sector length code db 0 ;double density flag db 63h ;checksum control code db 0 ;not used db 0 ;not used db 64h ;port 4006 write control byte db 0h ;not used ; 5" drive double density (soft sectored) [addr = 12d0] ;------------------------------------------------------ db 54h ;port 4006 control byte db 70h ;port 4007 control byte db 17 ;default starting track of write pre-compensation db 0 ;not used db 0 ;not used db 0 ;not used db 0 ;track number db 0 ;side number db 0 ;sector number db 0 ;double density flag db 1 ;sector length code db 60h ;checksum control db 0 ;not used db 0 ;not used db 24h ;port 4006 write control byte fm=false & mini=true db 0h ;not used page ;5" hard sectored drive constants ;================================ ; 5" drive single density (hard sectored) [addr = 12e0] ;------------------------------------------------------ sect5: db 64h ;port 4006 control byte db 73h ;port 4007 control byte db 17 ;default starting track of write pre-compensation db 0 ;not used db 0 ;not used db 0 ;not used db 0 ;track number (not used in this format) db 0 ;side number (not used in this format) db 0 ;sector number (not used in this format) db 1 ;sector length code db 0 ;double density flag db 73h ;checksum control byte db 0 ;not used db 0 ;not used db 64h ;port 4006 write control byte db 0h ;not used ; 5" drive double density (hard sectored) [addr = 12f0] ;------------------------------------------------------ db 54h ;port 4006 control byte db 7bh ;port 4007 control byte db 17 ;default starting track of write pre-compensation db 0 ;not used db 0 ;not used db 0 ;not used db 0 ;track number (not used in this format) db 0 ;side number (not used in this format) db 0 ;sector number (not used in this format) db 2 ;sector length code db 1 ;double density flag db 7bh ;checksum control byte db 0 ;not used db 0 ;not used db 54h ;port 4006 write control byte db 0h ;not used page ; drive parameter tables (loaded starting at location baseiy) ;============================================================ ; 8" drive table drive8: db 4dh ;high track + 1 db 0ffh ;current track db 0f3h ;drive pattern ; - ;logical drive number (inserted by reset routine) db 11h ;step time (lsb) db 1 ;step time (msb) db 0 ;head load delay time (lsb) default set in prep to 50ms db 0 ;head load delay time (msb) db 0 ;motor on delay time (lsb) default set in prep to .5sec db 0 ;motor on delay time (msb) db 11h ;step settle time (lsb) db 1 ;step settle time (msb) db 0h ;first sector's number db 0h ;user's specification - number of sectors per track db 10h ;default drive configuration byte db 0 ;user's specification - starting track of write precomp ; 5" drive table drive5: db 28h ;high track + 1 db 0ffh ;current track db 0f2h ;drive pattern ; - - ;logical drive number (inserted by reset routine) db 0cch ;step time (lsb) db 0 ;step time (msb) db 0 ;head load delay time (lsb) default set in prep to 50ms db 0 ;head load delay time (msb) db 0 ;motor on delay time (lsb) default set in prep to .5sec db 0 ;motor on delay time (msb) db 0 ;step settle time (lsb) db 2 ;step settle time (msb) db 0h ;user's specification - first sector's number db 0h ;user's specification - number of sectors per track +1 db 3ch ;default drive configuration byte (2eh = hard sect.) db 0 ;user's specification - starting track of write precomp ; system status and control area (loaded into memory at 13f0h) const: db 40h ;current drive table pointer lsb db 13h ;current drive table pointer msb db 0 ;not used db 0 ;not used db 0 ;not used db 0 ;not used db 6 ;port 4006 memory image db 0 ;not used db 0 ;not used db 0 ;not used db 0 ;sector count - sector_decalibrated_flag db 0 ;index count db 0ah ;retries on crc error db 16h ;max. number of revolutions before drive deselected db 0 ;system status byte db 0 ;break character count ds 1000h - $, 0ffh page ;====================================================================== ; memory layout ;============== ; ;---------------------------------------------------------------------- ; stack ;------ ; ; 1000 - 102f ; ;---------------------------------------------------------------------- ; user area ;---------- ; ; 1030 - 128f ; ;---------------------------------------------------------------------- ; read/write vector tables ;------------------------- ; ; 1290 - 1297 rtbl ; 1298 - 129f wtbl ; ;---------------------------------------------------------------------- ; drive constants tables (see sect8 and sect5 for table definitions) ;------------------------------------------------------------------- ; ; 12a0 8" single density ; 12b0 8" double " ; 12c0 5" single " soft sectored ; 12d0 5" double " " sectored ; 12e0 5" single " hard sectored ; 12f0 5" double " " sectored ; ; each drive table is organized as follows: ; ...0 port 4006 control byte ; ...1 port 4007 control byte ; ...2 default starting track of write pre-compensation ; ...3 not used ; ...4 not used ; ...5 not used ; ...6 track number ; ...7 side number ; ...8 sector number ; ...9 sector length code ; ...a double density id byte ; ...b port 4007 crc control byte ; ...c not used ; ...d not used ; ...e port 4006 write control byte ; ...f not used ; ; 1300 - 130f not used page ;---------------------------------------------------------------------- ; current drive's constants ;-------------------------- ; 1) this area is loaded by cdisk from the drive constants tables ; ; 1310 port 4006 read control byte ; 1311 port 4007 control byte ; 1312 starting track of write pre-compensation ; 1313 not used ; 1314 not used ; 1315 not used ; 1316 track number ; 1317 0/1 (side 0/ side 1) ; 1318 sector number ; 1319 sector length code (0-128, 1-256, 2-512, 3-1024) ; 131a 0/1 (single density/double density) ; 131b port 4007 crc control code ; 131c not used ; 131d not used ; 131e port 4006 write control byte ; 131f not used ; ;---------------------------------------------------------------------- ; dma pointer ;------------ ; ; 1320 dma address lsb ; 1321 dma address msb ; 1322 dma address extended page ; ;---------------------------------------------------------------------- ; parameter save area (see exec) ;------------------------------- ; ; 1323 track number ; 1324 sector number (side number in high order bit) ; 1325 drive number ; ;---------------------------------------------------------------------- ; misc storage ;------------- ; ; 1326 status address lsb ; 1327 status address msb ; 1328 status address extended page ; 1329 next command ; 132a dma byte count lsb ; 132b dma byte count msb ; 132c data byte ; 132d data byte ; 132e stksav stack pointer location during reads and writes (lsb) ; 132f " " " " " " " (msb) ; page ;---------------------------------------------------------------------- ; transfer parameter buffer ;-------------------------- ; 1330 block#1 extended page address ; block#1 byte count less one ; . ; . ; . ; block#k extended page address ; block#k byte count less one ; extended page address ; 1 (signal to stop block transfers) ; (conditional) 40/3f (zero flag set/zero flag not set) ; (conditional) a1/0 (port 4007 crc control code/nop) ; 40 (zero flag set) ; a1 (port 4007 crc control) ; ;---------------------------------------------------------------------- ; drive parameter tables (dpt) ;----------------------------- ; 1340 - 134f physical drive a logical drive 0/4 (first 8" drive) ; 1350 - 135f " " b " " 1/5 ; 1360 - 136f " " c " " 2/6 ; 1370 - 137f " " d " " 3/7 ; 1380 - 138f " " e " " 4/0 (first 5.25" drive) ; 1390 - 139f " " f " " 5/1 ; 13a0 - 13af " " g " " 6/2 ; 13b0 - 13bf " " h " " 7/3 ; ; each dpt is organized as follows: ; ...0 maximum track value plus one ; ...1 current track (ff if heads not calibrated) ; ...2 drive pattern for port 4004/4005 ; ...3 logical drive number ; ...4 step time (lsb) ; ...5 step time (msb) ; ...6 head load delay time (lsb) ; ...7 head load delay time (msb) ; ...8 motor on delay time (lsb) ; ...9 motor on delay time (msb) ; ...a step settle time (lsb) ; ...b step settle time (msb) ; ...c user specified lowest sector number ; ...d user specified number of sectors per track + 1 ; ...e drive configuration byte ; bit 0 1 = (@ver) sector header has been verified ; " 1 1 = (@hrd) media is hard sectored ; " 2 1 = (@5in) drive is five inch (4004 = 5" port) ; " 3 1 = (@moc) motor has on/off control ; " 4 1 = (@dbl) media is double density ; " 5 1 = (@nrc) drive doesn't have ready line ; " 6 1 = (@nhc) drive can't unload heads ; " 7 1 = (@hdl) heads are currently loaded ; ...f user specified starting track of write precompensation ; page ;---------------------------------------------------------------------- ; channel command pointers and stack areas ;----------------------------------------- ; ; 13c0 current command pointer lsb ; 13c1 " " " msb ; 13c2 " " " extended page ; 13c3 not used (was = retry constant) ; 13c4 initial command pointer address lsb ; 13c5 " " " " msb ; 13c6 " " " " extended page ; ; 13c7 - 13d7 stack space used during monitor go: command execution ; 13d8 & 13d9 monitor execution address (see go:) ; 13da & 13db monitor 'normal return' address (see go:) ; ; 13dc stack for monitor, after any errors, a halt or interrupt request ; ; monitor save locations for registers (see go: in monitor) ; --------------------------------------------------------- ; 13dc f - af 13e2 l - hl 13e8 f - af' 13ee l - hl' ; 13dd a 13e3 h 13e9 a 13ef h ; 13de b - bc 13e4 ix - ix 13ea c - bc' ; 13df c 13e5 ix 13eb b ; 13e0 e - de 13e6 iy - iy 13ec e - de' ; 13e1 d 13e7 iy 13ed d ; ;---------------------------------------------------------------------- ; system status and control area ;------------------------------- ; ; 13f0 current drive pointer lsb ; 13f1 current drive pointer msb ; 13f2 not used ; 13f3 not used ; 13f4 not used ; 13f5 not used ; 13f6 memory image of port 4006 - only used by serout ; 13f7 not used ; 13f8 not used ; 13f9 not used ; 13fa sector count ; 13fb index count ; 13fc max. number of retries on encountering a crc error ; 13fd max. number of revolutions before drive deselected ; 13fe (xflag) system status byte ; bit 0 1 = (@ser) serial_input_status = active ; " 1 1 = (@mon) monitor_status = active ; " 2 1 = (@mot) motor_status = active ; " 3 1 = (@sct) sector count is calibrated ; " 4 1 = (@hol) sector/index hole has been processed ; " 5 1 = (@fhf) first hole found ; 13ff break character count page ;---------------------------------------------------------------------- ; timing constants for the sdelay routine (msb first) ;---------------------------------------------------- ; 1) any constants used in the home routine should be unique in ; the lsb (1 digit in the lsb causes roughly a 3% change) ; ; time msb lsb ; 1ms - 00 22 (hex) ; 3ms - 00 66 ; 5ms - 00 aa ; 6ms - 00 cc ; 8ms - 01 11 ; 15ms - 02 00 ; 20ms - 02 ab ; 26ms - 03 74 ; 30ms - 04 01 ; 40ms - 05 54 ; ;---------------------------------------------------------------------- ; error code summary ;------------------- ; 40 - normal completion code - no error in operation ; 80 - host issued improper command code ; 81 - host issued improper disk drive number ; 82 - drive not ready ; 83 - track number out of range for selected drive ; 84 - unreadable media ; 85 - sector length code is too large ; 86 . not used (was = error in sector header scan) ; 87 - seek error ; 88 - missing sector header id (sync) byte ; 89 - track number mis-match ; 8a - side number (0=bottom or first) mis-match ; 8b - sector number mis-match ; 8c - sector length mis-match ; 8d - crc error in sector header ; 8e - crc error in data field ; 8f - host issued improper sector number for current media ; 90 - diskette is write protected ; 91 - lost data - dma channel did not respond in time ; 92 - lost command - channel did not respond in time ; 93 - time-out waiting for data id mark ; 94 - heads failed to move to the home position end