blob: 5ecae160351baf945e8753598ad85fcd94fc74a9 [file] [log] [blame]
// Copyright (c) 2012-2013 Ludvig Strigeus
// This program is GPL Licensed. See COPYING for the full license.
module LenCtr_Lookup(input [4:0] X, output [7:0] Yout);
reg [6:0] Y;
always @*
begin
case(X)
0: Y = 7'h05;
1: Y = 7'h7F;
2: Y = 7'h0A;
3: Y = 7'h01;
4: Y = 7'h14;
5: Y = 7'h02;
6: Y = 7'h28;
7: Y = 7'h03;
8: Y = 7'h50;
9: Y = 7'h04;
10: Y = 7'h1E;
11: Y = 7'h05;
12: Y = 7'h07;
13: Y = 7'h06;
14: Y = 7'h0D;
15: Y = 7'h07;
16: Y = 7'h06;
17: Y = 7'h08;
18: Y = 7'h0C;
19: Y = 7'h09;
20: Y = 7'h18;
21: Y = 7'h0A;
22: Y = 7'h30;
23: Y = 7'h0B;
24: Y = 7'h60;
25: Y = 7'h0C;
26: Y = 7'h24;
27: Y = 7'h0D;
28: Y = 7'h08;
29: Y = 7'h0E;
30: Y = 7'h10;
31: Y = 7'h0F;
endcase
end
assign Yout = {Y, 1'b0};
endmodule
module SquareChan(input clk, input ce, input reset, input sq2,
input [1:0] Addr,
input [7:0] DIN,
input MW,
input LenCtr_Clock,
input Env_Clock,
input Enabled,
input [7:0] LenCtr_In,
output reg [3:0] Sample,
output IsNonZero);
reg [7:0] LenCtr;
// Register 1
reg [1:0] Duty;
reg EnvLoop, EnvDisable, EnvDoReset;
reg [3:0] Volume, Envelope, EnvDivider;
wire LenCtrHalt = EnvLoop; // Aliased bit
assign IsNonZero = (LenCtr != 0);
// Register 2
reg SweepEnable, SweepNegate, SweepReset;
reg [2:0] SweepPeriod, SweepDivider, SweepShift;
reg [10:0] Period;
reg [11:0] TimerCtr;
reg [2:0] SeqPos;
wire [10:0] ShiftedPeriod = (Period >> SweepShift);
wire [10:0] PeriodRhs = (SweepNegate ? (~ShiftedPeriod + {10'b0, sq2}) : ShiftedPeriod);
wire [11:0] NewSweepPeriod = Period + PeriodRhs;
wire ValidFreq = Period[10:3] >= 8 && (SweepNegate || !NewSweepPeriod[11]);
always @(posedge clk) if (reset) begin
LenCtr <= 0;
Duty <= 0;
EnvDoReset <= 0;
EnvLoop <= 0;
EnvDisable <= 0;
Volume <= 0;
Envelope <= 0;
EnvDivider <= 0;
SweepEnable <= 0;
SweepNegate <= 0;
SweepReset <= 0;
SweepPeriod <= 0;
SweepDivider <= 0;
SweepShift <= 0;
Period <= 0;
TimerCtr <= 0;
SeqPos <= 0;
end else if (ce) begin
// Check if writing to the regs of this channel
// NOTE: This needs to be done before the clocking below.
if (MW) begin
case(Addr)
0: begin
// if (sq2) $write("SQ0: Duty=%d, EnvLoop=%d, EnvDisable=%d, Volume=%d\n", DIN[7:6], DIN[5], DIN[4], DIN[3:0]);
Duty <= DIN[7:6];
EnvLoop <= DIN[5];
EnvDisable <= DIN[4];
Volume <= DIN[3:0];
end
1: begin
// if (sq2) $write("SQ1: SweepEnable=%d, SweepPeriod=%d, SweepNegate=%d, SweepShift=%d, DIN=%X\n", DIN[7], DIN[6:4], DIN[3], DIN[2:0], DIN);
SweepEnable <= DIN[7];
SweepPeriod <= DIN[6:4];
SweepNegate <= DIN[3];
SweepShift <= DIN[2:0];
SweepReset <= 1;
end
2: begin
// if (sq2) $write("SQ2: Period=%d. DIN=%X\n", DIN, DIN);
Period[7:0] <= DIN;
end
3: begin
// Upper bits of the period
// if (sq2) $write("SQ3: PeriodUpper=%d LenCtr=%x DIN=%X\n", DIN[2:0], LenCtr_In, DIN);
Period[10:8] <= DIN[2:0];
LenCtr <= LenCtr_In;
EnvDoReset <= 1;
SeqPos <= 0;
end
endcase
end
// Count down the square timer...
if (TimerCtr == 0) begin
// Timer was clocked
TimerCtr <= {Period, 1'b0};
SeqPos <= SeqPos - 1;
end else begin
TimerCtr <= TimerCtr - 1;
end
// Clock the length counter?
if (LenCtr_Clock && LenCtr != 0 && !LenCtrHalt) begin
LenCtr <= LenCtr - 1;
end
// Clock the sweep unit?
if (LenCtr_Clock) begin
if (SweepDivider == 0) begin
SweepDivider <= SweepPeriod;
if (SweepEnable && SweepShift != 0 && ValidFreq)
Period <= NewSweepPeriod[10:0];
end else begin
SweepDivider <= SweepDivider - 1;
end
if (SweepReset)
SweepDivider <= SweepPeriod;
SweepReset <= 0;
end
// Clock the envelope generator?
if (Env_Clock) begin
if (EnvDoReset) begin
EnvDivider <= Volume;
Envelope <= 15;
EnvDoReset <= 0;
end else if (EnvDivider == 0) begin
EnvDivider <= Volume;
if (Envelope != 0 || EnvLoop)
Envelope <= Envelope - 1;
end else begin
EnvDivider <= EnvDivider - 1;
end
end
// Length counter forced to zero if disabled.
if (!Enabled)
LenCtr <= 0;
end
reg DutyEnabled;
always @* begin
// Determine if the duty is enabled or not
case (Duty)
0: DutyEnabled = (SeqPos == 7);
1: DutyEnabled = (SeqPos >= 6);
2: DutyEnabled = (SeqPos >= 4);
3: DutyEnabled = (SeqPos < 6);
endcase
// Compute the output
if (LenCtr == 0 || !ValidFreq || !DutyEnabled)
Sample = 0;
else
Sample = EnvDisable ? Volume : Envelope;
end
endmodule
module TriangleChan(input clk, input ce, input reset,
input [1:0] Addr,
input [7:0] DIN,
input MW,
input LenCtr_Clock,
input LinCtr_Clock,
input Enabled,
input [7:0] LenCtr_In,
output [3:0] Sample,
output IsNonZero);
//
reg [10:0] Period, TimerCtr;
reg [4:0] SeqPos;
//
// Linear counter state
reg [6:0] LinCtrPeriod, LinCtr;
reg LinCtrl, LinHalt;
wire LinCtrZero = (LinCtr == 0);
//
// Length counter state
reg [7:0] LenCtr;
wire LenCtrHalt = LinCtrl; // Aliased bit
wire LenCtrZero = (LenCtr == 0);
assign IsNonZero = !LenCtrZero;
//
always @(posedge clk) if (reset) begin
Period <= 0;
TimerCtr <= 0;
SeqPos <= 0;
LinCtrPeriod <= 0;
LinCtr <= 0;
LinCtrl <= 0;
LinHalt <= 0;
LenCtr <= 0;
end else if (ce) begin
// Check if writing to the regs of this channel
if (MW) begin
case (Addr)
0: begin
LinCtrl <= DIN[7];
LinCtrPeriod <= DIN[6:0];
end
2: begin
Period[7:0] <= DIN;
end
3: begin
Period[10:8] <= DIN[2:0];
LenCtr <= LenCtr_In;
LinHalt <= 1;
end
endcase
end
// Count down the period timer...
if (TimerCtr == 0) begin
TimerCtr <= Period;
end else begin
TimerCtr <= TimerCtr - 1;
end
//
// Clock the length counter?
if (LenCtr_Clock && !LenCtrZero && !LenCtrHalt) begin
LenCtr <= LenCtr - 1;
end
//
// Clock the linear counter?
if (LinCtr_Clock) begin
if (LinHalt)
LinCtr <= LinCtrPeriod;
else if (!LinCtrZero)
LinCtr <= LinCtr - 1;
if (!LinCtrl)
LinHalt <= 0;
end
//
// Length counter forced to zero if disabled.
if (!Enabled)
LenCtr <= 0;
//
// Clock the sequencer position
if (TimerCtr == 0 && !LenCtrZero && !LinCtrZero)
SeqPos <= SeqPos + 1;
end
// Generate the output
assign Sample = SeqPos[3:0] ^ {4{~SeqPos[4]}};
//
endmodule
module NoiseChan(input clk, input ce, input reset,
input [1:0] Addr,
input [7:0] DIN,
input MW,
input LenCtr_Clock,
input Env_Clock,
input Enabled,
input [7:0] LenCtr_In,
output [3:0] Sample,
output IsNonZero);
//
// Envelope volume
reg EnvLoop, EnvDisable, EnvDoReset;
reg [3:0] Volume, Envelope, EnvDivider;
// Length counter
wire LenCtrHalt = EnvLoop; // Aliased bit
reg [7:0] LenCtr;
//
reg ShortMode;
reg [14:0] Shift = 1;
assign IsNonZero = (LenCtr != 0);
//
// Period stuff
reg [3:0] Period;
reg [11:0] NoisePeriod, TimerCtr;
always @* begin
case (Period)
0: NoisePeriod = 12'h004;
1: NoisePeriod = 12'h008;
2: NoisePeriod = 12'h010;
3: NoisePeriod = 12'h020;
4: NoisePeriod = 12'h040;
5: NoisePeriod = 12'h060;
6: NoisePeriod = 12'h080;
7: NoisePeriod = 12'h0A0;
8: NoisePeriod = 12'h0CA;
9: NoisePeriod = 12'h0FE;
10: NoisePeriod = 12'h17C;
11: NoisePeriod = 12'h1FC;
12: NoisePeriod = 12'h2FA;
13: NoisePeriod = 12'h3F8;
14: NoisePeriod = 12'h7F2;
15: NoisePeriod = 12'hFE4;
endcase
end
//
always @(posedge clk) if (reset) begin
EnvLoop <= 0;
EnvDisable <= 0;
EnvDoReset <= 0;
Volume <= 0;
Envelope <= 0;
EnvDivider <= 0;
LenCtr <= 0;
ShortMode <= 0;
Shift <= 1;
Period <= 0;
TimerCtr <= 0;
end else if (ce) begin
// Check if writing to the regs of this channel
if (MW) begin
case (Addr)
0: begin
EnvLoop <= DIN[5];
EnvDisable <= DIN[4];
Volume <= DIN[3:0];
end
2: begin
ShortMode <= DIN[7];
Period <= DIN[3:0];
end
3: begin
LenCtr <= LenCtr_In;
EnvDoReset <= 1;
end
endcase
end
// Count down the period timer...
if (TimerCtr == 0) begin
TimerCtr <= NoisePeriod;
// Clock the shift register. Use either
// bit 1 or 6 as the tap.
Shift <= {
Shift[0] ^ (ShortMode ? Shift[6] : Shift[1]),
Shift[14:1]};
end else begin
TimerCtr <= TimerCtr - 1;
end
// Clock the length counter?
if (LenCtr_Clock && LenCtr != 0 && !LenCtrHalt) begin
LenCtr <= LenCtr - 1;
end
// Clock the envelope generator?
if (Env_Clock) begin
if (EnvDoReset) begin
EnvDivider <= Volume;
Envelope <= 15;
EnvDoReset <= 0;
end else if (EnvDivider == 0) begin
EnvDivider <= Volume;
if (Envelope != 0)
Envelope <= Envelope - 1;
else if (EnvLoop)
Envelope <= 15;
end else
EnvDivider <= EnvDivider - 1;
end
if (!Enabled)
LenCtr <= 0;
end
// Produce the output signal
assign Sample =
(LenCtr == 0 || Shift[0]) ?
0 :
(EnvDisable ? Volume : Envelope);
endmodule
module DmcChan(input clk, input ce, input reset,
input odd_or_even,
input [2:0] Addr,
input [7:0] DIN,
input MW,
output [6:0] Sample,
output DmaReq, // 1 when DMC wants DMA
input DmaAck, // 1 when DMC byte is on DmcData. DmcDmaRequested should go low.
output [15:0] DmaAddr, // Address DMC wants to read
input [7:0] DmaData, // Input data to DMC from memory.
output Irq,
output IsDmcActive);
reg IrqEnable;
reg IrqActive;
reg Loop; // Looping enabled
reg [3:0] Freq; // Current value of frequency register
reg [6:0] Dac = 0; // Current value of DAC
reg [7:0] SampleAddress; // Base address of sample
reg [7:0] SampleLen; // Length of sample
reg [7:0] ShiftReg; // Shift register
reg [8:0] Cycles; // Down counter, is the period
reg [14:0] Address; // 15 bits current address, 0x8000-0xffff
reg [11:0] BytesLeft; // 12 bits bytes left counter 0 - 4081.
reg [2:0] BitsUsed; // Number of bits left in the SampleBuffer.
reg [7:0] SampleBuffer; // Next value to be loaded into shift reg
reg HasSampleBuffer; // Sample buffer is nonempty
reg HasShiftReg; // Shift reg is non empty
reg [8:0] NewPeriod[0:15];
reg DmcEnabled;
reg [1:0] ActivationDelay;
assign DmaAddr = {1'b1, Address};
assign Sample = Dac;
assign Irq = IrqActive;
assign IsDmcActive = DmcEnabled;
assign DmaReq = !HasSampleBuffer && DmcEnabled && !ActivationDelay[0];
initial begin
NewPeriod[0] = 428;
NewPeriod[1] = 380;
NewPeriod[2] = 340;
NewPeriod[3] = 320;
NewPeriod[4] = 286;
NewPeriod[5] = 254;
NewPeriod[6] = 226;
NewPeriod[7] = 214;
NewPeriod[8] = 190;
NewPeriod[9] = 160;
NewPeriod[10] = 142;
NewPeriod[11] = 128;
NewPeriod[12] = 106;
NewPeriod[13] = 84;
NewPeriod[14] = 72;
NewPeriod[15] = 54;
end
// Shift register initially loaded with 07
always @(posedge clk) begin
if (reset) begin
IrqEnable <= 0;
IrqActive <= 0;
Loop <= 0;
Freq <= 0;
Dac <= 0;
SampleAddress <= 0;
SampleLen <= 0;
ShiftReg <= 8'hff;
Cycles <= 439;
Address <= 0;
BytesLeft <= 0;
BitsUsed <= 0;
SampleBuffer <= 0;
HasSampleBuffer <= 0;
HasShiftReg <= 0;
DmcEnabled <= 0;
ActivationDelay <= 0;
end else if (ce) begin
if (ActivationDelay == 3 && !odd_or_even) ActivationDelay <= 1;
if (ActivationDelay == 1) ActivationDelay <= 0;
if (MW) begin
case (Addr)
0: begin // $4010 il-- ffff IRQ enable, loop, frequency index
IrqEnable <= DIN[7];
Loop <= DIN[6];
Freq <= DIN[3:0];
if (!DIN[7]) IrqActive <= 0;
end
1: begin // $4011 -ddd dddd DAC
// This will get missed if the Dac <= far below runs, that is by design.
Dac <= DIN[6:0];
end
2: begin // $4012 aaaa aaaa sample address
SampleAddress <= DIN[7:0];
end
3: begin // $4013 llll llll sample length
SampleLen <= DIN[7:0];
end
5: begin // $4015 write ---D NT21 Enable DMC (D)
IrqActive <= 0;
DmcEnabled <= DIN[4];
// If the DMC bit is set, the DMC sample will be restarted only if not already active.
if (DIN[4] && !DmcEnabled) begin
Address <= {1'b1, SampleAddress, 6'b000000};
BytesLeft <= {SampleLen, 4'b0000};
ActivationDelay <= 3;
end
end
endcase
end
Cycles <= Cycles - 1;
if (Cycles == 1) begin
Cycles <= NewPeriod[Freq];
if (HasShiftReg) begin
if (ShiftReg[0]) begin
Dac[6:1] <= (Dac[6:1] != 6'b111111) ? Dac[6:1] + 6'b000001 : Dac[6:1];
end else begin
Dac[6:1] <= (Dac[6:1] != 6'b000000) ? Dac[6:1] + 6'b111111 : Dac[6:1];
end
end
ShiftReg <= {1'b0, ShiftReg[7:1]};
BitsUsed <= BitsUsed + 1;
if (BitsUsed == 7) begin
HasShiftReg <= HasSampleBuffer;
ShiftReg <= SampleBuffer;
HasSampleBuffer <= 0;
end
end
// Acknowledge DMA?
if (DmaAck) begin
Address <= Address + 1;
BytesLeft <= BytesLeft - 1;
HasSampleBuffer <= 1;
SampleBuffer <= DmaData;
if (BytesLeft == 0) begin
Address <= {1'b1, SampleAddress, 6'b000000};
BytesLeft <= {SampleLen, 4'b0000};
DmcEnabled <= Loop;
if (!Loop && IrqEnable)
IrqActive <= 1;
end
end
end
end
endmodule
module ApuLookupTable(input clk, input [7:0] in_a, input [7:0] in_b, output [15:0] out);
reg [15:0] lookup[0:511];
reg [15:0] tmp_a, tmp_b;
initial begin
lookup[ 0] = 0; lookup[ 1] = 760; lookup[ 2] = 1503; lookup[ 3] = 2228;
lookup[ 4] = 2936; lookup[ 5] = 3627; lookup[ 6] = 4303; lookup[ 7] = 4963;
lookup[ 8] = 5609; lookup[ 9] = 6240; lookup[ 10] = 6858; lookup[ 11] = 7462;
lookup[ 12] = 8053; lookup[ 13] = 8631; lookup[ 14] = 9198; lookup[ 15] = 9752;
lookup[ 16] = 10296; lookup[ 17] = 10828; lookup[ 18] = 11349; lookup[ 19] = 11860;
lookup[ 20] = 12361; lookup[ 21] = 12852; lookup[ 22] = 13334; lookup[ 23] = 13807;
lookup[ 24] = 14270; lookup[ 25] = 14725; lookup[ 26] = 15171; lookup[ 27] = 15609;
lookup[ 28] = 16039; lookup[ 29] = 16461; lookup[ 30] = 16876; lookup[256] = 0;
lookup[257] = 439; lookup[258] = 874; lookup[259] = 1306; lookup[260] = 1735;
lookup[261] = 2160; lookup[262] = 2581; lookup[263] = 2999; lookup[264] = 3414;
lookup[265] = 3826; lookup[266] = 4234; lookup[267] = 4639; lookup[268] = 5041;
lookup[269] = 5440; lookup[270] = 5836; lookup[271] = 6229; lookup[272] = 6618;
lookup[273] = 7005; lookup[274] = 7389; lookup[275] = 7769; lookup[276] = 8147;
lookup[277] = 8522; lookup[278] = 8895; lookup[279] = 9264; lookup[280] = 9631;
lookup[281] = 9995; lookup[282] = 10356; lookup[283] = 10714; lookup[284] = 11070;
lookup[285] = 11423; lookup[286] = 11774; lookup[287] = 12122; lookup[288] = 12468;
lookup[289] = 12811; lookup[290] = 13152; lookup[291] = 13490; lookup[292] = 13825;
lookup[293] = 14159; lookup[294] = 14490; lookup[295] = 14818; lookup[296] = 15145;
lookup[297] = 15469; lookup[298] = 15791; lookup[299] = 16110; lookup[300] = 16427;
lookup[301] = 16742; lookup[302] = 17055; lookup[303] = 17366; lookup[304] = 17675;
lookup[305] = 17981; lookup[306] = 18286; lookup[307] = 18588; lookup[308] = 18888;
lookup[309] = 19187; lookup[310] = 19483; lookup[311] = 19777; lookup[312] = 20069;
lookup[313] = 20360; lookup[314] = 20648; lookup[315] = 20935; lookup[316] = 21219;
lookup[317] = 21502; lookup[318] = 21783; lookup[319] = 22062; lookup[320] = 22339;
lookup[321] = 22615; lookup[322] = 22889; lookup[323] = 23160; lookup[324] = 23431;
lookup[325] = 23699; lookup[326] = 23966; lookup[327] = 24231; lookup[328] = 24494;
lookup[329] = 24756; lookup[330] = 25016; lookup[331] = 25274; lookup[332] = 25531;
lookup[333] = 25786; lookup[334] = 26040; lookup[335] = 26292; lookup[336] = 26542;
lookup[337] = 26791; lookup[338] = 27039; lookup[339] = 27284; lookup[340] = 27529;
lookup[341] = 27772; lookup[342] = 28013; lookup[343] = 28253; lookup[344] = 28492;
lookup[345] = 28729; lookup[346] = 28964; lookup[347] = 29198; lookup[348] = 29431;
lookup[349] = 29663; lookup[350] = 29893; lookup[351] = 30121; lookup[352] = 30349;
lookup[353] = 30575; lookup[354] = 30800; lookup[355] = 31023; lookup[356] = 31245;
lookup[357] = 31466; lookup[358] = 31685; lookup[359] = 31904; lookup[360] = 32121;
lookup[361] = 32336; lookup[362] = 32551; lookup[363] = 32764; lookup[364] = 32976;
lookup[365] = 33187; lookup[366] = 33397; lookup[367] = 33605; lookup[368] = 33813;
lookup[369] = 34019; lookup[370] = 34224; lookup[371] = 34428; lookup[372] = 34630;
lookup[373] = 34832; lookup[374] = 35032; lookup[375] = 35232; lookup[376] = 35430;
lookup[377] = 35627; lookup[378] = 35823; lookup[379] = 36018; lookup[380] = 36212;
lookup[381] = 36405; lookup[382] = 36597; lookup[383] = 36788; lookup[384] = 36978;
lookup[385] = 37166; lookup[386] = 37354; lookup[387] = 37541; lookup[388] = 37727;
lookup[389] = 37912; lookup[390] = 38095; lookup[391] = 38278; lookup[392] = 38460;
lookup[393] = 38641; lookup[394] = 38821; lookup[395] = 39000; lookup[396] = 39178;
lookup[397] = 39355; lookup[398] = 39532; lookup[399] = 39707; lookup[400] = 39881;
lookup[401] = 40055; lookup[402] = 40228; lookup[403] = 40399; lookup[404] = 40570;
lookup[405] = 40740; lookup[406] = 40909; lookup[407] = 41078; lookup[408] = 41245;
lookup[409] = 41412; lookup[410] = 41577; lookup[411] = 41742; lookup[412] = 41906;
lookup[413] = 42070; lookup[414] = 42232; lookup[415] = 42394; lookup[416] = 42555;
lookup[417] = 42715; lookup[418] = 42874; lookup[419] = 43032; lookup[420] = 43190;
lookup[421] = 43347; lookup[422] = 43503; lookup[423] = 43659; lookup[424] = 43813;
lookup[425] = 43967; lookup[426] = 44120; lookup[427] = 44273; lookup[428] = 44424;
lookup[429] = 44575; lookup[430] = 44726; lookup[431] = 44875; lookup[432] = 45024;
lookup[433] = 45172; lookup[434] = 45319; lookup[435] = 45466; lookup[436] = 45612;
lookup[437] = 45757; lookup[438] = 45902; lookup[439] = 46046; lookup[440] = 46189;
lookup[441] = 46332; lookup[442] = 46474; lookup[443] = 46615; lookup[444] = 46756;
lookup[445] = 46895; lookup[446] = 47035; lookup[447] = 47173; lookup[448] = 47312;
lookup[449] = 47449; lookup[450] = 47586; lookup[451] = 47722; lookup[452] = 47857;
lookup[453] = 47992; lookup[454] = 48127; lookup[455] = 48260; lookup[456] = 48393;
lookup[457] = 48526; lookup[458] = 48658;
end
always @(posedge clk) begin
tmp_a <= lookup[{1'b0, in_a}];
tmp_b <= lookup[{1'b1, in_b}];
end
assign out = tmp_a + tmp_b;
endmodule
module APU(input clk, input ce, input reset,
input [4:0] ADDR, // APU Address Line
input [7:0] DIN, // Data to APU
output [7:0] DOUT, // Data from APU
input MW, // Writes to APU
input MR, // Reads from APU
input [4:0] audio_channels, // Enabled audio channels
output [15:0] Sample,
output DmaReq, // 1 when DMC wants DMA
input DmaAck, // 1 when DMC byte is on DmcData. DmcDmaRequested should go low.
output [15:0] DmaAddr, // Address DMC wants to read
input [7:0] DmaData, // Input data to DMC from memory.
output odd_or_even,
output IRQ); // IRQ asserted
// Which channels are enabled?
reg [3:0] Enabled;
// Output samples from the 4 channels
wire [3:0] Sq1Sample,Sq2Sample,TriSample,NoiSample;
// Output samples from the DMC channel
wire [6:0] DmcSample;
wire DmcIrq;
wire IsDmcActive;
// Generate internal memory write signals
wire ApuMW0 = MW && ADDR[4:2]==0; // SQ1
wire ApuMW1 = MW && ADDR[4:2]==1; // SQ2
wire ApuMW2 = MW && ADDR[4:2]==2; // TRI
wire ApuMW3 = MW && ADDR[4:2]==3; // NOI
wire ApuMW4 = MW && ADDR[4:2]>=4; // DMC
wire ApuMW5 = MW && ADDR[4:2]==5; // Control registers
wire Sq1NonZero, Sq2NonZero, TriNonZero, NoiNonZero;
// Common input to all channels
wire [7:0] LenCtr_In;
LenCtr_Lookup len(DIN[7:3], LenCtr_In);
// Frame sequencer registers
reg FrameSeqMode;
reg [15:0] Cycles;
reg ClkE, ClkL;
reg Wrote4017;
reg [1:0] IrqCtr;
reg InternalClock; // APU Differentiates between Even or Odd clocks
assign odd_or_even = InternalClock;
// Generate each channel
SquareChan Sq1(clk, ce, reset, 0, ADDR[1:0], DIN, ApuMW0, ClkL, ClkE, Enabled[0], LenCtr_In, Sq1Sample, Sq1NonZero);
SquareChan Sq2(clk, ce, reset, 1, ADDR[1:0], DIN, ApuMW1, ClkL, ClkE, Enabled[1], LenCtr_In, Sq2Sample, Sq2NonZero);
TriangleChan Tri(clk, ce, reset, ADDR[1:0], DIN, ApuMW2, ClkL, ClkE, Enabled[2], LenCtr_In, TriSample, TriNonZero);
NoiseChan Noi(clk, ce, reset, ADDR[1:0], DIN, ApuMW3, ClkL, ClkE, Enabled[3], LenCtr_In, NoiSample, NoiNonZero);
DmcChan Dmc(clk, ce, reset, odd_or_even, ADDR[2:0], DIN, ApuMW4, DmcSample, DmaReq, DmaAck, DmaAddr, DmaData, DmcIrq, IsDmcActive);
// Reading this register clears the frame interrupt flag (but not the DMC interrupt flag).
// If an interrupt flag was set at the same moment of the read, it will read back as 1 but it will not be cleared.
reg FrameInterrupt, DisableFrameInterrupt;
//mode 0: 4-step effective rate (approx)
//---------------------------------------
// - - - f 60 Hz
// - l - l 120 Hz
// e e e e 240 Hz
//mode 1: 5-step effective rate (approx)
//---------------------------------------
// - - - - - (interrupt flag never set)
// l - l - - 96 Hz
// e e e e - 192 Hz
always @(posedge clk) if (reset) begin
FrameSeqMode <= 0;
DisableFrameInterrupt <= 0;
FrameInterrupt <= 0;
Enabled <= 0;
InternalClock <= 0;
Wrote4017 <= 0;
ClkE <= 0;
ClkL <= 0;
Cycles <= 4; // This needs to be 5 for proper power up behavior
IrqCtr <= 0;
end else if (ce) begin
FrameInterrupt <= IrqCtr[1] ? 1 : (ADDR == 5'h15 && MR || ApuMW5 && ADDR[1:0] == 3 && DIN[6]) ? 0 : FrameInterrupt;
InternalClock <= !InternalClock;
IrqCtr <= {IrqCtr[0], 1'b0};
Cycles <= Cycles + 1;
ClkE <= 0;
ClkL <= 0;
if (Cycles == 7457) begin
ClkE <= 1;
end else if (Cycles == 14913) begin
ClkE <= 1;
ClkL <= 1;
ClkE <= 1;
ClkL <= 1;
end else if (Cycles == 22371) begin
ClkE <= 1;
end else if (Cycles == 29829) begin
if (!FrameSeqMode) begin
ClkE <= 1;
ClkL <= 1;
Cycles <= 0;
IrqCtr <= 3;
FrameInterrupt <= 1;
end
end else if (Cycles == 37281) begin
ClkE <= 1;
ClkL <= 1;
Cycles <= 0;
end
// Handle one cycle delayed write to 4017.
Wrote4017 <= 0;
if (Wrote4017) begin
if (FrameSeqMode) begin
ClkE <= 1;
ClkL <= 1;
end
Cycles <= 0;
end
// if (ClkE||ClkL) $write("%d: Clocking %s%s\n", Cycles, ClkE?"E":" ", ClkL?"L":" ");
// Handle writes to control registers
if (ApuMW5) begin
case (ADDR[1:0])
1: begin // Register $4015
Enabled <= DIN[3:0];
// $write("$4015 = %X\n", DIN);
end
3: begin // Register $4017
FrameSeqMode <= DIN[7]; // 1 = 5 frames cycle, 0 = 4 frames cycle
DisableFrameInterrupt <= DIN[6];
// If the internal clock is even, things happen
// right away.
if (!InternalClock) begin
if (DIN[7]) begin
ClkE <= 1;
ClkL <= 1;
end
Cycles <= 0;
end
// Otherwise they get delayed one clock
Wrote4017 <= InternalClock;
end
endcase
end
end
ApuLookupTable lookup(clk,
(audio_channels[0] ? {4'b0, Sq1Sample} : 8'b0) +
(audio_channels[1] ? {4'b0, Sq2Sample} : 8'b0),
(audio_channels[2] ? {4'b0, TriSample} + {3'b0, TriSample, 1'b0} : 8'b0) +
(audio_channels[3] ? {3'b0, NoiSample, 1'b0} : 8'b0) +
(audio_channels[4] ? {1'b0, DmcSample} : 8'b0),
Sample);
wire frame_irq = FrameInterrupt && !DisableFrameInterrupt;
// Generate bus output
assign DOUT = {DmcIrq, frame_irq, 1'b0,
IsDmcActive,
NoiNonZero,
TriNonZero,
Sq2NonZero,
Sq1NonZero};
assign IRQ = frame_irq || DmcIrq;
endmodule