NSF/MIDI phase complete (#297)

-All expansion audio visualizers working
-MIDI can now switch between controlling APU Pulse/Triangle split, APU Pulse, APU Triangle, MMC5 Pulse and MMC5 Pulse/APU Triangle split with C0 (patch commands)
-- If patch bit 6 ($40-$7F), APU Triangle only and uses normal octaves.
-- If patch bit 3 ($8). Then behaves like below, but uses MMC5 pulses in place of APU pulses
-- If patch bit 2 ($4). Then only pulses used. If not set, triangle is used at G#3 and is up one octave. iei G3->G4 for the triangle.
-- Patch bits 0 and 1 ($0-$03) set the pulse duty cycle.
This commit is contained in:
greyrogue
2022-02-20 20:02:01 -05:00
committed by GitHub
parent 2bea127cb6
commit ec32192117
7 changed files with 2547 additions and 745 deletions

2
NES.sv
View File

@@ -1642,7 +1642,7 @@ always @(posedge clk) begin
ines[12] <= 8'h00;
ines[13] <= 8'h00;
ines[14] <= 8'h00;
ines[15] <= 8'h00;
ines[15] <= 8'h19;//miracle piano; controllers swapped
state <= S_COPYPLAY;
clearclk <= 4'h0;
end

View File

@@ -2110,6 +2110,7 @@ NSF nsfplayer(
.chr_ain (chr_ain),
.chr_aout_b (chr_addr_b),
.chr_read (chr_read),
.chr_dout_b (chr_dout_b), // Special port
.chr_allow_b(chr_allow_b),
.vram_a10_b (vram_a10_b),
.vram_ce_b (vram_ce_b),

BIN
rtl/font.chr Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -2082,6 +2082,7 @@ module NSF(
input [13:0] chr_ain, // chr address in
inout [21:0] chr_aout_b, // chr address out
input chr_read, // chr ram read
inout [7:0] chr_dout_b, // chr data (non standard)
inout chr_allow_b, // chr allow write
inout vram_a10_b, // Value for A10 address line
inout vram_ce_b, // True if the address should be routed to the internal 2kB VRAM.
@@ -2097,13 +2098,14 @@ assign prg_aout_b = enable ? prg_aout : 22'hZ;
assign prg_dout_b = enable ? prg_dout : 8'hZ;
assign prg_allow_b = enable ? prg_allow : 1'hZ;
assign chr_aout_b = enable ? chr_aout : 22'hZ;
assign chr_dout_b = enable ? chr_dout : 8'hZ;
assign chr_allow_b = enable ? chr_allow : 1'hZ;
assign vram_a10_b = enable ? vram_a10 : 1'hZ;
assign vram_ce_b = enable ? vram_ce : 1'hZ;
assign irq_b = enable ? 1'b0 : 1'hZ;
assign flags_out_b = enable ? flags_out : 16'hZ;
assign audio_b = enable ? {audio_in[15:0]} : 16'hZ;
assign exp_audioe = enable ? nsf_reg[3][5:0] : 6'h00;
assign exp_audioe = enable ? (nsf_reg[3][5:0]==6'd0) ? ({2'b00,midi_reg[5'd7][3],3'b000}) : nsf_reg[3][5:0] : 6'h00;
wire [21:0] prg_aout, chr_aout;
wire [7:0] prg_dout;
@@ -2111,9 +2113,12 @@ wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire [15:0] flags_out = {14'd0, prg_bus_write, 1'b0};
wire [15:0] flags_out = {14'd0, prg_bus_write, has_chr_dout};
reg prg_bus_write;
wire has_chr_dout;
wire [7:0] chr_dout;
wire [3:0] submapper = flags[24:21];
reg [7:0] nsf_reg [15:0];
reg [15:0] counter;
@@ -2124,6 +2129,21 @@ reg [7:0] multiplier_1;
reg [7:0] multiplier_2;
wire [15:0] multiply_result = multiplier_1 * multiplier_2;
reg [7:0] apu_reg [31:0];
reg [7:0] mmc5_reg [15:0];
reg [7:0] vrc6_reg [15:0];
reg [7:0] midi_reg [31:0];
reg [7:0] ssb5_reg [15:0];
reg [3:0] ssb5_add;
reg [7:0] fds_reg [15:0];
reg [7:0] n163_reg[63:0];
reg autoinc;
reg [6:0] ram_ain;
reg do_inc;
reg [7:0] vrc7_reg[31:0];
reg [4:0] vrc7_add;
/*
;----------------------------------
; NSF player for PowerPak
@@ -2145,6 +2165,7 @@ wire [15:0] multiply_result = multiplier_1 * multiplier_2;
;
;-----------------------------------
*/
integer i;
always @(posedge clk) begin
// 21.477272MHz/1MHz
// Using 21.5; Replace with actual pll?
@@ -2173,6 +2194,16 @@ always @(posedge clk) begin
nsf_reg[4'hD] <= 8'h05;
nsf_reg[4'hE] <= 8'h06;
nsf_reg[4'hF] <= 8'hFF;
for (i=0;i<32;i++)
apu_reg[i] <= i;
for (i=0;i<32;i++)
midi_reg[i] <= 8'h00;
for (i=0;i<16;i++)
vrc6_reg[i] <= 8'h00;
for (i=0;i<16;i++)
ssb5_reg[i] <= 8'h00;
for (i=0;i<16;i++)
fds_reg[i] <= 8'h00;
end else if (ce) begin
if ((prg_ain[15:4]==12'h5FF) && prg_write)
nsf_reg[prg_ain[3:0]] <= prg_din;
@@ -2182,6 +2213,35 @@ always @(posedge clk) begin
multiplier_1 <= prg_din;
if ((prg_ain==16'h5206) && prg_write)
multiplier_2 <= prg_din;
if ((prg_ain[15:5]==11'b0100_0000_000) && prg_write)
apu_reg[prg_ain[4:0]] <= prg_din;
if ((prg_ain[15:5]==11'b0100_0000_001) && prg_write)
midi_reg[prg_ain[4:0]] <= prg_din;
if ((prg_ain[15:5]==11'b0101_0000_000) && prg_ain[3]==1'b0 && prg_write)
mmc5_reg[{prg_ain[4],prg_ain[2:0]}] <= prg_din;
if ((prg_ain[15:14]==2'b10 && prg_ain[13:12] != 2'b00 && prg_ain[11:2] == 10'b0000_0000_00) && prg_write)
vrc6_reg[{prg_ain[13:12],prg_ain[1:0]}] <= prg_din;
if ((prg_ain[15: 0]==16'hC000) && prg_write)
ssb5_add <= prg_din[3:0];
if ((prg_ain[15: 0]==16'hE000) && prg_write)
ssb5_reg[ssb5_add] <= prg_din;
if ((prg_ain[15:4]==12'h408) && prg_write)
fds_reg[prg_ain[3:0]] <= prg_din;
//ram in
do_inc<= 0;
if (do_inc)
ram_ain<=ram_ain+1'd1;
if(prg_ain==16'hF800 && prg_write)
{autoinc,ram_ain}<=prg_din;
else if(prg_ain==16'h4800 & autoinc)
do_inc<=1;
if(prg_ain==16'h4800 && prg_write && ram_ain[6])
n163_reg[ram_ain[5:0]] <= prg_din;
if ((prg_ain[15: 0]==16'h9010) && prg_write)
vrc7_add <= {prg_din[5:4],prg_din[2:0]};
if ((prg_ain[15: 0]==16'h9030) && prg_write)
vrc7_reg[vrc7_add] <= prg_din;
end
end
@@ -2197,12 +2257,233 @@ always begin
endcase
end
reg [4:0] ppu_line;
always @(posedge clk) begin
if ((chr_ain[13:11] == 3'b10_0) && (chr_ain[9:6] != 4'b11_11) && chr_read) begin
ppu_line <= chr_ain[9:5];
end
end
wire pul0 = ppu_line[4:1] == 4'd2;
wire pul1 = ppu_line[4:1] == 4'd3;
wire tria = ppu_line[4:1] == 4'd4;
wire nois = ppu_line[4:1] == 4'd5;
wire samp = ppu_line[4:1] == 4'd6;
wire m5pul0 = ppu_line[4:1] == 4'd7 && !(exp_audioe[4] && n163_max) && !exp_audioe[1];
wire m5pul1 = ppu_line[4:1] == 4'd8 && !(exp_audioe[4] && n163_max) && !exp_audioe[1];
wire m5samp = ppu_line[4:1] == 4'd9 && !exp_audioe[4] && !exp_audioe[2] && !exp_audioe[1];
wire v6pul0 = ppu_line[4:1] == 4'd10 && !exp_audioe[5] && !exp_audioe[4] && !exp_audioe[1]; //e=0
wire v6pul1 = ppu_line[4:1] == 4'd11 && !exp_audioe[5] && !exp_audioe[4] && !exp_audioe[1]; //e=0
wire v6saw = ppu_line[4:1] == 4'd12 && !exp_audioe[5] && !exp_audioe[4] && !exp_audioe[1]; //e=0
wire s5pul0 = ppu_line[4:1] == 4'd10 && exp_audioe[5];
wire s5pul1 = ppu_line[4:1] == 4'd11 && exp_audioe[5];
wire s5pul2 = ppu_line[4:1] == 4'd12 && exp_audioe[5];
wire fds = ppu_line[4:1] == 4'd9 && exp_audioe[2] && !exp_audioe[4];
wire n163_0 = ppu_line[4:1] == 4'd9 && exp_audioe[4];
wire n163_1 = ppu_line[4:1] == 4'd10 && !exp_audioe[5] && exp_audioe[4];
wire n163_2 = ppu_line[4:1] == 4'd11 && !exp_audioe[5] && exp_audioe[4];
wire n163_3 = ppu_line[4:1] == 4'd12 && !exp_audioe[5] && exp_audioe[4];
wire n163_4 = ppu_line[4:1] == 4'd13 && exp_audioe[4] && n163_max;
wire n163_5 = ppu_line[4:1] == 4'd14 && exp_audioe[4] && n163_max;
wire n163_6 = ppu_line[4:1] == 4'd7 && exp_audioe[4] && n163_max;
wire n163_7 = ppu_line[4:1] == 4'd8 && exp_audioe[4] && n163_max;
wire vrc7_0 = ppu_line[4:1] == 4'd7 && !(exp_audioe[4] && n163_max) && exp_audioe[1];
wire vrc7_1 = ppu_line[4:1] == 4'd8 && !(exp_audioe[4] && n163_max) && exp_audioe[1];
wire vrc7_2 = ppu_line[4:1] == 4'd9 && !exp_audioe[4] && exp_audioe[1];
wire vrc7_3 = ppu_line[4:1] == 4'd10 && !exp_audioe[4] && exp_audioe[1];
wire vrc7_4 = ppu_line[4:1] == 4'd11 && !exp_audioe[4] && exp_audioe[1];
wire vrc7_5 = ppu_line[4:1] == 4'd12 && !exp_audioe[4] && exp_audioe[1];
wire apu_type = pul0 | pul1 | tria | nois | samp;
wire mmc5_type = m5samp | m5pul0 | m5pul1;
wire vrc6_type = v6pul0 | v6pul1 | v6saw;
wire ssb5_type = s5pul0 | s5pul1 | s5pul2;
wire fds_type = fds;
wire n163_type = n163_0 | n163_1 | n163_2 | n163_3 | n163_4 | n163_5 | n163_6 | n163_7;
wire vrc7_type = vrc7_0 | vrc7_1 | vrc7_2 | vrc7_3 | vrc7_4 | vrc7_5;
//wire [3:0] voi_idx = {!(ppu_line[4]^ppu_line[3]),!ppu_line[3],ppu_line[2:1]};
wire [2:0] n163_idx = ~(n163_7 ? 3'd7 : n163_6 ? 3'd6 : n163_5 ? 3'd5 : n163_4 ? 3'd4 : n163_3 ? 3'd3 : n163_2 ? 3'd2 : n163_1 ? 3'd1 : 3'd0);
wire n163_max = n163_reg[6'h3F][6];
wire [2:0] vrc7_idx = vrc7_5 ? 3'd5 : vrc7_4 ? 3'd4 : vrc7_3 ? 3'd3 : vrc7_2 ? 3'd2 : vrc7_1 ? 3'd1 : 3'd0;
wire apu_off = !midi_reg[5'h0B][{1'b0, samp, tria | nois, pul1 | nois}]; // 'h403B = copy of read value of 'h4015 - voice on bits [4:0]
wire vrc6_off = !vrc6_reg[{!v6pul0,!v6pul1,2'b10}][7];
wire fds_off = fds_reg[9][7];
wire [1:0] ssb5_idx = {s5pul2,s5pul1};
wire ssb5_off = ssb5_reg[7][ssb5_idx] & ssb5_reg[7][ssb5_idx+3'd3];
wire mmc5_off = m5samp ? (mmc5_reg[4'h8]==0 && mmc5_reg[4'h9]==0) : !mmc5_reg[4'hD][!m5pul0];
wire n163_off = n163_reg[6'h3F][6:4] < (~n163_idx);
wire vrc7_off = 1'b0;
wire voi_off = ssb5_type ? ssb5_off : n163_type ? n163_off : vrc6_type ? vrc6_off : vrc7_type ? vrc7_off : mmc5_type ? mmc5_off : fds_type ? fds_off : apu_off;
wire [3:0] puls_vol = apu_reg[{2'b00,pul1,2'b00}][3:0]; //h'4000 or 'h4004 [3:0]
wire [3:0] tria_vol = (|apu_reg[5'h08][6:0]) ? 4'hF : 4'h0; // h'4008 [6:0]
wire [3:0] nois_vol = apu_reg[12][3:0]; // h'400C [3:0]
wire [3:0] samp_vol = 4'hF;
wire [3:0] apu_vol = samp ? samp_vol : nois ? nois_vol : tria ? tria_vol : puls_vol;
wire [3:0] vrc6_pul_vol = vrc6_reg[{!v6pul0,!v6pul1,2'b00}][3:0];
wire [3:0] vrc6_saw_vol = {vrc6_reg[4'b1100][5:3],|vrc6_reg[4'b1100][2:0]};
wire [3:0] vrc6_vol = v6saw ? vrc6_saw_vol : vrc6_pul_vol;
wire [3:0] fds_vol = {fds_reg[0][5:3],|fds_reg[0][2:0]};
wire [3:0] ssb5_vol = ssb5_reg[{2'b10,ssb5_idx}][3:0];
wire [3:0] mmc5_samp_vol = 4'hF;
wire [3:0] mmc5_pul_vol = mmc5_reg[{1'b0,m5pul1,2'b00}][3:0];
wire [3:0] mmc5_vol = m5samp ? mmc5_samp_vol : mmc5_pul_vol;
wire [3:0] n163_vol = n163_reg[{n163_idx,3'b111}][3:0];
wire [3:0] vrc7_vol = vrc7_reg[{2'b11,vrc7_idx}][3:0];
wire [3:0] voi_vol = ssb5_type ? ssb5_vol : n163_type ? n163_vol : vrc6_type ? vrc6_vol : vrc7_type ? vrc7_vol : mmc5_type ? mmc5_vol : fds_type ? fds_vol : apu_vol;
wire [4:0] n_freq = {1'b0,~apu_reg[5'h0E][3:0]}; // 'h400E = noise period; invert for period
wire [4:0] s_freq = {1'b0,apu_reg[5'h10][3:0]}; // 'h4010/5010 = sample freq
wire [4:0] ms_freq = mmc5_reg[4'h8][0] ? 5'h1 : 5'h0; // 'h4010/5010 = sample freq
wire [4:0] freq = nois ? n_freq : samp ? s_freq : ms_freq;
wire use_freq = nois | samp | m5samp;
wire [4:0] voi_tab_idx;
always begin
casez({n163_max,exp_audioe,ppu_line}) //6,5
12'b???????_000??: voi_tab_idx = 5'd0; //Info;
12'b???????_001??: voi_tab_idx = {4'd0,ppu_line[1]}; //Apu;
12'b???????_010??: voi_tab_idx = {4'd1,ppu_line[1]}; //Apu;
12'b???????_0110?: voi_tab_idx = 5'd0; //Samp;
12'b??0??0?_0111?: voi_tab_idx = 5'd3; //MMC5;
12'b??0??0?_1000?: voi_tab_idx = 5'd4; //MMC5;
12'b0?1????_0111?: voi_tab_idx = 5'd3; //MMC5;
12'b0?1????_1000?: voi_tab_idx = 5'd4; //MMC5;
12'b??0??1?_0111?: voi_tab_idx = 5'd20; //VRC7;
12'b??0??1?_1000?: voi_tab_idx = 5'd21; //VRC7;
12'b1?1????_0111?: voi_tab_idx = 5'd18; //N163;
12'b1?1????_1000?: voi_tab_idx = 5'd19; //N163;
12'b??0?1??_1001?: voi_tab_idx = 5'd12; //FDS;
12'b??0?00?_1001?: voi_tab_idx = 5'd0; //MMC5;
12'b??0?01?_1001?: voi_tab_idx = 5'd22; //VRC7;
12'b??1????_1001?: voi_tab_idx = 5'd8; //N163;
12'b?00??0?_101??: voi_tab_idx = {!ppu_line[1]?5'd5:5'd6}; //VRC6;
12'b?00??0?_1100?: voi_tab_idx = 5'd7; //VRC6;
12'b?1?????_101??: voi_tab_idx = {!ppu_line[1]?5'd13:5'd14}; //SSB5;
12'b?1?????_1100?: voi_tab_idx = 5'd15; //SSB5;
12'b?00??1?_1010?: voi_tab_idx = 5'd23; //VRC7;
12'b?00??1?_1011?: voi_tab_idx = 5'd24; //VRC7;
12'b?00??1?_1100?: voi_tab_idx = 5'd25; //VRC7;
12'b?01????_1010?: voi_tab_idx = 5'd9; //N163;
12'b?01????_1011?: voi_tab_idx = 5'd10; //N163;
12'b?01????_1100?: voi_tab_idx = 5'd11; //N163;
12'b1?1????_1101?: voi_tab_idx = 5'd16; //N163;
12'b1?1????_1110?: voi_tab_idx = 5'd17; //N163;
12'b0??????_1101?: voi_tab_idx = 5'd0; //Reg;
12'b0??????_1110?: voi_tab_idx = 5'd0; //Reg;
12'b1?0????_1101?: voi_tab_idx = 5'd0; //Reg;
12'b1?0????_1110?: voi_tab_idx = 5'd0; //Reg;
12'b???????_1111?: voi_tab_idx = 5'd0; //VBlank;
endcase
end
reg [4:0] find_count;
reg [4:0] find_idx;
wire [11:0] period [25:0];
assign period[0] = {1'b0,apu_reg[3][2:0],apu_reg[2][7:0]};
assign period[1] = {1'b0,apu_reg[7][2:0],apu_reg[6][7:0]};
assign period[2] = {apu_reg[11][2:0],apu_reg[10][7:0],1'b0};
assign period[3] = (exp_audioe[3] | 1) ? {1'b0,mmc5_reg[3][2:0],mmc5_reg[2][7:0]} : 12'hFFF;
assign period[4] = (exp_audioe[3] | 1) ? {1'b0,mmc5_reg[7][2:0],mmc5_reg[6][7:0]} : 12'hFFF;
assign period[5] = exp_audioe[0] ? {vrc6_reg[6][3:0],vrc6_reg[5][7:0]} : 12'hFFF;
assign period[6] = exp_audioe[0] ? {vrc6_reg[10][3:0],vrc6_reg[9][7:0]}: 12'hFFF;
assign period[7] = exp_audioe[0] ? {vrc6_reg[14][3:0],vrc6_reg[13][7:0]} : 12'hFFF;
assign period[8] = exp_audioe[4] ? {n163_reg[6'b111100][1:0],n163_reg[6'b111010][7:0],n163_reg[6'b111000][7:6]} : 12'hFFF;
assign period[9] = exp_audioe[4] ? {n163_reg[6'b110100][1:0],n163_reg[6'b110010][7:0],n163_reg[6'b110000][7:6]} : 12'hFFF;
assign period[10] = exp_audioe[4] ? {n163_reg[6'b101100][1:0],n163_reg[6'b101010][7:0],n163_reg[6'b101000][7:6]} : 12'hFFF;
assign period[11] = exp_audioe[4] ? {n163_reg[6'b100100][1:0],n163_reg[6'b100010][7:0],n163_reg[6'b100000][7:6]} : 12'hFFF;
assign period[12] = exp_audioe[2] ? {fds_reg[3][3:0],fds_reg[2][7:0]} : 12'hFFF;
assign period[13] = exp_audioe[5] ? {ssb5_reg[1][2:0],ssb5_reg[0][7:0],1'b1} : 12'hFFF;
assign period[14] = exp_audioe[5] ? {ssb5_reg[3][2:0],ssb5_reg[2][7:0],1'b1} : 12'hFFF;
assign period[15] = exp_audioe[5] ? {ssb5_reg[5][2:0],ssb5_reg[4][7:0],1'b1} : 12'hFFF;
assign period[16] = exp_audioe[4] ? {n163_reg[6'b011100][1:0],n163_reg[6'b011010][7:0],n163_reg[6'b011000][7:6]} : 12'hFFF;
assign period[17] = exp_audioe[4] ? {n163_reg[6'b010100][1:0],n163_reg[6'b010010][7:0],n163_reg[6'b010000][7:6]} : 12'hFFF;
assign period[18] = exp_audioe[4] ? {n163_reg[6'b001100][1:0],n163_reg[6'b001010][7:0],n163_reg[6'b001000][7:6]} : 12'hFFF;
assign period[19] = exp_audioe[4] ? {n163_reg[6'b000100][1:0],n163_reg[6'b000010][7:0],n163_reg[6'b000000][7:6]} : 12'hFFF;
assign period[20] = exp_audioe[1] ? {vrc7_reg[5'b10000][0],vrc7_reg[5'b01000][7:0],3'b111} : 12'hFFF;
assign period[21] = exp_audioe[1] ? {vrc7_reg[5'b10001][0],vrc7_reg[5'b01001][7:0],3'b111} : 12'hFFF;
assign period[22] = exp_audioe[1] ? {vrc7_reg[5'b10010][0],vrc7_reg[5'b01010][7:0],3'b111} : 12'hFFF;
assign period[23] = exp_audioe[1] ? {vrc7_reg[5'b10011][0],vrc7_reg[5'b01011][7:0],3'b111} : 12'hFFF;
assign period[24] = exp_audioe[1] ? {vrc7_reg[5'b10100][0],vrc7_reg[5'b01100][7:0],3'b111} : 12'hFFF;
assign period[25] = exp_audioe[1] ? {vrc7_reg[5'b10101][0],vrc7_reg[5'b01101][7:0],3'b111} : 12'hFFF;
wire [11:0] period78 = period[find_idx] - {3'b000,period[find_idx][11:3]};
wire [11:0] period1615 = {1'b0,period[find_idx][11:1]} + {5'b0,period[find_idx][11:5]} - {9'b0,period[find_idx][11:9]}; // add ~1/16th (should be half step?): down an octave to avoid add overflow
wire use_n163 = (find_idx[4:2]==3'b010) || (find_idx[4:2]==3'b100);
wire use_vrc7 = (find_idx[4:2]==3'b101) || (find_idx[4:1]==4'b1100);
wire use_v6saw = (find_idx==5'd7);
wire use_fds = (find_idx==5'd12);
wire use78 = use_v6saw || use_vrc7;
wire use1615 = use_n163;
wire [11:0] period_use = use_n163? period1615 : use78 ? period78 : period[find_idx];
wire [17:0] find_bits;
logic [10:0] find_note_lut[80]; //not used; done in asm
assign find_note_lut = '{
11'h7f1,11'h77f,11'h713,11'h6ad,11'h64d,11'h5f3,11'h59d,11'h54c,
11'h500,11'h4b8,11'h474,11'h434, 11'h3f8,11'h3bf,11'h389,11'h356,
11'h326,11'h2f9,11'h2ce,11'h2a6,11'h280,11'h25c,11'h23a,11'h21a,
11'h1fb,11'h1df,11'h1c4,11'h1ab, 11'h193,11'h17c,11'h167,11'h152,
11'h13f,11'h12d,11'h11c,11'h10c,11'h0fd,11'h0ef,11'h0e1,11'h0d5,
11'h0c9,11'h0bd,11'h0b3,11'h0a9, 11'h09f,11'h096,11'h08e,11'h086,
11'h07e,11'h077,11'h070,11'h06a,11'h064,11'h05e,11'h059,11'h054,
11'h04f,11'h04b,11'h046,11'h042, 11'h03f,11'h03b,11'h038,11'h034,
11'h031,11'h02f,11'h02c,11'h029,11'h027,11'h025,11'h023,11'h021,
11'h01f,11'h01d,11'h01b,11'h01a, 11'h018,11'h017,11'h015,11'h014
};
logic [9:0] find_steps_lut[32];
assign find_steps_lut = '{
10'h3D4, 10'h39B, 10'h363, 10'h32E,
10'h2F9, 10'h2C6, 10'h295, 10'h265,
10'h236, 10'h209, 10'h1DD, 10'h1B2,
10'h189, 10'h160, 10'h139, 10'h113,
10'h0EE, 10'h0CA, 10'h0A7, 10'h085,
10'h064, 10'h044, 10'h025, 10'h007,
10'h000, 10'h000, 10'h000, 10'h000,
10'h000, 10'h000, 10'h000, 10'h000
};
always begin
casez(period_use[11:2])
10'b1???_????_??: find_bits = {4'd8,4'd0,period_use[10:1]};
10'b01??_????_??: find_bits = {4'd7,4'd1,period_use[9:0]};
10'b001?_????_??: find_bits = {4'd6,4'd2,period_use[8:0],1'b1};
10'b0001_????_??: find_bits = {4'd5,4'd3,period_use[7:0],2'b10};
10'b0000_1???_??: find_bits = {4'd4,4'd4,period_use[6:0],3'b100};
10'b0000_01??_??: find_bits = {4'd3,4'd5,period_use[5:0],4'b1000};
10'b0000_001?_??: find_bits = {4'd2,4'd6,period_use[4:0],5'b10000};
10'b0000_0001_??: find_bits = {4'd1,4'd7,period_use[3:0],6'b100000};
10'b0000_0000_1?: find_bits = {4'd0,4'd8,period_use[2:0],7'b1000000};
10'b0000_0000_0?: find_bits = {4'hF,4'd9,10'b1111111111}; // always find_count = 0
endcase
end
reg [4:0] spot [25:0];
reg [3:0] oct_no [25:0];
always @(posedge clk) begin
find_count <= find_count + 1'b1;
if ((find_bits[9:0]>find_steps_lut[find_count[4:0]]) || (find_count[4:3] == 2'b11)) begin
spot[find_idx] <= (use_fds) || (use_vrc7) || (use_n163) ? (find_bits[17:14]==4'hF) ? 5'd0 : 5'd24 - find_count : find_count;
oct_no[find_idx] <= ((use_fds) || (use_vrc7) || (use_n163) ? (find_bits[17:14]==4'hF) ? 4'd0: find_bits[17:14] : find_bits[13:10]) - ((use_vrc7) ? 5'd8 - vrc7_reg[{2'b10,!find_idx[2],find_idx[1:0]}][3:1] : 5'd0);
find_count <= 0;
find_idx <= (find_idx == 5'd25) ? 5'd0 : find_idx + 1'b1;
end
end
always begin
prg_bus_write = 1'b1;
if (prg_ain == 16'h5205) begin
prg_dout = multiply_result[7:0];
end else if (prg_ain == 16'h5206) begin
prg_dout = multiply_result[15:8];
end else if (prg_ain == 16'h4029) begin
prg_dout = find_note_lut[{midi_reg[5'h09][2:0],midi_reg[5'h08]}][1:0];
end else if (prg_ain == 16'h4028) begin
prg_dout = {5'h0,find_note_lut[{midi_reg[5'h09][2:0],midi_reg[5'h08]}][7:0]};
end else if (prg_ain[15:5]==11'b0100_0000_001) begin
prg_dout = midi_reg[prg_ain[4:0]];
end else if (prg_ain[15:8] == 8'h40) begin
prg_dout = fds_din;
end else if (prg_ain == 16'h5FF2) begin
@@ -2213,14 +2494,53 @@ always begin
end
end
assign prg_aout = ((submapper == 4'hF) && ({prg_ain[15:1],1'b0} == 16'hFFFC)) ? {10'h0, prg_ain[11:0]} : {prg_bank, prg_ain[11:0]};
assign prg_allow = (((prg_ain[15] || ((prg_ain>=16'h4080) && (prg_ain<16'h4FFF))) && !prg_write) || (prg_ain[15:13]==3'b011)
|| (prg_ain[15:10]==6'b010111 && prg_ain[9:4]!=6'b111111) || ((prg_ain>=16'h8000) && (prg_ain<16'hDFFF) && exp_audioe[2]));
assign chr_allow = flags[15]; // CHR RAM always...
assign chr_aout = {9'b10_0000_000, chr_ain[12:0]};
assign vram_ce = chr_ain[13];
wire patt_bars = chr_ain[13:11] == 3'b00_1; // 'h0800-'hfff bar pattern for pulse1,pulse2,triangle,noise,sample volume
assign chr_aout = {9'b10_0000_000, chr_read && patt_bars ? {5'b0_0001,chr_ain[7:0]} : chr_ain[12:0]}; //replace 'h0800-'h0fff with 'h0100-'h01ff = bars location
assign vram_ce = chr_ain[13] & !has_chr_dout;
assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11];
wire nt0 = chr_ain[13:10] == 4'b10_00; // 'h2000-23FF
wire [4:0] line_no = chr_ain[9:5]; // line # d0 - 31
wire reg_line = ((line_no==5'b11_010) || (line_no==5'b11_011) || (line_no==5'b11_100) || (line_no==5'b11_101)) && (!n163_max || !exp_audioe[4]); // d4,5,6
wire alt4 = chr_ain[2] == 1'b1; // every other (odd) four characters
wire midi_line = (line_no[4:1] == 4'b01_01) || (line_no[4:1] == 4'b01_10); // lines d10-13
wire last16 = chr_ain[4] == 1'b1; // second half d16-31
wire last1 = chr_ain[4:0] == 5'd31; // char d31
wire last2 = chr_ain[4:0] == 5'd30; // char d30
wire last3 = chr_ain[4:0] == 5'd29; // char d29
wire print_reg = nt0 && alt4 && reg_line;
wire print_midi = nt0 && midi_line && last16;
wire print_spot = nt0 && !print_midi && (apu_type || mmc5_type || fds_type || vrc6_type || vrc7_type || n163_type || ssb5_type);
wire print_oct = nt0 && !midi_line && !use_freq && print_spot && last1;
wire print_let = nt0 && !midi_line && !use_freq && print_spot && last2;
wire print_shp = nt0 && !midi_line && !use_freq && print_spot && last3 && sharp;
wire [4:0] reg_ind = {!chr_ain[6],chr_ain[5:3],!chr_ain[1]}; // inverted1 means MSB first. lines up with d4,5,6
wire [4:0] midi_ind = {!chr_ain[6],chr_ain[5],chr_ain[3:1]}; // last16 means ignore 4
wire [7:0] chr_num = print_reg ? exp_audioe[3] ? mmc5_reg[reg_ind] : exp_audioe[4] ? n163_reg[{~reg_ind[4:2],~reg_ind[1],reg_ind[0],reg_ind[1:0]==2'b01}] : exp_audioe[2] ? fds_reg[reg_ind] : exp_audioe[1] ? vrc7_reg[reg_ind] : exp_audioe[0] ? vrc6_reg[{reg_ind[3:2],reg_ind[1]^reg_ind[0],!reg_ind[0]}] : exp_audioe[5] ? ssb5_reg[reg_ind] : apu_reg[reg_ind] : print_midi ? (n163_max && exp_audioe[4] && (midi_reg[5'd7]==8'd0)) ? n163_reg[{~midi_ind[4:2],~midi_ind[1],midi_ind[0],midi_ind[1:0]==2'b01}] : midi_reg[midi_ind] : oct_no[voi_tab_idx] + (inc_oct ? 1'b1 : 1'b0);
wire [4:0] oct_idx = {1'b0,oct_no[voi_tab_idx]} + {oct_no[voi_tab_idx],1'b0}; // 3 * oct_no[voi_idx[1:0]]
wire [4:0] spot_chr = use_freq ? freq : oct_idx + {3'b000,spot[voi_tab_idx][4:3]}; // noise+sample=d0-15, tri=d0-24 (25-27->24), puls=d3-27 (28-30->27)
wire [3:0] spot_vol = voi_off ? 4'h0 : voi_vol;
wire has_spot_chr = print_spot && (chr_ain[4:0] == spot_chr);
wire skip_vol = !print_midi && !print_oct && patt_bars && ((spot_vol[2:0]<=~chr_ain[2:0] && !(spot_vol[3] && !chr_ain[3])) || (!spot_vol[3] && chr_ain[3])); // skip middle volume bar if vol[2:0] is less than pixelrow[2:0]; skip outer volume bars if vol[3] is not set
wire spot_vol_row = !chr_ain[5]; // d18,20,22,24,26
wire [4:0] spot_val = {spot_vol_row ? {4'b1000,use_freq ? 1'b1 : 1'b0}:5'h0}; // d18->10010, 20->10100, 22->10110, 24->11001, 25->11011: [4]=1 means bar pattern, [3:1]=voi_idx will be in chr_ain[10:8]->ignored, [0]=chr_ain[7] 0x0 = 0x800, 0x1 = 0x0880 pattern table address; will be adjusted to 0x100 and 0x180
wire [4:0] note_val = spot[voi_tab_idx][4:0];
wire inc_note = (note_val == 5'd23) || (note_val == 5'd24);
wire inc_oct = (note_val > 5'd5);
wire [4:0] note_no = inc_note ? 5'd4 : (note_val + (note_val > 5'd14 ? 5'd9 : note_val > 5'd5 ? 5'd7 : 5'd5));
wire [7:0] letter = {5'b0100_0,note_no[4:2]}; // A=$41,G=$47
wire sharp = note_no[1]; // #=$23
wire cpu_ppu_write = prg_write && prg_ain[15:12]==4'h2; // 'h2000-2FFF
assign has_chr_dout = !(cpu_ppu_write) && (print_reg || print_spot || skip_vol || print_midi || print_oct || print_let || print_shp);
assign chr_dout = skip_vol ? {8'h00} : print_let ? letter : print_shp ? 8'h23 : (print_spot && !print_oct) ? has_spot_chr ? {spot_val,use_freq?3'b000:spot[voi_tab_idx][2:0]} : {8'h20} : {4'h0, chr_ain[0] ? chr_num[3:0] : chr_num[7:4]};
endmodule
// 111 - Cheapocabra/GTROM

View File

@@ -600,7 +600,7 @@ wire [15:0] prg_addr = addr;
wire [7:0] prg_din = dbus & (prg_conflict ? cpumem_din : 8'hFF);
wire prg_read = mr_int && cart_pre && !apu_cs && !ppu_cs;
wire prg_write = mw_int && cart_pre && !apu_cs && !ppu_cs;
wire prg_write = mw_int && cart_pre;
wire prg_allow, prg_bus_write, prg_conflict, vram_a10, vram_ce, chr_allow;
wire [24:0] prg_linaddr;

893
rtl/nsf.asm Normal file
View File

@@ -0,0 +1,893 @@
;----------------------------------
; NSF player for PowerPak
;
; Player rom is at $4100-4FFF (NSF header at $4100)
;
; PowerPak registers:
;
; 5FF0: timer latch LSB
; 5FF1: timer latch MSB
; 5FF2: timer status (Read: bit7=timer wrapped, Write: clear status)
; 5FF3: Expansion audio control (copy header[0x7B] here)
; 5FF6-5FFF: banking registers (as described in NSF spec)
;
; Timer details:
; PowerPak NSF mapper has a 16bit 1MHz counter that counts down from [5FF1:5FF0] to 0.
; After the counter reaches 0, it's automatically reloaded and timer status bit is set.
; Clear the status bit by writing to $5FF2.
;
;-----------------------------------
A = $80
B = $40
SELECT = $20
START = $10
UP = $08
DOWN = $04
LEFT = $02
RIGHT = $01
HDR_BASE = $4100
HDR_SONGS = HDR_BASE+$06
HDR_FIRST = HDR_BASE+$07
HDR_LOAD = HDR_BASE+$08
HDR_INIT = HDR_BASE+$0a
HDR_PLAY = HDR_BASE+$0c
HDR_TITLE = HDR_BASE+$0e
HDR_ARTIST = HDR_BASE+$2e
HDR_COPYRIGHT = HDR_BASE+$4e
HDR_NTSC_LO = HDR_BASE+$6E
HDR_NTSC_HI = HDR_BASE+$6F
HDR_BANK = HDR_BASE+$70
HDR_PAL_LO = HDR_BASE+$78
HDR_PAL_HI = HDR_BASE+$79
HDR_EXP_HW = HDR_BASE+$7b
STACK_TOP = $1f4
CURRENT = $1f4 ;song#
PLAYING = $1f5 ;nonzero=song is playing
JOYPAD = $1f6 ;button state
JOYD = $1f7 ;button 0->1
JOYTMP = $1f8
FRAME = $1f9
PAL = $1fa ;1=PAL detected
DIVTMP = $1fb
STR = $1fc ;4 bytes used for printing current song #
; header ------
db $4E,$45,$53,$1A
db $01 ;PRG size/16k
db $00 ;CHR size/8k
db $00,$00 ;flags,mapper
db $00,$00,$00,$00,$00,$00,$00,$00
.org $4000
.pad HDR_BASE+$80
reset ;-------
sei
ldx #<(STACK_TOP-1)
txs
stx PLAYING
lda #$00
sta $2000 ;nmi off
sta $2001 ;screen off
jsr pal_detect
ldx #8 ;load CHR: 128 tiles = $800 bytes
ldy #$00
sty $2006
sty $2006
lda #<chr
sta 0
lda #>chr
sta 1
- lda (0),y
sta $2007
iny
bne -
inc 1
dex
bpl -
lda #$3f ;set palette
sta $2006
lda #$00
sta $2006
ldx #8
- lda #$0f
sta $2007
lda #$30
sta $2007
sta $2007
sta $2007
dex
bne -
lda #$20 ;clear screens
sta $2006
lda #$00
sta $2006
tax
ldy #4 ;$10
lda #$20 ;" "
- sta $2007
inx
bne -
dey
bne -
lda #$20
sta $2006
lda #$70
sta $2006
lda #$2f ;"/"
sta $2007
lda HDR_SONGS
jsr deci
- lda STR,y ;print #songs
sta $2007
dey
bpl -
lda #$20 ;print song infos
sta $2006
lda #$42
sta $2006
ldy #0
- lda HDR_TITLE,y
beq +
sta $2007
iny
cpy #30
bne -
+
lda #$20
sta $2006
lda #$22
sta $2006
ldy #0
- lda HDR_ARTIST,y
beq +
sta $2007
iny
cpy #30
bne -
+
lda #$20
sta $2006
lda #$02
sta $2006
ldy #0
- lda HDR_COPYRIGHT,y
beq +
sta $2007
iny
cpy #30
bne -
+
lda HDR_EXP_HW ;enable extra audio HW (also PRG write enable for FDS)
sta $5ff3
ora #$40 ;- kevtris - swap VRC6 registers which is how all NSFs use VRC6
ldx #$55
stx $3ff2
ldx #$aa
stx $3fea ;- these two writes enable the chip select mode in the HDNES hardware
sta $3ffa ;- automatic enable setting for HDNES
jsr timer_init
lda HDR_FIRST ;init first song
sta CURRENT
jsr init
lda #%00001010 ;screen on, sprites off
sta $2001
busywait
lda $5ff2
bpl busywait
sta $5ff2
inc FRAME
jsr joyread
jsr play
jmp copyapustatus ;busywait
pal_detect ;-----------------
lda $2002
- lda $2002
bpl -
ldy #24 ;eat about 30000 cycles (1 NTSC frame)
ldx #100
- dex
bne -
dey
bne -
lda $2002 ;VBL flag is set if NTSC
bmi +
inx
+ stx PAL
rts
init ;------------------ ;begin CURRENT song
jsr stopsound
jsr bank_init
lda HDR_EXP_HW ;clear 6000-7fff unless FDS is enabled
and #4
bne no_wram_clear
lda #$60
sta 1
lda #0
sta 0
tay
- sta (0),y
iny
bne -
inc 1
bpl -
no_wram_clear
lda #0 ;clear 0000-07ff
tax
- sta $00,x
sta $200,x ;(not 100-1ff)
sta $300,x
sta $400,x
sta $500,x
sta $600,x
sta $700,x
inx
bne -
lda CURRENT ;print song#
jsr deci
lda #$20 ;<---
- iny
sta STR,y
cpy #3
bne -
lda $2002
- lda $2002
bpl -
lda #$20
sta $2006
lda #$6d
sta $2006
lda STR+2
sta $2007
lda STR+1
sta $2007
lda STR
sta $2007
lda #$00 ;reset scroll
sta $2006
sta $2006
lda PLAYING
bne +
rts
+
ldx CURRENT ;call INIT w/ A=song#, X=pal
dex
txa
ldx PAL
jmp (HDR_INIT)
bank_init ;---------------
lda #0
ldx #7
- ora HDR_BANK,x
dex
bpl -
tax
bne banked_nsf
not_banked:
lda HDR_LOAD+1
lsr
lsr
lsr
lsr
tax
ldy #0
- tya
sta $5ff0,x
iny
inx
cpx #$10
bne -
dey
sty $5ff7
dey
sty $5ff6
rts
banked_nsf:
ldx #7
- lda HDR_BANK,x
sta $5ff8,x
dex
bpl -
lda HDR_BANK+6 ;FDS also has banks @ 6000-7FFF
sta $5ff6
lda HDR_BANK+7
sta $5ff7
rts
timer_init ;-------------
lda HDR_NTSC_LO
ldy HDR_NTSC_HI
ldx PAL
beq +
lda HDR_PAL_LO
ldy HDR_PAL_HI
+
cpy #0
beq fixit
+ cmp #$41
bne time_ok
cpy #$1a
bne time_ok
fixit
lda time_lo,x
ldy time_hi,x
time_ok
sta $5ff0
sty $5ff1
rts
time_hi db $41, $4e
time_lo db $1a, $20
stopsound ;---------------
lda #$00 ;reset sound regs
sta $4015
sta $4008
lda #$10
sta $4000
sta $4004
sta $400c
lda #$0f
sta $4015
lda #$40
sta $4017
lda #$c0 ;FDS reset
sta $4083
sta $4080
sta $4084
sta $4087
sta $4089
lda #$e8
sta $408a
;-----------------------kevtris start
lda HDR_EXP_HW
and #$01
beq szz0
lda #$00 ;shut up VRC6
sta $9000
sta $a000
sta $b000
szz0
lda HDR_EXP_HW
and #$02
beq szz1
lda #$00 ;shut up VRC7
ldx #$35
sswt ldy #$10
stx $9010
nop
nop
nop
nop
sta $9030
sswx dey
bne sswx
dex
bne sswt
szz1
lda HDR_EXP_HW
and #$08
beq ssz2
sta $5015 ;shut up MMC5
ssz2
lda HDR_EXP_HW
and #$10
beq ssz3
ldx #$80 ;shut up N163
stx $f800 ;start from address 0, auto increment
sswq sta $4800
dex
bpl sswq ;clear all RAM
ssz3
lda HDR_EXP_HW
and #$20
beq ssz4
ldx #$0f ;shut up 5B
sswz stx $c000
sta $e000
dex
bpl sswz
ssz4
;-------------------kevtris end
rts
play ;--------------------
lda PLAYING
beq +
lda #DOWN
bit JOYPAD
beq ++
lda FRAME
lsr
bcc +
++ lda #UP
bit JOYPAD
beq playhere
jsr playhere
jsr playhere
playhere
jmp (HDR_PLAY)
+ rts
joyread ;----------------
lda JOYPAD
pha
jsr joyread2
retry lda JOYPAD
sta JOYTMP
jsr joyread2
lda JOYPAD
eor JOYTMP
bne retry
pla
eor JOYPAD
and JOYPAD
sta JOYD
asl ;A
bcc +
_A ldx CURRENT
cpx HDR_SONGS
bcc ++
ldx #$00
++ inx
stx CURRENT
jmp init
+
asl ;B
bcc +
_B dec CURRENT
bne ++
lda HDR_SONGS
sta CURRENT
++ jmp init
+
asl ;SEL
bcc +
;lda #$00
;sta PLAYING
;jmp stopsound
jmp powerpak_bios_reset
+
asl ;START
bcc +
lda #$01
sta PLAYING
jmp init
+
asl ;UP
asl ;DOWN
asl ;LEFT
bcs _B
asl ;RIGHT
bcs _A
rts
joyread2 ;----------------
jmp joyreadpiano
; nop
; nop
; ldx #1
; stx $4016
; dex
; stx $4016
; ldx #$08
;- clc
; lda $4017 ;kevtris - added to read controller 2 so the HDNES menu is still accessable
; lda $4016
; ora #$fc
; adc #3
; rol JOYPAD
; dex
; bne -
; rts
div10 ;------------------
ldx #0 ;in: A=#
stx DIVTMP ;out: X=#/10, A=remainder
cmp #%10100000
bcc +
sbc #%10100000
+ rol DIVTMP
cmp #%01010000
bcc +
sbc #%01010000
+ rol DIVTMP
cmp #%00101000
bcc +
sbc #%00101000
+ rol DIVTMP
cmp #%00010100
bcc +
sbc #%00010100
+ rol DIVTMP
cmp #%00001010
bcc +
sbc #%00001010
+ rol DIVTMP
ldx DIVTMP
rts
deci ;------------------- ;in: A=#
ldy #$ff ;out: A,X=? Y=strlen-1
- jsr div10
iny
sta STR,y
txa
bne -
rts
powerpak_bios_reset ;---------
lda #$00 ;screen off
sta $2000
sta $2001
jsr stopsound
ldx #(resetcode_end - resetcode - 1)
- lda resetcode,x
sta $0,x
dex
bpl -
jmp 0
resetcode
lda #1
sta $4207
jmp ($fffc)
resetcode_end
;------------------------
chr .bin "font.chr" ;ascii chr set
notesLow
db $F1,$7F,$13,$AD,$4D,$F3,$9D,$4C,$00,$B8,$74,$34
db $F8,$BF,$89,$56,$26,$F9,$CE,$A6,$80,$5C,$3A,$1A
db $FB,$DF,$C4,$AB,$93,$7C,$67,$52,$3F,$2D,$1C,$0C
db $FD,$EF,$E1,$D5,$C9,$BD,$B3,$A9,$9F,$96,$8E,$86
db $7E,$77,$70,$6A,$64,$5E,$59,$54,$4F,$4B,$46,$42
db $3F,$3B,$38,$34,$31,$2F,$2C,$29,$27,$25,$23,$21
db $1F,$1D,$1B,$1A,$18,$17,$15,$14,$00,$00,$00,$00
notesHigh
db $07,$07,$07,$06,$06,$05,$05,$05,$05,$04,$04,$04
db $03,$03,$03,$03,$03,$02,$02,$02,$02,$02,$02,$02
db $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01
; can cut off here and do a compare (>= use $00) instead to save space
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
MIDI_BUFF = $4030 ; 4030-403f
MIDI_READ = $402f
MIDI_START = $402e
MIDI_NEXT = $402d
MIDI_RECENT = $402c
APU_STATUSC = $402b ; Copy of last write to $4015
MMC5_STATUSC = $402a ; Copy of last write to $5015
LOOKUP_HI_R = $4029 ; Read - Based on 402a - change?
LOOKUP_LOW_R = $4028 ; Read - Based on 402a - change?
MIDI_PATCH = $4027
MIDI_NOTE2 = $4026 ; keep in order 2,1 separate by 4
MIDI_NOTE3 = $4025
AVAILABLE = $4024
FDS_ENABLE = $4023 ; Don't use this
MIDI_NOTE1 = $4022
MIDI_TMP2 = $4021
MIDI_TMP = $4020
joyreadpiano
ldx #1
stx $4016
dex
nop
nop
nop
stx $4016
stx MIDI_READ
stx MIDI_READ
lda $4016
ror a
bcs readmidi
jmp joy2read
readmidi
;nop ; /needed on real hw?
;nop ; /needed on real hw?
ldx #8
readmidibit
lda $4016
ror a
rol MIDI_READ
dex
bne readmidibit
lda MIDI_READ
eor #$ff
sta MIDI_READ
ldx MIDI_NEXT
sta MIDI_BUFF,x
inx
txa ; these txa, and #0F, tax could be replaced with simple stx MIDI_NEXT (ie inc MIDI_NEXT) if buffer was 256 bytes
and #$0F
tax
sta MIDI_NEXT
ldx MIDI_START
lda MIDI_BUFF,x
cmp #$ff
bne checkF
jmp doReset
checkF
and #$f0
cmp #$f0
bne checkC
testNext
inx
txa
and #$0F
tax
cpx MIDI_NEXT
bne contF
jmp doneTest
contF
lda MIDI_BUFF,x
and #$80
bne testNext
jmp doNext
checkC
cmp #$c0
bne check8B
inx
txa
and #$0F
tax
cpx MIDI_NEXT
bne doPatch
jmp doneTest
doPatch
lda MIDI_BUFF,x
sta MIDI_PATCH
jmp doNext
check8B
inx
inx
inx
txa
and #$0F
tax
cpx MIDI_NEXT
beq noteComplete
jmp doneTest
noteComplete
ldx MIDI_START
lda MIDI_BUFF,x
and #$e0
cmp #$80
beq doNote
jmp doNext
doNote
ldx MIDI_START
inx
txa
and #$0F
tax
lda MIDI_BUFF,x
sta MIDI_TMP ; note
tay
inx
txa
and #$0F
tax
lda MIDI_BUFF,x
sta MIDI_TMP2 ; vol
tya
cmp #$71
bmi noteOkLow
jmp doNext
noteOkLow
cmp #$21
bpl noteOkHigh
jmp doNext
noteOkHigh
lda MIDI_PATCH
cmp #$40
bcs jNoteTri
and #$04
bne noTri
tya
cmp #$39 ; split at A3
bpl noTri
jNoteTri
jmp noteTri
noTri
tya
cmp MIDI_NOTE1
bne testPulse2
lda #$00
jmp doPulse
testPulse2
cmp MIDI_NOTE2
bne useRecent
lda #$04
jmp doPulse
useRecent
lda MIDI_TMP2 ; /vol
bne swapRecent
jmp doNext
swapRecent
lda MIDI_RECENT
eor #04
doPulse
tax
tya
sta MIDI_NOTE1,x
sec
sbc #$21
tay
lda MIDI_PATCH
and #$08
bne noteM5
lda notesLow,y
sta $4002,x
lda notesHigh,y
ora #$08
sta $4003,x
jmp getVol
noteM5
lda notesLow,y
sta $5002,x
lda notesHigh,y
ora #$08
sta $5003,x
getVol
lda MIDI_TMP2 ; /vol
cmp #$78
bpl setPulsVol
clc
adc #$07
setPulsVol
ror
ror
ror
and #$0f
tay
ora #$30
sta MIDI_TMP
lda MIDI_PATCH
ror
ror
ror
and #$c0
ora MIDI_TMP
sta MIDI_TMP
lda MIDI_PATCH
and #$08
bne volM5
lda MIDI_TMP
sta $4000,x
lda #$00
sta $4001,x
lda APU_STATUSC
ora #$03
sta $4015
jmp upRecent
volM5
lda MIDI_TMP
sta $5000,x
lda #$00
sta $5001,x
lda MMC5_STATUSC
ora #$03
sta $5015
upRecent
tya
bne setRecent
txa
eor #$04
tax
setRecent
stx MIDI_RECENT
jmp doNext
noteTri
tya
cmp MIDI_NOTE3
beq doTri
lda MIDI_TMP2
beq doNext
tya
doTri
sta MIDI_NOTE3
ldy #$3F
cpy MIDI_PATCH
bcc normOct
;sec ; Must be set already if here
sbc #$09 ; (Up one octave)
jmp setTri
normOct
sec
sbc #$15 ; correct
setTri
tay
lda notesLow,y
sta $400a
lda notesHigh,y
ora #$f8
sta $400b
lda MIDI_TMP2
beq triOff
lda #$ff
bne triSet
triOff
lda #$80
triSet
sta $4008
lda APU_STATUSC
ora #$04
sta $4015
doNext
lda MIDI_NEXT
sta MIDI_START
doneTest
doReset
joy2read
ldx #$08
- clc
lda $4017
ora #$fc
adc #3
rol JOYPAD
dex
bne -
rts
.pad $4fed
copyapustatus
lda $4015
sta APU_STATUSC
lda $5015
sta MMC5_STATUSC
jmp busywait
.pad $4ffc ;powerpak bios jumps to (4FFC)
.dw reset ;vectors are NOT mapped to $Fxxx, IRQ/NMI must be disabled.
.align $4000