From aa6b43963e8da67789f0659ebf07ad184a59248d Mon Sep 17 00:00:00 2001 From: Morten Jagd Christensen Date: Fri, 15 Nov 2024 15:13:27 +0100 Subject: [PATCH] docs, udp logger, scripts --- docs/source/log.rst | 7 ++++++ src/devices/disk.py | 19 ++++++++------ src/devices/display.py | 14 +++-------- src/devices/printer.py | 7 ++++-- src/devices/z80io.py | 56 +++++++++++++++++++++--------------------- src/utils/logdisk | 4 +++ src/utils/logio | 4 +++ src/utils/logprt | 4 +++ src/utils/udptx.py | 24 ++++++++++++++++++ 9 files changed, 91 insertions(+), 48 deletions(-) create mode 100755 src/utils/logdisk create mode 100755 src/utils/logio create mode 100755 src/utils/logprt create mode 100644 src/utils/udptx.py diff --git a/docs/source/log.rst b/docs/source/log.rst index a91f94c..5a580c7 100644 --- a/docs/source/log.rst +++ b/docs/source/log.rst @@ -630,3 +630,10 @@ The emulated system now comes preloaded with two floppy images: datamuseum and debugdisk. The first has good integrity the second has a lot of bad tracks so most programs will fail to load and execute. Some good files are: SCR, ALTER, DISPTEST, PTEST, COPY + +2024 11 13 +---------- + +Added support for UDP based logging via the class **utils/UdpTx**. Added scripts +to start logging in **utils**. Now the emulator console can be used for debugging +and the other windows for device-specific inspection. diff --git a/src/devices/disk.py b/src/devices/disk.py index fa0db18..8ca9dc8 100644 --- a/src/devices/disk.py +++ b/src/devices/disk.py @@ -1,12 +1,15 @@ import sys import utils.misc as misc +import utils.udptx as udp # System can have multple Disks (Floppy, Harddisks) # Disks can have multiple Drives (0, 1, ...) +udptx = udp.UdpTx(port=5006, timestamp=True, nl=True) + class Disk: def __init__(self, type, drives): - print(f'disk {type} has {len(drives)} drives') + udptx.send(f'disk {type} has {len(drives)} drives') self.type = type self.selected_drive = -1 self.numdrives = len(drives) @@ -37,10 +40,10 @@ def select_drive(self, i): self.selected_drive = drive # allow selection of unavailable drives if drive <= len(self.drives): - print(f'select_drive: {self.type}/{drive} - available') + udptx.send(f'select_drive: {self.type}/{drive} - available') self.drive = self.drives[drive - 1] else: - print(f'select_drive: {self.type}/{drive} - unavailable') + udptx.send(f'select_drive: {self.type}/{drive} - unavailable') self.drive = 'unavailable' assert self.selected_drive in [1, 2, 3, 4, 5, 6, 7] @@ -76,16 +79,17 @@ def isbusy(self): def status(self) -> int: if self.type != 'floppy': - print(f'disk.status: {self.type} drive not available') + udptx.send(f'disk.status: {self.type} drive not available') return 0 if not self.isdriveavailable(): - print(f'status: drive {self.selected_drive} not valid') + udptx.send(f'status: drive {self.selected_drive} not valid') return 0 return self.drive.status() class Drive: def __init__(self, driveno, fs): # + self.udp = udp.UdpTx(port=5006) self.driveno = driveno self.tracks = fs.tracks self.bytes_per_track = fs.bpt @@ -112,12 +116,12 @@ def step(self, direction): if direction: # UP msg = f'disk {self.driveno}, step up 0x{direction:02x}' msg += f', track {self.current_track} -> {self.current_track + 1}' - print(msg) + udptx.send(msg) self.current_track = (self.current_track + 1) % self.tracks else: # DOWN msg = f'disk {self.driveno}, step down {direction:02x}' msg += f', track {self.current_track} -> {self.current_track - 1}' - print(msg) + udptx.send(msg) if self.current_track == 0: return self.current_track -= 1 @@ -150,7 +154,6 @@ def gettrackno(self): def isbusy(self): while self.current_byte not in self.marks[self.current_track]: self.current_byte = (self.current_byte + 1) % self.bytes_per_track - #print(f'mark {self.current_byte}') return True diff --git a/src/devices/display.py b/src/devices/display.py index 11c2d8b..19abfb6 100644 --- a/src/devices/display.py +++ b/src/devices/display.py @@ -1,18 +1,10 @@ -import socket +import utils.udptx as udp ''' Display emulator for Q1 ''' -def txudp(message): - UDP_IP = "127.0.0.1" - UDP_PORT = 5005 - - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.sendto(message.encode(), (UDP_IP, UDP_PORT)) - - class Display: def __init__(self, height=12, width=40): @@ -21,6 +13,8 @@ def __init__(self, height=12, width=40): self.pos = (0,0) self.buffer = [[chr(0x20) for x in range(width)] for y in range(height)] + self.udp = udp.UdpTx(port=5005) + def _incx(self): x, y = self.pos @@ -57,7 +51,7 @@ def update(self): msg = chr(self.pos[0]) + chr(self.pos[1]) for l in self.buffer: msg += ''.join(l) - txudp(msg) + self.udp.send(msg) if __name__ == '__main__': import time diff --git a/src/devices/printer.py b/src/devices/printer.py index be45fb7..f6a001b 100644 --- a/src/devices/printer.py +++ b/src/devices/printer.py @@ -1,6 +1,9 @@ +import utils.udptx as udp +udptx = udp.UdpTx(port=5008, timestamp=True, nl=True) + # "Q1 ASM IO addresses usage Q1 Lite" p. 75 - 77 class SerialImpactPrinter: @@ -40,7 +43,7 @@ def ctrl_06(self, value: int): v = cm * dy y = y + v self.pos = [x, y] - #print(f'SI printer ctrl: move {dir} {v:6.2f} cm. pos ({x:6.2f}, {y:6.2f})') + udptx.send(f'SI printer ctrl 06: move {dir} {v:6.2f} cm. pos ({x:6.2f}, {y:6.2f})') @@ -71,4 +74,4 @@ def ctrl_07(self, value: int): desc += 'forward ' desc += 'motion' - #print(f'SI printer ctrl: - 0x{value:02x} [{desc}]') + udptx.send(f'SI printer ctrl 07: {value:02x}: [{desc}]') diff --git a/src/devices/z80io.py b/src/devices/z80io.py index d5f0219..aa5764f 100644 --- a/src/devices/z80io.py +++ b/src/devices/z80io.py @@ -4,9 +4,12 @@ import devices.disk as disk import devices.display as display import devices.printer as printer +import utils.udptx as udp # +udptx = udp.UdpTx(port=5007, timestamp=True, nl=True) + def isprintable(c): """True if character is printable ASCII""" return 0x20 <= c <= 0x7D @@ -29,7 +32,6 @@ def __init__(self, m, floppys, hds): self.go = 0 self.stop = 0 self.timeout = False - self.verbose = False self.register_in_cb( 0x00, self.handle_rtc_in) self.register_out_cb(0x00, self.handle_rtc_out) @@ -69,10 +71,6 @@ def __init__(self, m, floppys, hds): self.register_out_cb(0x1b, self.handle_disk_out_1b) - def print(self, s): - if self.verbose: - print(s) - ### Functions for registering and handling IO def register_out_cb(self, outaddr: int, outfunc): @@ -87,7 +85,8 @@ def handle_io_in(self, value) -> int: if inaddr in self.incb: return self.incb[inaddr]() - print(f'IO - unregistered input address 0x{inaddr:02x} at pc {self.m.pc:04x}, exiting') + udptx.send(f'0x{inaddr:02x} - unregistered input address at pc {self.m.pc:04x}, exiting') + print(msg) print() sys.exit() return 0 @@ -98,7 +97,9 @@ def handle_io_out(self, outaddr, outval): if outaddr in self.outcb: self.outcb[outaddr](outval) else: - print(f'IO - unregistered output address 0x{outaddr:02x} (0x{outval:02x})') + msg = f'0x{outaddr:02x} - unregistered output address, value (0x{outval:02x})' + udptx.send(msg) + print(msg) sys.exit() @@ -112,13 +113,13 @@ def handle_rtc_in(self) -> int: return 0 def handle_rtc_out(self, val): - print(f"setting timeout value {val} not supported") + udptx.send(f"0x00 out - rtc: setting timeout value {val} not supported") ### Display def handle_display_in(self) -> int: - self.print('IO - display status: 32 + 16 (Lite, 40 char)') + udptx.send('0x04 in - display status: 32 + 16 (Lite, 40 char)') return 32 + 16 @@ -135,7 +136,7 @@ def handle_display_out_ctrl(self, val) -> str: desc = 'advance right (or new line)' else: desc = f'0x{val:02}' - self.print(f"IO out - display control - {desc}") + #udptx.send(f"IO out - display control - {desc}") ### Keyboard @@ -148,7 +149,7 @@ def handle_key_in(self) -> int: if self.stop: retval = 0x0f self.stop = 0 - self.print(f'IO in - key : 0x{retval:02x}') + udptx.send(f'0x01 in - key: 0x{retval:02x}') return retval @@ -170,7 +171,7 @@ def handle_key_out(self, val): desc += 'K3 ' if val & 0x80: desc += 'INS ' - print(f'IO out - key [{desc}]') + udptx.send(f'0x01 out - key: [{desc}]') ### Printer 5,6,7 - Serial Impact Printer @@ -181,19 +182,23 @@ def handle_key_out(self, val): # "Q1 ASM IO addresses usage Q1 Lite" p. 77 def handle_printer_in_5(self) -> int: status = self.printer.status() - self.print(f'IO in - printer 0x5 status - {status} (1 == selected)') + udptx.send(f'0x05 in - printer status - {status} (1 == selected)') return status + # Print character at current position def handle_printer_out_5(self, val : int): + udptx.send(f'0x05 out - printer output - {val}') self.printer.output(val) def handle_printer_out_6(self, val): + udptx.send(f'0x06 out - printer ctrl 1 - {val}') self.printer.ctrl_06(val) def handle_printer_out_7(self, val): + udptx.send(f'0x07 out - printer ctrl 2 - {val}') self.printer.ctrl_07(val) @@ -202,7 +207,7 @@ def handle_printer_out_7(self, val): # From "Q1 ASM IO addresses usage Q1 Lite" p. 77 def handle_printer_in_8(self) -> int: status = 0x01 - self.print(f'IO in - printer 0x8 status - {status} (1 == selected)') + udptx.send(f'0x08 in - printer status - {status} (1 == selected)') return status @@ -210,18 +215,18 @@ def handle_printer_in_8(self) -> int: ### From "Q1 ASM IO addresses usage Q1 Lite" p. 77 - 80 def handle_disk_out_0a(self, val): if val: - self.print(f'IO out - floppy (control 1 ) - (0x{val:02x})') + udptx.send(f'0x0a out - floppy (control 1 ) - (0x{val:02x})') self.floppy.control1(val) def handle_disk_out_0b(self, val): if val: - self.print(f'IO out - floppy (control 2 ) - (0x{val:02x})') + udptx.send(f'0x0b out - floppy (control 2 ) - (0x{val:02x})') self.floppy.control2(val) def handle_disk_out_09(self, val): - self.print(f'IO out - floppy (data) - (0x{val:02x})') + udptx.send(f'0x09 out - floppy (data) - (0x{val:02x})') self.floppy.data_out(val) @@ -239,44 +244,39 @@ def handle_unkn_in_0c(self): # status 0x80 - program stuck in start up # status 0x40 - DINDEX stuck in F5 status = 0x00 - self.print(f'IO in - unknown in for 0xc - (return {status})') + udptx.send(f'0x0c in - unknown in for 0xc - (return {status})') return status def handle_unkn_out_0c(self, val): + udptx.send(f'0x0c out - unknown device - (0x{val:02x})') if val == 0xa: print(self.prtbuf) self.prtbuf="" else: self.prtbuf += chr(val) - #print(f'{chr(val)}') - - - def handle_disk_out_0c(self, val): - print(f'IO out - unknown device - (0x{val:02x})') - ### Disk 2 Data and Control ### From "Q1 Assembler" p. 52 - 54 def handle_disk_in_19(self): retval = self.hdd.data_in() - self.print(f'IO in - hdd (data): {retval}') + udptx.send(f'0x19 in - hdd (data): {retval}') return retval def handle_disk_in_1a(self): retval = self.hdd.status() - self.print(f'IO in - hdd (status): {retval}') + udptx.send(f'0x1a in - hdd (status): {retval}') return retval def handle_disk_out_1a(self, val): if val: - self.print(f'IO out - hdd (control 1 ) - (0x{val:02x})') + udptx.send(f'0x1a out - hdd (control 1 ) - (0x{val:02x})') self.hdd.control1(val) def handle_disk_out_1b(self, val): if val != 0: - self.print(f'IO out - hdd (control 2 ) - (0x{val:02x})') + udptx.send(f'0x1b out - hdd (control 2 ) - (0x{val:02x})') self.hdd.control2(val) diff --git a/src/utils/logdisk b/src/utils/logdisk new file mode 100755 index 0000000..61b0049 --- /dev/null +++ b/src/utils/logdisk @@ -0,0 +1,4 @@ +printf "\e]1337;SetBadgeFormat=%s\a" \ + $(echo "Q1 Disk" | base64) + +nc -kluvw 0 127.0.0.1 5006 diff --git a/src/utils/logio b/src/utils/logio new file mode 100755 index 0000000..2142c06 --- /dev/null +++ b/src/utils/logio @@ -0,0 +1,4 @@ +printf "\e]1337;SetBadgeFormat=%s\a" \ + $(echo "Q1 IO" | base64) + +nc -kluvw 0 127.0.0.1 5007 diff --git a/src/utils/logprt b/src/utils/logprt new file mode 100755 index 0000000..1b05837 --- /dev/null +++ b/src/utils/logprt @@ -0,0 +1,4 @@ +printf "\e]1337;SetBadgeFormat=%s\a" \ + $(echo "Q1 PRINTER" | base64) + +nc -kluvw 0 127.0.0.1 5008 diff --git a/src/utils/udptx.py b/src/utils/udptx.py new file mode 100644 index 0000000..2a85769 --- /dev/null +++ b/src/utils/udptx.py @@ -0,0 +1,24 @@ +import socket +import time + + +class UdpTx: + + def __init__(self, ip='127.0.0.1', port=9901, timestamp=False, nl=False, term=False): + self.addr = (ip, port) + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.ts = timestamp + self.toffset = time.time() + self.nl = nl + self.term = term + + + def send(self, message: str): + msg = message + if self.term: + print(msg) + if self.ts: + msg = f'{time.time()-self.toffset:7.3f}: ' + msg + if self.nl: + msg += '\n' + self.sock.sendto(msg.encode(), self.addr)