mirror of
https://github.com/hsoft/collapseos.git
synced 2024-09-07 19:18:46 +10:00
We now treat the block device as fixed-size rather than trying to grow it in response to kernel activity. Previously, if you tried to create 2 files in a row, only the first 1 would actually appear, because the device only ever got larger when a byte was written immediately past the end of the device. Now we just let the kernel write bytes to the disk anywhere, so new files can be created even when the previous file is not completely full. Also, fix buffer overrun in reading filesystem image, and use a more idiomatic fgetc loop.
208 lines
5.2 KiB
C
208 lines
5.2 KiB
C
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include "../emul.h"
|
|
#include "shell-bin.h"
|
|
|
|
/* Collapse OS shell with filesystem
|
|
*
|
|
* On startup, if "cfsin" directory exists, it packs it as a afke block device
|
|
* and loads it in. Upon halting, unpcks the contents of that block device in
|
|
* "cfsout" directory.
|
|
*
|
|
* Memory layout:
|
|
*
|
|
* 0x0000 - 0x3fff: ROM code from shell.asm
|
|
* 0x4000 - 0x4fff: Kernel memory
|
|
* 0x5000 - 0xffff: Userspace
|
|
*
|
|
* I/O Ports:
|
|
*
|
|
* 0 - stdin / stdout
|
|
* 1 - Filesystem blockdev data read/write. Reads and write data to the address
|
|
* previously selected through port 2
|
|
*/
|
|
|
|
//#define DEBUG
|
|
#define MAX_FSDEV_SIZE 0x20000
|
|
|
|
// in sync with glue.asm
|
|
#define RAMSTART 0x2000
|
|
#define STDIO_PORT 0x00
|
|
#define FS_DATA_PORT 0x01
|
|
// Controls what address (24bit) the data port returns. To select an address,
|
|
// this port has to be written to 3 times, starting with the MSB.
|
|
// Reading this port returns an out-of-bounds indicator. Meaning:
|
|
// 0 means addr is within bounds
|
|
// 1 means that we're equal to fsdev size (error for reading, ok for writing)
|
|
// 2 means more than fsdev size (always invalid)
|
|
// 3 means incomplete addr setting
|
|
#define FS_ADDR_PORT 0x02
|
|
|
|
static uint8_t fsdev[MAX_FSDEV_SIZE] = {0};
|
|
static uint32_t fsdev_ptr = 0;
|
|
// 0 = idle, 1 = received MSB (of 24bit addr), 2 = received middle addr
|
|
static int fsdev_addr_lvl = 0;
|
|
static int running;
|
|
|
|
static uint8_t iord_stdio()
|
|
{
|
|
int c = getchar();
|
|
if (c == EOF) {
|
|
running = 0;
|
|
}
|
|
return (uint8_t)c;
|
|
}
|
|
|
|
static uint8_t iord_fsdata()
|
|
{
|
|
if (fsdev_addr_lvl != 0) {
|
|
fprintf(stderr, "Reading FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
|
|
return 0;
|
|
}
|
|
if (fsdev_ptr < MAX_FSDEV_SIZE) {
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Reading FSDEV at offset %d\n", fsdev_ptr);
|
|
#endif
|
|
return fsdev[fsdev_ptr];
|
|
} else {
|
|
fprintf(stderr, "Out of bounds FSDEV read at %d\n", fsdev_ptr);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static uint8_t iord_fsaddr()
|
|
{
|
|
if (fsdev_addr_lvl != 0) {
|
|
return 3;
|
|
} else if (fsdev_ptr >= MAX_FSDEV_SIZE) {
|
|
fprintf(stderr, "Out of bounds FSDEV addr request at %d / %d\n", fsdev_ptr, MAX_FSDEV_SIZE);
|
|
return 2;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void iowr_stdio(uint8_t val)
|
|
{
|
|
if (val == 0x04) { // CTRL+D
|
|
running = 0;
|
|
} else {
|
|
putchar(val);
|
|
}
|
|
}
|
|
|
|
static void iowr_fsdata(uint8_t val)
|
|
{
|
|
if (fsdev_addr_lvl != 0) {
|
|
fprintf(stderr, "Writing to FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
|
|
return;
|
|
}
|
|
if (fsdev_ptr < MAX_FSDEV_SIZE) {
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Writing to FSDEV (%d)\n", fsdev_ptr);
|
|
#endif
|
|
fsdev[fsdev_ptr] = val;
|
|
} else {
|
|
fprintf(stderr, "Out of bounds FSDEV write at %d\n", fsdev_ptr);
|
|
}
|
|
}
|
|
|
|
static void iowr_fsaddr(uint8_t val)
|
|
{
|
|
if (fsdev_addr_lvl == 0) {
|
|
fsdev_ptr = val << 16;
|
|
fsdev_addr_lvl = 1;
|
|
} else if (fsdev_addr_lvl == 1) {
|
|
fsdev_ptr |= val << 8;
|
|
fsdev_addr_lvl = 2;
|
|
} else {
|
|
fsdev_ptr |= val;
|
|
fsdev_addr_lvl = 0;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
FILE *fp = NULL;
|
|
while (1) {
|
|
int c = getopt(argc, argv, "f:");
|
|
if (c < 0) {
|
|
break;
|
|
}
|
|
switch (c) {
|
|
case 'f':
|
|
fp = fopen(optarg, "r");
|
|
if (fp == NULL) {
|
|
fprintf(stderr, "Can't open %s\n", optarg);
|
|
return 1;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Usage: shell [-f fsdev]\n");
|
|
return 1;
|
|
}
|
|
}
|
|
// Setup fs blockdev
|
|
if (fp == NULL) {
|
|
fp = popen("../cfspack/cfspack cfsin", "r");
|
|
if (fp == NULL) {
|
|
fprintf(stderr, "Can't initialize filesystem. Leaving blank.\n");
|
|
}
|
|
}
|
|
if (fp != NULL) {
|
|
fprintf(stderr, "Initializing filesystem\n");
|
|
int i = 0;
|
|
int c;
|
|
while ((c = fgetc(fp)) != EOF && i < MAX_FSDEV_SIZE) {
|
|
fsdev[i++] = c & 0xff;
|
|
}
|
|
if (i == MAX_FSDEV_SIZE) {
|
|
fprintf(stderr, "Filesytem image too large.\n");
|
|
return 1;
|
|
}
|
|
pclose(fp);
|
|
}
|
|
|
|
bool tty = isatty(fileno(stdin));
|
|
struct termios termInfo;
|
|
if (tty) {
|
|
// Turn echo off: the shell takes care of its own echoing.
|
|
if (tcgetattr(0, &termInfo) == -1) {
|
|
printf("Can't setup terminal.\n");
|
|
return 1;
|
|
}
|
|
termInfo.c_lflag &= ~ECHO;
|
|
termInfo.c_lflag &= ~ICANON;
|
|
tcsetattr(0, TCSAFLUSH, &termInfo);
|
|
}
|
|
|
|
|
|
Machine *m = emul_init();
|
|
m->ramstart = RAMSTART;
|
|
m->iord[STDIO_PORT] = iord_stdio;
|
|
m->iord[FS_DATA_PORT] = iord_fsdata;
|
|
m->iord[FS_ADDR_PORT] = iord_fsaddr;
|
|
m->iowr[STDIO_PORT] = iowr_stdio;
|
|
m->iowr[FS_DATA_PORT] = iowr_fsdata;
|
|
m->iowr[FS_ADDR_PORT] = iowr_fsaddr;
|
|
// initialize memory
|
|
for (int i=0; i<sizeof(KERNEL); i++) {
|
|
m->mem[i] = KERNEL[i];
|
|
}
|
|
// Run!
|
|
running = 1;
|
|
|
|
while (running && emul_step());
|
|
|
|
if (tty) {
|
|
printf("Done!\n");
|
|
termInfo.c_lflag |= ECHO;
|
|
termInfo.c_lflag |= ICANON;
|
|
tcsetattr(0, TCSAFLUSH, &termInfo);
|
|
emul_printdebug();
|
|
}
|
|
return 0;
|
|
}
|