Skip to content

Commit d387675

Browse files
committed
wip
1 parent 966124a commit d387675

File tree

4 files changed

+88
-52
lines changed

4 files changed

+88
-52
lines changed

pyboy/__main__.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ def valid_file_path(path):
7676

7777
parser.add_argument("--serial-bind", action="store_true", help="Bind to this TCP addres for using Link Cable")
7878
parser.add_argument(
79-
"--serial-address", default=None, type=str, help="Connect (or bind) to this TCP addres for using Link Cable"
79+
"--serial-address", default=None, type=str, help="Connect (or bind) to this TCP address for using Link Cable"
80+
)
81+
parser.add_argument(
82+
"--serial-interrupt-based", action="store_true", help="Use only interrupt-based transfers for using Link Cable"
8083
)
8184

8285
gameboy_type_parser = parser.add_mutually_exclusive_group()
@@ -96,8 +99,8 @@ def valid_file_path(path):
9699

97100
def main():
98101
argv = parser.parse_args()
99-
if argv.serial_bind and not argv.serial_address:
100-
parser.error("--serial-bind requires --serial-address")
102+
if (argv.serial_bind or argv.serial_interrupt_based) and not argv.serial_address:
103+
parser.error("--serial-bind and --serial-interrupt-based requires --serial-address")
101104

102105
log_level(argv.log_level)
103106

pyboy/core/mb.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def __init__(
2828
profiling=False,
2929
serial_address=None,
3030
serial_bind=None,
31+
serial_interrupt_based=False,
3132
):
3233
if bootrom_file is not None:
3334
logger.info("Boot-ROM file provided")
@@ -45,7 +46,7 @@ def __init__(
4546
self.bootrom = bootrom.BootROM(bootrom_file, cgb)
4647
self.ram = ram.RAM(cgb, randomize=randomize)
4748
self.cpu = cpu.CPU(self, profiling)
48-
self.serial = serial.Serial(serial_address, serial_bind)
49+
self.serial = serial.Serial(serial_address, serial_bind, serial_interrupt_based)
4950

5051
if cgb:
5152
self.lcd = lcd.CGBLCD(
@@ -217,7 +218,6 @@ def tick(self):
217218
self.serial.cycles_to_transmit(),
218219
mode0_cycles,
219220
)
220-
cycles = 4
221221

222222
# Profiling
223223
self.cpu.add_opcode_hit(0x76, cycles // 4)

pyboy/core/serial.py

+77-45
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
import logging
22
import os
3+
import queue
34
import socket
45
import sys
6+
import threading
57

68
logger = logging.getLogger(__name__)
79

810
SERIAL_FREQ = 8192 # Hz
911
CPU_FREQ = 4213440 # Hz
1012

13+
async_recv = queue.Queue()
14+
15+
16+
def async_comms(socket):
17+
while True:
18+
item = socket.recv(1)
19+
async_recv.put(item)
20+
1121

1222
class Serial:
13-
def __init__(self, serial_address, serial_bind, serial_interrupt_based=True):
23+
def __init__(self, serial_address, serial_bind, serial_interrupt_based):
1424
self.SB = 0
1525
self.SC = 0
1626
self.connection = None
@@ -44,69 +54,91 @@ def __init__(self, serial_address, serial_bind, serial_interrupt_based=True):
4454
logger.info(f"Connecting to {serial_address}")
4555
self.connection.connect(address_tuple)
4656
logger.info(f"Connection successful!")
47-
# self.connection.setblocking(False)
4857

49-
def tick(self, cycles):
5058
# if self.serial_interrupt_based:
51-
# if self.SC & 1: # Master
52-
# if self.SC & 0x80:
53-
# logger.info(f'Master sending!')
54-
# self.connection.send(bytes([self.SB]))
55-
# # self.connection.setblocking(True)
56-
# data = self.connection.recv(1)
57-
# self.SB = data[0]
58-
# self.SC &= 0b0111_1111
59-
# return True
60-
# else:
61-
# try:
62-
# if self.SC & 0x80:
63-
# # self.connection.setblocking(False)
64-
# logger.info(f'Slave recv!')
65-
# self.connection.send(bytes([self.SB]))
66-
# data = self.connection.recv(1)
67-
# self.SB = data[0]
68-
# self.SC &= 0b0111_1111
69-
# return True
70-
# except BlockingIOError:
71-
# pass
72-
# return False
73-
# return False
74-
# else:
75-
# Check if serial is in progress
59+
# logger.info("Interrupt-based serial emulation active!")
60+
# self.recv_thread = threading.Thread(target=async_comms, args=(self.connection,))
61+
# self.recv_thread.start()
7662

63+
def tick(self, cycles):
7764
if self.connection is None:
7865
return
7966

80-
if self.SC & 0x80 == 0:
67+
# self.cycles_count += 1
68+
69+
if self.serial_interrupt_based:
70+
if self.SC & 0x80 == 0: # Performance optimization. Games might not set this on slave
71+
return False
72+
73+
self.cycles_count += 1
74+
75+
if (self.cycles_to_transmit() == 0):
76+
if self.SC & 1: # Master
77+
if self.SC & 0x80:
78+
logger.info(f"Master sending!")
79+
self.connection.send(bytes([self.SB]))
80+
data = self.connection.recv(1)
81+
self.SB = data[0]
82+
self.SC &= 0b0111_1111
83+
self.cycles_count = 0
84+
return True
85+
else:
86+
# try:
87+
# data = async_recv.get(block=False)
88+
# except queue.Empty:
89+
# return False
90+
try:
91+
data = self.connection.recv(1, socket.MSG_DONTWAIT)
92+
except BlockingIOError:
93+
return False
94+
95+
logger.info(f"Slave recv!")
96+
self.connection.send(bytes([self.SB]))
97+
# data = self.connection.recv(1)
98+
self.SB = data[0]
99+
self.SC &= 0b0111_1111
100+
self.cycles_count = 0
101+
return True
81102
return False
103+
else:
104+
# Check if serial is in progress
105+
if self.SC & 0x80 == 0:
106+
return False
82107

83-
self.cycles_count += 1
108+
self.cycles_count += 1
84109

85-
if (self.cycles_to_transmit() == 0):
86-
# if self.SC & 1: # Master
87-
send_bit = bytes([(self.SB >> 7) & 1])
88-
self.connection.send(send_bit)
110+
if (self.cycles_to_transmit() == 0):
111+
# if self.SC & 1: # Master
112+
send_bit = bytes([(self.SB >> 7) & 1])
113+
self.connection.send(send_bit)
89114

90-
data = self.connection.recv(1)
91-
self.SB = ((self.SB << 1) & 0xFF) | data[0] & 1
115+
data = self.connection.recv(1)
116+
self.SB = ((self.SB << 1) & 0xFF) | data[0] & 1
92117

93-
logger.info(f"recv sb: {self.SB:08b}")
94-
self.trans_bits += 1
118+
logger.info(f"recv sb: {self.SB:08b}")
119+
self.trans_bits += 1
95120

96-
self.cycles_count = 0
121+
self.cycles_count = 0
97122

98-
if self.trans_bits == 8:
99-
self.trans_bits = 0
100-
self.SC &= 0b0111_1111
101-
return True
123+
if self.trans_bits == 8:
124+
self.trans_bits = 0
125+
self.SC &= 0b0111_1111
126+
return True
127+
return False
102128
return False
103129

104130
def cycles_to_transmit(self):
105-
if self.SC & 0x80:
106-
return max(self.cycles_target - self.cycles_count, 0)
131+
if self.connection:
132+
if self.SC & 0x80:
133+
return max(self.cycles_target - self.cycles_count, 0)
134+
# return CPU_FREQ // SERIAL_FREQ
135+
else:
136+
return 1 << 16
107137
else:
108138
return 1 << 16
109139

110140
def stop(self):
111141
if self.connection:
112142
self.connection.close()
143+
# if self.serial_interrupt_based and self.recv_thread:
144+
# self.recv_thread.kill()

pyboy/pyboy.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,9 @@ def __init__(
8888
cgb,
8989
randomize=randomize,
9090
profiling=profiling,
91-
serial_address=kwargs["serial_address"],
92-
serial_bind=kwargs["serial_bind"],
91+
serial_address=kwargs.get("serial_address"),
92+
serial_bind=kwargs.get("serial_bind"),
93+
serial_interrupt_based=kwargs.get("serial_interrupt_based"),
9394
)
9495

9596
# Performance measures

0 commit comments

Comments
 (0)