meow.git

direct.c

espurr
/* meow - 6502 assembler
 * Copyright (C) 2024-2025 ArcNyxx
 * see LICENCE file for licensing information */

#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "direct.h"
#include "label.h"
#include "meow.h"

extern char ps[0xffff];
extern uint16_t pc;

static void
org(void)
{
	char *next;
	if ((next = strtok(NULL, " \t\n\r")) == NULL) {
		meow("error: .org directive without address");
		return;
	}
	if (strtok(NULL, " \t\n\r") != NULL)
		meow("warn: .org directive followed by extraneous information");

	char *endptr;
	unsigned long num = strnum(next, &endptr);
	if (errno != 0 || num > USHRT_MAX)
		meow("warn: .org directive address too large: %s", next);
	if (endptr[0] != '\0')
		meow("warn: .org directive invalid address: %s", next);
	pc = num & 0xffff;
}

static void
data(void)
{
	char *next;
	while ((next = strtok(NULL, " \t\n\r")) != NULL) {
		char *endptr;
		long num = strnum(next, &endptr);
		if (errno != 0 || num > SHRT_MAX || num < SHRT_MIN)
			meow("warn: .data directive immediate is too large: %s",
					next);
		if (endptr[0] != '\0')
			meow("warn: .data directive immediate invalid: %s",
					next);

		if (num > SCHAR_MAX || num < SCHAR_MIN) {
			int16_t snum = num;
			ps[pc++] = snum & 0xff;
			ps[pc++] = (snum & 0xff00) >> 2;
		} else {
			ps[pc++] = (int8_t)num;
		}
	}
}

static void
addr(void)
{
	char *next;
	while ((next = strtok(NULL, " \t\n\r")) != NULL) {
		bool zp = next[0] == '!';
		if (islabel(next)) {
			labeladd(next + zp, pc, zp ? BIT8 : BIT16);
			pc += 1 + !zp;
			continue;
		}

		char *endptr;
		long num = strnum(next + zp, &endptr);
		if (endptr[0] == '\0')
			meow("warn: .addr directive address invalid: %s",
					next);

		if (zp) {
			if (errno != 0 || num > SCHAR_MAX || num < SCHAR_MIN)
				meow("warn: .addr directive zero page address "
						"is out of 8-bit range: %s",
						next);
			ps[pc++] = (int8_t)num;
		} else {
			if (errno != 0 || num > SHRT_MAX || num < SHRT_MIN)
				meow("warn: .addr directive address is out of "
						"16-bit range: %s", next);
			int16_t snum = num;
			ps[pc++] = snum & 0xff;
			ps[pc++] = (snum & 0xff00) >> 2;
		}
	}
}

static void
str(void)
{
	char *str;
	if ((str = strtok(NULL, "\n\r")) == NULL)
		return;
	for (size_t i = 0; str[i] != '\0'; ++i) {
		if (str[i] == '\\') {
			switch (str[++i]) {
			case '\\':
				ps[pc++] = '\\';
				break;
			case 'n':
				ps[pc++] = '\n';
				break;
			default:
				meow("warn: .str directive invalid escape "
						"sequence");
			}
		} else {
			ps[pc++] = str[i];
		}
	}
}

void
direct(const char *dir)
{
	if (!strcasecmp(dir, "org"))
		org();
	else if (!strcasecmp(dir, "data"))
		data();
	else if (!strcasecmp(dir, "addr"))
		addr();
	else if (!strcasecmp(dir, "str"))
		str();
	else
		meow("error: unrecognised directive: %s", dir);
}