shithub: rgbds

Download patch

ref: 4d13d57491ab39034af7af05d4e42ea5a14793f2
parent: be6bc7460ba14c6449248c29921baf466f079ff6
author: Antonio Niño Díaz <[email protected]>
date: Thu Mar 29 21:43:24 EDT 2018

Fix behaviour of '@'

When '@' is used as argument of any instruction (LD, JR, JP, etc), the
address it refers to is the address of the first byte of the
instruction. When '@' is used with DB/DW/DL, it refers to the same
address it is being placed at. This means that instructions need an
offset of 1 byte and others need an offset of 0 bytes.

The assembler doesn't evaluate anything related to '@' because it would
only work in sections with a fixed base address. It is left to the
linker. This means that the offset needs to be added to the RPN
expression of the patch that is saved in the linker. It isn't enough by
adding an offset to the final expresion. The following value wouldn't be
calculated correctly (even if it doesn't make sense):

    JP @ * @

The correct patch is `(@ - 1) * (@ - 1)`, not `(@ * @) - 1`.

This patch introduces an offset on 1 byte by default in every line, and
sets it to 0 only if a DB/DW/DL is detected.

Signed-off-by: Antonio Niño Díaz <[email protected]>

--- a/src/asm/asmy.y
+++ b/src/asm/asmy.y
@@ -31,6 +31,7 @@
 uint32_t nListCountEmpty;
 char *tzNewMacro;
 uint32_t ulNewMacroSize;
+int32_t nPCOffset;
 
 static void bankrangecheck(char *name, uint32_t secttype, int32_t org,
 			   int32_t bank)
@@ -582,6 +583,7 @@
 lines		: /* empty */
 		| lines {
 				nListCountEmpty = 0;
+				nPCOffset = 1;
 			} line '\n' {
 				nLineNo += 1;
 				nTotalLines += 1;
@@ -658,9 +660,9 @@
 		| import
 		| export
 		| global
-		| db
-		| dw
-		| dl
+		| { nPCOffset = 0; } db
+		| { nPCOffset = 0; } dw
+		| { nPCOffset = 0; } dl
 		| ds
 		| section
 		| rsreset
@@ -1108,8 +1110,39 @@
 
 relocconst	: T_ID
 		{
-			rpn_Symbol(&$$, $1);
-			$$.nVal = sym_GetValue($1);
+			/*
+			 * The value of @ needs to be evaluated by the linker,
+			 * it can only be calculated by the assembler in very
+			 * few cases (when the base address of a section is
+			 * known).
+			 *
+			 * '@' is a bit special in that it means different
+			 * things depending on when it is used:
+			 *
+			 * - JR/LD/ADD/etc: It refers to the first byte of the
+			 *   instruction (1 byte offset relative to the value
+			 *   stored in the ROM).
+			 * - DB/DW/DL: It refers to the address of the value
+			 *   that is being saved (0 byte offset relative to the
+			 *   value stored in the ROM.
+			 *
+			 * This offset must be added whenever '@' is added to a
+			 * RPN expression so that the linker can calculate the
+			 * correct result of any expression that uses '@'.
+			 */
+			if ((strcmp($1, "@") == 0) && (nPCOffset != 0)) {
+				struct Expression sTemp, sOffset;
+
+				rpn_Symbol(&sTemp, $1);
+				sTemp.nVal = sym_GetValue($1);
+
+				rpn_Number(&sOffset, nPCOffset);
+
+				rpn_SUB(&$$, &sTemp, &sOffset);
+			} else {
+				rpn_Symbol(&$$, $1);
+				$$.nVal = sym_GetValue($1);
+			}
 		}
 		| T_NUMBER
 		{