initial sh4 scif support for interacting with dcload

This commit is contained in:
Anthony Pesch 2017-10-25 18:22:23 -04:00
parent e06ce8fa71
commit 1ce904f5a9
13 changed files with 780 additions and 10 deletions

View File

@ -208,6 +208,7 @@ set(RELIB_SOURCES
src/guest/pvr/tr.c
src/guest/rom/boot.c
src/guest/rom/flash.c
src/guest/serial/serial.c
src/guest/sh4/sh4.c
src/guest/sh4/sh4_ccn.c
src/guest/sh4/sh4_dbg.c
@ -216,6 +217,7 @@ set(RELIB_SOURCES
src/guest/sh4/sh4_mem.c
src/guest/sh4/sh4_mmu.c
src/guest/sh4/sh4_tmu.c
src/guest/sh4/sh4_scif.c
src/guest/debugger.c
src/guest/dreamcast.c
src/guest/memory.c
@ -402,6 +404,19 @@ target_compile_definitions(recc PRIVATE ${RELIB_DEFS})
target_compile_options(recc PRIVATE ${RELIB_FLAGS})
endif()
# reload
set(RELOAD_SOURCES
${RELIB_SOURCES}
src/host/null_host.c
tools/reload/main.c)
source_group_by_dir(RELOAD_SOURCES)
add_executable(reload ${RELOAD_SOURCES})
target_include_directories(reload PUBLIC ${RELIB_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(reload ${RELIB_LIBS})
target_compile_definitions(reload PRIVATE ${RELIB_DEFS})
target_compile_options(reload PRIVATE ${RELIB_FLAGS})
# retex
set(RETEX_SOURCES
${RELIB_SOURCES}

View File

@ -40,6 +40,7 @@ SOURCES_C := $(CORE_DIR)/src/core/assert.c \
$(CORE_DIR)/src/guest/arm7/arm7.c \
$(CORE_DIR)/src/guest/rom/boot.c \
$(CORE_DIR)/src/guest/rom/flash.c \
$(CORE_DIR)/src/guest/serial/serial.c \
$(CORE_DIR)/src/guest/sh4/sh4.c \
$(CORE_DIR)/src/guest/sh4/sh4_ccn.c \
$(CORE_DIR)/src/guest/sh4/sh4_dbg.c \
@ -48,6 +49,7 @@ SOURCES_C := $(CORE_DIR)/src/core/assert.c \
$(CORE_DIR)/src/guest/sh4/sh4_mem.c \
$(CORE_DIR)/src/guest/sh4/sh4_mmu.c \
$(CORE_DIR)/src/guest/sh4/sh4_tmu.c \
$(CORE_DIR)/src/guest/sh4/sh4_scif.c \
$(CORE_DIR)/src/guest/debugger.c \
$(CORE_DIR)/src/guest/dreamcast.c \
$(CORE_DIR)/src/guest/memory.c \

View File

@ -92,6 +92,14 @@ void *dc_create_device(struct dreamcast *dc, size_t size, const char *name,
return dev;
}
void dc_remove_serial_device(struct dreamcast *dc) {
dc->serial = NULL;
}
void dc_add_serial_device(struct dreamcast *dc, struct serial *serial) {
dc->serial = serial;
}
void dc_input(struct dreamcast *dc, int port, int button, uint16_t value) {
maple_handle_input(dc->maple, port, button, value);
}

View File

@ -137,6 +137,7 @@ struct dreamcast {
struct maple *maple;
struct pvr *pvr;
struct ta *ta;
struct serial *serial;
struct list devices;
/* client callbacks */
@ -159,6 +160,8 @@ void dc_suspend(struct dreamcast *dc);
void dc_resume(struct dreamcast *dc);
void dc_tick(struct dreamcast *dc, int64_t ns);
void dc_input(struct dreamcast *dc, int port, int button, uint16_t value);
void dc_add_serial_device(struct dreamcast *dc, struct serial *serial);
void dc_remove_serial_device(struct dreamcast *dc);
/* device registration */
void *dc_create_device(struct dreamcast *dc, size_t size, const char *name,

36
src/guest/serial/serial.c Normal file
View File

@ -0,0 +1,36 @@
#include "guest/serial/serial.h"
#include "guest/dreamcast.h"
struct serial {
struct device;
void *userdata;
getchar_cb getchar;
putchar_cb putchar;
};
static int serial_init(struct device *dev) {
struct serial *serial = (struct serial *)dev;
return 1;
}
void serial_putchar(struct serial *serial, int c) {
serial->putchar(serial->userdata, c);
}
int serial_getchar(struct serial *serial) {
return serial->getchar(serial->userdata);
}
void serial_destroy(struct serial *serial) {
dc_destroy_device((struct device *)serial);
}
struct serial *serial_create(struct dreamcast *dc, void *userdata,
getchar_cb getchar, putchar_cb putchar) {
struct serial *serial =
dc_create_device(dc, sizeof(struct serial), "serial", &serial_init, NULL);
serial->userdata = userdata;
serial->getchar = getchar;
serial->putchar = putchar;
return serial;
}

18
src/guest/serial/serial.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef SERIAL_H
#define SERIAL_H
#include <stdint.h>
struct dreamcast;
typedef int (*getchar_cb)(void *);
typedef void (*putchar_cb)(void *, int);
struct serial *serial_create(struct dreamcast *dc, void *userdata,
getchar_cb getchar, putchar_cb putchar);
void serial_destroy(struct serial *serial);
int serial_getchar(struct serial *serial);
void serial_putchar(struct serial *serial, int c);
#endif

View File

@ -8,6 +8,7 @@
#include "guest/sh4/sh4_intc.h"
#include "guest/sh4/sh4_mem.h"
#include "guest/sh4/sh4_mmu.h"
#include "guest/sh4/sh4_scif.h"
#include "guest/sh4/sh4_types.h"
#include "jit/frontend/sh4/sh4_guest.h"
#include "jit/jit.h"
@ -56,6 +57,11 @@ struct sh4 {
uint32_t utlb_sq_map[64];
struct sh4_tlb_entry utlb[64];
/* scif */
uint32_t SCFSR2_last_read;
struct sh4_scif_fifo receive_fifo;
struct sh4_scif_fifo transmit_fifo;
/* tmu */
struct timer *tmu_timers[3];
};

View File

@ -18,12 +18,12 @@ static void sh4_dmac_check(struct sh4 *sh4, int channel) {
chcr = sh4->CHCR3;
break;
default:
LOG_FATAL("Unexpected DMA channel");
LOG_FATAL("sh4_dmac_check unexpected channel %d", channel);
break;
}
CHECK(sh4->DMAOR->DDT || !sh4->DMAOR->DME || !chcr->DE,
"Non-DDT DMA not supported");
"sh4_dmac_check only DDT DMA unsupported");
}
void sh4_dmac_ddt(struct sh4 *sh4, struct sh4_dtr *dtr) {

View File

@ -145,7 +145,7 @@ SH4_REG(0xffe8000c, SCFTDR2, 0x00000000, uint32_t)
SH4_REG(0xffe80010, SCFSR2, 0x00000060, union scfsr2)
SH4_REG(0xffe80014, SCFRDR2, 0x00000000, uint32_t)
SH4_REG(0xffe80018, SCFCR2, 0x00000000, union scfcr2)
SH4_REG(0xffe8001c, SCFDR2, 0x00000000, uint32_t)
SH4_REG(0xffe8001c, SCFDR2, 0x00000000, union scfdr2)
SH4_REG(0xffe80020, SCSPTR2, 0x00000000, uint32_t)
SH4_REG(0xffe80024, SCLSR2, 0x00000000, union sclsr2)
SH4_REG(0xfff00000, SDIR, 0x0000ffff, uint32_t)

311
src/guest/sh4/sh4_scif.c Normal file
View File

@ -0,0 +1,311 @@
/*
* serial communication interface implementation
*
* this implementation is very incomplete. primarily, the serial port's transfer
* rate is not emulated, transfers are instead pumped when the status register
* is polled. due to this, features like overrun are also not emulated, it's
* just made to never occur
*
* with that said, the implemntation is complete enough to communicate with
* dcload, which is the primary use case
*/
#include "guest/serial/serial.h"
#include "guest/sh4/sh4.h"
static int fifo_size(struct sh4_scif_fifo *q) {
int size = q->head - q->tail;
/* check if the head pointer has wrapped around */
if (size < 0) {
size = sizeof(q->data) - (q->tail - q->head);
}
return size;
}
static int fifo_dequeue(struct sh4_scif_fifo *q) {
if (q->tail == q->head) {
return -1;
}
int data = q->data[q->tail];
q->tail = (q->tail + 1) % sizeof(q->data);
return data;
}
static int fifo_enqueue(struct sh4_scif_fifo *q, int data) {
int size = fifo_size(q);
/* never let the fifo completely fill up (see sh4_scif.h) */
if (size == SCIF_FIFO_SIZE) {
return 0;
}
q->data[q->head] = data;
q->head = (q->head + 1) % sizeof(q->data);
return 1;
}
static int receive_triggered(struct sh4 *sh4) {
static uint32_t receive_triggers[] = {1, 4, 8, 14};
return sh4->SCFDR2->R >= receive_triggers[sh4->SCFCR2->RTRG];
}
static int receive_dequeue(struct sh4 *sh4) {
struct sh4_scif_fifo *q = &sh4->receive_fifo;
int data = fifo_dequeue(q);
if (data == -1) {
return -1;
}
sh4->SCFDR2->R = fifo_size(q);
/* RDF isn't cleared when reading from SCFRDR2, it must be explicitly cleared
by writing to SCFSR2 */
return data;
}
static int receive_enqueue(struct sh4 *sh4, int data) {
struct sh4_scif_fifo *q = &sh4->receive_fifo;
/* TODO raise ORER on overflow */
int res = fifo_enqueue(q, data);
CHECK(res);
sh4->SCFDR2->R = fifo_size(q);
sh4->SCFSR2->RDF = receive_triggered(sh4);
/* raise interrupt if enabled and triggered */
if (sh4->SCSCR2->RIE && sh4->SCFSR2->RDF) {
sh4_raise_interrupt(sh4, SH4_INT_SCIFRXI);
}
return 1;
}
static void receive_reset(struct sh4 *sh4) {
struct sh4_scif_fifo *q = &sh4->receive_fifo;
q->head = q->tail = 0;
sh4->SCFDR2->R = 0;
sh4->SCFSR2->RDF = 0;
sh4_clear_interrupt(sh4, SH4_INT_SCIFRXI);
}
static int transmit_triggered(struct sh4 *sh4) {
static uint32_t transmit_triggers[] = {8, 4, 2, 1};
return sh4->SCFDR2->T <= transmit_triggers[sh4->SCFCR2->TTRG];
}
static int transmit_ended(struct sh4 *sh4) {
return sh4->SCFDR2->T == 0;
}
static int transmit_dequeue(struct sh4 *sh4) {
struct sh4_scif_fifo *q = &sh4->transmit_fifo;
int data = fifo_dequeue(q);
if (data == -1) {
return -1;
}
sh4->SCFDR2->T = fifo_size(q);
sh4->SCFSR2->TDFE = transmit_triggered(sh4);
sh4->SCFSR2->TEND = transmit_ended(sh4);
/* raise interrupt if enabled and triggered */
if (sh4->SCSCR2->TIE && sh4->SCFSR2->TDFE) {
sh4_raise_interrupt(sh4, SH4_INT_SCIFTXI);
}
return data;
}
static int transmit_enqueue(struct sh4 *sh4, int data) {
struct sh4_scif_fifo *q = &sh4->transmit_fifo;
/* TODO discard when full */
int res = fifo_enqueue(q, data);
CHECK(res);
sh4->SCFDR2->T = fifo_size(q);
/* TDFE isn't cleared when writing SCFTDR2, it must be explicitly cleared
by writing to SCFSR2 */
return 1;
}
static void transmit_reset(struct sh4 *sh4) {
struct sh4_scif_fifo *q = &sh4->transmit_fifo;
q->head = q->tail = 0;
sh4->SCFDR2->T = 0;
sh4->SCFSR2->TEND = 1;
sh4->SCFSR2->TDFE = 1;
sh4_clear_interrupt(sh4, SH4_INT_SCIFTXI);
}
static void sh4_scif_run(struct sh4 *sh4) {
struct serial *serial = sh4->dc->serial;
if (!serial) {
return;
}
/* transfer rates aren't emulated at all, just completely fill / drain each
queue at this point */
if (sh4->SCSCR2->RE && !sh4->SCLSR2->ORER) {
while (sh4->SCFDR2->R < SCIF_FIFO_SIZE) {
int data = serial_getchar(serial);
if (data == -1) {
break;
}
receive_enqueue(sh4, data);
}
}
if (sh4->SCSCR2->TE) {
while (sh4->SCFDR2->T > 0) {
int data = transmit_dequeue(sh4);
CHECK_NE(data, -1);
serial_putchar(serial, data);
}
}
}
REG_W32(sh4_cb, SCSMR2) {
struct sh4 *sh4 = dc->sh4;
sh4->SCSMR2->full = value;
/* none of the fancy transfer modes are supported */
CHECK_EQ(sh4->SCSMR2->full, 0);
}
REG_W32(sh4_cb, SCBRR2) {
struct sh4 *sh4 = dc->sh4;
/* TODO handle transfer rate */
*sh4->SCBRR2 = value;
}
REG_W32(sh4_cb, SCSCR2) {
struct sh4 *sh4 = dc->sh4;
sh4->SCSCR2->full = value;
CHECK_EQ(sh4->SCSCR2->CKE1, 0);
/* transmission has ended */
if (!sh4->SCSCR2->TE) {
sh4->SCFSR2->TEND = 1;
}
/* clear interrupts if disabled */
if (!sh4->SCSCR2->REIE && !sh4->SCSCR2->RIE) {
sh4_clear_interrupt(sh4, SH4_INT_SCIFERI);
sh4_clear_interrupt(sh4, SH4_INT_SCIFBRI);
}
if (!sh4->SCSCR2->RIE) {
sh4_clear_interrupt(sh4, SH4_INT_SCIFRXI);
}
if (!sh4->SCSCR2->TIE) {
sh4_clear_interrupt(sh4, SH4_INT_SCIFTXI);
}
}
REG_R32(sh4_cb, SCFTDR2) {
LOG_FATAL("unexpected read from SCFTDR2");
}
REG_W32(sh4_cb, SCFTDR2) {
struct sh4 *sh4 = dc->sh4;
transmit_enqueue(sh4, (int)value);
}
REG_R32(sh4_cb, SCFSR2) {
struct sh4 *sh4 = dc->sh4;
sh4_scif_run(sh4);
/* in order to clear the SCFSR2 bits, they must be read first */
sh4->SCFSR2_last_read = sh4->SCFSR2->full;
return sh4->SCFSR2->full;
}
REG_W32(sh4_cb, SCFSR2) {
struct sh4 *sh4 = dc->sh4;
/* can only clear ER, TEND, TDFE, BRK, RDF and DR */
value |= 0xffffff0c;
/* can only clear if the flag was previously read as 1 */
value |= ~sh4->SCFSR2_last_read;
sh4->SCFSR2->full &= value;
/* RDF / TDFE / TEND aren't cleared if still valid */
sh4->SCFSR2->RDF = receive_triggered(sh4);
sh4->SCFSR2->TDFE = transmit_triggered(sh4);
sh4->SCFSR2->TEND = transmit_ended(sh4);
/* clear RXI if RDF is cleared */
if (sh4->SCSCR2->RIE && !sh4->SCFSR2->RDF) {
sh4_clear_interrupt(sh4, SH4_INT_SCIFRXI);
}
/* clear TXI if TDFE is cleared */
if (sh4->SCSCR2->TIE && !sh4->SCFSR2->TDFE) {
sh4_clear_interrupt(sh4, SH4_INT_SCIFTXI);
}
}
REG_R32(sh4_cb, SCFRDR2) {
struct sh4 *sh4 = dc->sh4;
uint32_t data = receive_dequeue(sh4);
return data;
}
REG_W32(sh4_cb, SCFRDR2) {
LOG_FATAL("unexpected write to SCFRDR2");
}
REG_W32(sh4_cb, SCFCR2) {
struct sh4 *sh4 = dc->sh4;
sh4->SCFCR2->full = value;
/* unsupported */
CHECK_EQ(sh4->SCFCR2->LOOP, 0);
/* reset fifos */
if (sh4->SCFCR2->RFRST) {
receive_reset(sh4);
}
if (sh4->SCFCR2->TFRST) {
transmit_reset(sh4);
}
/* TODO handle MCE */
/* unsupported */
CHECK_EQ(sh4->SCFCR2->RSTRG, 0);
}
REG_R32(sh4_cb, SCLSR2) {
struct sh4 *sh4 = dc->sh4;
return sh4->SCLSR2->full;
}
REG_W32(sh4_cb, SCLSR2) {
struct sh4 *sh4 = dc->sh4;
/* TODO ORER can only be cleared if read as 1 first */
sh4->SCLSR2->full = value;
}

15
src/guest/sh4/sh4_scif.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef SH4_SCIF_H
#define SH4_SCIF_H
#define SCIF_FIFO_SIZE 16
struct sh4_scif_fifo {
int head;
int tail;
/* ringbuffers have an ambiguous case when the head is equal to the tail - the
queue could be full or empty. add one to the fifo size to avoid this */
uint8_t data[SCIF_FIFO_SIZE + 1];
};
#endif

View File

@ -191,17 +191,23 @@ union scfcr2 {
uint32_t RFRST : 1;
uint32_t TFRST : 1;
uint32_t MCE : 1;
uint32_t TTRG0 : 1;
uint32_t TTRG1 : 1;
uint32_t RTRG0 : 1;
uint32_t RTRG1 : 1;
uint32_t RSTRG0 : 1;
uint32_t RSTRG1 : 1;
uint32_t RSTRG2 : 1;
uint32_t TTRG : 2;
uint32_t RTRG : 2;
uint32_t RSTRG : 3;
uint32_t : 21;
};
};
union scfdr2 {
uint32_t full;
struct {
uint32_t R : 5;
uint32_t : 3;
uint32_t T : 5;
uint32_t : 19;
};
};
union sclsr2 {
uint32_t full;
struct {

350
tools/reload/main.c Normal file
View File

@ -0,0 +1,350 @@
#include "core/core.h"
#include "core/filesystem.h"
#include "core/thread.h"
#include "guest/dreamcast.h"
#include "guest/serial/serial.h"
#include "guest/sh4/sh4.h"
enum {
STATE_LOADING,
STATE_RUNNING,
STATE_SHUTDOWN,
};
static void sys_write();
typedef void (*syscall_cb)();
static syscall_cb syscalls[] = {
NULL, /* exit */
NULL, /* fstat */
sys_write, /* write */
NULL, /* read */
NULL, /* open */
NULL, /* close */
NULL, /* create */
NULL, /* link */
NULL, /* unlink */
NULL, /* chdir */
NULL, /* chmod */
NULL, /* lseek */
NULL, /* time */
NULL, /* state */
NULL, /* utime */
NULL, /* unknown */
NULL, /* opendir */
NULL, /* closedir */
NULL, /* readdir */
NULL, /* readsectors */
NULL, /* gdbpacket */
NULL, /* rewinddir */
};
/*
* serial device to communicate with dcload
*/
/* the serial device has an infinitely-large queue for incoming and outgoing
data, providing a higher-level interface for transmitting data on top of
the raw putchar and getchar callbacks */
#define BLOCK_SIZE 4096
struct data_block {
uint8_t data[BLOCK_SIZE];
int read;
int write;
struct list_node it;
};
static mutex_t dev_mutex;
static struct list dev_readq;
static struct list dev_writeq;
static void queue_putchar(struct list *list, int c) {
mutex_lock(dev_mutex);
struct data_block *block = list_last_entry(list, struct data_block, it);
if (!block || block->write >= BLOCK_SIZE) {
struct data_block *next = calloc(1, sizeof(*block));
list_add_after_entry(list, block, next, it);
block = next;
}
block->data[block->write++] = (uint8_t)c;
mutex_unlock(dev_mutex);
}
static int queue_getchar(struct list *list) {
int c = -1;
mutex_lock(dev_mutex);
struct data_block *block = list_first_entry(list, struct data_block, it);
if (block && block->read < block->write) {
c = (int)block->data[block->read++];
if (block->read >= BLOCK_SIZE) {
list_remove(list, &block->it);
free(block);
}
}
mutex_unlock(dev_mutex);
return c;
}
/* called on the emulation thread when the scif is ready to receive another
character */
static int dev_getchar(void *userdata) {
return queue_getchar(&dev_writeq);
}
/* called on the emulation thread when the scif is transmitting another
character */
static void dev_putchar(void *userdata, int c) {
queue_putchar(&dev_readq, c);
}
static void dev_read_raw(void *ptr, int size) {
uint8_t *data = ptr;
while (size) {
int c = queue_getchar(&dev_readq);
/* TODO use a condition variable instead of spinning / locking constantly */
/* block until a char is available */
if (c == -1) {
continue;
}
*(data++) = c;
size--;
}
}
static void dev_write_raw(const void *ptr, int size) {
const uint8_t *data = ptr;
while (size--) {
queue_putchar(&dev_writeq, *(data++));
}
}
static void dev_read_blob(void *ptr, int size) {
uint8_t *data = ptr;
char type;
dev_read_raw(&type, 1);
CHECK_EQ(type, 'U');
int n;
dev_read_raw(&n, 4);
dev_read_raw(data, n);
int sum;
dev_read_raw(&sum, 1);
char ok = 'G';
dev_write_raw(&ok, 1);
}
static void dev_write_checked(const void *ptr, int size) {
uint8_t tmp[4];
CHECK_LE(size, (int)sizeof(tmp));
dev_write_raw(ptr, size);
dev_read_raw(tmp, size);
CHECK(memcmp(ptr, tmp, size) == 0);
}
/*
* dcload syscalls
*/
static void sys_write() {
int fd;
dev_read_raw(&fd, 4);
int n;
dev_read_raw(&n, 4);
char *data = malloc(n);
dev_read_blob(data, n);
int res = write(fd, data, n);
dev_write_checked(&res, 4);
free(data);
}
/*
* dcload commands
*/
char checksum(const void *ptr, int size) {
const uint8_t *data = ptr;
char sum = 0;
while (size--) {
sum ^= *(data++);
}
return sum;
}
static void run_code(uint32_t addr) {
/* send over serial if */
{
char cmd = 'A';
int console = 1;
dev_write_checked(&cmd, 1);
dev_write_checked(&addr, 4);
dev_write_checked(&console, 4);
}
/* parse syscall responses */
{
while (1) {
char cmd;
dev_read_raw(&cmd, 1);
if (!cmd) {
break;
}
CHECK(cmd >= 0 && cmd < (int)ARRAY_SIZE(syscalls));
syscall_cb cb = syscalls[(int)cmd];
CHECK_NOTNULL(cb, "run_code unexpected syscall=%d", cmd);
cb();
}
}
}
static void load_code(uint32_t addr, const char *path) {
uint8_t *bin = NULL;
int bin_size = 0;
/* load file */
{
FILE *fp = fopen(path, "rb");
CHECK(fp);
fseek(fp, 0, SEEK_END);
bin_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
bin = malloc(bin_size);
int n = (int)fread(bin, 1, bin_size, fp);
CHECK_EQ(n, bin_size);
fclose(fp);
}
/* send over serial if */
{
/* write load binary command */
char cmd = 'B';
dev_write_checked(&cmd, 1);
dev_write_checked(&addr, 4);
dev_write_checked(&bin_size, 4);
/* write payload */
char type = 'U';
char sum = checksum(bin, bin_size);
dev_write_raw(&type, 1);
dev_write_checked(&bin_size, 4);
dev_write_raw(bin, bin_size);
dev_write_raw(&sum, 1);
dev_read_raw(&type, 1);
CHECK_EQ(type, 'G');
}
free(bin);
}
/*
* main program
*/
static volatile int state;
static struct dreamcast *dc;
static thread_t dc_thread;
static void *dc_main(void *data) {
const char *dcload_path = data;
struct serial *serial = NULL;
int res = 0;
dc = dc_create(NULL);
CHECK_NOTNULL(dc);
serial = serial_create(dc, NULL, dev_getchar, dev_putchar);
dc_add_serial_device(dc, serial);
res = dc_load(dc, dcload_path);
CHECK(res);
state = STATE_RUNNING;
while (state == STATE_RUNNING) {
dc_tick(dc, 1000);
}
serial_destroy(serial);
serial = NULL;
dc_destroy(dc);
dc = NULL;
return NULL;
}
int main(int argc, char **argv) {
if (argc < 3) {
LOG_INFO("reload /path/to/dcload-serial.cdi /path/to/test.bin ...");
return EXIT_FAILURE;
}
/* set application directory */
char appdir[PATH_MAX];
char userdir[PATH_MAX];
int r = fs_userdir(userdir, sizeof(userdir));
CHECK(r);
snprintf(appdir, sizeof(appdir), "%s" PATH_SEPARATOR ".redream", userdir);
fs_set_appdir(appdir);
/* startup machine */
dev_mutex = mutex_create();
CHECK_NOTNULL(dev_mutex);
dc_thread = thread_create(&dc_main, NULL, argv[1]);
CHECK_NOTNULL(dc_thread);
/* wait for it to initialize */
while (state == STATE_LOADING) {
}
/* run each binary */
const uint32_t code_addr = 0x8c010000;
for (int i = 2; i < argc; i++) {
load_code(code_addr, argv[i]);
run_code(code_addr);
}
/* shutdown machine */
void *result;
state = STATE_SHUTDOWN;
thread_join(dc_thread, &result);
dc_thread = NULL;
mutex_destroy(dev_mutex);
dev_mutex = NULL;
return EXIT_SUCCESS;
}