v85: add floppy, ramdisc and rtc
authorAlan Cox <alan@linux.intel.com>
Thu, 28 Feb 2019 21:33:28 +0000 (21:33 +0000)
committerAlan Cox <alan@linux.intel.com>
Thu, 28 Feb 2019 21:33:28 +0000 (21:33 +0000)
Based on the emulations added to the emulator. This hopefully gives people some
more examples to work with when writing stuff.

12 files changed:
Kernel/platform-v85/Makefile
Kernel/platform-v85/README
Kernel/platform-v85/config.h
Kernel/platform-v85/devices.c
Kernel/platform-v85/devrd.c [new file with mode: 0644]
Kernel/platform-v85/devrd.h [new file with mode: 0644]
Kernel/platform-v85/fdc765.s [new file with mode: 0644]
Kernel/platform-v85/main.c
Kernel/platform-v85/mdrive.s [new file with mode: 0644]
Kernel/platform-v85/msm5832.c [new file with mode: 0644]
Kernel/platform-v85/platform_fdc765.h [new file with mode: 0644]
Kernel/platform-v85/v85.s

index 4ac258b..5b77ad2 100644 (file)
@@ -1,14 +1,14 @@
 LIBPATH=../../Library/libs
 LIBC=$(LIBPATH)/libc8080.a $(ACK_ROOT)/share/ack/cpm/libem.a
 
-CSRCS += devices.c main.c devtty.c
+CSRCS += devices.c main.c devtty.c msm5832.c devrd.c
 
 DISCARD_DSRCS = ../dev/devide_discard.c
-DSRCS = ../dev/devide.c ../dev/mbr.c ../dev/blkdev.c
+DSRCS = ../dev/devide.c ../dev/mbr.c ../dev/blkdev.c ../dev/devfdc765.c
 
 CROSS_CCOPTS += -I../dev/
 
-ASRCS = crt0.s commonmem.s v85.s tricks.s end.s
+ASRCS = crt0.s commonmem.s v85.s tricks.s end.s fdc765.s mdrive.s
 
 AOBJS = $(ASRCS:.s=.o)
 COBJS = $(CSRCS:.c=.o)
@@ -49,6 +49,7 @@ image:
        ../tty.o ../devsys.o ../usermem.o ../syscall_fs2.o \
        ../syscall_fs3.o ../syscall_exec16.o  \
        blkdev.o mbr.o devide.o devide_discard.o \
+       devfdc765.o fdc765.o msm5832.o devrd.o mdrive.o \
        ../usermem_std-8080.o $(LIBC) end.o
        anm fuzix.bin | ../tools/8080map > ../fuzix.map
        ../tools/ack2kernel -v fuzix.bin ../fuzix.bin
index be6e7cb..5088493 100644 (file)
@@ -21,3 +21,7 @@ Perhaps repeats of:
                pop b
                dex b
                jnk loop ...
+
+For the matching emulator see:
+
+https://github.com/EtchedPixels/V85
index f8595bb..fa9699b 100644 (file)
@@ -1,7 +1,7 @@
 /* We have an RTC */
-#undef CONFIG_RTC
+#define CONFIG_RTC
 /* And we can read ToD from it */
-#undef CONFIG_RTC_FULL
+#define CONFIG_RTC_FULL
 /* Enable to make ^Z dump the inode table for debug */
 #undef CONFIG_IDUMP
 /* Enable to make ^A drop back into the monitor */
index 317f270..1f17178 100644 (file)
@@ -7,6 +7,8 @@
 #include <devfd.h>
 #include <blkdev.h>
 #include <devide.h>
+#include <devfdc765.h>
+#include <devrd.h>
 
 
 struct devsw dev_tab[] =  /* The device driver switch table */
@@ -14,16 +16,21 @@ struct devsw dev_tab[] =  /* The device driver switch table */
 // minor    open         close        read      write       ioctl
 // -----------------------------------------------------------------
   /* 0: /dev/hd                Hard disc block devices */
-  {  blkdev_open, no_close,    blkdev_read,   blkdev_write,  blkdev_ioctl },
+  {  blkdev_open, no_close,    blkdev_read,   blkdev_write, blkdev_ioctl },
   /* 1: /dev/fd                Floppy disc block devices  */
-  {  no_open,    no_close,    no_rdwr,   no_rdwr,    no_ioctl },
+  {  devfd_open,  no_close,    devfd_read,    devfd_write,  no_ioctl     },
   /* 2: /dev/tty       TTY devices */
-  {  tty_open,    tty_close,   tty_read,  tty_write,  tty_ioctl },
+  {  tty_open,    tty_close,   tty_read,      tty_write,    tty_ioctl    },
   /* 3: /dev/lpr       Printer devices */
-  {  no_open,     no_close,    no_rdwr,   no_rdwr,    no_ioctl  },
+  {  no_open,     no_close,    no_rdwr,       no_rdwr,      no_ioctl     },
   /* 4: /dev/mem etc   System devices (one offs) */
-  {  no_open,     sys_close,   sys_read,  sys_write,  sys_ioctl  },
+  {  no_open,     sys_close,   sys_read,      sys_write,    sys_ioctl    },
   /* Pack to 7 with nxio if adding private devices and start at 8 */
+  {  nxio_open,          no_close,    no_rdwr,       no_rdwr,      no_ioctl     },
+  {  nxio_open,          no_close,    no_rdwr,       no_rdwr,      no_ioctl     },
+  {  nxio_open,          no_close,    no_rdwr,       no_rdwr,      no_ioctl     },
+  /* 8: /dev/rd                RAMdisc block devices  */
+  {  devrd_open,  no_close,    devrd_read,    devrd_write,  no_ioctl     },
 };
 
 bool validdev(uint16_t dev)
@@ -38,5 +45,7 @@ bool validdev(uint16_t dev)
 
 void device_init(void)
 {
+    /* TODO: init the FDC and program step rate etc */
     devide_init();
+    devrd_init();
 }
diff --git a/Kernel/platform-v85/devrd.c b/Kernel/platform-v85/devrd.c
new file mode 100644 (file)
index 0000000..5e12930
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *     RAM drive (512K)
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devrd.h>
+
+static uint8_t rd_sizeh;
+
+static int rd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+    uint8_t page = 0;
+    uint16_t ct = 0;
+    if (rawflag == 2)
+        page = swappage;
+    else if (rawflag == 1) {
+        if (d_blkoff(BLKSHIFT))
+            return -1;
+        page = udata.u_page;
+    }
+    /* Work in 256 byte pages for ease. Max size is 8MB so can't overflow */
+    udata.u_nblock <<= 1;
+    udata.u_block <<= 1;
+    while(ct < udata.u_nblock) {
+        (is_read?rd_input:rd_output)(udata.u_dptr, udata.u_block, page);
+        udata.u_dptr += 256;
+        udata.u_block++;
+        ct++;
+    }
+    return ct << 8;
+}
+
+int devrd_open(uint8_t minor, uint16_t flag)
+{
+    if (minor || !rd_sizeh) {
+        udata.u_error = ENODEV;
+        return -1;
+    }
+    return 0;
+}
+
+int devrd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag;
+    return rd_transfer(minor, true, rawflag);
+}
+
+int devrd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag;rawflag;minor;
+    return rd_transfer(minor, false, rawflag);
+}
+
+void devrd_init(void)
+{
+    uint8_t i;
+    for (i = 0; i < 8; i++) {
+        if (rd_present(i) == 0)
+            break;
+        rd_sizeh += 8; /* 8 * 64K per board */
+    }
+    if (rd_sizeh)
+        kprintf("rd0: %dKb available.\n", rd_sizeh * 64);
+}
diff --git a/Kernel/platform-v85/devrd.h b/Kernel/platform-v85/devrd.h
new file mode 100644 (file)
index 0000000..a93c607
--- /dev/null
@@ -0,0 +1,8 @@
+extern void rd_input(uint8_t *addr, uint16_t block, uint16_t page);
+extern void rd_output(uint8_t *addr, uint16_t block, uint8_t page);
+extern uint8_t rd_present(uint8_t board);
+
+extern int devrd_open(uint8_t minor, uint16_t flag);
+extern int devrd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+extern int devrd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+extern void devrd_init(void);
diff --git a/Kernel/platform-v85/fdc765.s b/Kernel/platform-v85/fdc765.s
new file mode 100644 (file)
index 0000000..2c23cca
--- /dev/null
@@ -0,0 +1,350 @@
+#
+!
+!              765 Floppy Controller Support
+!
+!      This is based upon the Amstrad NC200 driver by David Given
+!
+!      TODO
+!      Initialize drive step rate etc (we rely on the firmware for now)
+!      Step rate
+!      Head load/unload times
+!      Write off time
+!
+
+#include "../kernel-8080.def"
+
+       .define _fd765_do_nudge_tc
+       .define _fd765_do_recalibrate
+       .define _fd765_do_seek
+       .define _fd765_do_read
+       .define _fd765_do_write
+       .define _fd765_do_read_id
+       .define _fd765_motor_on
+       .define _fd765_motor_off
+
+       .define _fd765_track
+       .define _fd765_head
+       .define _fd765_sector
+       .define _fd765_status
+       .define _fd765_buffer
+       .define _fd765_is_user
+       .define _fd765_sectors
+       .define _fd765_drive
+
+       .define diskmotor
+
+
+.sect .common
+
+!
+! Twiddle the Terminal Count line to the FDC
+!
+_fd765_do_nudge_tc:
+       mvi a,1
+       out 0x1A
+       dcr a
+       out 0x1A
+       ret
+
+! Writes A to the FDC data register.
+
+fd765_tx:
+       push psw
+fd765_tx_loop:
+       in 19
+       add a
+       jnc fd765_tx_loop
+       add a
+       jc fd765_tx_exit                ! controller doesn't want data ??
+       pop psw
+       out 18
+       xthl                    ! check delay length is ok
+       xthl
+fd765_tx_exit:
+       ret
+
+! Reads bytes from the FDC data register until the FDC tells us to stop (by
+! lowering DIO in the status register).
+
+fd765_read_status:
+       lxi h,_fd765_status
+read_status_loop:
+       in 0x19
+       add a                           ! RQM...
+       jnc read_status_loop            ! ...low, keep waiting 
+       add a                           ! DIO...
+       rnc                             ! ...low, no more data
+       in 0x18
+       mov m,a
+       inx h
+       xthl                            ! wait for the 765A
+       xthl
+       xthl
+       xthl
+       jmp read_status_loop            ! next byte
+_fd765_status:
+       .data4 0                        ! 8 bytes of status data
+       .data4 0
+
+! Sends the head/drive byte of a command.
+
+send_head:
+       lhld _fd765_head                ! l = head h = drive)
+       mov a, l
+       add a
+       add a
+       add h
+       jmp fd765_tx
+
+! Performs a RECALIBRATE command.
+
+_fd765_do_recalibrate:
+       mvi a,0x07                              ! RECALIBRATE
+       call fd765_tx
+       lda _fd765_drive                        ! drive
+       call fd765_tx
+       jmp wait_for_seek_ending
+
+! Performs a SEEK command.
+
+_fd765_do_seek:
+       mvi a,0x0f                              ! SEEK
+       call fd765_tx
+       call send_head                          ! specified head, drive #0
+       lda _fd765_track                        ! specified track
+       call fd765_tx
+       jmp wait_for_seek_ending
+_fd765_track:
+       .data1 0
+_fd765_sector:
+       .data1 0
+!
+!      These two must remain adjacent see send_head
+!
+_fd765_head:
+       .data1 0
+_fd765_drive:
+       .data1 0
+
+! Waits for a SEEK or RECALIBRATE command to finish by polling SENSE INTERRUPT STATUS.
+wait_for_seek_ending:
+       mvi a,0x08                              ! SENSE INTERRUPT STATUS
+       call fd765_tx
+       call fd765_read_status
+
+       lda _fd765_status
+       ani 0x20
+       jz wait_for_seek_ending
+
+       ! Now settle the head
+       mvi a,60                ! 30ms @ 6MHz
+
+wait_ms:
+       push b
+wait_ms_loop:
+       mvi b,0xDC
+wait_ms_loop2:
+       dcr b                   ! 4
+       jnz wait_ms_loop2       ! 10 if taken
+       dcr a                   ! 4
+       jnz wait_ms_loop        ! 10 if taken
+       pop b
+       ret
+
+_fd765_motor_off:
+       xra a
+       out 0x1B
+       sta diskmotor
+       ret
+
+_fd765_motor_on:
+       lda diskmotor
+       ora a
+       rnz
+       ! Take effect
+       sta diskmotor
+       ! Now wait for spin up
+
+       mvi e,10
+wait2:
+       lxi b,0x61A8            ! 6MHz
+wait1:
+       dcx b                   !       6
+       mov a,b                 !       4
+       ora c                   !       4
+       jnz wait1               !       10 (taken)      24 per loop
+       dcr e                   !       4
+       jnz wait2               !       10 (taken)
+       ret
+!
+! Reads a 512-byte sector, after having previously saught to the right track.
+!
+! We need to be doubly careful here as the 765A has a 'feature' whereby it
+! won't report an overrun on the last byte so we must always make timing
+!
+_fd765_do_read:
+       mvi a,0x46                      ! READ SECTOR MFM
+
+       call setup_read_or_write
+
+       lda _fd765_is_user
+       ora a
+       push psw
+       cnz map_process_always
+
+       di                              ! performance critical,
+                                       ! run with interrupts off
+
+       xra a
+       call fd765_tx                   ! send the final unused byte
+                                       ! to fire off the command       
+       lhld _fd765_buffer
+       mvi e,0
+       jmp read_wait
+!
+!      First 256 bytes. Fix me - timings/optimization eg can we avoid
+!      second add a on an 8080
+!
+read_loop:
+       in 0x18
+       mov m,a
+       inx h
+       dcr e
+       jz read_wait2
+read_wait:
+       in 0x19                 ! read the fdc status
+       add a                   ! bit 7 into C
+       jnc read_wait           ! 7 clear => not ready
+       add a                   ! bit 6 into C bit 5 into sign
+       jp read_loop            ! 5 set => still transferring
+       jmp read_finished
+!
+!      Second 256 bytes
+!
+read_loop2:
+       in 0x18
+       mov m,a
+       inx h
+       dcr e
+       jz read_finished
+read_wait2:
+       in 0x19
+       add a
+       jnc read_wait2
+       add a
+       jp read_loop2
+       jmp read_finished
+!
+!      And done
+!
+read_finished:
+       shld _fd765_buffer
+       ! This is wrong - we should issue it before we read the last
+       ! according to some docs ?
+       call _fd765_do_nudge_tc         ! Tell FDC we've finished
+       ei
+
+       call fd765_read_status
+
+       pop psw
+       rz
+       jmp map_kernel
+
+!
+!      Write is much like read just the other direction
+!
+_fd765_do_write:
+                                       ! interrupts off
+       mvi a,0x45                      ! WRITE SECTOR MFM
+       call setup_read_or_write
+
+       lda _fd765_is_user
+       ora a
+       push psw
+       cnz map_process_always
+
+       di
+
+       xra a
+       call fd765_tx                   ! send the final unused 0 byte
+                                       ! to fire off the command       
+       lhld _fd765_buffer
+       mvi e,0
+       jmp write_wait
+
+write_loop:
+       mov a,m
+       out 0x18
+       inx h
+       dcr e
+       jz write_wait2
+write_wait:
+       in 0x19
+       add a
+       jnc write_wait
+       add a
+       jp write_loop
+       jmp write_finished
+write_loop2:
+       mov a,m
+       out 0x18
+       inx h
+       dcr e
+       jz write_finished
+write_wait2:
+       in 0x19
+       add a
+       jnc write_wait2
+       add a
+       jp write_loop2
+write_finished:
+!      shld _fdc765_buffer
+       call _fd765_do_nudge_tc         ! Tell FDC we've finished
+       ei
+       call fd765_read_status
+       pop psw
+       rz
+       jmp map_kernel
+
+! Given an FDC opcode in A, sets up a read or write.
+
+setup_read_or_write:
+       call fd765_tx                   ! 0: send opcode (in A)
+       call send_head                  ! 1: specified head, drive #0
+       lda _fd765_track                ! 2: specified track
+       call fd765_tx
+       lda _fd765_head                 ! 3: specified head
+       call fd765_tx
+       lda _fd765_sector               ! 4: specified sector
+       mov b, a
+       call fd765_tx
+       mvi a,2                         ! 5: bytes per sector: 512
+       call fd765_tx
+       lda _fd765_sectors
+       add b                           ! add first sector
+       dcr a                           ! 6: last sector (*inclusive*)
+       call fd765_tx
+       mvi a,0x2A                      ! 7: Gap 3 length (5.25 and 3)
+                                       ! for 3.5 use 1B
+       call fd765_tx
+       ! We return with the final unused 0 value not written. We need all
+       ! the other stuff lined up before we write this.
+       ret
+
+_fd765_buffer:
+       .data2 0
+_fd765_is_user:
+       .data1 0
+_fd765_sectors:
+       .data1 0
+diskmotor:
+       .data1 0
+
+! Read the next sector ID off the disk.
+! (Only used for debugging.)
+
+_fd765_do_read_id:
+       mvi a,0x4a                              ! READ MFM ID
+       call fd765_tx
+       call send_head                          ! specified head, drive 0
+       jmp fd765_read_status
index 62b4bb8..a26e41a 100644 (file)
@@ -4,6 +4,7 @@
 #include <printf.h>
 #include <devtty.h>
 #include <blkdev.h>
+#include <devfdc765.h>
 
 uaddr_t ramtop = PROGTOP;
 uint16_t swap_dev = 0xFFFF;
@@ -41,6 +42,7 @@ void platform_interrupt(void)
 {
  tty_poll();
  timer_interrupt();
+ devfd_spindown();
 }
 
 /* Nothing to do for the map of init */
diff --git a/Kernel/platform-v85/mdrive.s b/Kernel/platform-v85/mdrive.s
new file mode 100644 (file)
index 0000000..bbb8796
--- /dev/null
@@ -0,0 +1,84 @@
+#include "../kernel-8080.def"
+!
+!      I/O mapped ram drive interface
+!
+       .sect .text
+
+       .define _rd_present
+
+_rd_present:
+       ldsi 2
+       lhlx                    ! L = board to probe for
+       lxi d,0
+       mov a,l
+       rlc                     ! Board into bits 3-5 (board select)
+       rlc
+       rlc
+       mov l,a
+       out 0xC7
+       xra a
+       out 0xC7
+       out 0xC7                ! We should now be pointed to the first byte
+       mvi a,0xAA
+       out 0xC6                ! Write AA55
+       mvi a,0x55
+       out 0xC6
+       mov a,l
+       out 0xC7
+       xra a
+       out 0xC7
+       out 0xC7
+       in 0xC6
+       cpi 0xAA
+       rnz
+       in 0xC6
+       cpi 0x55
+       rnz
+       inx d                   ! 1 = found
+       ret
+
+       .sect .common
+
+       .define _rd_input
+       .define _rd_output
+
+rd_setup:
+       ldsi 6                  ! Second argument (block number), allowing
+                               ! for extra call
+       lhlx
+       mov a,h
+       out 0xC7
+       mov a,l
+       out 0xC7
+       xra a
+       out 0xC7
+       ldsi 8                  ! Third argument (user page)
+       lhlx
+       mov a,l
+       mvi d,0                 ! 256 bytes to do
+       ldsi 4                  ! First argumnent (target0, allowing for call)
+       lhlx                    ! into HL
+
+       ora a                   ! Kernel mapped
+       rz
+       jmp map_process_a       ! User mapped or swap
+
+_rd_input:
+       call rd_setup
+rd_inloop:
+       in 0xC6
+       mov m,a
+       inx h
+       dcr d
+       jnz rd_inloop
+       jmp map_kernel
+
+_rd_output:
+       call rd_setup
+rd_outloop:
+       mov a,m
+       out 0xC6
+       inx h
+       dcr d
+       jnz rd_outloop
+       jmp map_kernel
diff --git a/Kernel/platform-v85/msm5832.c b/Kernel/platform-v85/msm5832.c
new file mode 100644 (file)
index 0000000..aca9e9f
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *     RTC driver for the OKI MSM 5832 mapped Dual systems style. Much like
+ *     the Trash 80 clock but indirectly mapped and with auto-hold.
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <rtc.h>
+
+extern uint8_t rtc_get(uint8_t port);
+
+uint8_t platform_rtc_secs(void)
+{
+    irqflags_t irqflags = di();
+    uint8_t secs = rtc_get(0);
+    irqrestore(irqflags);
+    return secs;
+}
+
+int platform_rtc_read(void)
+{
+    uint16_t len = sizeof(struct cmos_rtc);
+    irqflags_t irqflags;
+    struct cmos_rtc cmos;
+    uint8_t *p;
+    uint8_t r, y;
+
+    if (udata.u_count < len)
+        len = udata.u_count;
+
+    irqflags = di();
+
+    p = cmos.data.bytes;
+    y  = rtc_get(11);
+    if (y >= 0x70)
+        *p++ = 0x19;
+    else
+        *p++ = 0x20;
+    *p++ = y;
+    *p++ = rtc_get(9) & 0x1F;
+    *p++ = rtc_get(7) & 0x3F;
+    *p++ = rtc_get(4) & 0x3F;
+    *p++ = rtc_get(2) & 0x7F;
+    *p++ = rtc_get(0) & 0x7F;
+    irqrestore(irqflags);
+
+    cmos.type = CMOS_RTC_BCD;
+    if (uput(&cmos, udata.u_base, len) == -1)
+        return -1;
+    return len;
+}
+
+/* TODO */
+int platform_rtc_write(void)
+{
+       udata.u_error = EOPNOTSUPP;
+       return -1;
+}
diff --git a/Kernel/platform-v85/platform_fdc765.h b/Kernel/platform-v85/platform_fdc765.h
new file mode 100644 (file)
index 0000000..be3f317
--- /dev/null
@@ -0,0 +1,8 @@
+/*
+ *     Platform specifics
+ */
+
+#define FDC_MOTOR_TIMEOUT      10              /* Seconds */
+
+#define FDC765_MAX_FLOPPY      4               /* Four drives */
+
index 2384d03..9cb5ede 100644 (file)
@@ -445,3 +445,32 @@ writeloop:
        jnz writeloop
        pop b
        jmp map_kernel
+
+!
+!      Real time clock
+!
+       .sect .text
+
+       .define _rtc_get
+
+_rtc_get:
+       ldsi 2
+       lhlx            ! Get the first argument
+       mov a,l
+       out 0xF1        ! Set the accessed register
+       in 0xF0         ! Get the nibble back
+       ani 0x0F        ! High bits are undefined
+       mov e,a         ! Save
+       mov a,l
+       inr a
+       out 0xF1        ! Get the next register
+       in 0xF0         ! Get the nibble of data
+       rlc             ! Left four bits
+       rlc
+       rlc
+       rlc
+       ani 0xF0        ! Mask undefined bits
+       ora e           ! Add in the low bits we got
+       mov e,a         ! For a C return
+       mvi d,0
+       ret