/* ----------------------------------------------------------------------------- General Sound ----------------------------------------------------------------------------- 18.08.2018 Reworked first verilog version 19.08.2018 Produce proper signed output 20.08.2018 Use external SDR/DDR RAM for page 2 and up 21.05.2020 Use external SDR/DDR RAM for all ROM/RAM CPU: Z80 @ 28MHz ROM: 32K RAM: up to 4096KB INT: 37.5KHz #xxBB Command register - регистр команд, доступный для записи #xxBB Status register - регистр состояния, доступный для чтения bit 7 флаг данных bit <6:1> Не определен bit 0 флаг команд. Этот регистр позволяет определить состояние GS, в частности можно ли прочитать или записать очередной байт данных, или подать очередную команду, и т.п. #xxB3 Data register - регистр данных, доступный для записи. В этот регистр Спектрум записывает данные, например, это могут быть аргументы команд. #xxB3 Output register - регистр вывода, доступный для чтения. Из этого регистра Спектрум читает данные, идущие от GS Внутренние порта: #xx00 "расширенная память" - регистр доступный для записи bit <3:0> переключают страницы по 32Kb, страница 0 - ПЗУ bit <7:0> не используются порты 1 - 5 "обеспечивают связь с SPECTRUM'ом" #xx01 чтение команды General Sound'ом bit <7:0> код команды #xx02 чтение данных General Sound'ом bit <7:0> данные #xx03 запись данных General Sound'ом для SPECTRUM'a bit <7:0> данные #xx04 чтение слова состояния General Sound'ом bit 0 флаг команд bit 7 флаг данных #xx05 сбрасывает бит D0 (флаг команд) слова состояния порты 6 - 9 "регулировка громкости" в каналах 1 - 4 #xx06 "регулировка громкости" в канале 1 bit <5:0> громкость bit <7:6> не используются #xx07 "регулировка громкости" в канале 2 bit <5:0> громкость bit <7:6> не используются #xx08 "регулировка громкости" в канале 3 bit <5:0> громкость bit <7:6> не используются #xx09 "регулировка громкости" в канале 4 bit <5:0> громкость bit <7:6> не используются #xx0A устанавливает бит 7 слова состояния не равным биту 0 порта #xx00 #xx0B устанавливает бит 0 слова состояния равным биту 5 порта #xx06 Распределение памяти #0000 - #3FFF - первые 16Kb ПЗУ #4000 - #7FFF - первые 16Kb первой страницы ОЗУ #8000 - #FFFF - листаемые страницы по 32Kb страница 0 - ПЗУ, страница 1 - первая страница ОЗУ страницы 2... ОЗУ Данные в каналы заносятся при чтении процессором ОЗУ по адресам #6000 - #7FFF автоматически. */ module gs ( input RESET, input CLK, input CE, input A, input [7:0] DI, output [7:0] DO, input CS_n, input WR_n, input RD_n, output [20:0] MEM_ADDR, output [7:0] MEM_DI, input [7:0] MEM_DO, output MEM_RD, output MEM_WR, input MEM_WAIT, output MEM_ROM, output [14:0] OUTL, output [14:0] OUTR ); parameter INT_DIV = 291; // port #xxBB : #xxB3 assign DO = A ? {flag_data, 6'b111111, flag_cmd} : port_03; // CPU reg int_n; wire cpu_m1_n; wire cpu_mreq_n; wire cpu_iorq_n; wire cpu_rd_n; wire cpu_wr_n; wire [15:0] cpu_a_bus; wire [7:0] cpu_do_bus; T80pa CPU ( .RESET_n(~RESET), .CLK(CLK), .CEN_p(CE & ~MEM_WAIT), .INT_n(int_n), .M1_n(cpu_m1_n), .MREQ_n(cpu_mreq_n), .IORQ_n(cpu_iorq_n), .RD_n(cpu_rd_n), .WR_n(cpu_wr_n), .A(cpu_a_bus), .DO(cpu_do_bus), .DI(cpu_di_bus) ); // INT# always @(posedge CLK) begin reg [9:0] cnt; if (RESET) begin cnt <= 0; int_n <= 1; end else if(CE) begin cnt <= cnt + 1'b1; if (cnt == INT_DIV) begin // 37.48kHz cnt <= 0; int_n <= 0; end end if (~cpu_iorq_n & ~cpu_m1_n) int_n <= 1; end reg flag_data; reg flag_cmd; always @(posedge CLK) begin if (~cpu_iorq_n & cpu_m1_n) begin case(cpu_a_bus[3:0]) 'h2: flag_data <= 0; 'h3: flag_data <= 1; 'h5: flag_cmd <= 0; 'hA: flag_data <= ~port_00[0]; 'hB: flag_cmd <= port_09[5]; endcase end if (~CS_n) begin if (~A & ~RD_n) flag_data <= 0; if (~A & ~WR_n) flag_data <= 1; if ( A & ~WR_n) flag_cmd <= 1; end end reg [7:0] port_BB; reg [7:0] port_B3; always @(posedge CLK) begin if (RESET) begin port_BB <= 0; port_B3 <= 0; end else if (~CS_n && ~WR_n) begin if(A) port_BB <= DI; else port_B3 <= DI; end end reg [5:0] port_00; reg [7:0] port_03; reg signed [6:0] port_06, port_07, port_08, port_09; reg signed [7:0] ch_a, ch_b, ch_c, ch_d; always @(posedge CLK) begin if (RESET) begin port_00 <= 0; port_03 <= 0; end else begin if (~cpu_iorq_n & ~cpu_wr_n) begin case(cpu_a_bus[3:0]) 0: port_00 <= cpu_do_bus[5:0]; 3: port_03 <= cpu_do_bus; 6: port_06 <= cpu_do_bus[5:0]; 7: port_07 <= cpu_do_bus[5:0]; 8: port_08 <= cpu_do_bus[5:0]; 9: port_09 <= cpu_do_bus[5:0]; endcase end if (~cpu_mreq_n && ~cpu_rd_n && cpu_a_bus[15:13] == 3 && ~MEM_WAIT) begin case(cpu_a_bus[9:8]) 0: ch_a <= {~MEM_DO[7],MEM_DO[6:0]}; 1: ch_b <= {~MEM_DO[7],MEM_DO[6:0]}; 2: ch_c <= {~MEM_DO[7],MEM_DO[6:0]}; 3: ch_d <= {~MEM_DO[7],MEM_DO[6:0]}; endcase end end end wire [7:0] cpu_di_bus = (~cpu_mreq_n && ~cpu_rd_n) ? MEM_DO : (~cpu_iorq_n && ~cpu_rd_n && cpu_a_bus[3:0] == 1) ? port_BB : (~cpu_iorq_n && ~cpu_rd_n && cpu_a_bus[3:0] == 2) ? port_B3 : (~cpu_iorq_n && ~cpu_rd_n && cpu_a_bus[3:0] == 4) ? {flag_data, 6'b111111, flag_cmd} : 8'hFF; wire [5:0] page_addr = cpu_a_bus[15] ? port_00 : cpu_a_bus[14]; assign MEM_ADDR = {page_addr, &cpu_a_bus[15:14], cpu_a_bus[13:0]}; assign MEM_RD = ~cpu_rd_n & ~cpu_mreq_n; assign MEM_WR = ~cpu_wr_n & ~cpu_mreq_n & ~MEM_ROM; assign MEM_DI = cpu_do_bus; assign MEM_ROM = ~|page_addr; reg signed [14:0] out_a,out_b,out_c,out_d; always @(posedge CLK) begin if(CE) begin out_a <= ch_a * port_06; out_b <= ch_b * port_07; out_c <= ch_c * port_08; out_d <= ch_d * port_09; end end reg signed [14:0] outl, outr; always @(posedge CLK) begin if(CE) begin outl <= out_a + out_b; outr <= out_c + out_d; end end assign OUTL = outl; assign OUTR = outr; endmodule