From 5aa05091c7b081a89e1a942d0de6e40c9888daed Mon Sep 17 00:00:00 2001 From: Adam Mayer Date: Tue, 21 Nov 2017 12:09:54 -0500 Subject: [PATCH] initial checkin of Apple 410 doc and driver --- 10print.py | 19 +++++ apple410.py | 50 ++++++++++++ doc/Apple 401 Plotter.md | 153 +++++++++++++++++++++++++++++++++++++ moore.py | 54 +++++++++++++ plot_scripts/self_text.scr | 105 +++++++++++++++++++++++++ plot_to_svg.py | 112 +++++++++++++++++++++++++++ roms/B9801YL.bin | Bin 0 -> 8192 bytes roms/B9801YM.bin | Bin 0 -> 8192 bytes roms/B9801YN.bin | Bin 0 -> 8192 bytes roms/README.md | 10 +++ roms/ROM.bin | Bin 0 -> 24576 bytes roms/test_extractor.py | 20 +++++ send_to_plotter.py | 9 +++ 13 files changed, 532 insertions(+) create mode 100755 10print.py create mode 100755 apple410.py create mode 100644 doc/Apple 401 Plotter.md create mode 100755 moore.py create mode 100644 plot_scripts/self_text.scr create mode 100755 plot_to_svg.py create mode 100644 roms/B9801YL.bin create mode 100644 roms/B9801YM.bin create mode 100644 roms/B9801YN.bin create mode 100644 roms/README.md create mode 100644 roms/ROM.bin create mode 100755 roms/test_extractor.py create mode 100755 send_to_plotter.py diff --git a/10print.py b/10print.py new file mode 100755 index 0000000..a4b3ef9 --- /dev/null +++ b/10print.py @@ -0,0 +1,19 @@ +#!/usr/bin/python3 + +import apple410 +import random +import math + +lineht=100 +# 2394 x 1700 +w=2394 +h=1700 + +a = apple410.Apple410('/dev/ttyUSB0') +a.send("LS{}".format(lineht)) +ydim = math.floor(h/lineht) +xdim = math.floor(w/lineht) +for y in range(ydim): + a.send("MA0,{}".format(lineht*y)) + a.send("PL"+''.join(map(lambda _:random.choice('\\/'),range(xdim)))) +a.send("CH") diff --git a/apple410.py b/apple410.py new file mode 100755 index 0000000..7482588 --- /dev/null +++ b/apple410.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +import serial +import time +import sys + +class Apple410: + """A simple class for queing up commands for the Apple 410""" + def __init__(self, portname, baud=1200): + self.serial = serial.Serial(portname, baud, rtscts=True, dsrdtr=True, timeout=0.1) + self.pos = (0,0) + self.wd = self.vp = (0,0,2394,1759) + + def sendchar(self, c): + self.serial.flush() + while not self.serial.cts: + time.sleep(0.1) + while not self.serial.dsr: + time.sleep(0.1) + self.serial.write(c.encode('ascii')) + + def send(self, command): + for c in command: + self.sendchar(c) + self.sendchar('\x03') + + def move_to(self, coords): + self.send('MA{},{}'.format(coords[0],coords[1])) + self.pos = coords + + def draw_to(self, coords): + self.send('DA{:.2f},{:.2f}'.format(coords[0],coords[1])) + self.pos = coords + + def pen_select(self, index): + self.send('PS{}'.format(index)) + +if __name__ == '__main__': + scr = 'test_script.cmds' + if len(sys.argv) > 1: + scr = sys.argv[1] + print("Running file {}".format(scr)) + a = Apple410('/dev/ttyUSB0') + if scr == "-": + f = sys.stdin + else: + f = open(scr) + for line in f.readlines(): + print("SENDING: {}".format(line.strip())) + a.send(line.strip()) + diff --git a/doc/Apple 401 Plotter.md b/doc/Apple 401 Plotter.md new file mode 100644 index 0000000..3f7ffbc --- /dev/null +++ b/doc/Apple 401 Plotter.md @@ -0,0 +1,153 @@ +# The Apple 401 Color Plotter + +## General concepts + +The way the 401 interprets a coordinate at any given time depends on the current +viewport and window settings. The "viewport" is a rectangle specified in hardware +absolute coordinates and defines the physical area in which coordinates are resolved. +The "window" defines the virtual coordinates of the drawing area, which are then +mapped to the viewport. + +Commands sent to the 401 are terminated by the ASCII "end of text" delimiter, 0x03. + +### Serial connection + +The 8 DIP switches (SW1-SW8) on the back of the plotter are used to configure the +serial interface. + +SW1 selects the data bits. ON = 7 bits, OFF = 8 bits. + +SW2 selects whether parity is used. ON = no parity bit, OFF = use a parity bit. +If parity is selected, SW3 determines the type of parity bit. ON = odd parity, +OFF = even parity. + +SW4 and SW5 select the number of stop bits: +| SW4 | SW5 | Stop bits | +|------|------|---------------| +| OFF | OFF | 2 stop bits | +| ON | OFF | 1.5 stop bits | +| OFF | ON | 1 stop bit | +| ON | ON | invalid | + +SW6, SW7, and SW8 select the baud rate: +| SW6 | SW7 | SW8 | Baud | +|-----|-----|-----|------------| +| OFF | OFF | OFF | 75 baud | +| OFF | OFF | ON | 150 baud | +| OFF | ON | OFF | 300 baud | +| OFF | ON | ON | 600 baud | +| ON | OFF | OFF | 1200 baud | +| ON | OFF | ON | 2400 baud | +| ON | ON | OFF | 4800 baud | +| ON | ON | ON | 9600 baud | + +**The 401 uses hardware handshaking.** It never returns data to the computer +over the serial line and sets DSR when its internal buffer is full. Sending even +one byte after the DSR line has been set will corrupt the 401's command buffer +and trigger an error. Most USB to RS232 cables will attempt to bundle up several +bytes before transmission, so unless you're sure that your cable can handle it +I recommend flushing the serial connection after every byte and checking the DSR +line yourself. + +## Command Reference + +### Move Absolute (MA) + +``` +MAx,y +``` + +Raise the pen and move the plotter head to the position specified by the x,y coordinates. + +Params: 2 + +### ??Move relative (MR)?? + +``` +MRx,y +``` + +Raise the pen and move the plotter head to a position offset x,y from its +current position. + +Params: 2 +### Draw absolute (DA) + +``` +DAx,y(,x,y..) +``` + +Lower the pen and draw a line from the current position to the position +specified by the x,y coordinates. Continues + +Params: arb. + +==??Draw relative (DR)??== + +DRx,y(,x,y)* + +lower pen and draw line from current position to an x,y offset. Multiple +offsets may be specified in a single command. + +Params: arb. + +==Circle (CA)== + +CAr,x,y + +lower pen and draw a circle of radius r centered at x,y. + +Params: 3 + +==Letter size (LS)== + +LSs + +sets the font size to s + +==Letter rotation (LR)== + +LRtheta + +draw following text rotated by theta +in degrees, clockwise + +==Print letters (PL)== + +PLtext + +lower pen and draw specified text at current position + +==Pen select (PS)== + +PSi + +select pen i (where i is in 1-4) + + +LT - Line type - + 1 solid + 2 ?? + 3 ?? + 4 ?? + 5 ?? param + 6 ?? + +XT - ? xticks ? -- 5 params +YT - ? yticks ? -- 5 params +CH - ??? -- no params +PM - Point Marking -- 1 param +PS - pen select +PV - ??? - 1 param +WD - window - 4 params +AC - arc - 5 params +LI - ??? - no params/arb ??? +IM - ??? - 2 params +PK - ??? - no params/arb +RS - ??? - ?0x80 as params? +VP - Viewport - 4 params +UL - ??? - no params/arb ? 1 param 0-9? +SP - ??? - 1 param ?? +LF - ??? - 1 param ? 0-9 +SL - ??? - 1 param -- Slanted lettering (by %?) + diff --git a/moore.py b/moore.py new file mode 100755 index 0000000..cc29928 --- /dev/null +++ b/moore.py @@ -0,0 +1,54 @@ +#!/usr/bin/python3 +from itertools import chain +from math import sin,cos,pi +import apple410 + +axiom = list("LFL+F+LFL") + +rules = { 'L' : list("-RF+LFL+FR-"), + 'R' : list("+LF-RFR-FL+") } + +def L_repl(c): + global rules + r = rules.get(c) + if r: + return r + return [c] + +def L_iter(system): + a = map(L_repl,system) + return list(chain.from_iterable(a)) + +def L_sys(system,depth): + l = axiom + for i in range(depth): + l = L_iter(l) + return l + +seq=L_sys(axiom,5) + +direction = 0.0 +theta = (pi/2) +distance = 16 +location = (452.0,451.0) + +def move(pos,angle,distance): + nx = pos[0] + sin(angle)*distance + ny = pos[1] + cos(angle)*distance + return (nx,ny) + +a = apple410.Apple410('/dev/ttyUSB0') +a.pen_select(2) +a.move_to(location) + +for e in seq: + if e == 'F': + nl = move(location, direction, distance) + a.draw_to(nl) + print("DA {}".format(nl)) + location = nl + elif e == '+': + direction = direction + theta + elif e == '-': + direction = direction - theta + diff --git a/plot_scripts/self_text.scr b/plot_scripts/self_text.scr new file mode 100644 index 0000000..e64404d --- /dev/null +++ b/plot_scripts/self_text.scr @@ -0,0 +1,105 @@ +MA0,0 +DA2394,0,2394,1700,0,1700,0,0 +PS2 +WD0,0,150,140 +LS4 +MA7,6.6 +PLApple +LS2 +PS3 +MA20,19.2 +LR30 +PLApple +PS4 +MA30,28.8 +LR60 +PLApple +PS1 +MA40,38.4 +LR90 +PLApple +PS2 +MA50,48 +LR120 +PLApple +PS3 +MA60,57.6 +LR150 +PLApple +PS4 +MA70,67.2 +LR180 +PLApple +PS1 +MA80,76.8 +LR210 +PLApple +PS2 +MA90,86.4 +LR240 +PLApple +PS3 +MA100,96 +LR270 +PLApple +PS4 +MA110,105.6 +LR300 +PLApple +PS1 +MA120,115.2 +LR330 +PLApple +LS4 +PS2 +MA127,121.8 +LR360 +PLApple +VP1500,250,2300,1050 +WD0,0,100,100 +PS1 +CA50,50,50 +CA10,50,50 +PS2 +CA25,75,50 +CA25,25,50 +AC25,0,180,50,75 +AC25,180,360,50,25 +PS3 +CA15,85,50 +CA15,50,15 +CA15,15,50 +CA15,50,85 +PS4 +CA10,90,50 +CA10,50,10 +CA10,10,50 +CA 10,50,90 +VP0,821,1197,1700 +WD0,0,140,100 +MA10,50 +XT3,120,12,2,2 +MA10,10 +YT1,80,8,4,0 +PS1 +LT5,10 +MA20,60 +DA30,90,40,20,50,20,60,10,70,80 +LT6 +PS2 +DA80,90,90,40,100,60,120,60,130,70 +VP900,100,1400,600 +WD0,0,100,100 +PS3 +LT1 +MA0,0 +DA0,100,10,100,0,0,20,100,30,100,0,0,40,100,50,100,0,0 +DA60,100,70,100,0,0,80,100,90,100,0,0,100,100,100,90 +DA0,0,100,80,100,70,0,0,100,60,100,50,0,0,100,40,100,30 +DA0,0,100,20,100,10,0,0,100,0 +VP0,0,2394,1759 +WD0,0,2394,1759 +MA1550,110 +LS30 +PLApple Color Plotter Test +CH diff --git a/plot_to_svg.py b/plot_to_svg.py new file mode 100755 index 0000000..27bd77d --- /dev/null +++ b/plot_to_svg.py @@ -0,0 +1,112 @@ +#!/usr/bin/python3 +import sys +import svgwrite +import math + +d = svgwrite.Drawing(profile='tiny') + + +def getnum(line): + pass + +def coordlist(line,expected=-1): + l = list(map(float,line.split(','))) + if expected > -1: + assert len(l) == expected + return l + +pens = [ + svgwrite.rgb(0, 0, 0, '%'), + svgwrite.rgb(100, 0, 0, '%'), + svgwrite.rgb(0, 100, 0, '%'), + svgwrite.rgb(0, 0, 100, '%') ] + +pennum = 0 +pos = (0.0,0.0) +text_rotation = 0.0 +text_size = 1 +# "viewport"? part of the page to plot on +vp = (0.0,0.0,2394,1759) +# virtual size for viewport +wd = (0.0,0.0,2394,1759) + +def tcoord(x, vpl, vph, wdl, wdh): + vpw = vph-vpl + wdw = wdh-wdl + nx = vpw*(x/wdw) + return nx + vpl + +def transform1(x): + vpw = vp[2]-vp[0] + wdw = wd[2]-wd[0] + nx = vpw*(x/wdw) + return nx + +def transform(x,y): + nx = tcoord(x,vp[0],vp[2],wd[0],wd[2]) + ny = tcoord(y,vp[1],vp[3],wd[1],wd[3]) + return nx, ny + +for line in sys.stdin.readlines(): + line = line.strip() + cmd = line[0:2] + line = line[2:].strip() + if cmd == 'MA': + l=coordlist(line,2) + pos=transform(l[0],l[1]) + elif cmd == 'VP': #viewport? + l=coordlist(line,4) + vp=(l[0],l[1],l[2],l[3]) + elif cmd == 'WD': + l=coordlist(line,4) + wd=(l[0],l[1],l[2],l[3]) + elif cmd == 'DA': + l=coordlist(line) + while len(l) > 0: + nextp = transform(l[0],l[1]) + l = l[2:] + d.add(d.line(pos,nextp,stroke=pens[pennum])) + pos = nextp + elif cmd == 'CA': #circle + l=coordlist(line) + r=transform1(l[0]) + p=transform(l[1],l[2]) + c=d.circle(center=p,r=r) + c.fill(opacity=0) + c.stroke(color=pens[pennum],opacity=100,width=2) + d.add(c) + elif cmd == 'AC': #arc + l=coordlist(line) + r=transform1(l[0]) + p=transform(l[3],l[4]) + t2, t1 = math.radians(l[1]),math.radians(l[2]) + x1, y1 = p[0] + (r*math.cos(t1)), p[1] + (r*math.sin(t1)) + x2, y2 = p[0] + (r*math.cos(t2)), p[1] + (r*math.sin(t2)) + ds="M {} {} A {} {} 0 0 0 {} {}".format(x1,y1,r,r,x2,y2) + path=d.path( + d=ds, + stroke=pens[pennum]) + path.fill(opacity=0) + d.add(path) + elif cmd == 'LR': + text_rotation = float(line) + elif cmd == 'LS': + text_size = float(line) + elif cmd == 'PS': + pennum = int(line) - 1 + #print("Switching to pen {}".format(pennum)) + elif cmd == 'PL': + t = d.text(line,insert=pos) + t.rotate(text_rotation,center=pos) + d.add(t) + pass + else: + #print("Command {}".format(cmd)) + pass + +d.write(sys.stdout) + + + + + diff --git a/roms/B9801YL.bin b/roms/B9801YL.bin new file mode 100644 index 0000000000000000000000000000000000000000..3fb0127ad32bcc9b087dc320802255fa09d06be5 GIT binary patch literal 8192 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 IfC!NP1A7&C;Q#;t literal 0 HcmV?d00001 diff --git a/roms/B9801YN.bin b/roms/B9801YN.bin new file mode 100644 index 0000000000000000000000000000000000000000..37c10a92e2c169343df3cc782b9ca6e3ffc6138c GIT binary patch literal 8192 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 jJ#)=OU?u`H5txa7%fHggm0FokkdI84 zQxfIEL^(fEercvWBVTSPlxquRN4{*ymmkfSf0{4T*~Luxb7eQ*u3*AA7p=S9)2+=@0oSyt#2wbzh5~2^rRPXXlUah}*Yo@uWXJ(N0dBII){$ zK@OPZTk>VwGT)HzYo8Dk_wm=>A+OgX`M*-eq0f{&u8)*yh(|;I9`_YF%Vj=o9bNO3 zm6&+4R|C(W^pgLW-bv}vys^8CJi)p5n5%jRgb`Pl7o2kH%=TkuX{z~+6K6To3uJk`sfSEkz{H8aCOf#Z z4{1&QF3aZaFDUC0xFn*{D4j3%eN3V>#Hdjaq_16;*ncQQilg~>R?Wv=jZzkJnE0Lm zY1ELpTEDY&w#~|&Q+lj1vh_okHCDE!%Vi;}D3?W8eJ=7*jH17e%;bBlcRH;*oMpWA zF6V@ZR}1`s6Sr!}@&#mGK51M)zTn8Kxui_%*Vmkw$rG)Xh+0M9xa471@<*;t>t9^f z^DgTrF6*Z*;TkY=q?#;F9ADf7=RkjH8NEF$xaEBVhZC_UmLK^@*k z*%~KXGh}O~tT&UT@l;y0lf1zxWUIAUFGQ~=J2|q;dRmC?2Bh?mTCKvBMmw$CM~Yrc z)@l{Rqt96S6r!CDF|zB(E@IOvtvOGbE^}xl6tY8mxqDIH;sBZsQc}T!cK;DziWK2_ zE#9l6d3|I|Zls&HSYp3WdXbg{wqJ^4${V_K+BdZto%*kI#N`mDm)_}g1WrWp{-g+xNkLU z9Z{x8_l;%^Xtw&nSsg*EFL3sl&hITw_*w{RhXh_1TvR;iYas+ULN{lTe$`L|H{c`h zab8%ANs%B8Sz<@qP&X7ZD;m`^dJ?hZlUEB+W%)TAif54D zNMB@-LxBlzMkYTh36Wg#BNF+VD>cfEdP)^8xO^x;ukn(L*Kh;9B%hZ&@uMpql}0NW zr|>q5I3*kr$~5RXO0z&7B%#}Ax_*#7X-YqhR;pTB%ngn6&G@5WWxcJmEM$uKtjGE@ zbnB>d<9E6y8!R2aH-Faty7_N|mRO%W$G~0pY)H?wUNGAc zM+m%Q+5Lj~E7RZ5I-|{q=EyQbndTMqm+0A9-GNiZMxn|0EZiW%9gLy5J@?JD*gKB4 zn!ngPWtHXCK2zMdBt`!fx!IsKk14%nW~4y27?^f=Pc@H5YuS5DGI`0L3WVnkcD=qfURH{!T~H>JSRiXAlNDGX+>sgOe{uor$27 z21~U?+o$sldMQM!)S#Pl7Y6|N1AiRk{AW*bUelmZ8%1;2<>&69&zP=(iJ6AKuR$ z(+aC5dbN!q3qV)^T-}NV8bf_WQFh_{LXDxRu42cIZ*QV#E9KAEOscQ2g0*D6RimqG z$I) zJ*vJU4;Cz8$yKC_B%Z zr)rviwHc?cYC_IoVi?MbBxriM6Q#()tYq^vlPjq)*?$T{H5F!Q0AVf2E-C9JBLl_Rs0&p2JWXWXO6bTUBLWO;wo%80Vvq_9%_qfEmEctHC2ifxLbZG> zwh9OXtIAD%f-;8rAqB1xzpAN3Eeu+%D8m)KRlI6MNX^rPt80-BwU$aln)F5gK_~?w zbR47RnkpE^XbNlWp4WR z_Pg)ibeDPMrk!_dG?g_Pj_6`Bf*2BNqlfN54>+_LkKHfy4c$xwtLUSFKC1B;Jz{!) zne*pz99J*($Xf1o=@_1mNiWM0+%f4Dnddapt9W)eU&GVsd>zz3NKQQ6(pz}?`|?mV9A15IvWc<=)c zX}}{D;I~jb8~xGFj(hx(DcrgDCo}lJ2n)w!jGZzc%Z(lpPRAG@P(jti3MJ-;iG4A~ z$}mwAqX?by1mywwYDzEkI$!E@1tZKeJE4?MSS`ElW`h@s`>3JlXf+)pqLwqQgr;~gUOX7IJ&4PcRPmH9$h-G7FYE(9& zX6zY8%5F8bK7aI>SMmBr<$b}I#9X=>@T8YV5wAp09D~sIAHPA=3@U*duPJb*n zJ4QyAj#w-f^WUmvAHbdx4)am4T*dua?t_^vf^ z0kYKSN7lqSD{{-du49R>&^Uq%_E61ah z`{?py_dyy=Aws-DF2p(9jTpdQtR5Dft;2hYlv4g;V7T}#W4vD}jh5Yia=+vLD@K6v zezyZpnl{5{5C%MoU8ONFwHOHvF;Gx zzbD=|KJ(f!_jRI!6$J-h-7ddB+)}EV4=1q^^p$MPSDDMi}=aN^Z zy-Op`5VSO6k*H;ai17d=({%=I_tjJu=tEqE>k5X7`Yfv~$3?BhV)BD)c^J9Ma`OnB zc?3j8`-IUtM%SEmO^$bOH%Cm4A0fjDBknCCrl|q4AW>?Sy#3E{m|_mckeSmlMWhup z&|@8S`QiA}1A~N581xrUZhc;y>R)UgS>#asWxS(E@%JbrWCJf&bOp2e@1IWRlQ0nY z`g=IJa(XZuCY(Tk5j^9ZS5xF~PbUM@eZ3=-TOFcS&qaqwA@3XXNWBA4&m*CnW)M>% zqR;{oBXadowtAEcR)0`8C_G?fbs3`d@l$SSK3?T|&(Tq;ZO)6f{+elOXU;7}yL zhtjYL_YCLWV%EDGi~6vhaYI6pGO&Id8d)!!@Pb!y+ns1I@3ibtd;t))u&zTCg1k7% zeUn))s7>l?34Zr>QGeBH2Z$fLzis}&eYg32ay-^=`j}1sxbAM@Sc1D%)Ceyp(0*nP zf94rF$yT`DfQ1jcw^;)V`6UIb2ex|ELd6EOby0K?VEs1Jmnwl-G1q`t6NF%PUofjL zIIk~gRybjPBCq2{pI4?%(kv|HHr&<)SD}^ABZ2Nz?QH&Jysj=tREkWzBY>qwzX2sk zIFAJPDvN+`4Zz{c z6;B^9X3P;-613y%hxy`oPX|U*3T4oo#Y*Y&1;&NpNJ64Shepr=eP;}Vp$Oo33bPo+ zbEx)AqRK&omuVhUCQQtmt`{OHKmS6MFqr}l@;P^vxtJW7Az-wH)9tW2r7L138pl{4 z7CXA4SMmq7#bSVRjM0?*Sm%rH9iZVLOBwzDJtzR$jo-r=inYX#)yxZ=Vzgdk4S|ML z)qQN_^)Y*1a2e|*9)rarilAbCl|sj2jK_4&csMNNG=`=M3LM4F@g501F;>e4lX?e6 zYafwD*RbUqkJS2n-;ChGzF=uz?|{AZLzkmf)CRjnv-{1UL5vA%#Mt0eF)27nO!Imb za+AfBO_|IH^+2A&0y?i(USc6jEb@t@3B-kDav|9(q2cL6M?WIpl+-(dK@66Sx_Ys0 z1|hCFGid+q72{cAQ+@GXaMBp&vjKlsa3M4)HS_T!ShS*T3th=n{J}~67n6ch>7!e| zn1l=(SB>YmgXnq1xNba14p%6~t>d9ujL{X&2S4`0M-;4?F{je0PBmW2|KwcMnraa+ zAE80jdzh!d^^7qRZ$Mel__{M^XH1m(6?an$o%tklEmR)kzB$n2^l0q`7h0WH`c}7L zNj?}MPHhiHoZyY>G)Ga#!sKMEVtimc7q`)bB8uaDPzzYNm-lqXFkRN)J$w!;{1}Fk zW%K)=;yeRb&Lf=UK|O~9kn0Pm4}|Ock7trS^U2F8q$7n^_P>%11+3r_X(x?BkAd+g z2TQTgh;qGO@}|PZR(|BT)e3i30pj6QxBIo{(H4{7Ta~Bz){ln=+sCjoZ}d z3MV$SlTJbJ!ULxDW*2Kx&1ZbO!4B`I`j147xZBY?EjIhxX%jcPf;cJl9jD~xvATC; zq%dSD43%~`gWL(Ue6+=Lhh>jsWMmjmT9cA*aT?b+;dleS(IZ|D`Mf~bB|+Bm5kKxiUF%EV9>q zT=;r6N?*E2r*wYzo{R1;6&*U+hqz!UW{Ntod`Jrn{($*cAk5>82m>KQ7`UhfKsP(P zHDTa!Z55Mq?(-=s@c83X!pz-QN8i6cn)}D$nACAK)IsH2nnpjy8pJ2Lv<@EbZH>{j~ zzp1i@dzY(Q5jSS4YcVcKVv(Ebjq_(TtuU6)Xj)}#n6cV6ZON>P%IUYws9VNunX#-s zZraSIT5ieAmKEJk&Ro?rt$*fqb==Qpt!OgF&aSIAo}Im-&Nw<--DCg%=(#UI<_fe& zZbQpJE}QALLJayZ+BdJVjuo1PnGyyW;b5J)$UwTxrcWq#Q5buv&g=+d5eaKFkaCJ6 zQ+R*wEZXh2af7sV2H_N^rWEEl%hBd@$Slm2(4zwU*eqBmNN4thn6UtW=oslx!Z;7u z|A_~(G5kA)R!qULN(a468esfO3>YxcbkT3fST|v+VC)2#6CSsyN!oY}vPlJx!Htim ztR_eo8W2}T#uFvhB%RnH@O}E8)$3ptehN3%AoMo?6G?-H@t~G%^1w9raO6hhP@q=#jURo07aysS|BLUE?0Qu>JG=*xUevRGEGjgO~` z0Rv==qvMKR^120G4fZD=m3n;x)uqRzBX;tYKz1ghukm;#^zi85U5bD%N4Q@tivz4I zFiadEvywe@5>}%(7hlTMukUp=e;v(b6eN_DJu>$IobtpYeQA-%MauQH-3D zJY#$Oj4iFJrWN(QA?h&)ke4J|Fwr6d7K=ksi9b4Rd1J|GO8_{30=F(Hi845pkr~5t z?K6hYMPkbFaB`p*n=DaLph$i)O(Wl({5t;qI{As@qxjK(UL`(09%@1O&SZ~5!JV8Z zK3>5vFEAr8JXepoNB_SkUp}X69P~4}biZw03P!(gVT2txf!!TXycGMcBHY02x>j{K z;d?0bbSx`jSgR7g6(;-ugiJ<2@!UTVfUgJ#CgRYcw(tWc5T?BcZvH(MfPqoyAd)*Q z6h$du!%Gpaqa1JmmJpMrCPO7Nt*%au$|xB7en)uB%|y|`KRkoU3*%XOVLVer>GB1J zW2g>s{37k^>YZl+++;#Gv*lFztF;(scU#8j~QjN2ss7c}5C-YM}86}nyG zpP-NedC(oqaM$m!9H|(@P%)@5&<0f&*+JxSHiRHxf(1&l`O9y@=9(ykp0F{;5m@DP z^1pfZSv2PC%?}BOQ!w;4MGgtirqJ;?Owmjgp+gkNhj~$!Po+>imH~l5xuP)YQpwtR z7S*ai93V#2<=}iHF5si@y<%h%bsx zv0oezKM+U6FGPJtOvcQN^o-n$;*83Sx{T{HHe}q9u`6SL#)BC@&N!U$R7Ow6%NcKE z^ktBYKV^h?gAjpslMC^pkj8HnHirx2U#J|au_*1h{Srs(wPTkhj) zgz^w?PZ1;2>|o>gij?(xH&lf9d$ZP?dClf6HDaW`I|R105Pv9jOI78!5Hh5uS5?wf zwCVe8o0Dy68vv&4w^c6<@#a))t()fpA?!!$DF}d(6ApnS@{!`!gL~JTLwtHlO}Tk} zh>uJM=0F734KSO68&fv!0|y`C*ITwm+#BL~(Y$`|{!KaqBxtizH|?iXDhDwvhC`5* zlB1(xMAh)%-M0~uvH%?1Dn@E8w?GX|iX}y>;rSHKEI@KNIHBr&$yy`?>+l+3Ux?Qy z?~fSQY@=-{UrudwOU}~d)auO}_ED)c8DuK82HHY%F3UiZ*HX>75PvZJ3QHO&40$Nc zpxvmY`Q%ao(A?aT!E&nRg-bPP_a6e;oVvceM%;=#q?yaVwY3I}hEy)%P!(u~WF{cR zu#|Fg7DG3vSDTfi=|PjLvHVhjGK(t32AElgx`3Lr!E&gkoTfsvMUzV|r_U5~^?JOy zltVTHA{kO_hisJ1a)>H%$Wl{Iu}rq5yvBesO|IDz;^zt8 zg&5-Zr*BHts`fjS)(v_p^-1yG{R|COvzVgYn37&Y3wU7do`3D{sGO?g>qW*I%X;;u1hYb&lGdjdb~M!kd7K;R!X^!5iAFo zt_kvpZL1@BU$S*S}|9VJzR2wdQ+SH@%j%wmGZgv#ie4?A_01 zcb&VM12R;O&$8C1W_N7L>b#RNT^wf$C!x&_C-K@i0g6x!ew1|+$yz%-yQ4n4^WN;; zuZ?S0!!J#X&{gRSHGGxOjjDJcRKrK4C0E)-Lj->xjD1zGDs}ULeeg=4P({jX`1_J8 zY3N|ip{>=nl#R9uGa4saf;DRG((2OqEoo@uHr}!nI8oSqmDASnQ7IXjlcM--DffT( z)@>8jy@p?wl39D}GDP46J!*gf(6y{vMcT^IRtluGmacW>kPCz+W#c{?jaFR#yOwR4 zQU4P4P%4c{MQ%kCepSVw$=+I73zr>LwBlY~|l=}{D zxi(+bDO+mNs}bQ^mQ5mSTjA|tS=S&qwf=;Cb|P*#J^sEFq;g+4;x%fksa<2(IrC`! z9&hU-{#lc-lyD7ifSK>-qfpche3a#WR4C!7b$q=~c*$z?$!zIQc-3nBSOqbeZMq)A zfYekB0G7||u^y9z(TwEBBt;mc3x=dBmDqVnlT7Goqv@G`b5 zk&u8oj1)f2cq3QXp2lVHf6kyIba9#wPvf)KVbj5Do{U^$69mUt4=Ozhc58AYdX&sb z-JLHg8*WoZ*G%drtLEbX8#}(Zo2KfuAUH87B|!^LcD|eAcQHjIFo%*X-egB(n6 zB+{Of8=*HF{Tbm2O=Rx4OuFUZBY&Fjc^5mRR3lGOA0`Eee_6$u=#~#fgo$i7NZkw? z1;v5QT+qF04XPr`Yia}&&Q^c-eQ1DKx-0ve?Ak>PGOA;Ema6MrQ$VO`0?Iq!~;XL6Ww zMUc@4wMA|Gi0Q%%yQ(tsiMiOo2#Q42I+yN!`7B*b^8=I%alEHT*lVP3iGtELzM!s| z3Yy&Tuo%Y{uA{|4{B%i@4ze}E3zG4G%BSc#Q-q!`bxMt+YkH;jV{LPbwSB#&S>{M= zKGGHU-qG4#i58;-k5Y%on1!k{5X|-I1jXy?#In7ke?#t|cXu!%S35|)o157S{_b2a zy)JN>E@5scP#}X&2FSCy@Y6pq7Y0ms`{~C0A|3%$v}bb%6)1$gF--K8K|BU=TG=TH z)41eb2}^yLic?A0S9n~juJqIB7@BijHy40E(c$>H+yNebAC-!F>ETg5qEg9g^Vlp5 zbpd*-b1^mr0|GNG6@fN@=9al|v{Ri?!Fm1g31LMZo4fj8056?(BTGB;?#|nlwv?@)-zN*SldX7SO%$g)!0!dGZf_-jfX1n))a;|-$^(@ZOvXN-LaBSfR~gDo*-&|a#sEAI4WKS1 znNQxZcf-cYP&Vk`gd7?_lO|=;R_wP`hHl0tDV9lSbZ>5E&#|MThBJMkppLcv zZmiInm6hdg?O$vqC43ntYKskvm6w$+U@v2_l;JL(EVV9H9(}&Aj6b3rKm7{a&WF|T zOdh+X+SbdMMS8qSCbxb-Z8$tdPHZT1-9F=W&(d~8oTTE5oIUhJywc0Je+#D%q|?J6 zDC&NsqP&hHoH**D#q9%JKDej@j30f|AIIGo^?{zgPe)I}U5YE=5A-)i(QreI`W~lG zn)D>&e^RgVKRu=0K>6AY4={NT=-Lgv12^EnK30_kjdZ7e6`7H*=vPHg8m-jfxUNQ0 zVP3j{Y|K|OHBz!pN~=uPNNa6m4-h&Dux%|Ravj)QMU^$C+LB=J(G>=#g}o;^l@4rP zMav1HCxo{<5fJmk35{c?l=S>0JWJ zqo^daPH#iR(OL~9Vf%^E=r-j?`PimZgg&{;;lv?v__BNV{H^& zOU&i{{wtdp9BimttaP zLJ|6}CVp})alp7K91oirrG>fL(MoI>sK6Eq6a%wu9JtMi?F1tWE;%i1aT;%Rx!9jlwMmh*Hg#e=<+ zi-O=Z!pgrX5lX3j2Zgy#6b}s-(J*4QXj3xFO@ipl-@yFKRzrO1k(uK?pBD!c@I<(Aen5iAVGvo?*mi` zVb*1omBk2`sm%WDw)GcnmNt6vj$B&B040Zqc3Op5boodv}^sqa<*pp2BryhKJY9{!*pL z2TGI)+}Kkk?qs*o9VoOSUQh4t_jMM$I-P{?8RQJBdB`7D59jbW?H=?tl^G-pv5 zwQq;><^Bg3vI^P%@Iv@4{O!htq=~Kwzs2}>Vjof+4Z>}AO$6ACN$6uNolcC`F|_If zHN5Z+0uYdp$-7%!5evPLr{Z?ecAT~w=`;;h#V~0nBSBe4Vp%Eebh_>2{)I@JsgE~P zJ#(+KYY__jnl6W)#BkkSD&bFYANR5xqHRK+!s!6`FQsDiKG!)~D>%_{Vi4nMT8O?A z&0ycaphQQu(n5u5j!`T%1ll#$C9%OVaMV&NPN3ZrHmm#!j4MC9kd3`Vz2sD3;Otq> zFP-is#S5iY9OFUgttdV-)j7Jx;zTb@+csQxQ-(5caSq*~9onoTe_7Z&ICO`8XmcdM z_1ir93ObZnxfb`I%=*!#7jcvTg%Q5!`~T4<_z%ogEiuu;3{!$ zuu?P!ufv&4o0uG|5G}zbG3DhYv(HX2#I8f~58?6GQ{R=txyi(9}S4f7{vRggcH7Yhl7 z0iXyAd5Yt3s5}bc(WPgeET8T=P;S!jeU!@AawM-pz1WH~rWoj>fxU-Q@4Ko9bO<<5 zs2Gn=MmvWAQlyh5)JMK37vAD<)mSsL&D1+P4-7{_A38OgZA07DL~z_rF85BbNtqZ!O&+FULV+k+Q5lWh5s!C)oaOT4pktfyPUswZL5IEU^pDb z=A4L4r9X9zbn<6R#KwjHpC$igT(TUNjQczaf0FBkB_-HY;osm)mwZArZlvQ3J1%+) z$p+ZTt596mW&UymLs}@3az%u>dPK&B0!NX*7KeGnbxXs#qr?X=SoDn6Vm8S-#WMcn zZA+QmNSw`9KSFbWJId%c%R(m)EJfyZPEbZR+=d>s(>m38=ED9Dkc_<0H92$LNiNW9usiqXwBH{F3^?`fGe3Qws`?;Y3t&?WBWGOW?Rec!NID1wi@n!TRHcj zt(1Gzwt)M&Er&Z9F$upp{3-5v?lJBV*TLP%ZR0j@H*u@E66xdJX9cb9W9rKVQQ%(1~${`gxP{hmOX;U!Zfd#j$Pv<{cMKC-nAFM0donNMx& z9Bp$O+vl#H<$BlF>bMlNwdL5`a~g5g9|Ir4SM>*N?p8juN7V3Kcac)DTxktHOFymx;3IQuuxOfe@_;=vPqNi#=13#k$zl6Qa*niqTXLRMnl9N( z=SxqId8;x-X$177k<3iVwj1o<}s8>^sSQgXHgHX&X5-7cg;lOr!w`>60_ zk!qDG0Sy${T0L@}ssz}ZbaF$r1{g>KXB0RQ@8mtz7O>-#t6iR5?Q4H7Kpw@!=Fw`z z@Lzg_D&$o<9T(w&@Iwo}8$rvWJ!f@C&bs)HqnJ3+#yvLBG54fg?Tb1uk!S4w#jaPX z6>8CCurSk@TGVp2)-X^#GY0g0#!i}H&rFFPl4c-O%K-xmGpd?Hlz17JxTW23Dci2O zp{Y*KK|M%GLS9ZTv1`B;1u@7VtQomozM-lt76<=3?Pu<=le_IFx7ynd&=Q#m4OiL~ zpyaLg__jx={S|k-JC0896j&e8Q2^^Y6ImHCMNtxtd0#?T`LNwma0F zL#lDw93Ck!5efMlLBUc>!SR^+awO!P`XaO*fos6VBgy&WPH9b-TCMXXrS-YiM0^9| zSJkv&o9*kj`Op5O8t2N}6Hkx)<0sW43Oz9Whh^v%wadIC=X-sb1yZM@qyOQW#XoFp zces99GiB=3v1t92wnxTFmhE1A>A9L7d)q^hu+y@bJh`mZ(eJD&oM&lUJoZsdn*^UD z&n-LOIk48z@2&BiFPv{dSdu@i@p^0Zyhka_wX{(QT zUU}uR@vQWaNS2q5A2ya2N)CJf`ejqwPU(@0ofcK)oy(vyQiT@n()p3|(6O+}QkVsy zj!QoPF=vq_IBD#$WpC^8K@FM%s(W9d*|J!Ea@pc91HPU#tz$d)5ilbG^GP ze)+j&D8g1pTKCv{%hI^{TylZb=IEbSi#u<2p&-I1+{L?h_5XcY*(fv@Jp6g#6gF0t z_b*(2a^Z5XdxtA-IqVs?JZL&|=W^0s4RwN}CvI7ec$Do%O3hh*xjSdk_KM{z@WG42 zc6nYcRyQn`^QzKXCJXgS>| zfRI+V*)4WOx^o_iG*5%Tu7}rIx_+|S()C#5(PLsnTO{2<@FC`{%iW#*uP?`1uKuGDEimMqBLqcsZF@E0n5q z>~{zi62^V5)LOkEnghNo5`U|QFFwDk9Vh54I#*1s99mwT)sVF+>tIg8FANGI3!(4uwZcXj=b)>;sn?YgfP!^z-0S5GYlMuieX_WxTgIa=o`uL}(Lg1P27 z?sjnoBZ#K1U|un9ZWK=n=Jvl@>-YOj-}ZX^COshenY!S7*O#^JjP}U;4znbt&F(*3N3Xc8uRAplxJD9Nf97yq058v(PR0^wKX~xo zgPwa=w>ew~>e_P_t%e@=N*-T8DCbad4s1ua>nCs5d6nHi+}@eM{bUR7#wKv`0SKPg zhwE#7Q02^)(*Ejq;q;A;L${dMT<>lqA+aw4N-d|&|8VJujO6fTGJP}B!26**7kDraI8e&64JMOT*b*Nv{H z8+%bs{JLql3-BK7Mgcy@{WR(qcc097?C-l4JhA)zwZHB@jkuN13~=vHdvp(e9N;~= z6^A-rFLP>sU-tOE&$KUcwZ=bcN|7}XS(ZoZs51U-Tg;i-m{i_kSo(2=U%-{ z=}LY2*$_=zFxQiNs>-?_58%vxNd1lyDbUNFq@%%~ zs~56qB!6y%`%6@yu?DPcq1yO_IvQVSsweGLXoLZ-yaHZ-1-yxYw5I@p#?TAjbCCbun7=KNNeL>9W1ubjjXfx_~r~*3$+aEjQ*u&(*U^ zd!m}&Z#vx2=WaEWncQEx+i-8PuR*ui9Iz{wLs*kDh+pCwf*YU8TJpGvdwvG@@v_8+ z#@=k8Z00e8i2Q)DjI%ecUgx$e4z)9-y193`-fVF6zu9ogbm@zRQCq-Wf<@Pvi_Ks7 z=^Vjr1caziq<5eMnWC!vge&@TNnfjb7vzTAONt|f_4Qbb(+gYc7wPxVj}K8sY6jSg z8L&*_^#M;#RI?nlSLxixVV=e2kMQ(w>2~iBC*#gylEeL`-QA`D3_!ZSs4N79%a)iv za_=&qcgSxxxLZ-2T14(chM6LLy#t7i4C-p!t@;p7vXRMELNP4~A&VX?@t|7}I#~gt zbZ@N^a%8%oDCDxjf=cAxMpoEDH!XZ&e?3+65EDS#OqypZ3GyDO$4Bj`2Gmb}&Y36U z@~M&#Gv|!#YJzu7eO!#CloQphkBj=UuH%=6n^YZIgS=TgUt^kes_W9Srm~3noM=?d z>zyU{x^WLBN-#YL-k31)b2&~Ngf}bs>W1d=s*wM!uya0w8@c?lB#)@nx)5C zE}v902EM1S$ppHZD%@BRJy z-%sClazQg#)0=B`{WZ-%b^!;(qGma!+3UZ$S!Zqftu6gqn=P|lH#Vyc`tD}6L7&~f zshQl-;?lLKt@%u}=82Zs*EZ#cn&CyJ%@a-erWQ2i&So^_x)uxC@l7q{E-K=xcKpxH zj8u2^$a3daURvYp6n*XH{U)y3q3%<66w&2MvXu;REG?Ei7 zXV$g^@ba8l*kZZ5`L?zEhs}2i?O55Gha&_DNNDKL8%S79KUyDM1 zkL1vfk3G_IRone)%YW8(zuJNnj#AapaNpI!8g4o-oU}rn7e=qRGB5n7CD$;`teb)+ zrvP(3^NMR`h?W)o<`w_R46)tebayBUA}w5j*`tCPUJ*V*fKLMM$EOYdRDczZ7#SBG z`5WnR5u6a~CG5L}3+M;t%m#_Pts^Nbd+}KWT(tO5!ku~_aj)?9k^|glxa?2A7(u@y zu|H0dZ(6~kif`Z>g}I-*?cx-7hPTygM9ieGCurCF-eC5R!c0BzKOkY+YsPXeBn<=Q~C-Y-uQyTDJwBA zZhBYSiz^Hgoo^vSgX)*g=nWx`*EdBseY279KeA-|O`_je;2R5kV}Wlh@Qnq&vA{PL P_{IX?Sm6KJ0tfy#GPyMT literal 0 HcmV?d00001 diff --git a/roms/test_extractor.py b/roms/test_extractor.py new file mode 100755 index 0000000..2c82ea4 --- /dev/null +++ b/roms/test_extractor.py @@ -0,0 +1,20 @@ +#!/usr/bin/python3 +import sys + +st_start = 0x94b + +assert len(sys.stdin.buffer.read(st_start)) == st_start + +while 1: + cmdlen = ord(sys.stdin.buffer.read(1)) + if cmdlen == 0: + break + cmd = sys.stdin.buffer.read(cmdlen) + if cmd[-1] != 3: + print("Invalid entry") + break + else: + print(cmd[0:-1].decode('ascii')) + + + diff --git a/send_to_plotter.py b/send_to_plotter.py new file mode 100755 index 0000000..ea65809 --- /dev/null +++ b/send_to_plotter.py @@ -0,0 +1,9 @@ +#!/usr/bin/python3 +import serial + +print("opening port") +s = serial.Serial("/dev/ttyUSB0", 9600, timeout=1) +print("opened") +s.write(b'PS4\x03') +print("Sent pen select\n") +print("recv {}\n".format(s.read(100)))