diff --git a/README.md b/README.md
index b4adb3f..369c8ee 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ HERE HOLD I IF IMMEDIATE INVERT J LEAVE LITERAL LOOP LSHIFT M* MAX MIN MOD MOVE
 REPEAT ROT RSHIFT S>D S" SIGN SM/REM SOURCE SPACE SPACES STATE SWAP THEN TYPE U. U< UNTIL UM* UM/MOD UNLOOP VARIABLE WHILE WORD XOR [ ['] [CHAR] ] 
 
 CORE-EXT:
-.( :NONAME 0<> 0> 2>R 2R> 2R@ <> ?DO AGAIN FALSE HEX NIP PAD PICK ROLL S\" TO TRUE TUCK U.R U> UNUSED VALUE
+.( :NONAME 0<> 0> 2>R 2R> 2R@ <> ?DO AGAIN BUFFER: CASE ENDCASE ENDOF FALSE HEX HOLDS MARKER NIP OF PAD PICK ROLL S\" TO TRUE TUCK U.R U> UNUSED VALUE WITHIN
 \
 
 Other sets:
@@ -69,8 +69,8 @@ CORE:
 ABORT" ACCEPT EVALUATE KEY
 
 CORE-EXT:
-.R ACTION-OF BUFFER: C" CASE COMPILE, DEFER DEFER! DEFER@ ENDCASE ENDOF ERASE HOLDS IS
-MARKER OF PARSE PARSE-NAME REFILL RESTORE-INPUT SAVE-INPUT SOURCE-ID WITHIN [COMPILE]
+.R ACTION-OF C" COMPILE, DEFER DEFER! DEFER@ ERASE IS
+PARSE PARSE-NAME REFILL RESTORE-INPUT SAVE-INPUT SOURCE-ID [COMPILE]
 ```
 
 .
diff --git a/include/fh_config.h b/include/fh_config.h
index 52fd421..0daf892 100644
--- a/include/fh_config.h
+++ b/include/fh_config.h
@@ -32,5 +32,6 @@
 #define MAGICADDR_STATE            0xFFFF57a7ULL
 #define MAGICADDR_INPTR            0xFFFFF175ULL
 #define MAGICADDR_UNRESOLVED       0xFFFFFBADULL
+#define MAGICADDR_ENDCASE_UNRESOLVED 0xFFFC5BADULL
 
 #endif //FORTH_FH_CONFIG_H
diff --git a/include/fh_runtime.h b/include/fh_runtime.h
index 48ced42..9fbc980 100644
--- a/include/fh_runtime.h
+++ b/include/fh_runtime.h
@@ -44,6 +44,13 @@ enum fh_instruction_kind {
   /* Jump if zero */
   FH_INSTR_JUMPZERO,
 
+  /* Jump if the two elements on stack do not equal, consuming the top one. 
+   * Otherwise consume both and continue. */
+  FH_INSTR_OF,
+
+  /* Endcase, pop the testval from DS */
+  FH_INSTR_ENDCASE,
+
   /* Loop exit */
   FH_INSTR_LEAVE,
 
diff --git a/src/fh_builtins_control.c b/src/fh_builtins_control.c
index d4c19b2..4d7a949 100644
--- a/src/fh_builtins_control.c
+++ b/src/fh_builtins_control.c
@@ -118,6 +118,7 @@ static enum fh_error wp_loop(struct fh_thread_s *fh, const struct fh_word_s *w)
     ii->data = endaddr;
   }
 
+  // Resolve LEAVEs
   while (startaddr < loopendaddr) {
     ii = fh_instr_at(fh, startaddr);
     if (!ii) {
@@ -223,6 +224,86 @@ static enum fh_error wp_ij(struct fh_thread_s *fh, const struct fh_word_s *w)
   return FH_OK;
 }
 
+
+
+static enum fh_error w_case(struct fh_thread_s *fh, const struct fh_word_s *w)
+{
+  (void) w;
+  enum fh_error rv;
+  TRY(cs_push(fh, fh->here)); // save marker for ENDCASE to resolve all the ENDOF's within
+  ENSURE_STATE(FH_STATE_COMPILE);
+  return FH_OK;
+}
+
+static enum fh_error w_of(struct fh_thread_s *fh, const struct fh_word_s *w)
+{
+  (void) w;
+  enum fh_error rv;
+  ENSURE_STATE(FH_STATE_COMPILE);
+  TRY(cs_push(fh, fh->here)); // save the marker for ENDOF
+  TRY(fh_put_instr(fh, FH_INSTR_OF, MAGICADDR_UNRESOLVED));
+  return FH_OK;
+}
+
+static enum fh_error w_endof(struct fh_thread_s *fh, const struct fh_word_s *w)
+{
+  (void) w;
+  enum fh_error rv;
+  ENSURE_STATE(FH_STATE_COMPILE);
+  uint32_t ofaddr;
+  TRY(cs_pop(fh, &ofaddr));
+  
+  struct fh_instruction_s *of_instr = fh_instr_at(fh, ofaddr);
+  if (!of_instr || of_instr->data != MAGICADDR_UNRESOLVED) {
+    LOGE("CASE-OF control stack corruption");
+    return FH_ERR_INTERNAL;
+  }
+
+  of_instr->data = fh->here + INSTR_SIZE; // next
+
+  TRY(fh_put_instr(fh, FH_INSTR_JUMP, MAGICADDR_ENDCASE_UNRESOLVED)); // go to end of CASEs
+  return FH_OK;
+}
+
+static enum fh_error w_endcase(struct fh_thread_s *fh, const struct fh_word_s *w)
+{
+  (void) w;
+  enum fh_error rv;
+  ENSURE_STATE(FH_STATE_COMPILE);
+  uint32_t caseaddr;
+  TRY(cs_pop(fh, &caseaddr));
+  
+  // Now walk the instructions and resolve every MAGICADDR_ENDCASE_UNRESOLVED
+  
+  uint32_t caseendaddr = fh->here;
+  struct fh_instruction_s *ii;
+  
+  // Resolve ENDOF. TODO copied from LOOP impl, unify?
+  while (caseaddr < caseendaddr) {
+    ii = fh_instr_at(fh, caseaddr);
+    if (!ii) {
+      LOGE("WHAT?");
+      return FH_ERR_INTERNAL;
+    }
+    if (ii->kind == FH_INSTR_JUMP && ii->data == MAGICADDR_ENDCASE_UNRESOLVED) {
+      LOG("Resolve endof jump");
+      ii->data = caseendaddr + INSTR_SIZE;
+    }
+
+    // forward, skipping strings safely
+    if (ii->kind == FH_INSTR_TYPESTR || ii->kind == FH_INSTR_ALLOCSTR) {
+      caseaddr += INSTR_SIZE + ii->data;
+      caseaddr = WORDALIGNED(caseaddr);
+    } else {
+      caseaddr += INSTR_SIZE;
+    }
+  }
+  
+  TRY(fh_put_instr(fh, FH_INSTR_ENDCASE, 0));
+  
+  return FH_OK;
+}
+
 const struct name_and_handler fh_builtins_control[] = {
     {"i",       wp_ij,     0, 0},
     {"j",       wp_ij,     0, 1},
@@ -241,6 +322,10 @@ const struct name_and_handler fh_builtins_control[] = {
     {"again",   w_again,   1, 0},
     {"until",   w_until,   1, 0},
     {"unloop",  w_unloop,  0, 0},
+    {"case",    w_case,    1, 0},
+    {"of",      w_of,      1, 0},
+    {"endof",   w_endof,   1, 0},
+    {"endcase", w_endcase, 1, 0},
 
     { /* end marker */ }
 };
diff --git a/src/fh_runtime.c b/src/fh_runtime.c
index 47fb16c..f9e0774 100644
--- a/src/fh_runtime.c
+++ b/src/fh_runtime.c
@@ -139,7 +139,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
   fh->execptr += INSTR_SIZE;
 
   uint32_t strl;
-  uint32_t val;
+  uint32_t val, testval;
   uint32_t limit, index;
 
   LOG("0x%08x: Instr %s, 0x%08x", fh->execptr, instr_name(instr->kind), instr->data);
@@ -321,6 +321,29 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
       fh->execptr = instr->data;
       goto instr;
 
+    case FH_INSTR_OF:
+      LOG("\x1b[35mExec: OF\x1b[m");
+      if (instr->data == MAGICADDR_UNRESOLVED) {
+        LOGE("Encountered unresolved OF!");
+        goto end;
+      }
+      TRY(ds_pop(fh, &testval));
+      TRY(ds_pop(fh, &val));
+      
+      LOG("Val %d, testval %d", val, testval);
+      
+      if (testval != val) {
+        LOG("No match, go to ENDOF");
+        TRY(ds_push(fh, val));
+        fh->execptr = instr->data;
+      }
+      goto instr;
+      
+    case FH_INSTR_ENDCASE:
+      LOG("\x1b[35mExec: ENDCASE\x1b[m");
+      TRY(ds_pop(fh, &val)); // discard the tested value
+      goto instr;
+
       /* special case for strings stored in compile memory */
     case FH_INSTR_ALLOCSTR:
     case FH_INSTR_TYPESTR:
diff --git a/src/fh_see.c b/src/fh_see.c
index 2280255..e336583 100644
--- a/src/fh_see.c
+++ b/src/fh_see.c
@@ -53,6 +53,14 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w)
             }
             break;
 
+          case FH_INSTR_OF:
+            FHPRINT("OF(value %d / 0x%08x)\n", instr->data, instr->data);
+            break;
+
+          case FH_INSTR_ENDCASE:
+            FHPRINT("ENDCASE\n");
+            break;
+
           case FH_INSTR_TO:
             w2 = fh_word_at(fh, instr->data);
             if (w2) {