Author |
Message |
- Joined: 14 Oct 2006
- Posts: 256
- Location: NYC
|
Any other Z80 I/O Mirrors?
Posted: Thu Nov 02, 2006 5:02 am
|
In Charles MacDonald's smstech-20021112.txt he states that inputs are mirrored at $C0 and $C1. The VDP control is also mirrored at $BD. I was wondering if there are any other significant Z80 I/O mirrors?
|
|
|
- Joined: 18 Sep 1999
- Posts: 498
- Location: Portland, Oregon USA
|
Posted: Thu Nov 02, 2006 7:12 am
|
Yes, there are other mirrors. Here's how I/O ports actually work for the SMS and SMS2 (GG is slightly different):
Only bits 0, 6, 7 of the I/O port number is decoded in the SMS. This means that all even number ports between $00 and $3E are the same. Likewise for $40 through $7E, $80 through $BE, and $C0 through $FE.
Similarly, all odd-numbered ports between $01 and $3F are the same, as are $41 through $7F, $81 through $BF, and $C1 through $FF.
--
Eric R. Quinn
|
|
|
- Joined: 25 Dec 2005
- Posts: 607
- Location: São Paulo - Brazil
|
Posted: Thu Nov 02, 2006 3:31 pm
|
I was wondering, about mirrors:
(Suppose A and A')
1. If you write A', is the new value mirrored at A ?
2. Is the write and mirror operation atomic ?
I thought at first (if 1. doesn't apply) that mirrors help solve classic exchange problems, like:
(Suppose A,B,A',B' and I want to exchange A and B)
B' <-- A
A <-- B
B <-- B'
|
|
|
- Site Admin
- Joined: 19 Oct 1999
- Posts: 14770
- Location: London
|
Posted: Thu Nov 02, 2006 4:17 pm
|
The "mirrors" are a physical thing. Think of the bits in the address. If the chip only looks at bits 0, 6 and 7 then %11000001 and %11001001 look the same because all it sees is %11-----1 in both cases.
Thus, reading or writing to/from any mirrored address is equivalent no matter which address you use for either part.
A few things to note:
1. On the SMS, most I/O ports are not simple read/write - generally you don't read back what you wrote, regardless of mirrors.
2. A notable use of mirroring in memory is that writing to paging registers (eg. $ffff) is simultaneously writing to a mirror of RAM - $ffff is a mirror of $dfff. However, the paging hardware only responds to writes to $ffff. So, you can write to $ffff and read back from $ffff or $dfff. But writing to $dfff won't affect paging. Confusing, huh? The same goes for "reading back" the 3D glasses "register".
3. Mirroring is also why overdumps generally have the same data repeated multiple times.
Also, it's not an "operation" - it's no more or less atomic than using non-mirrored addresses. Non-standard I/O address usage just hurts emulator compatibility, which is why an emulator ought to handle it, which is simple enough - address &= 0x3e will set all the unused bits. Most of the standard addresses (3e/3f/7e/7f/be/bf) have those middle 5 bits set, the others (dc/dd) can be changed in the source (to fe/ff), and the GG I/O ports are a special case anyway.
|
|
|
- Joined: 25 Dec 2005
- Posts: 607
- Location: São Paulo - Brazil
|
Posted: Thu Nov 02, 2006 5:43 pm
|
Thanks for the info Maxim.
I didn't get confused about the paging info =)
I used the word operation 'cause I thought of 2 sequential/concurrent actions, like:
[Entering critical region]
Write to the address
Copy to mirror
[Leaving critical region]
If I understood what you said, you just write to an address and take for granted the mirroring right (next opcode can just read the mirror) ?
So, can't we say that, inside the chip, the above procedure is atomic ?
|
|
|
- Site Admin
- Joined: 08 Jul 2001
- Posts: 8675
- Location: Paris, France
|
Posted: Thu Nov 02, 2006 6:24 pm
|
Niloctronic, the key to understand is Maxim's first sentence:
Quote The "mirrors" are a physical thing. Think of the bits in the address. If the chip only looks at bits 0, 6 and 7 then %11000001 and %11001001 look the same because all it sees is %11-----1 in both cases.
There's no such thing as 'copy to mirror'. Qualifying the operation as 'atomic' doesn't apply since there's only one reading/writing done. It's just some address bits that get maskedout because they are physically unconnected.
|
|
|
- Joined: 25 Dec 2005
- Posts: 607
- Location: São Paulo - Brazil
|
Posted: Thu Nov 02, 2006 7:58 pm
|
Bock wrote There's no such thing as 'copy to mirror'. Qualifying the operation as 'atomic' doesn't apply since there's only one reading/writing done. It's just some address bits that get maskedout because they are physically unconnected.
Ok guys, summing up: the chip can manipulate the address line to write once in more than one byte of RAM, right ? Well, enough of theory heh.
Tks again.
|
|
|
- Site Admin
- Joined: 19 Oct 1999
- Posts: 14770
- Location: London
|
Posted: Thu Nov 02, 2006 10:00 pm
|
Niloctronic wrote Ok guys, summing up: the chip can manipulate the address line to write once in more than one byte of RAM, right ?
Wrong :)
Let's say I have three switches in front of me that control lightbulbs. That gives me a total of 8 different light combinations.
The light bulbs are in a different room, where my chef is waiting to see what kind of food to cook me. (Hey, why not.) However, what I don't know is, he is only looking at bulbs 2 and 3; he doesn't care about bulb 1.
So, if I want dish 011, I press switches 2 and 2, but not 1. He sees 2 lights and cooks me a dish appropriately.
Later, I decide to try dish 111, so I press all 3 switches. He still sees 2 lights and gives me the same dish.
From his point of view, there are 2 bulbs and 4 possible dishes. From my point of view, there are 3 switches, and 8 possible combinations, but the first 4 are mirrors of the second 4.
000 Dish A
001 Dish B
010 Dish C
011 Dish D
100 Dish A
101 Dish B
110 Dish C
111 Dish D
One of the bits is simply not connected and as a result, mirroring appears. Now consider if my switches are the CPU's address bus and the dishes are bytes of RAM (hey, my example is getting strained now); writing to 000 will work the same as writing to 100, and likewise for reading, no matter the order; there's still only 4 bytes of RAM in total, they just all have 2 effective addresses where they can be written.
|
|
|
- Joined: 14 Aug 2000
- Posts: 748
- Location: Adelaide, Australia
|
shell game
Posted: Fri Nov 03, 2006 1:37 am
|
I'll take what's behind Dish D!
Can I buy a vowel too?
For clarity, I've listed all the port numbers that can be used by a games/software programmer for the corresponding tasks in the SMS (generated with xls) below. The ports in each list will have the same effect when used.
Slot Enable Port (officially 0x3E):
0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E
Joypad Port Control Port (officially 0x3F):
0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x31, 0x33, 0x35, 0x37, 0x39, 0x3B, 0x3D, 0x3F
VDP / PSG Port (officially 0x7E):
0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5A, 0x5C, 0x5E, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6E, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E
VDP / PSG Port (officially 0-x7F):
0x41, 0x43, 0x45, 0x47, 0x49, 0x4B, 0x4D, 0x4F, 0x51, 0x53, 0x55, 0x57, 0x59, 0x5B, 0x5D, 0x5F, 0x61, 0x63, 0x65, 0x67, 0x69, 0x6B, 0x6D, 0x6F, 0x71, 0x73, 0x75, 0x77, 0x79, 0x7B, 0x7D, 0x7F
VDP Data Port (Officially 0xBE):
0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8E, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA4, 0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC, 0xBE
VDP Command Port (officially 0xBF)
0x81, 0x83, 0x85, 0x87, 0x89, 0x8B, 0x8D, 0x8F, 0x91, 0x93, 0x95, 0x97, 0x99, 0x9B, 0x9D, 0x9F, 0xA1, 0xA3, 0xA5, 0xA7, 0xA9, 0xAB, 0xAD, 0xAF, 0xB1, 0xB3, 0xB5, 0xB7, 0xB9, 0xBB, 0xBD, 0xBF
Joypad 1 Port Status Port (officially 0xDC)
0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE, 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, 0xF0, 0xF2, 0xF4, 0xF6, 0xF8, 0xFA, 0xFC, 0xFE
Joypad 2 Port Status Port (officially 0xDD):
0xC1, 0xC3, 0xC5, 0xC7, 0xC9, 0xCB, 0xCD, 0xCF, 0xD1, 0xD3, 0xD5, 0xD7, 0xD9, 0xDB, 0xDD, 0xDF, 0xE1, 0xE3, 0xE5, 0xE7, 0xE9, 0xEB, 0xED, 0xEF, 0xF1, 0xF3, 0xF5, 0xF7, 0xF9, 0xFB, 0xFD, 0xFF
asynchronous.
|
|
|
- Joined: 14 Oct 2006
- Posts: 256
- Location: NYC
|
Posted: Fri Nov 03, 2006 4:44 am
|
Thanks for the information, guys!
|
|
|
- Site Admin
- Joined: 19 Oct 1999
- Posts: 14770
- Location: London
|
Posted: Fri Nov 03, 2006 7:18 am
|
Wow, that seems like an inefficient way to do the logic :)
if (GG_mode && port_number<=6)
{
...
}
else switch (port_number & 0x3e)
{
case 0x3e & 0x3e: port_3e(data); break;
case 0x3f & 0x3e: port_3f(data); break;
case 0x7e & 0x3e: port_7e(data); break;
case 0x7f & 0x3e: port_7f(data); break;
case 0xbe & 0x3e: port_be(data); break;
case 0xbf & 0x3e: port_bf(data); break;
case 0xdc & 0x3e: port_dc(data); break;
case 0xdd & 0x3e: port_dd(data); break;
} ought to cover everything. (Most of the "& 0x3e"s are unnecessarym but I added them for elegance, and they'll be calculated at compile-time anyway. Maybe not in VB, though...)
|
|
|
- Joined: 18 Sep 1999
- Posts: 498
- Location: Portland, Oregon USA
|
Posted: Fri Nov 03, 2006 6:40 pm
|
Oops. You made a small mistake Maxim. The code should read:
if (GG_mode && port_number<=6)
{
...
}
else switch (port_number | 0x3e)
{
case 0x3e | 0x3e: port_3e(data); break;
case 0x3f | 0x3e: port_3f(data); break;
case 0x7e | 0x3e: port_7e(data); break;
case 0x7f | 0x3e: port_7f(data); break;
case 0xbe | 0x3e: port_be(data); break;
case 0xbf | 0x3e: port_bf(data); break;
case 0xdc | 0x3e: port_fe(data); break;
case 0xdd | 0x3e: port_ff(data); break;
}
-OR-
if (GG_mode && port_number<=6)
{
...
}
else switch (port_number & 0xc1)
{
case 0x3e & 0xc1: port_00(data); break;
case 0x3f & 0xc1: port_01(data); break;
case 0x7e & 0xc1: port_40(data); break;
case 0x7f & 0xc1: port_41(data); break;
case 0xbe & 0xc1: port_80(data); break;
case 0xbf & 0xc1: port_81(data); break;
case 0xdc & 0xc1: port_c0(data); break;
case 0xdd & 0xc1: port_c1(data); break;
}
You were masking the wrong bits with the AND operation. (As evidenced by the fact that 0x3e & 0x3e == 0x7e & 0x3e == 0xbe & 0x3e == 0xdc &0x3e == 0x3e, and 0x3f & 0x3e == 0x7f & 0x3e == 0xbf & 0x3e == 0x3e, and 0xdd &0x3e == 0x1c)
For what it's worth MesaDX uses the top style.
--
Eric R. Quinn
|
|
|
- Joined: 28 Sep 1999
- Posts: 1198
|
Posted: Fri Nov 03, 2006 7:32 pm
|
Eric R. Quinn wrote Oops. You made a small mistake Maxim. The code should read:
I see what you mean, but isn't this simple enough?
switch(port & 0xC1)
{
case 0x00:
case 0x01:
case 0x40:
case 0x41:
case 0x80:
case 0x81:
case 0xC0:
case 0xC1:
}
I've never seen the syntax you guys are using in the case statements before. :P
|
|
|
- Site Admin
- Joined: 19 Oct 1999
- Posts: 14770
- Location: London
|
Posted: Fri Nov 03, 2006 9:56 pm
|
Grr. I ought to have thought a bit more thoroughly.
Charles: I was aiming to have the "official" port numbers line up on the left of each case handler; however, the last 2 ($dc and $dd) are the only two "official" values that aren't already ORe with $3e. So I could write $fe and $ff there (potentially confusing), or do the ORing explicitly (since the calculation is done at compile time). From there, it's one more step to ad the ORing to all of them, just for "elegance" (and having everything lining up vertically).
|
|
|
- Site Admin
- Joined: 08 Jul 2001
- Posts: 8675
- Location: Paris, France
|
Posted: Sat Nov 04, 2006 12:34 am
|
As a matter of style, I would use "official" port values (as Maxim mentionned) along with ~0x3E (==0xC1 but readable).
Not how SG-1000/SC-3000/SF-7000 also use other ports. For simplicity of coding, one could use a table of 256 function pointers and initialize depending on the emulated system (Not so cache friendly).
|
|
|
- Joined: 08 Dec 2005
- Posts: 488
- Location: Melbourne, Australia
|
Posted: Sun Nov 05, 2006 2:15 pm
|
Charles MacDonald wrote I see what you mean, but isn't this simple enough?
switch(port & 0xC1)
That's what I have been using. If the SMS only looks at address lines 7, 6 and 1, it makes sense to me for an emulator to do exactly that, and not worry about "official" port addresses.
Eric R. Quinn wrote Here's how I/O ports actually work for the SMS and SMS2 (GG is slightly different)
How is the GG different? I am aware of bits 7 and 6 of port 0x00 being used for the start button and region detection, but I also read that the GG uses ports 0x01-0x06 for different reasons than the SMS. Does anyone know what these other ports are used for? Are these GG-specific ports mirrored anywhere?
|
|
|
- Joined: 14 Oct 2006
- Posts: 256
- Location: NYC
|
Posted: Sun Nov 05, 2006 9:06 pm
|
Public Function inn(port As Long) As Long 'Read from a Z80 Port.
If port& = &H0& And IsGameGear Then 'game gear controller (for start button).
Call JoystickInput
If MovieRecording Then Put #3, , ggcontroller
If MoviePlaying Then Get #3, , ggcontroller: Call MoviePlayEnd
inn = ggcontroller
Exit Function
End If
Select Case port&
'Vertical Port.
Case &H40&, &H42&, &H44&, &H46&, &H48&, &H4A&, &H4C&, &H4E&, &H50&, &H52&, &H54&, &H56&, &H58&, &H5A&, &H5C&, &H5E&, &H60&, &H62&, &H64&, &H66&, &H68&, &H6A&, &H6C&, &H6E&, &H70&, &H72&, &H74&, &H76&, &H78&, &H7A&, &H7C&, &H7E&
inn = vdp.getVCount
'controller 1 and some of controller 2.
Case &HC0&, &HC2&, &HC4&, &HC6&, &HC8&, &HCA&, &HCC&, &HCE&, &HD0&, &HD2&, &HD4&, &HD6&, &HD8&, &HDA&, &HDC&, &HDE&, &HE0&, &HE2&, &HE4&, &HE6&, &HE8&, &HEA&, &HEC&, &HEE&, &HF0&, &HF2&, &HF4&, &HF6&, &HF8&, &HFA&, &HFC&, &HFE&
Call JoystickInput
If MovieRecording Then Put #3, , controller1
If MoviePlaying Then Get #3, , controller1: Call MoviePlayEnd
inn = controller1
'rest of controller 2.
Case &HC1&, &HC3&, &HC5&, &HC7&, &HC9&, &HCB&, &HCD&, &HCF&, &HD1&, &HD3&, &HD5&, &HD7&, &HD9&, &HDB&, &HDD&, &HDF&, &HE1&, &HE3&, &HE5&, &HE7&, &HE9&, &HEB&, &HED&, &HEF&, &HF1&, &HF3&, &HF5&, &HF7&, &HF9&, &HFB&, &HFD&, &HFF&
Call JoystickInput
If MovieRecording Then Put #3, , controller2
If MoviePlaying Then Get #3, , controller2: Call MoviePlayEnd
inn = controller2
'VDP Data Port.
Case &H80&, &H82&, &H84&, &H86&, &H88&, &H8A&, &H8C&, &H8E&, &H90&, &H92&, &H94&, &H96&, &H98&, &H9A&, &H9C&, &H9E&, &HA0&, &HA2&, &HA4&, &HA6&, &HA8&, &HAA&, &HAC&, &HAE&, &HB0&, &HB2&, &HB4&, &HB6&, &HB8&, &HBA&, &HBC&, &HBE&
inn = vdp.dataread
'VDP Control Port.
Case &H81&, &H83&, &H85&, &H87&, &H89&, &H8B&, &H8D&, &H8F&, &H91&, &H93&, &H95&, &H97&, &H99&, &H9B&, &H9D&, &H9F&, &HA1&, &HA3&, &HA5&, &HA7&, &HA9&, &HAB&, &HAD&, &HAF&, &HB1&, &HB3&, &HB5&, &HB7&, &HB9&, &HBB&, &HBD&, &HBF&
inn = vdp.controlread
Case Else
inn = 255&
End Select
Exit Function
End Function
Yes, this is where you cringe.
|
|
|
- Site Admin
- Joined: 19 Oct 1999
- Posts: 14770
- Location: London
|
Posted: Sun Nov 05, 2006 10:08 pm
|
You'll need some better handling of the GG ports (01-05?), for some GG games to run. It may be that this explicit mega-case is faster - you should see what difference it makes if you do the bitwise operations to reduce the jump table to less entries. Or does VB not have bitwise operations?
|
|
|
- Joined: 14 Oct 2006
- Posts: 256
- Location: NYC
|
Posted: Sun Nov 05, 2006 10:42 pm
|
You use And's and Or's for bitwise operations, the whole thing is a mess (Visual Basic). I haven't bothered with *any* Game Gear stuff yet, only the Start button. I suppose I could add it back into the Case now, I don't remember why I took it out.
|
|
|