/* hitutils - The Sims HIT (dis)assembler and linker hitasm.c - Copyright (c) 2012 Niotso Project Author(s): Fatbag Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #define HITASM_HEADERS #include "hitutils.h" static uint8_t ObjectHeader[] = { 0x7F, 0x45, 0x4C, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 }; enum { txt, out, filecount }; typedef struct { uint8_t * Data; size_t Position; size_t Size; char * Name; } ByteWriterContext; static void bw_expand(ByteWriterContext *bwc){ void * ptr; if(bwc->Size > SIZE_MAX/2 || !(ptr = realloc(bwc->Data, bwc->Size<<=1))) Shutdown_M("%sCould not allocate memory for %s section.\n", "hitasm: Error: ", bwc->Name); bwc->Data = ptr; } static void bw_write32(ByteWriterContext *bwc, uint32_t value){ if(bwc->Size-bwc->Position < 4) bw_expand(bwc); write_uint32(bwc->Data+bwc->Position, value); bwc->Position += 4; } static void bw_write8(ByteWriterContext *bwc, uint8_t value){ if(bwc->Size-bwc->Position < 1) bw_expand(bwc); bwc->Data[bwc->Position] = value; bwc->Position += 1; } static void bw_write_memory(ByteWriterContext *bwc, const uint8_t *ptr, size_t size){ while(bwc->Size-bwc->Position < size) bw_expand(bwc); do bwc->Data[bwc->Position++] = *ptr++; while(--size); } static void bw_write_string(ByteWriterContext *bwc, const char *string){ do bw_write8(bwc, *string); while(*string++); } typedef struct { char * Token; size_t Line, NextLine; size_t Col, NextCol; int GaveLastToken; ByteReaderContext brc; } ParserContext; enum { TK_CROSSLINES = 1, CN_LABELONLY = 1 }; enum Sections { Text, SymbolTable, StringTable, RelocationTable, SectionCount }; static FILE *hFile = NULL; static char *path[filecount] = {NULL}; static uint8_t *data[filecount] = {NULL}; static ByteWriterContext Section[] = { {0,0,1024,"text"}, {0,0,1024,"symtab"}, {0,0,1024,"strtab"}, {0,0,1024,".rel.text"} }; static uint8_t * add_symbol(const char * Name){ bw_write32(&Section[SymbolTable], Section[StringTable].Position); bw_write32(&Section[SymbolTable], 0); bw_write32(&Section[SymbolTable], 0); bw_write32(&Section[SymbolTable], 18); bw_write_string(&Section[StringTable], Name); return Section[SymbolTable].Data + Section[SymbolTable].Position - 16; } static uint8_t * find_symbol_by_name(const char * Name, uint32_t * SymbolIndex){ uint32_t p; for(p=48; p>4; return Section[SymbolTable].Data + p; } } if(SymbolIndex) *SymbolIndex = p>>4; return NULL; } static __inline int whitespace(const uint8_t *Data){ return (Data[0] == ' ' || Data[0] == '\t' || Data[0] == '\r' || Data[0] == '\n' || Data[0] == '\0'); } static __inline int comment(const uint8_t *Data){ return (Data[0] == ';' || (Data[0] == '#' && whitespace(Data+1))); } static int parser_next_token(ParserContext *pc, int CrossLines){ if(!pc->brc.Size) return 0; if(pc->GaveLastToken){ pc->GaveLastToken = 0; /* find the start of the next token */ while(1){ if(whitespace(pc->brc.Data)){ if(pc->brc.Data[0] == '\n'){ pc->NextLine++; pc->NextCol = 1; }else if(pc->brc.Data[0] == '\t'){ pc->NextCol += 4 - ((pc->NextCol-1)%4); }else{ pc->NextCol++; } }else if(comment(pc->brc.Data)){ /* skip to the end of the line */ pc->NextLine++; pc->NextCol = 1; do { if(!--pc->brc.Size) return 0; pc->brc.Data++; } while(pc->brc.Data[0] != '\n'); }else break; if(!--pc->brc.Size) return 0; pc->brc.Data++; } } if(!CrossLines && pc->Line != pc->NextLine) return 0; pc->Token = (char*)pc->brc.Data; pc->Line = pc->NextLine; pc->Col = pc->NextCol++; pc->GaveLastToken = 1; /* mark the end of this token with a null character */ while(1){ if(!--pc->brc.Size) return 1; pc->brc.Data++; if(whitespace(pc->brc.Data)){ if(pc->brc.Data[0] == '\n'){ pc->NextLine++; pc->NextCol = 1; }else if(pc->brc.Data[0] == '\t'){ pc->NextCol += 4 - ((pc->NextCol-1)%4); }else{ pc->NextCol++; } break; }else if(comment(pc->brc.Data)){ /* skip to the end of the line */ pc->NextLine++; pc->NextCol = 1; do { if(!--pc->brc.Size) return 1; pc->brc.Data++; } while(pc->brc.Data[0] != '\n'); break; }else pc->NextCol++; } pc->brc.Size--; pc->brc.Data[0] = '\0'; pc->brc.Data++; return 1; } static __inline void verify_eol(ParserContext *pc){ if(parser_next_token(pc, 0)) Shutdown_M("%s:%u:%u: error: expected newline before '%s'\n", path[txt], pc->Line, pc->Col, pc->Token); } static void verify_name(const ParserContext *pc, const char *thistoken, const char *nexttoken){ const char *Name; for(Name = pc->Token; *Name; Name++) if((Name == pc->Token || *Name < '0' || *Name > '9') && (*Name < 'A' || *Name > 'Z') && (*Name < '_' || *Name > '_') && (*Name < 'a' || *Name > 'z')) Shutdown_M("%s:%u:%u: error: expected %s before '%c'\n", path[txt], pc->Line, pc->Col, (Name == pc->Token) ? thistoken : nexttoken, *Name); } static __inline void parser_add_symbol(const ParserContext *pc){ uint8_t * Symbol; verify_name(pc, "label or integer constant", "newline"); /* we're using the ELF st_size field (offset 8) to hold the line number temporarily */ Symbol = find_symbol_by_name(pc->Token, NULL); if(Symbol){ if(read_uint32(Symbol+4)) Shutdown_M("%s:%u:%u: error: label '%s' redefined (previous definition at %s:%u:1)\n", path[txt], pc->Line, pc->Col, pc->Token, path[txt], read_uint32(Symbol+8)); }else Symbol = add_symbol(pc->Token); write_uint32(Symbol+4, Section[Text].Position); write_uint32(Symbol+8, pc->Line); } static __inline void parser_add_reference(const ParserContext *pc){ uint32_t SymbolIndex; verify_name(pc, "label", "operand or newline"); if(!find_symbol_by_name(pc->Token, &SymbolIndex)) add_symbol(pc->Token); bw_write32(&Section[RelocationTable], Section[Text].Position); bw_write32(&Section[RelocationTable], (SymbolIndex<<8)|0x02); } static uint32_t read_integer(const ParserContext *pc, uint32_t maxval){ unsigned long value; char * endptr; if(pc->Token[0] == '-') Shutdown_M("%s:%u:%u: error: integer constant '%s' may not be negative\n", path[txt], pc->Line, pc->Col, pc->Token); if(pc->Token[0] < '0' || pc->Token[0] > '9') Shutdown_M("%s:%u:%u: error: expected integer constant before '%s'\n", path[txt], pc->Line, pc->Col, pc->Token); if(pc->Token[0] == '0' && (pc->Token[1] == 'x' || pc->Token[1] == '0' || pc->Token[1] == 'o')) Shutdown_M("%s:%u:%u: error: illegal %s prefix '%s%c' on integer constant '%s'\n", path[txt], pc->Line, pc->Col, pc->Token[1] == 'x' ? "hexadecimal" : "octal", pc->Token[1] != '0' ? "0" : "", pc->Token[1], pc->Token); value = strtoul(pc->Token, &endptr, 10); if(*endptr == '.') Shutdown_M("%s:%u:%u: error: illegal floating point constant '%s'\n", path[txt], pc->Line, pc->Col, endptr, pc->Token); if(*endptr != '\0') Shutdown_M("%s:%u:%u: error: illegal suffix '%s' on integer constant '%s'\n", path[txt], pc->Line, pc->Col, endptr, pc->Token); if(value > (unsigned long) maxval || errno == ERANGE) Shutdown_M("%s:%u:%u: error: integer constant '%s' is out of range\n", path[txt], pc->Line, pc->Col, pc->Token); return (uint32_t)value; } static uint32_t read_constant(ParserContext *pc, int Type, uint32_t maxval){ unsigned i; if(pc->Token[0] == '#') /* note that the tokens "#" and "" will never be returned by the parser */ pc->Token++; if((pc->Token[0] >= '0' && pc->Token[0] <= '9') || pc->Token[0] == '-' || pc->Token[0] == '.'){ if(Type == CN_LABELONLY) Shutdown_M("%s:%u:%u: error: explicit address '%s' is forbidden\n", path[txt], pc->Line, pc->Col, pc->Token); return read_integer(pc, maxval); } verify_name(pc, "constant or label", "operand or newline"); for(i=0; iToken)){ if(Constants[i].Value > maxval) Shutdown_M("%s:%u:%u: error: integer constant '%s' (%u) is out of range\n", path[txt], pc->Line, pc->Col, pc->Token, Constants[i].Value); return Constants[i].Value; } } if(maxval != 0xFFFFFFFF) Shutdown_M("%s:%u:%u: error: address referenced by label '%s' is out of range\n", path[txt], pc->Line, pc->Col, pc->Token); parser_add_reference(pc); return 0; } static __inline uint8_t read_instruction(const ParserContext *pc, uint32_t *operands){ uint8_t i; verify_name(pc, "instruction", "operand or newline"); for(i=0; iToken, Instructions[i].Name)){ *operands = Instructions[i].Operands; return i+1; } } return 0; } static __inline uint8_t read_variable(const ParserContext *pc, const variable_t *Variables, size_t VariableCount){ unsigned i; verify_name(pc, "variable", "operand or newline"); for(i=0; iToken)) return Variables[i].Value; Shutdown_M("%s:%u:%u: error: unrecognized variable '%s'\n", path[txt], pc->Line, pc->Col, pc->Token); return 0; } static void Shutdown(){ unsigned i; for(i=0; i.\n" "hitutils is maintained by the Niotso project.\n" "Home page: \n"); return 0; } for(i=1; i<(unsigned)argc-1; i++){ if(!strcmp(argv[i], "-ts1")) SimsVersion = VERSION_TS1; else if(!strcmp(argv[i], "-tso")) SimsVersion = VERSION_TSO; else if(!strcmp(argv[i], "-f")) overwrite = 1; else if(i != (unsigned)argc-2){ if(!strcmp(argv[i], "-o")) path[out] = argv[++i]; else break; } else break; } path[txt] = argv[i]; for(i=0; i= '0' && pc.Token[0] <= '9') || pc.Token[0] == '-' || pc.Token[0] == '.' || pc.Token[0] == '#' || !(opcode = read_instruction(&pc, &operands))){ /* declare bytes (db and dd pseudo-instructions) */ do { if(pc.Token[0] != '#') bw_write8(&Section[Text], read_integer(&pc, 0x000000FF)); else bw_write32(&Section[Text], read_constant(&pc, 0, 0xFFFFFFFF)); } while(parser_next_token(&pc, 0)); continue; }else{ const char * InstructionName = pc.Token; bw_write8(&Section[Text], opcode); for(i=0; (operands >>= 4) != 0; i++){ int type = operands & 15; const char *position[] = {"one","two","three","four"}; if(!parser_next_token(&pc, 0)){ int j; for(j=i+1; (operands >>= 4) != 0; j++); Shutdown_M("%s:%u:%u: error: instruction '%s' wants %s operands but only %s supplied\n", path[txt], pc.Line, pc.Col, pc.Token, InstructionName, position[j], position[i]); } if(type == o_byte) bw_write8(&Section[Text], read_constant(&pc, 0, 0x000000FF)); else if(type == o_dword) bw_write32(&Section[Text], read_constant(&pc, 0, 0xFFFFFFFF)); else if(type == o_address) bw_write32(&Section[Text], read_constant(&pc, CN_LABELONLY, 0xFFFFFFFF)); else if(type == o_variable) bw_write8(&Section[Text], read_variable(&pc, Variables, VariableCount)); else if(type == o_jump){ /* TODO: Change this */ bw_write32(&Section[Text], read_constant(&pc, CN_LABELONLY, 0xFFFFFFFF)); } } } } verify_eol(&pc); } if(InBinary) Shutdown_M("%s:%u:%u: error: expected ']' for end of BINARY section before end of file\n", path[txt], pc.Line, pc.Col); /**** ** Prepare and write out the ELF object header and all sections */ for(i=48+8; i