Access to the lightpen using Vectrex32

Started by jamel, October 31, 2019, 08:13:50 PM

Previous topic - Next topic

jamel

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

Vectrex32

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

jamel

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

Vectrex32

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

jaymzjulian

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

jaymzjulian

(Where i wrote "right" above, read "button 4" - i was looking at the wrong pinout like a dumbass ;))

jaymzjulian

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.

Vectrex32

Sounds like I was over-engineering it.

- Bob

jaymzjulian

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. 

Vectrex32

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

Mutchler

Would this workaround slow the process down at all, Bob?

Vectrex32

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

Malban

#12
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...

Vectrex32

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


Malban

#14
Proof of concept:

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