diff --git a/.gitignore b/.gitignore index e2b79f2..5a234ce 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ *.asm *.map *.lib +.vscode diff --git a/README.md b/README.md index 8a841d6..e3f1e50 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,20 @@ Handheld PC based on the original 6502 CPU that fits into a pocket. # Simulator -Pocket265 simulator is there to make FW developement easier, but it can be also used to get a feel for the computer usage. I2C memory and UART are not simulated. It uses [simak65](https://github.com/agkaminski/simak65) simulator code. +Pocket265 simulator is there to make FW developement easier, but it can be also used to get a feel for the computer usage. Only UART is not simulated. It uses [simak65](https://github.com/agkaminski/simak65) simulator code. + +## Usage + +./pocket265-sim [OPTIONS] + +where options are: +- -c: path to the computer ROM (obligatory), +- -f: simulated CPU frequency, from 0 (max speed, no throttling) to 20000000 (Hz), +- -r: simulated RAM size in bytes, +- -e: simulated EEPROM file (will be updated after end of the simulation, if write is performed!), +- -h: usage. + +## Builing To run the simulation first build and install [simak65](https://github.com/agkaminski/simak65):
@@ -51,6 +64,8 @@ then build the simulator: $ (cd sim && make all)+## Running the simulation + In the `sim/` directory is a script that starts the simulator with memory initialized with Pocket265 firmware - `runsim.sh`. To run the simulator:
$ cd sim diff --git a/sim/Makefile b/sim/Makefile index 8615f22..7628c57 100644 --- a/sim/Makefile +++ b/sim/Makefile @@ -3,7 +3,7 @@ CFLAGS := -g -Wall -Werror -Wextra -Wno-type-limits -O2 -ansi -std=gnu99 DEBUG := -DNDEBUG OUT = pocket265-sim -OBJ = pocket265.o +OBJ = pocket265.o i2c.o LIB = simak65 %.o: %.c diff --git a/sim/i2c.c b/sim/i2c.c new file mode 100644 index 0000000..ccf5149 --- /dev/null +++ b/sim/i2c.c @@ -0,0 +1,146 @@ +/* 24xx I2C memory simulator + * Copyright A.K. 2018, 2023 + */ + +#include+#include +#include "i2c.h" + +void i2c_step(struct i2c_ctx *ctx, const struct i2c_bus *in, struct i2c_bus *out) +{ + *out = ctx->prevout; + + if (in->scl) { + if (ctx->previn.sda && !in->sda) { + /* Start condition */ + ctx->state = i2c_dev_addr; + ctx->regbit = 0; + } + else if (!ctx->previn.sda && in->sda) { + /* Stop condtion */ + ctx->state = i2c_idle; + } + } + + if (in->scl && !ctx->previn.scl) { + /* Rising edge */ + switch (ctx->state) { + case i2c_dev_addr: + ctx->reg = (ctx->reg << 1) | !!in->sda; + if (++ctx->regbit > 7) { + ctx->regbit = 0; + if (ctx->devaddr == (ctx->reg >> 1)) { + if (ctx->reg & 1) + ctx->next = i2c_read; + else + ctx->next = i2c_addr; + ctx->state = i2c_ack; + } + else { + ctx->state = i2c_idle; + } + } + break; + + case i2c_addr: + ctx->address = (ctx->address << 1) | !!in->sda; + ++ctx->regbit; + if (ctx->regbit == 8) { + ctx->state = i2c_ack; + ctx->next = i2c_addr; + } + else if (ctx->regbit > 15) { + ctx->regbit = 0; + ctx->next = i2c_write; + ctx->state = i2c_ack; + } + break; + + case i2c_write: + ctx->reg = (ctx->reg << 1) | !!in->sda; + if (++ctx->regbit > 7) { + ctx->regbit = 0; + ctx->mem[ctx->address % ctx->memsz] = ctx->reg; + ++ctx->address; + ctx->next = i2c_write; + ctx->state = i2c_ack; + } + break; + + case i2c_read: + ++ctx->regbit; + if (ctx->regbit == 9) { + if (in->sda) { + ctx->state = i2c_idle; + ctx->regbit = 0; + } + else { + ctx->regbit = 0; + ++ctx->address; + } + } + break; + + case i2c_ack: + ctx->state = ctx->next; + break; + + default: + break; + } + } + else if (!in->scl && ctx->previn.scl) { + /* Falling ege */ + switch (ctx->state) { + case i2c_ack: + out->sda = 0; + break; + + case i2c_read: + if (ctx->regbit == 0) + ctx->reg = ctx->mem[ctx->address % ctx->memsz]; + + if (ctx->regbit <= 7) { + out->sda = !!(ctx->reg & 0x80); + ctx->reg <<= 1; + } + else { + out->sda = 1; + } + break; + + default: + out->sda = 1; + break; + } + } + + ctx->previn = *in; + ctx->prevout = *out; +} + +void i2c_destroy(struct i2c_ctx *ctx) +{ + free(ctx->mem); + ctx->mem = NULL; +} + +int i2c_init(struct i2c_ctx *ctx, size_t memsz, uint8_t devaddr) +{ + ctx->previn = (struct i2c_bus){ .sda = 1, .scl = 1 }; + ctx->prevout = (struct i2c_bus){ .sda = 1, .scl = 1 }; + + ctx->state = i2c_idle; + ctx->address = 0; + ctx->reg = 0; + ctx->regbit = 0; + ctx->devaddr = devaddr; + + ctx->mem = malloc(memsz); + if (ctx->mem == NULL) { + return -1; + } + ctx->memsz = memsz; + + return 0; +} diff --git a/sim/i2c.h b/sim/i2c.h new file mode 100644 index 0000000..51a182f --- /dev/null +++ b/sim/i2c.h @@ -0,0 +1,49 @@ +/* 24xx I2C memory simulator + * Copyright A.K. 2018, 2023 + */ + +#ifndef I2C_H_ +#define I2C_H_ + +#include +#include + +struct i2c_bus { + int sda; + int scl; +}; + +enum i2c_state { + i2c_idle, + i2c_dev_addr, + i2c_addr, + i2c_read, + i2c_write, + i2c_ack +}; + +struct i2c_ctx { + struct i2c_bus previn; + struct i2c_bus prevout; + + uint8_t *mem; + size_t memsz; + + uint16_t address; + + uint8_t devaddr; + + uint8_t reg; + uint8_t regbit; + + enum i2c_state state; + enum i2c_state next; +}; + +void i2c_step(struct i2c_ctx *ctx, const struct i2c_bus *in, struct i2c_bus *out); + +void i2c_destroy(struct i2c_ctx *ctx); + +int i2c_init(struct i2c_ctx *ctx, size_t memsz, uint8_t devaddr); + +#endif diff --git a/sim/pocket265.c b/sim/pocket265.c index ac41c6d..3dc0479 100644 --- a/sim/pocket265.c +++ b/sim/pocket265.c @@ -17,6 +17,8 @@ #include #include +#include "i2c.h" + #define RAM_START 0x0000u #define RAM_SIZE 0x2000u @@ -32,16 +34,22 @@ #define NMI_ACK_ADDR 0xc800u #define GPIO_ADDR 0xcc00u +#define EEPROM_SIZE (32 * 1024) + +/* Memory */ static uint8_t *g_ram; static size_t g_ramsz = RAM_SIZE; static uint8_t g_rom[ROM_SIZE]; -static uint8_t g_gpio_out; +/* I/O */ static char g_screen[12]; static int g_screen_update = 1; static uint8_t g_key_row[6]; static int g_nmi = 1; static volatile int g_exit = 0; +static struct i2c_bus i2c_in; +static struct i2c_bus i2c_out; +static int g_romwp = 1; static void pocket265_nmi_ack(void) { @@ -62,6 +70,8 @@ static uint8_t pocket265_keyread(uint16_t addr) static uint8_t pocket265_ioread(uint16_t addr) { + uint8_t gpio = 0; + switch (addr & PERIPH_ADDR_MASK) { case KEYBOARD_ADDR: return pocket265_keyread(addr); @@ -69,7 +79,10 @@ static uint8_t pocket265_ioread(uint16_t addr) pocket265_nmi_ack(); break; case GPIO_ADDR: - return g_gpio_out & 0xf; + gpio |= !!(i2c_out.sda && i2c_in.sda) << 1; /* SDA */ + gpio |= !!(i2c_out.scl && i2c_in.scl) << 2; /* SCL */ + gpio |= !!g_romwp << 3; /* ROM_WP */ + return gpio |= 0xf1; /* Unused */ } return 0xff; @@ -88,16 +101,13 @@ static void pocket265_iowrite(uint16_t addr, uint8_t byte) pocket265_nmi_ack(); break; case GPIO_ADDR: - g_gpio_out = byte & 0xf; + i2c_in.sda = !!(byte & (1 << 1)); + i2c_in.scl = !!(byte & (1 << 2)); + g_romwp = !!(byte & (1 << 3)); break; } } -static int pocket265_is_rom_wp(void) -{ - return (g_gpio_out & (1 << 3)) ? 1 : 0; -} - static uint8_t pocket265_read(uint16_t addr) { if (addr >= RAM_START && addr < (RAM_START + g_ramsz)) @@ -114,7 +124,7 @@ static void pocket265_write(uint16_t addr, uint8_t byte) { if (addr >= RAM_START && addr < (RAM_START + g_ramsz)) g_ram[addr - RAM_START] = byte; - else if (addr >= ROM_START && addr < (ROM_START + ROM_SIZE) && !pocket265_is_rom_wp()) + else if (addr >= ROM_START && addr < (ROM_START + ROM_SIZE) && !g_romwp) g_rom[addr - ROM_START] = byte; else if (addr >= PERIPH_ADDR_START && addr <= PERIPH_ADDR_END) pocket265_iowrite(addr, byte); @@ -188,20 +198,20 @@ static useconds_t gettime_us(void) static void usage(const char *p) { - printf("usage: %s -c [-r ] [-f ]\n", p); + printf("usage: %s -c [-r ] [ -e ] [-f ]\n", p); } static void sighandler(int n) { - (void)n; + (void)n; - g_exit = 1; + g_exit = 1; } int main(int argc, char *argv[]) { int c; - FILE *firmware = NULL; + FILE *firmware = NULL, *eeprom = NULL; struct simak65_cpustate cpu; const struct simak65_bus ops = { .read = pocket265_read, .write = pocket265_write }; unsigned int cycles = 0; @@ -209,8 +219,9 @@ int main(int argc, char *argv[]) useconds_t prev, now, prev_nmi = 0, prev_ui = 0, ns_per_cycle = 0; int ns_error = 0; static struct termios oldt, newt; + struct i2c_ctx i2c; - while ((c = getopt(argc, argv, "c:f:r:h")) != -1) { + while ((c = getopt(argc, argv, "c:f:r:e:h")) != -1) { switch (c) { case 'c': if ((firmware = fopen(optarg, "r")) == NULL) { @@ -233,6 +244,12 @@ int main(int argc, char *argv[]) return 1; } break; + case 'e': + if ((eeprom = fopen(optarg, "r+")) == NULL) { + fprintf(stderr, "can't open EEPROM file %s\n", optarg); + return 1; + } + break; case 'h': usage(argv[0]); return 0; @@ -263,6 +280,14 @@ int main(int argc, char *argv[]) return 1; } + if (i2c_init(&i2c, EEPROM_SIZE, 0x50) < 0) { + fprintf(stderr, "i2c init failed"); + return 1; + } + + if (eeprom != NULL) + (void)!fread(i2c.mem, 1, i2c.memsz, eeprom); + signal(SIGINT, sighandler); tcgetattr(STDIN_FILENO, &oldt); @@ -280,6 +305,7 @@ int main(int argc, char *argv[]) while (!g_exit) { simak65_step(&cpu, &cycles); + i2c_step(&i2c, &i2c_in, &i2c_out); now = gettime_us(); @@ -319,6 +345,14 @@ int main(int argc, char *argv[]) tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + if (eeprom != NULL) { + rewind(eeprom); + (void)!fwrite(i2c.mem, 1, i2c.memsz, eeprom); + fflush(eeprom); + fclose(eeprom); + } + + i2c_destroy(&i2c); free(g_ram); return 0;