;
;  Demonstration color/graphics program
;  Copyright (C) 1984, Robert B. K. Dewar
;
;  This program contains a set of basic routines for writing
;  to the screen in 200 x 320 graphics mode, and a driver
;  which generates an intricate pattern using straight lines.
;  This pattern is displayed in two variations using all
;  possible color and palette selections and also
;
;  The program terminates when any key is pressed
;
;  Loop through color/pattern combinations
;

	  .model small

;-------------------------------------------------------------------------------
	  .code
start:	 mov   ax,@data
	 mov   ds,ax	      ;set DS to data segment
	 mov   es,ax	      ;set ES to data segment also

cploop:  mov   ax,4	      ;set 200 x 320 color mode
	 int   10h	      ;(also blanks the screen)
	 mov   ah,11	      ;set background color
	 mov   bl,control     ;from control location
	 shr   bx,1
	 shr   bx,1
	 and   bx,1111b       ;(bh set to zero = select bg color)
	 int   10h
	 mov   ah,11	      ;select palette
	 mov   bl,control     ;from control location
	 and   bx,1
	 mov   bh,1
	 int   10h
;
;  Write legends at top and bottom of screen
;
	 mov   dh,0	      ;set cursor location for message
	 mov   dl,6
	 mov   si,offset msgt ;point to top message
	 mov   bx,1	      ;display using color 1
	 call  gr_msg
	 mov   dh,24	      ;set cursor location for message
	 mov   dl,6
	 mov   si,offset msgb ;point to bottom message
	 mov   bx,2	      ;display using color 2
	 call  gr_msg
;
;  Prepare to draw lines
;
	 mov   bx,offset ls   ;bx points to current start position
	 mov   al,1	      ;al is current color
;
;  Outer loop (starting points for lines)
;
oloop:	 mov   bp,offset ls   ;bp points to first end point
	 mov   dx,[bx]	      ;set start row
	 mov   cx,[bx+2]      ;set start column
	 page
;
;  Inner loop (ending point for lines) connect all points
;
iloop:	 mov   si,ds:[bp]     ;set end row
	 mov   di,ds:[bp+2]   ;set end column
	 inc   al	      ;bump color umn
	 and   al,11b	      ;isolate to 0-3
	 jnz   iloop2	      ;jump if color 1-3
;
;  Color 0 (the background color) is either skipped or included
;  depending on the setting of the pattern control bit
;
	 test  control,10b    ;for color 0, test pattern style
	 jnz   iloop2	      ;in pattern style 1, allow color 0
	 inc   al	      ;in pattern style 2, use only 1,2,3
;
;  Ready to write the line
;
iloop2:  call  gr_lin	      ;write line
	 add   bp,4	      ;bump to next end point
	 cmp   bp,offset lp   ;more end points to go?
	 jbe   iloop	      ;yes-loop
	 add   bx,4	      ;no-bump to next start point
	 push  ax	      ;save color
	 mov   ah,1	      ;key available?
	 int   16h
	 jnz   exit	      ;yes-exit
	 pop   ax	      ;no-restore key
	 cmp   bx,offset lp   ;more start points to go
	 jb    oloop	      ;yes-go output next line
	 inc   control	      ;no-bump control location
	 jmp   cploop	      ;and get next color/pattern
;
;  Exit when key is pressed
;
exit:	 mov   ah,0	      ;read and skip the key
	 int   16h
	 mov   ax,2	      ;reset 80 x 25 BW mode
	 int   10h

	 mov   ax,4c00h
	 int   21h	      ;exit to DOS


;
;  Write Message Routine
;
;	 (dx)		      ;Cursor location (screen is 25 x 40)
;	 (bl)		      ;Color to use for message characters
;	 (si)		      ;Points to message (terminated by 00h)
;	 call  gr_msg	      ;call to write message
;
gr_msg	 proc  near
	 push  ax	      ;save registers
	 push  bx
	 push  si	      ;note: si,di,bp destroyed
	 push  di	      ;        by write TTY function
	 push  bp
	 cld		      ;ensure auto-increment
	 mov   bh,0	      ;page zero for writes
	 mov   ah,2	      ;set cursor as requested
	 int   10h
;
;  Loop through characters of message
;
gr_msg1: lodsb		      ;load next character
	 or    al,al	      ;test terminating zero?
	 jz    gr_msg2	      ;yes-done
	 push  si	      ;no-save pointer
	 mov   ah,14	      ;write char in TTY mode
	 int   10h
	 pop   si	      ;restore character
	 jmp   gr_msg1	      ;loop for next character
;
;  Here with all characters written
;
gr_msg2: pop   bp	      ;restore registers
	 pop   di
	 pop   si
	 pop   bx
	 pop   ax
	 ret		      ;return to caller
gr_msg	 endp
	 page
;
;  Write Line Routine
;
;	 (al)		      ;Color (0,1,2,3)
;	 (dx)		      ;Starting row (0-199)
;	 (cx)		      ;Starting column (0-319)
;	 (si)		      ;Ending row (0-199)
;	 (di)		      ;Ending column (0-319)
;	 call  gr_lin	      ;Call to write line
;
;
;  Entry point
;
gr_lin	 proc
	 push  ax	      ;save registers
	 push  bx
	 push  cx
	 push  dx
	 push  si
	 push  di
	 push  bp
;
;  Check cases, get absolute difference in rows
;
	 push  ax	      ;save color
	 mov   ax,dx	      ;starting row
	 sub   ax,si	      ;minus ending row
	 jns   gr_lin1	      ;skip if not negative
	 neg   ax	      ;else take absolute value
;
;  Get absolute difference in columns
;
gr_lin1: mov   bx,cx	      ;starting column
	 sub   bx,di	      ;minus ending column
	 jns   gr_lin2	      ;skip if not negative
	 neg   bx	      ;else take absolute value
;
;  Check cases
;
gr_lin2: cmp   ax,bx	      ;compare abs row change wi abs col change
	 jae   gr_lin6	      ;jump if row change >= column change
;
;  Here we have the case of column change greater than row change. In this
;  case we will place a dot in each column, adjusting the row appropriately.
;
	 mov   bp,bx	      ;column change to bp
	 inc   bp	      ;plus one = number of columns
	 push  dx	      ;save start row for a moment
	 mov   dx,ax	      ;row change to (ax,dx)
	 sub   ax,ax
	 div   bx	      ;divide by column change
	 mov   bx,ax	      ;and put fractional part back in bx
	 pop   dx	      ;restore start row
	 page
;
;  We always want the starting row less than the ending row for this case
;
	 cmp   dx,si	      ;compare start row with ending row
	 jbe   gr_lin3	      ;skip if OK
	 xchg  dx,si	      ;else switch rows
	 xchg  cx,di	      ;switch columns
;
;  Get direction setting and setup registers for loop
;
gr_lin3: cmp   cx,di	      ;start col : end col (CF set if start < end)
	 mov   ax,0	      ;ax = 1 if start < end, 0 if start >= end
	 adc   ax,ax
	 add   ax,ax	      ;ax = 2 if start < end, 0 if start >= end
	 dec   ax	      ;ax = +1 if start < end, -1 if start >= end
	 mov   direc,ax       ;set proper direction
	 mov   di,dx	      ;starting row to (di,si)
	 sub   si,si	      ;(starting column is set in cx)
	 pop   ax	      ;restore color
;
;  This is the loop for the case where the column changes by 1 for each dot
;
;    (di,si)   Current row (di is integer part, si is binary fraction)
;    (bx)      Increment for row as binary fraction
;    (cx)      Current column
;    (al)      Color to be set
;    (bp)      Number of columns required
;    (direc)   +1 if column to be incremented, -1 if column to be decremented
;
;  Note that the column may increment or decrement, the row always increases
;
gr_lin4: mov   dx,di	      ;copy row
	 or    si,si	      ;test rounding required (fraction >= 0.5)
	 jns   gr_lin5
	 inc   dx	      ;round up if fraction >= 0.5
;
;  Ready to write next dot
;
gr_lin5: call  gr_dot	      ;write the dot
	 add   cx,direc       ;adjust column
	 add   si,bx	      ;add to get fractional col for next dot
	 adc   di,0
	 dec   bp	      ;loop if more columns to go
	 jnz   gr_lin4
	 jmp   gr_lin11       ;exit
;
;  Here we have the case of row change greater than column change. In this
;  case we will place a dot in each row, adjusting the column appropriately.
;
gr_lin6: push  dx	      ;save start row for a moment
	 mov   bp,ax	      ;row change to bp
	 mov   dx,bx	      ;column change to (ax,dx)
	 sub   ax,ax
	 mov   bx,0ffffh      ;set fraction = near enough to 1 if #rows = #cols
	 cmp   bp,dx
	 je    gr_lin7
	 div   bp	      ;else divide column change by row change
	 mov   bx,ax	      ;and put fractional part back in bx
	 page
;
;  We always want the starting column less than the ending column for this case
;
gr_lin7: pop   dx	      ;restore start row
	 inc   bp	      ;bump row change to get number of rows
	 cmp   cx,di	      ;compare starting column with ending column
	 jbe   gr_lin8	      ;skip if OK
	 xchg  dx,si	      ;else switch rows
	 xchg  cx,di	      ;switch columns
;
;  Get direction setting and setup registers for loop
;
gr_lin8: cmp   dx,si	      ;start row : end row (CF set if start < end)
	 mov   ax,0	      ;ax = 1 if start < end, 0 if start >= end
	 adc   ax,ax
	 add   ax,ax	      ;ax = 2 if start < end, 0 if start >= end
	 dec   ax	      ;ax = +1 if start < end, -1 if start >= end
	 mov   direc,ax       ;set proper direction
	 mov   di,cx	      ;starting column to (di,si)
	 sub   si,si	      ;(starting row is set in cx)
	 pop   ax	      ;restore color
;
;  This is the loop for the case where the row changes by 1 for each dot
;
;    (di,si)   Current column (di is integer part, si is binary fraction)
;    (bx)      Increment for column as binary fraction
;    (dx)      Current row
;    (al)      Color to be set
;    (bp)      Number of rows required
;    (direc)   +1 if row to be incremented, -1 if row to be decremented
;
;  Note that the row may increment or decrement, the column always increases
;
gr_lin9: mov   cx,di	      ;copy column
	 or    si,si	      ;test rounding required (fraction >= 0.5)
	 jns   gr_lin10
	 inc   cx	      ;round up if fraction >= 0.5
;
;  Here ready to write next dot
;
gr_lin10: call gr_dot	      ;write the dot
	 add   dx,direc       ;adjust row
	 add   si,bx	      ;add to get fractional col for next dot
	 adc   di,0
	 dec   bp	      ;loop if more rows to go
	 jnz   gr_lin9
;
;  Exit point after completing writing of line
;
gr_lin11: pop bp	      ;restore registers
	 pop   di
	 pop   si
	 pop   dx
	 pop   cx
	 pop   bx
	 pop   ax
	 ret		      ;return to caller
gr_lin	 endp
	 page
;
;  Write Dot Routine
;
;	 (al)		      ;Color (0,1,2,3)
;	 (dx)		      ;Row (0-199)
;	 (cx)		      ;Column (0-319)
;	 call  gr_dot	      ;Call to write dot
;
gr_dot	 proc
	 push  ax	      ;save registers
	 push  cx
	 push  dx
	 push  di
	 push  es
	 mov   di,0b800h      ;point to screen segment
	 mov   es,di
	 mov   ah,11111100b   ;set mask to strip old color
;
;  Determine position in byte and set ah to clear old dot, al to set new
;
	 mov   di,cx	      ;copy column to di
	 not   cl	      ;flip last 2 bits of column
	 and   cl,11b	      ;isolate 2 LSB of column flipped
	 shl   cl,1	      ;adjust to get shift count
	 rol   ah,cl	      ;adjust isolation mask
	 shl   al,cl	      ;adjust color position
	 shr   di,1	      ;get byte offset in di
	 shr   di,1
	 shr   dx,1	      ;move lsb of row to CF, is it set?
	 jnc   gr_dot1	      ;no
	 add   di,2000h       ;yes-adjust for odd row
;
;  Now (di) has been adjusted by 2000h for the odd row case if needed
;
gr_dot1: shl   dx,1	      ;row * 2
	 shl   dx,1	      ;row * 4
	 shl   dx,1	      ;row * 8
	 shl   dx,1	      ;row * 16
	 add   di,dx	      ;add row * 16 into byte
	 shl   dx,1	      ;row * 32
	 shl   dx,1	      ;row * 64
	 add   di,dx	      ;add row * 64 into byte
;
;  At this stage, di points to the byte we want, ah = mask, al = color
;
	 and   ah,es:[di]     ;clear old color entry from byte
	 or    al,ah	      ;set new color
	 stosb		      ;store adjusted byte
	 pop   es	      ;restore registers
	 pop   di
	 pop   dx
	 pop   cx
	 pop   ax
	 ret		      ;return to caller
gr_dot	 endp
;-------------------------------------------------------------------------------


;-------------------------------------------------------------------------------
	  .data
;
;  Messages for top and bottom of screen
;

msgt     db    'POINT GRAPHICS DEMONSTRATION',0
msgb     db    'PRESS ANY KEY TO EXIT TO DOS',0

;
;  Table of points. These points are computed as follows:
;
;   pi := 3.1415926535;
;   angle := 0;
;   while angle < 2*pi do
;   begin
;      y = round (100 + 085 * sin (angle))
;      x = round (160 + 105 * cos (angle))
;      angle := angle + 2 * pi / 32;
;   end
;
;  Here the cos/sin functions generate the unit circle at
;  at equally spaced angles. The point 100,160 is the center
;  of the screen which is used as the origin of the generated
;  circle, and 085/105 scales up to point positions, allowing
;  for the fact that vertical/horizontal spacings differ.
;
ls	 dw    100,265
	 dw    117,263
	 dw    133,257
	 dw    147,247
	 dw    160,234
	 dw    171,218
	 dw    179,200
	 dw    183,180
	 dw    185,160
	 dw    183,140
	 dw    179,120
	 dw    171,102
	 dw    160,086
	 dw    147,073
	 dw    133,063
	 dw    117,057
	 dw    100,055
	 dw    083,057
	 dw    067,063
	 dw    053,073
	 dw    040,086
	 dw    029,102
	 dw    021,120
	 dw    017,140
	 dw    015,160
	 dw    017,180
	 dw    021,200
	 dw    029,218
	 dw    040,234
	 dw    053,247
	 dw    067,257
lp	 dw    083,263

;
;  Control location (controls current colors and pattern)
;  bit 0 = palette, bit 1 = pattern select, bits 2-5 = bg color
;
control  db    0

;  Memory location used by gr_lin routine
;
direc	 dw    ?	      ;direction (+1/-1 for forward/backward)
;-------------------------------------------------------------------------------


;-------------------------------------------------------------------------------
	  .stack 100h
;-------------------------------------------------------------------------------

	 end start
