From:   IN%"MERTUS@BROWNCOG.BITNET" 17-APR-1992 05:09
To: IN%"kgreen@CCIT.ARIZONA.EDU"
CC: 
Subj:   dt2821.asm

Return-path: <MERTUS@BROWNCOG.BITNET>
Received: from Arizona.edu (Osprey.Telcom.Arizona.EDU) by CCIT.ARIZONA.EDU
 (PMDF #12663) id <01GIXXCBYM7K90O09E@CCIT.ARIZONA.EDU>; Fri,
 17 Apr 1992 05:09 MST
Received: from BROWNCOG.BITNET (MERTUS@BROWNCOG) by Arizona.edu (PMDF #12663)
 id <01GIXXB5LM0WA9RZUS@Arizona.edu>; Fri, 17 Apr 1992 05:08 MST
Received: from BROWNCOG.BITNET by BROWNCOG.BITNET (PMDF #12572) id
 <01GIY3JU9DEC0008YO@BROWNCOG.BITNET>; Fri, 17 Apr 1992 08:07 EST
Date: Fri, 17 Apr 1992 08:07 EST
From: MERTUS@BROWNCOG.BITNET
Subject: dt2821.asm
To: kgreen@CCIT.ARIZONA.EDU
Message-id: <01GIY3JU9DEC0008YO@BROWNCOG.BITNET>
X-Envelope-to: kgreen
X-VMS-To: IN%"kgreen@rvax.ccit.arizona.edu"

;
;  File Name:  DT2821.ASM V 2.01
;  Created:     7-JAN-1989  2:54 on Compaq III by John Mertus
;  Revision #2: 17-APR-1992 08:07 on the uVAX
;
;*********************************************************************
;
;        This is the driver program for the DT2821 board.  It only
;  supports DMA and input.
;
;  NOTE:  This software is provided for noncommerical uses only.
;  Any commerical use without my written permission is and will
;  be considered a violation of copyright laws.
;
;                                    -John Mertus  Jan 89
;
;  Edit History.  V 1.01  Inital release for D/A
;                 V 1.02  Added A/D routines
;                 V 2.01  Went to 2.0 format
;
;*********************************************************************
;
        TITLE  DT2821 LIBRARY
;
        .MODEL COMPACT, C
;
; Equates and macros
;
        bit0    EQU     1H              ; lsb
        bit1    EQU     2H              ; bit 1 value
        bit2    EQU     4H              ; bit 2 value
        bit3    EQU     8H              ; bit 3 value
        bit4    EQU     10H             ; bit 4 value
        bit5    EQU     20H             ; bit 5 value
        bit6    EQU     40H             ; bit 6 value
        bit7    EQU     80H             ; bit 7 value
        bit8    EQU     100H            ; bit 8 value
        bit9    EQU     200H            ; bit 9 value
        bit10   EQU     400H            ; bit 10 value
        bit11   EQU     800H            ; bit 11 value
        bit12   EQU     1000H           ; bit 12 value
        bit13   EQU     2000H           ; bit 13 value
        bit14   EQU     4000H           ; bit 14 value
        bit15   EQU     8000H           ; msb
;
; DMA register constants
;
        PageReg5   EQU  8BH             ; DMA Page Select, channel 5
        PageReg6   EQU  89H             ; DMA Page Select, channel 6
        PageReg7   EQU  8AH             ; DMA Page Select, channel 7
 
        BaseReg5   EQU  0C4H            ; Base Address, channel 5
        BaseReg6   EQU  0C8H            ; Base Address, channel 6
        BaseReg7   EQU  0CCH            ; Base Address, channel 7
 
        CountReg5  EQU  0C6H            ; Word Count, channel 5
        CountReg6  EQU  0CAH            ; Word Count, channel 6
        CountReg7  EQU  0CEH            ; Word Count, channel 7
 
        MaskReg    EQU  0D4H            ; Mask Register
        ModeReg    EQU  0D6H            ; Mode Register
        FlipFlop   EQU  0D8H            ; Byte Pointer Flip/Flop
;
; modes for DMA
;
        read_DMA   EQU  48H             ; DMA read, noauto
        write_DMA  EQU  44H             ; DMA write, noauto
;
; Interrupt register constants
;
        IntCmdMaster  EQU 020H          ; Cmd reg address for master 8259
        IntCmdSlave   EQU 0A0H          ; Cmd reg address for slave 8259
        IntEOI        EQU 020H          ; Non-specific END OF INTERRUPT cmd
;
;  Base vector address
;
        BASE    EQU     240H             ; Base register
 
;
;----------------------------------------------------------------------
;
;   Offset for the C subroutines in the LOADER.C subroutine
;
        DAD_INIT        EQU     0
        DAD_RESET       EQU     1
        DAD_ERROR       EQU     2
        DA_SETC         EQU     3
        DA_STOP         EQU     4
        DA_CLOCK        EQU     5
        DA_STATUS       EQU     6
        DA_WOB          EQU     7
        DA_DMA          EQU     8
        DA_START        EQU     9
        AD_STOP         EQU     10
        AD_CLOCK        EQU     11
        AD_STATUS       EQU     12
        AD_WOB          EQU     13
        AD_DMA          EQU     14
        AD_START        EQU     15
        AD_SETC         EQU     16
;
;   Data segment.  This contains all the pointers to the subroutines
;  and all the global variables.
;
        .DATA
;
;  All local data is in the code segment.
;
;  Code starts here
;
        .CODE
        ASSUME CS:_TEXT, DS:NOTHING, ES:NOTHING, SS: NOTHING
 
        ORG 100h
;
;  Get the routine offsets.  This is fixed as follows:
;       First word, offset of GLOBAL data
;       Next  word, number of routines defined
;          Pairs (num,routine)
;
;       Each pair has two words, the first word is the routine number
;       as expected and defined by DTLIBDEF.I, and the second is
;       the offset of the routine.
;
DUMMY:  DW ID_BYTE                      ; Global data start
        DW 16                           ; Number of routines
        DW dt_reset,DAD_RESET
        DW DAD_error_msg,DAD_ERROR
 
        DW da_clear,DA_STOP
        DW DA_set_channels, DA_SETC
        DW DAD_bf_status,DA_STATUS
        DW set_clock,DA_CLOCK
        DW wait_on_buffer,DA_WOB
        DW DA_setup_dma,DA_DMA
        DW start_output,DA_START
 
        DW ad_clear,AD_STOP
        DW DAD_bf_status,AD_STATUS
        DW set_clock,AD_CLOCK
        DW wait_on_buffer,AD_WOB
        DW AD_setup_dma,AD_DMA
        DW start_input,AD_START
        DW AD_set_channels, AD_SETC
;
;------------------------------------------------------------------------
; start of global data set via the CONFIG program
;
;  These variables must be loaded from the constants to define the DMA
;  channel used, it is the responsiblity of DTCONFIG to initialize them
;  properly.
;
ID_BYTE         DB      'DT2821   ',0
VERSION         DB 2,1          ; Version number High, Low
CENTER          DW 2048         ; Center line
                DW 0,0,0        ; For expansion
;
; Start of global data
;
ADCSR           DW BASE         ;  A/D control register R/W
CHANCSR         DW BASE + 2     ;  A/D channel list control register
ADDAT           DW BASE + 4     ;  A/D data register RO
DACSR           DW BASE + 6     ;  D/A and DIO control register R/W
DADAT           DW BASE + 8     ;  D/A data register WO
DIODAT          DW BASE + 10    ;  DIO data register R/W
SUPCSR          DW BASE + 12    ;  DMA control register R/W
TMRCTR          DW BASE + 14    ;  Timer/Counter register R/W
;
;  DMA channels
;
modebit         EQU     0
pagereg         EQU     2
basereg         EQU     4
countreg        EQU     6
dmapage         EQU     8
dmaoffset       EQU     10
;
;  This contains all the information about the DMA tables
;
DMA_TABLE       DW      1               ; ModebitA
                DW      PageReg5        ; PageRegA
                DW      BaseReg5        ; BaseRegA
                DW      CountReg5       ; CountRegA
PageA           DW      0               ; Page for buffer A
OffA            DW      0               ; the Base Offset from PageA
 
                DW      2               ; ModebitB
                DW      PageReg6        ; PageRegB
                DW      BaseReg6        ; BaseRegB
                DW      CountReg6       ; CountRegB
PageB           DW      0               ; Page for buffer B
OffB            DW      0               ; Base offset from Page B
;
; Interrupt definitions pre-defined for interrupt level 15 (FC)
;
IntMaskReg      DW      0A1H    ; 8259 Mask Register address
intvector       DW      077H    ; Vector number
IRQ_line        DW      080H    ; IRQ bit value of Mask
;
;------------------------------------------------------------------------
;
;  Local data storage
;
;
;  zero
;
BUFFER_SIZE     DW         0            ; Buffer size in WORDS
CLOCKF          DB         1            ; CLock factor
SCHAN           DB         0            ; Start channel
ExitDMA         DW         0            ; <> 0 means exit DMA
Error_word      DW         0            ; Multibit error word
;
;******** Error codes ********
;  Every error is assigned a separate bit on the error word.  At a
;  DA clear this word should be cleared.
;
        DMA_DONE        EQU 1H          ;  Sucessfull compleation
        DA_ERROR        EQU 2H          ;  DA Hardware error
        AD_ERROR        EQU 4h          ;  AD hardware error
        LIST_ERROR      EQU 8h          ;  Channel gain list error
        ERR_CTRL_C      EQU 10h         ;  ^C abort
        ODDBUFFER       EQU 2000H       ; Buffer must be word aligned
        NOCLEAR         EQU 4000H       ; D/A ready will not clear
;
;  error table
;
ERR_TABLE       DW ERR_1, ERR_2, ERR_4, ERR_8, ERR_10, ERR_20
                DW ERR_40, ERR_80, ERR_100, ERR_200, ERR_400
                DW ERR_800, ERR_1000, ERR_2000, ERR_4000, ERR_8000
;
;  Error report will create this error string
;
ERROR_STRING    DB '?DT2821-Error'
NULL            DB 0
;
;  error messages
;
ERR_1   DB '-DMA has completed',0
ERR_2   DB '-D/A hardware conversion error',0
ERR_4   DB '-A/D hardware conversion error',0
ERR_8   DB '-Channel list error',0
ERR_10  DB '-',0
ERR_20  DB '-',0
ERR_40  DB '-',0
ERR_80  DB '-',0
ERR_100 DB '-',0
ERR_200 DB '-',0
ERR_400 DB '-',0
ERR_800 DB '-',0
ERR_1000 DB '-',0
ERR_2000 DB '-Buffer must be word aligned',0
ERR_4000 DB '-D/A ready bit will not clear',0
ERR_8000 DB '-',0
;
;  Start of code
;
        LOCALS $$
;
;-----------set_dma_buffer-----------------John Mertus  January 89----
;
;   This sets up the dma buffer.  BX comes in with the buffer number
;   0 or 1.  AX has the DMA mode in it, i.e. READ or WRITE.
;
;   AX, BX and DX are destroyed.
;
;   Will allways return success, no carry bit set
;
;
set_dma_buffer PROC
;
;  Compute which part of the table to use (there are easier ways to
;  to this!)
;
        push    ax
        mov     ax,bx
        mov     bx,12
        mul     bl
        mov     bx,ax
        pop     ax
 
        or      ax,cs:DMA_TABLE[bx + modebit]   ; Get mode
        mov     dx,ModeReg                      ; Port
        out     dx,al                           ; set the mode
;
;  set the offset
;
        mov     dx,FlipFlop                     ; clear flip flop flag
        xor     ax,ax
        out     dx,al
 
        mov     ax,cs:DMA_TABLE[bx + dmaoffset]
        mov     dx,cs:DMA_TABLE[bx + basereg]
        out     dx,al
        mov     al,ah
        out     dx,al
;
;  Output the size
;
        mov     ax,BUFFER_SIZE
        mov     dx,cs:DMA_TABLE[bx + countreg]
        out     dx,al
        mov     al,ah
        out     dx,al
;
;  Get page
;
        mov     ax,cs:DMA_TABLE[bx + dmapage]
        mov     dx,cs:DMA_TABLE[bx + pagereg]
        out     dx,al
;
;  enable DMA channel
;
        mov     ax,cs:DMA_TABLE[bx + modebit]
        mov     dx,MaskReg
        out     dx,al
;
;  Go home
;
        ret
 
set_dma_buffer ENDP
;
;-----------dt_reset-------------------------John Mertus  January 89----
;
;   This does a master board reset.
;   Will allways return success
;
 
        public  dt_reset
dt_reset  PROC far
 
        mov     dx,SUPCSR                       ; Supervisor register
        mov     ax,bit0                         ; Reset command
        out     dx,ax                           ; out it
        xor     ax,ax                           ; Return success
        ret
 
dt_reset   ENDP
 
;
;--------------set_clock----------------------John Mertus---Dec 88---
;
;  This sets the clock to a sampling rate.
;
;    call from C is DAD_set_clock(unsigned srate);
;
;    return is 0 for no error, otherwise the error word.
;
;
;
MAX_CLOCK       DD 4000000
TMP_CLK_LOW     DW 0
TMP_CLK_HI      DW 0
IDIVISOR        DW 0
PSRATE  DW      10000                           ; Physical sampling rate
 
        PUBLIC  set_clock
 
set_clock PROC far
        ARG srate:word, nsrate:far ptr word, f: far ptr word;
;
; Find physical rate
;
        mov     ax,[srate]                      ; get sampling rate
        mov     cl,cs:[CLOCKF]                  ; clock factor
        xor     ch,ch                           ; clear out high
        mul     cx                              ; multiply them
        les     bx,[f]                          ; return factor
        mov     es:[bx],cx
        mov     cs:[PSRATE],ax                  ; save physical rate
;
; DX:AX is maximum clock rate
;
        mov     ax,word ptr cs:[MAX_CLOCK]
        mov     dx,word ptr cs:[MAX_CLOCK+2]
        mov     cs:[IDIVISOR],1                 ; start at 1
;
;  division loop, do the following
;
;       for (idivsor=1; idivsor<16; i++) {
;               tic = l/psrate;
;               if (tic == 0) continue;
;               if (tic <= 256) break;
;               l = l/2;
;               if (idivsor == 1) l = l/2;
;               }
;
$$1:    mov     cs:[TMP_CLK_LOW],ax
        mov     cs:[TMP_CLK_HI],dx
 
        mov     bx,cs:[PSRATE]                  ; compute MAX_CLOCK/srate
        div     bx
 
        cmp     ax,256                          ; preset too small?
        jle     $$4                             ; yes
;
;  Divide the clock word by 2 and try again
;
$$2:    mov     ax,cs:[TMP_CLK_LOW]
        mov     dx,cs:[TMP_CLK_HI]
        shr     ax,1                            ; divide ax by 2
        shr     dx,1                            ; and dx
        jnc     $$3                             ; no low bit
        or      ax,8000H                        ; set hi bit
 
$$3:    inc     cs:[IDIVISOR]
        cmp     cs:[IDIVISOR],2                 ; see if first cycle
        jne     $$1
;
; divide by 2 again
;
        shr     ax,1
        shr     dx,1
        jnc     $$1
        or      ax,8000H
        jmp     $$1
;
;  IDIVSOR contains the correct divsor count, AX the preset
;  Now decide if above or below sampling rate is the best
;       if ((l % tic) > l % ((tic+1))) tic = tic+1;
;
; save remainder
;
$$4:    mov     bx,ax
        mov     ax,cs:[TMP_CLK_LOW]
        mov     dx,cs:[TMP_CLK_HI]
        div     bx
        sub     ax,cs:[PSRATE]
        push    ax
;
;  fixup the tic+1 being greater than 256
;
        cmp     bx,256
        je      $$5
 
        inc     bx
 
$$5:    mov     ax,cs:[TMP_CLK_LOW]
        mov     dx,cs:[TMP_CLK_HI]
        div     bx
        neg     ax
        add     ax,cs:[PSRATE]
 
        pop     cx                              ; cx is old remainerer
        cmp     ax,cx                           ; which is smaller?
        jle     $$6
        dec     bx
;
;  compute the actual sampling rate
;
$$6:    mov     ax,cs:[TMP_CLK_LOW]
        mov     dx,cs:[TMP_CLK_HI]
        div     bx
;
;  store stampling rate
;
        push    bx
        les     bx,[nsrate]
        mov     es:[bx],ax
        pop     bx
;
;  Now the divisor is in [IDIVISOR] and BX the correct number of tics
;
        mov     ax,bx
        not     ax                              ; take twos complement
        mov     ah,byte ptr cs:[IDIVISOR]
;
;  output clock rate
;
        mov     dx,cs:[TMRCTR]
        out     dx,ax
        xor     ax,ax                           ; return success
        ret
set_clock ENDP
 
;
;------------DAD_error_msg--------------John Mertus-----Jan--89---
;
;   This returns a pointer to the error string.  The requested error
; comes from DAD_error();
;
;   C  call
;       char *DAD_error_msg(unsigned errnum, errmsg);
;
        PUBLIC  DAD_error_msg
DAD_error_msg PROC far
        ARG     errnum:word, errmsg:far PTR dword
 
        push    ds                      ; save the data area
        push    di
        push    si
 
        mov     ax,cs
        mov     ds,ax
;
;  copy the header string
;
        les     di,[errmsg]
        mov     si,offset ERROR_STRING
        mov     cx,14
        rep     movsb
        dec     di                              ; back up over null
;
;  get error
;
        mov     ax,[errnum]
;
;  Loop, copying each string depending upon the error
;
        mov     cx,16
        xor     bx,bx           ; clear bx
l7:     test    ax,1            ; is it a 1?
        jz      l8              ; no
;
;  copy index into table
;
        mov     si,ERR_TABLE[BX]
l9:     movsb
        cmp     byte ptr es:[di-1],0
        jnz     l9
        dec     di              ; backup over zero
;
;   end of copying loop
;
l8:     shr     ax,1            ; find next bit
        inc     bx              ; next offset in table
        inc     bx
        loop    l7
;
; restore critical areas and go home
;
        pop     si
        pop     di
        pop     ds
        ret
DAD_error_msg ENDP
 
;
;--------------DA_setup_DMA---------------------John Mertus---January 89---
;
;  This sets up the DMA buffers.  In this case, the address are
;  copied over together with the size.
;
;  C call is
;       int dt_setup_da_DMA (int far *b, unsigned sz, int nu);
;
;  *b is an array of buffer addressed
;  size is the size in words of each buffer
;  nu is the number of buffer (must be 2)
;
        PUBLIC  DA_setup_DMA
DA_setup_DMA PROC FAR
        ARG     buffer:far PTR dword, sz:word, no:word ;
;
;  Get the buffer address
;
        les     bx,[buffer]                     ; get buffer address
        les     ax,es:[bx]
        test    ax,1                            ; see if odd address
        jz      $$10                            ; no
        mov     ax,ODDBUFFER
        ret                                     ; go home
;
;  Find the number of bytes to start of buffer
;    compute dx:ax = (es/1000h)*10000h + 16*(es % 1000h) + ax
;
$$10:   mov     bx,es                           ; long word dx:ax
        mov     cl,4
        shl     bx,cl                           ; bx = 16*(es % 1000)
        mov     dx,es
        mov     cl,12
        shr     dx,cl
        add     ax,bx                           ; ax = ax + bx
        jnc     $$1                             ; carry bit?
        inc     dx
;
;  Make bytes into words
;
$$1:    shr     ax,1                            ; Divide low by 2
        shr     dx,1                            ; ditto high
        jnc     $$2                             ; see high's low bit
        or      ax,8000h                        ;   need to be low high
;
;  DX:AX is the word offset, i.e DX is page, AX is offset
;
$$2:    shl     dx,1                            ; Pages are even
        mov     cs:[PageA],DX
        mov     cs:[OffA],AX
;
;  Get the second buffer address
;
        les     bx,[buffer]                     ; get buffer address
        les     ax,es:[bx+4]
        test    ax,1                            ; see if odd buffer
        jz      $$11
        mov     ax,ODDBUFFER
        ret
;
;  Find the number of bytes to start of buffer
;    compute dx:ax = (es/1000h)*10000h + 16*(es % 1000h) + ax
;
$$11:   mov     bx,es                           ; long word dx:ax
        mov     cl,4
        shl     bx,cl                           ; bx = 16*(es % 1000)
        mov     dx,es
        mov     cl,12
        shr     dx,cl
        add     ax,bx                           ; ax = ax + bx
        jnc     $$3                             ; carry bit?
        inc     dx
;
;  Make bytes into words
;
$$3:    shr     ax,1                            ; Divide low by 2
        shr     dx,1                            ; ditto high
        jnc     $$4                             ; see high's low bit
        or      ax,8000h                        ;   need to be low high
;
;  DX:AX is the word offset, i.e DX is page, AX is offset
;
$$4:    shl     dx,1                            ; pages are even
        mov     cs:[PageB],DX
        mov     cs:[OffB],AX
;
;  Save Buffer size in words
;
        mov     ax,[sz]
        dec     ax                              ; DMA count 1 smaller than size
        mov     cs:[BUFFER_SIZE],ax             ; save it
;
;  Setup the DMA channels
;
        mov     bx,0                            ; buffer A
        mov     ax,Read_DMA                     ; D/A output
        call    set_dma_buffer
 
        mov     bx,1                            ; buffer B
        mov     ax,Read_DMA                     ; D/A output
        call    set_dma_buffer
;
;  restore critical registers and go home
;
        xor     ax,ax                           ; no error
        ret
 
DA_setup_DMA ENDP
 
;
;--------------AD_setup_DMA---------------------John Mertus---January 89---
;
;  This sets up the DMA buffers.  In this case, the address are
;  copied over together with the size.
;
;  C call is
;       int ad_set_buffers(int far *b, unsigned sz, int nu);
;
;  where chan is the channel (ignored here)
;  *b is an array of buffer addressed
;  size is the size in words of each buffer
;  nu is the number of buffer (must be 2)
;
        PUBLIC  AD_setup_DMA
AD_setup_DMA PROC FAR
        ARG     buffer:far PTR dword, sz:word, no:word ;
;
;  Get the buffer address
;
        les     bx,[buffer]                     ; get buffer address
        les     ax,es:[bx]
        test    ax,1                            ; see if odd address
        jz      $$10                            ; no
        mov     ax,ODDBUFFER
        ret                                     ; go home
;
;  Find the number of bytes to start of buffer
;    compute dx:ax = (es/1000h)*10000h + 16*(es % 1000h) + ax
;
$$10:   mov     bx,es                           ; long word dx:ax
        mov     cl,4
        shl     bx,cl                           ; bx = 16*(es % 1000)
        mov     dx,es
        mov     cl,12
        shr     dx,cl
        add     ax,bx                           ; ax = ax + bx
        jnc     $$1                             ; carry bit?
        inc     dx
;
;  Make bytes into words
;
$$1:    shr     ax,1                            ; Divide low by 2
        shr     dx,1                            ; ditto high
        jnc     $$2                             ; see high's low bit
        or      ax,8000h                        ;   need to be low high
;
;  DX:AX is the word offset, i.e DX is page, AX is offset
;
$$2:    shl     dx,1                            ; Pages are even
        mov     cs:[PageA],DX
        mov     cs:[OffA],AX
;
;  Get the second buffer address
;
        les     bx,[buffer]                     ; get buffer address
        les     ax,es:[bx+4]
        test    ax,1                            ; see if odd buffer
        jz      $$11
        mov     ax,ODDBUFFER
        ret
;
;  Find the number of bytes to start of buffer
;    compute dx:ax = (es/1000h)*10000h + 16*(es % 1000h) + ax
;
$$11:   mov     bx,es                           ; long word dx:ax
        mov     cl,4
        shl     bx,cl                           ; bx = 16*(es % 1000)
        mov     dx,es
        mov     cl,12
        shr     dx,cl
        add     ax,bx                           ; ax = ax + bx
        jnc     $$3                             ; carry bit?
        inc     dx
;
;  Make bytes into words
;
$$3:    shr     ax,1                            ; Divide low by 2
        shr     dx,1                            ; ditto high
        jnc     $$4                             ; see high's low bit
        or      ax,8000h                        ;   need to be low high
;
;  DX:AX is the word offset, i.e DX is page, AX is offset
;
$$4:    shl     dx,1                            ; pages are even
        mov     cs:[PageB],DX
        mov     cs:[OffB],AX
;
;  Save Buffer size in words
;
        mov     ax,[sz]
        dec     ax                              ; DMA count 1 smaller than size
        mov     cs:[BUFFER_SIZE],ax             ; save it
;
;  Setup the DMA channels
;
        mov     bx,0                            ; buffer A
        mov     ax,Write_DMA                    ; D/A output
        call    set_dma_buffer
 
        mov     bx,1                            ; buffer B
        mov     ax,Write_DMA                    ; D/A output
        call    set_dma_buffer
;
;  restore critical registers and go home
;
        xor     ax,ax                           ; no error
        ret
 
AD_setup_DMA ENDP
;
;-----------start_output-----------------------John Mertus---January-89---
;
;    This start the D/A output
;       Call from C
;               void da_start();
;
        PUBLIC  start_output
start_output    PROC FAR
        push    ds
;
;  Initialize the D/A subsystem, enable buffer B
;
        mov     dx,cs:[SUPCSR]
        in      ax,dx
        or      ax,bit5 + bit9
        out     dx,ax
;
;  setup the DA control register, this also defines the channels
;
        mov     ax,bit5
        mov     bl,cs:[SCHAN]
        cmp     bl,2                            ; two channels?
        jge     $$2                             ; yes
;
;  check channel
;
        or      ax,bit8                         ; select 1 channel
        cmp     bl,1
        jne     $$2
        or      ax,bit9                         ; DA zero
;
;  Load DACSR
;
$$2:    mov     dx,cs:[DACSR]
        out     dx,ax
;
;--------------------------------------
;  Load the necesary codes to make
;  the handler a D/A handler
;--------------------------------------
;
        mov     cs:[DADCSR],dx
        mov     cs:[TYPE_DMA],Read_DMA
        mov     cs:[HARD_ERROR],DA_ERROR
 
;
;--------------------------------------
;  Install the D/A interrupt handler
;--------------------------------------
;
        mov     ax,cs                           ; D/A handler segment is
        mov     ds,ax                           ; same as code segment
        mov     dx,offset dad_handler
 
        mov     al,byte ptr cs:[intvector]      ; get interrupt vector
        mov     ah,25h
        int     21h                             ; set it
;
;  Turn off interrupt mask
;
        cli                                     ; disable all interrupts
        mov     dx,cs:[IntMaskReg]              ; clear the IRQ line
        in      al,dx                           ; get settings
        mov     ah,byte ptr cs:[IRQ_line]       ; get IRQ line
        not     ah                              ; clear IRQ bit
        and     al,ah                           ;   in Mask reg
        out     dx,al                           ; store it
        sti                                     ; enable all interrupts
;
;--------------------------------------
;  setup the SUPCSR
;--------------------------------------
;
        mov     dx,cs:[SUPCSR]
        in      ax,dx
        or      ax,bit11 + bit12                ; Dual DMA and D/A clocked
        out     dx,ax
;
; enable interrupt on ready for DA
;
        mov     dx,cs:[DACSR]
        in      ax,dx
        or      ax,bit6
        out     dx,ax
;
; enable interrupt on error
;
        mov     dx,cs:[SUPCSR]
        in      ax,dx                           ; may not have to do this
        or      ax,bit14                        ; interrupt on errors
        out     dx,ax
;
;---------------------------------
;  wait for first word to load
;---------------------------------
;
        mov     cx,0FFFFh                       ; large delay
        mov     dx,cs:[DACSR]
$$1:    in      ax,dx                           ; check D/A ready bit
        test    ax,bit7                         ; bit set
        je      $$3                             ; no, go ahead
        loop    $$1
;
        mov     ax,NOCLEAR                      ; return no clear error
        pop     ds
        ret
;
;--------------------------------------
;  Clear the EXIT DMA flag
;--------------------------------------
;
$$3:    mov     cs:[ExitDMA],0
        mov     cs:[Error_word],0
;
;--------------------------------------
;  At last, start the D/A output
;--------------------------------------
;
        mov     dx,cs:[SUPCSR]
        in      ax,dx
        or      ax,bit3
        out     dx,ax
;
;  Close up shop and Go home.
;
        xor     ax,ax                   ; No error
        pop     ds
        ret
 
start_output    ENDP
;
;-----------dad_handler-------------------------John Mertus---January-89---
;
;    This is the D/A or A/D interrupt routine.  It is called either on
;  a error or when the buffer is done.
;
;  If DAD error has occured, the error flag is set, a hardwared error
;  DA_HARD or AD hard reported in dma_error_no and we terminate.
;
;  Otherwise if DMA_exit is set we don't reprogram the DMA buffer
;  and set BUFFER_DONE in dma_error_no.
;
;  Other the DMA channel just finished is reprogrammed,
;
;  The following three words must be setup for a READ or WRITE
;
DADCSR          dw      0               ; Either ADCSR or DACSR
TYPE_DMA        dw      0               ; Either WRITE_DMA or READ_DMA
HARD_ERROR      dw      0               ; Either AD_ERROR or DA_ERROR
;
dad_handler:
;
;  save all registers
;
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    ds
        push    es
        push    bp
;
;  See if there is an error
;
        mov     dx,cs:[SUPCSR]
        in      ax,dx
        test    ax,bit15
        jz      $$1                             ; Error
;
;-----------------------------------
;    No Error, DMA done interrupted
;-----------------------------------
;
;       Find the channel number to process
;
        and     ax,bit9                         ; ax is current buffer
        mov     cl,9                            ; ax = 0 for channel A
        shr     ax,cl                           ; ax = 1 for channel B
;
;   Reset tge DMA controller
;
        cmp     cs:[ExitDMA],0
        jne     $$2
 
        mov     bx,ax
        inc     bl
        and     bx,1                            ; swap buffers
 
        mov     ax,cs:[type_DMA]                ; type (read or write)
        call    set_dma_buffer
;
;  Now clear DMA done
;
$$2:    mov     dx,cs:[SUPCSR]
        in      ax,dx
        and     ax,0FDFFH
        or      ax,bit13
        out     dx,ax
;
;  if flag is set, shut off DMA
;
$$1:    cmp     cs:[ExitDMA],0
        jz      $$3
 
        mov     dx,cs:[SUPCSR]
        in      ax,dx
        and     ax,0EDFFh
        out     dx,ax
 
        mov     ax,cs:[error_word]
        or      ax,DMA_DONE
        mov     cs:[error_word],ax
;
;  Now check to see if there was a DA conversion error
;
$$3:    mov     dx,cs:[DACSR]
        in      ax,dx
        test    ax,bit15
        jz      $$4                     ; No error
        mov     ax,cs:[error_word]
        or      ax,cs:[hard_error]
        mov     cs:[error_word],ax
;
; issue EOI to master 8259
;
$$4:    mov     dx,IntCmdMaster
        mov     al,IntEOI
        out     dx,al
;
; if interrupt was on slave 8259 then issue a EOI to it too
;
        cmp     cs:[IntMaskReg],0A1h
        jne     $$5
        mov     dx,IntCmdSlave
        mov     al,IntEOI
        out     dx,al
;
; restore all registers
;
$$5:    pop     bp
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        iret
;
;----------DAD_buf_status-------------John Mertus-------January--89--
;
;  This gives information about the buffer status of the current DMA count
;  The call from C is
;
;  unsigned c, int b
;
;               int DAD_bf_status(&c, &b)
;
;  Where c is the number of words that has been processed, b is the
;  current buffer being processed.  The error is returned, which in this
;  case is alway 0.
;
;
DAD_bf_status PROC FAR
        ARG  C:FAR PTR WORD, b:FAR PTR WORD
 
        call    DMA_count                       ; get count
        mov     dx,bx                           ; save buffer number
        sub     ax,cs:[BUFFER_SIZE]
        neg     ax
;
;  Return the values
;
        les     bx,[c]                          ; get count address
        mov     es:[bx],ax                      ; store it
 
        les     bx,[b]                          ; get buffer address
        mov     es:[bx],dx                      ; store it
;
;  Return Error value
;
        mov     ax,cs:[error_word]
        ret
 
DAD_bf_status ENDP
 
;
;----------DMA_count------------------John Mertus-------January--89--
;
;  This just returns the DMA count of the #1 DMA channel.  First find
;  out which buffer we are on, read the DMA count and check the buffer
;  hasn't switched.  If it has reread the DMA count.
;
;       ax returns with the count
;       bx with the buffer number
;
DMA_count PROC
 
        push    cx
        push    dx
;
;  Find which buffer is reading
;
$$1:    mov     dx,cs:[SUPCSR]
        in      ax,dx
        and     ax,bit9                         ; ax is current buffer
        push    ax
        mov     cl,9                            ; ax = 0 for channel A
        shr     ax,cl                           ; ax = 1 for channel B
        pop     cx                              ; save it for later
;
;  Find which count register to use
;
        mul     ax
        mov     bx,12
        mul     bl
        mov     bx,ax
        mov     dx,cs:DMA_Table[bx + CountReg]
;
;  Read a dma count
;
        in      ax,dx
        mov     bx,ax                           ; save it
;
;  See if buffer has switched
;
        mov     dx,cs:[SUPCSR]
        in      ax,dx
        and     ax,bit9                         ; ax is current buffer
        cmp     ax,cx                           ; see if equal
        jne     $$1
;
;  They agree, we are ok
;
        mov     cl,9
        shr     ax,cl
        xchg    ax,bx
        pop     dx
        pop     cx
        ret
 
DMA_count ENDP
;
;-----------da_clear-------------------------John Mertus  January 89----
;
;   This stops the board and clears out all errors.
;  Notice that AX will be zero on no error, nonzero on error
;
        public  da_clear
da_clear PROC far
        mov     cs:[ExitDMA],1
        mov     dx,cs:[SUPCSR]
        in      ax,dx
        and     ax,0EDFFh
        out     dx,ax
;
;--------------------------------------
;  Turn off the DA clock
;--------------------------------------
;
        mov     dx,cs:[DACSR]
        in      ax,dx
        mov     bx,bit5
        not     bx
        and     ax,bx
        out     dx,ax
;
;--------------------------------------
;  Mask the IRQ interrupt line
;--------------------------------------
;
        cli                                     ; disable all interrupts
        mov     dx,cs:[IntMaskReg]              ; clear the IRQ line
        in      al,dx                           ; get settings
        or      al,byte ptr cs:[IRQ_line]       ; get IRQ line
        out     dx,al                           ; store it
        sti                                     ; enable all interrupts
;
;--------------------------------------
;  Clear Error, Return old value
;--------------------------------------
;
        mov     ax,cs:[error_word]
        mov     cs:[error_word],0
        ret
 
da_clear ENDP
;
;--------------wait_on_buffer---------------------John Mertus---Dec 88---
;
;  This waits for a buffer to finish OR words words to be transfered
;
;  C call is
;       int wait_on_buffer (int bufnum, unsigned words);
;
;  bufnum is the buffer number (0 or 1)
;  words is the number of words to transfer
;
        PUBLIC  wait_on_buffer
wait_on_buffer PROC FAR
        ARG     bufnum:word, words:word
 
        mov     cx,[bufnum]             ; get buffer number
$$1:    call    DMA_count               ; get count
        cmp     cl,bl                   ; see if we have passed boundary
        jne     $$2
;
; Check number of words transfered, will be in ax
;
        sub     ax,cs:[BUFFER_SIZE]
        neg     ax
        cmp     ax,[words]
        jb      $$1                     ; not enough transfered
 
$$2:    xor     ax,ax                   ; return no error
        ret                             ; go home
 
wait_on_buffer ENDP
;
;-----------ad_set_channels-------------------John Mertus---January-89---
;
        PUBLIC  AD_set_channels
;
;  This emulates a channel gain list.  If the list cannot be convered,
; an error occures.
;  Call from C is
;       int ad_set_channels(int num, char list[], int s);
;
AD_set_channels  PROC FAR
 
        ARG     num:word, list: far ptr DWORD, s:word
        push    ds
;
; Get number of channels
;
        mov     cx,[num]
        mov     cs:[CLOCKF],cl          ; Save factor for clock
        lds     si,[list]               ; point to list
        cld
        mov     bx,cx                   ; store value
        dec     bx                      ; must have more than 1 value
        jge     $$1                     ; Ok
;
; Flag list error
;
$$0:    mov     ax,LIST_ERROR           ; list error
        pop     ds
        ret
;
;  Setup to load channel/gain list
;
$$1:    or      bx,bit15                ; Enable load
        mov     ax,bx
        mov     dx,cs:[CHANCSR]
        out     dx,ax                   ; Enable list
 
        mov     dx,cs:[ADCSR]
        in      ax,dx
        and     ax,0FFC0h               ; clear out list
        mov     bx,ax                   ; save it
 
$$2:    push    cx                      ; save loop counter
        mov     cx,bx                   ; get AD/CSR template
        lodsb
        or      cl,al                   ; set channel
        lodsb
        and     al,3                    ; clear out any garbage
 
        shl     al,1                    ; mulitply by 16
        shl     al,1                    ; smaller than push cl
        shl     al,1                    ;              mov  cl,4
        shl     al,1                    ;              shl  al,cl
                                        ;              pop  cl
        or      cl,al                   ; set gain bits
        mov     ax,cx
        out     dx,ax                   ; store it
        pop     cx                      ; restore loop counter
        loop    $$2
;
;  Turn off channel gain list enable
;
        mov     dx,cs:[CHANCSR]
        in      ax,dx
        mov     bx,bit15
        not     bx
        and     ax,bx                   ; clear out bit 15
        out     dx,ax
;
;  Common return
;
        xor     ax,ax                   ; no error
        pop     ds
        ret
 
AD_set_channels  ENDP
 
;
;-----------da_set_channels-------------------John Mertus---January-89---
;
        PUBLIC  da_set_channels
;
;  This emulates a channel gain list.  If the list cannot be convered,
; an error occures.
;  Call from C is
;       int da_set_channels(int num, int schan, int s);
;
DA_set_channels  PROC FAR
        ARG     num:word, chan:word, s:word
;
; Get number of channels
;
        mov     cx,[num]
;
; Make sure we are in the range
;
        cmp     cx,0
        jle     $$1
 
        cmp     cx,2
        jle     $$2
;
;  Report error
;
$$1:    mov     ax,LIST_ERROR           ; flag error
        ret                             ; return
;
;  Number of channels ok, check start
;
$$2:    mov     cs:[CLOCKF],cl          ; Save factor for clock
        mov     ax,[chan]               ; get channel
        cmp     ax,0
        jl      $$1
        cmp     ax,1
        jg      $$1
;
;  Override channel if both
;
        cmp     cl,2
        jne     $$3
        mov     al,2
 
$$3:    mov     cs:[SCHAN],al           ; Save start channel
;
;  Return success
;
        xor     ax,ax
        ret
 
DA_set_channels  ENDP
;
;-----------ad_clear-------------------------John Mertus  January 89----
;
;   This stops the board and clears out all errors.
;  Notice that AX will be zero on no error, nonzero on error
;
        public  ad_clear
ad_clear PROC far
        mov     cs:[ExitDMA],1
        mov     dx,cs:[SUPCSR]
        in      ax,dx
        and     ax,0EDFFh
        out     dx,ax
;
;--------------------------------------
;  Turn off the AD clock
;--------------------------------------
;
        mov     dx,cs:[ADCSR]
        in      ax,dx
        mov     bx,bit9
        not     bx
        and     ax,bx
        out     dx,ax
;
;--------------------------------------
;  Mask the IRQ interrupt line
;--------------------------------------
;
        cli                                     ; disable all interrupts
        mov     dx,cs:[IntMaskReg]              ; clear the IRQ line
        in      al,dx                           ; get settings
        or      al,byte ptr cs:[IRQ_line]       ; get IRQ line
        out     dx,al                           ; store it
        sti                                     ; enable all interrupts
;
;--------------------------------------
;  Clear Error, Return old value
;--------------------------------------
;
        mov     ax,cs:[error_word]
        mov     cs:[error_word],0
        ret
 
ad_clear ENDP
;
;-----------start_input-----------------------John Mertus---January-89---
;
;    This start the A/D input
;       Call from C
;               void ad_start();
;
        PUBLIC  start_input
start_input     PROC FAR
        push    ds
;
;  Initialize the A/D subsystem, enable buffer B
;
        mov     dx,cs:[SUPCSR]
        in      ax,dx
        or      ax,bit6 + bit9
        out     dx,ax
;
;--------------------------------------
;  Load the necesary codes to make
;  the handler a A/D handler
;--------------------------------------
;
        mov     dx,cs:[ADCSR]
        mov     cs:[DADCSR],dx
        mov     cs:[TYPE_DMA],WRITE_DMA
        mov     cs:[HARD_ERROR],AD_ERROR
 
;
;--------------------------------------
;  Install the D/A interrupt handler
;--------------------------------------
;
        mov     ax,cs                           ; D/A handler segment is
        mov     ds,ax                           ; same as code segment
        mov     dx,offset dad_handler
 
        mov     al,byte ptr cs:[intvector]      ; get interrupt vector
        mov     ah,25h
        int     21h                             ; set it
;
;  Turn off interrupt mask
;
        cli                                     ; disable all interrupts
        mov     dx,cs:[IntMaskReg]              ; clear the IRQ line
        in      al,dx                           ; get settings
        mov     ah,byte ptr cs:[IRQ_line]       ; get IRQ line
        not     ah                              ; clear IRQ bit
        and     al,ah                           ;   in Mask reg
        out     dx,al                           ; store it
        sti                                     ; enable all interrupts
;
;--------------------------------------
;  setup the SUPCSR
;--------------------------------------
;
        mov     dx,cs:[SUPCSR]
        in      ax,dx
        or      ax,bit10 + bit12                ; Dual DMA and A/D clocked
        out     dx,ax
;
;  setup the AD control register. (Interrupt on A/D ready)
;
        mov     ax,bit9 + bit6
        mov     dx,cs:[ADCSR]
        out     dx,ax
;
; enable interrupt on error
;
        mov     dx,cs:[SUPCSR]
        in      ax,dx                           ; may not have to do this
        or      ax,bit14                        ; interrupt on errors
        out     dx,ax
;
;---------------------------------
;  wait for first word to load
;---------------------------------
;
        mov     cx,0FFFFh                       ; large delay
        mov     dx,cs:[ADCSR]
$$1:    in      ax,dx                           ; check D/A ready bit
        test    ax,bit7                         ; bit set
        je      $$3                             ; no, go ahead
        loop    $$1
;
        mov     ax,NOCLEAR                      ; return no clear error
        pop     ds
        ret
;
;--------------------------------------
;  Clear the EXIT DMA flag
;--------------------------------------
;
$$3:    mov     cs:[ExitDMA],0
        mov     cs:[Error_word],0
;
;--------------------------------------
;  At last, start the D/A output
;--------------------------------------
;
        mov     dx,cs:[SUPCSR]
        in      ax,dx
        or      ax,bit3
        out     dx,ax
;
;  Close up shop and Go home.
;
        xor     ax,ax                   ; No error
        pop     ds
        ret
 
start_input     ENDP
;
;  End of program
;
        END     DUMMY
 
