; V1.10 ; ; To compile use: ; ; AS80 [1.31] - Assembler for 8080/8085/Z80 microprocessor. ; ; Available from: ; - http://www.falstaff.demon.co.uk/cross.html ; - ftp://ftp.simtel.net/pub/simtelnet/msdos/crossasm/as80_131.zip ; - and many Simtel mirrors. ; ; as80 -i -l -n -x2 -v -z mz-1e05.asm ; ;----< MFM Minifloppy control >---- ; ; ; Call condition ; ; Case of disk initialize ; Drive N = IX+0 (0 - 3) ; ; ; Case of sequential read & write ; Drive N = IX+0 (0 - 3) ; ; Sector addrs = IX+1,2 (0 - $045F) H C S ; (0 - 1119) -> 70 x 16 sectors -> 2 x 35 x 16 ; Byte size = IX+3,4 ; Address = IX+5,6 ; Next track = IX+7 ; Next sector = IX+8 ; Start track = IX+9 ; Start sector = IX+10 ; ; ; I/O Port address ; CR EQU $D8 ; CommandRegister TR EQU $D9 ; TrackRegister SCR EQU $DA ; SeCtorRegister DR EQU $DB ; DataRegister DM EQU $DC ; DriveMotor HS EQU $DD ; HeadSelect TIMST EQU $0033 ; ; Subroutine work ; BPRO EQU $CF00 BUF EQU $11A3 BPARA EQU BPRO - 23 ; BootPARAmeter CMD EQU BPARA + 11 ; CoMmanD MTFG EQU CMD + 1 ; MoTorFlaG CLBF0 EQU MTFG + 1 CLBF1 EQU CLBF0 + 1 CLBF2 EQU CLBF1 + 1 CLBF3 EQU CLBF2 + 1 VRFCNT EQU CLBF3 + 1 ; VeRiFyCouNT STAFG EQU VRFCNT + 1 ; STAtusFlaG ; ; ;--------< Ercode map >-------- ; ; 50 : Not ready ; 41 : Data error ; Track 80 err ; Write protect err ; Seek err ; CRC err ; Lost data ; 54 : Unformat ; Recode not found ; 56 : Invalid data ; ; ORG $F000 MZ_1E05: NOP LD HL,$00AD JR L_F007 FDX: EX (SP),HL L_F007: LD (BPARA + 21),HL XOR A LD DE,0 CALL TIMST CALL FDCC ; FD i/o check JP NZ,NOTIO LD DE,BPARA ; destination address LD HL,BOOT ; source address LD BC,11 ; 11 bytes LDIR ; copy SJP: LD IX,BPARA CALL BREAD ; read from drive 0, sector 0, ; LD HL,BPRO ; compare this address LD DE,IPLMC ; with the IPL MasterCode LD B,7 ; this are 7 bytes : 3,'IPLPRO' MCHECK: LD C,(HL) LD A,(DE) CP C JP NZ,MASTE ; not equal than MasterError INC HL INC DE DJNZ MCHECK ; else Master was found LD DE,IPLM0 ; 'IPL IS LOADING' RST $18 LD DE,BPRO + 7 ; NAME RST $18 LD HL,(BPRO + $16) ; TARGETADDRESS from BootBlock LD A,H OR L JR NZ,L_F051 ; if it is != 0 than normal file LD HL,(BPRO + $18) ; TARGETADDRESS from BootBlock LD A,H OR L JR Z,L_F057 ; if it is also 0 than ROM replace file L_F051: XOR A ; else normal file, LD HL,(BPRO + $18) ; TARGETADDRESS from BootBlock JR L_F05C L_F057: LD A,$FF ; target is at $0000, bankswitching is needed LD HL,$1200 ; for now use temporary buffer at $1200 L_F05C: LD ($CEFD),A LD (IX + 5),L ; set the TargetAddress LD (IX + 6),H LD HL,(BPRO + $14) ; BYTE SIZE from BootBlock LD (IX + 3),L LD (IX + 4),H LD HL,(BPRO + $1E) ; START SECTOR from BootBlock LD (IX + 1),L LD (IX + 2),H ; CALL BREAD CALL MOFF LD A,($CEFD) CP $FF JR NZ,L_F093 OUT ($E0),A LD HL,$1200 ; SourceAddress LD DE,(BPRO + $16) ; TargetAddress LD BC,(BPRO + $14) ; ByteCounter LDIR ; copy L_F093: LD BC,$0200 ; Default code LD HL,(BPRO + $18) ; TARGET/EXECUTION ADDRESS from BootBlock JP (HL) MASTE: CALL MOFF LD DE,ERRM1 ; 'NOT MASTER' JR ERRTR1 ERRTRT: CP 50 NOTIO: LD DE,IPLM3 ; 'MAKE READY FD' JR Z,ERRTR1 LD DE,ERRM0 ; 'FD:LOADING ERROR' ERRTR1: CALL $0009 RST $18 LD SP,$10EE LD HL,(BPARA + 21) EX (SP),HL RET ; ; ; PARAMETER SETTING ; IPLMC: DB $03 ; IPL MASTER FLAG DB 'IPLPRO' BOOT: DB $00 ; DRIVE NO. DW $0000 ; SECTOR ADDR. DW $0100 ; IFM BYTE SIZE DW BPRO ; IFM LOADING ADDR. DW $0000 ; IX+7,8 (track 0, sector 0) ERRM1: DB 'FD:NOT MASTER',$0D IPLM0: DB 'IPL IS LOADING ',$0D IPLM3: DB 'MAKE READY FD',$0D ERRM0: DB 'FD:LOADING ERROR',$0D FDCC: LD A,$A5 LD B,A OUT (TR),A CALL DLY80U IN A,(TR) CP B RET L_F111: DB $00, $00 ; ; ; READY CHECK ; READY: LD A,(MTFG) RRCA CALL NC,MTON LD A,(IX + 0) ; DRIVE NO SET OR $84 OUT (DM),A ; DRIVE SELECT MOTON XOR A LD (CMD),A CALL DLY60M LD HL,0 REDY0: DEC HL LD A,H OR L JR Z,REDY1 IN A,(CR) ; STATUS GET CPL RLCA JR C,REDY0 LD C,(IX + 0) LD HL,CLBF0 LD B,$00 ADD HL,BC BIT 0,(HL) JR NZ,REDY2 CALL RCLB SET 0,(HL) REDY2: RET REDY1: LD A,$32 JP ERJMP ; ; ; MOTOR ON ; MTON: LD A,$80 OUT (DM),A LD B,16 MTD1: CALL DLY60M DJNZ MTD1 LD A,1 LD (MTFG),A RET ; ; ; SEEK TREATMENT ; SEEK: LD A,$1B ; 1x = SEEK, CALL CMDOT1 ; load head, no verify, max stepping rate AND $99 RET ; ; ; MOTOR OFF ; MOFF: PUSH AF CALL DLY1M ; 1000 US DELAY XOR A OUT (DM),A LD (CLBF0),A LD (CLBF1),A LD (CLBF2),A LD (CLBF3),A LD (MTFG),A POP AF RET ; ; ; RECALIBRATION ; RCLB: LD A,$0B ; 0x = RESTORE (seek track 0) CALL CMDOT1 ; load head, no verify, max stepping rate AND $85 XOR $04 RET Z L_F189: JP ERROR ; ; ; COMMAND OUT ROUTINE ; CMDOT1: LD (CMD),A CPL OUT (CR),A CALL BSYON CALL DLY60M IN A,(CR) CPL LD (STAFG),A RET ; ; ; BUSY AND WAIT ; BSYON: PUSH DE PUSH HL CALL BSY0 BSYON2: LD HL,$0000 BSYON0: DEC HL LD A,H OR L JR Z,BSYON1 IN A,(CR) RRCA JR NC,BSYON0 POP HL POP DE RET ; BSYON1: DEC E JR NZ,BSYON2 BSYONE: LD A,$29 POP HL POP DE JP ERJMP ; BSYOFF: PUSH DE PUSH HL CALL BSY0 BSYOF2: LD HL,$0000 BSYOF0: DEC HL LD A,H OR L JR Z,BSYOF1 IN A,(CR) ; Status Register RRCA JR C,BSYOF0 POP HL POP DE RET ; BSYOF1: DEC E JR NZ,BSYOF2 JR BSYONE ; BSY0: CALL DLY80U LD E,$07 RET ; ; ; SEQUENTIAL READ ; BREAD: CALL CNVRT CALL PARST1 ; HL = IX + 5,6 (TargetAddress) RE8: CALL SIDST CALL SEEK JP NZ,ERJMP CALL PARST2 ; C = DataRegister DI ; disable interrupts LD A,$94 ; 9x = READ SECTOR, multiple records CALL CMDOT2 ; compare for side 0, 15ms delay, RE6: ; disable side select compare LD B,0 ; ByteCounter = 0, to load 256 bytes of the sector RE4: IN A,(CR) RRCA JR C,RE3 RRCA JR C,RE4 INI ; (HL) = in(C), B = B - 1 , HL = HL + 1 JR NZ,RE4 INC (IX + 8) ; NextSector = NextSector + 1 LD A,(IX + 8) CP $11 ; if NextSector = 17 JR Z,L_F213 ; than end DEC D ; else SectorCounter = SectorCounter - 1 JR NZ,RE6 ; if SectorCounter = 0 JR L_F214 ; than end L_F213: DEC D L_F214: CALL INTER RE3: EI ; enable interrupts IN A,(CR) CPL LD (STAFG),A AND $FF JR NZ,ERROR CALL ADJ ; adjust sector and track JP Z,REND LD A,(IX + 7) ; track JR RE8 REND: LD A,$80 OUT (DM),A ; motor on RET ; ; ; PARAMETER SET ; ; PARST1: CALL READY LD D,(IX + 4) ; D = bytes to read (highbyte) (256 bytes) LD A,(IX + 3) ; A = bytes to read (lowbyte) OR A ; if A = 0 JR Z,L_F23F ; than it's ok INC D ; else read 256 bytes more (1 sector) L_F23F: LD A,(IX + 10) ; NextSector = StartSector LD (IX + 8),A LD A,(IX + 9) ; NextTrack = StartTrack LD (IX + 7),A LD L,(IX + 5) ; HL = TargetAddress LD H,(IX + 6) RET ; ; ; SIZE SEEK SET ; SIDST: SRL A CPL OUT (DR),A JR NC,L_F25D ; NC than Head 0 LD A,1 ; else Head 1 JR L_F25E L_F25D: XOR A L_F25E: CPL OUT (HS),A ; set HeadSelect RET ; ; ; TRACK & SECTOR SET ; PARST2: LD C,DR LD A,(IX + 7) ; A = NextTrack SRL A CPL OUT (TR),A LD A,(IX + 8) ; A = NextSector CPL OUT (SCR),A RET ; ; ; ADJUST SECT & TRACK ; ADJ: LD A,(IX + 8) ; A = NextSector CP 17 ; if NextSector = 17 JR NZ,L_F282 ; than the border is not reached LD A,$01 ; else LD (IX + 8),A ; NextSector = 1 INC (IX + 7) ; NextTrack = NextTrack + 1 L_F282: LD A,D OR A RET ; ; ; COMMAND OUT & WAIT ; CMDOT2: LD (CMD),A CPL OUT (CR),A CALL BSYOFF RET ; ; ; FORCE INTERRUPT ; INTER: LD A,$D8 CPL OUT (CR),A CALL BSYON RET ; ; ; STATUS CHECK ; ERROR: LD A,(CMD) CP $0B ; Restore (seek track 0) JR Z,ERCK1 CP $1B ; Seek JR Z,ERCK1 CP $F4 ; Write track JR Z,ERCK1 LD A,(STAFG) BIT 7,A JR NZ,ERRET BIT 6,A JR NZ,ERRET1 BIT 4,A LD A,54 JR NZ,ERJMP JR ERRET1 ERCK1: LD A,(STAFG) BIT 7,A JR NZ,ERRET ERRET1: LD A,41 JR ERJMP ERRET: LD A,50 ERJMP: CALL MOFF JP ERRTRT ; ; ; SECTOR TO TRACK & SECTOR CONVERT ; CNVRT: LD B,0 ; TrackCounter = 0 LD DE,16 ; 16 sectors per track LD L,(IX + 1) ; HL = SectorAddress LD H,(IX + 2) XOR A TRANS0: SBC HL,DE ; SectorAddress - SectorPerTrack JR C,TRANS1 ; if < 0 than ready INC B ; else TrackCounter = TrackCounter + 1 JR TRANS0 ; next try TRANS1: ADD HL,DE ; undo the last substraction LD H,B INC L ; adjust sector (sector is 1..16 and not 0..15) LD (IX + 9),H ; set StartTrack LD (IX + 10),L ; set StartSector RET ; ; ; TIME DELAY ( 1m & 60m & 80u ) ; DLY80U: PUSH DE LD DE,15 JP DLYT DLY1M: PUSH DE LD DE,160 JP DLYT DLY60M: PUSH DE LD DE,8230 DLYT: DEC DE LD A,E OR D JR NZ,DLYT POP DE RET ORG $FFF0 DB ' 84.03.14 V1.0A'