// FFin1 : Atari 2600 bank switcher for 2K/4K games
// Original 23/07/2014
// Update : 22/08/2014
// Fit into XC9536 CPLD
// ISE 12.1

`timescale 1ns/100ps

// Firware is used to control bank-switching mechanism at boot-up.
// At the initial state, bank 0 is set and should correspond to the bootloader bank.
// When bootloader needs to switch to a bank (1 to 255, 0 is the bootloader), 
// it performs a read at @1000h + <bank_number>. The data coming from the ROM 
// is memorized and applied to the upper rom addresses (A19 - A12).
// To work properly, ROM (1Mo) should be splitted into 256 * 4Ko areas.
// Areas from 1 to 255 should contain 4Ko image games (or 2x dupplicated 2Ko games).
// Area 0 is reserved to bootloader. First 256 bytes of the bank 0 MUST contain values, such as :
// 1000h : 00h
// 1001h : 01h
// ...
// 10FEh : FEh
// 10FFh : FFh
// Note : On 2600, A15, A14 & A13 do not exist. When A12=1, it means the system accesses to the ROM.
// So, F000h or 1000h have the same effect.

// -------------------------------------
// UCF (XC9536 PLCC44)
// -------------------------------------
// NET "bus_a<4>"  LOC = "P28"  ; # A12
// NET "bus_a<3>"  LOC = "P27"  ;  # A11
// NET "bus_a<2>"  LOC = "P26"  ;  # A10
// NET "bus_a<1>"  LOC = "P25"  ; # A9
// NET "bus_a<0>"  LOC = "P24"  ; # A8

// NET "bus_d<7>"  LOC = "P4"  ;
// NET "bus_d<6>"  LOC = "P3"  ;
// NET "bus_d<5>"  LOC = "P2"  ;
// NET "bus_d<4>"  LOC = "P1"  ;
// NET "bus_d<3>"  LOC = "P44"  ;
// NET "bus_d<2>"  LOC = "P43"  ;
// NET "bus_d<1>"  LOC = "P42"  ;
// NET "bus_d<0>"  LOC = "P40"  ;

// NET "reset_n"  LOC = "P8"  ;

// NET "rom_bank<7>"  LOC = "P18"  ; # XA19
// NET "rom_bank<6>"  LOC = "P19"  ; # XA18
// NET "rom_bank<5>"  LOC = "P20"  ; # XA17
// NET "rom_bank<4>"  LOC = "P38"  ; # XA16
// NET "rom_bank<3>"  LOC = "P37"  ; # XA15
// NET "rom_bank<2>"  LOC = "P36"  ; # XA14
// NET "rom_bank<1>"  LOC = "P33"  ; # XA13
// NET "rom_bank<0>"  LOC = "P29"  ; # XA12

// NET "rom_cs_n"  LOC = "P35"  ;
// NET "rom_oe_n"  LOC = "P34"  ;
// ----------------------------------------

module FFin1 (reset_n, bus_a, bus_d, rom_bank, rom_cs_n, rom_oe_n);
  input reset_n; // Power on / user reset
  input [4:0] bus_a; // A12 - A8 from BUS (5 bits)
  input [7:0] bus_d; // D7 - D0 from BUS (8 bits)
  output [7:0] rom_bank; // A19 - A12 (8 bits)
  output rom_cs_n;
  output rom_oe_n;

  wire [7:0] rom_bank;
  reg [7:0] cpld_bank;

  reg boot_n;

  wire flip;

  assign rom_bank = cpld_bank;
  assign rom_cs_n = ~bus_a[4]; // A12=1
  assign rom_oe_n = 0; // Always enabled

  assign flip = (bus_a[4:0] == 5'b10000) ? 1'b0 : 1'b1;

  always @(posedge flip or negedge reset_n)
  begin
    if (~reset_n) begin // Power-up or user reset
	cpld_bank <= 7'b0000000; // Rom Bank 0 (bootloader)
	boot_n <= 0; // Bank-switching is allowed
    end
    else if (~boot_n) begin 
	cpld_bank <= bus_d;
	boot_n <= 1; // Banks-witching is blocked
    end
  end 
endmodule

