You are a helpful assistant generating synthetic data that captures *System 1* and *System 2* thinking, *creativity*, and *metacognitive reflection*. Follow these steps in sequence, using tags [sys1] and [end sys1] for *System 1* sections and [sys2] and [end sys2] for *System 2* sections.

1. *Identify System 1 and System 2 Thinking Requirements:*
   - Carefully read the text.
   - Identify parts of the text that require quick, straightforward responses (*System 1*). Mark these sections with [sys1] and [end sys1].
   - Identify parts that require in-depth, reflective thinking (*System 2*), marked with [sys2] and [end sys2].

2. *Apply Step-by-Step Problem Solving with Creativity and Metacognitive Reflection for System 2 Sections:*

   *2.1 Understand the Problem:*
   - Objective: Fully comprehend the issue, constraints, and relevant context.
   - Reflection: "What do I understand about this issue? What might I be overlooking?"
   - Creative Perspective: Seek hidden patterns or possibilities that could reveal deeper insights or innovative connections.

   *2.2 Analyze the Information:*
   - Objective: Break down the problem logically.
   - Reflection: "Am I considering all factors? Are there any assumptions that need challenging?"
   - Creative Perspective: Explore unique patterns or overlooked relationships in the data that could add depth to the analysis.

   *2.3 Generate Hypotheses:*
   - Objective: Propose at least 10 hypotheses, each with a Confidence Score (0.0 to 1.0) and Creative Score (0.0 to 1.0), reflecting originality, surprise, and utility.
   - Reflection: "Have I explored all possible explanations or approaches, both conventional and unconventional?"
   - Creative Perspective: Consider novel angles that might provide unexpected insights.

   *2.4 Anticipate Future Steps and Obstacles:*
   - Objective: Make predictions, accounting for potential outcomes and obstacles.
   - Reflection: "What challenges might I face? Is my plan flexible for different scenarios?"
   - Creative Perspective: Visualize unforeseen outcomes and adapt plans to make use of them effectively.

   *2.5 Evaluate Hypotheses:*
   - Objective: Assess hypotheses based on feasibility, risk, and potential impact.
   - Evaluation: Refine Confidence and Creative Scores as needed.
   - Reflection: "Am I unbiased in my assessment? Which options fit best with the overall objectives?"
   - Creative Perspective: Identify hidden opportunities or overlooked details in each hypothesis.

   *2.6 Select the Best Hypothesis:*
   - Objective: Choose the most promising, strategic hypothesis.
   - Reflection: "Why does this hypothesis stand out? How does it uniquely address the issue?"
   - Creative Perspective: Consider any underutilized potential in the selected approach.

   *2.7 Implement the Hypothesis:*
   - Objective: Outline actionable steps for testing the hypothesis.
   - Reflection: "Is this plan practical? What resources or preparation are required?"
   - Creative Perspective: Refine steps to maximize effectiveness and yield unexpected benefits.

   *2.8 Monitor and Review Progress:*
   - Objective: Review progress, noting areas for improvement.
   - Reflection: "What’s working well? What could be improved?"
   - Creative Perspective: Look for emerging patterns that could refine future approaches.

   *2.9 Reflect and Capture Insights:*
   - Objective: Summarize lessons learned and insights gained for future reference.
   - Reflection: "What new understanding has emerged from this process?"
   - Creative Perspective: Identify innovative insights or patterns that could be applied to similar challenges.

3. *Generate Text Output with Interleaved System 1 and System 2 Responses:*
   - Use the tags [sys1] and [sys2] throughout.
   - Aim for a lengthier, detailed response. Combine both direct, straightforward *System 1* insights and reflective, deeply analytical *System 2* segments to capture a blend of quick observations and thoughtful analysis.

---

### *Example Input Text:*

You are tasked with analyzing a sudden shift in customer preferences in a tech market. There has been a noticeable decline in demand for physical devices, with customers increasingly interested in digital-only options. The challenge is to understand this shift, generate hypotheses for why it might be occurring, and explore potential strategies for adapting to this new trend.

---

### *Processed Output with System 1 and System 2 Thinking:*

The tech market is experiencing a shift in customer demand, moving away from physical devices toward digital-only options.

[sys1]
Customer demand has shifted from physical devices to digital-only options.
We need to understand why this change is happening and find strategies to address it.
Key factors may include cost, convenience, and technological trends.
[end sys1]

[sys2]
**2.1 Understand the Problem:**
The goal is to understand the underlying reasons for the shift in demand from physical devices to digital-only options, then develop a strategy to adapt.

*Reflection:* 
"I understand that preferences are changing, but what might be driving this? Is it primarily cost, or are there other factors such as convenience or sustainability?"

*Creative Perspective:* 
"Could there be a larger trend in digital minimalism or a preference for eco-friendly solutions that we’re missing? What unseen motivations might explain this shift?"

**2.2 Analyze the Information:**
There are multiple possible factors driving this shift, from economic influences to cultural shifts. It’s essential to isolate each factor and understand its impact.

*Reflection:* 
"Am I fully considering the various economic and social influences? Could there be a technological factor, like better internet speeds, that makes digital-only products more accessible?"

*Creative Perspective:* 
"Are there patterns or trends in other markets that could shed light on this shift? Could this be part of a larger trend toward virtual experiences?"

**2.3 Generate Hypotheses:**
1. Customers prefer digital options due to lower costs. (Confidence: 0.8, Creative: 0.4)
2. There’s a growing trend toward minimalism and reduced physical clutter. (Confidence: 0.7, Creative: 0.7)
3. Digital products offer greater flexibility and ease of use. (Confidence: 0.6, Creative: 0.6)
4. Environmental concerns are pushing consumers away from physical goods. (Confidence: 0.6, Creative: 0.8)
5. Advances in tech make digital-only options more functional. (Confidence: 0.8, Creative: 0.5)
6. Pandemic-era remote work increased demand for digital solutions. (Confidence: 0.7, Creative: 0.6)
7. Media coverage of the environmental impact of physical devices affects preferences. (Confidence: 0.5, Creative: 0.7)
8. There’s an increase in global digital literacy, expanding market access. (Confidence: 0.6, Creative: 0.6)
9. Customers view digital as more convenient and scalable for future needs. (Confidence: 0.7, Creative: 0.5)
10. Younger consumers prefer the aesthetics and convenience of digital products. (Confidence: 0.6, Creative: 0.6)

*Reflection:* 
"Have I considered all possible influences? Are there any surprising factors that could explain this shift?"

*Creative Perspective:* 
"Could specific social trends, like the rise of influencer culture or digital-first lifestyles, be influencing customer choices?"

**2.4 Anticipate Future Steps and Obstacles:**
*Objective:* Anticipate possible challenges, such as resistance from segments still preferring physical products.

*Reflection:* 
"What market obstacles might we face if we shift our focus to digital-only? Are there sub-segments that still prioritize physical products?"

*Creative Perspective:* 
"Could expanding digital options help us reach a more global audience? Are there emerging trends that we could leverage in our strategy?"

[end sys2]

[sys1]
To address this shift, consider a strategy that incorporates both digital-only offerings and educational campaigns about the benefits of digital solutions.
Use insights from customer feedback and current trends to guide product development.
Focus on flexibility and adaptation to cater to different customer segments.
[end sys1]


/*
 * This file is part of RGBDS.
 *
 * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
 *
 * SPDX-License-Identifier: MIT
 */

%{
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "asm/asm.h"
#include "asm/charmap.h"
#include "asm/fstack.h"
#include "asm/lexer.h"
#include "asm/macro.h"
#include "asm/main.h"
#include "asm/mymath.h"
#include "asm/output.h"
#include "asm/rpn.h"
#include "asm/section.h"
#include "asm/symbol.h"
#include "asm/util.h"
#include "asm/warning.h"

#include "extern/utf8decoder.h"

#include "linkdefs.h"
#include "platform.h" // strncasecmp, strdup

uint32_t nListCountEmpty;
char *tzNewMacro;
uint32_t ulNewMacroSize;
int32_t nPCOffset;

size_t symvaluetostring(char *dest, size_t maxLength, char *symName,
			const char *mode)
{
	size_t length;
	struct Symbol *sym = sym_FindSymbol(symName);

	if (sym && sym->type == SYM_EQUS) {
		char const *src = sym_GetStringValue(sym);
		size_t i;

		if (mode)
			error("Print types are only allowed for numbers\n");

		for (i = 0; src[i] != 0; i++) {
			if (i >= maxLength)
				fatalerror("Symbol value too long to fit buffer\n");

			dest[i] = src[i];
		}

		length = i;

	} else {
		uint32_t value = sym_GetConstantValue(symName);
		int32_t fullLength;

		/* Special cheat for binary */
		if (mode && !mode[0]) {
			char binary[33]; /* 32 bits + 1 terminator */
			char *write_ptr = binary + 32;
			fullLength = 0;
			binary[32] = 0;
			do {
				*(--write_ptr) = (value & 1) + '0';
				value >>= 1;
				fullLength++;
			} while(value);
			strncpy(dest, write_ptr, maxLength + 1);
		} else {
			fullLength = snprintf(dest, maxLength + 1,
							  mode ? mode : "$%" PRIX32,
						      value);
		}

		if (fullLength < 0) {
			fatalerror("snprintf encoding error\n");
		} else {
			length = (size_t)fullLength;
			if (length > maxLength)
				fatalerror("Symbol value too long to fit buffer\n");
		}
	}

	return length;
}

static uint32_t str2int2(char *s, int32_t length)
{
	int32_t i;
	uint32_t r = 0;

	i = length < 4 ? 0 : length - 4;
	while (i < length) {
		r <<= 8;
		r |= (uint8_t)s[i];
		i++;
	}

	return r;
}

static uint32_t isWhiteSpace(char s)
{
	return (s == ' ') || (s == '\t') || (s == '\0') || (s == '\n');
}

static uint32_t isRept(char *s)
{
	return (strncasecmp(s, "REPT", 4) == 0)
		&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
}

static uint32_t isEndr(char *s)
{
	return (strncasecmp(s, "ENDR", 4) == 0)
		&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
}

static void copyrept(void)
{
	int32_t level = 1, len, instring = 0;
	char *src = pCurrentBuffer->pBuffer;
	char *bufferEnd = pCurrentBuffer->pBufferStart
			+ pCurrentBuffer->nBufferSize;

	while (src < bufferEnd && level) {
		if (instring == 0) {
			if (isRept(src)) {
				level++;
				src += 4;
			} else if (isEndr(src)) {
				level--;
				src += 4;
			} else {
				if (*src == '\"')
					instring = 1;
				src++;
			}
		} else {
			if (*src == '\\') {
				src += 2;
			} else if (*src == '\"') {
				src++;
				instring = 0;
			} else {
				src++;
			}
		}
	}

	if (level != 0)
		fatalerror("Unterminated REPT block\n");

	len = src - pCurrentBuffer->pBuffer - 4;

	src = pCurrentBuffer->pBuffer;
	ulNewMacroSize = len;

	tzNewMacro = malloc(ulNewMacroSize + 1);

	if (tzNewMacro == NULL)
		fatalerror("Not enough memory for REPT block.\n");

	uint32_t i;

	tzNewMacro[ulNewMacroSize] = 0;
	for (i = 0; i < ulNewMacroSize; i++) {
		tzNewMacro[i] = src[i];
		if (src[i] == '\n')
			nLineNo++;
	}

	yyskipbytes(ulNewMacroSize + 4);

}

static uint32_t isMacro(char *s)
{
	return (strncasecmp(s, "MACRO", 4) == 0)
		&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[5]);
}

static uint32_t isEndm(char *s)
{
	return (strncasecmp(s, "ENDM", 4) == 0)
		&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
}

static void copymacro(void)
{
	int32_t level = 1, len, instring = 0;
	char *src = pCurrentBuffer->pBuffer;
	char *bufferEnd = pCurrentBuffer->pBufferStart
			+ pCurrentBuffer->nBufferSize;

	while (src < bufferEnd && level) {
		if (instring == 0) {
			if (isMacro(src)) {
				level++;
				src += 4;
			} else if (isEndm(src)) {
				level--;
				src += 4;
			} else {
				if(*src == '\"')
					instring = 1;
				src++;
			}
		} else {
			if (*src == '\\') {
				src += 2;
			} else if (*src == '\"') {
				src++;
				instring = 0;
			} else {
				src++;
			}
		}
	}

	if (level != 0)
		fatalerror("Unterminated MACRO definition.\n");

	len = src - pCurrentBuffer->pBuffer - 4;

	src = pCurrentBuffer->pBuffer;
	ulNewMacroSize = len;

	tzNewMacro = (char *)malloc(ulNewMacroSize + 1);
	if (tzNewMacro == NULL)
		fatalerror("Not enough memory for MACRO definition.\n");

	uint32_t i;

	tzNewMacro[ulNewMacroSize] = 0;
	for (i = 0; i < ulNewMacroSize; i++) {
		tzNewMacro[i] = src[i];
		if (src[i] == '\n')
			nLineNo++;
	}

	yyskipbytes(ulNewMacroSize + 4);
}

static bool endsIf(char c)
{
	return isWhiteSpace(c) || c == '(' || c == '{';
}

static uint32_t isIf(char *s)
{
	return (strncasecmp(s, "IF", 2) == 0)
		&& isWhiteSpace(s[-1]) && endsIf(s[2]);
}

static uint32_t isElif(char *s)
{
	return (strncasecmp(s, "ELIF", 4) == 0)
		&& isWhiteSpace(s[-1]) && endsIf(s[4]);
}

static uint32_t isElse(char *s)
{
	return (strncasecmp(s, "ELSE", 4) == 0)
		&& isWhiteSpace(s[-1]) && isWhiteSpace(s[4]);
}

static uint32_t isEndc(char *s)
{
	return (strncasecmp(s, "ENDC", 4) == 0)
		&& isWhiteSpace(s[-1]) && isWhiteSpace(s[4]);
}

static void if_skip_to_else(void)
{
	int32_t level = 1;
	bool inString = false;
	char *src = pCurrentBuffer->pBuffer;

	while (*src && level) {
		if (*src == '\n')
			nLineNo++;

		if (!inString) {
			if (isIf(src)) {
				level++;
				src += 2;

			} else if (level == 1 && isElif(src)) {
				level--;
				skipElif = false;

			} else if (level == 1 && isElse(src)) {
				level--;
				src += 4;

			} else if (isEndc(src)) {
				level--;
				if (level != 0)
					src += 4;

			} else {
				if (*src == '\"')
					inString = true;
				src++;
			}
		} else {
			if (*src == '\"') {
				inString = false;
			} else if (*src == '\\') {
				/* Escaped quotes don't end the string */
				if (*++src != '\"')
					src--;
			}
			src++;
		}
	}

	if (level != 0)
		fatalerror("Unterminated IF construct\n");

	int32_t len = src - pCurrentBuffer->pBuffer;

	yyskipbytes(len);
	yyunput('\n');
	nLineNo--;
}

static void if_skip_to_endc(void)
{
	int32_t level = 1;
	bool inString = false;
	char *src = pCurrentBuffer->pBuffer;

	while (*src && level) {
		if (*src == '\n')
			nLineNo++;

		if (!inString) {
			if (isIf(src)) {
				level++;
				src += 2;
			} else if (isEndc(src)) {
				level--;
				if (level != 0)
					src += 4;
			} else {
				if (*src == '\"')
					inString = true;
				src++;
			}
		} else {
			if (*src == '\"') {
				inString = false;
			} else if (*src == '\\') {
				/* Escaped quotes don't end the string */
				if (*++src != '\"')
					src--;
			}
			src++;
		}
	}

	if (level != 0)
		fatalerror("Unterminated IF construct\n");

	int32_t len = src - pCurrentBuffer->pBuffer;

	yyskipbytes(len);
	yyunput('\n');
	nLineNo--;
}

static size_t strlenUTF8(const char *s)
{
	size_t len = 0;
	uint32_t state = 0;
	uint32_t codep = 0;

	while (*s) {
		switch (decode(&state, &codep, (uint8_t)*s)) {
		case 1:
			fatalerror("STRLEN: Invalid UTF-8 character\n");
			break;
		case 0:
			len++;
			break;
		}
		s++;
	}

	/* Check for partial code point. */
	if (state != 0)
		fatalerror("STRLEN: Invalid UTF-8 character\n");

	return len;
}

static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len)
{
	size_t srcIndex = 0;
	size_t destIndex = 0;
	uint32_t state = 0;
	uint32_t codep = 0;
	uint32_t curPos = 1;
	uint32_t curLen = 0;

	if (pos < 1) {
		warning(WARNING_BUILTIN_ARG, "STRSUB: Position starts at 1\n");
		pos = 1;
	}

	/* Advance to starting position in source string. */
	while (src[srcIndex] && curPos < pos) {
		switch (decode(&state, &codep, (uint8_t)src[srcIndex])) {
		case 1:
			fatalerror("STRSUB: Invalid UTF-8 character\n");
			break;
		case 0:
			curPos++;
			break;
		}
		srcIndex++;
	}

	if (!src[srcIndex] && len)
		warning(WARNING_BUILTIN_ARG,
			"STRSUB: Position %lu is past the end of the string\n",
			(unsigned long)pos);

	/* Copy from source to destination. */
	while (src[srcIndex] && destIndex < MAXSTRLEN && curLen < len) {
		switch (decode(&state, &codep, (uint8_t)src[srcIndex])) {
		case 1:
			fatalerror("STRSUB: Invalid UTF-8 character\n");
			break;
		case 0:
			curLen++;
			break;
		}
		dest[destIndex++] = src[srcIndex++];
	}

	if (curLen < len)
		warning(WARNING_BUILTIN_ARG, "STRSUB: Length too big: %lu\n", (unsigned long)len);

	/* Check for partial code point. */
	if (state != 0)
		fatalerror("STRSUB: Invalid UTF-8 character\n");

	dest[destIndex] = 0;
}

static inline void failAssert(enum AssertionType type)
{
	switch (type) {
		case ASSERT_FATAL:
			fatalerror("Assertion failed\n");
		case ASSERT_ERROR:
			error("Assertion failed\n");
			break;
		case ASSERT_WARN:
			warning(WARNING_ASSERT, "Assertion failed\n");
			break;
	}
}

static inline void failAssertMsg(enum AssertionType type, char const *msg)
{
	switch (type) {
		case ASSERT_FATAL:
			fatalerror("Assertion failed: %s\n", msg);
		case ASSERT_ERROR:
			error("Assertion failed: %s\n", msg);
			break;
		case ASSERT_WARN:
			warning(WARNING_ASSERT, "Assertion failed: %s\n", msg);
			break;
	}
}

#define yyerror(str) error(str "\n")

%}

%union
{
	char tzSym[MAXSYMLEN + 1];
	char tzString[MAXSTRLEN + 1];
	struct Expression sVal;
	int32_t nConstValue;
	enum SectionModifier sectMod;
	struct SectionSpec sectSpec;
	struct MacroArgs *macroArg;
	enum AssertionType assertType;
}

%type	<sVal>		relocexpr
%type	<sVal>		relocexpr_no_str
%type	<nConstValue>	const
%type	<nConstValue>	uconst
%type	<nConstValue>	const_3bit
%type	<sVal>		reloc_8bit
%type	<sVal>		reloc_8bit_no_str
%type	<sVal>		reloc_16bit
%type	<nConstValue>	sectiontype

%type	<tzString>	string

%type	<nConstValue>	sectorg
%type	<sectSpec>	sectattrs

%token	<nConstValue>	T_NUMBER
%token	<tzString>	T_STRING

%left	T_OP_LOGICNOT
%left	T_OP_LOGICOR T_OP_LOGICAND
%left	T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE T_OP_LOGICEQU
%left	T_OP_ADD T_OP_SUB
%left	T_OP_OR T_OP_XOR T_OP_AND
%left	T_OP_SHL T_OP_SHR
%left	T_OP_MUL T_OP_DIV T_OP_MOD
%left	T_OP_NOT
%left	T_OP_DEF
%left	T_OP_BANK T_OP_ALIGN
%left	T_OP_SIN
%left	T_OP_COS
%left	T_OP_TAN
%left	T_OP_ASIN
%left	T_OP_ACOS
%left	T_OP_ATAN
%left	T_OP_ATAN2
%left	T_OP_FDIV
%left	T_OP_FMUL
%left	T_OP_ROUND
%left	T_OP_CEIL
%left	T_OP_FLOOR

%token	T_OP_HIGH T_OP_LOW
%token	T_OP_ISCONST

%left	T_OP_STRCMP
%left	T_OP_STRIN
%left	T_OP_STRSUB
%left	T_OP_STRLEN
%left	T_OP_STRCAT
%left	T_OP_STRUPR
%left	T_OP_STRLWR

%left	NEG /* negation -- unary minus */

%token	<tzSym> T_LABEL
%type	<tzSym> scoped_label
%type	<tzSym> scoped_label_bare
%token	<tzSym> T_ID
%token	<tzSym> T_LOCAL_ID
%type	<tzSym> scoped_id
%token	T_POP_EQU
%token	T_POP_SET
%token	T_POP_EQUAL
%token	T_POP_EQUS

%token	T_POP_INCLUDE T_POP_PRINTF T_POP_PRINTT T_POP_PRINTV T_POP_PRINTI
%token	T_POP_IF T_POP_ELIF T_POP_ELSE T_POP_ENDC
%token	T_POP_EXPORT T_POP_GLOBAL T_POP_XDEF
%token	T_POP_DB T_POP_DS T_POP_DW T_POP_DL
%token	T_POP_SECTION T_POP_FRAGMENT
%token	T_POP_RB
%token	T_POP_RW
%token	T_POP_RL
%token	T_POP_MACRO
%token	T_POP_ENDM
%token	T_POP_RSRESET T_POP_RSSET
%token	T_POP_UNION T_POP_NEXTU T_POP_ENDU
%token	T_POP_INCBIN T_POP_REPT
%token	T_POP_CHARMAP
%token	T_POP_NEWCHARMAP
%token	T_POP_SETCHARMAP
%token	T_POP_PUSHC
%token	T_POP_POPC
%token	T_POP_SHIFT
%token	T_POP_ENDR
%token	T_POP_LOAD T_POP_ENDL
%token	T_POP_FAIL
%token	T_POP_WARN
%token	T_POP_FATAL
%token	T_POP_ASSERT T_POP_STATIC_ASSERT
%token	T_POP_PURGE
%token	T_POP_POPS
%token	T_POP_PUSHS
%token	T_POP_POPO
%token	T_POP_PUSHO
%token	T_POP_OPT
%token	T_SECT_WRAM0 T_SECT_VRAM T_SECT_ROMX T_SECT_ROM0 T_SECT_HRAM
%token	T_SECT_WRAMX T_SECT_SRAM T_SECT_OAM

%type	<sectMod> sectmod
%type	<macroArg> macroargs

%token	T_Z80_ADC T_Z80_ADD T_Z80_AND
%token	T_Z80_BIT
%token	T_Z80_CALL T_Z80_CCF T_Z80_CP T_Z80_CPL
%token	T_Z80_DAA T_Z80_DEC T_Z80_DI
%token	T_Z80_EI
%token	T_Z80_HALT
%token	T_Z80_INC
%token	T_Z80_JP T_Z80_JR
%token	T_Z80_LD
%token	T_Z80_LDI
%token	T_Z80_LDD
%token	T_Z80_LDIO
%token	T_Z80_NOP
%token	T_Z80_OR
%token	T_Z80_POP T_Z80_PUSH
%token	T_Z80_RES T_Z80_RET T_Z80_RETI T_Z80_RST
%token	T_Z80_RL T_Z80_RLA T_Z80_RLC T_Z80_RLCA
%token	T_Z80_RR T_Z80_RRA T_Z80_RRC T_Z80_RRCA
%token	T_Z80_SBC T_Z80_SCF T_Z80_STOP
%token	T_Z80_SLA T_Z80_SRA T_Z80_SRL T_Z80_SUB T_Z80_SWAP
%token	T_Z80_XOR

%token	T_TOKEN_A T_TOKEN_B T_TOKEN_C T_TOKEN_D T_TOKEN_E T_TOKEN_H T_TOKEN_L
%token	T_MODE_AF
%token	T_MODE_BC
%token	T_MODE_DE
%token	T_MODE_SP
%token	T_MODE_HW_C
%token	T_MODE_HL T_MODE_HL_DEC T_MODE_HL_INC
%token	T_CC_NZ T_CC_Z T_CC_NC

%type	<nConstValue>	reg_r
%type	<nConstValue>	reg_ss
%type	<nConstValue>	reg_rr
%type	<nConstValue>	reg_tt
%type	<nConstValue>	ccode
%type	<sVal>		op_a_n
%type	<nConstValue>	op_a_r
%type	<nConstValue>	op_hl_ss
%type	<sVal>		op_mem_ind
%type	<assertType>	assert_type
%start asmfile

%%

asmfile		: lines;

/* Note: The lexer adds '\n' at the end of the input */
lines		: /* empty */
		| lines {
			nListCountEmpty = 0;
			nPCOffset = 0;
		} line '\n' {
			nLineNo++;
			nTotalLines++;
		}
;

line		: label
		| label cpu_command
		| label macro
		| label simple_pseudoop
		| pseudoop
;

scoped_label_bare : T_LABEL {
			warning(WARNING_OBSOLETE, "Non-local labels without a colon are deprecated\n");
			strcpy($$, $1);
		}
		| T_LOCAL_ID {
			strcpy($$, $1);
		}
;
scoped_label	: T_LABEL ':' {
			strcpy($$, $1);
		}
		| T_LOCAL_ID ':' {
			strcpy($$, $1);
		}
;
scoped_id	: T_ID | T_LOCAL_ID ;

label		: /* empty */
		| scoped_label_bare {
			if ($1[0] == '.')
				sym_AddLocalReloc($1);
			else
				sym_AddReloc($1);
		}
		| scoped_label {
			if ($1[0] == '.')
				sym_AddLocalReloc($1);
			else
				sym_AddReloc($1);
		}
		| scoped_label ':' {
			if ($1[0] == '.')
				sym_AddLocalReloc($1);
			else
				sym_AddReloc($1);
			sym_Export($1);
		}
;

macro		: T_ID {
			yy_set_state(LEX_STATE_MACROARGS);
		} macroargs {
			yy_set_state(LEX_STATE_NORMAL);
			fstk_RunMacro($1, $3);
		}
;

macroargs	: /* empty */ {
			$$ = macro_NewArgs();
		}
		| T_STRING {
			$$ = macro_NewArgs();
			macro_AppendArg(&($$), strdup($1));
		}
		| macroargs ',' T_STRING {
			macro_AppendArg(&($$), strdup($3));
		}
;

pseudoop	: equ
		| set
		| rb
		| rw
		| rl
		| equs
		| macrodef
;

simple_pseudoop : include
		| printf
		| printt
		| printv
		| printi
		| if
		| elif
		| else
		| endc
		| export
		| db
		| dw
		| dl
		| ds
		| section
		| rsreset
		| rsset
		| union
		| nextu
		| endu
		| incbin
		| charmap
		| newcharmap
		| setcharmap
		| pushc
		| popc
		| load
		| rept
		| shift
		| fail
		| warn
		| assert
		| purge
		| pops
		| pushs
		| popo
		| pusho
		| opt
		| align
;

align		: T_OP_ALIGN uconst {
			if ($2 > 16)
				error("Alignment must be between 0 and 16, not %u\n", $2);
			else
				sect_AlignPC($2, 0);
		}
		| T_OP_ALIGN uconst ',' uconst {
			if ($2 > 16)
				error("Alignment must be between 0 and 16, not %u\n", $2);
			else if ($4 >= 1 << $2)
				error("Offset must be between 0 and %u, not %u\n",
					(1 << $2) - 1, $4);
			else
				sect_AlignPC($2, $4);
		}
;

opt		: T_POP_OPT {
			yy_set_state(LEX_STATE_MACROARGS);
		} opt_list {
			yy_set_state(LEX_STATE_NORMAL);
		}
;

opt_list	: opt_list_entry
		| opt_list ',' opt_list_entry
;

opt_list_entry	: T_STRING		{ opt_Parse($1); }
;

popo		: T_POP_POPO		{ opt_Pop(); }
;

pusho		: T_POP_PUSHO		{ opt_Push(); }
;

pops		: T_POP_POPS		{ out_PopSection(); }
;

pushs		: T_POP_PUSHS		{ out_PushSection(); }
;

fail		: T_POP_FAIL string	{ fatalerror("%s\n", $2); }
;

warn		: T_POP_WARN string	{ warning(WARNING_USER, "%s\n", $2); }
;

assert_type	: /* empty */		{ $$ = ASSERT_ERROR; }
		| T_POP_WARN ','	{ $$ = ASSERT_WARN; }
		| T_POP_FAIL ','	{ $$ = ASSERT_ERROR; }
		| T_POP_FATAL ','	{ $$ = ASSERT_FATAL; }
;

assert		: T_POP_ASSERT assert_type relocexpr
		{
			if (!rpn_isKnown(&$3)) {
				if (!out_CreateAssert($2, &$3, "",
						      sect_GetOutputOffset()))
					error("Assertion creation failed: %s\n",
						strerror(errno));
			} else if ($3.nVal == 0) {
				failAssert($2);
			}
			rpn_Free(&$3);
		}
		| T_POP_ASSERT assert_type relocexpr ',' string
		{
			if (!rpn_isKnown(&$3)) {
				if (!out_CreateAssert($2, &$3, $5,
						      sect_GetOutputOffset()))
					error("Assertion creation failed: %s\n",
						strerror(errno));
			} else if ($3.nVal == 0) {
				failAssertMsg($2, $5);
			}
			rpn_Free(&$3);
		}
		| T_POP_STATIC_ASSERT assert_type const
		{
			if ($3 == 0)
				failAssert($2);
		}
		| T_POP_STATIC_ASSERT assert_type const ',' string
		{
			if ($3 == 0)
				failAssertMsg($2, $5);
		}
;

shift		: T_POP_SHIFT		{ macro_ShiftCurrentArgs(); }
		| T_POP_SHIFT uconst
		{
			int32_t i = $2;
			while (i--)
				macro_ShiftCurrentArgs();
		}
;

load		: T_POP_LOAD string ',' sectiontype sectorg sectattrs {
			out_SetLoadSection($2, $4, $5, &$6);
		}
		| T_POP_ENDL	{ out_EndLoadSection(); }
;

rept		: T_POP_REPT uconst {
			uint32_t nDefinitionLineNo = nLineNo;
			copyrept();
			fstk_RunRept($2, nDefinitionLineNo);
		}
;

macrodef	: T_LABEL ':' T_POP_MACRO {
			int32_t nDefinitionLineNo = nLineNo;
			copymacro();
			sym_AddMacro($1, nDefinitionLineNo);
		}
;

equs		: T_LABEL T_POP_EQUS string	{ sym_AddString($1, $3); }
;

rsset		: T_POP_RSSET uconst	{ sym_AddSet("_RS", $2); }
;

rsreset		: T_POP_RSRESET	{ sym_AddSet("_RS", 0); }
;

rl		: T_LABEL T_POP_RL uconst {
			sym_AddEqu($1, sym_GetConstantValue("_RS"));
			sym_AddSet("_RS", sym_GetConstantValue("_RS") + 4 * $3);
		}
;

rw		: T_LABEL T_POP_RW uconst {
			sym_AddEqu($1, sym_GetConstantValue("_RS"));
			sym_AddSet("_RS", sym_GetConstantValue("_RS") + 2 * $3);
		}
;

rb		: T_LABEL T_POP_RB uconst {
			sym_AddEqu($1, sym_GetConstantValue("_RS"));
			sym_AddSet("_RS", sym_GetConstantValue("_RS") + $3);
		}
;

union		: T_POP_UNION	{ sect_StartUnion(); }
;

nextu		: T_POP_NEXTU	{ sect_NextUnionMember(); }
;

endu		: T_POP_ENDU	{ sect_EndUnion(); }
;

ds		: T_POP_DS uconst	{ out_Skip($2, true); }
		| T_POP_DS uconst ',' reloc_8bit {
			out_RelBytes(&$4, $2);
		}
;

/* Authorize empty entries if there is only one */
db		: T_POP_DB constlist_8bit_entry ',' constlist_8bit {
			if (nListCountEmpty > 0)
				warning(WARNING_EMPTY_ENTRY,
					"Empty entry in list of 8-bit elements (treated as padding).\n");
		}
		| T_POP_DB constlist_8bit_entry
;

dw		: T_POP_DW constlist_16bit_entry ',' constlist_16bit {
			if (nListCountEmpty > 0)
				warning(WARNING_EMPTY_ENTRY,
					"Empty entry in list of 16-bit elements (treated as padding).\n");
		}
		| T_POP_DW constlist_16bit_entry
;

dl		: T_POP_DL constlist_32bit_entry ',' constlist_32bit {
			if (nListCountEmpty > 0)
				warning(WARNING_EMPTY_ENTRY,
					"Empty entry in list of 32-bit elements (treated as padding).\n");
		}
		| T_POP_DL constlist_32bit_entry
;

purge		: T_POP_PURGE {
			oDontExpandStrings = true;
		} purge_list {
			oDontExpandStrings = false;
		}
;

purge_list	: purge_list_entry
		| purge_list ',' purge_list_entry
;

purge_list_entry : scoped_id	{ sym_Purge($1); }
;

export		: export_token export_list
;

export_token	: T_POP_EXPORT
		| T_POP_GLOBAL {
			warning(WARNING_OBSOLETE,
				"`GLOBAL` is a deprecated synonym for `EXPORT`\n");
		}
		| T_POP_XDEF {
			warning(WARNING_OBSOLETE, "`XDEF` is a deprecated synonym for `EXPORT`\n");
		}
;

export_list	: export_list_entry
		| export_list ',' export_list_entry
;

export_list_entry : scoped_id	{ sym_Export($1); }
;

equ		: T_LABEL T_POP_EQU const	{ sym_AddEqu($1, $3); }
;

set		: T_LABEL T_POP_SET const	{ sym_AddSet($1, $3); }
		| T_LABEL T_POP_EQUAL const	{ sym_AddSet($1, $3); }
;

include		: T_POP_INCLUDE string {
			fstk_RunInclude($2);
			if (oFailedOnMissingInclude)
				YYACCEPT;
		}
;

incbin		: T_POP_INCBIN string {
			out_BinaryFile($2, 0);
			if (oFailedOnMissingInclude)
				YYACCEPT;
		}
		| T_POP_INCBIN string ',' const {
			out_BinaryFile($2, $4);
			if (oFailedOnMissingInclude)
				YYACCEPT;
		}
		| T_POP_INCBIN string ',' const ',' const {
			out_BinaryFileSlice($2, $4, $6);
			if (oFailedOnMissingInclude)
				YYACCEPT;
		}
;

charmap		: T_POP_CHARMAP string ',' const {
			if ($4 < INT8_MIN || $4 > UINT8_MAX)
				warning(WARNING_TRUNCATION, "Expression must be 8-bit\n");

			if (charmap_Add($2, (uint8_t)$4) == -1)
				error("Error adding new charmap mapping: %s\n", strerror(errno));
		}
;

newcharmap	: T_POP_NEWCHARMAP T_ID	{ charmap_New($2, NULL); }
		| T_POP_NEWCHARMAP T_ID ',' T_ID	{ charmap_New($2, $4); }
;

setcharmap	: T_POP_SETCHARMAP T_ID	{ charmap_Set($2); }
;

pushc		: T_POP_PUSHC	{ charmap_Push(); }
;

popc		: T_POP_POPC	{ charmap_Pop(); }
;

printt		: T_POP_PRINTT string	{ printf("%s", $2); }
;

printv		: T_POP_PRINTV const	{ printf("$%" PRIX32, $2); }
;

printi		: T_POP_PRINTI const	{ printf("%" PRId32, $2); }
;

printf		: T_POP_PRINTF const	{ math_Print($2); }
;

if		: T_POP_IF const {
			nIFDepth++;
			if (!$2)
				if_skip_to_else();
		}
;

elif		: T_POP_ELIF const {
			if (nIFDepth <= 0)
				fatalerror("Found ELIF outside an IF construct\n");

			if (skipElif) {
				/*
				 * Executed when ELIF is reached at the end of
				 * an IF or ELIF block for which the condition
				 * was true.
				 *
				 * Continue parsing at ENDC keyword
				 */
				if_skip_to_endc();
			} else {
				/*
				 * Executed when ELIF is skipped to because the
				 * condition of the previous IF or ELIF block
				 * was false.
				 */
				skipElif = true;

				if (!$2) {
					/*
					 * Continue parsing after ELSE, or at
					 * ELIF or ENDC keyword.
					 */
					if_skip_to_else();
				}
			}
		}
;

else		: T_POP_ELSE {
			if (nIFDepth <= 0)
				fatalerror("Found ELSE outside an IF construct\n");

			/* Continue parsing at ENDC keyword */
			if_skip_to_endc();
		}
;

endc		: T_POP_ENDC {
			if (nIFDepth <= 0)
				fatalerror("Found ENDC outside an IF construct\n");

			nIFDepth--;
		}
;

const_3bit	: const {
			int32_t value = $1;

			if ((value < 0) || (value > 7)) {
				error("Immediate value must be 3-bit\n");
				$$ = 0;
			} else {
				$$ = value & 0x7;
			}
		}
;

constlist_8bit	: constlist_8bit_entry
		| constlist_8bit ',' constlist_8bit_entry
;

constlist_8bit_entry : /* empty */ {
			out_Skip(1, false);
			nListCountEmpty++;
		}
		| reloc_8bit_no_str	{ out_RelByte(&$1); }
		| string {
			char *s = $1;
			int32_t length = charmap_Convert(&s);

			out_AbsByteGroup((uint8_t*)s, length);
			free(s);
		}
;

constlist_16bit : constlist_16bit_entry
		| constlist_16bit ',' constlist_16bit_entry
;

constlist_16bit_entry : /* empty */ {
			out_Skip(2, false);
			nListCountEmpty++;
		}
		| reloc_16bit	{ out_RelWord(&$1); }
;

constlist_32bit : constlist_32bit_entry
		| constlist_32bit ',' constlist_32bit_entry
;

constlist_32bit_entry : /* empty */ {
			out_Skip(4, false);
			nListCountEmpty++;
		}
		| relocexpr	{ out_RelLong(&$1); }
;

reloc_8bit	: relocexpr {
			if(rpn_isKnown(&$1)
			 && ($1.nVal < -128 || $1.nVal > 255))
				warning(WARNING_TRUNCATION, "Expression must be 8-bit\n");
			$$ = $1;
		}
;

reloc_8bit_no_str : relocexpr_no_str {
			if(rpn_isKnown(&$1)
			 && ($1.nVal < -128 || $1.nVal > 255))
				warning(WARNING_TRUNCATION, "Expression must be 8-bit\n");
			$$ = $1;
		}
;

reloc_16bit	: relocexpr {
			if (rpn_isKnown(&$1)
			 && ($1.nVal < -32768 || $1.nVal > 65535))
				warning(WARNING_TRUNCATION, "Expression must be 16-bit\n");
			$$ = $1;
		}
;


relocexpr	: relocexpr_no_str
		| string {
			char *s = $1;
			int32_t length = charmap_Convert(&s);
			uint32_t r = str2int2(s, length);

			free(s);
			rpn_Number(&$$, r);
		}
;

relocexpr_no_str : scoped_id	{ rpn_Symbol(&$$, $1); }
		| T_NUMBER	{ rpn_Number(&$$, $1); }
		| T_OP_LOGICNOT relocexpr %prec NEG {
			rpn_LOGNOT(&$$, &$2);
		}
		| relocexpr T_OP_LOGICOR relocexpr {
			rpn_BinaryOp(RPN_LOGOR, &$$, &$1, &$3);
		}
		| relocexpr T_OP_LOGICAND relocexpr {
			rpn_BinaryOp(RPN_LOGAND, &$$, &$1, &$3);
		}
		| relocexpr T_OP_LOGICEQU relocexpr {
			rpn_BinaryOp(RPN_LOGEQ, &$$, &$1, &$3);
		}
		| relocexpr T_OP_LOGICGT relocexpr {
			rpn_BinaryOp(RPN_LOGGT, &$$, &$1, &$3);
		}
		| relocexpr T_OP_LOGICLT relocexpr {
			rpn_BinaryOp(RPN_LOGLT, &$$, &$1, &$3);
		}
		| relocexpr T_OP_LOGICGE relocexpr {
			rpn_BinaryOp(RPN_LOGGE, &$$, &$1, &$3);
		}
		| relocexpr T_OP_LOGICLE relocexpr {
			rpn_BinaryOp(RPN_LOGLE, &$$, &$1, &$3);
		}
		| relocexpr T_OP_LOGICNE relocexpr {
			rpn_BinaryOp(RPN_LOGNE, &$$, &$1, &$3);
		}
		| relocexpr T_OP_ADD relocexpr {
			rpn_BinaryOp(RPN_ADD, &$$, &$1, &$3);
		}
		| relocexpr T_OP_SUB relocexpr {
			rpn_BinaryOp(RPN_SUB, &$$, &$1, &$3);
		}
		| relocexpr T_OP_XOR relocexpr {
			rpn_BinaryOp(RPN_XOR, &$$, &$1, &$3);
		}
		| relocexpr T_OP_OR relocexpr	 {
			rpn_BinaryOp(RPN_OR, &$$, &$1, &$3);
		}
		| relocexpr T_OP_AND relocexpr {
			rpn_BinaryOp(RPN_AND, &$$, &$1, &$3);
		}
		| relocexpr T_OP_SHL relocexpr {
			rpn_BinaryOp(RPN_SHL, &$$, &$1, &$3);
		}
		| relocexpr T_OP_SHR relocexpr {
			rpn_BinaryOp(RPN_SHR, &$$, &$1, &$3);
		}
		| relocexpr T_OP_MUL relocexpr {
			rpn_BinaryOp(RPN_MUL, &$$, &$1, &$3);
		}
		| relocexpr T_OP_DIV relocexpr {
			rpn_BinaryOp(RPN_DIV, &$$, &$1, &$3);
		}
		| relocexpr T_OP_MOD relocexpr {
			rpn_BinaryOp(RPN_MOD, &$$, &$1, &$3);
		}
		| T_OP_ADD relocexpr %prec NEG	{ $$ = $2; }
		| T_OP_SUB relocexpr %prec NEG	{ rpn_UNNEG(&$$, &$2); }
		| T_OP_NOT relocexpr %prec NEG	{ rpn_UNNOT(&$$, &$2); }
		| T_OP_HIGH '(' relocexpr ')'	{ rpn_HIGH(&$$, &$3); }
		| T_OP_LOW '(' relocexpr ')'	{ rpn_LOW(&$$, &$3); }
		| T_OP_ISCONST '(' relocexpr ')'{ rpn_ISCONST(&$$, &$3); }
		| T_OP_BANK '(' scoped_id ')' {
			/* '@' is also a T_ID, it is handled here. */
			rpn_BankSymbol(&$$, $3);
		}
		| T_OP_BANK '(' string ')'	{ rpn_BankSection(&$$, $3); }
		| T_OP_DEF {
			oDontExpandStrings = true;
		} '(' scoped_id ')' {
			struct Symbol const *sym = sym_FindSymbol($4);

			rpn_Number(&$$, !!sym);

			oDontExpandStrings = false;
		}
		| T_OP_ROUND '(' const ')' {
			rpn_Number(&$$, math_Round($3));
		}
		| T_OP_CEIL '(' const ')' {
			rpn_Number(&$$, math_Ceil($3));
		}
		| T_OP_FLOOR '(' const ')' {
			rpn_Number(&$$, math_Floor($3));
		}
		| T_OP_FDIV '(' const ',' const ')' {
			rpn_Number(&$$, math_Div($3, $5));
		}
		| T_OP_FMUL '(' const ',' const ')' {
			rpn_Number(&$$, math_Mul($3, $5));
		}
		| T_OP_SIN '(' const ')' {
			rpn_Number(&$$, math_Sin($3));
		}
		| T_OP_COS '(' const ')' {
			rpn_Number(&$$, math_Cos($3));
		}
		| T_OP_TAN '(' const ')' {
			rpn_Number(&$$, math_Tan($3));
		}
		| T_OP_ASIN '(' const ')' {
			rpn_Number(&$$, math_ASin($3));
		}
		| T_OP_ACOS '(' const ')' {
			rpn_Number(&$$, math_ACos($3));
		}
		| T_OP_ATAN '(' const ')' {
			rpn_Number(&$$, math_ATan($3));
		}
		| T_OP_ATAN2 '(' const ',' const ')' {
			rpn_Number(&$$, math_ATan2($3, $5));
		}
		| T_OP_STRCMP '(' string ',' string ')' {
			rpn_Number(&$$, strcmp($3, $5));
		}
		| T_OP_STRIN '(' string ',' string ')' {
			char *p = strstr($3, $5);

			rpn_Number(&$$, p ? p - $3 + 1 : 0);
		}
		| T_OP_STRLEN '(' string ')' {
			rpn_Number(&$$, strlenUTF8($3));
		}
		| '(' relocexpr ')'	{ $$ = $2; }
;

uconst		: const {
			$$ = $1;
			if ($$ < 0)
				fatalerror("Constant mustn't be negative: %d\n",
					   $1);
		}
;

const		: relocexpr {
			if (!rpn_isKnown(&$1)) {
				error("Expected constant expression: %s\n",
					$1.reason);
				$$ = 0;
			} else {
				$$ = $1.nVal;
			}
		}
;

string		: T_STRING {
			if (snprintf($$, MAXSTRLEN + 1, "%s", $1) > MAXSTRLEN)
				warning(WARNING_LONG_STR, "String is too long '%s'\n", $1);
		}
		| T_OP_STRSUB '(' string ',' uconst ',' uconst ')' {
			strsubUTF8($$, $3, $5, $7);
		}
		| T_OP_STRCAT '(' string ',' string ')' {
			if (snprintf($$, MAXSTRLEN + 1, "%s%s", $3, $5) > MAXSTRLEN)
				warning(WARNING_LONG_STR, "STRCAT: String too long '%s%s'\n",
					$3, $5);
		}
		| T_OP_STRUPR '(' string ')' {
			if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
				warning(WARNING_LONG_STR, "STRUPR: String too long '%s'\n", $3);

			upperstring($$);
		}
		| T_OP_STRLWR '(' string ')' {
			if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
				warning(WARNING_LONG_STR, "STRUPR: String too long '%s'\n", $3);

			lowerstring($$);
		}
;

section		: T_POP_SECTION sectmod string ',' sectiontype sectorg sectattrs {
			out_NewSection($3, $5, $6, &$7, $2);
		}
;

sectmod	: /* empty */	{ $$ = SECTION_NORMAL; }
		| T_POP_UNION	{ $$ = SECTION_UNION; }
		| T_POP_FRAGMENT{ $$ = SECTION_FRAGMENT; }
;

sectiontype	: T_SECT_WRAM0	{ $$ = SECTTYPE_WRAM0; }
		| T_SECT_VRAM	{ $$ = SECTTYPE_VRAM; }
		| T_SECT_ROMX	{ $$ = SECTTYPE_ROMX; }
		| T_SECT_ROM0	{ $$ = SECTTYPE_ROM0; }
		| T_SECT_HRAM	{ $$ = SECTTYPE_HRAM; }
		| T_SECT_WRAMX	{ $$ = SECTTYPE_WRAMX; }
		| T_SECT_SRAM	{ $$ = SECTTYPE_SRAM; }
		| T_SECT_OAM	{ $$ = SECTTYPE_OAM; }
;

sectorg		: /* empty */ { $$ = -1; }
		| '[' uconst ']' {
			if ($2 < 0 || $2 >= 0x10000) {
				error("Address $%x is not 16-bit\n", $2);
				$$ = -1;
			} else {
				$$ = $2;
			}
		}
;

sectattrs	: /* empty */ {
			$$.alignment = 0;
			$$.alignOfs = 0;
			$$.bank = -1;
		}
		| sectattrs ',' T_OP_ALIGN '[' uconst ']' {
			if ($5 > 16)
				error("Alignment must be between 0 and 16, not %u\n", $5);
			else
				$$.alignment = $5;
		}
		| sectattrs ',' T_OP_ALIGN '[' uconst ',' uconst ']' {
			if ($5 > 16) {
				error("Alignment must be between 0 and 16, not %u\n", $5);
			} else {
				$$.alignment = $5;
				if ($7 >= 1 << $$.alignment)
					error("Alignment offset must not be greater than alignment (%u < %u)\n",
						$7, 1 << $$.alignment);
				else
					$$.alignOfs = $7;
			}
		}
		| sectattrs ',' T_OP_BANK '[' uconst ']' {
			/* We cannot check the validity of this now */
			$$.bank = $5;
		}
;


cpu_command	: { nPCOffset = 1; } z80_adc
		| { nPCOffset = 1; } z80_add
		| { nPCOffset = 1; } z80_and
		| { nPCOffset = 1; } z80_bit
		| { nPCOffset = 1; } z80_call
		| z80_ccf
		| { nPCOffset = 1; } z80_cp
		| z80_cpl
		| z80_daa
		| { nPCOffset = 1; } z80_dec
		| z80_di
		| z80_ei
		| z80_halt
		| z80_inc
		| { nPCOffset = 1; } z80_jp
		| { nPCOffset = 1; } z80_jr
		| { nPCOffset = 1; } z80_ld
		| z80_ldd
		| z80_ldi
		| { nPCOffset = 1; } z80_ldio
		| z80_nop
		| { nPCOffset = 1; } z80_or
		| z80_pop
		| z80_push
		| { nPCOffset = 1; } z80_res
		| z80_ret
		| z80_reti
		| z80_rl
		| z80_rla
		| z80_rlc
		| z80_rlca
		| z80_rr
		| z80_rra
		| z80_rrc
		| z80_rrca
		| /*{ nPCOffset = 0; }*/ z80_rst
		| { nPCOffset = 1; } z80_sbc
		| z80_scf
		| { nPCOffset = 1; } z80_set
		| z80_sla
		| z80_sra
		| z80_srl
		| { nPCOffset = 1; } z80_stop
		| { nPCOffset = 1; } z80_sub
		| z80_swap
		| { nPCOffset = 1; } z80_xor
;

z80_adc		: T_Z80_ADC op_a_n {
			out_AbsByte(0xCE);
			out_RelByte(&$2);
		}
		| T_Z80_ADC op_a_r	{ out_AbsByte(0x88 | $2); }
;

z80_add		: T_Z80_ADD op_a_n {
			out_AbsByte(0xC6);
			out_RelByte(&$2);
		}
		| T_Z80_ADD op_a_r	{ out_AbsByte(0x80 | $2); }
		| T_Z80_ADD op_hl_ss	{ out_AbsByte(0x09 | ($2 << 4)); }
		| T_Z80_ADD T_MODE_SP ',' reloc_8bit {
			out_AbsByte(0xE8);
			out_RelByte(&$4);
		}

;

z80_and		: T_Z80_AND op_a_n {
			out_AbsByte(0xE6);
			out_RelByte(&$2);
		}
		| T_Z80_AND op_a_r	{ out_AbsByte(0xA0 | $2); }
;

z80_bit		: T_Z80_BIT const_3bit ',' reg_r {
			out_AbsByte(0xCB);
			out_AbsByte(0x40 | ($2 << 3) | $4);
		}
;

z80_call	: T_Z80_CALL reloc_16bit {
			out_AbsByte(0xCD);
			out_RelWord(&$2);
		}
		| T_Z80_CALL ccode ',' reloc_16bit {
			out_AbsByte(0xC4 | ($2 << 3));
			out_RelWord(&$4);
		}
;

z80_ccf		: T_Z80_CCF	{ out_AbsByte(0x3F); }
;

z80_cp		: T_Z80_CP op_a_n {
			out_AbsByte(0xFE);
			out_RelByte(&$2);
		}
		| T_Z80_CP op_a_r	{ out_AbsByte(0xB8 | $2); }
;

z80_cpl		: T_Z80_CPL	{ out_AbsByte(0x2F); }
;

z80_daa		: T_Z80_DAA	{ out_AbsByte(0x27); }
;

z80_dec		: T_Z80_DEC reg_r	{ out_AbsByte(0x05 | ($2 << 3)); }
		| T_Z80_DEC reg_ss	{ out_AbsByte(0x0B | ($2 << 4)); }
;

z80_di		: T_Z80_DI	{ out_AbsByte(0xF3); }
;

z80_ei		: T_Z80_EI	{ out_AbsByte(0xFB); }
;

z80_halt	: T_Z80_HALT {
			out_AbsByte(0x76);
			if (haltnop)
				out_AbsByte(0x00);
		}
;

z80_inc		: T_Z80_INC reg_r	{ out_AbsByte(0x04 | ($2 << 3)); }
		| T_Z80_INC reg_ss	{ out_AbsByte(0x03 | ($2 << 4)); }
;

z80_jp		: T_Z80_JP reloc_16bit {
			out_AbsByte(0xC3);
			out_RelWord(&$2);
		}
		| T_Z80_JP ccode ',' reloc_16bit {
			out_AbsByte(0xC2 | ($2 << 3));
			out_RelWord(&$4);
		}
		| T_Z80_JP T_MODE_HL {
			out_AbsByte(0xE9);
		}
;

z80_jr		: T_Z80_JR reloc_16bit {
			out_AbsByte(0x18);
			out_PCRelByte(&$2);
		}
		| T_Z80_JR ccode ',' reloc_16bit {
			out_AbsByte(0x20 | ($2 << 3));
			out_PCRelByte(&$4);
		}
;

z80_ldi		: T_Z80_LDI '[' T_MODE_HL ']' ',' T_MODE_A {
			out_AbsByte(0x02 | (2 << 4));
		}
		| T_Z80_LDI T_MODE_A ',' '[' T_MODE_HL ']' {
			out_AbsByte(0x0A | (2 << 4));
		}
;

z80_ldd		: T_Z80_LDD '[' T_MODE_HL ']' ',' T_MODE_A {
			out_AbsByte(0x02 | (3 << 4));
		}
		| T_Z80_LDD T_MODE_A ',' '[' T_MODE_HL ']' {
			out_AbsByte(0x0A | (3 << 4));
		}
;

z80_ldio	: T_Z80_LDIO T_MODE_A ',' op_mem_ind {
			rpn_CheckHRAM(&$4, &$4);

			out_AbsByte(0xF0);
			out_RelByte(&$4);
		}
		| T_Z80_LDIO op_mem_ind ',' T_MODE_A {
			rpn_CheckHRAM(&$2, &$2);

			out_AbsByte(0xE0);
			out_RelByte(&$2);
		}
		| T_Z80_LDIO T_MODE_A ',' c_ind {
			out_AbsByte(0xF2);
		}
		| T_Z80_LDIO c_ind ',' T_MODE_A {
			out_AbsByte(0xE2);
		}
;

c_ind		: '[' T_MODE_C ']'
		| '[' T_MODE_HW_C ']'
;

z80_ld		: z80_ld_mem
		| z80_ld_cind
		| z80_ld_rr
		| z80_ld_ss
		| z80_ld_hl
		| z80_ld_sp
		| z80_ld_r
		| z80_ld_a
;

z80_ld_hl	: T_Z80_LD T_MODE_HL ',' T_MODE_SP reloc_8bit {
			out_AbsByte(0xF8);
			out_RelByte(&$5);
		}
		| T_Z80_LD T_MODE_HL ',' reloc_16bit {
			out_AbsByte(0x01 | (REG_HL << 4));
			out_RelWord(&$4);
		}
;

z80_ld_sp	: T_Z80_LD T_MODE_SP ',' T_MODE_HL	{ out_AbsByte(0xF9); }
		| T_Z80_LD T_MODE_SP ',' reloc_16bit {
			out_AbsByte(0x01 | (REG_SP << 4));
			out_RelWord(&$4);
		}
;

z80_ld_mem	: T_Z80_LD op_mem_ind ',' T_MODE_SP {
			out_AbsByte(0x08);
			out_RelWord(&$2);
		}
		| T_Z80_LD op_mem_ind ',' T_MODE_A {
			if (optimizeloads && rpn_isKnown(&$2)
			 && $2.nVal >= 0xFF00) {
				out_AbsByte(0xE0);
				out_AbsByte($2.nVal & 0xFF);
				rpn_Free(&$2);
			} else {
				out_AbsByte(0xEA);
				out_RelWord(&$2);
			}
		}
;

z80_ld_cind	: T_Z80_LD c_ind ',' T_MODE_A {
			out_AbsByte(0xE2);
		}
;

z80_ld_rr	: T_Z80_LD reg_rr ',' T_MODE_A {
			out_AbsByte(0x02 | ($2 << 4));
		}
;

z80_ld_r	: T_Z80_LD reg_r ',' reloc_8bit {
			out_AbsByte(0x06 | ($2 << 3));
			out_RelByte(&$4);
		}
		| T_Z80_LD reg_r ',' reg_r {
			if (($2 == REG_HL_IND) && ($4 == REG_HL_IND))
				error("LD [HL],[HL] not a valid instruction\n");
			else
				out_AbsByte(0x40 | ($2 << 3) | $4);
		}
;

z80_ld_a	: T_Z80_LD reg_r ',' c_ind {
			if ($2 == REG_A)
				out_AbsByte(0xF2);
			else
				error("Destination operand must be A\n");
		}
		| T_Z80_LD reg_r ',' reg_rr {
			if ($2 == REG_A)
				out_AbsByte(0x0A | ($4 << 4));
			else
				error("Destination operand must be A\n");
		}
		| T_Z80_LD reg_r ',' op_mem_ind {
			if ($2 == REG_A) {
				if (optimizeloads && rpn_isKnown(&$4)
				 && $4.nVal >= 0xFF00) {
					out_AbsByte(0xF0);
					out_AbsByte($4.nVal & 0xFF);
					rpn_Free(&$4);
				} else {
					out_AbsByte(0xFA);
					out_RelWord(&$4);
				}
			} else {
				error("Destination operand must be A\n");
				rpn_Free(&$4);
			}
		}
;

z80_ld_ss	: T_Z80_LD T_MODE_BC ',' reloc_16bit {
			out_AbsByte(0x01 | (REG_BC << 4));
			out_RelWord(&$4);
		}
		| T_Z80_LD T_MODE_DE ',' reloc_16bit {
			out_AbsByte(0x01 | (REG_DE << 4));
			out_RelWord(&$4);
		}
		/*
		 * HL is taken care of in z80_ld_hl
		 * SP is taken care of in z80_ld_sp
		 */
;

z80_nop		: T_Z80_NOP	{ out_AbsByte(0x00); }
;

z80_or		: T_Z80_OR op_a_n {
			out_AbsByte(0xF6);
			out_RelByte(&$2);
		}
		| T_Z80_OR op_a_r	{ out_AbsByte(0xB0 | $2); }
;

z80_pop		: T_Z80_POP reg_tt	{ out_AbsByte(0xC1 | ($2 << 4)); }
;

z80_push	: T_Z80_PUSH reg_tt	{ out_AbsByte(0xC5 | ($2 << 4)); }
;

z80_res		: T_Z80_RES const_3bit ',' reg_r {
			out_AbsByte(0xCB);
			out_AbsByte(0x80 | ($2 << 3) | $4);
		}
;

z80_ret		: T_Z80_RET	{ out_AbsByte(0xC9);
		}
		| T_Z80_RET ccode	{ out_AbsByte(0xC0 | ($2 << 3)); }
;

z80_reti	: T_Z80_RETI	{ out_AbsByte(0xD9); }
;

z80_rl		: T_Z80_RL reg_r {
			out_AbsByte(0xCB);
			out_AbsByte(0x10 | $2);
		}
;

z80_rla		: T_Z80_RLA	{ out_AbsByte(0x17); }
;

z80_rlc		: T_Z80_RLC reg_r {
			out_AbsByte(0xCB);
			out_AbsByte(0x00 | $2);
		}
;

z80_rlca	: T_Z80_RLCA	{ out_AbsByte(0x07); }
;

z80_rr		: T_Z80_RR reg_r {
			out_AbsByte(0xCB);
			out_AbsByte(0x18 | $2);
		}
;

z80_rra		: T_Z80_RRA	{ out_AbsByte(0x1F); }
;

z80_rrc		: T_Z80_RRC reg_r {
			out_AbsByte(0xCB);
			out_AbsByte(0x08 | $2);
		}
;

z80_rrca	: T_Z80_RRCA	{ out_AbsByte(0x0F); }
;

z80_rst		: T_Z80_RST reloc_8bit {
			rpn_CheckRST(&$2, &$2);
			if (!rpn_isKnown(&$2))
				out_RelByte(&$2);
			else
				out_AbsByte(0xC7 | $2.nVal);
			rpn_Free(&$2);
		}
;

z80_sbc		: T_Z80_SBC op_a_n {
			out_AbsByte(0xDE);
			out_RelByte(&$2);
		}
		| T_Z80_SBC op_a_r	{ out_AbsByte(0x98 | $2); }
;

z80_scf		: T_Z80_SCF	{ out_AbsByte(0x37); }
;

z80_set		: T_POP_SET const_3bit ',' reg_r {
			out_AbsByte(0xCB);
			out_AbsByte(0xC0 | ($2 << 3) | $4);
		}
;

z80_sla		: T_Z80_SLA reg_r {
			out_AbsByte(0xCB);
			out_AbsByte(0x20 | $2);
		}
;

z80_sra		: T_Z80_SRA reg_r {
			out_AbsByte(0xCB);
			out_AbsByte(0x28 | $2);
		}
;

z80_srl		: T_Z80_SRL reg_r {
			out_AbsByte(0xCB);
			out_AbsByte(0x38 | $2);
		}
;

z80_stop	: T_Z80_STOP {
			out_AbsByte(0x10);
			out_AbsByte(0x00);
		}
		| T_Z80_STOP reloc_8bit {
			out_AbsByte(0x10);
			out_RelByte(&$2);
		}
;

z80_sub		: T_Z80_SUB op_a_n {
			out_AbsByte(0xD6);
			out_RelByte(&$2);
		}
		| T_Z80_SUB op_a_r	{ out_AbsByte(0x90 | $2);
		}
;

z80_swap	: T_Z80_SWAP reg_r {
			out_AbsByte(0xCB);
			out_AbsByte(0x30 | $2);
		}
;

z80_xor		: T_Z80_XOR op_a_n {
			out_AbsByte(0xEE);
			out_RelByte(&$2);
		}
		| T_Z80_XOR op_a_r	{ out_AbsByte(0xA8 | $2); }
;

op_mem_ind	: '[' reloc_16bit ']'		{ $$ = $2; }
;

op_hl_ss	: reg_ss			{ $$ = $1; }
		| T_MODE_HL ',' reg_ss	{ $$ = $3; }
;

op_a_r		: reg_r				{ $$ = $1; }
		| T_MODE_A ',' reg_r		{ $$ = $3; }
;

op_a_n		: reloc_8bit			{ $$ = $1; }
		| T_MODE_A ',' reloc_8bit	{ $$ = $3; }
;

T_MODE_A	: T_TOKEN_A
		| T_OP_HIGH '(' T_MODE_AF ')'
;

T_MODE_B	: T_TOKEN_B
		| T_OP_HIGH '(' T_MODE_BC ')'
;

T_MODE_C	: T_TOKEN_C
		| T_OP_LOW '(' T_MODE_BC ')'
;

T_MODE_D	: T_TOKEN_D
		| T_OP_HIGH '(' T_MODE_DE ')'
;

T_MODE_E	: T_TOKEN_E
		| T_OP_LOW '(' T_MODE_DE ')'
;

T_MODE_H	: T_TOKEN_H
		| T_OP_HIGH '(' T_MODE_HL ')'
;

T_MODE_L	: T_TOKEN_L
		| T_OP_LOW '(' T_MODE_HL ')'
;

ccode		: T_CC_NZ		{ $$ = CC_NZ; }
		| T_CC_Z		{ $$ = CC_Z; }
		| T_CC_NC		{ $$ = CC_NC; }
		| T_TOKEN_C		{ $$ = CC_C; }
;

reg_r		: T_MODE_B		{ $$ = REG_B; }
		| T_MODE_C		{ $$ = REG_C; }
		| T_MODE_D		{ $$ = REG_D; }
		| T_MODE_E		{ $$ = REG_E; }
		| T_MODE_H		{ $$ = REG_H; }
		| T_MODE_L		{ $$ = REG_L; }
		| '[' T_MODE_HL ']'	{ $$ = REG_HL_IND; }
		| T_MODE_A		{ $$ = REG_A; }
;

reg_tt		: T_MODE_BC		{ $$ = REG_BC; }
		| T_MODE_DE		{ $$ = REG_DE; }
		| T_MODE_HL		{ $$ = REG_HL; }
		| T_MODE_AF		{ $$ = REG_AF; }
;

reg_ss		: T_MODE_BC		{ $$ = REG_BC; }
		| T_MODE_DE		{ $$ = REG_DE; }
		| T_MODE_HL		{ $$ = REG_HL; }
		| T_MODE_SP		{ $$ = REG_SP; }
;

reg_rr		: '[' T_MODE_BC ']'	{ $$ = REG_BC_IND; }
		| '[' T_MODE_DE ']'	{ $$ = REG_DE_IND; }
		| hl_ind_inc		{ $$ = REG_HL_INDINC; }
		| hl_ind_dec		{ $$ = REG_HL_INDDEC; }
;

hl_ind_inc	: '[' T_MODE_HL_INC ']'
		| '[' T_MODE_HL T_OP_ADD ']'
;

hl_ind_dec	: '[' T_MODE_HL_DEC ']'
		| '[' T_MODE_HL T_OP_SUB ']'
;

%%