~swisschili/toolchain-6502

dab15a62100fca488c5bbbfed678aa63eec450d4 — swissChili 3 months ago 7acb4ce
Add code generator to assembler, re compile colors.dat with assembler
7 files changed, 175 insertions(+), 27 deletions(-)

M CMakeLists.txt
M as/CMakeLists.txt
M as/as.c
M as/map.c
A as/test/colors.s
M colors.dat
M cpu.c
M CMakeLists.txt => CMakeLists.txt +9 -0
@@ 7,6 7,15 @@ option(BUILD_ASSEMBLER ON)

include_directories(nuklear)

include(TestBigEndian)
test_big_endian(BIG_ENDIAN)

if (${BIG_ENDIAN})
	add_compile_definitions(BIG_ENDIAN)
else()
	add_compile_definitions(LITTLE_ENDIAN)
endif()

subdirs(as)

if (${GEN_INSTRUCTIONS_HEADER})

M as/CMakeLists.txt => as/CMakeLists.txt +11 -0
@@ 2,4 2,15 @@ cmake_minimum_required(VERSION 3.0)

project(6502 VERSION 0.1.0 LANGUAGES C)

include(TestBigEndian)
test_big_endian(BIG_ENDIAN)


if (${BIG_ENDIAN})
	add_compile_definitions(BIG_ENDIAN)
else()
	add_compile_definitions(LITTLE_ENDIAN)
endif()


add_executable(6502-as main.c as.h as.c map.h map.c hash.c hash.c)

M as/as.c => as/as.c +140 -25
@@ 4,6 4,8 @@
#include "../mnemonics.h"
#include "map.h"

#include <endian.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>


@@ 20,6 22,8 @@ enum

#define ERR "\033[31m"
#define RESET "\033[0m"
#define MAX_LEN (0xFFFF - 0x600)
#define MAX_INSTS (MAX_LEN / 2)

typedef struct
{


@@ 35,6 39,13 @@ typedef struct
	};
} inst_t;

typedef struct ll_node
{
	struct ll_node *last;
	char name[32];
	uint16_t addr;
} ll_node_t;

// Normal strtok() counts 2 seperators as one, ie asdf\n\njlk is 2 lines.
// This functions counts that as 3 lines, and will return an empty line in between.
char *strtok_fix(char *string, const char *token)


@@ 65,6 76,12 @@ char *strtok_fix(char *string, const char *token)
	return pos;
}

void putshort(uint16_t a, FILE *out)
{
	uint16_t le = htole16(a);
	fwrite(&le, sizeof(uint16_t), 1, out);
}

void print_inst(inst_t *arg)
{
	char *arg_types =


@@ 150,6 167,7 @@ char *parse_label(char **code)
		(*code)++;
		return start;
	}
	printf(">> lkl: **code == %c %x\n", **code, **code);

	*code = start;



@@ 302,13 320,13 @@ bool parse_arg(char *code, int am, inst_t *inst)
		{
			inst->arg_type = ARG_16;
			inst->long_arg = num;
			return true;
			return ws_end(&code);
		}
		else if ((lbl = parse_label_name(&code)))
		{
			inst->arg_type = ARG_ABS;
			strncpy(inst->label, lbl, 32);
			return true;
			return ws_end(&code);
		}
		break;



@@ 339,7 357,7 @@ bool parse_arg(char *code, int am, inst_t *inst)

		inst->arg_type = ARG_16;
		inst->long_arg = num;
		return true;
		return ws_end(&code);

	case AM_AX:
	case AM_ZPX:


@@ 359,14 377,17 @@ bool parse_arg(char *code, int am, inst_t *inst)
			inst->arg_type = ARG_8;
			inst->byte_arg = num8;
		}
		printf("Parsing AM_* worked, now parsing ,\n");
		if (!skip(&code, ","))
			return false;

		skip_ws(&code);

		if (tolower(*code) != (am == AM_AY || am == AM_ZPY ? 'y' : 'x'))
		printf(", worked yup\n");
		char reg = (am == AM_AY || am == AM_ZPY ? 'y' : 'x');
		printf("reg is %c, *code is %c %x\n", reg, tolower(*code), tolower(*code));
		if (tolower(*code) != reg)
			return false;

		(code)++;
		return ws_end(&code);

	case AM_ZIX:


@@ 413,15 434,30 @@ bool parse_arg(char *code, int am, inst_t *inst)
	return false;
}

// Since program counter can never be < $600, return 0 on failure
uint16_t ll_find(ll_node_t *head, char *name)
{
	ll_node_t *last = head;
	while (last)
	{
		head = last;
		if (!strcasecmp(head->name, name))
			return head->addr;
		last = head->last;
	}
	return 0;
}

uint32_t assemble(char *code, FILE *out)
{
	uintptr_t num_insts = 0,
		pc = 0x600;
	uint16_t num_insts = 0;
	uint16_t pc = 0x600;
	uint32_t line_no = 1;
	map_t *labels = new_map();
	ll_node_t *last_node = NULL;
	char *line,
		*orig_line,
		*line_start;
	inst_t **insts = calloc(sizeof(inst_t), MAX_INSTS);

	printf("Assembling File\n");
	printf("%s\n", code);


@@ 435,10 471,11 @@ uint32_t assemble(char *code, FILE *out)
		
		if (*line == 0)
			goto end_of_line;
		printf("line %d: \033[36m%.12s\033[0m\n", line_no, line);
		
		skip_ws(&line);

		
		printf("line %d: \033[36m%.12s\033[0m\n", line_no, line);
		
		if (is_eol(*line))
		{
			printf("skip_ws() brought us to EOL\n");


@@ 446,26 483,30 @@ uint32_t assemble(char *code, FILE *out)
		}
		
		char *label = parse_label(&line);
		printf(">> label == %.5s %p\n", label, label);
		skip_ws(&code);
		if (is_eol(*line))
			goto end_of_line;
		//if (is_eol(*line))
		//	goto end_of_line;
		char *mn = parse_inst(&line);
		printf(" skipping %d ", skip_ws(&line));
		//printf("\033[33m%s\033[0m\n", line);
		
		bool no_argument = false;
		printf("eol is %c ($%x)\n", *line, *line);
		if (is_eol(*line))
		{
			no_argument = true;
			printf("... no argument\n");
		}
		int32_t mnemonic = -1;

		if (label)
		{
			map_set(labels, label, (void *)pc);
			printf("Set label %s at $%lx\n", label, pc);
			printf("Storing label %s\n", label);
			ll_node_t *head = malloc(sizeof(ll_node_t));
			head->last = last_node;
			strncpy(head->name, label, 32);
			// strncpy won't zero the last byte if its over 32 bytes long
			head->name[31] = 0;
			head->addr = pc;
			last_node = head;
			printf("Set label %s at $%x\n", label, pc);
		}

		if (mn)


@@ 479,39 520,113 @@ uint32_t assemble(char *code, FILE *out)
			MNEMONICS
			{
				printf(ERR "Could not parse instruction on line %d\n%s\n" RESET, line_no, orig_line);
				free(line_start);
				goto cleanup;
			}
#undef MN

			printf("Got instruction %s %d\n", mn, mnemonic);

			inst_t arg;
			inst_t *arg = malloc(sizeof(inst_t));
			arg->line = line_no;
			// printf("Parsing '%s'\n", line);
#define INST(_mn, am, op, len) \
			if ((no_argument && (_mn == AM_IMP || _mn == AM_ACC))		\
				 || (mnemonic == _mn && parse_arg(line, am, &arg)))		\
				 || (mnemonic == _mn && parse_arg(line, am, arg)))		\
			{															\
				arg.opcode = op;										\
				printf("AM_ succeeded: %s\n", #am);						\
				arg->opcode = op;										\
				pc += len;												\
				print_inst(&arg);										\
				print_inst(arg);										\
			}															\
			else

			INSTRUCTIONS
			{
				printf("\033[31mCould not be parsed: %s '%s'\033[0m\n", mn, line);
				free(line_start);
				goto cleanup;
			}
#undef INST

			insts[num_insts++] = arg;
		}
	end_of_line:
		line_no++;
		printf("Line is %d\n", line_no);
		orig_line = strtok_fix(NULL, "\n");
		free(line_start);
	}

	// Generate machine code
	for (int i = 0, curr_pc = 0x600; insts[i]; i++)
	{
		putc(insts[i]->opcode, out);

		switch (insts[i]->arg_type)
		{
		case ARG_8:
			putc(insts[i]->byte_arg, out);
			curr_pc += 2;
			break;
		case ARG_16:
			putshort(insts[i]->long_arg, out);
			curr_pc += 3;
			break;
		case ARG_8REL:
			putc(insts[i]->rel_arg, out);
			curr_pc += 2;
			break;
		case ARG_ABS:
		{
			uint16_t lbl;
			if (!(lbl = ll_find(last_node, insts[i]->label)))
			{
				printf(ERR "Error on line %d: label '%s' is not defined" RESET "\n", insts[i]->line, insts[i]->label);
				goto cleanup;
			}
			curr_pc += 3;

			putshort(lbl, out);
			break;
		}
		case ARG_REL:
		{
			uint16_t lbl;
			if (!(lbl = ll_find(last_node, insts[i]->label)))
			{
				printf(ERR "Error on line %d: label '%s' is not defined" RESET "\n", insts[i]->line, insts[i]->label);
				goto cleanup;
			}
			curr_pc += 2;
			int16_t diff = lbl - curr_pc;
			printf("ARG_REL, pc (after) == %x, diff = %d\n", curr_pc, diff);
			if ((diff < 0 ? -diff : diff) > 0xFF)
			{
				printf(ERR "Error on line %d: label '%s' is too far away for a relative jump" RESET "\n", insts[i]->line, insts[i]->label);
				printf("pc == %hx, label is at %hx\n", curr_pc, lbl);
				goto cleanup;
			}
			putshort((uint8_t) diff, out);
			break;
		}
		default:
			curr_pc++;
		}
	}
	
cleanup:
	free_map(labels);
	printf("-----\n");
	printf("At end, there are %d instructions\n", num_insts);
	while (last_node)
	{
		ll_node_t *curr_node = last_node;
		last_node = curr_node->last;
		free(curr_node);
	}
	for (int i = 0; insts[i]; i++)
		free(insts[i]);
	free(insts);
	fflush(out);

	return num_insts;
}

M as/map.c => as/map.c +1 -1
@@ 70,7 70,7 @@ void map_set(map_t *m, char *k, void *v)
	{
		map_node val =
				{
						malloc(strlen(k)),
						malloc(strlen(k) + 1),
						v,
						1,
						h,

A as/test/colors.s => as/test/colors.s +13 -0
@@ 0,0 1,13 @@
;;; Shows some nice colors on the screen

	LDY #$0
loop:
	TYA
	STA $200, Y
	STA $300, Y
	STA $400, Y
	STA $500, Y
	INY
	CMP #$ff
	BCC loop
	BRK

M colors.dat => colors.dat +0 -0

M cpu.c => cpu.c +1 -1
@@ 42,7 42,7 @@ uint16_t le_to_native(uint8_t a, uint8_t b)
#ifdef LITTLE_ENDIAN
	return b << 8 | a;
#else
	return a << 8 | b;
	return le16toh(a << 8 | b);
#endif
}