shithub: rgbds

Download patch

ref: c37253fe5ae5e838334baa67ddb2b4d264815591
parent: 0ed8d3859df61c38e8cecd2612741bdcd71467b4
parent: 6963d77f8af0509f18ad43cc03100bedd3b29e34
author: Eldred Habert <[email protected]>
date: Tue Feb 11 03:51:00 EST 2020

Merge pull request #480 from ISSOtm/section

Improve section management

--- a/Makefile
+++ b/Makefile
@@ -61,6 +61,7 @@
 	src/asm/math.o \
 	src/asm/output.o \
 	src/asm/rpn.o \
+	src/asm/section.o \
 	src/asm/symbol.o \
 	src/asm/util.o \
 	src/asm/warning.o \
--- a/include/asm/fstack.h
+++ b/include/asm/fstack.h
@@ -48,7 +48,7 @@
 void fstk_AddIncludePath(char *s);
 uint32_t fstk_RunMacro(char *s);
 void fstk_RunRept(uint32_t count, int32_t nReptLineNo);
-FILE *fstk_FindFile(char *fname, char **incPathUsed);
+FILE *fstk_FindFile(char const *fname, char **incPathUsed);
 int32_t fstk_GetLine(void);
 
 #endif /* RGBDS_ASM_FSTACK_H */
--- a/include/asm/output.h
+++ b/include/asm/output.h
@@ -11,43 +11,15 @@
 
 #include <stdint.h>
 
-#include "asm/rpn.h"
+#include "linkdefs.h"
 
-struct Section {
-	char *pzName;
-	uint8_t nType;
-	uint32_t nPC;
-	uint32_t nOrg;
-	uint32_t nBank;
-	uint32_t nAlign;
-	struct Section *pNext;
-	struct Patch *pPatches;
-	uint8_t *tData;
-};
+struct Expression;
 
 extern char *tzObjectname;
+extern struct Section *pSectionList, *pCurrentSection;
 
-void out_PrepPass2(void);
 void out_SetFileName(char *s);
-struct Section *out_FindSectionByName(const char *pzName);
-void out_NewSection(char *pzName, uint32_t secttype);
-void out_NewAbsSection(char *pzName, uint32_t secttype, int32_t org,
-		       int32_t bank);
-void out_NewAlignedSection(char *pzName, uint32_t secttype, int32_t alignment,
-			   int32_t bank);
-void out_AbsByte(int32_t b);
-void out_AbsByteGroup(char *s, int32_t length);
-void out_RelByte(struct Expression *expr);
-void out_RelWord(struct Expression *expr);
-void out_PCRelByte(struct Expression *expr);
+void out_CreatePatch(uint32_t type, struct Expression *expr);
 void out_WriteObject(void);
-void out_Skip(int32_t skip);
-void out_BinaryFile(char *s);
-void out_BinaryFileSlice(char *s, int32_t start_pos, int32_t length);
-void out_String(char *s);
-void out_AbsLong(int32_t b);
-void out_RelLong(struct Expression *expr);
-void out_PushSection(void);
-void out_PopSection(void);
 
 #endif /* RGBDS_ASM_OUTPUT_H */
--- /dev/null
+++ b/include/asm/section.h
@@ -1,0 +1,46 @@
+
+#include <stdint.h>
+
+#include "linkdefs.h"
+
+struct Expression;
+
+struct Section {
+	char *pzName;
+	enum SectionType nType;
+	uint32_t nPC;
+	uint32_t nOrg;
+	uint32_t nBank;
+	uint32_t nAlign;
+	struct Section *pNext;
+	struct Patch *pPatches;
+	uint8_t *tData;
+};
+
+struct SectionSpec {
+	int32_t bank;
+	int32_t alignment;
+};
+
+struct Section *out_FindSectionByName(const char *pzName);
+void out_NewSection(char const *pzName, uint32_t secttype, int32_t org,
+		    struct SectionSpec const *attributes);
+void out_SetLoadSection(char const *name, uint32_t secttype, int32_t org,
+			struct SectionSpec const *attributes);
+void out_EndLoadSection(void);
+
+struct Section *sect_GetSymbolSection(void);
+
+void out_AbsByte(uint8_t b);
+void out_AbsByteGroup(uint8_t const *s, int32_t length);
+void out_Skip(int32_t skip);
+void out_String(char const *s);
+void out_RelByte(struct Expression *expr);
+void out_RelWord(struct Expression *expr);
+void out_RelLong(struct Expression *expr);
+void out_PCRelByte(struct Expression *expr);
+void out_BinaryFile(char const *s);
+void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
+
+void out_PushSection(void);
+void out_PopSection(void);
--- a/src/asm/asmy.y
+++ b/src/asm/asmy.y
@@ -22,8 +22,8 @@
 #include "asm/lexer.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"
@@ -37,20 +37,6 @@
 uint32_t ulNewMacroSize;
 int32_t nPCOffset;
 
-static void bankrangecheck(char *name, uint32_t secttype, int32_t org,
-			   int32_t bank)
-{
-	if (secttype != SECTTYPE_ROMX && secttype != SECTTYPE_VRAM
-	 && secttype != SECTTYPE_SRAM && secttype != SECTTYPE_WRAMX)
-		yyerror("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections");
-	else if (bank < bankranges[secttype][0] || bank > bankranges[secttype][1])
-		yyerror("%s bank value $%x out of range ($%x to $%x)",
-		        typeNames[secttype], bank, bankranges[secttype][0],
-		        bankranges[secttype][1]);
-
-	out_NewAbsSection(name, secttype, org, bank);
-}
-
 size_t symvaluetostring(char *dest, size_t maxLength, char *symName,
 			const char *mode)
 {
@@ -419,7 +405,6 @@
 
 	nPC = unionStart[unionIndex];
 	pCurrentSection->nPC = unionStart[unionIndex];
-	pPCSymbol->nValue = unionStart[unionIndex];
 }
 
 static size_t strlenUTF8(const char *s)
@@ -509,6 +494,7 @@
 	char tzString[MAXSTRLEN + 1];
 	struct Expression sVal;
 	int32_t nConstValue;
+	struct SectionSpec sectSpec;
 }
 
 %type	<sVal>		relocconst
@@ -521,6 +507,9 @@
 
 %type	<tzString>	string
 
+%type	<nConstValue>	sectorg
+%type	<sectSpec>	sectattrs
+
 %token	<nConstValue>	T_NUMBER
 %token	<tzString>	T_STRING
 
@@ -586,6 +575,7 @@
 %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_PURGE
@@ -741,6 +731,7 @@
 		| setcharmap
 		| pushc
 		| popc
+		| load
 		| rept
 		| shift
 		| fail
@@ -788,6 +779,15 @@
 shift		: T_POP_SHIFT		{ sym_ShiftCurrentMacroArgs(); }
 ;
 
+load		: T_POP_LOAD string comma sectiontype sectorg sectattrs
+		{
+			out_SetLoadSection($2, $4, $5, &$6);
+		}
+		| T_POP_ENDL
+		{
+			out_EndLoadSection();
+		}
+
 rept		: T_POP_REPT uconst
 		{
 			uint32_t nDefinitionLineNo = nLineNo;
@@ -868,7 +868,6 @@
 			nUnionDepth--;
 			nPC = unionStart[nUnionDepth] + unionSize[nUnionDepth];
 			pCurrentSection->nPC = nPC;
-			pPCSymbol->nValue = nPC;
 		}
 ;
 
@@ -1149,7 +1148,7 @@
 			char *s = $1;
 			int32_t length = charmap_Convert(&s);
 
-			out_AbsByteGroup(s, length);
+			out_AbsByteGroup((uint8_t*)s, length);
 			free(s);
 		}
 ;
@@ -1384,39 +1383,10 @@
 		}
 ;
 
-section		: T_POP_SECTION string comma sectiontype
+section		: T_POP_SECTION string comma sectiontype sectorg sectattrs
 		{
-			out_NewSection($2, $4);
+			out_NewSection($2, $4, $5, &$6);
 		}
-		| T_POP_SECTION string comma sectiontype '[' uconst ']'
-		{
-			if (($6 >= 0) && ($6 < 0x10000))
-				out_NewAbsSection($2, $4, $6, -1);
-			else
-				yyerror("Address $%x not 16-bit", $6);
-		}
-		| T_POP_SECTION string comma sectiontype comma T_OP_ALIGN '[' uconst ']'
-		{
-			out_NewAlignedSection($2, $4, $8, -1);
-		}
-		| T_POP_SECTION string comma sectiontype comma T_OP_BANK '[' uconst ']'
-		{
-			bankrangecheck($2, $4, -1, $8);
-		}
-		| T_POP_SECTION string comma sectiontype '[' uconst ']' comma T_OP_BANK '[' uconst ']'
-		{
-			if (($6 < 0) || ($6 > 0x10000))
-				yyerror("Address $%x not 16-bit", $6);
-			bankrangecheck($2, $4, $6, $11);
-		}
-		| T_POP_SECTION string comma sectiontype comma T_OP_ALIGN '[' uconst ']' comma T_OP_BANK '[' uconst ']'
-		{
-			out_NewAlignedSection($2, $4, $8, $13);
-		}
-		| T_POP_SECTION string comma sectiontype comma T_OP_BANK '[' uconst ']' comma T_OP_ALIGN '[' uconst ']'
-		{
-			out_NewAlignedSection($2, $4, $13, $8);
-		}
 ;
 
 sectiontype	: T_SECT_WRAM0	{ $$ = SECTTYPE_WRAM0; }
@@ -1446,6 +1416,36 @@
 		{
 			warning(WARNING_OBSOLETE, "BSS section name is deprecated, use WRAM0 instead.");
 			$$ = SECTTYPE_WRAM0;
+		}
+;
+
+sectorg		: { $$ = -1; }
+		| '[' uconst ']'
+		{
+			if ($2 < 0 || $2 >= 0x10000)
+				yyerror("Address $%x is not 16-bit", $2);
+			else
+				$$ = $2;
+		}
+;
+
+sectattrs	:
+		{
+			$$.alignment = 0;
+			$$.bank = -1;
+		}
+		| sectattrs comma T_OP_ALIGN '[' uconst ']'
+		{
+			if ($5 < 0 || $5 > 16)
+				yyerror("Alignment must be between 0 and 16 bits, not %u",
+					$5);
+			else
+				$$.alignment = $5;
+		}
+		| sectattrs comma T_OP_BANK '[' uconst ']'
+		{
+			/* We cannot check the validity of this now */
+			$$.bank = $5;
 		}
 ;
 
--- a/src/asm/fstack.c
+++ b/src/asm/fstack.c
@@ -334,7 +334,7 @@
 	}
 }
 
-FILE *fstk_FindFile(char *fname, char **incPathUsed)
+FILE *fstk_FindFile(char const *fname, char **incPathUsed)
 {
 	char path[_MAX_PATH];
 	int32_t i;
--- a/src/asm/globlex.c
+++ b/src/asm/globlex.c
@@ -17,8 +17,8 @@
 #include "asm/lexer.h"
 #include "asm/main.h"
 #include "asm/rpn.h"
+#include "asm/section.h"
 #include "asm/symbol.h"
-#include "asm/symbol.h"
 #include "asm/warning.h"
 
 #include "helpers.h"
@@ -507,6 +507,9 @@
 	{"rept", T_POP_REPT},
 	/* Not needed but we have it here just to protect the name */
 	{"endr", T_POP_ENDR},
+
+	{"load", T_POP_LOAD},
+	{"endl", T_POP_ENDL},
 
 	{"if", T_POP_IF},
 	{"else", T_POP_ELSE},
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -19,6 +19,7 @@
 #include "asm/lexer.h"
 #include "asm/main.h"
 #include "asm/rpn.h"
+#include "asm/section.h"
 #include "asm/warning.h"
 
 #include "extern/err.h"
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -23,6 +23,7 @@
 #include "asm/main.h"
 #include "asm/output.h"
 #include "asm/rpn.h"
+#include "asm/section.h"
 #include "asm/symbol.h"
 #include "asm/warning.h"
 
@@ -30,8 +31,6 @@
 
 #include "linkdefs.h"
 
-void out_SetCurrentSection(struct Section *pSect);
-
 struct Patch {
 	char tzFilename[_MAX_PATH + 1];
 	uint32_t nLine;
@@ -49,76 +48,13 @@
 	struct PatchSymbol *pBucketNext; /* next symbol in hash table bucket */
 };
 
-struct SectionStackEntry {
-	struct Section *pSection;
-	struct sSymbol *pScope; /* Section's symbol scope */
-	struct SectionStackEntry *pNext;
-};
-
 struct PatchSymbol *tHashedPatchSymbols[HASHSIZE];
 struct Section *pSectionList, *pCurrentSection;
 struct PatchSymbol *pPatchSymbols;
 struct PatchSymbol **ppPatchSymbolsTail = &pPatchSymbols;
 char *tzObjectname;
-struct SectionStackEntry *pSectionStack;
 
 /*
- * Section stack routines
- */
-void out_PushSection(void)
-{
-	struct SectionStackEntry *pSect;
-
-	pSect = malloc(sizeof(struct SectionStackEntry));
-	if (pSect == NULL)
-		fatalerror("No memory for section stack");
-
-	pSect->pSection = pCurrentSection;
-	pSect->pScope = sym_GetCurrentSymbolScope();
-	pSect->pNext = pSectionStack;
-	pSectionStack = pSect;
-}
-
-void out_PopSection(void)
-{
-	if (pSectionStack == NULL)
-		fatalerror("No entries in the section stack");
-
-	struct SectionStackEntry *pSect;
-
-	pSect = pSectionStack;
-	out_SetCurrentSection(pSect->pSection);
-	sym_SetCurrentSymbolScope(pSect->pScope);
-	pSectionStack = pSect->pNext;
-	free(pSect);
-}
-
-static uint32_t getmaxsectionsize(uint32_t secttype, char *sectname)
-{
-	switch (secttype) {
-	case SECTTYPE_ROM0:
-		return 0x8000; /* If ROMX sections not used */
-	case SECTTYPE_ROMX:
-		return 0x4000;
-	case SECTTYPE_VRAM:
-		return 0x2000;
-	case SECTTYPE_SRAM:
-		return 0x2000;
-	case SECTTYPE_WRAM0:
-		return 0x2000; /* If WRAMX sections not used */
-	case SECTTYPE_WRAMX:
-		return 0x1000;
-	case SECTTYPE_OAM:
-		return 0xA0;
-	case SECTTYPE_HRAM:
-		return 0x7F;
-	default:
-		break;
-	}
-	errx(1, "Section \"%s\" has an invalid section type.", sectname);
-}
-
-/*
  * Count the number of symbols used in this object
  */
 static uint32_t countsymbols(void)
@@ -127,13 +63,12 @@
 	uint32_t count = 0;
 
 	pSym = pPatchSymbols;
-
 	while (pSym) {
 		count++;
 		pSym = pSym->pNext;
 	}
 
-	return (count);
+	return count;
 }
 
 /*
@@ -145,13 +80,12 @@
 	uint32_t count = 0;
 
 	pSect = pSectionList;
-
 	while (pSect) {
 		count++;
 		pSect = pSect->pNext;
 	}
 
-	return (count);
+	return count;
 }
 
 /*
@@ -168,7 +102,7 @@
 		pPatch = pPatch->pNext;
 	}
 
-	return (r);
+	return r;
 }
 
 /*
@@ -209,8 +143,7 @@
 		sec = sec->pNext;
 	}
 
-	fatalerror("%s: Unknown section", __func__);
-	return (uint32_t)(-1);
+	fatalerror("Unknown section '%s'", pSect->pzName);
 }
 
 /*
@@ -379,7 +312,7 @@
 /*
  * Create a new patch (includes the rpn expr)
  */
-void createpatch(uint32_t type, struct Expression *expr)
+void out_CreatePatch(uint32_t type, struct Expression *expr)
 {
 	struct Patch *pPatch;
 	uint16_t rpndata;
@@ -396,7 +329,7 @@
 	pPatch->nType = type;
 	fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename));
 	pPatch->nLine = nLineNo;
-	pPatch->nOffset = nPC;
+	pPatch->nOffset = pCurrentSection->nPC;
 
 	while ((rpndata = rpn_PopByte(expr)) != 0xDEAD) {
 		switch (rpndata) {
@@ -481,52 +414,6 @@
 }
 
 /*
- * A quick check to see if we have an initialized section
- */
-static void checksection(void)
-{
-	if (pCurrentSection == NULL)
-		fatalerror("Code generation before SECTION directive");
-}
-
-/*
- * A quick check to see if we have an initialized section that can contain
- * this much initialized data
- */
-static void checkcodesection(void)
-{
-	checksection();
-	if (!sect_HasData(pCurrentSection->nType)) {
-		fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)",
-			   pCurrentSection->pzName);
-	} else if (nUnionDepth > 0) {
-		fatalerror("UNIONs cannot contain code or data");
-	}
-}
-
-/*
- * Check if the section has grown too much.
- */
-static void checksectionoverflow(uint32_t delta_size)
-{
-	uint32_t maxSize = getmaxsectionsize(pCurrentSection->nType,
-					     pCurrentSection->pzName);
-	uint32_t newSize = pCurrentSection->nPC + delta_size;
-
-	if (newSize > maxSize) {
-		/*
-		 * This check is here to trap broken code that generates
-		 * sections that are too big and to prevent the assembler from
-		 * generating huge object files or trying to allocate too much
-		 * memory.
-		 * The real check must be done at the linking stage.
-		 */
-		fatalerror("Section '%s' is too big (max size = 0x%X bytes, reached 0x%X).",
-			   pCurrentSection->pzName, maxSize, newSize);
-	}
-}
-
-/*
  * Write an objectfile
  */
 void out_WriteObject(void)
@@ -570,400 +457,4 @@
 	tzObjectname = s;
 	if (CurrentOptions.verbose)
 		printf("Output filename %s\n", s);
-}
-
-struct Section *out_FindSectionByName(const char *pzName)
-{
-	struct Section *pSect = pSectionList;
-
-	while (pSect) {
-		if (strcmp(pzName, pSect->pzName) == 0)
-			return pSect;
-
-		pSect = pSect->pNext;
-	}
-
-	return NULL;
-}
-
-/*
- * Find a section by name and type. If it doesn't exist, create it
- */
-struct Section *out_FindSection(char *pzName, uint32_t secttype, int32_t org,
-				int32_t bank, int32_t alignment)
-{
-	struct Section *pSect = out_FindSectionByName(pzName);
-
-	if (pSect) {
-		if (secttype == pSect->nType
-			&& ((uint32_t)org) == pSect->nOrg
-			&& ((uint32_t)bank) == pSect->nBank
-			&& ((uint32_t)alignment == pSect->nAlign)) {
-			return pSect;
-		}
-		fatalerror("Section already exists but with a different type");
-	}
-
-	pSect = malloc(sizeof(struct Section));
-	if (pSect == NULL)
-		fatalerror("Not enough memory for section");
-
-	pSect->pzName = strdup(pzName);
-	if (pSect->pzName == NULL)
-		fatalerror("Not enough memory for sectionname");
-
-	// Force the bank to be 0 if that's the only possibility
-	switch (secttype) {
-	case SECTTYPE_ROM0:
-	case SECTTYPE_WRAM0:
-	case SECTTYPE_OAM:
-	case SECTTYPE_HRAM:
-		bank = 0;
-	}
-
-	pSect->nType = secttype;
-	pSect->nPC = 0;
-	pSect->nOrg = org;
-	pSect->nBank = bank;
-	pSect->nAlign = alignment;
-	pSect->pNext = pSectionList;
-	pSect->pPatches = NULL;
-
-	/* It is only needed to allocate memory for ROM sections. */
-	if (sect_HasData(secttype)) {
-		uint32_t sectsize;
-
-		sectsize = getmaxsectionsize(secttype, pzName);
-		pSect->tData = malloc(sectsize);
-		if (pSect->tData == NULL)
-			fatalerror("Not enough memory for section");
-	} else {
-		pSect->tData = NULL;
-	}
-
-	/*
-	 * Add the new section to the list
-	 * at the beginning because order doesn't matter
-	 */
-	pSectionList = pSect;
-
-	return pSect;
-}
-
-/*
- * Set the current section
- */
-void out_SetCurrentSection(struct Section *pSect)
-{
-	if (nUnionDepth > 0)
-		fatalerror("Cannot change the section within a UNION");
-
-	pCurrentSection = pSect;
-	nPC = (pSect != NULL) ? pSect->nPC : 0;
-
-	pPCSymbol->nValue = nPC;
-	pPCSymbol->pSection = pCurrentSection;
-	pPCSymbol->isConstant = pSect && pSect->nOrg != -1;
-}
-
-/*
- * Set the current section by name and type
- */
-void out_NewSection(char *pzName, uint32_t secttype)
-{
-	out_SetCurrentSection(out_FindSection(pzName, secttype, -1, -1, 1));
-}
-
-/*
- * Set the current section by name and type
- */
-void out_NewAbsSection(char *pzName, uint32_t secttype, int32_t org,
-		       int32_t bank)
-{
-	out_SetCurrentSection(out_FindSection(pzName, secttype, org, bank, 1));
-}
-
-/*
- * Set the current section by name and type, using a given byte alignment
- */
-void out_NewAlignedSection(char *pzName, uint32_t secttype, int32_t alignment,
-			   int32_t bank)
-{
-	if (alignment < 0 || alignment > 16)
-		yyerror("Alignment must be between 0-16 bits.");
-
-	out_SetCurrentSection(out_FindSection(pzName, secttype, -1, bank,
-					      1 << alignment));
-}
-
-/*
- * Output an absolute byte (bypassing ROM/union checks)
- */
-void out_AbsByteBypassCheck(int32_t b)
-{
-	checksectionoverflow(1);
-	b &= 0xFF;
-	pCurrentSection->tData[nPC] = b;
-	pCurrentSection->nPC++;
-	nPC++;
-	pPCSymbol->nValue++;
-}
-
-/*
- * Output an absolute byte
- */
-void out_AbsByte(int32_t b)
-{
-	checkcodesection();
-	out_AbsByteBypassCheck(b);
-}
-
-void out_AbsByteGroup(char *s, int32_t length)
-{
-	checkcodesection();
-	checksectionoverflow(length);
-	while (length--)
-		out_AbsByte(*s++);
-}
-
-/*
- * Skip this many bytes
- */
-void out_Skip(int32_t skip)
-{
-	checksection();
-	checksectionoverflow(skip);
-	if (!sect_HasData(pCurrentSection->nType)) {
-		pCurrentSection->nPC += skip;
-		nPC += skip;
-		pPCSymbol->nValue += skip;
-	} else if (nUnionDepth > 0) {
-		while (skip--)
-			out_AbsByteBypassCheck(CurrentOptions.fillchar);
-	} else {
-		checkcodesection();
-		while (skip--)
-			out_AbsByte(CurrentOptions.fillchar);
-	}
-}
-
-/*
- * Output a NULL terminated string (excluding the NULL-character)
- */
-void out_String(char *s)
-{
-	checkcodesection();
-	checksectionoverflow(strlen(s));
-	while (*s)
-		out_AbsByte(*s++);
-}
-
-/*
- * Output a relocatable byte. Checking will be done to see if it
- * is an absolute value in disguise.
- */
-void out_RelByte(struct Expression *expr)
-{
-	checkcodesection();
-	checksectionoverflow(1);
-	if (!rpn_isKnown(expr)) {
-		pCurrentSection->tData[nPC] = 0;
-		createpatch(PATCHTYPE_BYTE, expr);
-		pCurrentSection->nPC++;
-		nPC++;
-		pPCSymbol->nValue++;
-	} else {
-		out_AbsByte(expr->nVal);
-	}
-	rpn_Free(expr);
-}
-
-/*
- * Output an absolute word
- */
-void out_AbsWord(int32_t b)
-{
-	checkcodesection();
-	checksectionoverflow(2);
-	b &= 0xFFFF;
-	pCurrentSection->tData[nPC] = b & 0xFF;
-	pCurrentSection->tData[nPC + 1] = b >> 8;
-	pCurrentSection->nPC += 2;
-	nPC += 2;
-	pPCSymbol->nValue += 2;
-}
-
-/*
- * Output a relocatable word. Checking will be done to see if
- * it's an absolute value in disguise.
- */
-void out_RelWord(struct Expression *expr)
-{
-	checkcodesection();
-	checksectionoverflow(2);
-	if (!rpn_isKnown(expr)) {
-		pCurrentSection->tData[nPC] = 0;
-		pCurrentSection->tData[nPC + 1] = 0;
-		createpatch(PATCHTYPE_WORD, expr);
-		pCurrentSection->nPC += 2;
-		nPC += 2;
-		pPCSymbol->nValue += 2;
-	} else {
-		out_AbsWord(expr->nVal);
-	}
-	rpn_Free(expr);
-}
-
-/*
- * Output an absolute longword
- */
-void out_AbsLong(int32_t b)
-{
-	checkcodesection();
-	checksectionoverflow(sizeof(int32_t));
-	pCurrentSection->tData[nPC] = b & 0xFF;
-	pCurrentSection->tData[nPC + 1] = b >> 8;
-	pCurrentSection->tData[nPC + 2] = b >> 16;
-	pCurrentSection->tData[nPC + 3] = b >> 24;
-	pCurrentSection->nPC += 4;
-	nPC += 4;
-	pPCSymbol->nValue += 4;
-}
-
-/*
- * Output a relocatable longword. Checking will be done to see if
- * is an absolute value in disguise.
- */
-void out_RelLong(struct Expression *expr)
-{
-	checkcodesection();
-	checksectionoverflow(4);
-	if (!rpn_isKnown(expr)) {
-		pCurrentSection->tData[nPC] = 0;
-		pCurrentSection->tData[nPC + 1] = 0;
-		pCurrentSection->tData[nPC + 2] = 0;
-		pCurrentSection->tData[nPC + 3] = 0;
-		createpatch(PATCHTYPE_LONG, expr);
-		pCurrentSection->nPC += 4;
-		nPC += 4;
-		pPCSymbol->nValue += 4;
-	} else {
-		out_AbsLong(expr->nVal);
-	}
-	rpn_Free(expr);
-}
-
-/*
- * Output a PC-relative relocatable byte. Checking will be done to see if it
- * is an absolute value in disguise.
- */
-void out_PCRelByte(struct Expression *expr)
-{
-	checkcodesection();
-	checksectionoverflow(1);
-	if (!rpn_isKnown(expr) || pCurrentSection->nOrg == -1) {
-		pCurrentSection->tData[nPC] = 0;
-		createpatch(PATCHTYPE_JR, expr);
-		pCurrentSection->nPC++;
-		nPC++;
-		pPCSymbol->nValue++;
-	} else {
-		/* Target is relative to the byte *after* the operand */
-		uint16_t address = pCurrentSection->nOrg + nPC + 1;
-		/* The offset wraps (jump from ROM to HRAM, for loopexample) */
-		int16_t offset = expr->nVal - address;
-
-		if (offset < -128 || offset > 127) {
-			yyerror("jr target out of reach (expected -129 < %d < 128)", offset);
-			out_AbsByte(0);
-		} else {
-			out_AbsByte(offset);
-		}
-	}
-	rpn_Free(expr);
-}
-
-/*
- * Output a binary file
- */
-void out_BinaryFile(char *s)
-{
-	FILE *f;
-
-	f = fstk_FindFile(s, NULL);
-	if (f == NULL) {
-		if (oGeneratedMissingIncludes) {
-			oFailedOnMissingInclude = true;
-			return;
-		}
-		err(1, "Unable to open incbin file '%s'", s);
-	}
-
-	int32_t fsize;
-
-	fseek(f, 0, SEEK_END);
-	fsize = ftell(f);
-	fseek(f, 0, SEEK_SET);
-
-	checkcodesection();
-	checksectionoverflow(fsize);
-
-	int32_t dest = nPC;
-	int32_t todo = fsize;
-
-	while (todo--)
-		pCurrentSection->tData[dest++] = fgetc(f);
-
-	pCurrentSection->nPC += fsize;
-	nPC += fsize;
-	pPCSymbol->nValue += fsize;
-	fclose(f);
-}
-
-void out_BinaryFileSlice(char *s, int32_t start_pos, int32_t length)
-{
-	FILE *f;
-
-	if (start_pos < 0)
-		fatalerror("Start position cannot be negative");
-
-	if (length < 0)
-		fatalerror("Number of bytes to read must be greater than zero");
-
-	f = fstk_FindFile(s, NULL);
-	if (f == NULL) {
-		if (oGeneratedMissingIncludes) {
-			oFailedOnMissingInclude = true;
-			return;
-		}
-		err(1, "Unable to open included file '%s'", s);
-	}
-
-	int32_t fsize;
-
-	fseek(f, 0, SEEK_END);
-	fsize = ftell(f);
-
-	if (start_pos >= fsize)
-		fatalerror("Specified start position is greater than length of file");
-
-	if ((start_pos + length) > fsize)
-		fatalerror("Specified range in INCBIN is out of bounds");
-
-	fseek(f, start_pos, SEEK_SET);
-
-	checkcodesection();
-	checksectionoverflow(length);
-
-	int32_t dest = nPC;
-	int32_t todo = length;
-
-	while (todo--)
-		pCurrentSection->tData[dest++] = fgetc(f);
-
-	pCurrentSection->nPC += length;
-	nPC += length;
-	pPCSymbol->nValue += length;
-
-	fclose(f);
 }
--- a/src/asm/rgbasm.5
+++ b/src/asm/rgbasm.5
@@ -296,6 +296,66 @@
 can then later be used to restore it.
 Useful for defining sections in included files when you don't want to destroy the section context for the program that included your file.
 The number of entries in the stack is limited only by the amount of memory in your machine.
+.Ss RAM Code
+Sometimes you want to have some code in RAM.
+But then you can't simply put it in a RAM section, you have to store it in ROM and copy it to RAM at some time.
+.Pp
+This means the code (or data) will not be stored in the place it gets executed.
+Luckily,
+.Ic LOAD
+blocks are the perfect solution to that.
+Here's an example of how to use them:
+.Bd -literal -offset indent
+    SECTION "LOAD example", ROMX
+    CopyCode:
+        ld de, RAMCode
+        ld hl, RAMLocation
+        ld c, RAMLocation.end - RAMLocation
+    .loop
+        ld a, [de]
+        inc de
+        ld [hli], a
+        dec c
+        jr nz, .loop
+        ret
+
+    RAMCode:
+      LOAD "RAM code", WRAM0
+    RAMLocation:
+        ld hl, .string
+        ld de, $9864
+    .copy
+        ld a, [hli]
+        ld [de], a
+        inc de
+        and a
+        jr nz, .copy
+        ret
+
+    .string
+        db "Hello World!", 0
+    .end
+      ENDL
+.Ed
+.Pp
+A
+.Ic LOAD
+block feels similar to a
+.Ic SECTION
+declaration because it creates a new one.
+All data and code generated within such a block is placed in the current section like usual, but all labels are created as if the it was placed in this newly-created section.
+.Pp
+In the example above, all of the code and data will end up in the "LOAD example" section.
+You will notice the
+.Ic RAMCode
+and
+.Ic RAMLocation
+labels.
+The former is situated in ROM, where the code is stored, the latter in RAM, where the code will be loaded.
+.Pp
+You cannot nest
+.Ic LOAD
+blocks, nor can you change the current section within them.
 .Sh SYMBOLS
 .Pp
 .Ss Symbols
--- a/src/asm/rpn.c
+++ b/src/asm/rpn.c
@@ -19,8 +19,8 @@
 #include "asm/asm.h"
 #include "asm/main.h"
 #include "asm/rpn.h"
+#include "asm/section.h"
 #include "asm/symbol.h"
-#include "asm/output.h"
 #include "asm/warning.h"
 
 /* Makes an expression "not known", also setting its error message */
--- /dev/null
+++ b/src/asm/section.c
@@ -1,0 +1,553 @@
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "asm/fstack.h"
+#include "asm/main.h"
+#include "asm/output.h"
+#include "asm/rpn.h"
+#include "asm/section.h"
+#include "asm/warning.h"
+
+#include "extern/err.h"
+
+struct SectionStackEntry {
+	struct Section *pSection;
+	struct sSymbol *pScope; /* Section's symbol scope */
+	struct SectionStackEntry *pNext;
+};
+
+struct SectionStackEntry *pSectionStack;
+static struct Section *currentLoadSection = NULL;
+
+/*
+ * A quick check to see if we have an initialized section
+ */
+static void checksection(void)
+{
+	if (pCurrentSection == NULL)
+		fatalerror("Code generation before SECTION directive");
+}
+
+/*
+ * A quick check to see if we have an initialized section that can contain
+ * this much initialized data
+ */
+static void checkcodesection(void)
+{
+	checksection();
+
+	if (!sect_HasData(pCurrentSection->nType))
+		fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)",
+			   pCurrentSection->pzName);
+	else if (nUnionDepth > 0)
+		fatalerror("UNIONs cannot contain code or data");
+}
+
+/*
+ * Check if the section has grown too much.
+ */
+static void checksectionoverflow(uint32_t delta_size)
+{
+	uint32_t maxSize = maxsize[pCurrentSection->nType];
+	uint32_t newSize = pCurrentSection->nPC + delta_size;
+
+	if (newSize > maxSize) {
+		/*
+		 * This check is here to trap broken code that generates
+		 * sections that are too big and to prevent the assembler from
+		 * generating huge object files or trying to allocate too much
+		 * memory.
+		 * The real check must be done at the linking stage.
+		 */
+		fatalerror("Section '%s' is too big (max size = 0x%X bytes, reached 0x%X).",
+			   pCurrentSection->pzName, maxSize, newSize);
+	}
+}
+
+struct Section *out_FindSectionByName(const char *pzName)
+{
+	struct Section *pSect = pSectionList;
+
+	while (pSect) {
+		if (strcmp(pzName, pSect->pzName) == 0)
+			return pSect;
+
+		pSect = pSect->pNext;
+	}
+
+	return NULL;
+}
+
+/*
+ * Find a section by name and type. If it doesn't exist, create it
+ */
+static struct Section *getSection(char const *pzName, enum SectionType type,
+				  int32_t org, int32_t bank, int32_t alignment)
+{
+	if (bank != -1) {
+		if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
+		 && type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX)
+			yyerror("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections");
+		else if (bank < bankranges[type][0]
+		      || bank > bankranges[type][1])
+			yyerror("%s bank value $%x out of range ($%x to $%x)",
+				typeNames[type], bank,
+				bankranges[type][0], bankranges[type][1]);
+	}
+
+	if (alignment != 1) {
+		/* It doesn't make sense to have both set */
+		uint32_t mask = alignment - 1;
+
+		if (org != -1) {
+			if (org & mask)
+				yyerror("Section \"%s\"'s fixed address doesn't match its alignment",
+					pzName);
+			else
+				alignment = 1; /* Ignore it if it's satisfied */
+		}
+	}
+
+	if (org != -1) {
+		if (org < startaddr[type] || org > endaddr(type))
+			yyerror("Section \"%s\"'s fixed address %#x is outside of range [%#x; %#x]",
+				pzName, org, startaddr[type], endaddr(type));
+	}
+
+	struct Section *pSect = out_FindSectionByName(pzName);
+
+	if (pSect) {
+		if (type == pSect->nType
+			&& ((uint32_t)org) == pSect->nOrg
+			&& ((uint32_t)bank) == pSect->nBank
+			&& ((uint32_t)alignment == pSect->nAlign)) {
+			return pSect;
+		}
+		fatalerror("Section already exists but with a different type");
+	}
+
+	pSect = malloc(sizeof(*pSect));
+	if (pSect == NULL)
+		fatalerror("Not enough memory for section");
+
+	pSect->pzName = strdup(pzName);
+	if (pSect->pzName == NULL)
+		fatalerror("Not enough memory for sectionname");
+
+	if (nbbanks(type) == 1)
+		bank = bankranges[type][0];
+
+	pSect->nType = type;
+	pSect->nPC = 0;
+	pSect->nOrg = org;
+	pSect->nBank = bank;
+	pSect->nAlign = alignment;
+	pSect->pNext = pSectionList;
+	pSect->pPatches = NULL;
+
+	/* It is only needed to allocate memory for ROM sections. */
+	if (sect_HasData(type)) {
+		uint32_t sectsize;
+
+		sectsize = maxsize[type];
+		pSect->tData = malloc(sectsize);
+		if (pSect->tData == NULL)
+			fatalerror("Not enough memory for section");
+	} else {
+		pSect->tData = NULL;
+	}
+
+	/*
+	 * Add the new section to the list
+	 * at the beginning because order doesn't matter
+	 */
+	pSectionList = pSect;
+
+	return pSect;
+}
+
+/*
+ * Set the current section
+ */
+static void setSection(struct Section *pSect)
+{
+	if (nUnionDepth > 0)
+		fatalerror("Cannot change the section within a UNION");
+
+	nPC = (pSect != NULL) ? pSect->nPC : 0;
+
+	pPCSymbol->pSection = pSect;
+	pPCSymbol->isConstant = pSect && pSect->nOrg != -1;
+}
+
+/*
+ * Set the current section by name and type
+ */
+void out_NewSection(char const *pzName, uint32_t type, int32_t org,
+		    struct SectionSpec const *attributes)
+{
+	if (currentLoadSection)
+		fatalerror("Cannot change the section within a `LOAD` block");
+
+	struct Section *pSect = getSection(pzName, type, org, attributes->bank,
+					   1 << attributes->alignment);
+
+	nPC = pSect->nPC;
+	setSection(pSect);
+	pCurrentSection = pSect;
+}
+
+/*
+ * Set the current section by name and type
+ */
+void out_SetLoadSection(char const *name, uint32_t type, int32_t org,
+			struct SectionSpec const *attributes)
+{
+	if (currentLoadSection)
+		fatalerror("`LOAD` blocks cannot be nested");
+
+	struct Section *pSect = getSection(name, type, org, attributes->bank,
+					   1 << attributes->alignment);
+
+	nPC = pSect->nPC;
+	setSection(pSect);
+	currentLoadSection = pSect;
+}
+
+void out_EndLoadSection(void)
+{
+	if (!currentLoadSection)
+		yyerror("Found `ENDL` outside of a `LOAD` block");
+	currentLoadSection = NULL;
+	sym_SetCurrentSymbolScope(NULL);
+
+	nPC = pCurrentSection->nPC;
+	setSection(pCurrentSection);
+}
+
+struct Section *sect_GetSymbolSection(void)
+{
+	return currentLoadSection ? currentLoadSection : pCurrentSection;
+}
+
+/*
+ * Output an absolute byte (bypassing ROM/union checks)
+ */
+static void absByteBypassCheck(uint8_t b)
+{
+	pCurrentSection->tData[pCurrentSection->nPC++] = b;
+	if (currentLoadSection)
+		currentLoadSection->nPC++;
+	nPC++;
+}
+
+/*
+ * Output an absolute byte
+ */
+void out_AbsByte(uint8_t b)
+{
+	checkcodesection();
+	checksectionoverflow(1);
+	absByteBypassCheck(b);
+}
+
+void out_AbsByteGroup(uint8_t const *s, int32_t length)
+{
+	checkcodesection();
+	checksectionoverflow(length);
+	while (length--)
+		absByteBypassCheck(*s++);
+}
+
+/*
+ * Skip this many bytes
+ */
+void out_Skip(int32_t skip)
+{
+	checksection();
+	checksectionoverflow(skip);
+	if (!sect_HasData(pCurrentSection->nType)) {
+		pCurrentSection->nPC += skip;
+		if (currentLoadSection)
+			currentLoadSection->nPC += skip;
+		nPC += skip;
+	} else if (nUnionDepth > 0) {
+		while (skip--)
+			absByteBypassCheck(CurrentOptions.fillchar);
+	} else {
+		checkcodesection();
+		while (skip--)
+			absByteBypassCheck(CurrentOptions.fillchar);
+	}
+}
+
+/*
+ * Output a NULL terminated string (excluding the NULL-character)
+ */
+void out_String(char const *s)
+{
+	checkcodesection();
+	checksectionoverflow(strlen(s));
+	while (*s)
+		absByteBypassCheck(*s++);
+}
+
+/*
+ * Output a relocatable byte. Checking will be done to see if it
+ * is an absolute value in disguise.
+ */
+void out_RelByte(struct Expression *expr)
+{
+	if (!rpn_isKnown(expr)) {
+		out_CreatePatch(PATCHTYPE_BYTE, expr);
+		out_AbsByte(0);
+	} else {
+		out_AbsByte(expr->nVal);
+	}
+	rpn_Free(expr);
+}
+
+/*
+ * Output an absolute word
+ */
+static void absWord(uint16_t b)
+{
+	checkcodesection();
+	checksectionoverflow(2);
+	pCurrentSection->tData[pCurrentSection->nPC++] = b & 0xFF;
+	pCurrentSection->tData[pCurrentSection->nPC++] = b >> 8;
+	if (currentLoadSection)
+		currentLoadSection->nPC += 2;
+	nPC += 2;
+}
+
+/*
+ * Output a relocatable word. Checking will be done to see if
+ * it's an absolute value in disguise.
+ */
+void out_RelWord(struct Expression *expr)
+{
+	if (!rpn_isKnown(expr)) {
+		out_CreatePatch(PATCHTYPE_WORD, expr);
+		absWord(0);
+	} else {
+		absWord(expr->nVal);
+	}
+	rpn_Free(expr);
+}
+
+/*
+ * Output an absolute longword
+ */
+static void absLong(uint32_t b)
+{
+	checkcodesection();
+	checksectionoverflow(4);
+	pCurrentSection->tData[pCurrentSection->nPC++] = b & 0xFF;
+	pCurrentSection->tData[pCurrentSection->nPC++] = b >> 8;
+	pCurrentSection->tData[pCurrentSection->nPC++] = b >> 16;
+	pCurrentSection->tData[pCurrentSection->nPC++] = b >> 24;
+	if (currentLoadSection)
+		currentLoadSection->nPC += 4;
+	nPC += 4;
+}
+
+/*
+ * Output a relocatable longword. Checking will be done to see if
+ * is an absolute value in disguise.
+ */
+void out_RelLong(struct Expression *expr)
+{
+	if (!rpn_isKnown(expr)) {
+		out_CreatePatch(PATCHTYPE_LONG, expr);
+		absLong(0);
+	} else {
+		absLong(expr->nVal);
+	}
+	rpn_Free(expr);
+}
+
+/*
+ * Output a PC-relative relocatable byte. Checking will be done to see if it
+ * is an absolute value in disguise.
+ */
+void out_PCRelByte(struct Expression *expr)
+{
+	checkcodesection();
+	checksectionoverflow(1);
+	if (!rpn_isKnown(expr) || pCurrentSection->nOrg == -1) {
+		out_CreatePatch(PATCHTYPE_JR, expr);
+		pCurrentSection->tData[pCurrentSection->nPC++] = 0;
+		if (currentLoadSection)
+			currentLoadSection->nPC++;
+		nPC++;
+	} else {
+		/* Target is relative to the byte *after* the operand */
+		uint16_t address = pCurrentSection->nOrg + nPC + 1;
+		/* The offset wraps (jump from ROM to HRAM, for loopexample) */
+		int16_t offset = expr->nVal - address;
+
+		if (offset < -128 || offset > 127) {
+			yyerror("jr target out of reach (expected -129 < %d < 128)", offset);
+			out_AbsByte(0);
+		} else {
+			out_AbsByte(offset);
+		}
+	}
+	rpn_Free(expr);
+}
+
+/*
+ * Output a binary file
+ */
+void out_BinaryFile(char const *s)
+{
+	FILE *f = fstk_FindFile(s, NULL);
+
+	if (!f) {
+		if (oGeneratedMissingIncludes) {
+			oFailedOnMissingInclude = true;
+			return;
+		}
+		fatalerror("Error opening INCBIN file '%s': %s", s,
+			   strerror(errno));
+	}
+
+	int32_t fsize = -1;
+	int byte;
+
+	checkcodesection();
+	if (fseek(f, 0, SEEK_END) != -1) {
+		fsize = ftell(f);
+		rewind(f);
+
+		checksectionoverflow(fsize);
+	} else if (errno != ESPIPE) {
+		yyerror("Error determining size of INCBIN file '%s': %s", s,
+			strerror(errno));
+	}
+
+	while ((byte = fgetc(f)) != EOF) {
+		if (fsize == -1)
+			checksectionoverflow(1);
+		pCurrentSection->tData[pCurrentSection->nPC++] = byte;
+		if (currentLoadSection)
+			currentLoadSection->nPC++;
+		nPC++;
+	}
+
+	if (ferror(f))
+		yyerror("Error reading INCBIN file '%s': %s", s,
+			strerror(errno));
+
+	fclose(f);
+}
+
+void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
+{
+	if (start_pos < 0) {
+		yyerror("Start position cannot be negative (%d)", start_pos);
+		start_pos = 0;
+	}
+
+	if (length < 0) {
+		yyerror("Number of bytes to read cannot be negative (%d)",
+			length);
+		length = 0;
+	}
+	if (length == 0) /* Don't even bother with 0-byte slices */
+		return;
+
+	FILE *f = fstk_FindFile(s, NULL);
+
+	if (!f) {
+		if (oGeneratedMissingIncludes) {
+			oFailedOnMissingInclude = true;
+			return;
+		}
+		fatalerror("Error opening INCBIN file '%s': %s", s,
+			   strerror(errno));
+	}
+
+	checkcodesection();
+	checksectionoverflow(length);
+
+	int32_t fsize;
+
+	if (fseek(f, 0, SEEK_END) != -1) {
+		fsize = ftell(f);
+
+		if (start_pos >= fsize) {
+			yyerror("Specified start position is greater than length of file");
+			return;
+		}
+
+		if ((start_pos + length) > fsize)
+			fatalerror("Specified range in INCBIN is out of bounds");
+
+		fseek(f, start_pos, SEEK_SET);
+	} else {
+		if (errno != ESPIPE)
+			yyerror("Error determining size of INCBIN file '%s': %s",
+				s, strerror(errno));
+		/* The file isn't seekable, so we'll just skip bytes */
+		while (start_pos--)
+			(void)fgetc(f);
+	}
+
+	int32_t todo = length;
+
+	while (todo--) {
+		int byte = fgetc(f);
+
+		if (byte != EOF) {
+			pCurrentSection->tData[pCurrentSection->nPC++] = byte;
+			if (currentLoadSection)
+				currentLoadSection->nPC++;
+			nPC++;
+		} else if (ferror(f)) {
+			yyerror("Error reading INCBIN file '%s': %s", s,
+				strerror(errno));
+		} else {
+			yyerror("Premature end of file (%d bytes left to read)",
+				todo + 1);
+		}
+	}
+
+	fclose(f);
+}
+
+/*
+ * Section stack routines
+ */
+void out_PushSection(void)
+{
+	struct SectionStackEntry *pSect;
+
+	pSect = malloc(sizeof(struct SectionStackEntry));
+	if (pSect == NULL)
+		fatalerror("No memory for section stack");
+
+	pSect->pSection = pCurrentSection;
+	pSect->pScope = sym_GetCurrentSymbolScope();
+	pSect->pNext = pSectionStack;
+	pSectionStack = pSect;
+}
+
+void out_PopSection(void)
+{
+	if (pSectionStack == NULL)
+		fatalerror("No entries in the section stack");
+
+	struct SectionStackEntry *pSect;
+
+	pSect = pSectionStack;
+	setSection(pSect->pSection);
+	pCurrentSection = pSect->pSection;
+	sym_SetCurrentSymbolScope(pSect->pScope);
+	pSectionStack = pSect->pNext;
+	free(pSect);
+}
--- a/src/asm/symbol.c
+++ b/src/asm/symbol.c
@@ -21,7 +21,7 @@
 #include "asm/symbol.h"
 #include "asm/main.h"
 #include "asm/mymath.h"
-#include "asm/output.h"
+#include "asm/section.h"
 #include "asm/util.h"
 #include "asm/warning.h"
 
@@ -64,6 +64,11 @@
 	return nLineNo;
 }
 
+static int32_t CallbackPC(struct sSymbol const *self)
+{
+	return self->pSection ? self->pSection->nOrg + self->pSection->nPC : 0;
+}
+
 /*
  * Get the nValue field of a symbol
  */
@@ -573,7 +578,7 @@
 		nsym->isExported = true;
 
 	nsym->pScope = scope;
-	nsym->pSection = pCurrentSection;
+	nsym->pSection = sect_GetSymbolSection();
 	/* Labels need to be assigned a section, except PC */
 	if (!pCurrentSection && strcmp(tzSym, "@"))
 		yyerror("Label \"%s\" created outside of a SECTION",
@@ -722,6 +727,7 @@
 
 	sym_AddReloc("@");
 	pPCSymbol = findsymbol("@", NULL);
+	pPCSymbol->Callback = CallbackPC;
 	sym_AddEqu("_NARG", 0);
 	p_NARGSymbol = findsymbol("_NARG", NULL);
 	p_NARGSymbol->Callback = Callback_NARG;
--- a/src/link/main.c
+++ b/src/link/main.c
@@ -177,10 +177,10 @@
 	}
 
 	/* Patch the size array depending on command-line options */
-	if (is32kMode)
-		maxsize[SECTTYPE_ROM0] = 0x8000;
-	if (isWRA0Mode)
-		maxsize[SECTTYPE_WRAM0] = 0x2000;
+	if (!is32kMode)
+		maxsize[SECTTYPE_ROM0] = 0x4000;
+	if (!isWRA0Mode)
+		maxsize[SECTTYPE_WRAM0] = 0x1000;
 
 	/* Patch the bank ranges array depending on command-line options */
 	if (isDmgMode)
--- a/src/linkdefs.c
+++ b/src/linkdefs.c
@@ -13,11 +13,11 @@
 };
 
 uint16_t maxsize[] = {
-	[SECTTYPE_ROM0]  = 0x4000,
+	[SECTTYPE_ROM0]  = 0x8000,
 	[SECTTYPE_ROMX]  = 0x4000,
 	[SECTTYPE_VRAM]  = 0x2000,
 	[SECTTYPE_SRAM]  = 0x2000,
-	[SECTTYPE_WRAM0] = 0x1000,
+	[SECTTYPE_WRAM0] = 0x2000,
 	[SECTTYPE_WRAMX] = 0x1000,
 	[SECTTYPE_OAM]   = 0x00A0,
 	[SECTTYPE_HRAM]  = 0x007F
--- /dev/null
+++ b/test/asm/fixed-oob.asm
@@ -1,0 +1,15 @@
+SECTION "ROM0", ROM0[$BABE]
+
+SECTION "ROMX", ROMX[$BEEF]
+
+SECTION "VRAM", VRAM[$C0DE]
+
+SECTION "SRAM", SRAM[$CAFE]
+
+SECTION "WRAM0", WRAM0[$DEAD]
+
+SECTION "WRAMX", WRAMX[$DAD]
+
+SECTION "OAM", OAM[$CAB]
+
+SECTION "HRAM", HRAM[$BAD]
--- /dev/null
+++ b/test/asm/fixed-oob.err
@@ -1,0 +1,15 @@
+ERROR: fixed-oob.asm(1):
+    Section "ROM0"'s fixed address 0xbabe is outside of range [0; 0x7fff]
+ERROR: fixed-oob.asm(3):
+    Section "ROMX"'s fixed address 0xbeef is outside of range [0x4000; 0x7fff]
+ERROR: fixed-oob.asm(5):
+    Section "VRAM"'s fixed address 0xc0de is outside of range [0x8000; 0x9fff]
+ERROR: fixed-oob.asm(7):
+    Section "SRAM"'s fixed address 0xcafe is outside of range [0xa000; 0xbfff]
+ERROR: fixed-oob.asm(11):
+    Section "WRAMX"'s fixed address 0xdad is outside of range [0xd000; 0xdfff]
+ERROR: fixed-oob.asm(13):
+    Section "OAM"'s fixed address 0xcab is outside of range [0xfe00; 0xfe9f]
+ERROR: fixed-oob.asm(15):
+    Section "HRAM"'s fixed address 0xbad is outside of range [0xff80; 0xfffe]
+error: Assembly aborted (7 errors)!
--- /dev/null
+++ b/test/asm/ram-code.asm
@@ -1,0 +1,20 @@
+SECTION "test", ROM0[1]
+	call Target
+	LOAD "new", WRAM0[$C001]
+Target:	dl $DEADBEEF
+.end
+	ENDL
+After:
+	jp Target
+	ld hl, Word
+	dw Byte, Target.end, After
+
+SECTION "ram test", WRAM0 ; Should end up at $C005
+Word:
+	dw
+
+SECTION "small ram test", WRAM0 ; Should end up at $C000
+Byte:
+	db
+
+	PRINTT "{Target}\n{Target.end}\n{After}\n"
--- /dev/null
+++ b/test/asm/ram-code.out
@@ -1,0 +1,3 @@
+$C001
+$C005
+$8
binary files /dev/null b/test/asm/ram-code.out.bin differ
--- a/test/link/fixed-oob.asm
+++ /dev/null
@@ -1,15 +1,0 @@
-SECTION "ROM0", ROM0[$BABE]
-
-SECTION "ROMX", ROMX[$BEEF]
-
-SECTION "VRAM", VRAM[$C0DE]
-
-SECTION "SRAM", SRAM[$CAFE]
-
-SECTION "WRAM0", WRAM0[$DEAD]
-
-SECTION "WRAMX", WRAMX[$DAD]
-
-SECTION "OAM", OAM[$CAB]
-
-SECTION "HRAM", HRAM[$BAD]
--- a/test/link/fixed-oob.out
+++ /dev/null
@@ -1,14 +1,0 @@
-warning: Section "VRAM"'s fixed address 0xc0de is outside of range [0x8000; 0x9fff]
-warning: Section "VRAM"'s end address 0xc0de is greater than last address 0xa000
-warning: Section "OAM"'s fixed address 0xcab is outside of range [0xfe00; 0xfe9f]
-warning: Section "WRAM0"'s fixed address 0xdead is outside of range [0xc000; 0xcfff]
-warning: Section "WRAM0"'s end address 0xdead is greater than last address 0xd000
-warning: Section "HRAM"'s fixed address 0xbad is outside of range [0xff80; 0xfffe]
-warning: Section "SRAM"'s fixed address 0xcafe is outside of range [0xa000; 0xbfff]
-warning: Section "SRAM"'s end address 0xcafe is greater than last address 0xc000
-warning: Section "WRAMX"'s fixed address 0xdad is outside of range [0xd000; 0xdfff]
-warning: Section "ROMX"'s fixed address 0xbeef is outside of range [0x4000; 0x7fff]
-warning: Section "ROMX"'s end address 0xbeef is greater than last address 0x8000
-warning: Section "ROM0"'s fixed address 0xbabe is outside of range [0; 0x3fff]
-warning: Section "ROM0"'s end address 0xbabe is greater than last address 0x4000
-error: Sanity checks failed