From af87405630136fead5af5a10e47f952f0be89da1 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 28 Feb 2019 21:33:28 +0000 Subject: [PATCH] v85: add floppy, ramdisc and rtc Based on the emulations added to the emulator. This hopefully gives people some more examples to work with when writing stuff. --- Kernel/platform-v85/Makefile | 7 +- Kernel/platform-v85/README | 4 + Kernel/platform-v85/config.h | 4 +- Kernel/platform-v85/devices.c | 19 +- Kernel/platform-v85/devrd.c | 66 +++++ Kernel/platform-v85/devrd.h | 8 + Kernel/platform-v85/fdc765.s | 350 ++++++++++++++++++++++++++ Kernel/platform-v85/main.c | 2 + Kernel/platform-v85/mdrive.s | 84 +++++++ Kernel/platform-v85/msm5832.c | 59 +++++ Kernel/platform-v85/platform_fdc765.h | 8 + Kernel/platform-v85/v85.s | 29 +++ 12 files changed, 630 insertions(+), 10 deletions(-) create mode 100644 Kernel/platform-v85/devrd.c create mode 100644 Kernel/platform-v85/devrd.h create mode 100644 Kernel/platform-v85/fdc765.s create mode 100644 Kernel/platform-v85/mdrive.s create mode 100644 Kernel/platform-v85/msm5832.c create mode 100644 Kernel/platform-v85/platform_fdc765.h diff --git a/Kernel/platform-v85/Makefile b/Kernel/platform-v85/Makefile index 4ac258b3..5b77ad22 100644 --- a/Kernel/platform-v85/Makefile +++ b/Kernel/platform-v85/Makefile @@ -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 diff --git a/Kernel/platform-v85/README b/Kernel/platform-v85/README index be6e7cb2..5088493f 100644 --- a/Kernel/platform-v85/README +++ b/Kernel/platform-v85/README @@ -21,3 +21,7 @@ Perhaps repeats of: pop b dex b jnk loop ... + +For the matching emulator see: + +https://github.com/EtchedPixels/V85 diff --git a/Kernel/platform-v85/config.h b/Kernel/platform-v85/config.h index f8595bb3..fa9699b1 100644 --- a/Kernel/platform-v85/config.h +++ b/Kernel/platform-v85/config.h @@ -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 */ diff --git a/Kernel/platform-v85/devices.c b/Kernel/platform-v85/devices.c index 317f270d..1f17178d 100644 --- a/Kernel/platform-v85/devices.c +++ b/Kernel/platform-v85/devices.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include 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 index 00000000..5e12930b --- /dev/null +++ b/Kernel/platform-v85/devrd.c @@ -0,0 +1,66 @@ +/* + * RAM drive (512K) + */ + +#include +#include +#include +#include + +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 index 00000000..a93c6078 --- /dev/null +++ b/Kernel/platform-v85/devrd.h @@ -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 index 00000000..2c23ccab --- /dev/null +++ b/Kernel/platform-v85/fdc765.s @@ -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 diff --git a/Kernel/platform-v85/main.c b/Kernel/platform-v85/main.c index 62b4bb8b..a26e41a9 100644 --- a/Kernel/platform-v85/main.c +++ b/Kernel/platform-v85/main.c @@ -4,6 +4,7 @@ #include #include #include +#include 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 index 00000000..bbb8796f --- /dev/null +++ b/Kernel/platform-v85/mdrive.s @@ -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 index 00000000..aca9e9f4 --- /dev/null +++ b/Kernel/platform-v85/msm5832.c @@ -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 +#include +#include +#include + +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 index 00000000..be3f3171 --- /dev/null +++ b/Kernel/platform-v85/platform_fdc765.h @@ -0,0 +1,8 @@ +/* + * Platform specifics + */ + +#define FDC_MOTOR_TIMEOUT 10 /* Seconds */ + +#define FDC765_MAX_FLOPPY 4 /* Four drives */ + diff --git a/Kernel/platform-v85/v85.s b/Kernel/platform-v85/v85.s index 2384d031..9cb5edee 100644 --- a/Kernel/platform-v85/v85.s +++ b/Kernel/platform-v85/v85.s @@ -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 -- 2.34.1