PAGE 60 TITLE IPL Program for CP/M and MP/M loaders ; ; SGBOOT.MAC ; ; The Systems Group cold boot loader program, ; for the Models FDC-2800/1 floppy disk controllers. ; ; edit history: ; 81Jun17 jel - Updated for dual FDC boot capabilities ; ; This program is designed to be stored in the ROM on the FDC-2800/1 ; board. The reserved track(s) of a CP/M or MP/M diskette is read in ; beginning at LDRORG (normally 100 hex), then control transfered to ; the loader at LDRE (normally 1700 hex, BOOT entry point of LDRBIOS). ; Refer to table at end of source for the types of diskette that may ; be read. ; Upon reset of the FDC board, the ROM on the board is read from 0, ; which contains a jump to the following code that turns of the BOOT ; function of the FDC board. The IPL portion of the ROM is then ; transfered to the address IPLORG (normally 3000 hex) and executed at ; that same address. The first procedure of the IPL program is to turn ; the ROM on the FDC board off, thus clearing the system of all BOOT ; related ROM. ; Source is written for MACRO-80 (Microsoft's macro assembler) TRUE EQU -1 ; define true FALSE EQU 0 ; define false @UDSK EQU 4 ; current disk number HDSYS EQU TRUE ; if hard disk boot attached IPLORG EQU 3000H ; boot loader origin LDRORG EQU 100H ; origin of second loader on disk LDRE EQU 1700H ; second loader entry point ; Equates and definitions RTRYS EQU 5 ; Number of Retrys ND EQU 1 ; non-DMA mode bit ; 5" drive parameters for 765 MHLT EQU 75 ; head load time MSRT EQU 20 ; drive step rate MHUT EQU 480 ; head unload time ; 8" drive parameters for 765 HLT EQU 50 ; head load time SRT EQU 8 ; drive step rate HUT EQU 240 ; head unload time ; ; FDC-2800 base assignments DSKB8 EQU 080H ; base of 8" controller DSKB5 EQU 090H ; base of 5" controller ; base relative port assignments FDCMSR EQU 0 ; 765 FDC Main status register DDATA EQU 1 ; 765 FDC Main data register CONTRL EQU 2 ; Main board control port ZDMA EQU 4 ; Z-80 DMA device ; SECBUF EQU 0FC00H ; memory mapped sector buffer addr ; ; NEC 765 intruction set used. SCYCMD EQU 03H ; specify drive parameters SDSCMD EQU 04H ; sense drive status RDCMD EQU 06H ; read sector RECCMD EQU 07H ; recalibrate SISCMD EQU 08H ; sense interrupt status RIDCMD EQU 0AH ; read sector ID SKCMD EQU 0FH ; seek command ; ; Control Port (CONTRL) bit assigment for output. BTDI EQU 01H ; Boot (1=disabled) SBENA EQU 02H ; Sector buffer enable (1=true) INTEN EQU 04H ; Interrupt enable (1=unmask) ROMDI EQU 08H ; ROM enable (1=disabled) MOTOR EQU 10H ; Motor control (1=start) PIOEN EQU 20H ; programmed I/O mode (1=true) ; ; Control Port bit assignment for input. PGMER EQU 40H ; programmed I/O time-out error INTRQ EQU 80H ; 765 interrupt request PAGE ; ; FDC port relative macros INP MACRO PORT ; relative input macro CALL INPUT ; call the routine DB PORT ; pass rel port next in memory ENDM OUTP MACRO PORT ; relative output macro CALL OUTPUT ; call the routine DB PORT ; pass rel port next in memory ENDM ; After power-up or reset of the FDC board, the ROM on the board appears at ; every 2k boundry and the CPU begins executing from the ROM at address 0. ; The following jump transfer the program counter to inside the real address ; of the ROM at F003 hex. ; BEGROM: JMP BEGROM+3 ; ; Now the BOOT function of the FDC is disabled by the next output allowing ; system RAM to be accessed. ; MVI A,BTDI ; disable BOOT code OUT DSKB8+CONTRL ; send to 8" controller OUT DSKB5+CONTRL ; send to 5" controller also ; ; Now a small delay is imposed to allow things to settle after ; a power-up or reset. ; LXI H,0 ; delay after start-up DELAY: DCX H MOV A,L ORA H JNZ DELAY ; ; Now the IPL portion of the ROM is moved to address IPLORG in system ; RAM and control transfer to the IPL program. ; LXI H,ROMIPL ; IPL storage in ROM LXI D,IPLORG ; IPL origin in memory LXI B,IPLLEN ; IPL length for transfer MOVE: MOV A,M STAX D INX H INX D DCX B MOV A,C ORA B JNZ MOVE JMP IPLORG ; and execute it SUBTTL Main IPL program PAGE ROMIPL EQU $ ; beginning of IPL storage in ROM ; ; The following IPL is designed to run in system RAM at address IPLORG. ; It is stored in the ROM and moved and executed by the ROM. ; .PHASE IPLORG ; START: DI ; insure interrupts are disabled LXI SP,START ; initialize stack pointer ; ; The first procedure is to determine what kind of processor is executing ; (Z80 or 8080). This is used to determine if Z80 full track reads may be ; performed. An 8080 flag is set. ; MVI A,1 .Z80 JR YESZ80 .8080 STA I8080 ; set 8080 flag YESZ80: ; ; The default value for the control latch on the FDC board is sent to ; reset the port. This latch is pre-set for BOOT disable and ROM disable ; functions. ; LDA LATCH ; get default latch code OUT DSKB8+CONTRL ; send to 8" controller OUT DSKB5+CONTRL ; send to 5" controller also ; ; ; This is the entry point of the outside shell of disk retrys. And the failure ; of a boot attemp, BOOTERR branches here after waiting untill the diskette has ; been removed from the drive. ; REBOOT: ; LXI SP,START ; re-initialize stack pointer XRA A STA MINI ; reset mini flag ; NEWFDC: CALL FDCINIT ; initialize controller JC NXTFDC ; select other controller if not present XRA A STA DRIVE ; start scan with drive 0 ; WAITRY: LDA DRIVE ANI 03H STA @UDSK ; update drive variable in Page 0 RRC RRC MOV B,A LDA LATCH ANI 03FH ORA B STA LATCH OUTP CONTRL ; CALL SDS ANI 20H ; test ready line CNZ DRVRY ; yes, drive is ready ; LXI H,800H WTRY1: DCX H MOV A,L ORA H JNZ WTRY1 ; LXI H,DRIVE MOV A,M INR A ANI 03H MOV M,A JNZ WAITRY ; NXTFDC: ; select other FDC board LDA MINI CMA STA MINI IF HDSYS ORA A CZ HDCHK ; let hard disk make boot attemp ENDIF JMP NEWFDC ; loop for new controller ; ; A diskette has now been inserted, recalibrate drive 0. ; DRVRY: LXI B,RECCMD SHL 8 + 2 ; get command and length CALL CMDRDY ; issue it CALL DPOLL ; wait for completion LXI B,RECCMD SHL 8 + 2 CALL CMDRDY ; issue second recal for 96 TPI drives CALL DPOLL XRA A STA TRACK ; reset track number ; ; Next the density of the diskette is determined by issuing read ID in first ; double then single density and checking for proper status. The N (bytes/rec) ; code is stored in bits 1-0 of the ACC and bit-6 is set if the good read ID ; was in double-density. ; MVI B,RIDCMD+40H ; get double-density read ID CALL READID ; issue it JNZ CKSD LDA RWSTBL+6 ORI 40H ; set double-density bit JMP GDID CKSD: MVI B,RIDCMD ; get single-density read ID CALL READID ; issue it LDA RWSTBL+6 JZ GDID RET ; back to drive scanning ; ; A good read ID branches here where the N bits are checked for 0 (128 byte ; sectors) and if so the DTL field is set to 128 else it is set to 255. ; GDID: MOV B,A ; save ID info in B LDA MINI ; get mini flag ORA A MOV A,B ; restore ID info JZ GDID1 ; if 8" FDC ORI 20H ; set bit-5 for mini's GDID1: PUSH PSW ; save ID info on stack LXI H,DTL MVI M,80H ; initially set DTL to 128 ANI 03H STA N ; set N JZ SEC128 ; if 128 byte sectors MVI M,0FFH ; now set DTL to 255 SEC128: ; ; Now based on the N bits left in the ACC, the proper read/write ; gap (GPL) is loaded from the RGAPS table into the 765 RW table. ; MOV C,A MVI B,0 LXI H,RGAPS DAD B MOV A,M STA GPL ; set GPL ; ; If running under 8080, use the N bytes per sector code to produce ; the track terminal count (SIZE). ; LXI H,128 FNDSTC: DCR C JM SETSTC DAD H JMP FNDSTC SETSTC: DCX H ; adjust for terminal count SHLD TC ; ; Now the double-density status bit-6 is used to set the MF bit of ; the 765 read command. ; POP PSW PUSH PSW ; restore status bits ANI 040H ; mask double-density bit ORI RDCMD ; add in read command STA FDCOP ; store command ; ; Now based on the density bit and the N bits, find a table entry for this ; type of diskette and patched additional parameters; end of track (EOT), ; Number of reserved tracks (NOTRKS), The full track data length for programmed ; I/O. ; POP PSW ANI 7FH ; mask status bits MOV B,A LXI H,RLENS ; base of format table FNDLEN: MOV A,M ; get first table entry INX H CPI 0FFH ; end of table? RZ ; yes, go back to scanning CMP B ; match? JZ PATLEN ; yes, patch values INX H INX H INX H INX H ; point to next entry JMP FNDLEN ; and loop for more PATLEN: MOV A,M INX H STA EOT ; set EOT in RW table ; MOV A,M INX H STA NOTRKS ; set number of tracks ; MOV E,M INX H MOV D,M XCHG SHLD TRKTC ; store transfer code. ; ; Now initialize the address pointer used by the read track routine. ; LXI H,LDRORG SHLD ADDPNT ; ; One track (both sides in the case of double-sided media) is read ; per pass in this loop beginning with the current track in the RW table. ; RDLOOP: LXI H,DRIVE MOV A,M ANI 03H ; reset head select bit MOV M,A INX H INX H MVI M,0 ; set head field to 0 CALL RDTRK ; read the track RC ; if reading errors ; LXI H,NOTRKS ; point to track counter DCR M ; one less track JZ LDRE ; execute loader if zero ; CALL SDS ; get drive status ANI 08H ; test two-sided status JZ RDLP2 ; branch if one-sided ; LXI H,DRIVE MOV A,M ORI 04H ; set head 1 MOV M,A INX H INX H MVI M,1 ; set head field to 1 LDA MINI ; get mini flag ORA A JZ RDLP1 ; if not mini's LDA FDCOP ; get 765 read command ANI 40H ; mask density bit ORI RIDCMD ; add in read ID command MOV B,A CALL READID ; read ID from second side JNZ RDLP2 ; disk is single-sided if bad read ID LDA RWSTBL+4 ; get head field from returned ID CPI 1 ; is it truly head 1? JNZ RDLP2 ; no, disk must be single-sided RDLP1: CALL RDTRK ; read track on side one RC ; if reading errors ; LXI H,NOTRKS ; point to track counter DCR M ; one less track JZ LDRE ; execute loader if zero ; RDLP2: LXI H,TRACK INR M ; select next sequential track LXI B,SKCMD SHL 8 + 3 CALL CMDRDY ; step head there CALL DPOLL ; wait for completion JMP RDLOOP ; loop for more track reads ; ; ; One track but on either side is read in at ADDPNT address, then ADDPNT ; address is set to the next byte of RAM after the track data. This loop ; is set to perform RTRYS number retrys in reading the track data. ; Note that there are two loops for reading a track of data, one for Z-80's ; and using programmed I/O, and one loop for 8080's using the DMA sector ; buffer. ; RDTRK: MVI A,1 STA SECTOR ; initialize for sector one ; LDA I8080 ; get processor flag ORA A JNZ RDMA ; use DMA for 8080's ; MVI A,RTRYS ; get initial number of retrys RPIO.1: STA RTCNT ; set retry counter ; LDA FDCOP MOV B,A ; FDC command in B MVI C,9 ; length of a read command CALL CMDRDY ; issue FDC command ; LHLD TRKTC XCHG ; get transfer code in DE MOV B,E ; INIR count in B reg LHLD ADDPNT ; get address pointer ; LDA LATCH ; get latch value ORI PIOEN ; set programmed I/O bit STA LATCH ; set new latch value OUTP CONTRL ; send to FDC board ; CALL FBASE ; get FDC base address ADI DDATA MOV C,A ; 765 data port addr in C .Z80 ; for programmed I/O loop PGMRD: INIR ; mass input DEC D ; more INIR's to go? JR NZ,PGMRD ; yes if non-zero .8080 LDA LATCH ; get current latch ANI PIOEN XOR 0FFH ; reset programmed I/O bit STA LATCH ; update latch OUTP CONTRL ; send to controller board ; INP CONTRL ; get control status ANI PGMER ; test PGM error bit JZ RPIO.2 ; if not, wait for the interrupt ; CALL RESYNC ; re-syncronize with the uPD765 JMP PIO.ERR ; and branch to error routine ; RPIO.2: PUSH H CALL DPOLL ; wait for completion interrupt POP H LDA RWSTBL ; get ST-0 ANI 0C0H ; mask error bits JZ RPIO.E ; read track exit CPI 40H ; check for improper termination JNZ PIO.ERR ; true error if not this type ; LDA RWSTBL+1 ; get ST-1 ANI 80H ; test EOC JNZ RPIO.E ; yes, normal PGM error ; PIO.ERR: LDA RTCNT ; get retry counter DCR A ; cut it once JNZ RPIO.1 ; if more read trys to go STC RET ; with CY set for reading errors ; RPIO.E: SHLD ADDPNT ; store new address pointer XRA A RET ; with CY reset for success ; Read track DMA loop. ; Track data is read one sector at a time using the DMA sector buffer. ; RDMA: MVI A,RTRYS ; RDMA.1: STA RTCNT ; set retry counter ; LXI H,RWDMA ; point to DMA table MVI B,RWDMAL ; get length RDMA.2: MOV A,M INX H OUTP ZDMA ; program device DCR B JNZ RDMA.2 ; LDA FDCOP ; get 765 command MOV B,A MVI C,9 CALL CMDRDY ; issue command ; CALL DPOLL ; wait for device LDA RWSTBL ; get results ANI 0C0H ; mask error bits JZ RDMA.3 ; if good read ; LDA RTCNT ; get retry counter DCR A JNZ RDMA.1 ; loop if more retrys STC RET ; with CY set for reading errors ; RDMA.3: LDA LATCH ; get latch ORI SBENA ; set sector buffer enable STA LATCH OUTP CONTRL ; send to board ; LHLD TC INX H ; transfer length = TC + 1 MOV B,H MOV C,L LHLD ADDPNT ; data goes to current address XCHG LXI H,SECBUF ; data comes from sector buffer MOVEDATA: MOV A,M STAX D INX H INX D DCX B MOV A,B ORA C JNZ MOVEDATA ; XCHG SHLD ADDPNT ; store new current address ; LDA LATCH ; get latch ANI SBENA XOR 0FFH ; reset sector buffer enable STA LATCH OUTP CONTRL ; send to board ; LXI H,SECTOR INR M ; bump sector number LDA EOT ; get the end of track CMP M ; check for end JNC RDMA ; if more sectors to read XRA A RET ; with CY reset for track read SUBTTL Driver Subroutines PAGE ; Perform Sector Read ID command already contained in the B reg. ; READID: MVI C,2 CALL CMDRDY ; issue command CALL DPOLL ; wait for completion LDA RWSTBL ; get status ANI 0C0H ; mask error bits RET ; with zero set for success ; Perform sense drive status and return with results in the ACC. ; SDS: LXI B,SDSCMD SHL 8 + 2 CALL CMDRDY ; issue command CALL CMDRES ; read status LDA RWSTBL ; get status RET ; and return with it ; Send command to 765 subroutine. ; initial command in reg B, additional bytes are sent from the ; beginning of the READ/WRITE table as requested by the 765. ; register C contains the number of bytes that should be transfered. ; CMDRDY: LDA MINI ; get mini flag ORA A ; test it JZ CMDSND ; skip motor code if not mini ; PUSH B ; save command and length LXI B,SDSCMD SHL 8 + 2 CALL CMDSND ; sense drive status CALL CMDRES LXI H,RWSTBL ; point to ST-0 ; LDA LATCH ; turn the motor on ORI MOTOR ; set motor on bit OUTP CONTRL ANI MOTOR XOR 0FFH ; reset motor on bit OUTP CONTRL ; POP B ; restore callers command and length LDA RWSTBL ; get ST-0 ANI 20H ; test for ready line from drive status JNZ CMDSND ; and issue command if ready ; ; Now time out for one second to allow the drives to start-up LXI H,0 WT1SEC: XTHL XTHL DCX H MOV A,H ORA L JNZ WT1SEC ; CMDSND: INP FDCMSR ; get main status register ANI 10H ; mask FDC busy bit-4 JNZ CMDRDY ; loop if busy MOV A,B ; get 765 command byte CPI SCYCMD ; specify command? LXI H,RWTBL ; point to RW table JNZ CMDOUT ; no specify, use RW table LDA MINI ; get mini flag ORA A ; test it LXI H,SPEC8 ; point to 8" specify table JZ CMDS1 ; branch if 8" FDC LXI H,SPEC5 ; point to 5" specify table CMDS1: LDA I8080 ; get 8080 flag ORA A ; test it JNZ CMDOUT ; use DMA 765 mode if 8080 INX H ; point to second parameter MOV A,M ORI ND ; add in non-DMA bit MOV M,A DCX H CMDOUT: INP FDCMSR ; get main status register RAL ; shift RQM into CY JNC CMDOUT ; loop till ready RAL ; shift DIO into CY RC ; if 765 full MOV A,B ; get byte for output OUTP DDATA ; send it MOV B,M ; get next byte for output in B INX H ; bump RW table pointer DCR C ; count=count-1 JNZ CMDOUT ; loop if more to send RET ; done ; Receive NEC 765 result phase subroutine. ; The results of an operation are read out of the 765 as ; requested to be read by the DIO bit-6. The results are loaded ; into the RW status table. ; CMDRES: LXI H,RWSTBL ; set result table pointer CMDRS1: INP FDCMSR ; get main status register RAL ; shift RQM into CY JNC CMDRS1 ; loop till ready RAL ; shift DIO into CY RNC ; if done receiving INP DDATA ; get result byte MOV M,A ; store data in table INX H ; bump table pointer JMP CMDRS1 ; and loop for more ; Disk polling subroutine. This is called when waiting on the 765 ; to perform an operation in which it will interrupt when completed. ; DPOLL: INP CONTRL ; get control status ANI INTRQ ; test interrupt line JZ DPOLL ; and loop until received ; FLINT: INP FDCMSR ; read 765 status ANI 10H ; busy? (read or write in process) JNZ RWDN.1 ; yes, get results LXI B,SISCMD*256+2 CALL CMDSND ; issue sense interrupt command RWDN.1: CALL CMDRES ; get results LDA RWSTBL ANI 0C0H ; mask error bits CPI 0C0H ; drive ready change? JNZ RWDN.E ; no, return status INP FDCMSR ANI 0FH ; any drive seeking? JNZ DPOLL ; yes, wait for it RWDN.E: ; ; If using the DMA operation mode, the Z80 DMA device is disabled ; and any pending DMA interrupt is reset. ; MVI A,083H OUTP ZDMA ; disable DMA device MVI A,0A3H OUTP ZDMA ; reset INT RET ; done ; Clear the uPD765 status port. ; This routine is called after an error in a programmed I/O transfer ; with the uPD765. The Main status register is used to clear the device ; of any command or result transfer and re-syncronize with the device. ; RESYNC: INP FDCMSR ; get main status register RAL ; shift RQM into CY JNC RESYNC ; loop till ready ; INP FDCMSR ANI 10H ; test controller busy bit RZ ; and quit if busy bit reset INP FDCMSR ANI 40H ; now test DIO for direction JNZ SYNC1 ; read data port if bit is set XRA A OUTP DDATA ; write null data if bit is reset JMP RESYNC ; and retest main status SYNC1: INP DDATA ; read some data JMP RESYNC ; and retest main status ; FDC initialization routine. ; FDCINIT: ; ; The presence of the controller is tested first by reading ; the 765 status port and looking for an 80H from the upper nibble. ; INP FDCMSR ; read main status register ANI 0F0H ; mask out lower nibble CPI 80H ; check for 765 STC ; return with CY set RNZ ; if FDC is not present ; ; The uPD765 is checked to see if drive 0 is recalibrating after a power-up ; sequence and if so, wait for the interrupt and read the results. ; INP FDCMSR ; get main status register ANI 00FH ; test for a seek CNZ DPOLL ; and wait for it ; ; If running under an 8080 processor, initialize the DMA device and ; read back one register to check for it's presence. ; LDA I8080 ORA A JZ FDCI1 ; if not an 8080 ; CALL FBASE ; get controller base ADI 6 ; compute DMA port B LSB STA PBLSB ; store in DMA table ; LXI H,IZDMA MVI B,IZDMAL INIDMA: MOV A,M INX H OUTP ZDMA DCR B JNZ INIDMA ; INP ZDMA ; read port B MSB ORA A ; it must be zero STC ; return with CY set RNZ ; if DMA device in not present ; FDCI1: ; ; The initial specify command for drive parameters is sent to the 765. ; LXI B,SCYCMD SHL 8 + 3 CALL CMDSND ; issue specify command XRA A RET ; with CY reset for success ; Relative port input and output routines ; INPUT: XTHL ; get back first on stack CALL FBASE ADD M ; add in relative port STA PTI1+1 ; store port after input instruction INX H XTHL ; swap stack back PTI1: IN 0 ; do the input RET ; and return with it OUTPUT: XTHL ; get first on stack PUSH PSW ; save output data CALL FBASE ADD M ; add in relative port STA PTO1+1 ; store port after output instruction INX H POP PSW ; restore saved output data XTHL ; swap back stack PTO1: OUT 0 ; do the output RET ; done ; Find the base address of the controller depending on the ; mini flag. ; FBASE: LDA MINI ORA A ; test mini flag MVI A,DSKB8 ; get 8" FDC base first RZ ; if 8" FDC MVI A,DSKB5 ; sub in 5" FDC base RET SUBTTL Variables, Tables, and Buffers PAGE ; Read/Write gap values (GPL) for sectors sizes from 128 to 1024. ; RGAPS: DB 07H,0EH,1BH,35H ; read gaps ; ; Diskette lengths and parameters table. Each entry 5 bytes in legth ; consisting the format type byte, the EOT value for the RW table and ; the number of reserved tracks (or sides) of the diskette, and the ; transfer code equivilent to the number of data bytes on a track. ; RLENS: ; 8" single-density DB 00,26,2 ; 128 X 26, 2 tracks DW 0D00H ; 8" double-density DB 43H,8,1 ; 1024 X 8, 1 track DW 2000H ; 5" single-density DB 20H,16,4 ; 128 X 16, 4 tracks DW 800H ; 5" double-density DB 63H,5,2 ; 1024 X 5, 2 tracks DW 1400H ; DB 0FFH ; end of table marker ; ; ADDPNT: DW 0 ; current DMA address TRKTC: DW 0 ; Track terminal count FDCOP: DB 0 ; FDC operation NOTRKS: DB 0 ; number of tracks to read RTCNT: DB 0 ; retry counter I8080: DB 0 ; 8080 processor flag MINI: DB 0 ; mini flag LATCH: DB 009H ; current latch setting ; ; 765 read/write table RWTBL: DRIVE: DB 0 ; drive number TRACK: DB 0 ; track number HEAD: DB 0 ; head number SECTOR: DB 0 ; sector number N: DB 0 ; bytes/sector code EOT: DB 0 ; end of track GPL: DB 0 ; gap length DTL: DB 0 ; data length ; RWSTBL: DB 0,0,0,0,0,0,0 ; 765 status table ; ; 765 specify tables for both 8" and 5" FDC's SPEC8: DB (16-SRT) SHL 4 + (HUT/16) DB HLT/2 SHL 1 SPEC5: DB (16-MSRT/2) SHL 4 + (MHUT/32) DB MHLT/4 SHL 1 ; ; Z80 DMA table: Sent to device for initialization or re-initialization IZDMA: DB 083H ; disable DMA DB 0C3H,0C3H,0C3H DB 0C3H,0C3H,0C3H ; reset device. DB 0A3H ; reset INT DB 014H ; port A=memory, port A increments. DB 028H ; port B=I/O, port B fixed. DB 08AH ; stop end of block, ready active high DB 0D5H ; burst mode, ICB and port B LSB follows PBLSB: DB 0 ; port B LSB DB 002H ; INT at end of block DB 001H ; Port B equals temp source DB 0CFH ; load Port B fixed address ; The next bytes set the DMA device up to read Port B MSB DB 0BBH ; read mask follows DB 040H ; return port B MSB IZDMAL EQU $-IZDMA ; length of transfer ; ; Z80 DMA table: Sent to device for reading and writing. RWDMA: DB 083H ; disable DMA WR0: DB 079H ; Port A address and block length follows ADDR: DW SECBUF ; patched port A address TC: DW 0 ; patched terminal count (length) DB 0CFH ; load registers DB 0ABH ; enable INT DB 087H ; enable DMA RWDMAL EQU $-RWDMA ; length of transfer IF HDSYS INCLUDE HDBOOT.MAC ; start hard disk boot code here ENDIF IPLLEN EQU $-START .DEPHASE END