/* ----------------------------------------------------------------------------- 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 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 #(parameter ROMFILE="gs105b.mif") ( 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 [14:0] OUTL, output [14:0] OUTR ); // port #xxBB : #xxB3 assign DO = A ? {bit7, 6'b111111, bit0} : 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(CE) begin cnt <= cnt + 1'b1; if (cnt == 746) begin // 37.48kHz cnt <= 0; int_n <= 0; end end if (~cpu_iorq_n & ~cpu_m1_n) int_n <= 1; end reg bit7; reg bit0; always @(posedge CLK) begin if (~cpu_iorq_n & cpu_m1_n) begin case(cpu_a_bus[3:0]) 'h2: bit7 <= 0; 'h3: bit7 <= 1; 'h5: bit0 <= 0; 'hA: bit7 <= ~port_00[0]; 'hB: bit0 <= port_09[5]; endcase end else if (~CS_n) begin if (~A & ~RD_n) bit7 <= 0; if (~A & ~WR_n) bit7 <= 1; if ( A & ~WR_n) bit0 <= 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) 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 && !page_addr[5:1]) ? mem_do : (~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) ? {bit7, 6'b111111, bit0} : 8'hFF; wire mem_wr = ~cpu_wr_n & ~cpu_mreq_n & |page_addr; wire mem_rd = ~cpu_rd_n & ~cpu_mreq_n; 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 = mem_rd && |page_addr[5:1]; assign MEM_WR = mem_wr && |page_addr[5:1]; assign MEM_DI = cpu_do_bus; wire [7:0] mem_do; dpram #(.ADDRWIDTH(16), .MEM_INIT_FILE(ROMFILE)) mem ( .clock(CLK), .address_a(MEM_ADDR[15:0]), .wren_a(mem_wr && !page_addr[5:1]), .data_a(cpu_do_bus), .q_a(mem_do) ); 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