Created
October 14, 2013 18:55
-
-
Save etscrivner/6980266 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/**************************************** | |
* Disassembler for Chip8 binary file format. | |
* | |
* Author: Eric Scrivner (eric@shift.com) | |
*/ | |
import core.exception; | |
import std.exception; | |
import std.stdio; | |
import std.string; | |
import std.file : file_exists = exists; | |
/** The maximum number of bytes to read from a file at once */ | |
immutable uint FILE_CHUNK_SIZE = 4096; | |
/**************************************** | |
* Assembles a correctly byte ordered instruction from the two component | |
* bytes. | |
* | |
* This method constructs a new `ushort' value from two `ubyte' values and is | |
* used to assemble instructions read from a Chip8 program file. | |
* | |
* Params: | |
* msb = the most significant byte | |
* lsb = the least significant byte | |
* | |
* Returns: The fully assembled instruction | |
*/ | |
pure ushort assemble_instruction(immutable ubyte msb, immutable ubyte lsb) | |
body { | |
return msb << 8 | lsb; | |
} | |
unittest { | |
assert(assemble_instruction(0xFF, 0xAA) == 0xFFAA); | |
} | |
/**************************************** | |
* Print out the instruction indicated by the given 16-bit value. | |
* | |
* Params: | |
* instruction = The instruction to be printed | |
* | |
* Returns: The string equivalent of the given instruction. | |
* | |
*/ | |
string instruction_to_string(immutable ushort instruction) | |
body { | |
switch(instruction & 0xF000) { | |
default: | |
return "UNKNOWN"; | |
case 0x0000: | |
switch (instruction & 0x00FF) { | |
case 0xE0: | |
return "CLS"; | |
case 0xEE: | |
return "RET"; | |
default: | |
return format("SYS %03X", instruction & 0x0FFF); | |
} | |
break; | |
case 0x1000: | |
return format("JP %03X", instruction & 0x0FFF); | |
case 0x2000: | |
return format("CALL %03X", instruction & 0x0FFF); | |
case 0x3000: | |
return format("SE V%01X, %02X", (instruction >> 8) & 0xF, instruction & 0xFF); | |
} | |
} | |
unittest { | |
assert(instruction_to_string(0x00E0) == "CLS"); | |
assert(instruction_to_string(0x00EE) == "RET"); | |
assert(instruction_to_string(0x01AB) == "SYS 1AB"); | |
assert(instruction_to_string(0x1ABC) == "JP ABC"); | |
assert(instruction_to_string(0x2ABC) == "CALL ABC"); | |
assert(instruction_to_string(0x3F23) == "SE VF, 23"); | |
} | |
/**************************************** | |
* Displays the disassembled format of the given file assuming it is a Chp8 | |
* ROM file. | |
* | |
* Params: | |
* file = The file to be disassembled | |
* | |
*/ | |
void disassemble_file(File file) | |
in { | |
assert(file.isOpen(), "File is not open"); | |
assert(file.size % 2 == 0, "File does not contain an even number of bytes"); | |
} | |
body { | |
foreach (ubyte[] buffer; file.byChunk(FILE_CHUNK_SIZE)) { | |
for (uint i = 0; i < buffer.length; i += 2) { | |
writeln(i, " ", i + 1, "(", buffer.length, ")"); | |
auto next_instruction = assemble_instruction(buffer[i], buffer[i + 1]); | |
writeln(instruction_to_string(next_instruction)); | |
} | |
} | |
} | |
unittest { | |
assertThrown!AssertError(disassemble_file(File())); | |
} | |
void main(string args[]) | |
in { | |
assert(args.length > 1, "At least one command line argument required."); | |
assert(file_exists(args[1]), format("File `%s' does not exist.", args[1])); | |
} | |
body { | |
auto rom_file = File(args[1]); | |
disassemble_file(rom_file); | |
} | |
unittest { | |
assertThrown!AssertError(main([])); | |
assertThrown!AssertError(main(["badErroryName.d"])); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment