|
|
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;
}
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.