title mse_line0.asm
; from HULL
;	this programs the mouse so that if you move it, only the x-coord
;	of the mouse is read and used to determine the position of a
;	vertical line.  The left of the line is brown and the right is
;	green.  There is no save of old mode, so the computer
;	is in a funny state at the end...
.model small
.stack 100h
.data
mouse_dir db 0
new_div   db 40
;
;
.code
MAIN:
	 MOV   AX, @DATA
	 MOV   DS, AX
	 mov   ax, 0
	 int   33h              ;initialize the mouse
	 call loop_n_paint      ; mail routine
	 call cleanup           ; reset to DOS colors for output
	 MOV   AX, 4C00h
	 INT   21H           ; then return to DOS
mouse_pos:
	 push  ax
	 push  bx
	 push  cx
	 push  dx
	 mov   ax, 3
	 int   33h           ; get mouse position in cx
	 shr   cx, 1
	 shr   cx, 1
	 shr   cx, 1         ; translate into columns
	 cmp   cx, 27        ; find out where it is on the screen
	 jbe   leftmove
	 cmp   cx, 54
	 jbe   dontmove
	 mov   mouse_dir, +1   ; must be going to the right
	 jmp   mouse_pos_exit
leftmove: 
	 mov   mouse_dir, -1   ; going to the left
	 jmp   mouse_pos_exit
dontmove:
	 mov   mouse_dir, 0
mouse_pos_exit:
	 pop   dx
	 pop   cx
	 pop   bx
	 pop   ax
	 ret
dividing_line:
	 push   ax
	 call   mouse_pos         ; is mouse moving left or right??
	 mov    al, new_div     
	 add    al, mouse_dir     ; figure new position
	 cmp    al, 1
	 jb     one_it
	 cmp    al, 79
	 jbe    keepit
	 mov    al, 79
	 jmp    keepit
one_it:  mov    al, 1
keepit:  mov    new_div, al
	 pop    ax
	 ret
;input:  the column number in the dl register
;         the color (attribute) in the bl register
draw_col: 
	 push  ax
	 push  bx
	 push  cx
	 push  dx
	 mov   dh, 0      ; start in row 0
	 mov   cx, 1      ; one char at a time
	 mov   al, ' '    ; write out spaces
col_loop:
	 call  paint      ; paint one character
	 inc   dh         ; next row
	 cmp   dh, 24
	 jbe   col_loop   ; end of screen??
	 pop   dx
	 pop   cx
	 pop   bx
	 pop   ax
	 ret
loop_n_paint:
	 push  ax
	 push  bx
	 push  cx
	 push  dx
; first, initialize the left side of the screen to red 
	 mov   bl, 40h    ; red
	 mov   dl, 0     ; start column 0
redinit: call draw_col
	 inc   dl
	 cmp   dl, new_div
	 jb    redinit
; now the right side to green
	 mov   bl, 20h
greeninit:
	 call draw_col
	 inc    dl
	 cmp    dl, 79
	 jbe    greeninit
; screen is now initialized
big_loop:
	 mov    ax, 3
	 int    33h
	 and    bx, 011b          ; either mouse button pressed?
	 jnz     end_loop
	 call   dividing_line
	 cmp    mouse_dir, 0
	 je     waiting
	 jl     draw_green
; if you get here, the last mouse move was to the left.  Therefore,
draw
; a red column
	 mov    dl, new_div
	 dec    dl             ; column to the left, actually!
	 mov    bl, 40h
	 call   draw_col
	 jmp    waiting
draw_green: 
	 mov    dl, new_div
	 mov    bl, 20h
	 call   draw_col
waiting: call   delay
	 jmp    big_loop      ;slow down the action
end_loop:
	 pop   dx
	 pop   cx
	 pop   bx
	 pop   ax
	 ret
;
;
CLEANUP:
	 MOV   DX,0          ; reset screen to normal color
	 MOV   CX,25*80
	 MOV   BL,07h         ; grey on black
	 MOV   AL,' '
	 CALL  PAINT
;
;
DELAY    PROC
	 PUSH  AX            ; save registers
	 PUSH  DX
	 MOV   DH,25         ; get cursor off screen area
	 MOV   DL,0          ;   (cleaner appearence)
	 CALL  SETPOS
	 SUB   AX,AX         ; zero frequency for rest
	 MOV   DX,6          ; delay of 0.06 secs is reasonable
	 CALL  NOTE          ; execute delay
	 POP   DX            ; restore registers
	 POP   AX
	 RET                 ; return to caller
DELAY    ENDP
;
;
;  Routine to play note on speaker
;
;        (AX)            Frequency in Hz (32 - 32000)
;        (DX)            Duration in units of 1/100 second
;        CALL  NOTE
;
;  Note: a frequency of zero, means rest (silence) for the indicated
;  time, allowing this routine to be used simply as a timing delay.
;
;  Definitions for timer gate control
;
CTRL     EQU   61H           ; timer gate control port
TIMR     EQU   00000001B     ; bit to turn timer on
SPKR     EQU   00000010B     ; bit to turn speaker on
;
;  Definitions of input/output ports to access timer chip
;
TCTL     EQU   043H          ; port for timer control
TCTR     EQU   042H          ; port for timer count values
;
;  Definitions of timer control values (to send to control port)
;
TSQW     EQU   10110110B     ; timer 2, 2 bytes, sq wave, binary
LATCH    EQU   10000000B     ; latch timer 2
;
;  Define 32 bit value used to set timer frequency
;
FRHI     EQU   0012H         ; timer frequency high (1193180 / 256)
FRLO     EQU   34DCH         ; timer low (1193180 mod 256)
;
NOTE     PROC
	 PUSH  AX            ; save registers
	 PUSH  BX
	 PUSH  CX
	 PUSH  DX
	 PUSH  SI
	 MOV   BX,AX         ; save frequency in BX
	 MOV   CX,DX         ; save duration in CX
;
;  We handle the rest (silence) case by using an arbitrary frequency
to
;  program the clock so that the normal approach for getting the right
;  delay functions, but we will leave the speaker off in this case.
;
	 MOV   SI,BX         ; copy frequency to BX
	 OR    BX,BX         ; test zero frequency (rest)
	 JNZ   NOT1          ; jump if not
	 MOV   BX,256        ; else reset to arbitrary non-zero
;
;  Initialize timer and set desired frequency
;
NOT1:    MOV   AL,TSQW       ; set timer 2 in square wave mode
	 OUT   TCTL,AL
	 MOV   DX,FRHI       ; set DX:AX = 1193180 decimal
	 MOV   AX,FRLO       ;   = clock frequency
	 DIV   BX            ; divide by desired frequency
	 OUT   TCTR,AL       ; output low order of divisor
	 MOV   AL,AH         ; output high order of divisor
	 OUT   TCTR,AL
;
;  Turn the timer on, and also the speaker (unless frequency 0 = rest)
;
	 IN    AL,CTRL       ; read current contents of control port
	 OR    AL,TIMR       ; turn timer on
	 OR    SI,SI         ; test zero frequency
	 JZ    NOT2          ; skip if so (leave speaker off)
	 OR    AL,SPKR       ; else turn speaker on as well
;
;  Compute number of clock cycles required at this frequency
;
NOT2:    OUT   CTRL,AL       ; rewrite control port
	 XCHG  AX,BX         ; frequency to AX
	 MUL   CX            ; frequency times secs/100 to DX:AX
	 MOV   CX,100        ; divide by 100 to get number of beats
	 DIV   CX
	 SHL   AX,1          ; times 2 because two clocks/beat
	 XCHG  AX,CX         ; count of clock cycles to CX
;
;  Loop through clock cycles
;
NOT3:    CALL  RCTR          ; read initial count
;
;  Loop to wait for clock count to get reset. The count goes from the
;  value we set down to 0, and then is reset back to the set value
;
NOT4:    MOV   DX,AX         ; save previous count in DX
	 CALL  RCTR          ; read count again
	 CMP   AX,DX         ; compare new count : old count
	 JB    NOT4          ; loop if new count is lower
	 LOOP  NOT3          ; else reset, count down cycles
;
;  Wait is complete, so turn off clock and return
;
	 IN    AL,CTRL       ; read current contents of port
	 AND   AL,0FFH-TIMR-SPKR ; reset timer/speaker control bits
	 OUT   CTRL,AL       ; rewrite control port
	 POP   SI            ; restore registers
	 POP   DX
	 POP   CX
	 POP   BX
	 POP   AX
	 RET                 ; return to caller
NOTE     ENDP
;
;  Routine to read count, returns current timer 2 count in AX
;
RCTR     PROC
	 MOV   AL,LATCH      ; latch the counter
	 OUT   TCTL,AL       ; latch counter
	 IN    AL,TCTR       ; read lsb of count
	 MOV   AH,AL
	 IN    AL,TCTR       ; read msb of count
	 XCHG  AH,AL         ; count is in AX
	 RET                 ; return to caller
RCTR     ENDP
;
;  Routine to set cursor position
;
;        (DH,DL)             Cursor line, column
;        CALL  SETPOS
;
SETPOS   PROC
	 PUSH  AX            ; save registers
	 PUSH  BX
	 MOV   BH,0          ; set cursor
	 MOV   AH,2
	 INT   10H
	 POP   BX            ; restore registers
	 POP   AX
	 RET                 ; return to caller
SETPOS   ENDP
;
;  Routine to paint characters on screen
;
;        (DH,DL)             Line, column of start of area to paint
;        (CX)                Number of characters to paint
;        (BL)                Required color
;        (AL)                Character code
;        CALL  PAINT
;
PAINT    PROC
	 PUSH  AX            ; save registers
	 PUSH  BX
	 CMP   CX,0          ; skip if no chars to paint
	 JE    PAINT1
	 CALL  SETPOS        ; set cursor position
	 MOV   BH,0          ; write the characters
	 MOV   AH,9
	 INT   10H
;
;  Here with characters painted
;
PAINT1:  POP   BX            ; restore registers
	 POP   AX
	 RET                 ; return to caller
PAINT    ENDP
	 END main
