My new emulator

Emulator and emulator development specific topics
Thommy
Posts: 52
Joined: Wed Sep 28, 2011 6:37 pm

My new emulator

Post by Thommy »

If you'll indulge me...

As I've said over on the hardware section of this forum, I'm working on a new emulator. It's structured quite differently from existing emulators, being based on the concept of completely sealed modules that communicate by signal wires — aping the original hardware rather than the normal structure of an emulator. So, for example, the Z80 emulation exposes 40 pins and devices interface with it by communicating on those pins. That's designed to be accurate to half a cycle, as per the timing diagrams provided on the Z80 data sheet. So I believe that should be the first Z80 emulation to be completely accurate to the data sheet.

For the purposes of emulation a ZX80, I have a fictional module that I've called a ULA which does all of the logic of the machine outside of the Z80 and a free-running CRT emulation that a video signal is passed to (unrealistically without a PAL encode and decode in between).

The emulation stuff is written in C99 for platform neutrality. I'm using the delegate and observer patterns widely, which I think is another structural difference between my code and other emulators. The user interface is platform specific and hooks itself in by providing appropriate delegates leaving the actual emulation stuff completely sealed and conformant with the model-view-controller pattern. I'm a Mac owner so the only user interface I've written so far is Cocoa based.

The advantages of this design are:
  • original machine logic can usually be implemented clearly and directly, leading to shorter code that is more likely to be bug free and accurate.
  • all modules are genuinely modular without introducing unrealistic timing characteristics.
  • the delegate pattern neatly separates platform-specific from platform-neutral code.
Disadvantages are all related to the processing cost, primarily. Such an emulation is at least an order of magnitude slower than a more traditional structure. Since a traditional structure can be made cycle accurate for a specific machine, there's an argument that the user doesn't benefit. My counterargument would be that the end user benefits because the emulation code is cleaner, more maintainable and significantly easier to extend, making those things more likely, and in any case doesn't suffer because modern machines can take the extra strain.

The current build has some performance areas I'd like to address but operates at about 35% CPU on my i3 2010 iMac. That compares with 3% at most for ZXSP. Though to put things in perspective, that means I could run about 12 emulated ZX80 machines before performance became a problem. Partly because I've not implemented a fast loading hack, tape input is currently echoed to output (in a scratchy way since I've done something wrong with my audio queue) when running and that costs a further 10%. At present the incoming signal is sampled at 6.5Mhz then a Kaiser window is used to strip inaudible frequencies before it's point sampled down to a more reasonable 44100Hz. I've been arguably gratuitous on that detail for the sake of testing code paths and so as to follow my rule that original logic is to be expressed where possible. The current build is attached for the curious, operation should be straightforward though I think the debugging panel can possibly still throw the emulator into an infinite loop so watch for that. In theory it'll work on any Intel machine with at least 10.6 but I've tested it only on a 10.7 machine, and haven't yet written an alternative code path to go fullscreen the old fashioned way. You may also need to rename your tape files to .80. And I'm already aware of several user interface bugs.

Clock Signal is the working title and I've vague aspirations to make the first accurate multi-machine emulator but no real plans. ZX81 support will definitely be forthcoming. Since all code is brand new, it's very likely currently to have inaccuracies due to errors. It's all written very cleanly and copiously commented, with the intention of going open source — I'm hopeful that means someone else will be able to deal with writing Windows, QT, KDE, etc front ends. On a soap opera front, I've no home Internet access for at least three weeks so I'm limited to commenting infrequently and only with such files as I remember to bring from home until then.

Relevant questions for forum posters:
  • what's the minimum amount of emulated hardware you'd consider acceptable in a ZX80 and ZX81 emulator?
  • what other pieces of hardware would it be nice to have?
  • what's the overall feeling with respect to the usual emulator fictions; debugging panels, instant loading, etc?
  • what about file format support? Presumably p/80/o/81 and TZX?
More general comments would also be welcomed...

EDITED: a couple of screenshots are now also attached, since Mac users are probably in the minority. They don't show anything all that interesting though.
Attachments
Screen Shot 2011-10-03 at 18.12.31.png
(147.88 KiB) Downloaded 4347 times
Screen Shot 2011-10-03 at 17.56.30.png
Screen Shot 2011-10-03 at 17.56.30.png (68.45 KiB) Viewed 9447 times
Clock Signal.zip
(92.58 KiB) Downloaded 299 times
Last edited by Thommy on Mon Oct 03, 2011 7:13 pm, edited 1 time in total.
User avatar
Paul
Posts: 1511
Joined: Thu May 27, 2010 8:15 am
Location: Germanys west end

Re: My new emulator

Post by Paul »

Thats great news.
I immediately downloaded it and can confirm that it won't start on OSX 10.5.4 :(
Would it be very difficult to support 10.5?

Cheers
Paul
In theory, there is no difference between theory and practice. But, in practice, there is.
Thommy
Posts: 52
Joined: Wed Sep 28, 2011 6:37 pm

Re: My new emulator

Post by Thommy »

It wouldn't be all that difficult at present. The sticking point is that I'm using blocks (the closure construct that Apple added to C and Objective-C in OS X v10.6) in order to thread hop for the purposes of running the emulation off the main thread without really having to do any work for myself. Detecting their availability at runtime and doing a route around is easy at present as they're only in OS-specific user interface stuff, which is tiny. I'm going to find a way to extend them into the emulation code but only in a platform neutral fashion so I can hopefully do the right stuff there.

With respect to other OSs, I used to be handy in win32 programming but I suspect that's no longer the thing unless you want your program to look like a refugee from 1995. I guess some sort of bridge into managed code and a WPF front-end would be the most user-friendly thing? I'm tending towards an MIT licence release when the moment comes, to try to encourage help.
sirmorris
Posts: 2811
Joined: Thu May 08, 2008 5:45 pm

Re: My new emulator

Post by sirmorris »

I'm sad I won't be seeing this for a while :(

I'm interested in helping out with a Windows port though my time is pretty limited.

* what's the minimum amount of emulated hardware you'd consider acceptable in a ZX80 and ZX81 emulator?
'80: 3 & 16K ram packs.
'81: 16K ram pack.

* what other pieces of hardware would it be nice to have?

ZXpand (natch ;¬), zon-x, UDG board, hires capability.

*what's the overall feeling with respect to the usual emulator fictions; debugging panels, instant loading, etc?

For me the debugger is paramount.

* what about file format support? Presumably p/80/o/81 and TZX?

You got it! The ability to snapshot the machine in its entirety - right down to the clock cycle - would be wonderful.


C
sirmorris
Posts: 2811
Joined: Thu May 08, 2008 5:45 pm

Re: My new emulator

Post by sirmorris »

Ha, I knew it was around here somewhere! Way back in 2008 I was thinking something along these lines:

viewtopic.php?t=69
viewtopic.php?f=3&t=128&p=1032#p1032

So now there is Clock Signal - which is the vast part of what I was asking for :) I don't imagine hanging a hardware expression of the bus from the back of the emulation would be difficult...
Thommy
Posts: 52
Joined: Wed Sep 28, 2011 6:37 pm

Re: My new emulator

Post by Thommy »

sirmorris wrote:So now there is Clock Signal
Ha, not yet there isn't. I'm still expecting to discover such flaws in my design as to make the implementation entirely wasted. Most of the reason I started the project is that I want to gain a better understanding of how things operate at the electrical level, the corollary being that I'll probably make many missteps along the way.

Anyway, thanks to all for the kind words. I can see I've quite a lot of work ahead of me. I guess ZX81 and TZX support are the immediate next things.

A new build is attached that quite probably may work on 10.5, but isn't tested. And being a twenty minute job, it takes the shortcut of just doing everything on the main thread if blocks aren't available — a very shoddy way to do things with obvious user experience and performance problems but that's just the temporary solution. Xcode can no longer build for PowerPC but I doubt that'll be a practical issue unless I can speed the thing up. I've also thrown in a couple of gratuitous new screenshots.

Audio should also be much improved and a potential tape loading bug is fixed, so it's good for everyone really. And given my schedule for the week I don't expect to get much more done for a while.

With respect to the linked idea of potentially exposing the simulated bus state via hardware, the current header file for my Z80 implementation is copied below. Comments may not be entirely accurate since they're primarily for my own benefit; I'm not going to do a proper release until I've had a time to review all my comments.

One the design decisions was that the Z80 can be integrated into an emulation as either a passive or an active component. You can use it entirely as a polled device, toggling clock to run it and then inspecting lines or you can build an emulator around it being the de facto logical component. That duality has the effect that it doesn't fully simulate a bus (eg, setting a line input actually sets it; if multiple devices are connected to the same signal then whoever sets most recently will replace the previous value) but it has a reasonably involved idea of observers and can be told to clock itself for a specified number of half cycles. I think I may have been a little too wishy washy on principles.

I've taken the pragmatic consideration that the rest of the ZX80 is a single module, so that stuff is set up to get and set Z80 lines appropriately and to output to the CRT correctly but otherwise to act as it likes internally. So it's intended to be completely accurate in how it sets the lines on which it communicates, and clean at a logical level but it's not implemented as a series of discrete ICs.

As the header implies, the whole project is less than a month old, so it's not necessarily entirely settled. Please criticise now — especially if I'm doing anything that looks just plain silly or wrong.

Code: Select all

//
//  Z80.h
//  LLZ80
//
//  Created by Thomas Harte on 11/09/2011.
//  Copyright (c) 2011. All rights reserved.
//

#ifndef LLZ80_Z80_h
#define LLZ80_Z80_h

/*

	Notes on Patterns
	=================

	This Z80 core is intended to be accurate to the nearest
	half cycle.

	External devices connect to the Z80 via its pins. This
	code is written in terms of 'signals', which are the things
	carried by pins. Quite a lot of them are Boolean, but the
	data signal is a collection of eight of the original pins
	and the address signal is a collection of sixteen of the
	original pins.
	
	Connected devices are observers. The simulated Z80 actually
	has quite a lot of boiler-plate logic built in. So observers
	generally request to be notified only when a given test
	is satisfied.

	Supplied tests are:

		-	when any of a given set of Boolean signals changes value
		-	when a given set of Boolean signals assumes a specified
			state
		-	a combination of the above two

	For the purposes of supplying debugging tools and for loading
	and saving machine state files, the following functionality is
	also provided:

		-	call outs in between every instruction fetch
		-	reading and writing of otherwise unexposed internal
			state, including all registers
		-	an internal half-cycle timing count is kept, which
			can be read from or written to

	Note that the semantics related to active lines may initially
	be confusing. To allow logic to be expressed clearly in C, an
	active Boolean signal has a non-zero value and an inactive
	signal has a zero value. The alternative was to give an active
	line a zero value because on the Z80 lines are active low and
	both zero and a low signal indicate absence.

	Although I was conflicted and the aim of this simulator is to
	be accurate to real hardware, I decided to favour semantics
	over the voltage metaphor.

*/

#include <stdint.h>

/*

	Creation and destruction. We're reference counting.

		Use llz80_create to create a new Z80 instance.
		You'll get back NULL on failure or an opaque
		handle to a Z80, which is an owning reference.

		Use llz80_retain and llz80_release to increment
		and decrement the retain count. The Z80 will
		be deallocated and all memory released back
		to the system when the retain count gets to zero.

*/
void *llz80_create(void);
void *llz80_retain(void *z80);	// returns the same instance passed in
void llz80_release(void *z80);

/*

	Signal observing.

		Just like a real z80, external devices
		communicate with the Z80 by observing
		changes in its signal lines. Arbitrarily
		many observers can be added to each Z80
		instance.

		Observers can ask to be notified when
		specified lines change, when given lines
		assume a certain value or when a combination
		of those two things happens. Observers are
		always notified as soon as the given
		condition is satisfied, then not again unless
		it ceases to be satisfied and becomes
		satisfeied again.

		See the getters and setters below for
		information on how to read and write actual
		line values.

		The return result when adding observers is
		NULL on failure or an opaque handle. Use the
		handle to remove the observer later on.

		There's no need to remove all observers
		manually when destroying a Z80.

*/
typedef enum
{
	// read only signals
	LLZ80SignalAddress				= 0x8000,
	LLZ80SignalInputOutputRequest	= 0x2,
	LLZ80SignalMachineCycleOne		= 0x4,
	LLZ80SignalRead					= 0x8,
	LLZ80SignalWrite				= 0x10,
	LLZ80SignalMemoryRequest		= 0x20,
	LLZ80SignalRefresh				= 0x40,
	LLZ80SignalBusAcknowledge		= 0x80,
	LLZ80SignalHalt					= 0x100,

	// read/write signals (but see notes re:clock)
	LLZ80SignalData					= 0x4000,
	LLZ80SignalClock				= 0x400,

	// logically write only signals
	// (though the last value written
	// can be read back as a fiction
	// of the emulation)
	LLZ80SignalInterruptRequest		= 0x800,
	LLZ80SignalNonMaskableInterruptRequest	= 0x1000,
	LLZ80SignalReset				= 0x2000,
	LLZ80SignalWait					= 0x1,
	LLZ80SignalBusRequest			= 0x200
} LLZ80Signal;

typedef void (* llz80_signalObserver)(void *z80, unsigned int changedLines, void *context);

// a standard observer is contacted every time any of the nominated lines changes
void *llz80_addSignalObserver(void *z80, llz80_signalObserver observer, unsigned int linesToObserve, void *context);

// a mask condition observer is contacted if one of the nominated lines changes,
// and subsequently the lines described by the mask have the values given
// in the value. It possibly feels a bit convoluted to have three fields, but
// the point is that you can watch some lines for any change while watching
// others for only when they carry a specific state.
void *llz80_addSignalObserverWithMaskCondition(
	void *z80,
	llz80_signalObserver observer,
	unsigned int linesToObserve,	// you'll be notified if any of these lines changes, and...
	unsigned int lineMask,			// ... these lines ...
	unsigned int lineValues,		// ... have these values (ie, list all that you want active here)
	void *context);

// a test condition is a simplified mask condition; the observer fires only when
// the nominated transition so as to have the nominated value.
void *llz80_addSignalObserverWithTestCondition(
	void *z80,
	llz80_signalObserver observer,
	unsigned int linesToObserve,	// you'll be notified when these lines ...
	unsigned int lineValues,		// ... assume these values
	void *context);

// an active condition is a simplified mask condition; the observer will fire
// when the nominated lines all become active simultaneously
void *llz80_addSignalObserverWithActiveCondition(
	void *z80,
	llz80_signalObserver observer,
	unsigned int lineValues,		// you'll be notified when these lines all go active
	void *context);

// remove observer does as the name says; pass in the opaque handle you received when
// adding the observer
void llz80_removeSignalObserver(void *z80, void *observerHandle);

/*

	Getters and setters.

		These methods can be used to read or write
		the Z80 signals. Note that Boolean lines
		are set as active or inactive using the
		defined constants.

		Ints are used for getting and setting. The
		signal lines will be either LLZ80_INACTIVE
		or LLZ80_ACTIVE. Reading the data lines will
		return an 8 bit value padded up to an int.
		Reading the address lines will return a 16 bit
		value padded up to an int. In both cases the
		extra bits are 0 - this is explicitly not a
		sign extension.

*/

#define LLZ80_ACTIVE	-1
#define LLZ80_INACTIVE	0

void llz80_setSignal(void *z80, LLZ80Signal signal, int value);
int llz80_getSignal(void *z80, LLZ80Signal signal);

/*

	Call this method to run an instance of the Z80
	for the nominated number of half cycles. If the
	Z80 is clocked at 1.5 Mhz, calling this with an
	argument of 3,000,000 has the effect of running
	the Z80 for one second.

*/
void llz80_runForHalfCycles(void *z80, unsigned int numberOfHalfCycles);
void llz80_stopRunning(void *z80);

/*

	Monitoring functionality.

		A bunch of unrealistic hooks that allow certain
		components of the Z80's internal state to be
		observed or polled.

		Specifically: an observer can be notified whenever
		a new instruction fetch is about to occur (ie, in
		between opcodes).

		Internal registers can be read or written. You can't
		do that on a real Z80 and no external emulated
		components should do so. This functionality is
		provided primarily so that debuggers can be built
		and state saves can be implemented.

*/

typedef void (* llz80_instructionObserver)(void *z80, void *context);
void *llz80_monitor_addInstructionObserver(void *z80, llz80_instructionObserver observer, void *context);
void llz80_monitor_removeInstructionObserver(void *z80, void *observer);

typedef enum
{
	// normal registers
	LLZ80MonitorValueARegister,	LLZ80MonitorValueFRegister,
	LLZ80MonitorValueBRegister,	LLZ80MonitorValueCRegister,
	LLZ80MonitorValueDRegister,	LLZ80MonitorValueERegister,
	LLZ80MonitorValueHRegister,	LLZ80MonitorValueLRegister,

	LLZ80MonitorValueAFRegister,
	LLZ80MonitorValueBCRegister,
	LLZ80MonitorValueDERegister,
	LLZ80MonitorValueHLRegister,

	// dash registers
	LLZ80MonitorValueADashRegister,	LLZ80MonitorValueFDashRegister,
	LLZ80MonitorValueBDashRegister,	LLZ80MonitorValueCDashRegister,
	LLZ80MonitorValueDDashRegister,	LLZ80MonitorValueEDashRegister,
	LLZ80MonitorValueHDashRegister,	LLZ80MonitorValueLDashRegister,

	LLZ80MonitorValueAFDashRegister,
	LLZ80MonitorValueBCDashRegister,
	LLZ80MonitorValueDEDashRegister,
	LLZ80MonitorValueHLDashRegister,

	// miscellaneous registers
	LLZ80MonitorValueRRegister,
	LLZ80MonitorValueIRegister,

	// special-purpose registers
	LLZ80MonitorValueIXRegister,
	LLZ80MonitorValueIYRegister,
	LLZ80MonitorValueSPRegister,
	LLZ80MonitorValuePCRegister,

	// flags
	LLZ80MonitorValueIFF1Flag,
	LLZ80MonitorValueIFF2Flag,

	// misc
	LLZ80MonitorValueInterruptMode,

	// fictitious
	LLZ80MonitorValueHalfCyclesToDate

} LLZ80MonitorValue;

/*

	The getter and setter for those 'values' listed above, which
	are all registers and register pairs, the interrupt flags and
	mode and the count of half cycles since this z80 began running.

	The half cycle count is guaranteed to be accurate to half-a-cyle.

	Most other values are guaranteed to be accurate only to within
	the current whole instruction. Since these aren't exposed by a
	real Z80, it's more than sufficiently accurate for a register
	modified by an opcode to be modified anywhere during the time
	that operation is documented to take. 

	The exceptions are the I and R registers. They are exposed by
	a real Z80 since together they form the refresh address. They
	therefore should be accurate to the half cycle.

	Corollary: if you're writing values, you probably want to do
	it within an instruction observer, to ensure you don't adjust
	state mid-operation and end up with an unpredictable result.

*/
int llz80_monitor_getInternalValue(void *z80, LLZ80MonitorValue key);
void llz80_monitor_setInternalValue(void *z80, LLZ80MonitorValue key, int value);

#endif
Attachments
Screen Shot 2011-10-04 at 00.52.10.png
(707.92 KiB) Downloaded 4272 times
Screen Shot 2011-10-04 at 00.45.52.png
(512.19 KiB) Downloaded 4270 times
Clock Signal 2.zip
(93.22 KiB) Downloaded 260 times
Thommy
Posts: 52
Joined: Wed Sep 28, 2011 6:37 pm

Re: My new emulator

Post by Thommy »

To comment on these directly..
sirmorris wrote:I'm sad I won't be seeing this for a while :(

I'm interested in helping out with a Windows port though my time is pretty limited.
Yes, sorry about that. I have a Windows 7 virtual machine so it's not necessarily a lost cause, I just don't really know where to start nowadays.
sirmorris wrote:* what's the minimum amount of emulated hardware you'd consider acceptable in a ZX80 and ZX81 emulator?
'80: 3 & 16K ram packs.
'81: 16K ram pack.
I've negligently offered 1k, 2k and 16k as options at the minute. I guess I was thinking of one of the Timex machines. I'll just change the 2k option.
sirmorris wrote:* what other pieces of hardware would it be nice to have?

ZXpand (natch ;¬), zon-x, UDG board, hires capability.
High resolution graphics of all varieties should just work, assuming I've read the available documentation correctly. As far as that aspect goes, I've decided that all RAM expansions are static RAM and are connected to load the bus on a refresh cycle — i.e. they act just like the internal RAM does. So the emulator is following what I think is the accurate display cycle (if you forgive me for inaccurately referring to 'the ULA'; it's convenient even if grossly inaccurate):
  • M1 + read + an address with bit 15 set causes the ULA to consider the value on the bus. If bit 6 isn't set then a NOP is forced, the inverse flag is read and the low 6 bits are kept until the next refresh cycle...
  • during the next refresh cycle the video address is built from parts of the refresh address, the 6 bits just kept and the line counter. Either the ROM serves that address or it declines to; if it declines then the bus value loaded by the [static] RAM in response to the refresh cycle ends up in the video shift register.
  • the shift register is shifted out onto the video line, with the inverse flag applied
  • starting sync zeroes the line counter and starts outputting the sync level to the CRT, which (very, very approximately) charges up a capacitor as sync accumulates which, if left to charge sufficiently will fire a vertical sync; the capacitor leaks at other times
  • a zero in bit 6 of the refresh address requests an interrupt, an interrupt acknowledge (m1 + ioreq, from memory, but the code was written with data sheets to hand so don't worry if I've got that wrong) starts the horizontal sync process, which is subsequently clocked on M1
The CRT is responsible for obeying vertical and horizontal syncs appropriately. At present is uses an accurate mechanism for vertical sync, obeying them only if they occur during the valid period and otherwise automatically triggering one a little after it would normally expect one. Horizontal sync inaccurately applies the same logic; I'm working on flywheel strategies that will much more closely match real CRTs. However, the reason different logic is applied to horizontal sync on real TVs is that noise is a real problem, which it obviously isn't here.

I have to admit that I've not found any documentation for the other things yet. I think a ZonX is just an AY, in which case that's fairly easy, and presumably a UDG board does something like replace the relevant ROM areas with RAM?

I'll ask about ZXpand when an appropriate moment comes.
sirmorris wrote:*what's the overall feeling with respect to the usual emulator fictions; debugging panels, instant loading, etc?

For me the debugger is paramount.
What about external connections for a debugger? Is there a well-established intermediate format for source code or an assembler capable of being extended so that a debugger could display your actual original code rather than a mere disassembly?

Would it be beneficial to open the debugger to external control so that you could have an editor/compiler that connected to the emulator?
sirmorris wrote:* what about file format support? Presumably p/80/o/81 and TZX?

You got it! The ability to snapshot the machine in its entirety - right down to the clock cycle - would be wonderful.
Cycle accurate snapshots are potentially a complicated issue because I don't have any real documentation on when the Z80 mutates its internal state. It's obvious a lot of the time from the memory access patterns but not always explicit. As such I guarantee only that internal, unexposed state will be correct to the nearest whole instruction. I think that, given the level of documentation, in terms of creating a robust file format you probably want to be able to store in terms of "unexposed internal state was this at end of last instruction, and we're now x half cycles into the current instruction". That prima facie means being able to rewind the emulator, which realistically probably means storing the state at appropriate intervals and advancing from there to the current time. Though I guess then you can find a way not to spend that much during the normal flow of things and do heavy lifting only on save, so it isn't too much of a performance issue.

Traditional emulators dodge that whole minefield by rounding all timings to the nearest whole instruction, I guess.
User avatar
Paul
Posts: 1511
Joined: Thu May 27, 2010 8:15 am
Location: Germanys west end

Re: My new emulator

Post by Paul »

Hi Thommy,
Memory modells:
For ZX80 there was no 2K Version afaik. There was 1K internal, 3K external and later 16K external.
2K was for Timex1000,
ZX81 was 1K and there were External Rampacks in 16K, 32K and 64K, where 64K was giving 56K Memory waisting 8K what was ROM.
For a start 1K, 16K and maybe another 8K from 8192 to 16384 are sufficient for most tests with this emulator.
Later, for advanced features, RAM in all areas in blocks of 8K, with writeprotection in 4K blocks(8-16K area) would be very good.
There are some machines that have a high memory block introduced at 0-8K, as a ROM replacement, and this is Writeprotected in 0-8K Area, but the shadow of it above 32768 is write enabled so mainpulation in that area will be in the "ROM" area immediately so it can be patched on the fly. In the ZX2000, for example, this high memroy area is from 8000h to 9FFFh. RAM configurations in ZX2000 are manipulated with a "POKE-CARD". Poke 7,11 will insert RAM from 8000h to 9FFFh at 0h,
Poke 7,136 would do the same, but also initiate a RESET to start the new loaded OS.
Last edited by Paul on Tue Oct 04, 2011 4:00 pm, edited 1 time in total.
In theory, there is no difference between theory and practice. But, in practice, there is.
zx80nut
Posts: 108
Joined: Mon May 23, 2011 2:10 pm
Location: A bit north of Cardiff, Wales.
Contact:

Re: My new emulator

Post by zx80nut »

Hi.
The level of detail that you have considered is impressive :)

The mod that I have for graphic chars/UDG is by dK'tronics, which is on a small board containing a ROM and a couple of other chips. I haven't looked to see how the 4K graphics ROM fits into the existing memory map, though (it contains both graphics and code).
The board also contains a socket to plug in a RAM chip to allow UDG.

...I must fit it sometime.

Grant.
User avatar
PokeMon
Posts: 2264
Joined: Sat Sep 17, 2011 6:48 pm

Re: My new emulator

Post by PokeMon »

Thommy wrote: Most of the reason I started the project is that I want to gain a better understanding of how things operate at the electrical level, the corollary being that I'll probably make many missteps along the way.
Why don't you buy an oscilloscope or a logic analyzer to see what's going on ? So you are now working only in a virtual world.
Anyway, have fun. :mrgreen:
Post Reply