1 /** 2 * RAM dump loader/saver 3 */ 4 module dcpu.ram_io; 5 6 import core.stdc.stdlib, std.stdio, std.bitmanip, std.conv, std.array, std..string; 7 8 /// Type of machine code file 9 enum TypeHexFile { 10 lraw, /// Little-endian raw binary file 11 braw, /// Big-endian raw binary file 12 ahex, /// Hexadecimal ASCII file 13 hexd, /// Hexadecimal ASCII dump file 14 b2, /// Base 2 binary data (0bxxxxxxxx_xxxxxxxx) 15 dat /// Assembly DATs (DAT 0x0000) 16 } 17 18 /** 19 * Load a file with a image of a RAM 20 * Params: 21 * type = Type of file 22 * file = Name and path of the file 23 * Returns a array with a raw binary image of the file 24 */ 25 ushort[] load_ram(TypeHexFile type, const string filename ) 26 in { 27 assert (filename.length >0, "Invalid filename"); 28 } body { 29 auto f = File(filename, "r"); 30 scope(exit) {f.close();} 31 32 ushort[] img = new ushort[0]; 33 34 ulong i; 35 if (type == TypeHexFile.lraw || type == TypeHexFile.braw) { // RAW binary file 36 while (i < 0x10000 && !f.eof) { 37 ubyte[2] buffer = [0, 0]; 38 if (f.rawRead(buffer).length == 0) { 39 break; // EOF 40 } 41 42 if (type == TypeHexFile.lraw) { // little-endian 43 img ~= littleEndianToNative!ushort(buffer); 44 } else { 45 img ~= bigEndianToNative!ushort(buffer); 46 } 47 i++; 48 } 49 50 } else if (type == TypeHexFile.ahex) { // plain ASCII hex file 51 foreach ( line; f.byLine()) { // each line only have a hex 16-bit word 52 if (i >= 0x1000) 53 break; 54 img ~= parse!ushort(line, 16); 55 i++; 56 } 57 } else if (type == TypeHexFile.hexd) { // plain ASCII hex dump file 58 foreach ( line; f.byLine()) { // each line contains one or more words of 16 bit in hexadecimal 59 if (line.length < 1 || line[0] == ';') { 60 continue; // Skip line, becasue it's a comment 61 } 62 63 auto words = split(strip(line)); 64 if (words.length < 2 || words[0].length < 4) { 65 throw new Exception("Bad format. Expected Addr: hexdata -> " ~ cast(string)line ); 66 } 67 68 if (words[0][0..2] == "0x" || words[0][0..2] == "0X") 69 words[0] = words[0][2..$]; 70 ushort addr = parse!ushort(words[0], 16); 71 72 i=0; 73 foreach (word; words[1..$]) { 74 auto tmp = addr + i++; 75 if (tmp >= 0x10000) // Out of bounds 76 throw new Exception("Bad format. Data out of bounds " ~ format("0x%04X", tmp)); 77 78 if (img.length <= tmp) 79 img.length = cast(size_t)(tmp +1); 80 81 if (word.length > 3) { 82 img[cast(size_t)(tmp)] = parse!ushort(word, 16); 83 } 84 } 85 } 86 } else if (type == TypeHexFile.b2) { // plain ASCII list of numbers in base 2 (0bxxxxxxxx_xxxxxxxx) 87 import std.algorithm; 88 foreach ( line; f.byLine()) { // each line contains one or more words of 16 bit in hexadecimal 89 // Keep alone the number in base 2 90 line = chompPrefix(chompPrefix(line.strip(' '), "0B"), "0b"); 91 if (line.length < 16 ) { 92 continue; // Skip line because it's a bad line (ushort -> 16 bits) 93 } 94 auto r = findSplit(line, ['_']); 95 line = r[0] ~ r[2]; // Skips '_' 96 97 img ~= parse!ushort(line, 2); 98 } 99 } else if (type == TypeHexFile.dat) { // assembly file that contains dat lines with code. Only process DAT lines 100 foreach ( line; f.byLine()) { 101 line = strip(line); 102 if (line.length <= 0) { 103 continue; 104 } 105 if (line[0] == '.') { // Handle the '.' 106 line = line[1..$]; 107 } 108 // dat dddd|0xhhhh 109 if (line.length < 5 || ( 110 line[0..3] != "dat" && line[0..3] != "DAT" ) ) { 111 continue; // Skip line 112 } 113 114 auto words = split(line[4..$], ","); 115 if (words.length < 1 ) { 116 throw new Exception("Bad format. DAT without data"); 117 } 118 119 foreach (word; words) { 120 if (img.length >= 0x10000) // Out of bounds 121 throw new Exception("Bad format. Data out of bounds " ~ format("0x%04X", img.length)); 122 word = strip(word); 123 if (word.length > 3 && (word[0..2] == "0x" || word[0..2] == "0X")) { 124 word = word[2..$]; 125 img ~= parse!ushort(word, 16); 126 } else if (word.length > 1) { 127 img ~= parse!ushort(word, 10); 128 } 129 } 130 } 131 } else { 132 throw new Exception("Not implemented file type"); 133 } 134 135 return img; 136 } 137 138 void save_ram(TypeHexFile type, const string filename , ushort[] img) 139 in { 140 assert (filename.length >0, "Invalid filename"); 141 assert (img.length < 0x10000, "Invalid ram image"); 142 } body { 143 auto f = File(filename, "w"); 144 scope(exit) {f.close();} 145 146 if (type == TypeHexFile.lraw || type == TypeHexFile.braw) { // RAW binary file 147 foreach (word; img) { 148 ubyte[2] dbyte = void; 149 if (type == TypeHexFile.lraw) { // little-endian 150 dbyte = nativeToLittleEndian!ushort(word); 151 } else { 152 dbyte = nativeToBigEndian!ushort(word); 153 } 154 f.rawWrite(dbyte); 155 } 156 } else if (type == TypeHexFile.ahex) { // plain ASCII hex file 157 foreach ( word; img) { // each line only have a hex 16-bit word 158 f.writeln(format("%04X", word)); 159 } 160 } else if (type == TypeHexFile.hexd) { // plain ASCII hex dump file 161 foreach (addr ,word; img) { 162 if ((addr % 8) == 0) { 163 f.write(format("0x%04X: ", addr)); 164 } 165 f.write(format("%04X ", word)); 166 if (addr > 6 && ((addr+1) % 8) == 0) { 167 f.writeln(); 168 } 169 } 170 } else if (type == TypeHexFile.b2) { // plain ASCII list of numbers in base 2 (0bxxxxxxxx_xxxxxxxx) 171 foreach (word; img) { 172 f.writeln(format("0b%b, ", word)); 173 } 174 } else if (type == TypeHexFile.dat) { // assembly file that contains dat lines with code. Only process DAT lines 175 foreach (word; img) { 176 f.writeln(format("DAT %04X ", word)); 177 } 178 } else { 179 throw new Exception("Not implemented file type"); 180 } 181 } 182