; Intersystems FDC II bootstrap program ; Written by Robert Bedichek February 1980 if 0 This program resides in a 1K nonvolitile memory that starts at address zero. It reads a variable number of sectors from the first two cylinders of drive zero, surface zero. The first eight bytes of the first sector of the outermost cylinder gives information about how to load the data. The diskette can be recorded in either FM (single density) or MFM (double density). Sector sizes ranging from 128 bytes/sector to 8196 bytes/sector are accommodated. After the data is loaded, control is passed to an address specified in the first eight bytes of the first sector. The memory in which this program resides is switched off before control is passed. Note that this program uses the end of read/write memory for stack space. So, the load parameters should not be set so that the transfer over-writes this part of memory. An area WorkLen bytes from the end of memory is used to read in the first sector for the load parameters. This may be over-written by the data loaded. Bootstrap register allocation and conventions Register | Contents _____________________________________________________________________ A | Scratch register B | Starting sector number for next read operation C | Ending " " " " " " D | Instruction length (looked at by StartIO) E | Intruction opcode ( " " " " ) H | First argument for instruction (looked at by startio) L | Second " " " ( " " " "" ) FLAGS | Scratch (alternate set) A' | N - bytes per sector (0:128 1:256 2:512 3:1024 4:2048 .. ) B' | Starting sector number for cylinder 1 C' | Ending " " " " " D' | Scratch E' | " HL' | Points to list of load parameters. FLAGS' | Carry = 1 <==> MFM Carry = 0 <==> FM IX | Starting address for booted program IY | Not used The load paramters are on drive zero, side zero, cylinder zero in the first eight bytes of the first sector. Their format: |____.____|____.____|____.____|____.____|__ unused ... Word 0 1 2 3 Word 0: Address to start loading at. Word 1: Address to pass control to after data is loaded. Word 2: Start and end sectors of segment to be loaded from cylinder 0. ( 0, 0 ) means don't load anything from cylinder 0. Word 3: Start and end sectors of segment to be loaded from cylinder 1. ( 0, 0 ) means don't load anything from cylinder 1. Example: 00 E4 03 FA 02 26 1 26 This will load sectors 2 through 26 from cylinder 0 and then sectors 1 through 26 from cylinder 1. The data will be load contiguously starting at location E400 and execution will begin at FA03. endif ; Port addresses DBase equ 0B0H ; FDC II occupies block 16 bytes long. DStat equ DBase+0 ; FDC II status register. DData equ DBase+1 ; Command port address. DRead equ DBase+2 ; out DRead sets controller for read operation. ENROM equ DBase+6 ; Writing to this port will enable the ROM. HiDMA equ DBase+8 ; High byte of DMA address register. MiDMA equ DBase+10 ; Middle byte (A15 - A8) of DMA address reg. LoDMA equ DBase+12 ; Low byte of DMA address. DsROM equ DBase+14 ; Writing to this port turns off the ROM. Lights equ 0FFH ; Front panel programmed output lights. Step equ 10 ; Milliseconds per step. MStart equ 8192 ; Address to start testing memory at. WorkLen equ 8192+256 ; Must be a multiple of 256 Start org 0 di ; No funny business. out DRead ; Set up the DMA controller for reading. ; Non-destructively test memory to find the end of it (to find a scratch ; area to use for reading the load parameters. lxi h,MStart ; Address to start testing at. MemLp mov a,m cma mov m,a ; Store complement. cmp m ; Did the location actually get complemented ? jrnz EndHit ; Branch if we just ran into a non-RAM area. cma mov m,a ; Restore the byte to its original value. inx h mov a,h ora l ; Have we just overflowed ? jrnz MemLp ; HL points to one past the last good RAM location. EndHit sphl ; So we can do procedure calls. mov a,h sui WorkLen/256 mov h,a ; HL points to an area WorkLen long. exx exaf xra a exaf ; Initialize NEC 765 lxi d,303H ; 3 byte instruction, opcode = specify lxi h,16-Step*16+0EH*256+0FEH ; Steping rate and other junk. call StartIO ; Do it. ; Sense Drive zero status, wait until drive is ready RdyWait lxi d,200H+4 ;2 byte instruction, opcode=sense drive status lxi h,0 ; Drive zero,head zero. call StartIO ; Do it. call WaitStat in DData ; retreive Status Register 3 ani 20h ; check ready bit jrz RdyWait ; repeat if drive not ready ; Recalibrate Drive zero. lxi d,200H+7 ; 2 byte instruction, opcode = recalibrate mvi H,0 ; Drive zero, head zero. call StartIO call SenInt ; Sense interrupt status to make NEC happy. ; Alternately try reading the header at single and double density. RHLP lxi d,200H+0AH ; 2 byte instruction, opcode = read header lxi h,0 ; Drive zero. call StartIO call Result jrz Success ; Branch if the read was good. ; Complement the C' to switch the density setting. exaf cmc exaf jmpr RHLP ; A' now has the right N and C' has the right density. ; Now we can load the data ; Load the DMA registers with the address of the work area. Success exx xra a out HiDMA ; A23 - A16 are zero. mov a,h ; High byte of work area. out MiDMA mov a,l ; Low byte of work area address. out LoDMA exx ; Read sector 1 on side zero, drive zero, head zero, cylinder zero ; to get the load information. lxi d,400H+6 ; 9 byte instruction, opcode = read. lxi h,0 ; head 0, drive 0, cylinder 0 lxi b,101h ; Read from sector 1 to sector 1. call StartIO call Result jnz Start ; restart process if error ; Read the data ; Load the DMA registers with the load address. exx mov a,m ; Low byte of load address. out LoDMA inx h mov a,m out MiDMA ; Put next word (the start address) in IX. inx h mov e,m inx h mov d,m push d ; Get start address into IX by pushing it pop ix ; onto the stack from DE and poping from IX. ; Get starting and ending sector numbers inx h mov a,m ; Starting sector number for track zero. exx mov b,a exx inx h mov a,m exx mov c,a exx ;Now get starting and ending sector numbers for cyinder one. inx h mov b,m inx h mov c,m exx lxi h,0 ; Start on drive zero, cylinder zero. RdNext lxi d,400H+6 ; 9 byte instruction, opcode = read. call StartIO ; Read a cylinder. call Result jnz Start ; restart process if error inr l ; cylinder := cylinder + 1 mov a,l cpi 2 ; Are we done ? jz ByeBye ; Seek to cylinder one. lxi d,300H+0FH ; 2 byte instruction, opcode = seek call StartIO call SenInt ; Make NEC happy. ; Put cylinder 1's start and end sectors in B and C. exx push b exx pop b ; Check if either sector specification for track two is 0 ; If so, we are done mov a,b cpi 0 jz ByeBye mov a,c cpi 0 jz ByeBye jmpr RdNext ; StartIO loads up the NEC 765's registers with bytes from the Z-80's ; registers. If the carry is set, the operation will be done in MFM. ; Registers A and D are trashed. StartIO call WaitCom exaf jrnc FM ; Branch if we are doing instrucion in FM. bset 6,e ; Turn on MFM bit. FM exaf mov a,e out DData ; Open wide 765. out Lights ; Display opcode on front panel lights. ; Check to see if this is a one byte instruction. dcr d rz ; Load first argument call WaitCom mov a,h out DData dcr d rz ; Load second argument. call WaitCom mov a,l out DData dcr d rz ; Load head number - always zero. call WaitCom xra a out DData ; Load starting sector number. call WaitCom mov a,b out DData ; Load N call WaitCom exaf out DData exaf ; Load endinf sector number. call WaitCom mov a,c out DData ; Load gap length call WaitCom exaf push psw lxi d,GPLTAB add e MOV E,A ; I know that GPLTAB does not span a 256 byte boundry. pop psw exaf ldax d ; A := GPLTAB( N ) out DData ; Load data transfer length. call WaitCom mvi a,0ffh exaf push psw ora a ; Is N = 0 jrnz NZero exaf mvi A,80H exaf NZero pop psw exaf out DData ret ; WaitCom waits for the data register in the NEC 765 to be ready for ; loading. Affects no registers. WaitCom push psw push b TP mvi B,20 ; Retry count for loading command register. WaitLp in DStat ani 0C0H ; Just look at the top two bit of the status register. cpi 080H jrz OKNow djnz WaitLp ; Time out - read the data port to try to remedy the problem. in DData jmpr TP OKNow pop b pop psw ret ; Read the interrupt status by issuing a sense interrupt status instruction. ; This is mandatory after a seek or recalibrate. SenInt lxi d,100H+8 ; 1 byte instruction, opcode = sense int. call StartIO mvi b,20 ; A fine number. RO in DStat ani 0c0h cpi 0c0h jrnz NotYet in DData ; Dump it. NotYet djnz RO in DStat ani 1 ; Look at the FDC busy bits. jrnz SenInt ret ; Result is called to perform the Result Phase of the NEC 765's operation ; sequence. Seven status bytes are read from the 765. A' is set to ; N (the last byte read), which gives the number of bytes/sector Result push h ; ST0 reads the first status byte in the result phase. A <> 0 <==> error call WaitStat in DData ani 18h ; Look at fault and not ready bits. mov h,a ; Read second status byte ( ST1 ). call WaitStat in DData ani 35H ; Look at CRC, Over Run, Missing Header & sector bits. ora h mov h,a ; Read the third status byte (ST2) call WaitStat in DData ani 31h ; CRC error, wrong cylinder, missing address mark. ora h mov h,a ; Read all those dumb bytes that you get at the end. call WaitStat in DData ; Cylinder call WaitStat in DData ; Head number (0..1) call WaitStat in DData ; Sector number. call WaitStat exaf in DData ; N - bytes/sector exaf mov a,h ; Get error flag. ora a ; Set Z to 1 if error occurred. pop h ret ; WaitStat returns when the status register is ready to be read. ; Register A and the flags are burned. WaitStat push b WtLp in DStat ani 0C0H ; Look at top two bits of the status register. cpi 0C0H jrnz WtLp pop b ret ; Gap length table - indexed by N GPLTAB DB 7,14,27,53 ; The following code is executed after the data has been loaded ; from disk. It turns off the ROM and then executes a pcix ; instruction. ; ram by code at the SKIP label. instr out DsROM pcix instrl equ $-instr ByeBye lxi h,-10 ; Space for instructions below the stack. dad sp push h xchg lxi h,instr ; First byte of instructions to move to RAM. lxi b,instrl ; Length of instructions. ldir pop h pchl ; Branch to intructions in RAM. end Start