Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Z80: having _wait() before _mwrite(addr, data) looks incorrect. #76

Open
hlide opened this issue Mar 14, 2023 · 3 comments
Open

Z80: having _wait() before _mwrite(addr, data) looks incorrect. #76

hlide opened this issue Mar 14, 2023 · 3 comments

Comments

@hlide
Copy link

hlide commented Mar 14, 2023

add(f'_wait();_mwrite({addr},{data});{action}')

This line looks incorrect for me.

Let me take an example. SHARP MZ-700 has an LSI which handles PAL/NTSC video circuit and also decodes address bus for dispatching between DRAM, ROM, peripherals and VRAM. If CPU is accessing the video RAM ($D000-$DFFF) and there is no horizontal blank currently then /WAIT is set to 0 to pause the CPU and avoid a VRAM access conflict. So, that /WAIT is set to 0 because LSI decodes a VRAM address present on the address bus (so a set_ab_x(addr, Z80_MREQ|Z80_WR) ; should be done before a call to _wait()).

That means, the emulated LSI chip would need to check first that address setting to set Z80_WAIT upon the first tick of MWR T1 and retrieves the data on the next tick (MWR T2) just after passing the _wait() call. So just setting Z80_MREQ|Z80_WR at the second tick is wrong and should be done in two parts: first setting the address bus with Z80_MREQ|Z80_WR|Z80_AS (so the system can set the Z80_WAIT if needed) then Z80_MREQ|Z80_WR|Z80_DS (so CPU can retrieve the final data given by the system when clears Z80_WAIT)

elif mcycle.type == 'mwrite':
l(f'// -- mwrite')
addr = mcycle.items['ab']
data = mcycle.items['db']
add(f'_set_ab_x({addr},Z80_MREQ|Z80_WR|Z80_AS)')
add(f'_wait();_set_db_x({data},Z80_MREQ|Z80_WR|Z80_DS);{action}')
add('')

Maybe there is a better solution than having Z80_AS and Z80_DS but I'm pretty sure Z80_WAIT is not well handled currently.

Same issue with Z80_IORQ|Z80_WR. :)

Still with SHARP MZ-700, the access to Monitor ROM ($0000-$0FFF) wants a one-wait state, so you also have the same issue here as well.

@hlide
Copy link
Author

hlide commented Mar 14, 2023

One possibility is to transform:

  • Z80_MREQ and Z80_RD into one flag Z80_MRD
  • Z80_MREQ and Z80_WR into one flag Z80_MWR
  • Z80_IOREQ and Z80_RD into one flag Z80_IORD
  • Z80_IOREQ and Z80_WR into one flag Z80_IOWR

I think that something modern Z80s have done.

first setting address before calling _wait() through Z80_MREQ or Z80_IOREQ and reading/writing data bus though one of the new flags will be set after the call to _wait.

If the system is not setting /WAIT to 0, pins = system_tick(pins) can simply ignored Z80_MREQ and Z80_IOREQ and only handle Z80_MRD, Z80_MWR, Z80_IORD and Z80_IOWR the same way it formerly handled Z80_MREQ | Z80_RD/WR and Z80_IOREQ | Z80_RD/WR.

Obviously, we may also have Z80_M1 | Z80_MREQ | Z80_RD which may be turned into Z80_M1 | Z80_MRD so it is up to the system to handle Z80_M1 | Z80_MREQ case or not.

@floooh
Copy link
Owner

floooh commented Mar 14, 2023

Yeah, I'm painfully aware of the issue :D

I wrote about that here https://floooh.github.io/2021/12/17/cycle-stepped-z80.html#pin-timing-differences-to-a-real-z80

A 'proper' emulation would simply keep the MREQ and IOREQ pins active over multiple cycles, but the current solution is a compromise to simplify the 'system tick function' by preventing that the memory or io accesses are done multiple times.

The current configuration is what worked best for the CPC emulation, but I'm aware that this may then break other emulators.

I didn't really think about a solution so far except that I wanted to tinker a bot more with generating different versions of z80.h (e.g. an alternative 'slow but correct' version where the memory and io request pins behave like on a real Z80).

I haven't thought about adding new 'virtual' pins yet, it's definitely an interesting idea.

Only problem is that I currently need to focus on other things :)

@hlide
Copy link
Author

hlide commented Mar 14, 2023

Just for your information, I built a MCLZ8 (https://github.com/MicroCoreLabs/Projects/tree/master/MCLZ8) but I stumbled about some obstacles which made the MCLZ8 firmware a bad Z80 emulator (especially if you want to be fully compatible). So I was considering your z80 chip emulator which I find quite excellent (not speaking about your documentation). So I ported it into MCLZ8. While it compiles, I found out it is pretty hard to adapt it without some big changes in the generated file.

The reason is that I'm trying to do the opposite of what this project was made for: I just want to emulate a Z80, not the whole system. So I can't ignore the hardware signals that I have to maintain on several T-states (tick). And I realized that the control pins were not at all in phase with the need. Indeed, I need to do things by clock phase (half tick) and not by clock cycle.

So I'm going the third way now (in a similar fashion as your archived rz80 but in C++ using template X, Y and Z argument so each opcode table entry will contain a call to an optimized function which is one template function using X, Y and Z to do the operation by opcode). Of course, I would have liked to achieve something with your chips version but I get more headaches than solutions, especially with this notion of overlapped.

@hlide hlide changed the title Z80: having _wait() before of _mwrite(addr, data) looks incorrect. Z80: having _wait() before _mwrite(addr, data) looks incorrect. Mar 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants