My first crude DCPU-16 simulator…

April 6, 2012 | Computer Games, Operating Systems, Programming Languages | By: Mark VandeWettering

A few days ago, I mentioned that @notch, the creator of Minecraft, had a new idea for a space game that he was beginning to work on. One of the features of this game is that your spaceship would be controlled by a simulated computer that you could program. He released a preliminary specification for the 16 bit machine called DCPU-16. I like writing things like this, so I spent a couple of hours and hacked the simple core of one together. It doesn’t yet properly count cycles, but it does implement the basic instruction set, and executes the sample program that he provided in the notes. As yet, he hasn’t specified how things like I/O will work (I suspect some of the type 0 reserved opcodes will be used), so it’s not of much use, but it might serve as the basis for others to explores.

Addendum: The example program implements a simple loop with the target :loop, but I suspect the bottom of the loop should be an IFE rather than an IFN instruction, otherwise, the loop simple exits on the first iteration.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>

/*
 * An implementation of Notch's DCPU-16 specification, version 1.1
 * Written by Mark VandeWettering.
 */

/* this has to be 16 bits, because we aren't going to mask increments
 * and decrements.
 */
typedef unsigned short Word ;
#define MEMSIZE (65536)
Word M[MEMSIZE] ;
Word R[8] = {0, 0, 0, 0, 0, 0, 0, 0} ;
Word PC = 0 ;
Word SP = 0 ;
Word O = 0 ;
Word W = 0 ;
unsigned long cycles = 0L ;

#define OP(w)                   ((w)&(0xF))
#define FIELDA(w)               (((w)>>4)&(0x3F))
#define FIELDB(w)               (((w)>>10)&(0x3F))

unsigned char *Rn[] = { "A", "B", "C", "X", "Y", "Z", "I", "J"} ;

Word 
DecodeSRC(Word v)
{
    Word EA ;
    if (v < 0x8) {
        return R[v] ;
    } else if (v < 0x10) {
        return M[R[v&0x7]] ;
    } else if (v < 0x18) {
        EA = M[PC++] ;
        EA += R[v&0x7] ;
        return M[EA] ;
    } else if (v < 0x20) {
        switch (v) {
        case 0x18:
            return M[SP++] ;
        case 0x19:
            return M[SP] ;
        case 0x1a:
            return M[--SP] ;
        case 0x1b:
            return SP ;
        case 0x1c:
            return PC ;
        case 0x1d:
            return 0 ;
        case 0x1e:
            return M[M[PC++]] ;
        case 0x1f:
            return M[PC++] ;
        default:
            abort() ;
        }
    } else if (v < 0x40) {
        return v - 0x20 ;
    } 
    abort() ;
}

Word *
DecodeDST(Word v)
{
    Word EA ;
    Word *T ;
    if (v < 0x8) {
        return R+v ;
    } else if (v < 0x10) {
        return M+(v&0x7) ;
    } else if (v < 0x18) {
        EA = M[PC++] ;
        EA += R[v&0x7] ;
        return M + EA ;
    } else if (v < 0x1f) {
        switch (v) {
        case 0x18:
            return M+(SP++) ;
        case 0x19:
            return M+SP ;
        case 0x1a:
            return M+(--SP) ;
        case 0x1b:
            return &SP ;
        case 0x1c:
            return &PC ;
        case 0x1d:
            return &W ;
        case 0x1e:
            return M+PC++ ;
        case 0x1f:
            return &W ;
        default:
            abort() ;
        }
    } else if (v < 0x40) {
        return &W ;
    } 
    abort() ;
}

int
SkipArg(Word arg)
{
    if (arg >= 0x10 && arg < 0x18)
        return 1 ;
    if (arg == 0x1e)
        return 1 ;
    if (arg == 0x1f)
        return 1 ;
    return 0 ;
}

void
Skip() 
{
    Word I = M[PC++] ;
    Word A = FIELDA(I) ;
    Word B = FIELDB(I) ;
    PC += SkipArg(A) ;
    PC += SkipArg(B) ;
}

#define DEBUGPRINTF(x)          

void
execute()
{
    Word I ;
    Word T ;
    Word B ;
    Word * A ;
    uint32_t a, b ;

    I = M[PC] ;
    PC ++ ;

    if (OP(I) != 0)
        A = DecodeDST((I>>4)&0x3f) ;
    B = DecodeSRC((I>>10)&0x3f) ;
 
    switch (OP(I)) {
    case 0x0:
        switch (((I)>>4)&0x3F) {
        case 0x01:
            /* JSR */
            DEBUGPRINTF("... JSR") ;
            /* Push the PC on the stack */
            M[--SP] = PC ;
            PC = B ;
            break ;
        default:
            abort() ;
        }
        break ;
    case 0x1:   
        /* SET */
        DEBUGPRINTF("... SET\n") ;
        *A = B ;
        break ;
    case 0x2:   
        /* ADD */
        DEBUGPRINTF("... ADD\n") ;
        a = *A ;
        b = B ;
        *A = (a + b) ;
        O = (a + b) >> 16 ;
        break ;
    case 0x3:
        /* SUB */
        DEBUGPRINTF("... SUB\n") ;
        a = *A ;
        b = B ;
        *A = (a - b) ;
        O = (a - b) >> 16 ;
        break ;
    case 0x4:
        /* MUL */
        DEBUGPRINTF("... MUL\n") ;
        a = *A ;
        b = B ;
        *A = (a * b) ;
        O = ((a * b)>>16) ;
        break ;
    case 0x5:
        /* DIV */
        DEBUGPRINTF("... DIV\n") ;
        a = *A ;
        b = B ;
        if (b == 0) {
            *A = O = 0 ;
        } else {
            *A = a / b ;
            O = ((a<<16)/b) ;
        } 
        break ;
    case 0x6:
        /* MOD */
        DEBUGPRINTF("... MOD\n") ;
        a = *A ;
        b = B ;
        if (b == 0)
            *A = 0 ;
        else
            *A = a % b ;
        break ;
    case 0x7:
        /* SHL */
        DEBUGPRINTF("... SHL\n") ;
        a = *A ;
        b = B ;
        *A = a << b ;
        O = (a << b) >> 16 ;
        break ;
    case 0x8:
        /* SHR */
        DEBUGPRINTF("... SHR\n") ;
        a = *A ;
        b = B ;
        *A = a >> b ;
        O = (a << 16) >> b ;
    case 0x9:
        /* AND */
        DEBUGPRINTF("... AND\n") ;
        *A &= B ;
        break ;
    case 0xa:
        /* BOR */
        DEBUGPRINTF("... BOR\n") ;
        *A |= B ;
        break ;
    case 0xb:
        /* XOR */
        DEBUGPRINTF("... XOR\n") ;
        *A ^= B ;
        break ;
    case 0xc:
        /* IFE */
        DEBUGPRINTF("... IFE\n") ;
        if (*A == B) Skip() ;
        break ;
    case 0xd:
        /* IFN */
        DEBUGPRINTF("... IFN\n") ;
        if (*A != B) Skip() ;
        break ;
    case 0xe:
        /* IFG */
        DEBUGPRINTF("... IFG\n") ;
        if (*A > B) Skip() ;
        break ;
    case 0xf:
        /* IFB */
        DEBUGPRINTF("... IFB\n") ;
        if ((*A & B) != 0) Skip() ;
        break ;
    }
    DEBUGPRINTF("\n") ;
}

Word prog0[] = {
    /* 0000 */ 0x7c01, 0x0030, 0x7de1, 0x1000, 0x0020, 0x7803, 0x1000, 0xc00d,
    /* 0008 */ 0x7dc1, 0x001a, 0xa861, 0x7c01, 0x2000, 0x2161, 0x2000, 0x8463,
    /* 0010 */ 0x806d, 0x7dc1, 0x000d, 0x9031, 0x7c10, 0x0018, 0x7dc1, 0x001a,
    /* 0018 */ 0x9037, 0x61c1, 0x7dc1, 0x001a, 0x0000, 0x0000, 0x0000, 0x0000,
} ;
        
void
dumpregs()
{
    int i ;
    printf("... PC=0x%04X SP=0x%04X ", PC, SP) ;
    for (i=0; i<8; i++)
        printf("%s=0x%04X ", Rn[i], R[i]) ;
    printf("O=0x%04X\n", O) ;
}
        

main()
{
    printf("... DCPU-16 simulator.\n") ;
    printf("... version 1.0, written by Mark VandeWettering\n") ;
    printf("... loading %d words of program ...", sizeof(prog0)/sizeof(prog0[0])) ;

    int i ;
    for (i=0; i<sizeof(prog0)/sizeof(prog0[0]); i++) {
        M[i] = prog0[i] ;
    }
    printf("done.\n\n") ;

    for (i=0; i<20; i++) {
        execute() ;
    }
}