#536 Sony HBI-55 emulation bug

Next_release
open
nobody
None
5
2015-02-19
2014-05-30
No

When writing a value to HBI memory, it goes through the I8255 PPI IC, quote I8255.cc:

void I8255::outputPortC(byte value, EmuTime::param time)
{
    latchPortC = value;
    if (!(control & DIRECTION_C1)) {
        // output
        interface.writeC1(latchPortC >> 4, time);
    }
    if (!(control & DIRECTION_C0)) {
        // output
        interface.writeC0(latchPortC & 15, time);
    }
}

Note how the high nibble (writeC1) is output first, then the low nibble (writeC0).

Now, let’s look at MSXHBI55.cc:

void MSXHBI55::writeC0(nibble value, EmuTime::param /*time*/)
{
    writeLatch = (writeLatch & 0xF0) | value;
}
void MSXHBI55::writeC1(nibble value, EmuTime::param /*time*/)
{
    writeLatch = (writeLatch & 0x0F) | (value << 4);
    if (mode == 1) {
        sram->write(writeAddress, writeLatch);
    }
}

This code expects writeC0 to be called before writeC1 is called. But that’s not the case.

As a result, when I write 0D3H, it writes some other value. When I write 47H next, it will write 43H in stead, because it still uses the low nibble from the previous value.

Little test program:

10 OUT &HB3,&H80   ' configure PPI for write mode
20 OUT &HB0,&HC2   ' low address part
30 OUT &HB1,&H4A   ' = &H40 (write) + &H0A (high address part)
40 OUT &HB2,&HD3   ' the value to write
50 OUT &HB3,&H89   ' configure PPI for read mode
60 OUT &HB0,&HC2   ' low address part
70 OUT &HB1,&HCA   ' = &HC0 (read) + &H0A (high address part)
80 PRINT INP(&HB2) ' contents of &H0AC2

The output of this program should be 211 (0D3H). Note that due to the broken implementation, you need to run the program twice to get the correct result. Change line 40 to test a different value.

There’s also a more elaborate test program here: http://map.grauw.nl/resources/hbi55.php

By the way, the test program at the top of the MSXHBI55.cc listing contains this bit: “OUT &HB3,128-9(I<>0)”. I’m not entirely clear as to why it does not output 137 when I is 0? I think it should always output 137, the “-9(I<>0)” part should go. Otherwise, that should also do the trick.

I looked at the version history, it was broken since 2004 by 3a71a96.

Discussion

  • Laurens Holst

    Laurens Holst - 2014-05-31

    p.s. You can write some unit tests calling writeIO() / readIO() to verify the fix :).

     
  • Wouter Vermaelen

    Thanks for your report!

    Swapping C0/C1 (in either I8255 or MSXHBI55), would 'fix' the symptom. Though it still won't work if port C is set half as input, half as output.

    I wonder what happens on a real HBI55 in this scenario. When a nibble is set as input, the PPI makes the corresponding pins high impedance. I very much doubt the HBI55 will have pull-up (or -down) resistors attached to these pins because in normal circumstances they aren't needed. So, I think, it depends on the PPI whether '0's, '1' or random bits are written to the corresponding nibble in RAM that was set to input.

    @Laurens: You've already investigated enough to fix the 'normal' HBI55 use case. It would be nice if you could also investigate how this 'unusual' case behaves. Thanks!

    BTW: If I had to guess I'd say the nibble that was set as input behaves as-if you wrote 0b1111 to it. Laurens, if you can confirm this, that'll allow some simplifications in our current PPI emulation.

     
  • Laurens Holst

    Laurens Holst - 2014-05-31
    10 ' Unit test 1, writing nibbles
    11 M=&H80:O=1:A=&H11:EX=&H11:GOSUB 100
    12 M=&H81:O=1:A=&H22:EX=&H21:GOSUB 100
    13 M=&H88:O=1:A=&H33:EX=&H23:GOSUB 100
    14 M=&H89:O=1:A=&H44:EX=&H23:GOSUB 100
    20 ' Unit test 2, uses last output value
    21 M=&H80:O=1:A=&H11:EX=&H11:GOSUB 100
    22 M=&H80:O=2:A=&H99:EX=&H99:GOSUB 100
    23 M=&H81:O=1:A=&H22:EX=&H29:GOSUB 100
    30 ' Unit test 3, something's funny with address 0!
    31 M=&H80:O=0:A=&H11:EX=&H01:GOSUB 100
    32 M=&H81:O=0:A=&H22:EX=&H21:GOSUB 100
    33 M=&H88:O=0:A=&H33:EX=&H23:GOSUB 100
    34 M=&H89:O=0:A=&H44:EX=&H23:GOSUB 100
    40 END
    100 OUT &HB3,M
    110 OUT &HB0,O
    120 OUT &HB1,&H40
    130 OUT &HB2,A
    140 OUT &HB3,&H89
    150 OUT &HB0,O
    160 OUT &HB1,&HC0
    170 I=INP(&HB2)
    180 IF EX <> I THEN PRINT "Expected:";EX;", was:";I
    190 RETURN
    run
    Ok
    

    So, it remembers the last output value and just changes the bits that are in output mode. I imagine the bit-set command does the same thing.

    Note the third test, when I use O=0 in stead of 1 the return value is not the value I just set! I guess that explains the <> 0 in BIFI’s test, there’s something going on there.

     
  • Wouter Vermaelen

    Fixed in revision 523a56eda.
    The original test case now passes. The 2nd test passes part 1 and part2 but not part 3. So there is indeed something funny with address 0 :-)

    Shall we leave this bug report open for the address 0 stuff? Or should we create a separate one?

    @Laurens: Very nice article about HBI55 on http://map.grauw.nl. Though I think there's a mistake in the table for port B bit 7, read and write are swapped.

     
  • Laurens Holst

    Laurens Holst - 2014-06-01

    Thanks, table is fixed :).