From 9d250a8234da73334a47f72b992f13c81d2d25cc Mon Sep 17 00:00:00 2001 From: Gyorgy Szombathelyi Date: Fri, 17 Aug 2018 00:37:11 +0200 Subject: [PATCH 1/5] BBC: standard clocks only in sd_card and user_io --- cores/bbc/fpga/mist/bbc_mist_top.qsf | 5 +- cores/bbc/fpga/mist/bbc_mist_top.v | 15 +- cores/bbc/fpga/mist/sd_card.v | 275 +++++++++++---------- cores/bbc/fpga/mist/user_io.v | 352 ++++++++++++++++----------- 4 files changed, 369 insertions(+), 278 deletions(-) diff --git a/cores/bbc/fpga/mist/bbc_mist_top.qsf b/cores/bbc/fpga/mist/bbc_mist_top.qsf index 2be9912..d40c332 100644 --- a/cores/bbc/fpga/mist/bbc_mist_top.qsf +++ b/cores/bbc/fpga/mist/bbc_mist_top.qsf @@ -133,7 +133,7 @@ set_location_assignment PIN_65 -to AUDIO_L set_location_assignment PIN_80 -to AUDIO_R set_global_assignment -name ENABLE_SIGNALTAP OFF -set_global_assignment -name USE_SIGNALTAP_FILE signal_tap.stp +set_global_assignment -name USE_SIGNALTAP_FILE output_files/stp2.stp set_global_assignment -name ENABLE_CONFIGURATION_PINS OFF set_global_assignment -name ENABLE_NCE_PIN OFF set_global_assignment -name ENABLE_BOOT_SEL_PIN OFF @@ -185,6 +185,7 @@ set_global_assignment -name VHDL_FILE "../../rtl/sn76489-1.0/sn76489_clock_div-c set_global_assignment -name VHDL_FILE "../../rtl/sn76489-1.0/sn76489_clock_div.vhd" set_global_assignment -name VHDL_FILE "../../rtl/sn76489-1.0/sn76489_attenuator-c.vhd" set_global_assignment -name VHDL_FILE "../../rtl/sn76489-1.0/sn76489_attenuator.vhd" +set_global_assignment -name VERILOG_FILE sd_card.v set_global_assignment -name VERILOG_FILE user_io.v set_global_assignment -name VERILOG_FILE osd.v set_global_assignment -name VERILOG_FILE data_io.v @@ -197,4 +198,4 @@ set_global_assignment -name QIP_FILE smmc.qip set_global_assignment -name QIP_FILE os12.qip set_global_assignment -name QIP_FILE basic2.qip set_global_assignment -name QIP_FILE dfs09.qip -set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file +set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top diff --git a/cores/bbc/fpga/mist/bbc_mist_top.v b/cores/bbc/fpga/mist/bbc_mist_top.v index 823352c..cf48ddf 100755 --- a/cores/bbc/fpga/mist/bbc_mist_top.v +++ b/cores/bbc/fpga/mist/bbc_mist_top.v @@ -34,7 +34,7 @@ module bbc_mist_top( // SPI - inout SPI_DO, + output SPI_DO, input SPI_DI, input SPI_SCK, input SPI_SS2, // data_io @@ -157,6 +157,8 @@ wire [7:0] sd_dout; wire sd_dout_strobe; wire [7:0] sd_din; wire sd_din_strobe; +wire [8:0] sd_buff_addr; +wire sd_ack_conf; wire [7:0] joystick_0; wire [7:0] joystick_1; @@ -167,8 +169,9 @@ wire scandoubler_disable; user_io #(.STRLEN(CONF_STR_LEN)) user_io( .conf_str ( CONF_STR ), - // the spi interface + .clk_sys(clk_32m), + // the spi interface .SPI_CLK ( SPI_SCK ), .SPI_SS_IO ( CONF_DATA0 ), .SPI_MISO ( SPI_DO ), // tristate handling inside user_io @@ -196,6 +199,8 @@ user_io #(.STRLEN(CONF_STR_LEN)) user_io( .sd_dout_strobe(sd_dout_strobe ), .sd_din ( sd_din ), .sd_din_strobe (sd_din_strobe ), + .sd_buff_addr (sd_buff_addr ), + .sd_ack_conf (sd_ack_conf ), .ps2_clk ( clk_14k ), .ps2_kbd_clk ( ps2_clk ), @@ -211,18 +216,21 @@ assign user_via_cb1_in = user_via_pb_out[1]; sd_card sd_card ( // connection to io controller + .clk(clk_32m), .io_lba (sd_lba ), .io_rd (sd_rd), .io_wr (sd_wr), .io_ack (sd_ack), + .io_ack_conf (sd_ack_conf ), .io_conf (sd_conf), .io_sdhc (sd_sdhc), .io_din (sd_dout), .io_din_strobe (sd_dout_strobe), .io_dout (sd_din), .io_dout_strobe ( sd_din_strobe), + .io_buff_addr (sd_buff_addr ), - .allow_sdhc ( 1'b0), // SDHC not supported + .allow_sdhc ( 1'b1), // SDHC not supported // connection to local CPU .sd_cs ( sd_cs ), @@ -459,6 +467,7 @@ wire sideways_ram = // status[2] is '1' of low mapping is selected in the menu wire basic_map = status[2]?(mem_romsel == 4'h0):(mem_romsel == 4'he); +//wire smmc_map = 0; wire smmc_map = status[2]?(mem_romsel == 4'h2):(mem_romsel == 4'hc); assign mem_di = diff --git a/cores/bbc/fpga/mist/sd_card.v b/cores/bbc/fpga/mist/sd_card.v index 5e7fed3..fa7ef95 100644 --- a/cores/bbc/fpga/mist/sd_card.v +++ b/cores/bbc/fpga/mist/sd_card.v @@ -25,25 +25,29 @@ // http://elm-chan.org/docs/mmc/mmc_e.html module sd_card ( + input clk, // link to user_io for io controller output [31:0] io_lba, output reg io_rd, output reg io_wr, - input io_ack, - output io_conf, - output io_sdhc, - + input io_ack, + input io_ack_conf, + output io_conf, + output io_sdhc, + // data coming in from io controller - input [7:0] io_din, - input io_din_strobe, + input [7:0] io_din, + input io_din_strobe, // data going out to io controller - output [7:0] io_dout, - input io_dout_strobe, + output [7:0] io_dout, + input io_dout_strobe, + + input [8:0] io_buff_addr, // configuration input input allow_sdhc, - + input sd_cs, input sd_sck, input sd_sdi, @@ -53,15 +57,23 @@ module sd_card ( // set io_rd once read_state machine starts waiting (rising edge of req_io_rd) // and clear it once io controller uploads something (io_ack==1) reg req_io_rd = 1'b0; // set when write_state is changed to RD_STATE_WAIT_IO -always @(posedge req_io_rd or posedge io_ack) begin - if(io_ack) io_rd <= 1'b0; - else io_rd <= 1'b1; -end reg req_io_wr = 1'b0; // set when write_state is changed to WR_STATE_BUSY -always @(posedge req_io_wr or posedge io_ack) begin - if(io_ack) io_wr <= 1'b0; - else io_wr <= 1'b1; + +always @(posedge clk) begin + reg old_io_ack; + reg req_io_rdD; + reg req_io_wrD; + + old_io_ack <= io_ack; + req_io_rdD <= req_io_rd; + req_io_wrD <= req_io_wr; + + if(~old_io_ack & io_ack) { io_rd, io_wr } <= 2'b00; + else begin + if (~req_io_rdD & req_io_rd) io_rd <= 1; + if (~req_io_wrD & req_io_wr) io_wr <= 1; + end end wire [31:0] OCR = { 1'b0, io_sdhc, 30'h0 }; // bit30 = 1 -> high capaciry card (sdhc) @@ -88,7 +100,6 @@ reg [2:0] write_state = WR_STATE_IDLE; reg card_is_reset = 1'b0; // flag that card has received a reset command reg [6:0] sbuf; reg cmd55; -reg new_cmd_rcvd; reg [7:0] cmd = 8'h00; reg [2:0] bit_cnt = 3'd0; // counts bits 0-7 0-7 ... reg [3:0] byte_cnt= 4'd15; // counts bytes @@ -104,118 +115,92 @@ reg [3:0] reply_len; // the sector buffer by the io controller. This signal is kept set as long // as the read state machine is in the "wait for io controller" state (state 1) wire rd_wait_io = (read_state != RD_STATE_IDLE); -reg rd_io_ack_i = 1'b0; -always @(negedge io_ack or negedge rd_wait_io) begin - if(!rd_wait_io) rd_io_ack_i <= 1'b0; - else rd_io_ack_i <= 1'b1; -end +reg rd_io_ack = 1'b0; wire wr_wait_io = (write_state == WR_STATE_BUSY); -reg wr_io_ack_i = 1'b0; -always @(negedge io_ack or negedge wr_wait_io) begin - if(!wr_wait_io) wr_io_ack_i <= 1'b0; - else wr_io_ack_i <= 1'b1; -end +reg wr_io_ack = 1'b0; -// bring xx_io_ack into sd cards clock domain -reg wr_io_ack; -reg rd_io_ack; -always @(posedge sd_sck) begin - rd_io_ack <= rd_io_ack_i; - wr_io_ack <= wr_io_ack_i; +always @(posedge clk) begin + reg old_io_ack; + old_io_ack <= io_ack; + + if(!wr_wait_io) wr_io_ack <= 1'b0; + else if (~io_ack & old_io_ack) begin + wr_io_ack <= 1'b1; + end + + if(!rd_wait_io) rd_io_ack <= 1'b0; + else if (~io_ack & old_io_ack) begin + rd_io_ack <= 1'b1; + end end // ------------------------- SECTOR BUFFER ----------------------- // the buffer itself. Can hold one sector -reg [7:0] buffer [511:0]; +reg [8:0] buffer_ptr; +wire [7:0] buffer_dout; +reg [7:0] buffer_din; +reg buffer_write_strobe; -// ---------------- buffer read engine ----------------------- -reg [8:0] buffer_rptr; -reg buffer_read_strobe; -wire buffer_dout_strobe = buffer_read_strobe || io_dout_strobe; -reg [7:0] buffer_dout; -assign io_dout = buffer_dout; +sd_card_dpram #(8, 9) buffer_dpram +( + .clock_a (clk), + .address_a (io_buff_addr), + .data_a (io_din), + .wren_a (io_din_strobe & io_ack), + .q_a (io_dout), -// buffer_rptr is increased in a diferent clock domain than it's -// evaluated. These single bit registers bring certain states from -// one domain into the other one in a safe (atomic) way -reg buffer_read_sector_done; -reg buffer_read_ciscid_done; - -always @(posedge buffer_dout_strobe or posedge new_cmd_rcvd) begin - if(new_cmd_rcvd == 1) begin - buffer_rptr <= 9'd0; - buffer_read_sector_done <= 1'b0; - buffer_read_ciscid_done <= 1'b0; - end else begin - buffer_dout <= buffer[buffer_rptr]; - buffer_rptr <= buffer_rptr + 9'd1; - if(buffer_rptr == 511) buffer_read_sector_done <= 1'b1; - if(buffer_rptr == 15) buffer_read_ciscid_done <= 1'b1; - end -end - -// ---------------- buffer write engine ----------------------- -reg [8:0] buffer_wptr; -reg buffer_write_strobe; -wire buffer_din_strobe = io_din_strobe || buffer_write_strobe; -wire [7:0] buffer_din = (cmd == 8'h51)?io_din:{sbuf, sd_sdi}; - -always @(posedge buffer_din_strobe or posedge new_cmd_rcvd) begin - if(new_cmd_rcvd == 1) - buffer_wptr <= 9'd0; - else begin - buffer[buffer_wptr] <= buffer_din; - buffer_wptr <= buffer_wptr + 9'd1; - end -end + .clock_b (clk), + .address_b (buffer_ptr), + .data_b (buffer_din), + .wren_b (buffer_write_strobe), + .q_b (buffer_dout) +); wire [7:0] WRITE_DATA_RESPONSE = 8'h05; // ------------------------- CSD/CID BUFFER ---------------------- -assign io_conf = (csd_wptr == 0); // csd_wptr still 0 -> configuration required +reg [7:0] conf; +assign io_conf = io_configuring; -// the 32 bytes as sent from the io controller -reg [7:0] cid [15:0]; -reg [7:0] csd [15:0]; -reg [7:0] conf; +reg io_configuring = 1; +reg [4:0] conf_buff_ptr; +wire [7:0] conf_byte; -reg [7:0] cid_byte; -reg [7:0] csd_byte; -reg [5:0] csd_wptr = 6'd0; +sd_card_dpram #(8, 6) conf_dpram +( + .clock_a (clk), + .address_a (io_buff_addr), + .data_a (io_din), + .wren_a (io_din_strobe & io_ack_conf), + + .clock_b (clk), + .address_b (conf_buff_ptr), + .q_b (conf_byte) +); // conf[0]==1 -> io controller is using an sdhc card wire io_has_sdhc = conf[0]; assign io_sdhc = allow_sdhc && io_has_sdhc; -always @(posedge io_din_strobe) begin - // if io controller sends data without asserting io_ack, then it's - // updating the config - if(!io_ack && (csd_wptr <= 32)) begin - - if(csd_wptr < 16) // first 16 bytes are cid - cid[csd_wptr[3:0]] <= io_din; - if((csd_wptr >= 16) && (csd_wptr < 32)) // then comes csd - csd[csd_wptr[3:0]] <= io_din; - if(csd_wptr == 32) // finally a config byte - conf <= io_din; - - csd_wptr <= csd_wptr + 6'd1; +always @(posedge clk) begin + if(io_din_strobe && io_ack_conf && io_buff_addr== 32) begin + conf <= io_din; + io_configuring <= 0; end end - -always @(posedge buffer_dout_strobe) begin - cid_byte <= cid[buffer_rptr[3:0]]; - csd_byte <= csd[buffer_rptr[3:0]]; -end - -// ----------------- spi transmitter -------------------- + +always@(posedge clk) begin + + reg illegal_write_state /* synthesis noprune */; + reg old_sd_sck; + old_sd_sck <= sd_sck; // advance transmitter state machine on falling sck edge, so data is valid on the // rising edge -always@(negedge sd_sck) begin - if(sd_cs == 0) begin - buffer_read_strobe <= 1'b0; +// ----------------- spi transmitter -------------------- + if(sd_cs == 0 && old_sd_sck && ~sd_sck) begin + sd_sdo <= 1'b1; // default: send 1's (busy/wait) req_io_rd <= 1'b0; @@ -257,8 +242,10 @@ always@(negedge sd_sck) begin // waiting for io controller to return data RD_STATE_WAIT_IO: begin + buffer_ptr <= 0; if(rd_io_ack && (bit_cnt == 7)) read_state <= RD_STATE_SEND_TOKEN; + end // send data token @@ -267,7 +254,7 @@ always@(negedge sd_sck) begin if(bit_cnt == 7) begin read_state <= RD_STATE_SEND_DATA; // next: send data - buffer_read_strobe <= 1'b1; // trigger read of first data byte + conf_buff_ptr <= (cmd == 8'h4a) ? 5'h0 : 5'h10; end end @@ -276,27 +263,29 @@ always@(negedge sd_sck) begin if(cmd == 8'h51) // CMD17: READ_SINGLE_BLOCK sd_sdo <= buffer_dout[~bit_cnt]; else if(cmd == 8'h49) // CMD9: SEND_CSD - sd_sdo <= csd_byte[~bit_cnt]; + sd_sdo <= conf_byte[~bit_cnt]; else if(cmd == 8'h4a) // CMD10: SEND_CID - sd_sdo <= cid_byte[~bit_cnt]; + sd_sdo <= conf_byte[~bit_cnt]; else sd_sdo <= 1'b1; if(bit_cnt == 7) begin // sent 512 sector data bytes? - if((cmd == 8'h51) && buffer_read_sector_done) // (buffer_rptr == 0)) + if((cmd == 8'h51) && &buffer_ptr) // (buffer_ptr ==511)) read_state <= RD_STATE_IDLE; // next: send crc. It's ignored so return to idle state - + // sent 16 cid/csd data bytes? - else if(((cmd == 8'h49)||(cmd == 8'h4a)) && buffer_read_ciscid_done) // && (buffer_rptr == 16)) + else if(((cmd == 8'h49)||(cmd == 8'h4a)) && conf_buff_ptr[3:0] == 4'h0f) // && (buffer_rptr == 16)) read_state <= RD_STATE_IDLE; // return to idle state - - else - buffer_read_strobe <= 1'b1; // not done yet -> trigger read of next data byte + + else begin + buffer_ptr <= buffer_ptr + 1'd1; + conf_buff_ptr<= conf_buff_ptr+ 1'd1; + end end end endcase - + // ------------------ write support ---------------------- // send write data response if(write_state == WR_STATE_SEND_DRESP) @@ -306,19 +295,18 @@ always@(negedge sd_sck) begin if(write_state == WR_STATE_BUSY) sd_sdo <= 1'b0; end -end -// spi receiver -reg illegal_write_state /* synthesis noprune */; + if (buffer_write_strobe) begin + buffer_write_strobe <= 1'b0; + buffer_ptr <= buffer_ptr + 1'd1; + end -always @(posedge sd_sck or posedge sd_cs) begin + // spi receiver // cs is active low if(sd_cs == 1) begin bit_cnt <= 3'd0; - end else begin + end else if (~old_sd_sck & sd_sck) begin illegal_write_state <= 1'b0; - new_cmd_rcvd <= 1'b0; - buffer_write_strobe <= 1'b0; req_io_wr <= 1'b0; bit_cnt <= bit_cnt + 3'd1; @@ -338,7 +326,6 @@ always @(posedge sd_sck or posedge sd_cs) begin (read_state == RD_STATE_IDLE) && sbuf[6:5] == 2'b01) begin byte_cnt <= 4'd0; cmd <= { sbuf, sd_sdi}; - new_cmd_rcvd <= 1'b1; // set cmd55 flag if previous command was 55 cmd55 <= (cmd == 8'h77); @@ -434,16 +421,19 @@ always @(posedge sd_sck or posedge sd_cs) begin // waiting for data token WR_STATE_EXP_DTOKEN: - if({ sbuf, sd_sdi} == 8'hfe ) + if({ sbuf, sd_sdi} == 8'hfe ) begin write_state <= WR_STATE_RECV_DATA; + buffer_ptr <= 9'd0; + end // transfer 512 bytes WR_STATE_RECV_DATA: begin // push one byte into local buffer buffer_write_strobe <= 1'b1; + buffer_din <= { sbuf, sd_sdi }; // all bytes written? - if(buffer_wptr == 511) + if(&buffer_ptr) write_state <= WR_STATE_RECV_CRC0; end @@ -465,7 +455,7 @@ always @(posedge sd_sck or posedge sd_cs) begin WR_STATE_BUSY: if(wr_io_ack) write_state <= WR_STATE_IDLE; - + default: illegal_write_state <= 1'b1; endcase @@ -474,3 +464,38 @@ always @(posedge sd_sck or posedge sd_cs) begin end endmodule + +module sd_card_dpram #(parameter DATAWIDTH=8, ADDRWIDTH=9) +( + input clock_a, + input [ADDRWIDTH-1:0] address_a, + input [DATAWIDTH-1:0] data_a, + input wren_a, + output reg [DATAWIDTH-1:0] q_a, + + input clock_b, + input [ADDRWIDTH-1:0] address_b, + input [DATAWIDTH-1:0] data_b, + input wren_b, + output reg [DATAWIDTH-1:0] q_b +); + +reg [DATAWIDTH-1:0] ram[0:(1<= 2) && (byte_cnt < 6)) - SPI_MISO <= sd_lba[{5-byte_cnt, ~bit_cnt}]; - else - SPI_MISO <= 1'b0; - end - - // reading sd card write data - else if(cmd == 8'h18) - SPI_MISO <= sd_din[~bit_cnt]; - - else - SPI_MISO <= 1'b0; - end - end -end +//wire [7:0] spi_sck_D = { spi_sck_D[6:0], SPI_CLK } /* synthesis keep */; +//wire spi_sck = (spi_sck && spi_sck_D != 8'h00) || (!spi_sck && spi_sck_D == 8'hff); +wire spi_sck = SPI_CLK; // ---------------- PS2 --------------------- @@ -307,108 +261,210 @@ always@(negedge spi_sck or posedge status[0]) begin end end -// SPI receiver -always@(posedge spi_sck or posedge SPI_SS_IO) begin +// SPI bit and byte counters +always@(posedge spi_sck or posedge SPI_SS_IO) begin if(SPI_SS_IO == 1) begin - bit_cnt <= 3'd0; - byte_cnt <= 8'd0; - sd_ack <= 1'b0; - sd_dout_strobe <= 1'b0; - sd_din_strobe <= 1'b0; + bit_cnt <= 0; + byte_cnt <= 0; end else begin - sd_dout_strobe <= 1'b0; - sd_din_strobe <= 1'b0; - - if(bit_cnt != 7) - sbuf[6:0] <= { sbuf[5:0], SPI_MOSI }; - - bit_cnt <= bit_cnt + 3'd1; if((bit_cnt == 7)&&(byte_cnt != 8'd255)) byte_cnt <= byte_cnt + 8'd1; - // finished reading command byte - if(bit_cnt == 7) begin - if(byte_cnt == 0) begin - cmd <= { sbuf, SPI_MOSI}; - - // fetch first byte when sectore FPGA->IO command has been seen - if({ sbuf, SPI_MOSI} == 8'h18) - sd_din_strobe <= 1'b1; - - if(({ sbuf, SPI_MOSI} == 8'h17) || ({ sbuf, SPI_MOSI} == 8'h18)) - sd_ack <= 1'b1; - - end else begin - - // buttons and switches - if(cmd == 8'h01) - but_sw <= { sbuf[3:0], SPI_MOSI }; + bit_cnt <= bit_cnt + 1'd1; + end +end - if(cmd == 8'h02) - joystick_0 <= { sbuf, SPI_MOSI }; - - if(cmd == 8'h03) - joystick_1 <= { sbuf, SPI_MOSI }; - - if(cmd == 8'h04) begin - // store incoming ps2 mouse bytes - ps2_mouse_fifo[ps2_mouse_wptr] <= { sbuf, SPI_MOSI }; - ps2_mouse_wptr <= ps2_mouse_wptr + 1; - end +// SPI transmitter FPGA -> IO +reg [7:0] spi_byte_out; - if(cmd == 8'h05) begin - // store incoming ps2 keyboard bytes - ps2_kbd_fifo[ps2_kbd_wptr] <= { sbuf, SPI_MOSI }; - ps2_kbd_wptr <= ps2_kbd_wptr + 1; - end - - if(cmd == 8'h15) - status <= { sbuf[6:0], SPI_MOSI }; - - // send sector IO -> FPGA - if(cmd == 8'h17) begin - // flag that download begins -// sd_dout <= { sbuf, SPI_MOSI}; - sd_dout_strobe <= 1'b1; - end - - // send sector FPGA -> IO - if(cmd == 8'h18) - sd_din_strobe <= 1'b1; - - // send SD config IO -> FPGA - if(cmd == 8'h19) begin - // flag that download begins -// sd_dout <= { sbuf, SPI_MOSI}; - // sd card knows data is config if sd_dout_strobe is asserted - // with sd_ack still being inactive (low) - sd_dout_strobe <= 1'b1; - end - - // joystick analog - if(cmd == 8'h1a) begin - // first byte is joystick indes - if(byte_cnt == 1) - stick_idx <= { sbuf[1:0], SPI_MOSI }; - else if(byte_cnt == 2) begin - // second byte is x axis - if(stick_idx == 0) - joystick_analog_0[15:8] <= { sbuf, SPI_MOSI }; - else if(stick_idx == 1) - joystick_analog_1[15:8] <= { sbuf, SPI_MOSI }; - end else if(byte_cnt == 3) begin - // third byte is y axis - if(stick_idx == 0) - joystick_analog_0[7:0] <= { sbuf, SPI_MOSI }; - else if(stick_idx == 1) - joystick_analog_1[7:0] <= { sbuf, SPI_MOSI }; - end - end +always@(negedge spi_sck or posedge SPI_SS_IO) begin + if(SPI_SS_IO == 1) begin + SPI_MISO <= 1'bZ; + end else begin + SPI_MISO <= spi_byte_out[~bit_cnt]; + end +end - end +always@(posedge spi_sck or posedge SPI_SS_IO) begin + reg [31:0] sd_lba_r; + + if(SPI_SS_IO == 1) begin + spi_byte_out <= core_type; + end else begin + // read the command byte to choose the response + if(bit_cnt == 7) begin + if(!byte_cnt) cmd <= {sbuf, SPI_MOSI}; + + spi_byte_out <= 0; + case({(!byte_cnt) ? {sbuf, SPI_MOSI} : cmd}) + // reading config string + 8'h14: if(byte_cnt < STRLEN) spi_byte_out <= conf_str[(STRLEN - byte_cnt - 1)<<3 +:8]; + + // reading sd card status + 8'h16: if(byte_cnt == 0) begin + spi_byte_out <= sd_cmd; + sd_lba_r <= sd_lba; + end + else if(byte_cnt < 5) spi_byte_out <= sd_lba_r[(4-byte_cnt)<<3 +:8]; + + // reading sd card write data + 8'h18: spi_byte_out <= sd_din; + 8'h1b: + // send alternating flag byte and data + if(byte_cnt[0]) spi_byte_out <= serial_out_status; + else spi_byte_out <= serial_out_byte; + endcase end end end - + +// SPI receiver IO -> FPGA + +reg spi_receiver_strobe_r; +reg spi_transfer_end_r; +reg [7:0] spi_byte_in_r; + +// Read at spi_sck clock domain, assemble bytes for transferring to clk_sys +always@(posedge spi_sck or posedge SPI_SS_IO) begin + + if(SPI_SS_IO == 1) begin + spi_receiver_strobe_r <= 0; + spi_transfer_end_r <= 1; + end else begin + spi_receiver_strobe_r <= 0; + spi_transfer_end_r <= 0; + + if(bit_cnt != 7) + sbuf[6:0] <= { sbuf[5:0], SPI_MOSI }; + + // finished reading a byte, prepare to transfer to clk_sys + if(bit_cnt == 7) begin + spi_byte_in_r <= { sbuf, SPI_MOSI}; + spi_receiver_strobe_r <= 1; + end + end +end + +// Process bytes from SPI at the clk_sys domain +always @(posedge clk_sys) begin + + reg spi_receiver_strobe; + reg spi_transfer_end; + reg [7:0] spi_byte_in; + reg spi_receiver_strobeD; + reg spi_transfer_endD; + reg [7:0] spi_byte_inD; + reg [7:0] acmd; + reg [7:0] abyte_cnt; // counts bytes + + //synchronize between SPI and sys clock domains + spi_receiver_strobeD <= spi_receiver_strobe_r; + spi_receiver_strobe <= spi_receiver_strobeD; + spi_transfer_endD <= spi_transfer_end_r; + spi_transfer_end <= spi_transfer_endD; + spi_byte_inD <= spi_byte_in_r; + spi_byte_in <= spi_byte_inD; + + if(sd_dout_strobe) begin + sd_dout_strobe<= 0; + if(~&sd_buff_addr) sd_buff_addr <= sd_buff_addr + 1'b1; + end + + if(sd_din_strobe) begin + sd_din_strobe<= 0; + if(~&sd_buff_addr) sd_buff_addr <= sd_buff_addr + 1'b1; + end + + if (~spi_transfer_endD & spi_transfer_end) begin + abyte_cnt <= 8'd0; + sd_ack <= 1'b0; + sd_ack_conf <= 1'b0; + sd_dout_strobe <= 1'b0; + sd_din_strobe <= 1'b0; + sd_buff_addr<= 0; + end else if (~spi_receiver_strobeD & spi_receiver_strobe) begin + + if(abyte_cnt != 8'd255) + abyte_cnt <= byte_cnt + 8'd1; + + if(abyte_cnt == 0) begin + acmd <= spi_byte_in; + + // fetch first byte when sectore FPGA->IO command has been seen + if(spi_byte_in == 8'h18) + sd_din_strobe <= 1'b1; + + if((spi_byte_in == 8'h17) || (spi_byte_in == 8'h18)) + sd_ack <= 1'b1; + + end else begin + + // buttons and switches + if(acmd == 8'h01) + but_sw <= spi_byte_in; + + if(acmd == 8'h02) + joystick_0 <= spi_byte_in; + + if(acmd == 8'h03) + joystick_1 <= spi_byte_in; + + if(acmd == 8'h04) begin + // store incoming ps2 mouse bytes + ps2_mouse_fifo[ps2_mouse_wptr] <= spi_byte_in; + ps2_mouse_wptr <= ps2_mouse_wptr + 1; + end + + if(acmd == 8'h05) begin + // store incoming ps2 keyboard bytes + ps2_kbd_fifo[ps2_kbd_wptr] <= spi_byte_in; + ps2_kbd_wptr <= ps2_kbd_wptr + 1; + end + + if(acmd == 8'h15) + status <= spi_byte_in; + + // send sector IO -> FPGA + if(acmd == 8'h17) begin + // flag that download begins + sd_dout_strobe <= 1'b1; + sd_dout <= spi_byte_in; + end + + // send sector FPGA -> IO + if(acmd == 8'h18) + sd_din_strobe <= 1'b1; + + // send SD config IO -> FPGA + if(acmd == 8'h19) begin + // flag that download begins + sd_dout_strobe <= 1'b1; + sd_ack_conf <= 1'b1; + sd_dout <= spi_byte_in; + end + + // joystick analog + if(acmd == 8'h1a) begin + // first byte is joystick indes + if(abyte_cnt == 1) + stick_idx <= spi_byte_in[2:0]; + else if(abyte_cnt == 2) begin + // second byte is x axis + if(stick_idx == 0) + joystick_analog_0[15:8] <= spi_byte_in; + else if(stick_idx == 1) + joystick_analog_1[15:8] <= spi_byte_in; + end else if(abyte_cnt == 3) begin + // third byte is y axis + if(stick_idx == 0) + joystick_analog_0[7:0] <= spi_byte_in; + else if(stick_idx == 1) + joystick_analog_1[7:0] <= spi_byte_in; + end + end + end + end +end + endmodule From 2638ce825968d4dc9f3b971f699a0b32082eb2f7 Mon Sep 17 00:00:00 2001 From: Gyorgy Szombathelyi Date: Tue, 21 Aug 2018 03:07:17 +0200 Subject: [PATCH 2/5] BBC: Update VIA --- cores/bbc/fpga/mist/bbc_mist_top.qsf | 2 +- cores/bbc/fpga/mist/bbc_mist_top.v | 50 +- cores/bbc/rtl/bbc.v | 67 +- cores/bbc/rtl/m6522.v | 1165 -------------------------- cores/bbc/rtl/m6522.vhd | 1015 ++++++++++++++++++++++ 5 files changed, 1088 insertions(+), 1211 deletions(-) delete mode 100755 cores/bbc/rtl/m6522.v create mode 100644 cores/bbc/rtl/m6522.vhd diff --git a/cores/bbc/fpga/mist/bbc_mist_top.qsf b/cores/bbc/fpga/mist/bbc_mist_top.qsf index d40c332..ec7ad0b 100644 --- a/cores/bbc/fpga/mist/bbc_mist_top.qsf +++ b/cores/bbc/fpga/mist/bbc_mist_top.qsf @@ -193,7 +193,7 @@ set_global_assignment -name VERILOG_FILE clockgen.v set_global_assignment -name VERILOG_FILE ../rtl/scandoubler.v set_global_assignment -name VERILOG_FILE ../../rtl/bbc.v set_global_assignment -name CDF_FILE output_files/Chain4.cdf -set_global_assignment -name VERILOG_FILE ../../rtl/m6522.v +set_global_assignment -name VHDL_FILE ../../rtl/m6522.vhd set_global_assignment -name QIP_FILE smmc.qip set_global_assignment -name QIP_FILE os12.qip set_global_assignment -name QIP_FILE basic2.qip diff --git a/cores/bbc/fpga/mist/bbc_mist_top.v b/cores/bbc/fpga/mist/bbc_mist_top.v index cf48ddf..4219765 100755 --- a/cores/bbc/fpga/mist/bbc_mist_top.v +++ b/cores/bbc/fpga/mist/bbc_mist_top.v @@ -208,31 +208,29 @@ user_io #(.STRLEN(CONF_STR_LEN)) user_io( ); // wire the sd card to the user port -wire sd_sck = user_via_pb_out[1]; -wire sd_cs = 1'b0; -wire sd_sdi = user_via_pb_out[0]; -wire sd_sdo = user_via_cb2_in; -assign user_via_cb1_in = user_via_pb_out[1]; +wire sd_sck; +wire sd_cs; +wire sd_sdi; +wire sd_sdo; -sd_card sd_card ( - // connection to io controller + sd_card sd_card ( + // connection to io controller .clk(clk_32m), - .io_lba (sd_lba ), - .io_rd (sd_rd), - .io_wr (sd_wr), - .io_ack (sd_ack), + .io_lba (sd_lba ), + .io_rd (sd_rd), + .io_wr (sd_wr), + .io_ack (sd_ack), .io_ack_conf (sd_ack_conf ), - .io_conf (sd_conf), - .io_sdhc (sd_sdhc), - .io_din (sd_dout), - .io_din_strobe (sd_dout_strobe), - .io_dout (sd_din), - .io_dout_strobe ( sd_din_strobe), + .io_conf (sd_conf), + .io_sdhc (sd_sdhc), + .io_din (sd_dout), + .io_din_strobe (sd_dout_strobe), + .io_dout (sd_din), + .io_dout_strobe ( sd_din_strobe), .io_buff_addr (sd_buff_addr ), - .allow_sdhc ( 1'b1), // SDHC not supported - - // connection to local CPU + + // connection to local CPU .sd_cs ( sd_cs ), .sd_sck ( sd_sck ), .sd_sdi ( sd_sdi ), @@ -325,11 +323,12 @@ bbc BBC( .VID_ADR ( vid_adr ), .VID_DI ( vid_data ), - .SHIFT ( autoboot_shift ), - - .user_via_pb_out ( user_via_pb_out ), - .user_via_cb1_in ( user_via_cb1_in ), - .user_via_cb2_in ( user_via_cb2_in ), + .SHIFT ( autoboot_shift ), + + .SDCLK (sd_sck ), + .SDSS (sd_cs ), + .SDMISO (sd_sdo ), + .SDMOSI (sd_sdi ), .joy_but ( { joystick_1[4], joystick_0[4] } ), .joy0_axis0 ( joystick_analog_0[15:8] ), @@ -467,7 +466,6 @@ wire sideways_ram = // status[2] is '1' of low mapping is selected in the menu wire basic_map = status[2]?(mem_romsel == 4'h0):(mem_romsel == 4'he); -//wire smmc_map = 0; wire smmc_map = status[2]?(mem_romsel == 4'h2):(mem_romsel == 4'hc); assign mem_di = diff --git a/cores/bbc/rtl/bbc.v b/cores/bbc/rtl/bbc.v index e5aea27..584091a 100644 --- a/cores/bbc/rtl/bbc.v +++ b/cores/bbc/rtl/bbc.v @@ -38,11 +38,11 @@ module bbc( // externally pressed "shift" key for autoboot input SHIFT, - - // expose pins required for mmc - output [7:0] user_via_pb_out, - input user_via_cb1_in, - input user_via_cb2_in, + + output SDSS, + output SDCLK, + output SDMOSI, + input SDMISO, // analog joystick input input [1:0] joy_but, @@ -176,7 +176,8 @@ wire [7:0] sound_di; wire [7:0] sound_ao; // System VIA signals -wire [7:0] sys_via_do; +wire [7:0] sys_via_do; +reg [7:0] sys_via_do_r; wire sys_via_do_oe_n; wire sys_via_irq_n; wire sys_via_ca1_in; @@ -197,7 +198,8 @@ wire [7:0] sys_via_pb_out; wire [7:0] sys_via_pb_oe_n; // User VIA signals -wire [7:0] user_via_do; +wire [7:0] user_via_do; +reg [7:0] user_via_do_r; wire user_via_do_oe_n; wire user_via_irq_n; reg user_via_ca1_in; @@ -207,16 +209,28 @@ wire user_via_ca2_oe_n; wire [7:0] user_via_pa_in; wire [7:0] user_via_pa_out; wire [7:0] user_via_pa_oe_n; -//TH wire user_via_cb1_in; +wire user_via_cb1_in; wire user_via_cb1_out; wire user_via_cb1_oe_n; -//TH wire user_via_cb2_in; +wire user_via_cb2_in; wire user_via_cb2_out; wire user_via_cb2_oe_n; wire [7:0] user_via_pb_in; -//TH wire [7:0] user_via_pb_out; +wire [7:0] user_via_pb_out; wire [7:0] user_via_pb_oe_n; +// MMC +// SDCLK is driven from either PB1 or CB1 depending on the SR Mode +wire sdclk_int = ~user_via_pb_oe_n[1] ? user_via_pb_out[1] : + (~user_via_cb1_oe_n ? user_via_cb1_out : 1); +assign SDCLK = sdclk_int; +assign user_via_cb1_in = sdclk_int; +// SDMOSI is always driven from PB0 +assign SDMOSI = ~user_via_pb_oe_n[0] ? user_via_pb_out[0] : 1; +// SDMISO is always read from CB2 +assign user_via_cb2_in = SDMISO; +assign SDSS = 0; + // calulation for display address reg [3:0] process_3_aa; @@ -297,38 +311,44 @@ cpu CPU ( assign cpu_r_nw = ~cpu_we; - m6522 SYS_VIA ( // System VIA is reset by power on reset only - .ENA_4(mhz4_clken), - .CLK(CLK32M_I), + .I_RS(cpu_a[3:0]), .I_DATA(cpu_do), .O_DATA(sys_via_do), .O_DATA_OE_L(sys_via_do_oe_n), + .I_RW_L(cpu_r_nw), .I_CS1(sys_via_enable), .I_CS2_L(1'b 0), // nCS2(1'b 0), .O_IRQ_L(sys_via_irq_n), - .I_P2_H(mhz1_clken), - .RESET_L(reset_n), - + .I_CA1(sys_via_ca1_in), .I_CA2(sys_via_ca2_in), .O_CA2(sys_via_ca2_out), .O_CA2_OE_L(sys_via_ca2_oe_n), + .I_PA(sys_via_pa_in), .O_PA(sys_via_pa_out), .O_PA_OE_L(sys_via_pa_oe_n), + .I_CB1(sys_via_cb1_in), .O_CB1(sys_via_cb1_out), .O_CB1_OE_L(sys_via_cb1_oe_n), + .I_CB2(sys_via_cb2_in), .O_CB2(sys_via_cb2_out), .O_CB2_OE_L(sys_via_cb2_oe_n), + .I_PB(sys_via_pb_in), .O_PB(sys_via_pb_out), - .O_PB_OE_L(sys_via_pb_oe_n) + .O_PB_OE_L(sys_via_pb_oe_n), + + .I_P2_H(mhz1_clken), + .RESET_L(reset_n), + .ENA_4(mhz4_clken), + .CLK(CLK32M_I) ); m6522 USER_VIA ( @@ -490,6 +510,15 @@ initial begin : via_init end +// This is needed as in v003 of the 6522 data out is only valid while I_P2_H is asserted +// I_P2_H is driven from mhz1_clken +always @(posedge CLK32M_I) begin + if (mhz1_clken) begin + user_via_do_r <= user_via_do; + sys_via_do_r <= sys_via_do; + end +end + // rom select latch always @(posedge CLK32M_I) begin @@ -619,8 +648,8 @@ assign cpu_di = ram_enable === 1'b 1 ? MEM_DI : mos_enable === 1'b 1 ? MEM_DI : crtc_enable === 1'b 1 ? crtc_do : acia_enable === 1'b 1 ? 8'b 00000010 : - sys_via_enable === 1'b 1 ? sys_via_do : - user_via_enable === 1'b 1 ? user_via_do : + sys_via_enable === 1'b 1 ? sys_via_do_r : + user_via_enable === 1'b 1 ? user_via_do_r : adc_enable === 1'b 1 ? adc_do : //tube_enable === 1'b 1 ? tube_do : //adlc_enable === 1'b 1 ? bbcddr_out : diff --git a/cores/bbc/rtl/m6522.v b/cores/bbc/rtl/m6522.v deleted file mode 100755 index f3b6a5c..0000000 --- a/cores/bbc/rtl/m6522.v +++ /dev/null @@ -1,1165 +0,0 @@ -`timescale 1 ns / 1 ns // timescale for following modules -// -// A simulation model of VIC20 hardware -// Copyright (c) MikeJ - March 2003 -// -// All rights reserved -// -// Redistribution and use in source and synthezised forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// Redistributions in synthesized form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// Neither the name of the author nor the names of other contributors may -// be used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// You are responsible for any legal issues arising from your use of this code. -// -// The latest version of this file can be found at: www.fpgaarcade.com -// -// Email vic20@fpgaarcade.com -// -// -// Revision list -// -// version 004 fixes to PB7 T1 control and Mode 0 Shift Register operation -// version 003 fix reset of T1/T2 IFR flags if T1/T2 is reload via reg5/reg9 from wolfgang (WoS) -// Ported to numeric_std and simulation fix for signal initializations from arnim laeuger -// version 002 fix from Mark McDougall, untested -// version 001 initial release -// not very sure about the shift register, documentation is a bit light. - -module m6522 ( - I_RS, - I_DATA, - O_DATA, - O_DATA_OE_L, - I_RW_L, - I_CS1, - I_CS2_L, - O_IRQ_L, - I_CA1, - I_CA2, - O_CA2, - O_CA2_OE_L, - I_PA, - O_PA, - O_PA_OE_L, - I_CB1, - O_CB1, - O_CB1_OE_L, - I_CB2, - O_CB2, - O_CB2_OE_L, - I_PB, - O_PB, - O_PB_OE_L, - I_P2_H, - RESET_L, - ENA_4, - CLK); - - -input [3:0] I_RS; -input [7:0] I_DATA; -output reg [7:0] O_DATA; -output O_DATA_OE_L; -input I_RW_L; -input I_CS1; -input I_CS2_L; -output O_IRQ_L; -input I_CA1; -input I_CA2; -output O_CA2; -output O_CA2_OE_L; -input [7:0] I_PA; -output [7:0] O_PA; -output [7:0] O_PA_OE_L; -input I_CB1; -output O_CB1; -output O_CB1_OE_L; -input I_CB2; -output O_CB2; -output O_CB2_OE_L; -input [7:0] I_PB; -output [7:0] O_PB; -output [7:0] O_PB_OE_L; -input I_P2_H; -input RESET_L; -input ENA_4; -input CLK; - - -reg O_CA2; -reg O_CA2_OE_L; - - -// port b -wire O_CB1; -wire O_CB1_OE_L; -reg O_CB2; -reg O_CB2_OE_L; -reg [1:0] phase = 2'b00; -reg p2_h_t1; -wire cs; - -// registers -reg [7:0] r_ddra; -reg [7:0] r_ora; -reg [7:0] r_ira; -reg [7:0] r_ddrb; -reg [7:0] r_orb; -reg [7:0] r_irb; -reg [7:0] r_t1l_l; -reg [7:0] r_t1l_h; -reg [7:0] r_t2l_l; -reg [7:0] r_t2l_h; -// not in real chip -reg [7:0] r_sr; -reg [7:0] r_acr; -reg [7:0] r_pcr; -wire [7:0] r_ifr; -reg [6:0] r_ier; -reg sr_write_ena; -reg sr_read_ena; -reg ifr_write_ena; -reg ier_write_ena; -wire [7:0] clear_irq; -reg [7:0] load_data; - -// timer 1 -reg [15:0] t1c; -reg t1c_active; -reg t1c_done; -reg t1_w_reset_int; -reg t1_r_reset_int; -reg t1_load_counter; -reg t1_reload_counter; -reg t1_toggle; -reg t1_irq; - -// timer 2 -reg [15:0] t2c; -reg t2c_active; -reg t2c_done; -reg t2_pb6; -reg t2_pb6_t1; -reg t2_w_reset_int; -reg t2_r_reset_int; -reg t2_load_counter; -reg t2_reload_counter; -reg t2_irq; -reg t2_sr_ena; - -// shift reg -reg [3:0] sr_cnt; -reg sr_cb1_oe_l; -reg sr_cb1_out; -reg sr_drive_cb2; -reg sr_strobe; -reg sr_strobe_t1; -reg sr_strobe_falling; -reg sr_strobe_rising; -reg sr_irq; -reg sr_out; -reg sr_off_delay; - -// io -reg w_orb_hs; -reg w_ora_hs; -reg r_irb_hs; -reg r_ira_hs; -reg ca_hs_sr; -reg ca_hs_pulse; -reg cb_hs_sr; -reg cb_hs_pulse; - -reg ca1_ip_reg; -reg cb1_ip_reg; -wire ca1_int; -wire cb1_int; -reg ca1_irq; -reg cb1_irq; -reg ca2_ip_reg; -reg cb2_ip_reg; -wire ca2_int; -wire cb2_int; -reg ca2_irq; -reg cb2_irq; -reg final_irq; -reg p_timer2_ena; -reg p_sr_dir_out; -reg p_sr_ena; -reg p_sr_cb1_op; -reg p_sr_cb1_ip; -reg p_sr_use_t2; -reg p_sr_free_run; -reg p_sr_sr_count_ena; - -initial begin - t2_irq = 1'b 0; -end - -initial begin - t1_irq = 1'b 0; -end - - -always @(posedge CLK) begin - - if (ENA_4 === 1'b 1) begin - p2_h_t1 <= I_P2_H; - - if (p2_h_t1 === 1'b 0 & I_P2_H === 1'b 1) begin - phase <= 2'b 11; - end - else begin - phase <= phase + 1'b 1; - end - end -end - - -// internal clock phase -assign cs = (I_CS1 === 1'b 1 & I_CS2_L === 1'b 0 & I_P2_H === 1'b 1) ? 1'b1 : 1'b0; - -// peripheral control reg (pcr) -// 0 ca1 interrupt control (0 +ve edge, 1 -ve edge) -// 3..1 ca2 operation -// 000 input -ve edge -// 001 independend interrupt input -ve edge -// 010 input +ve edge -// 011 independend interrupt input +ve edge -// 100 handshake output -// 101 pulse output -// 110 low output -// 111 high output -// 7..4 as 3..0 for cb1,cb2 -// auxiliary control reg (acr) -// 0 input latch PA (0 disable, 1 enable) -// 1 input latch PB (0 disable, 1 enable) -// 4..2 shift reg control -// 000 disable -// 001 shift in using t2 -// 010 shift in using o2 -// 011 shift in using ext clk -// 100 shift out free running t2 rate -// 101 shift out using t2 -// 101 shift out using o2 -// 101 shift out using ext clk -// 5 t2 timer control (0 timed interrupt, 1 count down with pulses on pb6) -// 7..6 t1 timer control -// 00 timed interrupt each time t1 is loaded pb7 disable -// 01 continuous interrupts pb7 disable -// 00 timed interrupt each time t1 is loaded pb7 one shot output -// 01 continuous interrupts pb7 square wave output -// - -always @(posedge CLK) begin - - if (RESET_L === 1'b 0) begin - r_ora <= 8'h 00; - r_orb <= 8'h 00; - r_ddra <= 8'h 00; - r_ddrb <= 8'h 00; - r_acr <= 8'h 00; - r_pcr <= 8'h 00; - w_orb_hs <= 1'b 0; - w_ora_hs <= 1'b 0; - end - else begin - if (ENA_4 === 1'b 1) begin - w_orb_hs <= 1'b 0; - w_ora_hs <= 1'b 0; - - if (cs === 1'b 1 & I_RW_L === 1'b 0) begin - case (I_RS) - 4'h 0: begin - r_orb <= I_DATA; - w_orb_hs <= 1'b 1; - end - - 4'h 1: begin - r_ora <= I_DATA; - w_ora_hs <= 1'b 1; - end - - 4'h 2: begin - r_ddrb <= I_DATA; - end - - 4'h 3: begin - r_ddra <= I_DATA; - end - - 4'h B: begin - r_acr <= I_DATA; - end - - 4'h C: begin - r_pcr <= I_DATA; - end - - 4'h F: begin - r_ora <= I_DATA; - end - - default: - ; - - endcase - end - - if (r_acr[7] === 1'b1) begin - // DMB: Forgetting to clear B7 broke Acornsoft Planetoid - if(t1_load_counter) - r_orb[7] <= 1'b0; // writing T1C-H resets bit 7 - else if (t1_toggle === 1'b1) - r_orb[7] <= ~r_orb[7]; // toggle - end - end - end -end - - -always @(posedge CLK) begin - - if (RESET_L === 1'b 0) begin - // The spec says, this is not reset. - // Fact is that the 1541 VIA1 timer won't work, - // as the firmware ONLY sets the r_t1l_h latch!!!! - // DMB: These are observed power on values from the Beeb - // DMB: Note, it's a little overzeleaous setting these on reset; that doesn't happen in reality - r_t1l_l <= 8'h FE; - r_t1l_h <= 8'h FF; - r_t2l_l <= 8'h FE; - r_t2l_h <= 8'h FF; - end - else begin - - if (ENA_4 === 1'b 1) begin - t1_w_reset_int <= 1'b0; - t1_load_counter <= 1'b0; - t2_w_reset_int <= 1'b0; - t2_load_counter <= 1'b0; - load_data <= 8'h 00; - sr_write_ena <= 1'b0; - ifr_write_ena <= 1'b0; - ier_write_ena <= 1'b0; - - if (cs === 1'b 1 & I_RW_L === 1'b 0) begin - load_data <= I_DATA; - - case (I_RS) - 4'h 4: begin - r_t1l_l <= I_DATA; - end - - 4'h 5: begin - r_t1l_h <= I_DATA; - t1_w_reset_int <= 1'b1; - t1_load_counter <= 1'b1; - end - - 4'h 6: begin - r_t1l_l <= I_DATA; - end - - 4'h 7: begin - r_t1l_h <= I_DATA; - t1_w_reset_int <= 1'b1; - end - - 4'h 8: begin - r_t2l_l <= I_DATA; - end - - 4'h 9: begin - r_t2l_h <= I_DATA; - t2_w_reset_int <= 1'b1; - t2_load_counter <= 1'b1; - end - - 4'h A: begin - sr_write_ena <= 1'b1; - end - - 4'h D: begin - ifr_write_ena <= 1'b1; - end - - 4'h E: begin - ier_write_ena <= 1'b1; - end - - default: - ; - - endcase - end - end - end -end - - -assign O_DATA_OE_L = (cs === 1'b 1 & I_RW_L === 1'b 1) ? 1'b0 : 1'b1; - - -always @(posedge CLK) begin - - if (ENA_4 === 1'b 1) begin - - t1_r_reset_int <= 1'b0; - t2_r_reset_int <= 1'b0; - sr_read_ena <= 1'b0; - r_irb_hs <= 1'b 0; - r_ira_hs <= 1'b 0; - O_DATA <= 8'h 00; // default - - if (cs === 1'b 1 & I_RW_L === 1'b 1) begin - case (I_RS) - // when x"0" => O_DATA <= r_irb; r_irb_hs <= '1'; - // fix from Mark McDougall, untested - 4'h 0: begin - O_DATA <= r_irb & ~r_ddrb | r_orb & r_ddrb; - r_irb_hs <= 1'b 1; - end - - 4'h 1: begin - O_DATA <= r_ira; - r_ira_hs <= 1'b 1; - end - - 4'h 2: begin - O_DATA <= r_ddrb; - end - - 4'h 3: begin - O_DATA <= r_ddra; - end - - 4'h 4: begin - O_DATA <= t1c[7:0]; - t1_r_reset_int <= 1'b1; - end - - 4'h 5: begin - O_DATA <= t1c[15:8]; - end - - 4'h 6: begin - O_DATA <= r_t1l_l; - end - - 4'h 7: begin - O_DATA <= r_t1l_h; - end - - 4'h 8: begin - O_DATA <= t2c[7:0]; - t2_r_reset_int <= 1'b1; - end - - 4'h 9: begin - O_DATA <= t2c[15:8]; - end - - 4'h A: begin - O_DATA <= r_sr; - sr_read_ena <= 1'b1; - end - - 4'h B: begin - O_DATA <= r_acr; - end - - 4'h C: begin - O_DATA <= r_pcr; - end - - 4'h D: begin - O_DATA <= r_ifr; - end - - 4'h E: begin - O_DATA <= {1'b 1, r_ier}; - end - - 4'h F: begin - O_DATA <= r_ira; - end - - default: - ; - - endcase - end - end -end - -// -// IO -// - -// if the shift register is enabled, cb1 may be an output -// in this case, we should listen to the CB1_OUT for the interrupt - -assign cb1_in_mux = (sr_cb1_oe_l === 1'b 1) ? I_CB1 : sr_cb1_out; - - -// ca1 control -assign ca1_int = (r_pcr[0] === 1'b 0) ? (ca1_ip_reg == 1'b1 & I_CA1 == 1'b0) : // negative edge - (ca1_ip_reg == 1'b0 & I_CA1 == 1'b1); // positive edge - - -// cb1 control -assign cb1_int = (r_pcr[4] === 1'b 0) ? (cb1_ip_reg == 1'b1 & cb1_in_mux == 1'b0) : // negative edge - (cb1_ip_reg == 1'b0 & cb1_in_mux == 1'b1); - - -assign ca2_int = (r_pcr[3] === 1'b 1) ? 1'b0 : - (r_pcr[2] === 1'b 1) ? (ca2_ip_reg == 1'b 0 & I_CA2 == 1'b 1): - (ca2_ip_reg == 1'b 1 & I_CA2 == 1'b 0); - -assign cb2_int = (r_pcr[7] === 1'b 1) ? 1'b0 : - (r_pcr[6] === 1'b 1) ? (cb2_ip_reg == 1'b 0 & I_CB2 == 1'b 1): - (cb2_ip_reg == 1'b 1 & I_CB2 == 1'b 0); - - -always @(posedge CLK) begin - - if (RESET_L === 1'b 0) begin - O_CA2 <= 1'b 0; - O_CA2_OE_L <= 1'b 1; - O_CB2 <= 1'b 0; - O_CB2_OE_L <= 1'b 1; - ca_hs_sr <= 1'b 0; - ca_hs_pulse <= 1'b 0; - cb_hs_sr <= 1'b 0; - cb_hs_pulse <= 1'b 0; - end - else begin - if (ENA_4 === 1'b 1) begin - - // ca - if (phase === 2'b 00 & (w_ora_hs === 1'b 1 | - r_ira_hs === 1'b 1)) begin - ca_hs_sr <= 1'b 1; - end - else if (ca1_int ) begin - ca_hs_sr <= 1'b 0; - end - - if (phase === 2'b 00) begin - ca_hs_pulse <= w_ora_hs | r_ira_hs; - end - - O_CA2_OE_L <= ~r_pcr[3]; - // ca2 output - case (r_pcr[3:1]) - 3'b 000: begin - O_CA2 <= 1'b 0; - // input - end - - 3'b 001: begin - O_CA2 <= 1'b 0; - // input - end - - 3'b 010: begin - O_CA2 <= 1'b 0; - // input - end - - 3'b 011: begin - O_CA2 <= 1'b 0; - // input - end - - 3'b 100: begin - O_CA2 <= ~ca_hs_sr; - // handshake - end - - 3'b 101: begin - O_CA2 <= ~ca_hs_pulse; - // pulse - end - - 3'b 110: begin - O_CA2 <= 1'b 0; - // low - end - - 3'b 111: begin - O_CA2 <= 1'b 1; - // high - end - - default: - ; - - endcase - if (phase === 2'b 00 & w_orb_hs === 1'b 1) begin - cb_hs_sr <= 1'b 1; - // cb - end - else if (cb1_int ) begin - cb_hs_sr <= 1'b 0; - end - - if (phase === 2'b 00) begin - cb_hs_pulse <= w_orb_hs; - end - - O_CB2_OE_L <= ~(r_pcr[7] | sr_drive_cb2); - // cb2 output or serial - if (sr_drive_cb2 === 1'b 1) begin - - // serial output - O_CB2 <= sr_out; - end - else begin - case (r_pcr[7:5]) - 3'b 000: begin - O_CB2 <= 1'b 0; - // input - end - - 3'b 001: begin - O_CB2 <= 1'b 0; - // input - end - - 3'b 010: begin - O_CB2 <= 1'b 0; - // input - end - - 3'b 011: begin - O_CB2 <= 1'b 0; - // input - end - - 3'b 100: begin - O_CB2 <= ~cb_hs_sr; - // handshake - end - - 3'b 101: begin - O_CB2 <= ~cb_hs_pulse; - // pulse - end - - 3'b 110: begin - O_CB2 <= 1'b 0; - // low - end - - 3'b 111: begin - O_CB2 <= 1'b 1; - // high - end - - default: - ; - - endcase - end - end - end -end - -assign O_CB1 = sr_cb1_out; -assign O_CB1_OE_L = sr_cb1_oe_l; - -always @(posedge CLK) begin - - if (RESET_L === 1'b 0) begin - ca1_irq <= 1'b 0; - ca2_irq <= 1'b 0; - cb1_irq <= 1'b 0; - cb2_irq <= 1'b 0; - end - else begin - if (ENA_4 === 1'b 1) begin - - // not pretty - if (ca1_int) begin - ca1_irq <= 1'b 1; - end - else if (r_ira_hs === 1'b 1 | w_ora_hs === 1'b 1 | - clear_irq[1] === 1'b 1 ) begin - ca1_irq <= 1'b 0; - end - - if (ca2_int) begin - ca2_irq <= 1'b 1; - end - else begin - if ((r_ira_hs === 1'b 1 | w_ora_hs === 1'b 1) & - r_pcr[1] === 1'b 0 | clear_irq[0] === 1'b 1) begin - ca2_irq <= 1'b 0; - end - end - - if (cb1_int) begin - cb1_irq <= 1'b 1; - end - else if (r_irb_hs === 1'b 1 | w_orb_hs === 1'b 1 | - clear_irq[4] === 1'b 1 ) begin - cb1_irq <= 1'b 0; - end - - if (cb2_int) begin - cb2_irq <= 1'b 1; - end - else begin - if ((r_irb_hs === 1'b 1 | w_orb_hs === 1'b 1) & - r_pcr[5] === 1'b 0 | clear_irq[3] === 1'b 1) begin - cb2_irq <= 1'b 0; - end - end - end - end -end - - -always @(posedge CLK) begin - - if (RESET_L === 1'b 0) begin - ca1_ip_reg <= 1'b 0; - cb1_ip_reg <= 1'b 0; - ca2_ip_reg <= 1'b 0; - cb2_ip_reg <= 1'b 0; - r_ira <= 8'h 00; - r_irb <= 8'h 00; - end - else begin - if (ENA_4 === 1'b 1) begin - - // we have a fast clock, so we can have input registers - ca1_ip_reg <= I_CA1; - cb1_ip_reg <= cb1_in_mux; - ca2_ip_reg <= I_CA2; - cb2_ip_reg <= I_CB2; - - if (r_acr[0] === 1'b 0) begin - r_ira <= I_PA; - - // enable latching - end - else begin - if (ca1_int) begin - r_ira <= I_PA; - end - end - - if (r_acr[1] === 1'b 0) begin - r_irb <= I_PB; - - // enable latching - end - else begin - if (cb1_int) begin - r_irb <= I_PB; - end - end - end - end -end - -// data direction reg (ddr) 0 = input, 1 = output - -assign O_PA = r_ora; -assign O_PA_OE_L = ~r_ddra; - -assign O_PB_OE_L[6:0] = ~r_ddrb[6:0]; -// not clear if r_ddrb(7) must be 1 as well, an output if under t1 control -assign O_PB_OE_L[7] = (r_acr[7] === 1'b 1) ? 1'b 0 : ~r_ddrb[7]; - -assign O_PB[7:0] = r_orb[7:0]; - -// -// Timer 1 -// - -reg p_timer1_done_done; -always @(posedge CLK) begin - - if (ENA_4 === 1'b1) begin - p_timer1_done_done = t1c == 16'h 0000; - t1c_done <= p_timer1_done_done & phase == 2'b 11; - if (phase === 2'b 11) begin - t1_reload_counter <= p_timer1_done_done & r_acr[6] == 1'b1; - end - if (t1_load_counter ) begin - t1c_done <= 1'b0; - end - end -end - - -always @(posedge CLK) begin - - if (ENA_4 === 1'b 1) begin - if (t1_load_counter | t1_reload_counter & phase === 2'b 11) begin - t1c[7:0] <= r_t1l_l; - t1c[15:8] <= r_t1l_h; - end - else if (phase === 2'b 11 ) begin - t1c <= t1c - 1'b 1; - end - - if (t1_load_counter | t1_reload_counter) begin - t1c_active <= 1'b1; - end - else if (t1c_done ) begin - t1c_active <= 1'b0; - end - - t1_toggle <= 1'b 0; - - if (t1c_active & t1c_done) begin - t1_toggle <= 1'b 1; - t1_irq <= 1'b 1; - end - else if (t1_w_reset_int | t1_r_reset_int | clear_irq[6] === 1'b 1 ) begin - t1_irq <= 1'b 0; - end - - if (t1_load_counter) begin // irq reset on load! - t1_irq <= 1'b 0; - end - end -end - -// -// Timer2 -// - -always @(posedge CLK) begin - - if (ENA_4 === 1'b 1) begin - if (phase === 2'b 01) begin - - // leading edge p2_h - t2_pb6 <= I_PB[6]; - t2_pb6_t1 <= t2_pb6; - end - end -end - -reg p_timer2_done_done; -always @(posedge CLK) begin - - if (ENA_4 === 1'b1) begin - p_timer2_done_done = t2c == 16'h 0000; - t2c_done <= p_timer2_done_done & phase == 2'b 11; - if (phase === 2'b 11) begin - t2_reload_counter <= p_timer2_done_done; - end - if (t2_load_counter ) begin // done reset on load! - t2c_done <= 1'b0; - end - end -end - - -always @(posedge CLK) begin - - if (ENA_4 === 1'b 1) begin - if (r_acr[5] === 1'b 0) begin - p_timer2_ena = 1'b1; - end - else begin - p_timer2_ena = t2_pb6_t1 == 1'b 1 & t2_pb6 == 1'b 0; - // falling edge - end - - if (t2_load_counter | t2_reload_counter & phase === 2'b 11) begin - - // not sure if t2c_reload should be here. Does timer2 just continue to - // count down, or is it reloaded ? Reloaded makes more sense if using - // it to generate a clock for the shift register. - t2c[7:0] <= r_t2l_l; - t2c[15:8] <= r_t2l_h; - end - else begin - if (phase === 2'b 11 & p_timer2_ena) begin - - // or count mode - t2c <= t2c - 1'b 1; - end - end - - t2_sr_ena <= t2c[7:0] == 8'h 00 & phase == 2'b 11; - - if (t2_load_counter) begin - t2c_active <= 1'b1; - end - else if (t2c_done ) begin - t2c_active <= 1'b0; - end - - if (t2c_active & t2c_done) begin - t2_irq <= 1'b 1; - end - else if (t2_w_reset_int | t2_r_reset_int | clear_irq[5] === 1'b 1 ) begin - t2_irq <= 1'b 0; - end - - if (t2_load_counter) begin // irq reset on load! - t2_irq <= 1'b 0; - end - - end -end - -// -// Shift Register -// - -always @(posedge CLK) begin - - if (RESET_L === 1'b 0) begin - r_sr <= 8'h 00; - sr_drive_cb2 <= 1'b 0; - sr_cb1_oe_l <= 1'b 1; - sr_cb1_out <= 1'b 0; - sr_strobe <= 1'b 1; - sr_cnt <= 4'b 0000; - sr_irq <= 1'b 0; - sr_out <= 1'b 1; - sr_off_delay <= 1'b 0; - end - else begin - if (ENA_4 === 1'b 1) begin - - // decode mode - p_sr_dir_out = r_acr[4]; - // output on cb2 - p_sr_cb1_op = 1'b 0; - p_sr_cb1_ip = 1'b 0; - p_sr_use_t2 = 1'b 0; - p_sr_free_run = 1'b 0; - - case (r_acr[4:2]) - 3'b 000: begin - p_sr_ena = 1'b 0; - p_sr_cb1_ip = 1'b 1; - end - - 3'b 001: begin - p_sr_ena = 1'b 1; - p_sr_cb1_op = 1'b 1; - p_sr_use_t2 = 1'b 1; - end - - 3'b 010: begin - p_sr_ena = 1'b 1; - p_sr_cb1_op = 1'b 1; - end - - 3'b 011: begin - p_sr_ena = 1'b 1; - p_sr_cb1_ip = 1'b 1; - end - - 3'b 100: begin - p_sr_ena = 1'b 1; - p_sr_use_t2 = 1'b 1; - p_sr_free_run = 1'b 1; - end - - 3'b 101: begin - p_sr_ena = 1'b 1; - p_sr_cb1_op = 1'b 1; - p_sr_use_t2 = 1'b 1; - end - - 3'b 110: begin - p_sr_ena = 1'b 1; - end - - 3'b 111: begin - p_sr_ena = 1'b 1; - p_sr_cb1_ip = 1'b 1; - end - - default: - ; - - endcase - if (p_sr_cb1_ip === 1'b 1) begin - sr_strobe <= I_CB1; - // clock select - // SR still runs even in disabled mode (on rising edge of CB1). It - // just doesn't generate any interrupts. - // Ref BBC micro advanced user guide p409 - end - else begin - if (sr_cnt[3] === 1'b 0 & p_sr_free_run === 1'b 0) begin - sr_strobe <= 1'b 1; - end - else begin - if (p_sr_use_t2 === 1'b 1 & t2_sr_ena | p_sr_use_t2 === - 1'b 0 & phase === 2'b 00) begin - sr_strobe <= ~sr_strobe; - end - end - end - - // latch on rising edge, shift on falling edge - if (sr_write_ena) begin - r_sr <= load_data; - end - else begin - if (p_sr_dir_out === 1'b 0) begin - - // input - if (sr_cnt[3] === 1'b 1 | p_sr_cb1_ip === 1'b 1) begin - if (sr_strobe_rising) begin - r_sr[0] <= I_CB2; - end - else if (sr_strobe_falling) begin - r_sr[7:1] <= r_sr[6:0]; - end - end - - sr_out <= 1'b 1; - - // output - end - else begin - if (sr_cnt[3] === 1'b 1 | sr_off_delay === 1'b 1 | - p_sr_cb1_ip === 1'b 1 | p_sr_free_run === 1'b 1) begin - if (sr_strobe_falling) begin - r_sr[7:1] <= r_sr[6:0]; - r_sr[0] <= r_sr[7]; - sr_out <= r_sr[7]; - end - end - else begin - sr_out <= 1'b 1; - end - end - end - - p_sr_sr_count_ena = sr_strobe_rising; - - // DMB: reseting sr_count when not enabled cause the sr to - // start running immediately it was enabled, which is incorrect - // and broke the latest SmartSPI ROM on the BBC Micro - if (p_sr_ena & (sr_write_ena | sr_read_ena)) begin - - // some documentation says sr bit in IFR must be set as well ? - sr_cnt <= 4'b 1000; - end - else if (p_sr_sr_count_ena & sr_cnt[3] === 1'b 1 ) begin - sr_cnt <= sr_cnt + 1'b 1; - end - - if (phase === 2'b 00) begin - sr_off_delay <= sr_cnt[3]; - // give some hold time when shifting out - end - - if (p_sr_sr_count_ena & sr_cnt === 4'b 1111 & p_sr_ena === - 1'b 1 & p_sr_free_run === 1'b 0) begin - sr_irq <= 1'b 1; - end - else if (sr_write_ena | sr_read_ena | clear_irq[2] === 1'b 1 ) begin - sr_irq <= 1'b 0; - end - - // assign ops - sr_drive_cb2 <= p_sr_dir_out; - sr_cb1_oe_l <= ~p_sr_cb1_op; - sr_cb1_out <= sr_strobe; - end - end -end - - -always @(posedge CLK) begin - - if (ENA_4 === 1'b 1) begin - sr_strobe_t1 <= sr_strobe; - sr_strobe_rising <= sr_strobe_t1 == 1'b 0 & sr_strobe == 1'b 1; - sr_strobe_falling <= sr_strobe_t1 == 1'b 1 & sr_strobe == 1'b 0; - end -end - -// -// Interrupts -// - -always @(posedge CLK) begin - - if (RESET_L === 1'b 0) begin - r_ier <= 7'b 0000000; - end - else begin - if (ENA_4 === 1'b 1) begin - if (ier_write_ena) begin - if (load_data[7] === 1'b 1) begin - - // set - r_ier <= r_ier | load_data[6:0]; - - // clear - end - else begin - r_ier <= r_ier & ~load_data[6:0]; - end - end - end - end -end - -assign O_IRQ_L = ~final_irq; -assign r_ifr = {final_irq, t1_irq, t2_irq, cb1_irq, cb2_irq, sr_irq, ca1_irq, ca2_irq}; - -always @(posedge CLK) begin - - if (RESET_L === 1'b 0) begin - final_irq <= 1'b 0; - end - else begin - if (ENA_4 === 1'b 1) begin - if ((r_ifr[6:0] & r_ier[6:0]) === 7'b 0000000) begin - final_irq <= 1'b 0; - // no interrupts - end - else begin - final_irq <= 1'b 1; - end - end - end -end - - -assign clear_irq = ifr_write_ena ? load_data : 8'h00; - -endmodule // module M6522 - diff --git a/cores/bbc/rtl/m6522.vhd b/cores/bbc/rtl/m6522.vhd new file mode 100644 index 0000000..87fee00 --- /dev/null +++ b/cores/bbc/rtl/m6522.vhd @@ -0,0 +1,1015 @@ +-- +-- A simulation model of VIC20 hardware - VIA implementation +-- Copyright (c) MikeJ - March 2003 +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- You are responsible for any legal issues arising from your use of this code. +-- +-- The latest version of this file can be found at: www.fpgaarcade.com +-- +-- Email vic20@fpgaarcade.com +-- +-- +-- Revision list +-- +-- dmb: ier bit 7 should read back as '1' +-- dmb: Fixes to sr_do_shift change that broke MMFS on the Beeb (SR mode 0) +-- version 005 Many fixes to all areas, VIA now passes all VICE tests +-- version 004 fixes to PB7 T1 control and Mode 0 Shift Register operation +-- version 003 fix reset of T1/T2 IFR flags if T1/T2 is reload via reg5/reg9 from wolfgang (WoS) +-- Ported to numeric_std and simulation fix for signal initializations from arnim laeuger +-- version 002 fix from Mark McDougall, untested +-- version 001 initial release +-- not very sure about the shift register, documentation is a bit light. + +library ieee ; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + +entity M6522 is + port ( + I_RS : in std_logic_vector(3 downto 0); + I_DATA : in std_logic_vector(7 downto 0); + O_DATA : out std_logic_vector(7 downto 0); + O_DATA_OE_L : out std_logic; + + I_RW_L : in std_logic; + I_CS1 : in std_logic; + I_CS2_L : in std_logic; + + O_IRQ_L : out std_logic; -- note, not open drain + + -- port a + I_CA1 : in std_logic; + I_CA2 : in std_logic; + O_CA2 : out std_logic; + O_CA2_OE_L : out std_logic; + + I_PA : in std_logic_vector(7 downto 0); + O_PA : out std_logic_vector(7 downto 0); + O_PA_OE_L : out std_logic_vector(7 downto 0); + + -- port b + I_CB1 : in std_logic; + O_CB1 : out std_logic; + O_CB1_OE_L : out std_logic; + + I_CB2 : in std_logic; + O_CB2 : out std_logic; + O_CB2_OE_L : out std_logic; + + I_PB : in std_logic_vector(7 downto 0); + O_PB : out std_logic_vector(7 downto 0); + O_PB_OE_L : out std_logic_vector(7 downto 0); + + I_P2_H : in std_logic; -- high for phase 2 clock ____----__ + RESET_L : in std_logic; + ENA_4 : in std_logic; -- clk enable + CLK : in std_logic + ); +end; + +architecture RTL of M6522 is + + signal phase : std_logic_vector(1 downto 0):="00"; + signal p2_h_t1 : std_logic; + signal cs : std_logic; + + -- registers + signal r_ddra : std_logic_vector(7 downto 0); + signal r_ora : std_logic_vector(7 downto 0); + signal r_ira : std_logic_vector(7 downto 0); + + signal r_ddrb : std_logic_vector(7 downto 0); + signal r_orb : std_logic_vector(7 downto 0); + signal r_irb : std_logic_vector(7 downto 0); + + signal r_t1l_l : std_logic_vector(7 downto 0); + signal r_t1l_h : std_logic_vector(7 downto 0); + signal r_t2l_l : std_logic_vector(7 downto 0); + signal r_t2l_h : std_logic_vector(7 downto 0); -- not in real chip + signal r_sr : std_logic_vector(7 downto 0); + signal r_acr : std_logic_vector(7 downto 0); + signal r_pcr : std_logic_vector(7 downto 0); + signal r_ifr : std_logic_vector(7 downto 0); + signal r_ier : std_logic_vector(6 downto 0); + + signal sr_write_ena : boolean; + signal sr_read_ena : boolean; + signal ifr_write_ena : boolean; + signal ier_write_ena : boolean; + signal clear_irq : std_logic_vector(7 downto 0); + signal load_data : std_logic_vector(7 downto 0); + + -- timer 1 + signal t1c : std_logic_vector(15 downto 0) := (others => '1'); -- simulators may not catch up w/o init here... + signal t1c_active : boolean; + signal t1c_done : boolean; + signal t1_w_reset_int : boolean; + signal t1_r_reset_int : boolean; + signal t1_load_counter : boolean; + signal t1_reload_counter : boolean; + signal t1_int_enable : boolean := false; + signal t1_toggle : std_logic; + signal t1_irq : std_logic := '0'; + signal t1_pb7 : std_logic := '1'; + signal t1_pb7_en_c : std_logic; + signal t1_pb7_en_d : std_logic; + + -- timer 2 + signal t2c : std_logic_vector(15 downto 0) := (others => '1'); -- simulators may not catch up w/o init here... + signal t2c_active : boolean; + signal t2c_done : boolean; + signal t2_pb6 : std_logic; + signal t2_pb6_t1 : std_logic; + signal t2_cnt_clk : std_logic := '1'; + signal t2_w_reset_int : boolean; + signal t2_r_reset_int : boolean; + signal t2_load_counter : boolean; + signal t2_reload_counter : boolean; + signal t2_int_enable : boolean := false; + signal t2_irq : std_logic := '0'; + signal t2_sr_ena : boolean; + + -- shift reg + signal sr_cnt : std_logic_vector(3 downto 0); + signal sr_cb1_oe_l : std_logic; + signal sr_cb1_out : std_logic; + signal sr_drive_cb2 : std_logic; + signal sr_strobe : std_logic; + signal sr_do_shift : boolean := false; + signal sr_strobe_t1 : std_logic; + signal sr_strobe_falling : boolean; + signal sr_strobe_rising : boolean; + signal sr_irq : std_logic; + signal sr_out : std_logic; + signal sr_active : boolean; + + -- io + signal w_orb_hs : std_logic; + signal w_ora_hs : std_logic; + signal r_irb_hs : std_logic; + signal r_ira_hs : std_logic; + + signal ca_hs_sr : std_logic; + signal ca_hs_pulse : std_logic; + signal cb_hs_sr : std_logic; + signal cb_hs_pulse : std_logic; + + signal cb1_in_mux : std_logic; + signal ca1_ip_reg_c : std_logic; + signal ca1_ip_reg_d : std_logic; + signal cb1_ip_reg_c : std_logic; + signal cb1_ip_reg_d : std_logic; + signal ca1_int : boolean; + signal cb1_int : boolean; + signal ca1_irq : std_logic; + signal cb1_irq : std_logic; + + signal ca2_ip_reg_c : std_logic; + signal ca2_ip_reg_d : std_logic; + signal cb2_ip_reg_c : std_logic; + signal cb2_ip_reg_d : std_logic; + signal ca2_int : boolean; + signal cb2_int : boolean; + signal ca2_irq : std_logic; + signal cb2_irq : std_logic; + + signal final_irq : std_logic; +begin + + p_phase : process + begin + -- internal clock phase + wait until rising_edge(CLK); + if (ENA_4 = '1') then + p2_h_t1 <= I_P2_H; + if (p2_h_t1 = '0') and (I_P2_H = '1') then + phase <= "11"; + else + phase <= std_logic_vector(unsigned(phase) + 1); + end if; + end if; + end process; + + p_cs : process(I_CS1, I_CS2_L, I_P2_H) + begin + cs <= '0'; + if (I_CS1 = '1') and (I_CS2_L = '0') and (I_P2_H = '1') then + cs <= '1'; + end if; + end process; + + -- peripheral control reg (pcr) + -- 0 ca1 interrupt control (0 +ve edge, 1 -ve edge) + -- 3..1 ca2 operation + -- 000 input -ve edge + -- 001 independend interrupt input -ve edge + -- 010 input +ve edge + -- 011 independend interrupt input +ve edge + -- 100 handshake output + -- 101 pulse output + -- 110 low output + -- 111 high output + -- 7..4 as 3..0 for cb1,cb2 + + -- auxiliary control reg (acr) + -- 0 input latch PA (0 disable, 1 enable) + -- 1 input latch PB (0 disable, 1 enable) + -- 4..2 shift reg control + -- 000 disable + -- 001 shift in using t2 + -- 010 shift in using o2 + -- 011 shift in using ext clk + -- 100 shift out free running t2 rate + -- 101 shift out using t2 + -- 101 shift out using o2 + -- 101 shift out using ext clk + -- 5 t2 timer control (0 timed interrupt, 1 count down with pulses on pb6) + -- 7..6 t1 timer control + -- 00 timed interrupt each time t1 is loaded pb7 disable + -- 01 continuous interrupts pb7 disable + -- 00 timed interrupt each time t1 is loaded pb7 one shot output + -- 01 continuous interrupts pb7 square wave output + -- + + p_write_reg_reset : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + r_ora <= x"00"; r_orb <= x"00"; + r_ddra <= x"00"; r_ddrb <= x"00"; + r_acr <= x"00"; r_pcr <= x"00"; + + w_orb_hs <= '0'; + w_ora_hs <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + w_orb_hs <= '0'; + w_ora_hs <= '0'; + if (cs = '1') and (I_RW_L = '0') then + case I_RS is + when x"0" => r_orb <= I_DATA; w_orb_hs <= '1'; + when x"1" => r_ora <= I_DATA; w_ora_hs <= '1'; + when x"2" => r_ddrb <= I_DATA; + when x"3" => r_ddra <= I_DATA; + + when x"B" => r_acr <= I_DATA; + when x"C" => r_pcr <= I_DATA; + when x"F" => r_ora <= I_DATA; + + when others => null; + end case; + end if; + + -- Set timer PB7 state, only on rising edge of setting ACR(7) + if ((t1_pb7_en_d = '0') and (t1_pb7_en_c = '1')) then + t1_pb7 <= '1'; + end if; + + if t1_load_counter then + t1_pb7 <= '0'; -- Reset internal timer 1 PB7 state on every timer load + elsif t1_toggle = '1' then + t1_pb7 <= not t1_pb7; + end if; + end if; + end if; + end process; + + p_write_reg : process (RESET_L, CLK) is + begin + if (RESET_L = '0') then + -- The spec says, this is not reset. + -- Fact is that the 1541 VIA1 timer won't work, + -- as the firmware ONLY sets the r_t1l_h latch!!!! + r_t1l_l <= (others => '1'); -- All latches default to FFFF + r_t1l_h <= (others => '1'); + r_t2l_l <= (others => '1'); + r_t2l_h <= (others => '1'); + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + t1_w_reset_int <= false; + t1_load_counter <= false; + + t2_w_reset_int <= false; + t2_load_counter <= false; + + load_data <= x"00"; + sr_write_ena <= false; + ifr_write_ena <= false; + ier_write_ena <= false; + + if (cs = '1') and (I_RW_L = '0') then + load_data <= I_DATA; + case I_RS is + when x"4" => r_t1l_l <= I_DATA; + when x"5" => r_t1l_h <= I_DATA; t1_w_reset_int <= true; + t1_load_counter <= true; + + when x"6" => r_t1l_l <= I_DATA; + when x"7" => r_t1l_h <= I_DATA; t1_w_reset_int <= true; + + when x"8" => r_t2l_l <= I_DATA; + when x"9" => r_t2l_h <= I_DATA; t2_w_reset_int <= true; + t2_load_counter <= true; + + when x"A" => sr_write_ena <= true; + when x"D" => ifr_write_ena <= true; + when x"E" => ier_write_ena <= true; + + when others => null; + end case; + end if; + end if; + end if; + end process; + + p_oe : process(cs, I_RW_L) + begin + O_DATA_OE_L <= '1'; + if (cs = '1') and (I_RW_L = '1') then + O_DATA_OE_L <= '0'; + end if; + end process; + + p_read : process(cs, I_RW_L, I_RS, r_irb, r_ira, r_ddrb, r_ddra, t1c, r_t1l_l, + r_t1l_h, t2c, r_sr, r_acr, r_pcr, r_ifr, r_ier, r_ora, r_orb, t1_pb7_en_d, t1_pb7) + variable orb : std_logic_vector(7 downto 0); + begin + t1_r_reset_int <= false; + t2_r_reset_int <= false; + sr_read_ena <= false; + r_irb_hs <= '0'; + r_ira_hs <= '0'; + O_DATA <= x"00"; -- default + orb := (r_irb and not r_ddrb) or (r_orb and r_ddrb); + + -- If PB7 under timer control, assign value from timer + if (t1_pb7_en_d = '1') then + orb(7) := t1_pb7; + end if; + + if (cs = '1') and (I_RW_L = '1') then + case I_RS is + when x"0" => O_DATA <= orb; r_irb_hs <= '1'; + when x"1" => O_DATA <= (r_ira and not r_ddra) or (r_ora and r_ddra); r_ira_hs <= '1'; + when x"2" => O_DATA <= r_ddrb; + when x"3" => O_DATA <= r_ddra; + when x"4" => O_DATA <= t1c( 7 downto 0); t1_r_reset_int <= true; + when x"5" => O_DATA <= t1c(15 downto 8); + when x"6" => O_DATA <= r_t1l_l; + when x"7" => O_DATA <= r_t1l_h; + when x"8" => O_DATA <= t2c( 7 downto 0); t2_r_reset_int <= true; + when x"9" => O_DATA <= t2c(15 downto 8); + when x"A" => O_DATA <= r_sr; sr_read_ena <= true; + when x"B" => O_DATA <= r_acr; + when x"C" => O_DATA <= r_pcr; + when x"D" => O_DATA <= r_ifr; + -- DMB: ier bit 7 should read back as '1' + when x"E" => O_DATA <= ('1' & r_ier); + when x"F" => O_DATA <= r_ira; + when others => null; + end case; + end if; + + end process; + + -- + -- IO + -- + + p_ca1_cb1_sel : process(sr_cb1_oe_l, sr_cb1_out, I_CB1) + begin + -- if the shift register is enabled, cb1 may be an output + -- in this case we should NOT listen to the input as + -- CB1 interrupts are not generated by the shift register + if (sr_cb1_oe_l = '1') then + cb1_in_mux <= I_CB1; + else + cb1_in_mux <= '1'; + end if; + end process; + + p_ca1_cb1_int : process(r_pcr, ca1_ip_reg_c, ca1_ip_reg_d, cb1_ip_reg_c, cb1_ip_reg_d) + begin + if (r_pcr(0) = '0') then -- ca1 control + -- negative edge + ca1_int <= (ca1_ip_reg_d = '1') and (ca1_ip_reg_c = '0'); + else + -- positive edge + ca1_int <= (ca1_ip_reg_d = '0') and (ca1_ip_reg_c = '1'); + end if; + + if (r_pcr(4) = '0') then -- cb1 control + -- negative edge + cb1_int <= (cb1_ip_reg_d = '1') and (cb1_ip_reg_c = '0'); + else + -- positive edge + cb1_int <= (cb1_ip_reg_d = '0') and (cb1_ip_reg_c = '1'); + end if; + end process; + + p_ca2_cb2_int : process(r_pcr, ca2_ip_reg_c, ca2_ip_reg_d, cb2_ip_reg_c, cb2_ip_reg_d) + begin + ca2_int <= false; + if (r_pcr(3) = '0') then -- ca2 input + if (r_pcr(2) = '0') then -- ca2 edge + -- negative edge + ca2_int <= (ca2_ip_reg_d = '1') and (ca2_ip_reg_c = '0'); + else + -- positive edge + ca2_int <= (ca2_ip_reg_d = '0') and (ca2_ip_reg_c = '1'); + end if; + end if; + + cb2_int <= false; + if (r_pcr(7) = '0') then -- cb2 input + if (r_pcr(6) = '0') then -- cb2 edge + -- negative edge + cb2_int <= (cb2_ip_reg_d = '1') and (cb2_ip_reg_c = '0'); + else + -- positive edge + cb2_int <= (cb2_ip_reg_d = '0') and (cb2_ip_reg_c = '1'); + end if; + end if; + end process; + + p_ca2_cb2 : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + O_CA2 <= '1'; -- Pullup is default + O_CA2_OE_L <= '1'; + O_CB2 <= '1'; -- Pullup is default + O_CB2_OE_L <= '1'; + + ca_hs_sr <= '0'; + ca_hs_pulse <= '0'; + cb_hs_sr <= '0'; + cb_hs_pulse <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- ca + if (phase = "00") and ((w_ora_hs = '1') or (r_ira_hs = '1')) then + ca_hs_sr <= '1'; + elsif ca1_int then + ca_hs_sr <= '0'; + end if; + + if (phase = "00") then + ca_hs_pulse <= w_ora_hs or r_ira_hs; + end if; + + O_CA2_OE_L <= not r_pcr(3); -- ca2 output + case r_pcr(3 downto 1) is + when "000" => O_CA2 <= I_CA2; -- input, output follows input + when "001" => O_CA2 <= I_CA2; -- input, output follows input + when "010" => O_CA2 <= I_CA2; -- input, output follows input + when "011" => O_CA2 <= I_CA2; -- input, output follows input + when "100" => O_CA2 <= not (ca_hs_sr); -- handshake + when "101" => O_CA2 <= not (ca_hs_pulse); -- pulse + when "110" => O_CA2 <= '0'; -- low + when "111" => O_CA2 <= '1'; -- high + when others => null; + end case; + + -- cb + if (phase = "00") and (w_orb_hs = '1') then + cb_hs_sr <= '1'; + elsif cb1_int then + cb_hs_sr <= '0'; + end if; + + if (phase = "00") then + cb_hs_pulse <= w_orb_hs; + end if; + + O_CB2_OE_L <= not (r_pcr(7) or sr_drive_cb2); -- cb2 output or serial + if (sr_drive_cb2 = '1') then -- serial output + O_CB2 <= sr_out; + else + case r_pcr(7 downto 5) is + when "000" => O_CB2 <= I_CB2; -- input, output follows input + when "001" => O_CB2 <= I_CB2; -- input, output follows input + when "010" => O_CB2 <= I_CB2; -- input, output follows input + when "011" => O_CB2 <= I_CB2; -- input, output follows input + when "100" => O_CB2 <= not (cb_hs_sr); -- handshake + when "101" => O_CB2 <= not (cb_hs_pulse); -- pulse + when "110" => O_CB2 <= '0'; -- low + when "111" => O_CB2 <= '1'; -- high + when others => null; + end case; + end if; + end if; + end if; + end process; + + O_CB1 <= sr_cb1_out; + O_CB1_OE_L <= sr_cb1_oe_l; + + p_ca_cb_irq : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + ca1_irq <= '0'; + ca2_irq <= '0'; + cb1_irq <= '0'; + cb2_irq <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- not pretty + if ca1_int then + ca1_irq <= '1'; + elsif (r_ira_hs = '1') or (w_ora_hs = '1') or (clear_irq(1) = '1') then + ca1_irq <= '0'; + end if; + + if ca2_int then + ca2_irq <= '1'; + else + if (((r_ira_hs = '1') or (w_ora_hs = '1')) and (r_pcr(1) = '0')) or + (clear_irq(0) = '1') then + ca2_irq <= '0'; + end if; + end if; + + if cb1_int then + cb1_irq <= '1'; + elsif (r_irb_hs = '1') or (w_orb_hs = '1') or (clear_irq(4) = '1') then + cb1_irq <= '0'; + end if; + + if cb2_int then + cb2_irq <= '1'; + else + if (((r_irb_hs = '1') or (w_orb_hs = '1')) and (r_pcr(5) = '0')) or + (clear_irq(3) = '1') then + cb2_irq <= '0'; + end if; + end if; + end if; + end if; + end process; + + p_input_reg : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + ca1_ip_reg_c <= '0'; + ca1_ip_reg_d <= '0'; + + cb1_ip_reg_c <= '0'; + cb1_ip_reg_d <= '0'; + + ca2_ip_reg_c <= '0'; + ca2_ip_reg_d <= '0'; + + cb2_ip_reg_c <= '0'; + cb2_ip_reg_d <= '0'; + + r_ira <= x"00"; + r_irb <= x"00"; + + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- we have a fast clock, so we can have input registers + ca1_ip_reg_c <= I_CA1; + ca1_ip_reg_d <= ca1_ip_reg_c; + + cb1_ip_reg_c <= cb1_in_mux; + cb1_ip_reg_d <= cb1_ip_reg_c; + + ca2_ip_reg_c <= I_CA2; + ca2_ip_reg_d <= ca2_ip_reg_c; + + cb2_ip_reg_c <= I_CB2; + cb2_ip_reg_d <= cb2_ip_reg_c; + + if (r_acr(0) = '0') then + r_ira <= I_PA; + else -- enable latching + if ca1_int then + r_ira <= I_PA; + end if; + end if; + + if (r_acr(1) = '0') then + r_irb <= I_PB; + else -- enable latching + if cb1_int then + r_irb <= I_PB; + end if; + end if; + end if; + end if; + end process; + + p_buffers : process(r_ddra, r_ora, r_ddrb, r_acr, r_orb, t1_pb7_en_d, t1_pb7) + begin + -- data direction reg (ddr) 0 = input, 1 = output + O_PA <= r_ora; + O_PA_OE_L <= not r_ddra; + + -- If PB7 is timer driven output set PB7 to the timer state, otherwise use value in ORB register + if (t1_pb7_en_d = '1') then + O_PB <= t1_pb7 & r_orb(6 downto 0); + else + O_PB <= r_orb; + end if; + + -- NOTE: r_ddrb(7) must be set to enable T1 output on PB7 - [various datasheets specify this] + O_PB_OE_L <= not r_ddrb; + end process; + + -- + -- Timer 1 + -- + + -- Detect change in r_acr(7), timer 1 mode for PB7 + p_pb7_enable : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + t1_pb7_en_c <= r_acr(7); + t1_pb7_en_d <= t1_pb7_en_c; + end if; + end process; + + p_timer1_done : process + variable done : boolean; + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + done := (t1c = x"0000"); + t1c_done <= done and (phase = "11"); + if (phase = "11") and not t1_load_counter then -- Don't set reload if T1L-H written + t1_reload_counter <= done; + elsif t1_load_counter then -- Cancel a reload when T1L-H written + t1_reload_counter <= false; + end if; + if t1_load_counter then -- done reset on load! + t1c_done <= false; + end if; + end if; + end process; + + p_timer1 : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + if t1_load_counter or (t1_reload_counter and phase = "11") then + t1c( 7 downto 0) <= r_t1l_l; + t1c(15 downto 8) <= r_t1l_h; + -- There is a need to write to Latch HI to enable interrupts for both continuous and one-shot modes + if t1_load_counter then + t1_int_enable <= true; + end if; + elsif (phase="11") then + t1c <= std_logic_vector(unsigned(t1c) - 1); + end if; + + if t1_load_counter or t1_reload_counter then + t1c_active <= true; + elsif t1c_done then + t1c_active <= false; + end if; + + t1_toggle <= '0'; + if t1c_active and t1c_done then + if t1_int_enable then -- Set interrupt only if T1L-H has been written + t1_toggle <= '1'; + t1_irq <= '1'; + if (r_acr(6) = '0') then -- Disable further interrupts if in one shot mode + t1_int_enable <= false; + end if; + end if; + elsif t1_w_reset_int or t1_r_reset_int or (clear_irq(6) = '1') then + t1_irq <= '0'; + end if; + if t1_load_counter then -- irq reset on load! + t1_irq <= '0'; + end if; + end if; + end process; + + -- + -- Timer2 + -- + + p_timer2_pb6_input : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + if (phase = "01") then -- leading edge p2_h + t2_pb6 <= I_PB(6); + t2_pb6_t1 <= t2_pb6; + end if; + end if; + end process; + + -- Ensure we don't start counting until the P2 clock after r_acr is changed + p_timer2_ena : process + begin + wait until rising_edge(I_P2_H); + if r_acr(5) = '0' then + t2_cnt_clk <= '1'; + else + t2_cnt_clk <= '0'; + end if; + end process; + + p_timer2_done : process + variable done : boolean; + variable done_sr : boolean; + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + done := (t2c = x"0000"); -- Normal timer expires at 0000 + done_sr := (t2c(7 downto 0) = x"00"); -- Shift register expires on low byte = 00 + t2c_done <= done and (phase = "11"); + if (phase = "11") then + t2_reload_counter <= done_sr; -- Timer 2 is only reloaded when used for the shift register + end if; + if t2_load_counter then -- done reset on load! + t2c_done <= false; + end if; + end if; + end process; + + p_timer2 : process + variable ena : boolean; + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + if (t2_cnt_clk ='1') then + ena := true; + t2c_active <= true; + t2_int_enable <= true; + else + ena := (t2_pb6_t1 = '1') and (t2_pb6 = '0'); -- falling edge + end if; + + -- Shift register reload is only active when shift register mode using T2 is enabled + if t2_reload_counter and (phase="11") and ((r_acr(4 downto 2) = "001") or (r_acr(4 downto 2) = "100") or (r_acr(4 downto 2) = "101")) then + t2c(7 downto 0) <= r_t2l_l; -- For shift register only low latch is loaded! + elsif t2_load_counter then + t2_int_enable <= true; + t2c( 7 downto 0) <= r_t2l_l; + t2c(15 downto 8) <= r_t2l_h; + else + if (phase="11") and ena then -- or count mode + t2c <= std_logic_vector(unsigned(t2c) - 1); + end if; + end if; + + -- Shift register strobe on T2 occurs one P2H clock after timer expires + -- so enable the strobe when we roll over to FF + t2_sr_ena <= (t2c(7 downto 0) = x"FF") and (phase = "11"); + + if t2_load_counter then + t2c_active <= true; + elsif t2c_done then + t2c_active <= false; + end if; + + if t2c_active and t2c_done and t2_int_enable then + t2_int_enable <= false; + t2_irq <= '1'; + elsif t2_w_reset_int or t2_r_reset_int or (clear_irq(5) = '1') then + t2_irq <= '0'; + end if; + if t2_load_counter then -- irq reset on load! + t2_irq <= '0'; + end if; + end if; + end process; + + -- + -- Shift Register + -- + + p_sr : process(RESET_L, CLK) + variable dir_out : std_logic; + variable ena : std_logic; + variable cb1_op : std_logic; + variable cb1_ip : std_logic; + variable use_t2 : std_logic; + variable free_run : std_logic; + variable sr_count_ena : boolean; + begin + if (RESET_L = '0') then + r_sr <= x"00"; + sr_drive_cb2 <= '0'; + sr_cb1_oe_l <= '1'; + sr_cb1_out <= '0'; + sr_do_shift <= false; + sr_strobe <= '1'; + sr_cnt <= "0000"; + sr_irq <= '0'; + sr_out <= '0'; + sr_active <= false; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- decode mode + dir_out := r_acr(4); -- output on cb2 + cb1_op := '0'; + cb1_ip := '0'; + use_t2 := '0'; + free_run := '0'; + + -- DMB: SR still runs even in disabled mode (on rising edge of CB1). + -- It just doesn't generate any interrupts. + -- Ref BBC micro advanced user guide p409 + + case r_acr(4 downto 2) is + -- DMB: in disabled mode, configure cb1 as an input + when "000" => ena := '0'; cb1_ip := '1'; -- 0x00 Mode 0 SR disabled + when "001" => ena := '1'; cb1_op := '1'; use_t2 := '1'; -- 0x04 Mode 1 Shift in under T2 control + when "010" => ena := '1'; cb1_op := '1'; -- 0x08 Mode 2 Shift in under P2 control + when "011" => ena := '1'; cb1_ip := '1'; -- 0x0C Mode 3 Shift in under control of ext clock + when "100" => ena := '1'; cb1_op := '1'; use_t2 := '1'; free_run := '1'; -- 0x10 Mode 4 Shift out free running under T2 control + when "101" => ena := '1'; cb1_op := '1'; use_t2 := '1'; -- 0x14 Mode 5 Shift out under T2 control + when "110" => ena := '1'; cb1_op := '1'; -- 0x18 Mode 6 Shift out under P2 control + when "111" => ena := '1'; cb1_ip := '1'; -- 0x1C Mode 7 Shift out under control of ext clock + when others => null; + end case; + + -- clock select + -- DMB: in disabled mode, strobe from cb1 + if (cb1_ip = '1') then + sr_strobe <= I_CB1; + else + if (sr_cnt(3) = '0') and (free_run = '0') then + sr_strobe <= '1'; + else + if ((use_t2 = '1') and t2_sr_ena) or + ((use_t2 = '0') and (phase = "00")) then + sr_strobe <= not sr_strobe; + end if; + end if; + end if; + + -- latch on rising edge, shift on falling edge of P2 + if sr_write_ena then + r_sr <= load_data; + sr_out <= r_sr(7); + + else + -- DMB: allow shifting in all modes + if (dir_out = '0') then + -- input + if (sr_cnt(3) = '1') or (cb1_ip = '1') then + if sr_strobe_rising then + sr_do_shift <= true; + r_sr(0) <= I_CB2; + -- DMB: Added sr_stroble_falling to avoid premature shift + -- (fixes issue with MMFS on the Beeb in SR mode 0) + elsif sr_do_shift and sr_strobe_falling then + sr_do_shift <= false; + r_sr(7 downto 1) <= r_sr(6 downto 0); + end if; + end if; + else + -- output + if (sr_cnt(3) = '1') or (cb1_ip = '1') or (free_run = '1') then + if sr_strobe_falling then + sr_out <= r_sr(7); + sr_do_shift <= true; + -- DMB: Added sr_stroble_falling to avoid premature shift + -- (fixes issue with MMFS on the Beeb in SR mode 0) + elsif sr_do_shift and sr_strobe_rising then + sr_do_shift <= false; + r_sr <= r_sr(6 downto 0) & r_sr(7); + end if; + end if; + end if; + end if; + + -- Set shift enabled flag, note does not get set for free_run mode ! + if (ena = '1') and (sr_cnt(3) = '1') then + sr_active <= true; + elsif (ena = '1') and (sr_cnt(3) = '0') and (phase="11") then + sr_active <= false; + end if; + + sr_count_ena := sr_strobe_rising; + + -- DMB: reseting sr_count when not enabled cause the sr to + -- start running immediately it was enabled, which is incorrect + -- and broke the latest SmartSPI ROM on the BBC Micro + if (ena = '1') and (sr_write_ena or sr_read_ena) and (not sr_active) then + -- some documentation says sr bit in IFR must be set as well ? + sr_cnt <= "1000"; + elsif sr_count_ena and (sr_cnt(3) = '1') then + sr_cnt <= std_logic_vector(unsigned(sr_cnt) + 1); + end if; + + if sr_count_ena and (sr_cnt = "1111") and (ena = '1') and (free_run = '0') then + sr_irq <= '1'; + elsif sr_write_ena or sr_read_ena or (clear_irq(2) = '1') then + sr_irq <= '0'; + end if; + + -- assign ops + sr_drive_cb2 <= dir_out; + sr_cb1_oe_l <= not cb1_op; + sr_cb1_out <= sr_strobe; + end if; + end if; + end process; + + p_sr_strobe_rise_fall : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + sr_strobe_t1 <= sr_strobe; + sr_strobe_rising <= (sr_strobe_t1 = '0') and (sr_strobe = '1'); + sr_strobe_falling <= (sr_strobe_t1 = '1') and (sr_strobe = '0'); + end if; + end process; + + -- + -- Interrupts + -- + + p_ier : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + r_ier <= "0000000"; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + if ier_write_ena then + if (load_data(7) = '1') then + -- set + r_ier <= r_ier or load_data(6 downto 0); + else + -- clear + r_ier <= r_ier and not load_data(6 downto 0); + end if; + end if; + end if; + end if; + end process; + + p_ifr : process(t1_irq, t2_irq, final_irq, ca1_irq, ca2_irq, sr_irq, + cb1_irq, cb2_irq) + begin + r_ifr(7) <= final_irq; + r_ifr(6) <= t1_irq; + r_ifr(5) <= t2_irq; + r_ifr(4) <= cb1_irq; + r_ifr(3) <= cb2_irq; + r_ifr(2) <= sr_irq; + r_ifr(1) <= ca1_irq; + r_ifr(0) <= ca2_irq; + + O_IRQ_L <= not final_irq; + end process; + + p_irq : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + final_irq <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + if ((r_ifr(6 downto 0) and r_ier(6 downto 0)) = "0000000") then + final_irq <= '0'; -- no interrupts + else + final_irq <= '1'; + end if; + end if; + end if; + end process; + + p_clear_irq : process(ifr_write_ena, load_data) + begin + clear_irq <= x"00"; + if ifr_write_ena then + clear_irq <= load_data; + end if; + end process; + +end architecture RTL; From 255202e36e12d10082f33f6d5a126888157b31be Mon Sep 17 00:00:00 2001 From: Gyorgy Szombathelyi Date: Wed, 22 Aug 2018 02:38:42 +0200 Subject: [PATCH 3/5] BBC: add an sdc file --- cores/bbc/fpga/mist/bbc_mist.sdc | 52 ++++++++++++++++++++++++++++ cores/bbc/fpga/mist/bbc_mist_top.qsf | 1 + 2 files changed, 53 insertions(+) create mode 100644 cores/bbc/fpga/mist/bbc_mist.sdc diff --git a/cores/bbc/fpga/mist/bbc_mist.sdc b/cores/bbc/fpga/mist/bbc_mist.sdc new file mode 100644 index 0000000..2252a6b --- /dev/null +++ b/cores/bbc/fpga/mist/bbc_mist.sdc @@ -0,0 +1,52 @@ +#************************************************************ +# THIS IS A WIZARD-GENERATED FILE. +# +# Version 13.1.4 Build 182 03/12/2014 SJ Full Version +# +#************************************************************ + +# Copyright (C) 1991-2014 Altera Corporation +# Your use of Altera Corporation's design tools, logic functions +# and other software and tools, and its AMPP partner logic +# functions, and any output files from any of the foregoing +# (including device programming or simulation files), and any +# associated documentation or information are expressly subject +# to the terms and conditions of the Altera Program License +# Subscription Agreement, Altera MegaCore Function License +# Agreement, or other applicable license agreement, including, +# without limitation, that your use is for the sole purpose of +# programming logic devices manufactured by Altera and sold by +# Altera or its authorized distributors. Please refer to the +# applicable agreement for further details. + + + +# Clock constraints + +create_clock -name "CLOCK_27" -period 37.037 [get_ports {CLOCK_27[0]}] +create_clock -name {SPI_SCK} -period 41.666 -waveform { 20.8 41.666 } [get_ports {SPI_SCK}] + +# Automatically constrain PLL and other generated clocks +derive_pll_clocks -create_base_clocks + +# Automatically calculate clock uncertainty to jitter and other effects. +derive_clock_uncertainty + +# Clock groups +set_clock_groups -asynchronous -group [get_clocks {SPI_SCK}] -group [get_clocks {CLOCKS|altpll_component|auto_generated|pll1|clk[*]}] + + +# Some relaxed constrain to the VGA pins. The signals should arrive together, the delay is not really important. +set_output_delay -clock [get_clocks {CLOCKS|altpll_component|auto_generated|pll1|clk[0]}] -max 0 [get_ports {VGA_*}] +set_output_delay -clock [get_clocks {CLOCKS|altpll_component|auto_generated|pll1|clk[0]}] -min -5 [get_ports {VGA_*}] + +# SDRAM delays +set_input_delay -clock [get_clocks {CLOCKS|altpll_component|auto_generated|pll1|clk[0]}] -max 6.4 [get_ports SDRAM_DQ[*]] +set_input_delay -clock [get_clocks {CLOCKS|altpll_component|auto_generated|pll1|clk[0]}] -min 3.2 [get_ports SDRAM_DQ[*]] + +set_output_delay -clock [get_clocks {CLOCKS|altpll_component|auto_generated|pll1|clk[0]}] -max 1.5 [get_ports {SDRAM_D* SDRAM_A* SDRAM_BA* SDRAM_n* SDRAM_CKE}] +set_output_delay -clock [get_clocks {CLOCKS|altpll_component|auto_generated|pll1|clk[0]}] -min -0.8 [get_ports {SDRAM_D* SDRAM_A* SDRAM_BA* SDRAM_n* SDRAM_CKE}] + +set_false_path -to [get_ports {AUDIO_L}] +set_false_path -to [get_ports {AUDIO_R}] +set_false_path -to [get_ports {LED}] diff --git a/cores/bbc/fpga/mist/bbc_mist_top.qsf b/cores/bbc/fpga/mist/bbc_mist_top.qsf index ec7ad0b..2d7ddb0 100644 --- a/cores/bbc/fpga/mist/bbc_mist_top.qsf +++ b/cores/bbc/fpga/mist/bbc_mist_top.qsf @@ -198,4 +198,5 @@ set_global_assignment -name QIP_FILE smmc.qip set_global_assignment -name QIP_FILE os12.qip set_global_assignment -name QIP_FILE basic2.qip set_global_assignment -name QIP_FILE dfs09.qip +set_global_assignment -name SDC_FILE bbc_mist.sdc set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top From e61fee2fa2e7980036a1f3ea39381b03bf8984c2 Mon Sep 17 00:00:00 2001 From: Gyorgy Szombathelyi Date: Thu, 23 Aug 2018 00:56:29 +0200 Subject: [PATCH 4/5] BBC: update CPU to improved T65 --- cores/bbc/fpga/mist/bbc_mist_top.qsf | 8 +- cores/bbc/rtl/ALU.v | 108 --- cores/bbc/rtl/T65/T65.qip | 4 + cores/bbc/rtl/T65/T65.vhd | 668 ++++++++++++++ cores/bbc/rtl/T65/T65_ALU.vhd | 293 ++++++ cores/bbc/rtl/T65/T65_MCode.vhd | 1239 ++++++++++++++++++++++++++ cores/bbc/rtl/T65/T65_Pack.vhd | 180 ++++ cores/bbc/rtl/bbc.v | 53 +- cores/bbc/rtl/cpu.v | 1225 ------------------------- 9 files changed, 2406 insertions(+), 1372 deletions(-) delete mode 100755 cores/bbc/rtl/ALU.v create mode 100644 cores/bbc/rtl/T65/T65.qip create mode 100755 cores/bbc/rtl/T65/T65.vhd create mode 100755 cores/bbc/rtl/T65/T65_ALU.vhd create mode 100755 cores/bbc/rtl/T65/T65_MCode.vhd create mode 100755 cores/bbc/rtl/T65/T65_Pack.vhd delete mode 100755 cores/bbc/rtl/cpu.v diff --git a/cores/bbc/fpga/mist/bbc_mist_top.qsf b/cores/bbc/fpga/mist/bbc_mist_top.qsf index 2d7ddb0..a0b82df 100644 --- a/cores/bbc/fpga/mist/bbc_mist_top.qsf +++ b/cores/bbc/fpga/mist/bbc_mist_top.qsf @@ -158,10 +158,10 @@ set_location_assignment PIN_7 -to LED set_global_assignment -name ADV_NETLIST_OPT_SYNTH_WYSIWYG_REMAP ON set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V" + +set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top set_global_assignment -name VERILOG_FILE ../../rtl/adc.v set_global_assignment -name VERILOG_FILE sdram.v -set_global_assignment -name VERILOG_FILE ../../rtl/cpu.v -set_global_assignment -name VERILOG_FILE ../../rtl/ALU.v set_global_assignment -name VERILOG_FILE ../../rtl/mc6845.v set_global_assignment -name VERILOG_FILE ../../rtl/ps2_intf.v set_global_assignment -name VERILOG_FILE ../../rtl/vidproc.v @@ -194,9 +194,9 @@ set_global_assignment -name VERILOG_FILE ../rtl/scandoubler.v set_global_assignment -name VERILOG_FILE ../../rtl/bbc.v set_global_assignment -name CDF_FILE output_files/Chain4.cdf set_global_assignment -name VHDL_FILE ../../rtl/m6522.vhd +set_global_assignment -name QIP_FILE ../../rtl/T65/T65.qip set_global_assignment -name QIP_FILE smmc.qip set_global_assignment -name QIP_FILE os12.qip set_global_assignment -name QIP_FILE basic2.qip set_global_assignment -name QIP_FILE dfs09.qip -set_global_assignment -name SDC_FILE bbc_mist.sdc -set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top +set_global_assignment -name SDC_FILE bbc_mist.sdc \ No newline at end of file diff --git a/cores/bbc/rtl/ALU.v b/cores/bbc/rtl/ALU.v deleted file mode 100755 index 8d05fc0..0000000 --- a/cores/bbc/rtl/ALU.v +++ /dev/null @@ -1,108 +0,0 @@ -/* - * ALU. - * - * AI and BI are 8 bit inputs. Result in OUT. - * CI is Carry In. - * CO is Carry Out. - * - * op[3:0] is defined as follows: - * - * 0011 AI + BI - * 0111 AI - BI - * 1011 AI + AI - * 1100 AI | BI - * 1101 AI & BI - * 1110 AI ^ BI - * 1111 AI - * - */ - -module ALU( clk, op, right, AI, BI, CI, CO, BCD, OUT, V, Z, N, HC, RDY ); - input clk; - input right; - input [3:0] op; // operation - input [7:0] AI; - input [7:0] BI; - input CI; - input BCD; // BCD style carry - output [7:0] OUT; - output CO; - output V; - output Z; - output N; - output HC; - input RDY; - -reg [7:0] OUT; -reg CO; -wire V; -wire Z; -reg N; -reg HC; - -reg AI7; -reg BI7; -reg [8:0] temp_logic; -reg [7:0] temp_BI; -reg [4:0] temp_l; -reg [4:0] temp_h; -wire [8:0] temp = { temp_h, temp_l[3:0] }; -wire adder_CI = (right | (op[3:2] == 2'b11)) ? 0 : CI; - -// calculate the logic operations. The 'case' can be done in 1 LUT per -// bit. The 'right' shift is a simple mux that can be implemented by -// F5MUX. -always @* begin - case( op[1:0] ) - 2'b00: temp_logic = AI | BI; - 2'b01: temp_logic = AI & BI; - 2'b10: temp_logic = AI ^ BI; - 2'b11: temp_logic = AI; - endcase - - if( right ) - temp_logic = { AI[0], CI, AI[7:1] }; -end - -// Add logic result to BI input. This only makes sense when logic = AI. -// This stage can be done in 1 LUT per bit, using carry chain logic. -always @* begin - case( op[3:2] ) - 2'b00: temp_BI = BI; // A+B - 2'b01: temp_BI = ~BI; // A-B - 2'b10: temp_BI = temp_logic; // A+A - 2'b11: temp_BI = 0; // A+0 - endcase -end - -// HC9 is the half carry bit when doing BCD add -wire HC9 = BCD & (temp_l[3:1] >= 3'd5); - -// CO9 is the carry-out bit when doing BCD add -wire CO9 = BCD & (temp_h[3:1] >= 3'd5); - -// combined half carry bit -wire temp_HC = temp_l[4] | HC9; - -// perform the addition as 2 separate nibble, so we get -// access to the half carry flag -always @* begin - temp_l = temp_logic[3:0] + temp_BI[3:0] + adder_CI; - temp_h = temp_logic[8:4] + temp_BI[7:4] + temp_HC; -end - -// calculate the flags -always @(posedge clk) - if( RDY ) begin - AI7 <= AI[7]; - BI7 <= temp_BI[7]; - OUT <= temp[7:0]; - CO <= temp[8] | CO9; - N <= temp[7]; - HC <= temp_HC; - end - -assign V = AI7 ^ BI7 ^ CO ^ N; -assign Z = ~|OUT; - -endmodule diff --git a/cores/bbc/rtl/T65/T65.qip b/cores/bbc/rtl/T65/T65.qip new file mode 100644 index 0000000..a5ea77b --- /dev/null +++ b/cores/bbc/rtl/T65/T65.qip @@ -0,0 +1,4 @@ +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "T65_Pack.vhd"] +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "T65_MCode.vhd"] +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "T65_ALU.vhd"] +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "T65.vhd"] diff --git a/cores/bbc/rtl/T65/T65.vhd b/cores/bbc/rtl/T65/T65.vhd new file mode 100755 index 0000000..b9fa335 --- /dev/null +++ b/cores/bbc/rtl/T65/T65.vhd @@ -0,0 +1,668 @@ +-- **** +-- T65(b) core. In an effort to merge and maintain bug fixes .... +-- +-- Ver 313 WoS January 2015 +-- Fixed issue that NMI has to be first if issued the same time as a BRK instruction is latched in +-- Now all Lorenz CPU tests on FPGAARCADE C64 core (sources used: SVN version 1021) are OK! :D :D :D +-- This is just a starting point to go for optimizations and detailed fixes (the Lorenz test can't find) +-- +-- Ver 312 WoS January 2015 +-- Undoc opcode timing fixes for $B3 (LAX iy) and $BB (LAS ay) +-- Added comments in MCode section to find handling of individual opcodes more easily +-- All "basic" Lorenz instruction test (individual functional checks, CPUTIMING check) work now with +-- actual FPGAARCADE C64 core (sources used: SVN version 1021). +-- +-- Ver 305, 306, 307, 308, 309, 310, 311 WoS January 2015 +-- Undoc opcode fixes (now all Lorenz test on instruction functionality working, except timing issues on $B3 and $BB): +-- SAX opcode +-- SHA opcode +-- SHX opcode +-- SHY opcode +-- SHS opcode +-- LAS opcode +-- alternate SBC opcode +-- fixed NOP with immediate param (caused Lorenz trap test to fail) +-- IRQ and NMI timing fixes (in conjuction with branches) +-- +-- Ver 304 WoS December 2014 +-- Undoc opcode fixes: +-- ARR opcode +-- ANE/XAA opcode +-- Corrected issue with NMI/IRQ prio (when asserted the same time) +-- +-- Ver 303 ost(ML) July 2014 +-- (Sorry for some scratchpad comments that may make little sense) +-- Mods and some 6502 undocumented instructions. +-- Not correct opcodes acc. to Lorenz tests (incomplete list): +-- NOPN (nop) +-- NOPZX (nop + byte 172) +-- NOPAX (nop + word da ... da: byte 0) +-- ASOZ (byte $07 + byte 172) +-- +-- Ver 303,302 WoS April 2014 +-- Bugfixes for NMI from foft +-- Bugfix for BRK command (and its special flag) +-- +-- Ver 300,301 WoS January 2014 +-- More merging +-- Bugfixes by ehenciak added, started tidyup *bust* +-- +-- MikeJ March 2005 +-- Latest version from www.fpgaarcade.com (original www.opencores.org) +-- **** +-- +-- 65xx compatible microprocessor core +-- +-- FPGAARCADE SVN: $Id: T65.vhd 1347 2015-05-27 20:07:34Z wolfgang.scherr $ +-- +-- Copyright (c) 2002...2015 +-- Daniel Wallner (jesus opencores org) +-- Mike Johnson (mikej fpgaarcade com) +-- Wolfgang Scherr (WoS pin4 at> +-- Morten Leikvoll () +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author(s), but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- ----- IMPORTANT NOTES ----- +-- +-- Limitations: +-- 65C02 and 65C816 modes are incomplete (and definitely untested after all 6502 undoc fixes) +-- 65C02 supported : inc, dec, phx, plx, phy, ply +-- 65D02 missing : bra, ora, lda, cmp, sbc, tsb*2, trb*2, stz*2, bit*2, wai, stp, jmp, bbr*8, bbs*8 +-- Some interface signals behave incorrect +-- NMI interrupt handling not nice, needs further rework (to cycle-based encoding). +-- +-- Usage: +-- The enable signal allows clock gating / throttling without using the ready signal. +-- Set it to constant '1' when using the Clk input as the CPU clock directly. +-- +-- TAKE CARE you route the DO signal back to the DI signal while R_W_n='0', +-- otherwise some undocumented opcodes won't work correctly. +-- EXAMPLE: +-- CPU : entity work.T65 +-- port map ( +-- R_W_n => cpu_rwn_s, +-- [....all other ports....] +-- DI => cpu_din_s, +-- DO => cpu_dout_s +-- ); +-- cpu_din_s <= cpu_dout_s when cpu_rwn_s='0' else +-- [....other sources from peripherals and memories...] +-- +-- ----- IMPORTANT NOTES ----- +-- + +library IEEE; + use IEEE.std_logic_1164.all; + use IEEE.numeric_std.all; + use work.T65_Pack.all; + +entity T65 is + port( + Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65C816 + Res_n : in std_logic; + Enable : in std_logic; + Clk : in std_logic; + Rdy : in std_logic; + Abort_n : in std_logic; + IRQ_n : in std_logic; + NMI_n : in std_logic; + SO_n : in std_logic; + R_W_n : out std_logic; + Sync : out std_logic; + EF : out std_logic; + MF : out std_logic; + XF : out std_logic; + ML_n : out std_logic; + VP_n : out std_logic; + VDA : out std_logic; + VPA : out std_logic; + A : out std_logic_vector(23 downto 0); + DI : in std_logic_vector(7 downto 0); + DO : out std_logic_vector(7 downto 0); + -- 6502 registers (MSB) PC, SP, P, Y, X, A (LSB) + Regs : out std_logic_vector(63 downto 0); + DEBUG : out T_t65_dbg + ); +end T65; + +architecture rtl of T65 is + + -- Registers + signal ABC, X, Y : std_logic_vector(15 downto 0); + signal P, AD, DL : std_logic_vector(7 downto 0) := x"00"; + signal PwithB : std_logic_vector(7 downto 0);--ML:New way to push P with correct B state to stack + signal BAH : std_logic_vector(7 downto 0); + signal BAL : std_logic_vector(8 downto 0); + signal PBR : std_logic_vector(7 downto 0); + signal DBR : std_logic_vector(7 downto 0); + signal PC : unsigned(15 downto 0); + signal S : unsigned(15 downto 0); + signal EF_i : std_logic; + signal MF_i : std_logic; + signal XF_i : std_logic; + + signal IR : std_logic_vector(7 downto 0); + signal MCycle : std_logic_vector(2 downto 0); + + signal Mode_r : std_logic_vector(1 downto 0); + signal ALU_Op_r : T_ALU_Op; + signal Write_Data_r : T_Write_Data; + signal Set_Addr_To_r : T_Set_Addr_To; + signal PCAdder : unsigned(8 downto 0); + + signal RstCycle : std_logic; + signal IRQCycle : std_logic; + signal NMICycle : std_logic; + + signal SO_n_o : std_logic; + signal IRQ_n_o : std_logic; + signal NMI_n_o : std_logic; + signal NMIAct : std_logic; + + signal Break : std_logic; + + -- ALU signals + signal BusA : std_logic_vector(7 downto 0); + signal BusA_r : std_logic_vector(7 downto 0); + signal BusB : std_logic_vector(7 downto 0); + signal BusB_r : std_logic_vector(7 downto 0); + signal ALU_Q : std_logic_vector(7 downto 0); + signal P_Out : std_logic_vector(7 downto 0); + + -- Micro code outputs + signal LCycle : std_logic_vector(2 downto 0); + signal ALU_Op : T_ALU_Op; + signal Set_BusA_To : T_Set_BusA_To; + signal Set_Addr_To : T_Set_Addr_To; + signal Write_Data : T_Write_Data; + signal Jump : std_logic_vector(1 downto 0); + signal BAAdd : std_logic_vector(1 downto 0); + signal BreakAtNA : std_logic; + signal ADAdd : std_logic; + signal AddY : std_logic; + signal PCAdd : std_logic; + signal Inc_S : std_logic; + signal Dec_S : std_logic; + signal LDA : std_logic; + signal LDP : std_logic; + signal LDX : std_logic; + signal LDY : std_logic; + signal LDS : std_logic; + signal LDDI : std_logic; + signal LDALU : std_logic; + signal LDAD : std_logic; + signal LDBAL : std_logic; + signal LDBAH : std_logic; + signal SaveP : std_logic; + signal Write : std_logic; + + signal Res_n_i : std_logic; + signal Res_n_d : std_logic; + + signal really_rdy : std_logic; + signal WRn_i : std_logic; + + signal NMI_entered : std_logic; + +begin + -- gate Rdy with read/write to make an "OK, it's really OK to stop the processor + really_rdy <= Rdy or not(WRn_i); + Sync <= '1' when MCycle = "000" else '0'; + EF <= EF_i; + MF <= MF_i; + XF <= XF_i; + R_W_n <= WRn_i; + ML_n <= '0' when IR(7 downto 6) /= "10" and IR(2 downto 1) = "11" and MCycle(2 downto 1) /= "00" else '1'; + VP_n <= '0' when IRQCycle = '1' and (MCycle = "101" or MCycle = "110") else '1'; + VDA <= '1' when Set_Addr_To_r /= Set_Addr_To_PBR else '0'; + VPA <= '1' when Jump(1) = '0' else '0'; + + -- debugging signals + DEBUG.I <= IR; + DEBUG.A <= ABC(7 downto 0); + DEBUG.X <= X(7 downto 0); + DEBUG.Y <= Y(7 downto 0); + DEBUG.S <= std_logic_vector(S(7 downto 0)); + DEBUG.P <= P; + + Regs <= std_logic_vector(PC) & std_logic_vector(S)& P & Y(7 downto 0) & X(7 downto 0) & ABC(7 downto 0); + + mcode : entity work.T65_MCode + port map( +--inputs + Mode => Mode_r, + IR => IR, + MCycle => MCycle, + P => P, +--outputs + LCycle => LCycle, + ALU_Op => ALU_Op, + Set_BusA_To => Set_BusA_To, + Set_Addr_To => Set_Addr_To, + Write_Data => Write_Data, + Jump => Jump, + BAAdd => BAAdd, + BreakAtNA => BreakAtNA, + ADAdd => ADAdd, + AddY => AddY, + PCAdd => PCAdd, + Inc_S => Inc_S, + Dec_S => Dec_S, + LDA => LDA, + LDP => LDP, + LDX => LDX, + LDY => LDY, + LDS => LDS, + LDDI => LDDI, + LDALU => LDALU, + LDAD => LDAD, + LDBAL => LDBAL, + LDBAH => LDBAH, + SaveP => SaveP, + Write => Write + ); + + alu : entity work.T65_ALU + port map( + Mode => Mode_r, + Op => ALU_Op_r, + BusA => BusA_r, + BusB => BusB, + P_In => P, + P_Out => P_Out, + Q => ALU_Q + ); + + -- the 65xx design requires at least two clock cycles before + -- starting its reset sequence (according to datasheet) + process (Res_n, Clk) + begin + if Res_n = '0' then + Res_n_i <= '0'; + Res_n_d <= '0'; + elsif Clk'event and Clk = '1' then + Res_n_i <= Res_n_d; + Res_n_d <= '1'; + end if; + end process; + + process (Res_n_i, Clk) + begin + if Res_n_i = '0' then + PC <= (others => '0'); -- Program Counter + IR <= "00000000"; + S <= (others => '0'); -- Dummy + PBR <= (others => '0'); + DBR <= (others => '0'); + + Mode_r <= (others => '0'); + ALU_Op_r <= ALU_OP_BIT; + Write_Data_r <= Write_Data_DL; + Set_Addr_To_r <= Set_Addr_To_PBR; + + WRn_i <= '1'; + EF_i <= '1'; + MF_i <= '1'; + XF_i <= '1'; + + elsif Clk'event and Clk = '1' then + if (Enable = '1') then + if (really_rdy = '1') then + WRn_i <= not Write or RstCycle; + + PBR <= (others => '1'); -- Dummy + DBR <= (others => '1'); -- Dummy + EF_i <= '0'; -- Dummy + MF_i <= '0'; -- Dummy + XF_i <= '0'; -- Dummy + + if MCycle = "000" then + Mode_r <= Mode; + + if IRQCycle = '0' and NMICycle = '0' then + PC <= PC + 1; + end if; + + if IRQCycle = '1' or NMICycle = '1' then + IR <= "00000000"; + else + IR <= DI; + end if; + + if LDS = '1' then -- LAS won't work properly if not limited to machine cycle 0 + S(7 downto 0) <= unsigned(ALU_Q); + end if; + end if; + + ALU_Op_r <= ALU_Op; + Write_Data_r <= Write_Data; + if Break = '1' then + Set_Addr_To_r <= Set_Addr_To_PBR; + else + Set_Addr_To_r <= Set_Addr_To; + end if; + + if Inc_S = '1' then + S <= S + 1; + end if; + if Dec_S = '1' and RstCycle = '0' then + S <= S - 1; + end if; + + if IR = "00000000" and MCycle = "001" and IRQCycle = '0' and NMICycle = '0' then + PC <= PC + 1; + end if; + -- + -- jump control logic + -- + case Jump is + when "01" => + PC <= PC + 1; + when "10" => + PC <= unsigned(DI & DL); + when "11" => + if PCAdder(8) = '1' then + if DL(7) = '0' then + PC(15 downto 8) <= PC(15 downto 8) + 1; + else + PC(15 downto 8) <= PC(15 downto 8) - 1; + end if; + end if; + PC(7 downto 0) <= PCAdder(7 downto 0); + when others => null; + end case; + end if; + end if; + end if; + end process; + + PCAdder <= resize(PC(7 downto 0),9) + resize(unsigned(DL(7) & DL),9) when PCAdd = '1' + else "0" & PC(7 downto 0); + + process (Res_n_i, Clk) + variable tmpP:std_logic_vector(7 downto 0);--Lets try to handle loading P at mcycle=0 and set/clk flags at same cycle + begin + if Res_n_i = '0' then + P <= x"00"; -- ensure we have nothing set on reset + elsif Clk'event and Clk = '1' then + tmpP:=P; + if (Enable = '1') then + if (really_rdy = '1') then + if MCycle = "000" then + if LDA = '1' then + ABC(7 downto 0) <= ALU_Q; + end if; + if LDX = '1' then + X(7 downto 0) <= ALU_Q; + end if; + if LDY = '1' then + Y(7 downto 0) <= ALU_Q; + end if; + if (LDA or LDX or LDY) = '1' then + tmpP:=P_Out; + end if; + end if; + if SaveP = '1' then + tmpP:=P_Out; + end if; + if LDP = '1' then + tmpP:=ALU_Q; + end if; + if IR(4 downto 0) = "11000" then + case IR(7 downto 5) is + when "000" =>--0x18(clc) + tmpP(Flag_C) := '0'; + when "001" =>--0x38(sec) + tmpP(Flag_C) := '1'; + when "010" =>--0x58(cli) + tmpP(Flag_I) := '0'; + when "011" =>--0x78(sei) + tmpP(Flag_I) := '1'; + when "101" =>--0xb8(clv) + tmpP(Flag_V) := '0'; + when "110" =>--0xd8(cld) + tmpP(Flag_D) := '0'; + when "111" =>--0xf8(sed) + tmpP(Flag_D) := '1'; + when others => + end case; + end if; + tmpP(Flag_B) := '1'; + if IR = "00000000" and MCycle = "100" and RstCycle = '0' then + --This should happen after P has been pushed to stack + tmpP(Flag_I) := '1'; + end if; + if SO_n_o = '1' and SO_n = '0' then + tmpP(Flag_V) := '1'; + end if; + if RstCycle = '1' then + tmpP(Flag_I) := '1'; + tmpP(Flag_D) := '0'; + end if; + tmpP(Flag_1) := '1'; + + P<=tmpP;--new way + + SO_n_o <= SO_n; + if IR(4 downto 0)/="10000" or Jump/="01" then -- delay interrupts during branches (checked with Lorenz test and real 6510), not best way yet, though - but works... + IRQ_n_o <= IRQ_n; + end if; + end if; + -- detect nmi even if not rdy + if IR(4 downto 0)/="10000" or Jump/="01" then -- delay interrupts during branches (checked with Lorenz test and real 6510) not best way yet, though - but works... + NMI_n_o <= NMI_n; + end if; + end if; + end if; + end process; + +--------------------------------------------------------------------------- +-- +-- Buses +-- +--------------------------------------------------------------------------- + + process (Res_n_i, Clk) + begin + if Res_n_i = '0' then + BusA_r <= (others => '0'); + BusB <= (others => '0'); + BusB_r <= (others => '0'); + AD <= (others => '0'); + BAL <= (others => '0'); + BAH <= (others => '0'); + DL <= (others => '0'); + elsif Clk'event and Clk = '1' then + if (Enable = '1') then + NMI_entered <= '0'; + if (really_rdy = '1') then + BusA_r <= BusA; + BusB <= DI; + + -- not really nice, but no better way found yet ! + if Set_Addr_To_r = Set_Addr_To_PBR or Set_Addr_To_r = Set_Addr_To_ZPG then + BusB_r <= std_logic_vector(unsigned(DI(7 downto 0)) + 1); -- required for SHA + end if; + + case BAAdd is + when "01" => + -- BA Inc + AD <= std_logic_vector(unsigned(AD) + 1); + BAL <= std_logic_vector(unsigned(BAL) + 1); + when "10" => + -- BA Add + BAL <= std_logic_vector(resize(unsigned(BAL(7 downto 0)),9) + resize(unsigned(BusA),9)); + when "11" => + -- BA Adj + if BAL(8) = '1' then + BAH <= std_logic_vector(unsigned(BAH) + 1); + end if; + when others => + end case; + + -- modified to use Y register as well + if ADAdd = '1' then + if (AddY = '1') then + AD <= std_logic_vector(unsigned(AD) + unsigned(Y(7 downto 0))); + else + AD <= std_logic_vector(unsigned(AD) + unsigned(X(7 downto 0))); + end if; + end if; + + if IR = "00000000" then + BAL <= (others => '1'); + BAH <= (others => '1'); + if RstCycle = '1' then + BAL(2 downto 0) <= "100"; + elsif NMICycle = '1' or (NMIAct = '1' and MCycle="100") or NMI_entered='1' then + BAL(2 downto 0) <= "010"; + if MCycle="100" then + NMI_entered <= '1'; + end if; + else + BAL(2 downto 0) <= "110"; + end if; + if Set_addr_To_r = Set_Addr_To_BA then + BAL(0) <= '1'; + end if; + end if; + + if LDDI = '1' then + DL <= DI; + end if; + if LDALU = '1' then + DL <= ALU_Q; + end if; + if LDAD = '1' then + AD <= DI; + end if; + if LDBAL = '1' then + BAL(7 downto 0) <= DI; + end if; + if LDBAH = '1' then + BAH <= DI; + end if; + end if; + end if; + end if; + end process; + + Break <= (BreakAtNA and not BAL(8)) or (PCAdd and not PCAdder(8)); + + with Set_BusA_To select + BusA <= + DI when Set_BusA_To_DI, + ABC(7 downto 0) when Set_BusA_To_ABC, + X(7 downto 0) when Set_BusA_To_X, + Y(7 downto 0) when Set_BusA_To_Y, + std_logic_vector(S(7 downto 0)) when Set_BusA_To_S, + P when Set_BusA_To_P, + ABC(7 downto 0) and DI when Set_BusA_To_DA, + (ABC(7 downto 0) or x"ee") and DI when Set_BusA_To_DAO,--ee for OAL instruction. constant may be different on other platforms.TODO:Move to generics + (ABC(7 downto 0) or x"ee") and DI and X(7 downto 0) when Set_BusA_To_DAX,--XAA, ee for OAL instruction. constant may be different on other platforms.TODO:Move to generics + ABC(7 downto 0) and X(7 downto 0) when Set_BusA_To_AAX,--SAX, SHA + (others => '-') when Set_BusA_To_DONTCARE;--Can probably remove this + + with Set_Addr_To_r select + A <= + "0000000000000001" & std_logic_vector(S(7 downto 0)) when Set_Addr_To_SP, + DBR & "00000000" & AD when Set_Addr_To_ZPG, + "00000000" & BAH & BAL(7 downto 0) when Set_Addr_To_BA, + PBR & std_logic_vector(PC(15 downto 8)) & std_logic_vector(PCAdder(7 downto 0)) when Set_Addr_To_PBR; + + -- This is the P that gets pushed on stack with correct B flag. I'm not sure if NMI also clears B, but I guess it does. + PwithB<=(P and x"ef") when (IRQCycle='1' or NMICycle='1') else P; + + with Write_Data_r select + DO <= + DL when Write_Data_DL, + ABC(7 downto 0) when Write_Data_ABC, + X(7 downto 0) when Write_Data_X, + Y(7 downto 0) when Write_Data_Y, + std_logic_vector(S(7 downto 0)) when Write_Data_S, + PwithB when Write_Data_P, + std_logic_vector(PC(7 downto 0)) when Write_Data_PCL, + std_logic_vector(PC(15 downto 8)) when Write_Data_PCH, + ABC(7 downto 0) and X(7 downto 0) when Write_Data_AX, + ABC(7 downto 0) and X(7 downto 0) and BusB_r(7 downto 0) when Write_Data_AXB, -- no better way found yet... + X(7 downto 0) and BusB_r(7 downto 0) when Write_Data_XB, -- no better way found yet... + Y(7 downto 0) and BusB_r(7 downto 0) when Write_Data_YB, -- no better way found yet... + (others=>'-') when Write_Data_DONTCARE;--Can probably remove this + + +------------------------------------------------------------------------- +-- +-- Main state machine +-- +------------------------------------------------------------------------- + + process (Res_n_i, Clk) + begin + if Res_n_i = '0' then + MCycle <= "001"; + RstCycle <= '1'; + IRQCycle <= '0'; + NMICycle <= '0'; + NMIAct <= '0'; + elsif Clk'event and Clk = '1' then + if (Enable = '1') then + if (really_rdy = '1') then + if MCycle = LCycle or Break = '1' then + MCycle <= "000"; + RstCycle <= '0'; + IRQCycle <= '0'; + NMICycle <= '0'; + if NMIAct = '1' and IR/=x"00" then -- delay NMI further if we just executed a BRK + NMICycle <= '1'; + NMIAct <= '0'; -- reset NMI edge detector if we start processing the NMI + elsif IRQ_n_o = '0' and P(Flag_I) = '0' then + IRQCycle <= '1'; + end if; + else + MCycle <= std_logic_vector(unsigned(MCycle) + 1); + end if; + end if; + --detect NMI even if not rdy + if NMI_n_o = '1' and (NMI_n = '0' and (IR(4 downto 0)/="10000" or Jump/="01")) then -- branches have influence on NMI start (not best way yet, though - but works...) + NMIAct <= '1'; + end if; + -- we entered NMI during BRK instruction + if NMI_entered='1' then + NMIAct <= '0'; + end if; + end if; + end if; + end process; + +end; diff --git a/cores/bbc/rtl/T65/T65_ALU.vhd b/cores/bbc/rtl/T65/T65_ALU.vhd new file mode 100755 index 0000000..c076ab0 --- /dev/null +++ b/cores/bbc/rtl/T65/T65_ALU.vhd @@ -0,0 +1,293 @@ +-- **** +-- T65(b) core. In an effort to merge and maintain bug fixes .... +-- +-- See list of changes in T65 top file (T65.vhd)... +-- +-- **** +-- 65xx compatible microprocessor core +-- +-- FPGAARCADE SVN: $Id: T65_ALU.vhd 1234 2015-02-28 20:14:50Z wolfgang.scherr $ +-- +-- Copyright (c) 2002...2015 +-- Daniel Wallner (jesus opencores org) +-- Mike Johnson (mikej fpgaarcade com) +-- Wolfgang Scherr (WoS pin4 at> +-- Morten Leikvoll () +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author(s), but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- Limitations : +-- See in T65 top file (T65.vhd)... + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use work.T65_Pack.all; + +entity T65_ALU is + port( + Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65816 + Op : in T_ALU_OP; + BusA : in std_logic_vector(7 downto 0); + BusB : in std_logic_vector(7 downto 0); + P_In : in std_logic_vector(7 downto 0); + P_Out : out std_logic_vector(7 downto 0); + Q : out std_logic_vector(7 downto 0) + ); +end T65_ALU; + +architecture rtl of T65_ALU is + + -- AddSub variables (temporary signals) + signal ADC_Z : std_logic; + signal ADC_C : std_logic; + signal ADC_V : std_logic; + signal ADC_N : std_logic; + signal ADC_Q : std_logic_vector(7 downto 0); + signal SBC_Z : std_logic; + signal SBC_C : std_logic; + signal SBC_V : std_logic; + signal SBC_N : std_logic; + signal SBC_Q : std_logic_vector(7 downto 0); + signal SBX_Q : std_logic_vector(7 downto 0); + +begin + + process (P_In, BusA, BusB) + variable AL : unsigned(6 downto 0); + variable AH : unsigned(6 downto 0); + variable C : std_logic; + begin + AL := resize(unsigned(BusA(3 downto 0) & P_In(Flag_C)), 7) + resize(unsigned(BusB(3 downto 0) & "1"), 7); + AH := resize(unsigned(BusA(7 downto 4) & AL(5)), 7) + resize(unsigned(BusB(7 downto 4) & "1"), 7); + +-- pragma translate_off + if is_x(std_logic_vector(AL)) then AL := "0000000"; end if; + if is_x(std_logic_vector(AH)) then AH := "0000000"; end if; +-- pragma translate_on + + if AL(4 downto 1) = 0 and AH(4 downto 1) = 0 then + ADC_Z <= '1'; + else + ADC_Z <= '0'; + end if; + + if AL(5 downto 1) > 9 and P_In(Flag_D) = '1' then + AL(6 downto 1) := AL(6 downto 1) + 6; + end if; + + C := AL(6) or AL(5); + AH := resize(unsigned(BusA(7 downto 4) & C), 7) + resize(unsigned(BusB(7 downto 4) & "1"), 7); + + ADC_N <= AH(4); + ADC_V <= (AH(4) xor BusA(7)) and not (BusA(7) xor BusB(7)); + +-- pragma translate_off + if is_x(std_logic_vector(AH)) then AH := "0000000"; end if; +-- pragma translate_on + + if AH(5 downto 1) > 9 and P_In(Flag_D) = '1' then + AH(6 downto 1) := AH(6 downto 1) + 6; + end if; + + ADC_C <= AH(6) or AH(5); + + ADC_Q <= std_logic_vector(AH(4 downto 1) & AL(4 downto 1)); + end process; + + process (Op, P_In, BusA, BusB) + variable AL : unsigned(6 downto 0); + variable AH : unsigned(5 downto 0); + variable C : std_logic; + variable CT : std_logic; + begin + CT:='0'; + if( Op=ALU_OP_AND or --"0001" These OpCodes used to have LSB set + Op=ALU_OP_ADC or --"0011" + Op=ALU_OP_EQ2 or --"0101" + Op=ALU_OP_SBC or --"0111" + Op=ALU_OP_ROL or --"1001" + Op=ALU_OP_ROR or --"1011" +-- Op=ALU_OP_EQ3 or --"1101" + Op=ALU_OP_INC --"1111" + ) then + CT:='1'; + end if; + + C := P_In(Flag_C) or not CT;--was: or not Op(0); + AL := resize(unsigned(BusA(3 downto 0) & C), 7) - resize(unsigned(BusB(3 downto 0) & "1"), 6); + AH := resize(unsigned(BusA(7 downto 4) & "0"), 6) - resize(unsigned(BusB(7 downto 4) & AL(5)), 6); + + -- pragma translate_off + if is_x(std_logic_vector(AL)) then AL := "0000000"; end if; + if is_x(std_logic_vector(AH)) then AH := "000000"; end if; + -- pragma translate_on + + if AL(4 downto 1) = 0 and AH(4 downto 1) = 0 then + SBC_Z <= '1'; + else + SBC_Z <= '0'; + end if; + + SBC_C <= not AH(5); + SBC_V <= (AH(4) xor BusA(7)) and (BusA(7) xor BusB(7)); + SBC_N <= AH(4); + + SBX_Q <= std_logic_vector(AH(4 downto 1) & AL(4 downto 1)); + + if P_In(Flag_D) = '1' then + if AL(5) = '1' then + AL(5 downto 1) := AL(5 downto 1) - 6; + end if; + AH := resize(unsigned(BusA(7 downto 4) & "0"), 6) - resize(unsigned(BusB(7 downto 4) & AL(6)), 6); + if AH(5) = '1' then + AH(5 downto 1) := AH(5 downto 1) - 6; + end if; + end if; + + SBC_Q <= std_logic_vector(AH(4 downto 1) & AL(4 downto 1)); + end process; + + process (Op, P_In, BusA, BusB, + ADC_Z, ADC_C, ADC_V, ADC_N, ADC_Q, + SBC_Z, SBC_C, SBC_V, SBC_N, SBC_Q, + SBX_Q) + variable Q_t : std_logic_vector(7 downto 0); + variable Q2_t : std_logic_vector(7 downto 0); + begin + -- ORA, AND, EOR, ADC, NOP, LD, CMP, SBC + -- ASL, ROL, LSR, ROR, BIT, LD, DEC, INC + P_Out <= P_In; + Q_t := BusA; + Q2_t := BusA; + case Op is + when ALU_OP_OR=> + Q_t := BusA or BusB; + when ALU_OP_AND=> + Q_t := BusA and BusB; + when ALU_OP_EOR=> + Q_t := BusA xor BusB; + when ALU_OP_ADC=> + P_Out(Flag_V) <= ADC_V; + P_Out(Flag_C) <= ADC_C; + Q_t := ADC_Q; + when ALU_OP_CMP=> + P_Out(Flag_C) <= SBC_C; + when ALU_OP_SAX=> + P_Out(Flag_C) <= SBC_C; + Q_t := SBX_Q; -- undoc: subtract (A & X) - (immediate) + when ALU_OP_SBC=> + P_Out(Flag_V) <= SBC_V; + P_Out(Flag_C) <= SBC_C; + Q_t := SBC_Q; -- undoc: subtract (A & X) - (immediate), then decimal correction + when ALU_OP_ASL=> + Q_t := BusA(6 downto 0) & "0"; + P_Out(Flag_C) <= BusA(7); + when ALU_OP_ROL=> + Q_t := BusA(6 downto 0) & P_In(Flag_C); + P_Out(Flag_C) <= BusA(7); + when ALU_OP_LSR=> + Q_t := "0" & BusA(7 downto 1); + P_Out(Flag_C) <= BusA(0); + when ALU_OP_ROR=> + Q_t := P_In(Flag_C) & BusA(7 downto 1); + P_Out(Flag_C) <= BusA(0); + when ALU_OP_ARR=> + Q_t := P_In(Flag_C) & (BusA(7 downto 1) and BusB(7 downto 1)); + P_Out(Flag_V) <= Q_t(5) xor Q_t(6); + Q2_t := Q_t; + if P_In(Flag_D)='1' then + if (BusA(3 downto 0) and BusB(3 downto 0)) > "0100" then + Q2_t(3 downto 0) := std_logic_vector(unsigned(Q_t(3 downto 0)) + x"6"); + end if; + if (BusA(7 downto 4) and BusB(7 downto 4)) > "0100" then + Q2_t(7 downto 4) := std_logic_vector(unsigned(Q_t(7 downto 4)) + x"6"); + P_Out(Flag_C) <= '1'; + else + P_Out(Flag_C) <= '0'; + end if; + else + P_Out(Flag_C) <= Q_t(6); + end if; + when ALU_OP_BIT=> + P_Out(Flag_V) <= BusB(6); + when ALU_OP_DEC=> + Q_t := std_logic_vector(unsigned(BusA) - 1); + when ALU_OP_INC=> + Q_t := std_logic_vector(unsigned(BusA) + 1); + when others => + null; + --EQ1,EQ2,EQ3 passes BusA to Q_t and P_in to P_out + end case; + + case Op is + when ALU_OP_ADC=> + P_Out(Flag_N) <= ADC_N; + P_Out(Flag_Z) <= ADC_Z; + when ALU_OP_CMP|ALU_OP_SBC|ALU_OP_SAX=> + P_Out(Flag_N) <= SBC_N; + P_Out(Flag_Z) <= SBC_Z; + when ALU_OP_EQ1=>--dont touch P + when ALU_OP_BIT=> + P_Out(Flag_N) <= BusB(7); + if (BusA and BusB) = "00000000" then + P_Out(Flag_Z) <= '1'; + else + P_Out(Flag_Z) <= '0'; + end if; + when ALU_OP_ANC=> + P_Out(Flag_N) <= Q_t(7); + P_Out(Flag_C) <= Q_t(7); + if Q_t = "00000000" then + P_Out(Flag_Z) <= '1'; + else + P_Out(Flag_Z) <= '0'; + end if; + when others => + P_Out(Flag_N) <= Q_t(7); + if Q_t = "00000000" then + P_Out(Flag_Z) <= '1'; + else + P_Out(Flag_Z) <= '0'; + end if; + end case; + + if Op=ALU_OP_ARR then + -- handled above in ARR code + Q <= Q2_t; + else + Q <= Q_t; + end if; + end process; + +end; diff --git a/cores/bbc/rtl/T65/T65_MCode.vhd b/cores/bbc/rtl/T65/T65_MCode.vhd new file mode 100755 index 0000000..867e0b8 --- /dev/null +++ b/cores/bbc/rtl/T65/T65_MCode.vhd @@ -0,0 +1,1239 @@ +-- **** +-- T65(b) core. In an effort to merge and maintain bug fixes .... +-- +-- See list of changes in T65 top file (T65.vhd)... +-- +-- **** +-- 65xx compatible microprocessor core +-- +-- FPGAARCADE SVN: $Id: T65_MCode.vhd 1234 2015-02-28 20:14:50Z wolfgang.scherr $ +-- +-- Copyright (c) 2002...2015 +-- Daniel Wallner (jesus opencores org) +-- Mike Johnson (mikej fpgaarcade com) +-- Wolfgang Scherr (WoS pin4 at> +-- Morten Leikvoll () +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author(s), but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- Limitations : +-- See in T65 top file (T65.vhd)... + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use ieee.std_logic_unsigned.all; +use work.T65_Pack.all; + +entity T65_MCode is + port( + Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65816 + IR : in std_logic_vector(7 downto 0); + MCycle : in T_Lcycle; + P : in std_logic_vector(7 downto 0); + LCycle : out T_Lcycle; + ALU_Op : out T_ALU_Op; + Set_BusA_To : out T_Set_BusA_To; -- DI,A,X,Y,S,P,DA,DAO,DAX,AAX + Set_Addr_To : out T_Set_Addr_To; -- PC Adder,S,AD,BA + Write_Data : out T_Write_Data; -- DL,A,X,Y,S,P,PCL,PCH,AX,AXB,XB,YB + Jump : out std_logic_vector(1 downto 0); -- PC,++,DIDL,Rel + BAAdd : out std_logic_vector(1 downto 0); -- None,DB Inc,BA Add,BA Adj + BreakAtNA : out std_logic; + ADAdd : out std_logic; + AddY : out std_logic; + PCAdd : out std_logic; + Inc_S : out std_logic; + Dec_S : out std_logic; + LDA : out std_logic; + LDP : out std_logic; + LDX : out std_logic; + LDY : out std_logic; + LDS : out std_logic; + LDDI : out std_logic; + LDALU : out std_logic; + LDAD : out std_logic; + LDBAL : out std_logic; + LDBAH : out std_logic; + SaveP : out std_logic; + Write : out std_logic + ); +end T65_MCode; + +architecture rtl of T65_MCode is + + signal Branch : std_logic; + signal ALUmore:std_logic; + +begin + + with IR(7 downto 5) select + Branch <= not P(Flag_N) when "000", + P(Flag_N) when "001", + not P(Flag_V) when "010", + P(Flag_V) when "011", + not P(Flag_C) when "100", + P(Flag_C) when "101", + not P(Flag_Z) when "110", + P(Flag_Z) when others; + + process (IR, MCycle, P, Branch, Mode) + begin + lCycle <= Cycle_1; + Set_BusA_To <= Set_BusA_To_ABC; + Set_Addr_To <= Set_Addr_To_PBR; + Write_Data <= Write_Data_DL; + Jump <= (others => '0'); + BAAdd <= "00"; + BreakAtNA <= '0'; + ADAdd <= '0'; + PCAdd <= '0'; + Inc_S <= '0'; + Dec_S <= '0'; + LDA <= '0'; + LDP <= '0'; + LDX <= '0'; + LDY <= '0'; + LDS <= '0'; + LDDI <= '0'; + LDALU <= '0'; + LDAD <= '0'; + LDBAL <= '0'; + LDBAH <= '0'; + SaveP <= '0'; + Write <= '0'; + AddY <= '0'; + ALUmore <= '0'; + + case IR(7 downto 5) is + when "100" => -- covers $8x,$9x + case IR(1 downto 0) is + when "00" => -- IR: $80,$84,$88,$8C,$90,$94,$98,$9C + Set_BusA_To <= Set_BusA_To_Y; + if IR(4 downto 2)="111" then -- SYA ($9C) + Write_Data <= Write_Data_YB; + else + Write_Data <= Write_Data_Y; + end if; + when "10" => -- IR: $82,$86,$8A,$8E,$92,$96,$9A,$9E + Set_BusA_To <= Set_BusA_To_X; + if IR(4 downto 2)="111" then -- SXA ($9E) + Write_Data <= Write_Data_XB; + else + Write_Data <= Write_Data_X; + end if; + when "11" => -- IR: $83,$87,$8B,$8F,$93,$97,$9B,$9F + if IR(4 downto 2)="110" then -- SHS ($9B) + Set_BusA_To <= Set_BusA_To_AAX; + LDS <= '1'; + else + Set_BusA_To <= Set_BusA_To_ABC; + end if; + if IR(4 downto 2)="111" or IR(4 downto 2)="110" or IR(4 downto 2)="100" then -- SHA ($9F, $93), SHS ($9B) + Write_Data <= Write_Data_AXB; + else + Write_Data <= Write_Data_AX; + end if; + when others => -- IR: $81,$85,$89,$8D,$91,$95,$99,$9D + Write_Data <= Write_Data_ABC; + end case; + when "101" => -- covers $Ax,$Bx + Set_BusA_To <= Set_BusA_To_DI; + case IR(1 downto 0) is + when "00" => -- IR: $A0,$A4,$A8,$AC,$B0,$B4,$B8,$BC + if IR(4) /= '1' or IR(2) /= '0' then--only for $A0,$A4,$A8,$AC or $B4,$BC + LDY <= '1'; + end if; + when "01" => -- IR: $A1,$A5,$A9,$AD,$B1,$B5,$B9,$BD + LDA <= '1'; + when "10" => -- IR: $A2,$A6,$AA,$AE,$B2,$B6,$BA,$BE + LDX <= '1'; + when others => -- IR: $A3,$A7,$AB,$AF,$B3,$B7,$BB,$BF (undoc) + LDX <= '1'; + LDA <= '1'; + if IR(4 downto 2)="110" then -- LAS (BB) + Set_BusA_To <= Set_BusA_To_S; + LDS <= '1'; + end if; + end case; + when "110" => -- covers $Cx,$Dx + case IR(1 downto 0) is + when "00" => -- IR: $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC + if IR(4) = '0' then--only for $Cx + LDY <= '1'; + end if; + Set_BusA_To <= Set_BusA_To_Y; + when others => -- IR: $C1,$C5,$C9,$CD,$D1,$D5,$D9,$DD, $C2,$C6,$CA,$CE,$D2,$D6,$DA,$DE, $C3,$C7,$CB,$CF,$D3,$D7,$DB,$DF + Set_BusA_To <= Set_BusA_To_ABC; + end case; + when "111" => -- covers $Ex,$Fx + case IR(1 downto 0) is + when "00" => -- IR: $E0,$E4,$E8,$EC,$F0,$F4,$F8,$FC + if IR(4) = '0' then -- only $Ex + LDX <= '1'; + end if; + Set_BusA_To <= Set_BusA_To_X; + when others => -- IR: $E1,$E5,$E9,$ED,$F1,$F5,$F9,$FD, $E2,$E6,$EA,$EE,$F2,$F6,$FA,$FE, $E3,$E7,$EB,$EF,$F3,$F7,$FB,$FF + Set_BusA_To <= Set_BusA_To_ABC; + end case; + when others => + end case; + + if IR(7 downto 6) /= "10" and IR(1) = '1' and (mode="00" or IR(0)='0') then--covers $0x-$7x, $Cx-$Fx x=2,3,6,7,A,B,E,F, for 6502 undocs + if IR=x"eb" then + Set_BusA_To <= Set_BusA_To_ABC; -- alternate SBC ($EB) + else + Set_BusA_To <= Set_BusA_To_DI; + end if; + end if; + + case IR(4 downto 0) is + -- IR: $00,$20,$40,$60,$80,$A0,$C0,$E0 + -- $08,$28,$48,$68,$88,$A8,$C8,$E8 + -- $0A,$2A,$4A,$6A,$8A,$AA,$CA,$EA + -- $18,$38,$58,$78,$98,$B8,$D8,$F8 + -- $1A,$3A,$5A,$7A,$9A,$BA,$DA,$FA + when "00000" | "01000" | "01010" | "11000" | "11010" => + -- Implied + case IR is + when x"00" => + -- BRK ($00) + lCycle <= Cycle_6; + case MCycle is + when Cycle_1 => + Set_Addr_To <= Set_Addr_To_SP; + Write_Data <= Write_Data_PCH; + Write <= '1'; + when Cycle_2 => + Dec_S <= '1'; + Set_Addr_To <= Set_Addr_To_SP; + Write_Data <= Write_Data_PCL; + Write <= '1'; + when Cycle_3 => + Dec_S <= '1'; + Set_Addr_To <= Set_Addr_To_SP; + Write_Data <= Write_Data_P; + Write <= '1'; + when Cycle_4 => + Dec_S <= '1'; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_5 => + LDDI <= '1'; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_6 => + Jump <= "10"; + when others => + end case; + when x"20" => -- JSR ($20) + lCycle <= Cycle_5; + case MCycle is + when Cycle_1 => + Jump <= "01"; + LDDI <= '1'; + Set_Addr_To <= Set_Addr_To_SP; + when Cycle_2 => + Set_Addr_To <= Set_Addr_To_SP; + Write_Data <= Write_Data_PCH; + Write <= '1'; + when Cycle_3 => + Dec_S <= '1'; + Set_Addr_To <= Set_Addr_To_SP; + Write_Data <= Write_Data_PCL; + Write <= '1'; + when Cycle_4 => + Dec_S <= '1'; + when Cycle_5 => + Jump <= "10"; + when others => + end case; + when x"40" => -- RTI ($40) + lCycle <= Cycle_5; + case MCycle is + when Cycle_1 => + Set_Addr_To <= Set_Addr_To_SP; + when Cycle_2 => + Inc_S <= '1'; + Set_Addr_To <= Set_Addr_To_SP; + when Cycle_3 => + Inc_S <= '1'; + Set_Addr_To <= Set_Addr_To_SP; + Set_BusA_To <= Set_BusA_To_DI; + when Cycle_4 => + LDP <= '1'; + Inc_S <= '1'; + LDDI <= '1'; + Set_Addr_To <= Set_Addr_To_SP; + when Cycle_5 => + Jump <= "10"; + when others => + end case; + when x"60" => -- RTS ($60) + lCycle <= Cycle_5; + case MCycle is + when Cycle_1 => + Set_Addr_To <= Set_Addr_To_SP; + when Cycle_2 => + Inc_S <= '1'; + Set_Addr_To <= Set_Addr_To_SP; + when Cycle_3 => + Inc_S <= '1'; + LDDI <= '1'; + Set_Addr_To <= Set_Addr_To_SP; + when Cycle_4 => + Jump <= "10"; + when Cycle_5 => + Jump <= "01"; + when others => + end case; + when x"08" | x"48" | x"5a" | x"da" => -- PHP, PHA, PHY*, PHX* ($08,$48,$5A,$DA) + lCycle <= Cycle_2; + if Mode = "00" and IR(1) = '1' then--2 cycle nop + lCycle <= Cycle_1; + end if; + case MCycle is + when Cycle_1 => + if mode/="00" or IR(1)='0' then --wrong on 6502 + Write <= '1'; + case IR(7 downto 4) is + when "0000" => + Write_Data <= Write_Data_P; + when "0100" => + Write_Data <= Write_Data_ABC; + when "0101" => + if Mode /= "00" then + Write_Data <= Write_Data_Y; + else + Write <= '0'; + end if; + when "1101" => + if Mode /= "00" then + Write_Data <= Write_Data_X; + else + Write <= '0'; + end if; + when others => + end case; + Set_Addr_To <= Set_Addr_To_SP; + end if; + when Cycle_2 => + Dec_S <= '1'; + when others => + end case; + when x"28" | x"68" | x"7a" | x"fa" => -- PLP, PLA, PLY*, PLX* ($28,$68,$7A,$FA) + lCycle <= Cycle_3; + if Mode = "00" and IR(1) = '1' then--2 cycle nop + lCycle <= Cycle_1; + end if; + case IR(7 downto 4) is + when "0010" =>--plp + LDP <= '1'; + when "0110" =>--pla + LDA <= '1'; + when "0111" =>--ply not for 6502 + if Mode /= "00" then + LDY <= '1'; + end if; + when "1111" =>--plx not for 6502 + if Mode /= "00" then + LDX <= '1'; + end if; + when others => + end case; + case MCycle is + when Cycle_sync => + if Mode /= "00" or IR(1) = '0' then--wrong on 6502 + SaveP <= '1'; + end if; + when Cycle_1 => + if Mode /= "00" or IR(1) = '0' then--wrong on 6502 + Set_Addr_To <= Set_Addr_To_SP; + LDP <= '0'; + end if; + when Cycle_2 => + Inc_S <= '1'; + Set_Addr_To <= Set_Addr_To_SP; + LDP <= '0'; + when Cycle_3 => + Set_BusA_To <= Set_BusA_To_DI; + when others => + end case; + when x"a0" | x"c0" | x"e0" => -- LDY, CPY, CPX ($A0,$C0,$E0) + -- Immediate + case MCycle is + when Cycle_sync => + when Cycle_1 => + Jump <= "01"; + when others => + end case; + when x"88" => -- DEY ($88) + LDY <= '1'; + case MCycle is + when Cycle_sync => + when Cycle_1 => + Set_BusA_To <= Set_BusA_To_Y; + when others => + end case; + when x"ca" => -- DEX ($CA) + LDX <= '1'; + case MCycle is + when Cycle_sync => + when Cycle_1 => + Set_BusA_To <= Set_BusA_To_X; + when others => + end case; + when x"1a" | x"3a" => -- INC*, DEC* ($1A,$3A) + if Mode /= "00" then + LDA <= '1'; -- A + else + lCycle <= Cycle_1;--undoc 2 cycle nop + end if; + case MCycle is + when Cycle_sync => + when Cycle_1 => + Set_BusA_To <= Set_BusA_To_S; + when others => + end case; + when x"0a" | x"2a" | x"4a" | x"6a" => -- ASL, ROL, LSR, ROR ($0A,$2A,$4A,$6A) + LDA <= '1'; -- A + Set_BusA_To <= Set_BusA_To_ABC; + case MCycle is + when Cycle_sync => + when Cycle_1 => + when others => + end case; + when x"8a" | x"98" => -- TYA, TXA ($8A,$98) + LDA <= '1'; + case MCycle is + when Cycle_sync => + when Cycle_1 => + when others => + end case; + when x"aa" | x"a8" => -- TAX, TAY ($AA,$A8) + case MCycle is + when Cycle_sync => + when Cycle_1 => + Set_BusA_To <= Set_BusA_To_ABC; + when others => + end case; + when x"9a" => -- TXS ($9A) + LDS <= '1'; -- will be set only in Cycle_sync + when x"ba" => -- TSX ($BA) + LDX <= '1'; + case MCycle is + when Cycle_sync => + when Cycle_1 => + Set_BusA_To <= Set_BusA_To_S; + when others => + end case; + when x"80" => -- undoc: NOP imm2 ($80) + case MCycle is + when Cycle_sync => + when Cycle_1 => + Jump <= "01"; + when others => + end case; + when others => -- others ($0A,$EA, $18,$38,$58,$78,$B8,$C8,$D8,$E8,$F8) + case MCycle is + when Cycle_sync => + when others => + end case; + end case; + + -- IR: $01,$21,$41,$61,$81,$A1,$C1,$E1 + -- $03,$23,$43,$63,$83,$A3,$C3,$E3 + when "00001" | "00011" => + -- Zero Page Indexed Indirect (d,x) + lCycle <= Cycle_5; + if IR(7 downto 6) /= "10" then -- ($01,$21,$41,$61,$C1,$E1,$03,$23,$43,$63,$C3,$E3) + LDA <= '1'; + if Mode="00" and IR(1)='1' then + lCycle <= Cycle_7; + end if; + end if; + case MCycle is + when Cycle_1 => + Jump <= "01"; + LDAD <= '1'; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_2 => + ADAdd <= '1'; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_3 => + BAAdd <= "01"; + LDBAL <= '1'; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_4 => + LDBAH <= '1'; + if IR(7 downto 5) = "100" then + Write <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_5=> + if Mode="00" and IR(1)='1' and IR(7 downto 6)/="10" then + Set_Addr_To <= Set_Addr_To_BA; + Write <= '1'; + LDDI<='1'; + end if; + when Cycle_6=> + Write <= '1'; + LDALU<='1'; + SaveP<='1'; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_7 => + ALUmore <= '1'; + Set_BusA_To <= Set_BusA_To_ABC; + when others => + end case; + + -- IR: $09,$29,$49,$69,$89,$A9,$C9,$E9 + when "01001" => + -- Immediate + if IR(7 downto 5)/="100" then -- all except undoc. NOP imm2 (not $89) + LDA <= '1'; + end if; + case MCycle is + when Cycle_1 => + Jump <= "01"; + when others => + end case; + + -- IR: $0B,$2B,$4B,$6B,$8B,$AB,$CB,$EB + when "01011" => + if Mode="00" then + -- Immediate undoc for 6500 + case IR(7 downto 5) is + when "010"|"011"|"000"|"001" =>--ALR,ARR + Set_BusA_To<=Set_BusA_To_DA; + LDA <= '1'; + when "100" =>--XAA + Set_BusA_To<=Set_BusA_To_DAX; + LDA <= '1'; + when "110" =>--SAX (SBX) + Set_BusA_To<=Set_BusA_To_AAX; + LDX <= '1'; + when "101" =>--OAL + Set_BusA_To<=Set_BusA_To_DAO; + LDA <= '1'; + when others=> + LDA <= '1'; + end case; + case MCycle is + when Cycle_1 => + Jump <= "01"; + when others => + end case; + end if; + + -- IR: $02,$22,$42,$62,$82,$A2,$C2,$E2 + -- $12,$32,$52,$72,$92,$B2,$D2,$F2 + when "00010" | "10010" => + -- Immediate, SKB, KIL + case MCycle is + when Cycle_sync => + when Cycle_1 => + if IR = "10100010" then + -- LDX ($A2) + Jump <= "01"; + LDX <= '1'; -- Moved, Lorenz test showed X changing on SKB (NOPx) + elsif IR(7 downto 4)="1000" or IR(7 downto 4)="1100" or IR(7 downto 4)="1110" then + -- undoc: NOP imm2 + Jump <= "01"; + else + -- KIL !!! + end if; + when others => + end case; + + -- IR: $04,$24,$44,$64,$84,$A4,$C4,$E4 + when "00100" => + -- Zero Page + lCycle <= Cycle_2; + case MCycle is + when Cycle_sync => + if IR(7 downto 5) = "001" then--24=BIT zpg + SaveP <= '1'; + end if; + when Cycle_1 => + Jump <= "01"; + LDAD <= '1'; + if IR(7 downto 5) = "100" then--84=sty zpg (the only write in this group) + Write <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_2 => + when others => + end case; + + -- IR: $05,$25,$45,$65,$85,$A5,$C5,$E5 + -- $06,$26,$46,$66,$86,$A6,$C6,$E6 + -- $07,$27,$47,$67,$87,$A7,$C7,$E7 + when "00101" | "00110" | "00111" => + -- Zero Page + if IR(7 downto 6) /= "10" and IR(1) = '1' and (mode="00" or IR(0)='0') then--covers 0x-7x,cx-fx x=2,3,6,7,a,b,e,f, for 6502 undocs + -- Read-Modify-Write + lCycle <= Cycle_4; + if Mode="00" and IR(0)='1' then + LDA<='1'; + end if; + case MCycle is + when Cycle_1 => + Jump <= "01"; + LDAD <= '1'; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_2 => + LDDI <= '1'; + if Mode="00" then--The old 6500 writes back what is just read, before changing. The 65c does another read + Write <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_3 => + LDALU <= '1'; + SaveP <= '1'; + Write <= '1'; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_4 => + if Mode="00" and IR(0)='1' then + Set_BusA_To<=Set_BusA_To_ABC; + ALUmore <= '1'; -- For undoc DCP/DCM support + LDDI <= '1'; -- requires DIN to reflect DOUT! + end if; + when others => + end case; + else + lCycle <= Cycle_2; + if IR(7 downto 6) /= "10" then + LDA <= '1'; + end if; + case MCycle is + when Cycle_sync => + when Cycle_1 => + Jump <= "01"; + LDAD <= '1'; + if IR(7 downto 5) = "100" then + Write <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_2 => + when others => + end case; + end if; + + -- IR: $0C,$2C,$4C,$6C,$8C,$AC,$CC,$EC + when "01100" => + -- Absolute + if IR(7 downto 6) = "01" and IR(4 downto 0) = "01100" then -- JMP ($4C,$6C) + if IR(5) = '0' then + lCycle <= Cycle_2; + case MCycle is + when Cycle_1 => + Jump <= "01"; + LDDI <= '1'; + when Cycle_2 => + Jump <= "10"; + when others => + end case; + else + lCycle <= Cycle_4; + case MCycle is + when Cycle_1 => + Jump <= "01"; + LDDI <= '1'; + LDBAL <= '1'; + when Cycle_2 => + LDBAH <= '1'; + if Mode /= "00" then + Jump <= "10"; + end if; + if Mode = "00" then + Set_Addr_To <= Set_Addr_To_BA; + end if; + when Cycle_3 => + LDDI <= '1'; + if Mode = "00" then + Set_Addr_To <= Set_Addr_To_BA; + BAAdd <= "01"; -- DB Inc + else + Jump <= "01"; + end if; + when Cycle_4 => + Jump <= "10"; + when others => + end case; + end if; + else + lCycle <= Cycle_3; + case MCycle is + when Cycle_sync => + if IR(7 downto 5) = "001" then--2c-BIT + SaveP <= '1'; + end if; + when Cycle_1 => + Jump <= "01"; + LDBAL <= '1'; + when Cycle_2 => + Jump <= "01"; + LDBAH <= '1'; + if IR(7 downto 5) = "100" then--80, sty, the only write in this group + Write <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_3 => + when others => + end case; + end if; + + -- IR: $0D,$2D,$4D,$6D,$8D,$AD,$CD,$ED + -- $0E,$2E,$4E,$6E,$8E,$AE,$CE,$EE + -- $0F,$2F,$4F,$6F,$8F,$AF,$CF,$EF + when "01101" | "01110" | "01111" => + -- Absolute + if IR(7 downto 6) /= "10" and IR(1) = '1' and (mode="00" or IR(0)='0') then -- ($0E,$2E,$4E,$6E,$CE,$EE, $0F,$2F,$4F,$6F,$CF,$EF) + -- Read-Modify-Write + lCycle <= Cycle_5; + if Mode="00" and IR(0) = '1' then + LDA <= '1'; + end if; + case MCycle is + when Cycle_1 => + Jump <= "01"; + LDBAL <= '1'; + when Cycle_2 => + Jump <= "01"; + LDBAH <= '1'; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_3 => + LDDI <= '1'; + if Mode="00" then--The old 6500 writes back what is just read, before changing. The 65c does another read + Write <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_4 => + Write <= '1'; + LDALU <= '1'; + SaveP <= '1'; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_5 => + if Mode="00" and IR(0)='1' then + ALUmore <= '1'; -- For undoc DCP/DCM support + Set_BusA_To<=Set_BusA_To_ABC; + end if; + when others => + end case; + else + lCycle <= Cycle_3; + if IR(7 downto 6) /= "10" then -- all but $8D, $8E, $8F, $AD, $AE, $AF ($AD does set LDA in an earlier case statement) + LDA <= '1'; + end if; + case MCycle is + when Cycle_sync => + when Cycle_1 => + Jump <= "01"; + LDBAL <= '1'; + when Cycle_2 => + Jump <= "01"; + LDBAH <= '1'; + if IR(7 downto 5) = "100" then--8d + Write <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_3 => + when others => + end case; + end if; + + -- IR: $10,$30,$50,$70,$90,$B0,$D0,$F0 + when "10000" => + -- Relative + -- This circuit dictates when the last + -- microcycle occurs for the branch depending on + -- whether or not the branch is taken and if a page + -- is crossed... + if (Branch = '1') then + lCycle <= Cycle_3; -- We're done @ T3 if branching...upper + -- level logic will stop at T2 if no page cross + -- (See the Break signal) + else + lCycle <= Cycle_1; + end if; + -- This decodes the current microcycle and takes the + -- proper course of action... + case MCycle is + -- On the T1 microcycle, increment the program counter + -- and instruct the upper level logic to fetch the offset + -- from the Din bus and store it in the data latches. This + -- will be the last microcycle if the branch isn't taken. + when Cycle_1 => + Jump <= "01"; -- Increments the PC by one (PC will now be PC+2) + -- from microcycle T0. + LDDI <= '1'; -- Tells logic in top level (T65.vhd) to route + -- the Din bus to the memory data latch (DL) + -- so that the branch offset is fetched. + -- In microcycle T2, tell the logic in the top level to + -- add the offset. If the most significant byte of the + -- program counter (i.e. the current "page") does not need + -- updating, we are done here...the Break signal at the + -- T65.vhd level takes care of that... + when Cycle_2 => + Jump <= "11"; -- Tell the PC Jump logic to use relative mode. + PCAdd <= '1'; -- This tells the PC adder to update itself with + -- the current offset recently fetched from + -- memory. + -- The following is microcycle T3 : + -- The program counter should be completely updated + -- on this cycle after the page cross is detected. + -- We don't need to do anything here... + when Cycle_3 => + when others => null; -- Do nothing. + end case; + + -- IR: $11,$31,$51,$71,$91,$B1,$D1,$F1 + -- $13,$33,$53,$73,$93,$B3,$D3,$F3 + when "10001" | "10011" => + lCycle <= Cycle_5; + if IR(7 downto 6) /= "10" then -- ($11,$31,$51,$71,$D1,$F1,$13,$33,$53,$73,$D3,$F3) + LDA <= '1'; + if Mode="00" and IR(1)='1' then + lCycle <= Cycle_7; + end if; + end if; + case MCycle is + when Cycle_1 => + Jump <= "01"; + LDAD <= '1'; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_2 => + LDBAL <= '1'; + BAAdd <= "01"; -- DB Inc + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_3 => + Set_BusA_To <= Set_BusA_To_Y; + BAAdd <= "10"; -- BA Add + LDBAH <= '1'; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_4 => + BAAdd <= "11"; -- BA Adj + if IR(7 downto 5) = "100" then + Write <= '1'; + elsif IR(1)='0' or IR=x"B3" then -- Dont do this on $x3, except undoc LAXiy $B3 (says real CPU and Lorenz tests) + BreakAtNA <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_5 => + if Mode="00" and IR(1)='1' and IR(7 downto 6)/="10" then + Set_Addr_To <= Set_Addr_To_BA; + LDDI<='1'; + Write <= '1'; + end if; + when Cycle_6 => + LDALU<='1'; + SaveP<='1'; + Write <= '1'; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_7 => + ALUmore <= '1'; + Set_BusA_To<=Set_BusA_To_ABC; + when others => + end case; + + -- IR: $14,$34,$54,$74,$94,$B4,$D4,$F4 + -- $15,$35,$55,$75,$95,$B5,$D5,$F5 + -- $16,$36,$56,$76,$96,$B6,$D6,$F6 + -- $17,$37,$57,$77,$97,$B7,$D7,$F7 + when "10100" | "10101" | "10110" | "10111" => + -- Zero Page, X + if IR(7 downto 6) /= "10" and IR(1) = '1' and (Mode="00" or IR(0)='0') then -- ($16,$36,$56,$76,$D6,$F6, $17,$37,$57,$77,$D7,$F7) + -- Read-Modify-Write + if Mode="00" and IR(0)='1' then + LDA<='1'; + end if; + lCycle <= Cycle_5; + case MCycle is + when Cycle_1 => + Jump <= "01"; + LDAD <= '1'; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_2 => + ADAdd <= '1'; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_3 => + LDDI <= '1'; + if Mode="00" then -- The old 6500 writes back what is just read, before changing. The 65c does another read + Write <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_4 => + LDALU <= '1'; + SaveP <= '1'; + Write <= '1'; + Set_Addr_To <= Set_Addr_To_ZPG; + if Mode="00" and IR(0)='1' then + LDDI<='1'; + end if; + when Cycle_5 => + if Mode="00" and IR(0)='1' then + ALUmore <= '1'; -- For undoc DCP/DCM support + Set_BusA_To<=Set_BusA_To_ABC; + end if; + when others => + end case; + else + lCycle <= Cycle_3; + if IR(7 downto 6) /= "10" and IR(0)='1' then -- dont LDA on undoc skip + LDA <= '1'; + end if; + case MCycle is + when Cycle_sync => + when Cycle_1 => + Jump <= "01"; + LDAD <= '1'; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_2 => + ADAdd <= '1'; + -- Added this check for Y reg. use, added undocs + if (IR(3 downto 1) = "011") then -- ($16,$36,$56,$76,$96,$B6,$D6,$F6,$17,$37,$57,$77,$97,$B7,$D7,$F7) + AddY <= '1'; + end if; + if IR(7 downto 5) = "100" then -- ($14,$34,$15,$35,$16,$36,$17,$37) the only write instruction + Write <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_ZPG; + when Cycle_3 => null; + when others => + end case; + end if; + + -- IR: $19,$39,$59,$79,$99,$B9,$D9,$F9 + -- $1B,$3B,$5B,$7B,$9B,$BB,$DB,$FB + when "11001" | "11011" => + -- Absolute Y + lCycle <= Cycle_4; + if IR(7 downto 6) /= "10" then + LDA <= '1'; + if Mode="00" and IR(1)='1' then + lCycle <= Cycle_6; + end if; + end if; + case MCycle is + when Cycle_1 => + Jump <= "01"; + LDBAL <= '1'; + when Cycle_2 => + Jump <= "01"; + Set_BusA_To <= Set_BusA_To_Y; + BAAdd <= "10"; -- BA Add + LDBAH <= '1'; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_3 => + BAAdd <= "11"; -- BA adj + if IR(7 downto 5) = "100" then--99/9b + Write <= '1'; + elsif IR(1)='0' or IR=x"BB" then -- Dont do this on $xB, except undoc $BB (says real CPU and Lorenz tests) + BreakAtNA <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_4 => -- just for undoc + if Mode="00" and IR(1)='1' and IR(7 downto 6)/="10" then + Set_Addr_To <= Set_Addr_To_BA; + LDDI<='1'; + Write <= '1'; + end if; + when Cycle_5 => + Write <= '1'; + LDALU<='1'; + Set_Addr_To <= Set_Addr_To_BA; + SaveP<='1'; + when Cycle_6 => + ALUmore <= '1'; + Set_BusA_To <= Set_BusA_To_ABC; + when others => + end case; + + -- IR: $1C,$3C,$5C,$7C,$9C,$BC,$DC,$FC + -- $1D,$3D,$5D,$7D,$9D,$BD,$DD,$FD + -- $1E,$3E,$5E,$7E,$9E,$BE,$DE,$FE + -- $1F,$3F,$5F,$7F,$9F,$BF,$DF,$FF + when "11100" | "11101" | "11110" | "11111" => + -- Absolute X + if IR(7 downto 6) /= "10" and IR(1) = '1' and (Mode="00" or IR(0)='0') then -- ($1E,$3E,$5E,$7E,$DE,$FE, $1F,$3F,$5F,$7F,$DF,$FF) + -- Read-Modify-Write + lCycle <= Cycle_6; + if Mode="00" and IR(0)='1' then + LDA <= '1'; + end if; + case MCycle is + when Cycle_1 => + Jump <= "01"; + LDBAL <= '1'; + when Cycle_2 => + Jump <= "01"; + Set_BusA_To <= Set_BusA_To_X; + BAAdd <= "10"; -- BA Add + LDBAH <= '1'; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_3 => + BAAdd <= "11"; -- BA adj + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_4 => + LDDI <= '1'; + if Mode="00" then--The old 6500 writes back what is just read, before changing. The 65c does another read + Write <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_5 => + LDALU <= '1'; + SaveP <= '1'; + Write <= '1'; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_6 => + if Mode="00" and IR(0)='1' then + ALUmore <= '1'; + Set_BusA_To <= Set_BusA_To_ABC; + end if; + when others => + end case; + else -- ($1C,$3C,$5C,$7C,$9C,$BC,$DC,$FC, $1D,$3D,$5D,$7D,$9D,$BD,$DD,$FD, $9E,$BE,$9F,$BF) + lCycle <= Cycle_4;--Or 3 if not page crossing + if IR(7 downto 6) /= "10" then + if Mode/="00" or IR(4)='0' or IR(1 downto 0)/="00" then + LDA <= '1'; + end if; + end if; + case MCycle is + when Cycle_sync => + when Cycle_1 => + Jump <= "01"; + LDBAL <= '1'; + when Cycle_2 => + Jump <= "01"; + -- special case $BE which uses Y reg as index!! + if(IR(7 downto 6)="10" and IR(4 downto 1)="1111") then + Set_BusA_To <= Set_BusA_To_Y; + else + Set_BusA_To <= Set_BusA_To_X; + end if; + BAAdd <= "10"; -- BA Add + LDBAH <= '1'; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_3 => + BAAdd <= "11"; -- BA adj + if IR(7 downto 5) = "100" then -- ($9E,$9F) + Write <= '1'; + else + BreakAtNA <= '1'; + end if; + Set_Addr_To <= Set_Addr_To_BA; + when Cycle_4 => + when others => + end case; + end if; + when others => + end case; + end process; + + process (IR, MCycle, Mode,ALUmore) + begin + -- ORA, AND, EOR, ADC, NOP, LD, CMP, SBC + -- ASL, ROL, LSR, ROR, BIT, LD, DEC, INC + case IR(1 downto 0) is + when "00" => + case IR(4 downto 2) is + -- IR: $00,$20,$40,$60,$80,$A0,$C0,$E0 + -- $04,$24,$44,$64,$84,$A4,$C4,$E4 + -- $0C,$2C,$4C,$6C,$8C,$AC,$CC,$EC + when "000" | "001" | "011" => + case IR(7 downto 5) is + when "110" | "111" => -- CP ($C0,$C4,$CC,$E0,$E4,$EC) + ALU_Op <= ALU_OP_CMP; + when "101" => -- LD ($A0,$A4,$AC) + ALU_Op <= ALU_OP_EQ2; + when "001" => -- BIT ($20,$24,$2C - $20 is ignored, as its a jmp) + ALU_Op <= ALU_OP_BIT; + when others => -- other, NOP/ST ($x0,$x4,$xC) + ALU_Op <= ALU_OP_EQ1; + end case; + + -- IR: $08,$28,$48,$68,$88,$A8,$C8,$E8 + when "010" => + case IR(7 downto 5) is + when "111" | "110" => -- IN ($C8,$E8) + ALU_Op <= ALU_OP_INC; + when "100" => -- DEY ($88) + ALU_Op <= ALU_OP_DEC; + when others => -- LD + ALU_Op <= ALU_OP_EQ2; + end case; + + -- IR: $18,$38,$58,$78,$98,$B8,$D8,$F8 + when "110" => + case IR(7 downto 5) is + when "100" => -- TYA ($98) + ALU_Op <= ALU_OP_EQ2; + when others => + ALU_Op <= ALU_OP_EQ1; + end case; + + -- IR: $10,$30,$50,$70,$90,$B0,$D0,$F0 + -- $14,$34,$54,$74,$94,$B4,$D4,$F4 + -- $1C,$3C,$5C,$7C,$9C,$BC,$DC,$FC + when others => + case IR(7 downto 5) is + when "101" => -- LD ($B0,$B4,$BC) + ALU_Op <= ALU_OP_EQ2; + when others => + ALU_Op <= ALU_OP_EQ1; + end case; + end case; + + when "01" => -- OR + case(to_integer(unsigned(IR(7 downto 5)))) is + when 0=> -- IR: $01,$05,$09,$0D,$11,$15,$19,$1D + ALU_Op<=ALU_OP_OR; + when 1=> -- IR: $21,$25,$29,$2D,$31,$35,$39,$3D + ALU_Op<=ALU_OP_AND; + when 2=> -- IR: $41,$45,$49,$4D,$51,$55,$59,$5D + ALU_Op<=ALU_OP_EOR; + when 3=> -- IR: $61,$65,$69,$6D,$71,$75,$79,$7D + ALU_Op<=ALU_OP_ADC; + when 4=>-- IR: $81,$85,$89,$8D,$91,$95,$99,$9D + ALU_Op<=ALU_OP_EQ1; -- STA + when 5=> -- IR: $A1,$A5,$A9,$AD,$B1,$B5,$B9,$BD + ALU_Op<=ALU_OP_EQ2; -- LDA + when 6=> -- IR: $C1,$C5,$C9,$CD,$D1,$D5,$D9,$DD + ALU_Op<=ALU_OP_CMP; + when others=> -- IR: $E1,$E5,$E9,$ED,$F1,$F5,$F9,$FD + ALU_Op<=ALU_OP_SBC; + end case; + + when "10" => + case(to_integer(unsigned(IR(7 downto 5)))) is + when 0=> -- IR: $02,$06,$0A,$0E,$12,$16,$1A,$1E + ALU_Op<=ALU_OP_ASL; + if IR(4 downto 2) = "110" and Mode/="00" then -- 00011010,$1A -> INC acc, not on 6502 + ALU_Op <= ALU_OP_INC; + end if; + when 1=> -- IR: $22,$26,$2A,$2E,$32,$36,$3A,$3E + ALU_Op<=ALU_OP_ROL; + if IR(4 downto 2) = "110" and Mode/="00" then -- 00111010,$3A -> DEC acc, not on 6502 + ALU_Op <= ALU_OP_DEC; + end if; + when 2=> -- IR: $42,$46,$4A,$4E,$52,$56,$5A,$5E + ALU_Op<=ALU_OP_LSR; + when 3=> -- IR: $62,$66,$6A,$6E,$72,$76,$7A,$7E + ALU_Op<=ALU_OP_ROR; + when 4=> -- IR: $82,$86,$8A,$8E,$92,$96,$9A,$9E + ALU_Op<=ALU_OP_BIT; + if IR(4 downto 2) = "010" then -- 10001010, $8A -> TXA + ALU_Op <= ALU_OP_EQ2; + else -- 100xxx10, $82,$86,$8E,$92,$96,$9A,$9E + ALU_Op <= ALU_OP_EQ1; + end if; + when 5=> -- IR: $A2,$A6,$AA,$AE,$B2,$B6,$BA,$BE + ALU_Op<=ALU_OP_EQ2; -- LDX + when 6=> -- IR: $C2,$C6,$CA,$CE,$D2,$D6,$DA,$DE + ALU_Op<=ALU_OP_DEC; + when others=> -- IR: $E2,$E6,$EA,$EE,$F2,$F6,$FA,$FE + ALU_Op<=ALU_OP_INC; + end case; + + when others => -- "11" undoc double alu ops + case(to_integer(unsigned(IR(7 downto 5)))) is + -- IR: $A3,$A7,$AB,$AF,$B3,$B7,$BB,$BF + when 5 => + if IR=x"bb" then--LAS + ALU_Op <= ALU_OP_AND; + else + ALU_Op <= ALU_OP_EQ2; + end if; + + -- IR: $03,$07,$0B,$0F,$13,$17,$1B,$1F + -- $23,$27,$2B,$2F,$33,$37,$3B,$3F + -- $43,$47,$4B,$4F,$53,$57,$5B,$5F + -- $63,$67,$6B,$6F,$73,$77,$7B,$7F + -- $83,$87,$8B,$8F,$93,$97,$9B,$9F + -- $C3,$C7,$CB,$CF,$D3,$D7,$DB,$DF + -- $E3,$E7,$EB,$EF,$F3,$F7,$FB,$FF + when others => + if IR=x"6b" then -- ARR + ALU_Op<=ALU_OP_ARR; + elsif IR=x"8b" then -- ARR + ALU_Op<=ALU_OP_XAA; -- we can't use the bit operation as we don't set all flags... + elsif IR=x"0b" or IR=x"2b" then -- ANC + ALU_Op<=ALU_OP_ANC; + elsif IR=x"eb" then -- alternate SBC + ALU_Op<=ALU_OP_SBC; + elsif ALUmore='1' then + case(to_integer(unsigned(IR(7 downto 5)))) is + when 0=> + ALU_Op<=ALU_OP_OR; + when 1=> + ALU_Op<=ALU_OP_AND; + when 2=> + ALU_Op<=ALU_OP_EOR; + when 3=> + ALU_Op<=ALU_OP_ADC; + when 4=> + ALU_Op<=ALU_OP_EQ1; -- STA + when 5=> + ALU_Op<=ALU_OP_EQ2; -- LDA + when 6=> + ALU_Op<=ALU_OP_CMP; + when others=> + ALU_Op<=ALU_OP_SBC; + end case; + else + case(to_integer(unsigned(IR(7 downto 5)))) is + when 0=> + ALU_Op<=ALU_OP_ASL; + when 1=> + ALU_Op<=ALU_OP_ROL; + when 2=> + ALU_Op<=ALU_OP_LSR; + when 3=> + ALU_Op<=ALU_OP_ROR; + when 4=> + ALU_Op<=ALU_OP_BIT; + when 5=> + ALU_Op<=ALU_OP_EQ2; -- LDX + when 6=> + ALU_Op<=ALU_OP_DEC; + if IR(4 downto 2)="010" then -- $6B + ALU_Op<=ALU_OP_SAX; -- special SAX (SBX) case + end if; + when others=> + ALU_Op<=ALU_OP_INC; + end case; + end if; + end case; + end case; + end process; + +end; diff --git a/cores/bbc/rtl/T65/T65_Pack.vhd b/cores/bbc/rtl/T65/T65_Pack.vhd new file mode 100755 index 0000000..f56c343 --- /dev/null +++ b/cores/bbc/rtl/T65/T65_Pack.vhd @@ -0,0 +1,180 @@ +-- **** +-- T65(b) core. In an effort to merge and maintain bug fixes .... +-- +-- See list of changes in T65 top file (T65.vhd)... +-- +-- **** +-- 65xx compatible microprocessor core +-- +-- FPGAARCADE SVN: $Id: T65_Pack.vhd 1234 2015-02-28 20:14:50Z wolfgang.scherr $ +-- +-- Copyright (c) 2002...2015 +-- Daniel Wallner (jesus opencores org) +-- Mike Johnson (mikej fpgaarcade com) +-- Wolfgang Scherr (WoS pin4 at> +-- Morten Leikvoll () +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author(s), but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- Limitations : +-- See in T65 top file (T65.vhd)... + +library IEEE; +use IEEE.std_logic_1164.all; + +package T65_Pack is + + constant Flag_C : integer := 0; + constant Flag_Z : integer := 1; + constant Flag_I : integer := 2; + constant Flag_D : integer := 3; + constant Flag_B : integer := 4; + constant Flag_1 : integer := 5; + constant Flag_V : integer := 6; + constant Flag_N : integer := 7; + + subtype T_Lcycle is std_logic_vector(2 downto 0); + constant Cycle_sync :T_Lcycle:="000"; + constant Cycle_1 :T_Lcycle:="001"; + constant Cycle_2 :T_Lcycle:="010"; + constant Cycle_3 :T_Lcycle:="011"; + constant Cycle_4 :T_Lcycle:="100"; + constant Cycle_5 :T_Lcycle:="101"; + constant Cycle_6 :T_Lcycle:="110"; + constant Cycle_7 :T_Lcycle:="111"; + + function CycleNext(c:T_Lcycle) return T_Lcycle; + + type T_Set_BusA_To is + ( + Set_BusA_To_DI, + Set_BusA_To_ABC, + Set_BusA_To_X, + Set_BusA_To_Y, + Set_BusA_To_S, + Set_BusA_To_P, + Set_BusA_To_DA, + Set_BusA_To_DAO, + Set_BusA_To_DAX, + Set_BusA_To_AAX, + Set_BusA_To_DONTCARE + ); + + type T_Set_Addr_To is + ( + Set_Addr_To_PBR, + Set_Addr_To_SP, + Set_Addr_To_ZPG, + Set_Addr_To_BA + ); + + type T_Write_Data is + ( + Write_Data_DL, + Write_Data_ABC, + Write_Data_X, + Write_Data_Y, + Write_Data_S, + Write_Data_P, + Write_Data_PCL, + Write_Data_PCH, + Write_Data_AX, + Write_Data_AXB, + Write_Data_XB, + Write_Data_YB, + Write_Data_DONTCARE + ); + + type T_ALU_OP is + ( + ALU_OP_OR, --"0000" + ALU_OP_AND, --"0001" + ALU_OP_EOR, --"0010" + ALU_OP_ADC, --"0011" + ALU_OP_EQ1, --"0100" EQ1 does not change N,Z flags, EQ2/3 does. + ALU_OP_EQ2, --"0101" Not sure yet whats the difference between EQ2&3. They seem to do the same ALU op + ALU_OP_CMP, --"0110" + ALU_OP_SBC, --"0111" + ALU_OP_ASL, --"1000" + ALU_OP_ROL, --"1001" + ALU_OP_LSR, --"1010" + ALU_OP_ROR, --"1011" + ALU_OP_BIT, --"1100" +-- ALU_OP_EQ3, --"1101" + ALU_OP_DEC, --"1110" + ALU_OP_INC, --"1111" + ALU_OP_ARR, + ALU_OP_ANC, + ALU_OP_SAX, + ALU_OP_XAA +-- ALU_OP_UNDEF--"----"--may be replaced with any? + ); + + type T_t65_dbg is record + I : std_logic_vector(7 downto 0); -- instruction + A : std_logic_vector(7 downto 0); -- A reg + X : std_logic_vector(7 downto 0); -- X reg + Y : std_logic_vector(7 downto 0); -- Y reg + S : std_logic_vector(7 downto 0); -- stack pointer + P : std_logic_vector(7 downto 0); -- processor flags + end record; + +end; + +package body T65_Pack is + + function CycleNext(c:T_Lcycle) return T_Lcycle is + begin + case(c) is + when Cycle_sync=> + return Cycle_1; + when Cycle_1=> + return Cycle_2; + when Cycle_2=> + return Cycle_3; + when Cycle_3=> + return Cycle_4; + when Cycle_4=> + return Cycle_5; + when Cycle_5=> + return Cycle_6; + when Cycle_6=> + return Cycle_7; + when Cycle_7=> + return Cycle_sync; + when others=> + return Cycle_sync; + end case; + end CycleNext; + +end T65_Pack; diff --git a/cores/bbc/rtl/bbc.v b/cores/bbc/rtl/bbc.v index 584091a..be67b15 100644 --- a/cores/bbc/rtl/bbc.v +++ b/cores/bbc/rtl/bbc.v @@ -115,7 +115,6 @@ wire cpu_we; wire [23:0] cpu_a; wire [7:0] cpu_di; -reg [7:0] cpu_di_r; wire [7:0] cpu_do; // CRTC signals @@ -284,32 +283,23 @@ address_decode ADDRDECODE( .mhz1_enable(mhz1_enable) ); -/*input clk; // CPU clock -input reset; // reset signal -output reg [15:0] AB; // address bus -input [7:0] DI; // data in, read bus -output [7:0] DO; // data out, write bus -output WE; // write enable -input IRQ; // interrupt request -input NMI; // non-maskable interrupt request -input RDY; // Ready signal. Pauses CPU when RDY=0 -*/ - -cpu CPU ( - .clk ( CLK32M_I ), - .reset ( ~reset_n ), +T65 CPU ( + .Mode (CPU_MODE), + .Res_n (reset_n), + .Enable (cpu_clken), + .Clk (CLK32M_I), + .Rdy (cpu_clken), + .Abort_n(cpu_abort_n), + .NMI_n (cpu_nmi_n), + .IRQ_n (cpu_irq_n), + .SO_n (cpu_so_n), + .R_W_n (cpu_r_nw), - .IRQ ( ~cpu_irq_n ), - .NMI ( ~cpu_nmi_n ), - - .WE ( cpu_we ), - .AB ( cpu_a[15:0] ), - .DI ( cpu_di_r ), - .DO ( cpu_do ), - .RDY ( cpu_clken ) + .DI (cpu_di), + .DO (cpu_do), + .A (cpu_a) ); -assign cpu_r_nw = ~cpu_we; m6522 SYS_VIA ( // System VIA is reset by power on reset only @@ -502,12 +492,10 @@ saa5050 TELETEXT ( initial begin : via_init - //user_via_cb1_in = 1'b 0; - user_via_ca2_in = 1'b 0; - user_via_ca1_in = 1'b 0; - user_via_ca2_in = 1'b 0; - crtc_lpstb = 1'b 0; - + user_via_ca1_in = 1'b 0; + user_via_ca2_in = 1'b 0; + crtc_lpstb = 1'b 0; + end // This is needed as in v003 of the 6522 data out is only valid while I_P2_H is asserted @@ -548,11 +536,6 @@ always @(posedge CLK32M_I) begin end - // retard DI by one cpu clock cycle - if (cpu_clken === 1'b1) begin - cpu_di_r <= cpu_di; - end - end end diff --git a/cores/bbc/rtl/cpu.v b/cores/bbc/rtl/cpu.v deleted file mode 100755 index 6081128..0000000 --- a/cores/bbc/rtl/cpu.v +++ /dev/null @@ -1,1225 +0,0 @@ -/* - * verilog model of 6502 CPU. - * - * (C) Arlet Ottens, - * - * Feel free to use this code in any project (commercial or not), as long as you - * keep this message, and the copyright notice. This code is provided "as is", - * without any warranties of any kind. - * - */ - -/* - * Note that not all 6502 interface signals are supported (yet). The goal - * is to create an Acorn Atom model, and the Atom didn't use all signals on - * the main board. - * - * The data bus is implemented as separate read/write buses. Combine them - * on the output pads if external memory is required. - */ - -module cpu( clk, reset, AB, DI, DO, WE, IRQ, NMI, RDY ); - -input clk; // CPU clock -input reset; // reset signal -output reg [15:0] AB; // address bus -input [7:0] DI; // data in, read bus -output [7:0] DO; // data out, write bus -output WE; // write enable -input IRQ; // interrupt request -input NMI; // non-maskable interrupt request -input RDY; // Ready signal. Pauses CPU when RDY=0 - -/* - * internal signals - */ - -reg [15:0] PC; // Program Counter -reg [7:0] ABL; // Address Bus Register LSB -reg [7:0] ABH; // Address Bus Register MSB -wire [7:0] ADD; // Adder Hold Register (registered in ALU) - -reg [7:0] DIHOLD; // Hold for Data In -reg DIHOLD_valid; // -wire [7:0] DIMUX; // - -reg [7:0] IRHOLD; // Hold for Instruction register -reg IRHOLD_valid; // Valid instruction in IRHOLD - -reg [7:0] AXYS[3:0]; // A, X, Y and S register file - -reg C = 0; // carry flag (init at zero to avoid X's in ALU sim) -reg Z = 0; // zero flag -reg I = 0; // interrupt flag -reg D = 0; // decimal flag -reg V = 0; // overflow flag -reg N = 0; // negative flag -wire AZ; // ALU Zero flag -wire AV; // ALU overflow flag -wire AN; // ALU negative flag -wire HC; // ALU half carry - -reg [7:0] AI; // ALU Input A -reg [7:0] BI; // ALU Input B -wire [7:0] DI; // Data In -wire [7:0] IR; // Instruction register -reg [7:0] DO; // Data Out -reg WE; // Write Enable -reg CI; // Carry In -wire CO; // Carry Out -wire [7:0] PCH = PC[15:8]; -wire [7:0] PCL = PC[7:0]; - -reg NMI_edge = 0; // captured NMI edge - -reg [1:0] regsel; // Select A, X, Y or S register -wire [7:0] regfile = AXYS[regsel]; // Selected register output - -parameter - SEL_A = 2'd0, - SEL_S = 2'd1, - SEL_X = 2'd2, - SEL_Y = 2'd3; - -/* - * define some signals for watching in simulator output - */ - - -`ifdef SIM -wire [7:0] A = AXYS[SEL_A]; // Accumulator -wire [7:0] X = AXYS[SEL_X]; // X register -wire [7:0] Y = AXYS[SEL_Y]; // Y register -wire [7:0] S = AXYS[SEL_S]; // Stack pointer -`endif - -wire [7:0] P = { N, V, 2'b11, D, I, Z, C }; - -/* - * instruction decoder/sequencer - */ - -reg [5:0] state; - -/* - * control signals - */ - -reg PC_inc; // Increment PC -reg [15:0] PC_temp; // intermediate value of PC - -reg [1:0] src_reg; // source register index -reg [1:0] dst_reg; // destination register index - -reg index_y; // if set, then Y is index reg rather than X -reg load_reg; // loading a register (A, X, Y, S) in this instruction -reg inc; // increment -reg write_back; // set if memory is read/modified/written -reg load_only; // LDA/LDX/LDY instruction -reg store; // doing store (STA/STX/STY) -reg adc_sbc; // doing ADC/SBC -reg compare; // doing CMP/CPY/CPX -reg shift; // doing shift/rotate instruction -reg rotate; // doing rotate (no shift) -reg backwards; // backwards branch -reg cond_true; // branch condition is true -reg [2:0] cond_code; // condition code bits from instruction -reg shift_right; // Instruction ALU shift/rotate right -reg alu_shift_right; // Current cycle shift right enable -reg [3:0] op; // Main ALU operation for instruction -reg [3:0] alu_op; // Current cycle ALU operation -reg adc_bcd; // ALU should do BCD style carry -reg adj_bcd; // results should be BCD adjusted - -/* - * some flip flops to remember we're doing special instructions. These - * get loaded at the DECODE state, and used later - */ -reg bit_ins; // doing BIT instruction -reg plp; // doing PLP instruction -reg php; // doing PHP instruction -reg clc; // clear carry -reg sec; // set carry -reg cld; // clear decimal -reg sed; // set decimal -reg cli; // clear interrupt -reg sei; // set interrupt -reg clv; // clear overflow -reg brk; // doing BRK - -reg res; // in reset - -/* - * ALU operations - */ - -parameter - OP_OR = 4'b1100, - OP_AND = 4'b1101, - OP_EOR = 4'b1110, - OP_ADD = 4'b0011, - OP_SUB = 4'b0111, - OP_ROL = 4'b1011, - OP_A = 4'b1111; - -/* - * Microcode state machine. Basically, every addressing mode has its own - * path through the state machine. Additional information, such as the - * operation, source and destination registers are decoded in parallel, and - * kept in separate flops. - */ - -parameter - ABS0 = 6'd0, // ABS - fetch LSB - ABS1 = 6'd1, // ABS - fetch MSB - ABSX0 = 6'd2, // ABS, X - fetch LSB and send to ALU (+X) - ABSX1 = 6'd3, // ABS, X - fetch MSB and send to ALU (+Carry) - ABSX2 = 6'd4, // ABS, X - Wait for ALU (only if needed) - BRA0 = 6'd5, // Branch - fetch offset and send to ALU (+PC[7:0]) - BRA1 = 6'd6, // Branch - fetch opcode, and send PC[15:8] to ALU - BRA2 = 6'd7, // Branch - fetch opcode (if page boundary crossed) - BRK0 = 6'd8, // BRK/IRQ - push PCH, send S to ALU (-1) - BRK1 = 6'd9, // BRK/IRQ - push PCL, send S to ALU (-1) - BRK2 = 6'd10, // BRK/IRQ - push P, send S to ALU (-1) - BRK3 = 6'd11, // BRK/IRQ - write S, and fetch @ fffe - DECODE = 6'd12, // IR is valid, decode instruction, and write prev reg - FETCH = 6'd13, // fetch next opcode, and perform prev ALU op - INDX0 = 6'd14, // (ZP,X) - fetch ZP address, and send to ALU (+X) - INDX1 = 6'd15, // (ZP,X) - fetch LSB at ZP+X, calculate ZP+X+1 - INDX2 = 6'd16, // (ZP,X) - fetch MSB at ZP+X+1 - INDX3 = 6'd17, // (ZP,X) - fetch data - INDY0 = 6'd18, // (ZP),Y - fetch ZP address, and send ZP to ALU (+1) - INDY1 = 6'd19, // (ZP),Y - fetch at ZP+1, and send LSB to ALU (+Y) - INDY2 = 6'd20, // (ZP),Y - fetch data, and send MSB to ALU (+Carry) - INDY3 = 6'd21, // (ZP),Y) - fetch data (if page boundary crossed) - JMP0 = 6'd22, // JMP - fetch PCL and hold - JMP1 = 6'd23, // JMP - fetch PCH - JMPI0 = 6'd24, // JMP IND - fetch LSB and send to ALU for delay (+0) - JMPI1 = 6'd25, // JMP IND - fetch MSB, proceed with JMP0 state - JSR0 = 6'd26, // JSR - push PCH, save LSB, send S to ALU (-1) - JSR1 = 6'd27, // JSR - push PCL, send S to ALU (-1) - JSR2 = 6'd28, // JSR - write S - JSR3 = 6'd29, // JSR - fetch MSB - PULL0 = 6'd30, // PLP/PLA - save next op in IRHOLD, send S to ALU (+1) - PULL1 = 6'd31, // PLP/PLA - fetch data from stack, write S - PULL2 = 6'd32, // PLP/PLA - prefetch op, but don't increment PC - PUSH0 = 6'd33, // PHP/PHA - send A to ALU (+0) - PUSH1 = 6'd34, // PHP/PHA - write A/P, send S to ALU (-1) - READ = 6'd35, // Read memory for read/modify/write (INC, DEC, shift) - REG = 6'd36, // Read register for reg-reg transfers - RTI0 = 6'd37, // RTI - send S to ALU (+1) - RTI1 = 6'd38, // RTI - read P from stack - RTI2 = 6'd39, // RTI - read PCL from stack - RTI3 = 6'd40, // RTI - read PCH from stack - RTI4 = 6'd41, // RTI - read PCH from stack - RTS0 = 6'd42, // RTS - send S to ALU (+1) - RTS1 = 6'd43, // RTS - read PCL from stack - RTS2 = 6'd44, // RTS - write PCL to ALU, read PCH - RTS3 = 6'd45, // RTS - load PC and increment - WRITE = 6'd46, // Write memory for read/modify/write - ZP0 = 6'd47, // Z-page - fetch ZP address - ZPX0 = 6'd48, // ZP, X - fetch ZP, and send to ALU (+X) - ZPX1 = 6'd49; // ZP, X - load from memory - -`ifdef SIM - -/* - * easy to read names in simulator output - */ -reg [8*6-1:0] statename; - -always @* - case( state ) - DECODE: statename = "DECODE"; - REG: statename = "REG"; - ZP0: statename = "ZP0"; - ZPX0: statename = "ZPX0"; - ZPX1: statename = "ZPX1"; - ABS0: statename = "ABS0"; - ABS1: statename = "ABS1"; - ABSX0: statename = "ABSX0"; - ABSX1: statename = "ABSX1"; - ABSX2: statename = "ABSX2"; - INDX0: statename = "INDX0"; - INDX1: statename = "INDX1"; - INDX2: statename = "INDX2"; - INDX3: statename = "INDX3"; - INDY0: statename = "INDY0"; - INDY1: statename = "INDY1"; - INDY2: statename = "INDY2"; - INDY3: statename = "INDY3"; - READ: statename = "READ"; - WRITE: statename = "WRITE"; - FETCH: statename = "FETCH"; - PUSH0: statename = "PUSH0"; - PUSH1: statename = "PUSH1"; - PULL0: statename = "PULL0"; - PULL1: statename = "PULL1"; - PULL2: statename = "PULL2"; - JSR0: statename = "JSR0"; - JSR1: statename = "JSR1"; - JSR2: statename = "JSR2"; - JSR3: statename = "JSR3"; - RTI0: statename = "RTI0"; - RTI1: statename = "RTI1"; - RTI2: statename = "RTI2"; - RTI3: statename = "RTI3"; - RTI4: statename = "RTI4"; - RTS0: statename = "RTS0"; - RTS1: statename = "RTS1"; - RTS2: statename = "RTS2"; - RTS3: statename = "RTS3"; - BRK0: statename = "BRK0"; - BRK1: statename = "BRK1"; - BRK2: statename = "BRK2"; - BRK3: statename = "BRK3"; - BRA0: statename = "BRA0"; - BRA1: statename = "BRA1"; - BRA2: statename = "BRA2"; - JMP0: statename = "JMP0"; - JMP1: statename = "JMP1"; - JMPI0: statename = "JMPI0"; - JMPI1: statename = "JMPI1"; - endcase - -//always @( PC ) -// $display( "%t, PC:%04x IR:%02x A:%02x X:%02x Y:%02x S:%02x C:%d Z:%d V:%d N:%d P:%02x", $time, PC, IR, A, X, Y, S, C, Z, V, N, P ); - -`endif - - - -/* - * Program Counter Increment/Load. First calculate the base value in - * PC_temp. - */ -always @* - case( state ) - DECODE: if( (~I & IRQ) | NMI_edge ) - PC_temp = { ABH, ABL }; - else - PC_temp = PC; - - - JMP1, - JMPI1, - JSR3, - RTS3, - RTI4: PC_temp = { DIMUX, ADD }; - - BRA1: PC_temp = { ABH, ADD }; - - BRA2: PC_temp = { ADD, PCL }; - - BRK2: PC_temp = res ? 16'hfffc : - NMI_edge ? 16'hfffa : 16'hfffe; - - default: PC_temp = PC; - endcase - -/* - * Determine wether we need PC_temp, or PC_temp + 1 - */ -always @* - case( state ) - DECODE: if( (~I & IRQ) | NMI_edge ) - PC_inc = 0; - else - PC_inc = 1; - - ABS0, - ABSX0, - FETCH, - BRA0, - BRA2, - BRK3, - JMPI1, - JMP1, - RTI4, - RTS3: PC_inc = 1; - - BRA1: PC_inc = CO ^~ backwards; - - default: PC_inc = 0; - endcase - -/* - * Set new PC - */ -always @(posedge clk) - if( RDY ) - PC <= PC_temp + PC_inc; - -/* - * Address Generator - */ - -parameter - ZEROPAGE = 8'h00, - STACKPAGE = 8'h01; - -always @* - case( state ) - ABSX1, - INDX3, - INDY2, - JMP1, - JMPI1, - RTI4, - ABS1: AB = { DIMUX, ADD }; - - BRA2, - INDY3, - ABSX2: AB = { ADD, ABL }; - - BRA1: AB = { ABH, ADD }; - - JSR0, - PUSH1, - RTS0, - RTI0, - BRK0: AB = { STACKPAGE, regfile }; - - BRK1, - JSR1, - PULL1, - RTS1, - RTS2, - RTI1, - RTI2, - RTI3, - BRK2: AB = { STACKPAGE, ADD }; - - INDY1, - INDX1, - ZPX1, - INDX2: AB = { ZEROPAGE, ADD }; - - ZP0, - INDY0: AB = { ZEROPAGE, DIMUX }; - - REG, - READ, - WRITE: AB = { ABH, ABL }; - - default: AB = PC; - endcase - -/* - * ABH/ABL pair is used for registering previous address bus state. - * This can be used to keep the current address, freeing up the original - * source of the address, such as the ALU or DI. - */ -always @(posedge clk) - if( state != PUSH0 && state != PUSH1 && RDY && - state != PULL0 && state != PULL1 && state != PULL2 ) - begin - ABL <= AB[7:0]; - ABH <= AB[15:8]; - end - -/* - * Data Out MUX - */ -always @* - case( state ) - WRITE: DO = ADD; - - JSR0, - BRK0: DO = PCH; - - JSR1, - BRK1: DO = PCL; - - PUSH1: DO = php ? P : ADD; - - BRK2: DO = (IRQ | NMI_edge) ? (P & 8'b1110_1111) : P; - - default: DO = regfile; - endcase - -/* - * Write Enable Generator - */ - -always @* - case( state ) - BRK0, // writing to stack or memory - BRK1, - BRK2, - JSR0, - JSR1, - PUSH1, - WRITE: WE = 1; - - INDX3, // only if doing a STA, STX or STY - INDY3, - ABSX2, - ABS1, - ZPX1, - ZP0: WE = store; - - default: WE = 0; - endcase - -/* - * register file, contains A, X, Y and S (stack pointer) registers. At each - * cycle only 1 of those registers needs to be accessed, so they combined - * in a small memory, saving resources. - */ - -reg write_register; // set when register file is written - -always @* - case( state ) - DECODE: write_register = load_reg & ~plp; - - PULL1, - RTS2, - RTI3, - BRK3, - JSR0, - JSR2 : write_register = 1; - - default: write_register = 0; - endcase - -/* - * BCD adjust logic - */ - -always @(posedge clk) - adj_bcd <= adc_sbc & D; // '1' when doing a BCD instruction - -reg [3:0] ADJL; -reg [3:0] ADJH; - -// adjustment term to be added to ADD[3:0] based on the following -// adj_bcd: '1' if doing ADC/SBC with D=1 -// adc_bcd: '1' if doing ADC with D=1 -// HC : half carry bit from ALU -always @* begin - casex( {adj_bcd, adc_bcd, HC} ) - 3'b0xx: ADJL = 4'd0; // no BCD instruction - 3'b100: ADJL = 4'd10; // SBC, and digital borrow - 3'b101: ADJL = 4'd0; // SBC, but no borrow - 3'b110: ADJL = 4'd0; // ADC, but no carry - 3'b111: ADJL = 4'd6; // ADC, and decimal/digital carry - endcase -end - -// adjustment term to be added to ADD[7:4] based on the following -// adj_bcd: '1' if doing ADC/SBC with D=1 -// adc_bcd: '1' if doing ADC with D=1 -// CO : carry out bit from ALU -always @* begin - casex( {adj_bcd, adc_bcd, CO} ) - 3'b0xx: ADJH = 4'd0; // no BCD instruction - 3'b100: ADJH = 4'd10; // SBC, and digital borrow - 3'b101: ADJH = 4'd0; // SBC, but no borrow - 3'b110: ADJH = 4'd0; // ADC, but no carry - 3'b111: ADJH = 4'd6; // ADC, and decimal/digital carry - endcase -end - -/* - * write to a register. Usually this is the (BCD corrected) output of the - * ALU, but in case of the JSR0 we use the S register to temporarily store - * the PCL. This is possible, because the S register itself is stored in - * the ALU during those cycles. - */ -always @(posedge clk) - if( write_register & RDY ) - AXYS[regsel] <= (state == JSR0) ? DIMUX : { ADD[7:4] + ADJH, ADD[3:0] + ADJL }; - -/* - * register select logic. This determines which of the A, X, Y or - * S registers will be accessed. - */ - -always @* - case( state ) - INDY1, - INDX0, - ZPX0, - ABSX0 : regsel = index_y ? SEL_Y : SEL_X; - - - DECODE : regsel = dst_reg; - - BRK0, - BRK3, - JSR0, - JSR2, - PULL0, - PULL1, - PUSH1, - RTI0, - RTI3, - RTS0, - RTS2 : regsel = SEL_S; - - default: regsel = src_reg; - endcase - -/* - * ALU - */ - -ALU ALU( .clk(clk), - .op(alu_op), - .right(alu_shift_right), - .AI(AI), - .BI(BI), - .CI(CI), - .BCD(adc_bcd & (state == FETCH)), - .CO(CO), - .OUT(ADD), - .V(AV), - .Z(AZ), - .N(AN), - .HC(HC), - .RDY(RDY) ); - -/* - * Select current ALU operation - */ - -always @* - case( state ) - READ: alu_op = op; - - BRA1: alu_op = backwards ? OP_SUB : OP_ADD; - - FETCH, - REG : alu_op = op; - - DECODE, - ABS1: alu_op = 1'bx; - - PUSH1, - BRK0, - BRK1, - BRK2, - JSR0, - JSR1: alu_op = OP_SUB; - - default: alu_op = OP_ADD; - endcase - -/* - * Determine shift right signal to ALU - */ - -always @* - if( state == FETCH || state == REG || state == READ ) - alu_shift_right = shift_right; - else - alu_shift_right = 0; - -/* - * Sign extend branch offset. - */ - -always @(posedge clk) - if( RDY ) - backwards <= DIMUX[7]; - -/* - * ALU A Input MUX - */ - -always @* - case( state ) - JSR1, - RTS1, - RTI1, - RTI2, - BRK1, - BRK2, - INDX1: AI = ADD; - - REG, - ZPX0, - INDX0, - ABSX0, - RTI0, - RTS0, - JSR0, - JSR2, - BRK0, - PULL0, - INDY1, - PUSH0, - PUSH1: AI = regfile; - - BRA0, - READ: AI = DIMUX; - - BRA1: AI = ABH; // don't use PCH in case we're - - FETCH: AI = load_only ? 0 : regfile; - - DECODE, - ABS1: AI = 8'hxx; // don't care - - default: AI = 0; - endcase - - -/* - * ALU B Input mux - */ - -always @* - case( state ) - BRA1, - JSR1, - RTS1, - RTI0, - RTI1, - RTI2, - INDX1, - READ, - REG, - JSR0, - JSR2, - BRK0, - BRK1, - BRK2, - PUSH0, - PUSH1, - PULL0, - RTS0: BI = 8'h00; - - BRA0: BI = PCL; - - DECODE, - ABS1: BI = 8'hxx; - - default: BI = DIMUX; - endcase - -/* - * ALU CI (carry in) mux - */ - -always @* - case( state ) - INDY2, - BRA1, - ABSX1: CI = CO; - - DECODE, - ABS1: CI = 1'bx; - - READ, - REG: CI = rotate ? C : - shift ? 0 : inc; - - FETCH: CI = rotate ? C : - compare ? 1 : - (shift | load_only) ? 0 : C; - - PULL0, - RTI0, - RTI1, - RTI2, - RTS0, - RTS1, - INDY0, - INDX1: CI = 1; - - default: CI = 0; - endcase - -/* - * Processor Status Register update - * - */ - -/* - * Update C flag when doing ADC/SBC, shift/rotate, compare - */ -always @(posedge clk ) - if( shift && state == WRITE ) - C <= CO; - else if( state == RTI2 ) - C <= DIMUX[0]; - else if( ~write_back && state == DECODE ) begin - if( adc_sbc | shift | compare ) - C <= CO; - else if( plp ) - C <= ADD[0]; - else begin - if( sec ) C <= 1; - if( clc ) C <= 0; - end - end - -/* - * Update Z, N flags when writing A, X, Y, Memory, or when doing compare - */ - -always @(posedge clk) - if( state == WRITE ) - Z <= AZ; - else if( state == RTI2 ) - Z <= DIMUX[1]; - else if( state == DECODE ) begin - if( plp ) - Z <= ADD[1]; - else if( (load_reg & (regsel != SEL_S)) | compare | bit_ins ) - Z <= AZ; - end - -always @(posedge clk) - if( state == WRITE ) - N <= AN; - else if( state == RTI2 ) - N <= DIMUX[7]; - else if( state == DECODE ) begin - if( plp ) - N <= ADD[7]; - else if( (load_reg & (regsel != SEL_S)) | compare ) - N <= AN; - end else if( state == FETCH && bit_ins ) - N <= DIMUX[7]; - -/* - * Update I flag - */ - -always @(posedge clk) - if( state == BRK3 ) - I <= 1; - else if( state == RTI2 ) - I <= DIMUX[2]; - else if( state == REG ) begin - if( sei ) I <= 1; - if( cli ) I <= 0; - end else if( state == DECODE ) - if( plp ) I <= ADD[2]; - -/* - * Update D flag - */ -always @(posedge clk ) - if( state == RTI2 ) - D <= DIMUX[3]; - else if( state == DECODE ) begin - if( sed ) D <= 1; - if( cld ) D <= 0; - if( plp ) D <= ADD[3]; - end - -/* - * Update V flag - */ -always @(posedge clk ) - if( state == RTI2 ) - V <= DIMUX[6]; - else if( state == DECODE ) begin - if( adc_sbc ) V <= AV; - if( clv ) V <= 0; - if( plp ) V <= ADD[6]; - end else if( state == FETCH && bit_ins ) - V <= DIMUX[6]; - -/* - * Instruction decoder - */ - -/* - * IR register/mux. Hold previous DI value in IRHOLD in PULL0 and PUSH0 - * states. In these states, the IR has been prefetched, and there is no - * time to read the IR again before the next decode. - */ - -reg RDY1 = 1; - -always @(posedge clk ) - RDY1 <= RDY; - -always @(posedge clk ) - if( ~RDY && RDY1 ) - DIHOLD <= DI; - -always @(posedge clk ) - if( reset ) - IRHOLD_valid <= 0; - else if( RDY ) begin - if( state == PULL0 || state == PUSH0 ) begin - IRHOLD <= DIMUX; - IRHOLD_valid <= 1; - end else if( state == DECODE ) - IRHOLD_valid <= 0; - end - -assign IR = (IRQ & ~I) | NMI_edge ? 8'h00 : - IRHOLD_valid ? IRHOLD : DIMUX; - -assign DIMUX = ~RDY1 ? DIHOLD : DI; - -/* - * Microcode state machine - */ -always @(posedge clk or posedge reset) - if( reset ) - state <= BRK0; - else if( RDY ) case( state ) - DECODE : - casex ( IR ) - 8'b0000_0000: state <= BRK0; - 8'b0010_0000: state <= JSR0; - 8'b0010_1100: state <= ABS0; // BIT abs - 8'b0100_0000: state <= RTI0; // - 8'b0100_1100: state <= JMP0; - 8'b0110_0000: state <= RTS0; - 8'b0110_1100: state <= JMPI0; - 8'b0x00_1000: state <= PUSH0; - 8'b0x10_1000: state <= PULL0; - 8'b0xx1_1000: state <= REG; // CLC, SEC, CLI, SEI - 8'b1xx0_00x0: state <= FETCH; // IMM - 8'b1xx0_1100: state <= ABS0; // X/Y abs - 8'b1xxx_1000: state <= REG; // DEY, TYA, ... - 8'bxxx0_0001: state <= INDX0; - 8'bxxx0_01xx: state <= ZP0; - 8'bxxx0_1001: state <= FETCH; // IMM - 8'bxxx0_1101: state <= ABS0; // even E column - 8'bxxx0_1110: state <= ABS0; // even E column - 8'bxxx1_0000: state <= BRA0; // odd 0 column - 8'bxxx1_0001: state <= INDY0; // odd 1 column - 8'bxxx1_01xx: state <= ZPX0; // odd 4,5,6,7 columns - 8'bxxx1_1001: state <= ABSX0; // odd 9 column - 8'bxxx1_11xx: state <= ABSX0; // odd C, D, E, F columns - 8'bxxxx_1010: state <= REG; // A, TXA, ... NOP - endcase - - ZP0 : state <= write_back ? READ : FETCH; - - ZPX0 : state <= ZPX1; - ZPX1 : state <= write_back ? READ : FETCH; - - ABS0 : state <= ABS1; - ABS1 : state <= write_back ? READ : FETCH; - - ABSX0 : state <= ABSX1; - ABSX1 : state <= (CO | store | write_back) ? ABSX2 : FETCH; - ABSX2 : state <= write_back ? READ : FETCH; - - INDX0 : state <= INDX1; - INDX1 : state <= INDX2; - INDX2 : state <= INDX3; - INDX3 : state <= FETCH; - - INDY0 : state <= INDY1; - INDY1 : state <= INDY2; - INDY2 : state <= (CO | store) ? INDY3 : FETCH; - INDY3 : state <= FETCH; - - READ : state <= WRITE; - WRITE : state <= FETCH; - FETCH : state <= DECODE; - - REG : state <= DECODE; - - PUSH0 : state <= PUSH1; - PUSH1 : state <= DECODE; - - PULL0 : state <= PULL1; - PULL1 : state <= PULL2; - PULL2 : state <= DECODE; - - JSR0 : state <= JSR1; - JSR1 : state <= JSR2; - JSR2 : state <= JSR3; - JSR3 : state <= FETCH; - - RTI0 : state <= RTI1; - RTI1 : state <= RTI2; - RTI2 : state <= RTI3; - RTI3 : state <= RTI4; - RTI4 : state <= DECODE; - - RTS0 : state <= RTS1; - RTS1 : state <= RTS2; - RTS2 : state <= RTS3; - RTS3 : state <= FETCH; - - BRA0 : state <= cond_true ? BRA1 : DECODE; - BRA1 : state <= (CO ^ backwards) ? BRA2 : DECODE; - BRA2 : state <= DECODE; - - JMP0 : state <= JMP1; - JMP1 : state <= DECODE; - - JMPI0 : state <= JMPI1; - JMPI1 : state <= JMP0; - - BRK0 : state <= BRK1; - BRK1 : state <= BRK2; - BRK2 : state <= BRK3; - BRK3 : state <= JMP0; - - endcase - -/* - * Additional control signals - */ - -always @(posedge clk) - if( reset ) - res <= 1; - else if( state == DECODE ) - res <= 0; - -always @(posedge clk) - if( state == DECODE && RDY ) - casex( IR ) - 8'b0xx01010, // ASLA, ROLA, LSRA, RORA - 8'b0xxxxx01, // ORA, AND, EOR, ADC - 8'b100x10x0, // DEY, TYA, TXA, TXS - 8'b1010xxx0, // LDA/LDX/LDY - 8'b10111010, // TSX - 8'b1011x1x0, // LDX/LDY - 8'b11001010, // DEX - 8'b1x1xxx01, // LDA, SBC - 8'bxxx01000: // DEY, TAY, INY, INX - load_reg <= 1; - - default: load_reg <= 0; - endcase - -always @(posedge clk) - if( state == DECODE && RDY ) - casex( IR ) - 8'b1110_1000, // INX - 8'b1100_1010, // DEX - 8'b101x_xx10: // LDX, TAX, TSX - dst_reg <= SEL_X; - - 8'b0x00_1000, // PHP, PHA - 8'b1001_1010: // TXS - dst_reg <= SEL_S; - - 8'b1x00_1000, // DEY, DEX - 8'b101x_x100, // LDY - 8'b1010_x000: // LDY #imm, TAY - dst_reg <= SEL_Y; - - default: dst_reg <= SEL_A; - endcase - -always @(posedge clk) - if( state == DECODE && RDY ) - casex( IR ) - 8'b1011_1010: // TSX - src_reg <= SEL_S; - - 8'b100x_x110, // STX - 8'b100x_1x10, // TXA, TXS - 8'b1110_xx00, // INX, CPX - 8'b1100_1010: // DEX - src_reg <= SEL_X; - - 8'b100x_x100, // STY - 8'b1001_1000, // TYA - 8'b1100_xx00, // CPY - 8'b1x00_1000: // DEY, INY - src_reg <= SEL_Y; - - default: src_reg <= SEL_A; - endcase - -always @(posedge clk) - if( state == DECODE && RDY ) - casex( IR ) - 8'bxxx1_0001, // INDY - 8'b10x1_x110, // LDX/STX zpg/abs, Y - 8'bxxxx_1001: // abs, Y - index_y <= 1; - - default: index_y <= 0; - endcase - - -always @(posedge clk) - if( state == DECODE && RDY ) - casex( IR ) - 8'b100x_x1x0, // STX, STY - 8'b100x_xx01: // STA - store <= 1; - - default: store <= 0; - - endcase - -always @(posedge clk ) - if( state == DECODE && RDY ) - casex( IR ) - 8'b0xxx_x110, // ASL, ROL, LSR, ROR - 8'b11xx_x110: // DEC/INC - write_back <= 1; - - default: write_back <= 0; - endcase - - -always @(posedge clk ) - if( state == DECODE && RDY ) - casex( IR ) - 8'b101x_xxxx: // LDA, LDX, LDY - load_only <= 1; - default: load_only <= 0; - endcase - -always @(posedge clk ) - if( state == DECODE && RDY ) - casex( IR ) - 8'b111x_x110, // INC - 8'b11x0_1000: // INX, INY - inc <= 1; - - default: inc <= 0; - endcase - -always @(posedge clk ) - if( (state == DECODE || state == BRK0) && RDY ) - casex( IR ) - 8'bx11x_xx01: // SBC, ADC - adc_sbc <= 1; - - default: adc_sbc <= 0; - endcase - -always @(posedge clk ) - if( (state == DECODE || state == BRK0) && RDY ) - casex( IR ) - 8'b011x_xx01: // ADC - adc_bcd <= D; - - default: adc_bcd <= 0; - endcase - -always @(posedge clk ) - if( state == DECODE && RDY ) - casex( IR ) - 8'b0xxx_x110, // ASL, ROL, LSR, ROR (abs, absx, zpg, zpgx) - 8'b0xxx_1010: // ASL, ROL, LSR, ROR (acc) - shift <= 1; - - default: shift <= 0; - endcase - -always @(posedge clk ) - if( state == DECODE && RDY ) - casex( IR ) - 8'b11x0_0x00, // CPX, CPY (imm/zp) - 8'b11x0_1100, // CPX, CPY (abs) - 8'b110x_xx01: // CMP - compare <= 1; - - default: compare <= 0; - endcase - -always @(posedge clk ) - if( state == DECODE && RDY ) - casex( IR ) - 8'b01xx_xx10: // ROR, LSR - shift_right <= 1; - - default: shift_right <= 0; - endcase - -always @(posedge clk ) - if( state == DECODE && RDY ) - casex( IR ) - 8'b0x1x_1010, // ROL A, ROR A - 8'b0x1x_x110: // ROR, ROL - rotate <= 1; - - default: rotate <= 0; - endcase - -always @(posedge clk ) - if( state == DECODE && RDY ) - casex( IR ) - 8'b00xx_xx10: // ROL, ASL - op <= OP_ROL; - - 8'b0010_x100: // BIT zp/abs - op <= OP_AND; - - 8'b01xx_xx10: // ROR, LSR - op <= OP_A; - - 8'b1000_1000, // DEY - 8'b1100_1010, // DEX - 8'b110x_x110, // DEC - 8'b11xx_xx01, // CMP, SBC - 8'b11x0_0x00, // CPX, CPY (imm, zpg) - 8'b11x0_1100: op <= OP_SUB; - - 8'b010x_xx01, // EOR - 8'b00xx_xx01: // ORA, AND - op <= { 2'b11, IR[6:5] }; - - default: op <= OP_ADD; - endcase - -always @(posedge clk ) - if( state == DECODE && RDY ) - casex( IR ) - 8'b0010_x100: // BIT zp/abs - bit_ins <= 1; - - default: bit_ins <= 0; - endcase - -/* - * special instructions - */ -always @(posedge clk ) - if( state == DECODE && RDY ) begin - php <= (IR == 8'h08); - clc <= (IR == 8'h18); - plp <= (IR == 8'h28); - sec <= (IR == 8'h38); - cli <= (IR == 8'h58); - sei <= (IR == 8'h78); - clv <= (IR == 8'hb8); - cld <= (IR == 8'hd8); - sed <= (IR == 8'hf8); - brk <= (IR == 8'h00); - end - -always @(posedge clk) - if( RDY ) - cond_code <= IR[7:5]; - -always @* - case( cond_code ) - 3'b000: cond_true = ~N; - 3'b001: cond_true = N; - 3'b010: cond_true = ~V; - 3'b011: cond_true = V; - 3'b100: cond_true = ~C; - 3'b101: cond_true = C; - 3'b110: cond_true = ~Z; - 3'b111: cond_true = Z; - endcase - - -reg NMI_1 = 0; // delayed NMI signal - -always @(posedge clk) - NMI_1 <= NMI; - -always @(posedge clk ) - if( NMI_edge && state == BRK3 ) - NMI_edge <= 0; - else if( NMI & ~NMI_1 ) - NMI_edge <= 1; - -endmodule From a490eec4f3cf4eac5836f6d4e2c6abfc732d885b Mon Sep 17 00:00:00 2001 From: Gyorgy Szombathelyi Date: Thu, 23 Aug 2018 01:13:19 +0200 Subject: [PATCH 5/5] BBC: replace Smart MMC ROM with MMFS 1.41 --- cores/bbc/fpga/mist/bbc_mist_top.qsf | 6 +- cores/bbc/fpga/mist/bbc_mist_top.v | 10 +- cores/bbc/fpga/mist/{smmc.qip => mmfs.qip} | 2 +- cores/bbc/fpga/mist/{smmc.v => mmfs.v} | 24 +- cores/bbc/roms/mmfs.hex | 513 ++++++++++++++++++++ cores/bbc/roms/mmfs.rom | Bin 0 -> 16384 bytes cores/bbc/roms/smmc.hex | 514 --------------------- cores/bbc/roms/smmc.rom | Bin 16384 -> 0 bytes 8 files changed, 534 insertions(+), 535 deletions(-) rename cores/bbc/fpga/mist/{smmc.qip => mmfs.qip} (89%) rename cores/bbc/fpga/mist/{smmc.v => mmfs.v} (90%) create mode 100644 cores/bbc/roms/mmfs.hex create mode 100644 cores/bbc/roms/mmfs.rom delete mode 100644 cores/bbc/roms/smmc.hex delete mode 100644 cores/bbc/roms/smmc.rom diff --git a/cores/bbc/fpga/mist/bbc_mist_top.qsf b/cores/bbc/fpga/mist/bbc_mist_top.qsf index a0b82df..8a251ba 100644 --- a/cores/bbc/fpga/mist/bbc_mist_top.qsf +++ b/cores/bbc/fpga/mist/bbc_mist_top.qsf @@ -159,7 +159,6 @@ set_global_assignment -name ADV_NETLIST_OPT_SYNTH_WYSIWYG_REMAP ON set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V" -set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top set_global_assignment -name VERILOG_FILE ../../rtl/adc.v set_global_assignment -name VERILOG_FILE sdram.v set_global_assignment -name VERILOG_FILE ../../rtl/mc6845.v @@ -195,8 +194,9 @@ set_global_assignment -name VERILOG_FILE ../../rtl/bbc.v set_global_assignment -name CDF_FILE output_files/Chain4.cdf set_global_assignment -name VHDL_FILE ../../rtl/m6522.vhd set_global_assignment -name QIP_FILE ../../rtl/T65/T65.qip -set_global_assignment -name QIP_FILE smmc.qip +set_global_assignment -name QIP_FILE mmfs.qip set_global_assignment -name QIP_FILE os12.qip set_global_assignment -name QIP_FILE basic2.qip set_global_assignment -name QIP_FILE dfs09.qip -set_global_assignment -name SDC_FILE bbc_mist.sdc \ No newline at end of file +set_global_assignment -name SDC_FILE bbc_mist.sdc +set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file diff --git a/cores/bbc/fpga/mist/bbc_mist_top.v b/cores/bbc/fpga/mist/bbc_mist_top.v index 4219765..0b1f7fe 100755 --- a/cores/bbc/fpga/mist/bbc_mist_top.v +++ b/cores/bbc/fpga/mist/bbc_mist_top.v @@ -405,11 +405,11 @@ basic2 basic2 ( .q ( basic_do ) ); -wire [7:0] smmc_do; -smmc smmc ( +wire [7:0] mmfs_do; +mmfs mmfs ( .clock ( clk_32m ), .address ( mem_adr[13:0] ), - .q ( smmc_do ) + .q ( mmfs_do ) ); audio AUDIO ( @@ -466,11 +466,11 @@ wire sideways_ram = // status[2] is '1' of low mapping is selected in the menu wire basic_map = status[2]?(mem_romsel == 4'h0):(mem_romsel == 4'he); -wire smmc_map = status[2]?(mem_romsel == 4'h2):(mem_romsel == 4'hc); +wire mmfs_map = status[2]?(mem_romsel == 4'h2):(mem_romsel == 4'hc); assign mem_di = ((mem_adr[15:14] == 2'b10) && basic_map) ? basic_do : - ((mem_adr[15:14] == 2'b10) && smmc_map) ? smmc_do : + ((mem_adr[15:14] == 2'b10) && mmfs_map) ? mmfs_do : ((mem_adr[15:14] == 2'b10) && (mem_romsel == 4'ha)) ? ram_do : mos_rom ? os_do : cpu_ram ? ram_do : diff --git a/cores/bbc/fpga/mist/smmc.qip b/cores/bbc/fpga/mist/mmfs.qip similarity index 89% rename from cores/bbc/fpga/mist/smmc.qip rename to cores/bbc/fpga/mist/mmfs.qip index 6e4e45f..0d0ccd7 100644 --- a/cores/bbc/fpga/mist/smmc.qip +++ b/cores/bbc/fpga/mist/mmfs.qip @@ -1,3 +1,3 @@ set_global_assignment -name IP_TOOL_NAME "ROM: 1-PORT" set_global_assignment -name IP_TOOL_VERSION "13.1" -set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "smmc.v"] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "mmfs.v"] diff --git a/cores/bbc/fpga/mist/smmc.v b/cores/bbc/fpga/mist/mmfs.v similarity index 90% rename from cores/bbc/fpga/mist/smmc.v rename to cores/bbc/fpga/mist/mmfs.v index aa24fb3..f482105 100644 --- a/cores/bbc/fpga/mist/smmc.v +++ b/cores/bbc/fpga/mist/mmfs.v @@ -4,7 +4,7 @@ // MODULE: altsyncram // ============================================================ -// File Name: smmc.v +// File Name: mmfs.v // Megafunction Name(s): // altsyncram // @@ -36,7 +36,7 @@ // synopsys translate_off `timescale 1 ps / 1 ps // synopsys translate_on -module smmc ( +module mmfs ( address, clock, q); @@ -84,9 +84,9 @@ module smmc ( altsyncram_component.clock_enable_input_a = "BYPASS", altsyncram_component.clock_enable_output_a = "BYPASS", `ifdef NO_PLI - altsyncram_component.init_file = "../../roms/smmc.rif" + altsyncram_component.init_file = "../../roms/mmfs.rif" `else - altsyncram_component.init_file = "../../roms/smmc.hex" + altsyncram_component.init_file = "../../roms/mmfs.hex" `endif , altsyncram_component.intended_device_family = "Cyclone III", @@ -123,7 +123,7 @@ endmodule // Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0" // Retrieval info: PRIVATE: JTAG_ID STRING "NONE" // Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0" -// Retrieval info: PRIVATE: MIFfilename STRING "../../roms/smmc.hex" +// Retrieval info: PRIVATE: MIFfilename STRING "../../roms/mmfs.hex" // Retrieval info: PRIVATE: NUMWORDS_A NUMERIC "16384" // Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0" // Retrieval info: PRIVATE: RegAddr NUMERIC "1" @@ -138,7 +138,7 @@ endmodule // Retrieval info: CONSTANT: ADDRESS_ACLR_A STRING "NONE" // Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS" // Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_A STRING "BYPASS" -// Retrieval info: CONSTANT: INIT_FILE STRING "../../roms/smmc.hex" +// Retrieval info: CONSTANT: INIT_FILE STRING "../../roms/mmfs.hex" // Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone III" // Retrieval info: CONSTANT: LPM_HINT STRING "ENABLE_RUNTIME_MOD=NO" // Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram" @@ -155,10 +155,10 @@ endmodule // Retrieval info: CONNECT: @address_a 0 0 14 0 address 0 0 14 0 // Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0 // Retrieval info: CONNECT: q 0 0 8 0 @q_a 0 0 8 0 -// Retrieval info: GEN_FILE: TYPE_NORMAL smmc.v TRUE -// Retrieval info: GEN_FILE: TYPE_NORMAL smmc.inc FALSE -// Retrieval info: GEN_FILE: TYPE_NORMAL smmc.cmp FALSE -// Retrieval info: GEN_FILE: TYPE_NORMAL smmc.bsf FALSE -// Retrieval info: GEN_FILE: TYPE_NORMAL smmc_inst.v FALSE -// Retrieval info: GEN_FILE: TYPE_NORMAL smmc_bb.v FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL mmfs.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL mmfs.inc FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL mmfs.cmp FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL mmfs.bsf FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL mmfs_inst.v FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL mmfs_bb.v FALSE // Retrieval info: LIB_FILE: altera_mf diff --git a/cores/bbc/roms/mmfs.hex b/cores/bbc/roms/mmfs.hex new file mode 100644 index 0000000..a40d941 --- /dev/null +++ b/cores/bbc/roms/mmfs.hex @@ -0,0 +1,513 @@ +:200000000000004CAE8E821A7B4D6F64656C2042204D4D465300312E3431002843293230E1 +:200020003131204D61746865720055524F4D6C1E02A2068E40FEE88E40FE60200584A976BE +:200040004CF4FF205880004469736320902120588000426164209017ADDD10D003209D968F +:20006000A9FF8D82108DDD10A202A9008D0001203B806885AE6885AFA00020FE83B1AE8D25 +:200080000101CA20FE83E8B1AE9D0001302FD0F3206B8D4C000148A90D20E980686085B300 +:2000A0006885AE6885AFA5B3489848A00020FE83B1AE300620E9804CAD8068A868186CAE07 +:2000C0000085B36885AE6885AFA5B3489848A00020FE83B1AE300620E3FF4CD08068A86845 +:2000E000186CAE00200D81A92E20058448A9EC2051938A48091020419368AA6820E3FF4C18 +:20010000429320748448202A82200D8168202F81D0D720748448202A82201D8168202F812F +:200120004CE3FF20268148A92020E3FF681860290FC90A90026906693060204A81CACA2033 +:200140004281B1B09D7210E8C860204D81B1B095BAE8C86020528C4C6A8120528CA5BA8587 +:20016000F2A5BB85F3A00020CB81A22020B081B01B8D0010C92ED00486CCF0EEC93AD0154B +:2001800020658C20B081B004C92EF0DE204E80CC6E616D6500AA20B081B034C92EF0D9A2E8 +:2001A000019D0010E820B081B027E007D0F3F0DC20C5FF08297FC90DF008C92090CEC97F1A +:2001C000F0CA286086F284F3A00060184CC2FFA201A9209D0010E8E040D0F8A206BD00106B +:2001E00095C5CA10F860200584B90F0E08297FD005202381F00620E98020E780A206B9083C +:200200000E297F20E980C8CA10F4202381A920281002A94C20E980A00120268188D0FA60AA +:200220004A4A4A4A4A4A2903604A4A4A4A4A600A0A0A0A0A602054813003205A8120A682A7 +:20024000B0F2206880D64E6F7420666F756E640020C48120528C20CB81F00320348AA92A3E +:200260008D001020CF81201A92203D824C7F8220C481A94E85BF201A9220CC992035822091 +:200280005C8320A282B0F8602099A4A900F01EA206B5C59D5810CA10F8A9208D5F10A95860 +:2002A000D006A200F00CA900482099A468AAA90085B6A000A90E85B7A5B6CD050FB0496955 +:2002C0000885B620DD8290EAA5CCA007200A83D0E1A4B63888888888888888886020058491 +:2002E000BD0010CDCE10D013E820DD82B01BC8C00790F6BD0010C920D00E60C007B0F420DD +:200300000A83D004E8C8D0D81860CDCE10F010CDCD10F00B20498351B6B002295F297F6027 +:2003200008204983B002295F297F286020CE92B9100E99080EB9100F99080FC8CC050F9099 +:20034000EE98E9088D050F186048295FC9419004C95B90013868602CC61030EC2005842003 +:20036000E6819848A96085B0A91085B120A883A00220268120908320908320908368A8B9F3 +:200380000E0F2903200D81B90F0F2005814C9680A203B9621020058188CAD0F620BC884C49 +:2003A00026812005844CA1A42005849848AAA012A9008891B0C002D0F920F383C8C8C00E86 +:2003C000D0F768AABD0F0E1004A90891B0BD0E0FA00420E183A00C4A4A48290391B068A065 +:2003E000084A4A482903C903D005A9FF91B0C891B0686020F683BD080F91B0E8C860E6AE40 +:20040000D002E6AF60488A489848A98448A92748A005BABD07014888D0F8A00ABD09019DC4 +:200420000B01CA88D0F6686868A868AA6860488A489848201084BA9D03014C2884A6B8A0DF +:200440000084B68AC90A9010E90AAAF818986910A8D890EFE6B6B0EBF884B565B5A89003EB +:20046000E6B618A6B9F0096956A8A5B6690285B6D884B560202E84A8F00A18F8A900690100 +:2004800088D0FBD860297FC97FF004C920B002A92E609848A90085B085B120CB81F04EC9B4 +:2004A00022F04A20C5FFB04538E9303040C90AB03C48A5B00A4826B1A6B10A26B10A26B1A8 +:2004C00085B06865B085B08A65B1AA6865B085B08A690085B1C902B01420C5FF90CAA6B0ED +:2004E000A5B1F004E8F006CA68A5B1186068A8A900AA386020C481205D8C20A1A4A0FF84E3 +:20050000A8C884AAB9000EC0089003B9F80E20E980C8C00CD0EE209E802028A6CD2081AD3A +:20052000209E80290D447269766520A5CD200D81A00D201982209E804F7074696F6E20AD21 +:20054000060F202A8248200D81209E802028A003680A0AAABD408620E980E88810F6209E35 +:2005600080290D4469722E203AADCA1020E480ADC91020E980A00B201982209E804C696249 +:200580002E203AADCC1020E480ADCB1020E980209680A000CC050FB017B90F0E4DC9102913 +:2005A0005FD008B90F0E2980990F0E20BB8890E4A00020C2859016A9FF8D82104C96802002 +:2005C000BB88CC050FB005B9080E30F36084ABA200B9080E2020839D6010C8E8E008D0F128 +:2005E00020C285B02338A206B90E0E202083FD601088CA10F320BC88B90F0E202083ED6736 +:200600001090CA20BB88B0D8A4ABB9080E098099080EAD6710C5AAF010A6AA85AAD00A2023 +:200620009680209680A0FFD009A4A8D0F5A005201982C884A8A4AB20238120E6814CB08576 +:200640006F6666004C4F414452554E0045584543B90E0F20228285C218A9FF790C0FB90F28 +:200660000F790D0F85C3B90E0F290365C285C238B9070FE5C348B9060F2903E5C2AAA90033 +:20068000C5C068E5C18AE5C460FF414343455353B24241434B55508C434C4F534580434F07 +:2006A0004D5041435485434F5059AC44454C4554458844455354524F598244495286445277 +:2006C00049564581454E41424C4580455886464F524DDF4652454584494E464F824C49422D +:2006E000864D41508452454E414D458D5449544C458A56455249465985574950458200149C +:200700004255494C448844554D50884C49535488524F4D538B5459504588001A444953438C +:20072000804449534B804D4D465380001E445554494C53804D4D4653805554494C538000A4 +:2007400022424F4F54874341548E4452495645844652454580494EF44F50C94F55548452F5 +:20076000454341548041424F55548000A58A4F9CCA93F49AAC9C62887488E889CE881D8C3E +:200780005282F91D589F7582EC895B9F038D608AF51D3088C58787B308B3C6B28BB1BFB268 +:2007A0008786BE8DBE8DC88D5F8FB1997299B899BF99DF2D062E7D2FC92EF12D8730F72D79 +:2007C000402CBBAD1E89A2B720CB81B1F2C80920C964F009884C1F8920C481A200BD8986D0 +:2007E00085BE9848E6BE6848A820CB81E8BD8986F028CA8886BFE8C8BD8986301651F229DD +:200800005FF0F3CAE8BD898610FAB1F2C92ED0D4C8B007B1F220498390CA68A5BE0AAABD2C +:200820006D87100648BD6C874860206CAB0980D0F3201A9220CC99203582B90F0E301E201F +:20084000E681201D9CD013A6B620649486B620C48884AB20B2A4A5AB85B620968020A282AF +:20086000B0D86020119220CC99203582205783202C834CB2A420C99B201A9220CC992035DC +:2008800082B90F0E300620E68120968020A282B0F020259CF0034C968020649420A682B9DA +:2008A0000F0E300320C48820A282B0F320B2A4209E800D44656C657465640DC8C8C8C8C828 +:2008C000C8C8C860202C83A4B620D48284B66020628C8DCA1060290385CD60203A8220F088 +:2008E0009220A88384BAA200A5BED006C8C8A202D008B90E0F85C220248CB9080F95BCC820 +:20090000E8E008D0F5203B8CA4BA2057834C73A4204E8B20F09220A8834C8AA420C48120BB +:20092000DC898CDA10205A818CD910208882B022ACDA10ADCB1085CCADCC1020D688205D7D +:2009400081208882B00C204E80FE636F6D6D616E6400B90E0F202082C903D044B90A0F39E2 +:200960000B0FC9FFD03AA206BD00109D0710CA10F7A90D8D0E10A9458D0010A92E8D011030 +:20098000A93A8D0210A5CD09308D0310A92E8D04108D0610A5CC8D0510A200A0104CF7FFC8 +:2009A00020E48818ADD910A865F28DD910A5F369008DDA10AD76102D77100DD610C9FFF07E +:2009C00016A5BE8D7410A5BF8D751020618DA274A010A9044C0604A9016CBE00A9FF85BE81 +:2009E000A5F285BAA5F385BB60A200F002A20220238A9DCA10A5CC9DC91060200584A5B028 +:200A000048A5B148202693A000C0C09005B90010B003B9001191B0C8C0F0D0ED6885B168A0 +:200A200085B060ADC91085CC20CB81D007A90020D688F02AADCA1020D68820B081B010C9E7 +:200A40003AD01420658C20B081B013C92EF0EB204E80CE6469720085CC20B08190F1A5CDF1 +:200A60006020CC9920528C20A283A20BA90020878ACA10FAE820B081B00720878AE00B9057 +:200A8000F320B2A44C968A9D0010E00890049DF80E609D000E60201DA5A00BB9001091B0B3 +:200AA0008810F84CB2A5201A9220CC99205481A20020CB81D02486AA20A682B0034C428280 +:200AC00020D192B90F0E297F05AA990F0E20578320A282B0EB4CB2A4A28020B081B0D72912 +:200AE0005FC94CF0F3204E80CF617474726962757465002005848AC904F01EC905F034C945 +:200B000002900B204E80CB6F7074696F6E00A2FF98F002A2008EC61060984820528C20A1B6 +:200B2000A4682030824D060F29304D060F8D060F4CB2A49808A6F4BDF00D29BF28F0020977 +:200B4000409DF00D60204380C666756C6C00205A8120A6829003202C83A5C048A5C14838C7 +:200B6000A5C2E5C085C0A5C3E5C185C1AD7A10ED781085C4208A8BAD79108D7510AD781029 +:200B80008D74106885BD6885BC60A90085C2A90285C3AC050FC0F8B056206F864CA78BF0AD +:200BA000A420D4822050869890F584B0AC050FC4B0F00FB9070E990F0EB9070F990F0F880F +:200BC000B0EDA20020FC8BB5C599080EC8E8E008D0F5B5BB8899080FCAD0F7205783984896 +:200BE000AC050F20BB888C050F20B2A468A860205880BE4361742066756C6C00AD7610294F +:200C0000030A0A45C429FC45C40A0A4D741029FC4D74100A0A45C229FC45C285C260A90113 +:200C20008DC71060A9008D7510A5C229088D7410F008A9FF8D75108D741060A9008D7710AC +:200C4000A5C2202082C903D005A9FF8D77108D761060ADC91085CCADCA104CD68820CB8127 +:200C6000F0F520CC9920B081B012C93AF0F738E9309009C904B00520D6881860204E80CDF0 +:200C80006472697665004CD299204380D66E6F7420666F756E640020CC99C922D0018884F0 +:200CA000B420B081C93AD00520658C9021A4B420578C209284B02A20CB81F01CC922D001F6 +:200CC00088A5B1D0B7A5B0C904B0B120D688209284B00E20CB81D0AEA5B185B9A5B085B8AA +:200CE0006020BAACA90085B720C9A5AD0D10F096AD0E10D091A5B930902009AD90E220EDAC +:200D0000A54CF58C20119220CC992054819848203D8220CE9284C468A820CC99A5CD48202E +:200D2000548168C5CDD01420A6829012C4C4F00E205880C4457869737473004C7C8CA4C498 +:200D400020BB88A207B5C599070E88CA10F74CB2A4A9EAA200A0FF20F4FF8A49FF8DD61038 +:200D60006048A9CA20060490F9686020518D300748A98A200604686024FF101D20469320D2 +:200D80006880114573636170650048202A8220928D68202F81990001C8604C2E21424F4F41 +:200DA000540D452E21424F4F540DA5B3209E804D6F64656C2042204D4D46530D0D900CA665 +:200DC000F4BDF00D2940F00160A9FF20009648A906202E80A20DBD58939D1202CA10F7A906 +:200DE000A820519384B186B0A207A01BB94B9391B0C8B94B9391B0C8A5F491B0C8CAD0EC10 +:200E00008C82108C831086CD8E9F10A20F204D93202693A0D3B1B01037A0D4B1B0303A2001 +:200E20001193A000B1B0C0C09005990010B00399001188D0EF20B8A2CDEE10D016A9A0A88F +:200E400048A93F20839768991D11E91FD0F1F009201193207E8E209E8E20518D68D00B209A +:200E6000A1A4AD060F202A82D00160A08DA29AC9029008F004A2A2D002A2A44CF7FFA92443 +:200E80008DC9108DCB10A9038DCC10A0008CCA108CC010888CC7108CC6108CDD1060A00E39 +:200EA000A90099E0108810FAA9018DEE106020ECB548BDF00D301E68C912F031C90BB014CC +:200EC0000AAABDD88E48BDD78E488AA6F44AC90B90026915606860D38EF68EFD8E3E8F577B +:200EE0008FD38ED38ED38E638FB48FC58FC004D0E32005844CC98DC017B002A01760984875 +:200F000085B1BCF00D98294005B19DF00DA90085B0C4B1F004A0D391B0A9FD205193CA8A98 +:200F2000A0D331B091B008C8281007B1B0100320FB89A90091B0A6F468A8A902C8C86020A6 +:200F4000058484B3A97A20F4FF8A3009C965D0EEA97820F4FF4CAA8D200584A2924CDD87A8 +:200F6000A276D0F9200584A4EF3030C07D902C20009620E88FD024A6F086B0A6F186B1A47C +:200F8000EFC81007085820BEB0286020528C20A283C83008A000AD040F91B060A900A891E7 +:200FA000B0C8AD070F91B0C8AD060F290391B0C8A90091B060200584B1F2A2A3C90DD09DD8 +:200FC00098A0034C7899200584202693A0D4B1B01015A00020E79520FB89202693A0D4A927 +:200FE0000091B0BA9D050160A90720F08FE011606C1E02202E844820119286B08EDB1084B7 +:20100000B18CDC10A200A000204A81203A81C012D0F968AAE8E008B008BD9B9348BD939354 +:2010200048A90060C90CB0F986B5AABD879348BD7B93488AA6B560C909B0FB20058420003F +:20104000968E7D108C7E10A8205690082C8110100320708D2860B9A3938DD710B9AC938DB2 +:20106000D810B9B5934A084A088D7F1020C891A00CB1B49960108810F8AD63102D64100DD6 +:20108000D610186901F00620618D18A9FF8D8110AD7F10B007A261A01020060428B0042832 +:2010A0006CD710A203BD691095B6CA10F8A2B6AC6010A90028B00320D998204796A203B505 +:2010C000B69D6910CA10F820BA91300DAC601020A090B00DA20920AE91A20520AE91D0ECD5 +:2010E000180820BA91A20520AE91A00C20C891B9601091B48810F8286020528C2099A4A9B0 +:201100000B8DD710A9918DD810D0BCAC6910CC050FB028B90F0E20498345CCB00229DF2982 +:201120007FF00520BB88D0E6A90720DC9185B0B9080E20DC91C8C6B0D0F5188C6910AD0483 +:201140000F8D60106020528C2099A4A90C20DC91A000C008B005B9000E9003B9F80E20DC54 +:2011600091C8C00CD0ECAD060F202A8220DC91A5CD4CDC9120D391ADCA10093020DC912057 +:20118000D391ADC910D05520D391ADCC10093020DC9120D391ADCB10D04248AD611085B8AC +:2011A000AD621085B9A2006860200584A201A004FE6010D004E888D0F760A203A9FF5D6590 +:2011C000109D6510CA10F560AD7D1085B4AD7E1085B560A901D005200197B0F62C811010CC +:2011E000058DE5FE30C3209A9181B84CA99120F69120EF9718602C81101006ADE5FE4CA960 +:2012000091209A91A1B84CA9912CC7103003CEC710A9FF8DCE108DCD1060A92A8DCE10A974 +:2012200023D0F320DC9220A883A9016020B99220A883202C83902420DC92206192207D92DC +:20124000501620DC92207D92501120DC92206192500920DC9220D19220A09220D58AA90184 +:2012600060200584A002B1B09D080FC8B1B09D090FC8B1B00A0A5D0E0F290C101B20058410 +:20128000A006B1B09D0A0FC8B1B09D0B0FC8B1B06A6A6A5D0E0F29C05D0E0F9D0E0FB8609B +:2012A000200584A00EB1B0290AF002A9805D0F0E29805D0F0E9D0F0E6020E6929023B90F5E +:2012C0000E1022205880C34C6F636B65640020BE9220058420909590214CF49420E692B096 +:2012E000196868A90060205A8120A682900C98AAADDB1085B0ADDC1085B160A98320F4FFA0 +:201300008CCF10A98420F4FF9838EDCF108DD01060A20A204D93202693A0D3A9FF91B08D4B +:20132000D310C891B06048A90085B0A6F4BDF00D293F85B16860200584A90FA201A000F0ED +:2013400014AAA903D00F200584A97ED008A98FD004A200A0FF4CF4FF1BFF1EFF21FF24FF96 +:2013600027FF2AFF2DFFF38F000D9600019700EF97003790007E9400249000F2C91BD71B5A +:20138000F3C2BD084F6E1B8A9689878984939392828289DA0F36494151222B88899292926B +:2013A00092929288EEEED7D7457487F9869191919191919190040203060704040404A21146 +:2013C000A01560200584A9774CF4FFA9208D861020C693A900186920F0E8A820EB93D0F55E +:2013E000A9208D861098F0E820E29648206696B046B91B1149FF2DC0108DC010B91711290E +:2014000060F034203C94B917112D8610F026AEC310B914119D0C0FB915119D0D0FB916110F +:201420002030825D0E0F29305D0E0F9D0E0F20B2A4ACC210208B97AEC5106860205794A2A5 +:2014400006B90C1195C58888CA10F6208F8290228CC310ACC21060B90E11297F85CCB917B5 +:20146000114CD688200584AD040F20A283CD040FF0E4204380C86368616E6765640029C0F1 +:20148000D0062005844CE093202E8486BA84BB85B424B408205A8120119220A682B02428A2 +:2014A0005003A9006008A900A20795BC9D7410CA10F8C6BEC6BFCE7610CE7710A94085C34F +:2014C000204E8B2808700320BE92209095900EB90C1110202808301C208B95B0F2ACC2103B +:2014E000D01B205880C0546F6F206D616E79206F70656E00205880C24F70656E00A9088DE6 +:20150000C410BD080E990011C8BD080F990011C8E8CEC410D0ECA210A900990011C8CAD0BF +:20152000F9ADC210A82029826911991311ADC110991B110DC0108DC010B9091169FFB90B0D +:20154000116900991911B90D11090F6900202282991A1128502E3008A980190C11990C117B +:20156000B90911991411B90B11991511B90D11202282991611A5CD19171199171198202996 +:2015800082091060A920991711D0EA8A484CCF95A9008DC210A90885B598AAA0A084B38A54 +:2015A00048A90885B2A5B52CC010F01DB9171145CD2903D01ABD080E590011297FD010E8E2 +:2015C000C8C8C6B2D0EF38B0108CC2108DC11038A5B3E92085B306B51868AAA4B3A5B5B079 +:2015E00002D0BA60200096ADC01048A9008D861098D00520D393F00320E093688DC010601A +:20160000488A48A900BA9D090168AA6860200584C9FFF0D0C000F00EC903B023200096C9BF +:2016200001D0244CD998C902B015200096F011A9FF95029503ADD9109500ADDA1095016022 +:20164000A904BA9D05016020E2968CC2100A6DC210A8B910119500B911119501B9121195E8 +:2016600002A900950360488EC5109829E08DC210F013202982A8A900386A88D0FCACC21089 +:201680002CC010D0036838606818604898C9109004C9189002A908202F82A86860A9C6205A +:2016A00051938AF00920C196D004A9C6D00CA9C720519320C196D011A9C7A200A0004CF46F +:2016C000FF8AA8208B96CCC210604898488AA820E29698204B99D004A2FFD002A20068A813 +:2016E0006860208B9620669690F7209D96205880DE4368616E6E656C00205880DF454F46AB +:2017000000202E8420E29698204B99D013B917112910D0E5A910207C97AEC510A9FE386063 +:20172000B91711300A205794208B9738209397B9101185BAB9131185BBA000B1BA48ACC2C8 +:2017400010A6BAE88A991011D01418B911116901991111B912116900991211208197186838 +:201760006018B90F1179111185C3991C11B90D11290379121185C2991D11A980191711D088 +:2017800005A97F3917119917111860B917112940F0451808EEDD10ACC210B9131185BDA9C2 +:2017A000FF8D74108D7510A90085BC85C0A90185C128B017B91C1185C3B91D1185C2208AF3 +:2017C000A4ACC210A9BF20839790062061972073A4CEDD10ACC210604CC392205880C1521B +:2017E000656164206F6E6C79002005844CF59720058420E29648B90C1130E0B90E1130D80D +:2018000020579498186904204B99D066203F94AEC31038BD070FFD0F0F48BD060FFD0E0F98 +:201820002903D91A11D01F68D91911D03484B48CC210209D96205880BF43616E2774206548 +:201840007874656E6400B91A11186901991A110A0A0A0A5D0E0F29305D0E0F9D0E0F68A9FB +:20186000009D0D0F991911A9009D0C0F20B2A4ACC210B917113017208B97B91411D00B98DC +:20188000204B99D005206197D00438209397B9101185BAB9131185BB68A00091BAACC210FA +:2018A000A940207C97E6BAA5BA991011D013208197B911116901991111B912116900991248 +:2018C0001198204B999011A920207C97A202B91011991411C8CA10F66020058420E296AC9D +:2018E000C210206399B02FB91411991011B91511991111B91611991211202599A5B648A527 +:20190000B748A5B848A90020E9976885B86885B76885B64CE298B500991011B50199111148 +:20192000B502991211A96F208397B90F117911118DC410B90D112903791211D91D11D098FF +:20194000ADC410D91C11D0904C7A97AAB91211DD1611D00EB91111DD1511D006B91011DD70 +:20196000141160B91411D500B91511F501B91611F5026098A200A01448209E800D4D4D46C2 +:20198000532086BF84B7A200BD1680F00620E980E8D0F5A90D20E980A90085B9A0012050FC +:2019A0009A20EB99209680C6B7D0ED68A8A2A34CDD8798A2B7A008D0BF98A276A005D0B86F +:2019C00020CB81F07820B08190FBB0E120CB81F00160206880DC53796E7461783A2086B905 +:2019E00020EB99A900203E9A4C0001A90785B8A6BFE0B79005A944203E9AE8BD8986300608 +:201A0000203E9A4CFA99A4B8300320509A86BFBD8986297F201A9A202A82200584290FF02C +:201A20001CA8A920203E9AA2FFE8BD5D9A10FA88D0F7297F203E9AE8BD5D9A10F7602005C3 +:201A400084A6B9F006E6B99D000160C6B84CE980A5B9D008A920203E9A8810FA60BC647262 +:201A60006976653EBC616673703EA84C29A83C64726976653E29A83C64726976653E292E2B +:201A80002E2EA83C6469723E29BC646E6F3E2F3C6473703EBC6673703ED02F552F4E2F4BA7 +:201AA0002F52BC7469746C653EA83C726F6D3E29BC736F757263653E203C646573742E3EED +:201AC000BC6F6C64206673703E203C6E6577206673703EA8283C662E646E6F3E29203C74FF +:201AE0002E646E6F3E2920283C616473703E29B4302F3830FF205D8C209E80436F6D7061CC +:201B00006374696E67203A8DD1108DD210200D81209680A00020EB9320FB9220A283AC05B4 +:201B20000F84CAA90285C8A90085C9A4CA20D482C0F8D028AD070F38E5C848AD060F2903E7 +:201B4000E5C9200D8168200581209E80206672656520736563746F72730DEA6084CA2057DC +:201B600083A4CAB90C0FC901A90085BC85C0790D0F85C4B90E0F0820228228690085C5B993 +:201B80000F0F85C6B90E0F290385C7C5C9D014A5C6C5C8D00E1865C485C8A5C965C585C9D2 +:201BA0004CC19BA5C8990F0FB90E0F29FC05C9990E0FA90085A885A920B2A420719D209979 +:201BC000A4A4CA205C834C2B9B2CC710104B20259CF00268684C968020628C8DD110206281 +:201BE0008C8DD210CDD110F031984820FB92209E80436F7079696E672066726F6D203AAD9C +:201C0000D110200D81209E8020746F203AADD210200D8120968068A818604C7C8C209E800D +:201C2000203A20900F209E80476F2028592F4E29203F20EA20369320E0FFB00E295FC95997 +:201C400008F002A94E20E98028604C7C8D4C458B20D89B20C99BA90085C785C985C885C61F +:201C600085A8ADD11085CD20A1A4AD070F85C4AD060F290385C5ADD21085CD20A1A4AD06B5 +:201C80000F2903C5C590C6D007AD070FC5C490BD20719D20A1A4A20AE0089005BDF80EB08A +:201CA00003BD000E9D0010CA10EE4C968A201A9220D89B20CC99205481ADD11020D6882070 +:201CC0003D82A5CC48A5B685AB205C83A200B9080E95C59D5010B9080F95BB9D4710E8C876 +:201CE000E008D0EAA5C120228285C3A5BF1869FFA5C0690085C4A5C3690085C5AD4E10852A +:201D0000C6AD4D10290385C7A9FF85A820719DADD11020D68820A283A5AB85B66885CC20C3 +:201D2000A282B09E6020609DADD21085CDA5CC4820A283208F829003202C836885CC202445 +:201D40008C203B8CA5C220228285C4208A8BA5C2290348A5C34820609D6885C86885C960F4 +:201D6000A211BD4510B4BA95BA989D4510CA10F260A90085BC85C0F06DA5C4A8CDD010A53C +:201D8000C5E9009003ACD01084C1A5C685C3A5C785C2ADCF1085BDADD11085CD20ED9D2053 +:201DA00073A4ADD21085CD24A8100720259DA90085A8A5C885C3A5C985C2ADCF1085BD2038 +:201DC000ED9D208AA4A5C11865C885C89002E6C9A5C11865C685C69002E6C738A5C4E5C178 +:201DE00085C4B002C6C5A5C405C5D08D60A9FF8D74108D751060A900F002A9FF85C985B27A +:201E0000101820CC99209284B00D0AD00A86B5E028F007E050F0034CD29920CB8184CAD0A0 +:201E20004B24C9300B209E80566572696679900A209E80466F726D6174EA209E8020776814 +:201E4000696368206472697665203F20EA20E0FFB031C920903020E98038E9309028C904C3 +:201E6000B02485CD209680A4CA4C6F9E20658C84CA24B2100320C99B20899EA4CA20CB8152 +:201E8000D0EA604C7C8D4C7C8C24C930112071A3209E80566572696679696E679017206EF7 +:201EA000A320239F209E80466F726D617474696E67A6CD86B2209E8020647269766520A55C +:201EC000CD200D81209E8020747261636B202020EA24C9300820349F8AF04585B5A2FF8E8A +:201EE0008210E886B4A90820E98020E980A5B420058120CCA4E6B4A5B4C5B5D0E824C910BA +:201F00001F20CAAF20239FA5B5C928D006A001A290D004A003A2208E070F8C060F20B2A43F +:201F20004C9680A0FF8C8210C89899000E99000FC8D0F76020A1A4AD060F2903AAAD070F24 +:201F4000A00A84B0A0FF38C8E5B0B0FBCA10F765B04898AA68F001E86038B0011866C62066 +:201F60005D8C20A28324C6301520C18041646472657373203A20204C656E6774680DAD0621 +:201F80000F290385C585C2AD070F85C438E90285C1B002C6C2A90285BBA90085BC85BF8528 +:201FA000C0AD050F29F8A8F028D0112048A020D482A5C438E5BBA5C5E5BC9015B9070F3868 +:201FC000E5BB85C108B9060F290328E5BC85C290DA84BD24C63009A5C105C2F0032024A037 +:201FE000A5C11865BF85BFA5C265C085C0A4BDD0BA24C6102EA8A6BFA9F838ED050F20739D +:20200000A020C180467265650DA5C438E5BFAAA5C5E5C0A8AD050F2073A020C18055736508 +:20202000640DEA60A5BC201D81A5BB20158120C18020202020203A2020A5C2201D81A5C1AA +:20204000201581A90D20E3FFB9060F4820228285BC6829031865BC85BCB9040FF002A90181 +:202060001879050F9002E6BC1879070F85BB9002E6BC60202B8220128120C1802046696CF0 +:2020800065732086BC84BD98201D818A20158120C18020536563746F727320A90085BB853D +:2020A000BEA21F86C1A2099D0010CA10FA06BB26BC26BD26BEA200A009BD00102AC90A901F +:2020C00002E90A9D0010E88810EFC6C110DFA020A205D002A02CBD0010D008C02CF004A946 +:2020E00020D005A02C18693020E3FFE003D0049820E3FFCA10DC20C18020427974657320BD +:20210000EA60A2FFD002A200A0FF8C821085B086B1203B806885B86885B9A0008C9F108CAA +:202120000001C8F007B1B8990001D0F6A5B0208A8DA5B1F018A92F990001C8AD8910208A02 +:202140008DAD8A10208A8DAD8B10208A8DA9009900014C0001A9DD8D8210A90085BE85BF90 +:2021600085C0207BA9206CA1F00F1860ADFE0FC955D005ADFF0FC9AA60AD000EC9EBD00EAA +:20218000AD020EC990D007AD0C0EC902F032ADC60F0A85BEADC70F2A85BFADC80F2AB00F71 +:2021A00085C0ADC90FD008207BA9206CA1F011206880FF4361726420666F726D61743F00A2 +:2021C000AD0B0ED0EAAD0C0EC902D0E3AD110E85C1AD120E4A66C14A66C14A66C185C2AD14 +:2021E0000D0E85C3AD0E0E0A2E0F0EB0C265BE85BEAD0F0E65BF85BF9002E6C0AD110E0D43 +:20220000120EF014AD160E8D240EAD170E8D250EA9008D260E8D270E0E240E2E250E2E2657 +:202220000E2E270EB089AE100E18A5BE6D240E85BEA5BF6D250E85BFA5C06D260E85C0CA6E +:20224000D0E7207BA93860A20086C4A90E85C5E8A00BB1C4290FD00D88B9F010D1C4D00536 +:202260008810F6301018A5C4692085C4D0E2E6C5CA10DD386018A5BE65C185BEA5BF65C222 +:2022800085BF9002E6C0A01BB1C44888B1C438E90285C468E90085C505C4F01A06C426C5BE +:2022A000A6C318A5BE65C485BEA5BF65C585BF9002E6C0CAD0EC1860A00DA90059E0100A1D +:2022C000A207900249120ACAD0F8900249128810EB09016020058420B8A2CDEE10D00160D3 +:2022E000204E80FF53756D0020058420B8A28DEE1060A20A203EA32013A98DED108CEC1013 +:20230000A9008DEB1020E8A22055A190052047A2B017A5BE8DE810A5BF8DE910A5C08DEAB9 +:2023200010A9FF8DEB104CE8A2206880FF496D616765206E6F7420666F756E642100A00A25 +:20234000B94AA399F0108810F76042454542202020204D4D42A6CDBDE4100A30266020726F +:20236000A3BDE01085B8BDE410290185B96038B00118A6CDBDE41010182908B003D01E60D8 +:20238000F02D60204380C972656164206F6E6C7900206880C74E6F20646973630020438064 +:2023A000C76E6F7420666F726D617474656400204380C7616C726561647920666F726D615E +:2023C00074746564002071A3BDE4106ABDE01008AAA90085BE2A4885C08A0A26C085BF8AB3 +:2023E00065BF85BF68690065C085C066BE8A286A66BE4A66BE4A66BE65BF85BFA5C06900C4 +:2024000085C038A5BE091F6DE81085BEA5BF6DE91085BFA5C06DEA1085C06020C5A318AD40 +:20242000971065BE85BEAD961029034865BF85BF9002E6C0AD951085CEAD96104A4A4A4A08 +:20244000290385CFAD941085C3F006E6CED002E6CF18AD971065CEAA6865CFC9039006D01B +:2024600005E021B00160204380FF6F766572666C6F77002075AB201BA420D6A9AD9E10F0E6 +:202480000320708D20E4ABA901602075AB201BA42055A32082AA4C7CA4AD8210C5CDD001D2 +:2024A000602075AB20C5A3207BA9A5CD8D82104CE4ABAD040F18F869018D040FD82075AB52 +:2024C00020C5A32055A32092A94CAAA4A5B4D005A6CD20C8A3A90585B624C93006207BA9EB +:2024E0004CE6A42092A9E6BEE6BED006E6BFD002E6C0C6B6D0E360A5B96AA4B8C898D0018C +:20250000382A2A2A2A2A48291FA8686A29F085B0982901090E85B19829FE09806018205E0C +:20252000A320F7A420A4A5A00FB1B0C9FFF002AA60204380C76E756D626572206E6F74203C +:2025400076616C6964008A48A203BDE010C5B8D00CBDE4102901C5B9D0039DE410CA10EA6D +:2025600068AA60A9C085B72021A5F00A1004A9C8D002A98085B720D4A22046A5A6CDA5B837 +:202580009DE010A5B905B79DE4104CE8A2297E186DE81085BEADE910690085BFADEA106963 +:2025A0000085C060CD8210F0FA8D8210208DA54C7BA9AD8210208DA54C92A9203D8420F73C +:2025C000A485B220A4A54C1FA6A90085B585B685B885B9A91085B0A90E85B1A98085B220B2 +:2025E000A4A54C1FA6C9FFF03E24B7303818A5B0691085B0D016A5B1490185B16AB00DA5A5 +:20260000B26902C9A0F02085B220A4A5E6B8D002E6B9F818A5B5690185B59002E6B6D8A0C1 +:202620000FB1B030C01860A9FF85B93860A9038D60FEAD62FE09038D62FE2077A7A91C8D17 +:202640006EFE602088A7A9042C6DFEF0FB2077A7AD6AFE608E60FE8D60FE8E60FE8D60FECF +:202660008E60FE8D60FE8E60FE8D60FE8E60FE8D60FE8E60FE8D60FE8E60FE8D60FEAD6A10 +:20268000FE600A2A29FD8D60FE09028D60FE2A29FD8D60FE09028D60FE2A29FD8D60FE092C +:2026A000028D60FE2A29FD8D60FE09028D60FE2A29FD8D60FE09028D60FE2A29FD8D60FE90 +:2026C00009028D60FE2A29FD8D60FE09028D60FE2A29FD8D60FE09028D60FE60A002A201FD +:2026E0002043A688D0FA60A200A007BD87102082A6E888D0F620FBA64C54A6A201A0008832 +:20270000F00FA9038E60FE8D60FEAD6AFE2901D0EEA90360A2012043A6C9FED0F960A200F0 +:20272000F002A6C32088A7A000AD9E10D00A205CA791BCC8CAD0F760205CA78DE5FEEAEA8A +:20274000EAC8CAD0F360A2FF8E8210E82088A7A000205CA799000EC8CAD0F660A904E00132 +:20276000F0092C6DFEF0FBAD6AFE602C6DFEF0FB2077A7AD6AFE60AD6BFE29E38D6BFEAD6F +:2027800062FE09028D62FE60AD62FE29FD8D62FEAD6BFE29E309088D6BFEAD6AFE6020DCC7 +:2027A000A6A9FE4C82A620DCA620FBA62066A6A8291FC905D030A2012043A6C9FFD0F9606E +:2027C000AC9E10D009B1BC2082A6C8D0F860A000ADE5FE2082A6C8D0F760A000B9000E2033 +:2027E00082A6C8D0F760982002A1C54D4D4320577269746520726573706F6E736520666124 +:20280000756C7420008D8810A9008D89108D8A108D8B108D8C10A9FF8D87108D8D108D8EC6 +:202820001060203180A9008D9F10A93285C2A00A20DEA6A9402005A8A9958D8D1020E7A637 +:202840002981C901F0034CEEA8A9018DDE10A9482005A8A9018D8B10A9AA8D8C10A9878D41 +:202860008D1020E7A6C901F024A9028DDE10A9412005A820E7A6C90290034CEEA824FF3013 +:202880006DC900D0E9A9028DDE104CD5A82043A62043A62043A62043A6A9772005A820E742 +:2028A000A6A9692005A8A9408D891020E7A6C900D0E7A97A2005A820E7A6C900D03020438E +:2028C000A62940482043A62043A62043A668D005A9028DDE10A9502005A8A9028D8B10206A +:2028E000E7A6D016A9408D9F10203B801860C6C2F0034C2EA8203B8038602002A1FF536563 +:202900007420626C6F636B206C656E206572726F722000A94A2005A82033A9A01020DEA63F +:202920002043A6A84C43A6A958D002A9512005A84CEFAB20E7A6D0034C14A72002A1C54DD5 +:202940004D432052656164206661756C74200020E7A6D0034C9EA72002A1C54D4D43205702 +:2029600072697465206661756C742000203180A9008D9E1085BCA90E85BD60206CA9202B78 +:20298000A92033A9201EA7E6BD201EA720DCA64C3B80206CA92027A9204FA920C0A7E6BD15 +:2029A00020C0A720A6A74C3B8048AD90108D7210AD91108D7310AD74102D75100DD61049FB +:2029C000FF8D9E1038F00D20618DA272A010684820060418686020318020E0A94C3B80601B +:2029E000A6CED004A5CFF0F7A90120A9A9A6CE66BE66C21007E886CED002E6CF06BE202B6F +:202A0000A9A5CFD00EA6CEE003B008A5C3D043E001F03624C2100D2033A9A00084C220DE47 +:202A2000A64C2CAA2033A9201EA7E6BD201EA7E6BD20DCA62023ACA9FE2021ABF0A1A5CF99 +:202A4000D0E2E003B0DE4C0BAA2033A9201EA74C79AA2033A924C21008A00020DEA64C6A0E +:202A6000AAC6CEF005201EA7E6BD2022A79849FFA8C820DEA6A5CED005A00020DEA64CDC0A +:202A8000A660203180208BAA4C3B80A6CED004A5CFF0EEA90020A9A9A6CE66BE6A06BE4840 +:202AA0002027A9681032A9518D88102033A92046A7A00020DEA6A00220DEA6A9588D8810A4 +:202AC000204FA920DAA720C0A720A6A7A9FF2021ABF0AEE6BD2023ACA5CFD02DA6CEF04070 +:202AE000CAD026A9518D88102033A9A00020DEA62046A7A00220DEA6A9588D8810204FA926 +:202B000020C0A720DAA74CA6A7204FA920C0A7E6BD20C0A7E6BD20A6A7A9FE2021ABD0B503 +:202B2000601865CE85CEAAA9FF65CF85CF05CE60203180A9008D9E10A202B5B395BECA109C +:202B4000F9202BA92033A9A90085BCA91085BDA90885C32022A7A0F820DEA6A90885BC8512 +:202B6000C32022A7A0FA20DEA64C3B802005842075AB4CE4ABA209B5BC9D9010CA10F82055 +:202B80002DA62C9F1070082022A8B01220B9AB20D4A2ADEB10F0016020F2A24CA8AB206875 +:202BA00080FF436172643F00A90085B9A20386CD86B82063A5CA10F66020D4A2ADEB10F03A +:202BC0000D2013A9CDED10D006CCEC10D00160A9008D9F10206880FF57726F6E6720636196 +:202BE00072642100A209BD901095BCCA10F860ADDE10C902D015A5C08D8910A5BF8D8A10F2 +:202C0000A5BE8D8B10A9008D8C1060A9008D8910A5C04A8D8A10A5BF6A8D8B10A5BE6A8D02 +:202C20008C1060ADDE10C902D011EE8B10EE8B10D008EE8A10D003EE891060EE8C10F0EDBE +:202C400060A98085B220A9A5A91085B0A90E85B1A9001820CFA3A203B5BE95B3CA10F9A045 +:202C60000FB1B0C9FFF0472030AB18A5B3692085B3A5B4690385B49002E6B5A00BB900101A +:202C800091B08810F818A5B0691085B0D0D1A5B1490185B16AB0C820B2A518A5B26902C9D5 +:202CA000A0F01685B220788D20A9A54C5FACA5B0D00466B190034CB2A560A2008E0E101811 +:202CC00020C2FFF02020C5FFB01BC92AF024C9619006C97BB00249209D0010E8E00CD0E5F8 +:202CE00020C5FF9012C90DD01DA9009D00108E0D10608D0E10F0E9206880FF426164207305 +:202D00007472696E67004CD299A000AE0D10F017B1B0F025C9619006C97BB0024920D900F8 +:202D200010D016C8CAD0E9B1B0F00CAD0D10C90CF005AD0E10F00218603860B005A92020F6 +:202D4000E980A2202095ADA92020E980A00FB1B03025A000B1B0F00820E980C8C00CD0F455 +:202D6000A92020E980C8C00DD0F8AAA00FB1B0D002A2508A4CE980A00D201982A9554CE94C +:202D800080202E84BDE01085B8BDE410290185B9203D84A200A004A5B6209EADA5B5484A05 +:202DA0004A4A4A20A7AD68290FF008A2301869304CE98088D002A2308A4CE980209E804404 +:202DC0005554494C53206279204D617274696E204D6174686572202832303131290DEA60CF +:202DE00020CC99A90085CD20CE8C2063A5A9004CCB8D20978C4C63A5205D8C20D4A2A6CDBB +:202E00008A9DE4104CE8A2A90085B785AA85AB209284B02586A886B885A985B9209284B0B4 +:202E20001F86A885A9E4B8E5B9101B206880FF4261642072616E676500A2FE86A8E886A992 +:202E4000A90085B885B9E6A8D002E6A920BAAC20BBA5A200AD0D10D004CA8E0E10A5B93015 +:202E60002AA5B8C5A8A5B9E5A9B0202009ADB012203BADF818A5AA690185AAA5AB690085CC +:202E8000ABD820788D20EDA54C5DAEA98620F4FFE000F003209680A5ABA200A004209EAD35 +:202EA000A5AA209EAD209E802064697363A5ABD004C6AAF005A97320E980209E8020666FF6 +:202EC000756E64EA4C96804CD29920CB81D0F8A20086A886A986AA86ABA98020A4A5A910C9 +:202EE00085B0A90E85B1A00FB1B0C9FFF03CF8A8100B18A5A8690185A89002E6A918A5AA6D +:202F0000690185AA9002E6ABD818A5B0691085B0D0D4A5B1490185B16AB0CBAD82106902F9 +:202F2000C9A0F00620A4A54CE6AEA004A200A5A9209EADA5A8209EAD209E80206F6620A23D +:202F400000A004A5AB209EADA5AA209EAD209E802064697363A5ABD006A5AAC901F005A97A +:202F60007320E980209E8020667265652028756E666F726D617474656429EA4C968084B3B8 +:202F8000A9FF85B7A90385CDA4B320CB81F00520658C9002A90048AAA93A20EEFF188A695E +:202FA0003020EEFFBDE4101014290185B9BDE01085B820BBA5C9FFF00438203BAD20E7FF2B +:202FC00068C5CDF004690190CD6038201EA51027A900A891B0C8C00FD0F998D00FA900F088 +:202FE00002A90F48201DA5300B68A00F91B020B2A54C63A54C9DA34CAFA320C99B201DA5FF +:203000002055A320BBA5209E804B696C6CEA38203BAD209E80203A20EA20349C0820968054 +:2030200028D006A9F0484CE4AF60A90F4838201EA530B610C22070B0B0242063A5209E8025 +:203040004469736320A2002095AD209E8020696E203AA5CDA200A002209EAD4CE7FF20685F +:2030600080FF4E6F206672656520646973637300A9FF85B720C9A5B00EC9F0F00920EDA588 +:20308000B005C9F0D0F7186020CB81F00AA20ADDAAB0F006CA10F84C038B8A29FEAABDB5CB +:2030A000B048BDB4B048C84C5D8C72526B4B6E4E7555705029B0F9AF34B0E0AFDCAFA001D2 +:2030C000A202203A81A00084C384CFB1B0300320D688206CABA005B1B018690785B220EB1E +:2030E000B0A4B291B0A90060A90060A6CDBDE41010212908D01AC8B1B085B3C953F01AC9B7 +:2031000057F016C94BD0E1BDE4100A100CA91260A91E60A91060A9FF60A5B2C90AD0F7A9B9 +:203120000085B5C8B1B0C950B0E60A85B40A26B50A26B565B485B49002E6B5C8B1B0C90AF5 +:20314000B0CE1865B485B49002E6B5C8B1B0291FF03185CE188871B0C90BB0B4A6CD20C821 +:20316000A318A5BE65B485BEA5BF65B585BF9002E6C0A5B3C94BF00E20D6A9AD9E10F00384 +:2031800020708DA900602082AA4C7BB1A90085A820ADB2A90F85AA20A0B23820C2FF84AB4F +:2031A00018F01F209284B01AC900D0F7E010B0F38A84AB85AA2016B2A4AB20CB8184ABD03B +:2031C000E26066A824A8100520D5B190032016B2C6AA10F060A90985F6A98085F7A4ABB1FB +:2031E000F2C90DF022C922F01EC8C92AF02620208385AE2092B2F00DA6AEE023F0E120206C +:2032000083C5AEF0DA18602092B2F008C920F0F7C90DD0F13860A4AAB1B4F0E948209E8009 +:20322000526F6D2098200281209E80203A20A92820E98068483004A020D002A0539820E9E9 +:203240008068A0200A1002A04C9820E980A92920E980202681205DB22096803860A9078549 +:20326000F6A98085F72092B285AEE6F6A01E2085B2B007202681882085B260C920B002A92A +:203280002020E98088A5F6C5AEB0062092B2D0EB18609848A4AA20B9FFE6F6AA68A88A601C +:2032A000202E84A9AA20519386B484B560BAA9009D070160A6F4BDF00D293F85ADE6AD60C9 +:2032C00020CFB3A900F00520CFB3A9FF85ABA94020CEFFA8A90DC000D01E4C428220D7FF4C +:2032E000B01EC90AF0F728D0084820FAB32026816820E3FF24FF301F25ABC90D084CDDB205 +:203300002820E7FFA9004CCEFF20CFB3A94020CEFFA8F0C620B4B224FF3064A5A9200EB47A +:20332000A5A8200EB4202681A90885ACA20020D7FFB00D81AC200EB4202681C6ACD0EF1841 +:20334000089013A92A20E3FF20E3FF202681A90081ACC6ACD0ED206CB320E7FFA9081865AC +:20336000A885A89002E6A928B09A90ABA90885ACA200A1AC20858420E3FFC6ACD0F26020FA +:2033800046932004B34C7C8D20CFB3A98020CEFF85AB20FAB320268120B4B2A2ACA0FF84B5 +:2033A000AE84B0C884ACA92085AF9820F1FF0884AAA4ABA200F007A1AC20D4FFE6ACA5AC4D +:2033C000C5AAD0F328B0B8A90D20D4FF4C92B3BAA9009D070120F3B3C90DD0034CD299A91A +:2033E0000085A885A948981865F2AAA5F36900A86860C8B1F2C920F0F960F818A5A86901A4 +:2034000085A8A5A9690085A9D8200EB4A5A848202A822016B468202F8120E3FF3860370587 +:203420009605F2050706270668065E052D0520054205A905D105868896981818821820C5E2 +:2034400006A820C50620D4FF4C9C0520C506A820D7FF4C3A0520E0FF6A2095062A4C9E05A2 +:2034600020C506F00B482082056820CEFF4C9E0520C506A8A90020CEFF4C9C0520C506A88A +:20348000A20420C50695FFCAD0F820C50620DAFF209506A203B500209506CA10F84C36006D +:2034A000A200A00020C506990007C8F004C90DD0F3A0076020820520F7FFA97F2CE2FE50A2 +:2034C000FB8DE3FE4C3600A21020C5069501CAD0F820820586008401A00020C50620DDFF03 +:2034E000209506A210B501209506CAD0F8F0D5A20D20C50695FFCAD0F820C506A00020D15B +:20350000FF48A20CB500209506CA10F8684C3A0520C506AA20C50620F4FF2CE2FE50FB8E09 +:20352000E3FE4C360020C506AA20C506A820C50620F4FF499DF0EB6A2095062CE2FE50FBC5 +:203540008CE3FE70D520C506A82CE2FE10FBAEE3FECA300F2CE2FE10FBADE3FE9D2801CA42 +:2035600010F298A228A00120F1FF2CE2FE10FBAEE3FECA300EBC28012CE2FE50FB8CE3FEDF +:20358000CA10F24C3600A20420C5069500CA10F8E8A0008A20F1FF9005A9FF4C9E05A200F5 +:2035A000A97F209506BD0007209506E8C90DD0F54C36002CE2FE50FB8DE3FE602CE6FE501F +:2035C000FB8DE7FE60A5FF386A300F48A90020BC069820BC068A20BC06682CE0FE50FB8D96 +:2035E000E1FE602CE2FE10FBADE3FE60C909D02A9848B1F2C90DD01EA9E9205193A6F498AC +:20360000F014209E800D5455424520484F535420322E33300DEA68A8A909C9FE905CD01B93 +:20362000C000F056A206A91420F4FF2CE0FE10FBADE1FEF04320EEFF4C2BB6A9AD8D2002F9 +:20364000A9068D2102A9168D0202A9008D0302A98E8DE0FEA000B9BCB6990004B91EB49952 +:203660000005B91EB599000688D0EB202104A241BD7BB69516CA10F8A90060A9FF209E06CF +:20368000ADE3FEA900209506A8B1FD209506C8B1FD209506AAD0F7A2FF9A582CE0FE1006D2 +:2036A000ADE1FE20EEFF2CE2FE10F02CE0FE30F0AEE3FE86516C0005008000004C84044CC4 +:2036C000A706C980902BC9C0B01A0940C515D0200878A905209E06A515209E0628A980858D +:2036E000158514600614B006C515F0041860851560087884138612209E06AAA003A5152012 +:203700009E06B112209E068810F8A0188CE0FEBD18058DE0FE4A4A90062CE5FE2CE5FE201F +:203720009E062CE6FE50FBB00DE004D0112014042095064C32004A9005A0888CE0FE28609E +:2037400058B00AD0034C9C05AE8D02F0E0A9FF20060490F920CE040878A90720C704A00082 +:203760008400B1008DE5FEEAEAEAC8D0F528E654D006E655D002E656E601240150D920CEB5 +:2037800004A904A000A2534C0604A98085548501A9202D0680A88453F019AE0780E8BD0027 +:2037A00080D0FABD01808553BD02808554BC0380BD048085568455600000000000000000FD +:2037C0000000000000000000000000000000000000000000000000000000000000000000E9 +:2037E0000000000000000000000000000000000000000000000000000000000000000000C9 +:203800000000000000000000000000000000000000000000000000000000000000000000A8 +:20382000000000000000000000000000000000000000000000000000000000000000000088 +:20384000000000000000000000000000000000000000000000000000000000000000000068 +:20386000000000000000000000000000000000000000000000000000000000000000000048 +:20388000000000000000000000000000000000000000000000000000000000000000000028 +:2038A000000000000000000000000000000000000000000000000000000000000000000008 +:2038C0000000000000000000000000000000000000000000000000000000000000000000E8 +:2038E0000000000000000000000000000000000000000000000000000000000000000000C8 +:203900000000000000000000000000000000000000000000000000000000000000000000A7 +:20392000000000000000000000000000000000000000000000000000000000000000000087 +:20394000000000000000000000000000000000000000000000000000000000000000000067 +:20396000000000000000000000000000000000000000000000000000000000000000000047 +:20398000000000000000000000000000000000000000000000000000000000000000000027 +:2039A000000000000000000000000000000000000000000000000000000000000000000007 +:2039C0000000000000000000000000000000000000000000000000000000000000000000E7 +:2039E0000000000000000000000000000000000000000000000000000000000000000000C7 +:203A00000000000000000000000000000000000000000000000000000000000000000000A6 +:203A2000000000000000000000000000000000000000000000000000000000000000000086 +:203A4000000000000000000000000000000000000000000000000000000000000000000066 +:203A6000000000000000000000000000000000000000000000000000000000000000000046 +:203A8000000000000000000000000000000000000000000000000000000000000000000026 +:203AA000000000000000000000000000000000000000000000000000000000000000000006 +:203AC0000000000000000000000000000000000000000000000000000000000000000000E6 +:203AE0000000000000000000000000000000000000000000000000000000000000000000C6 +:203B00000000000000000000000000000000000000000000000000000000000000000000A5 +:203B2000000000000000000000000000000000000000000000000000000000000000000085 +:203B4000000000000000000000000000000000000000000000000000000000000000000065 +:203B6000000000000000000000000000000000000000000000000000000000000000000045 +:203B8000000000000000000000000000000000000000000000000000000000000000000025 +:203BA000000000000000000000000000000000000000000000000000000000000000000005 +:203BC0000000000000000000000000000000000000000000000000000000000000000000E5 +:203BE0000000000000000000000000000000000000000000000000000000000000000000C5 +:203C00000000000000000000000000000000000000000000000000000000000000000000A4 +:203C2000000000000000000000000000000000000000000000000000000000000000000084 +:203C4000000000000000000000000000000000000000000000000000000000000000000064 +:203C6000000000000000000000000000000000000000000000000000000000000000000044 +:203C8000000000000000000000000000000000000000000000000000000000000000000024 +:203CA000000000000000000000000000000000000000000000000000000000000000000004 +:203CC0000000000000000000000000000000000000000000000000000000000000000000E4 +:203CE0000000000000000000000000000000000000000000000000000000000000000000C4 +:203D00000000000000000000000000000000000000000000000000000000000000000000A3 +:203D2000000000000000000000000000000000000000000000000000000000000000000083 +:203D4000000000000000000000000000000000000000000000000000000000000000000063 +:203D6000000000000000000000000000000000000000000000000000000000000000000043 +:203D8000000000000000000000000000000000000000000000000000000000000000000023 +:203DA000000000000000000000000000000000000000000000000000000000000000000003 +:203DC0000000000000000000000000000000000000000000000000000000000000000000E3 +:203DE0000000000000000000000000000000000000000000000000000000000000000000C3 +:203E00000000000000000000000000000000000000000000000000000000000000000000A2 +:203E2000000000000000000000000000000000000000000000000000000000000000000082 +:203E4000000000000000000000000000000000000000000000000000000000000000000062 +:203E6000000000000000000000000000000000000000000000000000000000000000000042 +:203E8000000000000000000000000000000000000000000000000000000000000000000022 +:203EA000000000000000000000000000000000000000000000000000000000000000000002 +:203EC0000000000000000000000000000000000000000000000000000000000000000000E2 +:203EE0000000000000000000000000000000000000000000000000000000000000000000C2 +:203F00000000000000000000000000000000000000000000000000000000000000000000A1 +:203F2000000000000000000000000000000000000000000000000000000000000000000081 +:203F4000000000000000000000000000000000000000000000000000000000000000000061 +:203F6000000000000000000000000000000000000000000000000000000000000000000041 +:203F8000000000000000000000000000000000000000000000000000000000000000000021 +:203FA000000000000000000000000000000000000000000000000000000000000000000001 +:203FC0000000000000000000000000000000000000000000000000000000000000000000E1 +:203FE0000000000000000000000000000000000000000000000000000000000000000000C1 +:00000001FF diff --git a/cores/bbc/roms/mmfs.rom b/cores/bbc/roms/mmfs.rom new file mode 100644 index 0000000000000000000000000000000000000000..3e82d4534969f7f2253a0537e3e04f7d8aeeaf1e GIT binary patch literal 16384 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