├── .gitignore ├── CMakeLists.txt ├── README.md ├── TODO ├── blockdevice ├── README.md ├── include │ └── blockdevice │ │ ├── blockdevice.h │ │ ├── fileblockdevice.h │ │ └── flashblockdevice.h └── src │ ├── fileblockdevice.c │ └── flashblockdevice.c ├── console ├── README.md ├── include │ └── console │ │ ├── console.h │ │ └── console_stdio_vt100.h └── src │ └── console_stdio_vt100.c ├── cpicom_screenshot_zork.png ├── cpm ├── README.md ├── include │ └── cpm │ │ ├── defs.h │ │ └── limits.h └── src │ ├── bdos.c │ ├── bios.c │ ├── cpm.inc │ ├── cpmdisc.h │ ├── intf.c │ ├── vt.c │ ├── vt.h │ └── z80.c ├── docs └── filesystem_compat_layer.odg ├── error ├── README.md ├── include │ └── error │ │ └── error.h └── src │ └── error.c ├── files ├── README.md ├── include │ └── files │ │ ├── compat.h │ │ ├── filecontext.h │ │ ├── filesystemmount.h │ │ ├── fileutil.h │ │ └── limits.h └── src │ ├── compat.c │ ├── filecontext.c │ ├── filesystemmount.c │ └── fileutil.c ├── fs ├── README.md ├── include │ └── fs │ │ ├── ff.h │ │ ├── ffconf.h │ │ ├── ffdiskio.h │ │ ├── filesystem.h │ │ ├── filesystemfat.h │ │ ├── filesystemlfs.h │ │ ├── lfs.h │ │ └── lfs_util.h └── src │ ├── ff.c │ ├── ffdiskio.c │ ├── ffsystem.c │ ├── filesystemfat.c │ ├── filesystemlfs.c │ ├── lfs.c │ └── lfs_util.c ├── klib ├── README.md ├── include │ └── klib │ │ ├── defs.h │ │ ├── list.h │ │ └── string.h └── src │ ├── list.c │ └── string.c ├── log ├── include │ └── log │ │ └── log.h └── src │ └── log.c ├── main ├── README.md └── main.c ├── misc └── cpm22disk.tar ├── pico_sdk_import.cmake ├── picocpm ├── README.md ├── include │ └── picocpm │ │ ├── config.h │ │ └── picocpm.h └── src │ ├── config.c │ └── picocpm.c ├── shell ├── README.md ├── include │ └── shell │ │ └── shell.h └── src │ ├── cmd_dir.c │ ├── cmd_dump.c │ ├── cmd_era.c │ ├── cmd_format.c │ ├── cmd_log.c │ ├── cmd_rename.c │ ├── cmd_setdef.c │ ├── cmd_stat.c │ ├── cmd_type.c │ ├── cmd_untar.c │ ├── cmd_yrecv.c │ ├── cmd_ysend.c │ └── shell.c └── ymodem ├── README.md ├── include └── ymodem │ └── ymodem.h └── src └── ymodem.c /.gitignore: -------------------------------------------------------------------------------- 1 | build_arm 2 | build_pico 3 | build_host 4 | samples 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.13) 2 | # Uncomment the following line to compile on PICO_W (With WiFi chip) 3 | # This is needed because PICO_W does its onboard LED differently 4 | #set (PICO_BOARD "pico_w") 5 | set (BINARY "cpicom") 6 | set (PROJ "cpicom") 7 | set (CMAKE_C_FLAGS_RELEASE "-Wall -Wextra") 8 | set (CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra") 9 | include (pico_sdk_import.cmake) 10 | project (${PROJ}) 11 | file (GLOB log_src CONFIGURE_DEPENDS "log/src/*.c") 12 | file (GLOB console_src CONFIGURE_DEPENDS "console/src/*.c") 13 | file (GLOB files_src CONFIGURE_DEPENDS "files/src/*.c") 14 | file (GLOB error_src CONFIGURE_DEPENDS "error/src/*.c") 15 | file (GLOB shell_src CONFIGURE_DEPENDS "shell/src/*.c") 16 | file (GLOB klib_src CONFIGURE_DEPENDS "klib/src/*.c") 17 | file (GLOB ymodem_src CONFIGURE_DEPENDS "ymodem/src/*.c") 18 | file (GLOB cpm_src CONFIGURE_DEPENDS "cpm/src/*.c") 19 | file (GLOB picocpm_src CONFIGURE_DEPENDS "picocpm/src/*.c") 20 | file (GLOB blockdev_src CONFIGURE_DEPENDS "blockdevice/src/*.c") 21 | file (GLOB fs_src CONFIGURE_DEPENDS "fs/src/*.c") 22 | pico_sdk_init() 23 | add_executable (${BINARY} ${log_src} ${error_src} ${console_src} ${files_src} ${shell_src} ${klib_src} ${ymodem_src} ${cpm_src} ${picocpm_src} ${blockdev_src} ${fs_src} "main/main.c") 24 | target_include_directories (${BINARY} PUBLIC log/include error/include console/include files/include shell/include klib/include ymodem/include cpm/include picocpm/include blockdevice/include fs/include) 25 | pico_enable_stdio_usb (${BINARY} 1) 26 | pico_enable_stdio_uart (${BINARY} 0) 27 | pico_add_extra_outputs (${BINARY}) 28 | if (PICO_ON_DEVICE) 29 | if (PICO_BOARD STREQUAL "pico_w") 30 | add_definitions( -DPICO_W ) 31 | target_link_libraries (${BINARY} pico_stdlib hardware_flash hardware_sync pico_cyw43_arch_none) 32 | else() 33 | target_link_libraries (${BINARY} pico_stdlib hardware_flash hardware_sync) 34 | endif() 35 | else() 36 | target_link_libraries (${BINARY} pico_stdlib) 37 | endif() 38 | 39 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Most the of the built-in shell commands are crude, and need further work. 2 | 3 | Implement a program search path. 4 | 5 | Some work needs to be done on buffering character output. Repeated calls 6 | to putch() with single characters are absurdly slow on the Pico. That's 7 | perhaps not a huge deal in something that is generally slow, but it's still 8 | not very elegant. However, my attempts to buffer character data internally, 9 | and send it in bursts, have not been hugely successful. There are all 10 | kinds of locking complications, and I suspect there are problem in that 11 | area in the USB library. WordStar fails with buffering enabled, and the 12 | particular feature of that program is it does a character read after 13 | every write. The connection remains unclear to me. 14 | 15 | TYPE command doesn't page. 16 | 17 | ASM has been know to crash the emulator, under circumstances I haven't 18 | been able to reproduce. 19 | 20 | CLS function 21 | 22 | DIR should be able to match filename wildcard argument (STAT can) 23 | 24 | FAT filesystem support needs to be tested with different block sizes 25 | and block counts. In particular, it's not clear whether to use 26 | FAT or FAT32, and which works best in particular configurations. 27 | -------------------------------------------------------------------------------- /blockdevice/README.md: -------------------------------------------------------------------------------- 1 | # blockdevice 2 | 3 | This directory contains implementations of the various filesystems that 4 | CPICOM supports. The behaviour of all filesystems is defined by the 5 | function contract set out if filesystem.h. 6 | -------------------------------------------------------------------------------- /blockdevice/include/blockdevice/blockdevice.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/blockdevice.h 6 | 7 | Defines the function pointers for all the block device operations, 8 | and the BlockDevice structure that is filled in by a specific 9 | device handler. 10 | 11 | Copyright (c)2001 Kevin Boone, GPL v3.0 12 | 13 | =========================================================================*/ 14 | 15 | #pragma once 16 | 17 | #include 18 | #include "files/limits.h" 19 | #include "error/error.h" 20 | 21 | typedef Error (*blockdevice_sync_fn) 22 | (void *context, int start_block, int count); 23 | typedef Error (*blockdevice_read_fn) 24 | (void *context, uint8_t *buffer, uint32_t block, uint32_t off, 25 | uint32_t size); 26 | typedef Error (*blockdevice_write_fn) 27 | (void *context, const uint8_t *buffer, uint32_t block, uint32_t off, 28 | uint32_t size); 29 | typedef Error (*blockdevice_erase_fn) 30 | (void *context, uint32_t block); 31 | 32 | typedef struct _BlockDeviceParams 33 | { 34 | char desc [BLOCKDEVICEPARAMS_MAX_DESC + 1]; 35 | uint32_t block_size; 36 | uint32_t block_count; 37 | blockdevice_sync_fn sync_fn; 38 | blockdevice_read_fn read_fn; 39 | blockdevice_write_fn write_fn; 40 | blockdevice_erase_fn erase_fn; 41 | void *context; 42 | } BlockDeviceParams; 43 | 44 | -------------------------------------------------------------------------------- /blockdevice/include/blockdevice/fileblockdevice.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | fileblockdevice.h 6 | 7 | Specification of the functions provided by the file block device 8 | handler. These functions satisfy the contract set out in 9 | blockdevice.h 10 | 11 | Copyright (c)2001 Kevin Boone, GPL v3.0 12 | 13 | =========================================================================*/ 14 | #pragma once 15 | 16 | #include "error/error.h" 17 | #include "blockdevice/blockdevice.h" 18 | 19 | struct _FileBlockDevice; 20 | typedef struct _FileBlockDevice FileBlockDevice; 21 | 22 | extern FileBlockDevice *fileblockdevice_create (const char *filename); 23 | extern void fileblockdevice_destroy (FileBlockDevice *self); 24 | extern Error fileblockdevice_initialize (FileBlockDevice *self); 25 | extern Error fileblockdevice_sync_fn (void *self, 26 | int start, int end); 27 | extern void fileblockdevice_get_params 28 | (FileBlockDevice *self, 29 | BlockDeviceParams *params); 30 | 31 | 32 | -------------------------------------------------------------------------------- /blockdevice/include/blockdevice/flashblockdevice.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/flashblockdevice 6 | 7 | An implementation of a block device in Pico flash memory 8 | 9 | Copyright (c)2001 Kevin Boone, GPL v3.0 10 | 11 | =========================================================================*/ 12 | 13 | #pragma once 14 | 15 | #include "error/error.h" 16 | #include "blockdevice/blockdevice.h" 17 | 18 | struct _FlashBlockDevice; 19 | typedef struct _FlashBlockDevice FlashBlockDevice; 20 | 21 | extern FlashBlockDevice *flashblockdevice_create (uint32_t flash_start, 22 | uint32_t pages); 23 | extern void flashblockdevice_destroy (FlashBlockDevice *self); 24 | extern Error flashblockdevice_initialize (FlashBlockDevice *self); 25 | extern Error flashblockdevice_sync_fn (void *self, 26 | int start, int end); 27 | extern void flashblockdevice_get_params 28 | (FlashBlockDevice *self, 29 | BlockDeviceParams *params); 30 | 31 | 32 | -------------------------------------------------------------------------------- /blockdevice/src/fileblockdevice.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/blockdevice.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "blockdevice/fileblockdevice.h" 18 | #include "log/log.h" 19 | 20 | #if PICO_ON_DEVICE 21 | #else 22 | 23 | #define BLOCK_SIZE 256 24 | 25 | struct _FileBlockDevice 26 | { 27 | char *filename; 28 | int fd; 29 | int size; 30 | }; 31 | 32 | // 33 | // fileblockdevice_create 34 | // 35 | FileBlockDevice *fileblockdevice_create (const char *filename) 36 | { 37 | FileBlockDevice *self = malloc (sizeof (FileBlockDevice)); 38 | self->filename = strdup (filename); 39 | self->fd = -1; 40 | return self; 41 | } 42 | 43 | // 44 | // fileblockdevice_destroy 45 | // 46 | void fileblockdevice_destroy (FileBlockDevice *self) 47 | { 48 | if (self) 49 | { 50 | if (self->fd >= 0) close (self->fd); 51 | if (self->filename) free (self->filename); 52 | free (self); 53 | } 54 | } 55 | 56 | // 57 | // fileblockdevice_initialize 58 | // 59 | Error fileblockdevice_initialize (FileBlockDevice *self) 60 | { 61 | Error ret = 0; 62 | 63 | self->fd = open (self->filename, O_RDWR); 64 | if (self->fd >= 0) 65 | { 66 | int l = lseek (self->fd, 0, SEEK_END); 67 | lseek (self->fd, 0, SEEK_SET); 68 | self->size = l; 69 | log_debug ("Block file '%s' has size %d", self->filename, 70 | self->size); 71 | } 72 | else 73 | { 74 | log_warn ("Can't initialize block file '%s': %s", self->filename, 75 | strerror (errno)); 76 | ret = errno; 77 | } 78 | return ret; 79 | } 80 | 81 | // 82 | // fileblockdevice_sync_fn 83 | // 84 | Error fileblockdevice_sync_fn (void *context, int start_block, int count) 85 | { 86 | (void)context; (void)start_block; (void)count; 87 | sync(); 88 | return 0; 89 | } 90 | 91 | Error fileblockdevice_read_fn 92 | (void *context, uint8_t *buffer, uint32_t block, uint32_t off, 93 | uint32_t size) 94 | { 95 | FileBlockDevice *self = (FileBlockDevice *) context; 96 | log_debug ("FileBD: read %d bytes from offset %d in block %d", 97 | (int)size, (int)off, (int)block); 98 | lseek (self->fd, (off_t) block * BLOCK_SIZE + (off_t)off, SEEK_SET); 99 | 100 | errno = 0; 101 | read (self->fd, buffer, (size_t)size); 102 | return errno; 103 | } 104 | 105 | Error fileblockdevice_write_fn 106 | (void *context, const uint8_t *buffer, uint32_t block, uint32_t off, 107 | uint32_t size) 108 | { 109 | FileBlockDevice *self = (FileBlockDevice *) context; 110 | log_debug ("FileBD: write %d bytes at offset %d in block %d", 111 | (int)size, (int)off, (int)block); 112 | lseek (self->fd, (off_t) block * BLOCK_SIZE + (off_t)off, SEEK_SET); 113 | 114 | errno = 0; 115 | write (self->fd, buffer, (size_t)size); 116 | return errno; 117 | } 118 | 119 | Error fileblockdevice_erase_fn 120 | (void *context, uint32_t block) 121 | { 122 | (void)context; (void)block; 123 | // Not necessary 124 | return 0; 125 | } 126 | 127 | // 128 | // fileblockdevice_get_params 129 | // 130 | void fileblockdevice_get_params (FileBlockDevice *self, 131 | BlockDeviceParams *params) 132 | { 133 | strcpy (params->desc, "Local file"); 134 | params->context = self; 135 | params->block_size = BLOCK_SIZE; 136 | params->block_count = self->size / params->block_size; 137 | params->sync_fn = fileblockdevice_sync_fn; 138 | params->read_fn = fileblockdevice_read_fn; 139 | params->write_fn = fileblockdevice_write_fn; 140 | params->erase_fn = fileblockdevice_erase_fn; 141 | } 142 | 143 | #endif 144 | 145 | -------------------------------------------------------------------------------- /blockdevice/src/flashblockdevice.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/flashblockdevice.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | 11 | 12 | #if PICO_ON_DEVICE 13 | 14 | 15 | 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "pico/stdlib.h" 24 | #include "hardware/sync.h" 25 | #include "hardware/flash.h" 26 | #include "blockdevice/flashblockdevice.h" 27 | #include "log/log.h" 28 | 29 | #if defined(PICO_W) 30 | #include "pico/cyw43_arch.h" 31 | #define LED_GPIO CYW43_WL_GPIO_LED_PIN 32 | #else 33 | #define LED_GPIO 25 34 | #endif 35 | 36 | #define ENABLE_LED 37 | 38 | #define BLOCK_SIZE 4096 39 | // Where the flash starts in the ARM memory map (this is where the 40 | // executable program is) 41 | #define FLASH_START_MEM 0x10000000 42 | 43 | // this variable is the last thing to be stored in flash 44 | // it cannot be set, but its address is usefull 45 | extern uint32_t __flash_binary_end; 46 | 47 | struct _FlashBlockDevice 48 | { 49 | uint32_t flash_start; 50 | uint32_t pages; 51 | uint32_t flash_storage_start_mem; 52 | alarm_id_t led_timer; 53 | }; 54 | 55 | 56 | // 57 | // flashblockdevice_create 58 | // 59 | FlashBlockDevice *flashblockdevice_create (uint32_t flash_start, 60 | uint32_t pages) 61 | { 62 | FlashBlockDevice *self = malloc (sizeof (FlashBlockDevice)); 63 | 64 | self->flash_start = flash_start; 65 | self->pages = pages; 66 | self->flash_storage_start_mem = FLASH_START_MEM + flash_start; 67 | // verify that its divisible by 4096 68 | if( (self->flash_storage_start_mem & (0xfff)) != 0x0 ) { 69 | printf("ERROR:Flash memory drives must start on multiple of 0x1000\n"); 70 | printf("%s tried to start at:%x\n", __FUNCTION__, (unsigned int)self->flash_storage_start_mem ); 71 | } 72 | #if defined(ENABLE_LED) 73 | #if !defined(PICO_W) 74 | gpio_init (LED_GPIO); 75 | gpio_set_dir (LED_GPIO, GPIO_OUT); 76 | #endif 77 | #endif 78 | self->led_timer = -1; 79 | return self; 80 | } 81 | 82 | // 83 | // flashblockdevice_destroy 84 | // 85 | void flashblockdevice_destroy (FlashBlockDevice *self) 86 | { 87 | if (self) 88 | { 89 | free (self); 90 | } 91 | } 92 | 93 | // 94 | // flashblockdevice_action_timeout 95 | // 96 | int64_t flashblockdevice_action_timeout (alarm_id_t id, void *self) 97 | { 98 | (void)id; 99 | #if defined(ENABLE_LED) 100 | #if defined(PICO_W) 101 | cyw43_arch_gpio_put(LED_GPIO, 0); 102 | #else 103 | gpio_put (LED_GPIO, 0); 104 | #endif 105 | #endif 106 | ((FlashBlockDevice *)self)->led_timer = -1; 107 | return 0; 108 | } 109 | 110 | // 111 | // flashblockdevice_show_action 112 | // 113 | void flashblockdevice_show_action (FlashBlockDevice *self) 114 | { 115 | if (self->led_timer <= 0) 116 | { 117 | self->led_timer = add_alarm_in_ms (60, flashblockdevice_action_timeout, 118 | self, FALSE); 119 | #if defined(ENABLE_LED) 120 | #if defined(PICO_W) 121 | cyw43_arch_gpio_put(LED_GPIO, 1); 122 | #else 123 | gpio_put (LED_GPIO, 1); 124 | #endif 125 | #endif 126 | } 127 | } 128 | 129 | // 130 | // flashblockdevice_initialize 131 | // 132 | Error flashblockdevice_initialize (FlashBlockDevice *self) 133 | { 134 | Error ret = 0; 135 | if (self->flash_storage_start_mem < (uint32_t)&__flash_binary_end) 136 | log_error ("Flash storage start is in the program area!"); 137 | 138 | return ret; 139 | } 140 | 141 | // 142 | // flashblockdevice_sync_fn 143 | // 144 | Error flashblockdevice_sync_fn (void *context, int start_block, int count) 145 | { 146 | (void)context; (void)start_block; (void)count; 147 | // Nothing to do here 148 | return 0; 149 | } 150 | 151 | Error flashblockdevice_read_fn 152 | (void *context, uint8_t *buffer, uint32_t block, uint32_t off, 153 | uint32_t size) 154 | { 155 | FlashBlockDevice *self = (FlashBlockDevice *) context; 156 | log_debug ("FlashBD: read %d bytes from offset %d in block %d\n", 157 | (int)size, (int)off, (int)block); 158 | flashblockdevice_show_action (self); 159 | char *mem = (char *)self->flash_storage_start_mem + 160 | ((int)block * (int)BLOCK_SIZE) + (int)off; 161 | memcpy (buffer, mem, size); 162 | return 0; 163 | } 164 | 165 | Error flashblockdevice_write_fn 166 | (void *context, const uint8_t *buffer, uint32_t block, uint32_t off, 167 | uint32_t size) 168 | { 169 | FlashBlockDevice *self = (FlashBlockDevice *) context; 170 | log_debug ("FlashBD: write %d bytes at offset %d in block %d\n", 171 | (int)size, (int)off, (int)block); 172 | flashblockdevice_show_action (self); 173 | int mem = self->flash_start + 174 | ((int)block * (int)BLOCK_SIZE) + (int)off; 175 | uint32_t ints = save_and_disable_interrupts(); 176 | flash_range_program ((uint32_t)mem, buffer, size); 177 | restore_interrupts (ints); 178 | return 0; 179 | } 180 | 181 | Error flashblockdevice_erase_fn 182 | (void *context, uint32_t block) 183 | { 184 | FlashBlockDevice *self = (FlashBlockDevice *) context; 185 | uint32_t ints = save_and_disable_interrupts(); 186 | flash_range_erase (self->flash_start + (block * BLOCK_SIZE), BLOCK_SIZE); 187 | restore_interrupts (ints); 188 | return 0; 189 | } 190 | 191 | // 192 | // flashblockdevice_get_params 193 | // 194 | void flashblockdevice_get_params (FlashBlockDevice *self, 195 | BlockDeviceParams *params) 196 | { 197 | strcpy (params->desc, "Flash"); 198 | params->context = self; 199 | params->block_size = BLOCK_SIZE; 200 | params->block_count = self->pages; 201 | params->sync_fn = flashblockdevice_sync_fn; 202 | params->read_fn = flashblockdevice_read_fn; 203 | params->write_fn = flashblockdevice_write_fn; 204 | params->erase_fn = flashblockdevice_erase_fn; 205 | } 206 | 207 | #endif 208 | 209 | -------------------------------------------------------------------------------- /console/README.md: -------------------------------------------------------------------------------- 1 | # console 2 | 3 | This directory contains the console handlers, for the console types that 4 | CPICOM supports. All consoles must conform to the function contract set 5 | out in console.h. 6 | 7 | -------------------------------------------------------------------------------- /console/include/console/console.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | console/console.h 6 | 7 | This header defines the abstract console control keys, and the 8 | ConsoleParams structure that all console implementations will have 9 | to implement. 10 | 11 | Copyright (c)2001 Kevin Boone, GPL v3.0 12 | 13 | =========================================================================*/ 14 | 15 | #pragma once 16 | 17 | #include 18 | #include "error/error.h" 19 | #include "klib/list.h" 20 | #include "picocpm/picocpm.h" 21 | 22 | // Key codes for cursor movement, etc 23 | #define VK_BACK 8 24 | #define VK_TAB 9 25 | #define VK_ENTER 10 26 | #define VK_ESC 27 27 | // Note that Linux console/terminal sends DEL when backspace is pressed 28 | #define VK_DEL 127 29 | #define VK_DOWN 1000 30 | #define VK_UP 1001 31 | #define VK_LEFT 1002 32 | #define VK_RIGHT 1003 33 | #define VK_PGUP 1004 34 | #define VK_PGDN 1005 35 | #define VK_HOME 1006 36 | #define VK_END 1007 37 | #define VK_INS 1008 38 | #define VK_CTRLUP 1009 39 | #define VK_CTRLDOWN 1010 40 | #define VK_CTRLLEFT 1011 41 | #define VK_CTRLRIGHT 1012 42 | #define VK_CTRLHOME 1013 43 | #define VK_CTRLEND 1014 44 | #define VK_SHIFTUP 1020 45 | #define VK_SHIFTDOWN 1021 46 | #define VK_SHIFTLEFT 1022 47 | #define VK_SHIFTRIGHT 1023 48 | #define VK_SHIFTHOME 1024 49 | #define VK_SHIFTEND 1025 50 | #define VK_SHIFTTAB 1026 51 | #define VK_CTRLSHIFTUP 1030 52 | #define VK_CTRLSHIFTDOWN 1031 53 | #define VK_CTRLSHIFTLEFT 1032 54 | #define VK_CTRLSHIFTRIGHT 1033 55 | #define VK_CTRLSHIFTHOME 1034 56 | #define VK_CTRLSHIFTEND 1035 57 | #define VK_INTR 2000 58 | #define VK_EOI 2001 59 | 60 | // 61 | // ConsoleProperties 62 | // This structure specifies the size and capabilities of the terminal 63 | // 64 | typedef struct _ConsoleProperties 65 | { 66 | int width; 67 | int height; 68 | } ConsoleProperties; 69 | 70 | typedef void (*InterruptHandler) (void *interrupt_context); 71 | 72 | typedef void (*console_out_string_fn) (void *context, const char *fmt,...); 73 | typedef void (*console_out_string_v_fn) (void *context, 74 | const char *fmt, va_list ap); 75 | typedef void (*console_out_endl_fn) (void *context); 76 | typedef void (*console_out_char_fn) (void *context, int c); 77 | typedef Error (*console_get_line_fn) (void *context, char *buff, int max, 78 | int max_history, List *history); 79 | typedef BOOL (*console_key_hit_fn) (void *context); 80 | typedef int (*console_get_char_timeout_fn) (void *context, int timeout); 81 | // Returns < 0 if no key waiting 82 | typedef int (*console_peek_char_fn) (void *context); 83 | typedef void (*console_set_interrupt_handler_fn) (void *context, 84 | InterruptHandler handler, void *int_context); 85 | typedef void (*console_get_properties_fn) (void *context, 86 | ConsoleProperties *properties); 87 | 88 | /** Clear the screen; don't move the cursor. */ 89 | typedef void (*console_cls_fn) (void *context); 90 | 91 | /** Move the cursor; don't change screen contents */ 92 | typedef void (*console_set_cursor_fn) (void *context, int row, int col); 93 | 94 | // Get key NO ECHO 95 | typedef int (*console_get_key_fn) (void *context); 96 | 97 | // 98 | // ConsoleParams 99 | // This structure specifies the functions that all console handlers must 100 | // implement (although implementations might, in some cases, be empty) 101 | // 102 | typedef struct _ConsoleParams 103 | { 104 | void *context; 105 | console_out_string_fn console_out_string; 106 | console_out_string_v_fn console_out_string_v; 107 | console_out_endl_fn console_out_endl; 108 | console_out_char_fn console_out_char; 109 | console_get_line_fn console_get_line; 110 | console_get_key_fn console_get_key; 111 | console_get_char_timeout_fn console_get_char_timeout; 112 | console_peek_char_fn console_peek_char; 113 | console_set_interrupt_handler_fn console_set_interrupt_handler; 114 | console_get_properties_fn console_get_properties; 115 | console_cls_fn console_cls; 116 | console_set_cursor_fn console_set_cursor; 117 | } ConsoleParams; 118 | 119 | -------------------------------------------------------------------------------- /console/include/console/console_stdio_vt100.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | console/console.h 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | #include "console/console.h" 16 | #include "picocpm/config.h" 17 | 18 | typedef struct _ConsoleStdioVT100 ConsoleStdioVT100; 19 | 20 | extern ConsoleStdioVT100 *consolestdiovt100_create (void); 21 | extern void consolestdiovt100_destroy (ConsoleStdioVT100 *self); 22 | 23 | extern void consolestdiovt100_get_params 24 | (ConsoleStdioVT100 *self, ConsoleParams *params); 25 | 26 | extern void consolestdiovt100_set_config 27 | (ConsoleStdioVT100 *self, Config *config); 28 | 29 | -------------------------------------------------------------------------------- /cpicom_screenshot_zork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinboone/cpicom/a43c2947a2143c7efb004629674b71db18b555ca/cpicom_screenshot_zork.png -------------------------------------------------------------------------------- /cpm/README.md: -------------------------------------------------------------------------------- 1 | # cpm 2 | 3 | This directory contains the Z80 emulator, BDOS hooks, and the actual 4 | BDOS implementation (in binary format). Most of this code is from 5 | a variety of different sources, and I (Kevin) don't claim to understand 6 | it very well. 7 | -------------------------------------------------------------------------------- /cpm/include/cpm/defs.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------*\ 2 | | defs.h -- main definitions for z80 emulator | 3 | | | 4 | | Copyright 1986-1988 by Parag Patel. All Rights Reserved. | 5 | | Copyright 1994-1995 by CodeGen, Inc. All Rights Reserved. | 6 | \*-----------------------------------------------------------------------*/ 7 | 8 | #ifndef __DEFS_H_ 9 | #define __DEFS_H_ 10 | 11 | #include "files/compat.h" 12 | #include "console/console.h" 13 | #include 14 | 15 | /* the current version of the z80 emulator */ 16 | #define VERSION "3.1" 17 | 18 | 19 | /* system definitions */ 20 | #if defined THINK_C || defined applec || defined macintosh 21 | # ifndef macintosh 22 | # define macintosh 23 | # endif 24 | #elif defined __MWERKS__ 25 | # define BeBox 26 | #elif defined MSDOS && defined GO32 27 | # define DJGPP 28 | # ifndef ENDIAN_LITTLE 29 | # define ENDIAN_LITTLE 30 | # endif 31 | #else 32 | # define UNIX /* cannot use "unix" since DJGPP defines it as well */ 33 | #endif 34 | 35 | 36 | /* some headers define macros this way */ 37 | #ifdef BYTE_ORDER 38 | # if BYTE_ORDER == LITTLE_ENDIAN 39 | # ifndef ENDIAN_LITTLE 40 | # define ENDIAN_LITTLE 41 | # endif 42 | # else 43 | # undef ENDIAN_LITTLE 44 | # endif 45 | #endif 46 | 47 | 48 | /* misc. handy defs */ 49 | 50 | #ifndef TRUE 51 | #define TRUE 1 52 | #endif 53 | 54 | #ifndef FALSE 55 | #define FALSE 0 56 | #endif 57 | 58 | typedef int boolean; 59 | 60 | 61 | #define CNTL(c) ((c) & 037) /* convert a char to its control equivalent */ 62 | 63 | 64 | /* handy typedefs for an 8-bit byte, 16-bit word, & 32-bit longword */ 65 | 66 | typedef unsigned char byte; 67 | typedef unsigned short word; 68 | typedef unsigned long longword; 69 | 70 | 71 | /* handy bit definitions - bit fields are not used as they are generally 72 | much slower than the equivalent logical masking operations */ 73 | 74 | #define BIT16 0x10000L 75 | #define BIT15 0x8000 76 | #define BIT14 0x4000 77 | #define BIT13 0x2000 78 | #define BIT12 0x1000 79 | #define BIT11 0x0800 80 | #define BIT10 0x0400 81 | #define BIT9 0x0200 82 | #define BIT8 0x0100 83 | #define BIT7 0x0080 84 | #define BIT6 0x0040 85 | #define BIT5 0x0020 86 | #define BIT4 0x0010 87 | #define BIT3 0x0008 88 | #define BIT2 0x0004 89 | #define BIT1 0x0002 90 | #define BIT0 0x0001 91 | 92 | /* handy masks to get a particular number of bits out */ 93 | 94 | #define MASK1 0x01 95 | #define MASK2 0x03 96 | #define MASK3 0x07 97 | #define MASK4 0x0F 98 | #define MASK5 0x1F 99 | #define MASK6 0x3F 100 | #define MASK7 0x7F 101 | #define MASK8 0xFF 102 | #define MASK12 0xFFF 103 | 104 | #define MASKU4 0xF0 105 | #define MASK16 0xFFFF 106 | 107 | 108 | /* z80 flag register definitions */ 109 | 110 | #define SIGN 0x80 111 | #define ZERO 0x40 112 | #define HALF 0x10 113 | #define PARITY 0x04 114 | #define OVERFLOW PARITY 115 | #define NEGATIVE 0x02 116 | #define CARRY 0x01 117 | 118 | 119 | /* z80 interrupt types - used to set the intr struct var */ 120 | 121 | #define INTRMASK 0xF00 122 | #define INT_FLAG 0x100 123 | #define NM_FLAG 0x200 124 | #define RESET_FLAG 0x400 125 | 126 | 127 | /* max number of the BIOS drive tables */ 128 | #define MAXDISCS 16 129 | 130 | 131 | typedef struct z80info 132 | { 133 | boolean event; 134 | /* byte regaf[2], regbc[2], regde[2], reghl[2]; */ 135 | word regaf, regbc, regde, reghl; 136 | word regaf2, regbc2, regde2, reghl2; 137 | word regsp, regpc, regix, regiy; 138 | byte regi, regr; 139 | byte iff, iff2, imode; 140 | byte reset, nmi, intr, halt; 141 | 142 | /* these point to the addresses of the above registers */ 143 | byte *reg[8]; 144 | word *regpairaf[4]; 145 | word *regpairsp[4]; 146 | word *regpairxy[4]; 147 | word *regixy[2]; 148 | byte *regir[2]; 149 | 150 | /* these are for the I/O, CP/M, and outside needs */ 151 | boolean trace; /* trace mode off/on */ 152 | boolean step; /* step-trace mode off/on */ 153 | int sig; /* caught a signal */ 154 | int syscall; /* CP/M syscall to be done */ 155 | int biosfn; /* BIOS function be done */ 156 | 157 | /* these are for the CP/M BIOS */ 158 | int drive; 159 | word dma; 160 | word track; 161 | word sector; 162 | MYFILE *drives[MAXDISCS]; 163 | long drivelen[MAXDISCS]; 164 | 165 | /* 64k bytes - may be allocated separately if desired */ 166 | byte mem[0x10000L]; 167 | 168 | #ifdef MEM_BREAK 169 | /* one for each byte of memory for breaks, memory-mapped I/O, etc */ 170 | byte membrk[0x10000L]; 171 | long numbrks; 172 | #endif 173 | 174 | int finished; // KB 175 | ConsoleParams *console_params; // KB 176 | 177 | } z80info; 178 | 179 | 180 | /* All the following macros assume that a variable named "z80" is 181 | available to access the above info. This is to allow multiple 182 | z80s to run without stepping on each other. 183 | */ 184 | 185 | 186 | /* These macros allow memory-mapped I/O if MEM_BREAK is defined. 187 | Because of this, these macros must be very carefully used, and 188 | there must not be ANY side-effects, such as increment/decerement 189 | in any of the macro args. Customizations go into read_mem() and 190 | write_mem(). 191 | */ 192 | 193 | #ifdef MEM_BREAK 194 | # define MEM(addr) \ 195 | (z80->membrk[(word)(addr)] ? \ 196 | read_mem(z80, addr) : \ 197 | z80->mem[(word)(addr)]) 198 | # define SETMEM(addr, val) \ 199 | (z80->membrk[(word)(addr)] ? \ 200 | write_mem(z80, addr, val) : \ 201 | (z80->mem[(word)(addr)] = (byte)(val))) 202 | 203 | /* various flags for "membrk" - others may be added */ 204 | # define M_BREAKPOINT 0x01 /* breakpoint */ 205 | # define M_READ_PROTECT 0x02 /* read-protected memory */ 206 | # define M_WRITE_PROTECT 0x04 /* write-protected memory */ 207 | # define M_MEM_MAPPED_IO 0x08 /* memory-mapped I/O addr */ 208 | 209 | #else 210 | # define MEM(addr) z80->mem[(word)(addr)] 211 | # define SETMEM(addr, val) (z80->mem[(word)(addr)] = (byte)(val)) 212 | #endif 213 | 214 | 215 | /* how to access the z80 registers & register pairs */ 216 | 217 | #ifdef ENDIAN_LITTLE 218 | # define A ((unsigned char *)&z80->regaf)[1] 219 | # define F ((unsigned char *)&z80->regaf)[0] 220 | # define B ((unsigned char *)&z80->regbc)[1] 221 | # define C ((unsigned char *)&z80->regbc)[0] 222 | # define D ((unsigned char *)&z80->regde)[1] 223 | # define E ((unsigned char *)&z80->regde)[0] 224 | # define H ((unsigned char *)&z80->reghl)[1] 225 | # define L ((unsigned char *)&z80->reghl)[0] 226 | #else 227 | # define A ((unsigned char *)&z80->regaf)[0] 228 | # define F ((unsigned char *)&z80->regaf)[1] 229 | # define B ((unsigned char *)&z80->regbc)[0] 230 | # define C ((unsigned char *)&z80->regbc)[1] 231 | # define D ((unsigned char *)&z80->regde)[0] 232 | # define E ((unsigned char *)&z80->regde)[1] 233 | # define H ((unsigned char *)&z80->reghl)[0] 234 | # define L ((unsigned char *)&z80->reghl)[1] 235 | #endif 236 | 237 | #define I z80->regi 238 | #define R z80->regr 239 | #define AF z80->regaf 240 | #define BC z80->regbc 241 | #define DE z80->regde 242 | #define HL z80->reghl 243 | #define AF2 z80->regaf2 244 | #define BC2 z80->regbc2 245 | #define DE2 z80->regde2 246 | #define HL2 z80->reghl2 247 | #define SP z80->regsp 248 | #define PC z80->regpc 249 | #define IX z80->regix 250 | #define IY z80->regiy 251 | #define IFF z80->iff 252 | #define IFF2 z80->iff2 253 | #define IMODE z80->imode 254 | #define RESET z80->reset 255 | #define NMI z80->nmi 256 | #define INTR z80->intr 257 | #define HALT z80->halt 258 | 259 | #define EVENT z80->event 260 | 261 | 262 | /* function externs: */ 263 | 264 | /* z80.c */ 265 | extern z80info *new_z80info(void); 266 | extern z80info *init_z80info(z80info *z80); 267 | extern z80info *destroy_z80info(z80info *z80); 268 | extern void delete_z80info(z80info *z80); 269 | 270 | extern boolean z80_emulator(z80info *z80, int count); 271 | 272 | extern int nobdos; 273 | 274 | /* inft.c */ 275 | extern void resetterm(void); 276 | extern void setterm(void); 277 | extern boolean input(z80info *z80, byte haddr, byte laddr, byte *val); 278 | extern void output(z80info *z80, byte haddr, byte laddr, byte data); 279 | extern void haltcpu(z80info *z80); 280 | extern word read_mem(z80info *z80, word addr); 281 | extern word write_mem(z80info *z80, word addr, byte val); 282 | extern void undefinstr(z80info *z80, byte instr); 283 | extern boolean loadfile (z80info *z80, const char *fname); 284 | extern uint8_t getdrive (const z80info *z80); 285 | extern uint8_t setdrive (const z80info *z80, uint8_t drive); 286 | 287 | /* bios.c */ 288 | extern void bios(z80info *z80, unsigned int fn); 289 | extern void sysreset(z80info *z80); 290 | extern void warmboot(z80info *z80); 291 | extern void finish(z80info *z80); 292 | extern void command(z80info *z80); 293 | 294 | /* disassem.c */ 295 | extern int disassemlen(void); 296 | extern int disassem(z80info *z80, word start, FILE *fp); 297 | 298 | /* bdos */ 299 | #define BDOS_HOOK 0xDC06 300 | void check_BDOS_hook(z80info *z80); 301 | extern int silent_exit; 302 | extern char *stuff_cmd; 303 | //extern int exec; 304 | extern int trace_bdos; 305 | extern int strace; 306 | char *bdos_decode(int n); 307 | int bdos_fcb(int n); 308 | void bdos_fcb_dump(z80info *z80); 309 | void cleanupfp (void); /* KB */ 310 | 311 | #endif /* __DEFS_H_ */ 312 | -------------------------------------------------------------------------------- /cpm/include/cpm/limits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Longest command line. This is normally stored in the area from 4 | // 0x80-0xFF, so that's 128 chars, minus one because the length is 5 | // stored at 0x80 6 | #define CPM_MAX_CMDLINE 127 7 | 8 | // Longest line that can be read by the read-line BDOS function. I don't 9 | // believe there is a limit, except that it's length will probably be 10 | // stored somewhere as 8 bytes. So call it 256. In any event, it has to 11 | // be as long as MAX_CMDLINE 12 | #define CPM_MAX_READ_LINE 256 13 | 14 | // Size of a CP/M record on disk 15 | #define BDOS_RECORD_SIZE 128 16 | 17 | // Size of a disk extent 18 | #define BDOS_EXTENT_SIZE 16384 19 | -------------------------------------------------------------------------------- /cpm/src/cpmdisc.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------*\ 2 | | Originally by Kevin Kayes, but he refused any responsibility for it. | 3 | | | 4 | | Copyright 1986-1988 by Parag Patel. All Rights Reserved. | 5 | | Copyright 1994 by CodeGen, Inc. All Rights Reserved. | 6 | \*-----------------------------------------------------------------------*/ 7 | 8 | 9 | #define SECTORSIZE 128 10 | #define SECTORSPERTRACK 26 11 | #define TRACKSPERDISC 77 12 | #define SECTOROFFSET 1 13 | #define TRACKOFFSET 0 14 | #define RESERVEDTRACKS 2 15 | #define SECTORSPERBLOCK 8 16 | #define SECTORSPEREXTENT 128 17 | #define EXTENTSIZE 32 18 | #define TOTALEXTENTS 64 19 | 20 | #define EXTENTSPERSECTOR (SECTORSIZE / EXTENTSIZE) 21 | #define TRACKSIZE (long)(SECTORSIZE * SECTORSPERTRACK) 22 | #define DISCSIZE (long)(TRACKSIZE * TRACKSPERDISC) 23 | 24 | unsigned char sectorxlat[] = { 25 | 1, 7, 13, 19, 26 | 25, 5, 11, 17, 27 | 23, 3, 9, 15, 28 | 21, 2, 8, 14, 29 | 20, 26, 6, 12, 30 | 18, 24, 4, 10, 31 | 16, 22 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /cpm/src/intf.c: -------------------------------------------------------------------------------- 1 | /*============================================================================ 2 | 3 | cpicom/cmp 4 | 5 | intf.c 6 | 7 | Various functions from the Z80 emulator route here, for dealing with 8 | communication. To be honest, I'm not sure how often these functions 9 | are actually used (e.g., direct I/O on input port 0 to read a character). 10 | 11 | Copyright 1986-1988 by Parag Patel. All Rights Reserved. 12 | Copyright 1994-1995 by CodeGen, Inc. All Rights Reserved. 13 | Now believed to be in the public domain. Modifications for CPICOM 14 | by Kevin Boone, 2021. 15 | 16 | ============================================================================*/ 17 | 18 | #include "files/compat.h" 19 | #include "cpm/limits.h" 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "cpm/defs.h" 29 | #include "vt.h" 30 | 31 | #include 32 | 33 | extern int errno; 34 | 35 | /* input -- z80 input instruction -- this function is called whenever 36 | an input port is referenced from the z80 to handle the real I/O -- 37 | it returns a byte to the z80 just like the real I/O instruction -- 38 | the arguments represent the data on the bus as it would be for a real 39 | z80 - this routine is restarted later if there is no input pending, 40 | and we must wait for some to occur */ 41 | 42 | boolean input (z80info *z80, byte haddr, byte laddr, byte *val) 43 | { 44 | (void)z80; 45 | ConsoleParams *cp = z80->console_params; 46 | unsigned int data; 47 | 48 | /* just uses the lower 8-bits of the I/O address for now... */ 49 | switch (laddr) 50 | { 51 | 52 | /* return a character from the keyboard - wait for it if necessary -- 53 | return "last" if we have already read in something via 0x01 */ 54 | case 0x00: 55 | if (1) 56 | { 57 | fflush(stdout); 58 | data = kget (cp, 0); 59 | /* data = getchar(); */ 60 | 61 | while ((data > 0x7f && errno == EINTR) || 62 | data == INTR_CHAR) 63 | { 64 | data = kget (cp, 0); 65 | /* data = getchar(); */ 66 | } 67 | } 68 | 69 | *val = data & 0x7F; 70 | break; 71 | 72 | /* return 0xFF if we have a character waiting to be read - save the 73 | character in "last" for 0x00 above */ 74 | case 0x01: 75 | fflush(stdout); 76 | 77 | if (constat (cp)) 78 | *val = 0xFF; 79 | else 80 | *val = 0x00; 81 | 82 | break; 83 | 84 | /* default - prompt the user for an input byte */ 85 | default: 86 | //resetterm(); 87 | MYPRINTF("INPUT : addr = %X%X DATA = ", haddr, laddr); 88 | fflush(stdout); 89 | scanf("%x", &data); 90 | //setterm(); 91 | *val = data; 92 | break; 93 | } 94 | 95 | return TRUE; 96 | } 97 | 98 | 99 | /*-----------------------------------------------------------------------*\ 100 | | output -- output the data at the specified I/O address 101 | \*-----------------------------------------------------------------------*/ 102 | 103 | void 104 | output(z80info *z80, byte haddr, byte laddr, byte data) 105 | { 106 | ConsoleParams *cp = z80->console_params; 107 | 108 | if (laddr == 0xFF) { 109 | /* BIOS call - interrupt the z80 before the next instruction 110 | since we may have to mess with the PC & other stuff - 111 | otherwise we would do it right here */ 112 | z80->event = TRUE; 113 | z80->halt = TRUE; 114 | z80->syscall = TRUE; 115 | z80->biosfn = data; 116 | 117 | if (z80->trace) 118 | { 119 | MYPRINTF("BIOS call %d\r\n", z80->biosfn); 120 | 121 | } 122 | } else if (laddr == 0) { 123 | /* output a character to the screen */ 124 | /* putchar(data); */ 125 | vt52 (cp, data); 126 | 127 | } else { 128 | /* Unimplemented -- just dump the data. */ 129 | MYPRINTF ("Unimplemented OUTPUT: addr = %X%X DATA = %X\r\n", haddr, laddr,data); 130 | } 131 | } 132 | 133 | 134 | /*-----------------------------------------------------------------------*\ 135 | | haltcpu -- this is called after the z80 halts -- it is used for 136 | | tracing & such 137 | \*-----------------------------------------------------------------------*/ 138 | 139 | void haltcpu (z80info *z80) 140 | { 141 | z80->halt = FALSE; 142 | 143 | /* we are tracing execution of the z80 */ 144 | if (z80->trace) 145 | { 146 | /* re-enable tracing */ 147 | z80->event = TRUE; 148 | z80->halt = TRUE; 149 | } 150 | 151 | /* a CP/M syscall - done here so tracing still works */ 152 | if (z80->syscall) 153 | { 154 | z80->syscall = FALSE; 155 | bios (z80, z80->biosfn); 156 | } 157 | } 158 | 159 | void undefinstr (z80info *z80, byte instr) 160 | { 161 | MYFPRINTFERR (stderr, "\r\nIllegal instruction 0x%.2X at PC=0x%.4X\r\n", 162 | instr, PC - 1); 163 | } 164 | 165 | uint8_t getdrive (const z80info *z80) 166 | { 167 | (void)z80; 168 | return filecontext_global_get_current_drive (); 169 | } 170 | 171 | uint8_t setdrive (const z80info *z80, uint8_t drive) 172 | { 173 | (void)z80; 174 | filecontext_global_set_current_drive (drive); 175 | return 0; 176 | } 177 | 178 | int cpm_runprog (ConsoleParams *console_params, const char *cmdline, 179 | z80info **z80_info) 180 | { 181 | char buff[CPM_MAX_CMDLINE + 1]; 182 | strncpy (buff, cmdline, CPM_MAX_CMDLINE); 183 | buff [CPM_MAX_CMDLINE] = 0; 184 | stuff_cmd = buff; 185 | 186 | z80info *z80 = new_z80info(); 187 | z80->console_params = console_params; 188 | *z80_info = z80; 189 | 190 | if (z80 == NULL) 191 | return -1; 192 | 193 | sysreset (z80); 194 | 195 | z80->finished = FALSE; 196 | while (!z80->finished) 197 | { 198 | z80_emulator (z80, 100000); 199 | } 200 | delete_z80info (z80); 201 | *z80_info = NULL; 202 | cleanupfp (); //KB -- cleanup file pointers that are still open 203 | return 0; 204 | } 205 | 206 | -------------------------------------------------------------------------------- /cpm/src/vt.c: -------------------------------------------------------------------------------- 1 | /*============================================================================ 2 | 3 | cpicom/cmp 4 | 5 | vt.c 6 | 7 | Functions for reading and writing the console. Many BIOS/BDOS functions 8 | route here. I don't know who the original author of this file is, but 9 | I'm guessing it's now PD. In any case, I've replaced most of the original 10 | logic with calls to functions in ConsoleParams. 11 | 12 | ============================================================================*/ 13 | 14 | #include "files/compat.h" 15 | #include "console/console.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "vt.h" 22 | 23 | int last = -1; 24 | 25 | int constat (ConsoleParams *cp) 26 | { 27 | return (cp->console_peek_char (cp->context) >= 0); 28 | } 29 | 30 | // Get a character, I'm not sure what the "w" param was originally intended 31 | // to do. A timeout, perhaps? Many BDOS/BIOS operations call this. 32 | int kget (ConsoleParams *cp, int w) 33 | { 34 | (void)w; 35 | int c = cp->console_get_char_timeout (cp->context, -1); 36 | if (c == 10) c = 13; // Not sure about this 37 | return c; 38 | } 39 | 40 | 41 | void putch (ConsoleParams *cp, int c) 42 | { 43 | cp->console_out_char (cp->context, c); 44 | } 45 | 46 | void putmes (ConsoleParams *cp, const char *s) 47 | { 48 | cp->console_out_string (cp->context, s); 49 | } 50 | 51 | /** vt52 -- all character output from BDOS/BIOS comes through here. The 52 | function is so-named on the basis that most CP/M programs that do 53 | terminal output expect to see a VT52. If you actually have a VT52 or 54 | something that emulates one, this function can be reconfigured just 55 | to output direct to the console. If CP/M programs (the few of them 56 | that exist) do ANSI/VT-100, this could also be disabled, but there needs 57 | to be a way to do that selectably. In practice, it seems OK to leave 58 | this conversion in place all the time. */ 59 | void vt52 (ConsoleParams *cp, int c) { 60 | //cp->console_out_char (cp->context, c); 61 | //return; 62 | static int state = 0, x, y; 63 | char buff[32]; 64 | switch (state) { 65 | case 0: 66 | switch (c) { 67 | #ifdef VBELL 68 | case 0x07: /* BEL: flash screen */ 69 | putmes (cp, "\033[?5h\033[?5l"); 70 | break; 71 | #endif 72 | case 0x7f: /* DEL: echo BS, space, BS */ 73 | putmes (cp, "\b \b"); 74 | break; 75 | case 0x1a: /* adm3a clear screen */ 76 | case 0x0c: /* vt52 clear screen */ 77 | putmes (cp, "\033[H\033[2J"); 78 | break; 79 | case 0x1e: /* adm3a cursor home */ 80 | putmes (cp, "\033[H"); 81 | break; 82 | case 0x1b: 83 | state = 1; /* esc-prefix */ 84 | break; 85 | case 1: 86 | state = 2; /* cursor motion prefix */ 87 | break; 88 | case 2: /* insert line */ 89 | putmes (cp, "\033[L"); 90 | break; 91 | case 3: /* delete line */ 92 | putmes (cp, "\033[M"); 93 | break; 94 | case 0x18: case 5: /* clear to eol */ 95 | putmes (cp, "\033[K"); 96 | break; 97 | case 0x12: case 0x13: 98 | break; 99 | default: 100 | putch (cp, c); 101 | } 102 | break; 103 | case 1: /* esc was sent */ 104 | switch (c) { 105 | case 0x1b: 106 | putch (cp, c); 107 | break; 108 | case '=': 109 | case 'Y': 110 | state = 2; 111 | break; 112 | case 'E': /* insert line */ 113 | putmes (cp, "\033[L"); 114 | break; 115 | case 'R': /* delete line */ 116 | putmes (cp, "\033[M"); 117 | break; 118 | case 'B': /* enable attribute */ 119 | state = 4; 120 | break; 121 | case 'C': /* disable attribute */ 122 | state = 5; 123 | break; 124 | case 'L': /* set line */ 125 | case 'D': /* delete line */ 126 | state = 6; 127 | break; 128 | case '*': /* set pixel */ 129 | case ' ': /* clear pixel */ 130 | state = 8; 131 | break; 132 | default: /* some true ANSI sequence? */ 133 | state = 0; 134 | putch (cp, 0x1b); 135 | putch (cp, c); 136 | } 137 | break; 138 | case 2: 139 | y = c - ' '+1; 140 | state = 3; 141 | break; 142 | case 3: 143 | x = c - ' '+1; 144 | state = 0; 145 | sprintf(buff, "\033[%d;%dH", y, x); 146 | putmes (cp, buff); 147 | break; 148 | case 4: /* +B prefix */ 149 | state = 0; 150 | switch (c) { 151 | case '0': /* start reverse video */ 152 | putmes (cp, "\033[7m"); 153 | break; 154 | case '1': /* start half intensity */ 155 | putmes (cp, "\033[1m"); 156 | break; 157 | case '2': /* start blinking */ 158 | putmes (cp, "\033[5m"); 159 | break; 160 | case '3': /* start underlining */ 161 | putmes (cp, "\033[4m"); 162 | break; 163 | case '4': /* cursor on */ 164 | putmes (cp, "\033[?25h"); 165 | break; 166 | case '6': /* remember cursor position */ 167 | putmes (cp, "\033[s"); 168 | break; 169 | case '5': /* video mode on */ 170 | case '7': /* preserve status line */ 171 | break; 172 | default: 173 | putch (cp, 0x1b); 174 | putch (cp, 'B'); 175 | putch (cp, c); 176 | } 177 | break; 178 | case 5: /* +C prefix */ 179 | state = 0; 180 | switch (c) { 181 | case '0': /* stop reverse video */ 182 | putmes (cp, "\033[27m"); 183 | break; 184 | case '1': /* stop half intensity */ 185 | putmes (cp, "\033[m"); 186 | break; 187 | case '2': /* stop blinking */ 188 | putmes (cp, "\033[25m"); 189 | break; 190 | case '3': /* stop underlining */ 191 | putmes (cp, "\033[24m"); 192 | break; 193 | case '4': /* cursor off */ 194 | putmes (cp, "\033[?25l"); 195 | break; 196 | case '6': /* restore cursor position */ 197 | putmes (cp, "\033[u"); 198 | break; 199 | case '5': /* video mode off */ 200 | case '7': /* don't preserve status line */ 201 | break; 202 | default: 203 | putch (cp, 0x1b); 204 | putch (cp, 'C'); 205 | putch (cp, c); 206 | } 207 | break; 208 | /* set/clear line/point */ 209 | case 6: 210 | case 7: 211 | case 8: 212 | state ++; 213 | break; 214 | case 9: 215 | state = 0; 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /cpm/src/vt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "console/console.h" 4 | 5 | /* Return true if input character available */ 6 | int constat (ConsoleParams *cp); 7 | 8 | /* Get input character: 9 | w = 0: wait until we have a character 10 | w = 1: return -1 if we don' have one 11 | */ 12 | int kget(ConsoleParams *cp, int w); 13 | 14 | /* Write character to terminal */ 15 | void vt52 (ConsoleParams *cp, int c); 16 | 17 | #define INTR_CHAR 31 /* control-underscore */ 18 | -------------------------------------------------------------------------------- /docs/filesystem_compat_layer.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinboone/cpicom/a43c2947a2143c7efb004629674b71db18b555ca/docs/filesystem_compat_layer.odg -------------------------------------------------------------------------------- /error/README.md: -------------------------------------------------------------------------------- 1 | # error 2 | 3 | This directory contains the list of error codes and the implementations 4 | of functions to render them as text strings. Note that standard POSIX 5 | errno codes are also used -- error.h essentially defines specific errors 6 | for CPICOM that are not part of the standard set. 7 | 8 | -------------------------------------------------------------------------------- /error/include/error/error.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | error/error.h 6 | 7 | Definitions of CPICOM error codes. Note that all the standard Linux 8 | errno codes may also be used -- the codes specific to CPICOM start 9 | at value 1000. 10 | 11 | Copyright (c)2001 Kevin Boone, GPL v3.0 12 | 13 | =========================================================================*/ 14 | 15 | #pragma once 16 | 17 | #include 18 | #define ERROR_MAX_ERRNO 1000 19 | 20 | typedef enum 21 | { 22 | ERROR_NOMEM = ENOMEM, 23 | ERROR_NOENT = ENOENT, 24 | ERROR_MFILE = EMFILE, 25 | ERROR_BADF = EBADF, 26 | ERROR_CORRUPT_FS = 1001, 27 | ERROR_UNDEFINED_DRIVE = 1002, 28 | ERROR_INTERRUPTED = 1003, 29 | ERROR_ENDOFINPUT = 1004, 30 | ERROR_BADDRIVELETTER = 1005, 31 | ERROR_ARGCOUNT = 1006, 32 | ERROR_NOMEDIUM = 1007, 33 | ERROR_EXISTS = 1008, 34 | ERROR_YMODEM = 1009, 35 | ERROR_EXE_TOO_LARGE = 1010, 36 | ERROR_CMDLINE_TOO_LONG = 1011, 37 | ERROR_UNIMPLEMENTED = 1012, 38 | ERROR_CMD_SYNTAX = 1013, 39 | ERROR_DRIVE_NOT_READY = 1014, 40 | ERROR_DISK_LOWLEVEL = 1015, 41 | ERROR_FILENAME = 1016, 42 | ERROR_INTERNAL = 1017 43 | } Error; 44 | 45 | extern const char *error_strerror (Error error); 46 | -------------------------------------------------------------------------------- /error/src/error.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | error/error.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | 11 | #include 12 | #include "error/error.h" 13 | 14 | // 15 | // error_strerror 16 | // 17 | const char *error_strerror (Error error) 18 | { 19 | if (error < ERROR_MAX_ERRNO) return strerror (error); 20 | switch (error) 21 | { 22 | case ERROR_NOENT: case ERROR_NOMEM: case ERROR_MFILE: 23 | case ERROR_BADF: 24 | break; // Already done 25 | case ERROR_CORRUPT_FS: return "Corrupted filesystem"; 26 | case ERROR_INTERRUPTED: return "Interrupted"; 27 | case ERROR_ENDOFINPUT: return "End of input"; 28 | case ERROR_BADDRIVELETTER: return "Bad drive letter"; 29 | case ERROR_ARGCOUNT: return "Incorrect argument count"; 30 | case ERROR_UNDEFINED_DRIVE: return "Undefined drive"; 31 | case ERROR_NOMEDIUM: return "No medium in drive"; 32 | case ERROR_EXISTS: return "File already exists"; 33 | case ERROR_YMODEM: return "YModem transfer failed"; 34 | case ERROR_EXE_TOO_LARGE: return "Executable too large"; 35 | case ERROR_CMDLINE_TOO_LONG: return "Command line too long"; 36 | case ERROR_UNIMPLEMENTED: return "Unimplemented feature"; 37 | case ERROR_CMD_SYNTAX: return "Command syntax error"; 38 | case ERROR_DRIVE_NOT_READY: return "Drive not ready"; 39 | case ERROR_DISK_LOWLEVEL: return "Lowlevel disk error"; 40 | case ERROR_FILENAME: return "Invalid filename"; 41 | case ERROR_INTERNAL: return "Unknown internal error"; 42 | } 43 | return NULL; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /files/README.md: -------------------------------------------------------------------------------- 1 | # files 2 | 3 | This directory includes the top-level file-handling functions (predominantly 4 | in filecontext.c/.h) and the POSIX file compatibility functions 5 | (my\_open, my\_fwrite, etc). Note that many of the compatibility functions 6 | are very crudely implemented at present. 7 | 8 | -------------------------------------------------------------------------------- /files/include/files/compat.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/compat.h 6 | 7 | Prototypes for all the substitute file-handling commands that CPICOM's 8 | file compatibility layer provides. There are also macros like 9 | MYOPEN that can be used in client code, to make it easier to switch 10 | between using host file operations and simulated filesystem operations. 11 | 12 | Copyright (c)2001 Kevin Boone, GPL v3.0 13 | 14 | =========================================================================*/ 15 | #pragma once 16 | 17 | #include "klib/string.h" 18 | #include "klib/list.h" 19 | #include "files/limits.h" 20 | #include "files/filecontext.h" 21 | 22 | // Space to allow for a BDOS filename (which won't have a directory) 23 | // A:ABCDEFGH.IJK -- 14 characters 24 | #define BDOS_MAX_FNAME 14 25 | 26 | #define MYFILE struct myfile 27 | #define MYFOPEN my_fopen 28 | #define MYFCLOSE my_fclose 29 | #define MYFWRITE my_fwrite 30 | #define MYFREAD my_fread 31 | #define MYFTELL my_ftell 32 | #define MYFSEEK my_fseek 33 | #define MYPUTC my_putc 34 | #define MYGETC my_getc 35 | #define MYFGETC my_getc 36 | #define MYPRINTF printf 37 | #define MYFFLUSH my_fflush 38 | #define MYFILENO(fp) fp->fd 39 | #define MYFPRINTFERR my_fprintferr 40 | #define FFLUSH_STDOUT fflush(stdout) 41 | #define MYFSTAT my_fstat 42 | #define MYFTRUNCATE my_ftruncate 43 | #define MYRENAME my_rename 44 | #define MYFATTRIB my_setattribute 45 | 46 | // If FILE_COMPAT_UPPERCASE is defined, all filenames are written 47 | // as uppercase 48 | #define FILE_COMPAT_UPPERCASE 49 | 50 | // Constants for my_fnmatch() 51 | #define MYFNM_NOMATCH -1 52 | #define MYFNM_LEADING_DIR 0x0001 53 | #define MYFNM_NOESCAPE 0x0002 54 | #define MYFNM_PERIOD 0x0004 55 | #define MYFNM_FILE_NAME 0x0008 56 | #define MYFNM_CASEFOLD 0x0010 57 | 58 | #define MYFILE struct myfile 59 | 60 | struct _my_DIR; 61 | typedef struct _my_DIR my_DIR; 62 | 63 | struct myfile 64 | { 65 | int fd; 66 | }; 67 | 68 | 69 | typedef struct _my_statfs 70 | { 71 | char bd_desc[BLOCKDEVICEPARAMS_MAX_DESC + 1]; 72 | char fs_desc[FILESYSTEMPARAMS_MAX_DESC + 1]; 73 | uint32_t block_size; 74 | uint32_t total_blocks; 75 | uint32_t free_blocks; 76 | } my_statfs; 77 | 78 | 79 | /** Get the drive number from the drive letter in the full path. If 80 | there is none, returns the current default drive. If a drive 81 | letter is present, it must be valid (but it isn't case-sensitive). 82 | Returns the drive number from 0 - FILESYSTEM_MAX_MOUNTS, or a 83 | negated error code if the drive is invalid. */ 84 | extern int compat_get_drive (const char *fullpath); 85 | 86 | /** Get the drive number from the drive letter in the full path. If 87 | there is none, returns -1; */ 88 | extern int compat_get_drive_no_default (const char *fullpath); 89 | 90 | /** Returns what ever is left after the drive letter. In principle, 91 | the result could be empty, but it should never be null. */ 92 | extern const char *compat_get_path (const char *filename); 93 | 94 | /** Split a filename into drive, name, extention, in 8.3 format. 95 | If there is no drive, this value is set to 0. *name and *ext 96 | must be of length at least 9 and 4 respectively. */ 97 | extern void compat_split_drive_name_ext_83 (const char *filename, 98 | char *drive_letter, char *name, char *ext); 99 | 100 | extern int my_open (const char *filename, int flags); 101 | extern int my_close (int fd); 102 | extern ssize_t my_write (int fd, const void *buf, size_t n); 103 | extern ssize_t my_read (int fd, void *buf, size_t n); 104 | extern int my_unlink (const char *path); 105 | extern my_DIR *my_opendir (const char *path); 106 | extern int my_closedir (my_DIR *dir); 107 | extern struct my_dirent *my_readdir (struct _my_DIR *dir); 108 | // my_create fails if the file already exists -- I'm not sure if 109 | // that's the usual behaviour 110 | extern int my_creat (const char *path, int mode); 111 | extern uint32_t my_lseek (int fd, uint32_t offset, int whence); 112 | extern int my_fstat (int fd, struct stat *statbuf); 113 | extern int my_stat (const char *path, struct stat *statbuf); 114 | extern int my_ftruncate (int fd, uint32_t length); 115 | extern struct myfile *my_fopen (const char *filename, const char *mode); 116 | extern int32_t my_fwrite (const void *ptr, int32_t size, 117 | int32_t nmemb, MYFILE *stream); 118 | extern int32_t my_fread (void *ptr, int32_t size, 119 | int32_t nmemb, MYFILE *f); 120 | extern int my_fclose (MYFILE *f); 121 | extern uint32_t my_ftell (MYFILE *f); 122 | extern int my_fseek (MYFILE *f, uint32_t offset, int whence); 123 | extern int my_getc (MYFILE *f); 124 | extern int my_putc (int c, MYFILE *f); 125 | extern int my_fflush (MYFILE *f); 126 | extern int my_setattribute(MYFILE *f); 127 | extern void my_fprintferr (void *f, const char *fmt, ...); 128 | extern int my_rename (const char *source, const char *target); 129 | 130 | 131 | // Substitute for the glibc globbing functions 132 | extern int my_fnmatch (const char *pattern, const char *string, int flags); 133 | extern void compat_globber (const char *pattern, List *list); 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /files/include/files/filecontext.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/filecontext.h 6 | 7 | FileContext is the entry point for all the Pico file handling 8 | functions. There are equivalents of many, but certainly not all, 9 | the standard POSIX functions. FileContext understands the idea of 10 | a numbered "drive". Each drive corresponds to a pairing between 11 | a fileystem (LittleFS, VFAT) and a specific block device. FileContext 12 | maintains a table of these drives, with their corresponding filesystem. 13 | The actual file operations are simple delegated to the filesystem, when 14 | the filesystem handler has been determined from the drive. 15 | 16 | Copyright (c)2001 Kevin Boone, GPL v3.0 17 | 18 | =========================================================================*/ 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include "klib/defs.h" 25 | #include "error/error.h" 26 | #include "files/compat.h" 27 | #include "fs/filesystem.h" 28 | #include "files/filesystemmount.h" 29 | #include "blockdevice/blockdevice.h" 30 | 31 | #define MAX_OPEN_FILES 64 32 | #define MAX_FNAME 255 33 | #define MAX_PATH 255 34 | 35 | #ifndef DT_DIR_ 36 | #define DT_DIR_ 4 37 | #endif 38 | 39 | #ifndef DT_REG 40 | #define DT_REG_ 8 41 | #endif 42 | 43 | typedef struct _OpenFileInfo 44 | { 45 | BOOL in_use; 46 | struct _FilesystemMount *mount; 47 | char *path; 48 | int drive; 49 | void *opaque1; 50 | void *opaque2; 51 | } OpenFileInfo; 52 | 53 | // my_dirent structure is a simple replacement for dirent, as used by 54 | // readdir, etc. 55 | struct my_dirent 56 | { 57 | char d_name[256]; 58 | uint8_t d_type; 59 | }; 60 | 61 | // my_DIR structure is a simple replacement for DIR, as used by 62 | // opendir, etc. 63 | typedef struct _my_DIR 64 | { 65 | int drive; 66 | void *opaque1; 67 | struct my_dirent current_de; 68 | } my_DIR; 69 | 70 | 71 | struct _FilesystemParams; 72 | struct _FileContext; 73 | struct _my_dirent; 74 | struct _my_DIR; 75 | struct _my_statfs; 76 | typedef struct _FileContext FileContext; 77 | 78 | // Global filesystem context 79 | extern FileContext *global_fc; 80 | 81 | extern FileContext *filecontext_create (void); 82 | extern void filecontext_destroy (FileContext *self); 83 | 84 | extern Error filecontext_unmount (FileContext *self, uint8_t drive_num); 85 | extern void filecontext_unmount_all (FileContext *self); 86 | 87 | extern Error filecontext_mount (FileContext *self, uint8_t drive_num, 88 | struct _FilesystemParams *fs_params, 89 | BlockDeviceParams *bd_params); 90 | 91 | extern Error filecontext_format (FileContext *self, uint8_t drive_num); 92 | 93 | // Sets errno, returns fd or -1 94 | extern int filecontext_open (FileContext *self, uint8_t drive_num, 95 | const char *name, int flags); 96 | 97 | // Sets errno, returns size or -1 98 | extern ssize_t filecontext_write (FileContext *self, int fd, 99 | const void *buf, size_t n); 100 | 101 | // Sets errno, return 0 or -1 102 | extern int filecontext_close (FileContext *self, int fd); 103 | 104 | extern Error filecontext_delete (FileContext *self, uint8_t drive, 105 | const char *filename); 106 | 107 | // Sets errno 108 | extern my_DIR * filecontext_opendir (FileContext *self, uint8_t drive, 109 | const char *path); 110 | 111 | extern Error filecontext_closedir (FileContext *self, 112 | my_DIR *dir); 113 | 114 | extern struct my_dirent *filecontext_readdir (FileContext *self, 115 | struct _my_DIR *dir); 116 | 117 | extern const OpenFileInfo *filecontext_get_ofi 118 | (const FileContext *self, int i); 119 | 120 | /** Gets the size of a file, if it exists. 121 | Sets errno, returns size or (uint32_t)-1 */ 122 | //extern uint32_t filecontext_size (const FileContext *self, 123 | // uint8_t drive, const char *path); 124 | 125 | extern Error filecontext_fstat (const FileContext *self, int fd, 126 | struct stat *sb); 127 | 128 | extern Error filecontext_ftruncate (FileContext *self, int fd, 129 | uint32_t length); 130 | 131 | extern Error filecontext_rename (const FileContext *self, 132 | uint8_t drive, 133 | const char *source_path, const char *target_path); 134 | 135 | extern Error filecontext_dstatfs (const FileContext *self, 136 | uint8_t drive, 137 | struct _my_statfs *statfs); 138 | 139 | /** Prepares a drive for first use (if preparation is actually required). 140 | Checks that the drive is actually usable. Returns an error code if 141 | it isn't. */ 142 | extern Error filecontext_activate_drive (FileContext *self, 143 | uint8_t drive_num); 144 | 145 | extern Error filecontext_stat (const FileContext *self, 146 | uint8_t drive_num, const char *path, struct stat *sb); 147 | 148 | // Returns 0-16 149 | extern int filecontext_global_get_current_drive (void); 150 | void filecontext_global_set_current_drive (uint8_t drive); 151 | 152 | // Sets errno, return fd or -1 153 | extern int filecontext_global_open (uint8_t drive, 154 | const char *path, int flags); 155 | 156 | /** Gets the size of a file, if it exists. 157 | Sets errno, returns size or (uint32_t)-1 */ 158 | extern uint32_t filecontext_global_size (uint8_t drive, 159 | const char *path); 160 | 161 | // Sets errno, return 0 or -1 162 | extern int filecontext_global_close (int fd); 163 | 164 | extern ssize_t filecontext_global_write (int fd, const void *buf, 165 | size_t n); 166 | 167 | extern ssize_t filecontext_global_read (int fd, void *buf, 168 | size_t n); 169 | 170 | extern Error filecontext_global_delete (uint8_t drive, 171 | const char *filename); 172 | 173 | extern my_DIR * filecontext_global_opendir (uint8_t drive, 174 | const char *path); 175 | 176 | extern Error filecontext_global_closedir (my_DIR *dir); 177 | 178 | extern struct my_dirent *filecontext_global_readdir (struct _my_DIR *dir); 179 | 180 | extern const OpenFileInfo *filecontext_global_get_ofi (int i); 181 | 182 | // Sets errno 183 | extern uint32_t filecontext_global_lseek (int fd, uint32_t offset, 184 | int whence); 185 | 186 | extern Error filecontext_global_fstat (int fd, struct stat *sb); 187 | 188 | extern Error filecontext_global_ftruncate (int fd, uint32_t length); 189 | 190 | extern Error filecontext_global_rename (uint8_t drive, 191 | const char *source_path, const char *target_path); 192 | 193 | extern Error filecontext_global_dstatfs (uint8_t drive, 194 | struct _my_statfs *statfs); 195 | 196 | extern Error filecontext_global_format (uint8_t drive_num); 197 | 198 | /** See filecontext_activate_drive for details. */ 199 | extern Error filecontext_global_activate_drive (uint8_t drive_num); 200 | 201 | extern Error filecontext_global_stat (uint8_t drive_num, 202 | const char *path, struct stat *sb); 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /files/include/files/filesystemmount.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/filesystemmount.h 6 | 7 | A FilesystemMount is a tuple of the drive number, the filesystem handler 8 | for the drive, and the underlying block device. The FileContext 9 | object maintains a table of these mounts. 10 | 11 | Copyright (c)2001 Kevin Boone, GPL v3.0 12 | 13 | =========================================================================*/ 14 | #pragma once 15 | 16 | #include 17 | #include 18 | #include "blockdevice/blockdevice.h" 19 | #include "fs/filesystem.h" 20 | #include "files/filecontext.h" 21 | 22 | // Number of filesystems that can be mounted (A: to P:) 23 | #define FILESYSTEM_MAX_MOUNTS 16 24 | 25 | struct _OpenFileInfo; 26 | struct _FilesystemMount; 27 | struct _FilesystemParams; 28 | struct _my_statfs; 29 | struct _my_DIR; 30 | 31 | typedef struct _FilesystemMount FilesystemMount; 32 | 33 | extern FilesystemMount *filesystemmount_create 34 | (const struct _FilesystemParams *fs_params, 35 | const BlockDeviceParams *bd_params); 36 | extern void filesystemmount_destroy (FilesystemMount *self); 37 | 38 | extern Error filesystemmount_mount (FilesystemMount *self); 39 | 40 | /** Unmount a filesystem. It is envisaged that this operation simply 41 | flushes outstanding operations -- it does not physically eject 42 | harware or free any resources. Although it can return an error code, 43 | the only likely reasons for failure are that the medium has been removed, 44 | or the filesystem is corrupt. */ 45 | extern Error filesystemmount_unmount (FilesystemMount *self); 46 | 47 | extern Error filesystemmount_format (FilesystemMount *self); 48 | 49 | extern Error filesystemmount_open (FilesystemMount *mount, 50 | const char *name, int flags, struct _OpenFileInfo *ofi); 51 | 52 | extern Error filesystemmount_close (FilesystemMount *mount, 53 | struct _OpenFileInfo *ofi); 54 | 55 | extern Error filesystemmount_write (FilesystemMount *mount, 56 | struct _OpenFileInfo *ofi, const uint8_t *buff, int n, 57 | uint32_t *written); 58 | 59 | extern Error filesystemmount_read (FilesystemMount *mount, 60 | struct _OpenFileInfo *ofi, uint8_t *buff, int n, 61 | uint32_t *read); 62 | 63 | extern Error filesystemmount_delete (FilesystemMount *self, 64 | const char *path); 65 | 66 | extern struct _my_DIR *filesystemmount_opendir (FilesystemMount *self, 67 | const char *path); 68 | 69 | extern Error filesystemmount_closedir (FilesystemMount *self, 70 | struct _my_DIR *dir); 71 | 72 | extern struct my_dirent *filesystemmount_readdir (FilesystemMount *self, 73 | struct _my_DIR *dir); 74 | 75 | extern uint32_t filesystemmount_lseek (FilesystemMount *self, 76 | struct _OpenFileInfo *ofi, uint32_t offset, int whence); 77 | 78 | //extern uint32_t filesystemmount_size (FilesystemMount *self, 79 | // const char *path); 80 | 81 | extern Error filesystemmount_fstat (FilesystemMount *self, 82 | const struct _OpenFileInfo *ofi, struct stat *sb); 83 | 84 | extern Error filesystemmount_stat (FilesystemMount *self, 85 | const char *path, struct stat *sb); 86 | 87 | extern Error filesystemmount_ftruncate (FilesystemMount *self, 88 | struct _OpenFileInfo *ofi, uint32_t length); 89 | 90 | 91 | extern Error filesystemmount_rename (FilesystemMount *self, 92 | const char *source, const char *target); 93 | 94 | extern Error filesystemmount_statfs (FilesystemMount *self, 95 | struct _my_statfs *statfs); 96 | 97 | 98 | -------------------------------------------------------------------------------- /files/include/files/fileutil.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | Basic file handling utilities 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include "error/error.h" 15 | 16 | extern Error fileutil_write_file (const char *filename, 17 | uint8_t *buff, int len); 18 | 19 | extern Error fileutil_append_file (const char *filename, 20 | uint8_t *buff, int len); 21 | 22 | 23 | -------------------------------------------------------------------------------- /files/include/files/limits.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/limits.h 6 | 7 | Various file-related constants. 8 | 9 | TODO: extract these from where they are scattered among many other 10 | files. 11 | 12 | Copyright (c)2001 Kevin Boone, GPL v3.0 13 | 14 | =========================================================================*/ 15 | 16 | #pragma once 17 | 18 | // Longest name of a block device (e.g. "FlashDB") 19 | #define BLOCKDEVICEPARAMS_MAX_DESC 32 20 | 21 | // Longest name of a filesystem (e.g., "LFS", "VFAT") 22 | #define FILESYSTEMPARAMS_MAX_DESC 32 23 | 24 | -------------------------------------------------------------------------------- /files/src/filesystemmount.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/filesystemmount.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "error/error.h" 16 | #include "blockdevice/blockdevice.h" 17 | #include "files/filesystemmount.h" 18 | #include "files/filecontext.h" 19 | 20 | struct _FilesystemMount 21 | { 22 | BlockDeviceParams bd_params; 23 | FilesystemParams fs_params; 24 | }; 25 | 26 | // 27 | // filesystemmount_create 28 | // 29 | FilesystemMount *filesystemmount_create 30 | (const FilesystemParams *fs_params, 31 | const BlockDeviceParams *bd_params) 32 | { 33 | FilesystemMount *self = malloc (sizeof (FilesystemMount)); 34 | if (self) 35 | { 36 | memcpy (&self->fs_params, fs_params, sizeof (FilesystemParams)); 37 | memcpy (&self->bd_params, bd_params, sizeof (BlockDeviceParams)); 38 | } 39 | return self; 40 | } 41 | 42 | // 43 | // filesystemmount_destroy 44 | // 45 | void filesystemmount_destroy (FilesystemMount *self) 46 | { 47 | if (self) 48 | { 49 | free (self); 50 | } 51 | } 52 | 53 | // 54 | // filesystemmount_unmount 55 | // 56 | Error filesystemmount_unmount (FilesystemMount *self) 57 | { 58 | Error ret = 0; 59 | ret = self->fs_params.unmount (&self->fs_params, &self->bd_params); 60 | return ret; 61 | } 62 | 63 | // 64 | // filesystemmount_mount 65 | // 66 | Error filesystemmount_mount (FilesystemMount *self) 67 | { 68 | Error ret = 0; 69 | ret = self->fs_params.mount (&self->fs_params, &self->bd_params); 70 | return ret; 71 | } 72 | 73 | // 74 | // filesystemmount_format 75 | // 76 | Error filesystemmount_format (FilesystemMount *self) 77 | { 78 | Error ret = 0; 79 | ret = self->fs_params.format (&self->fs_params, &self->bd_params); 80 | return ret; 81 | } 82 | 83 | // 84 | // filesystemmount_open 85 | // 86 | Error filesystemmount_open (FilesystemMount *self, const char *name, int flags, 87 | OpenFileInfo *ofi) 88 | { 89 | Error ret = 0; 90 | 91 | ret = self->fs_params.open (&self->fs_params, &self->bd_params, 92 | name, flags, ofi); 93 | if (ret == 0) 94 | { 95 | ofi->mount = self; 96 | } 97 | 98 | return ret; 99 | } 100 | 101 | // 102 | // filesystemmount_close 103 | // 104 | Error filesystemmount_close (FilesystemMount *self, 105 | struct _OpenFileInfo *ofi) 106 | { 107 | return self->fs_params.close (&self->fs_params, &self->bd_params, ofi); 108 | } 109 | 110 | // 111 | // filesystemmount_write 112 | // 113 | Error filesystemmount_write (FilesystemMount *self, 114 | struct _OpenFileInfo *ofi, const uint8_t *buff, int n, 115 | uint32_t *written) 116 | { 117 | return self->fs_params.write (&self->fs_params, &self->bd_params, ofi, 118 | buff, n, written); 119 | } 120 | 121 | // 122 | // filesystemmount_read 123 | // 124 | Error filesystemmount_read (FilesystemMount *self, 125 | struct _OpenFileInfo *ofi, uint8_t *buff, int n, 126 | uint32_t *read) 127 | { 128 | return self->fs_params.read (&self->fs_params, &self->bd_params, ofi, 129 | buff, n, read); 130 | } 131 | 132 | // 133 | // filesystemmount_delete 134 | // 135 | Error filesystemmount_delete (FilesystemMount *self, 136 | const char *path) 137 | { 138 | return self->fs_params.del (&self->fs_params, &self->bd_params, path); 139 | } 140 | 141 | // 142 | // filesystemmount_opendir 143 | // 144 | my_DIR *filesystemmount_opendir (FilesystemMount *self, const char *path) 145 | { 146 | return self->fs_params.opendir (&self->fs_params, &self->bd_params, path); 147 | } 148 | 149 | // 150 | // filesystemmount_closedir 151 | // 152 | Error filesystemmount_closedir (FilesystemMount *self, my_DIR *dir) 153 | { 154 | return self->fs_params.closedir (&self->fs_params, &self->bd_params, dir); 155 | } 156 | 157 | // 158 | // filesystemmount_readdir 159 | // 160 | struct my_dirent *filesystemmount_readdir (FilesystemMount *self, 161 | struct _my_DIR *dir) 162 | { 163 | return self->fs_params.readdir (&self->fs_params, &self->bd_params, dir); 164 | } 165 | 166 | // 167 | // filesystemmount_lseek 168 | // 169 | uint32_t filesystemmount_lseek (FilesystemMount *self, 170 | struct _OpenFileInfo *ofi, uint32_t offset, int whence) 171 | { 172 | return self->fs_params.lseek (&self->fs_params, &self->bd_params, ofi, 173 | offset, whence); 174 | } 175 | 176 | // 177 | // filesystemmount_size 178 | // 179 | //uint32_t filesystemmount_size (FilesystemMount *self, const char *path) 180 | // { 181 | // return self->fs_params.size (&self->fs_params, &self->bd_params, path); 182 | // } 183 | 184 | // 185 | // filesystemmount_stat 186 | // 187 | Error filesystemmount_stat (FilesystemMount *self, 188 | const char *path, struct stat *sb) 189 | { 190 | return self->fs_params.stat (&self->fs_params, 191 | &self->bd_params, path, sb); 192 | } 193 | 194 | // 195 | // filesystemmount_fstat 196 | // 197 | Error filesystemmount_fstat (FilesystemMount *self, 198 | const struct _OpenFileInfo *ofi, struct stat *sb) 199 | { 200 | return filesystemmount_stat (self, ofi->path, sb); 201 | } 202 | 203 | 204 | // 205 | // filesystemmount_ftruncate 206 | // 207 | extern Error filesystemmount_ftruncate (FilesystemMount *self, 208 | struct _OpenFileInfo *ofi, uint32_t length) 209 | { 210 | return self->fs_params.ftruncate (&self->fs_params, 211 | &self->bd_params, ofi, length); 212 | } 213 | 214 | // 215 | // filesystemmount_rename 216 | // 217 | Error filesystemmount_rename (FilesystemMount *self, 218 | const char *source, const char *target) 219 | { 220 | return self->fs_params.rename (&self->fs_params, 221 | &self->bd_params, source, target); 222 | } 223 | 224 | // 225 | // filesystemmount_statfs 226 | // 227 | extern Error filesystemmount_statfs (FilesystemMount *self, 228 | struct _my_statfs *statfs) 229 | { 230 | return self->fs_params.statfs (&self->fs_params, 231 | &self->bd_params, statfs); 232 | } 233 | 234 | -------------------------------------------------------------------------------- /files/src/fileutil.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Files 4 | 5 | files/fileutil.c 6 | 7 | (c)2021 Kevin Boone, GPLv3.0 8 | 9 | =========================================================================*/ 10 | 11 | #include 12 | #include 13 | #include 14 | #include "log/log.h" 15 | #include "files/fileutil.h" 16 | #include "files/compat.h" 17 | 18 | // 19 | // fileutil_write_file 20 | // 21 | Error fileutil_write_file (const char *filename, 22 | uint8_t *buff, int len) 23 | { 24 | Error ret = 0; 25 | 26 | log_debug ("fileutil_write_file file=%s len=%d", filename, len); 27 | 28 | int fd = my_open (filename, O_WRONLY | O_CREAT | O_TRUNC); 29 | if (fd >= 0) 30 | { 31 | int n = my_write (fd, buff, len); 32 | if (n != len) ret = errno; 33 | my_close (fd); 34 | } 35 | else 36 | ret = errno; 37 | 38 | return ret; 39 | } 40 | 41 | // 42 | // fileutil_append_file 43 | // 44 | Error fileutil_append_file (const char *filename, 45 | uint8_t *buff, int len) 46 | { 47 | Error ret = 0; 48 | 49 | log_debug ("fileutil_append_file file=%s len=%d", filename, len); 50 | 51 | int fd = my_open (filename, O_WRONLY | O_APPEND); 52 | if (fd >= 0) 53 | { 54 | int n = my_write (fd, buff, len); 55 | if (n != len) ret = errno; 56 | my_close (fd); 57 | } 58 | else 59 | ret = errno; 60 | 61 | return ret; 62 | } 63 | 64 | -------------------------------------------------------------------------------- /fs/README.md: -------------------------------------------------------------------------------- 1 | # fs 2 | 3 | Low-level filesystem handlers. All handlers must implement the function contract 4 | set out in filesystem.h. 5 | -------------------------------------------------------------------------------- /fs/include/fs/ffconf.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------/ 2 | / FatFs Functional Configurations 3 | /---------------------------------------------------------------------------*/ 4 | 5 | #define FFCONF_DEF 86631 /* Revision ID */ 6 | 7 | /*---------------------------------------------------------------------------/ 8 | / Function Configurations 9 | /---------------------------------------------------------------------------*/ 10 | 11 | #define FF_FS_READONLY 0 12 | /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) 13 | / Read-only configuration removes writing API functions, f_write(), f_sync(), 14 | / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() 15 | / and optional writing functions as well. */ 16 | 17 | 18 | #define FF_FS_MINIMIZE 0 19 | /* This option defines minimization level to remove some basic API functions. 20 | / 21 | / 0: Basic functions are fully enabled. 22 | / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() 23 | / are removed. 24 | / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. 25 | / 3: f_lseek() function is removed in addition to 2. */ 26 | 27 | 28 | #define FF_USE_FIND 0 29 | /* This option switches filtered directory read functions, f_findfirst() and 30 | / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ 31 | 32 | 33 | #define FF_USE_MKFS 1 34 | /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ 35 | 36 | 37 | #define FF_USE_FASTSEEK 0 38 | /* This option switches fast seek function. (0:Disable or 1:Enable) */ 39 | 40 | 41 | #define FF_USE_EXPAND 0 42 | /* This option switches f_expand function. (0:Disable or 1:Enable) */ 43 | 44 | 45 | #define FF_USE_CHMOD 0 46 | /* This option switches attribute manipulation functions, f_chmod() and f_utime(). 47 | / (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ 48 | 49 | 50 | #define FF_USE_LABEL 0 51 | /* This option switches volume label functions, f_getlabel() and f_setlabel(). 52 | / (0:Disable or 1:Enable) */ 53 | 54 | 55 | #define FF_USE_FORWARD 0 56 | /* This option switches f_forward() function. (0:Disable or 1:Enable) */ 57 | 58 | 59 | #define FF_USE_STRFUNC 0 60 | #define FF_PRINT_LLI 0 61 | #define FF_PRINT_FLOAT 0 62 | #define FF_STRF_ENCODE 0 63 | /* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and 64 | / f_printf(). 65 | / 66 | / 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect. 67 | / 1: Enable without LF-CRLF conversion. 68 | / 2: Enable with LF-CRLF conversion. 69 | / 70 | / FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2 71 | makes f_printf() support floating point argument. These features want C99 or later. 72 | / When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character 73 | / encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE 74 | / to be read/written via those functions. 75 | / 76 | / 0: ANSI/OEM in current CP 77 | / 1: Unicode in UTF-16LE 78 | / 2: Unicode in UTF-16BE 79 | / 3: Unicode in UTF-8 80 | */ 81 | 82 | 83 | /*---------------------------------------------------------------------------/ 84 | / Locale and Namespace Configurations 85 | /---------------------------------------------------------------------------*/ 86 | 87 | #define FF_CODE_PAGE 932 88 | /* This option specifies the OEM code page to be used on the target system. 89 | / Incorrect code page setting can cause a file open failure. 90 | / 91 | / 437 - U.S. 92 | / 720 - Arabic 93 | / 737 - Greek 94 | / 771 - KBL 95 | / 775 - Baltic 96 | / 850 - Latin 1 97 | / 852 - Latin 2 98 | / 855 - Cyrillic 99 | / 857 - Turkish 100 | / 860 - Portuguese 101 | / 861 - Icelandic 102 | / 862 - Hebrew 103 | / 863 - Canadian French 104 | / 864 - Arabic 105 | / 865 - Nordic 106 | / 866 - Russian 107 | / 869 - Greek 2 108 | / 932 - Japanese (DBCS) 109 | / 936 - Simplified Chinese (DBCS) 110 | / 949 - Korean (DBCS) 111 | / 950 - Traditional Chinese (DBCS) 112 | / 0 - Include all code pages above and configured by f_setcp() 113 | */ 114 | 115 | 116 | #define FF_USE_LFN 0 117 | #define FF_MAX_LFN 255 118 | /* The FF_USE_LFN switches the support for LFN (long file name). 119 | / 120 | / 0: Disable LFN. FF_MAX_LFN has no effect. 121 | / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 122 | / 2: Enable LFN with dynamic working buffer on the STACK. 123 | / 3: Enable LFN with dynamic working buffer on the HEAP. 124 | / 125 | / To enable the LFN, ffunicode.c needs to be added to the project. The LFN function 126 | / requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and 127 | / additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. 128 | / The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can 129 | / be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN 130 | / specification. 131 | / When use stack for the working buffer, take care on stack overflow. When use heap 132 | / memory for the working buffer, memory management functions, ff_memalloc() and 133 | / ff_memfree() exemplified in ffsystem.c, need to be added to the project. */ 134 | 135 | 136 | #define FF_LFN_UNICODE 0 137 | /* This option switches the character encoding on the API when LFN is enabled. 138 | / 139 | / 0: ANSI/OEM in current CP (TCHAR = char) 140 | / 1: Unicode in UTF-16 (TCHAR = WCHAR) 141 | / 2: Unicode in UTF-8 (TCHAR = char) 142 | / 3: Unicode in UTF-32 (TCHAR = DWORD) 143 | / 144 | / Also behavior of string I/O functions will be affected by this option. 145 | / When LFN is not enabled, this option has no effect. */ 146 | 147 | 148 | #define FF_LFN_BUF 255 149 | #define FF_SFN_BUF 12 150 | /* This set of options defines size of file name members in the FILINFO structure 151 | / which is used to read out directory items. These values should be suffcient for 152 | / the file names to read. The maximum possible length of the read file name depends 153 | / on character encoding. When LFN is not enabled, these options have no effect. */ 154 | 155 | 156 | #define FF_FS_RPATH 0 157 | /* This option configures support for relative path. 158 | / 159 | / 0: Disable relative path and remove related functions. 160 | / 1: Enable relative path. f_chdir() and f_chdrive() are available. 161 | / 2: f_getcwd() function is available in addition to 1. 162 | */ 163 | 164 | 165 | /*---------------------------------------------------------------------------/ 166 | / Drive/Volume Configurations 167 | /---------------------------------------------------------------------------*/ 168 | 169 | #define FF_VOLUMES 4 170 | /* Number of volumes (logical drives) to be used. (1-10) */ 171 | 172 | 173 | #define FF_STR_VOLUME_ID 0 174 | #define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" 175 | /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. 176 | / When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive 177 | / number in the path name. FF_VOLUME_STRS defines the volume ID strings for each 178 | / logical drives. Number of items must not be less than FF_VOLUMES. Valid 179 | / characters for the volume ID strings are A-Z, a-z and 0-9, however, they are 180 | / compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is 181 | / not defined, a user defined volume string table needs to be defined as: 182 | / 183 | / const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... 184 | */ 185 | 186 | 187 | #define FF_MULTI_PARTITION 0 188 | /* This option switches support for multiple volumes on the physical drive. 189 | / By default (0), each logical drive number is bound to the same physical drive 190 | / number and only an FAT volume found on the physical drive will be mounted. 191 | / When this function is enabled (1), each logical drive number can be bound to 192 | / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() 193 | / funciton will be available. */ 194 | 195 | 196 | #define FF_MIN_SS 512 197 | #define FF_MAX_SS 512 198 | /* This set of options configures the range of sector size to be supported. (512, 199 | / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and 200 | / harddisk, but a larger value may be required for on-board flash memory and some 201 | / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured 202 | / for variable sector size mode and disk_ioctl() function needs to implement 203 | / GET_SECTOR_SIZE command. */ 204 | 205 | 206 | #define FF_LBA64 0 207 | /* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) 208 | / To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ 209 | 210 | 211 | #define FF_MIN_GPT 0x10000000 212 | /* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and 213 | / f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */ 214 | 215 | 216 | #define FF_USE_TRIM 0 217 | /* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) 218 | / To enable Trim function, also CTRL_TRIM command should be implemented to the 219 | / disk_ioctl() function. */ 220 | 221 | 222 | 223 | /*---------------------------------------------------------------------------/ 224 | / System Configurations 225 | /---------------------------------------------------------------------------*/ 226 | 227 | #define FF_FS_TINY 0 228 | /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) 229 | / At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes. 230 | / Instead of private sector buffer eliminated from the file object, common sector 231 | / buffer in the filesystem object (FATFS) is used for the file data transfer. */ 232 | 233 | 234 | #define FF_FS_EXFAT 0 235 | /* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) 236 | / To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) 237 | / Note that enabling exFAT discards ANSI C (C89) compatibility. */ 238 | 239 | 240 | #define FF_FS_NORTC 0 241 | #define FF_NORTC_MON 1 242 | #define FF_NORTC_MDAY 1 243 | #define FF_NORTC_YEAR 2020 244 | /* The option FF_FS_NORTC switches timestamp functiton. If the system does not have 245 | / any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable 246 | / the timestamp function. Every object modified by FatFs will have a fixed timestamp 247 | / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. 248 | / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be 249 | / added to the project to read current time form real-time clock. FF_NORTC_MON, 250 | / FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. 251 | / These options have no effect in read-only configuration (FF_FS_READONLY = 1). */ 252 | 253 | 254 | #define FF_FS_NOFSINFO 0 255 | /* If you need to know correct free space on the FAT32 volume, set bit 0 of this 256 | / option, and f_getfree() function at first time after volume mount will force 257 | / a full FAT scan. Bit 1 controls the use of last allocated cluster number. 258 | / 259 | / bit0=0: Use free cluster count in the FSINFO if available. 260 | / bit0=1: Do not trust free cluster count in the FSINFO. 261 | / bit1=0: Use last allocated cluster number in the FSINFO if available. 262 | / bit1=1: Do not trust last allocated cluster number in the FSINFO. 263 | */ 264 | 265 | 266 | #define FF_FS_LOCK 0 267 | /* The option FF_FS_LOCK switches file lock function to control duplicated file open 268 | / and illegal operation to open objects. This option must be 0 when FF_FS_READONLY 269 | / is 1. 270 | / 271 | / 0: Disable file lock function. To avoid volume corruption, application program 272 | / should avoid illegal open, remove and rename to the open objects. 273 | / >0: Enable file lock function. The value defines how many files/sub-directories 274 | / can be opened simultaneously under file lock control. Note that the file 275 | / lock control is independent of re-entrancy. */ 276 | 277 | 278 | /* #include // O/S definitions */ 279 | #define FF_FS_REENTRANT 0 280 | #define FF_FS_TIMEOUT 1000 281 | #define FF_SYNC_t HANDLE 282 | /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs 283 | / module itself. Note that regardless of this option, file access to different 284 | / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() 285 | / and f_fdisk() function, are always not re-entrant. Only file/directory access 286 | / to the same volume is under control of this function. 287 | / 288 | / 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect. 289 | / 1: Enable re-entrancy. Also user provided synchronization handlers, 290 | / ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() 291 | / function, must be added to the project. Samples are available in 292 | / option/syscall.c. 293 | / 294 | / The FF_FS_TIMEOUT defines timeout period in unit of time tick. 295 | / The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, 296 | / SemaphoreHandle_t and etc. A header file for O/S definitions needs to be 297 | / included somewhere in the scope of ff.h. */ 298 | 299 | 300 | 301 | /*--- End of configuration options ---*/ 302 | -------------------------------------------------------------------------------- /fs/include/fs/ffdiskio.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level disk interface modlue include file (C)ChaN, 2019 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #include "blockdevice/blockdevice.h" 9 | #include "fs/ffconf.h" 10 | 11 | // instantiated in ffdiskio.c 12 | extern BlockDeviceParams bdp[FF_VOLUMES]; 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | /* Status of Disk Functions */ 19 | typedef BYTE DSTATUS; 20 | 21 | /* Results of Disk Functions */ 22 | typedef enum { 23 | RES_OK = 0, /* 0: Successful */ 24 | RES_ERROR, /* 1: R/W Error */ 25 | RES_WRPRT, /* 2: Write Protected */ 26 | RES_NOTRDY, /* 3: Not Ready */ 27 | RES_PARERR /* 4: Invalid Parameter */ 28 | } DRESULT; 29 | 30 | 31 | /*---------------------------------------*/ 32 | /* Prototypes for disk control functions */ 33 | 34 | 35 | DSTATUS disk_initialize (BYTE pdrv); 36 | DSTATUS disk_status (BYTE pdrv); 37 | DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); 38 | DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count); 39 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); 40 | 41 | 42 | /* Disk Status Bits (DSTATUS) */ 43 | 44 | #define STA_NOINIT 0x01 /* Drive not initialized */ 45 | #define STA_NODISK 0x02 /* No medium in the drive */ 46 | #define STA_PROTECT 0x04 /* Write protected */ 47 | 48 | 49 | /* Command code for disk_ioctrl fucntion */ 50 | 51 | /* Generic command (Used by FatFs) */ 52 | #define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */ 53 | #define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */ 54 | #define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */ 55 | #define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */ 56 | #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ 57 | 58 | /* Generic command (Not used by FatFs) */ 59 | #define CTRL_POWER 5 /* Get/Set power status */ 60 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 61 | #define CTRL_EJECT 7 /* Eject media */ 62 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 63 | 64 | /* MMC/SDC specific ioctl command */ 65 | #define MMC_GET_TYPE 10 /* Get card type */ 66 | #define MMC_GET_CSD 11 /* Get CSD */ 67 | #define MMC_GET_CID 12 /* Get CID */ 68 | #define MMC_GET_OCR 13 /* Get OCR */ 69 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 70 | #define ISDIO_READ 55 /* Read data form SD iSDIO register */ 71 | #define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ 72 | #define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ 73 | 74 | /* ATA/CF specific ioctl command */ 75 | #define ATA_GET_REV 20 /* Get F/W revision */ 76 | #define ATA_GET_MODEL 21 /* Get model name */ 77 | #define ATA_GET_SN 22 /* Get serial number */ 78 | 79 | #ifdef __cplusplus 80 | } 81 | #endif 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /fs/include/fs/filesystem.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/filesystem.h 6 | 7 | Typedefs for all the functions that a specific filesystem handler 8 | must implement, and the FilesystemParams structure in which they 9 | are stored. 10 | 11 | Copyright (c)2001 Kevin Boone, GPL v3.0 12 | 13 | =========================================================================*/ 14 | #pragma once 15 | 16 | #include 17 | #include 18 | #include "error/error.h" 19 | #include "fs/filesystem.h" 20 | #include "blockdevice/blockdevice.h" 21 | #include "files/filecontext.h" 22 | 23 | struct _FilesystemParams; 24 | struct _OpenFileInfo; 25 | struct _my_DIR; 26 | struct _my_statfs; 27 | 28 | typedef Error (*filesystem_open_fn) (struct _FilesystemParams *fs_params, 29 | BlockDeviceParams *bd_params, const char *name, int flags, 30 | struct _OpenFileInfo *ofi); 31 | 32 | typedef Error (*filesystem_write_fn) (struct _FilesystemParams *fs_params, 33 | BlockDeviceParams *bd_params, struct _OpenFileInfo *ofi, 34 | const uint8_t*buffer, uint32_t len, uint32_t *written); 35 | 36 | typedef Error (*filesystem_read_fn) (struct _FilesystemParams *fs_params, 37 | BlockDeviceParams *bd_params, struct _OpenFileInfo *ofi, 38 | uint8_t*buffer, uint32_t len, uint32_t *read); 39 | 40 | typedef Error (*filesystem_close_fn) (struct _FilesystemParams *fs_params, 41 | BlockDeviceParams *bd_params, struct _OpenFileInfo *ofi); 42 | 43 | typedef Error (*filesystem_mount_fn) 44 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params); 45 | 46 | typedef Error (*filesystem_unmount_fn) 47 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params); 48 | 49 | typedef Error (*filesystem_format_fn) 50 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params); 51 | 52 | typedef Error (*filesystem_stat_fn) 53 | (struct _FilesystemParams *fs_params, 54 | BlockDeviceParams *bd_params, const char *path, struct stat *sb); 55 | 56 | typedef Error (*filesystem_delete_fn) 57 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params, 58 | const char *filename); 59 | 60 | typedef struct _my_DIR *(*filesystem_opendir_fn) 61 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params, 62 | const char *path); 63 | 64 | typedef Error (*filesystem_closedir_fn) 65 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params, 66 | struct _my_DIR *dir); 67 | 68 | typedef struct my_dirent *(*filesystem_readdir_fn) 69 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params, 70 | struct _my_DIR *dir); 71 | 72 | typedef uint32_t (*filesystem_lseek_fn) 73 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params, 74 | struct _OpenFileInfo *ofi, uint32_t offset, int whence); 75 | 76 | typedef Error (*filesystem_ftruncate_fn) 77 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params, 78 | struct _OpenFileInfo *ofi, uint32_t length); 79 | 80 | //typedef uint32_t (*filesystem_size_fn) 81 | // (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params, 82 | // const char *path); 83 | 84 | typedef Error (*filesystem_rename_fn) 85 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params, 86 | const char *source, const char *target); 87 | 88 | typedef Error (*filesystem_statfs_fn) 89 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params, 90 | struct _my_statfs *statfs); 91 | 92 | typedef struct _FilesystemParams 93 | { 94 | char desc [FILESYSTEMPARAMS_MAX_DESC + 1]; 95 | filesystem_mount_fn mount; 96 | filesystem_unmount_fn unmount; 97 | filesystem_format_fn format; 98 | filesystem_open_fn open; 99 | filesystem_close_fn close; 100 | filesystem_write_fn write; 101 | filesystem_read_fn read; 102 | filesystem_delete_fn del; 103 | filesystem_opendir_fn opendir; 104 | filesystem_closedir_fn closedir; 105 | filesystem_readdir_fn readdir; 106 | filesystem_lseek_fn lseek; 107 | //filesystem_size_fn size; 108 | filesystem_stat_fn stat; 109 | filesystem_ftruncate_fn ftruncate; 110 | filesystem_rename_fn rename; 111 | filesystem_statfs_fn statfs; 112 | void *context; 113 | } FilesystemParams; 114 | 115 | -------------------------------------------------------------------------------- /fs/include/fs/filesystemfat.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/filesystemfat.h 6 | 7 | Specification for a wrapper for the FatFS filesystem implementation. 8 | 9 | Copyright (c)2001 Kevin Boone, GPL v3.0 10 | 11 | =========================================================================*/ 12 | #pragma once 13 | 14 | #include 15 | #include "blockdevice/blockdevice.h" 16 | #include "filesystem.h" 17 | 18 | struct _FilesystemFat; 19 | typedef struct _FilesystemFat FilesystemFat; 20 | 21 | extern FilesystemFat *filesystemfat_create (int ffvol); 22 | extern void filesystemfat_destroy (FilesystemFat *self); 23 | 24 | extern void filesystemfat_set_block_device (FilesystemFat *self, 25 | BlockDeviceParams *bd_params); 26 | 27 | extern void filesystemfat_get_params (FilesystemFat *self, 28 | FilesystemParams *fs_params); 29 | 30 | -------------------------------------------------------------------------------- /fs/include/fs/filesystemlfs.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/filesystemlfs.h 6 | 7 | Specification for a wrapper for the LitleFS filesystem implementation. 8 | 9 | Copyright (c)2001 Kevin Boone, GPL v3.0 10 | 11 | =========================================================================*/ 12 | #pragma once 13 | 14 | #include 15 | #include "blockdevice/blockdevice.h" 16 | #include "filesystem.h" 17 | 18 | struct _FilesystemLfs; 19 | typedef struct _FilesystemLfs FilesystemLfs; 20 | 21 | extern FilesystemLfs *filesystemlfs_create (void); 22 | extern void filesystemlfs_destroy (FilesystemLfs *self); 23 | 24 | extern void filesystemlfs_set_block_device (FilesystemLfs *self, 25 | BlockDeviceParams *bd_params); 26 | 27 | extern void filesystemlfs_get_params (FilesystemLfs *self, 28 | FilesystemParams *fs_params); 29 | 30 | 31 | -------------------------------------------------------------------------------- /fs/include/fs/lfs_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * lfs utility functions 3 | * 4 | * Copyright (c) 2017, Arm Limited. All rights reserved. 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | */ 7 | #ifndef LFS_UTIL_H 8 | #define LFS_UTIL_H 9 | 10 | // Users can override lfs_util.h with their own configuration by defining 11 | // LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). 12 | // 13 | // If LFS_CONFIG is used, none of the default utils will be emitted and must be 14 | // provided by the config file. To start, I would suggest copying lfs_util.h 15 | // and modifying as needed. 16 | #ifdef LFS_CONFIG 17 | #define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) 18 | #define LFS_STRINGIZE2(x) #x 19 | #include LFS_STRINGIZE(LFS_CONFIG) 20 | #else 21 | 22 | // System includes 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifndef LFS_NO_MALLOC 29 | #include 30 | #endif 31 | #ifndef LFS_NO_ASSERT 32 | #include 33 | #endif 34 | #if !defined(LFS_NO_DEBUG) || \ 35 | !defined(LFS_NO_WARN) || \ 36 | !defined(LFS_NO_ERROR) || \ 37 | defined(LFS_YES_TRACE) 38 | #include 39 | #endif 40 | 41 | #ifdef __cplusplus 42 | extern "C" 43 | { 44 | #endif 45 | 46 | 47 | // Macros, may be replaced by system specific wrappers. Arguments to these 48 | // macros must not have side-effects as the macros can be removed for a smaller 49 | // code footprint 50 | 51 | // Logging functions 52 | #ifndef LFS_TRACE 53 | #ifdef LFS_YES_TRACE 54 | #define LFS_TRACE_(fmt, ...) \ 55 | printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) 56 | #define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "") 57 | #else 58 | #define LFS_TRACE(...) 59 | #endif 60 | #endif 61 | 62 | #ifndef LFS_DEBUG 63 | #ifndef LFS_NO_DEBUG 64 | #define LFS_DEBUG_(fmt, ...) \ 65 | printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) 66 | #define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "") 67 | #else 68 | #define LFS_DEBUG(...) 69 | #endif 70 | #endif 71 | 72 | #ifndef LFS_WARN 73 | #ifndef LFS_NO_WARN 74 | #define LFS_WARN_(fmt, ...) \ 75 | printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) 76 | #define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "") 77 | #else 78 | #define LFS_WARN(...) 79 | #endif 80 | #endif 81 | 82 | #ifndef LFS_ERROR 83 | #ifndef LFS_NO_ERROR 84 | #define LFS_ERROR_(fmt, ...) \ 85 | printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) 86 | #define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "") 87 | #else 88 | #define LFS_ERROR(...) 89 | #endif 90 | #endif 91 | 92 | // Runtime assertions 93 | #ifndef LFS_ASSERT 94 | #ifndef LFS_NO_ASSERT 95 | #define LFS_ASSERT(test) assert(test) 96 | #else 97 | #define LFS_ASSERT(test) 98 | #endif 99 | #endif 100 | 101 | 102 | // Builtin functions, these may be replaced by more efficient 103 | // toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more 104 | // expensive basic C implementation for debugging purposes 105 | 106 | // Min/max functions for unsigned 32-bit numbers 107 | static inline uint32_t lfs_max(uint32_t a, uint32_t b) { 108 | return (a > b) ? a : b; 109 | } 110 | 111 | static inline uint32_t lfs_min(uint32_t a, uint32_t b) { 112 | return (a < b) ? a : b; 113 | } 114 | 115 | // Align to nearest multiple of a size 116 | static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) { 117 | return a - (a % alignment); 118 | } 119 | 120 | static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) { 121 | return lfs_aligndown(a + alignment-1, alignment); 122 | } 123 | 124 | // Find the smallest power of 2 greater than or equal to a 125 | static inline uint32_t lfs_npw2(uint32_t a) { 126 | #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) 127 | return 32 - __builtin_clz(a-1); 128 | #else 129 | uint32_t r = 0; 130 | uint32_t s; 131 | a -= 1; 132 | s = (a > 0xffff) << 4; a >>= s; r |= s; 133 | s = (a > 0xff ) << 3; a >>= s; r |= s; 134 | s = (a > 0xf ) << 2; a >>= s; r |= s; 135 | s = (a > 0x3 ) << 1; a >>= s; r |= s; 136 | return (r | (a >> 1)) + 1; 137 | #endif 138 | } 139 | 140 | // Count the number of trailing binary zeros in a 141 | // lfs_ctz(0) may be undefined 142 | static inline uint32_t lfs_ctz(uint32_t a) { 143 | #if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__) 144 | return __builtin_ctz(a); 145 | #else 146 | return lfs_npw2((a & -a) + 1) - 1; 147 | #endif 148 | } 149 | 150 | // Count the number of binary ones in a 151 | static inline uint32_t lfs_popc(uint32_t a) { 152 | #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) 153 | return __builtin_popcount(a); 154 | #else 155 | a = a - ((a >> 1) & 0x55555555); 156 | a = (a & 0x33333333) + ((a >> 2) & 0x33333333); 157 | return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; 158 | #endif 159 | } 160 | 161 | // Find the sequence comparison of a and b, this is the distance 162 | // between a and b ignoring overflow 163 | static inline int lfs_scmp(uint32_t a, uint32_t b) { 164 | return (int)(unsigned)(a - b); 165 | } 166 | 167 | // Convert between 32-bit little-endian and native order 168 | static inline uint32_t lfs_fromle32(uint32_t a) { 169 | #if !defined(LFS_NO_INTRINSICS) && ( \ 170 | (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ 171 | (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ 172 | (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) 173 | return a; 174 | #elif !defined(LFS_NO_INTRINSICS) && ( \ 175 | (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ 176 | (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ 177 | (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) 178 | return __builtin_bswap32(a); 179 | #else 180 | return (((uint8_t*)&a)[0] << 0) | 181 | (((uint8_t*)&a)[1] << 8) | 182 | (((uint8_t*)&a)[2] << 16) | 183 | (((uint8_t*)&a)[3] << 24); 184 | #endif 185 | } 186 | 187 | static inline uint32_t lfs_tole32(uint32_t a) { 188 | return lfs_fromle32(a); 189 | } 190 | 191 | // Convert between 32-bit big-endian and native order 192 | static inline uint32_t lfs_frombe32(uint32_t a) { 193 | #if !defined(LFS_NO_INTRINSICS) && ( \ 194 | (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ 195 | (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ 196 | (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) 197 | return __builtin_bswap32(a); 198 | #elif !defined(LFS_NO_INTRINSICS) && ( \ 199 | (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ 200 | (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ 201 | (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) 202 | return a; 203 | #else 204 | return (((uint8_t*)&a)[0] << 24) | 205 | (((uint8_t*)&a)[1] << 16) | 206 | (((uint8_t*)&a)[2] << 8) | 207 | (((uint8_t*)&a)[3] << 0); 208 | #endif 209 | } 210 | 211 | static inline uint32_t lfs_tobe32(uint32_t a) { 212 | return lfs_frombe32(a); 213 | } 214 | 215 | // Calculate CRC-32 with polynomial = 0x04c11db7 216 | uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); 217 | 218 | // Allocate memory, only used if buffers are not provided to littlefs 219 | // Note, memory must be 64-bit aligned 220 | static inline void *lfs_malloc(size_t size) { 221 | #ifndef LFS_NO_MALLOC 222 | return malloc(size); 223 | #else 224 | (void)size; 225 | return NULL; 226 | #endif 227 | } 228 | 229 | // Deallocate memory, only used if buffers are not provided to littlefs 230 | static inline void lfs_free(void *p) { 231 | #ifndef LFS_NO_MALLOC 232 | free(p); 233 | #else 234 | (void)p; 235 | #endif 236 | } 237 | 238 | 239 | #ifdef __cplusplus 240 | } /* extern "C" */ 241 | #endif 242 | 243 | #endif 244 | #endif 245 | -------------------------------------------------------------------------------- /fs/src/ffdiskio.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | fs/ffdiskio.c 6 | 7 | Plumbing for the FAT filesystem driver, which expects to be able 8 | to call functions disk_read(), disk_ioctl(), etc. 9 | 10 | Copyright (c)2001 Kevin Boone, GPL v3.0 11 | 12 | =========================================================================*/ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "fs/ff.h" 19 | #include "fs/ffdiskio.h" 20 | #include "log/log.h" 21 | 22 | // global object shall be defined here: 23 | BlockDeviceParams bdp[FF_VOLUMES]; 24 | 25 | 26 | 27 | // 28 | // disk_status 29 | // 30 | DSTATUS disk_status (BYTE pdrv) 31 | { 32 | (void)pdrv; 33 | // At present we don't have a way to check the underlying drive 34 | // status -- it is always ready. 35 | return 0; 36 | } 37 | 38 | // 39 | // disk_initialize 40 | // 41 | DSTATUS disk_initialize (BYTE pdrv) 42 | { 43 | (void)pdrv; 44 | // At present we have nothing to initialize 45 | return 0; 46 | } 47 | 48 | // 49 | // disk_get_block_and_offset 50 | // 51 | static void disk_get_block_and_offset (BlockDeviceParams *bd_params, 52 | LBA_t sector, int *block, int *offset) 53 | { 54 | if (FF_MIN_SS >= bd_params->block_size) 55 | { 56 | int bps = FF_MIN_SS / bd_params->block_size; 57 | *block = bps * sector; 58 | *offset = 0; 59 | } 60 | else 61 | { 62 | // Example: my sector size = 512, disk block size = 4096 63 | // We need sector 10. That's in block 01, two sector length in 64 | // Get disk block by dividing by blocks per sector -- 10 / 8 = 1; 65 | // To get the offset in sectors -- 10 % 8 = 2; 66 | int spb = bd_params->block_size / FF_MIN_SS; 67 | *block = sector / spb; 68 | *offset = (sector % spb) * FF_MIN_SS; 69 | } 70 | } 71 | 72 | 73 | // 74 | // disk_read 75 | // 76 | DRESULT disk_read (BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) 77 | { 78 | BlockDeviceParams *bd_params = &bdp[pdrv]; 79 | int block; int offset; 80 | disk_get_block_and_offset (bd_params, sector, &block, &offset); 81 | Error ret = bd_params->read_fn (bd_params->context, buff, block, offset, 82 | count * FF_MIN_SS); 83 | 84 | return (ret ? 1 : 0); // TODO convert return to a DRESULT 85 | } 86 | 87 | 88 | // 89 | // disk_write 90 | // 91 | DRESULT disk_write (BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) 92 | { 93 | BlockDeviceParams *bd_params = &bdp[pdrv]; 94 | int block; int offset; 95 | disk_get_block_and_offset (bd_params, sector, &block, &offset); 96 | Error ret = bd_params->write_fn (bd_params->context, buff, block, offset, 97 | count * FF_MIN_SS); 98 | return (ret ? 1 : 0); // TODO convert return to a DRESULT 99 | } 100 | 101 | 102 | // 103 | // disk_ioctl 104 | // 105 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void *buff) 106 | { 107 | (void)pdrv; 108 | (void)buff; 109 | switch (cmd) 110 | { 111 | case CTRL_SYNC: 112 | { 113 | BlockDeviceParams *bd_params = &bdp[pdrv]; 114 | bd_params->sync_fn (bd_params->context, 0, 0); 115 | return 0; 116 | } 117 | 118 | case GET_SECTOR_COUNT: 119 | { 120 | BlockDeviceParams *bd_params = &bdp[pdrv]; 121 | uint32_t blocks = bd_params->block_count; 122 | 123 | log_debug ("disk_ioctl block count = %d", blocks); 124 | uint32_t sectors; 125 | if (bd_params->block_size >= FF_MIN_SS) 126 | sectors = blocks * FF_MIN_SS / bd_params->block_size; 127 | else 128 | sectors = blocks * bd_params->block_size / FF_MIN_SS; 129 | 130 | uint32_t *r = (uint32_t*)buff; 131 | *r = sectors; 132 | 133 | log_debug ("disk_ioctl returning %d sectors", *r); 134 | 135 | return 0; 136 | } 137 | 138 | default: 139 | printf ("ioctl unhandled cmd %d\n", cmd); 140 | return RES_PARERR; 141 | } 142 | } 143 | 144 | // 145 | // disk_fattime 146 | // 147 | // We have no way to get a time -- return zero always 148 | DWORD get_fattime (void) 149 | { 150 | return 0; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /fs/src/ffsystem.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* Sample Code of OS Dependent Functions for FatFs */ 3 | /* (C)ChaN, 2018 */ 4 | /*------------------------------------------------------------------------*/ 5 | 6 | 7 | #include "fs/ff.h" 8 | 9 | 10 | #if FF_USE_LFN == 3 /* Dynamic memory allocation */ 11 | 12 | /*------------------------------------------------------------------------*/ 13 | /* Allocate a memory block */ 14 | /*------------------------------------------------------------------------*/ 15 | 16 | void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */ 17 | UINT msize /* Number of bytes to allocate */ 18 | ) 19 | { 20 | return malloc(msize); /* Allocate a new memory block with POSIX API */ 21 | } 22 | 23 | 24 | /*------------------------------------------------------------------------*/ 25 | /* Free a memory block */ 26 | /*------------------------------------------------------------------------*/ 27 | 28 | void ff_memfree ( 29 | void* mblock /* Pointer to the memory block to free (nothing to do if null) */ 30 | ) 31 | { 32 | free(mblock); /* Free the memory block with POSIX API */ 33 | } 34 | 35 | #endif 36 | 37 | 38 | 39 | #if FF_FS_REENTRANT /* Mutal exclusion */ 40 | 41 | /*------------------------------------------------------------------------*/ 42 | /* Create a Synchronization Object */ 43 | /*------------------------------------------------------------------------*/ 44 | /* This function is called in f_mount() function to create a new 45 | / synchronization object for the volume, such as semaphore and mutex. 46 | / When a 0 is returned, the f_mount() function fails with FR_INT_ERR. 47 | */ 48 | 49 | //const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */ 50 | 51 | 52 | int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */ 53 | BYTE vol, /* Corresponding volume (logical drive number) */ 54 | FF_SYNC_t* sobj /* Pointer to return the created sync object */ 55 | ) 56 | { 57 | /* Win32 */ 58 | *sobj = CreateMutex(NULL, FALSE, NULL); 59 | return (int)(*sobj != INVALID_HANDLE_VALUE); 60 | 61 | /* uITRON */ 62 | // T_CSEM csem = {TA_TPRI,1,1}; 63 | // *sobj = acre_sem(&csem); 64 | // return (int)(*sobj > 0); 65 | 66 | /* uC/OS-II */ 67 | // OS_ERR err; 68 | // *sobj = OSMutexCreate(0, &err); 69 | // return (int)(err == OS_NO_ERR); 70 | 71 | /* FreeRTOS */ 72 | // *sobj = xSemaphoreCreateMutex(); 73 | // return (int)(*sobj != NULL); 74 | 75 | /* CMSIS-RTOS */ 76 | // *sobj = osMutexCreate(&Mutex[vol]); 77 | // return (int)(*sobj != NULL); 78 | } 79 | 80 | 81 | /*------------------------------------------------------------------------*/ 82 | /* Delete a Synchronization Object */ 83 | /*------------------------------------------------------------------------*/ 84 | /* This function is called in f_mount() function to delete a synchronization 85 | / object that created with ff_cre_syncobj() function. When a 0 is returned, 86 | / the f_mount() function fails with FR_INT_ERR. 87 | */ 88 | 89 | int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */ 90 | FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ 91 | ) 92 | { 93 | /* Win32 */ 94 | return (int)CloseHandle(sobj); 95 | 96 | /* uITRON */ 97 | // return (int)(del_sem(sobj) == E_OK); 98 | 99 | /* uC/OS-II */ 100 | // OS_ERR err; 101 | // OSMutexDel(sobj, OS_DEL_ALWAYS, &err); 102 | // return (int)(err == OS_NO_ERR); 103 | 104 | /* FreeRTOS */ 105 | // vSemaphoreDelete(sobj); 106 | // return 1; 107 | 108 | /* CMSIS-RTOS */ 109 | // return (int)(osMutexDelete(sobj) == osOK); 110 | } 111 | 112 | 113 | /*------------------------------------------------------------------------*/ 114 | /* Request Grant to Access the Volume */ 115 | /*------------------------------------------------------------------------*/ 116 | /* This function is called on entering file functions to lock the volume. 117 | / When a 0 is returned, the file function fails with FR_TIMEOUT. 118 | */ 119 | 120 | int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ 121 | FF_SYNC_t sobj /* Sync object to wait */ 122 | ) 123 | { 124 | /* Win32 */ 125 | return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0); 126 | 127 | /* uITRON */ 128 | // return (int)(wai_sem(sobj) == E_OK); 129 | 130 | /* uC/OS-II */ 131 | // OS_ERR err; 132 | // OSMutexPend(sobj, FF_FS_TIMEOUT, &err)); 133 | // return (int)(err == OS_NO_ERR); 134 | 135 | /* FreeRTOS */ 136 | // return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE); 137 | 138 | /* CMSIS-RTOS */ 139 | // return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK); 140 | } 141 | 142 | 143 | /*------------------------------------------------------------------------*/ 144 | /* Release Grant to Access the Volume */ 145 | /*------------------------------------------------------------------------*/ 146 | /* This function is called on leaving file functions to unlock the volume. 147 | */ 148 | 149 | void ff_rel_grant ( 150 | FF_SYNC_t sobj /* Sync object to be signaled */ 151 | ) 152 | { 153 | /* Win32 */ 154 | ReleaseMutex(sobj); 155 | 156 | /* uITRON */ 157 | // sig_sem(sobj); 158 | 159 | /* uC/OS-II */ 160 | // OSMutexPost(sobj); 161 | 162 | /* FreeRTOS */ 163 | // xSemaphoreGive(sobj); 164 | 165 | /* CMSIS-RTOS */ 166 | // osMutexRelease(sobj); 167 | } 168 | 169 | #endif 170 | 171 | -------------------------------------------------------------------------------- /fs/src/filesystemfat.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | files/filesystemfat.c 6 | 7 | CPICOM-compatible wrapper for the FatFS filesystem handler 8 | 9 | Copyright (c)2001 Kevin Boone, GPL v3.0 10 | 11 | =========================================================================*/ 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "error/error.h" 19 | #include "files/compat.h" 20 | #include "log/log.h" 21 | #include "blockdevice/blockdevice.h" 22 | #include "fs/filesystemfat.h" 23 | #include "fs/ff.h" 24 | #include "fs/ffdiskio.h" 25 | 26 | struct _FilesystemFat 27 | { 28 | BlockDeviceParams bd_params; 29 | int ffvol; 30 | FATFS fatfs; 31 | }; 32 | 33 | // 34 | // filesystemfat_create 35 | // 36 | FilesystemFat *filesystemfat_create (int ffvol) 37 | { 38 | FilesystemFat *self = malloc (sizeof (FilesystemFat)); 39 | memset (self, 0, sizeof (FilesystemFat)); 40 | self->ffvol = ffvol; 41 | return self; 42 | } 43 | 44 | // 45 | // filesystemfat_destroy 46 | // 47 | void filesystemfat_destroy (FilesystemFat *self) 48 | { 49 | if (self) 50 | { 51 | free (self); 52 | } 53 | } 54 | 55 | // 56 | // filesystemfat_prepend_drive_num 57 | // 58 | char *filesystemfat_prepend_drive_num (const FilesystemFat *self, 59 | const char *path) 60 | { 61 | char *s = malloc (strlen (path) + 4); 62 | sprintf (s, "%d:%s", self->ffvol, path); 63 | return s; 64 | } 65 | 66 | // 67 | // filesystemfat_convert_error 68 | // 69 | static Error filesystemfat_convert_error (int lfs_error) 70 | { 71 | switch (lfs_error) 72 | { 73 | case 0: return 0; 74 | case FR_DISK_ERR: return ERROR_DISK_LOWLEVEL; 75 | case FR_NOT_READY: return ERROR_DRIVE_NOT_READY; 76 | case FR_NO_FILE: return ENOENT; 77 | case FR_NO_PATH: return ENOENT; 78 | case FR_INVALID_NAME: return ERROR_FILENAME; 79 | case FR_DENIED: return EACCES; 80 | case FR_EXIST: return EACCES; 81 | case FR_WRITE_PROTECTED: return EROFS; 82 | case FR_INVALID_DRIVE: return ERROR_BADDRIVELETTER; 83 | case FR_NO_FILESYSTEM: return ERROR_CORRUPT_FS; 84 | case FR_TOO_MANY_OPEN_FILES: return EMFILE; 85 | default: return ERROR_INTERNAL; 86 | } 87 | } 88 | 89 | // 90 | // filesystemfat_format 91 | // 92 | Error filesystemfat_format (FilesystemParams *fs_params, 93 | BlockDeviceParams *bd_params) 94 | { 95 | (void)bd_params; 96 | log_info ("Fmt LFS fs"); 97 | FilesystemFat *self = (FilesystemFat *)fs_params->context; 98 | char *temp = filesystemfat_prepend_drive_num (self, ""); 99 | int bufflen = 10 * FF_MIN_SS; 100 | void *buff = malloc (bufflen); 101 | MKFS_PARM opt; 102 | opt.fmt = FM_ANY; 103 | opt.au_size = 0; // Use default 104 | opt.align = 1; 105 | opt.n_fat = 2; 106 | opt.n_root = 0; 107 | int faterr = f_mkfs (temp, &opt, buff, bufflen); 108 | free (temp); 109 | free (buff); 110 | return filesystemfat_convert_error (faterr); 111 | } 112 | 113 | 114 | // 115 | // filesystemfat_set_block_device 116 | // 117 | void filesystemfat_set_block_device (FilesystemFat *self, 118 | BlockDeviceParams *bd_params) 119 | { 120 | memcpy (&self->bd_params, bd_params, sizeof (BlockDeviceParams)); 121 | 122 | //log_debug ("Set LFS blk sz to %d", self->cfg.read_size); 123 | } 124 | 125 | // 126 | // filesystemfat_mount 127 | // 128 | Error filesystemfat_mount (FilesystemParams *fs_params, 129 | BlockDeviceParams *bd_params) 130 | { 131 | (void)bd_params; 132 | log_info ("Mnt FAT fs"); 133 | FilesystemFat *self = (FilesystemFat *)fs_params->context; 134 | 135 | memcpy (&bdp[self->ffvol], bd_params, sizeof (BlockDeviceParams)); 136 | char *temp = filesystemfat_prepend_drive_num (self, ""); 137 | int faterr = f_mount (&self->fatfs, temp, 1); 138 | free (temp); 139 | 140 | return filesystemfat_convert_error (faterr); 141 | } 142 | 143 | // 144 | // filesystemfat_unmount 145 | // 146 | Error filesystemfat_unmount (FilesystemParams *fs_params, 147 | BlockDeviceParams *bd_params) 148 | { 149 | (void)bd_params; 150 | log_info ("Unmnt FAT fs"); 151 | FilesystemFat *self = (FilesystemFat *)fs_params->context; 152 | char *temp = filesystemfat_prepend_drive_num (self, ""); 153 | int faterr = f_unmount (temp); 154 | free (temp); 155 | return filesystemfat_convert_error (faterr); 156 | } 157 | 158 | // 159 | // filesystemfat_open 160 | // 161 | Error filesystemfat_open (FilesystemParams *fs_params, 162 | BlockDeviceParams *bd_params, const char *name, int flags, 163 | OpenFileInfo *ofi) 164 | { 165 | (void)bd_params; 166 | uint8_t fat_flags = 0; 167 | FilesystemFat *self = (FilesystemFat *)fs_params->context; 168 | 169 | if ((flags & O_ACCMODE) == O_RDONLY) fat_flags |= FA_READ; 170 | else if ((flags & O_ACCMODE) == O_WRONLY) fat_flags |= FA_WRITE; 171 | else fat_flags |= (FA_WRITE | FA_READ); 172 | if (flags & O_CREAT) fat_flags |= FA_OPEN_ALWAYS; 173 | //if (flags & O_EXCL) fat_flags |= LFS_O_EXCL; 174 | if (flags & O_TRUNC) fat_flags |= FA_CREATE_ALWAYS; 175 | if (flags & O_APPEND) fat_flags |= FA_OPEN_APPEND; 176 | 177 | log_debug ("FAT fs rq open %s flg=%04X fat_flg=%02X", name, 178 | flags, fat_flags); 179 | 180 | FIL *fil = malloc (sizeof (FIL)); 181 | char *temp = filesystemfat_prepend_drive_num (self, name); 182 | FRESULT faterr = f_open (fil, temp, fat_flags); 183 | free (temp); 184 | if (faterr == 0) 185 | { 186 | ofi->opaque1 = fil; 187 | } 188 | else 189 | { 190 | free (fil); 191 | } 192 | return filesystemfat_convert_error (faterr); 193 | } 194 | 195 | // 196 | // filesystemfat_close 197 | // 198 | Error filesystemfat_close (FilesystemParams *fs_params, 199 | BlockDeviceParams *bd_params, OpenFileInfo *ofi) 200 | { 201 | (void)bd_params; (void)fs_params; 202 | //FilesystemFat *self = (FilesystemFat *)fs_params->context; 203 | log_debug ("FAT fs rq close %s", ofi->path); 204 | FIL *fil = ofi->opaque1; 205 | int faterr = f_close (fil); 206 | free (fil); 207 | return filesystemfat_convert_error (faterr); 208 | } 209 | 210 | // 211 | // filesystemfat_read 212 | // 213 | Error filesystemfat_read (FilesystemParams *fs_params, 214 | BlockDeviceParams *bd_params, OpenFileInfo *ofi, 215 | uint8_t *buffer, uint32_t len, uint32_t *read) 216 | { 217 | (void)bd_params; (void)fs_params; 218 | //FilesystemFat *self = (FilesystemFat *)fs_params->context; 219 | log_debug ("FAT fs rq rd %s len=%d", 220 | ofi->path, (int)len); 221 | FIL *fil = ofi->opaque1; 222 | UINT n = 0; 223 | int faterr = f_read (fil, buffer, len, &n); 224 | if (faterr) 225 | { 226 | *read = 0; 227 | return filesystemfat_convert_error (faterr); 228 | } 229 | else 230 | { 231 | *read = n; 232 | return 0; 233 | } 234 | } 235 | 236 | // 237 | // filesystemfat_write 238 | // 239 | Error filesystemfat_write (FilesystemParams *fs_params, 240 | BlockDeviceParams *bd_params, OpenFileInfo *ofi, 241 | const uint8_t *buffer, uint32_t len, uint32_t *written) 242 | { 243 | (void)bd_params; (void)fs_params; 244 | //FilesystemFat *self = (FilesystemFat *)fs_params->context; 245 | log_debug ("LFS fs rq wrt %s len=%d", 246 | ofi->path, (int)len); 247 | FIL *fil = ofi->opaque1; 248 | UINT n = 0; 249 | int faterr = f_write (fil, buffer, len, &n); 250 | if (faterr) 251 | { 252 | *written = 0; 253 | return filesystemfat_convert_error (faterr); 254 | } 255 | else 256 | { 257 | *written = n; 258 | return 0; 259 | } 260 | } 261 | 262 | 263 | // 264 | // filesystemfat_closedir 265 | // 266 | Error filesystemfat_closedir (struct _FilesystemParams *fs_params, 267 | BlockDeviceParams *bd_params, 268 | my_DIR *dir) 269 | { 270 | (void)bd_params; (void)fs_params; 271 | log_debug ("VAT fs rq close dir"); 272 | Error ret = 0; 273 | //FilesystemFat *self = (FilesystemFat *)fs_params->context; 274 | DIR *fdir = dir->opaque1; 275 | int faterr = f_closedir (fdir); 276 | if (faterr == 0) 277 | { 278 | free (fdir); 279 | free (dir); 280 | } 281 | else 282 | { 283 | return filesystemfat_convert_error (faterr); 284 | } 285 | return ret; 286 | } 287 | 288 | // 289 | // filesystemfat_opendir 290 | // 291 | my_DIR *filesystemfat_opendir (FilesystemParams *fs_params, 292 | BlockDeviceParams *bd_params, const char *path) 293 | { 294 | log_debug ("FAT fs rq open dir %s", path); 295 | (void)bd_params; 296 | errno = 0; 297 | FilesystemFat *self = (FilesystemFat *)fs_params->context; 298 | my_DIR *ret = NULL; 299 | DIR *dir = malloc (sizeof (DIR)); 300 | char *temp = filesystemfat_prepend_drive_num (self, path); 301 | int faterr = f_opendir (dir, temp); 302 | free (temp); 303 | if (faterr == 0) 304 | { 305 | ret = malloc (sizeof (my_DIR)); 306 | if (ret) 307 | { 308 | memset (&ret->current_de, 0, sizeof (struct my_dirent)); 309 | ret->opaque1 = dir; 310 | errno = 0; 311 | } 312 | else 313 | errno = ERROR_NOMEM; 314 | } 315 | else 316 | { 317 | free (dir); 318 | errno = filesystemfat_convert_error (faterr); 319 | } 320 | return ret; 321 | } 322 | 323 | // 324 | // filesystemfat_readdir 325 | // 326 | struct my_dirent *filesystemfat_readdir 327 | (struct _FilesystemParams *fs_params, BlockDeviceParams *bd_params, 328 | struct _my_DIR *dir) 329 | { 330 | (void)bd_params; (void)fs_params; 331 | log_debug ("FAT fs rq read dir"); 332 | struct my_dirent *ret = NULL; 333 | //FilesystemFat *self = (FilesystemFat *)fs_params->context; 334 | DIR *fdir = dir->opaque1; 335 | FILINFO info; 336 | int faterr = f_readdir (fdir, &info); 337 | if (info.fname[0] == 0) // End of dir 338 | { 339 | errno = 0; 340 | ret = NULL; 341 | } 342 | else if (faterr == 0) // More files to come 343 | { 344 | strncpy (dir->current_de.d_name, info.fname, 345 | sizeof (dir->current_de.d_name) - 1); 346 | if (info.fattrib & AM_DIR) 347 | dir->current_de.d_type = DT_DIR_; 348 | else 349 | dir->current_de.d_type = DT_REG_; 350 | ret = &dir->current_de; 351 | } 352 | else // Error 353 | { 354 | errno = faterr; 355 | ret = NULL; 356 | } 357 | return ret; 358 | } 359 | 360 | // 361 | // filesystemfat_statfs 362 | // 363 | Error filesystemfat_statfs (struct _FilesystemParams *fs_params, 364 | BlockDeviceParams *bd_params, struct _my_statfs *statfs) 365 | { 366 | FATFS *fatfs; 367 | FilesystemFat *self = (FilesystemFat *)fs_params->context; 368 | uint32_t nclusters; 369 | char *temp = filesystemfat_prepend_drive_num (self, ""); 370 | int faterr = f_getfree (temp, &nclusters, &fatfs); 371 | free (temp); 372 | int free_sectors = nclusters * fatfs->csize; 373 | 374 | uint32_t free_blocks; 375 | if (bd_params->block_size >= FF_MIN_SS) 376 | free_blocks = free_sectors * bd_params->block_size / FF_MIN_SS; 377 | else 378 | free_blocks = free_sectors * FF_MIN_SS / bd_params->block_size; 379 | 380 | if (faterr == 0) 381 | { 382 | strcpy (statfs->fs_desc, fs_params->desc); 383 | strcpy (statfs->bd_desc, bd_params->desc); 384 | statfs->block_size = bd_params->block_size; 385 | statfs->total_blocks = bd_params->block_count; 386 | statfs->free_blocks = free_blocks; 387 | return 0; 388 | } 389 | else 390 | return filesystemfat_convert_error (faterr); 391 | } 392 | 393 | // 394 | // filesystemfat_stat 395 | // 396 | Error filesystemfat_stat (struct _FilesystemParams *fs_params, 397 | BlockDeviceParams *bd_params, const char *path, struct stat *sb) 398 | { 399 | uint32_t ret = (uint32_t)-1; 400 | errno = 0; 401 | FilesystemFat *self = (FilesystemFat *)fs_params->context; 402 | log_debug ("FAT fs rq stat %s", path); 403 | FILINFO info; 404 | char *temp = filesystemfat_prepend_drive_num (self, path); 405 | int faterr = f_stat (path, &info); 406 | free (temp); 407 | if (faterr) 408 | errno = filesystemfat_convert_error (faterr); 409 | else 410 | { 411 | sb->st_size = info.fsize; 412 | if (info.fattrib & AM_DIR) 413 | sb->st_mode = S_IFDIR | 0x0777; 414 | else 415 | sb->st_mode = S_IFREG | 0x0777; 416 | sb->st_blksize = bd_params->block_size; 417 | sb->st_blocks = bd_params->block_count; 418 | ret = 0; 419 | } 420 | return ret; 421 | } 422 | 423 | // 424 | // filesystemfat_delete 425 | // 426 | Error filesystemfat_delete (FilesystemParams *fs_params, 427 | BlockDeviceParams *bd_params, const char *path) 428 | { 429 | (void)bd_params; 430 | FilesystemFat *self = (FilesystemFat *)fs_params->context; 431 | log_debug ("FAT fs rq del %s", path); 432 | char *temp = filesystemfat_prepend_drive_num (self, path); 433 | int faterr = f_unlink (temp); 434 | free (temp); 435 | return filesystemfat_convert_error (faterr); 436 | } 437 | 438 | // 439 | // filesystemfat_lseek 440 | // 441 | uint32_t filesystemfat_lseek (struct _FilesystemParams *fs_params, 442 | BlockDeviceParams *bd_params, OpenFileInfo *ofi, 443 | uint32_t offset, int whence) 444 | { 445 | (void)bd_params; (void)fs_params; 446 | //FilesystemFat *self = (FilesystemFat *)fs_params->context; 447 | log_debug ("FAT fs rq lseek %s off=%d whence=%d", 448 | ofi->path, (int)offset, whence); 449 | FIL *fil = ofi->opaque1; 450 | uint32_t newoffset; 451 | if (whence == SEEK_END) 452 | newoffset = f_size (fil) - offset; 453 | else if (whence == SEEK_CUR) 454 | newoffset = offset + f_tell (fil); 455 | else 456 | newoffset = offset; 457 | 458 | int faterr = f_lseek (fil, newoffset); 459 | if (faterr) 460 | { 461 | errno = filesystemfat_convert_error (faterr); 462 | return -1; 463 | } 464 | else 465 | { 466 | return (uint32_t)f_tell (fil); 467 | } 468 | } 469 | 470 | // 471 | // filesystemfat_rename 472 | // 473 | Error filesystemfat_rename (struct _FilesystemParams *fs_params, 474 | BlockDeviceParams *bd_params, const char *source, 475 | const char *target) 476 | { 477 | (void) bd_params; 478 | FilesystemFat *self = (FilesystemFat *)fs_params->context; 479 | 480 | char *tempsource = filesystemfat_prepend_drive_num (self, source); 481 | char *temptarget = filesystemfat_prepend_drive_num (self, target); 482 | 483 | int faterr = f_rename (tempsource, temptarget); 484 | 485 | free (temptarget); 486 | free (tempsource); 487 | 488 | return filesystemfat_convert_error (faterr); 489 | } 490 | 491 | 492 | Error filesystemfat_ftruncate (struct _FilesystemParams *fs_params, 493 | BlockDeviceParams *bd_params, struct _OpenFileInfo *ofi, 494 | uint32_t length) 495 | { 496 | (void)bd_params; (void)fs_params; 497 | FIL *fil = ofi->opaque1; 498 | f_lseek (fil, length); 499 | int faterr = f_truncate (fil); 500 | return filesystemfat_convert_error (faterr); 501 | } 502 | 503 | 504 | 505 | // 506 | // filesystemfat_get_params 507 | // 508 | void filesystemfat_get_params (FilesystemFat *self, 509 | FilesystemParams *fs_params) 510 | { 511 | // TODO 512 | memset (fs_params, 0, sizeof (FilesystemParams)); 513 | strcpy (fs_params->desc, "FAT"); 514 | fs_params->context = self; 515 | fs_params->mount = filesystemfat_mount; 516 | fs_params->unmount = filesystemfat_unmount; 517 | fs_params->open = filesystemfat_open; 518 | fs_params->close = filesystemfat_close; 519 | fs_params->read = filesystemfat_read; 520 | fs_params->closedir = filesystemfat_closedir; 521 | fs_params->opendir = filesystemfat_opendir; 522 | fs_params->readdir = filesystemfat_readdir; 523 | fs_params->statfs = filesystemfat_statfs; 524 | fs_params->stat = filesystemfat_stat; 525 | fs_params->del = filesystemfat_delete; 526 | fs_params->lseek = filesystemfat_lseek; 527 | fs_params->write = filesystemfat_write; 528 | fs_params->rename = filesystemfat_rename; 529 | fs_params->ftruncate = filesystemfat_ftruncate; 530 | fs_params->format = filesystemfat_format; 531 | } 532 | 533 | 534 | 535 | -------------------------------------------------------------------------------- /fs/src/lfs_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lfs util functions 3 | * 4 | * Copyright (c) 2017, Arm Limited. All rights reserved. 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | */ 7 | #include "fs/lfs_util.h" 8 | 9 | // Only compile if user does not provide custom config 10 | #ifndef LFS_CONFIG 11 | 12 | 13 | // Software CRC implementation with small lookup table 14 | uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) { 15 | static const uint32_t rtable[16] = { 16 | 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 17 | 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 18 | 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 19 | 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c, 20 | }; 21 | 22 | const uint8_t *data = buffer; 23 | 24 | for (size_t i = 0; i < size; i++) { 25 | crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf]; 26 | crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf]; 27 | } 28 | 29 | return crc; 30 | } 31 | 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /klib/README.md: -------------------------------------------------------------------------------- 1 | # klib 2 | 3 | Various utility functions, mostly for string and list handling. 4 | 5 | -------------------------------------------------------------------------------- /klib/include/klib/defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifndef TRUE 6 | #define TRUE 1 7 | #endif 8 | #ifndef FALSE 9 | #define FALSE 0 10 | #endif 11 | 12 | #ifdef __cplusplus 13 | #define BEGIN_DECLS extern "C" { 14 | #define END_DECLS } 15 | #else 16 | #define BEGIN_DECLS 17 | #define END_DECLS 18 | #endif 19 | 20 | #ifndef BOOL 21 | typedef uint8_t BOOL; 22 | #endif 23 | 24 | #ifndef UTF8 25 | typedef uint8_t UTF8; 26 | #endif 27 | 28 | #ifndef UTF32 29 | typedef int32_t UTF32; 30 | #endif 31 | 32 | #ifndef BYTE 33 | typedef uint8_t BYTE; 34 | #endif 35 | 36 | 37 | -------------------------------------------------------------------------------- /klib/include/klib/list.h: -------------------------------------------------------------------------------- 1 | /*============================================================================ 2 | 3 | klib 4 | 5 | list.h 6 | 7 | Copyright (c)2000-2020 Kevin Boone, GPL v3.0 8 | 9 | ============================================================================*/ 10 | 11 | #pragma once 12 | 13 | #include "defs.h" 14 | 15 | struct _List; 16 | typedef struct _List List; 17 | 18 | // The comparison function should return -1, 0, +1, like strcmp. In practice 19 | // however, the functions that use this only care whether too things 20 | // are equal -- ordering is not important. The i1,i2 arguments are 21 | // pointers to the actual objects in the list. user_data is not used 22 | // at present 23 | typedef int (*ListCompareFn) (const void *i1, const void *i2, 24 | void *user_data); 25 | 26 | // A comparison function for list_sort. Rather confusingly, it looks exactly 27 | // like ListCompareFn, but the argument type is different. Here the i1,i2 28 | // are the addresses of pointers to objects in the list, not pointers. 29 | // So they are pointers to pointers. Sorry, but this is the way the 30 | // underlying qsort implementation works. For an example of coding a 31 | // sort function, see string_alpha_sort_fn. The user_data argument is 32 | // the value passed to the list_sort function itself, and is relevant 33 | // only to the caller 34 | typedef int (*ListSortFn) (const void *i1, const void *i2, 35 | void *user_data); 36 | 37 | typedef void* (*ListCopyFn) (const void *orig); 38 | typedef void (*ListItemFreeFn) (void *); 39 | 40 | List *list_create (ListItemFreeFn free_fn); 41 | void list_destroy (List *); 42 | void list_append (List *self, void *item); 43 | void list_prepend (List *self, void *item); 44 | void *list_get (const List *self, int index); 45 | void list_dump (List *self); 46 | int list_length (const List *self); 47 | BOOL list_contains (List *self, const void *item, ListCompareFn fn); 48 | void list_remove (List *self, const void *item, ListCompareFn fn); 49 | List *list_clone (List *self, ListCopyFn copyFn); 50 | List *list_create_strings (void); 51 | void list_remove_object (List *self, const void *item); 52 | void list_sort (List *self, ListSortFn fn, void *user_data); 53 | 54 | -------------------------------------------------------------------------------- /klib/include/klib/string.h: -------------------------------------------------------------------------------- 1 | /*============================================================================ 2 | 3 | klib 4 | 5 | string.h 6 | 7 | Copyright (c)2017 Kevin Boone, GPL v3.0 8 | 9 | ============================================================================*/ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include "defs.h" 15 | #include "list.h" 16 | 17 | struct _String; 18 | typedef struct _String String; 19 | 20 | typedef void (*StringTokGlobber) (String *token, List *list); 21 | 22 | extern StringTokGlobber string_tok_globber; 23 | 24 | BEGIN_DECLS 25 | 26 | String *string_create_empty (void); 27 | String *string_create (const char *s); 28 | String *string_clone (const String *self); 29 | int string_find (const String *self, const char *search); 30 | int string_find_last (const String *self, const char *search); 31 | void string_destroy (String *self); 32 | const char *string_cstr (const String *self); 33 | const char *string_cstr_safe (const String *self); 34 | void string_append_printf (String *self, const char *fmt,...); 35 | void string_append (String *self, const char *s); 36 | void string_append_c (String *self, const uint32_t c); 37 | void string_prepend (String *self, const char *s); 38 | int string_length (const String *self); 39 | String *string_substitute_all (const String *self, 40 | const char *search, const char *replace); 41 | void string_delete (String *self, const int pos, 42 | const int len); 43 | void string_insert (String *self, const int pos, 44 | const char *replace); 45 | String *string_encode_url (const char *s); 46 | void string_append_byte (String *self, const BYTE byte); 47 | void string_trim_left (String *self); 48 | void string_trim_right (String *self); 49 | BOOL string_ends_with (const String *self, const char *test); 50 | int string_alpha_sort_fn (const void *p1, const void*p2, 51 | void *user_data); 52 | List *string_split (const String *self, const char *delim); 53 | List *string_tokenize (const String *self); 54 | void string_delete_last (String *self); 55 | void string_insert_c_at (String *self, int pos, char c); 56 | void string_delete_c_at (String *self, int pos); 57 | // to_upper() works on ASCII only 58 | void string_to_upper (char *s); 59 | 60 | END_DECLS 61 | 62 | -------------------------------------------------------------------------------- /klib/src/list.c: -------------------------------------------------------------------------------- 1 | /*============================================================================ 2 | 3 | klib 4 | 5 | list.c 6 | 7 | Copyright (c)2000-2021 Kevin Boone, GPL v3.0 8 | 9 | Methods for maintaining a single-linked list. 10 | 11 | ============================================================================*/ 12 | 13 | #define _GNU_SOURCE 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "../include/klib/list.h" 19 | #include "../include/klib/string.h" 20 | 21 | #define LOG_IN 22 | #define LOG_OUT 23 | 24 | typedef struct _ListItem 25 | { 26 | struct _ListItem *next; 27 | void *data; 28 | } ListItem; 29 | 30 | struct _List 31 | { 32 | ListItemFreeFn free_fn; 33 | ListItem *head; 34 | }; 35 | 36 | /*========================================================================== 37 | list_create 38 | *==========================================================================*/ 39 | List *list_create (ListItemFreeFn free_fn) 40 | { 41 | LOG_IN 42 | List *list = malloc (sizeof (List)); 43 | memset (list, 0, sizeof (List)); 44 | list->free_fn = free_fn; 45 | LOG_OUT 46 | return list; 47 | } 48 | 49 | /*========================================================================== 50 | list_create_strings 51 | 52 | This is a helper function for creating a list of C strings -- not 53 | string objects 54 | *==========================================================================*/ 55 | List *list_create_strings (void) 56 | { 57 | return list_create (free); 58 | } 59 | 60 | /*========================================================================== 61 | list_destroy 62 | *==========================================================================*/ 63 | void list_destroy (List *self) 64 | { 65 | LOG_IN 66 | if (self) 67 | { 68 | ListItem *l = self->head; 69 | while (l) 70 | { 71 | if (self->free_fn) 72 | self->free_fn (l->data); 73 | ListItem *temp = l; 74 | l = l->next; 75 | free (temp); 76 | } 77 | 78 | free (self); 79 | } 80 | LOG_OUT 81 | } 82 | 83 | 84 | /*========================================================================== 85 | list_prepend 86 | Note that the caller must not modify or free the item added to the list. It 87 | will remain on the list until free'd by the list itself, by calling 88 | the supplied free function 89 | *==========================================================================*/ 90 | void list_prepend (List *self, void *item) 91 | { 92 | LOG_IN 93 | ListItem *i = malloc (sizeof (ListItem)); 94 | i->data = item; 95 | i->next = NULL; 96 | 97 | if (self->head) 98 | { 99 | i->next = self->head; 100 | self->head = i; 101 | } 102 | else 103 | { 104 | self->head = i; 105 | } 106 | LOG_OUT 107 | } 108 | 109 | 110 | /*========================================================================== 111 | list_append 112 | Note that the caller must not modify or free the item added to the list. 113 | It will remain on the list until free'd by the list itself, by calling 114 | the supplied free function 115 | *==========================================================================*/ 116 | void list_append (List *self, void *item) 117 | { 118 | LOG_IN 119 | ListItem *i = malloc (sizeof (ListItem)); 120 | i->data = item; 121 | i->next = NULL; 122 | 123 | if (self->head) 124 | { 125 | ListItem *l = self->head; 126 | while (l->next) 127 | l = l->next; 128 | l->next = i; 129 | } 130 | else 131 | { 132 | self->head = i; 133 | } 134 | LOG_OUT 135 | } 136 | 137 | 138 | /*========================================================================== 139 | list_length 140 | *==========================================================================*/ 141 | int list_length (const List *self) 142 | { 143 | LOG_IN 144 | ListItem *l = self->head; 145 | 146 | int i = 0; 147 | while (l != NULL) 148 | { 149 | l = l->next; 150 | i++; 151 | } 152 | 153 | LOG_OUT 154 | return i; 155 | } 156 | 157 | /*========================================================================== 158 | list_get 159 | *==========================================================================*/ 160 | void *list_get (const List *self, int index) 161 | { 162 | LOG_IN 163 | ListItem *l = self->head; 164 | int i = 0; 165 | while (l != NULL && i != index) 166 | { 167 | l = l->next; 168 | i++; 169 | } 170 | LOG_OUT 171 | return l->data; 172 | } 173 | 174 | 175 | /*========================================================================== 176 | list_dump 177 | For debugging purposes -- will only work at all if the list contains 178 | C strings 179 | *==========================================================================*/ 180 | void list_dump (List *self) 181 | { 182 | int i, l = list_length (self); 183 | for (i = 0; i < l; i++) 184 | { 185 | const char *s = list_get (self, i); 186 | printf ("%s\n", s); 187 | } 188 | } 189 | 190 | 191 | /*========================================================================== 192 | list_contains 193 | *==========================================================================*/ 194 | BOOL list_contains (List *self, const void *item, ListCompareFn fn) 195 | { 196 | LOG_IN 197 | ListItem *l = self->head; 198 | BOOL found = FALSE; 199 | while (l != NULL && !found) 200 | { 201 | if (fn (l->data, item, NULL) == 0) found = TRUE; 202 | l = l->next; 203 | } 204 | LOG_OUT 205 | return found; 206 | } 207 | 208 | 209 | 210 | /*========================================================================== 211 | list_remove_object 212 | Remove the specific item from the list, if it is present. The object's 213 | remove function will be called. This method can't be used to remove an 214 | object by value -- that is, you can't pass "dog" to the method to remove 215 | all strings whose value is "dog". Use list_remove() for that. 216 | *==========================================================================*/ 217 | void list_remove_object (List *self, const void *item) 218 | { 219 | LOG_IN 220 | ListItem *l = self->head; 221 | ListItem *last_good = NULL; 222 | while (l != NULL) 223 | { 224 | if (l->data == item) 225 | { 226 | if (l == self->head) 227 | { 228 | self->head = l->next; // l-> next might be null 229 | } 230 | else 231 | { 232 | if (last_good) last_good->next = l->next; 233 | } 234 | self->free_fn (l->data); 235 | ListItem *temp = l->next; 236 | free (l); 237 | l = temp; 238 | } 239 | else 240 | { 241 | last_good = l; 242 | l = l->next; 243 | } 244 | } 245 | LOG_OUT 246 | } 247 | 248 | 249 | /*========================================================================== 250 | list_remove 251 | Remove all items from the last that are a match for 'item', as 252 | determined by a comparison function. 253 | 254 | IMPORTANT -- The "item" argument cannot be a direct reference to an 255 | item already in the list. If that item is removed from the list its 256 | memory will be freed. The "item" argument will thus be an invalid 257 | memory reference, and the program will crash when it is next used. 258 | It is necessary to provide a comparison function, and items will be 259 | removed (and freed) that satisfy the comparison function. 260 | 261 | To remove one specific, known, item from the list, uselist_remove_object() 262 | *==========================================================================*/ 263 | void list_remove (List *self, const void *item, ListCompareFn fn) 264 | { 265 | LOG_IN 266 | ListItem *l = self->head; 267 | ListItem *last_good = NULL; 268 | while (l != NULL) 269 | { 270 | if (fn (l->data, item, NULL) == 0) 271 | { 272 | if (l == self->head) 273 | { 274 | self->head = l->next; // l-> next might be null 275 | } 276 | else 277 | { 278 | if (last_good) last_good->next = l->next; 279 | } 280 | self->free_fn (l->data); 281 | ListItem *temp = l->next; 282 | free (l); 283 | l = temp; 284 | } 285 | else 286 | { 287 | last_good = l; 288 | l = l->next; 289 | } 290 | } 291 | LOG_OUT 292 | } 293 | 294 | /*========================================================================== 295 | list_clone 296 | *==========================================================================*/ 297 | List *list_clone (List *self, ListCopyFn copyFn) 298 | { 299 | LOG_IN 300 | ListItemFreeFn free_fn = self->free_fn; 301 | List *new = list_create (free_fn); 302 | 303 | ListItem *l = self->head; 304 | while (l != NULL) 305 | { 306 | void *data = copyFn (l->data); 307 | list_append (new, data); 308 | l = l->next; 309 | } 310 | 311 | LOG_OUT 312 | return new; 313 | } 314 | 315 | 316 | /*========================================================================== 317 | list_sort 318 | 319 | Sort the list according to the supplied list sort function. This should 320 | return -1, 0, or 1 in the usual way. The arguments to this function are 321 | pointers to pointers to objects supplied by list_append, etc., not direct 322 | pointers. 323 | 324 | The implementation is rather ugly -- we copy the list data pointers to a 325 | flat array, then use qsort() on the array. Then we copy the list data 326 | pointers back into the list. So the original chain of links remains 327 | unchanged, but each link is now associated with a new data item. 328 | *==========================================================================*/ 329 | void list_sort (List *self, ListSortFn fn, void *user_data) 330 | { 331 | LOG_IN 332 | 333 | int length = list_length (self); 334 | 335 | void **temp = malloc (length * sizeof (void *)); 336 | ListItem *l = self->head; 337 | int i = 0; 338 | while (l != NULL) 339 | { 340 | temp[i] = l->data; 341 | l = l->next; 342 | i++; 343 | } 344 | 345 | // Sort temp 346 | 347 | qsort_r (temp, length, sizeof (void *), fn, user_data); 348 | 349 | // Copy the sorted data back 350 | 351 | l = self->head; 352 | i = 0; 353 | while (l != NULL) 354 | { 355 | l->data = temp[i]; 356 | l = l->next; 357 | i++; 358 | } 359 | 360 | free (temp); 361 | 362 | LOG_OUT 363 | } 364 | 365 | -------------------------------------------------------------------------------- /log/include/log/log.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | log/log.h 6 | 7 | Logging functions 8 | 9 | Copyright (c)2001 Kevin Boone, GPL v3.0 10 | 11 | =========================================================================*/ 12 | #pragma once 13 | 14 | #include "console/console.h" 15 | 16 | #define LOGLEVEL_ERROR 0 17 | #define LOGLEVEL_WARN 1 18 | #define LOGLEVEL_INFO 2 19 | #define LOGLEVEL_DEBUG 3 20 | #define LOGLEVEL_TRACE 4 21 | 22 | extern void log_set_console (ConsoleParams *console_params); 23 | extern void log_set_level (int level); 24 | 25 | extern void log_trace (const char *fmt,...); 26 | extern void log_debug (const char *fmt,...); 27 | extern void log_info (const char *fmt,...); 28 | extern void log_warn (const char *fmt,...); 29 | extern void log_error (const char *fmt,...); 30 | 31 | -------------------------------------------------------------------------------- /log/src/log.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | log/log.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "log/log.h" 19 | #include "console/console.h" 20 | 21 | static ConsoleParams *log_console; 22 | static int log_level = LOGLEVEL_WARN; 23 | 24 | void log_set_console (ConsoleParams *console_params) 25 | { 26 | log_console = console_params; 27 | } 28 | 29 | void log_set_level (int level) 30 | { 31 | if (level >= 0 && level <= LOGLEVEL_TRACE) 32 | log_level = level; 33 | } 34 | 35 | // 36 | // log_error 37 | // 38 | void log_error (const char *fmt,...) 39 | { 40 | va_list ap; 41 | va_start (ap, fmt); 42 | log_console->console_out_string (log_console->context, "ERROR: "); 43 | log_console->console_out_string_v (log_console->context, fmt, ap); 44 | log_console->console_out_endl (log_console->context); 45 | va_end (ap); 46 | } 47 | 48 | void log_debug (const char *fmt,...) 49 | { 50 | if (log_level < LOGLEVEL_DEBUG) return; 51 | va_list ap; 52 | va_start (ap, fmt); 53 | log_console->console_out_string (log_console->context, "DEBUG: "); 54 | log_console->console_out_string_v (log_console->context, fmt, ap); 55 | log_console->console_out_endl (log_console->context); 56 | va_end (ap); 57 | } 58 | 59 | void log_trace (const char *fmt,...) 60 | { 61 | if (log_level < LOGLEVEL_TRACE) return; 62 | va_list ap; 63 | va_start (ap, fmt); 64 | log_console->console_out_string (log_console->context, "TRACE: "); 65 | log_console->console_out_string_v (log_console->context, fmt, ap); 66 | log_console->console_out_endl (log_console->context); 67 | va_end (ap); 68 | } 69 | 70 | void log_warn (const char *fmt,...) 71 | { 72 | if (log_level < LOGLEVEL_WARN) return; 73 | va_list ap; 74 | va_start (ap, fmt); 75 | log_console->console_out_string (log_console->context, "WARN: "); 76 | log_console->console_out_string_v (log_console->context, fmt, ap); 77 | log_console->console_out_endl (log_console->context); 78 | va_end (ap); 79 | } 80 | 81 | void log_info (const char *fmt,...) 82 | { 83 | if (log_level < LOGLEVEL_INFO) return; 84 | va_list ap; 85 | va_start (ap, fmt); 86 | log_console->console_out_string (log_console->context, "INFO: "); 87 | log_console->console_out_string_v (log_console->context, fmt, ap); 88 | log_console->console_out_endl (log_console->context); 89 | va_end (ap); 90 | } 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /main/README.md: -------------------------------------------------------------------------------- 1 | # main 2 | 3 | Implementation of the main program entry point. This creates the filesystems 4 | and starts the shell. 5 | -------------------------------------------------------------------------------- /main/main.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | CPICOM 4 | 5 | main/main.c 6 | 7 | This is where it all starts. 8 | 9 | Copyright (c)2001 Kevin Boone, GPL v3.0 10 | 11 | =========================================================================*/ 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "log/log.h" 19 | #include "files/filecontext.h" 20 | #include "pico/stdlib.h" 21 | #if PICO_ON_DEVICE 22 | #include "blockdevice/flashblockdevice.h" 23 | #else 24 | #include "blockdevice/fileblockdevice.h" 25 | #endif 26 | 27 | //#define TEST_FAT 1 28 | 29 | #include "picocpm/picocpm.h" 30 | #include "fs/filesystemlfs.h" 31 | 32 | #ifdef TEST_FAT 33 | #include "fs/filesystemfat.h" 34 | #endif 35 | 36 | #include "error/error.h" 37 | #include "shell/shell.h" 38 | #include "console/console_stdio_vt100.h" 39 | #include "console/console.h" 40 | 41 | #if defined(PICO_W) 42 | #include "pico/stdlib.h" 43 | #include "pico/cyw43_arch.h" 44 | // get this from cpicom.elf.map, search for __flash_binary_end 45 | // then round up to 4K boundary 46 | #define PROGRAM_SIZE 0x78000 47 | #define BLOCKS_PER_DRIVE 189 48 | #else 49 | #define PROGRAM_SIZE 0x50000 50 | #define BLOCKS_PER_DRIVE 180 51 | #endif 52 | 53 | #define FLASH_SIZE 0x200000 54 | 55 | extern uint32_t __flash_binary_end; 56 | 57 | // 58 | // Start here 59 | // 60 | int main() 61 | { 62 | stdio_init_all(); 63 | 64 | #if defined(PICO_W) 65 | if (cyw43_arch_init()) { 66 | printf("Wi-Fi init failed"); 67 | return -1; 68 | } 69 | // seems like this is required here to keep it from crashing 70 | cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); 71 | 72 | /* 73 | int cnt = 1 ; 74 | while(cnt > 0 ) { 75 | cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); 76 | sleep_ms(250); 77 | cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); 78 | sleep_ms(250); 79 | cnt--; 80 | } 81 | */ 82 | #endif 83 | 84 | //log_set_level (LOGLEVEL_TRACE); 85 | log_set_level (LOGLEVEL_WARN); 86 | ConsoleStdioVT100 *csvt100 = consolestdiovt100_create(); 87 | 88 | ConsoleParams console_params; 89 | consolestdiovt100_get_params (csvt100, &console_params); 90 | log_set_console (&console_params); 91 | 92 | FileContext *fc = filecontext_create(); 93 | global_fc = fc; 94 | 95 | BlockDeviceParams bd_params_a; 96 | BlockDeviceParams bd_params_b; 97 | #ifdef TEST_FAT 98 | BlockDeviceParams bd_params_c; 99 | #endif 100 | 101 | #if PICO_ON_DEVICE 102 | if( (FLASH_SIZE - PROGRAM_SIZE)/2/4096 < BLOCKS_PER_DRIVE ) { 103 | printf("ERROR:There is %d bytes of FLASH available\n", (unsigned int)(FLASH_SIZE - PROGRAM_SIZE) ); 104 | printf("That means maximum of %d 4K blocks per drive\n", (unsigned int)(FLASH_SIZE - PROGRAM_SIZE)/2/4096); 105 | } 106 | FlashBlockDevice *fbd_a = flashblockdevice_create (PROGRAM_SIZE, BLOCKS_PER_DRIVE); 107 | Error errA = flashblockdevice_initialize (fbd_a); 108 | flashblockdevice_get_params (fbd_a, &bd_params_a); 109 | FlashBlockDevice *fbd_b = flashblockdevice_create (PROGRAM_SIZE + ((BLOCKS_PER_DRIVE+1) * 4096) , BLOCKS_PER_DRIVE ); 110 | Error errB = flashblockdevice_initialize (fbd_b); 111 | flashblockdevice_get_params (fbd_b, &bd_params_b); 112 | #else 113 | const char *home = getenv ("HOME"); 114 | char path[MAX_FNAME]; 115 | sprintf (path, "%s/drive-a.dsk", home); 116 | FileBlockDevice *fbd_a = fileblockdevice_create (path); 117 | Error errA = fileblockdevice_initialize (fbd_a); 118 | fileblockdevice_get_params (fbd_a, &bd_params_a); 119 | sprintf (path, "%s/drive-b.dsk", home); 120 | FileBlockDevice *fbd_b = fileblockdevice_create (path); 121 | Error errB = fileblockdevice_initialize (fbd_b); 122 | fileblockdevice_get_params (fbd_b, &bd_params_b); 123 | #ifdef TEST_FAT 124 | sprintf (path, "%s/drive-c.dsk", home); 125 | FileBlockDevice *fbd_c = fileblockdevice_create (path); 126 | Error errC = fileblockdevice_initialize (fbd_c); 127 | fileblockdevice_get_params (fbd_c, &bd_params_c); 128 | #endif 129 | #endif 130 | 131 | FilesystemLfs *lfsA = filesystemlfs_create (); 132 | if (errA == 0) 133 | { 134 | filesystemlfs_set_block_device (lfsA, &bd_params_a); 135 | FilesystemParams fs_params_a; 136 | filesystemlfs_get_params (lfsA, &fs_params_a); 137 | 138 | Error err = filecontext_mount (fc, 0, &fs_params_a, &bd_params_a); 139 | if (err) 140 | { 141 | log_error ("Mount error %d", err); 142 | log_info ("Formatting drive 0"); 143 | err = filecontext_format (fc, 0); 144 | if (err) 145 | log_error ("Format error %d", err); 146 | } 147 | } 148 | else 149 | log_error ("Can't create block device, error %d", errA); 150 | 151 | FilesystemLfs *lfsB = filesystemlfs_create (); 152 | if (errB == 0) 153 | { 154 | filesystemlfs_set_block_device (lfsB, &bd_params_b); 155 | FilesystemParams fs_params_b; 156 | filesystemlfs_get_params (lfsB, &fs_params_b); 157 | 158 | Error err = filecontext_mount (fc, 1, &fs_params_b, &bd_params_b); 159 | if (err) 160 | { 161 | log_error ("Mount error %d", err); 162 | log_info ("Formatting drive 1"); 163 | err = filecontext_format (fc, 1); 164 | if (err) 165 | log_error ("Format error %d", err); 166 | } 167 | } 168 | else 169 | log_error ("Can't create block device, error %d", errB); 170 | 171 | #ifdef TEST_FAT 172 | FilesystemFat *fatC = filesystemfat_create (0); 173 | if (errC == 0) 174 | { 175 | filesystemfat_set_block_device (fatC, &bd_params_c); 176 | FilesystemParams fs_params_c; 177 | filesystemfat_get_params (fatC, &fs_params_c); 178 | 179 | Error err = filecontext_mount (fc, 2, &fs_params_c, &bd_params_c); 180 | if (err) 181 | { 182 | log_error ("Mount error %d", err); 183 | } 184 | 185 | } 186 | else 187 | log_error ("Can't create block device, error %d", errC); 188 | #endif 189 | 190 | //========= 191 | /* 192 | int f = open ("main.com", O_RDONLY); 193 | if (f < 0) 194 | { 195 | fprintf (stderr, "can't open main.com\n"); 196 | exit (0); 197 | } 198 | 199 | int l = lseek (f, 0, SEEK_END); 200 | lseek (f, 0, SEEK_SET); 201 | 202 | BYTE *buf = malloc (l); 203 | 204 | read (f, buf, l); 205 | 206 | close (f); 207 | 208 | picoz80_copy_to_mem (picoz80, 0x100, buf, l); 209 | free (buf); 210 | 211 | picoz80_run (picoz80); 212 | 213 | */ 214 | //===== 215 | 216 | PicoCPM *picocpm = picocpm_create (); 217 | picocpm_set_console_params (picocpm, &console_params); 218 | consolestdiovt100_set_config (csvt100, picocpm_get_config(picocpm)); 219 | 220 | // Uncomment to have the program go straight to ymodem recv. 221 | //shell_do_line ("yrecv", &console_params, picocpm); 222 | //shell_do_line ("ysend main.com", &console_params, picocpm); 223 | 224 | #if PICO_ON_DEVICE 225 | // On the Pico, don't allow a ctrl+d to halt the board -- just reset 226 | while (TRUE) { 227 | #endif 228 | shell_main (&console_params, picocpm); 229 | #if PICO_ON_DEVICE 230 | 231 | } 232 | #endif 233 | 234 | picocpm_destroy (picocpm); 235 | 236 | filecontext_destroy (fc); 237 | // Must destroyfilecontext first, as it unmounts. But it can't unmount 238 | // if the individual handlers have been destroyed. 239 | if (lfsA) filesystemlfs_destroy (lfsA); 240 | if (lfsB) filesystemlfs_destroy (lfsB); 241 | #ifdef TEST_FAT 242 | if (fatC) filesystemfat_destroy (fatC); 243 | #endif 244 | 245 | #if PICO_ON_DEVICE 246 | flashblockdevice_destroy (fbd_a); 247 | flashblockdevice_destroy (fbd_b); 248 | #else 249 | fileblockdevice_destroy (fbd_a); 250 | fileblockdevice_destroy (fbd_b); 251 | 252 | #ifdef TEST_FAT 253 | fileblockdevice_destroy (fbd_c); 254 | #endif 255 | 256 | #endif 257 | 258 | 259 | #if defined(PICO_W) 260 | cyw43_arch_deinit(); 261 | #endif 262 | 263 | consolestdiovt100_destroy (csvt100); 264 | } 265 | 266 | 267 | -------------------------------------------------------------------------------- /misc/cpm22disk.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinboone/cpicom/a43c2947a2143c7efb004629674b71db18b555ca/misc/cpm22disk.tar -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /picocpm/README.md: -------------------------------------------------------------------------------- 1 | # picocpm 2 | 3 | Wrappers for the spawling complexity of the Z80 and CP/M emulation in 4 | the cpm/ directory. Ideally, the shell, etc., should interact only 5 | with these functions, not with the emulator directly. 6 | 7 | -------------------------------------------------------------------------------- /picocpm/include/picocpm/config.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | picocpm/config 6 | 7 | Storage and manipulate of the system configuration (not much can be 8 | configured yet). 9 | 10 | Copyright (c)2001 Kevin Boone, GPL v3.0 11 | 12 | =========================================================================*/ 13 | #pragma once 14 | 15 | struct Config; 16 | typedef struct _Config 17 | { 18 | BOOL page; 19 | BOOL flow_control; 20 | } Config; 21 | 22 | Config *config_create (void); 23 | void config_destroy (Config *self); 24 | 25 | -------------------------------------------------------------------------------- /picocpm/include/picocpm/picocpm.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | pciocpm/picocpm.h 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #pragma once 11 | 12 | #include "error/error.h" 13 | #include "console/console.h" 14 | #include "picocpm/config.h" 15 | 16 | struct PicoCPM; 17 | struct _ConsoleParams; 18 | typedef struct _PicoCPM PicoCPM; 19 | 20 | typedef void (*InterruptHandler) (void *interrupt_context); 21 | 22 | extern PicoCPM *picocpm_create (void); 23 | extern void picocpm_destroy (PicoCPM *self); 24 | extern Error picocpm_run (PicoCPM *self, const char *exe_path, 25 | int argc, char **argv); 26 | extern void picocpm_set_console_params (PicoCPM *self, 27 | struct _ConsoleParams *console_params); 28 | extern Config *picocpm_get_config (PicoCPM *self); 29 | 30 | -------------------------------------------------------------------------------- /picocpm/src/config.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | picocpm/config.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "error/error.h" 16 | #include "log/log.h" 17 | #include "picocpm/config.h" 18 | 19 | // 20 | // config_create 21 | // 22 | Config *config_create (void) 23 | { 24 | log_debug ("Crting shl cfg"); 25 | Config *self = malloc (sizeof (Config)); 26 | self->page = FALSE; 27 | self->flow_control = TRUE; 28 | return self; 29 | } 30 | 31 | // 32 | // config_destroy 33 | // 34 | void config_destroy (Config *self) 35 | { 36 | log_debug ("Destry shl cfg"); 37 | if (self) 38 | { 39 | free (self); 40 | } 41 | } 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /picocpm/src/picocpm.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | picocpm 6 | 7 | This module provides the interface between the command-line processor, 8 | filesystem, and the sprawling complexity that is the CPM emulator. 9 | 10 | =========================================================================*/ 11 | #include 12 | #include 13 | #include "picocpm/picocpm.h" 14 | #include "error/error.h" 15 | #include "log/log.h" 16 | #include "console/console.h" 17 | #include "cpm/limits.h" 18 | #include "shell/shell.h" 19 | #include "cpm/defs.h" 20 | 21 | // From com/intf.c 22 | extern int cpm_runprog (ConsoleParams *cp, const char *cmdline, 23 | z80info **z80_info); 24 | 25 | struct _PicoCPM 26 | { 27 | ConsoleParams *console_params; 28 | z80info *z80_info; 29 | Config *config; 30 | }; 31 | 32 | // 33 | // picocpm_create 34 | // 35 | PicoCPM *picocpm_create (void) 36 | { 37 | PicoCPM *self = malloc (sizeof (PicoCPM)); 38 | self->config = config_create(); 39 | return self; 40 | } 41 | 42 | // 43 | // picocpm_set_console_params 44 | // 45 | void picocpm_set_console_params (PicoCPM *self, 46 | ConsoleParams *console_params) 47 | { 48 | self->console_params = console_params; 49 | } 50 | 51 | // 52 | // picocpm_destroy 53 | // 54 | void picocpm_destroy (PicoCPM *self) 55 | { 56 | if (self) 57 | { 58 | config_destroy (self->config); 59 | free (self); 60 | } 61 | } 62 | 63 | // 64 | // picocpm_interrupt_handler 65 | // 66 | static void picocpm_interrupt_handler (void *context) 67 | { 68 | PicoCPM *self = (PicoCPM *) context; 69 | z80info *z80_info = self->z80_info; 70 | // The PicoCPM object is long-lived, and might get interrupts when 71 | // no CP/M program is running. We should ignore these -- presumably 72 | // something else will be handling them. 73 | if (z80_info) 74 | { 75 | z80_info->finished = TRUE; 76 | // We could launch a debugger or something here 77 | } 78 | } 79 | 80 | // 81 | // picocpm_run 82 | // 83 | Error picocpm_run (PicoCPM *self, const char *exe_path, int argc, char **argv) 84 | { 85 | Error ret = 0; 86 | char cmdline [CPM_MAX_CMDLINE + 1]; 87 | strcpy (cmdline, exe_path); 88 | char *dotpos = strchr (cmdline, '.'); 89 | // We might be passed a full filenane like "prog.com", but the BDOS 90 | // impleemntation will choke on the .com extension. Sigh. 91 | if (dotpos) *dotpos = 0; 92 | BOOL too_long = FALSE; 93 | for (int i = 1; i < argc && !too_long; i++) 94 | { 95 | if (strlen (cmdline) + strlen (argv[i] + 1 + 1) < CPM_MAX_CMDLINE) 96 | { 97 | strcat (cmdline, " "); 98 | strcat (cmdline, argv[i]); 99 | } 100 | else 101 | too_long = TRUE; 102 | } 103 | if (!too_long) 104 | { 105 | log_debug ("Running CP/M with cmdline '%s'", cmdline); 106 | self->console_params->console_set_interrupt_handler 107 | (self->console_params->context, picocpm_interrupt_handler, self); 108 | int ret = cpm_runprog (self->console_params, cmdline, &self->z80_info); 109 | 110 | //console_params->set_interrupt_handler (NULL, NULL); ?? 111 | (void) ret; // TODO -- if there ever is a return from runprog(), convert it 112 | } 113 | else 114 | { 115 | shell_write_string (self->console_params, 116 | "Cmdline too long for CP/M\n"); // TODO -- console 117 | } 118 | return ret; 119 | } 120 | 121 | // 122 | // picocpm_get_config 123 | // 124 | Config *picocpm_get_config (PicoCPM *self) 125 | { 126 | return self->config; 127 | } 128 | 129 | 130 | -------------------------------------------------------------------------------- /shell/README.md: -------------------------------------------------------------------------------- 1 | # shell 2 | 3 | Implementation of the main command-line shell (shell.c) and the various 4 | commands (cmd\_xxx.c). 5 | -------------------------------------------------------------------------------- /shell/include/shell/shell.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/shell.h 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #pragma once 11 | #include "picocpm/picocpm.h" 12 | #include "error/error.h" 13 | #include "console/console.h" 14 | #include "picocpm/config.h" 15 | 16 | extern void shell_write_endl (ConsoleParams *console_params); 17 | extern void shell_write_error (Error error, ConsoleParams *console_params); 18 | extern void shell_write_error_filename (Error error, 19 | const char *filename, ConsoleParams *console_params); 20 | extern void shell_write_string (ConsoleParams *console_params, 21 | const char *fmt,...); 22 | 23 | /** Reset the line count before outputting a large amount of screen data. 24 | This allows the shell's paging mechanism to work, if it is enabled. */ 25 | void shell_reset_linecount (void); 26 | 27 | /** Write a single line, and pause for paging if the number of lines written 28 | exceeds the screen length */ 29 | extern Error shell_writeln (ConsoleParams *console_params, 30 | const char *fmt,...); 31 | 32 | extern void shell_main (ConsoleParams *console_params, PicoCPM *picocpm); 33 | 34 | extern Error cmd_type_run (int argc, char **argv, 35 | ConsoleParams *console_params); 36 | extern Error cmd_log_run (int argc, char **argv, 37 | ConsoleParams *console_params); 38 | extern Error cmd_era_run (int argc, char **argv, 39 | ConsoleParams *console_params); 40 | extern Error cmd_dir_run (int argc, char **argv, 41 | ConsoleParams *console_params); 42 | extern Error cmd_yrecv_run (int argc, char **argv, 43 | ConsoleParams *console_params); 44 | extern Error cmd_ysend_run (int argc, char **argv, 45 | ConsoleParams *console_params); 46 | extern Error cmd_dump_run (int argc, char **argv, 47 | ConsoleParams *console_params); 48 | extern Error cmd_setdef_run (int argc, char **argv, 49 | ConsoleParams *console_params, Config *config); 50 | extern Error cmd_rename_run (int argc, char **argv, 51 | ConsoleParams *console_params); 52 | extern Error cmd_stat_run (int argc, char **argv, 53 | ConsoleParams *console_params); 54 | extern Error cmd_format_run (int argc, char **argv, 55 | ConsoleParams *console_params); 56 | extern Error cmd_untar_run (int argc, char **argv, 57 | ConsoleParams *console_params); 58 | 59 | extern Error shell_do_line (const char *buff, 60 | ConsoleParams *console_params, PicoCPM *picocmp); 61 | 62 | -------------------------------------------------------------------------------- /shell/src/cmd_dir.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/cmd_dir.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "error/error.h" 15 | #include "log/log.h" 16 | #include "cpm/limits.h" 17 | #include "shell/shell.h" 18 | #include "console/console.h" 19 | #include "files/compat.h" 20 | 21 | Error cmd_dir_run (int argc, char **argv, 22 | ConsoleParams *console_params) 23 | { 24 | my_DIR *dir; 25 | Error ret = 0; 26 | const char *name; 27 | if (argc > 1) 28 | name = argv[1]; 29 | else 30 | name = ""; 31 | 32 | ConsoleProperties cprops; 33 | console_params->console_get_properties (console_params->context, &cprops); 34 | int space = 14; // 8 + 3 + separators 35 | int num_across = cprops.width / space; 36 | if (num_across == 0) num_across = 1; 37 | 38 | dir = my_opendir (name); 39 | 40 | // TODO limit to single file, if a filename is specified 41 | 42 | if (dir) 43 | { 44 | shell_reset_linecount(); 45 | struct my_dirent *de = my_readdir (dir); 46 | BOOL stop = FALSE; 47 | int across = 0; 48 | if (de) do 49 | { 50 | if (de->d_type == DT_REG_) 51 | { 52 | shell_write_string (console_params, "%14s", de->d_name); 53 | if (across == num_across - 1) 54 | { 55 | shell_writeln (console_params, ""); 56 | across = 0; 57 | } 58 | across++; 59 | } 60 | de = my_readdir (dir); 61 | } while (de && !stop); 62 | my_closedir (dir); 63 | } 64 | else 65 | { 66 | shell_write_error_filename (errno, name, console_params); 67 | ret = errno; 68 | } 69 | 70 | return ret; 71 | } 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /shell/src/cmd_dump.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/cmd_dump.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "error/error.h" 15 | #include "log/log.h" 16 | #include "shell/shell.h" 17 | #include "console/console.h" 18 | #include "files/compat.h" 19 | 20 | Error cmd_dump_run (int argc, char **argv, 21 | ConsoleParams *console_params) 22 | { 23 | if (argc < 2) 24 | { 25 | shell_write_error (ERROR_ARGCOUNT, console_params); 26 | return ERROR_ARGCOUNT; 27 | } 28 | errno = 0; 29 | const char *filename = argv[1]; 30 | int f = my_open (filename, O_RDONLY); 31 | if (f >= 0) 32 | { 33 | char buff[16]; 34 | int off = 0; 35 | int n = 1; 36 | shell_reset_linecount(); 37 | BOOL stop = FALSE; 38 | while (n > 0 && !stop) 39 | { 40 | shell_write_string (console_params, "%08X ", off); 41 | n = my_read (f, buff, sizeof (buff)); 42 | if (n >= 0) 43 | { 44 | for (int i = 0; i < n; i++) 45 | { 46 | shell_write_string (console_params, "%02X ", (unsigned char) 47 | buff[i]); 48 | } 49 | } 50 | if (shell_writeln (console_params, "") != 0) stop = TRUE; 51 | off += n; 52 | } 53 | my_close (f); 54 | } 55 | else 56 | shell_write_error_filename (errno, filename, console_params); 57 | return errno; 58 | } 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /shell/src/cmd_era.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/era.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "error/error.h" 15 | #include "log/log.h" 16 | #include "shell/shell.h" 17 | #include "console/console.h" 18 | #include "files/compat.h" 19 | 20 | // 21 | // cmd_era_erase_filespec 22 | // 23 | Error cmd_era_erase_filespec (const char *fullpath, 24 | ConsoleParams *console_params) 25 | { 26 | List *list = list_create (free); 27 | compat_globber (fullpath, list); 28 | int l = list_length (list); 29 | for (int i = 0; i < l; i++) 30 | { 31 | const char *file = list_get (list, i); 32 | if (my_unlink (file) != 0) 33 | { 34 | shell_write_error_filename (errno, file, console_params); 35 | } 36 | } 37 | list_destroy (list); 38 | return 0; 39 | } 40 | 41 | // 42 | // cmd_era_erase_run 43 | // 44 | Error cmd_era_run (int argc, char **argv, 45 | ConsoleParams *console_params) 46 | { 47 | for (int i = 1; i < argc; i++) 48 | { 49 | const char *filespec = argv[i]; 50 | cmd_era_erase_filespec (filespec, console_params); 51 | // TODO 52 | } 53 | return 0; 54 | } 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /shell/src/cmd_format.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/cmd_format.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "error/error.h" 16 | #include "log/log.h" 17 | #include "shell/shell.h" 18 | #include "console/console.h" 19 | #include "files/compat.h" 20 | 21 | // 22 | // cmd_format_run 23 | // 24 | Error cmd_format_run (int argc, char **argv, 25 | ConsoleParams *console_params) 26 | { 27 | Error ret = 0; 28 | int drive_num; 29 | if (argc >= 2) 30 | drive_num = toupper (argv[1][0]) - 'A'; 31 | else 32 | drive_num = filecontext_global_get_current_drive (); 33 | 34 | if (drive_num < FILESYSTEM_MAX_MOUNTS) 35 | { 36 | char drive_letter = drive_num + 'A'; 37 | shell_write_string (console_params, "Delete all data on drive %c (y/n)? ", 38 | drive_letter); 39 | char buff[4]; 40 | console_params->console_get_line 41 | (console_params->context, buff, 3, 0, NULL); 42 | if (buff[0] == 'y' || buff[1] == 'Y') 43 | { 44 | ret = filecontext_global_format (drive_num); 45 | if (ret) 46 | { 47 | shell_write_error (ret, console_params); 48 | } 49 | } 50 | } 51 | else 52 | { 53 | ret = ERROR_BADDRIVELETTER; 54 | shell_write_error (ret, console_params); 55 | } 56 | return ret; 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /shell/src/cmd_log.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/cmd_log.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "error/error.h" 15 | #include "log/log.h" 16 | #include "shell/shell.h" 17 | #include "console/console.h" 18 | #include "files/compat.h" 19 | 20 | Error cmd_log_run (int argc, char **argv, 21 | ConsoleParams *console_params) 22 | { 23 | if (argc < 2) 24 | { 25 | shell_write_error (ERROR_ARGCOUNT, console_params); 26 | } 27 | int l = atoi (argv[1]); 28 | log_set_level (l); 29 | return 0; 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /shell/src/cmd_rename.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/cmd_rename.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "error/error.h" 15 | #include "log/log.h" 16 | #include "shell/shell.h" 17 | #include "console/console.h" 18 | #include "files/compat.h" 19 | 20 | // 21 | // cmd_rename_source_target 22 | // 23 | Error cmd_rename_source_target (ConsoleParams *cp, const char *source, 24 | const char *target) 25 | { 26 | Error ret = 0; 27 | log_debug ("cmd_ren_src_tgt: src=%s tgt=%s\n", source, target); 28 | if (MYRENAME (source, target) != 0) 29 | { 30 | shell_writeln (cp, "Can't rename %s to %s: %s", source, target, 31 | error_strerror (errno)); 32 | ret = errno; 33 | } 34 | return ret; 35 | } 36 | 37 | // 38 | // cmd_rename_source_target_spec 39 | // 40 | Error cmd_rename_source_target_spec (ConsoleParams *cp, const char *source, 41 | const char *target) 42 | { 43 | // TODO process wildcards here 44 | return cmd_rename_source_target (cp, source, target); 45 | } 46 | 47 | // 48 | // cmd_rename_run 49 | // 50 | Error cmd_rename_prompt (ConsoleParams *cp) 51 | { 52 | Error ret = 0; 53 | 54 | char source[BDOS_MAX_FNAME + 1]; 55 | char target[BDOS_MAX_FNAME + 1]; 56 | shell_write_string (cp, "Enter new name: "); 57 | ret = cp->console_get_line (cp->context, target, BDOS_MAX_FNAME, 0, NULL); 58 | if (ret == 0 && target[0] != 0) 59 | { 60 | shell_write_string (cp, "Enter old name: "); 61 | //shell_writeln (cp, ""); 62 | ret = cp->console_get_line (cp->context, source, BDOS_MAX_FNAME, 0, NULL); 63 | if (ret == 0 && source[0] != 0) 64 | { 65 | ret = cmd_rename_source_target_spec (cp, source, target); 66 | } 67 | } 68 | 69 | return ret; 70 | } 71 | 72 | // 73 | // cmd_rename_run 74 | // 75 | Error cmd_rename_do_spec (ConsoleParams *cp, const char *spec) 76 | { 77 | Error ret = 0; 78 | 79 | char buff[BDOS_MAX_FNAME * 2 + 2]; 80 | strncpy (buff, spec, BDOS_MAX_FNAME * 2); 81 | buff[sizeof(buff) - 1] = 0; 82 | 83 | char *eq = strchr (buff, '='); 84 | if (eq) 85 | { 86 | *eq = 0; 87 | const char *target = buff; 88 | const char *source = eq + 1; 89 | if (strlen (source) > 0 && strlen (target) > 0) 90 | { 91 | ret = cmd_rename_source_target_spec (cp, source, target); 92 | } 93 | else 94 | { 95 | ret = ERROR_CMD_SYNTAX; 96 | shell_write_error_filename (ret, spec, cp); 97 | } 98 | } 99 | else 100 | { 101 | ret = ERROR_CMD_SYNTAX; 102 | shell_write_error_filename (ret, spec, cp); 103 | } 104 | 105 | return ret; 106 | } 107 | 108 | // 109 | // cmd_rename_run 110 | // 111 | Error cmd_rename_run (int argc, char **argv, 112 | ConsoleParams *console_params) 113 | { 114 | Error ret = 0; 115 | if (argc == 1) 116 | { 117 | ret = cmd_rename_prompt (console_params); 118 | } 119 | else 120 | { 121 | for (int i = 1; i < argc && ret == 0; i++) 122 | ret = cmd_rename_do_spec (console_params, argv[i]); 123 | } 124 | return ret; 125 | } 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /shell/src/cmd_setdef.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/setdef.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "error/error.h" 15 | #include "log/log.h" 16 | #include "shell/shell.h" 17 | #include "picocpm/config.h" 18 | #include "console/console.h" 19 | #include "files/compat.h" 20 | 21 | //width height 22 | // 23 | // cmd_setdef_show 24 | // 25 | void cmd_setdef_show (ConsoleParams *console_params, Config *config) 26 | { 27 | shell_writeln (console_params, "%s", config->page ? "PAGE" : "NOPAGE"); 28 | shell_writeln (console_params, "%s", 29 | config->flow_control ? "FLOWCONTROL" : "NOFLOWCONTROL"); 30 | } 31 | 32 | // 33 | // cmd_setdef_do_nvp 34 | // 35 | Error cmd_setdef_do_nvp (ConsoleParams *console_params, char *buff, 36 | Config *config) 37 | { 38 | (void)console_params; 39 | Error ret = 0; 40 | string_to_upper (buff); 41 | if (strcmp (buff, "PAGE") == 0) 42 | config->page = TRUE; 43 | else if (strcmp (buff, "NOPAGE") == 0) 44 | config->page = FALSE; 45 | if (strcmp (buff, "FLOWCONTROL") == 0) 46 | config->flow_control = TRUE; 47 | else if (strcmp (buff, "NOFLOWCONTROL") == 0) 48 | config->flow_control = FALSE; 49 | return ret; 50 | } 51 | 52 | // 53 | // cmd_setdef_do_drives 54 | // 55 | Error cmd_setdef_do_drives (ConsoleParams *console_params, char *buff, 56 | Config *config) 57 | { 58 | (void)config; 59 | (void)buff; 60 | Error ret = ERROR_UNIMPLEMENTED; 61 | shell_write_error (ret, console_params); 62 | return ret; 63 | } 64 | 65 | // 66 | // cmd_setdef_do_arg 67 | // 68 | 69 | Error cmd_setdef_do_arg (ConsoleParams *console_params, const char *arg, 70 | Config *config) 71 | { 72 | Error ret = 0; 73 | char buff[20]; 74 | strncpy (buff, arg, sizeof (buff) - 1); 75 | buff[sizeof(buff) - 1] = 0; 76 | if (buff[0] == '[') 77 | { 78 | char *p = strchr (buff, ']'); 79 | if (p) *p = 0; 80 | ret = cmd_setdef_do_nvp (console_params, buff + 1, config); 81 | } 82 | else 83 | { 84 | ret = cmd_setdef_do_drives (console_params, buff, config); 85 | } 86 | return ret; 87 | } 88 | 89 | // 90 | // cmd_setdef_run 91 | // 92 | Error cmd_setdef_run (int argc, char **argv, 93 | ConsoleParams *console_params, Config *config) 94 | { 95 | Error ret = 0; 96 | if (argc == 1) 97 | { 98 | cmd_setdef_show (console_params, config); 99 | } 100 | else 101 | { 102 | for (int i = 1; i < argc; i++) 103 | { 104 | cmd_setdef_do_arg (console_params, argv[i], config); 105 | } 106 | } 107 | return ret; 108 | } 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /shell/src/cmd_stat.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/cmd_stat.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "error/error.h" 15 | #include "log/log.h" 16 | #include "shell/shell.h" 17 | #include "console/console.h" 18 | #include "files/compat.h" 19 | #include "cpm/limits.h" 20 | 21 | // 22 | // cmd_stat_do_drive 23 | // 24 | Error cmd_stat_do_drive (ConsoleParams *cp, const char *spec, 25 | uint8_t drive, const char *path) 26 | { 27 | Error ret = 0; 28 | if (path == NULL || path[0] == 0) 29 | { 30 | my_statfs statfs; 31 | int err = filecontext_global_dstatfs (drive, &statfs); 32 | if (err == 0) 33 | { 34 | uint32_t bytes_free = (int)statfs.block_size * (int)statfs.free_blocks; 35 | shell_writeln (cp, 36 | "%c: R/W, Space: %dk (%d of %d blocks of size %d)", drive + 'A', 37 | bytes_free / 1000, (int)statfs.free_blocks, (int)statfs.total_blocks, 38 | (int)statfs.block_size); 39 | } 40 | else 41 | { 42 | shell_write_error (err, cp); 43 | ret = err; 44 | } 45 | } 46 | else 47 | { 48 | List *list = list_create (free); 49 | compat_globber (spec, list); 50 | int l = list_length (list); 51 | 52 | BOOL header_done = FALSE; 53 | for (int i = 0; i < l; i++) 54 | { 55 | const char *file = list_get (list, i); 56 | struct stat sb; 57 | if (my_stat (file, &sb) == 0) 58 | { 59 | if (S_ISREG (sb.st_mode)) 60 | { 61 | if (!header_done) 62 | { 63 | shell_writeln (cp, "Recs Bytes Ext Acc"); 64 | header_done = TRUE; 65 | } 66 | uint32_t size = sb.st_size; 67 | int recs = (size + (BDOS_RECORD_SIZE - 1)) / BDOS_RECORD_SIZE; 68 | int extents = (size + (BDOS_EXTENT_SIZE - 1)) / BDOS_EXTENT_SIZE; 69 | int kb = size / 1024; 70 | shell_writeln (cp, "%4d%6dk%5d R/W %s", recs, kb, extents, file); 71 | } 72 | } 73 | } 74 | 75 | list_destroy (list); 76 | cmd_stat_do_drive (cp, NULL, drive, NULL); 77 | } 78 | return ret; 79 | } 80 | 81 | // 82 | // cmd_stat_run 83 | // 84 | Error cmd_stat_run (int argc, char **argv, 85 | ConsoleParams *console_params) 86 | { 87 | Error ret = 0; 88 | if (argc == 1) 89 | { 90 | for (int i = 0; i < FILESYSTEM_MAX_MOUNTS; i++) 91 | { 92 | ret = filecontext_global_activate_drive (i); 93 | if (ret == 0) 94 | ret = cmd_stat_do_drive (console_params, NULL, (uint8_t) i, NULL); 95 | } 96 | } 97 | else 98 | { 99 | const char *spec = argv[1]; 100 | int drive = compat_get_drive (spec); 101 | const char *path = compat_get_path (spec); 102 | 103 | ret = cmd_stat_do_drive (console_params, spec, (uint8_t) drive, path); 104 | } 105 | return ret; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /shell/src/cmd_type.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/type.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "error/error.h" 15 | #include "shell/shell.h" 16 | #include "console/console.h" 17 | #include "files/compat.h" 18 | 19 | Error cmd_type_run (int argc, char **argv, 20 | ConsoleParams *console_params) 21 | { 22 | if (argc < 2) 23 | { 24 | shell_write_error (ERROR_ARGCOUNT, console_params); 25 | return ERROR_ARGCOUNT; 26 | } 27 | 28 | int f = my_open (argv[1], O_RDONLY); 29 | if (f >= 0) 30 | { 31 | char buff[257]; 32 | int n = 1; 33 | while (n > 0) 34 | { 35 | n = my_read (f, buff, sizeof (buff) - 1); 36 | if (n >= 0) 37 | { 38 | buff[n] = 0; 39 | // In CP/M, ascii files that don't occupy a fixed number of 40 | // 128-byte records are padded with ctrl-z's. In general, 41 | // there's no way to know the true file length, other than 42 | // to the nearest block. 43 | char *zpos = strchr (buff, 26); // Truncate at ctrl-z 44 | if (zpos) *zpos = 0; 45 | shell_write_string (console_params, "%s", buff); 46 | } 47 | } 48 | my_close (f); 49 | } 50 | else 51 | shell_write_error_filename (errno, argv[1], console_params); 52 | return 0; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /shell/src/cmd_untar.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/cmd_untar.c 6 | 7 | Copyright (c)2001 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "error/error.h" 15 | #include "log/log.h" 16 | #include "shell/shell.h" 17 | #include "console/console.h" 18 | #include "files/compat.h" 19 | 20 | // 21 | // cmd_untar_parse_octal 22 | // 23 | static int cmd_untar_parse_octal (const char *p, size_t n) 24 | { 25 | int i = 0; 26 | 27 | while ((*p < '0' || *p > '7') && n > 0) 28 | { 29 | ++p; 30 | --n; 31 | } 32 | 33 | while (*p >= '0' && *p <= '7' && n > 0) 34 | { 35 | i *= 8; 36 | i += *p - '0'; 37 | ++p; 38 | --n; 39 | } 40 | 41 | return i; 42 | } 43 | 44 | // 45 | // cmd_untar_verify_checksum 46 | // 47 | static BOOL cmd_untar_verify_checksum (const char *p) 48 | { 49 | int u = 0; 50 | for (int n = 0; n < 512; ++n) 51 | { 52 | if (n < 148 || n > 155) 53 | u += ((unsigned char *)p)[n]; 54 | else 55 | u += 0x20; 56 | } 57 | 58 | int stored_cs = cmd_untar_parse_octal (p + 148, 8); 59 | return (u == stored_cs); 60 | } 61 | 62 | // 63 | // cmd_untar_is_end_of_archive 64 | // 65 | static BOOL cmd_untar_is_end_of_archive (const char *p) 66 | { 67 | for (int n = 511; n >= 0; --n) 68 | if (p[n] != '\0') 69 | return FALSE; 70 | return TRUE; 71 | } 72 | 73 | // 74 | // cmd_untar_do 75 | // 76 | static void cmd_untar_do (ConsoleParams *cp, MYFILE *a) 77 | { 78 | char buff[512]; 79 | MYFILE *f = NULL; 80 | 81 | for (;;) 82 | { 83 | size_t bytes_read = my_fread (buff, 1, sizeof (buff), a); 84 | if (bytes_read < sizeof (buff)) 85 | { 86 | shell_writeln (cp, "Short read: expected %d, got %d", 87 | sizeof (buff), (int)bytes_read); 88 | return; 89 | } 90 | if (cmd_untar_is_end_of_archive (buff)) 91 | { 92 | return; 93 | } 94 | if (!cmd_untar_verify_checksum (buff)) 95 | { 96 | shell_writeln (cp, "Checksum failure"); 97 | return; 98 | } 99 | int filesize = cmd_untar_parse_octal (buff + 124, 12); 100 | switch (buff[156]) 101 | { 102 | case '1': 103 | case '2': 104 | case '3': 105 | case '4': 106 | case '5': 107 | case '6': 108 | shell_writeln (cp, "Ignoring %s", buff); 109 | break; 110 | default: 111 | shell_writeln (cp, "Extracting file %s", buff); 112 | f = my_fopen (buff, "w"); 113 | break; 114 | } 115 | while (filesize > 0) 116 | { 117 | bytes_read = my_fread (buff, 1, sizeof(buff), a); 118 | if (bytes_read < sizeof (buff)) 119 | { 120 | shell_writeln (cp, "Short read: Expected %d, got %d", 121 | sizeof (buff), (int)bytes_read); 122 | return; 123 | } 124 | if (filesize < (int)sizeof (buff)) 125 | bytes_read = filesize; 126 | if (f) 127 | { 128 | if ((int)my_fwrite (buff, 1, bytes_read, f) != (int)bytes_read) 129 | { 130 | shell_writeln (cp, "Failed write"); 131 | my_fclose (f); 132 | f = NULL; 133 | } 134 | } 135 | filesize -= bytes_read; 136 | } 137 | if (f) 138 | { 139 | my_fclose (f); 140 | f = NULL; 141 | } 142 | } 143 | } 144 | 145 | // 146 | // cmd_untar_run 147 | // 148 | Error cmd_untar_run (int argc, char **argv, 149 | ConsoleParams *console_params) 150 | { 151 | if (argc < 2) 152 | { 153 | shell_write_error (ERROR_ARGCOUNT, console_params); 154 | return ERROR_ARGCOUNT; 155 | } 156 | errno = 0; 157 | char *filename = argv[1]; 158 | MYFILE *f = my_fopen (filename, "r"); 159 | if (f) 160 | { 161 | cmd_untar_do (console_params, f); 162 | my_fclose (f); 163 | } 164 | else 165 | shell_write_error_filename (errno, filename, console_params); 166 | return errno; 167 | } 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /shell/src/cmd_yrecv.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | CPICOM 4 | 5 | shell/yrecv.c 6 | 7 | Copyright (c)2021 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "error/error.h" 15 | #include "log/log.h" 16 | #include "shell/shell.h" 17 | #include "console/console.h" 18 | #include "files/compat.h" 19 | #include "ymodem/ymodem.h" 20 | 21 | // Largest file we are prepared to accept (essentially, large enough 22 | // to fill a 720k floppy). 23 | #define YMODEM_MAX 699904 24 | 25 | // 26 | // cmd_yrecv_usage 27 | // 28 | void cmd_yrecv_usage (ConsoleParams *console_params) 29 | { 30 | shell_write_string (console_params, "Usage: yrecv [filename]"); 31 | shell_write_endl (console_params); 32 | } 33 | 34 | // 35 | // cmd_yrecv_run 36 | // 37 | Error cmd_yrecv_run (int argc, char **argv, 38 | ConsoleParams *console_params) 39 | { 40 | Error ret = 0; 41 | // TODO options 42 | /* 43 | if (argc > 1) 44 | dir = my_opendir (argv[1]); 45 | else 46 | dir = my_opendir (""); 47 | */ 48 | 49 | char *filename = NULL; 50 | if (argc >= 1) 51 | { 52 | filename = argv[1]; 53 | } 54 | 55 | YmodemErr err = ymodem_receive (console_params, filename, YMODEM_MAX); 56 | if (err != 0) 57 | { 58 | if (filename) 59 | shell_writeln (console_params, "YModem transfer failed: %s", 60 | ymodem_strerror (err)); 61 | else 62 | shell_writeln (console_params, 63 | "YModem transfer failed for file %s: %s", filename, 64 | ymodem_strerror (err)); 65 | ret = ERROR_YMODEM; 66 | } 67 | 68 | return ret; 69 | } 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /shell/src/cmd_ysend.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | CPICOM 4 | 5 | shell/ysend.c 6 | 7 | Copyright (c)2021 Kevin Boone, GPL v3.0 8 | 9 | =========================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "error/error.h" 15 | #include "log/log.h" 16 | #include "shell/shell.h" 17 | #include "console/console.h" 18 | #include "files/compat.h" 19 | #include "ymodem/ymodem.h" 20 | 21 | // 22 | // cmd_send_usage 23 | // 24 | void cmd_ysend_usage (ConsoleParams *console_params) 25 | { 26 | shell_write_string (console_params, "Usage: ysend {filename}"); 27 | shell_write_endl (console_params); 28 | } 29 | 30 | // 31 | // cmd_ysend_run 32 | // 33 | Error cmd_ysend_run (int argc, char **argv, 34 | ConsoleParams *console_params) 35 | { 36 | Error ret = 0; 37 | 38 | char *filename = NULL; 39 | if (argc >= 1) 40 | { 41 | filename = argv[1]; 42 | } 43 | 44 | if (filename == NULL) 45 | { 46 | cmd_ysend_usage (console_params); 47 | return ERROR_CMD_SYNTAX; 48 | } 49 | 50 | YmodemErr err = ymodem_send (console_params, filename); 51 | if (err != 0) 52 | { 53 | if (filename) 54 | shell_writeln (console_params, "YModem transfer failed: %s", 55 | ymodem_strerror (err)); 56 | else 57 | shell_writeln (console_params, 58 | "YModem transfer failed for file %s: %s", filename, 59 | ymodem_strerror (err)); 60 | ret = ERROR_YMODEM; 61 | } 62 | 63 | return ret; 64 | } 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /shell/src/shell.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | cpicom 4 | 5 | shell/shell.c 6 | 7 | This is the implementation of the main command processor, along with 8 | various utility functions that specific shell commands may use. 9 | 10 | Copyright (c)2001 Kevin Boone, GPL v3.0 11 | 12 | =========================================================================*/ 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "error/error.h" 19 | #include "shell/shell.h" 20 | #include "klib/string.h" 21 | #include "console/console.h" 22 | #include "klib/list.h" 23 | #include "files/filecontext.h" 24 | #include "files/compat.h" 25 | #include "picocpm/config.h" 26 | #include "picocpm/picocpm.h" 27 | 28 | int shell_lines = 0; 29 | int shell_max_lines; // Get from console 30 | 31 | // Storing this here is ugly but, what the heck, there's only going to 32 | // be a single shell. 33 | static Config *config; 34 | 35 | // 36 | // shell_reset_linecount 37 | // 38 | void shell_reset_linecount (void) 39 | { 40 | shell_lines = 0; 41 | } 42 | 43 | // 44 | // shell_writeln 45 | // 46 | Error shell_writeln (ConsoleParams *console_params, 47 | const char *fmt,...) 48 | { 49 | Error ret = 0; 50 | va_list ap; 51 | va_start (ap, fmt); 52 | console_params->console_out_string_v (console_params->context, fmt, ap); 53 | console_params->console_out_endl (console_params->context); 54 | va_end (ap); 55 | if (config->page) 56 | { 57 | shell_lines++; 58 | if (shell_lines >= shell_max_lines - 1) 59 | { 60 | console_params->console_out_string (console_params->context, 61 | "[Press any key...]"); 62 | char c = console_params->console_get_char_timeout 63 | (console_params->context, -1); 64 | if (c == 0x03) // interrupt. Nasty, nasty, ugh. Ewww... 65 | ret = ERROR_INTERRUPTED; 66 | console_params->console_out_endl (console_params->context); 67 | shell_lines = 0; 68 | } 69 | } 70 | return ret; 71 | } 72 | 73 | // 74 | // shell_write_string_v 75 | // 76 | void shell_write_string (ConsoleParams *console_params, 77 | const char *fmt,...) 78 | { 79 | va_list ap; 80 | va_start (ap, fmt); 81 | console_params->console_out_string_v (console_params->context, fmt, ap); 82 | va_end (ap); 83 | } 84 | 85 | // 86 | // shell_write_end 87 | // 88 | void shell_write_endl (ConsoleParams *console_params) 89 | { 90 | console_params->console_out_endl (console_params->context); 91 | } 92 | 93 | // 94 | // shell_write_error 95 | // 96 | void shell_write_error (Error error, ConsoleParams *console_params) 97 | { 98 | // TODO 99 | const char *msg = error_strerror (error); 100 | if (msg) 101 | console_params->console_out_string (console_params->context, msg); 102 | else 103 | console_params->console_out_string (console_params->context, 104 | "Error %d", error); 105 | console_params->console_out_endl (console_params->context); 106 | } 107 | 108 | // 109 | // shell_write_error_filename 110 | // 111 | void shell_write_error_filename (Error error, const char *filename, 112 | ConsoleParams *console_params) 113 | { 114 | // TODO 115 | console_params->console_out_string (console_params, "%s: ", filename); 116 | shell_write_error (error, console_params); 117 | } 118 | 119 | // 120 | // shell_find_executable_on_drive 121 | // 122 | static BOOL shell_find_executable_on_drive (char drive_letter, 123 | const char *name83, char *ext83, char *exe_path) 124 | { 125 | char try_name[21]; 126 | 127 | try_name[0] = drive_letter; 128 | try_name[1] = ':'; 129 | try_name[2] = 0; 130 | 131 | if (ext83[0]) 132 | { 133 | strcat (try_name, name83); 134 | strcat (try_name, "."); 135 | strcat (try_name, ext83); 136 | } 137 | else 138 | { 139 | // TODO check .sub 140 | strcat (try_name, name83); 141 | strcat (try_name, "."); 142 | strcat (try_name, "COM"); 143 | } 144 | 145 | int fd = my_open (try_name, O_RDONLY); 146 | if (fd >= 0) 147 | { 148 | strcpy (exe_path, try_name); 149 | my_close (fd); 150 | return TRUE; 151 | } 152 | 153 | return FALSE; 154 | } 155 | 156 | // 157 | // Search for an executable (com or sub [not yet implemented]) 158 | // that matches the name. The name may include a drive, in which 159 | // case the text is a simple match. name may be in any case, but 160 | // the match is case-insensitve, and the result will be 161 | // upper-case. 162 | BOOL shell_find_executable (const char *name, char *exe_path) 163 | { 164 | BOOL ret = FALSE; 165 | 166 | char name83[9]; 167 | char ext83[4]; 168 | char drive_letter; 169 | compat_split_drive_name_ext_83 (name, &drive_letter, name83, ext83); 170 | 171 | if (drive_letter) 172 | { 173 | return shell_find_executable_on_drive (drive_letter, 174 | name83, ext83, exe_path); 175 | } 176 | else 177 | { 178 | // TODO search path 179 | return shell_find_executable_on_drive ('A', name83, ext83, exe_path); 180 | } 181 | 182 | return ret; 183 | } 184 | 185 | // 186 | // shell_do_line_argv 187 | // 188 | Error shell_do_line_argv (int argc, char **argv, 189 | ConsoleParams *console_params, PicoCPM *picocpm) 190 | { 191 | char exe_path [20]; // TODO 192 | Error ret = 0; 193 | if (argc == 0) return 0; 194 | 195 | if (strlen(argv[0]) == 2 && argv[0][1] == ':') 196 | { 197 | filecontext_global_set_current_drive (toupper(argv[0][0]) - 'A'); 198 | } 199 | else if (strcasecmp (argv[0], "foo") == 0) 200 | { 201 | shell_reset_linecount (); 202 | for (int i = 0; i < 50; i++) 203 | { 204 | shell_writeln (console_params, "line %d", i); 205 | } 206 | } 207 | else if (strncasecmp (argv[0], "ren", 3) == 0) 208 | { 209 | ret = cmd_rename_run (argc, argv, console_params); 210 | } 211 | else if (strncasecmp (argv[0], "typ", 3) == 0) 212 | { 213 | ret = cmd_type_run (argc, argv, console_params); 214 | } 215 | else if (strncasecmp (argv[0], "era", 3) == 0) 216 | { 217 | ret = cmd_era_run (argc, argv, console_params); 218 | } 219 | else if (strncasecmp (argv[0], "dir", 3) == 0) 220 | { 221 | ret = cmd_dir_run (argc, argv, console_params); 222 | } 223 | else if (strcasecmp (argv[0], "log") == 0) 224 | { 225 | ret = cmd_log_run (argc, argv, console_params); 226 | } 227 | else if (strncasecmp (argv[0], "yrecv", 3) == 0) 228 | { 229 | ret = cmd_yrecv_run (argc, argv, console_params); 230 | } 231 | else if (strncasecmp (argv[0], "ysend", 3) == 0) 232 | { 233 | ret = cmd_ysend_run (argc, argv, console_params); 234 | } 235 | else if (strncasecmp (argv[0], "dump", 3) == 0) 236 | { 237 | ret = cmd_dump_run (argc, argv, console_params); 238 | } 239 | else if (strncasecmp (argv[0], "untar", 3) == 0) 240 | { 241 | ret = cmd_untar_run (argc, argv, console_params); 242 | } 243 | else if (strncasecmp (argv[0], "stat", 3) == 0) 244 | { 245 | ret = cmd_stat_run (argc, argv, console_params); 246 | } 247 | else if (strcasecmp (argv[0], "format") == 0) 248 | { 249 | ret = cmd_format_run (argc, argv, console_params); 250 | } 251 | else if (strcasecmp (argv[0], "setdef") == 0) 252 | { 253 | ret = cmd_setdef_run (argc, argv, console_params, config); 254 | } 255 | else if (strcasecmp (argv[0], "cls") == 0) 256 | { 257 | console_params->console_cls (console_params->context); 258 | console_params->console_set_cursor (console_params->context, 0, 0); 259 | ret = 0; 260 | } 261 | else if (strcasecmp (argv[0], "test1") == 0) 262 | { 263 | for (int i = 0; i < 200; i++) 264 | { 265 | printf ("%d 123456890111111234568902345689023456890234568902345689023456890\n", i); 266 | 267 | for (int j = 0; j < 69; j++) 268 | ; // Do nothing 269 | } 270 | printf ("\n"); 271 | ret = 0; 272 | } 273 | else if (strcasecmp (argv[0], "test2") == 0) 274 | { 275 | for (int i = 0; i < 100; i++) 276 | { 277 | for (int j = 0; j < 70; j++) 278 | { 279 | console_params->console_out_char 280 | (console_params->context, 'x'); 281 | } 282 | console_params->console_out_char 283 | (console_params->context, 10); 284 | } 285 | ret = 0; 286 | } 287 | else if (shell_find_executable (argv[0], exe_path)) 288 | { 289 | picocpm_run (picocpm, exe_path, argc, argv); 290 | ret = 0; 291 | } 292 | else 293 | { 294 | shell_write_string (console_params, argv[0]); 295 | shell_write_string (console_params, "?"); 296 | shell_write_endl (console_params); 297 | } 298 | 299 | return ret; 300 | } 301 | 302 | 303 | // 304 | // shell_do_line 305 | // 306 | Error shell_do_line (const char *buff, ConsoleParams *console_params, 307 | PicoCPM *picocpm) 308 | { 309 | ConsoleProperties cprops; 310 | console_params->console_get_properties (console_params->context, &cprops); 311 | shell_max_lines = cprops.height; 312 | config = picocpm_get_config (picocpm); 313 | if (buff[0] == 0) return 0; 314 | 315 | Error ret = 0; 316 | String *sbuff = string_create (buff); 317 | List *args = string_tokenize (sbuff); 318 | int l = list_length (args); 319 | char **argv = malloc ((l + 1) * sizeof (char *)); 320 | 321 | if (argv) 322 | { 323 | for (int i = 0; i < l; i++) 324 | { 325 | const String *arg = list_get (args, i); 326 | argv[i] = strdup (string_cstr (arg)); 327 | } 328 | argv[l] = NULL; 329 | 330 | int argc = l; 331 | if (strcasecmp (argv[0], "quit") == 0) 332 | ret = ERROR_ENDOFINPUT; 333 | else 334 | ret = shell_do_line_argv (argc, argv, console_params, picocpm); 335 | 336 | for (int i = 0; i < l; i++) 337 | { 338 | free (argv[i]); 339 | } 340 | 341 | free (argv); 342 | } 343 | else 344 | { 345 | shell_write_error (ERROR_NOMEM, console_params); 346 | } 347 | 348 | list_destroy (args); 349 | string_destroy (sbuff); 350 | return ret; 351 | } 352 | 353 | // 354 | // shell_main 355 | // 356 | void shell_main (ConsoleParams *console_params, PicoCPM *picocpm) 357 | { 358 | List *history = list_create (free); 359 | config = picocpm_get_config (picocpm); 360 | BOOL stop = FALSE; 361 | shell_writeln (console_params, "CPICOM version 0.1d"); 362 | shell_writeln (console_params, "CP/M emulator for Raspberry Pi Pico"); 363 | shell_writeln (console_params, "By Kevin Boone, and many others."); 364 | shell_writeln (console_params, ""); 365 | while (!stop) 366 | { 367 | console_params->console_out_char 368 | (console_params->context, filecontext_global_get_current_drive() + 'A'); 369 | console_params->console_out_string (console_params->context, "> "); 370 | char buff[257]; 371 | Error err = console_params->console_get_line (console_params->context, 372 | buff, sizeof (buff) - 1, 10, history); 373 | if (err == 0) 374 | { 375 | err = shell_do_line (buff, console_params, picocpm); 376 | if (err == ERROR_ENDOFINPUT) 377 | stop = TRUE; 378 | shell_writeln (console_params, ""); 379 | } 380 | else if (err == ERROR_ENDOFINPUT) 381 | stop = TRUE; 382 | } 383 | 384 | list_destroy (history); 385 | } 386 | 387 | -------------------------------------------------------------------------------- /ymodem/README.md: -------------------------------------------------------------------------------- 1 | # ymodem 2 | 3 | Implementation of the ymodem send and receive functions. 4 | 5 | -------------------------------------------------------------------------------- /ymodem/include/ymodem/ymodem.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Ymodem 4 | 5 | xmodem/xmodem.h 6 | 7 | (c)2021 Kevin Boone, GPLv3.0 8 | 9 | Based on work placed into the public domain by 10 | Fredrik Hederstierna, 2014 11 | 12 | =========================================================================*/ 13 | 14 | #pragma once 15 | 16 | #include 17 | #include "console/console.h" 18 | 19 | typedef enum _YmodemErr 20 | { 21 | YmodemOK = 0, 22 | YmodemWriteFile, 23 | YmodemReadFile, 24 | YmodemTooBig, 25 | YmodemChecksum, 26 | YmodemCancelled, 27 | YmodemBadPacket, 28 | YmodemNoCRC 29 | } YmodemErr; 30 | 31 | 32 | /** Receive to the specified filename. If this is NULL, use the 33 | filename in the ymodem header. And if there's nothing there, either, 34 | use "untitled.txt". Of course, it makes no sense to use transmit 35 | multiples files and _not_ supply filenames. Similarly, it makes no 36 | sense to invoke this method with a filename, when we expect multiple 37 | files. */ 38 | extern YmodemErr ymodem_receive (ConsoleParams *consoleParams, 39 | const char *filename, uint32_t max_size); 40 | 41 | extern YmodemErr ymodem_send (ConsoleParams *consoleParams, 42 | const char *filename); 43 | 44 | /** Get an English string corresponding to the error code. */ 45 | extern const char *ymodem_strerror (YmodemErr err); 46 | 47 | 48 | 49 | 50 | 51 | --------------------------------------------------------------------------------