mirror of
https://github.com/rzzzwilson/pymlac.git
synced 2025-06-10 09:32:41 +00:00
189 lines
5.0 KiB
Python
Executable File
189 lines
5.0 KiB
Python
Executable File
"""
|
|
The Imlac display.
|
|
|
|
A temporary version that outputs a PPM graphics image.
|
|
|
|
The Imlac dislay X and Y registers are of size 11 bits. The most significant
|
|
6 bits are the MSB and the least significant 5 bits are the LSB. A DLXA (or
|
|
DLYA) loads the display X (or Y) register from the 10 bits of valu, with bit
|
|
10 of the X/Y register being set to 0.
|
|
|
|
The virtual display is 2048x2048 pixels, but the physical display is
|
|
1024x1024 pixels.
|
|
"""
|
|
|
|
|
|
from Globals import *
|
|
# if we don't have log.py, don't crash
|
|
try:
|
|
import log
|
|
log = log.Log('test.log', log.Log.DEBUG)
|
|
except ImportError:
|
|
def log(*args, **kwargs):
|
|
pass
|
|
|
|
|
|
# the widget version
|
|
__version__ = '0.0'
|
|
|
|
# type of events
|
|
DisplayStop = 1
|
|
|
|
|
|
################################################################################
|
|
# The actual pymlac display widget.
|
|
# This version is just a debug version that creates PPM files.
|
|
################################################################################
|
|
|
|
class Display(object):
|
|
|
|
# BackgroundColour = 'black'
|
|
# DrawColour = '#ffff88'
|
|
|
|
SYNC_HZ = 40
|
|
SYNC_40HZ_CYCLE_COUNT = int(CYCLES_PER_SECOND / SYNC_HZ)
|
|
|
|
BackgroundColour = 1
|
|
DrawColour = 0
|
|
|
|
# max coordinates of pymlac physical display
|
|
ScaleMaxX = 1024
|
|
ScaleMaxY = 1024
|
|
|
|
|
|
def __init__(self):
|
|
# create and initialise the image array
|
|
# reference .array[y*ScaleMaxX + x]
|
|
self.array = [self.BackgroundColour] * self.ScaleMaxY * self.ScaleMaxX
|
|
|
|
# set internal state
|
|
self.running = 0
|
|
self.cycle_count = 0
|
|
self.Sync40hz = 1
|
|
self.next_file_num = 0
|
|
|
|
# 'dirty' flag set after writing
|
|
self.dirty = False
|
|
|
|
log('Display: instance created')
|
|
|
|
def write(self):
|
|
"""Write display array to PPM file."""
|
|
|
|
self.next_file_num += 1
|
|
filename = 'pymlac_%06d.pbm' % self.next_file_num
|
|
with open(filename, 'wb') as handle:
|
|
# output header data
|
|
handle.write(bytes('P1\n', 'utf-8'))
|
|
handle.write(bytes('# created by pymlac %s\n' % __version__, 'utf-8'))
|
|
handle.write(bytes('%d %d\n' % (self.ScaleMaxX, self.ScaleMaxY), 'utf-8'))
|
|
|
|
# output graphics data
|
|
for v in self.array:
|
|
handle.write(bytes('%d\n' % v, 'utf-8'))
|
|
|
|
self.dirty = False
|
|
|
|
log('Display: array written to file %s' % filename)
|
|
|
|
def draw(self, x1, y1, x2, y2, dotted=False):
|
|
"""Draw a line on the screen.
|
|
|
|
x1, y1 start coordinates
|
|
x2, y2 end coordinates
|
|
dotted True if dotted line, else False (IGNORED)
|
|
|
|
Algorithm from:
|
|
http://csourcecodes.blogspot.com/2016/06/bresenhams-line-drawing-algorithm-generalized-c-program.html
|
|
"""
|
|
|
|
# convert virtual coords to physical
|
|
x1 = x1 // 2
|
|
y1 = y1 // 2
|
|
x2 = x2 // 2
|
|
y2 = y2 // 2
|
|
|
|
# invert the Y axis
|
|
y1 = self.ScaleMaxY - y1
|
|
y2 = self.ScaleMaxY - y2
|
|
|
|
# draw the line (Bresenham algorithm)
|
|
x = x1
|
|
y = y1
|
|
dx = abs(x2 - x1)
|
|
dy = abs(y2 - y1)
|
|
s1 = 1 if x2 > x1 else -1 # sign(x2 - x1)
|
|
s2 = 1 if y2 > y1 else -1 # sign(y2 - y1)
|
|
swap = False
|
|
self.array[(y-1)*self.ScaleMaxX + x - 1] = self.DrawColour
|
|
if dy > dx:
|
|
(dx, dy) = (dy, dx)
|
|
swap = True
|
|
p = 2*dy - dx
|
|
for i in range(0, dx):
|
|
self.array[(y-1)*self.ScaleMaxX + x - 1] = self.DrawColour
|
|
while p >= 0:
|
|
p = p - 2*dx
|
|
if swap:
|
|
x += s1
|
|
else:
|
|
y += s2
|
|
p = p + 2*dy
|
|
if swap:
|
|
y += s2
|
|
else:
|
|
x += s1
|
|
i += 1
|
|
|
|
# set display flag to "changed"
|
|
self.dirty = True
|
|
|
|
def clear(self):
|
|
"""Clear the display."""
|
|
|
|
log('Display: clearing the display')
|
|
|
|
# write display to next PPM file first
|
|
if self.dirty:
|
|
self.write()
|
|
self.array = [self.BackgroundColour] * self.ScaleMaxY * self.ScaleMaxX
|
|
self.dirty = False
|
|
|
|
def syncclear(self):
|
|
self.Sync40hz = 0
|
|
self.cycle_count = self.SYNC_40HZ_CYCLE_COUNT
|
|
|
|
def ready(self):
|
|
return self.Sync40hz
|
|
|
|
def tick(self, cycles):
|
|
"""Advance the internal state by given cycles."""
|
|
|
|
self.cycle_count -= cycles
|
|
if self.cycle_count <= 0:
|
|
self.Sync40hz = 1
|
|
self.cycle_count = 0
|
|
|
|
def close(self):
|
|
"""Close display, writing data if 'dirty'."""
|
|
|
|
if self.dirty:
|
|
self.write()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
"""Test case - draw radial lines."""
|
|
|
|
d = Display()
|
|
granularity = 32
|
|
for x in range(0, 2048, granularity):
|
|
d.draw(x, 0, 1023, 1023)
|
|
for y in range(0, 2048, granularity):
|
|
d.draw(2048, y, 1023, 1023)
|
|
for x in range(2048, 0, -granularity):
|
|
d.draw(x, 2048, 1023, 1023)
|
|
for y in range(2048, 0, -granularity):
|
|
d.draw(0, y, 1023, 1023)
|
|
d.clear()
|
|
d.close()
|