From fe0dbfa3f572257bbba4fcbed819198ee9515abc Mon Sep 17 00:00:00 2001 From: ajcasado Date: Wed, 8 Jan 2025 19:41:09 +0100 Subject: [PATCH 1/9] Remove unused files --- Kernel/platform/platform-cpc6128/devinput.c | 51 --------------------- Kernel/platform/platform-cpc6128/devinput.h | 2 - 2 files changed, 53 deletions(-) delete mode 100644 Kernel/platform/platform-cpc6128/devinput.c delete mode 100644 Kernel/platform/platform-cpc6128/devinput.h diff --git a/Kernel/platform/platform-cpc6128/devinput.c b/Kernel/platform/platform-cpc6128/devinput.c deleted file mode 100644 index 34e287b76d..0000000000 --- a/Kernel/platform/platform-cpc6128/devinput.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Joysticks off the AY sound chip - */ -#include -#include -#include -#include - -static char buf[32]; - -static struct s_queue kqueue = { - buf, buf, buf, sizeof(buf), 0, sizeof(buf) / 2 -}; - -/* Queue a character to the input device */ -void queue_input(uint8_t c) -{ - insq(&kqueue, c); - wakeup(&kqueue); -} - - -int plt_input_read(uint8_t *slot) -{ - uint8_t r, k; - if (remq(&kqueue, &r)) { - remq(&kqueue, &k); - *slot++ = KEYPRESS_CODE | r; - *slot = k; - return 2; - } - return 0; -} - -void plt_input_wait(void) -{ - psleep(&kqueue); /* We wake this on timers so it works for sticks */ -} - -int plt_input_write(uint8_t flag) -{ - flag; - udata.u_error = EINVAL; - return -1; -} - -void poll_input(void) -{ - wakeup(&kqueue); -} - \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128/devinput.h b/Kernel/platform/platform-cpc6128/devinput.h deleted file mode 100644 index 981f702450..0000000000 --- a/Kernel/platform/platform-cpc6128/devinput.h +++ /dev/null @@ -1,2 +0,0 @@ -extern void queue_input(uint8_t); -extern void poll_input(void); From 001112003685b83a22663e333159aeac088cee03 Mon Sep 17 00:00:00 2001 From: ajcasado Date: Wed, 8 Jan 2025 19:42:25 +0100 Subject: [PATCH 2/9] Remove unused sources from Makefile --- Kernel/platform/platform-cpc6128/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Kernel/platform/platform-cpc6128/Makefile b/Kernel/platform/platform-cpc6128/Makefile index 802b937c8d..cd09e33d16 100644 --- a/Kernel/platform/platform-cpc6128/Makefile +++ b/Kernel/platform/platform-cpc6128/Makefile @@ -1,7 +1,7 @@ CSRCS = devtty.c devices.c main.c CDSRCS = discard.c -DSRCS = ../../dev/tinyide.c ../../dev/tinydisk.c ../../dev/devfdc765.c ../../dev/tinysd.c ../../dev/ch375.c -DDSRCS = ../../dev/tinyide_discard.c ../../dev/tinydisk_discard.c ../../dev/tinysd_discard.c +DSRCS = ../../dev/tinyide.c ../../dev/tinydisk.c ../../dev/devfdc765.c ../../dev/ch375.c +DDSRCS = ../../dev/tinyide_discard.c ../../dev/tinydisk_discard.c DZSRCS = ../../dev/cpc/cpcide.c ../../dev/cpc/cpckeyboard.c ../../dev/cpc/devinput.c ../../dev/cpc/albireo.c DDZSRCS = ASRCS = crt0.s cpc6128.s cpcvideo.s fdc765.s From cd56063b53258f147fa56c4c1c38db5cbf293cd3 Mon Sep 17 00:00:00 2001 From: ajcasado Date: Wed, 15 Jan 2025 22:46:37 +0100 Subject: [PATCH 3/9] Add platform for Amstrad CPC with standard memory expansion used as ram disk for swap --- Kernel/platform/platform-cpc6128-SME/Makefile | 86 +++ Kernel/platform/platform-cpc6128-SME/Notes.md | 22 + Kernel/platform/platform-cpc6128-SME/README | 68 +++ .../platform/platform-cpc6128-SME/commonmem.s | 9 + Kernel/platform/platform-cpc6128-SME/config.h | 104 ++++ .../platform/platform-cpc6128-SME/cpc6128.s | 373 ++++++++++++ .../platform/platform-cpc6128-SME/cpcvideo.s | 44 ++ Kernel/platform/platform-cpc6128-SME/crt0.s | 211 +++++++ .../platform/platform-cpc6128-SME/devices.c | 63 +++ Kernel/platform/platform-cpc6128-SME/devrd.c | 80 +++ Kernel/platform/platform-cpc6128-SME/devrd.h | 20 + Kernel/platform/platform-cpc6128-SME/devtty.c | 132 +++++ Kernel/platform/platform-cpc6128-SME/devtty.h | 24 + .../platform/platform-cpc6128-SME/discard.c | 31 + Kernel/platform/platform-cpc6128-SME/fdc765.s | 530 ++++++++++++++++++ .../fuzix-platform-cpc6128-SME.pkg | 7 + .../platform-cpc6128-SME/fuzix.export | 0 .../platform-cpc6128-SME/fuzix.lnk.bck | 59 ++ .../platform/platform-cpc6128-SME/kernel.def | 13 + Kernel/platform/platform-cpc6128-SME/loader.s | 413 ++++++++++++++ .../platform-cpc6128-SME/loader_firmware.s | 94 ++++ Kernel/platform/platform-cpc6128-SME/main.c | 156 ++++++ .../platform-cpc6128-SME/platform_fdc765.h | 12 + .../platform/platform-cpc6128-SME/plt_ch375.h | 14 + .../platform/platform-cpc6128-SME/plt_ide.h | 13 + .../platform/platform-cpc6128-SME/rd_cpcsme.s | 107 ++++ Kernel/platform/platform-cpc6128-SME/rules.mk | 2 + .../platform/platform-cpc6128-SME/target.mk | 1 + Kernel/platform/platform-cpc6128-SME/tricks.s | 5 + 29 files changed, 2693 insertions(+) create mode 100644 Kernel/platform/platform-cpc6128-SME/Makefile create mode 100644 Kernel/platform/platform-cpc6128-SME/Notes.md create mode 100644 Kernel/platform/platform-cpc6128-SME/README create mode 100644 Kernel/platform/platform-cpc6128-SME/commonmem.s create mode 100644 Kernel/platform/platform-cpc6128-SME/config.h create mode 100644 Kernel/platform/platform-cpc6128-SME/cpc6128.s create mode 100644 Kernel/platform/platform-cpc6128-SME/cpcvideo.s create mode 100644 Kernel/platform/platform-cpc6128-SME/crt0.s create mode 100644 Kernel/platform/platform-cpc6128-SME/devices.c create mode 100644 Kernel/platform/platform-cpc6128-SME/devrd.c create mode 100644 Kernel/platform/platform-cpc6128-SME/devrd.h create mode 100644 Kernel/platform/platform-cpc6128-SME/devtty.c create mode 100644 Kernel/platform/platform-cpc6128-SME/devtty.h create mode 100644 Kernel/platform/platform-cpc6128-SME/discard.c create mode 100644 Kernel/platform/platform-cpc6128-SME/fdc765.s create mode 100644 Kernel/platform/platform-cpc6128-SME/fuzix-platform-cpc6128-SME.pkg create mode 100644 Kernel/platform/platform-cpc6128-SME/fuzix.export create mode 100644 Kernel/platform/platform-cpc6128-SME/fuzix.lnk.bck create mode 100644 Kernel/platform/platform-cpc6128-SME/kernel.def create mode 100644 Kernel/platform/platform-cpc6128-SME/loader.s create mode 100644 Kernel/platform/platform-cpc6128-SME/loader_firmware.s create mode 100644 Kernel/platform/platform-cpc6128-SME/main.c create mode 100644 Kernel/platform/platform-cpc6128-SME/platform_fdc765.h create mode 100644 Kernel/platform/platform-cpc6128-SME/plt_ch375.h create mode 100644 Kernel/platform/platform-cpc6128-SME/plt_ide.h create mode 100644 Kernel/platform/platform-cpc6128-SME/rd_cpcsme.s create mode 100644 Kernel/platform/platform-cpc6128-SME/rules.mk create mode 100644 Kernel/platform/platform-cpc6128-SME/target.mk create mode 100644 Kernel/platform/platform-cpc6128-SME/tricks.s diff --git a/Kernel/platform/platform-cpc6128-SME/Makefile b/Kernel/platform/platform-cpc6128-SME/Makefile new file mode 100644 index 0000000000..e5ca626012 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/Makefile @@ -0,0 +1,86 @@ +CSRCS = devtty.c devices.c main.c devrd.c +CDSRCS = discard.c +DSRCS = ../../dev/tinyide.c ../../dev/tinydisk.c ../../dev/devfdc765.c ../../dev/ch375.c +DDSRCS = ../../dev/tinyide_discard.c ../../dev/tinydisk_discard.c +DZSRCS = ../../dev/cpc/cpcide.c ../../dev/cpc/cpckeyboard.c ../../dev/cpc/devinput.c ../../dev/cpc/albireo.c +DDZSRCS = +ASRCS = crt0.s cpc6128.s cpcvideo.s fdc765.s rd_cpcsme.s +ASRCS += tricks.s commonmem.s +NSRCS = + +COBJS = $(CSRCS:.c=.rel) +CDOBJS = $(CDSRCS:.c=.rel) +AOBJS = $(ASRCS:.s=.rel) +DOBJS = $(patsubst ../../dev/%.c,%.rel, $(DSRCS)) +DDOBJS = $(patsubst ../../dev/%.c,%.rel, $(DDSRCS)) +DZOBJS = $(patsubst ../../dev/cpc/%.c,%.rel, $(DZSRCS)) +DDZOBJS = $(patsubst ../../dev/cpc/%.c,%.rel, $(DDZSRCS)) +NOBJS = $(patsubst ../../dev/net/%.c,%.rel, $(NSRCS)) +OBJS = $(COBJS) $(CDOBJS) $(AOBJS) $(DOBJS) $(DDOBJS) $(DZOBJS) $(DDZOBJS) $(NOBJS) + +CROSS_CCOPTS += -I../../dev/ -I../../dev/cpc/ -I../../dev/net + +CROSS_CC_SEG3 = --codeseg CODE3 + +all: $(OBJS) + +$(COBJS): %.rel: %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG3) -c $< + +$(CDOBJS): %.rel: %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(DOBJS): %.rel: ../../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG3) -c $< + +$(DDOBJS): %.rel: ../../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(DZOBJS): %.rel: ../../dev/cpc/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG3) -c $< + +$(DDZOBJS): %.rel: ../../dev/cpc/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(NOBJS): %.rel: ../../dev/net/%.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +$(AOBJS): %.rel: %.s + $(CROSS_AS) $(ASOPTS) $< + +clean: + rm -f $(OBJS) *.lst *.asm *.sym *.rst *.rel core *~ + rm -f loader.tmp loader.ihx loader.lst loader.bin padding.bin disk.raw + rm -rf fuzix.dsk + +image: + # Copy snapshot file template, transfer fuzix code to 0x100 and set execution at 0x100 + cp $(FUZIX_ROOT)/Standalone/filesystem-src/6128.sna $(IMAGES)/ + createSnapshot $(IMAGES)/6128.sna --loadFileData ../../fuzix.bin 256 + printf '\x01' | dd of=$(IMAGES)/6128.sna bs=1 seek=24 conv=notrunc + sdasz80 -l -o loader.s + sdldz80 -i loader.rel + hex2bin loader.ihx + + dd if=/dev/zero of=padding.bin bs=512 count=360 + # Make a disk image to work from + dd if=loader.bin of=padding.bin seek=0 bs=512 conv=notrunc + dd if=../../fuzix.bin of=padding.bin bs=512 seek=1 conv=notrunc + cat padding.bin >disk.raw + # And generate a 40 track cpc system disk from it + ../../tools/raw2dskcpc disk.raw fuzix.dsk 40 1 64 + cp fuzix.dsk $(IMAGES)/fuzix.dsk + +IMAGES = $(FUZIX_ROOT)/Images/$(TARGET) + +diskimage: + # Make a blank disk image with partition + dd if=$(FUZIX_ROOT)/Standalone/filesystem-src/parttab.64M of=$(IMAGES)/disk.img bs=64M conv=sync + # Add the file system + dd if=$(IMAGES)/filesys.img of=$(IMAGES)/disk.img bs=512 seek=2048 conv=notrunc + dd if=$(IMAGES)/filesys8.img of=$(IMAGES)/disk.img bs=512 seek=67584 conv=notrunc + # Make an emulator image of it + cat $(FUZIX_ROOT)/Standalone/filesystem-src/hdfheader $(IMAGES)/disk.img > $(IMAGES)/emu-ide.hdf + (cd $(FUZIX_ROOT)/Standalone/filesystem-src; ./build-mini-filesystem $(ENDIANFLAG) $(FUZIX_ROOT)/Images/$(TARGET)/root.raw 64 1440) + ../../tools/raw2dskcpc $(FUZIX_ROOT)/Images/$(TARGET)/root.raw $(FUZIX_ROOT)/Images/$(TARGET)/root.dsk 80 2 0 + \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/Notes.md b/Kernel/platform/platform-cpc6128-SME/Notes.md new file mode 100644 index 0000000000..95377e2735 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/Notes.md @@ -0,0 +1,22 @@ +# Amstrad CPC6128 + + +## Mmeory Model + +This port is based on the zx+3 port, taking advantage of Alan Cox's suggestion to take advantage of the fact that the memory maps C1, C2 and C3 correspond to the maps used in the zx+3 port. + +In the zx+3: +| User | 0 / 1 / 2 | Common 3 +| Kernel | 4 / 5 / 6 | Common 3 +| Video | 4 / 7 / 6 | Common 3 + +In the CPC6128: +| User | 4 / 5 / 6 | Common 7 (map C2) +| Kernel | 0 / 1 / 2 | Common 7 (map C1) +| Video | 0 / 3 / 2 | Common 7 (map c3) + +## TODO + +Lots of bugs. +Trying to mount the flopy hangs the machine, and may do nasty things to the floppy and the drive, don't try on a real machine!! +Fix memory size reporting 64 v 48K. \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/README b/Kernel/platform/platform-cpc6128-SME/README new file mode 100644 index 0000000000..fd31fdd0f5 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/README @@ -0,0 +1,68 @@ +# Amstrad CPC6128 + + +## Mmeory Model + +The CPC6128 supported memory maps: + + -MAP- C0 C1 C2 C3 C4 C5 C6 C7 + 0000-3FFF RAM_0 RAM_0 RAM_4 RAM_0 RAM_0 RAM_0 RAM_0 RAM_0 + 4000-7FFF RAM_1 RAM_1 RAM_5 RAM_3 RAM_4 RAM_5 RAM_6 RAM_7 + 8000-BFFF RAM_2 RAM_2 RAM_6 RAM_2 RAM_2 RAM_2 RAM_2 RAM_2 + C000-FFFF RAM_3 RAM_7 RAM_7 RAM_7 RAM_3 RAM_3 RAM_3 RAM_3 + +This port is based on the zx+3 port, following Alan Cox's suggestion to take advantage of the fact that memory maps C1, C2 and C3 correspond to the maps used in the zx+3 port. + +In the zx+3: +| User | 0 / 1 / 2 | Common 3 +| Kernel | 4 / 5 / 6 | Common 3 +| Video | 4 / 7 / 6 | Common 3 + +In the CPC6128: +| User | 4 / 5 / 6 | Common 7 (map C2) +| Kernel | 0 / 1 / 2 | Common 7 (map C1) +| Video | 0 / 3 / 2 | Common 7 (map C3) + + + +## STATUS + +Video mode 2 is used. The video driver configures the CRTC in 64x32 characters to do easy hardware scroll and use the whole video memory bank. + +The floppy driver seems to work. /dev/fd0 is drive A and /dev/fd1 is drive B. fd0 is hard coded to one side and fd1 to double side. A minimal system root disk image is generated to boot from fd1. Format is 9 sectors per track with sector ID from 1 to 9. + +The IDE driver that is supposed to work with the symbiface and xmass fails to initialize. FIXED, tested with ACE-DL emulator x-mass suport. + +The USB mass storage of the Albiero works using the ch375 driver used in other platforms. It should be easy to get it working with the Usifac/Ulifac. + +There isn't a proper loader, for now a snapshot is generated. FIXED, dsk floppy boot image generated. + +To test it burn disk.img on a spare usb pendrive and put it on the albireo. Load an run the snapshot or burn the dsk in a floppy and start FUZIX with |cpm. + + +## TODO + +Fix fdc driver. DONE +Fix IDE driver. DONE +Sometimes the top byte of the characters isn't drawn. FIXED +Vertical scroll shows the bottom line of the screen in the top of the screen. FIXED +Fix memory size reporting 64 v 48K (inherited from zx+3). +do_beep() doesn't seem to work. FIXED +Write a proper loader. DONE. +Configurable screen, at least add 80x25, maybe also change the video mode and routines to manage 6x8 fonts. +Support more hardware: M4 Board (storage, network and RTC), Ulifac/Usifac, networking with wifi module plugged in the usifac, sdcard in the Albireo, try slip with the serial port of the usifac... + +Fix lots of bugs. + +Switch to a thunked memory model based on C2 Map to use the standard and extended RAM expansions up to 4MiB, the Cromemco port could be a model to this solution. As ther is no real common as we are switching the whole 64k space, the common data area has to be updated in all the used banks, but this can give aprox. 60K for kernel and user and hold a lot of processes in memory with a big RAM expansion. If this proves to be too hard, a RAM disk for swapping can be a way to use the RAM expansions. + +Look for speed optimization opportunities. + +## BUILD & RUN + +make diskimage with cpc6128 target in base Makefile +.sna snapshot, .dsk Floppy image, and mass storage filesystem images are generated in Images folder. Tu boot from floppy use |cpm command from basic prompt + +To run on emulator use ACE-DL emulator and use disk.img as image for the x-mass IDE interface emulation. + + diff --git a/Kernel/platform/platform-cpc6128-SME/commonmem.s b/Kernel/platform/platform-cpc6128-SME/commonmem.s new file mode 100644 index 0000000000..595cb1a72c --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/commonmem.s @@ -0,0 +1,9 @@ +; +; Multiple app sizes and the fact the kernel and apps share the same banks +; means we need to put this somewhere low +; + .module commonmem + .area _COMMONMEM + + .include "../../cpu-z80/std-commonmem.s" + \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/config.h b/Kernel/platform/platform-cpc6128-SME/config.h new file mode 100644 index 0000000000..9ac4c33a2e --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/config.h @@ -0,0 +1,104 @@ +#define CONFIG_LARGE_IO_DIRECT(x) 1 /* We support direct to user I/O */ + +#define CONFIG_FDC765 +/* 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) */ +#define CONFIG_PROFIL +/* Multiple processes in memory at once */ +#define CONFIG_MULTI +/* CP/M emulation */ +#undef CONFIG_CPM_EMU + +/* Input layer support */ +#define CONFIG_INPUT +#define CONFIG_INPUT_GRABMAX 3 +/* Video terminal, not a serial tty */ +#define CONFIG_VT +/* Keyboard contains non-ascii symbols */ +#define CONFIG_UNIKEY + +/* Swap based one process in RAM */ +#define CONFIG_SWAP_ONLY +#define CONFIG_PARENT_FIRST +#define CONFIG_SPLIT_UDATA +#define UDATA_BLKS 1 +#define UDATA_SIZE 0x200 +#define CONFIG_DYNAMIC_BUFPOOL +#undef CONFIG_DYNAMIC_SWAP +#define MAXTICKS 60 /* Has to be high because we are swap only */ + +#undef CONFIG_KMOD + +#undef CONFIG_NET +#undef CONFIG_NET_NATIVE +#undef CONFIG_NET_WIZNET +#undef CONFIG_NET_W5100 + +/* Custom banking */ + +/* Banks as reported to user space */ +#define CONFIG_BANKS 1 + +/* Vt definitions */ +#define VT_WIDTH 64 +#define VT_HEIGHT 32 +#define VT_RIGHT 63 +#define VT_BOTTOM 31 + +#define TICKSPERSEC 300 /* Ticks per second */ +#define PROGBASE 0x0000 /* also data base */ +#define PROGLOAD 0x0100 /* also data base */ +#define PROGTOP 0xC000 /* Top of program, below C000 for simplicity + to get going */ + +#define BOOT_TTY (513) /* Set this to default device for stdio, stderr */ + /* In this case, the default is the first TTY device */ + +/* We need a tidier way to do this from the loader */ +#define CMDLINE NULL /* Location of root dev name */ + +/* Device parameters */ +#define NUM_DEV_TTY 1 + +#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */ +#define NBUFS 5 /* Number of block buffers */ +#define NMOUNTS 4 /* Number of mounts at a time */ + +#define SWAPBASE 0x0000 +#define SWAPTOP 0xC000UL +#define SWAP_SIZE 0x61 /* 48K + udata */ +#define EXTENDED_RAM_1024 +#ifdef EXTENDED_RAM_1024 + #define MAX_SWAPS 19 /*See platform devrd.c*/ + #define PTABSIZE 19 + #define TOTAL_SWAP_BLOCKS (1088-128) * 2 +#endif +#ifdef EXTENDED_RAM_512 + #define MAX_SWAPS 9 /*See platform devrd.c*/ + #define PTABSIZE 9 + #define TOTAL_SWAP_BLOCKS (576-128) * 2 +#endif +#define SWAPDEV 0x800 /* Device for swapping - RAM disk on standard memory expansion. */ + +/* We swap by hitting the user map */ +#define swap_map(x) ((uint8_t *)(x)) + +#define CONFIG_TD +#define CONFIG_TD_NUM 2 +/* IDE/CF support */ +#define CONFIG_TD_IDE +#define CONFIG_TINYIDE_SDCCPIO +#define CONFIG_TINYIDE_8BIT +#define IDE_IS_8BIT(x) 1 + +#define BOOTDEVICENAMES "hd#,fd" + +#define CONFIG_SMALL + + + + + diff --git a/Kernel/platform/platform-cpc6128-SME/cpc6128.s b/Kernel/platform/platform-cpc6128-SME/cpc6128.s new file mode 100644 index 0000000000..8b52c16f02 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/cpc6128.s @@ -0,0 +1,373 @@ +; +; Amstrad CPC6128 support + + .module cpc6128 + + ; exported symbols + .globl init_early + .globl init_hardware + .globl _program_vectors + .globl plt_interrupt_all + .globl interrupt_handler + .globl unix_syscall_entry + .globl null_handler + .globl nmi_handler + + .globl map_kernel + .globl map_proc_always + .globl map_proc + .globl map_kernel_di + .globl map_proc_always_di + .globl map_save_kernel + .globl map_restore + .globl map_kernel_restore + .globl map_for_swap + .globl map_video + .globl current_map + + + .globl _need_resched + .globl _int_disabled + .globl _vtborder + .globl diskmotor + + ; exported debugging tools + .globl _plt_monitor + .globl _plt_reboot + .globl outchar + + ; imported symbols + .globl _ramsize + .globl _procmem + + .globl _vtoutput + .globl _vtinit + + .globl _do_beep + + .globl outcharhex + .globl outhl, outde, outbc + .globl outnewline + .globl outstring + .globl outstringhex + + .globl ___sdcc_enter_ix + + .include "kernel.def" + .include "../../cpu-z80/kernel-z80.def" + +; ----------------------------------------------------------------------------- +; COMMON MEMORY BANK (above 0xF000) +; ----------------------------------------------------------------------------- + .area _COMMONMEM + +_plt_monitor: + ; + ; Not so much a monitor as wait for space + ; Part of this code is borrowed from https://github.com/lronaldo/cpctelera + + ld bc,#0x7fc2 + out (c),c ; keep us mapped + ld bc, #0xF782 ;; [3] Configure PPI 8255: Set Both Port A and Port C as Output. + out (c), c ;; [4] 82 = 1000 0010 : (B7=1)=> I/O Mode, (B6-5=00)=> Mode 1, + ;; (B4=0)=> Port A=Output, (B3=0)=> Port Cu=Output, + ;; (B2=0)=> Group B, Mode 0,(B1=1)=> Port B=Input, (B0=0)=> Port Cl=Output + ld bc, #0xF40E ;; [3] Write (0Eh = 14) on PPI 8255 Port A (F4h): the register we want to select on AY-3-8912 + ld e, b ;; [1] Save F4h into E to use it later in the loop + out (c), c ;; [4] + + ld bc, #0xF6C0 ;; [3] Write (C0h = 11 000000b) on PPI Port C (F6h): operation > select register + ld d, b ;; [1] Save F6h into D to use it later in the loop + out (c), c ;; [4] + .dw #0x71ED ; out (c), 0 ;; [4] out (C), 0 => Write 0 on PPI's Port C to put PSG's in inactive mode + ;; .... (required in between different operations) + ld bc, #0xF792 ;; [3] Configure PPI 8255: Set Port A = Input, Port C = Output. + out (c), c ;; [4] 92h= 1001 0010 : (B7=1)=> I/O Mode, (B6-5=00)=> Mode 1, + ;; (B4=1)=> Port A=Input, (B3=0)=> Port Cu=Output, + ;; (B2=0)=> Group B, Mode 0, (B1=1)=> Port B=Input, (B0=0)=> Port Cl=Output + ld a, #0x45 ;; SPACE + ld b, d ;; [1] B = F6h => Write the value of A to PPI's Port C to select next Matrix Line + out (c), a ;; [4] + ld b, e ;; [1] B = F4h => Read from PPI's Port A: Pressed/Not Pressed Values from PSG + in a,(c) + rla + jr c, _plt_monitor + +_plt_reboot: + di + ;halt ;we are debugging why we end here + ld bc, #0x7f89 ;this would set the firmware ready for boot into firmware with (out (c),c ; rst0) + out (c), c + rst 0 ; back into our booter + +plt_interrupt_all: + ret + + .area _COMMONMEM + +_int_disabled: + .db 1 + +_vtborder: ; needs to be common + .db 0 + + +; ----------------------------------------------------------------------------- +; KERNEL CODE BANK (below 0xC000) +; ----------------------------------------------------------------------------- + .area _CODE + +init_early: + + call _program_early_vectors + ret + +init_hardware: + ; set system RAM size + ld hl, #128 + ld (_ramsize), hl + ld hl, #64 ; 64K for kernel/screen/etc (FIXME) + ld (_procmem), hl + + ; Install rst shorteners + ld hl,#rstblock + ld de,#8 + ld bc,#32 + ldir + + ld bc,#0x7fc3 + ; bank 7 (common) in high in either mapping + ; video bank 3 at &4000 + out (c),c + ; and we should have special mapping + ; already by now + ld bc,#0x7f10 + out (c),c + ld a,#0x54 ; + ld (_vtborder), a + out (c),a ; black border + ld bc,#0x7f00 + out (c),c + ld a,#0x44 + out (c),a ; blue paper + ld bc,#0x7f01 + out (c),c + ld a,#0x4b + out (c),a ; white ink + + + ;we set the crtc for a screen with 64x32 colsxrows + ;pros: max number of characters on screen and easy hardware scroll + ;cons: 80x25 is more standard => TODO list (with mode change) + ld bc,#0xbc01 + out (c),c + ld bc,#0xbd20 + out (c),c + ld bc,#0xbc02 + out (c),c + ld bc,#0xbd2A + out (c),c + ld bc,#0xbc06 + out (c),c + ld bc,#0xbd20 + out (c),c + ld bc,#0xbc07 + out (c),c + ld bc,#0xbd22 + out (c),c + + call _do_beep + + ; screen initialization + call _vtinit + + ret + +;------------------------------------------------------------------------------ +; COMMON MEMORY PROCEDURES FOLLOW + + .area _COMMONMEM + +_program_early_vectors: + call map_proc_always + call set_vectors + call map_kernel +set_vectors: + ; write zeroes across all vectors + ld hl, #0 + ld de, #1 + ld bc, #0x007f ; program first 0x80 bytes only + ld (hl), #0x00 + ldir + + ; now install the interrupt vector at 0x0038 + ld a, #0xC3 ; JP instruction + ld (0x0038), a + ld hl, #interrupt_handler + ld (0x0039), hl + + ; set restart vector for FUZIX system calls + ld (0x0030), a ; (rst 30h is unix function call vector) + ld hl, #unix_syscall_entry + ld (0x0031), hl + + ld (0x0000), a + ld hl, #null_handler ; to Our Trap Handler + ld (0x0001), hl + + ld (0x0066), a ; Set vector for NMI + ld hl, #nmi_handler + ld (0x0067), hl + +_program_vectors: + ret + + ; Swap helper. Map the page in A into the address space such + ; that swap_map() gave the correct pointer to use. Undone by + ; a map_kernel_{restore} +map_proc: + ld a, h + or l + jr z, map_kernel +map_for_swap: +map_proc_always: +map_proc_always_di: + push af + ld a,#0xc2 ; 4 5 6 7 + jr map_a_pop +; +; Save and switch to kernel +; +map_save_kernel: + push af + ld a, (current_map) + ld (map_store), a + pop af +map_kernel_di: +map_kernel: +map_kernel_restore: + push af + ld a,#0xc1 ; 0 1 2 7 +map_a_pop: + push bc + ld (current_map),a + ld bc,#0x7f00 + out (c),a + pop bc + pop af + ret + +map_video: + push af + ld a,#0xc3 ; 0 3 2 7 + jr map_a_pop + +map_restore: + push af + ld a, (map_store) + jr map_a_pop + +; +; We have no easy serial debug output instead just breakpoint this +; address when debugging. +; +outchar: + ld (_tmpout), a + push bc + push de + push hl + push ix + ld hl, #1 + push hl + ld hl, #_tmpout + push hl + call _vtoutput + pop af + pop af + pop ix + pop hl + pop de + pop bc + ret + + .area _COMMONMEM +_tmpout: + .db 1 + +current_map: ; place to store current page number. Is needed + .db 0 ; because we have no ability to read 0xF4 port + ; to detect what page is mapped currently +map_store: + .db 0 + +_need_resched: + .db 0 + +diskmotor: + .db 0 + +; +; Stub helpers for code compactness. Note that +; sdcc_enter_ix is in the standard compiler support already +; + .area _DISCARD + +; +; The first two use an rst as a jump. In the reload sp case we don't +; have to care. In the pop ix case for the function end we need to +; drop the spare frame first, but we know that af contents don't +; matter +; + +rstblock: + jp ___sdcc_enter_ix + .ds 5 +___spixret: + ld sp,ix + pop ix + ret + .ds 3 +___ixret: + pop af + pop ix + ret + .ds 4 +___ldhlhl: + ld a,(hl) + inc hl + ld h,(hl) + ld l,a + ret + +; +; Helpers for the CH375 +; + .area _CODE + + .globl _nap20 + +_nap20: ;modified, in the cpc 1 nop = 1us. the call-ret add some us', it can be optimized (FIXME) + + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + + ret + diff --git a/Kernel/platform/platform-cpc6128-SME/cpcvideo.s b/Kernel/platform/platform-cpc6128-SME/cpcvideo.s new file mode 100644 index 0000000000..21e2af9b93 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/cpcvideo.s @@ -0,0 +1,44 @@ +; +; cpc vt primitives +; + + .module cpcvideo + + ; exported symbols + .globl _plot_char + .globl _scroll_down + .globl _scroll_up + .globl _cursor_on + .globl _cursor_off + .globl _cursor_disable + .globl _clear_lines + .globl _clear_across + .globl _do_beep + .globl _fontdata_8x8 + .globl _curattr + .globl _vtattr + + .globl map_video + .globl map_kernel + + ; Build the video library as the only driver + +CPCVID_ONLY .equ 1 +SCREENBASE .equ 0x40 + +.macro VIDEO_MAP + call map_video +.endm + +.macro VIDEO_UNMAP + call map_kernel +.endm + + .globl _fontdata_8x8 + +;_fontdata_8x8 .equ 0xF000 ; routines except this + ; to point to space char + .area _COMMONMEM + + .include "../../dev/cpc/video.s" + diff --git a/Kernel/platform/platform-cpc6128-SME/crt0.s b/Kernel/platform/platform-cpc6128-SME/crt0.s new file mode 100644 index 0000000000..042e521703 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/crt0.s @@ -0,0 +1,211 @@ + .module crt0 + + ; + ; High space - read only + ; + + .area _CODE + .area _CODE2 + ; + ; Try and keep code in the top 32K + ; + + + ; + ; Our common lives low + ; + .area _CODE3 + .area _VIDEO ; must end below 0x4000 + .area _FONT + .area _INITIALIZED + .area _HOME + .area _CONST + + ; + ; Beyond this point we just zero. + ; + + .area _DATA + .area _BSEG + .area _BSS + .area _HEAP + .area _GSINIT + .area _GSFINAL + + ; + ; Finally the buffers so they can expand + ; + .area _BUFFERS + ; Somewhere to throw it out of the way + .area _INITIALIZER + + .area _DISCARD + + + .area _COMMONMEM + + + + ; imported symbols + .globl _fuzix_main + .globl init_early + .globl init_hardware + .globl l__BUFFERS + .globl s__BUFFERS + .globl l__COMMONMEM + .globl s__COMMONMEM + .globl l__DATA + .globl s__DATA + .globl l__DISCARD + .globl s__DISCARD + .globl l__FONT + .globl s__FONT + .globl kstack_top + + .globl unix_syscall_entry + .globl nmi_handler + .globl interrupt_handler + + .include "kernel.def" + .include "../../cpu-z80/kernel-z80.def" + + ; + ; startup code + ; + ; We loaded the rest of the kernel from disk and jumped here + ; + + .area _CODE + + .globl _start + + + +_start: + + + di + ld sp, #kstack_top + ; + ; move the common memory where it belongs + ld hl, #s__DATA + ld de, #s__COMMONMEM + ld bc, #l__COMMONMEM + ldir + + ; then the font +; ld de, #s__FONT +; ld bc, #l__FONT +; ldir + + ; then the discard (backwards as will overlap) + ld de, #s__DISCARD + ld bc, #l__DISCARD-1 + ex de,hl + add hl,bc + ex de,hl + add hl,bc + lddr + ldd + + ; then zero the data area + ld hl, #s__DATA + ld de, #s__DATA + 1 + ld bc, #l__DATA - 1 + ld (hl), #0 + ldir + ; and buffers + ld hl, #s__BUFFERS + ld de, #s__BUFFERS + 1 + ld bc, #l__BUFFERS - 1 + ld (hl), #0 + ldir + + ;We are loading from a .sna with first 64k filled with fuzix.bin starting at 0x100 + ;copy bank 3 to bank 7 in C7 mode (7 at 0x4000), zero bank 3 (vmem) and switch to C1 (kernel map) + ;when a proper loader is done this should be managed there + + ld bc,#0x7fc7 + out (c),c + + ld hl, #0xc000 + ld de, #0x4000 + ld bc, #0x4000 + ldir + + ld bc,#0x7f00 + out (c),c + ld a,#0x44 + out (c),a ; blue paper + ld bc,#0x7f01 + out (c),c + ld a,#0x4b + out (c),a ; white ink + + ld hl, #0xc000 + ld de, #0xc001 + ld bc, #0x3fff + ld (hl), #0 + ldir + + ld bc,#0x7fc1 + out (c),c + + ld hl,#copyfont + ld de,#0xF000 + ld bc,#(copyfont_end-copyfont) + ldir + + call #0xF000 + + ld hl,#0xF000 + ld de,#0xF001 + ld bc,#(copyfont_end-copyfont-1) + ld (hl),#0 + ldir + + ; Configure memory map + call init_early + + ; Hardware setup + call init_hardware + + ; Call the C main routine + call _fuzix_main + + ; main shouldn't return, but if it does... + di +stop: halt + jr stop + + ;code to copy font +copyfont: ;;this wil be in the loader + di + ld bc, #0x7faa ;RMR ->UROM disable LROM enable + out (c),c + ld hl, #0x3800 ;Firmware (LROM) character bitmaps + ld de, #(_fontdata_8x8) + ld bc, #0x800 + ldir + ld bc, #0x7fae ;RMR ->UROM disable LROM disable + out (c),c + ret +copyfont_end: + + + .area _BUFFERS +; +; 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 +; + + .globl _bufpool + .area _BUFFERS + +_bufpool: + .ds BUFSIZE * NBUFS + + .globl _fontdata_8x8 + .area _FONT + _fontdata_8x8: + .ds 2048 diff --git a/Kernel/platform/platform-cpc6128-SME/devices.c b/Kernel/platform/platform-cpc6128-SME/devices.c new file mode 100644 index 0000000000..7dd80d5cb4 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/devices.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include +#include +/*#include */ +#include + +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 */ + { devfd_open, no_close, devfd_read, devfd_write, no_ioctl }, + /* 2: /dev/tty TTY devices */ + { tty_open, tty_close, tty_read, tty_write, cpcvt_ioctl }, + /* 3: /dev/lpr Printer devices */ + { no_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 }, + /* 5: Pack to 7 with nxio if adding private devices and start at 8 */ + /* 5: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 6: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 7: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 8: Standard memory expansions RAM swap */ + { rd_open, no_close, rd_read, rd_write, no_ioctl }, +}; + + +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; +} + +void device_init(void) +{ +#ifdef CONFIG_TD_IDE + ide_probe(); +#endif +#ifdef CONFIG_TD_SD + sd_probe(); +#endif +ch375_probe(); + +#ifdef CONFIG_NET + sock_init(); +#endif +} + diff --git a/Kernel/platform/platform-cpc6128-SME/devrd.c b/Kernel/platform/platform-cpc6128-SME/devrd.c new file mode 100644 index 0000000000..29c3a6e79e --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/devrd.c @@ -0,0 +1,80 @@ +/* + * CPC standard RAM bank memory expansions ramdisc driver, based on platform zxdiv48. + */ + +#include +#include +#include +#include + +static int rd_transfer(uint8_t is_read, uint8_t rawflag) +{ + nblock = udata.u_nblock; + block = udata.u_block; + rd_dptr = udata.u_dptr; + rd_wr = is_read; + uint16_t swap_bank_long; + uint8_t ct = 0; + + #ifdef DEBUG + kprintf("u_dptr %p Block %u u_nblock %u rd_wr %u\n",udata.u_dptr, udata.u_block, udata.u_nblock, rd_wr); + #endif + + /* It's a disk but only for swapping (and rd_io isn't general purpose) */ + if (((block + nblock) > (TOTAL_SWAP_BLOCKS - 1)) || (rawflag == 1)) { + udata.u_error = EIO; + kprintf("dev_rd_EIO"); + return -1; + } + + /* udata could change under us so keep variables privately */ + while (ct < nblock) { + swap_bank_long = (block >> 5); + swap_bank_long = swap_bank_long + 196 + (((swap_bank_long + 8) / 4) * 4); /*Convert bank number to Register MMR value + *See https://www.cpcwiki.eu/index.php/Gate_Array#Register_MMR_.28RAM_memory_mapping.29*/ + if (swap_bank_long > 255){ + rd_swap_bank = swap_bank_long - 64; + rd_swap_mem_port_h = 0x7e; + } + else{ + rd_swap_bank = swap_bank_long; + rd_swap_mem_port_h = 0x7f; + } + rd_proc_bank = ((uint16_t)rd_dptr / 0x4000) + 0xc4; + rd_swap_bank_addr = ((block & 31) << BLKSHIFT) + 0x4000; + rd_proc_bank_addr = ((uint16_t)rd_dptr & 0x3fff) + 0x4000; + + #ifdef DEBUG + if (nblock == 1) + kprintf("swap_bank %p swap_addr %p proc_bank %p proc_addr %p count %u\n", rd_swap_bank, rd_swap_bank_addr, rd_proc_bank, rd_proc_bank_addr, ct); + #endif + rd_io(); + block++; + rd_dptr += BLKSIZE; + ct++; + } + return ct << BLKSHIFT; /*Total bytes transferred*/ +} + +int rd_open(uint8_t minor, uint16_t flag) +{ + flag; + if(minor != 0) { + udata.u_error = ENODEV; + return -1; + } + return 0; +} + +int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return rd_transfer(true, rawflag); +} + +int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return rd_transfer(false, rawflag); +} + diff --git a/Kernel/platform/platform-cpc6128-SME/devrd.h b/Kernel/platform/platform-cpc6128-SME/devrd.h new file mode 100644 index 0000000000..e7c4451555 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/devrd.h @@ -0,0 +1,20 @@ +/* + * + * CPC standard RAM bank memory expansions ramdisc driver + */ + +int rd_open(uint8_t minor, uint16_t flags); +int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag); + +extern uint8_t rd_wr; +extern uint8_t rd_swap_bank; +extern uint8_t rd_swap_mem_port_h; +extern uint8_t rd_proc_bank; +extern uint8_t *rd_dptr; +extern uint16_t rd_swap_bank_addr; +extern uint16_t rd_proc_bank_addr; +extern uint16_t nblock; +extern blkno_t block; + +void rd_io(void); diff --git a/Kernel/platform/platform-cpc6128-SME/devtty.c b/Kernel/platform/platform-cpc6128-SME/devtty.c new file mode 100644 index 0000000000..0f1733f313 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/devtty.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char tbuf1[TTYSIZ]; + +uint8_t vtattr_cap = VTA_UNDERLINE; +extern uint8_t curattr; + +tcflag_t termios_mask[NUM_DEV_TTY + 1] = { + 0, + _CSYS +}; + + +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}, +}; + +/* tty1 is the screen */ + +/* Output for the system console (kprintf etc) */ +void kputchar(char c) +{ + if (c == '\n') + tty_putc(0, '\r'); + tty_putc(0, c); +} + +/* Both console and debug port are always ready */ +ttyready_t tty_writeready(uint8_t minor) +{ + minor; + return TTY_READY_NOW; +} + +void tty_putc(uint8_t minor, unsigned char c) +{ + minor; + vtoutput(&c, 1); +} + +int tty_carrier(uint8_t minor) +{ + minor; + return 1; +} + +void tty_setup(uint8_t minor, uint8_t flags) +{ + minor; +} + +void tty_sleeping(uint8_t minor) +{ + minor; +} + +void tty_data_consumed(uint8_t minor) +{ +} + + +/* This is used by the vt asm code, but needs to live in the kernel */ +uint16_t cursorpos; + +//Inherited from spectrum zx+3. For now ignore attributes, try later to implement inverse and underline touching char output directly +void vtattr_notify(void) +{ + // Attribute byte fixups: not hard as the colours map directly + // to the spectrum ones +/* if (vtattr & VTA_INVERSE) + curattr = ((vtink & 7) << 3) | (vtpaper & 7); + else + curattr = (vtink & 7) | ((vtpaper & 7) << 3); + + if (vtattr & VTA_FLASH) + curattr |= 0x80; + // How to map the bright bit - we go by either + if ((vtink | vtpaper) & 0x10) + curattr |= 0x40; +*/ + //we are now debugging other things + //vtink = 26; + //vtpaper = 1; + //cpcvt_ioctl(1, VTINK, &vtink); + //cpcvt_ioctl(1, VTPAPER, &vtpaper); + +} + + +__sfr __banked __at 0x7F00 gatearray; +/* see: https://www.cpcwiki.eu/index.php/Gate_Array */ + +int cpcvt_ioctl(uint8_t minor, uarg_t arg, char *ptr) +{ + uint8_t c; + if (minor == 1 && arg == VTBORDER) { + c = ugetc(ptr); + gatearray = PENR_BORDER_SELECT; + vtborder &= INKR_COLOR_SET; + vtborder |= c & 0x1F; + gatearray = vtborder; + return 0; + } + if (minor == 1 && arg == VTINK) { + c = ugetc(ptr); + gatearray = PENR_INK_SELECT; + vtink &= INKR_COLOR_SET; + vtink |= c & 0x1F; + gatearray = vtink; + return 0; + } + if (minor == 1 && arg == VTPAPER) { + c = ugetc(ptr); + gatearray = PENR_PAPER_SELECT; + vtpaper &= INKR_COLOR_SET; + vtpaper |= c & 0x1F; + gatearray = vtpaper; + return 0; + } + return vt_ioctl(minor, arg, ptr); +} diff --git a/Kernel/platform/platform-cpc6128-SME/devtty.h b/Kernel/platform/platform-cpc6128-SME/devtty.h new file mode 100644 index 0000000000..57d7727ead --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/devtty.h @@ -0,0 +1,24 @@ +#ifndef __DEVTTY_DOT_H__ +#define __DEVTTY_DOT_H__ + +void tty_pollirq(void); +static void keydecode(void); + +#define KEY_ROWS 10 +#define KEY_COLS 8 +#define PENR_BORDER_SELECT 0x10 +/*For now We are in mode 2 with pen 0 for paper and pen 1 for ink*/ +#define PENR_PAPER_SELECT 0x00 +#define PENR_INK_SELECT 0x01 +#define INKR_COLOR_SET 0x40 +extern uint8_t keymap[10]; +extern uint8_t keyboard[10][8]; +extern uint8_t shiftkeyboard[10][8]; + +extern uint8_t timer_wait; + +extern int cpcvt_ioctl(uint8_t minor, uarg_t arg, char *ptr); + +extern uint8_t vtborder; + +#endif diff --git a/Kernel/platform/platform-cpc6128-SME/discard.c b/Kernel/platform/platform-cpc6128-SME/discard.c new file mode 100644 index 0000000000..164fb2a423 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/discard.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include +#include + +uint8_t plt_param(char *p) +{ + return 0; +} + +/* Nothing to do for the map of init */ +void map_init(void) +{ + uint_fast8_t i; + for (i = 0; i < MAX_SWAPS; i++) + swapmap_init(i); +} + +void plt_copyright(void) +{ + kprintf("Amstrad CPC6128 platform\nCopyright (c) 2024-2025 Antonio J. Casado Alias\n"); +} +/* +void ide_reset(void) +{ + ide_std_reset(); +} +*/ + diff --git a/Kernel/platform/platform-cpc6128-SME/fdc765.s b/Kernel/platform/platform-cpc6128-SME/fdc765.s new file mode 100644 index 0000000000..76c0312ab3 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/fdc765.s @@ -0,0 +1,530 @@ +; +; 765 Floppy Controller Support +; +; This is based upon the Amstrad NC200 driver by David Given and +; this example https://cpctech.cpcwiki.de/source/fdcload.html from Kevin Thacker site +; +; It differs on the CPC in the following ways +; +; - The timings are tighter so we use in a,(c) jp p and other +; tricks to make the clocks. Even so it should be in uncontended RAM +; +; - The CPC doesn't expose the tc line, so if the 765 decides to +; expect more data or feed us more data all we can do is dump it or +; feed it crap until it shuts up +; +; - We do motor and head loading delays (possibly some of those should +; be backported - FIXME) +; +; - We don't hang if the controller tells us no more data when we +; think we need to feed it command bytes (BACKPORT NEEDED) +; +; TODO +; Initialize drive step rate etc (we rely on the firmware for now) +; Step rate +; Head load/unload times +; Write off time af +; (12ms step 30ms head stabilize, 4ms head load, max (0xf) head +; unload) +; + .module fdc765 + + .include "kernel.def" + .include "../../cpu-z80/kernel-z80.def" + + .globl map_proc_always + .globl map_kernel + + .globl _fd765_do_nudge_tc + .globl _fd765_do_recalibrate + .globl _fd765_do_seek + .globl _fd765_do_read + .globl _fd765_do_write + .globl _fd765_do_read_id + .globl _fd765_motor_on + .globl _fd765_motor_off + + .globl _fd765_track + .globl _fd765_head + .globl _fd765_sector + .globl _fd765_status + .globl _fd765_buffer + .globl _fd765_is_user + .globl _fd765_sectors + .globl _fd765_drive + + .globl _vtborder + + .globl diskmotor + + .area _COMMONMEM + +; +; Twiddle the Terminal Count line to the FDC. Not supported by the +; CPC +; +_fd765_do_nudge_tc: + ret + +; Writes A to the FDC data register. + +fd765_tx: + push bc +; ex af, af' +; ld bc,#0xfb7e ; floppy register (16bit access) +;fd765_tx_loop: +; in a, (c) +; add a +; jr nc, fd765_tx_loop +; ; FIXME: backport this fix +; add a +; jr c, fd765_tx_exit ; controller doesn't want data ?? +; ex af, af' +; ld c,#0x7f +; out (c), a +; ex (sp),hl +; ex (sp),hl +;fd765_tx_exit: + ld bc,#0xfb7e ;; I/O address for FDC main status register + push af ;; + fwc1: in a,(c) ;; + add a,a ;; + jr nc,fwc1 ;; + add a,a ;; + jr nc,fwc2 ;; + pop af ;; + ret + + fwc2: + pop af ;; + + inc c ;; + out (c),a ;; write command byte + dec c ;; + + ;; some FDC documents say there must be a delay between each + ;; command byte, but in practice it seems this isn't needed on CPC. + ;; Here for compatiblity. + ld a,#5 ;; + fwc3: dec a ;; + jr nz,fwc3 ;; + pop bc + ; FIXME: is our delay quite long enough for spec ? + ; might need them to be ex (sp),ix ? + 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: + ld hl, #_fd765_status +; ld c, #0x7e ; we flip between 2ffd 3ffd as we go +;read_status_loop: +; ld b,#0xfb ; control port +; in a, (c) +; rla ; RQM... +; jr nc, read_status_loop ; ...low, keep waiting +; rla ; DIO... +; ret nc ; ...low, no more data +; ld c,#0x7f ; data port +; in a,(c) ; INI ? FIXME +; ld (hl),a +; inc hl +; ex (sp),hl ; wait for the 765A +; ex (sp),hl +; ex (sp),hl +; ex (sp),hl +; jr read_status_loop ; next byte + ld bc,#0xfb7e + fr1: + in a,(c) + cp #0xc0 + jr c,fr1 + + inc c + in a,(c) + dec c + ld (hl),a + inc hl + + ld a,#5 + fr2: + dec a + jr nz,fr2 + in a,(c) + and #0x10 + jr nz,fr1 + + + ret +_fd765_status: + .ds 8 ; 8 bytes of status data + +; Sends the head/drive byte of a command. + +send_head: + ld hl, (_fd765_head) ; l = head h = drive) + ld a, l + add a + add a + add h + jr fd765_tx + +; Performs a RECALIBRATE command. + +_fd765_do_recalibrate: + ld a, #0x07 ; RECALIBRATE + call fd765_tx + ld a, (_fd765_drive) ; drive # + call fd765_tx + jr wait_for_seek_ending + +; Performs a SEEK command. + +_fd765_do_seek: + ld a, #0x0f ; SEEK + call fd765_tx + call send_head ; specified head, drive #0 + ld a, (_fd765_track) ; specified track + call fd765_tx + jr wait_for_seek_ending +_fd765_track: + .db 0 +_fd765_sector: + .db 0 +; +; These two must remain adjacent see send_head +; +_fd765_head: + .db 0 +_fd765_drive: + .db 0 + +; Waits for a SEEK or RECALIBRATE command to finish by polling SENSE INTERRUPT STATUS. +wait_for_seek_ending: + + ld a, #0x08 ; SENSE INTERRUPT STATUS + call fd765_tx + call fd765_read_status + + ld a, (#_fd765_status) + bit 5, a ; SE, seek end + jr z, wait_for_seek_ending + + bit 4,a + + ret + + ; Now settle the head (FIXME: what is the right value ?) + ld a, #30 ; 30ms +; +; This assumes uncontended timing +; +wait_ms: + push bc +wait_ms_loop: + ld b,#0xDC +wait_ms_loop2: + dec b + jr nz, wait_ms_loop2 + dec a + jr nz, wait_ms_loop + pop bc + ret + +_fd765_motor_off: + push bc + ld bc,#0xfa7e + xor a + ld (diskmotor),a + out (c),a + pop bc + ret + +_fd765_motor_on: + ld a,(diskmotor) + or a + ret nz + ld a,#0x01 + ld (diskmotor),a + ; Take effect + ld bc,#0xfa7e + out (c),a + ; Now wait for spin up + + ld e,#10 ; FIXME right value ?? +wait2: + ; The classic Z80 KHz timing loop + ld bc,#3548 ; 3.548MHz for spectrum, should change for cpc.FIXME +wait1: + dec bc + ld a,b + or c + jr nz, wait1 + dec e + jr nz, wait2 + 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: + ld a, #0x46 ; READ SECTOR MFM + + ; FIXME: need to return a last cmd byte here and write it + ; after this crap or we may miss if we write just the sector hits + ; the head (BACKPORT ME ??) + call setup_read_or_write + + ld a, (_fd765_is_user) + or a + push af + call nz, map_proc_always + + ld bc,#0x7f10 + out (c),c + ld c,#0x46 ;Cyan + out (c),c + + di ; performance critical, + ; run with interrupts off + xor a + call fd765_tx ; send the final unused byte + ; to fire off the command + ld hl, (_fd765_buffer) + ld bc, #0xfb7e + +fdc_data_read: + in a,(c) ;; FDC has data and the direction is from FDC to CPU + jp p,fdc_data_read ;; + and #0x20 ;; "Execution phase" i.e. indicates reading of sector data + jp z,fdc_read_end + + inc c ;; BC = I/O address for FDC data register + in a,(c) ;; read from FDC data register + ld (hl),a ;; write to memory + dec c ;; BC = I/O address for FDC main status register + inc hl ;; increment memory pointer + jp fdc_data_read + +fdc_read_end: + +; ld de, #0x2000 ; so we can make timing +; jp read_wait +; +; First 256 bytes + +;read_loop: +; inc c ; data port +; ini +; inc b +; dec c ; control port +; dec e +; jp z, read_wait2 +;read_wait: +; in a,(c) ; read the fdc status +; jp p, read_wait +; and d +; jp nz, read_loop +; jp read_finished +; +; Second 256 bytes +; +;read_loop2: +; inc c ; data port +; ini +; inc b +; dec c ; control port +; dec e +; jp z, read_flush_wait +;read_wait2: +; in a,(c) ; read the fdc status +; jp p, read_wait2 +; and d +; jp nz, read_loop2 +; jp read_finished +; +; Flush out any extra data (no tc control) +; +;read_flush: +; inc c +; in a,(c) +; dec c +;read_flush_wait: +; in a,(c) +; jp p, read_flush_wait +; and d +; jp nz, read_flush +; +; And done +; +read_finished: + ld (_fd765_buffer), hl + call _fd765_do_nudge_tc ; Tell FDC we've finished + ei + + call fd765_read_status + call tc_fix + + ld bc,#0x7f10 + out (c),c + ld a,(_vtborder) + out (c),a + + pop af + ret z + jp map_kernel + +; +; We will get an error reported that the command did not complete +; because the tc bit is not controllable. Spot that specific error +; and ignore it. +; +tc_fix: + ld hl,#_fd765_status + ld a,(hl) + and #0xC0 + cp #0x40 + ret nz + inc hl + bit 7,(hl) + ret z + res 7,(hl) + dec hl + res 6,(hl) + ret + +; +; Write is much like read just the other direction +; +_fd765_do_write: + ; interrupts off + ld a, #0x45 ; WRITE SECTOR MFM + call setup_read_or_write + + ld a, (_fd765_is_user) + or a + push af + call nz, map_proc_always + + ld bc,#0x7f10 + out (c),c + ld c,#0x47 ;Pink + out (c),c + + di + + xor a + call fd765_tx ; send the final unused 0 byte + ; to fire off the command + ld hl, (_fd765_buffer) + ld bc, #0xfb7e +fdc_data_write: + in a,(c) ;; FDC has data and the direction is from FDC to CPU + jp p,fdc_data_write ;; + and #0x20 ;; "Execution phase" i.e. indicates reading of sector data + jp z,fdc_write_end + + inc c ;; BC = I/O address for FDC data register + ld a,(hl) ;; read from memory + out (c),a ;; write to FDC data register + dec c ;; BC = I/O address for FDC main status register + inc hl ;; increment memory pointer + jp fdc_data_write + +fdc_write_end: +; ld de,#0x2000 ; to make timing +; jp write_wait +; +;write_loop: +; inc c +; outi +; inc b +; dec c +; dec e +; jp z, write_wait2 +;write_wait: +; in a,(c) +; jp p, write_wait +; and d +; jp nz, write_loop +; jp write_finished +;write_loop2: +; inc c +; outi +; inc b +; dec c +; dec e +; jp z, write_flush_wait +;write_wait2: +; in a,(c) +; jp p, write_wait2 +; and d +; jp nz, write_loop2 +; jp write_finished +;write_flush: +; inc c +; in a,(c) +; dec c +;write_flush_wait: +; in a,(c) +; jp p, write_flush_wait +; and d +; jp nz, write_flush +write_finished: + ld (_fd765_buffer), hl + call _fd765_do_nudge_tc ; Tell FDC we've finished + ei + call fd765_read_status + call tc_fix + + ld bc,#0x7f10 + out (c),c + ld a,(_vtborder) + out (c),a + + pop af + ret z + jp 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 + ld a, (_fd765_track) ; 2: specified track + call fd765_tx + ld a, (_fd765_head) ; 3: specified head + call fd765_tx + ld a, (_fd765_sector) ; 4: specified sector + ld b, a + call fd765_tx + ld a, #2 ; 5: bytes per sector: 512 + call fd765_tx + ld a, (_fd765_sectors) + add b ; add first sector + dec a ; 6: last sector (*inclusive*) + call fd765_tx + ld a, #0x2A ; 7: Gap 3 length (2A is standard for 3" drives) + 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: + .dw 0 +_fd765_is_user: + .db 0 +_fd765_sectors: + .db 0 + +; Read the next sector ID off the disk. +; (Only used for debugging.) + +_fd765_do_read_id: + ld a, #0x4a ; READ MFM ID + call fd765_tx + call send_head ; specified head, drive 0 + jp fd765_read_status diff --git a/Kernel/platform/platform-cpc6128-SME/fuzix-platform-cpc6128-SME.pkg b/Kernel/platform/platform-cpc6128-SME/fuzix-platform-cpc6128-SME.pkg new file mode 100644 index 0000000000..526097fcbd --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/fuzix-platform-cpc6128-SME.pkg @@ -0,0 +1,7 @@ +package platform-cpc6128-SME + +disable-pkg platform-cpc6128-SME + +l /bin/sh /bin/sh.orig +r /bin/sh +l /bin/fsh /bin/sh \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/fuzix.export b/Kernel/platform/platform-cpc6128-SME/fuzix.export new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Kernel/platform/platform-cpc6128-SME/fuzix.lnk.bck b/Kernel/platform/platform-cpc6128-SME/fuzix.lnk.bck new file mode 100644 index 0000000000..cb04214f01 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/fuzix.lnk.bck @@ -0,0 +1,59 @@ +-mwxuy +-i fuzix.ihx +-l z80 +-b _CODE=0x0100 +-b _COMMONMEM=0xF300 +platform/platform-cpc6128/crt0.rel +platform/platform-cpc6128/commonmem.rel +platform/platform-cpc6128/plus3.rel +platform/platform-cpc6128/zxvideo.rel +platform/platform-cpc6128/main.rel +platform/platform-cpc6128/discard.rel +start.rel +version.rel +cpu-z80/lowlevel-z80.rel +cpu-z80/usermem_std-z80.rel +platform/platform-cpc6128/tricks.rel +timer.rel +kdata.rel +usermem.rel +platform/platform-cpc6128/devices.rel +devio.rel +filesys.rel +blk512.rel +process.rel +inode.rel +syscall_exec.rel +syscall_exec16.rel +syscall_fs.rel +syscall_fs2.rel +syscall_fs3.rel +syscall_proc.rel +syscall_other.rel +syscall_net.rel +tty.rel +vt.rel +font8x8.rel +mm.rel +memalloc_none.rel +simple.rel +swap.rel +devsys.rel +devinput.rel +kmod.rel +network.rel +platform/platform-cpc6128/devtty.rel +platform/platform-cpc6128/tinyide.rel +platform/platform-cpc6128/tinyide_discard.rel +platform/platform-cpc6128/divide.rel +platform/platform-cpc6128/tinysd.rel +platform/platform-cpc6128/tinysd_discard.rel +platform/platform-cpc6128/zxmmc.rel +platform/platform-cpc6128/tinydisk.rel +platform/platform-cpc6128/tinydisk_discard.rel +platform/platform-cpc6128/devinput.rel +platform/platform-cpc6128/zxkeyboard.rel +platform/platform-cpc6128/devfdc765.rel +platform/platform-cpc6128/fdc765.rel +platform/platform-cpc6128/net_w5x00.rel +-e diff --git a/Kernel/platform/platform-cpc6128-SME/kernel.def b/Kernel/platform/platform-cpc6128-SME/kernel.def new file mode 100644 index 0000000000..d061dd72e8 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/kernel.def @@ -0,0 +1,13 @@ +; UZI mnemonics for memory addresses etc + +; We stick it straight after the tag +U_DATA__TOTALSIZE .equ 0x200 ; 256+256@F000 + +Z80_TYPE .equ 1 + +PROGBASE .equ 0x0000 +PROGLOAD .equ 0x0100 + +NBUFS .equ 5 + +Z80_MMU_HOOKS .equ 0 diff --git a/Kernel/platform/platform-cpc6128-SME/loader.s b/Kernel/platform/platform-cpc6128-SME/loader.s new file mode 100644 index 0000000000..be3d137ab4 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/loader.s @@ -0,0 +1,413 @@ +;;from https://cpctech.cpc-live.com/ +;; Load data by talking directly to the NEC765 floppy disc controller (FDC) +;; https://cpctech.cpcwiki.de/source/fdcload.html from Kevin Thacker site +;; Code assumes there is a drive 0 and there is a disc in it and the disc is formatted +;; to SYSTEM format. +.area BOOT (ABS) +.org #0xfdff + +;;ROM's & interrupts off + +di +ld b, #0x7f +ld c,#0b10101101 +out (c),c +;;from default boot org to the needed one +ld hl,#0x100 +ld de,#0xfdff +ld bc,#512 +ldir + +jp start + +start: +;;clean room +ld hl,#0 +ld de ,#1 +ld (#0),hl +ld bc,#0xfdfe +ldir +;;patch interrupt vector +LD HL,#0XC9FB +LD (#0X0038),HL +;; turn on disc motor +ld bc,#0xfa7e +ld a,#1 +out (c),a +;; the motor on all connected drives will be turned on +;; and the motor will start to speed up. +;; +;; The drive must be "Ready" to accept commands from the FDC. +;; A drive will be ready if: +;; * the drive motor has reached a stable speed +;; * there is a disc in the drive +;; +;; The following code is a delay which will ait enough time for the motor +;; to reach a stable speed. (i.e. the motor speed is not increasing or decreasing) +;; +;; All drives are not the same, some 3" drives take longer to reach a stable +;; speed so we need a longer delay to be compatible with these. +;; +;; At this point interrupts must be enabled. +ei +ld b,#30 ;; 30/6 = 5 frames or 5/50 of a second. +w1: +;; there are 6 CPC interrupts per frame. This waits for one of them +halt +djnz w1 +di +;; this is the drive we want to use +;; the code uses this variable. +ld a,#0 +ld (#drive),a + +;; recalibrate means to move the selected drive to track 0. +;; +;; track 0 is a physical signal from the drive that indicates when +;; the read/write head is at track 0 position. +;; +;; The drive itself doesn't know which track the read/write head is positioned over. +;; The FDC has an internal variable for each drive which holds the current track number. +;; This value is reset when the drive indicates the read/write head is over track 0. +;; The number is increased/decreased as the FDC issues step pulses to the drive to move the head +;; to the track we want. +;; +;; once a recalibrate has been done, both drive and fdc agree on the track. +;; +call fdc_recalibrate + +;; now the drive is at a known position and is ready the fdc knows it is at a known position +;; we can read data.. +;call read_file + +read_file: +;; set variable for starting sector for our data (#0xC1 is first sector ID for +;; SYSTEM format. Sector IDs are #0x41, #0x42, #0x43, #0x44, #0x45, #0x46, #0x47, #0x48 and #0x49. +;; This bootloader is in track 0 sector #0x41 to load with |cpm command from basic + +ld a,#0x42 +ld (#sector),a + +;; set variable for starting track for our data +;; Tracks are numbered 0 to 39 for 40 track drives and 0 to 79 for 80 track drives. +;; Some 3" drives can allow up to 42 tracks (0-41), some 80 track drives can allow up +;; to 83 tracks (0-82). +;; +;; Not all drives are the same however. The maximum that is compatible with all 3" drives +;; is 41 tracks. +ld a,#0 +ld (#track),a + +;; memory address to write data to (start) +ld de,#0x100 +ld (#data_ptr),de + +;; number of complete sectors to read for our data +;; 30 sectors, 512 bytes per sector. Total data to read is 30*512 = 15360 bytes. +ld a,#126 +ld (#sector_count),a + +read_sectors_new_track: +;; perform a seek (this means to move read/write head to track we want). +;; track is defined by the "track" variable. +;; +;; a recalibrate must be done on the drive before a seek is done. +;; +;; the fdc uses it's internal track value for the chosen drive to decide to seek up/down to +;; reach the desired track. The FDC issues "step pulses" which makes the read/write head move +;; 1 track at a time at the rate defined by the FDC specify command. +;; +;; e.g. if fdc thinks we are on track 10, and we ask it to move to track 5, it will step back 5 times +;; updating it's internal track number each time. +call fdc_seek + +read_sectors: +;; Send Read data command to FDC to read 1 sector. + +;; A track is layed out as follows: +;; +;; id field +;; data field +;; +;; id field +;; data field +;; +;; id field +;; data field +;; etc. +;; +;; we tell the FDC the values of the ID field we want. Once it finds a match it will then read +;; the data. If the ID field we want is not found, it will report an error. + + +ld a,#0b01000110 ;; read data command (mfm=double density reading mode) + ;; not multi-track. See FDC data sheet for list of commands and the + ;; number of bytes they need. +call fdc_write_command +ld a,(#drive) ;; physical drive and side + ;; bits 1,0 define drive, bit 2 defines side +call fdc_write_command +ld a,(#track) ;; C value from id field of sector we want to read +call fdc_write_command +ld a,#0 ;; H value from id field of sector we want to read +call fdc_write_command +ld a,(#sector) ;; R value from id field of sector we want to read +call fdc_write_command +ld a,#2 ;; N value from id field of sector we want to read + ;; this also determines the amount of data in the sector. + ;; 2 = 512 byte sector +call fdc_write_command +ld a,(#sector) ;; EOT = Last sector ID to read. This is the same as the first to read 1 sector. +call fdc_write_command +ld a,#0x2a ;; Gap Length for read. Not important. +call fdc_write_command +ld a,#0xff ;; DTL = Data length. Only valid when N is 0 it seems +call fdc_write_command + +;; There will be a delay here before the first byte of a sector is ready and +;; interrupts can be active. +;; +;; The FDC is reading from the track. It is searching for an ID field that +;; matches the values we have sent in the command. +;; +;; When it finds the ID field, there is furthur time before the data field +;; of the sector is found and it starts to read. +;; +;; Once it has found the data, we must read it all and quickly. +;; + + +;; interrupts must be off now for data to be read successfully. +;; +;; The CPU constantly asks the FDC if there is data ready, if there is +;; it reads it from the FDC and stores it in RAM. There is a timing +;; constraint, the FDC gives the CPU a byte every 32microseconds. +;; If the CPU fails to read one of the bytes in time, the FDC will report +;; an overrun error and stop data transfer. + + +;; current address to write data too. +ld de,(#data_ptr) + +;; this is the main loop +;; which reads the data +;; The FDC will give us a byte every 32us (double density disc format). +;; +;; We must read it within this time. + +fdc_data_read: +in a,(c) ;; FDC has data and the direction is from FDC to CPU +jp p,fdc_data_read ;; +and #0x20 ;; "Execution phase" i.e. indicates reading of sector data +jp z,fdc_read_end + +inc c ;; BC = I/O address for FDC data register +in a,(c) ;; read from FDC data register +ld (de),a ;; write to memory +dec c ;; BC = I/O address for FDC main status register +inc de ;; increment memory pointer +jp fdc_data_read + +fdc_read_end: +;; Interrupts can be enabled now we have completed the data transfer + + + +;; we will get here if we successfully read all the sector's data +;; OR if there was an error. + +;; read the result +call fdc_result + +;; check result +ld ix,#result_data +ld c,#0x54 +ld a,0(ix) +cp #0x40 +jp z,nerr +ld a,1(ix) +cp #0x80 +jp z,nerr +ld c,#0x40 +nerr: + +;; decrease number of sectors transferred +ld a,(#sector_count) +dec a +jp z,read_done +ld (#sector_count),a + +;; update ram pointer for next sector +ld hl,(#data_ptr) +ld bc,#512 +add hl,bc +ld (#data_ptr),hl + +;; update sector id (loops #0x41-#0x49). +ld a,(#sector) +inc a +ld (#sector),a +cp #0x4a ;; #0x49+1 (last sector id on the track+1) +jp nz,read_sectors +;; we read sector #0x49, the last on the track. +;; Update track variable so we seek to the next track before +;; reading the next sector +ld a,(#track) +inc a +ld (#track),a + +ld a,#0x41 ;; #0x41 = first sector id on the track +ld (#sector),a +jp read_sectors_new_track + +read_done: +ld bc,#0xfa7e +ld a,#0 +out (c),a ;stop the motor +jp 0x100 ;start FUZIX + +;;=============================================== +;; send command to fdc +;; + +fdc_write_command: + + +ld bc,#0xfb7e ;; I/O address for FDC main status register +push af ;; +fwc1: in a,(c) ;; +add a,a ;; +jr nc,fwc1 ;; +add a,a ;; +jr nc,fwc2 ;; +pop af ;; +ret + +fwc2: +pop af ;; + +inc c ;; +out (c),a ;; write command byte +dec c ;; + +;; some FDC documents say there must be a delay between each +;; command byte, but in practice it seems this isn't needed on CPC. +;; Here for compatiblity. +ld a,#5 ;; +fwc3: dec a ;; +jr nz,fwc3 ;; +ret ;; + +;;=============================================== +;; get result phase of command +;; +;; timing is not important here + +fdc_result: + +ld hl,#result_data +ld bc,#0xfb7e +fr1: +in a,(c) +cp #0xc0 +jr c,fr1 + +inc c +in a,(c) +dec c +ld (hl),a +inc hl + +ld a,#5 +fr2: +dec a +jr nz,fr2 +in a,(c) +and #0x10 +jr nz,fr1 + + +ret + +;;=============================================== + +;; physical drive +;; bit 1,0 are drive, bit 2 is side. +drive: +.db 0 + +;; physical track (updated during read) +track: +.db 0 + +;; id of sector we want to read (updated during read) +sector: +.db 0 + +;; number of sectors to read (updated during read) +sector_count: +.db 2 ;; enough for now + +;; address to write data to (updated during read) +data_ptr: +.ds 2 + +;;=============================================== + +fdc_seek: +ld a,#0b00001111 ;; seek command +call fdc_write_command +ld a,(#drive) +call fdc_write_command +ld a,(#track) +call fdc_write_command + +call fdc_seek_or_recalibrate +jp nz,fdc_seek +ret + +;;=============================================== + +fdc_recalibrate: + +;; seek to track 0 +ld a,#0b111 ;; recalibrate +call fdc_write_command +ld a,(#drive) ;; drive +call fdc_write_command + +call fdc_seek_or_recalibrate +jp nz,fdc_recalibrate +ret + +;;=============================================== +;; NZ result means to retry seek/recalibrate. + +fdc_seek_or_recalibrate: +ld a,#0b1000 ;; sense interrupt status +call fdc_write_command +call fdc_result + +;; recalibrate completed? +ld ix,#result_data +bit 5,0(ix) ;; Bit 5 of Status register 0 is "Seek complete" +jr z,fdc_seek_or_recalibrate +bit 4,0(ix) ;; Bit 4 of Status register 0 is "recalibrate/seek failed" +;; +;; Some FDCs will seek a maximum of 77 tracks at one time. This is a legacy/historical +;; thing when drives only had 77 tracks. 3.5" drives have 80 tracks. +;; +;; If the drive was at track 80 before the recalibrate/seek, then one recalibrate/seek +;; would not be enough to reach track 0 and the fdc will then report an error (meaning +;; it had seeked 77 tracks and failed to reach the track we wanted). +;; We repeat the recalibrate/seek to finish the movement of the read/write head. +;; +ret + +;;=============================================== + +file_buffer: +.dw 0x100 +result_data: +.ds 8 +end: \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/loader_firmware.s b/Kernel/platform/platform-cpc6128-SME/loader_firmware.s new file mode 100644 index 0000000000..b384288459 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/loader_firmware.s @@ -0,0 +1,94 @@ +;;from: https://cpctech.cpc-live.com/source/byteload.html, modified to load something bigger + +;; This example shows how to read a file byte by byte. +;; +;; A file without a header must be read this way, it can't be +;; read using CAS IN DIRECT (unless the in-memory header is patched) +;; +;; This example doesn't have any error checking. + +.area BOOT (ABS) +.org 0xa500 + +cas_in_open .equ 0xbc77 +cas_in_close .equ 0xbc7a +cas_in_char .equ 0xbc80 +kl_rom_walk .equ 0xbccb +mc_start_program .equ 0xbd16 + +ld c,#0xff +ld hl,#start +call mc_start_program +start: +ld hl,#0x0100 ;; address to load file data to (example) +push hl + +call kl_rom_walk +;; open file for reading +ld b,#end_filename-filename +ld hl,#filename +ld de,#two_k_buffer +call cas_in_open + +;; If a file is opened without a header: +;; - the filetype will be ASCII (0x16) +;; - the length and load address will be undefined. +;; +;; If a file is opened with a header, the +;; - the filetype will be taken from the header +;; - the length and load address will be taken from the header +;; +;; A file without a header can't be read with CAS IN DIRECT +;; and must be read using CAS IN CHAR. + +pop hl + +;; read a char from the file, character is returned in A register + +next_byte: +call cas_in_char +jr nc,not_eof +jr nz,not_eof + +;; could be end of file +;; test for hard end of file byte +cp #0xf +jr nz,not_eof +jr eof + +not_eof: +;; write byte to memory +ld (hl),a +inc hl +ld a,#0xA5 +cp h +jr z, avoid_crash +jr next_byte + + +eof: +call cas_in_close +ld de,#0xA500 +ld hl,#0xC000 +ld bc,#0x4000 +ldir +jp 0x100 ;; and go to fuzix + +avoid_crash: +ld b,#0x1b ;;0xc0-0xa5 +ld c,#0 +add hl,bc +jr next_byte + +;;------------------------------------------------------------------- +;; name of the file to read + +filename: +.ascii "FUZIX.BIN" +end_filename: + +;;------------------------------------------------------------------- +;; this buffer is filled with data from the file +.org 0xF7FF +two_k_buffer: +.blkb 2048 \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/main.c b/Kernel/platform/platform-cpc6128-SME/main.c new file mode 100644 index 0000000000..03dfa901c7 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/main.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include +#include +//#include + +uint16_t ramtop = PROGTOP; +uint16_t swap_dev = 0xFFFF; + +/* On idle we spin checking for the terminals. Gives us more responsiveness + for the polled ports */ +void plt_idle(void) +{ + /* We don't want an idle poll and IRQ driven tty poll at the same moment */ + __asm + halt + __endasm; +} + +uint8_t timer_wait; + +void plt_interrupt(void) +{ + tty_pollirq(); + timer_interrupt(); + poll_input(); + if (timer_wait) + wakeup(&timer_interrupt); + devfd_spindown(); +} + +/* + * So that we don't suck in a library routine we can't use from + * the runtime + */ + +size_t strlen(const char *p) +{ + size_t len = 0; + while(*p++) + len++; + return len; +} + +/* 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 the start of + * user space into buffers. + * + * We don't touch discard. Discard is just turned into user space. + */ +void plt_discard(void) +{ + uint16_t discard_size = ((uint16_t)&udata) - (uint16_t)bufpool_end; + bufptr bp = bufpool_end; +#ifdef CONFIG_KMOD + kmod_init(bufpool_end, &udata); +#endif + + 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; + } +} + +unsigned plt_kmod_set(uint8_t *top) +{ + /* Make sure all disk buffers are on disk */ + sync(); + /* Wind back until bufpool end is below the modules */ + while(bufpool_end > (void *)top) + bufpool_end--; + /* Any buffers lost we already wrote to disk, any new lookups will + not find them, and we know there are no other outstanding references + - sometimes having a dumb I/O layer is a win */ + return 0; +} + +#ifndef SWAPDEV +/* Adding dummy swapper since it is referenced by tricks.s */ +void swapper(ptptr p) +{ + p; +} +#endif + +#ifdef CONFIG_NET_W5100 + +uint8_t w5x00_readcb(uint16_t off) +{ +} + +uint8_t w5x00_readsb(uint8_t s, uint16_t off) +{ +} + +uint16_t w5x00_readcw(uint16_t off) +{ +} + +uint16_t w5x00_readsw(uint8_t s, uint16_t off) +{ +} + +void w5x00_bread(uint16_t bank, uint16_t off, void *pv, uint16_t n) +{ +} + +void w5x00_breadu(uint16_t bank, uint16_t off, void *pv, uint16_t n) +{ +} + +void w5x00_writecb(uint16_t off, uint8_t n) +{ +} + +void w5x00_writesb(uint8_t sock, uint16_t off, uint8_t n) +{ +} + +void w5x00_writecw(uint16_t off, uint16_t n) +{ +} + +void w5x00_writesw(uint8_t sock, uint16_t off, uint16_t n) +{ +} + +void w5x00_bwrite(uint16_t bank, uint16_t off, void *pv, uint16_t n) +{ +} + +void w5x00_bwriteu(uint16_t bank, uint16_t off, void *pv, uint16_t n) +{ +} + +void w5x00_setup(void) +{ +} +#endif diff --git a/Kernel/platform/platform-cpc6128-SME/platform_fdc765.h b/Kernel/platform/platform-cpc6128-SME/platform_fdc765.h new file mode 100644 index 0000000000..da41e4c3fc --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/platform_fdc765.h @@ -0,0 +1,12 @@ +/* + * Platform specifics + */ + +#define FDC_MOTOR_TIMEOUT 10 /* Seconds */ + +#define FDC765_MAX_FLOPPY 2 /* Two drives */ + + +/* Hard code for now. Two drives, second drive double sided */ +#define fdc765_ds 2 +#define fdc765_present 3 \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/plt_ch375.h b/Kernel/platform/platform-cpc6128-SME/plt_ch375.h new file mode 100644 index 0000000000..8a4f0a0457 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/plt_ch375.h @@ -0,0 +1,14 @@ +extern void nap20(void); +extern void ch375_rblock(uint8_t *ptr);/* __z88dk_fastcall; */ +extern void ch375_wblock(uint8_t *ptr);/* __z88dk_fastcall; */ + +__sfr __banked __at 0xFE80 ch375_dport; +__sfr __banked __at 0xFE81 ch375_sport; + +#define ch375_rdata() ch375_dport +#define ch375_rstatus() ch375_sport + +#define ch375_wdata(x) do {ch375_dport = (x); } while(0) +#define ch375_wcmd(x) do {ch375_sport = (x); } while(0) + +#define CH376_REG_DATA 0xFE80 \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/plt_ide.h b/Kernel/platform/platform-cpc6128-SME/plt_ide.h new file mode 100644 index 0000000000..4d6c0567cc --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/plt_ide.h @@ -0,0 +1,13 @@ +__sfr __banked __at 0xFD08 data; +__sfr __banked __at 0xFD09 error; +__sfr __banked __at 0xFD0A count; +__sfr __banked __at 0xFD0B sec; +__sfr __banked __at 0xFD0C cyll; +__sfr __banked __at 0xFD0D cylh; +__sfr __banked __at 0xFD0E devh; +__sfr __banked __at 0xFD0F cmd; +__sfr __banked __at 0xFD0F status; + +#define IDE_REG_DATA 0xFD08 + +#define IDE_NONSTANDARD_XFER diff --git a/Kernel/platform/platform-cpc6128-SME/rd_cpcsme.s b/Kernel/platform/platform-cpc6128-SME/rd_cpcsme.s new file mode 100644 index 0000000000..68eb1b4799 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/rd_cpcsme.s @@ -0,0 +1,107 @@ +; +; RAM disc helpers for CPC Standard Memory Expansions +; + + .area _CODE + + .globl _rd_io + + .globl _ct + .globl _block + .globl _nblock + .globl _rd_wr + .globl _rd_swap_mem_port_h + .globl _rd_swap_bank + .globl _rd_proc_bank + .globl _rd_swap_bank_addr + .globl _rd_proc_bank_addr + .globl _rd_dptr + .globl _int_disabled + .globl _vtborder + .globl map_kernel_restore + + +_rd_io: + di + ld a,(_rd_wr) + or a + jp z, is_wr + ld bc,#0x7f10 + out (c),c + ld c,#0x53 ;Bright cyan + out (c),c + ld a,(_rd_swap_mem_port_h) + ld b,a + ld c,#0xff + ld a,(_rd_swap_bank) + out (c),a + ld hl,(_rd_swap_bank_addr) + ld de, #swapbuffer + ld bc,#512 + ldir + ld bc,#0x7fff + ld a,(_rd_proc_bank) + out (c),a + ld hl,#swapbuffer + ld de,(_rd_proc_bank_addr) + ld bc,#512 + ldir +end_io: + ld bc,#0x7fc1 ; map kernel + out (c),c + call map_kernel_restore ; do it the right way + ld bc,#0x7f10 + out (c),c + ld a,(_vtborder) + out (c),a + ld a,(_int_disabled) + or a + ret nz + ei + ret +is_wr: + ld bc,#0x7f10 + out (c),c + ld c,#0x4c ;Bright red + out (c),c + ld bc,#0x7fff + ld a,(_rd_proc_bank) + out (c),a + ld hl,(_rd_proc_bank_addr) + ld de,#swapbuffer + ld bc,#512 + ldir + ld a,(_rd_swap_mem_port_h) + ld b,a + ld c,#0xff + ld a,(_rd_swap_bank) + out (c),a + ld hl,#swapbuffer + ld de,(_rd_swap_bank_addr) + ld bc,#512 + ldir + jp end_io + +_ct: + .db 0 +_rd_wr: + .db 0 +_rd_swap_bank: + .db 0 +_rd_proc_bank: + .db 0 +_rd_swap_mem_port_h: + .db 0x7f +_rd_swap_bank_addr: + .dw 0 +_rd_proc_bank_addr: + .dw 0 +_rd_dptr: + .dw 0 +_block: + .dw 0 +_nblock: + .dw 0 +swapbuffer: + .ds 512 + diff --git a/Kernel/platform/platform-cpc6128-SME/rules.mk b/Kernel/platform/platform-cpc6128-SME/rules.mk new file mode 100644 index 0000000000..edaf0e339a --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/rules.mk @@ -0,0 +1,2 @@ +export CROSS_CC_SYS5=--codeseg CODE3 +CROSS_CCOPTS += --peep-file $(FUZIX_ROOT)/Kernel/cpu-z80/rst.peep diff --git a/Kernel/platform/platform-cpc6128-SME/target.mk b/Kernel/platform/platform-cpc6128-SME/target.mk new file mode 100644 index 0000000000..3bffcde0c7 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/target.mk @@ -0,0 +1 @@ +export CPU = z80 diff --git a/Kernel/platform/platform-cpc6128-SME/tricks.s b/Kernel/platform/platform-cpc6128-SME/tricks.s new file mode 100644 index 0000000000..2d02424493 --- /dev/null +++ b/Kernel/platform/platform-cpc6128-SME/tricks.s @@ -0,0 +1,5 @@ + .include "kernel.def" + .include "../../cpu-z80/kernel-z80.def" + + .include "../../lib/z80single.s" + From 28aeb8f22372b815fa7b6a919c8e2ed3c47e6e8b Mon Sep 17 00:00:00 2001 From: ajcasado Date: Tue, 21 Jan 2025 20:48:17 +0100 Subject: [PATCH 4/9] merge cpc6128 ports --- Kernel/platform/platform-cpc6128-SME/Makefile | 86 --- Kernel/platform/platform-cpc6128-SME/Notes.md | 22 - Kernel/platform/platform-cpc6128-SME/README | 68 --- .../platform/platform-cpc6128-SME/commonmem.s | 9 - Kernel/platform/platform-cpc6128-SME/config.h | 104 ---- .../platform/platform-cpc6128-SME/cpc6128.s | 373 ------------ .../platform/platform-cpc6128-SME/cpcvideo.s | 44 -- Kernel/platform/platform-cpc6128-SME/crt0.s | 211 ------- .../platform/platform-cpc6128-SME/devices.c | 63 --- Kernel/platform/platform-cpc6128-SME/devrd.c | 80 --- Kernel/platform/platform-cpc6128-SME/devrd.h | 20 - Kernel/platform/platform-cpc6128-SME/devtty.c | 132 ----- Kernel/platform/platform-cpc6128-SME/devtty.h | 24 - .../platform/platform-cpc6128-SME/discard.c | 31 - Kernel/platform/platform-cpc6128-SME/fdc765.s | 530 ------------------ .../fuzix-platform-cpc6128-SME.pkg | 7 - .../platform-cpc6128-SME/fuzix.export | 0 .../platform-cpc6128-SME/fuzix.lnk.bck | 59 -- .../platform/platform-cpc6128-SME/kernel.def | 13 - Kernel/platform/platform-cpc6128-SME/loader.s | 413 -------------- .../platform-cpc6128-SME/loader_firmware.s | 94 ---- Kernel/platform/platform-cpc6128-SME/main.c | 156 ------ .../platform-cpc6128-SME/platform_fdc765.h | 12 - .../platform/platform-cpc6128-SME/plt_ch375.h | 14 - .../platform/platform-cpc6128-SME/plt_ide.h | 13 - .../platform/platform-cpc6128-SME/rd_cpcsme.s | 107 ---- Kernel/platform/platform-cpc6128-SME/rules.mk | 2 - .../platform/platform-cpc6128-SME/target.mk | 1 - Kernel/platform/platform-cpc6128-SME/tricks.s | 5 - 29 files changed, 2693 deletions(-) delete mode 100644 Kernel/platform/platform-cpc6128-SME/Makefile delete mode 100644 Kernel/platform/platform-cpc6128-SME/Notes.md delete mode 100644 Kernel/platform/platform-cpc6128-SME/README delete mode 100644 Kernel/platform/platform-cpc6128-SME/commonmem.s delete mode 100644 Kernel/platform/platform-cpc6128-SME/config.h delete mode 100644 Kernel/platform/platform-cpc6128-SME/cpc6128.s delete mode 100644 Kernel/platform/platform-cpc6128-SME/cpcvideo.s delete mode 100644 Kernel/platform/platform-cpc6128-SME/crt0.s delete mode 100644 Kernel/platform/platform-cpc6128-SME/devices.c delete mode 100644 Kernel/platform/platform-cpc6128-SME/devrd.c delete mode 100644 Kernel/platform/platform-cpc6128-SME/devrd.h delete mode 100644 Kernel/platform/platform-cpc6128-SME/devtty.c delete mode 100644 Kernel/platform/platform-cpc6128-SME/devtty.h delete mode 100644 Kernel/platform/platform-cpc6128-SME/discard.c delete mode 100644 Kernel/platform/platform-cpc6128-SME/fdc765.s delete mode 100644 Kernel/platform/platform-cpc6128-SME/fuzix-platform-cpc6128-SME.pkg delete mode 100644 Kernel/platform/platform-cpc6128-SME/fuzix.export delete mode 100644 Kernel/platform/platform-cpc6128-SME/fuzix.lnk.bck delete mode 100644 Kernel/platform/platform-cpc6128-SME/kernel.def delete mode 100644 Kernel/platform/platform-cpc6128-SME/loader.s delete mode 100644 Kernel/platform/platform-cpc6128-SME/loader_firmware.s delete mode 100644 Kernel/platform/platform-cpc6128-SME/main.c delete mode 100644 Kernel/platform/platform-cpc6128-SME/platform_fdc765.h delete mode 100644 Kernel/platform/platform-cpc6128-SME/plt_ch375.h delete mode 100644 Kernel/platform/platform-cpc6128-SME/plt_ide.h delete mode 100644 Kernel/platform/platform-cpc6128-SME/rd_cpcsme.s delete mode 100644 Kernel/platform/platform-cpc6128-SME/rules.mk delete mode 100644 Kernel/platform/platform-cpc6128-SME/target.mk delete mode 100644 Kernel/platform/platform-cpc6128-SME/tricks.s diff --git a/Kernel/platform/platform-cpc6128-SME/Makefile b/Kernel/platform/platform-cpc6128-SME/Makefile deleted file mode 100644 index e5ca626012..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/Makefile +++ /dev/null @@ -1,86 +0,0 @@ -CSRCS = devtty.c devices.c main.c devrd.c -CDSRCS = discard.c -DSRCS = ../../dev/tinyide.c ../../dev/tinydisk.c ../../dev/devfdc765.c ../../dev/ch375.c -DDSRCS = ../../dev/tinyide_discard.c ../../dev/tinydisk_discard.c -DZSRCS = ../../dev/cpc/cpcide.c ../../dev/cpc/cpckeyboard.c ../../dev/cpc/devinput.c ../../dev/cpc/albireo.c -DDZSRCS = -ASRCS = crt0.s cpc6128.s cpcvideo.s fdc765.s rd_cpcsme.s -ASRCS += tricks.s commonmem.s -NSRCS = - -COBJS = $(CSRCS:.c=.rel) -CDOBJS = $(CDSRCS:.c=.rel) -AOBJS = $(ASRCS:.s=.rel) -DOBJS = $(patsubst ../../dev/%.c,%.rel, $(DSRCS)) -DDOBJS = $(patsubst ../../dev/%.c,%.rel, $(DDSRCS)) -DZOBJS = $(patsubst ../../dev/cpc/%.c,%.rel, $(DZSRCS)) -DDZOBJS = $(patsubst ../../dev/cpc/%.c,%.rel, $(DDZSRCS)) -NOBJS = $(patsubst ../../dev/net/%.c,%.rel, $(NSRCS)) -OBJS = $(COBJS) $(CDOBJS) $(AOBJS) $(DOBJS) $(DDOBJS) $(DZOBJS) $(DDZOBJS) $(NOBJS) - -CROSS_CCOPTS += -I../../dev/ -I../../dev/cpc/ -I../../dev/net - -CROSS_CC_SEG3 = --codeseg CODE3 - -all: $(OBJS) - -$(COBJS): %.rel: %.c - $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG3) -c $< - -$(CDOBJS): %.rel: %.c - $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< - -$(DOBJS): %.rel: ../../dev/%.c - $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG3) -c $< - -$(DDOBJS): %.rel: ../../dev/%.c - $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< - -$(DZOBJS): %.rel: ../../dev/cpc/%.c - $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG3) -c $< - -$(DDZOBJS): %.rel: ../../dev/cpc/%.c - $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< - -$(NOBJS): %.rel: ../../dev/net/%.c - $(CROSS_CC) $(CROSS_CCOPTS) -c $< - -$(AOBJS): %.rel: %.s - $(CROSS_AS) $(ASOPTS) $< - -clean: - rm -f $(OBJS) *.lst *.asm *.sym *.rst *.rel core *~ - rm -f loader.tmp loader.ihx loader.lst loader.bin padding.bin disk.raw - rm -rf fuzix.dsk - -image: - # Copy snapshot file template, transfer fuzix code to 0x100 and set execution at 0x100 - cp $(FUZIX_ROOT)/Standalone/filesystem-src/6128.sna $(IMAGES)/ - createSnapshot $(IMAGES)/6128.sna --loadFileData ../../fuzix.bin 256 - printf '\x01' | dd of=$(IMAGES)/6128.sna bs=1 seek=24 conv=notrunc - sdasz80 -l -o loader.s - sdldz80 -i loader.rel - hex2bin loader.ihx - - dd if=/dev/zero of=padding.bin bs=512 count=360 - # Make a disk image to work from - dd if=loader.bin of=padding.bin seek=0 bs=512 conv=notrunc - dd if=../../fuzix.bin of=padding.bin bs=512 seek=1 conv=notrunc - cat padding.bin >disk.raw - # And generate a 40 track cpc system disk from it - ../../tools/raw2dskcpc disk.raw fuzix.dsk 40 1 64 - cp fuzix.dsk $(IMAGES)/fuzix.dsk - -IMAGES = $(FUZIX_ROOT)/Images/$(TARGET) - -diskimage: - # Make a blank disk image with partition - dd if=$(FUZIX_ROOT)/Standalone/filesystem-src/parttab.64M of=$(IMAGES)/disk.img bs=64M conv=sync - # Add the file system - dd if=$(IMAGES)/filesys.img of=$(IMAGES)/disk.img bs=512 seek=2048 conv=notrunc - dd if=$(IMAGES)/filesys8.img of=$(IMAGES)/disk.img bs=512 seek=67584 conv=notrunc - # Make an emulator image of it - cat $(FUZIX_ROOT)/Standalone/filesystem-src/hdfheader $(IMAGES)/disk.img > $(IMAGES)/emu-ide.hdf - (cd $(FUZIX_ROOT)/Standalone/filesystem-src; ./build-mini-filesystem $(ENDIANFLAG) $(FUZIX_ROOT)/Images/$(TARGET)/root.raw 64 1440) - ../../tools/raw2dskcpc $(FUZIX_ROOT)/Images/$(TARGET)/root.raw $(FUZIX_ROOT)/Images/$(TARGET)/root.dsk 80 2 0 - \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/Notes.md b/Kernel/platform/platform-cpc6128-SME/Notes.md deleted file mode 100644 index 95377e2735..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/Notes.md +++ /dev/null @@ -1,22 +0,0 @@ -# Amstrad CPC6128 - - -## Mmeory Model - -This port is based on the zx+3 port, taking advantage of Alan Cox's suggestion to take advantage of the fact that the memory maps C1, C2 and C3 correspond to the maps used in the zx+3 port. - -In the zx+3: -| User | 0 / 1 / 2 | Common 3 -| Kernel | 4 / 5 / 6 | Common 3 -| Video | 4 / 7 / 6 | Common 3 - -In the CPC6128: -| User | 4 / 5 / 6 | Common 7 (map C2) -| Kernel | 0 / 1 / 2 | Common 7 (map C1) -| Video | 0 / 3 / 2 | Common 7 (map c3) - -## TODO - -Lots of bugs. -Trying to mount the flopy hangs the machine, and may do nasty things to the floppy and the drive, don't try on a real machine!! -Fix memory size reporting 64 v 48K. \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/README b/Kernel/platform/platform-cpc6128-SME/README deleted file mode 100644 index fd31fdd0f5..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/README +++ /dev/null @@ -1,68 +0,0 @@ -# Amstrad CPC6128 - - -## Mmeory Model - -The CPC6128 supported memory maps: - - -MAP- C0 C1 C2 C3 C4 C5 C6 C7 - 0000-3FFF RAM_0 RAM_0 RAM_4 RAM_0 RAM_0 RAM_0 RAM_0 RAM_0 - 4000-7FFF RAM_1 RAM_1 RAM_5 RAM_3 RAM_4 RAM_5 RAM_6 RAM_7 - 8000-BFFF RAM_2 RAM_2 RAM_6 RAM_2 RAM_2 RAM_2 RAM_2 RAM_2 - C000-FFFF RAM_3 RAM_7 RAM_7 RAM_7 RAM_3 RAM_3 RAM_3 RAM_3 - -This port is based on the zx+3 port, following Alan Cox's suggestion to take advantage of the fact that memory maps C1, C2 and C3 correspond to the maps used in the zx+3 port. - -In the zx+3: -| User | 0 / 1 / 2 | Common 3 -| Kernel | 4 / 5 / 6 | Common 3 -| Video | 4 / 7 / 6 | Common 3 - -In the CPC6128: -| User | 4 / 5 / 6 | Common 7 (map C2) -| Kernel | 0 / 1 / 2 | Common 7 (map C1) -| Video | 0 / 3 / 2 | Common 7 (map C3) - - - -## STATUS - -Video mode 2 is used. The video driver configures the CRTC in 64x32 characters to do easy hardware scroll and use the whole video memory bank. - -The floppy driver seems to work. /dev/fd0 is drive A and /dev/fd1 is drive B. fd0 is hard coded to one side and fd1 to double side. A minimal system root disk image is generated to boot from fd1. Format is 9 sectors per track with sector ID from 1 to 9. - -The IDE driver that is supposed to work with the symbiface and xmass fails to initialize. FIXED, tested with ACE-DL emulator x-mass suport. - -The USB mass storage of the Albiero works using the ch375 driver used in other platforms. It should be easy to get it working with the Usifac/Ulifac. - -There isn't a proper loader, for now a snapshot is generated. FIXED, dsk floppy boot image generated. - -To test it burn disk.img on a spare usb pendrive and put it on the albireo. Load an run the snapshot or burn the dsk in a floppy and start FUZIX with |cpm. - - -## TODO - -Fix fdc driver. DONE -Fix IDE driver. DONE -Sometimes the top byte of the characters isn't drawn. FIXED -Vertical scroll shows the bottom line of the screen in the top of the screen. FIXED -Fix memory size reporting 64 v 48K (inherited from zx+3). -do_beep() doesn't seem to work. FIXED -Write a proper loader. DONE. -Configurable screen, at least add 80x25, maybe also change the video mode and routines to manage 6x8 fonts. -Support more hardware: M4 Board (storage, network and RTC), Ulifac/Usifac, networking with wifi module plugged in the usifac, sdcard in the Albireo, try slip with the serial port of the usifac... - -Fix lots of bugs. - -Switch to a thunked memory model based on C2 Map to use the standard and extended RAM expansions up to 4MiB, the Cromemco port could be a model to this solution. As ther is no real common as we are switching the whole 64k space, the common data area has to be updated in all the used banks, but this can give aprox. 60K for kernel and user and hold a lot of processes in memory with a big RAM expansion. If this proves to be too hard, a RAM disk for swapping can be a way to use the RAM expansions. - -Look for speed optimization opportunities. - -## BUILD & RUN - -make diskimage with cpc6128 target in base Makefile -.sna snapshot, .dsk Floppy image, and mass storage filesystem images are generated in Images folder. Tu boot from floppy use |cpm command from basic prompt - -To run on emulator use ACE-DL emulator and use disk.img as image for the x-mass IDE interface emulation. - - diff --git a/Kernel/platform/platform-cpc6128-SME/commonmem.s b/Kernel/platform/platform-cpc6128-SME/commonmem.s deleted file mode 100644 index 595cb1a72c..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/commonmem.s +++ /dev/null @@ -1,9 +0,0 @@ -; -; Multiple app sizes and the fact the kernel and apps share the same banks -; means we need to put this somewhere low -; - .module commonmem - .area _COMMONMEM - - .include "../../cpu-z80/std-commonmem.s" - \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/config.h b/Kernel/platform/platform-cpc6128-SME/config.h deleted file mode 100644 index 9ac4c33a2e..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/config.h +++ /dev/null @@ -1,104 +0,0 @@ -#define CONFIG_LARGE_IO_DIRECT(x) 1 /* We support direct to user I/O */ - -#define CONFIG_FDC765 -/* 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) */ -#define CONFIG_PROFIL -/* Multiple processes in memory at once */ -#define CONFIG_MULTI -/* CP/M emulation */ -#undef CONFIG_CPM_EMU - -/* Input layer support */ -#define CONFIG_INPUT -#define CONFIG_INPUT_GRABMAX 3 -/* Video terminal, not a serial tty */ -#define CONFIG_VT -/* Keyboard contains non-ascii symbols */ -#define CONFIG_UNIKEY - -/* Swap based one process in RAM */ -#define CONFIG_SWAP_ONLY -#define CONFIG_PARENT_FIRST -#define CONFIG_SPLIT_UDATA -#define UDATA_BLKS 1 -#define UDATA_SIZE 0x200 -#define CONFIG_DYNAMIC_BUFPOOL -#undef CONFIG_DYNAMIC_SWAP -#define MAXTICKS 60 /* Has to be high because we are swap only */ - -#undef CONFIG_KMOD - -#undef CONFIG_NET -#undef CONFIG_NET_NATIVE -#undef CONFIG_NET_WIZNET -#undef CONFIG_NET_W5100 - -/* Custom banking */ - -/* Banks as reported to user space */ -#define CONFIG_BANKS 1 - -/* Vt definitions */ -#define VT_WIDTH 64 -#define VT_HEIGHT 32 -#define VT_RIGHT 63 -#define VT_BOTTOM 31 - -#define TICKSPERSEC 300 /* Ticks per second */ -#define PROGBASE 0x0000 /* also data base */ -#define PROGLOAD 0x0100 /* also data base */ -#define PROGTOP 0xC000 /* Top of program, below C000 for simplicity - to get going */ - -#define BOOT_TTY (513) /* Set this to default device for stdio, stderr */ - /* In this case, the default is the first TTY device */ - -/* We need a tidier way to do this from the loader */ -#define CMDLINE NULL /* Location of root dev name */ - -/* Device parameters */ -#define NUM_DEV_TTY 1 - -#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */ -#define NBUFS 5 /* Number of block buffers */ -#define NMOUNTS 4 /* Number of mounts at a time */ - -#define SWAPBASE 0x0000 -#define SWAPTOP 0xC000UL -#define SWAP_SIZE 0x61 /* 48K + udata */ -#define EXTENDED_RAM_1024 -#ifdef EXTENDED_RAM_1024 - #define MAX_SWAPS 19 /*See platform devrd.c*/ - #define PTABSIZE 19 - #define TOTAL_SWAP_BLOCKS (1088-128) * 2 -#endif -#ifdef EXTENDED_RAM_512 - #define MAX_SWAPS 9 /*See platform devrd.c*/ - #define PTABSIZE 9 - #define TOTAL_SWAP_BLOCKS (576-128) * 2 -#endif -#define SWAPDEV 0x800 /* Device for swapping - RAM disk on standard memory expansion. */ - -/* We swap by hitting the user map */ -#define swap_map(x) ((uint8_t *)(x)) - -#define CONFIG_TD -#define CONFIG_TD_NUM 2 -/* IDE/CF support */ -#define CONFIG_TD_IDE -#define CONFIG_TINYIDE_SDCCPIO -#define CONFIG_TINYIDE_8BIT -#define IDE_IS_8BIT(x) 1 - -#define BOOTDEVICENAMES "hd#,fd" - -#define CONFIG_SMALL - - - - - diff --git a/Kernel/platform/platform-cpc6128-SME/cpc6128.s b/Kernel/platform/platform-cpc6128-SME/cpc6128.s deleted file mode 100644 index 8b52c16f02..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/cpc6128.s +++ /dev/null @@ -1,373 +0,0 @@ -; -; Amstrad CPC6128 support - - .module cpc6128 - - ; exported symbols - .globl init_early - .globl init_hardware - .globl _program_vectors - .globl plt_interrupt_all - .globl interrupt_handler - .globl unix_syscall_entry - .globl null_handler - .globl nmi_handler - - .globl map_kernel - .globl map_proc_always - .globl map_proc - .globl map_kernel_di - .globl map_proc_always_di - .globl map_save_kernel - .globl map_restore - .globl map_kernel_restore - .globl map_for_swap - .globl map_video - .globl current_map - - - .globl _need_resched - .globl _int_disabled - .globl _vtborder - .globl diskmotor - - ; exported debugging tools - .globl _plt_monitor - .globl _plt_reboot - .globl outchar - - ; imported symbols - .globl _ramsize - .globl _procmem - - .globl _vtoutput - .globl _vtinit - - .globl _do_beep - - .globl outcharhex - .globl outhl, outde, outbc - .globl outnewline - .globl outstring - .globl outstringhex - - .globl ___sdcc_enter_ix - - .include "kernel.def" - .include "../../cpu-z80/kernel-z80.def" - -; ----------------------------------------------------------------------------- -; COMMON MEMORY BANK (above 0xF000) -; ----------------------------------------------------------------------------- - .area _COMMONMEM - -_plt_monitor: - ; - ; Not so much a monitor as wait for space - ; Part of this code is borrowed from https://github.com/lronaldo/cpctelera - - ld bc,#0x7fc2 - out (c),c ; keep us mapped - ld bc, #0xF782 ;; [3] Configure PPI 8255: Set Both Port A and Port C as Output. - out (c), c ;; [4] 82 = 1000 0010 : (B7=1)=> I/O Mode, (B6-5=00)=> Mode 1, - ;; (B4=0)=> Port A=Output, (B3=0)=> Port Cu=Output, - ;; (B2=0)=> Group B, Mode 0,(B1=1)=> Port B=Input, (B0=0)=> Port Cl=Output - ld bc, #0xF40E ;; [3] Write (0Eh = 14) on PPI 8255 Port A (F4h): the register we want to select on AY-3-8912 - ld e, b ;; [1] Save F4h into E to use it later in the loop - out (c), c ;; [4] - - ld bc, #0xF6C0 ;; [3] Write (C0h = 11 000000b) on PPI Port C (F6h): operation > select register - ld d, b ;; [1] Save F6h into D to use it later in the loop - out (c), c ;; [4] - .dw #0x71ED ; out (c), 0 ;; [4] out (C), 0 => Write 0 on PPI's Port C to put PSG's in inactive mode - ;; .... (required in between different operations) - ld bc, #0xF792 ;; [3] Configure PPI 8255: Set Port A = Input, Port C = Output. - out (c), c ;; [4] 92h= 1001 0010 : (B7=1)=> I/O Mode, (B6-5=00)=> Mode 1, - ;; (B4=1)=> Port A=Input, (B3=0)=> Port Cu=Output, - ;; (B2=0)=> Group B, Mode 0, (B1=1)=> Port B=Input, (B0=0)=> Port Cl=Output - ld a, #0x45 ;; SPACE - ld b, d ;; [1] B = F6h => Write the value of A to PPI's Port C to select next Matrix Line - out (c), a ;; [4] - ld b, e ;; [1] B = F4h => Read from PPI's Port A: Pressed/Not Pressed Values from PSG - in a,(c) - rla - jr c, _plt_monitor - -_plt_reboot: - di - ;halt ;we are debugging why we end here - ld bc, #0x7f89 ;this would set the firmware ready for boot into firmware with (out (c),c ; rst0) - out (c), c - rst 0 ; back into our booter - -plt_interrupt_all: - ret - - .area _COMMONMEM - -_int_disabled: - .db 1 - -_vtborder: ; needs to be common - .db 0 - - -; ----------------------------------------------------------------------------- -; KERNEL CODE BANK (below 0xC000) -; ----------------------------------------------------------------------------- - .area _CODE - -init_early: - - call _program_early_vectors - ret - -init_hardware: - ; set system RAM size - ld hl, #128 - ld (_ramsize), hl - ld hl, #64 ; 64K for kernel/screen/etc (FIXME) - ld (_procmem), hl - - ; Install rst shorteners - ld hl,#rstblock - ld de,#8 - ld bc,#32 - ldir - - ld bc,#0x7fc3 - ; bank 7 (common) in high in either mapping - ; video bank 3 at &4000 - out (c),c - ; and we should have special mapping - ; already by now - ld bc,#0x7f10 - out (c),c - ld a,#0x54 ; - ld (_vtborder), a - out (c),a ; black border - ld bc,#0x7f00 - out (c),c - ld a,#0x44 - out (c),a ; blue paper - ld bc,#0x7f01 - out (c),c - ld a,#0x4b - out (c),a ; white ink - - - ;we set the crtc for a screen with 64x32 colsxrows - ;pros: max number of characters on screen and easy hardware scroll - ;cons: 80x25 is more standard => TODO list (with mode change) - ld bc,#0xbc01 - out (c),c - ld bc,#0xbd20 - out (c),c - ld bc,#0xbc02 - out (c),c - ld bc,#0xbd2A - out (c),c - ld bc,#0xbc06 - out (c),c - ld bc,#0xbd20 - out (c),c - ld bc,#0xbc07 - out (c),c - ld bc,#0xbd22 - out (c),c - - call _do_beep - - ; screen initialization - call _vtinit - - ret - -;------------------------------------------------------------------------------ -; COMMON MEMORY PROCEDURES FOLLOW - - .area _COMMONMEM - -_program_early_vectors: - call map_proc_always - call set_vectors - call map_kernel -set_vectors: - ; write zeroes across all vectors - ld hl, #0 - ld de, #1 - ld bc, #0x007f ; program first 0x80 bytes only - ld (hl), #0x00 - ldir - - ; now install the interrupt vector at 0x0038 - ld a, #0xC3 ; JP instruction - ld (0x0038), a - ld hl, #interrupt_handler - ld (0x0039), hl - - ; set restart vector for FUZIX system calls - ld (0x0030), a ; (rst 30h is unix function call vector) - ld hl, #unix_syscall_entry - ld (0x0031), hl - - ld (0x0000), a - ld hl, #null_handler ; to Our Trap Handler - ld (0x0001), hl - - ld (0x0066), a ; Set vector for NMI - ld hl, #nmi_handler - ld (0x0067), hl - -_program_vectors: - ret - - ; Swap helper. Map the page in A into the address space such - ; that swap_map() gave the correct pointer to use. Undone by - ; a map_kernel_{restore} -map_proc: - ld a, h - or l - jr z, map_kernel -map_for_swap: -map_proc_always: -map_proc_always_di: - push af - ld a,#0xc2 ; 4 5 6 7 - jr map_a_pop -; -; Save and switch to kernel -; -map_save_kernel: - push af - ld a, (current_map) - ld (map_store), a - pop af -map_kernel_di: -map_kernel: -map_kernel_restore: - push af - ld a,#0xc1 ; 0 1 2 7 -map_a_pop: - push bc - ld (current_map),a - ld bc,#0x7f00 - out (c),a - pop bc - pop af - ret - -map_video: - push af - ld a,#0xc3 ; 0 3 2 7 - jr map_a_pop - -map_restore: - push af - ld a, (map_store) - jr map_a_pop - -; -; We have no easy serial debug output instead just breakpoint this -; address when debugging. -; -outchar: - ld (_tmpout), a - push bc - push de - push hl - push ix - ld hl, #1 - push hl - ld hl, #_tmpout - push hl - call _vtoutput - pop af - pop af - pop ix - pop hl - pop de - pop bc - ret - - .area _COMMONMEM -_tmpout: - .db 1 - -current_map: ; place to store current page number. Is needed - .db 0 ; because we have no ability to read 0xF4 port - ; to detect what page is mapped currently -map_store: - .db 0 - -_need_resched: - .db 0 - -diskmotor: - .db 0 - -; -; Stub helpers for code compactness. Note that -; sdcc_enter_ix is in the standard compiler support already -; - .area _DISCARD - -; -; The first two use an rst as a jump. In the reload sp case we don't -; have to care. In the pop ix case for the function end we need to -; drop the spare frame first, but we know that af contents don't -; matter -; - -rstblock: - jp ___sdcc_enter_ix - .ds 5 -___spixret: - ld sp,ix - pop ix - ret - .ds 3 -___ixret: - pop af - pop ix - ret - .ds 4 -___ldhlhl: - ld a,(hl) - inc hl - ld h,(hl) - ld l,a - ret - -; -; Helpers for the CH375 -; - .area _CODE - - .globl _nap20 - -_nap20: ;modified, in the cpc 1 nop = 1us. the call-ret add some us', it can be optimized (FIXME) - - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - - ret - diff --git a/Kernel/platform/platform-cpc6128-SME/cpcvideo.s b/Kernel/platform/platform-cpc6128-SME/cpcvideo.s deleted file mode 100644 index 21e2af9b93..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/cpcvideo.s +++ /dev/null @@ -1,44 +0,0 @@ -; -; cpc vt primitives -; - - .module cpcvideo - - ; exported symbols - .globl _plot_char - .globl _scroll_down - .globl _scroll_up - .globl _cursor_on - .globl _cursor_off - .globl _cursor_disable - .globl _clear_lines - .globl _clear_across - .globl _do_beep - .globl _fontdata_8x8 - .globl _curattr - .globl _vtattr - - .globl map_video - .globl map_kernel - - ; Build the video library as the only driver - -CPCVID_ONLY .equ 1 -SCREENBASE .equ 0x40 - -.macro VIDEO_MAP - call map_video -.endm - -.macro VIDEO_UNMAP - call map_kernel -.endm - - .globl _fontdata_8x8 - -;_fontdata_8x8 .equ 0xF000 ; routines except this - ; to point to space char - .area _COMMONMEM - - .include "../../dev/cpc/video.s" - diff --git a/Kernel/platform/platform-cpc6128-SME/crt0.s b/Kernel/platform/platform-cpc6128-SME/crt0.s deleted file mode 100644 index 042e521703..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/crt0.s +++ /dev/null @@ -1,211 +0,0 @@ - .module crt0 - - ; - ; High space - read only - ; - - .area _CODE - .area _CODE2 - ; - ; Try and keep code in the top 32K - ; - - - ; - ; Our common lives low - ; - .area _CODE3 - .area _VIDEO ; must end below 0x4000 - .area _FONT - .area _INITIALIZED - .area _HOME - .area _CONST - - ; - ; Beyond this point we just zero. - ; - - .area _DATA - .area _BSEG - .area _BSS - .area _HEAP - .area _GSINIT - .area _GSFINAL - - ; - ; Finally the buffers so they can expand - ; - .area _BUFFERS - ; Somewhere to throw it out of the way - .area _INITIALIZER - - .area _DISCARD - - - .area _COMMONMEM - - - - ; imported symbols - .globl _fuzix_main - .globl init_early - .globl init_hardware - .globl l__BUFFERS - .globl s__BUFFERS - .globl l__COMMONMEM - .globl s__COMMONMEM - .globl l__DATA - .globl s__DATA - .globl l__DISCARD - .globl s__DISCARD - .globl l__FONT - .globl s__FONT - .globl kstack_top - - .globl unix_syscall_entry - .globl nmi_handler - .globl interrupt_handler - - .include "kernel.def" - .include "../../cpu-z80/kernel-z80.def" - - ; - ; startup code - ; - ; We loaded the rest of the kernel from disk and jumped here - ; - - .area _CODE - - .globl _start - - - -_start: - - - di - ld sp, #kstack_top - ; - ; move the common memory where it belongs - ld hl, #s__DATA - ld de, #s__COMMONMEM - ld bc, #l__COMMONMEM - ldir - - ; then the font -; ld de, #s__FONT -; ld bc, #l__FONT -; ldir - - ; then the discard (backwards as will overlap) - ld de, #s__DISCARD - ld bc, #l__DISCARD-1 - ex de,hl - add hl,bc - ex de,hl - add hl,bc - lddr - ldd - - ; then zero the data area - ld hl, #s__DATA - ld de, #s__DATA + 1 - ld bc, #l__DATA - 1 - ld (hl), #0 - ldir - ; and buffers - ld hl, #s__BUFFERS - ld de, #s__BUFFERS + 1 - ld bc, #l__BUFFERS - 1 - ld (hl), #0 - ldir - - ;We are loading from a .sna with first 64k filled with fuzix.bin starting at 0x100 - ;copy bank 3 to bank 7 in C7 mode (7 at 0x4000), zero bank 3 (vmem) and switch to C1 (kernel map) - ;when a proper loader is done this should be managed there - - ld bc,#0x7fc7 - out (c),c - - ld hl, #0xc000 - ld de, #0x4000 - ld bc, #0x4000 - ldir - - ld bc,#0x7f00 - out (c),c - ld a,#0x44 - out (c),a ; blue paper - ld bc,#0x7f01 - out (c),c - ld a,#0x4b - out (c),a ; white ink - - ld hl, #0xc000 - ld de, #0xc001 - ld bc, #0x3fff - ld (hl), #0 - ldir - - ld bc,#0x7fc1 - out (c),c - - ld hl,#copyfont - ld de,#0xF000 - ld bc,#(copyfont_end-copyfont) - ldir - - call #0xF000 - - ld hl,#0xF000 - ld de,#0xF001 - ld bc,#(copyfont_end-copyfont-1) - ld (hl),#0 - ldir - - ; Configure memory map - call init_early - - ; Hardware setup - call init_hardware - - ; Call the C main routine - call _fuzix_main - - ; main shouldn't return, but if it does... - di -stop: halt - jr stop - - ;code to copy font -copyfont: ;;this wil be in the loader - di - ld bc, #0x7faa ;RMR ->UROM disable LROM enable - out (c),c - ld hl, #0x3800 ;Firmware (LROM) character bitmaps - ld de, #(_fontdata_8x8) - ld bc, #0x800 - ldir - ld bc, #0x7fae ;RMR ->UROM disable LROM disable - out (c),c - ret -copyfont_end: - - - .area _BUFFERS -; -; 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 -; - - .globl _bufpool - .area _BUFFERS - -_bufpool: - .ds BUFSIZE * NBUFS - - .globl _fontdata_8x8 - .area _FONT - _fontdata_8x8: - .ds 2048 diff --git a/Kernel/platform/platform-cpc6128-SME/devices.c b/Kernel/platform/platform-cpc6128-SME/devices.c deleted file mode 100644 index 7dd80d5cb4..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/devices.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -/*#include */ -#include -#include -/*#include */ -#include - -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 */ - { devfd_open, no_close, devfd_read, devfd_write, no_ioctl }, - /* 2: /dev/tty TTY devices */ - { tty_open, tty_close, tty_read, tty_write, cpcvt_ioctl }, - /* 3: /dev/lpr Printer devices */ - { no_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 }, - /* 5: Pack to 7 with nxio if adding private devices and start at 8 */ - /* 5: unused */ - { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, - /* 6: unused */ - { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, - /* 7: unused */ - { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, - /* 8: Standard memory expansions RAM swap */ - { rd_open, no_close, rd_read, rd_write, no_ioctl }, -}; - - -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; -} - -void device_init(void) -{ -#ifdef CONFIG_TD_IDE - ide_probe(); -#endif -#ifdef CONFIG_TD_SD - sd_probe(); -#endif -ch375_probe(); - -#ifdef CONFIG_NET - sock_init(); -#endif -} - diff --git a/Kernel/platform/platform-cpc6128-SME/devrd.c b/Kernel/platform/platform-cpc6128-SME/devrd.c deleted file mode 100644 index 29c3a6e79e..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/devrd.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * CPC standard RAM bank memory expansions ramdisc driver, based on platform zxdiv48. - */ - -#include -#include -#include -#include - -static int rd_transfer(uint8_t is_read, uint8_t rawflag) -{ - nblock = udata.u_nblock; - block = udata.u_block; - rd_dptr = udata.u_dptr; - rd_wr = is_read; - uint16_t swap_bank_long; - uint8_t ct = 0; - - #ifdef DEBUG - kprintf("u_dptr %p Block %u u_nblock %u rd_wr %u\n",udata.u_dptr, udata.u_block, udata.u_nblock, rd_wr); - #endif - - /* It's a disk but only for swapping (and rd_io isn't general purpose) */ - if (((block + nblock) > (TOTAL_SWAP_BLOCKS - 1)) || (rawflag == 1)) { - udata.u_error = EIO; - kprintf("dev_rd_EIO"); - return -1; - } - - /* udata could change under us so keep variables privately */ - while (ct < nblock) { - swap_bank_long = (block >> 5); - swap_bank_long = swap_bank_long + 196 + (((swap_bank_long + 8) / 4) * 4); /*Convert bank number to Register MMR value - *See https://www.cpcwiki.eu/index.php/Gate_Array#Register_MMR_.28RAM_memory_mapping.29*/ - if (swap_bank_long > 255){ - rd_swap_bank = swap_bank_long - 64; - rd_swap_mem_port_h = 0x7e; - } - else{ - rd_swap_bank = swap_bank_long; - rd_swap_mem_port_h = 0x7f; - } - rd_proc_bank = ((uint16_t)rd_dptr / 0x4000) + 0xc4; - rd_swap_bank_addr = ((block & 31) << BLKSHIFT) + 0x4000; - rd_proc_bank_addr = ((uint16_t)rd_dptr & 0x3fff) + 0x4000; - - #ifdef DEBUG - if (nblock == 1) - kprintf("swap_bank %p swap_addr %p proc_bank %p proc_addr %p count %u\n", rd_swap_bank, rd_swap_bank_addr, rd_proc_bank, rd_proc_bank_addr, ct); - #endif - rd_io(); - block++; - rd_dptr += BLKSIZE; - ct++; - } - return ct << BLKSHIFT; /*Total bytes transferred*/ -} - -int rd_open(uint8_t minor, uint16_t flag) -{ - flag; - if(minor != 0) { - udata.u_error = ENODEV; - return -1; - } - return 0; -} - -int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag) -{ - flag;minor; - return rd_transfer(true, rawflag); -} - -int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag) -{ - flag;minor; - return rd_transfer(false, rawflag); -} - diff --git a/Kernel/platform/platform-cpc6128-SME/devrd.h b/Kernel/platform/platform-cpc6128-SME/devrd.h deleted file mode 100644 index e7c4451555..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/devrd.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * CPC standard RAM bank memory expansions ramdisc driver - */ - -int rd_open(uint8_t minor, uint16_t flags); -int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag); -int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag); - -extern uint8_t rd_wr; -extern uint8_t rd_swap_bank; -extern uint8_t rd_swap_mem_port_h; -extern uint8_t rd_proc_bank; -extern uint8_t *rd_dptr; -extern uint16_t rd_swap_bank_addr; -extern uint16_t rd_proc_bank_addr; -extern uint16_t nblock; -extern blkno_t block; - -void rd_io(void); diff --git a/Kernel/platform/platform-cpc6128-SME/devtty.c b/Kernel/platform/platform-cpc6128-SME/devtty.c deleted file mode 100644 index 0f1733f313..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/devtty.c +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static char tbuf1[TTYSIZ]; - -uint8_t vtattr_cap = VTA_UNDERLINE; -extern uint8_t curattr; - -tcflag_t termios_mask[NUM_DEV_TTY + 1] = { - 0, - _CSYS -}; - - -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}, -}; - -/* tty1 is the screen */ - -/* Output for the system console (kprintf etc) */ -void kputchar(char c) -{ - if (c == '\n') - tty_putc(0, '\r'); - tty_putc(0, c); -} - -/* Both console and debug port are always ready */ -ttyready_t tty_writeready(uint8_t minor) -{ - minor; - return TTY_READY_NOW; -} - -void tty_putc(uint8_t minor, unsigned char c) -{ - minor; - vtoutput(&c, 1); -} - -int tty_carrier(uint8_t minor) -{ - minor; - return 1; -} - -void tty_setup(uint8_t minor, uint8_t flags) -{ - minor; -} - -void tty_sleeping(uint8_t minor) -{ - minor; -} - -void tty_data_consumed(uint8_t minor) -{ -} - - -/* This is used by the vt asm code, but needs to live in the kernel */ -uint16_t cursorpos; - -//Inherited from spectrum zx+3. For now ignore attributes, try later to implement inverse and underline touching char output directly -void vtattr_notify(void) -{ - // Attribute byte fixups: not hard as the colours map directly - // to the spectrum ones -/* if (vtattr & VTA_INVERSE) - curattr = ((vtink & 7) << 3) | (vtpaper & 7); - else - curattr = (vtink & 7) | ((vtpaper & 7) << 3); - - if (vtattr & VTA_FLASH) - curattr |= 0x80; - // How to map the bright bit - we go by either - if ((vtink | vtpaper) & 0x10) - curattr |= 0x40; -*/ - //we are now debugging other things - //vtink = 26; - //vtpaper = 1; - //cpcvt_ioctl(1, VTINK, &vtink); - //cpcvt_ioctl(1, VTPAPER, &vtpaper); - -} - - -__sfr __banked __at 0x7F00 gatearray; -/* see: https://www.cpcwiki.eu/index.php/Gate_Array */ - -int cpcvt_ioctl(uint8_t minor, uarg_t arg, char *ptr) -{ - uint8_t c; - if (minor == 1 && arg == VTBORDER) { - c = ugetc(ptr); - gatearray = PENR_BORDER_SELECT; - vtborder &= INKR_COLOR_SET; - vtborder |= c & 0x1F; - gatearray = vtborder; - return 0; - } - if (minor == 1 && arg == VTINK) { - c = ugetc(ptr); - gatearray = PENR_INK_SELECT; - vtink &= INKR_COLOR_SET; - vtink |= c & 0x1F; - gatearray = vtink; - return 0; - } - if (minor == 1 && arg == VTPAPER) { - c = ugetc(ptr); - gatearray = PENR_PAPER_SELECT; - vtpaper &= INKR_COLOR_SET; - vtpaper |= c & 0x1F; - gatearray = vtpaper; - return 0; - } - return vt_ioctl(minor, arg, ptr); -} diff --git a/Kernel/platform/platform-cpc6128-SME/devtty.h b/Kernel/platform/platform-cpc6128-SME/devtty.h deleted file mode 100644 index 57d7727ead..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/devtty.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef __DEVTTY_DOT_H__ -#define __DEVTTY_DOT_H__ - -void tty_pollirq(void); -static void keydecode(void); - -#define KEY_ROWS 10 -#define KEY_COLS 8 -#define PENR_BORDER_SELECT 0x10 -/*For now We are in mode 2 with pen 0 for paper and pen 1 for ink*/ -#define PENR_PAPER_SELECT 0x00 -#define PENR_INK_SELECT 0x01 -#define INKR_COLOR_SET 0x40 -extern uint8_t keymap[10]; -extern uint8_t keyboard[10][8]; -extern uint8_t shiftkeyboard[10][8]; - -extern uint8_t timer_wait; - -extern int cpcvt_ioctl(uint8_t minor, uarg_t arg, char *ptr); - -extern uint8_t vtborder; - -#endif diff --git a/Kernel/platform/platform-cpc6128-SME/discard.c b/Kernel/platform/platform-cpc6128-SME/discard.c deleted file mode 100644 index 164fb2a423..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/discard.c +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include -#include -#include -#include -#include - -uint8_t plt_param(char *p) -{ - return 0; -} - -/* Nothing to do for the map of init */ -void map_init(void) -{ - uint_fast8_t i; - for (i = 0; i < MAX_SWAPS; i++) - swapmap_init(i); -} - -void plt_copyright(void) -{ - kprintf("Amstrad CPC6128 platform\nCopyright (c) 2024-2025 Antonio J. Casado Alias\n"); -} -/* -void ide_reset(void) -{ - ide_std_reset(); -} -*/ - diff --git a/Kernel/platform/platform-cpc6128-SME/fdc765.s b/Kernel/platform/platform-cpc6128-SME/fdc765.s deleted file mode 100644 index 76c0312ab3..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/fdc765.s +++ /dev/null @@ -1,530 +0,0 @@ -; -; 765 Floppy Controller Support -; -; This is based upon the Amstrad NC200 driver by David Given and -; this example https://cpctech.cpcwiki.de/source/fdcload.html from Kevin Thacker site -; -; It differs on the CPC in the following ways -; -; - The timings are tighter so we use in a,(c) jp p and other -; tricks to make the clocks. Even so it should be in uncontended RAM -; -; - The CPC doesn't expose the tc line, so if the 765 decides to -; expect more data or feed us more data all we can do is dump it or -; feed it crap until it shuts up -; -; - We do motor and head loading delays (possibly some of those should -; be backported - FIXME) -; -; - We don't hang if the controller tells us no more data when we -; think we need to feed it command bytes (BACKPORT NEEDED) -; -; TODO -; Initialize drive step rate etc (we rely on the firmware for now) -; Step rate -; Head load/unload times -; Write off time af -; (12ms step 30ms head stabilize, 4ms head load, max (0xf) head -; unload) -; - .module fdc765 - - .include "kernel.def" - .include "../../cpu-z80/kernel-z80.def" - - .globl map_proc_always - .globl map_kernel - - .globl _fd765_do_nudge_tc - .globl _fd765_do_recalibrate - .globl _fd765_do_seek - .globl _fd765_do_read - .globl _fd765_do_write - .globl _fd765_do_read_id - .globl _fd765_motor_on - .globl _fd765_motor_off - - .globl _fd765_track - .globl _fd765_head - .globl _fd765_sector - .globl _fd765_status - .globl _fd765_buffer - .globl _fd765_is_user - .globl _fd765_sectors - .globl _fd765_drive - - .globl _vtborder - - .globl diskmotor - - .area _COMMONMEM - -; -; Twiddle the Terminal Count line to the FDC. Not supported by the -; CPC -; -_fd765_do_nudge_tc: - ret - -; Writes A to the FDC data register. - -fd765_tx: - push bc -; ex af, af' -; ld bc,#0xfb7e ; floppy register (16bit access) -;fd765_tx_loop: -; in a, (c) -; add a -; jr nc, fd765_tx_loop -; ; FIXME: backport this fix -; add a -; jr c, fd765_tx_exit ; controller doesn't want data ?? -; ex af, af' -; ld c,#0x7f -; out (c), a -; ex (sp),hl -; ex (sp),hl -;fd765_tx_exit: - ld bc,#0xfb7e ;; I/O address for FDC main status register - push af ;; - fwc1: in a,(c) ;; - add a,a ;; - jr nc,fwc1 ;; - add a,a ;; - jr nc,fwc2 ;; - pop af ;; - ret - - fwc2: - pop af ;; - - inc c ;; - out (c),a ;; write command byte - dec c ;; - - ;; some FDC documents say there must be a delay between each - ;; command byte, but in practice it seems this isn't needed on CPC. - ;; Here for compatiblity. - ld a,#5 ;; - fwc3: dec a ;; - jr nz,fwc3 ;; - pop bc - ; FIXME: is our delay quite long enough for spec ? - ; might need them to be ex (sp),ix ? - 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: - ld hl, #_fd765_status -; ld c, #0x7e ; we flip between 2ffd 3ffd as we go -;read_status_loop: -; ld b,#0xfb ; control port -; in a, (c) -; rla ; RQM... -; jr nc, read_status_loop ; ...low, keep waiting -; rla ; DIO... -; ret nc ; ...low, no more data -; ld c,#0x7f ; data port -; in a,(c) ; INI ? FIXME -; ld (hl),a -; inc hl -; ex (sp),hl ; wait for the 765A -; ex (sp),hl -; ex (sp),hl -; ex (sp),hl -; jr read_status_loop ; next byte - ld bc,#0xfb7e - fr1: - in a,(c) - cp #0xc0 - jr c,fr1 - - inc c - in a,(c) - dec c - ld (hl),a - inc hl - - ld a,#5 - fr2: - dec a - jr nz,fr2 - in a,(c) - and #0x10 - jr nz,fr1 - - - ret -_fd765_status: - .ds 8 ; 8 bytes of status data - -; Sends the head/drive byte of a command. - -send_head: - ld hl, (_fd765_head) ; l = head h = drive) - ld a, l - add a - add a - add h - jr fd765_tx - -; Performs a RECALIBRATE command. - -_fd765_do_recalibrate: - ld a, #0x07 ; RECALIBRATE - call fd765_tx - ld a, (_fd765_drive) ; drive # - call fd765_tx - jr wait_for_seek_ending - -; Performs a SEEK command. - -_fd765_do_seek: - ld a, #0x0f ; SEEK - call fd765_tx - call send_head ; specified head, drive #0 - ld a, (_fd765_track) ; specified track - call fd765_tx - jr wait_for_seek_ending -_fd765_track: - .db 0 -_fd765_sector: - .db 0 -; -; These two must remain adjacent see send_head -; -_fd765_head: - .db 0 -_fd765_drive: - .db 0 - -; Waits for a SEEK or RECALIBRATE command to finish by polling SENSE INTERRUPT STATUS. -wait_for_seek_ending: - - ld a, #0x08 ; SENSE INTERRUPT STATUS - call fd765_tx - call fd765_read_status - - ld a, (#_fd765_status) - bit 5, a ; SE, seek end - jr z, wait_for_seek_ending - - bit 4,a - - ret - - ; Now settle the head (FIXME: what is the right value ?) - ld a, #30 ; 30ms -; -; This assumes uncontended timing -; -wait_ms: - push bc -wait_ms_loop: - ld b,#0xDC -wait_ms_loop2: - dec b - jr nz, wait_ms_loop2 - dec a - jr nz, wait_ms_loop - pop bc - ret - -_fd765_motor_off: - push bc - ld bc,#0xfa7e - xor a - ld (diskmotor),a - out (c),a - pop bc - ret - -_fd765_motor_on: - ld a,(diskmotor) - or a - ret nz - ld a,#0x01 - ld (diskmotor),a - ; Take effect - ld bc,#0xfa7e - out (c),a - ; Now wait for spin up - - ld e,#10 ; FIXME right value ?? -wait2: - ; The classic Z80 KHz timing loop - ld bc,#3548 ; 3.548MHz for spectrum, should change for cpc.FIXME -wait1: - dec bc - ld a,b - or c - jr nz, wait1 - dec e - jr nz, wait2 - 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: - ld a, #0x46 ; READ SECTOR MFM - - ; FIXME: need to return a last cmd byte here and write it - ; after this crap or we may miss if we write just the sector hits - ; the head (BACKPORT ME ??) - call setup_read_or_write - - ld a, (_fd765_is_user) - or a - push af - call nz, map_proc_always - - ld bc,#0x7f10 - out (c),c - ld c,#0x46 ;Cyan - out (c),c - - di ; performance critical, - ; run with interrupts off - xor a - call fd765_tx ; send the final unused byte - ; to fire off the command - ld hl, (_fd765_buffer) - ld bc, #0xfb7e - -fdc_data_read: - in a,(c) ;; FDC has data and the direction is from FDC to CPU - jp p,fdc_data_read ;; - and #0x20 ;; "Execution phase" i.e. indicates reading of sector data - jp z,fdc_read_end - - inc c ;; BC = I/O address for FDC data register - in a,(c) ;; read from FDC data register - ld (hl),a ;; write to memory - dec c ;; BC = I/O address for FDC main status register - inc hl ;; increment memory pointer - jp fdc_data_read - -fdc_read_end: - -; ld de, #0x2000 ; so we can make timing -; jp read_wait -; -; First 256 bytes - -;read_loop: -; inc c ; data port -; ini -; inc b -; dec c ; control port -; dec e -; jp z, read_wait2 -;read_wait: -; in a,(c) ; read the fdc status -; jp p, read_wait -; and d -; jp nz, read_loop -; jp read_finished -; -; Second 256 bytes -; -;read_loop2: -; inc c ; data port -; ini -; inc b -; dec c ; control port -; dec e -; jp z, read_flush_wait -;read_wait2: -; in a,(c) ; read the fdc status -; jp p, read_wait2 -; and d -; jp nz, read_loop2 -; jp read_finished -; -; Flush out any extra data (no tc control) -; -;read_flush: -; inc c -; in a,(c) -; dec c -;read_flush_wait: -; in a,(c) -; jp p, read_flush_wait -; and d -; jp nz, read_flush -; -; And done -; -read_finished: - ld (_fd765_buffer), hl - call _fd765_do_nudge_tc ; Tell FDC we've finished - ei - - call fd765_read_status - call tc_fix - - ld bc,#0x7f10 - out (c),c - ld a,(_vtborder) - out (c),a - - pop af - ret z - jp map_kernel - -; -; We will get an error reported that the command did not complete -; because the tc bit is not controllable. Spot that specific error -; and ignore it. -; -tc_fix: - ld hl,#_fd765_status - ld a,(hl) - and #0xC0 - cp #0x40 - ret nz - inc hl - bit 7,(hl) - ret z - res 7,(hl) - dec hl - res 6,(hl) - ret - -; -; Write is much like read just the other direction -; -_fd765_do_write: - ; interrupts off - ld a, #0x45 ; WRITE SECTOR MFM - call setup_read_or_write - - ld a, (_fd765_is_user) - or a - push af - call nz, map_proc_always - - ld bc,#0x7f10 - out (c),c - ld c,#0x47 ;Pink - out (c),c - - di - - xor a - call fd765_tx ; send the final unused 0 byte - ; to fire off the command - ld hl, (_fd765_buffer) - ld bc, #0xfb7e -fdc_data_write: - in a,(c) ;; FDC has data and the direction is from FDC to CPU - jp p,fdc_data_write ;; - and #0x20 ;; "Execution phase" i.e. indicates reading of sector data - jp z,fdc_write_end - - inc c ;; BC = I/O address for FDC data register - ld a,(hl) ;; read from memory - out (c),a ;; write to FDC data register - dec c ;; BC = I/O address for FDC main status register - inc hl ;; increment memory pointer - jp fdc_data_write - -fdc_write_end: -; ld de,#0x2000 ; to make timing -; jp write_wait -; -;write_loop: -; inc c -; outi -; inc b -; dec c -; dec e -; jp z, write_wait2 -;write_wait: -; in a,(c) -; jp p, write_wait -; and d -; jp nz, write_loop -; jp write_finished -;write_loop2: -; inc c -; outi -; inc b -; dec c -; dec e -; jp z, write_flush_wait -;write_wait2: -; in a,(c) -; jp p, write_wait2 -; and d -; jp nz, write_loop2 -; jp write_finished -;write_flush: -; inc c -; in a,(c) -; dec c -;write_flush_wait: -; in a,(c) -; jp p, write_flush_wait -; and d -; jp nz, write_flush -write_finished: - ld (_fd765_buffer), hl - call _fd765_do_nudge_tc ; Tell FDC we've finished - ei - call fd765_read_status - call tc_fix - - ld bc,#0x7f10 - out (c),c - ld a,(_vtborder) - out (c),a - - pop af - ret z - jp 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 - ld a, (_fd765_track) ; 2: specified track - call fd765_tx - ld a, (_fd765_head) ; 3: specified head - call fd765_tx - ld a, (_fd765_sector) ; 4: specified sector - ld b, a - call fd765_tx - ld a, #2 ; 5: bytes per sector: 512 - call fd765_tx - ld a, (_fd765_sectors) - add b ; add first sector - dec a ; 6: last sector (*inclusive*) - call fd765_tx - ld a, #0x2A ; 7: Gap 3 length (2A is standard for 3" drives) - 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: - .dw 0 -_fd765_is_user: - .db 0 -_fd765_sectors: - .db 0 - -; Read the next sector ID off the disk. -; (Only used for debugging.) - -_fd765_do_read_id: - ld a, #0x4a ; READ MFM ID - call fd765_tx - call send_head ; specified head, drive 0 - jp fd765_read_status diff --git a/Kernel/platform/platform-cpc6128-SME/fuzix-platform-cpc6128-SME.pkg b/Kernel/platform/platform-cpc6128-SME/fuzix-platform-cpc6128-SME.pkg deleted file mode 100644 index 526097fcbd..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/fuzix-platform-cpc6128-SME.pkg +++ /dev/null @@ -1,7 +0,0 @@ -package platform-cpc6128-SME - -disable-pkg platform-cpc6128-SME - -l /bin/sh /bin/sh.orig -r /bin/sh -l /bin/fsh /bin/sh \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/fuzix.export b/Kernel/platform/platform-cpc6128-SME/fuzix.export deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Kernel/platform/platform-cpc6128-SME/fuzix.lnk.bck b/Kernel/platform/platform-cpc6128-SME/fuzix.lnk.bck deleted file mode 100644 index cb04214f01..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/fuzix.lnk.bck +++ /dev/null @@ -1,59 +0,0 @@ --mwxuy --i fuzix.ihx --l z80 --b _CODE=0x0100 --b _COMMONMEM=0xF300 -platform/platform-cpc6128/crt0.rel -platform/platform-cpc6128/commonmem.rel -platform/platform-cpc6128/plus3.rel -platform/platform-cpc6128/zxvideo.rel -platform/platform-cpc6128/main.rel -platform/platform-cpc6128/discard.rel -start.rel -version.rel -cpu-z80/lowlevel-z80.rel -cpu-z80/usermem_std-z80.rel -platform/platform-cpc6128/tricks.rel -timer.rel -kdata.rel -usermem.rel -platform/platform-cpc6128/devices.rel -devio.rel -filesys.rel -blk512.rel -process.rel -inode.rel -syscall_exec.rel -syscall_exec16.rel -syscall_fs.rel -syscall_fs2.rel -syscall_fs3.rel -syscall_proc.rel -syscall_other.rel -syscall_net.rel -tty.rel -vt.rel -font8x8.rel -mm.rel -memalloc_none.rel -simple.rel -swap.rel -devsys.rel -devinput.rel -kmod.rel -network.rel -platform/platform-cpc6128/devtty.rel -platform/platform-cpc6128/tinyide.rel -platform/platform-cpc6128/tinyide_discard.rel -platform/platform-cpc6128/divide.rel -platform/platform-cpc6128/tinysd.rel -platform/platform-cpc6128/tinysd_discard.rel -platform/platform-cpc6128/zxmmc.rel -platform/platform-cpc6128/tinydisk.rel -platform/platform-cpc6128/tinydisk_discard.rel -platform/platform-cpc6128/devinput.rel -platform/platform-cpc6128/zxkeyboard.rel -platform/platform-cpc6128/devfdc765.rel -platform/platform-cpc6128/fdc765.rel -platform/platform-cpc6128/net_w5x00.rel --e diff --git a/Kernel/platform/platform-cpc6128-SME/kernel.def b/Kernel/platform/platform-cpc6128-SME/kernel.def deleted file mode 100644 index d061dd72e8..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/kernel.def +++ /dev/null @@ -1,13 +0,0 @@ -; UZI mnemonics for memory addresses etc - -; We stick it straight after the tag -U_DATA__TOTALSIZE .equ 0x200 ; 256+256@F000 - -Z80_TYPE .equ 1 - -PROGBASE .equ 0x0000 -PROGLOAD .equ 0x0100 - -NBUFS .equ 5 - -Z80_MMU_HOOKS .equ 0 diff --git a/Kernel/platform/platform-cpc6128-SME/loader.s b/Kernel/platform/platform-cpc6128-SME/loader.s deleted file mode 100644 index be3d137ab4..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/loader.s +++ /dev/null @@ -1,413 +0,0 @@ -;;from https://cpctech.cpc-live.com/ -;; Load data by talking directly to the NEC765 floppy disc controller (FDC) -;; https://cpctech.cpcwiki.de/source/fdcload.html from Kevin Thacker site -;; Code assumes there is a drive 0 and there is a disc in it and the disc is formatted -;; to SYSTEM format. -.area BOOT (ABS) -.org #0xfdff - -;;ROM's & interrupts off - -di -ld b, #0x7f -ld c,#0b10101101 -out (c),c -;;from default boot org to the needed one -ld hl,#0x100 -ld de,#0xfdff -ld bc,#512 -ldir - -jp start - -start: -;;clean room -ld hl,#0 -ld de ,#1 -ld (#0),hl -ld bc,#0xfdfe -ldir -;;patch interrupt vector -LD HL,#0XC9FB -LD (#0X0038),HL -;; turn on disc motor -ld bc,#0xfa7e -ld a,#1 -out (c),a -;; the motor on all connected drives will be turned on -;; and the motor will start to speed up. -;; -;; The drive must be "Ready" to accept commands from the FDC. -;; A drive will be ready if: -;; * the drive motor has reached a stable speed -;; * there is a disc in the drive -;; -;; The following code is a delay which will ait enough time for the motor -;; to reach a stable speed. (i.e. the motor speed is not increasing or decreasing) -;; -;; All drives are not the same, some 3" drives take longer to reach a stable -;; speed so we need a longer delay to be compatible with these. -;; -;; At this point interrupts must be enabled. -ei -ld b,#30 ;; 30/6 = 5 frames or 5/50 of a second. -w1: -;; there are 6 CPC interrupts per frame. This waits for one of them -halt -djnz w1 -di -;; this is the drive we want to use -;; the code uses this variable. -ld a,#0 -ld (#drive),a - -;; recalibrate means to move the selected drive to track 0. -;; -;; track 0 is a physical signal from the drive that indicates when -;; the read/write head is at track 0 position. -;; -;; The drive itself doesn't know which track the read/write head is positioned over. -;; The FDC has an internal variable for each drive which holds the current track number. -;; This value is reset when the drive indicates the read/write head is over track 0. -;; The number is increased/decreased as the FDC issues step pulses to the drive to move the head -;; to the track we want. -;; -;; once a recalibrate has been done, both drive and fdc agree on the track. -;; -call fdc_recalibrate - -;; now the drive is at a known position and is ready the fdc knows it is at a known position -;; we can read data.. -;call read_file - -read_file: -;; set variable for starting sector for our data (#0xC1 is first sector ID for -;; SYSTEM format. Sector IDs are #0x41, #0x42, #0x43, #0x44, #0x45, #0x46, #0x47, #0x48 and #0x49. -;; This bootloader is in track 0 sector #0x41 to load with |cpm command from basic - -ld a,#0x42 -ld (#sector),a - -;; set variable for starting track for our data -;; Tracks are numbered 0 to 39 for 40 track drives and 0 to 79 for 80 track drives. -;; Some 3" drives can allow up to 42 tracks (0-41), some 80 track drives can allow up -;; to 83 tracks (0-82). -;; -;; Not all drives are the same however. The maximum that is compatible with all 3" drives -;; is 41 tracks. -ld a,#0 -ld (#track),a - -;; memory address to write data to (start) -ld de,#0x100 -ld (#data_ptr),de - -;; number of complete sectors to read for our data -;; 30 sectors, 512 bytes per sector. Total data to read is 30*512 = 15360 bytes. -ld a,#126 -ld (#sector_count),a - -read_sectors_new_track: -;; perform a seek (this means to move read/write head to track we want). -;; track is defined by the "track" variable. -;; -;; a recalibrate must be done on the drive before a seek is done. -;; -;; the fdc uses it's internal track value for the chosen drive to decide to seek up/down to -;; reach the desired track. The FDC issues "step pulses" which makes the read/write head move -;; 1 track at a time at the rate defined by the FDC specify command. -;; -;; e.g. if fdc thinks we are on track 10, and we ask it to move to track 5, it will step back 5 times -;; updating it's internal track number each time. -call fdc_seek - -read_sectors: -;; Send Read data command to FDC to read 1 sector. - -;; A track is layed out as follows: -;; -;; id field -;; data field -;; -;; id field -;; data field -;; -;; id field -;; data field -;; etc. -;; -;; we tell the FDC the values of the ID field we want. Once it finds a match it will then read -;; the data. If the ID field we want is not found, it will report an error. - - -ld a,#0b01000110 ;; read data command (mfm=double density reading mode) - ;; not multi-track. See FDC data sheet for list of commands and the - ;; number of bytes they need. -call fdc_write_command -ld a,(#drive) ;; physical drive and side - ;; bits 1,0 define drive, bit 2 defines side -call fdc_write_command -ld a,(#track) ;; C value from id field of sector we want to read -call fdc_write_command -ld a,#0 ;; H value from id field of sector we want to read -call fdc_write_command -ld a,(#sector) ;; R value from id field of sector we want to read -call fdc_write_command -ld a,#2 ;; N value from id field of sector we want to read - ;; this also determines the amount of data in the sector. - ;; 2 = 512 byte sector -call fdc_write_command -ld a,(#sector) ;; EOT = Last sector ID to read. This is the same as the first to read 1 sector. -call fdc_write_command -ld a,#0x2a ;; Gap Length for read. Not important. -call fdc_write_command -ld a,#0xff ;; DTL = Data length. Only valid when N is 0 it seems -call fdc_write_command - -;; There will be a delay here before the first byte of a sector is ready and -;; interrupts can be active. -;; -;; The FDC is reading from the track. It is searching for an ID field that -;; matches the values we have sent in the command. -;; -;; When it finds the ID field, there is furthur time before the data field -;; of the sector is found and it starts to read. -;; -;; Once it has found the data, we must read it all and quickly. -;; - - -;; interrupts must be off now for data to be read successfully. -;; -;; The CPU constantly asks the FDC if there is data ready, if there is -;; it reads it from the FDC and stores it in RAM. There is a timing -;; constraint, the FDC gives the CPU a byte every 32microseconds. -;; If the CPU fails to read one of the bytes in time, the FDC will report -;; an overrun error and stop data transfer. - - -;; current address to write data too. -ld de,(#data_ptr) - -;; this is the main loop -;; which reads the data -;; The FDC will give us a byte every 32us (double density disc format). -;; -;; We must read it within this time. - -fdc_data_read: -in a,(c) ;; FDC has data and the direction is from FDC to CPU -jp p,fdc_data_read ;; -and #0x20 ;; "Execution phase" i.e. indicates reading of sector data -jp z,fdc_read_end - -inc c ;; BC = I/O address for FDC data register -in a,(c) ;; read from FDC data register -ld (de),a ;; write to memory -dec c ;; BC = I/O address for FDC main status register -inc de ;; increment memory pointer -jp fdc_data_read - -fdc_read_end: -;; Interrupts can be enabled now we have completed the data transfer - - - -;; we will get here if we successfully read all the sector's data -;; OR if there was an error. - -;; read the result -call fdc_result - -;; check result -ld ix,#result_data -ld c,#0x54 -ld a,0(ix) -cp #0x40 -jp z,nerr -ld a,1(ix) -cp #0x80 -jp z,nerr -ld c,#0x40 -nerr: - -;; decrease number of sectors transferred -ld a,(#sector_count) -dec a -jp z,read_done -ld (#sector_count),a - -;; update ram pointer for next sector -ld hl,(#data_ptr) -ld bc,#512 -add hl,bc -ld (#data_ptr),hl - -;; update sector id (loops #0x41-#0x49). -ld a,(#sector) -inc a -ld (#sector),a -cp #0x4a ;; #0x49+1 (last sector id on the track+1) -jp nz,read_sectors -;; we read sector #0x49, the last on the track. -;; Update track variable so we seek to the next track before -;; reading the next sector -ld a,(#track) -inc a -ld (#track),a - -ld a,#0x41 ;; #0x41 = first sector id on the track -ld (#sector),a -jp read_sectors_new_track - -read_done: -ld bc,#0xfa7e -ld a,#0 -out (c),a ;stop the motor -jp 0x100 ;start FUZIX - -;;=============================================== -;; send command to fdc -;; - -fdc_write_command: - - -ld bc,#0xfb7e ;; I/O address for FDC main status register -push af ;; -fwc1: in a,(c) ;; -add a,a ;; -jr nc,fwc1 ;; -add a,a ;; -jr nc,fwc2 ;; -pop af ;; -ret - -fwc2: -pop af ;; - -inc c ;; -out (c),a ;; write command byte -dec c ;; - -;; some FDC documents say there must be a delay between each -;; command byte, but in practice it seems this isn't needed on CPC. -;; Here for compatiblity. -ld a,#5 ;; -fwc3: dec a ;; -jr nz,fwc3 ;; -ret ;; - -;;=============================================== -;; get result phase of command -;; -;; timing is not important here - -fdc_result: - -ld hl,#result_data -ld bc,#0xfb7e -fr1: -in a,(c) -cp #0xc0 -jr c,fr1 - -inc c -in a,(c) -dec c -ld (hl),a -inc hl - -ld a,#5 -fr2: -dec a -jr nz,fr2 -in a,(c) -and #0x10 -jr nz,fr1 - - -ret - -;;=============================================== - -;; physical drive -;; bit 1,0 are drive, bit 2 is side. -drive: -.db 0 - -;; physical track (updated during read) -track: -.db 0 - -;; id of sector we want to read (updated during read) -sector: -.db 0 - -;; number of sectors to read (updated during read) -sector_count: -.db 2 ;; enough for now - -;; address to write data to (updated during read) -data_ptr: -.ds 2 - -;;=============================================== - -fdc_seek: -ld a,#0b00001111 ;; seek command -call fdc_write_command -ld a,(#drive) -call fdc_write_command -ld a,(#track) -call fdc_write_command - -call fdc_seek_or_recalibrate -jp nz,fdc_seek -ret - -;;=============================================== - -fdc_recalibrate: - -;; seek to track 0 -ld a,#0b111 ;; recalibrate -call fdc_write_command -ld a,(#drive) ;; drive -call fdc_write_command - -call fdc_seek_or_recalibrate -jp nz,fdc_recalibrate -ret - -;;=============================================== -;; NZ result means to retry seek/recalibrate. - -fdc_seek_or_recalibrate: -ld a,#0b1000 ;; sense interrupt status -call fdc_write_command -call fdc_result - -;; recalibrate completed? -ld ix,#result_data -bit 5,0(ix) ;; Bit 5 of Status register 0 is "Seek complete" -jr z,fdc_seek_or_recalibrate -bit 4,0(ix) ;; Bit 4 of Status register 0 is "recalibrate/seek failed" -;; -;; Some FDCs will seek a maximum of 77 tracks at one time. This is a legacy/historical -;; thing when drives only had 77 tracks. 3.5" drives have 80 tracks. -;; -;; If the drive was at track 80 before the recalibrate/seek, then one recalibrate/seek -;; would not be enough to reach track 0 and the fdc will then report an error (meaning -;; it had seeked 77 tracks and failed to reach the track we wanted). -;; We repeat the recalibrate/seek to finish the movement of the read/write head. -;; -ret - -;;=============================================== - -file_buffer: -.dw 0x100 -result_data: -.ds 8 -end: \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/loader_firmware.s b/Kernel/platform/platform-cpc6128-SME/loader_firmware.s deleted file mode 100644 index b384288459..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/loader_firmware.s +++ /dev/null @@ -1,94 +0,0 @@ -;;from: https://cpctech.cpc-live.com/source/byteload.html, modified to load something bigger - -;; This example shows how to read a file byte by byte. -;; -;; A file without a header must be read this way, it can't be -;; read using CAS IN DIRECT (unless the in-memory header is patched) -;; -;; This example doesn't have any error checking. - -.area BOOT (ABS) -.org 0xa500 - -cas_in_open .equ 0xbc77 -cas_in_close .equ 0xbc7a -cas_in_char .equ 0xbc80 -kl_rom_walk .equ 0xbccb -mc_start_program .equ 0xbd16 - -ld c,#0xff -ld hl,#start -call mc_start_program -start: -ld hl,#0x0100 ;; address to load file data to (example) -push hl - -call kl_rom_walk -;; open file for reading -ld b,#end_filename-filename -ld hl,#filename -ld de,#two_k_buffer -call cas_in_open - -;; If a file is opened without a header: -;; - the filetype will be ASCII (0x16) -;; - the length and load address will be undefined. -;; -;; If a file is opened with a header, the -;; - the filetype will be taken from the header -;; - the length and load address will be taken from the header -;; -;; A file without a header can't be read with CAS IN DIRECT -;; and must be read using CAS IN CHAR. - -pop hl - -;; read a char from the file, character is returned in A register - -next_byte: -call cas_in_char -jr nc,not_eof -jr nz,not_eof - -;; could be end of file -;; test for hard end of file byte -cp #0xf -jr nz,not_eof -jr eof - -not_eof: -;; write byte to memory -ld (hl),a -inc hl -ld a,#0xA5 -cp h -jr z, avoid_crash -jr next_byte - - -eof: -call cas_in_close -ld de,#0xA500 -ld hl,#0xC000 -ld bc,#0x4000 -ldir -jp 0x100 ;; and go to fuzix - -avoid_crash: -ld b,#0x1b ;;0xc0-0xa5 -ld c,#0 -add hl,bc -jr next_byte - -;;------------------------------------------------------------------- -;; name of the file to read - -filename: -.ascii "FUZIX.BIN" -end_filename: - -;;------------------------------------------------------------------- -;; this buffer is filled with data from the file -.org 0xF7FF -two_k_buffer: -.blkb 2048 \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/main.c b/Kernel/platform/platform-cpc6128-SME/main.c deleted file mode 100644 index 03dfa901c7..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/main.c +++ /dev/null @@ -1,156 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -//#include - -uint16_t ramtop = PROGTOP; -uint16_t swap_dev = 0xFFFF; - -/* On idle we spin checking for the terminals. Gives us more responsiveness - for the polled ports */ -void plt_idle(void) -{ - /* We don't want an idle poll and IRQ driven tty poll at the same moment */ - __asm - halt - __endasm; -} - -uint8_t timer_wait; - -void plt_interrupt(void) -{ - tty_pollirq(); - timer_interrupt(); - poll_input(); - if (timer_wait) - wakeup(&timer_interrupt); - devfd_spindown(); -} - -/* - * So that we don't suck in a library routine we can't use from - * the runtime - */ - -size_t strlen(const char *p) -{ - size_t len = 0; - while(*p++) - len++; - return len; -} - -/* 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 the start of - * user space into buffers. - * - * We don't touch discard. Discard is just turned into user space. - */ -void plt_discard(void) -{ - uint16_t discard_size = ((uint16_t)&udata) - (uint16_t)bufpool_end; - bufptr bp = bufpool_end; -#ifdef CONFIG_KMOD - kmod_init(bufpool_end, &udata); -#endif - - 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; - } -} - -unsigned plt_kmod_set(uint8_t *top) -{ - /* Make sure all disk buffers are on disk */ - sync(); - /* Wind back until bufpool end is below the modules */ - while(bufpool_end > (void *)top) - bufpool_end--; - /* Any buffers lost we already wrote to disk, any new lookups will - not find them, and we know there are no other outstanding references - - sometimes having a dumb I/O layer is a win */ - return 0; -} - -#ifndef SWAPDEV -/* Adding dummy swapper since it is referenced by tricks.s */ -void swapper(ptptr p) -{ - p; -} -#endif - -#ifdef CONFIG_NET_W5100 - -uint8_t w5x00_readcb(uint16_t off) -{ -} - -uint8_t w5x00_readsb(uint8_t s, uint16_t off) -{ -} - -uint16_t w5x00_readcw(uint16_t off) -{ -} - -uint16_t w5x00_readsw(uint8_t s, uint16_t off) -{ -} - -void w5x00_bread(uint16_t bank, uint16_t off, void *pv, uint16_t n) -{ -} - -void w5x00_breadu(uint16_t bank, uint16_t off, void *pv, uint16_t n) -{ -} - -void w5x00_writecb(uint16_t off, uint8_t n) -{ -} - -void w5x00_writesb(uint8_t sock, uint16_t off, uint8_t n) -{ -} - -void w5x00_writecw(uint16_t off, uint16_t n) -{ -} - -void w5x00_writesw(uint8_t sock, uint16_t off, uint16_t n) -{ -} - -void w5x00_bwrite(uint16_t bank, uint16_t off, void *pv, uint16_t n) -{ -} - -void w5x00_bwriteu(uint16_t bank, uint16_t off, void *pv, uint16_t n) -{ -} - -void w5x00_setup(void) -{ -} -#endif diff --git a/Kernel/platform/platform-cpc6128-SME/platform_fdc765.h b/Kernel/platform/platform-cpc6128-SME/platform_fdc765.h deleted file mode 100644 index da41e4c3fc..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/platform_fdc765.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Platform specifics - */ - -#define FDC_MOTOR_TIMEOUT 10 /* Seconds */ - -#define FDC765_MAX_FLOPPY 2 /* Two drives */ - - -/* Hard code for now. Two drives, second drive double sided */ -#define fdc765_ds 2 -#define fdc765_present 3 \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/plt_ch375.h b/Kernel/platform/platform-cpc6128-SME/plt_ch375.h deleted file mode 100644 index 8a4f0a0457..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/plt_ch375.h +++ /dev/null @@ -1,14 +0,0 @@ -extern void nap20(void); -extern void ch375_rblock(uint8_t *ptr);/* __z88dk_fastcall; */ -extern void ch375_wblock(uint8_t *ptr);/* __z88dk_fastcall; */ - -__sfr __banked __at 0xFE80 ch375_dport; -__sfr __banked __at 0xFE81 ch375_sport; - -#define ch375_rdata() ch375_dport -#define ch375_rstatus() ch375_sport - -#define ch375_wdata(x) do {ch375_dport = (x); } while(0) -#define ch375_wcmd(x) do {ch375_sport = (x); } while(0) - -#define CH376_REG_DATA 0xFE80 \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128-SME/plt_ide.h b/Kernel/platform/platform-cpc6128-SME/plt_ide.h deleted file mode 100644 index 4d6c0567cc..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/plt_ide.h +++ /dev/null @@ -1,13 +0,0 @@ -__sfr __banked __at 0xFD08 data; -__sfr __banked __at 0xFD09 error; -__sfr __banked __at 0xFD0A count; -__sfr __banked __at 0xFD0B sec; -__sfr __banked __at 0xFD0C cyll; -__sfr __banked __at 0xFD0D cylh; -__sfr __banked __at 0xFD0E devh; -__sfr __banked __at 0xFD0F cmd; -__sfr __banked __at 0xFD0F status; - -#define IDE_REG_DATA 0xFD08 - -#define IDE_NONSTANDARD_XFER diff --git a/Kernel/platform/platform-cpc6128-SME/rd_cpcsme.s b/Kernel/platform/platform-cpc6128-SME/rd_cpcsme.s deleted file mode 100644 index 68eb1b4799..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/rd_cpcsme.s +++ /dev/null @@ -1,107 +0,0 @@ -; -; RAM disc helpers for CPC Standard Memory Expansions -; - - .area _CODE - - .globl _rd_io - - .globl _ct - .globl _block - .globl _nblock - .globl _rd_wr - .globl _rd_swap_mem_port_h - .globl _rd_swap_bank - .globl _rd_proc_bank - .globl _rd_swap_bank_addr - .globl _rd_proc_bank_addr - .globl _rd_dptr - .globl _int_disabled - .globl _vtborder - .globl map_kernel_restore - - -_rd_io: - di - ld a,(_rd_wr) - or a - jp z, is_wr - ld bc,#0x7f10 - out (c),c - ld c,#0x53 ;Bright cyan - out (c),c - ld a,(_rd_swap_mem_port_h) - ld b,a - ld c,#0xff - ld a,(_rd_swap_bank) - out (c),a - ld hl,(_rd_swap_bank_addr) - ld de, #swapbuffer - ld bc,#512 - ldir - ld bc,#0x7fff - ld a,(_rd_proc_bank) - out (c),a - ld hl,#swapbuffer - ld de,(_rd_proc_bank_addr) - ld bc,#512 - ldir -end_io: - ld bc,#0x7fc1 ; map kernel - out (c),c - call map_kernel_restore ; do it the right way - ld bc,#0x7f10 - out (c),c - ld a,(_vtborder) - out (c),a - ld a,(_int_disabled) - or a - ret nz - ei - ret -is_wr: - ld bc,#0x7f10 - out (c),c - ld c,#0x4c ;Bright red - out (c),c - ld bc,#0x7fff - ld a,(_rd_proc_bank) - out (c),a - ld hl,(_rd_proc_bank_addr) - ld de,#swapbuffer - ld bc,#512 - ldir - ld a,(_rd_swap_mem_port_h) - ld b,a - ld c,#0xff - ld a,(_rd_swap_bank) - out (c),a - ld hl,#swapbuffer - ld de,(_rd_swap_bank_addr) - ld bc,#512 - ldir - jp end_io - -_ct: - .db 0 -_rd_wr: - .db 0 -_rd_swap_bank: - .db 0 -_rd_proc_bank: - .db 0 -_rd_swap_mem_port_h: - .db 0x7f -_rd_swap_bank_addr: - .dw 0 -_rd_proc_bank_addr: - .dw 0 -_rd_dptr: - .dw 0 -_block: - .dw 0 -_nblock: - .dw 0 -swapbuffer: - .ds 512 - diff --git a/Kernel/platform/platform-cpc6128-SME/rules.mk b/Kernel/platform/platform-cpc6128-SME/rules.mk deleted file mode 100644 index edaf0e339a..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/rules.mk +++ /dev/null @@ -1,2 +0,0 @@ -export CROSS_CC_SYS5=--codeseg CODE3 -CROSS_CCOPTS += --peep-file $(FUZIX_ROOT)/Kernel/cpu-z80/rst.peep diff --git a/Kernel/platform/platform-cpc6128-SME/target.mk b/Kernel/platform/platform-cpc6128-SME/target.mk deleted file mode 100644 index 3bffcde0c7..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/target.mk +++ /dev/null @@ -1 +0,0 @@ -export CPU = z80 diff --git a/Kernel/platform/platform-cpc6128-SME/tricks.s b/Kernel/platform/platform-cpc6128-SME/tricks.s deleted file mode 100644 index 2d02424493..0000000000 --- a/Kernel/platform/platform-cpc6128-SME/tricks.s +++ /dev/null @@ -1,5 +0,0 @@ - .include "kernel.def" - .include "../../cpu-z80/kernel-z80.def" - - .include "../../lib/z80single.s" - From 02737d77bfd604226f543f91e398953db6b9d4f0 Mon Sep 17 00:00:00 2001 From: ajcasado Date: Tue, 21 Jan 2025 20:55:37 +0100 Subject: [PATCH 5/9] Merge cpc6128 ports. Use of standard memory expansions as swap space. --- Kernel/platform/platform-cpc6128/Makefile | 6 +- Kernel/platform/platform-cpc6128/README | 10 +- Kernel/platform/platform-cpc6128/config.h | 35 ++++-- Kernel/platform/platform-cpc6128/devices.c | 25 ++-- Kernel/platform/platform-cpc6128/devrd.c | 83 +++++++++++++ Kernel/platform/platform-cpc6128/devrd.h | 20 ++++ Kernel/platform/platform-cpc6128/discard.c | 5 + Kernel/platform/platform-cpc6128/fuzix.lnk | 2 + Kernel/platform/platform-cpc6128/rd_cpcsme.c | 117 +++++++++++++++++++ 9 files changed, 276 insertions(+), 27 deletions(-) create mode 100644 Kernel/platform/platform-cpc6128/devrd.c create mode 100644 Kernel/platform/platform-cpc6128/devrd.h create mode 100644 Kernel/platform/platform-cpc6128/rd_cpcsme.c diff --git a/Kernel/platform/platform-cpc6128/Makefile b/Kernel/platform/platform-cpc6128/Makefile index cd09e33d16..b4a4cf1813 100644 --- a/Kernel/platform/platform-cpc6128/Makefile +++ b/Kernel/platform/platform-cpc6128/Makefile @@ -1,11 +1,11 @@ -CSRCS = devtty.c devices.c main.c +CSRCS = devtty.c devices.c main.c devrd.c rd_cpcsme.c CDSRCS = discard.c DSRCS = ../../dev/tinyide.c ../../dev/tinydisk.c ../../dev/devfdc765.c ../../dev/ch375.c DDSRCS = ../../dev/tinyide_discard.c ../../dev/tinydisk_discard.c DZSRCS = ../../dev/cpc/cpcide.c ../../dev/cpc/cpckeyboard.c ../../dev/cpc/devinput.c ../../dev/cpc/albireo.c DDZSRCS = -ASRCS = crt0.s cpc6128.s cpcvideo.s fdc765.s -ASRCS += tricks.s commonmem.s +ASRCS = crt0.s cpc6128.s cpcvideo.s fdc765.s +ASRCS += tricks.s commonmem.s NSRCS = COBJS = $(CSRCS:.c=.rel) diff --git a/Kernel/platform/platform-cpc6128/README b/Kernel/platform/platform-cpc6128/README index fd31fdd0f5..8c6c54a8b6 100644 --- a/Kernel/platform/platform-cpc6128/README +++ b/Kernel/platform/platform-cpc6128/README @@ -1,7 +1,7 @@ # Amstrad CPC6128 -## Mmeory Model +## Memory Model The CPC6128 supported memory maps: @@ -23,7 +23,13 @@ In the CPC6128: | Kernel | 0 / 1 / 2 | Common 7 (map C1) | Video | 0 / 3 / 2 | Common 7 (map C3) +The use of standard memory expansions as swap space has been implemented. With this memory map, the ability to have multiple tasks simultaneously in RAM has not been implemented. This is because the expanded memory can only be mapped in 16k blocks starting at address 0x4000, which removes the common area from the map (this mapping is used to implement the RAM disk for swap while executing code in the non-common code area). Alternatively, it can be mapped in 64k blocks, replacing the entire memory space without any common area to rely on.. See this references: +https://www.cpcwiki.eu/index.php/Gate_Array#Register_MMR_.28RAM_memory_mapping.29 +https://www.cpcwiki.eu/index.php/Standard_Memory_Expansions +This latter 64k block mapping approach, which involves switching between 64k blocks, is being considered for another port inspired by the memory usage in the Cromemco port. + +Two standard memory expansion sizes are supported: 512k and 1024k. To build the port with either of these options, simply define the macros EXTENDED_RAM_512 or EXTENDED_RAM_1024 depending on the desired size. If neither is defined, the build will default to supporting a swap partition on any of the supported disk types. ## STATUS @@ -54,7 +60,7 @@ Support more hardware: M4 Board (storage, network and RTC), Ulifac/Usifac, netwo Fix lots of bugs. -Switch to a thunked memory model based on C2 Map to use the standard and extended RAM expansions up to 4MiB, the Cromemco port could be a model to this solution. As ther is no real common as we are switching the whole 64k space, the common data area has to be updated in all the used banks, but this can give aprox. 60K for kernel and user and hold a lot of processes in memory with a big RAM expansion. If this proves to be too hard, a RAM disk for swapping can be a way to use the RAM expansions. +Switch to a thunked memory model based on C2 Map to use the standard and extended RAM expansions up to 4MiB, the Cromemco port could be a model to this solution. As there is no real common as we are switching the whole 64k space, the common data area has to be updated in all the used banks, but this can give aprox. 60K for kernel and user and hold a lot of processes in memory with a big RAM expansion. Look for speed optimization opportunities. diff --git a/Kernel/platform/platform-cpc6128/config.h b/Kernel/platform/platform-cpc6128/config.h index 85a8d69511..8194de1997 100644 --- a/Kernel/platform/platform-cpc6128/config.h +++ b/Kernel/platform/platform-cpc6128/config.h @@ -27,8 +27,8 @@ #define UDATA_BLKS 1 #define UDATA_SIZE 0x200 #define CONFIG_DYNAMIC_BUFPOOL -#define CONFIG_DYNAMIC_SWAP -#define MAXTICKS 20 /* Has to be high because we are swap only */ + +#define MAXTICKS 60 /* Has to be high because we are swap only */ #undef CONFIG_KMOD @@ -70,8 +70,24 @@ #define SWAPBASE 0x0000 #define SWAPTOP 0xC000UL #define SWAP_SIZE 0x61 /* 48K + udata */ -#define MAX_SWAPS 16 -#define SWAPDEV (swap_dev) /* Device for swapping (dynamic). */ +#define EXTENDED_RAM_1024 +#ifdef EXTENDED_RAM_1024 + #define MAX_SWAPS 19 /*See platform devrd.c*/ + #define PTABSIZE 19 + #define TOTAL_SWAP_BLOCKS (1088-128) * 2 +#endif +#ifdef EXTENDED_RAM_512 + #define MAX_SWAPS 9 /*See platform devrd.c*/ + #define PTABSIZE 9 + #define TOTAL_SWAP_BLOCKS (576-128) * 2 +#endif +#if defined EXTENDED_RAM_512 || defined EXTENDED_RAM_1024 + #define SWAPDEV 0x800 /* Device for swapping - RAM disk on standard memory expansion. */ +#else + #define CONFIG_DYNAMIC_SWAP + #define SWAPDEV (swap_dev) /* Device for swapping (dynamic). */ + #define MAX_SWAPS 16 +#endif /* We swap by hitting the user map */ #define swap_map(x) ((uint8_t *)(x)) @@ -83,15 +99,12 @@ #define CONFIG_TINYIDE_SDCCPIO #define CONFIG_TINYIDE_8BIT #define IDE_IS_8BIT(x) 1 -/* SD support */ -/* #define TD_SD_NUM 2 */ -/* #define CONFIG_TD_SD */ -/* Emulator for this platform needs bug workarounds */ -/* #undef CONFIG_TD_SD_EMUBUG */ -/*#undef SD_SPI_CALLTYPE __z88dk_fastcall */ #define BOOTDEVICENAMES "hd#,fd" #define CONFIG_SMALL -/* #define DEBUG */ \ No newline at end of file + + + + diff --git a/Kernel/platform/platform-cpc6128/devices.c b/Kernel/platform/platform-cpc6128/devices.c index f29a9c0ddb..2679324c4a 100644 --- a/Kernel/platform/platform-cpc6128/devices.c +++ b/Kernel/platform/platform-cpc6128/devices.c @@ -6,10 +6,11 @@ #include #include #include -#include +/*#include */ #include #include -#include +/*#include */ +#include struct devsw dev_tab[] = /* The device driver switch table */ { @@ -24,23 +25,25 @@ struct devsw dev_tab[] = /* The device driver switch table */ /* 4: /dev/mem etc System devices (one offs) */ { no_open, no_close, sys_read, sys_write, sys_ioctl }, /* 5: Pack to 7 with nxio if adding private devices and start at 8 */ - #ifdef CONFIG_NET - { 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 }, - { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl }, -#endif + #if defined EXTENDED_RAM_512 || defined EXTENDED_RAM_1024 + /* 5: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 6: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 7: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 8: Standard memory expansions RAM swap */ + { rd_open, no_close, rd_read, rd_write, no_ioctl }, + #endif }; - - 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; + return false; else return true; } diff --git a/Kernel/platform/platform-cpc6128/devrd.c b/Kernel/platform/platform-cpc6128/devrd.c new file mode 100644 index 0000000000..e8486f9d86 --- /dev/null +++ b/Kernel/platform/platform-cpc6128/devrd.c @@ -0,0 +1,83 @@ +/* + * CPC standard RAM bank memory expansions ramdisc driver, based on platform zxdiv48. + */ + +#include +#include +#include + +#if defined EXTENDED_RAM_512 || defined EXTENDED_RAM_1024 +#include + + +static int rd_transfer(uint8_t is_read, uint8_t rawflag) +{ + nblock = udata.u_nblock; + block = udata.u_block; + rd_dptr = udata.u_dptr; + rd_wr = is_read; + uint16_t swap_bank_long; + uint8_t ct = 0; + + #ifdef DEBUG + kprintf("u_dptr %p Block %u u_nblock %u rd_wr %u\n",udata.u_dptr, udata.u_block, udata.u_nblock, rd_wr); + #endif + + /* It's a disk but only for swapping (and rd_io isn't general purpose) */ + if (((block + nblock) > (TOTAL_SWAP_BLOCKS - 1)) || (rawflag == 1)) { + udata.u_error = EIO; + kprintf("dev_rd_EIO"); + return -1; + } + + /* udata could change under us so keep variables privately */ + while (ct < nblock) { + swap_bank_long = (block >> 5); + swap_bank_long = swap_bank_long + 196 + (((swap_bank_long + 8) / 4) * 4); /*Convert bank number to Register MMR value + *See https://www.cpcwiki.eu/index.php/Gate_Array#Register_MMR_.28RAM_memory_mapping.29*/ + if (swap_bank_long > 255){ + rd_swap_bank = swap_bank_long - 64; + rd_swap_mem_port_h = 0x7e; + } + else{ + rd_swap_bank = swap_bank_long; + rd_swap_mem_port_h = 0x7f; + } + rd_proc_bank = ((uint16_t)rd_dptr / 0x4000) + 0xc4; + rd_swap_bank_addr = ((block & 31) << BLKSHIFT) + 0x4000; + rd_proc_bank_addr = ((uint16_t)rd_dptr & 0x3fff) + 0x4000; + + #ifdef DEBUG + if (nblock == 1) + kprintf("swap_bank %p swap_addr %p proc_bank %p proc_addr %p count %u\n", rd_swap_bank, rd_swap_bank_addr, rd_proc_bank, rd_proc_bank_addr, ct); + #endif + rd_io(); + block++; + rd_dptr += BLKSIZE; + ct++; + } + return ct << BLKSHIFT; /*Total bytes transferred*/ +} + +int rd_open(uint8_t minor, uint16_t flag) +{ + flag; + if(minor != 0) { + udata.u_error = ENODEV; + return -1; + } + return 0; +} + +int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return rd_transfer(true, rawflag); +} + +int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return rd_transfer(false, rawflag); +} +#endif diff --git a/Kernel/platform/platform-cpc6128/devrd.h b/Kernel/platform/platform-cpc6128/devrd.h new file mode 100644 index 0000000000..e7c4451555 --- /dev/null +++ b/Kernel/platform/platform-cpc6128/devrd.h @@ -0,0 +1,20 @@ +/* + * + * CPC standard RAM bank memory expansions ramdisc driver + */ + +int rd_open(uint8_t minor, uint16_t flags); +int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag); + +extern uint8_t rd_wr; +extern uint8_t rd_swap_bank; +extern uint8_t rd_swap_mem_port_h; +extern uint8_t rd_proc_bank; +extern uint8_t *rd_dptr; +extern uint16_t rd_swap_bank_addr; +extern uint16_t rd_proc_bank_addr; +extern uint16_t nblock; +extern blkno_t block; + +void rd_io(void); diff --git a/Kernel/platform/platform-cpc6128/discard.c b/Kernel/platform/platform-cpc6128/discard.c index 207e33eff2..cd9a4c8605 100644 --- a/Kernel/platform/platform-cpc6128/discard.c +++ b/Kernel/platform/platform-cpc6128/discard.c @@ -13,6 +13,11 @@ uint8_t plt_param(char *p) /* Nothing to do for the map of init */ void map_init(void) { +#if defined EXTENDED_RAM_512 || defined EXTENDED_RAM_1024 + uint_fast8_t i; + for (i = 0; i < MAX_SWAPS; i++) + swapmap_init(i); +#endif } void plt_copyright(void) diff --git a/Kernel/platform/platform-cpc6128/fuzix.lnk b/Kernel/platform/platform-cpc6128/fuzix.lnk index 2ac77d8022..f9858617a7 100644 --- a/Kernel/platform/platform-cpc6128/fuzix.lnk +++ b/Kernel/platform/platform-cpc6128/fuzix.lnk @@ -6,6 +6,7 @@ platform/platform-cpc6128/crt0.rel platform/platform-cpc6128/commonmem.rel platform/platform-cpc6128/cpc6128.rel +platform/platform-cpc6128/rd_cpcsme.rel platform/platform-cpc6128/cpcvideo.rel platform/platform-cpc6128/main.rel platform/platform-cpc6128/discard.rel @@ -54,4 +55,5 @@ platform/platform-cpc6128/devfdc765.rel platform/platform-cpc6128/fdc765.rel platform/platform-cpc6128/ch375.rel platform/platform-cpc6128/albireo.rel +platform/platform-cpc6128/devrd.rel -e diff --git a/Kernel/platform/platform-cpc6128/rd_cpcsme.c b/Kernel/platform/platform-cpc6128/rd_cpcsme.c new file mode 100644 index 0000000000..b4f6481086 --- /dev/null +++ b/Kernel/platform/platform-cpc6128/rd_cpcsme.c @@ -0,0 +1,117 @@ +/* +* RAM disc helpers for CPC Standard Memory Expansions +*/ + +#include + +#undef di +#undef ei + +#if defined EXTENDED_RAM_512 || defined EXTENDED_RAM_1024 +static void CODESEG(void) __naked { __asm .area _CODE __endasm; } /* .area _CODE */ +void rd_io(void) __naked +{ + __asm + + .globl _rd_io + + .globl _block + .globl _nblock + .globl _rd_wr + .globl _rd_swap_mem_port_h + .globl _rd_swap_bank + .globl _rd_proc_bank + .globl _rd_swap_bank_addr + .globl _rd_proc_bank_addr + .globl _rd_dptr + .globl _int_disabled + .globl _vtborder + .globl map_kernel_restore + + + + di + ld a,(_rd_wr) + or a + jp z, is_wr + ld bc,#0x7f10 + out (c),c + ld c,#0x53 ;Bright cyan + out (c),c + ld a,(_rd_swap_mem_port_h) + ld b,a + ld c,#0xff + ld a,(_rd_swap_bank) + out (c),a + ld hl,(_rd_swap_bank_addr) + ld de, #swapbuffer + ld bc,#512 + ldir + ld bc,#0x7fff + ld a,(_rd_proc_bank) + out (c),a + ld hl,#swapbuffer + ld de,(_rd_proc_bank_addr) + ld bc,#512 + ldir +end_io: + ld bc,#0x7fc1 ; map kernel + out (c),c + call map_kernel_restore ; do it the right way + ld bc,#0x7f10 + out (c),c + ld a,(_vtborder) + out (c),a + ld a,(_int_disabled) + or a + ret nz + ei + ret +is_wr: + ld bc,#0x7f10 + out (c),c + ld c,#0x4c ;Bright red + out (c),c + ld bc,#0x7fff + ld a,(_rd_proc_bank) + out (c),a + ld hl,(_rd_proc_bank_addr) + ld de,#swapbuffer + ld bc,#512 + ldir + ld a,(_rd_swap_mem_port_h) + ld b,a + ld c,#0xff + ld a,(_rd_swap_bank) + out (c),a + ld hl,#swapbuffer + ld de,(_rd_swap_bank_addr) + ld bc,#512 + ldir + jp end_io + +_rd_wr: + .db 0 +_rd_swap_bank: + .db 0 +_rd_proc_bank: + .db 0 +_rd_swap_mem_port_h: + .db 0x7f +_rd_swap_bank_addr: + .dw 0 +_rd_proc_bank_addr: + .dw 0 +_rd_dptr: + .dw 0 +_block: + .dw 0 +_nblock: + .dw 0 +swapbuffer: + .ds 512 + +__endasm; +} +#endif + From 78be4edc6b327954f86930e857d274761c5a8607 Mon Sep 17 00:00:00 2001 From: ajcasado Date: Tue, 21 Jan 2025 21:49:10 +0100 Subject: [PATCH 6/9] Update Readme file --- Kernel/platform/platform-cpc6128/README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Kernel/platform/platform-cpc6128/README b/Kernel/platform/platform-cpc6128/README index 8c6c54a8b6..6692dfd724 100644 --- a/Kernel/platform/platform-cpc6128/README +++ b/Kernel/platform/platform-cpc6128/README @@ -23,13 +23,13 @@ In the CPC6128: | Kernel | 0 / 1 / 2 | Common 7 (map C1) | Video | 0 / 3 / 2 | Common 7 (map C3) -The use of standard memory expansions as swap space has been implemented. With this memory map, the ability to have multiple tasks simultaneously in RAM has not been implemented. This is because the expanded memory can only be mapped in 16k blocks starting at address 0x4000, which removes the common area from the map (this mapping is used to implement the RAM disk for swap while executing code in the non-common code area). Alternatively, it can be mapped in 64k blocks, replacing the entire memory space without any common area to rely on.. See this references: +The use of standard memory expansions as swap space has been implemented. With this memory map, the ability to have multiple tasks simultaneously in RAM has not been implemented. This is because the expanded memory can only be mapped in 16k blocks starting at address 0x4000, which removes the common area from the map (this mapping is used to implement the RAM disk for swap while executing code in the non-common code area). Alternatively, it can be mapped in 64k blocks, replacing the entire memory space without any common area to rely on. See this references: https://www.cpcwiki.eu/index.php/Gate_Array#Register_MMR_.28RAM_memory_mapping.29 https://www.cpcwiki.eu/index.php/Standard_Memory_Expansions This latter 64k block mapping approach, which involves switching between 64k blocks, is being considered for another port inspired by the memory usage in the Cromemco port. -Two standard memory expansion sizes are supported: 512k and 1024k. To build the port with either of these options, simply define the macros EXTENDED_RAM_512 or EXTENDED_RAM_1024 depending on the desired size. If neither is defined, the build will default to supporting a swap partition on any of the supported disk types. +Two standard memory expansion sizes are supported: 512k and 1024k. To build the port with either of these options, simply define the macros EXTENDED_RAM_512 or EXTENDED_RAM_1024 depending on the desired size in the file config.h. If neither is defined, the build will default to supporting a swap partition on any of the supported disk types. ## STATUS From 79a61f0e60af4c13d22a6f9318d71c3fd45ddcb0 Mon Sep 17 00:00:00 2001 From: ajcasado Date: Wed, 22 Jan 2025 01:20:12 +0100 Subject: [PATCH 7/9] Correction of maximum number of proccesses for 512k ram expansion as swap --- Kernel/platform/platform-cpc6128/config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Kernel/platform/platform-cpc6128/config.h b/Kernel/platform/platform-cpc6128/config.h index 8194de1997..c98d8d1416 100644 --- a/Kernel/platform/platform-cpc6128/config.h +++ b/Kernel/platform/platform-cpc6128/config.h @@ -77,8 +77,8 @@ #define TOTAL_SWAP_BLOCKS (1088-128) * 2 #endif #ifdef EXTENDED_RAM_512 - #define MAX_SWAPS 9 /*See platform devrd.c*/ - #define PTABSIZE 9 + #define MAX_SWAPS 8 /*See platform devrd.c*/ + #define PTABSIZE 8 #define TOTAL_SWAP_BLOCKS (576-128) * 2 #endif #if defined EXTENDED_RAM_512 || defined EXTENDED_RAM_1024 From 6a9f0317b7650e6d19ef04720f246bc991498583 Mon Sep 17 00:00:00 2001 From: Warren Toomey Date: Wed, 22 Jan 2025 14:51:38 +1000 Subject: [PATCH 8/9] Applications/wish: Added the wish shell. --- Applications/Makefile | 6 +- Applications/wish/Ifdefs | 95 +++ Applications/wish/Installation | 54 ++ Applications/wish/LICENSE | 27 + Applications/wish/Makefile.68000 | 1 + Applications/wish/Makefile.common | 23 + Applications/wish/README | 29 + Applications/wish/Wish.1 | 1216 +++++++++++++++++++++++++++++ Applications/wish/Wishrc | 15 + Applications/wish/alias.c | 73 ++ Applications/wish/bind.c | 317 ++++++++ Applications/wish/builtin.c | 275 +++++++ Applications/wish/clebuf.c | 79 ++ Applications/wish/clex.c | 476 +++++++++++ Applications/wish/comlined.c | 775 ++++++++++++++++++ Applications/wish/exec.c | 374 +++++++++ Applications/wish/file.c | 129 +++ Applications/wish/fuzix-wish.pkg | 7 + Applications/wish/global.c | 3 + Applications/wish/header.h | 206 +++++ Applications/wish/hist.c | 151 ++++ Applications/wish/job.c | 530 +++++++++++++ Applications/wish/m_fuzix.h | 26 + Applications/wish/machine.h | 26 + Applications/wish/main.c | 333 ++++++++ Applications/wish/malloc.c | 79 ++ Applications/wish/meta.c | 1073 +++++++++++++++++++++++++ Applications/wish/parse.c | 186 +++++ Applications/wish/posixjob.c | 148 ++++ Applications/wish/prints.c | 353 +++++++++ Applications/wish/proto.h | 227 ++++++ Applications/wish/signal.c | 66 ++ Applications/wish/term.c | 314 ++++++++ Applications/wish/ucbjob.c | 160 ++++ Applications/wish/v7job.c | 162 ++++ Applications/wish/val.c | 134 ++++ Applications/wish/var.c | 201 +++++ Applications/wish/wish | Bin 0 -> 46868 bytes 38 files changed, 8348 insertions(+), 1 deletion(-) create mode 100644 Applications/wish/Ifdefs create mode 100644 Applications/wish/Installation create mode 100644 Applications/wish/LICENSE create mode 100644 Applications/wish/Makefile.68000 create mode 100644 Applications/wish/Makefile.common create mode 100644 Applications/wish/README create mode 100644 Applications/wish/Wish.1 create mode 100644 Applications/wish/Wishrc create mode 100644 Applications/wish/alias.c create mode 100644 Applications/wish/bind.c create mode 100644 Applications/wish/builtin.c create mode 100644 Applications/wish/clebuf.c create mode 100644 Applications/wish/clex.c create mode 100644 Applications/wish/comlined.c create mode 100644 Applications/wish/exec.c create mode 100644 Applications/wish/file.c create mode 100644 Applications/wish/fuzix-wish.pkg create mode 100644 Applications/wish/global.c create mode 100644 Applications/wish/header.h create mode 100644 Applications/wish/hist.c create mode 100644 Applications/wish/job.c create mode 100644 Applications/wish/m_fuzix.h create mode 100644 Applications/wish/machine.h create mode 100644 Applications/wish/main.c create mode 100644 Applications/wish/malloc.c create mode 100644 Applications/wish/meta.c create mode 100644 Applications/wish/parse.c create mode 100644 Applications/wish/posixjob.c create mode 100644 Applications/wish/prints.c create mode 100644 Applications/wish/proto.h create mode 100644 Applications/wish/signal.c create mode 100644 Applications/wish/term.c create mode 100644 Applications/wish/ucbjob.c create mode 100644 Applications/wish/v7job.c create mode 100644 Applications/wish/val.c create mode 100644 Applications/wish/var.c create mode 100644 Applications/wish/wish diff --git a/Applications/Makefile b/Applications/Makefile index 1ec3f9a453..d7f6e1a7c0 100644 --- a/Applications/Makefile +++ b/Applications/Makefile @@ -1,7 +1,7 @@ # APPS = util cmd sh games cave cpm v7games games cursesgames \ as09 ld09 netd MWC flashrom ue cpmfs plato \ - emulators cpnet dw assembler CC cpp ar + emulators cpnet dw assembler CC cpp ar wish .PHONY: $(APPS) @@ -79,6 +79,9 @@ cpp: ar: +(cd ar; $(MAKE) -f Makefile.$(USERCPU)) +wish: + +(cd wish; $(MAKE) -f Makefile.$(USERCPU)) + clean: (cd util; $(MAKE) -f Makefile.$(USERCPU) clean) (cd V7/cmd; $(MAKE) -f Makefile.$(USERCPU) clean) @@ -104,3 +107,4 @@ clean: (cd cpp; $(MAKE) -f Makefile.$(USERCPU) clean) (cd dw; $(MAKE) -f Makefile.$(USERCPU) clean) (cd ar; $(MAKE) -f Makefile.$(USERCPU) clean) + (cd wish; $(MAKE) -f Makefile.$(USERCPU) wish) diff --git a/Applications/wish/Ifdefs b/Applications/wish/Ifdefs new file mode 100644 index 0000000000..b4b38b8683 --- /dev/null +++ b/Applications/wish/Ifdefs @@ -0,0 +1,95 @@ + #defines used in Wish 2.0 + ------------------------- + +$Revision: 41.1 $ $Date: 1995/12/29 02:57:38 $ + +Here are the defines that are used in Wish 2.0. Code that uses specific +library calls and system calls are appropriately ifdef'd. + +DEBUG /* debugging code used in development */ +MALLOCDEBUG /* malloc debugging code used in development */ +MINIX1_5 /* Minix 1.5 specific stuff */ +NOTYET /* code that's not being used, & may never be */ +POSIXJOB /* POSIX style job control */ +PROTO /* Ansi-C function prototypes */ +STDARG /* use */ +UCBJOB /* 4.3BSD style job control */ +USES_DIRECT /* struct direct instead of struct dirent */ +USES_GETWD /* getwd() use instead of getcwd() */ +USES_MORESIG /* sgtty moresig code */ +USES_SGTTY /* bcopy() and sgttyb/TCIOGETP use */ +USES_TCGETA /* termio/TCGETA use */ +USES_TERMIOS /* termios/tcgetattr use */ +V7JOB /* 7th Edition ptrace(1) job control */ +VARARGS /* use */ + +A small amount of code is specific to some systems. The following systems +are defined in the m_*.h files. Only the MINIX1_5 define is used in the +source code. If you are porting Wish to another system, define a name for +the system, but try not to use this when #ifdef'ing code. Use the #define's +above, or create a new define, e.g #define USES_INDEX if using index(). + + +AUX2_01 /* A/UX 2.01 */ +COHERENT /* Coherent */ +FREEBSD_1 /* FreeBSD 1.x */ +FREEBSD_2 /* FreeBSD 2.x */ +GENBSD /* Generic BSD 4.x machine */ +GENSYSV /* Generic SysV machine */ +J386BSD0_1 /* 386BSD-0.1 */ +MINIX1_5 /* Minix 1.5 */ +MINIX1_7 /* Minix 1.7 */ +OSX5_1_ATT /* Pyramid under OSx 5.1a, SysV Universe */ +OSX5_1_BSD /* Pyramid OSx 5.1a, BSD Universe */ +SOLARIS_2 /* Solaris 2.x */ +SUNOS3 /* Sun OS 3.x */ +SUNOS4 /* Sun OS 4.x */ +ULTRIX4_2 /* Ultrix 4.2 */ + +Job Control +----------- + + There are four styles of job control available: 7th Edition Unix +job control (using ptrace), Berkeley job control, POSIX job control and +no job control whatsoever. These are enabled by the following defines, only +one of which should be defined. + +#define POSIXJOB /* POSIX job control */ +#define V7JOB /* 7th Edition Unix job control */ +#define UCBJOB /* Berkeley job control */ + +Argument Passing +---------------- + + The prints() [and friends] functions take a variable number of +arguments. Where possible, argument passing macros should be used. There +are three main types: no argument passing macros, varargs.h style argument +passing and stdarg.h stype argument passing macros. These are enabled by the +following defines, only one of which should be defined. + +#define VARARGS /* varargs.h style argument passing */ +#define STDARG /* stdarg.h stype argument passing */ + + +Miscellaneous +------------- + + There are a few other miscellaneous defines which control things +like types of functions used for directory searches, current working directory +information. + +#define USES_GETWD /* Uses getwd(), not getcwd() */ + +#define PLONG int /* The type of arguments to ptrace(). Minix 1.5 */ + /* uses longs! This is only used when V7JOB is on */ + +#define SIGTYPE void /* Signal handlers return this type. Usually void or */ + /* int, but might also be __sighandler_t */ + +#define USES_DIRECT /* Uses struct direct, not struct dirent */ + +#define PROTO /* The compiler supports Ansi-C prototyped function */ + /* declarations */ + +#define DEBUG /* Debugging code in Wish is #ifdef'd with this. You */ + /* should not need (or want) to turn this on. */ diff --git a/Applications/wish/Installation b/Applications/wish/Installation new file mode 100644 index 0000000000..e8f58e1649 --- /dev/null +++ b/Applications/wish/Installation @@ -0,0 +1,54 @@ + Installation of Wish 2.0 + ------------------------ + +$Revision: 41.4 $ $Date: 2003/04/21 13:13:32 $ + +1) Link the most appropriate file from the list of the m_*.h files to machine.h. + The current machine types are: + + m_386bsd.h 386BSD-0.1 + m_attpyr.h OSx 5.1a, SysV Universe + m_aux.h A/UX 2.01 + m_bsdpyr.h OSx 5.1a, BSD Universe + m_coherent.h Coherent + m_freebsd1.h FreeBSD 1.x + m_freebsd2.h FreeBSD 2.x + m_freebsd4.h FreeBSD 4.x + m_genbsd.h Generic 4.x BSD machine + m_gensysv.h Generic SysV machine + m_minix1.5.h Minix 1.5 + m_minix1.7.h Minix 1.7 + m_solaris.h Solaris + m_sunos3.h Sun OS 3.x + m_sunos4.h Sun OS 4.x + m_ultrix.h Ultrix 4.2 + + Not all of these have been tested recently, only those described in the + `Systems' file. If you get any to work, especially with the changes + required, let me know. + + If there is no m_*.h file for your system, pick one which is close, and + alter it to work: + + POSIX: m_sunos4.h or m_386bsd.h + Old SYSV: m_gensysv.h + 4.3BSD: m_genbsd.h + + Put a unique system #define in the file, and try to #ifdef code with this + define only when absolutely necessary. The file `Ifdefs' has information + about what defines are available in Wish. Please send me a copy of the new + machine file when it works. + +2) Copy the file Std.Makefile to Makefile. Make sure Makefile is writeable. + Edit the Makefile to define how to compile the shell. You should define: + + CFLAGS The compiler flags used when compiling each .c file + LDFLAGS The link flags used when linking the program + CC The name of your compiler + DEFINES Special defines needed during the compilation stage + CLIB Any libraries needed during the link stage + FINAL_TOUCHES Any final touches needed after the linking + + Suggestions for these defines are given in the Makefile. + +3) Run `make' to make the shell. diff --git a/Applications/wish/LICENSE b/Applications/wish/LICENSE new file mode 100644 index 0000000000..1726f18925 --- /dev/null +++ b/Applications/wish/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2016, Warren Toomey +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Wish nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Applications/wish/Makefile.68000 b/Applications/wish/Makefile.68000 new file mode 100644 index 0000000000..a1afdf7fe9 --- /dev/null +++ b/Applications/wish/Makefile.68000 @@ -0,0 +1 @@ +include Makefile.common diff --git a/Applications/wish/Makefile.common b/Applications/wish/Makefile.common new file mode 100644 index 0000000000..367928f220 --- /dev/null +++ b/Applications/wish/Makefile.common @@ -0,0 +1,23 @@ +include $(FUZIX_ROOT)/Target/rules.$(USERCPU) + +SRC = alias.c bind.c builtin.c clebuf.c clex.c comlined.c exec.c file.c \ + hist.c job.c main.c malloc.c meta.c parse.c prints.c signal.c \ + term.c val.c var.c + +.SUFFIXES: .c .o + +OBJ = $(SRC:.c=.o) + +all: wish + +.c.o: + $(CC) $(CFLAGS) $(CCOPTS) -c $< + +wish: $(OBJ) + $(LINKER) $(CRT0) $^ -o $@ $(LINKER_OPT) -ltermcap$(USERCPU) $(LINKER_TAIL) + +clean: + rm -f *.o wish core *~ *.asm *.lst *.sym *.map *.noi *.lk *.ihx *.tmp *.bin size.report + +rmbak: + rm -f *~ core diff --git a/Applications/wish/README b/Applications/wish/README new file mode 100644 index 0000000000..35bdb06516 --- /dev/null +++ b/Applications/wish/README @@ -0,0 +1,29 @@ + Wish 2.0 - Warren's Interactive Shell + ------------------------------------- + +Welcome to Wish, a sort-of-tcsh-like shell based largely on the Clam shell +written by Callum Gibson. The aims of wish are: + + + to be as small as possible, so as to compile on small + systems like Minix + + to have some of the interactive features of tcsh, such + as command-line editing, tab and ^D expansion, history, + aliases etc. + + to be portable across as many systems as possible, + + to be fast and have minimal impact on the system. + +I worked on this shell from 1991 to 1996 and then gave up to finish off +my Ph.D. This release of Wish is an alpha-release, and I doubt I will ever +get back to finishing it off. It is known to run on FreeBSD 4.7, but I have +not tried it on any other system since 1996. + +The manual is in nroff form in `Wish.1'. Instructions on how to compile wish +are in the `Installation'. If you are porting wish to another system, read +the file `Ifdefs', `Systems' and the `m_*.h' files. + +Anybody who would like to take over development of the shell should email me, +as I can supply some good documentation about the development up to now, and +also a good description of the shell's internals. + + Warren Toomey wkt@tuhs.org 1996, then a bit in 2003. + diff --git a/Applications/wish/Wish.1 b/Applications/wish/Wish.1 new file mode 100644 index 0000000000..3d36a2f725 --- /dev/null +++ b/Applications/wish/Wish.1 @@ -0,0 +1,1216 @@ +."$Revision: 41.2 $ $Date: 1995/12/29 03:23:32 $ +.TH WISH 1L +.nr LL 6i +.SH NAME +wish \- command interpreter and programming shell +.SH SYNOPSIS +.B wish +[ options ] [ script-file ] +.SH DESCRIPTION +.PP +.I Wish +is a shell (command interpreter) which reads commands from either a +file or the terminal and executes them. It executes system commands as well +as builtin commands (see below for description). Main features include: a +history mechanism, enabling previous commands to be easily redone; a +comprehensive command line editor for ease of use from terminal; word completion +and option listing on the command line; full input and output redirection and +aliases. These features are described in detail below. +.PP +This manual describes internal version 41 of Wish 2.0. +.PP +Wish is derived from the Clam shell, written by Callum Gibson, and is in some +sense a partial rewrite of Clam with some new features added and some old +features removed. Many thanks to Callum for allowing me to use his source, +and for his suggestions and criticisms during Wish's development. +.".SH OPTIONS +."The shell may be invoked with a number of options: +.".TP 4 +.".B \-c +." \- the following arguments are passed to Wish for execution. +.".TP 4 +.".B \-e +." \- exit shell if any command returns a non-zero exit status. +.".TP4 +.".B \-f +." \- fast start up. Do not read the .wishrc file. +.".TP 4 +.".B \-p +." \- parse the commands but do not execute so that checking can be performed +."on shell scripts. +.".TP 4 +.".B \-v +." \- verbose mode. Commands echo back in same form as saved to history +."prior to execution. No metacharacter expansion is done (*,?,{},[],``) +."before the line is echoed. +.".TP 4 +.".B \-x +." \- similar to verbose mode except that commands are echoed back in expanded +."form. +.".TP 4 +.".B \-V +." \- Enter verbose mode even before .wishrc is read. +.".TP 4 +.".B \-X +." \- \-X is to \-x as \-V is to \-v +.PP +.SH Basic Command Line Parsing +.PP +.PP +The command line, whether read from stdin or file, is broken up into +.I words. +Words are sequences of characters which are separated by +word-delimiters (the most obvious example is a +.I space). +The following characters have effects on the words of the +command line and act as word delimiters: +.PP +< +.I filename. +Standard input redirection. Instead of reading input from the +terminal, input is read from the named file (the following word). e.g. +.PP +% prog < inputfile +.PP +> +.I filename. +Standard output redirection. Instead of outputting to the +terminal screen, output is redirected to the named file. e.g. +.PP +% prog > outputfile +.PP +.PP +n> +.I filename. +Other output redirection. Instead of outputting to the +terminal screen, output from the given file descriptor +is redirected to the named file. e.g. +.PP +% prog 5> outputfile +.PP +2> Standard error redirection. The default is the terminal screen, but instead +standard error output is directed to the named file. e.g. +.PP +% program 2> errfile +.PP +[n]>> +.I filename. +As above but the named file is appended to, not erased first. +.PP +| Pipe output of one program to input of another. A series of programs joined +by pipes is called a +.I pipeline. +A pipe joins the standard output +of the first program to the standard input of the second program. e.g. +.PP +% prog1 | prog2 +.PP +& background. When this symbol appears, it runs the previous command in +the background. A number indicating the process id and Wish job number +(job control only) is returned and the prompt is given immediately for the next +command. +.PP +; semicolon is used to separate pipelines. A semicolon can be typed instead +of a newline so that a new command or pipeline can begin. The effect is +exactly the same as a newline and thus it has the highest priority of these +metacharacters. +.".PP +."&& The double ampersand separates two commands (or command pipelines) and +."specifies that the latter should only be executed if the former returns a zero +."status (i.e. terminates successfully). +.".PP +."|| The double pipe is similar to the double ampersand except that it executes +."the latter if the former returns with +.".I non-zero +."status. +.PP +There are valid combinations of these, too, although it is illegal to +redirect output to two places, for example. +Some other characters also have special meaning and these may cause the words +to be interpreted in different ways. Ordinarily the words become the arguments +of an executing program. +." We don't have backslash as yet, sigh. +."To use any of these characters without invoking their +."special meaning, it is necessary to use the backslash character `\\' which +." causes the shell to ignore them and treat them as part of a word. +Most of +these "metacharacters" will also be ignored inside double quotes `"' and +all (except history substitution commands using !) are ignored inside single +quotes `''. (See the section titled "Quoting") +.PP +.SH Job Control +.PP +.PP +If job control is included, Wish +allows the user to have control over their processes or +.I jobs. +Whenever a process is run it is given a job number. This applies to +all background jobs and the current foreground one if it exists. This number +can then be used to identify a process for use with bg or fg. +The list of all currently active jobs can be displayed with the +.I jobs +command. +The +.I bg +and +.I fg +commands allow jobs to be placed into the background (where +the prompt is returned but the command still runs) or the foreground (where +the user is in an interactive state with the process). The stop signal can be +generated by typing `^Z' while a foreground job is running. This has the effect +of stopping that process immediately, which may be restarted with either the bg +or fg command. +." I don't think we have ^Y yet. +." A foreground process may also be stopped by typing `^Y' which +." delays stopping until the input buffer is emptied. +Note that bg and fg always +refer to the current (most recent) job +by default. For further explanation, see the "Builtin Commands" section below. +.PP +Background jobs are allowed to write to the terminal by default but not +read from it. (The shell places a job in the background by disassociating it +with the control terminal's process group. See +.I setpgrp(2)). +.PP + The +.I stty(1) +"tostop" mode can be used if output from background jobs is to be disallowed. +This mode will cause background jobs to stop if a write to the terminal is +attempted. Reads from the background are always disallowed. +The user is notified of a processes status after return is pressed +and before the next prompt appears. This gives information about completion +or stoppage of background jobs. +.PP +Those people using +.B Minix +should use +.I stty(1) +to redefine their `quit' key from ^\\ to ^Z, as there is a primitive +form of job control available when +.I Wish +is compiled with #define V7JOB. This doesn't always work, but most of +the time you can ^Z, bg and fg. +.PP +Job control under Posix systems is still slightly broken; I haven't +had the time to sit down and grok the full implications of the Posix +rationale for job control. +.PP +.SH Command Line Editing (CLE). +.PP +.PP +This feature allows the use of control keys and meta-character key sequences +to perform editing operations upon the current line. This is done by using +"cbreak" terminal mode (see +.I stty(1) +) which causes an interrupt to be generated after each character is typed and +hence available for input, rather than just at the end of a line when return is +typed. Wish also turns echoing off so +that it can position the characters properly +and effect autowrap, control character representation, etc. These two modes +are reset when a foreground job is executed. Other modes are settable by the +user and not changed by Wish. +.PP +European users will be happy to know that the CLE is now 8-bit transparent. +For users with 7-bit keyboards, the CLE now has a command to produce +8-bit keystrokes. When 8-bit characters cannot be displayed on the screen, +turn the Wish variable `MSB' on; 8-bit characters will then be displayed +in bold. +.PP +The Command Line Editor provides many editing commands. The following +table lists the possible commands. The three columns hold: the default +key binding for the command, the internal key value for the command (usually +the same as the key binding), and a brief description of the action. Most +actions are obvious; the complex ones are described below. +.PP +.in +2 +.nf +KEY VALUE ACTION +=== ===== ====== +^@ \\000 save current position +^A \\001 goto start of line +^B \\002 go backwards one character +^C \\003 interrupt - discard line and start again +^D \\004 delete current char, list options, or exit shell +^E \\005 goto end of line +^F \\006 go forward one character +^G \\017 kill whole line +^H \\010 back-delete one character +^I TAB \\011 complete current word +^J LF \\012 finish line (same as return) +^K \\013 kill from cursor to end of line +^L FF \\014 clear screen and redisplay line +^M CR \\015 finish line (same as linefeed) +^N \\016 step forward in history +^O \\017 toggle insert/overwrite mode +^P \\020 step backward in history +^Q \\021 resume tty output +^R \\022 redisplay the line +^S \\023 stop tty output +^T \\024 transpose the current and previous chars +^U \\025 accepts number and key to perform num times +^V \\026 next character to be read literally +^W \\027 delete word backwards +^X \\030 goto saved position +^Y \\031 yank the previous word into a buffer +^Z suspend process (no effect upon CLE) +^[ ESC prefix for meta-commands +^\\ \\034 beep +^] \\035 turn CLE mode bit on (see bindings below) +^^ \\036 turn CLE mode bit off (see bindings below) +^_ \\037 turn the following character's msb on +SPC../ insert that character +0..9 insert the character or read digit (^U) +:..~ insert that character +^? DEL back-delete character (same as ^H) +ESC-^P \\201 match previous partial command +ESC-b +ESC-B \\202 skip backwards one word +ESC-d +ESC-D \\203 delete word forwards +ESC-f +ESC-F \\204 skip forward one word +ESC-h +ESC-H \\205 get help on word +ESC-p +ESC-P \\206 insert buffer onto the line +ESC-y +ESC-Y \\207 yank the next word into a buffer +ESC-/ \\210 search forward for next typed character +ESC-? \\211 search backwards for next typed character +.in -2 +.fi +.PP +Note that some keys which normally generate signals (e.g. ^Z, ^Y, ^\\) +do not do so whilst a line is being entered/edited. Their signal generating +status is returned whenever a foreground job is in progress. +.PP +.SH Complex CLE Functions +.PP +.PP +Some of the CLE commands are complicated. Their full description is given here. +.PP +^D Delete char, list options, leave shell +.PP +This command has three functions. If you use the command at the beginning of +a line, you will exit the shell. If your cursor is on top of a character, +that character will be deleted. Finally, if your cursor is at the end of the +line, a list of `expansions' will be shown. This command will show files, +programs you can run, user's home directories and variables, depending upon +how you use it. +.PP +For example, to see a list of files in your current directory, you can do +.PP +.nf + % word ^D +.fi +.PP +To see all the files starting with `s', do +.PP +.nf + % word word s^D +.fi +.PP +The word can be a relative or absolute path name; to see the files starting +with `std' in the include directory, and the parent directory, do +.PP +.nf + % word word /usr/include/std^D + % word word ../std^D +.fi +.PP +If the word is the first on the line, aliases, builtins and programs in +your PATH are listed. To see all the commands, and all the commands starting +with `cl' do +.PP +.nf + % ^D + % cl^D +.fi +.PP +If the word begins with a `$', the command will give a list of variable names; +to see all the variable names, and all starting with `H', do +.PP +.nf + % word word $^D + % word word $H^D +.fi +.PP +If the word begins with a `~', the command gives a list of users. Examples: +.PP +.nf + % word word ~^D + % word word ~ro^D +.fi +.PP +Words with `/' signs in them will be expanded internally to their pathname +equivalents. The word `~#/' means the list of aliases and builtins. +For example, to list the files in fred's directory, in $HOME, and the +aliases and builtins, do +.PP +.nf + % word word ~fred/^D + % word word $HOME/^D + % word word ~#/^D +.fi +.PP + Filename Completion +.PP +Filename completion is closely associated with the list options function. If +you use instead of ^D, the word on the command line is completed up +to the first duplicate letter. For example, if you have two files 'bigfile.c' +and `bigfile.o' in the current directory, then +.PP +.nf + % ls bi becomes + % ls bigfile. +.fi +.PP +Words with only one match are fully completed. This function understands the +same words as the list options functions, so you can complete variables, +usernames, programs, and files in directories like +.PP +.nf + % ls $HOME/.cl becomes + % ls $HOME/.wishrc +.fi +.PP +Quoting Commands +.PP +The CLE provides commands to quote characters instead of them performing +functions. The first is ^V. For example, to insert a ^A on the line without +^A doing its usual action, do +.PP +.nf + % word word ^V^A +.fi +.PP +The ^_ command works exactly the same, but also turns the character's +most significant bit on, so 8-bit keystrokes can be entered from a 7-bit +keyboard. +.PP +^U Repeat Keystroke +.PP +^U is followed by a decimal number (which isn't displayed), and one character. +That character is repeated number times. +.PP +ESC-^P Match Partial Command +.PP +This command only works for the first word. When the cursor is at the end of +the first word, and this command is done, the last command that matched +the word is added to the line. For example: +.PP +.nf + % ls -l + % wc file + % echo hello + % lESC-^P becomes + % ls -l +.fi +.PP +Word Commands +.PP +The word commands are affected by the Wish variable wordterm. This can be set +to holds the characters that `end' a word. If this variable isn't defined, +the CLE assumes it holds the following characters: space, tab, > < | / ; = +& and `. +.PP +.SH Bindings +.PP +.PP +Wish now distinguishes between the commands available in the CLE and the +keystrokes that cause the commands to occur. This allows Wish to have +different editor bindings. For example, Wish defaults to an emacs-like +binding, as noted above, but can be given vi-like bindings by sourcing +the file Vibind. +.PP +The command characters given in the above table are used by the Wish +.B bind +builtin command, which can bind a keystroke sequence to another keystroke +sequence. With the default emacs-like style, most of the emacs keys are +identical to the internal CLE commands, and only the ESC commands are +internally bound by Wish. Setting up another editor style involves the use +of the `bind' and `unbind' builtins described in the Builtin Commands +section. +.PP +The CLE also offers a boolean `mode'. +When the mode is on, all current key bindings apply. However, when the mode +is off, key bindings marked as `mode only' are disabled. This allows the +vi-style set of bindings to be used. +.PP +The help command calls the system manual command with the word immediately +preceding the cursor as the argument. +.PP +." The next history command (^N) will attempt to predict the user's next command +." if it reaches the end of the history list (i.e. when nothing has been typed +." on the current line, yet) by looking for the most recent occurrence of +." the previous command (not +." including the previous command itself) and then loading the command +." .I after +." that onto the command line. This may be set up to occur automatically by +." setting the TARDIS variable (see Special Variables section). This enables +." the user to set up a loop of commands. A beep sounds if the shell is unable to +." predict what command is next (if the previous command never occurred before, +." for instance). +." .PP +." .nf +." % comm1 +." % comm2 +." % comm3 +." % comm1 +." % ^N --> % comm2 +." .fi +." .PP +.SH Variables. +.PP +.PP +The shell variable facility relies on the following metacharacters: +.PP +$ denoting a variable. Wish will interpret $ as a reference to a +shell variable. +The word immediately adjacent to the `$' is taken +as the variable name. This word is delimited by any of the metacharacters above, +by quotes, by another `$', or by an array subscript. e.g. +.PP +% echo $fred +.PP +This will echo the contents of the variable fred. +." , while this: +." .PP +." % echo $fred[2] +." .PP +." will echo the contents of the third field of fred. Note that array subscripts +." start from zero. Any access to a nonexistent field will return a null word. +.PP += assignment of variables. Variables are assigned simply by stating the name(s) +of the variable(s), followed by the equal `=' sign, and then the value(s). +." It is possible to assign two or more variables simultaneously. e.g. +." .PP +." % a b = x y +." .PP +." This statement assigns 'x' to $a and 'y' to $b. Any left-over words on the +." right hand side of the equals are assigned to the last variable listed on +." the left hand side. Similarly, any left-over variable names (on LHS) are +." assigned null values. Note, however, that this assignment is not truly +." concurrent (at this stage). (i.e. assignment is done left to right, not as +." if it all happened simultaneously. Therefore, variable swapping is not +." possible with only two variables. "a b=$b $a" would set both "a" and "b" to the +." original value of "b". +." .PP +." += appending to a variable. Variables can also be appended to with the `+=' +." operator. This works in the same way as the `=' but does not destroy the old +." value of the variable. To append to a variable, the variable must have existed +." previously. +.PP +Wish is responsible for two separate sets of variables. The examples given +above refer to +.I internal +shell variables. These variables are immediately +accessible to the user and also act as flags for some Wish functions +(e.g. HASH,ignoreeof,UNIV). The other set is known as +.I environment +variables. +Environment variables are passed to any new process by the execve system call. +When the shell is started all environment variables are loaded into the +shell's internal variable storage space. +When this initialisation occurs, +these variables are automatically marked for +.I export +, meaning that when execution of a command +takes place, the variables marked for export form the environment for +the executed command. +It is also possible to mark any variable for export by +using the +.I export +command described below. +.PP +." While there is no +." .I set +." command, variables can be deleted using the +." .I unset +." command. Also +." .I unsetenv +." can be used to delete environment variables. If you +." don't want all of the environment variables cluttering up your internal +." variable list, there is no harm in deleting them but they will not be +." re-exported. To reload the environment variables, simply use the command: +." .PP +." e.g. % PATH=$PATH +." .PP +." since the environment is searched for the variable if it does not exist in the +." internal variable list. +." .PP +." A new feature found only in Wish is the +." .I edit +." command which allows the user +." to edit a variable. This is particularly handy for long variables such as PATH +." and TERMCAP in which only a small component has to be altered. Editing is done +." using the command line editor features described above. +.PP +Wish also has several inbuilt variables: +.PP +.nf + $$ - The process-id of the process + $? - The exit status of the last process + $# - The number of arguments to the process + $n - (where n is a digit) The n'th argument to the process. + $* - All the arguments to the process (except $0) +.fi +.PP +When used interactively, these refer to the Wish itself. However, in +aliases and Wish scripts, these variables refer to the alias or script. +For example, if `prog' is a Wish script, and you type +.PP +% prog foo bar +.PP +then from within "prog", $0 will be "prog", $1 will be "foo" and $2 will be +"bar". Similarly, an alias such as +.PP +% alias l '/bin/ls -lg $*' +.PP +will do '/bin/ls -lg' on all of its arguments. +.PP +.SH History and History Substitution. +.PP +.PP +The history mechanism enables every command typed to be stored and accessed +at a later time. To get a list of previous commands use the +.I history +command. Wish does not enter the +.I history +command into the history, nor does +it store single letter lines since these can be more easily retyped than +by using history substitution (described below). The number of commands +remembered by Wish is set by the internal "history" variable. +.PP +It is possible to recall a previous line(s) from the history by using the +metacharacter `!', followed by the absolute history number, a relative history +number, a string which matches the most recent command starting with the +same string, or another `!' which refers to the previous command. e.g. +.PP +% !12 fred +.PP +This will substitute the history event numbered 12 onto the line at that +position and the argument "fred" will remain as an argument to that command. +.PP +% !-3 +.PP +This will substitute the event from three commands ago. It is still possible +to have an argument or a different substitution on the same line. +.PP +% !fo +.PP +This will substitute the most recent command starting with "fo". +.PP +You can also append characters onto a previous history command rather than +adding a new word. If we issued a command "ls -l" and then used "!la" the +shell would search for a command starting with "la" instead of finding "ls -l" +and appending the "a". So, to achieve this, simply quote the characters that +are to be appended (actually you only need quote the first). Hence, +.br +% !l"a" +.br +will give the desired substitution of "ls -la". Spaces may be included in +the string by using backslash or quotes. +.br +% !"v d" + +." .PP +." Another form of substitution exists which operates only on the previous +." line as an error correction mechanism. Although this is mostly superseded by +." the history pullback (^P) and command line editor operations, it was included +." for completeness. If a line starts with a `^' and has the format: +." .PP +." % ^string1^string2 +." .PP +." then string1 is replaced by string2 in the previous command line. +." .PP +." .B Command Substitution. +." .PP +." The +." .I output +." of a command can be substituted into the command line by using +." .I backquotes +." to surround the command. e.g. +." .PP +." % d=`date` +." .PP +." This command causes the system program +." .I date(1) +." to be executed, and the resulting output is loaded (word by word) into the +." line to be parsed. +." .I Words +." are sequences of non-blank characters. A +." .I blank +." is a space, tab or newline. The "blank" characters are all interpreted as +." spaces when loaded into the line, however, the output of a program in +." backquotes can be preserved as a single argument by surrounding the backquotes +." with double quotes (""). Single quotes would cause the backquotes to remain +." uninterpreted, however. +.PP +.SH Filename Substitution. +.PP +.PP +Rather than type a lot of filenames, sometimes it is possible to specify +a group of files using the "globbing" technique. This means that using the +following metacharacters, you can specify files in any directory using a +sort of shorthand notation. The shell then performs what is called "pattern +matching" to produce the file names wanted. +.PP +* represents any number of characters including zero. +.br +? represents any one character. +.br +[] encloses a list of characters that can match. Two characters may also +have a hyphen separating them meaning "up to". So [A-Za-z] includes the +letters 'A' to 'Z' and 'a' to 'z'. +.PP +e.g. ab* matches all files in current directory starting with "ab". +.br +/usr/bin/lo*.? matches all files in /usr/bin starting with "lo" then +ending with a "." and any other character. +.br +[abc]*.c matches all the C-files in the current directory starting with +an "a", "b", or "c". +.PP +It is also possible to specify +.I subdirectories +using pattern matching e.g. +*/*.c matches all C-files in any subdirectory. +.PP +A further abbreviation method is available for referring to the home +directories of users. This is done by typing a `~' followed by a +.I username +or if no username is given then the home directory of the user running +the shell is used (i.e. ~ refers to your directory). +.br +e.g. "~cgibson" will be replaced by "/u1/hons/cgibson" +.br +"~mike/work" will be replaced by "/u1/staff/mike/work". +.PP +Wish now fully copes with systems that uses yypasswd. +." .PP +." It is also possible to abbreviate by using the {} metacharacters. Actually, +." only the `{' is recognised as a metacharacter. This can be achieved by +." placing a set of strings separated by commas `,' inside the curly braces. +." Note that the shell blindly expands without checking for existence of files +." (no matching done) and thus this command can refer to words other than +." filenames. +." .PP +." e.g. % ls {a,b,c}*.c +." .PP +." This will once again give the listing of C-files in the current directory +." but the difference is that before pattern matching takes place, the line +." is expanded to: "ls a*.c b*.c c*.c" and hence is less efficient since the +." shell must match 3 separate patterns. The following example shows another +." use of the curly brace expansion. +." .PP +." % echo {0,1}{0,1}{0,1}{0,1}{0,1}{0,1}{0,1} +." .PP +." This will echo the representation of the binary numbers from 0 to 127 +." because left to right order is preserved. +." .PP +." So, it is possible to combine any number of these metacharacters *,?,[],{},~ +." to save +." typing, but note that the more complicated the pattern, the longer the shell +." will take to expand it, particularly if many subdirectory levels are involved. +." Also, if many different sets of braces are given in the one word then the +." expansion becomes large and may not even fit on a line. For example, the above +." example using {0,1} has two options in each set of braces and seven sets of +." braces, resulting in two to the power of eight (128) different words. +.PP +.SH Quoting. +.PP +.PP +There are three types of quotes (not including backquotes) which may be used to +stop the above mentioned metacharacters from being interpreted. +." The most +." powerful is the +." .I backslash +." `\\' which only quotes the next character. +." This will quote absolutely anything except +." .I null +." (^@) which may not be quoted since it represents the end of a line +." (null-terminated strings. See +." .I string(3) +." ) and can't be typed, anyway. The next set of quotes, called +Single quotes '' will stop all interpretation except history +substitution (!). +." It is necessary to use the backslash here if the actual +." exclamation mark character is intended. +The last set of quotes is double quotes. +They allow history and variable substitutions but disallow pattern matching. +Note that quotes may be nested but only the outer set have any effect. +." The exception is the use of the +." .I backslash +." when quoting the `!' and when quoting +." the `$' and ``' in double quotes. +.PP +.SH Aliases - User defined Command Sequences +.PP +.PP +It is possible for the user to define their own set of aliases. Unlike +.I Csh(1) +where the first word of any command is checked to see if it's an +alias, and then the alias is substituted onto the line, Wish allows full +shell scripts to be defined which are then executed by the shell after +parsing the line. These aliases are stored internally and executed in +a similar way as a Wish script. Thus it is possible to pass arguments as +$1 $2 etc. or $* for all arguments. In fact, the aliases should behave exactly +as a script stored in the user's directory but without taking up directory +space. Remember that the more aliases that exist, the more slowly commands +will run. +." This problem is greatly reduced in Wish compared with +." .I Csh +." if +." .I hashing +." is on, because the builtins and aliases are hashed from the path along +." with all the other commands and so access to them is still quick via this +." method. The only reason for not using hashing would be if the hash table got +." too full (too many files on your PATH) or process space was needed on the +." system. +." Also using the `~#' metanotation, you can specify explicitly to use a builtin +." or alias if they occur lower down in the path then a system command of the same +." name. e.g. +." .PP +." % ~#/list +." .PP +." This will execute the builtin command +." .I list +." rather than the system command +." .I list(1). +Because of the way that these aliases are implemented, it is easy to +call builtins, system commands, and even other aliases, from within an alias. +You should be careful not to call an alias from within itself since this will +cause an error. Aliases can be created using the builtin command +.I alias +, see below in Builtin Commands section. +.PP +.SH Builtin Commands +.PP +.PP +This section describes the commands that are builtin to the shell. +Builtins are only executed in a subshell if they are at the start or in the +middle of a pipeline or run in the background. +." They exist in a virtual directory +." ~# and can be referred to in this way. See the Aliases section above for more +." information. +.PP +This section is divided up into several subsections, because some +builtins can be left out if they cannot be used or are not needed. +To find out which builtins you have, do +.PP +.nf + % ~#/^D +.fi +.PP +This gives you a list of aliases and builtins. +.PP +.B 1. Standard Builtins +.PP +.TP 4 +." alias [ [ -e -l -s] [ ] ] +alias [ [ -e -s] [ ] ] +When invoked with no argument this command lists the aliases +currently defined. +." If the -e flag is given then an editor (default is .I vi(1)) +." is invoked upon a temporary file and this allows the user to create their +." Wish script which is loaded into the shell as an alias upon completion. This +." is needed if +." multi-lined aliases are to be defined since Wish's command line editor will not +." allow newlines to be displayed and inserted. +The -l flag instructs Wish to load +up the alias from the named file (the alias is given the same name). In the same +way -s causes Wish to save the alias to file. If none +of these flags appear, then the first argument is taken as the alias name, and +the remaining arguments as the alias definition. If there are no other +arguments, that alias's definition is printed. +.TP 4 +bind [[-m] keysequence [newkeysequence]] +This command allows the user to bind a series of keystrokes to a new series +of keystrokes. Note that most ASCII control characters (\\000 to \\037), and +some other control characters (\\201 to \\211) cause the CLE to perform a +function, so most bindings are to one or more of these characters. +With no arguments, +bind +shows the current set of key bindings. With a keysequence argument, +the binding of +the given sequence is shown. With a keysequence and a value, the sequence is +bound to the action. For example: +.PP + % bind '^[[A' ^P +.PP +binds the up-arrow key to the `step backward in history' action. +Bind +also understands C-like octal sequences, so that +.PP + % bind '^[[A' \001hello\005 +.PP +will cause the up-arrow to insert the word `hello' at the beginning of the +line. +.PP +If a keysequence is bound using the `-m' option, then the binding is marked +`mode-only', and is only invoked when the CLE is in MODEON mode. Otherwise +it is ignored. +.PP +Note that the CLE uses the first match found when parsing keyboard input. +For example, if the current bindings are: +.PP + % bind + ^[[A is bound to ^P + ^[[B is bound to ^N + ^[[Aa is bound to ^A + ^[[B is bound to ^E +.PP +then the keyboard input `^[[Aa' will do the command ^P (i.e. go back in +history), and then +add an `a' to the command line; similarly, the input '^[[B' will do the +command ^E (i.e. go to the end of the line). +.PP +Key bindings can be recursive. A large error will occur if you do +.PP + % bind a aa +.PP +as the next time you type `a', it will become an infinite number of a's, +up to the size of the CLE line buffer. +.TP 4 +cd [ ] or chdir [ ] +This command changes the current working directory of the +current shell process. Thus, if cd occurs inside a child process, when that +process dies, the parent will not have changed directory. Cd takes a single +argument which is the name of the directory to change to. The directory name +may be a full path name starting from the root directory or a path name +relative to the current directory. The `~' notation can also be used to refer +to the home directory of users. +.TP 4 +echo [-n] +Echo writes the given arguments out on a single line on standard output. A +newline is appended unless the -n option is given. +." .TP 4 +." edit +." Edit takes one argument which is a variable name that is +." to be edited. The editor used is the command line editor. After completion of +." the editing sequence, press return and the variable is saved. Note variables +." must exist prior to editing them. (See also Command Line Editing section). +." .TP 4 +." exec [ ] +." Replace the current shell with a different program which is +." supplied as arguments. All arguments are passed to +." .I execve(2). +." .TP 4 +." exit [ ] +." Causes the termination of the shell (unless it is the +." login shell). If a numerical argument is given then the shell exits +." with that argument. Default status is zero. +.TP 4 +export [ [ - ] ] +mark a variable for export, or if `-' flag given, unmark +export ability for that variable. When a variable is marked for export, it +means that the internal copy of the variable is transferred to the environment +whenever a program is executed, making the new copy available to the new +process. +.TP 4 +history [ m[-n ] ] +Lists the previously executed commands remembered by the shell. An optional +range can be specified which will list the commands numbered from +.I m +to +.I n. +A single number argument will display the m most recent commands. +.TP 4 +list [ env ] +List the internal variables defined in Wish. With the "env" option, the +environment variables are listed (like +.I printenv(1)). +." .TP 4 +." load +." Load the arguments supplied onto the next command line. Can be used to +." load up alias definitions for example if they need to be slightly altered, +." or to check a history substitution (or other substitution) without automatically +." executing the line. Allows a second carriage return for confirmation. +." .TP 4 +." lock +." Lock the keyboard. User is prompted for a password which must be retyped +." to confirm it. The shell enters "lock" mode and this mode can only be exited by +." once again typing the correct password. +." .TP 4 +." logout +." The command used to logout from the shell and hangup from +." the system. This command only has effect from the lowest shell level (i.e. +." the login shell). +." .TP 4 +." repeat +." This statement causes the command to be executed the number of times specified. +.TP 4 +source [ ] +Cause the Wish script to be executed in the current shell, as if the contents +of the file had been typed from the keyboard. This is useful if the environment +variables are to be set for the shell. (e.g. if you have a script that changes +your TERMCAP or PRINTER variable etc.) This script could also be loaded as an +alias with "alias -l". +.TP 4 +tilde [-l], tilde [[shorthand] [dirname]] +Wish keeps an internal list of shorthand names for directories. Tilde adds +the given shorthand/dirname pair to the list. From then on, you can use +~shorthand to mean the dirname. If just shorthand is given, the expansion +for that shorthand is given. With no arguments, all the internal pairs are +given. The -l argument will also show the username/directory pairs from +the /etc/passwd file. +.TP 4 +unalias ... +Delete the named alias if it exists. If not an error message is returned. +.TP 4 +unbind keysequence +Unbinds the given key sequence, if that sequence has been bound. +.TP 4 +untilde +Removes the shorthand name/directory pair from the internal list of +directory shorthands. +unset ... +Delete the internal variable named. If an environment variable of the same +name exists, it will be deleted. +.TP 4 +unsetenv ... +Delete the environment variable named. This command should be used with +caution since some system programs use the environment variables. The +shell with still have an non-exported copy of the variable. +." .TP 4 +." which [ -o ] [ -O ] +." In its basic form, tells you the full pathname of the file you execute by +." typing . With -o, aliases are ignored and with -O aliases and builtins +." are ignored. +.PP +.B 2. Job Control Builtins +.PP +.TP 4 +bg [ % ] [ ] +This command has the effect of starting the current job (which will be +stopped) in the background if given no arguments. If given arguments it starts +the specified jobs in the background. The arguments are simply the job numbers +which have been assigned by the shell. These can be obtained using the +.I jobs +command. +.TP 4 +fg [ % ] [ ] +Cause the current job to execute in the foreground +whether stopped or executing in the background. If an argument is supplied +it is taken as the job number (as described in +.I bg +and Job Control section). +.TP 4 +jobs +Give a list of current jobs (processes) recognised by +Wish. These jobs are the either running in the background or are stopped. +The shell cannot keep track of processes that result from a +.I fork(2). +In the att universe, job control is severely restricted. (See Job Control +section). +." .TP 4 +." kill [ - ] % [ or ] ... +." Kill the specified job number or process id (pid) with the optionally +." specified +." .I signal. +." Signals are the same as those defined in +." .I sigvec(2) +." (ucb) or +." .I signal(2) +." (att) but with the prefix "SIG" removed. If the signal is unspecified, then +." the SIGTERM signal is sent. If the SIGTERM and SIGHUP signals are sent the +." the shell will send the +." SIGCONT signal as well, which is needed if a stopped job is to receive the +." signal. +." .TP 4 +." stop [ % ... ] +." Stop the current (or named) job that is running in the background. Jobs may be +." restarted using either +." .I bg +." or +." .I fg. +." .TP 4 +." suspend +." Cause this shell to suspend or stop. Control is returned to an outer level +." but the shell will remain in a stopped state (receives a stop signal, like +." typing ^Z). +." .TP 4 +." wait +." Cause the shell to wait for all background processes. The wait can be +." interrupted (^C) at which stage the shell prints a list of all jobs +." currently outstanding. +." .PP +." .B 3. Hashing Builtins +." .PP +." .TP 4 +." hashstat [ x ] +." Display an analysis of the internal hashing table. +." With no arguments, hashstat returns the actual hits and misses whenever the +." shell tries to execute a program and also a hit ratio. With the single argument +." "x", Wish returns the hits and misses encountered when creating the hash +." table, an expected hit ratio, and how full the table is. +." .TP 4 +." rehash +." This causes the files on the user's path to be entered into a hash table. +." If hashing was previously turned off, then it is turned on with this command. +." You will need to do this if a new executable file appears in a directory on +." your path. +." .PP +." .B 4. Non-Minix Builtins +." .PP +." .TP 4 +." help [ ] +." Help gives some additional information about the previous error that occurred, +." or if supplied with an internal error number, explains that particular error +." in more detail. +." .PP +." .B 5. Shell Script Builtins +." .PP +." Currently case, for, if, repeat, shift, and while exist as stub builtins, i.e +." they do nothing. It is planned to give Wish a scripting language as close +." as possible to sh(1). The only written shell script builtin is read. +." .PP +." .TP 4 +." read [ ... ] +." Read from the standard input until an end of line is reached. The characters +." that were read are assigned to the named. If more than one variable +." is to be read, the first word of the input is assigned to the first variable; +." the second to the second respectively; the final variable is assigned any left +." text. If there are more variables than words read, remaining variables are +." assigned null strings. Note this is logically equivalent to variable assignment. +." (see Variables.) +.PP +.SH Special Variables. +.PP +.PP +This section contains a description of the variables which are used by the +shell to indicate certain conditions or which actually affect the running +of the shell in some way. The user may set these variables to suit their own +personal tastes. +.TP 4 +KEEPSTTY +This variable affects the state of the terminal under Wish. If not defined, +you can change the terminal state using stty, and Wish will use the new +state, with the exception of cbreak/cooked modes. with KEEPSTTY, Wish will +always reset the terminal to the initial state. This is useful if you have +a program with sometimes crashes, leaving the terminal in nl state or even +changing the speed. Defining this variable will set the terminal back to a +sane state when you return to Wish. +.TP 4 +MSB +This variable affects the way characters with their msb on are printed. +Wish defaults to printing characters as they are. However, if MSB is set to +any value, characters above 127 are AND-ed back to 0-127, and are printed +in bold. +." HASH +." If HASH exists, hashing for the path will be turned on. If unset then +." hashing is turned off. HASH needs no value. +." .TP 4 +." TARDIS +." Cause the shell to automatically try to predict what command the user wants +." typed every time the prompt is returned. This is the same as typing ^N every +." time the prompt appears. +." .TP 4 +." UNIV +." For dual universe implementations of Unix, such as Pyramid OSx. +." Can be set to either "att" or "ucb" +." and is probably extremely machine dependent. Contact your system administrator +." regarding this. +.TP 4 +beep +This variable can be set to any string of characters +which will be output whenever a bell would normally sound. By default, +this value is ASCII 7 (^G). If set to null, the shell will give no warning +bell. (For terminals with inverse video, "^[[?5h^[[?5l" will produce a +flash). +.TP 4 +cwd +Holds the value of the current working directory. This variable is automatically +updated by the shell whenever a cd or chdir occurs. It generally should not be +changed by the user. +.TP 4 +." history +." The number of commands the shell is to remember for use with the +." .I history +." command. If set to zero, then of course no commands are remembered. The larger +." history's value, the more space is occupied by the shell process since it must +." store all the previous commands. History is set to 25 by default. +." .TP 4 +." ignoreeof +." If set, ignoreeof causes the shell to ignore the ^D key as a means of exiting +." the shell. Ignoreeof needs no value. +." .TP 4 +." nohistdup +." If set, successive identical lines are not saved in the history. +." .TP 4 +prompt +.in -4 +.br +." prompt2 +." .in +4 +." .br +Prompt contains the format description for the user prompt. By default +this is a `%' sign. Prompt2 contains the format description for the second +prompt, given when more information is needed, like in a while, case, or if +statement being executed in interactive mode (i.e. from keyboard). +The format description is simply a string of characters which are displayed, +except that some special attributes may be entered using the `%' metacharacter. +.br +.in +4 +%% a percent `%' sign. +.br +%d current working directory. +.br +%! +.br +%h current history number. +.br +%S start standout mode (see +.I termcap(4)). +.br +%s stop standout mode. +.br +%@ +.br +%t time in 24-hour format with seconds. +.br +.in -4 +If any other character follows the `%' then the `%' and then that character is +printed. Other characters are just printed. e.g. "%d[%h] " could produce +"/u1/staff/mike/work[58]" as the prompt. +This prompt format is similar to that used by +.I tcsh(1). +." .TP 4 +." umask +." Contains an octal number representing the umask for files when created by +." the shell. The default file creation mode can be found by OR-ing this value +." with octal 0777. See +." .I chmod(1) +." and +." .I umask(2). +." .TP 4 +." wid +." Sets the terminal width. Tells the command line editor when to effect a line +." wrap so that the cursor stays in the right place. +.TP 4 +wordterm +This variable holds all the characters that delimit words, and is used in +the word-oriented CLE functions. If undefined, the CLE will use the +characters SPACE, TAB, > < | / ; = & and `. +.SH AUTHORS +Callum Gibson- Honours project 1988. +Warren Toomey. +.SH FILES +.nf +~/.wishrc Read at beginning of execution of Wish. +." ~/.history Saved commands from history list. +." ~/.login Read only by login shell, after .wishrc. +." ~/.logout Read at logout time. +." /etc/passwd source of directories for ~name. +." /usr/local/man help manual called from CLE, defined at +." compile time. +." /usr/local/lib/wish.hlp Text for help builtin command. +.fi +.SH LIMITATIONS +Maximum line length is 2048 (but redefinable). Maximum number of arguments +is 512. Not all of the above features exist, yet, and some new ones may be added +in future versions. +.SH SEE ALSO +chmod(1), csh(1), sh(1), stty(1), tcsh(1), exec(2), fork(2), pipe(2), umask(2), +wait(2), string(3), signal(3), termcap(4), tty(4), environ(7). diff --git a/Applications/wish/Wishrc b/Applications/wish/Wishrc new file mode 100644 index 0000000000..ddd53e63eb --- /dev/null +++ b/Applications/wish/Wishrc @@ -0,0 +1,15 @@ +# Here are some basic aliases and stuff for Wish 2.0 +# +# setenv PATH $HOME/.bin:/usr/local/bin:/usr/etc:/usr/ucb:/bin:/usr/bin +set dir ' ' +set prompt '%d: ' +# +alias , 'make $*' +alias boring 'echo I know' +alias h history +alias j jobs +alias logo exit +#alias ls '/bin/ls -lg $*' +#alias l '/bin/ls -C $*' +alias mn 'nroff -man $* | less' +alias zless 'zcat $* | less' diff --git a/Applications/wish/alias.c b/Applications/wish/alias.c new file mode 100644 index 0000000000..e1ad54c31a --- /dev/null +++ b/Applications/wish/alias.c @@ -0,0 +1,73 @@ +/* This file contains functions relevant to aliases, including alias + * creation in two ways, listing, interpreting. + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +#ifndef NO_ALIAS +struct vallist alist= /* The list of aliases */ + { NULL, NULL}; + +static struct val *aline; /* Pointer to one alias */ + +/* Checkalias returns a pointer to the alias named by aname. If + * no alias is found, it returns NULL. It also points aline to the + * alias definition. + */ +struct val *checkalias(aname) + char *aname; +{ + aline= searchval(&alist, aname, TRUE, FALSE); + return(aline); +} + +/* Getaliasline returns the alias one line at a time. + * It depends on having aline initialised correctly. + */ +bool getaliasline(line, nosave) + uchar *line; + int *nosave; + { + + *nosave=0; + if (aline==NULL) return(FALSE); /* No alias to return */ + strcpy(line,aline->val); + aline=NULL; + return(TRUE); + } + +int alias(argc,argv) + int argc; + char *argv[]; +{ + struct val *v; + +#ifdef DEBUG + fprints(2,"In alias with argc %d argv[1] %s\n",argc,argv[1]); +#endif + + if (argc==1) + for (v=alist.head; v; v=v->next) prints("%s\t'%s'\n",v->name, v->val); + if (argc==2) + { + v= searchval(&alist,argv[1],TRUE,FALSE); + prints("%s\t'%s'\n",v->name, v->val); + } + if (argc==3) + setval(argv[1], argv[2], &alist); + return(0); +} + +int unalias(argc,argv) + int argc; + char *argv[]; +{ + int i; + + for (i=1;argv[i];i++) + searchval(&alist,argv[i],FALSE,FALSE); + return(0); +} +#endif diff --git a/Applications/wish/bind.c b/Applications/wish/bind.c new file mode 100644 index 0000000000..e992672454 --- /dev/null +++ b/Applications/wish/bind.c @@ -0,0 +1,317 @@ +/* The key binding routines used by the command line editor + * + * $Revision: 41.3 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +/* Wish allows keystrokes to be bound to other keystrokes. The following + * structure is used to hold these bindings. Note well the mode bit; a + * binding with mode!=0 is only used when the CLE has it's mode bit on. + */ + +#ifndef NO_BIND +struct keybind +{ + uchar *key; /* The key sequence we have bound */ + int len; /* The length of the key sequence */ + uchar *cmd; /* The string it is mapped to */ + uchar mode; /* Mode bit. Binding only used when mode on */ + struct keybind *next; +}; + + +static int Keylength = 0; /* The maximum key length */ +static struct keybind *Bindhead = NULL; /* List of bindings */ + +uchar bindbuf[512]; /* Buffer used to expand bindings */ +uchar *bindptr; /* Pointer into bindbuf */ +uchar CLEmode; /* This holds the CLE mode */ + +/* The default key bindings + */ + +static char *defbind[15][3] = { + { NULL, "\033\020", "\201" }, + { NULL, "\033B", "\202" }, + { NULL, "\033b", "\202" }, + { NULL, "\033D", "\203" }, + { NULL, "\033d", "\203" }, + { NULL, "\033F", "\204" }, + { NULL, "\033f", "\204" }, + { NULL, "\033H", "\205" }, + { NULL, "\033h", "\205" }, + { NULL, "\033P", "\206" }, + { NULL, "\033p", "\206" }, + { NULL, "\033Y", "\207" }, + { NULL, "\033y", "\207" }, + { NULL, "\033/", "\210" }, + { NULL, "\033?", "\211" } +}; + +/* Bind is a builtin. With no arguments, it lists the current key bindings. + * With 1 arg, it shows the binding (if any) for argv[1]. With 2 args + * the string argv[1] will be replaced by argv[2]. + */ + +int +Bind(argc, argv) + int argc; + uchar *argv[]; +{ + int s, showall; + uchar *key, *cmd, *ctemp; + struct keybind *temp, *Bindtail; + + if (argc > 4 || argc < 3) + { + fprints(2, "usage: bind [[-m] key [value]]\n"); + return (1); + } + showall = 0; + switch (argc) + { + case 4: + key = argv[2]; + cmd = argv[3]; /* Bind a string to another */ + goto bindit; /* Yuk, a goto */ + case 3: + key = argv[1]; + cmd = argv[2]; /* Bind a string to another */ + bindit: + s = strlen((char *)key); /* Get the key's length */ + if (s == 0) + break; + + temp = (struct keybind *) malloc(sizeof(struct keybind)); + if (!temp) + { + perror("malloc"); + return (1); + } + temp->key = (uchar *) malloc(s + 1); + if (!(temp->key)) + { + perror("malloc"); + return (1); + } + + strcpy((char *)temp->key, (char *)key); /* Copy the key */ + temp->len = s; + temp->cmd = (uchar *) malloc(strlen((char *)cmd) + 1); + if (!(temp->cmd)) + { + perror("malloc"); + return (1); + } + /* Copy the value */ + ctemp = temp->cmd; + while (*cmd != EOS) + { + if (*cmd != '\\') + *(ctemp++) = *(cmd++); + else + { + showall = 0; + cmd++; /* or an octal value */ + while (isdigit(*cmd)) + showall = (showall << 3) + (*(cmd++) - 48); + *(ctemp++) = showall & 0xff; + } + } + *ctemp = EOS; + temp->next = NULL; + if (argc == 4) + temp->mode = 1; + else + temp->mode = 0; + if (s > Keylength) + Keylength = s; + + if (!Bindhead) /* Add to linked list */ + Bindhead = temp; /* Currently this allows duplicates :-( */ + else + { + for (Bindtail = Bindhead; Bindtail->next; Bindtail = Bindtail->next); + Bindtail->next = temp; + } + break; + case 1: + showall = 1; /* Print one or more bindings */ + case 2: + for (temp = Bindhead; temp; temp = temp->next) + if (showall || !strcmp((char *)temp->key, (char *)key)) + { + if (temp->mode) + prints(" * "); + else + prints(" "); + mprint(temp->key, 1); + for (s = Keylength - temp->len; s; s--) + write(1, " ", 1); + prints(" bound to "); + mprint(temp->cmd, 0); + } + } + return (0); +} + +/* Unbind is a builtin which removes argv[1] from the list of key bindings */ + +int +unbind(argc, argv) + int argc; + uchar *argv[]; +{ + int s; + uchar *key; + struct keybind *temp, *t2; + + if (argc != 2) + { + fprints(2, "usage: unbind string, or unbind all\n"); + return (1); + } + key = argv[1]; + if (!strcmp((char *)key,"all")) + { + for (temp = Bindhead, t2 = Bindhead; temp; t2 = temp, temp = temp->next) + { + free(temp->key); + free(temp->cmd); + free(temp); + } + Bindhead = NULL; + return(0); + } + s = strlen((char *)key); /* Get the key's length */ + if (s == 0) + return (1); + + Keylength = 0; + for (temp = Bindhead, t2 = Bindhead; temp; t2 = temp, temp = temp->next) + if (s == temp->len && !strcmp((char *)temp->key, (char *)key)) + { + if (temp == Bindhead) + Bindhead = temp->next; + else + t2->next = temp->next; + free(temp->key); + free(temp->cmd); + free(temp); + } + else if (temp->len > Keylength) + Keylength = temp->len; + return (0); +} + +/* Initialise the default key bindings */ +void initbind() + { + int i; + + for (i = 0; i < 15; i++) + Bind(3, (uchar **) defbind[i]); /* Set default bindings */ + CLEmode = 0; /* and start in mode 1 */ + } + + +/* Exbind: get one or more characters from the user, expanding bindings + * along the way. This routine is recursive & hairy! When called from + * getcomcmd(), inbuf is NULL, indicating we want user keystrokes. + * These are read in, and if there are no partial bind matches, are + * returned to getcomcmd(). If any partial matches, they are buffered + * in bindbuf until either no partials or 1 exact match. Once a match is + * found, we call ourselves with inbuf pointing to the replacement string. + * Thus bindings can recurse, up to the size of bindbuf. Note also that + * after we recurse once, we check to see if there are any leftover chars + * in inbuf, and recurse on them as well. + */ + +#ifdef PROTO +static void expbind(uchar *inbuf) +#else +static void +expbind(inbuf) /* Expand bindings from user's input */ + uchar *inbuf; +#endif +{ + uchar a, *startptr, *exactptr; + int c, currlen, partial, exact; + struct keybind *temp; + + if (inbuf == NULL) + bindptr = bindbuf; + startptr = bindptr; + currlen = 0; + + while (1) /* Look for a keystroke binding */ + { + partial = exact = 0; + if ((inbuf == NULL) || ((a = *(inbuf++)) == EOS)) + c = read(0, (char *)&a, 1); + if (c != -1) /* Decide which uchar to put in the buffer */ + { + *(bindptr++) = a; + *bindptr = EOS; + currlen++; + if ((int) (bindptr - bindbuf) > 510) + return; + } + + for (temp = Bindhead; temp != NULL; temp = temp->next) + { /* Count the # of partial & exact matches */ + /* We exclude mode bindings when CLEmode==0 */ + if (CLEmode == 0 && temp->mode == 1) + continue; + if (currlen > temp->len) + continue; + if (!strcmp((char *)startptr, (char *)temp->key)) + { + exact++; + exactptr = temp->cmd; + } + if (!strncmp((char *)startptr, (char *)temp->key, currlen)) + partial++; + } + if (partial == 0) + break; /* No binding at all */ + if (partial == 1 && exact == 1) /* An exact match, call ourselves */ + { + bindptr = startptr; /* with the matched word */ + expbind(exactptr); + break; + } + } + + if (inbuf != NULL && *inbuf != EOS) /* If any part of our word left over */ + expbind(inbuf); /* check it as well */ +} + +/* Getcomcmd converts a user's keystokes into the commands used by the CLE. + * If there are no chars handy in bindbuf, we call expbind() to get some. + * Then we scan thru the chars and deliver chars or commands to the CLE. + * Hopefully because we use commands>255, the CLE will work with 8-bit + * extended ASCII. + */ + +int +getcomcmd() /* Get either a character or a command from the */ +{ /* user's input */ + int c; + + /* If no chars, get chars from stdin and */ + /* expand bindings */ + while ((c = *(bindptr++)) == 0) + { + expbind(NULL); + bindptr = bindbuf; + } + + /* Default to usual keys */ +#ifdef DEBUG + fprints(2, "Returning %x\n", c); +#endif + return (c); +} +#endif /* NO_BIND */ diff --git a/Applications/wish/builtin.c b/Applications/wish/builtin.c new file mode 100644 index 0000000000..4362ab9ad8 --- /dev/null +++ b/Applications/wish/builtin.c @@ -0,0 +1,275 @@ +/* This file does builtins + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +#ifdef PROTO +static int Echo(int argc, char *argv[]), Cd(int argc, char *argv[]); +static int Tilde(int argc, char *argv[]), Untilde(int argc, char *argv[]); +static int Exit(int argc, char *argv[]), Exec(int argc, char *argv[]); +static int Umask(int argc, char *argv[]); +#else +static int Echo(), Cd(); +static int Tilde(), Untilde(); +static int Exit(), Exec(); +static int Umask(); +#endif + +struct builptr buillist[] = { + {"cd", Cd}, + {"echo", Echo}, + {"exit", Exit}, + {"exec", Exec}, + {"umask", Umask}, + {"source", source}, +#ifndef NO_BIND + {"bind", Bind}, + {"unbind", unbind}, +#endif +#ifndef NO_HISTORY + {"history", history}, +#endif +#ifndef NO_VAR + {"export", export}, + {"unexport", unset}, + {"set", set}, + {"unset", unset}, + {"setenv", set}, + {"shift", shift}, +#endif +#ifndef NO_ALIAS + {"alias", alias}, + {"unalias", unalias}, +#endif +#ifndef NO_TILDE + {"tilde", Tilde}, + {"untilde", Untilde}, +#endif +#ifndef NO_JOB +#if defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) + {"bg", bg}, + {"fg", fg}, +#endif + {"jobs", joblist}, + {"kill", Kill}, +#endif + {NULL, NULL}}; + + +static int +Exit(argc, argv) + int argc; + char *argv[]; +{ + int how; + + if (argc == 2) + how = atoi(argv[1]); + else + how = 0; + leave_shell(how); + return (1); +} + +static int +Exec(argc, argv) + int argc; + char *argv[]; +{ + argv++; + execvp(argv[0], argv); + fprints(2, "Can't exec %s\n", argv[0]); + return (1); +} + +static int +Echo(argc, argv) + int argc; + char *argv[]; +{ + int doreturn = 1; + int firstarg = 1; + + if (argc > 1 && !strcmp(argv[1], "-n")) + { + doreturn = 0; + firstarg = 2; + } + + for (; firstarg < argc; firstarg++) + { + (void) write(1, argv[firstarg], strlen(argv[firstarg])); + (void) write(1, " ", 1); + } + if (doreturn) + (void) write(1, "\n", 1); + return (0); +} + +/* Here is the global definition for the current directory variable */ +char currdir[128]; + +static int +Cd(argc, argv) + int argc; + char *argv[]; +{ + extern struct vallist vlist; + char *path; + + if (argc > 1) + path = argv[1]; + else if ((path = EVget("HOME")) == NULL) + path = "."; + if (chdir(path) == -1) + { + fprints(2, "%s: bad directory\n", path); + return (1); + } +#ifdef USES_GETWD + if (getwd(currdir)) +#else + if (getcwd(currdir, MAXPL)) +#endif + { + setval("cwd", currdir, &vlist); + return (0); + } + else + { + (void) write(2, "Can't get cwd properly\n", 23); + return (1); + } +} + + +#ifndef NO_TILDE +/* Tilde is a builtin which associates a dir name + * with a shorthand for that dir. + * If called with -l, print out all the passwd file too. + * + * Name holds the shorthand name (starting with a ~). + * Var holds the actual name of the directory. + */ + +struct vallist tlist; /* The tilde list */ + +static int +Tilde(argc, argv) + int argc; + char *argv[]; +{ + struct val *t; + struct passwd *entry; + bool printall = FALSE; + + switch (argc) + { + case 3: + setval(argv[1], argv[2], &tlist); + return (0); + break; + case 2: + if (strcmp(argv[1], "-l")) + { + if ((t = searchval(&tlist, argv[1], TRUE, FALSE)) != NULL) + prints("%s %s\n", t->name, t->val); + break; + } + else + printall = TRUE; + case 1: + for (t = tlist.head; t; t = t->next) + prints("%s %s\n", t->name, t->val); + if (printall) + { + while ((entry = getpwent()) != NULL) + prints("%s %s\n", entry->pw_name, entry->pw_dir); + endpwent(); + } + break; + default: + prints("Usage: tilde [shorthand [dir]] or tilde -l\n"); + return (1); + } + return (0); +} + + +/* Untilde removes a shorthand from the tilde list */ + +static int +Untilde(argc, argv) + int argc; + char *argv[]; +{ + int i; + + if (argc < 2) + { + prints("Usage: %s [short] [short] ...\n", argv[0]); + return (1); + } + for (i = 1; i < argc; i++) + if (!searchval(&tlist, argv[i], FALSE, FALSE)) + prints("No such shorthand: %s\n", argv[i]); + return (0); +} +#endif + + +static int Umask(argc,argv) + int argc; + char *argv[]; + { + int umaskval; + char *cmd; + + switch(argc) + { + case 1: umaskval=umask(0); umask(umaskval); + prints("0%o\n",umaskval); + return(0); + case 2: if (*argv[1]=='0') /* Convert octal */ + { + cmd= argv[1]; umaskval=0; + while (isdigit(*cmd)) + umaskval = (umaskval << 3) + (*(cmd++) - 48); + } + else umaskval=atoi(argv[1]); + umask(umaskval); + return(0); + default: + fprints(2,"Usage: umask [value]\n"); return(1); + } + } + +/* Do builtin. This returns either the positive exit status of the builtin, + * or -1 indicating there was no builtin. We also return 0 for the `pid' + * of the builtin. Note that `fg' is an exception - it returns the new + * fg pid (i.e exit status for fg of 0) OR 0 (i.e exit status for fg of 1). + * So we have to do some mangling with the fg return value only. + */ +int +builtin(argc, argv, rtnpid) + int argc; + char *argv[]; + int *rtnpid; +{ + struct builptr *bptr; + + *rtnpid= 0; /* Usually */ + for (bptr = buillist; bptr->name != NULL; bptr++) + { + if (!strcmp(argv[0], bptr->name)) + if (strcmp(argv[0],"fg")) /* Not fg */ + return ((*(bptr->fptr)) (argc, argv)); + else + { *rtnpid= (*(bptr->fptr)) (argc, argv); + return((*rtnpid)?0:1); + } + } + return (-1); +} diff --git a/Applications/wish/clebuf.c b/Applications/wish/clebuf.c new file mode 100644 index 0000000000..7e0cb157b9 --- /dev/null +++ b/Applications/wish/clebuf.c @@ -0,0 +1,79 @@ +/* Command Line Editor Buffer functions + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +/* This version of CLE buffers all characters destined for the terminal + * into a buffer which is flushed just before getcomcmd is called. This + * should hopefully reduce the number of syscalls used in the CLE. The + * following variables hold the buffer & associated pointers. + */ + +#define OUTSIZE 128 /* The size of the buffer */ + +#ifndef NO_COMLINED +extern int curs[2]; +#endif +extern int wid; +extern bool Msb; + +static char outbuf[OUTSIZE]; /* The buffer we use */ +static char *outptr = outbuf; /* Ptr into outbuf for next char */ +static int outcnt = 0; /* Number of chars in outbuf */ + +/* Flushbuf simply flushes the buffer to stdout */ + +void +flushbuf() +{ + if (outcnt == 0) return; + write(1, outbuf, outcnt); + outcnt = 0; + outptr = outbuf; +} + +/* Addbuf takes a string and adds it to the outbuf. If outbuf is + * full, we flush the buffer. + */ + +void +addbuf(str) + char *str; +{ + while (1) + { + for (; *str && outcnt < (OUTSIZE - 1); outcnt++) + *(outptr++) = *(str++); + if (*str == EOS) return; + flushbuf(); + } +} + +/* Mputc prints out a character and updates the cursor position. + * It also handles control chars by preceding them with a caret. + */ + +void +mputc(b) + int b; +{ + extern char *so, *se; + uchar c = (uchar) b; + char d = c; + + if (Msb && c > 0x7f) + { addbuf(so); d &= 0x7f; } + *(outptr++) = d; /* We mimic addbuf for 1 char */ + if (++outcnt == (OUTSIZE - 1)) flushbuf(); + if (Msb && c > 0x7f) addbuf(se); +#ifndef NO_COMLINED + curs[0]++; + if (curs[0] >= wid) + { addbuf("\n"); /* goto start of next line */ + curs[0] = curs[0] % wid; /* hopefully gives zero */ + curs[1]++; + } +#endif +} diff --git a/Applications/wish/clex.c b/Applications/wish/clex.c new file mode 100644 index 0000000000..db218e16ba --- /dev/null +++ b/Applications/wish/clex.c @@ -0,0 +1,476 @@ +/* Here is the declaration of the candidate array. This is used in meta.c + * and probably in parse.c, as well as several million other places. + */ + +#include "header.h" + +struct candidate carray[MAXCAN]; +extern int ncand; +static int maxlen; + +/* Compare is the routine used by qsort to reorder the elements + * in the carray. + */ +int +compare(a, b) +#ifdef BSD43 + CONST struct candidate *a, *b; +#else + CONST void *a, *b; +#endif +{ + struct candidate *c, *d; + + c = (struct candidate *) a; + d = (struct candidate *) b; + return (strcmp(c->name, d->name)); +} + +/* Addcarray adds a new node to the carray. If prev is non-null, then + * prev->next points to the new node. If malc is TRUE, space is malloc'd + * and the word copied. + */ +void +addcarray(word, prev, mode, malc) + char *word; + struct candidate *prev; + int mode; + bool malc; /* Ha ha - a play on clam :-) */ +{ + int j; + struct candidate *here; + + if (ncand == MAXCAN) return; + here = &carray[ncand]; + if (malc && word) + { j = strlen(word); + here->name = (char *) Malloc((unsigned) (j + 2), "addcarray"); + strcpy(here->name, word); + } + else here->name = word; + if (prev >= carray) prev->next = here; + here->next = NULL; + here->mode = mode; + ncand++; +} + + +#ifndef NO_CLEX +/* Print out the maxlen partial match on the word, placing the result at + * pos in the given line. + */ +#ifdef PROTO +static void extend(char *line, int *pos, char *word) +#else +static void +extend(line, pos, word) + char *line; + int *pos; + char *word; +#endif +{ + extern char *wbeep; + extern int beeplength; + int i, j, nostop = 1; + char *newword, *t; + + if (*word == '~' || *word == '$') word++; + t = strrchr(word, '/'); /* Go to the last '/' */ + if (t != NULL) word = ++t; + + switch (ncand) + { + case 0: + Beep; + return; + case 1: + newword = carray[0].name; + default: + if ((newword = (char *) malloc((unsigned) maxlen + 2)) == NULL) + return; + for (i = 0; i < maxlen + 1; i++) /* Clear the new word */ + newword[i] = EOS; + strcpy(newword, word); /* Set up as much as we have */ + + for (i = strlen(word); nostop && i < maxlen; i++) + for (j = 0; j < ncand; j++) + { if (strlen(carray[j].name) <= i) /* Candidate too short, stop */ + { newword[i] = EOS; nostop = 0; break; } + if (newword[i] == 0) /* Copy 1 letter over */ + newword[i] = carray[j].name[i]; + if (newword[i] != carray[j].name[i]) /* Doesn't match the copy */ + { newword[i] = EOS; /* the scrub letter and stop */ + nostop = 0; + break; + } + } + } + for (i = strlen(word); i < strlen(newword); i++) + insert((uchar *) line, (*pos)++, (uchar) newword[i]); + + if (ncand == 1) + { if ((carray[0].mode & S_IFMT) == S_IFDIR) + insert((uchar *) line, (*pos)++, '/'); + else + insert((uchar *) line, (*pos)++, ' '); + } + else + { Beep; free(newword); } +} + + +/* Print out the candidates found in columns. + */ +#ifdef PROTO +static void colprint(void) +#else +static void +colprint() +#endif +{ + extern int wid; + int i, j, collength, numperline, index; + char format[6]; + + maxlen += 2; + numperline = wid / maxlen; + collength = ncand / numperline; + if (ncand % numperline) collength++; + sprints(format, "%%%ds", maxlen); + for (i = 0; i < collength; i++) + { write(1, "\n", 1); + for (j = 0; j < numperline; j++) + { index = i + j * collength; + if (index >= ncand) break; + if ((carray[index].mode & S_IFMT) == S_IFDIR) + strcat(carray[index].name, "/"); +#ifdef S_IFLNK + else if ((carray[index].mode & S_IFMT) == S_IFLNK) + strcat(carray[index].name, "@"); +#endif + else if (carray[index].mode & 0111) + strcat(carray[index].name, "*"); + prints(format, carray[index].name); + } + } + write(1, "\n", 1); +} + + + + +/* Find the name of a file, given a partial word to match against. The + * word may be an absolute path name, or a relative one. Any matches + * against the word are added to the carray. + */ +#ifdef PROTO +static void findfile(char *word) +#else +static void +findfile(word) + char *word; +#endif +{ + extern char currdir[]; + int i, j; + char partdir[MAXWL]; + char *cddir; + char *match; + DIR *dirp; + struct stat statbuf; +#ifdef USES_DIRECT + struct direct *entry; +#else + struct dirent *entry; +#endif + + for (i = 0; i < MAXWL; i++) + partdir[i] = EOS; + + if (word != NULL && *word != EOS) + { + /* Make full pathname */ + if (*word != '/') + { strcpy(partdir, currdir); + strcat(partdir, "/"); + } + strcat(partdir, word); + + /* Find the directory name */ + if ((match = strrchr(partdir, '/')) == NULL) + { prints("Looney! No / in word %s\n", partdir); return; } + + *match = EOS; + match++; /* Match holds the partial file name */ + + i = strlen(match); /* Get the length of the name */ + } + else + { strcpy(partdir, currdir); i = 0; } + + cddir= partdir; + if (*partdir == EOS) /* Can occur when only / is 1st char */ + cddir="/"; + + if ((dirp = opendir(cddir)) == NULL) + { prints("Could not open the directory %s\n", cddir); return; } + + if (chdir(cddir) == 0) + while ((entry = readdir(dirp)) != NULL && ncand < MAXCAN) + { + /* Ignore dot and dot-dot */ + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + continue; + + /* If we find a partial match add to list */ + if (i == 0 || !strncmp(entry->d_name, match, i)) + { j = strlen(entry->d_name); + if (j > maxlen) maxlen = j; + /* and get the mode as well */ + statbuf.st_mode = 0; + stat(entry->d_name, &statbuf); + addcarray(entry->d_name, NULL, statbuf.st_mode, TRUE); + } + } + closedir(dirp); + chdir(currdir); +} + + +/* Find the name of a variable, or if word has a / in it, get the + * variable's value and treat it as a path. + * Any matches against the word are added to the carray. + */ +#ifdef PROTO +static void finddollar(char *word) +#else +static void +finddollar(word) + char *word; +#endif +{ + char dir[MAXWL]; + extern struct vallist vlist; + struct val *v; + int i, j, mode; + char *a; + + word++; /* Skip over dollar sign */ + i = strlen(word); + if ((a = strchr(word, '/')) != NULL) /* We have to find a file */ + { *(a++) = EOS; /* Get the var name */ + for (v = vlist.head; v; v = v->next) + if (!strncmp(v->name, word, i)) + { sprints(dir, "%s/%s", v->val, a); /* Form the full path name */ + findfile(dir); /* and match against it */ + } + *(--a) = '/'; /* Extend needs to see the / */ + return; + } + + /* Look through the var names */ + for (v = vlist.head; v; v = v->next) + if (!strncmp(v->name, word, i)) + { j = strlen(v->name); + if (*(v->val) == '/') mode = S_IFDIR; /* Guess if it's a dir */ + else mode = 0; + addcarray(v->name, NULL, mode, TRUE); + if (j > maxlen) maxlen = j; + } +} + +/* Find the name of an alias or builtin, given the partial word. + * Any matches against the word are added to the carray. + */ + +#ifdef PROTO +static void findbuilt(char *word) +#else +static void +findbuilt(word) + char *word; +#endif +{ + extern struct builptr buillist[]; +#ifndef NO_ALIAS + extern struct vallist alist; +#endif + int h, i, j; + struct val *v; + + i = strlen(word); + +#ifndef NO_ALIAS + for (v = alist.head; v; v = v->next) + if (!strncmp(v->name, word, i)) + { j = strlen(v->name); + addcarray(v->name, NULL, 0700, TRUE); + if (j > maxlen) maxlen = j; + } +#endif + + for (h = 0; buillist[h].name; h++) /* Then the builtins */ + if (i == 0 || !strncmp(buillist[h].name, word, i)) + { j = strlen(buillist[h].name); + addcarray(buillist[h].name, NULL, 0700, TRUE); + if (j > maxlen) maxlen = j; + } +} + +/* Find the name of a file, or a user, given the partial word. If there + * are no slashes, just go for a user, else get the home dir & call + * findfile. Any matches against the word are added to the carray. + * + * This now uses the tilde list. + */ +#ifdef PROTO +static void findpasswd(char *word) +#else +static void +findpasswd(word) + char *word; +#endif +{ + extern struct vallist tlist; + struct val *t; + int i, j; + char dir[MAXWL]; + + struct passwd *entry; + + if (!strncmp(word, "~#/", 3)) /* If looking for builtins, */ + { findbuilt(&word[3]); + return; + } /* use findbuilt() */ + + if (strchr(word, '/') != NULL)/* We have to find a file */ + { tilde(word, dir); + if (*dir != 0) findfile(dir); + return; + } + + word++; /* Skip over tilde */ + i = strlen(word); + + /* Try our tilde list first */ + for (t = tlist.head; t; t = t->next) + if (!strncmp(t->name, word, i)) + { j = strlen(t->name); + addcarray(t->name, NULL, S_IFDIR, TRUE); + if (j > maxlen) maxlen = j; + } + + while ((entry = getpwent()) != NULL && ncand < MAXCAN) + { + if (i == 0 || !strncmp(entry->pw_name, word, i)) + { j = strlen(entry->pw_name); + addcarray(entry->pw_name, NULL, S_IFDIR, TRUE); + if (j > maxlen) maxlen = j; + } + } + endpwent(); +} + +/* Find the name of a file by looking through $PATH. + * Any matches against the word are added to the carray. + */ +#ifdef PROTO +static void findbin(char *word) +#else +static void +findbin(word) + char *word; +#endif +{ + int i; + char word2[MAXWL]; + char *Path, *thispath, *temp; + + temp = EVget("PATH"); /* Get the PATH value */ + if (temp == NULL) return; + Path = thispath = (char *) malloc((unsigned) strlen(temp) + 4); + if (thispath == NULL) return; + strcpy(thispath, temp); + while (thispath != NULL) + { for (i = 0; i < MAXWL; i++) word2[i] = EOS; + temp = thispath; + while (*temp != EOS && *temp != ' ' && *temp != ':') temp++; + if (*temp == EOS) temp = NULL; + else *(temp++) = EOS; + + strcpy(word2, thispath); + strcat(word2, "/"); + if (word != NULL || *word != EOS) strcat(word2, word); + + findfile(word2); + + while (temp != NULL && *temp != EOS && (*temp == ' ' || *temp == ':')) + temp++; + if (temp == NULL || *temp == EOS) thispath = NULL; + else thispath = temp; + } + free(Path); +} + + +/* Complete subsumes the work of two routines in old Clam, depending on how: + * + * case 0: Print out a columnated list of files that match the word + * at position pos. The line is unchanged. + * case 1: Try to complete as much as possible the word at pos, by + * using the find routines above. Add the completion to the line. + */ +void +complete(line, pos, how) + char *line; + int *pos; + bool how; +{ + extern char *wbeep, yankbuf[]; + extern int beeplength; + extern char *wordterm; + char *b, *yankword = yankbuf; + int startpos; + + ncand = maxlen = 0; + + if (line[(*pos) - 1] == ' ') + { + strcpy(yankbuf, ""); + startpos = *pos - 1; + } /* nothing there to get */ + else + { b = wordterm; /* first get the thing we've got so far */ + wordterm = " "; + startpos = yankprev((uchar *) line, *pos); + wordterm = b; + } + + if (*yankword == '\'' || *yankword == '"' || *yankword == '`') + yankword++; + if (startpos == 0 && *yankword != '.' && *yankword != '/' && *yankword != '~' + && *yankword != '$') + findbin(yankword); + else + switch (*yankword) + { case '~': + findpasswd(yankword); break; + case '$': + finddollar(yankword); break; + default: + findfile(yankword); + } + + if (how == TRUE) extend(line, pos, yankword); + else + { if (ncand == 0) Beep; + else + { qsort((void *) carray, ncand, sizeof(struct candidate), compare); + colprint(); + prprompt(); + show((uchar *) line, TRUE); + } + } + while ((--ncand) >= 0) free(carray[ncand].name); +} +#endif /* NO_CLEX */ diff --git a/Applications/wish/comlined.c b/Applications/wish/comlined.c new file mode 100644 index 0000000000..f6ed3a221d --- /dev/null +++ b/Applications/wish/comlined.c @@ -0,0 +1,775 @@ +/* List of commands defined for the command line editor. + * We only use the ctrl chars, so that people can use + * the editor with 8-bit ascii characters. + * + * $Revision: 41.3 $ $Date: 2003/04/21 13:08:43 $ + */ + +#include "header.h" + +#define MARK 0 /* Save position, make a mark */ +#define START 1 /* Go to start of line */ +#define BAKCH 2 /* Go back one character */ +#define INT 3 /* Interrupt this task */ +#define DELCH 4 /* Delete the current character */ +#define END 5 /* Goto the end of the line */ +#define FORCH 6 /* Go forward one char */ +#define BEEP 7 /* Simply ring the bell */ +#define BKSP 8 /* Backspace over previous character */ +#define COMPLETE 9 /* Complete the current word */ +#define FINISH 10 /* Finish and execute the line */ +#define KILLEOL 11 /* Kill from cursor to end of line */ +#define CLREDISP 12 /* Clear screen & redisplay line */ +#define NL 13 /* Same as FINISH */ +#define NEXTHIST 14 /* Step forward in history */ +#define OVERWRITE 15 /* Toggle insert/overwrite mode */ +#define BACKHIST 16 /* Step backward in history */ +#define XON 17 /* Resume tty output */ +#define REDISP 18 /* Redisplay the line */ +#define XOFF 19 /* Stop tty output */ +#define TRANSPCH 20 /* Transpose current & previous characters */ +#define KILLALL 21 /* Kill the whole line */ +#define QUOTE 22 /* Quote next character literally */ +#define DELWD 23 /* Delete word backwards */ +#define GOMARK 24 /* Goto a mark */ +#define YANKLAST 25 /* Yank the previous word into a buffer */ +#define SUSP 26 /* Suspend process */ + +/* A Gap here for 27 This can be filled as needed */ +/* A Gap here for 28 This can be filled as needed */ + +#define MODEON 29 /* Include bindings with mode bit on */ +#define MODEOFF 30 /* Exclude bindings with mode bit on */ +#define QUOTEUP 31 /* Turn the following char's msb on */ + +#define DEL 127 /* Same as BKSP */ + +#define MATCHPART 129 /* Match a previous partial command */ +#define BAKWD 130 /* Go backwards one word */ +#define DELWDF 131 /* Delete word forwards */ +#define FORWD 132 /* Go forwards one word */ +#define GETHELP 133 /* Get help on a word */ +#define PUT 134 /* Insert buffer on the line */ +#define YANKNEXT 135 /* Yank the next word into the buffer */ +#define SEARCHF 136 /* Search forward for next typed character */ +#define SEARCHB 137 /* Search backwards for next typed character */ + +#define isctrl(x) (((x+1)&0x7f)<33) + +/* Strip takes the line, and removes leading spaces. If the first non-space + * character is a hash, it returns 1. This should also remove trailing + * comments; I might just move the whole thing into meta_1. + */ +#ifdef PROTO +static int strip(uchar *line) +#else +static int +strip(line) + uchar *line; +#endif +{ + int i, nosave = 0; + + for (i = 0; line[i] == ' '; i++); + if (line[i] == '#') + { nosave = 1; i++; } + if (i) strcpy((char *) line, (char *) &line[i]); + return (nosave); +} + +bool Msb; /* Is var Msb defined? */ +#ifndef NO_COMLINED +uchar yankbuf[512]; /* Buffer used when yanking words */ +uchar *wordterm; /* Characters that terminate words */ +extern int wid; /* The width of the screen (minus 1) */ + +/* A quick blurb on the curs[] structure. + * Curs[0] holds the column pos'n of the cursor, curs[1] holds the # of + * lines the cursor is below the prompt line. e.g (0,0) is the first char + * of the prompt, (25,3) is in column 25, 3 lines below the prompt line. + */ +int curs[2]; + + +/* Go moves the cursor to the position (vert,hor), and updates the cursor */ +#ifdef PROTO +static void go(int hor, int vert) +#else +static void +go(hor, vert) + int hor, vert; +#endif +{ + extern char *bs, *nd, *up; + int hdiff, vdiff; + + vdiff = vert - curs[1]; /* vertical difference between */ + /* current and future positions */ + + if (vdiff <= 0) /* if negative go up */ + for (; vdiff; vdiff++) addbuf(up); + else + { /* else go down */ + for (; vdiff; vdiff--) addbuf("\n"); + curs[0] = 0; + } + + hdiff = hor - curs[0]; /* horizontal difference between */ + /* current and future positions */ + curs[0] = hor; + curs[1] = vert; /* a new current pos, hopefully */ + /* assigned here because hor changed */ + /* below and curs needed above */ + + if (hdiff < 0) /* if negative go back */ + { + if (-hdiff <= hor) /* if shorter distance just use ^H */ + for (; hdiff; hdiff++) addbuf(bs); + else + { /* else cr and go forward */ + addbuf("\r"); + for (; hor; hor--) addbuf(nd); + } + } + else + for (; hdiff; hdiff--) /* have to go forward */ + addbuf(nd); +} + +/* Backward: Move the cursor backwards one character */ +#ifdef PROTO +static void backward(void) +#else +static void +backward() +#endif +{ + extern char *bs; + + if (curs[0] == 0) go(wid - 1, curs[1] - 1); + else { addbuf(bs); curs[0]--; } +} + +/* Forward: Move the cursor forwards one character */ +#ifdef PROTO +static void forward(void) +#else +static void +forward() +#endif +{ + extern char *nd; + + curs[0]++; + if (curs[0] >= wid) + { + addbuf("\n"); /* goto start of next line */ + curs[0] = curs[0] % wid; /* hopefully gives zero */ + curs[1]++; + } + else addbuf(nd); +} + +/* Show is a routine that replaces four routines in the old version of Clam. + * The flag variable holds the id of which `routine' to emulate: + * + * case 0: Insert Insert the letter at position pos in the line, + * updating the cursor, and redrawing the line. + * case 1: Overwrite Overwrite the letter at pos in the line, + * updating the cursor, and redrawing the line. + * Note that to toggle ins/ovw, we can have a + * variable ovwflag, and do ovwflag= 1-ovwflag. + * case 2: Show Just redisplay the line. + * case 3: Goend Goto the end of the line. + */ +int +Show(line, pos, let, flag) + uchar *line; + int let, pos, flag; +{ + extern int lenprompt; + int i, horig=0, vorig=0; + uchar letter = (uchar) let, c; + + switch (flag) + { /* Case 0: insert character */ + case 0: + for (i = pos; line[i]; i++); /* goto end of line */ + for (; i != pos; i--) /* copy characters forward */ + line[i] = line[i - 1]; + case 1: + line[pos] = letter; /* Case 1: overwrite char */ + letter &= 0x7f; + if (isctrl(letter)) horig = curs[0] + 2; + else horig = curs[0] + 1; /* Calculate the new cursor */ + vorig = curs[1]; /* position */ + if (horig > wid - 1) + { horig = horig % wid; + vorig++; + } + break; + case 2: + pos = 0; /* Case 2: show the line */ + horig = curs[0]; + vorig = curs[1]; /* save original values */ + curs[0] = lenprompt; + curs[1] = 0; + } /* Case 3: goto end of line */ + for (c = line[pos]; c; c = line[++pos]) /* write out rest of line */ + { + if (isctrl(c)) /* if it's a control char */ + { mputc('^'); /* print out the ^ and */ + mputc(c | 64); /* the equivalent char for it */ + } + else mputc(c); /* else just show it */ + } + if (flag != 3) + go(horig, vorig); /* h/vorig unused for goend */ + return (pos); /* goend only uses this value */ +} + + +/* I'm not exactly sure what copyback() does yet - Warren */ +#ifdef PROTO +static void copyback(uchar *line, int pos, int count) +#else +static void +copyback(line, pos, count) + uchar *line; + int pos, count; +#endif +{ + uchar c; + int i, horig, vorig, wipe; + +#ifdef DEBUG + fprints(2, "pos %d count %d\n", pos, count); +#endif + if ((i = pos + count) < MAXLL) + { + wipe = 0; + for (horig = pos; horig < i; horig++) + if (isctrl(line[horig])) + wipe += 2; /* calculate amount to blank */ + else + wipe++; + for (; line[i] != EOS; ++i) /* copy line back count chars */ + line[i - count] = line[i]; + for (horig = i - count; i > horig && i < MAXLL + 1; i--) + line[i - 1] = EOS; /* end with nulls */ + } + else + { + write(2, "count passed to copyback is too big\n", 35); + return; + } + horig = curs[0]; + vorig = curs[1]; + for (c = line[pos]; c; c = line[++pos]) + if (isctrl(c)) + { + mputc('^'); + mputc(c | 64); + } + else + mputc(c); +#ifdef DEBUG + fprints(2, "wipe %d\n", wipe); +#endif + for (i = 0; i < wipe; i++) + mputc(' '); /* blank old chars */ + go(horig, vorig); +} + +#define delnextword(line,pos) nextword(line,&pos,0) +#define forword(line,pos) nextword(line,pos,1) +#define yanknext(line,pos) nextword(line,&pos,2) + +#define delprevword(line,pos) prevword(line,pos,0) +#define backword(line,pos) prevword(line,pos,1) + +/* The following two routines each replace three separate ones from old Clam */ + +/* Nextword works on the word after/at the cursor poition, depending on flag: + * + * case 0: Delnextword The word after the cursor is removed + * from the line, and the display is updated. + * case 1: Forword The cursor is moved to the start of the + * next word. + * case 2: Yanknext The word after the cursor is put into yankbuf. + * + * Although pos is passed as a pointer, only forword() updates the value. + */ +#ifdef PROTO +static void nextword(uchar *line, int *p, int flag) +#else +static void +nextword(line, p, flag) + uchar *line; + int *p, flag; +#endif +{ + int inword = 0, l = 1, pos = *p, charcount = 0; + uchar c; + + while (l) + { + if ((c = line[pos]) == EOS) + { l = 0; break; } + if (strchr((char *) wordterm, c) != NULL) /* Found end of a word */ + { charcount++; + pos++; + if (inword) l = 0; + } + else + { inword = 1; + charcount++; + pos++; + } + } + +#ifdef DEBUG + fprints(2, "Deleting %d chars\n", charcount); +#endif + switch (flag) + { + case 0: + copyback(line, *p, charcount); + break; + case 1: + for (; l < charcount; l++) forward(); /* move forward */ + *p = pos; + case 2: + for (pos = *p; l < charcount; l++, pos++) + yankbuf[l] = line[pos]; + yankbuf[l] = EOS; + } +} + +/* Prevword works on the word before the cursor poition, depending on flag: + * + * case 0: Delprevword The word before the cursor is removed + * from the line, and the display is updated. + * case 1: Backword The cursor is moved to the start of the + * previous word. + * case 2: Yankprev The word before the cursor is put into yankbuf. + * + * Although pos is passed as a pointer, only delprevword and + * backword update the value. + */ +int +prevword(line, p, flag) + uchar *line; + int *p, flag; +{ + int inword = 0, l = 1, q, pos = *p, charcount = 0; + + while (l) + { + if (pos == 0) + { l = 0; break; } + if (strchr((char *) wordterm, line[pos - 1]) != NULL) + { /* Found end of a word */ + if (inword) l = 0; + else + { charcount++; pos--; } + } + else + { + inword = 1; + charcount++; + pos--; + } + } +#ifdef DEBUG + fprints(2, "Deleting %d chars\n", charcount); +#endif + + switch (flag) + { + case 0: + for (; l < charcount; l++) backward(); /* move back */ + copyback(line, pos, charcount); /* and copy the line on top */ + *p = pos; + break; + case 1: + for (; l < charcount; l++) backward(); /* move back */ + *p = pos; + break; + case 2: + q = pos; + for (; l < charcount; l++, q++) yankbuf[l] = line[q]; + yankbuf[l] = EOS; + } + return (pos); +} + + +/* Clrline: The line from the position pos is cleared */ +#ifdef PROTO +static void clrline(uchar *line, int pos) +#else +static void +clrline(line, pos) + uchar *line; + int pos; +#endif +{ + extern char *cd; + int i, horig=0, vorig=0; + + if (*cd != EOS) /* If there's a special char for clr */ + { /* then use it */ + addbuf(cd); + for (i = pos; line[i]; i++) line[i] = EOS; +#ifdef DEBUG + fprints(2, "cleared ok\n"); +#endif + } + else horig = curs[0]; /* else we gotta use spaces. */ + vorig = curs[1]; /* Orginal values for curs set. */ + for (i = pos; line[i]; i++) /* Wipe out all those chars */ + { /* with spaces (so slow), */ + mputc(' '); + if (isctrl(line[i])) mputc(' '); + line[i] = EOS; + } + go(horig, vorig); /* restore original curs position */ +} + +/* Transpose transposes the characters at pos and pos-1 */ +#ifdef PROTO +static void transpose(uchar *line, int pos) +#else +static void +transpose(line, pos) + uchar *line; + int pos; +#endif +{ + uchar temp; + + if (isctrl(line[pos - 1])) backward(); + backward(); + temp = line[pos]; + line[pos] = line[pos - 1]; + line[pos - 1] = temp; + if (isctrl(line[pos - 1])) + { mputc('^'); + mputc(line[pos - 1] | 64); + } + else mputc(line[pos - 1]); + if (isctrl(line[pos])) + { mputc('^'); + mputc(line[pos] | 64); + } + else mputc(line[pos]); + if (isctrl(line[pos])) backward(); + backward(); +} + + +/* Getuline gets a line from the user, returning a flag if the line + * should be saved. + */ +bool +getuline(line, nosave) + uchar *line; + int *nosave; +{ + extern char *wbeep, *cl, *cd; + extern uchar bindbuf[], *bindptr, CLEmode; + extern int errno, lenprompt, curr_hist; + uchar a; + int c, times = 1, i, pos = 0, hist = curr_hist, + hsave = lenprompt, vsave = 0, possave = 0; + int beeplength = strlen(wbeep); + + memset(line, 0, MAXLL); + wordterm = (uchar *) EVget("Wordterm"); /* Determine the word + terminators */ + if (wordterm == (uchar *) NULL || *wordterm == EOS) + wordterm = (uchar *) " \t><|/;=&`"; + if (EVget("Msb")) Msb = TRUE; + else Msb = FALSE; + bindptr = bindbuf; /* Set up the pointer to the bind buffer */ + *bindptr = EOS; + + prprompt(); /* Print out our prompt */ + setcbreak(); /* and set the terminal into cbreak mode */ + curs[0] = lenprompt; /* lenprompt global set by prprompt or when + prompt set */ + curs[1] = 0; /* start on line 0 */ + while (1) + { + flushbuf(); /* Ensure user can see the current line */ + c = getcomcmd(); /* Get a character or a command */ + for (; times > 0; times--) + switch (c) + { + case EOF: + fprints(2, "%d\n", errno); + perror("comlined"); + exit(1); + case MARK: + hsave = curs[0]; /* save position (make mark) */ + vsave = curs[1]; + possave = pos; + break; + case START: + go(lenprompt, 0); /* goto start of the line */ + pos = 0; + break; + case BAKCH: + if (pos > 0) /* if not at home, go back */ + { if (isctrl(line[pos - 1])) backward(); + backward(); + pos--; + } + else Beep; /* else ring bell */ + break; + case INT: + pos = goend(line, pos); + addbuf("\n"); + flushbuf(); + setcooked(); + return (FALSE); + case DELCH: + if (line[0] == EOS) /* Leave shell if possible */ + { flushbuf(); + if (EVget("ignoreeof")) + fprints(2, "Use `exit' to exit wish\n"); + else leave_shell(0); /* eof */ + setcooked(); + return (FALSE); /* return if no exit */ + } + else if (line[pos] != EOS) + copyback(line, pos, 1); /* delete char */ + else + complete((char *) line, &pos, FALSE); + break; + case END: + pos = goend(line, pos); /* goto end of the line */ + break; + case FORCH: + if (line[pos] != EOS) /* if not at end, go forward */ + { if (isctrl(line[pos])) forward(); + forward(); + pos++; + } + else Beep; /* else ring bell */ + break; + case KILLALL: + go(lenprompt, 0); /* goto start */ + pos = 0; + clrline(line, 0); /* and kill from pos=0 */ + hist = curr_hist; /* reset hist */ + for (pos = 0; pos < MAXLL; pos++) + line[pos] = EOS; + pos = possave = 0; + break; + case DEL: + case BKSP: + if (pos > 0) /* if not at home, delete */ + { if (isctrl(line[pos - 1])) backward(); + backward(); + copyback(line, --pos, 1); /* move line back on to */ + } /* prev char */ + else Beep; /* else ring bell */ + break; + case COMPLETE: + if (line[0] != EOS) /* try to complete word */ + complete((char *) line, &pos, TRUE); + else Beep; + break; + case NL: + case FINISH: + pos = goend(line, pos); + addbuf("\n"); + flushbuf(); + setcooked(); + line[pos++] = EOS; + *nosave = strip(line);/* process it now */ + if (line[0] != EOS) return (TRUE); + else return (FALSE); +#ifndef NO_HISTORY + case NEXTHIST: + if (hist < curr_hist) /* put next hist in line buf */ + { loadhist((char *) line, ++hist); + goto redisp; /* Yuk, a goto */ + } + else Beep; + break; + case BACKHIST: + if (hist == 1) Beep; + else + { if (hist == curr_hist) /* in line buf */ + (void) savehist((char *) line, 0); + loadhist((char *) line, --hist); + } +#endif + case REDISP: + redisp: + go(lenprompt, 0); + addbuf(cd); /* Clear to end of screen */ + show(line, TRUE); /* Show the line typed so far. */ + pos = goend(line, 0); /* goto end of the line */ + break; + case CLREDISP: + addbuf(cl); /* Clear the screen */ + flushbuf(); + prprompt(); /* Reprint the prompt and */ + show(line, TRUE); /* the line typed so far. */ + break; + case KILLEOL: + clrline(line, pos); /* kill line from cursor on */ + break; + case XON: + continue; /* can't use this */ + case XOFF: + continue; /* can't use this */ + case TRANSPCH: + if (pos > 0 && line[pos] != EOS) /* if not home or at end */ + transpose(line, pos); /* swap current and prev char */ + else Beep; /* else ring bell */ + break; + case QUOTE: + if (pos >= MAXLL) + { Beep; + continue; + } + mputc('"'); + backward(); /* literal char */ + flushbuf(); + read(0, (char *) &a, 1); + c = a; + if (c) /* don't allow EOS (null) */ + insert(line, pos++, c); + break; + case DELWD: + if (pos) delprevword(line, &pos); + else Beep; + break; + case GOMARK: + pos = possave; + go(hsave, vsave); + break; + case YANKLAST: + if (pos != 0) /* if not at home */ + yankprev(line, pos);/* yank previous word */ + else Beep; /* else ring bell */ + break; + case SUSP: + continue; + case BAKWD: + if (pos > 0) + backword(line, &pos); + else Beep; + break; + case DELWDF: + if (line[pos] != EOS) + delnextword(line, pos); + else Beep; + break; + case FORWD: + if (line[pos] != EOS) + forword(line, &pos); + else Beep; + break; + case PUT: + if (pos > MAXLL - strlen((char *) yankbuf)) + { Beep; + break; + } + for (i = 0; yankbuf[i]; i++) /* insert yank buffer */ + insert(line, pos++, yankbuf[i]); + break; + case YANKNEXT: + if (line[pos] != EOS) /* if not at end */ + yanknext(line, pos);/* yank next word */ + else Beep; /* else ring bell */ + break; + case SEARCHF: + if (line[pos]) /* search forward */ + { read(0, (char *) &a, 1); + c = a; /* char to search for */ + for (i = pos + 1; line[i] && c != line[i]; i++); + if (line[i]) + while (pos < i) + { pos++; + if (isctrl(line[pos - 1])) forward(); + forward(); + } + else Beep; /* not found */ + } + else Beep; /* at end of line */ + break; + case SEARCHB: + if (pos > 0) /* search backwards */ + { read(0, (char *) &a, 1); + c = a; /* char to search for */ + for (i = pos - 1; i >= 0 && c != line[i]; i--); + if (i >= 0) + while (pos > i) + { pos--; + if (isctrl(line[pos])) backward(); + backward(); + } + else Beep; /* not found */ + } + else Beep; /* at end of line */ + break; + case MODEON: + CLEmode = 1; /* Turn the CLE mode on */ + break; + case MODEOFF: + CLEmode = 0; /* Turn the CLE mode off */ + break; + case BEEP: + Beep; /* Simply beep */ + break; + case QUOTEUP: + if (pos >= MAXLL) /* Turn on next char's msb */ + { Beep; + continue; + } + mputc('"'); + backward(); /* literal char */ + flushbuf(); + read(0, (char *) &a, 1); + c = a; + if (c) /* don't allow EOS (null) */ + { c |= 0x80; + insert(line, pos++, c); + } + break; + default: + if (pos >= MAXLL - 1) + { Beep; + break; + } + insert(line, pos++, c); + break; + } + times = 1; + } +} +#else /* NO_COMLINED */ +bool +getuline(line, nosave) + uchar *line; + int *nosave; +{ + int i; + + prprompt(); /* Print out our prompt */ + for (i=0; inewfd[i]) + { + close(i); /* Close the one we are using */ + /* and dup back the old one */ + if (dup2(d->newfd[i], i) == -1) + { + fprints(2, "Can't undup fd %d\n", i); + } + close(d->newfd[i]); + } + } + /* Now pull the dup object off the stack */ + if (!nd) + duptop = d->prev; + free(d); +} + +/* + * Redirect redirects the file descriptors for fds 0 to 2 for the + * current process; for each fd it also takes a bitmap indicating whether + * to append to the output file, or to open from an input file. + * If an error occurs, we return -1, else 0. We only push a dupstack + * if no error occurs, so we must not call undirect afterwards. + */ +#ifdef PROTO +static int redirect(struct rdrct newfd[3]) +#else +static int +redirect(newfd) + struct rdrct newfd[3]; +#endif +{ + int i, j, appnd; + int mode, flags; + struct dupstack *d; + +#ifdef DEBUG + fprints(2, "Redirect...\n"); +#endif + + + d = (struct dupstack *) Malloc(sizeof(struct dupstack), "redirect"); + + /* Clear all the fields */ + d->newfd[0] = d->newfd[1] = d->newfd[2] = 0; + + for (i = 0; i < 3; i++) /* Do each fd in turn */ + { + /* Not this one */ + if (newfd[i].fd < 3 && newfd[i].file == NULL) + continue; + appnd = newfd[i].how & H_APPEND; + + + j = dup(i); /* Copy that file desc */ + if (j == -1) + { + fprints(2, "Couldn't dup fd %d\n", i); + undirect(d); + return (-1); + } + else + d->newfd[i] = j; + + if (newfd[i].fd > 2) /* Fd is already open */ + { + close(i); + if (dup2(newfd[i].fd, i) == -1) + { + fprints(2, "Could not dup2(%d,%d)\n", newfd[i].fd, i); + undirect(d); + return (-1); + } +#ifdef DEBUG + fprints(2, "Dup'd %d to %d\n", newfd[i].fd, i); +#endif + close(newfd[i].fd); + } + /* Open this file */ + else + { + close(i); + if (newfd[i].how & H_FROMFILE) /* for reading */ + { + flags = O_RDONLY; + if (open(newfd[i].file, flags) == -1) + { + fprints(2, "Can't open %s\n", newfd[i].file); + undirect(d); + return (-1); + } + } + else + /* for writing */ + { + flags = O_WRONLY | O_CREAT; + if (!appnd) + flags |= O_TRUNC; + else + flags |= O_APPEND; + mode = 0666; + if (open(newfd[i].file, flags, mode) == -1) + { + fprints(2, "Can't open %s\n", newfd[i].file); + undirect(d); + return (-1); + } +#ifdef DEBUG + fprints(2, "Opened %s as %d\n", newfd[i].file, i); +#endif + } + } + } + /* Now put the old fd's on the stack */ + d->prev = duptop; + duptop = d; + return (0); +} + +#ifndef NO_ALIAS +#define MAXAL 16 +static char *curr_alias[MAXAL]; /* The list of aliases we have done */ +static int adepth = 0; /* Our current depth */ + +/* Runalias checks to see if there is an alias, and then runs it. + * It either returns the exit status of the alias, or -1 if there + * was no alias. The only bit in how we are interested in is + * H_BCKGND. This gets passed onto doline to indicate how to waitfor() + * the child. + */ +#ifdef PROTO +static int runalias(int argc, char *argv[], int how) +#else +static int +runalias(argc, argv, how) + int argc; + char *argv[]; + int how; +#endif +{ + extern int Argc, saveh, Exitstatus; + extern char **Argv; +#ifdef PROTO + extern bool(*getaline) (uchar *line , int *nosave ); + bool(*oldgetline) (uchar *line , int *nosave ); +#else + extern bool(*getaline) (); + bool(*oldgetline) (); +#endif + + int i, oldsaveh, oldargc; + char **oldargv; + + /* Check for already used alias */ + for (i = 0; i < adepth; i++) + if (!strcmp(curr_alias[i], argv[0])) + return (-1); + + if (checkalias(argv[0]) != NULL) /* Find an alias */ + { + oldargc = Argc; + oldargv = Argv; + Argc = argc; + Argv = argv; + oldsaveh = saveh; + oldgetline = getaline; + if (adepth == MAXAL) + { + fprints(2, "Aliases nested too deep\n"); + /* exit(1); */ + return (-1); + } + curr_alias[adepth++] = argv[0]; +#ifdef DEBUG + fprints(11, "About to send the alias through doline()\n"); +#endif + saveh = FALSE; + how= (how & H_BCKGND) | TRUE; + getaline = getaliasline; + doline(how); + adepth--; + Argc = oldargc; + Argv = oldargv; + getaline = oldgetline; + saveh = oldsaveh; + return (Exitstatus); + } + else + return (-1); +} +#endif /* NO_ALIAS */ + + +static int bgpgrp; /* Background process group id */ + +/* Invoke simple command. This takes the args to pass to the executed + * process/builtin, the list of new file descriptors, and a bitmap + * indicating how to run the process. We only use the background bits + * at the moment. + */ + +int +invoke(argc, argv, newfd, how, anydups) + int argc, how; + char *argv[]; +struct rdrct newfd[]; +int anydups; + +{ + extern int saveh, Exitstatus; + int pid, i; + +#ifdef DEBUG + if (argv[0]) + fprints(2, "Invoking %s\n", argv[0]); +#endif + /* Firstly redirect the input/output */ + if (anydups) + { + i = redirect(newfd); + if (i == -1) + return (0); + } + + if (!(how & H_BCKGND)) /* If a foreground process */ + { + bgpgrp=0; /* Reset for next bg process */ + /* Try builtins first */ + if (argc == 0 || ((i = builtin(argc, argv, &pid)) != -1)) + { + Exitstatus = i; + if (anydups) + undirect(NULL); + return (pid); + } + +#ifndef NO_ALIAS + if ((i = runalias(argc, argv, how)) >= 0) + { Exitstatus= i; + if (anydups) + undirect(NULL); + return (0); + } +#endif + } + + /* Else fork/exec the process */ + switch (pid = fork()) + { + case -1: + fprints(2, "Can't create new process\n"); + if (anydups) + undirect(NULL); + return (0); + /* Restore signals to normal if fg */ + case 0: + saveh = FALSE; + dflsig(); +#ifdef V7JOB + ptrace(0,0,(PLONG)0,(PLONG)0); /* Enable tracing on the child */ +#endif + if (how & H_BCKGND) + { /* Move process to new proc-grp if bg */ +#if defined(UCBJOB) || defined(POSIXJOB) +#ifdef POSIXJOB + /* First get a pgrp-id if none */ + if (bgpgrp==0) bgpgrp=getpid(); + i = setpgid(0, bgpgrp); + if (i == -1) + fprints(2, "I was -1\n"); +#else + i = setpgrp(0, getpid()); + if (i == -1) + fprints(2, "I was -1\n"); +#endif /* POSIXJOB */ +#endif /* UCBJOB || POSIXJOB */ + } +#ifndef NO_VAR + if (!EVupdate()) + fatal("Can't update environment"); +#endif + /* The fork only wants fds 0,1,2 */ +#ifdef DEBUG + fprints(2, "The child is closing 3 to 20\n"); +#endif + for (i = 3; i < 20; i++) + close(i); + + /* Try builtins first */ + if (argc == 0 || ((i = builtin(argc, argv, &pid)) != -1)) + { + exit(i); + } + +#ifndef NO_ALIAS + if ((i = runalias(argc, argv, how)) >= 0) + { + if (anydups) + undirect(NULL); + exit(i); + } +#endif + /* Finally exec() the beast */ + + execvp(argv[0], argv); + /* Failed, exit the child */ + fprints(2, "Can't execute %s\n", argv[0]); + exit(1); + + default: +#ifdef DEBUG + fprints(2, "Just forked %s, pid %d\n", argv[0], pid); +#endif + /* and return the new pid */ +#ifndef NO_JOB + addjob(pid, argv[0], how & H_BCKGND); +#endif +#ifdef V7JOB +/* We can't just leave a backgrounded process as is, because it will + * stop on the exec(), and because we won't waitfor() it, it will stay + * like that. So we wake it up here. + */ + if (how & H_BCKGND) + ptrace(7,pid,(PLONG)1,(PLONG)0); +#endif + if (anydups) + undirect(NULL); + return (pid); + } +} diff --git a/Applications/wish/file.c b/Applications/wish/file.c new file mode 100644 index 0000000000..8b6aa9db5f --- /dev/null +++ b/Applications/wish/file.c @@ -0,0 +1,129 @@ +/* File does the work for opening & reading lines from files. We keep a + * stack of open fds and buffers for each file. This is to minimise the + * overhead or read()ing, but avoiding the use of stdio. + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +static int currfile = 0; +static int filedesc[10], bufbytes[10]; +static char *bufptr[10], *filebuf[10]; + + +/* Fileopen opens the named file read-only, and sets the number of bytes + * buffered in yankbuf to zero. NOTE if filename==NULL, we assume that + * fd has already been open. + */ +bool +fileopen(filename, fd) + char *filename; + int fd; +{ + if (currfile == 9) return (FALSE); /* No space on stack left */ + currfile++; + if (filename) + { if ((filedesc[currfile] = open(filename, O_RDONLY, 0)) == -1) + { currfile--; fprints(2, "Can't open %s\n", filename); return (FALSE); } + } + else + filedesc[currfile] = fd; + + filebuf[currfile] = (char *) malloc(MAXLL); + if (filebuf[currfile] == NULL) + { close(filedesc[currfile]); currfile--; + fprints(2, "Can't malloc file buffer\n"); return (FALSE); + } + bufptr[currfile] = filebuf[currfile]; + bufbytes[currfile] = 0; + return (TRUE); +} + +void +fileclose() +{ + free(filebuf[currfile]); + close(filedesc[currfile]); + currfile--; +} + +/* Getfileline obtains a line from the opened file. + */ +bool +getfileline(line, nosave) + uchar *line; + int *nosave; +{ + int in_line = 0; + + *nosave = 0; + while (1) /* Until we have a line, copy stuff */ + { /* We've already got some to copy */ + if (!in_line) + { + for (; bufbytes[currfile] && (*bufptr[currfile] == ' ' || + *bufptr[currfile] == '\t'); bufbytes[currfile]--, bufptr[currfile]++); + in_line = 0; + } + if (bufbytes[currfile] == 0) + { in_line = 1; goto doread; } /* Yuk, a goto */ + + if (*bufptr[currfile] == '#') *nosave = 1; + for (; bufbytes[currfile] && *bufptr[currfile] != '\n'; bufbytes[currfile]--) + *line++ = *bufptr[currfile]++; + + if (bufbytes[currfile]) /* We hit a \n, so return */ + { *line = EOS; bufptr[currfile]++; + bufbytes[currfile]--; return (TRUE); + } + +doread: + bufptr[currfile] = filebuf[currfile]; + /* Get more characters */ + bufbytes[currfile] = read(filedesc[currfile], filebuf[currfile], MAXLL); + if (bufbytes[currfile] < 1) return (FALSE); /* End of file */ + } +} + +int +source(argc, argv) + int argc; + char *argv[]; +{ + extern int saveh; +#ifdef PROTO + extern bool(*getaline) (uchar *line , int *nosave ); + bool(*oldgetline) (uchar *line , int *nosave ); +#else + extern bool(*getaline) (); + bool(*oldgetline) (); +#endif + + int oldsaveh; + + if (argc != 2) + { prints("Usage: source file\n"); return (1); } + +#ifdef DEBUG + prints("About to fileopen %s\n", argv[1]); +#endif + + if (fileopen(argv[1], 0) == FALSE) + { prints("Unable to source file %s\n", argv[1]); return (1); } + else + { +#ifdef DEBUG + prints("About to send the file through doline()\n"); +#endif + oldsaveh = saveh; + saveh = FALSE; + oldgetline = getaline; + getaline = getfileline; + doline(FALSE); + getaline = oldgetline; + saveh = oldsaveh; + } + fileclose(); + return (0); +} diff --git a/Applications/wish/fuzix-wish.pkg b/Applications/wish/fuzix-wish.pkg new file mode 100644 index 0000000000..f5561bcd8e --- /dev/null +++ b/Applications/wish/fuzix-wish.pkg @@ -0,0 +1,7 @@ +package wish +if-file wish + +f 0755 /usr/bin/wish wish +f 0655 /usr/man/man1/wish.1 Wish.1 +f 0644 /root/.wishrc Wishrc + diff --git a/Applications/wish/global.c b/Applications/wish/global.c new file mode 100644 index 0000000000..2b42c0960f --- /dev/null +++ b/Applications/wish/global.c @@ -0,0 +1,3 @@ +#include "header.h" + +char currdir[128]; diff --git a/Applications/wish/header.h b/Applications/wish/header.h new file mode 100644 index 0000000000..2739cc7938 --- /dev/null +++ b/Applications/wish/header.h @@ -0,0 +1,206 @@ +/* Definitions of all the various names and macros that we need to + * compile Wish. + * + * $Revision: 41.3 $ $Date: 2003/04/21 13:08:43 $ + */ + +#include "machine.h" + +/* These defines turn off some of the capabilities + * of the shell. They are mainly used for finding + * the location of bugs. + */ +#undef NO_ALIAS /* No alias functions */ +#undef NO_JOB /* No job functions */ +#undef NO_HISTORY /* No history stuff */ +#undef NO_TILDE /* No tilde stuff */ +#undef NO_VAR /* No variables */ +#undef NO_COMLINED /* No command line editor */ +#ifdef NO_COMLINED +# define NO_CLEX /* No command line extensions */ +# define NO_BIND /* No key bindings */ +#endif + +#if defined(__STDC__) && __STDC__ && !defined(MINIX1_5) +# define PROTO +# define CONST const +# ifndef __GNUC__ +# include +# endif +#else +# define CONST +#endif + +#ifndef NULL /* Generic null pointer. May need */ +# define NULL 0 /* changing on some machines to */ +#endif /* (char *)0 or (void *)0 */ + +#ifndef SIGTYPE /* Signal handlers return this */ +# define SIGTYPE void +#endif + +#undef EOF /* Minix defines this in stdio.h */ +#define EOF -1 + +#ifndef MAXSIG +# define MAXSIG 27 /* Maximum signals known to Wish */ +#endif + +#define UNDEF -1 /* Used when setting cbreak etc. */ +#define EOS '\0' /* End of string terminator */ +#define BADFD -2 +#define MAXARG 300 /* Max # args passed to processes */ +#define MAXWORD 200 /* Only used in parse, try to lose */ +#define MAXFNAME 200 /* Only used in parse, try to lose */ +#define MAXPL 512 /* Path length */ +#define MAXLL 2048 /* Used in comlined & hist */ +#define MAXWL 512 /* Used in clex & meta */ +#define MAXCAN 1000 /* maximum number of candidates */ + + +/* The following used to be enums, + * but they get used an awful lot + * with ints. + */ +#undef TRUE +#undef FALSE +#define TRUE 1 +#define FALSE 0 +#if (!defined(FREEBSD_4) && !defined(FREEBSD_5)) +typedef int bool; /* Must be int as we pass bools as f'n args */ +#endif +typedef unsigned char uchar; /* CLE uses unsigned chars throughout */ + +/* We use the following when debugging malloc/free */ +#ifdef MALLOCDEBUG +# define free myFree +# define malloc myMalloc +#endif + +#define fatal(mess) { fprints(2,"%s\n",mess); exit(1); } + + +/* + * These structures are used to store the definitions for aliases, history, + * the tilde lists, variables. + */ + +struct val { + char *name; /* The name of the var/history/tilde/alias */ + char *val; /* The value */ + int hnum; /* History only: the history number */ + bool exported; /* Variable only: is this exported? */ + struct val *next; +}; + +struct vallist { + struct val *head; /* Singly-linked list with head/tail */ + struct val *tail; +}; + +/* Clex and Meta + * + * The following structure is used by both clex.c and meta.c. Meta + * uses it to hold candidates files that matched a ^D or expression. + * Here the mode holds the mode of each file as obtained by stat(). + * Clex uses the struct to build a linked list of words that will + * eventually be parsed by the parser. Here, the mode is a complex bitfield. + * For normal words,the mode is used as a bitfield to indicate: + * - if the name has been malloc'd (mode&TRUE) + * - if the name has an invisible space at the end (mode&C_SPACE) + * - if the name is in single quotes (mode&C_QUOTE) + * - if the name is in backquotes (mode&C_BACKQUOTE) + * - if the name is in curly brackets (mode&C_CURLY) + * - if the name starts with a $ sign (mode&C_DOLLAR) + * Often there will be nodes in the list with mode==0 & name==NULL, these + * indicate words removed during meta and should be skipped by the parser. + */ + +#define C_SPACE 002 /* Word has a space on the end */ +#define C_DOLLAR 004 /* Word starts with a $ sign */ +#define C_QUOTE 010 /* Word in single quotes */ +/* #define C_DBLQUOTE 020 * Word in double quotes */ +#define C_CURLY 020 /* Word in curly braces {} */ +#define C_BACKQUOTE 040 /* Word in backquotes */ +/* #define C_QUOTEBITS 060 * Dbl and back quote bits */ + +/* Some words have name==NULL & mode!=0; these are special words for the + * parser. They are separate, non-malloc'd, non-quoted words, and the bits + * used above can be reused. The bits must be in C_WORDMASK + */ +#define C_WORDMASK 01700 /* Bits must fall in here */ + +#define C_SEMI 0100 /* The word is a semicolon */ +#define C_DOUBLE C_SEMI /* Add this to `double' the symbol */ +#define C_PIPE 0200 /* The word is a pipe */ +#define C_DBLPIPE 0300 /* The word is a double pipe */ +#define C_AMP 0400 /* The word is an ampersand */ +#define C_DBLAMP 0500 /* The word is a double ampersand */ +#define C_LT 0600 /* The word is a less-than. */ +#define C_LTLT 0700 /* The word is two less-thans. */ +#define C_FD 03 /* File descriptor bits */ +#define C_GT 01000 /* The word is a greater-than. Bits */ + /* in the mask C_FD hold the fd (1-2) */ +#define C_GTGT 01100 /* The word is two greater-thans.Bits */ + /* in the mask C_FD hold the fd (0-9) */ +struct candidate + { char *name; /* The file's name */ + struct candidate *next; /* Next field in linked list */ + int mode; /* File's mode, or malloc'd bool */ + }; + +/* Execution + * + * The how parameter to execute() indicates how the process should be + * executed. + */ + +#define H_BCKGND 002 /* Process running in background */ +#define H_FROMFILE 004 /* Process has input from file */ +#define H_APPEND 010 /* Process is appending output */ + +/* Redirection + * + * The rdrct structure holds the new file descriptors for the process, + * or their file names (if any). Redirect() gets 3 of these, and + * for (i=0 to 2) { if name not null, open(name); else if fd not 0, + * dup2(fd,i); } + * The how field can have the H_ bits described above. + */ + +struct rdrct { + int fd; /* The input file descriptor */ + int how; /* How to open up the file */ + char *file; /* Input file's name */ + }; + +/* Command Line Editing + * + * Several old routines have been subsumed by one routine, Show(). + * Unfortunately, these are used in comlined, clex and hist, so the + * defines that map the old onto the new have to be out here. + */ + +#define insert(a,b,c) (void)Show(a,b,c,0) +#define show(a,c) (void)Show(a,0,0,2) +#define goend(a,b) Show(a,b,0,3) +#define yankprev(line,pos) prevword(line,&pos,2) +#define Beep write(1,wbeep,beeplength) + + +/* Builtins + * + * The following structure holds the builtins. This is only used by + * builtin.c and clex.c. Pity we can't leave it in builtin.c + */ + +struct builptr { + char *name; +#ifdef PROTO + int (*fptr)(int argc, char *argv[]); +#else + int (*fptr)(); +#endif + }; + +#include "proto.h" diff --git a/Applications/wish/hist.c b/Applications/wish/hist.c new file mode 100644 index 0000000000..56efd069f5 --- /dev/null +++ b/Applications/wish/hist.c @@ -0,0 +1,151 @@ +/* The command history is stored as a singly linked list, each node holding + * a command, its history number, and a pointer to the next node. + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +#ifndef NO_HISTORY +static struct vallist hlist = /* The history list */ + { NULL, NULL}; +int curr_hist = 1; /* The current history number */ +static int maxhist = 25; /* Maximum # of saved lines */ +static int curr_saved = 0; /* How many we have saved currently */ +static bool nohistdup = TRUE; /* Do we omit duplicate lines? */ + + +/* Savehist saves the given line into the history with the given history + * number, and ensuring that there are no more than max histories. + * It returns 1 if saved, or 0 if a duplicate or other errors. + */ +int +savehist(line, andadd) + char *line; + bool andadd; +{ + struct val *v; + char *mh; + + mh = EVget("history"); /* Set the current history */ + if (mh) maxhist = atoi(mh); + + /* Adjust the number saved if necessary */ + while (curr_saved > maxhist) { pullval(&hlist); curr_saved--; } + + /* If we don't care about duplicates, or there */ + /* isn't a duplicate, save the history */ + if (!nohistdup || ((v = searchval(&hlist, line, TRUE, FALSE)) == NULL)) + { + v = (struct val *) Malloc(sizeof(struct val), "savehist malloc"); + v->name = Malloc(strlen(line) + 1, "savehist malloc"); + strcpy(v->name, line); v->hnum = curr_hist++; appendval(&hlist, v); + if (++curr_saved > maxhist) { pullval(&hlist); curr_saved--; } + } + return (1); +} + +/* Loadhist finds the command line with the given histnum, and loads it + * at the given pos into the command line. + */ +void +loadhist(line, histnum) + char *line; + int histnum; +{ + struct val *ptr; + + /* Find either the last hist or the histnum one */ + for (ptr = hlist.head; ptr && ptr->hnum != histnum; ptr = ptr->next); + if (ptr) strcpy(line, ptr->name); /* copy into the command line */ + /* else we can't find that number so load */ + /* nothing. Maybe it should beep here */ +} + +/* The history builtin prints out the current history list. + * None of the arguments are used. + */ +int +history(argc, argv) + int argc; + char *argv[]; +{ + struct val *ptr; + + for (ptr = hlist.head; ptr && ptr->hnum < curr_hist; ptr = ptr->next) + if (ptr->hnum >= curr_hist - maxhist) + { prints("%d ", ptr->hnum); + mprint((uchar *) ptr->name, 0); /* this routine intercepts the ^ chars */ + } + return (0); +} + +/* Getnumhist returns the command line with the given history number. + * If none exists, NULL is returned. + */ +#ifdef PROTO +static char * getnumhist(int histnum) +#else +static char * +getnumhist(histnum) + int histnum; +#endif +{ + struct val *ptr; + + for (ptr = hlist.head; ptr; ptr = ptr->next) + if (ptr->hnum == histnum) return (ptr->name); + return (NULL); +} + +/* Gethist takes a event string, and returns a match from + * the history. The event can be one of the following: + * Value Return value + * an integer The command line with the given histnum + * "!" The last command line + * -integer The line integer histories ago + * word The last line that strncmps the word + * + * If no event is found, NULL is returned. + */ +char * +gethist(event) + char *event; +{ + struct val *ptr, *this; + int histnum, len; + + if ((*event >= '0' && *event <= '9') || (*event == '-') || (*event == '!')) + { + switch (*event) + { + case '-': + histnum = curr_hist - atoi(event + 1); + break; + case '!': + histnum = curr_hist - 1; + break; + default: + histnum = atoi(event); + } +#ifdef DEBUG + prints("Histnum is %d\n", histnum); +#endif + return (getnumhist(histnum)); + } + else + { + /* + If the history matches the requested event then that history line is + copied into the event pointer. Note that the first histnum characters of + event will not change and thus further searches for more recently + matching histories will still be valid. + */ + len= strlen(event); + for (ptr=NULL, this = hlist.head; this; this = this->next) + if (!strncmp(this->name, event, len)) ptr=this; + + if (ptr) return (ptr->name); else return(NULL); + } +} +#endif /* NO_HISTORY */ diff --git a/Applications/wish/job.c b/Applications/wish/job.c new file mode 100644 index 0000000000..b97007a5d2 --- /dev/null +++ b/Applications/wish/job.c @@ -0,0 +1,530 @@ +/* Job control. If UCBJOB is defined, we use BSD4.x job control. If POSIXJOB + * is defined, we use POSIX job control. IF V7JOB is defined, we provide + * job control using ptrace(). If none are defined, we simulate job control + * as best we can. + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +/* We define the wait return status macros for SysV. We also define + * WIFCORE, which evaluates as true if WIFSIGNALED is TRUE and + * a core has been dumped. This only works under SysV and BSD. + * Also defined are WRUNFG and WRUNBG, which are true is the + * process is running in the fore or background. + */ +#define RUNFG -1 /* Status if running in fg */ +#define RUNBG -2 /* Status if running in bg */ + +#define WAIT_T int +#define STATUS status +#ifdef UCBJOB +# undef WAIT_T +# undef STATUS +# define WAIT_T union wait +# define STATUS status.w_status +#endif + + +/* The job structure hold the information needed to manipulate the + * jobs, using job numbers instead of pids. Note that the linked + * list used by Wish is ordered by jobnumber. + */ +struct job +{ + int jobnumber; /* The job number */ + int pid; /* The pid of the job */ + char *name; /* Job's argv[0]; */ + char *dir; /* The job's working directory */ + WAIT_T status; /* Job's status */ + bool changed; /* Changed since last CLE? */ + struct job *next; /* Pointer to next job */ +}; + +static struct job *jtop = NULL, /* The head of the linked list */ + *currentjob = NULL; /* Pointer to current job structure */ + +int Exitstatus = 0; /* The exit status of the last child */ +static bool donepipe = TRUE; /* Addjob makes a new currentjob if this is */ + /* TRUE, then sets it FALSE. Joblist sets */ + /* it TRUE again. */ + +#ifdef PROTO +# define P(s) s +#else +# define P(s) () +#endif + +#if defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) +static struct job *findjob P((int pid )); +static void rmjob P((struct job *ptr )); +#endif +#undef P + +#ifdef POSIXJOB +# include "posixjob.c" +#endif + +#ifdef UCBJOB +# include "ucbjob.c" +#endif + +#ifdef V7JOB +# include "v7job.c" +#endif + +#if defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) +/* Given a process id, return a pointer to it's job structure */ +static struct job * +findjob(pid) + int pid; +{ + struct job *ptr; + + for (ptr = jtop; ptr; ptr = ptr->next) + if (ptr->pid == pid) + return (ptr); + return (NULL); +} +#endif + + +/* Given a job number, return it's process id */ +#ifdef PROTO +static int pidfromjob(int jobno) +#else +static int +pidfromjob(jobno) + int jobno; +#endif +{ + struct job *ptr; + + for (ptr = jtop; ptr; ptr = ptr->next) + if (ptr->jobnumber == jobno) + return (ptr->pid); + return (-1); +} + + +/* Add the pid and it's argv[0] to the job list. Return the allocated + * job number. + */ +int +addjob(pid, name, isbg) + int pid, isbg; + char *name; +{ + extern char currdir[]; + int jobno, last = 0; + struct job *ptr, *old, *new; + +#ifndef V7JOB + if (currentjob && donepipe == FALSE) /* We already have a current job */ + { +#ifdef DEBUG + fprints(2, "I already got one, you sons of a donkey\n"); +#endif + return (0); + } +#endif + /* Build the structure */ + ptr = old = (struct job *) malloc((unsigned) sizeof(struct job)); + if (ptr == NULL) + { + perror("addjob"); + return (0); + } + ptr->pid = pid; + ptr->name = (char *) malloc((unsigned) (strlen(name) + 1)); + if (ptr->name) + (void) strcpy(ptr->name, name); + else + { + perror("addjob"); + return (0); + } + /* We need some way of indicating the child */ + /* is still running. I am using RUNxx for now. */ + if (isbg) + ptr->STATUS = RUNBG; + else + ptr->STATUS = RUNFG; + + ptr->dir = (char *) malloc((unsigned) (strlen(currdir) + 1)); + if (ptr->dir) + (void) strcpy(ptr->dir, currdir); + else + { + perror("addjob"); + return (0); + } + ptr->changed = FALSE; + + /* Find the end of list */ + if (jtop == NULL) + { + jobno = ptr->jobnumber = 1; + jtop = ptr; ptr->next = NULL; + } + else + { /* Find a gap to put the node in */ + for (last = 0, old = new = jtop; + new != NULL && new->jobnumber - last == 1; + old = new, last = new->jobnumber, new = new->next); + last++; + if (new == NULL) + { + old->next = ptr; + ptr->next = NULL; + } + else + { + ptr->next = new; + old->next = ptr; + } + jobno = ptr->jobnumber = last; + } + + currentjob = ptr; /* a new current job */ + donepipe = FALSE; +#ifdef DEBUG + fprints(2, "added jobno %d pid %d\n", jobno, pid); +#endif + return (jobno); +} + +#if defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) +/* Remove the job from the job list */ +static void +rmjob(ptr) + struct job *ptr; +{ + struct job *prev; + + if (ptr==NULL) return; +#ifdef DEBUG + fprints(2, "removing pid %d from list\n", ptr->pid); +#endif + if (ptr==jtop) jtop=jtop->next; + else + { + for (prev=jtop;prev && prev->next!=ptr; prev=prev->next) + ; + if (prev==NULL) return; + prev->next= ptr->next; + } + free(ptr->name); + if (ptr == currentjob) + { + currentjob = NULL; + Exitstatus = (WIFEXITED(ptr->status)) ? WEXITSTATUS(ptr->status) : 1; + } + free(ptr); +} +#endif + +/* Print out the list of current jobs and their status. + * Note although this is a builtin, it is called from + * main with an argc value of 0, to show new jobs only. + */ +int +joblist(argc, argv) + int argc; + char *argv[]; +{ +#ifndef FUZIX + struct job *ptr, *next; + bool exitzero; + + if (argc > 1) + { + prints("Usage: jobs\n"); + return (1); + } + +#if defined(UCBJOB) || defined(POSIXJOB) + waitfor(0); /* Collect any jobs doline() might have missed */ +#endif + + for (ptr = jtop; ptr; ptr = ptr->next) + { +/* Printing out conditions are tricky. We print if we're being called as + * a builtin (when argc!=0); otherwise only if the status has changed, + * and it wasn't the current job or exited with an exit status of 0. + * Also don't print if it's RUNFG or RUNBG. + */ + exitzero = (WIFEXITED(ptr->status) && (WEXITSTATUS(ptr->status) == 0)); + if (argc || (ptr->changed && !WRUNFG(ptr->status) && !WRUNBG(ptr->status) + && ptr != currentjob && !exitzero)) + { + if (currentjob && currentjob->pid == ptr->pid) + prints(" + [%d] %d", ptr->jobnumber, ptr->pid); + else + prints(" [%d] %d", ptr->jobnumber, ptr->pid); + prints(" %s ", ptr->name); + /* Still running */ + if (WRUNFG(ptr->status) || WRUNBG(ptr->status)) + { + prints("\n"); + continue; + } + if (WIFEXITED(ptr->status)) + { + prints(" Exited %d\n", WEXITSTATUS(ptr->status)); + continue; + } + if (WIFSTOPPED(ptr->status)) + { + prints(" Stopped %s\n", siglist[WSTOPSIG(ptr->status)]); + continue; + } + if (WIFSIGNALED(ptr->status)) + { + prints(" %s", siglist[WTERMSIG(ptr->status)]); + if (WIFCORE(ptr->status)) + prints(" (core dumped)\n"); + else + prints("\n"); + continue; + } + } + } + for (ptr = jtop; ptr; ptr = next) + { + next= ptr->next; + if (WRUNFG(ptr->status) || WRUNBG(ptr->status)|| WIFSTOPPED(ptr->status)) + ptr->changed = FALSE; + else + rmjob(ptr); + } + donepipe = TRUE; /* It must be, if doline() is calling us */ +#endif + return (0); +} + +int +Kill(argc, argv) + int argc; + char *argv[]; + +{ + int pid, sig = 0; + char *jobname, *sigwd; +#ifdef V7JOB + struct job *job; +#endif + + switch (argc) + { + case 2: + sig = 9; + jobname = argv[1]; + goto killit; /* Yuk, a goto */ + case 3: + jobname = argv[2]; + sigwd = argv[1]; + if (*sigwd == '-') + sigwd++; + sig = atoi(sigwd); +#ifdef FUZIX + goto killit; /* Yuk, a goto */ +#else + if (sig == 0) + for (i = 1; signame[i]; i++) + if (!strcmp(sigwd, signame[i])) + { + sig = i; + break; + } +#endif + killit: + if (sig == 0) + { + fprints(2,"Bad signal argument\n"); + return (1); + } + if (*jobname == '%') + pid = pidfromjob(atoi(++jobname)); + else + pid = atoi(jobname); + if (pid < 1) + { + fprints(2, "No such job number: %s\n", jobname); + return (1); + } +#ifdef V7JOB +/* A stopped job can't be killed until the parent (i.e us) restarts it. + * So if the job is stopped, we restart it so we can kill it :-) + */ + job= findjob(pid); + if (job && WIFSTOPPED(job->status)) + { +# ifdef DEBUG + fprints(2,"About to restart ptrace the job\n"); +# endif + ptrace(7,pid,(PLONG)1,(PLONG)0); /* Start it up again */ + } +#endif + kill(pid, sig); + return (0); + default: +#ifdef FUZIX + prints("Usage: kill [-]number job|pid\n"); +#else + prints("Usage: kill [-]signame|number job|pid\n"); + prints(" Signal names are:\n"); + for (i = 1; signame[i]; i++) + { + if (i % 6 == 1) + prints(" "); + prints("%s ", signame[i]); + if (i % 6 == 0) + prints("\n"); + } + prints("\n"); +#endif + return (1); + } +} + +#ifdef FUZIX +void +waitfor(pid) + int pid; +{ + int status; + int wpid; + do { + wpid = wait(&status); + } while (wpid != pid); +} +#endif + + +#if defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) +/* Builtins */ +int +bg(argc, argv) + int argc; + char *argv[]; + +{ + int pid=0; + char *c=NULL; + + if (argc > 2) + { + prints("usage: bg [pid]\n"); + return (1); + } + if (argc == 1) + { +#ifdef DEBUG + fprints(2,"Trying to use current job pointer\n"); +#endif + if (currentjob) + { + pid = currentjob->pid; + } + } + else + { + c = argv[1]; + if (*c == '%') + pid = pidfromjob(atoi(++c)); + else + pid = atoi(c); + } + if (pid < 1) + { + fprints(2, "No such job number: %s\n", c); + return (1); + } + bgstuff(pid); + + return (0); +} + + +/* Fg is a special builtin. Instead of returning an exitstatus, it returns + * the pid of the fg'd process. Builtin() knows about this. + */ +int +fg(argc, argv) + int argc; + char *argv[]; + +{ + extern char currdir[]; + extern struct vallist vlist; + int pid; + struct job *ptr; + char *c; + + if (argc > 2) + { + prints("usage: fg [pid]\n"); + return (0); + } + if (argc == 1) + { +#ifdef DEBUG + fprints(2,"Trying to use current job pointer\n"); +#endif + if (currentjob) + { + pid = currentjob->pid; + } + } + else + { + c = argv[1]; + if (*c == '%') + pid = pidfromjob(atoi(++c)); + else + pid = atoi(c); + } + if (pid < 1) + { + fprints(2, "No such job number: %s\n", c); + return (0); + } + + if (fgstuff(pid)) return(0); + + ptr = findjob(pid); + if (ptr) + { + if (strcmp(ptr->dir, currdir)) /* If directory has changed */ + { + fprints(2, "[%d] %d %s (wd now: %s)\n", ptr->jobnumber, ptr->pid, + ptr->name, ptr->dir); + + chdir(ptr->dir); /* Reset our current directory */ + strcpy(currdir,ptr->dir); + setval("cwd", currdir, &vlist); + } + else + fprints(2, "[%d] %d %s\n", ptr->jobnumber, ptr->pid, ptr->name); + } + else + { + fprints(2, "Couldn't find job ptr in fg\n"); + return (0); + } + /* and finally wake them up */ + +#ifndef V7JOB +# ifdef DEBUG + fprints(2,"About to SIGCONT %d\n", pid); +# endif + kill(pid, SIGCONT); +#endif + + ptr->STATUS = RUNFG; + currentjob = ptr; + return (pid); +} +#endif /* defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) */ diff --git a/Applications/wish/m_fuzix.h b/Applications/wish/m_fuzix.h new file mode 100644 index 0000000000..17edaa1e6c --- /dev/null +++ b/Applications/wish/m_fuzix.h @@ -0,0 +1,26 @@ +/* FUZIX specific includes and defines + * + */ +#define FUZIX0_4 /* FUZIX 0.4 */ +#define FUZIX + +#define USES_TERMIOS +#define PROTO +#define STDARG + +#include +#include +#include /* Only for perror */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Applications/wish/machine.h b/Applications/wish/machine.h new file mode 100644 index 0000000000..17edaa1e6c --- /dev/null +++ b/Applications/wish/machine.h @@ -0,0 +1,26 @@ +/* FUZIX specific includes and defines + * + */ +#define FUZIX0_4 /* FUZIX 0.4 */ +#define FUZIX + +#define USES_TERMIOS +#define PROTO +#define STDARG + +#include +#include +#include /* Only for perror */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Applications/wish/main.c b/Applications/wish/main.c new file mode 100644 index 0000000000..f461c4da91 --- /dev/null +++ b/Applications/wish/main.c @@ -0,0 +1,333 @@ +/* The main loop of the shell + */ + +#include "header.h" +char x,y,z; + +int Argc; /* The number of arguments for Wish */ +char **Argv; /* The argument list for Wish */ +static char *prompt; /* A pointer to the prompt string */ +int lenprompt; /* and the length of the prompt */ +int saveh; /* Should we save history? */ +static bool Loginshell=FALSE; /* Indicates if we're a login shell */ +static struct candidate *next_word; + +#ifdef PROTO +bool(*getaline) (uchar *line,int *nosave) = getuline; +#else +bool(*getaline) () = getuline; /* Our input routine defaults to getuline() */ +#endif + + +/* Lengthint is used by prprompt to determine the number + * of chars in the string version of an integer; this is + * needed so the prompt length is calculated properly. + */ +#ifdef PROTO +static int lengthint(int num) +#else +static int lengthint(num) + int num; +#endif +{ + int i; + + for (i = 0; num; num = num / 10, i++); + return (i); +} + +/* Print out the current time in a set length string */ +#ifdef PROTO +static void printime(void) +#else +static void +printime() +#endif +{ + long clock; + struct tm *t; + + time(&clock); + t = localtime(&clock); + prints("%2d:%02d:%02d", t->tm_hour, t->tm_min, t->tm_sec); +} + +/* Prprompt prints out the prompt. It parses all the special options + * that the prompt can take, and determines the effective length of + * the prompt as it appears on the screen. If there is no $prompt, + * it defaults to '% '. + */ +void +prprompt() +{ + extern int curr_hist; + extern char *so, *se; + char c; + int i, len; + + lenprompt = 0; + prompt = EVget("prompt"); + if (prompt == NULL) + prompt = "% "; + for (i = 0; prompt[i]; i++) + if (prompt[i] == '%') + switch (prompt[++i]) + { + case EOS: + case '%': + write(1, "%", 1); + lenprompt++; + break; + case '!': +#ifndef NO_HISTORY + case 'h': + lenprompt += lengthint(curr_hist); + prints("%d", curr_hist); + break; +#endif + case 'd': + len = strlen(EVget("cwd")); + lenprompt += len; + write(1, EVget("cwd"), len); + break; + case 'S': + prints("%s", so); + break; + case 's': + prints("%s", se); /* temp stop standout */ + break; + case '@': + case 't': + lenprompt += 8; + printime(); + break; + default: + write(1, "%", 1); + lenprompt += 2; + if (prompt[i] < 32 || prompt[i] == 127) + { + write(1, "^", 1); + c = prompt[i] + 64; + write(1, &c, 1); + lenprompt++; + } + else + write(1, &prompt[i], 1); + } + else + { + if (prompt[i] < 32 || prompt[i] == 127) + { + write(1, "^", 1); + c = prompt[i] + 64; + write(1, &c, 1); + lenprompt++; + } + else + write(1, &prompt[i], 1); + lenprompt++; + } +} + +/* Set the terminal back to normal + * before leaving the shell + */ +void +leave_shell(how) + int how; +{ + char *argv[2]; + + setcooked(); + if (Loginshell) /* If we're a login shell */ + { + argv[0] = "source"; + argv[1] = ".klogout"; /* Source .klogout */ + source(2, argv); + } + write(1, "\n", 1); + exit(how); +} + +/* Setup does most of the initialisation of the shell. Firstly the default + * variables & the environ variables are set up. Then the termcap strings + * are found, and then any other misc. things are done. + */ +#ifdef PROTO +static void setup(void) +#else +static void +setup() +#endif +{ + extern char currdir[]; + extern struct vallist vlist; + char *argv[2]; + char *home; + + /* Initialise the environment */ + if (!EVinit()) + fatal("Can't initialise environment"); +#ifdef USES_GETWD + if (getwd(currdir)) /* Get the current directory */ +#else + if (getcwd(currdir, MAXPL)) +#endif + setval("cwd", currdir, &vlist); + else + write(2, "Can't get cwd properly\n", 23); + setval("prompt", "% ", &vlist); /* Set the prompt to % for now */ + setval("Version", "2.0.41", &vlist); + catchsig(); /* Catch signals */ + getstty(); /* Set up the stty for Wish */ + terminal(); /* Get the termcap strings */ + +#ifndef NO_BIND + initbind(); /* Set the default key bindings */ +#endif + + argv[0] = "source"; + home= EVget("HOME"); + if (home) + { argv[1]= Malloc(strlen(home) + strlen(".wishrc")+3, "setup malloc"); + sprints(argv[1],"%s/.wishrc", home); + } + else argv[1] = ".wishrc"; /* Source .wishrc */ + source(2, argv); + free(argv[1]); + if (*Argv[0]=='-') /* If we're a login shell */ + { + Loginshell= TRUE; /* Make sure we know */ + if (home) + { argv[1]= Malloc(strlen(home) + strlen(".wishlogin")+3, "setup malloc"); + sprints(argv[1],"%s/.wishlogin", home); + } + else argv[1] = ".wishlogin"; /* Source .wishlogin */ + source(2, argv); + free(argv[1]); + } + saveh = TRUE; +} + + +/* Doline is the loop which gets a line from the user, runs it through + * the metachar expansion, and finally calls command() to execute it. + * It returns when there are no more lines available - so it really + * should be called dolines()! + * + * If isalias is FALSE, we are not expanding aliases. Otherwise we are, + * so don't clear the carray, our args are in there. Also, how will + * then hold H_BCKGND if we are a subshell. + */ + +void +doline(isalias) + int isalias; +{ + extern struct candidate carray[], *wordlist; /* Wordlist is list or words */ + extern int Exitstatus, ncand; + int i; + +/* Nextword holds the word for the next pipeline, + * termword holds the token that terminated the pipeline. + */ + struct candidate *termword; + char *linebuf; + int pid, q, oldncand; + int term; + +#ifdef DEBUG + i = (int) sbrk(0); + prints("In doline, Brk value is %x\n", i); + prints("Getaline f'n ptr is %x\n", getaline); +#endif + linebuf = (char *) malloc(MAXLL); + if (linebuf == NULL) + fatal("Couldn't malloc linebuf\n"); + + while ((*getaline) (linebuf, &q) == TRUE) /* Get a line from user */ + { + if (q || *linebuf == EOS) + continue; + +#ifdef DEBUG + fprints(2, "In doline, line is %s\n", linebuf); +#endif + if (isalias == FALSE) + ncand = 0; /* Initialise the carray */ + next_word = &carray[ncand]; /* list for meta.c */ + oldncand = ncand; + + if (meta_1(linebuf, FALSE) == FALSE) + continue; /* Expand ! */ +#ifndef NO_HISTORY + if (saveh) + savehist(expline(carray), 1); /* Save the line */ +#endif + +/* At this point, we need to extract pipelines + * from the parsed line, so that we expand + * metachars properly. This means we need yet + * another loop. Oh well. + */ + for (wordlist = next_word; wordlist != NULL; wordlist = next_word) + { + /* Find pipeline end */ + while (next_word && (i = next_word->mode & C_WORDMASK) != C_SEMI && i != C_DBLAMP + && i != C_DBLPIPE) + next_word = next_word->next; + /* If we found one */ + if (next_word) + { + termword = next_word; /* Save it */ + next_word = next_word->next; /* Nextword is next pipeline */ + termword->next = NULL; /* Terminate our pipeline */ + } + meta_2(); /* Expand metacharacters */ + term = command(&pid, FALSE, NULL, FALSE); /* Actually run it here */ + + if (isalias & H_BCKGND) pid= -pid; /* Wait ALWAYS if subshell */ + if (term != C_AMP && pid) + waitfor(pid); /* Wait for it to finish */ + if (term == C_DBLAMP && Exitstatus != 0) + break; + if (term == C_DBLPIPE && Exitstatus == 0) + break; +#ifndef NO_JOB + if (saveh) + joblist(0, NULL); /* print the list of jobs */ +#endif + + } + + for (i = oldncand; i < ncand; i++) /* Free the command words */ + if (carray[i].mode == TRUE) + { + carray[i].mode = FALSE; + free(carray[i].name); + } + ncand = oldncand; + + } + free(linebuf); +} + +/* Main. This is the bit that starts the whole ball rolling. + */ +int +main(argc, argv) + int argc; + char *argv[]; + +{ + + Argc = argc; /* Set up arg variables */ + Argv = argv; +#ifdef MALLOCDEBUG + initmall(); /* Initialise the malloc arrays */ +#endif + setup(); /* Set up the vars & the termcap stuff */ + + while (1) + doline(FALSE); /* We only exit when we shutdown() */ +} diff --git a/Applications/wish/malloc.c b/Applications/wish/malloc.c new file mode 100644 index 0000000000..e42969b1be --- /dev/null +++ b/Applications/wish/malloc.c @@ -0,0 +1,79 @@ +/* This is a version of malloc which will always + * return a valid pointer, or exit(1) with a + * message. + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +#ifdef MALLOCDEBUG +static void *StartSbrk; +static void *mallptr[1024]; +static int mallwho[1024]; +static int mallsize[1024]; +#endif + +char *Malloc(size, mesg) + unsigned int size; + char *mesg; + { + char *a; + + if ((a=(char *)malloc(size))==NULL) + { fprints(2,"Malloc error: %s\n",mesg); exit(1); } + return(a); + } + + +/* The following routines are non-portable, and are used solely for + * debugging malloc/free calls. + */ + +#ifdef MALLOCDEBUG +# undef free +# undef malloc + +void initmall() + { + int i; + + for (i=0;i<1024;i++) mallptr[i]=NULL; + StartSbrk= sbrk(0); + } + +void *myMalloc(size) + unsigned int size; + { + void *a; + int i, *b; + + b= (int *)&size; b--; + a= (void *)malloc(size); + if (a) + for (i=0;i<1024;i++) + if (mallptr[i]==NULL) + { mallptr[i]=a; mallwho[i]= *b; mallsize[i]=size; break; } + fprints(2,"0x%x malloc'd 0x%x, size %d\n",*b,a,size); + return(a); + } + +void myFree(ptr) + void *ptr; + { + int *b; + int i; + + b= (int *)&ptr; b--; + fprints(2,"0x%x ",*b); + if (ptr<= StartSbrk) + { fprints(2,"Trying to free a bad pointer, 0x%x\n",ptr); exit(1); } + else + { + for (i=0;i<1024;i++) if (mallptr[i]==ptr) { mallptr[i]=NULL; break; } + if (i==1024) { fprints(2,"Unmalloc'd ptr 0x%x\n",ptr); exit(1); } + fprints(2,"%d Freeing 0x%x\n",i,ptr); + free(ptr); + } + } +#endif /* MALLOCDEBUG */ diff --git a/Applications/wish/meta.c b/Applications/wish/meta.c new file mode 100644 index 0000000000..dcd1fc3e88 --- /dev/null +++ b/Applications/wish/meta.c @@ -0,0 +1,1073 @@ +/* Metacharacter expansion. This file holds the routines that do the first + * parsing of the command line, and the expansion of the metacharacters + * ' " ` \ $ ~ * ? and [ + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" +#define MAXMATCH 512 +#define OK 0 +#define NX_ERR -1 +#define BR_ERR -2 + + +extern struct candidate carray[]; /* The matched files */ +extern struct candidate *next_word; +int ncand; +struct candidate *wordlist; /* The list of words for the parser */ +static int Mode; /* Used by star */ +static char *qline; /* ``'d line from backquot */ + +#ifdef PROTO +static int matchdir(char *directory, char *pattern); +#else +static int matchdir(); +#endif +#ifdef DEBUG +static void listcarray(); +#endif + +/* Match takes a string, and a pattern, which can contain *, ? , \ and [], + * and returns 0 if the strings matched the pattern, otherwise a negative + * number. If the pattern ends in a '/', matchdir is called with the + * arg accumdir/string to find further matches. Match also adds each match + * into the matches array given above. + */ +#ifdef PROTO +static int match(char *string, char *pattern, char *accumdir) +#else +static int +match(string, pattern, accumdir) + char *string, *pattern, *accumdir; +#endif +{ + char c, *findex, *pindex, *fmatch, *pmatch, rempat[MAXWL], where[MAXPL]; + int i, mismatch, found, star; + + findex = string; /* Initialise our vars */ + pindex = pattern; + if ((*findex == '.') && (*pindex != '.')) /* Don't match . unless asked */ + return (NX_ERR); + mismatch = star = 0; + while (*findex && !mismatch) /* While no mismatch */ + { + switch (*pindex) /* Match the pattern's char */ + { + case '*': + pindex++; /* Skip the '*' */ + pmatch = pindex; /* We must match from here on */ + fmatch = findex; + star = 1; /* Found a star */ + break; + case '?': + pindex++; /* just increment, letter automatically + matches. */ + findex++; + break; + case '[': + found = 0; /* For all chars in [] */ + for (; (*pindex) && (*pindex != ']'); pindex++) + { + switch (*pindex) + { + case '\\': + if (*(pindex + 1) == *findex) /* Try match on next char */ + found = 1; + break; + case '-': + if ((*(pindex + 1) == ']') || (*(pindex - 1) == '[')) + if (*pindex == *findex) + { + found = 1; /* Try a match on '-' */ + break; + } /* or try all chars in the range */ + for (c = *(pindex - 1) + 1; c <= *(pindex + 1) && !found && c != ']'; c++) + if (c == *findex) + found = 1; + break; + default: /* Default: just match that char */ + if (*pindex == *findex) + found = 1; + break; + } + } +/* + * We could exit the loop as soon as found is set to 1 but we have to + * keep going to the end of the list so we can recommence matching + * after the ']'. This also allows us to test for the unmatched + * bracket. + */ + if (*pindex == EOS) + { + fprints(2, "Unmatched bracket '['\n"); + return (BR_ERR); + } + if (found) + { + pindex++; /* move past the ']' */ + findex++; /* and on to the next letter */ + break; + } + if (!star) + mismatch = 1; /* failure for this letter of file */ + else + { + pindex = pmatch; + findex = ++fmatch; + } + break; + case '\\': + pindex++; /* go on to next symbol */ + + default: + if (*findex != *pindex) /* No match and not a star */ + if (!star) /* gives an error */ + mismatch = 1; + else + { + pindex = pmatch; /* Move back to the char we must */ + findex = ++fmatch; /* match, & move along string */ + } + else + { + pindex++; /* Yes, a match, move up */ + findex++; + } + break; + } + } + if (mismatch) + return (NX_ERR); /* it didn't match, go on to next one */ + for (; *pindex == '*'; pindex++); /* get rid of trailing stars in pattern */ + if (*findex == *pindex) + { + /* add it to the candidate list */ + where[0] = EOS; + if (*accumdir) + { + strcpy(where, accumdir); + strcat(where, "/"); + } + strcat(where, string); + if (ncand == MAXCAN) + return (OK); + carray[ncand].name = (char *) malloc((unsigned) (strlen(where) + 4)); + if (carray[ncand].name == NULL) + return (OK); + strcpy(carray[ncand].name, where); + carray[ncand++].mode = Mode | TRUE | C_SPACE; /* Was malloc'd */ + return (OK); + } + else if (*pindex == '/') /* test if file is a directory */ + { + for (i = 0; *pindex; i++, pindex++) + rempat[i] = *(pindex + 1);/* even copies null across */ + strcpy(where, accumdir); + if (where[0] != EOS) + { + strcat(where, "/"); + strcat(where, string); + } + else + strcpy(where, string); + return (matchdir(where, rempat)); /* recursively call this pair again */ + } + else /* it just doesn't match */ + return (NX_ERR); +} + +/* Matchdir takes a directory name and a pattern, and returns 0 if any + * files in the directory match the pattern. If none, it returns a negative + * number. Note that this may be recursive, as it calls match(), which calls + * matchdir(). + */ +#ifdef PROTO +static int matchdir(char *directory, char *pattern) +#else +static int +matchdir(directory, pattern) + char *directory, *pattern; +#endif +{ + DIR *dirp; + +#ifdef USES_DIRECT + struct direct *entry; +#else + struct dirent *entry; +#endif + int foundany = 0; + + if (*directory != EOS) + { + if ((dirp = opendir(directory)) == NULL) + return (NX_ERR); + } + else + { + if ((dirp = opendir(".")) == NULL) + return (NX_ERR); + } + while ((entry = readdir(dirp)) != NULL) + if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..") && + !match(entry->d_name, pattern, directory)) + foundany++; + closedir(dirp); + if (foundany) + return (OK); + else + return (NX_ERR); +} + +#ifdef PROTO +static void finddir(char *word, char *dir) +#else +static +void +finddir(word, dir) + char *word, *dir; +#endif +{ + char c; + int i = 0, j, l = 1; + +/* This function finds the directory to start the matching from. */ + while (l) + switch (c = word[i]) + { + case '*': + case '?': + case '[': + for (; dir[i] != '/' && i; i--); /* go back to end of previous + component */ + if (i) + dir[i++] = EOS; + else if (dir[0] == '/') + dir[++i] = EOS; + else + dir[0] = EOS; + l = 0; + break; + default: + dir[i++] = c; + } + for (l = i, j = 0; word[l]; l++, j++) + word[j] = word[l]; + word[j] = EOS; +} + + +#ifndef NO_TILDE +/* Tilde takes the word beginning with a ~, and returns the word with the + * first part replaced by the directory name. If ~/, we use $HOME + * e.g ~fred -> /u1/staff/fred + * ~jim/Dir/a*.c -> /usr/tmp/jim/Dir/a*.c + * ~/Makefile -> /u1/staff/warren/Makefile + * If it cannot expand, it returns dir as a zero-terminated string + * + * It also uses the tilde list now as well. + */ +void +tilde(word, dir) + char *word, *dir; +{ + extern struct vallist tlist; + + struct passwd *entry; + struct val *t; + char *a; + + word++; + *dir = EOS; + if (*word == '/') /* use $HOME if ~/ */ + { + a = word; + entry = getpwuid(getuid()); + strcpy(dir, entry->pw_dir); + } + else + { + a = strchr(word, '/'); /* Get the word to parse */ + if (a != NULL) + *a = EOS; + for (t = tlist.head; t; t = t->next) /* Try the tilde list next */ + { + if (!strcmp(word, t->name)) + { + strcpy(dir, t->val); + break; + } + } + if (*dir == EOS) /* Finally, consult the passwd file */ + { + entry = getpwnam(word); /* Get the directory's name */ + if (entry) + strcpy(dir, entry->pw_dir); + } + } + endpwent(); + if (*dir == EOS) + return; + /* Form the real partial name */ + if (a != NULL) + { + *a = '/'; + strcat(dir, a); + } +} +#endif + +/* Dollar expands a variable. It takes a pointer to a candidiate, + * and replaces it with the variable's value. + */ +#ifdef PROTO +static void dollar(struct candidate *cand) +#else +static void +dollar(cand) + struct candidate *cand; +#endif +{ + extern int Exitstatus, Argc; + extern char **Argv; + struct candidate *next; + char *end, *value; + char c = EOS; + char *a, *doll; + unsigned int length, base, mall = 0; /* Mall is 1 if we've malloc'd */ + + + doll = cand->name; + /* If a special variable */ + if (strlen(doll) == 1 || doll[1] == '=') + { + switch (*doll) + { + case '$': /* Replace with pid */ + value = (char *) malloc(10); + length = getpid(); + if (value) + { + sprints(value, "%d", length); + mall = 1; + } + break; + case '#': /* Replace with # of args */ + value = (char *) malloc(10); + if (value) + { + sprints(value, "%d", Argc - 1); + mall = 1; + } + break; + case '?': /* Replace with exist status */ + value = (char *) malloc(10); + if (value) + { + sprints(value, "%d", Exitstatus); + mall = 1; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': /* Replace with Argv[n] */ + case '5': + case '6': + case '7': + case '8': + case '9': + length = *doll - '0'; + value = (length < Argc ? Argv[length] : ""); + break; + case '*': /* Replace with all args */ + if (Argc > 1) + { + base = ncand; + ncand += (Argc - 1); + carray[ncand - 1].next = cand->next; + carray[ncand - 1].name = Argv[Argc - 1]; + carray[ncand - 1].mode = C_SPACE; + cand->next = &carray[base]; + for (length = 1; base < ncand - 1; base++, length++) + { + carray[base].name = Argv[length]; + carray[base].mode = C_SPACE; + carray[base].next = &carray[base + 1]; + } + } + if (cand->mode & TRUE) + { + free(cand->name); + cand->mode = FALSE; + } + cand->name = NULL; + return; + default: + fprints(2, "Unknown variable $%c\n", *doll); + return; + } + if (doll[1] == '=') /* An assignment */ + { + end = doll + 1; + c = '='; + } + } + else + { /* A normal variable */ + /* Find the last char of the var */ + for (end = doll; end != EOS && isalnum(*end); end++); + c = *end; + *end = EOS; /* Terminate the name */ + + value = EVget(doll); /* Get the value */ + if (!value) + { + fprints(2, "Unknown variable $%s\n", doll); + return; + } + else if (c == '[') /* or a field of it */ + { + length = atoi(++end); /* Get the field number */ + /* Skip the number */ + while (*end != ']' && *end != EOS) + end++; + if (*end == ']') + end++; + c = *end; + *end = EOS; + for (a = value; length; length--) + { /* Find a gap */ + if (!(a = strpbrk(a, " \t"))) + { + value = NULL; + break; + } + while (*a == ' ' || *a == '\t') + a++; /* Skip the gap */ + if (*a == EOS) + { + value = NULL; + break; + } + } + if (value) /* If a valid field */ + { + length = strcspn(a, " \t"); /* Get the length of the field */ + value = (char *) malloc(length + 2); + if (value) + { + strncpy(value, a, length); /* Make a copy - can't change var */ + value[length] = EOS; + mall = TRUE; + } + } + } + } + + cand->mode &= ~C_DOLLAR; + if (c) /* If there's more to the value */ + { + *end = c; /* Insert the rest in the list */ + next = cand->next; + addcarray(end, cand, cand->mode | TRUE, TRUE); + carray[ncand - 1].next = next; + cand->mode &= ~C_SPACE; + } + if (cand->mode & TRUE) + free(cand->name); /* we can replace totally */ + cand->name = value; + if (mall) + cand->mode |= TRUE; + else + cand->mode &= ~TRUE; +#ifdef DEBUG + fprints(2, " In dollar "); + listcarray(); +#endif + return; +} + + +/* Expline takes a linked list of words, and converts them into a normal + * string. This is used to save history. No double quotes now. + */ +char * +expline(list) + struct candidate *list; +{ + struct candidate *q; + char *out, *a; + int i, mode = 0; + bool lastspace = FALSE; + + /* Find the amount to malloc */ + for (i = 0, q = list; q != NULL; q = q->next) + if (q->name) + i += strlen(q->name) + 6; /* Just in case */ + + out = (char *) malloc((unsigned) i + 4); + if (out == NULL) + return (NULL); + + for (a = out, q = list; q; q = q->next) + { + mode = q->mode; + if (q->name) /* It's a word */ + { + if (lastspace) + *(a++) = ' '; + if (mode & C_QUOTE) + *(a++) = '\''; + if (mode & C_CURLY) + *(a++) = '{'; + if (mode & C_BACKQUOTE) + *(a++) = '`'; + if (mode & C_DOLLAR) + *(a++) = '$'; /* Add $ to vars */ + strcpy(a, q->name); + a += strlen(q->name); /* Add the word!! */ + if (mode & C_QUOTE) + *(a++) = '\''; + if (mode & C_CURLY) + *(a++) = '}'; + if (mode & C_BACKQUOTE) + *(a++) = '`'; + lastspace = mode & C_SPACE; + } + else + { + if (lastspace) + *(a++) = ' '; + switch (mode & C_WORDMASK) + { + case C_SEMI: + strcpy(a, "; "); + a += 2; + break; + case C_PIPE: + strcpy(a, "| "); + a += 2; + break; + case C_DBLPIPE: + strcpy(a, "|| "); + a += 3; + break; + case C_AMP: + strcpy(a, "& "); + a += 2; + break; + case C_DBLAMP: + strcpy(a, "&& "); + a += 3; + break; + case C_LT: + strcpy(a, "< "); + a += 2; + break; + case C_LTLT: + strcpy(a, "<< "); + a += 3; + break; + case C_GT: + if ((mode & C_FD) == 2) + { + strcpy(a, ">& "); + a += 3; + } + else + { + strcpy(a, "> "); + a += 2; + } + break; + case C_GTGT: + strcpy(a, ">> "); + a += 3; + break; + } + lastspace = FALSE; + } + } + return (out); +} + +#ifdef DEBUG +/* Wordlist is used for debugging to print out the carray linked list */ +static void +listcarray() +{ + struct candidate *curr; + + fprints(2, "Here's the wordlist:\n"); + for (curr = wordlist; curr != NULL; curr = curr->next) + { + fprints(2, "--> %x ", curr); + if (curr->name) + fprints(2, "%s", curr->name); + if (curr->mode & C_WORDMASK) + switch (curr->mode & C_WORDMASK) + { + case C_SEMI: + fprints(2, "; C_SEMI"); + break; + case C_PIPE: + fprints(2, "| C_PIPE"); + break; + case C_DBLPIPE: + fprints(2, "|| C_DBLPIPE"); + break; + case C_AMP: + fprints(2, "& C_AMP"); + break; + case C_DBLAMP: + fprints(2, "&& C_DBLAMP"); + break; + case C_LT: + fprints(2, "< C_LT"); + break; + case C_LTLT: + fprints(2, "<< C_LTLT"); + break; + case C_GT: + fprints(2, "%d> C_GT", curr->mode & C_FD); + break; + case C_GTGT: + fprints(2, "%d>> C_GTGT", curr->mode & C_FD); + break; + default: + fprints(2, "%o C_UNKNOWN", curr->mode); + } + else + { + if (curr->mode & C_SPACE) + fprints(2, " C_SPACE"); + if (curr->mode & C_DOLLAR) + fprints(2, " C_DOLLAR"); + if (curr->mode & C_QUOTE) + fprints(2, " C_QUOTE"); + if (curr->mode & C_CURLY) + fprints(2, " C_DBLQUOTE"); + if (curr->mode & C_BACKQUOTE) + fprints(2, " C_BACKQUOTE"); + if (curr->mode & TRUE) + fprints(2, " (malloc'd)"); + } + fprints(2, " %x\n", curr->next); + } +} + +#endif + +/* Addword is used by Meta_1 to add a word into the carray list. + * This is where history is expanded. + */ +#ifdef PROTO +static void addword(char *string, int mode) +#else +static void +addword(string, mode) + char *string; + int mode; +#endif +{ + char *b; + + if (string != NULL) /* It's a word, check it out */ + { + if (*string == EOS) + return; + +#ifndef NO_HISTORY + /* Find any history and retrieve it */ + if ((mode & (C_QUOTE | C_BACKQUOTE | C_DOLLAR | C_CURLY)) == 0) + if ( *string == '!' && (b = gethist(++string)) != NULL) + { + meta_1(b, FALSE); /* Expand it too */ + return; + } +#endif + /* mode &= ~FALSE; I have NO idea what this does! */ + } + addcarray(string, &carray[ncand - 1], mode, mode & TRUE); +} + + +/* Star expands * ? and [] in the carray */ +#ifdef PROTO +static void star(struct candidate *curr) +#else +static void +star(curr) + struct candidate *curr; +#endif +{ + char dir[MAXWL]; + struct candidate *a; + int base; + + Mode = curr->mode & ~(TRUE | C_SPACE); + base = ncand; /* Save start of expansion */ + dir[0] = EOS; + finddir(curr->name, dir); /* expand them */ + a = curr->next; + if (!matchdir(dir, curr->name)) + { + qsort((void *) &carray[base], ncand - base, sizeof(struct candidate), compare); + /* Now insert into list */ + carray[ncand - 1].next = a; + curr->next = &carray[base]; + for (; base < ncand - 1; base++) + carray[base].next = &carray[base + 1]; + } + if (curr->mode & TRUE) + { + free(curr->name); + curr->mode = FALSE; + } + curr->name = NULL; +#ifdef DEBUG + fprints(2, " In star "); + listcarray(); +#endif +} + +/* Getbackqline is called when backquot forks to run a `command`. + */ +#ifdef PROTO +static bool getbackqline( uchar *line, int *nosave) +#else +static bool +getbackqline(line, nosave) + uchar *line; + int *nosave; +#endif +{ + *nosave = 0; + if (qline) + { + strcpy(line, qline); + qline = NULL; + return (TRUE); + } + else + return (FALSE); +} + +/* Backquot expands backquotes */ +#ifdef PROTO +static void backquot(struct candidate *curr) +#else +static void +backquot(curr) + struct candidate *curr; +#endif +{ +#ifdef PROTO + extern bool(*getaline) (uchar *line , int *nosave ); +#else + extern bool(*getaline) (); +#endif + extern int saveh; + struct candidate *first; + int term, base; + int pfd[2]; + char *line; + + term = pipe(pfd); + if (term == -1) + { + fprints(2, "Cannot open pipe in backquot\n"); + return; + } + switch (fork()) + { + case -1: + fatal("Bad fork in meta_4\n"); + + case 0: + /* Redirect our output */ + close(pfd[0]); + close(1); + dup(pfd[1]); + close(pfd[1]); + qline = curr->name; + getaline = getbackqline; /* Pass line to doline */ + saveh = FALSE; + doline(FALSE); + exit(0); /* and die */ + default: + close(pfd[1]); + base = ncand; /* Get start of expansion */ + first = curr; + curr = curr->next; /* Move curr up now */ + line = (char *) malloc(MAXLL); + if (line == NULL) + { + fprints(2, "Can't malloc line in backquot\n"); + return; + } + fileopen(NULL, pfd[0]); + while (getfileline(line, &term)) + meta_1(line, C_SPACE | TRUE); /* Add words to the carray */ + fileclose(); + free(line); + /* Delete first node */ + if (first->mode & TRUE) + free(first->name); + first->name = NULL; + first->mode = 0; + /* Insert words in list */ + if (ncand > base) + { + carray[base - 1].next = NULL; /* Undo addcarray */ + first->next = &carray[base]; + carray[ncand - 1].next = curr; + } + } +#ifdef DEBUG + fprints(2, " In backquot "); + listcarray(); +#endif +} + +/* Meta_1 takes the user's input line (old), and builds a linked list of words + * in the carray. It also expands history. Meta_1 returns TRUE if + * successful; if any errors occur, it returns FALSE. + * + * If mustmalc is TRUE, meta_1 assumes the word must be strcpy'd. + * As of version 38, support for double quotes is gone, and backquotes are + * treated in the same way as single quotes. + */ +bool +meta_1(old, mustmalc) + char *old; + bool mustmalc; +{ + char bq[3]; /* Used to find backslashes in quotes */ + char *a, *old2; + char c; + int mode; + int lastdollar = 0; /* Was last word a variable? */ + + bq[0]= '\\'; bq[2]= EOS; + while (1) /* Parse each word */ + { + a = strpbrk(old, " \t\n\\'`{;|<>&$"); + /* why `&& *old' below - Not all strpbrk()s give NULL when *old=EOS */ + if (a != NULL && *old) /* We found a word */ + { + c = *a; + *a = EOS; + mode = (c == ' ' || c == '\t') ? C_SPACE : 0; + /* Add the word to the list */ + if (a > old) + addword(old, mode | lastdollar | mustmalc); + + mode = 0; + switch (c) /* Now deal with the `token' */ + { + case '{': + mode = C_CURLY; + c = '}'; + goto quotes; /* Yuk, a goto */ + case '`': + mode = C_BACKQUOTE; + goto quotes; /* Yuk, a goto */ + case '\'': + mode = C_QUOTE; + quotes: + old2= old = ++a; bq[1]= c; + quotes2: + a = strpbrk(old2,bq); /* Find either slosh or matching quote */ + if (a == NULL) + { + fprints(2, "No matching %c\n", c); + return (FALSE); + } + if (*a == '\\') + { + if (a>old) /* there is something before the slosh */ + { + *a= EOS; + addword(old, mode | lastdollar | mustmalc); + } + old= ++a; old2= old+1; + goto quotes2; /* Yuk, another goto */ + } + *(a++) = EOS; /* Terminate the word */ + if (*a == ' ' || *a == '\t') /* Check for trailing spaces */ + { + a++; + mode |= C_SPACE; + } + addword(old, mode | lastdollar | mustmalc); + break; + case '\\': + old = (char *) malloc(2); + if (old) + { + old[0] = *(a + 1); + old[1] = EOS; + addword(old, mode | lastdollar | TRUE); + } + a += 2; + break; + case '|': + mode = C_PIPE; + goto doubles; /* Yuk, a goto */ + case '&': + mode = C_AMP; + goto doubles; /* Yuk, a goto */ + case '<': + mode = C_LT; + doubles: + a++; + if (*a == c) + { + a++; + mode += C_DOUBLE; + } + addword(NULL, mode); + break; + case '>': + mode = 1 | C_GT; /* Default to stdout */ + switch (*(a + 1)) + { + case '>': + mode = 1 | C_GTGT; + a++; + break; + case '&': + mode = 2 | C_GT; + a++; + } + addword(NULL, mode); + a++; + break; + case ';': + addword(NULL, C_SEMI); + case '$': + if (lastdollar && (a==old)) /* We found a $$ */ + { + a++; mode= C_DOLLAR | TRUE ; + mode|= ((*a==' ')||(*a=='\t')) ? C_SPACE : FALSE; + addword("$",mode); a--; + c=' '; /* Don't recognise 2nd $ below */ + } + case ' ': + case '\t': + a++; + case '\n': + break; + default: + fprints(2, "Unknown token %c in meta_1", c); + return (FALSE); + } + lastdollar = (c == '$') ? C_DOLLAR : 0; /* Mark variables */ + + /* Ok, skip intermediate spaces */ + for (old = a; *old && (*old == ' ' || *old == '\t'); old++); + } + else + { + addword(old, lastdollar | mustmalc); + break; + } + } +#ifdef DEBUG + fprints(2, " In meta_1 "); + wordlist = next_word; + listcarray(); +#endif + return (TRUE); +} + + +/* Joinup concatenates two carray elements together. The following pointers + * must be non-null: curr, curr->next, curr->name, curr->next->name. + */ +#ifdef PROTO +static void joinup(struct candidate *curr) +#else +static void +joinup(curr) + struct candidate *curr; +#endif +{ + char *a; + + a = (char *) malloc((unsigned) (strlen(curr->name) + strlen(curr->next->name) + 1)); + if (!a) + return; + strcpy(a, curr->name); + strcat(a, curr->next->name); + if (curr->mode & TRUE) + free(curr->name); + curr->mode = curr->next->mode | TRUE; + curr->next = curr->next->next; + curr->name = a; +} + +/* Meta_2 expands $ and ~ in the carray */ +void +meta_2() +{ + struct candidate *curr; + char *a, tildir[MAXWL]; + + for (curr = wordlist; curr != NULL; curr = curr->next) + { + a = curr->name; + if (a == NULL || curr->mode & C_QUOTE) + continue; + +#ifndef NO_TILDE + if (*a == '~') /* If a ~ */ + { + tilde(a, tildir); /* expand it too */ +#ifdef DEBUG + fprints(2, "tildir is %s\n", tildir); +#endif + if (curr->mode & TRUE) + free(curr->name); + curr->name = (char *) malloc((unsigned) strlen(tildir) + 4); + if (curr->name != NULL) + { + strcpy(curr->name, tildir); + curr->mode = TRUE | C_SPACE; + } + } +#endif + + if (curr->mode & C_DOLLAR) /* If it has a $ */ + dollar(curr); /* expand it */ + + } + for (curr = wordlist; curr != NULL; curr = curr->next) + { + while (curr->name && curr->next && curr->next->name && !(curr->mode & C_SPACE)) + joinup(curr); /* No space, we'd better join em */ + +#ifdef NOTYET + if (curr->mode & C_CURLY) /* If it is in {} */ + curly(curr); /* expand it */ +#endif + + if (curr->mode & C_BACKQUOTE) /* Ha! Found a backquote word */ + backquot(curr); + + if (!(curr->mode & C_QUOTE) && curr->name && strpbrk(curr->name, "*?[")) + star(curr); + + while (curr->name && curr->next && curr->next->name + && !(curr->mode & ~TRUE) && !(curr->next->mode & ~(TRUE|C_SPACE))) + joinup(curr); /* No space, we'd better join em */ + } +#ifdef DEBUG + fprints(2, " In meta_2 "); + listcarray(); +#endif +} diff --git a/Applications/wish/parse.c b/Applications/wish/parse.c new file mode 100644 index 0000000000..5afa3b14c5 --- /dev/null +++ b/Applications/wish/parse.c @@ -0,0 +1,186 @@ +/* Parsing the input file + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +/* The parser uses the symbols parsed by meta.c, but it needs a few more + * definitions for proper parsing. These are below. + */ + +#define C_WORD C_SPACE /* This indicates the returned word is a */ + /* usual word */ +#define C_EOF FALSE /* Returned when no more tokens left */ + +extern struct candidate *wordlist; /* The list of words to parse */ +static struct candidate *pcurr; /* A ptr to the word we are parsing */ + +/* Gettoken returns the symbol which stands for the current word, or 0. + * If 0, word points to a string that holds a normal word. This is guaranteed + * not to be clobbered until we get back out to main(). + */ +#ifdef PROTO +static int gettoken(char **word, int *fd) +#else +static int +gettoken(word, fd) + char **word; + int *fd; +#endif +{ + int mode; + + /* Skip any null words */ + while (1) + { + /* Return EOF on end of list */ + if (pcurr == NULL) + return (C_EOF); + mode = pcurr->mode; + + if (mode & C_WORDMASK) /* We've found a token, return */ + { + *word = NULL; + *fd = mode & C_FD; + mode &= C_WORDMASK; /* stripping any fd */ + pcurr = pcurr->next; + return (mode); + } + + if (pcurr->name && *(pcurr->name) != EOS) + { + *word = pcurr->name; /* just return it */ + pcurr = pcurr->next; + return (C_WORD); + } + pcurr = pcurr->next; /* Not a valid word or symbol, skip */ + } +} + +/* Command parses the input and calls invoke() to redirect and execute + * each simple command in the parsebuf. It returns the pid to wait on + * in waitpid, or 0 if no pid to wait on, as well as the token that ended + * the pasebuf. This routine is recursive, and when passed makepipe=TRUE, + * makes a pipe, and returns the fd of the writing end in pipefdp. + */ +int +command(waitpid, makepipe, pipefdp, anydups) /* Do simple command */ + int *waitpid, *pipefdp; + int makepipe, anydups; +{ + int token, term; + int argc, pid, pfd[2]; + char *argv[MAXARG + 1]; + char *word; + int i, fd, how = 0; + struct rdrct newfd[3]; + + argc = 0; + argv[0] = NULL; + for (i = 0; i < 3; i++) + { + newfd[i].fd = 0; + newfd[i].how= 0; + newfd[i].file = NULL; + } + if (makepipe == FALSE) + pcurr = wordlist; /* Start parsing the wordlist */ + + while (1) + { + token = gettoken(&word, &fd); +#ifdef DEBUG + prints("Token is %o", token); + if (word != NULL) + prints(" word is %s", word); + write(1, "\n", 1); +#endif + switch (token) + { + case C_WORD: + if (argc == MAXARG) + { + fprints(2, "Too many args\n"); + break; + } + argv[argc++] = word; + continue; + case C_LT: + if (makepipe) + { + fprints(2, "Extra <\n"); + break; + } + if (gettoken(&(newfd[fd].file), &i) != C_WORD) + { + fprints(2, "Illegal <\n"); + break; + } + newfd[fd].how = H_FROMFILE; + anydups= TRUE; + continue; + case C_GT: + case C_GTGT: + if (newfd[fd].fd != 0 || newfd[fd].file != NULL) + { + fprints(2, "Extra > or >>\n"); + break; + } + if (gettoken(&(newfd[fd].file), &i) != C_WORD) + { + fprints(2, "Illegal > or >>\n"); + break; + } + if (token == C_GTGT) + newfd[fd].how = H_APPEND; + anydups= TRUE; + continue; + case C_AMP: /* If a pipe, call ourselves to get */ + case C_EOF: /* the write file descriptor */ + case C_SEMI: + case C_DBLAMP: + case C_DBLPIPE: + case C_PIPE: + argv[argc] = NULL; + if (token == C_PIPE) + { + anydups= TRUE; + if (newfd[1].fd != 0 || newfd[1].file != NULL) + { + fprints(2, "> or >> conflicts with |\n"); + break; + } + term = command(waitpid, TRUE, &(newfd[1].fd),anydups); + } /* and set up the terminal token */ + else + term = token; + /* If called by us, make the needed pipe */ + if (makepipe) + { + if (pipe(pfd) == -1) + { + perror("pipe"); + break; + } + /* and return the write file descriptor */ +#ifdef DEBUG + fprints(2, "Opened pipe fds %d and %d\n", pfd[0], pfd[1]); +#endif + *pipefdp = pfd[1]; + newfd[0].fd = pfd[0]; + } + if (term == C_AMP) + how = H_BCKGND; + pid = invoke(argc, argv, newfd, how, anydups); + /* End of command line, return pid to wait */ + if (token != C_PIPE) + *waitpid = pid; + if (argc == 0 && token != C_EOF) + fprints(2, "Missing command\n"); + return (term); + default: + prints("Unknown token %o in command()\n", token); + } + } +} diff --git a/Applications/wish/posixjob.c b/Applications/wish/posixjob.c new file mode 100644 index 0000000000..98d5e97e9f --- /dev/null +++ b/Applications/wish/posixjob.c @@ -0,0 +1,148 @@ +/* POSIX Job Control functions + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + * + */ + +#define WIFCORE(x) (FALSE) /* We can't tell */ +#define WRUNFG(x) ((x) == RUNFG) +#define WRUNBG(x) ((x) == RUNBG) + +/* The following tables are all VERY OS-dependent, especially on POSIX + * systems. If you find the shell giving you weird signal messages, you + * should change the names of the signals below. + */ +static char *siglist[] = { + "", "Hangup", "Interrupt", "Quit", "Illegal Instruction", + "Trace/BPT Trap", "IOT Trap", "EMT Trap", "Floating Point Exception", + "Killed", "Bus Error", "Segmentation Violation", "Bad System Call", + "Broken Pipe", "Alarm", "Terminated" ,"Urgent Socket Condition", + "Stopped (signal)", "Stopped", "Continue", "Child Status Change", + "(tty input)", "(tty output)", "I/O", "Cpu Time Limit", + "File Size Limit", "Virtual Time Alarm", "Profile Alarm", + "Window Change", "Resource Lost", "User Signal 1", "User Signal 2" +}; + +char *signame[] = { + "", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", + "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM" ,"URG", "STOP", "TSTP", + "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU", "XFSZ", "VTALRM", "PROF", + "WINCH", "LIST", "USR1", "USR2", NULL +}; + +/* Waitfor is called to wait for a requested job to stop/die. It receives the + * new status of any children. If it's the requested job, we return. Pid holds + * the process-id of the job we are after; if it is 0, we return when no + * processes are left waiting to report a change in state. + * + * If pid is negative, we are a subshell and _must_ return only when the pid + * dies. We set the subshell flag and make pid positive again. Then, if we + * find out that the job was stopped, we wake it up. This works because we + * are put to sleep at the same time as the child, and the original shell + * fg()s _us_. + */ +void +waitfor(pid) + int pid; +{ + struct job *thisjob; + int wpid; + bool subshell=FALSE; + int status; + + int waitflags; + waitflags = (pid == 0) ? WNOHANG | WUNTRACED : WUNTRACED; + +#ifdef DEBUG + fprints(2,"In waitfor\n"); +#endif + + if (pid<0) { pid= -pid; subshell= TRUE; } + while (1) + { + wpid = waitpid(-1, &status, waitflags); + +#ifdef DEBUG + fprints(2,"waitxx() returned\n"); +#endif + if (wpid == -1 || wpid == 0) + break; + + if (subshell && WIFSTOPPED(status)) /* We can't return yet */ + { + kill(wpid, SIGCONT); /* Wake the child up first - waah! */ + continue; + } +#ifndef NO_JOB + thisjob = findjob(wpid); + if (thisjob == NULL) + continue; + thisjob->status = status; + thisjob->changed = TRUE; +#endif + if (pid == wpid) + return; + } +} + +/* Stopjob is only called when we received a SIGTSTP. If we are wait3()ing + * a pid, we send a SIGSTOP to that pid. This should then cause wait3() + * to exit, and thus waitfor() will return. + */ +SIGTYPE +stopjob(a) + int a; +{ + signal(SIGTSTP, stopjob); +#ifdef DEBUG + fprints(2,"In stopjobs\n"); +#endif + if (currentjob) + { +#ifdef DEBUG + fprints(2,"About to stop pid %d\n", currentjob->pid); +#endif + kill(currentjob->pid, SIGSTOP); + } +} + + +static void +bgstuff(pid) + int pid; +{ + struct job *ptr; + + setpgid(pid, pid); +#ifdef DEBUG + fprints(2,"About to SIGCONT %d\n", pid); +#endif + kill(pid, SIGCONT); +#ifndef NO_JOB + ptr = findjob(pid); + if (ptr) { ptr->STATUS = RUNBG; currentjob= ptr; } +#endif +} + + +static int +fgstuff(pid) + int pid; +{ + +/* Under UCBJOB and POSIXJOB, we don't try & bring the pid into our + * pgrp, that doesn't work. Instead, we move the terminal + * over to that pgrp, and move ourselves to that pgrp as well. + */ + if (tcsetpgrp(0, pid) == -1) /* Move the terminal to that pgrp */ + { + perror("fg tcsetpgrp"); + return (1); + } + if (setpgid(0, pid) == -1) /* Set shell's process group to the pid's */ + { + perror("fg setpgid"); + return (1); + } + return(0); +} diff --git a/Applications/wish/prints.c b/Applications/wish/prints.c new file mode 100644 index 0000000000..b4030cf4d2 --- /dev/null +++ b/Applications/wish/prints.c @@ -0,0 +1,353 @@ +/* This file contains functions that replace printf, fprintf and sprintf, + * which are smaller and do not use stdio buffering. They can only handle + * %c %s %o %d and %x as format types, although %2x and %02x can be done as + * well. Note that fprintf takes as its first argument a file descriptor, + * not a FILE pointer. + * + * These routines were derived from the prints routine in Minix 1.5. + * + * $Revision: 41.2 $ $Date: 2003/04/21 13:09:53 $ + */ +#define NO_PRINTS_DEFN +#include "header.h" + +/* If your compiler/system supports one of the following, + * you should use it, preferably stdarg.h. + */ +#ifdef STDARG +#include +#endif +#ifdef VARARGS +#ifdef va_start +# undef va_start +#endif +#include +#endif + +#if !defined(STDARG) && !defined(VARARGS) + +/* Otherwise we must make do with these */ +#define va_list char ** +#define va_arg(x,t) (t)*(x++) +#endif + +#define MAXDIG 11 /* 32 bits in radix 8 */ +#define TRUNC_SIZE 1024 + +static char Buf[TRUNC_SIZE], *Bufp; +static char Intbuf[MAXDIG]; +static int Out; +static char *Dest; + +#ifdef PROTO +static void __itoa(char *p, unsigned int num, int radix) +#else +static void +_itoa(p, num, radix) + char *p; + unsigned int num; + int radix; +#endif +{ + int i; + char *q; + + q = p + MAXDIG; + do + { + i = (int) (num % radix); + i += '0'; + if (i > '9') + i += 'A' - '0' - 10; + *--q = i; + } while ((num = (num / radix))); + i = p + MAXDIG - q; + do + *p++ = *q++; + while (--i); + *p = 0; +} + +#ifdef PROTO +static void _put(char c) +#else +static void +_put(c) + char c; +#endif +{ + if (Bufp < &Buf[TRUNC_SIZE]) + *Bufp++ = c; +} + +#ifdef PROTO +static void printvoid(char *str, va_list ap) +#else +static void +printvoid(str, ap) + char *str; + va_list ap; +#endif +{ + int w; + int k, x, radix; + char *p, *p1, c, fillchar; + + Bufp = Buf; + while (*str != '\0') + { + if (*str != '%') + { + _put(*str++); + continue; + } + w = 0; + fillchar = ' '; + str++; + while (*str >= '0' && *str <= '9') + { + if (*str == '0' && !w) + fillchar = '0'; + w = 10 * w + (*str - '0'); + str++; + } + + switch (*str) + { + case 'c': + k = va_arg(ap, int); + _put(k); + str++; + continue; + case 's': + p = va_arg(ap, char *); + p1 = p; + while ((c = *p++)) + _put(c); + for (x = strlen(p1); w > x; w--) + _put(fillchar); + str++; + continue; + case 'x': + radix = 16; + goto printnum; + case 'd': + radix = 10; + goto printnum; + case 'o': + radix = 8; + printnum: + x = va_arg(ap, int); + str++; + __itoa(Intbuf, x, radix); + p = Intbuf; + for (x = strlen(p); w > x; w--) + _put(fillchar); + while ((c = *p++)) + _put(c); + continue; + default: + _put(*str++); + continue; + } + + } + /* write everything in one blow. */ + if (Out == -1) + { + *Bufp++ = 0; +#ifdef USES_BCOPY + bcopy(Buf, Dest, (int) (Bufp - Buf)); +#else + memcpy(Dest, Buf, (int) (Bufp - Buf)); +#endif + } + else + write(Out, Buf, (int) (Bufp - Buf)); +} + +#ifdef VARARGS +/* For systems that have varargs.h, use the following three routines + */ +/* VARARGS */ +void +fprints(va_alist) +va_dcl +{ + va_list argptr; + char *str; + + va_start(argptr); + Out = va_arg(argptr, int); + str = va_arg(argptr, char *); + printvoid(str, argptr); + va_end(argptr); +} +/* VARARGS */ +void +sprints(va_alist) +va_dcl +{ + va_list argptr; + char *str; + + va_start(argptr); + Dest = va_arg(argptr, char *); + Out = -1; + str = va_arg(argptr, char *); + printvoid(str, argptr); + va_end(argptr); +} +/* VARARGS */ +void +prints(va_alist) +va_dcl +{ + va_list argptr; + char *str; + + va_start(argptr); + Out = 1; + str = va_arg(argptr, char *); + printvoid(str, argptr); + va_end(argptr); +} + +#endif + +#ifdef STDARG +/* For systems that have stdarg.h, use the following three routines + */ +/* VARARGS */ +#ifdef PROTO +void fprints(int fd, char *str, ...) +#else +void fprints(fd, str) + int fd; + char *str; +#endif +{ + va_list argptr; + + va_start(argptr, str); + Out = fd; + printvoid(str, argptr); + va_end(argptr); +} +/* VARARGS */ +#ifdef PROTO +void sprints(char *out, char *str, ...) +#else +void sprints(out, str) + char *out; + char *str; +#endif +{ + va_list argptr; + + va_start(argptr, str); + Dest = out; + Out = -1; + printvoid(str, argptr); + va_end(argptr); +} +/* VARARGS */ +#ifdef PROTO +void prints(char *str, ...) +#else +void prints(str) + char *str; +#endif +{ + va_list argptr; + + va_start(argptr, str); + Out = 1; + printvoid(str, argptr); + va_end(argptr); +} + +#endif + +#if !defined(STDARG) && !defined(VARARGS) +/* For systems that have neither, we use these. + */ +/* VARARGS */ +void +fprints(fd, str, argptr) + int fd; + char *str; + char *argptr; +{ + Out = fd; + printvoid(str, &argptr); +} +/* VARARGS */ +void +sprints(out, str, argptr) + char *out; + char *str; + char *argptr; +{ + Dest = out; + Out = -1; + printvoid(str, &argptr); +} +/* VARARGS */ +void +prints(str, argptr) + char *str; + char *argptr; +{ + Out = 1; + printvoid(str, &argptr); +} + +#endif + +/* Mprint is a small utility routine that prints out a line with control + * characters converted to Ascii, e.g ^A etc. If nocr is true, no \n is + * appended. Mprint now uses the MSB variable to determine how to print + * out msb-on chars. If MSB, use standout mode. + */ +void +mprint(line, nocr) + uchar *line; + int nocr; +{ + extern char *so, *se; + char c, low, buf[MAXLL]; + bool msb; + int i, j, k; + + if (EVget("Msb")) + msb = TRUE; + else + msb = FALSE; + for (j = 0, i = 0; line[i]; i++) + { + c = line[i]; + low = c & 0x7f; /* Turn the msb off */ + + if (low < 32 || low == 127) /* If a ctrl-char, insert ^ */ + { + buf[j++] = '^'; + c |= 64; + low |= 64; /* and make it a capital */ + } + if (msb && low != c) /* Bold msb-on char if wanted */ + { + for (k = 0; so[k]; j++, k++) + buf[j] = so[k]; + buf[j++] = low; + for (k = 0; se[k]; j++, k++) + buf[j] = se[k]; + } + else + buf[j++] = c; + } + buf[j] = EOS; + write(1, buf, j); + if (!nocr) + write(1, "\n", 1); +} diff --git a/Applications/wish/proto.h b/Applications/wish/proto.h new file mode 100644 index 0000000000..069c18d9da --- /dev/null +++ b/Applications/wish/proto.h @@ -0,0 +1,227 @@ +/* Prototypes for Wish. This file contains the K&R and Ansi prototypes for + * all the non-static routines in the shell, plus prototypes for all the + * external routines that the shell uses. Expect to comment some of these + * out depending upon what is already defined in your system's header files. + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#ifdef PROTO +# define P(s) s +#else +# define P(s) () +#endif + + +/* Trying to use the intuitive prototypes below AND + * allow the use of stdargs or varargs is basically + * impossible. + * + */ +#ifndef NO_PRINTS_DEFN +void fprints P((int fd, CONST char *fmt, ...)); +void sprints P((char *out, CONST char *fmt, ...)); +void prints P((CONST char *fmt, ...)); +#endif + +void mprint P((uchar *line , int nocr )); + +/* alias.c */ +struct val *checkalias P((char *aname )); +bool getaliasline P((uchar *line , int *nosave )); +int alias P((int argc , char *argv [])); +int unalias P((int argc , char *argv [])); + +/* bind.c */ +int Bind P((int argc , uchar *argv [])); +int unbind P((int argc , uchar *argv [])); +void initbind P((void )); +int getcomcmd P((void )); + +/* builtin.c */ +int builtin P((int argc , char *argv [], int *rtnpid )); + +/* clebuf.c */ +void flushbuf P((void )); +void addbuf P((char *str )); +void mputc P((int b )); + +/* clex.c */ +int compare P((CONST void *a , CONST void *b )); +void addcarray P((char *word , struct candidate *prev , int mode , bool malc )); +void complete P((char *line , int *pos , bool how )); + +/* comlined.c */ +int Show P((uchar *line , int pos , int let , int flag )); +int prevword P((uchar *line , int *p , int flag )); +bool getuline P((uchar *line , int *nosave )); + +/* exec.c */ +int invoke P((int argc , char *argv [], struct rdrct newfd [], int how , int anydups )); + +/* file.c */ +bool fileopen P((char *filename , int fd )); +void fileclose P((void )); +bool getfileline P((uchar *line , int *nosave )); +int source P((int argc , char *argv [])); + +/* hist.c */ +int savehist P((char *line , bool andadd )); +void loadhist P((char *line , int histnum )); +int history P((int argc , char *argv [])); +char *gethist P((char *event )); + +/* job.c */ +int addjob P((int pid , char *name , int isbg )); +int joblist P((int argc , char *argv [])); +int Kill P((int argc , char *argv [])); +int bg P((int argc , char *argv [])); +int fg P((int argc , char *argv [])); + +/* posixjob.c */ +void waitfor P((int pid )); +SIGTYPE stopjob P((int a)); + +/* main.c */ +void prprompt P((void )); +void leave_shell P((int how )); +void doline P((int isalias )); +int main P((int argc , char *argv [])); + +/* malloc.c */ +char *Malloc P((unsigned int size , char *mesg )); +void initmall P((void )); +void *myMalloc P((unsigned int size )); +void myFree P((void *ptr )); + +/* meta.c */ +void tilde P((char *word , char *dir )); +char *expline P((struct candidate *list )); +bool meta_1 P((char *old , bool mustmalc )); +void meta_2 P((void )); + +/* parse.c */ +int command P((int *waitpid , int makepipe , int *pipefdp , int anydups )); + +/* signal.c */ +void catchsig P((void )); +void dflsig P((void )); + +/* term.c */ +void getstty P((void )); +void setcbreak P((void )); +void setcooked P((void )); +void getstty P((void )); +void setcbreak P((void )); +void setcooked P((void )); +void getstty P((void )); +void setcbreak P((void )); +void setcooked P((void )); +void terminal P((void )); + +/* val.c */ +void appendval P((struct vallist *l , struct val *v )); +struct val *searchval P((struct vallist *l , char *name , int mode , bool sub )); +void saveval P((struct vallist *l , struct val *v )); +struct val *pullval P((struct vallist *l )); +void setval P((char *name , char *val , struct vallist *l )); + +/* var.c */ +char *EVget P((char *name )); +bool EVinit P((void )); +bool EVupdate P((void )); +int export P((int argc , char *argv [])); +int shift P((int argc , char *argv [])); +int unset P((int argc , char *argv [])); +int set P((int argc , char *argv [])); + + + +/* The following are defined so that gcc -Wall won't complain about + * missing prototypes - you may need to alter or comment them out + * for your own machine. Some of these were borrowed from Minix. + */ + + +#ifdef UNUSED +/* Ansi C routines */ + +int atoi P((CONST char *nptr)); +void free P((void *ptr)); +void *malloc P((size_t size)); +void *memcpy P((void *s1, CONST void *s2, size_t n)); +void *memset P((void *s, int c, size_t n)); +void qsort P((void *base, size_t nmemb, size_t size, + int (*compare)(CONST void *a, CONST void *b))); +void *realloc P((void *ptr, size_t size)); +char *strcat P((char *s1, CONST char *s2)); +char *strchr P((CONST char *s, int c)); +int strcmp P((CONST char *s1, CONST char *s2)); +char *strcpy P((char *s1, CONST char *s2)); +size_t strlen P((CONST char *s)); +int strncmp P((CONST char *s1, CONST char *s2, size_t n)); +char *strncpy P((char *s1, CONST char *s2, size_t n)); +char *strpbrk P((CONST char *s1, CONST char *s2)); +char *strrchr P((CONST char *s, int c)); + + +/* POSIX routines */ + +int chdir P((CONST char *path)); +int closedir P((DIR *dirp)); +int close P((int fd)); +int dup P((int fd)); +int dup2 P((int fd, int fd2)); +int execvp P((CONST char *file, char *argv[])); +void exit P((int status)); +pid_t fork P((void)); +char *getcwd P((char *buf, int size)); +pid_t getpid P((void)); +uid_t getuid P((void)); +struct passwd *getpwent P((void)); +struct passwd *getpwnam P((CONST char *name)); +struct passwd *getpwuid P((int uid)); +void endpwent P((void)); +int kill P((pid_t pid, int sig)); +struct tm *localtime P((CONST time_t *timer)); +off_t lseek P((int fd, off_t offset, int whence)); +int open P((CONST char *path, int oflag, mode_t modes)); +void perror P((CONST char *s)); +int pipe P((int fildes[2])); +int read P((int fd, char *buf, unsigned int n)); +int setpgid P((int pid, int pgid)); +int getpgrp P((int pid)); +SIGTYPE (*signal)(); +int stat P((CONST char *filename, struct stat *s)); +int tcgetattr P((int fd, struct termios *tptr)); +int tcsetattr P((int fd, int opt_actions, struct termios *tptr)); +int tcsetpgrp P((int fd, int pgrp)); +time_t time P((time_t *timeptr)); +pid_t wait P((int *stat_loc)); +pid_t waitpid P((pid_t pid, int *stat_loc, int options)); +int write P((int fd, CONST char *buf, unsigned int n)); +DIR *opendir P((CONST char *dirname)); +int closedir P((DIR *dirp)); +#ifdef USES_DIRECT +struct direct *readdir P((DIR *dirp)); +#else +struct dirent *readdir P((DIR *dirp)); +#endif + + +/* Other routines */ + +char *getcwd P((char *buf)); +int ioctl P((int fd, int request, ... )); +int setpgrp P((int pid, int pgid)); +int tgetent P((char *bp, CONST char *name)); +int tgetflag P((CONST char *id)); +int tgetnum P((CONST char *id)); +char *tgetstr P((CONST char *id, char **area)); +int wait3 P((union wait *status, int options, struct rusage *rusage)); +void bzero P((char *b1, int length)); +void bcopy P((CONST char *b1, char *b2, int length)); + +#endif /* UNUSED */ + +#undef P diff --git a/Applications/wish/signal.c b/Applications/wish/signal.c new file mode 100644 index 0000000000..83ea5d8504 --- /dev/null +++ b/Applications/wish/signal.c @@ -0,0 +1,66 @@ +/* Most of the functions that handle signals are kept in this file. + * Exceptions are some signal() calls done in job.c + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +/* Graceful is set to catch most of the unused signals in Wish. All it + * does is print out an error message and exit. + */ +#ifdef PROTO +static SIGTYPE graceful(int sig) +#else +static SIGTYPE +graceful(sig) + int sig; +#endif +{ + extern char *signame[]; + + setcooked(); + fprints(2, "Received SIG%s signal, # %d\n", signame[sig],sig); + exit(1); +} + + +/* Catchsig is called once at Wish startup, and it sets graceful to + * catch most of the signals unused by Wish. + */ +void +catchsig() +{ +#ifdef DEBUG + int i; + + for (i = 1; i <= MAXSIG; i++) + { + if (i != SIGKILL && i != SIGCONT) /* SIGKILL cannot be caught or ignored */ + signal(i, graceful); + } +#endif + + signal(SIGINT, SIG_IGN); /* Sometimes taken out for debugging */ + signal(SIGQUIT, SIG_IGN); + +#if defined(UCBJOB) || defined(POSIXJOB) + /* Also catch these for job control */ + signal(SIGTSTP, stopjob); + signal(SIGCHLD, SIG_IGN); +#endif +} + +/* Dflsig sets all the signals to their default handlers, so that exec'd + * programs will have a standard signal environment. + */ +void +dflsig() +{ + int i; + +#ifdef DEBUG + fprints(2,"%d signals defined\n",MAXSIG); +#endif + for (i = 1; i <= MAXSIG; i++) signal(i, SIG_DFL); +} diff --git a/Applications/wish/term.c b/Applications/wish/term.c new file mode 100644 index 0000000000..ad05f3eeef --- /dev/null +++ b/Applications/wish/term.c @@ -0,0 +1,314 @@ +/* This file contains routines for setting the terminal characteristics, + * and for getting the termcap strings. + * + * $Revision: 41.3 $ $Date: 2003/04/21 13:08:43 $ + */ + +#include "header.h" + +int beeplength; /* Strlen of beep */ +int wid; /* The width of the screen (minus 1) */ + + /* The termcap strings Wish uses */ +char *bs, *nd, *cl, *cd, *up, *so, *se, *wbeep; + +static char *termcapbuf; /* Buffer to get termcap strings */ + +/* Wish keeps the ``normal'' terminal characteristics (i.e the ones Wish's + * children see) in a structure, and restores the characteristics each + * time it forks. The structure depends upon the OS we are being compiled + * under. + */ + +#ifdef USES_TCGETA +static struct termio tbuf, tbuf2; + +#endif + +#ifdef USES_SGTTY +static struct sgttyb tbuf, tbuf2; +static struct tchars sbuf, sbuf2; + +# ifdef USES_MORESIG +static struct ltchars moresigc, moresigc2; +# endif +#endif + +#ifdef USES_TERMIOS +static struct termios tbuf, tbuf2; + +#endif + + +#ifdef DEBUG +/* Printctrl prints out a terminal sequence that Wish got + * from termcap. Only used when debugging the ensure Wish + * is actually getting the strings, and the right ones. + */ +static void +printctrl(name, str) + char *name, *str; +{ + int i; + + prints("%s: ", name); + for (i = 0; str[i]; i++) + if (str[i] > 31) + prints("%c",str[i]); + else + { + prints("%c",'^'); + if (str[i] > 26) + prints("%c",str[i] + 64); + else + prints("%c",str[i] + 96); + } + prints('\n'); +} + +#endif + + +/* There are 3 versions of getstty, setcooked and setcbreak, + * defined by USES_TCGETA, USES_TERMIOS and USES_SGTTY + */ + +#ifdef USES_TCGETA +/* Getstty: Get the current terminal structures for Wish */ +void +getstty() +{ + if (ioctl(0, TCGETA, &tbuf)) + perror("ioctl in getstty"); + memcpy(&tbuf2, &tbuf, sizeof(tbuf)); +} + +/* Setcbreak: Set terminal to cbreak mode */ +void +setcbreak() +{ + bool keepstty; + + if (EVget("Keepstty")) + keepstty = TRUE; + else + keepstty = FALSE; + + if (!keepstty) + getstty(); + tbuf2.c_lflag &= ~(ICANON | ECHO | ISIG); /* Turn off canonical input and echo */ + tbuf2.c_cc[4] = 1; /* read 1 char before returning like CBREAK */ + if (ioctl(0, TCSETA, &tbuf2)) + perror("ioctl in setcbreak"); +} + +/* Setcooked: Set terminal to cooked mode */ +void +setcooked() +{ + if (ioctl(0, TCSETA, &tbuf)) + perror("ioctl in setcooked"); +} + +#endif /* USES_TCGETA */ + + +#ifdef USES_TERMIOS +/* Getstty: Get the current terminal structures for Wish */ +void +getstty() +{ + if (tcgetattr(0, &tbuf)) + perror("tcgetattr in getstty"); + memcpy(&tbuf2, &tbuf, sizeof(tbuf)); +} + +/* Setcbreak: Set terminal to cbreak mode */ +void +setcbreak() +{ + int i; + bool keepstty; + + if (EVget("Keepstty")) + keepstty = TRUE; + else + keepstty = FALSE; + + if (!keepstty) + getstty(); + /* Turn off canonical input and echo */ + tbuf2.c_lflag = tbuf2.c_lflag & (~ICANON) & (~ECHO); + for (i=0; i< NCCS; i++) tbuf2.c_cc[i]=0; + tbuf2.c_cc[VMIN] = 1; /* read 1 char before returning like CBREAK */ + if (tcsetattr(0, TCSANOW, &tbuf2)) + perror("cbreak tcsetattr"); +} + +/* Setcooked: Set terminal to cooked mode */ +void +setcooked() +{ + if (tcsetattr(0, TCSANOW, &tbuf)) + perror("cooked tcsetattr"); +} + +#endif /* USES_TERMIOS */ + + +#ifdef USES_SGTTY +/* Getstty: Get the current terminal structures for Wish */ +void +getstty() +{ + if (ioctl(0, TIOCGETP, &tbuf))/* get the sgttyb struct */ + perror("ioctl2 in getstty"); + if (ioctl(0, TIOCGETC, &sbuf))/* get the tchars struct */ + perror("ioctl3 in getstty"); +#ifdef USES_MORESIG + bcopy(&sbuf, &sbuf2, sizeof(sbuf)); /* and copy them so we can change */ + bcopy(&tbuf, &tbuf2, sizeof(tbuf)); + if (ioctl(0, TIOCGLTC, &moresigc)) /* get the ltchars struct */ + perror("ioctl4 in getstty"); + bcopy(&moresigc, &moresigc2, sizeof(moresigc)); +#else + memcpy(&sbuf2, &sbuf, sizeof(sbuf)); /* and copy them so we can change */ + memcpy(&tbuf2, &tbuf, sizeof(tbuf)); +#endif +} + +/* Setcbreak: Set terminal to cbreak mode */ +void +setcbreak() +{ + bool keepstty; + + if (EVget("Keepstty")) + keepstty = TRUE; + else + keepstty = FALSE; + + if (!keepstty) + getstty(); + /* setup terminal with ioctl calls */ + + tbuf2.sg_flags |= CBREAK; /* cbreak mode to get each char */ + tbuf2.sg_flags &= (~ECHO); /* do not echo chars to screen */ + if (ioctl(0, TIOCSETP, &tbuf2)) /* put it back, modified */ + perror("ioctl1 su"); + sbuf2.t_intrc = (UNDEF); /* no interrupt or quitting */ + /* sbuf2.t_quitc=(UNDEF); Allow quit while debugging */ + sbuf2.t_eofc = (UNDEF); /* or eof signalling */ + if (ioctl(0, TIOCSETC, &sbuf2)) /* put it back, modified */ + perror("ioctl2 scb"); +#ifdef USES_MORESIG + moresigc2.t_suspc = (UNDEF); /* no stopping */ + moresigc2.t_dsuspc = (UNDEF); /* or delayed stopping */ + moresigc2.t_rprntc = (UNDEF); /* or reprinting */ + moresigc2.t_flushc = (UNDEF); /* or flushing */ + moresigc2.t_werasc = (UNDEF); /* or word erasing */ + moresigc2.t_lnextc = (UNDEF); /* or literal quoting */ + if (ioctl(0, TIOCSLTC, &moresigc2)) /* put it back, modified */ + perror("ioctl3"); +#endif +} + + +/* Setcooked: Set terminal to cooked mode */ +void +setcooked() +{ + if (ioctl(0, TIOCSETP, &tbuf)) + perror("ioctl1 sd"); + if (ioctl(0, TIOCSETC, &sbuf)) + perror("ioctl2 sd"); +#ifdef USES_MORESIG + if (ioctl(0, TIOCSLTC, &moresigc)) /* set ltchars struct to be default */ + perror("ioctl3 in setcooked"); +#endif +} + +#endif /* USES_SGTTY */ + + +/* gettstring: Given the name of a termcap string, gets the string + * and places it into loc. Returns 1 if ok, 0 if no string. + * If no string, loc[0] is set to EOS. + */ +#ifdef PROTO +static bool gettstring(char *name, char **loc) +#else +static bool +gettstring(name, loc) + char *name, **loc; +#endif +{ + char bp[50], *area = bp; + + if (tgetstr(name, &area) != NULL) + { + area = bp; + while (isdigit(*area)) + area++; /* Skip time delay chars */ + *loc = (char *) malloc((unsigned) strlen(area) + 2); + if (!(*loc)) + return (FALSE); + strcpy(*loc, area); + return (TRUE); + } + else + *loc = ""; + return (FALSE); +} + + +/* Terminal is called at the beginning of Wish to get the termcap + * strings needed by the Command Line Editor. If they are not got, + * or the wrong ones are found, the CLE will act strangely. Wish + * should at this stage default to a dumber CLE. + */ +void +terminal() +{ + extern int beeplength; + char *t, term[20]; + +/* set up cursor control sequences from termcap */ + + t=EVget("TERM"); + if (!t) fatal("No termcap entry available"); + strncpy(term, t, 10); + termcapbuf = (char *) malloc(2048); + if (!termcapbuf) + return; + tgetent(termcapbuf, term); + if (tgetflag("bs") == 1) + bs = "\b"; + else + gettstring("bc", &bs); + if ((wid = tgetnum("co")) == -1) + wid = 80; + wid--; /* this is to eliminate unwanted auto newlines */ + gettstring("cl", &cl); + gettstring("cd", &cd); + gettstring("nd", &nd); + gettstring("up", &up); + gettstring("so", &so); + gettstring("se", &se); + gettstring("bl", &wbeep); + if (*wbeep == EOS) + + wbeep = "\007"; + beeplength = strlen(wbeep); +#ifdef DEBUG + printctrl("bs", bs); + printctrl("cl", cl); + printctrl("cd", cd); + printctrl("nd", nd); + printctrl("up", up); + printctrl("so", so); + printctrl("se", se); + printctrl("beep", wbeep); +#endif + free(termcapbuf); +} diff --git a/Applications/wish/ucbjob.c b/Applications/wish/ucbjob.c new file mode 100644 index 0000000000..81f419a446 --- /dev/null +++ b/Applications/wish/ucbjob.c @@ -0,0 +1,160 @@ +/* 4.3BSD Job Control functions + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + * + */ + +#define WIFCORE(x) ((x).w_coredump) +#ifndef WEXITSTATUS +# define WEXITSTATUS(x) ((x).w_retcode) +#endif +#ifndef WTERMSIG +# define WTERMSIG(x) ((x).w_termsig) +#endif +#ifndef WSTOPSIG +# define WSTOPSIG(x) ((x).w_stopsig) +#endif +#define WRUNFG(x) ((x).w_status == RUNFG) +#define WRUNBG(x) ((x).w_status == RUNBG) + +/* The following tables are all VERY OS-dependent, especially on POSIX + * systems. If you find the shell giving you weird signal messages, you + * should change the names of the signals below. + */ + +static char *siglist[] = { + "", "Hangup", "Interrupt", "Quit", "Illegal Instruction", + "Trace/BPT Trap", "IOT Trap", "EMT Trap", "Floating Point Exception", + "Killed", "Bus Error", "Segmentation Violation", "Bad System Call", + "Broken Pipe", "Alarm", "Terminated" ,"Urgent Socket Condition", + "Stopped (signal)", "Stopped", "Continue", "Child Status Change", + "(tty input)", "(tty output)", "I/O", "Cpu Time Limit", + "File Size Limit", "Virtual Time Alarm", "Profile Alarm", + "Window Change", "Resource Lost", "User Signal 1", "User Signal 2" +}; + +char *signame[] = { + "", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", + "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM" ,"URG", "STOP", "TSTP", + "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU", "XFSZ", "VTALRM", "PROF", + "WINCH", "LIST", "USR1", "USR2", NULL +}; + +/* Waitfor is called to wait for a requested job to stop/die. It receives the + * new status of any children. If it's the requested job, we return. Pid holds + * the process-id of the job we are after; if it is 0, we return when no + * processes are left waiting to report a change in state. + * + * If pid is negative, we are a subshell and _must_ return only when the pid + * dies. We set the subshell flag and make pid positive again. Then, if we + * find out that the job was stopped, we wake it up. This works because we + * are put to sleep at the same time as the child, and the original shell + * fg()s _us_. + */ +void +waitfor(pid) + int pid; +{ + struct job *thisjob; + int wpid; + bool subshell=FALSE; + + union wait status; + + int waitflags; + waitflags = (pid == 0) ? WNOHANG | WUNTRACED : WUNTRACED; + +#ifdef DEBUG + fprints(2,"In waitfor\n"); +#endif + + if (pid<0) { pid= -pid; subshell= TRUE; } + while (1) + { + wpid = wait3(&status, waitflags, NULL); + +#ifdef DEBUG + fprints(2,"waitxx() returned\n"); +#endif + if (wpid == -1 || wpid == 0) + break; + + if (subshell && WIFSTOPPED(status)) /* We can't return yet */ + { + kill(wpid, SIGCONT); /* Wake the child up first - waah! */ + continue; + } + thisjob = findjob(wpid); + if (thisjob == NULL) + continue; + thisjob->status = status; + thisjob->changed = TRUE; + if (pid == wpid) + return; + } +} + +/* Stopjob is only called when we received a SIGTSTP. If we are wait3()ing + * a pid, we send a SIGSTOP to that pid. This should then cause wait3() + * to exit, and thus waitfor() will return. + */ +SIGTYPE +stopjob() +{ + signal(SIGTSTP, stopjob); +#ifdef DEBUG + fprints(2,"In stopjobs\n"); +#endif + if (currentjob) + { +#ifdef DEBUG + fprints(2,"About to stop pid %d\n", currentjob->pid); +#endif + kill(currentjob->pid, SIGSTOP); + } +} + + +static void +bgstuff(pid) + int pid; +{ + struct job *ptr; + + setpgrp(pid, pid); +# ifdef DEBUG + fprints(2,"About to SIGCONT %d\n", pid); +# endif + kill(pid, SIGCONT); + ptr = findjob(pid); + if (ptr) + { ptr->STATUS = RUNBG; currentjob= ptr; } +} + + +static int +fgstuff(pid) + int pid; +{ + int pgrp; + +/* Under UCBJOB and POSIXJOB, we don't try & bring the pid into our + * pgrp, that doesn't work. Instead, we move the terminal + * over to that pgrp, and move ourselves to that pgrp as well. + */ + pgrp = getpgrp(pid); /* Determine if job's pgrp != ours */ + if (pgrp == pid) + { + if (ioctl(0, TIOCSPGRP, &pgrp) == -1) /* Move the terminal to that pgrp */ + { + perror("fg setpgrp ioctl"); + return (1); + } + if (setpgrp(getpid(), pgrp) == -1) /* Set shell's process group to the pid's */ + { + perror("fg setpgrp"); + return (1); + } + } + return(0); +} diff --git a/Applications/wish/v7job.c b/Applications/wish/v7job.c new file mode 100644 index 0000000000..635f49e18a --- /dev/null +++ b/Applications/wish/v7job.c @@ -0,0 +1,162 @@ +/* 7th Edition Job Control functions + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + * + */ +#ifndef WIFSTOPPED +#define WIFSTOPPED(x) (((x) & 0xFF) == 0x7F) /* As per my Version7 manual */ +#define WIFSIGNALED(x) ((x) & 0xFF) +#define WIFEXITED(x) (((x) & 0xFF) == 0) +#define WEXITSTATUS(x) ((x) >> 8) +#define WTERMSIG(x) ((x) & 0xFF) +#define WSTOPSIG(x) ((x) >> 8) +#endif +#define WIFCORE(x) ((x) & 0x80) +#define WRUNFG(x) ((x) == RUNFG) +#define WRUNBG(x) ((x) == RUNBG) + +/* The following tables are all VERY OS-dependent, especially on POSIX + * systems. If you find the shell giving you weird signal messages, you + * should change the names of the signals below. + */ +static char *siglist[] = { + "", "Hangup", "Interrupt", "Quit", "Illegal Instruction", + "Trace/BPT Trap", "IOT Trap", "EMT Trap", "Floating Point Exception", + "Killed", "Bus Error", "Segmentation Violation", "Bad System Call", + "Broken Pipe", "Alarm", "Terminated", "User Signal 1", "User Signal 2", + "Child Death", "Power Failure" +}; + +char *signame[] = { + "", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", + "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM", "USR1", "USR2", "CLD", + "PWR", NULL +}; + + +/* Waitfor is called to wait for a requested job to stop/die. It receives the + * new status of any children. If it's the requested job, we return. Pid holds + * the process-id of the job we are after; if it is 0, we return when no + * processes are left waiting to report a change in state. + * + * If pid is negative, we are a subshell and _must_ return only when the pid + * dies. We set the subshell flag and make pid positive again. Then, if we + * find out that the job was stopped, we wake it up. This works because we + * are put to sleep at the same time as the child, and the original shell + * fg()s _us_. + */ +void +waitfor(pid) + int pid; +{ + struct job *thisjob; + int wpid; + bool subshell=FALSE; + int stopsig; + + int status; + +#ifdef DEBUG + fprints(2,"In waitfor\n"); +#endif + + if (pid<0) { pid= -pid; subshell= TRUE; } + while (1) + { + if (pid == 0) + return; + wpid = wait(&status); + +#ifdef DEBUG + fprints(2,"waitxx() returned with %x\n",status); +#endif + if (wpid == -1 || wpid == 0) + break; + + thisjob = findjob(wpid); + if (thisjob == NULL) + continue; + +/* Version 7 job control is quite interesting. Since we don't have a ^Z + * key, we use the ^\ key instead. Luckily this is caught by the parent + * and stops the process. When any signal is caught by the child, it + * stops. We must arrange to stop it on SIGQUIT (^\), and restart it on + * any other signal. + */ + if (WIFSTOPPED(status)) + { + stopsig= WSTOPSIG(status); +#ifdef DEBUG + fprints(2,"Stopped status %x, stopsig %d (%s)\n", + status,stopsig,signame[stopsig]); +#endif + if (stopsig!=SIGQUIT) + { + if (stopsig== SIGTRAP) + { +#ifdef DEBUG + fprints(2,"About to ptrace(7,%d,1,0)\n",wpid); +#endif + ptrace(7,wpid,(PLONG)1,(PLONG)0); /* Keep it going after exec */ + } + else + { +#ifdef DEBUG + fprints(2,"About to ptrace(7,%d,1,stopsig)\n",wpid); +#endif + ptrace(7,wpid,(PLONG)1,(PLONG)stopsig); /* Deliver the signal */ + } + continue; + } + } /* SIGQUIT falls out to below where job is marked stopped */ + + thisjob->status = status; + thisjob->changed = TRUE; + if (pid == wpid) + return; + } +} + +#ifdef PROTO +static void bgstuff(int pid) +#else +static void +bgstuff(pid) + int pid; +#endif +{ + struct job *ptr; + +#ifdef DEBUG + fprints(2,"About to ptrace(7,%d,1,0)\n", pid); +#endif + ptrace(7,pid,(PLONG)1,(PLONG)0); /* Start it up again */ + if (ptr) + { ptr->STATUS = RUNBG; currentjob= ptr; } +} + +#ifdef PROTO +static int fgstuff(int pid) +#else +static int +fgstuff(pid) + int pid; +#endif +{ + struct job *ptr; + + ptr=findjob(pid); + +/* We need to wake up stopped jobs before we can wait on them */ + if ((ptr) && WIFSTOPPED(ptr->status)) + { +#ifdef DEBUG + fprints(2,"About to ptrace(7,%d,1,0)\n", pid); +#endif + ptrace(7,pid,(PLONG)1,(PLONG)0); /* Start it up again */ + } + + ptr->STATUS = RUNFG; + currentjob = ptr; + return (0); +} diff --git a/Applications/wish/val.c b/Applications/wish/val.c new file mode 100644 index 0000000000..ca55f1baad --- /dev/null +++ b/Applications/wish/val.c @@ -0,0 +1,134 @@ +/* Functions dealing with lists of values + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +void +appendval(l, v) + struct vallist *l; + struct val *v; +{ + v->next = NULL; + if (l->head == NULL) l->head = l->tail = v; + else + { l->tail->next = v; l->tail = v; } +} + + + +/* Searchval searches through a val list for a given val. + * When a position is found, searchval operates according + * to the mode value: if true, return the val, else delete + * the val. If sub is true, substring matches are used. + */ + +struct val * +searchval(l, name, mode, sub) + struct vallist *l; + char *name; + int mode; + bool sub; +{ + struct val *last, *this; + int len; + + switch (sub) + { + case TRUE: + len = strlen(name); + for (last = this = l->head; this; last = this, this = this->next) + if (!strncmp(this->name, name, len)) + break; + break; + case FALSE: + for (last = this = l->head; this; last = this, this = this->next) + if (!strcmp(this->name, name)) + break; + break; + default: + fatal("Bad sub value in searchval"); + } + if (this == NULL) return (NULL); + + switch (mode) + { + case FALSE: + if (l->head == l->tail) + { l->head = l->tail = NULL; return (this); } + + if (this == l->head) l->head = this->next; + else last->next = this->next; + + if (this == l->tail) l->tail = last; + this->next = NULL; + case TRUE: + return (this); + default: + fatal("Bad mode value in searchval"); + } +} + +/* Save the value into the list in the correct order + */ +void +saveval(l, v) + struct vallist *l; + struct val *v; +{ + struct val *last, *this; + + if (l->head == NULL) + { l->head = l->tail = v; v->next = NULL; return; } + + for (last = this = l->head; this; last = this, this = this->next) + { if (!strcmp(this->name, v->name)) + { free(this->val); free(v->name); /* Overwrite the old val */ + this->val = v->val; this->exported = v->exported; + free(v); return; + } + if (strcmp(this->name, v->name) > 0) break; + } + + if (this == NULL) + { l->tail->next = v; v->next = NULL; + l->tail = v; return; + } + + if (this == last) + { v->next = l->head; l->head = v; return; } + + v->next = this; last->next = v; +} + +struct val * +pullval(l) + struct vallist *l; +{ + struct val *temp; + + if (l->head == NULL) return (NULL); + + temp = l->head; + if (l->head == l->tail) + l->head = l->tail = NULL; + else + l->head = l->head->next; + temp->next = NULL; + return (temp); +} + +void +setval(name, val, l) + char *name, *val; + struct vallist *l; +{ + struct val *v; + + v = (struct val *) Malloc(sizeof(struct val), "setval val malloc"); + v->name = Malloc(strlen(name) + 1, "setval name malloc"); + v->val = Malloc(strlen(val) + 1, "setval val malloc"); + strcpy(v->name, name); strcpy(v->val, val); + v->exported = FALSE; saveval(l, v); +} diff --git a/Applications/wish/var.c b/Applications/wish/var.c new file mode 100644 index 0000000000..142619d94a --- /dev/null +++ b/Applications/wish/var.c @@ -0,0 +1,201 @@ +/* Variables + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +struct vallist vlist= /* The linked list of vars */ + { NULL, NULL}; + +char * +EVget(name) /* Get value of variable */ + char *name; +{ + struct val *v; + + if ((v = searchval(&vlist, name, TRUE, FALSE)) == NULL || v->name == NULL) + return (NULL); + return (v->val); +} + + +bool +EVinit() /* Initialise symtable from environment */ +{ + extern char **environ; + int i; + char c, *name, *val; + struct val *v; + + for (i = 0; environ[i] != NULL; i++) + { + name = environ[i]; + val = strchr(name, '='); + c = *val; + *(val++) = '\0'; + v= (struct val *)Malloc(sizeof(struct val), "EVinit val malloc"); + v->name= Malloc(strlen(name)+1, "EVinit name malloc"); + v->val= Malloc(strlen(val)+1, "EVinit val malloc"); + strcpy(v->name, name); strcpy(v->val, val); + v->exported=TRUE; + saveval(&vlist, v); + *(--val) = c; + } + return (TRUE); +} + +#ifndef NO_VAR +bool +EVupdate() /* Build envp from symbol table */ +{ + extern char **environ; + int i, envi, nvlen; + struct val *v; + static bool updated = FALSE; + + for (i = 0, v = vlist.head; v != NULL; v = v->next) + i += v->exported; + + if (!updated) + if ((environ = (char **) malloc((i + 1) * sizeof(char *))) == NULL) + return (FALSE); + envi = 0; + for (v = vlist.head; v != NULL; v = v->next) + { + if (v->name == NULL || !v->exported) + continue; + nvlen = strlen(v->name) + strlen(v->val) + 2; + if (!updated) + { + if ((environ[envi] = (char *) malloc(nvlen)) == NULL) + return (FALSE); + } + else if ((environ[envi] = (char *) realloc(environ[envi], nvlen)) == NULL) + return (FALSE); + sprints(environ[envi], "%s=%s", v->name, v->val); + envi++; + } + environ[envi] = NULL; + updated = TRUE; + return (TRUE); +} + +int +export(argc, argv) /* Export command */ + int argc; + char *argv[]; +{ + int i; + struct val *v; + + if (argc < 2) + { + prints("Usage: export var [var] ...\n"); + return (1); + } + for (i = 1; i < argc; i++) + { + v = searchval(&vlist, argv[i], TRUE, FALSE); /* Setenv */ + if (v == NULL) + prints("No such variable: %s\n", argv[i]); + else + v->exported = TRUE; + } + return (0); +} + +int +shift(argc, argv) + int argc; + char *argv[]; +{ + extern int Argc; + extern char **Argv; + int i = 1; + + if (argc > 2) + { + prints("Usage: shift [val]\n"); + return (1); + } + if (argc == 2) + i = atoi(argv[1]); + if (i > Argc) + { + prints("Not enough vars to shift\n"); + return (1); + } + Argv += i; + Argc -= i; + return (0); +} + +int +unset(argc, argv) + int argc; + char *argv[]; +{ + int i, j; + struct val *v; + + j = strcmp(argv[0], "unexport"); + if (argc < 2) + { + prints("Usage: %s [var] [var] ...\n", argv[0]); + return (1); + } + + for (i = 1; i < argc; i++) + switch (j) + { + case 0: + v = searchval(&vlist, argv[i], TRUE, FALSE); /* Unsetenv */ + if (v == NULL) + prints("No such variable: %s\n", argv[i]); + else + v->exported = FALSE; + break; + default: + if (!searchval(&vlist, argv[i], FALSE, FALSE)) + prints("No such variable: %s\n", argv[i]); + } + return (0); +} + +int +set(argc, argv) + int argc; + char *argv[]; +{ + struct val *v; + + if ((argc !=1) && (argc!=3)) + { prints("Usage: %s variable value, or %s\n", argv[0], argv[0]); return (1); } + + if (argc==3) + { setval(argv[1], argv[2], &vlist); + if (!strcmp(argv[0], "setenv")) + { + v = searchval(&vlist, argv[1], TRUE, FALSE); /* Setenv */ + if (v != NULL) v->exported = TRUE; + } + } + else + { + if (!strcmp(argv[0], "setenv")) + { + for (v = vlist.head; v != NULL; v = v->next) + if (v->name != NULL && v->exported) + prints("%s=%s\n", v->name, v->val); + } + else + { + for (v = vlist.head; v != NULL; v = v->next) + if (v->name != NULL) + prints("%s=%s\n", v->name, v->val); + } + } + return (0); +} +#endif /* NO_VAR */ diff --git a/Applications/wish/wish b/Applications/wish/wish new file mode 100644 index 0000000000000000000000000000000000000000..afb8aa402583f2aabdf70ad9efa7a51d68eb5196 GIT binary patch literal 46868 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�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

!@3&DgscbGMEyV9c+pQHLh5 z{p(H;`)fCRAokY-7{m_j!CsKZ*N1ToC-6Pa;5;tkDz0M$cW@8)@fc6>94~q9Odt}` zh($aSk&IL*NJkcOkdHzvMhVJLfojyD0ZnK@8#>{D6K?pRqYnevib3qaPVB*69KazQ l!x@~%MO?>CjNlIL;yGUOUT=X&L_pFz+V6W literal 0 HcmV?d00001 From 763a6eef800873f962ca138fea16937d335898dc Mon Sep 17 00:00:00 2001 From: Warren Toomey Date: Wed, 22 Jan 2025 14:52:23 +1000 Subject: [PATCH 9/9] Applications/wish/wish: Oops, removed the binary. --- Applications/wish/wish | Bin 46868 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Applications/wish/wish diff --git a/Applications/wish/wish b/Applications/wish/wish deleted file mode 100644 index afb8aa402583f2aabdf70ad9efa7a51d68eb5196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46868 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�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

!@3&DgscbGMEyV9c+pQHLh5 z{p(H;`)fCRAokY-7{m_j!CsKZ*N1ToC-6Pa;5;tkDz0M$cW@8)@fc6>94~q9Odt}` zh($aSk&IL*NJkcOkdHzvMhVJLfojyD0ZnK@8#>{D6K?pRqYnevib3qaPVB*69KazQ l!x@~%MO?>CjNlIL;yGUOUT=X&L_pFz+V6W