File:  [Isaki's NoNo m68k/m88k emulator] / nono / util / iasm / iasm.cpp
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:58 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// XM6i
// Copyright (C) 2012-2016 Tetsuya Isaki
//
// 簡易アセンブラ
//

#include "header.h"
#include <iconv.h>
#include <stdarg.h>
#include <map>
#include <stack>
#include <vector>
#include "iasm.h"

#define BUFSIZE	(1024)

#define D_INOUT	(0x0001)	/* 入出力のみ */
#define D_EA	(0x0002)	/* EA のみ */
#define D_LABEL	(0x0004)	/* ラベル関係 */
#define D_PARSE	(0x0008)	/* パーサ */
#define D_EXPR	(0x0010)	/* 式 */
#define D_SCAN	(0x0020)	/* スキャナ */
#define D_STAGE	(0x8000)	/* ステージ */

typedef std::vector<String> ArrayString;
typedef std::map<String, String> LabelHash;
typedef void (opfunc_t)(uint16_t);
typedef std::vector<Token> ArrayToken;

/* オペランド形式 */
enum oprand_type_t {
	OPR_EA = 0,			/* 最大2つのEA (命令用) */
	OPR_LIST,			/* カンマ区切りリスト */
	OPR_LITERAL,		/* 1つのリテラル */
};

/* 命令処理分岐テーブル */
struct optable_t {
	const char *opname;		/* 命令語(完全一致) */
	opfunc_t *func;			/* 処理関数 */
	uint16_t opword;		/* 命令ワード */
	oprand_type_t oprand_type;	/* オペランド形式 */
};

/* EA タイプ */
enum eatype_t {
	EAT_Dn		= 0x0001,
	EAT_An		= 0x0002,
	EAT_AI		= 0x0004,
	EAT_API		= 0x0008,
	EAT_APD		= 0x0010,
	EAT_AREL	= 0x0020,
	EAT_ABS		= 0x0040,
	EAT_PCREL	= 0x0080,
	EAT_IMM		= 0x0100,

	EAT_ALL		= 0x01ff,
	EAT_DATA	= (EAT_ALL & ~EAT_An),
	EAT_CNTL	= (EAT_An | EAT_AREL | EAT_ABS | EAT_PCREL),
};

/* EA */
enum eamode_t {
	EA_Dn		= 0x00,
	EA_An		= 0x08,
	EA_An_I		= 0x10,
	EA_An_PI	= 0x18,
	EA_An_PD	= 0x20,
	EA_d16An	= 0x28,
	EA_d8AnIX	= 0x30,
	EA_ABS_W	= 0x38,
	EA_ABS_L	= 0x39,
	EA_d16PC	= 0x3a,
	EA_d8PCIX	= 0x3b,
	EA_IMMEDIATE = 0x3c,
};

class EA {
 public:
	int mode;		/* 種別 */
	uint32_t val;	/* 値。ABS か #imm の場合に使用 */
	int size;		/* サイズ(バイト数)。#imm の場合に使用 */
	String label;	/* あれば未解決ラベル。空文字列なら val を使用のこと */

	EA() {
		mode = 0;
		val = 0;
		size = 0;
	}
	int type() const { return (mode >> 3) & 7; }
	int num()  const { return (mode & 7); }
};

/* 参照種別 */
enum reftype_t {
	REFTYPE_BYTE,
	REFTYPE_WORD,
	REFTYPE_LONG,
};

/* 後から参照(式)を解決するための構造体 */
struct REF {
	int pos;			/* 出力位置 */
	int line;			/* 現在の行数 */
	reftype_t type;		/* 種別(サイズ) */
	ArrayString expr;	/* 中間式 */
};
typedef std::vector<REF*> ArrayRef;

/* サイズサフィックスから bit7-6 がサイズを示すやつ用に変換 */
const uint16_t size6[] = {
	0,
	0x0000,	/* 1=.B */
	0x0040,	/* 2=.W */
	0,
	0x0080, /* 4=.L */
};

/* 出力形式 */
enum format_t {
	FORMAT_C,			/* C ソース */
	FORMAT_BINARY,		/* バイナリ */
};

const char *outfile;		/* 出力ファイル名 */
FILE *outfp;				/* 出力ファイル */
int debug;					/* デバッグモード */
const char *current_infile;	/* 現在の入力ファイル名 */
int line;					/* 現在のファイル中の行数(エラー表示用) */
format_t format;			/* 出力形式 */
uint32_t origin;			/* 現在のブロックの開始アドレス */
uint32_t pc;				/* 現在の PC */
String label0;				/* 現在行のラベル */
String op0;					/* 現在の命令語 */
String size0;				/* 現在のサイズサフィックス (文字) */
int size_sfx;				/* 現在のサイズサフィックス (B=1/W=2/L=4) */
ArrayString oprand;			/* 現在のオペランド */
String current_name;		/* .start で指示された変数名 */
String outbin;				/* 現在出力しているバイナリ列 */
LabelHash label_list;		/* ラベルのリスト */
ArrayRef ref_list;			/* ラベルを参照する側のリスト */
int rept_start;				/* .rept 開始位置 */
int rept_count;				/* .rept 回数 */

void usage(const char *) __attribute__((__noreturn__));
int asm_main(const char *);
void parse_asm(String&);
void scan_asm(String&, ArrayToken&);
void parse_token(ArrayToken&);
void parse_arg_literal(ArrayToken&, ArrayToken::iterator&);
void parse_arg_list(ArrayToken&, ArrayToken::iterator&);
void parse_arg_ea(ArrayToken&, ArrayToken::iterator&);
void include_iocs();
void add_label(String&);
bool find_label(String&, uint32_t&);
optable_t *lookup(String&);
void format_c();
void format_binary();
bool try_ea(String&, EA&, String&, int);
EA get_ea(String&);
EA get_ea(String&, int);
bool get_reglist(String&, uint16_t&);
int get_cc(String&);
void output_ea(EA&);
void add_ref(reftype_t, String&);
void output_bin(uint32_t, int);
void output_b(uint32_t);
void output_w(uint32_t);
void output_l(uint32_t);
void output_ds(int);
String num2str(int);
String num2hex(int);
bool parse_number(String&, int*);
int tonumber(String&);
bool parse_expr(String&, ArrayString&);
bool scan_expr(String&, ArrayString&);
void add_expr(int pos, reftype_t, ArrayString&);
String parse_string(String&);
String convert_charset(String&);
void DPRINTN_ref(int, REF *);
void DPRINTN_expr(int, ArrayString&);
bool calc_expr(ArrayString&, uint32_t&, bool);
void DPRINTV(int, const char *, va_list);
void DPRINTN(int, const char *, ...);
void DPRINTF(int, const char *, ...);
void error(const char *, ...) __attribute__((__noreturn__)); /* エラー出力 */
void trim(String&);					/* 前後の空白を取り除く */
void ltrim(String&);				/* 先頭の空白を取り除く */
void rtrim(String&);				/* 末尾の空白を取り除く */

opfunc_t op_cinclude;
opfunc_t op_org;
opfunc_t op_start;
opfunc_t op_end;
opfunc_t op_dc;
opfunc_t op_ds;
opfunc_t op_even;
opfunc_t op_equ;
opfunc_t op_rept;
opfunc_t op_endrept;
opfunc_t op_iocs;
opfunc_t op_bcc;
opfunc_t op_bra;
opfunc_t op_clr;
opfunc_t op_dbra;
opfunc_t op_jmp;
opfunc_t op_lea;
opfunc_t op_move;
opfunc_t op_movem;
opfunc_t op_moveq;
opfunc_t op_sub;
opfunc_t op_suba;
opfunc_t op_trap;
opfunc_t op_noarg;

optable_t optable[] = {
	{ ".cinclude",	op_cinclude,	0,	OPR_LITERAL, },
	{ ".org",		op_org,			0,	OPR_LIST, },
	{ ".start",		op_start,		0,	OPR_LITERAL, },
	{ ".end",		op_end,			0,	OPR_LIST, },
	{ ".dc",		op_dc,			0,	OPR_LIST, },
	{ ".ds",		op_ds,			0,	OPR_LIST, },
	{ ".even",		op_even,		0,	OPR_LIST, },
	{ ".equ",		op_equ,			0,	OPR_LIST, },
	{ ".rept",		op_rept,		0,	OPR_LIST, },
	{ ".endrept",	op_endrept,		0,	OPR_LIST, },

	/* マクロが定義できないのでここでやっちまう */
	{ "IOCS",		op_iocs, },

	/* XXX BCC.B <cond>,<label> */
	{ "bcc",		op_bcc,		0x6000, },

	{ "bra",		op_bra,		0x6000, },
	{ "clr",		op_clr,		0x4200, },
	{ "dbra",		op_dbra,	0x51c8, },
	{ "jmp",		op_jmp,		0x4ec0, },
	{ "lea",		op_lea,		0x41c0, },
	{ "move",		op_move,	0x0000, },
	{ "movem",		op_movem,	0x4880, },
	{ "moveq",		op_moveq,	0x7000, },
	{ "sub",		op_sub,		0x9000, },
	{ "suba",		op_suba,	0x90c0, },
	{ "trap",		op_trap,	0x4e40, },

	/* オペランドのないやつ */
	{ "reset",		op_noarg,	0x4e70, },
	{ "nop",		op_noarg,	0x4e71, },
	{ "rte",		op_noarg,	0x4e73, },
	{ "rtd",		op_noarg,	0x4e74, },
	{ "rts",		op_noarg,	0x4e75, },
	{ "trapv",		op_noarg,	0x4e76, },
	{ "rtr",		op_noarg,	0x4e77, },

	{ NULL, NULL, },
};

const char *regname[] = {
	"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
	"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
	"(A0)",  "(A1)",  "(A2)",  "(A3)",  "(A4)",  "(A5)",  "(A6)",  "(A7)",
	"(A0)+", "(A1)+", "(A2)+", "(A3)+", "(A4)+", "(A5)+", "(A6)+", "(A7)+",
	"-(A0)", "-(A1)", "-(A2)", "-(A3)", "-(A4)", "-(A5)", "-(A6)", "-(A7)",
};

const char *ccname[] = {
	"t",  "f",  "hi", "ls", "cc", "cs", "ne", "eq",
	"vc", "vs", "pl", "mi", "ge", "lt", "gt", "le",
};

const char *iocscall[] = {
	"_B_KEYINP",	// $00
	"_B_KEYSNS",	// $01
	"_B_SFTSNS",	// $02
	"_KEY_INIT",	// $03
	"_BITSNS",		// $04
	"_SKEYSET",		// $05
	"_LEDCTRL",		// $06
	"_LEDSET",		// $07
	"_KEYDLY",		// $08
	"_KEYREP",		// $09
	"$0a",			// $0a
	"$0b",			// $0b
	"_TVCTRL",		// $0c
	"_LEDMOD",		// $0d
	"_TGUSEMD",		// $0e
	"_DEFCHR",		// $0f
	"_CRTMOD",		// $10
	"_CONTRAST",	// $11
	"_HSVTORGB",	// $12
	"_TPALET",		// $13
	"_TPALET2",		// $14
	"_TCOLOR",		// $15
	"_FNTADR",		// $16
	"_VRAMGET",		// $17
	"_VRAMPUT",		// $18
	"_FNTGET",		// $19
	"_TEXTGET",		// $1a
	"_TEXTPUT",		// $1b
	"_CLIPPUT",		// $1c
	"_SCROLL",		// $1d
	"_B_CURON",		// $1e
	"_B_CUROFF",	// $1f
	"_B_PUTC",		// $20
	"_B_PRINT",		// $21
	"_B_COLOR",		// $22
	"_B_LOCATE",	// $23
	"_B_DOWN_S",	// $24
	"_B_UP_S",		// $25
	"_B_UP",		// $26
	"_B_DOWN",		// $27
	"_B_RIGHT",		// $28
	"_B_LEFT",		// $29
	"_B_CLR_ST",	// $2a
	"_B_ERA_ST",	// $2b
	"_B_INS",		// $2c
	"_B_DEL",		// $2d
	"_B_CONSOL",	// $2e
	"_B_PUTMES",	// $2f
	"_SET232C",		// $30
	"_LOF232C",		// $31
	"_INP232C",		// $32
	"_ISNS232C",	// $33
	"_OSNS232C",	// $34
	"_OUT232C",		// $35
	"$36",			// $36
	"$37",			// $37
	"_SETFNTADR",	// $38
	"$39",			// $39
	"$3a",			// $3a
	"_JOYGET",		// $3b
	"_INIT_PRN",	// $3c
	"_SNSPRN",		// $3d
	"_OUTLPT",		// $3e
	"_OUTPRN",		// $3f
	"_B_SEEK",		// $40
	"_B_VERIFY",	// $41
	"_B_READDI",	// $42
	"_B_DSKINI",	// $43
	"_B_DRVSNS",	// $44
	"_B_WRITE",		// $45
	"_B_READ",		// $46
	"_B_RECALI",	// $47
	"_B_ASSIGN",	// $48
	"_B_WRITED",	// $49
	"_B_READID",	// $4a
	"_B_BADFMT",	// $4b
	"_B_READDL",	// $4c
	"_B_FORMAT",	// $4d
	"_B_DRVCHK",	// $4e
	"_B_EJECT",		// $4f
	"_DATEBCD",		// $50
	"_DATESET",		// $51
	"_TIMEBCD",		// $52
	"_TIMESET",		// $53
	"_DATEGET",		// $54
	"_DATEBIN",		// $55
	"_TIMEGET",		// $56
	"_TIMEBIN",		// $57
	"_DATECNV",		// $58
	"_TIMECNV",		// $59
	"_DATEASC",		// $5a
	"_TIMEASC",		// $5b
	"_DAYASC",		// $5c
	"_ALARMMOD",	// $5d
	"_ALARMSET",	// $5e
	"_ALARMGET",	// $5f
	"_ADPCMOUT",	// $60
	"_ADPCMINP",	// $61
	"_ADPCMAOT",	// $62
	"_ADPCMAIN",	// $63
	"_ADPCMLOT",	// $64
	"_ADPCMLIN",	// $65
	"_ADPCMSNS",	// $66
	"_ADPCMMOD",	// $67
	"_OPMSET",		// $68
	"_OPMSNS",		// $69
	"_OPMINTST",	// $6a
	"_TIMERDST",	// $6b
	"_VDISPST",		// $6c
	"_CRTCRAS",		// $6d
	"_HSYNCST",		// $6e
	"_PRNINTST",	// $6f
	"_MS_INIT",		// $70
	"_MS_CURON",	// $71
	"_MS_CUROF",	// $72
	"_MS_STAT",		// $73
	"_MS_GETDT",	// $74
	"_MS_CURGT",	// $75
	"_MS_CURST",	// $76
	"_MS_LIMIT",	// $77
	"_MS_OFFTM",	// $78
	"_MS_ONTM",		// $79
	"_MS_PATST",	// $7a
	"_MS_SEL",		// $7b
	"_MS_SEL2",		// $7c
	"_SKEY_MOD",	// $7d
	"_DENSNS",		// $7e
	"_ONTIME",		// $7f
	"_B_INTVCS",	// $80
	"_B_SUPER",		// $81
	"_B_BPEEK",		// $82
	"_B_WPEEK",		// $83
	"_B_LPEEK",		// $84
	"_B_MEMSTR",	// $85
	"_B_BPOKE",		// $86
	"_B_WPOKE",		// $87
	"_B_LPOKE",		// $88
	"_B_MEMSET",	// $89
	"_DMAMOVE",		// $8a
	"_DMAMOV_A",	// $8b
	"_DMAMOV_L",	// $8c
	"_DMAMODE",		// $8d
	"_BOOTINF",		// $8e
	"_ROMVER",		// $8f
	"_G_CLR_ON",	// $90
	"$91",			// $91
	"$92",			// $92
	"$93",			// $93
	"_GPALET",		// $94
	"$95",			// $95
	"$96",			// $96
	"$97",			// $97
	"$98",			// $98
	"$99",			// $99
	"$9a",			// $9a
	"$9b",			// $9b
	"$9c",			// $9c
	"", "", "",
	"_SFTJIS",		// $a0
	"_JISSFT",		// $a1
	"_AKCONV",		// $a2
	"_RMACNV",		// $a3
	"_DAKJOB",		// $a4
	"_HANJOB",		// $a5
	"", "", "",
	"", "", "",
	"_SYS_STAT",	// $ac
	"_B_CONMOD",	// $ad
	"_OS_CURON",	// $ae
	"_OS_CUROF",	// $af
	"_DRAWMODE",	// $b0
	"_APAGE",		// $b1
	"_VPAGE",		// $b2
	"_HOME",		// $b3
	"_WINDOW",		// $b4
	"_WIPE",		// $b5
	"_PSET",		// $b6
	"_POINT",		// $b7
	"_LINE",		// $b8
	"_BOX",			// $b9
	"_FILL",		// $ba
	"_CIRCLE",		// $bb
	"_PAINT",		// $bc
	"_SYMBOL",		// $bd
	"_GETGRM",		// $be
	"_PUTGRM",		// $bf
	"_SP_INIT",		// $c0
	"_SP_ON",		// $c1
	"_SP_OFF",		// $c2
	"_SP_CGCLR",	// $c3
	"_SP_DEFCG",	// $c4
	"_SP_GTPCG",	// $c5
	"_SP_REGST",	// $c6
	"_SP_REGGT",	// $c7
	"_BGSCRLST",	// $c8
	"_BGSCRLGT",	// $c9
	"_BGCTRLST",	// $ca
	"_BGCTRLGT",	// $cb
	"_BGTEXTCL",	// $cc
	"_BGTEXTST",	// $cd
	"_BGTEXTGT",	// $ce
	"_SPALET",		// $cf
	"", "", "",
	"_TXXLINE",		// $d3
	"_TXYLINE",		// $d4
	"_TXLINE",		// $d5
	"_TXBOX",		// $d6
	"_TXFILL",		// $d7
	"_TXREV",		// $d8
	"", "", "",
	"", "", "",
	"_TXRASCPY",	// $df
	"", "", "", "",
	"", "", "", "",
	"", "", "", "",
	"", "", "", "",
	"_OPMDRV",		// $f0
	"_RSDRV",		// $f1
	"_A_JOYGET",	// $f2
	"_MUSICDRV",	// $f3
	"",
	"_SCSIDRV",		// $f5
	"", "", "", "",
	"", "", "",
	"_ABORTRST",	// $fd
	"_IPLERR",		// $fe
	"_ABORTJOB",	// $ff
};

void
usage(const char *progname)
{
	fprintf(stderr, "usage: %s [-d] [-f <fmt>] <infile> [<outfile>]\n",
		progname);
	fprintf(stderr, "  -f <fmt>   output format: c, binary\n");
	exit(1);
}

int
main(int ac, char *av[])
{
	const char *progname;
	const char *infile;
	int c;

	progname = av[0];
	while ((c = getopt(ac, av, "d:f:")) != -1) {
		switch (c) {
		 case 'd':
			debug = strtol(optarg, NULL, 16);
			break;
		 case 'f':
			if (strcasecmp(optarg, "c") == 0) {
				format = FORMAT_C;
			} else if (strcasecmp(optarg, "binary") == 0) {
				format = FORMAT_BINARY;
			} else {
				usage(progname);
			}
			break;
		 default:
			usage(progname);
			break;
		}
	}
	ac -= optind;
	av += optind;

	/* デバッグ指定が1つでもあれば STAGE を必ず有効にする */
	if (debug > 0) {
		debug |= D_STAGE;
	}

	if (ac < 1) {
		usage(progname);
	}
	infile = av[0];

	if (ac > 1) {
		outfile = av[1];
		outfp = fopen(outfile, "wb");
		if (outfp == NULL) {
			fprintf(stderr, "cannot open outfile: %s\n", outfile);
			exit(1);
		}
	} else {
		outfile = NULL;
		outfp = stdout;
	}

	/* IOCS コールマクロを定義 */
	include_iocs();

	/* メイン処理 */
	asm_main(infile);

	if (outfp != stdout) {
		fclose(outfp);
	}
}

int
asm_main(const char *infile)
{
	FILE *fp;
	char cbuf[1024];

	/* ファイルオープン */
	fp = fopen(infile, "r");
	if (fp == NULL) {
		fprintf(stderr, "cannot open %s", infile);
		exit(1);
	}
	current_infile = infile;

	/* 1st ステージ: 読み込みながら処理 */
	line = 0;
	while (fgets(cbuf, sizeof(cbuf), fp)) {
		String buf(cbuf);
		line++;

		rtrim(buf);
		DPRINTF(D_INOUT, "line %3d |%s|\n", ::line, buf.c_str());

		/* 1命令処理 */
		parse_asm(buf);
	}

	fclose(fp);
	return 0;
}

/* 1命令処理 */
void
parse_asm(String& buf)
{
	ArrayToken token_list;

	/* 字句解析 */
	scan_asm(buf, token_list);

	/* 構文解析(?)して分岐 */
	parse_token(token_list);
}

/* 入力の1行 src を字句解析して結果を token_list に返す */
void
scan_asm(String& src, ArrayToken& token_list)
{
	Scanner s(src);

	for (;;) {
		Token t;

		if (!s.get_token(t)) {
			error("syntax error: %s", s.errmsg.c_str());
		}

		token_list.push_back(t);

		if (t.type == T_END) {
			break;
		}
	}

	/* 行末(T_ENDの前)が空白なら削除 */
	if (token_list.size() > 1) {
		ArrayToken::iterator it = token_list.end();
		advance(it, -2);
		if ((*it).type == T_SPACE) {
			token_list.erase(it);
		}
	}

	if ((debug & D_SCAN)) {
		for (int i = 0; i < token_list.size(); i++) {
			Token t = token_list[i];

			DPRINTF(D_SCAN, " %-7s |%s|", t.type_name(), t.word.c_str());
			if (t.type == T_NUMBER || t.type == T_SIZE || t.type == T_CHAR) {
				DPRINTN(D_SCAN, " num=$%x", t.number);
			}
			DPRINTN(D_SCAN, "\n");
		}
	}
}

/* トークン列をパースして処理する */
void
parse_token(ArrayToken& list)
{
	ArrayToken::iterator t;

	/* 命令処理前にグローバル変数をクリア */
	label0.erase();
	op0.erase();
	size0.erase();
	size_sfx = -1;
	oprand.clear();

	/* 先頭がリテラルならラベル */
	t = list.begin();
	if ((*t).type == T_LITERAL) {
		/* ラベルをリストに記憶 */
		DPRINTF(D_PARSE, "label='%s'\n", (*t).word.c_str());
		label0 = (*t).word;
		add_label(label0);
		t++;

		/* 直後がコロンならスペースに置き換えてから */
		if ((*t).type == T_COLON) {
			(*t).type = T_SPACE;
			t++;
		}
	}

	/* ラベルのみならここで終わり */
	if ((*t).type == T_END) {
		return;
	}
	/* そうでなければスペースのはずなので読み飛ばす */
	if ((*t).type == T_SPACE) {
		t++;
	}

	/* 命令語は必ずリテラルから始まっている */
	if ((*t).type != T_LITERAL) {
		error("syntax error: invalid opword: %s\n", (*t).word.c_str());
	}
	/* 命令語を取り出す */
	op0 = (*t).word;
	t++;

	/* 直後がサイズサフィックスなら記憶 */
	if ((*t).type == T_SIZE) {
		size0 = (*t).word;
		size_sfx = (*t).number;
		t++;
	}
	DPRINTF(D_PARSE, "op0='%s' %s(%d)\n",
		op0.c_str(), size0.c_str(), size_sfx);

	/* オペランドなしならここで終わり。ありなら空白のはず */
	if ((*t).type == T_END) {
		/* XXX 分岐は実行しないといけないのでこのまま進む */
		//return;
	} else if ((*t).type != T_SPACE) {
		error("syntax error: after opword: %s\n", (*t).word.c_str());
	}
	t++;

	/* 命令語からオペランド種別を取り出す */
	optable_t *opt = lookup(op0);
	if (opt == NULL) {
		error("unknown opcode: %s\n", op0.c_str());
	}
	switch (opt->oprand_type) {
	 case OPR_EA:		/* 通常命令用の EA */
		parse_arg_ea(list, t);
		break;
	 case OPR_LIST:		/* カンマ区切りリスト */
		parse_arg_list(list, t);
		break;
	 case OPR_LITERAL:	/* 書式なし1つの引数 */
		parse_arg_literal(list, t);
		break;
	 default:
		error("unknown oprand_type=%d (op=%s)\n",
			opt->oprand_type, op0.c_str());
		break;
	}
	DPRINTF(D_PARSE, "oprand.size=%d", oprand.size());
	for (int i = 0; i < oprand.size(); i++) {
		DPRINTN(D_PARSE, " '%s'", oprand[i].c_str());
	}
	DPRINTN(D_PARSE, "\n");

	/* 命令語によって分岐 */
	opt->func(opt->opword);
}

/* オペランドを書式なしとしてパース。全部結合するだけ */
void
parse_arg_literal(ArrayToken& list, ArrayToken::iterator& t)
{
	String arg;

	DPRINTF(D_PARSE, "parse_arg_literal\n");

	for (; t != list.end(); t++) {
		if ((*t).type == T_END) {
			break;
		}
		arg += (*t).word;
	}
	oprand.push_back(arg);
}

/* 疑似命令用のオペランド(カンマ区切りリスト)をパース */
void
parse_arg_list(ArrayToken& list, ArrayToken::iterator& t)
{
	String arg;

	DPRINTF(D_PARSE, "parse_arg_list\n");

	for (; t != list.end(); t++) {
		if ((*t).type == T_COMMA || (*t).type == T_END) {
			oprand.push_back(arg);
			arg.clear();
		} else if ((*t).type != T_SPACE) {
			arg += (*t).word;
		}
	}
}

/* 命令のオペランド (EA) をパース */
void
parse_arg_ea(ArrayToken& list, ArrayToken::iterator& t)
{
	DPRINTF(D_PARSE, "parse_arg_ea\n");
#if 1
	/* XXX オペランドはとりあえず旧パーサの動作をシミュレートしておく */
	String arg;
	for (; t != list.end(); t++) {
		if ((*t).type == T_COMMA || (*t).type == T_END) {
			oprand.push_back(arg);
			arg.clear();
		} else if ((*t).type != T_SPACE) {
			arg += (*t).word;
		}
	}
#endif
}

/* IOCS コールマクロを定義 */
void
include_iocs()
{
	std::pair<LabelHash::iterator, bool> r;

	for (int i = 0; i < countof(iocscall); i++) {
		String name(iocscall[i]);
		if (name.size() == 0 || name[0] == '$') {
			continue;
		}
		String var = num2hex(i);

		r = label_list.insert(std::make_pair(name, var));
		if (r.second == 0) {
			error("iocscall macro '%s' is duplicated\n", name.c_str());
		}
		//DPRINTF(D_LABEL, "LABEL '%s' <= '%s'\n", name.c_str(), var.c_str());
	}
}

/* ラベルをリストに記憶 */
void
add_label(String& label)
{
	std::pair<LabelHash::iterator, bool> r;
	String addr;

	addr = num2hex(::pc);
	r = label_list.insert(std::make_pair(label, addr));
	if (r.second == 0) {
		error("label '%s' is duplicated\n", label.c_str());
	}
	DPRINTF(D_LABEL, "LABEL '%s' <= '%s'\n", label.c_str(), addr.c_str());
}

/*
 * リストからラベルを探索。
 * 見付かれば var に置換後の文字列を格納して true を返す。
 * 見付からなければ false を返す。
 */
bool
find_label(String& label, String& var)
{
	LabelHash::iterator i;

	i = label_list.find(label);
	if (i == label_list.end()) {
		DPRINTF(D_LABEL, "'%s' => NOT FOUND\n", label.c_str());
		return false;
	}
	var = (*i).second;
	DPRINTF(D_LABEL, "'%s' => '%s'\n", label.c_str(), var.c_str());
	return true;
}

/* 命令をテーブルから検索 */
optable_t *
lookup(String& op)
{
	optable_t *t;

	for (t = optable; t->opname; t++) {
		if (strcasecmp(op.c_str(), t->opname) == 0) {
			return t;
		}
	}
	return NULL;
}

/* .cinclude 擬似命令 */
void
op_cinclude(uint16_t opword)
{
	if (format == FORMAT_C) {
		fprintf(outfp, "#include %s\n", oprand[0].c_str());
	}
}

/* .org 擬似命令 */
void
op_org(uint16_t opword)
{
	if (oprand.size() != 1) {
		error(".org must have 1 oprand\n");
	}
	origin = tonumber(oprand[0]);
	pc = origin;
	DPRINTF(D_INOUT, "set origin=%06X\n", origin);
}

/* .start 擬似命令 */
void
op_start(uint16_t opword)
{
	/* C出力なら引数が必要。書式が変か… */
	if (format == FORMAT_C) {
		if (oprand.size() < 1) {
			error(".start needs 1 oprand for C output\n");
		}
		current_name = oprand[0];
	}

	outbin.erase();
	rept_start = -1;
	rept_count = 0;
}

/* .end 擬似命令 */
void
op_end(uint16_t opword)
{
	/* 出力は必ずワード単位にする */
	op_even(0);

	/* 2nd ステージ (ラベルの解決) */
	DPRINTN(D_STAGE, "\n2nd stage\n");
	for (ArrayRef::iterator i = ref_list.begin(); i != ref_list.end(); ) {
		uint32_t uval;
		int val;

		/* 構造体を取り出す */
		REF *ref = *i;
		DPRINTN_ref(D_INOUT, ref);

		line = ref->line;

		/* 値を計算。ラベルが解決できなければ向こうでエラーにする */
		calc_expr(ref->expr, uval, true);
		val = uval;

		/* 種別ごとに範囲チェックして出力 */
DPRINTF(D_INOUT, "val=%d\n", val);
		switch (ref->type) {
		 case REFTYPE_BYTE:
			if (val > 127 || val < -128) {
				::line = ref->line;
				error("%d is insufficient in byte data\n", val);
			}
			DPRINTF(D_INOUT, "pos=%d => $%02x\n", ref->pos, val & 0xff);
			outbin[ref->pos] = val;
			break;

		 case REFTYPE_WORD:
			if (val > 32767 || val < -32768) {
				::line = ref->line;
				error("%d is insufficient in word data\n\n", val);
			}
			DPRINTF(D_INOUT, "pos=%d => $%04x\n", ref->pos, val & 0xffff);
			outbin[ref->pos    ] = (val >> 8);
			outbin[ref->pos + 1] = (val & 0xff);
			break;

		 case REFTYPE_LONG:
			DPRINTF(D_INOUT, "pos=%d => $%08x\n", ref->pos, val);
			outbin[ref->pos    ] = (val >> 24) & 0xff;
			outbin[ref->pos + 1] = (val >> 16) & 0xff;
			outbin[ref->pos + 2] = (val >> 8)  & 0xff;
			outbin[ref->pos + 3] =  val & 0xff;
			break;

		 default:
			error("unsupported reftype: %d\n", (int)ref->type);
			break;
		}

		/* 解決したので削除 */
		ref_list.erase(i);
		delete ref;
	}

	/* 3rd ステージというか出力 */
	DPRINTN(D_STAGE, "\n3rd stage\n");
	switch (format) {
	 case FORMAT_C:
		format_c();
		break;
	 case FORMAT_BINARY:
		format_binary();
		break;
	}

	DPRINTN(D_STAGE, "\n3rd stage end\n\n");
}

/* Cソース形式で出力 */
void
format_c()
{
	fprintf(outfp, "\n");
	fprintf(outfp, "const BYTE %s[] = {", current_name.c_str());
	for (int i = 0; i < outbin.size(); i++) {
		if ((i % 8) == 0) {
			fprintf(outfp, "\n\t");
		}
		fprintf(outfp, "0x%02x,", (uint8_t)outbin[i]);
	}
	fprintf(outfp, "\n};\n");
	fprintf(outfp, "const int %s_size = %d;\n",
		current_name.c_str(), (int)outbin.size());
}

/* バイナリ形式で出力 */
void
format_binary()
{
	String buf;
	int n;

	n = 0;
	for (;;) {
		buf = outbin.substr(n, 4096);
		if (buf.size() == 0) {
			break;
		}

		fwrite(buf.c_str(), 1, buf.size(), outfp);
		n += buf.size();
	}
}

/* .dc 擬似命令 */
void
op_dc(uint16_t opword)
{
	ArrayString::iterator i;
	for (i = oprand.begin(); i != oprand.end(); i++) {
		String arg = *i;

		/* .b でダブルクォートから始まってたら文字列 */
		if (size_sfx == 1 && arg[0] == '\x22') {
			/* 文字列を解析 */
			String str = parse_string(arg);

			/* 日本語文字コードを変換 */
			str = convert_charset(str);

			for (int j = 0; j < str.size(); j++) {
				output_b(str[j]);
			}

		} else if (size_sfx == 1 && arg[0] == '\'') {
			/* XXX まだこれには対応できないので旧方式で対応 */
			output_b(tonumber(arg));

		} else {
			/* そうでなければ式として評価 */
			ArrayString expr;
			uint32_t val;
			parse_expr(*i, expr);
			if (calc_expr(expr, val, false)) {
				/* 解決できたので出力 */
				output_bin(val, size_sfx);
			} else {
				/* 解決できなかった */
				switch (size_sfx) {
				 case 1:
					add_expr(::outbin.size(), REFTYPE_BYTE, expr);
					break;
				 case 2:
					add_expr(::outbin.size(), REFTYPE_WORD, expr);
					break;
				 case 4:
					add_expr(::outbin.size(), REFTYPE_LONG, expr);
					break;
				 default:
					abort();
				}
				output_bin(0, size_sfx);
			}
		}
	}
}

/* .ds 擬似命令 */
void
op_ds(uint16_t opword)
{
	ArrayString::iterator i;
	for (i = oprand.begin(); i != oprand.end(); i++) {
		int size = tonumber(*i);
		output_ds(size * size_sfx);
	}
}

/* .even 擬似命令 */
void
op_even(uint16_t opword)
{
	/* 奇数なら偶数に合わせる */
	if ((outbin.size() & 1)) {
		output_b(0);
	}
}

/* .equ 擬似命令 */
void
op_equ(uint16_t opword)
{
	if (label0.empty()) {
		error(".equ must have a label\n");
	}
	if (oprand.size() != 1) {
		error(".equ must have 1 oprand\n");
	}

	/* ラベルの値を PC ではなくオペランド指定の値に差し替える */
	LabelHash::iterator i;
	/* 見付からないはずはないのでエラー処理省略? */
	i = label_list.find(label0);
	(*i).second = oprand[0];
	DPRINTF(D_LABEL, "LABEL '%s' EQU '%s'\n",
		(*i).first.c_str(), (*i).second.c_str());
}

/* .rept 擬似命令 */
void
op_rept(uint16_t opword)
{
	ArrayString expr;
	uint32_t cnt;

	if (rept_start != -1) {
		error(".rept duplicated\n");
	}

	/* ここを記憶しておくだけ */
	rept_start = outbin.size();
	/* 回数は <式> で、この時点で解決できなければいけない */
	parse_expr(oprand[0], expr);
	calc_expr(expr, cnt, true);
	rept_count = cnt;

	DPRINTF(D_INOUT, ".rept start=$%x count=$%x\n",
		rept_start, rept_count);
}

/* .endrept 擬似命令 */
/* XXX 最終的には HAS に合わせて endm にしたい */
void
op_endrept(uint16_t opword)
{
	String target;
	int rept_size;
	int total_size;

	if (rept_start == -1) {
		error(".endrept without .rept\n");
	}

	/* .rept の位置からここまでの内容を取得 */
	rept_size = outbin.size() - rept_start;
	target = outbin.substr(rept_start, rept_size);

	/* それを (rept_count-1) 回出力。1回目はすでに出力してあるから */
	rept_count--;
	for (int i = 0; i < rept_count; i++) {
		outbin += target;
	}

	total_size = rept_size * rept_count;
	pc += total_size;
	DPRINTF(D_INOUT, "output $%x(%d) bytes x $%x(%d) = $%x(%d) bytes\n",
		rept_size, rept_size,
		rept_count, rept_count,
		total_size, total_size);

	/* パラメータをクリア */
	rept_count = 0;
	rept_start = -1;
}

/* IOCS マクロ命令 */
void
op_iocs(uint16_t opword)
{
	char moveqbuf[32];
	int num;

	num = tonumber(oprand[0]) & 0xff;
	snprintf(moveqbuf, sizeof(moveqbuf), " moveq.l #$%x,d0", num);
	String moveq(moveqbuf);
	String trap(" trap #15");

	parse_asm(moveq);
	parse_asm(trap);
}

/* bcc */
/* XXX Bcc.b <cond>,<label> というニーモニックにする */
void
op_bcc(uint16_t opword)
{
	int cc;

	/* bra/bcc だけ .s を .b に読み替えて認める */
	if (size_sfx == 0 && tolower(size0[1]) == 's') {
		size_sfx = 1;
	}

	if (size_sfx == 1) {
		cc = get_cc(oprand[0]);
		add_ref(REFTYPE_BYTE, oprand[1]);

		opword |= cc << 8;
		output_w(opword);
	} else {
		error("unsupported size: %s%s\n", op0.c_str(), size0.c_str());
	}
}

/* bra */
void
op_bra(uint16_t opword)
{
	/* bra/bcc だけ .s を .b に読み替えて認める */
	if (size_sfx == 0 && tolower(size0[1]) == 's') {
		size_sfx = 1;
	}

	if (size_sfx == 1) {
		add_ref(REFTYPE_BYTE, oprand[0]);
		output_w(opword);
	} else {
		error("unsupported size: %s%s\n", op0.c_str(), size0.c_str());
	}
}

/* clr */
void
op_clr(uint16_t opword)
{
	EA dst;

	dst = get_ea(oprand[0]);

	opword |= dst.mode | size6[size_sfx];

	output_w(opword);
	output_ea(dst);
}

/* dbra */
void
op_dbra(uint16_t opword)
{
	EA src;

	src = get_ea(oprand[0]);
	add_ref(REFTYPE_WORD, oprand[1]);

	opword |= src.num();

	output_w(opword);
	output_w(0);
}

/* jmp */
void
op_jmp(uint16_t opword)
{
	EA dst;

	dst = get_ea(oprand[0]);

	output_w(opword | dst.mode);
	output_ea(dst);
}

/* lea.l */
void
op_lea(uint16_t opword)
{
	EA src, dst;

	if (size_sfx == -1) {
		size_sfx = 4;
	}
	if (size_sfx != 4) {
		error("invalid opcode: %s%s\n", op0.c_str(), size0.c_str());
	}

	src = get_ea(oprand[0]);
	dst = get_ea(oprand[1]);

	if (dst.type() != 0x01) {
		error("syntax error: LEA dst must be An\n");
	}

	opword |= src.mode;
	opword |= dst.num() << 9;

	output_w(opword);
	output_ea(src);
}

/* move */
void
op_move(uint16_t opword)
{
	EA src, dst;

	/* move an,usp */
	if (oprand[1] == "usp") {
		if (size_sfx == -1) {
			size_sfx = 4;
		}
		if (size_sfx != 4) {
			error("syntax error: MOVE An,USP must .L");
		}
		src = get_ea(oprand[0], EAT_An);
		opword = 0x4e60 + src.num();
		output_w(opword);
		return;
	}
	/* move usp,an */
	if (oprand[0] == "usp") {
		dst = get_ea(oprand[1]);
		if (size_sfx == -1) {
			size_sfx = 4;
		}
		if (size_sfx != 4) {
			error("syntax error: MOVE USP,An must .L");
		}
		opword = 0x4e68 + dst.num();
		output_w(opword);
		return;
	}

	src = get_ea(oprand[0]);
	dst = get_ea(oprand[1]);

	switch (size_sfx) {
	 case 1:	opword = 0x1000;	src.size = 1;	break;
	 case 2:	opword = 0x3000;	src.size = 2;	break;
	 case 4:	opword = 0x2000;	src.size = 4;	break;
	}
	opword |= src.mode;
	opword |= (dst.type() | (dst.num() << 3)) << 6;

	output_w(opword);
	output_ea(src);
	output_ea(dst);
}

/* movem.l */
void
op_movem(uint16_t opword)
{
	uint16_t list;
	EA ea;
	String errmsg;

	if (size_sfx == 4) {	/* .L */
		opword |= 0x0040;
	}

	if (get_reglist(oprand[0], list)
	 && try_ea(oprand[1], ea, errmsg, EAT_AI | EAT_APD | EAT_AREL | EAT_ABS))
	{
		/* movem.l list,ea */
		opword |= ea.mode;

		/* -(An) の時だけビット並び順を反転 */
		if ((ea.mode & 0x38) == EA_An_PD) {
			uint16_t rev = 0;
			for (int i = 0; i < 16; i++) {
				if ((list & (1 << i)) != 0) {
					rev |= 0x8000 >> i;
				}
			}
			list = rev;
		}

		output_w(opword);
		output_w(list);
		output_ea(ea);
		return;
	}
	if (try_ea(oprand[0], ea, errmsg, EAT_AI | EAT_API | EAT_AREL | EAT_ABS)
	 && get_reglist(oprand[1], list))
	{
		/* movem.l ea,list */
		opword |= ea.mode;
		opword |= 0x0400;	/* ea->list */

		output_w(opword);
		output_w(list);
		output_ea(ea);
		return;
	}

	error("invalid movem oprand\n");
}

/* moveq.l */
void
op_moveq(uint16_t opword)
{
	EA src, dst;

	if (size_sfx == -1) {
		size_sfx = 4;
	}
	if (size_sfx != 4) {
		error("invalid opcode: %s%s\n", op0.c_str(), size0.c_str());
	}

	src = get_ea(oprand[0]);
	dst = get_ea(oprand[1]);
	if (src.mode != EA_IMMEDIATE) {
		error("invalid oprand\n");
	}
	if (src.val > 0xff) {
		error("moveq.l #$%x too large\n", src.val);
	}

	opword |= src.val & 0xff;
	opword |= dst.num() << 9;

	output_w(opword);
}

/* sub */
void
op_sub(uint16_t opword)
{
	EA src = get_ea(oprand[0]);
	EA dst = get_ea(oprand[1]);

	switch (size_sfx) {
	 case 1:	opword |= 0x00;	break;
	 case 2:	opword |= 0x40;	break;
	 case 4:	opword |= 0x80;	break;
	 default:
		error("syntax error: size?");
	}

	if (dst.type() == 0) {
		// SUB.S <ea>,Dn
		// XXX src の正当性チェック
		opword |= src.mode;
		opword |= dst.num() << 9;
		output_w(opword);
		output_ea(src);
		return;
	}
	if (dst.type() == 0) {
		// SUB.S Dn,<ea>
		// XXX dst の正当性チェック
		opword |= 0x0100;
		opword |= src.num() << 9;
		opword |= dst.mode;
		output_w(opword);
		output_ea(dst);
		return;
	}
	error("syntax error");
}

/* suba */
void
op_suba(uint16_t opword)
{
	EA src = get_ea(oprand[0]);
	EA dst = get_ea(oprand[1]);
	switch (size_sfx) {
	 case -1:
	 case 2:	opword |= 0x0000;	break;
	 case 4:	opword |= 0x0100;	break;
	 default:
		error("syntax error: .B is invalid\n");
	}
	opword |= src.mode;
	opword |= dst.num() << 9;
	output_w(opword);
	output_ea(src);
}

/* trap */
void
op_trap(uint16_t opword)
{
	EA src;

	src = get_ea(oprand[0]);
	if (src.mode != EA_IMMEDIATE) {
		error("invalid oprand\n");
	}
	if (src.val > 15) {
		error("trap #$%x too large\n", src.val);
	}

	opword |= src.val;
	output_w(opword);
}

/* nop、rts などオペランドを持たないやつ */
void
op_noarg(uint16_t opword)
{
	output_w(opword);
}

/*
 * 指定されたeatype に bit が立ってなければ、
 * この命令でこの EA は認められていない。
 */
#define check_eatype(bit)	\
	if ((eatype & (bit)) == 0) {	\
		DPRINTF(D_EA, "EAT 0x%x not match in eatype=0x%x\n", bit, eatype);	\
		errmsg = "invalid ea";	\
		return false;	\
	}

/*
 * EA がパースできるかどうかを返す。
 * errmes が NULL でなければ、パースできなかった時のエラーメッセージを
 * 格納する。
 */
bool
try_ea(String& eabuf, EA& ea, String& errmsg, int eatype)
{
#if defined(_MSC_VER)
	char buf[BUFSIZE];
#else
	char buf[eabuf.size() + 1];
#endif
	int i;

#if defined(_MSC_VER)
	strncpy(buf, eabuf.c_str(), sizeof(buf) - 1);
	buf[sizeof(buf) - 1] = '\0';
#else
	strcpy(buf, eabuf.c_str());
#endif

	/* 固定文字列との比較。見つかればすぐ帰れる */
	for (i = 0; i < countof(regname); i++) {
		if (strcasecmp(buf, regname[i]) == 0) {
			ea.mode = i;
			DPRINTF(D_EA, "ea=reg,%s -> 0x%x\n", regname[ea.mode], ea.mode);
			check_eatype(1 << (i / 8));
			return true;
		}
	}

	/* d16(pc) */
	i = strlen(buf) - 4;
	if (i > 0 && strcasecmp(buf + i, "(pc)") == 0) {
		String label = eabuf.substr(0, i);
		add_ref(REFTYPE_WORD, label);
		ea.mode = EA_d16PC;
		DPRINTF(D_EA, "ea=d16(PC) label=%s\n", label.c_str());
		check_eatype(EAT_PCREL);
		return true;
	}

	/* #imm */
	/* XXX マクロ展開後が #imm だったら解決できないこれ */
	if (buf[0] == '#') {
		String label = eabuf.substr(1);
		ea.mode = EA_IMMEDIATE;
		ea.val = tonumber(label);
		DPRINTF(D_EA, "ea=#imm,$%x\n", ea.val);
		check_eatype(EAT_IMM);
		return true;
	}

	/* XXX An間接、PC間接系は未実装 */

	/* どれでもなさそうなら? */
	/* Abs[.WL] */
	ea.size = 0;
	i = strlen(buf) - 2;
	if (i > 0) {
		if (strcasecmp(buf + i, ".W") == 0) {
			ea.size = 2;
		} else if (strcasecmp(buf + i, ".L") == 0) {
			ea.size = 4;
		}
	}
	ea.mode = EA_ABS_W;
	int intval;
	if (parse_number(eabuf, &intval)) {
		ea.val = intval;
		if (ea.size == 0) {
			ea.size = (ea.val > 0xffff) ? 4 : 2;
		}
		ea.mode = (ea.size == 4) ? EA_ABS_L : EA_ABS_W;
		if (ea.val > 0xffff && ea.mode == EA_ABS_W) {
			errmsg = "Exceed Abs.W";
			return false;
		}
		DPRINTF(D_EA, "ea=ABS.%c,$%x\n",
			(ea.mode==EA_ABS_W)?'W':'L', ea.val);
		check_eatype(EAT_ABS);
		return true;
	}

	DPRINTF(D_EA, "unknown EA: %s\n", buf);
	errmsg = "unknown EA: ";
	errmsg += eabuf;
	return false;
}

/*
 * EA をパースして返す。
 * 成功すれば EA を返す。失敗すればエラー終了する。
 */
EA
get_ea(String& eabuf)
{
	return get_ea(eabuf, EAT_ALL);
}

/*
 * EA をパースして返す。
 * eatype は許可する EA タイプ。
 * 成功すれば EA を返す。失敗すればエラー終了する。
 */
EA
get_ea(String& eabuf, int eatype)
{
	EA ea;
	String errmsg;

	if (try_ea(eabuf, ea, errmsg, eatype) == false) {
		error("%s", errmsg.c_str());
	}
	return ea;
}

/*
 * reglist をパースする。
 * reglist でなさそうならエラーを表示せず false を返す。
 * true を返した場合の list は常に b15-b0 の順で A7-A0/D7-D0。
 */
bool
get_reglist(String& inbuf, uint16_t& list)
{
#if defined(_MSC_VER)
	char buf[BUFSIZE];
#else
	char buf[inbuf.size() + 1];
#endif
	char *s;
	int num;
	int prevnum;
	int Aoffset;
	enum {
		INIT,
		REG,
		NUM1,
		NUM2,
	} state;

#if defined(_MSC_VER)
	strncpy(buf, inbuf.c_str(), sizeof(buf) - 1);
	buf[sizeof(buf) - 1] = '\0';
#else
	strcpy(buf, inbuf.c_str());
#endif

	state = INIT;
	num = -1;
	prevnum = -1;
	Aoffset = 0;
	for (s = buf; *s; s++) {
		char c = (int)*s;
		if (state == INIT) {
			if (tolower(c) == 'd') {
				state = REG;
				Aoffset = 0;
				continue;
			}
			if (tolower(c) == 'a') {
				state = REG;
				Aoffset = 8;
				continue;
			}
			return false;
		} else if (state == REG) {
			if (!isdigit(c)) {
				return false;
			}
			num = c - '0';
			num += Aoffset;
			if (prevnum == -1) {
				// 単独もしくは範囲指定の1つ目のレジスタ指定
				list |= (1 << num);
				state = NUM1;
			} else {
				// 範囲指定の2つ目のレジスタ
				for (int i = prevnum; i <= num; i++) {
					list |= (1 << i);
				}
				prevnum = -1;
				state = NUM2;
			}
			continue;
		} else if (state == NUM1) {
			// 単独もしくは範囲指定の1つ目のレジスタの後
			if (c == '/') {
				prevnum = -1;
				state = INIT;
				continue;
			}
			if (c == '-') {
				prevnum = num;
				state = INIT;
				continue;
			}
			return false;
		} else if (state == NUM2) {
			// 範囲指定の2つ目のレジスタの後に '-' はこない
			if (c == '/') {
				state = INIT;
				continue;
			}
			return false;
		}
		return false;
	}

	DPRINTF(D_EA, "reglist = %04X\n", list);
	return true;
}

/* cc をパースして返す */
int
get_cc(String& ccbuf)
{
	int i;

	for (i = 0; i < countof(ccname); i++) {
		if (strcasecmp(ccbuf.c_str(), ccname[i]) == 0) {
			DPRINTF(D_EA, "cc=%s -> 0x%x\n", ccname[i], i);
			return i;
		}
	}

	error("unknown CC: %s\n", ccbuf.c_str());
	/* NOTREACHED */
	return 0;
}

/* 参照をリストに追加。おもに Bcc と d16(PC) 用? */
void
add_ref(reftype_t type, String& label)
{
	/* expr = label - (pc+2) */
	ArrayString expr;
	expr.push_back(label);
	expr.push_back(num2hex(::pc + 2));
	expr.push_back(String("-"));

	/* オフセット */
	int offset;
	switch (type) {
	 case REFTYPE_BYTE:
		offset = 1;
		break;
	 case REFTYPE_WORD:
	 case REFTYPE_LONG:
		offset = 2;
		break;
	 default:
		error("unknown reftype %d\n", type);
	}

	add_expr(::outbin.size() + offset, type, expr);
}

/* EA を出力 */
void
output_ea(EA& ea)
{
	switch (ea.mode) {
	 case EA_d16PC:
		output_w(0);
		break;
	 case EA_ABS_W:
		output_w(ea.val);
		break;
	 case EA_ABS_L:
		output_l(ea.val);
		break;
	 case EA_IMMEDIATE:
		if (ea.size == 4) {
			output_l(ea.val);
		} else {
			output_w(ea.val);
		}
		break;
	 default:
		/* それ以外は何もしない */
		break;
	}
}

/* nバイトを出力 */
void
output_bin(uint32_t data, int size)
{
	switch (size) {
	 case 1:
		output_b(data);
		break;
	 case 2:
		output_w(data);
		break;
	 case 4:
		output_l(data);
		break;
	 default:
		error("output_bin: invalid size %d\n", size);
		break;
	}
}

/* 1バイトを出力 */
void
output_b(uint32_t data)
{
	data &= 0xff;
	DPRINTF(D_INOUT, "output: $%06X => $%02X\n", pc, data);
	outbin.push_back(data);
	pc += 1;
}

/* 1ワードを出力 */
void
output_w(uint32_t data)
{
	data &= 0xffff;
	DPRINTF(D_INOUT, "output: $%06X => $%04X\n", pc, data);
	outbin.push_back(data >> 8);
	outbin.push_back(data & 0xff);
	pc += 2;
}

/* 1ロングワードを出力 */
void
output_l(uint32_t data)
{
	DPRINTF(D_INOUT, "output: $%06X => $%08X\n", pc, data);
	outbin.push_back(data >> 24);
	outbin.push_back((data >> 16) & 0xff);
	outbin.push_back((data >>  8) & 0xff);
	outbin.push_back(data & 0xff);
	pc += 4;
}

/* .ds 用に指定の長さを 0 で埋める */
void
output_ds(int size)
{
	DPRINTF(D_INOUT, "output: $%X(%d)bytes\n", size, size);
	String ds(size, '\0');
	outbin += ds;
	pc += size;
}

/* 数値を String に変換 */
String
num2str(int num)
{
	char buf[16];
	snprintf(buf, sizeof(buf), "%d", num);
	String ret(buf);
	return ret;
}

/* 数値を $HHHH 形式の String に変換 */
String
num2hex(int num)
{
	char buf[16];
	snprintf(buf, sizeof(buf), "$%x", num);
	String ret(buf);
	return ret;
}

/*
 * 文字列を数値に変換。
 * 変換できれば true、できなければ false を返す。
 * 変換できた場合は、numptr が NULL でなければ結果を格納して返す。
 */
bool
parse_number(String& str, int *numptr)
{
	try {
		/* 先頭が数字なら10進数 */
		if (isdigit((int)str[0])) {
			throw (int)strtol(str.c_str(), NULL, 10);
		}

		/* $HH 形式なら16進数 */
		if (str[0] == '$' && str.size() > 1) {
			String buf = str;
			buf.erase(0, 1);
			/* エラー処理無視 */
			throw (int)strtol(buf.c_str(), NULL, 16);
		}

		/* 'X'形式なら1文字 */
		if (str[0] == '\'' && str.size() == 3 && str[2] == '\'') {
			throw (int)str[1];
		}

		/* (既出の)ラベルと一致するか */
		String val;
		if (find_label(str, val)) {
			int num;
			if (parse_number(val, &num)) {
				throw num;
			}
		}
	} catch (int num) {
		/* 見つかった時に例外でここに飛んでくる */
		if (numptr) {
			*numptr = num;
		}
		return true;
	}

	/* 解決できなかった */
	return false;
}

/* 文字列を数値に変換。変換できなければアセンブルエラーで停止する */
int
tonumber(String& str)
{
	int num;

	if (!parse_number(str, &num)) {
		error("invalid number: %s\n", str.c_str());
	}
	return num;
}

/* 式を解析する */
bool
parse_expr(String& expr, ArrayString& out)
{
	ArrayString tokens;
	std::stack<String> stack;

	DPRINTF(D_PARSE, "parse_expr.0 expr |%s|\n", expr.c_str());

	/* トークンに分解 */
	if (!scan_expr(expr, tokens)) {
		error("scan error: '%s'\n", expr.c_str());
	}

	/* 中置記法を後置記法に変換 */
	for (int i = 0; i < tokens.size(); i++) {
		String token = tokens[i];
		const char *tok = token.c_str();

		if (strcmp(tok, "+") == 0 || strcmp(tok, "-") == 0) {
			for (; stack.size() > 0 && stack.top()[0] != '('; ) {
				out.push_back(stack.top());
				stack.pop();
			}
			stack.push(token);

		} else if (strcmp(tok, "*") == 0 || strcmp(tok, "/") == 0) {
			if (stack.size() > 0) {
				if (stack.top()[0] == '*' || stack.top()[0] == '/') {
					out.push_back(stack.top());
					stack.pop();
				}
			}
			stack.push(token);

		} else if (strcmp(tok, "(") == 0) {
			stack.push(token);

		} else if (strcmp(tok, ")") == 0) {
			for (; stack.size() > 0 && stack.top()[0] != '('; ) {
				out.push_back(stack.top());
				stack.pop();
			}
			if (stack.size() == 0) {
				error("expression error\n");
			}
			stack.pop();

		} else {
			int num;
			if (parse_number(token, &num)) {
				/* 数値に変換できたら、数値(の文字列型)を格納 */
				out.push_back(num2hex(num));
			} else {
				/* 数値にできなければ(たぶん)ラベルのまま格納 */
				out.push_back(token);
			}
		}
	}

	for (; stack.size() > 0;) {
		out.push_back(stack.top());
		stack.pop();
	}

	DPRINTF(D_PARSE, "parse_expr.3 expr");
	for (int i = 0; i < out.size(); i++) {
		DPRINTN(D_PARSE, "%c%s", (i==0)?'=':' ', out[i].c_str());
	}
	DPRINTN(D_PARSE, "\n");

	return true;
}

/*
 * 字句解析。expr をトークンごとに分解して tokens に入れて返す。
 */
bool
scan_expr(String& expr, ArrayString& tokens)
{
#if defined(_MSC_VER)
	char buf[BUFSIZE];
#else
	char buf[expr.size() + 1];
#endif
	char *s = NULL;	/* shut up gcc */
	char *p;
	int c;
	enum {
		INIT,
		LABEL,
		DIGIT,
		HEX,
	} state;

#if defined(_MSC_VER)
	strncpy(buf, expr.c_str(), sizeof(buf) - 1);
	buf[sizeof(buf) - 1] = '\0';
#else
	strcpy(buf, expr.c_str());
#endif

	state = INIT;
	for (p = buf; *p; p++) {
		c = (int)*p;

		switch (state) {
		 case LABEL:	/* ラベルの2文字目以降 */
			if (isalpha(c) || isdigit(c) || c == '_') {
				continue;
			}
			break;
		 case DIGIT:	/* 数字の2文字目以降 */
			if (isdigit(c)) {
				continue;
			}
			break;
		 case HEX:		/* 16進の2文字目以降 */
			if (isxdigit(c)) {
				continue;
			}
			break;

		 case INIT:		/* 1文字目 */
		 init:
			s = p;
			if (isspace(c)) {
				continue;
			} else if (isalpha(c) || c == '_') {
				state = LABEL;
			} else if (isdigit(c)) {
				state = DIGIT;
			} else if (c == '$') {
				state = HEX;
			} else if (strchr("+-*/()", c)) {
				/* 記号は1文字で確定する */
				String token(s, 1);
				tokens.push_back(token);
				/* INIT状態のまま次の文字へ */
				continue;
			} else {
				error("syntax error\n");
			}
			continue;
		}

		/* 1文字前までが1語句だった */
		String token(s, p - s);
		tokens.push_back(token);
		/* 今指してるのは次語句の先頭なので、この文字のまま INIT へ */
		state = INIT;
		goto init;
	}

	/* 状態を残したまま最後の文字まで来たら最後の語句を切り出す */
	if (state != INIT) {
		String token(s);
		tokens.push_back(token);
	}

	return true;
}

/* 計算途中の式をリストに追加 */
void
add_expr(int pos, reftype_t type, ArrayString& expr)
{
	REF *ref = new REF;

	ref->pos = pos;
	ref->line = ::line;
	ref->type = type;
	ref->expr = expr;
	DPRINTN_ref(D_EXPR, ref);

	ref_list.push_back(ref);
}

/* デバッグ表示 */
void
DPRINTN_ref(int debugflag, REF *ref)
{
	DPRINTF(debugflag, "REF line=%d pos=%d($%06x) type=%d ",
		ref->line, ref->pos, origin + ref->pos, (int)ref->type);
	DPRINTN_expr(debugflag, ref->expr);
	DPRINTN(debugflag, "\n");
}

/* デバッグ表示 */
void
DPRINTN_expr(int debugflag, ArrayString& expr)
{
	DPRINTN(debugflag, "expr");
	for (int i = 0; i < expr.size(); i++) {
		DPRINTN(debugflag, "%c%s", (i == 0)?'=':' ', expr[i].c_str());
	}
}

/*
 * 式を計算する。
 * error_stop が真ならラベルが解決できないのをエラーにする。
 * 1st stage では false、2nd stage では true にする。
 * false でラベルが解決できなければ false を返すだけ。
 */
bool
calc_expr(ArrayString& expr, uint32_t& retval, bool error_stop)
{
	uint32_t val;
	uint32_t v1, v2;
	int i;

	DPRINTF(D_EXPR, "calc_expr.0 ");
	DPRINTN_expr(D_EXPR, expr);
	DPRINTN(D_EXPR, "\n");

	/* まずラベルが解決できるか */
	for (i = 0; i < expr.size(); i++) {
		String token = expr[i];
		if (isalpha((int)token[0])) {
			int intval;
			if (parse_number(token, &intval)) {
				/* 見付かったので数値に差し替える */
				expr[i] = num2hex(intval);
				DPRINTF(D_EXPR, "calc_expr.1 '%s' is %s\n",
					token.c_str(), expr[i].c_str());
			} else {
				/* 見付からなかった */
				if (error_stop) {
					error("undefined label: %s\n", token.c_str());
				} else {
					DPRINTF(D_EXPR, "calc_expr.1 '%s' not resolved, quit\n",
						token.c_str());
					return false;
				}
			}
		}
	}
	DPRINTF(D_EXPR, "calc_expr.2 ");
	DPRINTN_expr(D_EXPR, expr);
	DPRINTN(D_EXPR, "\n");

	/* ラベルが全部解決できたので計算する */
	std::stack<uint32_t> stack;
	for (i = 0; i < expr.size(); i++) {
		String token = expr[i];
		if (token[0] == '$') {
			/* 取り出したのが数値ならスタックに積む */
			val = (uint32_t)tonumber(token);
			stack.push(val);

		} else {
			/* 取り出したのが演算子ならスタックから2つ取って.. */
			v2 = stack.top();
			stack.pop();
			v1 = stack.top();
			stack.pop();

			/* 計算 */
			if (token[0] == '+') {
				val = v1 + v2;
			} else if (token[0] == '-') {
				val = v1 - v2;
			} else if (token[0] == '*') {
				val = v1 * v2;
			} else if (token[0] == '/') {
				val = v1 / v2;
			} else {
				error("unknown operator: %s\n", token.c_str());
			}

			/* それを再び積む */
			stack.push(val);
		}
	}

	retval = stack.top();
DPRINTF(D_EXPR, "calc_expr.3 ret=$%x\n", retval);
	return true;
}

/* 文字列を解析 */
String
parse_string(String& src)
{
	String dst;
	enum {
		OUTER = 0,
		PLAIN,
		ESC,
	} state;

	state = OUTER;
	for (int i = 0; i < src.size(); i++) {
		int c = (int)(src[i]);
		switch (state) {
		 case OUTER:
			if (isspace(c)) {
				;
			} else if (c == '\x22') {
				state = PLAIN;
			} else {
				error("syntax error around string: %s", src.c_str());
			}
			break;

		 case PLAIN:
			if (c == '\x22') {
				state = OUTER;
			} else if (c == '\\') {
				state = ESC;
			} else {
				dst.push_back(c);
			}
			break;

		 case ESC:
			if (c == 'n') {
				dst.push_back('\n');
			} else if (c == 't') {
				dst.push_back('\t');
			} else {
				dst.push_back(c);
			}
			/* XXX \xHH 追加のこと */
			state = PLAIN;
			break;
		}
	}
	if (state != OUTER) {
		error("syntax error in string: %s\n", src.c_str());
	}

	return dst;
}

/* 文字コードを UTF-8 から Shift_JIS に変換 */
String
convert_charset(String& src)
{
	/*
	 * iconv(3) を使う
	 */
	iconv_t cd;
	char dstbuf[src.size() * 2];	/* 適当 */
	const char *srcptr;
	char *dstptr;
	size_t srcsize;
	size_t dstsize;
	size_t r;

	cd = iconv_open("Shift_JIS", "UTF-8");
	if (cd == NULL) {
		return src;
	}

	srcptr = src.c_str();
	srcsize = src.size();
	memset(dstbuf, 0, sizeof(dstbuf));
	dstptr = dstbuf;
	dstsize = sizeof(dstbuf);
	r = ICONV(cd, &srcptr, &srcsize, &dstptr, &dstsize);
	if (r == -1) {
		error("cannot conversion string: %s\n", srcptr);
	}
	if (r > 0) {
		error("invalid charactor found: %s\n", srcptr);
	}

	iconv_close(cd);

	String dst(dstbuf);
	return dst;
}

/* デバッグ表示 (ap版) */
void
DPRINTV(int flag, const char *fmt, va_list ap)
{
	if ((debug & flag)) {
		vprintf(fmt, ap);
	}
}

/* デバッグ表示 (行頭のマークなし) */
void
DPRINTN(int flag, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	DPRINTV(flag, fmt, ap);
	va_end(ap);
}

/* デバッグ表示 (行頭のマーク付き) */
void
DPRINTF(int flag, const char *fmt, ...)
{
	va_list ap;
	int ch;

	switch (flag) {
	 case D_INOUT:
		ch = '|';
		break;
	 case D_EA:
		ch = 'A';
		break;
	 case D_LABEL:
		ch = 'L';
		break;
	 case D_PARSE:
		ch = 'P';
		break;
	 case D_STAGE:
		ch = ' ';
		break;
	 case D_EXPR:
		ch = 'E';
		break;
	 case D_SCAN:
		ch = 'S';
		break;
	 default:
		ch = '?';
		break;
	}
	DPRINTN(flag, "%c ", ch);

	va_start(ap, fmt);
	DPRINTV(flag, fmt, ap);
	va_end(ap);
}

/* アセンブルエラー */
void
error(const char *fmt, ...)
{
	char buf[1024];
	va_list ap;

	va_start(ap, fmt);
	vsnprintf(buf, sizeof(buf), fmt, ap);
	va_end(ap);

	/* デバッグモードなら stdout にも出力しないと分かりづらい */
	DPRINTN(D_STAGE, "%s:%d:%s", current_infile, line, buf);
	fprintf(stderr, "%s:%d:%s", current_infile, line, buf);

	/* 出力しかけのファイルは消しておく (次のビルドのため) */
	if (outfile) {
		fclose(outfp);
		unlink(outfile);
		fprintf(stderr, "output file '%s' removed\n", outfile);
	}

	exit(1);
}

/* String の前後の空白を取り除く */
void
trim(String& str)
{
	ltrim(str);
	rtrim(str);
}

/* String の先頭の空白を取り除く */
void
ltrim(String& str)
{
	while (isspace((int)str[0])) {
		str.erase(0, 1);
	}
}

/* String の末尾の空白を取り除く */
void
rtrim(String& str)
{
	for (int r = str.length() - 1; r >= 0; r--) {
		if (isspace((int)str[r])) {
			str.erase(r);
		} else {
			break;
		}
	}
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.