From: Will Sowerbutts Date: Wed, 21 Jan 2015 13:31:49 +0000 (+0000) Subject: Kernel: p112 floppy disk driver and floppy disk boot support X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=75f284352db2d346745f611c20b88a0e1fe04652;p=FUZIX.git Kernel: p112 floppy disk driver and floppy disk boot support The driver for the SMC floppy disk controller is a straight port of the driver from UZI-180. It uses neither DMA nor interrupts. The p112 platform can now be booted from floppy disk. The current boot sector is somewhat rudimentary and requires the floppy disk to be dedicated to the kernel (it cannot also hold a Fuzix filesystem). --- diff --git a/Kernel/cpu-z180/z180.def b/Kernel/cpu-z180/z180.def index 27e9270a..4b0145c0 100644 --- a/Kernel/cpu-z180/z180.def +++ b/Kernel/cpu-z180/z180.def @@ -71,7 +71,9 @@ PORT_C_DDR .equ 0xDD ; Port C data direction PORT_C_DATA .equ 0xDE ; Port C data register Z182_SYSCONFIG .equ 0xEF ; System Configuration Register -Z182_ROMBR .equ 0xE8 ; ROMBR register +Z182_RAMUBR .equ 0xE6 ; RAM upper boundary register +Z182_RAMLBR .equ 0xE7 ; RAM lower boundary register +Z182_ROMBR .equ 0xE8 ; ROM boundary register ; Debugging DEBUGBANK .equ 0 diff --git a/Kernel/platform-p112/Makefile b/Kernel/platform-p112/Makefile index 03e76567..f469c717 100644 --- a/Kernel/platform-p112/Makefile +++ b/Kernel/platform-p112/Makefile @@ -1,6 +1,6 @@ -CSRCS += devices.c main.c devtty.c +CSRCS += devices.c main.c devtty.c devfd.c DSRCS = ../dev/blkdev.c ../dev/devide.c ../dev/mbr.c ../dev/ds1302.c -ASRCS = crt0.s z180.s commonmem.s p112.s ds1302-p112.s monitor.s +ASRCS = crt0.s z180.s commonmem.s p112.s ds1302-p112.s devfd2.s monitor.s flopboot.s AOBJS = $(ASRCS:.s=.rel) COBJS = $(CSRCS:.c=.rel) @@ -12,7 +12,7 @@ CROSS_CCOPTS += -I../dev/ JUNK = *.rel *.lst *.asm *.sym *.rst *.map *.ihx *.bin -all: $(OBJS) +all: $(OBJS) flopboot.bin $(AOBJS): %.rel: %.s $(CROSS_AS) $(ASOPTS) $< @@ -24,9 +24,15 @@ $(DOBJS): %.rel: ../dev/%.c $(CROSS_CC) $(CROSS_CCOPTS) -c $< clean: - rm -f $(OBJS) $(JUNK) core *~ fuzix.com + rm -f $(OBJS) $(JUNK) core *~ fuzix.com fuzix-boot.fdd z180.rel: z180.s kernel.def ../cpu-z180/z180.s +flopboot.bin: flopboot.s + $(CROSS_AS) $(ASOPTS) flopboot.s + sdldz80 -nmi flopboot.rel + makebin -s 65536 flopboot.ihx > flopboot.bin + image: ../cpm-loader/makecpmloader ../cpm-loader/cpmload.bin ../fuzix.bin 0x88 fuzix.com + ./flopboot-mkimage flopboot.bin ../fuzix.bin fuzix-boot.fdd diff --git a/Kernel/platform-p112/README b/Kernel/platform-p112/README index e38cf047..79614479 100644 --- a/Kernel/platform-p112/README +++ b/Kernel/platform-p112/README @@ -3,9 +3,35 @@ By Will Sowerbutts Assumes a P112 fitted with 1024KB RAM and G-IDE -Only supported storage device at this time is the G-IDE +Supported hardware: + - Real time clock + - SMC floppy disk drive controller + - G-IDE hard disk drive controller + - First serial port (ESCC channel A, tty0) -To build, edit ../Makefile to read: +To build the kernel, edit the CPU and TARGET lines in Kernel/Makefile to read: + export TARGET=p112 + export CPU=z180 +Then run "make clean; make all" in the "Kernel" directory. -export TARGET= p112 -export CPU = z180 +There are two ways to boot the system. + +The file "Kernel/platform-p112/fuzix.com" is a CP/M executable which will load +and boot the Fuzix kernel from within CP/M on the P112. This works well, +however the kernel is now starting to get quite large and may be too large for +CP/M to load. + +The file "Kernel/platform-p112/fuzix-boot.fdd" can be used to make a bootable +floppy disk. Write the file to the first few tracks of a floppy disk, then put +it in the first floppy drive and tell the P112 ROM to boot from floppy (for +example by typing "Z1" in the P112 ROM monitor). This will load and boot the +kernel, and does not suffer from the same kernel size restrictions as the CP/M +loading method. To write the fuzix-boot.fdd file to a floppy under Linux you +can use dd as follows; + dd if=fuzix-boot.fdd bs=512 of=/dev/fd0 + +The resulting floppy disk can currently be used only for the kernel, a separate +floppy disk or IDE hard disk must be used to hold the root file system. In the +future it should be possible to port the UZI-180 "insboot" program so that the +floppy boot sector can read the kernel directly out of a Fuzix file system on +the floppy disk. diff --git a/Kernel/platform-p112/config.h b/Kernel/platform-p112/config.h index f664412e..72aa2b56 100644 --- a/Kernel/platform-p112/config.h +++ b/Kernel/platform-p112/config.h @@ -60,3 +60,6 @@ /* We have a DS1302, we can read the time of day from it */ #define CONFIG_RTC #define CONFIG_RTC_INTERVAL 30 /* deciseconds between reading RTC seconds counter */ + +/* We have the P112 floppy controller */ +#define CONFIG_P112_FLOPPY diff --git a/Kernel/platform-p112/devfd.c b/Kernel/platform-p112/devfd.c new file mode 100644 index 00000000..2d376ccc --- /dev/null +++ b/Kernel/platform-p112/devfd.c @@ -0,0 +1,141 @@ +/*************************************************************** + UZI (Unix Z80 Implementation) Kernel: devflop.c +---------------------------------------------------------------- + Adapted from UZI By Doug Braun, and UZI280 by Stefan Nitschke + Copyright (C) 1998 by Harold F. Bower + Portions Copyright (C) 1995 by Stefan Nitschke +****************************************************************/ +/* 2015-01-17 Will Sowerbutts: Ported from UZI-180 to Fuzix */ + +#include +#include +#include +#include "devfd.h" + +/* functions implemented in devfd2.s */ +extern int devfd_init(uint8_t minor); +extern int devfd_read(uint8_t minor); +extern int devfd_write(uint8_t minor); + +/* variables in devfd2.s */ +extern uint8_t devfd_track, devfd_sector, devfd_error; +extern char *devfd_buffer; + +extern struct { + uint8_t logged; + uint8_t cbyte0; + uint8_t cbyte1; + uint8_t gap3; + uint8_t spt; + uint8_t sector1; + uint8_t format; + uint8_t spinup; + uint8_t curtrk; + uint8_t ncyl; +} devfd_dtbl[4]; + +static int fd_transfer(bool rwflag, uint8_t minor, uint8_t rawflag) +{ + uint8_t nblocks, blocks; + uint16_t firstblk; + uint16_t retc; + irqflags_t irq; + + switch(rawflag){ + case 0: + nblocks = 1; + devfd_buffer = udata.u_buf->bf_data; + firstblk = udata.u_buf->bf_blk; + break; + case 1: + nblocks = udata.u_count >> 9; + devfd_buffer = udata.u_base; + firstblk = udata.u_offset >> BLKSHIFT; + break; +#ifdef SWAPDEV + case 2: + nblocks = swapcnt >> 9; + devfd_buffer = swapbase; + firstblk = swapblk; + break; +#endif + default: + goto failout; + } + + devfd_track = firstblk / devfd_dtbl[minor].spt; + devfd_sector = firstblk % devfd_dtbl[minor].spt; /* Base 0 Sect # */ + devfd_error = 0; + + if (devfd_track >= devfd_dtbl[minor].ncyl){ + goto failout; + } + + blocks = nblocks; + for (;;) + { + irq = di(); + if (rwflag) + retc = devfd_read(minor); + else + retc = devfd_write(minor); + irqrestore(irq); + + if (retc) + break; + + if(--nblocks == 0) + break; + + if (++devfd_sector > devfd_dtbl[minor].spt) + { + devfd_sector = 0; + ++devfd_track; + } + devfd_buffer += 128 << (devfd_dtbl[minor].format & 3); + } + + if (devfd_error) { + kprintf("fd_%s: error %d track %d sector %d\n", + rwflag ? "read" : "write", devfd_error, devfd_track, devfd_sector); + panic("fd_transfer"); + } + + if (retc) + goto failout; + + return blocks; +failout: + udata.u_error = ENXIO; + return -1; +} + +int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag; /* unused */ + return fd_transfer(true, minor, rawflag); +} + +int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag; /* unused */ + return fd_transfer(false, minor, rawflag); +} + +int fd_open(uint8_t minor, uint16_t flags) +{ + flags; /* unused */ + + if (devfd_init(minor)) { + udata.u_error = ENXIO; + return -1; + } + + return 0; +} + +int fd_close(uint8_t minor) +{ + devfd_dtbl[minor].logged = 0; /* Mark Drive as logged out */ + return 0; +} diff --git a/Kernel/platform-p112/devfd.h b/Kernel/platform-p112/devfd.h new file mode 100644 index 00000000..2019c576 --- /dev/null +++ b/Kernel/platform-p112/devfd.h @@ -0,0 +1,11 @@ +#ifndef __DEVFD_DOT_H__ +#define __DEVFD_DOT_H__ + +/* public interface */ +int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag); +int fd_open(uint8_t minor, uint16_t flags); +int fd_close(uint8_t minor); +void fd_tick(void); + +#endif diff --git a/Kernel/platform-p112/devfd2.s b/Kernel/platform-p112/devfd2.s new file mode 100644 index 00000000..87fd8857 --- /dev/null +++ b/Kernel/platform-p112/devfd2.s @@ -0,0 +1,686 @@ +;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +; D-X Designs Pty Ltd P112 Floppy disk Routines +; Copyright (C) 1998 by Harold F. Bower +;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +; 2015-01-17 Will Sowerbutts: Ported to sdas/Fuzix from UZI-180 + + .module devfd2 + .z180 + + ; imported symbols + .globl _devfd_dtbl + + ; exported sybols + .globl _devfd_init + .globl _devfd_read + .globl _devfd_write + .globl _devfd_track + .globl _devfd_sector + .globl _devfd_error + .globl _devfd_buffer + .globl _fd_tick + + .include "kernel.def" + .include "../kernel.def" + +;------------------------------------------------------------------------------ + .area _CODE + +; 092 - Drive Control Register (Write Only) +; 7 6 5 4 3 2 1 0 +; | | | | | | +-+-- Drive (00=0, 01=1, 10=2, 11=3) +; | | | | | +------ 1 = Normal Opn, 0 = Reset Controller +; | | | | +-------- 1 = Enable DMA Pins, 0 = Disable DRQ,DAK,INT pins +; | | | +---------- 1 = Enable Drive 0 Motor +; | | +------------ 1 = Enable Drive 1 Motor +; | +-------------- 1 = Enable Drive 2 Motor +; +---------------- 1 = Enable Drive 3 Motor +; 093 - (Not Used) +; 094 - Data-Rate Select (Write) / Main Status Register (Read) +; 7 6 5 4 3 2 1 0 (Write) + +; 7 6 5 4 3 2 1 0 (Read) +; | | | | +-+-+-+-- Drives Seeking (0=B0 Set, 1=B1 Set,.. 3=B3 Set) +; | | | +---------- 1 = Command In Progress, 0 = Command Ended +; | | +------------ 1 = Non-DMA Execution, 0 = DMA Execution +; | +-------------- 1 = Read, 0 = Write +; +---------------- 1 = Request for Master, 0 = Internal Execution +; +; 095 - Data/Command Register (Read/Write) +; (Byte Writes/Reads) +; 096 - (Not Used) +; 097 - Data Rate Register (Write) / Disk Changed Bit (Read) +; 7 6 5 4 3 2 1 0 (Write) +; | | | | | | +-+-- 00=500 kb/s, RPM/LC Hi, 01=250/300 kb/s (RPM/LC Lo) +; | | | | | | 10=250 kb/s, RPM/LC Lo, 11=1000 kb/s (RPM/LC Hi/Lo) +; +-+-+-+-+-+------ (Not Used) +; +; 7 6 5 4 3 2 1 0 (Read) +; | +-+-+-+-+-+-+-- (Tri-State, used for HD Controller) +; +---------------- 1 = Disk Changed (latched complement of DSKCHG inp) +; +; 0A0 - DMA I/O Select Port (DMA configuration Only) +;------------------------------------------------------------- +FDCBAS .equ 0x90 ; SMC 37C665 Controller Base Address +DCR .equ FDCBAS+2 ; Drive Control Register +MSR .equ FDCBAS+4 ; Main Status Register +DR .equ FDCBAS+5 ; Data Register +DRR .equ FDCBAS+7 ; Data Rate Register/Disk Changed Bit in B7 + +MONTIM .equ 250 ; Motor On time (Seconds * TICKSPERSEC) + +; Offsets in Drive Data Table (defined at end of this module) +oFLG .equ 0 ; 0 = Not Logged, 1 = Drive Logged +oPRM1 .equ 1 ; Step Rate (B7-4), HUT (3-0) +oPRM2 .equ 2 ; Hd Load in 4mS steps (0=infinite) +oGAP3 .equ 3 ; Gap 3 Length for Read +oSPT .equ 4 ; Sectors-per-Track +oSEC1 .equ 5 ; First Sector Number +oFMT .equ 6 ; Bit-mapped Format byte +oSPIN .equ 7 ; Spinup delay (1/20-secs) +oTRK .equ 8 ; Current Head Position (Track) + +;------------------------------------------------------------- +; Determine if the controller exists and a drive is attached +; fdInit (int minor); +; Enter: Drive Minor # is on Stack +; Exit : HL = 0 if All is Ok, Non-Zero if Error + +_devfd_init: + XOR A + LD (motim),A ; Mark Motors as initially OFF + LD (hd),A ; and initially Head #0 + + POP HL ; Return Addr + POP BC ; minor (in C) + PUSH BC ; Keep on Stack for Exit + PUSH HL + LD A,C + LD (drive),A ; Save Desired Device + CP #4 ; Legal? + JR NC,NoDrv ; ..Exit if Error + CALL ActivA ; Else force Reset (B2=0) + LD B,#0 +indel1: DJNZ indel1 ; (settle) + CALL Activ8 ; then bring out of Reset +indel2: DJNZ indel2 ; (settle, B already =0) + IN A,(MSR) + CP #0x80 ; Do we have correct Ready Status? + JR NZ,NoDrv ; ..exit Error if Not + + LD A,(drive) + CALL GetPrm ; Pt to this drive's table entry + PUSH HL + POP IY + LD oFLG(IY), #0 ; Ensure drive is Unlogged + CALL Spec ; Set Controller Params + JR C,NoDrv ; ..Error if TimeOut, Else.. + CALL Recal ; Recalibrate (home) Drive + JR NZ,NoDrv ; ..Error if it failed + LD oFLG(IY), #1 ; Mark drive as active + LD HL,#0 ; Load Ok Status + RET + +NoDrv: LD HL,#0xFFFF ; Set Error Status + RET + +;------------------------------------------------------------- +; This routine Reads/Writes data from buffer trying up to 4 times +; before giving up. If an error occurs after the next-to-last +; try, the heads are homed to force a re-seek. +; +; Enter: Drive Minor # is on Stack. Entry Point sets Read/Write Flag +; Exit : A = 0, Zero Set if Ok, A <> 0, Zero Reset if Errors +; (also returns H = 0 and L set to A for compatibilty with C code) +; Uses : AF,HL + +_devfd_read: + LD A,#1 + .db 0x21 ;..Trash HL, fall thru.. +_devfd_write: + LD A,#0 ; has to be two bytes -- do not optimise to xor a! + LD (rdOp),A + + POP HL ; Return Addr + POP BC ; minor (->C) + PUSH BC ; Keep on Stack for Exit + PUSH HL + LD A,C + LD (drive),A ; Save Desired Device +;; CP 4 ; Legal? +;; JR NC,NoDrv ; ..Exit if Error + + CALL Setup ; Set up subsystem +;;-- LD HL,buffer ; Point to the host buffer +;;-- LD (actDma),HL ; and set Memory Pointer + + LD A,#4 ; Get the maximum retry count +Rwf1: LD (rwRtry),A + LD D,#0xFF ; (Verify needed) + CALL SEEK ; Try to seek to the desired track + JR NZ,Rwf2 ; ..jump if No Good + + LD A,(rdOp) + OR A ; Read operation? + LD A,#0x05 ; Load DP8473 Write Command + JR Z,SWrite ; No, must be Write + INC A ; (A=06H) Load DP8473 Read Command +SWrite: OR #0x40 ; Set MFM Mode Bit + PUSH BC ; Save Regs + LD C,A ; Save + LD B,#9 ; Read/Write Comnds are 9 bytes + + LD A,(eot) ; Get Last Sctr # + PUSH AF ; (save for Exit) + LD A,(sect) ; Get Desired Sector # + LD (eot),A ; make last to Read only one Sector + +;;-- LD HL,(actDma) ; Get actual DMA Addr + ld hl,(_devfd_buffer) ;;-- + CALL FdCmd ; Execute Read/Write + + POP AF ; Restore Last Sctr # + LD (eot),A ; to Comnd Blk + + LD A,(st1) ; Get Status Reg 1 + AND #0x34 ; Return Any Error Bits + POP BC ; Restore Regs + LD (_devfd_error),A ; (store Error bits) + JR Z,FhdrX ; ..jump to return if No Errors + +Rwf2: LD A,(rwRtry) ; Get retry count + CP #2 ; Are we on Next to last try? + CALL Z,Recal ; Return to Track 0 if so + LD A,(rwRtry) ; and re-fetch try count + DEC A ; Do we have more retries left? + JR NZ,Rwf1 ; ..jump to try again if more tries remain + + OR #0xFF ; Else show Error +FhdrX: LD L,A + LD H,#0 + RET ; and Exit + +;------------------------------------------------------------- +; SPEC - Do a Specify Command, setting Step Rate and Head +; Load/Unload Time. Settings require tailoring to Drive. +; +; Enter: IY -> Drive Table entry for current drive +; Exit : Nothing +; Uses : AF,BC + +Spec: CALL WRdyT ; Wait for RQM (hope DIO is Low!), Disable Ints + RET C ; ..Error if Timed Out + LD A,#0x03 ; Do an FDC Specify Command + OUT (DR),A + + CALL WRdyT + RET C ; ..Error if Timed Out + LD A,oPRM1(IY) ; first Rate Byte (Step Rate, HUT) + OUT (DR),A + + CALL WRdyT + RET C ; ..Error if Timed Out + LD A,oPRM2(IY) ; Get Head Load Time + ADD A,A ; Shift value left (doubles count) + INC A ; Set LSB for Non-DMA Operation + OUT (DR),A + XOR A ; Return Ok Flag + RET + +;------------------------------------------------------------- +; RECAL Recalibrate Current "drive" (moves heads to track 0). +; Enter : IY -> Current Drive Table Entry +; Variable "drive" set to desired floppy unit +; Return: A = 0 if Ok, NZ if Error. Flags reflect A +; Uses : AF All other Registers Preserved/Not Affected +; +; NOTE: BC Must be preserved by this routine. + +Recal: LD A,(hd) ; Get head # + ADD A,A + ADD A,A ; Shift to B3 + PUSH HL ; (preserve regs) + LD HL,#drive + OR (HL) ; add Drive bits + POP HL ; (restore regs) + + LD (hdr),A ; in Command Block + LD A,#3 ; Give this 3 chances to Home +Recal1: LD (retrys),A + PUSH BC ; Save needed regs + PUSH HL + LD BC,#(2*256+7) ; (2-byte Recalibrate Comnd = 07H) + CALL FdCmd ; execute Recalibrate + CALL FdcDn ; Clear Pending Ints, Wait for Seek Complete + POP HL ; (restore regs) + POP BC + AND #0x10 ; Homed? (B4=1 if No Trk0 found) + JR Z,RecOk ; ..jump to Store if Ok + LD A,(retrys) + DEC A ; Any trys left? + JR NZ,Recal1 ; ..loop if So + DEC A ; Else set Error Flag (0-->FF) + RET + +RecOk: XOR A ; Get a Zero (track / flag) + LD oTRK(IY),A ; Set in Table + RET ; and return + +;------------------------------------------------------------- +; READID - Read the first Valid Address Mark on a track. +; +; Enter : "hdr" byte set in Command Blk +; Return: A = 0 if Ok, NZ if Error. Flags reflect A +; Uses : AF All other Registers Preserved/Not Affected + +ReadID: LD A,#0x4a ; Load ReadID Command + MFM Mode byte + PUSH BC ; Save regs + LD B,#2 ; two bytes in ReadID Command + LD C,A ; move Command to C + CALL FdCmd ; Activate DP8473 FDC + + LD A,(st1) ; Get Status Reg 1 + AND #0x25 ; Return Any Error Bits + POP BC ; Restore regs + RET ; ..and quit + +;------------------------------------------------------------- +; SETUP - Set parameters necessary to Read/Write from Active Drive +; Enter: Variable "drive" set to current Drive (0..3) +; Variables _devfd_track and _devfd_sector set to desired block address +; Exit : IY -> Drive's Table entry +; Uses : AF,BC,HL + +Setup: LD A,(drive) + PUSH AF + CALL GetPrm ; Pt to Current Drive's Table + PUSH HL + POP IY + POP BC + LD A,(active) ; Get current Activation Byte + AND #0xf0 ; keep only motors + OR B ; add drive bits + CALL Activ8 ; save new byte and activate FDC + LD A,(_devfd_track) ; Get Host Track # + SRL A ; Div by 2 (LSB to Carry) + LD (trk),A ; Physical Track # to Comnd Blk + LD A,#0 + ADC A,A ; LSB becomes Head # + LD (hd),A ; save in Comnd Blk + ADD A,A + ADD A,A ; Shift to B3 + LD HL,#drive + OR (HL) ; add Drive bits + LD (hdr),A ; Save in Comnd Blk + LD A,oGAP3(IY) + LD (gpl),A ; Set Gap3 Length + LD A,oSPT(IY) + LD (eot),A ; Final Sector # on Trk + LD A,oFMT(IY) + AND #3 ; B0/1 of Format byte is Sector Size + LD (rsz),A ; save in Comnd Blk + LD A,#0xFF + LD (dtl),A ; Set Data Length code + LD A,(_devfd_sector) + ADD A,oSEC1(IY) ; Offset Sector # (base 0) by 1st Sector # + LD (sect),A ; set in Comnd Blk + + XOR A ; (Preset Hi 500 kbps, 3.5 & 5.25" Rate) + BIT 7,oFMT(IY) ; Hi (500 kbps) Speed? + JR NZ,StSiz0 ; ..jump if Hi-Density/Speed to Set if Yes + LD A,oFMT(IY) + AND #0x0c ; Else Get Drive size + CP #0x08 ; 5.25"? + LD A,#0x02 ; (Prepare for 250 kbps) + JR NZ,StSiz0 ; ..jump if Not 5.25" w/Rate Set + BIT 4,oFMT(IY) ; Hi-Density capable drive? + LD A,#0x02 ; (Prepare for 250 kbps) + JR Z,StSiz0 ; ..jump if No + LD A,#0x01 ; Else set to 300 kbps (@360 rpm = 250kbps) +StSiz0: OUT (DRR),A ; Set Rate in FDC Reg + LD D,A ; preserve Rate bits + IN0 A,(0x1F) ; Read Z80182 CPU Cntrl Reg (B7=1 if Hi Speed) + RLA ; Speed to Bit Carry..Turbo? + LD A,#(CPU_CLOCK_KHZ/1000) ; (Get Processor Rate in MHz) + JR C,StSiz1 ; ..jump if Turbo for longer delay + SRL A ; Else divide rate by 2 +StSiz1: INC D + DEC D ; 500 kb/s (Hi-Speed) Rate (D=0)? + JR NZ,StSiz2 ; ..jump if Not + LD A,#1 ; Else minimum delay for "High-Speed" +StSiz2: LD (dlyCnt),A ; save delay count + RET + +;------------------------------------------------------------- +; SEEK - Set the Track for disk operations and seek to it. +; +; Enter : A = Desired Track Number +; D = Verify flag (0=No, FF=Yes) +; Return: A = 0, Zero Flag Set (Z) if Ok, A <> 0 Zero Clear (NZ) if Error +; Uses : AF All other Registers Preserved/Not Affected + +SEEK: PUSH HL ; Save Regs used here + PUSH DE + PUSH BC + + LD A,(trk) ; Get Track # + CP oTRK(IY) ; Is desired Track same as last logged? + LD oTRK(IY),A ; (set as if we made it there) + JR NZ,SEEKNV ; ..jump if Not Same + INC D ; Else Set to No Verify (FF->0) +SEEKNV: LD A,#4 ; Get the maximum Retry Count +SEEK1: LD (retrys),A ; save remaining Retry Count + LD BC,#(3*256+0x0F); (3-byte Seek Command = 0FH) + CALL FdCmd ; Execute the Seek + CALL FdcDn ; Clear Pending Int, wait for Seek Complete + + AND #0xE0 + CP #0x20 + JR NZ,SEEK2 ;; + + AND #0x40 ; Set NZ if Abnormal Termination + + LD B,A ;; Save Seek Status + LD A,(trk) ;; Check track # + CP C ;; Same track? + JR NZ,SEEK2 ;; Jump to Retry if NOT + LD A,B ;; Restore Seek Status + + INC D ; Are we Verifying (FF -> 0)? + CALL Z,ReadID ; Read next ID Mark if So + DEC D ; (Correct for Test, 0 -> FF) + + OR A ; Set Status (Seek Status if No ReadID) + JR Z,SEEKX ; ..exit if Ok + +SEEK2: LD A,(retrys) ; Else get trys remaining + DEC A ; Any left (80-track could need two)? + JR NZ,SEEK1 ; ..loop to try again if More + DEC A ; Else set Error Flag (0->FF) + +SEEKX: POP BC ; Restore Regs + POP DE + POP HL + RET + +;------------------------------------------------------------- +; FDCMD - Send Command to DP-8473 FDC +; Enter: B = # of Bytes in Command, C = Command Byte +; HL -> Buffer for Read/Write Data (If Needed) +; Exit : AF = Status byte +; Uses : AF. All other registers preserved/unused + +FdCmd: PUSH HL ; Save regs (for Exit) + PUSH BC + PUSH HL ; save pointer for possible Transfer + CALL Motor ; Ensure motors are On + LD HL,#comnd ; Point to Command Block + LD (HL),C ; command passed in C + LD C,#DR ; DP8473 Data Port +OtLoop: CALL WRdy ; Wait for RQM (hoping DIO is Low) (No Ints) + OUTI ; Output Command bytes to FDC + JR NZ,OtLoop ; ..loop til all bytes sent + + POP HL ; Restore Possible Transfer Addr +FdCi1: CALL WRdy + BIT 5,A ; In Execution Phase? + JR Z,FdcRes ; ..jump if Not to check result + BIT 6,A ; Write? + JR NZ,FdCi2 ; ..jump if Not to Read + OUTI ; Else Write a Byte from (HL) to (C) + JR FdCi1 ; and check for next + +FdCi2: INI ; Read a byte from (C) to (HL) + JR FdCi1 ; and check for next + +FdcRes: LD HL,#st0 ; Point to Status Result area +IsGo: CALL WRdy +;;--- CALL _ei ; (Ints Ok now) + BIT 4,A ; End of Status/Result? + JR Z,FdcXit ; ..exit if So + BIT 6,A ; Another byte Ready? + JR Z,FdcXit ; ..exit if Not + INI ; Else Read Result/Status Byte + JR IsGo ; ..loop for next + +FdcXit: POP BC ; Restore Regs + POP HL + RET + +;------------------------------------------------------------- +; Check for Proper Termination of Seek/Recalibrate Actions by +; executing a Check Interrupt Command returning ST0 in A. +; Enter: None. Used after Seek/Recalibrate Commands +; Exit : A = ST0 Result Byte, C = PCN result byte +; Uses : AF and C. All other registers preserved/unused + +FdcDn: PUSH HL ; Don't alter regs +FdcDn0: CALL WRdy1 + LD A,#8 ; Sense Interrupt Status Comnd + OUT (DR),A + CALL WRdy1 + IN A,(DR) ; Get first Result Byte (ST0) + LD L,A + CP #0x80 ; Invalid Command? + JR Z,FdcDn0 ; ..jump to exit if So + CALL WRdy1 + IN A,(DR) ; Read Second Result Byte (Trk #) + LD C,A ; ..into C + LD A,L + BIT 5,A ; Command Complete? + JR Z,FdcDn0 ; ..loop if Not + POP HL + RET + +;------------------------------------------------------------- +; MOTOR CONTROL. This routine performs final selection of +; the drive control latch and determines if the Motors are +; already spinning. If they are off, then the Motors are +; activated and the spinup delay time in tenths-of-seconds +; is performed before returning. +; +; Enter : None +; Return: None +; Uses : HL. Remaining Registers Preserved/Not Affected + +Motor: PUSH AF ; Save Regs + LD A,(motim) ; Get remaining Seconds + OR A ; Already On? + LD A,#MONTIM ; (get On Time) + LD (motim),A ; always reset + JR NZ,MotorX ; ..exit if already running + PUSH BC + LD A,(hdr) ; Get current Drive + OR #0xF4 ; Set All Motors On and Controller Active + CALL Activ8 ; Do It! + LD A,(drive) ; Get Current drive + CALL GetPrm ; Pt to Param table + LD BC,#oSPIN + ADD HL,BC ; offset to Spinup Delay + LD A,(HL) ; Get value + LD (mtm),A ; to GP Counter + EI ; Ensure Ints are ABSOLUTELY Active.. +MotoLp: LD A,(mtm) ; ..otherwise, loop never times out! + OR A ; Up to Speed? + JR NZ,MotoLp ; ..loop if Not + DI ; No Ints now.. + POP BC +MotorX: POP AF ; Restore Reg + RET + +;------------------------------------------------------------- +; Wait for FDC RQM to become Ready with Timeout indicator. +; Timeout Length is arbitrary and depends on CPU Clock Rate. + +WRdyT: ;DI ; No Ints while we are doing I/O + LD BC,#30000 ; << Arbitrary >> +WRdyT0: DEC BC + LD A,B + OR C ; Timed Out? + SCF ; (set Error Flag in case) + RET Z ; ..return Error Flag if Yes + IN A,(MSR) ; Read Status Reg + AND #0x80 ; Interrupt Present (also kill Carry)? + RET NZ ; ..return Ok if Yes + JR WRdyT0 ; ..else loop to try again + +;------------------------------------------------------------- +; Wait for FDC RQM to become Ready, return DIO status in +; Zero Flag. Pause before reading status port (~12 mS +; specified, some assumed in code). + +WRdy: ;DI ; No Ints while we are doing I/O + ; (entry to avoid Disabling Ints) +WRdy1: LD A,(dlyCnt) ; Get delay count +WRdy0: DEC A ; count down + JR NZ,WRdy0 ; for ~6 uS Delay +WRdyL: IN A,(MSR) ; Read Main Status Register + BIT 7,A ; Interrupt Present? + RET NZ ; Return if So + JR WRdyL ; Else Loop + +;------------------------------------------------------------- +; Return Pointer to Parameters of selected Drive +; Enter: A = Drive (0..3) +; Exit : HL -> Parameter entry of drive +; Uses : AF,HL + +GetPrm: PUSH DE + LD DE,#TBLSIZ ; Entry Size + LD HL,#_devfd_dtbl ; Init to table start + INC A +GetPr0: DEC A ; End? + JR Z,GetPrX ; ..quit if Yes, Ptr set + ADD HL,DE ; Else step to next + JR GetPr0 ; ..loop til found + +GetPrX: POP DE + RET + +;------------------------------------------------------------- +; This routine called at each Clock Interrupt. It is used +; to provide any necessary timer/timeout functions. +; Enter: None. +; Exit : HL -> mtm byte variable +; AF - destroyed +; Uses : AF,HL + +_fd_tick: + LD HL,#motim ; Point to FDC Motor-On timer + LD A,(HL) + OR A ; Already Timed out? + JR Z,TDone ; ..jump if Yes + DEC (HL) ; Else count down + CALL Z,MotOff ; stop motors if timed out +TDone: INC HL ; Advance ptr to watchdog/spinup timer (mtm) + LD A,(HL) + OR A ; Timed out? + RET Z ; ..quit if Yes + DEC (HL) ; Else count down + RET ; exit + +;------------------------------------------------------------- +; Motor Off routine. Force Off to delay on next select +; Enter: None. (Motoff) +; A = FDC Device Control Reg bits (Activ8/ActivA) +; Exit : A = Current DCR Register / "active" byte settings +; Uses : AF + +MotOff: XOR A + LD (motim),A ; Ensure Motors Marked as OFF + LD A,(active) ; Get current settings + AND #7 ; strip off Motor bits +Activ8: OR #4 ; (ensure FDC out of Reset) +ActivA: LD (active),A ; save + OUT (DCR),A ; and Command! + RET + +;------------------- Data Storage Area ----------------------- +; Disk and Drive parameters are dictated by table entries. While +; not all parameters are implemented in this module, they may be +; added as desired. A bit-mapped byte is used defined as: + +; D D D D D D D D Format Byte +; 7 6 5 4 3 2 1 0 +; | | | | | | +-+----- Sector Size: 000=128, 001=256, 010=512, 011=1024 bytes +; | | | | +-+--------- Disk Size: 00=fixed disk, 01=8", 10=5.25", 11=3.5" +; | | | +------------- 0 = Normal 300 RPM MFM, 1 = "High-Density" Drive +; | | +--------------- 0 = Single-Sided, 1 = Double-Sided +; | +----------------- 0 = Double-Density, 1 = Single-Density +; +------------------- 0 = 250 kbps (normal MFM), 1 = 500 kbps (Hi-Density) + +IBMPC3 .equ 0xAE ; 10101110B ; HD, DD, DS, 3.5", 512-byte Sctrs (1.44 MB) +UZIHD3 .equ 0xAF ; 10101111B ; HD, DD, DS, 3.5", 1024-byte Sctrs (1.76 MB) +IBMPC5 .equ 0xAA ; 10101010B ; HD, DD, DS, 5.25", 512-byte Sctrs (1.2 MB) +UZIHD5 .equ 0xAB ; 10101011B ; HD, DD, DS, 5.25", 1024-byte Sctrs (1.44 MB) +DSQD3 .equ 0x2F ; 00101111B ; MFM, DD, DS, 3.5", 1024-byte Sctrs (800 KB) +DSDD3 .equ 0x2E ; 00101110B ; MFM, DD, DS, 3.5", 512-byte Sctrs (800 KB) +DSQD5 .equ 0x2B ; 00101011B ; MFM, DD, DS, 5.25", 1024-byte Sctrs (800 KB) +DSDD5 .equ 0x2A ; 00101010B ; MFM, DD, DS, 5.25", 512-byte Sctrs (800 KB) + +_devfd_dtbl: ; Drive Param Table. 1 Entry Per Drive. + .db 0, 0xCF, 1, 27, 18, 1, IBMPC3, 10, 0, 160 + ; | | | | | | | | | +- Number of Cylinders + ; | | | | | | | | +- Current Track Number + ; | | | | | | | +- Spinup (1/20-secs) + ; | | | | | | +-- Format Byte (See above) + ; | | | | | +------- First Sector Number + ; | | | | +------- Physical Sectors-Per-Track + ; | | | +----------- Gap3 (Size 512=27, 1024=13) + ; | | +---------------- Hd Load in 4mS steps (0=inf) + ; | +--------------------- Step Rate (B7-4) = 4mS (2's compl) + ; HUT (B3-0) = 240 mS + ; +--------------------------- Drive Logged (FF), Unlogged (0) +TBLSIZ .equ . - _devfd_dtbl + .db 0, 0xCF, 1, 27, 18, 1, IBMPC3, 10, 0, 160 + .db 0, 0xCF, 1, 27, 18, 1, IBMPC3, 10, 0, 160 + .db 0, 0xCF, 1, 27, 18, 1, IBMPC3, 10, 0, 160 + +; -->>> NOTE: Do NOT move these next two variables out of sequence !!! <<<-- +motim: .db 0 ; Motor On Time Counter +mtm: .db 0 ; Floppy Spinup Time down-counter + +dlyCnt: .db (CPU_CLOCK_KHZ/1000) ; Delay to avoid over-sampling status register + +;------------------------------------------------------------------------------ + .area _DATA + +drive: .ds 1 ; (minor) Currently Selected Drive +active: .ds 1 ; Current bits written to Dev Contr Reg (DCR) +;;--block: DEFS 1 ; Index in Buffer for desired 512-byte block +;;--buffer: DEFS 1024 ; Physical Sector Buffer. Max Possible Size + +_devfd_sector: .ds 1 +_devfd_track: .ds 1 ; LSB used as Head # in DS formats +_devfd_error: .ds 1 +_devfd_buffer: .ds 2 + +comnd: .ds 1 ; Storage for Command in execution +hdr: .ds 1 ; Head (B2), Drive (B0,1) +trk: .ds 1 ; Track (t) +hd: .ds 1 ; Head # (h) +sect: .ds 1 ; Physical Sector Number +rsz: .ds 1 ; Bytes/Sector (n) +eot: .ds 1 ; End-of-Track Sect # +gpl: .ds 1 ; Gap Length +dtl: .ds 1 ; Data Length + +; FDC Operation Result Storage Area + +st0: .ds 1 ; Status Byte 0 +st1: .ds 1 ; Status Byte 1 (can also be PCN) + .ds 1 ; ST2 - Status Byte 2 + .ds 1 ; RC - Track # + .ds 1 ; RH - Head # (0/1) + .ds 1 ; RR - Sector # + .ds 1 ; RN - Sector Size + +actDma: .ds 2 ; 16-bit DMA Address + +; DISK Subsystem Variable Storage + +rdOp: .ds 1 ; Read/write flag +retrys: .ds 1 ; Number of times to try Opns +rwRtry: .ds 1 ; Number of read/write tries +DRVSPD: .ds 1 ; Drive Speed +DRVSIZ: .ds 1 ; Drive Size diff --git a/Kernel/platform-p112/devices.c b/Kernel/platform-p112/devices.c index 9edd3de4..e814e816 100644 --- a/Kernel/platform-p112/devices.c +++ b/Kernel/platform-p112/devices.c @@ -7,12 +7,19 @@ #include #include #include +#ifdef CONFIG_P112_FLOPPY +#include "devfd.h" +#endif struct devsw dev_tab[] = /* The device driver switch table */ { /* open close read write ioctl */ { blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_ioctl }, /* 0: /dev/hd -- standard block device interface */ +#ifdef CONFIG_P112_FLOPPY + { fd_open, fd_close, fd_read, fd_write, no_ioctl }, /* 1: /dev/fd -- floppy drive */ +#else { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, /* 1: unused slot */ +#endif { tty_open, tty_close, tty_read, tty_write, tty_ioctl }, /* 2: /dev/tty -- serial ports */ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, /* 3: unused slot */ { no_open, no_close, sys_read, sys_write, sys_ioctl }, /* 4: /dev/mem etc System devices (one offs) */ diff --git a/Kernel/platform-p112/flopboot-mkimage b/Kernel/platform-p112/flopboot-mkimage new file mode 100755 index 00000000..43ad457c --- /dev/null +++ b/Kernel/platform-p112/flopboot-mkimage @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +import sys + +def load_bootstrap(filename): + f = open(filename, 'rb') + f.seek(0xF800) + return f.read(512) + +def stamp_bootstrap(sector, kernel): + # trim off dummy configuration + sector = sector[:510] + + # penultimate byte is number of sectors to load + sector += chr( (len(kernel)+511)/512 ) + + # compute the final byte (checksum, verified by the ROM) + b = sum(ord(c) for c in sector) + sector += chr(-b & 0xff) + + return sector + +# load the floppy boot sector +bootstrap = load_bootstrap(sys.argv[1]) + +# load the kernel image +kernel = open(sys.argv[2], 'rb').read() + +# make the boot image +image = stamp_bootstrap(bootstrap, kernel) + kernel + +# write output file +open(sys.argv[3], 'wb').write(image) diff --git a/Kernel/platform-p112/flopboot.s b/Kernel/platform-p112/flopboot.s new file mode 100644 index 00000000..8cbe0d00 --- /dev/null +++ b/Kernel/platform-p112/flopboot.s @@ -0,0 +1,154 @@ +; 2015-01-19 Will Sowerbutts +; Simple P112 floppy bootloader for Fuzix +; Boots only from drive 0, floppy is used solely for kernel, one +; cannot have a filesystem on there as well. +; +; Assembles to a single sector +; +; based on: +; Boot loader for P112 UZI180 floppy disks. +; Uses the ROM monitor Disk I/O routines. +; Copyright (C) 2001, Hector Peraza + + .module flopboot + .z180 + + .include "kernel.def" + .include "../cpu-z180/z180.def" + +dparm .equ 0x0B +loadaddr .equ 0x8000 ; P112 bootstrap loads us here +himem .equ 0xF800 ; We use F800 to approx FC00 +stack .equ 0xFE00 ; P112 ROM uses FE00 upwards +fuzix_entry .equ 0x88 ; Kernel must be loaded at 0x88 upwards +cmdline .equ 0x80 ; CP/M command line area + + .area _LOADER (ABS) + .org himem +start: + + ; P112 ROM loads us at 8000, we copy ourselves to the correct location + ld hl, #loadaddr + ld de, #himem + ld bc, #512 + ldir ; copy into high memory + jp loader ; jump into new copy + +loader: + ld sp, #stack ; put SP somewhere safe + ld a,#0xF8 ; keep loader and BIOS data area mapped, with ROM in low 32K + out0 (MMU_CBAR),a + in0 a, (MMU_CBR) + out0 (MMU_BBR), a + in0 a,(Z182_SYSCONFIG) + set 3,a ; enable the BIOS ROM in case it was shadowed + out0 (Z182_SYSCONFIG),a + ld hl, #msg + rst #0x20 + ld a, (sectors) ; load number of sectors -- set by flopboot-mkimage + ld b, a + ld hl, #1 ; kernel starts in the second sector on the floppy + ld de, #fuzix_entry ; load address in memory +loop: + push bc + push hl + push de + call spin + ld ix,(dparm) + call xlate ; translate block number to track and sector + ld a, #2 ; read command + ld b, #1 ; number of sectors + ld d, #0 ; drive 0 + ld hl, #bfr + rst #0x08 ; P112 disk services + jr c, error + ld hl, #bfr + pop de ; restore load address + ld bc, #512 + ld a,#0xF0 ; RAM into low 32K + out0 (MMU_CBAR),a + ldir + ld a,#0xF8 ; ROM into low 32K + out0 (MMU_CBAR),a + pop hl + pop bc + ; setup for next block + inc hl + djnz loop + ; completed loading + ld hl, #donemsg + rst #0x20 + in0 a,(Z182_SYSCONFIG) + set 3,a ; disable ROM + out0 (Z182_SYSCONFIG),a + ld a,#0xF0 ; RAM into low 32K + out0 (MMU_CBAR),a + ; store an empty command line + xor a + ld hl,#cmdline + ld (hl), a + inc hl + ld (hl), a + ; jump in to kernel code + jp fuzix_entry + +msg: .ascii "Loading ... " + .db 0 + +whirl: .ascii "/-\|" + +donemsg: .db 8, 32, 13 + .ascii "Booting ..." + .db 13, 0 + +spin: + push hl + ld hl, #whirl + ld a, b + and #3 + add l + ld l, a + ld a, #8 ; backspace + rst #0x18 + ld a, (hl) + rst #0x18 + pop hl + ret + +error: + ld hl, #errmsg + rst #0x20 + rst #0x38 + +errmsg: .ascii "Load error" + .db 13, 10, 0 + +; input: block number in HL +; output: track/side in C, sector in E +xlate: ld e,4(ix) ; SPT + call div + ld c,l ; track + add a,13(ix) ; add 1st sector number + ld e,a + ret + +; HL/E = HL remainder in A +div: ld b,#16+1 + xor a +div1: adc a,a + sbc a,e + jr nc,div0 + add a,e +div0: ccf + adc hl,hl + djnz div1 + ret + +bfr: ; next 512 bytes used as temporary buffer + +space: .ds (510-(.-start)) +sectors: .db 0 +checksum: .db 0 +.ifne (512-(.-start)) +.error something has gone wrong, this should assemble to exactly 512 bytes. +.endif diff --git a/Kernel/platform-p112/fuzix.lnk b/Kernel/platform-p112/fuzix.lnk index 880d89fd..73cc8019 100644 --- a/Kernel/platform-p112/fuzix.lnk +++ b/Kernel/platform-p112/fuzix.lnk @@ -38,4 +38,6 @@ platform-p112/mbr.rel platform-p112/ds1302.rel platform-p112/ds1302-p112.rel platform-p112/monitor.rel +platform-p112/devfd.rel +platform-p112/devfd2.rel -e diff --git a/Kernel/platform-p112/main.c b/Kernel/platform-p112/main.c index 361457ea..1121bca3 100644 --- a/Kernel/platform-p112/main.c +++ b/Kernel/platform-p112/main.c @@ -4,6 +4,9 @@ #include #include #include "config.h" +#ifdef CONFIG_P112_FLOPPY +#include "devfd.h" +#endif uint16_t ramtop = PROGTOP; extern unsigned char irqvector; @@ -16,6 +19,10 @@ void z180_timer_interrupt(void) a = TIME_TMDR0L; a = TIME_TCR; +#ifdef CONFIG_P112_FLOPPY + fd_tick(); +#endif + timer_interrupt(); } diff --git a/Kernel/platform-p112/p112.s b/Kernel/platform-p112/p112.s index c27ca19d..f95b4879 100644 --- a/Kernel/platform-p112/p112.s +++ b/Kernel/platform-p112/p112.s @@ -36,12 +36,9 @@ init_early: ld a, #0x0c out0 (0x92), a - ; unmap ROM - xor a - out0 (Z182_ROMBR), a - + ; Z80182: disable ROM, entire physical address space maps to RAM in0 a, (Z182_SYSCONFIG) - or #0x08 ; disable ROM chip select (is this required as well as setting Z182_ROMBR?) + set 3, a out0 (Z182_SYSCONFIG), a jp z180_init_early