]> git.ozlabs.org Git - minimigmac.git/commitdiff
Initial commit
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>
Sun, 19 Jun 2011 12:00:10 +0000 (22:00 +1000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Sun, 19 Jun 2011 12:00:10 +0000 (22:00 +1000)
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
45 files changed:
.gitignore [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
fpga/Makefile [new file with mode: 0644]
fpga/addr_decode.v [new file with mode: 0644]
fpga/cpu_intf.v [new file with mode: 0644]
fpga/ctrl.v [new file with mode: 0644]
fpga/fpga_clocks.v [new file with mode: 0644]
fpga/fpga_clocks_dll.v [new file with mode: 0644]
fpga/iwm.v [new file with mode: 0644]
fpga/mem_intf.v [new file with mode: 0644]
fpga/minimigmac.ucf [new file with mode: 0644]
fpga/minimigmac.v [new file with mode: 0644]
fpga/ncr5380.v [new file with mode: 0644]
fpga/ps2.v [new file with mode: 0644]
fpga/ps2_kbd.v [new file with mode: 0644]
fpga/ps2_mouse.v [new file with mode: 0644]
fpga/rtc.v [new file with mode: 0644]
fpga/scc.v [new file with mode: 0644]
fpga/scope.v [new file with mode: 0644]
fpga/sim_bench.v [new file with mode: 0644]
fpga/sim_clocks.v [new file with mode: 0644]
fpga/sim_kbd.v [new file with mode: 0644]
fpga/sim_mouse.v [new file with mode: 0644]
fpga/sim_pic.v [new file with mode: 0644]
fpga/sim_sram.v [new file with mode: 0644]
fpga/spi_backbus.v [new file with mode: 0644]
fpga/spi_slave.v [new file with mode: 0644]
fpga/via6522.v [new file with mode: 0644]
fpga/video.v [new file with mode: 0644]
fpga/xilinx/minimigmac.xise [new file with mode: 0644]
pic/18f252_mod.lkr [new file with mode: 0644]
pic/Makefile [new file with mode: 0644]
pic/fat16.c [new file with mode: 0644]
pic/fat16.h [new file with mode: 0644]
pic/fat16_defs.h [new file with mode: 0644]
pic/hardware.c [new file with mode: 0644]
pic/hardware.h [new file with mode: 0644]
pic/main.c [new file with mode: 0644]
pic/minimigmac.piklab [new file with mode: 0644]
pic/mmc.c [new file with mode: 0644]
pic/mmc.h [new file with mode: 0644]
pic/mmc_cmds.h [new file with mode: 0644]
pic/scsi.c [new file with mode: 0644]
pic/scsi.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a65a45b
--- /dev/null
@@ -0,0 +1,25 @@
+*.o
+*.d
+fpga/xilinx/*
+!fpga/xilinx/minimigmac.xise
+*.vpi
+*~
+*.bin
+*.elf
+fpga/testrom/testrom
+*.vcd
+*.vvp
+*.bak
+*.adb
+*.lst
+*.map
+*.hex
+*.cof
+*.cof
+*.cof
+*.cof
+*.cof
+*.cof
+*.cof
+*.cof
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..5e8b33c
--- /dev/null
+++ b/README
@@ -0,0 +1,100 @@
+Minimigmac v0.1
+---------------
+
+Copyright 2011 Ben Herrenschmidt <benh@kernel.crashing.org>
+
+Licenced under the GPL, see COPYING file.
+
+! Warning !
+
+A bunch of stuff aren't quite working yet, the whole thing actually
+has some stability issues when "hot", see the TODO file and help
+is welcome :-)
+
+This currently requires a 4M minimig to emulate a 2M MacPlus. I
+will probably try to make it work on a 2M minimig at some point.
+
+Build/Install instructions
+--------------------------
+
+* Build the FPGA using Xilinx tools, project file not included,
+  and put the result in a file named "MIMIGMAC.BIN" on the sd-card.
+
+  The toplevel module is "minimigmac" in minimigmac. The sim_*
+  modules and the Makefile are for simulation with iverilog.
+  This requires m68k_vpi.vpi amd m68k.v (my VERY CRUDE) VPI
+  module containing Hampa Hug's 68k emulator. It can be found
+  at http://ozlabs.org/~benh/m68k-vpi-0.1.tar.bz2
+
+  Or use my prebuilt binaries
+
+* Build the PIC code using sdcc (I use piklab as a front-end) and
+  download that to the PIC
+
+  Or use my prebuild binaries
+
+* Get your (legitimate) copy of the MacPlus ROM onto the sd-card
+  in a file named "MIMIGMAC.ROM"
+
+* Make a 20M (I used 21M just in case, see below) file to use as a
+  SCSI disk image on the sd-card, name it "MACHD.IMG"
+
+Hard disk image notes:
+----------------------
+
+Since we don't emulate floppies yet, you need to have a pre-installed
+hard disk image.
+
+This is not a raw filesystem image as used by most Mac emulators out
+there, but instead needs to be a raw image of a Mac formatted SCSI
+disk, more specifically a Seagate ST 225N :-) I've built such an image
+using the "pce" emulator from Hampa Hug, I'll put some instructions
+below. Eventually, when floppy emulation is implemented, things will
+be easier to install.
+
+The reason for that is that the Macintosh ROM doesn't contain a proper
+SCSI disk driver. Instead, it uses the low level SCSI Manager routines
+in "polled" mode at boot to load such a driver from the disk itself.
+
+Since I don't have at this point a free driver I can make visible there
+(by simulating the partition map etc...) we need to make things look
+good enough for Apple's own driver to work. Unfortunately, Apple's
+SCSI formatting/partitioning tool only works with "known" disks (well
+there are versions that work with any disk but not the one I've used
+so far).
+
+Note also that SCSI emulation is incomplete. It works with the driver
+coming with System 4.2 Finder 5.5, which doesn't do anything but
+READ6/WRITE6 (not even a GET_CAPACITY or INQUIRY) but you may have
+problems with other variants. I will fix that eventually.
+
+So to create the disk image, first I've downloaded & built PCE which
+you can find at http://www.hampa.ch/
+
+I've then modified the default MacPlus config file so that its scsi
+device section looks like:
+
+        device {
+                # The SCSI ID
+                id = 6
+
+                # The drive number. This number is used to identify
+                # a "disk" section. The number itself is meaningless.
+                drive = 128
+
+                # The vendor and product strings are returned by
+                # the SCSI Inquiry command.
+                vendor = " SEAGATE"
+                product = "          ST225N"
+        }
+
+(This is the same vendor/product strings that our PIC code passes
+to the Macintosh upon INQUIRY commands).
+
+I've created a 21MB disk image (in case the disk is a bit more than
+20MB though it's probably a bit less, ie, the Apple tool afaik uses
+a hard wired size from a table)
+
+I've then used System 4.2 floppy images to install a MacOS onto
+that disk image & copied it to my sd-card.
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..1f858fa
--- /dev/null
+++ b/TODO
@@ -0,0 +1,22 @@
+- Instability.... it tends to crash in odd ways when used
+  for a while (ie. when "hot"). Either a bad timing or
+  maybe a charge buildup on a hi-z line... I don't have
+  chipscope so that's going to be a bitch to debug, tho
+  maybe it will go away once I sort out how to do the timing
+  constraints properly (Help welcome !)
+- Sort out timings & DCM config
+- Keypad, capslock & other keyboard sim improvements.
+  Add a fifo from ps2 to mac, always keep first slot empty
+  unless fed a keypad cmd which fills two first slots
+- Add support for 2M variant of Minimig (1M MacPlus)
+- Simulate a 2.5M MacPlus on a 4M Minimig instead of a 2M one
+- SCSI Errors (try removing the SD-card !)
+- More SCSI commands (try Apple SD format tool...)
+- Embed an m68k SCSI driver to generate partitions from raw FS images ?
+- Sound
+- Floppy
+- OSD (to swap floppies)
+- RTC/nvram (how ? nvram file ? wire an i2c RTC to the PIC ?)
+- Can I fit more stuff in PIC anyways ?
+- Reset ? reboot not working... 
+- Some amount of serial emulation for the SCC (not just mouse)
diff --git a/fpga/Makefile b/fpga/Makefile
new file mode 100644 (file)
index 0000000..691566b
--- /dev/null
@@ -0,0 +1,25 @@
+default,all: sim_bench.vcd
+
+SRC := sim_bench.v sim_clocks.v sim_sram.v sim_pic.v sim_mouse.v sim_kbd.v
+SRC += m68k.v cpu_intf.v addr_decode.v
+SRC += mem_intf.v via6522.v iwm.v ncr5380.v scc.v
+SRC += spi_backbus.v spi_slave.v scope.v ctrl.v
+SRC += video.v rtc.v ps2.v ps2_mouse.v ps2_kbd.v
+SRC += minimigmac.v
+
+sim_bench.vvp: $(SRC)
+       iverilog -g2 -Wall -tvvp -D__IVERILOG__ -o $@ $^
+
+sim_bench.vcd: sim_bench.vvp m68k_vpi.vpi
+       vvp -M. -mm68k_vpi sim_bench.vvp
+
+wave: sim_bench.vcd
+       gtkwave ./sim_bench.vcd
+
+clean:
+       rm -f *.vvp
+       rm -f *.vcd
+       rm -f *.vpi
+       rm -f *.o
+distclean: clean
+       rm -f *~
diff --git a/fpga/addr_decode.v b/fpga/addr_decode.v
new file mode 100644 (file)
index 0000000..74e721f
--- /dev/null
@@ -0,0 +1,154 @@
+`timescale 1ns / 100ps
+
+/* Mac Plus address space
+ *
+ * - 000000  RAM (or ROM when overlay enabled)
+ * 
+ * - 400000  ROM
+ *     WARNING: The ROM does a fun test to decide whether SCSI is availble
+ *               or not (MacPlus vs. Mac512KE ?). It basically tests if
+ *              (0x420000) == (0x440000) and if it -is- then it assumes
+ *               SCSI is -not- there. So we must not "replicate" the ROM
+ *               in our address decoding. 
+ * 
+ * 
+ * - 580000  SCSI
+ *   580drn
+ *      ||\
+ *      |\ \ b0000=Rd b0001=Wr
+ *      | \- reg (b0000...b0111)
+ *      \----b00d0 where d=dack
+ *  (repeated to 5fffff)
+ *
+ * - 600000  RAM copy when overlay enabled (max 2M)
+ *           (should we just always decode that ?)
+ * - 800000
+ * 
+ * - 9FFFF7  ?? SCC Reset ? (*)
+ * 
+ * - 9FFFF8  SCC Rd (LDS=read UDS=write)
+ * - BFFFF9  SCC Wr
+ *     +0 Ch B control
+ *     +2 Ch A control
+ *     +4 Ch B data
+ *     +6 Ch B data
+ * 
+ * - DFE1FF  IWM
+ *
+ *     + 0000 = DFE1FF = ph0L   : CA0 = 0
+ *     + 0200 = DFE3FF = ph0H   : CA0 = 1
+ *     + 0400 = DFE5FF = ph1L   : CA1 = 0
+ *     + 0600 = DFE7FF = ph1H   : CA1 = 1
+ *     + 0800 = DFE9FF = ph2L   : CA2 = 0
+ *     + 0A00 = DFEBFF = ph2H   : CA2 = 1
+ *     + 0C00 = DFEDFF = ph3L   : LSTRB = 0
+ *     + 0E00 = DFEFFF = ph3H   : LSTRB = 1
+ *      + 1000 = DFF1FF = mtrOff : ENABLE = 0
+ *      + 1200 = DFF3FF = mtrOn  : ENABLE = 1
+ *      + 1400 = DFF5FF = intDrv : SELECT = 0
+ *      + 1600 = DFF7FF = extDrv : SELECT = 1
+ *     + 1800 = DFF9FF = q6L    : Q6 = 0
+ *     + 1A00 = DFFBFF = q6H    : Q6 = 1
+ *     + 1C00 = DFFDFF = q7L    : Q7 = 0
+ *     + 1E00 = DFFFFF = q7H    : Q7 = 1
+ * 
+ * - EFE1FE  VIA
+ *   Ex[xxxr][rrr1]xx - rrrr is RS3..RS0
+ * 
+ *    Port A (reg 0x1 or 0xf)
+ *     0x07  O Sound volume
+ *     0x08  O 1=Main snd buf 0=Alt snd buf
+ *     0x10  O 1=ROM overlay (pullup, so set at boot)
+ *     0x20  O Disk SEL line
+ *     0x40  O 1=Main video 0=Alt video
+ *     0x80  I SCC WReq
+ * 
+ *    Port B (reg 0x0)
+ *     0x01 IO RTC data
+ *     0x02  O RTC clock
+ *     0x04  O RTC enable
+ *     0x08  I Mouse switch
+ *     0x10  I Mouse X2
+ *     0x20  I Mouse Y2
+ *     0x40  I HBlank
+ *     0x80  O 1=Sound disable 0=Enable
+ * 
+ * - F00000  PhaseRead (according FDiasm) (*)
+ *
+ * - F80000  Expansion ROM
+ * 
+ * - FFFFF?  Autovector interrupt sim (we only check for top 6 bits 1)
+ *
+ * I am not certain how that "phase" setting works. Essentially we just
+ * return all 1's and the ROM seems happy enough. What happens is that
+ * very early at boot, the ROM does:
+ * 
+ *     movem.w (0xf0000000), d0-d2
+ * 
+ * Loading thus the 3 half words from f0000000, f0000002 and f0000004
+ * into d0, d1 and d2 respectively.
+ * 
+ * It then does a byte read of 9ffff7 which I believe resets the SCC.
+ * 
+ * It then extracts the low order bit of the 3 reads above, adds them
+ * up, substracts one, and check if the results is 0 or -1 (ie check
+ * if at least 2 of those reads had bit 0 set).
+ * 
+ * If yes, it then does a "phase adjust" by reading a word from 9ffff8,
+ * and that's about it.
+ */
+
+/*
+ * Combinational address decoder, returns a chip select for each
+ * device. Handles the special casing of the ROM overlay and major
+ * devices.
+ * 
+ * Returns cs_nack for a non-existing address region
+ * 
+ * We are quite lax with decoding to save logic, I haven't verified
+ * how "precise" a real macplus is but I haven't been anal at beeing
+ * as lax as possible neither...
+ */
+module addr_decode(addr, req, rom_ovl,
+                  cs_ram, cs_rom, cs_scsi, cs_scc, cs_iwm, cs_via,
+                  cs_ivec, cs_nack);
+       input [23:1]    addr;
+       input           req;    
+       input           rom_ovl;
+       output reg      cs_ram;
+       output reg      cs_rom;
+       output reg      cs_scsi;
+       output reg      cs_scc;
+       output reg      cs_iwm;
+       output reg      cs_via;
+       output reg      cs_ivec;
+       output reg      cs_nack;
+
+       always@(addr or req or rom_ovl) begin
+               cs_ram = 0;
+               cs_rom = 0;
+               cs_scsi = 0;
+               cs_scc = 0;
+               cs_iwm = 0;
+               cs_via = 0;
+               cs_ivec = 0;            
+               cs_nack = 0;
+
+               casez({req,addr[23:18],rom_ovl})
+                       8'b100????0: cs_ram = 1;
+                       8'b100????1: cs_rom = 1;
+                       8'b1010000?: cs_rom = 1;
+                       8'b101011??: cs_scsi = 1;
+                       8'b1011????: cs_ram = 1; /* only ovl=1 ? */
+                       8'b110?1???: cs_scc = 1;
+                       8'b11101???: cs_iwm = 1;
+                       8'b11110???: cs_via = 1;
+                       8'b1111111?: cs_ivec =1;                                
+                       default: cs_nack = req;
+               endcase
+       end
+endmodule
+
+       
+                       
+                       
\ No newline at end of file
diff --git a/fpga/cpu_intf.v b/fpga/cpu_intf.v
new file mode 100644 (file)
index 0000000..bc76d49
--- /dev/null
@@ -0,0 +1,526 @@
+`timescale 1ns / 100ps
+
+/*
+ * m68k CPU to internal bus interface
+ * 
+ * Devices & Memory bus protocol:
+ *
+ * We implement a two phase bus protocol to the outside world
+ * 
+ * - Address phase (phase 0) corresponds to CPU S4 & S5
+ *
+ * - Data phase (phase 1) corrsponds to CPU S6 & S7
+ * 
+ * Devices shall typically act on the clock edge at the begining of each
+ * phase, tho phase 0 can generally be ignored for simple devices. Phase 0
+ * shall not cause any action to be committed, but the phase 0 edge is
+ * when we sample the "ack" signal which allows transition to phase 1.
+ * 
+ * Thus phase 0 is only here to allow devices to delay/hold the bus, which
+ * is used by SCSI to make blind transfer reliable and our memory interface
+ * because I'm an idiot and didn't properly interleave CPU and video. I will
+ * probably use it from the SWIM interface too when transfering raw data.
+ * 
+ * Here are the signal that are valid at phase 0 and phase 1 clock
+ * edges. I also noted whether those signals remain valid in the clock
+ * cycle following phase 1 clock edge (ie, during phase 1 / S6/S7) since
+ * some devices such as memory rely on these.
+ * 
+ *  Signal     Phase 0 clk             Phase 1 clk             Phase 1
+ * 
+ *  bus_cs_xxx yes                     yes                     no (*)
+ *  bus_we     yes                     yes                     yes
+ *  bus_ube/lbe        no (**)                 yes                     no (**)
+ *  bus_addr   yes                     yes                     yes
+ *  bus_wdata  yes                     yes                     no (***)
+ *  bus_phase  0                       1                       0
+ * 
+ *  from device:
+ * 
+ *  bus_ack    no                      yes                     no (****)
+ *  bus_rdata  no                      yes                     n/a
+ * 
+ * (*) CS is lowered during phase 1 so that the next clock edge doesn't
+ *     get mistaken for a new phase 0 cycle.
+ * 
+ * (**) For a CPU initiated cycle, bus_ube/lbe are directly sourced from
+ *      the m68k's _UDS and _LDS lines, which for a write are only asserted
+ *      in S4, so right -after- phase 0 clock edge, and are released in S7
+ *      right after the negative edge following phase 1 clock edge. The
+ *      memory interface routes those signal directly to the SRAM, the
+ *      timing should be just right (with something like 15ns margin), but
+ *      we might want to establish constraints in the FPGA design to ensure
+ *      that. Alternatively, we could latch them on the clock negative edge
+ *      and maintain them all the way during phase 1 but that isnt necessary
+ *      for now I believe.
+ * 
+ * (***) For a CPU initiate cycle, bus_wdata is sources directly from the
+ *       m68k's data bus, which becomes invalid in S7, after the negedge
+ *       of the clock. There's a 15ns valid time between UDS/LDS going
+ *      up and data becoming invalid on a 16Mhz CPU, but here too we
+ *       might want to add constraints to the synthesis tool to guarantee
+ *       we stay within the margin.
+ * 
+ * (****) The CPU DTACK is set from bus_ack and sampled asynchronously
+ *        during phase 0 on the negative edge of the clock (end of S4),
+ *        and by our CPU interface logic on phase 1 clk to validate
+ *       actually going into phase 1 (else we remain in phase 0).
+ *       Thus dumb devices can just wire CS to ACK and devices that
+ *        want to hold the bus can do so by delaying ACK keeping the
+ *        interface in phase 0.
+ * 
+ * WARNING: While in phase 0, the CPU interface might decide to switch to
+ *          another master (the SPI interface is the only one for now),
+ *          so devices shouldn't commit/latch anything with the assumption
+ *          that a subsequent phase 0 will provide the same cs/address/...
+ *          signals. However, if a device does ack, then going into phase 1
+ *          on the next cycle is guaranteed.
+ */
+
+/* Backbus interface registers
+ *
+ * WARNING: Writing to the ADDR, DATA and MISC latches should only be
+ * done while the bus is in stopped state. If an attempt is made to
+ * write a latch that the CPU tries to update in the same cycle, the CPU
+ * wins and the SPI write is lost.
+ * 
+ * Similarily, reading from those latches may result in inconsistent
+ * values if done while the CPU interface is runnig as the SPI
+ * interface will latch the data at some "random" time after the
+ * strobe
+ */
+`define CPUINTF_REG_ADDR0      0
+`define CPUINTF_REG_ADDR1      1
+`define CPUINTF_REG_ADDR2      2
+`define CPUINTF_REG_DATA0      3
+`define CPUINTF_REG_DATA1      4
+`define CPUINTF_REG_MISC       5 /* bit 2: _UDS, bit 1: _LDS, bit 0: R_W */
+`define CPUINTF_REG_CTRL       6
+`define   CPUINTF_CTRL_STOP_BIT                0 /* Stop CPU */
+`define   CPUINTF_CTRL_RUN_BIT         1 /* Run CPU */
+`define   CPUINTF_CTRL_CYCLE_BIT       2 /* Issue an SPI generated bus cycle */
+`define   CPUINTF_CTRL_BKEN_BIT                6 /* Sticky, set with RUN to enable bkpt */
+`define   CPUINTF_CTRL_STEP_BIT                7 /* Sticky, set with RUN to single step */
+`define CPUINTF_REG_STAT       7 /* bus state machine */
+`define CPUINTF_REG_BKPT0      8
+`define CPUINTF_REG_BKPT1      9
+`define CPUINTF_REG_BKPT2      10
+
+module cpu_intf(input                  sysclk,
+               input                   reset,
+
+               /* m68k CPU pins */
+               inout [15:0]            cpu_data,
+               input [23:1]            cpu_addr,
+               input                   _cpu_as,
+               input                   _cpu_uds,
+               input                   _cpu_lds,
+               input                   cpu_r_w,
+               output                  _cpu_dtack,
+               /*inout                 _cpu_reset,*/
+
+               /* Backbus interface */
+               input [5:0]             bb_addr,
+               input [7:0]             bb_wdata,
+               output[7:0]             bb_rdata,
+               input                   bb_strobe,
+               input                   bb_wr,
+               
+               /* Common device bus interface */
+               output [23:1]           bus_addr,
+               output [15:0]           bus_wdata,
+               output                  bus_ube,
+               output                  bus_lbe,
+               output                  bus_we,
+               output                  bus_phase,
+
+               /* Per-device CS, ACK and data input */
+               output                  bus_cs_ram,
+               output                  bus_cs_rom,
+               input                   bus_ack_mem,
+               input [15:0]            bus_rdata_mem, /* RAM and ROM */
+               output                  bus_cs_scsi,
+               input                   bus_ack_scsi,
+               input [7:0]             bus_rdata_scsi,
+               output                  bus_cs_scc,
+               input                   bus_ack_scc,
+               input [7:0]             bus_rdata_scc,
+               output                  bus_cs_via,
+               input                   bus_ack_via,
+               input [7:0]             bus_rdata_via,
+               output                  bus_cs_iwm,
+               input                   bus_ack_iwm,
+               input [7:0]             bus_rdata_iwm,
+
+               /* Global control signals */
+               input                   rom_ovl /* from VIA */
+);
+       
+       /* For autovector simulation since we have no _avec pin */
+       wire            cs_ivec;
+
+       /* No target for a bus request */
+       wire            cs_nack;        
+
+       /* Bus request signal, activates chip selects */
+       wire            bus_req;
+
+       /* Cumulative ack result from devices */
+       wire            bus_ack;        
+
+       /* Address data and control latches (for spy and SPI ops) */
+       reg [23:1]      addr_latch;
+       reg [15:0]      data_latch;
+       reg             _uds_latch;
+       reg             _lds_latch;
+       reg             r_w_latch;
+
+       /* SPI control register */
+       reg [7:0]       spi_ctrl;       
+
+       /* Data bus mux */
+       wire [15:0]     bus_rdata;      /* Demuxed from devices */
+
+       /* Breakpoint */
+       reg [23:1]      addr_bkpt;
+       wire            addr_bphit;
+       
+       /* Signals from the SPI interface */
+       wire            spi_wreg;       
+       wire            spi_reg_addr0;
+       wire            spi_reg_addr1;
+       wire            spi_reg_addr2;
+       wire            spi_reg_data0;
+       wire            spi_reg_data1;
+       wire            spi_reg_misc;
+       wire            spi_reg_ctrl;
+       wire            spi_reg_stat;
+       wire            spi_reg_bkpt0;
+       wire            spi_reg_bkpt1;
+       wire            spi_reg_bkpt2;
+       
+       /* State machine */
+       localparam bi_state_idle = 8'h84; /* idle, addr & r_w from CPU */
+       localparam bi_state_cpu0 = 8'h85; /* cpu S4,S5 */
+       localparam bi_state_cpu1 = 8'h82; /* cpu S6,S7 */
+       localparam bi_state_spip = 8'h44; /* spi control, prep addr & r_w */
+       localparam bi_state_spi0 = 8'h45; /* spi control, pseudo S4,S5 */
+       localparam bi_state_spi1 = 8'h42; /* spi control, psuedo S6,S7 */
+       localparam bi_state_stop = 8'h48; /* spi control, stopped */
+
+       /* Note on individual state bits::
+           - 7 : CPU cycle
+           - 6 : SPI cycle
+           - 5 : unused
+           - 4 : unused
+           - 3 : stopped
+           - 2 : CS set based on _AS (or 1 for SPI)
+           - 1 : allow cpu data input and force dtack
+           - 0 : bus phase at next clock
+        
+          You may notice that we drop CS on CPU phase S6,S7. We must do
+          that for SPI as we must not have more than 2 phases with CS set
+          in one cycle, but we could leave bit 2 set for the CPU in that
+          stage as _AS is supposed to go down... doesn't matter much tho
+        */
+       reg [7:0]       state;
+       
+       /* Instanciate address decoder */       
+       addr_decode decode0(.addr(bus_addr),
+                           .req(bus_req),
+                           .rom_ovl(rom_ovl),
+                           .cs_ram(bus_cs_ram),
+                           .cs_rom(bus_cs_rom),
+                           .cs_scsi(bus_cs_scsi),
+                           .cs_scc(bus_cs_scc),
+                           .cs_iwm(bus_cs_iwm),
+                           .cs_via(bus_cs_via),
+                           .cs_ivec(cs_ivec),
+                           .cs_nack(cs_nack));
+
+       /* Generate bus address. Avoid propagating z state from the
+        * CPU to the FPGA by gating with _cpu_as
+        */
+       assign bus_addr = (!state[7]) ? addr_latch :
+                         _cpu_as ? 22'h3fffff : cpu_addr;
+
+       /* Data bus to CPU is hi-z unless CPU has r_w up -and- we are
+        * in phase 1. Should keep us safe from shorts. We always feed
+        * the CPU from the data latch
+        */
+       assign cpu_data = (cpu_r_w & state[1]) ? data_latch : 16'bz;
+
+       /* Data bus and control signals to devices */
+       assign bus_wdata = state[7] ? cpu_data : data_latch;
+       assign bus_ube = ~(state[7] ? _cpu_uds : _uds_latch);
+       assign bus_lbe = ~(state[7] ? _cpu_lds : _lds_latch);
+       assign bus_we = ~(state[7] ? cpu_r_w : r_w_latch);
+
+       /* Address decoder's enable signal which controls CS signals */
+       assign bus_req = state[2] & (state[6] | ~_cpu_as);
+
+       /* Bus phase output straight off the state machine */
+       assign bus_phase = state[0];
+
+       /* Data from devices demux (and generate autovector value) */
+       assign bus_rdata = (bus_cs_ram | bus_cs_rom) ? bus_rdata_mem :
+                          bus_cs_scsi ? { bus_rdata_scsi, 8'h0 } :
+                          bus_cs_scc  ? { bus_rdata_scc, 8'h0 } :
+                          bus_cs_via  ? { bus_rdata_via, 8'h0 } :
+                          bus_cs_iwm  ? { 8'h0, bus_rdata_iwm } :
+                          cs_ivec ? { 11'b0, 2'b11, bus_addr[3:1] } :
+`ifdef __IVERILOG__
+                          16'hbeef;
+`else  
+                          16'b1111_1111_1111_1111;
+`endif 
+
+       /* We OR all acks, devices shall not set ack if their CS isn't set
+        * additionally, we ack for ivec and nack signals from the decoder
+        * as those are handled locally
+        */
+       assign bus_ack = bus_ack_mem |
+                        bus_ack_scsi |
+                        bus_ack_scc |
+                        bus_ack_via |
+                        bus_ack_iwm |
+                        cs_ivec | cs_nack;
+`ifdef __IVERILOG__
+       /* Debug stuff, sim only */
+       always@(posedge sysclk or posedge reset) begin
+               if (cs_nack && bus_we && bus_phase) begin
+                       $write("%c", bus_wdata[15:8]);
+               end
+       end
+`endif
+
+
+       /* Breakpoint check */
+       assign addr_bphit = (cpu_addr == addr_bkpt) &
+                           spi_ctrl[`CPUINTF_CTRL_BKEN_BIT];
+                           
+       /* Generate _DTACK for CPU cycles only. Note that the doc is
+        * unlcear as to whether we must keep it asserted until
+        * _AS goes back up or whether we can take it off as soon as
+        * state S5 or or later is reached.
+        * Better safe than sorry, we keep it down all the way for now
+        * at least until I have more than my crude sim to test with.
+        */
+       assign _cpu_dtack = _cpu_as | ~state[7] | /* CPU cycle and AS asserted */
+                           (~state[1] & ~(state[0] & bus_ack));
+       /* Bus state machine */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       state <= bi_state_stop;
+               end else
+                 case(state)
+                         bi_state_idle: begin
+                                 /* SPI takeover */
+                                 if (spi_ctrl[`CPUINTF_CTRL_STOP_BIT])
+                                         state <=  bi_state_stop;
+
+                                 /* If the CPU is trying to start a cycle,
+                                  * move up. CS will already have been generated
+                                  * asynchronously on the bus interface. We also
+                                  * detect breakpoints there
+                                  */
+                                 else if (!_cpu_as) begin
+                                         if (addr_bphit)
+                                           state <= bi_state_stop;
+                                         else
+                                           state <= bi_state_cpu0;
+                                 end                     
+
+                                 /* If the CPU is idle and we are stepping,
+                                  * we stop again
+                                  */
+                                 else if (spi_ctrl[`CPUINTF_CTRL_STEP_BIT])
+                                   state <= bi_state_stop;
+                         end
+                         bi_state_cpu0: begin
+                                 /* We got an ack from the device, move to
+                                  * phase 1
+                                  */
+                                 if (bus_ack)
+                                   state <= bi_state_cpu1;
+
+                                 /* No ack, another try at SPI takeover */
+                                 else if (spi_ctrl[`CPUINTF_CTRL_STOP_BIT])
+                                   state <=  bi_state_stop;
+                         end
+                         bi_state_cpu1: begin
+                                 /* If stepping, stop now */
+                                 if (spi_ctrl[`CPUINTF_CTRL_STEP_BIT])
+                                   state <=  bi_state_stop;
+
+                                 /* Back to idle */
+                                 else
+                                   state <= bi_state_idle;
+                         end
+                         bi_state_spip: begin
+                                 state <= bi_state_spi0;
+                         end
+                         bi_state_spi0: begin
+                                 if (bus_ack)
+                                   state <= bi_state_spi1;
+                         end
+                         bi_state_spi1: begin
+                                 state <= bi_state_stop;
+                         end
+                         bi_state_stop: begin
+                                 /* Restart CPU */
+                                 if (spi_ctrl[`CPUINTF_CTRL_RUN_BIT])
+                                   state <= bi_state_idle;
+
+                                 /* Start a simulated cycle */
+                                 else if (spi_ctrl[`CPUINTF_CTRL_CYCLE_BIT])
+                                   state <= bi_state_spip;
+                         end
+                 endcase
+       end
+
+       /* Some SPI backbus interface combo decode logic */
+       assign spi_wreg = bb_strobe && bb_wr;
+       assign spi_reg_addr0 = bb_addr[3:0] == `CPUINTF_REG_ADDR0;
+       assign spi_reg_addr1 = bb_addr[3:0] == `CPUINTF_REG_ADDR1;
+       assign spi_reg_addr2 = bb_addr[3:0] == `CPUINTF_REG_ADDR2;
+       assign spi_reg_data0 = bb_addr[3:0] == `CPUINTF_REG_DATA0;
+       assign spi_reg_data1 = bb_addr[3:0] == `CPUINTF_REG_DATA1;
+       assign spi_reg_misc  = bb_addr[3:0] == `CPUINTF_REG_MISC;
+       assign spi_reg_ctrl  = bb_addr[3:0] == `CPUINTF_REG_CTRL;
+       assign spi_reg_stat  = bb_addr[3:0] == `CPUINTF_REG_STAT;
+       assign spi_reg_bkpt0 = bb_addr[3:0] == `CPUINTF_REG_BKPT0;
+       assign spi_reg_bkpt1 = bb_addr[3:0] == `CPUINTF_REG_BKPT1;
+       assign spi_reg_bkpt2 = bb_addr[3:0] == `CPUINTF_REG_BKPT2;
+
+       /* Latches used by SPI interface to latch address/data
+        * when generating cycles. When under CPU control, they
+        * latch the last CPU cycle for single step mode
+        */
+
+       /* Address latch */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       addr_latch <= 0;
+               end else begin
+                       /* Latch the CPU address for any CPU cycle in phase 0 */
+                       if (state[7]) begin
+                               if (state[0])
+                                 addr_latch <= cpu_addr;
+                       end else if (spi_wreg) begin
+                               /* Or fill from SPI */
+                               if (spi_reg_addr0)
+                                 addr_latch[23:16] <= bb_wdata;
+                               else if (spi_reg_addr1)
+                                 addr_latch[15:8] <= bb_wdata;
+                               else if (spi_reg_addr2)
+                                 addr_latch[7:1] <= bb_wdata[7:1];
+                       end                     
+               end
+       end
+
+       /* Data latch. The CPU always feed from this, we latch the data
+        * from the device at phase 1 clock edge
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       data_latch <= 0;
+               end else begin
+                       /* Latch the bus data in phase 1 clock. Don't
+                        * bother with ack, we'll latch a fresh value
+                        * later if not set
+                        */
+                       if (state[0]) begin
+                               if (bus_we)
+                                 data_latch <= bus_wdata;
+                               else
+                                 data_latch <= bus_rdata;
+                       /* Else handle write from SPI */
+                       end else if (spi_wreg) begin
+                               if (spi_reg_data0)
+                                 data_latch[15:8] <= bb_wdata;
+                               else if (spi_reg_data1)
+                                 data_latch[7:0] <= bb_wdata;
+                       end                     
+               end
+       end
+
+       /* Misc latches (uds/lds/rw) */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       _uds_latch <= 0;                        
+                       _lds_latch <= 0;
+                       r_w_latch <= 0;                 
+               end else begin
+                       /* Latch the CPU misc for any CPU cycle in phase 0 */
+                       if (state[7]) begin
+                               if (state[0]) begin
+                                       _uds_latch <= _cpu_uds;
+                                       _lds_latch <= _cpu_lds;
+                                       r_w_latch <= cpu_r_w;
+                               end                             
+                       end else if (spi_wreg) begin
+                               /* Or fill from SPI */
+                               if (spi_reg_misc) begin
+                                       _uds_latch <= bb_wdata[2];
+                                       _lds_latch <= bb_wdata[1];
+                                       r_w_latch <= bb_wdata[0];
+                               end
+                       end
+               end
+       end
+
+       /* SPI control register */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       spi_ctrl <= 0;
+               end else begin
+                       /* Stop bit cleared whenever we are stopped */
+                       if (state[3])
+                         spi_ctrl[`CPUINTF_CTRL_STOP_BIT] <= 0;
+                       /* Run bit cleared if we are in any CPU cycle */
+                       if (state[7])
+                         spi_ctrl[`CPUINTF_CTRL_RUN_BIT] <= 0;
+                       /* Cycle bit cleared when we are in an SPI cycle */
+                       if (state[6])                    
+                         spi_ctrl[`CPUINTF_CTRL_CYCLE_BIT] <= 0;
+
+                       /* Now write logic from SPI. All bits are OR except
+                        * for step and bken that is sticky
+                        */
+                       if (spi_wreg && spi_reg_ctrl) begin
+                               spi_ctrl[2:0] <= spi_ctrl[2:0] | bb_wdata[2:0];
+                               spi_ctrl[7:6] <= bb_wdata[7:6];
+                       end
+               end             
+       end     
+
+       /* Breakpoint register */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       addr_bkpt <= 0;
+               end else begin
+                       if (spi_wreg) begin
+                               /* Or fill from SPI */
+                               if (spi_reg_bkpt0)
+                                 addr_bkpt[23:16] <= bb_wdata;
+                               else if (spi_reg_bkpt1)
+                                 addr_bkpt[15:8] <= bb_wdata;
+                               else if (spi_reg_bkpt2)
+                                 addr_bkpt[7:1] <= bb_wdata[7:1];
+                       end                     
+               end
+       end
+       
+       /* SPI backbus register read logic */
+       assign bb_rdata = spi_reg_addr0 ? addr_latch[23:16] :
+                         spi_reg_addr1 ? addr_latch[15:8] :
+                         spi_reg_addr2 ? { addr_latch[7:1], 1'b0 } :
+                         spi_reg_data0 ? data_latch[15:8] :
+                         spi_reg_data1 ? data_latch[7:0] :
+                         spi_reg_misc  ? { 5'b0, _uds_latch, _lds_latch,
+                                           r_w_latch } :
+                         spi_reg_ctrl  ? spi_ctrl :
+                         spi_reg_stat  ? state :
+                         0;
+endmodule
diff --git a/fpga/ctrl.v b/fpga/ctrl.v
new file mode 100644 (file)
index 0000000..2c1110e
--- /dev/null
@@ -0,0 +1,68 @@
+/* RW: ID registers */
+`define CTRL_REG_FPGA_ID       0
+`define CTRL_REG_FPGA_VERSION  1
+
+/* WO: Global control register */
+`define CTRL_REG_GCTRL         2
+`define     GCTRL_CPU_UNRESET_BIT      0
+
+/* RO: PS2 Mouse Debug */
+`define CTRL_REG_PS2M_DBG0     3
+`define CTRL_REG_PS2M_DBG1     4
+
+/* RO: PS2 Kbd Debug */
+`define CTRL_REG_PS2K_DBG0     5
+`define CTRL_REG_PS2K_DBG1     6
+
+module ctrl
+(
+       input                           sysclk,
+       input                           reset,
+
+       /* Backbus interface */
+       input [5:0]                     bb_addr,
+       input [7:0]                     bb_wdata,
+       output [7:0]                    bb_rdata,
+       input                           bb_strobe,
+       input                           bb_wr,
+
+       /* Debug stuff */
+       input [15:0]                    ps2m_dbg,
+       input [15:0]                    ps2k_dbg,
+
+       /* Generated control signals */
+       output                          cpu_reset
+);
+       /* Latched registers */
+       reg [7:0]                       gctrl;
+
+       assign cpu_reset = ~gctrl[`GCTRL_CPU_UNRESET_BIT];
+
+       assign bb_rdata = (bb_addr[2:0] == `CTRL_REG_FPGA_ID) ? 8'h42 :
+                         (bb_addr[2:0] == `CTRL_REG_FPGA_VERSION) ? 8'h01 :
+                         (bb_addr[2:0] == `CTRL_REG_GCTRL) ? gctrl :
+                         (bb_addr[2:0] == `CTRL_REG_PS2M_DBG0) ? ps2m_dbg[15:8] :
+                         (bb_addr[2:0] == `CTRL_REG_PS2M_DBG1) ? ps2m_dbg[7:0] :
+                         (bb_addr[2:0] == `CTRL_REG_PS2K_DBG0) ? ps2k_dbg[15:8] :
+                         (bb_addr[2:0] == `CTRL_REG_PS2K_DBG1) ? ps2k_dbg[7:0] :
+                         0;
+
+       /* Register interface */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       gctrl <= 0;
+               end else begin
+                       if (bb_strobe && bb_wr) begin
+                               case(bb_addr[2:0])
+                                       `CTRL_REG_GCTRL:
+                                         gctrl <= bb_wdata;
+                               endcase
+                       end
+               end
+       end
+endmodule
+
+                                         
+                       
+                               
+       
diff --git a/fpga/fpga_clocks.v b/fpga/fpga_clocks.v
new file mode 100644 (file)
index 0000000..74e62f7
--- /dev/null
@@ -0,0 +1,71 @@
+module clockgen
+(
+       input   mclk,           /* 4.433619 Mhz input */
+       output  sysclk16,       /* System clock outputs */
+       output  sysclk33,       /* System clock outputs NOT IMPLEMENTED, running at 16.6 */
+       output  pixclk      /* Pixel clock */
+);
+       wire    pll_mclk;
+       wire    pll_c25;
+       wire    pll_c16;      
+
+       IBUFG mclk_buf( .I(mclk), .O(pll_mclk));
+
+       /* Generate 16.6 for the DLL cpu*/
+       DCM #
+       (
+               .CLKDV_DIVIDE(2.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,
+                                   // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0
+               .CLKFX_DIVIDE(4),   // Can be any integer from 1 to 32
+               .CLKFX_MULTIPLY(15), // Can be any integer from 2 to 32
+               .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature
+               .CLKIN_PERIOD(225.0),  // Specify period of input clock
+               .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE
+               .CLK_FEEDBACK("NONE"),  // Specify clock feedback of NONE, 1X or 2X
+               .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or an integer from 0 to 15
+               .DFS_FREQUENCY_MODE("LOW"),  // HIGH or LOW frequency mode for frequency synthesis
+               .DLL_FREQUENCY_MODE("LOW"),  // HIGH or LOW frequency mode for DLL
+               .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE
+               .FACTORY_JF(16'hC080),   // FACTORY JF values
+               .PHASE_SHIFT(0),     // Amount of fixed phase shift from -255 to 255
+               .STARTUP_WAIT("FALSE")   // Delay configuration DONE until DCM LOCK, TRUE/FALSE
+       )
+       pllmain
+       (
+               .CLKFX(pll_c16),   // DCM CLK synthesis out (M/D)
+               .CLKIN(pll_mclk)   // Clock input (from IBUFG, BUFG or DCM)
+       );
+
+       /* Generate a rough 25.1Mhz for the video */
+       DCM #
+       (
+               .CLKDV_DIVIDE(3.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,
+                                   // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0
+               .CLKFX_DIVIDE(3),   // Can be any integer from 1 to 32
+//             .CLKFX_MULTIPLY(16), // Can be any integer from 2 to 32
+               .CLKFX_MULTIPLY(17), // Can be any integer from 2 to 32
+               .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature
+               .CLKIN_PERIOD(225.0),  // Specify period of input clock
+               .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE
+               .CLK_FEEDBACK("NONE"),  // Specify clock feedback of NONE, 1X or 2X
+               .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or an integer from 0 to 15
+               .DFS_FREQUENCY_MODE("LOW"),  // HIGH or LOW frequency mode for frequency synthesis
+               .DLL_FREQUENCY_MODE("LOW"),  // HIGH or LOW frequency mode for DLL
+               .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE
+               .FACTORY_JF(16'hC080),   // FACTORY JF values
+               .PHASE_SHIFT(0),     // Amount of fixed phase shift from -255 to 255
+               .STARTUP_WAIT("FALSE")   // Delay configuration DONE until DCM LOCK, TRUE/FALSE
+       )
+       pllpix
+       (
+               .CLKFX(pll_c25),   // DCM CLK synthesis out (M/D)
+               .CLKIN(pll_mclk)   // Clock input (from IBUFG, BUFG or DCM)
+       );
+       
+       /* Global clock buffers */
+       BUFG clk33_buf( .I(pll_c16),    .O(sysclk33)); /* NOT IMPL. RUNNING AT 16Mhz */
+       BUFG clk16_buf( .I(pll_c16),    .O(sysclk16));
+       
+       /* Temporarily assign pixclock to 33Mhz, will fix later */
+       BUFG pixclk_buf( .I(pll_c25),   .O(pixclk));
+endmodule // clocks
diff --git a/fpga/fpga_clocks_dll.v b/fpga/fpga_clocks_dll.v
new file mode 100644 (file)
index 0000000..29194f5
--- /dev/null
@@ -0,0 +1,102 @@
+module clockgen
+(
+       input   mclk,           /* 4.433619 Mhz input */
+       output  sysclk16,       /* System clock outputs */
+       output  sysclk33,       /* System clock outputs */
+       output  pixclk      /* Pixel clock */
+);
+       wire    pll_mclk;
+       wire    pll_c33;
+       wire    pll_c25;
+       wire    dll_c33;        
+       wire    dll_c16;      
+
+       IBUFG mclk_buf( .I(mclk), .O(pll_mclk));
+
+       /* Generate 33.25 for the DLL cpu*/
+       DCM #
+       (
+               .CLKDV_DIVIDE(2.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,
+                                   // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0
+               .CLKFX_DIVIDE(2),   // Can be any integer from 1 to 32
+               .CLKFX_MULTIPLY(15), // Can be any integer from 2 to 32
+               .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature
+               .CLKIN_PERIOD(225.0),  // Specify period of input clock
+               .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE
+               .CLK_FEEDBACK("NONE"),  // Specify clock feedback of NONE, 1X or 2X
+               .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or an integer from 0 to 15
+               .DFS_FREQUENCY_MODE("LOW"),  // HIGH or LOW frequency mode for frequency synthesis
+               .DLL_FREQUENCY_MODE("LOW"),  // HIGH or LOW frequency mode for DLL
+               .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE
+               .FACTORY_JF(16'hC080),   // FACTORY JF values
+               .PHASE_SHIFT(0),     // Amount of fixed phase shift from -255 to 255
+               .STARTUP_WAIT("FALSE")   // Delay configuration DONE until DCM LOCK, TRUE/FALSE
+       )
+       pllmain
+       (
+               .CLKFX(pll_c33),   // DCM CLK synthesis out (M/D)
+               .CLKIN(pll_mclk)   // Clock input (from IBUFG, BUFG or DCM)
+       );
+
+       /* Generate a rough 25.1Mhz for the video */
+       DCM #
+       (
+               .CLKDV_DIVIDE(3.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,
+                                   // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0
+               .CLKFX_DIVIDE(3),   // Can be any integer from 1 to 32
+               .CLKFX_MULTIPLY(17), // Can be any integer from 2 to 32
+               .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature
+               .CLKIN_PERIOD(225.0),  // Specify period of input clock
+               .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE
+               .CLK_FEEDBACK("NONE"),  // Specify clock feedback of NONE, 1X or 2X
+               .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or an integer from 0 to 15
+               .DFS_FREQUENCY_MODE("LOW"),  // HIGH or LOW frequency mode for frequency synthesis
+               .DLL_FREQUENCY_MODE("LOW"),  // HIGH or LOW frequency mode for DLL
+               .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE
+               .FACTORY_JF(16'hC080),   // FACTORY JF values
+               .PHASE_SHIFT(0),     // Amount of fixed phase shift from -255 to 255
+               .STARTUP_WAIT("FALSE")   // Delay configuration DONE until DCM LOCK, TRUE/FALSE
+       )
+       pllpix
+       (
+               .CLKFX(pll_c25),   // DCM CLK synthesis out (M/D)
+               .CLKIN(pll_mclk)   // Clock input (from IBUFG, BUFG or DCM)
+       );
+       
+       /* DLL that 33.25Mhz input and generate the 16.6 from it. Note: The Xilinx tools don't
+        * seem to like when you do that ;-) Looks like we should really have put a faster
+        * crystal on the board
+        */
+       DCM #
+       (
+               .CLKDV_DIVIDE(2.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,
+                                   // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0
+               .CLKFX_DIVIDE(2),   // Can be any integer from 1 to 32
+               .CLKFX_MULTIPLY(2), // Can be any integer from 2 to 32
+               .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature
+               .CLKIN_PERIOD(30.0),  // Specify period of input clock
+               .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE
+               .CLK_FEEDBACK("1X"),  // Specify clock feedback of NONE, 1X or 2X
+               .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or an integer from 0 to 15
+               .DFS_FREQUENCY_MODE("LOW"),  // HIGH or LOW frequency mode for frequency synthesis
+               .DLL_FREQUENCY_MODE("LOW"),  // HIGH or LOW frequency mode for DLL
+               .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE
+               .FACTORY_JF(16'hC080),   // FACTORY JF values
+               .PHASE_SHIFT(0),     // Amount of fixed phase shift from -255 to 255
+               .STARTUP_WAIT("FALSE")   // Delay configuration DONE until DCM LOCK, TRUE/FALSE
+       )
+       dll
+       (
+               .CLKIN(pll_c33),        // Clock input (from IBUFG, BUFG or DCM)
+               .CLK0(dll_c33),         // 0 degree DCM CLK output
+               .CLKDV(dll_c16),        // Divided DCM CLK out (CLKDV_DIVIDE)
+               .CLKFB(sysclk33)        // DCM clock feedback
+       );
+
+       /* Global clock buffers */
+       BUFG clk33_buf( .I(dll_c33),    .O(sysclk33));
+       BUFG clk16_buf( .I(dll_c16),    .O(sysclk16));
+       
+       /* Temporarily assign pixclock to 33Mhz, will fix later */
+       BUFG pixclk_buf( .I(pll_c25),   .O(pixclk));
+endmodule // clocks
diff --git a/fpga/iwm.v b/fpga/iwm.v
new file mode 100644 (file)
index 0000000..52ee8aa
--- /dev/null
@@ -0,0 +1,221 @@
+`timescale 1ns / 100ps
+
+/*
+ * IWM module for minimigmac.
+ * 
+ *              Location to     Location to
+ *  IWM line    turn line on    turn line off
+ *
+ *  Disk state-control lines:
+ *    CA0       dBase+ph0H      dBase+ph0L
+ *    CA1       dBase+ph1H      dBase+ph1L
+ *    CA2       dBase+ph2H      dBase+ph2L
+ *    LSTRB     dBase+ph3H      dBase+ph3L
+ *
+ *  Disk enable line:
+ *    ENABLE    dBase+motorOn   dBase+motorOff
+ *
+ *  IWM internal states:
+ *    SELECT    dBase+extDrive  dBase+intDrive
+ *    Q6        dBase+q6H       dBase+q6L
+ *    Q7        dBase+q7H       dBase+q7L
+ *
+ *  ph0L      .EQU    512*0      ;CA0 off (0)
+ *  ph0H      .EQU    512*1      ;CA0 on (1)
+ *  ph1L      .EQU    512*2      ;CA1 off (0)
+ *  ph1H      .EQU    512*3      ;CA1 on (1)
+ *  ph2L      .EQU    512*4      ;CA2 off (0)
+ *  ph2H      .EQU    512*5      ;CA2 on (1)
+ *  ph3L      .EQU    512*6      ;LSTRB off (low)
+ *  ph3H      .EQU    512*7      ;LSTRB on (high)
+ *  mtrOff    .EQU    512*8      ;disk enable off
+ *  mtrOn     .EQU    512*9      ;disk enable on
+ *  intDrive  .EQU    512*10     ;select internal drive
+ *  extDrive  .EQU    512*11     ;select external drive
+ *  q6L       .EQU    512*12     ;Q6 off
+ *  q6H       .EQU    512*13     ;Q6 on
+ *  q7L       .EQU    512*14     ;Q7 off
+ *  q7H       .EQU    512*15     ;Q7 on
+ */
+
+`define IWM_REG_ph0L      0
+`define IWM_REG_ph0H      1
+`define IWM_REG_ph1L      2
+`define IWM_REG_ph1H      3
+`define IWM_REG_ph2L      4
+`define IWM_REG_ph2H      5
+`define IWM_REG_ph3L      6
+`define IWM_REG_ph3H      7
+`define IWM_REG_mtrOff    8
+`define IWM_REG_mtrOn     9
+`define IWM_REG_intDrive  10
+`define IWM_REG_extDrive  11
+`define IWM_REG_q6L       12
+`define IWM_REG_q6H       13
+`define IWM_REG_q7L       14
+`define IWM_REG_q7H       15
+
+
+module iwm(input       sysclk,
+          input        reset,
+
+          /* Bus interface. 4-bit address, to be wired
+           * appropriately upstream (to A9..A12)
+           */
+          input                cs,
+          input                we,
+          output               ack,
+          input                phase,
+          input [3:0]          rs,
+          input [7:0]          wdata,
+          output [7:0]         rdata,
+
+          /* Backbus interface */
+          input [5:0]          bb_addr,
+          input [7:0]          bb_wdata,
+          output reg [7:0]     bb_rdata,
+          input                bb_strobe,
+          input                bb_wr,
+
+          /* SEL line from VIA */
+          input                via_sel
+          );
+
+       /* IWM state register broken up by name */
+       reg     CA0;
+       reg     CA1;
+       reg     CA2;
+       reg     LSTRB;
+       reg     ENABLE;
+       reg     SELECT;
+       reg     Q6;
+       reg     Q7;
+
+       /* Internal wires */
+       wire    new_Q6;
+       wire    new_Q7;
+       wire    new_EN;
+       wire    reg_wr;
+
+       /* Internal data & mode registers */
+       reg [7:0] data_reg;
+       reg [4:0] mode_reg;
+
+       /* Status and write handshake are just wires */
+       wire [7:0] stat_reg;
+       wire [7:0] whsk_reg;
+
+       /* Sense line from disk */
+       wire       sense;
+
+       /* enbl1 and enbl2 lines (not inverted here unlike real HW) */
+       reg        enbl1;
+       reg        enbl2;
+
+       /* Disk register definitions */
+       wire [3:0] diskreg_sel; 
+`define DISK_REG_DIRTN         0  /* R/W: step direction */
+`define DISK_REG_CSTIN         1  /* R: disk in place (1 = no disk) */
+                                  /* W: ?? reset disk switch flag ? */
+`define DISK_REG_STEP          2  /* R: disk stepping (1 = complete) */
+                                  /* W: 0 = step disk */
+`define DISK_REG_WRTPRT                3  /* R: 0 = disk is write-protected */
+`define DISK_REG_MOTORON       4  /* R/W: 0 = motor on */
+`define DISK_REG_TK0           5  /* R: 0 = head at track 0 */
+`define DISK_REG_EJECT         6  /* R: disk switched */
+                                  /* W: 1 = eject disk */
+`define DISK_REG_TACH          7  /* R: tach-o-meter */
+`define DISK_REG_RDDATA0       8  /* R: lower head activate */
+`define DISK_REG_RDDATA9       9  /* R: upper head activate */
+`define DISK_REG_SIDES         12 /* R: number of sides (0=single, 1=dbl) */
+`define DISK_REG_READY         13 /* R: disk ready (head loaded) (0=ready) */
+`define DISK_REG_INSTALLED     14 /* R: drive present (0 = yes) */
+`define DISK_REG_DRVIN         15 /* R: drive present (0=yes, 1=no) XXX HD ?*/
+       
+       /* Toggle control lines */
+       always@(posedge reset or posedge sysclk) begin
+               if (reset) begin
+                       CA0 <= 0;
+                       CA1 <= 0;
+                       CA2 <= 0;
+                       LSTRB <= 0;
+                       ENABLE <= 0;
+                       SELECT <= 0;
+                       Q6 <= 0;
+                       Q7 <= 0;
+               end else if (cs && phase) begin
+                       case(rs[3:1])
+                               0: CA0 <= rs[0];
+                               1: CA1 <= rs[0];
+                               2: CA2 <= rs[0];
+                               3: LSTRB <= rs[0];
+                               4: ENABLE <= rs[0];
+                               5: SELECT <= rs[0];
+                               6: Q6 <= rs[0];
+                               7: Q7 <= rs[0];
+                       endcase
+               end             
+       end
+
+       /* ENBL1/2 lines. XXX We don't do the 1 second timer yet
+        * but I don't think the Mac ROM uses it anyways
+        */
+       always@(posedge reset or posedge sysclk) begin
+               if (reset) begin
+                       enbl1 <= 0;
+                       enbl2 <= 0;
+               end else begin
+                       if (cs && phase && rs[3:1] == 4) begin
+                               if (SELECT)
+                                 enbl2 <= rs[0];
+                               else
+                                 enbl1 <= rs[0];
+                       end
+               end
+       end
+               
+       /* Now for register read/writes, we need to use
+        * the "new" value of Q6, Q7 and ENABLE in the same
+        * cycle. We hack that up here.
+        */
+       assign new_Q6 = (cs && rs[3:1] == 6) ? rs[0] : Q6;
+       assign new_Q7 = (cs && rs[3:1] == 7) ? rs[0] : Q7;
+       assign new_EN = (cs && rs[3:1] == 4) ? rs[0] : ENABLE;
+       assign reg_wr = cs & phase & new_Q6 & new_Q7 & rs[0];
+
+       /* Internal register reads */
+       assign rdata = (!new_Q7 && !new_Q6 && new_EN) ? data_reg :
+                      (!new_Q7 &&  new_Q6)           ? stat_reg :
+                      ( new_Q7 && !new_Q6)           ? whsk_reg : 8'hff;
+
+       /* Internal write to mode register */
+       always@(posedge reset or posedge sysclk) begin
+               if (reset)
+                 mode_reg <= 0;
+               else if (reg_wr && !ENABLE)
+                 mode_reg <= wdata[4:0];
+       end
+       
+       /* Make up status and write handshake register wires */
+       assign stat_reg = { sense, 1'b0, enbl1 | enbl2, mode_reg[4:0] };
+       assign whsk_reg = 8'b11000000; /* XXX fixme */  
+
+       /* Disk register reads */
+//     assign diskreg_idx = { CA2, CA1, CA0, via_sel };        
+//     assign sense = diskregs = SELECT ? diskreg_b[diskreg_idx] :
+//                    diskreg_a[diskreg_idx];
+       assign sense = 1'b1;    
+
+       /* Test values for disk regs */
+       always@(posedge reset or posedge sysclk) begin
+               if (reset) begin
+//                     diskregs[0] <= 16'b1001_1100_0001_0110;
+//                     diskregs[1] <= 16'b1111_1111_1111_1111;
+               end
+       end
+       
+       /* For now always ack immediately, we'll change that
+        * eventually when we have something with real data
+        */
+       assign ack = cs;
+endmodule
diff --git a/fpga/mem_intf.v b/fpga/mem_intf.v
new file mode 100644 (file)
index 0000000..5f0d106
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ * Memory interface for Minimigmac
+ *
+ * Wraps the SRAM chips on the Minimig board and provides
+ * support for RAM and ROM access from the processor,
+ * backbus access via SPI, and "DMA" sound and video buffers
+ *
+ * Notes about video: We use a simple req edge based asynchronous
+ * protocol since we are running the main logic separately from
+ * the video timing generator. On each handshake, the memory
+ * interface will fetch the next 16 pixels. Video addresses are
+ * calculated internally based on the buffer select VIA bit for
+ * a fixed resolution of 512x342.
+ *
+ * Starting addresses of the video buffers depend on the model
+ * and amount of RAM. We emulate a 2Mb configuration but for
+ * completeness, here are the address for all supported
+ * configurations:
+ *
+ *  System                   Main Screen    Alternate
+ *
+ *  Macintosh Plus, 1Mb      $FA700         $F2700
+ *  Macintosh Plus, 2Mb      $1FA700        $1F2700
+ *  Macintosh Plus, 2.5Mb    $27A700        $272700
+ *  Macintosh Plus, 4Mb      $3FA700        $3F2700
+ *
+ * The above addresses look confusing but basically it's really
+ * just hard wired chip select I beleive on the real mac tho for
+ * now we just use a full blown latch to hold the address since
+ * we have silicon to spare :-)
+ *
+ * The total size of the video buffer is 0x5580 bytes
+ * 
+ * For audio, we have:
+ *
+ *  System                   Main Sound    Alternate
+ *  Macintosh Plus, 1Mb      $FFD00        $FA100
+ *  Macintosh Plus, 2Mb      $1FFD00       $1FA100
+ *  Macintosh Plus, 2.5Mb    $27FD00       $27A100
+ *  Macintosh Plus, 4Mb      $3FFD00       $3FA100
+ * 
+ * Implementation note:
+ * 
+ * This is sub-optimal. We have a 3 cycle round trip, which could
+ * probably go down to one or two (we have a mandatory idle cycle
+ * between 2-cycle accesses). Works for now and is easy tho.
+ * 
+ * The memory doesn't seem to require any hold times, on synthetized
+ * cycles (video, SPI, ...) we establish everything at once and leave
+ * the signals "on" for 2 cycles. On CPU initiated cycles, some
+ * signals are established later, see comments in the CPU interface
+ * module, but overall it fits the timings. The idle cycle should give
+ * us the necessary resting time to avoid cases of both the memory and
+ * the FPGA trying to drive the data bus.
+ */
+
+module mem_intf(input                  sysclk,
+               input                   reset,
+
+               /* SRAM pins */
+               inout [15:0]            ram_data,
+               output [19:1]           ram_address,
+               output [3:0]            _ram_ce,
+               output                  _ram_bhe,
+               output                  _ram_ble,
+               output                  _ram_we,
+               output                  _ram_oe,
+
+               /* Bus interface */
+               input                   bus_cs_ram,
+               input                   bus_cs_rom,
+               input                   bus_we,
+               output                  bus_ack,
+               input                   bus_ube,
+               input                   bus_lbe,
+               input                   bus_phase,
+               input [15:0]            bus_wdata,
+               output [15:0]           bus_rdata,
+               input [22:1]            bus_addr,
+
+               /* Backbus interface */
+               input [5:0]             bb_addr,
+               input [7:0]             bb_wdata,
+               output [7:0]            bb_rdata,
+               input                   bb_strobe,
+               input                   bb_wr,
+
+               /* Asynchronous video interface */
+               input                   vid_bufsel,
+               input                   vid_req,
+               output reg              vid_ack,
+               output reg [15:0]       vid_pixels
+
+               /* TODO: Audio */
+               );
+       
+       /* Owner of interface */
+       wire            own_bus;
+       wire            own_vid;
+       wire            own_spi;
+
+       /* Cooked bus address (ram/rom selection) */
+       wire [21:1]     cooked_address;
+       /* Latch of cs_rom vs. cs_ram */
+       reg             rom_select;
+
+       /* SPI byte write latch */
+       reg [21:0]      spi_addr;
+       reg [7:0]       spi_data;
+       reg             spi_pending;    
+       reg             spi_we;
+
+       /* SPI decode signals */
+       wire            spi_wreg;       
+       wire            spi_reg_addr0;
+       wire            spi_reg_addr1;
+       wire            spi_reg_addr2;
+       wire            spi_reg_data;
+
+       /* Muxed/cooked RAM control signals */
+       wire [15:0]     ram_data_out;
+       wire [21:1]     ram_addr;
+
+       /* Main memory enable */
+       wire            ram_en; 
+
+       /* Video address generator. Buffer select bit is
+        * inserted in there at bit 15. We cound down tho
+        * eventually I may replace that with a reset input
+        * from the video circuitry
+        */
+       parameter vid_buf_base = 'h1F2700 / 2;
+       parameter vid_buf_size = 'h2ac0;
+       reg [21:1]      vid_addr;
+       reg [13:0]      vid_words;
+       wire            vid_latch_pix;  
+
+       /* vid_req/vid_ack logic */
+       reg [2:0]       vid_req_sync;
+       reg             vid_req_pending;
+       wire            vid_ack_reset;
+
+       /* State machine */
+       localparam state_idle   = 8'h10;  /* idle, ram off, owner is bus */
+       localparam state_bus0   = 8'h11;  /* bus access phase 0 */
+       localparam state_bus1   = 8'h13;  /* bus access phase 1 */
+       localparam state_spi0   = 8'h21;  /* spi access phase 0 */
+       localparam state_spi1   = 8'h23;  /* spi access phase 1 */
+       localparam state_vid0   = 8'h41;
+       localparam state_vid1   = 8'h47;
+
+       /* State bits breakout:
+        *
+        *  7:
+        *  6: own bit video
+        *  5: own bit spi
+        *  4: own bit bus
+        *  3:
+        *  2: inc address (vid/audio)
+        *  1: bus phase
+        *  0: ram active
+        */
+       reg [7:0]       state;
+       
+       /* Own lines off the state machine */
+       assign own_bus = state[4];
+       assign own_spi = state[5];
+       assign own_vid = state[6];
+
+       /* First setup ram_data_out, _ram_we and ram_addr based
+        * on owner. ram_data_out is then applied to ram_data or
+        * not based on _ram_we.
+        * 
+        * XX. Remove the "default" case, make it SPI
+        */
+       assign ram_data_out     = own_bus ? bus_wdata :
+                                 own_spi ? { spi_data, spi_data } :
+                                 16'b1;        
+       
+       assign ram_en           = state[0];     
+       assign _ram_we          = (own_bus ? (bus_cs_rom | ~bus_we) :
+                                  own_spi ? ~spi_we : 1'b1) | ~ram_en;
+       assign _ram_bhe         = own_bus ? ~bus_ube :
+                                 own_spi ? spi_addr[0] :
+                                 own_vid ? 1'b0 :
+                                 1'b1;
+       assign _ram_ble         = own_bus ? ~bus_lbe :
+                                 own_spi ? ~spi_addr[0] :
+                                 own_vid ? 1'b0 :
+                                 1'b1;
+
+       /* Cook RAM address based on RAM/ROM selection
+        * We have 2M of RAM and 128k of ROM repeated
+        */
+       assign cooked_address = rom_select ?
+                       { 5'b10000, bus_addr[16:1] } :
+                       { 1'b0, bus_addr[20:1] };
+       
+
+       /* Mux appropriate source of RAM address */
+       assign ram_addr     = own_bus ? cooked_address :
+                             own_spi ? spi_addr[21:1] :
+                             own_vid ? vid_addr : 0;
+
+       /* Do ram_data input based on _ram_we */
+       assign ram_data = _ram_we ? 16'bz : ram_data_out;
+
+       /* And set ram output enable accordingly */
+       assign _ram_oe = ~_ram_we | ~ram_en;
+
+       /* Split ram_addr into banks */
+       assign ram_address = ram_addr[19:1];
+       assign _ram_ce[0] = ~(ram_addr[21:20] == 2'b00) | ~state[0];
+       assign _ram_ce[1] = ~(ram_addr[21:20] == 2'b01) | ~state[0];
+       assign _ram_ce[2] = ~(ram_addr[21:20] == 2'b10) | ~state[0];
+       assign _ram_ce[3] = ~(ram_addr[21:20] == 2'b11) | ~state[0];
+
+       /* RAM to bus data */
+       assign bus_rdata = own_bus ? ram_data : 16'b1;
+
+       /* Ack for bus request in phase 0. WARNING: Must reproduce
+        * the state machine logic in state_idle
+        */
+       assign bus_ack = state == state_bus0;
+
+       /* Latch rom_select on cs transitions */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 rom_select <= 0;
+               else begin
+                       if (bus_cs_ram)
+                         rom_select <= 0;
+                       else if (bus_cs_rom)
+                         rom_select <= 1;
+               end
+       end
+
+       /* State machine */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 state <= state_idle;
+               else
+                 case(state)
+                         state_idle: begin
+                                 /* Video request */
+                                 if (vid_req_pending)
+                                   state <= state_vid0;
+                                 /* SPI request */
+                                 else if (spi_pending)
+                                   state <= state_spi0;
+                                 /* CPU request */
+                                 else if (bus_cs_ram || bus_cs_rom)
+                                   state <= state_bus0;
+                         end
+                         state_bus0: begin
+                                 /* There should be no possible abort here
+                                  * since we have ack out
+                                  */
+                                 state <= state_bus1;
+                         end                     
+                         state_bus1: state <= state_idle;
+                         state_spi0: state <= state_spi1;
+                         state_spi1: state <= state_idle;
+                         state_vid0: state <= state_vid1;
+                         state_vid1: state <= state_idle;
+                 endcase
+       end     
+
+       /* vid_req input synchronizer and edge detect */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       vid_req_sync <= 3'b000;
+                       vid_req_pending <= 0;                   
+               end else begin
+                       vid_req_sync[2] <= vid_req_sync[1];
+                       vid_req_sync[1] <= vid_req_sync[0];
+                       vid_req_sync[0] <= vid_req;
+                       if (vid_req_sync[2] == 0 && vid_req_sync[1])
+                         vid_req_pending <= 1;
+                       else if (own_vid)
+                         vid_req_pending <= 0;                 
+               end
+       end
+       
+       assign vid_ack_reset = reset | ~vid_req_sync[1];
+       assign vid_latch_pix = state == state_vid0;
+       /* vid_ack generation, synchronous set, asynchronous reset */
+       always@(posedge sysclk or posedge vid_ack_reset) begin
+               if (vid_ack_reset)
+                 vid_ack <= 1'b0;
+               else if (vid_latch_pix)
+                 vid_ack <= 1'b1;
+       end
+
+       /* vid address calculation */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       vid_addr <= vid_buf_base;
+                       vid_addr[15] <= vid_bufsel;                     
+                       vid_words <= vid_buf_size-1;
+               end else begin
+                       if (state[2]) begin
+                               if (vid_words == 0) begin
+                                       vid_addr <= vid_buf_base;
+                                       vid_addr[15] <= vid_bufsel;
+                                       vid_words <= vid_buf_size-1;
+                               end else begin
+                                       vid_addr <= vid_addr + 1;
+                                       vid_words <= vid_words - 1;
+                               end                             
+                       end
+               end
+       end
+
+       /* latch pixels */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       vid_pixels <= 0;
+               end else begin
+                       if (vid_latch_pix) begin
+                               vid_pixels <= ram_data;
+                       end
+               end
+       end     
+       
+       /* SPI register interface */
+`define MEMINTF_REG_ADDR0      0
+`define MEMINTF_REG_ADDR1      1
+`define MEMINTF_REG_ADDR2      2
+`define        MEMINTF_REG_DATA        3
+       assign spi_wreg = bb_strobe && bb_wr;
+       assign spi_reg_addr0 = bb_addr[2:0] == `MEMINTF_REG_ADDR0;
+       assign spi_reg_addr1 = bb_addr[2:0] == `MEMINTF_REG_ADDR1;
+       assign spi_reg_addr2 = bb_addr[2:0] == `MEMINTF_REG_ADDR2;
+       assign spi_reg_data  = bb_addr[2:0] == `MEMINTF_REG_DATA;
+
+       /* SPI control */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       spi_pending <= 0;
+                       spi_we <= 0;                    
+               end else begin
+                       if (bb_strobe && spi_reg_data) begin
+                               spi_pending <= 1;
+                               spi_we <= bb_wr;
+                       end else if (own_spi) begin
+                               spi_pending <= 0;
+                       end
+               end
+       end
+
+       /* SPI data latch */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       spi_data <= 0;
+               end else begin
+                       if (spi_wreg && spi_reg_data) begin
+                               spi_data <= bb_wdata;
+                       end else if (own_spi && !spi_we && state[1]) begin
+                               spi_data <= spi_addr[0] ? ram_data[7:0] :
+                                           ram_data[15:8];
+                       end
+               end             
+       end     
+       
+       /* SPI address latch write */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       spi_addr <= 0;
+               end else begin
+                       if (own_spi && state[1]) begin
+                               spi_addr <= spi_addr + 1;
+                       end else if (spi_wreg) begin
+                               if (spi_reg_addr0)
+                                 spi_addr[21:16] <= bb_wdata[5:0];
+                               else if (spi_reg_addr1)
+                                 spi_addr[15:8] <= bb_wdata;
+                               else if (spi_reg_addr2)
+                                 spi_addr[7:0] <= bb_wdata;
+                       end
+               end
+       end
+
+       /* SPI backbus register read logic */
+       assign bb_rdata = spi_reg_addr0 ? { 2'b0, spi_addr[21:16] } :
+                         spi_reg_addr1 ? spi_addr[15:8] :
+                         spi_reg_addr2 ? spi_addr[7:0] :               
+                         spi_reg_data ? spi_data :
+                         0;
+endmodule
\ No newline at end of file
diff --git a/fpga/minimigmac.ucf b/fpga/minimigmac.ucf
new file mode 100644 (file)
index 0000000..dc87492
--- /dev/null
@@ -0,0 +1,199 @@
+INST "ram_data<0>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<1>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<2>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<3>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<4>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<5>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<6>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<7>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<8>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<9>"     TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<10>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<11>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<12>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<13>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<14>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_data<15>"    TNM = "RAMDAT"; #SRAM data\r
+INST "ram_address<1>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<2>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<3>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<4>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<5>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<6>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<7>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<8>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<9>"  TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<10>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<11>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<12>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<13>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<14>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<15>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<16>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<17>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<18>" TNM = "RAMADD"; #SRAM address\r
+INST "ram_address<19>" TNM = "RAMADD"; #SRAM address\r
+INST "_ram_bhe"        TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_ble"        TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_we"         TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_oe"         TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_ce[0]"      TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_ce[1]"      TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_ce[2]"      TNM = "RAMCTL"; #SRAM address and control\r
+INST "_ram_ce[3]"      TNM = "RAMCTL"; #SRAM address and control\r
+INST "_cpu_as"         TNM = "CPU_AS";\r
+INST "_cpu_uds"        TNM = "CPU_DS";\r
+INST "_cpu_lds"        TNM = "CPU_DS";\r
+INST "_cpu_dtack"      TNM = "CPU_DTACK";\r
+#TIMESPEC TS11 = FROM "FFS"    TO "RAMCTL"    33 ns;\r
+#TIMESPEC TS12 = FROM "RAMDAT" TO "FFS"       33 ns;\r
+#TIMESPEC TS13 = FROM "FFS"    TO "RAMDAT"    33 ns;\r
+#TIMESPEC TS14 = FROM "FFS"    TO "RAMADD"    33 ns;\r
+TIMESPEC TS14 = FROM "CPU_DS" TO "RAMCTL" 20 ns;\r
+TIMESPEC TS14 = FROM "CPU_AS" TO "RAMCTL" 20 ns;\r
+TIMESPEC TS15 = FROM "CPU_AS" TO "CPU_DTACK" 20 ns;\r
+#TIMESPEC TS16 = FROM "RAMDAT" TO "CPU_DTACK" 20 ns;\r
+#TIMESPEC TS17 = FROM "CPU_DS" TO "CPU_DTACK" 20 ns;\r
+\r
+#PACE: Start of Constraints generated by PACE\r
+#PACE: Start of PACE I/O Pin Assignments\r
+NET "mclk"             LOC = "P80"  |IOSTANDARD = LVCMOS33 |PERIOD = 225;\r
+NET "_cpu_reset"       LOC = "P95"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |PULLUP; \r
+NET "cpu_clk"          LOC = "P101" |IOSTANDARD = LVCMOS33 |SLEW = FAST |DRIVE = 24; \r
+NET "_cpu_dtack"       LOC = "P102" |IOSTANDARD = LVCMOS33 |SLEW = FAST |DRIVE = 12; \r
+NET "cpu_r_w"          LOC = "P106" |IOSTANDARD = LVCMOS33 ; \r
+NET "_cpu_as"          LOC = "P109" |IOSTANDARD = LVCMOS33 ; \r
+NET "_cpu_uds"         LOC = "P108" |IOSTANDARD = LVCMOS33 ; \r
+NET "_cpu_lds"         LOC = "P107" |IOSTANDARD = LVCMOS33 ; \r
+NET "_cpu_ipl[0]"      LOC = "P96"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_cpu_ipl[1]"      LOC = "P97"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_cpu_ipl[2]"      LOC = "P100" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "cpu_address[10]"  LOC = "P149" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[11]"  LOC = "P148" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[12]"  LOC = "P147" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[13]"  LOC = "P146" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[14]"  LOC = "P144" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[15]"  LOC = "P143" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[16]"  LOC = "P141" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[17]"  LOC = "P140" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[18]"  LOC = "P139" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[19]"  LOC = "P138" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[1]"   LOC = "P166" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[20]"  LOC = "P137" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[21]"  LOC = "P135" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[22]"  LOC = "P133" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[23]"  LOC = "P132" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[2]"   LOC = "P165" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[3]"   LOC = "P162" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[4]"   LOC = "P161" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[5]"   LOC = "P156" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[6]"   LOC = "P155" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[7]"   LOC = "P154" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[8]"   LOC = "P152" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_address[9]"   LOC = "P150" |IOSTANDARD = LVCMOS33 ; \r
+NET "cpu_data[0]"      LOC = "P111" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[10]"     LOC = "P124" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[11]"     LOC = "P125" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[12]"     LOC = "P126" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[13]"     LOC = "P128" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[14]"     LOC = "P130" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[15]"     LOC = "P131" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[1]"      LOC = "P113" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[2]"      LOC = "P114" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[3]"      LOC = "P115" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[4]"      LOC = "P116" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[5]"      LOC = "P117" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[6]"      LOC = "P119" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[7]"      LOC = "P120" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[8]"      LOC = "P122" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "cpu_data[9]"      LOC = "P123" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "_ram_ce[0]"       LOC = "P28"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_ce[1]"       LOC = "P20"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_ce[2]"       LOC = "P15"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_ce[3]"       LOC = "P18"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_oe"          LOC = "P39"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_we"          LOC = "P72"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_bhe"         LOC = "P40"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "_ram_ble"         LOC = "P42"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[10]"  LOC = "P58"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[11]"  LOC = "P61"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[12]"  LOC = "P62"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[13]"  LOC = "P63"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[14]"  LOC = "P64"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[15]"  LOC = "P79"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[16]"  LOC = "P78"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[17]"  LOC = "P77"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[18]"  LOC = "P76"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[19]"  LOC = "P74"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[1]"   LOC = "P27"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[2]"   LOC = "P26"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[3]"   LOC = "P24"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[4]"   LOC = "P22"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[5]"   LOC = "P21"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[6]"   LOC = "P35"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[7]"   LOC = "P36"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[8]"   LOC = "P37"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_address[9]"   LOC = "P57"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "ram_data[0]"      LOC = "P29"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[10]"     LOC = "P50"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[11]"     LOC = "P48"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[12]"     LOC = "P46"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[13]"     LOC = "P45"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[14]"     LOC = "P44"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[15]"     LOC = "P43"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[1]"      LOC = "P31"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[2]"      LOC = "P33"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[3]"      LOC = "P34"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[4]"      LOC = "P65"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[5]"      LOC = "P67"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[6]"      LOC = "P68"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[7]"      LOC = "P71"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[8]"      LOC = "P52"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "ram_data[9]"      LOC = "P51"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4 |KEEPER; \r
+NET "red[0]"           LOC = "P4"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "red[1]"           LOC = "P5"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "red[2]"           LOC = "P7"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "red[3]"           LOC = "P9"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "green[0]"         LOC = "P204" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "green[1]"         LOC = "P205" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "green[2]"         LOC = "P2"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "green[3]"         LOC = "P3"   |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "blue[0]"          LOC = "P198" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "blue[1]"          LOC = "P199" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "blue[2]"          LOC = "P200" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "blue[3]"          LOC = "P203" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "_hsync"           LOC = "P196" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "_vsync"           LOC = "P197" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "_joy1[0]"         LOC = "P172" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy1[1]"         LOC = "P171" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy1[2]"         LOC = "P169" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy1[3]"         LOC = "P167" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy1[4]"         LOC = "P168" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy1[5]"         LOC = "P175" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[0]"         LOC = "P182" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[1]"         LOC = "P181" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[2]"         LOC = "P180" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[3]"         LOC = "P176" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[4]"         LOC = "P178" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "_joy2[5]"         LOC = "P183" |IOSTANDARD = LVCMOS33 |PULLUP; \r
+NET "kbdclk"           LOC = "P11"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; \r
+NET "kbddat"           LOC = "P10"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; \r
+NET "msclk"            LOC = "P13"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; \r
+NET "msdat"            LOC = "P12"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; \r
+NET "left"             LOC = "P191" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "right"            LOC = "P190" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12; \r
+NET "sdi"              LOC = "P85"  |IOSTANDARD = LVCMOS33 ; \r
+NET "sdo"              LOC = "P19"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "sck"              LOC = "P90"  |IOSTANDARD = LVCMOS33 |CLOCK_DEDICATED_ROUTE = FALSE;\r
+NET "_spi_cs"          LOC = "P93"  |IOSTANDARD = LVCMOS33 ; \r
+NET "scsi_hshake"       LOC = "P86"  |IOSTANDARD = LVCMOS33 ;\r
+NET "nmi"             LOC = "P87"  |IOSTANDARD = LVCMOS33 ;\r
+NET "rxd"              LOC = "P185" |IOSTANDARD = LVCMOS33 ; \r
+NET "txd"              LOC = "P184" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "rts"              LOC = "P187" |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4; \r
+NET "cts"              LOC = "P189" |IOSTANDARD = LVCMOS33 ; \r
+NET "pwrled"           LOC = "P94"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 12 |PULLUP; \r
+NET "_15khz"           LOC = "P194" |IOSTANDARD = LVCMOS33 |PULLUP ; \r
+NET "gpio"             LOC = "P16"  |IOSTANDARD = LVCMOS33 |PULLUP ;\r
+NET "init_b"           LOC = "P83"  |IOSTANDARD = LVCMOS33 |SLEW = SLOW |DRIVE = 4;\r
+NET "_cpu_as" CLOCK_DEDICATED_ROUTE = FALSE;\r
diff --git a/fpga/minimigmac.v b/fpga/minimigmac.v
new file mode 100644 (file)
index 0000000..84caef3
--- /dev/null
@@ -0,0 +1,560 @@
+`timescale 1ns / 100ps
+
+/*
+ * Note about interrupt wiring:
+ * 
+ * Level IPL 0 <- VIA
+ *       IPL 1 <- SCC
+ *       IPL 2 <- xxx
+ * 
+ * With debug switch clamping all 3 down
+ */
+
+module minimigmac
+(
+       /* m68k CPU pins */
+       inout   [15:0] cpu_data,        // m68k data bus
+       input   [23:1] cpu_address,     // m68k address bus
+       output  [2:0] _cpu_ipl,         // m68k interrupt request
+       input   _cpu_as,                // m68k address strobe
+       input   _cpu_uds,               // m68k upper data strobe
+       input   _cpu_lds,               // m68k lower data strobe
+       input   cpu_r_w,                // m68k read / write
+       output  _cpu_dtack,             // m68k data acknowledge
+       inout   _cpu_reset,             // m68k reset
+       output  cpu_clk,                // m68k clock
+
+       /* SRAM pins */
+       inout   [15:0] ram_data,        // sram data bus
+       output  [19:1] ram_address,     // sram address bus
+       output  [3:0] _ram_ce,          // sram chip enable
+       output  _ram_bhe,               // sram upper byte select
+       output  _ram_ble,               // sram lower byte select
+       output  _ram_we,                // sram write enable
+       output  _ram_oe,                // sram output enable
+
+       /* RS232 pins */
+       input   rxd,                    // rs232 receive
+       output  txd,                    // rs232 send
+       input   cts,                    // rs232 clear to send
+       output  rts,                    // rs232 request to send
+
+       /* Joystick ports */
+       input   [5:0]_joy1,             // joystick 1 [fire2,fire,up,down,left,right]
+       input   [5:0]_joy2,             // joystick 2 [fire2,fire,up,down,left,right]
+
+       /* PS2 ports */
+       inout   msdat,                  // PS2 mouse data
+       inout   msclk,                  // PS2 mouse clk
+       inout   kbddat,                 // PS2 keyboard data
+       inout   kbdclk,                 // PS2 keyboard clk
+
+       /* Video pins */
+       output  _hsync,                 // horizontal sync
+       output  _vsync,                 // vertical sync
+       output  [3:0] red,              // red
+       output  [3:0] green,            // green
+       output  [3:0] blue,             // blue
+
+       /* Audio pins */
+       output  left,                   // audio bitstream left
+       output  right,                  // audio bitstream right
+
+       /* System pins */
+       input   mclk,                   // master system clock (4.433619 Mhz)
+       output  pwrled,                 // power led
+       output  init_b,
+       input   _15khz,                 // scandoubler disable (jumper, unused)
+       output  gpio,
+       output  scsi_hshake,            // handshake for scsi fifo
+       input   nmi,                    // macsbug !
+
+       /* SPI pins */
+       input   _spi_cs,                // SPI chip select
+       input   sdi,                    // SPI data input
+       inout   sdo,                    // SPI data output
+       input   sck                     // SPI clock
+);
+       /* Internal clocks */
+       wire    sysclk16;
+       wire    sysclk33;
+       wire    pixclk; 
+
+       /* Backbus SPI interface */
+       wire [5:0]      bb_addr;
+       wire [7:0]      bb_wdata;
+       wire [7:0]      bb_rdata;
+       wire            bb_strobe;
+       wire            bb_wr;
+
+       /* Chip-selects & rdata for backbus clients */
+       wire            bb_cs_scope;
+       wire [7:0]      bb_rdata_scope;
+       wire            bb_cs_ctrl;
+       wire [7:0]      bb_rdata_ctrl;
+       wire            bb_cs_cpu;
+       wire [7:0]      bb_rdata_cpu;
+       wire            bb_cs_iwm;
+       wire [7:0]      bb_rdata_iwm;
+       wire            bb_cs_scsi;
+       wire [7:0]      bb_rdata_scsi;
+       wire            bb_cs_mem;
+       wire [7:0]      bb_rdata_mem;
+       
+       /* Signals for the scope module */
+       wire [63:0]     scoped_sigs;    
+
+       /* Main bus between CPU and devices */
+       wire [23:1]     bus_addr;
+       wire [15:0]     bus_wdata;
+       wire            bus_ube;
+       wire            bus_lbe;
+       wire            bus_we;
+       wire            bus_phase;
+       wire            bus_cs_ram;
+       wire            bus_cs_rom;
+       wire            bus_ack_mem;
+       wire [15:0]     bus_rdata_mem;
+       wire            bus_cs_scsi;
+       wire            bus_ack_scsi;
+       wire [7:0]      bus_rdata_scsi;
+       wire            bus_cs_scc;
+       wire            bus_ack_scc;
+       wire [7:0]      bus_rdata_scc;
+       wire            bus_cs_via;
+       wire            bus_ack_via;
+       wire [7:0]      bus_rdata_via;
+       wire            bus_cs_iwm;
+       wire            bus_ack_iwm;
+       wire [7:0]      bus_rdata_iwm;
+
+       /* VIA wires */
+       wire [2:0]      via_pa_sndvol;
+       wire            via_pa_sndbuf_sel;      /* 1=main, 0=alt */            
+       wire            via_pa_rom_ovl;         /* 1=overlay */
+       wire            via_pa_disksel;
+       wire            via_pa_vidbuf_sel;      /* 1=main, 0=alt */
+       wire            via_pa_scc_wreq;
+       wire            via_pb_mouse_switch;
+       wire            via_pb_mouse_x2;
+       wire            via_pb_mouse_y2;
+       wire            via_pb_snddis;          /* 1=disabled */
+       wire [7:0]      via_kbd_out;
+       wire            via_kbd_out_strobe;
+       wire [7:0]      via_kbd_in;
+       wire            via_kbd_in_strobe;
+
+       /* Video wires */
+       wire            vid_req;
+       wire            vid_ack;
+       wire [15:0]     vid_pixels;
+       wire            vid_hblank;
+       
+       /* SCC additional wires */
+       wire            scc_mouse_x1;
+       wire            scc_mouse_y1;
+
+       /* RTC wires */
+       wire            rtc_onesec;     
+       wire            rtc_data_in;
+       wire            rtc_data_out;
+       wire            rtc_data_clock;
+       wire            _rtc_data_enable;
+
+       /* IRQs */
+       wire            _via_irq;
+       wire            _scc_irq;
+       reg             _prg_irq;       
+       reg             nmi_sync;       
+       
+       /* Control lines */
+       reg             led;
+
+       /* Reset hack */
+       reg [3:0]       reset_ctl = 4'b0000;/* Xilinx tool should cope */
+       wire            reset;
+       wire            ctrl_cpu_reset; 
+
+       /* Led test hack */
+       reg [31:0]      led_cnt;
+
+       /* Scope test hack */
+       reg [7:0]       scope_test;
+
+       /* PS2 mouse & kbd port debug */
+       wire [15:0]     ps2m_dbg;
+       wire [15:0]     ps2k_dbg;
+       
+       /* Not real synchronizers but will do */
+       reg             _vblank_sync;
+       reg             hblank_sync;
+
+       /* Instanciate clock generator */
+       clockgen clocks(.mclk(mclk),
+                       .sysclk16(sysclk16),
+                       .sysclk33(sysclk33),
+                       .pixclk(pixclk));
+
+       /* Low skew clock out trick from Matt */
+       OFDDRCPE OFDDRCPE_inst (\r
+               .Q(cpu_clk), // Data output (connect directly to
+                                            // top-level port)\r
+               .C0(sysclk16), // 0 degree clock input\r
+               .C1(~sysclk16), // 180 degree clock input\r
+               .CE(1'b1), // Clock enable input\r
+               .CLR(1'b0), // Asynchronous reset input\r
+               .D0(1'b1), // Posedge data input\r
+               .D1(1'b0), // Negedge data input\r
+               .PRE(1'b0) // Asynchronous preset input\r
+       );
+       
+       /* Instanciate backbus/SPI interface. Use 16Mhz clock for now */
+       spi_backbus spi_bb(.sysclk(sysclk16),
+                          .reset(reset),
+                          ._scs(_spi_cs),
+                          .sdi(sdi),
+                          .sdo(sdo),
+                          .sck(sck),
+                          .bb_addr(bb_addr),
+                          .bb_wdata(bb_wdata),
+                          .bb_rdata(bb_rdata),
+                          .bb_strobe(bb_strobe),
+                          .bb_wr(bb_wr));
+
+       /* Our reset currently comes reset_ctl, this will deassert it
+        * after 4clk of config (gives time to slower components like
+        * video to reset)
+        */
+       assign reset = ~reset_ctl[0];
+       always@(posedge sysclk16)
+         if (!ctrl_cpu_reset && !_cpu_reset)
+           reset_ctl <= 0;
+         else
+           reset_ctl[3:0] <= { 1'b1, reset_ctl[3:1] };
+       assign _cpu_reset = ctrl_cpu_reset ? 1'b0 : 1'bz;       
+
+       /* Instanciate scope module */
+       scope scop0(.sysclk(sysclk16),
+                   .capclk(sysclk16),
+                   .reset(reset),
+                   .sigs(scoped_sigs),
+                   .bb_addr(bb_addr),
+                   .bb_wdata(bb_wdata),
+                   .bb_rdata(bb_rdata_scope),
+                   .bb_strobe(bb_strobe && bb_cs_scope),
+                   .bb_wr(bb_wr));       
+
+`ifdef __disabled__
+       assign scoped_sigs = { msclk,   /* 63 */
+                              msdat,           /* 62 */
+                              cpu_data,        /* 61..46 */
+                              cpu_address,     /* 45..23 */
+                              _cpu_ipl,        /* 22..20 */
+                              _cpu_as,         /* 19 */
+                              _cpu_uds,        /* 18 */
+                              _cpu_lds,        /* 17 */
+                              cpu_r_w,         /* 16 */
+                              _cpu_dtack,      /* 15 */
+                              _cpu_reset,      /* 14 */
+                              6'b111111,
+                              scope_test};
+`else
+       assign scoped_sigs = 0;
+`endif
+       always@(posedge reset or posedge sysclk16) begin
+               if (reset) begin
+                       scope_test <= 0;
+               end else begin
+                       scope_test <= scope_test + 1;
+               end
+       end
+
+       /* Instanciate the CPU interface */
+       cpu_intf cpu0(.sysclk(sysclk16),
+                     .reset(reset),
+                     .cpu_data(cpu_data),
+                     .cpu_addr(cpu_address),
+                     ._cpu_as(_cpu_as),
+                     ._cpu_uds(_cpu_uds),
+                     ._cpu_lds(_cpu_lds),
+                     .cpu_r_w(cpu_r_w),
+                     ._cpu_dtack(_cpu_dtack),
+                     .bb_addr(bb_addr),
+                     .bb_wdata(bb_wdata),
+                     .bb_rdata(bb_rdata_cpu),
+                     .bb_strobe(bb_strobe && bb_cs_cpu),
+                     .bb_wr(bb_wr),
+                     .bus_addr(bus_addr),
+                     .bus_wdata(bus_wdata),
+                     .bus_ube(bus_ube),
+                     .bus_lbe(bus_lbe),
+                     .bus_we(bus_we),
+                     .bus_phase(bus_phase),
+                     .bus_cs_ram(bus_cs_ram),
+                     .bus_cs_rom(bus_cs_rom),
+                     .bus_ack_mem(bus_ack_mem),
+                     .bus_rdata_mem(bus_rdata_mem),
+                     .bus_cs_scsi(bus_cs_scsi),
+                     .bus_ack_scsi(bus_ack_scsi),
+                     .bus_rdata_scsi(bus_rdata_scsi),
+                     .bus_cs_scc(bus_cs_scc),
+                     .bus_ack_scc(bus_ack_scc),
+                     .bus_rdata_scc(bus_rdata_scc),
+                     .bus_cs_via(bus_cs_via),
+                     .bus_ack_via(bus_ack_via),
+                     .bus_rdata_via(bus_rdata_via),
+                     .bus_cs_iwm(bus_cs_iwm),
+                     .bus_ack_iwm(bus_ack_iwm),
+                     .bus_rdata_iwm(bus_rdata_iwm),
+                     .rom_ovl(via_pa_rom_ovl));
+
+       /* CPU interrupt inputs */
+       /* 
+        * Don't assert _via_irq if _scc_irq is asserted
+        * a temporary spurrious is fine but the ROM will not
+        * handle otherwise a level 3 (it just RTEs).
+        */
+       assign _cpu_ipl = _prg_irq ?
+                         {1'b1, _scc_irq, _via_irq | ~_scc_irq }
+                         : 3'b000;     
+
+       /* Instanciate memory interface */
+       mem_intf mem0(.sysclk(sysclk16),
+                     .reset(reset),
+                     .ram_data(ram_data),
+                     .ram_address(ram_address),
+                     ._ram_ce(_ram_ce),
+                     ._ram_bhe(_ram_bhe),
+                     ._ram_ble(_ram_ble),
+                     ._ram_we(_ram_we),
+                     ._ram_oe(_ram_oe),
+                     .bus_cs_ram(bus_cs_ram),
+                     .bus_cs_rom(bus_cs_rom),
+                     .bus_we(bus_we),
+                     .bus_ack(bus_ack_mem),
+                     .bus_addr(bus_addr[22:1]),
+                     .bus_ube(bus_ube),
+                     .bus_lbe(bus_lbe),
+                     .bus_phase(bus_phase),
+                     .bus_wdata(bus_wdata),
+                     .bus_rdata(bus_rdata_mem),
+                     .bb_addr(bb_addr),
+                     .bb_wdata(bb_wdata),
+                     .bb_rdata(bb_rdata_mem),
+                     .bb_strobe(bb_strobe & bb_cs_mem),
+                     .bb_wr(bb_wr),
+                     .vid_bufsel(via_pa_vidbuf_sel),
+                     .vid_req(vid_req),
+                     .vid_ack(vid_ack),
+                     .vid_pixels(vid_pixels)); 
+                             
+       /* Instanciate VIA */
+       via6522 via0(.sysclk(sysclk16),
+                    .reset(reset),
+                    .cs(bus_cs_via & bus_phase),
+                    .we(bus_we),
+                    .rs(bus_addr[12:9]),
+                    .wdata(bus_wdata[15:8]),
+                    .rdata(bus_rdata_via),
+                    ._irq(_via_irq),
+                    .pa_in7(via_pa_scc_wreq),
+                    .pa_out({via_pa_vidbuf_sel,
+                             via_pa_disksel,
+                             via_pa_rom_ovl,
+                             via_pa_sndbuf_sel,
+                             via_pa_sndvol}),
+                    .pb_out2_0({_rtc_data_enable,
+                                rtc_data_clock,
+                                rtc_data_out}),
+                    .pb_out7(via_pb_snddis),
+                    .pb_in6_3({hblank_sync,
+                               via_pb_mouse_y2,
+                               via_pb_mouse_x2,
+                               via_pb_mouse_switch}),
+                    .pb_in0(rtc_data_in),
+                    .sr_in(via_kbd_in),
+                    .sr_in_strobe(via_kbd_in_strobe),
+                    .sr_out(via_kbd_out),
+                    .sr_out_strobe(via_kbd_out_strobe),
+                    .ca1(_vblank_sync),
+                    .ca2(rtc_onesec));
+         
+       /* Simplified one-phase interface */
+       assign bus_ack_via = bus_cs_via;
+
+       /* Instanciate the RTC */
+       rtc rtc0(.sysclk(sysclk16),
+                .reset(reset),
+                .onesec(rtc_onesec),
+                .data_in(rtc_data_in),
+                .data_out(rtc_data_out),
+                .data_clock(rtc_data_clock),
+                ._data_enable(_rtc_data_enable));
+                
+       /* Instanciate scc */
+       scc scc0(.sysclk(sysclk16),
+                .reset_hw(reset),
+                .cs(bus_cs_scc & bus_phase),
+                .we(bus_we),
+                .rs(bus_addr[2:1]),
+                .wdata(bus_wdata[15:8]),
+                .rdata(bus_rdata_scc),
+                ._irq(_scc_irq),
+                .rxd(rxd),
+                .txd(txd),
+                .cts(cts),
+                .rts(rts),
+                .dcd_a(scc_mouse_x1),
+                .dcd_b(scc_mouse_y1),
+                .wreq(via_pa_scc_wreq));
+       /* Simplified one-phase interface */
+       assign bus_ack_scc = bus_cs_scc;        
+
+       /* Instanciate iwm */
+       iwm iwm0(.sysclk(sysclk16),
+                .reset(reset),
+                .cs(bus_cs_iwm),
+                .we(bus_we),
+                .ack(bus_ack_iwm),
+                .phase(bus_phase),
+                .rs(bus_addr[12:9]),
+                .wdata(bus_wdata[7:0]),
+                .rdata(bus_rdata_iwm),
+                .bb_addr(bb_addr),
+                .bb_wdata(bb_wdata),
+                .bb_rdata(bb_rdata_iwm),
+                .bb_strobe(bb_strobe && bb_cs_iwm),
+                .bb_wr(bb_wr),
+                .via_sel(via_pa_disksel));
+       
+       /* Instanciate scsi */
+       ncr5380 scsi0(.sysclk(sysclk16),
+                     .reset(reset),
+                     .bus_cs(bus_cs_scsi),
+                     .bus_we(bus_we),
+                     .bus_ack(bus_ack_scsi),
+                     .bus_phase(bus_phase),
+                     .bus_rs(bus_addr[6:4]),
+                     .dack(bus_addr[9]),
+                     .wdata(bus_wdata[15:8]),
+                     .rdata(bus_rdata_scsi),
+                     .scsi_hshake(scsi_hshake),
+                     .bb_addr(bb_addr),
+                     .bb_wdata(bb_wdata),
+                     .bb_rdata(bb_rdata_scsi),
+                     .bb_strobe(bb_strobe && bb_cs_scsi),
+                     .bb_wr(bb_wr));
+
+       /* Instanciate video output module */
+       video video0(.pixclk(pixclk),
+                    .reset(reset),
+                    .pixels(vid_pixels),
+                    .req_pix(vid_req),
+                    .ack_pix(vid_ack),
+                    ._hsync(_hsync),
+                    ._vsync(_vsync),
+                    .red(red),
+                    .green(green),
+                    .blue(blue),
+                    .hblank(vid_hblank));
+
+       /* Instanciate the PS/2 mouse interface */
+       ps2_mouse mouse0(.sysclk(sysclk16),
+                        .reset(reset),
+                        .ps2dat(msdat),
+                        .ps2clk(msclk),
+                        .x1(scc_mouse_x1),
+                        .y1(scc_mouse_y1),
+                        .x2(via_pb_mouse_x2),
+                        .y2(via_pb_mouse_y2),
+                        .button(via_pb_mouse_switch),
+                        .debug(ps2m_dbg));
+
+       /* Instanciate the PS/2 keyboard interface */
+       ps2_kbd kbd0(.sysclk(sysclk16),
+                    .reset(reset),
+                    .ps2dat(kbddat),
+                    .ps2clk(kbdclk),
+                    .data_out(via_kbd_out),
+                    .strobe_out(via_kbd_out_strobe),
+                    .data_in(via_kbd_in),
+                    .strobe_in(via_kbd_in_strobe),
+                    .debug(ps2k_dbg));
+
+       /* Instanciate global backbus control registers */
+       ctrl ctrl0(.sysclk(sysclk16),
+                  .reset(reset),
+                  .bb_addr(bb_addr),
+                  .bb_wdata(bb_wdata),
+                  .bb_rdata(bb_rdata_ctrl),
+                  .bb_strobe(bb_strobe && bb_cs_ctrl),
+                  .bb_wr(bb_wr),
+                  .ps2m_dbg(ps2m_dbg),
+                  .ps2k_dbg(ps2k_dbg),
+                  .cpu_reset(ctrl_cpu_reset));
+
+       /* Generate backbus chip selects and mux the backbus read data */
+       assign bb_cs_ctrl  = bb_addr[5:3] == 3'b000;
+       assign bb_cs_cpu   = bb_addr[5:4] == 3'b001;
+       assign bb_cs_mem   = bb_addr[5:3] == 3'b100;
+       assign bb_cs_iwm   = bb_addr[5:3] == 3'b101;
+       assign bb_cs_scsi  = bb_addr[5:3] == 3'b110;
+       assign bb_cs_scope = bb_addr[5:3] == 3'b111;
+
+       assign bb_rdata = bb_cs_ctrl  ? bb_rdata_ctrl :
+                         bb_cs_cpu   ? bb_rdata_cpu :
+                         bb_cs_iwm   ? bb_rdata_iwm :
+                         bb_cs_scsi  ? bb_rdata_scsi :
+                         bb_cs_mem   ? bb_rdata_mem :
+                         bb_cs_scope ? bb_rdata_scope : 8'hff;
+
+       /* Test LED */
+       always@(posedge sysclk16) begin
+               if (reset) begin
+                       led <= 0;
+                       led_cnt <= 0;
+               end else begin
+                       led_cnt <= led_cnt + 1;
+                       if (led_cnt == 1000000) begin
+                               led_cnt <= 0;
+                               led <= ~led;
+                       end
+               end
+       end
+
+       /* Programmer switch, latch the button */
+       always@(posedge sysclk16) begin
+               if (reset) begin
+                       nmi_sync <= 0;
+                       _prg_irq <= 1;
+               end else begin
+                       nmi_sync <= nmi;
+                       _prg_irq <= ~nmi_sync;
+               end
+       end     
+
+       /* Not real synchronizers but will do */
+       always@(posedge sysclk16 or posedge reset) begin
+               if (reset) begin
+                       _vblank_sync <= 1;
+                       hblank_sync <= 0;
+               end else begin
+                       _vblank_sync <= _vsync;
+                       hblank_sync <= vid_hblank;
+               end
+       end
+
+       /* LED output powered by a weak pullup */
+       assign pwrled = led ? 1'b1 : 1'bz;
+               
+       /* Unused output signals */
+       assign kbddat = 1'bz;
+       assign kbdclk = 1'bz;   
+
+       assign left = 0;
+       assign right = 0;
+
+       assign init_b = 0;
+       assign gpio = 1'bz;     
+       
+endmodule // mimigmac
diff --git a/fpga/ncr5380.v b/fpga/ncr5380.v
new file mode 100644 (file)
index 0000000..7ad7b46
--- /dev/null
@@ -0,0 +1,530 @@
+`timescale 1ns / 100ps
+
+/*
+ * NCR5380 SCSI module for minimigmac.
+ * 
+ * Located on high data bus, but writes are done at odd addresses as
+ * LDS is used as WR signals or something like that on a Mac Plus.
+ * 
+ * We don't care here and just ignore which side was used. We also don't
+ * implement the interrupt logic (well at least we don't route it to the
+ * CPU like a real Mac) and we do all sort of simplifications such as only
+ * supporting initiator mode
+ * 
+ * This is implementation is really just a gateway to the backbus, the
+ * microcontroller can observe SCSI line states and exchange data.
+ * 
+ * Notes:
+ * 
+ * - Arbitration -
+ * 
+ * We do not support external arbitration & reselection, thus we always
+ * win it. Thus our arbitration logic is as simple as reflecting the
+ * arbitration bit into ICR:AIP and never setting ICR:LA, no timing
+ * to care much about.
+ * 
+ * Note: We also don't wait for a bus free phase, so we assume SW will
+ * note try to arbitrate while BSY is still asserted by the target, if
+ * that was to happen, I could use a latch to delay arbitration to
+ * finding that BSY is false and checking SEL.
+ * 
+ * - Bus reset -
+ * 
+ * We don't do anything special, just report the state of the line. We
+ * might end up needing to latch it to give a chance for the uC to pick
+ * it up if it's asserted for tfoo short (for ex. the uC is in the middle
+ * of a data transfer).
+ * 
+ * - Busy monitoring -
+ * 
+ * Not implemented... might need to add, ROM doesn't ues it but MacOS
+ * might
+ *
+ * - Interrupts -
+ * 
+ * Nothing implemented, BSR:IRQ never set, ROM doesn't use it, but
+ * MacOS might ...  Select Enable Register is not implemented
+ * 
+ * - Data transfers
+ * 
+ * In dumb mode, it's all manual assertion of the various lines on
+ * both side. However, there's a problem with the Mac ROM when doing
+ * that.
+ * 
+ * When doing a read, the ROM does not wait for REQ to go down after the
+ * last byte has been read. If the PIC is slow enough to react, that menans
+ * that the subsequent call to SCSIComplete, will incorrectly match that REQ
+ * for the one of the status phase, and get a phase mismatch. This can happen
+ * with my SPI protocol which is somewhat sub optimal for alternating reads
+ * and writes
+ * 
+ * So the PIC has to be fast enough at lowering REQ when ACK gets set ...
+ * or always use automatic mode:
+ * 
+ * Automatic mode: when in that mode, REQ/ACK is handled in HW and the
+ * separate _scsi_dreq line is used to pace the PIC as follow:
+ * 
+ *  - On reads (target -> initiator)
+ * 
+ *     * Signal din_full set set when PIC writes to IDATA,
+ *       causes REQ to be asserted
+ *     * ACK (either manual or auto-generated by DMA mode)
+ *       clears din_full (and thus de-asserts REQ)
+ *     * _scsi_dreq is asserted based on a flip flop that
+ *        gets set when data is read by host (DACK) and set
+ *        when REQ is asserted (IDATA is written). This only
+ *        happens while automatic mode is enabled.
+ * 
+ * - On writes (initiator -> target)
+ *     * 
+ *
+
+/* Read registers */
+`define RREG_CDR       3'h0    /* Current SCSI data */
+`define RREG_ICR       3'h1    /* Initiator Command */
+`define RREG_MR                3'h2    /* Mode register */
+`define RREG_TCR       3'h3    /* Target Command */
+`define RREG_CSR       3'h4    /* SCSI bus status */
+`define RREG_BSR       3'h5    /* Bus and status */
+`define RREG_IDR       3'h6    /* Input data */
+`define RREG_RST       3'h7    /* Reset */
+
+/* Write registers */
+`define WREG_ODR       3'h0    /* Ouptut data */
+`define WREG_ICR       3'h1    /* Initiator Command */
+`define WREG_MR                3'h2    /* Mode register */
+`define WREG_TCR       3'h3    /* Target Command */
+`define WREG_SER       3'h4    /* Select Enable */
+`define WREG_DMAS      3'h5    /* Start DMA Send */
+`define WREG_DMATR     3'h6    /* Start DMA Target receive */
+`define WREG_IDMAR     3'h7    /* Start DMA Initiator receive */
+
+/* Backbus interface */
+`define BB_REG_LINES   0       /* Read only, line states set by initiator */
+`define BB_LINES_BSY   7
+`define BB_LINES_SEL   6       
+`define BB_LINES_ACK   5
+`define BB_LINES_ATN   4
+`define BB_LINES_RST   3
+
+`define BB_REG_ASSERT  1       /* RW, assert lines by target */
+`define BB_ASSERT_BSY  7
+`define BB_ASSERT_SEL  6
+`define BB_ASSERT_CD   5
+`define BB_ASSERT_IO   4
+`define BB_ASSERT_MSG  3
+`define BB_ASSERT_REQ  2
+`define BB_ASSERT_RST  1
+`define BB_ASSERT_AUTO 0       /* Automatic mode */
+
+`define BB_REG_ODATA   2
+`define BB_REG_IDATA   3
+`define BB_REG_ACNT_HI 4
+`define BB_REG_ACNT_LO 5
+
+/* MR bit numbers */
+`define MR_DMA_MODE    1
+`define MR_ARB         0
+
+/* ICR bit numbers */
+`define ICR_A_RST      7
+`define ICR_TEST_MODE  6
+`define ICR_DIFF_ENBL  5
+`define ICR_A_ACK      4
+`define ICR_A_BSY      3
+`define ICR_A_SEL      2
+`define ICR_A_ATN      1
+`define ICR_A_DATA     0
+
+/* TCR bit numbers */
+`define TCR_A_REQ      3
+`define TCR_A_MSG      2
+`define TCR_A_CD       1
+`define TCR_A_IO       0
+
+module ncr5380(input   sysclk,
+              input    reset,
+
+              /* Bus interface. 3-bit address, to be wired
+               * appropriately upstream (to A4..A6) plus one
+               * more bit (A9) wired as dack.
+               */
+              input            bus_cs,
+              input            bus_we,
+              output           bus_ack,
+              input            bus_phase,
+              input [2:0]      bus_rs,
+              input            dack,
+              input [7:0]      wdata,
+              output [7:0]     rdata,
+
+              /* Backbus interface */
+              input [5:0]      bb_addr,
+              input [7:0]      bb_wdata,
+              output [7:0]     bb_rdata,
+              input            bb_strobe,
+              input            bb_wr,
+
+              /* External request line */
+              output           scsi_hshake
+          );
+
+       /* Bus interface signals */
+       wire                    reg_rd;
+       wire                    reg_wr;
+       wire                    dma_rd;
+       wire                    dma_wr;
+       wire                    bus_hold;       
+
+       /* Mode Register */
+       reg [7:0]               mr;
+
+       /* Initiator Command Register */        
+       wire                    icr_aip;
+       wire                    icr_la;
+       wire [7:0]              icr_read;
+       reg [7:0]               icr;
+
+       /* Target Command Register */
+       reg [3:0]               tcr;
+
+       /* Backbus asserts */
+       reg [7:0]               bb_assert;
+
+       /* These are the simulated SCSI bus lines, we use positive
+        * polarity to simplify things
+        */
+       wire                    scsi_bsy;
+       wire                    scsi_sel;
+       wire                    scsi_ack;
+       wire                    scsi_atn;
+       wire                    scsi_cd;
+       wire                    scsi_io;
+       wire                    scsi_msg;
+       wire                    scsi_req;
+       wire                    scsi_rst;
+
+       /* What backbus sees of the above */
+       wire [7:0]              bb_lines;
+       wire                    bb_reg_rd;
+       wire                    bb_reg_wr;
+       wire                    bb_reg_lines;
+       wire                    bb_reg_assert;
+       wire                    bb_reg_odata;
+       wire                    bb_reg_idata;
+       wire                    bb_reg_acnt_hi;
+       wire                    bb_reg_acnt_lo;
+
+       /* SCSI bus status register */
+       wire [7:0]              csr;
+
+       /* Bus and Status register */
+       wire                    bsr_eodma;
+       wire                    bsr_dmarq;
+       wire                    bsr_perr;
+       wire                    bsr_irq;
+       wire                    bsr_pmatch;
+       wire                    bsr_berr;                       
+       wire [7:0]              bsr;
+
+       /* Current data register and logic */
+       wire [7:0]              cur_data;
+       wire                    out_en;
+
+       /* Data in and out latches and associated
+        * control logic for DMA
+        */
+       reg [7:0]               din;
+       reg [7:0]               dout;
+       reg                     dphase;
+       reg                     dma_en; 
+
+       /* BB automatic mode */
+       reg                     autoreq;
+       reg [15:0]              autocnt;
+       wire                    autoreg;
+       reg                     autodrop;                       
+       reg [7:0]               dout_latch;
+
+       /* --- Main host-side interface --- */
+
+       /* Register & DMA accesses decodes */
+       assign dma_rd = bus_cs & bus_phase & dack & ~bus_we;
+       assign dma_wr = bus_cs & bus_phase & dack & bus_we;
+       assign reg_rd = bus_cs & bus_phase & ~dack & ~bus_we;
+       assign reg_wr = bus_cs & bus_phase & ~dack & bus_we;
+
+       /* Hold bus to phase 0 on DMA accesses if necessary, this
+        * makes "blind" transfers 100% reliable since I really don't
+        * know if my SPI interface is fast enough
+        */
+       assign bus_hold = dack & (scsi_ack | ~scsi_req) & dma_en;
+       assign bus_ack = bus_cs & ~bus_hold;
+
+       /* System bus reads
+        *
+        * Q. to HW dudes... I don't need to test reg_rd everywhere, I can
+        * just let the mux toggle all the time, but would that consume 
+        * more power or it's so ridiculous we don't care ?
+        */
+       assign rdata = dma_rd              ? cur_data                   :
+                      reg_rd && bus_rs == `RREG_CDR ? cur_data         :
+                      reg_rd && bus_rs == `RREG_ICR ? icr_read         :
+                      reg_rd && bus_rs == `RREG_MR  ? mr               :
+                      reg_rd && bus_rs == `RREG_TCR ? { 4'h0 | tcr }   :
+                      reg_rd && bus_rs == `RREG_CSR ? csr              :
+                      reg_rd && bus_rs == `RREG_BSR ? bsr              :
+                      reg_rd && bus_rs == `RREG_IDR ? cur_data         :
+                      reg_rd && bus_rs == `RREG_RST ? 8'hff            :
+                      8'hff;
+       
+       /* DMA handhsaking logic. Two phase logic, in phase 0
+        * DRQ follows SCSI _REQ until we see DACK. In phase 1
+        * we just wait for SCSI _REQ to go down and go back to
+        * phase 0. We assert SCSI _ACK in phase 1.
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       dphase <= 0;                    
+               end else begin
+                       if (!dma_en) begin
+                               dphase <= 0;                            
+                       end else if (dphase == 0) begin
+                               /* Be careful to do that in bus phase 1,
+                                * not phase 0, or we would incorrectly
+                                * assert bus_hold and lock up the system
+                                */
+                               if ((dma_rd || dma_wr) && scsi_req) begin
+                                       dphase <= 1;
+                               end
+                       end else if (!scsi_req) begin
+                               dphase <= 0;
+                       end
+               end
+       end
+
+       /* Data out latch (in DMA mode, this is one cycle after we've
+        * asserted ACK)
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 dout <= 8'haa;
+               else if ((reg_wr && bus_rs == `WREG_ODR) || dma_wr)
+                 dout <= wdata;
+       end
+       
+       /* Current data register. Simplified logic: We loop back the
+        * output data if we are asserting the bus, else we get the
+        * input latch
+        */
+       assign cur_data = out_en ? dout : din;
+
+       /* Logic for "asserting the bus" simplified */
+       assign out_en = icr[`ICR_A_DATA] | mr[`MR_ARB]; 
+       
+       /* ICR read wires */
+       assign icr_read = { icr[`ICR_A_RST],
+                           icr_aip,
+                           icr_la,
+                           icr[`ICR_A_ACK],
+                           icr[`ICR_A_BSY],
+                           icr[`ICR_A_SEL],
+                           icr[`ICR_A_ATN],
+                           icr[`ICR_A_DATA] };
+       /* ICR write */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       icr <= 0;
+               end else if (reg_wr && (bus_rs == `WREG_ICR)) begin
+                       icr <= wdata;
+               end
+       end
+
+       /* MR write */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 mr <= 8'b0;           
+               else if (reg_wr && (bus_rs == `WREG_MR))
+                 mr <= wdata;
+       end
+
+       /* TCR write */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 tcr <= 4'b0;          
+               else if (reg_wr && (bus_rs == `WREG_TCR))
+                 tcr <= wdata[3:0];
+       end
+
+       /* DMA start send & receive registers. We currently ignore
+        * the direction.
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       dma_en <= 0;
+               end else begin
+                       if (!mr[`MR_DMA_MODE]) begin
+                               dma_en <= 0;
+                       end else if (reg_wr && (bus_rs == `WREG_DMAS)) begin
+                               dma_en <= 1;
+                       end else if (reg_wr && (bus_rs == `WREG_IDMAR)) begin
+                               dma_en <= 1;
+                       end
+               end
+       end
+
+       /* CSR (read only). We don't do parity */
+       assign csr = { scsi_rst, scsi_bsy, scsi_req, scsi_msg,
+                      scsi_cd, scsi_io, scsi_sel, 1'b0 };      
+
+       /* BSR (read only). We don't do a few things... */
+       assign bsr_eodma = 0;   /* We don't do EOP */
+       assign bsr_dmarq = scsi_req & ~dphase & dma_en;
+       assign bsr_perr = 0;    /* We don't do parity */
+       assign bsr_irq = 0;     /* XXX ? Does MacOS use this ? */
+       assign bsr_pmatch = tcr[`TCR_A_MSG] == scsi_msg &&
+                           tcr[`TCR_A_CD ] == scsi_cd  &&
+                           tcr[`TCR_A_IO ] == scsi_io; 
+       assign bsr_berr = 0;    /* XXX ? Does MacOS use this ? */
+       assign bsr = { bsr_eodma, bsr_dmarq, bsr_perr, bsr_irq,
+                      bsr_pmatch, bsr_berr, scsi_atn, scsi_ack };
+
+       /* --- Simulated SCSI Signals --- */
+
+       /* BSY logic (simplified arbitration, see notes) */
+       assign scsi_bsy = icr[`ICR_A_BSY] |
+                         bb_assert[`BB_ASSERT_BSY] |
+                         mr[`MR_ARB];
+       /* Remains of simplified arbitration logic */
+       assign icr_aip = mr[`MR_ARB];
+       assign icr_la = 0;
+
+       /* Other ORed SCSI signals */
+       assign scsi_sel = icr[`ICR_A_SEL] | bb_assert[`BB_ASSERT_SEL];
+       assign scsi_rst = icr[`ICR_A_RST] | bb_assert[`BB_ASSERT_RST];
+       assign scsi_ack = icr[`ICR_A_ACK] | dphase;
+       assign scsi_atn = icr[`ICR_A_ATN];
+
+       /* Other trivial lines set by target */
+       assign scsi_cd = bb_assert[`BB_ASSERT_CD];
+       assign scsi_io = bb_assert[`BB_ASSERT_IO];
+       assign scsi_msg = bb_assert[`BB_ASSERT_MSG];
+       assign scsi_req = bb_assert[`BB_ASSERT_REQ] | autoreq;
+
+       /* --- Backbus interface --- */
+
+       assign bb_reg_rd = bb_strobe & ~bb_wr;
+       assign bb_reg_wr  = bb_strobe & bb_wr;
+       assign bb_reg_lines = bb_addr[3:0] == `BB_REG_LINES;
+       assign bb_reg_assert = bb_addr[3:0] == `BB_REG_ASSERT;
+       assign bb_reg_odata = bb_addr[3:0] == `BB_REG_ODATA;
+       assign bb_reg_idata = bb_addr[3:0] == `BB_REG_IDATA;
+       assign bb_reg_acnt_hi = bb_addr[3:0] == `BB_REG_ACNT_HI;
+       assign bb_reg_acnt_lo = bb_addr[3:0] == `BB_REG_ACNT_LO;
+
+       /* Backbus lines, ie, pseudo SCSI bus state seen by the
+        * microcontroller. Note that they are positive logic unlike
+        * the real SCSI lines. We only care about the lines that are
+        * not asserted exclusively by the target
+        */     
+       assign bb_lines = { scsi_bsy, scsi_sel, scsi_ack, scsi_atn,
+                           scsi_rst, 3'b0 };
+
+       /* Backbus register reads */
+       assign bb_rdata = bb_reg_lines  ? bb_lines      :
+                         bb_reg_assert ? bb_assert     :
+                         bb_reg_odata  ? (bb_assert[`BB_ASSERT_AUTO] ? dout_latch : dout) :
+                         bb_reg_idata  ? din           :
+                         0;
+
+       /* External handshake logic. We assert it for now when SEL is
+        * asserted in ICR. Or in auto mode, when both req and ack are
+        * low
+        */
+       assign scsi_hshake = icr[`ICR_A_SEL] | icr[`ICR_A_RST] |
+                            (bb_assert[`BB_ASSERT_AUTO] & ~(scsi_ack | scsi_req));     
+
+       /* Data in latch */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 din <= 8'h55;
+               else if (bb_reg_wr && bb_reg_idata)
+                 din <= bb_wdata;
+       end
+       
+       /* BB assert register write */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 bb_assert <= 0;
+               else if (bb_reg_wr && bb_reg_assert)
+                 bb_assert <= bb_wdata;
+       end
+
+       /* --- Backbus "automatic" mode logic crackpot --- */
+
+       /* autocnt register write & decrement */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 autocnt <= 0;
+               else if (bb_reg_wr && bb_reg_acnt_hi)
+                 autocnt[15:8] <= bb_wdata;
+               else if (bb_reg_wr && bb_reg_acnt_lo)
+                 autocnt[7:0] <= bb_wdata;
+               else if (autoreg)
+                 autocnt <= autocnt - 1;               
+       end
+
+       /* Hack to workaround SPI interface giving us two strobes
+        * on the first byte of reads. We use this latch to drop
+        * the first one.
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 autodrop <= 0;
+               else begin
+                       if (bb_reg_wr && bb_reg_acnt_lo)
+                         autodrop <= 1;
+                       else if (bb_strobe)
+                         autodrop <= 0;                        
+               end
+       end
+       
+       /* An automatic register access happens when auto mode is
+        * enabled, autocnt is non-0 and we access the idata or
+        * odata register
+        */
+       assign autoreg = bb_assert[`BB_ASSERT_AUTO] & bb_strobe &
+                        (bb_reg_idata | (bb_reg_odata & ~autodrop)) &
+                        (autocnt != 0);
+
+       /* Automatic mode, toggle REQ automagically during
+        * data phases if there are still bytes to go
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 autoreq <= 0;
+               else begin
+                       if (!bb_assert[`BB_ASSERT_AUTO])
+                         autoreq <= 0;                 
+                       else if (autoreg)
+                         autoreq <= 1;
+                       else if (scsi_ack)
+                         autoreq <= 0;
+               end
+       end
+
+       /* In auto mode writes, we are slightly off as REQ is
+        * asserted by a read done by the PIC so we need to
+        * latch the data when ack is asserted so it's still
+        * valid by the time the PIC reads it. To simplify
+        * things we always latch on ACK
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 dout_latch <= 0;
+               else begin
+                       if (scsi_ack)
+                         dout_latch <= dout;
+               end
+       end     
+endmodule
diff --git a/fpga/ps2.v b/fpga/ps2.v
new file mode 100644 (file)
index 0000000..31f7fb4
--- /dev/null
@@ -0,0 +1,170 @@
+`timescale 1ns / 100ps
+
+/*
+ * Generic PS2 interface module
+ * 
+ * istrobe, oreq, oack and timeout are all 1-clk strobes,
+ * ibyte must be latched on that strobe, obyte is latched
+ * as oreq is detected, oreq is ignore while already
+ * sending.
+ * 
+ * we ignore bad parity on input for now
+ */
+
+module ps2(input       sysclk,
+          input        reset,
+
+          inout        ps2dat,
+          inout        ps2clk,
+
+          output       istrobe,
+          output [7:0] ibyte,
+
+          input        oreq,
+          input [7:0]  obyte,
+          output       oack,
+
+          output       timeout,
+
+          output[1:0]  dbg_state
+          );
+
+       reg [7:0]       clkbuf;
+       reg [7:0]       datbuf;
+       reg             clksync;
+       reg             clkprev;
+       reg             datsync;        
+       reg [10:0]      shiftreg;
+       reg [3:0]       shiftcnt;
+       wire            shiftend;
+       reg [1:0]       state;
+       wire            datout;
+       reg [23:0]      timecnt;        
+       wire            clkdown;
+       wire            opar;
+
+       /* State machine */
+       localparam ps2_state_idle = 0;
+       localparam ps2_state_ring = 1;
+       localparam ps2_state_send = 2;
+       localparam ps2_state_recv = 3;
+
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 state <= ps2_state_idle;
+               else begin
+                       if (timeout && !oreq)
+                         state <= ps2_state_idle;
+                       else
+                         case(state)
+                                 ps2_state_idle: begin
+                                         if (oreq)
+                                           state <= ps2_state_ring;
+                                         else if (clkdown)
+                                           state <= ps2_state_recv;
+                                 end
+                                 ps2_state_ring: begin
+                                         if (timecnt[12])
+                                           state <= ps2_state_send;
+                                 end
+                                 ps2_state_send: begin
+                                         if (shiftend)
+                                           state <= ps2_state_idle;
+                                 end
+                                 ps2_state_recv: begin
+                                         if (oreq)
+                                           state <= ps2_state_ring;
+                                         else if (shiftend)
+                                           state <= ps2_state_idle;
+                                 end
+                         endcase
+               end
+       end
+       assign dbg_state = state;       
+
+       /* Tristate control of clk & data */
+       assign datout = state == ps2_state_ring || state == ps2_state_send;
+       assign ps2dat = (datout & ~shiftreg[0]) ? 1'b0 : 1'bz;
+       assign ps2clk = (state == ps2_state_ring) ? 1'b0 : 1'bz;
+
+       /* Bit counter */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 shiftcnt <= 10;
+               else begin
+                       if (state == ps2_state_idle)
+                         shiftcnt <= 10;
+                       else if (state == ps2_state_ring)
+                         shiftcnt <= 11;                       
+                       else if (clkdown && state != ps2_state_ring)
+                         shiftcnt <= shiftcnt - 1;
+               end
+       end
+
+       /* Shift register, ticks on falling edge of ps2 clock */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 shiftreg <= 0;
+               else begin
+                       if (oreq)
+                         shiftreg <= { 1'b1, opar, obyte, 1'b0 };
+                       else if (clkdown && state != ps2_state_ring)
+                         shiftreg <= { datsync, shiftreg[10:1] };
+               end
+       end
+
+
+       /* Ack/strobe logic */
+       assign shiftend = shiftcnt == 0;        
+       assign oack = (state == ps2_state_send && shiftend);
+       assign istrobe = (state == ps2_state_recv && shiftend); 
+       assign ibyte = shiftreg[8:1];
+
+       /* Filters/synchronizers on PS/2 clock */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       clkbuf <= 0;
+                       clksync <= 0;
+                       clkprev <= 0;
+               end else begin
+                       clkprev <= clksync;                     
+                       clkbuf <= { clkbuf[6:0], ps2clk };
+                       if (clkbuf[7:2] == 6'b000000)
+                         clksync <= 0;
+                       if (clkbuf[7:2] == 6'b111111)
+                         clksync <= 1;
+               end
+       end
+       assign clkdown = clkprev & ~clksync;
+
+       /* Filters/synchronizers on PS/2 data */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       datbuf <= 0;
+                       datsync <= 0;
+               end else begin
+                       datbuf <= { datbuf[6:0], ps2dat };
+                       if (datbuf[7:2] == 6'b000000)
+                         datsync <= 0;
+                       if (datbuf[7:2] == 6'b111111)
+                         datsync <= 1;
+               end
+       end
+                     
+       /* Parity for output byte */
+       assign opar = ~(obyte[0] ^ obyte[1] ^ obyte[2] ^ obyte[3] ^
+                       obyte[4] ^ obyte[5] ^ obyte[6] ^ obyte[7]);     
+
+       /* Timeout logic */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 timecnt <= 0;
+               else begin
+                       if (clkdown | oreq)
+                         timecnt <= 0;
+                       else
+                         timecnt <= timecnt + 1;
+               end
+       end
+       assign timeout = (timecnt == 24'hff_ffff);      
+endmodule
diff --git a/fpga/ps2_kbd.v b/fpga/ps2_kbd.v
new file mode 100644 (file)
index 0000000..3049745
--- /dev/null
@@ -0,0 +1,800 @@
+`timescale 1ns / 100ps
+
+/*
+ * PS2 Keyboard to Mac interface module
+ */
+module ps2_kbd(        input                   sysclk,
+               input                   reset,
+
+               inout                   ps2dat,
+               inout                   ps2clk,
+                
+               input [7:0]             data_out,
+               input                   strobe_out,
+
+               output [7:0]            data_in,
+               output                  strobe_in,
+
+               output reg [15:0]       debug
+);
+
+       reg [8:0]               keymac;
+       reg                     key_pending;
+       reg [21:0]              pacetimer;
+       reg                     inquiry_active;
+       reg                     extended;
+       reg                     keybreak;
+       reg                     capslock;
+       reg                     haskey;
+       wire                    got_key;
+       wire                    got_break;
+       wire                    got_extend;
+       wire                    tick_short;
+       wire                    tick_long;
+       wire                    pop_key;        
+       reg                     cmd_inquiry;
+       reg                     cmd_instant;
+       reg                     cmd_model;
+       reg                     cmd_test;
+       reg[1:0]                state;
+       reg[1:0]                next;
+       reg                     nreq;
+       reg[7:0]                nbyte;
+       
+       /* PS2 interface signals */
+       wire                    istrobe;
+       wire [7:0]              ibyte;
+       reg                     oreq;
+       reg [7:0]               obyte;
+       wire                    oack;
+       wire                    timeout;
+       wire [1:0]              dbg_lowstate;
+       
+       ps2 ps20(.sysclk(sysclk),
+                .reset(reset),
+                .ps2dat(ps2dat),
+                .ps2clk(ps2clk),
+                .istrobe(istrobe),
+                .ibyte(ibyte),
+                .oreq(oreq),
+                .obyte(obyte),
+                .oack(oack),
+                .timeout(timeout),
+                .dbg_state(dbg_lowstate));
+       
+       /* --- PS2 side State machine ---
+        *
+        *  - at state_init: wait for BAT reply
+        *     * 0xaa      -> send 0xed -> state_led1
+        *     * bad reply -> send 0xff -> state_init
+        *     * timeout   -> send 0xff -> state_init
+        * 
+        *  - at state_led1: wait for ack, send LED state
+        *     * 0xfa      -> send 0xYY -> state_led2 YY=LED
+        *     * bad reply -> send 0xff -> state_init
+        *     * timeout   -> send 0xff -> state_init
+        * 
+        *  - at state_led2: wait for ack, go wait data
+        *     * 0xfa                   -> state_wait
+        *     * bad reply -> send 0xff -> state_init
+        *     * timeout   -> send 0xff -> state_init
+        * 
+        *  - at state_wait: wait for data byte
+        *     * capslock  -> send 0xed -> state_led1
+        *     * other                  -> state_wait
+        * 
+        * Works fine with my logitech USB/PS2 keyboard, but fails
+        * miserably with an old HP PS2 keyboard (which also behaves
+        * oddly with minimig as an amiga). Will eventually investigate...
+        */
+       localparam ps2k_state_init      = 0;
+       localparam ps2k_state_led1      = 1;
+       localparam ps2k_state_led2      = 2;
+       localparam ps2k_state_wait      = 3;
+
+
+       /* Unlike my other modules, here I'll play with a big fat
+        * combo logic. The outputs are:
+        *  - oreq   : triggers sending of a byte. Set based on either
+        *             timeout or istrobe, and as such only set for a
+        *             clock.
+        *  - next   : next state
+        *  - obyte  : next byte to send
+        */
+       always@(timeout or state or istrobe or ibyte or capslock) begin
+               nreq = 0;
+               next = state;
+               nbyte = 8'hff;
+
+               if (istrobe || timeout)
+                 case(state)
+                         ps2k_state_init: begin
+                                 if (istrobe && ibyte == 8'haa) begin
+                                         nbyte = 8'hed;
+                                         nreq = 1;
+                                         next = ps2k_state_led1;
+                                 end else if (ibyte != 8'hfa)
+                                   nreq = 1;
+                         end
+                         ps2k_state_led1: begin
+                                 nreq = 1;
+                                 if (istrobe && ibyte == 8'hfa) begin
+                                         nbyte = { 5'b00000, capslock, 1'b1, 1'b0 };
+                                         next = ps2k_state_led2;
+                                 end else
+                                   next = ps2k_state_init;
+                         end
+                         ps2k_state_led2: begin
+                                 if (istrobe && ibyte == 8'hfa)
+                                   next = ps2k_state_wait;
+                                 else begin
+                                         nreq = 1;
+                                         next = ps2k_state_init;
+                                 end
+                         end
+                         ps2k_state_wait: begin
+                                 /* Update LEDs */
+                                 if (istrobe && ibyte == 8'h58) begin
+                                         nbyte = 8'hed;
+                                         nreq = 1;
+                                         next = ps2k_state_led1;
+                                 end
+                         end                     
+                 endcase
+       end
+
+       /* State related latches. We latch oreq and obyte, we don't
+        * necessarily have to but that avoids back to back
+        * receive/send at the low level which can upset things
+        */
+       always@(posedge sysclk or posedge reset)
+         if (reset)
+           state <= ps2k_state_init;
+         else
+           state <= next;      
+       always@(posedge sysclk or posedge reset)
+         if (reset)
+           oreq <= 0;
+         else
+           oreq <= nreq;
+       always@(posedge sysclk or posedge reset)
+         if (reset)
+           obyte <= 0;
+         else
+           obyte <= nbyte;
+
+       assign got_key = (state == ps2k_state_wait) && istrobe;
+       assign got_break = { ibyte[7:1], 1'b0 } == 8'hf0;
+       assign got_extend = { ibyte[7:1], 1'b0 } == 8'he0;
+
+       /* Latch key info from PS2, handle capslock state */
+       always@(posedge sysclk or posedge reset)
+         if (reset) begin
+                 extended <= 0;
+                 keybreak <= 0;        
+                 capslock <= 0;
+         end else if (got_key) begin
+                 if (got_break)
+                   keybreak <= 1;
+                 else if (got_extend)
+                   extended <= 1;
+                 else begin
+                         keybreak <= 0;
+                         extended <= 0;
+
+                         /* Capslock handling */
+                         if (ibyte == 8'h58 && !keybreak)
+                           capslock <= ~capslock;
+                 end
+         end
+
+       /* --- Mac side --- */
+
+       /* Latch commands from Mac */
+       always@(posedge sysclk or posedge reset)
+         if (reset) begin
+                 cmd_inquiry <= 0;
+                 cmd_instant <= 0;
+                 cmd_model <= 0;
+                 cmd_test <= 0;
+         end else begin
+                 if (strobe_out) begin
+                         cmd_inquiry <= 0;
+                         cmd_instant <= 0;
+                         cmd_model <= 0;
+                         cmd_test <= 0;
+                         case(data_out)
+                                 8'h10: cmd_inquiry <= 1;
+                                 8'h14: cmd_instant <= 1;
+                                 8'h16: cmd_model   <= 1;
+                                 8'h36: cmd_test    <= 1;
+                         endcase
+                 end
+         end
+
+       /* Divide our clock to pace our responses to the Mac. tick_short ticks
+        * when we can respond to a command, and tick_long ticks when an inquiry
+        * command shall timeout
+        */
+       always@(posedge sysclk or posedge reset)
+         if (reset)
+           pacetimer <= 0;
+         else begin
+                 /* reset counter on command from Mac */
+                 if (strobe_out)
+                   pacetimer <= 0;               
+                 else if (!tick_long)
+                   pacetimer <= pacetimer + 1;
+         end
+       assign tick_long  = pacetimer == 22'h3fffff;
+       assign tick_short = pacetimer == 22'h000fff;
+
+       /* Delay inquiry responses to after tick_short */
+       always@(posedge sysclk or posedge reset)
+         if (reset)
+           inquiry_active <= 0;
+         else begin
+                 if (strobe_out | strobe_in)
+                   inquiry_active <= 0;
+                 else if (tick_short)
+                   inquiry_active <= cmd_inquiry;                
+         end   
+
+       /* Key answer to the mac XXX FIXME: keypad */
+       assign pop_key = (cmd_instant & tick_short) |
+                        (inquiry_active & tick_long) |
+                        (inquiry_active & key_pending);
+
+       /* Reply to Mac */
+       assign strobe_in = ((cmd_model | cmd_test) & tick_short) | pop_key;     
+
+       /* Latch key_pending */
+       always @(posedge sysclk or posedge reset)
+         if (reset)
+           key_pending <= 0;
+         else begin
+                 if (pop_key | cmd_model | cmd_test)
+                   key_pending <= 0;            
+                 else if (!key_pending & got_key && !got_break && !got_extend)
+                   key_pending <= 1;
+         end
+
+       /* Data to Mac ... XXX: Handle keypad and special case capslock */
+       assign data_in = cmd_test       ? 8'h7d :
+                        cmd_model      ? 8'h03 :
+                        key_pending    ? keymac[7:0] : 8'h7b;  
+
+       /* Keymap. XXX add option to assign ctrl/alt/windows to cmd/option
+        * differently
+        */
+       always @(posedge sysclk)
+         if (got_key && !key_pending) begin
+                 case({extended,ibyte}) // Scan Code Set 2
+                         9'h000:               keymac[8:0] <= 9'h07b;
+                         9'h001:               keymac[8:0] <= 9'h07b;  //F9
+                         9'h002:               keymac[8:0] <= 9'h07b;
+                         9'h003:               keymac[8:0] <= 9'h07b;  //F5
+                         9'h004:               keymac[8:0] <= 9'h07b;  //F3
+                         9'h005:               keymac[8:0] <= 9'h07b;  //F1
+                         9'h006:               keymac[8:0] <= 9'h07b;  //F2
+                         9'h007:               keymac[8:0] <= 9'h07b;  //F12 <OSD>
+                         9'h008:               keymac[8:0] <= 9'h07b;
+                         9'h009:               keymac[8:0] <= 9'h07b;  //F10
+                         9'h00a:               keymac[8:0] <= 9'h07b;  //F8
+                         9'h00b:               keymac[8:0] <= 9'h07b;  //F6
+                         9'h00c:               keymac[8:0] <= 9'h07b;  //F4
+                         9'h00d:               keymac[8:0] <= 9'h061;  //TAB
+                         9'h00e:               keymac[8:0] <= 9'h065;  //~ (`)
+                         9'h00f:               keymac[8:0] <= 9'h07b;
+                         9'h010:               keymac[8:0] <= 9'h07b;
+                         9'h011:               keymac[8:0] <= 9'h075;  //LEFT ALT (option)
+                         9'h012:               keymac[8:0] <= 9'h071;  //LEFT SHIFT
+                         9'h013:               keymac[8:0] <= 9'h07b;
+                         9'h014:               keymac[8:0] <= 9'h06f;  //CTRL (command)
+                         9'h015:               keymac[8:0] <= 9'h019;  //q
+                         9'h016:               keymac[8:0] <= 9'h025;  //1
+                         9'h017:               keymac[8:0] <= 9'h07b;
+                         9'h018:               keymac[8:0] <= 9'h07b;
+                         9'h019:               keymac[8:0] <= 9'h07b;
+                         9'h01a:               keymac[8:0] <= 9'h00d;  //z
+                         9'h01b:               keymac[8:0] <= 9'h003;  //s
+                         9'h01c:               keymac[8:0] <= 9'h001;  //a
+                         9'h01d:               keymac[8:0] <= 9'h01b;  //w
+                         9'h01e:               keymac[8:0] <= 9'h027;  //2
+                         9'h01f:               keymac[8:0] <= 9'h07b;
+                         9'h020:               keymac[8:0] <= 9'h07b;
+                         9'h021:               keymac[8:0] <= 9'h011;  //c
+                         9'h022:               keymac[8:0] <= 9'h00f;  //x
+                         9'h023:               keymac[8:0] <= 9'h005;  //d
+                         9'h024:               keymac[8:0] <= 9'h01d;  //e
+                         9'h025:               keymac[8:0] <= 9'h02b;  //4
+                         9'h026:               keymac[8:0] <= 9'h029;  //3
+                         9'h027:               keymac[8:0] <= 9'h07b;
+                         9'h028:               keymac[8:0] <= 9'h07b;
+                         9'h029:               keymac[8:0] <= 9'h063;  //SPACE
+                         9'h02a:               keymac[8:0] <= 9'h013;  //v
+                         9'h02b:               keymac[8:0] <= 9'h007;  //f
+                         9'h02c:               keymac[8:0] <= 9'h023;  //t
+                         9'h02d:               keymac[8:0] <= 9'h01f;  //r
+                         9'h02e:               keymac[8:0] <= 9'h02f;  //5
+                         9'h02f:               keymac[8:0] <= 9'h07b;
+                         9'h030:               keymac[8:0] <= 9'h07b;
+                         9'h031:               keymac[8:0] <= 9'h05b;  //n
+                         9'h032:               keymac[8:0] <= 9'h017;  //b
+                         9'h033:               keymac[8:0] <= 9'h009;  //h
+                         9'h034:               keymac[8:0] <= 9'h00b;  //g
+                         9'h035:               keymac[8:0] <= 9'h021;  //y
+                         9'h036:               keymac[8:0] <= 9'h02d;  //6
+                         9'h037:               keymac[8:0] <= 9'h07b;
+                         9'h038:               keymac[8:0] <= 9'h07b;
+                         9'h039:               keymac[8:0] <= 9'h07b;
+                         9'h03a:               keymac[8:0] <= 9'h05d;  //m
+                         9'h03b:               keymac[8:0] <= 9'h04d;  //j
+                         9'h03c:               keymac[8:0] <= 9'h041;  //u
+                         9'h03d:               keymac[8:0] <= 9'h035;  //7
+                         9'h03e:               keymac[8:0] <= 9'h039;  //8
+                         9'h03f:               keymac[8:0] <= 9'h07b;
+                         9'h040:               keymac[8:0] <= 9'h07b;
+                         9'h041:               keymac[8:0] <= 9'h057;  //<,
+                         9'h042:               keymac[8:0] <= 9'h051;  //k
+                         9'h043:               keymac[8:0] <= 9'h045;  //i
+                         9'h044:               keymac[8:0] <= 9'h03f;  //o
+                         9'h045:               keymac[8:0] <= 9'h03b;  //0
+                         9'h046:               keymac[8:0] <= 9'h033;  //9
+                         9'h047:               keymac[8:0] <= 9'h07b;
+                         9'h048:               keymac[8:0] <= 9'h07b;
+                         9'h049:               keymac[8:0] <= 9'h05f;  //>.
+                         9'h04a:               keymac[8:0] <= 9'h059;  //FORWARD SLASH
+                         9'h04b:               keymac[8:0] <= 9'h04b;  //l
+                         9'h04c:               keymac[8:0] <= 9'h053;  //;
+                         9'h04d:               keymac[8:0] <= 9'h047;  //p
+                         9'h04e:               keymac[8:0] <= 9'h037;  //-
+                         9'h04f:               keymac[8:0] <= 9'h07b;
+                         9'h050:               keymac[8:0] <= 9'h07b;
+                         9'h051:               keymac[8:0] <= 9'h07b;
+                         9'h052:               keymac[8:0] <= 9'h04f;  //'"
+                         9'h053:               keymac[8:0] <= 9'h07b;
+                         9'h054:               keymac[8:0] <= 9'h043;  //[
+                         9'h055:               keymac[8:0] <= 9'h031;  // = 
+                         9'h056:               keymac[8:0] <= 9'h07b;
+                         9'h057:               keymac[8:0] <= 9'h07b;
+                         9'h058:               keymac[8:0] <= 9'h073;  //CAPSLOCK
+                         9'h059:               keymac[8:0] <= 9'h071;  //RIGHT SHIFT
+                         9'h05a:               keymac[8:0] <= 9'h049;  //ENTER
+                         9'h05b:               keymac[8:0] <= 9'h03d;  //]
+                         9'h05c:               keymac[8:0] <= 9'h07b;
+                         9'h05d:               keymac[8:0] <= 9'h055;  //BACKSLASH
+                         9'h05e:               keymac[8:0] <= 9'h07b;
+                         9'h05f:               keymac[8:0] <= 9'h07b;
+                         9'h060:               keymac[8:0] <= 9'h07b;
+                         9'h061:               keymac[8:0] <= 9'h071;  //international left shift cut out (German '<>' key), 0x56 Set#1 code
+                         9'h062:               keymac[8:0] <= 9'h07b;
+                         9'h063:               keymac[8:0] <= 9'h07b;
+                         9'h064:               keymac[8:0] <= 9'h07b;
+                         9'h065:               keymac[8:0] <= 9'h07b;
+                         9'h066:               keymac[8:0] <= 9'h067;  //BACKSPACE
+                         9'h067:               keymac[8:0] <= 9'h07b;
+                         9'h068:               keymac[8:0] <= 9'h07b;
+                         9'h069:               keymac[8:0] <= 9'h127;  //KP 1
+                         9'h06a:               keymac[8:0] <= 9'h07b;
+                         9'h06b:               keymac[8:0] <= 9'h12d;  //KP 4
+                         9'h06c:               keymac[8:0] <= 9'h133;  //KP 7
+                         9'h06d:               keymac[8:0] <= 9'h07b;
+                         9'h06e:               keymac[8:0] <= 9'h07b;
+                         9'h06f:               keymac[8:0] <= 9'h07b;
+                         9'h070:               keymac[8:0] <= 9'h125;  //KP 0
+                         9'h071:               keymac[8:0] <= 9'h103;  //KP .
+                         9'h072:               keymac[8:0] <= 9'h129;  //KP 2
+                         9'h073:               keymac[8:0] <= 9'h12f;  //KP 5
+                         9'h074:               keymac[8:0] <= 9'h131;  //KP 6
+                         9'h075:               keymac[8:0] <= 9'h137;  //KP 8
+                         9'h076:               keymac[8:0] <= 9'b000;  //ESCAPE
+                         9'h077:               keymac[8:0] <= 9'h07b;  //NUMLOCK
+                         9'h078:               keymac[8:0] <= 9'h07b;  //F11 <OSD>
+                         9'h079:               keymac[8:0] <= 9'h07b;  //KP +
+                         9'h07a:               keymac[8:0] <= 9'h12b;  //KP 3
+                         9'h07b:               keymac[8:0] <= 9'h11d;  //KP -
+                         9'h07c:               keymac[8:0] <= 9'h07b;  //KP *
+                         9'h07d:               keymac[8:0] <= 9'h139;  //KP 9
+                         9'h07e:               keymac[8:0] <= 9'h07b;  //SCROLL LOCK / KP )
+                         9'h07f:               keymac[8:0] <= 9'h07b;
+                         9'h080:               keymac[8:0] <= 9'h07b;
+                         9'h081:               keymac[8:0] <= 9'h07b;
+                         9'h082:               keymac[8:0] <= 9'h07b;
+                         9'h083:               keymac[8:0] <= 9'h07b;  //F7
+                         9'h084:               keymac[8:0] <= 9'h07b;
+                         9'h085:               keymac[8:0] <= 9'h07b;
+                         9'h086:               keymac[8:0] <= 9'h07b;
+                         9'h087:               keymac[8:0] <= 9'h07b;
+                         9'h088:               keymac[8:0] <= 9'h07b;
+                         9'h089:               keymac[8:0] <= 9'h07b;
+                         9'h08a:               keymac[8:0] <= 9'h07b;
+                         9'h08b:               keymac[8:0] <= 9'h07b;
+                         9'h08c:               keymac[8:0] <= 9'h07b;
+                         9'h08d:               keymac[8:0] <= 9'h07b;
+                         9'h08e:               keymac[8:0] <= 9'h07b;
+                         9'h08f:               keymac[8:0] <= 9'h07b;
+                         9'h090:               keymac[8:0] <= 9'h07b;
+                         9'h091:               keymac[8:0] <= 9'h07b;
+                         9'h092:               keymac[8:0] <= 9'h07b;
+                         9'h093:               keymac[8:0] <= 9'h07b;
+                         9'h094:               keymac[8:0] <= 9'h07b;
+                         9'h095:               keymac[8:0] <= 9'h07b;
+                         9'h096:               keymac[8:0] <= 9'h07b;
+                         9'h097:               keymac[8:0] <= 9'h07b;
+                         9'h098:               keymac[8:0] <= 9'h07b;
+                         9'h099:               keymac[8:0] <= 9'h07b;
+                         9'h09a:               keymac[8:0] <= 9'h07b;
+                         9'h09b:               keymac[8:0] <= 9'h07b;
+                         9'h09c:               keymac[8:0] <= 9'h07b;
+                         9'h09d:               keymac[8:0] <= 9'h07b;
+                         9'h09e:               keymac[8:0] <= 9'h07b;
+                         9'h09f:               keymac[8:0] <= 9'h07b;
+                         9'h0a0:               keymac[8:0] <= 9'h07b;
+                         9'h0a1:               keymac[8:0] <= 9'h07b;
+                         9'h0a2:               keymac[8:0] <= 9'h07b;
+                         9'h0a3:               keymac[8:0] <= 9'h07b;
+                         9'h0a4:               keymac[8:0] <= 9'h07b;
+                         9'h0a5:               keymac[8:0] <= 9'h07b;
+                         9'h0a6:               keymac[8:0] <= 9'h07b;
+                         9'h0a7:               keymac[8:0] <= 9'h07b;
+                         9'h0a8:               keymac[8:0] <= 9'h07b;
+                         9'h0a9:               keymac[8:0] <= 9'h07b;
+                         9'h0aa:               keymac[8:0] <= 9'h07b;
+                         9'h0ab:               keymac[8:0] <= 9'h07b;
+                         9'h0ac:               keymac[8:0] <= 9'h07b;
+                         9'h0ad:               keymac[8:0] <= 9'h07b;
+                         9'h0ae:               keymac[8:0] <= 9'h07b;
+                         9'h0af:               keymac[8:0] <= 9'h07b;
+                         9'h0b0:               keymac[8:0] <= 9'h07b;
+                         9'h0b1:               keymac[8:0] <= 9'h07b;
+                         9'h0b2:               keymac[8:0] <= 9'h07b;
+                         9'h0b3:               keymac[8:0] <= 9'h07b;
+                         9'h0b4:               keymac[8:0] <= 9'h07b;
+                         9'h0b5:               keymac[8:0] <= 9'h07b;
+                         9'h0b6:               keymac[8:0] <= 9'h07b;
+                         9'h0b7:               keymac[8:0] <= 9'h07b;
+                         9'h0b8:               keymac[8:0] <= 9'h07b;
+                         9'h0b9:               keymac[8:0] <= 9'h07b;
+                         9'h0ba:               keymac[8:0] <= 9'h07b;
+                         9'h0bb:               keymac[8:0] <= 9'h07b;
+                         9'h0bc:               keymac[8:0] <= 9'h07b;
+                         9'h0bd:               keymac[8:0] <= 9'h07b;
+                         9'h0be:               keymac[8:0] <= 9'h07b;
+                         9'h0bf:               keymac[8:0] <= 9'h07b;
+                         9'h0c0:               keymac[8:0] <= 9'h07b;
+                         9'h0c1:               keymac[8:0] <= 9'h07b;
+                         9'h0c2:               keymac[8:0] <= 9'h07b;
+                         9'h0c3:               keymac[8:0] <= 9'h07b;
+                         9'h0c4:               keymac[8:0] <= 9'h07b;
+                         9'h0c5:               keymac[8:0] <= 9'h07b;
+                         9'h0c6:               keymac[8:0] <= 9'h07b;
+                         9'h0c7:               keymac[8:0] <= 9'h07b;
+                         9'h0c8:               keymac[8:0] <= 9'h07b;
+                         9'h0c9:               keymac[8:0] <= 9'h07b;
+                         9'h0ca:               keymac[8:0] <= 9'h07b;
+                         9'h0cb:               keymac[8:0] <= 9'h07b;
+                         9'h0cc:               keymac[8:0] <= 9'h07b;
+                         9'h0cd:               keymac[8:0] <= 9'h07b;
+                         9'h0ce:               keymac[8:0] <= 9'h07b;
+                         9'h0cf:               keymac[8:0] <= 9'h07b;
+                         9'h0d0:               keymac[8:0] <= 9'h07b;
+                         9'h0d1:               keymac[8:0] <= 9'h07b;
+                         9'h0d2:               keymac[8:0] <= 9'h07b;
+                         9'h0d3:               keymac[8:0] <= 9'h07b;
+                         9'h0d4:               keymac[8:0] <= 9'h07b;
+                         9'h0d5:               keymac[8:0] <= 9'h07b;
+                         9'h0d6:               keymac[8:0] <= 9'h07b;
+                         9'h0d7:               keymac[8:0] <= 9'h07b;
+                         9'h0d8:               keymac[8:0] <= 9'h07b;
+                         9'h0d9:               keymac[8:0] <= 9'h07b;
+                         9'h0da:               keymac[8:0] <= 9'h07b;
+                         9'h0db:               keymac[8:0] <= 9'h07b;
+                         9'h0dc:               keymac[8:0] <= 9'h07b;
+                         9'h0dd:               keymac[8:0] <= 9'h07b;
+                         9'h0de:               keymac[8:0] <= 9'h07b;
+                         9'h0df:               keymac[8:0] <= 9'h07b;
+                         9'h0e0:               keymac[8:0] <= 9'h07b;  //ps2 extended key
+                         9'h0e1:               keymac[8:0] <= 9'h07b;
+                         9'h0e2:               keymac[8:0] <= 9'h07b;
+                         9'h0e3:               keymac[8:0] <= 9'h07b;
+                         9'h0e4:               keymac[8:0] <= 9'h07b;
+                         9'h0e5:               keymac[8:0] <= 9'h07b;
+                         9'h0e6:               keymac[8:0] <= 9'h07b;
+                         9'h0e7:               keymac[8:0] <= 9'h07b;
+                         9'h0e8:               keymac[8:0] <= 9'h07b;
+                         9'h0e9:               keymac[8:0] <= 9'h07b;
+                         9'h0ea:               keymac[8:0] <= 9'h07b;
+                         9'h0eb:               keymac[8:0] <= 9'h07b;
+                         9'h0ec:               keymac[8:0] <= 9'h07b;
+                         9'h0ed:               keymac[8:0] <= 9'h07b;
+                         9'h0ee:               keymac[8:0] <= 9'h07b;
+                         9'h0ef:               keymac[8:0] <= 9'h07b;
+                         9'h0f0:               keymac[8:0] <= 9'h07b;  //ps2 release code
+                         9'h0f1:               keymac[8:0] <= 9'h07b;
+                         9'h0f2:               keymac[8:0] <= 9'h07b;
+                         9'h0f3:               keymac[8:0] <= 9'h07b;
+                         9'h0f4:               keymac[8:0] <= 9'h07b;
+                         9'h0f5:               keymac[8:0] <= 9'h07b;
+                         9'h0f6:               keymac[8:0] <= 9'h07b;
+                         9'h0f7:               keymac[8:0] <= 9'h07b;
+                         9'h0f8:               keymac[8:0] <= 9'h07b;
+                         9'h0f9:               keymac[8:0] <= 9'h07b;
+                         9'h0fa:               keymac[8:0] <= 9'h07b;  //ps2 ack code
+                         9'h0fb:               keymac[8:0] <= 9'h07b;
+                         9'h0fc:               keymac[8:0] <= 9'h07b;
+                         9'h0fd:               keymac[8:0] <= 9'h07b;
+                         9'h0fe:               keymac[8:0] <= 9'h07b;
+                         9'h0ff:               keymac[8:0] <= 9'h07b;
+                         9'h100:               keymac[8:0] <= 9'h07b;
+                         9'h101:               keymac[8:0] <= 9'h07b;
+                         9'h102:               keymac[8:0] <= 9'h07b;
+                         9'h103:               keymac[8:0] <= 9'h07b;
+                         9'h104:               keymac[8:0] <= 9'h07b;
+                         9'h105:               keymac[8:0] <= 9'h07b;
+                         9'h106:               keymac[8:0] <= 9'h07b;
+                         9'h107:               keymac[8:0] <= 9'h07b;
+                         9'h108:               keymac[8:0] <= 9'h07b;
+                         9'h109:               keymac[8:0] <= 9'h07b;
+                         9'h10a:               keymac[8:0] <= 9'h07b;
+                         9'h10b:               keymac[8:0] <= 9'h07b;
+                         9'h10c:               keymac[8:0] <= 9'h07b;
+                         9'h10d:               keymac[8:0] <= 9'h07b;
+                         9'h10e:               keymac[8:0] <= 9'h07b;
+                         9'h10f:               keymac[8:0] <= 9'h07b;
+                         9'h110:               keymac[8:0] <= 9'h07b;
+                         9'h111:               keymac[8:0] <= 9'h075;  //RIGHT ALT
+                         9'h112:               keymac[8:0] <= 9'h07b;
+                         9'h113:               keymac[8:0] <= 9'h07b;
+                         9'h114:               keymac[8:0] <= 9'h07b;
+                         9'h115:               keymac[8:0] <= 9'h07b;
+                         9'h116:               keymac[8:0] <= 9'h07b;
+                         9'h117:               keymac[8:0] <= 9'h07b;
+                         9'h118:               keymac[8:0] <= 9'h07b;
+                         9'h119:               keymac[8:0] <= 9'h07b;
+                         9'h11a:               keymac[8:0] <= 9'h07b;
+                         9'h11b:               keymac[8:0] <= 9'h07b;
+                         9'h11c:               keymac[8:0] <= 9'h07b;
+                         9'h11d:               keymac[8:0] <= 9'h07b;
+                         9'h11e:               keymac[8:0] <= 9'h07b;
+                         9'h11f:               keymac[8:0] <= 9'h06f;  //LEFT COMMAND (LEFT GUI)
+                         9'h120:               keymac[8:0] <= 9'h07b;
+                         9'h121:               keymac[8:0] <= 9'h07b;
+                         9'h122:               keymac[8:0] <= 9'h07b;
+                         9'h123:               keymac[8:0] <= 9'h07b;
+                         9'h124:               keymac[8:0] <= 9'h07b;
+                         9'h125:               keymac[8:0] <= 9'h07b;
+                         9'h126:               keymac[8:0] <= 9'h07b;
+                         9'h127:               keymac[8:0] <= 9'h06f;  //RIGHT AMIGA (RIGHT GUI)
+                         9'h128:               keymac[8:0] <= 9'h07b;
+                         9'h129:               keymac[8:0] <= 9'h07b;
+                         9'h12a:               keymac[8:0] <= 9'h07b;
+                         9'h12b:               keymac[8:0] <= 9'h07b;
+                         9'h12c:               keymac[8:0] <= 9'h07b;
+                         9'h12d:               keymac[8:0] <= 9'h07b;
+                         9'h12e:               keymac[8:0] <= 9'h07b;
+                         9'h12f:               keymac[8:0] <= 9'h06f;  //RIGHT AMIGA (APPS)
+                         9'h130:               keymac[8:0] <= 9'h07b;
+                         9'h131:               keymac[8:0] <= 9'h07b;
+                         9'h132:               keymac[8:0] <= 9'h07b;
+                         9'h133:               keymac[8:0] <= 9'h07b;
+                         9'h134:               keymac[8:0] <= 9'h07b;
+                         9'h135:               keymac[8:0] <= 9'h07b;
+                         9'h136:               keymac[8:0] <= 9'h07b;
+                         9'h137:               keymac[8:0] <= 9'h07b;
+                         9'h138:               keymac[8:0] <= 9'h07b;
+                         9'h139:               keymac[8:0] <= 9'h07b;
+                         9'h13a:               keymac[8:0] <= 9'h07b;
+                         9'h13b:               keymac[8:0] <= 9'h07b;
+                         9'h13c:               keymac[8:0] <= 9'h07b;
+                         9'h13d:               keymac[8:0] <= 9'h07b;
+                         9'h13e:               keymac[8:0] <= 9'h07b;
+                         9'h13f:               keymac[8:0] <= 9'h07b;
+                         9'h140:               keymac[8:0] <= 9'h07b;
+                         9'h141:               keymac[8:0] <= 9'h07b;
+                         9'h142:               keymac[8:0] <= 9'h07b;
+                         9'h143:               keymac[8:0] <= 9'h07b;
+                         9'h144:               keymac[8:0] <= 9'h07b;
+                         9'h145:               keymac[8:0] <= 9'h07b;
+                         9'h146:               keymac[8:0] <= 9'h07b;
+                         9'h147:               keymac[8:0] <= 9'h07b;
+                         9'h148:               keymac[8:0] <= 9'h07b;
+                         9'h149:               keymac[8:0] <= 9'h07b;
+                         9'h14a:               keymac[8:0] <= 9'h07b;  //KP /
+                         9'h14b:               keymac[8:0] <= 9'h07b;
+                         9'h14c:               keymac[8:0] <= 9'h07b;
+                         9'h14d:               keymac[8:0] <= 9'h07b;
+                         9'h14e:               keymac[8:0] <= 9'h07b;
+                         9'h14f:               keymac[8:0] <= 9'h07b;
+                         9'h150:               keymac[8:0] <= 9'h07b;
+                         9'h151:               keymac[8:0] <= 9'h07b;
+                         9'h152:               keymac[8:0] <= 9'h07b;
+                         9'h153:               keymac[8:0] <= 9'h07b;
+                         9'h154:               keymac[8:0] <= 9'h07b;
+                         9'h155:               keymac[8:0] <= 9'h07b;
+                         9'h156:               keymac[8:0] <= 9'h07b;
+                         9'h157:               keymac[8:0] <= 9'h07b;
+                         9'h158:               keymac[8:0] <= 9'h07b;
+                         9'h159:               keymac[8:0] <= 9'h07b;
+                         9'h15a:               keymac[8:0] <= 9'h119;  //KP ENTER
+                         9'h15b:               keymac[8:0] <= 9'h07b;
+                         9'h15c:               keymac[8:0] <= 9'h07b;
+                         9'h15d:               keymac[8:0] <= 9'h07b;
+                         9'h15e:               keymac[8:0] <= 9'h07b;
+                         9'h15f:               keymac[8:0] <= 9'h07b;
+                         9'h160:               keymac[8:0] <= 9'h07b;
+                         9'h161:               keymac[8:0] <= 9'h07b;
+                         9'h162:               keymac[8:0] <= 9'h07b;
+                         9'h163:               keymac[8:0] <= 9'h07b;
+                         9'h164:               keymac[8:0] <= 9'h07b;
+                         9'h165:               keymac[8:0] <= 9'h07b;
+                         9'h166:               keymac[8:0] <= 9'h07b;
+                         9'h167:               keymac[8:0] <= 9'h07b;
+                         9'h168:               keymac[8:0] <= 9'h07b;
+                         9'h169:               keymac[8:0] <= 9'h07b;  //END
+                         9'h16a:               keymac[8:0] <= 9'h07b;
+                         9'h16b:               keymac[8:0] <= 9'h10d;  //ARROW LEFT
+                         9'h16c:               keymac[8:0] <= 9'h07b;  //HOME
+                         9'h16d:               keymac[8:0] <= 9'h07b;
+                         9'h16e:               keymac[8:0] <= 9'h07b;
+                         9'h16f:               keymac[8:0] <= 9'h07b;
+                         9'h170:               keymac[8:0] <= 9'h07b;  //INSERT = HELP
+                         9'h171:               keymac[8:0] <= 9'h10f;  //DELETE
+                         9'h172:               keymac[8:0] <= 9'h111;  //ARROW DOWN
+                         9'h173:               keymac[8:0] <= 9'h07b;
+                         9'h174:               keymac[8:0] <= 9'h105;  //ARROW RIGHT
+                         9'h175:               keymac[8:0] <= 9'h10b;  //ARROW UP
+                         9'h176:               keymac[8:0] <= 9'h07b;
+                         9'h177:               keymac[8:0] <= 9'h07b;
+                         9'h178:               keymac[8:0] <= 9'h07b;
+                         9'h179:               keymac[8:0] <= 9'h07b;
+                         9'h17a:               keymac[8:0] <= 9'h07b;  //PGDN <OSD>
+                         9'h17b:               keymac[8:0] <= 9'h07b;
+                         9'h17c:               keymac[8:0] <= 9'h07b;  //PRTSCR <OSD>
+                         9'h17d:               keymac[8:0] <= 9'h07b;  //PGUP <OSD>
+                         9'h17e:               keymac[8:0] <= 9'h07b;  //ctrl+break
+                         9'h17f:               keymac[8:0] <= 9'h07b;
+                         9'h180:               keymac[8:0] <= 9'h07b;
+                         9'h181:               keymac[8:0] <= 9'h07b;
+                         9'h182:               keymac[8:0] <= 9'h07b;
+                         9'h183:               keymac[8:0] <= 9'h07b;
+                         9'h184:               keymac[8:0] <= 9'h07b;
+                         9'h185:               keymac[8:0] <= 9'h07b;
+                         9'h186:               keymac[8:0] <= 9'h07b;
+                         9'h187:               keymac[8:0] <= 9'h07b;
+                         9'h188:               keymac[8:0] <= 9'h07b;
+                         9'h189:               keymac[8:0] <= 9'h07b;
+                         9'h18a:               keymac[8:0] <= 9'h07b;
+                         9'h18b:               keymac[8:0] <= 9'h07b;
+                         9'h18c:               keymac[8:0] <= 9'h07b;
+                         9'h18d:               keymac[8:0] <= 9'h07b;
+                         9'h18e:               keymac[8:0] <= 9'h07b;
+                         9'h18f:               keymac[8:0] <= 9'h07b;
+                         9'h190:               keymac[8:0] <= 9'h07b;
+                         9'h191:               keymac[8:0] <= 9'h07b;
+                         9'h192:               keymac[8:0] <= 9'h07b;
+                         9'h193:               keymac[8:0] <= 9'h07b;
+                         9'h194:               keymac[8:0] <= 9'h07b;
+                         9'h195:               keymac[8:0] <= 9'h07b;
+                         9'h196:               keymac[8:0] <= 9'h07b;
+                         9'h197:               keymac[8:0] <= 9'h07b;
+                         9'h198:               keymac[8:0] <= 9'h07b;
+                         9'h199:               keymac[8:0] <= 9'h07b;
+                         9'h19a:               keymac[8:0] <= 9'h07b;
+                         9'h19b:               keymac[8:0] <= 9'h07b;
+                         9'h19c:               keymac[8:0] <= 9'h07b;
+                         9'h19d:               keymac[8:0] <= 9'h07b;
+                         9'h19e:               keymac[8:0] <= 9'h07b;
+                         9'h19f:               keymac[8:0] <= 9'h07b;
+                         9'h1a0:               keymac[8:0] <= 9'h07b;
+                         9'h1a1:               keymac[8:0] <= 9'h07b;
+                         9'h1a2:               keymac[8:0] <= 9'h07b;
+                         9'h1a3:               keymac[8:0] <= 9'h07b;
+                         9'h1a4:               keymac[8:0] <= 9'h07b;
+                         9'h1a5:               keymac[8:0] <= 9'h07b;
+                         9'h1a6:               keymac[8:0] <= 9'h07b;
+                         9'h1a7:               keymac[8:0] <= 9'h07b;
+                         9'h1a8:               keymac[8:0] <= 9'h07b;
+                         9'h1a9:               keymac[8:0] <= 9'h07b;
+                         9'h1aa:               keymac[8:0] <= 9'h07b;
+                         9'h1ab:               keymac[8:0] <= 9'h07b;
+                         9'h1ac:               keymac[8:0] <= 9'h07b;
+                         9'h1ad:               keymac[8:0] <= 9'h07b;
+                         9'h1ae:               keymac[8:0] <= 9'h07b;
+                         9'h1af:               keymac[8:0] <= 9'h07b;
+                         9'h1b0:               keymac[8:0] <= 9'h07b;
+                         9'h1b1:               keymac[8:0] <= 9'h07b;
+                         9'h1b2:               keymac[8:0] <= 9'h07b;
+                         9'h1b3:               keymac[8:0] <= 9'h07b;
+                         9'h1b4:               keymac[8:0] <= 9'h07b;
+                         9'h1b5:               keymac[8:0] <= 9'h07b;
+                         9'h1b6:               keymac[8:0] <= 9'h07b;
+                         9'h1b7:               keymac[8:0] <= 9'h07b;
+                         9'h1b8:               keymac[8:0] <= 9'h07b;
+                         9'h1b9:               keymac[8:0] <= 9'h07b;
+                         9'h1ba:               keymac[8:0] <= 9'h07b;
+                         9'h1bb:               keymac[8:0] <= 9'h07b;
+                         9'h1bc:               keymac[8:0] <= 9'h07b;
+                         9'h1bd:               keymac[8:0] <= 9'h07b;
+                         9'h1be:               keymac[8:0] <= 9'h07b;
+                         9'h1bf:               keymac[8:0] <= 9'h07b;
+                         9'h1c0:               keymac[8:0] <= 9'h07b;
+                         9'h1c1:               keymac[8:0] <= 9'h07b;
+                         9'h1c2:               keymac[8:0] <= 9'h07b;
+                         9'h1c3:               keymac[8:0] <= 9'h07b;
+                         9'h1c4:               keymac[8:0] <= 9'h07b;
+                         9'h1c5:               keymac[8:0] <= 9'h07b;
+                         9'h1c6:               keymac[8:0] <= 9'h07b;
+                         9'h1c7:               keymac[8:0] <= 9'h07b;
+                         9'h1c8:               keymac[8:0] <= 9'h07b;
+                         9'h1c9:               keymac[8:0] <= 9'h07b;
+                         9'h1ca:               keymac[8:0] <= 9'h07b;
+                         9'h1cb:               keymac[8:0] <= 9'h07b;
+                         9'h1cc:               keymac[8:0] <= 9'h07b;
+                         9'h1cd:               keymac[8:0] <= 9'h07b;
+                         9'h1ce:               keymac[8:0] <= 9'h07b;
+                         9'h1cf:               keymac[8:0] <= 9'h07b;
+                         9'h1d0:               keymac[8:0] <= 9'h07b;
+                         9'h1d1:               keymac[8:0] <= 9'h07b;
+                         9'h1d2:               keymac[8:0] <= 9'h07b;
+                         9'h1d3:               keymac[8:0] <= 9'h07b;
+                         9'h1d4:               keymac[8:0] <= 9'h07b;
+                         9'h1d5:               keymac[8:0] <= 9'h07b;
+                         9'h1d6:               keymac[8:0] <= 9'h07b;
+                         9'h1d7:               keymac[8:0] <= 9'h07b;
+                         9'h1d8:               keymac[8:0] <= 9'h07b;
+                         9'h1d9:               keymac[8:0] <= 9'h07b;
+                         9'h1da:               keymac[8:0] <= 9'h07b;
+                         9'h1db:               keymac[8:0] <= 9'h07b;
+                         9'h1dc:               keymac[8:0] <= 9'h07b;
+                         9'h1dd:               keymac[8:0] <= 9'h07b;
+                         9'h1de:               keymac[8:0] <= 9'h07b;
+                         9'h1df:               keymac[8:0] <= 9'h07b;
+                         9'h1e0:               keymac[8:0] <= 9'h07b;  //ps2 extended key(duplicate, see $e0)
+                         9'h1e1:               keymac[8:0] <= 9'h07b;
+                         9'h1e2:               keymac[8:0] <= 9'h07b;
+                         9'h1e3:               keymac[8:0] <= 9'h07b;
+                         9'h1e4:               keymac[8:0] <= 9'h07b;
+                         9'h1e5:               keymac[8:0] <= 9'h07b;
+                         9'h1e6:               keymac[8:0] <= 9'h07b;
+                         9'h1e7:               keymac[8:0] <= 9'h07b;
+                         9'h1e8:               keymac[8:0] <= 9'h07b;
+                         9'h1e9:               keymac[8:0] <= 9'h07b;
+                         9'h1ea:               keymac[8:0] <= 9'h07b;
+                         9'h1eb:               keymac[8:0] <= 9'h07b;
+                         9'h1ec:               keymac[8:0] <= 9'h07b;
+                         9'h1ed:               keymac[8:0] <= 9'h07b;
+                         9'h1ee:               keymac[8:0] <= 9'h07b;
+                         9'h1ef:               keymac[8:0] <= 9'h07b;
+                         9'h1f0:               keymac[8:0] <= 9'h07b;  //ps2 release code(duplicate, see $f0)
+                         9'h1f1:               keymac[8:0] <= 9'h07b;
+                         9'h1f2:               keymac[8:0] <= 9'h07b;
+                         9'h1f3:               keymac[8:0] <= 9'h07b;
+                         9'h1f4:               keymac[8:0] <= 9'h07b;
+                         9'h1f5:               keymac[8:0] <= 9'h07b;
+                         9'h1f6:               keymac[8:0] <= 9'h07b;
+                         9'h1f7:               keymac[8:0] <= 9'h07b;
+                         9'h1f8:               keymac[8:0] <= 9'h07b;
+                         9'h1f9:               keymac[8:0] <= 9'h07b;
+                         9'h1fa:               keymac[8:0] <= 9'h07b;  //ps2 ack code(duplicate see $fa)
+                         9'h1fb:               keymac[8:0] <= 9'h07b;
+                         9'h1fc:               keymac[8:0] <= 9'h07b;
+                         9'h1fd:               keymac[8:0] <= 9'h07b;
+                         9'h1fe:               keymac[8:0] <= 9'h07b;
+                         9'h1ff:               keymac[8:0] <= 9'h07b;
+                 endcase // case ({extended,ps2key[7:0]})
+                 keymac[7] <= keybreak;
+         end
+
+       /* Some debug signals for my own sanity */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 debug <= 0;
+               else begin
+                       if (istrobe)
+                         debug[15:8] <= ibyte;
+                       debug[7:0] <= { ps2clk, ps2dat, dbg_lowstate, 2'b0,
+                                       state };
+               end
+       end
+endmodule
diff --git a/fpga/ps2_mouse.v b/fpga/ps2_mouse.v
new file mode 100644 (file)
index 0000000..0a04f0a
--- /dev/null
@@ -0,0 +1,239 @@
+`timescale 1ns / 100ps
+
+/*
+ * PS2 Mouse to Mac interface module
+ */
+module ps2_mouse(input sysclk,
+                input  reset,
+
+                inout  ps2dat,
+                inout  ps2clk,
+                
+                output reg x1,
+                output reg y1,
+                output reg x2,
+                output reg y2,
+                output reg button,
+                
+                output reg [15:0] debug
+);
+       wire            istrobe;
+       wire [7:0]      ibyte;
+       wire            timeout;
+       wire            oack;
+       reg [7:0]       obyte;
+       reg             oreq;
+       reg [2:0]       state;
+       reg [2:0]       next;
+       reg [7:0]       nbyte;
+       reg             nreq;
+       reg [9:0]       xacc;
+       reg [9:0]       yacc;
+       reg             xsign;
+       reg             ysign;
+       reg [11:0]      clkdiv;
+       wire            tick;   
+       wire[1:0]       dbg_lowstate;
+       
+       ps2 ps20(.sysclk(sysclk),
+                .reset(reset),
+                .ps2dat(ps2dat),
+                .ps2clk(ps2clk),
+                .istrobe(istrobe),
+                .ibyte(ibyte),
+                .oreq(oreq),
+                .obyte(obyte),
+                .oack(oack),
+                .timeout(timeout),
+                .dbg_state(dbg_lowstate));
+       
+       /* State machine:
+        *
+        *  - at state_init: wait for BAT reply
+        *     * 0xaa                   -> state_id
+        *     * 0xfa      -> send 0xff -> state_init
+        *     * bad reply -> send 0xff -> state_init
+        *     * timeout   -> send 0xff -> state_init
+        * 
+        *  - at state_id: wait for device_id
+        *     * 0x00      -> send 0xf4 -> state_setup
+        *     * bad reply -> send 0xff -> state_init
+        *     * timeout   -> send 0xff -> state_init
+        * 
+        *  - at state_setup: wait for enable data reporting ack
+        *     * 0xfa                   -> state_byte0
+        *     * bad reply -> send 0xff -> state_init
+        *     * timeout   -> send 0xff -> state_init
+        * 
+        *  - at state_byte0: wait for data byte 0
+        *     * data                   -> state_byte1
+        *     * data                   -> state_byte1
+        *     * timeout   -> send 0xff -> state_init
+        * 
+        *  - at state_byte1: wait for data byte 1
+        *     * data                   -> state_byte2
+        *     * timeout   -> send 0xff -> state_init
+        *
+        *  - at state_byte2: wait for data byte 2
+        *     * data                   -> state_byte0
+        *     * timeout   -> send 0xff -> state_init
+        */
+       localparam ps2m_state_init      = 0;
+       localparam ps2m_state_id        = 1;
+       localparam ps2m_state_setup     = 2;
+       localparam ps2m_state_byte0     = 3;
+       localparam ps2m_state_byte1     = 4;
+       localparam ps2m_state_byte2     = 5;
+
+       /* Unlike my other modules, here I'll play with a big fat
+        * combo logic. The outputs are:
+        *  - oreq   : triggers sending of a byte. Set based on either
+        *             timeout or istrobe, and as such only set for a
+        *             clock.
+        *  - next   : next state
+        *  - obyte  : next byte to send
+        */
+       always@(timeout or state or istrobe or ibyte) begin
+               nreq = 0;
+               next = state;
+               nbyte = 8'hff;
+               if (timeout) begin
+                       next = ps2m_state_init;
+                       nreq = 1;                       
+               end else if (istrobe)
+                 case(state)
+                         ps2m_state_init: begin
+                                 if (ibyte == 8'haa)
+                                   next = ps2m_state_id;
+                                 else if (ibyte != 8'hfa)
+                                   nreq = 1;
+                         end
+                         ps2m_state_id: begin
+                                 nreq = 1;
+                                 if (ibyte == 8'h00) begin
+                                         nbyte = 8'hf4;
+                                         next = ps2m_state_setup;
+                                 end else
+                                   next = ps2m_state_init;
+                         end
+                         ps2m_state_setup: begin
+                                 if (ibyte == 8'hfa)
+                                   next = ps2m_state_byte0;
+                                 else begin
+                                         nreq = 1;
+                                         next = ps2m_state_init;
+                                 end
+                         end
+                         ps2m_state_byte0: next = ps2m_state_byte1;
+                         ps2m_state_byte1: next = ps2m_state_byte2;
+                         ps2m_state_byte2: next = ps2m_state_byte0;
+                 endcase
+       end
+
+       /* State related latches. We latch oreq and obyte, we don't
+        * necessarily have to but that avoids back to back
+        * receive/send at the low level which can upset things
+        */
+       always@(posedge sysclk or posedge reset)
+               if (reset)
+                 state <= ps2m_state_init;
+               else
+                 state <= next;
+       always@(posedge sysclk or posedge reset)
+               if (reset)
+                 oreq <= 0;
+               else
+                 oreq <= nreq;
+       always@(posedge sysclk or posedge reset)
+               if (reset)
+                 obyte <= 0;
+               else
+                 obyte <= nbyte;
+
+       /* Capture button state */
+       always@(posedge sysclk or posedge reset)
+               if (reset)
+                 button <= 1;
+               else if (istrobe && state == ps2m_state_byte0)
+                 button <= ~ibyte[0];          
+
+       /* Clock divider to flush accumulators */
+       always@(posedge sysclk or posedge reset)
+               if (reset)
+                 clkdiv <= 0;
+               else
+                 clkdiv <= clkdiv + 1;
+       assign tick = clkdiv == 0;
+
+       /* Toggle output lines base on accumulator */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       x1 <= 0;
+                       x2 <= 0;
+               end else if (tick && xacc != 0) begin
+                       x1 <= ~x1;
+                       x2 <= ~x1 ^ ~xacc[9];
+               end
+       end
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       y1 <= 0;
+                       y2 <= 0;
+               end else if (tick && yacc != 0) begin
+                       y1 <= ~y1;
+                       y2 <= ~y1 ^ ~yacc[9];
+               end
+       end
+
+       /* Capture sign bits */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       xsign <= 0;
+                       ysign <= 0;                     
+               end else if (istrobe && state == ps2m_state_byte0) begin
+                       xsign <= ibyte[4];
+                       ysign <= ibyte[5];
+               end
+       end
+
+       /* Movement accumulators. Needs tuning ! */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 xacc <= 0;
+               else begin
+                       /* Add movement, convert to a 10-bit number if not over */
+                       if (istrobe && state == ps2m_state_byte1 && xacc[8] == xacc[9])
+                               xacc <= xacc + { xsign, xsign, ibyte };
+                       else
+                         /* Decrement */
+                         if (tick && xacc != 0)
+                           xacc <= xacc + { {9{~xsign}}, 1'b1 };
+               end
+       end
+       
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 yacc <= 0;
+               else begin
+                       /* Add movement, convert to a 10-bit number if not over*/
+                       if (istrobe && state == ps2m_state_byte2 && yacc[8] == yacc[9])
+                               yacc <= yacc + { ysign, ysign, ibyte };
+                       else
+                         /* Decrement */
+                         if (tick && yacc != 0)
+                           yacc <= yacc + { {9{~ysign}}, 1'b1 };
+               end
+       end
+
+       /* Some debug signals for my own sanity */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 debug <= 0;
+               else begin
+                       if (istrobe)
+                         debug[15:8] <= ibyte;
+                       debug[7:0] <= { ps2clk, ps2dat, dbg_lowstate, 1'b0,
+                                       state };
+               end
+       end
+endmodule
diff --git a/fpga/rtc.v b/fpga/rtc.v
new file mode 100644 (file)
index 0000000..1928562
--- /dev/null
@@ -0,0 +1,180 @@
+/* Mac Plus real time clock
+ *
+ * Doesn't do much yet ... eventually needs a backbus interface
+ * to save/restore PRAM to the PIC. Not much I can do about the
+ * actual RTC tho.
+ * 
+ *
+ * NOTE:
+ * 
+ * The HW ref manual seems to indicate that the RTC chip latches
+ * on raising clock but SW should latch on falling clock. However
+ * that isn't what the ROM does.
+ * 
+ * The actual transfer done by the ROM looks like:
+ * 
+ *  * Byte send:
+ *     - Direction set to output, value 111 (disabled, clock & data high)
+ *     - Set enabled and clock to 0
+ *     8 times: - Set value bit and clock to 0
+ *              - Set clock to 1
+ * 
+ * So the transfer "completes" with clock high after sending the last bit,
+ * the next transfer is then either a read or a write. In the later case,
+ * the ROM goes back to the same routine above, which means it continues
+ * clocking bits without a significant "hickup".
+ * 
+ * For a read however, what happens next is:
+ * 
+ *    - Set direction to input (at this point clock is still high)
+ *    - Clear data (no effect as direction is input) and clock
+ *    8 times: - Set clock to 0
+ *             - Set clock to 1
+ *             - Read bit
+ *    - Set direction to output
+ * 
+ * Finally disable chip.
+ * 
+ * You will notice that this is quite bogus since there's no way
+ * for the RTC chip to know after a read when to stop driving the
+ * data line before the Mac sets the direction back to output,
+ * so there -will- be some contention... oops. This isn't a problem
+ * for us though as we use separate signals.
+ */
+
+module rtc(input               sysclk,
+          input                reset,
+          output reg           onesec, 
+          input                data_in,
+          output               data_out,
+          input                data_clock,
+          input                _data_enable);
+
+       parameter divider = 23'h7a1200;
+                          
+       /* Sysclk is 16Mhz, to generate a 1-sec pulse, we need
+        * a 23-bit counter
+        */
+       reg [22:0]              cnt;
+       reg                     oldclk;
+       wire                    dstrobe;
+       wire                    gotbyte;
+       reg [7:0]               rdata;  
+       reg [7:0]               shift;
+       reg [7:0]               addr;
+       reg                     dir;
+       reg                     ckaddr;
+       reg [2:0]               bitcnt;
+       
+       localparam rtc_state_cmd        = 0;
+       localparam rtc_state_read       = 1;
+       localparam rtc_state_write      = 2;
+       localparam rtc_state_cmd2       = 3;
+       reg [1:0]               state;
+
+       /* Generate one second tick */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       cnt <= divider;
+                       onesec <= 0;
+               end else begin
+                       if (cnt == 0) begin
+                               onesec <= ~onesec;
+                               cnt <= divider;
+                       end else
+                         cnt <= cnt - 1;
+               end
+       end
+
+       /* Data clock edge detect buffer */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 oldclk <= 1;
+               else
+                 oldclk <= data_clock;
+       end
+
+       assign dstrobe = data_clock & ~oldclk & ~_data_enable;
+
+`ifdef __foo__ 
+       /* Shift bit counter */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       bitcnt <= 7;
+               end else begin
+                       if (_data_enable)
+                         bitcnt <= 7;
+                       else if (dstrobe)
+                         bitcnt <= bitcnt - 1;
+               end
+       end
+       assign gotbyte = (bitcnt == 0) && dstrobe;      
+
+       /* Shift register */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                       shift <= 0;
+               else begin
+                       if (_data_enable)
+                         shift <= 0;
+                       else if (dstrobe) begin
+                               if (state == rtc_state_read && bitcnt == 3'b111)
+                                 shift <= rdata;
+                               else
+                                 shift <= { shift[6:0], data_in };
+                       end                     
+               end
+       end
+       assign data_out = shift[7];     
+
+       /* Latch/decode command byte into address, ckaddr and dir */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       addr <= 0;
+                       dir <= 0;
+                       ckaddr <= 0;                    
+               end else if (gotbyte) begin
+                       if (state == rtc_state_cmd) begin
+                               dir <= shift[7];
+                               addr <= shift;
+                       end else if (state == rtc_state_cmd2)
+                         addr <= { addr[2:0], shift[6:2] };                    
+               end             
+       end
+
+       /* State machine */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       state <= rtc_state_cmd;
+               end else begin
+                       if (_data_enable)
+                         state <= rtc_state_cmd;
+                       else if (gotbyte) begin
+                               if (state == rtc_state_cmd) begin
+                                       if (shift[6:3] == 4'b1111)
+                                         state <= rtc_state_cmd2;
+                                       else if (shift[7])
+                                         state <= rtc_state_read;
+                                       else
+                                         state <= rtc_state_write;
+                               end else if (state == rtc_state_cmd2) begin
+                                       if (addr[7])
+                                         state <= rtc_state_read;
+                                       else
+                                         state <= rtc_state_write;
+                               end else
+                                 state <= rtc_state_cmd;
+                       end
+               end
+       end
+
+       /* Read machine */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       rdata <= 0;
+               end else begin
+               end             
+       end
+`endif
+       
+endmodule
diff --git a/fpga/scc.v b/fpga/scc.v
new file mode 100644 (file)
index 0000000..6984af9
--- /dev/null
@@ -0,0 +1,688 @@
+`timescale 1ns / 100ps
+
+/*
+ * Zilog 8530 SCC module for minimigmac.
+ *
+ * Located on high data bus, but writes are done at odd addresses as
+ * LDS is used as WR signals or something like that on a Mac Plus.
+ * 
+ * We don't care here and just ignore which side was used.
+ * 
+ * NOTE: We don't implement the 85C30 or ESCC additions such as WR7'
+ * for now, it's all very simplified
+ */
+
+module scc(input       sysclk,
+          input        reset_hw,
+
+          /* Bus interface. 2-bit address, to be wired
+           * appropriately upstream (to A1..A2).
+           */
+          input        cs,
+          input        we,
+          input [1:0]  rs, /* [1] = data(1)/ctl [0] = a_side(1)/b_side */
+          input [7:0]  wdata,
+          output [7:0] rdata,
+          output       _irq,
+
+          /* A single serial port on Minimig */
+          input        rxd,
+          output       txd,
+          input        cts, /* normally wired to device DTR output
+                             * on Mac cables. That same line is also
+                             * connected to the TRxC input of the SCC
+                             * to do fast clocking but we don't do that
+                             * here
+                             */
+          output       rts, /* on a real mac this activates line
+                             * drivers when low */
+
+          /* DCD for both ports are hijacked by mouse interface */
+          input        dcd_a, /* We don't synchronize those inputs */
+          input        dcd_b,
+
+          /* Write request */
+          output       wreq
+          );
+
+       /* Register access is semi-insane */
+       reg [3:0]       rindex;
+       wire            wreg_a;
+       wire            wreg_b;
+       wire            wdata_a;
+       wire            wdata_b;
+       wire            rdata_a;
+       wire            rdata_b;
+
+       /* Resets via WR9, one clk pulses */
+       wire            reset_a;
+       wire            reset_b;
+       wire            reset;
+
+       /* Data registers */
+       reg [7:0]       data_a;
+       reg [7:0]       data_b;
+
+       /* Read registers */
+       wire [7:0]      rr0_a;
+       wire [7:0]      rr0_b;
+       wire [7:0]      rr1_a;
+       wire [7:0]      rr1_b;
+       wire [7:0]      rr2_b;
+       wire [7:0]      rr3_a;
+       wire [7:0]      rr10_a;
+       wire [7:0]      rr10_b;
+       wire [7:0]      rr15_a;
+       wire [7:0]      rr15_b;
+
+       /* Write registers. Only some are implemented,
+        * some result in actions on write and don't
+        * store anything
+        */
+       reg [7:0]       wr1_a;
+       reg [7:0]       wr1_b;
+       reg [7:0]       wr2;
+       reg [7:0]       wr3_a;
+       reg [7:0]       wr3_b;
+       reg [7:0]       wr4_a;
+       reg [7:0]       wr4_b;
+       reg [7:0]       wr5_a;
+       reg [7:0]       wr5_b;
+       reg [7:0]       wr6_a;
+       reg [7:0]       wr6_b;
+       reg [7:0]       wr7_a;
+       reg [7:0]       wr7_b;
+       reg [7:0]       wr8_a;
+       reg [7:0]       wr8_b;
+       reg [5:0]       wr9;
+       reg [7:0]       wr10_a;
+       reg [7:0]       wr10_b;
+       reg [7:0]       wr11_a;
+       reg [7:0]       wr11_b;
+       reg [7:0]       wr12_a;
+       reg [7:0]       wr12_b;
+       reg [7:0]       wr13_a;
+       reg [7:0]       wr13_b;
+       reg [7:0]       wr14_a;
+       reg [7:0]       wr14_b;
+       reg [7:0]       wr15_a;
+       reg [7:0]       wr15_b;
+
+       /* Status latches */
+       reg             latch_open_a;
+       reg             latch_open_b;
+       reg             dcd_latch_a;
+       reg             dcd_latch_b;
+       wire            dcd_ip_a;
+       wire            dcd_ip_b;
+       wire            do_latch_a;
+       wire            do_latch_b;
+       wire            do_extreset_a;
+       wire            do_extreset_b;  
+
+       /* IRQ stuff */
+       wire            rx_irq_pend_a;
+       wire            rx_irq_pend_b;
+       wire            tx_irq_pend_a;
+       wire            tx_irq_pend_b;
+       wire            ex_irq_pend_a;
+       wire            ex_irq_pend_b;
+       reg             ex_irq_ip_a;
+       reg             ex_irq_ip_b;
+       wire [2:0]      rr2_vec_stat;   
+               
+       /* Register/Data access helpers */
+       assign wreg_a  = cs & we & (~rs[1]) &  rs[0];
+       assign wreg_b  = cs & we & (~rs[1]) & ~rs[0];
+       assign wdata_a = cs & we & (rs[1] | (rindex == 8)) &  rs[0];
+       assign wdata_b = cs & we & (rs[1] | (rindex == 8)) & ~rs[0];
+       assign rdata_a = cs & (~we) & (rs[1] | (rindex == 8)) &  rs[0];
+       assign rdata_b = cs & (~we) & (rs[1] | (rindex == 8)) & ~rs[0];
+
+       /* Register index is set by a write to WR0 and reset
+        * after any subsequent write. We ignore the side
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 rindex <= 0;
+               else if (cs && !rs[1]) begin
+                       /* Default, reset index */
+                       rindex <= 0;
+
+                       /* Write to WR0 */
+                       if (we && rindex == 0) begin
+                               /* Get low index bits */
+                               rindex[2:0] <= wdata[2:0];
+                                 
+                               /* Add point high */
+                               rindex[3] <= (wdata[5:3] == 3'b001);
+                       end
+               end
+       end
+
+       /* Reset logic (write to WR9 cmd)
+        *
+        * Note about resets: Some bits are documented as unchanged/undefined on
+        * HW reset by the doc. We apply this to channel and soft resets, however
+        * we _do_ reset every bit on an external HW reset in this implementation
+        * to make the FPGA & synthesis tools happy.
+        */
+       assign reset   = ((wreg_a | wreg_b) & (rindex == 9) & (wdata[7:6] == 2'b11)) | reset_hw;
+       assign reset_a = ((wreg_a | wreg_b) & (rindex == 9) & (wdata[7:6] == 2'b10)) | reset;   
+       assign reset_b = ((wreg_a | wreg_b) & (rindex == 9) & (wdata[7:6] == 2'b01)) | reset;
+
+       /* WR1
+        * Reset: bit 5 and 2 unchanged */
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr1_a <= 0;
+               else begin
+                       if (reset_a)
+                         wr1_a <= { 2'b00, wr1_a[5], 2'b00, wr1_a[2], 2'b00 };
+                       else if (wreg_a && rindex == 1)
+                         wr1_a <= wdata;
+               end
+       end
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr1_b <= 0;
+               else begin
+                       if (reset_b)
+                         wr1_b <= { 2'b00, wr1_b[5], 2'b00, wr1_b[2], 2'b00 };
+                       else if (wreg_b && rindex == 1)
+                         wr1_b <= wdata;
+               end
+       end
+
+       /* WR2
+        * Reset: unchanged 
+        */
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr2 <= 0;
+               else if ((wreg_a || wreg_b) && rindex == 2)
+                 wr2 <= wdata;                 
+       end
+
+       /* WR3
+        * Reset: bit 0 to 0, otherwise unchanged.
+        */
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr3_a <= 0;
+               else begin
+                       if (reset_a)
+                         wr3_a[0] <= 0;
+                       else if (wreg_a && rindex == 3)
+                         wr3_a <= wdata;
+               end
+       end
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr3_b <= 0;
+               else begin
+                       if (reset_b)
+                         wr3_b[0] <= 0;
+                       else if (wreg_b && rindex == 3)
+                         wr3_b <= wdata;
+               end
+       end
+
+       /* WR4
+        * Reset: Bit 2 to 1, otherwise unchanged
+        */
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr4_a <= 0;
+               else begin
+                       if (reset_a)
+                         wr4_a[2] <= 1;
+                       else if (wreg_a && rindex == 4)
+                         wr4_a <= wdata;
+               end
+       end
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr4_b <= 0;
+               else begin
+                       if (reset_b)
+                         wr4_b[2] <= 1;
+                       else if (wreg_b && rindex == 4)
+                         wr4_b <= wdata;
+               end
+       end
+
+       /* WR5
+        * Reset: Bits 7,4,3,2,1 to 0
+        */
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr5_a <= 0;
+               else begin
+                       if (reset_a)
+                         wr5_a <= { 1'b0, wr5_a[6:5], 4'b0000, wr5_a[0] };                     
+                       else if (wreg_a && rindex == 5)
+                         wr5_a <= wdata;
+               end
+       end
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr5_b <= 0;
+               else begin
+                       if (reset_b)
+                         wr5_b <= { 1'b0, wr5_b[6:5], 4'b0000, wr5_b[0] };                     
+                       else if (wreg_b && rindex == 5)
+                         wr5_b <= wdata;
+               end
+       end
+
+       /* WR6
+        * Reset: Unchanged.
+        */
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr6_a <= 0;
+               else if (wreg_a && rindex == 6)
+                 wr6_a <= wdata;
+       end
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr6_b <= 0;
+               else if (wreg_b && rindex == 6)
+                 wr6_b <= wdata;
+       end
+
+       /* WR7
+        * Reset: Unchanged.
+        */
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr7_a <= 0;
+               else if (wreg_a && rindex == 7)
+                 wr7_a <= wdata;
+       end
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr7_b <= 0;
+               else if (wreg_b && rindex == 7)
+                 wr7_b <= wdata;
+       end
+       
+       /* WR9. Special: top bits are reset, handled separately, bottom
+        * bits are only reset by a hw reset
+        */
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr9 <= 0;
+               else if ((wreg_a || wreg_b) && rindex == 9)
+                 wr9 <= wdata[5:0];                    
+       end
+
+       /* WR10
+        * Reset: all 0, except chanel reset retains 6 and 5
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 wr10_a <= 0;
+               else begin
+                       if (reset_a)
+                         wr10_a <= { 1'b0, wr10_a[6:5], 5'b00000 };
+                       else if (wreg_a && rindex == 10)
+                         wr10_a <= wdata;
+               end             
+       end
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 wr10_b <= 0;
+               else begin
+                       if (reset_b)
+                         wr10_b <= { 1'b0, wr10_b[6:5], 5'b00000 };
+                       else if (wreg_b && rindex == 10)
+                         wr10_b <= wdata;
+               end             
+       end
+
+       /* WR11
+        * Reset: On full reset only, not channel reset
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 wr11_a <= 8'b00001000;
+               else if (wreg_a && rindex == 11)
+                 wr11_a <= wdata;
+       end
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 wr11_b <= 8'b00001000;
+               else if (wreg_b && rindex == 11)
+                 wr11_b <= wdata;
+       end
+
+       /* WR12
+        * Reset: Unchanged
+        */
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr12_a <= 0;
+               else if (wreg_a && rindex == 12)
+                 wr12_a <= wdata;
+       end
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr12_b <= 0;          
+               else if (wreg_b && rindex == 12)
+                 wr12_b <= wdata;
+       end
+
+       /* WR13
+        * Reset: Unchanged
+        */
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr13_a <= 0;
+               else if (wreg_a && rindex == 13)
+                 wr13_a <= wdata;
+       end
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr13_b <= 0;          
+               else if (wreg_b && rindex == 13)
+                 wr13_b <= wdata;
+       end
+
+       /* WR14
+        * Reset: Full reset maintains  top 2 bits,
+        * Chan reset also maitains bottom 2 bits, bit 4 also
+        * reset to a different value
+        */
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr14_a <= 0;
+               else begin
+                       if (reset)
+                         wr14_a <= { wr14_a[7:6], 6'b110000 };
+                       else if (reset_a)
+                         wr14_a <= { wr14_a[7:6], 4'b1000, wr14_a[1:0] };
+                       else if (wreg_a && rindex == 14)
+                         wr14_a <= wdata;
+               end             
+       end
+       always@(posedge sysclk or posedge reset_hw) begin
+               if (reset_hw)
+                 wr14_b <= 0;
+               else begin
+                       if (reset)
+                         wr14_b <= { wr14_b[7:6], 6'b110000 };
+                       else if (reset_b)
+                         wr14_b <= { wr14_b[7:6], 4'b1000, wr14_b[1:0] };
+                       else if (wreg_b && rindex == 14)
+                         wr14_b <= wdata;
+               end             
+       end
+
+       /* WR15 */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 wr15_a <= 8'b11111000;
+               else if (wreg_a && rindex == 15)
+                 wr15_a <= wdata;                      
+       end
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 wr15_b <= 8'b11111000;
+               else if (wreg_b && rindex == 15)
+                 wr15_b <= wdata;                      
+       end
+       
+       /* Read data mux */     
+       assign rdata = rs[1] && rs[0]       ? data_a :
+                      rs[1]                ? data_b :
+                      rindex ==  0 && rs[0] ? rr0_a :
+                      rindex ==  0          ? rr0_b :
+                      rindex ==  1 && rs[0] ? rr1_a :
+                      rindex ==  1          ? rr1_b :
+                      rindex ==  2 && rs[0] ? wr2 :
+                      rindex ==  2          ? rr2_b :
+                      rindex ==  3 && rs[0] ? rr3_a :
+                      rindex ==  3          ? 0 :
+                      rindex ==  4 && rs[0] ? rr0_a :
+                      rindex ==  4          ? rr0_b :
+                      rindex ==  5 && rs[0] ? rr1_a :
+                      rindex ==  5          ? rr1_b :
+                      rindex ==  6 && rs[0] ? wr2 :
+                      rindex ==  6          ? rr2_b :
+                      rindex ==  7 && rs[0] ? rr3_a :
+                      rindex ==  7          ? 0 :
+
+                      rindex ==  8 && rs[0] ? data_a :
+                      rindex ==  8          ? data_b :
+                      rindex ==  9 && rs[0] ? wr13_a :
+                      rindex ==  9          ? wr13_b :
+                      rindex == 10 && rs[0] ? rr10_a :
+                      rindex == 10          ? rr10_b :
+                      rindex == 11 && rs[0] ? rr15_a :
+                      rindex == 11          ? rr15_b :
+                      rindex == 12 && rs[0] ? wr12_a :
+                      rindex == 12          ? wr12_b :
+                      rindex == 13 && rs[0] ? wr13_a :
+                      rindex == 13          ? wr13_b :
+                      rindex == 14 && rs[0] ? rr10_a :
+                      rindex == 14          ? rr10_b :
+                      rindex == 15 && rs[0] ? rr15_a :
+                      rindex == 15          ? rr15_b : 8'hff;
+
+       /* RR0 */
+       assign rr0_a = { 1'b0, /* Break */
+                        1'b1, /* Tx Underrun/EOM */
+                        1'b0, /* CTS */
+                        1'b0, /* Sync/Hunt */
+                        wr15_a[3] ? dcd_latch_a : dcd_a, /* DCD */
+                        1'b1, /* Tx Empty */
+                        1'b0, /* Zero Count */
+                        1'b0  /* Rx Available */
+                        };
+       assign rr0_b = { 1'b0, /* Break */
+                        1'b1, /* Tx Underrun/EOM */
+                        1'b0, /* CTS */
+                        1'b0, /* Sync/Hunt */
+                        wr15_b[3] ? dcd_latch_b : dcd_b, /* DCD */
+                        1'b1, /* Tx Empty */
+                        1'b0, /* Zero Count */
+                        1'b0  /* Rx Available */
+                        };
+
+       /* RR1 */
+       assign rr1_a = { 1'b0, /* End of frame */
+                        1'b0, /* CRC/Framing error */
+                        1'b0, /* Rx Overrun error */
+                        1'b0, /* Parity error */
+                        1'b0, /* Residue code 0 */
+                        1'b1, /* Residue code 1 */
+                        1'b1, /* Residue code 2 */
+                        1'b1  /* All sent */
+                        };
+       
+       assign rr1_b = { 1'b0, /* End of frame */
+                        1'b0, /* CRC/Framing error */
+                        1'b0, /* Rx Overrun error */
+                        1'b0, /* Parity error */
+                        1'b0, /* Residue code 0 */
+                        1'b1, /* Residue code 1 */
+                        1'b1, /* Residue code 2 */
+                        1'b1  /* All sent */
+                        };
+       
+       /* RR2 (Chan B only, A is just WR2) */
+       assign rr2_b = { wr2[7],
+                        wr9[4] ? rr2_vec_stat[0] : wr2[6],
+                        wr9[4] ? rr2_vec_stat[1] : wr2[5],
+                        wr9[4] ? rr2_vec_stat[2] : wr2[4],
+                        wr9[4] ? wr2[3] : rr2_vec_stat[2],
+                        wr9[4] ? wr2[2] : rr2_vec_stat[1],
+                        wr9[4] ? wr2[1] : rr2_vec_stat[0],
+                        wr2[0]
+                        };
+       
+
+       /* RR3 (Chan A only) */
+       assign rr3_a = { 2'b0,
+                        rx_irq_pend_a, /* Rx interrupt pending */
+                        tx_irq_pend_a, /* Tx interrupt pending */
+                        ex_irq_pend_a, /* Status/Ext interrupt pending */
+                        rx_irq_pend_b,
+                        tx_irq_pend_b,
+                        ex_irq_pend_b
+                       };
+
+       /* RR10 */
+       assign rr10_a = { 1'b0, /* One clock missing */
+                         1'b0, /* Two clocks missing */
+                         1'b0,
+                         1'b0, /* Loop sending */
+                         1'b0,
+                         1'b0,
+                         1'b0, /* On Loop */
+                         1'b0
+                         };
+       assign rr10_b = { 1'b0, /* One clock missing */
+                         1'b0, /* Two clocks missing */
+                         1'b0,
+                         1'b0, /* Loop sending */
+                         1'b0,
+                         1'b0,
+                         1'b0, /* On Loop */
+                         1'b0
+                         };
+       
+       /* RR15 */
+       assign rr15_a = { wr15_a[7],
+                         wr15_a[6],
+                         wr15_a[5],
+                         wr15_a[4],
+                         wr15_a[3],
+                         1'b0,
+                         wr15_a[1],
+                         1'b0
+                         };
+
+       assign rr15_b = { wr15_b[7],
+                         wr15_b[6],
+                         wr15_b[5],
+                         wr15_b[4],
+                         wr15_b[3],
+                         1'b0,
+                         wr15_b[1],
+                         1'b0
+                         };
+       
+       /* Interrupts. Simplified for now
+        *
+        * Need to add latches. Tx irq is latched when buffer goes from full->empty,
+        * it's not a permanent state. For now keep it clear. Will have to fix that.
+        */
+       assign rx_irq_pend_a = 0;
+       assign tx_irq_pend_a = 0 /*& wr1_a[1]*/; /* Tx always empty for now */
+       assign ex_irq_pend_a = ex_irq_ip_a;
+       assign rx_irq_pend_b = 0;
+       assign tx_irq_pend_b = 0 /*& wr1_b[1]*/; /* Tx always empty for now */
+       assign ex_irq_pend_b = ex_irq_ip_b;
+
+       assign _irq = ~(wr9[3] & (rx_irq_pend_a |
+                                 rx_irq_pend_b |
+                                 tx_irq_pend_a |
+                                 tx_irq_pend_b |
+                                 ex_irq_pend_a |
+                                 ex_irq_pend_b));
+
+       /* XXX Verify that... also missing special receive condition */
+       assign rr2_vec_stat = rx_irq_pend_a ? 3'b110 :
+                             tx_irq_pend_a ? 3'b100 :
+                             ex_irq_pend_a ? 3'b101 :
+                             rx_irq_pend_b ? 3'b010 :
+                             tx_irq_pend_b ? 3'b000 :
+                             ex_irq_pend_b ? 3'b001 : 3'b011;
+       
+       /* External/Status interrupt & latch logic */
+       assign do_extreset_a = wreg_a & (rindex == 0) & (wdata[5:3] == 3'b010);
+       assign do_extreset_b = wreg_b & (rindex == 0) & (wdata[5:3] == 3'b010);
+
+       /* Internal IP bit set if latch different from source and
+        * corresponding interrupt is enabled in WR15
+        */
+       assign dcd_ip_a = (dcd_a != dcd_latch_a) & wr15_a[3];
+       assign dcd_ip_b = (dcd_b != dcd_latch_b) & wr15_b[3];
+
+       /* Latches close when an enabled IP bit is set and latches
+        * are currently open
+        */
+       assign do_latch_a = latch_open_a & (dcd_ip_a /* | cts... */);
+       assign do_latch_b = latch_open_b & (dcd_ip_b /* | cts... */);
+
+       /* "Master" interrupt, set when latch close & WR1[0] is set */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 ex_irq_ip_a <= 0;
+               else if (do_extreset_a)
+                 ex_irq_ip_a <= 0;
+               else if (do_latch_a && wr1_a[0])
+                 ex_irq_ip_a <= 1;
+       end
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 ex_irq_ip_b <= 0;
+               else if (do_extreset_b)
+                 ex_irq_ip_b <= 0;
+               else if (do_latch_b && wr1_b[0])
+                 ex_irq_ip_b <= 1;
+       end
+
+       /* Latch open/close control */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 latch_open_a <= 1;
+               else begin
+                       if (do_extreset_a)
+                         latch_open_a <= 1;
+                       else if (do_latch_a)
+                         latch_open_a <= 0;
+               end
+       end
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 latch_open_b <= 1;
+               else begin
+                       if (do_extreset_b)
+                         latch_open_b <= 1;
+                       else if (do_latch_b)
+                         latch_open_b <= 0;
+               end
+       end
+
+       /* Latches proper */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       dcd_latch_a <= 0;
+                       /* cts ... */
+               end else begin
+                       if (do_latch_a)
+                         dcd_latch_a <= dcd_a;
+                       /* cts ... */
+               end
+       end
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       dcd_latch_b <= 0;                       
+                       /* cts ... */
+               end else begin
+                       if (do_latch_b)
+                         dcd_latch_b <= dcd_b;
+                       /* cts ... */
+               end
+       end
+       
+       /* NYI */
+       assign txd = 1;
+       assign rts = 1;
+
+       assign wreq = 0;        
+endmodule
diff --git a/fpga/scope.v b/fpga/scope.v
new file mode 100644 (file)
index 0000000..c219f90
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Internal signal capture module
+ */
+
+`define SIGCAP_WIDTH   64
+`define SIGCAP_DEPTH   2048
+`define SIGCAP_ADDRBITS        16
+
+/* Capture RAM module, should infer a block RAM in ISE
+ *
+ * Based on XST example "Dual-Port Block RAM with Different Clocks"
+ */
+module capram
+(
+       input                           clka,
+       input                           clkb,
+       input                           wea,
+       input [`SIGCAP_ADDRBITS-1:0]    addra,
+       input [`SIGCAP_ADDRBITS-1:0]    addrb,
+       input [`SIGCAP_WIDTH-1:0]       dia,
+       output [`SIGCAP_WIDTH-1:0]      doa,
+       output [`SIGCAP_WIDTH-1:0]      dob
+);     
+       reg [`SIGCAP_WIDTH-1:0]         ram [`SIGCAP_DEPTH-1:0];
+       reg [`SIGCAP_ADDRBITS-1:0]      addr_rega;
+       reg [`SIGCAP_ADDRBITS-1:0]      addr_regb;
+
+       always @(posedge clka) begin
+               if (wea)
+                 ram[addra] <= dia;
+               addr_rega <= addra;
+       end
+       always @(posedge clkb) begin
+               addr_regb <= addrb;
+       end
+       assign doa = ram[addr_rega];
+       assign dob = ram[addr_regb];
+endmodule
+
+/* WO: Capture control register */
+`define SIGCAP_REG_CTRL                0
+`define    SIGCAP_CTRL_RESET   0 /* 8'b00000001 */
+`define    SIGCAP_CTRL_RUN     1 /* 8'b00000002 */
+
+/* RO: Total sample count in buffer */
+`define SIGCAP_REG_COUNT_0     2
+`define SIGCAP_REG_COUNT_1     3
+
+/* RW: Read next sample. Write reset read address */
+`define SIGCAP_REG_DATA                4
+
+/*
+ * Note about clock domains:
+ * 
+ * sysclk is used for the register interface, capclk for the actual
+ * capture. However, we don't use synchronizers when those domains
+ * collide, as we know that both clocks derive from the same DLL
+ * and shouldn't go metastable. Typically capclk is a multiple of
+ * sysclk
+ */
+module scope
+(
+       /* System clock */
+       input                           sysclk,
+       /* Capture clock */
+       input                           capclk,
+       /* System reset */
+       input                           reset,
+       /* Signals to capture */
+       input [`SIGCAP_WIDTH-1:0]       sigs,
+
+       /* Backbus interface */
+       input [5:0]                     bb_addr,
+       input [7:0]                     bb_wdata,
+       output [7:0]                    bb_rdata,
+       input                           bb_strobe,
+       input                           bb_wr
+ );
+       reg                             capturing;
+
+       reg [`SIGCAP_ADDRBITS-1:0]      cap_addr;
+       reg [`SIGCAP_ADDRBITS-1:0]      read_addr;
+       reg [2:0]                       read_byte;      
+       
+       wire [`SIGCAP_WIDTH-1:0]        cap_data;
+       reg [7:0]                       cap_byte;
+       wire [`SIGCAP_WIDTH-1:0]        ram_ignore;
+
+       reg                             do_reset;
+       reg                             do_run;
+
+       wire                            cap_full;
+       wire                            do_reset_combo;
+
+
+       /* Instanciate capture memory */
+       capram capram0(.clka(capclk),
+                      .clkb(sysclk),
+                      .wea(capturing && !cap_full),
+                      .addra(cap_addr),
+                      .addrb(read_addr),
+                      .dia(sigs),
+                      .doa(ram_ignore),
+                      .dob(cap_data));
+
+       /* Capture control logic */
+       always@(posedge capclk or posedge reset) begin
+               if (reset) begin
+                       cap_addr <= 0;                  
+               end else begin
+                       if (do_reset) begin
+                               capturing <= 0;
+                               cap_addr <= 0;
+                       end else if (!do_run) begin
+                               capturing <= 0;
+                       end else begin
+                               capturing <= 1;
+                               if (capturing && !cap_full) begin
+                                       cap_addr <= cap_addr + 1;
+                               end                             
+                       end
+               end
+       end
+       assign cap_full = cap_addr == (`SIGCAP_DEPTH - 1);
+
+       /* Mux memory output */
+       always@(read_byte or cap_data) begin
+               case (read_byte)
+                       0: cap_byte = cap_data[63:56];
+                       1: cap_byte = cap_data[55:48];
+                       2: cap_byte = cap_data[47:40];
+                       3: cap_byte = cap_data[39:32];
+                       4: cap_byte = cap_data[31:24];
+                       5: cap_byte = cap_data[23:16];
+                       6: cap_byte = cap_data[15:8];
+                       7: cap_byte = cap_data[7:0];
+                       default: cap_byte = cap_data[7:0];
+               endcase
+       end
+
+       /* Register read */
+       assign bb_rdata = (bb_addr[2:0] == `SIGCAP_REG_COUNT_0) ?
+                               cap_addr[`SIGCAP_ADDRBITS-1:8] :
+                         (bb_addr[2:0] == `SIGCAP_REG_COUNT_1) ? cap_addr[7:0] :
+                         cap_byte;     
+
+       /* Fifo address counter */
+       assign do_reset_combo = reset | do_reset;
+
+       always@(posedge sysclk or posedge do_reset_combo) begin
+               if (do_reset_combo) begin
+                       read_addr <= 0;
+                       read_byte <= 0;
+               end else begin
+                       if (bb_strobe && bb_addr[2:0] == `SIGCAP_REG_DATA) begin
+                               if (bb_wr) begin
+                                       read_byte <= 0;
+                                       read_addr <= 0;
+                               end else begin
+                                       read_byte <= read_byte + 1;
+                                       if (read_byte == 3'b111)
+                                         read_addr <= read_addr + 1;
+                               end                             
+                       end
+               end
+       end
+       
+       /* Control register write interface */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       do_reset <= 1;
+                       do_run <= 0;                    
+               end else begin
+                       if (bb_strobe && bb_wr && bb_addr[2:0] == `SIGCAP_REG_CTRL) begin
+                               do_reset <= bb_wdata[`SIGCAP_CTRL_RESET];
+                               do_run <= bb_wdata[`SIGCAP_CTRL_RUN];
+                       end
+               end
+       end
+endmodule
+
+       
+                               
+                       
+       
\ No newline at end of file
diff --git a/fpga/sim_bench.v b/fpga/sim_bench.v
new file mode 100644 (file)
index 0000000..e632ba5
--- /dev/null
@@ -0,0 +1,236 @@
+`timescale 1ns / 100ps
+
+module main();
+
+       /* Interface between FPGA and other siumulated components */
+
+       /* m68k interface */
+       wire [15:0]     cpu_data;
+       wire [23:1]     cpu_address;
+       wire [2:0]      _cpu_ipl;
+       wire            _cpu_as;
+       wire            _cpu_uds;
+       wire            _cpu_lds;
+       wire            cpu_r_w;
+       wire            _cpu_dtack;
+       wire            _cpu_reset;
+       wire            cpu_clk;
+
+       /* SRAM interface */
+       wire [15:0]     ram_data;
+       wire [19:1]     ram_address;
+       wire [3:0]      _ram_ce;        
+       wire            _ram_bhe;
+       wire            _ram_ble;
+       wire            _ram_we;
+       wire            _ram_oe;        
+
+       /* RS232 pins */
+       wire            rxd;
+       wire            txd;
+       wire            cts;
+       wire            rts;
+
+       /* Joystick ports */
+       wire [5:0]      _joy1;
+       wire [5:0]      _joy2;
+
+       /* PS2 ports */
+       wire            msdat;
+       wire            msclk;
+       wire            kbddat;
+       wire            kbdclk;
+
+       /* Video pins */
+       wire            _hsync;
+       wire            _vsync;
+       wire [3:0]      red;
+       wire [3:0]      green;
+       wire [3:0]      blue;
+
+       /* Audio pins */
+       wire            left;
+       wire            right;
+
+       /* System pins */
+       wire            mclk; /* Note: Ignored in sim */
+       wire            pwrled;
+       wire            init_b;
+       wire            _15khz;
+       wire            gpio;
+       wire            scsi_hshake;
+       wire            nmi;
+       
+       /* SPI pins */
+       wire            _spi_cs;        
+       wire            sdi;
+       wire            sdo;
+       wire            sck;
+
+       /* Unused CPU output pins */
+       wire [2:0]      cpu_fc;
+       wire            _cpu_bg;
+               
+       /* Instanciate 4 SRAM modules */
+       sim_sram512x16 ram0(.addr(ram_address),
+                           .data(ram_data),
+                           ._cs(_ram_ce[0]),
+                           ._oe(_ram_oe),
+                           ._we(_ram_we),
+                           ._ub(_ram_bhe),
+                           ._lb(_ram_ble));
+       
+       sim_sram512x16 ram1(.addr(ram_address),
+                           .data(ram_data),
+                           ._cs(_ram_ce[1]),
+                           ._oe(_ram_oe),
+                           ._we(_ram_we),
+                           ._ub(_ram_bhe),
+                           ._lb(_ram_ble));
+       
+       sim_sram512x16 ram2(.addr(ram_address),
+                           .data(ram_data),
+                           ._cs(_ram_ce[2]),
+                           ._oe(_ram_oe),
+                           ._we(_ram_we),
+                           ._ub(_ram_bhe),
+                           ._lb(_ram_ble));
+       
+       sim_sram512x16 ram3(.addr(ram_address),
+                           .data(ram_data),
+                           ._cs(_ram_ce[3]),
+                           ._oe(_ram_oe),
+                           ._we(_ram_we),
+                           ._ub(_ram_bhe),
+                           ._lb(_ram_ble));
+       
+       /* Instanciate CPU. Lotsa stuff not wired on Minimig */
+       m68k cpu0(.clk(cpu_clk),
+                .fc(cpu_fc),
+                .a(cpu_address),
+                ._as(_cpu_as),
+                ._uds(_cpu_uds),
+                ._lds(_cpu_lds),
+                .r_w(cpu_r_w),
+                ._dtack(_cpu_dtack),
+                .d(cpu_data),
+                ._ipl(_cpu_ipl),
+                ._avec(1'b1),
+                ._br(1'b1),
+                ._bg(_cpu_bg),
+                ._berr(1'b1),
+                ._reset(_cpu_reset),
+                ._halt(1'b1));
+
+       pullup(_cpu_reset);     
+       
+      defparam cpu.turbo = 1;
+//     defparam cpu.log_flags = 0;
+//     defparam cpu.log_flags = 'hff;
+
+       /* Instanciate the FPGA */
+       minimigmac minimigmac0(.cpu_data(cpu_data),
+                              .cpu_address(cpu_address),
+                              ._cpu_ipl(_cpu_ipl),
+                              ._cpu_uds(_cpu_uds),
+                              ._cpu_lds(_cpu_lds),
+                              ._cpu_as(_cpu_as),
+                              .cpu_r_w(cpu_r_w),
+                              ._cpu_dtack(_cpu_dtack),
+                              ._cpu_reset(_cpu_reset),
+                              .cpu_clk(cpu_clk),
+                              .ram_data(ram_data),
+                              .ram_address(ram_address),
+                              ._ram_ce(_ram_ce),
+                              ._ram_bhe(_ram_bhe),
+                              ._ram_ble(_ram_ble),
+                              ._ram_we(_ram_we),
+                              ._ram_oe(_ram_oe),
+                              .rxd(rxd),
+                              .txd(txd),
+                              .cts(cts),
+                              .rts(rts),
+                              ._joy1(_joy1),
+                              ._joy2(_joy2),
+                              .msdat(msdat),
+                              .msclk(msclk),
+                              .kbddat(kbddat),
+                              .kbdclk(kbdclk),
+                              ._hsync(_hsync),
+                              ._vsync(_vsync),
+                              .red(red),
+                              .green(green),
+                              .blue(blue),
+                              .left(left),
+                              .right(right),
+                              .mclk(mclk),
+                              .pwrled(pwrled),
+                              .init_b(init_b),
+                              ._15khz(_15khz),
+                              .gpio(gpio),
+                              .nmi(nmi),
+                              .scsi_hshake(scsi_hshake),
+                              ._spi_cs(_spi_cs),
+                              .sdi(sdi),
+                              .sdo(sdo),
+                              .sck(sck));
+
+       sim_pic pic0(.sdo(sdi),
+                    .sdi(sdo),
+                    .sck(sck),
+                    ._spi_cs(_spi_cs),
+                    .scsi_hshake(scsi_hshake),
+                    .nmi(nmi));
+
+       sim_mouse mouse0(.clk(msclk), .dat(msdat));
+       sim_kbd kbd0(.clk(kbdclk), .dat(kbddat));
+
+       integer rom_file;
+       integer rom_size;
+
+       initial begin
+                $dumpfile("sim_bench.vcd");
+                $dumpvars(0,minimigmac0);
+                $dumpvars(0,ram0);
+                $dumpvars(0,ram1);
+                $dumpvars(0,ram2);
+                $dumpvars(0,ram3);
+                $dumpvars(0,pic0);
+                $dumpvars(0,cpu0);
+                $dumpvars(0,mouse0);
+
+               $display("Loading ROM...");
+               rom_file = $fopenr("rom.bin");
+               rom_size = $fread(ram2.mem, rom_file, 0);
+               $fclose(rom_file);
+               $display("Loaded 0x%h bytes", rom_size);
+               $display("ram[841]=%h", ram2.mem['h841]);
+               $display("Disabling mem test");
+               ram0.mem['h2a2/2] = 'h0040;
+               ram0.mem['h2a4/2] = 'h0000;
+               $display("Disabling boot beep & clr screen etc...");
+               ram2.mem['hf4/2] = 'h4e71;
+               ram2.mem['hf6/2] = 'h4e71;
+               ram2.mem['hea/2] = 'h4e71;              
+               ram2.mem['hec/2] = 'h4e71;
+               ram2.mem['h34/2] = 'h4e71;
+               ram2.mem['h36/2] = 'h4e71;
+               ram2.mem['h38/2] = 'h4e71;
+               ram2.mem['h3a/2] = 'h4e71;      
+               ram2.mem['h3c/2] = 'h4e71;
+               ram2.mem['h3e/2] = 'h4e71;      
+               ram2.mem['h40/2] = 'h4e71;
+               /* checksum & test memory replaced with
+                * movea.l #memsize, a5 and beq->bra
+                */
+               ram2.mem['h12a/2] = 'h2a7c;             
+               ram2.mem['h12c/2] = 'h0020;
+               ram2.mem['h12e/2] = 'h0000;
+               ram2.mem['h130/2] = 'h4e71;
+               ram2.mem['h132/2] = 'h6000;
+               ram0.mem['h108/2] = 'h0020; /* memtop */
+               ram0.mem['h10a/2] = 'h0000;
+               #50000000;
+               $finish;
+       end
+endmodule
\ No newline at end of file
diff --git a/fpga/sim_clocks.v b/fpga/sim_clocks.v
new file mode 100644 (file)
index 0000000..3fa0d32
--- /dev/null
@@ -0,0 +1,62 @@
+`timescale 1ns / 100ps
+
+/*
+ * Generate clocks for simulation
+ * 
+ * We generate:
+ * 
+ *   - sysclk16 at 16.6666Mhz (60ns)
+ *   - sysclk33 at 33.3333Mhz (30ns)
+ * 
+ */
+module clockgen(input          mclk, /* ignored */
+               output reg      sysclk16,
+               output reg      sysclk33,
+               output reg      pixclk);
+
+       initial begin
+               sysclk16 = 0;
+               sysclk33 = 0;
+               pixclk = 0;             
+       end
+
+       always begin
+               #30 sysclk16 = ~sysclk16;
+       end
+       always begin
+               #15 sysclk33 = ~sysclk33;
+       end
+       always begin
+               #20 pixclk = ~pixclk;
+       end
+endmodule
+
+/* The xilinx OFDDRCPE IO module is simulated here for iverilog */
+module OFDDRCPE
+(
+       output          Q,
+       input           C0,
+       input           C1,
+       input           CE,
+       input           CLR,
+       input           D0,
+       input           D1,
+       input           PRE
+);
+       reg             c = 0;
+
+       assign Q = c == 0 ? D0 : D1;
+
+       always@(posedge C0 or posedge C1 or posedge CLR or posedge PRE) begin
+               if (CLR)
+                 c = 0;
+               else if (PRE)
+                 c = 1;
+               else if (CE) begin
+                       if (C0)
+                         c = 0;
+                       else if (C1)
+                         c = 1;                        
+               end             
+       end             
+endmodule
diff --git a/fpga/sim_kbd.v b/fpga/sim_kbd.v
new file mode 100644 (file)
index 0000000..d36a77c
--- /dev/null
@@ -0,0 +1,147 @@
+`timescale 1ns / 100ps
+
+module sim_kbd(inout clk, inout dat);
+`define CLK_HALF       5000    /* faster sim, should be more like 50000 */
+   
+   reg aclk;
+   reg adat;
+   reg [7:0] cmd;      
+   reg              wait_led;
+   
+   
+   task do_send;
+      input [7:0]      d;
+      output           ok;
+      reg [10:0]       sr;
+      reg              p;              
+      integer          i;              
+      begin
+        $display("sim_kbd: Send %x", d);
+        
+        p = ~(d[0] ^ d[1] ^ d[2] ^ d[3] ^
+              d[4] ^ d[5] ^ d[6] ^ d[7]);      
+        sr = { 1'b1, p, d, 1'b0 };
+        aclk = 0;
+        adat = 0;
+        ok = 1;
+        i = 11;
+        while(i > 0 && ok) begin
+           if (clk == 0) begin
+              $display("sim_kbd: Clock low, aborting send");
+              ok = 0;                                  
+           end else begin
+              adat = ~sr[0];
+              #`CLK_HALF aclk = 1;
+              #`CLK_HALF aclk = 0;
+              #1;                              
+              sr = { 1'b0, sr[10:1] };
+              i = i - 1;
+           end
+        end
+        aclk = 0;
+        adat = 0;
+        #`CLK_HALF;                    
+        #`CLK_HALF;                    
+      end
+   endtask
+
+   task do_receive;
+      output [7:0]     d;
+      output           ok;
+      reg [10:0]       sr;
+      reg              p;              
+      integer          i;              
+      begin
+        aclk = 0;
+        adat = 0;
+        sr = 0;                        
+        wait(dat == 0);
+        wait(clk != 0);        
+        ok = 1;
+        i = 10;
+        while(i > 0 && ok) begin
+           if (clk == 0) begin
+              $display("sim_kbd: Clock low, aborting receive");
+              ok = 0;                                  
+           end else begin
+              #`CLK_HALF aclk = 1;
+              #`CLK_HALF aclk = 0;
+              #1;                              
+              sr = { dat != 0, sr[10:1] };
+              i = i - 1;
+           end
+        end // while (i > 0 && ok)
+        adat = 1;
+        #`CLK_HALF aclk = 1;
+        #`CLK_HALF aclk = 0;
+        adat = 0;                      
+        //                     $display("rx shift final: %b", sr);                     
+        aclk = 0;
+        adat = 0;
+        d = sr[8:1];
+        #`CLK_HALF;                    
+        #`CLK_HALF;    
+        $display("sim_kbd: Got %x ok=%b", d, ok);
+      end
+   endtask
+   
+   pullup(clk);        
+   pullup(dat);
+
+   assign clk = aclk ? 1'b0 : 1'bz;
+   assign dat = adat ? 1'b0 : 1'bz;
+
+   reg ok;
+
+   always@(clk, dat) begin
+      while (clk == 0) begin
+        #`CLK_HALF;
+        if (clk == 0) begin
+           #`CLK_HALF;
+           if (clk == 0) begin
+              do_receive(cmd, ok);
+              if (ok) begin
+                 if (wait_led) begin
+                    do_send(8'hfa, ok);
+                    $display("sim_kbd: LEDs set to %x\n", cmd);
+                 end else begin
+                    if (cmd == 8'hed) begin
+                       wait_led = 1;                                              
+                       do_send(8'hfa, ok);
+                    end else if (cmd == 8'hff) begin
+                       if (ok)
+                         do_send(8'hfa, ok);
+                       if (ok)
+                         do_send(8'haa, ok);
+                    end
+                 end            
+              end                                      
+           end
+        end
+      end
+   end
+
+   initial begin       
+      aclk = 0;
+      adat = 0;
+      wait_led = 0;       
+      #`CLK_HALF;
+      #`CLK_HALF;
+      #`CLK_HALF;
+      #`CLK_HALF;
+      do_send(8'haa, ok);
+      #10000000;
+      do_send(8'h1c, ok);
+      #100000;
+      do_send(8'hf0, ok);
+      do_send(8'h1c, ok);
+      #1000000;
+      do_send(8'he0, ok);
+      do_send(8'h75, ok);
+      #100000;
+      do_send(8'he0, ok);
+      do_send(8'hf0, ok);
+      do_send(8'h75, ok);
+   end
+endmodule
+
diff --git a/fpga/sim_mouse.v b/fpga/sim_mouse.v
new file mode 100644 (file)
index 0000000..c2c8e03
--- /dev/null
@@ -0,0 +1,139 @@
+`timescale 1ns / 100ps
+
+module sim_mouse(inout clk, inout dat);
+`define CLK_HALF       5000    /* faster sim, should be more like 50000 */
+
+       reg aclk;
+       reg adat;
+       reg [7:0] cmd;  
+       
+       task do_send;
+               input [7:0]     d;
+               output          ok;
+               reg [10:0]      sr;
+               reg             p;              
+               integer         i;              
+               begin
+                       $display("sim_mouse: send %x", d);
+                       
+                       p = ~(d[0] ^ d[1] ^ d[2] ^ d[3] ^
+                             d[4] ^ d[5] ^ d[6] ^ d[7]);       
+                       sr = { 1'b1, p, d, 1'b0 };
+                       aclk = 0;
+                       adat = 0;
+                       ok = 1;
+                       i = 11;
+                       while(i > 0 && ok) begin
+                               if (clk == 0) begin
+                                       $display("Mouse: Clock low, aborting send");
+                                       ok = 0;                                 
+                               end else begin
+                                       adat = ~sr[0];
+                                       #`CLK_HALF aclk = 1;
+                                       #`CLK_HALF aclk = 0;
+                                       #1;                             
+                                       sr = { 1'b0, sr[10:1] };
+                                       i = i - 1;
+                               end
+                       end
+                       aclk = 0;
+                       adat = 0;
+                       #`CLK_HALF;                     
+                       #`CLK_HALF;                     
+               end
+       endtask
+
+       task do_receive;
+               output [7:0]    d;
+               output          ok;
+               reg [10:0]      sr;
+               reg             p;              
+               integer         i;              
+               begin
+                       aclk = 0;
+                       adat = 0;
+                       sr = 0;                 
+                       wait(dat == 0);
+                       wait(clk != 0); 
+                       ok = 1;
+                       i = 10;
+                       while(i > 0 && ok) begin
+                               if (clk == 0) begin
+                                       $display("Mouse: Clock low, aborting receive");
+                                       ok = 0;                                 
+                               end else begin
+                                       #`CLK_HALF aclk = 1;
+                                       #`CLK_HALF aclk = 0;
+                                       #1;                             
+                                       sr = { dat != 0, sr[10:1] };
+                                       i = i - 1;
+                               end
+                       end // while (i > 0 && ok)
+                       adat = 1;
+                       #`CLK_HALF aclk = 1;
+                       #`CLK_HALF aclk = 0;
+                       adat = 0;                       
+//                     $display("rx shift final: %b", sr);                     
+                       aclk = 0;
+                       adat = 0;
+                       d = sr[8:1];
+                       #`CLK_HALF;                     
+                       #`CLK_HALF;     
+                       $display("sim_mouse:  got %x ok=%b", d, ok);
+       end
+       endtask
+       
+       pullup(clk);    
+       pullup(dat);
+
+       assign clk = aclk ? 1'b0 : 1'bz;
+       assign dat = adat ? 1'b0 : 1'bz;
+
+       reg ok;
+
+       always@(clk, dat) begin
+               while (clk == 0) begin
+                       #`CLK_HALF;
+                       if (clk == 0) begin
+                               #`CLK_HALF;
+                               if (clk == 0) begin
+                                       do_receive(cmd, ok);
+                                       if (ok) begin
+                                               if (cmd == 8'hf4) begin
+                                                       do_send(8'hfa, ok);
+                                               end else if (cmd == 8'hff) begin
+                                                       if (ok)
+                                                         do_send(8'haa, ok);
+                                                       if (ok)
+                                                         do_send(8'h00, ok);
+                                               end
+                                       end                                     
+                               end
+                       end
+               end
+       end
+
+       initial begin   
+               aclk = 0;
+               adat = 0;
+               #`CLK_HALF;
+               #`CLK_HALF;
+               #`CLK_HALF;
+               #`CLK_HALF;
+               do_send(8'haa, ok);
+               do_send(8'h00, ok);
+               #6000000;
+               do_send(8'h00, ok);
+               do_send(8'h02, ok);
+               do_send(8'h00, ok);
+               #1000000;
+               do_send(8'h00, ok);
+               do_send(8'h02, ok);
+               do_send(8'h00, ok);
+               #1000000;
+               do_send(8'h00, ok);
+               do_send(8'h00, ok);
+               do_send(8'h02, ok);
+       end
+endmodule
+
diff --git a/fpga/sim_pic.v b/fpga/sim_pic.v
new file mode 100644 (file)
index 0000000..de82055
--- /dev/null
@@ -0,0 +1,746 @@
+`timescale 1ns / 100ps
+
+`define ROM_MAX        'h20000
+
+`define BB_WRITE               8'h80
+`define BB_AUTOINC             8'h40
+
+`define BB_REG_CTRL_BASE       6'h00
+`define BB_REG_CPU_BASE                6'h10
+`define BB_REG_MEM_BASE                6'h20
+`define BB_REG_IWM_BASE                6'h28
+`define BB_REG_SCSI_BASE       6'h30
+`define BB_REG_SCOPE_BASE      6'h38
+
+/* SCSI Backbus interface */
+`define BB_REG_LINES   0
+`define BB_LINES_BSY   7
+`define BB_LINES_SEL   6       
+`define BB_LINES_ACK   5
+`define BB_LINES_ATN   4
+`define BB_LINES_RST   3
+
+`define BB_REG_ASSERT  1
+`define BB_ASSERT_BSY  7
+`define BB_ASSERT_SEL  6
+`define BB_ASSERT_CD   5
+`define BB_ASSERT_IO   4
+`define BB_ASSERT_MSG  3
+`define BB_ASSERT_REQ  2
+`define BB_ASSERT_RST  1
+`define BB_ASSERT_AUTO 0       /* Automatic mode */
+
+`define BB_REG_ODATA   2
+`define BB_REG_IDATA   3
+`define BB_REG_ACNT_HI 4
+`define BB_REG_ACNT_LO 5
+
+module sim_pic
+(
+       input                   sdi,
+       output reg              sdo,
+       output reg              sck,
+       output reg              _spi_cs,
+       input                   scsi_hshake,
+       output reg              nmi);
+       integer rom_file;
+       integer rom_size;
+       reg [7:0] rom[0:`ROM_MAX];
+       reg [7:0] scsi_asserts;
+       reg [7:0] scsi_cdb[0:11];
+       reg [7:0] scsi_buf[0:2047];     
+       integer   dbg_foo;      
+
+       /* SPI half clock period */
+       parameter SPI_HCLOCK    = 40;
+       /* Setup time of data vs rising clock edge */
+       parameter SPI_HEAD      = 4;
+       
+       task do_spi;
+               input [7:0] out_byte;           /* Out to FPGA */
+               output reg [7:0] in_byte;       /* In from FPGA */
+               reg [7:0]    in_sr;
+               reg [7:0]    out_sr;
+               begin
+                       in_sr = 8'hff;
+                       out_sr = out_byte;                      
+                       repeat(8) begin
+                               sck = 0;
+                               #(SPI_HCLOCK-SPI_HEAD);                         
+                               sdo = out_sr[7];
+                               out_sr = { out_sr[6:0], 1'b0 };
+                               in_sr = { in_sr[6:0], sdi };
+                               #SPI_HEAD sck = 1;
+                               #SPI_HCLOCK;                            
+                       end
+                       sck =  0;
+                       in_byte = in_sr;
+               end             
+       endtask // do_spi
+               
+       task run_to;
+               input [23:0] addr;
+               reg [7:0]    stat;              
+               begin
+                       /* Set breakpoint */
+                       _spi_cs = 0;
+                       do_spi(`BB_WRITE | `BB_AUTOINC | `BB_REG_CPU_BASE | 8, val);
+                       do_spi(addr[23:16], val);
+                       do_spi(addr[15:8], val);
+                       do_spi(addr[7:0], val);
+                       _spi_cs = 1;
+                       #(SPI_HCLOCK*4);
+
+                       /* Run CPU interface */
+                       _spi_cs = 0;
+                       do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val);
+                       do_spi(8'h42, val); /* RUN + BKEN */
+                       _spi_cs = 1;
+                       #(SPI_HCLOCK*4);
+
+                       stat = 0;
+                       while(!stat[3]) begin
+                               /* Get CPU status */
+                               _spi_cs = 0;
+                               do_spi(`BB_REG_CPU_BASE | 7, val);
+                               do_spi(8'hff, val);
+                               do_spi(8'hff, stat);
+                               _spi_cs = 1;
+//                             $display("stat: %h", stat);
+//                             #(SPI_HCLOCK*40);
+                       end
+               end
+       endtask // run_to
+
+       task scsi_set_lines;
+               input [7:0] lines;
+               begin
+                       _spi_cs = 0;
+                       do_spi(`BB_WRITE | `BB_REG_SCSI_BASE | `BB_REG_ASSERT, val);
+                       do_spi(lines, val);                     
+                       _spi_cs = 1;
+                       #(SPI_HCLOCK*4);
+               end
+       endtask
+
+       task scsi_set;
+               input integer line;
+               begin
+                       scsi_asserts[line] = 1'b1;
+                       scsi_set_lines(scsi_asserts);
+               end             
+       endtask
+
+       task scsi_clear;
+               input integer line;
+               begin
+                       scsi_asserts[line] = 1'b0;
+                       scsi_set_lines(scsi_asserts);
+               end             
+       endtask
+       
+       task scsi_set_autocnt;
+               input integer cnt;
+               begin
+                       _spi_cs = 0;
+                       do_spi(`BB_WRITE | `BB_AUTOINC |
+                              `BB_REG_SCSI_BASE | `BB_REG_ACNT_HI, val);
+                       do_spi(cnt[15:8], val);
+                       do_spi(cnt[7:0], val);
+                       _spi_cs = 1;
+                       #(SPI_HCLOCK*4);
+               end                     
+       endtask
+
+       task scsi_set_data;
+               input [7:0] data;
+               begin
+                       _spi_cs = 0;
+                       do_spi(`BB_WRITE | `BB_REG_SCSI_BASE | `BB_REG_IDATA, val);
+                       do_spi(data, val);                      
+                       _spi_cs = 1;                    
+                       #(SPI_HCLOCK*4);
+               end             
+       endtask
+
+       task scsi_get_data;
+               output [7:0] val;
+               reg [7:0]    dummy;
+               begin
+                       _spi_cs = 0;
+                       do_spi(`BB_REG_SCSI_BASE | `BB_REG_ODATA, dummy);
+                       do_spi(8'hff, dummy);
+                       do_spi(8'hff, val);
+                       _spi_cs = 1;
+                       #(SPI_HCLOCK*4);
+               end             
+       endtask
+
+       task scsi_get_lines;
+               output [7:0] val;
+               reg [7:0]    dummy;
+               begin
+                       _spi_cs = 0;
+                       do_spi(`BB_REG_SCSI_BASE | `BB_REG_LINES, dummy);
+                       do_spi(8'hff, dummy);
+                       do_spi(8'hff, val);
+                       _spi_cs = 1;
+                       #(SPI_HCLOCK*4);
+               end             
+       endtask
+               
+       task scsi_do_cmd;
+               output integer len; 
+               output [7:0]   stat;
+               output         direction;
+               integer        i;
+
+               begin
+                       stat = 0;
+                       len = 0;
+                       direction = 0;                  
+                       case(scsi_cdb[0])
+                               'h12: begin /* INQUIRY */
+                                       direction = 1;
+                                       for (i=0; i < 256; i = i + 1)
+                                         scsi_buf[i] = 0;              
+                                       scsi_buf[4] = 32;
+                                       scsi_buf[8] = 'h46;
+                                       scsi_buf[9] = 'h4f;             
+                                       scsi_buf[10] = 'h4f;
+                                       scsi_buf[16] = 'h42;
+                                       scsi_buf[17] = 'h41;
+                                       scsi_buf[18] = 'h52;
+                                       len = scsi_cdb[4];
+                                       $display("CMD: INQUIRY 0x%h bytes", len);                                       
+                               end
+                               'h08: begin /* READ6 */
+                                       direction = 1;
+                                       len = 512 * scsi_cdb[4];
+                                       if (len == 0)
+                                         len = 512 * 256;                                      
+                                       $display("CMD: READ6 0x%h bytes", len);                                 
+                               end
+                               'h0a: begin /* WRITE6 */
+                                       direction = 0;
+                                       len = 512 * scsi_cdb[4];
+                                       if (len == 0)
+                                         len = 512 * 256;                                      
+                                       $display("CMD: WRITE6 0x%h bytes", len);                                        
+                               end
+                               'h28: begin /* READ10 */
+                                       direction = 1;
+                                       len = 512 * (scsi_cdb[7] * 256 + scsi_cdb[8]);                                  
+                                       $display("CMD: READ10 0x%h bytes", len);                                        
+                               end
+                               'h2a: begin /* WRITE10 */
+                                       direction = 0;
+                                       len = 512 * (scsi_cdb[7] * 256 + scsi_cdb[8]);                                  
+                                       $display("CMD: WRITE10 0x%h bytes", len);                                       
+                               end
+                               default: begin
+                                       stat = 1; /* CHECK CONDITION */
+                               end
+                       endcase
+               end
+       endtask // scsi_init_cmd
+
+       task do_scsi;
+               reg [7:0] lines;
+               reg [7:0] data;
+               integer   len, llen;
+               reg [7:0] stat;
+               reg       direction;
+               
+               begin
+                       scsi_get_lines(lines);
+                       $display("Waiting selection... Lines: %h", lines); 
+                       while (!lines[`BB_LINES_SEL] || lines[`BB_LINES_BSY])
+                         scsi_get_lines(lines);
+                       scsi_get_data(data);
+                       $display("Select ID: %h lines %h", data, lines);
+                       stat = 0;                       
+                       if (!data[6])
+                         $display("Wrong ID !");
+                       else begin
+                               $display("Setting BSY");
+                               scsi_set(`BB_ASSERT_BSY);
+                               $display("Waiting !SEL...");
+                               while(lines[`BB_LINES_SEL])
+                                 scsi_get_lines(lines);
+                               $display("Setting CMD, starting cmd transfer");
+                               scsi_set(`BB_ASSERT_CD);
+                               len = 1;
+                               i = 0;
+                               stat = 0;
+                               while(len) begin
+                                       scsi_set(`BB_ASSERT_REQ);
+                                       while(!lines[`BB_LINES_ACK])
+                                         scsi_get_lines(lines);
+                                       scsi_get_data(data);
+                                       $display("CMD %h = %h", i, data);                                       
+                                       scsi_cdb[i] = data;
+                                       len = len - 1;
+                                       i = i + 1;
+                                       if (i == 1) begin
+                                               /* get command size */
+                                               case (data)
+                                                       'h12: len =  6 - 1;
+                                                       'h08: len =  6 - 1;
+                                                       'h0a: len =  6 - 1;
+                                                       'h28: len = 10 - 1;
+                                                       'h2a: len = 10 - 1;
+                                                       default: begin
+                                                               len = 0;
+                                                               stat = 1;
+                                                       end
+                                               endcase
+                                               $display("CMD size adjust %h", len);
+                                       end
+                                       scsi_clear(`BB_ASSERT_REQ);
+                                       while(lines[`BB_LINES_ACK])
+                                         scsi_get_lines(lines);
+                               end
+
+                               scsi_do_cmd(len, stat, direction);
+                               scsi_clear(`BB_ASSERT_CD);
+                               if (direction)
+                                 scsi_set(`BB_ASSERT_IO);
+                               i = 0;                          
+                               while (len) begin
+                                       scsi_set(`BB_ASSERT_AUTO);      
+                                       /* Break up transfer */
+                                       if (len > 256)
+                                         llen = 256;
+                                       else
+                                         llen = len;
+                                       len = len - llen;
+                                       $display("Chunk of %h, remaining %h...", llen, len);
+                                       scsi_set_autocnt(llen);
+                                       _spi_cs = 0;
+                                       if (direction) begin
+                                               do_spi(`BB_WRITE | `BB_REG_SCSI_BASE | `BB_REG_IDATA, val);
+                                               while(llen != 0) begin
+                                                       $display("Writing byte %h", i);                                 
+                                                       do_spi(scsi_buf[i], val);                       
+                                                       wait (!scsi_hshake);
+                                                       wait (scsi_hshake);
+                                                       llen = llen - 1;
+                                                       i = i + 1;
+                                                       
+                                               end
+                                       end else begin
+                                               do_spi(`BB_REG_SCSI_BASE | `BB_REG_ODATA, val);
+                                               #160;
+                                               wait (scsi_hshake);
+                                               do_spi(8'hff, val);     
+                                               wait (!scsi_hshake);
+                                               $display("dummy1: %h", val);                            
+                                               wait (scsi_hshake);
+                                               do_spi(8'hff, val);                             
+                                               wait (!scsi_hshake);
+                                               $display("dummy2: %h", val);                            
+                                               wait (scsi_hshake);
+                                               do_spi(8'hff, val);                             
+                                               wait (!scsi_hshake);
+                                               $display("dummy3: %h", val);                            
+                                               while(llen != 0) begin
+                                                       wait (scsi_hshake) ;
+                                                       do_spi(8'hff, data);
+                                                       if (llen > 3)
+                                                         wait (!scsi_hshake);
+                                                       scsi_buf[i] = data;
+                                                       $display("DATA %h: %h", i, data);
+                                                       llen = llen - 1;
+                                                       i = i + 1;                                      
+                                               end
+                                       end                             
+                                       _spi_cs = 1;    
+                                       #(SPI_HCLOCK*4);
+                                       scsi_clear(`BB_ASSERT_AUTO);                            
+                               end                             
+                               $display("sending status %h", stat);                            
+                               scsi_set(`BB_ASSERT_CD);
+                               scsi_set(`BB_ASSERT_IO);
+                               scsi_set_data(stat);                                    
+                               scsi_set(`BB_ASSERT_REQ);
+                               while(!lines[`BB_LINES_ACK])
+                                 scsi_get_lines(lines);
+                               scsi_clear(`BB_ASSERT_REQ);
+                               while(lines[`BB_LINES_ACK])
+                                 scsi_get_lines(lines);
+                               scsi_set(`BB_ASSERT_MSG);
+                               $display("Sending message");
+                               scsi_set_data(0);                                       
+                               scsi_set(`BB_ASSERT_REQ);
+                               while(!lines[`BB_LINES_ACK])
+                                 scsi_get_lines(lines);
+                               scsi_clear(`BB_ASSERT_REQ);
+                               while(lines[`BB_LINES_ACK])
+                                 scsi_get_lines(lines);
+                               $display("Clearing phase & BSY");
+                               scsi_clear(`BB_ASSERT_CD);
+                               scsi_clear(`BB_ASSERT_IO);
+                               scsi_clear(`BB_ASSERT_MSG);
+                               scsi_clear(`BB_ASSERT_BSY);
+                       end // else: !if(!data[6])                      
+               end             
+       endtask // do_scsi
+       
+       task run_scsi;
+               begin
+                       while(1) begin                          
+                               wait (scsi_hshake)
+                                 do_scsi();
+                       end
+               end
+       endtask // run_scsi     
+       
+       task dump_latches;
+               reg [7:0] sval[0:7];            
+               begin
+                       /* Read latches */
+                       _spi_cs = 0;
+                       do_spi(`BB_AUTOINC | `BB_REG_CPU_BASE, val);
+                       do_spi(8'hff, val);
+                       do_spi(8'hff, sval[0]);
+                       do_spi(8'hff, sval[1]);
+                       do_spi(8'hff, sval[2]);
+                       do_spi(8'hff, sval[3]);
+                       do_spi(8'hff, sval[4]);
+                       do_spi(8'hff, sval[5]);
+                       _spi_cs = 1;
+                       #(SPI_HCLOCK*4);
+                       $display("RW:%b U:%b L:%b ADDR: %h DATA: %h",
+                                sval[5][0], sval[5][1], sval[5][2],
+                                { sval[0], sval[1], sval[2] },
+                                { sval[3], sval[4] });
+               end
+       endtask // dump_latches
+
+       task step;
+               input integer count;
+               begin
+                       repeat(count) begin
+                               /* Step CPU interface */
+                               _spi_cs = 0;
+                               do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val);
+                               do_spi(8'h82, val); /* RUN + STEP */
+                               _spi_cs = 1;
+                               #(SPI_HCLOCK*4);
+
+                               /* Should wait for completion... not useful
+                                * in practice as SPI is too slow
+                                */
+                       end // repeat (count)
+               end
+       endtask // step
+         
+
+       reg [7:0] val;
+//     reg [7:0] scopedump[0:(2048 * 8)-1];
+       integer   i;
+       
+       initial begin
+               nmi = 0;
+               _spi_cs = 1;
+               sdo = 1'bz;
+               sck = 0;
+               scsi_asserts = 0;
+
+               #100 _spi_cs = 0;
+               do_spi(`BB_AUTOINC | `BB_REG_CTRL_BASE, val);
+               do_spi(8'hff, val);
+               do_spi(8'hff, val);
+               $display("FPGA ID      = 0x%h", val);
+               do_spi(8'hff, val);
+               $display("FPGA Version = 0x%h", val);
+               do_spi(8'hff, val);
+               $display("FPGA Control = 0x%h", val);
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+
+               /* Un-reset CPU */
+               _spi_cs = 0;
+               do_spi(`BB_WRITE | `BB_REG_CTRL_BASE | 2, val);
+               do_spi(8'h01, val);
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+
+`ifdef foo     
+               $display("Running to puts...");
+               run_to('h40326c);
+               dump_latches();
+               repeat(1000) begin
+                       step(1);
+                       dump_latches();
+               end
+`endif
+
+               $display("Run....");
+               /* Run CPU interface */
+               _spi_cs = 0;
+               do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val);
+               do_spi(8'h02, val); /* RUN */
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+               run_scsi();
+`ifdef foo     
+               $display("Running to iwm_test1...");
+               run_to('h4010f0);
+               dump_latches();
+               repeat(100) begin
+                       step(1);
+                       dump_latches();
+               end
+`endif
+               
+`ifdef foo     
+               $display("Running to P_mBootBeep...");
+               run_to('h40028a);
+               dump_latches();
+               step(1);
+               dump_latches();
+               $display("Running to IWM init...");
+               run_to('h4000fc);
+               dump_latches();
+               step(1);
+               dump_latches();
+               $display("Running to P_ChecksumRomAndTestMemory...");
+               run_to('h400d76);
+               dump_latches();
+               step(1);
+               dump_latches();
+               $display("Running to P_BootPart2 (fake hit)...");
+               run_to('h400352);
+               dump_latches();
+               step(1);
+               dump_latches();
+               $display("Running to P_BootPart2...");
+               run_to('h400352);
+               dump_latches();
+               step(1);
+               dump_latches();
+
+               repeat(1000) begin
+                       step(1);
+                       dump_latches();
+               end
+`endif         
+               
+`ifdef __disabled__    
+               /* Write memory from 0x201000 using mem SPI (ROM) */
+               $display("Loading ROM...");             
+               rom_file = $fopenr("rom.bin");
+               rom_size = $fread(rom, rom_file, 0);
+               $fclose(rom_file);
+               _spi_cs = 0;
+               do_spi(`BB_WRITE | `BB_AUTOINC | `BB_REG_MEM_BSAE, val);
+               do_spi(8'h20, val); /* ROM base */
+               do_spi(8'h00, val);
+               do_spi(8'h00, val);
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+               _spi_cs = 0;
+               do_spi(`BB_WRITE | `BB_REG_MEM_BASE | 3, val);/
+               i = 0;          
+               repeat(rom_size) begin
+                       do_spi(rom[i], val);
+                       i = i+1;
+                       if ((i % 1024) == 0)
+                         $display("%h", i);                    
+               end
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+
+               $display("RAM dump...");
+               $display("%h %h %h %h %h %h %h %h",
+                        sval[0], sval[1], sval[2], sval[3],
+                        sval[4], sval[5], sval[6], sval[7]);
+               
+               /* Start the scope */
+               _spi_cs = 0;
+               do_spi(`BB_WRITE | `BB_REG_SCOPE_BASE, val);            
+               do_spi(8'h02, val);             
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+       
+               /* Un-reset CPU */
+               _spi_cs = 0;
+               do_spi(`BB_WRITE | `BB_REG_CTRL_BASE | 2, val);
+               do_spi(8'h01, val);
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+
+               /* Set breakpoint */
+               _spi_cs = 0;
+               do_spi(`BB_WRITE | `BB_AUTOINC | `BB_REG_CPU_BASE | 8, val);
+               do_spi(8'h00, val);
+               do_spi(8'h10, val);
+               do_spi(8'h22, val);
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+
+               /* Run CPU interface */
+               _spi_cs = 0;
+               do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val);
+               do_spi(8'h42, val); /* RUN + BKEN */
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+
+               repeat(10) begin
+                       /* Get CPU status */
+                       _spi_cs = 0;
+                       do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 7, val);
+                       do_spi(8'hff, val);
+                       do_spi(8'hff, val);
+                       _spi_cs = 1;
+                       #(SPI_HCLOCK*4);
+                       $display("CPU Stat: %h", val);
+               end             
+`endif
+               
+`ifdef foo     
+               $display("Steps:");             
+               repeat(100) begin
+                       /* Step CPU interface */
+                       _spi_cs = 0;
+                       do_spi(`BB_WRITE | `BB_REG_CPU_BASE | 6, val);
+                       do_spi(8'h82, val); /* RUN + STEP */
+                       _spi_cs = 1;
+                       #(SPI_HCLOCK*4);
+
+                       /* We shall wait for cycle to complete but we know
+                        * how slow our SPI is ...
+                        */
+                       /* Read latches */
+                       _spi_cs = 0;
+                       do_spi(`BB_AUTOINC | `BB_REG_CPU_BASE, val);
+                       do_spi(8'hff, val);
+                       do_spi(8'hff, sval[0]);
+                       do_spi(8'hff, sval[1]);
+                       do_spi(8'hff, sval[2]);
+                       do_spi(8'hff, sval[3]);
+                       do_spi(8'hff, sval[4]);
+                       do_spi(8'hff, sval[5]);
+                       _spi_cs = 1;
+                       #(SPI_HCLOCK*4);
+                       $display("RW:%b U:%b L:%b ADDR: %h DATA: %h",
+                                sval[5][0], sval[5][1], sval[5][2],
+                                { sval[0], sval[1], sval[2] },
+                                { sval[3], sval[4] });
+               end
+`endif
+
+`ifdef foo     
+               
+               $display("Reading 0x1020 via SPI initiaed read cycle:");
+               
+               /* Read value at 0x1020 */
+               _spi_cs = 0;
+               do_spi(8'hc8, val); /* b001_000 */
+               do_spi(8'h00, val);
+               do_spi(8'h10, val);
+               do_spi(8'h20, val);
+               do_spi(8'haa, val);
+               do_spi(8'h55, val);
+               do_spi(8'h01, val); /* _uds = 0, _lds = 0, r_w = 1 */
+               do_spi(8'h04, val); /* ctrl: cycle */
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+
+               /* Here too, shall wait but heh ! */
+
+               /* Read latches */
+               _spi_cs = 0;
+               do_spi(8'h48, val); /* b001_000 */
+               do_spi(8'hff, val);
+               do_spi(8'hff, sval[0]);
+               do_spi(8'hff, sval[1]);
+               do_spi(8'hff, sval[2]);
+               do_spi(8'hff, sval[3]);
+               do_spi(8'hff, sval[4]);
+               do_spi(8'hff, sval[5]);
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+               $display("RW:%b U:%b L:%b ADDR: %h DATA: %h",
+                        sval[5][0], sval[5][1], sval[5][2],
+                        { sval[0], sval[1], sval[2] },
+                        { sval[3], sval[4] });
+
+               /* Read memory from 0x1000 using mem SPI */
+               _spi_cs = 0;
+               do_spi(8'he0, val); /* b100_000 */
+               do_spi(8'h00, val);
+               do_spi(8'h10, val);
+               do_spi(8'h00, val);
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+               _spi_cs = 0;
+               do_spi(8'h23, val); /* b100_000 */
+               do_spi(8'hff, val);
+               do_spi(8'hff, val); /* add. dummy cycle for reads */
+               do_spi(8'hff, sval[0]);
+               do_spi(8'hff, sval[1]);
+               do_spi(8'hff, sval[2]);
+               do_spi(8'hff, sval[3]);
+               do_spi(8'hff, sval[4]);
+               do_spi(8'hff, sval[5]);
+               do_spi(8'hff, sval[6]);
+               do_spi(8'hff, sval[7]);
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+               
+               $display("RAM dump...");
+               $display("%h %h %h %h %h %h %h %h",
+                        sval[0], sval[1], sval[2], sval[3],
+                        sval[4], sval[5], sval[6], sval[7]);
+
+`endif
+               
+`ifdef foo     
+               $display("Run....");
+               
+               /* Run CPU interface */
+               _spi_cs = 0;
+               do_spi(8'h8e, val); /* b001_110 */
+               do_spi(8'h02, val); /* RUN */
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+`endif 
+               /* Wait a bit and stop the scope */
+               #4000000;
+
+`ifdef foo
+               _spi_cs = 0;
+               do_spi(`BB_WRITE | `BB_REG_SCOPE_BASE, val);            
+               do_spi(8'h00, val);             
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+`endif
+       
+`ifdef foo     
+               /* Dump scope output */
+               _spi_cs = 0;
+               do_spi(8'h3c, val);
+               do_spi(8'hff, val);
+               i = 0;          
+               repeat(2048) begin
+                       do_spi(8'hff, sval[0]);
+                       do_spi(8'hff, sval[1]);
+                       do_spi(8'hff, sval[2]);
+                       do_spi(8'hff, sval[3]);
+                       do_spi(8'hff, sval[4]);
+                       do_spi(8'hff, sval[5]);
+                       do_spi(8'hff, sval[6]);
+                       do_spi(8'hff, sval[7]);
+                       $display("%h %h %h %h %h %h %h %h",
+                                sval[0], sval[1], sval[2], sval[3],
+                                sval[4], sval[5], sval[6], sval[7]);
+               end
+               _spi_cs = 1;
+               #(SPI_HCLOCK*4);
+`endif         
+       end  
+            
+endmodule
diff --git a/fpga/sim_sram.v b/fpga/sim_sram.v
new file mode 100644 (file)
index 0000000..377c56f
--- /dev/null
@@ -0,0 +1,72 @@
+/* Behavioural of our SRAM chips */
+module sim_sram512x16(input [18:0]     addr,
+                     input             _cs,
+                     input             _oe,
+                     input             _we,
+                     input             _ub,
+                     input             _lb,
+                     inout [15:0]      data);
+       
+
+       reg             sel;
+       reg [15:0]      mem[0:'h7ffff];
+       reg [15:0]      dlatch;
+       wire [15:0]     dout;   
+
+       /* Simplified model, roughly based on a IS64LV6416L model adapted
+        * to look like our AS6C8016
+        */
+       parameter Taa   = 55;
+       parameter Toh   = 10;
+       parameter Tchz  = 20;
+       parameter Tas   = 0;
+       parameter Twhz  = 20;
+
+       wire            r_en = _we & (~_cs) & (~_oe);
+       wire            w_en = (~_we) & (~_cs) & ((~_ub) | (~_lb));
+       assign #(r_en ? Taa : Tchz) data = r_en ? dout : 16'bz;   
+       assign dout[ 7:0] = _lb ? 8'bz : mem[addr][ 7:0];
+       assign dout[15:8] = _ub ? 8'bz : mem[addr][15:8];
+
+       always@(addr or w_en) begin
+               #Tas
+                 if (w_en)
+                   #Twhz begin
+                           /* XXX Something's wrong with iverilog. It appears that
+                            * the if (w_en) above isn't enough, if I don't also test
+                            * w_en in the assignment belwow, it -will- write spuriously
+                            * to the RAMs even when w_en has never kicked for a while
+                            * and I really have no idea why. --BenH
+                            */
+                           mem[addr][ 7:0] = _lb | ~w_en ?mem[addr][ 7:0] : data[ 7:0];
+                           mem[addr][15:8] = _ub | ~w_en ?mem[addr][15:8] : data[15:8];
+                   end
+       end
+
+`ifdef foo
+       initial begin
+               
+               $display("initializing RAM...");
+               mem[0] = 0;
+               mem[1] = 'h2000 - 16;
+               mem[2] = 0;
+               mem[3] = 'h1000;
+               // 1000:       303c 000a       movew #10,%d0    
+               mem['h1000/2] = 'h303c;
+               mem['h1002/2] = 'h000a;
+               // 1004:       41fa 001a       lea %pc@(1020 <_buf>),%a0
+               mem['h1004/2] = 'h41fa;
+               mem['h1006/2] = 'h001a;                             
+               // 1008:       3080            movew %d0,%a0@
+               mem['h1008/2] = 'h3080;                             
+               // 100a:       0640 0001       addiw #1,%d0
+               mem['h100a/2] = 'h0640;
+               mem['h100c/2] = 'h0001;
+               // 100e:       6000 fff4       braw 1004
+               mem['h100e/2] = 'h6000;
+               mem['h1010/2] = 'hfff4;
+               $display("done");
+       end // initial begin
+`endif
+       
+endmodule
diff --git a/fpga/spi_backbus.v b/fpga/spi_backbus.v
new file mode 100644 (file)
index 0000000..9597474
--- /dev/null
@@ -0,0 +1,110 @@
+`timescale 1ns / 100ps
+
+/* SPI to backbus interface for minimigmac
+ *
+ * This layers on top of the spi_slave module. It provides a protocol
+ * which allows to read and write from of 8-bit wide, 6 addr bit bus
+ * which is connected to various modules in the system.
+ *
+ * The basic SPI protocol starts with one byte received by the FPGA,
+ * which is has the format <RW:1> <AI:1> <ADDR:6>
+ * 
+ * RW   : Read (0) / Write (1)
+ * AI   : Auto-increment
+ * ADDR : Register address
+ * 
+ * For writes, all subsequent bytes are directly relevant. For reads,
+ * the next byte after the read command shall be ignored, then subsequent
+ * bytes are relevant.
+ * 
+ * The backbus side protocol is:
+ * 
+ *  - Writes: at clock with strobe high and wr high, data and address
+ *            are stable and shall be latched.
+ *  - Reads : at clock with strobe high and wr low, address stable,
+ *            we latch data immediately.
+ */
+
+module spi_backbus
+(
+       /* Clock & Reset */
+       input                   sysclk,
+       input                   reset,
+       /* SPI signals (through to the SPI slave core) */
+       input                   sdi,
+       output                  sdo,
+       input                   sck,
+       input                   _scs,
+
+       /* Backbus interface */
+       output reg [5:0]        bb_addr,
+       output [7:0]            bb_wdata,
+       input [7:0]             bb_rdata,
+       output reg              bb_strobe,
+       output reg              bb_wr
+ );
+
+       /* SPI slave wires */
+       wire       spi_rx;
+       wire       spi_cmd;
+
+       /* Protocol internals */
+       reg        ainc;
+       reg        delayed_ainc;
+       reg        write;
+
+       /* Data read latch */
+       reg [7:0]  data;
+
+       /* Instanciate SPI slave interface */
+       spi_slave spi0(.sysclk(sysclk),
+                      .reset(reset),
+                      ._scs(_scs),
+                      .sdi(sdi),
+                      .sdo(sdo),
+                      .sck(sck),
+                      .wdata(data),
+                      .rdata(bb_wdata),
+                      .rx(spi_rx),
+                      .cmd(spi_cmd));
+
+       /* Data latch */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       data <= 0;
+               end else begin
+                       /* Latch read data at read strobe */
+                       if (bb_strobe && !bb_wr)
+                         data <= bb_rdata;
+               end
+       end
+
+       /* One state machine to rule them all coz I'm lazy */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       bb_addr <= 0;
+               end else begin
+                       /* Command byte. Establish addr, wr and set strobe on read */
+                       if (spi_rx && spi_cmd) begin
+                               bb_addr <= bb_wdata[5:0];
+                               delayed_ainc <= bb_wdata[6];
+                               ainc <= bb_wdata[7] ? 0 : bb_wdata[6];
+                               bb_wr <= bb_wdata[7];
+                               bb_strobe <= ~bb_wdata[7];
+                       end else if (spi_rx) begin
+                               /* Subsequent bytes */
+                               ainc <= delayed_ainc;
+                               if (ainc) begin
+                                       bb_addr <= bb_addr + 1;
+                               end
+                               /* New strobe */
+                               bb_strobe <= 1;                         
+                       end else begin
+                               bb_strobe <= 0;
+                       end             
+               end
+       end
+endmodule
+
+       
\ No newline at end of file
diff --git a/fpga/spi_slave.v b/fpga/spi_slave.v
new file mode 100644 (file)
index 0000000..9f91725
--- /dev/null
@@ -0,0 +1,159 @@
+`timescale 1ns / 100ps
+
+/* SPI slave module
+ *
+ * Expected configuration on the master is SPI mode 0
+ * (CPOL 0 CPHA 0)
+ *  - SCK idle state is 0
+ *  - We latch input on positive edges of SCK
+ *  - We setup output on negative edge of SCK
+ * 
+ * IE. For a PIC, this means setting:
+ *    CKP=0 CKE=1 SMP=0
+ */
+module spi_slave
+(
+   input               sysclk,
+   input               reset,  /* Async system reset */
+   input               _scs,
+   input               sdi,
+   output              sdo,
+   input               sck,
+   input [7:0]         wdata,  /* Output data */
+   output reg [7:0]    rdata,  /* Input data */
+   output              rx,     /* One-sysclk signal of a new byte */
+   output reg          cmd     /* Indicate first byte of rx, variable len */
+);
+
+       reg [6:0] in_sr;
+       reg [7:0] out_sr;
+       reg [2:0] cnt;  
+       reg       first;
+       reg       new_byte;
+       reg       rx1;
+       reg       rx2;
+       reg       rx3;
+       reg       tx;   
+
+       /* Bit counter */
+       always@(posedge sck or posedge _scs) begin
+               if (_scs) begin
+                       cnt <= 0;
+               end else begin
+                       cnt <= cnt + 1;
+               end
+       end
+       
+       /* Shift input, latch on positive edge of sck. We only shift in 7
+        * bits as we'll take sdi directly into the final latch.
+        */
+       always@(posedge sck) begin
+               if (!_scs) begin
+                       in_sr[6:0] <= { in_sr[5:0], sdi };
+               end
+       end
+
+       /* We latch the input byte so it remains stable for a while byte for
+        * consumption by sysclk domain.
+        */
+       always@(posedge sck) begin
+               if (!_scs && cnt == 3'b111) begin
+                       rdata[7:0] <= { in_sr[6:0], sdi };
+               end
+       end
+
+       /* Shift output. We latch "data" on bit 7 falling edge (so before we
+        * set new_byte and thus before rx catches up sysclk domain).
+        * 
+        * That means we are ahead by one bit, so we add a one bit delay latch
+        * on sdo
+        * 
+        */
+       always@(negedge sck or posedge _scs) begin
+               if (_scs) begin
+                       out_sr <= 8'hff;
+               end else begin          
+                       if (cnt == 3'b111) begin
+                               out_sr <= wdata;
+                       end else begin
+                               out_sr[7:0] <= { out_sr[6:0], 1'b0 };
+                       end                     
+               end
+       end
+
+       always@(negedge sck or posedge _scs) begin
+               if (_scs) begin
+                       tx <= 1;
+               end else begin
+                       tx <= out_sr[7];
+               end
+       end     
+       
+       /* We keep output hi-z if chip select not set. We do have a transcient
+        * undefined state between _scs assertion and the first clock, which
+        * I'm happy to ignore for now
+        */
+       assign sdo = _scs ? 1'bz : tx;
+
+       /* Now we need to generate rx. It's tricky because sck can/will stop
+        * immediately after the last bit.
+        * 
+        * What we do is we generate a signal "new_byte" at the same time as
+        * latching the input byte, which we clear half way through the next
+        * byte. We then double flip-flop synchronize that into sysclk domain
+        * where we do an edge detection. If we lose sck, this signal will
+        * remains set until we start a new transmission, but since the delay
+        * between two transmissions is unpredictable, we must make sure we
+        * don't clear it until half way through the new byte (ie, we keep it
+        * set even when SCS is gone). For the same reason we don't
+        * clear our input latch either when SCS is gone.
+        * 
+        * Now, the nasty thing is we need to reset that dude asynchronously
+        * from sysclk domain, which could probably use some sychronisation
+        * too. For now, we assume the sysclk reset happens long enough before
+        * sck toggles. This will do for us, but in a more complex system,
+        * some logic might be needed to ensure sck is effectively masked
+        * out until a few clocks after reset completes to avoid metastability
+        * 
+        * Note: FPGAs are nice, we could probably just rely on new_byte being
+        * 0 at powerup time :-)
+        */
+       always@(posedge sck or posedge reset) begin
+               if (reset) begin
+                       new_byte <= 0;
+               end else begin
+                       if (cnt == 3'b111) begin
+                               new_byte <= 1;
+                       end else if (cnt == 3'b011) begin
+                               new_byte <= 0;
+                       end
+               end             
+       end
+
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       rx1 <= 0;
+                       rx2 <= 0;
+                       rx3 <= 0;                       
+               end else begin
+                       rx1 <= new_byte;
+                       rx2 <= rx1;
+                       rx3 <= rx2;                     
+               end
+       end
+
+       assign rx = rx2 & !rx3;
+
+       /* "first" is set in sck domain during clocking of first byte,
+        * and flushed into "cmd" at bit 7, thus cmd moves along with
+        * the data itself and will be sampled with the data by the user.
+        */
+       always@(posedge sck or posedge _scs) begin
+               if (_scs) begin
+                       first <= 1;
+               end else if (cnt == 3'b111) begin
+                       first <= 0;
+                       cmd <= first;
+               end
+       end
+endmodule
diff --git a/fpga/via6522.v b/fpga/via6522.v
new file mode 100644 (file)
index 0000000..dfc54ef
--- /dev/null
@@ -0,0 +1,559 @@
+`timescale 1ns / 100ps
+
+/*
+ * VIA6522 module for minimigmac.
+ * 
+ * The processor interface part of the VIA modified to use my
+ * home made bus interface. The rest is hugely simplified to
+ * only operate in the modes used by a Macintosh. The shift
+ * register is not implemented as such, instead, we have a
+ * direct 8 bit bus to the keyboard interface.
+ *
+ * My understanding of the original Mac design is that the VIA
+ * two phase clock is fed from the original m68k's E line, thus
+ * ticks at 1/10 of the 7.82 Mhz CPU frequency.
+ * 
+ * In this variant, the register interface is clocked at the full
+ * internal system clock, and we generate artificially a 782 kHz
+ * enable signal from it to feed the timers. We don't aim for
+ * great precision here, so for now we simply divide 16.666 Mhz
+ * by 21 which gives us an approximate 793kHz.
+ * 
+ * In addition, to simplify the logic, the direction of the
+ * port A and B bits is hard wired and the reset values adapted
+ * to enable the ROM overlay at startup.
+ * 
+ */
+
+/* Register numbers */
+`define VIA_REG_ORB    4'h0
+`define VIA_REG_ORA    4'h1
+`define VIA_REG_DDRB   4'h2
+`define VIA_REG_DDRA   4'h3
+`define VIA_REG_T1CL   4'h4
+`define VIA_REG_T1CH   4'h5
+`define VIA_REG_T1LL   4'h6
+`define VIA_REG_T1LH   4'h7
+`define VIA_REG_T2CL   4'h8
+`define VIA_REG_T2CH   4'h9
+`define VIA_REG_SR     4'ha
+`define VIA_REG_ACR    4'hb
+`define VIA_REG_PCR    4'hc
+`define VIA_REG_IFR    4'hd
+`define VIA_REG_IER    4'he
+`define VIA_REG_ORA2   4'hf
+
+/* Interrupt bits */
+`define CA2_IRQ                0
+`define CA1_IRQ                1
+`define SR_IRQ         2
+`define CB2_IRQ                3
+`define CB1_IRQ                4
+`define T2_IRQ         5
+`define T1_IRQ         6
+  
+module via6522(input           sysclk,
+              input            reset,
+
+              /* Bus interface. 4-bit address, to be wired
+               * appropriately upstream (to A9..A12)
+               */
+              input            cs,
+              input            we,
+              input [3:0]      rs,
+              input [7:0]      wdata,
+              output [7:0]     rdata,
+              output           _irq,
+
+              /* Port A and B signals. Fixed direction. Port B bit
+               * 0 (RTC data) is duplicated for in/out.
+               */
+              input [7:7]      pa_in7,
+              output [6:0]     pa_out,
+              output [2:0]     pb_out2_0,
+              output           pb_out7,
+              input [6:3]      pb_in6_3,
+              input            pb_in0,
+              
+              /* CA1 and CA2 are inputs always */
+              input            ca1,
+              input            ca2,
+              
+              /* Pseudo shift-register keyboard interface */
+              input [7:0]      sr_in,
+              input            sr_in_strobe,
+              output [7:0]     sr_out,
+              output reg       sr_out_strobe);
+
+
+       /* Registers */
+       reg [7:0]  pa;
+       reg [7:0]  pb;
+       reg [7:0]  sr;
+       reg [7:0]  acr;
+       reg [6:0]  ier;
+       reg [6:0]  ifr;
+       reg [7:0]  pcr;
+       reg [15:0] t1;
+       reg [15:0] t1l;
+       reg [15:0] t2;
+       reg [7:0]  t2l;
+       reg        ddr_b0;
+       wire       pb0_v;
+       wire       ifr7;
+       reg        ca1_old;
+       reg        ca2_old;
+       reg        pb6_old;
+       reg        t1_armed;
+       reg        t2_armed;    
+       reg        t1_fired;
+       reg        t2_fired;    
+       reg [4:0]  ckdiv;
+       wire       ckhalf;
+       reg        t1pb7;       
+       wire       tstrobe;
+       wire       t2strobe;
+       wire       t1irq;
+       wire       t2irq;          
+
+       /* Register selects */
+       wire       rs_orb;
+       wire       rs_ora;
+       wire       rs_ddrb;
+       wire       rs_ddra;
+       wire       rs_t1cl;
+       wire       rs_t1ch;
+       wire       rs_t1ll;
+       wire       rs_t1lh;
+       wire       rs_t2cl;
+       wire       rs_t2ch;
+       wire       rs_sr;
+       wire       rs_acr;
+       wire       rs_pcr;
+       wire       rs_ifr;
+       wire       rs_ier;
+       wire       rs_ora2;
+
+       assign rs_orb  = cs && rs == `VIA_REG_ORB;
+       assign rs_ora  = cs && rs == `VIA_REG_ORA;
+       assign rs_ddrb = cs && rs == `VIA_REG_DDRB;
+       assign rs_ddra = cs && rs == `VIA_REG_DDRA;
+       assign rs_t1cl = cs && rs == `VIA_REG_T1CL;
+       assign rs_t1ch = cs && rs == `VIA_REG_T1CH;
+       assign rs_t1ll = cs && rs == `VIA_REG_T1LL;
+       assign rs_t1lh = cs && rs == `VIA_REG_T1LH;
+       assign rs_t2cl = cs && rs == `VIA_REG_T2CL;
+       assign rs_t2ch = cs && rs == `VIA_REG_T2CH;
+       assign rs_sr   = cs && rs == `VIA_REG_SR;
+       assign rs_acr  = cs && rs == `VIA_REG_ACR;
+       assign rs_pcr  = cs && rs == `VIA_REG_PCR;
+       assign rs_ifr  = cs && rs == `VIA_REG_IFR;
+       assign rs_ier  = cs && rs == `VIA_REG_IER;
+       assign rs_ora2 = cs && rs == `VIA_REG_ORA2;
+
+       /* Outputs & interrupt generation */
+       assign pa_out = pa[6:0] ;
+       assign pb_out2_0 = pb[2:0];
+       assign pb_out7 = acr[7] ? t1pb7 : pb[7];
+       assign sr_out = sr;
+       assign ifr7 = (ifr & ier) != 7'b0000000;
+       assign _irq = ~ifr7;
+
+       /* PB[0] value */
+       assign pb0_v = ddr_b0 ? pb[0] : pb_in0;
+       
+       /* Register reads */
+       assign rdata = rs_ora  ? { pa_in7, pa[6:0] }                    :
+                      rs_ora2 ? { pa_in7, pa[6:0] }                    :
+                      rs_orb  ? { pb[7], pb_in6_3, pb[2:1], pb0_v }    :
+                      rs_ddrb ? { 1'b1, 4'b0000, 2'b11, ddr_b0 }       :
+                      rs_ddra ? { 1'b0, 7'b1111111 }                   :
+                      rs_t1cl ? t1[7:0]                                :
+                      rs_t1ch ? t1[15:8]                               :
+                      rs_t1ll ? t1l[7:0]                               :
+                      rs_t1lh ? t1l[15:8]                              :
+                      rs_t2cl ? t2[7:0]                                :
+                      rs_t2ch ? t2[15:8]                               :
+                      rs_sr   ? sr                                     :
+                      rs_acr  ? acr                                    :
+                      rs_pcr  ? pcr                                    :
+                      rs_ifr  ? { ifr7, ifr }                          :
+                      rs_ier  ? { 1'b1, ier }                          :
+                      8'b11111111;     
+       
+       /* Write to ORA */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 pa <= 8'b11111111;
+               else if (we && (rs_ora || rs_ora2))
+                 pa <= wdata;
+       end
+
+       /* Write to ORB */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 pb <= 8'b11111111;
+               else if (we && rs_orb)
+                 pb <= wdata;          
+       end
+
+       /* Write to DDRA ignored */
+       /* Write to DDRB */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 ddr_b0 <= 1'b0;
+               else if (we && rs_orb)
+                 ddr_b0 <= wdata[0];
+       end
+
+       /* Write to ACR */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 acr <= 8'b0;
+               else if (we && rs_acr)
+                 acr <= wdata;
+       end
+
+       /* Write to PCR */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 pcr <= 8'b0;
+               else if (we && rs_pcr)
+                 pcr <= wdata;
+       end
+
+       /* Timers
+        *
+        * Note: When writing to TnCH, we load the timer immediately
+        * and start counting on the next tstrobe (ie. P2 clock), which
+        * means that depending on when the CPU hits, the first P2 period
+        * might be reduced to one sysclk ... this could be "fixed" by causing
+        * all VIA accesses to wait for a P2 clock to complete but I'm happy
+        * to ignore the "problem" for now.
+        * 
+        * Also, to keep things simple, PB7 goes up right after writing to
+        * T1CH, tho then follows proper periods (hopefully)
+        */
+
+       /* Clock division. We divide by 21 to get approx 793kHz */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 ckdiv <= 20;
+               else begin
+                       if (ckdiv == 0)
+                         ckdiv <= 20;
+                       else
+                         ckdiv <= ckdiv - 1;
+               end
+       end
+       assign tstrobe = (ckdiv == 0);
+       assign ckhalf = (ckdiv == 10);
+       
+       /* Timer 1 latches */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                       t1l <= 0;
+               else begin
+                       if (we && (rs_t1cl || rs_t1ll))
+                         t1l[7:0] <= wdata;                  
+                       if (we && (rs_t1ch || rs_t1lh))
+                         t1l[15:8] <= wdata;
+               end
+       end
+
+       /* Timer 1 counter */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                       t1 <= 0;
+               else begin
+                       /* Handle transfer from latch on write */
+                       if (we && rs_t1ch) begin
+                               t1[7:0] <= t1l[7:0];
+                               t1[15:8] <= wdata;
+                       end else if (tstrobe) begin
+                               /* In "free run", reload from latch
+                                *
+                                * XXX There's an inconsistency in the doco
+                                * as to whether we shall just roll over and
+                                * count in one-shot mode or whether we shall
+                                * -also- reload from the latch on T1 (it's
+                                * clear for T2). For now I just let it roll
+                                * over, we'll see if any SW gets upset
+                                */
+                               if (acr[6] && t1_fired)
+                                 t1 <= t1l;
+                               else
+                                 t1 <= t1 - 1;
+                       end
+               end
+       end
+
+       /* "armed" latch. This is set on a write and cleared when
+        * running out in "one shot" mode
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 t1_armed <= 0;
+               else begin
+                       /* Always armed when in continuous mode, else
+                        * arm when writing to T1CH
+                        */
+                       if ((we && rs_t1ch) || acr[6])
+                         t1_armed <= 1;
+                       else if (t1_fired && !acr[6])
+                         t1_armed <= 0;
+               end
+       end                     
+
+       /* "fired" latch. This is set on tstrobe with counter == 0
+        * and is used to do whatever has to be done (irq, pb7, ...)
+        * on the next half phase 2 clock. It's set for the duration
+        * of the next P2 period.
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 t1_fired <= 0;
+               else begin
+                       if (tstrobe)
+                         t1_fired <= t1_armed && t1 == 0;
+               end             
+       end
+
+       /* Generate PB7 output */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 t1pb7 <= 1;
+               else begin
+                       /* We are 0 when timer is armed and not fired yet */
+                       if (we && rs_t1ch)
+                         t1pb7 <= 0;
+                       /* We invert when fired */
+                       if (ckhalf && t1_fired)
+                         t1pb7 <= ~t1pb7;                      
+               end
+       end
+
+       /* T1 interrupt */
+       assign t1irq = ckhalf & t1_fired;
+
+       /* Timer 2
+        *
+        * Either one shot or count PB6 pulses (ie. hblank)
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 pb6_old <= 1'b0;              
+               else
+                 pb6_old <= pb_in6_3[6];
+       end
+       assign t2strobe = acr[5] ? (pb6_old & ~pb_in6_3[6]) : tstrobe;
+
+       /* Timer 2 latche (low only) */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                       t2l <= 0;
+               else begin
+                       if (we && rs_t2cl)
+                         t2l <= wdata;               
+               end
+       end
+
+       /* Timer 2 counter */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                       t2 <= 0;
+               else begin
+                       /* Handle transfer from latch on write */
+                       if (we && rs_t2ch) begin
+                               t2[7:0] <= t2l;
+                               t2[15:8] <= wdata;
+                       end else if (t2strobe)
+                         t2 <= t2 - 1;
+               end
+       end
+
+       /* T2 "armed" latch (see T1) */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 t2_armed <= 0;
+               else begin
+                       if (we && rs_t2ch)
+                         t2_armed <= 1;
+                       else if (t2_fired)
+                         t2_armed <= 0;
+               end
+       end                     
+
+       /* "fired" latch. This is set on tstrobe with counter == 0
+        * or in pulse counting right when hitting the 0 -> -1
+        * transitionand. It's set for the duration of the next P2 period
+        * in the former case, and for one sysclk in the later case.
+        */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 t2_fired <= 0;
+               else begin
+                       if (t2strobe)
+                         t2_fired <= t2_armed && t2 == 0;
+                       if (acr[5] && t2_fired)
+                         t2_fired <= 0;                        
+               end             
+       end
+
+       /* T2 Interrupt */
+       assign t2irq = acr[5] ? t2_fired : (t2_fired & ckhalf);
+       
+       /* Shift register
+        *
+        * CB1 is the kbd clock, CB2 the shift register,
+        * we don't implement them that way, instead we
+        * have a direct 8-bit bus to the PS2 conversion
+        * module. The Mac only uses these ACR modes:
+        * 111 used when sending to the keyboard
+        * 110 used to assert the data line to 0 to start comm
+        * 011 used when receiving from the keyboard
+        * 000 used when transitioning
+        * 
+        * We ignore the 110 mode as we don't need the
+        * keyboard to clock us. Thus we only care about
+        * mode 111 and 011. In the former, any write to
+        * sr results in an sr_out_strobe pulse to signal
+        * the keyboard. In mode 011, a pulse on sr_in_strobe
+        * results in updating the SR and eventually signaling
+        * an interrupt
+        */
+
+       /* Write to SR (including external input) */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 sr <= 8'b0;           
+               else begin
+                       if (we && rs_sr)
+                         sr <= wdata;
+                       if (acr[4:2] == 3'b011 && sr_in_strobe)
+                         sr <= sr_in;
+               end
+       end
+
+       /* Generate sr_out_strobe */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 sr_out_strobe <= 1'b0;                
+               else begin
+                       if (we && rs_sr && acr[4:2] == 3'b111)
+                         sr_out_strobe <= 1;
+                       else
+                         sr_out_strobe <= 0;   
+               end
+       end
+
+       /* CA1 / CA2 edge detection */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset) begin
+                       ca1_old <= 0;
+                       ca2_old <= 0;           
+               end else begin
+                       ca1_old <= ca1;
+                       ca2_old <= ca2;
+               end
+       end
+
+
+       /* Interrupts */
+
+       
+       /* Write to IER */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 ier <= 1'b0;          
+               else if (we && rs_ier) begin
+                       if (wdata[7])
+                         ier <= ier | wdata[6:0];
+                       else
+                         ier <= ier & ~wdata[6:0];
+               end
+       end
+
+       /* Handle IFR */
+       always@(posedge sysclk or posedge reset) begin
+               if (reset)
+                 ifr <= 8'b0;          
+               else begin
+                       /* First, write-1-to-clear */
+                       if (we && rs_ifr)
+                         ifr <= ifr & ~wdata[6:0];
+
+                       /* Now clear individual sources based on
+                        * register accesses
+                        */
+                       if (rs_ora && (pcr[3:1] == 3'b000 ||
+                                      pcr[3:1] == 3'b010))
+                         ifr[`CA2_IRQ] <= 0;
+                       if (rs_ora)
+                         ifr[`CA1_IRQ] <= 0;
+                       if (rs_sr)
+                         ifr[`SR_IRQ] <= 0;
+                       if (rs_orb && (pcr[7:5] == 3'b000 ||
+                                      pcr[7:5] == 3'b010))
+                         ifr[`CB2_IRQ] <= 0;
+                       if (rs_orb)
+                         ifr[`CB1_IRQ] <= 0;
+                       if (we && rs_t2ch)
+                         ifr[`T2_IRQ] <= 0;
+                       if ((!we) && rs_t2cl)
+                         ifr[`T2_IRQ] <= 0;
+                       if (we && (rs_t1ch || rs_t1lh))
+                         ifr[`T1_IRQ] <= 0;
+                       if ((!we) && rs_t1cl)
+                         ifr[`T1_IRQ] <= 0;
+
+                       /* Finally set sources based on relevant
+                        * events
+                        */
+
+                       /* -- SR interrupt --
+                        *
+                        /* Note that when shifting out, we strobe and
+                         * thus interrupt immediately when ACR is set
+                         * to 111 (shift under external clock). That is
+                         * sending commands to the keyboard.
+                         * 
+                         * When ACR is set to 110 (sending a 0 pulse to
+                         * the keyboard to wake it up), we just swallow
+                         * everything here and don't generate an interrupt,
+                         * the Mac ROM seems to be happy enough that way.
+                         */
+
+                       /* Shift out under control of external clock */
+                       if (we && rs_sr && acr[4:2] == 3'b111)
+                         ifr[`SR_IRQ] <= 1;
+
+                       /* Shift in under control of external clock */
+                       if (acr[4:2] == 3'b011 && sr_in_strobe)
+                         ifr[`SR_IRQ] <= 1;
+
+                       /* CA2 */
+                       if (pcr[3] == 0 &&
+                                   ((!pcr[2] &&  ca2_old && !ca2) ||
+                                    ( pcr[2] && !ca2_old &&  ca2)))
+                         ifr[`CA2_IRQ] <= 1;
+
+                       /* CA1 */
+                       if ((!pcr[0] &&  ca1_old && !ca1) ||
+                           ( pcr[0] && !ca1_old &&  ca1))
+                         ifr[`CA1_IRQ] <= 1;
+
+                       /* No CB1/CB2 on the mac ? Well they are kbd data
+                        * and clock, I don't think they are ever used, but
+                        * we may want to check...
+                        */
+
+                       /* Timer IRQs */
+                       if (t1irq)
+                         ifr[`T1_IRQ] <= 1;
+                       if (t2irq)
+                         ifr[`T2_IRQ] <= 1;
+               end
+       end
+endmodule
+
diff --git a/fpga/video.v b/fpga/video.v
new file mode 100644 (file)
index 0000000..bb4790c
--- /dev/null
@@ -0,0 +1,196 @@
+module video(input             pixclk,
+            input              reset,
+            input [15:0]       pixels,
+            output reg         req_pix,
+            input              ack_pix,
+            output             _hsync,
+            output             _vsync,
+            output [3:0]       red,
+            output [3:0]       green,
+            output [3:0]       blue,
+            output             hblank);
+
+       /* Max coordinate size in bits - 1 */
+       parameter csize = 9;
+
+       /* Fixed CRTC params for now. Based on a 640x480 VGA signal with
+        * added borders to center the Mac's 512x342.
+        * 
+        * The added border is 64 pixels left and right and 69 pixels top
+        * and bottom.
+        * 
+        * The constants are used as count-down and thus include a -1 to
+        * account for the 0 state.
+        */
+       parameter hdisp_frontp          =   8 - 1;
+       parameter hdisp_hsync           =  96 - 1;
+       parameter hdisp_backp           =  40 - 1;
+       parameter hdisp_lbord           =   8 + 64 - 1;
+       parameter hdisp_display         = 512 - 1;
+       parameter hdisp_rbord           =   8 + 64 - 1;
+
+       parameter vdisp_frontp          =   2 - 1;
+       parameter vdisp_vsync           =   2 - 1;
+       parameter vdisp_backp           =  25 - 1;
+       parameter vdisp_tbord           =   8 + 69 - 1;
+       parameter vdisp_display         = 342 - 1;
+       parameter vdisp_bbord           =   8 + 69 - 1;
+
+       /* Corresponding stages */
+       localparam hstage_frontp        = 0;
+       localparam hstage_hsync         = 1;
+       localparam hstage_backp         = 2;
+       localparam hstage_lbord         = 3;
+       localparam hstage_display       = 4;
+       localparam hstage_rbord         = 5;
+
+       localparam vstage_frontp        = 0;
+       localparam vstage_vsync         = 1;
+       localparam vstage_backp         = 2;
+       localparam vstage_tbord         = 3;
+       localparam vstage_display       = 4;
+       localparam vstage_bbord         = 5;
+
+       /* Running h/v counters */
+       reg [csize:0]           hcnt;
+       reg [csize:0]           vcnt;
+       reg [csize:0]           hnextcnt;
+       reg [csize:0]           vnextcnt;
+
+       /* State machines */
+       reg [2:0]               hstage;
+       reg [2:0]               vstage;
+
+       /* Output latches */
+       reg                     _hsync_r;
+       reg                     _vsync_r;
+       reg                     pix_r;
+
+       /* Pixel shift reg */
+       reg [15:0]              pix_shift;
+       wire                    vid_on;
+
+       /* Request/ack protocol helpers */
+       wire                    pix_req_reset;  
+
+       /* Obtain the new count for the next stage */
+       always@(hstage) begin
+               case(hstage)
+                       hstage_frontp:  hnextcnt = hdisp_hsync;
+                       hstage_hsync:   hnextcnt = hdisp_backp;
+                       hstage_backp:   hnextcnt = hdisp_lbord;
+                       hstage_lbord:   hnextcnt = hdisp_display;       
+                       hstage_display: hnextcnt = hdisp_rbord;
+                       hstage_rbord:   hnextcnt = hdisp_frontp;
+                       default:        hnextcnt = hdisp_frontp;
+               endcase
+       end
+       always@(vstage) begin
+               case(vstage)
+                       vstage_frontp:  vnextcnt = vdisp_vsync;
+                       vstage_vsync:   vnextcnt = vdisp_backp;
+                       vstage_backp:   vnextcnt = vdisp_tbord;
+                       vstage_tbord:   vnextcnt = vdisp_display;       
+                       vstage_display: vnextcnt = vdisp_bbord;
+                       vstage_bbord:   vnextcnt = vdisp_frontp;
+                       default:        vnextcnt = vdisp_frontp;
+               endcase
+       end
+
+       /* H counter */
+       always@(posedge pixclk or posedge reset) begin
+               if (reset) begin
+                       hcnt <= 0;
+                       hstage <= hstage_frontp;                        
+               end else begin
+                       if (hcnt == 0) begin
+                               hcnt <= hnextcnt;
+                               if (hstage == hstage_rbord)
+                                 hstage <= hstage_frontp;
+                               else
+                                 hstage <= hstage + 1;
+                       end else
+                         hcnt <= hcnt - 1;
+               end
+       end
+       
+       /* V counter */
+       always@(posedge pixclk or posedge reset) begin
+               if (reset) begin
+                       vcnt <= 0;
+                       vstage <= vstage_frontp;                        
+               end else begin
+                       if (hstage == hstage_rbord && hcnt == 0) begin
+                               if (vcnt == 0) begin
+                                       vcnt <= vnextcnt;
+                                       if (vstage == vstage_bbord)
+                                         vstage <= vstage_frontp;
+                                       else
+                                         vstage <= vstage + 1;
+                               end else begin
+                                       vcnt <= vcnt - 1;
+                               end
+                       end                     
+               end
+       end
+
+       /* Visible signal */
+       assign vid_on = hstage == hstage_display &&
+                       vstage == vstage_display;       
+
+       /* Pixel request, async clear */
+       assign pix_req_reset = reset | ack_pix;
+
+       /* Generate pixel request. NOTE: This relies on the display
+        * width being a multiple of 16
+        */
+       always@(posedge pixclk or posedge pix_req_reset) begin
+               if (pix_req_reset) begin
+                       req_pix <= 0;
+               end else begin
+                       if (vstage == vstage_display) begin
+                               if (hstage == hstage_backp && hcnt == 0) begin
+                                       req_pix <= 1;
+                               end else if (hstage == hstage_display &&
+                                        hcnt[3:0] == 4'b1111 &&
+                                            hcnt[csize:4]) begin
+                                       req_pix <= 1;
+                               end
+                       end
+                       
+               end
+       end     
+
+       /* Shift register */
+       always@(posedge pixclk or posedge reset) begin
+               if (reset) begin
+                       pix_shift <= 0;
+               end else begin
+                       if (hcnt[3:0] == 0)
+                         pix_shift <= pixels;
+                       else
+                         pix_shift[15:1] <= pix_shift[14:0];                   
+               end
+       end
+
+       /* Output latches */    
+       always@(posedge pixclk or posedge reset) begin
+               if (reset) begin
+                       _hsync_r <= 1;
+                       _vsync_r <= 1;
+                       pix_r <= 0;
+               end else begin
+                       _hsync_r <= ~(hstage == hstage_hsync);                  
+                       _vsync_r <= ~(vstage == vstage_vsync);
+                       pix_r <= vid_on & ~pix_shift[15];                       
+               end
+       end
+
+       /* Output wires */
+       assign _hsync = _hsync_r;
+       assign _vsync = _vsync_r;
+       assign red = { pix_r, pix_r, pix_r, pix_r };
+       assign green = { pix_r, pix_r, pix_r, pix_r };
+       assign blue = { pix_r, pix_r, pix_r, pix_r };
+       assign hblank = hstage != hstage_display;
+endmodule
diff --git a/fpga/xilinx/minimigmac.xise b/fpga/xilinx/minimigmac.xise
new file mode 100644 (file)
index 0000000..45a8591
--- /dev/null
@@ -0,0 +1,405 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
+<project xmlns="http://www.xilinx.com/XMLSchema" xmlns:xil_pn="http://www.xilinx.com/XMLSchema">
+
+  <header>
+    <!-- ISE source project file created by Project Navigator.             -->
+    <!--                                                                   -->
+    <!-- This file contains project source information including a list of -->
+    <!-- project source files, project and process properties.  This file, -->
+    <!-- along with the project source files, is sufficient to open and    -->
+    <!-- implement in ISE Project Navigator.                               -->
+    <!--                                                                   -->
+    <!-- Copyright (c) 1995-2011 Xilinx, Inc.  All rights reserved. -->
+  </header>
+
+  <version xil_pn:ise_version="13.1" xil_pn:schema_version="2"/>
+
+  <files>
+    <file xil_pn:name="../minimigmac.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="1"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="18"/>
+    </file>
+    <file xil_pn:name="../fpga_clocks.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="2"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="15"/>
+    </file>
+    <file xil_pn:name="../scope.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="3"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="7"/>
+    </file>
+    <file xil_pn:name="../spi_backbus.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="4"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="6"/>
+    </file>
+    <file xil_pn:name="../spi_slave.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="5"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="1"/>
+    </file>
+    <file xil_pn:name="../ctrl.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="6"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="16"/>
+    </file>
+    <file xil_pn:name="../cpu_intf.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="7"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="17"/>
+    </file>
+    <file xil_pn:name="../iwm.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="8"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="14"/>
+    </file>
+    <file xil_pn:name="../mem_intf.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="9"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="13"/>
+    </file>
+    <file xil_pn:name="../scc.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="10"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="8"/>
+    </file>
+    <file xil_pn:name="../ncr5380.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="11"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="12"/>
+    </file>
+    <file xil_pn:name="../via6522.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="12"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="5"/>
+    </file>
+    <file xil_pn:name="../addr_decode.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="13"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="3"/>
+    </file>
+    <file xil_pn:name="../video.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="14"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="4"/>
+    </file>
+    <file xil_pn:name="../rtc.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="15"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="9"/>
+    </file>
+    <file xil_pn:name="../ps2_mouse.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="16"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="10"/>
+    </file>
+    <file xil_pn:name="../ps2.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="17"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="2"/>
+    </file>
+    <file xil_pn:name="../ps2_kbd.v" xil_pn:type="FILE_VERILOG">
+      <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="18"/>
+      <association xil_pn:name="Implementation" xil_pn:seqID="11"/>
+    </file>
+    <file xil_pn:name="../minimigmac.ucf" xil_pn:type="FILE_UCF">
+      <association xil_pn:name="Implementation" xil_pn:seqID="0"/>
+    </file>
+  </files>
+
+  <properties>
+    <property xil_pn:name="Add I/O Buffers" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Allow Logic Optimization Across Hierarchy" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Allow SelectMAP Pins to Persist" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Allow Unexpanded Blocks" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Allow Unmatched LOC Constraints" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Allow Unmatched Timing Group Constraints" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Asynchronous To Synchronous" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Auto Implementation Compile Order" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Auto Implementation Top" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Automatic BRAM Packing" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Automatically Insert glbl Module in the Netlist" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Automatically Run Generate Target PROM/ACE File" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="BRAM Utilization Ratio" xil_pn:value="100" xil_pn:valueState="default"/>
+    <property xil_pn:name="Bring Out Global Set/Reset Net as a Port" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Bring Out Global Tristate Net as a Port" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Bus Delimiter" xil_pn:value="&lt;>" xil_pn:valueState="default"/>
+    <property xil_pn:name="CLB Pack Factor Percentage" xil_pn:value="100" xil_pn:valueState="default"/>
+    <property xil_pn:name="Case" xil_pn:value="Maintain" xil_pn:valueState="default"/>
+    <property xil_pn:name="Case Implementation Style" xil_pn:value="None" xil_pn:valueState="default"/>
+    <property xil_pn:name="Change Device Speed To" xil_pn:value="-4" xil_pn:valueState="default"/>
+    <property xil_pn:name="Change Device Speed To Post Trace" xil_pn:value="-4" xil_pn:valueState="default"/>
+    <property xil_pn:name="Combinatorial Logic Optimization" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Compile EDK Simulation Library" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Compile SIMPRIM (Timing) Simulation Library" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Compile UNISIM (Functional) Simulation Library" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Compile XilinxCoreLib (CORE Generator) Simulation Library" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Compile for HDL Debugging" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Configuration Clk (Configuration Pins)" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+    <property xil_pn:name="Configuration Pin Done" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+    <property xil_pn:name="Configuration Pin HSWAPEN" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+    <property xil_pn:name="Configuration Pin M0" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+    <property xil_pn:name="Configuration Pin M1" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+    <property xil_pn:name="Configuration Pin M2" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+    <property xil_pn:name="Configuration Pin Program" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+    <property xil_pn:name="Configuration Rate" xil_pn:value="Default (6)" xil_pn:valueState="default"/>
+    <property xil_pn:name="Correlate Output to Input Design" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Create ASCII Configuration File" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Create Binary Configuration File" xil_pn:value="true" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="Create Bit File" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Create I/O Pads from Ports" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Create IEEE 1532 Configuration File" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Create Logic Allocation File" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Create Mask File" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Create ReadBack Data Files" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Cross Clock Analysis" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="DCI Update Mode" xil_pn:value="As Required" xil_pn:valueState="default"/>
+    <property xil_pn:name="Decoder Extraction" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Delay Values To Be Read from SDF" xil_pn:value="Setup Time" xil_pn:valueState="default"/>
+    <property xil_pn:name="Device" xil_pn:value="xc3s400" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="Device Family" xil_pn:value="Spartan3" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="Device Speed Grade/Select ABS Minimum" xil_pn:value="-4" xil_pn:valueState="default"/>
+    <property xil_pn:name="Do Not Escape Signal and Instance Names in Netlist" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Done (Output Events)" xil_pn:value="6" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="Drive Done Pin High" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Enable BitStream Compression" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Enable Cyclic Redundancy Checking (CRC)" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Enable Debugging of Serial Mode BitStream" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Enable Hardware Co-Simulation" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Enable Internal Done Pipe" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Enable Message Filtering" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Enable Outputs (Output Events)" xil_pn:value="Default (5)" xil_pn:valueState="default"/>
+    <property xil_pn:name="Equivalent Register Removal XST" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Exclude Compilation of Deprecated EDK Cores" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Exclude Compilation of EDK Sub-Libraries" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Extra Effort" xil_pn:value="None" xil_pn:valueState="default"/>
+    <property xil_pn:name="Extra Effort (Highest PAR level only)" xil_pn:value="None" xil_pn:valueState="default"/>
+    <property xil_pn:name="FPGA Start-Up Clock" xil_pn:value="CCLK" xil_pn:valueState="default"/>
+    <property xil_pn:name="FSM Encoding Algorithm" xil_pn:value="Auto" xil_pn:valueState="default"/>
+    <property xil_pn:name="FSM Style" xil_pn:value="LUT" xil_pn:valueState="default"/>
+    <property xil_pn:name="Filter Files From Compile Order" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Flatten Output Netlist" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Functional Model Target Language ArchWiz" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+    <property xil_pn:name="Functional Model Target Language Coregen" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+    <property xil_pn:name="Functional Model Target Language Schematic" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Architecture Only (No Entity Declaration)" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Asynchronous Delay Report" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Clock Region Report" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Constraints Interaction Report" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Constraints Interaction Report Post Trace" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Datasheet Section" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Datasheet Section Post Trace" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Detailed MAP Report" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Multiple Hierarchical Netlist Files" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Post-Place &amp; Route Power Report" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Post-Place &amp; Route Simulation Model" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate RTL Schematic" xil_pn:value="Yes" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate SAIF File for Power Optimization/Estimation Par" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Testbench File" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Timegroups Section" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generate Timegroups Section Post Trace" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Generics, Parameters" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Global Optimization Goal" xil_pn:value="AllClockNets" xil_pn:valueState="default"/>
+    <property xil_pn:name="Global Set/Reset Port Name" xil_pn:value="GSR_PORT" xil_pn:valueState="default"/>
+    <property xil_pn:name="Global Tristate Port Name" xil_pn:value="GTS_PORT" xil_pn:valueState="default"/>
+    <property xil_pn:name="Hierarchy Separator" xil_pn:value="/" xil_pn:valueState="default"/>
+    <property xil_pn:name="ISim UUT Instance Name" xil_pn:value="UUT" xil_pn:valueState="default"/>
+    <property xil_pn:name="Ignore User Timing Constraints Map" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Ignore User Timing Constraints Par" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Implementation Top" xil_pn:value="Module|minimigmac" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="Implementation Top File" xil_pn:value="../minimigmac.v" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="Implementation Top Instance Path" xil_pn:value="/minimigmac" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="Include 'uselib Directive in Verilog File" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Include SIMPRIM Models in Verilog File" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Include UNISIM Models in Verilog File" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Include sdf_annotate task in Verilog File" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Incremental Compilation" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Insert Buffers to Prevent Pulse Swallowing" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Instantiation Template Target Language Xps" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+    <property xil_pn:name="JTAG Pin TCK" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+    <property xil_pn:name="JTAG Pin TDI" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+    <property xil_pn:name="JTAG Pin TDO" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+    <property xil_pn:name="JTAG Pin TMS" xil_pn:value="Pull Up" xil_pn:valueState="default"/>
+    <property xil_pn:name="Keep Hierarchy" xil_pn:value="No" xil_pn:valueState="default"/>
+    <property xil_pn:name="Language" xil_pn:value="VHDL" xil_pn:valueState="default"/>
+    <property xil_pn:name="Last Applied Goal" xil_pn:value="Balanced" xil_pn:valueState="default"/>
+    <property xil_pn:name="Last Applied Strategy" xil_pn:value="Xilinx Default (unlocked)" xil_pn:valueState="default"/>
+    <property xil_pn:name="Last Unlock Status" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Launch SDK after Export" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Library for Verilog Sources" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Load glbl" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Logical Shifter Extraction" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Manual Implementation Compile Order" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Map Effort Level" xil_pn:value="High" xil_pn:valueState="default"/>
+    <property xil_pn:name="Map Slice Logic into Unused Block RAMs" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Max Fanout" xil_pn:value="500" xil_pn:valueState="default"/>
+    <property xil_pn:name="Maximum Number of Lines in Report" xil_pn:value="1000" xil_pn:valueState="default"/>
+    <property xil_pn:name="Maximum Signal Name Length" xil_pn:value="20" xil_pn:valueState="default"/>
+    <property xil_pn:name="Move First Flip-Flop Stage" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Move Last Flip-Flop Stage" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Multiplier Style" xil_pn:value="Auto" xil_pn:valueState="default"/>
+    <property xil_pn:name="Mux Extraction" xil_pn:value="Yes" xil_pn:valueState="default"/>
+    <property xil_pn:name="Mux Style" xil_pn:value="Auto" xil_pn:valueState="default"/>
+    <property xil_pn:name="Netlist Hierarchy" xil_pn:value="As Optimized" xil_pn:valueState="default"/>
+    <property xil_pn:name="Netlist Translation Type" xil_pn:value="Timestamp" xil_pn:valueState="default"/>
+    <property xil_pn:name="Number of Clock Buffers" xil_pn:value="8" xil_pn:valueState="default"/>
+    <property xil_pn:name="Number of Paths in Error/Verbose Report" xil_pn:value="3" xil_pn:valueState="default"/>
+    <property xil_pn:name="Number of Paths in Error/Verbose Report Post Trace" xil_pn:value="3" xil_pn:valueState="default"/>
+    <property xil_pn:name="Optimization Effort" xil_pn:value="Normal" xil_pn:valueState="default"/>
+    <property xil_pn:name="Optimization Goal" xil_pn:value="Speed" xil_pn:valueState="default"/>
+    <property xil_pn:name="Optimization Strategy (Cover Mode)" xil_pn:value="Area" xil_pn:valueState="default"/>
+    <property xil_pn:name="Optimize Instantiated Primitives" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Bitgen Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Compiler Options" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Compiler Options Map" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Compiler Options Par" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Compiler Options Translate" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Compxlib Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Map Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other NETGEN Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Ngdbuild Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Place &amp; Route Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Simulator Commands Behavioral" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Simulator Commands Post-Map" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Simulator Commands Post-Route" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other Simulator Commands Post-Translate" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other XPWR Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Other XST Command Line Options" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Output Extended Identifiers" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Output File Name" xil_pn:value="minimigmac" xil_pn:valueState="default"/>
+    <property xil_pn:name="Overwrite Compiled Libraries" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Pack I/O Registers into IOBs" xil_pn:value="Auto" xil_pn:valueState="default"/>
+    <property xil_pn:name="Pack I/O Registers/Latches into IOBs" xil_pn:value="Off" xil_pn:valueState="default"/>
+    <property xil_pn:name="Package" xil_pn:value="pq208" xil_pn:valueState="default"/>
+    <property xil_pn:name="Perform Advanced Analysis" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Perform Advanced Analysis Post Trace" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Perform Timing-Driven Packing and Placement" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Place &amp; Route Effort Level (Overall)" xil_pn:value="High" xil_pn:valueState="default"/>
+    <property xil_pn:name="Place And Route Mode" xil_pn:value="Normal Place and Route" xil_pn:valueState="default"/>
+    <property xil_pn:name="Placer Effort Level (Overrides Overall Level)" xil_pn:value="None" xil_pn:valueState="default"/>
+    <property xil_pn:name="Port to be used" xil_pn:value="Auto - default" xil_pn:valueState="default"/>
+    <property xil_pn:name="Post Map Simulation Model Name" xil_pn:value="minimigmac_map.v" xil_pn:valueState="default"/>
+    <property xil_pn:name="Post Place &amp; Route Simulation Model Name" xil_pn:value="minimigmac_timesim.v" xil_pn:valueState="default"/>
+    <property xil_pn:name="Post Synthesis Simulation Model Name" xil_pn:value="minimigmac_synthesis.v" xil_pn:valueState="default"/>
+    <property xil_pn:name="Post Translate Simulation Model Name" xil_pn:value="minimigmac_translate.v" xil_pn:valueState="default"/>
+    <property xil_pn:name="Power Reduction Map" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Power Reduction Par" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Preferred Language" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+    <property xil_pn:name="Priority Encoder Extraction" xil_pn:value="Yes" xil_pn:valueState="default"/>
+    <property xil_pn:name="Produce Verbose Report" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Project Description" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Property Specification in Project File" xil_pn:value="Store all values" xil_pn:valueState="default"/>
+    <property xil_pn:name="RAM Extraction" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="RAM Style" xil_pn:value="Auto" xil_pn:valueState="default"/>
+    <property xil_pn:name="ROM Extraction" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="ROM Style" xil_pn:value="Auto" xil_pn:valueState="default"/>
+    <property xil_pn:name="Read Cores" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Regenerate Core" xil_pn:value="Under Current Project Setting" xil_pn:valueState="default"/>
+    <property xil_pn:name="Register Balancing" xil_pn:value="No" xil_pn:valueState="default"/>
+    <property xil_pn:name="Register Duplication" xil_pn:value="Off" xil_pn:valueState="default"/>
+    <property xil_pn:name="Register Duplication Xst" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Release Write Enable (Output Events)" xil_pn:value="Default (6)" xil_pn:valueState="default"/>
+    <property xil_pn:name="Rename Design Instance in Testbench File to" xil_pn:value="UUT" xil_pn:valueState="default"/>
+    <property xil_pn:name="Rename Top Level Architecture To" xil_pn:value="Structure" xil_pn:valueState="default"/>
+    <property xil_pn:name="Rename Top Level Entity to" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Rename Top Level Module To" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Report Fastest Path(s) in Each Constraint" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Report Fastest Path(s) in Each Constraint Post Trace" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Report Paths by Endpoint" xil_pn:value="3" xil_pn:valueState="default"/>
+    <property xil_pn:name="Report Paths by Endpoint Post Trace" xil_pn:value="3" xil_pn:valueState="default"/>
+    <property xil_pn:name="Report Type" xil_pn:value="Verbose Report" xil_pn:valueState="default"/>
+    <property xil_pn:name="Report Type Post Trace" xil_pn:value="Verbose Report" xil_pn:valueState="default"/>
+    <property xil_pn:name="Report Unconstrained Paths" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Report Unconstrained Paths Post Trace" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Reset DCM if SHUTDOWN &amp; AGHIGH performed" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Reset On Configuration Pulse Width" xil_pn:value="100" xil_pn:valueState="default"/>
+    <property xil_pn:name="Resource Sharing" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Retain Hierarchy" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Router Effort Level (Overrides Overall Level)" xil_pn:value="None" xil_pn:valueState="default"/>
+    <property xil_pn:name="Run Design Rules Checker (DRC)" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Run for Specified Time" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Run for Specified Time Map" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Run for Specified Time Par" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Run for Specified Time Translate" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Safe Implementation" xil_pn:value="No" xil_pn:valueState="default"/>
+    <property xil_pn:name="Security" xil_pn:value="Enable Readback and Reconfiguration" xil_pn:valueState="default"/>
+    <property xil_pn:name="Selected Simulation Root Source Node Behavioral" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Selected Simulation Root Source Node Post-Map" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Selected Simulation Root Source Node Post-Route" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Selected Simulation Root Source Node Post-Translate" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Selected Simulation Source Node" xil_pn:value="UUT" xil_pn:valueState="default"/>
+    <property xil_pn:name="Shift Register Extraction" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Show All Models" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Simulation Model Target" xil_pn:value="Verilog" xil_pn:valueState="default"/>
+    <property xil_pn:name="Simulation Run Time ISim" xil_pn:value="1000 ns" xil_pn:valueState="default"/>
+    <property xil_pn:name="Simulation Run Time Map" xil_pn:value="1000 ns" xil_pn:valueState="default"/>
+    <property xil_pn:name="Simulation Run Time Par" xil_pn:value="1000 ns" xil_pn:valueState="default"/>
+    <property xil_pn:name="Simulation Run Time Translate" xil_pn:value="1000 ns" xil_pn:valueState="default"/>
+    <property xil_pn:name="Simulator" xil_pn:value="ISim (VHDL/Verilog)" xil_pn:valueState="default"/>
+    <property xil_pn:name="Slice Packing" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Slice Utilization Ratio" xil_pn:value="100" xil_pn:valueState="default"/>
+    <property xil_pn:name="Specify 'define Macro Name and Value" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Specify Top Level Instance Names Behavioral" xil_pn:value="Default" xil_pn:valueState="default"/>
+    <property xil_pn:name="Specify Top Level Instance Names Post-Map" xil_pn:value="Default" xil_pn:valueState="default"/>
+    <property xil_pn:name="Specify Top Level Instance Names Post-Route" xil_pn:value="Default" xil_pn:valueState="default"/>
+    <property xil_pn:name="Specify Top Level Instance Names Post-Translate" xil_pn:value="Default" xil_pn:valueState="default"/>
+    <property xil_pn:name="Speed Grade" xil_pn:value="-4" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="Starting Placer Cost Table (1-100) Map" xil_pn:value="1" xil_pn:valueState="default"/>
+    <property xil_pn:name="Starting Placer Cost Table (1-100) Par" xil_pn:value="1" xil_pn:valueState="default"/>
+    <property xil_pn:name="Synthesis Tool" xil_pn:value="XST (VHDL/Verilog)" xil_pn:valueState="default"/>
+    <property xil_pn:name="Target Simulator" xil_pn:value="Please Specify" xil_pn:valueState="default"/>
+    <property xil_pn:name="Timing Mode Map" xil_pn:value="Non Timing Driven" xil_pn:valueState="default"/>
+    <property xil_pn:name="Timing Mode Par" xil_pn:value="Performance Evaluation" xil_pn:valueState="default"/>
+    <property xil_pn:name="Top-Level Module Name in Output Netlist" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Top-Level Source Type" xil_pn:value="HDL" xil_pn:valueState="default"/>
+    <property xil_pn:name="Trim Unconnected Signals" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Tristate On Configuration Pulse Width" xil_pn:value="0" xil_pn:valueState="default"/>
+    <property xil_pn:name="Unused IOB Pins" xil_pn:value="Pull Down" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use 64-bit PlanAhead on 64-bit Systems" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Clock Enable" xil_pn:value="Yes" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Project File Behavioral" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Project File Post-Map" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Project File Post-Route" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Project File Post-Translate" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Simulation Command File Behavioral" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Simulation Command File Map" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Simulation Command File Par" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Simulation Command File Translate" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Waveform Configuration File Behav" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Waveform Configuration File Map" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Waveform Configuration File Par" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Custom Waveform Configuration File Translate" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use LOC Constraints" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use RLOC Constraints" xil_pn:value="Yes" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Smart Guide" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Synchronous Reset" xil_pn:value="Yes" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Synchronous Set" xil_pn:value="Yes" xil_pn:valueState="default"/>
+    <property xil_pn:name="Use Synthesis Constraints File" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="User Browsed Strategy Files" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="UserID Code (8 Digit Hexadecimal)" xil_pn:value="0xFFFFFFFF" xil_pn:valueState="default"/>
+    <property xil_pn:name="VHDL Source Analysis Standard" xil_pn:value="VHDL-93" xil_pn:valueState="default"/>
+    <property xil_pn:name="Value Range Check" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="Verilog 2001 Xst" xil_pn:value="true" xil_pn:valueState="default"/>
+    <property xil_pn:name="Verilog Macros" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="Wait for DCI Match (Output Events) virtex2" xil_pn:value="Auto" xil_pn:valueState="default"/>
+    <property xil_pn:name="Wait for DLL Lock (Output Events)" xil_pn:value="Default (NoWait)" xil_pn:valueState="default"/>
+    <property xil_pn:name="Working Directory" xil_pn:value="." xil_pn:valueState="non-default"/>
+    <property xil_pn:name="Write Timing Constraints" xil_pn:value="false" xil_pn:valueState="default"/>
+    <property xil_pn:name="XOR Collapsing" xil_pn:value="true" xil_pn:valueState="default"/>
+    <!--                                                                                  -->
+    <!-- The following properties are for internal use only. These should not be modified.-->
+    <!--                                                                                  -->
+    <property xil_pn:name="PROP_BehavioralSimTop" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="PROP_DesignName" xil_pn:value="minimigmac" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="PROP_DevFamilyPMName" xil_pn:value="spartan3" xil_pn:valueState="default"/>
+    <property xil_pn:name="PROP_FPGAConfiguration" xil_pn:value="FPGAConfiguration" xil_pn:valueState="default"/>
+    <property xil_pn:name="PROP_PostMapSimTop" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="PROP_PostParSimTop" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="PROP_PostSynthSimTop" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="PROP_PostXlateSimTop" xil_pn:value="" xil_pn:valueState="default"/>
+    <property xil_pn:name="PROP_PreSynthesis" xil_pn:value="PreSynthesis" xil_pn:valueState="default"/>
+    <property xil_pn:name="PROP_intProjectCreationTimestamp" xil_pn:value="2010-10-22T13:02:34" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="PROP_intWbtProjectID" xil_pn:value="144A4261866D6D927EEA5C3F80F1E6AA" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="PROP_intWorkingDirLocWRTProjDir" xil_pn:value="Same" xil_pn:valueState="non-default"/>
+    <property xil_pn:name="PROP_intWorkingDirUsed" xil_pn:value="No" xil_pn:valueState="non-default"/>
+  </properties>
+
+  <bindings/>
+
+  <libraries/>
+
+  <autoManagedFiles>
+    <!-- The following files are identified by `include statements in verilog -->
+    <!-- source files and are automatically managed by Project Navigator.     -->
+    <!--                                                                      -->
+    <!-- Do not hand-edit this section, as it will be overwritten when the    -->
+    <!-- project is analyzed based on files automatically identified as       -->
+    <!-- include files.                                                       -->
+  </autoManagedFiles>
+
+</project>
diff --git a/pic/18f252_mod.lkr b/pic/18f252_mod.lkr
new file mode 100644 (file)
index 0000000..d5ace8b
--- /dev/null
@@ -0,0 +1,23 @@
+// File: 18f252.lkr
+// Sample linker script for the PIC18F252 processor
+
+// Not intended for use with MPLAB C18.  For C18 projects,
+// use the linker scripts provided with that product.
+
+// Modified for larger data bank to fit secbuf
+
+LIBPATH .
+
+CODEPAGE   NAME=page       START=0x0               END=0x7FFF
+CODEPAGE   NAME=idlocs     START=0x200000          END=0x200007       PROTECTED
+CODEPAGE   NAME=config     START=0x300000          END=0x30000D       PROTECTED
+CODEPAGE   NAME=devid      START=0x3FFFFE          END=0x3FFFFF       PROTECTED
+CODEPAGE   NAME=eedata     START=0xF00000          END=0xF000FF       PROTECTED
+
+ACCESSBANK NAME=accessram  START=0x0            END=0x7F
+DATABANK   NAME=gpr0       START=0x80           END=0xFF
+DATABANK   NAME=gpr1       START=0x100          END=0x1FF
+DATABANK   NAME=gpr2       START=0x200          END=0x2FF
+DATABANK   NAME=gpr3       START=0x300          END=0x3FF
+DATABANK   NAME=gpr45      START=0x400          END=0x5FF
+ACCESSBANK NAME=accesssfr  START=0xF80          END=0xFFF          PROTECTED
diff --git a/pic/Makefile b/pic/Makefile
new file mode 100644 (file)
index 0000000..9ad42b6
--- /dev/null
@@ -0,0 +1,28 @@
+OBJS = main.o hardware.o mmc.o fat16.o scsi.o
+
+LIBS  = /usr/share/sdcc/lib/pic16/libc18f.lib
+LIBS += /usr/share/sdcc/lib/pic16/libio18f252.lib
+
+SDCC_BASE_FLAGS =  -mpic16 -p18f252 -V --debug
+SDCC_CFLAGS = $(SDCC_BASE_FLAGS) --obanksel=2 --denable-peeps --optimize-cmp --optimize-df
+SDCC_LDFLAGS = $(SDCC_BASE_FLAGS) -Wl-c -Wl-m -Wl-s18f252_mod.lkr
+
+
+%.o : %.c
+       sdcc $(SDCC_CFLAGS) -c $^
+
+minimigmac.hex: $(OBJS) 18f252_mod.lkr
+       sdcc $(SDCC_LDFLAGS) -o$@ $(OBJS) $(LIBS)
+
+all: minimigmac.hex
+
+clean:
+       rm -rf *.o
+       rm -rf *.asm
+       rm -rf *.lst
+       rm -rf *.adb
+
+distclean: clean
+       rm -rf *~
+
+
diff --git a/pic/fat16.c b/pic/fat16.c
new file mode 100644 (file)
index 0000000..a47a71c
--- /dev/null
@@ -0,0 +1,542 @@
+/*
+Copyright 2005, 2006, 2007 Dennis van Weeren
+
+This file is part of Minimig
+
+Minimig is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+Minimig is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+This is a simple FAT16 handler. It works on a sector basis to allow fastest acces on disk
+images.
+
+Simplified from Minimig version
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "mmc.h"
+#include "fat16.h"
+#include "fat16_defs.h"
+
+/* Enable / Disable Debug info */
+#undef DEBUG_FAT
+
+/* Structure containing selected partition data */
+struct partition_info {
+       unsigned char   fatType;        // 0x10=FAT16, 0x20=FAT32
+       unsigned long   partStart;      // start LBA of partition
+       unsigned long   fatStart;       // start LBA of first FAT table
+       unsigned long   dataStart;      // start LBA of data field
+       unsigned long   rootDirCluster; // root directory cluster 
+       unsigned long   rootDirStart;   // start LBA of root directory table
+       unsigned short  rootDirEntries; // start LBA of root directory table
+       unsigned char   fatNo;          // number of FAT tables
+       unsigned char   clusterSize;    // size of a cluster in blocks
+       unsigned long   clusterMask;    // binary mask of cluster number
+};
+
+
+/* Open file info */
+struct file_info {
+       unsigned char   name[12];       // Short file name
+       unsigned char   attributes;     // File attributes
+       unsigned short  entry;          // File-entry index in directory table
+       unsigned long   sector;         // Sector index in file
+       unsigned long   len;            // File size
+       unsigned long   cluster;        // Current cluster
+       unsigned long   firstCluster;   // First file cluster
+};
+
+
+#define MAX_LFN_SIZE   128                             // Maximum supported long file name, default is 256
+
+/* Directory Seek Modes */
+#define DIRECTORY_BROWSE_START         0               // start search from beginning of directory
+#define        DIRECTORY_BROWSE_NEXT           2               // find next file in directory
+
+/* Sector buffer. Shared globally to save space on PIC */
+static unsigned long cached_cur_lba;
+
+/* Selected Partition */
+static struct partition_info fat_partition;
+
+static unsigned char longFilename[MAX_LFN_SIZE];       // Default long filename entry
+
+static struct file_info fat_cur_file;                  // global file handle
+static struct file_info fat_cur_dir;                   // global directory file handle
+
+// Long filename entry char pos
+static const unsigned char charLFNPos[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 };
+
+
+#ifdef DEBUG_FAT
+#define pr_debug(args...)      printf_tiny(args)
+#else
+#define pr_debug(args...)
+#endif
+
+/* Caching functions for MMC */
+static unsigned char cached_read(unsigned long lba)
+{
+       if (cached_cur_lba == lba)
+               return true;
+
+       if (!mmc_read(lba, secbuf))
+               return false;
+
+       cached_cur_lba = lba;
+       return true;
+}
+
+static unsigned char cached_write(unsigned long lba)
+{
+       cached_cur_lba = lba;
+       return mmc_write(lba, secbuf);
+}
+
+/* Calculate LBA from current file position cluster and sector */
+static unsigned long fat_get_lba(struct file_info near *file)
+{
+       unsigned long lba;
+
+       /* Calc sector to read, when first cluster is 0 it is root directory of FAT16 */
+       if (file->firstCluster == 0) {
+               lba = fat_partition.rootDirStart;
+               lba += file->sector;
+       } else {
+               lba = fat_partition.dataStart;                          // start of data in partition
+               lba += (file->cluster-2) * fat_partition.clusterSize;   // cluster offset
+               lba += file->sector & ~fat_partition.clusterMask;       // sector offset in cluster
+       }
+       return lba;
+}
+
+/* Find next file cluster from the FAT */
+static unsigned char fat_get_next_cluster(struct file_info near *file)
+{
+       unsigned short index;
+       unsigned long fatSectorLba;
+
+       /* calculate sector that contains FAT-link */
+       fatSectorLba = fat_partition.fatStart;
+
+       if (fat_partition.fatType & 0x10) {
+               /* FAT16 256 cluster infos in sector on disk */
+               fatSectorLba += (file->cluster) >> 8;
+
+               /* calculate offset */
+               index = (file->cluster) & 0xFF;
+               index <<= 1;
+       } else {
+               /* FAT32 128 cluster infos in sector on disk */
+               fatSectorLba += (file->cluster) >> 7;
+
+               /* FAT32 128 cluster infos in sector */
+               index = (file->cluster) & 0x7F;
+               index <<= 2;
+       }
+
+       /* Read sector of FAT */
+       if (!cached_read(fatSectorLba))
+               return false;
+
+       if (fat_partition.fatType & 0x10) {
+               /* get FAT-link */
+               file->cluster = *((unsigned short*)&secbuf[index]);
+
+               /* Exit if end of chain */
+               if (file->cluster == 0xffff)
+                       return false;
+       } else {
+               /* For FAT32 Read long instead short */
+               file->cluster = *((unsigned long*)&secbuf[index]);
+               file->cluster &= 0x0FFFFFFF;
+
+               /* Exit if end of chain */
+               if (file->cluster == 0x0fffffff)
+                       return false;
+       }
+       return true;
+}
+
+/* Point to next sector in file */
+static unsigned char fat_do_next_sector(struct file_info near *file)
+{
+       /* increment sector index */
+       file->sector++;
+
+       /* if we are now in another cluster, look up cluster */
+       if ((file->sector & ~fat_partition.clusterMask) == 0)
+               if (!fat_get_next_cluster(file))
+                       return false;
+
+       pr_debug(" n s=0x%lx c=%lx\r\n", file->sector, file->cluster);
+
+       return true;
+}
+
+/* Seek within a file (or directory) entry */
+static unsigned char fat_file_do_seek(struct file_info near *file, unsigned long sector)
+{
+       long currentClusterNo;
+       long clusterNo;
+
+       /* Calculate Current ClusterNo to avoid seek if new secor is on
+        * same cluster
+        */
+       currentClusterNo = (long)file->sector;
+       currentClusterNo /= (long)fat_partition.clusterSize;
+       
+       /* Sector in file to read */
+       file->sector = sector;
+
+       /* Calculate new ClusterNO in file */
+       clusterNo = (long)file->sector;
+       clusterNo /= (long)fat_partition.clusterSize;
+
+       /* Calc ClusterNo Difference */
+       currentClusterNo -= clusterNo; 
+       
+       /* Check if we are on same cluster */
+       if (currentClusterNo == 0) {
+               pr_debug(" S s=0x%lx c=%lx\r\n", file->sector, file->cluster);
+               return true;
+       }
+
+       /* Reset current cluster in file to first */
+       file->cluster = file->firstCluster;
+               
+       /* If first cluster in file exit */
+       if (clusterNo == 0)
+               return true;
+
+       /* Loop through cluster links */
+       while(--clusterNo >= 0) {
+               if (!fat_get_next_cluster(file))
+                       return false;
+               //              pr_debug(" -seek: clusterNo: 0x%lx", clusterNo);
+               //              pr_debug(" , cluster: 0x%lx\r\n", file->cluster);
+       }
+
+       pr_debug(" s s=0x%lx c=%lx\r\n", file->sector, file->cluster);
+       return true;
+}
+
+static unsigned char fat_process_dir_entry(struct file_info near *file, union FAT_directoryEntry * dirEntry)
+{
+       short i;
+       short char_offset;
+
+       /* Check if we are at directory end */
+       if (dirEntry->entry.shortName.name[0] == FAT_ENTRY_FREE) {
+               pr_debug("Entry free %d\r\n", file->entry);
+               return 0;       
+       }
+
+       /* Check if file deleted */
+       if (FAT_ENTRY_DELETED != dirEntry->entry.shortName.name[0]) {   
+               /* Check if longfilename */
+               if(FAT_ATTRIB_LFN_TEXT == dirEntry->entry.attributes) {
+                       /* Calc pos to copy part of LFN */
+                       char_offset = ((dirEntry->LFN.sequenceNo & 0x3f)-1) * 13;
+               
+                       /* Copy part of LFN */
+                       for(i = 0; i < 13 && (char_offset + i) < (MAX_LFN_SIZE - 1); i++)
+                               longFilename[char_offset + i] = dirEntry->bytes[charLFNPos[i]];
+               } else if (dirEntry->entry.attributes &
+                          (FAT_ATTRIB_HIDDEN | FAT_ATTRIB_SYSTEM | FAT_ATTRIB_VOLUME)) {
+                       pr_debug("FAT: Skiping entry no:%d attr:0x%02X\n",
+                                file->entry, dirEntry->entry.attributes);
+                       /* Clear longfile name just in case */
+                       memset(longFilename,0,MAX_LFN_SIZE);
+               } else {
+                       /* Copy name */
+                       memcpy(&file->name, &dirEntry->entry.shortName, 11);
+                       file->name[11] = 0x00; /* In theory not needed since never trashed */
+                       file->attributes = dirEntry->entry.attributes;
+                       file->len = dirEntry->entry.length; /* get length of file */
+                       file->firstCluster = (unsigned long)dirEntry->entry.firstClusterLow;    // get first cluster of file
+                       /* FAT32 high bytes of cluster */
+                       if (fat_partition.fatType & 0x20)
+                               file->firstCluster |= ((unsigned long)dirEntry->entry.firstClusterHigh) << 16;
+                       file->cluster = file->firstCluster; /* Copy current cluster to first cluster */
+                       file->sector = 0; /* Reset current sector index */
+
+                       /* Check if there is no LFN than copy short name as LFN */
+                       /* Skip entries that have LFN and entries starting with '.' (Parent and Current dir) */
+                       if(!longFilename[0] && (file->name[0] != '.')) {
+                               char_offset = 0;
+                               for(i=0; i < 11; i++) {
+                                       if(file->name[i] != ' ')
+                                               longFilename[char_offset++] = file->name[i];
+                                       if(7 == i)
+                                               longFilename[char_offset++] = '.';
+                               }
+                               /* Delete Last dot */
+                               if(longFilename[char_offset-1]=='.')
+                                       char_offset--;
+                               longFilename[char_offset] = 0x00;
+                       }
+                       return 1;                       
+               }
+       }
+       return 2;
+}
+
+/* Iterate directory entries */
+static unsigned char fat_get_dir_entry(struct file_info near *file, struct file_info near *dir,
+                                      unsigned char mode)
+{
+       short entryOffset;
+       char rc;
+
+       /* Clear long file name */
+       memset(longFilename,0,MAX_LFN_SIZE);
+
+       /* Check for iteration mode */
+       switch(mode) {
+       case DIRECTORY_BROWSE_NEXT:
+               file->entry++;
+               break;
+       case DIRECTORY_BROWSE_START:
+       default:
+               file->entry = 0;
+               break;
+       }
+       
+       /* Seek directory to selected file entry */
+       if(!fat_file_do_seek(dir, file->entry >> 4))
+               return false;
+
+       /* Infinite loop for search */
+       while(1) {
+               /* Read sector in buffer */
+               if (!cached_read(fat_get_lba(dir)))
+                       return false;
+
+               /* Calculate file entry offset in directory sector, 16 entries in sector 32 byte big */
+               entryOffset = (file->entry & 0xF) << 5;
+
+               /* Process directory entry */
+               rc = fat_process_dir_entry(file, (union FAT_directoryEntry *)(secbuf + entryOffset));
+               if (!rc)
+                       return false;
+               if (rc == 1)
+                       return true;
+
+               /* Go to next Fat Entry */
+               file->entry++;
+               
+               /* Check should we seek directory to next sector */
+               if (!(file->entry & 0xF))
+                       if (!fat_do_next_sector(dir))
+                               return false;
+       }
+}
+
+
+/* Move to root directory */
+static void fat_cd_root_dir(struct file_info near *dir)
+{
+       /* Clear Entry */
+       memset(dir, 0, sizeof(struct file_info));
+       dir->name[0] = '/';
+       dir->attributes = FAT_ATTRIB_DIR;
+       dir->firstCluster = fat_partition.rootDirCluster;
+       dir->cluster = dir->firstCluster;
+}
+
+/* Find file by name and open it, return len or -1 */
+long fat_open(const unsigned char *name)
+{
+       struct file_info near *file = &fat_cur_file;
+       unsigned char mode = DIRECTORY_BROWSE_START;
+       unsigned char i;
+
+       pr_debug("FAT: Open file: %s\n",name);
+       
+       while (fat_get_dir_entry(file, &fat_cur_dir, mode)) {
+               pr_debug(" found: %s...",file->name);
+               for(i=0; i<11; i++)
+                       if (file->name[i]!=name[i])
+                               break;
+               if (i==11) {
+                       pr_debug(" GOOD !\n"); 
+                       return file->len;
+               }
+               pr_debug("\n"); 
+               mode = DIRECTORY_BROWSE_NEXT;
+       }
+       
+       pr_debug("FAT: File not found\n");
+
+       return -1;
+}
+
+unsigned char fat_file_read(void)
+{
+       return cached_read(fat_get_lba(&fat_cur_file));
+}
+
+unsigned char fat_file_write(void)
+{
+       return cached_write(fat_get_lba(&fat_cur_file));
+}
+
+unsigned char fat_file_seek(unsigned long sector)
+{
+       return fat_file_do_seek(&fat_cur_file, sector);
+}
+unsigned char fat_file_next_sector(void)
+{
+       return fat_do_next_sector(&fat_cur_file);
+}
+void fat_inval_cache(void)
+{
+       cached_cur_lba = 0xffffffff;
+}
+
+/* fat_init()
+ *
+ * Checks if a card is present. if a card is present it will check for
+ * a valid FAT16 or FAT32 primary partition
+ */
+unsigned char fat_init(void)
+{
+       unsigned long fatsize;  // size of fat
+       unsigned long dirsize;  // size of directory region in sectors
+
+       struct MBR_Disk *mbr = (struct MBR_Disk *)secbuf;
+       struct FAT_Boot_Sector *boot = (struct FAT_Boot_Sector *)secbuf;
+
+       /* Invalidate cache */
+       cached_cur_lba = 0xffffffff;
+
+       /* Read partition sector */
+       if (!cached_read(0)) {
+               pr_debug("FAT: Error Unable to read partition sector\n");
+               return false;
+       }
+       
+       /* Check for signature */
+       if (mbr->signature != 0xAA55) { 
+               pr_debug("FAT: Error invalid FAT signature\n");
+               return false;
+       }
+
+       /* Check first partition type */
+       switch (mbr->partitions[0].Type) {
+               case PARTITION_TYPE_FAT16_32MB:
+               case PARTITION_TYPE_FAT16:
+                       /* First partition filesystem type: FAT16 */
+                       fat_partition.fatType = 0x10;
+                       /* Get start of first partition */
+                       fat_partition.partStart = mbr->partitions[0].LBAFirst;
+                       break;
+
+               case PARTITION_TYPE_FAT32:
+               case PARTITION_TYPE_FAT32_LBA:
+                       /* First partition filesystem type: FAT32 */
+                       fat_partition.fatType = 0x20;
+                       /* get start of first partition */
+                       fat_partition.partStart = mbr->partitions[0].LBAFirst;
+                       break;
+                       
+               default:
+                       pr_debug("FAT: Error no supported partition found\n");
+                       return false;
+       }
+
+       /* Read boot sector */
+       if (!cached_read(fat_partition.partStart)) {    
+               pr_debug("FAT: Error unable to read boot sector\n");
+               return false;
+       }
+
+       /* Check for near-jump or short-jump opcode */
+       if (0xE9 != boot->jumpInstruction[0] && 0xEB != boot->jumpInstruction[0]) {     
+               pr_debug("FAT: Error invalid 0x%x boot sector jump\n",
+                        boot->jumpInstruction[0]);
+               return false;
+       }
+
+       /* Check if blocksize is really 512 bytes */
+       if (0x200 != boot->bytesPerSector) {
+               pr_debug("FAT: Error block size not 512 bytes it is 0x%x\n",
+                        boot->bytesPerSector);
+               return false;
+       }
+
+       /* Check medium descriptor byte, must be 0xf8 for hard drive */
+       if (0xF8 != boot->mediaDescriptor) {
+               pr_debug("Error invalid media descriptor\n");
+               return false;
+       }
+       
+       /* Get cluster size */
+       fat_partition.clusterSize = boot->sectorsPerCluster;
+       /* calculate cluster mask */
+       fat_partition.clusterMask = ~(fat_partition.clusterSize-1);
+
+       
+       /* Calculate drive's parameters from bootsector, first up is size of directory */
+       /* Get Max ROOT Dir entries FAT16 only !!*/
+       fat_partition.rootDirEntries = boot->maxRootEntries;
+
+       /* Calculate start of FAT, size of FAT and number of FAT's */
+       fat_partition.fatStart = fat_partition.partStart + boot->reservedSectorCount;
+       fat_partition.fatNo = boot->noOfFATs;
+
+       /* When FAT16 */ 
+       if (0x10 == fat_partition.fatType) {
+               fatsize = boot->sectorsPerFAT;
+               // Fat 16 Root dir cluster
+               fat_partition.rootDirCluster = 0;
+               // calculate start of FAT16 ROOT directory 
+               fat_partition.rootDirStart = fat_partition.fatStart + (fat_partition.fatNo * fatsize);
+               // Calculate dire size in sectors
+               dirsize = ((fat_partition.rootDirEntries<<5)+511)>>9;
+               // calculate start of data
+               fat_partition.dataStart = fat_partition.rootDirStart + dirsize;
+       } else {
+               fatsize = boot->extParams.fat32Ext.sectorsPerFAT;
+               // calculate data start
+               fat_partition.dataStart = fat_partition.fatStart + (fat_partition.fatNo * fatsize);
+               // Fat 32 Root dir cluster
+               fat_partition.rootDirCluster = boot->extParams.fat32Ext.rootDirCluster; 
+               // Get FAT32 root dir start 
+               fat_partition.rootDirStart = fat_partition.dataStart +
+                       (fat_partition.rootDirCluster-2) * fat_partition.clusterSize;
+       }
+
+       /* Open Root Directory */
+       fat_cd_root_dir(&fat_cur_dir);
+       
+       pr_debug("FAT%d partition found.\n", fat_partition.fatType);
+       pr_debug("Fat Size:     %ld\n",fatsize);
+       pr_debug("No of fats:   %d\n",fat_partition.fatNo);
+       pr_debug("Fat start:    %ld\n", fat_partition.fatStart);
+       pr_debug("Direntries:   %d\n",fat_partition.rootDirEntries);
+       pr_debug("Dir start:    %ld\n", fat_partition.rootDirStart);
+       pr_debug("Data start:   %ld\n", fat_partition.dataStart);
+       pr_debug("Cluster size: %d\n",fat_partition.clusterSize);
+       pr_debug("Cluster mask: %lx\n",fat_partition.clusterMask);
+
+       return true;
+}
+
diff --git a/pic/fat16.h b/pic/fat16.h
new file mode 100644 (file)
index 0000000..2d4fea1
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef __FAT16_H__\r
+#define __FAT16_H__\r
+\r
+/* Global sector buffer, data for read/write actions is stored here\r
+ *\r
+ * Any operation (init, open, seek and read) will clobber it. If you\r
+ * modify this buffer content, you _MUST_ either follow with a\r
+ * fat_file_write or a fat_inval_cache _BEFORE_ any other open, read\r
+ * or seek operation\r
+ */\r
+extern unsigned char secbuf[512];\r
+\r
+/* Initialize, find first partition */\r
+unsigned char fat_init(void);\r
+\r
+/* Open a file by name, one open file at a time, replace the previous one */\r
+long fat_open(const unsigned char *name);\r
+\r
+/* Seek into file WARNING: Can clobber secbuf ! */\r
+unsigned char fat_file_seek(unsigned long sector);\r
+unsigned char fat_file_next_sector(void);\r
+\r
+/* Read and write current file sector */\r
+unsigned char fat_file_read(void);\r
+unsigned char fat_file_write(void);\r
+\r
+/* Invalidate sector cache */\r
+void fat_inval_cache(void);\r
+\r
+#endif /* __FAT16_H__ */\r
diff --git a/pic/fat16_defs.h b/pic/fat16_defs.h
new file mode 100644 (file)
index 0000000..7c4827b
--- /dev/null
@@ -0,0 +1,171 @@
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+/* Partition Type Defninition */
+#define PARTITION_TYPE_FREE            0x00    /* The partition table entry is not used. */
+#define PARTITION_TYPE_FAT12           0x01    /* The partition contains a FAT12 filesystem. */
+#define PARTITION_TYPE_FAT16_32MB      0x04    /* The partition contains a FAT16 filesystem with
+                                                  32MB maximum. */
+#define PARTITION_TYPE_EXTENDED                0x05    /* The partition is an extended partition with its own
+                                                  partition table. */
+#define PARTITION_TYPE_FAT16           0x06    /* The partition contains a FAT16 filesystem. */
+#define PARTITION_TYPE_FAT32           0x0b    /* The partition contains a FAT32 filesystem. */
+#define PARTITION_TYPE_FAT32_LBA       0x0c    /* The partition contains a FAT32 filesystem with LBA. */
+#define PARTITION_TYPE_FAT16_LBA       0x0e    /* The partition contains a FAT16 filesystem with LBA. */
+#define PARTITION_TYPE_EXTENDED_LBA    0x0f    /* The partition is an extended partition with LBA. */
+#define PARTITION_TYPE_UNKNOWN         0xff    /* The partition has an unknown type. */
+
+
+/* File Attributes */
+#define FAT_ATTRIB_READONLY    (1 << 0)        // The file is read-only.
+#define FAT_ATTRIB_HIDDEN      (1 << 1)        // The file is hidden.
+#define FAT_ATTRIB_SYSTEM      (1 << 2)        // The file is a system file.
+#define FAT_ATTRIB_VOLUME      (1 << 3)        // The file is empty and has the volume label as its name.
+#define FAT_ATTRIB_DIR         (1 << 4)        // The file is a directory.
+#define FAT_ATTRIB_ARCHIVE     (1 << 5)        // The file has to be archived.
+#define FAT_ATTRIB_LFN_TEXT    (FAT_ATTRIB_VOLUME | FAT_ATTRIB_SYSTEM | FAT_ATTRIB_HIDDEN |\
+                                FAT_ATTRIB_READONLY)   // 0x0F Long filename entry
+
+#define FAT_LFN_LAST_MASK      0x40            // Last long entry flag
+
+/* First Characters in Fat name */
+#define FAT_ENTRY_FREE                 0x00    /* Fat entry is free */
+#define FAT_ENTRY_KANJI                        0x05    /* Fat entry is kanji */
+#define FAT_ENTRY_PARENT_DIRECTORY     0x2E    /* Fat entry is directory or parent direcotry */
+#define FAT_ENTRY_DELETED              0xE5    /* Fat entry is deleted */
+
+
+/* Define partition structure */
+struct PRIMARY_Partition
+{
+       unsigned char Status;                           // Partition status
+       unsigned char CHSFirstBlock[3];
+       unsigned char Type;                             // Partition type
+       unsigned char CHSLastBlock[3];
+       unsigned long LBAFirst;                         // First LBA block
+       unsigned long LBABlocks;                        // Number of LBA blocks in partition
+};
+
+
+/* Define Master Boot Record */
+struct MBR_Disk
+{
+       unsigned char                   bootCode[440];  // Code Area
+       unsigned long                   diskSignature;  // Optional Disk signature
+       unsigned short                  reserved;       // Usually Nulls; 0x0000
+       struct PRIMARY_Partition        partitions[4];  // Table of primary partitions
+                                                       // (Four 16-byte entries, IBM Partition Table scheme)
+       unsigned short                  signature;      // MBR signature; 0xAA55
+};
+
+
+/* FAT 16 Extened bios parametars block */
+struct FAT16_ExtBiosParams
+{
+       unsigned char   physicalDriveNo;                // Physical drive number 
+       unsigned char   reserved;                       // Reserved ("current head") In Windows NT bit 0 is 
+                                                       // a dirty flag to request chkdsk at boot time.
+                                                       // bit 1 requests surface scan too.[28]
+       unsigned char   extBootSignature;               // Extended boot signature.Value is 0x29[27] or 0x28.
+       unsigned long   ID;                             // ID (serial number)
+       unsigned char   volumeLabel[11];                // Volume Label, padded with blanks (0x20).     
+       unsigned char   FATFileSystemType[8];           // FAT file system type, padded with blanks (0x20),
+                                                       // e.g.: "FAT12   ", "FAT16   ". This is not meant
+                                                       // to be used to determine drive type, however,
+                                                       // some utilities use it in this way.
+       unsigned char   OsBootCode[448];                // Operating system boot code
+};
+
+
+/* FAT32 Extended parametars block */
+struct FAT32_ExtBiosParams
+{
+       unsigned long   sectorsPerFAT;                  // Sectors per file allocation table 
+       unsigned short  flags;                          // FAT Flags 
+       unsigned short  version;                        // Version
+       unsigned long   rootDirCluster;                 // Cluster number of root directory start
+       unsigned short  fsInformationSector;            // Sector number of FS Information Sector 
+       unsigned short  bootSectorCopy;                 // Sector number of a copy of this boot sector 
+       unsigned char   reserved1[12];                  // Reserved 
+       unsigned char   physicalDriveNo;                // Physical drive number 
+       unsigned char   reserved2;                      // Reserved 
+       unsigned char   extBootSignature;               // Extended boot signature. 
+       unsigned long   ID;                             // ID (serial number) 
+       unsigned char   volumeLabel[11];                // Volume Label 
+       unsigned char   FATFileSystemType[8];           // FAT file system type: "FAT32   " 
+       unsigned char   OsBootCode[420];                // Operating system boot code 
+};
+
+
+/* Fat Boot Sector */
+struct FAT_Boot_Sector
+{
+       unsigned char   jumpInstruction[3];             // Jump instruction. This instruction will be executed and will skip past the rest of the (non-executable) header if the partition is booted from. See Volume Boot Record. If the jump is two-byte near jmp it is followed by a NOP instruction. 
+       unsigned char   oemName[8];                     // OEM Name (padded with spaces). This value determines in which system disk was formatted. MS-DOS checks this field to determine which other parts of the boot record can be relied on.[25][26] Common values are IBM  3.3 (with two spaces between the "IBM" and the "3.3"), MSDOS5.0 and MSWIN4.1 and mkdosfs. 
+       unsigned short  bytesPerSector;                 // Bytes per sector. A common value is 512, especially for file systems on IDE (or compatible) disks. The BIOS Parameter Block starts here. 
+       unsigned char   sectorsPerCluster;              // Sectors per cluster. Allowed values are powers of two from 1 to 128. However, the value must not be such that the number of bytes per cluster becomes greater than 32 KB. 
+       unsigned short  reservedSectorCount;            // Reserved sector count. The number of sectors before the first FAT in the file system image. Should be 1 for FAT12/FAT16. Usually 32 for FAT32. 
+       unsigned char   noOfFATs;                       // Number of file allocation tables. Almost always 2.
+       unsigned short  maxRootEntries;                 // Maximum number of root directory entries. Only used on FAT12 and FAT16, where the root directory is handled specially. Should be 0 for FAT32. This value should always be such that the root directory ends on a sector boundary (i.e. such that its size becomes a multiple of the sector size). 224 is typical for floppy disks. 
+       unsigned short  totalSectorsFAT16;              // Total sectors (if zero, use 4 byte value at offset totalSectorsFAT32) 
+       unsigned char   mediaDescriptor;                // Media descriptor 0xF8 Fixed disk (i.e. Hard disk).
+       unsigned short  sectorsPerFAT;                  // Sectors per File Allocation Table for FAT12/FAT16 
+       unsigned short  sectorsPerTrack;                // Sectors per track 
+       unsigned short  noHeads;                        // Number of heads 
+       unsigned long   hiddenSectors;                  // Hidden sectors 
+       unsigned long   totalSectorsFAT32;              // Total sectors (if greater than 65535; otherwise, see offset totalSectorsFAT16) 
+       union
+       {
+               struct FAT16_ExtBiosParams      fat16Ext;
+               struct FAT32_ExtBiosParams      fat32Ext;
+       } extParams;
+       unsigned short  signature;                      // Boot sector signature (0x55 0xAA)
+};
+
+
+// FAT Directory Entry
+struct FAT_dirEntryDefault
+{
+       struct
+       {
+               unsigned char name[8];          // File name
+               unsigned char ext[3];           // File name extension
+       } shortName;                            // Short file name
+       unsigned char attributes;               // File attributes
+       unsigned char reserved;                 // Reserved
+       unsigned char createTimeFineRes;        // Create time fine resolution 10ms
+       unsigned short createTime;              // Create time
+       unsigned short createDate;              // Create date
+       unsigned short lastAccessDate;          // Last access date
+       unsigned short firstClusterHigh;        // First cluster high two bytes (FAT32 only)
+       unsigned short modifiedTime;            // Last modified time
+       unsigned short modifiedDate;            // Last modified date
+       unsigned short firstClusterLow;         // First cluster low two bytes
+       unsigned long length;                   // File length
+};
+
+
+// FAT LFN Directory entry structure
+struct FAT_dirEntryLFN
+{
+       unsigned char   sequenceNo;             // Sequence number for long file name entry
+       unsigned short  fiveUTF16[5];           // Five UTF16 characters
+       unsigned char   attributes;             // Attributes always 0x0F
+       unsigned char   type;                   // LFN entry type always 0x00
+       unsigned char   checksum;               // Dos file name checksum
+       unsigned short  sixUTF16[6];            // Six UTF16 characters
+       unsigned short  firstCluster;           // First cluster always 0x0000
+       unsigned short  twoUTF16[2];            // Two UTF16 characters
+};
+
+
+// FAT file entry structure all possible structures combined
+union FAT_directoryEntry
+{
+       struct  FAT_dirEntryDefault     entry;          // Default File Entry
+       struct  FAT_dirEntryLFN         LFN;            // FAT LFN file entry structure
+       unsigned char                   bytes[32];      // Simple Byte Buffer
+};
+
+
+#endif /*  __FAT_DEFS_H__ */
diff --git a/pic/hardware.c b/pic/hardware.c
new file mode 100644 (file)
index 0000000..714b5b1
--- /dev/null
@@ -0,0 +1,247 @@
+/*\r
+Copyright 2005, 2006, 2007 Dennis van Weeren\r
+\r
+This file is part of Minimig\r
+\r
+Minimig is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 3 of the License, or\r
+(at your option) any later version.\r
+\r
+Minimig is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License\r
+along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+Hardware control routines\r
+\r
+27-11-2005     -started coding\r
+29-01-2006     -done a lot of work\r
+31-01-2006     -added key repeat\r
+06-02-2006     -took out all button handling stuff\r
+\r
+-- Goran Ljubojevic --\r
+2009-11-13     - OsdCommand added\r
+2009-11-21     - small cleanup\r
+2009-12-20     - systimer reset on every hardware init to support propper timings on reset\r
+2009-12-30     - Support for new FPGA firmware added in header file\r
+                       - GetFPGAStatus function added\r
+2010-01-29     - ResetFPGA() macro added to header file.\r
+2010-08-21     - YQ100818 FPGA core support\r
+2010-08-26     - Added firmwareConfiguration.h\r
+2010-09-09     - Added _SPI macro to help save space on multiple SPI calls\r
+2010-10-15      - Moved over to minimigmac, coding style changes, etc...\r
+*/\r
+\r
+#include <stdio.h>\r
+\r
+#include "hardware.h"\r
+\r
+static volatile unsigned short systimer;\r
+\r
+/*initialize hardware*/\r
+void hardware_init(void)\r
+{\r
+       /* Disable analog inputs */\r
+       ADCON1 = 0b00000110;\r
+\r
+       /* Initalize output register */\r
+       PORTA = 0b00100011;\r
+       PORTB = 0b00000000;\r
+       PORTC = 0b00010001;\r
+\r
+       /* Enable PORTB weak pullup */\r
+       INTCON2bits.RBPU = 0;\r
+\r
+       /* Initialize SPI to master mode 0, sample on posedge, 5Mhz */\r
+       SSPSTAT = 0x40;\r
+       SSPCON1 = 0x20;\r
+\r
+       /* Initialize input/ouput configuration */\r
+       TRISA = 0b11001100;\r
+       TRISB = 0b00101011;\r
+       TRISC = 0b10010000;\r
+\r
+       /* Initialize serial port */\r
+       SPBRG = 10;     /*115200 BAUD @ 20MHz*/\r
+       TXSTA = 0x24;\r
+       RCSTA = 0x90;\r
+\r
+       /* Init timer0, internal clk, prescaler 1:256 */\r
+       T0CON = 0xc7;\r
+       TMR0H = 0;\r
+       TMR0L = 0;\r
+\r
+       /* Enable interrupt for timer 0 */\r
+       INTCONbits.TMR0IE = 1;\r
+       INTCONbits.GIE = 1;\r
+}\r
+\r
+/* interrupt service routine */\r
+static void isr(void) interrupt 1\r
+{\r
+       unsigned short t;\r
+       static unsigned char button_state;\r
+\r
+       /* Clear timer 0 interrupt flag */\r
+       INTCONbits.TMR0IF = 0;\r
+\r
+       /* Set timer to timeout every 10ms\r
+        *  @20Mhz --> instruction = 200ns\r
+        *  200ns * 256 * 195  =  10ms\r
+        *\r
+        * Not sure we can just cast, so for now be gross\r
+        * since SDCC doesn't give us a 16-bit TMR0\r
+        */\r
+       t = TMR0L;\r
+       t |= ((unsigned short)TMR0H) << 8;\r
+       t -= 195;\r
+       TMR0H = t >> 8;\r
+       TMR0L = t & 0xff;\r
+\r
+       /* Increment system timer */\r
+       systimer++;\r
+\r
+       /* Debounce button */\r
+       if (check_button()) {\r
+               if (button_state < 10) {\r
+                       button_state++;\r
+                       if (button_state == 10) {\r
+                               putchar('[');\r
+                               putchar('N');\r
+                               putchar('M');\r
+                               putchar('I');\r
+                               putchar(']');\r
+                               NMI = 1;\r
+                       }\r
+               }\r
+       } else {\r
+               button_state = 0;\r
+               NMI = 0;\r
+       }\r
+}\r
+\r
+/* Get system timer + offset (handy for lots of things)*/\r
+unsigned short get_timer(unsigned short offset)\r
+{\r
+       unsigned short r;\r
+\r
+       /* Get system time SAFELY */\r
+       INTCONbits.GIE = 0;\r
+       r = systimer;\r
+       INTCONbits.GIE = 1;\r
+\r
+       /*add offset*/\r
+       r += offset;\r
+\r
+       return r;\r
+}\r
+\r
+/* Check if timer is past given time in <t>\r
+ * it may be maximum 30000 ticks in the future\r
+ */\r
+char check_timer(unsigned short t)\r
+{\r
+       /* calculate difference */\r
+       INTCONbits.GIE = 0;\r
+       t -= systimer;\r
+       INTCONbits.GIE = 1;\r
+\r
+       /* check if <t> has passed */\r
+       return t > 30000;\r
+}\r
+\r
+void wait_timer(unsigned short time)\r
+{\r
+    time = get_timer(time);\r
+    while (!check_timer(time))\r
+           ;\r
+}\r
+\r
+/* SPI-bus. Should inline ? */\r
+unsigned char do_spi(unsigned char d)\r
+{\r
+       SSPBUF = d;\r
+       while (!SSPSTATbits.BF);        /*Wait untill controller is ready*/\r
+       return(SSPBUF);                 /*Return with received value*/  \r
+}\r
+\r
+/* FPAG configuration serial interface */\r
+void shift_fpga(unsigned char byte)\r
+{\r
+       /*bit 0*/\r
+       DIN = 0;\r
+       CCLK = 0;\r
+       if (byte & 0x80)\r
+               DIN = 1;\r
+       CCLK = 1;\r
+\r
+       /*bit 1*/\r
+       DIN = 0;\r
+       CCLK = 0;\r
+       if (byte & 0x40)\r
+               DIN = 1;\r
+       CCLK = 1;\r
+\r
+       /*bit 2*/\r
+       DIN = 0;\r
+       CCLK = 0;\r
+       if (byte & 0x20)\r
+               DIN = 1;\r
+       CCLK = 1;\r
+\r
+       /*bit 3*/\r
+       DIN = 0;\r
+       CCLK = 0;\r
+       if (byte & 0x10)\r
+               DIN = 1;\r
+       CCLK = 1;\r
+\r
+       /*bit 4*/\r
+       DIN = 0;\r
+       CCLK = 0;\r
+       if (byte & 0x08)\r
+               DIN = 1;\r
+       CCLK = 1;\r
+\r
+       /*bit 5*/\r
+       DIN = 0;\r
+       CCLK = 0;\r
+       if (byte & 0x04)\r
+               DIN = 1;\r
+       CCLK = 1;\r
+\r
+       /*bit 6*/\r
+       DIN = 0;\r
+       CCLK = 0;\r
+       if (byte & 0x02)\r
+               DIN = 1;\r
+       CCLK = 1;\r
+\r
+       /*bit 7*/\r
+       DIN = 0;\r
+       CCLK = 0;\r
+       if (byte & 0x01)\r
+               DIN = 1;\r
+       CCLK = 1;\r
+}\r
+\r
+/* Put out a chacter to the serial port */\r
+void putchar(char ch) __wparam\r
+{\r
+       while(TXSTAbits.TRMT == 0);\r
+       TXREG = ch;\r
+       if (ch == 10) {\r
+               while(TXSTAbits.TRMT == 0);\r
+               TXREG = 13;\r
+       }\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
diff --git a/pic/hardware.h b/pic/hardware.h
new file mode 100644 (file)
index 0000000..60a40c4
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef __HARDWARE_H__\r
+#define __HARDWARE_H__\r
+\r
+#include <pic18f252.h>\r
+#include <stdbool.h>\r
+\r
+/* IO defines*/\r
+#define        PROG_B          TRISAbits.TRISA2        /* FPGA config */\r
+#define        PROG_B_VAL      PORTAbits.RA2           /* FPGA config */\r
+#define        DONE            PORTBbits.RB3           /* FPGA config */\r
+#define        INIT_B          PORTAbits.RA3           /* FPGA config */\r
+#define        CCLK            LATAbits.LATA5          /* FPGA config */\r
+#define        DIN             LATBbits.LATB7          /* FPGA config */\r
+#define        _SW0            PORTBbits.RB0           /* Switch input */\r
+#define        _SW1            PORTBbits.RB1           /* Switch input */\r
+#define        _M_CD           TRISAbits.TRISA0        /* mmc card clock disable */\r
+#define        _M_CS           PORTCbits.RC0           /* mmc card spi select */\r
+#define        _F_CS0          PORTAbits.RA1           /* FGPA spi0 select */\r
+#define        SCSI_HSHAKE     PORTBbits.RB5           /* SCSI data request */\r
+#define        NMI             PORTBbits.RB6           /* Programmer switch */\r
+\r
+/* Some IO accessors. static inline somewhat broken with sdcc so\r
+ * I use macros... sad\r
+ */\r
+#define spi_enable_fpga() do {         \\r
+       _F_CS0 = 0;                     \\r
+} while(0)\r
+\r
+#define spi_disable_fpga() do {                \\r
+       _F_CS0 = 1;                     \\r
+} while(0)\r
+\r
+#define spi_enable_osd() do {          \\r
+       _F_CS1 = 0;                     \\r
+} while(0)\r
+\r
+#define spi_disable_osd() do {         \\r
+       _F_CS1 = 1;                     \\r
+} while(0)\r
+\r
+#define spi_enable_sdcard() do {       \\r
+       _M_CD = 1;                      \\r
+       _M_CS = 0;                      \\r
+} while(0)\r
+\r
+#define spi_disable_sdcard() do {      \\r
+       _M_CS = 1;                      \\r
+       do_spi(0xff);                   \\r
+       _M_CD = 0;                      \\r
+} while(0)\r
+\r
+#define check_button(void) (!_SW0)\r
+\r
+#define reset_fpga() do {              \\r
+       PROG_B_VAL = 0;                 \\r
+       PROG_B = 0;                     \\r
+       PROG_B = 1;                     \\r
+} while(0)\r
+\r
+#define diskled_on() do {              \\r
+       PORTBbits.RB4 = 1;              \\r
+} while(0)\r
+\r
+#define diskled_off() do {             \\r
+       PORTBbits.RB4 = 0;              \\r
+} while(0)\r
+\r
+/* Functions */\r
+extern void hardware_init(void);\r
+\r
+extern unsigned short get_timer(unsigned short offset);\r
+extern char check_timer(unsigned short t);\r
+extern void wait_timer(unsigned short time);\r
+\r
+extern unsigned char do_spi(unsigned char data);\r
+extern void shift_fpga(unsigned char data);\r
+\r
+/* Backbus interface defines */\r
+#define BB_WRITE               0x80\r
+#define BB_AUTOINC             0x40\r
+\r
+#define BB_REG_CTRL_BASE        0x00\r
+#define BB_REG_CPU_BASE                0x10\r
+#define BB_REG_MEM_BASE                0x20\r
+#define BB_REG_IWM_BASE                0x28\r
+#define BB_REG_SCSI_BASE       0x30\r
+#define BB_REG_SCOPE_BASE      0x38\r
+\r
+\r
+#endif /* __HARDWARE_H__ */\r
+\r
diff --git a/pic/main.c b/pic/main.c
new file mode 100644 (file)
index 0000000..93d33fa
--- /dev/null
@@ -0,0 +1,833 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "hardware.h"
+#include "mmc.h"
+#include "fat16.h"
+#include "scsi.h"
+
+#define printf printf_tiny
+
+/* Sector buffer. Shared globally to save space on PIC
+ *
+ * XXX In this file to work around a missing bank select problem
+ */
+unsigned char secbuf[512];
+
+static char hex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c',
+                    'd','e','f'};
+
+/* Temp buffer used for tests */
+static unsigned char c[10];
+
+static unsigned char load_file(char *name, unsigned long addr)
+{
+       unsigned short t;
+       unsigned short n;
+       unsigned char *ptr = secbuf;
+       long size;
+
+       size = fat_open(name);
+       if (size < 0) {
+               printf("File %s not found !\n", name);
+               return false;
+       }
+       fat_file_seek(0);
+
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_AUTOINC | BB_REG_MEM_BASE);
+       do_spi((addr >> 16) & 0xff);
+       do_spi((addr >>  8) & 0xff);
+       do_spi((addr      ) & 0xff);
+       spi_disable_fpga();
+
+       t = 0;
+       n = (size + 7) >> 3;
+       do {
+               /* read sector if 512 (64*8) bytes done */
+               if ((t & 0x3f) == 0) {
+                       if ((t>>9)&1)
+                               diskled_on();
+                       else
+                               diskled_off();
+                       putchar('*');
+                       if (!fat_file_read()) {
+                               printf("Reading failed !\n");
+                               return false;
+                       }
+                       spi_enable_fpga();
+                       do_spi(BB_WRITE | BB_REG_MEM_BASE | 3);
+                       ptr = secbuf;
+               }
+
+               /* send data in packets of 8 bytes */
+               do_spi(*(ptr++));
+               do_spi(*(ptr++));
+               do_spi(*(ptr++));
+               do_spi(*(ptr++));
+               do_spi(*(ptr++));
+               do_spi(*(ptr++));
+               do_spi(*(ptr++));
+               do_spi(*(ptr++));
+               t++;
+
+               /* read next sector if 512 (64*8) bytes done */
+               if ((t & 0x3f) == 0) {
+                       spi_disable_fpga();
+                       fat_file_next_sector();
+               }
+               if ((t & 0x7ff) == 0)
+                       putchar(10);
+       } while (t < n);
+       spi_disable_fpga();
+
+       putchar(10);
+
+       return true;
+}
+
+static unsigned char configure_fpga(void)
+{
+       unsigned short t;
+       unsigned short n;
+       unsigned char *ptr = secbuf;
+       long size;
+
+       /* Reset FGPA configuration sequence */
+       reset_fpga();
+
+       // now wait for INIT to go high
+       t = 50000;
+       while (!INIT_B) {
+               if (--t==0) {
+                       printf("FPGA init is NOT high!\n");
+                       return false;
+               }
+       }
+       if (DONE) {
+               printf("FPGA done is high before configuration!\n");
+               return false;
+       }
+
+       /* Open bitstream file */
+       size = fat_open("MIMIGMACBIN");
+       if (size < 0) {
+               printf("No FPGA configuration file found!\n");
+               return false;
+       }
+       fat_file_seek(0);
+
+       /* Send all bytes to FPGA in loop */
+       t = 0;
+       n = size >> 3;
+       do {
+               /* read sector if 512 (64*8) bytes done */
+               if ((t & 0x3f) == 0) {
+                       if ((t>>9)&1)
+                               diskled_on();
+                       else
+                               diskled_off();
+                       putchar('*');
+                       if (!fat_file_read()) {
+                               printf("Reading failed !\n");
+                               return false;
+                       }
+                       ptr = secbuf;
+               }
+
+               /* send data in packets of 8 bytes */
+               shift_fpga(*(ptr++));
+               shift_fpga(*(ptr++));
+               shift_fpga(*(ptr++));
+               shift_fpga(*(ptr++));
+               shift_fpga(*(ptr++));
+               shift_fpga(*(ptr++));
+               shift_fpga(*(ptr++));
+               shift_fpga(*(ptr++));
+               t++;
+
+               /* read next sector if 512 (64*8) bytes done */
+               if ((t & 0x3f) == 0)
+                       fat_file_next_sector();
+               if ((t & 0x7ff) == 0)
+                       putchar(10);
+       } while (t < n);
+
+       shift_fpga(0xff);
+       shift_fpga(0xff);
+       shift_fpga(0xff);
+       shift_fpga(0xff);
+       shift_fpga(0xff);
+       shift_fpga(0xff);
+       shift_fpga(0xff);
+       shift_fpga(0xff);
+       shift_fpga(0xff);
+       shift_fpga(0xff);
+
+       diskled_off();
+
+       /* check if DONE is high */
+       if (DONE) {
+               shift_fpga(0xff);
+               shift_fpga(0xff);
+               shift_fpga(0xff);
+               shift_fpga(0xff);
+               shift_fpga(0xff);
+               shift_fpga(0xff);
+               shift_fpga(0xff);
+               shift_fpga(0xff);
+               shift_fpga(0xff);
+               shift_fpga(0xff);
+               putchar(10);
+               return true;
+       }
+
+       printf("FPGA done is NOT high!\n");
+       return false;
+}
+
+static void print_latches(void)
+{
+       printf("  RW:%d U:%d L:%d ADDR: %x %x %x DATA: %x %x\n",
+              !!(c[5] & 0x01),
+              !!(c[5] & 0x02),
+              !!(c[5] & 0x04),
+              c[0], c[1], c[2], c[3], c[4]);
+}
+
+#if 1
+static void dump_scope(void)
+{
+       unsigned short i;
+
+       /* Dump scope output */
+       spi_enable_fpga();
+       do_spi(BB_AUTOINC | BB_REG_SCOPE_BASE | 2);
+       do_spi(0xff);
+       c[0] = do_spi(0xff);
+       c[1] = do_spi(0xff);
+       printf("sample count: %x %x\n", c[0], c[1]);
+       spi_disable_fpga();
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_REG_SCOPE_BASE | 4);
+       do_spi(0xff);
+       spi_disable_fpga();
+
+       spi_enable_fpga();
+       do_spi(BB_REG_SCOPE_BASE | 4);
+       do_spi(0xff);
+       //      for (i = 0; i < 0x200; i++) {
+       for (i = 0; i < (32768/8); i++) {
+               c[0] = do_spi(0xff);
+               c[1] = do_spi(0xff);
+               c[2] = do_spi(0xff);
+               c[3] = do_spi(0xff);
+               c[4] = do_spi(0xff);
+               c[5] = do_spi(0xff);
+               c[6] = do_spi(0xff);
+               c[7] = do_spi(0xff);
+               printf("%x: %x %x %x %x %x %x %x %x\n", i * 8,
+                      c[0], c[1], c[2], c[3],
+                      c[4], c[5], c[6], c[7]);
+       }
+       spi_disable_fpga();
+}
+#endif
+
+static unsigned char check_fpga(void)
+{
+       spi_enable_fpga();
+       do_spi(BB_AUTOINC | BB_REG_CTRL_BASE);
+       do_spi(0xff);   /* pad */
+       c[0] = do_spi(0xff);    /* Read data 0 : ID */
+       c[1] = do_spi(0xff);    /* Read data 1 : Version */
+       spi_disable_fpga();
+
+       printf("FPGA ID %x Version %d\n", c[0], c[1]);
+       if (c[0] != 0x42 && c[1] != 1) {
+               printf("Unsupported !\n");
+               return false;
+       }
+
+       /* Test CPU inteface latches */
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_AUTOINC | BB_REG_CPU_BASE);
+       do_spi(0xde);
+       do_spi(0xad);
+       do_spi(0xbf);
+       do_spi(0xf0);
+       do_spi(0x0d);
+       do_spi(0xff);
+       spi_disable_fpga();
+
+       spi_enable_fpga();
+       do_spi(BB_AUTOINC | BB_REG_CPU_BASE);
+       do_spi(0xff);
+       c[0] = do_spi(0xff);
+       c[1] = do_spi(0xff);
+       c[2] = do_spi(0xff);
+       c[3] = do_spi(0xff);
+       c[4] = do_spi(0xff);
+       c[5] = do_spi(0xff);
+       spi_disable_fpga();
+
+       if (c[0] != 0xde || c[1] != 0xad || c[2] != 0xbe ||
+           c[3] != 0xf0 || c[4] != 0x0d) {
+               printf("Latches test FAILED want:\n");          
+               printf("  RW:1 U:1 L:1 ADDR: de ad be DATA: f0 0d\n");
+               printf("Got:\n");
+               print_latches();
+               return false;
+       }
+       printf("CPU interface latch test passed !\n");
+
+       return true;
+}
+
+/* Bits inverted from what goes into the SPI request */
+#define SIM_CYCLE_READ 0x00
+#define SIM_CYCLE_WRITE        0x01
+#define SIM_CYCLE_UPPER        0x02
+#define SIM_CYCLE_LOWER        0x04
+#define SIM_CYCLE_WORD (SIM_CYCLE_UPPER | SIM_CYCLE_LOWER)
+
+static unsigned short sim_bus_cycle(unsigned long addr,
+                                   unsigned short dat,
+                                   unsigned char flags)
+{
+       unsigned char stat;
+
+       /* Read value at 0x1020 */
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_AUTOINC | BB_REG_CPU_BASE);
+       do_spi((addr >> 16) & 0xff);
+       do_spi((addr >>  8) & 0xff);
+       do_spi((addr      ) & 0xff);
+       do_spi((dat  >> 8 ) & 0xff);
+       do_spi((dat       ) & 0xff);
+       do_spi(~flags);
+       do_spi(0x04); /* ctrl: cycle */
+       spi_disable_fpga();
+
+       /* Wait for stop state */
+       spi_enable_fpga();
+       do_spi(BB_REG_CPU_BASE + 0x7);
+       do_spi(0xff);
+       do
+               stat = do_spi(0xff);
+       while(!(stat & 0x08));
+       spi_disable_fpga();
+
+       /* Read latches */
+       spi_enable_fpga();
+       do_spi(BB_AUTOINC | BB_REG_CPU_BASE | 3);
+       do_spi(0xff);
+       dat  = do_spi(0xff);
+       dat  <<= 8;
+       dat  |= do_spi(0xff);
+       spi_disable_fpga();
+
+       return dat ;
+}
+
+static void dump_latches(void)
+{
+       /* Read latches */
+       spi_enable_fpga();
+       do_spi(BB_AUTOINC | BB_REG_CPU_BASE);
+       do_spi(0xff);
+       c[0] = do_spi(0xff);
+       c[1] = do_spi(0xff);
+       c[2] = do_spi(0xff);
+       c[3] = do_spi(0xff);
+       c[4] = do_spi(0xff);
+       c[5] = do_spi(0xff);
+       spi_disable_fpga();
+       printf("  RW:%d U:%d L:%d ADDR: %x %x %x DATA: %x %x %s\n",
+              !!(c[5] & 0x01),
+              !!(c[5] & 0x02),
+              !!(c[5] & 0x04),
+              c[0], c[1], c[2], c[3], c[4],
+              SCSI_HSHAKE ? " [SCSI]" : "");
+}
+
+static void run_to(unsigned long addr)
+{
+       unsigned char stat;
+
+       /* Set breakpoint */
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_AUTOINC | BB_REG_CPU_BASE | 8);
+       do_spi((addr >> 16) & 0xff);
+       do_spi((addr >>  8) & 0xff);
+       do_spi((addr      ) & 0xff);
+       spi_disable_fpga();
+
+       /* Start CPU with BP */
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+       do_spi(0x42); /* RUN + BKEN */
+       spi_disable_fpga();
+
+       /* Wait for stop state */
+       spi_enable_fpga();
+       do_spi(BB_REG_CPU_BASE + 0x7);
+       do_spi(0xff);
+       do {
+               if (SCSI_HSHAKE) {
+                       spi_disable_fpga();
+                       do_scsi();
+                       spi_enable_fpga();
+                       do_spi(BB_REG_CPU_BASE + 0x7);
+                       do_spi(0xff);
+               }
+               stat = do_spi(0xff);
+       } while(!(stat & 0x08));
+       spi_disable_fpga();
+}
+
+static void step(unsigned short n)
+{
+       unsigned short i;
+
+       for (i = 0; i < n; i++) {
+               /* Step CPU */
+               spi_enable_fpga();
+               do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+               do_spi(0x82); /* RUN + STEP */
+               spi_disable_fpga();
+
+#if 0 /* Need in theory but CPU never fast enough */
+               /* Wait for stop state */
+               spi_enable_fpga();
+               do_spi(BB_REG_CPU_BASE + 0x7);
+               do_spi(0xff);
+               do
+                       stat = do_spi(0xff);
+               while(!(stat & 0x08));
+               spi_disable_fpga();
+#endif
+       }
+}
+
+void main(void)
+{
+       unsigned char rc;
+       unsigned short i;
+       unsigned short v, v2;
+
+       hardware_init();
+
+       printf("Initializing MMC...\n");
+
+       rc = mmc_init();
+       if (!rc)
+               return;
+
+       printf("Initializing FAT...\n");
+
+       rc = fat_init();
+       if (!rc)
+               return;
+
+       printf("Configuring FPGA...\n");
+
+       if (!configure_fpga())
+               return;
+
+       //      printf("Checking FPGA...\n");
+       if (!check_fpga())
+               return;
+
+       printf("Loading ROM file...\n");
+       if (!load_file("MIMIGMACROM", 0x200000))
+               return;
+#if 0
+       printf("Loading Screenshot...\n");
+       if (!load_file("SCRNSHOTRAW", 0x1fa700))
+               return;
+#endif
+
+       printf("Opening SCSI disk image...\n");
+       scsi_open();
+
+#if 0
+       /* Start scope */
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_REG_SCOPE_BASE);
+       do_spi(0x02);
+       spi_disable_fpga();
+
+       /* Read ROM first words */
+       v = sim_bus_cycle(0, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       v2 = sim_bus_cycle(2, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       printf("Mem 0: %x %x\n", v, v2);
+       v = sim_bus_cycle(4, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       v2 = sim_bus_cycle(6, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       printf("Mem 4: %x %x\n", v, v2);
+
+       /* Read ROM las words */
+       v = sim_bus_cycle(0x41fff8, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       v2 = sim_bus_cycle(0x41fffa, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       printf("ROM end 0: %x %x\n", v, v2);
+       v = sim_bus_cycle(0x41fffc, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       v2 = sim_bus_cycle(0x41fffe, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       printf("ROM end 4: %x %x\n", v, v2);
+#endif
+
+       /* Un-reset CPU */
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_REG_CTRL_BASE | 2);
+       do_spi(0x01);
+       spi_disable_fpga();
+
+#if 0
+       {
+               unsigned char b,oldb,t;
+
+               spi_enable_fpga();
+               do_spi(BB_REG_CTRL_BASE | 6);
+               do_spi(0xff);
+               oldb = 0;
+               t = 0, i = 0;
+               while (i < 512) {
+                       oldb = b;
+                       b = do_spi(0xff);
+                       if (b == oldb && t < 0xff)
+                           t++;
+                       else {
+                               secbuf[i++] = b;
+                               secbuf[i++] = t;
+                               t = 0;
+                       }
+               }
+               spi_disable_fpga();
+               for (i = 0; i < 512; i+=2)
+                       printf("%x %x\n", secbuf[i], secbuf[i+1]);
+       }
+#endif
+
+#if 0
+
+
+       //      printf("Running to main()\n");
+       //      run_to(0x4010c8);
+       printf("Stepping...()\n");
+       dump_latches();
+       step(1);
+       dump_latches();
+       step(1);
+
+       for (i = 0; i < 1000; i++) {
+               step(1);
+               dump_latches();
+       }
+
+       for (;;)
+               ;
+#endif
+#if 0
+       for (;;) {
+               printf("Running to exception \n");
+               run_to(0x401336);
+               dump_latches();
+               step(1);
+       }
+#endif
+
+#if 0
+       printf("Running to P_mBootBeep...\n");
+       run_to(0x40028a);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to IWM init...\n");
+       run_to(0x4000fc);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to P_ChecksumRomAndTestMemory...\n");
+       run_to(0x400d76);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to P_BootPart2 (fake hit)...\n");
+       run_to(0x400352);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to P_BootPart2...\n");
+       run_to(0x400352);
+       dump_latches();
+       step(1);
+       dump_latches();
+#endif
+
+#if 0
+       /* Read video first words */
+       v = sim_bus_cycle(0x1fa700, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       v2 = sim_bus_cycle(0x1fa702, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       printf("Vid 0: %x %x\n", v, v2);
+       v = sim_bus_cycle(0x1fa704, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       v2 = sim_bus_cycle(0x1fa706, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       printf("Vid 4: %x %x\n", v, v2);
+       printf("Running to setting MemTop...\n");
+       run_to(0x40037e);
+       dump_latches();
+       step(1);
+       dump_latches();
+       step(1);
+       dump_latches();
+       step(1);
+       dump_latches();
+       step(1);
+       dump_latches();
+       v = sim_bus_cycle(0x108, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       printf("Detected memory size: 0x%x0000 bytes\n", v);
+       printf("Running to L53 (CPUflag test)...\n");
+       run_to(0x400584);
+       dump_latches();
+       for (i = 0; i < 50; i++) {
+               step(1);
+               dump_latches();
+       }
+       printf("Running to Bsr P5...\n");
+       run_to(0x4005fa);
+       dump_latches();
+       printf("Running to call to _InitResources...\n");
+       run_to(0x400600);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to P_mInitQueue...\n");
+       run_to(0x401a38);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to P_InitSCC...\n");
+       run_to(0x40082c);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to P_InitKeyboard...\n");
+       run_to(0x402568);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to P_mInitIOMgr...\n");
+       run_to(0x40087c);
+       dump_latches();
+       step(1);
+       dump_latches();
+#endif
+#if 0
+
+       printf("Running to P252...\n");
+       run_to(0x407d40);
+       dump_latches();
+
+       /* HWCfgFlags at 0xb22 bit 7 is SCSI */
+       v = sim_bus_cycle(0xb22, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       printf("HWCfgFlags=%x\n", v >> 8);
+       v = sim_bus_cycle(0xb2e, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       printf("SCSIDrvrs =%x\n", v >> 8);
+
+       printf("Running to SCSI driver jump...\n");
+       run_to(0x407dc4);
+       dump_latches();
+       printf("Running to SCSI driver csum...\n");
+       run_to(0x1d36);
+       dump_latches();
+#endif
+
+#if 0
+       printf("Running to after plot disk...\n");
+       run_to(0x4006e4);
+       dump_latches();
+       v = sim_bus_cycle(0x30a, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       v2 = sim_bus_cycle(0x30c, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       printf("DrvQHdr.Head=%x %x\n", v, v2);
+#endif
+
+#if 0
+       printf("Running to P_mInitIOMgr..x1...\n");
+       run_to(0x40089c);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to P_mInitIOMgr..x2...\n");
+       run_to(0x4008a6);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to P_mInitIOMgr..x3...\n");
+       run_to(0x4008ac);
+       dump_latches();
+#endif
+
+#if 0
+       for (i = 0; i < 1000; i++) {
+               step(1);
+               dump_latches();
+       }
+#endif
+       /* Run CPU */
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+       do_spi(0x02);
+       spi_disable_fpga();
+
+       i = 0;
+       for (;;) {
+               /* This is VERY timing sensitive, the ROM selection
+                * timeout is very short and the fact that we run at
+                * twice the speed doesn't help.
+                * We could improve that by pre-setting the SPI to point
+                * to the SCSI lines register and just read from them,
+                * or we could have some of the selection logic moved to
+                * the FPGA
+                *
+                * Better HACK: The FPGA could delay CPU read cycles
+                *              from the bus status register when SEL
+                *              is set by a significant amount in the
+                *              FPGA thus allowing a lot more time for
+                *              the selection process
+                */
+               if (SCSI_HSHAKE)
+                       do_scsi();
+       }
+
+#if 0
+       printf("Running to E_Sony_Open...\n");
+       run_to(0x417d9e);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to E_Sony_Open:17DFA (_VInstall)...\n");
+       run_to(0x417dfa);
+       dump_latches();
+       step(1);
+       dump_latches();
+       printf("Running to E_Sony_Open:17E12 (bsr P721)...\n");
+       run_to(0x417e12);
+       dump_latches();
+       for (i = 0; i < 1000; i++) {
+               step(1);
+               dump_latches();
+       }
+       printf("Running to E_Sony_Open:17E16 (bsr P720)...\n");
+       run_to(0x417e12);
+       dump_latches();
+       for (i = 0; i < 100; i++) {
+               step(1);
+               dump_latches();
+       }
+       printf("Running to E_Sony_Open:17E24 (bsr P715)...\n");
+       run_to(0x417e12);
+       dump_latches();
+       step(1);
+       dump_latches();
+       step(1);
+       dump_latches();
+       for (i = 0; i < 1000; i++) {
+               step(1);
+               dump_latches();
+       }
+       printf("Running to L58...\n");
+       run_to(0x400692);
+       dump_latches();
+       step(1);
+       dump_latches();
+       for (i = 0; i < 1000; i++) {
+               step(1);
+               dump_latches();
+       }
+#endif
+
+
+#if 0
+       for (i = 0; i < 100; i++) {
+               wait_timer(100);
+               spi_enable_fpga();
+               do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+               do_spi(0x01); /* RUN + STEP */
+               spi_disable_fpga();
+       
+               /* We shall wait for cycle to complete but we know
+                * how slow our SPI is ...
+                */
+               dump_latches();
+
+               spi_enable_fpga();
+               do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+               do_spi(0x02); /* RUN + STEP */
+               spi_disable_fpga();
+       }
+
+       /* Stop CPU */
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+       do_spi(0x01); /* RUN + STEP */
+       spi_disable_fpga();
+#endif
+
+#if 0
+       step(10000);
+       step(10000);
+       step(10000);
+       step(10000);
+       step(10000);
+       step(10000);
+       step(10000);
+       step(10000);
+       step(10000);
+       step(10000);
+       step(10000);
+#endif
+
+#if 0
+       /* Stop scope */
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_REG_SCOPE_BASE);
+       do_spi(0x00);
+       spi_disable_fpga();
+
+       printf("Reading 0x1020 via SPI initiated read cycle: ");
+       v = sim_bus_cycle(0x1020, 0, SIM_CYCLE_READ | SIM_CYCLE_WORD);
+       printf("%x\n", v);
+
+       /* Reading memory via RAM fifo */
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_AUTOINC | BB_REG_MEM_BASE);
+       do_spi(0x20); /* ROM base */
+       do_spi(0x10);
+       do_spi(0x00);
+       spi_disable_fpga();
+
+       spi_enable_fpga();
+       do_spi(BB_REG_MEM_BASE);
+       do_spi(0xff);
+       do_spi(0xff); /* one more dummy cycle with RAM fifo */
+       for (i = 0; i < 0x40; i++) {
+               printf(" %x", do_spi(0xff));
+               if ((i & 0xf) == 0xf)
+                       printf("\n");
+       }
+       printf("\n");
+#endif
+
+#if 0
+       /* Start CPU */
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_REG_CPU_BASE | 6);
+       do_spi(0x02); /* RUN */
+       spi_disable_fpga();
+#endif
+
+#if 0
+       for (;;)
+               ;
+#endif
+}
diff --git a/pic/minimigmac.piklab b/pic/minimigmac.piklab
new file mode 100644 (file)
index 0000000..a7e46ff
--- /dev/null
@@ -0,0 +1,74 @@
+<!DOCTYPE piklab>
+<piklab>
+  <general>
+    <device>18F252</device>
+    <tool>sdcc</tool>
+    <programmer>custom</programmer>
+    <files>
+      <item>main.c</item>
+      <item>hardware.c</item>
+      <item>/usr/share/sdcc/lib/pic16/libc18f.lib</item>
+      <item>/usr/share/sdcc/lib/pic16/libio18f252.lib</item>
+      <item>mmc.c</item>
+      <item>fat16.c</item>
+      <item>18f252_mod.lkr</item>
+      <item>scsi.c</item>
+    </files>
+    <custom_linker_script>/home/benh/hackplace/minimigmac/pic/18f252_mod.lkr</custom_linker_script>
+    <description></description>
+    <version>0.1</version>
+    <output_type>executable</output_type>
+  </general>
+  <editors/>
+  <compiler>
+    <has_custom_arguments>true</has_custom_arguments>
+    <custom_arguments>
+      <item>-m%FAMILY</item>
+      <item>-%DEVICE</item>
+      <item>-V</item>
+      <item>--debug</item>
+      <item>-I$(SRCPATH)</item>
+      <item>-c</item>
+      <item>%I</item>
+      <item>--obanksel=2</item>
+      <item>--denable-peeps</item>
+      <item>--optimize-cmp</item>
+      <item>--optimize-df</item>
+    </custom_arguments>
+    <includes>
+      <item>$(SRCPATH)</item>
+    </includes>
+    <custom_options></custom_options>
+  </compiler>
+  <linker>
+    <has_custom_arguments>false</has_custom_arguments>
+    <custom_arguments>
+      <item>-m%FAMILY</item>
+      <item>-%DEVICE</item>
+      <item>-V</item>
+      <item>--debug</item>
+      <item>-Wl-c</item>
+      <item>-Wl-m</item>
+      <item>$LKR(-Wl-s%LKR)</item>
+      <item>-I$(SRCPATH)</item>
+      <item>-o%O</item>
+      <item>%OBJS</item>
+      <item>%LIBS</item>
+    </custom_arguments>
+    <includes>
+      <item>$(SRCPATH)</item>
+    </includes>
+    <custom_options></custom_options>
+    <format>inhx32</format>
+  </linker>
+  <librarian>
+    <has_custom_arguments>false</has_custom_arguments>
+    <custom_arguments>
+      <item>-c</item>
+      <item>%O</item>
+      <item>%OBJS</item>
+      <item>%LIBS</item>
+    </custom_arguments>
+    <custom_options></custom_options>
+  </librarian>
+</piklab>
diff --git a/pic/mmc.c b/pic/mmc.c
new file mode 100644 (file)
index 0000000..9c1b300
--- /dev/null
+++ b/pic/mmc.c
@@ -0,0 +1,402 @@
+/*
+ * MMC/SD-Card driver
+ *
+ * Essentially copied from Minimig
+ *
+ * Current implementation is polled & synchronous
+ */
+
+#include <stdio.h>
+
+#include "hardware.h"
+#include "mmc.h"
+#include "mmc_cmds.h"
+
+/* Enable / Disable Debug info */
+#undef DEBUG_SDMMC
+
+/* MMC, or SD Card Specifification V1, V2, SDHC */
+#define        MMC_CARD_NONE                   0x00
+#define        MMC_CARD_MMC                    0x01
+#define        MMC_CARD_SD_RAW_SPEC_1          0x02
+#define MMC_CARD_SD_RAW_SPEC_2         0x04
+#define MMC_CARD_SD_RAW_SPEC_SDHC      0x08
+
+static unsigned char mmc_card;
+static unsigned char mmc_crc_7;
+static unsigned int mmc_timeout;
+static unsigned char mmc_resp[5];
+
+#ifdef DEBUG_SDMMC
+#define pr_debug(args...)      printf_tiny(args)
+#else
+#define pr_debug(args...)
+#endif
+
+/* Calculate CRC7 checksum */
+static void mmc_add_crc7(unsigned char c)
+{
+       unsigned char i;
+
+       for (i = 0; i < 8; i++) {
+               mmc_crc_7 <<= 1;
+               if (c & 0x80)
+                       mmc_crc_7 ^= 0x09;
+               if (mmc_crc_7 & 0x80)
+                       mmc_crc_7 ^= 0x09;
+               c <<= 1;
+       }
+}
+
+/* Send a byte and calculate CRC */
+static void mmc_spi_crc(unsigned char cmd)
+{
+       do_spi(cmd);
+       mmc_add_crc7(cmd);
+}
+
+/* Send a command to the SDcard */
+static void mmc_cmd_r0(char cmd, unsigned short addr_h, unsigned short addr_l)
+{
+       mmc_crc_7 = 0;
+
+       /* Flush SPI-bus */
+       do_spi(0xff);
+
+       /* Send command */
+       mmc_spi_crc(cmd);
+
+       /* Send address */
+       mmc_spi_crc(addr_h >> 8);
+       mmc_spi_crc(addr_h & 0xff);
+       mmc_spi_crc(addr_l >> 8);
+       mmc_spi_crc(addr_l & 0xff);
+
+       /* Cook and send CRC */
+       mmc_crc_7 <<= 1;
+       mmc_crc_7++;
+       do_spi(mmc_crc_7);
+}
+
+/* Send a command to the SDcard, a one byte response is expected */
+static void mmc_cmd_r1(char cmd, unsigned short addr_h, unsigned short addr_l)
+{
+       unsigned char i = 100;
+
+       mmc_cmd_r0(cmd, addr_h, addr_l);
+       do
+               mmc_resp[0] = do_spi(0xff);
+       while (mmc_resp[0] == 0xff && --i);
+}
+
+/* Send a command to the SDcard, a two byte response is expected */
+static void mmc_cmd_r2(char cmd, unsigned short addr_h, unsigned short addr_l)
+{
+       unsigned char i = 100;
+
+       mmc_cmd_r0(cmd, addr_h, addr_l);
+       do
+               mmc_resp[0] = do_spi(0xff);
+       while (mmc_resp[0] == 0xff && --i);
+       mmc_resp[1] = do_spi(0xff);
+}
+
+/* Send a command to the SDcard, a five byte response is expected */
+static void mmc_cmd_r3(char cmd, unsigned short addr_h, unsigned short addr_l)
+{
+       unsigned char i = 100;
+
+       mmc_cmd_r0(cmd, addr_h, addr_l);
+       do
+               mmc_resp[0] = do_spi(0xff);
+       while (mmc_resp[0] == 0xff && --i);
+       mmc_resp[1] = do_spi(0xff);
+       mmc_resp[2] = do_spi(0xff);
+       mmc_resp[3] = do_spi(0xff);
+       mmc_resp[4] = do_spi(0xff);
+}
+
+/* Enable the MMC/SD card correctly */
+unsigned char mmc_init(void)
+{
+       unsigned short lp;
+
+       /* Set SPI clock .. Might need to be smarter at changing the
+        * SPI clock based on what device is enabled...
+        */
+       SSPCON1 = 0x22; //spiclk = Fosc/64 (init clock 100-400 kHz)
+
+       _M_CD = 0;
+       _M_CS = 1;
+       wait_timer(10);
+
+       _M_CD = 1; /* Enable clock*/
+       _M_CS = 1; /* SDcard Disabled */
+       
+       /* Set SDcard in SPI-Mode, Reset*/
+       /* (10 * 8bits = 80 clockpulses) */
+       for (lp = 0; lp < 10; lp++)
+               do_spi(0xff);
+
+       /* Delay for a lot of milliseconds (least 16 bus clock cycles) */
+       for (lp=0; lp < 56000; lp++)
+               ; /* PRAY sdcc doesn't optimize that out !
+                    Should use timer instead ! */
+       wait_timer(10);
+
+       /* SDcard Enabled */
+       _M_CS = 0;
+
+       /* No card detected by default */
+       mmc_card = MMC_CARD_NONE;
+       
+       /* CMD0: Reset all cards to IDLE state */
+       mmc_cmd_r1(CMD0, 0, 0);
+       if (mmc_resp[0] != 0x01) {
+               pr_debug("No card detected ! (resp=%x)\n", mmc_resp[0]);
+               goto fail;
+       }
+
+       /* Default to MMC */
+       mmc_card = MMC_CARD_MMC;
+
+       /* Check for SD Card V2 */
+       /* (Voltage 2.7V - 3.6V, 0xaa test pattern) */
+       mmc_cmd_r3(CMD8, 0x0000, 0x01aa);
+       if (!(mmc_resp[0] & 0x04)) {
+               if (mmc_resp[3] == 0x01 && mmc_resp[4] == 0xaa) {
+                       pr_debug("SD card V2 detected, possible SDHC\n");
+                       mmc_card |= MMC_CARD_SD_RAW_SPEC_2;
+               } else {
+                       pr_debug("Error detecting SD V2 card\n");
+                       goto fail;
+               }
+       } else {
+               /* Check for SD card V1 */
+               mmc_cmd_r1(CMD55, 0, 0);
+               mmc_cmd_r1(CMD41, 0, 0);
+               if (!(mmc_resp[0] & 0x04)) {
+                       pr_debug("SD card V1 detected\n");
+                       mmc_card |= MMC_CARD_SD_RAW_SPEC_1;
+               } else
+                       pr_debug("MMC-card detected\n");
+       }
+       
+       /* Wait for card to get ready */
+       mmc_timeout = 0;
+       while (1) {
+               if (mmc_card & (MMC_CARD_SD_RAW_SPEC_1 | MMC_CARD_SD_RAW_SPEC_2)) {
+                       /* When SD Card detected */
+                       lp = 0x0;
+                       if (mmc_card & MMC_CARD_SD_RAW_SPEC_2)
+                               lp = 0x4000;
+                       /* Activate SD card init process */
+                       mmc_cmd_r1(CMD55, 0, 0);
+                       mmc_cmd_r1(CMD41, lp, 0);
+               } else {
+                       /* Activate the MMC cards init process */
+                       mmc_cmd_r1(CMD1, 0, 0);
+               }
+               
+               /* Check if card is idle */
+               if (mmc_resp[0] == 0x00)
+                       break;
+               mmc_timeout++;
+               if (mmc_timeout == 1000) {
+                       pr_debug("SD/MMC ACMD41/CMD1 response timeout...\n");
+                       goto fail;
+               }
+       }
+
+       /* Check for SDHC card */
+       if (mmc_card & MMC_CARD_SD_RAW_SPEC_2) {
+               /* Get operating conditions */
+               mmc_cmd_r3(CMD58, 0, 0);
+               if (mmc_resp[0]) {
+                       pr_debug("SD/MMC: Failed to get operating conditions\n");
+                       goto fail;
+               }
+               
+               if (mmc_resp[1] & 0x40) {       
+                       pr_debug("SDHC Card Detected\n");
+                       mmc_card |= MMC_CARD_SD_RAW_SPEC_SDHC;
+               }
+       }
+
+       /* Set block size to 512 bytes */
+       mmc_timeout = 0;
+       while(1) {
+               mmc_cmd_r1(CMD16, 0x0000, 0x0200);
+               if (!mmc_resp[0])
+                       break;
+               mmc_timeout++;
+               if(mmc_timeout == 100) {
+                       pr_debug("Set block size to 512 timeout\n");
+                       goto fail;
+               }
+       }
+       
+       spi_disable_sdcard();
+       
+       /* XXX Stuff below from minimig... it's strange... it sets SSPCON1 to a
+        * different speed from what is done in hardware_init.c for everybody
+        */
+       // TODO: Change speed for card access
+       SSPCON1 = 0x20; //spiclk = Fosc/4 (5 MHz)
+       return true;
+ fail:
+       mmc_card = MMC_CARD_NONE;
+       spi_disable_sdcard();
+       return false;
+}
+
+/* Read single block (with block-size set by CMD16 to 512 by default) */
+unsigned char mmc_read(unsigned long lba, unsigned char *out_data)
+{
+       unsigned short upper_lba, lower_lba;
+       unsigned char i;
+       unsigned char *p;
+
+       /* !SDHC uses byte address instead of LBA */
+       if (!(mmc_card & MMC_CARD_SD_RAW_SPEC_SDHC))
+               lba <<= 9;
+
+       pr_debug("SD/MMC: Reading LBA 0x%lx\n", lba);
+       
+       upper_lba = (unsigned short)(lba>>16);
+       lower_lba = (unsigned short)lba;
+
+       spi_enable_sdcard();
+
+       mmc_cmd_r1(CMD17, upper_lba, lower_lba);
+
+       /* Exit if invalid response*/
+       if (mmc_resp[0]) {
+               pr_debug("SD/MMC CMD17: invalid response %x\n", mmc_resp[0]);
+               goto fail;
+       }
+
+       /* Wait for start of data transfer with timeout */
+       mmc_timeout = 0;
+       while(do_spi(0xff) != 0xfe) {
+               if (mmc_timeout++ >= 50000) {
+                       pr_debug("SD/MMC CMD17: no data token\n");
+                       goto fail;
+               }
+       }
+
+       /* Read data and exit OK */
+       p = out_data;
+       for (i = 0; i < 128; i++) {
+               SSPBUF = 0xff;
+               while (!SSPSTATbits.BF)
+                       ;
+               *(p++) = SSPBUF;
+               SSPBUF = 0xff;
+               while (!SSPSTATbits.BF)
+                       ;
+               *(p++) = SSPBUF;
+               SSPBUF = 0xff;
+               while (!SSPSTATbits.BF)
+                       ;
+               *(p++) = SSPBUF;
+               SSPBUF = 0xff;
+               while (!SSPSTATbits.BF)
+                       ;
+               *(p++) = SSPBUF;
+       }
+
+       /* XXX Todo: check CRC */
+       do_spi(0xff);   //Read CRC lo byte
+       do_spi(0xff);   //Read CRC hi byte
+
+       spi_disable_sdcard();
+       return true;
+ fail:
+       spi_disable_sdcard();
+       return false;
+}
+
+
+
+/* Write: 512 Byte-Mode, this will not work (read MMC and SD-card specs) with any other
+ * sector/block size
+ */
+unsigned char mmc_write(unsigned long lba, unsigned char *in_data)
+{
+       unsigned short upper_lba, lower_lba;
+       unsigned char i;
+       unsigned char *p;
+
+       /* !SDHC uses byte address instead of LBA */
+       if (!(mmc_card & MMC_CARD_SD_RAW_SPEC_SDHC))
+               lba <<= 9;
+
+       pr_debug("SD/MMC: Writing LBA 0x%lx\n", lba);
+
+       upper_lba = (unsigned short)(lba>>16);
+       lower_lba = (unsigned short)lba;
+
+       spi_enable_sdcard();
+
+       mmc_cmd_r1(CMD24, upper_lba, lower_lba);
+
+       /* Exit if invalid response*/
+       if (mmc_resp[0]) {
+               pr_debug("SD/MMC CMD24: invalid response %x\n", mmc_resp[0]);
+               goto fail;
+       }
+
+       do_spi(0xff);   //One byte gap
+       do_spi(0xfe);   //Send Data token
+
+       /* Send bytes for sector */
+       p = in_data;
+       for (i = 0; i < 128; i++) {
+               SSPBUF = *(p++);
+               while (!SSPSTATbits.BF)
+                       ;
+               SSPBUF = *(p++);
+               while (!SSPSTATbits.BF)
+                       ;
+               SSPBUF = *(p++);
+               while (!SSPSTATbits.BF)
+                       ;
+               SSPBUF = *(p++);
+               while (!SSPSTATbits.BF)
+                       ;
+       }
+
+       /* XXX Todo: Send proper CRC ? */
+       do_spi(0xff);   //Send CRC lo byte
+       do_spi(0xff);   //Send CRC hi byte
+
+       /* Read packet response */
+       i = do_spi(0xff);
+
+       /* Status codes
+        *  010 = Data accepted
+        *  101 = Data rejected due to CRC error
+        *  110 = Data rejected due to write error
+        */
+       i &= 0b00011111;
+       if (i != 0b00000101) {
+               pr_debug("SD/MMC CMD24: write error %x\n", i);
+               goto fail;
+       }
+
+       mmc_timeout = 0;
+       /* Wait until the card has finished writing the data */
+       while (do_spi(0xff) == 0x00) {
+               if (mmc_timeout++ >= 50000) {
+                       pr_debug("SD/MMC CMD24: busy wait timeout\n");
+                       goto fail;
+               }
+       }
+       spi_disable_sdcard();
+       return true;
+ fail:
+       spi_disable_sdcard();
+       return false;
+}
+
diff --git a/pic/mmc.h b/pic/mmc.h
new file mode 100644 (file)
index 0000000..611005e
--- /dev/null
+++ b/pic/mmc.h
@@ -0,0 +1,8 @@
+#ifndef __MMC_H__
+#define __MMC_H__
+
+unsigned char mmc_init(void);
+unsigned char mmc_read(unsigned long lba, unsigned char *out_data);
+unsigned char mmc_write(unsigned long lba, unsigned char *in_data);
+
+#endif
diff --git a/pic/mmc_cmds.h b/pic/mmc_cmds.h
new file mode 100644 (file)
index 0000000..00678a5
--- /dev/null
@@ -0,0 +1,99 @@
+#ifndef __MMC_CMDS_H__
+#define __MMC_CMDS_H__
+
+/* MMC command set */
+#define                CMD0            0x40            /*Resets the multimedia card*/
+#define                CMD1            0x41            /*Activates the card's initialization process*/
+#define                CMD2            0x42            /*--*/
+#define                CMD3            0x43            /*--*/
+#define                CMD4            0x44            /*--*/
+#define                CMD5            0x45            /*reseved*/
+#define                CMD6            0x46            /*reserved*/
+#define                CMD7            0x47            /*--*/
+#define                CMD8            0x48            /* Sends SD Memory Card interface condition,
+                                                  which includes host supply voltage information
+                                                  and asks the card whether card supports voltage.
+                                                  Reserved bits shall be set to '0'.*/
+#define                CMD9            0x49            /*CSD : Ask the selected card to send its card
+                                                 specific data*/
+#define                CMD10           0x4a            /*CID : Ask the selected card to send its card
+                                                 identification*/
+#define                CMD11           0x4b            /*--*/
+#define                CMD12           0x4c            /*--*/
+#define                CMD13           0x4d            /*Ask the selected card to send its status register*/
+#define                CMD14           0x4e            /*--*/
+#define                CMD15           0x4f            /*--*/
+#define                CMD16           0x50            /*Select a block length (in bytes) for all following
+                                                 block commands (Read:between 1-512 and Write:only 512)*/
+#define                CMD17           0x51            /*Reads a block of the size selected by the SET_BLOCKLEN
+                                                 command, the start address and block length must be set
+                                                 so that the data transferred will not cross a physical
+                                                 block boundry*/
+#define                CMD18           0x52            /*--*/
+#define                CMD19           0x53            /*reserved*/
+#define                CMD20           0x54            /*--*/
+#define                CMD21           0x55            /*reserved*/
+#define                CMD22           0x56            /*reserved*/
+#define                CMD23           0x57            /*reserved*/
+#define                CMD24           0x58            /*Writes a block of the size selected by CMD16, the start
+                                                 address must be alligned on a sector boundry, the block
+                                                 length is always 512 bytes*/
+#define                CMD25           0x59            /*--*/
+#define                CMD26           0x5a            /*--*/
+#define                CMD27           0x5b            /*Programming of the programmable bits of the CSD*/
+#define                CMD28           0x5c            /*If the card has write protection features, this command
+                                                 sets the write protection bit of the addressed group.
+                                                 The porperties of the write protection are coded in
+                                                 the card specific data (WP_GRP_SIZE)*/
+#define                CMD29           0x5d            /*If the card has write protection features, this command
+                                                 clears the write protection bit of the addressed group*/
+#define                CMD30           0x5e            /*If the card has write protection features, this command
+                                                 asks the card to send the status of the write protection
+                                                 bits. 32 write protection bits (representing 32 write
+                                                 protect groups starting at the specific address) followed
+                                                 by 16 CRD bits are transferred in a payload format via
+                                                 the data line*/
+#define                CMD31           0x5f            /*reserved*/
+#define                CMD32           0x60            /*sets the address of the first sector of the erase group*/
+#define                CMD33           0x61            /*Sets the address of the last sector in a cont. range
+                                                 within the selected erase group, or the address of a
+                                                 single sector to be selected for erase*/
+#define                CMD34           0x62            /*Removes on previously selected sector from the erase
+                                                 selection*/
+#define                CMD35           0x63            /*Sets the address of the first erase group within a
+                                                 range to be selected for erase*/
+#define                CMD36           0x64            /*Sets the address of the last erase group within a
+                                                 continuos range to be selected for erase*/
+#define                CMD37           0x65            /*Removes one previously selected erase group from the
+                                                 erase selection*/
+#define                CMD38           0x66            /*Erases all previously selected sectors*/
+#define                CMD39           0x67            /*--*/
+#define                CMD40           0x68            /*--*/
+#define                CMD41           0x69            /*reserved*/
+#define                CMD42           0x6a            /*reserved*/
+#define                CMD43           0x6b            /*reserved*/
+#define                CMD44           0x6c            /*reserved*/
+#define                CMD45           0x6d            /*reserved*/
+#define                CMD46           0x6e            /*reserved*/
+#define                CMD47           0x6f            /*reserved*/
+#define                CMD48           0x70            /*reserved*/
+#define                CMD49           0x71            /*reserved*/
+#define                CMD50           0x72            /*reserved*/
+#define                CMD51           0x73            /*reserved*/
+#define                CMD52           0x74            /*reserved*/
+#define                CMD53           0x75            /*reserved*/
+#define                CMD54           0x76            /*reserved*/
+#define                CMD55           0x77            /*Indicates to the card that the next command is an
+                                                 application specific command rather than a standard
+                                                 command*/
+#define                CMD56           0x78            /*reserved*/
+#define                CMD57           0x79            /*reserved*/
+#define                CMD58           0x7a            /*reserved*/
+#define                CMD59           0x7b            /*Turns the CRC option ON or OFF. A '1' in the CRC option
+                                                 bit will turn the option ON, a '0' will turn it OFF*/
+#define                CMD60           0x7c            /*--*/
+#define                CMD61           0x7d            /*--*/
+#define                CMD62           0x7e            /*--*/
+#define                CMD63           0x7f            /*--*/
+
+#endif /*  __MMC_CMDS_H__ */
diff --git a/pic/scsi.c b/pic/scsi.c
new file mode 100644 (file)
index 0000000..4ca1a90
--- /dev/null
@@ -0,0 +1,392 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "hardware.h"
+#include "mmc.h"
+#include "fat16.h"
+#include "scsi.h"
+
+/* SCSI Backbus interface */
+#define BB_REG_LINES   0
+#define BB_LINES_BSY   7
+#define BB_LINES_SEL   6       
+#define BB_LINES_ACK   5
+#define BB_LINES_ATN   4
+#define BB_LINES_RST   3
+
+#define BB_REG_ASSERT  1
+#define BB_ASSERT_BSY  7
+#define BB_ASSERT_SEL  6
+#define BB_ASSERT_CD   5
+#define BB_ASSERT_IO   4
+#define BB_ASSERT_MSG  3
+#define BB_ASSERT_REQ  2
+#define BB_ASSERT_RST  1
+#define BB_ASSERT_AUTO 0       /* Automatic mode */
+
+#define BB_REG_ODATA   2
+#define BB_REG_IDATA   3
+#define BB_REG_ACNT_HI 4
+#define BB_REG_ACNT_LO 5
+
+#define MASK(bit)      (1u << (bit))
+
+static unsigned char scsi_cmd[12];
+static unsigned char scsi_assert;
+static unsigned long scsi_size;
+
+#define scsi_dbg(fmt...)
+
+void scsi_open(void)
+{
+       long size;
+
+       size = fat_open("MACHD   IMG");
+       if (size < 0) {
+               printf("File MACHD.IMG not found !\n");
+               return;
+       }
+       scsi_size = size;
+       fat_file_seek(0);
+}
+
+static unsigned char scsi_get_lines(void)
+{
+       unsigned char lines;
+
+       spi_enable_fpga();
+       do_spi(BB_REG_SCSI_BASE | BB_REG_LINES);
+       do_spi(0xff);
+       lines = do_spi(0xff);
+       spi_disable_fpga();
+
+       return lines;
+}
+
+static void scsi_set_assert(void)
+{
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_REG_SCSI_BASE | BB_REG_ASSERT);
+
+
+       do_spi(scsi_assert);
+       spi_disable_fpga();
+}
+
+static unsigned char scsi_get_data(void)
+{
+       unsigned char lines;
+
+       spi_enable_fpga();
+       do_spi(BB_REG_SCSI_BASE | BB_REG_ODATA);
+       do_spi(0xff);
+       lines = do_spi(0xff);
+       spi_disable_fpga();
+
+       return lines;
+}
+
+static void scsi_set_data(unsigned char val)
+{
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_REG_SCSI_BASE | BB_REG_IDATA);
+       do_spi(val);
+       spi_disable_fpga();
+}
+
+static void scsi_set_autocnt(unsigned short cnt)
+{
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_AUTOINC | BB_REG_SCSI_BASE | BB_REG_ACNT_HI);
+       do_spi(cnt >> 8);
+       do_spi(cnt & 0xff);
+       spi_disable_fpga();
+}
+
+static unsigned char scsi_do_data_in(unsigned short len)
+{
+       unsigned short i = 0;
+
+       scsi_assert |= MASK(BB_ASSERT_AUTO) | MASK(BB_ASSERT_IO);
+       scsi_set_assert();
+       scsi_set_autocnt(len);
+
+       spi_enable_fpga();
+       do_spi(BB_WRITE | BB_REG_SCSI_BASE | BB_REG_IDATA);
+       while(len--) {
+               do_spi(secbuf[i++]);
+               /* Handhake goes down approx. 160ns after the above
+                * and we then need to wait for it to go back up,
+                * the PIC executes an instruction in about 200ns so
+                * we shouldn't need any delay here before we test it
+                */
+               while(!SCSI_HSHAKE);
+       }
+       scsi_assert &= ~MASK(BB_ASSERT_AUTO);
+       scsi_set_assert();
+       return 0;
+}
+
+static unsigned char scsi_do_data_out(unsigned short len)
+{
+       unsigned short i = 0;
+
+       scsi_assert |= MASK(BB_ASSERT_AUTO);
+       scsi_assert &= ~MASK(BB_ASSERT_IO);
+       scsi_set_assert();
+       scsi_set_autocnt(len);
+
+       spi_enable_fpga();
+       do_spi(BB_REG_SCSI_BASE | BB_REG_ODATA);
+       /* See handshake timing comment in scsi_do_data_in() */
+       while(!SCSI_HSHAKE);
+       do_spi(0xff);
+       while(!SCSI_HSHAKE);
+       do_spi(0xff);
+       while(!SCSI_HSHAKE);
+       do_spi(0xff);
+       while(len--) {
+               while(!SCSI_HSHAKE);
+               secbuf[i++] = do_spi(0xff);
+       }
+       scsi_assert &= ~MASK(BB_ASSERT_AUTO);
+       scsi_set_assert();
+       return 0;
+}
+
+static unsigned char scsi_do_read(unsigned long lba,
+                                 unsigned short cnt)
+{
+       unsigned char rc = true;
+
+       if (scsi_size == 0)
+               return 1;
+       diskled_on();
+       fat_file_seek(lba);
+       while(cnt--) {
+               if (rc)
+                       rc = fat_file_read();
+               if (!rc) {
+                       diskled_off();
+                       return 1;
+               }
+               scsi_do_data_in(512);
+               if (cnt)
+                       rc = fat_file_next_sector();
+       }
+       diskled_off();
+       return 0;
+}
+
+static unsigned char scsi_do_write(unsigned long lba,
+                                  unsigned short cnt)
+{
+       unsigned char rc;
+
+       if (scsi_size == 0)
+               return 1;
+       diskled_on();
+       fat_file_seek(lba);
+       while(cnt--) {
+               scsi_do_data_out(512);
+               rc = fat_file_write();
+               if (rc && cnt)
+                       rc = fat_file_next_sector();
+               if (!rc) {
+                       diskled_off();
+                       return 1;
+               }
+       }
+       diskled_off();
+       return 0;
+}
+
+static unsigned char scsi_do_cmd(void)
+{
+       unsigned long lba;
+       unsigned short cnt;
+
+       switch(scsi_cmd[0]) {
+       case 0x00:
+               printf("scsi: TEST_UNIT_READY !\n");
+               return 0;
+       case 0x03:
+               printf("scsi: REQUEST_SENSE !\n");
+               fat_inval_cache();
+               memset(secbuf, 0, 13);
+               secbuf[0] = 0xf0;
+               return scsi_do_data_in(13);
+       case 0x04:
+               printf("scsi: FORMAT_UNIT !\n");
+               return 1;
+       case 0x08:
+               lba = scsi_cmd[1] & 0x1f;
+               lba = (lba << 8) | scsi_cmd[2];
+               lba = (lba << 8) | scsi_cmd[3];
+               cnt = scsi_cmd[4];
+               if (!cnt)
+                       cnt = 256;
+               printf("scsi: READ6 (lba=%lx cnt=%x) !\n", lba, cnt);
+               return scsi_do_read(lba, cnt);
+       case 0x0a:
+               lba = scsi_cmd[1] & 0x1f;
+               lba = (lba << 8) | scsi_cmd[2];
+               lba = (lba << 8) | scsi_cmd[3];
+               cnt = scsi_cmd[4];
+               if (!cnt)
+                       cnt = 256;
+               printf("scsi: WRITE6 (lba=%lx cnt=%x) !\n", lba, cnt);
+               return scsi_do_write(lba, cnt);
+       case 0x12:
+               printf("scsi: INQUIRY !\n");
+               fat_inval_cache();
+               memset(secbuf, 0, 36);
+               memcpy(secbuf + 8, " SEAGATE", 8);
+               memcpy(secbuf + 16, "          ST225N", 16);
+               secbuf[4] = 32;
+               cnt = scsi_cmd[4];
+               if (cnt > 36)
+                       cnt = 36;
+               return scsi_do_data_in(cnt);
+       case 0x15:
+               printf("scsi: MODE_SELECT !\n");
+               return 1;
+       case 0x1a:
+               printf("scsi: MODE_SENSE !\n");
+               return 1;
+       case 0x1b:
+               printf("scsi: START_STOP !\n");
+               return 1;
+       case 0x25:
+               printf("scsi: READ_CAPACITY !\n");
+               return 1;
+       case 0x28:
+               printf("scsi: READ10 !\n");
+               lba = scsi_cmd[2];
+               lba = (lba << 8) | scsi_cmd[3];
+               lba = (lba << 8) | scsi_cmd[4];
+               lba = (lba << 8) | scsi_cmd[5];
+               cnt = scsi_cmd[7];
+               cnt = (cnt << 8) | scsi_cmd[8];
+               return scsi_do_read(lba, cnt);
+       case 0x2a:
+               printf("scsi: WRITE10 !\n");
+               lba = scsi_cmd[2];
+               lba = (lba << 8) | scsi_cmd[3];
+               lba = (lba << 8) | scsi_cmd[4];
+               lba = (lba << 8) | scsi_cmd[5];
+               cnt = scsi_cmd[7];
+               cnt = (cnt << 8) | scsi_cmd[8];
+               return scsi_do_write(lba, cnt);
+       case 0x2f:
+               printf("scsi: VERIFY10 !\n");
+               return 10;
+       case 0x3c:
+               printf("scsi: READ_BUFFER !\n");
+               return 6;
+       }
+       return 1;
+}
+
+static void scsi_req_ack(void)
+{
+       unsigned char l;
+
+       scsi_assert |= MASK(BB_ASSERT_REQ);
+       scsi_set_assert();
+       do
+               l = scsi_get_lines();
+       while (!(l & MASK(BB_LINES_ACK)));
+}
+
+static void scsi_nreq_nack(void)
+{
+       unsigned char l;
+
+       scsi_assert &= ~MASK(BB_ASSERT_REQ);
+       scsi_set_assert();
+       do
+               l = scsi_get_lines();
+       while (l & MASK(BB_LINES_ACK));
+}
+
+void do_scsi(void)
+{
+       unsigned char lines, val, i, len, stat;
+
+       lines = scsi_get_lines();
+       if ((lines & MASK(BB_LINES_SEL)) /*&& !(lines & MASK(BB_LINES_BSY)) */) {
+               val = scsi_get_data();
+               if (!(val & MASK(6))) {
+                       //printf("scsi: Wrong ID 0x%x\n", val);
+                       return;
+               }
+               scsi_assert = MASK(BB_ASSERT_BSY);
+               scsi_set_assert();
+       } else if (lines & MASK(BB_LINES_RST)) {
+               printf("scsi: RESET !\n");
+               return;
+       } else {
+               printf("scsi: Glitch (reset ?) lines=%x!\n", lines);
+               return;
+       }
+       while(lines & MASK(BB_LINES_SEL))
+               lines = scsi_get_lines();
+       /* XXX Check for ATN for MESSAGE_OUT phase, Mac ROM doesn't do it */
+       scsi_dbg("scsi: Selected, command phase...\n");
+       i = 0;
+       stat = 0;
+       len = 1;
+       scsi_assert |= MASK(BB_ASSERT_CD);
+       scsi_set_assert();
+       while(len) {
+               scsi_req_ack();
+               val = scsi_get_data();
+               scsi_cmd[i++] = scsi_get_data();
+               scsi_dbg("scsi: CMD = 0x%x\n", val);
+               if (i == 1) {
+                       if (val == 0x00 || /* TEST_UNIT_READY */
+                           val == 0x03 || /* REQUEST_SENSE */
+                           val == 0x04 || /* FORMAT_UNIT */
+                           val == 0x08 || /* READ6 */
+                           val == 0x0a || /* WRITE6 */
+                           val == 0x12 || /* INQUIRY */
+                           val == 0x15 || /* MODE_SELECT */
+                           val == 0x1a || /* MODE_SENSE */
+                           val == 0x1b || /* START_STOP */
+                           val == 0x3c || /* READ_BUFFER */
+                           0)
+                               len = 6;
+                       else if (val == 0x25 || /* READ_CAPACITY */
+                                val == 0x28 || /* READ10 */
+                                val == 0x2a || /* WRITE10 */
+                                val == 0x2f || /* VERIFY10 */
+                                0)
+                               len = 10;
+                       else {
+                               stat = 1;
+                               goto fail;
+                       }
+               }
+               len--;
+       fail:
+               scsi_nreq_nack();
+       }
+       scsi_assert &= ~MASK(BB_ASSERT_CD);
+       stat = scsi_do_cmd();
+       scsi_dbg("Status phase (stat=%x)\n", stat);
+       scsi_assert |= MASK(BB_ASSERT_IO) | MASK(BB_ASSERT_CD);
+       scsi_set_data(stat);
+       scsi_req_ack();
+       scsi_nreq_nack();
+       scsi_dbg("Message IN phase (stat=%x)\n", stat);
+       scsi_assert |= MASK(BB_ASSERT_MSG);
+       scsi_set_data(0);
+       scsi_req_ack();
+       scsi_nreq_nack();
+       scsi_assert = 0;
+       scsi_set_assert();
+}
+
diff --git a/pic/scsi.h b/pic/scsi.h
new file mode 100644 (file)
index 0000000..95c41a7
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __SCSI_H__
+#define __SCSI_H__
+
+extern void scsi_open(void);
+extern void do_scsi(void);
+
+#endif /* __SCSI_H__ */