suzukiplan / z80

Single header Z80 emulator for C++ (C++11 or later)
MIT License
55 stars 9 forks source link

MMU out callback missing the MSB of the port #42

Closed JulienNevo closed 2 years ago

JulienNevo commented 2 years ago

When using the "out" callback of the MMU on the Z80 initialization, only the Less Significant Byte of the port is sent, so if using:

ld bc,#1234
ld a,#1
out (c),a

only "#34" is set in the port callback. It should be a 16 bits value.

suzukiplan commented 2 years ago

Current behavior:

#include "z80.hpp"

int main()
{
    unsigned char rom[256] = {
        0x01, 0x34, 0x12, // LD BC, $1234
        0x3E, 0x01,       // LD A, $01
        0xED, 0x79,       // OUT (C), A
        0xc3, 0x07, 0x00, // JMP $0007
    };
    Z80 z80([&rom](void* arg, unsigned short addr) {
        return rom[addr & 0xFF];
    }, [](void* arg, unsigned char addr, unsigned char value) {
        // nothing to do
    }, [](void* arg, unsigned char port) {
        return 0x00;
    }, [](void* arg, unsigned char port, unsigned char value) {
        printf("OUT port $%02X%02X <- $%02X\n",
            ((Z80*)arg)->reg.pair.B, // the top half (A8 through A15) of the address bus (reg.B)
            port, // the bottom half (A0 through A7) of the address bus to select the I/O device at one of 256 possible ports
            value
        );
    }, &z80);
    z80.setDebugMessage([](void* arg, const char* msg) { puts(msg); });
    z80.execute(50);
    return 0;
}

 ↓

[0000] LD BC<$0000>, $1234
[0003] LD A<$FF>, $01
[0005] OUT (C<$34>), A<$01>
OUT port $1234 <- $01
[0007] JP $0007
[0007] JP $0007
[0007] JP $0007

Are you saying that this is how you would like it to be?

#include "z80.hpp"

int main()
{
    unsigned char rom[256] = {
        0x01, 0x34, 0x12, // LD BC, $1234
        0x3E, 0x01,       // LD A, $01
        0xED, 0x79,       // OUT (C), A
        0xc3, 0x07, 0x00, // JMP $0007
    };
    Z80 z80([&rom](void* arg, unsigned short addr) {
        return rom[addr & 0xFF];
    }, [](void* arg, unsigned char addr, unsigned char value) {
        // nothing to do
    }, [](void* arg, unsigned short port) { // port: uchar -> ushort
        return 0x00;
    }, [](void* arg, unsigned short port, unsigned char value) { // port: uchar -> ushort
        printf("OUT port $%04X <- $%02X\n",
            port, // the full (A0 through A15) of the address bus to select the I/O device at one of 65536 possible ports
            value
        );
    }, &z80);
    z80.setDebugMessage([](void* arg, const char* msg) { puts(msg); });
    z80.execute(50);
    return 0;
}
JulienNevo commented 2 years ago

Yes indeed!

suzukiplan commented 2 years ago

Okay, I'll consider it.

JulienNevo commented 2 years ago

Basically, on Amstrad CPC, targeting the sound chip is done this way:

ld bc,#f400+port
out (c),c
ld bc,#f680
out (c),c
ld bc,#f600
out (c),c

ld bc,#f400+value
out (c),c
ld bc,#f6c0
out (c),c
ld bc,#f600
out (c),c

I need to know the 16 bits port that is addressed, else I cannot interpret the port/value correctly. The same is needed for other machines such as Spectrum, MSX, and their various interfaces.

suzukiplan commented 2 years ago

Currently addressing at the Pull Request https://github.com/suzukiplan/z80/pull/43

Ideally, I wanted to overload the callback, but the C++ language specification does not allow it, so I am trying to use a setter as follows:

    Z80 z80(readByte, writeByte, &mmu);
    z80.setPort16Callback([](void* arg, unsigned short port) {
        // port: the full (A0 through A15) of the address bus to select the I/O device at one of 65536 possible ports
    }, [](void* arg, unsigned short port, unsigned char value) {
        // port: the full (A0 through A15) of the address bus to select the I/O device at one of 65536 possible ports
    });

document: https://github.com/suzukiplan/z80/pull/43/commits/e74602901a7256f64b49a1e45626db1157517962

I'm a bit torn as to which one (8bit/16bit) should correspond to the specify in/out callbacks with the constructor...

I have written product code for several Z80 CPU-based emulators (SG-1000, SMS, GameGear, PC-8801 and MSX1) in the past, but have never encountered an example with more than 256 ports, so I am considering using 8-bit callbacks as the default as before.