Skip to content

Commit

Permalink
Merge pull request #43 from suzukiplan/1.7.0
Browse files Browse the repository at this point in the history
Support 16bit address port of in/out callbacks
  • Loading branch information
suzukiplan authored Oct 18, 2022
2 parents f1e4def + 6636171 commit c83809a
Show file tree
Hide file tree
Showing 12 changed files with 459 additions and 199 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Change log

## Version 1.7.0 (Oct 17, 2022 JST)

- **Destructive** change specification of in/out callback:
- before: 2nd argument is `unsigned char`
- `std::function<unsigned char(void*, unsigned char)> in`
- `std::function<void(void*, unsigned char, unsigned char)> out`
- after: 2nd argument is `unsigned short`
- `std::function<unsigned char(void*, unsigned short)> in`
- `std::function<void(void*, unsigned short, unsigned char)> out`
- Add an argument `returnPortAs16Bits` to the constructor to specify whether the port should receive 16-bit
- Add a constructor without set callbacks
- Add the set callbacks method: `setupCallback` and `setupCallbackFP`
- `setupCallback` ... using `std::function`
- `setupCallbackFP` ... using function pointer
- Add the set callback as function ponter methods:
- `setDebugMessageFP`
- `setConsumeClockFP`
- Optimize performance: `BreakOperands` and `BreakPoints`
- before: liner search the target address/opcode
- after: binary search the target address/opcode

## Version 1.6.0 (Oct 16, 2022 JST)

- Use `std::function` ... `constructor, addBreakPoint, addBreakOperand, setDebugMessage, setConsumeClockCallback, addReturnHandler, addCallHandler`
Expand Down
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ void writeByte(void* arg, unsigned short addr, unsigned char value)
}

// IN operand request from CPU
unsigned char inPort(void* arg, unsigned char port)
unsigned char inPort(void* arg, unsigned short port)
{
return ((MMU*)arg)->IO[port];
}

// OUT operand request from CPU
void outPort(void* arg, unsigned char port, unsigned char value)
void outPort(void* arg, unsigned short port, unsigned char value)
{
((MMU*)arg)->IO[port] = value;
}
Expand All @@ -106,6 +106,36 @@ void outPort(void* arg, unsigned char port, unsigned char value)
Z80 z80(readByte, writeByte, inPort, outPort, &mmu);
```

Note that by default, only the lower 8 bits of the port number can be obtained in the callback argument, and the upper 8 bits must be referenced from register B.

If you want to get it in 16 bits from the beginning, please initialize with 6th argument to `true` as follows:

```c++
Z80 z80(&mmu, readByte, writeByte, inPort, outPort, true);
```
Normally, `std::function` is used for callbacks, but in more performance-sensitive cases, a function pointer can be used.
```c++
Z80 z80(&mmu);
z80.setupCallbackFP([](void* arg, unsigned short addr) {
return 0x00; // read procedure
}, [](void* arg, unsigned char addr, unsigned char value) {
// write procedure
}, [](void* arg, unsigned short port) {
return 0x00; // input port procedure
}, [](void* arg, unsigned short port, unsigned char value) {
// output port procedure
});
```

However, using function pointers causes inconveniences such as the inability to specify a capture in a lambda expression.
In most cases, optimization with the `-O2` option will not cause performance problems, but in case environments where more severe performance is required, `setupCallbackFP` is recommended.

The following article (in Japanese) provides a performance comparison between function pointers and `std::function`:

https://qiita.com/suzukiplan/items/e459bf47f6c659acc74d

### 4. Execute

```c++
Expand Down Expand Up @@ -145,6 +175,7 @@ Debug message contains dynamic disassembly results step by step.
```
- call `resetDebugMessage` if you want to remove the detector.
- call `setDebugMessageFP` if you want to use the function pointer.
### Use break point
Expand Down Expand Up @@ -203,6 +234,7 @@ If you want to implement stricter synchronization, you can capture the CPU clock
```

- call `resetConsumeClockCallback` if you want to remove the detector.
- call `setConsumeClockCallbackFP` if you want to use the function pointer.

> With this callback, the CPU cycle (clock) can be synchronized in units of 3 to 4 Hz, and while the execution of a single Z80 instruction requires approximately 10 to 20 Hz of CPU cycle (time), the SUZUKI PLAN - Z80 Emulator can synchronize the CPU cycle (time) for fetch, execution, write back, etc. However, the SUZUKI PLAN - Z80 Emulator can synchronize fetches, executions, writes, backs, etc. in smaller units. This makes it easy to implement severe timing emulation.
Expand Down
5 changes: 3 additions & 2 deletions test-ex/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
CFLAGS=-I../ \
-std=c++11 \
-O2 \
-Wall \
-Wfloat-equal \
-Wshadow \
Expand All @@ -14,7 +15,7 @@ clean:
-rm cpm

cpm: cpm.cpp ../z80.hpp
clang -Os $(CFLAGS) cpm.cpp -lstdc++ -o cpm
clang $(CFLAGS) cpm.cpp -lstdc++ -o cpm

zexdoc: cpm
./cpm -e zexdoc.cim
Expand All @@ -23,7 +24,7 @@ zexall: cpm
./cpm -e zexall.cim

ci:
g++-10 -O2 $(CFLAGS) -Wclass-memaccess cpm.cpp -lstdc++ -o cpm
g++-10 $(CFLAGS) -Wclass-memaccess cpm.cpp -lstdc++ -o cpm
./cpm -e -n zexall.cim

full: cpm
Expand Down
7 changes: 4 additions & 3 deletions test-ex/cpm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ class CPM {
// bridge static functions
static inline unsigned char readMemory(void* arg, unsigned short addr) { return ((CPM*)arg)->readMemory(addr); }
static inline void writeMemory(void* arg, unsigned short addr, unsigned char value) { ((CPM*)arg)->writeMemory(addr, value); }
static inline unsigned char inPort(void* arg, unsigned char port) { return ((CPM*)arg)->inPort(port); }
static inline void outPort(void* arg, unsigned char port, unsigned char value) { ((CPM*)arg)->outPort(port, value); }
static inline unsigned char inPort(void* arg, unsigned short port) { return ((CPM*)arg)->inPort(port); }
static inline void outPort(void* arg, unsigned short port, unsigned char value) { ((CPM*)arg)->outPort(port, value); }

int main(int argc, char* argv[])
{
Expand Down Expand Up @@ -118,7 +118,8 @@ int main(int argc, char* argv[])
return 1;
}
CPM cpm;
Z80 z80(readMemory, writeMemory, inPort, outPort, &cpm);
Z80 z80(&cpm);
z80.setupCallbackFP(readMemory, writeMemory, inPort, outPort);
if (!cpm.init(cimPath)) {
puts("Cannot initialized");
return -1;
Expand Down
6 changes: 6 additions & 0 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ all:
make test-status
make test-im2
make test-branch
make test-out

test-clock:
clang $(CFLAGS) test-clock.cpp -lstdc++
Expand All @@ -32,3 +33,8 @@ test-branch:
clang $(CFLAGS) test-branch.cpp -lstdc++
./a.out > test-branch.txt
cat test-branch.txt

test-out:
clang $(CFLAGS) test-out.cpp -lstdc++
./a.out > test-out.txt
cat test-out.txt
4 changes: 2 additions & 2 deletions test/test-branch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ static void writeMemory(void* arg, unsigned short addr, unsigned char value)
ram[addr & 0x7FFF] = value;
}

static unsigned char inPort(void* arg, unsigned char port) { return 0xFF; }
static void outPort(void* arg, unsigned char port, unsigned char value) {}
static unsigned char inPort(void* arg, unsigned short port) { return 0xFF; }
static void outPort(void* arg, unsigned short port, unsigned char value) {}

static void printIdent()
{
Expand Down
4 changes: 2 additions & 2 deletions test/test-clock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ void writeByte(void* arg, unsigned short addr, unsigned char value)
((MMU*)arg)->RAM[addr] = value;
}

unsigned char inPort(void* arg, unsigned char port)
unsigned char inPort(void* arg, unsigned short port)
{
return ((MMU*)arg)->IO[port];
}

void outPort(void* arg, unsigned char port, unsigned char value)
void outPort(void* arg, unsigned short port, unsigned char value)
{
((MMU*)arg)->IO[port] = value;
}
Expand Down
4 changes: 2 additions & 2 deletions test/test-im2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ void writeMemory(void* ctx, unsigned short addr, unsigned char value)
ram[addr & 0x00FF] = value;
}

unsigned char readPort(void* ctx, unsigned char port) { return 0x00; }
void writePort(void* ctx, unsigned char port, unsigned char value) {}
unsigned char readPort(void* ctx, unsigned short port) { return 0x00; }
void writePort(void* ctx, unsigned short port, unsigned char value) {}

int main()
{
Expand Down
57 changes: 57 additions & 0 deletions test/test-out.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#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
0xED, 0x78, // IN A, (C)
0xc3, 0x09, 0x00, // JMP $0009
};

{
puts("=== 8bit port mode ===");
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) {
printf("IN port A <- $%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
);
return 0x00;
}, [](void* arg, unsigned short 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, false);
z80.setDebugMessage([](void* arg, const char* msg) { puts(msg); });
z80.execute(50);
}

{
puts("=== 16bit port mode ===");
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) {
printf("IN port A <- $%04X\n",
port // the full (A0 through A15) of the address bus to select the I/O device at one of 65536 possible ports
);
return 0;
}, [](void* arg, unsigned short port, unsigned char value) {
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, true);
z80.setDebugMessage([](void* arg, const char* msg) { puts(msg); });
z80.execute(50);
}
return 0;
}
16 changes: 16 additions & 0 deletions test/test-out.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
=== 8bit port mode ===
[0000] LD BC<$0000>, $1234
[0003] LD A<$FF>, $01
[0005] OUT (C<$34>), A<$01>
OUT port $1234 <- $01
IN port A <- $1234
[0007] IN A<$01>, (C<$34>) = $00
[0009] JP $0009
=== 16bit port mode ===
[0000] LD BC<$0000>, $1234
[0003] LD A<$FF>, $01
[0005] OUT (C<$34>), A<$01>
OUT port $1234 <- $01
IN port A <- $1234
[0007] IN A<$01>, (C<$34>) = $00
[0009] JP $0009
4 changes: 2 additions & 2 deletions test/test-status.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ void writeByte(void* arg, unsigned short addr, unsigned char value)
((MMU*)arg)->RAM[addr] = value;
}

unsigned char inPort(void* arg, unsigned char port)
unsigned char inPort(void* arg, unsigned short port)
{
return ((MMU*)arg)->IO[port];
}

void outPort(void* arg, unsigned char port, unsigned char value)
void outPort(void* arg, unsigned short port, unsigned char value)
{
((MMU*)arg)->IO[port] = value;
}
Expand Down
Loading

0 comments on commit c83809a

Please sign in to comment.