From c915b5d7f3bc5bc66e481001de240aae408acd6d Mon Sep 17 00:00:00 2001
From: Marco Aurelio da Costa <costa@gamic.com>
Date: Fri, 16 Apr 2021 14:42:16 -0300
Subject: [PATCH] Implemented charmap stack

New commands:
 .PUSHCHARMAP: will push the current charmap state into an internal stack
 .POPCHARMAP: will restore the current charmap to the last pushed charmap

Details:
  The push and pop facilities are implemented directly inside the tgttrans.h,
  to facilitate its reuse on the C compiler.
---
 src/ca65/pseudo.c     | 28 ++++++++++++++++++++++
 src/ca65/scanner.c    |  2 ++
 src/ca65/token.h      |  2 ++
 src/common/tgttrans.c | 56 +++++++++++++++++++++++++++++++++++++++++++
 src/common/tgttrans.h | 13 ++++++++++
 5 files changed, 101 insertions(+)

diff --git a/src/ca65/pseudo.c b/src/ca65/pseudo.c
index 26d01cbf1..df0482a5a 100644
--- a/src/ca65/pseudo.c
+++ b/src/ca65/pseudo.c
@@ -1568,6 +1568,19 @@ static void DoPageLength (void)
 
 
 
+static void DoPopCharmap (void)
+/* Restore a charmap */
+{
+    if (TgtTranslateStackIsEmpty ()) {
+        ErrorSkip ("Charmap stack is empty");
+        return;
+    }
+
+    TgtTranslatePop ();
+}
+
+
+
 static void DoPopCPU (void)
 /* Pop an old CPU setting from the CPU stack */
 {
@@ -1657,6 +1670,16 @@ static void DoPSC02 (void)
 
 
 
+static void DoPushCharmap (void)
+/* Save the current charmap */
+{
+    if (!TgtTranslatePush ()) {
+        ErrorSkip ("Charmap stack overflow");
+    }
+}
+
+
+
 static void DoPushCPU (void)
 /* Push the current CPU setting onto the CPU stack */
 {
@@ -2101,10 +2124,12 @@ static CtrlDesc CtrlCmdTab [] = {
     { ccNone,           DoUnexpected    },      /* .PARAMCOUNT */
     { ccNone,           DoPC02          },
     { ccNone,           DoPDTV          },
+    { ccNone,           DoPopCharmap    },
     { ccNone,           DoPopCPU        },
     { ccNone,           DoPopSeg        },
     { ccNone,           DoProc          },
     { ccNone,           DoPSC02         },
+    { ccNone,           DoPushCharmap   },
     { ccNone,           DoPushCPU       },
     { ccNone,           DoPushSeg       },
     { ccNone,           DoUnexpected    },      /* .REFERENCED */
@@ -2183,4 +2208,7 @@ void CheckPseudo (void)
     if (!IS_IsEmpty (&CPUStack)) {
         Warning (1, "CPU stack is not empty");
     }
+    if (!TgtTranslateStackIsEmpty ()) {
+        Warning (1, "Charmap stack is not empty");
+    }
 }
diff --git a/src/ca65/scanner.c b/src/ca65/scanner.c
index fb9905809..0452bb368 100644
--- a/src/ca65/scanner.c
+++ b/src/ca65/scanner.c
@@ -260,10 +260,12 @@ struct DotKeyword {
     { ".PARAMCOUNT",    TOK_PARAMCOUNT          },
     { ".PC02",          TOK_PC02                },
     { ".PDTV",          TOK_PDTV                },
+    { ".POPCHARMAP",    TOK_POPCHARMAP          },
     { ".POPCPU",        TOK_POPCPU              },
     { ".POPSEG",        TOK_POPSEG              },
     { ".PROC",          TOK_PROC                },
     { ".PSC02",         TOK_PSC02               },
+    { ".PUSHCHARMAP",   TOK_PUSHCHARMAP         },
     { ".PUSHCPU",       TOK_PUSHCPU             },
     { ".PUSHSEG",       TOK_PUSHSEG             },
     { ".REF",           TOK_REFERENCED          },
diff --git a/src/ca65/token.h b/src/ca65/token.h
index ab36028fd..a94254bfd 100644
--- a/src/ca65/token.h
+++ b/src/ca65/token.h
@@ -231,10 +231,12 @@ typedef enum token_t {
     TOK_PARAMCOUNT,
     TOK_PC02,
     TOK_PDTV,
+    TOK_POPCHARMAP,
     TOK_POPCPU,
     TOK_POPSEG,
     TOK_PROC,
     TOK_PSC02,
+    TOK_PUSHCHARMAP,
     TOK_PUSHCPU,
     TOK_PUSHSEG,
     TOK_REFERENCED,
diff --git a/src/common/tgttrans.c b/src/common/tgttrans.c
index bd2056505..af5bdf725 100644
--- a/src/common/tgttrans.c
+++ b/src/common/tgttrans.c
@@ -39,6 +39,8 @@
 #include "check.h"
 #include "target.h"
 #include "tgttrans.h"
+#include "coll.h"
+#include "xmalloc.h"
 
 
 
@@ -68,6 +70,9 @@ static unsigned char Tab[256] = {
     0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,
 };
 
+#define MAX_CHARMAP_STACK   16
+static Collection CharmapStack = STATIC_COLLECTION_INITIALIZER;
+
 
 
 /*****************************************************************************/
@@ -76,6 +81,8 @@ static unsigned char Tab[256] = {
 
 
 
+
+
 void TgtTranslateInit (void)
 /* Initialize the translation tables */
 {
@@ -127,3 +134,52 @@ void TgtTranslateSet (unsigned Index, unsigned char C)
     CHECK (Index < sizeof (Tab));
     Tab[Index] = C;
 }
+
+
+
+int TgtTranslatePush (void)
+/* Pushes the current translation table to the internal stack
+** Returns 1 on success, 0 on stack full
+*/
+{
+    unsigned char* TempTab;
+
+    if (CollCount (&CharmapStack) >= MAX_CHARMAP_STACK) {
+        return 0;
+    }
+
+    TempTab = xmalloc (sizeof (Tab));
+    memcpy (TempTab, Tab, sizeof (Tab));
+
+    CollAppend (&CharmapStack, TempTab);
+    return 1;
+}
+
+
+
+int TgtTranslatePop (void)
+/* Pops a translation table from the internal stack into the current table
+** Returns 1 on success, 0 on stack empty
+*/
+{
+    unsigned char* TempTab;
+
+    if (CollCount (&CharmapStack) == 0) {
+        return 0;
+    }
+
+    TempTab = CollPop (&CharmapStack);
+
+    memcpy (Tab, TempTab, sizeof (Tab));
+
+    xfree (TempTab);
+    return 1;
+}
+
+
+
+int TgtTranslateStackIsEmpty (void)
+/* Returns 1 if the internal stack is empty */
+{
+    return CollCount (&CharmapStack) == 0;
+}
diff --git a/src/common/tgttrans.h b/src/common/tgttrans.h
index 46981ec0f..a86d126db 100644
--- a/src/common/tgttrans.h
+++ b/src/common/tgttrans.h
@@ -70,6 +70,19 @@ void TgtTranslateStrBuf (StrBuf* Buf);
 void TgtTranslateSet (unsigned Index, unsigned char C);
 /* Set the translation code for the given character */
 
+int TgtTranslatePush (void);
+/* Pushes the current translation table to the internal stack
+** Returns 1 on success, 0 on stack full
+*/
+
+int TgtTranslatePop (void);
+/* Pops a translation table from the internal stack into the current table
+** Returns 1 on success, 0 on stack empty
+*/
+
+int TgtTranslateStackIsEmpty (void);
+/* Returns 1 if the internal stack is empty */
+
 
 
 /* End of tgttrans.h */