diff --git a/Kernel/platform/platform-rcbus-tp128/Makefile b/Kernel/platform/platform-rcbus-tp128/Makefile new file mode 100644 index 0000000000..d91419506b --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/Makefile @@ -0,0 +1,99 @@ +CROSS_CCOPTS += -I../../dev/ -I../../dev/net/ + + +CSRCS = devtty.c +CSRCS += devices.c main.c wiznet.c + +DISCSRCS = discard.c + +ASRCS = tp128.S crt0.S ide.S tricks.S commonmem.S + +DISCARD_DSRCS = ../../dev/tinydisk_discard.c ../../dev/ds1302_discard.c ../../dev/tinyide_discard.c +DSRCS = ../../dev/tinydisk.c ../../dev/tinyide.c +DSRCS += ../../dev/ds1302.c +NSRCS = ../../dev/net/net_w5x00.c ../../dev/net/net_w5300.c ../../dev/net/net_native.c + +DASRCS = ../../dev/ds1302_rcbusu.S + +COBJS = $(CSRCS:.c=.o) +AOBJS = $(ASRCS:.S=.o) +NOBJS = $(patsubst ../../dev/net/%.c,%.o, $(NSRCS)) +DISCOBJS = $(DISCSRCS:.c=.o) +DISCARD_DOBJS = $(patsubst ../../dev/%.c,%.o, $(DISCARD_DSRCS)) +DOBJS = $(patsubst ../../dev/%.c,%.o, $(DSRCS)) +DAOBJS = $(patsubst ../../dev/%.S,%.o, $(DASRCS)) + +OBJS = $(COBJS) $(AOBJS) $(NOBJS) $(DISCOBJS) $(DOBJS) $(DISCARD_DOBJS) $(DAOBJS) + +JUNK = *.o + +all: $(OBJS) + +$(COBJS): %.o: %.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +$(DISCOBJS): %.o: %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(DOBJS): %.o: ../../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +$(DISCARD_DOBJS): %.o: ../../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(NOBJS): %.o: ../../dev/net/%.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +$(AOBJS): %.o: %.S + $(CROSS_AS) $(ASOPTS) $< + +$(DAOBJS): %.o: ../../dev/%.S + $(CROSS_AS) $(ASOPTS) $@ $< + +clean: + rm -f $(OBJS) $(JUNK) core *~ *.ihx *.bin *.o *.tmp *.s + +# +# Attach the kernel to the boot block +# +image: loader.bin + $(CROSS_LD) -b -S 0x0080 -f SsLDBbXC -o fuzix.bin \ + crt0.o commonmem.o tp128.o ../../start.o ../../version.o \ + ../../cpu-z80u/lowlevel-z80u.o ../../usermem.o tricks.o main.o discard.o \ + ../../timer.o ../../kdata.o devices.o ../../devio.o ../../filesys.o \ + ../../blk512.o ../../process.o ../../inode.o ../../syscall_exec.o \ + ../../syscall_exec16.o ../../syscall_fs.o ../../syscall_fs2.o \ + ../../syscall_fs3.o ../../syscall_proc.o ../../syscall_other.o \ + ../../syscall_net.o ../../network.o ../../tty.o ../../mm.o ../../mm/bankfixed.o \ + ../../mm/memalloc_none.o ../../swap.o ../../devsys.o devtty.o \ + ../../dev/tinydisk.o ../../dev/tinydisk_discard.o \ + ../../dev/tinyide.o ../../dev/tinyide_discard.o ide.o \ + ../../dev/ds1302.o ../../dev/ds1302_discard.o ../../dev/ds1302_rcbusu.o \ + ../../dev/net/net_native.o ../../dev/net/net_w5x00.o \ + ../../dev/net/net_w5300.o wiznet.o \ + /opt/fcc/lib/z80/libz80.a -m fuzix.tmpmap + perl -lpe '$$_=hex' fuzix.tmpmap | paste -d" " - fuzix.tmpmap | sort -n | cut -d" " -f 2- >../../fuzix.map + cp fuzix.bin ../../fuzix.bin + +# +# Compile up the boot block +# +loader.bin: loader.S + asz80 loader.S -o loader.o + ldz80 -b -C 0 loader.o -o loader.tmp + dd if=loader.tmp bs=256 skip=208 of=loader.bin + +IMAGES = $(FUZIX_ROOT)/Images/$(TARGET) + +diskimage: + # Make a blank disk image with partition + dd if=$(FUZIX_ROOT)/Standalone/filesystem-src/parttab.20M of=$(IMAGES)/disk.img bs=20152320 conv=sync + # Add the file system + dd if=$(IMAGES)/filesys8.img of=$(IMAGES)/disk.img bs=512 seek=2048 conv=notrunc + # Bootstrap (in two pieces to avoid the partition table) + dd if=loader.bin of=$(IMAGES)/disk.img bs=446 count=1 conv=notrunc + dd if=loader.bin of=$(IMAGES)/disk.img bs=512 skip=1 seek=1 conv=notrunc + # And kernel + dd if=../../fuzix.bin of=$(IMAGES)/disk.img bs=256 seek=48 conv=notrunc + # Make an emulator image of it + cat $(FUZIX_ROOT)/Standalone/filesystem-src/idehdr.20M $(IMAGES)/disk.img > $(IMAGES)/emu-ide.img diff --git a/Kernel/platform/platform-rcbus-tp128/README.md b/Kernel/platform/platform-rcbus-tp128/README.md new file mode 100644 index 0000000000..98302fe635 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/README.md @@ -0,0 +1,30 @@ +# Fuzix for RCbus with Tadeusz 128K RAM board + +This is a banked RAM board that gives us a couple of 32K banks for user +space. Not ideal but usable within limits. User space lies in the upper 32K +of memory as the banking banks the high not low space. + +## Supported Hardware + +- Onboard CF adapter +- SIO + +## TODO +- Add ACIA + +## Optional Devices + +- CTC card at 0x88 (including SIO B port speed control). Requires channels 2 and 3 are chained. +- DS1302 at 0x0C or 0xC0 + +## Time Sources + +This must be supplied by an external card or the system will hang on +boot. The CTC provides a proper interval timer and is recommended. The RTC +provides a very slow to read clock only. + +## Devices To Maybe Add + +- TMS9918A video (and vblank timer interrupt) +- PS/2 keyboard and mouse +- External timer on a carrier pin diff --git a/Kernel/platform/platform-rcbus-tp128/commonmem.S b/Kernel/platform/platform-rcbus-tp128/commonmem.S new file mode 100644 index 0000000000..c23cde8270 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/commonmem.S @@ -0,0 +1,9 @@ +; +; The common memory area traditionally starts with the udata and the +; interrupt stacks. As this is standard in almost all cases you can +; just include the standard implementation. +; + .common + +#include "../../cpu-z80u/std-commonmem.S" + diff --git a/Kernel/platform/platform-rcbus-tp128/config.h b/Kernel/platform/platform-rcbus-tp128/config.h new file mode 100644 index 0000000000..5671e09365 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/config.h @@ -0,0 +1,135 @@ +/* Enable to make ^Z dump the inode table for debug */ +#undef CONFIG_IDUMP +/* Enable to make ^A drop back into the monitor */ +#undef CONFIG_MONITOR +/* Profil syscall support (not yet complete) */ +#undef CONFIG_PROFIL +/* Multiple processes in memory at once */ +#define CONFIG_MULTI + +/* Select a banked memory set up */ +#define CONFIG_BANK_FIXED +/* This is the number of banks of user memory available (maximum) */ +#define MAX_MAPS 3 /* 128 KByte... minus the kernel one */ +/* How big is each bank - in our case 32K, 48K is actually more common. This + is hardware dependant */ +#define MAP_SIZE 0x8000 +/* How many banks do we have in our address space */ +#define CONFIG_BANKS 2 /* 2 x 32K */ + +/* + * Define the program loading area (needs to match kernel.def) + */ +#define PROGBASE 0x8000 /* Base of user */ +#define PROGLOAD 0x8000 /* Load and run here */ +#define PROGTOP 0xFE00 /* Top of program, base of U_DATA stash */ +#define PROC_SIZE 32 /* Memory needed per process including stash */ +/* + * Definitions for swapping. + */ +#define SWAPDEV (swap_dev) /* A variable for dynamic, or a device major/minor */ +extern uint16_t swap_dev; +#define SWAP_SIZE 0x40 /* 32K in 512 byte blocks */ +#define SWAPBASE 0x8000 /* We swap the lot in one, include the */ +#define SWAPTOP 0x10000UL /* vectors so its a round number of sectors */ + +#define MAX_SWAPS 16 /* Maximum number of swapped out processes. + As we use the default 15 process max this + is definitely sufficient (14 would do) */ +/* + * When the kernel swaps something it needs to map the right page into + * memory using map_for_swap and then turn the user address into a + * physical address. For a simple banked setup there is no conversion + * needed so identity map it. + */ +#define swap_map(x) ((uint8_t *)(x)) + +/* + * Disk devices. For now just the onboard CF adapter using TINYDISK + * to do all the work. See plt_ide.h for the hardware config, the rest + * is handled by the drivers not the port. + */ + +#define CONFIG_TD +#define CONFIG_TD_NUM 1 +#define CONFIG_TD_IDE +#define CONFIG_TINYIDE_8BIT +#define CONFIG_TINYIDE_INDIRECT +#define IDE_IS_8BIT(x) 1 + +#define BOOTDEVICENAMES "hd#" + +/* We will resize the buffers available after boot. This is the normal setting */ +#define CONFIG_DYNAMIC_BUFPOOL +/* Swap will be set up when a suitably labelled partition is seen */ +#define CONFIG_DYNAMIC_SWAP +/* Larger transfers (including process execution) should go directly not via + the buffer cache. For all small (eg bit) systems this is the right setting + as it avoids polluting the small cache with data when it needs to be full + of directory and inode information */ +#define CONFIG_LARGE_IO_DIRECT(x) 1 + +/* Specify this if there is a real time clock capable of reporting seconds. It + will be used to lock the kernel time better to reality. Other details like + Y2K support, or even supporting dates as well don't matter */ +#define CONFIG_RTC_DS1302 +#define CONFIG_RTC +/* Specify that there is a full real time clock that can supply the date and + time to the system. */ +#define CONFIG_RTC_FULL +/* Set this if the system has no proper real time clock (or has configurations + where it lacks one). This is not usually needed but for platforms it is also + see platform-sbcv2/main.c on what is needed */ +#define CONFIG_NO_CLOCK +/* Set how often we actually poll this RTC in ticks - 1 means always. On the + SBCv2 it's slow so don't sync often. If we have no timer tick then we will + read the RTC regularly as needed - and it'll suck accordingly regardless + of this setting */ +#define CONFIG_RTC_INTERVAL 100 +/* + * How fast does the clock tick (if present), or how many times a second do + * we simulate if not. For a machine without video 10 is a good number. If + * you have video you probably want whatever vertical sync/blank interrupt + * rate the machine has. For many systems it's whatever the hardware gives + * you. + * + * Note that this needs to be divisible by 10 and at least 10. If your clock + * is a bit slower you may need to fudge things somewhat so that the kernel + * gets 10 timer interrupt calls per second. + */ +#define TICKSPERSEC 10 /* Ticks per second */ + +/* Core networking support */ +#define CONFIG_NET +/* With a WizNet card */ +#define CONFIG_NET_WIZNET +#undef CONFIG_NET_W5100 +#define CONFIG_NET_W5300 + +/* + * The device (major/minor) for the console and boot up tty attached to + * init at start up. 512 is the major 2, so all the tty devices are + * 512 + n where n is the tty. + */ +#define BOOT_TTY (512 + 1) /* Set this to default device for stdio, stderr */ + /* In this case, the default is the first TTY device */ +/* + * If you have a mechanism to pass in a root device configuration then + * this holds the address of the buffer (eg a CP/M command line or similar). + * If the configuration is fixed then this can be a string holding the + * configuration. NULL means 'prompt the user'. + */ +#define CMDLINE NULL /* Location of root dev name */ + +/* Device parameters */ +#define NUM_DEV_TTY 2 /* How many tty devices does the platform support */ +#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */ +#define NBUFS 5 /* Number of block buffers. Must be 4+ and must match + kernel.def */ +#define NMOUNTS 4 /* Number of mounts at a time */ + +/* This can optionally be set to force a default baud rate, eg if the system + console should match a firmware set rate */ +#define TTY_INIT_BAUD B115200 + +#define plt_copyright() diff --git a/Kernel/platform/platform-rcbus-tp128/crt0.S b/Kernel/platform/platform-rcbus-tp128/crt0.S new file mode 100644 index 0000000000..93f0e85295 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/crt0.S @@ -0,0 +1,25 @@ + ; startup code + .common + + ; Load at 0x0080 +start: + di + ld sp, kstack_top + ; zero the BSS area + ld hl, __bss + ld de, __bss + 1 + ld bc, __bss_size - 1 + ld (hl), 0 + ldir +; Zero buffers area + ld hl, __buffers + ld de, __buffers + 1 + ld bc, __buffers_size - 1 + ld (hl), 0 + ldir + call init_early + call init_hardware + call _fuzix_main + di +stop: halt + jr stop diff --git a/Kernel/platform/platform-rcbus-tp128/devices.c b/Kernel/platform/platform-rcbus-tp128/devices.c new file mode 100644 index 0000000000..b5485d1c83 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/devices.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This table is the glue that holds all the kernel device driver + * logic together. Each device driver provides methods for + * open, close, read, write and ioctl, although it can opt to use + * defaults as well. + * + * The validdev function is the same for all platforms but has to live + * in the same file as the table. Just paste it into each. + */ + +struct devsw dev_tab[] = /* The device driver switch table */ +{ + /* 0: /dev/hd Hard disc block devices */ + { td_open, no_close, td_read, td_write, td_ioctl }, + /* 1: /dev/fd Floppy disc block devices */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 2: /dev/tty TTY devices */ + { tty_open, tty_close, tty_read, tty_write, tty_ioctl }, + /* 3: /dev/lpr Printer devices */ + { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 4: /dev/mem etc System devices (one offs) */ + { no_open, no_close, sys_read, sys_write, sys_ioctl }, + /* Pack to 7 with nxio if adding private devices and start at 8 */ +}; + +bool validdev(uint16_t dev) +{ + /* This is a bit uglier than needed but the right hand side is + a constant this way */ + if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) - 1) + return false; + else + return true; +} diff --git a/Kernel/platform/platform-rcbus-tp128/devtty.c b/Kernel/platform/platform-rcbus-tp128/devtty.c new file mode 100644 index 0000000000..cf47cff9a6 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/devtty.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include + +extern void sio2_otir(uint8_t port); + +static uint8_t tbuf1[TTYSIZ]; +static uint8_t tbuf2[TTYSIZ]; + +static uint8_t sleeping; + +struct s_queue ttyinq[NUM_DEV_TTY + 1] = { /* ttyinq[0] is never used */ + {NULL, NULL, NULL, 0, 0, 0}, + {tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ / 2}, + {tbuf2, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ / 2}, +}; + +tcflag_t termios_mask[NUM_DEV_TTY + 1] = { + 0, + /* FIXME CTS/RTS */ + CSIZE|CSTOPB|PARENB|PARODD|_CSYS, + CSIZE|CBAUD|CSTOPB|PARENB|PARODD|_CSYS, +}; + +#define SIO0_BASE 0x80 +#define SIOA_C (SIO0_BASE + 0) +#define SIOA_D (SIO0_BASE + 1) +#define SIOB_C (SIO0_BASE + 2) +#define SIOB_D (SIO0_BASE + 3) + +uint8_t sio_r[] = { + 0x03, 0xC1, + 0x04, 0xC4, + 0x05, 0xEA +}; + +static uint16_t siobaud[] = { + 0xC0, /* 0 */ + 0, /* 50 */ + 0, /* 75 */ + 0, /* 110 */ + 0, /* 134 */ + 0, /* 150 */ + 0xC0, /* 300 */ + 0x60, /* 600 */ + 0xC0, /* 1200 */ + 0x60, /* 2400 */ + 0x30, /* 4800 */ + 0x18, /* 9600 */ + 0x0C, /* 19200 */ + 0x06, /* 38400 */ + 0x04, /* 57600 */ + 0x02 /* 115200 */ +}; + +static void sio2_setup(uint_fast8_t minor, uint_fast8_t flags) +{ + struct termios *t = &ttydata[minor].termios; + uint8_t r; + uint8_t baud; + + used(flags); + + baud = t->c_cflag & CBAUD; + if (baud < B300) + baud = B300; + + /* Set bits per character */ + sio_r[1] = 0x01 | ((t->c_cflag & CSIZE) << 2); + + r = 0xC4; +#if 0 + /* Until we add CTC detect and support */ + if (ctc_present && minor == 2) { + CTC_CH1 = 0x55; + CTC_CH1 = siobaud[baud]; + if (baud > B600) /* Use x16 clock and CTC divider */ + r = 0x44; + } else +#endif + baud = B115200; + + t->c_cflag &= ~CBAUD; + t->c_cflag |= baud; + + if (t->c_cflag & CSTOPB) + r |= 0x08; + if (t->c_cflag & PARENB) + r |= 0x01; + if (t->c_cflag & PARODD) + r |= 0x02; + sio_r[3] = r; + sio_r[5] = 0x8A | ((t->c_cflag & CSIZE) << 1); +} + +void tty_setup(uint_fast8_t minor, uint_fast8_t flags) +{ + sio2_setup(minor, flags); + sio2_otir(SIO0_BASE + 2 * (minor - 1)); + /* We need to do CTS/RTS support */ +} + +int tty_carrier(uint_fast8_t minor) +{ + uint8_t c; + uint8_t port; + + port = SIO0_BASE + 2 * (minor - 1); + out(port, 0); + c = in(port); + if (c & 0x08) + return 1; + return 0; +} + +void tty_poll(void) +{ + static uint8_t old_ca, old_cb; + uint8_t ca, cb; + uint8_t progress; + + /* Check for an interrupt */ + out(SIOA_C, 0); + if (!(in(SIOA_C) & 2)) + return; + + /* FIXME: need to process error/event interrupts as we can get + spurious characters or lines on an unused SIO floating */ + do { + progress = 0; + out(SIOA_C, 0); // read register 0 + ca = in(SIOA_C); + /* Input pending */ + if (ca & 1) { + progress = 1; + tty_inproc(1, in(SIOA_D)); + } + /* Break */ + if (ca & 2) + out(SIOA_C, 2 << 5); + /* Output pending */ + if ((ca & 4) && (sleeping & 2)) { + tty_outproc(2); + sleeping &= ~2; + out(SIOA_C, 5 << 3); // reg 0 CMD 5 - reset transmit interrupt pending + } + /* Carrier changed */ + if ((ca ^ old_ca) & 8) { + if (ca & 8) + tty_carrier_raise(1); + else + tty_carrier_drop(1); + } + out(SIOB_C, 0); // read register 0 + cb = in(SIOB_C); + if (cb & 1) { + tty_inproc(2, in(SIOB_D)); + progress = 1; + } + if ((cb & 4) && (sleeping & 8)) { + tty_outproc(3); + sleeping &= ~8; + out(SIOB_C, 5 << 3); // reg 0 CMD 5 - reset transmit interrupt pending + } + if ((cb ^ old_cb) & 8) { + if (cb & 8) + tty_carrier_raise(2); + else + tty_carrier_drop(2); + } + } while(progress); +} + +void tty_putc(uint_fast8_t minor, uint_fast8_t c) +{ + uint8_t port = SIO0_BASE + 1 + 2 * (minor - 1); + out(port, c); +} + +void tty_sleeping(uint_fast8_t minor) +{ + sleeping |= (1 << minor); +} + +/* Be careful here. We need to peek at RR but we must be sure nobody else + interrupts as we do this. Really we want to switch to irq driven tx ints + on this platform I think. Need to time it and see + + An asm common level tty driver might be a better idea + + Need to review this we should be ok as the IRQ handler always leaves + us pointing at RR0 */ +ttyready_t tty_writeready(uint_fast8_t minor) +{ + irqflags_t irq; + uint8_t c; + uint8_t port; + + irq = di(); + port = SIO0_BASE+ 2 * (minor - 1); + out(port, 0); + c = in(port); + irqrestore(irq); + + if (c & 0x04) /* THRE? */ + return TTY_READY_NOW; + return TTY_READY_SOON; +} + +void tty_data_consumed(uint_fast8_t minor) +{ + used(minor); +} + +/* kernel writes to system console -- never sleep! */ +void kputchar(uint_fast8_t c) +{ + while(tty_writeready(TTYDEV & 0xFF) != TTY_READY_NOW); + if (c == '\n') + tty_putc(TTYDEV & 0xFF, '\r'); + while(tty_writeready(TTYDEV & 0xFF) != TTY_READY_NOW); + tty_putc(TTYDEV & 0xFF, c); +} + +int rctty_open(uint_fast8_t minor, uint16_t flag) +{ + if (minor > 2) { + udata.u_error = ENODEV; + return -1; + } + return tty_open(minor, flag); +} diff --git a/Kernel/platform/platform-rcbus-tp128/devtty.h b/Kernel/platform/platform-rcbus-tp128/devtty.h new file mode 100644 index 0000000000..f0188e8658 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/devtty.h @@ -0,0 +1,8 @@ +#ifndef _DEVTTY_H +#define _DEVTTY_H + +extern void tty_poll(void); +extern uint8_t timermsr; + + +#endif diff --git a/Kernel/platform/platform-rcbus-tp128/discard.c b/Kernel/platform/platform-rcbus-tp128/discard.c new file mode 100644 index 0000000000..4ba339f43d --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/discard.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +extern uint8_t ctc_present; +extern int strcmp(const char *, const char *); + +/* + * Everything in this file ends up in discard which means the moment + * we try and execute init it gets blown away. That includes any + * variables declared here so beware! + */ + +/* + * We get passed each kernel command line argument. if we return 1 then + * we claim it, if not it gets passed to init. It's perfectly acceptable + * to act on a match and return to also pass it to init if you need to. + */ +uint_fast8_t plt_param(unsigned char *p) +{ +#if 0 + /* Need to add to the SIO driver TODO */ + if (strcmp(p, "msr") == 0) { + timermsr = 1; + plt_tick_present = 1; + return 1; + } +#endif + return 0; +} + +/* + * Set up our memory mappings. This is not needed for simple banked memory + * only more complex setups such as 16K paging. + */ +void map_init(void) +{ +} + +/* + * Add all our free pages to the system. They are numbered in pairs as + * the system has some h/w logic to fake the 512K RAM/ROM enough for ROMWBW + * but not us. + */ +void pagemap_init(void) +{ + uint8_t i; + pagemap_add(2); + pagemap_add(3); + /* 0/1 are the kernel */ +} + +/* + * Called after interrupts are enabled in order to enumerate and set up + * any devices. In our case we set up the SIO and then probe the CF. + */ + +void device_init(void) +{ + /* Probe the timers first as IDE needs a timer of some form */ + /* DS1302 can be at 0xC0 or 0x0C */ + if (ds1302_init() == 0) { + rtc_port = 0x0C; + if (ds1302_init() == 0 && ctc_present == 0) + panic("no timer"); + } + ide_probe(); + sock_init(); +} diff --git a/Kernel/platform/platform-rcbus-tp128/fuzix.export b/Kernel/platform/platform-rcbus-tp128/fuzix.export new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Kernel/platform/platform-rcbus-tp128/ide.S b/Kernel/platform/platform-rcbus-tp128/ide.S new file mode 100644 index 0000000000..e3c3b0f0db --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/ide.S @@ -0,0 +1,41 @@ + + .export _devide_read_data + .export _devide_write_data + + .common + +#define IDE_REG_DATA 0x0010 + +ide_setup: + ld bc, IDE_REG_DATA ; port and count + ld a, (_td_raw) + or a + jp z, map_buffers + dec a + jp z, map_proc_always + ld a,(_td_page) + jp map_for_swap + +_devide_read_data: + pop de + pop hl + push hl + push de + push bc + call ide_setup + inir ; transfer first 256 bytes + inir ; transfer second 256 bytes + pop bc + jp map_kernel + +_devide_write_data: + pop de + pop hl + push hl + push de + push bc + call ide_setup + otir ; transfer first 256 bytes + otir ; transfer second 256 bytes + pop bc + jp map_kernel diff --git a/Kernel/platform/platform-rcbus-tp128/kernelu.def b/Kernel/platform/platform-rcbus-tp128/kernelu.def new file mode 100644 index 0000000000..61b842a253 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/kernelu.def @@ -0,0 +1,43 @@ +; FUZIX mnemonics for memory addresses etc +; +; +; The U_DATA address. If we are doing a normal build this is the start +; of common memory. We do actually have a symbol for udata so +; eventually this needs to go away +; +U_DATA__TOTALSIZE .equ 0x200 ; 256+256 bytes @ F000 +; +; Space for the udata of a switched out process within the bank of +; memory that it uses. Normally placed at the very top +; +U_DATA_STASH .equ 0xFE00 ; FE00-FFFF +; +; Z80 systems start program space at 0, and load at 0x100 so that the +; low 256 bytes are free for syscall vectors and the like, with some +; also used as a special case by the CP/M emulator. +; +PROGBASE .equ 0x8000 +PROGLOAD .equ 0x8000 +; +; CPU type +; 0 = CMOS Z80 +; 1 = NMOS Z80 (also works with CMOS) +; 2 = Z180 +; +; If either NMOS or CMOS may be present pick NMOS as the NMOS build +; contains extra code to work around an erratum n the NUMS Z80 +; +Z80_TYPE .equ 0 ; CMOS Z80 +; +; For special platforms that have external memory protection hardware +; Just say 0. +; +Z80_MMU_HOOKS .equ 0 +; +; Set this if the platform has swap enabled in config.h +; +#define CONFIG_SWAP 1 +; +; The number of disk buffers. Must match config.h +; +NBUFS .equ 5 diff --git a/Kernel/platform/platform-rcbus-tp128/loader.S b/Kernel/platform/platform-rcbus-tp128/loader.S new file mode 100644 index 0000000000..ffcac74652 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/loader.S @@ -0,0 +1,126 @@ +; +; The CP/M command blindly loads the first 24 blocks moves them to the +; top 12K and then runs whatever is indicated at 0xFFFE/FFFF +; + +DATA equ 0x10 +ERROR equ 0x11 +FEATURES equ 0x11 +COUNT equ 0x12 +LBA_0 equ 0x13 +LBA_1 equ 0x14 +LBA_2 equ 0x15 +LBA_3 equ 0x16 +STATUS equ 0x17 +CMD equ 0x17 + +ERR equ 0 +DRQ equ 3 +READY equ 6 +BUSY equ 7 + +ATA_READ equ 0x20 + +SIOA_C equ 0x80 +SIOA_D equ 0x81 +SIOB_C equ 0x82 +SIOB_D equ 0x83 + + .abs + .org 0xD000 + + + .ds 10240 ; boot sector and more for loader space + + ; We are at F800 +start: + ; A modern partition setup has lots of room for boot space, so we + ; just load the blocks after the loader (24-147). We can't overlap + ; because of the FFFE/FFFF thing. + + ld sp, #0xFE00 + ld hl, hello + call serstr + + ld a,1 + out (0x38),a + + call ide_ready + ld a, 0xE0 + out (LBA_3),a + call ide_ready + xor a + out (LBA_2),a + out (LBA_1),a + + ld de, 0x7B18 ; sectors 24-119 + ld hl, 0x0000 ; load address +load_loop: + call ide_ready + ld a,e + out (LBA_0),a + ld a, 1 + out (COUNT),a + ld a, ATA_READ + out (CMD),a + nop + call ide_wait_drq + ld bc, DATA + inir + inir + ld a, '.' + call serout + inc e + dec d + jr nz, load_loop + ld hl, gogogo + call serstr + jp 0x0080 + +ide_ready: + in a,(STATUS) + bit BUSY,a + jr nz, ide_ready + bit READY,a + jr z, ide_ready + ret +ide_wait_drq: + in a,(STATUS) + bit DRQ,a + jr z,ide_wait_drq + ret + + +serout: + push af + ; wait for transmitter to be idle +ocloop_sio: + xor a ; read register 0 + out (SIOA_C), a + in a,(SIOA_C) ; read Line Status Register + and #0x04 ; get THRE bit + jr z,ocloop_sio + ; now output the char to serial port + pop af + out (SIOA_D),a + ret + +serstr: + ld a,(hl) + or a + ret z + inc hl + call serout + jr serstr + +hello: + .ascii 'TP128 RCBUS LOADER 0.1' + .byte 13,10,13,10 + .byte 0 +gogogo: + .byte 13,10 + .ascii 'Executing FUZIX...' + .byte 13,10,0 + + .org 0xFFFE + .word start diff --git a/Kernel/platform/platform-rcbus-tp128/main.c b/Kernel/platform/platform-rcbus-tp128/main.c new file mode 100644 index 0000000000..336855d3e2 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/main.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +uint16_t ramtop = PROGTOP; +uint16_t swap_dev = 0xFFFF; +uint8_t timermsr = 0; + +uint8_t plt_tick_present; +uint8_t ctc_present; + +/* Real time clock support for DS1302 driver - port and other pins */ +uint16_t rtc_port = 0xC0; +uint8_t rtc_shadow; + +#define CTC_CH0 0x88 +#define CTC_CH1 0x89 +#define CTC_CH2 0x8A +#define CTC_CH3 0x8B + +/* + * This routine is called continually when the machine has nothing else + * it needs to execute. On a machine with entirely interrupt driven + * hardware this could just halt for interrupt. + * + * The SBCv2 has no interrupts so we must call sync_clock(), and as the + * PropIO tty is not interrupt driven we also poll the ttys. This gives + * us a nice interactive feel when the machine is idle, even if a polled + * tty can otherwise suck. + */ +void plt_idle(void) +{ + /* Disable interrupts so we don't accidentally process a polled tty + and interrupt call at once and make a mess */ + irqflags_t irq = di(); + sync_clock(); + tty_poll(); + /* Restore prior state. */ + irqrestore(irq); +} + +/* + * This routine is called from the interrupt handler code to process + * interrupts. All of the nasty stuff (register saving, bank switching, + * reti instructions) is dealt with for you. + */ + +static int16_t timerct; + +/* Call timer_interrupt at 10Hz */ +static void timer_tick(uint8_t n) +{ + timerct += n; + while (timerct >= 20) { + timer_interrupt(); + timerct -= 20; + } +} + +void plt_interrupt(void) +{ + tty_poll(); + if (ctc_present) { + uint8_t n = 255 - in(CTC_CH3); + out(CTC_CH3, 0x47); + out(CTC_CH3, 0xFF); + timer_tick(n); + } +#ifdef CONFIG_NET_W5100 + w5x00_poll(); +#endif +#ifdef CONFIG_NET_W5300 + w5300_poll(); +#endif +} + +/* This points to the last buffer in the disk buffers. There must be at least + four buffers to avoid deadlocks. */ +struct blkbuf *bufpool_end = bufpool + NBUFS; + +/* + * We pack discard into the memory image is if it were just normal + * code but place it at the end after the buffers. When we finish up + * booting we turn everything from the buffer pool to common into + * buffers. This blows away the _DISCARD segment. + */ +void plt_discard(void) +{ + extern uint16_t _code; + uint16_t discard_size = (uint16_t)_code - (uint16_t)bufpool_end; + bufptr bp = bufpool_end; + + discard_size /= sizeof(struct blkbuf); + + kprintf("%d buffers added\n", discard_size); + + bufpool_end += discard_size; + + memset( bp, 0, discard_size * sizeof(struct blkbuf) ); + + for( bp = bufpool + NBUFS; bp < bufpool_end; ++bp ){ + bp->bf_dev = NO_DEVICE; + bp->bf_busy = BF_FREE; + } +} diff --git a/Kernel/platform/platform-rcbus-tp128/plt_ide.h b/Kernel/platform/platform-rcbus-tp128/plt_ide.h new file mode 100644 index 0000000000..79a718f834 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/plt_ide.h @@ -0,0 +1,14 @@ +#define data 0x10 +#define error 0x11 +#define count 0x12 +#define sec 0x13 +#define cyll 0x14 +#define cylh 0x15 +#define devh 0x16 +#define cmd 0x17 +#define status 0x17 + +#define IDE_REG_DATA 0x0010 + +#define ide_read(x) in(x) +#define ide_write(x,y) out(x,y) diff --git a/Kernel/platform/platform-rcbus-tp128/rules.mk b/Kernel/platform/platform-rcbus-tp128/rules.mk new file mode 100644 index 0000000000..937e27e0dc --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/rules.mk @@ -0,0 +1,2 @@ +#CROSS_CCOPTS += --peep-file $(FUZIX_ROOT)/Kernel/cpu-z80/rst.peep +CROSS_CCOPTS += -Os diff --git a/Kernel/platform/platform-rcbus-tp128/target.mk b/Kernel/platform/platform-rcbus-tp128/target.mk new file mode 100644 index 0000000000..e3cf8fab9c --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/target.mk @@ -0,0 +1,6 @@ +# +# Tell the build system what processor type we are using +# +export CPU = z80u +export USERCPU = z80 + diff --git a/Kernel/platform/platform-rcbus-tp128/tp128.S b/Kernel/platform/platform-rcbus-tp128/tp128.S new file mode 100644 index 0000000000..542d9ff7dd --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/tp128.S @@ -0,0 +1,382 @@ +; +; Tadeus 128K RAM support. Thiis has a slightly odd mapping scheme +; where the upper 32K is page between 3 banks but the lower bank is +; either fixed or ROM (with RAM write through) +; +; MMU setting via 0xC0 bits 1:0 +; 00 Low ROM (writes bank 0) High bank 1 +; 01 Low bank 0 High bank 1 +; 10 Low bank 0 High bank 2 +; 11 Low bank 0 High bank 3 +; +; This first chunk is mostly boilerplate to adjust for each +; system. +; + + .export init_early + .export init_hardware + .export _program_vectors + .export map_buffers + .export map_kernel + .export map_kernel_di + .export map_kernel_restore + .export map_proc + .export map_proc_a + .export map_proc_always + .export map_proc_always_di + .export map_save_kernel + .export map_restore + .export map_for_swap + .export plt_interrupt_all + .export _kernel_flag + .export _int_disabled + + ; exported debugging tools + .export _plt_monitor + .export _plt_reboot + .export outchar + +#include "kernelu.def" +#include "../../cpu-z80u/kernel-z80.def" + +; Base address of SIO/2 chip 0x80 +SIOA_C .equ 0x80 +SIOA_D .equ SIOA_C+1 +SIOB_C .equ SIOA_C+2 +SIOB_D .equ SIOA_C+3 + +CTC_CH0 .equ 0x88 ; CTC channel 0 and interrupt vector +CTC_CH1 .equ 0x89 ; CTC channel 1 (serial B) +CTC_CH2 .equ 0x8A ; CTC channel 2 (timer) +CTC_CH3 .equ 0x8B ; CTC channel 3 (timer count) + +; +; Buffers (we use asm to set this up as we need them in a special segment +; so we can recover the discard memory into the buffer pool +; + + .buffers + + .export _bufpool + +_bufpool: + .ds BUFSIZE * NBUFS + +; ----------------------------------------------------------------------------- +; COMMON MEMORY BANK (kept even when we task switch) +; ----------------------------------------------------------------------------- + .common +; +; Interrupt flag. This needs to be in common memory for most memory +; models. It starts as 1 as interrupts start off. +; +_int_disabled: + .byte 1 +; +; This method is invoked early in interrupt handling before any +; complex handling is done. It's useful on a few platforms but +; generally a ret is all that is needed +; +plt_interrupt_all: + ret + + .code ; so this is above 32K (TODO) +; +; If you have a ROM monitor you can get back to then do so, if not +; fall into reboot. +; +_plt_monitor: +; +; Reboot the system if possible, halt if not. On a system where the +; ROM promptly wipes the display you may want to delay or wait for +; a keypress here (just remember you may be interrupts off, no kernel +; mapped so hit the hardware). +; +_plt_reboot: + di + ld a, 0x43 + out (CTC_CH0),a + out (CTC_CH1),a + out (CTC_CH2),a + out (CTC_CH3),a + xor a + out (0x30),a ; ROM low + rst 0 + +; ----------------------------------------------------------------------------- +; KERNEL MEMORY BANK (may be below 0x8000, only accessible when the kernel is +; mapped) +; ----------------------------------------------------------------------------- + .code +; +; This routine is called very early, before the boot code shuffles +; things into place. +; +init_early: + ret + +; +; Serial I/O helper for SIO +; + .export _sio2_otir + +_sio2_otir: + pop de + pop hl + push hl + push de + push bc + ld b,0x06 + ld c,l + ld hl,_sio_r + otir + pop bc + ret + + +; ----------------------------------------------------------------------------- +; DISCARD is memory that will be recycled when we exec init +; ----------------------------------------------------------------------------- + .discard +; +; After the kernel has shuffled things into place this code is run. +; It's the best place to breakpoint or trace if you are not sure your +; kernel is loading and putting itself into place properly. +; +; It's required jobs are to set up the vectors, ramsize (total RAM), +; and procmem (total memory free to processs), as well as setting the +; interrupt mode but *not* enabling interrupts. Many platforms also +; program up support hardware like PIO and CTC devices here. +; +init_hardware: + ld hl, 128 ; 128K total + ld (_ramsize), hl + ld de, 64 ; 64 for the kernel bank + or a ; maybe use ROM for kernel too ? + sbc hl,de + ld (_procmem), hl + + ; set up interrupt vectors for the kernel + + ; + ; Defense in depth - shut everything up first + ; + + ld a, 0x43 + out (CTC_CH0),a ; set CH0 mode + out (CTC_CH1),a ; set CH1 mode + out (CTC_CH2),a ; set CH2 mode + out (CTC_CH3),a ; set CH3 mode + + ; now install the interrupt vector at 0x0038 + + ld a, 0xC3 ; JP instruction + ld (0x0038), a + ld hl, interrupt_handler + ld (0x0039), hl + + ld (0x0000), a + ld hl, null_handler ; to Our Trap Handler + ld (0x0001), hl + + ; + ; Probe for a CTC + ; + + ld a, 0x47 ; CTC 2 as counter + out (CTC_CH2),a + ld a, 0xAA ; Set a count + out (CTC_CH2),a + in a,(CTC_CH2) + cp 0xAA + jr z, maybe_ctc + cp 0xA9 ; Should be one less + jr nz, no_ctc +maybe_ctc: + ld a, 0x07 + out (CTC_CH2),a + ld a, 2 + out (CTC_CH2),a + + ; We are now counting down from 2 very fast, so should only see + ; those values on the bus + + ld b, 0 +ctc_check: + in a,(CTC_CH2) + and 0xFC + jr nz, no_ctc + djnz ctc_check + + ; + ; Looks like we have a CTC + ; + +have_ctc: + ld a, 1 + ld (_ctc_present),a + ld (_plt_tick_present),a + + ; + ; Set up timer for 200Hz + ; + + ld a, 0xB5 + out (CTC_CH2),a + ld a, 144 + out (CTC_CH2),a ; 200 Hz + + ; + ; Set up counter CH3 for official SIO (the SC110 sadly can't be + ; used this way). + + ld a, 0x47 + out (CTC_CH3),a + ld a, 255 + out (CTC_CH3),a + +no_ctc: + ld hl, sio_setup + ld bc, 0xA00 + SIOA_C ; 10 bytes to SIOA_C + otir + ld hl, sio_setup + ld bc, 0x0A00 + SIOB_C ; and to SIOB_C + otir + im 1 ; set CPU interrupt mode + + ret + +sio_setup: + .byte 0x00 + .byte 0x18 ; Reset + .byte 0x04 + .byte 0xC4 + .byte 0x01 + .byte 0x18 + .byte 0x03 + .byte 0xE1 + .byte 0x05 + .byte 0xEA ; RTS low + +; +; Bank switching unsurprisingly must be in common memory space so it's +; always available. +; + .commondata + +mapreg: .byte 0 ; Our map register is write only so keep a copy +mapsave: .byte 0 ; Saved copy of the previous map (see map_save) + +_kernel_flag: + .byte 1 ; We start in kernel mode + +; +; This is invoked with a NULL argument at boot to set the kernel +; vectors and then elsewhere in the kernel when the kernel knows +; a bank may need vectors writing to it. +; +_program_vectors: + ; Vectors live in fixed low space + ret + +; +; Mapping set up for the SC720 +; +; The top 32K bank holds kernel code and pieces of common memory +; The lower 32K is switched between the various user banks. +; +; The _di versions of the functions are called when we know interrupts +; are definitely off. In our case it's not useful information so both +; symbols end up at the same code. +; +map_buffers: + ; for us no difference. We could potentially use a low 32K bank + ; for buffers but it's not clear it would gain us much value +map_kernel_di: +map_kernel: +map_kernel_restore: ; On banked kernels this is different + ; on unbanked it is just a map_kernel + push af + ld a, 0x01 ; RAM 0 low 1 high (kernel) + ld (mapreg),a + out (0x38), a + pop af + ret + ; map_proc is called with HL either NULL or pointing to the + ; page mapping. Unlike the other calls it's allowed to trash AF +map_proc: + ld a, h + or l + jr z, map_kernel +map_proc_hl: + ld a, (hl) ; and fall through + ; + ; With a simple bank switching system you need to provide a + ; method to switch to the bank in A without corrupting any + ; other registers. The stack is safe in common memory. + ; For swap you need to provide what for simple banking is an + ; identical routine. +map_for_swap: +map_proc_a: ; used by bankfork + ld (mapreg), a ; bank + out (0x38), a + ret + + ; + ; Map the current process into memory. We do this by extracting + ; the bank value from u_page. + ; +map_proc_always_di: +map_proc_always: + push af + ld a, (_udata + U_DATA__U_PAGE) + ld (mapreg),a + out (0x38),a + pop af + ret + + ; + ; Save the existing mapping and switch to the kernel. + ; The place you save it to needs to be in common memory as you + ; have no idea what bank is live. Alternatively defer the save + ; until you switch to the kernel mapping + ; +map_save_kernel: push af + ld a, (mapreg) + ld (mapsave), a + ld a, 0x01 ; Kernel map (see map_kernel) + ld (mapreg),a + out (0x38),a + pop af + ret + ; + ; Restore the saved bank. Note that you don't need to deal with + ; stacking of banks (we never recursively use save/restore), and + ; that we may well call save and decide not to call restore. + ; +map_restore: + push af + ld a, (mapsave) + ld (mapreg), a + out (0x38), a + pop af + ret + + ; + ; Used for low level debug. Output the character in A without + ; corrupting other registers. May block. Interrupts and memory + ; state are undefined + ; +outchar: + push af +ocloop_sio: + xor a ; read register 0 + out (0x80), a + in a,(0x80) ; read Line Status Register + and 0x04 ; get THRE bit + jr z,ocloop_sio + ; now output the char to serial port + pop af + out (0x81),a + ret + diff --git a/Kernel/platform/platform-rcbus-tp128/tricks.S b/Kernel/platform/platform-rcbus-tp128/tricks.S new file mode 100644 index 0000000000..644bd70dce --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/tricks.S @@ -0,0 +1,23 @@ +; +; For simple banked systems there is a standard implementation. The +; only reason to do otherwise is for speed. A custom bank logic aware +; bank to bank copier will give vastly better fork() performance. +; +#include "kernelu.def" +#include "../../cpu-z80u/kernel-z80.def" + +; +; All of the fixed bank support is available as a library routine, +; however it is a performance sensitive area. Start with +; +#include "../../lib/z80ufixedbank.S" +; +; As well as using "usermem_std-z80.rel" in your link file for the +; userspace access operations. +; +; We can also use the standard fast user copiers because all the +; kernel objects we need to copy to/from userspace exist in the +; user mapping. +; +#include "../../lib/z80uuser1.s" + diff --git a/Kernel/platform/platform-rcbus-tp128/wiznet.c b/Kernel/platform/platform-rcbus-tp128/wiznet.c new file mode 100644 index 0000000000..0a604dd335 --- /dev/null +++ b/Kernel/platform/platform-rcbus-tp128/wiznet.c @@ -0,0 +1,233 @@ +#include + +/* + * Driver support for Wiznet 5100 and 5300 carrier modules. The + * 5500 on SPI is currently only enabled on the rcbus-z180 platform + */ + +/* + * Drive a WizNet 5100 using indirect mode at ports 0x28-0x2B + */ + +#ifdef CONFIG_NET + +#include +#include +#include + +#ifdef CONFIG_NET_W5100 +#include + +#define MR 0x0000 +#define MR_RESET 0x80 +#define MR_PB 0x10 +#define MR_PPPOE 0x08 +#define MR_AUTOINC 0x02 +#define MR_INDIRECT 0x01 + +/* Core helpers: platform supplied */ + +#define mr 0x28 +#define idm_ar0 0x29 +#define idm_ar1 0x2A +#define idm_dr 0x2B + +/* We assume indirect, autoinc is always set */ +uint8_t w5x00_readcb(uint16_t off) +{ + irqflags_t irq = di(); + uint8_t r; + out(idm_ar0, off >> 8); + out(idm_ar1, off); + r = in(idm_dr); + irqrestore(irq); + return r; +} + +uint8_t w5x00_readsb(uint8_t s, uint16_t off) +{ + off += ((uint16_t)s) << 8; + return w5x00_readcb(off); +} + +uint16_t w5x00_readcw(uint16_t off) +{ + irqflags_t irq = di(); + uint16_t n; + out(idm_ar0, off >> 8); + out(idm_ar1, off); + n = ((uint16_t)in(idm_dr)) << 8; + n |= in(idm_dr); + irqrestore(irq); + return n; +} + +uint16_t w5x00_readsw(uint8_t s, uint16_t off) +{ + off += ((uint16_t)s) << 8; + return w5x00_readcw(off); +} + +void w5x00_bread(uint16_t bank, uint16_t off, void *pv, uint16_t n) +{ + irqflags_t irq = di(); + uint8_t *p = pv; + off += bank; + out(idm_ar0, off >> 8); + out(idm_ar1, off); + while(n--) + *p++ = in(idm_dr); + irqrestore(irq); +} + +void w5x00_breadu(uint16_t bank, uint16_t off, void *pv, uint16_t n) +{ + irqflags_t irq = di(); + uint8_t *p = pv; + off += bank; + out(idm_ar0, off >> 8); + out (idm_ar1, off); + while(n--) + uputc(in(idm_dr), p++); + irqrestore(irq); +} + +void w5x00_writecb(uint16_t off, uint8_t n) +{ + irqflags_t irq = di(); + out(idm_ar0, off >> 8); + out(idm_ar1, off); + out(idm_dr, n); + irqrestore(irq); +} + +void w5x00_writesb(uint8_t sock, uint16_t off, uint8_t n) +{ + return w5x00_writecb((sock << 8) + off, n); +} + +void w5x00_writecw(uint16_t off, uint16_t n) +{ + irqflags_t irq = di(); + out(idm_ar0, off >> 8); + out(idm_ar1, off); + out(idm_dr, n >> 8); + out(idm_dr, n); + irqrestore(irq); +} + +void w5x00_writesw(uint8_t sock, uint16_t off, uint16_t n) +{ + return w5x00_writecw((sock << 8) + off, n); +} + +void w5x00_bwrite(uint16_t bank, uint16_t off, void *pv, uint16_t n) +{ + irqflags_t irq = di(); + uint8_t *p = pv; + off += bank; + out(idm_ar0, off >> 8); + out(idm_ar1, off); + while(n--) + out(idm_dr, *p++); + irqrestore(irq); +} + +void w5x00_bwriteu(uint16_t bank, uint16_t off, void *pv, uint16_t n) +{ + irqflags_t irq = di(); + uint8_t *p = pv; + off += bank; + out(idm_ar0, off >> 8); + out(idm_ar1, off); + while(n--) + out(idm_dr, ugetc(p++)); + irqrestore(irq); +} + +void w5x00_setup(void) +{ + out(mr, MR_AUTOINC|MR_INDIRECT); +} + +#else + +/* Wiznet 5300 has its own interface */ +#include + +#define mr0 0x28 +#define mr1 0x29 +#define idm_arh 0x2A +#define idm_arl 0x2B +#define idm_drh 0x2C +#define idm_drl 0x2D + +/* + * Not well documented: the drh/drl pair must always be used high then low, + * as if they are read the other way around the FIFO gets confused + */ + +/* Read a W5300 register in big endian order */ +uint16_t w5300_read(uint16_t off) +{ + irqflags_t irq = di(); + uint16_t r; + out(idm_arh, off >> 8); + out(idm_arl, off); + r = in(idm_drh) << 8; + r |= in(idm_drl); + irqrestore(irq); + return r; +} + +/* Read a W5300 register without byte swapping */ +uint16_t w5300_readn(uint16_t off) +{ + irqflags_t irq = di(); + uint16_t r; + out(idm_arh, off >> 8); + out(idm_arl, off); + r = in(idm_drh); + r |= in(idm_drl) << 8; + irqrestore(irq); + return r; +} + +/* Write a W5300 register in big endian order */ +void w5300_write(uint16_t off, uint16_t n) +{ + irqflags_t irq = di(); + out(idm_arh, off >> 8); + out(idm_arl, off); + out(idm_drh, n >> 8); + out(idm_drl, n); + irqrestore(irq); +} + +/* Write a W5300 register without byte swapping */ +void w5300_writen(uint16_t off, uint16_t n) +{ + irqflags_t irq = di(); + out(idm_arh, off >> 8); + out(idm_arl, off); + out(idm_drh, n); + out(idm_drl, n >> 8); + irqrestore(irq); +} + +/* + * The core W5300 code expects the FIFO to be native endian and the + * registers to be kept big endian. We also need to set extended hold + * because 7ns data hold is a bit too quick for the poor old Z80. + */ +void w5300_setup(void) +{ + uint16_t i; + out(mr1, 0x80); /* Reset */ + for (i = 0; i < 1000; i++); + out(mr0, in(mr0) | 0x05); /* FIFO in little endian, extended hold */ + out(mr1, 0x01); /* Indirect mode */ +} + +#endif +#endif