summaryrefslogtreecommitdiff
path: root/s51
diff options
context:
space:
mode:
Diffstat (limited to 's51')
-rw-r--r--s51/.gitignore1
-rw-r--r--s51/Makefile.am12
-rw-r--r--s51/commands60
-rw-r--r--s51/s51-command.c654
-rw-r--r--s51/s51-main.c239
-rw-r--r--s51/s51-parse.c241
-rw-r--r--s51/s51.1211
-rw-r--r--s51/s51.h123
8 files changed, 1541 insertions, 0 deletions
diff --git a/s51/.gitignore b/s51/.gitignore
new file mode 100644
index 00000000..cb909cf0
--- /dev/null
+++ b/s51/.gitignore
@@ -0,0 +1 @@
+s51
diff --git a/s51/Makefile.am b/s51/Makefile.am
new file mode 100644
index 00000000..4778d66b
--- /dev/null
+++ b/s51/Makefile.am
@@ -0,0 +1,12 @@
+bin_PROGRAMS=s51
+
+AM_CFLAGS=-I$(top_srcdir)/lib $(LIBUSB_CFLAGS)
+S51_LIBS=../lib/libcc.a -lreadline
+
+man_MANS = s51.1
+
+s51_DEPENDENCIES = $(S51_LIBS)
+
+s51_LDADD=$(S51_LIBS) $(LIBUSB_LIBS)
+
+s51_SOURCES = s51-parse.c s51-command.c s51-main.c
diff --git a/s51/commands b/s51/commands
new file mode 100644
index 00000000..aba65cd0
--- /dev/null
+++ b/s51/commands
@@ -0,0 +1,60 @@
+Listens on port 9756 for a command stream.
+
+Dump commands:
+ di <start> <end> - dump imem
+ ds <start> <end> - dump sprs
+ dx <start> <end> - dump xaddr
+
+ Returns a string of hex pairs, each preceded by a space,
+ with 8 pairs per line
+
+Memory access commands:
+ set mem <prefix> <start> <end>
+ dump <prefix> <start> <end>
+
+ <prefix> is one of:
+
+ xram - external ram or external stack
+ rom - code space
+ iram - internal ram or stack
+ sfr - special function register
+
+
+ dump <addr>
+ set bit <addr>
+
+ bit addressable space
+
+Set PC:
+
+ pc <addr>
+
+ Sets PC to specified address
+
+ pc
+
+ Returns current PC
+
+Breakpoints
+
+ break <addr>
+ clear <addr>
+
+Load a file
+
+ file "<filename>"
+
+Execution control:
+
+ run <start> - run starting at <start>
+ run <start> <stop> - set temporary bp at <stop>
+ run - continue
+ next - step over calls(?)
+ step - step one instruction
+
+ reset - reset the simulator
+ res - synonym?
+
+Error messages:
+
+ start with "Error:"
diff --git a/s51/s51-command.c b/s51/s51-command.c
new file mode 100644
index 00000000..4f803060
--- /dev/null
+++ b/s51/s51-command.c
@@ -0,0 +1,654 @@
+/*
+ * Copyright © 2008 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "s51.h"
+
+static uint16_t start_address;
+
+static enum command_result
+parse_int(char *value, int *result)
+{
+ char *endptr;
+
+ *result = strtol(value, &endptr, 0);
+ if (endptr == value)
+ return command_syntax;
+ return command_success;
+}
+
+static enum command_result
+parse_uint16(char *value, uint16_t *uint16)
+{
+ int v;
+ enum command_result result;
+
+ result = parse_int(value, &v);
+ if (result != command_success)
+ return command_error;
+ if (v < 0 || v > 0xffff)
+ return command_error;
+ *uint16 = v;
+ return command_success;
+}
+
+static enum command_result
+parse_uint8(char *value, uint8_t *uint8)
+{
+ int v;
+ enum command_result result;
+
+ result = parse_int(value, &v);
+ if (result != command_success)
+ return command_error;
+ if (v < 0 || v > 0xff)
+ return command_error;
+ *uint8 = v;
+ return command_success;
+}
+
+enum command_result
+command_quit (int argc, char **argv)
+{
+ ccdbg_reset(s51_dbg);
+ exit(0);
+ return command_error;
+}
+
+static void
+dump_bytes(uint8_t *memory, int length, uint16_t start, char *format)
+{
+ int group, i;
+
+ for (group = 0; group < length; group += 8) {
+ s51_printf(format, start + group);
+ for (i = group; i < length && i < group + 8; i++)
+ s51_printf("%02x ", memory[i]);
+ for (; i < group + 8; i++)
+ s51_printf(" ");
+ for (i = group; i < length && i < group + 8; i++) {
+ if (isascii(memory[i]) && isprint(memory[i]))
+ s51_printf("%c", memory[i]);
+ else
+ s51_printf(".");
+ }
+ s51_printf("\n");
+ }
+}
+
+enum command_result
+command_di (int argc, char **argv)
+{
+ uint16_t start, end;
+ uint8_t memory[65536];
+ uint8_t status;
+ int length;
+
+ if (argc != 3)
+ return command_error;
+ if (parse_uint16(argv[1], &start) != command_success)
+ return command_error;
+ if (parse_uint16(argv[2], &end) != command_success)
+ return command_error;
+ length = (int) end - (int) start + 1;
+ status = ccdbg_read_memory(s51_dbg, start + 0xff00, memory, length);
+ dump_bytes(memory, length, start, "0x%02x ");
+ return command_success;
+}
+
+enum command_result
+command_ds (int argc, char **argv)
+{
+ uint8_t start, end;
+ uint8_t memory[0x100];
+ uint8_t status;
+ int length;
+
+ if (argc != 3)
+ return command_error;
+ if (parse_uint8(argv[1], &start) != command_success)
+ return command_error;
+ if (parse_uint8(argv[2], &end) != command_success)
+ return command_error;
+ length = (int) end - (int) start + 1;
+ status = ccdbg_read_sfr(s51_dbg, start, memory, length);
+ dump_bytes(memory, length, start, "0x%02x ");
+ return command_success;
+}
+
+enum command_result
+command_dx (int argc, char **argv)
+{
+ uint16_t start, end;
+ uint8_t memory[65536];
+ uint8_t status;
+ int length;
+
+ if (argc != 3)
+ return command_error;
+ if (parse_uint16(argv[1], &start) != command_success)
+ return command_error;
+ if (parse_uint16(argv[2], &end) != command_success)
+ return command_error;
+ length = (int) end - (int) start + 1;
+ status = ccdbg_read_memory(s51_dbg, start, memory, length);
+ dump_bytes(memory, length, start, "0x%04x ");
+ return command_success;
+}
+
+enum command_result
+command_set (int argc, char **argv)
+{
+ uint16_t address;
+ uint8_t *data;
+ int len = argc - 3;
+ int i;
+ enum command_result ret = command_success;
+
+ if (len < 0)
+ return command_error;
+ if (parse_uint16(argv[2], &address) != command_success)
+ return command_error;
+ if (len == 0)
+ return command_success;
+ data = malloc(len);
+ if (!data)
+ return command_error;
+ for (i = 0; i < len; i++)
+ if (parse_uint8(argv[i+3], &data[i]) != command_success)
+ return command_error;
+
+ if (strcmp(argv[1], "xram") == 0) {
+ ccdbg_write_memory(s51_dbg, address, data, len);
+ } else if (strcmp(argv[1], "iram") == 0) {
+ ccdbg_write_memory(s51_dbg, address + 0xff00, data, len);
+ } else if (strcmp(argv[1], "sfr") == 0) {
+ ccdbg_write_sfr(s51_dbg, (uint8_t) address, data, len);
+ } else
+ ret = command_error;
+ free(data);
+ return ret;
+}
+
+enum command_result
+command_dump (int argc, char **argv)
+{
+ if (argv[1]) {
+ if (strcmp(argv[1], "rom") == 0 ||
+ strcmp(argv[1], "xram") == 0)
+ return command_dx(argc-1, argv+1);
+ if (strcmp(argv[1], "iram") == 0)
+ return command_di(argc-1, argv+1);
+ if (strcmp(argv[1], "sfr") == 0)
+ return command_ds(argc-1, argv+1);
+ }
+ return command_error;
+}
+
+enum command_result
+command_file (int argc, char **argv)
+{
+ struct hex_file *hex;
+ struct hex_image *image;
+ FILE *file;
+
+ if (argc != 2)
+ return command_error;
+ file = fopen (argv[1], "r");
+ if (!file)
+ return command_error;
+ hex = ccdbg_hex_file_read(file, argv[1]);
+ fclose(file);
+ if (!hex)
+ return command_error;
+ if (hex->nrecord == 0) {
+ ccdbg_hex_file_free(hex);
+ return command_error;
+ }
+ image = ccdbg_hex_image_create(hex);
+ ccdbg_hex_file_free(hex);
+ start_address = image->address;
+ ccdbg_set_rom(s51_dbg, image);
+ return command_success;
+}
+
+enum command_result
+command_pc (int argc, char **argv)
+{
+ uint16_t pc;
+ if (argv[1]) {
+ enum command_result result;
+ result = parse_uint16(argv[1], &pc);
+ if (result != command_success)
+ return result;
+ ccdbg_set_pc(s51_dbg, pc);
+ } else {
+ pc = ccdbg_get_pc(s51_dbg);
+ s51_printf(" 0x%04x 00\n", pc);
+ }
+ return command_success;
+}
+
+struct cc_break {
+ int enabled;
+ int temporary;
+ uint16_t address;
+};
+
+#define CC_NUM_BREAKPOINTS 4
+
+static struct cc_break breakpoints[CC_NUM_BREAKPOINTS];
+
+static void
+disable_breakpoint(int b)
+{
+ uint8_t status;
+
+ status = ccdbg_set_hw_brkpnt(s51_dbg, b, 0, breakpoints[b].address);
+ if (status != 0x00 && status != 0xff)
+ s51_printf("disable_breakpoint status 0x%02x\n", status);
+}
+
+static void
+enable_breakpoint(int b)
+{
+ uint8_t status;
+
+ status = ccdbg_set_hw_brkpnt(s51_dbg, b, 1, breakpoints[b].address);
+ if (status != 0xff)
+ s51_printf("enable_breakpoint status 0x%02x\n", status);
+}
+
+static void
+enable_breakpoints(void)
+{
+ int b;
+ for (b = 0; b < CC_NUM_BREAKPOINTS; b++)
+ if (breakpoints[b].enabled)
+ enable_breakpoint(b);
+}
+
+enum command_result
+set_breakpoint(uint16_t address, int temporary)
+{
+ int b;
+ uint8_t status;
+ for (b = 0; b < CC_NUM_BREAKPOINTS; b++) {
+ if (breakpoints[b].enabled == 0)
+ break;
+ if (breakpoints[b].address == address)
+ break;
+ }
+ if (b == CC_NUM_BREAKPOINTS) {
+ s51_printf("Error: too many breakpoints requested\n");
+ return command_success;
+ }
+ if (breakpoints[b].enabled == 0) {
+ breakpoints[b].address = address;
+ enable_breakpoint(b);
+ }
+ ++breakpoints[b].enabled;
+ s51_printf("Breakpoint %d at 0x%04x\n", b, address);
+ breakpoints[b].temporary += temporary;
+ return command_success;
+}
+
+enum command_result
+clear_breakpoint(uint16_t address, int temporary)
+{
+ int b;
+ uint8_t status;
+
+ for (b = 0; b < CC_NUM_BREAKPOINTS; b++) {
+ if (breakpoints[b].enabled != 0 &&
+ ((breakpoints[b].temporary != 0) == (temporary != 0)) &&
+ breakpoints[b].address == address)
+ break;
+ }
+ if (b == CC_NUM_BREAKPOINTS) {
+ s51_printf("Error: no matching breakpoint found\n");
+ return command_success;
+ }
+ --breakpoints[b].enabled;
+ breakpoints[b].temporary -= temporary;
+ if (breakpoints[b].enabled == 0) {
+ disable_breakpoint(b);
+ breakpoints[b].address = -1;
+ }
+ return command_success;
+}
+
+
+int
+find_breakpoint(uint16_t address)
+{
+ int b;
+
+ for (b = 0; b < CC_NUM_BREAKPOINTS; b++)
+ if (breakpoints[b].enabled && breakpoints[b].address == address)
+ break;
+ if (b == CC_NUM_BREAKPOINTS)
+ return -1;
+ return b;
+}
+
+enum command_result
+command_break (int argc, char **argv)
+{
+ int b;
+ uint16_t address;
+ enum command_result result;
+
+ if (argc == 1) {
+ for (b = 0; b < CC_NUM_BREAKPOINTS; b++)
+ if (breakpoints[b].enabled)
+ s51_printf("Breakpoint %d 0x%04x\n",
+ b, breakpoints[b].address);
+ return command_success;
+ }
+ if (argc != 2)
+ return command_error;
+ result = parse_uint16(argv[1], &address);
+ if (result != command_success)
+ return result;
+
+ return set_breakpoint(address, 0);
+}
+
+enum command_result
+command_clear (int argc, char **argv)
+{
+ int b;
+ uint16_t address;
+ enum command_result result;
+
+ if (argc != 2)
+ return command_error;
+ result = parse_uint16(argv[1], &address);
+ if (result != command_success)
+ return result;
+ return clear_breakpoint(address, 0);
+}
+
+void
+cc_stopped(uint8_t status)
+{
+ uint16_t pc;
+ int b;
+ int code;
+ char *reason;
+
+ pc = ccdbg_get_pc(s51_dbg);
+ if (status & CC_STATUS_CPU_HALTED) {
+ if ((status & CC_STATUS_HALT_STATUS) != 0) {
+ pc = pc - 1;
+ code = 104;
+ reason = "Breakpoint";
+ b = find_breakpoint(pc);
+ if (b != -1 && breakpoints[b].temporary)
+ clear_breakpoint(pc, 1);
+ ccdbg_set_pc(s51_dbg, pc);
+ } else {
+ code = 105;
+ reason = "Interrupt";
+ }
+ s51_printf("Stop at 0x%04x: (%d) %s\n",
+ pc, code, reason);
+ }
+}
+
+uint8_t
+cc_step(uint16_t pc)
+{
+ int b;
+ uint8_t status;
+
+ b = find_breakpoint(pc);
+ if (b != -1)
+ disable_breakpoint(b);
+ status = ccdbg_step_instr(s51_dbg);
+ if (b != -1)
+ enable_breakpoint(b);
+ return status;
+}
+
+enum command_result
+command_run (int argc, char **argv)
+{
+ uint16_t start, end;
+ enum command_result result;
+ uint16_t pc;
+ uint8_t status;
+ int b;
+
+ if (argv[1]) {
+ result = parse_uint16(argv[1], &start);
+ if (result != command_success)
+ return result;
+ if (argv[2]) {
+ result = parse_uint16(argv[2], &end);
+ if (result != command_success)
+ return result;
+ }
+ if (start_address && start == 0) {
+ start = start_address;
+ s51_printf("Starting at 0x%04x\n", start);
+ }
+ ccdbg_set_pc(s51_dbg, start);
+ }
+ else
+ start = ccdbg_get_pc(s51_dbg);
+ s51_printf("Resume at 0x%04x\n", start);
+ pc = start;
+ b = find_breakpoint(pc);
+ if (b != -1) {
+ cc_step(pc);
+ pc = ccdbg_get_pc(s51_dbg);
+ if (find_breakpoint(pc) != -1) {
+ status = ccdbg_read_status(s51_dbg);
+ cc_stopped(status);
+ return command_success;
+ }
+ }
+ ccdbg_resume(s51_dbg);
+ result = cc_wait();
+ return result;
+}
+
+enum command_result
+command_next (int argc, char **argv)
+{
+ return command_step(argc, argv);
+}
+
+enum command_result
+command_step (int argc, char **argv)
+{
+ uint16_t pc;
+ uint8_t opcode;
+ uint8_t a;
+
+ a = cc_step(ccdbg_get_pc(s51_dbg));
+ s51_printf(" ACC= 0x%02x\n", a);
+ pc = ccdbg_get_pc(s51_dbg);
+ ccdbg_read_memory(s51_dbg, pc, &opcode, 1);
+ s51_printf(" ? 0x%04x %02x\n", pc, opcode);
+ return command_success;
+}
+
+enum command_result
+command_load (int argc, char **argv)
+{
+ char *filename = argv[1];
+ FILE *file;
+ struct hex_file *hex;
+ struct hex_image *image;
+
+ if (!filename)
+ return command_error;
+ file = fopen(filename, "r");
+ if (!file) {
+ perror(filename);
+ return command_error;
+ }
+ hex = ccdbg_hex_file_read(file, filename);
+ fclose(file);
+ if (!hex) {
+ return command_error;
+ }
+ image = ccdbg_hex_image_create(hex);
+ ccdbg_hex_file_free(hex);
+ if (!image) {
+ fprintf(stderr, "image create failed\n");
+ return command_error;
+ }
+ if (image->address >= 0xf000) {
+ printf("Loading %d bytes to RAM at 0x%04x\n",
+ image->length, image->address);
+ ccdbg_write_hex_image(s51_dbg, image, 0);
+ } else {
+ fprintf(stderr, "Can only load to RAM\n");
+ }
+ ccdbg_hex_image_free(image);
+ return command_success;
+}
+
+enum command_result
+command_halt (int argc, char **argv)
+{
+ uint16_t pc;
+ ccdbg_halt(s51_dbg);
+ pc = ccdbg_get_pc(s51_dbg);
+ s51_printf("Halted at 0x%04x\n", pc);
+ return command_success;
+}
+
+enum command_result
+command_stop (int argc, char **argv)
+{
+ return command_success;
+}
+
+enum command_result
+command_reset (int argc, char **argv)
+{
+ ccdbg_debug_mode(s51_dbg);
+ ccdbg_halt(s51_dbg);
+ enable_breakpoints();
+ return command_success;
+}
+
+enum command_result
+command_status(int argc, char **argv)
+{
+ uint8_t status;
+
+ status = ccdbg_read_status(s51_dbg);
+ if ((status & CC_STATUS_CHIP_ERASE_DONE) == 0)
+ s51_printf("\tChip erase in progress\n");
+ if (status & CC_STATUS_PCON_IDLE)
+ s51_printf("\tCPU is idle (clock gated)\n");
+ if (status & CC_STATUS_CPU_HALTED)
+ s51_printf("\tCPU halted\n");
+ else
+ s51_printf("\tCPU running\n");
+ if ((status & CC_STATUS_POWER_MODE_0) == 0)
+ s51_printf("\tPower Mode 1-3 selected\n");
+ if (status & CC_STATUS_HALT_STATUS)
+ s51_printf("\tHalted by software or hw breakpoint\n");
+ else
+ s51_printf("\tHalted by debug command\n");
+ if (status & CC_STATUS_DEBUG_LOCKED)
+ s51_printf("\tDebug interface is locked\n");
+ if ((status & CC_STATUS_OSCILLATOR_STABLE) == 0)
+ s51_printf("\tOscillators are not stable\n");
+ if (status & CC_STATUS_STACK_OVERFLOW)
+ s51_printf("\tStack overflow\n");
+ return command_success;
+}
+
+static enum command_result
+info_breakpoints(int argc, char **argv)
+{
+ int b;
+ uint16_t address;
+ enum command_result result;
+
+ if (argc == 1) {
+ s51_printf("Num Type Disp Hit Cnt Address What\n");
+ for (b = 0; b < CC_NUM_BREAKPOINTS; b++)
+ if (breakpoints[b].enabled) {
+ s51_printf("%-3d fetch %s 1 1 0x%04x uc::disass() unimplemented\n",
+ b,
+ breakpoints[b].temporary ? "del " : "keep",
+ breakpoints[b].address);
+ }
+ return command_success;
+ }
+
+}
+
+static enum command_result
+info_help(int argc, char **argv);
+
+static struct command_function infos[] = {
+ { "breakpoints", "b", info_breakpoints, "[b]reakpoints",
+ "List current breakpoints\n" },
+ { "help", "?", info_help, "help",
+ "Print this list\n" },
+
+ { NULL, NULL, NULL, NULL, NULL },
+};
+
+static enum command_result
+info_help(int argc, char **argv)
+{
+ return command_function_help(infos, argc, argv);
+}
+
+enum command_result
+command_info(int argc, char **argv)
+{
+ struct command_function *func;
+
+ if (argc < 2)
+ return command_error;
+ func = command_string_to_function(infos, argv[1]);
+ if (!func)
+ return command_syntax;
+ return (*func->func)(argc-1, argv+1);
+}
+
+enum command_result
+cc_wait(void)
+{
+ for(;;) {
+ uint8_t status;
+ status = ccdbg_read_status(s51_dbg);
+ if (status & CC_STATUS_CPU_HALTED) {
+ cc_stopped(status);
+ return command_success;
+ }
+ if (s51_interrupted || s51_check_input()) {
+
+ ccdbg_halt(s51_dbg);
+ status = ccdbg_read_status(s51_dbg);
+ cc_stopped(status);
+ return command_interrupt;
+ }
+ }
+}
diff --git a/s51/s51-main.c b/s51/s51-main.c
new file mode 100644
index 00000000..4dbd4c60
--- /dev/null
+++ b/s51/s51-main.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright © 2008 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "s51.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <poll.h>
+
+static int s51_port = 0;
+static char *cpu = "8051";
+static double freq = 11059200;
+char *s51_prompt = "> ";
+struct ccdbg *s51_dbg;
+int s51_interrupted = 0;
+int s51_monitor = 0;
+
+static FILE *s51_input;
+static FILE *s51_output;
+
+static void
+usage(void)
+{
+ fprintf(stderr, "You're doing it wrong.\n");
+ exit(1);
+}
+
+void s51_sigint()
+{
+ s51_interrupted = 1;
+}
+
+int
+main(int argc, char **argv)
+{
+ int flags, opt;
+ char *endptr;
+ struct sigvec vec, ovec;
+
+ while ((opt = getopt(argc, argv, "PVvHhmt:X:c:r:Z:s:S:p:")) != -1) {
+ switch (opt) {
+ case 't':
+ cpu = optarg;
+ break;
+ case 'X':
+ freq = strtod(optarg, &endptr);
+ if (endptr == optarg)
+ usage();
+ if (endptr[0] != '\0') {
+ if (!strcmp(endptr, "k"))
+ freq *= 1000;
+ else if (!strcmp(endptr, "M") )
+ freq *= 1000000;
+ else
+ usage ();
+ }
+ break;
+ case 'c':
+ break;
+ case 'r':
+ case 'Z':
+ s51_port = strtol(optarg, &endptr, 0);
+ if (endptr == optarg || strlen(endptr) != 0)
+ usage();
+ break;
+ case 's':
+ break;
+ case 'S':
+ break;
+ case 'p':
+ s51_prompt = optarg;
+ break;
+ case 'P':
+ s51_prompt = NULL;
+ break;
+ case 'V':
+ break;
+ case 'v':
+ break;
+ case 'H':
+ exit (0);
+ break;
+ case 'h':
+ usage ();
+ break;
+ case 'm':
+ s51_monitor = 1;
+ break;
+ }
+ }
+ if (s51_port) {
+ int l, r, one = 1;
+ int s;
+ struct sockaddr_in in;
+
+ l = socket(AF_INET, SOCK_STREAM, 0);
+ if (l < 0) {
+ perror ("socket");
+ exit(1);
+ }
+ r = setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (int));
+ if (r) {
+ perror("setsockopt");
+ exit(1);
+ }
+ in.sin_family = AF_INET;
+ in.sin_port = htons(s51_port);
+ in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ r = bind(l, (struct sockaddr *) &in, sizeof (in));
+ if (r) {
+ perror("bind");
+ exit(1);
+ }
+ r = listen(l, 5);
+ if (r) {
+ perror("listen");
+ exit(1);
+ }
+ for (;;) {
+ struct sockaddr_in client_addr;
+ socklen_t client_len = sizeof (struct sockaddr_in);
+
+ s = accept(l, (struct sockaddr *)
+ &client_addr, &client_len);
+ if (s < 0) {
+ perror("accept");
+ exit(1);
+ }
+ s51_input = fdopen(s, "r");
+ s51_output = fdopen(s, "w");
+ if (!s51_input || !s51_output) {
+ perror("fdopen");
+ exit(1);
+ }
+ vec.sv_handler = SIG_IGN;
+ vec.sv_mask = 0;
+ vec.sv_flags = 0;
+ sigvec(SIGINT, &vec, &ovec);
+ command_read();
+ sigvec(SIGINT, &ovec, NULL);
+ fclose(s51_input);
+ fclose(s51_output);
+ }
+ } else {
+ s51_input = stdin;
+ s51_output = stdout;
+ vec.sv_handler = s51_sigint;
+ vec.sv_mask = 0;
+ vec.sv_flags = 0;
+ sigvec(SIGINT, &vec, &ovec);
+ command_read();
+ }
+ exit(0);
+}
+
+void
+s51_printf(char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(s51_output, format, ap);
+ if (s51_monitor)
+ vfprintf(stdout, format, ap);
+ va_end(ap);
+}
+
+void
+s51_putc(int c)
+{
+ putc(c, s51_output);
+}
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+int
+s51_read_line(char *line, int len)
+{
+ int ret;
+ if (s51_output == stdout && s51_input == stdin && s51_prompt) {
+ char *r;
+
+ r = readline(s51_prompt);
+ if (r == NULL)
+ return 0;
+ strncpy (line, r, len);
+ line[len-1] = '\0';
+ add_history(r);
+ return 1;
+ } else {
+ if (s51_prompt)
+ s51_printf("%s", s51_prompt);
+ else
+ s51_putc('\0');
+ fflush(s51_output);
+ ret = fgets(line, len, s51_input) != NULL;
+ if (s51_monitor)
+ printf("> %s", line);
+ fflush(stdout);
+ }
+ return ret;
+}
+
+int
+s51_check_input(void)
+{
+ struct pollfd input;
+ int r;
+ int c;
+
+ input.fd = fileno(s51_input);
+ input.events = POLLIN;
+ r = poll(&input, 1, 0);
+ if (r > 0) {
+ char line[256];
+ (void) s51_read_line(line, sizeof (line));
+ return 1;
+ }
+ return 0;
+}
diff --git a/s51/s51-parse.c b/s51/s51-parse.c
new file mode 100644
index 00000000..170c979d
--- /dev/null
+++ b/s51/s51-parse.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright © 2008 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "s51.h"
+
+static struct command_function functions[] = {
+ { "help", "?", command_help, "help", "Print this list\n" },
+ { "quit", "q", command_quit, "[q]uit", "Quit\n" },
+ { "di", "di", command_di, "di <start> <end>",
+ "Dump imem\n" },
+ { "ds", "ds", command_ds, "ds <start> <end>",
+ "Dump sprs\n" },
+ { "dx", "dx", command_dx, "dx <start> <end>",
+ "Dump xaddr\n" },
+ { "set", "t", command_set, "se[t] mem <prefix> <address> <data> ...",
+ "Set mem {xram|rom|iram|sfr}\n"
+ "set bit <addr>\n" },
+ { "dump", "d", command_dump, "[d]ump <prefix> <start> <end>",
+ "Dump {xram|rom|iram|sfr} <start> <end>\n" },
+ { "file", "file", command_file, "file <filename>",
+ "Pretend to load executable from <filename>\n" },
+ { "pc", "p", command_pc, "[p]c [addr]",
+ "Get or set pc value\n" },
+ { "break", "b", command_break,"[b]reak <addr>",
+ "Set break point\n" },
+ { "clear", "c", command_clear,"[c]lear <addr>",
+ "Clear break point\n" },
+ { "run", "r", command_run, "[r]un [start] [stop]",
+ "Run with optional start and temp breakpoint addresses\n" },
+ { "go", "g", command_run, "[g]o [start] [stop]",
+ "Run with optional start and temp breakpoint addresses\n" },
+ { "next", "n", command_next, "[n]ext",
+ "Step over one instruction, past any call\n" },
+ { "step", "s", command_step, "[s]tep",
+ "Single step\n" },
+ { "load", "l", command_load, "[l]oad <file>",
+ "Load a hex file into memory or flash" },
+ { "halt", "h", command_halt, "[h]alt",
+ "Halt the processor\n" },
+ { "reset","res",command_reset, "[res]et",
+ "Reset the CPU\n" },
+ { "status","status",command_status, "status",
+ "Display CC1111 debug status\n" },
+ { "info", "i", command_info, "[i]info",
+ "Get information\n" },
+ { "stop", "stop", command_stop, "stop",
+ "Ignored\n" },
+ { NULL, NULL, NULL, NULL, NULL },
+};
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE 1
+#endif
+
+static int
+string_to_int(char *s, int *v)
+{
+ char *endptr;
+
+ if (isdigit(s[0]) || s[0] == '-' || s[0] == '+') {
+ *v = strtol(s, &endptr, 0);
+ if (endptr == s)
+ return FALSE;
+ } else if (*s == '\'') {
+ s++;
+ if (*s == '\\') {
+ s++;
+ switch (*s) {
+ case 'n':
+ *v = '\n';
+ break;
+ case 't':
+ *v = '\t';
+ break;
+ default:
+ *v = (int) *s;
+ break;
+ }
+ } else
+ *v = (int) *s;
+ s++;
+ if (*s != '\'')
+ return FALSE;
+ }
+ else
+ return FALSE;
+ return TRUE;
+}
+
+struct command_function *
+command_string_to_function(struct command_function *functions, char *name)
+{
+ int i;
+ for (i = 0; functions[i].name; i++)
+ if (!strcmp(name, functions[i].name) ||
+ !strcmp(name, functions[i].alias))
+ return &functions[i];
+ return NULL;
+}
+
+enum command_result
+command_function_help(struct command_function *functions, int argc, char **argv)
+{
+ int i;
+ struct command_function *func;
+
+ if (argc == 1) {
+ for (i = 0; functions[i].name; i++)
+ s51_printf("%-10s%s\n", functions[i].name,
+ functions[i].usage);
+ } else {
+ for (i = 1; i < argc; i++) {
+ func = command_string_to_function(functions, argv[i]);
+ if (!func) {
+ s51_printf("%-10s unknown command\n", argv[i]);
+ return command_syntax;
+ }
+ s51_printf("%-10s %s\n%s", func->name,
+ func->usage, func->help);
+ }
+ }
+ return command_debug;
+}
+
+static int
+command_split_into_words(char *line, char **argv)
+{
+ char quotechar;
+ int argc;
+
+ argc = 0;
+ while (*line) {
+ while (isspace(*line))
+ line++;
+ if (!*line)
+ break;
+ if (*line == '"') {
+ quotechar = *line++;
+ *argv++ = line;
+ argc++;
+ while (*line && *line != quotechar)
+ line++;
+ if (*line)
+ *line++ = '\0';
+ } else {
+ *argv++ = line;
+ argc++;
+ while (*line && !isspace(*line))
+ line++;
+ if (*line)
+ *line++ = '\0';
+ }
+ }
+ *argv = 0;
+ return argc;
+}
+
+enum command_result
+command_help(int argc, char **argv)
+{
+ return command_function_help(functions, argc, argv);
+}
+
+void
+command_syntax_error(int argc, char **argv)
+{
+ s51_printf("Syntax error in:");
+ while (*argv)
+ s51_printf(" %s", *argv++);
+ s51_printf("\n");
+}
+
+void
+command_read (void)
+{
+ int argc;
+ char line[1024];
+ char *argv[20];
+ enum command_result result;
+ struct command_function *func;
+
+ s51_dbg = ccdbg_open ();
+ if (!s51_dbg) {
+ perror("ccdbg_open");
+ exit(1);
+ }
+ ccdbg_debug_mode(s51_dbg);
+ ccdbg_halt(s51_dbg);
+ s51_printf("Welcome to the non-simulated processor\n");
+ for (;;) {
+ if (s51_read_line (line, sizeof line) == 0)
+ break;
+ s51_interrupted = 0;
+ argc = command_split_into_words(line, argv);
+ if (argc > 0) {
+ func = command_string_to_function(functions, argv[0]);
+ if (!func)
+ command_syntax_error(argc, argv);
+ else
+ {
+ result = (*func->func)(argc, argv);
+ if (s51_interrupted)
+ result = command_interrupt;
+ switch (result) {
+ case command_syntax:
+ command_syntax_error(argc, argv);
+ break;
+ case command_error:
+ s51_printf("Error\n");
+ break;
+ case command_success:
+ break;
+ case command_interrupt:
+ ccdbg_halt(s51_dbg);
+ s51_printf("Interrupted\n");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ ccdbg_close(s51_dbg);
+ s51_printf("...\n");
+}
diff --git a/s51/s51.1 b/s51/s51.1
new file mode 100644
index 00000000..f2f59a52
--- /dev/null
+++ b/s51/s51.1
@@ -0,0 +1,211 @@
+.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH S51 1 "s51" ""
+.SH NAME
+s51 \- hex debugger for cc1111 processors
+.SH SYNOPSIS
+.B "s51"
+[\-t \fIcpu-type\fP]
+[\-X \fIfrequency\fP]
+[\-c]
+[\-r \fIlisten-port\fP]
+[\-Z \fIlisten-port\fP]
+[\-s]
+[\-S]
+[\-p \fIprompt\fP]
+[\-V]
+[\-v]
+[\-H]
+[\-h]
+[\-m]
+.SH DESCRIPTION
+.I s51
+connects to a cc1111 processor through a cp1203-based USB-to-serial
+converter board, using the GPIO pins available on that chip. It provides an
+interface compatible with the 8051 emulator of the same name (s51), but
+communicating with the real chip instead of an emulation. Using a modified
+version of the SDCC debugger (sdcdb), you can control program execution
+on the target machine at source-level.
+
+.SH OPTIONS
+The command line options are designed to be compatible with the 8051
+emulator so that it can be used with sdcdb. As such, they're all one letter
+long.
+.IP "\-t \fIcpu-type\fP"
+The 8051 emulator can operate as one of several different chips. Oddly, the
+real hardware cannot, so this option is ignored.
+.IP "\-X \fIfrequency\fP"
+Similarly, the emulator can pretend to run at an arbitrary frequency
+which the real hardware cannot do. Ignored.
+.IP "\-c"
+.IP "\-s"
+.IP "\-S"
+.IP "\-v"
+.IP "\-V"
+All ignored.
+.IP "\-r \fIlisten-port\fP, -Z \fIlisten-port\fP"
+The emulator and sdcdb communicate through a network socket. This option
+switches the debugger from communicating through stdin/stdout to listening
+on a specific network port instead. Once a connection is made, the debugger
+continues on, using that network port for command input and output. The
+debugger uses port 9756, and attempts to connect before launching s51, so if
+s51 is listening on this port before sdcdb is started, sdcdb will end up
+talking to the existing s51 instance. That's often useful for debugging s51
+itself.
+.IP "\-p \fIprompt\fP"
+This sets the command prompt to the specified string.
+.IP "\-P"
+This sets the command prompt to a single NUL character. This is for use by
+sdcdb.
+.IP "\-h"
+This should print a usage message, but does nothing useful currently.
+.IP "\-m"
+This option is not present in the original 8051 emulator, and causes s51 to
+dump all commands and replies that are received from and sent to sdcdb.
+.SH COMMANDS
+Once started, s51 connects to the cc1111 via the CP2103 using libusb2 and
+then reads and executes commands, either from stdin, or the nework
+connection to sdcdb.
+.PP
+Unlike the command line, s51 contains built-in help for each of these
+commands, via the 'help' command. Most of the commands are available in a
+long form and a single character short form. Below, the short form follows
+the long form after a comma.
+.IP "help, ? {command}"
+Without arguments, prints a list of available commands. With an argument
+prints more detail about the specific command
+.IP "quit, q"
+Terminates the application, without changing the state of the target
+processor.
+.IP "di [start] [end]"
+Dumps imem (256 bytes of "internal" memory) from start to end (inclusive).
+.IP "ds [start] [end]"
+Dumps sprs from start to end (inclusive). Note that while most sprs are
+visible in the global address space, some are not, so use this command
+instead of "dx" to read them.
+.IP "dx [start] [end]"
+Dump external (global) memory from start to end (inclusive).
+.IP "set, t <prefix> [start] {data ...}"
+Store to the memory space specified by prefix where prefix is one of "xram",
+"rom", "iram", or "sfr". Store bytes starting at start.
+.IP "dump, d <prefix> [start] [end]"
+Dump from the memory space specified by prefix, where prefix is one of
+"xram", "rom", "iram" or "sfr". Dumps from start to end (inclusive).
+.IP "file [filename]"
+Specifies an intel-format hex file (ihx) that contains the contents of the
+rom area loaded into the cc1111. This is used to respond to requests to dump
+rom memory contents without getting them from the cc1111 (which is slow).
+.IP "pc, p {address}"
+If the address argument is given, this sets the program counter to the
+specified value. Otherwise, the current program counter value is displayed.
+.IP "break, b [address]"
+Sets a breakpoint at the specified address. This uses the built-in hardware
+breakpoint support in the cc1111. As a result, it supports no more than four
+breakpoints at once. You must therefore use a modified version of sdcdb which
+changes how program execution is controlled to work within this limit.
+.IP "clear, c [address]"
+Clear a breakpoint from the specified address.
+.IP "run, r, go, g {start} {stop}"
+Resumes execution of the program. If the start argument is present, then it
+begins at that address, otherwise it continues running at the current pc. If
+a stop argument is present, then a temporary breakpoint is set at that
+address. This temporary breakpoint will be removed when execution hits it.
+.IP "next, n"
+Step one instruction. In the original s51 program this would ignore
+subroutines, but as sdcdb doesn't require this functionality, it's not
+available here.
+.IP "step, s"
+Step one instruction.
+.IP "load, l [filename]"
+This is not implemented, but it is supposed to load a hex file into flash.
+Use the ccload program instead.
+.IP "halt, h"
+Halt the processor. This is the only command which can be sent while the
+program is running. It is ignored at other times.
+.IP "reset, res"
+Reset the processor. This pulls the reset pin low and re-enables debug mode.
+Check the cc1111 documentation to see precisely what this does.
+.IP "status"
+This dumps the cc1111 debug status register.
+.IP "info, i breakpoints, b"
+List the current breakpoints.
+.IP "info, i help, ?"
+List the things you can get info on.
+.IP "stop"
+This doesn't do anything and is present only to retain compatibility with
+the original 8051 emulator.
+.SH "BOARD BRINGUP DEBUGGING"
+.PP
+While the original purpose for this program was to connect the source
+debugger with the hardware, it can also be used as a low-level hex debugger
+all on its own. In particular, all of the cc1111 peripherals can be
+manipulated directly from the s51 command line.
+.IP "Starting s51"
+If the CP2103 is plugged in, and the CC1111 is connected correctly, the
+\'s51\' command itself should connect to the device without trouble.
+Note that the CP2103 must have the GPIO pins configured correctly as well.
+.IP
+$ s51
+.br
+Welcome to the non-simulated processor
+.br
+> status
+.br
+ CPU halted
+.br
+ Halted by debug command
+.br
+>
+.IP "Turning on LEDs"
+Two of the cc1111 GPIO pins, P1_0 and P1_1 are capable of driving external
+LEDs. To control these, set the Port 1 direction bits to make these output
+pins and then change the Port 1 data to set them high or low:
+.IP
+> set sfr 0xfe 0x02 # set P1DIR to 0x2
+.br
+> set sfr 0x90 0x02 # set P1_1 to high
+.br
+> set sfr 0x90 0x00 # set P1_1 to low
+.IP "Reading the A/D converters"
+The six A/D converter inputs can each be connected to any of the P0 pins,
+ground, the A/D voltage refernece, an internal temperature sensor or VDD/3.
+To read one of these values, select an A/D converter to use then start the
+conversion process. The cc1111 manual has the table for selecting the input
+on page 144.
+.IP
+To configure one of the P0 pins for use by the A/D unit, we program the
+ADCCFG register, setting the bits in that which match the pins desired:
+.IP
+> set sfr 0xf2 0x3f # enable all 6 A/D inputs
+.IP
+To trigger a single conversion, we ask the A/D unit to perform an 'extra'
+conversion, which means to do a single conversion not a whole sequence of
+conversions. This is controlled by the ADCCON3 register at 0xB6:
+.IP
+> set sfr 0xb6 0xb2 # sample P0_2 using 12 bits of precision
+.br
+> ds 0xba 0xbb # dump the ADC data low and high regs
+.br
+> set sfr 0xb6 0xbe # sample internal temperature sensor
+.br
+> ds 0xba 0xbb # dump the ADC data low and high regs
+.SH "SEE ALSO"
+sdcdb(1), ccload(1)
+.SH AUTHOR
+Keith Packard
diff --git a/s51/s51.h b/s51/s51.h
new file mode 100644
index 00000000..f4dcce66
--- /dev/null
+++ b/s51/s51.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright © 2008 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ccdbg.h>
+
+extern char *s51_prompt;
+extern struct ccdbg *s51_dbg;
+extern int s51_interrupted;
+extern int s51_monitor;
+
+enum command_result {
+ command_success, command_debug, command_syntax, command_interrupt, command_error,
+};
+
+struct command_function {
+ char *name;
+ char *alias;
+ enum command_result (*func)(int argc, char **argv);
+ char *usage;
+ char *help;
+};
+
+struct command_function *
+command_string_to_function(struct command_function *functions, char *name);
+
+enum command_result
+command_function_help(struct command_function *functions, int argc, char **argv);
+
+void
+command_syntax_error(int argc, char **argv);
+
+enum command_result
+command_quit (int argc, char **argv);
+
+enum command_result
+command_help (int argc, char **argv);
+
+enum command_result
+command_stop (int argc, char **argv);
+
+enum command_result
+command_di (int argc, char **argv);
+
+enum command_result
+command_ds (int argc, char **argv);
+
+enum command_result
+command_dx (int argc, char **argv);
+
+enum command_result
+command_set (int argc, char **argv);
+
+enum command_result
+command_dump (int argc, char **argv);
+
+enum command_result
+command_file (int argc, char **argv);
+
+enum command_result
+command_pc (int argc, char **argv);
+
+enum command_result
+command_break (int argc, char **argv);
+
+enum command_result
+command_clear (int argc, char **argv);
+
+enum command_result
+command_run (int argc, char **argv);
+
+enum command_result
+command_next (int argc, char **argv);
+
+enum command_result
+command_step (int argc, char **argv);
+
+enum command_result
+command_load (int argc, char **argv);
+
+enum command_result
+command_halt (int argc, char **argv);
+
+enum command_result
+command_reset (int argc, char **argv);
+
+enum command_result
+command_status (int argc, char **argv);
+
+enum command_result
+command_info (int argc, char **argv);
+
+enum command_result
+cc_wait(void);
+
+void
+command_read (void);
+
+void
+s51_printf(char *format, ...);
+
+void
+s51_putc(int c);
+
+int
+s51_check_input(void);
+
+int
+s51_read_line(char *line, int len);