7

I am writing an emulator for the Jupiter ACE. I assumed the idle bus value would be 0xFF like the ZX Spectrum. Other emulators for the Jupiter ACE seem to assume the same. If there are no peripherals placing a value on the data bus what value should be simulated?

The reason I am asking is because I have come across a game for the Jupiter ACE, Valkyr, which is unusual in that it uses the Interrupt Mode 2. It places 0x3D in the I register but combined with a data bus value of 0xFF it fails, i.e. 0x3DFF seems to be the wrong vector and the simulated computer crashes back to the command prompt.

3
  • 4
    Are you certain the value is 0xFF always? Read this article about floating bus behaviour ramsoft.bbk.org.omegahg.com/floatingbus.html Commented Jul 8, 2021 at 13:09
  • There is a circuit diagram in TCJ issue 70. However with the way the video is done and all the resistors being used to avoid muxes between the video and the CPU when refreshing I'm at a loss to work out the value even with it. The ZX spectrum one is not always 0xFF either by the way. That's why games usually point I at 0x39xx as it's 257 bytes of 0xFF, then write 0x18 to 0xFFFF to get a JR backwards to FFF4. Commented Jul 8, 2021 at 17:34
  • 3
    Had a look in the ZesarUX source code for Jupiter ACE - That seems to assume $FF as well for the bus value in IM2. So, at least you're not alone ;) Commented Jul 9, 2021 at 8:02

4 Answers 4

8

Thanks @kpalser - we simply followed your instructions and ran the code experiments to demonstrate that on multiple Jupiter Ace systems - originals not emulators or recent re-designs such as the Minstrel4th - the value is not FFh but 20h. I've connected the Ace to my scope and logic analyzer and one day have a rational explanation. Until then, it is what it is.

Incidentally, Looking at the Z80 I/O ports at address 2, 128 and 129 produces values which often vary with time, and keypresses.

1
  • Thanks again for all your persistence in experimenting and finding other original ACE owners to participate. If the least significant bit of the port address is not set (i.e, all even address values like 2 & 128), then that makes sense because theory suggests the IN would read the keyboard. So the 129 being modified by the keypresses is a bit of an anomaly. I’m guessing something else is at play to remove the influence of the keyboard when the IM 2 vector read address is being created with the I register and bus value. If not Valkyr would potentially crash when keys are held down. Interesting! Commented Jul 14, 2021 at 20:19
9

I found an article in the ACE User issues #4 from 83 that explicitly states the value to always be 0x20. enter image description here

So, how to do it? The ACE usually runs in Interrupt Mode 1, which, when an interrupt occurs, will transfer control to location 58 Hex. It is vital the any interrupt routing either exits to or is accessed after this routine. The way to access another address is the use Interrupt Mode 2, which takes the high byte of the next address to be executed from the Interrupt register, and the low byte from the data bus. Due to the circuit of the ACE, the data read off the data bus will always be 20 Hex, so we can use any ram with address XX20 to point to our routine. The suggested method is as follows — work out what it is doing, and you will be able to adapt it as necessary to meet your own requirements:

Set RAMTOP to (say) $7F20 /32544)

Store $7F22 at $7F20 (NB. $ means the numbers are base sixteen)

Write code from $7F22 onwards with either;

RST 38 (FF) as the first instruction, and ending with RET (C9)

or End routine with a JP $38 (C3 nn nn) instruction

1
4

I've been researching this in some depth. The results surprised me. I didn't use an original machine for this research, as I don't have one; I used a clone (one that is close enough as to allow Valkyr to work; it basically replicates the schematics but with more modern RAM chips and a few extras that don't affect this).

For this research I've used this routine that I created some years ago, that allows me to find out the bus value at the time of an interrupt:
https://codeberg.org/pgimeno/Gists/src/branch/z80-asm--im2-bus-value/bus_iorq_m1.asm

The bus value depends on the contents of memory addresses 23E0-23FF, as well as on the contents of the character set RAM addresses referenced by the characters in that range. Against all predictions, both video RAMs are active at the time of the interrupt. I don't know why; it might be due to diode D11, which is one that I've always had trouble understanding. Using a 24K pull-up resistor in one of the data lines of the CPU did not alter the outcome; that's why I presume that indeed the RAMs are active. Update: After a second look at the schematics, there's simply nothing stopping the video RAMs from being active, as they are both active all the time unless the CPU is attempting to access one of them. Video is not produced because it is blocked by the serializer that sends the pixels to the screen one by one (Z28).

Anyway, the text RAM range 23E0-23FF (hex) corresponds to video lines 248-255 (dec) of the screen, which is exactly the range in which the /INT line is active, as well as the vertical sync signal labelled FIELD on the schematics.

So, when a character in text RAM is being read, its value is placed on the CPU data bus through an array of 1K resistors (unnamed in the schematics). At the same time, that character is used as an address into the character set RAM, which also places its value on the CPU data bus through another similar array of 1K resistors.

This means that both the character code and the character set byte will be sent to the bus, and whenever a character code bit is different from the corresponding character set bit, the value in the bus will actually be halfway between a 1 and a 0. Most Z80's used in real Jupiter machines (as well as the clone I have) seem to interpret this half-way bit as a 1. Consequently, the value seen by the CPU is the logical OR of the data from the text RAM and the data from the character set RAM (if the half-way bit were seen as a 0 instead, it would be the logical AND instead of OR).

Incidentally, that's the same reason why the character set RAM is documented as write-only, except in the case of attempting to read from it, it's CPU addresses (through Z17-Z18; the text RAM is disabled at that time) rather than character codes that conflict with character set RAM data, and not all bits - just D0-D6. Bit D7 of character set RAM is always readable, but bits D0-D6 will be affected by address lines A3-A9 (normally as an OR, like in the above case). OK, enough digression.

Now, why is the value seen always 20h? It turns out that this last line is filled mostly with spaces by the ROM, as you can readily check on a machine you just booted:

: MULTIPEEK CR BEGIN DUP WHILE SWAP DUP C@ . 1+ SWAP 1- REPEAT DROP DROP CR ; 10208 32 MULTIPEEK 

This prints:

32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 3 2 32 32 32 32 32 32 32 51 49 255 

YMMV for the last bytes.

The shape for the SPACE character has all zeros in the character set RAM, therefore when mixing these 32's with zeros via an OR operation, you get 32. You can't fully rely on this value, though - it might be different if the garbage bytes at the end are read instead of the spaces.

To always obtain a value of 0, you just have to fill that region of the text RAM (27E0-27FF) with all zeros and make sure that the character set entry for character code 0 (2800-2807) is filled with eight zeros. This will be reliable regardless of how the CPU interprets a half-way bit. Alternatively, you can obtain FF by filling 27E0-27FF and 2BF8-2BFF with FF's.

So, what should an emulator do in order to behave like the real machine? Well, that's tricky. First, remember that mask-able interrupts are triggered by level, not by edge. The Jupiter ACE interrupt line is held active for quite a long time - 1,664 CPU clock cycles, and that allows for multiple interrupts to happen while the /INT line is still low, unless the ISR takes at least that long before it re-enables interrupts. The standard ROM inserts a delay on purpose to deal with this.

The emulator should track how many CPU cycles have passed since the start of the vertical sync signal, and find out which character code and shape bytes are active in VRAM when the interrupt is actually triggered. To behave like all currently observed machines, the character code and the character shape bytes should be OR'ed, and that should be taken as the port value. The datasheet specifies that the value is read at the raising edge of the 5th cycle after the interrupt line is detected and an interrupt is possible. 5 cycles = 10 pixels, therefore the first row of the first character will never be read. This cycle count should be used to determine the character code and shape. Assuming that the variable cyc contains the CPU cycle at which the bus is read, where 0 is the start of the vertical sync signal:

bus = mem[0x23E0 + ((cyc >> 2) & 0x1F)]; bus |= mem[0x2800 + (char << 3) + (cyc / 208)]; intvec = mem[(i_reg << 8) + bus]; intvec |= mem[(i_reg << 8) + bus + 1] << 8; 

I'll update this if I find more details.

UPDATE: Actually, the memory region between 9985 and 10238 inclusive is called the "PAD", mentioned in the manual, and is used by the ROM. So, depending on the contents of the PAD, the 20h might not be something you could rely on.

UPDATE 2: Some peripherals may interfere with that. For example, Lex van Sonderen's AY interface does an incomplete I/O port decoding (does not take into account the /RD signal) and thus will place values in the bus, overriding the 1K resistors. The additional condition that A1=A5=0 is easy to meet depending on the address of the code being executed when the interrupt triggers.

2

Looking at a schematic of the Juppi I think you can safely assume that a floating data bus is read as 0xFF.

There are several memory chips connected but none of them is active because /MREQ is not active during the interrupt acknowledge cycle.

And then there are the video RAM and the character RAM. In order to separate their data bus from the other memories, serial resistors are inserted. This way the video output can be maintained while the CPU accesses the other memories.

The data bus of the character RAM is directly connected to the pixel shift register. And here is the part that kind of pulls up the level. This shift register is a TTL chip 74LS166. The "LS" series has inputs that read "high" when left floated, see the Wikipedia page on "Transistor–transistor logic".

So each data bus pin of the CPU has a path to VCC, first the separating resistor of the Juppi of 1kOhm, then the emitter diode of the input transistor inside the 74LS166, and finally the base resistor of nominally 4kOhm inside the 74LS166.

EDIT:

This question circled in the back of my brain, and this answer is not complete. Unfortunately I don't have a real Ace to check, but this is what showed up.

  • If the video RAM is enabled to provide a character to the character RAM at the time the CPU expects the vector, the character will be read as such.
  • If the character RAM is enabled to provide a pattern to the shift register at the time the CPU expects the vector, the pattern will be read as such.

Hm... this might explain why 0x20 is read most of the time. It is the code for space. (Possible experiment: Fill the screen with any other character and see which value is read.)

Conclusion: There is no definite value. You cannot use IM2 reliably.

3
  • 2
    Hmm it’s been verified as 0x20 on a couple of original Jupiter ACE computers: twitter.com/binarydinosaurs/status/1414291969107501062?s=21 plus my own experimentation with a game written for the original computer that fails with 0xFF. I haven’t answered my own question because I’m waiting for John Kennedy to post since he did all the leg work on getting an answer. Incidentally, clones like the Minstrel return a different value. Thanks for spending time to reply. Commented Jul 13, 2021 at 17:46
  • Oh, interesting. If you (or any other) have some insight, please post an detailed answer. I'm especially interested why that value is read. Perhaps some charge from the former memory cycle? Commented Jul 13, 2021 at 19:12
  • If ever I get a definite answer, then I’ll be sure to let you know. Perhaps some one with a real ACE could follow up on your suggested test. One thing to bear in mind is that the Valkyr game expects the 0x20 value for the Interrupt mode 2 otherwise it will crash. And given that the Interrupt is being called 50 times a second, I’d be inclined to conclude that at least under the conditions it sets up that the value is reliable returned the same. Commented Jul 14, 2021 at 20:06

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.