How to create a digital recorder/scope
In 1997, Hilchner in germany marketed the Rievo Lie-detector Since I am a playful kid, I saw some market
potential for these things in my country.
OK, I was proven wrong. These boring dutch didn't understand the nature of the thing. It was a FUN article, to
be used in a half drunk state in a party-cellar to get the right atmosphere. And the only questions I got were
if I would give a test-certificate.
Since I had a truckload of lie detectors (yes, this is a lie; in fact I had only 20) lying around I needed some other purpose for these fun-items. So I used my engineering wits to please my fellow dutch: I would make a TOOL out of a TOY.
I succeeded in converting the toy into a tool. But then the dutch began to bark that the 10 euro toy was worse
than a 1000 euro LeCroy. Talk about a sense for humor.
In the download section you can find the full LDA package. It contains instructions how to change a fun
article into a toy-tool. The DVM created from the toy is Really Good. It DOES measure at 10 bits accuracy and
with a Vref of 5 Volts, the accuracy is 5 mV. And that's not bad for such a cheap product.
Below you see the program that I developed for running the Rievo LD as and ADC (hence the name LDA) at breakneck speeds. And since I am a real engineer I added things only engineers can think of.
In this source I first used my (then new) grahical user interface routines that I published about in APJ issue 6 and which were later ported to Modula-2 in the VGAlib3 project.
The circuit drawings..
Here you see the circuit drawing for the original "Rievo Luegendetektor" as it was sold by Rievo. The design
was ingeniously simple.
Run a converted Rievo liedetector as a scope
To the left, you see the circuit drawing for the converted "Rievo Luegen detektor". I mainly removed the
capacitor and the 100k resistor. And wrote some software, of course.
The new LDA software.
The first part is easy: we set up data, constants and the rest. Also we have the version history in the header. As you see, this is an old program and I should have published it many moons ago.
This program is GNU GPL, so don't complain it it works in another way than you expected. You have the source, so if it doesn't do what you wanted, just change it such that it will meet your requirements.
name LDA04d
title Lie Detector as Adc
page 80, 120
; LDA01 : first version, create user interface OK : 31-03-1998
; LDA02 : add mouse interface OK : 16-04-1998
; LDA03 : make it measure OK : 17-04-1998
; : improved bargraph plotting to <50 æs avg (386DX-40) OK : 17-04-1998
; : let it show digits OK : 20-04-1998
; : optimize for speed OK : 24-04-1998
; LDA04 : incorporate timing functions OK : 24-04-1998
; : use JMP $+2 before OUT DX, AL OK : 26-04-1998
; : get it to sample and stop automatically OK : 10-09-1998
; : let it plot data OK : 10-09-1998
; : Bug fixed, on exit FindClick jumped to AFTER
; restoration of the 01C vector... OK : 11-09-1998
; : make it nicer to the eye OK : 12-09-1998
; : use INT 070 for the sampler interrupt OK : 28-09-1998
; : make it sample at 8192 Sps OK : 29-09-1998
; : fix IRQ-0 glitches OK : 29-09-1998
; : make it sample at selectable sample rates OK :
lf = 10
cr = 13
CHR_WID = 9 ; number of pixels per character
BarLines = 14 ; height of bargraph
LftButton = bit 0 ; Mouse button codes
RgtButton = bit 1
MidButton = bit 2
PowerFlag = bit 0 ; ADC has power?
FastMode = bit 1 ; too fast to update screen?
PackWord = bit 2 ; store 3 (10-bit) results in 4 bytes
TimeOut = bit 3 ; one sample has been taken, please process
TrigWait = bit 4 ; if set, wait on trigger
StopNow = bit 5 ; if set, stop sampling
Running = bit 6 ; if set, IRQ may sample
RefrshBar = bit 7
RefrshDig = bit 8
ForceDigs = bit 11 ; Enforce a refresh of digits
Upd8Digs = bit 12 ; Update digital readout
RefrPara = bit 13 ; Parameter window needs refreshing
RfrshBar = bit 14 ; Refresh scale of bargraph?
JustUpd8 = bit 15 ; after a LOAD CONFIG, update only?
ClkBit = bit 1 ; bitnumbers in COM port registers
TxDBit = bit 6
MaxGroup = 64 ; maximum nr of sample per group
MaxShift = 6 ; LN (MaxGroup) / LN (2)
MaxCount = 1024 ; Maximum ADC count + 1
xxx STRUC
Now dw ? ; current value
Prev dw ? ; previous value
ENDS
Infoblk1 STRUC
Win_X dw ? ; top-left window position, X and ...
Win_Y dw ? ; ... Y
Win_wid dw ? ; window width and ...
Win_hgt dw ? ; ... height
CurrX dw ? ; within window, current X-coordinate, ...
CurrY dw ? ; ... and Y
DeltaX dw ?
DeltaY dw ?
Indent dw ? ; Indentation for characters in PIXELS!
Multiply dw ? ; screenwidth handler
Watte01 dw ? ;
BoxCol db ? ; border colour
TxtCol db ? ; text colour
BckCol db ? ; background colour
MenuCol db ? ; menu text colour
ENDS
clr MACRO ; make the #1 parameter ZERO
xor #1, #1
#EM
ShowMouse MACRO ; Make the mouse visible
mov ax, 1
int 033
#EM
HideMouse MACRO ; Make the mouse invisible
mov ax, 2
int 033
#EM
Piep MACRO ; BEEEEEEEEEEEEP
mov ah, 2
mov dl, 07
int 021
#EM
Store MACRO ; store one value in buffer at [di]
mov al, #1
stosb
#EM
Topic MACRO ; start of printing message
dw #1, #2
db #3, #4
#EM
TopicEnd MACRO ; topics stop here
dw 0F000
#EM
Some data declarations.
What follows are some variables needed to run this program. I used lots of comments so it should be not too hard to find out what it's all about.
DATA segment
FirstByte equ $
dummy db ? ; cosmetic variable, just to please D86!
E_mask db ? ; First-byte mask
L_mask db ? ; Last-byte mask
C_val db ? ; char mask allignment
clk_bit db ? ; last value of clkbit port
ToPlot db ? ; Plotting mask for pixels
hdc db 6 dup (?) ; Hex/Dec Conversion buffer
DigBuff db 8 dup (?) ; Digits from digital display (new/old)
VidMode db ? ; original video mode
even
ZeroSeg dw ? ; fast way to load SegReg with Zero
BufSeg dw ? ; segment of ADC buffer data
DataSeg dw ? ; segment of variables (same as CodeSeg)
CodeSeg dw ? ; segment of instructions (same as DataSeg)
Flags dw ? ; several status bits
ComNr dw ? ; current COM port number
ComBase dw ? ; base address of current COM port
ClkPort dw ? ; ComBase + 4
; Item number in options list of parameter:
StartIt dw ? ; Start Item ÄÄÄÄÄÄÄÄ´>>
StopIt dw ? ; Stop Item [Mem, Man, ë+, ë-, Val, Up, Down]
UnitIt dw ? ; Unit Item [V, A, F, ê]
ModeIt dw ? ; Mode Item [DMM, Plot, Hist, Stat]
StatIt dw ? ; Status Item [Idle, Sampl, Wait, Ready, Single]
Vref dw ? ; reference voltage of ADC [mV]
Scale dw ? ; actual scaling factor [x100]
GroupS dw ? ; actual groupsize
ShiftIt dw ? ; shiftbits for this groupsize
SmpRate dw ? ; actual sample rate
FSval dw ? ; Full-Scale value [mV]
MathRate dw ? ; nr of groups per second
RefrCnt dw ? ; refresh counter
TrigMode dw ? ; triggermode
BarVal dw ?, ? ; value to use in bargraph display [mV]
AvgVal dw ?, ? ; Most recent value [0..1023]
AvgVolt dw ? ; Scaled and ready to print value
ThisVal dw ?, ? ; Scaled and ready to print value
AdcCount dw ? ; current sample number
AdcValue dw ? ; current sample value
RunTotal dw ? ; running total for averaging
Counter dw ? ; incremented by NewIRQ0 for screen updates
Sigma dw ? ; Most recent sigma
Segments dw ?, ? ; Segments to plot in bargraph
SubDiv dw ? ; subdivisons for axis printer
TotalDiv dw ? ; total nr of divisons
T_val dw ? ; total nr of bits to plot
E_val dw ? ; nr of bits in first byte
L_val dw ? ; nr of bits in last byte
K_val dw ? ; nr of BYTES to fill in between
P_mask dw ? ; mask for printing characters
MouseX dw ? ; mouse X coordinate
MouseY dw ? ; mouse Y coordinate
MouseB dw ? ; mouse button status
Clicker dw ? ; mouse clickfield counter
Handle dw ? ; filehandle storage
VidPoint dw ? ; videoRAM pointer
ShouldBe dw ? ; target for timing function [æs]
TrialRun dw ? ; Indirect call for DoW8 function
Delay dw ? ; actual value for delay loop [counts]
OneShot dw ? ; time it takes to take ONE sample [æs]
MaxRate dw ?, ? ; maximum sample rate [Sps]
SampTime dw ? ; maximum recording time at this rate [s]
even 4
OldIRQ0 dd ? ; pointer to old timer routine
OldClock dd ? ; pointer to old RTC routine
time dw 020 dup (?) ; space for timing values
RawBuff dw MaxGroup dup (?) ; space for averaging
ADCbuff dw 4096 dup (?)
dw 01FF dup (?)
stacktop dw ?
LastByte equ $
;-------------------------------
Window definitions and timer operation.
We start with the multiply-by-80 routine. It's needed by the graphic engine and it is used in the BOX-parameter lists.
Next come the 16 window definitions. Each definition is of type InfoBlk1 (see above) and describes a full window.
Next come some routines to control the microsecond timer in the hardware (CMOS) clock.
CODE segment
jmp main
mul_80: push bx ; calculate pixel address in 640 x 480 mode
shl ax, 4
mov bx, ax ; bx = 16 x SCR_Y
shl ax, 2 ; ax = 64 x SCR_Y
add ax, bx ; ax = 80 x SCR_Y
pop bx
ret
;-------------------------------
even
fullscrn dw 0, 0,640,480, 0, 0, 0, 0, 4, mul_80, 0 ; main screen window
db 12, 14, 3, 15
ParWin2 dw 5, 30,630,150, 8, 9, 0, 0, 4, mul_80, 0 ; Parameter window
db 10, 11, 3, 11
PlotWin dw 5,195,630,260, 0, 0, 0, 0, 4, mul_80, 0 ; Virtual plotting window
db 9, 15, 3, 7
PlotWin2 dw 6,196,628,256, 0, 0, 0, 0, 4, mul_80, 0 ; Actual plotting window
db 9, 15, 3, 7
ParWin dw 5, 30,630,150, 4, 9, 0, 0, 4, mul_80, 0 ; parameter window
db 10, 11, 3, 11
EditWin dw 5, 30,630,150, 4, 9, 0, 0, 4, mul_80, 0 ; Data entry window
db 5, 6, 15, 14
TopBar dw 5, 5,630, 20, 6, 0, 0, 0, 4, mul_80, 0 ; menu bar
db 4, 5, 1, 0E
BotBar dw 5,460,630, 18,15, 0, 0, 0, 4, mul_80, 0 ; copyrights
db 3, 6, 2, 0F
VrefBar dw 15,120,540, 50, 0, 0, 0, 0, 0, mul_80, 0 ; mouse scroll-bar Vref
db 10, 15, 4, 14
ScaleBar dw 15,120,540, 50, 0, 0, 0, 0, 0, mul_80, 0 ; mouse scroll-bar Scale
db 11, 14, 3, 12
DisplBar dw 10,150,500, 20, 0, 0, 0, 0, 0, mul_80, 0 ; Bargraph display
db 8, 10, 14, 4
DigSpac1 dw 16, 90, 40, 50, 0, 0, 0, 0, 0, mul_80, 0 ; Digital display, digit 1, MSD
db 9, 11, 14, 3
DigSpac2 dw 56, 90, 40, 50, 0, 0, 0, 0, 0, mul_80, 0 ; Digital display, digit 2
db 9, 11, 14, 3
DigSpac3 dw 96, 90, 40, 50, 0, 0, 0, 0, 0, mul_80, 0 ; Digital display. digit 3
db 9, 11, 12, 3
DigSpac4 dw 136, 90, 40, 50, 0, 0, 0, 0, 0, mul_80, 0 ; Digital display, digit 4, LSD
db 9, 11, 12, 3
MX_box dw 592, 38, 50, 60, 0, 0, 0, 0, 4, mul_80, 0 ; Mouse plotters
db 3, 6, 2, 0F
VGA_base dw 0A000 ; for ease of loading segment registers
;-------------------------------
DoZip: ret ; do absolutely NOTHING!
;-------------------------------
open_2: push ax ; open timer chip in mode 2
mov al, 034
out 043, al
xor al, al
out 040, al
out 040, al
pop ax
ret
;-------------------------------
close_2: push ax ; close mode 2 of timer chip
mov al, 036
out 043, al
xor al, al
out 040, al
out 040, al
pop ax
ret
;-------------------------------
rd_time: mov al, 6 ; read timer
out 043, al
in al, 040
mov ah, al
in al, 040
xchg al, ah
stosw
ret
;-------------------------------
Some mouse related functions.
Since this program was written to be fully mouse-operated (ditch that keybored!), the mouse must be
initialised and such.
The first routine is an alternate mouse-interrupt server. The second routine installs this server and the
third routine releases it from memory again.
AltMous: push ds ; let the mouse tell when to stop
cs mov ds, [DataSeg] ; set Data segment correctly
test bx, bit 4 ; right button released?
IF Z or [Flags], StopNow ; if so, set ABORT flag
pop ds
retf ; and exit
;-------------------------------
SetAltMous: ; install AltMous interrupt routine
push ax, cx, dx, es
mov dx, offset AltMous
mov es, [CodeSeg]
mov cx, bit 4 ; routine triggers on RELEASED right button
mov ax, 0C
int 033
pop es, dx, cx, ax
ret
;-------------------------------
ClrAltMous: ; uninstall AltMous interrupt routine
push ax, cx
clr cx
mov ax, 0C ; clear mask => driver disabled
int 033
pop cx, ax
ret
;-------------------------------
Key: mov ax, 0C07 ; wait for keypress
int 021
ret
;-------------------------------
The latter routine ('key') is used to wait for a keypress. This rouitne is only used for debugging purposes
since the finalised program is FULLY mouse-controlled.
Windowing the graphics way.
What follows is the core (kernel?) of the VGAlib library. It controls the VGA card and controls how pixels are manipulated. When you do things on your own in the VGA card, you have to set colours and also define how to place pixels.
The 'H_Line' function draws horizontal ines. This can be done by plotting each pixel on it's own but that
would mean that you would access most bytes in VGA memory space eight times: once for each pixel.
That can be done smarter so H_Line determines how many pixels fall in the first and last bytes of a line.
Hence the rest is in adjacent bytes and we don't need to isolate the bits there.
H_Line then plots the first and last isolated pixels and next fills in the 'in-between-pixels' byte-wise.
SetMask: push dx ; ah = mask
mov dx, 03CE
mov al, 8
out dx, ax ; set bit mask to "all on"
pop dx
ret
;-------------------------------
SetColr: push dx ; ah = colour
mov dx, 03C4
mov al, 2
out dx, ax ; select page register and colour
pop dx
ret
;-------------------------------
VGaddr: mov es, [VGA_base] ; calculate address in VGA memory
mov ax, [di.CurrY]
add ax, [di.Win_Y]
call [di.Multiply]
mov bx, [di.CurrX]
add bx, [di.Win_X]
shr bx, 3
add bx, ax ; bx = index address
ret
;-------------------------------
VgaPlot: mov al, [es:bx] ; Do the actual plotting
mov al, [ToPlot]
mov [es:bx], al
ret
;-------------------------------
PlotPix: push ax, bx, cx, es ; plot a point on screen
call VGaddr
mov cx, [di.CurrX] ; calculate plottingmask
add cx, [di.Win_X]
and cx, 0111xB
mov ah, 080
shr ah, cl
call SetMask
call VgaPlot
pop es, cx, bx, ax
ret
;-------------------------------
comment ^
First 1 ... ... K Last : byte in video memory
| | | | |
| # #|# # # # # # # #|# # # # # # # #|# # # |
+----------------+---------------+---------------+----------------+
E = 2 K = 2 L = 3 T = 21
E-val = 8 - ((CurrX + Win_X) AND 7) E-mask = 0FF shr ((8 - E-val) AND 7)
L-val = (T - E-val) AND 7 L-mask = 080 sar L-val
K-val = (T - E-val - L-val)/8 ^
L0: mov cx, [di.DeltaX] ; do a short line
L1: call PlotPix
inc [di.CurrX]
loop L1
pop es, cx, bx, ax
ret
H_Line: push ax, bx, cx, es ; optimized horizontal line drawing
cmp [di.DeltaX], 17 ; too few pixels for a bulk draw?
jb L0
mov cx, [di.CurrX] ; do a long line
add cx, [di.Win_X]
and cx, 0111xB
mov bx, 8
sub bx, cx
mov [E_val], bx ; number of pixels to plot in leftmost byte
mov al, 0FF
shr al, cl
mov [E_mask], al ; set up mask for plotting leftmost pixels
mov cx, [di.DeltaX]
sub cx, [E_val]
mov ax, cx
and ax, 0111xB
mov [L_val], ax ; number of pixels to plot in rightmost byte
sub cx, ax
shr cx, 3
mov [K_val], cx ; number of "full" bytes to plot
clr al
mov cx, [L_val]
cmp cx, 0
IF ne mov al, bit 7
dec cx
sar al, cl
mov [L_mask], al ; determine mask for last pixels
call VGaddr
mov ah, [E_mask]
call SetMask
call VgaPlot ; plot leftmost part
inc bx
mov cx, [K_val]
jcxz >L4
mov ah, 0FF
call SetMask
L3: call VgaPlot ; plot middle part
inc bx
loop L3
L4: mov ah, [L_mask]
call SetMask
call VgaPlot ; plot remaining pixels
mov ax, [di.DeltaX]
add [di.CurrX], ax
pop es, cx, bx, ax
ret
;-------------------------------
VertLin: push cx ; draw a vertical line
mov cx, [di.DeltaY]
L0: call PlotPix
inc [di.CurrY]
loop L0
pop cx
ret
;-------------------------------
MakeWin: push ax, cx ; make window
mov ah, [di.BckCol]
call SetColr ; define colour
mov cx, [di.Win_hgt]
clr ax
mov [di.CurrY], ax
L0: clr ax
mov [di.CurrX], ax
mov ax, [di.Win_wid]
mov [di.DeltaX], ax
call H_Line
inc [di.CurrY]
loop L0
pop cx, ax
ret
;-------------------------------
DelWin: push ax, cx ; delete window
mov [ToPlot], 0
mov ah, 0FF
call SetColr
mov cx, [di.Win_hgt]
clr ax
mov [di.CurrY], ax
L0: clr ax
mov [di.CurrX], ax
mov ax, [di.Win_wid]
mov [di.DeltaX], ax
call H_Line
inc [di.CurrY]
loop L0
mov [ToPlot], 0FF
pop cx, ax
ret
;-------------------------------
MakeBox: push ax, [di.CurrX], [di.CurrY], [di.DeltaX], [di.DeltaY] ; make a box
mov ah, [di.BoxCol]
call SetColr
mov ax, [di.Win_wid]
mov [di.DeltaX], ax
mov ax, [di.Win_hgt]
mov [di.DeltaY], ax
clr ax
mov [di.CurrX], ax
mov [di.CurrY], ax
call H_Line
dec [di.CurrX]
call VertLin
mov [di.CurrX], ax
mov [di.CurrY], ax
call VertLin
dec [di.CurrY]
call H_Line
pop [di.DeltaY], [di.DeltaX], [di.CurrY], [di.CurrX], ax
ret
;-------------------------------
Important routines: put text on-sceen.
Now that we can place boxes on the screen, we also need to fill these boxes with characters. These characters
are defined in a big array which is included into the source in the botoom line of this source. The bitmap
file is called bitmap.a86.
Take a look at bitmap.a86 and try to understand it. Control this file and you control your own characterset.
The problem with plotting text is that not all letters are plot on whole byte boundaries. So an 8 pixel character can be smeared out over two bytes. I could have defined that all tokens were to be printed on whole-byte boundaries but that would have taken away just about all the chamrs of this library.
L0: add [di.CurrY], 16 ; process 'LF'
L1: pop es, si, cx, bx
ret
L2: mov bx, [di.Indent]
mov [di.CurrX], bx ; process 'CR'
jmp L1
putchar: push bx, cx, si, es ; print char in al at (x,y)
cmp al, lf
je L0
cmp al, cr
je L2
mov bx, [di.CurrX]
add bx, CHR_WID
cmp bx, [di.Win_wid]
jbe >L3
mov bx, [di.Indent]
mov [di.CurrX], bx ; mimick 'CR'
add [di.CurrY], 16 ; mimick 'LF'
L3: mov cx, [di.CurrX]
add cx, [di.Win_X]
and cx, 0111xB
mov [C_val], cl ; store shiftcount for masks
mov bx, 0FF00
shr bx, cl ; setup plotting mask and ...
mov [P_mask], bx ; ... store it
clr ah
mov si, ax ; make address of pixels in bitmap
shl si, 4
add si, offset bitmap
call VGaddr ; bx = -> in video memory
mov ax, [P_mask]
call SetMask
mov cx, 16
L4: push cx
mov ah, [si]
clr al
mov cl, [C_val]
shr ax, cl
mov cl, [es:bx]
mov [es:bx], ah
add bx, 80
inc si
pop cx
loop L4
sub bx, 16 * 80 - 1
mov ax, [P_mask]
cmp al, 0 ; if nothing to do, ...
je >L6 ; ... skip this chapter
mov ah, al
call SetMask
mov cx, 16
sub si, cx
L5: push cx
mov ah, [si]
clr al
mov cl, [C_val]
shr ax, cl
mov cl, [es:bx]
mov [es:bx], al
add bx, 80
inc si
pop cx
loop L5
L6: add [di.CurrX], CHR_WID
jmp L1
;-------------------------------
print: mov ah, [di.TxtCol] ; print a table of text
call SetColr
L0: lodsw
cmp ax, 0F000 ; end of table?
je ret
mov [di.CurrX], ax
lodsw
mov [di.CurrY], ax
L1: lodsb
cmp al, 0
je L0
call putchar
jmp L1
;-------------------------------
Center: push ax, bx, cx, dx, si ; put a menubar on screen
mov ah, [di.MenuCol] ; si = -> menu-text (ASCIIZ)
call SetColr
clr cx
clr bx
D0: lodsb
cmp al, 0
je >D2 ; zero found => end of string
cmp al, ' '
jne >D1
inc cx ; increment space-counter
jmp D0
D1: add bx, CHR_WID ; increment character count (in pixels!)
jmp D0
D2: pop si
mov ax, [di.Win_wid]
sub ax, [di.Indent]
sub ax, [di.Indent]
sub ax, [di.CurrX]
sub ax, bx ; ax = # of spaces in line
clr dx
div cx
mov bx, ax ; bx = empty pixels between items
D3: lodsb
cmp al, ' '
jne >D4
add [di.CurrX], bx
jmp D3
D4: cmp al, 0
je >D5
cmp al, '_'
IF E mov al, ' '
call putchar
jmp D3
D5: pop dx, cx, bx, ax
ret
;-------------------------------
prax: push cx, si, ds, ax ; print AX on (scr_x, scr_y)
mov ah, [di.TxtCol]
call SetColr
pop ax, es
call hexdec
mov cx, 3
add si, 3
cld
L1: lodsb
call putchar
loop L1
pop si, cx
ret
;-------------------------------
Some additional routines.
Below are some routines used by other functions.
Show_Mouse: ; display mouse coordinates in special window
push ax, di
mov di, offset MX_box
mov [di.CurrX], 4
mov [di.CurrY], 2
mov ax, [MouseX]
call prax
mov [di.CurrX], 4
mov [di.CurrY], 20
mov ax, [MouseY]
call prax
pop di, ax
ret
;-------------------------------
GetMous: push ax, bx, cx, dx
mov ax, 3
int 033 ; call Mouse and:
mov [MouseB], bx ; store buttons, ...
mov [MouseX], cx ; ... horizontal and ...
mov [MouseY], dx ; ... vertical position.
call Show_Mouse
pop dx, cx, bx, ax
ret
;-------------------------------
hexdec: push cx, dx, di ; make hex number printable
mov di, offset hdc ; ax = number in hex
push di, ax
mov cx, 6
mov al, ' '
cld
rep stosb ; clear ASCII buffer
dec di ; point to LSB position
mov b [di], '0' ; init LSB
pop ax
mov cx, 10
L0: clr dx
div cx
add dl, '0'
mov [di], dl
dec di
or ax, ax
jnz L0
pop si, di, dx, cx
ret
;-------------------------------
L1: add bx, 8
inc [Clicker]
jmp >L2
L0: pop bx, ax
ret
FindClick: ; Was this a valid click of the mouse?
push ax, bx ; bx = -> klikken-tabel
mov bx, offset clix1
mov [Clicker], 0
L2: mov ax, [bx]
cmp ax, 0F0F0
je L0
cmp [MouseX], ax
jb L1 ; next entry
mov ax, [bx+2]
cmp [MouseY], ax
jb L1
mov ax, [bx+4]
cmp [MouseX], ax
ja L1
mov ax, [bx+6]
cmp [MouseY], ax
ja L1
cmp [MouseB], 0
je L0
Piep
L3: mov bx, [Clicker]
test [Flags], PowerFlag
jz >L4
cmp bx, 7
ja L0
L4: shl bx, 1
call ParaMous[bx]
jmp L0
;-------------------------------
Routines to control the hardware.
The Rievo used a TLC 1549 serial Analog to Digital converter. These have a very low pincount (8 pins in total)
but the data need to be clocked out one by one.
This is not difficult, but strict timing procedures must be met. Therefore these routines must be designed
with great care.
The routines are PowerUp for supplying power to the ADC chip, PowDown for turning off the power, ClkLo for
making the clockline LOW and Clock for generating a HIGH followed by a LOW on the clockline.
RdWord and RdFast are functions to get a result word from the ADC. RdFast may only be used when RdWord has
preceeded it.
PowerUp: push ax, dx ; supply power to ADC
mov dx, [ComBase]
add dx, 3
in al, dx
or al, TxDBit
jmp $+2
out dx, al
pop dx, ax
mov b [ParaPowr][0], 'O'
mov b [ParaPowr][1], 'N'
mov b [ParaPowr][2], ' '
mov [StatIt], 1
or [Flags], PowerFlag + RefrPara
ret
;-------------------------------
PowDown: push ax, dx ; remove power from ADC
mov dx, [ComBase]
add dx, 3
in al, dx
and al, not TxDBit
jmp $+2
out dx, al
pop dx, ax
mov b [ParaPowr][0], 'O'
mov b [ParaPowr][1], 'F'
mov b [ParaPowr][2], 'F'
mov [StatIt], 0
and [Flags], not PowerFlag
or [Flags], RefrPara
ret
;-------------------------------
ClkLo: push ax, dx ; Set ADC clock Low
mov dx, [ComBase]
add dx, 4
in al, dx
and al, not ClkBit
jmp $+2
out dx, al
mov [clk_bit], al
pop dx, ax
ret
;-------------------------------
clock: mov ah, al
sub dx, 2
mov al, [clk_bit]
xor al, ClkBit
out dx, al
xor al, ClkBit
jmp $+2
out dx, al
add dx, 2
mov al, ah
ret
;-------------------------------
RdWord: cli
push cx, dx
mov dx, [ComBase] ; read in one 10-bit word from ADC
add dx, 6
mov cx, bit 6 ; set end-point
L0: in al, dx ; get bit, ...
call clock
shl al, 3 ; ... isolate it, ...
rcl cx, 1 ; ... rotate it into buffer, ...
jnc L0 ; ... until done.
mov ax, cx
pop dx, cx
sti
ret ; with result in ax
;-------------------------------
RdFast: mov bx, bit 6 ; set end-point
L0: in al, dx ; get bit, ...
call clock
shl al, 3 ; ... isolate it, ...
rcl bx, 1 ; ... rotate it into buffer, ...
jnc L0 ; ... until done.
mov ax, bx
stosw ; and store it
ret ; with result in ax
;-------------------------------
Some more 'glue' routines.
FindPort: ; find ComBase for ComNr
push bx, es
mov bx, [ComNr]
shl bx, 1
add bx, 0400
mov es, [ZeroSeg]
es mov ax, [bx]
pop es, bx
ret
;-------------------------------
RelButt: call GetMous ; wait until Mouse Button released
cmp [MouseB], 0
jne RelButt
ret
;-------------------------------
L0: call RelButt
pop dx, cx, bx, ax
ret
DoSave: push ax, bx, cx, dx ; save settings to CFG file
mov dx, offset CFG_file
clr cx
mov ah, 03C
int 021 ; create CFG file
jc L0
mov [Handle], ax
mov dx, offset Flags
mov cx, offset FSval + 2
sub cx, dx
mov bx, [Handle]
mov ah, 040
int 021 ; save important data to file
mov bx, [Handle]
mov ah, 03E
int 021 ; close CFG file
jmp L0
;-------------------------------
comment ^
L0: pop dx, cx, bx, ax
ret
SaveDef: push ax, bx, cx, dx ; save settings to DEF file
mov dx, offset DEF_file
clr cx
mov ah, 03C
int 021 ; create CFG file
jc L0
mov [Handle], ax
mov dx, offset Flags
mov cx, offset FSval + 2
sub cx, dx
mov bx, [Handle]
mov ah, 040
int 021 ; save important data to file
mov bx, [Handle]
mov ah, 03E
int 021 ; close CFG file
jmp L0
;-------------------------------
L0: pop dx, cx, bx, ax
ret
LoadDef: push ax, bx, cx, dx
mov dx, offset DEF_file
mov ax, 03D00
int 021 ; open CFG file
jc L0
mov [Handle], ax
mov dx, offset Flags
mov cx, offset RefrCnt
sub cx, dx
mov bx, [Handle]
mov ah, 03F
int 021 ; read in critical data
mov bx, [Handle]
mov ah, 03E
int 021 ; close CFG file
call UpDate
jmp L0
;------------------------------- ^
L2: pop dx, cx, bx, ax
ret
DoLoad: push ax, bx, cx, dx
mov dx, offset CFG_file
mov ax, 03D00
int 021 ; open CFG file
jc L2
mov [Handle], ax
mov dx, offset Flags
mov cx, offset RefrCnt
sub cx, dx
mov bx, [Handle]
mov ah, 03F
int 021 ; read in critical data
mov bx, [Handle]
mov ah, 03E
int 021 ; close CFG file
call UpDate
call RelButt
jmp L0
;-------------------------------
Controlling the user interface options.
SetStat: push cx, si, di ; update "Status" field
mov si, [StatIt]
shl si, 3
add si, offset SamStat
mov di, offset ParaStat
mov cx, 8
rep movsb
pop di, si, cx
ret
;-------------------------------
L0: call RelButt
or [Flags], RefrPara
L1: call ParRfsh
pop di, si, cx
ret
SetStrt: push cx, si, di
mov ax, [StartIt]
test [Flags], JustUpd8 ; just refreshing?
jnz >L2
test [MouseB], LftButton + RgtButton
IF Z jmp L1
test [MouseB], LftButton
IF NZ inc ax
test [MouseB], RgtButton
IF NZ dec ax
cmp ax, 6
IF E clr ax
cmp ax, 0FFFF
IF E mov ax, 5
mov [StartIt], ax
L2: shl ax, 3
mov si, offset TrigStrt
add si, ax
mov di, offset ParaStrt
mov cx, 8
mov es, ds
rep movsb
jmp L0
;-------------------------------
UpDate: push ax, bx, cx, si, di
or [Flags], JustUpd8 ; SIGNAL: update only
test [Flags], PowerFlag
jnz >L0
mov b [ParaPowr][1], 'F'
mov b [ParaPowr][2], 'F'
jmp >L1
L0: mov b [ParaPowr][1], 'N'
mov b [ParaPowr][2], ' '
L1: mov ax, [ComNr]
add al, '1'
mov [ParaCom], al
call ShowFS
mov ax, [Vref]
mov di, offset ParaVref
call hexdec ; update after reloading settings
add si, 2
movsb
Store ','
movsb
movsb
movsb
mov ax, [Scale]
mov di, offset ParaScal
call hexdec ; update after reloading settings
add si, 3
movsb
Store ','
movsb
movsb
mov ax, [SmpRate]
mov di, offset ParaRate
call hexdec
inc si
mov cx, 5
rep movsb
Store ' '
Store 'S'
Store 'p'
Store 's'
mov ax, [GroupS]
call hexdec
add si, 4
mov di, offset ParaGrup
mov cx, 2
rep movsb
call SetStrt
call SetStop
call SetSpd
call SetUnit
call SetMode
or [Flags], RefrPara
call ParRfsh
and [Flags], not JustUpd8 ; end of update
pop di, si, cx, bx, ax
ret
;-------------------------------
Trigger: ret ; wait for trigger event to happen
;-------------------------------
L0: call RelButt
ret
WinClr: test [MouseB], LftButton + RgtButton
jz L0
push di
mov di, offset PlotWin2
call DelWin
pop di
jmp L0
;-------------------------------
PlotDot: push di ; plot dot on screen, value in AX
mov di, offset PlotWin2
shr ax, 2
neg ax
add ax, [di.Win_hgt]
mov [di.CurrY], ax
mov ah, [di.TxtCol]
call SetColr
call PlotPix
inc [di.CurrX]
cmp [di.CurrX], 512 + 50
IF NB or [Flags], StopNow
pop di
ret
;-------------------------------
L2: and [Flags], not Running ; disable sampling during INT 1C
call ClrAltMous ; disable mouse interrupt
call PowDown ; ADC off
ret
L0: call RelButt
ret
DoRun: test [MouseB], LftButton
jz L0
call PowerUp ; switch ADC on
call RdWord ; retrieve bogovalue
call RelButt ; make sure no mouse button depressed anymore
clr ax
mov [AdcCount], ax
mov [RunTotal], ax
mov [Counter], ax
mov [PlotWin2.CurrX], 50
test [Flags], TrigWait ; wait for trigger?
IF NZ call Trigger
and [Flags], not StopNow ; clear StopNow flag
call SetAltMous ; install mouse interruptor AND GO
or [Flags], Running
L1: test [Flags], StopNow ; mouse button pressed?
jnz L2 ; if so, stop sampling
test [Flags], TimeOut ; time to take a sample?
jz L1 ; if not, wait for it
and [Flags], not TimeOut ; reset TimeOut flag
call RdWord ; get value from ADC
mov [BarVal.Now], ax ; use this value for the bargraph
inc [AdcCount]
add ax, [RunTotal] ; summ it up
mov [RunTotal], ax ; store it
mov cx, [AdcCount] ; group filled up yet?
cmp cx, [GroupS]
jb L1 ; if not, loop back
mov cx, [ShiftIt] ; else prepare to ...
cmp cx, 0
IF NZ shr ax, cl ; ... average it
mov [AvgVal], ax ; store average for plotting and digital readout
call PlotDot ; and plot it in the Plotting Window
clr ax
mov [RunTotal], ax ; reset running total ...
mov [AdcCount], ax ; ... and current count
test [Flags], RefrshBar
IF NZ call ShowBar
test [Flags], RefrshDig
IF NZ call ShowDig
and [Flags], not (RefrshDig + RefrshBar)
jmp L1 ; and do some more waiting
;-------------------------------
L0: call RelButt ; wait
; call SaveDef ; save current settings
pop ax ; adjust stack
jmp exit ; and exit
DoQuit: test [MouseB], RgtButton ; exit to DOS
jnz L0 ; right button pressed?
call RelButt ; if not, wait
ret ; and return
;-------------------------------
SetFile: ret
;-------------------------------
L0: mov [ComBase], ax
mov ax, [ComNr]
add al, '1'
mov [ParaCom], al
or [Flags], RefrPara
L2: call RelButt ; wait until button is released
call ParRfsh ; refresh screen
ret ; and exit
SetComm: test [Flags], JustUpd8 ; just refreshing?
jnz >L1
test [MouseB], LftButton
jz L2
inc [ComNr]
L1: call FindPort
cmp ax, 0
jne L0
mov [ComNr], ax
jmp L1
;-------------------------------
L0: call PowDown
L1: call ParRfsh
L2: call RelButt
ret
SetPowr: test [MouseB], LftButton
jz L2
test [Flags], PowerFlag ; is it "ON "?
jnz L0
call PowerUp
jmp L1
;-------------------------------
L0: call RelButt
or [Flags], RefrPara
L1: call ParRfsh
pop di, si, cx
ret
SetStop: push cx, si, di
mov ax, [StopIt]
test [Flags], JustUpd8 ; just refreshing?
jnz >L2
test [MouseB], LftButton + RgtButton
IF Z jmp L1
test [MouseB], LftButton
IF NZ inc ax
test [MouseB], RgtButton
IF NZ dec ax
cmp ax, 7
IF E clr ax
cmp ax, 0FFFF
IF E mov ax, 6
mov [StopIt], ax
L2: shl ax, 3
mov si, offset TrigStop
add si, ax
mov di, offset ParaStop
mov cx, 8
mov es, ds
rep movsb
jmp L0
;-------------------------------
SetSpd: push ax, cx, si, di
mov ax, [SmpRate] ; ax = nr of samples per second
mov cx, [GroupS] ; cx = nr of samples per group
cmp ax, cx
IF B xchg ax, cx ; swap params if necessary
clr dx
div cx
mov [MathRate], ax ; store MATHEMATICAL samplerate
call hexdec
inc si
mov di, offset ParaSpd
mov cx, 5
rep movsb
mov al, '/'
mov cx, [SmpRate]
cmp cx, [GroupS]
IF B mov al, ' '
stosb
or [Flags], RefrPara
pop di, si, cx, ax
ret
;-------------------------------
Build the four '7 segment digits' and the bargraph.
DoDigit: push bx, cx, si, es ; display digit in al to screen.
mov [DigBuff+4][bx], al ; store new value for later
push ax
mov ah, [di.BckCol]
call SetColr
mov ah, 0FF
call Setmask
mov es, [VGA_base] ; calculate address in VGA memory
mov ax, [di.Win_Y]
call [di.Multiply]
mov bx, [di.Win_X]
shr bx, 3
add bx, ax ; es:bx = index address
mov [VidPoint], bx ; store bx pointer
pop ax
sub al, '0'
IF C mov al, 10 ; if SPACE, use "rub out pattern"
clr ah
mov cx, 10
mul cx
mov si, ax
add si, offset DigMap
L0: push cx
mov cx, 4
lodsb ; get bitpattern
L1: push cx
clr ah
shl ax, 2
push ax ; save for later
xchg ah, al
mov bx, offset BitTable
xlat ; al = bit pattern
mov bx, [VidPoint]
mov cx, 4
L2: mov ah, [es:bx]
mov [es:bx], al
add bx, 80
loop L2 ; 4 fractions plotted
sub bx, 320 - 1 ; point to next V-ram address
mov [VidPoint], bx
pop ax, cx
loop L1
add bx, 320 - 4
mov [VidPoint], bx
pop cx
loop L0
pop es, si, cx, bx
ret
;-------------------------------
DelDig: push ax, bx, cx, es ; delete digit, optimized for speed
mov ah, 0FF
call SetColr ; erase all bitplanes
mov ah, 0FF
call SetMask ; use eight bits per byte
mov es, [VGA_base] ; calculate address in VGA memory
mov ax, [di.Win_Y]
call [di.Multiply]
mov bx, [di.Win_X]
shr bx, 3
add bx, ax ; bx = index address
mov cx, 40
clr al
L0: push cx
mov cx, 4
L1: mov ah, [es:bx]
mov [es:bx], al
inc bx
loop L1
add bx, 80 - 4
pop cx
loop L0
pop es, cx, bx, ax
ret
;-------------------------------
L0: ; and [Flags], not Upd8Digs
pop di, si, cx, bx, ax
ret
ShowDig: test [Flags], Upd8Digs ; Put actual value on screen
jz ret
push ax, bx, cx, si, di
; mov ax, [AvgVal.Now]
; cmp ax, [AvgVal.Prev] ; same as before?
; je L0 ; if so, quit
; mov [AvgVal.Prev], ax ; else update .Prev
; mov ax, [AvgVal] ; get avg Count [0..1023]
mov ax, [BarVal.Now]
mul [Vref] ; make mV's [0..5.250.000]
shl dx, 6
shr ax, 10
or ax, dx ; = divide by 1024 [0..5.100]
mul [Scale]
mov si, 100
div si ; correct for hardware [0..25.000]
mov [ThisVal.Now], ax ; store result
call hexdec ; convert Ax value
inc si
mov di, offset DigSpac2
mov [di.BckCol], 14
cmp [ThisVal.Now], 9999 ; 4 or 5 digit value?
ja >L1
inc si ; if 4 digits, skip over leading space
cmp [ThisVal.Prev], 9999
IF A call DelDig ; if necessary, erase old colour
mov [di.BckCol], 12
L1: mov di, offset DigBuff ; store results
mov cx, 4
L2: lodsb
cmp al, ' '
IF E mov al, '0' ; cvt all spaces to zero's
stosb
loop L2
mov ax, [ThisVal.Now]
mov [ThisVal.Prev], ax
clr bx ; set up index pointer
mov di, offset DigSpac1
mov al, [DigBuff][bx]
cmp al, [DigBuff+4][bx] ; same as current digit?
IF NE call DoDigit
inc bx
mov di, offset DigSpac2
mov al, [DigBuff][bx]
cmp al, [DigBuff+4][bx] ; same as current digit?
IF NE call DoDigit
inc bx
mov di, offset DigSpac3
mov al, [DigBuff][bx]
cmp al, [DigBuff+4][bx] ; same as current digit?
IF NE call DoDigit
inc bx
mov di, offset DigSpac4
mov al, [DigBuff][bx]
cmp al, [DigBuff+4][bx] ; same as current digit?
IF NE call DoDigit
jmp L0
;-------------------------------
PlotSeg: push ax, bx, cx, es ; plot one segment of the BarGraph
mov cx, [di.CurrX] ; calculate plottingmask
add cx, [di.Win_X]
mov bx, cx ; save copy for later
and cx, 0111xB
mov ah, 080
shr ah, cl ; determine bit to plot
call SetMask ; set mask
mov es, [VGA_base] ; calculate address in VGA memory
mov ax, [di.CurrY]
add ax, [di.Win_Y]
call [di.Multiply]
shr bx, 3 ; bx = CurrX + Win_X / 8
add bx, ax ; bx = index address in VGA memory
mov cx, BarLines
mov al, [ToPlot]
L1: mov ah, [es:bx] ; Do the actual plotting
mov [es:bx], al
add bx, 80
loop L1
add [di.CurrX], 2
pop es, cx, bx, ax
ret
;-------------------------------
L0: pop di, cx, bx, ax
ret
ShowBar: push ax, bx, cx, di ; Put bargraph display on screen
mov ax, [BarVal.Now] ; get ADC-count [0..1023]
cmp ax, [BarVal.Prev]
je L0 ; current value same as previous value?
mov [BarVal.Prev], ax ; store new value of "Previous"
shr ax, 2 ; cvt ADC-count to [0..255]
mov [Segments.Now], ax ; ... store value
cmp ax, [Segments.Prev]
je L0 ; if same Bar-length, exit
mov di, offset DisplBar ; else, ...
mov ah, [di.BckCol]
call SetColr ; ... set colour, and ...
mov ax, [Segments.Now] ; restore current value
cmp ax, [Segments.Prev] ; is new val above/below previous one?
ja >L2 ; if ax > Segments, fill up bar
mov cx, [Segments.Prev] ; else erase superfluous segments
sub cx, ax ; calculate count
shl ax, 1 ; correct for bar-type
mov [di.CurrX], ax ; store coordinates
mov [di.CurrY], 0
mov [ToPlot], 0 ; determine plot/ERASE
L1: call PlotSeg ; plot the bar-line
loop L1
mov [ToPlot], 0FF ; determine PLOT/erase
jmp >L4
L2: mov cx, ax
mov bx, [Segments.Prev]
sub cx, bx ; determine COUNT
shl bx, 1
mov [di.CurrX], bx ; store coordinates
mov [di.CurrY], 0
L3: call PlotSeg
loop L3
L4: mov ax, [Segments.Now]
mov [Segments.Prev], ax ; remember current bar-length
test [Flags], RfrshBar ; time to refresh the bar-markers?
jz L0 ; if not, exit
mov cx, 4
mov [di.CurrY], BarLines
L5: push cx
mov [di.CurrX], 0
mov cx, 6
L6: call PlotPix
inc [di.CurrX]
call PlotPix
add [di.CurrX], 101
loop L6
inc [di.CurrY]
pop cx
loop L5
and [Flags], not RfrshBar
jmp L0
;-------------------------------
BrScale: push ax, cx, di ; make a scale on the Bar Graph
mov di, offset DisplBar
mov ah, [di.BckCol]
call SetColr
mov cx, 4
mov [di.CurrY], BarLines
L5: push cx
mov [di.CurrX], 0
mov cx, 6
L6: call PlotPix
inc [di.CurrX]
call PlotPix
add [di.CurrX], 101
loop L6
inc [di.CurrY]
pop cx
loop L5
and [Flags], not RfrshBar
pop di, cx, ax
ret
;-------------------------------
Enable the user to set Vref/Scale by means of sliders.
ShowVref: ; Show actual value of V-ref pointer
push cx, dx, si, di
call GetMous
mov ax, [MouseX]
sub ax, [di.Win_X]
add ax, 4750 - 20 ; make millivolts
mov dx, ax
call hexdec ; si = -> ASCII buffer
add si, 2
mov cx, si ; store si for later
mov di, offset VrefIs
movsb ; store results in Vref window
Store ','
movsb
movsb
movsb
pop di ; restore colour info
mov si, offset VrefSet
call print ; print in SetVref window
test [MouseB], LftButton ; if right button pressed, exit
jz >L0
push di
mov si, cx ; get old SI
mov di, offset ParaVref
movsb ; store results in Parameters window
Store ','
movsb
movsb
movsb
mov [Vref], dx
pop di
or [Flags], RefrPara
L0: pop si, dx, cx
ret
;-------------------------------
ParRfsh: test [Flags], RefrPara + JustUpd8
jz ret
push si, di
call SetStat
mov di, offset ParWin ; refresh parameter screen
mov si, offset params
call print
call ShowBar
and [Flags], not RefrPara
pop di, si
ret
;-------------------------------
ShowScale: ; Show actual value of Scale-pointer
push cx, dx, si, di
call GetMous
mov ax, [MouseX]
sub ax, [di.Win_X]
sub ax, 20 ; make scalefactor
mov dx, ax
call hexdec ; si = -> ASCII buffer
add si, 3
mov cx, si ; store si for later
mov di, offset ScaleIs
lodsb
cmp al, ' '
IF E mov al, '0'
stosb
Store ','
lodsb
cmp al, ' '
IF E mov al, '0'
stosb
movsb
pop di ; restore colour info
mov si, offset ScalSet
call print ; print in SetVref window
test [MouseB], LftButton ; if right button pressed, exit
jz >L0
push di
mov si, cx ; get old SI
mov di, offset ParaScal
lodsb ; store results in Parameters window
cmp al, ' '
IF E mov al, '0'
stosb
Store ','
lodsb
cmp al, ' '
IF E mov al, '0'
stosb
movsb
mov [Scale], dx
pop di
or [Flags], RefrPara
L0: pop si, dx, cx
ret
;-------------------------------
MakeAxis: ; make nice dented horizontal axis
push ax, cx, dx
mov dx, [di.CurrY]
mov [di.DeltaY], 8
mov ax, [TotalDiv]
div b [SubDiv]
mov cx, ax
L0: push cx
call VertLin
mov cx, [SubDiv]
shr cx, 1
L1: call PlotPix
add [di.CurrX], 2
loop L1
mov [di.CurrY], dx
pop cx
loop L0
mov [di.CurrY], dx
call VertLin
pop dx, cx, ax
ret
;-------------------------------
ShowFS: push ax, bx, dx, si, di ; show Full Scale on screen
pushf
mov ax, [Scale]
mul [Vref]
mov bx, 100
div bx ; ax = Full Scale voltage
mov [FSval], ax
call hexdec
add si, 4
mov di, offset ParaFull + 4
std
movsb
movsb
Store ','
lodsb
cmp al, ' '
IF E mov al, '0'
stosb
movsb
or [Flags], RefrPara
popf
pop di, si, dx, bx, ax
ret
;-------------------------------
SetVref: test [MouseB], LftButton ; set V-reference
jz ret ; if nothing to do, get out
push ax, cx, dx, si, di
or [Flags], RfrshBar ; signal: REFRESH needed
mov [Segments.Prev], 0
mov [BarVal.Prev], 0
call RelButt ; wait for button release
mov di, offset VrefBar
HideMouse
call DelWin ; erase old contents
call MakeWin ; make new background
call MakeBox ; fancy lines
push [MouseX], [MouseY]
mov cx, [di.Win_X]
mov dx, cx
add cx, 20
add dx, [di.Win_wid]
sub dx, 20
mov ax, 7
int 033 ; set horizontal boundaries
mov cx, [di.Win_Y]
add cx, 33
mov dx, cx
mov ax, 8
int 033 ; set vertical boundaries
mov cx, [di.Win_X]
add cx, [Vref]
sub cx, 4750 - 20
mov dx, [di.Win_Y]
add dx, 33
mov ax, 4
int 033 ; set mouse cursor
mov si, offset VrefSet
call print
mov [di.CurrX], 20
mov [di.CurrY], 25
mov [SubDiv], 50
mov [TotalDiv], 500
call MakeAxis
ShowMouse
L0: call ShowVref
test [MouseB], LftButton + RgtButton
jz L0
call RelButt
HideMouse ; to prevent odd blocks popping up
mov cx, 0
mov dx, 639
mov ax, 7
int 033 ; mouse boundaries back ...
mov cx, 0
mov dx, 479
mov ax, 8
int 033 ; ... to normal
pop dx, cx
mov ax, 4
int 033
call DelWin
or [Flags], Upd8Digs
mov w [DigBuff][4], ' '
mov w [DigBuff][6], ' ' ; clear out DigBuff.Prev
call ShowFS
call ParRfsh
ShowMouse ; show the mouse again
pop di, si, dx, cx, ax
ret
;-------------------------------
SetScal: test [MouseB], LftButton ; set scale factor
jz ret ; if nothing to do, get out
push ax, cx, dx, si, di
or [Flags], RfrshBar
mov [Segments.Prev], 0
mov [BarVal.Prev], 0
call RelButt ; wait for button release
mov di, offset ScaleBar
HideMouse
call DelWin ; erase old contents
call MakeWin ; make new background
call MakeBox ; fancy lines
push [MouseX], [MouseY]
mov cx, [di.Win_X]
mov dx, cx
add cx, 20 + 18
add dx, [di.Win_wid]
sub dx, 20
mov ax, 7
int 033 ; set horizontal boundaries
mov cx, [di.Win_Y]
add cx, 33
mov dx, cx
mov ax, 8
int 033 ; set vertical boundaries
mov cx, [di.Win_X]
add cx, [Scale]
add cx, 20
mov dx, [di.Win_Y]
add dx, 33
mov ax, 4
int 033 ; set mouse cursor
mov si, offset ScalSet
call print
mov [di.CurrX], 20
mov [di.CurrY], 25
mov [SubDiv], 100
mov [TotalDiv], 500
call MakeAxis
ShowMouse
L0: call ShowScale
test [MouseB], LftButton + RgtButton
jz L0
call RelButt
HideMouse ; to prevent odd blocks popping up
mov cx, 0
mov dx, 639
mov ax, 7
int 033 ; mouse boundaries back ...
mov cx, 0
mov dx, 479
mov ax, 8
int 033 ; ... to normal
pop dx, cx
mov ax, 4
int 033
call DelWin
call ShowFS
or [Flags], Upd8Digs
mov w [DigBuff][4], ' '
mov w [DigBuff][6], ' ' ; clear out DigBuff.Prev
call ParRfsh
ShowMouse ; show the mouse again
pop di, si, dx, cx, ax
ret
;-------------------------------
Control some more options on screen and update screen.
L1: mov [SmpRate], ax
call hexdec
inc si
mov di, offset ParaRate
mov cx, 5
rep movsb
mov ax, 'S '
stosw
mov ax, 'sp'
stosw
or [Flags], RefrPara
call SetSpd
call ParRfsh
L2: call RelButt
pop di, si, cx
ret
SetRate: push cx, si, di
test [MouseB], LftButton + RgtButton
jz L2
mov ax, [SmpRate]
test [MouseB], LftButton ; if LEFT button, ...
jz >L5
shl ax, 1 ; ... double the rate
jmp >L6
L5: shr ax, 1 ; if RIGHT button, ...
L6: cmp ax, 0 ; if too low, ...
jne >L3
mov ax, 1 ; ... fill with lowest value
jmp L1
L3: cmp ax, [MaxRate.Prev] ; if MaxRate, ...
jna >L4
mov ax, [MaxRate] ; ... fill it in
jmp L1
L4: mov si, [MaxRate] ; if below MaxRate,
shr si, 1
cmp ax, si
IF E mov ax, [MaxRate.Prev] ; switch to multiples of 2
jmp L1
;-------------------------------
L0: mov [GroupS], ax
call hexdec
add si, 4
mov di, offset ParaGrup
mov cx, 2
rep movsb
or [Flags], RefrPara
call SetSpd
call ParRfsh
L1: call RelButt
pop di, si, cx
ret
L2: shr ax, 1 ; so it is the right button!
dec [ShiftIt]
cmp ax, 0
ja L0
mov ax, 1
mov [ShiftIt], 0
jmp L0
SetGrup: push cx, si, di
test [MouseB], LftButton + RgtButton
jz L1
mov ax, [GroupS]
test [MouseB], LftButton
jz L2
shl ax, 1
inc [ShiftIt]
cmp ax, MaxGroup
jbe L0
mov ax, MaxGroup
mov [ShiftIt], MaxShift
jmp L0
;-------------------------------
L0: call RelButt
pop si, cx
ret
SetUnit: push cx, si
test [Flags], JustUpd8
jnz >L1
test [MouseB], LftButton + RgtButton
jz L0
L1: mov ax, [UnitIt]
test [MouseB], LftButton
IF NZ inc ax
test [MouseB], RgtButton
IF NZ dec ax
and ax, 011xB
mov [UnitIt], ax
mov si, offset Unitsss
add si, ax
mov al, [si]
mov [ParaUnit], al
or [Flags], RefrPara
call ParRfsh
jmp L0
;-------------------------------
L0: call RelButt ; wait till buttons released
pop di, si, cx ; and get out
ret
SetMode: push cx, si, di
test [Flags], JustUpd8 ; just updating?
jnz >L1 ; if so, skip the buttons part
test [MouseB], LftButton + RgtButton ; Button pressed?
jz L0 ; if not, exit
L1: mov ax, [ModeIt] ; get current value
test [MouseB], LftButton ; how to change?
IF NZ inc ax
test [MouseB], RgtButton
IF NZ dec ax
cmp ax, 4 ; out of bounds?
IF E clr ax
cmp ax, 0FFFF
IF E mov ax, 3
mov [ModeIt], ax
mov cx, ax
shl ax, 3
add ax, cx ; multiply by 9
mov si, offset DispMode
add si, ax
mov di, offset ParaMode
mov cx, 9
rep movsb
or [Flags], RefrPara
call ParRfsh
jmp L0
;-------------------------------
FillScreen:
mov di, offset fullscrn
call MakeBox ; compose main frame
mov di, offset PlotWin
call MakeBox ; make parameters windows
mov di, offset ParWin2
call MakeBox ; make plotting window
mov di, offset BotBar
mov si, offset copyleft
call Center ; print copyrights
mov di, offset TopBar
mov si, offset MenuBar
call Center ; print menu bar
call ShowFS
call ParRfsh
ret
;-------------------------------
Timing part I: slow signals.
Tune_W8: push ax, bx, cx, dx, si, di ; calibrate the wait-loop
mov [Delay], 419
mov di, offset time
mov si, di
mov [TrialRun], offset DoZip
call open_2 ; open hi res timer.
mov cx, 16 ; repeat 16 times
L0: call rd_time ; read timer, ...
call DoW8 ; ... wait specified time, and ...
call rd_time ; ... read timer again
loop L0 ; until 16 done
call close_2 ; close hi res timer
mov cx, 16
xor bx, bx ; bx = total
L1: lodsw ; get "start" count,
mov dx, ax ; store it
lodsw ; get "end" count,
sub dx, ax ; determine "time spent"
add bx, dx ; sum it all up
loop L1 ; until done
add bx, 8
shr bx, 4
mov ax, 500 ; get current value
mul [ShouldBe]
div bx ; compose better value
cmp ax, 0
IF E inc ax ; compensate for very S L O W machines....
mov [Delay], ax ; store new value
pop di, si, dx, cx, bx, ax
ret
;------------------------
DoW8: push cx ; timing routine
mov cx, [Delay]
L0: call [TrialRun]
loop L0
pop cx
ret
;-------------------------------
ChkTime: push si, di
mov [Delay], 250 ; initialize at 250 loop count
mov di, offset time
mov si, di
call open_2
mov dx, [ComBase] ; read in one 10-bit word from ADC
add dx, 6
mov [TrialRun], RdFast
call rd_time ; store (a) value
push di
call DoW8
pop di
call rd_time ; store (b) value
mov [TrialRun], DoZip
call rd_time ; store (c) value
push di
call DoW8
pop di
call rd_time ; store (d) value
call close_2
ddd: lodsw ; get startvalue (a)
mov bx, ax ; store it
lodsw
sub bx, ax ; subtract long-done value (b)
lodsw
sub ax, [si]
sub bx, ax ; bx = time until completion (a-b)-(c-d)
clr dx
mov ax, bx ; store it in DX:AX
mov bx, 298 ; divisor for time-in-æs
div bx ; ax = time in æs to complete ONE RdWord
mov [OneShot], ax ; store it
pop di, si
ret
;-------------------------------
Timing part II: sample speed and sample time.
MaxSpS: push ax, bx, dx ; determine maximum sample speed
mov dx, 15
mov ax, 16960 ; DX:AX = 1.000.000 [æs/s]
mov bx, [OneShot]
div bx ; AX = maximum sample rate
mov [MaxRate], ax
clr cl
L0: inc cl ; increment shiftcount
shr ax, 1 ; shift right
jnz L0 ; until AX empty
dec cl
mov ax, 1
shl ax, cl ; AX = 2nd highest sample rate
mov [MaxRate.Prev], ax
pop dx, bx, ax
ret
;-------------------------------
L0: mov bx, ax
shr bx, 1
add ax, bx
L1: mov [SampTime], ax
pop dx, bx, ax
ret
MaxTime: push ax, bx, dx ; determine maximum sample time
clr dx
mov ax, 08000
mov bx, [SmpRate]
div bx ; ax = nr of seconds to record
test [Flags], PackWord ; store 3 ADC-words in 4 bytes?
jz L1
jmp L0
;-------------------------------
SetSps: ret
;-------------------------------
New interrupt vectors.
L0: pop ds
jmp [cs:OldIRQ0]
NewIRQ0: push ds
cs mov ds, [DataSeg]
test [Flags], Running
jz L0
inc [Counter]
or [Flags], RefrshBar
test [Counter], 07
IF Z or [Flags], RefrshDig
jmp L0
;-------------------------------
NewIRQ8: push ax
mov al, 0C
out 070, al
in al, 071 ; clear interrupt flags
test [cs:Flags], Running ; are we running?
IF NZ or [cs:Flags], TimeOut ; if so, set TimeOut flag
mov al, 020
out 020, al
out 0A0, al
pop ax
iret
;-------------------------------
EnableNewIRQ8:
push ax
mov al, 0C
out 070, al
in al, 071 ; check register C first
mov al, 0A
out 070, al
mov al, 00100011xB ; select 1 kSps
out 071, al ; and set it in register A
mov al, 0B
out 070, al
mov al, 01000010xB
out 071, al ; and store it
in al, 0A1 ; get IRQ mask word
and al, not bit 0
out 0A1, al ; enable IRQ 8
pop ax
ret
;-------------------------------
ResetNewIRQ8:
push ax
mov al, 0A
out 070, al ; select register A
mov al, 00100110xB
out 071, al ; and set it back to PC default
mov al, 0B
out 070, al
mov al, 00000010xB
out 071, al
in al, 0A1 ; get IRQ mask word
or al, bit 0
out 0A1, al ; disable IRQ 8
pop ax
ret
;-------------------------------
Some initialisations.
SetVars: mov [ComBase], 03F8 ; init ComBase
mov [ShouldBe], 20 ; target value for timing = 20 æs
mov [SmpRate], 0400 ; init sample rate
mov [GroupS], 010 ; init group size
mov [ShiftIt], 4 ; and related shift count for averaging
mov [StopIt], 1 ; set to "Manual"
mov [ToPlot], 0FF
mov [FSval], 5100
mov [Scale], 100 ; Scale factor = 1,00
mov [Vref], 5100 ; prime V-ref with most probable value
ret
;-------------------------------
init: call SetVars ; init most import variables
call PowDown ; make sure ADC is OFF
call ClkLo ; prepare ADC for power-up
call ChkTime ; measure minimum sample time
call MaxSps ; determine maximum sample speed
mov ah, 0F
int 010 ; determine existing video mode
mov [VidMode], al ; store it
mov ax, 012
int 010 ; set 640 x 480 graphics
push es
mov ax, 0351C ; get old timervector
int 021
mov w [OldIRQ0], bx
mov w [OldIRQ0+2], es
mov dx, offset NewIRQ0
mov ax, 0251C
int 021 ; install new TIMER routine
mov ax, 03570
int 021
mov w [OldClock], bx
mov w [OldClock+2], es
mov dx, offset NewIRQ8
mov ax, 02570
int 021 ; install NewIRQ8 routine
call EnableNewIRQ8 ; and get it to work
pop es
mov ax, 0
int 033 ; init mouse
ShowMouse
call FillScreen
or [Flags], RfrshBar + RefrPara + Upd8Digs
call ShowDig
call BrScale
call Update
ret
;-------------------------------
The main routine for LDA.
main: mov cx, LastByte
mov di, FirstByte
sub cx, di
cld
rep stosb ; init all vars
mov ax, cs
mov [DataSeg], ax ;Â only for PROGRAM STRUCTURE
mov [CodeSeg], ax ;Ù
add ax, (LastByte/16) + 1
mov [BufSeg], ax
mov sp, offset stacktop
call init
L0: test [Flags], PowerFlag
jz >L1
call RdWord
mov [BarVal], ax
call ShowBar
or [Flags], Upd8Digs
call ShowDig
L1: call GetMous ; get mouse status
call FindClick
mov ah, 0B
int 021
cmp al, 0FF
jne L0
Exit from the LDA program and return to DOS.
exit: call PowDown
call ResetNewIRQ8
push ds
lds dx, [OldIRQ0]
mov ax, 0251C
int 021 ; restore timer vector
pop ds
push ds
lds dx, [OldClock]
mov ax, 02570
int 021 ; restore realtime clock vector
pop ds
mov ah, 0
mov al, [VidMode]
int 010 ; back to previous screenmode
mov ax, 0
int 033 ; reset mouse and -driver
mov ax, 04C00
int 021
;-------------------------------
Definitions to fill windows.
params: Topic 27, 9, 'COM : '
ParaCom db '1', 0
Topic 180, 9, 'Start : '
ParaStrt db 'Manual ', 0
Topic 369, 9, 'File : '
ParaFile db 'filename.ext ', 0
Topic 9, 28, 'Power : '
ParaPowr db 'OFF', 0
Topic 189, 28, 'Stop : '
ParaStop db 'Mem.Full', 0
Topic 351, 95, 'Status : '
ParaStat db 'Idle ', 0
Topic 189, 55, 'Rate : '
ParaRate db ' 1024 Sps ', 0
Topic 360, 55, 'Group : '
ParaGrup db '16 ', 0
Topic 483, 55, 'Speed : '
ParaSpd db ' 64/s', 0
Topic 492, 72, 'Vref : '
ParaVref db '5,100 V', 0
Topic 189, 72, 'Unit : '
ParaUnit db 'V ', 0
Topic 360, 72, 'Scale : '
ParaScal db '1,00 x', 0
Topic 189, 95, 'Mode : '
ParaMode db 'DMM ', 0
Topic 520, 120, '% FS '
ParaFull db '..... ', 0
TopicEnd
VrefSet: Topic 4, 2, '4,75', 0
Topic 500, 2, '5,25', 0
dw 250, 2
VrefIs db '5,000', 0
TopicEnd
ScalSet: Topic 16, 2, '0', 0
Topic 116, 2, '1', 0
Topic 216, 2, '2', 0
Topic 316, 2, '3', 0
Topic 416, 2, '4', 0
Topic 516, 2, '5', 0
dw 250, 2
ScaleIs db ' 1,0', 0
TopicEnd
SamStat db 'Idle ' ; Status
db 'Sampling'
db 'Waiting '
db 'Ready '
db 'Single '
DispMode db 'Voltmeter' ; Display method
db 'Recorder '
db 'Histogram'
db 'Statistix'
TrigStop db 'Mem.Full'
TrigStrt db 'Manual '
db 'Delta + '
db 'Delta - '
db 'Value '
db 'Up '
db 'Down '
Unitsss db 'VAFê',0
copyleft db 'A-klasse (C)_1998 NL-5012_GH__272 [aklasse@tip.nl]', 0
MenuBar db 'LD-ADC: Save Load _Cls Run Quit', 0
BitTable db 000, 00F, 0F0, 0FF ; translation table for ShowDig
even
clix1 dw 148, 6, 183, 16 ; Save ¿
dw 262, 6, 296, 16 ; Load ³
dw 373, 6, 407, 16 ; Cls ÃÄ Menu bar
dw 485, 6, 511, 16 ; Run ³
dw 588, 6, 622, 16 ; Quit Ù
dw 13, 59, 55, 70 ; Power
dw 364, 103, 407, 114 ; Scale
dw 495, 103, 530, 114 ; V-ref
dw 32, 40, 56, 50 ; COM
dw 184, 40, 228, 50 ; Start
dw 193, 59, 226, 70 ; Stop
dw 193, 86, 228, 98 ; Rate
dw 364, 86, 407, 98 ; Group
dw 193, 103, 228, 114 ; Unit
dw 193, 126, 228, 138 ; Mode
dw 373, 40, 410, 50 ; File
dw 0F0F0 ; end of table
ParaMous dw DoSave, DoLoad, WinClr, DoRun, DoQuit
dw SetPowr, SetScal, SetVref, SetComm, SetStrt
dw SetStop, SetRate, SetGrup, SetUnit, SetMode
dw SetFile
DigMap: db 03C, 8 dup (042), 03C ; '0'
db 018, 038, 008, 008, 008, 008, 008, 008, 008, 03E ; '1'
db 03C, 002, 002, 002, 03C, 040, 040, 040, 040, 03C ; '2'
db 03C, 002, 002, 002, 01C, 002, 002, 002, 002, 03C ; '3'
db 000, 042, 042, 042, 042, 03C, 002, 002, 002, 002 ; '4'
db 03C, 040, 040, 040, 03C, 002, 002, 002, 002, 03C ; '5'
db 03C, 040, 040, 040, 03C, 042, 042, 042, 042, 03C ; '6'
db 07C, 002, 002, 004, 008, 010, 010, 010, 010, 010 ; '7'
db 03C, 042, 042, 042, 03C, 042, 042, 042, 042, 03C ; '8'
db 03C, 042, 042, 042, 03C, 002, 002, 002, 002, 03C ; '9'
db 10 dup (000) ; ' '
CFG_file db 'LDA0$#$#.CFG', 0
DEF_file db 'LDA0$#$#.DEF', 0
INCLUDE bitmap.a86
Epilog.
Go get yourself an old Rievo 'Luegendetector' on Ebay or build one for yourself based on the SOAP drawings in the Soap section of fruttenboel. The SOAP project was influenced by the Rievo detector. Soap is superior since it's faster and completely isolated.
Project designed around 1998 and
Page equipped with FroogleBuster technology