diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 16e5ba1119333bfa0d6adfd4197a7fc01ccbc7a5..25079d45b53d488132212ed36af9e488b929df20 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -38,3 +38,10 @@ test-sw:
   script:
     - cd sw/
     - make test
+
+test_mm:
+    stage: test
+    image: bslathi19/modelsim_18.1:lite
+    script:
+      - cd hw/fpga/simulation/modelsim/
+      - vsim -do "do mm_testbench.do"
diff --git a/hw/fpga/addr_decode.sv b/hw/fpga/addr_decode.sv
index 01c32cbd43980d774444e7368a6cfb319051c58c..a145fccdfad9058502c918a661d2fce8d2423b4a 100644
--- a/hw/fpga/addr_decode.sv
+++ b/hw/fpga/addr_decode.sv
@@ -1,18 +1,22 @@
 module addr_decode(
-    input logic [15:0] addr,
+    input logic [23:0] addr,
     output logic sdram_cs,
     output logic rom_cs,
     output logic hex_cs,
     output logic uart_cs,
     output logic irq_cs,
-    output logic board_io_cs
+    output logic board_io_cs,
+	output logic mm_cs1,
+	output logic mm_cs2
 );
 
-assign rom_cs = addr >= 16'h8000;
-assign sdram_cs = addr < 16'h7ff0;
-assign hex_cs = addr >= 16'h7ff0 && addr < 16'h7ff4;
-assign uart_cs = addr >= 16'h7ff4 && addr < 16'h7ff6;
-assign board_io_cs = addr == 16'h7ff6;
-assign irq_cs  = addr == 16'h7fff;
+assign rom_cs = addr >= 24'h008000 && addr < 24'h010000;
+assign sdram_cs = addr < 24'h007fe0 || addr >= 24'h010000;
+assign mm_cs1 = addr >= 24'h007fe0 && addr < 24'h007ff0;
+assign hex_cs = addr >= 24'h007ff0 && addr < 24'h007ff4;
+assign uart_cs = addr >= 24'h007ff4 && addr < 24'h007ff6;
+assign board_io_cs = addr == 24'h007ff6;
+assign mm_cs2 = addr == 24'h007ff7;
+assign irq_cs  = addr == 24'h007fff;
 
 endmodule
diff --git a/hw/fpga/hvl/cs_testbench.sv b/hw/fpga/hvl/cs_testbench.sv
index 7624080d597921df00d4bc96bba067742e507ea3..a6e10729320faba518e995c17951c0caa59f4814 100644
--- a/hw/fpga/hvl/cs_testbench.sv
+++ b/hw/fpga/hvl/cs_testbench.sv
@@ -4,27 +4,29 @@ timeunit 10ns;
 
 timeprecision 1ns;
 
-logic [15:0] addr;
+logic [23:0] addr;
 logic sdram_cs;
 logic rom_cs;
 logic hex_cs;
 logic board_io_cs;
 logic uart_cs;
 logic irq_cs;
+logic mm_cs2;
+logic mm_cs1;
 
-int cs_count = sdram_cs + rom_cs + hex_cs + uart_cs + board_io_cs;
+int cs_count = sdram_cs + rom_cs + hex_cs + uart_cs + board_io_cs + mm_cs2 + mm_cs1;
 
 addr_decode dut(.*);
 
 initial begin : TEST_VECTORS
 
-    for (int i = 0; i < 2**16; i++) begin
+    for (int i = 0; i < 2**24; i++) begin
         addr <= i;
         #1
         assert(cs_count < 2)
         else
             $error("Multiple chip selects present!");
-        if (i < 16'h7ff0) begin
+        if (i < 16'h7fe0 || i >= 24'h010000) begin
             assert(sdram_cs == '1)
             else
                 $error("Bad CS! addr=%4x should have sdram_cs!", addr);
@@ -44,12 +46,22 @@ initial begin : TEST_VECTORS
             else
                 $error("Bad CS! addr=%4x should have board_io_cs!", addr);
         end
+        if (i == 16'h7ff7) begin
+            assert(mm_cs2 == '1)
+            else
+                $error("Bad CS! addr=%4x should have mm_cs2!", addr);
+        end
+        if (i >= 16'h7fe0 && i < 16'h7ff0) begin
+            assert(mm_cs1 == '1)
+            else
+                $error("Bad CS! addr=%4x should have mm_cs1!", addr);
+        end
         if (i == 16'h7fff) begin
             assert(irq_cs == '1)
             else
                 $error("Bad CS! addr=%4x should have irq_cs!", addr);
         end
-        if (i >= 2**15) begin
+        if (i >= 2**15 && i < 24'h010000) begin
             assert(rom_cs == '1)
             else
                 $error("Bad CS! addr=%4x should have rom_cs!", addr);
diff --git a/hw/fpga/hvl/mm_testbench.sv b/hw/fpga/hvl/mm_testbench.sv
new file mode 100644
index 0000000000000000000000000000000000000000..b4983376ee4d60728139273fb03d7d1462f41597
--- /dev/null
+++ b/hw/fpga/hvl/mm_testbench.sv
@@ -0,0 +1,85 @@
+module testbench();
+
+timeunit 10ns;
+
+timeprecision 1ns;
+
+logic clk_50, clk, cs;
+logic rw, MM_cs;
+logic rst;
+logic [3:0] RS, MA;
+logic [7:0] data_in;
+logic [7:0] data_out;
+
+logic [11:0] MO;
+
+logic [11:0] _data_in;
+assign _data_in = {4'h0, data_in};
+
+logic [11:0] _data_out;
+assign data_out = _data_out[7:0];
+
+logic [15:0] cpu_addr;
+logic [23:0] mm_address;
+assign MA = cpu_addr[15:12];
+assign mm_address = {MO, cpu_addr[11:0]};
+
+memory_mapper dut(
+	.data_in(_data_in),
+	.data_out(_data_out),
+	.*
+);
+
+always #1 clk_50 = clk_50 === 1'b0;
+always #100 clk = clk === 1'b0;
+
+task write_reg(logic [3:0] addr, logic [7:0] data);
+	@(negedge clk);
+	cs <= '1;
+	RS <= addr;
+	data_in <= data;
+	rw <= '0;
+	@(posedge clk);
+	cs <= '0;
+	rw <= '1;
+	@(negedge clk);
+endtask
+
+task enable(logic [7:0] data);
+	@(negedge clk);
+	MM_cs <= '1;
+	rw <= '0;
+	data_in <= data;
+	@(posedge clk);
+	rw <= '1;
+	MM_cs <= '0;
+	@(negedge clk);
+endtask
+
+initial begin
+	rst <= '1;
+	repeat(5) @(posedge clk);
+	rst <= '0;
+
+	cpu_addr <= 16'h0abc;
+	write_reg(4'h0, 8'hcc);
+	$display("Address: %x", mm_address);
+	assert(mm_address == 24'h000abc) else begin
+		$error("Bad address before enable!");
+	end
+
+	enable(1);
+	$display("Address: %x", mm_address);
+	assert(mm_address == 24'h0ccabc) else begin
+		$error("Bad address after enable!");
+	end
+
+	enable(0);
+	$display("Address: %x", mm_address);
+	assert(mm_address == 24'h000abc) else begin
+		$error("Bad address after enable!");
+	end
+	$finish();
+end
+
+endmodule
diff --git a/hw/fpga/memory_mapper.sv b/hw/fpga/memory_mapper.sv
new file mode 100644
index 0000000000000000000000000000000000000000..00858d76a371391226592b02d5245ff66110d522
--- /dev/null
+++ b/hw/fpga/memory_mapper.sv
@@ -0,0 +1,58 @@
+/*
+ *	This is based off of the 74LS610, but is not identical.
+	Some of the inputs are flipped so that they are all active high,
+	and some outputs are reordered.
+	Notably, when MM is low, MA is present on MO0-MO4, not 8 to 11.
+ */
+
+module memory_mapper(
+	input clk,
+	input rst,
+
+	input rw,
+	input cs,
+
+	input MM_cs,
+
+	input [3:0] RS,
+
+	input [3:0] MA,
+
+	input logic [11:0] data_in,
+	output logic [11:0] data_out,
+
+	output logic [11:0] MO
+);
+
+logic [11:0] RAM [16];
+
+logic MM;
+
+
+always_ff @(posedge clk) begin
+    if (rst) begin
+        MM <= '0;
+    end else begin
+        if (MM_cs & ~rw) begin					// can't read MM but do you really need too?
+            MM = |data_in;
+        end
+
+        if (cs & ~rw) begin					// write to registers
+            RAM[RS] <= data_in;
+        end else if (cs & rw) begin			// read registers
+            data_out <= RAM[RS];
+        end
+    end
+end
+
+
+always_comb begin
+	if (MM) begin						// normal mode
+		MO = RAM[MA];
+	end else begin						// passthrough mode
+		MO = {8'b0, MA};
+	end
+end
+
+endmodule
+
diff --git a/hw/fpga/sdram.sv b/hw/fpga/sdram.sv
index 522596f38f1d64e34a93e28b51e9f88db3701eaf..6b28f405b508ac588c0f3cc805dc75ec962c7ab3 100644
--- a/hw/fpga/sdram.sv
+++ b/hw/fpga/sdram.sv
@@ -2,7 +2,7 @@ module sdram(
     input rst,
     input clk_50,
     input cpu_clk,
-    input [15:0] addr,
+    input [23:0] addr,
     input sdram_cs,
     input rwb,
     input [7:0] data_in,
@@ -84,4 +84,4 @@ sdram_platform u0 (
     .sdram_wire_we_n(DRAM_WE_N)                          //.we_n
 );
 
-endmodule
\ No newline at end of file
+endmodule
diff --git a/hw/fpga/simulation/modelsim/mm_testbench.do b/hw/fpga/simulation/modelsim/mm_testbench.do
new file mode 100644
index 0000000000000000000000000000000000000000..9cdf64941120065d0e29567a71f639d8600c51e4
--- /dev/null
+++ b/hw/fpga/simulation/modelsim/mm_testbench.do
@@ -0,0 +1,24 @@
+transcript on
+if {[file exists rtl_work]} {
+	vdel -lib rtl_work -all
+}
+vlib rtl_work
+vmap work rtl_work
+
+vlog -sv -work work  {../../memory_mapper.sv}
+vlog -sv -work work  {../../hvl/mm_testbench.sv}
+
+vsim -t 1ps -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_lnsim_ver -L stratixv_ver -L stratixv_hssi_ver -L stratixv_pcie_hip_ver -L rtl_work -L work -voptargs="+acc"  testbench
+
+add wave -group {dut} -radix hexadecimal sim:/testbench/dut/*
+
+onfinish stop
+run -all
+
+if { [coverage attribute -name TESTSTATUS -concise] == "1"} {
+    echo Warning
+    quit -f -code 0
+}
+
+quit -code [coverage attribute -name TESTSTATUS -concise]
+
diff --git a/hw/fpga/super6502.qsf b/hw/fpga/super6502.qsf
index 09cd0fceba0fad1eb02a17d61495f85baabbe429..fdb728991730b0d8498d3348b73f00b230641c90 100644
--- a/hw/fpga/super6502.qsf
+++ b/hw/fpga/super6502.qsf
@@ -350,6 +350,7 @@ set_location_assignment PIN_V22 -to DRAM_LDQM
 set_location_assignment PIN_U22 -to DRAM_RAS_N
 set_location_assignment PIN_J21 -to DRAM_UDQM
 set_location_assignment PIN_V20 -to DRAM_WE_N
+set_global_assignment -name SYSTEMVERILOG_FILE memory_mapper.sv
 set_global_assignment -name SYSTEMVERILOG_FILE board_io.sv
 set_global_assignment -name SYSTEMVERILOG_FILE sdram.sv
 set_global_assignment -name QIP_FILE sdram_platform/synthesis/sdram_platform.qip
diff --git a/hw/fpga/super6502.sv b/hw/fpga/super6502.sv
index 896a50191a9ecdc29443f565fc41475dcc0b116b..fdfdafa1ba66304c831739e971b75a3e0eb6591f 100644
--- a/hw/fpga/super6502.sv
+++ b/hw/fpga/super6502.sv
@@ -3,15 +3,15 @@ module super6502(
     input                   clk_50,
     input   logic           rst_n,
     input   logic           button_1,
-    
+
     input   logic [15:0]    cpu_addr,
     inout   logic [7:0]     cpu_data,
-    
+
     input   logic           cpu_vpb,
     input   logic           cpu_mlb,
     input   logic           cpu_rwb,
     input   logic           cpu_sync,
-    
+
     output  logic           cpu_led,
     output  logic           cpu_resb,
     output  logic           cpu_rdy,
@@ -20,9 +20,9 @@ module super6502(
     output  logic           cpu_phi2,
     output  logic           cpu_be,
     output  logic           cpu_nmib,
-    
+
     output logic [6:0] HEX0, HEX1, HEX2, HEX3, HEX4, HEX5,
-    
+
     input   logic           UART_RXD,
     output  logic           UART_TXD,
 
@@ -42,7 +42,7 @@ module super6502(
     output             DRAM_CAS_N,
     output             DRAM_RAS_N
   );
-  
+
 logic rst;
 assign rst = ~rst_n;
 
@@ -60,6 +60,7 @@ logic [7:0] sdram_data_out;
 logic [7:0] uart_data_out;
 logic [7:0] irq_data_out;
 logic [7:0] board_io_data_out;
+logic [7:0] mm_data_out;
 
 logic sdram_cs;
 logic rom_cs;
@@ -67,6 +68,8 @@ logic hex_cs;
 logic uart_cs;
 logic irq_cs;
 logic board_io_cs;
+logic mm_cs1;
+logic mm_cs2;
 
 cpu_clk cpu_clk(
 	.inclk0(clk_50),
@@ -84,14 +87,34 @@ assign cpu_be = '1;
 assign cpu_nmib = '1;
 assign cpu_irqb = irq_data_out == 0;
 
+logic [11:0] mm_MO;
+
+logic [23:0] mm_addr;
+assign mm_addr = {mm_MO, cpu_addr[11:0]};
+
+memory_mapper memory_mapper(
+	.clk(clk),
+    .rst(rst),
+	.rw(cpu_rwb),
+	.cs(mm_cs1),
+	.MM_cs(mm_cs2),
+	.RS(cpu_addr[3:0]),
+	.MA(cpu_addr[15:12]),
+	.data_in(cpu_data_in),
+	.data_out(mm_data_out),
+	.MO(mm_MO)
+);
+
 addr_decode decode(
-    .addr(cpu_addr),
+    .addr(mm_addr),
     .sdram_cs(sdram_cs),
     .rom_cs(rom_cs),
     .hex_cs(hex_cs),
     .uart_cs(uart_cs),
     .irq_cs(irq_cs),
-    .board_io_cs(board_io_cs)
+    .board_io_cs(board_io_cs),
+	.mm_cs1(mm_cs1),
+	.mm_cs2(mm_cs2)
 );
 
 
@@ -106,6 +129,8 @@ always_comb begin
         cpu_data_out = irq_data_out;
     else if (board_io_cs)
         cpu_data_out = board_io_data_out;
+	else if (mm_cs1)
+		cpu_data_out = mm_data_out;
     else
         cpu_data_out = 'x;
 end
@@ -115,7 +140,7 @@ sdram sdram(
     .rst(rst),
     .clk_50(clk_50),
     .cpu_clk(cpu_phi2),
-    .addr(cpu_addr),
+    .addr(mm_addr),
     .sdram_cs(sdram_cs),
     .rwb(cpu_rwb),
     .data_in(cpu_data_in),
@@ -193,6 +218,6 @@ always_ff @(posedge clk_50) begin
     end
 
 end
- 
+
 endmodule
- 
\ No newline at end of file
+
diff --git a/sw/io.inc65 b/sw/io.inc65
index 61b079b434c7a745a40c3591f56cc0c16e39306a..5e87bc20afdbf83dd4da432cc3b26a9bd2dd5c02 100644
--- a/sw/io.inc65
+++ b/sw/io.inc65
@@ -8,4 +8,7 @@ UART_STATUS = UART + 1
 LED         = $7ff6
 SW          = LED
 
+MM_CTRL		= $7ff7
+MM_DATA		= $7fe0
+
 IRQ_STATUS  = $7fff
diff --git a/sw/main.c b/sw/main.c
index 1979789f98472becb8aa95d3cdaba306fd4e3390..4f894d84a807584a560860b95bd34d37102987e6 100644
--- a/sw/main.c
+++ b/sw/main.c
@@ -3,6 +3,7 @@
 
 #include "board_io.h"
 #include "uart.h"
+#include "mapper.h"
 
 int main() {
     int i;
@@ -13,6 +14,38 @@ int main() {
     clrscr();
     cprintf("Hello, world!\n");
 
+	for (i = 0; i < 16; i++){
+		cprintf("Mapping %1xxxx to %2xxxx\n", i, i);
+		mapper_write(i, i);
+	}
+
+	cprintf("Enabling Mapper\n");
+	mapper_enable(1);
+
+	cprintf("Writing 0xcccc to 0x4000\n");
+	*(unsigned int*)(0x4000) = 0xcccc;
+
+	cprintf("Writing 0xdddd to 0x5000\n");
+	*(unsigned int*)(0x5000) = 0xdddd;
+
+	cprintf("Mapping %1xxxx to %2xxxx\n", 4, 16);
+	mapper_write(16, 4);
+
+	cprintf("Mapping %1xxxx to %2xxxx\n", 5, 16);
+	mapper_write(16, 5);
+
+	cprintf("Writing 0xa5a5 to 0x4000\n");
+	*(unsigned int*)(0x4000) = 0xa5a5;
+
+	cprintf("Reading from 0x5000: %x\n", *(unsigned int*)(0x5000));
+
+	cprintf("Resetting map\n");
+	mapper_write(4, 4);
+	mapper_write(5, 5);
+
+	cprintf("Reading from 0x4000: %x\n", *(unsigned int*)(0x4000));
+	cprintf("Reading from 0x5000: %x\n", *(unsigned int*)(0x5000));
+
     while (1) {
 
         sw = sw_read();
diff --git a/sw/mapper.h b/sw/mapper.h
new file mode 100644
index 0000000000000000000000000000000000000000..8276b60060c0aa3791f3d3118c88ebf0d0a22ed3
--- /dev/null
+++ b/sw/mapper.h
@@ -0,0 +1,12 @@
+#ifndef _MAPPER_H
+#define _MAPPER_H
+
+#include <stdint.h>
+
+void mapper_enable(uint8_t en);
+
+uint8_t mapper_read(uint8_t addr);
+void mapper_write(uint8_t data, uint8_t addr);
+
+#endif
+
diff --git a/sw/mapper.s b/sw/mapper.s
new file mode 100644
index 0000000000000000000000000000000000000000..2ed1bc0d15ef785d6d611a96ee45f685c6025fca
--- /dev/null
+++ b/sw/mapper.s
@@ -0,0 +1,32 @@
+.include "io.inc65"
+
+.importzp sp, sreg
+
+.export _mapper_enable
+.export _mapper_read, _mapper_write
+
+.autoimport on
+
+.code
+
+
+; void mapper_enable(uint8_t en)
+_mapper_enable:
+	sta MM_CTRL
+	rts
+
+_mapper_read:
+	phx
+	tax
+	lda MM_DATA,x
+	ldx #$00
+	rts
+
+_mapper_write:
+	phx
+	tax
+	jsr popa
+	sta MM_DATA,x
+	plx
+	rts
+