diff --git a/nemu/include/common.h b/nemu/include/common.h
index d7658173d93d3e3c845fe43e6fbd7ad7608aa846..b734ebc0e3100c1c84e49fb8e93a1cb011d8eee5 100644
--- a/nemu/include/common.h
+++ b/nemu/include/common.h
@@ -11,7 +11,7 @@
 #endif
 
 /* You will define this macro in PA2 */
-//#define HAS_IOE
+#define HAS_IOE
 
 #include <stdint.h>
 #include <assert.h>
diff --git a/nemu/include/device/port-io.h b/nemu/include/device/port-io.h
index 84e2488d5a2a629abdcd4256710fdf88a275b62b..62969e3993f382838cd1652f4ef79b4149a5ab53 100644
--- a/nemu/include/device/port-io.h
+++ b/nemu/include/device/port-io.h
@@ -7,4 +7,10 @@ typedef void(*pio_callback_t)(ioaddr_t, int, bool);
 
 void* add_pio_map(ioaddr_t, int, pio_callback_t);
 
+template <size_t Bytes>
+uint32_t pio_read_common(ioaddr_t addr);
+
+template <size_t Bytes>
+void pio_write_common(ioaddr_t addr, uint32_t data);
+
 #endif
diff --git a/nemu/src/cpu/exec/control.cc b/nemu/src/cpu/exec/control.cc
index 8c05bb96a5619fd0996619a62c5194713792a9bf..f21343ac1bfc5c1942a7e407019b7b252e5ac725 100644
--- a/nemu/src/cpu/exec/control.cc
+++ b/nemu/src/cpu/exec/control.cc
@@ -66,7 +66,7 @@ namespace EHelperImpl {
   
   make_EHelper(call_rm) {
     RLIB_MACRO_DEBUG_ASSERT(decoding.is_operand_size_16 == false);
-    rlib::println("Reach call_rm. WARNING: this instruction impl is heavily broken.");
+    // rlib::println("Reach call_rm. WARNING: this instruction impl is heavily broken.");
 
     rtl_push<4>(&decoding.seq_eip);
     rtl_jr(&id_dest->val);
diff --git a/nemu/src/cpu/exec/system.cc b/nemu/src/cpu/exec/system.cc
index c1876cf6a13a644399072f7b7066dd6e5377cb13..7c820e92673ba5d05497edf801cad2d795d0acfb 100644
--- a/nemu/src/cpu/exec/system.cc
+++ b/nemu/src/cpu/exec/system.cc
@@ -1,4 +1,5 @@
 #include "cpu/exec.h"
+#include "device/port-io.h"
 
 void difftest_skip_ref();
 void difftest_skip_dut();
@@ -43,7 +44,14 @@ namespace EHelperImpl {
   }
   
   make_EHelper(in) {
-    TODO();
+    rtlreg_t tmp;
+  	switch(id_src->width)
+  	{
+  		case 1: tmp = pio_read_common<1>(id_src->val); break;
+  		case 2: tmp = pio_read_common<2>(id_src->val); break;
+  		case 4: tmp = pio_read_common<4>(id_src->val); break;
+  	}
+  	operand_write(id_dest, &tmp);
   
     print_asm_template2(in);
   
@@ -53,8 +61,13 @@ namespace EHelperImpl {
   }
   
   make_EHelper(out) {
-    TODO();
-  
+	  switch(id_src->width)
+	  {
+	  	case 1: pio_write_common<1>(id_dest->val, id_src->val); break;
+	  	case 2: pio_write_common<2>(id_dest->val, id_src->val); break;
+	  	case 4: pio_write_common<4>(id_dest->val, id_src->val); break;
+	  }
+
     print_asm_template2(out);
   
   #if defined(DIFF_TEST)
diff --git a/nemu/src/device/io/port-io.cc b/nemu/src/device/io/port-io.cc
index 0b0960b6470af32266529a9ac9e6ccd1d89f915c..cd9a62d8f3e93c655c35920760a519c697f6446f 100644
--- a/nemu/src/device/io/port-io.cc
+++ b/nemu/src/device/io/port-io.cc
@@ -39,49 +39,30 @@ void* add_pio_map(ioaddr_t addr, int len, pio_callback_t callback) {
   return pio_space + addr;
 }
 
-static inline uint32_t pio_read_common(ioaddr_t addr, int len) {
-  assert(addr + len - 1 < PORT_IO_SPACE_MAX);
-  pio_callback(addr, len, false);		// prepare data to read
-  switch (len) {
-    case 4: return *(uint32_t *)(pio_space + addr);
-    case 2: return *(uint16_t *)(pio_space + addr);
-    case 1: return *(uint8_t *)(pio_space + addr);
-    default: assert(0);
-  }
-}
-
-static inline void pio_write_common(ioaddr_t addr, uint32_t data, int len) {
-  assert(addr + len - 1 < PORT_IO_SPACE_MAX);
-  switch (len) {
-    case 4: *(uint32_t *)(pio_space + addr) = data; break;
-    case 2: *(uint16_t *)(pio_space + addr) = data; break;
-    case 1: *(uint8_t *)(pio_space + addr) = data; break;
-    default: assert(0);
-  }
-  pio_callback(addr, len, true);
-}
+template <size_t Bytes>
+uint32_t pio_read_common(ioaddr_t addr) {
+  static_assert(Bytes == 4 || Bytes == 2 || Bytes == 1);
+  using result_type = std::conditional_t<Bytes == 4, uint32_t, std::conditional_t<Bytes == 2, uint16_t, uint8_t>>;
 
-/* CPU interface */
-uint32_t pio_read_l(ioaddr_t addr) {
-  return pio_read_common(addr, 4);
+  assert(addr + Bytes - 1 < PORT_IO_SPACE_MAX);
+  pio_callback(addr, Bytes, false);		// prepare data to read
+  return *(result_type *)(pio_space + addr);
 }
 
-uint32_t pio_read_w(ioaddr_t addr) {
-  return pio_read_common(addr, 2);
-}
+template <size_t Bytes>
+void pio_write_common(ioaddr_t addr, uint32_t data) {
+  static_assert(Bytes == 4 || Bytes == 2 || Bytes == 1);
+  using result_type = std::conditional_t<Bytes == 4, uint32_t, std::conditional_t<Bytes == 2, uint16_t, uint8_t>>;
 
-uint32_t pio_read_b(ioaddr_t addr) {
-  return pio_read_common(addr, 1);
+  assert(addr + Bytes - 1 < PORT_IO_SPACE_MAX);
+  *(result_type *)(pio_space + addr) = data;
+  pio_callback(addr, Bytes, true);
 }
 
-void pio_write_l(ioaddr_t addr, uint32_t data) {
-  pio_write_common(addr, data, 4);
-}
 
-void pio_write_w(ioaddr_t addr, uint32_t data) {
-  pio_write_common(addr, data, 2);
-}
-
-void pio_write_b(ioaddr_t addr, uint32_t data) {
-  pio_write_common(addr, data, 1);
-}
+template uint32_t pio_read_common<4>(ioaddr_t addr);
+template uint32_t pio_read_common<2>(ioaddr_t addr);
+template uint32_t pio_read_common<1>(ioaddr_t addr);
+template void pio_write_common<4>(ioaddr_t addr, uint32_t data);
+template void pio_write_common<2>(ioaddr_t addr, uint32_t data);
+template void pio_write_common<1>(ioaddr_t addr, uint32_t data);
diff --git a/nemu/src/device/vga.cc b/nemu/src/device/vga.cc
index 20c1678c73548e6cd507d0f8c390dfc21d72d978..66445841d2b6d50cda986da93c74f0740338f649 100644
--- a/nemu/src/device/vga.cc
+++ b/nemu/src/device/vga.cc
@@ -7,8 +7,7 @@
 #include <SDL2/SDL.h>
 
 #define VMEM 0x40000
-
-#define SCREEN_PORT 0x100 // Note that this is not the standard
+#define SCREEN_PORT 0x100
 #define SCREEN_H 300
 #define SCREEN_W 400
 
@@ -33,8 +32,8 @@ void init_vga() {
   texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
       SDL_TEXTUREACCESS_STATIC, SCREEN_W, SCREEN_H);
 
-  screensize_port_base = add_pio_map(SCREEN_PORT, 4, NULL);
+  screensize_port_base = reinterpret_cast<decltype(screensize_port_base)>(add_pio_map(SCREEN_PORT, 4, nullptr));
   *screensize_port_base = ((SCREEN_W) << 16) | (SCREEN_H);
-  vmem = add_mmio_map(VMEM, 0x80000, NULL);
+  vmem = reinterpret_cast<decltype(vmem)>(add_mmio_map(VMEM, 0x80000, nullptr));
 }
 #endif	/* HAS_IOE */
diff --git a/nexus-am/am/arch/x86-nemu/src/devices/timer.c b/nexus-am/am/arch/x86-nemu/src/devices/timer.c
index b7c366cddf8fcc1c79f2d3d3504c876a90792bf7..9fd970cbd575995ee63c88b49ac719fc156972dd 100644
--- a/nexus-am/am/arch/x86-nemu/src/devices/timer.c
+++ b/nexus-am/am/arch/x86-nemu/src/devices/timer.c
@@ -3,11 +3,15 @@
 #include <amdev.h>
 
 size_t timer_read(uintptr_t reg, void *buf, size_t size) {
+  const size_t rtc_port_id = 0x48;
+  uint64_t curr_time;
+
   switch (reg) {
     case _DEVREG_TIMER_UPTIME: {
       _UptimeReg *uptime = (_UptimeReg *)buf;
-      uptime->hi = 0;
-      uptime->lo = 0;
+      curr_time = inl(rtc_port_id);
+      uptime->hi = curr_time >> 32;
+      uptime->lo = (uint32_t) curr_time;
       return sizeof(_UptimeReg);
     }
     case _DEVREG_TIMER_DATE: {
diff --git a/nexus-am/libs/klib/src/printf-lib/printf.c b/nexus-am/libs/klib/src/printf-lib/printf.c
index 8a700add4c85e36e2c9ff01fd1f1a7c3acb28e22..23edb2dfa671526c98e7d6c4ea036ddbb38d2880 100644
--- a/nexus-am/libs/klib/src/printf-lib/printf.c
+++ b/nexus-am/libs/klib/src/printf-lib/printf.c
@@ -35,6 +35,8 @@
 
 #include "printf.h"
 
+#include "am.h"
+void _putchar(char c) {_putc(c);}
 
 // define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
 // printf_config.h header file
diff --git a/nexus-am/libs/klib/src/stdio.c b/nexus-am/libs/klib/src/stdio.c
index 681f124b032d542c73631459224454daf83dbe19..5807363e6502af940aea29da7549ffc402b5e53a 100644
--- a/nexus-am/libs/klib/src/stdio.c
+++ b/nexus-am/libs/klib/src/stdio.c
@@ -30,8 +30,5 @@ int vsprintf_(char *out, const char *fmt, va_list va) {
 //  return 0;
 //}
 
-void _putchar(char character) {
-    ;
-}
 
 #endif