diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8778ddf..37c6ace 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
# Changes
+[25.06.2024]
+- Add APU enhancement on top of cheat engine
+
[20.03.2024]
- Add CHANGELOG.md
- Set device in nestang_nano20k.gprj
diff --git a/doc/cheats.md b/doc/cheats.md
new file mode 100644
index 0000000..95dbf64
--- /dev/null
+++ b/doc/cheats.md
@@ -0,0 +1,35 @@
+# Cheat Wizard
+
+## Format
+
+This new module accepts `*.cwz` binary files with the following format:
+
+- 128 bits (16 bytes) per cheat
+ - Four bytes correspond to compare enabled/disabled
+ - Next 4 bytes correspond to address
+ - Next 4 bytes correspond to compare value
+ - Next 4 bytes correspond to replace value
+
+Example for `Battletoads.nes`
+
+```
+00000001 00000320 00000000 00000004
+00000001 000023a2 000000d6 00000024
+00000001 000026b5 00000001 00000000
+00000001 00004fba 00000008 0000002f
+...
+```
+
+You'll need to use a hex editor to create the file.
+
+## How to use
+
+# Enable cheats
+
+Navigate to `2)Options->Cheats->Cheats Enabled:` and press `A` to enable or disable.
+
+## Load a cheat file
+
+Navigate to `2)Options->Cheats->Load cheat file` and press `A` to open the file explorer view. Choose the `.cwz` file for your game and press `A`, you'll see the message `Cheats loaded!` if the file was correctly loaded.
+
+After enabling cheats and loading them, you can load a `ROM` and cheats will be applied.
\ No newline at end of file
diff --git a/doc/images/wishbone_b4_piplined_singleAccess.JPG b/doc/images/wishbone_b4_piplined_singleAccess.JPG
new file mode 100644
index 0000000..99352df
Binary files /dev/null and b/doc/images/wishbone_b4_piplined_singleAccess.JPG differ
diff --git a/doc/images/wishbone_b4_piplined_singleReadCycle.JPG b/doc/images/wishbone_b4_piplined_singleReadCycle.JPG
new file mode 100644
index 0000000..d53cd7c
Binary files /dev/null and b/doc/images/wishbone_b4_piplined_singleReadCycle.JPG differ
diff --git a/doc/images/wishbone_b4_piplined_singleWriteCycle.JPG b/doc/images/wishbone_b4_piplined_singleWriteCycle.JPG
new file mode 100644
index 0000000..1e33ec2
Binary files /dev/null and b/doc/images/wishbone_b4_piplined_singleWriteCycle.JPG differ
diff --git a/doc/wishbone.md b/doc/wishbone.md
new file mode 100644
index 0000000..261796a
--- /dev/null
+++ b/doc/wishbone.md
@@ -0,0 +1,30 @@
+# Pipelined Wishbone B4 Bus
+
+## About
+
+- `iosys` is wishbone master
+- No `FIFO` implemented
+- No arbiter implemented
+- 128bit wide bus for `cheat_wizard`
+
+- Slaves
+ - `wishbone_slaves.sh`
+
+## Read
+
+
+
+## Write
+
+
+
+## References
+
+- [Wishbone bus](https://en.wikipedia.org/wiki/Wishbone_(computer_bus))
+- [Wishbone B4 Specifications](https://cdn.opencores.org/downloads/wbspec_b4.pdf)
+- [zipcpu](https://zipcpu.com/)
+- [Building a simple bus](https://zipcpu.com/zipcpu/2017/05/23/simplebus.html)
+- [Building a Simple Wishbone Master](https://zipcpu.com/blog/2017/06/08/simple-wb-master.html)
+- [Building a Simple Wishbone Slave](https://zipcpu.com/zipcpu/2017/05/29/simple-wishbone.html)
+- [Building Formal Assumptions to Describe Wishbone Behaviour](https://zipcpu.com/zipcpu/2017/11/07/wb-formal.html)
+- [Building a very simple wishbone interconnect](https://zipcpu.com/blog/2017/06/22/simple-wb-interconnect.html)
\ No newline at end of file
diff --git a/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/README.txt b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/README.txt
new file mode 100644
index 0000000..519dea1
--- /dev/null
+++ b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/README.txt
@@ -0,0 +1,9 @@
+NESTang - an FPGA NES implemented with Tang Nano 20K, Primer 25K
+
+What's new in v0.11:
+
+- Add a menu item to switch between snestang and nestang cores. Just put the .bin core files in /cores directory.
+
+See installation.pdf for instructions, including how to wire the SNES/NES controllers.
+
+Visit https://github.com/nand2mario/nestang for more instructions.
\ No newline at end of file
diff --git a/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/firmware.bin b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/firmware.bin
new file mode 100644
index 0000000..ff252ff
Binary files /dev/null and b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/firmware.bin differ
diff --git a/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/installation.pdf b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/installation.pdf
new file mode 100644
index 0000000..b253bee
Binary files /dev/null and b/nestang-0.11.1_nano20k_cheats_APUenhancement_20240625/installation.pdf differ
diff --git a/nestang_nano20k.gprj b/nestang_nano20k.gprj
index efec0df..016bb09 100644
--- a/nestang_nano20k.gprj
+++ b/nestang_nano20k.gprj
@@ -9,6 +9,7 @@
+
@@ -43,6 +44,7 @@
+
@@ -61,6 +63,7 @@
+
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..062a63d
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,9 @@
+wishbone_slave/
+wishbone_slave_bound/
+wishbone_slave_prf/
+wishbone_slave_cvr/
+
+cheat_wizard/
+cheat_wizard_bound/
+cheat_wizard_prf/
+cheat_wizard_cvr/
\ No newline at end of file
diff --git a/src/apu.v b/src/apu.v
index 6c699c0..970983f 100644
--- a/src/apu.v
+++ b/src/apu.v
@@ -372,6 +372,226 @@ module TriangleChan (
endmodule
+module TriangleChan_enhanced_5b (
+ input logic clk,
+ input logic phi1,
+ input logic aclk1,
+ input logic aclk1_d,
+ input logic reset,
+ input logic cold_reset,
+ input logic allow_us,
+ input logic [1:0] Addr,
+ input logic [7:0] DIN,
+ input logic write,
+ input logic [7:0] lc_load,
+ input logic LenCtr_Clock,
+ input logic LinCtr_Clock,
+ input logic Enabled,
+ output logic [4:0] Sample, // Change output Sample to 5 bits
+ output logic IsNonZero
+);
+ logic [10:0] Period, applied_period, TimerCtr;
+ logic [4:0] SeqPos; // Change SeqPos to 5 bits
+ logic [6:0] LinCtrPeriod, LinCtrPeriod_1, LinCtr;
+ logic LinCtrl, line_reload;
+ logic LinCtrZero;
+ logic lc;
+
+ logic LenCtrZero;
+ logic subunit_write;
+ logic [4:0] sample_latch; // Change sample_latch to 5 bits
+
+ assign LinCtrZero = ~|LinCtr;
+ assign IsNonZero = lc;
+ assign subunit_write = (Addr == 0 || Addr == 3) & write;
+
+ // Adjust Sample assignment for 5-bit output
+ assign Sample = (applied_period > 1 || allow_us) ? (SeqPos[4:0] ^ {5{~SeqPos[4]}}) : sample_latch;
+
+ LenCounterUnit LenTri (
+ .clk (clk),
+ .reset (reset),
+ .cold_reset (cold_reset),
+ .aclk1 (aclk1),
+ .aclk1_d (aclk1_d),
+ .len_clk (LenCtr_Clock),
+ .load_value (lc_load),
+ .halt_in (DIN[7]),
+ .addr (Addr[0]),
+ .is_triangle (1'b1),
+ .write (subunit_write),
+ .enabled (Enabled),
+ .lc_on (lc)
+ );
+
+ always_ff @(posedge clk) begin
+ if (phi1) begin
+ if (TimerCtr == 0) begin
+ TimerCtr <= Period;
+ applied_period <= Period;
+ if (IsNonZero & ~LinCtrZero)
+ SeqPos <= SeqPos + 1'd1;
+ end else begin
+ TimerCtr <= TimerCtr - 1'd1;
+ end
+ end
+
+ if (aclk1) begin
+ LinCtrPeriod_1 <= LinCtrPeriod;
+ end
+
+ if (LinCtr_Clock) begin
+ if (line_reload)
+ LinCtr <= LinCtrPeriod_1;
+ else if (!LinCtrZero)
+ LinCtr <= LinCtr - 1'd1;
+
+ if (!LinCtrl)
+ line_reload <= 0;
+ end
+
+ if (write) begin
+ case (Addr)
+ 0: begin
+ LinCtrl <= DIN[7];
+ LinCtrPeriod <= DIN[6:0];
+ end
+ 2: begin
+ Period[7:0] <= DIN;
+ end
+ 3: begin
+ Period[10:8] <= DIN[2:0];
+ line_reload <= 1;
+ end
+ endcase
+ end
+
+ if (reset) begin
+ sample_latch <= 5'h1F; // Initialize to 5-bit value
+ Period <= 0;
+ TimerCtr <= 0;
+ SeqPos <= 0;
+ LinCtrPeriod <= 0;
+ LinCtr <= 0;
+ LinCtrl <= 0;
+ line_reload <= 0;
+ end
+
+ if (applied_period > 1) sample_latch <= Sample;
+ end
+endmodule
+
+module TriangleChan_enhanced_6b (
+ input logic clk,
+ input logic phi1,
+ input logic aclk1,
+ input logic aclk1_d,
+ input logic reset,
+ input logic cold_reset,
+ input logic allow_us,
+ input logic [1:0] Addr,
+ input logic [7:0] DIN,
+ input logic write,
+ input logic [7:0] lc_load,
+ input logic LenCtr_Clock,
+ input logic LinCtr_Clock,
+ input logic Enabled,
+ output logic [5:0] Sample,
+ output logic IsNonZero
+);
+ logic [10:0] Period, applied_period, TimerCtr;
+ logic [5:0] SeqPos;
+ logic [6:0] LinCtrPeriod, LinCtrPeriod_1, LinCtr;
+ logic LinCtrl, line_reload;
+ logic LinCtrZero;
+ logic lc;
+
+ logic LenCtrZero;
+ logic subunit_write;
+ logic [5:0] sample_latch;
+
+ assign LinCtrZero = ~|LinCtr;
+ assign IsNonZero = lc;
+ assign subunit_write = (Addr == 0 || Addr == 3) & write;
+
+ assign Sample = (applied_period > 1 || allow_us) ? (SeqPos ^ {6{~SeqPos[5]}}) : sample_latch;
+
+ LenCounterUnit LenTri (
+ .clk (clk),
+ .reset (reset),
+ .cold_reset (cold_reset),
+ .aclk1 (aclk1),
+ .aclk1_d (aclk1_d),
+ .len_clk (LenCtr_Clock),
+ .load_value (lc_load),
+ .halt_in (DIN[7]),
+ .addr (Addr[0]),
+ .is_triangle (1'b1),
+ .write (subunit_write),
+ .enabled (Enabled),
+ .lc_on (lc)
+ );
+
+ always_ff @(posedge clk) begin
+ if (phi1) begin
+ if (TimerCtr == 0) begin
+ TimerCtr <= Period;
+ applied_period <= Period;
+ if (IsNonZero & ~LinCtrZero)
+ SeqPos <= SeqPos + 1'd1;
+ end else begin
+ TimerCtr <= TimerCtr - 1'd1;
+ end
+ end
+
+ if (aclk1) begin
+ LinCtrPeriod_1 <= LinCtrPeriod;
+ end
+
+ if (LinCtr_Clock) begin
+ if (line_reload)
+ LinCtr <= LinCtrPeriod_1;
+ else if (!LinCtrZero)
+ LinCtr <= LinCtr - 1'd1;
+
+ if (!LinCtrl)
+ line_reload <= 0;
+ end
+
+ if (write) begin
+ case (Addr)
+ 0: begin
+ LinCtrl <= DIN[7];
+ LinCtrPeriod <= DIN[6:0];
+ end
+ 2: begin
+ Period[7:0] <= DIN;
+ end
+ 3: begin
+ Period[10:8] <= DIN[2:0];
+ line_reload <= 1;
+ end
+ endcase
+ end
+
+ if (reset) begin
+ sample_latch <= 6'h3F;
+ Period <= 0;
+ TimerCtr <= 0;
+ SeqPos <= 0;
+ LinCtrPeriod <= 0;
+ LinCtr <= 0;
+ LinCtrl <= 0;
+ line_reload <= 0;
+ end
+
+ if (applied_period > 1) sample_latch <= Sample;
+ end
+
+endmodule
+
+
+
module NoiseChan (
input logic clk,
input logic ce,
@@ -799,7 +1019,10 @@ module APU (
output logic [15:0] Sample,
output logic DmaReq, // 1 when DMC wants DMA
output logic [15:0] DmaAddr, // Address DMC wants to read
- output logic IRQ // IRQ asserted high == asserted
+ output logic IRQ, // IRQ asserted high == asserted
+ // Enhanced APU
+ input logic apu_enhanced_ce,
+ input logic apu_mapper_saturates
);
logic [7:0] len_counter_lut[32];
@@ -840,6 +1063,7 @@ module APU (
logic [4:0] Enabled;
logic [3:0] Sq1Sample,Sq2Sample,TriSample,NoiSample;
+ logic [4:0] TriSample_enhanced;
logic [6:0] DmcSample;
logic DmcIrq;
logic IsDmcActive;
@@ -856,7 +1080,7 @@ module APU (
assign ApuMW4 = ADDR[4:2]>=4; // DMC
assign ApuMW5 = ADDR[4:2]==5; // Control registers
- logic Sq1NonZero, Sq2NonZero, TriNonZero, NoiNonZero;
+ logic Sq1NonZero, Sq2NonZero, TriNonZero, TriNonZero_enhanced, NoiNonZero;
logic ClkE, ClkL;
logic [4:0] enabled_buffer, enabled_buffer_1;
@@ -884,8 +1108,7 @@ module APU (
assign ClkL = (frame_half & aclk1_delayed);
// Generate bus output
- assign DOUT = {DmcIrq, irq_flag, 1'b0, IsDmcActive, NoiNonZero, TriNonZero,
- Sq2NonZero, Sq1NonZero};
+ assign DOUT = {DmcIrq, irq_flag, 1'b0, IsDmcActive, NoiNonZero, TriNonZero, TriNonZero_enhanced, Sq2NonZero, Sq1NonZero};
assign IRQ = frame_irq || DmcIrq;
@@ -953,6 +1176,25 @@ module APU (
.IsNonZero (TriNonZero)
);
+ TriangleChan_enhanced_5b Tri_enhanced (
+ .clk (clk),
+ .phi1 (phi1),
+ .aclk1 (aclk1),
+ .aclk1_d (aclk1_delayed),
+ .reset (reset),
+ .cold_reset (cold_reset),
+ .allow_us (allow_us),
+ .Addr (ADDR[1:0]),
+ .DIN (DIN),
+ .write (ApuMW2 && write),
+ .lc_load (lc_load),
+ .LenCtr_Clock (ClkL),
+ .LinCtr_Clock (ClkE),
+ .Enabled (Enabled[2]),
+ .Sample (TriSample_enhanced),
+ .IsNonZero (TriNonZero_enhanced)
+ );
+
NoiseChan Noi (
.clk (clk),
.ce (ce),
@@ -998,7 +1240,11 @@ module APU (
.noise (NoiSample),
.triangle (TriSample),
.dmc (DmcSample),
- .sample (Sample)
+ .sample (Sample),
+ // Enhanced APU
+ .apu_enhanced_ce(apu_enhanced_ce),
+ .apu_triangle_enhanced(TriSample_enhanced),
+ .apu_mapper_saturates(apu_mapper_saturates)
);
FrameCtr frame_counter (
@@ -1034,7 +1280,11 @@ module APUMixer (
input logic [3:0] triangle,
input logic [3:0] noise,
input logic [6:0] dmc,
- output logic [15:0] sample
+ output logic [15:0] sample,
+ // Enhanced APU
+ input logic apu_enhanced_ce,
+ input logic [5:0] apu_triangle_enhanced,
+ input logic apu_mapper_saturates
);
logic [15:0] pulse_lut[32];
@@ -1051,6 +1301,15 @@ assign tri_lut = '{
6'h20, 6'h24, 6'h28, 6'h2C, 6'h30, 6'h34, 6'h38, 6'h3C
};
+// Enhanced APU
+logic [5:0] tri_lut_enhanced_5b[32];
+assign tri_lut_enhanced_5b = '{
+ 6'h00, 6'h01, 6'h02, 6'h04, 6'h06, 6'h08, 6'h0A, 6'h0C,
+ 6'h0E, 6'h10, 6'h12, 6'h14, 6'h16, 6'h18, 6'h1A, 6'h1C,
+ 6'h1E, 6'h20, 6'h22, 6'h24, 6'h26, 6'h28, 6'h2A, 6'h2C,
+ 6'h2E, 6'h30, 6'h32, 6'h34, 6'h36, 6'h38, 6'h3A, 6'h3C
+};
+
logic [5:0] noise_lut[16];
assign noise_lut = '{
6'h00, 6'h03, 6'h05, 6'h08, 6'h0B, 6'h0D, 6'h10, 6'h13,
@@ -1145,11 +1404,94 @@ assign mix_lut = '{
16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000
};
+logic [15:0] mix_lut_enhanced[512];
+assign mix_lut_enhanced = '{
+ 16'h0000, 16'h0128, 16'h024F, 16'h0374, 16'h0497, 16'h05B8, 16'h06D7, 16'h07F5,
+ 16'h0911, 16'h0A2B, 16'h0B44, 16'h0C5B, 16'h0D71, 16'h0E84, 16'h0F96, 16'h10A7,
+ 16'h11B6, 16'h12C3, 16'h13CF, 16'h14DA, 16'h15E2, 16'h16EA, 16'h17EF, 16'h18F4,
+ 16'h19F6, 16'h1AF8, 16'h1BF7, 16'h1CF6, 16'h1DF3, 16'h1EEE, 16'h1FE9, 16'h20E1,
+ 16'h21D9, 16'h22CF, 16'h23C3, 16'h24B7, 16'h25A9, 16'h2699, 16'h2788, 16'h2876,
+ 16'h2963, 16'h2A4F, 16'h2B39, 16'h2C22, 16'h2D09, 16'h2DF0, 16'h2ED5, 16'h2FB9,
+ 16'h309B, 16'h317D, 16'h325D, 16'h333C, 16'h341A, 16'h34F7, 16'h35D3, 16'h36AD,
+ 16'h3787, 16'h385F, 16'h3936, 16'h3A0C, 16'h3AE1, 16'h3BB5, 16'h3C87, 16'h3D59,
+ 16'h3E29, 16'h3EF9, 16'h3FC7, 16'h4095, 16'h4161, 16'h422C, 16'h42F7, 16'h43C0,
+ 16'h4488, 16'h4550, 16'h4616, 16'h46DB, 16'h47A0, 16'h4863, 16'h4925, 16'h49E7,
+ 16'h4AA7, 16'h4B67, 16'h4C25, 16'h4CE3, 16'h4DA0, 16'h4E5C, 16'h4F17, 16'h4FD1,
+ 16'h508A, 16'h5142, 16'h51F9, 16'h52B0, 16'h5365, 16'h541A, 16'h54CE, 16'h5581,
+ 16'h5633, 16'h56E5, 16'h5795, 16'h5845, 16'h58F4, 16'h59A2, 16'h5A4F, 16'h5AFC,
+ 16'h5BA7, 16'h5C52, 16'h5CFC, 16'h5DA5, 16'h5E4E, 16'h5EF6, 16'h5F9D, 16'h6043,
+ 16'h60E8, 16'h618D, 16'h6231, 16'h62D4, 16'h6377, 16'h6418, 16'h64B9, 16'h655A,
+ 16'h65F9, 16'h6698, 16'h6736, 16'h67D4, 16'h6871, 16'h690D, 16'h69A8, 16'h6A43,
+ 16'h6ADD, 16'h6B76, 16'h6C0F, 16'h6CA7, 16'h6D3E, 16'h6DD5, 16'h6E6B, 16'h6F00,
+ 16'h6F95, 16'h7029, 16'h70BD, 16'h7150, 16'h71E2, 16'h7273, 16'h7304, 16'h7395,
+ 16'h7424, 16'h74B4, 16'h7542, 16'h75D0, 16'h765D, 16'h76EA, 16'h7776, 16'h7802,
+ 16'h788D, 16'h7917, 16'h79A1, 16'h7A2A, 16'h7AB3, 16'h7B3B, 16'h7BC3, 16'h7C4A,
+ 16'h7CD0, 16'h7D56, 16'h7DDB, 16'h7E60, 16'h7EE4, 16'h7F68, 16'h7FEB, 16'h806E,
+ 16'h80F0, 16'h8172, 16'h81F3, 16'h8274, 16'h82F4, 16'h8373, 16'h83F2, 16'h8471,
+ 16'h84EF, 16'h856C, 16'h85E9, 16'h8666, 16'h86E2, 16'h875E, 16'h87D9, 16'h8853,
+ 16'h88CD, 16'h8947, 16'h89C0, 16'h8A39, 16'h8AB1, 16'h8B29, 16'h8BA0, 16'h8C17,
+ 16'h8C8E, 16'h8D03, 16'h8D79, 16'h8DEE, 16'h8E63, 16'h8ED7, 16'h8F4A, 16'h8FBE,
+ 16'h9030, 16'h90A3, 16'h9115, 16'h9186, 16'h91F7, 16'h9268, 16'h92D8, 16'h9348,
+ 16'h93B8, 16'h9427, 16'h9495, 16'h9503, 16'h9571, 16'h95DF, 16'h964C, 16'h96B8,
+ 16'h9724, 16'h9790, 16'h97FB, 16'h9866, 16'h98D1, 16'h993B, 16'h99A5, 16'h9A0E,
+ 16'h9A77, 16'h9AE0, 16'h9B48, 16'h9BB0, 16'h9C18, 16'h9C7F, 16'h9CE6, 16'h9D4C,
+ 16'h9DB2, 16'h9E18, 16'h9E7D, 16'h9EE2, 16'h9F47, 16'h9FAB, 16'hA00F, 16'hA073,
+ 16'hA0D6, 16'hA139, 16'hA19B, 16'hA1FD, 16'hA25F, 16'hA2C1, 16'hA322, 16'hA383,
+ 16'hA3E3, 16'hA443, 16'hA4A3, 16'hA502, 16'hA562, 16'hA5C0, 16'hA61F, 16'hA67D,
+ 16'hA6DB, 16'hA738, 16'hA796, 16'hA7F2, 16'hA84F, 16'hA8AB, 16'hA907, 16'hA963,
+ 16'hA9BE, 16'hAA19, 16'hAA74, 16'hAACE, 16'hAB28, 16'hAB82, 16'hABDB, 16'hAC35,
+ 16'hAC8E, 16'hACE6, 16'hAD3E, 16'hAD96, 16'hADEE, 16'hAE46, 16'hAE9D, 16'hAEF4,
+ 16'hAF4A, 16'hAFA0, 16'hAFF6, 16'hB04C, 16'hB0A2, 16'hB0A2, 16'hB0A2, 16'hB0A2,
+ 16'hB0A2, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000,
+ 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h0000
+};
+
+// Square waves
wire [4:0] squares = square1 + square2;
wire [15:0] ch1 = pulse_lut[squares];
-wire [8:0] mix = 9'(tri_lut[triangle]) + 9'(noise_lut[noise]) + 9'(dmc_lut[dmc]);
-wire [15:0] ch2 = mix_lut[mix];
-assign sample = ch1 + ch2;
+// Normal mixer
+wire [8:0] mix_normal = 9'(tri_lut[triangle]) + 9'(noise_lut[noise]) + 9'(dmc_lut[dmc]);
+wire [15:0] ch2 = mix_lut[mix_normal];
+wire [15:0] sample_normal = ch1 + ch2;
+
+// Linear mixer + enhanced triangle wave
+wire [8:0] mix_enhanced = 9'((tri_lut_enhanced_5b[apu_triangle_enhanced])) + 9'(noise_lut[noise]) + 9'(dmc_lut[dmc]);
+wire [15:0] ch2_enhanced = mix_lut_enhanced[mix_enhanced >> 1];
+wire [15:0] sample_linear = ch1 + ch2_enhanced << 1;
+
+// Normal mixer + enhanced triangle wave
+wire [15:0] ch2_mix_normal_enhanced_tri = mix_lut[mix_enhanced];
+wire [15:0] sample_mix_normal_enhanced_tri = ch1 + ch2_enhanced;
+
+assign sample = !apu_enhanced_ce ? sample_normal :
+ !apu_mapper_saturates ? sample_linear :
+ sample_mix_normal_enhanced_tri;
endmodule
diff --git a/src/cart.sv b/src/cart.sv
index 9fcda71..a7526bd 100644
--- a/src/cart.sv
+++ b/src/cart.sv
@@ -53,7 +53,9 @@ module cart_top (
output reg [1:0] diskside_auto,
input [1:0] diskside,
input fds_busy, // FDS Disk Swap Busy
- input fds_eject // FDS Disk Swap Pause
+ input fds_eject, // FDS Disk Swap Pause
+ // Enhanced APU
+ input i_enhanced_apu_ce
);
tri0 prg_allow_b, vram_a10_b, vram_ce_b, chr_allow_b, irq_b;
@@ -1302,29 +1304,29 @@ NesEvent nesev(
// Notes : External audio needs to be mixed correctly. //
// Games : Akamajou Densetsu, Esper Dream 2, Mouryou Senki Madara //
//*****************************************************************************//
-// VRC6 vrc6(
-// .clk (clk),
-// .ce (ce),
-// .enable (me[24] | me[26]),
-// .flags (flags),
-// .prg_ain (prg_ain),
-// .prg_aout_b (prg_addr_b),
-// .prg_read (prg_read),
-// .prg_write (prg_write),
-// .prg_din (prg_din),
-// .prg_dout_b (prg_dout_b),
-// .prg_allow_b(prg_allow_b),
-// .chr_ain (chr_ain),
-// .chr_aout_b (chr_addr_b),
-// .chr_read (chr_read),
-// .chr_allow_b(chr_allow_b),
-// .vram_a10_b (vram_a10_b),
-// .vram_ce_b (vram_ce_b),
-// .irq_b (irq_b),
-// .flags_out_b(flags_out_b),
-// .audio_in (vrc6_audio),
-// .audio_b (audio_out_b)
-// );
+VRC6 vrc6(
+ .clk (clk),
+ .ce (ce),
+ .enable (me[24] | me[26]),
+ .flags (flags),
+ .prg_ain (prg_ain),
+ .prg_aout_b (prg_addr_b),
+ .prg_read (prg_read),
+ .prg_write (prg_write),
+ .prg_din (prg_din),
+ .prg_dout_b (prg_dout_b),
+ .prg_allow_b(prg_allow_b),
+ .chr_ain (chr_ain),
+ .chr_aout_b (chr_addr_b),
+ .chr_read (chr_read),
+ .chr_allow_b(chr_allow_b),
+ .vram_a10_b (vram_a10_b),
+ .vram_ce_b (vram_ce_b),
+ .irq_b (irq_b),
+ .flags_out_b(flags_out_b),
+ .audio_in (vrc6_audio),
+ .audio_b (audio_out_b)
+);
//*****************************************************************************//
// Name : Konami VRC-7 //
@@ -1828,17 +1830,18 @@ wire [15:0] vrc7_audio;
// );
wire [15:0] vrc6_audio;
-// vrc6_mixed snd_vrc6 (
-// .clk(clk),
-// .ce(ce),
-// .enable(me[24] | me[26] | (me[31] && exp_audioe[0])),
-// .wren(prg_write),
-// .addr_invert(me[26]),
-// .addr_in(prg_ain),
-// .data_in(prg_din),
-// .audio_in(audio_in),
-// .audio_out(vrc6_audio)
-// );
+vrc6_mixed snd_vrc6 (
+ .clk(clk),
+ .ce(ce),
+ .enable(me[24] | me[26] | (me[31] && exp_audioe[0])),
+ .wren(prg_write),
+ .addr_invert(me[26]),
+ .addr_in(prg_ain),
+ .data_in(prg_din),
+ .audio_in(audio_in),
+ .audio_out(vrc6_audio),
+ .i_enhanced_apu_ce(i_enhanced_apu_ce)
+);
reg [6:0] prg_mask;
diff --git a/src/cheat_wizard.sby b/src/cheat_wizard.sby
new file mode 100644
index 0000000..5ccdc3d
--- /dev/null
+++ b/src/cheat_wizard.sby
@@ -0,0 +1,27 @@
+[tasks]
+bound
+prf
+cvr
+
+[options]
+bound: mode bmc
+bound: depth 128
+prf: mode prove
+prf: depth 64
+cvr: mode cover
+cvr: depth 64
+cvr: append 20
+
+
+[engines]
+bound: smtbmc
+prf: smtbmc yices
+cvr: smtbmc
+
+[script]
+read_verilog -formal cheat_wizard.v
+prep -top cheat_wizard
+
+[files]
+cheat_wizard.v
+wishbone_slaves.vh
diff --git a/src/cheat_wizard.v b/src/cheat_wizard.v
new file mode 100644
index 0000000..0247d3e
--- /dev/null
+++ b/src/cheat_wizard.v
@@ -0,0 +1,199 @@
+module cheat_wizard(
+ input wire i_clk,
+ input wire i_reset_n,
+ // Cheats
+ input wire i_cheats_enabled,
+ input wire i_cheats_loaded,
+ input wire [23:0] i_sram_address,
+ input wire [7:0] i_sram_data,
+ output wire o_cheat_stb,
+ output wire [7:0] o_sram_data,
+ // Wishbone slave
+ input wire i_wb_cyc,
+ input wire i_wb_stb,
+ input wire i_wb_we,
+ input wire i_wb_err,
+ input wire [1:0] i_wb_addr,
+ input wire [128:0] i_wb_idata,
+ output wire o_wb_ack,
+ output wire o_wb_stall,
+ output wire o_wb_err
+ );
+// Wishbone Cheat Data FSM
+reg [7:0] cheats_nr;
+reg [7:0] cheats_nr_last;
+reg cheats_compare_enabled_temp;
+reg [16:0] cheats_address_temp;
+reg [7:0] cheat_compare_value_temp;
+reg [7:0] cheats_replace_value_temp;
+reg cheats_compare_enabled[3:0];
+reg [16:0] cheats_address[3:0];
+reg [7:0] cheat_compare_value[3:0];
+reg [7:0] cheats_replace_value[3:0];
+reg [1:0] cheat_data_ix;
+
+// Cheats stuff
+wire cheat_compare_stb_0;
+wire cheat_compare_stb_1;
+wire cheat_compare_stb_2;
+wire cheat_compare_stb_3;
+wire cheats_nr_stb;
+
+assign cheats_nr_stb = (cheats_nr_last != cheats_nr);
+
+assign cheat_compare_stb_0 = (~cheats_compare_enabled[0] ? 1'b1 : (i_sram_data == cheat_compare_value[0]));
+assign cheat_compare_stb_1 = (~cheats_compare_enabled[1] ? 1'b1 : (i_sram_data == cheat_compare_value[1]));
+assign cheat_compare_stb_2 = (~cheats_compare_enabled[2] ? 1'b1 : (i_sram_data == cheat_compare_value[2]));
+assign cheat_compare_stb_3 = (~cheats_compare_enabled[3] ? 1'b1 : (i_sram_data == cheat_compare_value[3]));
+assign o_cheat_stb = ( (i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[0])&&(cheat_compare_stb_0) ) ||
+ ( (i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[1])&&(cheat_compare_stb_1) ) ||
+ ( (i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[2])&&(cheat_compare_stb_2) ) ||
+ ( (i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[3])&&(cheat_compare_stb_3) );
+assign o_sram_data = !o_cheat_stb ? i_sram_data :
+ cheat_compare_stb_0 ? cheats_replace_value[0] :
+ cheat_compare_stb_1 ? cheats_replace_value[1] :
+ cheat_compare_stb_2 ? cheats_replace_value[2] :
+ cheat_compare_stb_3 ? cheats_replace_value[3] :
+ 8'h00;
+
+
+// Wishbone stuff
+reg wb_ack;
+reg wb_err;
+reg [128:0] wb_odata;
+reg wb_stall;
+
+initial wb_ack = 0;
+initial wb_err = 0;
+initial wb_odata = 0;
+initial wb_stall= 0;
+
+always @(posedge i_clk) begin
+ if((~i_reset_n)||(i_wb_err)) begin
+ end else begin
+ // cheats_data
+ if(i_reset_n) begin
+ if((i_wb_stb)&&(i_wb_we)&&(i_wb_we)&&((i_wb_addr == 2'h01))&&(i_wb_we)&&(~o_wb_err)) begin
+ wb_odata <= i_wb_idata;
+ wb_stall <= 1'b1;
+ cheat_data_ix <= 2'b00;
+
+ cheats_compare_enabled[i_wb_idata[111:104] - 1'b1] <= i_wb_idata[96];
+ cheats_address[i_wb_idata[111:104] - 1'b1] <= i_wb_idata[79:64];
+ cheat_compare_value[i_wb_idata[111:104] - 1'b1] <= i_wb_idata[39:32];
+ cheats_replace_value[i_wb_idata[111:104] - 1'b1] <= i_wb_idata[7:0];
+
+ end else
+ wb_stall <= 1'b0;
+ end
+ end
+end
+
+assign o_wb_ack = i_wb_stb;
+assign o_wb_stall = wb_stall;
+assign o_wb_err = wb_err;
+
+//
+// Formal methods
+//
+`ifdef FORMAL
+ // f_past_valid
+ reg f_past_valid;
+ initial f_past_valid = 1'b0;
+ initial assert(!f_past_valid);
+ always @(posedge i_clk)
+ f_past_valid = 1'b1;
+
+ // BMC Assumptions
+ always @(posedge i_clk)
+ if((f_past_valid)&&((~$past(i_reset_n))||(~$past(i_wb_err)))) begin
+ assume((i_wb_addr == 2'h01)||(i_wb_addr == 2'h02)||(i_wb_addr == 2'h03));
+ assume($stable(i_wb_addr));
+ assume($stable(i_wb_idata));
+ end
+
+ always @(posedge i_clk)
+ if((f_past_valid)&&((~$past(i_reset_n))||(~$past(i_wb_err))))
+ if(i_wb_addr == 2'h01)
+ assume(i_wb_idata == 128'h00000000_00000000_00000000_00000001);
+
+ always @(posedge i_clk)
+ if((f_past_valid)&&((~$past(i_reset_n))||(~$past(i_wb_err))))
+ if(i_wb_addr == 2'h02)
+ assume(i_wb_idata == 128'h00000000_00000000_00000000_00000001);
+
+ always @(posedge i_clk)
+ if((f_past_valid)&&((~$past(i_reset_n))||(~$past(i_wb_err))))
+ if(i_wb_addr == 2'h03)
+ assume(i_wb_idata == 128'h00000001_000023A2_000000D6_00000024);
+
+ always @(posedge i_clk)
+ if((f_past_valid)&&(~$past(f_past_valid))) begin
+ assume(i_wb_stb);
+ assume(i_wb_we);
+ assume(~i_wb_err);
+ assume(~i_wb_cyc);
+ end else begin
+ assume(~i_wb_stb);
+ assume(~i_wb_we);
+ assume(~i_wb_err);
+ assume(i_wb_cyc);
+ end
+
+ always @(posedge i_clk)
+ if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))))
+ if($past(i_cheats_enabled))
+ assume(i_cheats_enabled == $past(i_cheats_enabled));
+ always @(posedge i_clk)
+ if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))))
+ if($past(i_cheats_loaded))
+ assume(i_cheats_loaded == $past(i_cheats_loaded));
+
+
+ // BMC Assertions
+ always @(posedge i_clk)
+ if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))||(~$past(i_wb_err)))) begin
+ assert(wb_err == 1'b0);
+ end
+
+ // Cover
+ always @(posedge i_clk)
+ if((f_past_valid)&&(i_reset_n))
+ if((i_wb_stb)&&(i_wb_we)&&(~i_wb_err)&&(~o_wb_stall)&&(i_wb_addr == 2'h03)&&(i_wb_idata == 128'h00000001_000023A2_000000D6_00000024))
+ if((i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == 23'h00_23A2)&&(i_sram_data == 8'hD6))
+ cover(o_cheat_stb);
+
+ // Contract
+ always @(posedge i_clk)
+ if((f_past_valid)&&($past(i_reset_n))&&(i_reset_n)&&(($past(i_wb_stb))&&($past(i_wb_cyc))&&($past(i_wb_we))&&(~$past(i_wb_err))&&($past(i_wb_addr) == 1))) begin
+ assert(wb_odata == i_wb_idata);
+ end
+
+ always @(posedge i_clk)
+ if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))||(~$past(i_wb_err))))
+ if(i_wb_stb)
+ assert(o_wb_ack);
+
+ always @(posedge i_clk)
+ if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))||(~$past(i_wb_err))))
+ assert(cheat_compare_stb_0 == (~cheats_compare_enabled[0] ? 1'b1 : (i_sram_data == cheat_compare_value[0])));
+
+ always @(posedge i_clk)
+ if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))||(~$past(i_wb_err))))
+ if((cheat_compare_stb_0)&&(~cheat_compare_stb_1)&&(~cheat_compare_stb_2)&&(~cheat_compare_stb_3))
+ assert(o_cheat_stb == ( (i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[0])&&(cheat_compare_stb_0) ));
+
+ always @(posedge i_clk)
+ if((f_past_valid)&&($past(f_past_valid))&&((~$past(i_reset_n))||(~$past(i_wb_err))))
+ if((i_cheats_enabled)&&(i_cheats_loaded)&&(i_sram_address == cheats_address[0])&&(i_sram_data == cheat_compare_value[0]))
+ if((cheat_compare_stb_0)&&(~cheat_compare_stb_1)&&(~cheat_compare_stb_2)&&(~cheat_compare_stb_3)) begin
+ assert(o_sram_data == cheats_replace_value[0]);
+ end
+
+ // Induction assumptions
+
+ // Induction assertions
+
+`endif // FORMAL
+
+endmodule
\ No newline at end of file
diff --git a/src/game_loader.v b/src/game_loader.v
index cf16bf9..94d1b85 100644
--- a/src/game_loader.v
+++ b/src/game_loader.v
@@ -21,7 +21,9 @@ module GameLoader
output reg busy,
output reg done,
output reg error,
- output reg rom_loaded
+ output reg rom_loaded,
+ // APU Enhancement
+ output [7:0] o_mapper
);
reg [7:0] prgsize;
@@ -72,6 +74,8 @@ wire [3:0] prg_nvram = (is_nes20 ? ines[10][7:4] : 4'h0);
wire piano = is_nes20 && (ines[15][5:0] == 6'h19);
wire has_saves = ines[6][1];
+assign o_mapper = mapper;
+
assign mapper_flags[63:35] = 'd0;
assign mapper_flags[34:31] = prg_nvram; //NES 2.0 Save RAM shift size (64 << size)
assign mapper_flags[30] = piano;
diff --git a/src/iosys/iosys.v b/src/iosys/iosys.v
index 5888e56..25ca94f 100644
--- a/src/iosys/iosys.v
+++ b/src/iosys/iosys.v
@@ -31,6 +31,7 @@
`define PICOSOC_V
module iosys #(
+ `include "wishbone_slaves.vh",
parameter FREQ=21_477_000,
parameter [14:0] COLOR_LOGO=15'b00000_10101_00000,
parameter [15:0] CORE_ID=1 // 1: nestang, 2: snestang
@@ -83,7 +84,35 @@ module iosys #(
input sd_dat0, // MISO
output sd_dat1, // 1
output sd_dat2, // 1
- output sd_dat3 // 0 for SPI mode
+ output sd_dat3, // 0 for SPI mode
+
+ // Enhanced APU
+ output o_reg_enhanced_apu,
+
+ // Wishbone master
+ // The return bus wires
+ input wire i_wb_ack,
+ input wire i_wb_stall,
+ input wire [31:0] i_wb_idata,
+ input wire i_wb_err,
+ // The bus control output wires
+ output wire o_wb_cyc,
+ output wire o_wb_stb,
+ output wire o_wb_we,
+ input wire o_wb_err,
+ output wire [1:0] o_wb_addr,
+ output wire [128:0] o_wb_odata,
+ output wire [3:0] o_wb_sel,
+
+ // Cheats
+ output wire o_cheats_enabled,
+ output wire o_cheats_loaded,
+ // Debug
+ output wire [1:0] o_dbg_led,
+ // System Type
+ output wire [1:0] o_sys_type,
+ // Aspect Ratio
+ output wire o_reg_aspect_ratio
);
/* verilator lint_off PINMISSING */
@@ -106,6 +135,12 @@ reg flash_wr;
wire [31:0] spiflash_reg_do;
wire spiflash_reg_wait;
+// BSRAM - Use internal BRAM
+localparam NES_BSRAM_SIZE = 32'h2000;
+reg [7:0] reg_save_bsram;
+reg [7:0] reg_load_bsram;
+reg [7:0] reg_bsram [NES_BSRAM_SIZE-1:0];
+
always @(posedge clk) begin
if (~resetn) begin
flash_loaded <= 0;
@@ -179,12 +214,47 @@ wire cycle_reg_sel = mem_valid && (mem_addr == 32'h0200_0054); // c
wire id_reg_sel = mem_valid && (mem_addr == 32'h0200_0060);
+wire id_reg_enhanced_apu_sel = mem_valid && (mem_addr == 32'h0200_0080);
+
wire spiflash_reg_byte_sel = mem_valid && (mem_addr == 32'h0200_0070);
wire spiflash_reg_word_sel = mem_valid && (mem_addr == 32'h0200_0074);
wire spiflash_reg_ctrl_sel = mem_valid && (mem_addr == 32'h0200_0078);
+wire id_reg_enhanced_apu_sel = mem_valid && (mem_addr == 32'h0200_0080);
+
+// Cheats
+wire reg_cheats_enabled_sel = mem_valid && (mem_addr == 32'h0200_00A0);
+wire reg_cheats_loaded_sel = mem_valid && (mem_addr == 32'h0200_00C0);
+wire id_reg_cheats_sel_3 = mem_valid && (mem_addr == 32'h0200_00E0); // MMSB
+wire id_reg_cheats_sel_2 = mem_valid && (mem_addr == 32'h0200_0100);
+wire id_reg_cheats_sel_1 = mem_valid && (mem_addr == 32'h0200_0120);
+wire id_reg_cheats_sel_0 = mem_valid && (mem_addr == 32'h0200_0140); // LLSB
+wire id_reg_cheats_data_ready_sel = mem_valid && (mem_addr == 32'h0200_0160);
+
+// BSRAM
+wire id_reg_save_bsram = mem_valid && (mem_addr == 32'h0200_0180);
+wire id_reg_load_bsram = mem_valid && (mem_addr == 32'h0200_01A0);
+
+// System Type
+wire id_reg_sys_type = mem_valid && (mem_addr == 32'h0200_01C0);
+
+// Aspect Ratio
+wire id_reg_aspect_ratio = mem_valid && (mem_addr == 32'h0200_01E0);
+
+// Timer Interrupts
+wire id_reg_timer_interrupts = mem_valid && (mem_addr == 32'h0200_0200);
+// Timer0 Load Register
+wire id_reg_timer0_load_value = mem_valid && (mem_addr == 32'h0200_0220);
assign mem_ready = ram_ready || textdisp_reg_char_sel || simpleuart_reg_div_sel ||
- romload_reg_ctrl_sel || romload_reg_data_sel || joystick_reg_sel || time_reg_sel || cycle_reg_sel || id_reg_sel ||
+ romload_reg_ctrl_sel || romload_reg_data_sel || joystick_reg_sel || time_reg_sel || id_reg_sel || cycle_reg_sel || id_reg_sel ||
+ id_reg_enhanced_apu_sel ||
+ reg_cheats_enabled_sel || reg_cheats_loaded_sel || id_reg_cheats_data_ready_sel ||
+ id_reg_save_bsram || id_reg_load_bsram ||
+ id_reg_sys_type ||
+ id_reg_aspect_ratio ||
+ id_reg_timer_interrupts ||
+ id_reg_timer0_load_value ||
+ id_reg_cheats_sel_0 || id_reg_cheats_sel_1 || id_reg_cheats_sel_2 || id_reg_cheats_sel_3 ||
(simpleuart_reg_dat_sel && !simpleuart_reg_dat_wait) ||
((simplespimaster_reg_byte_sel || simplespimaster_reg_word_sel) && !simplespimaster_reg_wait) ||
(spiflash_reg_byte_sel || spiflash_reg_word_sel) && !spiflash_reg_wait ||
@@ -197,6 +267,20 @@ assign mem_rdata = ram_ready ? ram_rdata :
time_reg_sel ? time_reg :
cycle_reg_sel ? cycle_reg :
id_reg_sel ? {16'b0, CORE_ID} :
+ id_reg_enhanced_apu_sel ? reg_enhanced_apu :
+ reg_cheats_enabled_sel ? {31'h0, reg_cheats_enabled} :
+ reg_cheats_loaded_sel ? {31'h0, reg_cheats_loaded} :
+ id_reg_cheats_data_ready_sel ? {31'h0, reg_cheats_data_ready} :
+ id_reg_save_bsram ? {31'h0, reg_save_bsram} :
+ id_reg_load_bsram ? {31'h0, reg_load_bsram} :
+ id_reg_sys_type ? {30'b00_0000_0000_0000, reg_sys_type} :
+ id_reg_aspect_ratio ? {31'b000_0000_0000_0000, reg_aspect_ratio} :
+ id_reg_timer_interrupts ? {reg_timer_interrupts} :
+ id_reg_timer0_load_value ? {reg_timer0_load_value} :
+ id_reg_cheats_sel_3 ? reg_cheats[128:96] :
+ id_reg_cheats_sel_2 ? reg_cheats[95:64] :
+ id_reg_cheats_sel_1 ? reg_cheats[63:32] :
+ id_reg_cheats_sel_0 ? reg_cheats[31:0] :
(simplespimaster_reg_byte_sel | simplespimaster_reg_word_sel) ? simplespimaster_reg_do :
(spiflash_reg_byte_sel | spiflash_reg_word_sel) ? spiflash_reg_do :
32'h 0000_0000;
@@ -344,8 +428,213 @@ always @(posedge clk) begin
end
end
+// Enhanced RAM register
+reg reg_enhanced_apu;
+always @(posedge clk) begin
+ if(~resetn) begin
+ reg_enhanced_apu <= 0;
+ end
+ if(mem_addr == 32'h0200_0080) begin
+ reg_enhanced_apu <= mem_wdata;
+ end
+end
+
+assign o_reg_enhanced_apu = reg_enhanced_apu;
+
// assign led = ~{2'b0, (^ total_refresh[7:0]), s0, flash_cnt[12]}; // flash while loading
+// Enhanced RAM register
+reg reg_enhanced_apu;
+always @(posedge clk) begin
+ if(~resetn) begin
+ reg_enhanced_apu <= 0;
+ end
+ if(mem_addr == 32'h0200_0080) begin
+ reg_enhanced_apu <= mem_wdata;
+ end
+end
+
+assign o_reg_enhanced_apu = reg_enhanced_apu;
+
+//
+// Cheat engine
+//
+reg reg_cheats_enabled;
+reg reg_cheats_loaded_last;
+reg reg_cheats_loaded;
+reg reg_cheats_enabled_last;
+reg reg_cheats_data_ready;
+reg reg_cheats_data_ready_last;
+reg [128:0] reg_cheats;
+reg [128:0] reg_cheats_last;
+reg reg_cheats_data_sent;
+
+wire reg_cheats_enabled_stb;
+wire reg_cheats_loaded_stb;
+wire reg_cheats_data_ready_stb;
+wire reg_cheats_stb;
+
+initial reg_cheats_enabled = 1'b0;
+initial reg_cheats_enabled_last = 1'b0;
+initial reg_cheats_loaded = 1'b0;
+initial reg_cheats_loaded_last = 1'b0;
+initial reg_cheats_data_ready = 1'b0;
+initial reg_cheats_data_ready_last = 1'b0;
+initial reg_cheats = 0;
+initial reg_cheats_last = 0;
+
+assign reg_cheats_enabled_stb = (reg_cheats_enabled_last != reg_cheats_enabled);
+assign reg_cheats_loaded_stb = (reg_cheats_loaded_last != reg_cheats_loaded);
+assign reg_cheats_data_ready_stb = (reg_cheats_data_ready_last != reg_cheats_data_ready);
+assign reg_cheats_stb = (reg_cheats_last != reg_cheats);
+
+always @(posedge clk) begin
+ if(~resetn)
+ reg_cheats <= 0;
+ else begin
+ if(mem_addr == 32'h0200_00A0)
+ reg_cheats_enabled <= mem_wdata[0];
+ if(mem_addr == 32'h0200_00C0)
+ reg_cheats_loaded <= mem_wdata[0];
+ if(mem_addr == 32'h0200_00E0)
+ reg_cheats[127:96] <= mem_wdata;
+ if(mem_addr == 32'h0200_0100)
+ reg_cheats[95:64] <= mem_wdata;
+ if(mem_addr == 32'h0200_0120)
+ reg_cheats[63:32] <= mem_wdata;
+ if(mem_addr == 32'h0200_0140)
+ reg_cheats[31:0] <= mem_wdata;
+ if(mem_addr == 32'h0200_0160)
+ reg_cheats_data_ready <= mem_wdata[0];
+
+
+ if(reg_cheats_enabled_stb)
+ reg_cheats_enabled_last <= reg_cheats_enabled;
+ if(reg_cheats_loaded_stb)
+ reg_cheats_loaded_last <= reg_cheats_loaded;
+ if(reg_cheats_stb)
+ reg_cheats_last <= reg_cheats;
+ if(reg_cheats_loaded_stb)
+ reg_cheats_data_ready_last <= reg_cheats_data_ready;
+ else if((reg_cheats_data_ready)&&(~wb_cyc))
+ reg_cheats_data_ready <= 1'b0;
+ end
+end
+
+assign o_cheats_enabled = reg_cheats_enabled;
+assign o_cheats_loaded = reg_cheats_loaded;
+
+//
+// Wishbone master
+//
+reg [1:0] wb_addr;
+reg [128:0] wb_odata;
+reg wb_we;
+reg wb_cyc;
+reg wb_stb;
+reg wb_err;
+
+initial wb_err = 1'b0;
+initial wb_cyc = 1'b0;
+initial wb_stb = 1'b0;
+
+
+always @(posedge clk) begin
+ if((~resetn)||(i_wb_err)) begin
+ wb_cyc <= 1'b0;
+ wb_stb <= 1'b0;
+ if(~resetn) begin
+ // ToDo: reset stuff
+ end
+ end else begin
+ // ToDo: Do bus stuff
+ // cheats_data
+ if((reg_cheats_enabled)&&(reg_cheats_data_ready)&&(~i_wb_stall)&&(~wb_cyc)) begin
+ // wb_addr <= WISHBONE_SLAVE_ADDRESS_CHEATS_DATA;
+ wb_addr <= 2'h01;
+ wb_odata <= reg_cheats;
+ wb_we <= 1'b1;
+ wb_cyc <= 1'b1;
+ wb_stb <= 1'b1;
+ end
+ if(wb_cyc) begin
+ wb_addr <= 0;
+ wb_we <= 1'b0;
+ wb_stb <= 1'b0;
+ if(i_wb_ack)
+ wb_cyc <= 1'b0;
+ end
+ end
+end
+
+assign o_wb_addr = wb_addr;
+assign o_wb_odata = wb_odata;
+assign o_wb_we = wb_we;
+assign o_wb_stb = wb_stb;
+assign o_wb_cyc = wb_cyc;
+
+// System Type
+reg [1:0] reg_sys_type;
+initial reg_sys_type = 2'b00; // NTSC/Dendy
+always @(posedge clk) begin
+ if(~resetn)
+ reg_sys_type <= 2'b00;
+ else begin
+ if(mem_addr == 32'h0200_01C0)
+ reg_sys_type <= mem_wdata[1:0];
+ end
+end
+
+assign o_sys_type = reg_sys_type;
+
+// Aspect Ratio
+reg reg_aspect_ratio;
+initial reg_aspect_ratio = 1'b0; // 1:1
+always @(posedge clk) begin
+ if(~resetn)
+ reg_aspect_ratio <= 1'b0;
+ else begin
+ if(mem_addr == 32'h0200_01E0)
+ reg_aspect_ratio <= mem_wdata[0];
+ end
+end
+
+assign o_reg_aspect_ratio = reg_aspect_ratio;
+
+// Timer Interrupts
+reg [31:0] reg_timer_interrupts;
+initial reg_timer_interrupts <= 32'h0;
+always @(posedge clk)
+ if(~resetn)
+ reg_timer_interrupts <= 32'h0;
+ else begin
+ if(mem_addr == 32'h0200_0200)
+ reg_timer_interrupts <= mem_wdata[31:0];
+ if(timer0_counter == reg_timer0_load_value)
+ reg_timer_interrupts <= reg_timer_interrupts & 32'h01;
+ end
+
+// Timer0
+reg [31:0] timer0_counter;
+reg [31:0] reg_timer0_load_value;
+initial timer0_counter = 32'h0;
+initial reg_timer0_load_value = 32'h0;
+// Write register
+always @(posedge clk)
+ if(~resetn)
+ reg_timer0_load_value <= 32'h0;
+ else
+ if(mem_addr == 32'h0200_0220)
+ reg_timer0_load_value <= mem_wdata[31:0];
+// Counter
+always @(posedge clk)
+ if(~resetn)
+ timer0_counter <= 32'h0;
+ else if(timer0_counter < reg_timer0_load_value)
+ timer0_counter <= timer0_counter + 1;
+ else
+ timer0_counter <= 32'h0;
+
endmodule
module picosoc_regs (
diff --git a/src/mappers/VRC.sv b/src/mappers/VRC.sv
index 64d987d..d9e9964 100644
--- a/src/mappers/VRC.sv
+++ b/src/mappers/VRC.sv
@@ -605,7 +605,9 @@ module MAPVRC6( //signal descriptions in powerpak.v
input ce,// add
//output [15:0] audio,
- input mapper26
+ input mapper26,
+ // Enhanced APU
+ input i_enhanced_apu_ce
);
//wire [15:0] ain=prgain; //MAP18
@@ -842,7 +844,8 @@ module vrc6_mixed (
input [15:0] addr_in,
input [7:0] data_in,
input [15:0] audio_in, // Inverted audio from APU
- output [15:0] audio_out
+ output [15:0] audio_out,
+ input i_enhanced_apu_ce
);
vrc6sound snd_vrc6 (
@@ -855,7 +858,11 @@ vrc6sound snd_vrc6 (
.din(data_in),
.outSq1(vrc6sq1_out),
.outSq2(vrc6sq2_out),
- .outSaw(vrc6saw_out)
+ .outSaw(vrc6saw_out),
+ // Enhanced APU
+ .outSq1_enhanced(vrc6sq1_out_enhanced),
+ .outSq2_enhanced(vrc6sq2_out_enhanced),
+ .outSaw_enhanced(vrc6saw_out_enhanced)
);
//sound
@@ -864,10 +871,15 @@ vrc6sound snd_vrc6 (
wire [3:0] vrc6sq1_out;
wire [3:0] vrc6sq2_out;
wire [4:0] vrc6saw_out;
+ // Enhanced APU
+ wire [3:0] vrc6sq1_out_enhanced;
+ wire [3:0] vrc6sq2_out_enhanced;
+ wire [7:0] vrc6saw_out_enhanced;
// VRC6 sound is mixed before amplification, and them amplified linearly
wire [5:0] exp_audio = vrc6sq1_out + vrc6sq2_out + vrc6saw_out;
- wire [15:0] audio = {exp_audio, exp_audio, exp_audio[5:2]};
+ wire [7:0] exp_audio_enhanced = vrc6sq1_out_enhanced + vrc6sq2_out_enhanced + vrc6saw_out_enhanced >> 2;
+ wire [15:0] audio = (i_enhanced_apu_ce ? {exp_audio_enhanced, exp_audio_enhanced, exp_audio_enhanced[5:2]} : {exp_audio, exp_audio, exp_audio[5:2]});
// VRC6 audio is much louder than APU audio, so match the levels we have to reduce it
// to about 43% to match the audio ratio of the original Famicom with AD3. Note that the
@@ -886,9 +898,12 @@ module vrc6sound(
input addr_invert,
input [15:0] addr_in,
input [7:0] din,
- output [3:0] outSq1, //range=0..0x0F
- output [3:0] outSq2, //range=0..0x0F
- output [4:0] outSaw //range=0..0x1F
+ output [3:0] outSq1, //range=0..0x0F
+ output [3:0] outSq2, //range=0..0x0F
+ output [4:0] outSaw, //range=0..0x1F
+ output [7:0] outSq1_enhanced, //range=0..0xFF
+ output [7:0] outSq2_enhanced, //range=0..0xFF
+ output [7:0] outSaw_enhanced //range=0..0xFF
);
wire [15:0] ain=addr_invert ? {addr_in[15:2],addr_in[0],addr_in[1]} : addr_in; //MAP1A : MAP18
@@ -905,6 +920,10 @@ reg en0, en1, en2;
reg [3:0] duty0cnt, duty1cnt;
reg [2:0] duty2cnt;
reg [7:0] acc;
+reg [7:0] acc_enhanced;
+
+reg [7:0] sq1_acc_enhanced;
+reg [7:0] sq2_acc_enhanced;
always@(posedge clk) begin
if(~enable) begin
@@ -960,6 +979,34 @@ always@(posedge clk) begin
end
end
+always @(posedge clk) begin
+ if (enable && ce) begin
+ // Calculate enhanced square waveforms
+ if (sq1_acc_enhanced == 0) begin
+ sq1_acc_enhanced <= 255;
+ end else begin
+ sq1_acc_enhanced <= sq1_acc_enhanced - 1;
+ end
+
+ if (sq2_acc_enhanced == 0) begin
+ sq2_acc_enhanced <= 255;
+ end else begin
+ sq2_acc_enhanced <= sq2_acc_enhanced - 1;
+ end
+ end
+end
+
+always @(posedge clk) begin
+ if (enable && ce) begin
+ // Calculate enhanced sawtooth waveform
+ if (acc_enhanced == 0) begin
+ acc_enhanced <= 255;
+ end else begin
+ acc_enhanced <= acc_enhanced - 1;
+ end
+ end
+end
+
wire [4:0] duty0pos=duty0cnt+{1'b1,~duty0};
wire [4:0] duty1pos=duty1cnt+{1'b1,~duty1};
wire [3:0] ch0=((~duty0pos[4]|mode0)&en0)?vol0:4'd0;
@@ -970,5 +1017,9 @@ assign outSq1=ch0;
assign outSq2=ch1;
assign outSaw=ch2;
+assign outSq1_enhanced = sq1_acc_enhanced;
+assign outSq2_enhanced = sq2_acc_enhanced;
+assign outSaw_enhanced = acc_enhanced >> 2;
+
endmodule
diff --git a/src/nes.v b/src/nes.v
index 27a2d53..0f828e6 100644
--- a/src/nes.v
+++ b/src/nes.v
@@ -118,7 +118,10 @@ module NES(
output gg_avail,
input gg_reset,
output [2:0] emphasis,
- output save_written
+ output save_written,
+ // Enhanced APU
+ input i_APU_enhancements_ce,
+ input i_APU_mapper_saturates
);
@@ -331,6 +334,7 @@ DmaController dma(
wire apu_cs = addr >= 'h4000 && addr < 'h4018;
wire [7:0] apu_dout;
wire [15:0] sample_apu;
+wire NES_APU_enhancements_ce;
APU apu(
.MMC5 (1'b0),
@@ -353,7 +357,9 @@ APU apu(
.DmaData (from_data_bus),
.odd_or_even (odd_or_even),
.IRQ (apu_irq),
- .allow_us(1'b0)
+ .allow_us(1'b0),
+ .apu_enhanced_ce(i_APU_enhancements_ce),
+ .apu_mapper_saturates(i_APU_mapper_saturates)
);
assign sample = sample_a;
@@ -494,7 +500,9 @@ cart_top multi_mapper (
.fds_eject (fds_eject), // Used to trigger FDS disk changes
.fds_busy (fds_busy), // Used to trigger FDS disk changes
.diskside_auto (diskside_req),
- .diskside (diskside)
+ .diskside (diskside),
+ // Enhanced APU
+ .i_enhanced_apu_ce(NES_APU_enhancements_ce)
);
wire genie_ovr;
diff --git a/src/nes2hdmi.sv b/src/nes2hdmi.sv
index 2a2c4df..634a24e 100644
--- a/src/nes2hdmi.sv
+++ b/src/nes2hdmi.sv
@@ -12,7 +12,9 @@ module nes2hdmi (
input [8:0] cycle,
input [8:0] scanline,
input [15:0] sample,
- input aspect_8x7, // 1: 8x7 pixel aspect ratio mode
+
+ // Aspect Ratio
+ input wire i_reg_aspect_ratio,
// overlay interface
input overlay,
@@ -73,9 +75,6 @@ localparam BAUDRATE=115200;
`define EMBED_GAME
`endif
-// flags
-logic asp8x7_on = 1'b1;
-
// video stuff
wire [9:0] cy, frameHeight;
wire [10:0] cx, frameWidth;
@@ -212,7 +211,7 @@ end
// - For 8:7, total width is 36*24 + 13 = 877. Therefore x goes from 201 to 1077.
reg r2_active;
reg [4:0] xs, r_xs, r2_xs; // x step for each 7 NES pixel group, 0-23 for 8:7 pixel aspect ratio, or 0-20 for 1:1 pixel aspect ratio
-wire xload = asp8x7_on ?
+wire xload = i_reg_aspect_ratio ?
(xs == 5'd0 || xs == 5'd3 || xs == 5'd6 || xs == 5'd10 || xs == 5'd13 || xs == 5'd17 || xs == 5'd20)
: (xs == 5'd0 || xs == 5'd3 || xs == 5'd6 || xs == 5'd9 || xs == 5'd12 || xs == 5'd15 || xs == 5'd18);
reg r_xload;
@@ -224,7 +223,7 @@ reg [23:0] NES_PALETTE [0:63];
// Mix ratio of border pixels for 8x7 pixel aspect ratio
reg [15:0] mixratio;
reg mix;
-wire [15:0] next_mixratio = ~asp8x7_on ? 16'b0 : // no mixing for 1:1 pixel aspect ratio
+wire [15:0] next_mixratio = ~i_reg_aspect_ratio ? 16'b0 : // no mixing for 1:1 pixel aspect ratio
r_xs == 5'd3 ? {8'd110,8'd146} :
r_xs == 5'd6 ? {8'd219,8'd37} :
r_xs == 5'd10 ? {8'd73,8'd183} :
@@ -242,9 +241,9 @@ reg overlay_active;
// calc rgb value to hdmi
always_ff @(posedge clk_pixel) begin
reg [23:0] rgb_nes;
- if (asp8x7_on && cx == 11'd198 || ~asp8x7_on && cx == 11'd253)
+ if (i_reg_aspect_ratio && cx == 11'd198 || ~i_reg_aspect_ratio && cx == 11'd253)
active <= 1'b1;
- if (asp8x7_on && cx == 11'd1075 || ~asp8x7_on && cx == 11'd1021)
+ if (i_reg_aspect_ratio && cx == 11'd1075 || ~i_reg_aspect_ratio && cx == 11'd1021)
active <= 1'b0;
if (cx == 11'd256 && cy >= 10'd24 && cy < 10'd696)
overlay_active <= 1;
@@ -257,7 +256,7 @@ always_ff @(posedge clk_pixel) begin
r_active <= active; r2_active <= r_active;
r_xs <= xs; r2_xs <= r_xs;
if (active) begin
- if (asp8x7_on)
+ if (i_reg_aspect_ratio)
xs <= xs == 5'd23 ? 0 : xs + 1;
else
xs <= xs == 5'd20 ? 0 : xs + 1;
@@ -274,7 +273,7 @@ always_ff @(posedge clk_pixel) begin
// 2 - mix rgb and output
if (r2_active) begin
- if (asp8x7_on && mix)
+ if (i_reg_aspect_ratio && mix)
rgb_nes = {rmix[15:8], gmix[15:8], bmix[15:8]};
else
rgb_nes = rgbv;
diff --git a/src/nestang_top.sv b/src/nestang_top.sv
index 7f6773c..5a0e446 100644
--- a/src/nestang_top.sv
+++ b/src/nestang_top.sv
@@ -88,7 +88,7 @@ module nestang_top (
// Core settings
wire arm_reset = 0;
-wire [1:0] system_type = 2'b0;
+wire [1:0] system_type;
wire pal_video = 0;
wire [1:0] scanlines = 2'b0;
wire joy_swap = 0;
@@ -200,7 +200,7 @@ always @(posedge clk) begin
reset_cnt <= reset_cnt == 0 ? 0 : reset_cnt - 1;
if (reset_cnt == 0)
// if (reset_cnt == 0 && s1) // for nano
- sys_resetn <= ~(joy1_btns[5] && joy1_btns[2]); // 8BitDo Home button = Select + Down
+ sys_resetn <= ~(joy1_btns[5] && joy1_btns[3]); // Select + Up
end
`ifndef VERILATOR
@@ -237,8 +237,14 @@ assign fclk = sys_clk;
wire [31:0] status;
-
// Main NES machine
+wire [7:0] NES_memory_din_cpu;
+wire [7:0] NES_cheats_otuput_data_test;
+wire NES_top_cheats_stb;
+
+assign NES_top_cheats_stb= (NES_cheats_enabled)&&(NES_cheats_loaded)&&(NES_cheats_stb);
+assign NES_memory_din_cpu = (!NES_top_cheats_stb ? memory_din_cpu : NES_cheats_otuput_data);
+
NES nes(
.clk(clk), .reset_nes(reset_nes), .cold_reset(1'b0),
.sys_type(system_type), .nes_div(nes_ce),
@@ -252,7 +258,7 @@ NES nes(
.cpumem_addr(memory_addr_cpu),
.cpumem_read(memory_read_cpu),
- .cpumem_din(memory_din_cpu),
+ .cpumem_din(NES_cheats_otuput_data),
.cpumem_write(memory_write_cpu),
.cpumem_dout(memory_dout_cpu),
.ppumem_addr(memory_addr_ppu),
@@ -267,7 +273,10 @@ NES nes(
.int_audio(int_audio), // VRC6
.ext_audio(ext_audio),
- .apu_ce(), .gg(), .gg_code(), .gg_avail(), .gg_reset(), .emphasis(), .save_written()
+ .apu_ce(), .gg(), .gg_code(), .gg_avail(), .gg_reset(), .emphasis(), .save_written(),
+ // Enhanced APU
+ .i_APU_enhancements_ce(NES_enhanced_APU),
+ .i_APU_mapper_saturates((NES_mapper == 8'h04)||(NES_mapper == 8'h45)) // Mapper4/MMC3 and Mapper69 saturate so far
);
// loader_write -> clock when data available
@@ -322,7 +331,9 @@ GameLoader loader(
.mem_addr(loader_addr), .mem_data(loader_write_data), .mem_write(loader_write),
.bios_download(),
.mapper_flags(loader_flags), .busy(loader_busy), .done(loader_done),
- .error(loader_fail), .rom_loaded()
+ .error(loader_fail), .rom_loaded(),
+ // APU Enhancement
+ .o_mapper(NES_mapper)
);
assign int_audio = 1;
@@ -363,6 +374,7 @@ nes2hdmi u_hdmi ( // purple: RGB=440064 (010001000_00000000_01100100), BGR5=
.clk(clk), .resetn(sys_resetn),
.color(color), .cycle(cycle),
.scanline(scanline), .sample(sample >> 1),
+ .i_reg_aspect_ratio(NES_aspect_ratio),
.overlay(overlay), .overlay_x(overlay_x), .overlay_y(overlay_y),
.overlay_color(overlay_color),
.clk_pixel(hclk), .clk_5x_pixel(hclk5),
@@ -452,7 +464,8 @@ always @(posedge clk) begin // RV
endcase
end
end
-
+reg NES_enhanced_APU;
+reg [7:0] NES_mapper;
iosys #(.COLOR_LOGO(15'b01100_00000_01000), .CORE_ID(1) ) // purple nestang logo
iosys (
.clk(clk), .hclk(hclk), .resetn(sys_resetn),
@@ -474,7 +487,33 @@ iosys #(.COLOR_LOGO(15'b01100_00000_01000), .CORE_ID(1) ) // purple nestang
.uart_tx(UART_TXD), .uart_rx(UART_RXD),
.sd_clk(sd_clk), .sd_cmd(sd_cmd), .sd_dat0(sd_dat0), .sd_dat1(sd_dat1),
- .sd_dat2(sd_dat2), .sd_dat3(sd_dat3)
+ .sd_dat2(sd_dat2), .sd_dat3(sd_dat3),
+ .o_reg_enhanced_apu(NES_enhanced_APU),
+ // Wishbone master
+ .i_wb_ack(NES_wb_slave_ack),
+ .i_wb_stall(NES_wb_slave_stall),
+ .i_wb_idata(NES_wb_slave_data),
+ .i_wb_err(NES_wb_slave_err),
+ .o_wb_cyc(NES_wb_master_cyc),
+ .o_wb_stb(NES_wb_master_stb),
+ .o_wb_we(NES_wb_master_we),
+ .o_wb_err(NES_wb_master_err),
+ .o_wb_addr(NES_wb_slave_addr),
+ .o_wb_odata(NES_wb_master_data),
+ .o_wb_sel(NES_wb_master_sel),
+
+ // Cheats
+ .o_cheats_enabled(NES_cheats_enabled),
+ .o_cheats_loaded(NES_cheats_loaded),
+
+ // Debug LED
+ .o_dbg_led(),
+
+ // System Type
+ .o_sys_type(system_type),
+
+ // Aspect Ratio
+ .o_reg_aspect_ratio(NES_aspect_ratio)
);
// Controller input
@@ -554,10 +593,63 @@ assign joypad2_data[0] = joypad_bits2[0];
//assign led = ~{~UART_RXD, loader_done};
//assign led = ~{~UART_RXD, usb_conerr, loader_done};
-assign led = {joy1_btns[1], joy1_btns[0]};
+// assign led = {joy1_btns[1], joy1_btns[0]};
reg [23:0] led_cnt;
always @(posedge clk) led_cnt <= led_cnt + 1;
//assign led = {led_cnt[23], led_cnt[22]};
+//
+// Wishbone Bus
+//
+wire NES_wb_master_cyc;
+wire NES_wb_master_stb;
+wire NES_wb_master_we;
+wire NES_wb_master_err;
+wire [1:0] NES_wb_slave_addr;
+wire [128:0] NES_wb_master_data;
+wire NES_wb_slave_ack;
+wire NES_wb_slave_stall;
+wire NES_wb_slave_err;
+wire [128:0] NES_wb_slave_data;
+wire [2:0] NES_wb_slave_sel;
+
+// Cheats
+wire [7:0] NES_cheats_otuput_data;
+wire NES_cheats_stb;
+wire [23:0] NES_cheats_memory_addr_cpu;
+wire NES_cheats_enabled;
+wire NES_cheats_loaded;
+
+assign NES_cheats_memory_addr_cpu = {2'b00, memory_addr_cpu};
+// assign led[0] = ~NES_cheats_enabled;
+// assign led[1] = ~NES_cheats_loaded;
+
+cheat_wizard(
+ .i_clk(clk),
+ .i_reset_n(sys_resetn),
+ .i_cheats_enabled(NES_cheats_enabled),
+ .i_cheats_loaded(NES_cheats_loaded),
+ .i_sram_address(NES_cheats_memory_addr_cpu),
+ .i_sram_data(memory_din_cpu),
+ .o_cheat_stb(NES_cheats_stb),
+ .o_sram_data(NES_cheats_otuput_data),
+ .i_wb_cyc(NES_wb_master_cyc),
+ .i_wb_stb(NES_wb_master_stb),
+ .i_wb_we(NES_wb_master_we),
+ .i_wb_err(NES_wb_master_err),
+ .i_wb_addr(NES_wb_slave_addr),
+ .i_wb_idata(NES_wb_master_data),
+ .o_wb_ack(NES_wb_slave_ack),
+ .o_wb_stall(NES_wb_slave_stall),
+ .o_wb_err(NES_wb_slave_err)
+);
+
+// Aspect Ratio
+reg NES_aspect_ratio;
+initial NES_aspect_ratio = 1'b0;
+
+// assign led[0] = ~NES_cheats_enabled;
+// assign led[1] = ~NES_cheats_loaded;
+
endmodule
\ No newline at end of file
diff --git a/src/wishbone_slave.sby b/src/wishbone_slave.sby
new file mode 100644
index 0000000..0437ce4
--- /dev/null
+++ b/src/wishbone_slave.sby
@@ -0,0 +1,27 @@
+[tasks]
+bound
+prf
+cvr
+
+[options]
+bound: mode bmc
+bound: depth 128
+prf: mode prove
+prf: depth 64
+cvr: mode cover
+cvr: depth 250
+cvr: append 20
+
+
+[engines]
+bound: smtbmc
+prf: smtbmc yices
+cvr: smtbmc
+
+[script]
+read_verilog -formal wishbone_slave.v
+prep -top wishbone_slave
+
+[files]
+wishbone_slave.v
+wishbone_slaves.vh
diff --git a/src/wishbone_slaves.vh b/src/wishbone_slaves.vh
new file mode 100644
index 0000000..73c3a99
--- /dev/null
+++ b/src/wishbone_slaves.vh
@@ -0,0 +1,6 @@
+`ifndef _wishbone_slaves_vh_
+`define _wishbone_slaves_vh_
+
+parameter WISHBONE_SLAVE_ADDRESS_CHEATS_DATA = 1
+
+`endif
\ No newline at end of file