Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 00:39 05 May 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Picomite(VGA) V5.07.07 betas - bug fixes + focus on PIO

     Page 3 of 16    
Author Message
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 12:46pm 18 Jan 2023
Copy link to clipboard 
Print this post

Hi Peter,

Stumbing on something basic. Maybe you can help.
Struggling to get the packed array into a normal array (never done that before, so expect stupid errors). This is the most basic sample that is holding me up.

' Test signal: Three phase motor drive - picomite V50706+
' runs autonomously on GP0...GP5 50Hz

SetPin gp0,pwm  'CH 0a
SetPin gp1,pwm  'CH 0b
SetPin gp2,pwm  'CH 1a
SetPin gp3,pwm  'CH 1b
SetPin gp4,pwm  'CH 2a
SetPin gp5,pwm  'CH 2b

PWM 0, 50, 33.33, -66.66, 1, 1
PWM 1, 50, 33.33, -66.66, 1, 1
PWM 2, 50, 33.33, -66.66, 1, 1

PWM sync 0, 100/3, 200/3
pause 20


'----------------------------------- LA code PIO --------------------------

'PIO code to sample GP0..GP6 for logic analyzer
pio clear 1

'in this program the PIO reads GP0..GP5 brute force
'and pushes data into FIFO. The clock speed determines the
'sampling rate. There are 2 instructions per cycle
'taking 10000/2 / 50 = 100 samples per cycle.
 
'the PIO program
'address    code    mnemonics         comment
'.wrap target
'  0      4006      IN pins 6
'  1      8020      PUSH block
'.wrap
 
'program pio1
 pio program line 1,0,&h4006
 pio program line 1,1,&h8020
 
'configuration
f=1e4 'PIO run at 10kHz
p=pio(pinctrl 0,0,0,gp0,,,)       'IN base = GP0
e=pio(execctrl gp0,0,1)           'wrap 1 to 0

'write the configuration, running 100kHz (data in FIFO 10us after rising edge GP0)
 PIO init machine 1,0,f,p,e,,0     'start address = 0

'---------------------------- LA code MMBasic ----------------------------------

 
'do not start PIO, the DMA will do that.
length%=128
DIM data%(2*length%)        'array to put the 32 bit samples
dim packed%(length%)        'DMA array to pack 32 bit samples in 64 bit integers
PIO DMA_IN 1,0,2*length%,packed%(),ReadyInt

'wait forever to complete
do
 pause 1
loop
end

'-----------------------------------SUBS MMBasic --------------------------------
sub ReadyInt
 pio stop 1,0
 memory unpack packed%(),data%(),length%,32
 for i=0 to 2*length%-1
   print hex$(data%(i))
 next i
end sub


the error message I get has to do with missing dimensions....

Time taken: 2924mS
RUN
[66] Memory unpack packed%(),data%(),length%,32
Error : Dimensions


Feel so stupid... Help...
Edited 2023-01-18 22:49 by Volhout
PicomiteVGA PETSCII ROBOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 01:19pm 18 Jan 2023
Copy link to clipboard 
Print this post

Sorry the manual is wrong - see my example

Should be


MEMORY PACK sourceaddress%, Destinationaddress%, number, size
MEMORY UNPACK sourceaddress%, Destinationaddress%, number, size


Also be careful of your definitions of the arrays

How many elements in x(100) and how many in x(200) with default option base?

Try

Memory unpack Peek(varaddr packed%()),Peek(varaddr data%()),length%,32


In the next beta I'll modify memory pack and unpack so they match the manual and also optionally accept the address version
Edited 2023-01-18 23:21 by matherp
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 01:54pm 18 Jan 2023
Copy link to clipboard 
Print this post

That did it....

'---------------------------- LA code MMBasic ----------------------------------
 
 
'do not start PIO, the DMA will do that.
 length%=64
 DIM data%(2*length%-1)        'array to put the 32 bit samples
 dim packed%(length%-1)        'DMA array to pack 32 bit samples in 64 bit integers
 PIO DMA_IN 1,0,2*length%,packed%(),ReadyInt
 
'wait forever to complete
 dim a$(1)=("_","-")
 do
   pause 1
 loop
end
 
'-----------------------------------SUBS MMBasic --------------------------------
sub ReadyInt
 pio stop 1,0
 memory unpack peek(varaddr packed%()),peek(varaddr data%()),2*length%,32
 for j=0 to 5
   mask%=2^j
   for i=0 to 2*length%-1
     print a$(((data%(i) and mask%)=mask%));
   next i
   print
 next j
 'for i=0 to 2*length%-1
   'print hex$(data%(i))
 'next i
end sub





Now I can start implementing the trigger in PIO code....

Volhout
Edited 2023-01-18 23:56 by Volhout
PicomiteVGA PETSCII ROBOTS
 
Martin H.

Guru

Joined: 04/06/2022
Location: Germany
Posts: 904
Posted: 02:25pm 18 Jan 2023
Copy link to clipboard 
Print this post

  Volhout said  That did it....


Now I can start implementing the trigger in PIO code....

Volhout


for VGA it might look like this:



At Top and Button you have Space for Information Text and settings Messages:
You can also colorcode the first 16 Pixel of each Line with the CableColor as Tile BG-Color.

MODE 1:Colour 0,RGB(WHITE):CLS :Font 7
For f%=0 To 7
 Print @(0,88+48*f%) F%;" - Channel";f%
 TILE 0,5+f%*3,0,RGB(WHITE),6,2:TILE 6,5+f%*3,0,RGB(yellow),34,2
 TILE 0,4+f%*3,0,RGB(midgreen),40,1
 Line 0,64+f%*48,640,64+f%*48
Next
scl%=5
For f%=96 To 640 Step 4
 Line f%,62-2*(scl%=5),f%,63
 Inc scl%:If scl%=6 Then scl%=1
Next

'testrun
For F%=0 To 255
 For n%=0 To 7
   If f% And (2^n%) Then
     Plotline n%,1
   Else
     Plotline n%,0
   EndIf
 Next n%
 Next f%
Sub plotline ch%,Vl%
y%=ch%*48+88+16*(Vl%=0)
x%=96+2.125*f%
Line x%,y%,x%+2.125,y%
End Sub



cheers
Mart!n
Edited 2023-01-19 00:50 by Martin H.
'no comment
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 02:49pm 18 Jan 2023
Copy link to clipboard 
Print this post

Yip, that is the ultimate goal....

I currently focus only on the heart of the analyzer.

The user interface is not my strong side. What I like a lot about the Saleae analyzers is the fluent way they zoom (very quick). Currently only 128 samples in the array, but ultimately there may be 8000 samples 32 bit wide (32kb) and you want to zoom in / out to any block of 500 (640 screen, use 500 for waveformes, 140 for additional info), or scroll fluently. And also zoom in until only 50 samples of so on screen.

In that case a vertical cursor is nice. It will be very hard to see time synchonisation with the bare eye.

The VGA picomite has only limitted free IO pins. So we can sample 6 GPx pins very fast (40MHz or faster), but it would be nice to have more (16 pins or more).

On the VGA picomite we can use 4 x 74LVC165 shift registers to input 32 bits (using the 6 free GPx pins) and sample at 2MHz (shift register is read at 16MHz). You could real time analyze a 6502, databus and address bus and R/W and Ph2 and NMI and IRQ. Something for the 8 bit guy..

In that case we need some extra hardware to get a programmable trigger. Working on that. But I am thinking ahead now. First trigger and reconstruction of the data arrays using the 6 pins.

Another thing to think about is protocol analysis (UART decode/I2C decode).
Edited 2023-01-19 00:54 by Volhout
PicomiteVGA PETSCII ROBOTS
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 06:59pm 18 Jan 2023
Copy link to clipboard 
Print this post

@Peter,

When I use the normal DMA RX (no ring buffer) in below program, every keypress samples a new set of data from the GP0-5 pins.

But after some 6 or 7 times, the program exits with a panic.

I have tried forcing the DMA to a stop, and the message is DMA not running.
So the DMA has stopped, but MMBasic has problems.
Is this a memory leak ?




' Test signal: Three phase motor drive - picomite V50706+
' runs autonomously on GP0...GP5 50Hz
 
 SetPin gp0,pwm  'CH 0a
 SetPin gp1,pwm  'CH 0b
 SetPin gp2,pwm  'CH 1a
 SetPin gp3,pwm  'CH 1b
 SetPin gp4,pwm  'CH 2a
 SetPin gp5,pwm  'CH 2b
 
 PWM 0, 50, 33.33, -66.66, 1, 1
 PWM 1, 50, 33.33, -66.66, 1, 1
 PWM 2, 50, 33.33, -66.66, 1, 1
 
 PWM sync 0, 100/3, 200/3
 Pause 20
 
 
'----------------------------------- LA code PIO --------------------------
 
'PIO code to sample GP0..GP6 for logic analyzer
 PIO clear 1
 
'in this program the PIO reads GP0..GP5 brute force
'and pushes data into FIFO. The clock speed determines the
'sampling rate. There are 2 instructions per cycle
'taking 10000/2 / 50 = 100 samples per cycle.
 
'the PIO program
'address    code    mnemonics         comment
'  0      2016       WAIT GP22=0        wait for trigger to become low
'  1      2096      WAIT GP22=1       wait for trigger to become high
'.wrap target
'  2      4006      IN pins 6       read 6 pins
'  3      8020      PUSH block        and push
'.wrap
 
'program pio1
 PIO program line 1,0,&h2016
 PIO program line 1,1,&h2096
 PIO program line 1,2,&h4006
 PIO program line 1,3,&h8020
 
'configuration
 f=1e4   'PIO run at 10kHz
 p=Pio(pinctrl 0,0,0,gp0,,,)       'IN base = GP0
 e=Pio(execctrl gp22,2,3)          'wrap 3 to 2, gp22 is conditional JMP pin
 s=Pio(shiftctrl 0,0,0,0,0,0)      'shift in through LSB, out is not used
 
'write the configuration, running 10kHz (data in FIFO 10us after rising edge GP0)
 PIO init machine 1,0,f,p,e,s,0     'start address = 0
 
'---------------------------- LA code MMBasic ----------------------------------
'prepare trigger signal set to idle
 setpin gp22,dout
 pin(gp22)=0
 
'do not start PIO, the DMA will do that.
 Dim a$(1)=("_","-")
 length%=64
 Dim data%(2*length%-1)        'array to put the 32 bit samples
 Dim packed%(length%-1)        'DMA array to pack 32 bit samples in 64 bit integers
'  PIO DMA_IN 1,0,2*length%,packed%(),ReadyInt
 
'let the machine run, and wait for trigger
 do
   PIO DMA_IN 1,0,2*length%,packed%(),ReadyInt
   print "press any key to trigger"
do:loop while inkey$=""
   print "yes"
   pulse gp22,1
   pause 200
 loop
End
 
'-----------------------------------SUBS MMBasic --------------------------------
Sub ReadyInt
 PIO stop 1,0
 Memory unpack Peek(varaddr packed%()),Peek(varaddr data%()),2*length%,32
 For j=0 To 5
   mask%=2^j
   For i=0 To 2*length%-1
     Print a$(((data%(i) And mask%)=mask%));
   Next i
   Print
 Next j
 'PIO DMA_IN OFF
end sub

Edited 2023-01-19 05:01 by Volhout
PicomiteVGA PETSCII ROBOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 07:22pm 18 Jan 2023
Copy link to clipboard 
Print this post

See if this improves things.I assume you are developing on VGA?


PicoMiteVGA (2).zip
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 08:02am 19 Jan 2023
Copy link to clipboard 
Print this post

  matherp said  See if this improves things.I assume you are developing on VGA?


PicoMiteVGA (2).zip


Hi Peter,

This is improved.
The ***PANIC*** message is gone. But the code simply refuses to do a DMA after a few tries. (-or- the DMA's do not complete).



I am developing on the standard picomite. Since I try to bring maximum functionality to the VGA version, I need to use all available IO pins on the VGA version. That leaves me with no IO pins for debug. So I develop on the normal pico, and keep VGA in mind. On my standard pico, I have SDcard connected to the VGA pins (GP16...GP21), to get a group of 16 successive IO pins free (GP0...GP15) for PIO debugging.


Currently I work with 2 arrays. One packed array, and one data array. The packed array is resulting of the DMA action. The data array is where the display, data analysis, zooming etc.. will happen on. Until the moment that we can use a small data array and intelligen "unpacking" from the packed array (unpack small sections, like with a magnifier glass) the packed array is just eating up memory. At the moment I jave the data array (100%) and the packed array (size 50%) eating 150% variable space.
This could be overcome by adding the "64" size to the DMA (currently 1,4,8,16,32). In that case storage in the "packed" array would be wasting 32 bits per sample, but the data would be in integer format. No need for a "packed" array anymore.. Or am I dreaming..?

Volhout
Edited 2023-01-19 18:04 by Volhout
PicomiteVGA PETSCII ROBOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 09:39am 19 Jan 2023
Copy link to clipboard 
Print this post

OK, I think I've found it. You need to re-initialise the state machine after each run. The DMA call just enables it which runs from wherever the program counter happens to be. In the attached I've modified the PACK and UNPACK commands to match the manual

PicoMite.zip

Here is your code with the mod to make it reliable (call init in the interrupt routine)

' Test signal: Three phase motor drive - picomite V50706+
' runs autonomously on GP0...GP5 50Hz

SetPin gp0,pwm  'CH 0a
SetPin gp1,pwm  'CH 0b
SetPin gp2,pwm  'CH 1a
SetPin gp3,pwm  'CH 1b
SetPin gp4,pwm  'CH 2a
SetPin gp5,pwm  'CH 2b

PWM 0, 50, 33.33, -66.66, 1, 1
PWM 1, 50, 33.33, -66.66, 1, 1
PWM 2, 50, 33.33, -66.66, 1, 1

PWM sync 0, 100/3, 200/3
Pause 20


'----------------------------------- LA code PIO --------------------------

'PIO code to sample GP0..GP6 for logic analyzer
PIO clear 1

'in this program the PIO reads GP0..GP5 brute force
'and pushes data into FIFO. The clock speed determines the
'sampling rate. There are 2 instructions per cycle
'taking 10000/2 / 50 = 100 samples per cycle.

'the PIO program
'address    code    mnemonics         comment
'  0      2016       WAIT GP22=0        wait for trigger to become low
'  1      2096      WAIT GP22=1       wait for trigger to become high
'.wrap target
'  2      4006      IN pins 6       read 6 pins
'  3      8020      PUSH block        and push
'.wrap

'program pio1
PIO program line 1,0,&h2016
PIO program line 1,1,&h2096
PIO program line 1,2,&h4006
PIO program line 1,3,&h8020

'configuration
f=1e4   'PIO run at 10kHz
p=Pio(pinctrl 0,0,0,gp0,,,)       'IN base = GP0
e=Pio(execctrl gp22,2,3)          'wrap 3 to 2, gp22 is conditional JMP pin
s=Pio(shiftctrl 0,0,0,0,0,0)      'shift in through LSB, out is not used

'write the configuration, running 10kHz (data in FIFO 10us after rising edge GP0)
PIO init machine 1,0,f,p,e,s,0     'start address = 0

'---------------------------- LA code MMBasic ----------------------------------
'prepare trigger signal set to idle
SetPin gp22,dout
Pin(gp22)=0

'do not start PIO, the DMA will do that.
Dim a$(1)=("_","-")
length%=64
Dim data%(2*length%-1)        'array to put the 32 bit samples
Dim packed%(length%-1)        'DMA array to pack 32 bit samples in 64 bit integers
'  PIO DMA_IN 1,0,2*length%,packed%(),ReadyInt

'let the machine run, and wait for trigger
Do
  PIO DMA_IN 1,0,2*length%,packed%(),ReadyInt
  Print "press any key to trigger"
Do :Loop While Inkey$=""
  Print "yes"
  Pulse gp22,1
  Pause 200
Loop
End

'-----------------------------------SUBS MMBasic --------------------------------
Sub ReadyInt
PIO stop 1,0
Memory unpack packed%(),data%(),2*length%,32
For j=0 To 5
  mask%=2^j
  For i=0 To 2*length%-1
    Print a$(((data%(i) And mask%)=mask%));
  Next i
  Print
Next j
PIO init machine 1,0,f,p,e,s,0     'start address = 0
'PIO DMA_IN OFF
End Sub

Edited 2023-01-19 20:03 by matherp
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 10:10am 19 Jan 2023
Copy link to clipboard 
Print this post

Yes, there seems to be a relation with timing. See snip:

 PIO DMA_IN 1,0,2*length%,packed%(),ReadyInt
 Pause Rnd*200+200


If I remove the PAUSE, the PIO could potentially put one value in the FIFO before the DMA is ready. After few (6 or 7) triggers the codes tarts putting out "." (done%=0).

If I change the pause to PAUSE 20 (1 whole cycle of 50Hz), the DMA should be ready, but after 6 or 7 triggers I get:
Error: Invalid address - resetting


So you are right, there is a critical timing somewhere. Could this be because you have put the priority of the DMA too high (conflict with USB ?).

Regards,

Volhout
PicomiteVGA PETSCII ROBOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 10:16am 19 Jan 2023
Copy link to clipboard 
Print this post

Just completely changed my post - see above
 
Pluto
Guru

Joined: 09/06/2017
Location: Finland
Posts: 330
Posted: 11:31am 19 Jan 2023
Copy link to clipboard 
Print this post

Peter & Volhout, you are doing a great job!
I am following with great interest. I made some speed testing and in my opinion it looks very promising.



option list
PicoMite MMBasic Version 5.07.07b5
OPTION SYSTEM I2C GP16,GP17
OPTION FLASH SIZE 4194304
OPTION CPUSPEED (KHz) 378000
OPTION DISPLAY 50, 150


With PWM frequency set to 40MHz and PIO frequency set to 378MHz the program still works. Although the PWM output looks quite miserable.
option list

PicoMite MMBasic Version 5.07.07b5
OPTION SYSTEM I2C GP16,GP17
OPTION FLASH SIZE 4194304
OPTION CPUSPEED (KHz) 378000
OPTION DISPLAY 50, 150
OPTION LCDPANEL SSD1306I2C32, LANDSCAPE
>
________________________________________________________________________________________________________________________________
__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--
________________________________________________________________________________________________________________________________
-__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__-
________________________________________________________________________________________________________________________________
-__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__-
press any key to trigger


Very similar outlook by the logic analyzer. A few of the traces are just lines.

 
Pluto
Guru

Joined: 09/06/2017
Location: Finland
Posts: 330
Posted: 11:49am 19 Jan 2023
Copy link to clipboard 
Print this post

Matherp:
  Quote  You need to re-initialise the state machine after each run.

Matherp's program above:
Sub ReadyInt
PIO stop 1,0
Memory unpack packed%(),data%(),2*length%,32
For j=0 To 5
 mask%=2^j
 For i=0 To 2*length%-1
   Print a$(((data%(i) And mask%)=mask%));
 Next i
 Print
Next j
PIO init machine 1,0,f,p,e,s,0     'start address = 0
'PIO DMA_IN OFF
End Sub

PIO frequency can then be adjusted "on line" via GUI while the program is running. A possible route for adjustment of sampling speed for Zoom-in and Zoom-out?
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 12:43pm 19 Jan 2023
Copy link to clipboard 
Print this post

Thanks,

Now it works...
Thanks you for helping out...

Next on my list is doing the circular buffer, to allow for pre-trigger data.

This is what I envision:

MMBasic------------------------------------------
PIO DMA start
PIO write post trigger length (= 1/2 record length) in FIFO (starts capturing)

wait for completion of DMA

unpack data
get DMA packed pointer -> data array index
Write screen starting data index, with wrap around.


PIO code ------------------------------------------
.wrap target
  pull OSR blocking (wait for MMBasic to provide 1/2 record lenth)
  move OSR->X
loop:
  capturing data to fifo
  no trigger GPx jump to loop
loop2:
  capture data to fifo
  jump (X<>0) loop2 X--
.wrap

Volhout

Nice idea Pluto. That is only zooming IN/OUT while sampling (the capturing). The zoom IN/OUT when the data is already captured must happen on the packed%() or data%(). Both are needed.
Edited 2023-01-19 22:53 by Volhout
PicomiteVGA PETSCII ROBOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 05:26pm 19 Jan 2023
Copy link to clipboard 
Print this post

PicoMite(VGA)V5.07.07b6

https://geoffg.net/Downloads/picomite/PicoMite_Beta.zip

Various tidying up of recent changes
MEMORY PACK and UNPACK now accept array specifications as per the manual or a memory address as previously
 
Pluto
Guru

Joined: 09/06/2017
Location: Finland
Posts: 330
Posted: 07:21pm 19 Jan 2023
Copy link to clipboard 
Print this post

@Peter,
your program earlier on this page gives this after loading the latest firmware:
option list

PicoMite MMBasic Version 5.07.07b6
OPTION SYSTEM I2C GP16,GP17
OPTION FLASH SIZE 4194304
OPTION DISPLAY 50, 150
OPTION LCDPANEL SSD1306I2C32, LANDSCAPE
> RUN
press any key to trigger
yes
[80] Memory unpack packed%(),data%(),2*length%,32
Error : Dimensions
>  


  Matherp said  Various tidying up of recent changes
MEMORY PACK and UNPACK now accept array specifications as per the manual or a memory address as previously
 
Pluto
Guru

Joined: 09/06/2017
Location: Finland
Posts: 330
Posted: 07:28pm 19 Jan 2023
Copy link to clipboard 
Print this post

Is this not the correct way anymore?
Memory unpack packed%(),data%(),2*length%,32


I also noticed that the variable "lenght%" shows now as "LENGHT%" formatted as a command in MM Edit. Don't think that it was like that earlier?
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 07:34pm 19 Jan 2023
Copy link to clipboard 
Print this post

Is this not the correct way anymore?


That is what the manual says but up until today it didn't work.
You had to use PEEK(VARADDR array%()

Looks like there is still an issue - will post when fixed. In the meantime use peek(varaddr array%())
Edited 2023-01-20 05:43 by matherp
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 5913
Posted: 08:39pm 19 Jan 2023
Copy link to clipboard 
Print this post

  Pluto said  
I also noticed that the variable "lenght%" shows now as "LENGHT%" formatted as a command in MM Edit. Don't think that it was like that earlier?


MMEdit choices of what to highlight as a keyword is irreverent. It does not have any effect on the program.

LENGTH has been in the keyword list since it was a parameter for DIM AS STRING...

Jim
VK7JH
MMedit   MMBasic Help
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 10:48pm 19 Jan 2023
Copy link to clipboard 
Print this post

  Quote  Looks like there is still an issue


Should be OK now, please re-download. No version change
 
     Page 3 of 16    
Print this page
© JAQ Software 2024