Permalink
Cannot retrieve contributors at this time
1844 lines (1620 sloc)
50.4 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <ctype.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "defines.h" | |
| #include "memory.h" | |
| #include "write.h" | |
| #include "files.h" | |
| #include "analyze.h" | |
| extern struct reference *reference_first, *reference_last; | |
| extern struct label *labels_first, *labels_last; | |
| extern struct object_file *obj_first, *obj_last, *obj_tmp; | |
| extern struct section *sec_first, *sec_last, *sec_hd_first, sec_hd_last; | |
| extern struct stack *stacks_first, *stacks_last; | |
| extern struct slot slots[256]; | |
| extern unsigned char *rom, *rom_usage; | |
| extern unsigned char *file_header, *file_footer; | |
| extern int romsize, rombanks, banksize, verbose_mode, section_overwrite, symbol_mode; | |
| extern int pc_bank, pc_full, pc_slot, pc_slot_max, snes_rom_mode; | |
| extern int file_header_size, file_footer_size, *bankaddress, *banks; | |
| extern int memory_file_id, memory_file_id_source, memory_line_number, output_mode; | |
| extern int program_start, program_end, cpu_65816, snes_mode, smc_status; | |
| extern int snes_sramsize; | |
| #if 0 | |
| static int _sections_sort(const void *a, const void *b) { | |
| if ((*((struct section **)a))->size < (*((struct section **)b))->size) | |
| /* struct section* aa = *((struct section**)a); | |
| struct section* bb = *((struct section**)b); | |
| printf("comparing %s and %s\n",aa->name,bb->name); | |
| return -strcmp(aa->name, bb->name); */ | |
| return 1; | |
| return -1; | |
| } | |
| #endif | |
| static int _sections_sortalpha(const void *a, const void *b) { | |
| struct section* aa = *((struct section**)a); | |
| struct section* bb = *((struct section**)b); | |
| //printf("comparing %s and %s\n",aa->name,bb->name); | |
| #if 0 | |
| if(!aa->name) fprintf(stderr,"no aname\n"); | |
| if(!bb->name) fprintf(stderr,"no bname\n"); | |
| if(strcmp(aa->name, bb->name) == 0) return 0; | |
| if(aa->size < bb->size) return 1; | |
| return -1; | |
| #else | |
| int s = -strcmp(aa->name, bb->name); | |
| /* identical names -> sort by file id, lowest first */ | |
| if (!s) | |
| return (aa->file_id - bb->file_id); | |
| else | |
| return s; | |
| #endif | |
| } | |
| int smc_create_and_write(FILE *f) { | |
| int i; | |
| if (f == NULL) | |
| return FAILED; | |
| if (output_mode != OUTPUT_ROM) | |
| return FAILED; | |
| i = romsize/(8*1024); | |
| /* low byte of 8KB page count */ | |
| fprintf(f, "%c", i & 0xFF); | |
| /* high byte of 8KB page count */ | |
| fprintf(f, "%c", (i>>8) & 0xFF); | |
| /* emulation mode select (?) */ | |
| i = 0; | |
| if (snes_rom_mode == SNES_ROM_MODE_HIROM) | |
| i |= (1<<5) | (1<<4); | |
| i |= (snes_sramsize ^ 3) << 2; | |
| fprintf(f, "%c", i); | |
| /* bytes 3 to 7 are reserved, should be zero */ | |
| fprintf(f, "%c" ,0); | |
| fprintf(f, "%c" ,0); | |
| fprintf(f, "%c" ,0); | |
| fprintf(f, "%c" ,0); | |
| fprintf(f, "%c" ,0); | |
| /* bytes 8, 9 = ID Code = 0xAA, 0xBB */ | |
| fprintf(f, "%c", 0xAA); | |
| fprintf(f, "%c", 0xBB); | |
| /* 0x04 = SWC & SMC Game */ | |
| fprintf(f, "%c", 0x04); | |
| /* the rest of the header is zeroes */ | |
| for (i = 0; i < 512-11; i++) | |
| fprintf(f, "%c", 0); | |
| return SUCCEEDED; | |
| } | |
| void lookforsections(struct section** sa, int sn, int p, int* lookforsize, int* totalsections) | |
| { | |
| #if 1 | |
| struct section* s = sa[p]; | |
| *totalsections = 1; | |
| //fprintf(stderr,"section %s size %d\n",s->name,s->size); | |
| *lookforsize = s->size; | |
| int pp = p+1; | |
| if(strncmp(s->name, ".text", 5) == 0) return; | |
| while(pp < sn && !strcmp(s->name, sa[pp]->name) && s->status == sa[pp]->status) { | |
| //fprintf(stderr,"same section %s (%d and %d); size %d\n", s->name, p, pp, sa[pp]->size); | |
| *lookforsize += sa[pp]->size; | |
| pp++; (*totalsections)++; | |
| } | |
| //fprintf(stderr,"total size %d in %d sections\n", *lookforsize, *totalsections); | |
| #else | |
| *lookforsize = sa[p]->size; | |
| *totalsections = 1; | |
| #endif | |
| } | |
| int insert_sections(void) { | |
| struct section *s, **sa; | |
| int d, f, i, j, x, t, q, sn, p; | |
| char *ram_slots[256], *c; | |
| /* initialize ram slots */ | |
| for (i = 0; i < 256; i++) | |
| ram_slots[i] = NULL; | |
| /* find all touched slots */ | |
| s = sec_first; | |
| while (s != NULL) { | |
| if (s->status == SECTION_STATUS_RAM && ram_slots[s->slot] == NULL) { | |
| ram_slots[s->slot] = malloc(slots[s->slot].size); | |
| if (ram_slots[s->slot] == NULL) { | |
| fprintf(stderr, "INSERT_SECTIONS: Out of memory error.\n"); | |
| return FAILED; | |
| } | |
| memset(ram_slots[s->slot], 0, slots[s->slot].size); | |
| } | |
| s = s->next; | |
| } | |
| /* count the sections */ | |
| i = 0; | |
| s = sec_first; | |
| while (s != NULL) { | |
| /* no references - skip it */ | |
| if (s->alive == YES) | |
| i++; | |
| s = s->next; | |
| } | |
| sn = i; | |
| if (sn == 0) | |
| return SUCCEEDED; | |
| sa = malloc(sizeof(struct section *) * sn); | |
| if (sa == NULL) { | |
| fprintf(stderr, "INSERT_SECTIONS: Out of memory error.\n"); | |
| return FAILED; | |
| } | |
| /* insert the sections into an array for sorting */ | |
| i = 0; | |
| s = sec_first; | |
| while (s != NULL) { | |
| /* no references - skip it */ | |
| if (s->alive == YES) | |
| sa[i++] = s; | |
| s = s->next; | |
| } | |
| /* sort the sections by size, biggest first */ | |
| qsort(sa, sn, sizeof(struct section *), _sections_sortalpha); //_sections_sort); | |
| #if 0 | |
| for(i=0;i<sn;i++) | |
| { | |
| printf("s %s id %d\n",sa[i]->name,sa[i]->file_id); | |
| } | |
| #endif | |
| /* print the sizes (DEBUG) */ | |
| /* | |
| for (d = 0; d < i; d++) | |
| fprintf(stderr, "SIZE: %d\n", sa[d]->size); | |
| */ | |
| int lookforsize; /* total size of the section group */ | |
| int totalsections; /* number of sections in this group */ | |
| /* ram sections */ | |
| p = 0; | |
| while (p < sn) { | |
| s = sa[p]; | |
| totalsections = 1; | |
| /* search for free space */ | |
| if (s->status == SECTION_STATUS_RAM) { | |
| /* find out if there is more than one section with this name and set | |
| lookforsize and totalsections accordingly */ | |
| lookforsections(sa, sn, p, &lookforsize, &totalsections); | |
| c = ram_slots[s->slot]; | |
| i = slots[s->slot].size; | |
| t = 0; | |
| for (x = 0; x < i; x++, c++) { | |
| if (*c == 0) { | |
| for (q = 0; x < i && q < lookforsize; x++, q++, c++) { | |
| if (*c != 0) | |
| break; | |
| } | |
| if (q == lookforsize) { | |
| t = 1; | |
| break; | |
| } | |
| } | |
| } | |
| if (t == 0) { | |
| fprintf(stderr, "INSERT_SECTIONS: No room for RAM section \"%s\" (%d bytes) in slot %d.\n", s->name, lookforsize, s->slot); | |
| fprintf(stderr, "c-s %d lfs %d tts %d\n", c-ram_slots[s->slot], lookforsize, totalsections); | |
| return FAILED; | |
| } | |
| /* mark as used */ | |
| c = c - lookforsize; | |
| for (i = 0; i < lookforsize; i++, c++) | |
| *c = 1; | |
| c -= lookforsize; | |
| /* set the address of each section in this group */ | |
| for (i = 0; i < totalsections; i++) { | |
| sa[p + i]->address = c - ram_slots[s->slot]; | |
| c += sa[p + i]->size; | |
| } | |
| /* generate labels for start and end of this ramsection (group) */ | |
| struct label* l; | |
| l = malloc(sizeof(struct label)); | |
| sprintf(l->name, "__startramsection%s", s->name); | |
| l->file_id = s->file_id; | |
| l->file_id_source = s->file_id_source; | |
| l->linenumber = 0; | |
| l->rom_address = sa[p]->address; | |
| l->section = s->id; | |
| l->section_status = s->status; | |
| l->bank = s->bank; | |
| l->base = s->base; | |
| l->slot = s->slot; | |
| l->status = LABEL_STATUS_LABEL; | |
| l->address = sa[p]->address; | |
| add_label(l); | |
| struct label* ll = malloc(sizeof(struct label)); | |
| *ll = *l; | |
| sprintf(ll->name, "__endramsection%s", s->name); | |
| ll->rom_address = sa[p+i-1]->address; | |
| ll->address = sa[p+i-1]->address + sa[p+i-1]->size; | |
| add_label(ll); | |
| } | |
| p += totalsections; | |
| } | |
| /* free tmp memory */ | |
| for (i = 0; i < 256; i++) { | |
| if (ram_slots[i] != NULL) | |
| free(ram_slots[i]); | |
| } | |
| /* force sections */ | |
| p = 0; | |
| while (p < sn) { | |
| s = sa[p++]; | |
| if (s->status == SECTION_STATUS_FORCE) { | |
| memory_file_id = s->file_id; | |
| banksize = banks[s->bank]; | |
| pc_bank = s->address; | |
| pc_slot = slots[s->slot].address + pc_bank; | |
| pc_full = pc_bank + bankaddress[s->bank]; | |
| pc_slot_max = slots[s->slot].address + slots[s->slot].size; | |
| d = pc_full; | |
| i = d + s->size; | |
| s->output_address = d; | |
| section_overwrite = OFF; | |
| if (i > romsize) { | |
| fprintf(stderr, "%s:%s: INSERT_SECTIONS: Section \"%s\" (%d bytes) goes beyond the ROM size.\n", get_file_name(s->file_id), | |
| get_source_file_name(s->file_id, s->file_id_source), s->name, s->size); | |
| return FAILED; | |
| } | |
| if (s->address + s->size > banksize) { | |
| fprintf(stderr, "%s:%s: INSERT_SECTIONS: Section \"%s\" (%d bytes) overflows from ROM bank %d.\n", get_file_name(s->file_id), | |
| get_source_file_name(s->file_id, s->file_id_source), s->name, s->size, s->bank); | |
| return FAILED; | |
| } | |
| for (; d < i; d++) { | |
| if (rom_usage[d] != 0 && rom[d] != s->data[d - pc_full]) | |
| break; | |
| } | |
| if (d == i) { | |
| for (i = 0; i < s->size; i++) { | |
| if (mem_insert_pc(s->data[i], s->slot, s->bank) == FAILED) | |
| return FAILED; | |
| } | |
| } | |
| else { | |
| fprintf(stderr, "%s:%s: INSERT_SECTIONS: No room for section \"%s\" (%d bytes).\n", get_file_name(s->file_id), | |
| get_source_file_name(s->file_id, s->file_id_source), s->name, s->size); | |
| return FAILED; | |
| } | |
| } | |
| } | |
| /* absolute sections */ | |
| p = 0; | |
| while (p < sn) { | |
| s = sa[p++]; | |
| if (s->status == SECTION_STATUS_ABSOLUTE) { | |
| d = s->address; | |
| s->output_address = d; | |
| section_overwrite = ON; | |
| for (i = 0; i < s->size; i++) { | |
| if (mem_insert(d + i, s->data[i]) == FAILED) | |
| return FAILED; | |
| } | |
| } | |
| } | |
| /* free & semifree sections */ | |
| p = 0; | |
| while (p < sn) { | |
| s = sa[p++]; | |
| if (s->status == SECTION_STATUS_FREE || s->status == SECTION_STATUS_SEMIFREE) { | |
| pc_bank = s->address; | |
| d = bankaddress[s->bank]; | |
| /* align the starting address */ | |
| f = (pc_bank + d) % s->alignment; | |
| if (f > 0) | |
| pc_bank += s->alignment - f; | |
| i = FAILED; | |
| while (i == FAILED) { | |
| f = pc_bank; | |
| for (x = 0; pc_bank < banks[s->bank] && rom_usage[pc_bank + d] == 0 && x < s->size; pc_bank++, x++); | |
| if (x == s->size) { | |
| i = SUCCEEDED; | |
| break; | |
| } | |
| if (pc_bank == banks[s->bank]) { | |
| fprintf(stderr, "%s:%s: INSERT_SECTIONS: No room for section \"%s\" (%d bytes) in ROM bank %d.\n", get_file_name(s->file_id), | |
| get_source_file_name(s->file_id, s->file_id_source), s->name, s->size, s->bank); | |
| fprintf(stderr,"banks[s->bank] %d, s->address 0x%x\n", banks[s->bank], s->address); | |
| return FAILED; | |
| } | |
| /* find the next starting address */ | |
| f = (pc_bank + d) % s->alignment; | |
| if (f > 0) | |
| pc_bank += s->alignment - f; | |
| for (; pc_bank < banks[s->bank] && rom_usage[pc_bank + d] != 0; pc_bank += s->alignment); | |
| } | |
| memory_file_id = s->file_id; | |
| banksize = banks[s->bank]; | |
| pc_bank = f; | |
| pc_slot = slots[s->slot].address + pc_bank; | |
| pc_full = pc_bank + bankaddress[s->bank]; | |
| pc_slot_max = slots[s->slot].address + slots[s->slot].size; | |
| s->address = pc_bank; | |
| s->output_address = pc_full; | |
| section_overwrite = OFF; | |
| for (i = 0; i < s->size; i++) { | |
| if (mem_insert_pc(s->data[i], s->slot, s->bank) == FAILED) | |
| return FAILED; | |
| } | |
| } | |
| } | |
| /* superfree sections */ | |
| p = 0; | |
| while (p < sn) { | |
| totalsections = 1; | |
| s = sa[p]; | |
| if (s->status == SECTION_STATUS_SUPERFREE) { | |
| lookforsections(sa, sn, p, &lookforsize, &totalsections); | |
| /* go through all the banks */ | |
| i = FAILED; | |
| f = 0; | |
| for (q = 0; i == FAILED && q < rombanks; q++) { | |
| pc_bank = 0; | |
| d = bankaddress[q]; | |
| /* align the starting address */ | |
| f = (pc_bank + d) % s->alignment; | |
| if (f > 0) | |
| pc_bank += s->alignment - f; | |
| /* if the slotsize and banksize differ -> try the next bank */ | |
| if (banks[q] != slots[s->slot].size) | |
| continue; | |
| while (i == FAILED) { | |
| f = pc_bank; | |
| for (x = 0; pc_bank < banks[q] && rom_usage[pc_bank + d] == 0 && x < lookforsize; pc_bank++, x++); | |
| if (x == lookforsize) { | |
| i = SUCCEEDED; | |
| break; | |
| } | |
| if (pc_bank == banks[q]) | |
| break; | |
| /* find the next starting address */ | |
| f = (pc_bank + d) % s->alignment; | |
| if (f > 0) | |
| pc_bank += s->alignment - f; | |
| for (; pc_bank < banks[s->bank] && rom_usage[pc_bank + d] != 0; pc_bank += s->alignment); | |
| } | |
| } | |
| if (i == SUCCEEDED) { | |
| /* set bank for each section */ | |
| for(i = 0; i < totalsections; i++) sa[p+i]->bank = q-1; | |
| memory_file_id = s->file_id; | |
| banksize = banks[s->bank]; | |
| pc_bank = f; | |
| pc_slot = pc_bank; | |
| pc_full = pc_bank + bankaddress[s->bank]; | |
| pc_slot_max = slots[s->slot].size; | |
| section_overwrite = OFF; | |
| /* write each section to memory and set its address in memory and in output file correctly */ | |
| for(j = 0; j < totalsections; j++) { | |
| //fprintf(stderr,"inserting section %d (%s, align %d) bank %d slot %d\n", j, sa[p+j]->name, sa[p+j]->alignment, sa[p+j]->bank, sa[p+j]->slot); | |
| //fprintf(stderr,"pc_bank %d pc_full %d pc_slot %d\n", pc_bank, pc_full,pc_slot); | |
| sa[p+j]->address = pc_bank; | |
| sa[p+j]->output_address = pc_full; | |
| for (i = 0; i < sa[p+j]->size; i++) { | |
| //fprintf(stderr,"inserting section %s byte %d\n", sa[p+j]->name, i); | |
| if (mem_insert_pc(sa[p+j]->data[i], sa[p+j]->slot, sa[p+j]->bank) == FAILED) | |
| return FAILED; | |
| } | |
| } | |
| /* generate labels for section (group) start and end | |
| (not for text sections) */ | |
| if(strncmp(s->name,".text", 5)) { //totalsections > 1) { | |
| struct label* l = malloc(sizeof(struct label)); | |
| sprintf(l->name, "__startsection%s", s->name); | |
| l->file_id = s->file_id; | |
| l->file_id_source = s->file_id_source; | |
| l->linenumber = 0; | |
| l->rom_address = s->address; | |
| l->section = s->id; | |
| l->section_status = s->status; | |
| l->bank = s->bank; | |
| l->base = s->base; | |
| l->slot = s->slot; | |
| l->status = LABEL_STATUS_LABEL; | |
| l->address = s->address; | |
| add_label(l); | |
| struct label* ll = malloc(sizeof(struct label)); | |
| *ll = *l; | |
| sprintf(ll->name, "__endsection%s", s->name); | |
| ll->rom_address = sa[p+j-1]->address; | |
| ll->address = sa[p+j-1]->address + sa[p+j-1]->size; | |
| add_label(ll); | |
| } | |
| } | |
| else { | |
| fprintf(stderr, "%s:%s: INSERT_SECTIONS: No room for section \"%s\" (%d bytes).\n", get_file_name(s->file_id), | |
| get_source_file_name(s->file_id, s->file_id_source), s->name, s->size); | |
| return FAILED; | |
| } | |
| } | |
| p += totalsections; | |
| } | |
| /* overwrite sections */ | |
| p = 0; | |
| while (p < sn) { | |
| s = sa[p++]; | |
| if (s->status == SECTION_STATUS_OVERWRITE) { | |
| memory_file_id = s->file_id; | |
| banksize = banks[s->bank]; | |
| pc_bank = s->address; | |
| pc_slot = slots[s->slot].address + pc_bank; | |
| pc_full = pc_bank + bankaddress[s->bank]; | |
| pc_slot_max = slots[s->slot].address + slots[s->slot].size; | |
| s->output_address = pc_full; | |
| section_overwrite = ON; | |
| if (pc_full + s->size > romsize) { | |
| fprintf(stderr, "%s:%s: INSERT_SECTIONS: Section \"%s\" (%d bytes) goes beyond the ROM size.\n", get_file_name(s->file_id), | |
| get_source_file_name(s->file_id, s->file_id_source), s->name, s->size); | |
| return FAILED; | |
| } | |
| if (s->address + s->size > banksize) { | |
| fprintf(stderr, "%s:%s: INSERT_SECTIONS: Section \"%s\" (%d bytes) overflows from ROM bank %d.\n", get_file_name(s->file_id), | |
| get_source_file_name(s->file_id, s->file_id_source), s->name, s->size, s->bank); | |
| return FAILED; | |
| } | |
| for (i = 0; i < s->size; i++) { | |
| if (mem_insert_pc(s->data[i], s->slot, s->bank) == FAILED) | |
| return FAILED; | |
| } | |
| } | |
| } | |
| free(sa); | |
| return SUCCEEDED; | |
| } | |
| /* transform computation stack definitions to ordinary definitions */ | |
| int transform_stack_definitions(void) { | |
| struct label *l; | |
| struct stack *s; | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (l->status == LABEL_STATUS_STACK) { | |
| /* DEBUG | |
| printf("--------------------------------------\n"); | |
| printf("name: \"%s\"\n", l->name); | |
| printf("sect: \"%d\"\n", l->section); | |
| printf("slot: \"%d\"\n", l->slot); | |
| printf("status: \"%d\"\n", l->status); | |
| printf("file_id: \"%d\"\n", l->file_id); | |
| printf("value: \"%d\"\n", l->address); | |
| */ | |
| s = stacks_first; | |
| /* find the stack associated with the definition */ | |
| while (s != NULL) { | |
| if (s->file_id == l->file_id && s->id == l->address) | |
| break; | |
| s = s->next; | |
| } | |
| /* did we find it? */ | |
| if (s == NULL) { | |
| fprintf(stderr, "TRANSFORM_STACK_DEFINITIONS: No computation stack associated with computation definition label \"%s\". This is a fatal internal error. Please send the WLA DX author a bug report.\n", l->name); | |
| return FAILED; | |
| } | |
| /* is it ok? */ | |
| if (s->computed == 0) { | |
| fprintf(stderr, "TRANSFORM_STACK_DEFINITIONS: The computation of definition \"%s\" hasn't been solved. This is a fatal internal error. Please send the WLA DX author a bug report.\n", l->name); | |
| return FAILED; | |
| } | |
| /* do the transformation */ | |
| l->status = LABEL_STATUS_DEFINE; | |
| l->address = s->result; | |
| } | |
| l = l->next; | |
| } | |
| return SUCCEEDED; | |
| } | |
| int fix_labels(void) { | |
| struct section *s = NULL; | |
| struct label *l, *m; | |
| /* fix labels' addresses */ | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (l->status == LABEL_STATUS_LABEL || l->status == LABEL_STATUS_SYMBOL || l->status == LABEL_STATUS_BREAKPOINT) { | |
| if (l->section_status == ON) { | |
| s = sec_first; | |
| while (s != NULL) { | |
| if (s->id == l->section) { | |
| l->bank = s->bank; | |
| l->address += s->address; | |
| l->rom_address = l->address + bankaddress[l->bank]; | |
| if (s->status != SECTION_STATUS_ABSOLUTE) | |
| l->address += slots[l->slot].address; | |
| break; | |
| } | |
| s = s->next; | |
| } | |
| } | |
| else { | |
| l->rom_address = l->address + bankaddress[l->bank]; | |
| l->address += slots[l->slot].address; | |
| } | |
| } | |
| l = l->next; | |
| } | |
| /* check out if a label exists more than once in a different place */ | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (is_label_anonymous(l->name) == FAILED && (l->status == LABEL_STATUS_LABEL || l->status == LABEL_STATUS_DEFINE)) { | |
| m = l->next; | |
| while (m != NULL) { | |
| if (strcmp(m->name, l->name) == 0) { | |
| if (l->address != m->address && !(m->name[0] == '*' || is_label_section_local(m->name) == SUCCEEDED || is_label_file_local(m->name) == SUCCEEDED)) { | |
| if (l->status == LABEL_STATUS_DEFINE) | |
| fprintf(stderr, "%s: FIX_LABELS: Definition \"%s\" was defined more than once.\n", get_file_name(l->file_id), l->name); | |
| else { | |
| fprintf(stderr, "%s:%s:%d: FIX_LABELS: Label \"%s\" was defined more than once.\n", get_file_name(l->file_id), | |
| get_source_file_name(l->file_id, l->file_id_source), l->linenumber, l->name); | |
| fprintf(stderr,"mn %s islsl %d islfl %d\n", m->name, is_label_section_local(m->name) == SUCCEEDED, is_label_file_local(m->name) == SUCCEEDED); | |
| } | |
| return FAILED; | |
| } | |
| } | |
| m = m->next; | |
| } | |
| } | |
| l = l->next; | |
| } | |
| return SUCCEEDED; | |
| } | |
| int fix_references(void) { | |
| struct reference *r; | |
| struct section *s = NULL; | |
| struct label *l, lt; | |
| int i, x; | |
| section_overwrite = OFF; | |
| /* insert references */ | |
| r = reference_first; | |
| while (r != NULL) { | |
| x = r->address; | |
| /* search for the section of the reference and fix the address */ | |
| if (r->section_status == ON) { | |
| s = sec_first; | |
| while (s != NULL) { | |
| if (s->id == r->section) { | |
| r->bank = s->bank; | |
| x += s->address; | |
| r->address += s->address; | |
| break; | |
| } | |
| s = s->next; | |
| } | |
| /* reference is inside a discarded section? */ | |
| if (s != NULL && s->alive == NO) { | |
| r = r->next; | |
| continue; | |
| } | |
| if (s == NULL) { | |
| if (write_bank_header_references(r) == FAILED) | |
| return FAILED; | |
| r = r->next; | |
| continue; | |
| } | |
| } | |
| if (!(r->section_status == ON && s->status == SECTION_STATUS_ABSOLUTE)) { | |
| x += bankaddress[r->bank]; | |
| r->address += slots[r->slot].address; | |
| } | |
| /* find the destination */ | |
| l = labels_first; | |
| /* request for bank number? */ | |
| if (r->name[0] == ':') { | |
| if (is_label_anonymous(&r->name[1]) == SUCCEEDED) { | |
| l = get_closest_anonymous_label(&r->name[1], x, r->file_id, l, r->section_status, r->section); | |
| } | |
| else if (strcmp(&r->name[1], "CADDR") == 0 || strcmp(&r->name[1], "caddr") == 0) { | |
| lt.status = LABEL_STATUS_LABEL; | |
| strcpy(lt.name, &r->name[1]); | |
| lt.address = r->address; | |
| lt.bank = r->bank; | |
| lt.section_status = OFF; | |
| l = < | |
| } | |
| else { | |
| struct label *ll = NULL; | |
| while (l != NULL) { | |
| if (strcmp(l->name, &r->name[1]) == 0 && l->status != LABEL_STATUS_SYMBOL && l->status != LABEL_STATUS_BREAKPOINT) { | |
| if (!ll) { | |
| ll = l; | |
| } | |
| else { | |
| //printf("double label %s file_id %d r %s file_id %d\n", l->name, l->file_id, r->name, r->file_id); | |
| if (is_label_file_local(&r->name[1]) == SUCCEEDED) { | |
| if (l->file_id == r->file_id) | |
| ll = l; | |
| } | |
| else { | |
| fprintf(stderr,"%s:%s:%d: DUPLICATE LABEL: \"%s\" in files \"%s\" and \"%s\".\n", | |
| get_file_name(r->file_id), get_source_file_name(r->file_id, r->file_id_source), r->linenumber, | |
| &r->name[1],get_file_name(l->file_id),get_file_name(ll->file_id)); | |
| return FAILED; | |
| } | |
| } | |
| } | |
| l = l->next; | |
| } | |
| l = ll; | |
| } | |
| if (l == NULL) { | |
| fprintf(stderr, "%s:%s:%d: FIX_REFERENCES: Bank number request for an unknown label \"%s\".\n", | |
| get_file_name(r->file_id), get_source_file_name(r->file_id, r->file_id_source), r->linenumber, &r->name[1]); | |
| return FAILED; | |
| } | |
| if (cpu_65816 != 0) | |
| i = get_snes_pc_bank(l) >> 16; | |
| else | |
| i = l->bank; | |
| memory_file_id = r->file_id; | |
| memory_file_id_source = r->file_id_source; | |
| memory_line_number = r->linenumber; | |
| /* direct 16bit */ | |
| if (r->type == REFERENCE_TYPE_DIRECT_16BIT || r->type == REFERENCE_TYPE_RELATIVE_16BIT) { | |
| mem_insert_ref(x, i & 0xFF); | |
| mem_insert_ref(x + 1, (i >> 8) & 0xFF); | |
| } | |
| /* direct / relative 8bit with a definition */ | |
| else if (l->status == LABEL_STATUS_DEFINE) { | |
| fprintf(stderr, "%s:%s:%d: FIX_REFERENCES: Bank number request for a definition \"%s\"?\n", | |
| get_file_name(r->file_id), get_source_file_name(r->file_id, r->file_id_source), r->linenumber, l->name); | |
| return FAILED; | |
| } | |
| /* direct 24bit */ | |
| else if (r->type == REFERENCE_TYPE_DIRECT_24BIT) { | |
| mem_insert_ref(x, i & 0xFF); | |
| mem_insert_ref(x + 1, (i >> 8) & 0xFF); | |
| mem_insert_ref(x + 2, (i >> 16) & 0xFF); | |
| } | |
| /* relative/direct 8bit with a label */ | |
| else { | |
| mem_insert_ref(x, i & 0xFF); | |
| } | |
| } | |
| /* normal reference */ | |
| else { | |
| if (is_label_anonymous(r->name) == SUCCEEDED) { | |
| l = get_closest_anonymous_label(r->name, x, r->file_id, l, r->section_status, r->section); | |
| } | |
| else if (strcmp(r->name, "CADDR") == 0 || strcmp(r->name, "caddr") == 0) { | |
| lt.status = LABEL_STATUS_DEFINE; | |
| strcpy(lt.name, r->name); | |
| lt.address = r->address; | |
| lt.bank = r->bank; | |
| lt.section_status = OFF; | |
| l = < | |
| } | |
| else { | |
| while (l != NULL) { | |
| if (strcmp(l->name, r->name) != 0 || l->status == LABEL_STATUS_SYMBOL || l->status == LABEL_STATUS_BREAKPOINT) | |
| l = l->next; | |
| else { | |
| /* search for the section of the referencee */ | |
| if (is_label_section_local(r->name) == SUCCEEDED || is_label_file_local(r->name) == SUCCEEDED) { | |
| if (l->file_id == r->file_id) { | |
| if (l->section_status != r->section_status) { | |
| l = l->next; | |
| continue; | |
| } | |
| if (is_label_section_local(r->name) == SUCCEEDED && l->section_status == ON && l->section != r->section) { | |
| l = l->next; | |
| continue; | |
| } | |
| } | |
| else { | |
| l = l->next; | |
| continue; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| if (l == NULL) { | |
| fprintf(stderr, "%s:%s:%d: FIX_REFERENCES: Reference to an unknown label \"%s\".\n", | |
| get_file_name(r->file_id), get_source_file_name(r->file_id, r->file_id_source), r->linenumber, r->name); | |
| return FAILED; | |
| } | |
| memory_file_id = r->file_id; | |
| memory_file_id_source = r->file_id_source; | |
| memory_line_number = r->linenumber; | |
| /* direct 16bit */ | |
| if (r->type == REFERENCE_TYPE_DIRECT_16BIT) { | |
| i = l->address; | |
| mem_insert_ref(x, i & 0xFF); | |
| mem_insert_ref(x + 1, (i >> 8) & 0xFF); | |
| } | |
| /* direct / relative 8bit with a value definition */ | |
| else if (l->status == LABEL_STATUS_DEFINE && (r->type == REFERENCE_TYPE_DIRECT_8BIT || r->type == REFERENCE_TYPE_RELATIVE_8BIT)) { | |
| i = ((int)l->address) & 0xFFFF; | |
| if (i > 255 || i < -128) { | |
| fprintf(stderr, "%s:%s:%d: FIX_REFERENCES: Value ($%x) of \"%s\" is too much to be a 8bit value.\n", | |
| get_file_name(r->file_id), get_source_file_name(r->file_id, r->file_id_source), r->linenumber, i, l->name); | |
| return FAILED; | |
| } | |
| mem_insert_ref(x, i & 0xFF); | |
| } | |
| /* direct 24bit */ | |
| else if (r->type == REFERENCE_TYPE_DIRECT_24BIT) { | |
| i = l->address; | |
| if (l->status == LABEL_STATUS_LABEL) | |
| i += get_snes_pc_bank(l); | |
| mem_insert_ref(x, i & 0xFF); | |
| mem_insert_ref(x + 1, (i >> 8) & 0xFF); | |
| mem_insert_ref(x + 2, (i >> 16) & 0xFF); | |
| } | |
| /* relative 8bit with a label */ | |
| else if (r->type == REFERENCE_TYPE_RELATIVE_8BIT) { | |
| i = (((int)l->address) & 0xFFFF) - r->address - 1; | |
| if (i < -128 || i > 127) { | |
| fprintf(stderr, "%s:%s:%d: FIX_REFERENCES: Too large distance (%d bytes from $%x to $%x \"%s\") for a 8bit reference.\n", | |
| get_file_name(r->file_id), get_source_file_name(r->file_id, r->file_id_source), r->linenumber, i, r->address, (int)l->address, l->name); | |
| return FAILED; | |
| } | |
| mem_insert_ref(x, i & 0xFF); | |
| } | |
| /* relative 16bit with a label */ | |
| else if (r->type == REFERENCE_TYPE_RELATIVE_16BIT) { | |
| i = (((int)l->address) & 0xFFFF) - r->address - 2; | |
| if (i < -32768 || i > 65535) { | |
| fprintf(stderr, "%s:%s:%d: FIX_REFERENCES: Too large distance (%d bytes from $%x to $%x \"%s\") for a 16bit reference.\n", | |
| get_file_name(r->file_id), get_source_file_name(r->file_id, r->file_id_source), r->linenumber, i, r->address, (int)l->address, l->name); | |
| return FAILED; | |
| } | |
| mem_insert_ref(x, i & 0xFF); | |
| mem_insert_ref(x + 1, (i >> 8) & 0xFF); | |
| } | |
| else { | |
| i = ((int)l->address) & 0xFFFF; | |
| if (i > 255) { | |
| fprintf(stderr, "%s:%s:%d: FIX_REFERENCES: Value ($%x) of \"%s\" is too much to be a 8bit value.\n", | |
| get_file_name(r->file_id), get_source_file_name(r->file_id, r->file_id_source), r->linenumber, i, l->name); | |
| return FAILED; | |
| } | |
| mem_insert_ref(x, i & 0xFF); | |
| } | |
| } | |
| r = r->next; | |
| } | |
| return SUCCEEDED; | |
| } | |
| int write_symbol_file(char *outname, unsigned char mode) { | |
| struct section *s; | |
| struct label *l; | |
| char name[256], *p; | |
| FILE *f; | |
| int y; | |
| if (outname == NULL) | |
| return FAILED; | |
| strcpy(name, outname); | |
| p = name; | |
| for (y = 0; y < 255 && *p != '.' && *p != 0; y++, p++); | |
| *(p++) = '.'; | |
| *(p++) = 's'; | |
| *(p++) = 'y'; | |
| *(p++) = 'm'; | |
| *p = 0; | |
| f = fopen(name, "wb"); | |
| if (f == NULL) { | |
| fprintf(stderr, "MAIN: Error opening file \"%s\".\n", name); | |
| return FAILED; | |
| } | |
| fprintf(f, "; this file was created with wlalink by ville helin <vhelin@iki.fi>.\n"); | |
| if (mode == SYMBOL_MODE_NOCA5H) { | |
| /* NO$GMB SYMBOL FILE */ | |
| fprintf(f, "; no$gmb symbolic information for \"%s\".\n", outname); | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (is_label_anonymous(l->name) == SUCCEEDED || l->status == LABEL_STATUS_SYMBOL || l->status == LABEL_STATUS_BREAKPOINT) { | |
| l = l->next; | |
| continue; | |
| } | |
| /* skip all dropped section labels */ | |
| if (l->section_status == ON) { | |
| s = sec_first; | |
| while (l->section != s->id) | |
| s = s->next; | |
| if (s->alive == NO) { | |
| l = l->next; | |
| continue; | |
| } | |
| } | |
| if (l->status == LABEL_STATUS_LABEL) { | |
| if (snes_mode == 0) | |
| fprintf(f, "%.4x:%.4x %s\n", l->bank, (int)l->address, l->name); | |
| else | |
| fprintf(f, "%.4x:%.4x %s\n", get_snes_pc_bank(l)>>16, (int)l->address, l->name); | |
| } | |
| else | |
| fprintf(f, "0000:%.4x %s\n", (int)l->address, l->name); | |
| l = l->next; | |
| } | |
| } | |
| else { | |
| /* WLA SYMBOL FILE */ | |
| fprintf(f, "; wla symbolic information for \"%s\".\n", outname); | |
| /* labels */ | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (l->status != LABEL_STATUS_LABEL) { | |
| l = l->next; | |
| continue; | |
| } | |
| break; | |
| } | |
| if (l != NULL) { | |
| fprintf(f, "\n[labels]\n"); | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (l->status != LABEL_STATUS_LABEL) { | |
| l = l->next; | |
| continue; | |
| } | |
| if (is_label_anonymous(l->name) == SUCCEEDED) { | |
| l = l->next; | |
| continue; | |
| } | |
| /* skip all dropped section labels */ | |
| if (l->section_status == ON) { | |
| s = sec_first; | |
| while (l->section != s->id) | |
| s = s->next; | |
| if (s->alive == NO) { | |
| l = l->next; | |
| continue; | |
| } | |
| } | |
| if (snes_mode == 0) | |
| fprintf(f, "%.4x:%.4x %s\n", l->bank, (int)l->address, l->name); | |
| else | |
| fprintf(f, "%.4x:%.4x %s\n", get_snes_pc_bank(l)>>16, (int)l->address, l->name); | |
| l = l->next; | |
| } | |
| } | |
| /* symbols */ | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (l->status != LABEL_STATUS_SYMBOL) { | |
| l = l->next; | |
| continue; | |
| } | |
| break; | |
| } | |
| if (l != NULL) { | |
| fprintf(f, "\n[symbols]\n"); | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (l->status != LABEL_STATUS_SYMBOL) { | |
| l = l->next; | |
| continue; | |
| } | |
| if (snes_mode == 0) | |
| fprintf(f, "%.4x:%.4x %s\n", l->bank, (int)l->address, l->name); | |
| else | |
| fprintf(f, "%.4x:%.4x %s\n", get_snes_pc_bank(l)>>16, (int)l->address, l->name); | |
| l = l->next; | |
| } | |
| } | |
| /* breakpoints */ | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (l->status != LABEL_STATUS_BREAKPOINT) { | |
| l = l->next; | |
| continue; | |
| } | |
| break; | |
| } | |
| if (l != NULL) { | |
| fprintf(f, "\n[breakpoints]\n"); | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (l->status != LABEL_STATUS_BREAKPOINT) { | |
| l = l->next; | |
| continue; | |
| } | |
| if (snes_mode == 0) | |
| fprintf(f, "%.4x:%.4x\n", l->bank, (int)l->address); | |
| else | |
| fprintf(f, "%.4x:%.4x\n", get_snes_pc_bank(l)>>16, (int)l->address); | |
| l = l->next; | |
| } | |
| } | |
| /* definitions */ | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (l->status != LABEL_STATUS_DEFINE) { | |
| l = l->next; | |
| continue; | |
| } | |
| break; | |
| } | |
| if (l != NULL) { | |
| fprintf(f, "\n[definitions]\n"); | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (l->status != LABEL_STATUS_DEFINE) { | |
| l = l->next; | |
| continue; | |
| } | |
| if (is_label_anonymous(l->name) == SUCCEEDED) { | |
| l = l->next; | |
| continue; | |
| } | |
| fprintf(f, "%.8x %s\n", (int)l->address, l->name); | |
| l = l->next; | |
| } | |
| } | |
| } | |
| fclose(f); | |
| return SUCCEEDED; | |
| } | |
| int write_rom_file(char *outname) { | |
| struct section *s; | |
| FILE *f; | |
| int i, b, e; | |
| f = fopen(outname, "wb"); | |
| if (f == NULL) { | |
| fprintf(stderr, "WRITE_ROM_FILE: Error opening file \"%s\".\n", outname); | |
| return FAILED; | |
| } | |
| if (file_header != NULL) | |
| fwrite(file_header, 1, file_header_size, f); | |
| /* SMC header */ | |
| if (smc_status != 0) | |
| smc_create_and_write(f); | |
| /* ROM output mode */ | |
| if (output_mode == OUTPUT_ROM) { | |
| /* write bank by bank and bank header sections */ | |
| for (i = 0; i < rombanks; i++) { | |
| s = sec_hd_first; | |
| while (s != NULL) { | |
| if (s->bank == i) { | |
| fwrite(s->data, 1, s->size, f); | |
| break; | |
| } | |
| s = s->next; | |
| } | |
| fwrite(rom + bankaddress[i], 1, banks[i], f); | |
| } | |
| } | |
| /* program file output mode */ | |
| else { | |
| for (i = 0; i < romsize; i++) | |
| if (rom_usage[i] != 0) | |
| break; | |
| b = i; | |
| for (e = b; i < romsize; i++) | |
| if (rom_usage[i] != 0) | |
| e = i; | |
| s = sec_hd_first; | |
| while (s != NULL) { | |
| if (s->bank == 0) { | |
| fwrite(s->data, 1, s->size, f); | |
| break; | |
| } | |
| s = s->next; | |
| } | |
| fwrite(rom + b, 1, e - b + 1, f); | |
| program_start = b; | |
| program_end = e; | |
| } | |
| if (file_footer != NULL) | |
| fwrite(file_footer, 1, file_footer_size, f); | |
| fclose(f); | |
| return SUCCEEDED; | |
| } | |
| int compute_pending_calculations(void) { | |
| struct section *s; | |
| struct stack *sta; | |
| int k, a; | |
| section_overwrite = ON; | |
| /* first place the calculation stacks into the output */ | |
| sta = stacks_first; | |
| while (sta != NULL) { | |
| if (sta->position == STACK_POSITION_DEFINITION) { | |
| /* skip definition stacks */ | |
| sta = sta->next; | |
| continue; | |
| } | |
| if (sta->section_status == ON) { | |
| /* get section address */ | |
| s = sec_first; | |
| while (s != NULL) { | |
| if (sta->section == s->id) { | |
| sta->bank = s->bank; | |
| break; | |
| } | |
| s = s->next; | |
| } | |
| /* the computation is inside a discarded section? */ | |
| if (s != NULL && s->alive == NO) { | |
| sta = sta->next; | |
| continue; | |
| } | |
| /* it must be a bank header section! */ | |
| if (s == NULL) { | |
| sta = sta->next; | |
| continue; | |
| } | |
| /* remember the memory address (for CADDR) */ | |
| sta->memory_address = s->address + sta->address + slots[sta->slot].address; | |
| if (s->status != SECTION_STATUS_ABSOLUTE) | |
| sta->address += s->address + bankaddress[s->bank]; | |
| else | |
| sta->address += s->address; | |
| } | |
| else { | |
| /* remember the memory address (for CADDR) */ | |
| sta->memory_address = sta->address + slots[sta->slot].address; | |
| sta->address += bankaddress[sta->bank]; | |
| } | |
| sta = sta->next; | |
| } | |
| /* next parse the stack items */ | |
| sta = stacks_first; | |
| while (sta != NULL) { | |
| if (sta->position == STACK_POSITION_DEFINITION) | |
| k = 1; | |
| else { | |
| /* skip the calculations inside discarded sections */ | |
| if (sta->section_status == ON) { | |
| /* get the section */ | |
| s = sec_first; | |
| while (s != NULL) { | |
| if (sta->section == s->id) { | |
| break; | |
| } | |
| s = s->next; | |
| } | |
| if (s != NULL && s->alive == YES) | |
| k = 1; | |
| else | |
| k = 0; | |
| } | |
| else | |
| k = 1; | |
| } | |
| if (k == 1) { | |
| if (parse_stack(sta) == FAILED) | |
| return FAILED; | |
| } | |
| sta = sta->next; | |
| } | |
| /* then compute and place the results */ | |
| sta = stacks_first; | |
| while (sta != NULL) { | |
| /* is the stack inside a definition? */ | |
| if (sta->position == STACK_POSITION_DEFINITION) { | |
| /* all the references have been decoded, now compute */ | |
| if (compute_stack(sta, &k) == FAILED) | |
| return FAILED; | |
| /* next stack computation */ | |
| sta = sta->next; | |
| continue; | |
| } | |
| /* find source address */ | |
| if (sta->section_status == ON) { | |
| /* get section address */ | |
| s = sec_first; | |
| while (s != NULL) { | |
| if (sta->section == s->id) { | |
| sta->bank = s->bank; | |
| break; | |
| } | |
| s = s->next; | |
| } | |
| /* the computation is inside a discarded section? */ | |
| if (s != NULL && s->alive == NO) { | |
| sta = sta->next; | |
| continue; | |
| } | |
| /* it must be a bank header section! */ | |
| if (s == NULL) { | |
| if (write_bank_header_calculations(sta) == FAILED) | |
| return FAILED; | |
| sta = sta->next; | |
| continue; | |
| } | |
| } | |
| a = sta->address; | |
| /* all the references have been decoded, now compute */ | |
| if (compute_stack(sta, &k) == FAILED) | |
| return FAILED; | |
| memory_file_id = sta->file_id; | |
| memory_file_id_source = sta->file_id_source; | |
| memory_line_number = sta->linenumber; | |
| if (sta->type == STACKS_TYPE_8BIT) { | |
| if (k < -128 || k > 255) { | |
| fprintf(stderr, "%s:%s:%d: COMPUTE_PENDING_CALCULATIONS: Result (%d/$%x) of a computation is out of 8bit range.\n", | |
| get_file_name(sta->file_id), get_source_file_name(sta->file_id, sta->file_id_source), sta->linenumber, k, k); | |
| return FAILED; | |
| } | |
| if (mem_insert_ref(a, k) == FAILED) | |
| return FAILED; | |
| } | |
| else if (sta->type == STACKS_TYPE_16BIT) { | |
| if (k < -32768 || k > 65535) { | |
| fprintf(stderr, "%s:%s:%d: COMPUTE_PENDING_CALCULATIONS: Result (%d/$%x) of a computation is out of 16bit range.\n", | |
| get_file_name(sta->file_id), get_source_file_name(sta->file_id, sta->file_id_source), sta->linenumber, k, k); | |
| return FAILED; | |
| } | |
| if (mem_insert_ref(a, k & 0xFF) == FAILED) | |
| return FAILED; | |
| if (mem_insert_ref(a + 1, (k >> 8) & 0xFF) == FAILED) | |
| return FAILED; | |
| } | |
| else { | |
| if (k < -8388608 || k > 16777215) { | |
| fprintf(stderr, "%s:%s:%d: COMPUTE_PENDING_CALCULATIONS: Result (%d/$%x) of a computation is out of 24bit range.\n", | |
| get_file_name(sta->file_id), get_source_file_name(sta->file_id, sta->file_id_source), sta->linenumber, k, k); | |
| return FAILED; | |
| } | |
| if (mem_insert_ref(a, k & 0xFF) == FAILED) | |
| return FAILED; | |
| if (mem_insert_ref(a + 1, (k >> 8) & 0xFF) == FAILED) | |
| return FAILED; | |
| if (mem_insert_ref(a + 2, (k >> 16) & 0xFF) == FAILED) | |
| return FAILED; | |
| } | |
| /* next stack computation */ | |
| sta = sta->next; | |
| } | |
| return SUCCEEDED; | |
| } | |
| int compute_stack(struct stack *sta, int *result) { | |
| struct stackitem *s; | |
| struct stack *st; | |
| int r, t, z, x, res; | |
| double v[256], q; | |
| if (sta->under_work == YES) { | |
| fprintf(stderr, "%s:%s:%d: COMPUTE_STACK: A loop found in computation.\n", get_file_name(sta->file_id), | |
| get_source_file_name(sta->file_id, sta->file_id_source), sta->linenumber); | |
| return FAILED; | |
| } | |
| if (sta->computed == YES) { | |
| *result = sta->result; | |
| return SUCCEEDED; | |
| } | |
| sta->under_work = YES; | |
| x = sta->stacksize; | |
| s = sta->stack; | |
| for (r = 0, t = 0; r < x; r++, s++) { | |
| if (s->type == STACK_ITEM_TYPE_VALUE) { | |
| if (s->sign == SI_SIGN_NEGATIVE) | |
| v[t] = -s->value; | |
| else | |
| v[t] = s->value; | |
| t++; | |
| } | |
| else if (s->type == STACK_ITEM_TYPE_STACK) { | |
| /* we have a stack inside a stack! find the stack */ | |
| st = stacks_first; | |
| while (st != NULL) { | |
| if (st->id == s->value && st->file_id == s->sign) | |
| break; | |
| st = st->next; | |
| } | |
| if (st == NULL) { | |
| fprintf(stderr, "COMPUTE_STACK: A computation stack has gone missing. This is a fatal internal error. Please send the WLA DX author a bug report.\n"); | |
| return FAILED; | |
| } | |
| if (compute_stack(st, &res) == FAILED) | |
| return FAILED; | |
| v[t] = res; | |
| t++; | |
| } | |
| else { | |
| switch ((int)s->value) { | |
| case SI_OP_PLUS: | |
| v[t - 2] += v[t - 1]; | |
| t--; | |
| break; | |
| case SI_OP_MINUS: | |
| v[t - 2] -= v[t - 1]; | |
| t--; | |
| break; | |
| case SI_OP_XOR: | |
| /* 16bit XOR? */ | |
| if (v[t - 2] > 0xFF || v[t - 2] < -128 || v[t - 1] > 0xFF || v[t - 1] < -128) | |
| v[t - 2] = ((int)v[t - 1] ^ (int)v[t - 2]) & 0xFFFF; | |
| /* 8bit XOR */ | |
| else | |
| v[t - 2] = ((int)v[t - 1] ^ (int)v[t - 2]) & 0xFF; | |
| t--; | |
| break; | |
| case SI_OP_MULTIPLY: | |
| v[t - 2] *= v[t - 1]; | |
| t--; | |
| break; | |
| case SI_OP_OR: | |
| v[t - 2] = (int)v[t - 1] | (int)v[t - 2]; | |
| t--; | |
| break; | |
| case SI_OP_AND: | |
| v[t - 2] = (int)v[t - 1] & (int)v[t - 2]; | |
| t--; | |
| break; | |
| case SI_OP_LOW_BYTE: | |
| z = (int)v[t - 1]; | |
| v[t - 1] = z & 0xFF; | |
| break; | |
| case SI_OP_HIGH_BYTE: | |
| z = (int)v[t - 1]; | |
| v[t - 1] = (z>>8) & 0xFF; | |
| break; | |
| case SI_OP_MODULO: | |
| if (((int)v[t - 1]) == 0) { | |
| fprintf(stderr, "%s:%s:%d: COMPUTE_STACK: Modulo by zero.\n", get_file_name(sta->file_id), | |
| get_source_file_name(sta->file_id, sta->file_id_source), sta->linenumber); | |
| return FAILED; | |
| } | |
| v[t - 2] = (int)v[t - 2] % (int)v[t - 1]; | |
| t--; | |
| break; | |
| case SI_OP_DIVIDE: | |
| if (((int)v[t - 1]) == 0) { | |
| fprintf(stderr, "%s:%s:%d: COMPUTE_STACK: Division by zero.\n", get_file_name(sta->file_id), | |
| get_source_file_name(sta->file_id, sta->file_id_source), sta->linenumber); | |
| return FAILED; | |
| } | |
| v[t - 2] /= v[t - 1]; | |
| t--; | |
| break; | |
| case SI_OP_POWER: | |
| q = 1; | |
| for (z = 0; z < v[t - 1]; z++) | |
| q *= v[t - 2]; | |
| v[t - 2] = q; | |
| t--; | |
| break; | |
| case SI_OP_SHIFT_LEFT: | |
| v[t - 2] = (int)v[t - 2] << (int)v[t - 1]; | |
| t--; | |
| break; | |
| case SI_OP_SHIFT_RIGHT: | |
| v[t - 2] = (int)v[t - 2] >> (int)v[t - 1]; | |
| t--; | |
| break; | |
| } | |
| } | |
| } | |
| *result = v[0]; | |
| sta->result = v[0]; | |
| sta->computed = YES; | |
| sta->under_work = NO; | |
| return SUCCEEDED; | |
| } | |
| int write_bank_header_calculations(struct stack *sta) { | |
| struct section *s; | |
| unsigned char *t; | |
| int k; | |
| /* parse stack items */ | |
| if (parse_stack(sta) == FAILED) | |
| return FAILED; | |
| /* all the references have been decoded, now compute */ | |
| if (compute_stack(sta, &k) == FAILED) | |
| return FAILED; | |
| s = sec_hd_first; | |
| while (sta->section != s->id) | |
| s = s->next; | |
| t = s->data + sta->address; | |
| if (sta->type == STACKS_TYPE_8BIT) { | |
| if (k < -128 || k > 255) { | |
| fprintf(stderr, "%s:%s:%d: WRITE_BANK_HEADER_CALCULATIONS: Result (%d/$%x) of a computation is out of 8bit range.\n", | |
| get_file_name(sta->file_id), get_source_file_name(sta->file_id, sta->file_id_source), sta->linenumber, k, k); | |
| return FAILED; | |
| } | |
| *t = k & 0xFF; | |
| } | |
| else if (sta->type == STACKS_TYPE_16BIT) { | |
| if (k < -32768 || k > 65535) { | |
| fprintf(stderr, "%s:%s:%d: WRITE_BANK_HEADER_CALCULATIONS: Result (%d/$%x) of a computation is out of 16bit range.\n", | |
| get_file_name(sta->file_id), get_source_file_name(sta->file_id, sta->file_id_source), sta->linenumber, k, k); | |
| return FAILED; | |
| } | |
| *t = k & 0xFF; | |
| t++; | |
| *t = (k >> 8) & 0xFF; | |
| } | |
| else { | |
| if (k < -8388608 || k > 16777215) { | |
| fprintf(stderr, "%s:%s:%d: WRITE_BANK_HEADER_CALCULATIONS: Result (%d/$%x) of a computation is out of 24bit range.\n", | |
| get_file_name(sta->file_id), get_source_file_name(sta->file_id, sta->file_id_source), sta->linenumber, k, k); | |
| return FAILED; | |
| } | |
| *t = k & 0xFF; | |
| t++; | |
| *t = (k >> 8) & 0xFF; | |
| t++; | |
| *t = (k >> 16) & 0xFF; | |
| } | |
| return SUCCEEDED; | |
| } | |
| int write_bank_header_references(struct reference *r) { | |
| struct section *s; | |
| struct label *l; | |
| unsigned char *t; | |
| int a; | |
| s = sec_hd_first; | |
| while (r->section != s->id) | |
| s = s->next; | |
| t = s->data + r->address; | |
| /* find the destination */ | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (strcmp(l->name, r->name) == 0) { | |
| a = l->address; | |
| /* direct 16bit */ | |
| if (r->type == REFERENCE_TYPE_DIRECT_16BIT) { | |
| *t = a & 0xFF; | |
| t++; | |
| *t = (a >> 8) & 0xFF; | |
| break; | |
| } | |
| /* direct 8bit */ | |
| else if (r->type == REFERENCE_TYPE_DIRECT_8BIT) { | |
| if (a > 255 || a < -128) { | |
| fprintf(stderr, "%s:%s:%d: WRITE_BANK_HEADER_REFERENCES: Value (%d/$%x) of \"%s\" is too much to be a 8bit value.\n", | |
| get_file_name(r->file_id), get_source_file_name(r->file_id, r->file_id_source), r->linenumber, a, a, l->name); | |
| return FAILED; | |
| } | |
| *t = a & 0xFF; | |
| break; | |
| } | |
| /* direct 24bit */ | |
| else if (r->type == REFERENCE_TYPE_DIRECT_24BIT) { | |
| if (l->status == LABEL_STATUS_LABEL) | |
| a += get_snes_pc_bank(l); | |
| *t = a & 0xFF; | |
| t++; | |
| *t = (a >> 8) & 0xFF; | |
| t++; | |
| *t = (a >> 16) & 0xFF; | |
| break; | |
| } | |
| else { | |
| fprintf(stderr, "%s:%s:%d: WRITE_BANK_HEADER_REFERENCES: A relative reference (type %d) to label \"%s\".\n", | |
| get_file_name(r->file_id), get_source_file_name(r->file_id, r->file_id_source), r->linenumber, r->type, l->name); | |
| return FAILED; | |
| } | |
| } | |
| l = l->next; | |
| } | |
| if (l == NULL) { | |
| fprintf(stderr, "%s:%s:%d: WRITE_BANK_HEADER_REFERENCES: Reference to an unknown label \"%s\".\n", | |
| get_file_name(r->file_id), get_source_file_name(r->file_id, r->file_id_source), r->linenumber, r->name); | |
| return FAILED; | |
| } | |
| return SUCCEEDED; | |
| } | |
| /* transform all string items inside a computation stack into corresponding numbers */ | |
| int parse_stack(struct stack *sta) { | |
| struct stackitem *si; | |
| struct label *l, lt; | |
| double k; | |
| int g; | |
| si = sta->stack; | |
| g = 0; | |
| k = 0; | |
| while (g != sta->stacksize) { | |
| if (si->type == STACK_ITEM_TYPE_STRING) { | |
| l = labels_first; | |
| /* bank number search */ | |
| if (si->string[0] == ':') { | |
| if (is_label_anonymous(&si->string[1]) == SUCCEEDED) { | |
| l = get_closest_anonymous_label(&si->string[1], sta->address, sta->file_id, l, sta->section_status, sta->section); | |
| if (l != NULL) | |
| k = l->bank; | |
| } | |
| else if (strcmp(&si->string[1], "CADDR") == 0 || strcmp(&si->string[1], "caddr") == 0) { | |
| k = sta->bank; | |
| lt.status = LABEL_STATUS_DEFINE; | |
| l = < | |
| } | |
| else { | |
| while (l != NULL) { | |
| if (strcmp(l->name, &si->string[1]) == 0) { | |
| if (cpu_65816 != 0) | |
| k = get_snes_pc_bank(l) >> 16; | |
| else | |
| k = l->bank; | |
| break; | |
| } | |
| l = l->next; | |
| } | |
| } | |
| } | |
| /* normal label address search */ | |
| else { | |
| if (is_label_anonymous(si->string) == SUCCEEDED) { | |
| l = get_closest_anonymous_label(si->string, sta->address, sta->file_id, l, sta->section_status, sta->section); | |
| if (l != NULL) | |
| k = l->address; | |
| /* is the reference relative? */ | |
| if (sta->relative_references == YES) | |
| k = k - sta->memory_address - 1; | |
| } | |
| else if (strcmp(si->string, "CADDR") == 0 || strcmp(si->string, "caddr") == 0) { | |
| k = sta->memory_address; | |
| lt.status = LABEL_STATUS_DEFINE; | |
| l = < | |
| } | |
| else { | |
| while (l != NULL) { | |
| if (strcmp(l->name, si->string) == 0) { | |
| if (is_label_file_local(si->string) == SUCCEEDED) { | |
| if(sta->file_id == l->file_id) { | |
| k = l->address; | |
| break; | |
| } | |
| else { | |
| l = l->next; | |
| continue; | |
| } | |
| } | |
| else if (is_label_section_local(si->string) == SUCCEEDED) { | |
| if (sta->section == l->section) { | |
| k = l->address; | |
| break; | |
| } | |
| else { | |
| l = l->next; | |
| continue; | |
| } | |
| } | |
| else { | |
| k = l->address; | |
| break; | |
| } | |
| } | |
| l = l->next; | |
| } | |
| /* is the reference relative? */ | |
| if (l != NULL && sta->relative_references == YES) | |
| k = k - sta->memory_address - 1; | |
| } | |
| } | |
| if (l == NULL) { | |
| fprintf(stderr, "%s:%s:%d: PARSE_STACK: Unresolved reference to \"%s\".\n", get_file_name(sta->file_id), | |
| get_source_file_name(sta->file_id, sta->file_id_source), sta->linenumber, si->string); | |
| return FAILED; | |
| } | |
| /* 65816 cpu bank fix */ | |
| if (sta->type == STACKS_TYPE_24BIT && l->status == LABEL_STATUS_LABEL) | |
| k += get_snes_pc_bank(l); | |
| if (l->status == LABEL_STATUS_STACK) { | |
| /* here we abuse the stack item structure's members */ | |
| si->value = l->address; | |
| si->sign = l->file_id; | |
| si->type = STACK_ITEM_TYPE_STACK; | |
| } | |
| else { | |
| /* transform the label string into a value */ | |
| si->value = k; | |
| si->type = STACK_ITEM_TYPE_VALUE; | |
| } | |
| } | |
| si++; | |
| g++; | |
| } | |
| return SUCCEEDED; | |
| } | |
| int get_snes_pc_bank(struct label *l) { | |
| int x, k; | |
| /* do we override the user's banking scheme (.HIROM/.LOROM)? */ | |
| if (snes_mode != 0) { | |
| /* leave labels in RAM untouched */ | |
| if((l->address < 0x2000 && l->bank == 0) || l->bank == 0x7e || l->bank == 0x7f) { | |
| x = l->bank; | |
| } | |
| else { | |
| /* use rom_address instead of address, as address points to | |
| the position in destination machine's memory, not in rom */ | |
| k = l->rom_address; | |
| if (snes_rom_mode == SNES_ROM_MODE_HIROM) | |
| x = k / 0x10000; | |
| else | |
| x = k / 0x8000; | |
| } | |
| } | |
| /* or just use the user's banking chart */ | |
| else { | |
| x = l->bank; | |
| } | |
| x = (x + l->base) << 16; | |
| return x; | |
| } | |
| int correct_65816_library_sections(void) { | |
| struct section *s; | |
| struct label *l; | |
| s = sec_first; | |
| while (s != NULL) { | |
| if (s->library_status == ON && s->base_defined == ON) { | |
| l = labels_first; | |
| while (l != NULL) { | |
| if (l->section_status == ON && l->section == s->id) | |
| l->base = s->base; | |
| l = l->next; | |
| } | |
| } | |
| s = s->next; | |
| } | |
| return SUCCEEDED; | |
| } | |
| /* is the label of form -, --, ---, +, ++, +++, ... ? */ | |
| int is_label_anonymous(char *l) { | |
| int x, y; | |
| char c; | |
| if (strcmp(l, "_f") == 0 || strcmp(l, "_F") == 0 || strcmp(l, "_b") == 0 || strcmp(l, "_B") == 0 || strcmp(l, "__") == 0) | |
| return SUCCEEDED; | |
| c = *l; | |
| if (!(c == '-' || c == '+')) | |
| return FAILED; | |
| for (x = strlen(l), y = 0; y < x; y++) { | |
| if (*(l + y) != c) | |
| return FAILED; | |
| } | |
| return SUCCEEDED; | |
| } | |
| int is_label_section_local(char* l) | |
| { | |
| if(strncmp("__local_", l, 8) == 0) return SUCCEEDED; | |
| return FAILED; | |
| } | |
| int is_label_file_local(char* l) | |
| { | |
| if(strncmp("__tccs_", l, 7) == 0) return SUCCEEDED; | |
| return FAILED; | |
| } | |
| struct label *get_closest_anonymous_label(char *name, int rom_address, int file_id, struct label *l, int section_status, int section) { | |
| struct label *closest = NULL; | |
| int d = 999999, e; | |
| if (strcmp(name, "_b") == 0 || strcmp(name, "_B") == 0) { | |
| while (l != NULL) { | |
| if (strcmp("__", l->name) == 0 && file_id == l->file_id && section_status == l->section_status) { | |
| if (section_status == OFF || (section_status == ON && section == l->section)) { | |
| e = rom_address - l->rom_address; | |
| if (e >= 0 && e < d) { | |
| closest = l; | |
| d = e; | |
| } | |
| } | |
| } | |
| l = l->next; | |
| } | |
| return closest; | |
| } | |
| if (strcmp(name, "_f") == 0 || strcmp(name, "_F") == 0) { | |
| while (l != NULL) { | |
| if (strcmp("__", l->name) == 0 && file_id == l->file_id && section_status == l->section_status) { | |
| if (section_status == OFF || (section_status == ON && section == l->section)) { | |
| e = l->rom_address - rom_address; | |
| if (e > 0 && e < d) { | |
| closest = l; | |
| d = e; | |
| } | |
| } | |
| } | |
| l = l->next; | |
| } | |
| return closest; | |
| } | |
| /* -, --, +, ++, ... */ | |
| while (l != NULL) { | |
| if (strcmp(name, l->name) == 0 && file_id == l->file_id && section_status == l->section_status) { | |
| if (section_status == OFF || (section_status == ON && section == l->section)) { | |
| if (name[0] == '-') { | |
| e = rom_address - l->rom_address; | |
| if (e >= 0 && e < d) { | |
| closest = l; | |
| d = e; | |
| } | |
| } | |
| else { | |
| e = l->rom_address - rom_address; | |
| if (e > 0 && e < d) { | |
| closest = l; | |
| d = e; | |
| } | |
| } | |
| } | |
| } | |
| l = l->next; | |
| } | |
| return closest; | |
| } |