/* Creating a DCPU-16 (version 1.1) emulator! */ /* For all information, see http://dcpu.com/ ҂ ՅԄƖֆׇ */ #include /* Using libSDL for graphics & keyboard handling */ #include #include #include #include #include #include #include #include typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; // Graphics settings and memory addresses. static constexpr int WIDTH=128, HEIGHT=96, SCALE=4, GFX_BASE = 0x8000, // end at 0x8540 INPUT_BASE = 0x9000, // end at 0x9010 DISPLAY_BASE = 0x9040, // end at 0x9041 SPRITE_BASE = 0x9050, // end at 0x9070 N_SPRITES = 16; static const bool DisassemblyTrace = false; static const bool DisassemblyListing = false; enum reg_index { A,B,C, X,Y,Z, I,J, PC,SP,EX, POP,PEEK,PUSH, noreg=0xF, mem=0x80, imm=0x40 }; static const char regnames[][5] = {"A","B","C","X","Y","Z","I","J","PC","SP","O", "POP","PEEK","PUSH"}; static const u8 reg_specs[0x20] = {A ,B ,C ,X ,Y ,Z ,I ,J, // regs A |mem,B |mem,C |mem,X |mem,Y |mem,Z |mem,I |mem,J |mem, A|imm|mem,B|imm|mem,C|imm|mem,X|imm|mem,Y|imm|mem,Z|imm|mem,I|imm|mem,J|imm|mem, POP, PEEK, PUSH, SP, PC, EX, noreg|imm|mem, noreg|imm}; static const char ins_set[5*16*3+1] = "000JSR.........................................." "................................................" "................................................" "................................................" "nbiSETADDSUBMULDIVMODSHLSHRANDBORXORIFEIFNIFGIFB"; /* Disassemble() produces textual disassembly of single DCPU instruction at memory[pc] */ static std::unordered_multimap SymbolLookup; // ^ This global array maps addresses into labels. Used by disassembler later. static std::string Disassemble(unsigned pc, const u16* memory) { unsigned v = memory[pc++], o = (v & 0x0F), aa = (v>>4) & 0x3F, bb = (v>>10) & 0x3F; std::string opcode(&ins_set[3*(o ? 64+o : aa)], 3); auto doparam = [&](unsigned v) -> std::string { char Buf[32], sep[4] = "\0+ "; if(v >= 0x20) { std::sprintf(Buf, " 0x%X", v-0x20); return Buf; } std::string result(" "); if(reg_specs[v] & mem) result += '['; if((reg_specs[v] & 0xFu)!=noreg) { result += regnames[(reg_specs[v] & 0xFu)]; sep[0] = ' '; } if(reg_specs[v] & imm) { sprintf(Buf, "%s0x%X", sep, memory[pc++]); result += Buf; } if(reg_specs[v] & imm) for(auto i = SymbolLookup.equal_range(memory[pc-1]); i.first != i.second; ++i.first) result += "=" + i.first->second; if(reg_specs[v] & mem) result += ']'; return result; }; for(auto i = SymbolLookup.equal_range(pc-1); i.first != i.second; ++i.first) opcode = i.first->second + ": " + opcode; if(o) { opcode += doparam(aa); opcode += ','; } return opcode += doparam(bb); } /* Assembler() reads an entire assembler file and translates it into a DCPU memory file */ class Assembler { std::vector memory; // Expression is a list of multiplicative terms. Each is a constant + list of summed symbols. typedef std::vector>>> expression; // List of forward declarations, which can be patched later. std::vector> forward_declarations; // pclist is used for side-by-side disassembly & source code listing. std::vector> pclist; // Remember known labels (name -> address). std::unordered_map symbols; /* Define all reserved words */ std::unordered_map bops, operands; struct operand_type { bool set = false, brackets = false; u16 addr = 0, shift = 0; expression terms; void clear() { set=brackets=false; terms={{}}; } // Reset everything except addr&shift } op; bool comment = false, label = false, sign = false, dat = false; std::string id; u16 pc=0; std::pair simplify_expression(expression& expr, bool require_known = false) { // For each identifier in the term that is not a register, add it to the sum int register_number = -1; long const_total = 1; for(std::size_t b=0; bsecond; } else { // Not a CPU register. Is it a previously defined label? auto si = symbols.find(v.second); if(si == symbols.end()) { // No, it's not known yet. if(require_known) std::fprintf(stderr, "Error: Unresolved forward declaration of '%s'\n", v.second.c_str()); // Keep it for later. ++a; continue; } // Yes. Treat it as an integer offset. sum.first += (sign ? (-si->second) : (si->second)); } sum.second.erase( sum.second.begin() + a); } if(!sum.second.empty()) ++b; else { const_total *= sum.first; expr.erase( expr.begin() + b); } } // Add back the constant sum if it still will be needed. if(!expr.empty() && const_total != 1) expr.push_back( {const_total, {{}}} ); if(!expr.empty()) const_total = 0; return {const_total,register_number}; } void flush_operand(operand_type& op) { if(op.set) { // The assembler code contained a parameter at this point. // A parameter may be an identifier (from flush_id()), // or an integer constant. // Identifiers may also be names of CPU registers. // Calculate the identifiers. auto r = simplify_expression(op.terms); u16 value = r.first; int register_index = r.second; bool resolved = op.terms.empty(); if(!resolved) forward_declarations.emplace_back( pc, std::move(op.terms) ); // Determine the type of operand to synthesize bool has_register = register_index >= 0; bool has_brackets = op.brackets; bool has_offset = !resolved || value || !has_register; bool offset_is_small = resolved && value <= 0x1F; // Sanity checking if((has_brackets | has_register) && dat) std::fprintf(stderr, "Error: Cannot use brackets or registers in DAT parameters.\n"); if(has_register && has_offset && !has_brackets) std::fprintf(stderr, "Error: Register + index without brackets is invalid.\n"); if(has_register && has_offset && register_index >= 8) std::fprintf(stderr, "Error: Register + index are only valid with base registers.\n"); if(has_register && has_brackets && (register_index >= 8 && register_index != 0x1B)) std::fprintf(stderr, "Error: Register references are only valid with base registers and SP.\n"); if(!has_register && !has_brackets && has_offset && offset_is_small && !dat) { // It's a small integer that fits into the instruction verbatim. memory[op.addr] |= (value + 0x20) << op.shift; } else { // It contains a register, an integer, or both. unsigned code = has_brackets ? 0x1E : 0x1F; if(has_register) code = register_index; if(has_register && has_brackets) code |= (has_offset ? 0x10 : 0x08); if(register_index == 0x1B && has_brackets) code = 0x19; // [SP] codes for PEEK if(!dat) memory[op.addr] |= code << op.shift; // Omit opcode for DAT if(has_offset) memory[pc++] = value; } } op.clear(); sign = false; } void flush_id() { if(!id.empty()) { // The assembler code contained an identifier at this point. // Determine what to do with it. // Was it a label? Add it as a known symbol. if(label) { if(!symbols.insert( {id,pc} ).second) std::fprintf(stderr, "Error: Duplicate definition of '%s'\n", id.c_str()); SymbolLookup.insert( {pc,id} ); flush_operand(op); label = false; } else { // Was it an assembler mnemonic (i.e. name of an instruction)? auto ib = bops.find(id); if(ib != bops.end()) { // Yes. Place the opcode into the memory. flush_operand(op); if(DisassemblyListing) pclist.emplace_back( pc,"" ); auto b = ib->second; // This is an index of a string in ins_set[] dat = b == 0; op.addr = pc; op.shift = b < 64 ? 10 : 4; if(!dat) memory[pc++] = b < 64 ? b*16 : b%64; } else { // No. Remember the operand. flush_operand() will deal with it then. op.terms.back().second.emplace_back( sign,id ); op.set = true; sign = false; } } id.clear(); } } std::string line; void parse_code(const std::string& code) { std::size_t p=0, a, b=code.size(); for(a=0; a= 'A' && c <= 'Z') || c == '_' || (!id.empty() && c >= '0' && c <= '9')) { id += c; ++a; continue; } // Anything else ends an identifier. flush_id(); // A colon marks the current identifier as a label if(c == ':') { label=true; ++a; continue; } // Spaces will be ignored, but they do end an identifier if(std::isspace(c)) { ++a; continue; } // Some self-evident parsing of special characters if(c == ',') { flush_operand(op); op.shift += 6; ++a; continue; } if(c == '-') { sign = !sign; ++a; continue; } if(c == '*') { op.terms.emplace_back(); sign = false; ++a; continue; } if(c == '+' || c == ']') { ++a; continue; } if(c == '[') { op.brackets=true; ++a; continue; } // Anything else: If it isn't a number, it's an error size_t endptr; try { long l = std::stoi(code.substr(a,30), &endptr, 0); a += endptr; // Was a number. Remember it as an operand. if(sign) l = -l; op.terms.back().first += l; op.set = true; sign = false; } catch(...) { // Deal with invalid characters in input. std::fprintf(stderr, "Error: Invalid character: '%c'\n", c); ++a; continue; } } } public: explicit Assembler(const std::string& file_contents) : memory(0x10000) { // Build the list of reserved words. // Instructions: bops.insert( {"DAT",0} ); for(unsigned a=0; a() && { return std::move(memory); } }; struct Emulator { u16 memory[0x10000] = {}, reg[11] = {}; SDL_Surface* s = nullptr; Emulator() { // Initialize the SDL 1.2 library for graphics output. SDL_Init(SDL_INIT_VIDEO); SDL_InitSubSystem(SDL_INIT_VIDEO); std::signal(SIGINT, SIG_DFL); s = SDL_SetVideoMode(WIDTH*SCALE,HEIGHT*SCALE, 32,0); // For now, I am not using SDL 2.0, because it requires // considerably more lines of code to accomplish the // same things that I'm doing here. } void RenderGraphics(u8 pixbuf[WIDTH*HEIGHT]) { // Render the screen background. // It consists of 3x3 pixel cells, where each 3x3 cell can have // total of two colors from the global selection of 16 colors. for(unsigned y=0; y> ((x%3) + (y%3)*3)); if(x > 126) pixel = false; pixbuf[y*WIDTH+x] = (bg_cell >> (pixel ? 12 : 8)) & 0xF; } // Render the sprites atop that background const u16* ctrl = &memory[SPRITE_BASE], *ctrlend = ctrl + 2*N_SPRITES; for(; ctrl < ctrlend; ctrl += 2) { // Read the sprite header unsigned mode = (ctrl[1] >> 14) & 0x3; unsigned color = (ctrl[1] >> 10) & 0xF; unsigned bitsperpixel = ((mode&2)?3:4)-mode; // 4,2,2,1 // Calculate the sprite location and size int sx = -64 + (ctrl[0] >> 8 ); unsigned w = (mode&2) ? 16 : 8; int sy = -64 + (ctrl[0] & 0xFF); unsigned h = (mode&1) ? 16 : 8; // Render the sprite graphics. unsigned addr = GFX_BASE + 2*(ctrl[1] & 0x3FF); for(unsigned pixno=0, y=0; y> (16-bitsperpixel); if(pix != (mode ? color : 0)) if(sx >= 0 && sy >= 0 && sx < WIDTH && sy < HEIGHT) pixbuf[sy*WIDTH+sx] = (mode==3 ? color : pix); } } } void RenderText(u8 pixbuf[WIDTH*HEIGHT]) { static const u32 font4x8[256] = {0x00000000,0x06f99f96,0x069ff9f6,0x044eeea0,0x044eee44,0x0e44aa44,0x0e4eee44,0x004ee400,0xffb11bff,0x004aa400,0xffb55bff, 0xffb55bff,0x04e4aa40,0x0cc44757,0x0ff55557,0x02b6f6d4,0x08cefec8,0x0137f731,0x06f666f6,0x0a0aaaaa,0x03337bb7,0xe1699687, 0x0ff00000,0xf6f666f6,0x066666f6,0x06f66666,0x002ff200,0x004ff400,0x00f80000,0x0006f600,0x0fff6660,0x0666fff0,0x00000000, 0x04044444,0x00000aaa,0x00aeaea0,0x04e248e4,0x0a84442a,0x3ea54aa4,0x00000426,0x02488842,0x08422248,0x00a4e4a0,0x0044e440, 0x42600000,0x0000e000,0x04400000,0x08844422,0x04aaeea4,0x0e4444c4,0x0e8442a4,0x04a242a4,0x0222eaa2,0x04a22c8e,0x04aac8a4, 0x0884422e,0x04aa4aa4,0x04a26aa4,0x04400440,0x84400440,0x01248421,0x000e0e00,0x08421248,0x040442a4,0x068eeaa4,0x0aaaeaa4, 0x0caacaac,0x04a888a4,0x0caaaaac,0x0e88c88e,0x0888c88e,0x04aae8a4,0x0aaaeaaa,0x0e44444e,0x04a22222,0x0aaaccaa,0x0e888888, 0x0aaaeeea,0x0aaeeea2,0x0eaaaaae,0x0888caac,0x24aaaaa4,0x0aaacaac,0x04a248a4,0x0444444e,0x0eaaaaaa,0x044aaaaa,0x0aeeaaaa, 0x0aae4eaa,0x04444aaa,0x0e84442e,0x06444446,0x02244880,0x06222226,0x0000ae40,0xf0000000,0x00000246,0x06a62c00,0x0caaac88, 0x04a8a400,0x06aaa622,0x068ea400,0x0444e442,0xc26aa600,0x0aaaac88,0x04444c04,0x4a222202,0x0aacaa88,0x0e44444c,0x0aaeea00, 0x0aaaac00,0x04aaa400,0x88caac00,0x326aa600,0x0888ac00,0x0c248600,0x02444e40,0x06aaaa00,0x044aaa00,0x0aeeaa00,0x0aa4aa00, 0xc26aaa00,0x0e842e00,0x02448442,0x04440444,0x08442448,0x000000a5,0x0eaa4400,0x24a888a4,0x06aaaa0a,0x068ea442,0x06a62ca4, 0x06a62c0a,0x06a62c48,0x06a62cee,0xc4688600,0x068ea4a4,0x068ea40a,0x068ea448,0x0444c00a,0x0444c0a4,0x0444c048,0x0aaeaa4a, 0x0aaeaa4e,0x0e8c8e42,0x02cc6680,0x0baafaa7,0x04aaa4a4,0x04aaa40a,0x04aaa448,0x06aaa0a4,0x06aaa048,0xc26aaa0a,0x04aaaa4a, 0x04aaaa0a,0x44a88a44,0x0e88c8a4,0x04e4e4aa,0x09afacac,0x0844e452,0x06a62c42,0x0444c042,0x04aa4042,0x06aaa042,0x0aaac0a5, 0x0aeea20e,0x00e06aa6,0x00e04aa4,0x04a84404,0x00470000,0x002e0000,0x0e42ce44,0x02ea2e44,0x04444404,0x0055a550,0x00aa5aa0, 0x28282828,0x5a5a5a5a,0x7d7d7d7d,0x44444444,0x4444c444,0x444c4c44,0xaaaaaaaa,0xaaaae000,0x444c4c00,0xaaaa2aaa,0xaaaaaaaa, 0xaaaa2e00,0x000e2aaa,0x0000eaaa,0x000c4c44,0x4444c000,0x00007444,0x0000f444,0x4444f000,0x44447444,0x0000f000,0x4444f444, 0x44474744,0xaaaabaaa,0x000f8baa,0xaaab8f00,0x000f0baa,0xaaab0f00,0xaaab8baa,0x000f0f00,0xaaab0baa,0x000f0f44,0x0000faaa, 0x444f0f00,0xaaaaf000,0x0000faaa,0x00074744,0x44474700,0xaaaaf000,0xaaaafaaa,0x444f4f44,0x0000c444,0x44447000,0xffffffff, 0xffff0000,0xcccccccc,0x33333333,0x0000ffff,0x05aa5000,0x88caaca4,0x08888ae0,0x0aaaaaf0,0x0f94249f,0x04aaa700,0x84655550, 0x02222a50,0x0e4aaa4e,0x0699f996,0x0f699960,0x06962430,0x069f9600,0x08caea62,0x0068e860,0x0aaaaa40,0x00e0e0e0,0x0e044e44, 0x0e084248,0x0e024842,0x44444520,0x08444444,0x0040e040,0x00a50a50,0x00004aa4,0x00004000,0x00006000,0x046a2223,0x0000aaac, 0x0000e42c,0x00eeee00,0x00000000}; for(unsigned idx=0, y=0; y<12; ++y) for(unsigned x=0; x<32; ++x) { u16 v = memory[GFX_BASE + idx++]; if(v == 13) break; unsigned fg = v>>12, bg = (v>>8)&0xF, c = v&0xFF; if(!fg && !bg) c = 0x0F; c = font4x8[c]; for(unsigned yp=0; yp<8; ++yp) for(unsigned xp=0; xp<4; ++xp, c>>=1) pixbuf[(y*8+yp)*WIDTH + x*4+3-xp] = (c&1) ? fg : bg; } } void tick(unsigned n=1) { // For every 2000 CPU instructions, the screen will be refreshed. static unsigned count=0; count += n; if(count < 2000) return; count -= 2000; u8 pixbuf[WIDTH*HEIGHT]; if(memory[DISPLAY_BASE] & 1) RenderGraphics(pixbuf); else RenderText(pixbuf); // This is our global palette. static const u32 palette[16] = { 0x000000, 0x1B2632, 0x493C2B, 0x2F484E, 0x005784, 0xBE2633, 0x44891A, 0xA46422, 0x31A2F2, 0xE06F8B, 0xEB8931, 0x9D9D9D, 0xA3CE27, 0xB2DCEF, 0xFFE26B, 0xFFFFFF, }; // Place the pixels in the screen surface, also scaling it in the process. constexpr int span=1536, wid = span*12/2048, ywid=wid, iqwid=wid*18/10, buf=wid*2; static float sines[17][span + buf*3], iqkernel[iqwid], Y[16]; static bool cache_valid = false; if(!cache_valid) { for(int x=0; x> 16) + 0.587*((a >> 8)&0xFF) + 0.114*(a & 0xFF))/255.; float i = (0.596*(a >> 16) +-0.274*((a >> 8)&0xFF) +-0.322*(a & 0xFF)); float q = (0.211*(a >> 16) +-0.523*((a >> 8)&0xFF) + 0.312*(a & 0xFF)); // Save the luma in Y[p]; convert the chroma from polar into angular format float d = std::hypot(q, i) / 255., A = std::atan2(q, i); for(int x=0; x255)k=255; } int g; {int&k=g; k = ((I*-0.27f + Q*-0.65f) + Y/ywid)*255; if(k<0) k=0; if(k>255)k=255; } int b; {int&k=b; k = ((I*-1.10f + Q* 1.70f) + Y/ywid)*255; if(k<0) k=0; if(k>255)k=255; } ((u32*)(s->pixels))[ y*WIDTH*SCALE + x] = (r<<16) + (g<<8) + b; } } // Refresh the screen. SDL_Flip(s); // Also check for keyboard input events. SDL_Event event = { }; SDL_PollEvent( &event ); Uint8 *keystate = SDL_GetKeyState(NULL); #define keyonce(n) \ [&keystate]() -> bool { \ static bool old=false; \ bool res = keystate[n] && !old; \ old = keystate[n]; \ return res; }() // Put new keys into the device's keyboard buffer. static unsigned keyptr = 0; if(keyonce(SDLK_UP)) { if(!memory[INPUT_BASE+keyptr]) memory[INPUT_BASE+keyptr]=3; keyptr=(keyptr+1)&0xF; } if(keyonce(SDLK_DOWN)) { if(!memory[INPUT_BASE+keyptr]) memory[INPUT_BASE+keyptr]=4; keyptr=(keyptr+1)&0xF; } if(keyonce(SDLK_LEFT)) { if(!memory[INPUT_BASE+keyptr]) memory[INPUT_BASE+keyptr]=1; keyptr=(keyptr+1)&0xF; } if(keyonce(SDLK_RIGHT)) { if(!memory[INPUT_BASE+keyptr]) memory[INPUT_BASE+keyptr]=2; keyptr=(keyptr+1)&0xF; } } // Return a parameter to an instruction. Parameters are read-write, // i.e. the instruction may either read or write the register/memory location. template u16& value(u16& v) { // When skipping=true, the return value is insignificant; it only matters // whether PC is incremented the right amount, and that there are no side effects. switch( ((v&0x38)==0x18) ? (v&7)^skipping : 8+v/8 ) { enum { POP,PEEK,PUSH,reg_SP,reg_PC,reg_O,abs_mem,mem_lit, regs,reg_mem,idx_mem }; // Values 0x00-0x07 decode into basic registers: A,B,C,X,Y,Z,I,J case regs: return reg[v&7]; // Values 0x08-0x0F decode into indirect memory references. case reg_mem: return memory[ reg[v&7] ]; // Values 0x10-0x17 decode into indirect indexed memory references. case idx_mem: tick(); return memory[u16( reg[v&7] + memory[ reg[PC]++ ] )]; // Values 0x18-0x1F decode into various things: case POP: return memory[ reg[SP]++ ]; case PEEK: return memory[ reg[SP] ]; case PUSH: return memory[ --reg[SP] ]; case reg_SP: return reg[SP]; case reg_PC: return reg[PC]; case reg_O: return reg[EX]; case abs_mem: tick(); return memory[ memory[ reg[PC]++ ] ]; case mem_lit: tick(); return v = memory[ reg[PC]++ ]; // Values 0x20-0x3F decode into literals 0x00-0x1F. default: return v &= 0x1F; // Literals are actually read-only. Because the instruction _may_ // write into the target operand even if it is a literal, the // literals will be first stored into a temporary variable (v), // which can then be harmlessly overwritten. } } template void op() { tick(); if(DisassemblyTrace && !skipping) { int l = std::fprintf(stderr, "%04X %04X|%s", reg[PC], memory[reg[PC]], Disassemble(reg[PC],memory).c_str()); std::fprintf(stderr, "%*s", 50-l, ""); for(int i=0; i<10; ++i) std::fprintf(stderr, " %s:%04X", regnames[i], reg[i]); std::fprintf(stderr, "\n"); } // Read the next instruction from memory: u16 v = memory[reg[PC]++]; // Parse the individual components of the instruction: // Instruction format: bbbbbb aaaaaa oooo u16 o = (v & 0x0F), aa = (v>>4) & 0x3F, bb = (v>>10) & 0x3F; // List of basic instructions. Chosen by the value of "o": enum { nbi,SET,ADD,SUB,MUL,DIV,MOD,SHL,SHR,AND,BOR,XOR,IFE,IFN,IFG,IFB }; // List of non-basic instructions (nbi), chosen by the value of "aa": enum { JSR=0x11 }; // Parse the two parameters. (Note: "a" is skipped when nbi.) u16& a = o==nbi ? v : value(aa); u32 wa = a; u16& b = value(bb); u32 wr; // Then execute the instruction (unless the instruction is to be skipped). if(skipping) { if(o>=IFE && o<=IFB) op(); } else switch(o==nbi ? aa+0x10 : o) { // A simple move. It can also be used as // a PUSH/POP/RET/JMP with properly chosen parameters. case SET: tick(0); a = b; break; // Simple binary operations. case AND: tick(0); a &= b; break; case BOR: tick(0); a |= b; break; case XOR: tick(0); a ^= b; break; // Fundamental arithmetic operations. The overflow is stored in the EX register. case ADD: tick(1); wr = a+b; a = wr; reg[EX] = wr>>16; break; case SUB: tick(1); wr = a-b; a = wr; reg[EX] = wr>>16; break; case MUL: tick(1); wr = wa*b; a = wr; reg[EX] = wr>>16; break; // Bit-shifting left. EX stores the bits that were shifted out from the register. case SHL: tick(0); wr = wa<>16; break; // Division and modulo. If the divisor is 0, result is 0. case MOD: tick(2); a = b ? a%b : 0; break; case DIV: tick(2); wr = b ?(wa<<16)/b :0; a = wr>>16; reg[EX] = wr; break; // Bit-shifting right. EX stores the bits that were shifted out from the register. case SHR: tick(0); wr = (wa<<16) >> b; a = wr>>16; reg[EX] = wr; break; // Conditional execution. Skips the next instruction if the condition doesn't match. case IFE: tick(1); if(!(a == b)) op(); break; case IFN: tick(1); if(!(a != b)) op(); break; case IFG: tick(1); if(!(a > b)) op(); break; case IFB: tick(1); if(!(a & b)) op(); break; // Jump-To-Subroutine: Pushes the current program counter and chooses a new one. case JSR: tick(1); memory[--reg[SP]] = reg[PC]; reg[PC] = b; break; // Anything else is invalid. default: std::fprintf(stderr, "Invalid opcode %04X at PC=%X\n", v, reg[PC]); } } public: void run() { // Infinite loop! for(;;) op(); } }; int main() { // Declare the emulator. Emulator emu; // Load the initial RAM contents. char Buf[131072]; int len = std::fread(Buf, 1, sizeof(Buf), stdin); std::vector mem = Assembler( {Buf,Buf+len} ); std::copy(mem.begin(), mem.end(), std::begin(emu.memory)); //std::fwrite(emu.memory, 0x10000, 2, stdout); // Launch the emulator. emu.run(); }