OXYGENHUB
◆ main enhanced-x16-emulator / testbench / testbench.py
4.9 KiB · Python · last changed chore(): initial commit 25 days ago source blame history raw
1
import signal
2
import subprocess
3
4
class Status:
5
    C = 1
6
    Z = 2
7
    I = 4
8
    D = 8
9
    V = 64
10
    N = 128
11
12
class X16TestBench:
13
    emu = None
14
    labels = dict()
15
16
    """
17
    Class constructor.
18
19
    Args:
20
        emulatorpath: Path to the x16emu executable
21
        emulatoroptions: A list of launch options used when starting the emulator
22
23
    Raises:
24
        FileNotFoundError: Happens if emulatorpath not valid
25
    """
26
    def __init__(self, emulatorpath, emulatoroptions=[]):
27
        path = [emulatorpath, "-testbench"]
28
        for o in emulatoroptions:
29
            path.append(o)
30
        
31
        self.emu = subprocess.Popen(path, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
32
    
33
    def __del__(self):
34
        self.emu.stdout.close()
35
        self.emu.stdin.close()
36
        self.emu.terminate()
37
        self.emu.wait()
38
39
    """
40
    Waits until the emulator has sent the ready signal ("RDY").
41
42
    Args:
43
        timeout: Number of seconds until abort waiting
44
45
    Raises:
46
        Exception: If aborted by timeout
47
    """
48
    def waitReady(self, timeout=5):
49
        #Set timeout
50
        signal.signal(signal.SIGALRM, self.__timeout)
51
        signal.alarm(timeout)
52
53
        r = ""
54
        while r.startswith("RDY")==False:
55
            r = self.__readline()
56
            if r.startswith("ERR"):
57
                raise Exception(r[3:].strip())
58
59
    def __writeline(self, str):
60
        self.emu.stdin.write((str + "\n").encode('utf-8'))
61
        self.emu.stdin.flush()
62
63
    def __readline(self):
64
        return self.emu.stdout.readline().decode('utf-8')
65
66
    def __getresponse(self):
67
        r = ""
68
        while r == "":
69
            r = self.__readline()
70
        
71
        if r.startswith("ERR"):
72
            raise Exception("Invalid command")
73
        else:
74
            return r
75
76
    def __tohex8(self, val):
77
        if val < 0 or val > 255:
78
            raise Exception("Value out of range")
79
        else:
80
            return "%0.2X" % val
81
82
    def __tohex16(self, val):
83
        if val < 0 or val > 65535:
84
            raise Exception("Value out of range")
85
        else:
86
            return "%0.4X" % val
87
88
    def __toint8(self, val):
89
        i = int(val, 16)
90
        if i < 0 or i > 255:
91
            raise Exception("Value out of range")
92
        else:
93
            return i
94
    
95
    def __toint16(self, val):
96
        i = int(val, 16)
97
        if i < 0 or i > 65535:
98
            raise Exception("Value out of range")
99
        else:
100
            return i
101
102
    def __timeout(self, s, f):
103
        raise Exception("Timeout error")
104
105
    """
106
    Opens and reads a text file in VICE format, to import label names 
107
    and corresponding memory addresses.
108
109
    Params:
110
        path: Path to the label file
111
112
    Raises:
113
        FileNotFoundError
114
    """
115
    def importViceLabels(self, path):
116
        f = open(path, "r")
117
        for line in f.readlines():
118
            fields = line.split(" ")
119
            if len(fields) >= 3:
120
                self.labels[fields[2][1:].strip()] = self.__toint16(fields[1].strip())
121
        f.close()
122
123
    def setRamBank(self, value):
124
        self.__writeline("RAM " + self.__tohex8(value))
125
        self.waitReady()
126
127
    def setRomBank(self, value):
128
        self.__writeline("ROM " + self.__tohex8(value))
129
        self.waitReady()
130
    
131
    def setMemory(self, address, value):
132
        self.__writeline("STM " + self.__tohex16(address) + " " + self.__tohex8(value))
133
        self.waitReady()
134
135
    def fillMemory(self, address1, address2, value):
136
        self.__writeline("FLM " + self.__tohex16(address1) + " " + self.__tohex16(address2) + " " + self.__tohex8(value))
137
        self.waitReady()
138
139
    def setA(self, value):
140
        self.__writeline("STA " + self.__tohex8(value))
141
        self.waitReady()
142
143
    def setX(self, value):
144
        self.__writeline("STX " + self.__tohex8(value))
145
        self.waitReady()
146
147
    def setY(self, value):
148
        self.__writeline("STY " + self.__tohex8(value))
149
        self.waitReady()
150
151
    def setStatus(self, value):
152
        self.__writeline("SST " + self.__tohex8(value))
153
        self.waitReady()
154
    
155
    def setStackPointer(self, value):
156
        self.__writeline("SSP " + self.__tohex8(value))
157
        self.waitReady()
158
159
    def run(self, address, timeout=5):
160
        self.__writeline("RUN " + self.__tohex16(address))
161
        self.waitReady(timeout)
162
163
    def getMemory(self, address):
164
        self.__writeline("RQM " + self.__tohex16(address))
165
        return self.__toint8(self.__getresponse())
166
167
    def getA(self):
168
        self.__writeline("RQA")
169
        return self.__toint8(self.__getresponse())
170
171
    def getX(self):
172
        self.__writeline("RQX")
173
        return self.__toint8(self.__getresponse())
174
175
    def getY(self):
176
        self.__writeline("RQY")
177
        return self.__toint8(self.__getresponse())
178
179
    def getStatus(self):
180
        self.__writeline("RST")
181
        return self.__toint8(self.__getresponse())
182
183
    def getStackPointer(self):
184
        self.__writeline("RSP")
185
        return self.__toint8(self.__getresponse())