├── src ├── Makefile ├── Makefile.win32 ├── crc.h └── n64hijack.cpp ├── Makefile.win32 ├── Makefile ├── u64asm-src ├── Makefile ├── Makefile.win32 ├── about.h ├── defines.h ├── crc.h ├── exp.h ├── symbols.h ├── err.h ├── asm.h ├── pre.h ├── n64.cpp └── opcode.h ├── README.md └── patcher.asm /src/Makefile: -------------------------------------------------------------------------------- 1 | all: n64hijack 2 | 3 | OBJS = crc.h 4 | 5 | n64hijack: $(OBJS) 6 | $(CXX) -o n64hijack n64hijack.cpp 7 | 8 | clean: 9 | rm -f n64hijack 10 | -------------------------------------------------------------------------------- /Makefile.win32: -------------------------------------------------------------------------------- 1 | .PHONY: src u64asm-src 2 | 3 | all: src u64asm-src 4 | 5 | src: 6 | $(MAKE) -C $@ -f Makefile.win32 7 | 8 | u64asm-src: 9 | $(MAKE) -C $@ -f Makefile.win32 10 | -------------------------------------------------------------------------------- /src/Makefile.win32: -------------------------------------------------------------------------------- 1 | all: n64hijack 2 | 3 | OBJS = crc.h 4 | 5 | n64hijack: $(OBJS) 6 | $(CXX) -static -o n64hijack n64hijack.cpp 7 | 8 | 9 | clean: 10 | rm -f n64hijack.exe -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: src u64asm-src 2 | 3 | all: src u64asm-src 4 | 5 | src: 6 | $(MAKE) -C $@ 7 | mv $@/n64hijack ./n64hijack 8 | 9 | u64asm-src: 10 | $(MAKE) -C $@ 11 | mv $@/u64asm ./u64asm 12 | -------------------------------------------------------------------------------- /u64asm-src/Makefile: -------------------------------------------------------------------------------- 1 | all: u64asm 2 | 3 | OBJS = about.h asm.h crc.h defines.h err.h exp.h opcode.h pre.h symbols.h 4 | 5 | u64asm: $(OBJS) 6 | $(CXX) -o u64asm n64.cpp 7 | 8 | clean: 9 | rm -f u64asm 10 | -------------------------------------------------------------------------------- /u64asm-src/Makefile.win32: -------------------------------------------------------------------------------- 1 | all: u64asm 2 | 3 | OBJS = about.h asm.h crc.h defines.h err.h exp.h opcode.h pre.h symbols.h 4 | 5 | u64asm: $(OBJS) 6 | $(CXX) -static -o u64asm n64.cpp 7 | 8 | clean: 9 | rm -f u64asm.exe -------------------------------------------------------------------------------- /u64asm-src/about.h: -------------------------------------------------------------------------------- 1 | cout << setw(13) << ' ' << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"; 2 | cout << setw(13) << ' ' << "X X X X X\n"; 3 | cout << setw(13) << ' ' << "X X X X X\n"; 4 | cout << setw(13) << ' ' << "X X X XXXXXXXXXX\n"; 5 | cout << setw(13) << ' ' << "X X XXXXXXXXX X\n"; 6 | cout << setw(13) << ' ' << "X X XXXXXXXXXX X\n"; 7 | cout << setw(13) << ' ' << "X X X X X\n"; 8 | cout << setw(13) << ' ' << "X X X X X\n"; 9 | cout << setw(13) << ' ' << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"; 10 | cout << setw(13) << ' ' << " X Halley's Comet Software X\n"; 11 | cout << setw(13) << ' ' << " XXXXXXXXXXXXXXXXXXXXXXXXXXX\n\n"; 12 | 13 | 14 | cout << "U64ASM was written by themind in order to complete Neon64.\n"; 15 | cout << "anarko wrote the opcode list which this program uses.\n"; 16 | cout << "Andreas Sterbenz wrote the checksum calculation routine.\n"; 17 | cout << "Bung Enterprises Ltd. wrote most of the V64jr send routine.\n"; 18 | cout << "Disch helped to debug the expression evaluator.\n"; 19 | cout << "Mike Ryan ported the code to Linux.\n\n"; 20 | cout << "Version " << VERSION << " compiled " << CDATE << "\n\n"; 21 | //cout << setw(3) << ' ' << "This software may be distributed freely, without charge, in any form which\n"; 22 | //cout << setw(12) << ' ' <<"preserves credit to the author, Halley's Comet Software.\n\n"; 23 | cout << setw(16) << ' ' << "Copyright (c) 2002-2003 Halley's Comet Software\n"; 24 | cout << setw(30) << ' ' << "here.is/halleyscomet\n"; 25 | cout << "\nThanks for keeping the N64 scene alive!\n"; 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # n64hijack 2 | 3 | n64hijack is a command-line executable that allows you to easily insert some assembly to be run right before a N64 game begins. 4 | 5 | ## Running 6 | 7 | Usage is ``n64hijack infile outfile asmfile [--noWatchKill]`` where ``asmfile`` is the assembly to run at the start of the game. Adding the ``--noWatchKill`` argument disables the functionality where codes that write to WatchLo and WatchHi are automatically NOP'ed. In commercial games, writes to WatchLo and WatchHi are undoubtedly attempts to kill GameShark-type cheating devices. Leaving WatchKill enabled removes the need for F1-type codes, but may cause issues if a homebrew game relies on them. 8 | 9 | ## patcher.asm 10 | 11 | **patcher.asm** is an extra file included with the distribution of n64hijack. It demonstrates how to run code every time the general exception handler is called in an N64 game. It implements a GameShark-like cheat engine using a technique based on the work of parasyte in his [alt64](https://github.com/parasyte/alt64) project. It should be easily modifiable to allow you to insert your own code to run at the GEH and cut out the GameShark. Alternatively, you can keep the cheat engine and bake GameShark cheats directly into a ROM - there's a section at the end of **patcher.asm** to add in these cheats. 12 | 13 | *__IMPORTANT__ - some of the methods used in patcher.asm assume that the ROM being patched is a commercially-released game and not a piece of homebrew software. Additionally, hijacking the GEH as implemented in patcher.asm this __will not work on any emulator except for cen64__. Hijacking relies on WATCH exceptions, which no emulators outside of cen64 support (as WATCH exceptions are usually only used for debugging).* 14 | 15 | ## u64asm 16 | 17 | n64hijack relies on the [u64asm](https://github.com/mikeryan/n64dev/tree/master/util/u64asm) assembler by [hcs](http://www.hcs64.com/). It's not a perfect assembler as it still contains some bugs with pseudoinstructions (notably ``li`` and a lack of an unconditional branch, ``b``), but it's the best assembler available that doesn't require the installation of a large toolchain. -------------------------------------------------------------------------------- /u64asm-src/defines.h: -------------------------------------------------------------------------------- 1 | // Level 1 error types 2 | #define NO_ERROR 0 // there was no error 3 | #define RECOVERABLE_ERROR 1 // aka a warning 4 | #define FATAL_ERROR 2 // a real error, assembly must halt 5 | #define INCLUDE_ERROR 3 // not an error at all, just a 6 | // signal from Assemble() 7 | 8 | // Level 2 error types, more specific about the error. Errhandler decides 9 | // if each of these is a fatal error and prints the error message. 10 | #define ERR_UNKNOWN_INSTRUCTION 1 11 | #define ERR_TOO_MANY_OPERANDS 2 12 | #define ERR_UNEXPECTED_CHARS 3 13 | #define ERR_LINE_TOO_LONG 4 14 | #define ERR_MISSING_OPERAND 5 15 | #define ERR_COMPLEX_EXPRESSION 6 16 | #define ERR_NOT_DEFINED 7 17 | #define ERR_NOT_ALLOWED_IN_DELAY_SLOT 8 18 | #define ERR_BAD_OPERAND 9 19 | #define ERR_ALREADY_DEFINED 10 20 | #define ERR_UNTERMINATED_STRING 11 21 | #define ERR_NOT_A_DIGIT 12 22 | //#define ERR_TOO_MANY_SYMBOLS 13 23 | #define ERR_EQU_NEEDS_LABEL 14 24 | //#define ERR_INCLUDE 15 // not an actual error! 25 | #define ERR_VALUE_TOO_BIG 16 26 | #define ERR_NO_REG_MATH 17 27 | #define ERR_INDIRECTION 18 28 | #define ERR_UNCERTAIN 19 29 | #define ERR_IRRESOLVABLE_UNCERTAINTY 20 30 | #define ERR_FILE_ERROR 21 31 | #define ERR_ILLEGAL_DIRECTIVE 22 32 | #define ERR_HANDLED 23 // already reported 33 | #define ERR_OUT_OF_MEMORY 24 34 | #define ERR_NESTED_INCLUDE 25 35 | #define ERR_NUM_PARAMETERS 26 36 | #define ERR_TOO_BIG_IMM 27 37 | #define ERR_BRANCH_RANGE 28 38 | #define ERR_OBJEND 29 39 | #define ERR_NEST_OBJ 30 40 | #define ERR_ORG_IN_OBJ 31 41 | #define ERR_ASSERT_FAIL 32 42 | #define ERR_MACRO_NAME 33 43 | #define ERR_ZERO_LENGTH 34 44 | #define ERR_WATCH_FOUND 35 45 | #define ERR_ZERO_SIZE 36 46 | #define ERR_DBZ 37 47 | #define ERR_UNKNOWN_ERROR 0xFF 48 | 49 | #define DTYPE_INTEGER 0 50 | #define DTYPE_STRING 1 51 | #define DTYPE_REGISTER 2 52 | #define DTYPE_IREGISTER 3 // index register 53 | #define DTYPE_CREGISTER 4 54 | -------------------------------------------------------------------------------- /u64asm-src/crc.h: -------------------------------------------------------------------------------- 1 | // CRC calculation routine. 2 | 3 | // All important parts by 4 | // Andreas Sterbenz (stan@sbox.tu-graz.ac.at) 5 | // Who RE'd Nagra's CRC calculator. 6 | // This particular incarnation (as a self-contained function to use on an 7 | // already open file) by themind. 8 | 9 | #define max2(a, b) ( (a)>(b) ? (a) : (b) ) 10 | #define min2(a, b) ( (a)<(b) ? (a) : (b) ) 11 | 12 | #define BUFSIZE 32768 13 | 14 | #define CHECKSUM_START 0x1000 15 | #define CHECKSUM_LENGTH 0x100000L 16 | #define CHECKSUM_HEADERPOS 0x10 17 | #define CHECKSUM_END (CHECKSUM_START + CHECKSUM_LENGTH) 18 | 19 | #define CHECKSUM_STARTVALUE 0xf8ca4ddc 20 | 21 | #define ROL(i, b) (((i)<<(b)) | ((i)>>(32-(b)))) 22 | 23 | #define BYTES2LONG(b) ( (((b)[0] & 0xffL) << 24) | \ 24 | (((b)[1] & 0xffL) << 16) | \ 25 | (((b)[2] & 0xffL) << 8) | \ 26 | (((b)[3] & 0xffL)) ) 27 | 28 | #define HEADER_MAGIC 0x80371240 29 | 30 | /* 31 | Function crc: calculates the CRC for a file, given its handle and whether 32 | it's swapped or not. 33 | 34 | file1 = handle of the file to be checksummed 35 | swapped = an int, 0 if original format, 1 if byte-swapped 36 | 37 | returns an error level 38 | */ 39 | 40 | int crc(int file1) { 41 | long flen1=lseek(file1,0,SEEK_END); 42 | unsigned char * buffer1 = new unsigned char [BUFSIZE]; 43 | if (!buffer1) {cout << "Out of memory.\n"; return 1;} 44 | 45 | unsigned long sum1,sum2; 46 | unsigned long i; 47 | unsigned long c1, k1, k2; 48 | unsigned long t1, t2, t3, t4; 49 | unsigned long t5, t6; 50 | unsigned int n; 51 | long clen = CHECKSUM_LENGTH; 52 | long rlen = flen1 - CHECKSUM_START; 53 | 54 | lseek(file1, CHECKSUM_START, SEEK_SET); 55 | 56 | /* Below is the actual checksum calculation algorithm, which was 57 | reverse engineered out of Nagra's program. 58 | 59 | As you can see, the algorithm is total crap. Obviously it was 60 | designed to be difficult to guess and reverse engineer, and not 61 | to give a good checksum. A simple XOR and ROL 13 would give a 62 | just as good checksum. The ifs and the data dependent ROL are really 63 | extreme nonsense. 64 | */ 65 | 66 | t1 = CHECKSUM_STARTVALUE; 67 | t2 = CHECKSUM_STARTVALUE; 68 | t3 = CHECKSUM_STARTVALUE; 69 | t4 = CHECKSUM_STARTVALUE; 70 | t5 = CHECKSUM_STARTVALUE; 71 | t6 = CHECKSUM_STARTVALUE; 72 | 73 | for( ;; ) { 74 | if( rlen > 0 ) { 75 | n = read(file1,buffer1,min2(BUFSIZE,clen)); 76 | if( (n & 0x03) != 0 ) { 77 | n += read(file1,buffer1+n,4-(n&3)); 78 | } 79 | } else { 80 | n = min2(BUFSIZE, clen); 81 | } 82 | if( (n == 0) || ((n&3) != 0) ) { 83 | if( (clen != 0) || (n != 0) ) { 84 | cout << "WARNING: Short read, checksum may be incorrect.\n"; 85 | } 86 | break; 87 | } 88 | for( i=0; i 0 ) { 105 | rlen -= n; 106 | if( rlen <= 0 ) memset(buffer1, 0, BUFSIZE); 107 | } 108 | clen -= n; 109 | } 110 | 111 | delete [] buffer1; 112 | 113 | sum1 = t6 ^ t4 ^ t3; 114 | sum2 = t5 ^ t2 ^ t1; 115 | 116 | // Write it 117 | lseek(file1,CHECKSUM_HEADERPOS,SEEK_SET); 118 | write(file1,(unsigned char *)(&sum1)+3,1); 119 | write(file1,(unsigned char *)(&sum1)+2,1); 120 | write(file1,(unsigned char *)(&sum1)+1,1); 121 | write(file1,(unsigned char *)(&sum1)+0,1); 122 | write(file1,(unsigned char *)(&sum2)+3,1); 123 | write(file1,(unsigned char *)(&sum2)+2,1); 124 | write(file1,(unsigned char *)(&sum2)+1,1); 125 | write(file1,(unsigned char *)(&sum2)+0,1); 126 | 127 | return 0; 128 | } 129 | -------------------------------------------------------------------------------- /u64asm-src/exp.h: -------------------------------------------------------------------------------- 1 | // Expression evaluation 2 | 3 | unsigned long Evaluate(const char * s, int &c) { 4 | unsigned long total = 0; 5 | unsigned long op2,op3; 6 | signed long c2; 7 | 8 | total = EvOp(s,c); 9 | 10 | while (c < strlen(s)) { 11 | 12 | switch (s[c]) { 13 | case ' ': 14 | c++; 15 | continue; // from beginning of while loop 16 | case ',': 17 | return total; 18 | case '+': 19 | c++; 20 | op2 = EvOp(s,c); 21 | total += op2; 22 | break; 23 | case '-': 24 | c++; 25 | op2 = EvOp(s,c); 26 | total -= op2; 27 | break; 28 | case '*': 29 | c++; 30 | op2 = EvOp(s,c); 31 | total *= op2; 32 | break; 33 | case '/': 34 | c++; 35 | op2 = EvOp(s,c); 36 | if (op2 == 0) {ExpError=true; ExpErrorV=ERR_DBZ; return 0;} 37 | total /= op2; 38 | break; 39 | case '%': // overloaded 40 | c++; 41 | op2 = EvOp(s,c); 42 | total %= op2; 43 | break; 44 | case '|': 45 | c++; 46 | op2 = EvOp(s,c); 47 | total |= op2; 48 | break; 49 | case '&': 50 | c++; 51 | op2 = EvOp(s,c); 52 | total &= op2; 53 | break; 54 | case '!': 55 | c++; 56 | op2 = EvOp(s,c); 57 | total ^= op2; 58 | break; 59 | case '^': 60 | c++; 61 | op2 = EvOp(s,c); 62 | op3 = 1; 63 | if (((signed long)op2) < 0) { 64 | if (total == 0) {ExpError=true; return 0;} 65 | for (c2=0; c2 > ((signed long)op2); c2--) { 66 | op3/=total; 67 | } 68 | } else { 69 | for (c2=0; c2 < op2; c2++) { 70 | op3*=total; 71 | } 72 | } 73 | total=op3; 74 | break; 75 | case '@': 76 | c++; 77 | op2 = EvOp(s,c); 78 | if (((signed long)op2)<=0) {ExpError=true; return 0;} 79 | if (((signed long)total)<=0) {ExpError=true; return 0;} 80 | total=Root(total,op2); 81 | break; 82 | case '<': 83 | c++; 84 | op2 = EvOp(s,c); 85 | total = total << op2; 86 | break; 87 | case '>': 88 | c++; 89 | op2 = EvOp(s,c); 90 | total = total >> op2; 91 | break; 92 | case ')': 93 | return total; 94 | case '(': 95 | return total; 96 | default: 97 | ExpError=true; 98 | return 0; 99 | } 100 | if (symbolcertain && IsRegister) {ExpErrorV=ERR_NO_REG_MATH; ExpError=true; return 0;} 101 | } 102 | return total; 103 | } 104 | 105 | // Set, setsize, character in question 106 | int InSet(char * set, int setsize, char ciq) { 107 | int c; 108 | for (c=0; c < setsize; c++) {if (LowerCase(ciq)==set[c]) return c;} 109 | return c; 110 | } 111 | 112 | // Evaluates a single operand 113 | // Unary operators (-, ~) are covered here 114 | unsigned long EvOp(const char * s, int &c) { 115 | // Valid characters for an operand 116 | unsigned long total; 117 | int d; 118 | int c2; 119 | 120 | bool negflag=false, notflag=false, stillsearching=true; 121 | while (stillsearching && c < strlen(s)) { 122 | switch (s[c]) { 123 | case '-': 124 | negflag = !negflag; 125 | c++; 126 | break; 127 | case '~': 128 | notflag = !notflag; 129 | c++; 130 | break; 131 | case '0': 132 | case '1': 133 | case '2': 134 | case '3': 135 | case '4': 136 | case '5': 137 | case '6': 138 | case '7': 139 | case '8': 140 | case '9': 141 | case '%': 142 | case '$': 143 | case '#': 144 | total = EvInt(s,c); 145 | stillsearching=false; 146 | break; 147 | case '(': 148 | c++; 149 | total = Evaluate(s,c); 150 | stillsearching=false; 151 | if (s[c] != ')') ExpError=true; 152 | if (IsRegister) IsIndex=true; 153 | c++; 154 | break; 155 | case ' ': // ignore spaces 156 | c++; 157 | break; 158 | default: 159 | if (s[c]==')') {ExpError=true; return 0;} 160 | if (GetSymbol(s,c,c2)) {ExpErrorV=ERR_NOT_DEFINED; ExpError=true; return 0;} 161 | c++; 162 | total = SymbolList[c2].value; 163 | if (SymbolList[c2].type == DTYPE_REGISTER) IsRegister=true; 164 | if (SymbolList[c2].type == DTYPE_CREGISTER) {IsRegister=true; IsCOPRegister=true;} 165 | stillsearching=false; 166 | break; 167 | } 168 | } 169 | if (stillsearching) ExpError=true; 170 | return notflag?~(negflag?-total:total):(negflag?-total:total); 171 | } 172 | 173 | // Evaluates an integer in decimal, hex ($), bin (%), or octal (#) 174 | unsigned long EvInt(const char * s, int &c) { 175 | int base=10,d; 176 | char charset[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; 177 | //bool neg=false; 178 | unsigned long total = 0, ltotal=0; 179 | 180 | if (s[c] == '$') {base = 16; c++;} 181 | else if (s[c] == '%') {base = 2; c++;} 182 | else if (s[c] == '#') {base = 8; c++;} 183 | else if (s[c] == '0' && s[c+1] == 'x') {base = 16; c+=2;} // 0x10 and such 184 | 185 | if (InSet(charset, base, s[c]) == base) {ExpErrorV=ERR_NOT_A_DIGIT; ExpError=true; return 0;} 186 | for (; c < strlen(s); c++) { 187 | if ((d=InSet(charset, base, s[c])) == base) return total; 188 | ltotal=total; 189 | total=total*base+d; 190 | if (ltotal > total) {ExpErrorV=ERR_VALUE_TOO_BIG; ExpError=true;} 191 | } 192 | return total; 193 | } 194 | 195 | unsigned long DivRep(unsigned long divisor, unsigned long dividend, unsigned long times) { 196 | for (unsigned long c=0; c < times; c++) {divisor /= dividend;} 197 | return divisor; 198 | } 199 | 200 | unsigned long Root(unsigned long n, unsigned long groot) { 201 | unsigned long root,err,olderr; 202 | 203 | root=n/2; 204 | err = (root-DivRep(n,root,groot-1)); 205 | do { 206 | root -= err/groot; 207 | olderr=err; 208 | err = (root-DivRep(n,root,groot-1)); 209 | } while (olderr != err && err != 0); 210 | return root; 211 | } 212 | -------------------------------------------------------------------------------- /u64asm-src/symbols.h: -------------------------------------------------------------------------------- 1 | // contains some stuff related to symbols and registers 2 | 3 | #define SYMBOL_SIZE 32 4 | #define BUILTINSYMBOLS 107 5 | 6 | struct Symbol { 7 | char name[SYMBOL_SIZE]; 8 | unsigned long value; 9 | int type; 10 | bool certain; 11 | bool bexport; 12 | }; 13 | 14 | Symbol *SymbolList; 15 | unsigned int symcount; 16 | unsigned int maxsymbols; 17 | 18 | // registers are implemented as symbols 19 | 20 | void InitRegs(void) { 21 | const char * regnames[32] = {"r0 ","at ","v0 ","v1 ","a0 ","a1 ","a2 ","a3 ", 22 | "t0 ","t1 ","t2 ","t3 ","t4 ","t5 ","t6 ","t7 ", 23 | "s0 ","s1 ","s2 ","s3 ","s4 ","s5 ","s6 ","s7 ", 24 | "t8 ","t9 ","k0 ","k1 ","gp ","sp ","s8 ","ra "}; 25 | const char * regnames2[32]={"zero ","r1 ", "r2 ", "r3 ", "r4 ", "r5 ", "r6 ", "r7 ", 26 | "r8 ", "r9 ", "r10 ","r11 ","r12 ","r13 ","r14 ","r15 ", 27 | "r16 ","r17 ","r18 ","r19 ","r20 ","r21 ","r22 ","r23 ", 28 | "r24 ","r25 ","r26 ","r27 ","r28 ","r29 ","r30 ","r31 "}; 29 | const char * regnames0[10]={"r00 ","r01 ","r02 ","r03 ","r04 ","r05 ","r06 ","r07 ","r08 ","r09 "}; 30 | const char * cop0[32]={"index ","random ","entrylo0 ","entrylo1 ","context ", 31 | "pagemask ","wired ","c07 ","badvaddr ","count ", 32 | "entryhi ","compare ","status ","cause ","epc ","previd ", 33 | "config ","lladdr ","watchlo ","watchhi ","xcontext ", 34 | "c21 ","c22 ","c23 ","c24 ","c25 ","perr ","cacheerr ", 35 | "taglo ","taghi ","errorepc ","c31 "}; 36 | 37 | unsigned int c; 38 | for (c=0; c < 32; c++) { 39 | strcpy(SymbolList[c].name,regnames[c]); 40 | SymbolList[c].value = c; 41 | SymbolList[c].type = DTYPE_REGISTER; 42 | SymbolList[c].certain = true; 43 | SymbolList[c].bexport = false; 44 | } 45 | 46 | for (c=32; c < 64; c++) { 47 | strcpy(SymbolList[c].name,regnames2[c-32]); 48 | SymbolList[c].value = c-32; 49 | SymbolList[c].type = DTYPE_REGISTER; 50 | SymbolList[c].certain = true; 51 | SymbolList[c].bexport = false; 52 | } 53 | 54 | for (c=64; c < 74; c++) { 55 | strcpy(SymbolList[c].name,regnames0[c-64]); 56 | SymbolList[c].value = c-64; 57 | SymbolList[c].type = DTYPE_REGISTER; 58 | SymbolList[c].certain = true; 59 | SymbolList[c].bexport = false; 60 | } 61 | 62 | for (c=74; c < 106; c++) { 63 | strcpy(SymbolList[c].name,cop0[c-74]); 64 | SymbolList[c].value = c-74; 65 | SymbolList[c].type = DTYPE_CREGISTER; 66 | SymbolList[c].certain = true; 67 | SymbolList[c].bexport = false; 68 | } 69 | 70 | strcpy(SymbolList[106].name,"pc "); 71 | SymbolList[106].value = 0; 72 | SymbolList[106].type = DTYPE_INTEGER; 73 | SymbolList[106].certain = true; 74 | SymbolList[c].bexport = false; 75 | 76 | symcount = BUILTINSYMBOLS; 77 | return; 78 | } 79 | 80 | // Counts the number of symbols in the file, duplicate symbols will be 81 | // counted repeatedly and thus not caught here, but they will be later. 82 | // it counts as a symbol if the first character is not whitespace or if 83 | // it is not an include 84 | unsigned int CountSymbols(const char * asmfile) { 85 | ifstream input(asmfile); 86 | char instr[256]; 87 | unsigned int symbcount=0; 88 | 89 | if (!input) { 90 | cout << "Error opening input file " << asmfile << ".\n"; 91 | exit(1); // because its supposed to return a numeric value 92 | } 93 | 94 | while (!input.eof()) { 95 | 96 | input.getline(instr,256); 97 | 98 | // Process comment ignorance 99 | 100 | if (instr[0] != ';' && instr[0] != '#') { 101 | // if the first character is not blank 102 | if (IsChar(instr[0])) symbcount++; 103 | } 104 | } 105 | input.close(); 106 | return symbcount; 107 | } 108 | 109 | 110 | int LoadSymbols(const char * rasmfile) { 111 | //InitRegs(); // Load the basic symbols (register names) 112 | // already done in main() 113 | 114 | ifstream input(rasmfile); 115 | char asmfile[256]; 116 | char instr[256]; 117 | int c,lc,err,ic; 118 | unsigned int line=0,linecountfactor=1; 119 | 120 | strcpy(asmfile,rasmfile); 121 | 122 | if (!input) { 123 | cout << "Error opening input file " << asmfile << ".\n"; 124 | exit(1); // because its supposed to return a numeric value 125 | } 126 | 127 | while (!input.eof()) { 128 | 129 | line+=linecountfactor; 130 | input.getline(instr,256); 131 | 132 | if (instr[0]=='#') { 133 | switch (instr[1]) { 134 | case 'e': 135 | linecountfactor=0; 136 | break; 137 | case 's': 138 | linecountfactor=1; 139 | break; 140 | case 'f': 141 | for (c=3; c < strlen(instr); c++) { 142 | asmfile[c-3]=instr[c]; 143 | } 144 | asmfile[c-3]=0; 145 | #ifdef DEBUG 146 | cout << "File name changed to: " << asmfile << '\n'; 147 | #endif 148 | break; 149 | case 'l': 150 | line=0; 151 | for (c=3; c < strlen(instr); c++) { 152 | line*=10; 153 | line += instr[c]-'0'; 154 | } 155 | #ifdef DEBUG 156 | cout << "Line count changed to: " << line << '\n'; 157 | #endif 158 | break; 159 | case 't': 160 | break; 161 | default: 162 | return Errhandler(ERR_ILLEGAL_DIRECTIVE,line,asmfile,instr); 163 | } 164 | } else if (instr[0] != ';' && instr[0] != '#') { 165 | 166 | if (IsChar(instr[0])) { 167 | for (lc=0; lc < symcount; lc++) { 168 | if (CheckEq(instr,0,SymbolList[lc].name,ic)) { 169 | return Errhandler(ERR_ALREADY_DEFINED,line,asmfile,instr); 170 | } 171 | } 172 | for (c=0; IsChar(instr[c]); c++) { 173 | #ifdef DEBUG 174 | cout << instr[c]; 175 | #endif 176 | SymbolList[symcount].name[c] = LowerCase(instr[c]); 177 | } 178 | #ifdef DEBUG 179 | cout << "* "; 180 | #endif 181 | SymbolList[symcount].name[c]=' '; 182 | SymbolList[symcount].name[c+1]=0; 183 | SymbolList[symcount].certain=false; 184 | symcount++; 185 | #ifdef DEBUG 186 | cout << SymbolList[symcount-1].name << "*\n"; 187 | #endif 188 | } 189 | } 190 | } 191 | input.close(); 192 | return NO_ERROR; 193 | } 194 | 195 | -------------------------------------------------------------------------------- /src/crc.h: -------------------------------------------------------------------------------- 1 | // Modified for n64hijack 2 | 3 | /* snesrc - SNES Recompiler 4 | * 5 | * Mar 23, 2010: addition by spinout to actually fix CRC if it is incorrect 6 | * 7 | * Copyright notice for this file: 8 | * Copyright (C) 2005 Parasyte 9 | * 10 | * Based on uCON64's N64 checksum algorithm by Andreas Sterbenz 11 | * 12 | * This program is free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this program; if not, write to the Free Software 24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25 | */ 26 | 27 | 28 | #include 29 | #include 30 | 31 | namespace n64crc { 32 | 33 | //////////////////////////////////////////////////////////////////////////////////////// 34 | //////////////////////////////////////////////////////////////////////////////////////// 35 | // CONSTANTS 36 | 37 | const uint32_t N64_BOOTCODE_START = 0x40; 38 | const uint32_t N64_BOOTCODE_SIZE = (0x1000 - N64_BOOTCODE_START); 39 | 40 | const uint32_t N64_CRC1 = 0x10; 41 | const uint32_t N64_CRC2 = 0x14; 42 | 43 | const uint32_t GAME_START = 0x00001000; 44 | const uint32_t GAME_CHECKSUM_LENGTH = 0x00100000; 45 | 46 | const uint32_t CHECKSUM_CIC6102 = 0xF8CA4DDC; 47 | const uint32_t CHECKSUM_CIC6103 = 0xA3886759; 48 | const uint32_t CHECKSUM_CIC6105 = 0xDF26F436; 49 | const uint32_t CHECKSUM_CIC6106 = 0x1FEA617A; 50 | 51 | inline uint32_t _ROL (uint32_t i, uint32_t b) { 52 | return (((i) << (b)) | ((i) >> (32 - (b)))); 53 | } 54 | 55 | inline uint32_t _BYTES2LONG (uint8_t *b) { 56 | return ( (b)[0] << 24 | 57 | (b)[1] << 16 | 58 | (b)[2] << 8 | 59 | (b)[3] ); 60 | } 61 | 62 | inline void _WRITE32 (uint8_t *Buffer, uint32_t Offset, uint32_t Value) { 63 | Buffer[Offset] = (Value & 0xFF000000) >> 24; 64 | Buffer[Offset + 1] = (Value & 0x00FF0000) >> 16; 65 | Buffer[Offset + 2] = (Value & 0x0000FF00) >> 8; 66 | Buffer[Offset + 3] = (Value & 0x000000FF); 67 | } 68 | 69 | uint32_t crc_table[256]; 70 | 71 | 72 | //////////////////////////////////////////////////////////////////////////////////////// 73 | //////////////////////////////////////////////////////////////////////////////////////// 74 | // INTERNAL 75 | 76 | // Using some magic, generate a table that is used in CRC recalculation 77 | void _generateTable() { 78 | uint32_t crc, poly, i, j; 79 | 80 | poly = 0xEDB88320; 81 | for (i = 0; i < 256; i++) { 82 | crc = i; 83 | 84 | for (j = 8; j > 0; j--) { 85 | if (crc & 1) crc = (crc >> 1) ^ poly; 86 | else crc >>= 1; 87 | } 88 | 89 | crc_table[i] = crc; 90 | } 91 | } 92 | 93 | 94 | //////////////////////////////////////////////////////////////////////////////////////// 95 | //////////////////////////////////////////////////////////////////////////////////////// 96 | // EXPOSED FUNCTIONS 97 | 98 | // Calculates the CRC of the ROM's boot code (should be from 0x40 to 0xFFF) 99 | uint32_t calculateHeaderCrc(uint8_t *bootCode) { 100 | uint32_t crc = ~0; 101 | uint32_t i; 102 | 103 | for (i = 0; i < N64_BOOTCODE_SIZE; i++) { 104 | crc = (crc >> 8) ^ crc_table[(crc ^ bootCode[i]) & 0xFF]; 105 | } 106 | 107 | return ~crc; 108 | } 109 | 110 | 111 | // Guesses which CIC should be installed based on the game's boot code CRC. 112 | // Failing a match, it defaults to 6105. 113 | uint16_t cicFromCrc(uint8_t *data) { 114 | switch (calculateHeaderCrc(&data[N64_BOOTCODE_START])) { 115 | case 0x6170A4A1: return 6101; 116 | case 0x90BB6CB5: return 6102; 117 | case 0x0B050EE0: return 6103; 118 | case 0x98BC2C86: return 6105; 119 | case 0xACC8580A: return 6106; 120 | } 121 | 122 | return 6105; 123 | } 124 | 125 | // Calculates the CRC of the game's actual code. 126 | // The CRC only takes into account the first 1MB of the game, so only values 127 | // from 0x1000 to 0x00101000 in the ROM are checked. 128 | int calculateGameCrc(uint32_t *crc, uint8_t *data, uint16_t cic) { 129 | uint32_t i, seed; 130 | 131 | uint32_t t1, t2, t3; 132 | uint32_t t4, t5, t6; 133 | uint32_t r, d; 134 | 135 | switch (cic) { 136 | case 6101: 137 | case 6102: 138 | seed = CHECKSUM_CIC6102; 139 | break; 140 | case 6103: 141 | seed = CHECKSUM_CIC6103; 142 | break; 143 | case 6105: 144 | seed = CHECKSUM_CIC6105; 145 | break; 146 | case 6106: 147 | seed = CHECKSUM_CIC6106; 148 | break; 149 | default: 150 | return 1; 151 | } 152 | 153 | t1 = t2 = t3 = t4 = t5 = t6 = seed; 154 | 155 | i = GAME_START; 156 | while (i < (GAME_START + GAME_CHECKSUM_LENGTH)) { 157 | d = _BYTES2LONG(&data[i]); 158 | if ((t6 + d) < t6) t4++; 159 | t6 += d; 160 | t3 ^= d; 161 | r = _ROL(d, (d & 0x1F)); 162 | t5 += r; 163 | if (t2 > d) t2 ^= r; 164 | else t2 ^= t6 ^ d; 165 | 166 | if (cic == 6105) t1 += _BYTES2LONG(&data[N64_BOOTCODE_START + 0x0710 + (i & 0xFF)]) ^ d; 167 | else t1 += t5 ^ d; 168 | 169 | i += 4; 170 | } 171 | if (cic == 6103) { 172 | crc[0] = (t6 ^ t4) + t3; 173 | crc[1] = (t5 ^ t2) + t1; 174 | } 175 | else if (cic == 6106) { 176 | crc[0] = (t6 * t4) + t3; 177 | crc[1] = (t5 * t2) + t1; 178 | } 179 | else { 180 | crc[0] = t6 ^ t4 ^ t3; 181 | crc[1] = t5 ^ t2 ^ t1; 182 | } 183 | 184 | return 0; 185 | } 186 | 187 | uint8_t recalculate(uint8_t *buffer, uint32_t *crc, uint16_t *cic, bool *didRecalculate) { 188 | _generateTable(); 189 | *cic = cicFromCrc(buffer); 190 | 191 | // Try to calculate game CRC 192 | if (calculateGameCrc(crc, buffer, *cic)) { 193 | return 3; 194 | } else { 195 | didRecalculate[0] = didRecalculate[1] = false; 196 | 197 | if (crc[0] != _BYTES2LONG(&buffer[N64_CRC1])) { 198 | didRecalculate[0] = true; 199 | _WRITE32(buffer, N64_CRC1, crc[0]); 200 | } 201 | 202 | if (crc[1] != _BYTES2LONG(&buffer[N64_CRC2])) { 203 | didRecalculate[1] = true; 204 | _WRITE32(buffer, N64_CRC2, crc[1]); 205 | } 206 | } 207 | 208 | return 0; 209 | } 210 | 211 | 212 | } // end namespace n64crc 213 | -------------------------------------------------------------------------------- /u64asm-src/err.h: -------------------------------------------------------------------------------- 1 | // Error handling subroutine 2 | // Reports to the user when there is an error, line number and file, 3 | // and the seriousness. If the error is recoverable (a 'warning'), it keeps 4 | // track of how many recoverable errors there have been, this is reported 5 | // at the end of assembly by main(). 6 | 7 | int Errhandler(int error, unsigned long line, char * filename, char * instr) { 8 | int errlev; 9 | switch (error) { 10 | case NO_ERROR: 11 | return NO_ERROR; 12 | case ERR_UNKNOWN_INSTRUCTION: 13 | errlev = FATAL_ERROR; // can't just put a NOP there 14 | break; 15 | case ERR_TOO_MANY_OPERANDS: 16 | errlev = FATAL_ERROR; // nothing is stored 17 | break; 18 | case ERR_UNEXPECTED_CHARS: 19 | errlev = FATAL_ERROR; // crashes FindOps 20 | break; 21 | case ERR_LINE_TOO_LONG: 22 | errlev = FATAL_ERROR; // unsure how input handles this 23 | break; // in different situations 24 | case ERR_MISSING_OPERAND: 25 | errlev = FATAL_ERROR; // can't assemble an opcode without 26 | break; // an operand 27 | case ERR_COMPLEX_EXPRESSION: 28 | errlev = FATAL_ERROR; // all sorts of recursiveness messed 29 | break; // up 30 | case ERR_NOT_ALLOWED_IN_DELAY_SLOT: 31 | errlev = FATAL_ERROR; // because it won't run right 32 | break; 33 | case ERR_NOT_DEFINED: 34 | errlev = FATAL_ERROR; // can't just make something up 35 | break; 36 | case ERR_BAD_OPERAND: 37 | errlev = FATAL_ERROR; // don't want to squeeze something to fit 38 | break; 39 | case ERR_ALREADY_DEFINED: 40 | errlev = FATAL_ERROR; // stops Assemble() before opcode 41 | break; 42 | case ERR_UNTERMINATED_STRING: 43 | errlev = FATAL_ERROR; // It can't work 44 | break; 45 | case ERR_NOT_A_DIGIT: 46 | errlev = FATAL_ERROR; // number is permanently messed up 47 | break; 48 | case ERR_EQU_NEEDS_LABEL: 49 | errlev = FATAL_ERROR; // messes up otherwise 50 | break; 51 | case ERR_VALUE_TOO_BIG: 52 | errlev = RECOVERABLE_ERROR; 53 | break; 54 | case ERR_NO_REG_MATH: 55 | errlev = FATAL_ERROR; 56 | break; 57 | case ERR_INDIRECTION: 58 | errlev = FATAL_ERROR; 59 | break; 60 | case ERR_UNCERTAIN: 61 | errlev = NO_ERROR; 62 | break; 63 | case ERR_FILE_ERROR: 64 | errlev = FATAL_ERROR; 65 | break; 66 | case ERR_ILLEGAL_DIRECTIVE: 67 | errlev = FATAL_ERROR; 68 | break; 69 | case ERR_HANDLED: 70 | errlev = FATAL_ERROR; 71 | break; 72 | case ERR_OUT_OF_MEMORY: 73 | errlev = FATAL_ERROR; 74 | break; 75 | case ERR_NESTED_INCLUDE: 76 | errlev = FATAL_ERROR; 77 | break; 78 | case ERR_NUM_PARAMETERS: 79 | errlev = FATAL_ERROR; 80 | break; 81 | case ERR_TOO_BIG_IMM: 82 | errlev = RECOVERABLE_ERROR; 83 | break; 84 | case ERR_BRANCH_RANGE: 85 | errlev = RECOVERABLE_ERROR; 86 | break; 87 | case ERR_OBJEND: 88 | errlev = RECOVERABLE_ERROR; 89 | break; 90 | case ERR_NEST_OBJ: 91 | errlev = RECOVERABLE_ERROR; 92 | break; 93 | case ERR_ORG_IN_OBJ: 94 | errlev = RECOVERABLE_ERROR; 95 | break; 96 | case ERR_ASSERT_FAIL: 97 | errlev = FATAL_ERROR; 98 | break; 99 | case ERR_MACRO_NAME: 100 | errlev = RECOVERABLE_ERROR; 101 | break; 102 | case ERR_ZERO_LENGTH: 103 | errlev = FATAL_ERROR; 104 | break; 105 | case ERR_WATCH_FOUND: 106 | errlev = RECOVERABLE_ERROR; 107 | break; 108 | case ERR_ZERO_SIZE: 109 | errlev = RECOVERABLE_ERROR; 110 | break; 111 | case ERR_DBZ: 112 | errlev = FATAL_ERROR; 113 | break; 114 | case ERR_UNKNOWN_ERROR: 115 | default: 116 | errlev = FATAL_ERROR; // the assembler itself is messed up 117 | } 118 | if (line > 0) { 119 | if (errlev == FATAL_ERROR) cout << "\nError " << filename << ' ' << line << ": "; 120 | if (errlev == RECOVERABLE_ERROR) {cout << "\nWarning " << filename << ' ' << line << ": "; errtot++;} 121 | } else { 122 | if (errlev == FATAL_ERROR) cout << "\nError: "; 123 | if (errlev == RECOVERABLE_ERROR) {cout << "\nWarning: "; errtot++;} 124 | } 125 | if (errlev == NO_ERROR) return NO_ERROR; 126 | 127 | switch (error) { 128 | case ERR_UNKNOWN_INSTRUCTION: 129 | cout << "Unknown instruction.\n"; 130 | break; 131 | case ERR_TOO_MANY_OPERANDS: 132 | cout << "Too many parameters.\n"; 133 | break; 134 | case ERR_UNEXPECTED_CHARS: 135 | cout << "Unexpected characters at end of line.\n"; 136 | break; 137 | case ERR_LINE_TOO_LONG: 138 | cout << "Line too long.\n"; 139 | break; 140 | case ERR_MISSING_OPERAND: 141 | cout << "Parameter expected.\n"; 142 | break; 143 | case ERR_COMPLEX_EXPRESSION: 144 | cout << "Error in complex expression.\n"; 145 | break; 146 | case ERR_NOT_ALLOWED_IN_DELAY_SLOT: 147 | cout << "Instruction not allowed in delay slot.\n"; 148 | break; 149 | case ERR_NOT_DEFINED: 150 | cout << "Symbol not defined.\n"; 151 | break; 152 | case ERR_BAD_OPERAND: 153 | cout << "Wrong type of parameter.\n"; 154 | break; 155 | case ERR_ALREADY_DEFINED: 156 | cout << "Symbol already defined.\n"; 157 | break; 158 | case ERR_UNTERMINATED_STRING: 159 | cout << "Unterminated string.\n"; 160 | break; 161 | case ERR_NOT_A_DIGIT: 162 | cout << "Non-digit character found in numeric value.\n"; 163 | break; 164 | case ERR_EQU_NEEDS_LABEL: 165 | cout << "EQU and EQUR need to be used with a label.\n"; 166 | break; 167 | case ERR_VALUE_TOO_BIG: 168 | cout << "Value too big, 32-bit maximum.\n"; 169 | break; 170 | case ERR_NO_REG_MATH: 171 | cout << "Math operations cannot be performed on registers.\n"; 172 | break; 173 | case ERR_INDIRECTION: 174 | cout << "Syntax error in indirect addressing mode (reg).\n"; 175 | break; 176 | case ERR_FILE_ERROR: 177 | cout << "File not found.\n"; 178 | break; 179 | case ERR_ILLEGAL_DIRECTIVE: 180 | cout << "Illegal directive.\n"; 181 | break; 182 | case ERR_HANDLED: 183 | break; 184 | case ERR_OUT_OF_MEMORY: 185 | cout << "Out of memory.\n"; 186 | break; 187 | case ERR_NESTED_INCLUDE: 188 | cout << "A file cannot be included from within itself.\n"; 189 | break; 190 | case ERR_NUM_PARAMETERS: 191 | cout << "Wrong number of macro parameters.\n"; 192 | break; 193 | case ERR_TOO_BIG_IMM: 194 | cout << "Immediate value is too large.\n"; 195 | break; 196 | case ERR_BRANCH_RANGE: 197 | cout << "Branch out of range.\n"; 198 | break; 199 | case ERR_OBJEND: 200 | cout << "OBJEND without OBJ.\n"; 201 | break; 202 | case ERR_NEST_OBJ: 203 | cout << "Cannot nest OBJs.\n"; 204 | break; 205 | case ERR_ORG_IN_OBJ: 206 | cout << "An ORG within an OBJ can cause unwanted results.\n"; 207 | break; 208 | case ERR_ASSERT_FAIL: 209 | cout << "Assert failed.\n"; 210 | break; 211 | case ERR_MACRO_NAME: 212 | cout << "Macro name contains another macro name.\n"; 213 | break; 214 | case ERR_ZERO_LENGTH: 215 | cout << "Macro parameter cannot have zero length.\n"; 216 | break; 217 | case ERR_WATCH_FOUND: 218 | cout << "PC == watchpoint.\n"; 219 | break; 220 | case ERR_ZERO_SIZE: 221 | cout << "Attempt to make a zero sized string.\n"; 222 | break; 223 | case ERR_DBZ: 224 | cout << "Divide by zero.\n"; 225 | break; 226 | case ERR_UNKNOWN_ERROR: 227 | default: 228 | cout << "Unknown error #" << error << '\n'; 229 | } 230 | if (instr) cout << instr << '\n'; 231 | 232 | return errlev; 233 | } 234 | 235 | -------------------------------------------------------------------------------- /u64asm-src/asm.h: -------------------------------------------------------------------------------- 1 | // asm.h 2 | // The real body of the assembler, the biggest function is FindOperands 3 | // which parses operands and returns a nice list for the opcode function 4 | // to deal with 5 | 6 | // What is the point of this list? I'm not sure. 7 | // v0.0a - Last modified 2/22/02 8 | // v0.0b - 2/22/02 9 | // v0.0c - 2/24/02 10 | // v0.0d - 2/24/02-2/25/02 11 | // v0.0e - 2/27/02 12 | // v0.0f - 2/27/02-2/28/02 13 | // v0.0g - 3/14/02 14 | // v0.0h - 3/14/02 15 | // v0.0i - 3/15/02 16 | // v0.0j - 3/15/02 17 | // v0.0k - 3/16/02-3/17/02 18 | // v0.0l - 3/17/02 19 | // v0.0m - 3/17/02 20 | // v0.0n - 3/18/02-3/19/02 21 | // v0.0o - 3/21/02 22 | // v0.0p - 3/21/02 23 | // v0.0q - 3/22/02-3/24/02 24 | // v0.0r - 3/24/02-3/25/02 25 | // v0.0s - 3/26/02 26 | // v0.0t - 4/1/02-4/2/02 27 | // v0.0u - 4/2/02 28 | // v0.0v - 4/3/02 29 | // v0.0w - 4/5/02-4/8/02 30 | // v0.0x - 4/9/02-4/14/02 31 | // v0.0y - 4/20/02-5/9/02 32 | // v0.0z - 5/10/02-5/17/02 33 | // v0.1 - 5/17/02-5/21/02 34 | // v0.1a - 5/22/02 35 | // v0.1b - 6/60/02-7/4/02 36 | // v0.1c - 7/5/02 37 | // v0.1d - 7/6/02-7/7/02 38 | // v0.1e - 7/9/02-7/10/02 39 | // v0.1f - 7/10/02-7/11/02 40 | // v0.1g - 7/16/02 41 | // v0.1h - 7/17/02-7/21/02 42 | // v0.1i - 7/22/02-7/23/02 43 | // v0.1j - 8/12/02-8/13/02 44 | // v0.1k - 8/20/02-8/21/02 45 | // v0.1l - 8/23/02 46 | // v0.1m - 8/29/02 47 | // v0.1n - 10/2/02 48 | // v0.1o - 2/25/03 49 | 50 | #include "opcode.h" 51 | 52 | bool IsChar(char c) { 53 | return ((c!=' ') && (c!=0) && (c!=':')); 54 | } 55 | char LowerCase(char c) { 56 | if (c >= 'A' && c <= 'Z') return c+('a'-'A'); 57 | else return c; 58 | } 59 | bool CheckEq(const char *array, int aoff, const char *sstring, int &ic) { 60 | int c; 61 | ic=aoff; 62 | for (c=0; c < strlen(sstring); c++) { 63 | if (!IsChar(array[ic]) && sstring[c]==' ') return true; 64 | if (LowerCase(array[ic]) != sstring[c]) return false; 65 | ic++; 66 | } 67 | return true; 68 | } 69 | bool CheckSym(const char *array, int aoff, const char *sstring, int &ic) { 70 | int c; 71 | ic=aoff; 72 | #define OECSIZE 17 73 | char okendchars[OECSIZE] = {0,',','+','-','/','%','*','|','&','!','^','<','>','@',')','(',':'}; 74 | for (c=0; c < strlen(sstring); c++) { 75 | if (LowerCase(array[ic]) != sstring[c]) { 76 | if (!(sstring[c]==' ' && (InSet(okendchars,OECSIZE,array[ic]) < OECSIZE))) return false; 77 | } 78 | ic++; 79 | } 80 | return true; 81 | } 82 | 83 | int OperandOffset(const char *thestring, int cchar) { 84 | int c; 85 | for (c=cchar; (c < strlen(thestring)) && !IsChar(thestring[c]); c++) {} 86 | return c; 87 | } 88 | 89 | // Returns error code 90 | int FindOperands(const char *instr,int firstbyte, char **pointers, unsigned long *values, int *types, int &numops, int maxops) { 91 | int c, c2, c3; 92 | unsigned long total; 93 | int errl; 94 | bool inastring=false,nextop=true,indreg=false,wasp=false; 95 | numops = 0; 96 | thiscertain=true; // what if a later operand is certain? 97 | whenuncertain=0; 98 | for (c=firstbyte; c < strlen(instr); c++) { 99 | switch (instr[c]) { 100 | case '\"': 101 | if (numops >= maxops && nextop) return ERR_TOO_MANY_OPERANDS; 102 | if (nextop) nextop=false; 103 | else return ERR_UNEXPECTED_CHARS; 104 | numops++; 105 | inastring=true; 106 | c2=c; 107 | *(pointers+numops) = (char *)(instr+c+1); 108 | types[numops] = DTYPE_STRING; 109 | while ((c < strlen(instr)) && inastring) { 110 | c++; 111 | if (instr[c] == '\"') inastring=false; 112 | } 113 | values[numops] = c-c2-1; 114 | if (inastring) return ERR_UNTERMINATED_STRING; 115 | break; 116 | case '\'': 117 | if (numops >= maxops && nextop) return ERR_TOO_MANY_OPERANDS; 118 | if (nextop) nextop=false; 119 | else return ERR_UNEXPECTED_CHARS; 120 | numops++; 121 | inastring=true; 122 | c2=c; 123 | *(pointers+numops) = (char *)(instr+c+1); 124 | types[numops] = DTYPE_STRING; 125 | while ((c < strlen(instr)) && inastring) { 126 | c++; 127 | if (instr[c] == '\'') inastring=false; 128 | } 129 | values[numops] = c-c2-1; 130 | if (inastring) return ERR_UNTERMINATED_STRING; 131 | break; 132 | case ',': 133 | // expects another operand 134 | if (nextop) return ERR_MISSING_OPERAND; 135 | nextop=true; 136 | case ' ': // ignore spaces 137 | break; 138 | // Assumed to be a symbol 139 | default: 140 | if (numops >= maxops && nextop) return ERR_TOO_MANY_OPERANDS; 141 | if (nextop) nextop=false; 142 | else return ERR_UNEXPECTED_CHARS; 143 | ExpError=false; 144 | ExpErrorV=ERR_COMPLEX_EXPRESSION; 145 | IsRegister=false; 146 | IsIndex=false; 147 | IsCOPRegister=false; 148 | wasp = (instr[c]=='('); 149 | if (thiscertain) { 150 | total = Evaluate(instr,c); 151 | if (!thiscertain) whenuncertain=numops+1; 152 | } else total = Evaluate(instr,c); 153 | if (ExpError) return ExpErrorV; 154 | numops++; 155 | if (instr[c]=='(') { 156 | if (indreg) return ERR_INDIRECTION; 157 | indreg=true; 158 | nextop=true; 159 | } else { 160 | if (indreg && !IsIndex) return ERR_INDIRECTION; 161 | if (IsIndex) { 162 | if (!indreg && !wasp) return ERR_INDIRECTION; 163 | indreg=false; 164 | } 165 | } 166 | values[numops]=total; 167 | types[numops]=(IsIndex?DTYPE_IREGISTER:(IsRegister?(IsCOPRegister?DTYPE_CREGISTER:DTYPE_REGISTER):DTYPE_INTEGER)); 168 | #ifdef DEBUG 169 | cout.flags(ios::hex|ios::showbase); 170 | cout << "Result of expression: " << total << '\n'; 171 | cout.flags(ios::dec); 172 | #endif 173 | c--; 174 | break; 175 | } 176 | } 177 | if (nextop && !(numops==0)) return ERR_MISSING_OPERAND; 178 | if (!thiscertain) return ERR_UNCERTAIN; 179 | return NO_ERROR; 180 | } 181 | 182 | int GetSymbol(const char * instr, int &c, int &symnum) { 183 | bool inastring=true; 184 | int c2, c3; 185 | for (c2=0; (c2 < maxsymbols)&&inastring; c2++) { 186 | // Check the first char, theres no need to call CheckSym 187 | // if not even the 1st char matches. 188 | if (SymbolList[c2].name[0] == LowerCase(instr[c])) { 189 | if (CheckSym(instr,c,SymbolList[c2].name,c3)) { 190 | c=c3-2; // it makes sense, think about it 191 | if (!SymbolList[c2].certain) {thiscertain=false; symbolcertain=false; numuncertain++;} 192 | symnum=c2; 193 | inastring=false; 194 | #ifdef DEBUG 195 | cout.flags(ios::showbase | ios::hex); 196 | cout << "Symbol " << SymbolList[c2].name << "'s value: " << SymbolList[c2].value << '\n'; 197 | cout.flags(ios::dec); 198 | #endif 199 | } 200 | } 201 | } 202 | #ifndef DEBUG 203 | if (inastring) return ERR_NOT_DEFINED; 204 | #else 205 | if (inastring) { 206 | cout << "Bad symbol: " << instr+c <<"*\n"; 207 | return ERR_NOT_DEFINED; 208 | } 209 | #endif 210 | return NO_ERROR; 211 | } 212 | 213 | int Assemble(char *instr, int outhandle, unsigned long &pc) { 214 | SymbolList[106].value=pc; 215 | SymbolList[106].certain=pccertain; 216 | bool instring, labeled=false, blankline=false; 217 | char stringinage; 218 | int c, lc, ic; 219 | int fnret; 220 | 221 | #ifdef DEBUG 222 | cout.flags(ios::showbase | ios::hex); 223 | cout << setw(11) << pc << ' '; 224 | cout << setw(11) << lseek(outhandle,0,SEEK_CUR) << ' '; 225 | cout.flags(ios::dec); 226 | #endif 227 | 228 | #ifdef DEBUG 229 | cout.flags(ios::left); 230 | cout << setw(49) << instr << setw(0) << "*\n"; 231 | #endif 232 | 233 | c=0; 234 | 235 | // if the first character is not blank, there is a label, skip ahead 236 | // to the actual command 237 | if (IsChar(instr[0])) { 238 | for (; (c < strlen(instr))&&IsChar(instr[c]); c++) {} 239 | labeled=true; 240 | } 241 | 242 | for (; (c < strlen(instr))&&!IsChar(instr[c]); c++) {} 243 | blankline = (c==strlen(instr)); 244 | if (blankline && !labeled) return NO_ERROR; 245 | 246 | // equ can correct this itself 247 | if (labeled) { 248 | for (lc=0; lc < maxsymbols; lc++) { 249 | if (CheckEq(instr,0,SymbolList[lc].name,ic)) break; 250 | } 251 | SymbolList[lc].value = pc; 252 | SymbolList[lc].type = DTYPE_INTEGER; 253 | SymbolList[lc].certain = pccertain; 254 | SymbolList[lc].bexport = true; 255 | } 256 | if (blankline) return NO_ERROR; 257 | 258 | fnret = AsmInstr(pc,instr,c,outhandle); 259 | 260 | return fnret; 261 | } 262 | 263 | // One for all and every man for himself! 264 | -------------------------------------------------------------------------------- /src/n64hijack.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | || Project: n64hijack 3 | || File: n64hijack.cpp 4 | || Author: jungerman 5 | || 6 | || Patches ROMs to run pieces of code before a given game starts. 7 | || Included is a simple ASM file to hijack the GEH and run cheats like a GameShark. 8 | ***************************************************************************************************/ 9 | 10 | #include "crc.h" 11 | #include 12 | #include 13 | #include 14 | 15 | // Code that is inserted at 0x1000 in the rom to start up the patcher 16 | uint8_t jumpInstructions[] = { 17 | 0x3C, 0x08, 0x00, 0x00, // lui t0, $0000 ;Address needs to be changed from 0 18 | 0x01, 0x00, 0x60, 0x09, // jalr t0, t4 19 | 0x00, 0x00, 0x00, 0x00 // nop 20 | }; 21 | 22 | const uint8_t jumpInstructionsLength = sizeof(jumpInstructions); 23 | 24 | 25 | // Read the ROM and update a buffer to point to it 26 | uint32_t getFile (const char* filename, char* charBuffer[]) { 27 | std::ifstream romStream (filename, std::ifstream::binary); 28 | if (!romStream) { 29 | printf("[!] Unable to open input file.\n"); 30 | exit(3); 31 | } 32 | 33 | romStream.seekg(0, romStream.end); 34 | uint32_t length = romStream.tellg(); 35 | romStream.seekg(0, romStream.beg); 36 | 37 | *charBuffer = new char[length]; 38 | romStream.read(*charBuffer, length); 39 | 40 | if (!romStream) { 41 | printf("[!] Unable to read all input stream data.\n"); 42 | exit(4); 43 | } 44 | 45 | romStream.close(); 46 | 47 | return length; 48 | } 49 | 50 | 51 | // Write a char buffer to a file 52 | void writeBuffer (const char* filename, char charBuffer[], uint32_t length) { 53 | std::ofstream romStream (filename, std::ofstream::binary); 54 | if (!romStream) { 55 | printf("[!] Unable to open output file.\n"); 56 | exit(6); 57 | } 58 | romStream.write(charBuffer, length); 59 | romStream.close(); 60 | } 61 | 62 | 63 | // Recalculate the CRC and update the buffer accordingly 64 | void updateCrc (uint8_t buffer[]) { 65 | bool didRecalculate[2]; 66 | uint32_t crc[2]; 67 | uint16_t cic; 68 | 69 | if (n64crc::recalculate(buffer, crc, &cic, didRecalculate)) { 70 | printf("[!] Error checking/recalculating CRC.\n"); 71 | exit(2); 72 | } 73 | 74 | printf("[i] Detected CIC: %d\n", cic); 75 | printf("[i] CRC"); 76 | printf((didRecalculate[0] || didRecalculate[1]) ? " [fixed]:" : " [ok]: "); 77 | printf(" 0x%08X, 0x%08X\n", crc[0], crc[1]); 78 | } 79 | 80 | 81 | // Insert the jump to the patcher at the beginning of the game's code 82 | void insertJump (uint32_t patcherAddress, uint8_t buffer[]) { 83 | jumpInstructions[2] = 0xB0 + (patcherAddress & 0xFF); 84 | jumpInstructions[3] = patcherAddress >> 16; 85 | 86 | for (uint8_t i = 0; i < jumpInstructionsLength; i++) { 87 | buffer[n64crc::GAME_START + i] = jumpInstructions[i]; 88 | } 89 | } 90 | 91 | 92 | // Insert the patcher at the specified address in the buffer. 93 | // The patcher is first created by assembling asmFile using hcs's assembler u64asm. 94 | void insertPatcher (uint32_t patcherAddress, uint8_t buffer[], const char* asmFile) { 95 | char *charPatcherBuffer; 96 | uint8_t *patcherBuffer; 97 | uint32_t patcherLength; 98 | char overwrittenAssembly[17 * jumpInstructionsLength + 1] = {0}; 99 | char orgAssembly[30] = {0}; 100 | 101 | for (uint8_t i = 0; i < (jumpInstructionsLength / 4); i++) 102 | sprintf( 103 | &overwrittenAssembly[17 * i], 104 | " dw $%02X%02X%02X%02X\n", 105 | buffer[n64crc::GAME_START + (4 * i) ], 106 | buffer[n64crc::GAME_START + (4 * i) + 1], 107 | buffer[n64crc::GAME_START + (4 * i) + 2], 108 | buffer[n64crc::GAME_START + (4 * i) + 3]); 109 | writeBuffer("overwritten.asm", overwrittenAssembly, sizeof(overwrittenAssembly)); 110 | 111 | sprintf(orgAssembly, "PATCHER_ADDRESS equ $%08X\n", patcherAddress); 112 | writeBuffer("patcher_address.asm", orgAssembly, sizeof(orgAssembly)); 113 | 114 | #if defined __unix__ || defined __APPLE__ 115 | if (system((std::string("./u64asm ") + asmFile + " -ohijack_temp.bin > assembly.log").c_str())) { 116 | #elif defined _WIN32 || defined _WIN64 117 | if (system((std::string("u64asm.exe ") + asmFile + " -ohijack_temp.bin > assembly.log").c_str())) { 118 | #else 119 | #error "unknown platform" 120 | #endif 121 | printf("\n[!] Error during assembly. See assembly.log for details\n"); 122 | exit(5); 123 | } 124 | 125 | patcherLength = getFile("./hijack_temp.bin", &charPatcherBuffer); 126 | patcherBuffer = (uint8_t *) charPatcherBuffer; 127 | 128 | remove("overwritten.asm"); 129 | remove("patcher_address.asm"); 130 | remove("hijack_temp.bin"); 131 | remove("assembly.log"); 132 | 133 | for (uint16_t i = 0; i < patcherLength; i++) 134 | buffer[patcherAddress + i] = patcherBuffer[i]; 135 | } 136 | 137 | 138 | // Games are padded with 0xFFFFFFFF until their size is a multiple of 2MiB. 139 | // This loops through the buffer holding the ROM, last byte to the first. Stop looping when a non-0xFFFFFFFF word is found. 140 | // That means that the last bit of game code is encountered. 141 | // Return an address in the ROM that is the first one that ends in 0000 after the address where the last code was encountered. 142 | // Assumes there will be an address with those qualifications. 143 | uint32_t findPatcherLocation (uint8_t buffer[], uint32_t length) { 144 | uint32_t insertLocation = 0; 145 | for (uint32_t i = length - 4; i >= n64crc::GAME_START; i -= 4) { 146 | if ( 147 | (buffer[i ] != 0xFF) || 148 | (buffer[i + 1] != 0xFF) || 149 | (buffer[i + 2] != 0xFF) || 150 | (buffer[i + 3] != 0xFF) 151 | ) { 152 | insertLocation = i + 4; 153 | break; 154 | } 155 | } 156 | 157 | insertLocation = insertLocation + 0x10000; // These two lines take the address where the first non-0xFFFFFFFF word is found 158 | insertLocation = insertLocation & 0xFFFF0000; // and gets the next address that ends in 0000 where we can insert the patcher. 159 | 160 | printf("[i] Patcher address: 0x%08X\n", insertLocation); 161 | 162 | return insertLocation; 163 | } 164 | 165 | 166 | // Replace any instructions that move a value to the CP0 WatchLo or WatchHi register. 167 | // In commercial games, they are surely an attempt to disable the GameShark. 168 | // Removes the need for F1-type codes. 169 | void killWatchInstructions (uint8_t buffer[], uint32_t length) { 170 | for (uint32_t i = n64crc::GAME_START; i < length; i += 4) { 171 | // Instruction format: 172 | // mtc0 $rt, $rd 173 | // ... where $rt is stored in $rd. 174 | // Binary equivalent is 01000000 100[$rt] [$rd]000 00000000 175 | // Remember that registers are identified by 5 bits. 176 | // Searching for: 177 | // mtc0 [any register], watchlo 178 | // mtc0 [any register], watchhi 179 | // Values: 180 | // watchlo = 18 = 0b10010 181 | // watchhi = 19 = 0b10011 182 | // mask to match both = 0b11110 183 | if ( 184 | ((buffer[i ] ) == 0b01000000) && // Upper bits should always be this. 185 | ((buffer[i + 1] & 0b11100000) == 0b10000000) && // Mask out the value of the $rt register, we don't care. 186 | ((buffer[i + 2] & 0b11110111) == 0b10010000) && // Mask out the bit that is different between watchlo and watchhi so we match both for $rd. 187 | ((buffer[i + 3] ) == 0b00000000) // Lower bits should always be this. 188 | ) { 189 | // NOP it! 190 | buffer[i ] = 0x00; 191 | buffer[i + 1] = 0x00; 192 | buffer[i + 2] = 0x00; 193 | buffer[i + 3] = 0x00; 194 | printf("[i] Killed a watch disabler at 0x%08X\n", i); 195 | } 196 | } 197 | } 198 | 199 | 200 | // Run this whenever n64hijack is run with incorrect arguments. 201 | void argumentError () { 202 | printf("Usage: n64hijack infile outfile asmfile [--noWatchKill]\n"); 203 | printf("See README.md for help.\n"); 204 | exit(1); 205 | } 206 | 207 | 208 | // Main function 209 | // The user passes in three arguments 210 | // The first argument is the input ROM to be patched 211 | // The second is the filename to store the output ROM with 212 | // The third is a bit of assembly to be run when the ROM starts. 213 | // - Included with this project is an asm file to enable using GameShark-like cheats for a game. 214 | // - Note that it only works correctly on real hardware and the cen64 emulator. 215 | // The fourth disables the GameShark WATCH interrupt protection (on by default). The GameShark requires the usage of a WATCH 216 | // interrupt to correctly override the General Exception Handler. Some games attempt to prevent the GameShark from working 217 | // by overwriting the WATCH interrupt defined by the GameShark. The WatchKill functionality automatically patches these out. 218 | // Normally, this is worked around by using F1-type GameShark codes, but leaving this enabled removes the need for them. 219 | int main (int argc, char** argv) { 220 | char* charRomBuffer; 221 | uint8_t* romBuffer; 222 | uint32_t length, patcherLocation; 223 | bool doWatchKill = true; 224 | const char* hijackAsmFilename; 225 | 226 | if (argc == 5) { 227 | if (strcmp(argv[4], "--noWatchKill")) argumentError(); 228 | } else if ((argc > 5) || (argc < 4)) { 229 | argumentError(); 230 | } 231 | 232 | if (argc == 4) doWatchKill = false; 233 | 234 | hijackAsmFilename = argv[3]; 235 | 236 | length = getFile((const char *)argv[1], &charRomBuffer); 237 | romBuffer = (uint8_t *) charRomBuffer; 238 | 239 | char *romTitle = new char[21]; 240 | memcpy(romTitle, &charRomBuffer[0x20], 20); 241 | printf("[i] Image name: %s\n", romTitle); 242 | 243 | patcherLocation = findPatcherLocation(romBuffer, length); 244 | if (doWatchKill) killWatchInstructions(romBuffer, patcherLocation); // patcherLocation is used as length of the buffer since everything after it is padding. 245 | insertPatcher(patcherLocation, romBuffer, hijackAsmFilename); 246 | insertJump(patcherLocation, romBuffer); 247 | updateCrc(romBuffer); 248 | writeBuffer((const char *)argv[2], charRomBuffer, length); 249 | 250 | delete[] romTitle; 251 | delete[] charRomBuffer; 252 | return 0; 253 | } 254 | -------------------------------------------------------------------------------- /patcher.asm: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ;; Project: n64hijack 3 | ;; File: patcher.asm 4 | ;; Author: jungerman 5 | ;; 6 | ;; A majority of the file is originally by parasyte , adapted from his alt64 7 | ;; repository found here: https://github.com/parasyte/alt64 8 | ;; This file is somewhat messy as it contains some syntax necessary to let this compile under 9 | ;; anarko's N64ASM assembler. This is no longer relevant since hcs's u64asm assember is used, but 10 | ;; some bits are still in here, nonetheless. 11 | ;; The comments found in this file are a mixture of my own and all the original comments found in 12 | ;; parasyte's usage of this assembly. 13 | ;; I am not entirely sure if this is still the case, but at one point this file required that the 14 | ;; patcher label be placed at an address ending with 0x0000. 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | 17 | #include patcher_address.asm 18 | org $B0000000 + PATCHER_ADDRESS 19 | 20 | ; Installs general exception handler, router, and code engine 21 | patcher: 22 | lui t5, patcher > $10 ; Start of temporary patcher location 23 | lui t6, $8000 ; Start of cached memory 24 | lui t7, $007F ; Address mask 25 | ori t7, $FFFF 26 | lui t8, $807C ; Permanent code engine location 27 | ori t8, $5C00 28 | lui t9, cheat_list > $10 29 | ori t9, cheat_list & $FFFF ; Get temporary code lists location (stored before patcher) 30 | 31 | 32 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 33 | ;; BOOT-TIME CHEATS 34 | 35 | load_next_boot_cheat: 36 | lw v0, $0000(t9) 37 | bnez v0, boot_cheat_type ; if it's equal to zero, we're done with the pre-boot cheats. 38 | lw v1, $0004(t9) ; t9 = code list location 39 | beqz v1, install_geh ; Only gets here if first branch doesn't happen (cheat addr = 0) 40 | 41 | boot_cheat_type: 42 | addiu t9, t9, $0008 ; t9 = 2 words after code list location 43 | srl t2, v0, 24 ; t2 = code type 44 | addiu at, zero, $EE 45 | beq t2, at, boot_cheat_ee 46 | addiu at, zero, $F0 47 | and v0, v0, t7 48 | beq t2, at, boot_cheat_f0 49 | or v0, v0, t6 50 | addiu at, zero, $F1 51 | beq t2, at, boot_cheat_f1 52 | nop 53 | 54 | ; Fall through, assume FF (probably bad idea) 55 | ; Apply FF code type 56 | addiu at, zero, $FFFC ; Mask address 57 | beq zero, zero, load_next_boot_cheat ; Our assembler doesn't support the b pseudoinstruction... 58 | and t8, v0, at ; Update permanent code engine location 59 | 60 | boot_cheat_f1: 61 | ; Apply F1 code type 62 | beq zero, zero, load_next_boot_cheat 63 | sh v1, $0000(v0) 64 | 65 | boot_cheat_f0: 66 | ; Apply F0 code type 67 | beq zero, zero, load_next_boot_cheat 68 | sb v1, $0000(v0) 69 | 70 | boot_cheat_ee: 71 | ; Apply EE code type 72 | lui v0, $0040 73 | sw v0, $0318(t6) 74 | beq zero, zero, load_next_boot_cheat 75 | sw v0, $03F0(t6) 76 | 77 | 78 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 79 | ;; INSTALLATION 80 | 81 | install_geh: 82 | ; Install General Exception Handler 83 | srl at, t8, 2 84 | and v0, at, t7 85 | lui at, $0800 86 | or v0, v0, at 87 | ; Installs a jump to the code engine at $80000180 88 | sw v0, $0180(t6) ; v0 = $081F1700 -> j $807c5c00 89 | sw zero, $0184(t6) ; nop 90 | 91 | ; Install code engine to permanent location 92 | sw t8, $0188(t6) ; Save permanent code engine location 93 | addiu at, zero, patcher & $FFFF 94 | addiu v0, zero, code_engine_start & $FFFF 95 | addiu v1, zero, code_engine_end & $FFFF 96 | subu at, v0, at ; at = patcher length 97 | subu v1, v1, v0 ; v1 = code engine length 98 | addu v0, t5, at ; v0 = temporary code engine location 99 | 100 | loop_copy_code_engine: 101 | ; Actually do the copying of the code engine to its permanent locatoin 102 | lw at, $0000(v0) 103 | addiu v1, v1, -4 104 | sw at, $0000(t8) 105 | addiu v0, v0, 4 106 | bgtz v1, loop_copy_code_engine 107 | addiu t8, t8, 4 108 | sw t8, $018C(t6) ; Save permanent code list location 109 | ; comes right after the nop after the jump to the code engine at the GEH 110 | 111 | loop_copy_code_list: 112 | ; Install in-game code list 113 | lw v0, $0000(t9) ; t9 points to temporary code list location for in-game codes 114 | lw v1, $0004(t9) 115 | addiu t9, t9, 8 116 | sw v0, $0000(t8) ; store code list after code engine 117 | sw v1, $0004(t8) ; ^ 118 | bnez v0, loop_copy_code_list 119 | addiu t8, t8, 8 120 | bnez v1, loop_copy_code_list 121 | nop ; only continue if both address and data are zero 122 | 123 | ; Write cache to physical memory and invalidate (GEH) 124 | ori t0, t6, $0180 125 | addiu at, zero, $0010 126 | 127 | loop_cache_geh: ; Iterates 4 times, so 0180 - 018F get updated 128 | cache $19, $0000(t0) ; Data cache hit writeback 129 | cache $10, $0000(t0) ; Instruction cache hit invalidate 130 | addiu at, at, -4 131 | bnez at, loop_cache_geh 132 | addiu t0, t0, 4 133 | 134 | 135 | ; Write cache to physical memory and invalidate (code engine + list) 136 | lw t0, $0188(t6) 137 | subu at, t8, t0 138 | loop_cache_code_engine: 139 | cache $19, $0000(t0) ; Data cache hit writeback 140 | cache $10, $0000(t0) ; Instruction cache hit invalidate 141 | addiu at, at, -4 142 | bnez at, loop_cache_code_engine 143 | addiu t0, t0, 4 144 | 145 | 146 | ; Protect GEH via WatchLo/WatchHi 147 | addiu t0, zero, $0181 ; Watch $80000180 for writes 148 | mtc0 t0, watchlo ; Cp0 WatchLo 149 | nop 150 | mtc0 zero, watchhi ; Cp0 WatchHi 151 | 152 | 153 | ; Start game! 154 | #include overwritten.asm 155 | jr t4 156 | nop 157 | 158 | 159 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 160 | ;; CODE ENGINE 161 | 162 | code_engine_start: 163 | mfc0 k0, cause ; Cp0 Cause 164 | andi k1, k0, $1000 ; Pre-NMI 165 | bnezl k1, not_nmi ; If this is an NMI interrupt, clear WatchLo (since it's not automatically cleared on reboot) 166 | mtc0 zero, watchlo ; Cp0 WatchLo 167 | 168 | not_nmi: 169 | andi k0, k0, $7C 170 | addiu k1, zero, $5C ; Watchpoint 171 | bne k0, k1, run_code_engine 172 | 173 | ; Watch exception; manipulate register contents 174 | ; Changes things to be stored at $80000120 instead of GEH assuming they 175 | mfc0 k1, epc ; Cp0 EPC 176 | lw k1, $0000(k1) ; Load cause instruction 177 | lui k0, $03E0 178 | and k1, k1, k0 ; Mask (base) register ; Register holding offset 179 | srl k1, k1, 5 ; Shift it to the "rt" position ; Upper half of k1 now contains register number 180 | lui k0, $3740 ; Upper half "ori , k0, $0120" 181 | or k1, k1, k0 182 | ori k1, k1, $0120 ; Lower half "ori , k0, $0120" 183 | lui k0, $8000 184 | lw k0, $0188(k0) ; Load permanent code engine location ; Stored during installation 185 | sw k1, $0060(k0) ; Self-modifying code FTW! ; Place the modified instruction at the placeholder below 186 | cache $19, $0060(k0) ; Data cache hit writeback ; Cache hit our updated instruction below 187 | cache $10, $0060(k0) ; Instruction cache hit invalidate ; ^ 188 | lui k0, $8000 189 | nop ; Short delay for cache sync 190 | nop 191 | nop 192 | nop ; Placeholder for self-modifying code ; Offset reg is changed to $80000120 here 193 | eret 194 | 195 | run_code_engine: 196 | ; Run code engine 197 | lui k0, $8000 198 | lw k0, $0188(k0) ; k0 now contains the address of the code engine 199 | addiu k0, k0, -40 ; We're storing things in the 28 bytes before the code engine (which is safe, I guess...) 200 | sd v1, $0000(k0) ; Back up registers we clobber 201 | sd v0, $0008(k0) 202 | sd t9, $0010(k0) 203 | sd t8, $0018(k0) 204 | sd t7, $0020(k0) 205 | 206 | ; Handle cheats 207 | lui t9, $8000 208 | lw t9, $018C(t9) ; Get code list location 209 | load_next_ingame_cheat: 210 | lw v0, $0000(t9) ; Load address 211 | bnez v0, address_not_zero 212 | lw v1, $0004(t9) ; Load value 213 | beqz v1, both_are_zero 214 | nop 215 | 216 | ; Address == 0 (TODO) 217 | beq zero, zero, load_next_ingame_cheat 218 | 219 | address_not_zero: 220 | ; Address != 0 221 | addiu t9, t9, $0008 222 | srl t7, v0, 24 223 | sltiu k1, t7, $D0 ; Code type < $D0 ? 224 | sltiu t8, t7, $D4 ; Code type < $D4 ? 225 | xor k1, k1, t8 ; k1 = ($D0 >= code_type < $D4) 226 | addiu t8, zero, $50 227 | bne t7, t8, not_repeater ; Code type != $50 ? -> 3 228 | 229 | ; GS Patch/Repeater 230 | srl t8, v0, 8 231 | andi t8, t8, $00FF ; Get address count 232 | andi t7, v0, $00FF ; Get address increment 233 | lw v0, $0000(t9) ; Load address 234 | lw k1, $0004(t9) ; Load value 235 | addiu t9, t9, $0008 236 | 237 | repeater_write_loop: 238 | sh k1, $0000(v0) ; Repeater/Patch write 239 | addiu t8, t8, -1 240 | addu v0, v0, t7 241 | bnez t8, repeater_write_loop 242 | addu k1, k1, v1 243 | beq zero, zero, load_next_ingame_cheat 244 | 245 | not_repeater: 246 | ; GS RAM write or Conditional 247 | lui t7, $0300 248 | and t7, v0, t7 ; Test for 8-bit or 16-bit code type 249 | lui t8, $A07F 250 | ori t8, $FFFF 251 | and v0, v0, t8 252 | lui t8, $8000 253 | beqz k1, gs_ram_write 254 | or v0, v0, t8 ; Mask address 255 | 256 | ; GS Conditional 257 | sll k1, t7, 7 258 | beqzl k1, skip_word_write1 259 | lbu t8, $0000(v0) ; 8-bit conditional 260 | lhu t8, $0000(v0) ; 16-bit conditional 261 | skip_word_write1: 262 | srl t7, t7, 22 263 | andi t7, t7, 8 ; Test for equal-to or not-equal-to 264 | beql v1, t8, load_next_ingame_cheat 265 | add t9, t9, t7 ; Add if equal 266 | xori t7, t7, 8 267 | beq zero, zero, load_next_ingame_cheat 268 | add t9, t9, t7 ; Add if not-equal 269 | 270 | gs_ram_write: 271 | ; GS RAM write 272 | sll k1, t7, 7 273 | beqzl k1, skip_word_write2 274 | sb v1, $0000(v0) ; Constant 8-bit write 275 | sh v1, $0000(v0) ; Constant 16-bit write 276 | skip_word_write2: 277 | beq zero, zero, load_next_ingame_cheat 278 | 279 | both_are_zero: 280 | ; Restore registers from our temporary stack, and back to the game! 281 | ld t7, $0020(k0) 282 | ld t8, $0018(k0) 283 | ld t9, $0010(k0) 284 | ld v0, $0008(k0) 285 | j $80000120 286 | ld v1, $0000(k0) 287 | code_engine_end: 288 | patcher_end: 289 | 290 | cheat_list: 291 | 292 | ; BEGIN PRE-BOOT CODES 293 | 294 | ; Insert codes here 295 | 296 | dw $00000000 ; END OF LIST 297 | dw $0000 298 | 299 | 300 | 301 | ; BEGIN IN-GAME CODES 302 | 303 | ; Example code: play as a glove in Super Mario 64 304 | ; dw $8133B4D6 305 | ; dw $3C4C 306 | 307 | dw $00000000 ; END OF LIST 308 | dw $0000 -------------------------------------------------------------------------------- /u64asm-src/pre.h: -------------------------------------------------------------------------------- 1 | // macro/include preprocessor v0.9 2 | // now allowing nested macros (and correctly echoing directives) 3 | // optimized a bit by using single chars to represent parameters to eliminate 4 | // the need to check *every* character against the parameters 5 | // attempting to make another type of include, imacros, and other small 6 | // speedups 7 | // fixed crash with zero length parameter 8 | // trying to allow parenthesis within macro parameters 9 | 10 | int PreProcessor (char * asmfile) { 11 | unsigned long defcnt=0,totlines=0; 12 | unsigned int c,c2,c3,c4; 13 | int numdone; 14 | bool nest=false,contain; 15 | 16 | ofstream output("temp.tmp"); 17 | if (!output) {cout << "\nError opening output file temp.tmp\n"; return 1;} 18 | if (CopyFile(asmfile,output,defcnt,totlines,false)) {return 1;} 19 | output << "#t " << totlines << '\n'; 20 | output.close(); 21 | 22 | #ifdef DEBUG 23 | cout << defcnt << " defines\n"; 24 | #endif 25 | 26 | Macro *macros = new Macro[defcnt]; 27 | if (!macros) {cout << "\nOut of memory.\n"; return 1;} 28 | 29 | cout << " ."; 30 | ifstream input("temp.tmp"); 31 | if (!input) {cout << "\nError opening input file temp.tmp.\n"; return 1;} 32 | if (LoadMacs(macros,input)) {return 1;} 33 | input.close(); 34 | 35 | #ifdef DEBUG 36 | for (c=0; c < defcnt; c++) { 37 | cout << "Macro: \"" << macros[c].name << "\"\n"; 38 | cout << "Text:\n\""; 39 | for (c2=0; c2 < strlen(macros[c].text); c2++) { 40 | if (macros[c].text[c2] <= macros[c].parnum) { 41 | cout << "[parameter " << (int)macros[c].text[c2] << ']'; 42 | } else cout << macros[c].text[c2]; 43 | } 44 | cout << "\"\n"; 45 | } 46 | #endif 47 | 48 | cout << " ."; 49 | for (c=0; c < defcnt; c++) { 50 | contain=false; 51 | for (c2=0; c2 < defcnt; c2++) { 52 | if (c2!=c) { 53 | for (c3=0; c3 < strlen(macros[c].name) && !contain; c3++) { 54 | contain = contain || CheckMac(macros[c].name,c3,macros[c2].name,c4); 55 | } 56 | } 57 | } 58 | if (contain) Errhandler(ERR_MACRO_NAME,0,NULL,macros[c].name); 59 | } 60 | 61 | do { 62 | cout << " ."; 63 | input.open("temp.tmp"); 64 | output.open("temp2.tmp"); 65 | if (!input) {cout << "\nError opening input file temp.tmp\n"; return 1;} 66 | if (!output) if (!output) {cout << "\nError opening output file temp2.tmp\n"; return 1;} 67 | numdone=0; 68 | if (DoMacs(macros,defcnt,numdone,input,output,nest)) {return 1;} 69 | output.close(); 70 | input.close(); 71 | if (numdone > 0) { 72 | unlink("temp.tmp"); 73 | rename("temp2.tmp","temp.tmp"); 74 | } 75 | nest=true; 76 | } while (numdone>0); 77 | cout << '\n'; 78 | 79 | return 0; 80 | } 81 | 82 | int CopyFile(char * infile, ofstream & output, unsigned long &defcnt, unsigned long &totlines, bool macfile) { 83 | int c,c2; 84 | unsigned long linecnt=0; 85 | char * pathfile; 86 | char instr[256], *filenamepass,stringinnage; 87 | bool inastring=false,willtolive; 88 | ifstream input(infile); 89 | if (!input) { 90 | pathfile=new char[strlen(asmpath)+strlen(infile)+1]; 91 | for (c=0; c < strlen(asmpath); c++) { 92 | pathfile[c]=asmpath[c]; 93 | } 94 | for (c=strlen(asmpath); c < strlen(asmpath)+strlen(infile); c++) { 95 | pathfile[c]=infile[c-strlen(asmpath)]; 96 | } 97 | pathfile[c]=0; 98 | input.open(pathfile); 99 | delete [] pathfile; 100 | if (!input) {cout << "\nError opening input file " << infile << '\n'; return 1;} 101 | } 102 | 103 | output << "#f " << infile << '\n'; 104 | output << "#l 0\n"; 105 | if (macfile) output << "#m\n"; 106 | 107 | while (!input.eof()) { 108 | linecnt++; 109 | totlines++; 110 | input.getline(instr,255); 111 | if (strlen(instr) >= 254) {return Errhandler(ERR_LINE_TOO_LONG,linecnt,infile,NULL);} 112 | willtolive=true; 113 | for (c=0; c < strlen(instr) && willtolive; c++) { 114 | if (instr[c] == ';' && !inastring) { 115 | instr[c] = 0; 116 | willtolive=false; // no point in reading any further 117 | } else if (instr[c] == stringinnage && inastring) { 118 | inastring = false; 119 | } else if (instr[c] == '\"'|| (instr[c] == '\'' && !inastring)) { 120 | inastring = true; 121 | stringinnage = instr[c]; 122 | } 123 | } 124 | if (instr[0]=='#' && LowerCase(instr[1])=='i' && 125 | ((LowerCase(instr[2])=='n' && LowerCase(instr[3])=='c' && 126 | LowerCase(instr[4])=='l' && LowerCase(instr[5])=='u' && 127 | LowerCase(instr[6])=='d' && LowerCase(instr[7])=='e') || 128 | (LowerCase(instr[2])=='m' && LowerCase(instr[3])=='a' && 129 | LowerCase(instr[4])=='c' && LowerCase(instr[5])=='r' && 130 | LowerCase(instr[6])=='o' && LowerCase(instr[7])=='s')) && 131 | instr[8]==' ') { 132 | for (c=9; instr[c]==' ' && c < strlen(instr); c++) {} 133 | if (instr[c]=='\"') { 134 | c++; 135 | for (c2=c; instr[c2]!='\"' && c2 < strlen(instr); c2++) {} 136 | } else if (instr[c]=='\'') { 137 | c++; 138 | for (c2=c; instr[c2]!='\'' && c2 < strlen(instr); c2++) {} 139 | } else { 140 | for (c2=c; instr[c2]!=' ' && c2 < strlen(instr); c2++) {} 141 | } 142 | instr[c2]=0; 143 | filenamepass=instr+c; 144 | if (!strcmp(filenamepass,infile)) {return Errhandler(ERR_NESTED_INCLUDE,linecnt,infile,NULL);} 145 | if (LowerCase(instr[2])=='m') {if (CopyFile(filenamepass,output,defcnt,totlines,true)) return 1;} 146 | else {if (CopyFile(filenamepass,output,defcnt,totlines,false)) return 1;} 147 | output << "#f " << infile << '\n'; 148 | output << "#l " << linecnt << '\n'; 149 | continue; 150 | } else if (instr[0]=='#' && LowerCase(instr[1])=='d' && 151 | LowerCase(instr[2])=='e' && LowerCase(instr[3])=='f' && 152 | LowerCase(instr[4])=='i' && LowerCase(instr[5])=='n' && 153 | LowerCase(instr[6])=='e' && instr[7]==' ') { 154 | defcnt++; 155 | } else if (instr[0]=='#') { 156 | return Errhandler(ERR_ILLEGAL_DIRECTIVE,linecnt,infile,instr); 157 | } 158 | output << instr << '\n'; 159 | } 160 | input.close(); 161 | 162 | return 0; 163 | } 164 | 165 | int LoadMacs(Macro *macros, ifstream &input) { 166 | char instr[256],innerstr[256],asmfile[256]; 167 | char ** parameters; 168 | unsigned int c,c2,c3,mc,cold,thislen,pc,cstrlen; 169 | int sc; 170 | unsigned long line=0; 171 | unsigned int maccount=0; 172 | streampos spos; 173 | bool willtolive,willtolive2,continuate; 174 | while (!input.eof()) { 175 | parameters=NULL; 176 | line++; 177 | input.getline(instr,255); 178 | 179 | if (instr[0]=='#') { 180 | switch (instr[1]) { 181 | case 'f': 182 | for (c=3; c < strlen(instr); c++) { 183 | asmfile[c-3]=instr[c]; 184 | } 185 | asmfile[c-3]=0; 186 | break; 187 | case 'l': 188 | line=0; 189 | for (c=3; c < strlen(instr); c++) { 190 | line*=10; 191 | line+=instr[c]-'0'; 192 | } 193 | break; 194 | default: 195 | 196 | if (LowerCase(instr[1])=='d' && 197 | LowerCase(instr[2])=='e' && LowerCase(instr[3])=='f' && 198 | LowerCase(instr[4])=='i' && LowerCase(instr[5])=='n' && 199 | LowerCase(instr[6])=='e' && instr[7]==' ') { 200 | for (c=8; c < strlen(instr) && instr[c] == ' '; c++) {} 201 | // find size of macro name 202 | for (c2=0; c+c2 < strlen(instr) && instr[c+c2] != ' ' && instr[c+c2] != '('; c2++) {} 203 | // allocate mem 204 | macros[maccount].name = new char[c2+1]; 205 | if (!macros[maccount].name) {cout << "\nOut of memory.\n"; return 1;} 206 | for (c2=0; c+c2 < strlen(instr) && instr[c+c2] != ' ' && instr[c+c2] != '('; c2++) { 207 | macros[maccount].name[c2]=instr[c+c2]; 208 | } 209 | macros[maccount].name[c2]=0; 210 | for (mc=0; mc < maccount; mc++) { 211 | if (!strcmp(macros[maccount].name,macros[mc].name)) { 212 | return Errhandler(ERR_ALREADY_DEFINED,line,asmfile,instr); 213 | } 214 | } 215 | 216 | macros[maccount].parnum=0; 217 | cold=c+c2; 218 | c=c+c2; 219 | if (instr[c] == '(') { 220 | #ifdef DEBUG 221 | cout << "\nCounting parameters for " << macros[maccount].name << '\n'; 222 | #endif 223 | macros[maccount].parnum++; 224 | // count # of parameters 225 | willtolive=true; 226 | for (c=cold+1; willtolive && c < strlen(instr); c++) { 227 | switch (instr[c]) { 228 | case ',': 229 | macros[maccount].parnum++; 230 | break; 231 | case ')': 232 | willtolive=false; 233 | break; 234 | default: 235 | break; 236 | } 237 | } 238 | 239 | parameters = new char*[macros[maccount].parnum]; 240 | if (!parameters) {cout << "\nOut of memory.\n"; return 1;} 241 | 242 | // count the length of each parameter 243 | c=cold+1; 244 | for (c2=0; c2 < macros[maccount].parnum; c2++) { 245 | willtolive=true; 246 | thislen=0; 247 | for (; willtolive && c < strlen(instr); c++) { 248 | switch (instr[c]) { 249 | case ' ': 250 | break; 251 | case ',': 252 | willtolive=false; 253 | break; 254 | case ')': 255 | willtolive=false; 256 | break; 257 | default: 258 | thislen++; 259 | } 260 | } 261 | if (thislen==0) {Errhandler(ERR_ZERO_LENGTH,line,asmfile,instr); return 1;} 262 | parameters[c2] = new char[thislen+1]; 263 | if (!parameters[c2]) {cout << "\nOut of memory.\n"; return 1;} 264 | } 265 | // load each parameter into the array 266 | // parameters are case sens. 267 | c=cold+1; 268 | for (c2=0; c2 < macros[maccount].parnum; c2++) { 269 | willtolive=true; 270 | thislen=0; 271 | for (; willtolive && c < strlen(instr); c++) { 272 | switch (instr[c]) { 273 | case ' ': 274 | break; 275 | case ',': 276 | willtolive=false; 277 | break; 278 | case ')': 279 | willtolive=false; 280 | break; 281 | default: 282 | parameters[c2][thislen]=instr[c]; 283 | thislen++; 284 | } 285 | } 286 | parameters[c2][thislen]=0; 287 | } 288 | 289 | } // end of "if (instr[c] == '(')" 290 | 291 | for (; c < strlen(instr) && instr[c] == ' '; c++) {} 292 | macros[maccount].multiline=false; 293 | // find size of macro 294 | willtolive=true; 295 | thislen=0; 296 | strcpy(innerstr,instr); 297 | spos = input.tellg(); 298 | cold=c; 299 | willtolive=false; 300 | cstrlen=strlen(innerstr); 301 | for (sc=c; sc < cstrlen; sc++) { 302 | thislen++; 303 | if (innerstr[sc]=='\\') { 304 | macros[maccount].multiline=true; 305 | input.getline(innerstr,255); 306 | cstrlen=strlen(innerstr); 307 | sc=-1; 308 | } 309 | } 310 | input.seekg(spos); 311 | 312 | // allocate mem for it 313 | macros[maccount].text = new char[thislen+1]; 314 | 315 | if (!macros[maccount].text) {cout << "\nOut of memory.\n"; return 1;} 316 | willtolive=true; 317 | thislen=0; 318 | strcpy(innerstr,instr); 319 | c=cold; 320 | while (willtolive) { 321 | willtolive=false; 322 | for (c2=c; c2 < strlen(innerstr) && !willtolive; c2++) { 323 | if (innerstr[c2]=='\\') { 324 | macros[maccount].text[thislen]=0xa; 325 | willtolive=true; 326 | } else { 327 | willtolive2=true; 328 | for (pc=0; (pc < macros[maccount].parnum) && willtolive2; pc++) { 329 | if (CheckMac(innerstr,c2,parameters[pc],c3)) { 330 | willtolive2=false; 331 | macros[maccount].text[thislen]=(pc+1)|0x80; 332 | c2=c3-1; 333 | } 334 | } 335 | if (willtolive2) macros[maccount].text[thislen]=innerstr[c2]; 336 | } 337 | thislen++; 338 | } 339 | if (willtolive) { 340 | line++; 341 | input.getline(innerstr,255); 342 | if (strlen(innerstr) > 254) return Errhandler(ERR_LINE_TOO_LONG,line,asmfile,NULL); 343 | c=0; 344 | } 345 | } 346 | 347 | for (pc=0; pc < macros[maccount].parnum; pc++) { 348 | delete [] parameters[pc]; 349 | parameters[pc]=NULL; 350 | } 351 | if (macros[maccount].parnum > 0) {delete [] parameters; parameters=NULL;} 352 | 353 | macros[maccount].text[thislen] = 0; 354 | 355 | maccount++; 356 | } // end of if define 357 | } // end of big select 358 | } // end of if # 359 | } // end of while 360 | return 0; 361 | } 362 | 363 | int DoMacs(Macro * macros, unsigned long defcnt, int &numdone, ifstream &input, ofstream &output, bool nest) { 364 | unsigned long linenum=0; 365 | unsigned int c,c2=0,c3,c4,cold,mc,pc,thislen; 366 | char instr[256],asmfile[256]; 367 | char **parameters; 368 | unsigned int parnum,parencount; 369 | bool macfound,macfound2,willtolive,linecount=true,macfile=false; 370 | 371 | while (!input.eof()) { 372 | if (linecount) linenum++; 373 | input.getline(instr,255); 374 | 375 | if (instr[0]==0) { 376 | if (!macfile) output << '\n'; 377 | } else if (instr[0]=='#') { 378 | if (instr[1]=='m') { 379 | macfile=true; 380 | if (!nest) output << instr << '\n'; 381 | } 382 | if (instr[1]=='f') { 383 | macfile=false; 384 | for (c=3; c < strlen(instr); c++) { 385 | asmfile[c-3]=instr[c]; 386 | } 387 | asmfile[c-3]=0; 388 | output << instr << '\n'; 389 | } else if (instr[1]=='e') {output << instr << '\n'; linecount=false;} 390 | else if (instr[1]=='s') {output << instr << '\n'; linecount=true;} 391 | else if (instr[1]=='l') { 392 | linenum=0; 393 | for (c=3; c < strlen(instr); c++) { 394 | linenum*=10; 395 | linenum+=instr[c]-'0'; 396 | } 397 | output << instr << '\n'; 398 | // take any #defines out of the output stream (define absorbtion) 399 | } else if (LowerCase(instr[1])=='d' && 400 | LowerCase(instr[2])=='e' && LowerCase(instr[3])=='f' && 401 | LowerCase(instr[4])=='i' && LowerCase(instr[5])=='n' && 402 | LowerCase(instr[6])=='e' && instr[7]==' ') { 403 | output << '\n'; 404 | willtolive=true; 405 | for (c=0; c < strlen(instr) && willtolive; c++) {if (instr[c]=='\\') willtolive=false;} 406 | while (instr[c-1]=='\\') { 407 | linenum++; input.getline(instr,255); output << '\n'; 408 | willtolive=true; 409 | for (c=0; c < strlen(instr) && willtolive; c++) {if (instr[c]=='\\') willtolive=false;} 410 | } 411 | } else if (instr[1]=='t') { 412 | output << instr << '\n'; 413 | } 414 | } else { // end of if '#' 415 | if (!macfile) { // nothing gets echoed in a macro file 416 | 417 | // make an early pass to find if any macros are in the line 418 | macfound2=false; 419 | for (c=0; c < strlen(instr) && !macfound2; c++) { 420 | if (instr[c]==' ') continue; 421 | for (mc=0; mc < defcnt && !macfound2; mc++) { 422 | if (CheckMac(instr,c,macros[mc].name,c2)) macfound2=true; 423 | } 424 | } 425 | if (macfound2 && !nest) output << "#e\n"; 426 | for (c=0; macfound2 && c < strlen(instr); c++) { 427 | if (instr[c]==' ') {output << ' '; continue;} 428 | macfound=false; 429 | for (mc=0; mc < defcnt && !macfound; mc++) { 430 | if (CheckMac(instr,c,macros[mc].name,c2)) { 431 | macfound=true; 432 | numdone++; 433 | if (macros[mc].parnum==0) { 434 | output << macros[mc].text; 435 | } else { 436 | if (instr[c2]=='(') { 437 | // BEGIN PARAMETER RECOGNITION 438 | // count # of parameters 439 | willtolive=true; 440 | parnum=1; 441 | parencount=1; 442 | cold=c2; 443 | for (c3=cold+1; willtolive && c3 < strlen(instr); c3++) { 444 | switch (instr[c3]) { 445 | case ',': 446 | if (parencount==1) parnum++; 447 | break; 448 | case '(': 449 | parencount++; 450 | break; 451 | case ')': 452 | parencount--; 453 | if (parencount==0) willtolive=false; 454 | break; 455 | default: 456 | break; 457 | } 458 | } 459 | if (parnum != macros[mc].parnum) { 460 | #ifdef DEBUG 461 | cout << "Should be " << macros[mc].parnum << ", is " << parnum << ".\n"; 462 | #endif 463 | return Errhandler(ERR_NUM_PARAMETERS,linenum,asmfile,instr); 464 | } 465 | 466 | parameters = new char*[parnum]; 467 | if (!parameters) {cout << "\nOut of memory.\n"; return 1;} 468 | // count the length of each parameter 469 | c3=cold+1; 470 | parencount=1; 471 | for (pc=0; pc < parnum; pc++) { 472 | willtolive=true; 473 | thislen=0; 474 | for (; willtolive && c3 < strlen(instr); c3++) { 475 | switch (instr[c3]) { 476 | //case ' ': 477 | // break; 478 | case ',': 479 | if (parencount==1) willtolive=false; 480 | else thislen++; 481 | break; 482 | case '(': 483 | parencount++; 484 | thislen++; 485 | break; 486 | case ')': 487 | parencount--; 488 | if (parencount==0) willtolive=false; 489 | else thislen++; 490 | break; 491 | default: 492 | thislen++; 493 | } 494 | } 495 | if (thislen==0) {Errhandler(ERR_ZERO_LENGTH,linenum,asmfile,instr); return 1;} 496 | parameters[pc] = new char[thislen+1]; 497 | if (!parameters[pc]) {cout << "\nOut of memory.\n"; return 1;} 498 | } 499 | // load each parameter into the array 500 | // parameters are case sens. 501 | c3=cold+1; 502 | parencount=1; 503 | for (pc=0; pc < parnum; pc++) { 504 | willtolive=true; 505 | thislen=0; 506 | for (; willtolive && c3 < strlen(instr); c3++) { 507 | switch (instr[c3]) { 508 | //case ' ': 509 | // break; 510 | case ',': 511 | if (parencount==1) willtolive=false; 512 | else {parameters[pc][thislen]=instr[c3]; thislen++;} 513 | break; 514 | case ')': 515 | parencount--; 516 | if (parencount==0) willtolive=false; 517 | else {parameters[pc][thislen]=instr[c3]; thislen++;} 518 | break; 519 | case '(': 520 | parencount++; 521 | parameters[pc][thislen]=instr[c3]; 522 | thislen++; 523 | break; 524 | default: 525 | parameters[pc][thislen]=instr[c3]; 526 | thislen++; 527 | } 528 | } 529 | parameters[pc][thislen]=0; 530 | } 531 | c2=c3; // tell the above loop that the macro name ended where the 532 | // parenthesis ended 533 | for (c3=0; c3 < strlen(macros[mc].text); c3++) { 534 | if (((unsigned char)macros[mc].text[c3]) > 0x80) { 535 | output << parameters[(macros[mc].text[c3]&0x7f)-1]; 536 | } else output << macros[mc].text[c3]; 537 | } 538 | 539 | #ifdef DEBUG 540 | cout << "Called with parameters:\n"; 541 | #endif 542 | for (pc=0; pc < parnum; pc++) { 543 | #ifdef DEBUG 544 | cout << parameters[pc] << '\n'; 545 | #endif 546 | delete [] parameters[pc]; 547 | } 548 | delete [] parameters; 549 | //END PARAMETER RECOGNTION 550 | } else {return Errhandler(ERR_NUM_PARAMETERS,linenum,asmfile,instr);} 551 | } 552 | c=c2-1; 553 | } 554 | } 555 | if (!macfound) output << instr[c]; 556 | } 557 | if (!macfound2) output << instr; 558 | output << '\n'; 559 | if (macfound2 && !nest) output << "#s\n"; 560 | } 561 | } // end of else '#' 562 | } // end of main while 563 | return 0; 564 | } 565 | 566 | bool static CheckMac(char *array, unsigned int aoff, char *sstring, unsigned int &ic) { 567 | unsigned int c; 568 | ic=aoff; 569 | for (c=0; c < strlen(sstring); c++) { 570 | if (array[ic] != sstring[c]) return false; 571 | ic++; 572 | } 573 | return true; 574 | } 575 | 576 | // Telepancreasectomesis - The removal of a person's pancreas with 577 | // one's mind without physical contact from a 578 | // distance. 579 | 580 | 581 | // Complifyificationnessitudeocitation - opposite of simplification 582 | -------------------------------------------------------------------------------- /u64asm-src/n64.cpp: -------------------------------------------------------------------------------- 1 | // This project started 2/20/02 in order to do Neon64 2 | // Written on a 10 y/o machine very much like the N64: 100mhz, 7 MB RAM 3 | // But it has a 329 MB hard drive! 4 | 5 | #define VERSION "0.1p" 6 | #define CDATE "9/2/2003" 7 | 8 | // v0.0a - First code, only support for db strings. 2/22/02 9 | // v0.0b - Revised error codes, standardized operand reading 2/22/02 10 | // v0.0c - comment ignorance, operand recognition, intergal operands 11 | // (decimal, hex and binary) fixed some error reporting 2/24/02 12 | // v0.0d - enforced line too long error, debug stuff, speed reporting 2/25/02 13 | // v0.0e - added org, changed to correct method of argument access 14 | // added negation (only for decimal values) 2/27/02 15 | // v0.0f - began to set up symbol recognition (max 256 symbols of length 16 | // 31 characters each for now, symbols must be stored in lowercase) 17 | // Note: org does not immediately change the pc, the change 18 | // only takes effect on the next line. 19 | // Note: Something new will need to be done about the 20 | // offset(base) operand in the load/store instructions. Maybe treat 21 | // the opening parentesis as a comma and ignore the closing one? 22 | // Or just use a simpler syntax: lb t0, $100, t1. The () are unneeded, 23 | // its not as if there are really any addressing modes! 24 | // v0.0g - Cleared up some symbol stuff, added quotes. 25 | // v0.0h - Fixed error in CheckEq, made include, cleaned up Errhandler 26 | // I ended this version before I acutally finished working tonight 27 | // because I feared that the next step might mess up my work. 28 | // v0.0i - Added "1st pass", which counts the number of symbols so that 29 | // memory can be allocated for however many. Added r1-r31 reg names. 30 | // Cleaned out some commented code. Turned off obsolete warning. :-) 31 | // Rearranged a lot. Added some more quotes, put them in seperate file. 32 | // v0.0j - Fixed major error in previous version caused by r1-r31 change. 33 | // v0.0k - Added a lot of quotes, wrote complex expression evaluator but 34 | // haven't included it yet. 35 | // v0.0l - Added expression evaluation but not with symbols yet. 36 | // v0.0m - Made symbols in expression evaluation work, added '0x' prefix for 37 | // hexadecimal. Made equr and fixed equ and equr's error reporting. 38 | // Made expression error reporting more specific. Only macros now... 39 | // v0.0n - Actual opcodes (load+store), added macros where they were sorely 40 | // needed, so code is easy to write although the compiled prog is big. 41 | // Added the indexed () addressing mode, made it required for load/ 42 | // store instructions. Added dh and dw. Switched to medium mem model. 43 | // Labels can end in a colon that will be ignored. 44 | // v0.0o - About, changed UI a bit, added quotes. Didn't want to mess up this 45 | // nice new version ... 46 | // v0.0p - Put in the multi-pass certainty thing. It could use some more 47 | // testing, probably. 48 | // v0.0q - Fixed bug in symbol name loading with include files. 49 | // Fixed bug that allowed a symbol to be defined in terms of itself. 50 | // Fixed improper reporting of ERR_EQU_NEEDS_LABEL 51 | // Increased speed by not writing if uncertain. 52 | // v0.0r - Worked on uncertainty a bit more, cleaned up some too many labels 53 | // errors. Added dc*, incbin. Closed file before end. Made pointers 54 | // char instead of void. 55 | // v0.0s - Added Spaceballs quotes, revised about screen. 56 | // v0.0t - Incorporated preprocessor, made includes able to have single, 57 | // double, or no quotes. Added some Biblical and dragon warrior 58 | // quotes. Macros are done, all that remains is to add the rest 59 | // of the opcodes. 60 | // v0.0u - Fixed bug that prevented macro parameters from being deallocated. 61 | // Found a bug in n64asm that assembles dsllv wrong. 62 | // Added all opcodes (except the "special" and exception opcodes.) 63 | // test.asm assembles more or less the same as in asmn64, except 64 | // that it uses addiu in li instead of ori. Why? 65 | // v0.0v - Used a more complex method of calculating li, to accomodate signed 66 | // values. Made warnings type 1 errors. 67 | // Actually did some test runs! 68 | // As soon as some style warnings are added it'll be time for version 69 | // 1.0! 70 | // v0.0w - Added *more* opcodes. (break, mfc0, mtc0). Revised li again. 71 | // Did some more tests, got RSP working! 72 | // v0.0x - Cleaned up preprocessor error reporting a little. 73 | // Added PC to debug reporting. 74 | // Fixed bug that didn't limit load/store immeidate values to 75 | // 16 bits. 76 | // Realized that move had been in here since v0.0u :-) 77 | // v0.0y - Added NEG opcode, made it possible for include files to be in the 78 | // same directory as the assembler instead of where it is run from. 79 | // Made non-debug version not include "Counting Symbols . . ." 80 | // Two months ago this project began . . . 81 | // v0.0z - Another quote 82 | // v0.1 - Standardized opcode generation. Found big bug 83 | // in xor. Added obj/objend. Can't figure out the register math error. 84 | // v0.1a - Worked with arguments, added header file generation. 85 | // Added report to tell you what offset you're at, to aid in making 86 | // headers. 87 | // v0.1b - Debugging erroneous or with 0x20000000 for sw and sd. Which didn't 88 | // actually exist. Added offset, which actually tells you what offset 89 | // you're at. report gives you the PC. assert makes sure that it is 90 | // at the given PC, for use in headers, generates an error otherwise. 91 | // Made assert a part of the header. 92 | // v0.1c - Fixed a bug that checked for symbol type even when the symbol 93 | // was uncertain, only a problem in 'special' instructions. 94 | // Would it be so hard to have macros within macros? The answer 95 | // turned out to be no. 96 | // v0.1d - I added the symbol pc, which is the PC of the beginning of a line, 97 | // and fixed a problem with the macro nesting where internal 98 | // directives weren't being mirrored. 99 | // errors are now printed with the line that caused them. Removed the 100 | // "Immediate value too large" error, it just doesn't work. 101 | // v0.1e - Trying to speed up preprocessor. Exported Errhandler to another 102 | // file. Also made the line continuation character available anywhere 103 | // in the line, so if there's a comment afterward it should still be 104 | // OK. I also discovered that when deallocating an array you use 105 | // delete [], so I went through the program (mostly the preprocessor) 106 | // fixing that. Added a warning if a macro name contains another 107 | // macro name, made errtot (# of warnings) a global and had 108 | // Errhandler incrememnt it for an accurate count. 109 | // v0.1f - Made another type of include that expects a file with nothing 110 | // but macros, so we don't have to wade through all the blank lines. 111 | // This and some other tweaks made macro processing considerably 112 | // faster (Almost twice as fast now!) 113 | // v0.1g - Trying to speed up binary inclusion by loading the entire file 114 | // to memory. It worked. 115 | // v0.1h - A bit of a bug in macros ignores spaces in parameters. 116 | // Another bug crashed the program with a zero-sized macro parameter. 117 | // Fixed incbin error reporting and gave it a bad operand error. 118 | // Took "first org" comment out of headers. 119 | // Made equne to keep those labels out of headers. 120 | // Added cdate to program title, since I've had several versions of 121 | // v0.1h. 122 | // v0.1i - Large incbins cause errors... 123 | // A failed assert now tells you what the PC actually is. 124 | // Header no longer generated if assemble fails. 125 | // v0.1j - Maybe register math error is caused by uncertainty? Since I made it 126 | // only report the error if symbolcertain, the problem hasn't come 127 | // up again. I also fixed an error of the cache instruction, the 128 | // cache op was at the wrong bit position. 129 | // Fixed an error in eret, thanks to my good friend the MIPS R4000 130 | // User's Manual. 131 | // v0.1k - I found the CRC source, so now I have the assembler output 132 | // a completely checksummed N64 ROM image. (CRC code from stan). 133 | // Also added a modified version of Bung's drjr send utility, with 134 | // built-in byteswapping. U64ASM is now an all-in-one utility! 135 | // v0.1l - Extend generates an error if it it runs out of hard drive space. 136 | // v0.1m - Added watch instruction, reports which line contains a certain PC. 137 | // v0.1n - Checking for request to make zero items, prevented error 138 | // on zero assemble time. 139 | // v0.1o - Trying to find the source of some pain-in-the-ass errors. By 140 | // making the preprocessor only replace a \ with 0xa instead of 0xd 141 | // and 0xa I seem to have stopped it... 142 | // v0.1p - Added support for parenthesis in macro parameters to preprocessor 143 | // Found a problem with macros with 10 or more parameters: $0a, the 144 | // line feed character, would be interpreted as parameter 10... 145 | // I think a good solution would be to use characters 128 and up, 146 | // which of course excludes them from being used in any macro. 147 | // Now catch divide by zero error in divide operation (I had all the 148 | // special cases handled for all the other ops, but not the obvious 149 | // one.) 150 | 151 | //#define DEBUG 152 | //#define QUOTES 153 | #pragma warn -obs 154 | 155 | #include 156 | #include 157 | #include 158 | #include 159 | #include 160 | #include 161 | #include 162 | #include 163 | #include 164 | #include 165 | #include "defines.h" 166 | 167 | using namespace std; 168 | 169 | bool ExpError; 170 | int ExpErrorV; 171 | 172 | int errtot=0; 173 | 174 | bool IsRegister; 175 | bool IsIndex; 176 | bool IsCOPRegister; 177 | 178 | unsigned long watchpoint; 179 | bool watchactive=false; 180 | unsigned long this_line_count=0; 181 | 182 | bool pccertain=true; 183 | bool symbolcertain=true; 184 | bool thiscertain=true; 185 | unsigned int numuncertain; 186 | unsigned int whenuncertain; 187 | 188 | bool isfirstorg=true; 189 | unsigned long firstorg=0; 190 | 191 | unsigned long objstart; // where the object started 192 | unsigned long objinit; // initial value for the object 193 | bool objcert; 194 | bool inobj=false; 195 | 196 | char * asmpath; 197 | 198 | struct Macro { 199 | char *name; 200 | unsigned int parnum; 201 | char *text; 202 | bool multiline; 203 | }; 204 | 205 | void InitRegs(void); 206 | int LoadSymbols(const char * asmfiles, unsigned int line); 207 | unsigned int CountSymbols(const char *); 208 | int Errhandler(int, unsigned long, char *, char *); 209 | void Syntax(char *); 210 | int AssembleFile(const char *, int, unsigned long &,unsigned long &, bool &); 211 | bool IsChar(char); // Returns false if the character is a space or null 212 | char LowerCase(char); // Converts character to lowercase 213 | bool CheckEq(const char *, int, const char *, int &); // Sees if 2nd str is in 214 | // 1st str at int 215 | bool CheckSym(const char *, int, const char *, int &); // slightly different from 216 | // checkeq because it allows chars after the string 217 | // when there are spaces in the search string 218 | int Assemble(ifstream &, int, unsigned long &); // Main assembly function 219 | // returns error code 220 | int OperandOffset(const char *, int); // Finds the offset of the 221 | // first operand 222 | int FindOperands(const char *,int, char **, unsigned long *, int *, int &, int); 223 | int GetSymbol(const char *, int &, int &); 224 | 225 | // Expression evaluation prototypes 226 | int InSet(char *, int, char); 227 | unsigned long EvOp(const char *, int &); 228 | unsigned long EvInt(const char *, int &); 229 | unsigned long Evaluate(const char *, int &); 230 | unsigned long DivRep(unsigned long, unsigned long, unsigned long); 231 | unsigned long Root(unsigned long, unsigned long); 232 | 233 | // Preprocessor prototypes 234 | int CopyFile(char *, ofstream &, unsigned long &, unsigned long &, bool); 235 | char inline LowerCase(char); 236 | int LoadMacs(Macro *, ifstream &); 237 | int DoMacs(Macro *, unsigned long, int &, ifstream &, ofstream &, bool); 238 | int InSet(char * set, int setsize, char ciq); 239 | bool static CheckMac(char *array, unsigned int aoff, char *sstring, unsigned int &ic); 240 | int PreProcessor (char *); 241 | 242 | int crc(int); // Takes a file handle 243 | int drjrsend(char *); // Parameter is name of file to send. 244 | #include "symbols.h" 245 | #include "asm.h" 246 | #include "exp.h" 247 | #include "pre.h" 248 | #include "err.h" 249 | #include "crc.h" 250 | 251 | 252 | // get time in milliseconds 253 | unsigned long gettime(void) { 254 | struct timeval tv; 255 | gettimeofday(&tv, NULL); 256 | 257 | return tv.tv_sec * 1000 + tv.tv_usec / 1000; 258 | } 259 | 260 | int main(int argc, char *argv[]) { 261 | // fatalerror means the program was actually stopped 262 | // anerr means that a 'minor' error occured and was ignored 263 | bool fatalerror=false,willtolive,header=false,romout=false,drsend=false; 264 | unsigned long bios_start_time=gettime(); 265 | int err,c,c2; 266 | int outhandle,headhandle; 267 | unsigned long program_counter; 268 | unsigned long line_count; 269 | unsigned int numpasses=1; 270 | unsigned int lastnumuncertain; 271 | unsigned char * temp; 272 | char * pathfile; 273 | char smalltemp; 274 | 275 | // *** BEGIN ARGUMENT PROCESSING 276 | 277 | const char *asmfile = "temp2.tmp"; 278 | char *realfile = (char *)0; 279 | char *outfile = (char *)0; 280 | char *headfile = (char *)0; 281 | 282 | cout << "U64ASM " << VERSION; 283 | #ifdef DEBUG 284 | cout << " (Internal debug version)"; 285 | #endif 286 | cout << " (" << CDATE << ")\n"; 287 | cout << "(c) 2002-2003 Halley's Comet Software\n\n"; 288 | if (argc == 0) { 289 | Syntax(argv[0]); 290 | return 1; 291 | } 292 | 293 | for (c=1; c < argc; c++) { 294 | if (!strcmp(argv[c],"-about") || !strcmp(argv[c],"-credits")) { 295 | #include "about.h" 296 | return 0; 297 | } else if (argv[c][0] == '-' && argv[c][1] == 'o') { 298 | outfile = argv[c]+2; 299 | } else if (argv[c][0] == '-' && argv[c][1] == 'h') { 300 | header = true; 301 | headfile = argv[c]+2; 302 | if (argv[c][2]=='\0') headfile = (char *)0; 303 | } else if (argv[c][0] == '-' && argv[c][1] == 'r') romout=true; 304 | else { 305 | realfile = argv[c]; 306 | willtolive=true; 307 | for (c2=0; c2 < strlen(realfile) && willtolive; c2++) { 308 | if (realfile[c2]=='.') willtolive=false; 309 | } 310 | if (willtolive) { 311 | realfile = new char[strlen(realfile)+5]; 312 | strcpy(realfile,argv[c]); 313 | c2 = strlen(realfile); 314 | realfile[c2]='.'; 315 | realfile[c2+1]='a'; 316 | realfile[c2+2]='s'; 317 | realfile[c2+3]='m'; 318 | realfile[c2+4]='\0'; 319 | } 320 | } 321 | } 322 | if (!realfile) { 323 | Syntax(argv[0]); 324 | return 1; 325 | } 326 | if (!outfile) { 327 | outfile = new char[strlen(realfile)+5]; 328 | willtolive=true; 329 | for (c=0; c < strlen(realfile) && willtolive; c++) { 330 | outfile[c]=realfile[c]; 331 | if (realfile[c]=='.') willtolive=false; 332 | } 333 | outfile[c]='b'; 334 | outfile[c+1]='i'; 335 | outfile[c+2]='n'; 336 | outfile[c+3]='\0'; 337 | } 338 | if (header && !headfile) { 339 | headfile = new char[strlen(realfile)+1]; 340 | willtolive=true; 341 | for (c=0; c < strlen(realfile) && willtolive; c++) { 342 | headfile[c]=realfile[c]; 343 | if (realfile[c]=='.') willtolive=false; 344 | } 345 | headfile[c]='h'; 346 | headfile[c+1]='\0'; 347 | } 348 | 349 | // Find the path of the assembler, where files will be looked for if 350 | // they are not found in the current dir. 351 | // "Pathfinder" :-) 352 | 353 | for (c=strlen(argv[0])-1; c > 0; c--) { 354 | if (argv[0][c]=='\\') break; 355 | } 356 | asmpath = new char[c+2]; 357 | for (c2=0; c2<=c; c2++) { 358 | asmpath[c2]=argv[0][c2]; 359 | } 360 | asmpath[c2]=0; 361 | 362 | // ******* END PROCESSING ARGUMENTS 363 | 364 | cout << "Preprocessing ."; 365 | if (PreProcessor(realfile)) return 2; 366 | 367 | cout << "Loading symbols . . .\n"; 368 | #ifdef DEBUG 369 | cout << "Counting symbols . . .\n"; 370 | #endif 371 | maxsymbols=CountSymbols(asmfile)+BUILTINSYMBOLS; 372 | SymbolList = new Symbol[maxsymbols]; 373 | if (!SymbolList) { 374 | cout << "Out of memory.\n"; 375 | return 1; 376 | } 377 | 378 | InitRegs(); // Load the basic symbols (register names) 379 | if (LoadSymbols(asmfile)) {cout << "Fatal error, assembly must halt.\n"; return 2;} 380 | 381 | cout << "Assembling " << realfile << " to " << outfile << " . . .\n"; 382 | 383 | // ************ MAIN ASSEMBLY LOOP **************** 384 | // Once per pass. 385 | do { 386 | 387 | outhandle = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); 388 | if (outhandle < 0) { 389 | cout << "Error opening output file.\n"; 390 | return 1; 391 | } 392 | close(outhandle); 393 | outhandle = open(outfile, O_RDWR|O_EXCL, S_IREAD|S_IWRITE); 394 | 395 | if (outhandle < 0) { 396 | cout << "Error opening output file.\n"; 397 | return 1; 398 | } 399 | 400 | // A ROM needs a header. 401 | if (romout) { 402 | headhandle = open("header", O_RDONLY|O_EXCL, S_IREAD); 403 | if (headhandle <= 0) { 404 | 405 | pathfile=new char[strlen(asmpath)+7]; 406 | for (c=0; c < strlen(asmpath); c++) { 407 | pathfile[c]=asmpath[c]; 408 | } 409 | pathfile[c]='h'; pathfile[c+1]='e'; pathfile[c+2]='a'; 410 | pathfile[c+3]='d'; pathfile[c+4]='e'; pathfile[c+5]='r'; 411 | pathfile[c+6]=0; 412 | headhandle = open(pathfile, O_RDONLY|O_EXCL, S_IREAD); 413 | delete [] pathfile; 414 | if (headhandle <= 0) {cout << "Error opening N64 header.\n"; return 1;} 415 | } 416 | 417 | temp = new unsigned char [4096]; 418 | if (temp) { 419 | read(headhandle,temp,4096); 420 | write(outhandle,temp,4096); 421 | delete [] temp; 422 | } else { 423 | for (c=0; c < 4096; c++) { 424 | read(headhandle,&smalltemp,1); 425 | write(outhandle,&smalltemp,1); 426 | } 427 | } 428 | close(headhandle); 429 | } 430 | 431 | // Actual assembly begins 432 | cout << "Pass " << numpasses << " . . .\n"; 433 | 434 | program_counter=0; 435 | symbolcertain=true; 436 | pccertain=true; 437 | lastnumuncertain=numuncertain; 438 | numuncertain=0; 439 | line_count=1; 440 | if (AssembleFile(asmfile, outhandle, line_count, program_counter, fatalerror)) return 1; 441 | #ifdef DEBUG 442 | cout << "Total # uncertain: " << numuncertain << '\n'; 443 | #endif 444 | if (numpasses==1) lastnumuncertain=numuncertain+1; 445 | numpasses++; 446 | if (numuncertain >= lastnumuncertain) {cout << "Error: Irresolvable uncertainty.\nFatal error, assembly must halt.\n"; return 2;} 447 | 448 | program_counter = lseek(outhandle,0,SEEK_CUR);// gives offset of last 449 | // byte, size of file 450 | // Make a ROM image. 451 | if (romout && !fatalerror && symbolcertain && pccertain) { 452 | cout << "Extending " << outfile << " from " << program_counter << " bytes to "; 453 | cout << (((program_counter/2097152)+1)*2097152) << " bytes . . .\n"; 454 | program_counter=lseek(outhandle,(((program_counter/2097152)+1)*2097152)-1, SEEK_SET)+1; 455 | smalltemp=0; 456 | write(outhandle, &smalltemp, 1); 457 | cout << "Calculating checksum . . .\n"; 458 | if(crc(outhandle)) return 1; 459 | } 460 | 461 | close(outhandle); 462 | } while ((!symbolcertain || !pccertain) && !fatalerror); 463 | // ********** END OF MAIN ASSEMBLY LOOP ****************8 464 | 465 | if (header && !fatalerror) { 466 | cout << "Creating header " << headfile << " . . .\n"; 467 | ofstream headerfile(headfile); 468 | headerfile.flags(ios::hex|ios::showbase); 469 | headerfile << "; U64ASM v" << VERSION << " header file.\n"; 470 | headerfile << " assert " << firstorg << '\n'; 471 | for (c=BUILTINSYMBOLS; c < maxsymbols; c++) { 472 | if (SymbolList[c].type == DTYPE_INTEGER && SymbolList[c].bexport) { 473 | headerfile << SymbolList[c].name << " equ " << SymbolList[c].value << '\n'; 474 | } 475 | } 476 | headerfile << " incbin \"" << outfile << "\"\n"; 477 | headerfile.close(); 478 | } 479 | 480 | cout << flush; 481 | if (!fatalerror) { 482 | float total_time = ((float)(gettime()-bios_start_time))/1000.0; 483 | cout << "\n" << line_count << " lines assembled in "; 484 | cout << total_time << " seconds into " << program_counter << " bytes.\n"; 485 | if (total_time==0) { 486 | cout << "That's really fast!\n"; 487 | } else { 488 | cout << "That's " << line_count/total_time << " lines or " << program_counter/total_time << " bytes per second.\n"; 489 | } 490 | if (errtot > 0) { 491 | cout << errtot << " warning" << (errtot==1?" ":"s ") << "generated.\n"; 492 | return 2; // 1 is for file errors and the like. 493 | //return 1; // not as big an error 494 | //return 2; // still an error 495 | } 496 | } else return 3; // an acutal assembly error 497 | 498 | #ifndef DEBUG 499 | unlink("temp.tmp"); 500 | unlink("temp2.tmp"); 501 | #endif 502 | 503 | return 0; 504 | } 505 | 506 | int AssembleFile(const char * rasmfile, int outhandle, unsigned long &line_count, 507 | unsigned long &program_counter, bool &fatalerror) { 508 | char instr[256], asmfile[256]; 509 | int errl,linecountfactor=1,c; 510 | this_line_count=0; 511 | 512 | strcpy(asmfile,rasmfile); 513 | 514 | ifstream input(asmfile); 515 | if (!input) { 516 | cout << "Error opening input file " << asmfile << ".\n"; 517 | return 1; 518 | } 519 | 520 | while (!input.eof() && !fatalerror) { 521 | 522 | this_line_count+=linecountfactor; 523 | input.getline(instr,255); 524 | 525 | #ifdef DEBUG 526 | cout << this_line_count << ": "; 527 | #endif 528 | 529 | if (strlen(instr)==254) {errl=Errhandler(ERR_LINE_TOO_LONG,this_line_count,asmfile,NULL);} 530 | else { 531 | if (instr[0]=='#') { 532 | errl=NO_ERROR; 533 | switch (instr[1]) { 534 | case 'e': 535 | #ifdef DEBUG 536 | cout << '\n'; 537 | #endif 538 | linecountfactor=0; 539 | break; 540 | case 's': 541 | #ifdef DEBUG 542 | cout << '\n'; 543 | #endif 544 | linecountfactor=1; 545 | break; 546 | case 'f': 547 | for (c=3; c < strlen(instr); c++) { 548 | asmfile[c-3]=instr[c]; 549 | } 550 | asmfile[c-3]=0; 551 | #ifdef DEBUG 552 | cout << "File name changed to: " << asmfile << '\n'; 553 | #endif 554 | break; 555 | case 'l': 556 | this_line_count=0; 557 | for (c=3; c < strlen(instr); c++) { 558 | this_line_count*=10; 559 | this_line_count += instr[c]-'0'; 560 | } 561 | #ifdef DEBUG 562 | cout << "Line count changed to: " << this_line_count << '\n'; 563 | #endif 564 | break; 565 | case 't': 566 | line_count=0; 567 | for (c=3; c < strlen(instr); c++) { 568 | line_count*=10; 569 | line_count+=instr[c]-'0'; 570 | } 571 | #ifdef DEBUG 572 | cout << "Total line count = " << line_count << '\n'; 573 | #endif 574 | break; 575 | default: 576 | errl=ERR_ILLEGAL_DIRECTIVE; 577 | } 578 | } else errl=Assemble(instr,outhandle,program_counter); 579 | errl=Errhandler(errl,this_line_count,asmfile,instr); 580 | } 581 | if (errl==FATAL_ERROR) { 582 | cout << "Fatal error, assembly must halt.\n"; 583 | fatalerror=true; 584 | } 585 | } 586 | input.close(); 587 | #ifdef DEBUG 588 | cout << "End of " << asmfile << '\n'; 589 | #endif 590 | return 0; 591 | } 592 | 593 | void Syntax(char * exename) { 594 | cout << "Syntax is:\n"; 595 | cout << exename << " [optional switches]\n"; 596 | cout << ": The file containing the assembly source. If no extension\n"; 597 | cout << " is used, .asm is assumed.\n"; 598 | cout << "-o: Use this switch to specify the name of the binary output\n"; 599 | cout << " file. If this switch is not used, the source file name,\n"; 600 | cout << " with a .bin extension, will be used. If no extension is\n"; 601 | cout << " used, .bin is assumed.\n"; 602 | cout << "-h
: Use this switch to cause the generation of a header file,\n"; 603 | cout << " which will be written with the values of any defined labels\n"; 604 | cout << " in the program and an incbin instruction at the bottom to\n"; 605 | cout << " include the assembled code. An assert statement is placed at"; 606 | cout << " the top of the file, causing the assembler to check that the"; 607 | cout << " preassembled code is at the proper location in the program.\n"; 608 | cout << " If no file name is given, the source file name with a .h\n"; 609 | cout << " extension is used. If no extension is given, .h is assumed.\n"; 610 | cout << "-r: This switch causes the output to be a complete, extended,\n"; 611 | cout << " checksummed N64 ROM image. The assembler expects the N64\n"; 612 | cout << " boot header to be in a file called \"header\" in either the\n"; 613 | cout << " same directory as the source or the assembler.\n"; 614 | cout << "-about or -credits Gives some information about the program and those who made\n"; 615 | cout << " it possible.\n"; 616 | return; 617 | } 618 | 619 | // Reading this code constitutes a federal offense. 620 | // This source code is the exclusive property of Halley's Comet Software 621 | // (c) 2002 622 | 623 | // Written entirely by Adam Gashlin (a.k.a. themind) in edit.com, compiled 624 | // with Borland C++ and A LOT OF PATIENCE. 625 | 626 | // HalleysCometSoftware@hotmail.com 627 | -------------------------------------------------------------------------------- /u64asm-src/opcode.h: -------------------------------------------------------------------------------- 1 | // opcode.h: standardized instruction->opcode engine 2 | // Forgive me for using 'parameter' and 'operand' interchangably, 3 | // I usually mean parameter. 4 | 5 | #define PTYPE_MASK 0xC0 6 | #define PTYPE_INTEGER 0x00 7 | #define PTYPE_REGISTER 0x40 8 | #define PTYPE_IREGISTER 0x80 9 | #define PTYPE_CREGISTER 0xC0 10 | 11 | #define PSIZE_MASK 0x38 12 | #define PSIZE_5 0x00 13 | #define PSIZE_10 0x08 14 | #define PSIZE_16 0x10 15 | #define PSIZE_20 0x18 16 | #define PSIZE_26 0x20 17 | 18 | #define PLOC_MASK 0x07 19 | #define PLOC_0 0x00 20 | #define PLOC_6 0x01 21 | #define PLOC_11 0x02 22 | #define PLOC_16 0x03 23 | #define PLOC_21 0x04 24 | 25 | // Some common parameters 26 | #define POFFSET PSIZE_16|PLOC_0|PTYPE_INTEGER 27 | #define PTARGET PSIZE_26|PLOC_0|PTYPE_INTEGER 28 | #define PIMMEDIATE PSIZE_16|PLOC_0|PTYPE_INTEGER 29 | #define PBASE PSIZE_5|PLOC_21|PTYPE_IREGISTER 30 | #define PRS PSIZE_5|PLOC_21|PTYPE_REGISTER 31 | #define PRT PSIZE_5|PLOC_16|PTYPE_REGISTER 32 | #define PRD PSIZE_5|PLOC_11|PTYPE_REGISTER 33 | #define PSA PSIZE_5|PLOC_6|PTYPE_INTEGER 34 | #define PFS PSIZE_5|PLOC_11|PTYPE_CREGISTER 35 | #define PFD PSIZE_5|PLOC_6|PTYPE_CREGISTER 36 | #define PFT PSIZE_5|PLOC_16|PTYPE_CREGISTER 37 | #define PCODE PSIZE_20|PLOC_6|PTYPE_INTEGER 38 | #define PSHORTCODE PSIZE_10|PLOC_6|PTYPE_INTEGER 39 | #define P_OP PSIZE_5|PLOC_16|PTYPE_INTEGER 40 | 41 | // Flags 42 | #define FLS 0x01 43 | #define FARITH 0x02 44 | #define FBRK 0x04 45 | #define FALLOC 0x08 46 | #define FJMP 0x10 47 | #define FBRANCH 0x20 48 | #define FSPECIAL 0x40 49 | #define FIMM 0x80 50 | 51 | int db_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 52 | int dh_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 53 | int dw_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 54 | int dcb_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 55 | int dch_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 56 | int dcw_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 57 | int incbin_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 58 | int org_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 59 | int equ_fn(const char *, int); 60 | int equr_fn(const char *, int); 61 | int equne_fn(const char *, int); 62 | int li_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 63 | int la_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 64 | int move_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 65 | int obj_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 66 | int objend_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 67 | int report_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 68 | int offset_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 69 | int assert_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 70 | int watch_fn(char **, unsigned long *, int *, int, unsigned long &, bool, int); 71 | 72 | struct instruction 73 | { 74 | const char * name; 75 | unsigned long base; 76 | int numparams; 77 | char paramtypes[3]; 78 | char flags; 79 | }; 80 | 81 | #define LOADSTORE(name,base) {name,(unsigned long)base<<26,3,{PRT,POFFSET,PBASE},FLS} 82 | #define ARITH(name,base) {name,base,3,{PRD,PRS,PRT},FARITH} 83 | #define IMMARITH(name,base) {name,(unsigned long)base<<26,3,{PRT,PRS,PIMMEDIATE},FARITH|FIMM} 84 | #define MULTDIV(name,base) {name,base,2,{PRS,PRT},0} 85 | #define SHIFT(name,base) {name,base,3,{PRD,PRT,PSA},FARITH} 86 | #define SHIFTV(name,base) {name,base,3,{PRD,PRT,PRS},FARITH} 87 | #define SETON(name,base) {name,base,3,{PRD,PRS,PRT},0} 88 | #define IMMSETON(name,base) {name,(unsigned long)base<<26,3,{PRT,PRS,PIMMEDIATE},FIMM} 89 | #define BRANCHEQ(name,base) {name,(unsigned long)base<<26,3,{PRS,PRT,POFFSET},FIMM|FBRANCH} 90 | #define BRANCH(name,base) {name,base,2,{PRS,POFFSET},FIMM|FBRANCH} 91 | 92 | #define NUMINSTRUCTIONS 124 93 | const instruction instruct[NUMINSTRUCTIONS] = { 94 | LOADSTORE("lb ",32),LOADSTORE("lbu ",36),LOADSTORE("ld ",55), 95 | LOADSTORE("ldl ",26),LOADSTORE("ldr ",27),LOADSTORE("lh ",33), 96 | LOADSTORE("lhu ",37),LOADSTORE("ll ",48),LOADSTORE("lld ",52), 97 | LOADSTORE("lw ",35),LOADSTORE("lwl ",34),LOADSTORE("lwr ",38), 98 | LOADSTORE("lwu ",39),LOADSTORE("sb ",40),LOADSTORE("sc ",56), 99 | LOADSTORE("scd ",60),LOADSTORE("sd ",63),LOADSTORE("sdl ",44), 100 | LOADSTORE("sdr ",45),LOADSTORE("sh ",41),LOADSTORE("sw ",43), 101 | LOADSTORE("swl ",42),LOADSTORE("swr ",46),{"sync ",15,0,{0},0}, 102 | ARITH("add ",32),IMMARITH("addi ",8),IMMARITH("addiu ",9), 103 | ARITH("addu ",33),ARITH("and ",36),IMMARITH("andi ",12), 104 | ARITH("dadd ",44),IMMARITH("daddi ",24),IMMARITH("daddiu ",25), 105 | ARITH("daddu ",45),MULTDIV("ddiv ",30),MULTDIV("div ",26), 106 | MULTDIV("divu ",27),MULTDIV("dmult ",28),MULTDIV("dmultu ",29), 107 | SHIFT("dsll ",56),SHIFT("dsll32 ",60),SHIFTV("dsllv ",20), 108 | SHIFT("dsra ",59),SHIFT("dsra32 ",63),SHIFTV("dsrav ",23), 109 | SHIFT("dsrl ",58),SHIFT("dsrl32 ",62),SHIFTV("dsrlv ",22), 110 | ARITH("dsub ",46),ARITH("dsubu ",47),{"lui ",(unsigned long)15<<26,2,{PRT,PIMMEDIATE},0}, // immediate removed because this will not be sign extended 111 | {"mfhi ",16,1,{PRD},0},{"mflo ",18,1,{PRD},0},{"mthi ",17,1,{PRS},0}, 112 | {"mtlo ",19,1,{PRS},0},MULTDIV("mult ",24),MULTDIV("multu ",25), 113 | ARITH("nor ",39),ARITH("or ",37),IMMARITH("ori ",13), 114 | SHIFT("sll ",0),SHIFTV("sllv ",4),SETON("slt ",42), 115 | IMMSETON("slti ",10),IMMSETON("sltiu ",11),SETON("sltu ",43), 116 | SHIFT("sra ",3),SHIFTV("srav ",7),SHIFT("srl ",2), 117 | SHIFTV("srlv ",6),ARITH("sub ",34),ARITH("subu ",35), 118 | ARITH("xor ",38),IMMARITH("xori ",14), // 74 instructions 119 | BRANCHEQ("beq ",4),BRANCHEQ("beql ",20), 120 | BRANCH("bgez ",(unsigned long)1<<26|(unsigned long)1<<16), 121 | BRANCH("bgezal ",(unsigned long)1<<26|(unsigned long)17<<16), 122 | BRANCH("bgezall ",(unsigned long)1<<26|(unsigned long)19<<16), 123 | BRANCH("bgezl ",(unsigned long)1<<26|(unsigned long)3<<16), 124 | BRANCH("bgtz ",(unsigned long)7<<26), 125 | BRANCH("bgtzl ",(unsigned long)23<<26), 126 | BRANCH("blez ",(unsigned long)6<<26), 127 | BRANCH("blezl ",(unsigned long)22<<26), 128 | BRANCH("bltz ",(unsigned long)1<<26), 129 | BRANCH("bltzal ",(unsigned long)1<<26|(unsigned long)16<<16), 130 | BRANCH("bltzall ",(unsigned long)1<<26|(unsigned long)18<<16), 131 | BRANCH("bltzl ",(unsigned long)1<<26|(unsigned long)2<<16), 132 | BRANCHEQ("bne ",5), 133 | BRANCHEQ("bnel ",21), 134 | {"j ",(unsigned long)2<<26,1,{PTARGET},FJMP}, 135 | {"jal ",(unsigned long)3<<26,1,{PTARGET},FJMP}, 136 | {"jalr ",9,2,{PRS,PRD},0}, 137 | {"jr ",8,1,{PRS},0}, 138 | {"db ",0,0,{0},FSPECIAL|FALLOC},{"dh ",1,0,{0},FSPECIAL|FALLOC}, 139 | {"dw ",2,0,{0},FSPECIAL|FALLOC},{"dcb ",3,2,{0},FSPECIAL}, 140 | {"dch ",4,2,{0},FSPECIAL},{"dcw ",5,2,{0},FSPECIAL}, 141 | {"incbin ",6,1,{0},FSPECIAL},{"org ",7,1,{0},FSPECIAL}, 142 | {"nop ",0,0,{0},0},{"li ",8,2,{0},FSPECIAL}, 143 | {"la ",9,2,{0},FSPECIAL},{"move ",10,2,{0},FSPECIAL}, 144 | {"break ",13,1,{PCODE},FBRK},{"syscall ",12,1,{PCODE},FBRK}, 145 | {"cache ",(unsigned long)47<<26,3,{P_OP,POFFSET,PBASE},0}, 146 | {"eret ",(unsigned long)0x42000018,0,{0},0}, 147 | {"mfc0 ",(unsigned long)0x10<<26,2,{PRT,PFS},0}, 148 | {"mtc0 ",((unsigned long)4<<21)|((unsigned long)0x10<<26),2,{PRT,PFS},0}, 149 | {"bnez ",(unsigned long)5<<26,2,{PRS,POFFSET},FBRANCH|FIMM}, 150 | {"bnezl ",(unsigned long)21<<26,2,{PRS,POFFSET},FBRANCH|FIMM}, 151 | {"beqz ",(unsigned long)4<<26,2,{PRS,POFFSET},FBRANCH|FIMM}, 152 | {"beqzl ",(unsigned long)20<<26,2,{PRS,POFFSET},FBRANCH|FIMM}, 153 | {"neg ",34,2,{PRD,PRT},FARITH},{"negu ",35,2,{PRD,PRT},FARITH}, 154 | {"obj ",11,1,{0},FSPECIAL},{"objend ",12,0,{0},FSPECIAL}, 155 | {"report ",13,0,{0},FSPECIAL},{"offset ",14,0,{0},FSPECIAL}, 156 | {"assert ",15,1,{0},FSPECIAL},{"watch ",16,1,{0},FSPECIAL} 157 | }; 158 | 159 | int (*specialfcn[])(char **,unsigned long *,int *,int,unsigned long &,bool,int) = { 160 | db_fn,dh_fn,dw_fn,dcb_fn,dch_fn,dcw_fn,incbin_fn,org_fn,li_fn,la_fn, 161 | move_fn,obj_fn,objend_fn,report_fn,offset_fn,assert_fn,watch_fn}; 162 | 163 | int AsmInstr(unsigned long &pc, const char * instr, int c, int outhandle) { 164 | unsigned long opcode; 165 | int icount,ic; // ic is the offset to start parameter searching at. 166 | int temp; 167 | bool found=false,watchfound=false; 168 | 169 | if (CheckEq(instr,c,"equ ",ic)) { 170 | return equ_fn(instr,ic); 171 | } 172 | if (CheckEq(instr,c,"equr ",ic)) { 173 | return equr_fn(instr,ic); 174 | } 175 | if (CheckEq(instr,c,"equne ",ic)) { 176 | return equne_fn(instr,ic); 177 | } 178 | 179 | for (icount=0; icount < NUMINSTRUCTIONS; icount++) { 180 | if (CheckEq(instr,c,instruct[icount].name,ic)) { 181 | found=true; 182 | break; 183 | } 184 | } 185 | if (!found) return ERR_UNKNOWN_INSTRUCTION; 186 | 187 | if (watchactive && pc==watchpoint) watchfound=true; 188 | 189 | char * pointers[64]; 190 | unsigned long values[64]; 191 | int types[64]; 192 | int err; 193 | int numops; 194 | bool certain=true; 195 | err=FindOperands(instr,OperandOffset(instr,ic),pointers,values,types,numops,((instruct[icount].flags&FALLOC)?63:instruct[icount].numparams)); 196 | if (err==ERR_UNCERTAIN) { 197 | if (!(instruct[icount].flags&FSPECIAL)) {pc+=4; return NO_ERROR;} 198 | certain=false; 199 | } else if (err) return err; 200 | if (instruct[icount].flags&FLS && numops < 3) { 201 | if (types[2] == DTYPE_IREGISTER) { 202 | types[3] = types[2]; 203 | values[3] = values[2]; 204 | types[2] = DTYPE_INTEGER; 205 | values[2] = 0; 206 | numops++; 207 | } else { 208 | types[3] = DTYPE_IREGISTER; 209 | values[3] = 0; 210 | numops++; 211 | } 212 | } 213 | if (instruct[icount].flags&FARITH && numops < instruct[icount].numparams) { 214 | if (instruct[icount].numparams==3) { 215 | types[3] = types[2]; 216 | values[3] = values[2]; 217 | } 218 | types[2] = types[1]; 219 | values[2] = values[1]; 220 | numops++; 221 | } 222 | 223 | opcode = instruct[icount].base; 224 | 225 | if (numops != instruct[icount].numparams && !(instruct[icount].flags&FALLOC) && !(instruct[icount].flags&FBRK && numops==0)) return ERR_MISSING_OPERAND; 226 | if (!(instruct[icount].flags&FSPECIAL)) { 227 | 228 | for (c=1; c < (numops+1); c++) { 229 | switch (instruct[icount].paramtypes[c-1]&PTYPE_MASK) { 230 | case PTYPE_INTEGER: 231 | if (types[c] != DTYPE_INTEGER) return ERR_BAD_OPERAND; 232 | if (instruct[icount].flags&FIMM) { 233 | if (instruct[icount].flags&FBRANCH) { 234 | values[c] = (signed long)(values[c] - (pc+4))/4; 235 | if (values[c] > 0x7fff && ((values[c]&0xffff8000) != 0xffff8000)) return ERR_BRANCH_RANGE; 236 | } /* else { 237 | if (values[c] > 0x7fff && ((values[c]&0xffff8000) != 0xffff8000)) return ERR_TOO_BIG_IMM; 238 | } */ 239 | } 240 | break; 241 | case PTYPE_REGISTER: 242 | if (types[c] != DTYPE_REGISTER) return ERR_BAD_OPERAND; 243 | break; 244 | case PTYPE_IREGISTER: 245 | if (types[c] != DTYPE_IREGISTER) return ERR_BAD_OPERAND; 246 | break; 247 | case PTYPE_CREGISTER: 248 | if (types[c] != DTYPE_CREGISTER) return ERR_BAD_OPERAND; 249 | break; 250 | default: 251 | return ERR_UNKNOWN_ERROR; 252 | } 253 | switch (instruct[icount].paramtypes[c-1]&PSIZE_MASK) { 254 | case PSIZE_5: 255 | values[c] &= 0x1f; 256 | break; 257 | case PSIZE_10: 258 | values[c] &= 0x3ff; 259 | break; 260 | case PSIZE_16: 261 | values[c] &= 0xffff; 262 | break; 263 | case PSIZE_20: 264 | values[c] &= 0xfffff; 265 | break; 266 | case PSIZE_26: 267 | if (instruct[icount].flags&FJMP) values[c] /= 4; 268 | values[c] &= 0x3ffffff; 269 | break; 270 | default: 271 | return ERR_UNKNOWN_ERROR; 272 | } 273 | switch (instruct[icount].paramtypes[c-1]&PLOC_MASK) { 274 | case PLOC_0: 275 | opcode |= values[c]; 276 | break; 277 | case PLOC_6: 278 | opcode |= values[c]<<6; 279 | break; 280 | case PLOC_11: 281 | opcode |= values[c]<<11; 282 | break; 283 | case PLOC_16: 284 | opcode |= values[c]<<16; 285 | break; 286 | case PLOC_21: 287 | opcode |= values[c]<<21; 288 | break; 289 | default: 290 | return ERR_UNKNOWN_ERROR; 291 | } 292 | } 293 | 294 | // put into big-endian format 295 | opcode = (opcode&0x000000ff)<<24 | (opcode&0x0000ff00)<<8 | (opcode&0x00ff0000)>>8 | (opcode&0xff000000)>>24; 296 | 297 | write(outhandle,&opcode,4); 298 | pc+=4; // doh! 299 | 300 | return watchfound?ERR_WATCH_FOUND:NO_ERROR; 301 | } else { 302 | temp=specialfcn[instruct[icount].base](pointers,values,types,numops,pc,certain,outhandle); 303 | if (temp) return temp; 304 | else if (watchfound) return ERR_WATCH_FOUND; 305 | return NO_ERROR; 306 | } 307 | } 308 | 309 | #define SHUTUP pointers=pointers; values=values; types=types; numops=numops; pc=pc, certain=certain, outhandle=outhandle; 310 | 311 | int db_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 312 | unsigned int c; 313 | 314 | if (numops == 0) return ERR_MISSING_OPERAND; 315 | for (c=1; c <= numops; c++) { 316 | if (types[c]==DTYPE_STRING) { 317 | if (pccertain && symbolcertain) write(outhandle,pointers[c],values[c]); 318 | pc+=values[c]; 319 | } else if (types[c]==DTYPE_INTEGER) { 320 | if (pccertain && symbolcertain) write(outhandle,values+c,1); 321 | pc++; 322 | } else return ERR_BAD_OPERAND; 323 | } 324 | SHUTUP 325 | return NO_ERROR; 326 | } 327 | 328 | // Halfword (2 bytes) 329 | int dh_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 330 | unsigned int c; 331 | 332 | if (numops == 0) return ERR_MISSING_OPERAND; 333 | for (c=1; c <= numops; c++) { 334 | if (types[c]==DTYPE_INTEGER) { 335 | if (pccertain && symbolcertain) { 336 | values[c] = ((values[c])&0xff00) >> 8 | ((values[c])&0x00ff) << 8; 337 | write(outhandle,&(values[c]),2); 338 | } 339 | pc+=2; 340 | } else return ERR_BAD_OPERAND; 341 | } 342 | SHUTUP 343 | return NO_ERROR; 344 | } 345 | 346 | // Word (4 bytes) 347 | int dw_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 348 | unsigned int c; 349 | 350 | if (numops == 0) return ERR_MISSING_OPERAND; 351 | for (c=1; c <= numops; c++) { 352 | if (types[c]==DTYPE_INTEGER) { 353 | if (pccertain && symbolcertain) { 354 | values[c] = (values[c]&0x000000ff)<<24 | (values[c]&0x0000ff00)<<8 | (values[c]&0x00ff0000)>>8 | (values[c]&0xff000000)>>24; 355 | write(outhandle,&(values[c]),4); 356 | } 357 | pc+=4; 358 | } else return ERR_BAD_OPERAND; 359 | } 360 | SHUTUP 361 | return NO_ERROR; 362 | } 363 | 364 | // Doubleword isn't possible right now, everything currently works with 365 | // 4 bytes so 8 bytes is out of the question. 366 | 367 | // Makes multiple bytes. dcb <# bytes>, 368 | int dcb_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 369 | unsigned long c; 370 | 371 | if (types[1]==DTYPE_INTEGER && types[2]==DTYPE_INTEGER) { 372 | if (!certain) { 373 | if (whenuncertain==1) { 374 | pccertain=false; 375 | } else { 376 | pc+=values[1]; 377 | } 378 | return NO_ERROR; 379 | } 380 | if (pccertain && symbolcertain) { 381 | for (c=0; c < values[1]; c++) { 382 | write(outhandle, values+2, 1); 383 | } 384 | } 385 | pc+=values[1]; 386 | if (values[1]==0) return ERR_ZERO_SIZE; 387 | } else return ERR_BAD_OPERAND; 388 | SHUTUP 389 | return NO_ERROR; 390 | } 391 | 392 | int dch_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 393 | unsigned long c; 394 | 395 | if (types[1]==DTYPE_INTEGER && types[2]==DTYPE_INTEGER) { 396 | if (!certain) { 397 | if (whenuncertain==1) { 398 | pccertain=false; 399 | } else { 400 | pc+=values[1]*2; 401 | } 402 | return NO_ERROR; 403 | } 404 | if (pccertain && symbolcertain) { 405 | for (c=0; c < values[1]; c++) { 406 | write(outhandle,(char *)(values+2)+1,1); 407 | write(outhandle,(char *)(values+2),1); 408 | } 409 | } 410 | pc+=values[1]*2; 411 | if (values[1]==0) return ERR_ZERO_SIZE; 412 | } else return ERR_BAD_OPERAND; 413 | SHUTUP 414 | return NO_ERROR; 415 | } 416 | 417 | int dcw_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 418 | unsigned long c; 419 | 420 | if (types[1]==DTYPE_INTEGER && types[2]==DTYPE_INTEGER) { 421 | if (!certain) { 422 | if (whenuncertain==1) { 423 | pccertain=false; 424 | } else { 425 | pc+=values[1]*4; 426 | } 427 | return NO_ERROR; 428 | } 429 | if (pccertain && symbolcertain) { 430 | for (c=0; c < values[1]; c++) { 431 | write(outhandle,(char *)(values+2)+3,1); 432 | write(outhandle,(char *)(values+2)+2,1); 433 | write(outhandle,(char *)(values+2)+1,1); 434 | write(outhandle,(char *)(values+2),1); 435 | } 436 | } 437 | pc+=values[1]*4; 438 | if (values[1]==0) return ERR_ZERO_SIZE; 439 | } else return ERR_BAD_OPERAND; 440 | SHUTUP 441 | return NO_ERROR; 442 | } 443 | 444 | int incbin_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 445 | unsigned long c,size; 446 | int inhandle; 447 | char * pathfile; 448 | unsigned char * temp = NULL; 449 | unsigned char smalltemp; 450 | 451 | if (types[1]==DTYPE_STRING) { 452 | pointers[1][values[1]] = 0; 453 | inhandle = open(pointers[1], O_RDONLY|O_EXCL, S_IREAD); 454 | if (inhandle <= 0) { 455 | 456 | pathfile=new char[strlen(asmpath)+strlen(pointers[1])+1]; 457 | for (c=0; c < strlen(asmpath); c++) { 458 | pathfile[c]=asmpath[c]; 459 | } 460 | for (c=strlen(asmpath); c < strlen(asmpath)+strlen(pointers[1]); c++) { 461 | pathfile[c]=pointers[1][c-strlen(asmpath)]; 462 | } 463 | pathfile[c]=0; 464 | inhandle = open(pathfile, O_RDONLY|O_EXCL, S_IREAD); 465 | delete [] pathfile; 466 | if (inhandle <= 0) {return ERR_FILE_ERROR;} 467 | } 468 | 469 | size = lseek(inhandle,0,SEEK_END); 470 | lseek(inhandle,0,SEEK_SET); 471 | 472 | if (pccertain && symbolcertain) { 473 | temp = new unsigned char [1024]; 474 | if (temp) { 475 | for (c=1024; c <= size; c+=1024) { 476 | read(inhandle,temp,1024); 477 | write(outhandle,temp,1024); 478 | } 479 | if (c!=size) { 480 | read(inhandle,temp,size-(c-1024)); 481 | write(outhandle,temp,size-(c-1024)); 482 | } 483 | delete [] temp; 484 | } else { 485 | for (c=0; c < size; c++) { 486 | read(inhandle,&smalltemp,1); 487 | write(outhandle,&smalltemp,1); 488 | } 489 | } 490 | } 491 | pc+=size; 492 | 493 | close(inhandle); 494 | } else return ERR_BAD_OPERAND; 495 | SHUTUP 496 | return NO_ERROR; 497 | } 498 | 499 | int org_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 500 | pccertain=true; 501 | 502 | if (!certain) {pccertain=false; return NO_ERROR;} 503 | if (types[1]==DTYPE_INTEGER) { 504 | pc=values[1]; 505 | if (isfirstorg) {firstorg=pc; isfirstorg=false;} 506 | if (inobj) return ERR_ORG_IN_OBJ; 507 | return NO_ERROR; 508 | } 509 | SHUTUP 510 | return ERR_BAD_OPERAND; 511 | } 512 | 513 | int equ_fn(const char *instr, int firstbyte) { 514 | char * pointers[2]; 515 | unsigned long values[2]; 516 | int types[2]; 517 | int numops; 518 | int err; 519 | 520 | int c,ic; 521 | bool equ_uncert=false; 522 | 523 | err=FindOperands(instr,firstbyte,pointers,values,types,numops,1); 524 | if (err==ERR_UNCERTAIN) {equ_uncert=true;} 525 | else if (err) return err; 526 | if (numops < 1) return ERR_MISSING_OPERAND; 527 | if (types[1]==DTYPE_INTEGER) { 528 | for (c=0; c < maxsymbols; c++) { 529 | if (CheckEq(instr,0,SymbolList[c].name,ic)) break; 530 | } 531 | SymbolList[c].value = values[1]; 532 | SymbolList[c].certain = !equ_uncert; 533 | SymbolList[c].bexport = true; 534 | return NO_ERROR; 535 | } 536 | 537 | return ERR_BAD_OPERAND; 538 | } 539 | 540 | int equr_fn(const char *instr, int firstbyte) { 541 | char * pointers[2]; 542 | unsigned long values[2]; 543 | int types[2]; 544 | int numops; 545 | int err; 546 | int c,ic; 547 | bool equr_uncert=false; 548 | 549 | err=FindOperands(instr,firstbyte,pointers,values,types,numops,1); 550 | if (err==ERR_UNCERTAIN) {equr_uncert=true;} 551 | else if (err) return err; 552 | if (numops < 1) return ERR_MISSING_OPERAND; 553 | if (types[1]==DTYPE_REGISTER || types[1]==DTYPE_CREGISTER || equr_uncert) { 554 | for (c=0; c < maxsymbols; c++) { 555 | if (CheckEq(instr,0,SymbolList[c].name,ic)) break; 556 | } 557 | SymbolList[c].value = values[1]; 558 | SymbolList[c].certain = !equr_uncert; 559 | SymbolList[c].type = types[1]; 560 | SymbolList[c].bexport = false; // equrs aren't bexported 561 | return NO_ERROR; 562 | } 563 | 564 | return ERR_BAD_OPERAND; 565 | } 566 | 567 | // ne=no bexport, keeps these declarations out of headers 568 | int equne_fn(const char *instr, int firstbyte) { 569 | char * pointers[2]; 570 | unsigned long values[2]; 571 | int types[2]; 572 | int numops; 573 | int err; 574 | 575 | int c,ic; 576 | bool equ_uncert=false; 577 | 578 | err=FindOperands(instr,firstbyte,pointers,values,types,numops,1); 579 | if (err==ERR_UNCERTAIN) {equ_uncert=true;} 580 | else if (err) return err; 581 | if (numops < 1) return ERR_MISSING_OPERAND; 582 | if (types[1]==DTYPE_INTEGER) { 583 | for (c=0; c < maxsymbols; c++) { 584 | if (CheckEq(instr,0,SymbolList[c].name,ic)) break; 585 | } 586 | SymbolList[c].value = values[1]; 587 | SymbolList[c].certain = !equ_uncert; 588 | SymbolList[c].bexport = false; 589 | return NO_ERROR; 590 | } 591 | 592 | return ERR_BAD_OPERAND; 593 | } 594 | 595 | // Used for 16-bit immediate values (halfwords) 596 | // or something that fits into one instruction 597 | int li_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 598 | unsigned long opcode,imm,reg; 599 | 600 | if (!symbolcertain || (types[1]==DTYPE_REGISTER && types[2]==DTYPE_INTEGER)) { 601 | if (pccertain && symbolcertain) { 602 | imm=values[2]; 603 | reg=values[1]; 604 | if (imm < 0x10000) { 605 | 606 | if (imm & 0x8000) { 607 | opcode=((unsigned long)13 << 26) | (reg << 16) | imm; 608 | } else { 609 | opcode=((unsigned long)9 << 26) | (reg << 16) | imm; 610 | } 611 | 612 | //opcode=((unsigned long)13 << 26) | (reg << 16) |imm; 613 | } else { 614 | /* 615 | if ((imm & 0xffff8000) != 0xffff8000) { 616 | return ERR_TOO_BIG_IMM; 617 | } else { 618 | */ 619 | // otherwise its just a signed value 620 | opcode=((unsigned long)9 << 26) | (reg << 16) | (imm & 0xffff); 621 | //} 622 | } 623 | opcode = (opcode&0x000000ff)<<24 | (opcode&0x0000ff00)<<8 | (opcode&0x00ff0000)>>8 | (opcode&0xff000000)>>24; 624 | write(outhandle,&opcode,4); 625 | } 626 | pc+=4; 627 | } else return ERR_BAD_OPERAND; 628 | SHUTUP 629 | return NO_ERROR; 630 | } 631 | 632 | // Used for 32-bit immediate values (words) 633 | int la_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 634 | unsigned long opcode=(unsigned long)15 << 26; 635 | 636 | if (!symbolcertain || (types[1]==DTYPE_REGISTER && types[2]==DTYPE_INTEGER)) { 637 | if (pccertain && symbolcertain) { 638 | opcode|=((values[2]&0xFFFF0000) >> 16) | (values[1] << 16); 639 | opcode = (opcode&0x000000ff)<<24 | (opcode&0x0000ff00)<<8 | (opcode&0x00ff0000)>>8 | (opcode&0xff000000)>>24; 640 | write(outhandle,&opcode,4); 641 | opcode=(unsigned long)13 << 26; 642 | opcode|=(values[2]&0xFFFF) | (values[1] << 16) | (values[1] << 21); 643 | opcode = (opcode&0x000000ff)<<24 | (opcode&0x0000ff00)<<8 | (opcode&0x00ff0000)>>8 | (opcode&0xff000000)>>24; 644 | write(outhandle,&opcode,4); 645 | } 646 | pc+=8; 647 | } else return ERR_BAD_OPERAND; 648 | SHUTUP 649 | return NO_ERROR; 650 | } 651 | 652 | int move_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 653 | unsigned long opcode=32; 654 | 655 | if (!symbolcertain || (types[1]==DTYPE_REGISTER && types[2]==DTYPE_REGISTER)) { 656 | if (pccertain && symbolcertain) { 657 | opcode|=(values[1] << 11) | (values[2] << 16); 658 | opcode = (opcode&0x000000ff)<<24 | (opcode&0x0000ff00)<<8 | (opcode&0x00ff0000)>>8 | (opcode&0xff000000)>>24; 659 | write(outhandle,&opcode,4); 660 | } 661 | pc+=4; 662 | } else return ERR_BAD_OPERAND; 663 | SHUTUP 664 | return NO_ERROR; 665 | } 666 | 667 | int obj_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 668 | objcert=pccertain; 669 | pccertain=true; 670 | 671 | if (inobj) return ERR_NEST_OBJ; 672 | if (!certain) {pccertain=false; return NO_ERROR;} 673 | if (types[1]==DTYPE_INTEGER) { 674 | objstart=pc; 675 | pc=values[1]; 676 | objinit=pc; 677 | inobj=true; 678 | return NO_ERROR; 679 | } 680 | SHUTUP 681 | return ERR_BAD_OPERAND; 682 | } 683 | int objend_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 684 | pccertain=objcert; 685 | 686 | if (!inobj) return ERR_OBJEND; 687 | pc=(pc-objinit)+objstart; 688 | inobj=false; 689 | SHUTUP 690 | return NO_ERROR; 691 | } 692 | 693 | int report_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 694 | if (pccertain && symbolcertain) { 695 | cout.flags(ios::hex|ios::showbase); 696 | cout << "PC = " << pc << ".\n"; 697 | cout.flags(ios::dec); 698 | } 699 | SHUTUP 700 | return NO_ERROR; 701 | } 702 | int offset_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 703 | if (pccertain && symbolcertain) { 704 | cout.flags(ios::hex|ios::showbase); 705 | cout << "Offset = " << lseek(outhandle,0,SEEK_CUR) << ".\n"; 706 | cout.flags(ios::dec); 707 | } 708 | SHUTUP 709 | return NO_ERROR; 710 | } 711 | int assert_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 712 | if (pccertain && symbolcertain) { 713 | if (types[1]==DTYPE_INTEGER) { 714 | if (pc!=values[1]) { 715 | cout.flags(ios::hex|ios::showbase); 716 | cout << "PC = " << pc << ".\n"; 717 | cout.flags(ios::dec); 718 | return ERR_ASSERT_FAIL; 719 | } 720 | } else return ERR_BAD_OPERAND; 721 | } 722 | SHUTUP 723 | return NO_ERROR; 724 | } 725 | int watch_fn(char ** pointers, unsigned long * values, int * types, int numops, unsigned long &pc, bool certain, int outhandle) { 726 | if (pccertain && symbolcertain) { 727 | if (types[1]==DTYPE_INTEGER) { 728 | watchpoint=values[1]; 729 | watchactive=true; 730 | } else return ERR_BAD_OPERAND; 731 | } 732 | SHUTUP 733 | return NO_ERROR; 734 | } 735 | --------------------------------------------------------------------------------