Hi Everyone,
First of all, let me introduce myself very quickly. I am a new owner of a Vectrex32 and started playing around this great tool. I used to have a Vectrex in 1984, "lost it", and recently bought one for fun.
I also have a working lightpen (tested with Art Master, as the second joystick), but I cannot find any function to read it (ideally the position it is pointing to as x, y). I tried using WaitForFrame, with the pen connected as the second joystick and reading out all the values for the second joystick (x, y, b1, ... b4 - in all possible modes) and pointing to the screen in various positions, but I get nothing but 0. Any suggestions on how to use the lightpen with Vectrex32?
Thank you in advance for your guidance.
-Jamel
Welcome Jamel!
There's currently nothing in the Vectrex32 that supports reading a light pen. It's conceivable that you could rig something up using the CodeSprite, which allows you to write machine language into the 6809's memory space. But that would be a challenging project.
I've thought about adding support for the 3D imager and the light pen but I haven't figured out how I would do it. Part of the problem is my lack of understanding of how Vectrex games normally handle those devices. So I'm sorry to say, it's not going to happen anytime soon.
(If anyone does manage to control an imager or light pen with a Code Sprite, I promise I will incorporate it in a new version of Vectrex32 quickly!)
- Bob
Hi Bob,
Thank you for your welcome, and fast answer.
Got it. I see your idea, maybe there is an address - or range - we could read out using ML code we would inject using the CodeSprite and store it in the dual-port RAM. I have no clue if such addresses exist, but I will look around the web, and definitively keep reading this forum (and who knows, someone may know).
-Jamel
I suspect it's harder than that. The light pen probably triggers a 6809 interrupt when it detects light. Perhaps the interrupt routine sets a flag. Meanwhile, after each line the 6809 draws, it can check the flag to see if that line was selected by the light pen.
But that would only tell you what line was selected. If you wanted to know what point in the line the light pen was at, you would need to know the precise moment the interrupt occurred. Instead of just setting a flag, the interrupt routine would probably read a timer and store its value. Then you'd need to calculate where the pen was at that instant.
It requires a fair amount of code to pull this off. The V32's limited dual port RAM might not be up to it. You could copy some code into the 6809's RAM, but there's not a whole lot of that either.
So at the very minimum, this is difficult. It might be impossible. :-(
- Bob
afaict, the lightpen is literally trigger on/off - looking at the source, how artmaster handles this, is by only drawing the picker item, testing for light trigger - which is hooked up to pin 4, which is direction right. it then repeating this for every pickable item.
given all of that: I think you could achieve a version of it by disabling every object except your picker, drawing a frame, and then testing the controller. You'd want to do a high frame rate, of course, but this could work. I might try and write a test program tomorrow for this, since I actually do have a original vectrex lightpen here, so I should be able to test this theory.
The way you'd do this with a codesprite, is actually kind of indicated by the commented art master source - which is at http://vectrexmuseum.com/share/coder/DIS/TAFT/NEW/ART.ASM - which is that it does a scan by drawing a series of small lines around the screen to try and find the pen. The interesting function for these purposes is draw_with_pick_check. I'm still learning 6809 myself (I'm quite proficient at 6502/6510/z80, but i've never done raw 6809 before), but I could potentially come up with that code sprite at some point.
That having been said, if you did want to add this to the vectrex3d api code yourself I'd be willing to loan you my lightpen for a little while
(Where i wrote "right" above, read "button 4" - i was looking at the wrong pinout like a dumbass ;))
also what i said about being maybe able to do it in basic won't work for really obvious reasons - i.e. because it has to be _as_ the vector is being drawn, for the beam to be lit while the button scan is happening ;). so yeah, need to do the codesprite.
Sounds like I was over-engineering it.
- Bob
Well, on some experimentation I was under-engineering it ;). Once I started playing with this, I hit an interesting problem that actually explained some things that made bits of that artmaster source makes sense.
Essentially, the problem is this - the entire working of a lightpen is you have to trigger it _as the beam is drawing_. This is why artmaster uses the IRQ on the button line to store what it's doing. My mistake was assuming that there would be _enough_ persistence to read it out straight after a line draw, but as far as I can tell this isn't actually the case.
so you've got code that looks like this:
andcc #$EF; * Enable IRQ on 6809
lda #$82; * Enable CA1 on PIA
sta <$0E;
lda #$7E; * Set up IRQ handler
sta $CBF8;
ldd #ISR_handler;
std $CBF9;
And then ISR_handler is a simple function that just stores the data from the draw points:
; Really that function just needs to shove some sort of index of the currently
; being drawn line somewhere - so the implementation for this would probably
; be something along the lines of store the index in the draw list or some such....
; maybe...
process_ISR:
com <$0A;
clr <$05;
subb #$7F; * Calculate x coordinate
negb;
aslb;
sex;
subd #$0080;
lda $C882; * Retrieve y coordinate
tfr d,x;
jsr disable_interrupts;
lda #$FF; * Lightpen found
rts;
*
* ISR_handler()
*
* This is the entry point called by the OS, whenever the
* lightpen generates an IRQ interrupt. This procedure
* discards the 12 bytes of saved state information
* placed on the stack by the 6809, including the return
* address for the interrupted routine. Then it calls the
* process_ISR() routine, to process the IRQ request.
ISR_handler:
com <$0A;
leas 12,s;
jmp process_ISR;
DB $12;
Hopeuflly being fast enough to not break the vector drawing timing - the artmaster code, of course, does NOT use the bios drawing routines, and I suspect this may even be required, but honestly my reading of the bios code makes it _seem_ like this shoudl work with it.... so maybe?. So all of this is "fine", except of course you can't really point an IRQ at codesprite, right, at least by my reading of it.
No, you can't point an IRQ at a CodeSprite. But at startup, you could have a CodeSprite copy a block of code into the 6809's RAM and set the IRQ to point at that.
- Bob
Would this workaround slow the process down at all, Bob?
Quote from: Mutchler on November 06, 2019, 08:10:14 AM
Would this workaround slow the process down at all, Bob?
You mean putting interrupt handling code in the 6809's RAM? No, that shouldn't slow things down appreciably. Copying the code is a one-time thing at the beginning of the BASIC program - just remember to remove the CodeSprite after it runs once. Then there's the usual caveats about keeping your interrupt handler short and fast.
- Bob
Hi,
I haven't used Vectrex 32 in a while - so forgive me if I reply some nonsense ...
Actually I think it should be possible to use a lightpen in BASIC.
In theory - I haven't tried it.
And you do not have to use interrupt routines.
BUT - I dunno how - but you need to pass some information from a CodeSprite back to the "caller". Perhaps some reference pointer to a variable or so. Is that possible?
If so you could:
Build a codeSprite that clears the CA1 interrupt flag
Build a codeSprite that checks the CA1 interrupt flag and sets a return code.
Than you could "wrap" a draw with the above...
And if the return code was set, than the drawn vector was "Picked".
If the above is possible in BASIC, than you can do all lightpen work that is needed.
The three most used lightpen situations
a) pick a vector
b) move a vector
c) find a position
Can be implemented in BASIC.
a) is actually the above
b) draw a "web" like in Artmaster, but with a BASIC subroutine
c) Find a position - implement as a BASIC subroutine, but unlike Artmaster...
Artmaster draws 122 horziontal lines and the line that is picked is the "Y" position.
Artmaster than uses an interrupt routine to calculate the X position.
BUT instead of that, you might as well draw 122 vertical lines and see where the pick occured - that would be your X position.
It is a bit slower - yes, but that shouldn't really matter.
Cheers Malban
PS
Just read about the Code Sprite.
You said it is "live" data (and as such in RAM).
Could a code sprite therefor change itself?
Set e.g. the first byte of its own code to: "1" or "0"?
And could than from BASIC the live data be accessed with (in your example words):
if (machineCode[0] = 1) then ...
If so - would the CodeSprite regenerate in the next round? Or would one use a byte that is otherwise ignored, so not to destroy the assembler code?
PPS
Ahhh... reading further...
Above (live array) probably will not work...
But possible using the PEEK function. Does Vectrex32 use any RAM that is not used by the BIOS? If not than any address between say $c880 and $c900 will work to be used as a result byte...
A code sprite executing on the 6809 could not change the live data held in the V32. But a Peek would, indeed, work for passing data from the 6809 to the V32.
- Bob
Proof of concept:
http://vectrex.malban.de/tmp/Lightpen.mp4 (http://vectrex.malban.de/tmp/Lightpen.mp4)
' Define display configuration.
scale = 80
intensity = 100
frame_rate = 50
' Define various UI configuration.
instructions = { _
{-50, 100, "LIGHTPEN TEST"} _
}
picked = { _
{-10, -50, "PICKED"} _
}
call initRAMSub()
DIM peekdata[2]' Send a message to the Vectrex to peek at memory
wasSelected = 0
pickResultAddress = $c880
repeat
call IntensitySprite(intensity)
call SetFrameRate(frame_rate)
call ScaleSprite(scale)
' Display instructions.
textSize = {60, 3}
call TextSizeSprite(textSize)
call TextListSprite(instructions)
if wasSelected = 3 then
call TextListSprite(picked)
endif
call pokeRAM(0, $c880)
call draw_LP_Line(50,0,3)
call draw_LP_Line(0,50,3)
call draw_LP_Line(-50,0,3)
call draw_LP_Line(0,-50,3)
wasSelected = 0
CALL Peek(pickResultAddress, 1, peekdata)
' Get user input
controls = WaitForFrame( JoystickDigital, Controller1 + Controller2, JoystickX + JoystickY )
WHILE peekdata[1] = 0
ENDWHILE
wasSelected =peekdata[2][1]
call ClearScreen()
until controls[1, 6]
stop
' draws line with current scale etc
' when a lightpenPick was done, than
' Peek $c880 = which
' (after WaitForFrame...)
sub draw_LP_Line(x,y, which)
' all values must be FORCIBLY bytes
' negative values (e.g. -50, must be written as 205
if x<0 then
x = 256 +x
endif
if y<0 then
y = 256 +y
endif
drawLineWithPick = {$CC, y, x, $bd, $c9, 0, $27, 5, $86, which, $b7, $c8, $80}
call CodeSprite(drawLineWithPick)
endsub
sub pokeRAM(what, where)
if what<0 then
what = 256 +what
endif
poke_RAM = {$86, what, $b7, (where/256) MOD 256, where MOD 256}
call CodeSprite(poke_RAM)
endsub
sub initRAMSub
initDrawPickLineDSub = { _
$20, $27, _
$b7, $d0, $01, _
$7f, $d0, $00, _
$86, $ff, _
$7c, $d0, $00, _
$f7, $d0, $01, _
$b7, $d0, $0a, _
$7f, $d0, $05, _
$cc, $00, $40, _
$f5, $d0, $0d, _
$27, $fb, _
$1e, $11, _
$b7, $d0, $0a, _
$b6, $d0, $0d, _
$84, $02, _
$39, _
$1f, $51, _
$30, $88, $d7, _
$ce, $c9, $00, _
$ec, $81, _
$ed, $c1, _
$11, $83, $c9, $27, _
$25, $f6 _
}
call CodeSprite(initDrawPickLineDSub)
controls = WaitForFrame( JoystickDigital, Controller1 + Controller2, JoystickX + JoystickY)
call ClearScreen()
endsub
'copies a lineDrawWith Pick subroutine to $c900
' which is called with the coordinates in D
'
' |;***************************************************************************
' 0056 |copy
' 0056 20 27 | bra start
' |
' 0058 |draw_vector_with_pick_check:
' 0058 b7 d0 01 | sta >VIA_port_a ; Set rel y position.
' 005b 7f d0 00 | clr >VIA_port_b ; mux sel = integrator Y, mux enabled
' 005e 86 ff | lda #$ff ; pattern = $ff -> full line
' 0060 7c d0 00 | inc >VIA_port_b ; mux off
' 0063 f7 d0 01 | stb >VIA_port_a ; Set rel x position.
' 0066 b7 d0 0a | sta >VIA_shift_reg ; Set line pattern.
' 0069 7f d0 05 | clr >VIA_t1_cnt_hi ; start t1 timer (scale)
' 006c cc 00 40 | ldd #$0040 ; bit 6 = t1 timer interrupt
' 006f |timerCheck_loop:
' 006f f5 d0 0d | bitb >VIA_int_flags ; check if scale has run out
' 0072 27 fb | beq timerCheck_loop ; if interrupt not set -> timer still running
' 0074 1e 11 | exg x,x ; delay
' 0076 b7 d0 0a | sta >VIA_shift_reg ; shiftreg = 0, pattern = 0, light is off
' 0079 b6 d0 0d | lda >VIA_int_flags ; Check for a lightpen pick
' 007c 84 02 | anda #$02 ; bit 1 = ca1 interrupt
' 007e 39 | rts
' |
' 007f |draw_vector_with_pick_check_end
' 007f |start
' 007f 1f 51 | tfr pc,x
' 0081 |here
' 0081 30 88 d7 | leax <(draw_vector_with_pick_check-here),x
' 0084 ce c9 00 | ldu #$c900
' 0087 |copyOn
' 0087 ec 81 | ldd ,x++
' 0089 ed c1 | std ,u++
' 008b 11 83 c9 27 | cmpu #$c900 +(draw_vector_with_pick_check_end-draw_vector_with_pick_check)
' 008f 25 f6 | blo copyOn
More on the lighten subject (+ assembler sources, that might be added/changed to codeSprites) "within" Vide: http://vectrex.malban.de/preliminary/7fc4fa15.html (http://vectrex.malban.de/preliminary/7fc4fa15.html)
Super cool, at least from the video - sadly I couldn't get it to pick at all with my original lightpen (it works fine with artmaster/animaction, so i think the hardware is fine?). The result of the peek is always 129, if that means anything.
I'm assuming this wants the pen in port 2, as per usual, right?
I only tested it with my "work" Vectrex -- there (as in the Video) it works.
I can test it (later today) with some other Vectrex.
I used Vectrex32 firmware 1.20 - didn't put a test in front.
If you have a very "dim" vectrex - perhaps you only need to regulate the brightness a bit?
Anyways.. the result of the peek = 129?
That would be $81.
Which is a strange value. The function writes (on success) the "which" value - which is 3.
And before the function call, I explicitly write a 0 to it (pokeRAM(0, $c880)).
So... I have no clue on how that value could be put into that location.
best guess on my part is the firmware?
I'll try with a 1.20 firmware later today (I'm on a beta firmware so it's entirely possible that's what is up :)). Nectar given that you're setting the byte to 0, something is obviously up on my end
I can't imagine how any of the changes in the beta software would affect this. But I guess the proof of the pudding is in the eating.
- Bob
So here's something interesting:
I can reproduce this failing on 1.20, but I can reproduce it for a weirdly small case:
repeat
wasSelected = 0
call pokeRAM(44, $c880)
CALL Peek($c880, 1, peekdata)
' Get user input
controls = WaitForFrame( JoystickDigital, Controller1 , JoystickX + JoystickY )
WHILE peekdata[1] = 0
ENDWHILE
print peekdata[2][1]
wasSelected =peekdata[2][1]
print "wassel: ",wasSelected
call ClearScreen()
until controls[1, 6]
stop
Will _always_ return 129 on my vectrex for c880, no matter what i poke in it. When I tried c886 instead, it does correctly return whatever value I place in there! Sure enough, if I changed the light pen code to use c886 instaed of c880, it works great :).
I'm hoping to have time on the weekend to turn this into a small little POC vector drawing program demo, mostly because it'd be nice to sketch on the screen without having to swap the cartridge for artmaster ;)
I wonder if the c880 thing is something mildly wrong with my vectrex - i do have this other weird issue, where i have to prop up the vectrex32 cart for it to contact right (currently using a can of compressed air ;)), but every game I've thrown at this has run flawlessly, and i haven't had other weird problems with the vectrex32, so it's def weird, especially since even c881 seems to work! Surely it couldn't be just "that cell of ram is dead in my vectrex, but it affects "nothing else".....
The light pen bug is my fault. Turns out I am using memory location $C880.
I'll fix that in the next release.
- Bob