disassembler sorta
This commit is contained in:
		
							parent
							
								
									e09f663b9b
								
							
						
					
					
						commit
						0a08fa128b
					
				
							
								
								
									
										169
									
								
								source/app.d
								
								
								
								
							
							
						
						
									
										169
									
								
								source/app.d
								
								
								
								
							| 
						 | 
				
			
			@ -8,10 +8,175 @@ import imgui;
 | 
			
		|||
 | 
			
		||||
struct Chip8Status {
 | 
			
		||||
 | 
			
		||||
	struct Assembly {
 | 
			
		||||
		
 | 
			
		||||
		import assembler;
 | 
			
		||||
 | 
			
		||||
		alias OpCode = Assembler.OpCode;
 | 
			
		||||
		alias Instr = Assembler.Instruction;
 | 
			
		||||
		alias Arg = Assembler.Argument;
 | 
			
		||||
 | 
			
		||||
		enum ViewMode {
 | 
			
		||||
			Memory,
 | 
			
		||||
			Disassembly
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private {
 | 
			
		||||
 | 
			
		||||
			Chip8* emu_;
 | 
			
		||||
			ViewMode mode_;
 | 
			
		||||
			int range_start_;
 | 
			
		||||
			int range_end_;
 | 
			
		||||
 | 
			
		||||
			// disassembly state
 | 
			
		||||
			Instr[] disassembly_;
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void initialize(Chip8* emu) {
 | 
			
		||||
			emu_ = emu;
 | 
			
		||||
		} // initialize
 | 
			
		||||
		
 | 
			
		||||
		static const (char)* formatQual(Arg a, bool with_comma = false) {
 | 
			
		||||
			if (with_comma) {
 | 
			
		||||
				switch (a) with (Arg) {
 | 
			
		||||
					case Vx, Vy, V0: return "V%hu, ";
 | 
			
		||||
					case nibble: return "%01X, ";
 | 
			
		||||
					case beit: return "0x%02X, ";
 | 
			
		||||
					case addr: return "0x%03X, ";
 | 
			
		||||
					default: return "%s, ";
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				switch (a) with (Arg) {
 | 
			
		||||
					case Vx, Vy, V0: return "V%hu";
 | 
			
		||||
					case nibble: return "0x%01X";
 | 
			
		||||
					case beit: return "0x%02X";
 | 
			
		||||
					case addr: return "0x%03X";
 | 
			
		||||
					default: return "%s";
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void disassemble() {
 | 
			
		||||
			ubyte[] instrs = emu_.ram[range_start_ .. range_end_];
 | 
			
		||||
			disassembly_ = Assembler.disassemble(instrs);
 | 
			
		||||
			writefln("%s", disassembly_);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void setRange(ushort start, ushort end) {
 | 
			
		||||
			range_start_ = start;
 | 
			
		||||
			range_end_ = end;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void draw() {
 | 
			
		||||
 | 
			
		||||
			import std.range : chunks;
 | 
			
		||||
 | 
			
		||||
			if (mode_ == ViewMode.Memory) igBegin("Emulator Assembly - Memory View");
 | 
			
		||||
			else if (mode_ == ViewMode.Disassembly) igBegin("Emulator Assembly - Disassembly View");
 | 
			
		||||
 | 
			
		||||
			if (igButton("Switch Mode")) {
 | 
			
		||||
				mode_ = cast(ViewMode)((mode_ + 1) % 2);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (mode_ == ViewMode.Memory) {
 | 
			
		||||
			} else if (mode_ == ViewMode.Disassembly) {
 | 
			
		||||
				igInputInt("Range Start: ", &range_start_);
 | 
			
		||||
				igInputInt("Range End: ", &range_end_);
 | 
			
		||||
				if (igButton("Disassemble")) {
 | 
			
		||||
					disassemble();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			if (range_start_ < 0) range_start_ = 0;
 | 
			
		||||
			if (range_start_ > ushort.max) range_start_ = 0;
 | 
			
		||||
 | 
			
		||||
			if (range_end_ < range_start_) range_end_ = 0;
 | 
			
		||||
			if (range_end_ > ushort.max) range_end_ = 0;
 | 
			
		||||
 | 
			
		||||
			if (mode_ == ViewMode.Memory) {
 | 
			
		||||
				/*
 | 
			
		||||
				foreach (ref ubyte[] b; emu_.ram[0x200 + range_start_ .. range_end_].chunks(2)) {
 | 
			
		||||
					ushort opcode = (*(cast(ushort*)(b.ptr)));
 | 
			
		||||
					igText("0x%04X", opcode);
 | 
			
		||||
				}
 | 
			
		||||
				*/
 | 
			
		||||
			} else if (mode_ == ViewMode.Disassembly) {
 | 
			
		||||
				foreach (ref instr; disassembly_) {
 | 
			
		||||
					switch  (instr.args) {
 | 
			
		||||
						case 3:
 | 
			
		||||
 | 
			
		||||
							igText("%s", instr.op);
 | 
			
		||||
							igSameLine();
 | 
			
		||||
 | 
			
		||||
							Arg a1_arg;
 | 
			
		||||
							auto a1 = instr.a1(a1_arg);
 | 
			
		||||
							igText(formatQual(a1_arg), a1, true);
 | 
			
		||||
							igSameLine();
 | 
			
		||||
 | 
			
		||||
							Arg a2_arg;
 | 
			
		||||
							auto a2 = instr.a2(a2_arg);
 | 
			
		||||
							igText(formatQual(a2_arg), a2, true);
 | 
			
		||||
							igSameLine();
 | 
			
		||||
 | 
			
		||||
							Arg a3_arg;
 | 
			
		||||
							auto a3 = instr.a3(a3_arg);
 | 
			
		||||
							igText(formatQual(a3_arg), a3);
 | 
			
		||||
 | 
			
		||||
							break;
 | 
			
		||||
 | 
			
		||||
						case 2:
 | 
			
		||||
							
 | 
			
		||||
							igText("%s", instr.op);
 | 
			
		||||
							igSameLine();
 | 
			
		||||
 | 
			
		||||
							Arg a1_arg;
 | 
			
		||||
							auto a1 = instr.a1(a1_arg);
 | 
			
		||||
							igText(formatQual(a1_arg), a1, true);
 | 
			
		||||
							igSameLine();
 | 
			
		||||
 | 
			
		||||
							Arg a2_arg;
 | 
			
		||||
							auto a2 = instr.a2(a2_arg);
 | 
			
		||||
							igText(formatQual(a2_arg), a2);
 | 
			
		||||
							
 | 
			
		||||
							break;
 | 
			
		||||
 | 
			
		||||
						case 1:
 | 
			
		||||
 | 
			
		||||
							igText("%s", instr.op);
 | 
			
		||||
							igSameLine();
 | 
			
		||||
 | 
			
		||||
							Arg a1_arg;
 | 
			
		||||
							auto a1 = instr.a1(a1_arg);
 | 
			
		||||
							igText(formatQual(a1_arg), a1);
 | 
			
		||||
							
 | 
			
		||||
							break;
 | 
			
		||||
 | 
			
		||||
						case 0:
 | 
			
		||||
 | 
			
		||||
							igText("%s", instr.op);
 | 
			
		||||
							break;
 | 
			
		||||
						
 | 
			
		||||
						default:
 | 
			
		||||
							break;
 | 
			
		||||
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			igEnd();
 | 
			
		||||
 | 
			
		||||
		} // draw
 | 
			
		||||
 | 
			
		||||
	} // Assembly
 | 
			
		||||
 | 
			
		||||
	// emu ptr
 | 
			
		||||
	Emulator* run_;
 | 
			
		||||
	Chip8* emu_;
 | 
			
		||||
 | 
			
		||||
	// assembly/disassembly
 | 
			
		||||
	Assembly ass_;
 | 
			
		||||
 | 
			
		||||
	// loaded program
 | 
			
		||||
	const (char)* loaded_program;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +190,7 @@ struct Chip8Status {
 | 
			
		|||
	void initialize(Emulator* run, Chip8* emu) {
 | 
			
		||||
		this.run_ = run;
 | 
			
		||||
		this.emu_ = emu;
 | 
			
		||||
		ass_.initialize(emu);
 | 
			
		||||
	} // initialize
 | 
			
		||||
 | 
			
		||||
	alias Callback = float delegate(int idx, const char** out_text);
 | 
			
		||||
| 
						 | 
				
			
			@ -65,6 +231,7 @@ struct Chip8Status {
 | 
			
		|||
		loaded_program = "chip8_picture.ch8";
 | 
			
		||||
		auto buf = read("programs/chip8_picture.ch8");
 | 
			
		||||
		emu_.load(0x200, buf); // do ze load yes, will copy all the data in
 | 
			
		||||
		ass_.setRange(cast(ushort)0x200, cast(ushort)(0x200 + cast(ushort)buf.length));
 | 
			
		||||
 | 
			
		||||
	} // loadShortcut
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -94,6 +261,8 @@ struct Chip8Status {
 | 
			
		|||
 | 
			
		||||
	void draw() {
 | 
			
		||||
 | 
			
		||||
		ass_.draw();
 | 
			
		||||
 | 
			
		||||
		if (!status_menu_) return;
 | 
			
		||||
 | 
			
		||||
		if (igBeginMainMenuBar()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,44 +43,51 @@ struct Assembler {
 | 
			
		|||
		0xFx65 - LD Vx, [I]
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	enum OpCode {
 | 
			
		||||
	enum OpCode : const (char)* {
 | 
			
		||||
 | 
			
		||||
		UNK, // UNKNOWN
 | 
			
		||||
		UNK = "UNK", // UNKNOWN
 | 
			
		||||
 | 
			
		||||
		CLS, // CLS
 | 
			
		||||
		RET, // RET
 | 
			
		||||
		CALL, // CALL addr
 | 
			
		||||
		CLS = "CLS", // CLS
 | 
			
		||||
		RET = "RET", // RET
 | 
			
		||||
		CALL = "CALL", // CALL addr
 | 
			
		||||
 | 
			
		||||
		ADD, // ADD Vx, Vy
 | 
			
		||||
		ADD = "ADD", // ADD Vx, Vy
 | 
			
		||||
			 // ADD Vx, byte
 | 
			
		||||
			 // ADD I, Vx
 | 
			
		||||
 | 
			
		||||
		SUB, // SUB Vx, Vy
 | 
			
		||||
		SUB = "SUB", // SUB Vx, Vy
 | 
			
		||||
			 // SUB Vx, byte
 | 
			
		||||
 | 
			
		||||
		SUBN, // SUBN Vx, Vy
 | 
			
		||||
		SUBN = "SUBN", // SUBN Vx, Vy
 | 
			
		||||
 | 
			
		||||
		SE, // SE Vx, Vy
 | 
			
		||||
		SE = "SE", // SE Vx, Vy
 | 
			
		||||
			// SE Vx, byte
 | 
			
		||||
 | 
			
		||||
		SNE, // SNE Vx, byte
 | 
			
		||||
		SKP, // SKP Vx
 | 
			
		||||
		SKNP, // SKNP Vx
 | 
			
		||||
		SNE = "SNE", // SNE Vx, byte
 | 
			
		||||
		SKP = "SKP", // SKP Vx
 | 
			
		||||
		SKNP = "SKNP", // SKNP Vx
 | 
			
		||||
 | 
			
		||||
		SHL, // SHL Vx {, Vy}
 | 
			
		||||
		SHR, // SHR Vx {, Vy}
 | 
			
		||||
		XOR, // XOR Vx, Vy
 | 
			
		||||
		AND, // AND Vx, Vy
 | 
			
		||||
		OR, // OR Vx, Vy
 | 
			
		||||
		SHL = "SHL", // SHL Vx {, Vy}
 | 
			
		||||
		SHR = "SHR", // SHR Vx {, Vy}
 | 
			
		||||
		XOR = "XOR", // XOR Vx, Vy
 | 
			
		||||
		AND = "AND", // AND Vx, Vy
 | 
			
		||||
		OR = "OR", // OR Vx, Vy
 | 
			
		||||
 | 
			
		||||
		LD, // LD Vx, DT
 | 
			
		||||
		LD = "LD", // LD Vx, DT
 | 
			
		||||
			// LD DT, Vx
 | 
			
		||||
			// LD ST, Vx
 | 
			
		||||
			// LD Vx, K
 | 
			
		||||
			// LD [I], Vx
 | 
			
		||||
			// LD Vx, [I]
 | 
			
		||||
		
 | 
			
		||||
		RND // RND Vx, byte
 | 
			
		||||
		RND = "RND", // RND Vx, byte,
 | 
			
		||||
 | 
			
		||||
		JP = "JP", // JP addr
 | 
			
		||||
			// JP V0, addr
 | 
			
		||||
		
 | 
			
		||||
		DRW = "DRW", // DRW Vx, Vy, nibble
 | 
			
		||||
 | 
			
		||||
		SYS = "SYS" // SYS addr | probably unused? but still
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -90,20 +97,137 @@ struct Assembler {
 | 
			
		|||
 | 
			
		||||
		Vx,
 | 
			
		||||
		Vy,
 | 
			
		||||
		V0,
 | 
			
		||||
 | 
			
		||||
		I,
 | 
			
		||||
		I_addr,
 | 
			
		||||
		DT,
 | 
			
		||||
		ST,
 | 
			
		||||
		K,
 | 
			
		||||
		B,
 | 
			
		||||
		F,
 | 
			
		||||
		
 | 
			
		||||
		beit,
 | 
			
		||||
		nibble,
 | 
			
		||||
		addr,
 | 
			
		||||
		val
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct Instruction {
 | 
			
		||||
        OpCode op;
 | 
			
		||||
        Argument a1;
 | 
			
		||||
        Argument a2;
 | 
			
		||||
 | 
			
		||||
		@property {
 | 
			
		||||
			ubyte vx() {
 | 
			
		||||
				return (v_ & 0x0F00) >> 8;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ubyte vy() {
 | 
			
		||||
				return (v_ & 0x00F0) >> 4;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ubyte n() {
 | 
			
		||||
				return (v_ & 0x000F);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ubyte nn() {
 | 
			
		||||
				return (v_ & 0x00FF);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ushort nnn() {
 | 
			
		||||
				return (v_ & 0x0FFF);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private {
 | 
			
		||||
 | 
			
		||||
			ushort v_; // raw value
 | 
			
		||||
 | 
			
		||||
			OpCode op_;
 | 
			
		||||
			Argument a1_;
 | 
			
		||||
			Argument a2_;
 | 
			
		||||
			Argument a3_;
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void* extract(Argument a) {
 | 
			
		||||
			final switch (a) with (Argument) {
 | 
			
		||||
 | 
			
		||||
				case Vx: return cast(void*)(vx());
 | 
			
		||||
				case Vy: return cast(void*)(vy());
 | 
			
		||||
				case V0: return cast(void*)(0); 
 | 
			
		||||
 | 
			
		||||
				case I: return cast(void*)("I".ptr);
 | 
			
		||||
				case I_addr: return cast(void*)("[I]".ptr);
 | 
			
		||||
 | 
			
		||||
				case DT: return cast(void*)("DT".ptr);
 | 
			
		||||
				case ST: return cast(void*)("ST".ptr);
 | 
			
		||||
 | 
			
		||||
				case B: return cast(void*)("B".ptr);
 | 
			
		||||
				case F: return cast(void*)("F".ptr);
 | 
			
		||||
				case K: return cast(void*)("K".ptr);
 | 
			
		||||
 | 
			
		||||
				case nibble: return cast(void*)(n());
 | 
			
		||||
				case beit: return cast(void*)(nn());
 | 
			
		||||
				case addr: return cast(void*)(nnn());
 | 
			
		||||
				
 | 
			
		||||
				case Nil: return cast(void*)(0);
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		@property
 | 
			
		||||
		OpCode op() {
 | 
			
		||||
			return op_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void* a1(out Argument a) {
 | 
			
		||||
			a = a1_;
 | 
			
		||||
			return extract(a1_);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void* a2(out Argument a) {
 | 
			
		||||
			a = a2_;
 | 
			
		||||
			return extract(a2_);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void* a3(out Argument a) {
 | 
			
		||||
			a = a3_;
 | 
			
		||||
			return extract(a3_);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@property int args() {
 | 
			
		||||
			int args = 0;
 | 
			
		||||
			args += (a1_ != Argument.Nil);
 | 
			
		||||
			args += (a2_ != Argument.Nil);
 | 
			
		||||
			args += (a3_ != Argument.Nil);
 | 
			
		||||
			return args;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this(ushort v, OpCode op, Argument a) {
 | 
			
		||||
			v_ = v;
 | 
			
		||||
			op_ = op;
 | 
			
		||||
			a1_ = a;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		this(ushort v, OpCode op, Argument a1, Argument a2) {
 | 
			
		||||
			v_ = v;
 | 
			
		||||
			op_ = op;
 | 
			
		||||
			a1_ = a1;
 | 
			
		||||
			a2_ = a2;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		this(ushort v, OpCode op, Argument a1, Argument a2, Argument a3) {
 | 
			
		||||
			v_ = v;
 | 
			
		||||
			op_ = op;
 | 
			
		||||
			a1_ = a1;
 | 
			
		||||
			a2_ = a2;
 | 
			
		||||
			a3_ = a3;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this(ushort v, OpCode op) {
 | 
			
		||||
			v_ = v;
 | 
			
		||||
			op_ = op;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	import std.array;
 | 
			
		||||
| 
						 | 
				
			
			@ -129,52 +253,98 @@ struct Assembler {
 | 
			
		|||
 | 
			
		||||
	} // assemble
 | 
			
		||||
 | 
			
		||||
	const (char)[] disassemble(ubyte[] instructions) {
 | 
			
		||||
	static Instruction[] disassemble(ubyte[] instructions) {
 | 
			
		||||
 | 
			
		||||
		import std.range : chunks;
 | 
			
		||||
 | 
			
		||||
		foreach (ref ubyte[] i; instructions.chunks(2)) {
 | 
			
		||||
		auto instr_output = Appender!(Instruction[])();
 | 
			
		||||
 | 
			
		||||
			ushort opcode = (*(cast(ushort*)(i.ptr)));
 | 
			
		||||
		foreach (ref ubyte[] i; instructions.chunks(2)) {
 | 
			
		||||
			ushort opcode = i[0] << 8 | i[1];
 | 
			
		||||
 | 
			
		||||
			switch (opcode & 0xF000) {
 | 
			
		||||
				case 0x0000:
 | 
			
		||||
					switch (opcode & 0x0FFF) {
 | 
			
		||||
						case 0x00E0: // 0x00E0 Clears the screen. | CLS
 | 
			
		||||
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.CLS);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x00EE: // 0x00EE Returns from a subroutine. | RET
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.RET);
 | 
			
		||||
							break;
 | 
			
		||||
						default: // 0x0NNN Calls RCA 1802 program at address NNN. Not necessary for most ROMs. | SYS addr
 | 
			
		||||
							//assert(0, "0x0NNN RCA 1802 program opcode not implemented!");
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.SYS, Argument.addr);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x1000: // 0x1NNN Jumps to address NNN. | JP addr
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.JP, Argument.addr);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x2000: // 0x2NNN Calls subroutine at NNN. | CALL addr
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.CALL, Argument.addr);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x3000: // 0x3XNN Skips the next instruction if VX equals NN. | SE Vx, byte
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.SE, Argument.Vx, Argument.beit);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x4000: // 0x4XNN Skips the next instruction if VX doesn't equal NN. | SNE Vx, byte
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.SNE, Argument.Vx, Argument.beit);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x5000: // 0x5XYO Skips the next instruction if VX equals VY. | SE Vx, Vy
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.SE, Argument.Vx, Argument.Vy);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x6000: // 0x6XNN Sets VX to NN. | LD Vx, byte
 | 
			
		||||
				case 0x7000: // 0x7XNN Adds NN to VX. | AD Vx, byte
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.LD, Argument.Vx, Argument.beit);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x7000: // 0x7XNN Adds NN to VX. | ADD Vx, byte
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.ADD, Argument.Vx, Argument.beit);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x8000:
 | 
			
		||||
					switch (opcode) {
 | 
			
		||||
						case 0x0000: // 0x8XY0 Sets VX to the value of VY. | LD Vx, Vy
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.LD, Argument.Vx, Argument.Vy);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0001: // 0x8XY1 Sets VX to VX or VY. | OR Vx, Vy
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.OR, Argument.Vx, Argument.Vy);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0002: // 0x8XY2	Sets VX to VX and VY. | AND Vx, Vy
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.AND, Argument.Vx, Argument.Vy);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0003: // 0x8XY3 Sets VX to VX xor VY. | XOR Vx, Vy
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.XOR, Argument.Vx, Argument.Vy);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0004: // 0x8XY4 Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't. | ADD Vx, Vy
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.ADD, Argument.Vx, Argument.Vy);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0005: // 0x8XY5 VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't. | SUB Vx, Vy
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.SUB, Argument.Vx, Argument.Vy);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0006: // 0x8XY6 Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift. | SHR Vx {, Vy}
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.SHR, Argument.Vx); // FIXME? what about Vy here actually?
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0007: // 0x8XY7 Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't. | SUBN Vx, Vy
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.SUBN, Argument.Vx, Argument.Vy);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x000E: // 0x8XYE Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift. | SHL Vx {, Vy}
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.SHL, Argument.Vx); // FIXME? what about Vy here actually?
 | 
			
		||||
							break;
 | 
			
		||||
						default: // unhandled for some reason
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.UNK);
 | 
			
		||||
							writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x9000: // 0x9XYO Skips the next instruction if VX doesn't equal VY. | SNE Vx, Vy
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.SNE, Argument.Vx, Argument.Vy);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0xA000: // 0xANNN  Sets I to the address NNN. | LD I, addr
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.LD, Argument.I, Argument.addr);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0xB000: // 0xBNNN Jumps to the address NNN plus V0. | JP V0, addr
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.JP, Argument.V0, Argument.addr);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0xC000: // 0xCXNN Sets VX to the result of a bitwise and operation on a random number and NN. | RND Vx, byte
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.RND, Argument.Vx, Argument.beit);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				// 0xDXYN | DRW Vx, Vy, nibble
 | 
			
		||||
				// Sprites stored in memory at location in index register (I), 8bits wide.
 | 
			
		||||
| 
						 | 
				
			
			@ -183,11 +353,18 @@ struct Assembler {
 | 
			
		|||
				// Sprites are drawn starting at position VX, VY. N is the number of 8bit rows that need to be drawn.
 | 
			
		||||
				// If N is greater than 1, second line continues at position VX, VY+1, and so on.
 | 
			
		||||
				case 0xD000:
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.DRW, Argument.Vx, Argument.Vy, Argument.beit);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0xE000:
 | 
			
		||||
					switch (opcode & 0x000F) {
 | 
			
		||||
						case 0x000E: // 0xEX9E Skips the next instruction if the key stored in VX is pressed. | SKP Vx
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.SKP, Argument.Vx);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0001: // 0xEXA1 Skips the next instruction if the key stored in VX isn't pressed. | SKNP Vx
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.SKNP, Argument.Vx);
 | 
			
		||||
							break;
 | 
			
		||||
						default: // unhandled for some reason
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.UNK);
 | 
			
		||||
							writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
| 
						 | 
				
			
			@ -195,28 +372,48 @@ struct Assembler {
 | 
			
		|||
				case 0xF000:
 | 
			
		||||
					switch (opcode & 0x00FF) {
 | 
			
		||||
						case 0x0007: // 0xFX07 Sets VX to the value of the delay timer. | LD Vx, DT
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.LD, Argument.Vx, Argument.DT);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x000A: // 0xFX0A A key press is awaited, and then stored in VX. | LD Vx, K
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.LD, Argument.Vx, Argument.K);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0015: // 0xFX15 Sets the delay timer to VX. | LD DT, Vx
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.LD, Argument.DT, Argument.Vx);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0018: // 0xFX18 Sets the sound timer to VX. | LD ST, Vx
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.LD, Argument.ST, Argument.Vx);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x001E: // 0xFX1E Adds VX to I. | ADD I, Vx
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.ADD, Argument.I, Argument.DT);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0029: // 0xFX29 Sets I to the location of the sprite for the character in VX. | LD F, Vx
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.LD, Argument.F, Argument.DT);
 | 
			
		||||
							break;
 | 
			
		||||
						// 0xFX33 Stores the Binary-coded decimal representation of VX,
 | 
			
		||||
						//  with the most significant of three digits at the address in I,
 | 
			
		||||
						//  the middle digit at I plus 1, and the least significant digit at I plus 2.
 | 
			
		||||
						case 0x0033: // 0xFX33 ??? FIXME? | LD B, Vx
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.LD, Argument.B, Argument.Vx);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0055: // 0xFX55 Stores V0 to VX in memory starting at address I. | LD [I], Vx
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.LD, Argument.I_addr, Argument.DT);
 | 
			
		||||
							break;
 | 
			
		||||
						case 0x0065: // 0xFX65 Fills V0 to VX with values from memory starting at address I. | LD Vx, [I]
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.LD, Argument.Vx, Argument.I_addr);
 | 
			
		||||
							break;
 | 
			
		||||
						default: // unhandled for some reason
 | 
			
		||||
							instr_output ~= Instruction(opcode, OpCode.UNK);
 | 
			
		||||
							writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					instr_output ~= Instruction(opcode, OpCode.UNK);
 | 
			
		||||
					writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		return "";
 | 
			
		||||
		return instr_output.data;
 | 
			
		||||
 | 
			
		||||
	} // dissassemble
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue