diff --git a/nemu/include/monitor/watchpoint.h b/nemu/include/monitor/watchpoint.h
index 7d4c0e5e061eb64da051e2cc83d1fb26c5a9d878..a105199ae5048285097cdbd28c5e697bedeb3082 100644
--- a/nemu/include/monitor/watchpoint.h
+++ b/nemu/include/monitor/watchpoint.h
@@ -17,7 +17,7 @@ struct WP {
   bool evalulate() {
     auto new_value = evaluate_expr(expr);
     std::swap(new_value, curr_value);
-    return new_value != curr_value;
+    return new_value != curr_value && curr_value != 0;
   }
   WP(std::string e, int id) : expr(e), id(id) {
     evalulate(); // initial expr value.
diff --git a/nemu/prebuilt/nemu-eb60a1a27066a289437c541b605da103dc5b71e4 b/nemu/prebuilt/nemu-eb60a1a27066a289437c541b605da103dc5b71e4
new file mode 100755
index 0000000000000000000000000000000000000000..565c8134909991ebe3541ceba835defebda41359
Binary files /dev/null and b/nemu/prebuilt/nemu-eb60a1a27066a289437c541b605da103dc5b71e4 differ
diff --git a/nemu/prebuilt/nemu-nommio-eb60a1a27066a289437c541b605da103dc5b71e4 b/nemu/prebuilt/nemu-nommio-eb60a1a27066a289437c541b605da103dc5b71e4
new file mode 100755
index 0000000000000000000000000000000000000000..8bfd79721a8db98827b3b0effb0f52f27b9c87cf
Binary files /dev/null and b/nemu/prebuilt/nemu-nommio-eb60a1a27066a289437c541b605da103dc5b71e4 differ
diff --git a/nemu/src/device/device.cc b/nemu/src/device/device.cc
index b36ae401cc29a4af046bc59ab548579e0d775e9e..f9a53043270df70ae03c50f63d62a931fc39c3b3 100644
--- a/nemu/src/device/device.cc
+++ b/nemu/src/device/device.cc
@@ -40,8 +40,6 @@ static void timer_sig_handler(int signum) {
   Assert(ret == 0, "Can not set timer");
 }
 
-void device_update() {} // Now an independent thread will do it.
-
 void device_update_impl() {
   if (update_screen_flag) {
     update_screen();
@@ -72,7 +70,16 @@ void device_update_impl() {
   }
 }
 
-static void device_update_thread_daemon() {
+void device_update() {
+#ifdef ENABLE_ASYNC_RENDER
+#else
+  if(device_update_flag.exchange(false)) {
+    device_update_impl();
+  }
+#endif
+}
+
+[[maybe_unused]] static void device_update_thread_daemon() {
   while(true) {
     if(device_update_flag.exchange(false)) {
       device_update_impl();
@@ -104,7 +111,9 @@ void init_device() {
   ret = setitimer(ITIMER_VIRTUAL, &it, NULL);
   Assert(ret == 0, "Can not set timer");
 
+#ifdef ENABLE_ASYNC_RENDER
   std::thread(device_update_thread_daemon).detach();
+#endif
 }
 #else
 
diff --git a/nemu/src/device/vga.cc b/nemu/src/device/vga.cc
index 8095b786bb84fac392e771fc34680c39a880632c..8db9ea52c6bb19e644bb63884cd7a186f44ab6ef 100644
--- a/nemu/src/device/vga.cc
+++ b/nemu/src/device/vga.cc
@@ -19,9 +19,11 @@ static uint32_t (*vmem) [SCREEN_W];
 static uint32_t *screensize_port_base;
 
 inline void SDL_ErrorCheck(int ret) {
+#ifdef DEBUG
   if(ret != 0) {
     rlib::println("SDL_Error: ret=", ret, ", GETERR=", SDL_GetError());
   }
+#endif
 }
 
 static void init_vga_impl() {
@@ -38,7 +40,9 @@ static void init_vga_impl() {
 
 void update_screen() {
 #ifndef DISABLE_MMIO
+#ifdef ENABLE_ASYNC_RENDER
   if(window == nullptr) init_vga_impl();
+#endif
   SDL_ErrorCheck(SDL_UpdateTexture(texture, NULL, vmem, SCREEN_W * sizeof(vmem[0][0])));
   SDL_ErrorCheck(SDL_RenderClear(renderer));
   SDL_ErrorCheck(SDL_RenderCopy(renderer, texture, NULL, NULL));
@@ -47,8 +51,12 @@ void update_screen() {
 }
 
 void init_vga() {
+#ifdef ENABLE_ASYNC_RENDER
   // Because of fucking SDL design, vga_init should be done in updating thread.
   // Do nothing in main thread.
+#else
+  init_vga_impl();
+#endif
 }
 
 #endif	/* HAS_IOE */
diff --git a/nemu/src/monitor/debug/expr_impl/lexer.l b/nemu/src/monitor/debug/expr_impl/lexer.l
index 851781d09a0703631b37e37bf87ece900272e0b1..108a9f39c9b76dadf758b2e39829b18258075424 100644
--- a/nemu/src/monitor/debug/expr_impl/lexer.l
+++ b/nemu/src/monitor/debug/expr_impl/lexer.l
@@ -13,7 +13,7 @@
 %%
 
 [ \t\n]       {}
-0x[0-9a-f]+      {yylval.ival = (int)strtol(yytext, NULL, 16); return T_INT;}
+0[xX][0-9a-f]+      {yylval.ival = (int)strtol(yytext, NULL, 16); return T_INT;}
 [0-9]+        {yylval.ival = atoi(yytext); return T_INT;}
 "=="          {return T_EQUAL;}
 "!="          {return T_NEQUAL;}
@@ -21,6 +21,7 @@
 [-+*/()]      {return yytext[0];}
 
 "%"[a-z]{3}   {yylval.ival = ((uint32_t)yytext[2] << 8) + yytext[3]; return T_REG;}
+"$"[a-z]{3}   {yylval.ival = ((uint32_t)yytext[2] << 8) + yytext[3]; return T_REG;}
 .             {throw std::runtime_error(std::string("lexer error around token: ") + yytext);}
 %%
 // Registers: eax='a'+'x', ebx='b'+'x', ...
diff --git a/nemu/src/monitor/debug/ui.cc b/nemu/src/monitor/debug/ui.cc
index 73158491ea81cd766308441595894f4f9b986275..2332389eab03c5e8fbbc176b62c5be8e3a89f695 100644
--- a/nemu/src/monitor/debug/ui.cc
+++ b/nemu/src/monitor/debug/ui.cc
@@ -70,6 +70,7 @@ static struct {
   { "help", "Display informations about all supported commands", cmd_help },
   { "c", "Continue the execution of the program", cmd_c },
   { "n", "= GDB `n`", cmd_n },
+  { "si", "= GDB `n`", cmd_n },
   { "info", "= GDB `info`, supporting `info r` / `info w`", cmd_info },
   { "x", "x <bytes> <startAddr or expr>, dump memory content.", cmd_x },
   { "w", "w <expr>, add watchpoint for $expr", cmd_w },
@@ -201,7 +202,7 @@ static int cmd_w(char *_args) {
   if(_args == NULL)
     throw std::invalid_argument("w <expr>");
   
-  watchpoints.emplace_front(std::string(_args), ++max_watchpoint_id);
+  watchpoints.emplace_front(std::string(_args), max_watchpoint_id++);
   auto iter = watchpoints.begin(); // not thread-safe.
   rlib::println("Add watchpoint:", *iter);
   return 0;
diff --git a/nexus-am/apps/typing/draw.c b/nexus-am/apps/typing/draw.c
index a83d7b995bf80e4a89f6b0ebeb3afd21e5a1716f..11bd1ad05dd902cb3b31175fa3fe3293f9d22e7b 100644
--- a/nexus-am/apps/typing/draw.c
+++ b/nexus-am/apps/typing/draw.c
@@ -1,6 +1,6 @@
 #include "game.h"
 
-//#define SCREEN_STRETCH
+#define SCREEN_STRETCH
 
 static uint32_t canvas[H][W];