Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: MostAwesomeDude/dcpu
base: ad0d4adef3
...
head fork: MostAwesomeDude/dcpu
compare: 7258bcfb39
Checking mergeability… Don't worry, you can still create the pull request.
  • 11 commits
  • 7 files changed
  • 0 commit comments
  • 2 contributors
View
15 README
@@ -1,8 +1,7 @@
DCPU-16 Playground
-This is an emulator, assembler, and a (still primitive) implementation of
-the Forth programming language for notch's DCPU-16. See
-http://0x10c.com/doc/dcpu-16.txt for details.
+This is an emulator, assembler, and an implementation of the Forth programming
+language for notch's DCPU-16. See http://0x10c.com/doc/dcpu-16.txt for details.
There are also a couple of useful scripts for swapping endianness of binary
images and dumping to hex in a format supported by some other emulators.
@@ -28,8 +27,7 @@ and has a number of nice options. It builds on both Mac OS and Linux with gcc.
I think it's about as close to the current consensus specs as is achievable
with ncurses. Obviously bitmapped graphics aren't supported, so the terminal
driver doesn't do anything special with the 'character table' (0x8180-0x827f),
-it just ignores the contents. I also don't do anything with the so-called
-'video border' at 0x8280.
+it just ignores the contents.
The emulator supports the 'standard' 16-word keyboard ring buffer at 0x9000.
I'm not sure what other emulators are doing, but this one will refuse to
@@ -55,8 +53,11 @@ goforth
go, folks, go forth. trust your brain! trust your body!'
The real fun starts in goforth.dasm and goforth.ft. goforth is an
-implementation of the Forth programming language for DCPU-16. It's still
-primitive and there's more work to be done, but it's already quite usable.
+implementation of the Forth programming language for DCPU-16. There's still
+plenty of work to be done, but it's already quite usable. For example, it
+contains a built-in decompiler/disassembler and other amenities. (In fact, you
+might take a look at the file disasm.ft for an example of some non-trivial code
+written in goforth.)
The bootstrapped goforth image should run on any DCPU-16 emulator that
supports I/O. (It'll run on others, too, but won't do much...)
View
61 disasm.ft
@@ -48,9 +48,14 @@
: count ( c-addr -- c-addr u ) dup 1+ swap @ ;
: id. ( nt -- ) count lenmask and type ;
+: xu.r0 ( u n -- ) base @ >r hex u.r0 r> base ! ;
+
+
+\ colon decompiler for forth words
+
: '"' char " emit ;
: see-word ( addr -- n )
- space base @ >r hex dup 4 u.r0 ." : " r> base !
+ space dup 4 xu.r0 ." : "
dup @ case
['] lit of ." < " 1+ ? ." >" 2 endof
['] litstring of '"' 1+ dup @ 2 + swap count type '"' endof
@@ -59,18 +64,64 @@
>name id. 1 swap
endcase ;
\ includes start, excludes limit
-: see-range ( limit start -- ) ?do i see-word cr +loop ;
+: see-range ( limit start -- ) cr ?do i see-word cr +loop ;
+
+
+\ native disassembler for primitives (and any other memory, obviously)
+
+variable /disasm-pc
+: disasm-pc@ ( -- u ) /disasm-pc @ 1 /disasm-pc +! ;
+: "regs" S" abcxyzij" ;
+: "opname" S" xxxsetaddsubmuldivmodshlshrandborxorifeifnifgifb" ;
+: opname ( op -- ) 3 * "opname" drop + 3 type space ;
+: disasm-op ( inst -- op ) 0x f and ;
+: disasm-a ( inst -- arg ) 4 rshift 0x 3f and ;
+: disasm-b ( inst -- arg ) 10 rshift ;
+: disasm-nextlit ( -- ) disasm-pc@ @ 0 .r ;
+: decode-arg ( arg -- ) case
+ 0x 18 of ." pop" endof
+ 0x 19 of ." peek" endof
+ 0x 1a of ." push" endof
+ 0x 1b of ." sp" endof
+ 0x 1c of ." pc" endof
+ 0x 1d of ." o" endof
+ 0x 1e of ." [" disasm-nextlit ." ]" endof
+ 0x 1f of disasm-nextlit endof
+ dup 0x 08 u< if 7 and "regs" drop + @ emit else
+ dup 0x 10 u< if 7 and ." [" "regs" drop + @ emit ." ]" else
+ dup 0x 18 u< if 7 and ." [" disasm-nextlit ." +" "regs" drop + @ emit ." ]" else
+ dup 0x 1f u> if 0x 1f and 0 .r else
+ dup ." unknown oparg: " 2 xu.r0
+ endcase ;
+: disasm-special ( inst -- )
+ dup disasm-a dup 0x 01 =
+ if drop ." jsr" else ." unk[" 2 xu.r0 ." ]" then space
+ disasm-b decode-arg ;
+: disasm-inst ( -- )
+ space disasm-pc@ dup 4 xu.r0 ." : "
+ @ dup disasm-op ?dup if
+ opname dup disasm-a decode-arg ." , " disasm-b decode-arg
+ else
+ disasm-special
+ then ;
+\ include start excludes limit
+: disasm-range ( limit start -- )
+ cr /disasm-pc !
+ begin dup /disasm-pc @ > while disasm-inst cr repeat drop ;
+
+
+\ higher-level 'see' support
-: docol-see ( xt -- ) ." colon-defined word:" cr
- 1+ next-head see-range ;
+: docol-see ( xt -- ) ." colon-defined word:" 1+ next-head see-range ;
: const-see ( xt -- ) ." constant: " 1+ ? ;
: var-see ( xt -- ) ." variable: " 1+ ? ;
+: prim-see ( xt -- ) ." primitive:" next-head disasm-range ;
: xt-see dup @ case
docol: of dup docol-see endof
dovar: of dup var-see endof
docon: of dup const-see endof
( xt [xt] -- )
- 2dup 1- = if drop ." primitive" else \ TODO disassemble
+ 2dup 1- = if prim-see else
( TODO heuristic check for does> dispatch at the target address )
." unrecognized code field value"
endcase
View
1  emulator/dcpu.h
@@ -46,6 +46,7 @@ typedef uint64_t tstamp_t;
#define DISPLAY_HZ 30
#define VRAM_ADDR 0x8000
+#define BORDER_ADDR 0x8280
#define SCR_HEIGHT 12
#define SCR_WIDTH 32
#define KBD_BAUD 4000
View
2  emulator/disassembler.c
@@ -77,7 +77,7 @@ u16 *disassemble(u16 *pc, char *out) {
pc = dis_operand(pc, b, out+strlen(out));
return pc;
}
- if (a > 0 && a < 7) {
+ if (a > 0 && a < NUM_NBOPCODES) {
sprintf(out, "%s ", nbopnames[a]);
pc = dis_operand(pc, b, out+strlen(out));
return pc;
View
27 emulator/terminal.c
@@ -34,12 +34,15 @@
struct term_t {
+ WINDOW *border;
+ WINDOW *vidwin;
WINDOW *dbgwin;
tstamp_t tickns;
tstamp_t nexttick;
tstamp_t keyns;
tstamp_t nextkey;
u16 keypos;
+ u16 curborder;
};
static struct term_t term;
@@ -56,12 +59,15 @@ void dcpu_initterm(void) {
term.keyns = 1000000000 / KBD_BAUD;
term.nextkey = dcpu_now();
term.keypos = 0;
+ term.curborder = 0;
// should be done prior to initscr, and it doesn't matter if we do it twice
initscr();
start_color();
cbreak();
keypad(stdscr, true);
+ term.border = subwin(stdscr, 14, 36, 0, 0);
+ term.vidwin = subwin(stdscr, 12, 32, 1, 2);
term.dbgwin = subwin(stdscr, LINES - (SCR_HEIGHT+3), COLS, SCR_HEIGHT+2, 0);
scrollok(term.dbgwin, true);
keypad(term.dbgwin, true);
@@ -137,7 +143,7 @@ void dcpu_dbgterm(void) {
}
static void draw(u16 word, u16 row, u16 col) {
- move(row+1, col+1);
+ wmove(term.vidwin, row, col);
char letter = word & 0x7f;
bool blink = word & 0x80;
@@ -149,18 +155,27 @@ static void draw(u16 word, u16 row, u16 col) {
// use color_set() rather than COLOR_PAIR() since we may have more than 256
// colors. unfortunately that doesn't really solve the problem, since every
// linux that i can find still doesn't ship ncurses 6. how sad.
- color_set(color(fg, bg), NULL);
- if (blink) attron(A_BLINK);
- addch(letter);
- if (blink) attroff(A_BLINK);
+ wcolor_set(term.vidwin, color(fg, bg), NULL);
+ if (blink) wattron(term.vidwin, A_BLINK);
+ waddch(term.vidwin, letter);
+ if (blink) wattroff(term.vidwin, A_BLINK);
+}
+
+static void draw_border(dcpu *dcpu) {
+ if (term.curborder != (dcpu->ram[BORDER_ADDR] & 0xf)) {
+ term.curborder = dcpu->ram[BORDER_ADDR] & 0xf;
+ wbkgd(term.border, A_NORMAL | COLOR_PAIR(color(0, term.curborder)) | ' ');
+ wrefresh(term.border);
+ }
}
void dcpu_redraw(dcpu *dcpu) {
+ draw_border(dcpu);
u16 *addr = &dcpu->ram[VRAM_ADDR];
for (u16 i = 0; i < SCR_HEIGHT; i++)
for (u16 j = 0; j < SCR_WIDTH; j++)
draw(*addr++, i, j);
- refresh();
+ wrefresh(term.vidwin);
}
void dcpu_termtick(dcpu *dcpu, tstamp_t now) {
View
52 goforth.dasm
@@ -853,6 +853,14 @@ isspace_: set j, 0
set j, -1
set pc, pop
+ ; Takes address of character in b and lowers it in-place.
+lower_: ifg [b], 0x5a
+ set pc, lower_.end
+ ifg 0x41, [b]
+ set pc, lower_.end
+ add [b], 0x20 ; Shift the character from upper- to lowercase.
+lower_.end: set pc, pop
+
; expects address of string in a, upper limit in c
dropspace_: ife a, c
set pc, pop ; input exhausted, bail
@@ -915,32 +923,50 @@ pword.end: set [z], a ; push parsed length
sub [var_inptr], [var_srcptr]
next
-
- ; >number ( u1 a1 u1 -- u2 a2 u2 ) TODO wrap!
-
- ; TODO other bases!
; convert word to number. word len in a, ptr in b, accumulator in i.
; modifies i, advances b and decrements a as it parses...
tonum_: ife a, 0 ; bail if exhausted
set pc, tonum_.end
- ifg [b], 0x39 ; bail if char > '9'
- set pc, tonum_.end
+ ifg [b], 0x39 ; Try a character if char > '9'
+ set pc, tonum_.chr
ifg 0x30, [b] ; bail if char < '0'
set pc, tonum_.end
- mul i, 10 ; shift i one place
set j, [b]
sub j, 0x30 ; remove '0'
+tonum_.mul: ifg j, [var_base] ; bail if not a digit in current base
+ set pc, tonum_.end
+ ife j, [var_base]
+ set pc, tonum_.end
+ mul i, [var_base] ; shift i one place
add i, j ; add it
sub a, 1 ; advance to `next' char
add b, 1
set pc, tonum_ ; and begin again...
+tonum_.chr: jsr lower_ ; Lower the character.
+ ifg [b], 0x7a ; Not a lowercase character.
+ set pc, tonum_.end
+ ifg 0x61, [b] ; Also not a lowercase character.
+ set pc, tonum_.end
+ set j, [b]
+ sub j, 0x57
+ set pc, tonum_.mul
tonum_.end: set pc, pop
+ ; >number ( u1 a1 u1 -- u2 a2 u2 )
+ defcode(>number, 0, tonumber)
+ set a, [z]
+ set b, [1+z]
+ set i, [2+z]
+ jsr number_
+ set [2+z], i
+ set [1+z], b
+ set [z], a
+ next
+
; convert word to number, possibly negative.
; takes word len in a, ptr in b.
- ; returns result in i, advances b and decrements a as it parses...
-number_: set i, 0 ; no accumulator so far
- set push, 0 ; number is positive
+ ; accumulates result in i, advances b and decrements a as it parses...
+number_: set push, 0 ; number is positive
ife a, 0 ; bail if exhausted
set pc, numb_.end
ifn [b], 0x2d ; if first char is '-'
@@ -1049,7 +1075,8 @@ compile__: set a, [z] ; load length
set pc, [x] ; and execute it...
compile_.4: jsr comma_ ; non-immediate. compile the value in j.
set pc, compile_.3
-compile_.1: jsr number_
+compile_.1: set i, 0 ; no accumulator so far...
+ jsr number_
ife a, 0
set pc, compile_.2
set [var_succ], 0x0 ; not a number. bail.
@@ -1184,7 +1211,8 @@ interp_: set a, [z] ; load length
set pc, interp_.1 ; not in dict...
set x, j ; found. update the codeword pointer
set pc, [x] ; and execute it...
-interp_.1: jsr number_
+interp_.1: set i, 0 ; no accumulator so far...
+ jsr number_
ife a, 0
set pc, interp_.2
set [var_succ], 0x0 ; not a number. bail.
View
24 goforth.ft
@@ -166,9 +166,19 @@
\ endcase ;
+\ standard base-changing words
+: decimal ( -- ) 10 base ! ;
+: hex ( -- ) 16 base ! ;
+
+
\ char grabs first char of next word, but it needs to be state-smart...
: char parse-word drop @
state @ if [compile] literal then ; immediate
+\ and a state-smart hex parsing word. this should really be done gforth-style...
+: 0x base @ >r hex
+ 0 parse-word >number 2drop \ TODO check errors
+ state @ if [compile] literal then
+ r> base ! ; immediate
\ stack manipulators
@@ -188,11 +198,6 @@
: max ( n n -- n ) 2dup > unless swap then drop ;
-\ standard base-changing words
-: decimal ( -- ) 10 base ! ;
-: hex ( -- ) 16 base ! ;
-
-
\ printing...
: spaces ( n -- ) 0 max 0 ?do space loop ;
@@ -261,17 +266,26 @@ variable quitword
\ words to handle terminal attributes. see below for examples...
\ i'm not super happy with this design...
0 constant term
+hex 8280 constant border decimal
+
: r ( term -- term ) 4 or ;
: g ( term -- term ) 2 or ;
: b ( term -- term ) 1 or ;
: hi ( term -- term ) 8 or ;
+
+\ Set up some masks.
term r g b hi 2* invert constant bgmask
term r g b hi 2* 4 lshift invert constant fgmask
+
: bg ( term -- ) 2* conattr @ bgmask and or conattr ! ;
: fg ( term -- ) 2* 4 lshift conattr @ fgmask and or conattr ! ;
: blink ( -- ) conattr @ 1 or conattr ! ; \ this one is a toggle
: defcon ( -- ) term bg term r g b fg ;
+\ Convenience words for messing with the border.
+: border! ( color -- ) border ! ;
+: border@ ( -- color ) border @ ;
+
\ finally, the traditional repl that checks after each line for stack underflow.
\ surprisingly, because of the memory layout, this simple check is enough to

No commit comments for this range

Something went wrong with that request. Please try again.