ULA revistited.

Any discussions related to the creation of new hardware or software for the ZX80 or ZX81
User avatar
Paul
Posts: 1517
Joined: Thu May 27, 2010 8:15 am
Location: Germanys west end

Re: ULA revistited.

Post by Paul »

If you get it running and there would be space left you might be able to add the functionality of the clockdoubler that is quite popular.
I hope so much that you succeed .
In theory, there is no difference between theory and practice. But, in practice, there is.
User avatar
Andy Rea
Posts: 1606
Joined: Fri May 09, 2008 2:48 pm
Location: Planet Earth
Contact:

Re: ULA revistited.

Post by Andy Rea »

Hi Prime,

the oscilator circuit is took directly from the great Bodo Wenzels design.

oops.... :oops:

missed a connection between base of the npn (bc548) and the junction of the 2 capacitors...

that should get it working.

ah the 6.5Mhz crystal :lol: when i built a zx80 clone i used a 6.5536Mhz crystal which are more widely available, divides nicely to seconds...

i too am working on a cpld design, hoping to squeeze it onto a board measuring just 1" x 2.5" that will plug directly into the ula socket, i'm having a wonderful time just now learning verilog HDL.

@ Paul
um clock doubling that would be good, will have to look into it for then next incarceration if there is one, for now i want to duplicate my logic (which i have proved works) as close as possible, and then move on from there.

regards Andy
what's that Smell.... smells like fresh flux and solder fumes...
User avatar
Andy Rea
Posts: 1606
Joined: Fri May 09, 2008 2:48 pm
Location: Planet Earth
Contact:

Re: ULA revistited.

Post by Andy Rea »

updated schematic, to correct a couple of errors.

https://picasaweb.google.com/lh/photo/K ... directlink

the 47K resitor connected between GND and Tape-in may need to be altered to suit your device, the Gal i used has a reasonable internal pullup this resistor makes the input sit just at the switching threshold.

regards Andy.
what's that Smell.... smells like fresh flux and solder fumes...
Prime
Posts: 134
Joined: Thu Mar 24, 2011 3:02 am

Re: ULA revistited.

Post by Prime »

OK here's my Verilog code so far....This is based on the previous version using gates so doesn't have the back portch etc *yet*

I'm currently getting a stable picture on poweron however typing some characters causes the display to corrupt, 2 for example seems to fetch successive pixel rows one character space further to the right for each new line.
However the Inverse K is fine, as are 1 and 0 I'm not too sure what's causing this.

The design fits into a Xilinx XC95108, however I believe that it is using less than 72 macrocels, so should fit into an XC9572, tab size is 3, for some strange reason that's what webpack defaults to.....

Be aware that the comments in some places may not match the code........

Code: Select all

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 		Ramoth
// Engineer: 		Phill.Harvey-Smith, based on pervious work by Andy Rea
// 
// Create Date:   17:52:53 03/24/2011 
// Design Name: 	ZX81 ULA replacement	
// Module Name:   ULA 
// Project Name: 
// Target Devices: XC9572 / XC95108
// Tool versions: 
// Description: 	To serve as a replacement for the ULA in the ZX81 computer.
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
// I have used the Acorn convention of prefixing active low outputs and inputs to
// che design with a lower case n so for example nRD would be an active loaw read 
// strobe
// 
//////////////////////////////////////////////////////////////////////////////////
module ULA(
    inout [7:0] 	Data,			// Databus from Z80
    inout [15:14] AddrH,		// Two parts of Address bus
    inout [8:0] 	AddrL,
    
	 input 			nHALT,		// Halt from Z80
    input			nRD,			// Read and write strobes from Z80
	 input			nWR,
    input 			nIORQ,		// IO request from Z80
    input 			nMREQ,		// Memory request from Z80
    input 			nM1,			// M1 from Z80
    input 			USAUK,		// USA or UK jumper
	 
	 input 			CLKIN,		// 6.5MHz clock input signal
    input 			TapeIn,		// Input from cassette when loading
    input [4:0] 	Kbd,			// Keyboard row inputs
    
    output 			nNMI,
	 output 			CLKOUT,		// 3.5Mhz clock output to Z80 
    output 			nROMCS,		// ROP chip select signal
    output 			nRAMCS,		// RAM chip select signal
    output 			VidOut,		// Video output signal 
    output 			Blank,		// Video blanking signal
    output 			CSync,		// Composite Sync
	 
	 // Debugging ports
	 inout [7:0]	PortA,
	 inout [7:0]	PortB,
	 inout [7:0]	PortC
    );

// Local registers

	reg	[7:0]		HCount;		// HSync Counter 	
	reg	[3:0]		Micro;		// Micro counter
	reg	[7:0]		ShiftReg;	// Video shift register
	reg	CLK325;	
	reg VSync;
	reg NMIon;
	
	// Clock generator output clock to Z80 (3.25MHz), is generated by dividing input clock
	// 6.5MHz by 2.
	always @(posedge CLKIN)
	begin
		CLK325 <= !CLK325;
	end
	
	// Aliases just for convenience
	assign CLK65	= CLKIN;
	assign nCLK65	= !CLKIN;
	assign CLKOUT 	= !CLK325;
	
	// HSync generation
	// Counter will be reset to 0 if the count reaches 207 or vsync is active.
	assign HClear = (HCount==207) | VSync;
	
	always @(negedge CLK325 or posedge HClear)
	begin
		if(HClear)
			HCount <= 0;
		else
			HCount <= HCount+1;
	end
	
	// HSync generated at end of count
	assign HSync	= HCount[6] & HCount[7];
	assign nHSync	= !HSync;
	
	// Chip selects
	// ROMCS active when mREQ valid and A14 low, RAMCS valid when MREQ active and A14 high
	// This effectivly devides the memory map up as follows :
	//	$0000 - $3FFF	ROMCS
	// $4000 - $7FFF	RAMCS
	// $8000 - $BFFF	ROMCS
	// $C000 - $FFFF	RAMCS
	assign nROMCS	= nMREQ | AddrH[14];
	assign nRAMCS	= nMREQ | !AddrH[14];
	
	// IO read and write strobes
	assign nIORD	= nRD | nIORQ;
	assign nIOWR	= nWR | nIORQ;
	
	// VSync generation
	// Read of location $FE
	assign nFERD		= nIORD | AddrL[0];
	assign VSyncOn		= (nFERD | NMIon);
	assign VSyncOff	= nIOWR;
	
	// Togge VSync on and off as needded
 	always @(negedge VSyncOn or negedge VSyncOff)
	begin
		if (!VSyncOn)
			VSync <= 1'b1;
		else
			VSync <= 1'b0;
	end
	
	// NMI control
	assign NMIEnable	= nIOWR | AddrL[0];
	assign NMIDisable	= nIOWR | AddrL[1];
	
	// Toggle NMIon on and off as needed
	always @(negedge NMIEnable or negedge NMIDisable)
	begin
		if (!NMIEnable)
			NMIon <= 1'b1;
		else
			NMIon <= 1'b0;
	end
	
	assign nNMI	= !NMIon | nHSync;
	//assign nNMI = 1'b1;
	
	// Micro counter
	reg MicroEnabled;
	assign nL_PRE	= !(Micro[3:0]==8); //!Micro[3]; 
	
	always @(negedge  nL_PRE or negedge nM1)
	begin
		if(!nL_PRE)
			MicroEnabled <= 1'b0;
		else
			MicroEnabled <= 1'b1;
	end
	
	// Only clock if enabled
	always @(negedge nCLK65 or negedge MicroEnabled)
	begin
		if (!MicroEnabled)
			Micro <= 4'b0;
		else 
			Micro <= Micro+1;
	end
	
	// NOP Pulse
	reg	NOPTC;
	
	assign NOPulse		= !(Micro[0] & Micro[1]) | Micro[2]; // low when Micro == 3
	assign nNOPulse	= !NOPulse;										
	assign NOPEnable	= !(!Data[6] & AddrH[15] & nHALT) | nM1;
	
	always @(posedge nNOPulse or negedge nL_PRE)
	begin
		if(!nL_PRE)
			NOPTC <= 1'b1;
		else 
			NOPTC <= NOPEnable;
	end
	
	assign nDO_NOP	= NOPulse | NOPTC;
	
	reg [7:0] CharLatch;
	//assign CharLatchEnable	= (Micro[0] & Micro[1]) | Micro[2]; // Micro >= 3
	assign CharLatchEnable	= (Micro[2:0] == 2);
	
	always @(posedge CharLatchEnable)
	begin
		CharLatch[7:0] <= {Data[7], 1'b0, Data[5:0]};
	end
	
	reg [2:0] RowCount;
	
	always @(negedge HSync or posedge VSync)
	begin
		if(VSync)
			RowCount[2:0] <= 3'b0;
		else
			RowCount <= RowCount+1;
	end
	
	assign CharLatchOE 	= ((Micro[0] | Micro[1]) & Micro[2]);	// Micro > 4
	assign AddrL[8:0]		= CharLatchOE ? {CharLatch[5:0], RowCount[2:0]} : 9'bz; 

	// Read of keyboard / tape port 
	// Forced NOP
	// Tri-state data lines unless reading location $FE
	wire [7:0] DataOut;

	assign DataEn			= !nDO_NOP | !nFERD;
	assign DataOut[7:0]	= nFERD ? {8'b0} : {TapeIn, USAUK, 1'b0, Kbd[4:0]};
	assign Data				= DataEn ? DataOut : 8'bz;
	
	// Video inverter
	reg Invert2;
	
	assign InvertCLK	= (Micro[0] & Micro[1] & Micro[2]);
	assign LD_SHIFT	= !InvertCLK | NOPTC;
	
	always @(posedge InvertCLK) 
	begin
		Invert2 <= !CharLatch[7] | NOPTC;
	end
	
	always @(posedge CLK65 or negedge LD_SHIFT)
	begin
		if (!LD_SHIFT)
			ShiftReg[7:0] <= Data[7:0];
		else
			ShiftReg[7:0] <= {ShiftReg[6:0], 1'b0};
	end
	
	assign VidOut	= ShiftReg[7] ^ Invert2;
	assign CSync	= !HSync ^ VSync;
	
	assign PortA[7:0]	= HCount[7:0];
	assign PortB[7:0]	= {HSync,VSync,DataEn,CharLatchOE, Micro[3:0]};
	assign PortC[7:0]	= {NMIEnable,NMIDisable,nDO_NOP , NOPTC,NOPEnable,NOPulse , NMIon, nNMI};
	
endmodule

Note also that PortA, PortB and PortC are just some of the unused pins wired to 2x5 0.1" headers so that I can use them for monitoring internal signals for debugging purposes.

Cheers.

Phill.
User avatar
Andy Rea
Posts: 1606
Joined: Fri May 09, 2008 2:48 pm
Location: Planet Earth
Contact:

Re: ULA revistited.

Post by Andy Rea »

Thats pretty cool, i've managed to learn enough Verilog in the last week or so that i can understand what's going on :D 2 weeks ago i would have been guessing at best.

first thing, so away with the logic that inverts the video comming out of the shift register, it was well rubbish on the original board i built. and invert the data bus on the load of the shift register instead, you should already of captured bit 7 when you grabbed the databus for the alternate rom addressing.

adding blanking is also quite easy. imediatly after the Hsync, the hysnc counter resets to 0, bit 4 will then stay low for another 16 clock cycles (3.25Mhz)

so add a latch to start blanking at the begging of the hsync pulse, and clear it when bit 4 of the count goes high,

then alter the shift register so that the output bit remains low whilst the blanking is active.

leave the rest of the shift register to do it's stuff, i know of at least one program that outputs active video during the blanking time.

Regards Andy
what's that Smell.... smells like fresh flux and solder fumes...
Prime
Posts: 134
Joined: Thu Mar 24, 2011 3:02 am

Re: ULA revistited.

Post by Prime »

Success !

It's now working well enough to enter programs without getting any corrupt characters.

I've posted a couple of images here : http://protein.bio.warwick.ac.uk/~bshu/ZX81-ULA/ as they are too big to upload.
Just uploaded Eagle schematic and board files of the hardware I'm using to the above location.

Lateset version of verilog is here :

Code: Select all

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 		Ramoth
// Engineer: 		Phill.Harvey-Smith, based on pervious work by Andy Rea
// 
// Create Date:   17:52:53 03/24/2011 
// Design Name: 	ZX81 ULA replacement	
// Module Name:   ULA 
// Project Name: 
// Target Devices: XC9572 / XC95108
// Tool versions: 
// Description: 	To serve as a replacement for the ULA in the ZX81 computer.
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
// I have used the Acorn convention of prefixing active low outputs and inputs to
// che design with a lower case n so for example nRD would be an active loaw read 
// strobe
// 
// 2011-03-30 WooHoo it's working !
//
//////////////////////////////////////////////////////////////////////////////////
module ULA(
    inout [7:0] 	Data,			// Databus from Z80
    inout [15:14] AddrH,		// Two parts of Address bus
    inout [8:0] 	AddrL,
    
	 input 			nHALT,		// Halt from Z80
    input			nRD,			// Read and write strobes from Z80
	 input			nWR,
    input 			nIORQ,		// IO request from Z80
    input 			nMREQ,		// Memory request from Z80
    input 			nM1,			// M1 from Z80
    input 			USAUK,		// USA or UK jumper
	 
	 input 			CLKIN,		// 6.5MHz clock input signal
    input 			TapeIn,		// Input from cassette when loading
    input [4:0] 	Kbd,			// Keyboard row inputs
    
    output 			nNMI,
	 output 			CLKOUT,		// 3.5Mhz clock output to Z80 
    output 			nROMCS,		// ROP chip select signal
    output 			nRAMCS,		// RAM chip select signal
    output 			VidOut,		// Video output signal 
    output 			Blank,		// Video blanking signal
    output 			CSync,		// Composite Sync
	 
	 // Debugging ports
	 inout [7:0]	PortA,
	 inout [7:0]	PortB,
	 inout [7:0]	PortC
    );

// Local registers

	reg	[7:0]		HCount;		// HSync Counter 	
	reg	[3:0]		Micro;		// Micro counter
	reg	[7:0]		ShiftReg;	// Video shift register
	reg	CLK325;	
	reg VSync;
	reg NMIon;
	
	// Clock generator output clock to Z80 (3.25MHz), is generated by dividing input clock
	// 6.5MHz by 2.
	always @(posedge CLKIN)
	begin
		CLK325 <= !CLK325;
	end
	
	// Aliases just for convenience
	assign CLK65	= CLKIN;
	assign nCLK65	= !CLKIN;
	assign CLKOUT 	= CLK325;
	
	// HSync generation
	// Counter will be reset to 0 if the count reaches 207 or vsync is active.
	assign HClear 	= ((HCount==207) & CLK325) | VSync;
	assign Blank	= (HCount < 16);
	
	always @(negedge CLK325 or posedge HClear)
	begin
		if(HClear)
			HCount <= 0;
		else
			HCount <= HCount+1;
	end
	
	// HSync generated at end of count
	assign HSync	= HCount[6] & HCount[7];
	assign nHSync	= !HSync;
	
	// Chip selects
	// ROMCS active when mREQ valid and A14 low, RAMCS valid when MREQ active and A14 high
	// This effectivly devides the memory map up as follows :
	//	$0000 - $3FFF	ROMCS
	// $4000 - $7FFF	RAMCS
	// $8000 - $BFFF	ROMCS
	// $C000 - $FFFF	RAMCS
	assign nROMCS	= nMREQ | AddrH[14];
	assign nRAMCS	= nMREQ | !AddrH[14];
	
	// IO read and write strobes
	assign nIORD	= nRD | nIORQ;
	assign nIOWR	= nWR | nIORQ;
	
	// VSync generation
	// Read of location $FE
	assign nFERD		= nIORD | AddrL[0];
	assign VSyncOn		= (nFERD | NMIon);
	assign VSyncOff	= nIOWR;
	
	// Togge VSync on and off as needded
 	always @(negedge VSyncOn or negedge VSyncOff)
	begin
		if (!VSyncOn)
			VSync <= 1'b1;
		else
			VSync <= 1'b0;
	end
	
	// NMI control
	assign NMIEnable	= nIOWR | AddrL[0];
	assign NMIDisable	= nIOWR | AddrL[1];
	
	// Toggle NMIon on and off as needed
	always @(negedge NMIEnable or negedge NMIDisable)
	begin
		if (!NMIEnable)
			NMIon <= 1'b1;
		else
			NMIon <= 1'b0;
	end
	
	assign nNMI	= !NMIon | nHSync;
	//assign nNMI = 1'b1;
	
	// Micro counter
	reg MicroEnabled;
	assign L_PRE	= (Micro[3:0]==8); //!Micro[3]; 
	
	always @(posedge L_PRE or negedge nM1)
	begin
		if(L_PRE)
			MicroEnabled <= 1'b0;
		else
			MicroEnabled <= 1'b1;
	end
	
	// Only clock if enabled
	always @(negedge CLK65 or negedge MicroEnabled)
	begin
		if (!MicroEnabled)
			Micro <= 4'b0;
		else 
			Micro <= Micro+1;
	end
	
	// NOP Pulse
	reg	NOPTC;
	
	// On the CPLD the NOP pulse had to stretch over half cycles 3 and 4
	// or the Z80 will sometimes latch the character data instead of the NOP !
	assign NOPulse		= ((Micro[2:0]>=3) & (Micro[2:0]<=4)); 	
	assign NOPEnable	= !(!Data[6] & AddrH[15] & nHALT) | nM1;
	
	always @(posedge NOPulse or posedge L_PRE)
	begin
		if(L_PRE)
			NOPTC <= 1'b1;
		else 
			NOPTC <= NOPEnable;
	end
	
	assign nDO_NOP	= !NOPulse | NOPTC;
	
	// Character latch, latch the character in during half cycle 2, befor the NOP
	// is forced. The bottom 6 bits are the character data, bit 6 is always 0 so is
	// not latched, bit 7 is the Inverse bit
	reg [5:0] CharLatch;
	reg Invert;
	assign CharLatchEnable	= (Micro[2:0] == 2);
	
	always @(posedge CharLatchEnable)
	begin
		CharLatch[5:0] <= Data[5:0];
		Invert <= !Data[7];
	end
	
	reg [2:0] RowCount;
	
	always @(negedge HSync or posedge VSync)
	begin
		if(VSync)
			RowCount[2:0] <= 3'b0;
		else
			RowCount <= RowCount+1;
	end
	
	// Output the address of the required bit pattern for half cycles 5 till 7
	assign CharLatchOE 	= ((Micro[0] | Micro[1]) & Micro[2]);	// Micro > 4
	assign AddrL[8:0]		= CharLatchOE ? {CharLatch[5:0], RowCount[2:0]} : 9'bz; 

	// Data transfer from ULA to Z80 will happen when :
	// The Z80 reads port $FE : keyboard / usa-uk / tape port 
	// The ULA forces a NOP as part of the video cycle.
	// Otherwise the output data lines are tri-stated.
	wire [7:0] DataOut;

	assign DataEn			= !nDO_NOP | !nFERD;
	assign DataOut[7:0]	= nFERD ? {8'b0} : {TapeIn, USAUK, 1'b0, Kbd[4:0]};
	assign Data				= DataEn ? DataOut : 8'bz;
	
	// Load or shift, selects when the video shift register should load data from the 
	// bus or should shift it out as video.
	assign LD_SHIFT	= !(Micro[2:0]==7) | NOPTC;
	
	// Video shifter, latches bit pattern data from the bus and shifts out the video.
	// If the invert bit is set then the data is inverted as it is latched. 
	always @(posedge CLK65 /*or negedge LD_SHIFT*/)
	begin
		if (!LD_SHIFT)
			ShiftReg[7:0] <= Invert ? ~Data[7:0] : Data[7:0];
		else 
			ShiftReg[7:0] <= {ShiftReg[6:0], 1'b1};
	end
	
	// Generate video from blanking and syncs
	assign VidOut	= Blank ? 1'b0 : ShiftReg[7];
	assign CSync	= !HSync ^ VSync;
	
	// For debug.
	assign PortA[7:0]	= HCount[7:0];
	assign PortB[7:0]	= {HSync,VSync,DataEn,CharLatchOE, Micro[3:0]};
	assign PortC[7:0]	= {NMIEnable,NMIDisable,nDO_NOP , NOPTC,NOPEnable,NOPulse , NMIon, nNMI};
	
endmodule

Now to source some flatpack xc9572s......

Cheers.

Phill.
User avatar
Andy Rea
Posts: 1606
Joined: Fri May 09, 2008 2:48 pm
Location: Planet Earth
Contact:

Re: ULA revistited.

Post by Andy Rea »

Blimey thats fast work... do you make the PCB's yourself ?

regards the force nop timing, in my gal design it extends from the begining of the last 1/4 of T2 to the end of the first 1/4 of T3 (just one 6.5Mhz cycle) as the Z80 samples the data bus on the rising edge of T3 as you have discovered :lol:

i test the conditiond for a video cycle 1/2 (6.5Mhz clock) cycle before this as it's good to leave it as late as possible to allow for slow memory devices.. in fact now having just said that does anybody have a timmign diagram of a zeddy using the dynamic 16K ram pack ? thats ras n' cas n' all aint it so probably slower than static memory....

oh yeah one more tip. the databus needs to be open collector not just tri-state, there are some keyboard / joystick interfaces that plug into the extension slot that correctly decode port $FE (ferd as i call it) and they work, the only explanation is that the original ULA must be open collector so only drive the lines low and rely on the pull-up resistors to provide a logic high. i don't know about the xilinx software but the altera software i've been using defaulted to tristate i had to go into the pin assiments and tell it i wanted open collecter ( well open drain, but that another story :lol: )

Regards Andy
what's that Smell.... smells like fresh flux and solder fumes...
sirmorris
Posts: 2811
Joined: Thu May 08, 2008 5:45 pm

Re: ULA revistited.

Post by sirmorris »

I'm loving this thread - keep the pr0n coming ;)
Prime
Posts: 134
Joined: Thu Mar 24, 2011 3:02 am

Re: ULA revistited.

Post by Prime »

Andy Rea wrote:Blimey thats fast work... do you make the PCB's yourself ?
Yep :) Pretty much designed one night, exposed-developed-etched then drilled and soldered th next day :)
regards the force nop timing, in my gal design it extends from the begining of the last 1/4 of T2 to the end of the first 1/4 of T3 (just one 6.5Mhz cycle) as the Z80 samples the data bus on the rising edge of T3 as you have discovered :lol:

i test the conditiond for a video cycle 1/2 (6.5Mhz clock) cycle before this as it's good to leave it as late as possible to allow for slow memory devices.. in fact now having just said that does anybody have a timmign diagram of a zeddy using the dynamic 16K ram pack ? thats ras n' cas n' all aint it so probably slower than static memory....
Yeah I had to adjust the timing slightly, as it was not working correctly when I used a slower 6116 (insted of the 20ns SRAM that I was using !), or my Memotech RAM pack, seems to be working correctly now. I've not been able to test with a proper Sinclair ram pack as I don't have one, but will try it with the 3K ZX80 pack that I have.
oh yeah one more tip. the databus needs to be open collector not just tri-state, there are some keyboard / joystick interfaces that plug into the extension slot that correctly decode port $FE (ferd as i call it) and they work, the only explanation is that the original ULA must be open collector so only drive the lines low and rely on the pull-up resistors to provide a logic high. i don't know about the xilinx software but the altera software i've been using defaulted to tristate i had to go into the pin assiments and tell it i wanted open collecter ( well open drain, but that another story :lol: )
I checked by searching on the web and apparently defining it 8'bz should make it open collector / drain, so it should work. Humm I may try and see if I can design a JS/KBD interface to test that.... humm wonder if there are any schematics of such a device out there ?

@sirmorris I guess your ZXPAND with it's joystick emulation would qualify there, now wonder if there's room in your PIC for some PS/2 keyboard controler code....

Now if I can get the tape input to work correctly I'll be pretty much done, it works for 1K Chess, but fails to load QS Scramble, putting a scope probe looks like the QS Scramble tape has a lower level input so that may well be the problem, will disconnect the 47K and see if that helps. I know both tapes load fine on my (original** unmodified ZX81).

**Not only my first ZX81, but my first computer !.

Cheers.

Phill.
Prime
Posts: 134
Joined: Thu Mar 24, 2011 3:02 am

Re: ULA revistited.

Post by Prime »

sirmorris wrote:I'm loving this thread - keep the pr0n coming ;)
Yeah p0rn on a ZX81, not gonna be that good in monochrome 64x48 is it ????? Mind with ZXPAND and Hi-res.....

Mind I also have the Memotech hi-res interface, must reverse-engineer that at some point, I'd be more keen to do that but I only have one of them and don't wanna break it !

Cheers,

Phill.
Post Reply