diff --git a/README.md b/README.md
index 45a8fe3..3a0f82f 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
# The Graphics Gremlin - a Retro ISA Video Card
-The Graphics Gremlin is an FPGA-based ISA video card specifically designed to emulate certain old video standards. This initial release emulates the original IBM PC monochrome graphics adapter (MDA) as well as the original IBM color graphics adapter (CGA). Since the logic is defined by the bitstream loaded into the FPGA, new emulations may be available in the future to support other video standards.
+The Graphics Gremlin is an FPGA-based ISA video card specifically designed to emulate certain old video standards. This initial release emulates the original IBM PC monochrome graphics adapter (MDA) and the Hercules Graphics Mode (HGC), as well as the original IBM color graphics adapter (CGA). Since the logic is defined by the bitstream loaded into the FPGA, new emulations may be available in the future to support other video standards.

-But why emulate an old video card when they are still fairly easy to find online? Cards aren't hard to find, but monitors that can sync to the unusual frequencies used by MDA (18KHz) and CGA (15KHz) are much harder to find, and these frequencies are rarely supported by modern LCD monitors or video capture hardware.
+But why emulate an old video card when they are still fairly easy to find online? Cards aren't hard to find, but monitors that can sync to the unusual frequencies used by MDA/HGC (18KHz) and CGA (15KHz) are much harder to find, and these frequencies are rarely supported by modern LCD monitors or video capture hardware.
-For both MDA and CGA, the Graphics Gremlin has a VGA port that can deliver video running at standard (31KHz) frequencies that are well supported by LCD monitors, VGA-to-HDMI converters, and USB capture devices.
+For both MDA/HGC and CGA, the Graphics Gremlin has a VGA port that can deliver video running at standard (31KHz) frequencies that are well supported by LCD monitors, VGA-to-HDMI converters, and USB capture devices.
Here are the design files. The BOM includes Mouser Electronics parts numbers for everything except for the 0.1" headers which are typically cut to length anyway.
@@ -91,15 +91,17 @@ The bitstream is selected using switches 3 and 4:
| open | open | Bitstream 0 | MDA (VGA compatible signal) |
| open | closed | Bitstream 1 | MDA (MDA monitors only) |
| closed | open | Bitstream 2 | CGA (both VGA and CGA compatible signals) |
-| closed | closed | Bitstream 3 | Not used |
+| closed | closed | Bitstream 3 | Sound (Adlib & CMS) |
-For example, if you want to use MDA with a VGA monitor, set switches 3 and 4
+For example, if you want to use MDA/HGC with a VGA monitor, set switches 3 and 4
to the open (up) position. (CGA has support for both VGA and CGA monitors built in since it implements a line doubler.)
+Bitstream 3 is reserved for exclusive use with sound cards via the RCA connector, initially Adlib and Game Blaster (CMS) has been implemented.
+
The remaining two switches have a function that is bitstream-dependent.
-| Switch | MDA (VGA comp.) | MDA | CGA |
-| ------ | --------------- | --- | --- |
+| Switch | MDA/HGC (VGA comp.) | MDA/HGC | CGA |
+| ------ | ------------------- | ------- | --- |
| 1 | Not used | Not used | closed=composite mode. open=VGA mode |
| 2 | Not used | Not used | closed=thin font. open=normal font |
@@ -125,18 +127,18 @@ Unlike the namesake video cards of old, the FPGA comes up with the internal card
Checking these first can help you narrow down the source of the problem.
-To confirm proper operation on the ISA bus, it's helpful to set up a PC with a VGA card (or CGA card) and the Graphics Gremlin configured for MDA. These can coexist on the same PC.
+To confirm proper operation on the ISA bus, it's helpful to set up a PC with a VGA card (or CGA card) and the Graphics Gremlin configured for MDA/HGC. These can coexist on the same PC.
-Boot up the PC and run the DOS DEBUG program. Then see if you can access the CRTC registers. Unlike the original MDA card, these can be read back. Note: if you use the VGA compatible MDA mode, then these registers cannot be written to.
+Boot up the PC and run the DOS DEBUG program. Then see if you can access the CRTC registers. Unlike the original MDA/HGC card, these can be read back. Note: if you use the VGA compatible MDA/HGC mode, then these registers cannot be written to.
```
o 3b4 0
i 3b5
```
-This should return a number that is not 0 or FF. (The exact number depends on which MDA bitstream you are using).
+This should return a number that is not 0 or FF. (The exact number depends on which MDA/HGC bitstream you are using).
-If that works, then check to see if you can read and write the MDA video memory area:
+If that works, then check to see if you can read and write the MDA/HGC video memory area:
`e b000:0000 55 aa 55 aa`...
@@ -155,22 +157,18 @@ In general, use a logical process of elimination to find where the fault (or fau
## Emulation Accuracy
-The logic for both the MDA and CGA cards is as close as I could get it to match the schematics available in the technical reference manuals, with two exceptions. The VRAM interface is specific to the 8-bit SRAM chip that I am using instead of the 16-bit SRAM on the original MDA or the 16-bit DRAM used on the CGA, so it had to be quite different from the originals. As a result, the sequencer state machine had to be designed from scratch. There are some other minor differences mostly to support the nonstandard VGA-compatible signal outputs.
+The logic for both the MDA/HGC and CGA cards is as close as I could get it to match the schematics available in the technical reference manuals, with two exceptions. The VRAM interface is specific to the 8-bit SRAM chip that I am using instead of the 16-bit SRAM on the original MDA/HGC or the 16-bit DRAM used on the CGA, so it had to be quite different from the originals. As a result, the sequencer state machine had to be designed from scratch. There are some other minor differences mostly to support the nonstandard VGA-compatible signal outputs.
Accuracy is a work in progress. Certain demos, like 8088MPH, that require cycle-accurate operation on a 4.77MHz PC, don't work 100% correctly. The causes of this aren't yet fully understood.
## Future Plans
-Although the card supports just MDA and CGA, I'd like to support other video standards in the future. EGA and even VGA would be nice, but there are two huge challenges associated with that: they use custom gate array chips and they also use a 32-bit memory bus. This means I would need to quadruple the pixel clock to produce four 8-bit fetches from the SRAM. Due to this bus bandwidth limitation, Super VGAs are totally off the table.
-
-Other people have requested support for Hercules, Tandy, and PCjr graphics, which I might get around to implementing someday.
+Although the card supports just MDA/HGC and CGA, I'd like to support other video standards in the future. EGA and even VGA would be nice, but there are two huge challenges associated with that: they use custom gate array chips and they also use a 32-bit memory bus. This means I would need to quadruple the pixel clock to produce four 8-bit fetches from the SRAM. Due to this bus bandwidth limitation, Super VGAs are totally off the table.
There are also some neat non-graphics uses for the card. With some clever programming, the card could be turned into a memory card to extend the RAM in some of the older IBM PC and XT machines (XMS RAM). It could also emulate an expanded memory card for machines that can't run EMM386. BIOS extension ROMs stored in the NOR flash chip could also be mapped to ROM areas in the PC memory map, which might also be useful.
The card could act as a fancy POST card, perhaps even with a mini bus analyzer built in (using a connected VGA monitor).
-It's even theoretically possible to implement a sound card using the composite video jack (and 7-bit resistor DAC) as the sound output.
-
## License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
International License. See [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/).
diff --git a/SW/CGAVIDEO.COM b/SW/CGAVIDEO.COM
new file mode 100644
index 0000000..5114309
Binary files /dev/null and b/SW/CGAVIDEO.COM differ
diff --git a/SW/TDYKBD.COM b/SW/TDYKBD.COM
new file mode 100644
index 0000000..f8dbc34
--- /dev/null
+++ b/SW/TDYKBD.COM
@@ -0,0 +1 @@
+�a@�a�
\ No newline at end of file
diff --git a/SW/TDYVIDEO.COM b/SW/TDYVIDEO.COM
new file mode 100644
index 0000000..6bcc2b7
--- /dev/null
+++ b/SW/TDYVIDEO.COM
@@ -0,0 +1 @@
+�p���
\ No newline at end of file
diff --git a/verilog/Makefile b/verilog/Makefile
index 0cc1f68..d67e7f9 100644
--- a/verilog/Makefile
+++ b/verilog/Makefile
@@ -8,8 +8,8 @@
#
PROJ = isavideo
-IMAGES = mda70_top mda_top cga_top
-SOURCES = mda70_top.v mda_top.v mda.v crtc6845.v mda_vgaport.v mda_vram.v mda_attrib.v mda_sequencer.v mda_pixel.v cga_top.v cga.v cga_vgaport.v cga_sequencer.v cga_pixel.v cga_attrib.v cga_vram.v cga_composite.v cga_scandoubler.v
+IMAGES = mda70_top mda_top cga_top sound_top
+SOURCES = mda70_top.v mda_top.v mda.v UM6845R.v mda_vgaport.v mda_vram.v mda_attrib.v mda_sequencer.v mda_pixel.v cga_top.v cga.v cga_vgaport.v cga_sequencer.v cga_pixel.v cga_attrib.v cga_vram.v cga_composite.v cga_scandoubler.v sound_top.v sound.v saa1099.v ./jtopl/hdl/jtopl2.v ./jtopl/hdl/jtopl.v ./jtopl/hdl/jtopl_acc.v ./jtopl/hdl/jtopl_op.v ./jtopl/hdl/jtopl_eg.v ./jtopl/hdl/jtopl_pg.v ./jtopl/hdl/jtopl_lfo.v ./jtopl/hdl/jtopl_timers.v ./jtopl/hdl/jtopl_mmr.v ./jtopl/hdl/jtopl_reg.v ./jtopl/hdl/jtopl_div.v ./jtopl/hdl/jtopl_sh_rst.v ./jtopl/hdl/jtopl_pg_comb.v ./jtopl/hdl/jtopl_noise.v ./jtopl/hdl/jtopl_sh.v ./jtopl/hdl/jtopl_eg_comb.v ./jtopl/hdl/jtopl_eg_cnt.v ./jtopl/hdl/jtopl_exprom.v ./jtopl/hdl/jtopl_logsin.v ./jtopl/hdl/jtopl_single_acc.v ./jtopl/hdl/jtopl_eg_final.v ./jtopl/hdl/jtopl_eg_pure.v ./jtopl/hdl/jtopl_eg_step.v ./jtopl/hdl/jtopl_eg_ctrl.v ./jtopl/hdl/jtopl_pg_rhy.v ./jtopl/hdl/jtopl_pg_sum.v ./jtopl/hdl/jtopl_pg_inc.v ./jtopl/hdl/jtopl_pm.v ./jtopl/hdl/jtopl_reg_ch.v ./jtopl/hdl/jtopl_csr.v ./jtopl/hdl/jtopl_slot_cnt.v
PIN_DEF = gremlin.pcf
DEVICE = hx8k
ODIR = build
diff --git a/verilog/README.md b/verilog/README.md
index c9efb8d..ddd55e8 100644
--- a/verilog/README.md
+++ b/verilog/README.md
@@ -2,11 +2,11 @@
(Click here for the [main README](https://github.com/schlae/graphics-gremlin/blob/main/README.md))
-The FPGA code is divided into two major sets of files, those for CGA graphics and those for MDA graphics. At some point I'll tidy up and make a nice organized directory tree, but for now they're all in the same place.
+The FPGA code is divided into two major sets of files, those for CGA graphics and those for MDA/HGC graphics. At some point I'll tidy up and make a nice organized directory tree, but for now they're all in the same place.
-* mda\_top.v: The top level file instantiating the MDA graphics logic
-* mda70\_top.v: An alternative top level file for VGA compatible MDA graphics
-* mda.v: Implements MDA ISA interface, IO registers and instantiates the CRTC, SRAM interface, sequencer, and pixel engine
+* mda\_top.v: The top level file instantiating the MDA/HGC graphics logic
+* mda70\_top.v: An alternative top level file for VGA compatible MDA/HGC graphics
+* mda.v: Implements MDA/HGC ISA interface, IO registers and instantiates the CRTC, SRAM interface, sequencer, and pixel engine
* crtc6845.v: This is my mostly-accurate recreation of the old Motorola 6845 CRT controller chip. It generates all the sync timings as well as the character and row addresses. There are probably slight differences between it and the real thing.
* mda\_sequencer.v: Controls timing across the entire card, deciding when to fetch SRAM data, look up character bits from the character ROM, and allow ISA bus access to the SRAM
* mda\_vram.v: Implements the state machine to arbitrate ISA bus and pixel engine access to the video ram (external SRAM)
@@ -14,11 +14,11 @@ The FPGA code is divided into two major sets of files, those for CGA graphics an
* mda\_attrib.v: The attribute generator applies video attributes to the raw pixel data, including brightness, underline, inverse video, blinking. It also applies the blinking cursor.
* mda\_vgaport.v: This module turns the digital MDA video signals into numbers to drive the resistor ladder DAC connected to the VGA port. If you (gasp) dislike amber monochrome monitors, then you can hack this code to make it green or white.
-CGA graphics logic is similar to MDA and shares the same crtc6845.v logic, but the cards are different enough that I couldn't share more.
+CGA graphics logic is similar to MDA/HGC and shares the same crtc6845.v logic, but the cards are different enough that I couldn't share more.
* cga\_top.v: Instantiates top level CGA logic.
* cga.v: Implements the ISA bus interface, CGA control registers, wait state generator, and most of the other CGA modules
* cga\_sequencer.v: Generates most of the timing signals used on the card, including memory fetches and pixel engine timing.
-* cga\_vram.v: Implements a very basic address MUX for the SRAM interface. This actually causes too much CGA snow, and should be improved using the MDA VRAM interface as a model.
+* cga\_vram.v: Implements a very basic address MUX for the SRAM interface. This actually causes too much CGA snow, and should be improved using the HGC VRAM interface as a model.
* cga\_pixel.v: The CGA pixel engine takes data from the SRAM, does a character lookup (text mode only), and shifts the data out 1 or 2 bits at a time, depending on the video mode.
* cga\_attrib.v: The attribute generator applies video attributes to the raw pixels data, including color, brightness, and blinking.
* cga\_composite.v: Contains the flip flops used to generate NTSC composite color as well as new sync pulses. The output is a 7-bit signal passed off to the green DAC channel for the RCA jack on the card.
diff --git a/verilog/UM6845R.v b/verilog/UM6845R.v
new file mode 100644
index 0000000..2365111
--- /dev/null
+++ b/verilog/UM6845R.v
@@ -0,0 +1,342 @@
+//============================================================================
+// UM6845R for Amstrad CPC
+// Copyright (C) 2018 Sorgelig
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//============================================================================
+
+`default_nettype wire
+module UM6845R
+(
+ input CLOCK,
+ input CLKEN,
+ input nCLKEN,
+ input nRESET,
+ input CRTC_TYPE,
+
+ input ENABLE,
+ input nCS,
+ input R_nW,
+ input RS,
+ input [7:0] DI,
+ output reg [7:0] DO,
+
+ input lock,
+ output line_reset,
+
+ output reg VSYNC,
+ output reg HSYNC,
+ output DE,
+ output FIELD,
+ output CURSOR,
+
+ output [13:0] MA,
+ output [4:0] RA
+);
+
+parameter H_TOTAL = 0;
+parameter H_DISP = 0;
+parameter H_SYNCPOS = 0;
+parameter H_SYNCWIDTH = 0;
+parameter V_TOTAL = 0;
+parameter V_TOTALADJ = 0;
+parameter V_DISP = 0;
+parameter V_SYNCPOS = 0;
+parameter V_MAXSCAN = 0;
+parameter C_START = 0;
+parameter C_END = 0;
+
+/* verilator lint_off WIDTH */
+
+assign FIELD = ~field & interlace[0];
+
+assign MA = row_addr_r;
+assign RA = line | (field & interlace[0]);
+
+assign DE = de[R8_skew & ~{2{CRTC_TYPE}}];
+
+assign line_reset = hcc_last;
+
+reg [7:0] R0_h_total = H_TOTAL;
+reg [7:0] R1_h_displayed = H_DISP;
+reg [7:0] R2_h_sync_pos = H_SYNCPOS;
+reg [3:0] R3_v_sync_width;
+reg [3:0] R3_h_sync_width = H_SYNCWIDTH;
+reg [6:0] R4_v_total = V_TOTAL;
+reg [4:0] R5_v_total_adj = V_TOTALADJ;
+reg [6:0] R6_v_displayed = V_DISP;
+reg [6:0] R7_v_sync_pos = V_SYNCPOS;
+reg [1:0] R8_skew;
+reg [1:0] R8_interlace = 2'd2;
+reg [4:0] R9_v_max_line = V_MAXSCAN;
+reg [1:0] R10_cursor_mode = 2'd0;
+reg [4:0] R10_cursor_start = C_START;
+reg [4:0] R11_cursor_end = C_END;
+reg [5:0] R12_start_addr_h = 6'd0;
+reg [7:0] R13_start_addr_l = 8'd0;
+reg [5:0] R14_cursor_h = 6'd0;
+reg [7:0] R15_cursor_l = 8'd0;
+
+reg [4:0] addr;
+always @(*) begin
+ DO = 8'hFF;
+ if (ENABLE & ~nCS) begin
+ if (RS) begin
+ case (addr)
+ 10: DO = {R10_cursor_mode, R10_cursor_start};
+ 11: DO = R11_cursor_end;
+ 12: DO = CRTC_TYPE ? 8'h00 : R12_start_addr_h;
+ 13: DO = CRTC_TYPE ? 8'h00 : R13_start_addr_l;
+ 14: DO = R14_cursor_h;
+ 15: DO = R15_cursor_l;
+ 31: DO = CRTC_TYPE ? 8'hFF : 8'h00;
+ default: DO = 0;
+ endcase
+ end
+ else if(CRTC_TYPE) begin
+ DO = vde ? 8'h00 : 8'h20; // status for CRTC1
+ end
+ end
+end
+
+always @(posedge CLOCK) begin
+ if (ENABLE & ~nCS & ~R_nW & ~lock) begin
+ if (~RS) addr <= DI[4:0];
+ else begin
+ case (addr)
+ 00: R0_h_total <= DI;
+ 01: R1_h_displayed <= DI;
+ 02: R2_h_sync_pos <= DI;
+ 03: {R3_v_sync_width,R3_h_sync_width} <= DI;
+ 04: R4_v_total <= DI[6:0];
+ 05: R5_v_total_adj <= DI[4:0];
+ 06: R6_v_displayed <= DI[6:0];
+ 07: R7_v_sync_pos <= DI[6:0];
+ 08: {R8_skew, R8_interlace} <= {DI[5:4],DI[1:0]};
+ 09: R9_v_max_line <= DI[4:0];
+ 10: {R10_cursor_mode,R10_cursor_start} <= DI[6:0];
+ 11: R11_cursor_end <= DI[4:0];
+ 12: R12_start_addr_h <= DI[5:0];
+ 13: R13_start_addr_l <= DI[7:0];
+ 14: R14_cursor_h <= DI[5:0];
+ 15: R15_cursor_l <= DI[7:0];
+ endcase
+ end
+ end
+end
+
+wire [4:0] interlace = &R8_interlace[1:0];
+
+reg in_adj;
+
+reg [7:0] hcc;
+wire hcc_last = (hcc == R0_h_total) && (CRTC_TYPE || R0_h_total); // always false if !R0_h_total on CRTC0
+wire [7:0] hcc_next = hcc_last ? 8'h00 : hcc + 1'd1;
+
+reg [4:0] line;
+wire [4:0] line_max = (in_adj ? (|R5_v_total_adj ? R5_v_total_adj-1'd1 : 5'd0) : R9_v_max_line) & ~interlace;
+reg line_last_r;
+wire line_last = (line == line_max) || !line_max;
+wire [4:0] line_next = ((CRTC_TYPE ? line_last : line_last_r) ? 5'd0 : line + 1'd1 + interlace) & ~interlace;
+wire line_new = hcc_last;
+
+reg [6:0] row;
+reg row_last_r;
+wire row_last = (row == R4_v_total) || (!CRTC_TYPE && !R4_v_total);
+wire row_frame_last = ((CRTC_TYPE ? row_last : row_last_r) | in_adj) & ~frame_adj;
+wire [6:0] row_next = row_frame_last ? 7'd0 : row + 1'd1;
+wire row_new = line_new & (CRTC_TYPE ? line_last : line_last_r);
+
+reg frame_adj_r;
+wire frame_adj_CRTC0 = (hcc == 2) ? frame_adj_r & |R5_v_total_adj : frame_adj_r;
+wire frame_adj_CRTC1 = row_last && ~in_adj && R5_v_total_adj;
+wire frame_adj = CRTC_TYPE ? frame_adj_CRTC1 : frame_adj_CRTC0;
+wire frame_new = row_new & row_frame_last;
+
+// counters
+reg field;
+always @(posedge CLOCK) begin
+ if(~nRESET) begin
+ hcc <= 0;
+ line <= 0;
+ row <= 0;
+ in_adj <= 0;
+ field <= 0;
+ end
+ else if(CLKEN) begin
+ hcc <= hcc_next;
+ if(line_new) line <= line_next;
+ if(hcc == 0) begin
+ line_last_r <= line_last;
+ row_last_r <= row_last;
+ frame_adj_r <= line_last & row_last & ~in_adj;
+ end
+ // CRTC0 always schedule the adjustment run at HCC=0,
+ // then at HCC=2 it decides that it really has to run
+ if(hcc == 2) frame_adj_r <= frame_adj_r & |R5_v_total_adj;
+
+ if(row_new) begin
+ row <= row_next;
+ if(frame_adj) in_adj <= 1;
+ else if(frame_new) begin
+ in_adj <= 0;
+ row <= 0;
+ field <= ~field & R8_interlace[0];
+ end
+ end
+ end
+end
+
+wire CRTC1_reload = CRTC_TYPE & (frame_new | (~line_last & !row & !hcc_next)); //CRTC1 reloads addr on every line of 1st row
+wire CRTC0_reload = ~CRTC_TYPE & frame_new;
+wire row_addr_save = hcc == R1_h_displayed && (CRTC_TYPE ? line_last : line_last_r);
+
+// address
+reg [13:0] row_addr; // saved pointer
+reg [13:0] row_addr_r; // current pointer
+always @(posedge CLOCK) begin
+ if(CLKEN) begin
+ if(row_addr_save) row_addr <= row_addr_r; // save current pointer
+
+ if(hcc_last & !row_addr_save) row_addr_r <= row_addr; // restore the pointer, take care of simultaneous saving and restoring
+ if(!hcc_last) row_addr_r <= row_addr_r + 1'd1;
+
+ if(CRTC0_reload) begin
+ row_addr <= {R12_start_addr_h, R13_start_addr_l};
+ row_addr_r <= {R12_start_addr_h, R13_start_addr_l};
+ end
+ if(CRTC1_reload) begin
+ row_addr_r <= {R12_start_addr_h, R13_start_addr_l};
+ end
+ end
+end
+
+// horizontal output
+reg hde;
+reg [3:0] hsc;
+
+wire hsync_on = hcc == R2_h_sync_pos && R3_h_sync_width != 0;
+wire hsync_off = (hsc == R3_h_sync_width) || (CRTC_TYPE && R3_h_sync_width == 0);
+
+always @(posedge CLOCK) begin
+
+ if(~nRESET) begin
+ hsc <= 0;
+ hde <= 0;
+ HSYNC <= 0;
+ end
+ else begin
+ // should be a half char delay (other edge of the clock?)
+ if (hsync_off) HSYNC <= 0;
+ else if (hsync_on) HSYNC <= 1;
+
+ if (ENABLE & RS & ~nCS & ~R_nW & addr == 5'd01 & hcc == DI) hde <= 0;
+
+ if (CLKEN) begin
+ if(line_new) hde <= 1;
+ if(hcc_next == R1_h_displayed) hde <= 0;
+
+ if(HSYNC) hsc <= hsc + 1'd1;
+ else hsc <= 0;
+ end
+ end
+end
+
+// vertical output
+reg vde, vde_r;
+reg VSYNC_r;
+reg [3:0] vsc;
+reg vsync_allow;
+always @(posedge CLOCK) VSYNC <= VSYNC_r; // delay the same as HSYNC to not confuse the GA
+always @(posedge CLOCK) begin
+
+ if(~nRESET) begin
+ vsc <= 0;
+ vde <= 0;
+ vde_r <= 0;
+ VSYNC_r<= 0;
+ vsync_allow <= 1;
+ end
+ else if (CLKEN) begin
+ if (!CRTC_TYPE && row == 0 && line == 0 && R6_v_displayed == 0) begin
+ vde <= ~vde;
+ vde_r <= ~vde_r;
+ end
+
+ if(row_new) begin
+ if((frame_new & row !=0) | row_next != row) vsync_allow <= 1;
+ if(frame_new) begin vde <= 1; vde_r <= 1; end
+ if(row_next == R6_v_displayed) begin vde <= 0; vde_r <= 0; end
+ end
+ if(field ? (hcc_next == {1'b0, R0_h_total[7:1]}) : line_new) begin
+ if(vsc) vsc <= vsc - 1'd1;
+ else if (vsync_allow & (field ? (row == R7_v_sync_pos && !line) : (row_next == R7_v_sync_pos && line_last))) begin
+ VSYNC_r <= 1;
+ // Don't allow a new vsync until a new row (Onescreen Colonies) or the R7 is written (PHX)
+ vsync_allow <= 0;
+ vsc <= (CRTC_TYPE ? 4'd0 : R3_v_sync_width) - 1'd1;
+ end
+ else VSYNC_r <= 0;
+ end
+ end
+ else if (nCLKEN) begin
+ if (!CRTC_TYPE && row == 0 && line == 0 && R6_v_displayed == 0) begin
+ vde <= ~vde;
+ vde_r <= ~vde_r;
+ end
+ end
+
+ if (ENABLE & RS & ~nCS & ~R_nW & addr == 5'd07) begin
+ vsync_allow <= 1;
+ if (row == DI[6:0] && !VSYNC_r) begin
+ // TODO: extra conditions for CRTC0
+ VSYNC_r <= 1;
+ vsc <= (CRTC_TYPE ? 4'd0 : R3_v_sync_width) - 1'd1;
+ end
+ end
+ if (nCLKEN & ENABLE & RS & ~nCS & ~R_nW & addr == 5'd06) begin
+ if (CRTC_TYPE) begin
+ if (row == DI[6:0]) vde_r <= 0;
+ if (row != DI[6:0] && DI[6:0] != 0) vde <= vde_r;
+ if (row == R6_v_displayed && DI[6:0] != row) vde <= 1;
+ if (row == DI[6:0] || DI[6:0] == 0) vde <= 0;
+ end else begin
+ if (row == DI[6:0] && !(row == 0 && line == 0)) vde_r <= 0;
+ end
+ end
+end
+
+wire [3:0] de = {1'b0, dde[1:0], hde & vde & vde_r};
+reg [1:0] dde;
+always @(posedge CLOCK) if (CLKEN) dde <= {dde[0],de[0]};
+
+// Cursor control
+reg cursor_line;
+assign CURSOR = hde & vde & MA == {R14_cursor_h, R15_cursor_l} & cursor_line;
+
+always @(posedge CLOCK) begin
+
+ if(~nRESET) begin
+ cursor_line <= 0;
+ end
+ else if (CLKEN) begin
+ if (line == R10_cursor_start)
+ cursor_line <= 1;
+ else if (line == R11_cursor_end)
+ cursor_line <= 0;
+ end
+ end
+
+endmodule
diff --git a/verilog/cga.v b/verilog/cga.v
index 04cd728..cabaf3b 100644
--- a/verilog/cga.v
+++ b/verilog/cga.v
@@ -51,9 +51,14 @@ module cga(
wire crtc_cs;
wire status_cs;
+ wire tandy_newcolorsel_cs;
wire colorsel_cs;
wire control_cs;
wire bus_mem_cs;
+ wire video_mem_cs;
+ wire tandy_page_cs;
+ wire nmi_mask_register_cs;
+ wire tandy_mode_cs;
reg[7:0] bus_int_out;
wire[7:0] bus_out_crtc;
@@ -61,11 +66,15 @@ module cga(
wire[7:0] cga_status_reg;
reg[7:0] cga_control_reg = 8'b0010_1000; // 0010_1001
reg[7:0] cga_color_reg = 8'b0000_0000;
+ reg[7:0] tandy_color_reg = 8'b0000_0000;
+ reg[3:0] tandy_newcolor = 4'b0000;
+ reg[3:0] tandy_bordercol = 4'b0000;
+ reg[4:0] tandy_modesel = 5'b00000;
+ reg tandy_palette_set;
wire hres_mode;
wire grph_mode;
wire bw_mode;
wire mode_640;
- wire tandy_16_mode;
wire video_enabled;
wire blink_enabled;
@@ -107,6 +116,9 @@ module cga(
wire cpu_memsel;
reg[1:0] wait_state = 2'd0;
reg bus_rdy_latch;
+ reg [7:0] tandy_page_data = 8'h00;
+ reg [7:0] nmi_mask_register_data = 8'hFF;
+ reg tandy_mode = 1'b0;
// Synchronize ISA bus control lines to our clock
always @ (posedge clk)
@@ -123,11 +135,27 @@ module cga(
// Mapped IO
assign crtc_cs = (bus_a[19:3] == IO_BASE_ADDR[19:3]) & ~bus_aen; // 3B4/3B5
assign status_cs = (bus_a == IO_BASE_ADDR + 20'hA) & ~bus_aen;
+ assign tandy_newcolorsel_cs = (bus_a == IO_BASE_ADDR + 20'hE) & ~bus_aen;
assign control_cs = (bus_a == IO_BASE_ADDR + 20'h8) & ~bus_aen;
assign colorsel_cs = (bus_a == IO_BASE_ADDR + 20'h9) & ~bus_aen;
-
- // Memory-mapped from B0000 to B7FFF
- assign bus_mem_cs = (bus_a[19:15] == FRAMEBUFFER_ADDR[19:15]);
+ assign tandy_mode_cs = (bus_a[15:0] == 16'h0370) & ~bus_aen;
+ assign tandy_page_cs = (bus_a[15:0] == 16'h03DF) & ~bus_aen & tandy_mode;
+ assign nmi_mask_register_cs = (bus_a[15:3] == (16'h00a0 >> 3)) & ~bus_aen & tandy_mode; // 0xa0 .. 0xa7
+
+ assign bus_mem_cs = (bus_a[19:15] == FRAMEBUFFER_ADDR[19:15]); // B8000 - BFFFF (16 KB / 32 KB)
+ assign video_mem_cs = (bus_a[19:17] == nmi_mask_register_data[3:1]) & tandy_mode; // 128KB
+
+ /*
+ // Tandy base memory shared, not implementable with Graphics Gremlin, address is only input by design
+ // As a result, some games or programs designed for Tandy will not be able to run or be played
+ always @ (*)
+ begin
+ if (bus_mem_cs && ~bus_iow_l && tandy_mode)
+ latch_bus_a = {nmi_mask_register_data[3:1], tandy_page_data[3] ? {1'b0, tandy_page_data[5:3], bus_a[14:0]} : {2'b00, tandy_page_data[5:4], bus_a[14:0]}};
+ else
+ latch_bus_a = bus_a;
+ end
+ */
// Mux ISA bus data from every possible internal source.
always @ (*)
@@ -135,7 +163,11 @@ module cga(
if (bus_mem_cs & ~bus_memr_l) begin
bus_int_out <= bus_out_mem;
end else if (status_cs & ~bus_ior_l) begin
- bus_int_out <= cga_status_reg;
+ bus_int_out <= cga_status_reg;
+ end else if (tandy_mode_cs & ~bus_ior_l) begin
+ bus_int_out <= {7'b0, tandy_mode};
+ end else if (nmi_mask_register_cs & ~bus_ior_l) begin
+ bus_int_out <= nmi_mask_register_data;
end else if (crtc_cs & ~bus_ior_l & (bus_a[0] == 1)) begin
bus_int_out <= bus_out_crtc;
end else begin
@@ -144,7 +176,7 @@ module cga(
end
// Only for read operations does bus_dir go high.
- assign bus_dir = ((crtc_cs | status_cs) & ~bus_ior_l) |
+ assign bus_dir = ((crtc_cs | status_cs | tandy_mode_cs | nmi_mask_register_cs) & ~bus_ior_l) |
(bus_mem_cs & ~bus_memr_l);
assign bus_out = bus_int_out;
@@ -204,42 +236,68 @@ module cga(
assign mode_640 = cga_control_reg[4]; // 1=640x200 mode, 0=others
assign blink_enabled = cga_control_reg[5];
- // FIXME: temporary for testing
- assign tandy_16_mode = cga_control_reg[6];
+ wire tandy_border_en = tandy_modesel[2];
+ wire tandy_color_4 = tandy_modesel[3];
+ wire tandy_color_16 = tandy_modesel[4];
assign hsync = hsync_int;
-
+
// Update control or color register
always @ (posedge clk)
begin
+ tandy_palette_set <= 1'b0;
if (~bus_iow_synced_l) begin
if (control_cs) begin
cga_control_reg <= bus_d;
end else if (colorsel_cs) begin
cga_color_reg <= bus_d;
+ end else if (status_cs) begin
+ tandy_color_reg <= bus_d;
+ end else if (tandy_mode_cs) begin
+ tandy_mode <= bus_d[0];
+ end else if (tandy_page_cs) begin // Tandy Page Data
+ tandy_page_data <= bus_d;
+ end else if (nmi_mask_register_cs) begin // Mask Register
+ nmi_mask_register_data <= bus_d;
+ end else if (tandy_newcolorsel_cs && tandy_color_reg[7:4] == 4'b0001) begin // Palette Mask Register
+ tandy_newcolor <= bus_d[3:0];
+ tandy_palette_set <= 1'b1;
+ end else if (tandy_newcolorsel_cs && tandy_color_reg[3:0] == 4'b0010) begin // Border Color
+ tandy_bordercol <= bus_d[3:0];
+ end else if (tandy_newcolorsel_cs && tandy_color_reg[3:0] == 4'b0011) begin // Mode Select
+ tandy_modesel <= bus_d[4:0];
end
+
end
end
-
- // CRT controller (MC6845 compatible)
- crtc6845 crtc (
- .clk(clk),
- .divclk(crtc_clk),
- .cs(crtc_cs),
- .a0(bus_a[0]),
- .write(~bus_iow_synced_l),
- .read(~bus_ior_synced_l),
- .bus(bus_d),
- .bus_out(bus_out_crtc),
- .lock(1'b0),
- .hsync(hsync_int),
- .vsync(vsync_l),
- .display_enable(display_enable),
- .cursor(cursor),
- .mem_addr(crtc_addr),
- .row_addr(row_addr),
- .line_reset(line_reset)
- );
+
+ UM6845R crtc (
+ .CLOCK(clk),
+ .CLKEN(crtc_clk),
+ // .nCLKEN(),
+ .nRESET(1'b1),
+ .CRTC_TYPE(1'b1),
+
+ .ENABLE(1'b1),
+ .nCS(~crtc_cs),
+ .R_nW(bus_iow_synced_l),
+ .RS(bus_a[0]),
+ .DI(bus_d),
+ .DO(bus_out_crtc),
+
+ .lock(1'b0),
+ .line_reset(line_reset),
+
+ .VSYNC(vsync_l),
+ .HSYNC(hsync_int),
+ .DE(display_enable),
+ // .FIELD(),
+ .CURSOR(cursor),
+
+ .MA(crtc_addr),
+ .RA(row_addr)
+
+ );
// CGA 40 column timings
defparam crtc.H_TOTAL = 8'd56; // 113
@@ -255,16 +313,20 @@ module cga(
defparam crtc.C_END = 5'd7;
// Interface to video SRAM chip
+
+ wire [18:0] CGA_VRAM_ADDR;
+ assign CGA_VRAM_ADDR = {4'h0, pixel_addr14, pixel_addr13, crtc_addr[11:0],
+ vram_read_a0};
+
`ifdef CGA_SNOW
cga_vram video_buffer (
.clk(clk),
- .isa_addr({4'b000, bus_a[14:0]}),
+ .isa_addr(video_mem_cs ? {4'b0000, bus_a[14:0]} : tandy_page_data[3] ? {3'b000, tandy_page_data[5:3], bus_a[13:0]} : {2'b00, tandy_page_data[5:4], bus_a[14:0]}),
.isa_din(bus_d),
.isa_dout(bus_out_mem),
- .isa_read(bus_mem_cs & ~bus_memr_synced_l),
- .isa_write(bus_mem_cs & ~bus_memw_synced_l),
- .pixel_addr({4'h0, pixel_addr14, pixel_addr13, crtc_addr[11:0],
- vram_read_a0}),
+ .isa_read((bus_mem_cs | video_mem_cs) & ~bus_memr_synced_l),
+ .isa_write((bus_mem_cs | video_mem_cs) & ~bus_memw_synced_l),
+ .pixel_addr((grph_mode & hres_mode) ? {tandy_page_data[2:1], CGA_VRAM_ADDR[14:0]} : {tandy_page_data[2:0], CGA_VRAM_ADDR[13:0]}),
.pixel_data(ram_1_d),
.pixel_read(vram_read),
.ram_a(ram_a),
@@ -276,13 +338,12 @@ module cga(
// Just use the MDA VRAM interface (no snow)
mda_vram video_buffer (
.clk(clk),
- .isa_addr({4'b000, bus_a[14:0]}),
+ .isa_addr(tandy_mode ? video_mem_cs ? {4'b0000, bus_a[14:0]} : tandy_page_data[3] ? {3'b000, tandy_page_data[5:3], bus_a[13:0]} : {2'b00, tandy_page_data[5:4], bus_a[14:0]} : {5'b00000, bus_a[13:0]}),
.isa_din(bus_d),
.isa_dout(bus_out_mem),
- .isa_read(bus_mem_cs & ~bus_memr_synced_l),
- .isa_write(bus_mem_cs & ~bus_memw_synced_l),
- .pixel_addr({4'h0, pixel_addr14, pixel_addr13, crtc_addr[11:0],
- vram_read_a0}),
+ .isa_read((bus_mem_cs | video_mem_cs) & ~bus_memr_synced_l),
+ .isa_write((bus_mem_cs | video_mem_cs) & ~bus_memw_synced_l),
+ .pixel_addr(tandy_mode ? (grph_mode & hres_mode) ? {tandy_page_data[2:1], CGA_VRAM_ADDR[14:0]} : {tandy_page_data[2:0], CGA_VRAM_ADDR[13:0]} : CGA_VRAM_ADDR[13:0]),
.pixel_data(ram_1_d),
.pixel_read(vram_read),
.ram_a(ram_a),
@@ -316,7 +377,8 @@ module cga(
.isa_op_enable(isa_op_enable),
.hclk(hclk),
.lclk(lclk),
- .tandy_16_gfx(tandy_16_mode & grph_mode & hres_mode)
+ .tandy_16_gfx(grph_mode & hres_mode),
+ .tandy_color_16(tandy_color_16)
);
// Pixel pusher
@@ -327,7 +389,6 @@ module cga(
.grph_mode(grph_mode),
.bw_mode(bw_mode),
.mode_640(mode_640),
- .tandy_16_mode(tandy_16_mode),
.thin_font(thin_font),
.vram_data(ram_1_d),
.vram_read_char(vram_read_char),
@@ -343,6 +404,12 @@ module cga(
.vsync(vsync_l),
.video_enabled(video_enabled),
.cga_color_reg(cga_color_reg),
+ .tandy_palette_color(tandy_color_reg[3:0]),
+ .tandy_newcolor(tandy_newcolor),
+ .tandy_palette_set(tandy_palette_set),
+ .tandy_bordercol(tandy_bordercol),
+ .tandy_color_4(tandy_color_4),
+ .tandy_color_16(tandy_color_16),
.video(video)
);
@@ -368,7 +435,7 @@ module cga(
.bw_mode(bw_mode),
.comp_video(comp_video)
);
-
+/*
cga_scandoubler scandoubler (
.clk(clk),
.line_reset(line_reset),
@@ -376,5 +443,5 @@ module cga(
.dbl_hsync(dbl_hsync),
.dbl_video(dbl_video)
);
-
+*/
endmodule
diff --git a/verilog/cga_attrib.v b/verilog/cga_attrib.v
index 3c2b13b..b91df0f 100644
--- a/verilog/cga_attrib.v
+++ b/verilog/cga_attrib.v
@@ -27,7 +27,11 @@ module cga_attrib(
input c1,
input pix_640,
input [3:0] pix_tandy,
- output reg[3:0] pix_out
+ input [3:0] tandy_bordercol,
+ input tandy_color_4,
+ input tandy_color_16,
+ output reg[3:0] pix_out,
+ output wire overscan
);
reg blinkdiv;
@@ -67,18 +71,21 @@ module cga_attrib(
// Determine mux A and mux B inputs for selecting output colors.
assign mux_a = ~display_enable |
(grph_mode ?
- (tandy_16_mode ? 0 : (~(~mode_640 & (c0 | c1)))) :
+ ((tandy_16_mode | tandy_color_16) ? 0 : (~(~mode_640 & (c0 | c1)))) :
~alpha_dots);
assign mux_b = grph_mode | ~display_enable;
// Shutter closes when video is blanked during sync
- assign shutter = (hsync | vsync) | (mode_640 ? ~(display_enable & pix_640) : 0);
+ assign shutter = (hsync | vsync) | ((mode_640 & ~tandy_color_4) ? ~(display_enable & pix_640) : 1'b0);
// Blue palette selection bit
assign selblue = bw_mode ? c0 : cga_color_reg[5];
- assign active_area = tandy_16_mode ? pix_tandy : {cga_color_reg[4], c1, c0, selblue};
-
+ assign active_area = tandy_color_4 ? {1'b0, c1, c0, 1'b0} :
+ (tandy_16_mode | tandy_color_16) ? pix_tandy : {cga_color_reg[4], c1, c0, selblue};
+
+ assign overscan = (mux_b & mux_a);
+
always @ (*)
begin
if (shutter) begin
@@ -88,7 +95,7 @@ module cga_attrib(
2'b00: pix_out <= att_fg; // Text foreground
2'b01: pix_out <= att_bg; // Text background
2'b10: pix_out <= active_area; // Graphics
- 2'b11: pix_out <= cga_color_reg[3:0]; // Overscan color
+ 2'b11: pix_out <= (tandy_16_mode | tandy_color_16) ? tandy_bordercol : cga_color_reg[3:0]; // Overscan color
endcase
end
end
diff --git a/verilog/cga_pixel.v b/verilog/cga_pixel.v
index 04e18a0..3b32b5c 100644
--- a/verilog/cga_pixel.v
+++ b/verilog/cga_pixel.v
@@ -14,7 +14,6 @@ module cga_pixel(
input grph_mode,
input bw_mode,
input mode_640,
- input tandy_16_mode,
input thin_font,
input[7:0] vram_data,
input vram_read_char,
@@ -30,12 +29,18 @@ module cga_pixel(
input vsync,
input video_enabled,
input[7:0] cga_color_reg,
+ input[3:0] tandy_palette_color,
+ input[3:0] tandy_newcolor,
+ input tandy_palette_set,
+ input[3:0] tandy_bordercol,
+ input tandy_color_4,
+ input tandy_color_16,
output[3:0] video
);
reg[7:0] attr_byte;
reg[7:0] char_byte;
- reg[7:0] char_byte_old;
+ reg[7:0] char_byte_del;
reg[7:0] attr_byte_del;
reg[7:0] charbits;
reg[1:0] cursor_del;
@@ -45,22 +50,51 @@ module cga_pixel(
reg[1:0] pix_bits;
reg[1:0] pix_bits_old;
reg[3:0] tandy_bits;
+ reg overscan;
+
+ reg[3:0] tandy_palette[0:15];
+
wire pix_640;
wire[10:0] rom_addr;
wire load_shifter;
- wire[2:0] charpix_sel;
+ wire [2:0] charpix_sel;
+ reg[3:0] video_out;
// Character ROM
reg[7:0] char_rom[0:4095];
initial $readmemh("cga.hex", char_rom, 0, 4095);
+
+ initial begin
+ tandy_palette[0] = 4'h0; tandy_palette[1] = 4'h1; tandy_palette[2] = 4'h2; tandy_palette[3] = 4'h3;
+ tandy_palette[4] = 4'h4; tandy_palette[5] = 4'h5; tandy_palette[6] = 4'h6; tandy_palette[7] = 4'h7;
+ tandy_palette[8] = 4'h8; tandy_palette[9] = 4'h9; tandy_palette[10] = 4'ha; tandy_palette[11] = 4'hb;
+ tandy_palette[12] = 4'hc; tandy_palette[13] = 4'hd; tandy_palette[14] = 4'he; tandy_palette[15] = 4'hf;
+ end
+
+
+ always @ (*)
+ begin
+ if (overscan)
+ video = tandy_color_4 ? video_out : tandy_palette[video_out];
+ else if (tandy_color_4)
+ video = tandy_palette[{ 2'b00, video_out[2:1] }];
+ else if (mode_640)
+ video = tandy_palette[{ 2'b000, pix_640 }];
+ else
+ video = tandy_palette[video_out];
+ end
+
+
// Latch character and attribute data from VRAM
// at appropriate times
always @ (posedge clk)
begin
+ if (tandy_palette_set)
+ tandy_palette[tandy_palette_color] = tandy_newcolor;
+
if (vram_read_char) begin
char_byte <= vram_data;
- char_byte_old <= char_byte;
end
if (vram_read_att) begin
attr_byte <= vram_data;
@@ -70,6 +104,10 @@ module cga_pixel(
// Fetch pixel data for graphics modes
wire [2:0]muxin;
assign muxin = hres_mode ? (clk_seq[3:1] + 3'd6) : (clk_seq[4:2] + 3'd7);
+
+ always @ (posedge clk)
+ char_byte_del <= char_byte;
+
always @ (*)
begin
if (video_enabled) begin
@@ -78,14 +116,14 @@ module cga_pixel(
// Tandy uses "high res" mode for both 320x200x16
// and 640x200x4 color modes
case (muxin)
- 3'd0: pix_bits <= char_byte[7:6];
- 3'd1: pix_bits <= char_byte[5:4];
- 3'd2: pix_bits <= char_byte[3:2];
- 3'd3: pix_bits <= char_byte[1:0];
- 3'd4: pix_bits <= attr_byte[7:6];
- 3'd5: pix_bits <= attr_byte[5:4];
- 3'd6: pix_bits <= attr_byte[3:2];
- 3'd7: pix_bits <= attr_byte[1:0];
+ 3'd0: pix_bits <= tandy_color_4 ? { attr_byte[7], char_byte_del[7] } : char_byte_del[7:6];
+ 3'd1: pix_bits <= tandy_color_4 ? { attr_byte[6], char_byte_del[6] } : char_byte_del[5:4];
+ 3'd2: pix_bits <= tandy_color_4 ? { attr_byte[5], char_byte_del[5] } : char_byte_del[3:2];
+ 3'd3: pix_bits <= tandy_color_4 ? { attr_byte[4], char_byte_del[4] } : char_byte_del[1:0];
+ 3'd4: pix_bits <= tandy_color_4 ? { attr_byte[3], char_byte_del[3] } : attr_byte[7:6];
+ 3'd5: pix_bits <= tandy_color_4 ? { attr_byte[2], char_byte_del[2] } : attr_byte[5:4];
+ 3'd6: pix_bits <= tandy_color_4 ? { attr_byte[1], char_byte_del[1] } : attr_byte[3:2];
+ 3'd7: pix_bits <= tandy_color_4 ? { attr_byte[0], char_byte_del[0] } : attr_byte[1:0];
default: pix_bits <= 2'b0;
endcase
end else begin
@@ -106,11 +144,16 @@ module cga_pixel(
// Look up character byte in our character ROM table
assign rom_addr = {char_byte, row_addr[2:0]};
+ wire pattern_chr = (char_byte >= 8'hB0 && char_byte <= 8'hDF);
+
always @ (posedge clk)
begin
// Only load character bits at this point
if (charrom_read) begin
- charbits <= char_rom[{~thin_font, rom_addr}];
+ if (row_addr > 5'd7)
+ charbits <= pattern_chr ? char_rom[{~thin_font, 11'b0} | {char_byte, 3'd7}] : 8'b0;
+ else
+ charbits <= char_rom[{~thin_font, 11'b0} | rom_addr];
end
end
@@ -143,14 +186,14 @@ module cga_pixel(
wire[2:0] tmp_clk_seq;
assign tmp_clk_seq = clk_seq + 3'd7;
assign pix_640 = tmp_clk_seq[1] ? pix_bits[0] : pix_bits[1];
-
- // In Tandy 320x200x16 mode, concatenate two adjacent pixels
+
+ // In Tandy 320x200x16 and 160x200x16 modes, concatenate two adjacent pixels
wire temp;
assign temp = clk_seq[1:0] == 2'b00;
always @ (posedge clk)
begin
if (clk_seq[0]) begin
- if (clk_seq[1]) begin
+ if (muxin[0]) begin
tandy_bits <= {pix_bits_old, pix_bits};
end else begin
pix_bits_old <= pix_bits;
@@ -174,7 +217,7 @@ module cga_pixel(
.grph_mode(grph_mode),
.bw_mode(bw_mode),
.mode_640(mode_640),
- .tandy_16_mode(tandy_16_mode),
+ .tandy_16_mode(hres_mode),
.display_enable(display_enable_del[0]),
.blink_enabled(blink_enabled),
.blink(blink),
@@ -186,7 +229,11 @@ module cga_pixel(
.c1(pix_bits[1]),
.pix_640(pix_640),
.pix_tandy(tandy_bits),
- .pix_out(video)
+ .tandy_bordercol(tandy_bordercol),
+ .tandy_color_4(tandy_color_4),
+ .tandy_color_16(tandy_color_16),
+ .pix_out(video_out),
+ .overscan(overscan)
);
endmodule
diff --git a/verilog/cga_sequencer.v b/verilog/cga_sequencer.v
index 6336549..e56981c 100644
--- a/verilog/cga_sequencer.v
+++ b/verilog/cga_sequencer.v
@@ -21,7 +21,8 @@ module cga_sequencer(
output isa_op_enable,
output hclk,
output lclk,
- input tandy_16_gfx
+ input tandy_16_gfx,
+ input tandy_color_16
);
wire crtc_clk_int;
@@ -51,7 +52,8 @@ module cga_sequencer(
assign vram_read_char = (clkdiv == 5'd2) || (hres_mode ? (clkdiv == 5'd18) : 0);
assign vram_read_att = (clkdiv == 5'd3) || (hres_mode ? (clkdiv == 5'd19) : 0);
assign charrom_read = (clkdiv == 5'd3) || (hres_mode ? (clkdiv == 5'd19) : 0);// 3 and 19?
- assign disp_pipeline = (clkdiv == (tandy_16_gfx ? 5'd7 : 5'd4)) || (hres_mode ? (clkdiv == (tandy_16_gfx ? 5'd23 : 5'd20)) : 0);
+ assign disp_pipeline = (clkdiv == (tandy_color_16 ? 5'd9 : tandy_16_gfx ? 5'd7 : 5'd4)) || (hres_mode ? (clkdiv == (tandy_16_gfx ? 5'd23 : 5'd20)) : 0);
+
assign crtc_clk = crtc_clk_int;
assign clk_seq = clkdiv;
// Leave a gap of at least 2 cycles between the end of ISA operation and
diff --git a/verilog/cga_top.v b/verilog/cga_top.v
index 915fca9..8135f81 100644
--- a/verilog/cga_top.v
+++ b/verilog/cga_top.v
@@ -66,7 +66,7 @@ module cga_top(
wire[7:0] bus_out;
wire[3:0] video;
- wire[3:0] vga_video;
+// wire[3:0] vga_video;
wire composite_on;
wire thin_font;
@@ -123,7 +123,7 @@ module cga_top(
// CGA digital to analog converter
cga_vgaport vga (
.clk(clk_main),
- .video(vga_video),
+ .video(video),
.red(vga_red),
.green(vga_green),
.blue(vga_blue)
@@ -135,6 +135,7 @@ module cga_top(
assign blue = composite_on ? 6'd0 : vga_blue;
assign vga_vsync = vsync;
+ assign vga_hsync = hsync;
cga cga1 (
.clk(clk_main),
@@ -152,10 +153,10 @@ module cga_top(
.ram_a(ram_a),
.ram_d(ram_d),
.hsync(hsync),
- .dbl_hsync(vga_hsync),
+// .dbl_hsync(vga_hsync),
.vsync(vsync),
.video(video),
- .dbl_video(vga_video),
+// .dbl_video(vga_video),
.comp_video(comp_video),
.thin_font(thin_font)
);
diff --git a/verilog/crtc6845.v b/verilog/crtc6845.v
deleted file mode 100644
index c8a6c62..0000000
--- a/verilog/crtc6845.v
+++ /dev/null
@@ -1,262 +0,0 @@
-// Graphics Gremlin
-//
-// Copyright (c) 2021 Eric Schlaepfer
-// This work is licensed under the Creative Commons Attribution-ShareAlike 4.0
-// International License. To view a copy of this license, visit
-// http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to Creative
-// Commons, PO Box 1866, Mountain View, CA 94042, USA.
-//
-`default_nettype none
-module crtc6845(
- input clk,
- input divclk,
-
- // ISA bus
- input cs,
- input a0,
- input write,
- input read,
- input[7:0] bus,
- output[7:0] bus_out,
-
- input lock,
-
- // Video control signals
- output hsync,
- output vsync,
- output display_enable,
- output cursor,
- output [13:0] mem_addr,
- output [4:0] row_addr,
- output line_reset);
-
- parameter H_TOTAL = 0;
- parameter H_DISP = 0;
- parameter H_SYNCPOS = 0;
- parameter H_SYNCWIDTH = 0;
- parameter V_TOTAL = 0;
- parameter V_TOTALADJ = 0;
- parameter V_DISP = 0;
- parameter V_SYNCPOS = 0;
- parameter V_MAXSCAN = 0;
- parameter C_START = 0;
- parameter C_END = 0;
-
- reg[4:0] cur_addr;
- reg[7:0] bus_out;
-
- // Address register
- always @ (posedge clk) begin
- if (~a0 & write & cs) begin
- cur_addr <= bus[4:0];
- end
- end
-
- // Register file
- always @ (posedge clk) begin
- if (a0 & write & cs & (~lock | (cur_addr > 5'd9))) begin
- case (cur_addr)
- 5'd0: h_total <= bus;
- 5'd1: h_disp <= bus;
- 5'd2: h_syncpos <= bus;
- 5'd3: h_syncwidth <= bus[3:0];
- 5'd4: v_total <= bus[6:0];
- 5'd5: v_totaladj <= bus[4:0];
- 5'd6: v_disp <= bus[6:0];
- 5'd7: v_syncpos <= bus[6:0];
- // Register 8 not implemented
- 5'd9: v_maxscan <= bus[4:0];
- 5'd10: c_start <= bus[6:0];
- 5'd11: c_end <= bus[4:0];
- 5'd12: start_a[13:8] <= bus[5:0];
- 5'd13: start_a[7:0] <= bus;
- 5'd14: cursor_a[13:8] <= bus[5:0];
- 5'd15: cursor_a[7:0] <= bus;
- default: ;
- endcase
- end
- end
- // TODO: Add light pen register (optional)
- always @ (*)
- begin
- case (cur_addr)
- 5'd0: bus_out <= h_total;
- 5'd1: bus_out <= h_disp;
- 5'd2: bus_out <= h_syncpos;
- 5'd3: bus_out <= h_syncwidth;
- 5'd4: bus_out <= v_total;
- 5'd5: bus_out <= v_totaladj;
- 5'd6: bus_out <= v_disp;
- 5'd7: bus_out <= v_syncpos;
- 5'd8: bus_out <= 8'h00;
- 5'd9: bus_out <= v_maxscan;
- 5'd10: bus_out <= c_start;
- 5'd11: bus_out <= c_end;
- 5'd12: bus_out <= {2'b00, start_a[13:8]};
- 5'd13: bus_out <= start_a[7:0];
- 5'd14: bus_out <= {2'b00, cursor_a[13:8]};
- 5'd15: bus_out <= cursor_a[7:0];
- 5'd16: bus_out <= 8'h00; // Light pen regs
- 5'd17: bus_out <= 8'h00;
- default: bus_out <= 8'h00;
- endcase;
- end
-
-// TODO: parameterize these defaults
- reg [7:0] h_total = H_TOTAL; //R0 97
- reg [7:0] h_disp = H_DISP; //R1 80
- reg [7:0] h_syncpos = H_SYNCPOS; //R2 82
- reg [3:0] h_syncwidth = H_SYNCWIDTH; //R3 15
-
- reg [6:0] v_total = V_TOTAL; //R4 25
- reg [4:0] v_totaladj = V_TOTALADJ; //R5 6
- reg [6:0] v_disp = V_DISP; //R6 25
- reg [6:0] v_syncpos = V_SYNCPOS; //R7 25
- reg [4:0] v_maxscan = V_MAXSCAN; //R9 13
-
- reg [6:0] c_start = C_START; //R10 11
- reg [4:0] c_end = C_END; //R11 12
-
- reg [13:0] start_a = 14'd0; //R13/R14
-
- reg [13:0] cursor_a = 14'd92; //R14/R15
-
- // Counters
- reg [7:0] h_count = 8'd0;
- reg [3:0] h_synccount = 4'd1; // Must start at 1
- reg [4:0] v_scancount = 5'd0;
- reg [6:0] v_rowcount = 7'd0;
- reg [3:0] v_synccount = 4'd0;
- reg [4:0] cursor_counter = 5'd0; // Cursor blink
-
-
- wire [4:0] next_v_scancount;
- wire [13:0] ma = 14'd0;
- reg [13:0] ma_rst = 14'd0; // Column reset of memory address
-
- reg vs = 1'b0;
- reg hs = 1'b0;
- reg hdisp = 1'b1;
- reg vdisp = 1'b1;
-
- wire cur_on;
- wire blink;
-
- wire h_end;
- wire v_end;
-
- assign vsync = vs;
- assign hsync = hs;
- assign display_enable = hdisp & vdisp;
-
- assign row_addr = v_scancount;
-
- assign h_end = (h_count == h_total);
-
- assign line_reset = h_end;
-
- // Horizontal counter
- always @ (posedge clk)
- begin
- if (divclk) begin
- if (h_count == h_total) begin
- h_count <= 8'd0;
- hdisp <= 1'b1;
- end else begin
- h_count <= h_count + 1;
- // Blanking
- if (h_count + 1 == h_disp) begin
- hdisp <= 1'b0;
- end
- // Sync output
- if (h_count + 1 == h_syncpos) begin
- hs <= 1'b1;
- end
- end
- end
-
- // Horizontal sync timer
- if (divclk & hs) begin
- if (h_synccount == h_syncwidth) begin
- h_synccount <= 4'b1;
- hs <= 1'b0;
- end else begin
- h_synccount <= h_synccount + 4'b1;
- end
- end
- end
-
-
- assign v_end = (v_rowcount == v_total) &
- (v_scancount == v_maxscan + v_totaladj);
- // Vertical counter
- always @ (posedge clk)
- begin
- if (divclk & (h_count == h_total)) begin // was h_syncpos
-
- if (v_rowcount != v_total) begin
- // Vertical count event
- if (v_scancount != v_maxscan) begin
- v_scancount <= v_scancount + 1;
- end else begin
- v_scancount <= 0;
- v_rowcount <= v_rowcount + 1;
-
- // Handle vertical pulse
- if (v_rowcount + 1 == v_syncpos) begin
- vs <= 1'b1;
- end
-
- // Handle blanking
- if (v_rowcount + 1 == v_disp) begin
- vdisp <= 1'b0;
- end
- end
- end else begin
- // Pad with vertical adjust
- if (v_scancount != v_maxscan + v_totaladj) begin
- v_scancount <= v_scancount + 1;
- end else begin
- v_scancount <= 0;
- v_rowcount <= 0;
- vdisp <= 1'b1;
- cursor_counter <= cursor_counter + 1;
- end
- end
-
- // Vertical sync pulse is fixed at 16 scan line times
- // Vsync pulse turns off after 16 lines
- if (vs) begin
- if (v_synccount == 4'd15) begin
- v_synccount <= 4'd0;
- vs <= 0;
- end else begin
- v_synccount <= v_synccount + 1;
- end
- end
- end
- end
-
- // Cursor
- assign cur_on = (v_scancount >= c_start[4:0]) &
- (v_scancount <= c_end[4:0]);
- assign blink = (c_start[6:5] == 2'b00) |
- (c_start[5] ? cursor_counter[4] : cursor_counter[3]);
- assign cursor = (cursor_a == mem_addr) & cur_on &
- blink & (c_start[6:5] != 2'b01) & display_enable;
-
- // Memory address generator
- assign mem_addr = start_a + ma_rst + {6'b000000, h_count};
- always @ (posedge clk)
- begin
- if (divclk & (v_end | h_end)) begin
- if (v_end) begin
- ma_rst <= 14'd0;
- end else begin
- if (v_scancount == v_maxscan) begin
- ma_rst <= ma_rst + {6'b000000, h_disp};
- end
- end
- end
- end
-endmodule
diff --git a/verilog/isavideo.binm b/verilog/isavideo.binm
index c8b8bc9..198ff26 100644
Binary files a/verilog/isavideo.binm and b/verilog/isavideo.binm differ
diff --git a/verilog/isavideo_t.v b/verilog/isavideo_t.v
index 4946d2d..87333c3 100644
--- a/verilog/isavideo_t.v
+++ b/verilog/isavideo_t.v
@@ -33,9 +33,9 @@ module isavideo_t;
wire ram_we_l;
wire[18:0] ram_a;
wire[7:0] ram_d;
- // Use isavideo here for MDA, cga_top for CGA.
+ // Use isavideo here for HGC, cga_top for CGA.
cga_top dut (
-// .clk_10m(clk), // for MDA
+// .clk_10m(clk), // for HGC
.clk_14m318(clk), // for CGA
.bus_a(bus_a),
diff --git a/verilog/jtopl/LICENSE b/verilog/jtopl/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/verilog/jtopl/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/verilog/jtopl/README.md b/verilog/jtopl/README.md
new file mode 100644
index 0000000..a6e6e1e
--- /dev/null
+++ b/verilog/jtopl/README.md
@@ -0,0 +1,131 @@
+# JTOPL FPGA Clone of Yamaha OPL hardware by Jose Tejada (@topapate)
+
+You can show your appreciation through
+* [Patreon](https://patreon.com/topapate), by supporting releases
+* [Paypal](https://paypal.me/topapate), with a donation
+
+
+JTOPL is an FM sound source written in Verilog, fully compatible with YM3526. This project will most likely grow to include other Yamaha chips of the OPL family.
+
+## Features
+
+The implementation tries to be as close to original hardware as possible. Low usage of FPGA resources has also been a design goal.
+
+*Accuracy*
+
+* Follows Y8950 block diagram by Yamaha
+* Barrel shift registers used for configuration values
+* Takes note of all known reverse engineered information, particularly die shots
+* Accurate at sample level, and at internal cycle clock where reasonable
+* Original architecture kept as much as possible
+
+Some reference works used:
+
+* [NukeYT's Nuked-OPLL](https://github.com/nukeykt/Nuked-OPLL)
+* [Research by Andete](https://github.com/andete/ym2413)
+* [Mitsutaka Okazaki's emu2413](https://github.com/digital-sound-antiques/emu2413)
+
+*Modern Design for FPGA*
+
+* Fully synchronous
+* Clock enable input for easy integration
+* Avoids bulky multiplexers
+
+Directories:
+
+* hdl -> all relevant RTL files, written in verilog
+* ver -> test benches
+* ver/verilator -> test bench that can play vgm files
+
+## Usage
+
+Although many files are shared, each chip has its own top level file to instantiate. There are YAML files for each one that detail the list of files used for each file. These files can be easily converted to whatever format you need, like .qip.
+
+Not all the chips of OPL series are implemented yet, so take the following table as a plan which I am working on.
+
+Chip | Top Level Cell | YAML file | Type | Patches | Implemented | Usage
+--------|----------------|-------------|-------------|---------|--------------|------------------
+YM3526 | jtopl.v | jt26.yaml | OPL | | Yes | Bubble Bobble
+YM3812 | jtopl2.v | jtopl2.yaml | OPL2 | | Yes | Robocop
+Y8950 | jt8950.v | jt8950.yaml | OPL+ADPCM | | Not yet | MSX-Audio
+YM2413 | jt2413.v | jt2413.yaml | OPL-L | Yes | WIP | Pang!
+YM2423 | - | - | OPL-LX | Yes | No plans | Atari ST FM cart
+YMF281 | - | - | OPL-LLP | Yes | No plans | Pachinko
+YMF262 | jt262.v | jt262.yaml | OPL3 | | Not yet |
+
+### Chip differences
+
+Chip | Type | EG bits | Features
+---------|--------------|---------|-------------------------------
+YM3526 | OPL | 9? | Basic OPL
+YM2413 | OPLL | 7 | Removes depth options for vibrato/tremolo
+Y8950 | OPL+ADPCM | 9? | Adds ADPCM
+YM3812 | OPL2 | 9? | Adds waveform select. Four waveforms
+YMF262 | OPL3 | 9 | No CSM. More operator modes, more channels
+
+## Simulation
+
+There are several simulation test benches in the **ver** folder. The most important one is in the **ver/verilator** folder. The simulation script is called with the shell script **go** in the same folder. The script will compile the file **test.cpp** together with other files and the design and will simulate the tune specificied with the -f command. It can read **vgm** tunes and generate .wav output of them.
+
+### Tested Features
+
+Each feature is tested with a given .jtt file in the **ver/verilator/tests** folder.
+
+Feature | JTT File | Status (commit) | Remarks
+---------------|-----------|-----------------|--------
+ TL | TL | |
+ EG rates | rates | |
+ fnum | fnum_abs | Passed 4a2c3cc | Checks absolute value of a note
+ FB | fb | Passed 6e6178d |
+ connection | mod | |
+ EG type | perc | |
+ All slots | slots | | no modulation
+ All slots | slots_mod | | Modulate some channels
+ KSL | ksl1/2/3 | Passed 4a2c3cc | See note*
+ AM | am | Passed fc6ad19 |
+ Vibratto | vib | Passed 44a540f |
+ CSM | | | Not implemented
+ OPL2 waves | tone_w? | Passed | Implemented
+ Keyboard split| | Untested b4345fa| Not implemented
+
+ Note* values don't match the app notes but implementation follows reverse engineering of OPLL and OPL3. Measuring from first note of an octave to last note of the next seems to fit better the table in the notes.
+
+## Rhythm Instruments
+
+They are bass drum, snare drum, tom-tom, high-hat, cymbals and top cymbals. Channels 6,7 and 8 are used for these instruments.
+
+For patch-based OPL chips, there were specific values for each operator register of these instruments. However, for non-patched synthesizers, the user still had to enter register values. So it looks like the benefit from the rhythm feature was:
+
+* Ability to enter more than one key-on command at once
+* Noisy phase for three instruments
+* Forced no modulation on 5 five instruments
+
+Short name | Instrument | Slot | Phase | EG | Modulation |
+-----------|------------|---------|---------|------|------------|
+ BD | Bass drum | 13 & 16 | | Drum | Normal |
+ HH | High hat | 14 | Special | Drum | No |
+ TOM | Tom tom | 15 | | Drum | No |
+ SD | Snare drum | 17 | Special | Drum | No |
+ TOP-CYM | Top cymbal | 18 | Special | Drum | No |
+
+## Related Projects
+
+Other sound chips from the same author (Verilog RTL)
+
+Chip | Repository
+-----------------------|------------
+YM2203, YM2612, YM2610 | [JT12](https://github.com/jotego/jt12)
+YM2151 | [JT51](https://github.com/jotego/jt51)
+YM3526 | [JTOPL](https://github.com/jotego/jtopl)
+YM2149 | [JT49](https://github.com/jotego/jt49)
+sn76489an | [JT89](https://github.com/jotego/jt89)
+OKI 6295 | [JT6295](https://github.com/jotego/jt6295)
+OKI MSM5205 | [JT5205](https://github.com/jotego/jt5205)
+
+Cycle accurate FM chips from Nuked (software emulation)
+
+Chip | Repository
+--------------------|------------------------
+OPLL | [Nuked-OPLL](https://github.com/nukeykt/Nuked-OPLL)
+OPL3 | [Nuked-OPL3](https://github.com/nukeykt/Nuked-OPL3)
+YM3438 | [Nuked-OPN2](https://github.com/nukeykt/Nuked-OPN2)
\ No newline at end of file
diff --git a/verilog/jtopl/doc/Makefile b/verilog/jtopl/doc/Makefile
new file mode 100644
index 0000000..d0c64d0
--- /dev/null
+++ b/verilog/jtopl/doc/Makefile
@@ -0,0 +1,5 @@
+all: lfo_count opll_patches
+
+lfo_count: lfo_count.cc opll.c
+
+opll_patches: opll_patches.c opll.c
\ No newline at end of file
diff --git a/verilog/jtopl/doc/Y8950 app notes.pdf b/verilog/jtopl/doc/Y8950 app notes.pdf
new file mode 100644
index 0000000..700c537
Binary files /dev/null and b/verilog/jtopl/doc/Y8950 app notes.pdf differ
diff --git a/verilog/jtopl/doc/Y8950 datasheet.pdf b/verilog/jtopl/doc/Y8950 datasheet.pdf
new file mode 100644
index 0000000..d150856
Binary files /dev/null and b/verilog/jtopl/doc/Y8950 datasheet.pdf differ
diff --git a/verilog/jtopl/doc/YM2413.pdf b/verilog/jtopl/doc/YM2413.pdf
new file mode 100644
index 0000000..68ea09a
Binary files /dev/null and b/verilog/jtopl/doc/YM2413.pdf differ
diff --git a/verilog/jtopl/doc/YM3812 datasheet.pdf b/verilog/jtopl/doc/YM3812 datasheet.pdf
new file mode 100644
index 0000000..095acad
Binary files /dev/null and b/verilog/jtopl/doc/YM3812 datasheet.pdf differ
diff --git a/verilog/jtopl/doc/Yamaha YM3812 Application Manual.pdf b/verilog/jtopl/doc/Yamaha YM3812 Application Manual.pdf
new file mode 100644
index 0000000..f17ca74
Binary files /dev/null and b/verilog/jtopl/doc/Yamaha YM3812 Application Manual.pdf differ
diff --git a/verilog/jtopl/doc/lfo_count.cc b/verilog/jtopl/doc/lfo_count.cc
new file mode 100644
index 0000000..7f9dc25
--- /dev/null
+++ b/verilog/jtopl/doc/lfo_count.cc
@@ -0,0 +1,22 @@
+#include "opll.h"
+#include
+#include
+
+using namespace std;
+
+int main() {
+ opll_t fm;
+ OPLL_Reset(&fm, opll_type_ym2413);
+ int last=0;
+ for( int k=0; k<1'000'000; k++ ) {
+ int32_t buffer[2];
+ if( last != fm.lfo_am_out ) {
+ cout << setw(10) << k << " " << setw(4) << fm.lfo_am_counter
+ << " " << (int)fm.lfo_am_car << " DIR=" << (int)fm.lfo_am_dir
+ << " " << (int)fm.lfo_am_out << '\n';
+ last = fm.lfo_am_out;
+ }
+ OPLL_Clock( &fm, buffer);
+ }
+}
+
diff --git a/verilog/jtopl/doc/notes.ods b/verilog/jtopl/doc/notes.ods
new file mode 100644
index 0000000..85415e1
Binary files /dev/null and b/verilog/jtopl/doc/notes.ods differ
diff --git a/verilog/jtopl/doc/opl3.c b/verilog/jtopl/doc/opl3.c
new file mode 100644
index 0000000..2168d44
--- /dev/null
+++ b/verilog/jtopl/doc/opl3.c
@@ -0,0 +1,1381 @@
+/* Nuked OPL3
+ * Copyright (C) 2013-2020 Nuke.YKT
+ *
+ * This file is part of Nuked OPL3.
+ *
+ * Nuked OPL3 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * Nuked OPL3 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Nuked OPL3. If not, see .
+
+ * Nuked OPL3 emulator.
+ * Thanks:
+ * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh):
+ * Feedback and Rhythm part calculation information.
+ * forums.submarine.org.uk(carbon14, opl3):
+ * Tremolo and phase generator calculation information.
+ * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
+ * OPL2 ROMs.
+ * siliconpr0n.org(John McMaster, digshadow):
+ * YMF262 and VRC VII decaps and die shots.
+ *
+ * version: 1.8
+ */
+
+#include
+#include
+#include
+#include "opl3.h"
+
+#define RSM_FRAC 10
+
+// Channel types
+
+enum {
+ ch_2op = 0,
+ ch_4op = 1,
+ ch_4op2 = 2,
+ ch_drum = 3
+};
+
+// Envelope key types
+
+enum {
+ egk_norm = 0x01,
+ egk_drum = 0x02
+};
+
+
+//
+// logsin table
+//
+
+static const Bit16u logsinrom[256] = {
+ 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471,
+ 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365,
+ 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd,
+ 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261,
+ 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f,
+ 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd,
+ 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195,
+ 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166,
+ 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c,
+ 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118,
+ 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8,
+ 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db,
+ 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1,
+ 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9,
+ 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094,
+ 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081,
+ 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070,
+ 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060,
+ 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052,
+ 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045,
+ 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039,
+ 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f,
+ 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026,
+ 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e,
+ 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017,
+ 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011,
+ 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c,
+ 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007,
+ 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004,
+ 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002,
+ 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001,
+ 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000
+};
+
+//
+// exp table
+//
+
+static const Bit16u exprom[256] = {
+ 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4,
+ 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9,
+ 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f,
+ 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756,
+ 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e,
+ 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706,
+ 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0,
+ 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba,
+ 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695,
+ 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671,
+ 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e,
+ 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b,
+ 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609,
+ 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8,
+ 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8,
+ 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8,
+ 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589,
+ 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b,
+ 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d,
+ 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530,
+ 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514,
+ 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8,
+ 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc,
+ 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2,
+ 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8,
+ 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e,
+ 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475,
+ 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d,
+ 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445,
+ 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d,
+ 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416,
+ 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400
+};
+
+//
+// freq mult table multiplied by 2
+//
+// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15
+//
+
+static const Bit8u mt[16] = {
+ 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30
+};
+
+//
+// ksl table
+//
+
+static const Bit8u kslrom[16] = {
+ 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64
+};
+
+static const Bit8u kslshift[4] = {
+ 8, 1, 2, 0
+};
+
+//
+// envelope generator constants
+//
+
+static const Bit8u eg_incstep[4][4] = {
+ { 0, 0, 0, 0 },
+ { 1, 0, 0, 0 },
+ { 1, 0, 1, 0 },
+ { 1, 1, 1, 0 }
+};
+
+//
+// address decoding
+//
+
+static const Bit8s ad_slot[0x20] = {
+ 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
+ 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static const Bit8u ch_slot[18] = {
+ 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32
+};
+
+//
+// Envelope generator
+//
+
+typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope);
+typedef void(*envelope_genfunc)(opl3_slot *slott);
+
+static Bit16s OPL3_EnvelopeCalcExp(Bit32u level)
+{
+ if (level > 0x1fff)
+ {
+ level = 0x1fff;
+ }
+ return (exprom[level & 0xff] << 1) >> (level >> 8);
+}
+
+static Bit16s OPL3_EnvelopeCalcSin0(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ Bit16u neg = 0;
+ phase &= 0x3ff;
+ if (phase & 0x200)
+ {
+ neg = 0xffff;
+ }
+ if (phase & 0x100)
+ {
+ out = logsinrom[(phase & 0xff) ^ 0xff];
+ }
+ else
+ {
+ out = logsinrom[phase & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg;
+}
+
+static Bit16s OPL3_EnvelopeCalcSin1(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ phase &= 0x3ff;
+ if (phase & 0x200)
+ {
+ out = 0x1000;
+ }
+ else if (phase & 0x100)
+ {
+ out = logsinrom[(phase & 0xff) ^ 0xff];
+ }
+ else
+ {
+ out = logsinrom[phase & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3));
+}
+
+static Bit16s OPL3_EnvelopeCalcSin2(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ phase &= 0x3ff;
+ if (phase & 0x100)
+ {
+ out = logsinrom[(phase & 0xff) ^ 0xff];
+ }
+ else
+ {
+ out = logsinrom[phase & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3));
+}
+
+static Bit16s OPL3_EnvelopeCalcSin3(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ phase &= 0x3ff;
+ if (phase & 0x100)
+ {
+ out = 0x1000;
+ }
+ else
+ {
+ out = logsinrom[phase & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3));
+}
+
+static Bit16s OPL3_EnvelopeCalcSin4(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ Bit16u neg = 0;
+ phase &= 0x3ff;
+ if ((phase & 0x300) == 0x100)
+ {
+ neg = 0xffff;
+ }
+ if (phase & 0x200)
+ {
+ out = 0x1000;
+ }
+ else if (phase & 0x80)
+ {
+ out = logsinrom[((phase ^ 0xff) << 1) & 0xff];
+ }
+ else
+ {
+ out = logsinrom[(phase << 1) & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg;
+}
+
+static Bit16s OPL3_EnvelopeCalcSin5(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ phase &= 0x3ff;
+ if (phase & 0x200)
+ {
+ out = 0x1000;
+ }
+ else if (phase & 0x80)
+ {
+ out = logsinrom[((phase ^ 0xff) << 1) & 0xff];
+ }
+ else
+ {
+ out = logsinrom[(phase << 1) & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3));
+}
+
+static Bit16s OPL3_EnvelopeCalcSin6(Bit16u phase, Bit16u envelope)
+{
+ Bit16u neg = 0;
+ phase &= 0x3ff;
+ if (phase & 0x200)
+ {
+ neg = 0xffff;
+ }
+ return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg;
+}
+
+static Bit16s OPL3_EnvelopeCalcSin7(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ Bit16u neg = 0;
+ phase &= 0x3ff;
+ if (phase & 0x200)
+ {
+ neg = 0xffff;
+ phase = (phase & 0x1ff) ^ 0x1ff;
+ }
+ out = phase << 3;
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg;
+}
+
+static const envelope_sinfunc envelope_sin[8] = {
+ OPL3_EnvelopeCalcSin0,
+ OPL3_EnvelopeCalcSin1,
+ OPL3_EnvelopeCalcSin2,
+ OPL3_EnvelopeCalcSin3,
+ OPL3_EnvelopeCalcSin4,
+ OPL3_EnvelopeCalcSin5,
+ OPL3_EnvelopeCalcSin6,
+ OPL3_EnvelopeCalcSin7
+};
+
+enum envelope_gen_num
+{
+ envelope_gen_num_attack = 0,
+ envelope_gen_num_decay = 1,
+ envelope_gen_num_sustain = 2,
+ envelope_gen_num_release = 3
+};
+
+static void OPL3_EnvelopeUpdateKSL(opl3_slot *slot)
+{
+ Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 2)
+ - ((0x08 - slot->channel->block) << 5);
+ if (ksl < 0)
+ {
+ ksl = 0;
+ }
+ slot->eg_ksl = (Bit8u)ksl;
+}
+
+static void OPL3_EnvelopeCalc(opl3_slot *slot)
+{
+ Bit8u nonzero;
+ Bit8u rate;
+ Bit8u rate_hi;
+ Bit8u rate_lo;
+ Bit8u reg_rate = 0;
+ Bit8u ks;
+ Bit8u eg_shift, shift;
+ Bit16u eg_rout;
+ Bit16s eg_inc;
+ Bit8u eg_off;
+ Bit8u reset = 0;
+ slot->eg_out = slot->eg_rout + (slot->reg_tl << 2)
+ + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem;
+ if (slot->key && slot->eg_gen == envelope_gen_num_release)
+ {
+ reset = 1;
+ reg_rate = slot->reg_ar;
+ }
+ else
+ {
+ switch (slot->eg_gen)
+ {
+ case envelope_gen_num_attack:
+ reg_rate = slot->reg_ar;
+ break;
+ case envelope_gen_num_decay:
+ reg_rate = slot->reg_dr;
+ break;
+ case envelope_gen_num_sustain:
+ if (!slot->reg_type)
+ {
+ reg_rate = slot->reg_rr;
+ }
+ break;
+ case envelope_gen_num_release:
+ reg_rate = slot->reg_rr;
+ break;
+ }
+ }
+ slot->pg_reset = reset;
+ ks = slot->channel->ksv >> ((slot->reg_ksr ^ 1) << 1);
+ nonzero = (reg_rate != 0);
+ rate = ks + (reg_rate << 2);
+ rate_hi = rate >> 2;
+ rate_lo = rate & 0x03;
+ if (rate_hi & 0x10)
+ {
+ rate_hi = 0x0f;
+ }
+ eg_shift = rate_hi + slot->chip->eg_add;
+ shift = 0;
+ if (nonzero)
+ {
+ if (rate_hi < 12)
+ {
+ if (slot->chip->eg_state)
+ {
+ switch (eg_shift)
+ {
+ case 12:
+ shift = 1;
+ break;
+ case 13:
+ shift = (rate_lo >> 1) & 0x01;
+ break;
+ case 14:
+ shift = rate_lo & 0x01;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ shift = (rate_hi & 0x03) + eg_incstep[rate_lo][slot->chip->timer & 0x03];
+ if (shift & 0x04)
+ {
+ shift = 0x03;
+ }
+ if (!shift)
+ {
+ shift = slot->chip->eg_state;
+ }
+ }
+ }
+ eg_rout = slot->eg_rout;
+ eg_inc = 0;
+ eg_off = 0;
+ // Instant attack
+ if (reset && rate_hi == 0x0f)
+ {
+ eg_rout = 0x00;
+ }
+ // Envelope off
+ if ((slot->eg_rout & 0x1f8) == 0x1f8)
+ {
+ eg_off = 1;
+ }
+ if (slot->eg_gen != envelope_gen_num_attack && !reset && eg_off)
+ {
+ eg_rout = 0x1ff;
+ }
+ switch (slot->eg_gen)
+ {
+ case envelope_gen_num_attack:
+ if (!slot->eg_rout)
+ {
+ slot->eg_gen = envelope_gen_num_decay;
+ }
+ else if (slot->key && shift > 0 && rate_hi != 0x0f)
+ {
+ eg_inc = ((~slot->eg_rout) << shift) >> 4;
+ }
+ break;
+ case envelope_gen_num_decay:
+ if ((slot->eg_rout >> 4) == slot->reg_sl)
+ {
+ slot->eg_gen = envelope_gen_num_sustain;
+ }
+ else if (!eg_off && !reset && shift > 0)
+ {
+ eg_inc = 1 << (shift - 1);
+ }
+ break;
+ case envelope_gen_num_sustain:
+ case envelope_gen_num_release:
+ if (!eg_off && !reset && shift > 0)
+ {
+ eg_inc = 1 << (shift - 1);
+ }
+ break;
+ }
+ slot->eg_rout = (eg_rout + eg_inc) & 0x1ff;
+ // Key off
+ if (reset)
+ {
+ slot->eg_gen = envelope_gen_num_attack;
+ }
+ if (!slot->key)
+ {
+ slot->eg_gen = envelope_gen_num_release;
+ }
+}
+
+static void OPL3_EnvelopeKeyOn(opl3_slot *slot, Bit8u type)
+{
+ slot->key |= type;
+}
+
+static void OPL3_EnvelopeKeyOff(opl3_slot *slot, Bit8u type)
+{
+ slot->key &= ~type;
+}
+
+//
+// Phase Generator
+//
+
+static void OPL3_PhaseGenerate(opl3_slot *slot)
+{
+ opl3_chip *chip;
+ Bit16u f_num;
+ Bit32u basefreq;
+ Bit8u rm_xor, n_bit;
+ Bit32u noise;
+ Bit16u phase;
+
+ chip = slot->chip;
+ f_num = slot->channel->f_num;
+ if (slot->reg_vib)
+ {
+ Bit8s range;
+ Bit8u vibpos;
+
+ range = (f_num >> 7) & 7;
+ vibpos = slot->chip->vibpos;
+
+ if (!(vibpos & 3))
+ {
+ range = 0;
+ }
+ else if (vibpos & 1)
+ {
+ range >>= 1;
+ }
+ range >>= slot->chip->vibshift;
+
+ if (vibpos & 4)
+ {
+ range = -range;
+ }
+ f_num += range;
+ }
+ basefreq = (f_num << slot->channel->block) >> 1;
+ phase = (Bit16u)(slot->pg_phase >> 9);
+ if (slot->pg_reset)
+ {
+ slot->pg_phase = 0;
+ }
+ slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1;
+ // Rhythm mode
+ noise = chip->noise;
+ slot->pg_phase_out = phase;
+ if (slot->slot_num == 13) // hh
+ {
+ chip->rm_hh_bit2 = (phase >> 2) & 1;
+ chip->rm_hh_bit3 = (phase >> 3) & 1;
+ chip->rm_hh_bit7 = (phase >> 7) & 1;
+ chip->rm_hh_bit8 = (phase >> 8) & 1;
+ }
+ if (slot->slot_num == 17 && (chip->rhy & 0x20)) // tc
+ {
+ chip->rm_tc_bit3 = (phase >> 3) & 1;
+ chip->rm_tc_bit5 = (phase >> 5) & 1;
+ }
+ if (chip->rhy & 0x20)
+ {
+ rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7)
+ | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5)
+ | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5);
+ switch (slot->slot_num)
+ {
+ case 13: // hh
+ slot->pg_phase_out = rm_xor << 9;
+ if (rm_xor ^ (noise & 1))
+ {
+ slot->pg_phase_out |= 0xd0;
+ }
+ else
+ {
+ slot->pg_phase_out |= 0x34;
+ }
+ break;
+ case 16: // sd
+ slot->pg_phase_out = (chip->rm_hh_bit8 << 9)
+ | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8);
+ break;
+ case 17: // tc
+ slot->pg_phase_out = (rm_xor << 9) | 0x80;
+ break;
+ default:
+ break;
+ }
+ }
+ n_bit = ((noise >> 14) ^ noise) & 0x01;
+ chip->noise = (noise >> 1) | (n_bit << 22);
+}
+
+//
+// Slot
+//
+
+static void OPL3_SlotWrite20(opl3_slot *slot, Bit8u data)
+{
+ if ((data >> 7) & 0x01)
+ {
+ slot->trem = &slot->chip->tremolo;
+ }
+ else
+ {
+ slot->trem = (Bit8u*)&slot->chip->zeromod;
+ }
+ slot->reg_vib = (data >> 6) & 0x01;
+ slot->reg_type = (data >> 5) & 0x01;
+ slot->reg_ksr = (data >> 4) & 0x01;
+ slot->reg_mult = data & 0x0f;
+}
+
+static void OPL3_SlotWrite40(opl3_slot *slot, Bit8u data)
+{
+ slot->reg_ksl = (data >> 6) & 0x03;
+ slot->reg_tl = data & 0x3f;
+ OPL3_EnvelopeUpdateKSL(slot);
+}
+
+static void OPL3_SlotWrite60(opl3_slot *slot, Bit8u data)
+{
+ slot->reg_ar = (data >> 4) & 0x0f;
+ slot->reg_dr = data & 0x0f;
+}
+
+static void OPL3_SlotWrite80(opl3_slot *slot, Bit8u data)
+{
+ slot->reg_sl = (data >> 4) & 0x0f;
+ if (slot->reg_sl == 0x0f)
+ {
+ slot->reg_sl = 0x1f;
+ }
+ slot->reg_rr = data & 0x0f;
+}
+
+static void OPL3_SlotWriteE0(opl3_slot *slot, Bit8u data)
+{
+ slot->reg_wf = data & 0x07;
+ if (slot->chip->newm == 0x00)
+ {
+ slot->reg_wf &= 0x03;
+ }
+}
+
+static void OPL3_SlotGenerate(opl3_slot *slot)
+{
+ slot->out = envelope_sin[slot->reg_wf](slot->pg_phase_out + *slot->mod, slot->eg_out);
+}
+
+static void OPL3_SlotCalcFB(opl3_slot *slot)
+{
+ if (slot->channel->fb != 0x00)
+ {
+ slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb);
+ }
+ else
+ {
+ slot->fbmod = 0;
+ }
+ slot->prout = slot->out;
+}
+
+//
+// Channel
+//
+
+static void OPL3_ChannelSetupAlg(opl3_channel *channel);
+
+static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data)
+{
+ opl3_channel *channel6;
+ opl3_channel *channel7;
+ opl3_channel *channel8;
+ Bit8u chnum;
+
+ chip->rhy = data & 0x3f;
+ if (chip->rhy & 0x20)
+ {
+ channel6 = &chip->channel[6];
+ channel7 = &chip->channel[7];
+ channel8 = &chip->channel[8];
+ channel6->out[0] = &channel6->slots[1]->out;
+ channel6->out[1] = &channel6->slots[1]->out;
+ channel6->out[2] = &chip->zeromod;
+ channel6->out[3] = &chip->zeromod;
+ channel7->out[0] = &channel7->slots[0]->out;
+ channel7->out[1] = &channel7->slots[0]->out;
+ channel7->out[2] = &channel7->slots[1]->out;
+ channel7->out[3] = &channel7->slots[1]->out;
+ channel8->out[0] = &channel8->slots[0]->out;
+ channel8->out[1] = &channel8->slots[0]->out;
+ channel8->out[2] = &channel8->slots[1]->out;
+ channel8->out[3] = &channel8->slots[1]->out;
+ for (chnum = 6; chnum < 9; chnum++)
+ {
+ chip->channel[chnum].chtype = ch_drum;
+ }
+ OPL3_ChannelSetupAlg(channel6);
+ OPL3_ChannelSetupAlg(channel7);
+ OPL3_ChannelSetupAlg(channel8);
+ //hh
+ if (chip->rhy & 0x01)
+ {
+ OPL3_EnvelopeKeyOn(channel7->slots[0], egk_drum);
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel7->slots[0], egk_drum);
+ }
+ //tc
+ if (chip->rhy & 0x02)
+ {
+ OPL3_EnvelopeKeyOn(channel8->slots[1], egk_drum);
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel8->slots[1], egk_drum);
+ }
+ //tom
+ if (chip->rhy & 0x04)
+ {
+ OPL3_EnvelopeKeyOn(channel8->slots[0], egk_drum);
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel8->slots[0], egk_drum);
+ }
+ //sd
+ if (chip->rhy & 0x08)
+ {
+ OPL3_EnvelopeKeyOn(channel7->slots[1], egk_drum);
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel7->slots[1], egk_drum);
+ }
+ //bd
+ if (chip->rhy & 0x10)
+ {
+ OPL3_EnvelopeKeyOn(channel6->slots[0], egk_drum);
+ OPL3_EnvelopeKeyOn(channel6->slots[1], egk_drum);
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel6->slots[0], egk_drum);
+ OPL3_EnvelopeKeyOff(channel6->slots[1], egk_drum);
+ }
+ }
+ else
+ {
+ for (chnum = 6; chnum < 9; chnum++)
+ {
+ chip->channel[chnum].chtype = ch_2op;
+ OPL3_ChannelSetupAlg(&chip->channel[chnum]);
+ OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[0], egk_drum);
+ OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[1], egk_drum);
+ }
+ }
+}
+
+static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data)
+{
+ if (channel->chip->newm && channel->chtype == ch_4op2)
+ {
+ return;
+ }
+ channel->f_num = (channel->f_num & 0x300) | data;
+ channel->ksv = (channel->block << 1)
+ | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01);
+ OPL3_EnvelopeUpdateKSL(channel->slots[0]);
+ OPL3_EnvelopeUpdateKSL(channel->slots[1]);
+ if (channel->chip->newm && channel->chtype == ch_4op)
+ {
+ channel->pair->f_num = channel->f_num;
+ channel->pair->ksv = channel->ksv;
+ OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]);
+ OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]);
+ }
+}
+
+static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data)
+{
+ if (channel->chip->newm && channel->chtype == ch_4op2)
+ {
+ return;
+ }
+ channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8);
+ channel->block = (data >> 2) & 0x07;
+ channel->ksv = (channel->block << 1)
+ | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01);
+ OPL3_EnvelopeUpdateKSL(channel->slots[0]);
+ OPL3_EnvelopeUpdateKSL(channel->slots[1]);
+ if (channel->chip->newm && channel->chtype == ch_4op)
+ {
+ channel->pair->f_num = channel->f_num;
+ channel->pair->block = channel->block;
+ channel->pair->ksv = channel->ksv;
+ OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]);
+ OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]);
+ }
+}
+
+static void OPL3_ChannelSetupAlg(opl3_channel *channel)
+{
+ if (channel->chtype == ch_drum)
+ {
+ if (channel->ch_num == 7 || channel->ch_num == 8)
+ {
+ channel->slots[0]->mod = &channel->chip->zeromod;
+ channel->slots[1]->mod = &channel->chip->zeromod;
+ return;
+ }
+ switch (channel->alg & 0x01)
+ {
+ case 0x00:
+ channel->slots[0]->mod = &channel->slots[0]->fbmod;
+ channel->slots[1]->mod = &channel->slots[0]->out;
+ break;
+ case 0x01:
+ channel->slots[0]->mod = &channel->slots[0]->fbmod;
+ channel->slots[1]->mod = &channel->chip->zeromod;
+ break;
+ }
+ return;
+ }
+ if (channel->alg & 0x08)
+ {
+ return;
+ }
+ if (channel->alg & 0x04)
+ {
+ channel->pair->out[0] = &channel->chip->zeromod;
+ channel->pair->out[1] = &channel->chip->zeromod;
+ channel->pair->out[2] = &channel->chip->zeromod;
+ channel->pair->out[3] = &channel->chip->zeromod;
+ switch (channel->alg & 0x03)
+ {
+ case 0x00:
+ channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
+ channel->pair->slots[1]->mod = &channel->pair->slots[0]->out;
+ channel->slots[0]->mod = &channel->pair->slots[1]->out;
+ channel->slots[1]->mod = &channel->slots[0]->out;
+ channel->out[0] = &channel->slots[1]->out;
+ channel->out[1] = &channel->chip->zeromod;
+ channel->out[2] = &channel->chip->zeromod;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ case 0x01:
+ channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
+ channel->pair->slots[1]->mod = &channel->pair->slots[0]->out;
+ channel->slots[0]->mod = &channel->chip->zeromod;
+ channel->slots[1]->mod = &channel->slots[0]->out;
+ channel->out[0] = &channel->pair->slots[1]->out;
+ channel->out[1] = &channel->slots[1]->out;
+ channel->out[2] = &channel->chip->zeromod;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ case 0x02:
+ channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
+ channel->pair->slots[1]->mod = &channel->chip->zeromod;
+ channel->slots[0]->mod = &channel->pair->slots[1]->out;
+ channel->slots[1]->mod = &channel->slots[0]->out;
+ channel->out[0] = &channel->pair->slots[0]->out;
+ channel->out[1] = &channel->slots[1]->out;
+ channel->out[2] = &channel->chip->zeromod;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ case 0x03:
+ channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
+ channel->pair->slots[1]->mod = &channel->chip->zeromod;
+ channel->slots[0]->mod = &channel->pair->slots[1]->out;
+ channel->slots[1]->mod = &channel->chip->zeromod;
+ channel->out[0] = &channel->pair->slots[0]->out;
+ channel->out[1] = &channel->slots[0]->out;
+ channel->out[2] = &channel->slots[1]->out;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ }
+ }
+ else
+ {
+ switch (channel->alg & 0x01)
+ {
+ case 0x00:
+ channel->slots[0]->mod = &channel->slots[0]->fbmod;
+ channel->slots[1]->mod = &channel->slots[0]->out;
+ channel->out[0] = &channel->slots[1]->out;
+ channel->out[1] = &channel->chip->zeromod;
+ channel->out[2] = &channel->chip->zeromod;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ case 0x01:
+ channel->slots[0]->mod = &channel->slots[0]->fbmod;
+ channel->slots[1]->mod = &channel->chip->zeromod;
+ channel->out[0] = &channel->slots[0]->out;
+ channel->out[1] = &channel->slots[1]->out;
+ channel->out[2] = &channel->chip->zeromod;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ }
+ }
+}
+
+static void OPL3_ChannelWriteC0(opl3_channel *channel, Bit8u data)
+{
+ channel->fb = (data & 0x0e) >> 1;
+ channel->con = data & 0x01;
+ channel->alg = channel->con;
+ if (channel->chip->newm)
+ {
+ if (channel->chtype == ch_4op)
+ {
+ channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con);
+ channel->alg = 0x08;
+ OPL3_ChannelSetupAlg(channel->pair);
+ }
+ else if (channel->chtype == ch_4op2)
+ {
+ channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con);
+ channel->pair->alg = 0x08;
+ OPL3_ChannelSetupAlg(channel);
+ }
+ else
+ {
+ OPL3_ChannelSetupAlg(channel);
+ }
+ }
+ else
+ {
+ OPL3_ChannelSetupAlg(channel);
+ }
+ if (channel->chip->newm)
+ {
+ channel->cha = ((data >> 4) & 0x01) ? ~0 : 0;
+ channel->chb = ((data >> 5) & 0x01) ? ~0 : 0;
+ }
+ else
+ {
+ channel->cha = channel->chb = (Bit16u)~0;
+ }
+}
+
+static void OPL3_ChannelKeyOn(opl3_channel *channel)
+{
+ if (channel->chip->newm)
+ {
+ if (channel->chtype == ch_4op)
+ {
+ OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm);
+ OPL3_EnvelopeKeyOn(channel->pair->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOn(channel->pair->slots[1], egk_norm);
+ }
+ else if (channel->chtype == ch_2op || channel->chtype == ch_drum)
+ {
+ OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm);
+ }
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm);
+ }
+}
+
+static void OPL3_ChannelKeyOff(opl3_channel *channel)
+{
+ if (channel->chip->newm)
+ {
+ if (channel->chtype == ch_4op)
+ {
+ OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm);
+ OPL3_EnvelopeKeyOff(channel->pair->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOff(channel->pair->slots[1], egk_norm);
+ }
+ else if (channel->chtype == ch_2op || channel->chtype == ch_drum)
+ {
+ OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm);
+ }
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm);
+ }
+}
+
+static void OPL3_ChannelSet4Op(opl3_chip *chip, Bit8u data)
+{
+ Bit8u bit;
+ Bit8u chnum;
+ for (bit = 0; bit < 6; bit++)
+ {
+ chnum = bit;
+ if (bit >= 3)
+ {
+ chnum += 9 - 3;
+ }
+ if ((data >> bit) & 0x01)
+ {
+ chip->channel[chnum].chtype = ch_4op;
+ chip->channel[chnum + 3].chtype = ch_4op2;
+ }
+ else
+ {
+ chip->channel[chnum].chtype = ch_2op;
+ chip->channel[chnum + 3].chtype = ch_2op;
+ }
+ }
+}
+
+static Bit16s OPL3_ClipSample(Bit32s sample)
+{
+ if (sample > 32767)
+ {
+ sample = 32767;
+ }
+ else if (sample < -32768)
+ {
+ sample = -32768;
+ }
+ return (Bit16s)sample;
+}
+
+void OPL3_Generate(opl3_chip *chip, Bit16s *buf)
+{
+ Bit8u ii;
+ Bit8u jj;
+ Bit16s accm;
+ Bit8u shift = 0;
+
+ buf[1] = OPL3_ClipSample(chip->mixbuff[1]);
+
+ for (ii = 0; ii < 15; ii++)
+ {
+ OPL3_SlotCalcFB(&chip->slot[ii]);
+ OPL3_EnvelopeCalc(&chip->slot[ii]);
+ OPL3_PhaseGenerate(&chip->slot[ii]);
+ OPL3_SlotGenerate(&chip->slot[ii]);
+ }
+
+ chip->mixbuff[0] = 0;
+ for (ii = 0; ii < 18; ii++)
+ {
+ accm = 0;
+ for (jj = 0; jj < 4; jj++)
+ {
+ accm += *chip->channel[ii].out[jj];
+ }
+ chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha);
+ }
+
+ for (ii = 15; ii < 18; ii++)
+ {
+ OPL3_SlotCalcFB(&chip->slot[ii]);
+ OPL3_EnvelopeCalc(&chip->slot[ii]);
+ OPL3_PhaseGenerate(&chip->slot[ii]);
+ OPL3_SlotGenerate(&chip->slot[ii]);
+ }
+
+ buf[0] = OPL3_ClipSample(chip->mixbuff[0]);
+
+ for (ii = 18; ii < 33; ii++)
+ {
+ OPL3_SlotCalcFB(&chip->slot[ii]);
+ OPL3_EnvelopeCalc(&chip->slot[ii]);
+ OPL3_PhaseGenerate(&chip->slot[ii]);
+ OPL3_SlotGenerate(&chip->slot[ii]);
+ }
+
+ chip->mixbuff[1] = 0;
+ for (ii = 0; ii < 18; ii++)
+ {
+ accm = 0;
+ for (jj = 0; jj < 4; jj++)
+ {
+ accm += *chip->channel[ii].out[jj];
+ }
+ chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb);
+ }
+
+ for (ii = 33; ii < 36; ii++)
+ {
+ OPL3_SlotCalcFB(&chip->slot[ii]);
+ OPL3_EnvelopeCalc(&chip->slot[ii]);
+ OPL3_PhaseGenerate(&chip->slot[ii]);
+ OPL3_SlotGenerate(&chip->slot[ii]);
+ }
+
+ if ((chip->timer & 0x3f) == 0x3f)
+ {
+ chip->tremolopos = (chip->tremolopos + 1) % 210;
+ }
+ if (chip->tremolopos < 105)
+ {
+ chip->tremolo = chip->tremolopos >> chip->tremoloshift;
+ }
+ else
+ {
+ chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift;
+ }
+
+ if ((chip->timer & 0x3ff) == 0x3ff)
+ {
+ chip->vibpos = (chip->vibpos + 1) & 7;
+ }
+
+ chip->timer++;
+
+ chip->eg_add = 0;
+ if (chip->eg_timer)
+ {
+ while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0)
+ {
+ shift++;
+ }
+ if (shift > 12)
+ {
+ chip->eg_add = 0;
+ }
+ else
+ {
+ chip->eg_add = shift + 1;
+ }
+ }
+
+ if (chip->eg_timerrem || chip->eg_state)
+ {
+ if (chip->eg_timer == 0xfffffffff)
+ {
+ chip->eg_timer = 0;
+ chip->eg_timerrem = 1;
+ }
+ else
+ {
+ chip->eg_timer++;
+ chip->eg_timerrem = 0;
+ }
+ }
+
+ chip->eg_state ^= 1;
+
+ while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt)
+ {
+ if (!(chip->writebuf[chip->writebuf_cur].reg & 0x200))
+ {
+ break;
+ }
+ chip->writebuf[chip->writebuf_cur].reg &= 0x1ff;
+ OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg,
+ chip->writebuf[chip->writebuf_cur].data);
+ chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE;
+ }
+ chip->writebuf_samplecnt++;
+}
+
+void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf)
+{
+ while (chip->samplecnt >= chip->rateratio)
+ {
+ chip->oldsamples[0] = chip->samples[0];
+ chip->oldsamples[1] = chip->samples[1];
+ OPL3_Generate(chip, chip->samples);
+ chip->samplecnt -= chip->rateratio;
+ }
+ buf[0] = (Bit16s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt)
+ + chip->samples[0] * chip->samplecnt) / chip->rateratio);
+ buf[1] = (Bit16s)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt)
+ + chip->samples[1] * chip->samplecnt) / chip->rateratio);
+ chip->samplecnt += 1 << RSM_FRAC;
+}
+
+void OPL3_Reset(opl3_chip *chip, Bit32u samplerate)
+{
+ Bit8u slotnum;
+ Bit8u channum;
+
+ memset(chip, 0, sizeof(opl3_chip));
+ for (slotnum = 0; slotnum < 36; slotnum++)
+ {
+ chip->slot[slotnum].chip = chip;
+ chip->slot[slotnum].mod = &chip->zeromod;
+ chip->slot[slotnum].eg_rout = 0x1ff;
+ chip->slot[slotnum].eg_out = 0x1ff;
+ chip->slot[slotnum].eg_gen = envelope_gen_num_release;
+ chip->slot[slotnum].trem = (Bit8u*)&chip->zeromod;
+ chip->slot[slotnum].slot_num = slotnum;
+ }
+ for (channum = 0; channum < 18; channum++)
+ {
+ chip->channel[channum].slots[0] = &chip->slot[ch_slot[channum]];
+ chip->channel[channum].slots[1] = &chip->slot[ch_slot[channum] + 3];
+ chip->slot[ch_slot[channum]].channel = &chip->channel[channum];
+ chip->slot[ch_slot[channum] + 3].channel = &chip->channel[channum];
+ if ((channum % 9) < 3)
+ {
+ chip->channel[channum].pair = &chip->channel[channum + 3];
+ }
+ else if ((channum % 9) < 6)
+ {
+ chip->channel[channum].pair = &chip->channel[channum - 3];
+ }
+ chip->channel[channum].chip = chip;
+ chip->channel[channum].out[0] = &chip->zeromod;
+ chip->channel[channum].out[1] = &chip->zeromod;
+ chip->channel[channum].out[2] = &chip->zeromod;
+ chip->channel[channum].out[3] = &chip->zeromod;
+ chip->channel[channum].chtype = ch_2op;
+ chip->channel[channum].cha = 0xffff;
+ chip->channel[channum].chb = 0xffff;
+ chip->channel[channum].ch_num = channum;
+ OPL3_ChannelSetupAlg(&chip->channel[channum]);
+ }
+ chip->noise = 1;
+ chip->rateratio = (samplerate << RSM_FRAC) / 49716;
+ chip->tremoloshift = 4;
+ chip->vibshift = 1;
+}
+
+void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v)
+{
+ Bit8u high = (reg >> 8) & 0x01;
+ Bit8u regm = reg & 0xff;
+ switch (regm & 0xf0)
+ {
+ case 0x00:
+ if (high)
+ {
+ switch (regm & 0x0f)
+ {
+ case 0x04:
+ OPL3_ChannelSet4Op(chip, v);
+ break;
+ case 0x05:
+ chip->newm = v & 0x01;
+ break;
+ }
+ }
+ else
+ {
+ switch (regm & 0x0f)
+ {
+ case 0x08:
+ chip->nts = (v >> 6) & 0x01;
+ break;
+ }
+ }
+ break;
+ case 0x20:
+ case 0x30:
+ if (ad_slot[regm & 0x1f] >= 0)
+ {
+ OPL3_SlotWrite20(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v);
+ }
+ break;
+ case 0x40:
+ case 0x50:
+ if (ad_slot[regm & 0x1f] >= 0)
+ {
+ OPL3_SlotWrite40(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v);
+ }
+ break;
+ case 0x60:
+ case 0x70:
+ if (ad_slot[regm & 0x1f] >= 0)
+ {
+ OPL3_SlotWrite60(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v);
+ }
+ break;
+ case 0x80:
+ case 0x90:
+ if (ad_slot[regm & 0x1f] >= 0)
+ {
+ OPL3_SlotWrite80(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v);
+ }
+ break;
+ case 0xe0:
+ case 0xf0:
+ if (ad_slot[regm & 0x1f] >= 0)
+ {
+ OPL3_SlotWriteE0(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v);
+ }
+ break;
+ case 0xa0:
+ if ((regm & 0x0f) < 9)
+ {
+ OPL3_ChannelWriteA0(&chip->channel[9 * high + (regm & 0x0f)], v);
+ }
+ break;
+ case 0xb0:
+ if (regm == 0xbd && !high)
+ {
+ chip->tremoloshift = (((v >> 7) ^ 1) << 1) + 2;
+ chip->vibshift = ((v >> 6) & 0x01) ^ 1;
+ OPL3_ChannelUpdateRhythm(chip, v);
+ }
+ else if ((regm & 0x0f) < 9)
+ {
+ OPL3_ChannelWriteB0(&chip->channel[9 * high + (regm & 0x0f)], v);
+ if (v & 0x20)
+ {
+ OPL3_ChannelKeyOn(&chip->channel[9 * high + (regm & 0x0f)]);
+ }
+ else
+ {
+ OPL3_ChannelKeyOff(&chip->channel[9 * high + (regm & 0x0f)]);
+ }
+ }
+ break;
+ case 0xc0:
+ if ((regm & 0x0f) < 9)
+ {
+ OPL3_ChannelWriteC0(&chip->channel[9 * high + (regm & 0x0f)], v);
+ }
+ break;
+ }
+}
+
+void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v)
+{
+ Bit64u time1, time2;
+
+ if (chip->writebuf[chip->writebuf_last].reg & 0x200)
+ {
+ OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff,
+ chip->writebuf[chip->writebuf_last].data);
+
+ chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE;
+ chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time;
+ }
+
+ chip->writebuf[chip->writebuf_last].reg = reg | 0x200;
+ chip->writebuf[chip->writebuf_last].data = v;
+ time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY;
+ time2 = chip->writebuf_samplecnt;
+
+ if (time1 < time2)
+ {
+ time1 = time2;
+ }
+
+ chip->writebuf[chip->writebuf_last].time = time1;
+ chip->writebuf_lasttime = time1;
+ chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE;
+}
+
+void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples)
+{
+ Bit32u i;
+
+ for(i = 0; i < numsamples; i++)
+ {
+ OPL3_GenerateResampled(chip, sndptr);
+ sndptr += 2;
+ }
+}
diff --git a/verilog/jtopl/doc/opl3.h b/verilog/jtopl/doc/opl3.h
new file mode 100644
index 0000000..313a759
--- /dev/null
+++ b/verilog/jtopl/doc/opl3.h
@@ -0,0 +1,154 @@
+/* Nuked OPL3
+ * Copyright (C) 2013-2020 Nuke.YKT
+ *
+ * This file is part of Nuked OPL3.
+ *
+ * Nuked OPL3 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * Nuked OPL3 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Nuked OPL3. If not, see .
+
+ * Nuked OPL3 emulator.
+ * Thanks:
+ * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh):
+ * Feedback and Rhythm part calculation information.
+ * forums.submarine.org.uk(carbon14, opl3):
+ * Tremolo and phase generator calculation information.
+ * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
+ * OPL2 ROMs.
+ * siliconpr0n.org(John McMaster, digshadow):
+ * YMF262 and VRC VII decaps and die shots.
+ *
+ * version: 1.8
+ */
+
+#ifndef OPL_OPL3_H
+#define OPL_OPL3_H
+
+#include
+
+#define OPL_WRITEBUF_SIZE 1024
+#define OPL_WRITEBUF_DELAY 2
+
+typedef uintptr_t Bitu;
+typedef intptr_t Bits;
+typedef uint64_t Bit64u;
+typedef int64_t Bit64s;
+typedef uint32_t Bit32u;
+typedef int32_t Bit32s;
+typedef uint16_t Bit16u;
+typedef int16_t Bit16s;
+typedef uint8_t Bit8u;
+typedef int8_t Bit8s;
+
+typedef struct _opl3_slot opl3_slot;
+typedef struct _opl3_channel opl3_channel;
+typedef struct _opl3_chip opl3_chip;
+
+struct _opl3_slot {
+ opl3_channel *channel;
+ opl3_chip *chip;
+ Bit16s out;
+ Bit16s fbmod;
+ Bit16s *mod;
+ Bit16s prout;
+ Bit16s eg_rout;
+ Bit16s eg_out;
+ Bit8u eg_inc;
+ Bit8u eg_gen;
+ Bit8u eg_rate;
+ Bit8u eg_ksl;
+ Bit8u *trem;
+ Bit8u reg_vib;
+ Bit8u reg_type;
+ Bit8u reg_ksr;
+ Bit8u reg_mult;
+ Bit8u reg_ksl;
+ Bit8u reg_tl;
+ Bit8u reg_ar;
+ Bit8u reg_dr;
+ Bit8u reg_sl;
+ Bit8u reg_rr;
+ Bit8u reg_wf;
+ Bit8u key;
+ Bit32u pg_reset;
+ Bit32u pg_phase;
+ Bit16u pg_phase_out;
+ Bit8u slot_num;
+};
+
+struct _opl3_channel {
+ opl3_slot *slots[2];
+ opl3_channel *pair;
+ opl3_chip *chip;
+ Bit16s *out[4];
+ Bit8u chtype;
+ Bit16u f_num;
+ Bit8u block;
+ Bit8u fb;
+ Bit8u con;
+ Bit8u alg;
+ Bit8u ksv;
+ Bit16u cha, chb;
+ Bit8u ch_num;
+};
+
+typedef struct _opl3_writebuf {
+ Bit64u time;
+ Bit16u reg;
+ Bit8u data;
+} opl3_writebuf;
+
+struct _opl3_chip {
+ opl3_channel channel[18];
+ opl3_slot slot[36];
+ Bit16u timer;
+ Bit64u eg_timer;
+ Bit8u eg_timerrem;
+ Bit8u eg_state;
+ Bit8u eg_add;
+ Bit8u newm;
+ Bit8u nts;
+ Bit8u rhy;
+ Bit8u vibpos;
+ Bit8u vibshift;
+ Bit8u tremolo;
+ Bit8u tremolopos;
+ Bit8u tremoloshift;
+ Bit32u noise;
+ Bit16s zeromod;
+ Bit32s mixbuff[2];
+ Bit8u rm_hh_bit2;
+ Bit8u rm_hh_bit3;
+ Bit8u rm_hh_bit7;
+ Bit8u rm_hh_bit8;
+ Bit8u rm_tc_bit3;
+ Bit8u rm_tc_bit5;
+ //OPL3L
+ Bit32s rateratio;
+ Bit32s samplecnt;
+ Bit16s oldsamples[2];
+ Bit16s samples[2];
+
+ Bit64u writebuf_samplecnt;
+ Bit32u writebuf_cur;
+ Bit32u writebuf_last;
+ Bit64u writebuf_lasttime;
+ opl3_writebuf writebuf[OPL_WRITEBUF_SIZE];
+};
+
+void OPL3_Generate(opl3_chip *chip, Bit16s *buf);
+void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf);
+void OPL3_Reset(opl3_chip *chip, Bit32u samplerate);
+void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v);
+void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v);
+void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples);
+#endif
diff --git a/verilog/jtopl/doc/opll.c b/verilog/jtopl/doc/opll.c
new file mode 100644
index 0000000..b65c0ce
--- /dev/null
+++ b/verilog/jtopl/doc/opll.c
@@ -0,0 +1,1107 @@
+/*
+ * Copyright (C) 2019 Nuke.YKT
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * Yamaha YM2413 emulator
+ * Thanks:
+ * siliconpr0n.org(digshadow, John McMaster):
+ * VRC VII decap and die shot.
+ *
+ * version: 1.0.1
+ */
+
+#include
+#include "opll.h"
+
+enum {
+ eg_num_attack = 0,
+ eg_num_decay = 1,
+ eg_num_sustain = 2,
+ eg_num_release = 3
+};
+
+enum {
+ rm_num_bd0 = 0,
+ rm_num_hh = 1,
+ rm_num_tom = 2,
+ rm_num_bd1 = 3,
+ rm_num_sd = 4,
+ rm_num_tc = 5
+};
+
+/* logsin table */
+static const uint16_t logsinrom[256] = {
+ 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471,
+ 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365,
+ 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd,
+ 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261,
+ 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f,
+ 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd,
+ 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195,
+ 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166,
+ 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c,
+ 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118,
+ 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8,
+ 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db,
+ 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1,
+ 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9,
+ 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094,
+ 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081,
+ 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070,
+ 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060,
+ 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052,
+ 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045,
+ 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039,
+ 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f,
+ 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026,
+ 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e,
+ 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017,
+ 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011,
+ 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c,
+ 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007,
+ 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004,
+ 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002,
+ 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001,
+ 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000
+};
+
+/* exp table */
+static const uint16_t exprom[256] = {
+ 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4,
+ 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9,
+ 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f,
+ 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756,
+ 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e,
+ 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706,
+ 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0,
+ 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba,
+ 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695,
+ 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671,
+ 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e,
+ 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b,
+ 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609,
+ 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8,
+ 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8,
+ 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8,
+ 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589,
+ 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b,
+ 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d,
+ 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530,
+ 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514,
+ 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8,
+ 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc,
+ 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2,
+ 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8,
+ 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e,
+ 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475,
+ 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d,
+ 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445,
+ 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d,
+ 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416,
+ 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400
+};
+
+static const opll_patch_t patch_ds1001[opll_patch_max] = {
+ { 0x05, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0e, 0x08 },{ 0x08, 0x01 },{ 0x04, 0x02 },{ 0x02, 0x07 } },
+ { 0x14, 0x00, 0x01, 0x05,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0d, 0x0f },{ 0x08, 0x06 },{ 0x02, 0x01 },{ 0x03, 0x02 } },
+ { 0x08, 0x00, 0x01, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x0b },{ 0x0a, 0x02 },{ 0x02, 0x01 },{ 0x00, 0x02 } },
+ { 0x0c, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x06 },{ 0x08, 0x04 },{ 0x06, 0x02 },{ 0x01, 0x07 } },
+ { 0x1e, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x02, 0x01 },{ 0x00, 0x00 },{ 0x0e, 0x07 },{ 0x01, 0x06 },{ 0x00, 0x02 },{ 0x01, 0x08 } },
+ { 0x06, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x02, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x0e },{ 0x03, 0x02 },{ 0x0f, 0x0f },{ 0x04, 0x04 } },
+ { 0x1d, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x08, 0x08 },{ 0x02, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x07 } },
+ { 0x22, 0x01, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x07 },{ 0x02, 0x02 },{ 0x00, 0x01 },{ 0x01, 0x07 } },
+ { 0x25, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x05, 0x01 },{ 0x00, 0x00 },{ 0x04, 0x07 },{ 0x00, 0x03 },{ 0x07, 0x00 },{ 0x02, 0x01 } },
+ { 0x0f, 0x00, 0x01, 0x07,{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x00 },{ 0x05, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x0a },{ 0x08, 0x05 },{ 0x05, 0x00 },{ 0x01, 0x02 } },
+ { 0x24, 0x00, 0x00, 0x07,{ 0x00, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x07, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x08, 0x08 },{ 0x02, 0x01 },{ 0x02, 0x02 } },
+ { 0x11, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x03 },{ 0x00, 0x00 },{ 0x06, 0x07 },{ 0x05, 0x04 },{ 0x01, 0x01 },{ 0x08, 0x06 } },
+ { 0x13, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x02 },{ 0x03, 0x00 },{ 0x0c, 0x09 },{ 0x09, 0x05 },{ 0x00, 0x00 },{ 0x03, 0x02 } },
+ { 0x0c, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x03 },{ 0x00, 0x00 },{ 0x09, 0x0c },{ 0x04, 0x00 },{ 0x03, 0x0f },{ 0x03, 0x06 } },
+ { 0x0d, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x01 },{ 0x01, 0x02 },{ 0x00, 0x00 },{ 0x0c, 0x0d },{ 0x01, 0x05 },{ 0x05, 0x00 },{ 0x06, 0x06 } },
+
+ { 0x18, 0x00, 0x01, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0d, 0x00 },{ 0x0f, 0x00 },{ 0x06, 0x00 },{ 0x0a, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x08 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } }
+};
+
+static const opll_patch_t patch_ym2413[opll_patch_max] = {
+ { 0x1e, 0x01, 0x00, 0x07,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0d, 0x07 },{ 0x00, 0x08 },{ 0x00, 0x01 },{ 0x00, 0x07 } },
+ { 0x1a, 0x00, 0x01, 0x05,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0d, 0x0f },{ 0x08, 0x07 },{ 0x02, 0x01 },{ 0x03, 0x03 } },
+ { 0x19, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x02, 0x00 },{ 0x0f, 0x0c },{ 0x02, 0x04 },{ 0x01, 0x02 },{ 0x01, 0x03 } },
+ { 0x0e, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x06 },{ 0x08, 0x04 },{ 0x07, 0x02 },{ 0x00, 0x07 } },
+ { 0x1e, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x02, 0x01 },{ 0x00, 0x00 },{ 0x0e, 0x07 },{ 0x00, 0x06 },{ 0x00, 0x02 },{ 0x00, 0x08 } },
+ { 0x16, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x02 },{ 0x00, 0x00 },{ 0x0e, 0x07 },{ 0x00, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x08 } },
+ { 0x1d, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x08, 0x08 },{ 0x02, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x07 } },
+ { 0x2d, 0x01, 0x00, 0x04,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x07 },{ 0x02, 0x02 },{ 0x00, 0x00 },{ 0x00, 0x07 } },
+ { 0x1b, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x06, 0x06 },{ 0x04, 0x05 },{ 0x01, 0x01 },{ 0x00, 0x07 } },
+ { 0x0b, 0x01, 0x01, 0x00,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x08, 0x0f },{ 0x05, 0x07 },{ 0x07, 0x00 },{ 0x01, 0x07 } },
+ { 0x03, 0x01, 0x00, 0x01,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x02, 0x00 },{ 0x0f, 0x0e },{ 0x0a, 0x04 },{ 0x01, 0x00 },{ 0x00, 0x04 } },
+ { 0x24, 0x00, 0x00, 0x07,{ 0x00, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x07, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x08, 0x08 },{ 0x02, 0x01 },{ 0x02, 0x02 } },
+ { 0x0c, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x0f },{ 0x02, 0x05 },{ 0x02, 0x04 },{ 0x00, 0x02 } },
+ { 0x15, 0x00, 0x00, 0x03,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x0c, 0x09 },{ 0x09, 0x05 },{ 0x00, 0x00 },{ 0x03, 0x02 } },
+ { 0x09, 0x00, 0x00, 0x03,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x02, 0x00 },{ 0x0f, 0x0e },{ 0x01, 0x04 },{ 0x04, 0x01 },{ 0x00, 0x03 } },
+
+ { 0x18, 0x00, 0x01, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0d, 0x00 },{ 0x0f, 0x00 },{ 0x06, 0x00 },{ 0x0a, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x04 },{ 0x00, 0x08 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } }
+};
+
+static const opll_patch_t patch_ymf281[opll_patch_max] = {
+ { 0x1a, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x02, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x06 },{ 0x0f, 0x07 },{ 0x00, 0x01 },{ 0x00, 0x06 } },
+ { 0x05, 0x00, 0x00, 0x01,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x0f, 0x08 },{ 0x06, 0x03 },{ 0x08, 0x00 },{ 0x00, 0x03 } },
+ { 0x16, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x02, 0x00 },{ 0x0f, 0x0d },{ 0x02, 0x03 },{ 0x01, 0x00 },{ 0x01, 0x03 } },
+ { 0x0b, 0x00, 0x01, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x06 },{ 0x08, 0x04 },{ 0x07, 0x01 },{ 0x00, 0x07 } },
+ { 0x1e, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x02, 0x01 },{ 0x00, 0x00 },{ 0x0e, 0x07 },{ 0x01, 0x06 },{ 0x00, 0x02 },{ 0x00, 0x08 } },
+ { 0x02, 0x00, 0x01, 0x06,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x02, 0x00 },{ 0x09, 0x06 },{ 0x0a, 0x01 },{ 0x02, 0x02 },{ 0x00, 0x07 } },
+ { 0x1b, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x08, 0x08 },{ 0x04, 0x03 },{ 0x01, 0x00 },{ 0x00, 0x07 } },
+ { 0x0a, 0x00, 0x00, 0x02,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x07, 0x02 },{ 0x03, 0x00 },{ 0x06, 0x06 },{ 0x06, 0x04 },{ 0x04, 0x02 },{ 0x00, 0x07 } },
+ { 0x07, 0x00, 0x00, 0x03,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0c, 0x07 },{ 0x05, 0x07 },{ 0x05, 0x00 },{ 0x01, 0x07 } },
+ { 0x1e, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x00 },{ 0x06, 0x01 },{ 0x01, 0x00 },{ 0x0f, 0x0f },{ 0x02, 0x03 },{ 0x0f, 0x0f },{ 0x00, 0x03 } },
+ { 0x18, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x0e },{ 0x05, 0x03 },{ 0x02, 0x01 },{ 0x00, 0x03 } },
+ { 0x24, 0x00, 0x00, 0x07,{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x07, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x08, 0x08 },{ 0x02, 0x00 },{ 0x02, 0x03 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x05, 0x04 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x0f, 0x03 },{ 0x07, 0x0f },{ 0x00, 0x05 } },
+ { 0x03, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x01 },{ 0x0f, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x0e },{ 0x0c, 0x03 },{ 0x03, 0x0f },{ 0x0f, 0x0c } },
+ { 0x00, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x01 },{ 0x00, 0x00 },{ 0x0b, 0x08 },{ 0x0f, 0x04 },{ 0x00, 0x0f },{ 0x00, 0x05 } },
+
+ { 0x18, 0x00, 0x01, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0d, 0x00 },{ 0x0f, 0x00 },{ 0x06, 0x00 },{ 0x0a, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x04 },{ 0x00, 0x08 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } }
+};
+
+static const opll_patch_t patch_ym2423[opll_patch_max] = {
+ { 0x1b, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x09, 0x05 },{ 0x04, 0x04 },{ 0x01, 0x00 },{ 0x00, 0x05 } },
+ { 0x12, 0x00, 0x00, 0x04,{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x03, 0x01 },{ 0x01, 0x00 },{ 0x0f, 0x0f },{ 0x03, 0x02 },{ 0x0a, 0x0e },{ 0x00, 0x09 } },
+ { 0x11, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x02 },{ 0x0f, 0x0f },{ 0x02, 0x02 },{ 0x05, 0x07 },{ 0x00, 0x05 } },
+ { 0x28, 0x00, 0x00, 0x07,{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x03, 0x02 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x03, 0x02 },{ 0x09, 0x0b },{ 0x00, 0x04 } },
+ { 0x17, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x02, 0x01 },{ 0x02, 0x00 },{ 0x05, 0x06 },{ 0x01, 0x0f },{ 0x07, 0x00 },{ 0x00, 0x09 } },
+ { 0x18, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x03, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x07, 0x04 },{ 0x05, 0x08 },{ 0x00, 0x05 } },
+ { 0x1c, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x05, 0x07 },{ 0x01, 0x01 },{ 0x02, 0x02 },{ 0x00, 0x06 } },
+ { 0x1b, 0x00, 0x00, 0x07,{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x04 },{ 0x00, 0x00 },{ 0x07, 0x03 },{ 0x03, 0x0f },{ 0x00, 0x00 },{ 0x00, 0x06 } },
+ { 0x0d, 0x00, 0x00, 0x03,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x04, 0x06 },{ 0x02, 0x0f },{ 0x02, 0x00 },{ 0x00, 0x06 } },
+ { 0x10, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x02 },{ 0x0f, 0x0f },{ 0x03, 0x03 },{ 0x02, 0x00 },{ 0x00, 0x04 } },
+ { 0x1b, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0c, 0x09 },{ 0x05, 0x06 },{ 0x0f, 0x0f },{ 0x00, 0x06 } },
+ { 0x1b, 0x00, 0x00, 0x00,{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x09, 0x01 },{ 0x03, 0x00 },{ 0x0f, 0x0f },{ 0x05, 0x03 },{ 0x07, 0x0f },{ 0x00, 0x02 } },
+ { 0x11, 0x00, 0x00, 0x03,{ 0x00, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x02 },{ 0x02, 0x00 },{ 0x09, 0x0b },{ 0x04, 0x01 },{ 0x0e, 0x0f },{ 0x00, 0x07 } },
+ { 0x17, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x0d, 0x0e },{ 0x03, 0x01 },{ 0x0b, 0x0e },{ 0x00, 0x0b } },
+ { 0x0d, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x06 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x02, 0x04 },{ 0x02, 0x09 },{ 0x00, 0x09 } },
+
+ { 0x18, 0x00, 0x01, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0d, 0x00 },{ 0x0f, 0x00 },{ 0x06, 0x00 },{ 0x0a, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x04 },{ 0x00, 0x08 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } }
+};
+
+static const uint32_t ch_offset[18] = {
+ 1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, 6, 7, 8, 6, 7, 8, 0
+};
+
+static const uint32_t pg_multi[16] = {
+ 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30
+};
+
+static const uint32_t eg_stephi[4][4] = {
+ { 0, 0, 0, 0 },
+ { 1, 0, 0, 0 },
+ { 1, 0, 1, 0 },
+ { 1, 1, 1, 0 }
+};
+
+static const uint32_t eg_ksltable[16] = {
+ 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64
+};
+
+static void OPLL_DoIO(opll_t *chip) {
+ /* Write signal check */
+ chip->write_a_en = (chip->write_a & 0x03) == 0x01;
+ chip->write_d_en = (chip->write_d & 0x03) == 0x01;
+ chip->write_a <<= 1;
+ chip->write_d <<= 1;
+}
+
+static void OPLL_DoModeWrite(opll_t *chip) {
+ if ((chip->write_mode_address & 0x10) && chip->write_d_en) {
+ uint8_t slot = chip->write_mode_address & 0x01;
+ switch (chip->write_mode_address & 0x0f) {
+ case 0x00:
+ case 0x01:
+ chip->patch.multi[slot] = chip->write_data & 0x0f;
+ chip->patch.ksr[slot] = (chip->write_data >> 4) & 0x01;
+ chip->patch.et[slot] = (chip->write_data >> 5) & 0x01; // enable sustain
+ chip->patch.vib[slot] = (chip->write_data >> 6) & 0x01;
+ chip->patch.am[slot] = (chip->write_data >> 7) & 0x01;
+ break;
+
+ case 0x02:
+ chip->patch.ksl[0] = (chip->write_data >> 6) & 0x03;
+ chip->patch.tl = chip->write_data & 0x3f;
+ break;
+
+ case 0x03:
+ chip->patch.ksl[1] = (chip->write_data >> 6) & 0x03;
+ chip->patch.dc = (chip->write_data >> 4) & 0x01;
+ chip->patch.dm = (chip->write_data >> 3) & 0x01;
+ chip->patch.fb = chip->write_data & 0x07;
+ break;
+
+ case 0x04:
+ case 0x05:
+ chip->patch.dr[slot] = chip->write_data & 0x0f;
+ chip->patch.ar[slot] = (chip->write_data >> 4) & 0x0f;
+ break;
+
+ case 0x06:
+ case 0x07:
+ chip->patch.rr[slot] = chip->write_data & 0x0f;
+ chip->patch.sl[slot] = (chip->write_data >> 4) & 0x0f;
+ break;
+
+ case 0x0e:
+ chip->rhythm = chip->write_data & 0x3f;
+ if (chip->chip_type == opll_type_ds1001) {
+ chip->rhythm |= 0x20;
+ }
+ chip->rm_enable = (chip->rm_enable & 0x7f) | ((chip->rhythm << 2) & 0x80);
+ break;
+
+ case 0x0f:
+ chip->testmode = chip->write_data & 0x0f;
+ break;
+ }
+ }
+}
+
+void OPLL_Reset(opll_t *chip, uint32_t chip_type) {
+ uint32_t i;
+ memset(chip, 0, sizeof(opll_t));
+ chip->chip_type = chip_type;
+ if (chip_type == opll_type_ds1001) {
+ /* Rhythm mode is always on */
+ chip->rhythm = 0x20;
+ chip->rm_enable = (int8_t)0x80;
+ }
+ switch (chip_type) {
+ case opll_type_ds1001:
+ chip->patchrom = patch_ds1001;
+ break;
+ case opll_type_ymf281:
+ case opll_type_ymf281b:
+ chip->patchrom = patch_ymf281;
+ break;
+ case opll_type_ym2423:
+ chip->patchrom = patch_ym2423;
+ break;
+ case opll_type_ym2413:
+ case opll_type_ym2413b:
+ case opll_type_ym2420:
+ default:
+ chip->patchrom = patch_ym2413;
+ break;
+ }
+ for (i = 0; i < 18; i++) {
+ chip->eg_state[i] = eg_num_release;
+ chip->eg_level[i] = 0x7f;
+ chip->eg_out = 0x7f;
+ }
+ chip->rm_select = rm_num_tc + 1;
+}
+
+static void OPLL_DoRegWrite(opll_t *chip) {
+ /* Address */
+ if (chip->write_a_en) {
+ if ((chip->write_data & 0xc0) == 0x00) {
+ /* FM Write */
+ chip->write_fm_address = 1;
+ chip->address = chip->write_data;
+ } else {
+ chip->write_fm_address = 0;
+ }
+ }
+ /* Data */
+ if (chip->write_fm_address && chip->write_d_en) {
+ chip->data = chip->write_data;
+ }
+
+ /* Update registers */
+ if (chip->write_fm_data && !chip->write_a_en) {
+ if ((chip->address & 0x0f) == chip->cycles && chip->cycles < 16) {
+ uint32_t channel = chip->cycles % 9;
+ switch (chip->address & 0xf0) {
+ case 0x10:
+ if (chip->chip_type == opll_type_ym2420)
+ {
+ chip->fnum[channel] = (chip->fnum[channel] & 0x0f) | ((chip->data & 0x1f) << 4);
+ chip->block[channel] = (chip->data >> 5) & 0x07;
+ }
+ else
+ chip->fnum[channel] = (chip->fnum[channel] & 0x100) | chip->data;
+ break;
+ case 0x20:
+ if (chip->chip_type == opll_type_ym2420)
+ chip->fnum[channel] = (chip->fnum[channel] & 0x1f0) | (chip->data & 0x0f);
+ else
+ {
+ chip->fnum[channel] = (chip->fnum[channel] & 0xff) | ((chip->data & 0x01) << 8);
+ chip->block[channel] = (chip->data >> 1) & 0x07;
+ }
+ chip->kon[channel] = (chip->data >> 4) & 0x01;
+ chip->son[channel] = (chip->data >> 5) & 0x01;
+ break;
+ case 0x30:
+ chip->vol[channel] = chip->data & 0x0f;
+ chip->inst[channel] = (chip->data >> 4) & 0x0f;
+ break;
+ }
+ }
+ }
+
+
+ if (chip->write_a_en) {
+ chip->write_fm_data = 0;
+ }
+ if (chip->write_fm_address && chip->write_d_en) {
+ chip->write_fm_data = 1;
+ }
+ if (chip->write_a_en) {
+ if (((chip->write_data & 0xf0) == 0x00)) {
+ chip->write_mode_address = 0x10 | (chip->write_data & 0x0f);
+ } else {
+ chip->write_mode_address = 0x00;
+ }
+ }
+
+}
+
+static void OPLL_PreparePatch1(opll_t *chip) {
+ uint8_t instr;
+ uint32_t mcsel = ((chip->cycles + 1) / 3) & 0x01;
+ uint32_t instr_index;
+ uint32_t ch = ch_offset[chip->cycles];
+ const opll_patch_t *patch;
+ instr = chip->inst[ch];
+ if (instr > 0) {
+ instr_index = opll_patch_1 + instr - 1;
+ }
+ if (chip->rm_select <= rm_num_tc) {
+ instr_index = opll_patch_drum_0 + chip->rm_select;
+ }
+ if (chip->rm_select <= rm_num_tc || instr > 0) {
+ patch = &chip->patchrom[instr_index];
+ } else {
+ patch = &chip->patch;
+ }
+ if (chip->rm_select == rm_num_hh || chip->rm_select == rm_num_tom) {
+ chip->c_tl = chip->inst[ch] << 2;
+ } else if (mcsel == 1) {
+ chip->c_tl = chip->vol[ch] << 2;
+ } else {
+ chip->c_tl = patch->tl;
+ }
+
+ chip->c_adrr[0] = patch->ar[mcsel];
+ chip->c_adrr[1] = patch->dr[mcsel];
+ chip->c_adrr[2] = patch->rr[mcsel];
+ chip->c_et = patch->et[mcsel];
+ chip->c_ksr = patch->ksr[mcsel];
+ chip->c_ksl = patch->ksl[mcsel];
+ chip->c_ksr_freq = (chip->block[ch] << 1) | (chip->fnum[ch] >> 8);
+ chip->c_ksl_freq = (chip->fnum[ch]>>5);
+ chip->c_ksl_block = (chip->block[ch]);
+}
+
+static void OPLL_PreparePatch2(opll_t *chip) {
+ uint8_t instr;
+ uint32_t mcsel = ((chip->cycles + 1) / 3) & 0x01;
+ uint32_t instr_index;
+ const opll_patch_t *patch;
+ instr = chip->inst[ch_offset[chip->cycles]];
+ if (instr > 0) {
+ instr_index = opll_patch_1 + instr - 1;
+ }
+ if (chip->rm_select <= rm_num_tc) {
+ instr_index = opll_patch_drum_0 + chip->rm_select;
+ }
+ if (chip->rm_select <= rm_num_tc || instr > 0) {
+ patch = &chip->patchrom[instr_index];
+ } else {
+ patch = &chip->patch;
+ }
+
+ chip->c_fnum = chip->fnum[ch_offset[chip->cycles]];
+ chip->c_block = chip->block[ch_offset[chip->cycles]];
+
+ chip->c_multi = patch->multi[mcsel];
+ chip->c_sl = patch->sl[mcsel];
+ chip->c_fb = patch->fb;
+ chip->c_vib = patch->vib[mcsel];
+ chip->c_am = patch->am[mcsel];
+ chip->c_dc <<= 1;
+ chip->c_dm <<= 1;
+ chip->c_dc |= patch->dc;
+ chip->c_dm |= patch->dm;
+}
+
+static void OPLL_PhaseGenerate(opll_t *chip) {
+ uint32_t ismod;
+ uint32_t phase;
+ uint16_t pg_out;
+ uint8_t rm_bit;
+
+ chip->pg_phase[(chip->cycles + 17) % 18] = chip->pg_phase_next + chip->pg_inc;
+
+ if ((chip->rm_enable & 0x40) && (chip->cycles == 13 || chip->cycles == 14)) {
+ ismod = 0;
+ } else {
+ ismod = ((chip->cycles + 3) / 3) & 1;
+ }
+ phase = chip->pg_phase[chip->cycles];
+ /* KeyOn event check */
+ if ((chip->testmode & 0x04)
+ || (ismod && (chip->eg_dokon & 0x8000)) || (!ismod && (chip->eg_dokon & 0x01))) {
+ chip->pg_phase_next = 0;
+ } else {
+ chip->pg_phase_next = phase;
+ }
+ /* Rhythm mode */
+ if (chip->cycles == 13) {
+ chip->rm_hh_bit2 = (phase >> (2 + 9)) & 1;
+ chip->rm_hh_bit3 = (phase >> (3 + 9)) & 1;
+ chip->rm_hh_bit7 = (phase >> (7 + 9)) & 1;
+ chip->rm_hh_bit8 = (phase >> (8 + 9)) & 1;
+ } else if (chip->cycles == 17 && (chip->rm_enable & 0x80)) {
+ chip->rm_tc_bit3 = (phase >> (3 + 9)) & 1;
+ chip->rm_tc_bit5 = (phase >> (5 + 9)) & 1;
+ }
+ if ((chip->rm_enable & 0x80)) {
+ switch (chip->cycles) {
+ case 13:
+ /* HH */
+ rm_bit = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7)
+ | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5)
+ | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5);
+ pg_out = rm_bit << 9;
+ if (rm_bit ^ (chip->rm_noise & 1)) {
+ pg_out |= 0xd0;
+ } else {
+ pg_out |= 0x34;
+ }
+ break;
+ case 16:
+ /* SD */
+ pg_out = (chip->rm_hh_bit8 << 9)
+ | ((chip->rm_hh_bit8 ^ (chip->rm_noise & 1)) << 8);
+ break;
+ case 17:
+ /* TC */
+ rm_bit = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7)
+ | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5)
+ | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5);
+ pg_out = (rm_bit << 9) | 0x100;
+ break;
+ default:
+ pg_out = phase >> 9;
+ }
+ } else {
+ pg_out = phase >> 9;
+ }
+ chip->pg_out = pg_out;
+}
+
+static void OPLL_PhaseCalcIncrement(opll_t *chip) {
+ uint32_t freq;
+ uint16_t block;
+ freq = chip->c_fnum << 1;
+ block = chip->c_block;
+ /* Apply vibrato */
+ if (chip->c_vib) {
+ switch (chip->lfo_vib_counter) {
+ case 0:
+ case 4:
+ break;
+ case 1:
+ case 3:
+ freq += freq >> 8;
+ break;
+ case 2:
+ freq += freq >> 7;
+ break;
+ case 5:
+ case 7:
+ freq -= freq >> 8;
+ break;
+ case 6:
+ freq -= freq >> 7;
+ break;
+ }
+ }
+ /* Apply block */
+ freq = (freq << block) >> 1;
+
+ chip->pg_inc = (freq * pg_multi[chip->c_multi]) >> 1;
+}
+
+static void OPLL_EnvelopeKSLTL(opll_t *chip) {
+ int32_t ksl;
+
+ ksl = eg_ksltable[chip->c_ksl_freq]-((8-chip->c_ksl_block)<<3);
+ if (ksl < 0) {
+ ksl = 0;
+ }
+
+ ksl <<= 1;
+
+ if (chip->c_ksl) {
+ ksl = ksl >> (3-chip->c_ksl);
+ } else {
+ ksl = 0;
+ }
+
+ chip->eg_ksltl = ksl + (chip->c_tl<<1);
+}
+
+static void OPLL_EnvelopeOutput(opll_t *chip) {
+ int32_t level = chip->eg_level[(chip->cycles+17)%18];
+
+ level += chip->eg_ksltl;
+
+ if (chip->c_am) {
+ level += chip->lfo_am_out;
+ }
+
+ if (level >= 128) {
+ level = 127;
+ }
+
+ if (chip->testmode & 0x01) {
+ level = 0;
+ }
+
+ chip->eg_out = level;
+}
+
+static void OPLL_EnvelopeGenerate(opll_t *chip) {
+ uint8_t timer_inc;
+ uint8_t timer_bit;
+ uint8_t timer_low;
+ uint8_t rate;
+ uint8_t state_rate;
+ uint8_t ksr;
+ uint8_t sum;
+ uint8_t rate_hi;
+ uint8_t rate_lo;
+ int32_t level;
+ int32_t next_level;
+ uint8_t zero;
+ uint8_t state;
+ uint8_t next_state;
+ int32_t step;
+ int32_t sl;
+ uint32_t mcsel = ((chip->cycles + 1) / 3) & 0x01;
+
+
+ /* EG timer */
+ if ((chip->eg_counter_state & 3) != 3) {
+ timer_inc = 0;
+ } else if (chip->cycles == 0) {
+ timer_inc = 1;
+ } else {
+ timer_inc = chip->eg_timer_carry;
+ }
+ timer_low = chip->eg_timer & 3;
+ timer_bit = chip->eg_timer & 1;
+ timer_bit += timer_inc;
+ chip->eg_timer_carry = timer_bit >> 1;
+ chip->eg_timer = ((timer_bit & 1) << 17) | (chip->eg_timer >> 1);
+ if (chip->testmode & 0x08) {
+ chip->eg_timer &= 0x2ffff;
+ chip->eg_timer |= (chip->write_data << (16 - 2)) & 0x10000;
+ }
+ if (!chip->eg_timer_shift_stop && ((chip->eg_timer >> 16) & 1)) {
+ chip->eg_timer_shift = chip->cycles;
+ }
+ if (chip->cycles == 0 && (chip->eg_counter_state_prev & 1) == 1) {
+ chip->eg_timer_low_lock = timer_low;
+ chip->eg_timer_shift_lock = chip->eg_timer_shift;
+ if (chip->eg_timer_shift_lock > 13)
+ chip->eg_timer_shift_lock = 0;
+
+ chip->eg_timer_shift = 0;
+ }
+ chip->eg_timer_shift_stop |= (chip->eg_timer >> 16) & 1;
+ if (chip->cycles == 0) {
+ chip->eg_timer_shift_stop = 0;
+ }
+ chip->eg_counter_state_prev = chip->eg_counter_state;
+ if (chip->cycles == 17) {
+ chip->eg_counter_state++;
+ }
+
+ level = chip->eg_level[(chip->cycles+16)%18];
+ next_level = level;
+ zero = level == 0;
+ chip->eg_silent = level == 0x7f;
+
+ if (chip->eg_state[(chip->cycles+16)%18] != eg_num_attack && (chip->eg_off&2) && !(chip->eg_dokon&2)) {
+ next_level = 0x7f;
+ }
+
+ if (chip->eg_maxrate && (chip->eg_dokon&2)) {
+ next_level = 0x00;
+ }
+
+
+ state = chip->eg_state[(chip->cycles+16)%18];
+ next_state = eg_num_attack;
+
+ step = 0;
+ sl = chip->eg_sl;
+
+ switch (state) {
+ case eg_num_attack:
+ if (!chip->eg_maxrate && (chip->eg_kon & 2) && !zero) {
+ int32_t shift = (chip->eg_rate_hi < 12) ? chip->eg_inc_lo : (chip->eg_rate_hi - 11 + chip->eg_inc_hi);
+ if (shift > 0) {
+ step = ~level >> (5 - shift);
+ }
+ }
+ if (zero) {
+ next_state = eg_num_decay;
+ } else {
+ next_state = eg_num_attack;
+ }
+ break;
+ case eg_num_decay:
+ if (!(chip->eg_off & 2) && !(chip->eg_dokon & 2) && (level >> 3) != sl)
+ {
+ uint8_t i0 = chip->eg_rate_hi == 15 || (chip->eg_rate_hi == 14 && chip->eg_inc_hi);
+ uint8_t i1 = (chip->eg_rate_hi == 14 && !chip->eg_inc_hi) || (chip->eg_rate_hi == 13 && chip->eg_inc_hi) ||
+ (chip->eg_rate_hi == 13 && !chip->eg_inc_hi && (chip->eg_counter_state_prev & 1))
+ || (chip->eg_rate_hi == 12 && chip->eg_inc_hi && (chip->eg_counter_state_prev & 1))
+ || (chip->eg_rate_hi == 12 && !chip->eg_inc_hi && ((chip->eg_counter_state_prev & 3) == 3))
+ || (chip->eg_inc_lo && ((chip->eg_counter_state_prev & 3) == 3));
+ step = (i0<<1) | i1;
+ }
+ if ((level >> 3) == sl) {
+ next_state = eg_num_sustain;
+ } else {
+ next_state = eg_num_decay;
+ }
+ break;
+ case eg_num_sustain:
+ case eg_num_release:
+ if (!(chip->eg_off & 2) && !(chip->eg_dokon & 2))
+ {
+ uint8_t i0 = chip->eg_rate_hi == 15 || (chip->eg_rate_hi == 14 && chip->eg_inc_hi);
+ uint8_t i1 = (chip->eg_rate_hi == 14 && !chip->eg_inc_hi) || (chip->eg_rate_hi == 13 && chip->eg_inc_hi) ||
+ (chip->eg_rate_hi == 13 && !chip->eg_inc_hi && (chip->eg_counter_state_prev & 1))
+ || (chip->eg_rate_hi == 12 && chip->eg_inc_hi && (chip->eg_counter_state_prev & 1))
+ || (chip->eg_rate_hi == 12 && !chip->eg_inc_hi && ((chip->eg_counter_state_prev & 3) == 3))
+ || (chip->eg_inc_lo && ((chip->eg_counter_state_prev & 3) == 3));
+ step = (i0<<1) | i1;
+ }
+ next_state = state;
+ break;
+ }
+
+ if (!(chip->eg_kon & 2)) {
+ next_state = eg_num_release;
+ }
+ if (chip->eg_dokon & 2) {
+ next_state = eg_num_attack;
+ }
+
+ chip->eg_level[(chip->cycles+16)%18] = next_level+step;
+ chip->eg_state[(chip->cycles+16)%18] = next_state;
+
+ rate_hi = chip->eg_rate >> 2;
+ rate_lo = chip->eg_rate & 3;
+ chip->eg_inc_hi = eg_stephi[rate_lo][chip->eg_timer_low_lock];
+ sum = (chip->eg_timer_shift_lock + rate_hi) & 0x0f;
+ chip->eg_inc_lo = 0;
+ if (rate_hi < 12 && !chip->eg_zerorate) {
+ switch (sum) {
+ case 12:
+ chip->eg_inc_lo = 1;
+ break;
+ case 13:
+ chip->eg_inc_lo = (rate_lo >> 1) & 1;
+ break;
+ case 14:
+ chip->eg_inc_lo = rate_lo & 1;
+ break;
+ }
+ }
+ chip->eg_maxrate = rate_hi == 0x0f;
+
+ chip->eg_rate_hi = rate_hi;
+
+ chip->eg_kon <<= 1;
+ chip->eg_kon |= chip->kon[ch_offset[chip->cycles]];
+ chip->eg_off <<= 1;
+ chip->eg_off |= (chip->eg_level[chip->cycles] >> 2) == 0x1f;
+ switch (chip->rm_select) {
+ case rm_num_bd0:
+ case rm_num_bd1:
+ chip->eg_kon |= (chip->rhythm >> 4) & 1;
+ break;
+ case rm_num_sd:
+ chip->eg_kon |= (chip->rhythm >> 3) & 1;
+ break;
+ case rm_num_tom:
+ chip->eg_kon |= (chip->rhythm >> 2) & 1;
+ break;
+ case rm_num_tc:
+ chip->eg_kon |= (chip->rhythm >> 1) & 1;
+ break;
+ case rm_num_hh:
+ chip->eg_kon |= chip->rhythm & 1;
+ break;
+ }
+
+ /* Calculate rate */
+ rate = 0;
+ chip->eg_dokon <<= 1;
+ state_rate = chip->eg_state[chip->cycles];
+ if (state_rate == eg_num_release && (chip->eg_kon&1) && (chip->eg_off&1)) {
+ state_rate = eg_num_attack;
+ chip->eg_dokon |= 1;
+ }
+ switch (state_rate) {
+ case eg_num_attack:
+ rate = chip->c_adrr[0];
+ break;
+ case eg_num_decay:
+ rate = chip->c_adrr[1];
+ break;
+ case eg_num_sustain:
+ if (!chip->c_et) {
+ rate = chip->c_adrr[2];
+ }
+ break;
+ case eg_num_release:
+ if (chip->son[ch_offset[chip->cycles]]) {
+ rate = 5;
+ } else {
+ rate = chip->c_adrr[2];
+ }
+ break;
+ }
+ if (!(chip->eg_kon&1) && !mcsel && chip->rm_select != rm_num_tom && chip->rm_select != rm_num_hh) {
+ rate = 0;
+ }
+ if ((chip->eg_kon&1) && chip->eg_state[chip->cycles] == eg_num_release && !(chip->eg_off&1)) {
+ rate = 12;
+ }
+ if (!(chip->eg_kon&1) && !chip->son[ch_offset[chip->cycles]] && mcsel == 1 && !chip->c_et) {
+ rate = 7;
+ }
+ chip->eg_zerorate = rate == 0;
+ ksr = chip->c_ksr_freq;
+ if (!chip->c_ksr)
+ ksr >>= 2;
+ chip->eg_rate = (rate << 2) + ksr;
+ if (chip->eg_rate & 0x40) {
+ chip->eg_rate = 0x3c | (ksr & 3);
+ }
+ chip->eg_sl = chip->c_sl;
+}
+
+static void OPLL_Channel(opll_t *chip) {
+ int16_t ch_out = chip->ch_out;
+ uint8_t ismod = (chip->cycles / 3) & 1;
+ uint8_t mute_m = ismod || ((chip->rm_enable&0x40) && (chip->cycles+15)%18 >= 12);
+ if (chip->chip_type == opll_type_ds1001) {
+ chip->output_m = ch_out;
+ if (chip->output_m >= 0) {
+ chip->output_m++;
+ }
+ if (mute_m) {
+ chip->output_m = 0;
+ }
+ chip->output_r = 0;
+ return;
+ } else {
+ uint8_t mute_r = 1;
+ /* TODO: This might be incorrect */
+ if ((chip->rm_enable & 0x40)) {
+ switch (chip->cycles) {
+ case 16: /* HH */
+ case 17: /* TOM */
+ case 0: /* BD */
+ case 1: /* SD */
+ case 2: /* TC */
+ case 3: /* HH */
+ case 4: /* TOM */
+ case 5: /* BD */
+ case 9: /* TOM */
+ case 10: /* TOM */
+ mute_r = 0;
+ break;
+ }
+ }
+ if (chip->chip_type == opll_type_ym2413b || chip->chip_type == opll_type_ymf281b) {
+ if (mute_m)
+ chip->output_m = 0;
+ else
+ chip->output_m = ch_out;
+ if (mute_r)
+ chip->output_r = 0;
+ else
+ chip->output_r = ch_out;
+ } else {
+ int16_t sign = ch_out >> 8;
+ if (ch_out >= 0) {
+ ch_out++;
+ sign++;
+ }
+ if (mute_m)
+ chip->output_m = sign;
+ else
+ chip->output_m = ch_out;
+ if (mute_r)
+ chip->output_r = sign;
+ else
+ chip->output_r = ch_out;
+ }
+ }
+}
+
+static void OPLL_Operator(opll_t *chip) {
+ uint8_t ismod1, ismod2, ismod3;
+ uint32_t op_mod;
+ uint16_t exp_shift;
+ int16_t output;
+ uint32_t level;
+ uint32_t phase;
+ int16_t routput;
+ if ((chip->rm_enable & 0x80) && (chip->cycles == 15 || chip->cycles == 16)) {
+ ismod1 = 0;
+ } else {
+ ismod1 = ((chip->cycles + 1) / 3) & 1;
+ }
+ if ((chip->rm_enable & 0x40) && (chip->cycles == 13 || chip->cycles == 14)) {
+ ismod2 = 0;
+ } else {
+ ismod2 = ((chip->cycles + 3) / 3) & 1;
+ }
+ if ((chip->rm_enable & 0x40) && (chip->cycles == 16 || chip->cycles == 17)) {
+ ismod3 = 0;
+ } else {
+ ismod3 = (chip->cycles / 3) & 1;
+ }
+
+ op_mod = 0;
+
+ if (ismod3) {
+ op_mod |= chip->op_mod << 1;
+ }
+
+ if (ismod2 && chip->c_fb) {
+ op_mod |= chip->op_fbsum >> (7 - chip->c_fb);
+ }
+
+ exp_shift = chip->op_exp_s;
+ if (chip->eg_silent || ((chip->op_neg&2) && (ismod1 ? (chip->c_dm&4) : (chip->c_dc&4)))) {
+ exp_shift |= 12;
+ }
+
+ output = chip->op_exp_m>>exp_shift;
+ if (!chip->eg_silent && (chip->op_neg&2)) {
+ output = ~output;
+ }
+
+ level = chip->op_logsin+(chip->eg_out<<4);
+ if (level >= 4096) {
+ level = 4095;
+ }
+
+ chip->op_exp_m = exprom[level & 0xff];
+ chip->op_exp_s = level >> 8;
+
+ phase = (op_mod + chip->pg_out) & 0x3ff;
+ if (phase & 0x100) {
+ phase ^= 0xff;
+ }
+ chip->op_logsin = logsinrom[phase & 0xff];
+ chip->op_neg <<= 1;
+ chip->op_neg |= phase >> 9;
+ chip->op_fbsum = (chip->op_fb1[(chip->cycles + 3) % 9] + chip->op_fb2[(chip->cycles + 3) % 9]) >> 1;
+
+ if (ismod1) {
+ chip->op_fb2[chip->cycles%9] = chip->op_fb1[chip->cycles%9];
+ chip->op_fb1[chip->cycles%9] = output;
+ }
+ chip->op_mod = output&0x1ff;
+
+ if (chip->chip_type == opll_type_ds1001) {
+ routput = 0;
+ } else {
+ switch (chip->cycles) {
+ case 2:
+ routput = chip->ch_out_hh;
+ break;
+ case 3:
+ routput = chip->ch_out_tm;
+ break;
+ case 4:
+ routput = chip->ch_out_bd;
+ break;
+ case 8:
+ routput = chip->ch_out_sd;
+ break;
+ case 9:
+ routput = chip->ch_out_tc;
+ break;
+ default:
+ routput = 0; /* TODO: Not quite true */
+ break;
+ }
+ switch (chip->cycles) {
+ case 15:
+ chip->ch_out_hh = output>>3;
+ break;
+ case 16:
+ chip->ch_out_tm = output>>3;
+ break;
+ case 17:
+ chip->ch_out_bd = output>>3;
+ break;
+ case 0:
+ chip->ch_out_sd = output>>3;
+ break;
+ case 1:
+ chip->ch_out_tc = output>>3;
+ break;
+ default:
+ break;
+ }
+ }
+
+ chip->ch_out = ismod1 ? routput : (output>>3);
+}
+
+static void OPLL_DoRhythm(opll_t *chip) {
+ uint8_t nbit;
+
+ /* Noise */
+ nbit = (chip->rm_noise ^ (chip->rm_noise >> 14)) & 0x01;
+ nbit |= (chip->rm_noise == 0x00) | ((chip->testmode >> 1) & 0x01);
+ chip->rm_noise = (nbit << 22) | (chip->rm_noise >> 1);
+}
+
+static void OPLL_DoLFO(opll_t *chip) {
+ uint8_t am_inc = 0;
+ uint8_t am_bit;
+
+ /* Update counter */
+ if (chip->cycles == 17) {
+ uint8_t vib_step = ((chip->lfo_counter & 0x3ff) + 1) >> 10;
+ chip->lfo_am_step = ((chip->lfo_counter & 0x3f) + 1) >> 6;
+ vib_step |= (chip->testmode >> 3) & 0x01;
+ chip->lfo_vib_counter += vib_step;
+ chip->lfo_vib_counter &= 0x07;
+ chip->lfo_counter++;
+ }
+
+ /* LFO AM */
+ if ((chip->lfo_am_step || (chip->testmode & 0x08)) && chip->cycles < 9) {
+ am_inc = chip->lfo_am_dir | (chip->cycles == 0);
+ }
+
+ if (chip->cycles >= 9) {
+ chip->lfo_am_car = 0;
+ }
+
+ if (chip->cycles == 0) {
+ if (chip->lfo_am_dir && (chip->lfo_am_counter & 0x7f) == 0) {
+ chip->lfo_am_dir = 0;
+ } else if (!chip->lfo_am_dir && (chip->lfo_am_counter & 0x69) == 0x69) {
+ chip->lfo_am_dir = 1;
+ }
+ }
+
+ am_bit = chip->lfo_am_counter & 0x01;
+ am_bit += am_inc + chip->lfo_am_car;
+ chip->lfo_am_car = am_bit >> 1;
+ am_bit &= 0x01;
+ chip->lfo_am_counter = (am_bit << 8) | (chip->lfo_am_counter >> 1);
+
+
+ /* Reset LFO */
+ if (chip->testmode & 0x02) {
+ chip->lfo_vib_counter = 0;
+ chip->lfo_counter = 0;
+ chip->lfo_am_dir = 0;
+ chip->lfo_am_counter &= 0xff;
+ }
+}
+
+
+void OPLL_Clock(opll_t *chip, int32_t *buffer) {
+ buffer[0] = chip->output_m;
+ buffer[1] = chip->output_r;
+ if (chip->cycles == 0) {
+ chip->lfo_am_out = (chip->lfo_am_counter >> 3) & 0x0f;
+ }
+ chip->rm_enable >>= 1;
+ OPLL_DoModeWrite(chip);
+ chip->rm_select++;
+ if (chip->rm_select > rm_num_tc) {
+ chip->rm_select = rm_num_tc + 1;
+ }
+ if (chip->cycles == 11 && (chip->rm_enable & 0x80) == 0x80) {
+ chip->rm_select = rm_num_bd0;
+ }
+ OPLL_PreparePatch1(chip);
+
+ OPLL_Channel(chip);
+
+ OPLL_PhaseGenerate(chip);
+
+ OPLL_Operator(chip);
+
+ OPLL_PhaseCalcIncrement(chip);
+
+ OPLL_EnvelopeOutput(chip);
+ OPLL_EnvelopeKSLTL(chip);
+ OPLL_EnvelopeGenerate(chip);
+
+ OPLL_DoLFO(chip);
+ OPLL_DoRhythm(chip);
+ OPLL_PreparePatch2(chip);
+ OPLL_DoRegWrite(chip);
+ OPLL_DoIO(chip);
+ chip->cycles = (chip->cycles + 1) % 18;
+
+}
+
+
+void OPLL_Write(opll_t *chip, uint32_t port, uint8_t data) {
+ chip->write_data = data;
+ if (port & 1) {
+ /* Data */
+ chip->write_d |= 1;
+ } else {
+ /* Address */
+ chip->write_a |= 1;
+ }
+}
diff --git a/verilog/jtopl/doc/opll.h b/verilog/jtopl/doc/opll.h
new file mode 100644
index 0000000..c3e02e8
--- /dev/null
+++ b/verilog/jtopl/doc/opll.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2019 Nuke.YKT
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * Yamaha YM2413 emulator
+ * Thanks:
+ * siliconpr0n.org(digshadow, John McMaster):
+ * VRC VII decap and die shot.
+ *
+ * version: 1.0.1
+ */
+
+#ifndef OPLL_H
+#define OPLL_H
+
+#include
+
+enum {
+ opll_type_ym2413 = 0x00, /* Yamaha YM2413 */
+ opll_type_ds1001, /* Konami VRC VII */
+ opll_type_ym2413b, /* Yamaha YM2413B */
+ opll_type_ymf281, /* Yamaha YMF281 */
+ opll_type_ymf281b, /* Yamaha YMF281B */
+ opll_type_ym2420, /* Yamaha YM2420 */
+ opll_type_ym2423, /* Yamaha YM2423 */
+};
+
+enum {
+ opll_patch_1 = 0x00,
+ opll_patch_2,
+ opll_patch_3,
+ opll_patch_4,
+ opll_patch_5,
+ opll_patch_6,
+ opll_patch_7,
+ opll_patch_8,
+ opll_patch_9,
+ opll_patch_10,
+ opll_patch_11,
+ opll_patch_12,
+ opll_patch_13,
+ opll_patch_14,
+ opll_patch_15,
+ opll_patch_drum_0,
+ opll_patch_drum_1,
+ opll_patch_drum_2,
+ opll_patch_drum_3,
+ opll_patch_drum_4,
+ opll_patch_drum_5,
+ opll_patch_max
+};
+
+typedef struct {
+ uint8_t tl;
+ uint8_t dc;
+ uint8_t dm;
+ uint8_t fb;
+ uint8_t am[2];
+ uint8_t vib[2];
+ uint8_t et[2];
+ uint8_t ksr[2];
+ uint8_t multi[2];
+ uint8_t ksl[2];
+ uint8_t ar[2];
+ uint8_t dr[2];
+ uint8_t sl[2];
+ uint8_t rr[2];
+} opll_patch_t;
+
+typedef struct {
+ uint32_t chip_type;
+ uint32_t cycles;
+ uint32_t slot;
+ const opll_patch_t *patchrom;
+ /* IO */
+ uint8_t write_data;
+ uint8_t write_a;
+ uint8_t write_d;
+ uint8_t write_a_en;
+ uint8_t write_d_en;
+ uint8_t write_fm_address;
+ uint8_t write_fm_data;
+ uint8_t write_mode_address;
+ uint8_t address;
+ uint8_t data;
+ /* Envelope generator */
+ uint8_t eg_counter_state;
+ uint8_t eg_counter_state_prev;
+ uint32_t eg_timer;
+ uint8_t eg_timer_low_lock;
+ uint8_t eg_timer_carry;
+ uint8_t eg_timer_shift;
+ uint8_t eg_timer_shift_lock;
+ uint8_t eg_timer_shift_stop;
+ uint8_t eg_state[18];
+ uint8_t eg_level[18];
+ uint8_t eg_kon;
+ uint32_t eg_dokon;
+ uint8_t eg_off;
+ uint8_t eg_rate;
+ uint8_t eg_maxrate;
+ uint8_t eg_zerorate;
+ uint8_t eg_inc_lo;
+ uint8_t eg_inc_hi;
+ uint8_t eg_rate_hi;
+ uint16_t eg_sl;
+ uint16_t eg_ksltl;
+ uint8_t eg_out;
+ uint8_t eg_silent;
+ /* Phase generator */
+ uint16_t pg_fnum;
+ uint8_t pg_block;
+ uint16_t pg_out;
+ uint32_t pg_inc;
+ uint32_t pg_phase[18];
+ uint32_t pg_phase_next;
+ /* Operator */
+ int16_t op_fb1[9];
+ int16_t op_fb2[9];
+ int16_t op_fbsum;
+ int16_t op_mod;
+ uint8_t op_neg;
+ uint16_t op_logsin;
+ uint16_t op_exp_m;
+ uint16_t op_exp_s;
+ /* Channel */
+ int16_t ch_out;
+ int16_t ch_out_hh;
+ int16_t ch_out_tm;
+ int16_t ch_out_bd;
+ int16_t ch_out_sd;
+ int16_t ch_out_tc;
+ /* LFO */
+ uint16_t lfo_counter;
+ uint8_t lfo_vib_counter;
+ uint16_t lfo_am_counter;
+ uint8_t lfo_am_step;
+ uint8_t lfo_am_dir;
+ uint8_t lfo_am_car;
+ uint8_t lfo_am_out;
+ /* Register set */
+ uint16_t fnum[9];
+ uint8_t block[9];
+ uint8_t kon[9];
+ uint8_t son[9];
+ uint8_t vol[9];
+ uint8_t inst[9];
+ uint8_t rhythm;
+ uint8_t testmode;
+ opll_patch_t patch;
+ uint8_t c_instr;
+ uint8_t c_op;
+ uint8_t c_tl;
+ uint8_t c_dc;
+ uint8_t c_dm;
+ uint8_t c_fb;
+ uint8_t c_am;
+ uint8_t c_vib;
+ uint8_t c_et;
+ uint8_t c_ksr;
+ uint8_t c_ksr_freq;
+ uint8_t c_ksl_freq;
+ uint8_t c_ksl_block;
+ uint8_t c_multi;
+ uint8_t c_ksl;
+ uint8_t c_adrr[3];
+ uint8_t c_sl;
+ uint16_t c_fnum;
+ uint16_t c_block;
+ /* Rhythm mode */
+ int8_t rm_enable;
+ uint32_t rm_noise;
+ uint32_t rm_select;
+ uint8_t rm_hh_bit2;
+ uint8_t rm_hh_bit3;
+ uint8_t rm_hh_bit7;
+ uint8_t rm_hh_bit8;
+ uint8_t rm_tc_bit3;
+ uint8_t rm_tc_bit5;
+
+ int16_t output_m;
+ int16_t output_r;
+
+} opll_t;
+
+void OPLL_Reset(opll_t *chip, uint32_t chip_type);
+void OPLL_Clock(opll_t *chip, int32_t *buffer);
+void OPLL_Write(opll_t *chip, uint32_t port, uint8_t data);
+#endif
diff --git a/verilog/jtopl/doc/opll_patches.c b/verilog/jtopl/doc/opll_patches.c
new file mode 100644
index 0000000..b71d291
--- /dev/null
+++ b/verilog/jtopl/doc/opll_patches.c
@@ -0,0 +1,69 @@
+#include "opll.h"
+#include
+
+opll_patch_t patch_ym2413[opll_patch_max] = {
+ { 0x1e, 0x01, 0x00, 0x07,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0d, 0x07 },{ 0x00, 0x08 },{ 0x00, 0x01 },{ 0x00, 0x07 } },
+ { 0x1a, 0x00, 0x01, 0x05,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0d, 0x0f },{ 0x08, 0x07 },{ 0x02, 0x01 },{ 0x03, 0x03 } },
+ { 0x19, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x02, 0x00 },{ 0x0f, 0x0c },{ 0x02, 0x04 },{ 0x01, 0x02 },{ 0x01, 0x03 } },
+ { 0x0e, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x06 },{ 0x08, 0x04 },{ 0x07, 0x02 },{ 0x00, 0x07 } },
+ { 0x1e, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x02, 0x01 },{ 0x00, 0x00 },{ 0x0e, 0x07 },{ 0x00, 0x06 },{ 0x00, 0x02 },{ 0x00, 0x08 } },
+ { 0x16, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x02 },{ 0x00, 0x00 },{ 0x0e, 0x07 },{ 0x00, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x08 } },
+ { 0x1d, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x08, 0x08 },{ 0x02, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x07 } },
+ { 0x2d, 0x01, 0x00, 0x04,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x07 },{ 0x02, 0x02 },{ 0x00, 0x00 },{ 0x00, 0x07 } },
+ { 0x1b, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x06, 0x06 },{ 0x04, 0x05 },{ 0x01, 0x01 },{ 0x00, 0x07 } },
+ { 0x0b, 0x01, 0x01, 0x00,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x08, 0x0f },{ 0x05, 0x07 },{ 0x07, 0x00 },{ 0x01, 0x07 } },
+ { 0x03, 0x01, 0x00, 0x01,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x02, 0x00 },{ 0x0f, 0x0e },{ 0x0a, 0x04 },{ 0x01, 0x00 },{ 0x00, 0x04 } },
+ { 0x24, 0x00, 0x00, 0x07,{ 0x00, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x07, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x08, 0x08 },{ 0x02, 0x01 },{ 0x02, 0x02 } },
+ { 0x0c, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x0f },{ 0x02, 0x05 },{ 0x02, 0x04 },{ 0x00, 0x02 } },
+ { 0x15, 0x00, 0x00, 0x03,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x0c, 0x09 },{ 0x09, 0x05 },{ 0x00, 0x00 },{ 0x03, 0x02 } },
+ { 0x09, 0x00, 0x00, 0x03,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x02, 0x00 },{ 0x0f, 0x0e },{ 0x01, 0x04 },{ 0x04, 0x01 },{ 0x00, 0x03 } },
+
+ { 0x18, 0x00, 0x01, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0d, 0x00 },{ 0x0f, 0x00 },{ 0x06, 0x00 },{ 0x0a, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x04 },{ 0x00, 0x08 } },
+ { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } }
+};
+
+void make_bin(char *data) {
+ for( int k=0; kam[0]<<7)
+ | (p->vib[0]<<6)
+ | (p->et[0]<<5)
+ | (p->ksr[0]<<4)
+ | (p->multi[0]);
+ d[1]=(p->am[1]<<7)
+ | (p->vib[1]<<6)
+ | (p->et[1]<<5)
+ | (p->ksr[1]<<4)
+ | (p->multi[1]);
+ d[2]=(p->ksl[0]<<6) | (p->tl);
+ d[3]=(p->ksl[1]<<6) | (p->dc<<4)
+ | (p->dm<<3) | (p->fb);
+ d[4]=(p->ar[0]<<4) | (p->dr[0]);
+ d[5]=(p->ar[1]<<4) | (p->dr[1]);
+ d[6]=(p->sl[0]<<4) | (p->rr[0]);
+ d[7]=(p->sl[1]<<4) | (p->rr[1]);
+ for( int j=0; j<8; j++ ) {
+ *data++ = d[j];
+ }
+ }
+}
+
+int main() {
+ char data[8*21];
+ make_bin(data);
+ for( int j=0;j<21;j++ ) {
+ printf(" patch[%02d]=64'h",j+1);
+ char *p = data+8*(j+1)-1;
+ for( int k=7; k>=0; k-- ) {
+ printf("%02X",(unsigned)(*p--)&0xff);
+ }
+ printf(";\n");
+ }
+ return 0;
+}
+
diff --git a/verilog/jtopl/doc/ym2413 app notes.pdf b/verilog/jtopl/doc/ym2413 app notes.pdf
new file mode 100644
index 0000000..cc601d3
Binary files /dev/null and b/verilog/jtopl/doc/ym2413 app notes.pdf differ
diff --git a/verilog/jtopl/hdl/common.yaml b/verilog/jtopl/hdl/common.yaml
new file mode 100644
index 0000000..ec7e461
--- /dev/null
+++ b/verilog/jtopl/hdl/common.yaml
@@ -0,0 +1,28 @@
+here:
+ - jtopl_acc.v
+ - jtopl_csr.v
+ - jtopl_div.v
+ - jtopl_lfo.v
+ - jtopl_pm.v
+ - jtopl_eg_cnt.v
+ - jtopl_eg_comb.v
+ - jtopl_eg_ctrl.v
+ - jtopl_eg_final.v
+ - jtopl_eg_pure.v
+ - jtopl_eg_step.v
+ - jtopl_eg.v
+ - jtopl_exprom.v
+ - jtopl_logsin.v
+ - jtopl_op.v
+ - jtopl_pg_comb.v
+ - jtopl_pg_inc.v
+ - jtopl_pg_sum.v
+ - jtopl_pg_rhy.v
+ - jtopl_pg.v
+ - jtopl_reg_ch.v
+ - jtopl_sh_rst.v
+ - jtopl_sh.v
+ - jtopl_slot_cnt.v
+ - jtopl_single_acc.v
+ - jtopl_timers.v
+ - jtopl_noise.v
diff --git a/verilog/jtopl/hdl/jt2413.v b/verilog/jtopl/hdl/jt2413.v
new file mode 100644
index 0000000..339a3b9
--- /dev/null
+++ b/verilog/jtopl/hdl/jt2413.v
@@ -0,0 +1,212 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 10-6-2020
+
+ */
+
+module jt2413(
+ input rst, // rst should be at least 6 clk&cen cycles long
+ input clk, // CPU clock
+ input cen, // optional clock enable, it not needed leave as 1'b1
+ input [ 7:0] din,
+ input addr,
+ input cs_n,
+ input wr_n,
+ // combined output
+ output signed [15:0] snd,
+ output sample
+);
+
+parameter OPL_TYPE=11;
+
+wire cenop, write, zero;
+wire [ 1:0] group;
+wire [17:0] slot;
+wire [ 3:0] trem;
+
+// Phase
+wire [ 8:0] fnum_I;
+wire [ 2:0] block_I;
+wire [ 3:0] mul_II;
+wire [ 9:0] phase_IV;
+wire pg_rst_II;
+wire viben_I;
+wire [ 2:0] vib_cnt;
+// envelope configuration
+wire en_sus_I; // enable sustain
+wire [ 3:0] keycode_II;
+wire [ 3:0] arate_I; // attack rate
+wire [ 3:0] drate_I; // decay rate
+wire [ 3:0] rrate_I; // release rate
+wire [ 3:0] sl_I; // sustain level
+wire ksr_II; // key scale rate - affects rates
+wire [ 1:0] ksl_IV; // key scale level - affects amplitude
+// envelope operation
+wire keyon_I;
+wire eg_stop;
+// envelope number
+wire amen_IV;
+wire [ 5:0] tl_IV;
+wire [ 9:0] eg_V;
+// Global values
+wire am_dep, vib_dep, rhy_en;
+// Operator
+wire [ 2:0] fb_I;
+wire [ 1:0] wavsel_I;
+wire op, con_I, op_out, con_out;
+
+wire signed [12:0] op_result;
+
+assign write = !cs_n && !wr_n;
+assign eg_stop = 0;
+assign sample = zero;
+
+jtopll_mmr #(.OPL_TYPE(OPL_TYPE)) u_mmr(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cen ), // external clock enable
+ .cenop ( cenop ), // internal clock enable
+ .din ( din ),
+ .write ( write ),
+ .addr ( addr ),
+ // location
+ .zero ( zero ),
+ .group ( group ),
+ .op ( op ),
+ .slot ( slot ),
+ .rhy_en ( rhy_en ),
+ // Phase Generator
+ .fnum_I ( fnum_I ),
+ .block_I ( block_I ),
+ .mul_II ( mul_II ),
+ // Operator
+ .wavsel_I ( wavsel_I ),
+ // Envelope Generator
+ .keyon_I ( keyon_I ),
+ .en_sus_I ( en_sus_I ),
+ .arate_I ( arate_I ),
+ .drate_I ( drate_I ),
+ .rrate_I ( rrate_I ),
+ .sl_I ( sl_I ),
+ .ks_II ( ksr_II ),
+ .tl_IV ( tl_IV ),
+ .ksl_IV ( ksl_IV ),
+ .amen_IV ( amen_IV ),
+ .viben_I ( viben_I ),
+ // Global Values
+ .am_dep ( am_dep ),
+ .vib_dep ( vib_dep ),
+ // Timbre
+ .fb_I ( fb_I ),
+ .con_I ( con_I )
+);
+
+jtopl_lfo u_lfo(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cenop ( cenop ),
+ .slot ( slot ),
+ .vib_cnt ( vib_cnt ),
+ .trem ( trem )
+);
+
+jtopl_pg u_pg(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cenop ( cenop ),
+ .slot ( slot ),
+ .rhy_en ( rhy_en ),
+ // Channel frequency
+ .fnum_I ( { fnum_I, 1'b0 } ),
+ .block_I ( block_I ),
+ // Operator multiplying
+ .mul_II ( mul_II ),
+ // phase modulation from LFO (vibrato at 6.4Hz)
+ .vib_cnt ( vib_cnt ),
+ .vib_dep ( vib_dep ),
+ .viben_I ( viben_I ),
+ // phase operation
+ .pg_rst_II ( pg_rst_II ),
+
+ .keycode_II ( keycode_II ),
+ .phase_IV ( phase_IV )
+);
+
+jtopl_eg u_eg(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cenop ( cenop ),
+ .zero ( zero ),
+ .eg_stop ( eg_stop ),
+ // envelope configuration
+ .en_sus_I ( en_sus_I ), // enable sustain
+ .keycode_II ( keycode_II ),
+ .arate_I ( arate_I ), // attack rate
+ .drate_I ( drate_I ), // decay rate
+ .rrate_I ( rrate_I ), // release rate
+ .sl_I ( sl_I ), // sustain level
+ .ksr_II ( ksr_II ), // key scale
+ // envelope operation
+ .keyon_I ( keyon_I ),
+ // envelope number
+ .fnum_I ( { fnum_I, 1'b0 } ),
+ .block_I ( block_I ),
+ .lfo_mod ( trem ),
+ .amsen_IV ( amen_IV ),
+ .ams_IV ( am_dep ),
+ .tl_IV ( tl_IV ),
+ .ksl_IV ( ksl_IV ),
+ .eg_V ( eg_V ),
+ .pg_rst_II ( pg_rst_II )
+);
+
+jtopl_op #(.OPL_TYPE(OPL_TYPE)) u_op(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cenop ( cenop ),
+
+ // location of current operator
+ .group ( group ),
+ .op ( op ),
+ .zero ( zero ),
+
+ .pg_phase_I ( phase_IV ),
+ .eg_atten_II( eg_V ), // output from envelope generator
+ .fb_I ( fb_I ), // voice feedback
+ .wavsel_I ( wavsel_I ), // sine mask (OPL2)
+
+ .con_I ( con_I ),
+ .op_result ( op_result ),
+ .op_out ( op_out ),
+ .con_out ( con_out )
+);
+
+jtopl_acc u_acc(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cenop ( cenop ),
+ .zero ( zero ),
+ .op_result ( op_result ),
+ .op ( op_out ),
+ .con ( con_out ),
+ .snd ( snd )
+);
+
+
+endmodule
+
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jt2413.yaml b/verilog/jtopl/hdl/jt2413.yaml
new file mode 100644
index 0000000..7767623
--- /dev/null
+++ b/verilog/jtopl/hdl/jt2413.yaml
@@ -0,0 +1,5 @@
+here:
+ - common.yaml
+ - jt2413.v
+ - jtopll_mmr.v
+ - jtopll_reg.v
diff --git a/verilog/jtopl/hdl/jt26.qip b/verilog/jtopl/hdl/jt26.qip
new file mode 100644
index 0000000..8f3994c
--- /dev/null
+++ b/verilog/jtopl/hdl/jt26.qip
@@ -0,0 +1,30 @@
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_acc.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_csr.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_div.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_lfo.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pm.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_cnt.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_comb.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_ctrl.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_final.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_pure.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_step.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_exprom.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_logsin.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_mmr.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_op.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pg_comb.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pg_inc.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pg_sum.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pg_rhy.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pg.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_reg.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_sh_rst.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_sh.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_single_acc.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_timers.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_noise.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_slot_cnt.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_reg_ch.v]
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl.v]
diff --git a/verilog/jtopl/hdl/jt26.yaml b/verilog/jtopl/hdl/jt26.yaml
new file mode 100644
index 0000000..9db083a
--- /dev/null
+++ b/verilog/jtopl/hdl/jt26.yaml
@@ -0,0 +1,5 @@
+here:
+ - common.yaml
+ - jtopl_mmr.v
+ - jtopl_reg.v
+ - jtopl.v
diff --git a/verilog/jtopl/hdl/jtopl.v b/verilog/jtopl/hdl/jtopl.v
new file mode 100644
index 0000000..51f54e3
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl.v
@@ -0,0 +1,253 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 10-6-2020
+
+ */
+
+module jtopl(
+ input rst, // rst should be at least 6 clk&cen cycles long
+ input clk, // CPU clock
+ input cen, // optional clock enable, it not needed leave as 1'b1
+ input [ 7:0] din,
+ input addr,
+ input cs_n,
+ input wr_n,
+ output [ 7:0] dout,
+ output irq_n,
+ // combined output
+ output signed [15:0] snd,
+ output sample
+);
+
+parameter OPL_TYPE=1;
+
+wire cenop;
+wire write;
+wire [ 1:0] group;
+wire [17:0] slot;
+wire [ 3:0] trem;
+
+// Timers
+wire flag_A, flag_B, flagen_A, flagen_B;
+wire [ 7:0] value_A;
+wire [ 7:0] value_B;
+wire load_A, load_B;
+wire clr_flag_A, clr_flag_B;
+wire overflow_A;
+wire zero; // Single-clock pulse at the begginig of s1_enters
+
+// Phase
+wire [ 9:0] fnum_I;
+wire [ 2:0] block_I;
+wire [ 3:0] mul_II;
+wire [ 9:0] phase_IV;
+wire pg_rst_II;
+wire viben_I;
+wire [ 2:0] vib_cnt;
+// envelope configuration
+wire en_sus_I; // enable sustain
+wire [ 3:0] keycode_II;
+wire [ 3:0] arate_I; // attack rate
+wire [ 3:0] drate_I; // decay rate
+wire [ 3:0] rrate_I; // release rate
+wire [ 3:0] sl_I; // sustain level
+wire ksr_II; // key scale rate - affects rates
+wire [ 1:0] ksl_IV; // key scale level - affects amplitude
+// envelope operation
+wire keyon_I;
+wire eg_stop;
+// envelope number
+wire amen_IV;
+wire [ 5:0] tl_IV;
+wire [ 9:0] eg_V;
+// Global values
+wire am_dep, vib_dep, rhy_en;
+// Operator
+wire [ 2:0] fb_I;
+wire [ 1:0] wavsel_I;
+wire op, con_I, op_out, con_out;
+
+wire signed [12:0] op_result;
+
+assign write = !cs_n && !wr_n;
+assign dout = { ~irq_n, flag_A, flag_B, 5'd6 };
+assign eg_stop = 0;
+assign sample = zero;
+
+jtopl_mmr #(.OPL_TYPE(OPL_TYPE)) u_mmr(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cen ), // external clock enable
+ .cenop ( cenop ), // internal clock enable
+ .din ( din ),
+ .write ( write ),
+ .addr ( addr ),
+ .zero ( zero ),
+ .group ( group ),
+ .op ( op ),
+ .slot ( slot ),
+ .rhy_en ( rhy_en ),
+ // Timers
+ .value_A ( value_A ),
+ .value_B ( value_B ),
+ .load_A ( load_A ),
+ .load_B ( load_B ),
+ .flagen_A ( flagen_A ),
+ .flagen_B ( flagen_B ),
+ .clr_flag_A ( clr_flag_A ),
+ .clr_flag_B ( clr_flag_B ),
+ .flag_A ( flag_A ),
+ .overflow_A ( overflow_A ),
+ // Phase Generator
+ .fnum_I ( fnum_I ),
+ .block_I ( block_I ),
+ .mul_II ( mul_II ),
+ // Operator
+ .wavsel_I ( wavsel_I ),
+ // Envelope Generator
+ .keyon_I ( keyon_I ),
+ .en_sus_I ( en_sus_I ),
+ .arate_I ( arate_I ),
+ .drate_I ( drate_I ),
+ .rrate_I ( rrate_I ),
+ .sl_I ( sl_I ),
+ .ks_II ( ksr_II ),
+ .tl_IV ( tl_IV ),
+ .ksl_IV ( ksl_IV ),
+ .amen_IV ( amen_IV ),
+ .viben_I ( viben_I ),
+ // Global Values
+ .am_dep ( am_dep ),
+ .vib_dep ( vib_dep ),
+ // Timbre
+ .fb_I ( fb_I ),
+ .con_I ( con_I )
+);
+
+jtopl_timers u_timers(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cenop ( cenop ),
+ .zero ( zero ),
+ .value_A ( value_A ),
+ .value_B ( value_B ),
+ .load_A ( load_A ),
+ .load_B ( load_B ),
+ .flagen_A ( flagen_A ),
+ .flagen_B ( flagen_B ),
+ .clr_flag_A ( clr_flag_A ),
+ .clr_flag_B ( clr_flag_B ),
+ .flag_A ( flag_A ),
+ .flag_B ( flag_B ),
+ .overflow_A ( overflow_A ),
+ .irq_n ( irq_n )
+);
+
+jtopl_lfo u_lfo(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cenop ( cenop ),
+ .slot ( slot ),
+ .vib_cnt ( vib_cnt ),
+ .trem ( trem )
+);
+
+jtopl_pg u_pg(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cenop ( cenop ),
+ .slot ( slot ),
+ .rhy_en ( rhy_en ),
+ // Channel frequency
+ .fnum_I ( fnum_I ),
+ .block_I ( block_I ),
+ // Operator multiplying
+ .mul_II ( mul_II ),
+ // phase modulation from LFO (vibrato at 6.4Hz)
+ .vib_cnt ( vib_cnt ),
+ .vib_dep ( vib_dep ),
+ .viben_I ( viben_I ),
+ // phase operation
+ .pg_rst_II ( pg_rst_II ),
+
+ .keycode_II ( keycode_II ),
+ .phase_IV ( phase_IV )
+);
+
+jtopl_eg u_eg(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cenop ( cenop ),
+ .zero ( zero ),
+ .eg_stop ( eg_stop ),
+ // envelope configuration
+ .en_sus_I ( en_sus_I ), // enable sustain
+ .keycode_II ( keycode_II ),
+ .arate_I ( arate_I ), // attack rate
+ .drate_I ( drate_I ), // decay rate
+ .rrate_I ( rrate_I ), // release rate
+ .sl_I ( sl_I ), // sustain level
+ .ksr_II ( ksr_II ), // key scale
+ // envelope operation
+ .keyon_I ( keyon_I ),
+ // envelope number
+ .fnum_I ( fnum_I ),
+ .block_I ( block_I ),
+ .lfo_mod ( trem ),
+ .amsen_IV ( amen_IV ),
+ .ams_IV ( am_dep ),
+ .tl_IV ( tl_IV ),
+ .ksl_IV ( ksl_IV ),
+ .eg_V ( eg_V ),
+ .pg_rst_II ( pg_rst_II )
+);
+
+jtopl_op #(.OPL_TYPE(OPL_TYPE)) u_op(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cenop ( cenop ),
+
+ // location of current operator
+ .group ( group ),
+ .op ( op ),
+ .zero ( zero ),
+
+ .pg_phase_I ( phase_IV ),
+ .eg_atten_II( eg_V ), // output from envelope generator
+ .fb_I ( fb_I ), // voice feedback
+ .wavsel_I ( wavsel_I ), // sine mask (OPL2)
+
+ .con_I ( con_I ),
+ .op_result ( op_result ),
+ .op_out ( op_out ),
+ .con_out ( con_out )
+);
+
+jtopl_acc u_acc(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cenop ( cenop ),
+ .zero ( zero ),
+ .op_result ( op_result ),
+ .op ( op_out ),
+ .con ( con_out ),
+ .snd ( snd )
+);
+
+endmodule
+
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl.yaml b/verilog/jtopl/hdl/jtopl.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/verilog/jtopl/hdl/jtopl2.qip b/verilog/jtopl/hdl/jtopl2.qip
new file mode 100644
index 0000000..dbdbe8f
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl2.qip
@@ -0,0 +1,2 @@
+set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl2.v ]
+set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) jt26.qip ]
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl2.v b/verilog/jtopl/hdl/jtopl2.v
new file mode 100644
index 0000000..3d76bbb
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl2.v
@@ -0,0 +1,52 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 10-10-2021
+
+ */
+
+module jtopl2(
+ input rst, // rst should be at least 6 clk&cen cycles long
+ input clk, // CPU clock
+ input cen, // optional clock enable, it not needed leave as 1'b1
+ input [ 7:0] din,
+ input addr,
+ input cs_n,
+ input wr_n,
+ output [ 7:0] dout,
+ output irq_n,
+ // combined output
+ output signed [15:0] snd,
+ output sample
+);
+
+ `define JTOPL2
+ jtopl #(.OPL_TYPE(2)) u_base(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cen ),
+ .din ( din ),
+ .addr ( addr ),
+ .cs_n ( cs_n ),
+ .wr_n ( wr_n ),
+ .dout ( dout ),
+ .irq_n ( irq_n ),
+ .snd ( snd ),
+ .sample ( sample )
+ );
+
+endmodule
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl2.yaml b/verilog/jtopl/hdl/jtopl2.yaml
new file mode 100644
index 0000000..abdac1a
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl2.yaml
@@ -0,0 +1,3 @@
+here:
+ - jtopl2.v
+ - jt26.yaml
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_acc.v b/verilog/jtopl/hdl/jtopl_acc.v
new file mode 100644
index 0000000..6bd9628
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_acc.v
@@ -0,0 +1,48 @@
+/* This file is part of JTOPL.
+
+
+ JTOPL program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 20-6-2020
+
+*/
+
+module jtopl_acc(
+ input rst,
+ input clk,
+ input cenop,
+ input signed [12:0] op_result,
+ input zero,
+ input op, // 0 for modulator operators
+ input con, // 0 for modulated connection
+ output signed [15:0] snd
+);
+
+wire sum_en;
+
+assign sum_en = op | con;
+
+// Continuous output
+jtopl_single_acc u_acc(
+ .clk ( clk ),
+ .cenop ( cenop ),
+ .op_result ( op_result ),
+ .sum_en ( sum_en ),
+ .zero ( zero ),
+ .snd ( snd )
+);
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopl_csr.v b/verilog/jtopl/hdl/jtopl_csr.v
new file mode 100644
index 0000000..1642a54
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_csr.v
@@ -0,0 +1,77 @@
+/* This file is part of JTOPL.
+
+
+ JTOPL program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 17-6-2020
+
+*/
+
+module jtopl_csr #(
+ parameter LEN=18, W=34
+) ( // Circular Shift Register + input mux
+ input rst,
+ input clk,
+ input cen,
+ input [ 7:0] din,
+ output [W-1:0] shift_out,
+
+ input up_mult,
+ input up_ksl_tl,
+ input up_ar_dr,
+ input up_sl_rr,
+ input up_wav,
+ input update_op_I,
+ input update_op_II,
+ input update_op_IV
+);
+
+
+wire [W-1:0] regop_in;
+
+jtopl_sh_rst #(.width(W),.stages(LEN)) u_regch(
+ .clk ( clk ),
+ .cen ( cen ),
+ .rst ( rst ),
+ .din ( regop_in ),
+ .drop ( shift_out )
+);
+
+wire up_mult_I = up_mult & update_op_I;
+wire up_mult_II = up_mult & update_op_II;
+wire up_mult_IV = up_mult & update_op_IV;
+wire up_ksl_tl_IV = up_ksl_tl & update_op_IV;
+wire up_ar_dr_op = up_ar_dr & update_op_I;
+wire up_sl_rr_op = up_sl_rr & update_op_I;
+wire up_wav_I = up_wav & update_op_I;
+
+assign regop_in[31:0] = { // 4 bytes:
+ up_mult_IV ? din[7] : shift_out[31], // AM enable
+ up_mult_I ? din[6:5] : shift_out[30:29], // Vib enable, EG type, KSR
+ up_mult_II ? din[4:0] : shift_out[28:24], // KSR + Mult
+
+ up_ksl_tl_IV? din : shift_out[23:16], // KSL + TL
+
+ up_ar_dr_op ? din : shift_out[15: 8],
+
+ up_sl_rr_op ? din : shift_out[ 7: 0]
+ };
+
+`ifdef JTOPL2
+ assign regop_in[33:32] = up_wav_I ? din[1:0] : shift_out[33:32];
+`endif
+
+endmodule // jtopl_reg
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_div.v b/verilog/jtopl/hdl/jtopl_div.v
new file mode 100644
index 0000000..f1de235
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_div.v
@@ -0,0 +1,47 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 10-6-2020
+
+ */
+
+module jtopl_div(
+ input rst,
+ input clk,
+ input cen,
+ output reg cenop // clock enable at operator rate
+);
+
+parameter OPL_TYPE=1;
+
+localparam W = 2; // OPL_TYPE==2 ? 1 : 2;
+
+reg [W-1:0] cnt;
+
+`ifdef SIMULATION
+initial cnt={W{1'b0}};
+`endif
+
+always @(posedge clk) if(cen) begin
+ cnt <= cnt+1'd1;
+end
+
+always @(posedge clk) begin
+ cenop <= cen && (&cnt);
+end
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopl_eg.v b/verilog/jtopl/hdl/jtopl_eg.v
new file mode 100644
index 0000000..17101df
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_eg.v
@@ -0,0 +1,203 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 17-6-2020
+
+ */
+
+module jtopl_eg (
+ input rst,
+ input clk,
+ input cenop,
+ input zero,
+ input eg_stop,
+ // envelope configuration
+ input en_sus_I, // enable sustain
+ input [3:0] keycode_II,
+ input [3:0] arate_I, // attack rate
+ input [3:0] drate_I, // decay rate
+ input [3:0] rrate_I, // release rate
+ input [3:0] sl_I, // sustain level
+ input ksr_II, // key scale
+ // envelope operation
+ input keyon_I,
+ // envelope number
+ input [9:0] fnum_I,
+ input [2:0] block_I,
+ input [3:0] lfo_mod,
+ input amsen_IV,
+ input ams_IV,
+ input [5:0] tl_IV,
+ input [1:0] ksl_IV,
+
+ output reg [9:0] eg_V,
+ output reg pg_rst_II
+);
+
+parameter SLOTS=18;
+
+wire [14:0] eg_cnt;
+
+jtopl_eg_cnt u_egcnt(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cenop & ~eg_stop ),
+ .zero ( zero ),
+ .eg_cnt ( eg_cnt)
+);
+
+wire keyon_last_I;
+wire keyon_now_I = !keyon_last_I && keyon_I;
+wire keyoff_now_I = keyon_last_I && !keyon_I;
+
+wire cnt_in_II, cnt_lsb_II, step_II, pg_rst_I;
+
+wire [2:0] state_in_I, state_next_I;
+
+reg attack_II, attack_III;
+wire [4:0] base_rate_I;
+reg [4:0] base_rate_II;
+wire [5:0] rate_out_II;
+reg [5:1] rate_in_III;
+reg step_III;
+wire sum_out_II;
+reg sum_in_III;
+
+wire [9:0] eg_in_I, pure_eg_out_III, eg_next_III, eg_out_IV;
+reg [9:0] eg_in_II, eg_in_III, eg_in_IV;
+reg [3:0] keycode_III, keycode_IV;
+wire [3:0] fnum_IV;
+wire [2:0] block_IV;
+
+
+jtopl_eg_comb u_comb(
+ ///////////////////////////////////
+ // I
+ .keyon_now ( keyon_now_I ),
+ .keyoff_now ( keyoff_now_I ),
+ .state_in ( state_in_I ),
+ .eg_in ( eg_in_I ),
+ // envelope configuration
+ .en_sus ( en_sus_I ),
+ .arate ( arate_I ), // attack rate
+ .drate ( drate_I ), // decay rate
+ .rrate ( rrate_I ),
+ .sl ( sl_I ), // sustain level
+
+ .base_rate ( base_rate_I ),
+ .state_next ( state_next_I ),
+ .pg_rst ( pg_rst_I ),
+ ///////////////////////////////////
+ // II
+ .step_attack ( attack_II ),
+ .step_rate_in ( base_rate_II ),
+ .keycode ( keycode_II ),
+ .eg_cnt ( eg_cnt ),
+ .cnt_in ( cnt_in_II ),
+ .ksr ( ksr_II ),
+ .cnt_lsb ( cnt_lsb_II ),
+ .step ( step_II ),
+ .step_rate_out ( rate_out_II ),
+ .sum_up_out ( sum_out_II ),
+ ///////////////////////////////////
+ // III
+ .pure_attack ( attack_III ),
+ .pure_step ( step_III ),
+ .pure_rate ( rate_in_III[5:1] ),
+ .pure_eg_in ( eg_in_III ),
+ .pure_eg_out ( pure_eg_out_III ),
+ .sum_up_in ( sum_in_III ),
+ ///////////////////////////////////
+ // IV
+ .fnum ( fnum_IV ),
+ .block ( block_IV ),
+ .lfo_mod ( lfo_mod ),
+ .amsen ( amsen_IV ),
+ .ams ( ams_IV ),
+ .ksl ( ksl_IV ),
+ .tl ( tl_IV ),
+ .final_keycode ( keycode_IV ),
+ .final_eg_in ( eg_in_IV ),
+ .final_eg_out ( eg_out_IV )
+);
+
+always @(posedge clk) if(cenop) begin
+ eg_in_II <= eg_in_I;
+ attack_II <= state_next_I[0];
+ base_rate_II<= base_rate_I;
+ pg_rst_II <= pg_rst_I;
+
+ eg_in_III <= eg_in_II;
+ attack_III <= attack_II;
+ rate_in_III <= rate_out_II[5:1];
+ step_III <= step_II;
+ sum_in_III <= sum_out_II;
+
+ eg_in_IV <= pure_eg_out_III;
+ eg_V <= eg_out_IV;
+
+ keycode_III <= keycode_II;
+ keycode_IV <= keycode_III;
+end
+
+jtopl_sh #( .width(1), .stages(SLOTS) ) u_cntsh(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .din ( cnt_lsb_II),
+ .drop ( cnt_in_II )
+);
+
+jtopl_sh #( .width(4), .stages(3) ) u_fnumsh(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .din ( fnum_I[9:6] ),
+ .drop ( fnum_IV )
+);
+
+jtopl_sh #( .width(3), .stages(3) ) u_blocksh(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .din ( block_I ),
+ .drop ( block_IV )
+);
+
+jtopl_sh_rst #( .width(10), .stages(SLOTS-3), .rstval(1'b1) ) u_egsh(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .rst ( rst ),
+ .din ( eg_in_IV ),
+ .drop ( eg_in_I )
+);
+
+jtopl_sh_rst #( .width(3), .stages(SLOTS), .rstval(1'b1) ) u_egstate(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .rst ( rst ),
+ .din ( state_next_I ),
+ .drop ( state_in_I )
+);
+
+jtopl_sh_rst #( .width(1), .stages(SLOTS), .rstval(1'b0) ) u_konsh(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .rst ( rst ),
+ .din ( keyon_I ),
+ .drop ( keyon_last_I )
+);
+
+
+endmodule // jtopl_eg
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_eg_cnt.v b/verilog/jtopl/hdl/jtopl_eg_cnt.v
new file mode 100644
index 0000000..8559660
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_eg_cnt.v
@@ -0,0 +1,44 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 17-6-2020
+
+ */
+
+module jtopl_eg_cnt(
+ input rst,
+ input clk,
+ input cen,
+ input zero,
+ output reg [14:0] eg_cnt
+);
+
+always @(posedge clk, posedge rst) begin : envelope_counter
+ if( rst ) begin
+ eg_cnt <=15'd0;
+ end
+ else begin
+ if( zero && cen ) begin
+ // envelope counter increases at each zero input
+ // This is different from OPN/M where it increased
+ // once every three zero inputs
+ eg_cnt <= eg_cnt + 1'b1;
+ end
+ end
+end
+
+endmodule
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_eg_comb.v b/verilog/jtopl/hdl/jtopl_eg_comb.v
new file mode 100644
index 0000000..4ea7a9b
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_eg_comb.v
@@ -0,0 +1,130 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 17-6-2020
+
+ */
+
+module jtopl_eg_comb(
+ input keyon_now,
+ input keyoff_now,
+ input [ 2:0] state_in,
+ input [ 9:0] eg_in,
+ // envelope configuration
+ input en_sus, // enable sustain
+ input [ 3:0] arate, // attack rate
+ input [ 3:0] drate, // decay rate
+ input [ 3:0] rrate,
+ input [ 3:0] sl, // sustain level
+
+ output [ 4:0] base_rate,
+ output [ 2:0] state_next,
+ output pg_rst,
+ ///////////////////////////////////
+ // II
+ input step_attack,
+ input [ 4:0] step_rate_in,
+ input [ 3:0] keycode,
+ input [14:0] eg_cnt,
+ input cnt_in,
+ input ksr,
+ output cnt_lsb,
+ output step,
+ output [ 5:0] step_rate_out,
+ output sum_up_out,
+ ///////////////////////////////////
+ // III
+ input pure_attack,
+ input pure_step,
+ input [ 5:1] pure_rate,
+ input [ 9:0] pure_eg_in,
+ output [ 9:0] pure_eg_out,
+ input sum_up_in,
+ ///////////////////////////////////
+ // IV
+ input [ 3:0] lfo_mod,
+ input [ 3:0] fnum,
+ input [ 2:0] block,
+ input amsen,
+ input ams,
+ input [ 5:0] tl,
+ input [ 1:0] ksl,
+ input [ 3:0] final_keycode,
+ input [ 9:0] final_eg_in,
+ output [ 9:0] final_eg_out
+);
+
+// I
+jtopl_eg_ctrl u_ctrl(
+ .keyon_now ( keyon_now ),
+ .keyoff_now ( keyoff_now ),
+ .state_in ( state_in ),
+ .eg ( eg_in ),
+ // envelope configuration
+ .en_sus ( en_sus ),
+ .arate ( arate ), // attack rate
+ .drate ( drate ), // decay rate
+ .rrate ( rrate ),
+ .sl ( sl ), // sustain level
+
+ .base_rate ( base_rate ),
+ .state_next ( state_next ),
+ .pg_rst ( pg_rst )
+);
+
+// II
+
+jtopl_eg_step u_step(
+ .attack ( step_attack ),
+ .base_rate ( step_rate_in ),
+ .keycode ( keycode ),
+ .eg_cnt ( eg_cnt ),
+ .cnt_in ( cnt_in ),
+ .ksr ( ksr ),
+ .cnt_lsb ( cnt_lsb ),
+ .step ( step ),
+ .rate ( step_rate_out ),
+ .sum_up ( sum_up_out )
+);
+
+// III
+
+wire [9:0] egin, egout;
+jtopl_eg_pure u_pure(
+ .attack ( pure_attack ),
+ .step ( pure_step ),
+ .rate ( pure_rate ),
+ .eg_in ( pure_eg_in ),
+ .eg_pure( pure_eg_out ),
+ .sum_up ( sum_up_in )
+);
+
+// IV
+
+jtopl_eg_final u_final(
+ .fnum ( fnum ),
+ .block ( block ),
+ .lfo_mod ( lfo_mod ),
+ .amsen ( amsen ),
+ .ams ( ams ),
+ .tl ( tl ),
+ .ksl ( ksl ),
+ .eg_pure_in ( final_eg_in ),
+ .eg_limited ( final_eg_out )
+);
+
+endmodule // jtopl_eg_comb
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_eg_ctrl.v b/verilog/jtopl/hdl/jtopl_eg_ctrl.v
new file mode 100644
index 0000000..2940a92
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_eg_ctrl.v
@@ -0,0 +1,87 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 17-6-2020
+
+ */
+
+module jtopl_eg_ctrl(
+ input keyon_now,
+ input keyoff_now,
+ input [2:0] state_in,
+ input [9:0] eg,
+ // envelope configuration
+ input en_sus, // enable sustain
+ input [3:0] arate, // attack rate
+ input [3:0] drate, // decay rate
+ input [3:0] rrate,
+ input [3:0] sl, // sustain level
+
+ output reg [4:0] base_rate,
+ output reg [2:0] state_next,
+ output reg pg_rst
+);
+
+localparam ATTACK = 3'b001,
+ DECAY = 3'b010,
+ HOLD = 3'b100,
+ RELEASE= 3'b000; // default state is release
+
+// wire is_decaying = state_in[1] | state_in[2];
+
+wire [4:0] sustain = { &sl, sl}; //93dB if sl==4'hF
+
+always @(*) begin
+ pg_rst = keyon_now;
+end
+
+always @(*)
+ casez ( { keyoff_now, keyon_now, state_in} )
+ 5'b01_???: begin // key on
+ base_rate = {arate,1'b0};
+ state_next = ATTACK;
+ end
+ {2'b00, ATTACK}:
+ if( eg==10'd0 ) begin
+ base_rate = {drate,1'b0};
+ state_next = DECAY;
+ end
+ else begin
+ base_rate = {arate,1'b0};
+ state_next = ATTACK;
+ end
+ {2'b00, DECAY}: begin
+ if( eg[9:5] >= sustain ) begin
+ base_rate = en_sus ? 5'd0 : {rrate,1'b0};
+ state_next = en_sus ? HOLD : RELEASE;
+ end else begin
+ base_rate = {drate,1'b0};
+ state_next = DECAY;
+ end
+ end
+ {2'b00, HOLD}: begin
+ base_rate = 5'd0;
+ state_next = HOLD;
+ end
+ default: begin // RELEASE, note that keyoff_now==1 will enter this state too
+ base_rate = {rrate,1'b1};
+ state_next = RELEASE; // release
+ end
+ endcase
+
+
+endmodule // jtopl_eg_ctrl
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_eg_final.v b/verilog/jtopl/hdl/jtopl_eg_final.v
new file mode 100644
index 0000000..0fa38d5
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_eg_final.v
@@ -0,0 +1,69 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 17-6-2020
+
+ */
+
+module jtopl_eg_final(
+ input [3:0] lfo_mod,
+ input [3:0] fnum,
+ input [2:0] block,
+ input amsen,
+ input ams,
+ input [5:0] tl,
+ input [1:0] ksl, // level damped by pitch
+ input [9:0] eg_pure_in,
+ output reg [9:0] eg_limited
+);
+
+reg [ 5:0] am_final;
+reg [11:0] sum_eg_tl;
+reg [11:0] sum_eg_tl_am;
+reg [ 8:0] ksl_dB;
+reg [ 6:0] ksl_lut[0:15];
+reg [ 7:0] ksl_base;
+
+always @(*) begin
+ ksl_base = {1'b0, ksl_lut[fnum]}- { 1'b0, 4'd8-{1'b0,block}, 3'b0 };
+ if( ksl_base[7] || ksl==2'b0 ) begin
+ ksl_dB = 9'd0;
+ end else begin
+ ksl_dB = {ksl_base[6:0],2'b0} >> ~ksl;
+ end
+end
+
+always @(*) begin
+ am_final = amsen ? ( ams ? {lfo_mod, 2'b0} : {2'b0, lfo_mod} ) : 6'd0;
+ sum_eg_tl = { 2'b0, tl, 3'd0 } +
+ { 1'b0, ksl_dB, 1'd0 } +
+ { 1'b0, eg_pure_in}; // leading zeros needed to compute correctly
+ sum_eg_tl_am = sum_eg_tl + { 5'd0, am_final };
+end
+
+always @(*) begin
+ eg_limited = sum_eg_tl_am[11:10]==2'd0 ? sum_eg_tl_am[9:0] : 10'h3ff;
+end
+
+initial begin
+ ksl_lut[ 0] = 7'd00; ksl_lut[ 1] = 7'd32; ksl_lut[ 2] = 7'd40; ksl_lut[ 3] = 7'd45;
+ ksl_lut[ 4] = 7'd48; ksl_lut[ 5] = 7'd51; ksl_lut[ 6] = 7'd53; ksl_lut[ 7] = 7'd55;
+ ksl_lut[ 8] = 7'd56; ksl_lut[ 9] = 7'd58; ksl_lut[10] = 7'd59; ksl_lut[11] = 7'd60;
+ ksl_lut[12] = 7'd61; ksl_lut[13] = 7'd62; ksl_lut[14] = 7'd63; ksl_lut[15] = 7'd64;
+end
+
+endmodule
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_eg_pure.v b/verilog/jtopl/hdl/jtopl_eg_pure.v
new file mode 100644
index 0000000..ea4c8a4
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_eg_pure.v
@@ -0,0 +1,87 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 17-6-2020
+
+ */
+
+module jtopl_eg_pure(
+ input attack,
+ input step,
+ input [ 5:1] rate,
+ input [ 9:0] eg_in,
+ input sum_up,
+ output reg [9:0] eg_pure
+);
+
+reg [ 3:0] dr_sum;
+reg [ 9:0] dr_adj;
+reg [10:0] dr_result;
+
+always @(*) begin : dr_calculation
+ case( rate[5:2] )
+ 4'b1100: dr_sum = 4'h2; // 12
+ 4'b1101: dr_sum = 4'h4; // 13
+ 4'b1110: dr_sum = 4'h8; // 14
+ 4'b1111: dr_sum = 4'hf;// 15
+ default: dr_sum = { 2'b0, step, 1'b0 };
+ endcase
+ // Decay rate attenuation is multiplied by 4 for SSG operation
+ dr_adj = {6'd0, dr_sum};
+ dr_result = dr_adj + eg_in;
+end
+
+reg [ 7:0] ar_sum0;
+reg [ 8:0] ar_sum1;
+reg [10:0] ar_result;
+reg [ 9:0] ar_sum;
+
+always @(*) begin : ar_calculation
+ casez( rate[5:2] )
+ default: ar_sum0 = {2'd0, eg_in[9:4]};
+ 4'b1011, 4'b1100: ar_sum0 = {1'd0, eg_in[9:3]}; // 'hb
+ // 4'b1101: ar_sum0 = {1'd0, eg_in[9:3]}; // 'hd
+ // 4'b111?: ar_sum0 = eg_in[9:2]; // 'he/f
+ 4'b1101, 4'b111?: ar_sum0 = eg_in[9:2]; // 'he/f
+ endcase
+ ar_sum1 = ar_sum0+9'd1;
+ if( rate[5:2] == 4'he )
+ ar_sum = { ar_sum1, 1'b0 };
+ else if( rate[5:2] > 4'hb )
+ ar_sum = step ? { ar_sum1, 1'b0 } : { 1'b0, ar_sum1 }; // adds ar_sum1*3/2 max
+ // else if( rate[5:2] == 4'hb )
+ // ar_sum = step ? { ar_sum1, 1'b0 } : 10'd0; // adds ar_sum1 max
+ else
+ ar_sum = step ? { 1'b0, ar_sum1 } : 10'd0; // adds ar_sum1/2 max
+ ar_result = eg_in-ar_sum;
+end
+
+///////////////////////////////////////////////////////////
+// rate not used below this point
+reg [9:0] eg_pre_fastar; // pre fast attack rate
+always @(*) begin
+ if(sum_up) begin
+ if( attack )
+ eg_pre_fastar = ar_result[10] ? 10'd0: ar_result[9:0];
+ else
+ eg_pre_fastar = dr_result[10] ? 10'h3FF : dr_result[9:0];
+ end
+ else eg_pre_fastar = eg_in;
+ eg_pure = (attack&rate[5:1]==5'h1F) ? 10'd0 : eg_pre_fastar;
+end
+
+endmodule
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_eg_step.v b/verilog/jtopl/hdl/jtopl_eg_step.v
new file mode 100644
index 0000000..a1f76c4
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_eg_step.v
@@ -0,0 +1,106 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 17-6-2020
+
+ */
+
+module jtopl_eg_step(
+ input attack,
+ input [ 4:0] base_rate,
+ input [ 3:0] keycode,
+ input [14:0] eg_cnt,
+ input cnt_in,
+ input ksr,
+ output cnt_lsb,
+ output reg step,
+ output reg [ 5:0] rate,
+ output reg sum_up
+);
+
+reg [6:0] pre_rate;
+
+always @(*) begin : pre_rate_calc
+ if( base_rate == 5'd0 )
+ pre_rate = 7'd0;
+ else
+ pre_rate = { 1'b0, base_rate, 1'b0 } + // base_rate LSB is always zero except for RR
+ ({ 3'b0, keycode } >> (ksr ? 1 : 3));
+end
+
+always @(*)
+ rate = pre_rate>=7'b1111_00 ? 6'b1111_11 : pre_rate[5:0];
+
+reg [2:0] cnt;
+
+reg [4:0] mux_sel;
+always @(*) begin
+ mux_sel = attack ? (rate[5:2]+4'd1): {1'b0,rate[5:2]};
+end
+
+always @(*)
+ case( mux_sel )
+ 5'h0: cnt = eg_cnt[13:11];
+ 5'h1: cnt = eg_cnt[12:10];
+ 5'h2: cnt = eg_cnt[11: 9];
+ 5'h3: cnt = eg_cnt[10: 8];
+ 5'h4: cnt = eg_cnt[ 9: 7];
+ 5'h5: cnt = eg_cnt[ 8: 6];
+ 5'h6: cnt = eg_cnt[ 7: 5];
+ 5'h7: cnt = eg_cnt[ 6: 4];
+ 5'h8: cnt = eg_cnt[ 5: 3];
+ 5'h9: cnt = eg_cnt[ 4: 2];
+ 5'ha: cnt = eg_cnt[ 3: 1];
+ default: cnt = eg_cnt[ 2: 0];
+ endcase
+
+////////////////////////////////
+reg [7:0] step_idx;
+
+always @(*) begin : rate_step
+ if( rate[5:4]==2'b11 ) begin // 0 means 1x, 1 means 2x
+ if( rate[5:2]==4'hf && attack)
+ step_idx = 8'b11111111; // Maximum attack speed, rates 60&61
+ else
+ case( rate[1:0] )
+ 2'd0: step_idx = 8'b00000000;
+ 2'd1: step_idx = 8'b10001000; // 2
+ 2'd2: step_idx = 8'b10101010; // 4
+ 2'd3: step_idx = 8'b11101110; // 6
+ endcase
+ end
+ else begin
+ if( rate[5:2]==4'd0 && !attack)
+ step_idx = 8'b11111110; // limit slowest decay rate
+ else
+ case( rate[1:0] )
+ 2'd0: step_idx = 8'b10101010; // 4
+ 2'd1: step_idx = 8'b11101010; // 5
+ 2'd2: step_idx = 8'b11101110; // 6
+ 2'd3: step_idx = 8'b11111110; // 7
+ endcase
+ end
+ // a rate of zero keeps the level still
+ step = rate[5:1]==5'd0 ? 1'b0 : step_idx[ cnt ];
+end
+
+assign cnt_lsb = cnt[0];
+always @(*) begin
+ sum_up = cnt[0] != cnt_in;
+end
+
+endmodule // eg_step
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_exprom.v b/verilog/jtopl/hdl/jtopl_exprom.v
new file mode 100644
index 0000000..47ac351
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_exprom.v
@@ -0,0 +1,305 @@
+/* This file is part of JTOPL.
+
+
+ JTOPL program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Based on Sauraen VHDL version of OPN/OPN2, which is based on die shots.
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 20-6-2020
+
+*/
+
+// Yamaha used the same table for OPN, OPM and OPL
+// Originally written in more compact way that required some logic to decompress
+// Not really worth compressing when the target is an FPGA as one BRAM will be
+// used in either case. So it's better to leave it uncompress and save the
+// decoding logic
+
+// altera message_off 10030
+
+module jtopl_exprom
+(
+ input [7:0] addr,
+ input clk,
+ input cen,
+ output reg [9:0] exp
+);
+
+ reg [9:0] explut_jt51[255:0];
+ initial
+ begin
+ explut_jt51[8'd000] = 10'd1018;
+ explut_jt51[8'd001] = 10'd1013;
+ explut_jt51[8'd002] = 10'd1007;
+ explut_jt51[8'd003] = 10'd1002;
+ explut_jt51[8'd004] = 10'd0996;
+ explut_jt51[8'd005] = 10'd0991;
+ explut_jt51[8'd006] = 10'd0986;
+ explut_jt51[8'd007] = 10'd0980;
+ explut_jt51[8'd008] = 10'd0975;
+ explut_jt51[8'd009] = 10'd0969;
+ explut_jt51[8'd010] = 10'd0964;
+ explut_jt51[8'd011] = 10'd0959;
+ explut_jt51[8'd012] = 10'd0953;
+ explut_jt51[8'd013] = 10'd0948;
+ explut_jt51[8'd014] = 10'd0942;
+ explut_jt51[8'd015] = 10'd0937;
+ explut_jt51[8'd016] = 10'd0932;
+ explut_jt51[8'd017] = 10'd0927;
+ explut_jt51[8'd018] = 10'd0921;
+ explut_jt51[8'd019] = 10'd0916;
+ explut_jt51[8'd020] = 10'd0911;
+ explut_jt51[8'd021] = 10'd0906;
+ explut_jt51[8'd022] = 10'd0900;
+ explut_jt51[8'd023] = 10'd0895;
+ explut_jt51[8'd024] = 10'd0890;
+ explut_jt51[8'd025] = 10'd0885;
+ explut_jt51[8'd026] = 10'd0880;
+ explut_jt51[8'd027] = 10'd0874;
+ explut_jt51[8'd028] = 10'd0869;
+ explut_jt51[8'd029] = 10'd0864;
+ explut_jt51[8'd030] = 10'd0859;
+ explut_jt51[8'd031] = 10'd0854;
+ explut_jt51[8'd032] = 10'd0849;
+ explut_jt51[8'd033] = 10'd0844;
+ explut_jt51[8'd034] = 10'd0839;
+ explut_jt51[8'd035] = 10'd0834;
+ explut_jt51[8'd036] = 10'd0829;
+ explut_jt51[8'd037] = 10'd0824;
+ explut_jt51[8'd038] = 10'd0819;
+ explut_jt51[8'd039] = 10'd0814;
+ explut_jt51[8'd040] = 10'd0809;
+ explut_jt51[8'd041] = 10'd0804;
+ explut_jt51[8'd042] = 10'd0799;
+ explut_jt51[8'd043] = 10'd0794;
+ explut_jt51[8'd044] = 10'd0789;
+ explut_jt51[8'd045] = 10'd0784;
+ explut_jt51[8'd046] = 10'd0779;
+ explut_jt51[8'd047] = 10'd0774;
+ explut_jt51[8'd048] = 10'd0770;
+ explut_jt51[8'd049] = 10'd0765;
+ explut_jt51[8'd050] = 10'd0760;
+ explut_jt51[8'd051] = 10'd0755;
+ explut_jt51[8'd052] = 10'd0750;
+ explut_jt51[8'd053] = 10'd0745;
+ explut_jt51[8'd054] = 10'd0741;
+ explut_jt51[8'd055] = 10'd0736;
+ explut_jt51[8'd056] = 10'd0731;
+ explut_jt51[8'd057] = 10'd0726;
+ explut_jt51[8'd058] = 10'd0722;
+ explut_jt51[8'd059] = 10'd0717;
+ explut_jt51[8'd060] = 10'd0712;
+ explut_jt51[8'd061] = 10'd0708;
+ explut_jt51[8'd062] = 10'd0703;
+ explut_jt51[8'd063] = 10'd0698;
+ explut_jt51[8'd064] = 10'd0693;
+ explut_jt51[8'd065] = 10'd0689;
+ explut_jt51[8'd066] = 10'd0684;
+ explut_jt51[8'd067] = 10'd0680;
+ explut_jt51[8'd068] = 10'd0675;
+ explut_jt51[8'd069] = 10'd0670;
+ explut_jt51[8'd070] = 10'd0666;
+ explut_jt51[8'd071] = 10'd0661;
+ explut_jt51[8'd072] = 10'd0657;
+ explut_jt51[8'd073] = 10'd0652;
+ explut_jt51[8'd074] = 10'd0648;
+ explut_jt51[8'd075] = 10'd0643;
+ explut_jt51[8'd076] = 10'd0639;
+ explut_jt51[8'd077] = 10'd0634;
+ explut_jt51[8'd078] = 10'd0630;
+ explut_jt51[8'd079] = 10'd0625;
+ explut_jt51[8'd080] = 10'd0621;
+ explut_jt51[8'd081] = 10'd0616;
+ explut_jt51[8'd082] = 10'd0612;
+ explut_jt51[8'd083] = 10'd0607;
+ explut_jt51[8'd084] = 10'd0603;
+ explut_jt51[8'd085] = 10'd0599;
+ explut_jt51[8'd086] = 10'd0594;
+ explut_jt51[8'd087] = 10'd0590;
+ explut_jt51[8'd088] = 10'd0585;
+ explut_jt51[8'd089] = 10'd0581;
+ explut_jt51[8'd090] = 10'd0577;
+ explut_jt51[8'd091] = 10'd0572;
+ explut_jt51[8'd092] = 10'd0568;
+ explut_jt51[8'd093] = 10'd0564;
+ explut_jt51[8'd094] = 10'd0560;
+ explut_jt51[8'd095] = 10'd0555;
+ explut_jt51[8'd096] = 10'd0551;
+ explut_jt51[8'd097] = 10'd0547;
+ explut_jt51[8'd098] = 10'd0542;
+ explut_jt51[8'd099] = 10'd0538;
+ explut_jt51[8'd100] = 10'd0534;
+ explut_jt51[8'd101] = 10'd0530;
+ explut_jt51[8'd102] = 10'd0526;
+ explut_jt51[8'd103] = 10'd0521;
+ explut_jt51[8'd104] = 10'd0517;
+ explut_jt51[8'd105] = 10'd0513;
+ explut_jt51[8'd106] = 10'd0509;
+ explut_jt51[8'd107] = 10'd0505;
+ explut_jt51[8'd108] = 10'd0501;
+ explut_jt51[8'd109] = 10'd0496;
+ explut_jt51[8'd110] = 10'd0492;
+ explut_jt51[8'd111] = 10'd0488;
+ explut_jt51[8'd112] = 10'd0484;
+ explut_jt51[8'd113] = 10'd0480;
+ explut_jt51[8'd114] = 10'd0476;
+ explut_jt51[8'd115] = 10'd0472;
+ explut_jt51[8'd116] = 10'd0468;
+ explut_jt51[8'd117] = 10'd0464;
+ explut_jt51[8'd118] = 10'd0460;
+ explut_jt51[8'd119] = 10'd0456;
+ explut_jt51[8'd120] = 10'd0452;
+ explut_jt51[8'd121] = 10'd0448;
+ explut_jt51[8'd122] = 10'd0444;
+ explut_jt51[8'd123] = 10'd0440;
+ explut_jt51[8'd124] = 10'd0436;
+ explut_jt51[8'd125] = 10'd0432;
+ explut_jt51[8'd126] = 10'd0428;
+ explut_jt51[8'd127] = 10'd0424;
+ explut_jt51[8'd128] = 10'd0420;
+ explut_jt51[8'd129] = 10'd0416;
+ explut_jt51[8'd130] = 10'd0412;
+ explut_jt51[8'd131] = 10'd0409;
+ explut_jt51[8'd132] = 10'd0405;
+ explut_jt51[8'd133] = 10'd0401;
+ explut_jt51[8'd134] = 10'd0397;
+ explut_jt51[8'd135] = 10'd0393;
+ explut_jt51[8'd136] = 10'd0389;
+ explut_jt51[8'd137] = 10'd0385;
+ explut_jt51[8'd138] = 10'd0382;
+ explut_jt51[8'd139] = 10'd0378;
+ explut_jt51[8'd140] = 10'd0374;
+ explut_jt51[8'd141] = 10'd0370;
+ explut_jt51[8'd142] = 10'd0367;
+ explut_jt51[8'd143] = 10'd0363;
+ explut_jt51[8'd144] = 10'd0359;
+ explut_jt51[8'd145] = 10'd0355;
+ explut_jt51[8'd146] = 10'd0352;
+ explut_jt51[8'd147] = 10'd0348;
+ explut_jt51[8'd148] = 10'd0344;
+ explut_jt51[8'd149] = 10'd0340;
+ explut_jt51[8'd150] = 10'd0337;
+ explut_jt51[8'd151] = 10'd0333;
+ explut_jt51[8'd152] = 10'd0329;
+ explut_jt51[8'd153] = 10'd0326;
+ explut_jt51[8'd154] = 10'd0322;
+ explut_jt51[8'd155] = 10'd0318;
+ explut_jt51[8'd156] = 10'd0315;
+ explut_jt51[8'd157] = 10'd0311;
+ explut_jt51[8'd158] = 10'd0308;
+ explut_jt51[8'd159] = 10'd0304;
+ explut_jt51[8'd160] = 10'd0300;
+ explut_jt51[8'd161] = 10'd0297;
+ explut_jt51[8'd162] = 10'd0293;
+ explut_jt51[8'd163] = 10'd0290;
+ explut_jt51[8'd164] = 10'd0286;
+ explut_jt51[8'd165] = 10'd0283;
+ explut_jt51[8'd166] = 10'd0279;
+ explut_jt51[8'd167] = 10'd0276;
+ explut_jt51[8'd168] = 10'd0272;
+ explut_jt51[8'd169] = 10'd0268;
+ explut_jt51[8'd170] = 10'd0265;
+ explut_jt51[8'd171] = 10'd0262;
+ explut_jt51[8'd172] = 10'd0258;
+ explut_jt51[8'd173] = 10'd0255;
+ explut_jt51[8'd174] = 10'd0251;
+ explut_jt51[8'd175] = 10'd0248;
+ explut_jt51[8'd176] = 10'd0244;
+ explut_jt51[8'd177] = 10'd0241;
+ explut_jt51[8'd178] = 10'd0237;
+ explut_jt51[8'd179] = 10'd0234;
+ explut_jt51[8'd180] = 10'd0231;
+ explut_jt51[8'd181] = 10'd0227;
+ explut_jt51[8'd182] = 10'd0224;
+ explut_jt51[8'd183] = 10'd0220;
+ explut_jt51[8'd184] = 10'd0217;
+ explut_jt51[8'd185] = 10'd0214;
+ explut_jt51[8'd186] = 10'd0210;
+ explut_jt51[8'd187] = 10'd0207;
+ explut_jt51[8'd188] = 10'd0204;
+ explut_jt51[8'd189] = 10'd0200;
+ explut_jt51[8'd190] = 10'd0197;
+ explut_jt51[8'd191] = 10'd0194;
+ explut_jt51[8'd192] = 10'd0190;
+ explut_jt51[8'd193] = 10'd0187;
+ explut_jt51[8'd194] = 10'd0184;
+ explut_jt51[8'd195] = 10'd0181;
+ explut_jt51[8'd196] = 10'd0177;
+ explut_jt51[8'd197] = 10'd0174;
+ explut_jt51[8'd198] = 10'd0171;
+ explut_jt51[8'd199] = 10'd0168;
+ explut_jt51[8'd200] = 10'd0164;
+ explut_jt51[8'd201] = 10'd0161;
+ explut_jt51[8'd202] = 10'd0158;
+ explut_jt51[8'd203] = 10'd0155;
+ explut_jt51[8'd204] = 10'd0152;
+ explut_jt51[8'd205] = 10'd0148;
+ explut_jt51[8'd206] = 10'd0145;
+ explut_jt51[8'd207] = 10'd0142;
+ explut_jt51[8'd208] = 10'd0139;
+ explut_jt51[8'd209] = 10'd0136;
+ explut_jt51[8'd210] = 10'd0133;
+ explut_jt51[8'd211] = 10'd0130;
+ explut_jt51[8'd212] = 10'd0126;
+ explut_jt51[8'd213] = 10'd0123;
+ explut_jt51[8'd214] = 10'd0120;
+ explut_jt51[8'd215] = 10'd0117;
+ explut_jt51[8'd216] = 10'd0114;
+ explut_jt51[8'd217] = 10'd0111;
+ explut_jt51[8'd218] = 10'd0108;
+ explut_jt51[8'd219] = 10'd0105;
+ explut_jt51[8'd220] = 10'd0102;
+ explut_jt51[8'd221] = 10'd0099;
+ explut_jt51[8'd222] = 10'd0096;
+ explut_jt51[8'd223] = 10'd0093;
+ explut_jt51[8'd224] = 10'd0090;
+ explut_jt51[8'd225] = 10'd0087;
+ explut_jt51[8'd226] = 10'd0084;
+ explut_jt51[8'd227] = 10'd0081;
+ explut_jt51[8'd228] = 10'd0078;
+ explut_jt51[8'd229] = 10'd0075;
+ explut_jt51[8'd230] = 10'd0072;
+ explut_jt51[8'd231] = 10'd0069;
+ explut_jt51[8'd232] = 10'd0066;
+ explut_jt51[8'd233] = 10'd0063;
+ explut_jt51[8'd234] = 10'd0060;
+ explut_jt51[8'd235] = 10'd0057;
+ explut_jt51[8'd236] = 10'd0054;
+ explut_jt51[8'd237] = 10'd0051;
+ explut_jt51[8'd238] = 10'd0048;
+ explut_jt51[8'd239] = 10'd0045;
+ explut_jt51[8'd240] = 10'd0042;
+ explut_jt51[8'd241] = 10'd0040;
+ explut_jt51[8'd242] = 10'd0037;
+ explut_jt51[8'd243] = 10'd0034;
+ explut_jt51[8'd244] = 10'd0031;
+ explut_jt51[8'd245] = 10'd0028;
+ explut_jt51[8'd246] = 10'd0025;
+ explut_jt51[8'd247] = 10'd0022;
+ explut_jt51[8'd248] = 10'd0020;
+ explut_jt51[8'd249] = 10'd0017;
+ explut_jt51[8'd250] = 10'd0014;
+ explut_jt51[8'd251] = 10'd0011;
+ explut_jt51[8'd252] = 10'd0008;
+ explut_jt51[8'd253] = 10'd0006;
+ explut_jt51[8'd254] = 10'd0003;
+ explut_jt51[8'd255] = 10'd0000;
+ end
+
+ always @ (posedge clk) if(cen)
+ exp <= explut_jt51[addr];
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopl_lfo.v b/verilog/jtopl/hdl/jtopl_lfo.v
new file mode 100644
index 0000000..0aa5015
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_lfo.v
@@ -0,0 +1,80 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 21-6-2020
+ */
+
+// Follows OPLL Reverse Engineering from Nuked
+// https://github.com/nukeykt/Nuked-OPLL
+
+// The AM logic renders a triangular waveform. The logic for it is rather
+// obscure, but apparently that's how the original was done
+
+module jtopl_lfo(
+ input rst,
+ input clk,
+ input cenop,
+ input [17:0] slot,
+ output [ 2:0] vib_cnt,
+ output reg [ 3:0] trem
+);
+
+parameter [6:0] LIM=7'd60;
+
+reg [12:0] cnt;
+reg am_inc, am_incen, am_dir, am_step;
+reg [ 1:0] am_bit;
+reg am_carry;
+reg [ 8:0] am_cnt;
+
+wire [12:0] next = cnt+1'b1;
+
+assign vib_cnt = cnt[12:10];
+
+always @(*) begin
+ am_inc = (slot[0] | am_dir ) & am_step & am_incen;
+ am_bit = {1'b0, am_cnt[0]} + {1'b0, am_inc} + {1'b0, am_carry & am_incen};
+end
+
+always @(posedge clk) begin
+ if( rst ) begin
+ cnt <= 13'd0;
+ am_incen <= 1;
+ am_dir <= 0;
+ am_carry <= 0;
+ am_cnt <= 9'd0;
+ am_step <= 0;
+ end else if( cenop ) begin
+ if( slot[17] ) begin
+ cnt <= next;
+ am_step <= &next[5:0];
+ am_incen <= 1;
+ end
+ else if(slot[8]) am_incen <= 0;
+ am_cnt <= { am_bit[0], am_cnt[8:1] };
+ am_carry <= am_bit[1];
+ if( slot[0] ) begin
+ if( am_dir && am_cnt[6:0]==7'd0 ) am_dir <= 0;
+ else
+ if( !am_dir && ( (am_cnt[6:0]&7'h69) == 7'h69) ) am_dir <= 1;
+ end
+ // output
+ if( slot[0] ) trem <= am_cnt[6:3];
+ end
+end
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopl_logsin.v b/verilog/jtopl/hdl/jtopl_logsin.v
new file mode 100644
index 0000000..fe9295a
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_logsin.v
@@ -0,0 +1,297 @@
+/* This file is part of JTOPL.
+
+ JTOPL program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Based on Sauraen VHDL version of OPN/OPN2, which is based on die shots.
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 13-6-2020
+
+*/
+
+//altera message_off 10030
+
+module jtopl_logsin(
+ input clk,
+ input cen,
+ input [ 7:0] addr,
+ output reg [11:0] logsin
+);
+
+reg [11:0] sinelut[255:0];
+initial begin
+ sinelut[8'd000] = 12'h000;
+ sinelut[8'd001] = 12'h000;
+ sinelut[8'd002] = 12'h000;
+ sinelut[8'd003] = 12'h000;
+ sinelut[8'd004] = 12'h000;
+ sinelut[8'd005] = 12'h000;
+ sinelut[8'd006] = 12'h000;
+ sinelut[8'd007] = 12'h000;
+ sinelut[8'd008] = 12'h001;
+ sinelut[8'd009] = 12'h001;
+ sinelut[8'd010] = 12'h001;
+ sinelut[8'd011] = 12'h001;
+ sinelut[8'd012] = 12'h001;
+ sinelut[8'd013] = 12'h001;
+ sinelut[8'd014] = 12'h001;
+ sinelut[8'd015] = 12'h002;
+ sinelut[8'd016] = 12'h002;
+ sinelut[8'd017] = 12'h002;
+ sinelut[8'd018] = 12'h002;
+ sinelut[8'd019] = 12'h003;
+ sinelut[8'd020] = 12'h003;
+ sinelut[8'd021] = 12'h003;
+ sinelut[8'd022] = 12'h004;
+ sinelut[8'd023] = 12'h004;
+ sinelut[8'd024] = 12'h004;
+ sinelut[8'd025] = 12'h005;
+ sinelut[8'd026] = 12'h005;
+ sinelut[8'd027] = 12'h005;
+ sinelut[8'd028] = 12'h006;
+ sinelut[8'd029] = 12'h006;
+ sinelut[8'd030] = 12'h007;
+ sinelut[8'd031] = 12'h007;
+ sinelut[8'd032] = 12'h007;
+ sinelut[8'd033] = 12'h008;
+ sinelut[8'd034] = 12'h008;
+ sinelut[8'd035] = 12'h009;
+ sinelut[8'd036] = 12'h009;
+ sinelut[8'd037] = 12'h00a;
+ sinelut[8'd038] = 12'h00a;
+ sinelut[8'd039] = 12'h00b;
+ sinelut[8'd040] = 12'h00c;
+ sinelut[8'd041] = 12'h00c;
+ sinelut[8'd042] = 12'h00d;
+ sinelut[8'd043] = 12'h00d;
+ sinelut[8'd044] = 12'h00e;
+ sinelut[8'd045] = 12'h00f;
+ sinelut[8'd046] = 12'h00f;
+ sinelut[8'd047] = 12'h010;
+ sinelut[8'd048] = 12'h011;
+ sinelut[8'd049] = 12'h011;
+ sinelut[8'd050] = 12'h012;
+ sinelut[8'd051] = 12'h013;
+ sinelut[8'd052] = 12'h014;
+ sinelut[8'd053] = 12'h014;
+ sinelut[8'd054] = 12'h015;
+ sinelut[8'd055] = 12'h016;
+ sinelut[8'd056] = 12'h017;
+ sinelut[8'd057] = 12'h017;
+ sinelut[8'd058] = 12'h018;
+ sinelut[8'd059] = 12'h019;
+ sinelut[8'd060] = 12'h01a;
+ sinelut[8'd061] = 12'h01b;
+ sinelut[8'd062] = 12'h01c;
+ sinelut[8'd063] = 12'h01d;
+ sinelut[8'd064] = 12'h01e;
+ sinelut[8'd065] = 12'h01f;
+ sinelut[8'd066] = 12'h020;
+ sinelut[8'd067] = 12'h021;
+ sinelut[8'd068] = 12'h022;
+ sinelut[8'd069] = 12'h023;
+ sinelut[8'd070] = 12'h024;
+ sinelut[8'd071] = 12'h025;
+ sinelut[8'd072] = 12'h026;
+ sinelut[8'd073] = 12'h027;
+ sinelut[8'd074] = 12'h028;
+ sinelut[8'd075] = 12'h029;
+ sinelut[8'd076] = 12'h02a;
+ sinelut[8'd077] = 12'h02b;
+ sinelut[8'd078] = 12'h02d;
+ sinelut[8'd079] = 12'h02e;
+ sinelut[8'd080] = 12'h02f;
+ sinelut[8'd081] = 12'h030;
+ sinelut[8'd082] = 12'h031;
+ sinelut[8'd083] = 12'h033;
+ sinelut[8'd084] = 12'h034;
+ sinelut[8'd085] = 12'h035;
+ sinelut[8'd086] = 12'h037;
+ sinelut[8'd087] = 12'h038;
+ sinelut[8'd088] = 12'h039;
+ sinelut[8'd089] = 12'h03b;
+ sinelut[8'd090] = 12'h03c;
+ sinelut[8'd091] = 12'h03e;
+ sinelut[8'd092] = 12'h03f;
+ sinelut[8'd093] = 12'h040;
+ sinelut[8'd094] = 12'h042;
+ sinelut[8'd095] = 12'h043;
+ sinelut[8'd096] = 12'h045;
+ sinelut[8'd097] = 12'h046;
+ sinelut[8'd098] = 12'h048;
+ sinelut[8'd099] = 12'h04a;
+ sinelut[8'd100] = 12'h04b;
+ sinelut[8'd101] = 12'h04d;
+ sinelut[8'd102] = 12'h04e;
+ sinelut[8'd103] = 12'h050;
+ sinelut[8'd104] = 12'h052;
+ sinelut[8'd105] = 12'h053;
+ sinelut[8'd106] = 12'h055;
+ sinelut[8'd107] = 12'h057;
+ sinelut[8'd108] = 12'h059;
+ sinelut[8'd109] = 12'h05b;
+ sinelut[8'd110] = 12'h05c;
+ sinelut[8'd111] = 12'h05e;
+ sinelut[8'd112] = 12'h060;
+ sinelut[8'd113] = 12'h062;
+ sinelut[8'd114] = 12'h064;
+ sinelut[8'd115] = 12'h066;
+ sinelut[8'd116] = 12'h068;
+ sinelut[8'd117] = 12'h06a;
+ sinelut[8'd118] = 12'h06c;
+ sinelut[8'd119] = 12'h06e;
+ sinelut[8'd120] = 12'h070;
+ sinelut[8'd121] = 12'h072;
+ sinelut[8'd122] = 12'h074;
+ sinelut[8'd123] = 12'h076;
+ sinelut[8'd124] = 12'h078;
+ sinelut[8'd125] = 12'h07a;
+ sinelut[8'd126] = 12'h07d;
+ sinelut[8'd127] = 12'h07f;
+ sinelut[8'd128] = 12'h081;
+ sinelut[8'd129] = 12'h083;
+ sinelut[8'd130] = 12'h086;
+ sinelut[8'd131] = 12'h088;
+ sinelut[8'd132] = 12'h08a;
+ sinelut[8'd133] = 12'h08d;
+ sinelut[8'd134] = 12'h08f;
+ sinelut[8'd135] = 12'h092;
+ sinelut[8'd136] = 12'h094;
+ sinelut[8'd137] = 12'h097;
+ sinelut[8'd138] = 12'h099;
+ sinelut[8'd139] = 12'h09c;
+ sinelut[8'd140] = 12'h09f;
+ sinelut[8'd141] = 12'h0a1;
+ sinelut[8'd142] = 12'h0a4;
+ sinelut[8'd143] = 12'h0a7;
+ sinelut[8'd144] = 12'h0a9;
+ sinelut[8'd145] = 12'h0ac;
+ sinelut[8'd146] = 12'h0af;
+ sinelut[8'd147] = 12'h0b2;
+ sinelut[8'd148] = 12'h0b5;
+ sinelut[8'd149] = 12'h0b8;
+ sinelut[8'd150] = 12'h0bb;
+ sinelut[8'd151] = 12'h0be;
+ sinelut[8'd152] = 12'h0c1;
+ sinelut[8'd153] = 12'h0c4;
+ sinelut[8'd154] = 12'h0c7;
+ sinelut[8'd155] = 12'h0ca;
+ sinelut[8'd156] = 12'h0cd;
+ sinelut[8'd157] = 12'h0d1;
+ sinelut[8'd158] = 12'h0d4;
+ sinelut[8'd159] = 12'h0d7;
+ sinelut[8'd160] = 12'h0db;
+ sinelut[8'd161] = 12'h0de;
+ sinelut[8'd162] = 12'h0e2;
+ sinelut[8'd163] = 12'h0e5;
+ sinelut[8'd164] = 12'h0e9;
+ sinelut[8'd165] = 12'h0ec;
+ sinelut[8'd166] = 12'h0f0;
+ sinelut[8'd167] = 12'h0f4;
+ sinelut[8'd168] = 12'h0f8;
+ sinelut[8'd169] = 12'h0fb;
+ sinelut[8'd170] = 12'h0ff;
+ sinelut[8'd171] = 12'h103;
+ sinelut[8'd172] = 12'h107;
+ sinelut[8'd173] = 12'h10b;
+ sinelut[8'd174] = 12'h10f;
+ sinelut[8'd175] = 12'h114;
+ sinelut[8'd176] = 12'h118;
+ sinelut[8'd177] = 12'h11c;
+ sinelut[8'd178] = 12'h121;
+ sinelut[8'd179] = 12'h125;
+ sinelut[8'd180] = 12'h129;
+ sinelut[8'd181] = 12'h12e;
+ sinelut[8'd182] = 12'h133;
+ sinelut[8'd183] = 12'h137;
+ sinelut[8'd184] = 12'h13c;
+ sinelut[8'd185] = 12'h141;
+ sinelut[8'd186] = 12'h146;
+ sinelut[8'd187] = 12'h14b;
+ sinelut[8'd188] = 12'h150;
+ sinelut[8'd189] = 12'h155;
+ sinelut[8'd190] = 12'h15b;
+ sinelut[8'd191] = 12'h160;
+ sinelut[8'd192] = 12'h166;
+ sinelut[8'd193] = 12'h16b;
+ sinelut[8'd194] = 12'h171;
+ sinelut[8'd195] = 12'h177;
+ sinelut[8'd196] = 12'h17c;
+ sinelut[8'd197] = 12'h182;
+ sinelut[8'd198] = 12'h188;
+ sinelut[8'd199] = 12'h18f;
+ sinelut[8'd200] = 12'h195;
+ sinelut[8'd201] = 12'h19b;
+ sinelut[8'd202] = 12'h1a2;
+ sinelut[8'd203] = 12'h1a9;
+ sinelut[8'd204] = 12'h1b0;
+ sinelut[8'd205] = 12'h1b7;
+ sinelut[8'd206] = 12'h1be;
+ sinelut[8'd207] = 12'h1c5;
+ sinelut[8'd208] = 12'h1cd;
+ sinelut[8'd209] = 12'h1d4;
+ sinelut[8'd210] = 12'h1dc;
+ sinelut[8'd211] = 12'h1e4;
+ sinelut[8'd212] = 12'h1ec;
+ sinelut[8'd213] = 12'h1f5;
+ sinelut[8'd214] = 12'h1fd;
+ sinelut[8'd215] = 12'h206;
+ sinelut[8'd216] = 12'h20f;
+ sinelut[8'd217] = 12'h218;
+ sinelut[8'd218] = 12'h222;
+ sinelut[8'd219] = 12'h22c;
+ sinelut[8'd220] = 12'h236;
+ sinelut[8'd221] = 12'h240;
+ sinelut[8'd222] = 12'h24b;
+ sinelut[8'd223] = 12'h256;
+ sinelut[8'd224] = 12'h261;
+ sinelut[8'd225] = 12'h26d;
+ sinelut[8'd226] = 12'h279;
+ sinelut[8'd227] = 12'h286;
+ sinelut[8'd228] = 12'h293;
+ sinelut[8'd229] = 12'h2a0;
+ sinelut[8'd230] = 12'h2af;
+ sinelut[8'd231] = 12'h2bd;
+ sinelut[8'd232] = 12'h2cd;
+ sinelut[8'd233] = 12'h2dc;
+ sinelut[8'd234] = 12'h2ed;
+ sinelut[8'd235] = 12'h2ff;
+ sinelut[8'd236] = 12'h311;
+ sinelut[8'd237] = 12'h324;
+ sinelut[8'd238] = 12'h339;
+ sinelut[8'd239] = 12'h34e;
+ sinelut[8'd240] = 12'h365;
+ sinelut[8'd241] = 12'h37e;
+ sinelut[8'd242] = 12'h398;
+ sinelut[8'd243] = 12'h3b5;
+ sinelut[8'd244] = 12'h3d3;
+ sinelut[8'd245] = 12'h3f5;
+ sinelut[8'd246] = 12'h41a;
+ sinelut[8'd247] = 12'h443;
+ sinelut[8'd248] = 12'h471;
+ sinelut[8'd249] = 12'h4a6;
+ sinelut[8'd250] = 12'h4e4;
+ sinelut[8'd251] = 12'h52e;
+ sinelut[8'd252] = 12'h58b;
+ sinelut[8'd253] = 12'h607;
+ sinelut[8'd254] = 12'h6c3;
+ sinelut[8'd255] = 12'h859;
+end
+
+always @ (posedge clk) if(cen) begin
+ logsin <= sinelut[addr];
+end
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopl_mmr.v b/verilog/jtopl/hdl/jtopl_mmr.v
new file mode 100644
index 0000000..ee61b0b
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_mmr.v
@@ -0,0 +1,276 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 10-6-2020
+ */
+
+module jtopl_mmr(
+ input rst,
+ input clk,
+ input cen,
+ output cenop,
+ input [ 7:0] din,
+ input write,
+ input addr,
+ // location
+ output zero,
+ output [ 1:0] group,
+ output op,
+ output [17:0] slot,
+ output reg rhy_en,
+ // Timers
+ output reg [ 7:0] value_A,
+ output reg [ 7:0] value_B,
+ output reg load_A,
+ output reg load_B,
+ output reg flagen_A,
+ output reg flagen_B,
+ output reg clr_flag_A,
+ output reg clr_flag_B,
+ input flag_A,
+ input overflow_A,
+ // Phase Generator
+ output [ 9:0] fnum_I,
+ output [ 2:0] block_I,
+ output [ 3:0] mul_II,
+ output viben_I,
+ // Operator
+ output [ 1:0] wavsel_I,
+ // Envelope Generator
+ output keyon_I,
+ output en_sus_I, // enable sustain
+ output [ 3:0] arate_I, // attack rate
+ output [ 3:0] drate_I, // decay rate
+ output [ 3:0] rrate_I, // release rate
+ output [ 3:0] sl_I, // sustain level
+ output ks_II, // key scale
+ output [ 5:0] tl_IV,
+ output amen_IV,
+ // global values
+ output reg am_dep,
+ output reg vib_dep,
+ output [ 1:0] ksl_IV,
+ // Operator configuration
+ output [ 2:0] fb_I,
+ output con_I
+);
+
+parameter OPL_TYPE=1;
+
+jtopl_div #(OPL_TYPE) u_div (
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cen ),
+ .cenop ( cenop )
+);
+
+localparam [7:0] REG_TESTYM = 8'h01,
+ REG_CLKA = 8'h02,
+ REG_CLKB = 8'h03,
+ REG_TIMER = 8'h04,
+ REG_CSM = 8'h08,
+ REG_RYTHM = 8'hBD;
+
+reg [ 7:0] selreg; // selected register
+reg [ 7:0] din_copy;
+reg csm, effect;
+reg [ 1:0] sel_group; // group to update
+reg [ 2:0] sel_sub; // subslot to update
+reg up_fnumlo, up_fnumhi, up_fbcon,
+ up_mult, up_ksl_tl, up_ar_dr, up_sl_rr,
+ up_wav;
+reg wave_mode, // 1 if waveform selection is enabled (OPL2)
+ csm_en,
+ note_sel; // keyboard split, not implemented
+reg [ 4:0] rhy_kon;
+
+// this runs at clk speed, no clock gating here
+// if I try to make this an async rst it fails to map it
+// as flip flops but uses latches instead. So I keep it as sync. reset
+always @(posedge clk) begin
+ if( rst ) begin
+ selreg <= 8'h0;
+ sel_group <= 2'd0;
+ sel_sub <= 3'd0;
+ // Updaters
+ up_fbcon <= 0;
+ up_fnumlo <= 0;
+ up_fnumhi <= 0;
+ up_mult <= 0;
+ up_ksl_tl <= 0;
+ up_ar_dr <= 0;
+ up_sl_rr <= 0;
+ up_wav <= 0;
+ // Rhythms
+ rhy_en <= 0;
+ rhy_kon <= 5'd0;
+ // sensitivity to LFO
+ am_dep <= 0;
+ vib_dep <= 0;
+ csm_en <= 0;
+ note_sel <= 0;
+ // OPL2 waveforms
+ wave_mode <= 0;
+ // timers
+ { value_A, value_B } <= 16'd0;
+ { clr_flag_B, clr_flag_A, load_B, load_A } <= 4'd0;
+ flagen_A <= 1;
+ flagen_B <= 1;
+ din_copy <= 8'd0;
+ end else begin
+ // WRITE IN REGISTERS
+ if( write ) begin
+ if( !addr ) begin
+ selreg <= din;
+ end else begin
+ // Global registers
+ din_copy <= din;
+ up_fnumhi <= 0;
+ up_fnumlo <= 0;
+ up_fbcon <= 0;
+ up_mult <= 0;
+ up_ksl_tl <= 0;
+ up_ar_dr <= 0;
+ up_sl_rr <= 0;
+ up_wav <= 0;
+ // General control (<0x20 registers)
+ casez( selreg )
+ REG_TESTYM: if(OPL_TYPE>1) wave_mode <= din[5];
+ REG_CLKA: value_A <= din;
+ REG_CLKB: value_B <= din;
+ REG_TIMER: begin
+ clr_flag_A <= din[7];
+ clr_flag_B <= din[7];
+ if (~din[7]) begin
+ flagen_A <= ~din[6];
+ flagen_B <= ~din[5];
+ { load_B, load_A } <= din[1:0];
+ end
+ end
+ REG_CSM: {csm_en, note_sel} <= din[7:6];
+ default:;
+ endcase
+ // Operator registers
+ // Mapping done according to Table 2-3, page 7 of YM3812 App. Manual
+ if( selreg >= 8'h20 &&
+ (selreg < 8'hA0 || (selreg>=8'hE0 && OPL_TYPE>1) ) &&
+ selreg[2:0]<=3'd5 && selreg[4:3]!=2'b11) begin
+ sel_group <= selreg[4:3];
+ sel_sub <= selreg[2:0];
+ case( selreg[7:5] )
+ 3'b001: up_mult <= 1;
+ 3'b010: up_ksl_tl <= 1;
+ 3'b011: up_ar_dr <= 1;
+ 3'b100: up_sl_rr <= 1;
+ 3'b111: up_wav <= OPL_TYPE!=1;
+ default:;
+ endcase
+ end
+ // Channel registers
+ if( selreg[3:0]<=4'd8) begin
+ case( selreg[7:4] )
+ 4'hA: up_fnumlo <= 1;
+ 4'hB: up_fnumhi <= 1;
+ 4'hC: up_fbcon <= 1;
+ default:;
+ endcase
+ end
+ if( selreg[7:4]>=4'hA && selreg[7:4]<4'hd
+ && selreg[3:0]<=8 ) begin
+ // Each group has three channels
+ // Channels 0-2 -> group 0
+ // Channels 3-5 -> group 1
+ // Channels 6-8 -> group 2
+ // other -> group 3 - ignored
+ sel_group <= selreg[3:0] < 4'd3 ? 2'd0 :
+ selreg[3:0] < 4'd6 ? 2'd1 :
+ selreg[3:0] < 4'd9 ? 2'd2 : 2'd3;
+ sel_sub <= selreg[3:0] < 4'd6 ? selreg[2:0] :
+ { 1'b0, ~&selreg[2:1], selreg[0] };
+ end
+ // Global register
+ if( selreg==REG_RYTHM ) begin
+ am_dep <= din[7];
+ vib_dep <= din[6];
+ rhy_en <= din[5];
+ rhy_kon <= din[4:0];
+ end
+ end
+ end
+ else if(cenop) begin /* clear once-only bits */
+ { clr_flag_B, clr_flag_A } <= 2'd0;
+ end
+ end
+end
+
+jtopl_reg #(.OPL_TYPE(OPL_TYPE)) u_reg(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cenop ),
+ .din ( din_copy ),
+ .write ( write ),
+ // Pipeline order
+ .zero ( zero ),
+ .group ( group ),
+ .op ( op ),
+ .slot ( slot ),
+
+ .sel_group ( sel_group ), // group to update
+ .sel_sub ( sel_sub ), // subslot to update
+
+ .rhy_en ( rhy_en ),
+ .rhy_kon ( rhy_kon ),
+
+ //input csm,
+ //input flag_A,
+ //input overflow_A,
+
+ .up_fbcon ( up_fbcon ),
+ .up_fnumlo ( up_fnumlo ),
+ .up_fnumhi ( up_fnumhi ),
+
+ .up_mult ( up_mult ),
+ .up_ksl_tl ( up_ksl_tl ),
+ .up_ar_dr ( up_ar_dr ),
+ .up_sl_rr ( up_sl_rr ),
+ .up_wav ( up_wav ),
+
+ // PG
+ .fnum_I ( fnum_I ),
+ .block_I ( block_I ),
+ .mul_II ( mul_II ),
+ .viben_I ( viben_I ),
+ // OP
+ .wavsel_I ( wavsel_I ),
+ .wave_mode ( wave_mode ),
+ // EG
+ .keyon_I ( keyon_I ),
+ .en_sus_I ( en_sus_I ),
+ .arate_I ( arate_I ),
+ .drate_I ( drate_I ),
+ .rrate_I ( rrate_I ),
+ .sl_I ( sl_I ),
+ .ks_II ( ks_II ),
+ .ksl_IV ( ksl_IV ),
+ .amen_IV ( amen_IV ),
+ .tl_IV ( tl_IV ),
+ // Timbre - Neiro
+ .fb_I ( fb_I ),
+ .con_I ( con_I )
+);
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopl_noise.v b/verilog/jtopl/hdl/jtopl_noise.v
new file mode 100644
index 0000000..e300966
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_noise.v
@@ -0,0 +1,47 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 24-6-2020
+
+ */
+
+// Following research by Andete in
+// https://github.com/andete/ym2413
+// This has been research for the YM2413 (OPLL)
+// I assume other OPL chips use the same one
+
+module jtopl_noise(
+ input rst, // rst should be at least 6 clk&cen cycles long
+ input clk, // CPU clock
+ input cen, // optional clock enable, it not needed leave as 1'b1
+ output noise
+);
+
+reg [22:0] poly;
+reg nbit;
+
+assign noise = poly[22] ^ poly[9] ^ poly[8] ^ poly[0];
+
+always @(posedge clk, posedge rst) begin
+ if( rst )
+ poly <= 1;
+ else if(cen) begin
+ poly <= poly==0 ? 23'd1 : { poly[21:0], noise };
+ end
+end
+
+endmodule
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_op.v b/verilog/jtopl/hdl/jtopl_op.v
new file mode 100644
index 0000000..2cb417c
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_op.v
@@ -0,0 +1,262 @@
+/* This file is part of JTOPL.
+
+
+ JTOPL program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Based on Sauraen VHDL version of OPN/OPN2, which is based on die shots.
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 19-6-2020
+
+*/
+
+
+module jtopl_op(
+ input rst,
+ input clk,
+ input cenop,
+
+ // these signals need be delayed
+ input [1:0] group,
+ input op, // 0 for modulator operators
+ input con_I,
+ input [2:0] fb_I, // voice feedback
+
+ input zero,
+
+ input [9:0] pg_phase_I,
+ input [1:0] wavsel_I,
+ input [9:0] eg_atten_II, // output from envelope generator
+
+
+ output reg signed [12:0] op_result,
+ output op_out,
+ output con_out
+);
+
+parameter OPL_TYPE=1;
+
+localparam OPW=13, // Operator Width
+ PW=OPW*2; // Previous data Width
+
+reg [11:0] level_II;
+reg signbit_II, signbit_III;
+reg nullify_II;
+
+wire [ 8:0] ctrl_in, ctrl_dly;
+wire [ 1:0] group_d;
+wire op_d, con_I_d;
+wire [ 1:0] wavsel_d;
+wire [ 2:0] fb_I_d;
+
+reg [PW-1:0] prev, prev0_din, prev1_din, prev2_din;
+wire [PW-1:0] prev0, prev1, prev2;
+
+assign ctrl_in = { wavsel_I, group, op, con_I, fb_I };
+assign { wavsel_d, group_d, op_d, con_I_d, fb_I_d } = ctrl_dly;
+
+jtopl_sh #( .width(9), .stages(3)) u_delay(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .din ( ctrl_in ),
+ .drop ( ctrl_dly )
+);
+
+jtopl_sh #( .width(2), .stages(3)) u_condly(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .din ( {op_d, con_I_d} ),
+ .drop ( {op_out, con_out} )
+);
+
+always @(*) begin
+ prev0_din = op_d && group_d==2'd0 ? { prev0[OPW-1:0], op_result } : prev0;
+ prev1_din = op_d && group_d==2'd1 ? { prev1[OPW-1:0], op_result } : prev1;
+ prev2_din = op_d && group_d==2'd2 ? { prev2[OPW-1:0], op_result } : prev2;
+ case( group_d )
+ default: prev = prev0;
+ 2'd1: prev = prev1;
+ 2'd2: prev = prev2;
+ endcase
+end
+
+jtopl_sh #( .width(PW), .stages(3)) u_csr0(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .din ( prev0_din ),
+ .drop ( prev0 )
+);
+
+jtopl_sh #( .width(PW), .stages(3)) u_csr1(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .din ( prev1_din ),
+ .drop ( prev1 )
+);
+
+jtopl_sh #( .width(PW), .stages(3)) u_csr2(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .din ( prev2_din ),
+ .drop ( prev2 )
+);
+
+
+reg [ 10:0] subtresult;
+reg [OPW-1:0] shifter;
+wire signed [OPW-1:0] fb1 = prev[PW-1:OPW];
+wire signed [OPW-1:0] fb0 = prev[OPW-1:0];
+
+// REGISTER/CYCLE 1
+// Creation of phase modulation (FM) feedback signal, before shifting
+reg signed [OPW-1:0] modmux_I;
+reg signed [OPW-1:0] fbmod_I;
+
+always @(*) begin
+ modmux_I = op_d ? op_result : fb1+fb0;
+ // OPL-L shifts by 8-FB
+ // OPL3 shifts by 9-FB
+ // OPLL seems to use lower resolution for OPW so it makes
+ // sense that it shifts by one fewer
+ fbmod_I = modmux_I>>>(4'd9-{1'b0,fb_I_d});
+end
+
+reg signed [9:0] phasemod_I;
+
+always @(*) begin
+ // Shift FM feedback signal
+ if (op_d)
+ phasemod_I = con_I_d ? 10'd0 : modmux_I[9:0];
+ else
+ phasemod_I = fb_I_d==3'd0 ? 10'd0 : fbmod_I[9:0];
+end
+
+reg [ 9:0] phase;
+reg [ 7:0] aux_I;
+
+always @(*) begin
+ phase = phasemod_I + pg_phase_I;
+ aux_I = phase[7:0] ^ {8{~phase[8]}};
+end
+
+// REGISTER/CYCLE 1
+
+always @(posedge clk) if( cenop ) begin
+ if( OPL_TYPE==1 ) begin
+ signbit_II <= phase[9];
+ nullify_II <= 0;
+ end else begin
+ signbit_II <= wavsel_d==0 && phase[9];
+ nullify_II <= (wavsel_d==2'b01 && phase[9]) || (wavsel_d==2'b11 && phase[8]);
+ end
+end
+
+wire [11:0] logsin_II;
+
+jtopl_logsin u_logsin (
+ .clk ( clk ),
+ .cen ( cenop ),
+ .addr ( aux_I[7:0] ),
+ .logsin ( logsin_II )
+);
+
+// REGISTER/CYCLE 2
+// Sine table
+// Main sine table body
+
+always @(*) begin
+ subtresult = eg_atten_II + logsin_II[11:2];
+ level_II = { subtresult[9:0], logsin_II[1:0] } | {12{subtresult[10]}};
+ if( nullify_II ) begin
+ level_II = ~12'h0;
+ end
+end
+
+wire [9:0] mantissa_III;
+reg [3:0] exponent_III;
+
+jtopl_exprom u_exprom(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .addr ( level_II[7:0] ),
+ .exp ( mantissa_III )
+);
+
+always @(posedge clk) if( cenop ) begin
+ exponent_III <= level_II[11:8];
+ signbit_III <= signbit_II;
+end
+
+// REGISTER/CYCLE 3
+// 2's complement & Carry-out discarded
+
+always @(*) begin
+ // Floating-point to integer, and incorporating sign bit
+ shifter = { 2'b01, mantissa_III,1'b0 } >> exponent_III;
+end
+
+// It looks like OPLL and OPL3 don't do full 2's complement but just bit inversion
+always @(posedge clk) if( cenop ) begin
+ op_result <= ( shifter ^ {OPW{signbit_III}});// + {13'd0,signbit_III};
+end
+
+`ifdef SIMULATION
+reg signed [OPW-1:0] op_sep0_0;
+reg signed [OPW-1:0] op_sep1_0;
+reg signed [OPW-1:0] op_sep2_0;
+reg signed [OPW-1:0] op_sep0_1;
+reg signed [OPW-1:0] op_sep1_1;
+reg signed [OPW-1:0] op_sep2_1;
+reg signed [OPW-1:0] op_sep4_0;
+reg signed [OPW-1:0] op_sep5_0;
+reg signed [OPW-1:0] op_sep6_0;
+reg signed [OPW-1:0] op_sep4_1;
+reg signed [OPW-1:0] op_sep5_1;
+reg signed [OPW-1:0] op_sep6_1;
+reg signed [OPW-1:0] op_sep7_0;
+reg signed [OPW-1:0] op_sep8_0;
+reg signed [OPW-1:0] op_sep9_0;
+reg signed [OPW-1:0] op_sep7_1;
+reg signed [OPW-1:0] op_sep8_1;
+reg signed [OPW-1:0] op_sep9_1;
+reg [ 4:0] sepcnt;
+
+always @(posedge clk) if(cenop) begin
+ sepcnt <= zero ? 5'd0 : sepcnt+5'd1;
+ case( (sepcnt+3)%18 )
+ 0: op_sep0_0 <= op_result;
+ 1: op_sep1_0 <= op_result;
+ 2: op_sep2_0 <= op_result;
+ 3: op_sep0_1 <= op_result;
+ 4: op_sep1_1 <= op_result;
+ 5: op_sep2_1 <= op_result;
+ 6: op_sep4_0 <= op_result;
+ 7: op_sep5_0 <= op_result;
+ 8: op_sep6_0 <= op_result;
+ 9: op_sep4_1 <= op_result;
+ 10: op_sep5_1 <= op_result;
+ 11: op_sep6_1 <= op_result;
+ 12: op_sep7_0 <= op_result;
+ 13: op_sep8_0 <= op_result;
+ 14: op_sep9_0 <= op_result;
+ 15: op_sep7_1 <= op_result;
+ 16: op_sep8_1 <= op_result;
+ 17: op_sep9_1 <= op_result;
+ endcase
+end
+
+`endif
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopl_pg.v b/verilog/jtopl/hdl/jtopl_pg.v
new file mode 100644
index 0000000..d1c6a0b
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_pg.v
@@ -0,0 +1,129 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 13-6-2020
+
+ */
+
+module jtopl_pg(
+ input rst,
+ input clk,
+ input cenop,
+ input [17:0] slot,
+ input rhy_en,
+ // Channel frequency
+ input [ 9:0] fnum_I,
+ input [ 2:0] block_I,
+ // Operator multiplying
+ input [ 3:0] mul_II,
+ // phase modulation from LFO (vibrato at 6.4Hz)
+ input [ 2:0] vib_cnt,
+ input vib_dep,
+ input viben_I,
+ // phase operation
+ input pg_rst_II,
+
+ output reg [ 3:0] keycode_II,
+ output [ 9:0] phase_IV
+);
+
+parameter CH=9;
+
+wire [ 3:0] keycode_I;
+wire [16:0] phinc_I;
+reg [16:0] phinc_II;
+wire [18:0] phase_drop, phase_in;
+wire [ 9:0] phase_II;
+wire noise;
+reg [ 9:0] hh, tc;
+reg rm_xor;
+wire hh_en, sd_en, tc_en;
+
+always @(posedge clk) if(cenop) begin
+ keycode_II <= keycode_I;
+ phinc_II <= phinc_I;
+end
+
+// Rhythm phase
+always @(posedge clk, posedge rst) begin
+ if( rst ) begin
+ hh <= 10'd0;
+ tc <= 10'd0;
+ end else begin
+ if( slot[13] ) hh <= phase_drop[18:9];
+ if( slot[17] ) tc <= phase_drop[18:9];
+ rm_xor <= (hh[2]^hh[7]) | (hh[3]^tc[5]) | (tc[3]^tc[5]);
+ end
+end
+
+assign hh_en = rhy_en & slot[14]; // 13+1
+assign sd_en = rhy_en & slot[17]; // 16+1
+assign tc_en = rhy_en & slot[ 0]; // (17+1)%18
+
+jtopl_noise u_noise(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .rst ( rst ),
+ .noise ( noise )
+);
+
+jtopl_pg_comb u_comb(
+ .block ( block_I ),
+ .fnum ( fnum_I ),
+ // Phase Modulation
+ .vib_cnt ( vib_cnt ),
+ .vib_dep ( vib_dep ),
+ .viben ( viben_I ),
+
+ .keycode ( keycode_I ),
+ // Phase increment
+ .phinc_out ( phinc_I ),
+ // Phase add
+ .mul ( mul_II ),
+ .phase_in ( phase_drop ),
+ .pg_rst ( pg_rst_II ),
+ .phinc_in ( phinc_II ),
+ // Rhythm
+ .hh_en ( hh_en ),
+ .sd_en ( sd_en ),
+ .tc_en ( tc_en ),
+ .rm_xor ( rm_xor ),
+ .noise ( noise ),
+ .hh ( hh ),
+
+ .phase_out ( phase_in ),
+ .phase_op ( phase_II )
+);
+
+jtopl_sh_rst #( .width(19), .stages(2*CH) ) u_phsh(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .rst ( rst ),
+ .din ( phase_in ),
+ .drop ( phase_drop)
+);
+
+jtopl_sh_rst #( .width(10), .stages(2) ) u_pad(
+ .clk ( clk ),
+ .cen ( cenop ),
+ .rst ( rst ),
+ .din ( phase_II ),
+ .drop ( phase_IV )
+);
+
+endmodule
+
diff --git a/verilog/jtopl/hdl/jtopl_pg_comb.v b/verilog/jtopl/hdl/jtopl_pg_comb.v
new file mode 100644
index 0000000..25a2988
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_pg_comb.v
@@ -0,0 +1,95 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 13-6-2020
+
+*/
+
+module jtopl_pg_comb (
+ input [ 2:0] block,
+ input [ 9:0] fnum,
+ // Phase Modulation
+ input [ 2:0] vib_cnt,
+ input vib_dep,
+ input viben,
+
+ output [ 3:0] keycode,
+ // Phase increment
+ output [16:0] phinc_out,
+ // Phase add
+ input [ 3:0] mul,
+ input [18:0] phase_in,
+ input pg_rst,
+ // input signed [7:0] pm_in,
+ input [16:0] phinc_in,
+ // Rhythm
+ input noise,
+ input [ 9:0] hh,
+ input hh_en,
+ input tc_en,
+ input sd_en,
+ input rm_xor,
+
+ output [18:0] phase_out,
+ output [ 9:0] phase_op
+);
+
+wire signed [3:0] pm_offset;
+wire [9:0] phase_pre;
+
+assign keycode = { block, fnum[9] };
+
+/* pm and pg_inc operate in parallel */
+jtopl_pm u_pm(
+ .vib_cnt ( vib_cnt ),
+ .fnum ( fnum ),
+ .vib_dep ( vib_dep ),
+ .viben ( viben ),
+ .pm_offset ( pm_offset )
+);
+
+jtopl_pg_inc u_inc(
+ .block ( block ),
+ .fnum ( fnum ),
+ .pm_offset ( pm_offset ),
+ .phinc_pure ( phinc_out )
+);
+
+// pg_sum uses the output from the previous blocks
+
+jtopl_pg_sum u_sum(
+ .mul ( mul ),
+ .phase_in ( phase_in ),
+ .pg_rst ( pg_rst ),
+ .phinc_pure ( phinc_in ),
+ .phase_out ( phase_out ),
+ .phase_op ( phase_pre )
+);
+
+jtopl_pg_rhy u_rhy(
+ .phase_pre ( phase_pre ),
+ // Rhythm
+ .noise ( noise ),
+ .hh ( hh ),
+ .hh_en ( hh_en ),
+ .tc_en ( tc_en ),
+ .sd_en ( sd_en ),
+ .rm_xor ( rm_xor ),
+ .phase_op ( phase_op )
+);
+
+endmodule // jtopl_pg_comb
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_pg_inc.v b/verilog/jtopl/hdl/jtopl_pg_inc.v
new file mode 100644
index 0000000..0064387
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_pg_inc.v
@@ -0,0 +1,38 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 13-6-2020
+
+*/
+
+module jtopl_pg_inc (
+ input [ 2:0] block,
+ input [ 9:0] fnum,
+ input signed [ 3:0] pm_offset,
+ output reg [16:0] phinc_pure
+);
+
+reg [16:0] freq;
+
+always @(*) begin
+ freq = { 7'd0, fnum } + { {13{pm_offset[3]}}, pm_offset };
+ // Add PM here
+ freq = freq << block;
+ phinc_pure = freq >> 1;
+end
+
+endmodule // jtopl_pg_inc
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_pg_rhy.v b/verilog/jtopl/hdl/jtopl_pg_rhy.v
new file mode 100644
index 0000000..d9c9073
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_pg_rhy.v
@@ -0,0 +1,49 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 25-6-2020
+
+ */
+
+module jtopl_pg_rhy (
+ input [ 9:0] phase_pre,
+ // Rhythm
+ input noise,
+ input [ 9:0] hh,
+ input hh_en,
+ input tc_en,
+ input sd_en,
+ input rm_xor,
+ output reg [ 9:0] phase_op
+);
+
+always @(*) begin
+ if( hh_en ) begin
+ phase_op = {rm_xor, 9'd0 };
+ if( rm_xor ^ noise )
+ phase_op = phase_op | 10'hd0;
+ else
+ phase_op = phase_op | 10'h34;
+ end else if( sd_en ) begin
+ phase_op = { hh[8], hh[8]^noise, 8'd0 };
+ end else if( tc_en ) begin
+ phase_op = { rm_xor, 9'h80 };
+ end else
+ phase_op = phase_pre;
+end
+
+endmodule // jtopl_pg_sum
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_pg_sum.v b/verilog/jtopl/hdl/jtopl_pg_sum.v
new file mode 100644
index 0000000..71dc8e3
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_pg_sum.v
@@ -0,0 +1,52 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 13-6-2020
+
+ */
+
+// Original hardware uses an adder to do the multiplication
+// but I think it will take less resources of the FPGA to
+// use a real multiplier instead
+
+module jtopl_pg_sum (
+ input [ 3:0] mul,
+ input [18:0] phase_in,
+ input pg_rst,
+ input [16:0] phinc_pure,
+
+ output reg [18:0] phase_out,
+ output reg [ 9:0] phase_op
+);
+
+reg [21:0] phinc_mul;
+reg [ 4:0] factor[0:15];
+
+always @(*) begin
+ phinc_mul = { 5'b0, phinc_pure} * factor[mul];
+ phase_out = pg_rst ? 'd0 : (phase_in + phinc_mul[19:1]);
+ phase_op = phase_out[18:9];
+end
+
+initial begin
+ factor[ 0] = 5'd01; factor[ 1] = 5'd02; factor[ 2] = 5'd04; factor[ 3] = 5'd06;
+ factor[ 4] = 5'd08; factor[ 5] = 5'd10; factor[ 6] = 5'd12; factor[ 7] = 5'd14;
+ factor[ 8] = 5'd16; factor[ 9] = 5'd18; factor[10] = 5'd20; factor[11] = 5'd20;
+ factor[12] = 5'd24; factor[13] = 5'd24; factor[14] = 5'd30; factor[15] = 5'd30;
+end
+
+endmodule // jtopl_pg_sum
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_pm.v b/verilog/jtopl/hdl/jtopl_pm.v
new file mode 100644
index 0000000..d4bf764
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_pm.v
@@ -0,0 +1,47 @@
+ /* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 21-6-2020
+ */
+
+// Based on Nuked's work on OPLL and OPL3
+
+module jtopl_pm (
+ input [ 2:0] vib_cnt,
+ input [ 9:0] fnum,
+ input vib_dep,
+ input viben,
+ output reg [ 3:0] pm_offset
+);
+
+reg [2:0] range;
+
+always @(*) begin
+ if( vib_cnt[1:0]==2'b00 )
+ range = 3'd0;
+ else begin
+ range = fnum[9:7]>>vib_cnt[0];
+ if(!vib_dep) range = range>>1;
+ end
+ if( vib_cnt[2] )
+ pm_offset = ~{1'b0, range } + 4'd1;
+ else
+ pm_offset = {1'b0, range };
+ if(!viben) pm_offset = 4'd0;
+end
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopl_reg.v b/verilog/jtopl/hdl/jtopl_reg.v
new file mode 100644
index 0000000..f3ed5dc
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_reg.v
@@ -0,0 +1,199 @@
+/* This file is part of JTOPL
+
+ JTOPL program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 13-6-2020
+
+*/
+
+module jtopl_reg(
+ input rst,
+ input clk,
+ input cen,
+ input [7:0] din,
+ input write,
+ // Pipeline order
+ output zero,
+ output [1:0] group,
+ output op, // 0 for modulator operators
+ output [17:0] slot, // hot one encoding of active slot
+
+ input [1:0] sel_group, // group to update
+ input [2:0] sel_sub, // subslot to update
+
+ input rhy_en, // rhythm enable
+ input [4:0] rhy_kon, // key-on for each rhythm instrument
+
+ //input csm,
+ //input flag_A,
+ //input overflow_A,
+
+ input up_fbcon,
+ input up_fnumlo,
+ input up_fnumhi,
+ input up_mult,
+ input up_ksl_tl,
+ input up_ar_dr,
+ input up_sl_rr,
+ input up_wav,
+
+ // PG
+ output [9:0] fnum_I,
+ output [2:0] block_I,
+ // channel configuration
+ output [2:0] fb_I,
+
+ output [3:0] mul_II, // frequency multiplier
+ output [1:0] ksl_IV, // key shift level
+ output amen_IV,
+ output viben_I,
+ // OP
+ output [1:0] wavsel_I,
+ input wave_mode,
+ // EG
+ output keyon_I,
+ output [5:0] tl_IV,
+ output en_sus_I, // enable sustain
+ output [3:0] arate_I, // attack rate
+ output [3:0] drate_I, // decay rate
+ output [3:0] rrate_I, // release rate
+ output [3:0] sl_I, // sustain level
+ output ks_II, // key scale
+ output con_I
+);
+
+parameter OPL_TYPE=1;
+
+localparam CH=9;
+
+wire [2:0] subslot;
+wire match;
+
+
+// channel data
+wire [2:0] fb_in = din[3:1];
+wire con_in = din[0];
+
+wire up_fnumlo_ch = up_fnumlo & match,
+ up_fnumhi_ch = up_fnumhi & match,
+ up_fbcon_ch = up_fbcon & match,
+ update_op_I = !write && sel_group == group && sel_sub == subslot;
+
+reg update_op_II, update_op_III, update_op_IV;
+
+assign match = { group, subslot } == { sel_group, sel_sub};
+
+jtopl_slot_cnt u_slot_cnt(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cen ),
+
+ // Pipeline order
+ .zero ( zero ),
+ .group ( group ),
+ .op ( op ), // 0 for modulator operators
+ .subslot(subslot),
+ .slot ( slot ) // hot one encoding of active slot
+);
+
+always @(posedge clk) begin
+ if(write) begin
+ update_op_II <= 0;
+ update_op_III <= 0;
+ update_op_IV <= 0;
+ end else if( cen ) begin
+ update_op_II <= update_op_I;
+ update_op_III <= update_op_II;
+ update_op_IV <= update_op_III;
+ end
+end
+
+localparam OPCFGW = 4*8 + (OPL_TYPE!=1 ? 2 : 0);
+
+wire [OPCFGW-1:0] shift_out;
+wire en_sus, rhy_oen;
+
+// Sustained is disabled in rhythm mode for channels in group 2 (i.e. 6,7,8)
+assign en_sus_I = rhy_oen ? 1'b0 : en_sus;
+
+jtopl_csr #(.LEN(CH*2),.W(OPCFGW)) u_csr(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cen ),
+ .din ( din ),
+ .shift_out ( shift_out ),
+ .up_mult ( up_mult ),
+ .up_ksl_tl ( up_ksl_tl ),
+ .up_ar_dr ( up_ar_dr ),
+ .up_sl_rr ( up_sl_rr ),
+ .up_wav ( up_wav ),
+ .update_op_I ( update_op_I ),
+ .update_op_II ( update_op_II ),
+ .update_op_IV ( update_op_IV )
+);
+
+assign { amen_IV, viben_I, en_sus, ks_II, mul_II,
+ ksl_IV, tl_IV,
+ arate_I, drate_I,
+ sl_I, rrate_I } = shift_out[4*8-1:0];
+
+generate
+ if( OPL_TYPE==1 )
+ assign wavsel_I = 0;
+ else
+ assign wavsel_I = shift_out[OPCFGW-1:OPCFGW-2] & {2{wave_mode}};
+endgenerate
+
+
+// Memory for CH registers
+localparam KONW = 1,
+ FNUMW = 10,
+ BLOCKW = 3,
+ FBW = 3,
+ CONW = 1;
+localparam CHCSRW = KONW+FNUMW+BLOCKW+FBW+CONW;
+
+wire [CHCSRW-1:0] chcfg, chcfg_inmux;
+wire keyon_csr, con_csr, rhyon_csr;
+wire disable_con;
+
+assign chcfg_inmux = {
+ up_fnumhi_ch ? din[5:0] : { keyon_csr, block_I, fnum_I[9:8] },
+ up_fnumlo_ch ? din : fnum_I[7:0],
+ up_fbcon_ch ? { fb_in, con_in } : { fb_I, con_csr }
+};
+
+assign disable_con = rhy_oen && !slot[12] && !slot[13];
+assign con_I = !rhy_en || !disable_con ? con_csr : 1'b1;
+assign { keyon_csr, block_I, fnum_I, fb_I, con_csr } = chcfg;
+assign keyon_I = rhy_oen ? rhyon_csr : keyon_csr;
+
+jtopl_reg_ch#(CHCSRW) u_reg_ch(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cen ),
+ .zero ( zero ),
+ .rhy_en ( rhy_en ),
+ .rhy_kon ( rhy_kon ),
+ .slot ( slot ),
+ .group ( group ),
+ .chcfg_inmux ( chcfg_inmux ),
+ .chcfg ( chcfg ),
+ .rhy_oen ( rhy_oen ),
+ .rhyon_csr ( rhyon_csr )
+);
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopl_reg_ch.v b/verilog/jtopl/hdl/jtopl_reg_ch.v
new file mode 100644
index 0000000..2acf15f
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_reg_ch.v
@@ -0,0 +1,111 @@
+/* This file is part of JTOPL
+
+ JTOPL program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 28-5-2022
+
+*/
+
+module jtopl_reg_ch#( parameter
+ CHCSRW = 10
+) (
+ input rst,
+ input clk,
+ input cen,
+ input zero,
+ input rhy_en,
+ input [4:0] rhy_kon,
+ input [17:0] slot,
+
+ input [1:0] group,
+ input [CHCSRW-1:0] chcfg_inmux,
+
+ output reg [CHCSRW-1:0] chcfg,
+ output reg rhy_oen, // high for rhythm operators if rhy_en is set
+ output rhyon_csr
+);
+
+// Rhythm key-on CSR
+localparam BD=4, SD=3, TOM=2, TC=1, HH=0;
+
+reg [CHCSRW-1:0] chcfg0_in, chcfg1_in, chcfg2_in;
+wire [CHCSRW-1:0] chcfg0_out, chcfg1_out, chcfg2_out;
+
+reg [5:0] rhy_csr;
+
+assign rhyon_csr = rhy_csr[5];
+
+always @(*) begin
+ case( group )
+ default: chcfg = chcfg0_out;
+ 2'd1: chcfg = chcfg1_out;
+ 2'd2: chcfg = chcfg2_out;
+ endcase
+ chcfg0_in = group==2'b00 ? chcfg_inmux : chcfg0_out;
+ chcfg1_in = group==2'b01 ? chcfg_inmux : chcfg1_out;
+ chcfg2_in = group==2'b10 ? chcfg_inmux : chcfg2_out;
+end
+
+`ifdef SIMULATION
+reg [CHCSRW-1:0] chsnap0, chsnap1,chsnap2;
+
+always @(posedge clk) if(zero) begin
+ chsnap0 <= chcfg0_out;
+ chsnap1 <= chcfg1_out;
+ chsnap2 <= chcfg2_out;
+end
+`endif
+
+always @(posedge clk, posedge rst) begin
+ if( rst ) begin
+ rhy_csr <= 6'd0;
+ rhy_oen <= 0;
+ end else if(cen) begin
+ if(slot[11]) rhy_oen <= rhy_en;
+ if(slot[17]) begin
+ rhy_csr <= { rhy_kon[BD], rhy_kon[HH], rhy_kon[TOM],
+ rhy_kon[BD], rhy_kon[SD], rhy_kon[TC] };
+ rhy_oen <= 0;
+ end else
+ rhy_csr <= { rhy_csr[4:0], rhy_csr[5] };
+ end
+end
+
+jtopl_sh_rst #(.width(CHCSRW),.stages(3)) u_group0(
+ .clk ( clk ),
+ .cen ( cen ),
+ .rst ( rst ),
+ .din ( chcfg0_in ),
+ .drop ( chcfg0_out )
+);
+
+jtopl_sh_rst #(.width(CHCSRW),.stages(3)) u_group1(
+ .clk ( clk ),
+ .cen ( cen ),
+ .rst ( rst ),
+ .din ( chcfg1_in ),
+ .drop ( chcfg1_out )
+);
+
+jtopl_sh_rst #(.width(CHCSRW),.stages(3)) u_group2(
+ .clk ( clk ),
+ .cen ( cen ),
+ .rst ( rst ),
+ .din ( chcfg2_in ),
+ .drop ( chcfg2_out )
+);
+
+endmodule
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_sh.v b/verilog/jtopl/hdl/jtopl_sh.v
new file mode 100644
index 0000000..9be1990
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_sh.v
@@ -0,0 +1,42 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 19-6-2020
+ */
+
+// stages must be greater than 2
+module jtopl_sh #(parameter width=5, stages=24 )
+(
+ input clk,
+ input cen,
+ input [width-1:0] din,
+ output [width-1:0] drop
+);
+
+reg [stages-1:0] bits[width-1:0];
+
+genvar i;
+generate
+ for (i=0; i < width; i=i+1) begin: bit_shifter
+ always @(posedge clk) if(cen) begin
+ bits[i] <= {bits[i][stages-2:0], din[i]};
+ end
+ assign drop[i] = bits[i][stages-1];
+ end
+endgenerate
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopl_sh_rst.v b/verilog/jtopl/hdl/jtopl_sh_rst.v
new file mode 100644
index 0000000..a9d5e2a
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_sh_rst.v
@@ -0,0 +1,54 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 13-6-2020
+ */
+
+// stages must be greater than 2
+module jtopl_sh_rst #(parameter width=5, stages=18, rstval=1'b0 )
+(
+ input rst,
+ input clk,
+ input cen,
+ input [width-1:0] din,
+ output [width-1:0] drop
+);
+
+reg [stages-1:0] bits[width-1:0];
+
+genvar i;
+integer k;
+generate
+initial
+ for (k=0; k < width; k=k+1) begin
+ bits[k] = { stages{rstval}};
+ end
+endgenerate
+
+generate
+ for (i=0; i < width; i=i+1) begin: bit_shifter
+ always @(posedge clk, posedge rst)
+ if( rst ) begin
+ bits[i] <= {stages{rstval}};
+ end else if(cen) begin
+ bits[i] <= {bits[i][stages-2:0], din[i]};
+ end
+ assign drop[i] = bits[i][stages-1];
+ end
+endgenerate
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopl_single_acc.v b/verilog/jtopl/hdl/jtopl_single_acc.v
new file mode 100644
index 0000000..6043a46
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_single_acc.v
@@ -0,0 +1,61 @@
+/* This file is part of JTOPL.
+
+
+ JTOPL program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 20-6-2020
+
+*/
+
+// Accumulates an arbitrary number of inputs with saturation
+// restart the sum when input "zero" is high
+
+module jtopl_single_acc #(parameter
+ INW=13, // input data width
+ OUTW=16 // output data width
+)(
+ input clk,
+ input cenop,
+ input [INW-1:0] op_result,
+ input sum_en,
+ input zero,
+ output reg [OUTW-1:0] snd
+);
+
+// for full resolution use INW=14, OUTW=16
+// for cut down resolution use INW=9, OUTW=12
+// OUTW-INW should be > 0
+
+reg signed [OUTW-1:0] next, acc, current;
+reg overflow;
+
+wire [OUTW-1:0] plus_inf = { 1'b0, {(OUTW-1){1'b1}} }; // maximum positive value
+wire [OUTW-1:0] minus_inf = { 1'b1, {(OUTW-1){1'b0}} }; // minimum negative value
+
+always @(*) begin
+ current = sum_en ? { {(OUTW-INW){op_result[INW-1]}}, op_result } : {OUTW{1'b0}};
+ next = zero ? current : current + acc;
+ overflow = !zero &&
+ (current[OUTW-1] == acc[OUTW-1]) &&
+ (acc[OUTW-1]!=next[OUTW-1]);
+end
+
+always @(posedge clk) if( cenop ) begin
+ acc <= overflow ? (acc[OUTW-1] ? minus_inf : plus_inf) : next;
+ if(zero) snd <= acc;
+end
+
+endmodule
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_slot_cnt.v b/verilog/jtopl/hdl/jtopl_slot_cnt.v
new file mode 100644
index 0000000..dac7448
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_slot_cnt.v
@@ -0,0 +1,65 @@
+/* This file is part of JTOPL
+
+ JTOPL program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 13-6-2020
+
+*/
+
+module jtopl_slot_cnt(
+ input rst,
+ input clk,
+ input cen,
+
+ // Pipeline order
+ output zero,
+ output reg [ 1:0] group,
+ output reg op, // 0 for modulator operators
+ output reg [ 2:0] subslot,
+ output reg [17:0] slot // hot one encoding of active slot
+);
+
+// Each group contains three channels
+// and each subslot contains six operators
+wire [2:0] next_sub = subslot==3'd5 ? 3'd0 : (subslot+3'd1);
+wire [1:0] next_group = subslot==3'd5 ? (group==2'b10 ? 2'b00 : group+2'b1) : group;
+
+`ifdef SIMULATION
+// These signals need to operate during rst
+// initial state is not relevant (or critical) in real life
+// but we need a clear value during simulation
+initial begin
+ group = 2'd0;
+ subslot = 3'd0;
+ slot = 18'd1;
+end
+`endif
+
+assign zero = slot[0];
+
+always @(posedge clk) begin : up_counter
+ if( cen ) begin
+ { group, subslot } <= { next_group, next_sub };
+ if( { next_group, next_sub }==5'd0 ) begin
+ slot <= 18'd1;
+ end else begin
+ slot <= { slot[16:0], 1'b0 };
+ end
+ op <= next_sub >= 3'd3;
+ end
+end
+
+endmodule
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopl_timers.v b/verilog/jtopl/hdl/jtopl_timers.v
new file mode 100644
index 0000000..636e135
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopl_timers.v
@@ -0,0 +1,125 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 10-6-2020
+
+ */
+
+module jtopl_timers(
+ input clk,
+ input rst,
+ input cenop,
+ input zero,
+ input [7:0] value_A,
+ input [7:0] value_B,
+ input load_A,
+ input load_B,
+ input clr_flag_A,
+ input clr_flag_B,
+ output flag_A,
+ output flag_B,
+ input flagen_A,
+ input flagen_B,
+ output overflow_A,
+ output irq_n
+);
+
+wire pre_A, pre_B;
+
+assign flag_A = pre_A & flagen_A;
+assign flag_B = pre_B & flagen_B;
+
+assign irq_n = ~( flag_A | flag_B );
+
+// 1 count per 288 master clock ticks
+jtopl_timer #(.MW(2)) timer_A (
+ .clk ( clk ),
+ .rst ( rst ),
+ .cenop ( cenop ),
+ .zero ( zero ),
+ .start_value( value_A ),
+ .load ( load_A ),
+ .clr_flag ( clr_flag_A),
+ .flag ( pre_A ),
+ .overflow ( overflow_A)
+);
+
+// 1 count per 288*4 master clock ticks
+jtopl_timer #(.MW(4)) timer_B(
+ .clk ( clk ),
+ .rst ( rst ),
+ .cenop ( cenop ),
+ .zero ( zero ),
+ .start_value( value_B ),
+ .load ( load_B ),
+ .clr_flag ( clr_flag_B),
+ .flag ( pre_B ),
+ .overflow ( )
+);
+
+endmodule
+
+module jtopl_timer #(parameter MW=2) (
+ input clk,
+ input rst,
+ input cenop,
+ input zero,
+ input [7:0] start_value,
+ input load,
+ input clr_flag,
+ output reg flag,
+ output reg overflow
+);
+
+reg [7:0] cnt, next, init;
+reg [MW-1:0] free_cnt, free_next;
+reg load_l, free_ov;
+
+always@(posedge clk)
+ if( clr_flag || rst)
+ flag <= 1'b0;
+ else if(cenop && zero && load && overflow) flag<=1'b1;
+
+always @(*) begin
+ {free_ov, free_next} = { 1'b0, free_cnt} + 1'b1;
+/* verilator lint_off WIDTH */
+ {overflow, next } = {1'b0, cnt}+free_ov;
+/* verilator lint_on WIDTH */
+ init = start_value;
+end
+
+always @(posedge clk) begin
+ load_l <= load;
+ if( (!load_l && load) || rst) begin
+ cnt <= start_value;
+ end else if( cenop && zero && load ) begin
+ cnt <= overflow ? init : next;
+ end
+end
+
+// Free running counter, resetting
+// the value of this part with the load
+// event can slow down music, vg Bad Dudes
+always @(posedge clk) begin
+ if( rst ) begin
+ free_cnt <= 0;
+ end else if( cenop && zero ) begin
+ free_cnt <= free_next;
+ end
+end
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopll.yaml b/verilog/jtopl/hdl/jtopll.yaml
new file mode 100644
index 0000000..2a8a1ed
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopll.yaml
@@ -0,0 +1,3 @@
+here:
+ - jtopll.v
+ - jtopl.yaml
\ No newline at end of file
diff --git a/verilog/jtopl/hdl/jtopll_mmr.v b/verilog/jtopl/hdl/jtopll_mmr.v
new file mode 100644
index 0000000..642953f
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopll_mmr.v
@@ -0,0 +1,207 @@
+/* This file is part of JTOPL.
+
+ JTOPL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 10-6-2020
+ */
+
+module jtopll_mmr(
+ input rst,
+ input clk,
+ input cen,
+ output cenop,
+ input [ 7:0] din,
+ input write,
+ input addr,
+ // location
+ output zero,
+ output [ 1:0] group,
+ output op,
+ output [17:0] slot,
+ output reg rhy_en,
+ // Phase Generator
+ output [ 8:0] fnum_I,
+ output [ 2:0] block_I,
+ output [ 3:0] mul_II,
+ output viben_I,
+ // Operator
+ output [ 1:0] wavsel_I,
+ // Envelope Generator
+ output keyon_I,
+ output en_sus_I, // enable sustain
+ output [ 3:0] arate_I, // attack rate
+ output [ 3:0] drate_I, // decay rate
+ output [ 3:0] rrate_I, // release rate
+ output [ 3:0] sl_I, // sustain level
+ output ks_II, // key scale
+ output [ 5:0] tl_IV,
+ output amen_IV,
+ // global values
+ output reg am_dep,
+ output reg vib_dep,
+ output [ 1:0] ksl_IV,
+ // Operator configuration
+ output [ 2:0] fb_I,
+ output con_I
+);
+
+parameter OPL_TYPE=1;
+
+jtopl_div #(OPL_TYPE) u_div (
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cen ),
+ .cenop ( cenop )
+);
+
+localparam [7:0] REG_TESTYM = 8'h0F,
+ REG_RYTHM = 8'h0E;
+
+reg [ 7:0] selreg; // selected register
+reg [ 7:0] din_copy;
+reg csm, effect;
+reg [ 1:0] sel_group; // group to update
+reg [ 2:0] sel_sub; // subslot to update
+reg up_fnumlo, up_fnumhi, up_inst,
+ up_original;
+reg wave_mode, // 1 if waveform selection is enabled (OPL2)
+ csm_en,
+ note_sel; // keyboard split, not implemented
+reg [ 4:0] rhy_kon;
+
+// this runs at clk speed, no clock gating here
+// if I try to make this an async rst it fails to map it
+// as flip flops but uses latches instead. So I keep it as sync. reset
+always @(posedge clk) begin
+ if( rst ) begin
+ selreg <= 0;
+ sel_group <= 0;
+ sel_sub <= 0;
+ // Updaters
+ up_inst <= 0;
+ up_fnumlo <= 0;
+ up_fnumhi <= 0;
+ up_original <= 0;
+ // Rhythms
+ rhy_en <= 0;
+ rhy_kon <= 0;
+ // sensitivity to LFO
+ am_dep <= 0;
+ vib_dep <= 0;
+ csm_en <= 0;
+ note_sel <= 0;
+ // OPL2 waveforms
+ wave_mode <= 0;
+ din_copy <= 0;
+ end else begin
+ // WRITE IN REGISTERS
+ if( write ) begin
+ if( !addr ) begin
+ selreg <= din;
+ end else begin
+ // Global registers
+ din_copy <= din;
+ up_fnumhi <= 0;
+ up_fnumlo <= 0;
+ up_inst <= 0;
+ up_original <= 0;
+ // Operator registers
+ // Mapping done according to Table 2-3, page 7 of YM3812 App. Manual
+ if( selreg < 8 ) begin
+ up_original <= 1;
+ sel_sub <= selreg[2:0];
+ end
+ // Channel registers
+ if( selreg[3:0]<=4'd8) begin
+ case( selreg[7:4] )
+ 4'h1: up_fnumlo <= 1;
+ 4'h2: up_fnumhi <= 1;
+ 4'h3: up_inst <= 1;
+ default:;
+ endcase
+ end
+ if( selreg[7:4]>=4'h1 && selreg[7:4]<4'h4
+ && selreg[3:0]<=8 ) begin
+ // Each group has three channels
+ // Channels 0-2 -> group 0
+ // Channels 3-5 -> group 1
+ // Channels 6-8 -> group 2
+ // other -> group 3 - ignored
+ sel_group <= selreg[3:0] < 4'd3 ? 2'd0 :
+ selreg[3:0] < 4'd6 ? 2'd1 :
+ selreg[3:0] < 4'd9 ? 2'd2 : 2'd3;
+ sel_sub <= selreg[3:0] < 4'd6 ? selreg[2:0] :
+ { 1'b0, ~&selreg[2:1], selreg[0] };
+ end
+ // Global register
+ if( selreg==REG_RYTHM ) begin
+ am_dep <= din[7];
+ vib_dep <= din[6];
+ rhy_en <= din[5];
+ rhy_kon <= din[4:0];
+ end
+ end
+ end
+ end
+end
+
+jtopll_reg u_reg(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cenop ),
+ .din ( din_copy ),
+ // Pipeline order
+ .zero ( zero ),
+ .group ( group ),
+ .op ( op ),
+ .slot ( slot ),
+
+ .sel_group ( sel_group ), // group to update
+ .sel_sub ( sel_sub ), // subslot to update
+
+ .rhy_en ( rhy_en ),
+ .rhy_kon ( rhy_kon ),
+
+ .up_original( up_original ),
+ .up_inst ( up_inst ),
+ .up_fnumlo ( up_fnumlo ),
+ .up_fnumhi ( up_fnumhi ),
+
+ // PG
+ .fnum_I ( fnum_I ),
+ .block_I ( block_I ),
+ .mul_II ( mul_II ),
+ .viben_I ( viben_I ),
+ // OP
+ .wavsel_I ( wavsel_I ),
+ .wave_mode ( wave_mode ),
+ // EG
+ .keyon_I ( keyon_I ),
+ .en_sus_I ( en_sus_I ),
+ .arate_I ( arate_I ),
+ .drate_I ( drate_I ),
+ .rrate_I ( rrate_I ),
+ .sl_I ( sl_I ),
+ .ks_II ( ks_II ),
+ .ksl_IV ( ksl_IV ),
+ .amen_IV ( amen_IV ),
+ .tl_IV ( tl_IV ),
+ // Timbre - Neiro
+ .fb_I ( fb_I ),
+ .con_I ( con_I )
+);
+
+endmodule
diff --git a/verilog/jtopl/hdl/jtopll_reg.v b/verilog/jtopl/hdl/jtopll_reg.v
new file mode 100644
index 0000000..50f8711
--- /dev/null
+++ b/verilog/jtopl/hdl/jtopll_reg.v
@@ -0,0 +1,208 @@
+/* This file is part of JTOPL
+
+ JTOPL program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JTOPL program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JTOPL. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 27-5-2022
+
+*/
+
+module jtopll_reg(
+ input rst,
+ input clk,
+ input cen,
+ input [7:0] din,
+
+ input rhy_en, // rhythm enable
+ input [4:0] rhy_kon, // key-on for each rhythm instrument
+
+ // Pipeline order
+ output zero,
+ output [1:0] group,
+ output op, // 0 for modulator operators
+ output [17:0] slot, // hot one encoding of active slot
+
+ // Register update
+ input [1:0] sel_group, // group to update
+ input [2:0] sel_sub, // subslot to update
+
+ input up_fnumlo,
+ input up_fnumhi,
+ input up_inst,
+ input up_original,
+
+ // PG
+ output [8:0] fnum_I,
+ output [2:0] block_I,
+ // channel configuration
+ output [2:0] fb_I,
+
+
+ output reg [3:0] mul_II, // frequency multiplier
+ output [1:0] ksl_IV, // key shift level
+ output amen_IV,
+ output viben_I,
+ // OP
+ output [1:0] wavsel_I,
+ input wave_mode,
+ // EG
+ output keyon_I,
+ output [5:0] tl_IV,
+ output en_sus_I, // enable sustain
+ output [3:0] arate_I, // attack rate
+ output [3:0] drate_I, // decay rate
+ output [3:0] rrate_I, // release rate
+ output [3:0] sl_I, // sustain level
+ output reg ks_II, // key scale
+ output con_I // 1 for adding the modulator operator at the accumulator
+ // carrier op. are always added
+
+);
+
+localparam CH=9;
+
+
+reg [ 5:0] rhy_csr;
+wire rhy_oen, rhyon_csr;
+wire [ 2:0] subslot;
+wire match;
+wire [ 3:0] vol_I; // channel volume
+
+wire [ 5:0] tl_I;
+wire [ 1:0] ksl_I;
+wire [ 3:0] mul_I;
+wire amen_I, ks_I;
+
+// The original instrument (programmable patch) is at location 0
+reg [63:0] patch[0:(16+6-1)]; // instrument memory, 15 instruments + original + 6 drums
+wire [ 3:0] inst_I;
+wire [ 4:0] inst_sel;
+
+assign wavsel_I[1] = 0;
+assign match = { group, subslot } == { sel_group, sel_sub};
+
+jtopl_slot_cnt u_slot_cnt(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cen ),
+ // Pipeline order
+ .zero ( zero ),
+ .group ( group ),
+ .op ( op ), // 0 for modulator operators
+ .subslot(subslot ),
+ .slot ( slot ) // hot one encoding of active slot
+);
+
+always @(posedge clk, posedge rst) begin
+ if( rst ) begin
+ patch[00]<=64'h0000000000000000;
+ patch[01]<=64'h170078D0171E6171;
+ patch[02]<=64'h1323F7D80D1A4113;
+ patch[03]<=64'h2311C4F200990113;
+ patch[04]<=64'h277064A8070E6131;
+ patch[05]<=64'h280076E0061E2132;
+ patch[06]<=64'h180071E005162231;
+ patch[07]<=64'h07108182071D6121;
+ patch[08]<=64'h070072A2142D2123;
+ patch[09]<=64'h17106564061B6161;
+ patch[10]<=64'h0771F785180B6141;
+ patch[11]<=64'h0410E4FA11830113;
+ patch[12]<=64'h1222F8F80724C117;
+ patch[13]<=64'h4220F5C2050C5061;
+ patch[14]<=64'h020395C903550101;
+ patch[15]<=64'h1340E4F103894161;
+ patch[16]<=64'h006A00DF0F180001;
+ patch[17]<=64'h00A700C800000001;
+ patch[18]<=64'h005900F800000005;
+ patch[19]<=64'h6D00F80000000100;
+ patch[20]<=64'h4800D80000000100;
+ patch[21]<=64'h5500AA0000000100;
+ end else begin
+ if( up_original ) begin
+ patch[0][ {sel_sub,3'd0} +: 8 ] <= din;
+ end
+ end
+end
+
+// Selects the current patch
+assign inst_sel = rhy_oen ? { 2'b10, subslot } : { 1'b0, inst_I };
+assign { amen_I, viben_I, en_sus_I, ks_I, mul_I } = patch[ inst_sel ][ (op ? 8:0) +: 8 ];
+assign ksl_I = patch[ inst_sel ][ (op ? 31:23) -: 2 ];
+assign tl_I =
+ rhy_oen & (slot[13] | slot[14]) ? { inst_I, 2'd0 } : // HH and TT have the volume set this way
+ op ? { vol_I, 2'd0 } : patch[ inst_sel ][ 16 +: 6 ];
+assign wavsel_I[0] = patch[ inst_sel ][ op ? 28 : 27];
+assign fb_I = op ? 3'd0 : patch[ inst_sel ][ 24 +: 3 ];
+assign { arate_I, drate_I } = patch[ inst_sel ][ (op ? 40 : 32) +: 8 ];
+assign { sl_I, rrate_I } = patch[ inst_sel ][ op ? 56 : 48 +: 8 ];
+
+always @(posedge clk, posedge rst) begin
+ if( rst ) begin
+ { ks_II, mul_II } <= 0;
+ end else if(cen) begin
+ { ks_II, mul_II } <= { ks_I, mul_I };
+ end
+end
+
+jtopl_sh_rst #(.width(2+1+6),.stages(3)) u_iv(
+ .clk ( clk ),
+ .cen ( cen ),
+ .rst ( rst ),
+ .din ( { ksl_I, amen_I, tl_I } ),
+ .drop ( { ksl_IV, amen_IV, tl_IV } )
+);
+
+// Memory for CH registers
+localparam KONW = 1,
+ SUSENW = 1,
+ FNUMW = 9,
+ BLOCKW = 3,
+ INSTW = 4,
+ VOLW = 4;
+localparam CHCSRW = SUSENW+KONW+FNUMW+BLOCKW+INSTW+VOLW;
+
+wire [CHCSRW-1:0] chcfg, chcfg_inmux;
+wire sus_en, keyon_csr, con_csr;
+wire up_fnumlo_ch = up_fnumlo & match,
+ up_fnumhi_ch = up_fnumhi & match,
+ up_inst_ch = up_inst & match;
+
+assign chcfg_inmux = {
+ up_fnumhi_ch ? din[5:0] : { sus_en, keyon_csr, block_I, fnum_I[8] },
+ up_fnumlo_ch ? din : fnum_I[7:0],
+ up_inst_ch ? din : { inst_I, vol_I }
+};
+
+assign con_I = rhy_oen && !slot[12]; // slot 12 = BD, which uses modulation
+ // slots 13/14 as rhythm, need to be added in the accumulator, so con_I is set to 1
+assign { sus_en, keyon_csr, block_I, fnum_I[8:0], inst_I, vol_I } = chcfg;
+assign keyon_I = rhy_oen ? rhyon_csr : keyon_csr;
+
+jtopl_reg_ch#(CHCSRW) u_reg_ch(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cen ),
+ .zero ( zero ),
+ .rhy_en ( rhy_en ),
+ .rhy_kon ( rhy_kon ),
+ .slot ( slot ),
+ .group ( group ),
+ .chcfg_inmux ( chcfg_inmux ),
+ .chcfg ( chcfg ),
+ .rhy_oen ( rhy_oen ),
+ .rhyon_csr ( rhyon_csr )
+);
+
+endmodule
diff --git a/verilog/jtopl/ver/jtopl_eg/sweep.cpp b/verilog/jtopl/ver/jtopl_eg/sweep.cpp
new file mode 100644
index 0000000..9ba347e
--- /dev/null
+++ b/verilog/jtopl/ver/jtopl_eg/sweep.cpp
@@ -0,0 +1,151 @@
+/*
+
+ This file runs a simulation on the purely combinational logic of the envelope generator.
+ The simulation is controlled via text files
+
+ The text file is a sequence of write commands that will configure the inputs to the logic
+ then a wait command will kick the simulation for a given number of clocks
+
+ The LFO is always running. The simulations show that SSG is well implemented and that
+ the circuit behaves within bounds for extreme cases
+
+ The core logic of the ASDR envelope is simulated on separate test bench eg2.
+
+ Arguments:
+ -w write VCD (always enabled, uncomment to evaluate this argument)
+ -o path-to-test-file
+
+*/
+
+#include
+#include
+#include
+#include
+#include "Vsweep.h"
+#include "verilated_vcd_c.h"
+
+using namespace std;
+
+
+vluint64_t main_time = 0; // Current simulation time
+const vluint64_t HALFPERIOD=133; // 3.57MHz (133ns * 2)
+Vsweep top;
+VerilatedVcdC* vcd;
+bool keep = true;
+
+void clock(int n) {
+ while( n-->0 ) {
+ top.eval();
+ if(keep) vcd->dump(main_time);
+
+ main_time += HALFPERIOD;
+ top.clk=1;
+ top.eval();
+ if(keep) vcd->dump(main_time);
+
+ main_time += HALFPERIOD;
+ top.clk=0;
+ }
+}
+
+double sc_time_stamp () { // Called by $time in Verilog
+ return main_time;
+}
+
+void reset() {
+ top.rst = 1;
+ top.keyon_I = 0;
+ clock(18*2);
+ top.rst = 0;
+}
+
+void attack_sweep() {
+ // YM2413
+ // float atime[] = { 0.1, 1738,863,432,216,108,54,27,13.52,6.76,3.38,1.69,0.84,0.5,0.28,0.1 };
+ // YM3812
+ float atime[] = { 0.1, 2826, 1413, 706, 353, 176, 88, 44, 22, 11, 5.52, 2.76, 1.40, 0.7, 0.38,0.1 };
+
+ top.ksr_II=1;
+ for( top.arate_I=3; top.arate_I<15; top.arate_I++ )
+ for( top.block_I=0; top.block_I<4; top.block_I++ )
+ {
+ reset();
+ top.keyon_I = 1;
+ vluint64_t t0=main_time;
+ int limit=1'000'000;
+ if( top.arate_I!=0) {
+ while( (int)top.eg_V!=0 && --limit ) {
+ clock( 10 );
+ }
+ }
+ if( limit==0 ) {
+ cout << "ARATE " << hex << (int)top.arate_I << dec << " timeout " << "(" << (int) top.eg_V << ")\n";
+ if( top.arate_I>5 ) break; // do not continue
+ }
+ if( (int)top.eg_V==0 ) {
+ float delta=(float)main_time-t0;
+ delta /= 1e6;
+ float err = (delta-atime[top.arate_I])/atime[top.arate_I]*100.0;
+ printf("ARATE %X (block %d) %6.2f ms (%4.1f %%)\n",
+ top.arate_I, top.block_I, delta, err );
+ }
+ }
+}
+
+void decay_sweep() {
+ // YM3812
+ float atime[] = { 0.1, 39280,19640,9820,4910,2455,1227,613.7,306.8,153.44,76.72,38.36,19.20,9.60,4.80,2.40 };
+
+ top.arate_I=15;
+ top.sl_I=15;
+ for( top.drate_I= keep ? 5 : 1; top.drate_I<16; top.drate_I++ ) {
+ reset();
+ top.keyon_I = 1;
+ clock( 100 );
+ vluint64_t t0=main_time;
+ int limit=1'000'000;
+ if( top.drate_I!=0) {
+ while( (int)top.eg_V<0x3e0 && --limit ) {
+ clock( 10 );
+ }
+ }
+ if( limit==0 ) {
+ cout << "DRATE " << hex << (int)top.drate_I << dec << " timeout " << "(" << (int) top.eg_V << ")\n";
+ if( top.drate_I>5 ) break; // do not continue
+ }
+ if( (int)top.eg_V>=0x3e0 ) {
+ float delta=(float)main_time-t0;
+ delta /= 1e6;
+ float err = (delta-atime[top.drate_I])/atime[top.drate_I]*100.0;
+ printf("DRATE %X (block %d) %6.2f ms (%4.1f %%)\n",
+ top.drate_I, top.block_I, delta, err );
+ } else {
+ printf("End not reached\n");
+ }
+ }
+}
+
+int main(int argc, char *argv[]) {
+ int err_code=0;
+ vcd = new VerilatedVcdC;
+ bool trace=true;
+
+ if( trace ) {
+ Verilated::traceEverOn(true);
+ top.trace(vcd,99);
+ vcd->open("test.vcd");
+ }
+
+ //attack_sweep();
+ decay_sweep();
+
+ if(trace) vcd->close();
+ // VerilatedCov::write("logs/coverage.dat");
+ delete vcd;
+ return err_code;
+}
+
+
+void remove_blanks( char*& str ) {
+ while( *str!=0 && (*str==' ' || *str=='\t') ) str++;
+}
\ No newline at end of file
diff --git a/verilog/jtopl/ver/jtopl_eg/sweep.sh b/verilog/jtopl/ver/jtopl_eg/sweep.sh
new file mode 100644
index 0000000..79cc28c
--- /dev/null
+++ b/verilog/jtopl/ver/jtopl_eg/sweep.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+if ! verilator -f sweep.f sweep.cpp --cc --exe --trace --timescale 1ns/1ns > s; then
+ cat s; rm s
+ exit $?
+fi
+
+if ! make -j -C obj_dir -f Vsweep.mk Vsweep > s; then
+ cat s; rm s
+ exit $?
+fi
+
+if ! obj_dir/Vsweep $*; then
+ exit $?
+fi
+# verilator_coverage logs/coverage.dat --annotate coverage
diff --git a/verilog/jtopl/ver/jtopl_eg/sweep.v b/verilog/jtopl/ver/jtopl_eg/sweep.v
new file mode 100644
index 0000000..0e490ae
--- /dev/null
+++ b/verilog/jtopl/ver/jtopl_eg/sweep.v
@@ -0,0 +1,86 @@
+module test(
+ input rst,
+ input clk,
+ input eg_stop,
+ // envelope configuration
+ input en_sus_I, // enable sustain
+ input [3:0] arate_I, // attack rate
+ input [3:0] drate_I, // decay rate
+ input [3:0] rrate_I, // release rate
+ input [3:0] sl_I, // sustain level
+ input ksr_II, // key scale
+ // envelope operation
+ input keyon_I,
+ // envelope number
+ input [9:0] fnum_I,
+ input [2:0] block_I,
+ input [3:0] lfo_mod,
+ input amsen_IV,
+ input ams_IV,
+ input [5:0] tl_IV,
+ input [1:0] ksl_IV,
+
+ output reg [9:0] eg_V,
+ output reg pg_rst_II
+);
+
+wire cenop, zero, op;
+wire kon = keyon_I;// & zero;
+wire [17:0] slot;
+wire [ 3:0] keycode_II = { block_I, fnum_I[9] };
+
+reg cen=0;
+
+always @(posedge clk) cen <= ~cen;
+
+jtopl_div u_div(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cen ),
+ .cenop ( cenop ) // clock enable at operator rate
+);
+
+jtopl_slot_cnt u_slot_cnt(
+ .rst ( rst ),
+ .clk ( clk ),
+ .cen ( cenop ),
+
+ // Pipeline order
+ .zero ( zero ),
+ .group ( ),
+ .op ( op ), // 0 for modulator operators
+ .subslot( ),
+ .slot ( slot ) // hot one encoding of active slot
+);
+
+jtopl_eg uut(
+ .rst ( rst ),
+ .clk ( clk ),
+ // .cen ( cen ),Mas
+ .cenop ( cenop ),
+ .zero ( zero ),
+ .eg_stop ( eg_stop ),
+ // envelope configuration
+ .en_sus_I ( en_sus_I ), // enable sustain
+ .keycode_II ( keycode_II ),
+ .arate_I ( arate_I ), // attack rate
+ .drate_I ( drate_I ), // decay rate
+ .rrate_I ( rrate_I ), // release rate
+ .sl_I ( sl_I ), // sustain level
+ .ksr_II ( ksr_II ), // key scale
+ // envelope operation
+ .keyon_I ( kon ),
+ // envelope number
+ .fnum_I ( fnum_I ),
+ .block_I ( block_I ),
+ .lfo_mod ( lfo_mod ),
+ .amsen_IV ( amsen_IV ),
+ .ams_IV ( ams_IV ),
+ .tl_IV ( tl_IV ),
+ .ksl_IV ( ksl_IV ),
+
+ .eg_V ( eg_V ),
+ .pg_rst_II ( pg_rst_II )
+);
+
+endmodule
\ No newline at end of file
diff --git a/verilog/jtopl/ver/jtopl_eg_comb_tb/README b/verilog/jtopl/ver/jtopl_eg_comb_tb/README
new file mode 100644
index 0000000..d76a773
--- /dev/null
+++ b/verilog/jtopl/ver/jtopl_eg_comb_tb/README
@@ -0,0 +1,15 @@
+This file runs a simulation on the purely combinational logic of the envelope generator.
+The simulation is controlled via text files
+
+The text file is a sequence of write commands that will configure the inputs to the logic
+then a wait command will kick the simulation for a given number of clocks. Test files
+are stored in the tests folder.
+
+The LFO is always running. The simulations show that SSG is well implemented and that
+the circuit behaves within bounds for extreme cases
+
+The core logic of the ASDR envelope is simulated on separate test bench eg2.
+
+Arguments:
+ -w write VCD (always enabled, uncomment to evaluate this argument)
+ -f path-to-test-file
\ No newline at end of file
diff --git a/verilog/jtopl/ver/jtopl_eg_comb_tb/sim.sh b/verilog/jtopl/ver/jtopl_eg_comb_tb/sim.sh
new file mode 100644
index 0000000..02ad4d4
--- /dev/null
+++ b/verilog/jtopl/ver/jtopl_eg_comb_tb/sim.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+if ! verilator -f gather.f test.cpp --cc --exe --trace > s; then
+ cat s; rm s
+ exit $?
+fi
+
+if ! make -j -C obj_dir -f Vtest.mk Vtest > s; then
+ cat s; rm s
+ exit $?
+fi
+
+if ! obj_dir/Vtest $*; then
+ exit $?
+fi
+# verilator_coverage logs/coverage.dat --annotate coverage
diff --git a/verilog/jtopl/ver/jtopl_eg_comb_tb/test.cpp b/verilog/jtopl/ver/jtopl_eg_comb_tb/test.cpp
new file mode 100644
index 0000000..78880d4
--- /dev/null
+++ b/verilog/jtopl/ver/jtopl_eg_comb_tb/test.cpp
@@ -0,0 +1,200 @@
+/*
+
+ This file runs a simulation on the purely combinational logic of the envelope generator.
+ The simulation is controlled via text files
+
+ The text file is a sequence of write commands that will configure the inputs to the logic
+ then a wait command will kick the simulation for a given number of clocks
+
+ The LFO is always running. The simulations show that SSG is well implemented and that
+ the circuit behaves within bounds for extreme cases
+
+ The core logic of the ASDR envelope is simulated on separate test bench eg2.
+
+ Arguments:
+ -w write VCD (always enabled, uncomment to evaluate this argument)
+ -o path-to-test-file
+
+*/
+
+#include
+#include
+#include
+#include "Vtest.h"
+#include "verilated_vcd_c.h"
+
+using namespace std;
+
+class Stim {
+ ifstream file;
+ int wait_count, line_cnt;
+ void read_line();
+public:
+ // inputs
+ int keyon_now, keyoff_now, state_in, eg_in, arate, drate, rate2, rrate, sl,
+ keycode, eg_cnt, cnt_in, ksr, en_sus, sum_up_in,
+ lfo_mod, amsen, ams, tl;
+ // outputs
+ int state_next, pg_rst, cnt_lsb, pure_eg_out, eg_out, sum_up_out;
+ void reset();
+ void apply(Vtest* dut);
+ void get(Vtest* dut);
+ void next(Vtest* dut);
+ void open(const char *filename);
+ bool done() { return file.eof() && wait_count==0; }
+ Stim() { reset(); }
+};
+
+vluint64_t main_time = 0; // Current simulation time
+
+double sc_time_stamp () { // Called by $time in Verilog
+ return main_time;
+}
+
+int main(int argc, char *argv[]) {
+
+ Vtest* top = new Vtest;
+ Stim stim;
+ int err_code=0;
+ VerilatedVcdC* vcd = new VerilatedVcdC;
+ bool trace=true;
+
+ for(int k=1; ktrace(vcd,99);
+ vcd->open("test.vcd");
+ }
+
+ // time 0 state:
+ stim.apply(top); top->eval();
+ if(trace) vcd->dump(main_time);
+
+ try{
+ do {
+ stim.next(top);
+ if(trace) vcd->dump(main_time);
+ } while( !stim.done() );
+ } catch( int ) {}
+ quit:
+ if(trace) vcd->close();
+ // VerilatedCov::write("logs/coverage.dat");
+ delete vcd;
+ delete top;
+ return err_code;
+}
+
+
+void Stim::reset() {
+ keyon_now=0, keyoff_now=0, state_in=0, eg_in=0x3ff, arate=0, drate=0,
+ rrate=0, sl=0, en_sus=0,
+ keycode=0, eg_cnt=0, cnt_in=0, ksr=0,
+ lfo_mod=0, amsen=0, ams=0, tl=0;
+ sum_up_in = 0;
+ wait_count=0;
+}
+
+void Stim::apply(Vtest* dut) {
+ dut->keyon_now = keyon_now;
+ dut->keyoff_now = keyoff_now;
+ dut->state_in = state_in;
+ dut->sum_up_in = sum_up_in;
+ dut->eg_in = eg_in;
+ dut->arate = arate;
+ dut->drate = drate;
+ dut->rrate = rrate;
+ dut->sl = sl;
+ dut->en_sus = en_sus;
+
+ dut->keycode = keycode;
+ dut->eg_cnt = eg_cnt;
+ dut->cnt_in = cnt_in;
+ dut->ksr = ksr;
+
+ dut->lfo_mod = lfo_mod;
+ dut->amsen = amsen;
+ dut->ams = ams;
+ dut->tl = tl;
+}
+
+void Stim::get(Vtest* dut) {
+ state_next = dut->state_next;
+ pg_rst = dut->pg_rst;
+ cnt_lsb = dut->cnt_lsb;
+ pure_eg_out = dut->pure_eg_out;
+ eg_out = dut->eg_out;
+ sum_up_out = dut->sum_up_out;
+}
+
+void Stim::next(Vtest* dut) {
+ read_line();
+ apply(dut);
+ dut->eval();
+ main_time+=20000; // zero period=20us = 4*18*1/3.6MHz
+ get(dut);
+ state_in = state_next;
+ eg_in = pure_eg_out;
+ cnt_in = cnt_lsb;
+ sum_up_in = sum_up_out;
+ eg_cnt++;
+ lfo_mod++;
+ if( wait_count>0 ) wait_count--;
+}
+
+void Stim::open(const char *filename ) {
+ line_cnt = 0;
+ file.open(filename);
+}
+
+void remove_blanks( char*& str ) {
+ while( *str!=0 && (*str==' ' || *str=='\t') ) str++;
+}
+
+void Stim::read_line() {
+ while( !file.eof() && file.good() && wait_count==0) {
+ char line[256]="";
+ char *noblanks;
+ do{
+ file.getline(line,128);
+ line_cnt++;
+ //cout << line_cnt << '\n';
+ noblanks = line;
+ remove_blanks(noblanks);
+ } while( (noblanks[0]=='#' || strlen(line)==0) && !file.eof() );
+ char cmd[256];
+ int value;
+ if( sscanf( noblanks, "%[a-z_12] = %x", cmd, &value ) != 2 ) {
+ if( strcmp(cmd,"reset")==0 ) { reset(); continue; }
+ cout << "ERROR: Incomplete line " << line_cnt << "\n\t" << noblanks << '\n';
+ throw 1;
+ }
+ while( true ) {
+ if( strcmp(cmd,"keyon")==0 ) { keyon_now = value; break; }
+ if( strcmp(cmd,"keyoff")==0 ) { keyoff_now = value; break; }
+ if( strcmp(cmd,"state")==0 ) { state_in = value; break; }
+ if( strcmp(cmd,"eg")==0 ) { eg_in = value; break; }
+ if( strcmp(cmd,"arate")==0 ) { arate = value; break; }
+ if( strcmp(cmd,"drate")==0 ) { drate = value; break; }
+ if( strcmp(cmd,"rrate")==0 ) { rrate = value; break; }
+ if( strcmp(cmd,"sl")==0 ) { sl = value; break; }
+ if( strcmp(cmd,"keycode")==0) { keycode = value; break; }
+ if( strcmp(cmd,"eg_cnt")==0 ) { eg_cnt = value; break; }
+ if( strcmp(cmd,"cnt_in")==0 ) { cnt_in = value; break; }
+ if( strcmp(cmd,"ksr")==0 ) { ksr = value; break; }
+ if( strcmp(cmd,"lfo_mod")==0) { lfo_mod = value; break; }
+ if( strcmp(cmd,"amsen")==0 ) { amsen = value; break; }
+ if( strcmp(cmd,"ams")==0 ) { ams = value; break; }
+ if( strcmp(cmd,"tl")==0 ) { tl = value; break; }
+ if( strcmp(cmd,"wait")==0 ) { wait_count = value; break; }
+ cout << "ERROR: Unknown command '" << cmd << "'\n";
+ throw 2;
+ }
+ }
+}
\ No newline at end of file
diff --git a/verilog/jtopl/ver/jtopl_eg_comb_tb/test.v b/verilog/jtopl/ver/jtopl_eg_comb_tb/test.v
new file mode 100644
index 0000000..599b992
--- /dev/null
+++ b/verilog/jtopl/ver/jtopl_eg_comb_tb/test.v
@@ -0,0 +1,97 @@
+module test(
+ input keyon_now,
+ input keyoff_now,
+ input [ 2:0] state_in,
+ input [ 9:0] eg_in,
+ // envelope configuration
+ input en_sus, // enable sustain
+ input [ 3:0] arate, // attack rate
+ input [ 3:0] drate, // decay rate
+ input [ 3:0] rrate,
+ input [ 3:0] sl, // sustain level
+
+ output reg [ 2:0] state_next,
+ output reg pg_rst,
+ ///////////////////////////////////
+ // II
+ input [ 3:0] keycode,
+ input [14:0] eg_cnt,
+ input cnt_in,
+ input ksr,
+ output cnt_lsb,
+ output sum_up_out,
+ ///////////////////////////////////
+ // III
+ input sum_up_in,
+ output reg [ 9:0] pure_eg_out,
+ ///////////////////////////////////
+ // IV
+ input [ 3:0] lfo_mod,
+ input [ 3:0] fnum,
+ input [ 2:0] block,
+ input amsen,
+ input ams,
+ input [ 5:0] tl,
+ input [ 1:0] ksl,
+ input [ 3:0] final_keycode,
+ output reg [ 9:0] eg_out
+);
+
+wire [4:0] base_rate;
+wire attack = state_next[0];
+wire step;
+wire [5:0] step_rate_out;
+
+jtopl_eg_comb uut(
+ .keyon_now ( keyon_now ),
+ .keyoff_now ( keyoff_now ),
+ .state_in ( state_in ),
+ .eg_in ( eg_in ),
+ // envelope configuration
+ .en_sus ( en_sus ),
+ .arate ( arate ), // attack rate
+ .drate ( drate ), // decay rate
+ .rrate ( rrate ),
+ .sl ( sl ), // sustain level
+
+ .base_rate ( base_rate ),
+ .state_next ( state_next ),
+ .pg_rst ( pg_rst ),
+
+ // Frequency settings
+ .fnum ( fnum ),
+ .block ( block ),
+ .ksl ( ksl ),
+ .ksr ( ksr ),
+ .final_keycode ( final_keycode ),
+
+ ///////////////////////////////////
+ // II
+ .step_attack ( attack ),
+ .step_rate_in ( base_rate ),
+ .keycode ( keycode ),
+ .eg_cnt ( eg_cnt ),
+ .cnt_in ( cnt_in ),
+ .cnt_lsb ( cnt_lsb ),
+ .step ( step ),
+ .step_rate_out ( step_rate_out ),
+ .sum_up_out ( sum_up_out ),
+ ///////////////////////////////////
+ // III
+ .pure_attack ( attack ),
+ .pure_step ( step ) ,
+ .pure_rate (step_rate_out[5:1]),
+ .pure_eg_in ( eg_in ),
+ .pure_eg_out ( pure_eg_out ),
+ .sum_up_in ( sum_up_in ),
+ ///////////////////////////////////
+ // IV
+ .lfo_mod ( lfo_mod ),
+ .amsen ( amsen ),
+ .ams ( ams ),
+ .tl ( tl ),
+ .final_eg_in ( pure_eg_out ),
+ .final_eg_out ( eg_out )
+);
+
+endmodule // test
\ No newline at end of file
diff --git a/verilog/jtopl/ver/jtopl_eg_comb_tb/tests/attack.tst b/verilog/jtopl/ver/jtopl_eg_comb_tb/tests/attack.tst
new file mode 100644
index 0000000..dc73cec
--- /dev/null
+++ b/verilog/jtopl/ver/jtopl_eg_comb_tb/tests/attack.tst
@@ -0,0 +1,19 @@
+reset
+arate=A
+drate=7
+rrate=A
+sl=6
+keycode=0
+ksr=1
+
+keyon=1
+wait=1
+keyon=0
+
+wait=2000
+
+keyoff=1
+wait=1
+keyoff=0
+
+wait=2000
\ No newline at end of file
diff --git a/verilog/jtopl/ver/verilator/VGMParser.cpp b/verilog/jtopl/ver/verilator/VGMParser.cpp
new file mode 100644
index 0000000..14cb4ed
--- /dev/null
+++ b/verilog/jtopl/ver/verilator/VGMParser.cpp
@@ -0,0 +1,565 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "VGMParser.hpp"
+#include "WaveWritter.hpp"
+
+using namespace std;
+
+void JTTParser::open(const char* filename, int limit) {
+ file.open(filename);
+ if ( !file.good() ) cerr << "Failed to open file: " << filename << '\n';
+ cerr << "Open " << filename << '\n';
+ done=false;
+ line_cnt = 0;
+ // try to get the chip type from the 1st line
+ chip_cfg = ym3526;
+}
+
+void JTTParser::remove_blanks( char*& str ) {
+ if( str==NULL ) {
+ cerr << "Syntax error at line " << line_cnt << '\n';
+ throw 0;
+ }
+ while( *str!=0 && (*str==' ' || *str=='\t') ) str++;
+}
+
+void JTTParser::parse_opdata(char *txt_arg, int cmd_base) {
+ int ch, op, int_val, read=0;
+ read=sscanf( txt_arg, " %X , %X , %X ", &ch, &op, &int_val );
+ if( read != 3 ) {
+ cerr << "Syntax error at line " << line_cnt << '\n';
+ throw 0;
+ }
+ // adjust for writting order of device
+ switch(op) {
+ case 0: op=0; break;
+ case 1: op=2; break;
+ case 2: op=1; break;
+ case 3: op=3; break;
+ }
+
+ val = int_val;
+ cmd = cmd_base | ((op<<2) | ch);
+}
+
+void JTTParser::parse_chdata(char *txt_arg, int cmd_base) {
+ int ch, int_val, read=0;
+ read=sscanf( txt_arg, " %X , %X ", &ch, &int_val );
+ if( read == 1 ) {
+ int_val = ch;
+ ch = default_ch;
+ }
+ else if( read != 2 ) {
+ cerr << "Missing arguments at line " << line_cnt << '\n';
+ throw 0;
+ }
+
+ val = int_val;
+ cmd = cmd_base | ch;
+}
+
+JTTParser::JTTParser(int c) : RipParser(c) {
+ op_commands["dt_mul"] = 0x40;
+ op_commands["tl"] = 0x60;
+ op_commands["ks_ar"] = 0x60;
+ op_commands["amsen_d1r"] = 0x60;
+ op_commands["dt2_d2r"] = 0x60;
+ op_commands["d1l_rr"] = 0x60;
+
+
+ global_commands["kon"] = 0x28;
+ global_commands["timer"] = 0x27;
+ default_ch = 0;
+}
+
+JTTParser::~JTTParser() {
+}
+
+int JTTParser::parse() {
+ if(done) return cmd_finish;
+ while( !file.eof() && file.good() ) {
+ try {
+ char line[128]="";
+ char *noblanks;
+ do{
+ file.getline(line,128);
+ line_cnt++;
+ noblanks = line;
+ remove_blanks(noblanks);
+ } while( (noblanks[0]=='#' || strlen(line)==0) && !file.eof() );
+ if( strlen(line)==0 ) { done=true; return cmd_finish; }
+ char line2[128];
+ strncpy( line2, line, 128 ); line2[127]=0;
+ char *txt_cmd = strtok( line2, "#" );
+ // cerr << "TXT CMD = " << txt_cmd << "\n";
+ remove_blanks(txt_cmd);
+
+ if( txt_cmd[0]=='$' ) {
+ int aux0, aux1;
+ char *line=txt_cmd+1;
+ if( sscanf( line, "%X,%X", &aux0, &aux1 )!= 2 ) {
+ cerr << "ERROR: Incomplete line " << line_cnt << '\n';
+ return cmd_error;
+ }
+ addr = (aux0&0x100) ? 1 : 0;
+ cmd = aux0 & 0xff;
+ val = aux1 & 0xff;
+ return cmd_write;
+ }
+
+ if( strcmp(txt_cmd, "finish")==0 ) {
+ done=true;
+ return cmd_finish;
+ }
+ char *txt_arg = strchr( txt_cmd, ' ');
+ char cmd_base;
+ if( txt_arg==NULL ) {
+ cerr << "ERROR: Incomplete line " << line_cnt << '\n';
+ cerr << "txt_cmd = " << txt_cmd << '\n';
+ done=true;
+ return cmd_error;
+ }
+ *txt_arg = 0;
+ txt_arg++;
+
+ if( strcmp(txt_cmd, "wait")==0 ) {
+ int aux;
+ sscanf( txt_arg, "%d", &aux );
+ wait = aux;
+ wait *= 128*clk_period;
+ // cerr << "Wait for " << wait << '\n';
+ return cmd_wait;
+ }
+ // OP commands
+ auto op_cmd = op_commands.find(txt_cmd);
+ if( op_cmd != op_commands.end() ) {
+ cmd_base = op_cmd->second;
+ parse_opdata(txt_arg, cmd_base);
+ return cmd_write;
+ }
+ // CH commands
+ auto ch_cmd = ch_commands.find(txt_cmd);
+ if( ch_cmd != ch_commands.end() ) {
+ cmd_base = ch_cmd->second;
+ parse_chdata(txt_arg, cmd_base);
+ return cmd_write;
+ }
+ // Global commands
+ auto global_cmd = global_commands.find(txt_cmd);
+ if( global_cmd != global_commands.end() ) {
+ cmd = global_cmd->second;
+ int aux;
+ if( sscanf( txt_arg,"%X", &aux) != 1 ) {
+ cerr << "ERROR: Expecting value in line " << line_cnt << '\n';
+ return cmd_error;
+ }
+ val = (char)aux;
+ addr=0;
+ return cmd_write;
+ }
+
+ cerr << "ERROR: incorrect syntax at line " << line_cnt << '\n';
+ cerr << '\t' << line << '\n';
+ done=true;
+ return cmd_error;
+ }
+ catch( int ) { done=true; return cmd_error; }
+ }
+ done=true;
+ return cmd_finish;
+}
+
+
+uint64_t VGMParser::length() {
+ uint64_t l = totalwait*1e9/44100; // total number of samples in ns
+ return l;
+}
+
+void VGMParser::open(const char* filename, int limit) {
+ file.open(filename,ios_base::binary);
+ if ( !file.good() ) cerr << "Failed to open file: " << filename << '\n';
+ cerr << "Open " << filename << '\n';
+ stream_id = cmd = val = addr = 0;
+ file.seekg(0x18);
+ file.read((char*)& totalwait, 4);
+ totalwait &= 0xffffffff;
+ // read version number
+ char version[2];
+ file.seekg(0x08);
+ file.read( version,2 );
+ // Read the chip frequency, this is located at different
+ // positions depending on the chip type so it also determines
+ // which chip is used in the file
+ chip_cfg = unknown;
+ // Try to read the YM2413 frequency first
+ file.seekg(0x10); // offset to YM2413
+ file.read( (char*) &ym_freq, 4 );
+ if( ym_freq!=0 ) {
+ chip_cfg = ym2413;
+ } else {
+ file.seekg(0x50); // offset to YM3812
+ file.read( (char*) &ym_freq, 4 );
+ chip_cfg = ym3812;
+ }
+ cerr << "YM Freq = " << dec << ym_freq << " Hz\n";
+ // seek out data start
+ if( version[0]<0x50 && version[1]==1 ) {
+ cerr << "VGM version < 1.50 in this file. Data offset set at 0x40\n";
+ file.seekg(0x40);
+ }
+ else {
+ int32_t start;
+ file.seekg(0x34);
+ file.read( (char*)&start, 4 );
+ start+=0x34;
+ file.seekg(start);
+ }
+ done=false;
+ // open translation file
+ string aux = filename;
+ auto pos = aux.find_last_of('/');
+ if( pos == string::npos ) pos=0; else pos++;
+ aux = aux.substr( pos ); // trim path
+ aux = aux+".jtt";
+ ftrans.open(aux);
+ cur_time=0;
+ if( stream_data != NULL ) { delete stream_data; stream_data=NULL; }
+ data_offset=0;
+ pending_wait=0;
+ // max_PSG_warning = 10;
+ stream_notmplemented_info = true;
+}
+
+VGMParser::~VGMParser() {
+ file.close();
+ ftrans.close();
+ if( stream_data != NULL ) { delete stream_data; stream_data=NULL; }
+}
+
+void VGMParser::translate_cmd() {
+ char line[128];
+ int _cmd = cmd; _cmd&=0xff;
+ int _val = val; _val&=0xff;
+ bool done=false;
+ if(!done) sprintf(line,"$%d%02X,%02X", addr,_cmd,_val );
+ ftrans << line;
+ if( cmd == 0x28 ) {
+ if( val&0xf0 )
+ ftrans << " # Key on";
+ else
+ ftrans << " # Key off";
+ }
+ ftrans << '\n';
+}
+
+void VGMParser::translate_wait() {
+ float ws = wait;
+ ws /= 44100.0; // wait in seconds
+ cur_time += ws;
+ const float Tsyn = 24.0*clk_period*1e-9;
+ float wsyn = ws/Tsyn;
+ ftrans << "wait " << (int)wsyn << " # ";
+ ftrans << cur_time << " s\n";
+ //ftrans << wait << " -> " << ws << " Total: " << cur_time << "s \n";
+}
+
+int VGMParser::parse() {
+ if(done) return -1;
+ if( pending_wait !=0 ) {
+ wait = pending_wait;
+ translate_wait();
+ adjust_wait();
+ pending_wait = 0;
+ return cmd_wait; // request wait
+ }
+ while( !file.eof() && file.good() ) {
+ unsigned char vgm_cmd;
+ file.read( (char*)&vgm_cmd, 1);
+ if( !file.good() ) return -1; // finish immediately
+ // cerr << "VGM 0x" << hex << (((int)vgm_cmd)&0xff) << '\n';
+ char extra[2];
+ switch( vgm_cmd ) {
+ case 0x55: // YM2203 write
+ case 0x56:
+ case 0x58: // YM2610
+ // addr = 0;
+ file.read( extra, 2);
+ // cmd = extra[0];
+ // val = extra[1];
+ // translate_cmd();
+ // return cmd_write;
+ return cmd_nop;
+ //case 0xA5: // Write to dual YM2203
+ // file.read(extra,2); // ignore
+ // continue;
+ case 0x51: // YM2413 aa vv write
+ case 0x53: // A1=1
+ case 0x54: // YM2151 write
+ file.read( extra, 2);
+ cmd = extra[0];
+ val = extra[1];
+ translate_cmd();
+ return cmd_write;
+ case 0x57:
+ case 0x5A: // YM3812 write register
+ case 0x5B: // YM3526 write register
+ case 0x59: { // YM2610
+ addr = 1;
+ file.read( extra, 2);
+ cmd = extra[0];
+ val = extra[1];
+ // int icmd = ((int)cmd)&0xff;
+ // if( icmd < 0x30 ) {
+ // }
+ translate_cmd();
+ return cmd_write;
+ }
+ case 0x61:
+ uint16_t rd_wait;
+ file.read( (char*) &rd_wait, 2);
+ wait = rd_wait;
+ translate_wait();
+ adjust_wait();
+ return cmd_wait; // request wait
+ case 0x62:
+ wait = 735;
+ translate_wait();
+ adjust_wait();
+ return cmd_wait; // wait one frame (NTSC)
+ case 0x63:
+ wait = 882; // wait one frame (PAL)
+ translate_wait();
+ adjust_wait();
+ return cmd_wait;
+ case 0x66:
+ done=true;
+ return -1; // finish
+ // continue;
+ case 0x67: // data block:
+ {
+ file.seekg( 1, ios_base::cur ); // skip 0x66 byte
+ unsigned char type;
+ file.read( (char*)&type, 1 );
+ if( !(type==0 || (type >=0x80 && type<0xc0)) ) {// compressed stream
+ cerr << "ERROR: Unsupported data block type " << hex << (unsigned)type << '\n';
+ return -2;}
+ uint32_t length;
+ file.read( (char*)&length, 4 );
+ if( length == 0 ) {
+ cerr << "WARNING: zero-sized data stream in input file\n";
+ continue; }
+ switch( type ) {
+ case 0: { // uncompressed data
+ stream_data = new char[length];
+ file.read( stream_data, length );
+ break;
+ }
+ default: {
+ int skip = length;
+ cerr << "INFO: skipping unsupported block type "
+ << hex << (type&0xff) <<
+ " of length " << dec << skip << '\n';
+ if( skip!= 0 ) file.seekg( skip, ios_base::cur );
+ break;
+ }
+ }
+ }
+
+ // wait short commands (bad design option for VGM file designer)
+ case 0x70: case 0x71: case 0x72: case 0x73:
+ case 0x74: case 0x75: case 0x76: case 0x77:
+ case 0x78: case 0x79: case 0x7A: case 0x7B:
+ case 0x7c: case 0x7d: case 0x7e: case 0x7f:
+ wait=(vgm_cmd&0xf)+1;
+ translate_wait();
+ adjust_wait();
+ return 1;
+ case 0x4F: // PSG command, ignore
+ case 0x50:
+ file.read(extra,1);
+ cmd=extra[0];
+ /* { // Decode command
+ int lsb = cmd&0xf;
+ if( cmd & 0x80 )
+ switch( (cmd>>4)&0x7 ) {
+ case 0: cerr << "PSG Tone0 MSB\n"; break;
+ case 1: cerr << "PSG Tone1 MSB\n"; break;
+ case 2: cerr << "PSG Tone2 MSB\n"; break;
+ case 3: cerr << "PSG Noise CTRL\n"; break;
+ case 4: cerr << "PSG vol 0 = " << lsb <<'\n'; break;
+ case 5: cerr << "PSG vol 1 = " << lsb <<'\n'; break;
+ case 6: cerr << "PSG vol 2 = " << lsb <<'\n'; break;
+ case 7: cerr << "PSG vol 3 = " << lsb <<'\n'; break;
+ }
+ else cerr << "PSG repeat\n";
+ } */
+ return cmd_psg;
+ // DAC writes
+ case 0x80: case 0x81: case 0x82: case 0x83:
+ case 0x84: case 0x85: case 0x86: case 0x87:
+ case 0x88: case 0x89: case 0x8A: case 0x8B:
+ case 0x8c: case 0x8d: case 0x8e: case 0x8f:
+ pending_wait=(vgm_cmd&0xf); // will reply with a wait on next call
+ cmd=0x2a;
+ val=stream_data[data_offset++]; // buffer overrun risk here.
+ translate_cmd();
+ return cmd_write;
+ case 0x90: // setup stream control
+ {
+ char aux[4];
+ file.read( aux, 4);
+ stream_id = aux[0];
+ if( aux[1]!=2 ) {
+ cerr << "Error: DAC stream different from YM2612 type\n";
+ return cmd_error;
+ }
+ int cmd0 = aux[2], val0=aux[3];
+ cerr << "Stream ID " << stream_id << " write " << val0
+ << " to port " << cmd0 << '\n';
+ }
+ continue;
+ case 0x91: // set stream data
+ case 0x95: // start stream, fast call
+ {
+ if( stream_notmplemented_info ) {
+ cerr << "WARNING: Stream commands 0x90-0x95 are not implemented\n";
+ stream_notmplemented_info = false;
+ }
+ int32_t aux;
+ file.read( (char*) & aux, 4 );
+ continue; // not implemented
+ }
+ case 0x92: // set stream frequency
+ {
+ char tt;
+ int32_t aux;
+ file.read( &tt, 1 );
+ file.read( (char*) & aux, 4 );
+ continue; // not implemented
+ }
+ case 0x93: // start stream
+ {
+ char tt;
+ int32_t aux;
+ file.read( &tt, 1 );
+ file.read( (char*) & aux, 4 );
+ file.read( &tt, 1 );
+ file.read( (char*) & aux, 4 );
+ continue; // not implemented
+ }
+ case 0x94: // stop stream
+ {
+ char ss;
+ file.read( &ss, 1 );
+ }
+ case 0xe0:
+ file.read( (char*)&data_offset, 4);
+ continue;
+ default:
+ cerr << "ERROR: Unsupported VGM command 0x" << hex << (((int)vgm_cmd)&0xff)
+ << " at offset 0x" << (int)file.tellg() << '\n';
+ return -2;
+ }
+ }
+ return -1;
+}
+
+void Gym::open(const char* filename, int limit) {
+ file.open(filename,ios_base::binary);
+ if ( !file.good() ) cerr << "Failed to open file: " << filename << '\n';
+ cerr << "Open " << filename << '\n';
+ cmd = val = addr = 0;
+ count = 0;
+ max_PSG_warning = 10;
+ count_limit = limit;
+ chip_cfg = ym2612;
+}
+
+int Gym::parse() {
+ char c;
+ do {
+ if( ! file.good() ) return -1; // finish
+ file.read( &c, 1);
+ count++;
+ // cerr << "Read " << (int)c << '\n';
+ // cerr << (int) c << " @ " << file.tellg() << '\n';
+ if( count> count_limit && count_limit>0 ) {
+ cerr << "GYM command limit achieved.\n";
+ return -1;
+ }
+ switch(c) {
+ case 0:
+ wait = 735; // 16.7ms
+ adjust_wait();
+ return 1;
+ case 3: {
+ file.read(&c,1);
+ unsigned p = (unsigned char)c;
+ if(max_PSG_warning>0) {
+ max_PSG_warning--;
+ cerr << "Attempt to write to PSG port " << p << endl;
+ if(max_PSG_warning==0) cerr << "No more PSG warnings will be shown\n";
+ }
+ continue;
+ }
+ case 1:
+ case 2: {
+ char buf[2];
+ file.read(buf,2);
+ cmd = buf[0];
+ val = buf[1];
+ addr = (c == 2); // if c==2 then write to top bank of JT12
+ return cmd_write;
+ }
+ default:
+ cerr << "Wrong code ( " << ((int)c) << ") in GYM file\n";
+ continue;
+ }
+ }while(file.good());
+ // cerr << "Done\n";
+ return -1;
+}
+
+RipParser* ParserFactory( const char *filename, int clk_period ) {
+ string aux(filename);
+ auto ext = aux.find_last_of('.');
+ if( ext == string::npos ) {
+ cerr << "ERROR: The filename must end in .gym or .vgm\n";
+ return NULL;
+ }
+ RipParser *gym;
+ if( aux.substr(ext)==".gym") {
+ gym = new Gym(clk_period); gym->open(filename);
+ return gym;
+ }
+ if( aux.substr(ext)==".vgm") {
+ gym = new VGMParser(clk_period); gym->open(filename);
+ return gym;
+ }
+ if( aux.substr(ext)==".jtt") {
+ gym = new JTTParser(clk_period); gym->open(filename);
+ return gym;
+ }
+ cerr << "ERROR: The filename must end in .gym or .vgm\n";
+ return NULL;
+}
+
+int RipParser::period() {
+ return 0;
+}
+
+int VGMParser::period() {
+ // cerr << "Freq = " << ym_freq << '\n';
+ return ym_freq==0 ? 0 : 1000'000/(ym_freq/1000);
+}
+
+int JTTParser::period() {
+ return 280; // 3.57MHz
+}
+
diff --git a/verilog/jtopl/ver/verilator/VGMParser.hpp b/verilog/jtopl/ver/verilator/VGMParser.hpp
new file mode 100644
index 0000000..b4374b3
--- /dev/null
+++ b/verilog/jtopl/ver/verilator/VGMParser.hpp
@@ -0,0 +1,95 @@
+#ifndef __VGMPARSER
+#define __VGMPARSER
+
+#include
+#include