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)
../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
pop b
dex b
jnk loop ...
+
+For the matching emulator see:
+
+https://github.com/EtchedPixels/V85
/* 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 */
#include <devfd.h>
#include <blkdev.h>
#include <devide.h>
+#include <devfdc765.h>
+#include <devrd.h>
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)
void device_init(void)
{
+ /* TODO: init the FDC and program step rate etc */
devide_init();
+ devrd_init();
}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+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);
--- /dev/null
+#
+!
+! 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
#include <printf.h>
#include <devtty.h>
#include <blkdev.h>
+#include <devfdc765.h>
uaddr_t ramtop = PROGTOP;
uint16_t swap_dev = 0xFFFF;
{
tty_poll();
timer_interrupt();
+ devfd_spindown();
}
/* Nothing to do for the map of init */
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * Platform specifics
+ */
+
+#define FDC_MOTOR_TIMEOUT 10 /* Seconds */
+
+#define FDC765_MAX_FLOPPY 4 /* Four drives */
+
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