Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
9837 | turbocat | 1 | Multi-Z80 32 Bit emulator |
2 | Copyright 1996, 1997, 1998, 1999, 2000 - Neil Bradley, All rights reserved |
||
3 | |||
4 | MZ80 License agreement |
||
5 | ----------------------- |
||
6 | |||
7 | (MZ80 Refers to both the assembly code emitted by makez80.c and makez80.c |
||
8 | itself) |
||
9 | |||
10 | MZ80 May be distributed in unmodified form to any medium. |
||
11 | |||
12 | MZ80 May not be sold, or sold as a part of a commercial package without |
||
13 | the express written permission of Neil Bradley (neil@synthcom.com). This |
||
14 | includes shareware. |
||
15 | |||
16 | Modified versions of MZ80 may not be publicly redistributed without author |
||
17 | approval (neil@synthcom.com). This includes distributing via a publicly |
||
18 | accessible LAN. You may make your own source modifications and distribute |
||
19 | MZ80 in source or object form, but if you make modifications to MZ80 |
||
20 | then it should be noted in the top as a comment in makez80.c. |
||
21 | |||
22 | MZ80 Licensing for commercial applications is available. Please email |
||
23 | neil@synthcom.com for details. |
||
24 | |||
25 | Synthcom Systems, Inc, and Neil Bradley will not be held responsible for |
||
26 | any damage done by the use of MZ80. It is purely "as-is". |
||
27 | |||
28 | If you use MZ80 in a freeware application, credit in the following text: |
||
29 | |||
30 | "Multi-Z80 CPU emulator by Neil Bradley (neil@synthcom.com)" |
||
31 | |||
32 | must accompany the freeware application within the application itself or |
||
33 | in the documentation. |
||
34 | |||
35 | Legal stuff aside: |
||
36 | |||
37 | If you find problems with MZ80, please email the author so they can get |
||
38 | resolved. If you find a bug and fix it, please also email the author so |
||
39 | that those bug fixes can be propogated to the installed base of MZ80 |
||
40 | users. If you find performance improvements or problems with MZ80, please |
||
41 | email the author with your changes/suggestions and they will be rolled in |
||
42 | with subsequent releases of MZ80. |
||
43 | |||
44 | The whole idea of this emulator is to have the fastest available 32 bit |
||
45 | Multi-Z80 emulator for the x86, giving maximum performance. |
||
46 | |||
47 | MZ80 Contact information |
||
48 | ------------------------- |
||
49 | |||
50 | Author : Neil Bradley (neil@synthcom.com) |
||
51 | Distribution: ftp://ftp.synthcom.com/pub/emulators/cpu/makez80.zip (latest) |
||
52 | |||
53 | You can join the cpuemu mailing list on Synthcom for discussion of Neil |
||
54 | Bradley's Z80 (and other) CPU emulators. Send a message to |
||
55 | "cpuemu-request@synthcom.com" with "subscribe" in the message body. The |
||
56 | traffic is fairly low, and is used as a general discussion and announcement |
||
57 | for aforementioned emulators. |
||
58 | |||
59 | |||
60 | MZ80 Documentation |
||
61 | ------------------- |
||
62 | |||
63 | MZ80 Is a full featured Z80 emulator coded in 32 bit assembly. It runs well |
||
64 | over a hundred games, in addition to it supporting many undocumented Z80 |
||
65 | instructions required to run some of the Midway MCR games, Galaga, and |
||
66 | countless other wonderful Z80 based arcade games. |
||
67 | |||
68 | MZ80 Contains a makez80.c program that must be compiled. It is the program |
||
69 | that emits the assembly code that NASM will compile. This minimizes the |
||
70 | possibility of bugs creeping in to MZ80 for the different addressing modes |
||
71 | for each instruction. It requires NASM 0.97 or greater. |
||
72 | |||
73 | The goal of MZ80 is to have a high performance Z80 emulator that is capable |
||
74 | of running multiple emulations concurrently at full speed, even on lower-end |
||
75 | machines (486/33). MZ80 Harnesses the striking similarities of both the Z80 |
||
76 | and the x86 instruction sets to take advantage of flag handling which greatly |
||
77 | reduces the time required to emulate a processor, so no extra time is spent |
||
78 | computing things that are already available in the native x86 processor, |
||
79 | allowing it to perform leaps and bounds over comparable C based Z80 emulators |
||
80 | on the same platform. |
||
81 | |||
82 | MZ80 Is designed exclusively for use with NASM, the Netwide Assembler. This |
||
83 | gives the ultimate in flexibility, as NASM can emit object files that work |
||
84 | with Watcom, Microsoft Visual C++ (4.0-current), DJGPP, Borland C++, and |
||
85 | gcc under FreeBSD or Linux. MZ80 Has been tested with each one of these |
||
86 | compilers and is known to work properly on each. |
||
87 | |||
88 | |||
89 | What's in the package |
||
90 | --------------------- |
||
91 | |||
92 | MZ80.TXT - This text file |
||
93 | |||
94 | MAKEZ80.C - Multi Z80 32 Bit emulator emitter program |
||
95 | |||
96 | MZ80.H - C Header file for MZ80 functions |
||
97 | |||
98 | |||
99 | What's new in this release |
||
100 | -------------------------- |
||
101 | |||
102 | Revision 3.4: |
||
103 | |||
104 | * Fixed the overflow flag not getting cleared in the SetOverflow() |
||
105 | routine. It caused strange problems with a handful of Genesis games |
||
106 | * Removed invalid instruction in the C version so that more |
||
107 | instructions will execute |
||
108 | |||
109 | Revision 3.3: |
||
110 | |||
111 | * Undocumented opcodes added to the C emitter |
||
112 | * Bug fix to the C emission that properly handles shared RAM regions |
||
113 | (I.E. with handlers that are NULL) |
||
114 | * Now using 32 bit registers to do register/memory access. Slight |
||
115 | speed increase (assembly version only) |
||
116 | |||
117 | Revision 3.2: |
||
118 | |||
119 | * R Register emulation now accurate with a real Z80 |
||
120 | * mz80int() Called when interrupts are disabled causes the |
||
121 | z80intPending flag to be set, and an interrupt will be caused after |
||
122 | the execution of EI and the next instruction. See "IMPORTANT NOTE |
||
123 | ABOUT INTERRUPTS" below |
||
124 | * The instruction after EI executes fully before interrupt status is |
||
125 | checked. (as does a real Z80) |
||
126 | |||
127 | |||
128 | Revision 3.1: |
||
129 | |||
130 | * Fixed bug in memory dereference when handler was set to NULL (keeps |
||
131 | system from crashing or faulting) |
||
132 | * Removed the only stricmp() from the entire file and replaced it |
||
133 | with strcmp() so that stdlibs without it will compile |
||
134 | * Changed cyclesRemaining > 0 to cyclesRemaining >= 0 to be compatible |
||
135 | with the ASM core |
||
136 | * Removed additional sub [dwCyclesRemaining], 5 at the beginning of |
||
137 | mz80exec() (ASM Core only). Increases timing accuracy. |
||
138 | * NMIs And INTs add additional time to dwElapsedTicks as it should |
||
139 | * mz80ReleaseTimeslice() Sets remaining clocks to 0 instead of 1 |
||
140 | |||
141 | |||
142 | Revision 3.0: |
||
143 | |||
144 | * All instructions validated against a real Z80. Used an ISA card |
||
145 | with a Z80 on it to validate flag handling, instruction handling, |
||
146 | timing, and other goodies. The only thing not implemented/emulated |
||
147 | is flag bit 3 & 5 emulation. Believed to be 100% bug free! |
||
148 | * 80% Speed improvement over version 2.7 of mz80 |
||
149 | * z80stb.c Removed. Use -c to emit a C version of mz80! API compatible! |
||
150 | Note that this is mostly, but not fully, debugged, so consider the |
||
151 | C version a beta! It's at least healthier than z80stb.c was. The C |
||
152 | version does not include the undocumented Z80 instructions. |
||
153 | * mz80nmi() No longer trashes registers it uses when using -cs |
||
154 | * IN/OUT Instructions work properly when using -16 |
||
155 | * IN A, (xxh) uses A as high 8 bits of I/O fetch address when using -16 |
||
156 | * IM 0/IM 1 Description in documentation fixed |
||
157 | * Sizes of all context registers increased to 32 bits - for speed! |
||
158 | * IFF1/IFF2 Now properly emulated |
||
159 | * JR Instruction offset can fetch from $ffff and properly wrap |
||
160 | * LDIR/LDDR Instruction now won't go to completion - instead it will |
||
161 | run until BC=0 or the # of cycles to execute have expired. These |
||
162 | instructions used to run to completion - even beyond the # of cycles |
||
163 | left to execute |
||
164 | * INI/IND/INIR/INDR countdown bug fixed - it was decrementing B twice |
||
165 | for each IN! Whoops! |
||
166 | * If you specify NULL as a handler address to a memory region, mz80 will |
||
167 | use vpData as a pointer to where that block of data resides. Quite |
||
168 | useful for multiprocessor emulations that share the same memory. |
||
169 | * EDI Now keeps track of cycle counting for faster execution |
||
170 | * Modified memory region scanning code to use 32 bit registers instead |
||
171 | of their 16 bit counterparts |
||
172 | * Get/SetContext() uses rep movsd/movsb. Insignificant overall, but |
||
173 | why waste the time? |
||
174 | * Debugging routines added. See the "DEBUGGING" section below for more |
||
175 | information. NOTE: The debugging routines are not yet available in |
||
176 | the C emission. |
||
177 | * Timing done slightly differently now. Mz80 now executes one |
||
178 | instruction past the timing given on input. For example, mz80exec(0) |
||
179 | will cause a single instruction to be executed (thusly -ss was |
||
180 | removed). |
||
181 | |||
182 | Revision 2.7: |
||
183 | |||
184 | * Fixed OTIR/OTDR/INIR/INDR instructions so their 16 bit counterparts |
||
185 | work properly |
||
186 | * Emulation core 30-70% faster overall than 2.6 due to optimization to |
||
187 | the timing routines |
||
188 | * Replaced word reads/writes with a special word write routine rather |
||
189 | than the standard calling to read/write byte functions |
||
190 | * z80stb.c (the C equivalent of mz80) compiles properly now |
||
191 | * Fixed OS/2 text/segment issue |
||
192 | * Fixed bug in set/getCPU context that ensures that ES=DS and avoids |
||
193 | crashes. Caused crashes under OS/2 and other OS's |
||
194 | |||
195 | Revision 2.6: |
||
196 | |||
197 | * Emulator core 5-30% faster overall. Some 16 and 8 bit instructions |
||
198 | sped up when using their 32 bit equivalents. |
||
199 | * Fix to -l so that proper labels without leading and trailing |
||
200 | underscores so Linux/FreeBSD compiles will work properly |
||
201 | * Single step now executes the # of instructions passed in to z80exec() |
||
202 | instead of just 1 as it had in prior releases. This is only active |
||
203 | when the -ss option is used. |
||
204 | * The -nt option was added. This will cause the timing information to |
||
205 | not be added in, speeding up execution. Warning: Only do this if your |
||
206 | emulated target does not require instruction timing! |
||
207 | * Updated documentation errors |
||
208 | * C Version of mz80 (mz80.c) that is API compliant is distributed with |
||
209 | the archive (With kind permission of Edward Massey). |
||
210 | |||
211 | Revision 2.5: |
||
212 | |||
213 | * Fixed an unconditional flag being cleared in the ddcbxx instructions. |
||
214 | It caused Donkey Kong's barrels to not roll. |
||
215 | |||
216 | Revision 2.4: |
||
217 | |||
218 | * Fixed improper HALT handling (didn't advance the PTR when it should) |
||
219 | * Fixed SRL (IX+$xx) instruction so that carry wasn't trashed |
||
220 | * Fixed single stepping problems with it giving too much time to |
||
221 | any given instruction |
||
222 | * Fixed half carry flag handling with 16 bit SBC and ADD instructions |
||
223 | * Fixed DAA emulation so that parity flags weren't getting trashed |
||
224 | |||
225 | Revision 2.3: |
||
226 | |||
227 | * Fixed many stack handling bugs |
||
228 | * Timing problems fixed. The prior version was causing massive |
||
229 | overruns on maximum timeslices with some insutructions. |
||
230 | |||
231 | Revision 2.2: |
||
232 | |||
233 | * Fixed a bug in CPI/CPD/CPIR/CPDR that mishandled flags |
||
234 | * All known bugs are out of mz80 now |
||
235 | * Added the -cs option to route all stack operations through the |
||
236 | handlers (required for games like Galaga) |
||
237 | |||
238 | Revision 2.1: |
||
239 | |||
240 | * Fixed a bug in CPI/CPD/CPIR/CPDR that caused intermittent lockups. |
||
241 | Also fixed a bug that caused erratic behavior in several video games. |
||
242 | * Added INI/IND/INIR/INDR instruction group |
||
243 | * Added OUTI/OUTD/OTIR/OTDR instruction group |
||
244 | |||
245 | Revision 1.0: |
||
246 | |||
247 | * First release! The whole thing is new! |
||
248 | |||
249 | |||
250 | ASSEMBLING FOR USE WITH WATCOM C/C++ |
||
251 | ------------------------------------ |
||
252 | |||
253 | Watcom, by default, uses register calling conventions, as does MZ80. To |
||
254 | create a proper emulator for Watcom: |
||
255 | |||
256 | makez80 MZ80.asm -x86 |
||
257 | |||
258 | From here: |
||
259 | |||
260 | nasm -f win32 MZ80.asm |
||
261 | |||
262 | Link the MZ80.obj with your Watcom linker. |
||
263 | |||
264 | |||
265 | ASSEMBLING FOR USE WITH MICROSOFT VISUAL C++ AND BORLAND C++ |
||
266 | -------------------------------------------------------------------- |
||
267 | |||
268 | Visual C++ and Borland C++ use stack calling conventions by default. To |
||
269 | create a proper emulator for these compilers: |
||
270 | |||
271 | makez80 MZ80.asm -s -x86 |
||
272 | |||
273 | For Visual C++ or Borland C++: |
||
274 | |||
275 | nasm -f win32 MZ80.asm |
||
276 | |||
277 | Link with your standard Visual C++ or Borland C++. |
||
278 | |||
279 | |||
280 | ASSEMBLING FOR USE WITH DJGPP, GCC/FREEBSD, OR GCC/LINUX |
||
281 | -------------------------------------------------------------------- |
||
282 | |||
283 | DJGPP Uses stack calling conventions: |
||
284 | |||
285 | makez80 MZ80.asm -s -x86 |
||
286 | |||
287 | To assemble: |
||
288 | |||
289 | nasm -f coff MZ80.asm |
||
290 | |||
291 | Link with your standard DJGPP linker. The same holds true for GCC under |
||
292 | FreeBSD or Linux. If you're using GCC, use the -l option to generate "plain" |
||
293 | labels so that gcc's linker will properly link things. |
||
294 | |||
295 | |||
296 | MAKEZ80 COMMAND LINE OPTIONS |
||
297 | ---------------------------- |
||
298 | |||
299 | -s - Use stack calling conventions (DJGPP, MSVC, Borland, etc...) |
||
300 | |||
301 | -cs - Force all stack operations to go through the Read/Write memory handlers. |
||
302 | This slows things down, but is useful when needed. |
||
303 | |||
304 | -16 - Treat all I/O input and output as 16 bit (BC) |
||
305 | |||
306 | -l - Create 'plain' labels - ones without leading and trailing underscores |
||
307 | |||
308 | -nt - Do not generate timing code - this speeds the emulator up, but the |
||
309 | downside is that no timing info is available. |
||
310 | |||
311 | -c - Emit a C mz80 emulator (API Compatible with the assembly version - |
||
312 | handy for porters!) |
||
313 | |||
314 | -x86 - Emit an assembly (x86) mz80 emulator |
||
315 | |||
316 | -os2 - Generate OS/2 compatible segmentation |
||
317 | |||
318 | |||
319 | IMPORTANT NOTE ABOUT INTERRUPTS |
||
320 | ------------------------------- |
||
321 | |||
322 | A minor change was made between the 3.1 and 3.2 versions of makez80 in the |
||
323 | way that interrupts were handled. |
||
324 | |||
325 | On a real Z80, the !INT line is a level triggered interrupt, meaning that if |
||
326 | the interrupt line is held low, the Z80 will continue to take interrupts |
||
327 | immediately after the instruction after the EI instruction is executed until |
||
328 | the interrupt line is high again. |
||
329 | |||
330 | In 3.1, if an interrupt came in and interrupts were disabled, the interrupt |
||
331 | would never be "latched" for later execution. The Z80 does not have any |
||
332 | internal latching capabilities, however external hardware often does hold |
||
333 | the interrupt line low until the interrupt is executed, in effect, a latch. |
||
334 | |||
335 | I've only found one video game so far that requires the "raising/lowering" |
||
336 | of the interrupt line (Ataxx). In the games that I've tried, it has improved |
||
337 | performance, in some cases drastically, and in others not at all. This can |
||
338 | be accounted for by interrupts being taken now, where they were being dropped |
||
339 | in prior mz80 releases. |
||
340 | |||
341 | mz80 Emulates the most commonly used scenario. Now when mz80int() is executed |
||
342 | and a nonzero value is returned (indicating interrupts were disabled), it |
||
343 | will set z80intPending, and the interrupt will be taken after execution of |
||
344 | one instruction beyond the EI instruction. |
||
345 | |||
346 | So now, if mz80int() returns a nonzero value, that means an interrupt is |
||
347 | latched. If clearing this latch is desired or the old behavior of 3.1 is |
||
348 | desired, make a call to the mz80ClearPendingInterrupt() call. It's a 2 |
||
349 | instruction call that has extremely small overhead and will not affect |
||
350 | performance in any measurable way. |
||
351 | |||
352 | In any case, MZ80 will now execute one instruction after EI regardless of |
||
353 | how much time is available to avoid the possibility of an interrupt request |
||
354 | coming in directly after the EI instruction. |
||
355 | |||
356 | |||
357 | STEPS TO EMULATION |
||
358 | ------------------ |
||
359 | |||
360 | NOTE: -16 Is a command line option that will treat all I/O as 16 bit. That |
||
361 | is, in an instruction like "IN AL, (C)", the addressed passed to the I/O |
||
362 | handler will be BC instead of just C. Bear this in mind when considering your |
||
363 | emulated platform. |
||
364 | |||
365 | There are a few steps you want to go through to get proper emulation, and a |
||
366 | few guidelines must be followed. |
||
367 | |||
368 | 1) Create a MZ80CONTEXT |
||
369 | |||
370 | 2) Create your virtual 64K memory space using whatever means of obtaining |
||
371 | memory you need to do. |
||
372 | |||
373 | 3) Set mz80Base in your context to be the base of your 64K memory space |
||
374 | |||
375 | 4) Load up your image to be emulated within that 64K address space. |
||
376 | |||
377 | 5) Set z80IoRead and z80IoWrite to their appropriate structure arrays. Here's |
||
378 | an example: |
||
379 | |||
380 | struct z80PortRead ReadPorts[] = |
||
381 | { |
||
382 | {0x10, 0x1f, SoundChip1Read}, |
||
383 | {0x20, 0x2f, SoundChip2Read} |
||
384 | {(UINT32) -1, (UINT32) -1, NULL} |
||
385 | }; |
||
386 | |||
387 | When an IN instruction occurs, mz80 will probe this table looking for a |
||
388 | handler to the address of the "IN" instruction. If it is found in the list, |
||
389 | it's up to the handler to return the proper value. Otherwise, a value of |
||
390 | 0ffh is returned internally if no handler for that I/O address is found. In |
||
391 | the case above, SoundChip1Read is called when the I/O address is between 0x10- |
||
392 | 0x1f. A similar structure is used for I/O writes as well (OUT): |
||
393 | |||
394 | struct z80PortWrite WritePorts[] = |
||
395 | { |
||
396 | {0x20, 0x2f, SoundChip2Write}, |
||
397 | {0x30, 0x36, VideoCtrlWrite}, |
||
398 | {(UINT32) -1, (UINT32) -1, NULL} |
||
399 | } |
||
400 | |||
401 | Of course, this does the opposite that the z80PortRead struct, and instead |
||
402 | looks for a handler to hand some data to. If it doesn't find an appropriate |
||
403 | handler, nothing happens. |
||
404 | |||
405 | 6) Set mz80MemoryRead & mz80MemoryWrite to their appropriate structure |
||
406 | arrays. Here is an example: |
||
407 | |||
408 | struct MemoryWriteByte GameWrite[] = |
||
409 | { |
||
410 | {0x3000, 0x3fff, VideoWrite}, |
||
411 | {0x4000, 0x4fff, SpriteWrite}, |
||
412 | {(UINT32) -1, (UINT32) -1, NULL} |
||
413 | }; |
||
414 | |||
415 | The above example says that any time a write occurs in the 0x3000-0x3fff |
||
416 | range, call the VideoWrite routine. The same holds true for the SpriteWrite |
||
417 | region as well. |
||
418 | |||
419 | NOTE: When your write handler is called, it is passed the address of the |
||
420 | write and the data that is to be written to it. If your handler doesn't |
||
421 | write the data to the virtual image, the mz80 internal code will not. |
||
422 | |||
423 | NOTE: These routines will *NOT* be called when execution asks for these |
||
424 | addresses. It will only call them when a particular instruction uses the |
||
425 | memory at these locations. |
||
426 | |||
427 | If you wish for a region to be RAM, just leave it out of your memory region |
||
428 | exception list. The WriteMemoryByte routine will treat it as read/write |
||
429 | RAM and will write to mz80Base + addr directly. |
||
430 | |||
431 | If you wish to protect ROM regions (not often necessary), create a range that |
||
432 | encompasses the ROM image, and have it call a routine that does nothing. This |
||
433 | will prevent data from being written back onto the ROM image. |
||
434 | |||
435 | Leave your last entry in the table as shown above, with a null handler and |
||
436 | 0xffffffff-0xffffffff as your read address. Even though the Z80 only |
||
437 | addresses 64K of space, the read/write handlers are defined as 32 bit so |
||
438 | the compiler won't pass junk in the upper 16 bits of the address lines. Not |
||
439 | only that, it allows orthoganality for future CPU emulators that may use |
||
440 | these upper bits. |
||
441 | |||
442 | You can do a mz80GetContext() if you'd like to read the current context of |
||
443 | the registers. Note that by the time your handler gets called, the program |
||
444 | counter will be pointing to the *NEXT* instruction. |
||
445 | |||
446 | struct MemoryReadByte GameRead[] = |
||
447 | { |
||
448 | {0x2000, 0x200f, ReadHandler}, |
||
449 | {(UINT32) -1, (UINT32) -1, NULL} |
||
450 | }; |
||
451 | |||
452 | Same story here. If you have a special handler for an attempted read at a |
||
453 | particular address, place its range in this table and create a handler |
||
454 | routine for it. |
||
455 | |||
456 | If you don't define a handler for a particular region, then the ReadMemoryByte |
||
457 | in mz80.ASM will actually read the value out of mz80Base + the offset |
||
458 | required to complete the instruction. |
||
459 | |||
460 | 7) Set the intAddr and nmiAddr to the addresses where you want mz80 to start |
||
461 | executing when an interrupt or NMI happens. Take a look at the section |
||
462 | entitled "INTERRUPTS" below for more information on this. |
||
463 | |||
464 | 8) Call mz80SetContext() on your Z80 context |
||
465 | |||
466 | 9) Call mz80Reset(). This will prime the program counter and cause a virtual |
||
467 | CPU-wide reset. |
||
468 | |||
469 | 10) Once you have those defined, you're ready to begin emulation. There's some |
||
470 | sort of main loop that you'll want. Maybe something like: |
||
471 | |||
472 | while (hit == 0) |
||
473 | { |
||
474 | if (lastSec != (UINT32) time(0)) |
||
475 | { |
||
476 | diff = (mz80clockticks - prior) / 3000000; |
||
477 | printf("%ld Clockticks, %ld frames, %ld Times original speed\n", MZ80clockticks - prior, frames, diff); |
||
478 | frames = 0; |
||
479 | prior = mz80clockticks; |
||
480 | lastSec = time(0); |
||
481 | if (kbhit()) |
||
482 | { |
||
483 | getch(); |
||
484 | hit = 1; |
||
485 | } |
||
486 | } |
||
487 | |||
488 | /* 9000 Cycles per NMI (~3 milliseconds @ 3MHZ) */ |
||
489 | |||
490 | dwResult = mz80exec(9000); |
||
491 | mz80clockticks += mz80GetElapsedTicks(TRUE); |
||
492 | mz80nmi(); |
||
493 | |||
494 | /* If the result is not 0x80000000, it's an address where |
||
495 | an invalid instruction was hit. */ |
||
496 | |||
497 | if (0x80000000 != dwResult) |
||
498 | { |
||
499 | mz80GetContext(&sCpu1); |
||
500 | printf("Invalid instruction at %.2x\n", sCpu1.MZ80pc); |
||
501 | exit(1); |
||
502 | } |
||
503 | } |
||
504 | |||
505 | Call mz80exec() With the # of virtual CPU cycles you'd like mz80 to |
||
506 | execute. Be sure to use the mz80GetElapsedTicks() call *AFTER* execution to |
||
507 | see how many virtual CPU cycles it actually executed. For example, if you tell |
||
508 | mz80 to execute 500 virtual CPU cycles, it will execute slightly more. Anything |
||
509 | from 500 to 524 (24 cycles being the longest any 1 instruction takes in the |
||
510 | Z80). |
||
511 | |||
512 | Use the mz80GetElapsedTicks() call for more accurate cycle counting. Of course, |
||
513 | this is only if you have *NOT* included the -nt option. |
||
514 | |||
515 | If you pass FALSE to the mz80GetElapsedTicks() function, the internal CPU |
||
516 | elapsed tick clock will not be reset. The elapsed tick counter is something |
||
517 | that continues to increase every emulated instruction, and like an odometer, |
||
518 | will keep counting unless you pass TRUE to mz80GetElapsedTicks(), of which |
||
519 | case it will return you the current value of the elapsed ticks and set it to |
||
520 | |||
521 | |||
522 | NOTE: The bigger value you pass to mz80exec, the greater benefit you get out |
||
523 | of the virtual registers persisting within the emulator, and it will run |
||
524 | faster. Pass in a value that is large enough to take advantage of it, but |
||
525 | not so often that you can't handle nmi or int's properly. |
||
526 | |||
527 | If you wish to create a virtual NMI, call mz80nmi(), and it will be taken |
||
528 | the next time you call mz80exec, or alternately if you have a handler call |
||
529 | mz80nmi/mz80int(), the interrupt will be taken upon return. Note that |
||
530 | mz80nmi() doesn't actually execute any code - it only primes the emulator to |
||
531 | begin executing NMI/INT code. |
||
532 | |||
533 | NOTE: mz80int() is defined with a UINT32 as a formal parameter. Depending |
||
534 | upon what interrupt mode you're executing in (described later), it may or may |
||
535 | not take a value. |
||
536 | |||
537 | NMI's can interrupt interrupts, but not the other way around - just like a |
||
538 | real Z80. If your program is already in an interrupt, another one will not be |
||
539 | taken. The same holds true for an NMI - Just like a real Z80! |
||
540 | |||
541 | |||
542 | MUTLI-PROCESSOR NOTES |
||
543 | --------------------- |
||
544 | |||
545 | Doing multi processor support is a bit trickier, but is still fairly straight- |
||
546 | forward. |
||
547 | |||
548 | For each processor to be emulated, go through steps 1-7 above - giving each |
||
549 | CPU its own memory space, register storage, and read/write handlers. |
||
550 | |||
551 | |||
552 | EXECUTION OF MULTI-CPUS: |
||
553 | ------------------------- |
||
554 | |||
555 | When you're ready to execute a given CPU, do the following: |
||
556 | |||
557 | mz80SetContext(contextPointer); |
||
558 | |||
559 | This will load up all information saved before into the emulator and ready it |
||
560 | for execution. Then execute step 7 above to do your virtual NMI's, interrupts, |
||
561 | etc... All CPU state information is saved within a context. |
||
562 | |||
563 | When the execution cycle is complete, do the following to save the updated |
||
564 | context away for later: |
||
565 | |||
566 | mz80GetContext(contextPointer); |
||
567 | |||
568 | Give each virtual processor a slice of time to execute. Don't make the values |
||
569 | too small or it will spend its time swapping contexts. While this in itself |
||
570 | isn't particularly CPU expensive, the more time you spend executing the better. |
||
571 | mz80 Keeps all of the Z80 register in native x86 register (including most |
||
572 | of the flags, HL, BC, and A). If no context swap is needed, then you get the |
||
573 | added advantage of the register storage. For example, let's say you were |
||
574 | running two Z80s - one at 2.0MHZ and one at 3.0MHZ. An example like this |
||
575 | might be desirable: |
||
576 | |||
577 | mz80SetContext(cpu1Context); // Set CPU #1's information |
||
578 | mz80exec(2000); // 2000 Instructions for 2.0MHZ CPU |
||
579 | mz80GetContext(cpu1Context); // Get CPU #1's state info |
||
580 | |||
581 | mz80SetContext(cpu2Context); // Set CPU #2's state information |
||
582 | mz80exec(3000); // 3000 Instructions for 3.0MHZ CPU |
||
583 | mz80GetContext(cpu2Context); // Get CPU #2's state information |
||
584 | |||
585 | This isn't entirely realistic, but if you keep the instruction or timing |
||
586 | ratios between the emulated CPUs even, then timing is a bit more accurate. |
||
587 | |||
588 | NOTE: If you need to make a particular CPU give up its own time cycle because |
||
589 | of a memory read/write, simply trap a particular address (say, a write to a |
||
590 | slave processor) and call mz80ReleaseTimeslice(). It will not execute any |
||
591 | further instructions, and will give up its timeslice. Put this in your |
||
592 | read/write memory trap. |
||
593 | |||
594 | NOTE: You are responsible for "holding back" the processor emulator from |
||
595 | running too fast. |
||
596 | |||
597 | |||
598 | INTERRUPTS |
||
599 | ---------- |
||
600 | |||
601 | The Z80 has three interrupt modes: IM 0 - IM 2. Each act differently. Here's |
||
602 | a description of each: |
||
603 | |||
604 | IM 0 |
||
605 | |||
606 | This mode will cause the Z80 to be able to pull a "single byte instruction" |
||
607 | off the bus when an interrupt occurs. Since we're not doing bus cycle |
||
608 | emulation, it acts identically to mode 1 (described below). The formal |
||
609 | parameter to mz80int() is ignored. There is really no point in actually |
||
610 | emulating the instruction execution since any instruction that would be |
||
611 | executed would be a branch instruction! |
||
612 | |||
613 | IM 1 |
||
614 | |||
615 | This mode is the "default" mode that the Z80 (and mz80 for that matter) comes |
||
616 | up in. When you call mz80reset(), the interrupt address is set to 38h and |
||
617 | the NMI address is set to 66h. So when you're in IM 1 and mz80int() is |
||
618 | called, the formal parameter is ignored and the z80intAddr/z80nmiAddr values |
||
619 | are appropriately loaded into the program counter. |
||
620 | |||
621 | IM 2 |
||
622 | |||
623 | This mode causes the Z80 to read the upper 8 bits from the current value |
||
624 | of the "I" register, and the lower 8 bits from the value passed into mz80int(). |
||
625 | So, if I contained 35h, and you did an mz80int(0x64), then an interrupt at |
||
626 | address 3564h would be taken. Simple! |
||
627 | |||
628 | |||
629 | OTHER GOODIES |
||
630 | ------------- |
||
631 | |||
632 | MZ80 Has a nice feature for allowing the same handler to handle different |
||
633 | data regions on a single handler. Here's an example: |
||
634 | |||
635 | struct PokeyDataStruct Pokey1; |
||
636 | struct PokeyDataStruct Pokey2; |
||
637 | |||
638 | struct MemoryWriteByte GameWrite[] = |
||
639 | { |
||
640 | {0x1000, 0x100f, PokeyHandler, Pokey1}, |
||
641 | {0x1010, 0x101f, PokeyHandler, Pokey2}, |
||
642 | {(UINT32) -1, (UINT32) -1, NULL} |
||
643 | }; |
||
644 | |||
645 | void PokeyHandler(UINT32 dwAddr, UINT8 bData, struct sMemoryWriteByte *psMem) |
||
646 | { |
||
647 | struct PokeyDataStruct *psPokey = psMem->pUserArea; |
||
648 | |||
649 | // Do stuff with psPokey here.... |
||
650 | } |
||
651 | |||
652 | This passes in the pointer to the sMemoryWriteByte structure that caused |
||
653 | the handler to be called. The pUserArea is a user defined address that can |
||
654 | be anything. It is not necessary to fill it in with anything or even |
||
655 | initialize it if the handler doesn't actually use it. |
||
656 | |||
657 | This allows a single handler to handle multiple data references. This is |
||
658 | particularly useful when handling sound chip emulation, where there might |
||
659 | be more than one of a given device. Sure beats having multiple unique |
||
660 | handlers that are identical with the exception of the data area where it |
||
661 | writes! This allows a good deal of flexibility. |
||
662 | |||
663 | The same construct holds for MemoryReadByte, z80PortRead, and z80PortWrite, |
||
664 | so all can take advantage of this feature. |
||
665 | |||
666 | |||
667 | SHARED MEMORY FEATURES |
||
668 | ---------------------- |
||
669 | |||
670 | MZ80 Also has another useful feature for dealing with shared memory regions: |
||
671 | |||
672 | UINT8 bSharedRAM[0x100]; |
||
673 | |||
674 | struct MemoryWriteByte Processor1[] = |
||
675 | { |
||
676 | {0x1000, 0x10ff, NULL, bSharedRAM}, |
||
677 | {(UINT32) -1, (UINT32) -1, NULL} |
||
678 | }; |
||
679 | |||
680 | struct MemoryWriteByte Processor2[] = |
||
681 | { |
||
682 | {0x1000, 0x10ff, NULL, bSharedRAM}, |
||
683 | {(UINT32) -1, (UINT32) -1, NULL} |
||
684 | }; |
||
685 | |||
686 | If the handler address is NULL, mz80 will look at the pUserArea field as a |
||
687 | pointer to RAM to read from/write to. This comes in extremely handy when you |
||
688 | have an emulation that requires two or more processors writing to the same |
||
689 | memory block. And it's lots faster than creating a handler that writes to |
||
690 | a common area as well. |
||
691 | |||
692 | |||
693 | DEBUGGING |
||
694 | --------- |
||
695 | |||
696 | Several new functions have been added to mz80 that assist the emulator |
||
697 | author by providing a standard set of functions for register access: |
||
698 | |||
699 | UINT8 mz80SetRegisterValue(void *pContext, UINT32 dwRegister, UINT32 dwValue) |
||
700 | |||
701 | This allows setting of any register within the Z80. The register field can be |
||
702 | one of the following values (defined in mz80.h): |
||
703 | |||
704 | CPUREG_PC |
||
705 | CPUREG_Z80_AF |
||
706 | CPUREG_Z80_BC |
||
707 | CPUREG_Z80_DE |
||
708 | CPUREG_Z80_HL |
||
709 | CPUREG_Z80_AFPRIME |
||
710 | CPUREG_Z80_BCPRIME |
||
711 | CPUREG_Z80_DEPRIME |
||
712 | CPUREG_Z80_HLPRIME |
||
713 | CPUREG_Z80_IX |
||
714 | CPUREG_Z80_IY |
||
715 | CPUREG_Z80_SP |
||
716 | CPUREG_Z80_I |
||
717 | CPUREG_Z80_R |
||
718 | CPUREG_Z80_A |
||
719 | CPUREG_Z80_B |
||
720 | CPUREG_Z80_C |
||
721 | CPUREG_Z80_D |
||
722 | CPUREG_Z80_E |
||
723 | CPUREG_Z80_H |
||
724 | CPUREG_Z80_L |
||
725 | CPUREG_Z80_F |
||
726 | CPUREG_Z80_CARRY |
||
727 | CPUREG_Z80_NEGATIVE |
||
728 | CPUREG_Z80_PARITY |
||
729 | CPUREG_Z80_OVERFLOW |
||
730 | CPUREG_Z80_HALFCARRY |
||
731 | CPUREG_Z80_ZERO |
||
732 | CPUREG_Z80_SIGN |
||
733 | CPUREG_Z80_IFF1 |
||
734 | CPUREG_Z80_IFF2 |
||
735 | |||
736 | Each individual register's value can be set, including the flags at the end. |
||
737 | The only valid values for the flags are 1 and 0. Setting these will |
||
738 | automatically adjust the "F" register. |
||
739 | |||
740 | If pContext is NULL, then the registers in the currently active context are |
||
741 | changed. If pContext points to a non-NULL area, that area is assumed to be |
||
742 | a CONTEXTMZ80 structure where the new register value will be written. |
||
743 | |||
744 | If mz80SetRegisterValue() returns a nonzero value, either the register value |
||
745 | or register is out of range or invalid. |
||
746 | |||
747 | |||
748 | UINT32 mz80GetRegisterValue(void *pContext, UINT32 dwRegister) |
||
749 | |||
750 | This returns the value of the register given on input (listed above as |
||
751 | CPUREG_Z80_xxxxx). Flag values will be 1 or 0. |
||
752 | |||
753 | If pContext is NULL, then the registers in the currently active context are |
||
754 | read. If pContext points to a non-NULL area, that area is assumed to be |
||
755 | a CONTEXTMZ80 structure from which register values are pulled. |
||
756 | |||
757 | |||
758 | UINT32 mz80GetRegisterTextValue(void *pContext, UINT32 dwRegister, |
||
759 | UINT8 *pbTextArea) |
||
760 | |||
761 | This returns the textual representation of the value of a given register. |
||
762 | It is a text printable string that can be used in sprintf() statements and |
||
763 | the like. This function is useful because different representations for |
||
764 | registers (like flags) can be a group of 8 flag bytes instead of a single |
||
765 | value. |
||
766 | |||
767 | On entry, pContext being set to NULL indicates that mz80 should get the |
||
768 | register value from the currently active context. Otherwise, it is assumed |
||
769 | to be pointing to a CONTEXTMZ80 structure, which contains the value of the |
||
770 | registers to be read. |
||
771 | |||
772 | pbTextArea points to a buffer where the value text can be written. This points |
||
773 | to a user supplied buffer. |
||
774 | |||
775 | On exit, if any nonzero value is encountered, either the register # is out |
||
776 | of range or pbTextArea is NULL. |
||
777 | |||
778 | |||
779 | UINT8 *mz80GetRegisterName(UINT32 dwRegister) |
||
780 | |||
781 | This returns a pointer to the textual name of the register passed in. NULL |
||
782 | Is returned if the register index (CPUREG_Z80_xxxx table described above) is |
||
783 | out of range. DO NOT MODIFY THE TEXT! It is static data. |
||
784 | |||
785 | |||
786 | FINAL NOTES |
||
787 | ----------- |
||
788 | |||
789 | I have debugged MZ80.ASM to the best of my abilities. There might still be |
||
790 | a few bugs floating around in it, but I'm not aware of any. I've validated |
||
791 | all instructions (That I could) against a custom built Z80 on an ISA card |
||
792 | (that fits in a PC) so I'm quite confident that it works just like a real |
||
793 | Z80. |
||
794 | |||
795 | If you see any problems, please point them out to me, as I am eager to make |
||
796 | mz80 the best emulator that I can. |
||
797 | |||
798 | If you have questions, comments, etc... about mz80, please don't hesitate |
||
799 | to send me an email. And if you use mz80 in your emulator, I'd love to take |
||
800 | a look at your work. If you have special needs, or need implementation |
||
801 | specific hints, feel free to email me, Neil Bradley (neil@synthcom.com). I |
||
802 | will do my best to help you. |
||
803 | |||
804 | Enjoy! |
||
805 | |||
806 | Neil Bradley |
||
807 | neil@synthcom.com |
||
808 |