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.
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
CPPFLAGS += -DHAVE_FOPENCOOKIE -D_GNU_SOURCE
endif
+CPPFLAGS += -DHAVE_LIBVNCSERVER -I/opt/local/include
+LDFLAGS += -L/opt/local/lib -lvncserver
all: $(PROGRAMS)
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
*
* Justin Wind <justin.wind@gmail.com>
* 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
*
#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)
}
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
#endif
;
void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) {
- trace_cb_ = fn;
+ if (fn)
+ trace_cb_ = fn;
+ else
+ trace_cb_ = printf_null_;
}
}
}
-/* 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;
}
}
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;
}
* 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__,
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;
}
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;
}
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);
break;
case 0x19: /* PEEK/[SP] */
- *v = &(d->ram[sp]);
+ *v = &(vm->ram[sp]);
*e_addr = sp;
TRACE("%s>> PEEK [0x%04x] (0x%04x)",
__func__,
*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,
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;
*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,
/* 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;\
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_)
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 */
/* 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) {
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_) {
WARN("reserved nbi opcode invoked");
- ACCT_ILL(d->pc - pc_adjust);
+ ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
}
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);
}
OP_IMPL(nbi_ias) {
OP_NBI(nbi_ias);
- d->ia = *a;
+ vm->reg[DCPU16_REG_IA] = *a;
ACCT_R(ev_a_addr);
}
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);
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) {
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) {
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) {
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[] = {
/* 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_) {
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) {
*/
*b = *a;
- d->cycle += 1;
+ vm->cycle += 1;
ACCT_W(ev_b_addr);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
*b = *b % *a;
}
- d->cycle += 3;
+ vm->cycle += 3;
ACCT_W(ev_a_addr);
}
*b = (short)*b % (short)*a;
}
- d->cycle += 3;
+ vm->cycle += 3;
ACCT_W(ev_b_addr);
}
*b = *b & *a;
- d->cycle += 1;
+ vm->cycle += 1;
ACCT_W(ev_b_addr);
}
*b = *b | *a;
- d->cycle += 1;
+ vm->cycle += 1;
ACCT_W(ev_b_addr);
}
*b = *b ^ *a;
- d->cycle += 1;
+ vm->cycle += 1;
ACCT_W(ev_b_addr);
}
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");
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");
*b = acc;
- d->ex = acc >> 16;
+ vm->reg[DCPU16_REG_EX] = acc >> 16;
- d->cycle += 2;
+ vm->cycle += 2;
ACCT_W(ev_b_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(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) {
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) {
if (*b != *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle++;
+ vm->skip_ = 1;
+ vm->cycle++;
}
- d->cycle += 2;
+ vm->cycle += 2;
}
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) {
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) {
if (*b < *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle++;
+ vm->skip_ = 1;
+ vm->cycle++;
}
- d->cycle += 2;
+ vm->cycle += 2;
}
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) {
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);
}
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);
}
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);
}
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);
}
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[] = {
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 */
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
/*
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
#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 */
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;
}
}
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");
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_));
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;
/* 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
if (vm == NULL)
WARN("%s: %s(%zu): %s", __func__, "calloc", strerror(errno));
+ vm->warn_cb_ = warn_cb_;
+ vm->trace_cb_ = trace_cb_;
+
return vm;
}
* release a dcpu16 instance
*/
void dcpu16_delete(struct dcpu16 **vm) {
- if (!vm || !*vm) return;
+ if (!vm || !*vm)
+ return;
free(*vm);
*vm = NULL;
#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 */
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 */
};
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;
};
/* 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 *, ...));
+++ /dev/null
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <sysexits.h>
-#include <time.h>
-#include <assert.h>
-
-#ifdef HAVE_LIBPNG
-#include <setjmp.h>
-#include <png.h>
-#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);
-}
+++ /dev/null
-#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 */
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#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;
+ }
+}
--- /dev/null
+#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 */
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#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;
+ }
+}
--- /dev/null
+#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 */
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBPNG
+#include <setjmp.h>
+#include <png.h>
+#endif /* HAVE_LIBPNG */
+
+#ifdef HAVE_LIBVNCSERVER
+#include <rfb/rfb.h>
+#include <rfb/keysym.h>
+#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;
+ }
+}
--- /dev/null
+#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 */
#include "dcpu16.h"
#include "common.h"
-#include "display.h"
+
+#include "hw_lem1802.h"
/*
* shell-like driver for dcpu16 core
* Justin Wind <justin.wind@gmail.com>
* 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
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_);
return 0;
}
+
/*
Here follows the various commands the shell can execute.
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]) {
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]) {
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;
}
}
while (count--) {
- dcpu16_disassemble_print(vm, vm->pc);
+ dcpu16_disassemble_print(vm, vm->reg[DCPU16_REG_PC]);
printf("\n");
dcpu16_step(vm);
}
+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;
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");
}
}
#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 */
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 }
};
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;