From: Justin Wind Date: Sat, 12 May 2012 18:55:51 +0000 (-0700) Subject: further changes for v1.7: cpu fixes, support for 'hardware' devices, display to vnc X-Git-Url: https://git.squeep.com/?a=commitdiff_plain;h=0a1b4588f79e3719af9431a98da44350030df754;p=dcpu16 further changes for v1.7: cpu fixes, support for 'hardware' devices, display to vnc small changes throughout for 1.7, normalized some variable names, all registers now indexable in array. fixed behavior of branch-skip. changes to hw interfaces. added initial clock, keyboard, and display modules. display module now supports rendering as vnc server. hardware devices are still somewhat incomplete and lack testing. --- diff --git a/Makefile b/Makefile index f0f3b71..263c8e3 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,8 @@ CC = clang endif PROGRAMS = as-dcpu16 vm-dcpu16 -SOURCES = common.c dcpu16.c as-dcpu16.c vm-dcpu16.c display.c +SOURCES = common.c dcpu16.c as-dcpu16.c vm-dcpu16.c \ + hw_clock.c hw_keyboard.c hw_lem1802.c CPPFLAGS += -DDEBUG @@ -25,6 +26,8 @@ ifeq ($(UNAME),Linux) CPPFLAGS += -DHAVE_FOPENCOOKIE -D_GNU_SOURCE endif +CPPFLAGS += -DHAVE_LIBVNCSERVER -I/opt/local/include +LDFLAGS += -L/opt/local/lib -lvncserver all: $(PROGRAMS) @@ -37,7 +40,7 @@ depend: .depend include .depend -vm-dcpu16: vm-dcpu16.o dcpu16.o common.o display.o +vm-dcpu16: vm-dcpu16.o dcpu16.o common.o hw_clock.o hw_keyboard.o hw_lem1802.o as-dcpu16: as-dcpu16.o dcpu16.o common.o diff --git a/dcpu16.c b/dcpu16.c index 75c6cf0..d7b3d48 100644 --- a/dcpu16.c +++ b/dcpu16.c @@ -18,10 +18,6 @@ * * Justin Wind * 2012 04 05 - implementation started - * 2012 04 06 - first functionality achieved - * 2012 04 09 - minor cleanups - * 2012 04 10 - moved cli to separate module - * 2012 04 12 - added basic callback support for address accesses * 2012 05 05 - start of v1.7 revisions * 2012 05 08 - v1.7 revisions mostly complete * @@ -42,7 +38,23 @@ static const char * const src_id_ = "$Id$"; #define OPCODE_OPERAND_B_BITS 5 #define OPCODE_OPERAND_A_BITS 6 -static const char * const regnames_ = "ABCXYZIJ"; +const char * const dcpu16_reg_names[] = { + "A", + "B", + "C", + "X", + "Y", + "Z", + "I", + "J", + "PC", + "SP", + "EX", + "IA", + NULL +}; + +static void printf_null_(char *fmt, ...) { (void)fmt; } /* some default warning and debug reporting functions, which can be overridden by clients */ #define WARN(...) do { if (warn_cb_) warn_cb_(__VA_ARGS__); } while (0) @@ -60,7 +72,10 @@ void warn_(char *fmt, ...) { } static void (*warn_cb_)(char *fmt, ...) = warn_; void dcpu16_warn_cb_set(void (*fn)(char *fmt, ...)) { - warn_cb_ = fn; + if (fn) + warn_cb_ = fn; + else + warn_cb_ = printf_null_; } #ifdef DEBUG @@ -88,7 +103,10 @@ static void (*trace_cb_)(char *fmt, ...) = #endif ; void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) { - trace_cb_ = fn; + if (fn) + trace_cb_ = fn; + else + trace_cb_ = printf_null_; } @@ -106,21 +124,16 @@ void acct_event_(struct dcpu16 *vm, dcpu16_acct_event ev, DCPU16_WORD addr) { } } -/* convert register name to index into register array */ -static inline -off_t reg_index_(int reg) { - return strchr(regnames_, reg) - regnames_; -} /* add an entry to the interrupt queue */ static -int interrupt_enqueue_(struct dcpu16 *d, DCPU16_WORD message) { - d->interrupts_[d->interrupts_tail_] = message; - d->interrupts_tail_ += 1; - d->interrupts_tail_ %= DCPU16_INTERRUPT_QUEUE_SIZE; +int interrupt_enqueue_(struct dcpu16 *vm, DCPU16_WORD message) { + vm->interrupts_[vm->interrupts_tail_] = message; + vm->interrupts_tail_ += 1; + vm->interrupts_tail_ %= DCPU16_INTERRUPT_QUEUE_SIZE; - if (d->interrupts_tail_ == d->interrupts_head_) { - d->on_fire_ = 1; + if (vm->interrupts_tail_ == vm->interrupts_head_) { + vm->on_fire_ = 1; WARN("interrupt queue overflow (system is now on fire)"); return -1; } @@ -129,17 +142,17 @@ int interrupt_enqueue_(struct dcpu16 *d, DCPU16_WORD message) { } static -DCPU16_WORD interrupt_dequeue_(struct dcpu16 *d) { +DCPU16_WORD interrupt_dequeue_(struct dcpu16 *vm) { DCPU16_WORD message; - if (d->interrupts_tail_ == d->interrupts_head_) { + if (vm->interrupts_tail_ == vm->interrupts_head_) { WARN("interrupt underflow"); return 0; } - message = d->interrupts_[d->interrupts_head_]; - d->interrupts_head_ += 1; - d->interrupts_head_ %= DCPU16_INTERRUPT_QUEUE_SIZE; + message = vm->interrupts_[vm->interrupts_head_]; + vm->interrupts_head_ += 1; + vm->interrupts_head_ %= DCPU16_INTERRUPT_QUEUE_SIZE; return message; } @@ -152,20 +165,20 @@ DCPU16_WORD interrupt_dequeue_(struct dcpu16 *d) { * e_addr is set to a referenced address, for accounting callback * pc_adjust is set to how to change the program counter * stack_adjust is set to how to change the stack pointer - * cycles is set to number of cycles spent looking up operand + * cycle_adjust is set to number of cycles spent looking up operand * * zero all adjustables before decoding first operand, and pass in these values when * decoding next operand.. * */ static inline -void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, DCPU16_WORD value_data, +void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a, DCPU16_WORD value_data, DCPU16_WORD *work_v, DCPU16_WORD **v, DCPU16_WORD *e_addr, short *pc_adjust, short *sp_adjust, unsigned int *cycle_adjust) { assert(value <= 0x3f); - DCPU16_WORD pc = (DCPU16_WORD)(d->pc + *pc_adjust), - sp = (DCPU16_WORD)(d->sp + *sp_adjust); + DCPU16_WORD pc = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *pc_adjust), + sp = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *sp_adjust); TRACE("%s: pc:0x%04x sp:0x%04x value_data:0x%04x\n", __func__, @@ -174,21 +187,21 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, value_data); if (value <= 0x07) { /* register */ - *v = d->reg + value; - TRACE("%s>> %c (0x%04x)", + *v = vm->reg + value; + TRACE("%s>> %s (0x%04x)", __func__, - regnames_[value], + dcpu16_reg_names[value], **v); return; } if (value <= 0x0f) { /* [register] */ - *v = &(d->ram[ d->reg[value & 0x07] ]); - *e_addr = d->reg[value & 0x07]; - TRACE("%s>> [%c] [0x%04x] (0x%04x)", + *v = &(vm->ram[ vm->reg[value & 0x07] ]); + *e_addr = vm->reg[value & 0x07]; + TRACE("%s>> [%s] [0x%04x] (0x%04x)", __func__, - regnames_[value & 0x07], - d->reg[value & 0x07], + dcpu16_reg_names[value & 0x07], + vm->reg[value & 0x07], **v); return; } @@ -196,13 +209,13 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, if (value <= 0x17) { /* [next word + register] */ *pc_adjust += 1; /* consume next word */ *cycle_adjust += 1; - *e_addr = value_data + d->reg[value & 0x07]; - *v = d->ram + *e_addr; - TRACE("%s>> [nextword + %c] [0x%04x + 0x%04x] (0x%04x)", + *e_addr = value_data + vm->reg[value & 0x07]; + *v = vm->ram + *e_addr; + TRACE("%s>> [nextword + %s] [0x%04x + 0x%04x] (0x%04x)", __func__, - regnames_[value & 0x07], + dcpu16_reg_names[value & 0x07], value_data, - d->reg[value & 0x07], + vm->reg[value & 0x07], **v); return; } @@ -210,12 +223,12 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, switch (value) { case 0x18: /* PUSH/[--SP] or POP/[SP++] */ if (value_is_a == 0) { /* b */ - *v = &(d->ram[sp - 1]); + *v = &(vm->ram[sp - 1]); *sp_adjust -= 1; *e_addr = sp - 1; TRACE("%s>> PUSH [0x%04x] (0x%04x)", __func__, sp - 1, **v); } else { /* a */ - *v = &(d->ram[sp]); + *v = &(vm->ram[sp]); *sp_adjust += 1; *e_addr = sp; TRACE("%s>> POP [0x%04x] (0x%04x)", __func__, sp, **v); @@ -223,7 +236,7 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, break; case 0x19: /* PEEK/[SP] */ - *v = &(d->ram[sp]); + *v = &(vm->ram[sp]); *e_addr = sp; TRACE("%s>> PEEK [0x%04x] (0x%04x)", __func__, @@ -235,7 +248,7 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, *pc_adjust += 1; *cycle_adjust += 1; *e_addr = sp + value_data; - *v = d->ram + *e_addr; + *v = vm->ram + *e_addr; TRACE("%s>> PICK 0x%04x [0x%04x] (0x%04x)", __func__, value_data, @@ -244,26 +257,26 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, break; case 0x1b: /* SP */ - *v = &(d->sp); + *v = &(vm->reg[DCPU16_REG_SP]); TRACE("%s>> %s (0x%04x)", __func__, - "SP", + dcpu16_reg_names[DCPU16_REG_SP], **v); break; case 0x1c: /* PC */ - *v = &(d->pc); + *v = &(vm->reg[DCPU16_REG_PC]); TRACE("%s>> %s (0x%04x)", __func__, - "PC", + dcpu16_reg_names[DCPU16_REG_PC], **v); break; case 0x1d: /* EX */ - *v = &(d->ex); + *v = &(vm->reg[DCPU16_REG_EX]); TRACE("%s>> %s (0x%04x)", __func__, - "EX", + dcpu16_reg_names[DCPU16_REG_EX], **v); break; @@ -271,7 +284,7 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, *pc_adjust += 1; *cycle_adjust += 1; *e_addr = value_data; - *v = d->ram + *e_addr; + *v = vm->ram + *e_addr; TRACE("%s>> [nextword] [0x%04x] (0x%04x)", __func__, value_data, @@ -310,7 +323,7 @@ struct opcode_entry { /* opcode doesn't adjust its own PC, the step function which invoked it handles that */ /* opcode does adjust stack and cycle count */ -#define OP_IMPL(x) static void op_##x(struct dcpu16 *d, DCPU16_WORD val_b, DCPU16_WORD val_b_data, DCPU16_WORD val_a, DCPU16_WORD val_a_data) +#define OP_IMPL(x) static void op_##x(struct dcpu16 *vm, DCPU16_WORD val_b, DCPU16_WORD val_b_data, DCPU16_WORD val_a, DCPU16_WORD val_a_data) #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\ DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\ @@ -318,15 +331,15 @@ struct opcode_entry { unsigned int cycle_adjust = 0;\ do {\ op_type;\ - value_decode_(d, val_a, 1, val_a_data,\ - &d->reg_work_[1], &a, &ev_a_addr,\ + value_decode_(vm, val_a, 1, val_a_data,\ + &vm->reg_work_[1], &a, &ev_a_addr,\ &pc_adjust, &sp_adjust, &cycle_adjust);\ - d->sp += sp_adjust;\ - d->cycle += cycle_adjust;\ + vm->reg[DCPU16_REG_SP] += sp_adjust;\ + vm->cycle += cycle_adjust;\ } while (0) #define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data -#define OP_BASIC_ value_decode_(d, val_b, 0, val_b_data,\ - &d->reg_work_[0], &b, &ev_b_addr,\ +#define OP_BASIC_ value_decode_(vm, val_b, 0, val_b_data,\ + &vm->reg_work_[0], &b, &ev_b_addr,\ &pc_adjust, &sp_adjust, &cycle_adjust) #define OP_BASIC(x) OP_TYPE(OP_BASIC_) #define OP_NBI(x) OP_TYPE(OP_NBI_) @@ -337,9 +350,9 @@ struct opcode_entry { accounting helpers, these fire off the related callbacks for memory reads, memory writes, and execution of reserved instructions */ -#define ACCT_R(addr) do { acct_event_(d, DCPU16_ACCT_EV_READ, addr); } while (0) -#define ACCT_W(addr) do { acct_event_(d, DCPU16_ACCT_EV_WRITE, addr); } while (0) -#define ACCT_ILL(addr) do { acct_event_(d, DCPU16_ACCT_EV_NOP, addr); } while (0) +#define ACCT_R(addr) do { acct_event_(vm, DCPU16_ACCT_EV_READ, addr); } while (0) +#define ACCT_W(addr) do { acct_event_(vm, DCPU16_ACCT_EV_WRITE, addr); } while (0) +#define ACCT_ILL(addr) do { acct_event_(vm, DCPU16_ACCT_EV_NOP, addr); } while (0) /* extended opcodes */ @@ -355,10 +368,10 @@ OP_IMPL(nbi__reserved_) { /* reserved for future expansion */ /* fire an illegal instruction event for current instruction */ - DCPU16_WORD future_opcode = (d->ram[d->pc - pc_adjust] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS)); + DCPU16_WORD future_opcode = (vm->ram[vm->reg[DCPU16_REG_PC] - pc_adjust] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS)); WARN("reserved future opcode 0x%04x invoked", future_opcode); - ACCT_ILL(d->pc - pc_adjust); + ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust); } OP_IMPL(nbi_jsr) { @@ -367,12 +380,12 @@ OP_IMPL(nbi_jsr) { ACCT_R(ev_a_addr); - d->ram[ --d->sp ] = d->pc; - d->pc = *a; + vm->ram[ --vm->reg[DCPU16_REG_SP] ] = vm->reg[DCPU16_REG_PC]; + vm->reg[DCPU16_REG_PC] = *a; - d->cycle += 2; + vm->cycle += 2; - ACCT_W(d->sp + 1); + ACCT_W(vm->reg[DCPU16_REG_SP] + 1); } OP_IMPL(nbi__reserved2_) { @@ -381,7 +394,7 @@ OP_IMPL(nbi__reserved2_) { WARN("reserved nbi opcode invoked"); - ACCT_ILL(d->pc - pc_adjust); + ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust); } OP_IMPL(nbi_int) { @@ -389,29 +402,29 @@ OP_IMPL(nbi_int) { ACCT_R(ev_a_addr); - if (d->ia) { - if ( interrupt_enqueue_(d, *a) ) { + if (vm->reg[DCPU16_REG_IA]) { + if ( interrupt_enqueue_(vm, *a) ) { WARN("failed to queue interrupt"); return; } - if (d->interrupts_deferred_) + if (vm->interrupts_deferred_) return; - d->interrupts_deferred_ = 1; - d->ram[--d->sp] = d->pc; - d->ram[--d->sp] = d->reg[reg_index_('A')]; - d->pc = d->ia; - d->reg[0] = *a; + vm->interrupts_deferred_ = 1; + vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_PC]; + vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_A]; + vm->reg[DCPU16_REG_PC] = vm->reg[DCPU16_REG_IA]; + vm->reg[0] = *a; } - d->cycle += 4; + vm->cycle += 4; } OP_IMPL(nbi_iag) { OP_NBI(nbi_iag); - *a = d->ia; + *a = vm->reg[DCPU16_REG_IA]; ACCT_W(ev_a_addr); } @@ -419,7 +432,7 @@ OP_IMPL(nbi_iag) { OP_IMPL(nbi_ias) { OP_NBI(nbi_ias); - d->ia = *a; + vm->reg[DCPU16_REG_IA] = *a; ACCT_R(ev_a_addr); } @@ -428,18 +441,18 @@ OP_IMPL(nbi_ias) { OP_IMPL(nbi_rfi) { OP_NBI(nbi_rfi); - d->interrupts_deferred_ = 0; - d->reg[reg_index_('A')] = d->ram[d->sp++]; - d->pc = d->ram[d->sp++]; + vm->interrupts_deferred_ = 0; + vm->reg[DCPU16_REG_A] = vm->ram[vm->reg[DCPU16_REG_SP]++]; + vm->reg[DCPU16_REG_PC] = vm->ram[vm->reg[DCPU16_REG_SP]++]; } OP_IMPL(nbi_iaq) { OP_NBI(nbi_iaq); if (*a) { - d->interrupts_deferred_ = 1; + vm->interrupts_deferred_ = 1; } else { - d->interrupts_deferred_ = 0; + vm->interrupts_deferred_ = 0; } ACCT_R(ev_a_addr); @@ -450,9 +463,9 @@ OP_IMPL(nbi_hwn) { ACCT_W(ev_a_addr); - *a = d->hw_table_entries_; + *a = vm->hw_table_entries_; - d->cycle += 2; + vm->cycle += 2; } OP_IMPL(nbi_hwq) { @@ -460,23 +473,23 @@ OP_IMPL(nbi_hwq) { ACCT_R(ev_a_addr); - if (*a >= d->hw_table_entries_) { + if (*a >= vm->hw_table_entries_) { WARN("hardware query for non-extant device 0x%04x", *a); - d->reg[reg_index_('A')] = 0; - d->reg[reg_index_('B')] = 0; - d->reg[reg_index_('C')] = 0; - d->reg[reg_index_('X')] = 0; - d->reg[reg_index_('Y')] = 0; + vm->reg[DCPU16_REG_A] = 0; + vm->reg[DCPU16_REG_B] = 0; + vm->reg[DCPU16_REG_C] = 0; + vm->reg[DCPU16_REG_X] = 0; + vm->reg[DCPU16_REG_Y] = 0; return; } - d->reg[reg_index_('A')] = d->hw_table_[*a].id_l; - d->reg[reg_index_('B')] = d->hw_table_[*a].id_h; - d->reg[reg_index_('C')] = d->hw_table_[*a].ver; - d->reg[reg_index_('X')] = d->hw_table_[*a].mfg_l; - d->reg[reg_index_('Y')] = d->hw_table_[*a].mfg_h; + vm->reg[DCPU16_REG_A] = vm->hw_table_[*a].id_l; + vm->reg[DCPU16_REG_B] = vm->hw_table_[*a].id_h; + vm->reg[DCPU16_REG_C] = vm->hw_table_[*a].ver; + vm->reg[DCPU16_REG_X] = vm->hw_table_[*a].mfg_l; + vm->reg[DCPU16_REG_Y] = vm->hw_table_[*a].mfg_h; - d->cycle += 4; + vm->cycle += 4; } OP_IMPL(nbi_hwi) { @@ -484,13 +497,16 @@ OP_IMPL(nbi_hwi) { ACCT_R(ev_a_addr); - if (*a > d->hw_table_entries_) { + if (*a > vm->hw_table_entries_) { WARN("interrupt for non-extant device 0x%04x", *a); return; } - d->cycle += 4; - d->hw_table_[*a].int_fn(d, d->hw_table_[*a].data); + vm->cycle += 4; + if (vm->hw_table_[*a].hwi) + vm->hw_table_[*a].hwi(vm, vm->hw_table_[*a].data); + else + WARN("hardware 0x%04x has no interrupt handler", *a); } OP_IMPL(nbi_hcf) { @@ -498,10 +514,10 @@ OP_IMPL(nbi_hcf) { ACCT_R(ev_a_addr); - d->on_fire_ = 1; + vm->on_fire_ = 1; WARN("system on fire"); - d->cycle += 9; + vm->cycle += 9; } static const struct opcode_entry opcode_nbi_entries[] = { @@ -533,7 +549,7 @@ static const struct opcode_entry opcode_nbi_entries[] = { /* basic opcodes */ /* - N.B. the following function does not decode values, (thus does not advance sp) + N.B. the following function does not decode values. Decoding is handled by the secondary opcode functions it calls. */ OP_IMPL(_nbi_) { @@ -551,7 +567,7 @@ OP_IMPL(_nbi_) { assert(e->impl != NULL); TRACE(">> %s 0x%04x", e->name, val_b); - e->impl(d, 0, 0, val_a, val_a_data); + e->impl(vm, 0, 0, val_a, val_a_data); } OP_IMPL(set) { @@ -566,7 +582,7 @@ OP_IMPL(set) { */ *b = *a; - d->cycle += 1; + vm->cycle += 1; ACCT_W(ev_b_addr); } @@ -580,9 +596,9 @@ OP_IMPL(add) { ACCT_R(ev_a_addr); *b = acc; - d->ex = (acc > 0xffff); + vm->reg[DCPU16_REG_EX] = (acc > 0xffff); - d->cycle += 2; + vm->cycle += 2; ACCT_W(ev_b_addr); } @@ -596,9 +612,9 @@ OP_IMPL(sub) { ACCT_R(ev_a_addr); *b = acc; - d->ex = (acc > 0xffff); + vm->reg[DCPU16_REG_EX] = (acc > 0xffff); - d->cycle += 2; + vm->cycle += 2; ACCT_W(ev_b_addr); } @@ -612,9 +628,9 @@ OP_IMPL(mul) { ACCT_R(ev_a_addr); *b = acc; - d->ex = acc >> 16; + vm->reg[DCPU16_REG_EX] = acc >> 16; - d->cycle += 2; + vm->cycle += 2; ACCT_W(ev_b_addr); } @@ -628,9 +644,9 @@ OP_IMPL(mli) { ACCT_R(ev_a_addr); *b = acc; - d->ex = acc >> 16; + vm->reg[DCPU16_REG_EX] = acc >> 16; - d->cycle += 2; + vm->cycle += 2; ACCT_W(ev_b_addr); } @@ -644,13 +660,13 @@ OP_IMPL(div) { if (*a == 0) { *b = 0; - d->ex = 0; + vm->reg[DCPU16_REG_EX] = 0; } else { *b = *b / *a; - d->ex = (*b << 16) / *a; + vm->reg[DCPU16_REG_EX] = (*b << 16) / *a; } - d->cycle += 3; + vm->cycle += 3; ACCT_W(ev_b_addr); } @@ -664,13 +680,13 @@ OP_IMPL(dvi) { if (*a == 0) { *b = 0; - d->ex = 0; + vm->reg[DCPU16_REG_EX] = 0; } else { *b = (short)*b / (short)*a; - d->ex = (short)(*b << 16) / (short)*a; + vm->reg[DCPU16_REG_EX] = (short)(*b << 16) / (short)*a; } - d->cycle += 3; + vm->cycle += 3; ACCT_W(ev_b_addr); } @@ -688,7 +704,7 @@ OP_IMPL(mod) { *b = *b % *a; } - d->cycle += 3; + vm->cycle += 3; ACCT_W(ev_a_addr); } @@ -706,7 +722,7 @@ OP_IMPL(mdi) { *b = (short)*b % (short)*a; } - d->cycle += 3; + vm->cycle += 3; ACCT_W(ev_b_addr); } @@ -720,7 +736,7 @@ OP_IMPL(and) { *b = *b & *a; - d->cycle += 1; + vm->cycle += 1; ACCT_W(ev_b_addr); } @@ -734,7 +750,7 @@ OP_IMPL(bor) { *b = *b | *a; - d->cycle += 1; + vm->cycle += 1; ACCT_W(ev_b_addr); } @@ -748,7 +764,7 @@ OP_IMPL(xor) { *b = *b ^ *a; - d->cycle += 1; + vm->cycle += 1; ACCT_W(ev_b_addr); } @@ -762,9 +778,9 @@ OP_IMPL(shr) { ACCT_R(ev_a_addr); *b = acc & 0xffff; - d->ex = (*b << 16) >> *a; + vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a; - d->cycle += 2; + vm->cycle += 2; WARN("IMPLEMENT"); @@ -780,9 +796,9 @@ OP_IMPL(asr) { ACCT_R(ev_a_addr); *b = acc & 0xffff; - d->ex = (*b << 16) >> *a; + vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a; - d->cycle += 2; + vm->cycle += 2; WARN("IMPLEMENT"); @@ -799,9 +815,9 @@ OP_IMPL(shl) { *b = acc; - d->ex = acc >> 16; + vm->reg[DCPU16_REG_EX] = acc >> 16; - d->cycle += 2; + vm->cycle += 2; ACCT_W(ev_b_addr); } @@ -816,11 +832,11 @@ OP_IMPL(ifb) { if ((*b & *a) != 0) { /* */ } else { - d->skip_ = 1; - d->cycle += 1; + vm->skip_ = 1; + vm->cycle += 1; } - d->cycle += 2; + vm->cycle += 2; } OP_IMPL(ifc) { @@ -831,13 +847,13 @@ OP_IMPL(ifc) { ACCT_R(ev_a_addr); if ((*b & *a) == 0) { - + /* */ } else { - d->skip_ = 1; - d->cycle += 1; + vm->skip_ = 1; + vm->cycle += 1; } - d->cycle += 2; + vm->cycle += 2; } OP_IMPL(ife) { @@ -850,11 +866,11 @@ OP_IMPL(ife) { if (*b == *a) { /* */ } else { - d->skip_ = 1; - d->cycle += 1; + vm->skip_ = 1; + vm->cycle += 1; } - d->cycle += 2; + vm->cycle += 2; } OP_IMPL(ifn) { @@ -867,11 +883,11 @@ OP_IMPL(ifn) { if (*b != *a) { /* */ } else { - d->skip_ = 1; - d->cycle++; + vm->skip_ = 1; + vm->cycle++; } - d->cycle += 2; + vm->cycle += 2; } OP_IMPL(ifg) { @@ -884,11 +900,11 @@ OP_IMPL(ifg) { if (*b > *a) { /* */ } else { - d->skip_ = 1; - d->cycle++; + vm->skip_ = 1; + vm->cycle++; } - d->cycle += 2; + vm->cycle += 2; } OP_IMPL(ifa) { @@ -901,11 +917,11 @@ OP_IMPL(ifa) { if (*b > *a) { /* */ } else { - d->skip_ = 1; - d->cycle += 1; + vm->skip_ = 1; + vm->cycle += 1; } - d->cycle += 2; + vm->cycle += 2; } OP_IMPL(ifl) { @@ -918,11 +934,11 @@ OP_IMPL(ifl) { if (*b < *a) { /* */ } else { - d->skip_ = 1; - d->cycle++; + vm->skip_ = 1; + vm->cycle++; } - d->cycle += 2; + vm->cycle += 2; } OP_IMPL(ifu) { @@ -935,11 +951,11 @@ OP_IMPL(ifu) { if (*b < *a) { /* */ } else { - d->skip_ = 1; - d->cycle += 1; + vm->skip_ = 1; + vm->cycle += 1; } - d->cycle += 2; + vm->cycle += 2; } OP_IMPL(adx) { @@ -950,14 +966,14 @@ OP_IMPL(adx) { ACCT_R(ev_b_addr); ACCT_R(ev_a_addr); - acc = *b + *a + d->ex; + acc = *b + *a + vm->reg[DCPU16_REG_EX]; *b = acc & 0xffff; if (acc > 0xffff) - d->ex = 0x0001; + vm->reg[DCPU16_REG_EX] = 0x0001; else - d->ex = 0x0000; + vm->reg[DCPU16_REG_EX] = 0x0000; - d->cycle += 3; + vm->cycle += 3; ACCT_W(ev_b_addr); } @@ -970,14 +986,14 @@ OP_IMPL(sbx) { ACCT_R(ev_b_addr); ACCT_R(ev_a_addr); - acc = *b - *a + d->ex; + acc = *b - *a + vm->reg[DCPU16_REG_EX]; *b = acc & 0xffff; if (acc > 0xffff) - d->ex = 0xffff; + vm->reg[DCPU16_REG_EX] = 0xffff; else - d->ex = 0; + vm->reg[DCPU16_REG_EX] = 0; - d->cycle += 3; + vm->cycle += 3; ACCT_W(ev_b_addr); } @@ -990,10 +1006,10 @@ OP_IMPL(sti) { ACCT_R(ev_a_addr); *b = *a; - d->reg[reg_index_('I')] += 1; - d->reg[reg_index_('J')] += 1; + vm->reg[DCPU16_REG_I] += 1; + vm->reg[DCPU16_REG_J] += 1; - d->cycle += 2; + vm->cycle += 2; ACCT_W(ev_b_addr); } @@ -1006,10 +1022,10 @@ OP_IMPL(std) { ACCT_R(ev_a_addr); *b = *a; - d->reg[reg_index_('I')] -= 1; - d->reg[reg_index_('J')] -= 1; + vm->reg[DCPU16_REG_I] -= 1; + vm->reg[DCPU16_REG_J] -= 1; - d->cycle += 2; + vm->cycle += 2; ACCT_W(ev_b_addr); } @@ -1019,7 +1035,7 @@ OP_IMPL(_reserved_) { WARN("reserved opcode invoked"); - ACCT_ILL(d->pc - pc_adjust); + ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust); } static const struct opcode_entry opcode_basic_entries[] = { @@ -1061,11 +1077,11 @@ static const struct opcode_entry opcode_basic_entries[] = { static inline void dump_operand_value_(DCPU16_WORD value, DCPU16_WORD nextword, unsigned int value_position) { if (value <= 0x07) { - printf(" %c", regnames_[value]); + printf(" %s", dcpu16_reg_names[value]); } else if (value <= 0x0f) { - printf(" [%c]", regnames_[value & 0x07]); + printf(" [%s]", dcpu16_reg_names[value & 0x07]); } else if (value <= 0x17) { - printf(" [0x%04x + %c]", nextword, regnames_[value & 0x07]); + printf(" [0x%04x + %s]", nextword, dcpu16_reg_names[value & 0x07]); } else switch (value) { case 0x18: if (value_position == 0) { /* b */ @@ -1160,13 +1176,13 @@ DCPU16_WORD dcpu16_mnemonify_buf(DCPU16_WORD *buf) { print the words of the instruction at addr, followed by its assembly representation returns the length of the instruction in words */ -DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) { +DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *vm, DCPU16_WORD addr) { DCPU16_WORD opcode, b, a, instr_len, i, *b_data, *a_data; - DCPU16_WORD buf[3] = { d->ram[addr], d->ram[(DCPU16_WORD)(addr + 1)], d->ram[(DCPU16_WORD)(addr + 2)] }; + DCPU16_WORD buf[3] = { vm->ram[addr], vm->ram[(DCPU16_WORD)(addr + 1)], vm->ram[(DCPU16_WORD)(addr + 2)] }; unsigned int indent = 0; unsigned int partial = 0; - if (!d) return 0; + if (!vm) return 0; #if 0 /* @@ -1175,7 +1191,7 @@ DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) { could be data which happen to match instructions.. */ for (i = 3; i; i--) { - instruction_decode_(d->ram, (DCPU16_WORD)(addr - i), &opcode, &b, &b_data, &a, &a_data, &instr_len); + instruction_decode_(vm->ram, (DCPU16_WORD)(addr - i), &opcode, &b, &b_data, &a, &a_data, &instr_len); if (instr_len > i) partial++; if (instr_len == i @@ -1187,12 +1203,12 @@ DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) { #endif /* just need instr_len */ - instruction_decode_(d->ram, addr, &opcode, &b, &b_data, &a, &a_data, &instr_len); + instruction_decode_(vm->ram, addr, &opcode, &b, &b_data, &a, &a_data, &instr_len); /* show the raw words */ - printf("%04x", d->ram[addr]); + printf("%04x", vm->ram[addr]); for (i = 1; i < instr_len; i++) { - printf(" %04x", d->ram[addr + i]); + printf(" %04x", vm->ram[addr + i]); } /* align things neatly, show the instruction */ @@ -1207,64 +1223,78 @@ DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) { return instr_len; } +int dcpu16_interrupt(struct dcpu16 *vm, DCPU16_WORD message) { + TRACE("%s>> message:0x%04x", __func__, message); + return interrupt_enqueue_(vm, message); +} + /* execute the next instruction */ -void dcpu16_step(struct dcpu16 *d) { +void dcpu16_step(struct dcpu16 *vm) { DCPU16_WORD opcode, b, a, instr_len, *b_data, *a_data; + size_t i; const struct opcode_entry *e; - if (!d) return; - - acct_event_(d, DCPU16_ACCT_EV_CYCLE, d->pc); + if (!vm) return; - /* if we're currently servicing interrupts */ - if (d->interrupts_deferred_ == 0) { - /* and there are interrupts to be serviced */ - if (d->interrupts_head_ != d->interrupts_tail_) { - DCPU16_WORD message; - message = interrupt_dequeue_(d); + /* signal interested parties that a new cycle has ticked */ + TRACE("%s: sending global cycle event", __func__); + acct_event_(vm, DCPU16_ACCT_EV_CYCLE, vm->reg[DCPU16_REG_PC]); - if (d->ia) { - TRACE("servicing interrupt IA:0x%04x message:0x%04x \n", d->ia, message); - /* then service the next interrupt */ - d->interrupts_deferred_ = 1; - d->ram[--d->sp] = d->pc; - d->ram[--d->sp] = d->reg[reg_index_('A')]; - d->pc = d->ia; - d->reg[0] = message; - } else { - TRACE("ignoring interrupt IA:0"); - } + /* signal attached hardware */ + for (i = 0; i < vm->hw_table_entries_; i++) { + if (vm->hw_table_[i].cycle) { + TRACE("%s: sending cycle to %s", __func__, vm->hw_table_[i].name_); + vm->hw_table_[i].cycle(vm, vm->hw_table_[i].data); } } - /* and make sure to execute an instruction after an interrupt */ + instruction_decode_(vm->ram, vm->reg[DCPU16_REG_PC], &opcode, &b, &b_data, &a, &a_data, &instr_len); - instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len); + /* consume what we decoded */ + /* this happens immediately as PC might be re-set as an operation */ + vm->reg[DCPU16_REG_PC] += instr_len; + /* run the operation */ for (e = opcode_basic_entries; e->impl; e++) { if (e->value == opcode) { TRACE("%s>> %s 0x%04x, 0x%04x", __func__, e->name, b, a); - e->impl(d, b, b_data ? *b_data : 0, a, a_data ? *a_data : 0); + e->impl(vm, b, b_data ? *b_data : 0, a, a_data ? *a_data : 0); break; } } - /* get ready for the next one */ - d->pc += instr_len; - /* and jump over next instr(s) if needed */ - if (d->skip_) { - instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len); - d->pc += instr_len; + while (vm->skip_) { + instruction_decode_(vm->ram, vm->reg[DCPU16_REG_PC], &opcode, &b, &b_data, &a, &a_data, &instr_len); + vm->reg[DCPU16_REG_PC] += instr_len; TRACE("++ SKIPPED %x words", instr_len); if (opcode >= 0x10 && opcode <= 0x17) { /* skipping a branch instruction? skip branch's skippable instruction as well */ - d->cycle += 1; - instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len); - d->pc += instr_len; - TRACE("++ SKIPPED %x words", instr_len); + vm->cycle += 1; + } else { + vm->skip_ = 0; + } + } + + /* if we're currently servicing interrupts */ + if (vm->interrupts_deferred_ == 0) { + /* and there are interrupts to be serviced */ + if (vm->interrupts_head_ != vm->interrupts_tail_) { + DCPU16_WORD message; + message = interrupt_dequeue_(vm); + + if (vm->reg[DCPU16_REG_IA]) { + TRACE("servicing interrupt IA:0x%04x message:0x%04x \n", vm->reg[DCPU16_REG_IA], message); + /* then service the next interrupt */ + vm->interrupts_deferred_ = 1; + vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_PC]; + vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_A]; + vm->reg[DCPU16_REG_PC] = vm->reg[DCPU16_REG_IA]; + vm->reg[DCPU16_REG_A] = message; + } else { + TRACE("ignoring interrupt IA:0"); + } } - d->skip_ = 0; } } @@ -1272,41 +1302,41 @@ void dcpu16_step(struct dcpu16 *d) { print the current state of the machine shows current cycle count, registers, and next instruction */ -void dcpu16_state_print(struct dcpu16 *d) { +void dcpu16_state_print(struct dcpu16 *vm) { unsigned int i; - if (!d) return; + if (!vm) return; printf(" "); for (i = 0; i < 8; i++) - printf(" %c:0x%04x", regnames_[i], d->reg[i]); + printf(" %s:0x%04x", dcpu16_reg_names[i], vm->reg[i]); printf("\n"); printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:", - d->cycle, - "EX", d->ex, - "SP", d->sp, - "PC", d->pc, - "IA", d->ia, + vm->cycle, + dcpu16_reg_names[DCPU16_REG_EX], vm->reg[DCPU16_REG_EX], + dcpu16_reg_names[DCPU16_REG_SP], vm->reg[DCPU16_REG_SP], + dcpu16_reg_names[DCPU16_REG_PC], vm->reg[DCPU16_REG_PC], + dcpu16_reg_names[DCPU16_REG_IA], vm->reg[DCPU16_REG_IA], "PC"); - dcpu16_disassemble_print(d, d->pc); + dcpu16_disassemble_print(vm, vm->reg[DCPU16_REG_PC]); printf("\n"); } /* dcpu16_dump_ram * print raw ram contents from start to stop */ -void dcpu16_dump_ram(struct dcpu16 *d, DCPU16_WORD start, DCPU16_WORD end) { +void dcpu16_dump_ram(struct dcpu16 *vm, DCPU16_WORD start, DCPU16_WORD end) { unsigned int i, j; const unsigned int n = 8; /* words per line */ - if (!d) return; + if (!vm) return; for (i = start, j = 0; i <= end; i++, j++) { if (j % n == 0) printf("0x%04x:\t", i); - printf(" %04x%s", d->ram[i], (j % n) == (n - 1) ? "\n" : ""); + printf(" %04x%s", vm->ram[i], (j % n) == (n - 1) ? "\n" : ""); } if ((j % n) != (n - 1)) printf("\n"); @@ -1319,6 +1349,11 @@ int dcpu16_hw_add(struct dcpu16 *vm, struct dcpu16_hw *hw) { if (!vm || !hw) return -1; + if (vm->hw_table_entries_ == 0xffff) { + WARN("maximum hardware entries reached"); + return -1; + } + if (vm->hw_table_entries_ == vm->hw_table_allocated_) { size_t new_entries = vm->hw_table_allocated_ + 32; void *tmp_ptr = realloc(vm->hw_table_, new_entries * sizeof * (vm->hw_table_)); @@ -1343,6 +1378,9 @@ int dcpu16_hw_add(struct dcpu16 *vm, struct dcpu16_hw *hw) { int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event mask, dcpu16_ev_cb_t *fn, void *data) { struct dcpu16_acct_cb cb; + if (!vm) + return -1; + cb.mask = mask; cb.fn = fn; cb.data = data; @@ -1367,19 +1405,30 @@ int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event mask, dcpu16_ev_cb_t *f /* dcpu16_reset * signals cpu to reset, clearing runstate and ram, then reload any init callbacks */ -void dcpu16_reset(struct dcpu16 *d) { - if (!d) return; - - d->cycle = 0; - memset(d->reg, 0, sizeof d->reg); - d->pc = 0; - d->sp = 0; - d->ex = 0; - d->ia = 0; - d->skip_ = 0; - memset(d->ram, 0, sizeof d->ram); - - acct_event_(d, DCPU16_ACCT_EV_RESET, 0); +void dcpu16_reset(struct dcpu16 *vm) { + size_t i; + + if (!vm) + return; + + vm->skip_ = 0; + vm->interrupts_deferred_ = 0; + vm->on_fire_ = 0; + memset(vm->interrupts_, 0, sizeof vm->interrupts_); + vm->interrupts_head_ = 0; + vm->interrupts_tail_ = 0; + + /* signal attached hardware */ + for (i = 0; i < vm->hw_table_entries_; i++) { + if (vm->hw_table_[i].reset) + vm->hw_table_[i].reset(vm, vm->hw_table_[i].data); + } + + memset(vm->reg, 0, sizeof vm->reg); + memset(vm->ram, 0, sizeof vm->ram); + vm->cycle = 0; + + acct_event_(vm, DCPU16_ACCT_EV_RESET, 0); } /* dcpu16_new @@ -1392,6 +1441,9 @@ struct dcpu16 *dcpu16_new(void) { if (vm == NULL) WARN("%s: %s(%zu): %s", __func__, "calloc", strerror(errno)); + vm->warn_cb_ = warn_cb_; + vm->trace_cb_ = trace_cb_; + return vm; } @@ -1399,7 +1451,8 @@ struct dcpu16 *dcpu16_new(void) { * release a dcpu16 instance */ void dcpu16_delete(struct dcpu16 **vm) { - if (!vm || !*vm) return; + if (!vm || !*vm) + return; free(*vm); *vm = NULL; diff --git a/dcpu16.h b/dcpu16.h index 35c1fe8..f2bf787 100644 --- a/dcpu16.h +++ b/dcpu16.h @@ -10,8 +10,28 @@ typedef unsigned short DCPU16_WORD; #define DCPU16_RAM 0x10000 #define DCPU16_INTERRUPT_QUEUE_SIZE 256 +extern const char * const dcpu16_reg_names[]; +enum dcpu16_register_indexes { + DCPU16_REG_A = 0, + DCPU16_REG_B, + DCPU16_REG_C, + DCPU16_REG_X, + DCPU16_REG_Y, + DCPU16_REG_Z, + DCPU16_REG_I, + DCPU16_REG_J, + DCPU16_REG_PC, + DCPU16_REG_SP, + DCPU16_REG_EX, + DCPU16_REG_IA, + DCPU16_REG__NUM +}; + /* a self-contained dcpu16 core */ struct dcpu16 { + void (*warn_cb_)(char *fmt, ...); + void (*trace_cb_)(char *fmt, ...); + struct dcpu16_acct_cb *cb_table_; /* list of callbacks to invoke for certain events */ size_t cb_table_entries_; /* callback list maintenance */ size_t cb_table_allocated_; /* callback list maintenance */ @@ -29,11 +49,7 @@ struct dcpu16 { size_t interrupts_tail_; /* interrupt queue maintenance */ unsigned long long cycle; /* number of cycles this core has executed */ - DCPU16_WORD reg[8]; /* system registers, a b c x y z i j */ - DCPU16_WORD pc; /* program counter */ - DCPU16_WORD sp; /* stack pointer */ - DCPU16_WORD ex; /* overflow */ - DCPU16_WORD ia; /* interrupt address */ + DCPU16_WORD reg[DCPU16_REG__NUM]; /* system registers, a b c x y z i j */ DCPU16_WORD ram[DCPU16_RAM]; /* memory */ }; @@ -51,15 +67,21 @@ struct dcpu16_acct_cb { dcpu16_acct_event mask; }; +typedef void (dcpu16_hw_signal_t)(struct dcpu16 *, void *); /* these are used to define hardware attached to the system */ struct dcpu16_hw { + struct dcpu16 *vm; /* which system do I belong to */ char *name_; + DCPU16_WORD id_l; DCPU16_WORD id_h; DCPU16_WORD ver; DCPU16_WORD mfg_l; DCPU16_WORD mfg_h; - void (*int_fn)(struct dcpu16 *, void *); + + dcpu16_hw_signal_t *hwi; + dcpu16_hw_signal_t *cycle; + dcpu16_hw_signal_t *reset; void *data; }; @@ -93,6 +115,8 @@ void dcpu16_step(struct dcpu16 *); /* release a core */ void dcpu16_delete(struct dcpu16 **); +int dcpu16_interrupt(struct dcpu16 *, DCPU16_WORD); + /* register callbacks to handle warning and debug messages, default is writing to stderr, may be set to null */ void dcpu16_warn_cb_set(void (*)(char *, ...)); void dcpu16_trace_cb_set(void (*)(char *, ...)); diff --git a/display.c b/display.c deleted file mode 100644 index 0fbfbf5..0000000 --- a/display.c +++ /dev/null @@ -1,499 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_LIBPNG -#include -#include -#endif /* HAVE_LIBPNG */ - -#include "dcpu16.h" -#include "display.h" -#include "chargen-4x8.h" - -/* - preliminary attempt at handling display - - currently keeps raw display output in a pixel buffer, which is updated on every write to cpu's memory in display-space. - every [n] cycles, display buffer is rendered into an image file (png or pnm) in memory - - TODO: blinking -*/ - -#define DISPLAY_BASE 0x8000 -#define DISPLAY_END 0x8179 -#define DISPLAY_CELL_MAP 0x8180 -#define DISPLAY_MISC 0x8280 - -#define CELL_X 32 -#define CELL_Y 12 - -#define CELL_X_SZ 4 -#define CELL_Y_SZ 8 - -#define PIX_X 160 -#define PIX_Y 128 -#define PIX_BORDER 16 - -/* total display is 160 pixels by 128 pixels */ -/* active display is 32x12 cells (each 4x8 pixels), surrounded by 16 pixel border */ -/* cells are rendered from cell map, which is bitmap of two words, defining four eight-bit columns */ - -struct pixel_ { - unsigned char r; - unsigned char g; - unsigned char b; -}; - -/* buf will hold image file in memory */ -struct memstream_cookie_ { - char *buf; - size_t allocated; - size_t endpos; - off_t offset; - size_t chunk_size; -}; - -struct dcpu16_display_ { - char *outfile; - struct pixel_ *pixbuf; /* raw pixels */ - struct memstream_cookie_ memstream_cookie; /* streambuffer contains rendered image file from pixbuf */ - unsigned long long cycle_last_write; - unsigned int pixbuf_dirty : 1; - unsigned int file_dirty : 1; -}; - -static inline -struct pixel_ pcolor_(unsigned int c) { - struct pixel_ p = { 0, 0, 0 }; - - switch (c) { - case 0x1: p.r=0x00, p.g=0x00, p.b=0xaa; break; /* dark blue */ - case 0x2: p.r=0x00, p.g=0xaa, p.b=0x00; break; /* green */ - case 0x3: p.r=0x00, p.g=0xaa, p.b=0xaa; break; /* cyan */ - case 0x4: p.r=0xaa, p.g=0x00, p.b=0x00; break; /* red */ - case 0x5: p.r=0xaa, p.g=0x00, p.b=0xaa; break; /* magenta */ - case 0x6: p.r=0xaa, p.g=0xaa, p.b=0x55; break; /* yellow [] */ - case 0x7: p.r=0xaa, p.g=0xaa, p.b=0xff; break; /* pale blue */ - case 0x8: p.r=0x55, p.g=0x55, p.b=0x55; break; /* grey */ - case 0x9: p.r=0x55, p.g=0x55, p.b=0xff; break; /* also blue */ - case 0xa: p.r=0x55, p.g=0xff, p.b=0x55; break; /* light green */ - case 0xb: p.r=0x55, p.g=0xff, p.b=0xff; break; /* light cyan */ - case 0xc: p.r=0xff, p.g=0x55, p.b=0x55; break; /* light red */ - case 0xd: p.r=0xff, p.g=0x55, p.b=0xff; break; /* light magenta */ - case 0xe: p.r=0xff, p.g=0xff, p.b=0x55; break; /* light yellow */ - case 0xf: p.r=0xff, p.g=0xff, p.b=0xff; break; /* white */ - default: p.r=0x00, p.g=0x00, p.b=0x00; /* black */ - } - - return p; -} - -/* support functions for writing streams into memory buffer */ -static -void memstream_cookie_init_(struct memstream_cookie_ *c, size_t chunk_size) { - assert(c); - assert(chunk_size); - - c->buf = NULL; - c->allocated = 0; - c->endpos = 0; - c->offset = 0; - c->chunk_size = chunk_size; -} - -static -void memstream_cookie_fini_(struct memstream_cookie_ *c) { - assert(c); - - free(c->buf); - c->allocated = c->endpos = c->offset = 0; -} - -static -/* of course these can't be the same */ -#ifdef HAVE_FOPENCOOKIE -ssize_t memstream_write_fn_(void *c, const char *buf, size_t size) -#else /* HAVE_FOPENCOOKIE */ -int memstream_write_fn_(void *c, const char *buf, int size) -#endif /* HAVE_FOPENCOOKIE */ -{ - struct memstream_cookie_ *cookie = c; - - while ((size_t)size + cookie->offset > cookie->allocated) { - size_t new_allocated = cookie->allocated + cookie->chunk_size; - void *tmp_ptr = realloc(cookie->buf, new_allocated); - if (tmp_ptr == NULL) { - return -1; - } - cookie->buf = tmp_ptr; - cookie->allocated = new_allocated; - } - - memcpy(cookie->buf + cookie->offset, buf, size); - cookie->offset += size; - if (cookie->offset > (off_t)cookie->endpos) { - cookie->endpos = cookie->offset; - } - - return size; -} - -/* should this just flood-fill entire display? */ -static inline -void display_draw_border_(struct pixel_ *pixbuf, struct pixel_ color) { - size_t x, y; - size_t i; - - /* top */ - for (y = 0; y < PIX_BORDER; y++) { - for (x = 0; x < PIX_X; x++) { - i = (y * PIX_X) + x; - pixbuf[i] = color; - i = ((PIX_Y - y) * PIX_X) + x; - pixbuf[i] = color; - } - } - - /* sides */ - for (y = PIX_BORDER; y < (PIX_Y - PIX_BORDER + 1); y++) - for (x = 0; x < PIX_BORDER; x++) { - i = (y * PIX_X) + x; - pixbuf[i] = color; - pixbuf[i + (PIX_X - PIX_BORDER)] = color; - } - -} - -/* render a character cell to the display */ -static inline -void display_draw_cell_(struct pixel_ *pixbuf, DCPU16_WORD *cell_map, DCPU16_WORD index, int cell_x, int cell_y, struct pixel_ fg, struct pixel_ bg) { - struct pixel_ *cellstart = pixbuf; /* start of display */ - unsigned int pix_x, pix_y; - unsigned char *cell_bitmap = (unsigned char *)(cell_map + (index * sizeof index)); - - assert(cell_x < CELL_X); - assert(cell_y < CELL_Y); - - cellstart += (PIX_X * PIX_BORDER); /* skip top border */ - - cellstart += (CELL_Y_SZ * PIX_X) * cell_y; /* skip down to row */ - - cellstart += PIX_BORDER; /* skip side border */ - - cellstart += (CELL_X_SZ) * cell_x; /* skip to column */ - - for (pix_x = 0; pix_x < CELL_X_SZ; pix_x++) { - for (pix_y = 0; pix_y < CELL_Y_SZ; pix_y++) { - if ((cell_bitmap[pix_x] >> pix_y) & 0x01) - cellstart[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = fg; - else - cellstart[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = bg; - } - } -} - -#ifdef HAVE_LIBPNG -/* write status callback */ -static -void display_png_write_row_cb_(png_structp png_ptr, png_uint_32 row, int pass) { - (void)png_ptr, (void)row, (void)pass; - /* - fprintf(stderr, "%s:%u:%d\n", __func__, row, pass); - */ -} - -static -void display_png_user_error_fn_(png_structp png_ptr, png_const_charp error_msg) { - (void)png_ptr; - fprintf(stderr, "PNG:ERROR:%s\n", error_msg); - exit(EX_SOFTWARE); -} - -static -void display_png_user_warning_fn_(png_structp png_ptr, png_const_charp warning_msg) { - (void)png_ptr; - fprintf(stderr, "PNG:WARNING:%s\n", warning_msg); - return; -} - -/* write png file */ -static -void display_png_write_(struct dcpu16_display_ *d) { - FILE *f; - png_structp png_ptr; - png_infop info_ptr; - size_t i; - - png_voidp user_error_ptr = NULL; - -#ifdef HAVE_FOPENCOOKIE - /* linux-style memory stream */ - cookie_io_functions_t cookie_io_functions = { - .read = NULL, - .write = memstream_write_fn_, - .seek = NULL, - .close = NULL - }; - f = fopencookie(&d->memstream_cookie, "wb", cookie_io_functions); - if (f == NULL) { - fprintf(stderr, "%s():%s\n", "fopencookie", strerror(errno)); - return; - } - -#else /* HAVE_FOPENCOOKIE */ -#ifdef HAVE_FUNOPEN - /* BSD-style memory stream */ - f = funopen(&d->memstream_cookie, - NULL, /* don't care about read */ - memstream_write_fn_, - NULL, /* don't care about seek */ - NULL /* don't care about close */ - ); - if (f == NULL) { - fprintf(stderr, "%s():%s\n", "funopen", strerror(errno)); - return; - } - -#else /* HAVE_FUNOPEN */ - /* default to writing file if we can't buffer in memory */ - f = fopen(d->outfile, "wb"); - if (f == NULL) { - return; - } -#endif /* HAVE_FUNOPEN */ -#endif /* HAVE_FOPENCOOKIE */ - - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, user_error_ptr, display_png_user_error_fn_, display_png_user_warning_fn_); - if (png_ptr == NULL) { - goto f_done; - } - - info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) { - png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - goto f_done; - } - - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_write_struct(&png_ptr, &info_ptr); - goto f_done; -} - - png_init_io(png_ptr, f); - png_set_write_status_fn(png_ptr, display_png_write_row_cb_); - - /* png_set_filter(png_ptr, 0, PNG_FILTER_NONE); */ - /* png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); */ - - png_set_IHDR(png_ptr, info_ptr, PIX_X, PIX_Y, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - - png_write_info(png_ptr, info_ptr); - - for (i = 0; i < PIX_Y; i++) { - png_write_row(png_ptr, (unsigned char *)(d->pixbuf + (i * PIX_X))); - } - - png_write_end(png_ptr, info_ptr); - - png_destroy_write_struct(&png_ptr, &info_ptr); - -f_done: - fclose(f); -} -#else /* HAVE_LIBPNG */ - -/* if PNG not available, just write pnm file */ -static -void display_pnm_write_(struct dcpu16_display_ *d) { - size_t i; - FILE *f; - - f = fopen(d->outfile, "w"); - if (f == NULL) { - fprintf(stderr, "%s('%s'):%s\n", "fopen", filename, strerror(errno)); - return; - } - - /* write header... */ - /* PNM binary */ - /* x y */ - /* max value */ - fprintf(f, "P6\n%d %d\n255\n", PIX_X, PIX_Y); - - /* write out image bytes in r g b order */ - for (i = 0; i < PIX_X * PIX_Y; i++) { - fwrite(&d->pixbuf[i].r, 1, 1, f); - fwrite(&d->pixbuf[i].g, 1, 1, f); - fwrite(&d->pixbuf[i].b, 1, 1, f); - } - - fclose(f); -} - -#endif /* HAVE_LIBPNG */ - -static -void display_image_render_(struct dcpu16_display_ *d) { -#ifdef HAVE_LIBPNG - display_png_write_(d); -#else /* HAVE_LIBPNG */ - display_pnm_write_(d); -#endif /* HAVE_LIBPNG */ - d->file_dirty = 1; -} - - -/* the callback to register to be run on display init/reset */ -/* currently this populates the chargen map 'from rom'.. */ -/* and clears the display buffers */ -void display_reset_fn(struct dcpu16 *vm, dcpu16_acct_event e, DCPU16_WORD addr, void *data) { - struct dcpu16_display_ *d = (struct dcpu16_display_ *)data; - - (void)e, (void)addr; - - memcpy(vm->ram + DISPLAY_CELL_MAP, chargen_4x8_glyphs, sizeof chargen_4x8_glyphs); - - memset(vm->ram + DISPLAY_BASE, 0, (DISPLAY_END - DISPLAY_BASE) * sizeof *(vm->ram)); - - memset(d->pixbuf, 0, PIX_X * PIX_Y * sizeof *(d->pixbuf)); - - d->pixbuf_dirty = 1; -} - -/* the callback to register with the cpu for occasionally doing something with the display buffer */ -void display_cycle_fn(struct dcpu16 *vm, dcpu16_acct_event e, DCPU16_WORD addr, void *data) { - struct dcpu16_display_ *d = (struct dcpu16_display_ *)data; - - const unsigned long long min_cycles_between_writes = 1536; - const time_t min_seconds_between_file_write = 5; - - static time_t last_file_write_ = 0; - time_t now; - - (void)e, (void)addr; - - if (d->pixbuf_dirty) { - if (vm->cycle > d->cycle_last_write + min_cycles_between_writes) { - display_image_render_(d); - d->pixbuf_dirty = 0; - d->cycle_last_write = vm->cycle; - } - } - - if (d->file_dirty) { - now = time(NULL); - if (now > last_file_write_ + min_seconds_between_file_write) { - FILE *of; - - of = fopen(d->outfile, "wb"); - if (of == NULL) { - fprintf(stderr, "%s('%s'):%s\n", "fopen", d->outfile, strerror(errno)); - return; - } - - fwrite(d->memstream_cookie.buf, d->memstream_cookie.endpos, 1, of); - fclose(of); - - d->file_dirty = 0; - last_file_write_ = now; - } - } -} - -/* the callback to register with the cpu for watching memory updates */ -/* 'data' is an allocated display buffer */ -void display_fn(struct dcpu16 *vm, dcpu16_acct_event e, DCPU16_WORD addr, void *data) { - DCPU16_DISPLAY *d = (DCPU16_DISPLAY *)data; - unsigned char index, blink, bg, fg; - unsigned int cell_x, cell_y; - - (void)e; - - if (addr < DISPLAY_BASE || addr > DISPLAY_MISC) - return; - - if (addr > DISPLAY_END && addr < DISPLAY_MISC) { - unsigned char updated_index = addr - DISPLAY_CELL_MAP; - /* a cell map was updated, refresh entire screen */ - for (cell_x = 0; cell_x < CELL_X; cell_x++) { - for (cell_y = 0; cell_y < CELL_Y; cell_y++) { - addr = DISPLAY_BASE + cell_x + (cell_y * CELL_X); - index = vm->ram[addr] & 0x7f; - if (index != updated_index) - continue; - blink = (vm->ram[addr] >> 7) & 0x01; - bg = (vm->ram[addr] >> 8) & 0x0f; - fg = (vm->ram[addr] >> 12) & 0x0f; - display_draw_cell_(d->pixbuf, vm->ram + DISPLAY_CELL_MAP, index, cell_x, cell_y, pcolor_(fg), pcolor_(bg)); - } - } - - d->pixbuf_dirty = 1; - return; - } - - if (addr == DISPLAY_MISC) { - /* new border color */ - char border = vm->ram[addr] & 0x0f; - display_draw_border_(d->pixbuf, pcolor_(border)); - - d->pixbuf_dirty = 1; - return; - } - - cell_x = (addr - DISPLAY_BASE) % CELL_X; - cell_y = (addr - DISPLAY_BASE) / CELL_X; - - index = vm->ram[addr] & 0x7f; - blink = (vm->ram[addr] >> 7) & 0x01; - bg = (vm->ram[addr] >> 8) & 0x0f; - fg = (vm->ram[addr] >> 12) & 0x0f; - - display_draw_cell_(d->pixbuf, vm->ram + DISPLAY_CELL_MAP, index, cell_x, cell_y, pcolor_(fg), pcolor_(bg)); - - d->pixbuf_dirty = 1; -} - -/* init the pixel buffer */ -DCPU16_DISPLAY *display_new(const char *filename) { - DCPU16_DISPLAY *d = calloc(1, sizeof *d); - if (d == NULL) - return NULL; - - d->pixbuf = calloc(PIX_X * PIX_Y, sizeof *(d->pixbuf)); - if (d->pixbuf == NULL) { - free(d); - return NULL; - } - - d->outfile = strdup(filename); - if (d->outfile == NULL) { - free(d->pixbuf); - free(d); - return NULL; - } - - memstream_cookie_init_(&d->memstream_cookie, 1024 * 8); - - d->cycle_last_write = 0; - d->pixbuf_dirty = 0; - d->file_dirty = 0; - - return d; -} - -void display_free(DCPU16_DISPLAY *d) { - if (d) { - free(d->pixbuf); - d->pixbuf = NULL; - free(d->outfile); - d->outfile = NULL; - memstream_cookie_fini_(&d->memstream_cookie); - } - free(d); -} diff --git a/display.h b/display.h deleted file mode 100644 index fe88cc3..0000000 --- a/display.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef DISPLAY_H_MJYI1IAV -#define DISPLAY_H_MJYI1IAV - -#include "dcpu16.h" - -typedef struct dcpu16_display_ DCPU16_DISPLAY; - -void display_reset_fn(struct dcpu16 *, dcpu16_acct_event, DCPU16_WORD, void *); -void display_cycle_fn(struct dcpu16 *, dcpu16_acct_event, DCPU16_WORD, void *); -void display_fn(struct dcpu16 *, dcpu16_acct_event, DCPU16_WORD, void *); -DCPU16_DISPLAY *display_new(const char *); -void display_free(DCPU16_DISPLAY *); - -#endif /* DISPLAY_H_MJYI1IAV */ diff --git a/hw_clock.c b/hw_clock.c new file mode 100644 index 0000000..89d5912 --- /dev/null +++ b/hw_clock.c @@ -0,0 +1,112 @@ +#include +#include +#include + +#include "dcpu16.h" +#include "hw_clock.h" + +static dcpu16_hw_signal_t clock_reset_; +static dcpu16_hw_signal_t clock_cycle_; +static dcpu16_hw_signal_t clock_hwi_; +static struct dcpu16_hw hw_ = { + .name_ = "Generic Clock (compatible)", + .id_l = 0xb402, + .id_h = 0x12d0, + .ver = 0x0001, + .mfg_l = 0x0000, + .mfg_h = 0x0000, + .hwi = clock_hwi_, + .cycle = clock_cycle_, + .reset = clock_reset_, + .data = (struct clock_ *)NULL +}; + +struct clock_ { + DCPU16_WORD cycle_; + DCPU16_WORD rate; + DCPU16_WORD tick; + DCPU16_WORD interrupt_message; +}; + +static +void clock_reset_(struct dcpu16 *vm, void *data) { + struct clock_ *clock = (struct clock_ *)data; + + (void)vm; + + memset(clock, 0, sizeof *clock); +} + +static +void clock_cycle_(struct dcpu16 *vm, void *data) { + struct clock_ *clock = (struct clock_ *)data; + + /* cycle is only called 100000 times per second */ + /* maximum rate is 60hz / word_max = 3932160 */ + + if (clock->rate == 0) + return; + + clock->cycle_++; + if (clock->cycle_ >= clock->rate) { + /* THIS CHECK IS WRONG, JUST A PLACEHOLDER */ + clock->cycle_ = 0; + clock->tick += 1; + + if (clock->interrupt_message) { + if (dcpu16_interrupt(vm, clock->interrupt_message)) + vm->warn_cb_("%s: could not send interrupt", hw_.name_); + } + } +} + +static +void clock_hwi_(struct dcpu16 *vm, void *data) { + struct clock_ *clock = (struct clock_ *)data; + DCPU16_WORD reg_a = vm->reg[DCPU16_REG_A]; + DCPU16_WORD reg_b = vm->reg[DCPU16_REG_B]; + + switch (reg_a) { + case 0: /* set tick gather rate, 60hz/B */ + clock->rate = reg_b; + break; + + case 1: /* fetch elapsed count since rate was set */ + vm->reg[DCPU16_REG_C] = clock->tick; + break; + + case 2: + clock->interrupt_message = reg_b; + break; + } +} + +/* instantitate a new clock */ +struct dcpu16_hw *clock_new(struct dcpu16 *vm) { + struct dcpu16_hw *hw; + + hw = calloc(1, sizeof *hw); + if (hw == NULL) { + vm->warn_cb_("%s():%s", "calloc", strerror(errno)); + return NULL; + } + memcpy(hw, &hw_, sizeof *hw); + hw->data = calloc(1, sizeof hw->data); + if (hw->data == NULL) { + vm->warn_cb_("%s():%s", "calloc", strerror(errno)); + free(hw); + return NULL; + } + + return hw; +} + +void clock_del(struct dcpu16_hw **hw) { + if (hw) { + free((*hw)->data); + (*hw)->data = NULL; + + free((*hw)); + *hw = NULL; + } +} diff --git a/hw_clock.h b/hw_clock.h new file mode 100644 index 0000000..263c03b --- /dev/null +++ b/hw_clock.h @@ -0,0 +1,7 @@ +#ifndef CLOCK_H_P5G2G9FA +#define CLOCK_H_P5G2G9FA + +struct dcpu16_hw *clock_new(struct dcpu16 *); +void clock_del(struct dcpu16_hw **); + +#endif /* CLOCK_H_P5G2G9FA */ diff --git a/hw_keyboard.c b/hw_keyboard.c new file mode 100644 index 0000000..71e016f --- /dev/null +++ b/hw_keyboard.c @@ -0,0 +1,123 @@ +#include +#include +#include + +#include "dcpu16.h" +#include "hw_keyboard.h" + +#define BUF_SZ 32 + +static dcpu16_hw_signal_t keyboard_reset_; +static dcpu16_hw_signal_t keyboard_cycle_; +static dcpu16_hw_signal_t keyboard_hwi_; +static struct dcpu16_hw hw_ = { + .name_ = "Generic Keyboard (compatible)", + .id_l = 0x7406, + .id_h = 0x30cf, + .ver = 0x0001, + .mfg_l = 0x0000, + .mfg_h = 0x0000, + .hwi = keyboard_hwi_, + .cycle = keyboard_cycle_, + .reset = keyboard_reset_, + .data = (struct keyboard_ *)NULL +}; + +struct keyboard_ { + char *buf; + size_t buf_sz; + DCPU16_WORD interrupt_message; +}; + +static +void keyboard_reset_(struct dcpu16 *vm, void *data) { + struct keyboard_ *keyboard = (struct keyboard_ *)data; + + (void)vm; + + keyboard->interrupt_message = 0; + memset(keyboard->buf, 0, keyboard->buf_sz); +} + +static +void keyboard_cycle_(struct dcpu16 *vm, void *data) { + struct keyboard_ *keyboard = (struct keyboard_ *)data; + + (void)vm, (void)keyboard; +} + +static +void keyboard_hwi_(struct dcpu16 *vm, void *data) { + struct keyboard_ *keyboard = (struct keyboard_ *)data; + DCPU16_WORD reg_a = vm->reg[DCPU16_REG_A]; + DCPU16_WORD reg_b = vm->reg[DCPU16_REG_B]; + size_t i; + + switch (reg_a) { + case 0: /* clear keyboard buffer */ + memset(keyboard->buf, 0, keyboard->buf_sz); + break; + + case 1: /* get next key from buffer as C */ + vm->reg[DCPU16_REG_C] = keyboard->buf[0]; + for (i = 1; i < keyboard->buf_sz; i++) { + keyboard->buf[i-1] = keyboard->buf[i]; + } + keyboard->buf[i] = '\0'; + break; + + case 2: /* get currently-pressed-state of key in B as C */ + vm->warn_cb_("IMPLEMENT"); + (void)reg_b; + vm->reg[DCPU16_REG_C] = 0; + break; + + case 3: /* set interrupt state */ + keyboard->interrupt_message = reg_b; + break; + + } +} + +struct dcpu16_hw *keyboard_new(struct dcpu16 *vm) { + struct dcpu16_hw *hw; + char *b; + + hw = calloc(1, sizeof *hw); + if (hw == NULL) { + vm->warn_cb_("%s():%s", "calloc", strerror(errno)); + return NULL; + } + memcpy(hw, &hw_, sizeof *hw); + hw->data = calloc(1, sizeof hw->data); + if (hw->data == NULL) { + vm->warn_cb_("%s():%s", "calloc", strerror(errno)); + free(hw); + return NULL; + } + + b = calloc(BUF_SZ, sizeof *b); + if (b == NULL) { + vm->warn_cb_("%s():%s", "calloc", strerror(errno)); + free(hw->data); + free(hw); + return NULL; + } + ((struct keyboard_ *)(hw->data))->buf = b; + ((struct keyboard_ *)(hw->data))->buf_sz = BUF_SZ; + + return hw; +} + +void keyboard_del(struct dcpu16_hw **hw) { + if (hw) { + free(((struct keyboard_ *)((*hw)->data))->buf); + ((struct keyboard_ *)((*hw)->data))->buf = NULL; + + free((*hw)->data); + (*hw)->data = NULL; + + free(*hw); + *hw = NULL; + } +} diff --git a/hw_keyboard.h b/hw_keyboard.h new file mode 100644 index 0000000..cf7eb4c --- /dev/null +++ b/hw_keyboard.h @@ -0,0 +1,7 @@ +#ifndef KEYBOARD_H_Y2G5EOAS +#define KEYBOARD_H_Y2G5EOAS + +struct dcpu16_hw *keyboard_new(struct dcpu16 *); +void keyboard_del(struct dcpu16_hw **); + +#endif /* KEYBOARD_H_Y2G5EOAS */ diff --git a/hw_lem1802.c b/hw_lem1802.c new file mode 100644 index 0000000..72af16a --- /dev/null +++ b/hw_lem1802.c @@ -0,0 +1,509 @@ +#include +#include +#include +#include + +#ifdef HAVE_LIBPNG +#include +#include +#endif /* HAVE_LIBPNG */ + +#ifdef HAVE_LIBVNCSERVER +#include +#include +#endif /* HAVE_LIBVNCSERVER */ + +#include "dcpu16.h" +#include "chargen-4x8.h" +#include "hw_lem1802.h" + +static dcpu16_hw_signal_t lem1802_reset_; +static dcpu16_hw_signal_t lem1802_cycle_; +static dcpu16_hw_signal_t lem1802_hwi_; +static struct dcpu16_hw hw_ = { + .name_ = "LEM1802 - Low Energy Monitor", + .id_l = 0xf615, + .id_h = 0x7349, + .ver = 0x1802, + .mfg_l = 0x8b36, + .mfg_h = 0x1c6c, + .hwi = lem1802_hwi_, + .cycle = lem1802_cycle_, + .reset = lem1802_reset_, + .data = (struct lem1802_ *)NULL +}; + +#define PIX_X 160 +#define PIX_Y 128 +#define PIX_BORDER 16 +#define CELL_X_SZ 4 +#define CELL_Y_SZ 8 + +#define PALETTE_ENTRIES 16 +static const DCPU16_WORD palette_default_[PALETTE_ENTRIES] = { + 0x0000, /* black */ + 0x000a, /* blue */ + 0x00a0, /* green */ + 0x00aa, /* cyan */ + 0x0a05, /* red */ + 0x0a0f, /* magenta */ + 0x0aa5, /* yellow */ + 0x0aaf, /* pale blue */ + 0x0555, /* grey */ + 0x055f, /* light blue */ + 0x05f5, /* light green*/ + 0x05ff, /* light cyan */ + 0x0f55, /* light red */ + 0x0f5f, /* light magenta */ + 0x0ff5, /* light yellow */ + 0x0fff, /* white */ +}; + +struct pixel_ { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; +}; + +struct lem1802_ { + long long cycle_activated; /* for tracking 'turn on delay' */ + + DCPU16_WORD video_base; + DCPU16_WORD font_base; + DCPU16_WORD palette_base; + DCPU16_WORD border_color; + struct pixel_ *pixbuf; + + unsigned int refresh_rate; /* redraw every n cycles */ + unsigned int refresh_tally_; /* tick */ + + unsigned int blink_rate; /* toggle every n cycles? still figuring this out.. */ + unsigned int blink_tally_; /* tick */ + unsigned int blink_state; + + int (*render)(void *, struct pixel_ *, size_t, size_t); + void *renderer_data; +}; + +static inline +void pixel_color_(struct pixel_ *pix, DCPU16_WORD color) { + pix->r = (color & 0x000f) | ((color & 0x000f) << 8); + pix->g = ((color & 0x00f0) >> 8) | (color & 0x00f0); + pix->b = ((color & 0x0f00) >> 8) | ((color & 0x0f00) >> 16); + pix->a = ((color & 0xf000) >> 16) | ((color & 0xf000) >> 24); +} + +static +void pixbuf_border_paint_(struct pixel_ *pixbuf, struct pixel_ *border) { + size_t x, y, i; + + /* top */ + for (y = 0; y < PIX_BORDER; y++) { + for (x = 0; x < PIX_X; x++) { + i = (y * PIX_X) + x; + pixbuf[i] = *border; + i = ((PIX_Y - y) * PIX_X) + x; + pixbuf[i] = *border; + } + } + + /* sides */ + for (y = PIX_BORDER; y < (PIX_Y - PIX_BORDER + 1); y++) + for (x = 0; x < PIX_BORDER; x++) { + i = (y * PIX_X) + x; + pixbuf[i] = *border; + pixbuf[i + (PIX_X - PIX_BORDER)] = *border; + } +} + +static +void font_tile_paint_(struct pixel_ *p, struct pixel_ *fg, struct pixel_ *bg, DCPU16_WORD *tile) { + size_t pix_x, pix_y; + unsigned char *font_bitmap = (unsigned char *)tile; + + for (pix_x = 0; pix_x < CELL_X_SZ; pix_x++) { + for (pix_y = 0; pix_y < CELL_Y_SZ; pix_y++) { + if ((font_bitmap[pix_x] >> pix_y) & 0x01) + p[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = *fg; + else + p[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = *bg; + } + } +} + +static +void pixbuf_addr_paint_(struct pixel_ *pixbuf, DCPU16_WORD *mem, DCPU16_WORD addr, DCPU16_WORD *palette, DCPU16_WORD *tiles, unsigned int blink_state) { + struct pixel_ *tilestart = pixbuf; /* start of display */ + unsigned int cell_x = addr % (PIX_X / CELL_X_SZ), + cell_y = addr / (PIX_X / CELL_X_SZ); + struct pixel_ fg, bg; + DCPU16_WORD *font_bitmap; + int blink; + + cell_x = addr % (PIX_X / CELL_X_SZ); + cell_y = addr / (PIX_X / CELL_X_SZ); + + blink = mem[addr] & 0x0080; + + /* tiles take two words each */ + font_bitmap = tiles + (2 * (mem[addr] & 0x7f)); + + pixel_color_(&bg, palette[(mem[addr] >> 8) & 0x0f]); + if (blink && blink_state) + pixel_color_(&fg, palette[(mem[addr] >> 8) & 0x0f]); + else + pixel_color_(&fg, palette[(mem[addr] >> 12) & 0x0f]); + + tilestart += (PIX_X * PIX_BORDER); /* skip top border */ + tilestart += (CELL_Y_SZ * PIX_X) * cell_y; /* skip down to row */ + + tilestart += PIX_BORDER; /* skip side border */ + tilestart += (CELL_X_SZ) * cell_x; /* skip to column */ + + font_tile_paint_(tilestart, &fg, &bg, font_bitmap); +} + +static +void lem1802_pixbuf_refresh_full_(struct lem1802_ *display, DCPU16_WORD *mem) { + struct pixel_ border; + size_t tile; + + if (display->video_base == 0) { + /* disconnected, blank display. static might be fun, too */ + memset(display->pixbuf, 0, PIX_X * PIX_Y * sizeof *display->pixbuf); + return; + } + + pixel_color_(&border, display->border_color); + pixbuf_border_paint_(display->pixbuf, &border); + + for (tile = 0; tile < (PIX_X / CELL_X_SZ) * (PIX_Y / CELL_Y_SZ); tile++) { + pixbuf_addr_paint_(display->pixbuf, + mem, + display->video_base + tile, + display->palette_base ? mem + display->palette_base : palette_default_, + display->font_base ? mem + display->font_base : (unsigned short *)chargen_4x8_glyphs, + display->blink_state); + } +} + +static +int pixbuf_render_pnm_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) { + FILE *f = (FILE *)data; + size_t i; + fprintf(f, "P6\n" + "%lu %lu\n" + "255\n", + x, y); + for (i = 0; i < x * y; i++) { + fwrite(&pixbuf[i].r, 1, 1, f); + fwrite(&pixbuf[i].g, 1, 1, f); + fwrite(&pixbuf[i].b, 1, 1, f); + } + fclose(f); + + return 0; +} + +#ifdef HAVE_LIBPNG +static +void pixbuf_render_png_user_warning_(png_structp png, png_const_charp msg) { + (void)png, (void)msg; +} +static +void pixbuf_render_png_user_error_(png_structp png, png_const_charp msg) { + (void)png, (void)msg; +} +static +int pixbuf_render_png_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) { + FILE *f = (FILE *)data; + int retval = 0; + png_structp png; + png_infop info; + size_t i; + + png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, pixbuf_render_png_user_error_, pixbuf_render_png_user_warning_); + if (png == NULL) { + goto f_done; + } + + info = png_create_info_struct(png); + if (info == NULL) { + png_destroy_write_struct(&png, (png_infopp)NULL); + goto f_done; + } + + if (setjmp(png_jmpbuf(png))) { + png_destroy_write_struct(&png, &info); + goto f_done; + } + + png_init_io(png, f); + png_set_IHDR(png, info, x, y, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_invert_alpha(png); + png_write_info(png, info); + for (i = 0; i < y; i++) { + png_write_row(png, (unsigned char *)(pixbuf + (i * x))); + } + png_write_end(png, info); + + png_destroy_write_struct(&png, &info); + +f_done: + fclose(f); + + return retval; +} +#endif /* HAVE_LIBPNG */ + +#ifdef HAVE_LIBVNCSERVER +struct vnc_server_ { + rfbScreenInfoPtr rfbScreen; +}; + +/* create and return a new struct vnc_server_, for use as a vnc renderer's generic data */ +void *lem1802_vnc_init_data(int argc, char *argv[], struct dcpu16_hw *hw) { + struct vnc_server_ *s; + struct pixel_ *pixbuf = ((struct lem1802_ *)(hw->data))->pixbuf; + int paddedWidth = PIX_X + ( (PIX_X & 3) ? (4 - (PIX_X & 3)) : 0 ); + int height = PIX_Y; + int bitsPerSample = 8; + int samplesPerPixel = 3; + int bytesPerPixel = 4; + + s = calloc(1, sizeof *s); + if (s == NULL) + return NULL; + + s->rfbScreen = rfbGetScreen(&argc, argv, paddedWidth, height, bitsPerSample, samplesPerPixel, bytesPerPixel); + if (s->rfbScreen == NULL) { + free(s); + return NULL; + } + + s->rfbScreen->desktopName = "lem1802"; + s->rfbScreen->alwaysShared = TRUE; + +#if 0 + s->rfbScreen->kbdAddEvent = HandleKey; */ + s->rfbScreen->httpDir = "../classes"; +#endif + + s->rfbScreen->frameBuffer = (char *)pixbuf; + + rfbInitServer(s->rfbScreen); + + rfbRunEventLoop(s->rfbScreen,-1,TRUE); + + return s; +} + +static +int pixbuf_render_vnc_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) { + int retval = 0; + struct vnc_server_ *s = (struct vnc_server_ *)data; + + (void)pixbuf; + + /* derp */ + rfbMarkRectAsModified(s->rfbScreen,0,0,x,y); + + return retval; +} +#endif /* HAVE_LIBVNCSERVER */ + + +static +void lem1802_reset_(struct dcpu16 *vm, void *data) { + struct lem1802_ *display = (struct lem1802_ *)data; + + display->cycle_activated = vm->cycle; + + display->video_base = 0; + display->font_base = 0; + display->palette_base = 0; + display->border_color = 0; + + memset(display->pixbuf, 0, PIX_X * PIX_Y * sizeof *display->pixbuf); + + display->refresh_tally_ = 0; + display->blink_tally_ = 0; + display->blink_state = 0; +} + +static +void lem1802_cycle_(struct dcpu16 *vm, void *data) { + struct lem1802_ *display = (struct lem1802_ *)data; + + (void)vm; + /* + maybe just step through video memory (if set) + one word per clock..? could just cheat and + use accounting callbacks.. + + for now just count cycles and issue a full refresh/render + every so often + */ + + display->blink_tally_++; + if (display->blink_tally_ >= display->blink_rate) + display->blink_state ^= 1; + + display->refresh_tally_++; + if (display->refresh_tally_ >= display->refresh_rate) { + display->refresh_tally_ = 0; + if (display->render) + display->render(display->renderer_data, display->pixbuf, PIX_X, PIX_Y); + } + + if (display->render) { + lem1802_pixbuf_refresh_full_(display, vm->ram); + display->render(display->renderer_data, display->pixbuf, PIX_X, PIX_Y); + } +} + +static +void lem1802_hwi_(struct dcpu16 *vm, void *data) { + struct lem1802_ *display = (struct lem1802_ *)data; + DCPU16_WORD reg_a = vm->reg[DCPU16_REG_A]; + DCPU16_WORD reg_b = vm->reg[DCPU16_REG_B]; + size_t i; + + switch (reg_a) { + case 0: /* MEM_MAP_SCREEN */ + if (display->cycle_activated == 0 && reg_b) { + display->cycle_activated = vm->cycle; + } + display->video_base = reg_b; + break; + + case 1: /* MEM_MAP_FONT */ + display->font_base = reg_b; + break; + + case 2: /* MEM_MAP_PALETTE */ + display->palette_base = reg_b; + break; + + case 3: /* SET_BORDER_COLOR */ + display->border_color = reg_b & 0x000f; + break; + + case 4: /* MEM_DUMP_FONT */ + for (i = 0; i < 128 ; i++) { + vm->ram[reg_b] = chargen_4x8_glyphs[reg_b][0] << 8; + vm->ram[reg_b] |= chargen_4x8_glyphs[reg_b][1]; + reg_b += 1; + vm->ram[reg_b] = chargen_4x8_glyphs[reg_b][2] << 8; + vm->ram[reg_b] |= chargen_4x8_glyphs[reg_b][3]; + reg_b += 1; + } + vm->cycle += 256; + break; + + case 5: /* MEM_DUMP_PALETTE */ + for (i = 0; i < 16; i++) { + vm->ram[reg_b] = palette_default_[i]; + reg_b += 1; + } + vm->cycle += 16; + break; + } +} + +static struct renderer_ { + char *name; + char *args; + int (*renderer)(void *, struct pixel_ *, size_t, size_t); +} lem1802_renderers_[] = { + { "pnm", "filename", pixbuf_render_pnm_ }, +#ifdef HAVE_LIBPNG + { "png", "filename", pixbuf_render_png_ }, +#endif /* HAVE_LIBPNG */ +#ifdef HAVE_LIBVNCSERVER + { "vnc", "", pixbuf_render_vnc_ }, +#endif /* HAVE_LIBVNCSERVER */ + { "none", "", NULL }, + { NULL, NULL, NULL } +}; + +int lem1802_renderer_set(struct dcpu16_hw *hw, const char *renderer, void *data) { + struct renderer_ *r; + + for (r = lem1802_renderers_; r->renderer; r++) { + if (strcmp(renderer, r->name) == 0) { + ((struct lem1802_ *)(hw->data))->render = r->renderer; + ((struct lem1802_ *)(hw->data))->renderer_data = data; + return 0; + } + } + + return -1; +} + +char *lem1802_renderers_iter(void **iterp, char **name, char **args) { + struct renderer_ **r = (struct renderer_ **)iterp; + + if (*r == NULL) + *r = lem1802_renderers_; + else + (*r)++; + + if ((*r)->name == NULL) { + *r = NULL; + return NULL; + } + + *name = (*r)->name; + *args = (*r)->args; + + return (*r)->name; +} + +/* instantitate a new display */ +struct dcpu16_hw *lem1802_new(struct dcpu16 *vm) { + struct dcpu16_hw *hw; + + hw = calloc(1, sizeof *hw); + if (hw == NULL) { + vm->warn_cb_("%s():%s", "calloc", strerror(errno)); + return NULL; + } + + memcpy(hw, &hw_, sizeof *hw); + + hw->data = calloc(1, sizeof(struct dcpu16_hw)); + if (hw->data == NULL) { + vm->warn_cb_("%s():%s", "calloc", strerror(errno)); + free(hw); + return NULL; + } + + ((struct lem1802_ *)(hw->data))->pixbuf = calloc(1, PIX_X * PIX_Y * sizeof *((struct lem1802_ *)(hw->data))->pixbuf); + if (((struct lem1802_ *)(hw->data))->pixbuf == NULL) { + vm->warn_cb_("%s():%s", "calloc", strerror(errno)); + free(hw->data); + free(hw); + return NULL; + } + + return hw; +} + +void lem1802_del(struct dcpu16_hw **hw) { + if (hw) { + if (*hw) { + if ((*hw)->data) { + if (((struct lem1802_ *)(*hw)->data)->pixbuf) { + free(((struct lem1802_ *)(*hw)->data)->pixbuf); + ((struct lem1802_ *)(*hw)->data)->pixbuf = NULL; + } + free((*hw)->data); + (*hw)->data = NULL; + } + free(*hw); + } + *hw = NULL; + } +} diff --git a/hw_lem1802.h b/hw_lem1802.h new file mode 100644 index 0000000..fb739f4 --- /dev/null +++ b/hw_lem1802.h @@ -0,0 +1,12 @@ +#ifndef LEM1802_H_WH5E5NOE +#define LEM1802_H_WH5E5NOE + +#include "dcpu16.h" + +struct dcpu16_hw *lem1802_new(struct dcpu16 *); +void lem1802_del(struct dcpu16_hw **); +int lem1802_renderer_set(struct dcpu16_hw *, const char *, void *); +char *lem1802_renderers_iter(void **, char **, char **); +void *lem1802_vnc_init_data(int argc, char *argv[], struct dcpu16_hw *hw); + +#endif /* LEM1802_H_WH5E5NOE */ diff --git a/vm-dcpu16.c b/vm-dcpu16.c index 5924074..6324717 100644 --- a/vm-dcpu16.c +++ b/vm-dcpu16.c @@ -12,7 +12,8 @@ #include "dcpu16.h" #include "common.h" -#include "display.h" + +#include "hw_lem1802.h" /* * shell-like driver for dcpu16 core @@ -21,6 +22,7 @@ * Justin Wind * 2012 04 10 - implementation started * 2012 04 12 - cleanup, better shell loop + * 2012 05 12 - support v1.7 style devices * * TODO * handle quotes in shell command parsing @@ -65,7 +67,7 @@ void usage_(char *prog, unsigned int full) { if (full) { fprintf(f, "\nOptions:\n" "\t [file] -- ram image to load initially\n" - "\t -v -- displays slightly more information\n" + "\t -v -- prints slightly more information while operating\n" "\t -h -- this screen\n"); fprintf(f, "\n%78s\n", src_id_); @@ -140,6 +142,7 @@ int file_load_(struct dcpu16 *vm, char *filename, DCPU16_WORD addr) { return 0; } + /* Here follows the various commands the shell can execute. @@ -235,7 +238,7 @@ COMMAND_IMPL(dump) { return 0; } } - if (arg_count < 2) addr[0] = vm->pc; + if (arg_count < 2) addr[0] = vm->reg[DCPU16_REG_PC]; if (arg_count < 3) addr[1] = addr[0]; if (addr[1] < addr[0]) { @@ -266,7 +269,7 @@ COMMAND_IMPL(disassemble) { return 0; } } - if (arg_count < 2) addr[0] = vm->pc; + if (arg_count < 2) addr[0] = vm->reg[DCPU16_REG_PC]; if (arg_count < 3) addr[1] = addr[0]; if (addr[1] < addr[0]) { @@ -298,7 +301,7 @@ COMMAND_IMPL(step) { errno = 0; count = strtoul(arg_vector[1], &ep, 0); if (errno - || !(*arg_vector[0] && *ep == '\0') ) { + || !(*arg_vector[1] && *ep == '\0') ) { fprintf(stderr, "count '%s' is not a valid number: %s\n", arg_vector[1], strerror(errno)); return 0; } @@ -310,7 +313,7 @@ COMMAND_IMPL(step) { } while (count--) { - dcpu16_disassemble_print(vm, vm->pc); + dcpu16_disassemble_print(vm, vm->reg[DCPU16_REG_PC]); printf("\n"); dcpu16_step(vm); @@ -328,6 +331,46 @@ COMMAND_HELP(step) { } +COMMAND_IMPL(set) { + int addr, value; + DCPU16_WORD *v; + + (void)arg_count; + + /* check if addr is a register */ + for (addr = 0; dcpu16_reg_names[addr]; addr++) { + if (strcasecmp(arg_vector[1], dcpu16_reg_names[addr]) == 0) + break; + } + if (addr < DCPU16_REG__NUM) { + v = vm->reg + addr; + } else { + addr = str_to_word(arg_vector[1]); + if (addr < 0) { + fprintf(stderr, "address '%s' is not a valid word: %s\n", arg_vector[1], strerror(errno)); + return 0; + } + v = vm->ram + addr; + } + + value = str_to_word(arg_vector[2]); + if (value < 0) { + fprintf(stderr, "address '%s' is not a valid word: %s\n", arg_vector[2], strerror(errno)); + return 0; + } + + *v = value; + + return 0; +} + +COMMAND_HELP(set) { + fprintf(f, "\tset addr value\n"); + if (summary) return; + + fprintf(f, "Sets addr to value."); +} + COMMAND_IMPL(run) { struct sigaction act; (void)arg_count, (void)arg_vector; @@ -348,7 +391,7 @@ COMMAND_IMPL(run) { if (opt_.verbose > 1) dcpu16_state_print(vm); else if (opt_.verbose) { - dcpu16_disassemble_print(vm, vm->pc); + dcpu16_disassemble_print(vm, vm->reg[DCPU16_REG_PC]); printf("\n"); } } @@ -373,53 +416,82 @@ static const char * const display_filename_default_ = #endif /* HAVE_LIBPNG */ ; COMMAND_IMPL(display) { - static DCPU16_DISPLAY *display = NULL; - const char *filename = display_filename_default_; + struct dcpu16_hw *hw = lem1802_new(vm); + const char *renderer = arg_vector[1]; + const char *renderer_arg = NULL; + void *renderer_data = NULL; - if (arg_count == 2) { - filename = arg_vector[1]; - } + if (arg_count == 3) + renderer_arg = arg_vector[2]; - if (display) { - printf("display already enabled..\n"); + if (hw == NULL) { + fprintf(stderr, "failed to initialize new display\n"); return 0; } - display = display_new(filename); + /* handle per-renderer setup of data.. */ + /* FIXME: these are awkward */ + if (strcmp(renderer, "pnm") == 0) { + if (renderer_arg == NULL) + renderer_arg = display_filename_default_; + renderer_data = (void *)renderer_arg; + } - if (display == NULL) { - fprintf(stderr, "failed to initialize display buffer\n"); - return 0; +#ifdef HAVE_LIBPNG + if (strcmp(renderer, "png") == 0) { + if (renderer_arg == NULL) + renderer_arg = display_filename_default_; + renderer_data = (void *)renderer_arg; } +#endif /* HAVE_LIBPNG */ - if (dcpu16_acct_add(vm, DCPU16_ACCT_EV_WRITE, display_fn, display)) { - fprintf(stderr, "failed to register display update callback\n"); - return 0; +#ifdef HAVE_LIBVNCSERVER + if (strcmp(renderer, "vnc") == 0) { + int argc = 1; + char *argv[] = { "vm-dcpu16", NULL }; + + renderer_data = lem1802_vnc_init_data(argc, argv, hw); + + /* FIXME: keep refs to vnc displays around somewhere, in global list maybe.. */ + /* keyboards will want to attach to them as well.. */ + + if (renderer_data == NULL) { + fprintf(stderr, "failed to initialize vnc\n"); + lem1802_del(&hw); + return 0; + } } +#endif /* HAVE_LIBVNCSERVER */ - if (dcpu16_acct_add(vm, DCPU16_ACCT_EV_RESET, display_reset_fn, display)) { - fprintf(stderr, "failed to register display reset callback\n"); + if (lem1802_renderer_set(hw, renderer, renderer_data)) { + fprintf(stderr, "failed to set back-end renderer for display\n"); + lem1802_del(&hw); return 0; } - if (dcpu16_acct_add(vm, DCPU16_ACCT_EV_CYCLE, display_cycle_fn, display)) { - fprintf(stderr, "failed to register display cycle callback\n"); + if (dcpu16_hw_add(vm, hw)) { + fprintf(stderr, "failed to attach new display\n"); + lem1802_del(&hw); return 0; } - /* init display as if reset occurred */ - display_reset_fn(vm, DCPU16_ACCT_EV_RESET, 0, display); - return 0; } COMMAND_HELP(display) { - fprintf(f, "\tdisplay [file]\n"); + char *name, *args; + void *iter; + + fprintf(f, "\tdisplay renderer [renderer data]\n"); if (summary) return; - fprintf(f, "Attaches display interface, begins updating an image file of display contents...\n" - "Image filename may be specified, defaults to '%s'\n", - display_filename_default_ + fprintf(f, "Attaches new display unit, using 'renderer' as back-end output.\n" ); + + fprintf(f, "Supported renderers:\n"); + iter = NULL; + while ( (lem1802_renderers_iter(&iter, &name, &args)) ) { + fprintf(f, "\t%s %s\n", name, args); + } } /* gather all these together into a searchable table */ @@ -436,8 +508,9 @@ static struct command_ command_table_[] = { COMMAND_ENTRY(disassemble, 0, 2), COMMAND_ENTRY(step, 0, 1), COMMAND_ENTRY(run, 0, 0), + COMMAND_ENTRY(set, 2, 2), COMMAND_ENTRY(reset, 0, 0), - COMMAND_ENTRY(display, 0, 1), + COMMAND_ENTRY(display, 1, 2), { NULL, 0, 0, NULL, NULL } }; @@ -519,13 +592,13 @@ int main(int argc, char **argv) { for (line = line_prev = NULL, tok_v = tok_v_prev = NULL, tok_c = tok_c_prev= 0, - snprintf(prompt, sizeof prompt, prompt_fmt, vm->pc), + snprintf(prompt, sizeof prompt, prompt_fmt, vm->reg[DCPU16_REG_PC]), dcpu16_state_print(vm); (line = readline(prompt)); printf("\n"), - snprintf(prompt, sizeof prompt, prompt_fmt, vm->pc), + snprintf(prompt, sizeof prompt, prompt_fmt, vm->reg[DCPU16_REG_PC]), dcpu16_state_print(vm)) { const char whitespace[] = " \t"; char *line_start;