acme/src/basics.c

275 lines
6.4 KiB
C

// ACME - a crossassembler for producing 6502/65c02/65816 code.
// Copyright (C) 1998-2014 Marco Baye
// Have a look at "acme.c" for further info
//
// basic assembly stuff
#include <stdlib.h>
#include <stdio.h>
#include "config.h"
#include "cpu.h"
#include "basics.h"
#include "alu.h"
#include "dynabuf.h"
#include "input.h"
#include "global.h"
#include "output.h"
#include "tree.h"
#include "typesystem.h"
// constants
#define USERMSG_DYNABUF_INITIALSIZE 80
static const char s_08[] = "08";
#define s_8 (s_08 + 1) // Yes, I know I'm sick
#define s_16 (s_65816 + 3) // Yes, I know I'm sick
// variables
static struct dynabuf *user_message; // dynamic buffer (!warn/error/serious)
// helper function for !8, !16, !24 and !32 pseudo opcodes
static enum eos output_objects(void (*fn)(intval_t))
{
do
fn(ALU_any_int());
while (Input_accept_comma());
return ENSURE_EOS;
}
// Insert 8-bit values ("!08" / "!8" / "!by" / "!byte" pseudo opcode)
static enum eos PO_08(void)
{
return output_objects(Output_8b);
}
// Insert 16-bit values ("!16" / "!wo" / "!word" pseudo opcode)
static enum eos PO_16(void)
{
return output_objects(Output_16b);
}
// Insert 24-bit values ("!24" pseudo opcode)
static enum eos PO_24(void)
{
return output_objects(Output_24b);
}
// Insert 32-bit values ("!32" pseudo opcode)
static enum eos PO_32(void)
{
return output_objects(Output_32b);
}
// Include binary file
static enum eos PO_binary(void)
{
FILE *fd;
int byte;
intval_t size = -1, // means "not given" => "until EOF"
skip = 0;
// if file name is missing, don't bother continuing
if (Input_read_filename(TRUE))
return SKIP_REMAINDER;
// try to open file
fd = fopen(GLOBALDYNABUF_CURRENT, FILE_READBINARY);
if (fd == NULL) {
Throw_error(exception_cannot_open_input_file);
return SKIP_REMAINDER;
}
// read optional arguments
if (Input_accept_comma()) {
if (ALU_optional_defined_int(&size)
&& (size < 0))
Throw_serious_error("Negative size argument.");
if (Input_accept_comma())
ALU_optional_defined_int(&skip); // read skip
}
// check whether including is a waste of time
if ((size >= 0) && (pass_undefined_count || pass_real_errors)) {
Output_fake(size); // really including is useless anyway
} else {
// really insert file
fseek(fd, skip, SEEK_SET); // set read pointer
// if "size" non-negative, read "size" bytes.
// otherwise, read until EOF.
while (size != 0) {
byte = getc(fd);
if (byte == EOF)
break;
Output_byte(byte);
size--;
}
// if more should have been read, warn and add padding
if (size > 0) {
Throw_warning("Padding with zeroes.");
do
Output_byte(0);
while (--size);
}
}
fclose(fd);
// if verbose, produce some output
if ((pass_count == 0) && (Process_verbosity > 1)) {
int amount = vcpu_get_statement_size();
printf("Loaded %d (0x%04x) bytes from file offset %ld (0x%04lx).\n",
amount, amount, skip, skip);
}
return ENSURE_EOS;
}
// Reserve space by sending bytes of given value ("!fi" / "!fill" pseudo opcode)
static enum eos PO_fill(void)
{
intval_t fill = FILLVALUE_FILL,
size = ALU_defined_int();
if (Input_accept_comma())
fill = ALU_any_int();
while (size--)
Output_8b(fill);
return ENSURE_EOS;
}
// force explicit label definitions to set "address" flag ("!addr"). Has to be re-entrant.
static enum eos PO_addr(void) // Now GotByte = illegal char
{
SKIPSPACE();
if (GotByte == CHAR_SOB) {
typesystem_force_address_block();
return ENSURE_EOS;
}
typesystem_force_address_statement(TRUE);
return PARSE_REMAINDER;
}
// show user-defined message
static enum eos throw_string(const char prefix[], void (*fn)(const char *))
{
struct result_t result;
DYNABUF_CLEAR(user_message);
DynaBuf_add_string(user_message, prefix);
do {
if (GotByte == '"') {
// parse string
GetQuotedByte(); // read initial character
// send characters until closing quote is reached
while (GotByte && (GotByte != '"')) {
DYNABUF_APPEND(user_message, GotByte);
GetQuotedByte();
}
if (GotByte == CHAR_EOS)
return AT_EOS_ANYWAY;
// after closing quote, proceed with next char
GetByte();
} else {
// parse value
ALU_any_result(&result);
if (result.flags & MVALUE_IS_FP) {
// floating point
if (result.flags & MVALUE_DEFINED) {
char buffer[40];
// write up to 30 significant characters.
// remaining 10 should suffice for sign,
// decimal point, exponent, terminator etc.
sprintf(buffer, "%.30g", result.val.fpval);
DynaBuf_add_string(user_message, buffer);
} else {
DynaBuf_add_string(user_message, "<UNDEFINED FLOAT>");
}
} else {
// integer
if (result.flags & MVALUE_DEFINED) {
char buffer[32]; // 11 for dec, 8 for hex
sprintf(buffer, "%ld (0x%lx)", (long) result.val.intval, (long) result.val.intval);
DynaBuf_add_string(user_message, buffer);
} else {
DynaBuf_add_string(user_message, "<UNDEFINED INT>");
}
}
}
} while (Input_accept_comma());
DynaBuf_append(user_message, '\0');
fn(user_message->buffer);
return ENSURE_EOS;
}
////
//static enum eos PO_debug(void)
//static enum eos PO_info(void)
//static enum eos PO_print(void)
//{
// return throw_string();
//}
// throw warning as given in source code
static enum eos PO_warn(void)
{
return throw_string("!warn: ", Throw_warning);
}
// throw error as given in source code
static enum eos PO_error(void)
{
return throw_string("!error: ", Throw_error);
}
// throw serious error as given in source code
static enum eos PO_serious(void)
{
return throw_string("!serious: ", Throw_serious_error);
}
// pseudo ocpode table
static struct node_t pseudo_opcodes[] = {
PREDEFNODE(s_08, PO_08),
PREDEFNODE(s_8, PO_08),
PREDEFNODE("by", PO_08),
PREDEFNODE("byte", PO_08),
PREDEFNODE(s_16, PO_16),
PREDEFNODE("wo", PO_16),
PREDEFNODE("word", PO_16),
PREDEFNODE("24", PO_24),
PREDEFNODE("32", PO_32),
PREDEFNODE("bin", PO_binary),
PREDEFNODE("binary", PO_binary),
PREDEFNODE("fi", PO_fill),
PREDEFNODE("fill", PO_fill),
PREDEFNODE("addr", PO_addr),
PREDEFNODE("address", PO_addr),
// PREDEFNODE("debug", PO_debug),
// PREDEFNODE("info", PO_info),
// PREDEFNODE("print", PO_print),
PREDEFNODE("warn", PO_warn),
PREDEFNODE(s_error, PO_error),
PREDEFLAST("serious", PO_serious),
// ^^^^ this marks the last element
};
// register pseudo opcodes and create dynamic buffer
void Basics_init(void)
{
user_message = DynaBuf_create(USERMSG_DYNABUF_INITIALSIZE);
Tree_add_table(&pseudo_opcode_tree, pseudo_opcodes);
}