Created
March 26, 2017 07:25
-
-
Save alberthdev/4f935f179b082c94893ea06f778d892c to your computer and use it in GitHub Desktop.
directive.cpp w/ debug statements for crash
This file contains hidden or 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
/* TEST INPUT: | |
* .addinstr 0 0 0000000000000000000000000000 | |
* | |
* Save to crash.asm, and run (for extra fun): | |
* ./spasm crash.asm | |
* valgrind --leak-check=full ./spasm crash.asm | |
* | |
* This contains the bug fix - to remove it, search | |
* for "BUG FIX IS ON THE NEXT LINE", and comment out the | |
* following line. | |
*/ | |
#include "stdafx.h" | |
#include "spasm.h" | |
#include "directive.h" | |
#include "utils.h" | |
#include "pass_one.h" | |
#include "pass_two.h" | |
#include "parser.h" | |
#include "opcodes.h" | |
#include "console.h" | |
#include "expand_buf.h" | |
#include "errors.h" | |
/* | |
* Handles a directive, returns the | |
* new location in the file | |
*/ | |
char *handle_directive (const char *ptr) { | |
static const char *dirs[] = {"db", "dw", "end", "org", "byte", "word", "fill", "block", "addinstr", | |
"echo", "error", "list", "nolist", "equ", "show", "option", "seek", "assume", "dl", "long", NULL}; | |
char name_buf[32]; | |
int dir; | |
//same deal as handle_preop, just with directives instead | |
bool valid_directive = false; | |
unsigned name_len = 0; | |
while (ptr[name_len] != 0 && !isspace(ptr[name_len])) { | |
name_len++; | |
} | |
// If longer than name_buf, it can't be a valid directive. | |
if (name_len < sizeof(name_buf)) { | |
// Copy string for comparing against | |
memcpy(name_buf, ptr, name_len); | |
name_buf[name_len] = 0; | |
dir = 0; | |
while (dirs[dir]) { | |
if (!strcasecmp(dirs[dir], name_buf)) { | |
valid_directive = true; | |
break; | |
} | |
dir++; | |
} | |
} | |
if (!valid_directive) | |
return handle_opcode_or_macro ((char *) ptr - 1); | |
ptr = skip_whitespace(&ptr[name_len]); | |
switch (dir) { | |
case 0: //DB | |
case 4: //BYTE | |
{ | |
ptr = parse_emit_string (ptr, ES_BYTE, NULL); | |
break; | |
} | |
case 1: //DW | |
case 5: //WORD | |
{ | |
ptr = parse_emit_string (ptr, ES_WORD, NULL); | |
break; | |
} | |
case 18: //DL | |
case 19: //LONG | |
{ | |
ptr = parse_emit_string (ptr, ES_LONG, NULL); | |
break; | |
} | |
case 3: //ORG | |
{ | |
int value; | |
char value_str[256] = ""; | |
bool fResult = read_expr(&ptr, value_str, ""); | |
if (fResult == true) | |
{ | |
if (parse_num(value_str, &value) == true) | |
{ | |
if (value < 0) | |
{ | |
SetLastSPASMError(SPASM_ERR_INVALID_ADDRESS, skip_whitespace(value_str)); | |
} | |
else | |
{ | |
program_counter = value; | |
} | |
} | |
} | |
if ((fResult == false) || (strlen(skip_whitespace(value_str)) == 0)) | |
{ | |
SetLastSPASMError(SPASM_ERR_VALUE_EXPECTED); | |
} | |
break; | |
} | |
case 6: //FILL | |
case 7: //BLOCK | |
{ | |
int size, fill_value; | |
char szSize[256], szFill[256]; | |
bool old_listing_on = listing_on; | |
//listing_on = false; | |
read_expr (&ptr, szSize, ","); | |
parse_num (szSize, &size); | |
ptr = skip_whitespace (ptr); | |
if (*ptr == ',') | |
ptr++; | |
if (read_expr (&ptr, szFill, "")) { | |
//if there's a value to fill the block with, then handle that | |
parse_num (szFill, &fill_value); | |
} else { | |
//otherwise use 0 | |
fill_value = 0; | |
} | |
if (size < 0) { | |
SetLastSPASMError(SPASM_ERR_SIZE_MUST_BE_POSITIVE, szSize); | |
listing_on = old_listing_on; | |
break; | |
} | |
if (fill_value < -128 || fill_value > 255) | |
{ | |
SetLastSPASMWarning(SPASM_WARN_TRUNCATING_8); | |
} | |
program_counter += size; | |
stats_datasize += size; | |
while (size-- > 0) | |
write_out (fill_value & 0xFF); | |
//listing_on = old_listing_on; | |
break; | |
} | |
case 8: //ADDINSTR | |
{ | |
instr *instr = (struct _instr *) malloc (sizeof (struct _instr)); | |
char word[256]; | |
int result; | |
char *mnemonic = NULL; | |
size_t i, base = 0, size_left; | |
int j; | |
opcode *last_opcode = NULL, *curr_opcode = all_opcodes, *new_opcode; | |
memset (instr, 0, sizeof (struct _instr)); | |
// Mnemonic | |
if (!read_expr (&ptr, word, " \t")) goto addinstr_fail; | |
mnemonic = strdup (word); | |
// Args | |
if (!read_expr (&ptr, word, " \t")) goto addinstr_fail; | |
reduce_string (word); | |
instr->args = strdup (word);DPRINT("instr->args has been init'd @ 0x%0x\n", instr->args); | |
// Instruction data | |
if (!read_expr (&ptr, word, " \t")) goto addinstr_fail; | |
DPRINT("passed instruction data check\n"); | |
DPRINT("instr->args check1 @ 0x%0x\n", instr->args); | |
conv_hex (word, word + strlen (word), &result); | |
DPRINT("instr->args check2 @ 0x%0x\n", instr->args); | |
instr->instr_size = strlen (word) / 2; | |
/* BUG FIX IS ON THE NEXT LINE */ | |
if (instr->instr_size > 8) goto addinstr_fail; | |
DPRINT("instr->instr_size = %d\n", instr->instr_size); | |
DPRINT("instr->args check3 @ 0x%0x\n", instr->args); | |
for (j = instr->instr_size - 1; j >= 0; j--) { | |
DPRINT("instr->args check4 @ 0x%0x\n", instr->args); | |
instr->instr_data[instr->instr_size - j - 1] = (result >> (j * 8)) & 0xFF; | |
} | |
// Size | |
if (!read_expr (&ptr, word, " \t")) goto addinstr_fail; | |
DPRINT("passed size1 check\n"); | |
if (!parse_num (word, &instr->size)) goto addinstr_fail; | |
DPRINT("passed size2 check\n"); | |
// Class | |
read_expr (&ptr, word, " \t"); | |
// Extended | |
read_expr (&ptr, word, " \t"); | |
// End data ... | |
if (read_expr (&ptr, word, " \t")) { | |
int output; | |
conv_hex (word, word + strlen (word), &output); | |
instr->end_data = (unsigned char) output; | |
instr->has_end_data = true; | |
} | |
size_left = instr->size - instr->instr_size; | |
while ((i = strcspn (&instr->args[base], "*")) + base != strlen (instr->args)) { | |
switch (size_left - instr->has_end_data) { | |
case 2: ((char *) instr->args)[base+i] = '*'; break; | |
case 1: ((char *) instr->args)[base+i] = '&'; break; | |
default: | |
((char *) instr->args)[base+i] = '&'; break; | |
//show_error ("Invalid wildcard type in ADDRINSTR"); | |
//goto addinstr_fail; | |
break; | |
} | |
size_left -= 2; | |
base += i + 1; | |
} | |
new_opcode = (opcode *) malloc (sizeof (opcode)); | |
new_opcode->name = mnemonic; | |
new_opcode->num_instrs = 1; | |
new_opcode->use_count = 0; | |
new_opcode->instrs = instr; | |
new_opcode->is_added = true; | |
while (curr_opcode) { | |
if (strcasecmp (mnemonic, curr_opcode->name) == 0) | |
break; | |
last_opcode = curr_opcode; | |
curr_opcode = curr_opcode->next; | |
} | |
if (curr_opcode == NULL) { | |
last_opcode->next = new_opcode; | |
new_opcode->next = NULL; | |
} else { | |
new_opcode->next = curr_opcode; | |
if (last_opcode) | |
last_opcode->next = new_opcode; | |
else | |
all_opcodes = new_opcode; | |
} | |
// You can ignore class, etc | |
ptr = skip_to_line_end (ptr); | |
break; | |
addinstr_fail: | |
DPRINT("jumped to addinstr_fail!\n", instr->args); | |
DPRINT("instr->args @ 0x%0x\n", instr->args); | |
SetLastSPASMError(SPASM_ERR_INVALID_ADDINSTR); | |
DPRINT("instr->args @ 0x%0x\n", instr->args); | |
if (mnemonic) { free((void *) mnemonic); DPRINT("freed mnemonic\n"); } | |
if (instr && instr->args) { DPRINT("freeing instr->args @ 0x%0x\n", instr->args); free ((void *) instr->args); DPRINT("freed instr->args\n"); } | |
if (instr) { free (instr); DPRINT("freed instr struct\n"); } | |
ptr = skip_to_line_end(ptr); | |
break; | |
} | |
case 9: //ECHO | |
{ | |
if (ptr[0] == '>') | |
{ | |
char target_format[2] = "w"; | |
FILE *echo_target; | |
char filename[MAX_PATH]; | |
char temp_filename[MAX_PATH]; | |
define_t *define; | |
if ((++ptr)[0] == '>') | |
target_format[0] = 'a'; | |
ptr = skip_whitespace (ptr + 1); | |
if (is_end_of_code_line (ptr)) { | |
SetLastSPASMError(SPASM_ERR_FILENAME_EXPECTED); | |
return NULL; | |
} | |
read_expr (&ptr, filename, " \t"); | |
// Is the filename given a macro? | |
if ((define = search_defines (filename))) | |
strncpy (filename, skip_whitespace(define->contents), sizeof (filename)); | |
reduce_string(filename); | |
if (is_abs_path(filename)) { | |
strncpy(temp_filename, skip_whitespace (filename), sizeof (temp_filename)); | |
} else { | |
strncpy(temp_filename, temp_path, sizeof (temp_filename)); | |
strncat(temp_filename, "/", sizeof (temp_filename) - 1); | |
strncat(temp_filename, skip_whitespace (filename), sizeof (temp_filename) - 1); | |
} | |
echo_target = fopen (fix_filename (temp_filename), target_format); | |
if (echo_target == NULL) { | |
SetLastSPASMError(SPASM_ERR_NO_ACCESS, filename); | |
return NULL; | |
} | |
//if the output's redirected to a file, process it now | |
WORD orig_attributes = save_console_attributes(); | |
set_console_attributes (COLOR_GREEN); | |
ptr = parse_emit_string (ptr, ES_ECHO, echo_target); | |
restore_console_attributes(orig_attributes); | |
} else { | |
char expr[256]; | |
read_expr (&ptr, expr, ""); | |
// If it craps out for some reason, save it for the next pass | |
int session = StartSPASMErrorSession(); | |
parse_emit_string (expr, ES_ECHO, NULL); | |
if (IsSPASMErrorSessionFatal(session) == true) | |
{ | |
add_pass_two_output (expr, OUTPUT_ECHO); | |
} | |
else if (GetSPASMErrorSessionErrorCount(session) > 0) | |
{ | |
WORD orig_attributes = save_console_attributes(); | |
set_console_attributes(COLOR_GREEN); | |
int internal_session = StartSPASMErrorSession(); | |
parse_emit_string (expr, ES_ECHO, stdout); | |
restore_console_attributes(orig_attributes); | |
EndSPASMErrorSession(internal_session); | |
ReplaySPASMErrorSession(session); | |
} | |
else | |
{ | |
expand_buf_t *echo = eb_init(256); | |
parse_emit_string (expr, ES_FCREATE, echo); | |
char *echo_string = eb_extract(echo); | |
eb_free(echo); | |
char *expanded_string = escape_string(echo_string); | |
free(echo_string); | |
add_pass_two_output (expanded_string, OUTPUT_ECHO); | |
free(expanded_string); | |
} | |
EndSPASMErrorSession(session); | |
} | |
break; | |
} | |
case 10: //ERROR | |
{ | |
expand_buf_t *eb = eb_init(64); | |
ptr = parse_emit_string(ptr, ES_FCREATE, eb); | |
char *error_str = eb_extract(eb); | |
eb_free(eb); | |
SetLastSPASMError(SPASM_ERR_CUSTOM, error_str); | |
free(error_str); | |
break; | |
} | |
case 11: //LIST | |
{ | |
//if listing was off already, then the listing | |
// for the start of this line wouldn't have been | |
// written, so make sure the end doesn't get | |
// written either | |
if (!listing_on) | |
listing_for_line_done = true; | |
listing_on = true; | |
break; | |
} | |
case 12: //NOLIST | |
{ | |
//if listing is on, then it would've written | |
// the starting stuff for this line already, | |
// so take that out | |
if ((mode & MODE_LIST) && listing_on) | |
listing_offset -= 14; | |
listing_on = false; | |
break; | |
} | |
case 13: //EQU | |
{ | |
// Finally, a proper .equ! | |
int value; | |
char value_str[256]; | |
read_expr (&ptr, value_str, ""); | |
if (!parse_num (value_str, &value) && parser_forward_ref_err) { | |
} else { | |
if (last_label == NULL) | |
SetLastSPASMError(SPASM_ERR_EQUATE_MISSING_LABEL); | |
else | |
last_label->value = value; | |
} | |
break; | |
} | |
case 14: //SHOW | |
{ | |
char name[256]; | |
define_t *define; | |
//get the name | |
read_expr (&ptr, name, ""); | |
define = search_defines (name); | |
if (define == NULL) { | |
//if it doesn't exist yet, save it for the second pass | |
add_pass_two_output (name, OUTPUT_SHOW); | |
} else { | |
//otherwise, show it now | |
show_define (define); | |
} | |
break; | |
} | |
case 15: //OPTION | |
{ | |
char *word = NULL; | |
arg_context_t context = ARG_CONTEXT_INITIALIZER; | |
while ((word = extract_arg_string(&ptr, &context)) != NULL) | |
{ | |
char name[256], *expr = word; | |
char *define_name; | |
define_t *define; | |
read_expr(&expr, name, "="); | |
if (*expr == '=') | |
{ | |
expr++; | |
} | |
if (!(isalpha(name[0]))) { | |
SetLastSPASMError(SPASM_ERR_INVALID_OPTION, name); | |
return (char *) ptr; | |
} | |
if (is_end_of_code_line (skip_whitespace (expr))) | |
expr = strdup ("1"); | |
else { | |
//if (!parse_num (expr, NULL)) | |
// return NULL; | |
expr = strdup (expr); | |
} | |
if (strlen (name) == 0) { | |
show_error ("Invalid option statement"); | |
return NULL; | |
} | |
define_name = (char *) malloc (strlen (name) + 3); | |
strcat (strcpy (define_name, "__"), name); | |
define = add_define (define_name, NULL); | |
set_define (define, expr, -1, false); | |
free(expr); | |
} | |
break; | |
} | |
case 16: //SEEK | |
{ | |
int value; | |
char value_str[256]; | |
read_expr (&ptr, value_str, ""); | |
parse_num (value_str, (int *) &value); | |
//printf("value_str: %s\npc: %d\n", value_str, program_counter); | |
if (value > program_counter && (value - program_counter > output_buf_size - (out_ptr - output_contents))) | |
show_fatal_error ("Seek location %d out of bounds", value); | |
else if (value < program_counter && (value - (int) program_counter + (out_ptr - output_contents) < 0)) | |
show_fatal_error ("Seek value %d too small", value); | |
out_ptr += value - ((int) program_counter); | |
//printf("base: %p; ptr: %p\n", output_contents, out_ptr); | |
program_counter = value; | |
break; | |
} | |
case 17: //ASSUME | |
{ | |
char args[256]; | |
read_expr(&ptr, args, ""); | |
char word[256]; | |
char *value_str = args; | |
read_expr(&value_str, word, "="); | |
int value = 1; | |
bool success = true; | |
if (*value_str == '=') { | |
success = parse_num(value_str+1, &value); | |
} | |
if (!(mode & MODE_EZ80) || strcasecmp(word, "adl") || !success) { | |
SetLastSPASMError(SPASM_ERR_INVALID_OPTION, args); | |
return (char *)ptr; | |
} | |
adl_mode = (value != 0); | |
break; | |
} | |
} | |
return (char *) ptr; | |
} | |
/* | |
* Shows contents of a #define | |
*/ | |
void show_define (define_t *define) { | |
WORD console_attrib = save_console_attributes(); | |
set_console_attributes (COLOR_BLUE); | |
fputs (define->name, stdout); | |
if (define->num_args > 0) { | |
int i; | |
putchar ('('); | |
for (i = 0; i < define->num_args; i++) { | |
if (i != 0) fputs (", ", stdout); | |
fputs (define->args[i], stdout); | |
} | |
putchar (')'); | |
} | |
if (define->contents != NULL) | |
{ | |
printf (": %s", define->contents); | |
} | |
putchar ('\n'); | |
restore_console_attributes(console_attrib); | |
#ifdef WIN32 | |
if (define->contents != NULL) | |
{ | |
OutputDebugString(define->contents); | |
OutputDebugString("\n"); | |
} | |
#endif | |
} | |
/* | |
* Parses a list of values and strings, | |
* either prints them to the output, | |
* writes them to a file, or adds them | |
* to the program output | |
*/ | |
char *parse_emit_string (const char *ptr, ES_TYPE type, void *echo_target) { | |
static int level = 0; | |
char *word = NULL; | |
int session; | |
bool fWasParsed; | |
char *name_end; | |
level++; | |
arg_context_t context = ARG_CONTEXT_INITIALIZER; | |
while ((word = extract_arg_string(&ptr, &context)) != NULL) | |
{ | |
// handle strings | |
if (word[0] == '"') | |
{ | |
char *next = next_expr (word, EXPR_DELIMS); | |
if (*next != '\0') | |
goto echo_error; | |
reduce_string (word); | |
if (type == ES_ECHO) | |
{ | |
if (echo_target != NULL) | |
{ | |
fprintf ((FILE *) echo_target, "%s", word); | |
} | |
} | |
else if (type == ES_FCREATE) { | |
eb_append((expand_buf_t *) echo_target, word, strlen(word)); | |
} else { | |
int i; | |
for (i = 0; word[i]; i++) { | |
if ((mode & MODE_NORMAL) || (mode & MODE_LIST)) | |
write_out (word[i]); | |
stats_datasize++; | |
program_counter++; | |
} | |
} | |
} else { | |
int value; | |
session = StartSPASMErrorSession(); | |
fWasParsed = parse_num(word, &value); | |
if (fWasParsed == true) | |
{ | |
switch (type) | |
{ | |
case ES_ECHO: | |
{ | |
if (echo_target != NULL) | |
{ | |
fprintf ((FILE *) echo_target, "%d", value); | |
} | |
break; | |
} | |
#ifdef USE_BUILTIN_FCREATE | |
case ES_FCREATE: | |
{ | |
char buffer[256]; | |
sprintf_s(buffer, "%d", value); | |
eb_append((expand_buf_t *) echo_target, buffer, -1); | |
break; | |
} | |
#endif | |
case ES_BYTE: | |
{ | |
write_arg(value, ARG_NUM_8, 0, 0); | |
stats_datasize++; | |
program_counter++; | |
break; | |
} | |
case ES_WORD: | |
{ | |
write_arg(value, ARG_NUM_16, 0, 0); | |
stats_datasize+=2; | |
program_counter+=2; | |
break; | |
} | |
case ES_LONG: | |
{ | |
write_arg(value, ARG_NUM_24, 0, 0); | |
stats_datasize+=3; | |
program_counter+=3; | |
break; | |
} | |
} | |
ReplaySPASMErrorSession(session); | |
} | |
else if (IsSPASMErrorSessionFatal(session) == false && type != ES_FCREATE) | |
{ | |
switch (type) | |
{ | |
case ES_ECHO: | |
break; | |
case ES_BYTE: | |
{ | |
add_pass_two_expr(word, ARG_NUM_8, 0, 0); | |
stats_datasize++; | |
program_counter++; | |
break; | |
} | |
case ES_WORD: | |
{ | |
add_pass_two_expr(word, ARG_NUM_16, 0, 0); | |
stats_datasize+=2; | |
program_counter+=2; | |
break; | |
} | |
case ES_LONG: | |
{ | |
add_pass_two_expr(word, ARG_NUM_24, 0, 0); | |
stats_datasize += 3; | |
program_counter += 3; | |
break; | |
} | |
} | |
ReplayFatalSPASMErrorSession(session); | |
} | |
else | |
{ | |
char name[256]; | |
char *next; | |
define_t *define; | |
name_end = word; | |
//printf("error occured: %d, forward: %d, value: %d\n", error_occurred, parser_forward_ref_err, value); | |
read_expr (&name_end, name, "("); | |
//printf("Looking up %s\n", name); | |
next = name_end; | |
read_expr (&next, NULL, ")"); | |
if (*next != '\0') | |
goto echo_error; | |
if ((define = search_defines (name))) { | |
char *expr; | |
list_t *args = NULL; | |
//handle defines | |
if (define->contents == NULL) | |
{ | |
SetLastSPASMError(SPASM_ERR_ARG_USED_WITHOUT_VALUE, name); | |
} | |
if (*(name_end - 1) == '(') name_end--; | |
name_end = parse_args (name_end, define, &args); | |
if (!name_end) | |
return (char *) ptr; | |
expr = parse_define (define); | |
if (expr != NULL) | |
{ | |
arg_context_t nested_context = ARG_CONTEXT_INITIALIZER; | |
// Is it some kind of argument list? | |
const char *nested_expr = expr; | |
extract_arg_string(&nested_expr, &nested_context); | |
if (extract_arg_string(&nested_expr, &nested_context) != NULL) | |
{ | |
// if it is, plug it in to the emit string | |
parse_emit_string(expr, type, echo_target); | |
} | |
else | |
{ | |
if (IsErrorInSPASMErrorSession(session, SPASM_ERR_EXCEEDED_RECURSION_LIMIT) == false) | |
{ | |
int inner_session = StartSPASMErrorSession(); | |
parse_emit_string(expr, type, echo_target); | |
if (IsSPASMErrorSessionFatal(inner_session)) | |
{ | |
EndSPASMErrorSession(inner_session); | |
AddSPASMErrorSessionAnnotation(session, _T("Error during evaluation of macro '%s'"), define->name); | |
ReplaySPASMErrorSession(session); | |
} | |
else | |
{ | |
EndSPASMErrorSession(inner_session); | |
} | |
//ReplaySPASMErrorSession(session); | |
} | |
else | |
{ | |
ReplaySPASMErrorSession(session); | |
} | |
} | |
free (expr); | |
} | |
remove_arg_set (args); | |
} | |
else | |
{ | |
echo_error: | |
switch (type) | |
{ | |
case ES_ECHO: | |
{ | |
if (echo_target != NULL) | |
{ | |
fprintf((FILE *) echo_target, "(error)"); | |
} | |
break; | |
} | |
case ES_FCREATE: | |
{ | |
SetLastSPASMError(SPASM_ERR_LABEL_NOT_FOUND, word); | |
eb_append((expand_buf_t *) echo_target, "(error)", -1); | |
break; | |
} | |
} | |
ReplaySPASMErrorSession(session); | |
} | |
} | |
EndSPASMErrorSession(session); | |
} | |
} | |
if (type == ES_ECHO && level == 1) { | |
if (echo_target == stdout) putchar ('\n'); | |
else { | |
if (echo_target != NULL) | |
{ | |
fclose ((FILE *) echo_target); | |
} | |
} | |
} | |
level--; | |
return (char *) ptr; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment