diff --git a/firmware/adb/README.md b/firmware/adb/README.md index 8134067..7f7e41a 100644 --- a/firmware/adb/README.md +++ b/firmware/adb/README.md @@ -2,3 +2,24 @@ This directory contains the firmware for the ADB (Apple Desktop Bus) chip, which is actually an Apple-branded PIC16CR54 microcontroller. + +# Functioning of the ADB + +See Guide to the Macintosh Family Hardware, page 312. + +* Automatically polls the last device that has sent data, the _active + device_. Then shifts it out to the VIA, that causes an interrupt. + +* A non-active device asserts a _Service Request_ signal to the ADB + when it has data to send. The ADB asserts an interrupt request + signal to the VIA, which sets bit 3 in data register 3 to 0. + Respond to requests of the ADB Manager to poll each device to find + which one asserted the Service Request. + +Data bits are encoded as follows: + +* Bit-cell time: 100 us + +* 0: low 65 us + +* 1: low 35 us diff --git a/hardware/fpga/bbu/Makefile b/hardware/fpga/bbu/Makefile index c36d963..3ac9abe 100644 --- a/hardware/fpga/bbu/Makefile +++ b/hardware/fpga/bbu/Makefile @@ -12,8 +12,14 @@ all: $(TARGETS) bbu.vvp: bbu.v common.vh test_stdlogic.vvp: test_stdlogic.v stdlogic.v common.vh + test_mac128pal.vvp: test_mac128pal.v test_stdlogic.v stdlogic.v \ mac128pal.v common.vh + iverilog -Wanachronisms -Wimplicit -Wportbind -Wselect-range \ + -Winfloop -Wsensitivity-entire-vector \ + -Wsensitivity-entire-array \ + -o $@ $< + ./test_mac128pal.vvp -lxt clean: rm -f $(TARGETS) diff --git a/hardware/fpga/bbu/docs/getsheets.sh b/hardware/fpga/bbu/docs/getsheets.sh index 5988e0e..e9893ff 100755 --- a/hardware/fpga/bbu/docs/getsheets.sh +++ b/hardware/fpga/bbu/docs/getsheets.sh @@ -8,3 +8,4 @@ curl -L -o 'http://www.applelogic.org/files/Z8530UM.pdf' curl -L -O 'http://archive.6502.org/datasheets/rockwell_r6522_via.pdf' curl -L -O 'http://www.assmann-wsw.com/fileadmin/datasheets/ASS_0981_CO.pdf' curl -L -O 'https://media.digikey.com/pdf/Data%20Sheets/Rohm%20PDFs/MSM51V17405F.pdf' +curl -L -O 'http://www.bitsavers.org/components/vti/databook/1986_VTI_Gate_Array_Design_Manual.pdf' diff --git a/hardware/fpga/bbu/lfsr.c b/hardware/fpga/bbu/lfsr.c new file mode 100644 index 0000000..86330c1 --- /dev/null +++ b/hardware/fpga/bbu/lfsr.c @@ -0,0 +1,55 @@ +/* +20210403/https://patentimages.storage.googleapis.com/52/04/e8/f5a5f0a7214a9c/US4910670.pdf +20210403/https://github.com/mamedev/mame/blob/master/src/mame/drivers/mac128.cpp#L486 +20210403/https://en.wikipedia.org/wiki/Linear-feedback_shift_register +*/ + +#include +#include + +int +main(int argc, char *argv[]) +{ + unsigned int k = 1; + /* if (argc != 2) + return 1; + if (sreg == 0) + return 1; */ + + fputs(" 0, ", stdout); + while (k < 64) { + unsigned char sreg; + unsigned char preg; + unsigned char j; + unsigned int i = 0; + /* sreg = (char)atoi(argv[1]); */ + sreg = k; + sreg &= 64 - 1; + /* N.B.: Careful, we do not check the exit condition at the start. + We must have a special hardware condition check to allow + shifting once before triggering the exit condition. As we also + do for the zero condition. */ + do { + sreg = (sreg >> 1) | + ((((sreg >> 0) ^ (sreg >> 1)) & 1) << 5); + sreg &= 64 - 1; + + preg = sreg; + /* j = 6; + while (j > 0) { + j--; + putchar((preg & (1 << 5)) ? '1' : '0'); + preg <<= 1; + } + putchar('\n'); */ + i++; + } while (sreg != 0x20); + printf("%2d, ", i); + k++; + if ((k & (8 - 1)) == 0) + putchar('\n'); + } + putchar('\n'); + + return 0; +} diff --git a/hardware/fpga/bbu/mac128pal.v b/hardware/fpga/bbu/mac128pal.v index 47b67ac..e2fd16c 100644 --- a/hardware/fpga/bbu/mac128pal.v +++ b/hardware/fpga/bbu/mac128pal.v @@ -53,12 +53,15 @@ module tsm(simclk, n_res, output wire s0, dtack; `power wire vcc; + reg qx; + // We must implement RESET for simulation or else this will never // stabilize. always @(negedge n_res) begin // casl = 1; cash = 1; s0 = 1; dtack = 1; ras <= 1; vclk <= 1; q2 <= 1; q1 <= 1; + qx <= 1; end // Simulate combinatorial logic. @@ -88,18 +91,30 @@ module tsm(simclk, n_res, ~(~pclk & q1 & s1 // video cycle | ~pclk & q1 & ~ramen & dtack // processor cycle | pclk & ~ras); // any other cycle - vclk <= + /* vclk <= ~(~q1 & pclk & q2 & vclk // divide by 8 (1MHz) | ~vclk & q1 | ~vclk & ~pclk - | ~vclk & ~q2); + | ~vclk & ~q2); */ + vclk <= + ~(~sysclk // one-shot divide by 8 (1MHz) + | q1 + | ~pclk + | ~qx + | ~q2); q1 <= ~(~pclk & q1 | pclk & ~q1); // divide `pclk` by 2 (4MHz) + // TODO FIXME: Real PALs don't have this extra bit of memory! + qx <= + ~(~q1 & ~pclk & qx // divide by 4 (2MHz) + | ~qx & q1 + | ~qx & pclk); q2 <= - ~(~q1 & pclk & q2 // divide by 4 (2MHz) + ~(~q1 & ~pclk & qx & q2 // divide by 8 (1MHz) | ~q2 & q1 - | ~q2 & ~pclk); + | ~q2 & pclk + | ~q2 & ~qx); end end endmodule @@ -131,6 +146,9 @@ module lag(simclk, n_res, snddma <= 1; reslin <= 1; resnyb <= 1; end + // TODO FIXME: After converting to one-shot vclk, we have a few + // glitches still to fix up. + // Simulate registered logic. always @(posedge sysclk) begin if (n_res) begin @@ -232,6 +250,11 @@ the whole interplay works in detail. regular visible area sweep before we de-assert *HSYNC, so actually we won't be drawing these pixels during horizontal blanking. + Nevertheless, if you do need a signal that covers the whole active + pixel period, you can logically OR together *HSYNC and VIAPB6. + Make sure that you use buffering between the VIA and VIAPB6 to + prevent the CPU from driving the CRT haywire. + 3. Now, the ultimate conclusion of the glitched counter. When we wrap-around to zero, both *HSYNC and VIAPB6 are still asserted. We check for this condition to generate resnyb in the middle of a @@ -393,21 +416,22 @@ module tsg(simclk, n_res, // Simulate registered logic. always @(posedge sysclk) begin if (n_res) begin - // TODO VERIFY: q6 missing? - q6 <= ~(0); + q6 <= ~(~q3); // delayed copy of 1MHz q3 signal clkscc <= - ~(clkscc & ~pclk & ~q4 - | clkscc & ~pclk & ~q3 - | clkscc & ~pclk & vclk - | ~clkscc & pclk - | ~clkscc & q4 & q3 & ~vclk); // skip one inversion every 32 cycles + ~(clkscc & pclk & ~q4 + | clkscc & pclk & ~q6 + | clkscc & pclk & q3 + | ~clkscc & ~pclk + | ~clkscc & q4 & q6 & ~q3); // skip one inversion every 32 cycles viacb1 <= ~(~keyclk); // buffer the keyboard clock pclk <= ~(pclk); // divide SYSCLK by 2 (8MHz) - q3 <= ~(~vclk); // `sysclk` / 16 + q3 <= + ~(~q3 & ~vclk + | vclk & q3); // `sysclk` / 16 q4 <= - ~(q4 & q3 & ~vclk // `sysclk` / 32 - | ~q4 & ~q3 // } J for generating CLKSCC - | ~q4 & vclk); + ~(q4 & q6 & ~q3 // `sysclk` / 32 + | ~q4 & ~q6 // } J for generating CLKSCC + | ~q4 & q3); end end endmodule @@ -431,7 +455,7 @@ module asg(simclk, n_res, // We must implement RESET for simulation or else this will never // stabilize. always @(negedge n_res) begin - n_dmald <= 1; + n_dmald <= 1; pwm <= 1; r5 <= 1; r4 <= 1; r3 <= 1; r2 <= 1; r1 <= 1; r0 <= 1; end @@ -450,14 +474,52 @@ module asg(simclk, n_res, // N.B.: This expansion almost exceeds the term limit of the // PAL. + // * Load value on *DMALD. + + // * If value is zero, set exit condition immediately. + // Otherwise, set "not exit" condition. This way we can + // load 0x20 and not immediately quit. + + // * Regular loop, shift and check for exit condition on + // shifted condition. Okay, that's a bit ugly, but fine. + // It would be simpler to check for exit condition on + // stored value. + + // ((a) ? ((b) ? d : e) : c); + // (a & ((b & c) | (~b & d))) | (~a & c); + // r0 <= + // ~(~a & ~c + // | ~a & ~c & b + // | ~a & ~c & ~e + // | ~b & a & ~e + // | ~b & a & ~e & ~c + // | ~b & ~c & ~a & ~c + // | ~b & ~c & ~e & a + // | ~b & ~c & ~e & ~c + // | ~d & a & b + // | ~d & a & b & ~c + // | ~d & a & ~e + // | ~d & a & ~e & ~c + // | ~d & ~c & ~a + // | ~d & ~c & b & a + // | ~d & ~c & b + // | ~d & ~c & ~e & a + // | ~d & ~c & ~e & ~c); + + r0 <= (~n_dmald) ? rdq0 : ((pwm & vclk) ? r1 : r0); + // TODO FIXME: Not in PAL equation format. - r0 <= n_dmald & (r0 ^ ~pwm); - r1 <= n_dmald & (r1 ^ (r0 & ~pwm)); - r2 <= n_dmald & (r2 ^ (r1 & r0 & ~pwm)); - r3 <= n_dmald & (r3 ^ (r2 & r1 & r0 & ~pwm)); - r4 <= n_dmald & (r4 ^ (r3 & r2 & r1 & r0 & ~pwm)); - r5 <= n_dmald & (r5 ^ (r4 & r3 & r2 & r1 & r0 & ~pwm)); - pwm <= n_dmald & r5 & r4 & r3 & r2 & r1 & r0; + r0 <= (~n_dmald) ? rdq0 : ((pwm & vclk) ? r1 : r0); + r1 <= (~n_dmald) ? rdq1 : ((pwm & vclk) ? r2 : r1); + r2 <= (~n_dmald) ? rdq2 : ((pwm & vclk) ? r3 : r2); + r3 <= (~n_dmald) ? rdq3 : ((pwm & vclk) ? r4 : r3); + r4 <= (~n_dmald) ? rdq4 : ((pwm & vclk) ? r5 : r4); + r5 <= (~n_dmald) ? rdq5 : ((pwm & vclk) ? (r0 ^ r1) : r5); + pwm <= (~n_dmald) + ? ((~rdq5 & ~rdq4 & ~rdq3 & ~rdq2 & ~rdq1 & ~rdq0) ? 0 : 1) + : ((pwm & vclk) + ? (((r0 ^ r1) & ~r5 & ~r4 & ~r3 & ~r2 & ~r1) ? 0 : 1) + : pwm); end end endmodule diff --git a/hardware/fpga/bbu/test_mac128pal.v b/hardware/fpga/bbu/test_mac128pal.v index 0ffb8d5..307ceb3 100644 --- a/hardware/fpga/bbu/test_mac128pal.v +++ b/hardware/fpga/bbu/test_mac128pal.v @@ -109,10 +109,10 @@ module test_mac128pal(); // Set simulation time limit. initial begin - // #1920000 $finish; + #1920000 $finish; // PLEASE NOTE: We must simulate LOTS of cycles in order to see // what the oscilloscope trace for one video frame looks like. - #30720000 $finish; + // #30720000 $finish; end // We can use `$display()` for printf-style messages and implement