|
|
Joined: Feb 2014
Posts: 1,237 Likes: 224
Very Senior Member
|
|
Very Senior Member
Joined: Feb 2014
Posts: 1,237 Likes: 224 |
In searching for info about the supersketch I came across the VersaWriter which was a similar device that used an arm with two sections that would use potentiometer analog inputs to calculate the position of the tip. These were used by Roberta Williams and Jordan Mechner in making their games. https://www.filfre.net/2011/10/mystery-house-part-1/https://www.museumofplay.org/blog/c...on-documents-revolution-in-game-graphicsSome nice pictures here of the apple and atari versions: https://atariage.com/forums/topic/293821-i-have-a-versawriter-i-am-trying-to-evaluate/And an ad for the pc version: ![[Linked Image from i.imgur.com]](https://i.imgur.com/fYvrHjf.png) They had these for the Apple 2, atari, pc (that I know of). I was able to find a disk image for the Atari 800 and set out to figure how it worked. Let's start "vestigating" If I run the atari disk image under the 800xl driver it will hang up while trying to read the controller ports. If I hit joystick right and left quickly that will "unstick" it so that gives us some hints. ![[Linked Image from i.imgur.com]](https://i.imgur.com/aSXfGfL.png) ![[Linked Image from i.imgur.com]](https://i.imgur.com/KaDw5cV.png) Analyzing it, it appears that this is reading a potentiometer input by writing to $d300 a strobe with 30, choosing the axis to read with 20 or 10, then hitting the strobe again with 30. It uses the X and Y registers to hold a 16bit value, counts until the $d300 input goes low, then returns the count in 4ffe/4fff. Putting them side by side, the x axis gets read with 30,20,30 and then anding $d300 with $40, exiting when bit 6 clears. The y axis gets read with 30,10,30 and then anding $d300 with $80, exiting when bit 7 clears. Why they didn't use the potentiometer input on the atari is a bit baffling. It's got paddle inputs so why not use them? add some various bits to a400_state: m_mypot0(*this, "myanalog_0"),
m_mypot1(*this, "myanalog_1")
DECLARE_READ8_MEMBER(a800xl_d300_read);
DECLARE_WRITE8_MEMBER(a800xl_d300_write);
double m_versatime;
Our d300 read routine:
READ8_MEMBER(a400_state::a800xl_d300_read)
{
if (machine().time().as_double() > m_versatime) return 0;
return 0xff;
}
WRITE8_MEMBER(a400_state::a800xl_d300_write)
{
if (data==0x10) { m_versatime = machine().time().as_double() + attotime::from_nsec(50000).as_double()*m_mypot0->read();}
if (data==0x20) { m_versatime = machine().time().as_double() + attotime::from_nsec(50000).as_double()*m_mypot1->read();}
}
Modify the address map:
void a400_state::a800xl_mem(address_map &map)
{
map(0x0000, 0xcfff).rw(FUNC(a400_state::a800xl_low_r), FUNC(a400_state::a800xl_low_w));
map(0xd000, 0xd0ff).rw(m_gtia, FUNC(gtia_device::read), FUNC(gtia_device::write));
map(0xd100, 0xd1ff).noprw();
map(0xd200, 0xd2ff).rw("pokey", FUNC(pokey_device::read), FUNC(pokey_device::write));
map(0xd300, 0xd3ff).rw(m_pia, FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
// insert our d300 read and write routine
map(0xd300, 0xd300).rw(FUNC(a400_state::a800xl_d300_read), FUNC(a400_state::a800xl_d300_write));
map(0xd400, 0xd4ff).rw(m_antic, FUNC(antic_device::read), FUNC(antic_device::write));
map(0xd500, 0xd7ff).noprw();
map(0xd800, 0xffff).rw(FUNC(a400_state::a800xl_high_r), FUNC(a400_state::a800xl_high_w));
}
add some analog inputs:
PORT_START("myanalog_0")
PORT_BIT(0xff, 0x74, IPT_PADDLE) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0x00,0xe4) PORT_PLAYER(1) PORT_NAME("myanalog_0")
PORT_START("myanalog_1")
PORT_BIT(0xff, 0x74, IPT_PADDLE) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0x00,0xe4) PORT_PLAYER(2) PORT_NAME("myanalog_1")
and now when you get the analog just right, you can see by moving just the x or the y axis that it will "sweep" in a circular motion. The 1 key draws, space to turn off drawing. ![[Linked Image from i.imgur.com]](https://i.imgur.com/HyIA8Kv.png)
|
|
|
|
|
Joined: Feb 2004
Posts: 2,651 Likes: 371
Very Senior Member
|
|
Very Senior Member
Joined: Feb 2004
Posts: 2,651 Likes: 371 |
If you're going to emulate that device, you should have the option for MAME to do the inverse kinematic transform for you so X/Y work intuitively. It's not too difficult in two dimensions.
|
|
|
|
|
Joined: Mar 2006
Posts: 1,082 Likes: 7
Very Senior Member
|
|
Very Senior Member
Joined: Mar 2006
Posts: 1,082 Likes: 7 |
... Why they didn't use the potentiometer input on the atari is a bit baffling. It's got paddle inputs so why not use them? ... My guess is because of the infamous 'pokey bug': As I understand it, the pokey ADC works by slowly charging the analog input pin via an on-die(?) resistor, and incrementing(?) an 8 bit counter until the voltage read from the pin hits a threshold, then stopping the counter, dumping the pin back to ground via a transistor, waiting a bit, then resetting the register to zero(?) and repeating. if you read from the ADC input register while it is still counting up, you will read a bogus/invalid value, and there's no way to tell whether you're reading it in one of the 'bad' states or not. Most games which used the Pokey analog inputs worked around this by reading the ADC 3 or 5 times and 'voting' on which values were valid by majority. The 1983 star wars arcade game famously still does this on its analog inputs even though the programmers got frustrated enough with the pokey analog reading that they switched to using dedicated analog devices ADC chips instead! They left the input voting/smoothing code in there and active by accident! The commodore SID doesn't have this issue, since it uses two separate registers (really a counter and an 8 bit latch) for reading the analog/paddle inputs, and latches the value when the voltage threshold is reached, so the register never reads as invalid garbage. Why Atari didn't do this on the pokey is unknown. It is actually possible that the pokey does have a separate 8-bit adc latch, but due to some state machine screwup elsewhere it sometimes gets stuck in a transparent or otherwise bogus state. The pokey die schematics can be found at http://www.atarimuseum.com/archives/chips/pokey.zip but I have not investigated the way the ADC actually works very deeply; the information above is based mostly on other sources (asking people on IRC, etc). EDIT2: looking at the pot scan circuitry schematics on page C012294-3, it at least seems like my assessment was largely correct: there's an array of 8 hysteresis/level compare circuits for the 8 pot inputs, and an 8x8 array of what seem to be counters storing the current count from each of those inputs, multiplexed using a single 4 or 8 bit counter (4 bits in fast mode, 8 bits in slow mode), and no 'latch' other than the fact that 7/8ths of the time one of the other counters is being incremented.
Last edited by Lord Nightmare; 10/03/19 08:12 PM.
"...in the end, all is crab."
|
|
|
|
|
Joined: Mar 2001
Posts: 17,316 Likes: 280
Very Senior Member
|
|
Very Senior Member
Joined: Mar 2001
Posts: 17,316 Likes: 280 |
Alternatively it's possible they wanted to use the same hardware with the same pots on all systems and the values weren't amenable to the POKEY's ADC hardware.
|
|
|
|
|
Joined: Feb 2014
Posts: 1,237 Likes: 224
Very Senior Member
|
|
Very Senior Member
Joined: Feb 2014
Posts: 1,237 Likes: 224 |
That's an interesting story about the "Pokey bug". I guess hardware has bugs just like software. I was thinking about how awesome the pokey chip was to have 8 channels of paddle inputs that get read in hardware every screen frame, enough so that you could have 4 analog joysticks. I'm guessing that the Atari 5200 must have used the pokey to read the analog joystick controllers since the original 5200 had 4 controller ports. According to a post on atariage: "The XL/XE as well as the 2600 is - without any tricks - capable of reading a twin-axis analog input device with up to five buttons - closest to that in existence is the Touch Tablet with its 3 buttons. Many more digital inputs for the same two ports are possible - cf. 2600 keyboard controllers, Multijoy8 and Multijoy16 - with specially programmed software." === Back to the versawriter, I think I've figured out the trigonometry to do the calculation, but it'd be good to know the exact dimensions of the arms, I'm guessing somewhere between 4 and 5 inches long. I think the arm pivot must be about 1 inch from the top of the image holder. Anybody have a versawriter to do the measurements? Atari Basic is quirky, but I managed to get it to display the contents of the VWCALDA7.VER and VWCALDA8.VER files which are the calibration values to feed to the drawing programs. There are two different sets of calibration values, one for graphics 7 and one for graphics 8, as I guess that the paddle reading takes different timing on the different modes. ![[Linked Image from i.imgur.com]](https://i.imgur.com/jCImKcr.png) The first 4 values in the file are the load address and the length of the file, the load address being $600 and the length being 8 bytes. I try to run the calibration program with my emulated paddles and it can't seem to write to the disk properly, giving me an error #138 (device not ready). ![[Linked Image from i.imgur.com]](https://i.imgur.com/mIDke9B.png) ![[Linked Image from i.imgur.com]](https://i.imgur.com/7tv45JJ.png) It pokes the values into memory at $600 so let's just save them out with the debugger command "save cal.bin,600,8" , and after loading the drawing program with RUN "D:VWSOFTG8.BAS or RUN "D:VW then loading the calibration data with "load cal.bin,600" ![[Linked Image from i.imgur.com]](https://i.imgur.com/MdRE5Z5.png) So far I can make it do this: ![[Linked Image from i.imgur.com]](https://i.imgur.com/AKzdDfQ.png) This is moving up and down and left and right with the mouse. It's not perfect, a little bit wiggly, but we're getting closer. BTW what is the BACKSLASH2 key? That's what the atari driver maps to backspace by default. Somewhere I read that's a key on international keyboards, something about oem_102?. After making do without the backspace key for awhile, I finally mapped it to the "real" backspace key. While searching around for atari keyboard layouts I came across this layout, it's so pretty. One day it'd be awesome if mame could have an onscreen keyboard that looks like this. https://www.keyboard-design.com/layouts/60/Atari-800.html
|
|
|
|
|
Joined: May 2004
Posts: 1,019 Likes: 145
Very Senior Member
|
|
Very Senior Member
Joined: May 2004
Posts: 1,019 Likes: 145 |
BACKSLASH2 is the key to the left of the Z key: ![[Linked Image from kbd-intl.narod.ru]](http://kbd-intl.narod.ru/images/en-intl.png)
|
|
|
|
|
Joined: Mar 2001
Posts: 17,316 Likes: 280
Very Senior Member
|
|
Very Senior Member
Joined: Mar 2001
Posts: 17,316 Likes: 280 |
Right. BACKSLASH2 doesn't exist on US-ANSI keyboards, so mapping things to it is as much of a mistake as my trying to use LWIN.
|
|
|
|
|
Joined: Feb 2014
Posts: 1,237 Likes: 224
Very Senior Member
|
|
Very Senior Member
Joined: Feb 2014
Posts: 1,237 Likes: 224 |
So let's figure out how do get an Atari 800 screendump so we can really study VWCALSFT.BAS. There doesn't seem to be a printer for the Atari 800 driver, so we'll just do LIST and then pause the listing by toggling CTRL+1. A little lua script for screendump:
function ram(adr) return emu.item(manager:machine().devices[":ram"].items["0/m_pointer"]):read(adr) end
function between(c,a,b) return a<=c and c<=b end
function boffset(x,y) return y*40+x end
function scrascii(c) c=c&0x7f
if between(c,0,0x3f) then return c+0x20 end
if c==0x7f then return 32 end
if between(c,0x60,0x7f) then return c end
if between (c,0x40,0x5f) then return string.byte("*") end
end
function scrdump(startcol) startcol = startcol or 2 for y=0,23 do for x=startcol,39 do io.write (string.char(scrascii(ram(boffset(x,y)+ram(88)+ram(89)*256)))) end print () end end
-- a little test of our screendump
emu.keypost([[100 PRINT CHR$(125):FOR I=0 TO 5:PRINT:NEXT I:FOR I=0 TO 255:POKE I+PEEK(88)+PEEK(89)*256,I:NEXT I
RUN
]])
scrdump(0)
With a little bit of stitching we get a nice listing, though CHR$(125) doesn't come through so that was added manually. READY
LOAD "D:VWCALSFT.BAS
READY
LIST
100 GRAPHICS 8:POKE 752,1
110 DIM P8(8),P7(8),FS$(16),IO$(1)
120 N127=127:N947=947
130 LGTHL=8:LGTHH=0:AUX2=0
140 FS$="D:VWPDIV03.VER":AUX1=4:PTGT=7:GOSUB 30500
200 POKE 54018,56:POKE 54016,48:POKE 54018,60
430 POKE 20245,N0:POKE 20254,N0:POKE 20262,N0:POKE 20268,N0:POKE 20303,N0:POKE 20312,N0
440 POKE 20320,N0:POKE 20326,N0
2000 BS=1:POKE 764,255
2100 PRINT CHR$(125);
2105 IF BS>=5 THEN 3000
2110 PRINT "POSITION ARMS AS SHOWN IN DIAGRAM ";BS
2120 PRINT "PADDLE(0)= PADDLE(1)= "
2125 PRINT :PRINT "PRESS SPACE BAR WHEN READY";
2130 BS=BS+1
2140 GRAPHICS 8+32:POKE 752,1:SETCOLOR 2,0,0
2150 GOSUB 6000
2160 P8(BS-2)=P0:P8(BS+2)=P1
2170 GRAPHICS 7+32:POKE 752,1:SETCOLOR 2,0,0
2180 GOSUB 6000
2190 P7(BS-2)=P0:P7(BS+2)=P1
2200 POKE 656,1:POKE 657,12:PRINT P0;" ";:POKE 657,31:PRINT P1;" ";
2210 IF PEEK(764)=255 THEN 2140
2220 POKE 764,255
2300 PRINT CHR$(125):FOR WT=1 TO 130:NEXT WT:GOTO 2100
3000 PRINT CHR$(125);"CALIBRATION IS COMPLETE ":TRAP 5500
3010 A=P8(0):B=P8(1):FLG=0:GOSUB 5000
3020 POKE 1536,SL:POKE 1537,SH:POKE 1538,OL:POKE 1539,OH
3030 A=P8(6):B=P8(7):FLG=1:GOSUB 5000
3050 POKE 1540,SL:POKE 1541,SH:POKE 1542,OL:POKE 1543,OH
3060 FS$="D:VWCALDA8.VER":ADDL=0:ADDH=6:AUX1=8:PTGT=11:GOSUB 30500
3100 A=P7(0):B=P7(1):FLG=0:GOSUB 5000
3120 POKE 1536,SL:POKE 1537,SH:POKE 1538,OL:POKE 1539,OH
3130 A=P7(6):B=P7(7):FLG=1:GOSUB 5000
3150 POKE 1540,SL:POKE 1541,SH:POKE 1542,OL:POKE 1543,OH
3160 FS$="D:VWCALDA7.VER":ADDL=0:ADDH=6:AUX1=8:PTGT=11:GOSUB 30500
3600 TRAP 40000:RUN "D:VW"
3610 END
5000 IF A>=B THEN POP :GOTO 5500
5050 S0=8192/(B-A)
5060 IF FLG=1 THEN O0=S0*B:GOTO 5100
5070 O0=16384+S0*B
5100 SH=INT(S0)
5110 SL=INT((S0-SH)*256+0.5)
5120 OV=INT(O0+0.5)
5130 OH=INT(OV/256)
5140 OL=OV-OH*256
5200 RETURN
5500 GRAPHICS 0:PRINT "ERROR IN ARM PLACEMENT----"
5510 PRINT "PLEASE REVIEW THE INSTRUCTIONS AND"
5520 PRINT "THEN REBOOT THE DISK TO TRY AGAIN."
5530 PRINT :END
6000 W=USR(20224)
6010 P0=PEEK(20478)+256*PEEK(20479)
6020 W=USR(20228)
6030 P1=PEEK(20478)+256*PEEK(20479)
6040 RETURN
30500 FOR X=1 TO LEN(FS$):POKE 1599+X,ASC(FS$(X,X)):NEXT X
30600 POKE 1616,AUX1:POKE 1617,AUX2:POKE 1618,PTGT
30605 POKE 1408,ADDL:POKE 1409,ADDH:POKE 1410,LGTHL:POKE 1411,LGTHH
30610 M=USR(1624):IF PEEK(N947)>N127 THEN 30900
30630 M=USR(1627):IF PEEK(N947)>N127 THEN 30900
30635 M=USR(1630):IF PEEK(N947)>N127 THEN 30900
30640 M=USR(1633):IF PEEK(N947)>N127 THEN 30900
30650 RETURN
30900 TEMP=PEEK(N947):M=USR(1633)
30910 GRAPHICS 0:PRINT "I/O ERROR # ";TEMP:POP
30920 IF TEMP<>167 THEN 30960
30930 PRINT "HAVE YOU LOCKED 'VWCALDA8.VER' AND "
30940 PRINT "'VWCALDA7.VER?"
30950 PRINT "PLEASE UNLOCK AND REBOOT THE DISK":END
30960 PRINT "REBOOT THE DISK WHEN THE PROBLEM"
30970 PRINT "IS RESOLVED."
30980 END
READY
We read the paddles under two different graphics modes, graphic mode 8 and graphic mode 7. The 32 is added to not clear the screen (probably to speeds up the switching). Coming in BS starts at 2, and gets incremented on each pass. The BS-2 and BS+2 indexes put paddle0 values in indexes 0 to 3, and paddle1 values in indexes 4 to 7. Diagram 1 : P8(0)=paddle0 P8(4)=paddle1 Diagram 2 : P8(1)=paddle0 P8(5)=paddle1 Diagram 3 : P8(2)=paddle0 P8(6)=paddle1 Diagram 4 : P8(3)=paddle0 P8(7)=paddle1 The P8 array is filled out while under graphics 8 mode. The P7 array is filled out while under graphics 7 mode. 2140 GRAPHICS 8+32:POKE 752,1:SETCOLOR 2,0,0
2150 GOSUB 6000
2160 P8(BS-2)=P0:P8(BS+2)=P1
2170 GRAPHICS 7+32:POKE 752,1:SETCOLOR 2,0,0
2180 GOSUB 6000
2190 P7(BS-2)=P0:P7(BS+2)=P1 gosub 6000 is our routine to read paddle0 and paddle1, $4f00=20224 to read paddle0 and $4f04=20228 to read paddle1, returning the 16 bit paddle values in $4ffe and $4fff (20478 and 20479) 6000 W=USR(20224)
6010 P0=PEEK(20478)+256*PEEK(20479)
6020 W=USR(20228)
6030 P1=PEEK(20478)+256*PEEK(20479)
6040 RETURN
Now here's where things get calculated: Diagram 1 and 2 serve to calibrate paddle 0. Diagram 3 and 4 serve to calibrate paddle 1. A gets set to paddle0 for diagram 1. B gets set to paddle0 for diagram 2. Then we set FLG=0 and gosub 5000 to do the heavy calculating.
3010 A=P8(0):B=P8(1):FLG=0:GOSUB 5000
3020 POKE 1536,SL:POKE 1537,SH:POKE 1538,OL:POKE 1539,OH
A gets set to paddle1 for diagram 3. B gets set to paddle1 for diagram 4. Set FLG=1 and GOSUB 5000 to do the heavy calculating.
3030 A=P8(6):B=P8(7):FLG=1:GOSUB 5000
3050 POKE 1540,SL:POKE 1541,SH:POKE 1542,OL:POKE 1543,OH
3060 FS$="D:VWCALDA8.VER":ADDL=0:ADDH=6:AUX1=8:PTGT=11:GOSUB 30500
If A>=B then error out and goto 5550. B must always be greater than A or it doesn't make sense. We calculate our scale with 8192/(B-A). We calculate our origin with 16384 + scale * B for paddle0 or scale * B for paddle1, depending on how FLG is set. IF FLG=1 THEN O0=S0*B else O0=16384+S0*B. 5000 IF A>=B THEN POP :GOTO 5500
5050 S0=8192/(B-A)
5060 IF FLG=1 THEN O0=S0*B:GOTO 5100
5070 O0=16384+S0*B
5100 SH=INT(S0)
5110 SL=INT((S0-SH)*256+0.5)
5120 OV=INT(O0+0.5)
5130 OH=INT(OV/256)
5140 OL=OV-OH*256
5200 RETURN
These calculations are done beforehand so later all we have to do is a 16bit x 16bit multiply, as the division by (B-A) is done for us (we are just multiplying by the reciprocal of (B-A)).
|
|
|
|
|
Joined: Feb 2014
Posts: 1,237 Likes: 224
Very Senior Member
|
|
Very Senior Member
Joined: Feb 2014
Posts: 1,237 Likes: 224 |
So once we have these equations, we can make a spreadsheet, and with a bit of trial and error, deduce what the original values of the paddles when it was calibrated. The values in VWCALDA8.VER are 58,20,193,115,84,20,70,70. ![[Linked Image from i.imgur.com]](https://i.imgur.com/DUb9zBP.png) We find that PDL0 was A=250 and B=655, and PDL1 was A=482 and B=885. ![[Linked Image from i.imgur.com]](https://i.imgur.com/807vXVB.png) the formulas in the sheet: ![[Linked Image from i.imgur.com]](https://i.imgur.com/vOS9qM7.png) ![[Linked Image from i.imgur.com]](https://i.imgur.com/o7Qh6Mb.png) From a cool article on the versawriter, I now know the length of the arms: 6 inches each and the dimensions of the main board: it measures 12" vertically and 13.5" horizontally. ATARI CLASSICS Volume 2, Issue 3 / June 1993 / PAGE 26 LOOKiNG BACK: WHAT IS VERSAWRITER? GARY MATTESON AC STAFF REVIEWER https://www.atarimagazines.com/atariclassics/v2n3/looking_back.php
|
|
|
|
|
Joined: Feb 2014
Posts: 1,237 Likes: 224
Very Senior Member
|
|
Very Senior Member
Joined: Feb 2014
Posts: 1,237 Likes: 224 |
Just posting my analysis of the versawriter reading routines: PADDLE 0 computation scale=(8192/(B-A)) origin=16384+(8192/(B-A))*B 8192 = 32 * 256 0 = 0x0000 0 * 256 = 0 degrees 8192 = 0x2000 32 * 256 = 90 degrees 16384 = 0x4000 64 * 256 = 180 degrees 24576 = 0x6000 96 * 256 = 270 degrees take x, multiply x by scale, subtract that value from origin: origin - scale * pdl = angle of position origin - scale * pdl = angle if pdl=B: 16384 + (8192/(B-A))*B - 8192/(B-A))*B = 16384 + 0 (since B-B=0) or 180 degrees if pdl=A: 16384 + (8192/(B-A))*B - 8192/(B-A))*A = 16384 + 8192*(1) (since (B-A)/(B-A)=1) or 270 degrees this result is the angle that arm 1 is pointing in (with angle=0 pointing right on the x axis). PADDLE 1 computation exactly the same but we don't add 16384 to the origin: scale=(8192/(B-A)) origin=(8192/(B-A))*B origin - scale * pdl = angle if pdl=B: (8192/(B-A))*B - 8192/(B-A))*B = 8192 * 0 (since B-B=0) or 0 degrees if pdl=A: (8192/(B-A))*B - 8192/(B-A))*A = 8192*(1) (since (B-A)/(B-A)=1) or 90 degrees so the resulting angle direction is the angle from paddle 0 + angle from paddle 1 = direction that arm 2 is pointing. The x and y coordinates are initialized to $4000,$4000 (one of the final steps is to divide this coordinate by 32 so this can be treated as 2.0,2.0). x=2.0*32;y=2.0*32; We read paddle0, take the precomputed origin value and subtract scale*pdl0. Now we have a value for angle0 and we add cos(angle0) to x and sin (angle0) to y. Our sine table is pre-multiplied by 32 and this value of 32 represents 1.0 multiplied by our arm length of 6.0 inches. (we can treat our sin/cos as "unit circle values" where each unit represents 6.0 inches). The code to handle sin also calculates cos from the sin table by using cos theta=90-sin theta by eor the angle with #$1FFF (one's complement, effectively subtracting the angle 0-1fff from 90). All the values in the sine table are positive so we add or subtract from x or y depending on the quadrant. Our sin table is 4096 bytes/2 bytes per entry for 2048 entries over 90 degrees. Dividing 90 degrees / 2048 steps gives a resolution of .05 degree. angle0 = origin0 - scale0 * pdl0; x+=cos(angle0);y+=sin(angle0); Paddle1 is then read, and the precomputed origin value is taken and scale*pdl1 is subtracted: Then this angle1 is added to angle0 so we are "turning" left from the direction pointed by angle0. angle1 = (origin1 - scale1*pdl1); angle1 += angle0; x+=cos(angle1);y+=sin(angle1); The final steps are to scale our resulting x/y coordinates to screen coordinates. We divide by 32; x >>= 5; y >>= 5; Subtract 0x1ea8 from x, 0x4aa8 from y. x-=0x1ea8; this has the effect of subtracting 0.95 y-=0x4aa8; this has the effect of subtracting 2.33 Then multiply by 0x990e and 0x7740. x*=0x990e; this multiplies by 153.05 (153.05 * 2 = 306, very close to horizontal resolution of 320) 12 inches mapped to 306 pixels (25.5 pix/inch)) y*=0x7740; this multiplies by 119.25 (119.25 * 1.67 = 198.73, very close to vertical resolution of 192) (119.25 * 1.33 = 159, matches visible vertical resolution of 160 with 4 text lines at the bottom and it's 8 inches mapping to 160 pixels (20 pix/inch)) x coordinate before subtracting 0.95 runs from (1.0 to 3.0, or 2-1 to 2+1). y coordinate before subtracting 2.33 runs from (2.0 to 4.0, or 2,2+1+1). x coordinate of 1.0 becomes screen x = 6 x coordinate of 3.0 becomes screen x = 312 y coordinate of 2.33 becomes screen y = 0 y coordinate of 3.67 becomes screen y = 159 The versawriter reading code at $4c00-$4dff implements 16 bit addition ($4d2e) and 16 bit subtraction ($4d1a) routines using two 16 bit "registers" at $610/611 (ACC1) and $612/613 (ACC2). $4d2e does ACC1 += ACC2, AND $4d1a does ACC1 -= ACC2; There is a routine to do 16 bit x 16 bit multiply with a 32 bit result ($4d42), multiplicand in cf,d0, multiplier in cb,cc, with results in cb,cc,cd,ce cb=lowest byte ce=highest byte. After writing up my routine to feed the paddle values to the versawriter code, I was still getting wacky results, which was driving me wacky, so I thought, why not make sure it's getting the correct values so I decided to just stuff the value in 4dfc/4dfd and patching the paddle read routine to just copy these values into 4dfe/4dff. Analog reading by the versawriter routine: ![[Linked Image from i.imgur.com]](https://i.imgur.com/kBoB5gA.png) Directly plugging the analog values into memory: ![[Linked Image from i.imgur.com]](https://i.imgur.com/OsbzGOa.png) Lo and behold I get "perfect" straight axis lines. Something is affecting the timing of the read routine. At the beginning of the read routine, there's code to busy-wait until we are on scanline #1 (waiting for VCOUNTER=1), so that should allow the analog read to have consistent timing. Somehow this isn't working correctly. Interestingly, the variation in reading is very consistent, it tracks the exact same weird paths. So I guess there's more mysteries to be solved.
|
|
|
|
0 members (),
628
guests, and
6
robots. |
|
Key:
Admin,
Global Mod,
Mod
|
|
|
Forums9
Topics9,399
Posts122,883
Members5,092
| |
Most Online3,327 Nov 10th, 2025
|
|
These forums are sponsored by Superior Solitaire, an ad-free card game collection for macOS and iOS. Download it today!
|
|
|
|