meow.git
instruct.c
/* meow - 6502 assembler
* Copyright (C) 2024-2025 ArcNyxx
* see LICENCE file for licensing information */
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include "instruct.h"
#include "label.h"
#include "meow.h"
#include "tables.h"
static const char *modes[] = {
"implied",
"absolute",
"absolute indexed with x",
"absolute indexed with y",
"absolute indirect",
"immediate",
"zero page",
"zero page indexed with x",
"zero page indexed with y",
"zero page indexed with x indirect",
"zero page indirect indexed with y"
};
extern char ps[0xffff];
extern uint16_t pc;
static addr_t
argument(char **arg)
{
if ((*arg) == NULL)
return IMP;
if ((*arg)[0] == '#') {
++(*arg);
return IMM;
}
if ((*arg)[0] == '!') {
char *chr = strchr(*arg + 1, ',');
if (chr == NULL) {
++(*arg);
return ZP;
}
addr_t addr;
if (toupper(chr[1]) == 'X')
addr = ZPX;
else if (toupper(chr[1]) == 'Y')
addr = ZPY;
else
return INVADDR;
if (chr[2] != '\0' || chr == *arg + 1)
return INVADDR;
++(*arg);
chr[0] = '\0';
return addr;
}
if ((*arg)[0] == '(') {
if ((*arg)[1] != '!') {
char *chr = strchr((*arg) + 1, ')');
if (chr == NULL || chr[1] != '\0' || chr == *arg + 1)
return INVADDR;
++(*arg);
chr[0] = '\0';
return ABSI;
}
char *chr = strchr((*arg) + 2, ')');
addr_t addr;
if (chr == NULL || chr == (*arg) + 2)
return INVADDR;
if (toupper(chr[-1]) == 'X' && chr[-2] == ',' &&
chr[1] == '\0') {
addr = ZPXI;
chr[-2] = '\0';
} else if (chr[1] == ',' && toupper(chr[2]) == 'Y' &&
chr[3] == '\0') {
addr = ZPYI;
chr[0] = '\0';
} else {
return INVADDR;
}
*arg += 2;
return addr;
}
char *chr = strchr((*arg), ',');
if (chr == NULL)
return ABS;
addr_t addr;
if (toupper(chr[1]) == 'X')
addr = ABSX;
else if (toupper(chr[1]) == 'Y')
addr = ABSY;
else
return INVADDR;
if (chr[2] != '\0' || chr == *arg)
return INVADDR;
chr[0] = '\0';
return addr;
}
void
instruct(char *first)
{
char *second = strtok(NULL, " \t\n\r");
if (second != NULL && strtok(NULL, " \t\n\r") != NULL)
meow("warn: instruction followed by extraneous information");
mne_t mne;
if ((mne = mnemonic(first)) == INVMNE) {
meow("error: invalid instruction mnemonic: %s", first);
return;
}
addr_t addr;
if ((addr = argument(&second)) == INVADDR) {
meow("error: invalid argument format: %s", second);
return;
}
if (addr == ABS && (mne >= BCC && mne <= BVS))
addr = REL; /* determined based on instruction */
uint8_t code;
if ((code = opcode(mne, addr)) == 0xff) {
meow("error: invalid instruction: %s using addressing mode %s",
first, modes[addr]);
return;
}
ps[pc++] = code;
if (addr == IMP)
return;
asiz_t type = BIT8;
if (addr == REL)
type = RELS;
else if (addr >= ABS && addr <= ABSI)
type = BIT16;
if (addr != IMM && islabel(second)) {
labeladd(second, pc, type);
pc += 1 + (type == BIT16);
return;
}
char *endptr;
long num = strnum(second, &endptr);
if (endptr[0] != '\0')
meow("warn: number invalid: %s", second);
if (type != BIT16) {
if (errno != 0 || num > SCHAR_MAX || num < SCHAR_MIN)
meow("warn: number is out of 8-bit range: %s", second);
ps[pc++] = (int8_t)num;
} else {
if (errno != 0 || num > SHRT_MAX || num < SHRT_MIN)
meow("warn: number is out of 16-bit range: %s", second);
int16_t snum = num;
ps[pc++] = snum & 0xff;
ps[pc++] = (snum & 0xff00) >> 2;
}
}