Metropoli BBS
VIEWER: monograf.asm MODE: TEXT (ASCII)
;	MONOGRAF.DRV -- Lotus Driver for Graphics on Monochrome Display
;	============
;
;		(For use with Lotus 1-2-3 Version 1A)
;
;	(C) Copyright Charles Petzold, 1985

CSEG		Segment
		Assume	CS:CSEG

		Org	0
Beginning	dw	Offset EndDriver,1,1,Offset Initialize

		Org	18h
		db	"Monochrome Graphics (C) Charles Petzold, 1985",0

		Org	40h
		dw	40 * 8 - 1		; Maximum Dot Column
		dw	25 * 8 - 1		; Maximum Dot Row
		dw	10, 7, 6, 10, 7, 6, 256
		db	-1			; For one monitor

		Org	53h
		Jmp	Near Ptr ClearScreen	; Call 0 -- Clear Screen
		Jmp	Near Ptr ColorSet	; Call 1 -- Set Color
		Jmp	Near Ptr SetAddress	; Call 2 -- Set Row/Col Addr
		Jmp	Near Ptr DrawLine	; Call 3 -- Draw a Line
		Jmp	Near Ptr Initialize	; Call 4 -- Write Dot (nothing)
		Jmp	Near Ptr WriteChar	; Call 5 -- Write a Character
		Jmp	Near Ptr DrawBlock	; Call 6 -- Draw a Block
		Jmp	Near Ptr Initialize	; Call 7 -- Read Dot (nothing)
		Jmp	Near Ptr Initialize	; Call 8 -- Video Reset

;	Initialization Routine
;	----------------------

Initialize	Proc	Far
		Mov	AX,0		; This is standard
		Or	AX,AX		;   for all drivers 
		Ret
Initialize	EndP

;	Common Data Used in Routines
;	-----------------------------------

CharacterRow	dw	?				; from 0 to 24
CharacterCol	dw	?				; from 0 to 79
ScreenAddress	dw	?,0B000h			; Offset & Segment
CurrentColor	db	?,7				; For Screen Output
Colors		db	219,219,178,177,176,219,178	; Actually blocks

;	Row and Column Conversion of AX from graphics to character
;	----------------------------------------------------------

Rounder		dw	0			; Value to add before division
Divisor		db	?			; Value to divide by
MaxDots		dw	?			; Number of dots

RowConvertRnd:	Mov	[Rounder],4		; Row rounding -- add 4
RowConvert:	Mov	[Divisor],8		; Row normal -- divide by 8
		Mov	[MaxDots],200		; 25 lines times 8 dots
		Jmp	Short Convert		; And do generalized conversion

ColConvertRnd:	Mov	[Rounder],2		; Column rounding -- add 2
ColConvert:	Mov	[Divisor],4		; Will divide by 4
		Mov	[MaxDots],320		; 40 columns times 4 dots

Convert:	Cmp	AX,[MaxDots]		; See if graphics value OK
		Jb	OKToConvert		; It is if under maximum
		Jl	Negative		; But could be negative
		Sub	AX,[MaxDots]		; Otherwise wrap down
		Jmp	Convert			; And check again
Negative:	Add	AX,[MaxDots]		; Negatives wrap up
		Jmp	Convert			; And check again

OkToConvert:	Add	AX,[Rounder]		; Add rounding value
		Div	[Divisor]		; Divide	
		Cbw				; And convert to word
		Mov	[Rounder],0		; For next time through
		Ret

;	Calc Offset -- DX, CX character positions in
;	-----------

CalcOffset:	Push	AX
		Push	DX

		Mov	AX,80			; Columns Per Line
		Mul	DX			; AX now at beginning of row
		Add	AX,CX			; Add column value
		Add	AX,AX			; Double for attributes
		Mov	[ScreenAddress],AX	; Save as the current address

		Pop	DX
		Pop	AX

		Ret

;	Address Convert -- DX, CX row and column converted to character
;	---------------

AddrConvert:	Push	AX

		Mov	AX,DX			; This is graphics row
		Call	RowConvert		; Convert to character row
		Mov	DX,AX			; Save back in DX
		Mov	[CharacterRow],AX	; And save value in memory

		Mov	AX,CX			; This is graphics column
		Call	ColConvert		; Convert to character column
		Mov	CX,AX			; Back in CX
		Mov	[CharacterCol],AX	; And value also saved

		Call	CalcOffset		; Find the screen destination

		Pop	AX

		Ret

;	Call 0 -- Clear Screen -- AL = 0 for B&W
;	======================        -1 for Color

ClearScreen	Proc	Far
		Mov	AX,0B000h	; Monochrome Segment 
		Mov	ES,AX		; Set EX to it 
		Sub	DI,DI		; Start at zero
		Mov	CX,25 * 80	; Number of characters
		Mov	AX,0720h	; Blanks only
		Cld			; Forward direction
		Rep	Stosw		; Do it
		Ret
ClearScreen	EndP

;	Call 1 -- Color Set -- AL = Color (0, 1-6)
;	-------------------

ColorSet	Proc	Far
		Mov	BX,Offset Colors	; Blocks for 7 colors 
		Xlat	Colors			; Translate the bytes
		Mov	[CurrentColor],AL	; And save it
		Ret
ColorSet	EndP

;	Call 2 -- Set Address -- DX = Graphics Row
;	---------------------	 CX = Graphics Columns

SetAddress	Proc	Far
		Call	AddrConvert		; One routine does it all
		Ret
SetAddress	EndP

;	Call 3 -- Draw Line -- DX = End Row
;	-------------------    CX = End Column

DrawLine	Proc	Far
		Les	DI,DWord Ptr [ScreenAddress]	; Beginning address
		Mov	AX,[CharacterCol]	; AX now beginning column
		Mov	BX,[CharacterRow]	; BX now beginning row

		Call	AddrConvert		; CX,DX now ending col, row

		Cmp	AX,CX			; See if cols are the same
		Je	VertLine		; If so, it's vertical line	

		Cmp	BX,DX			; See if rows are the same
		Jne	DrawLineEnd		; If not, don't draw anything

HorizLine:	Sub	CX,AX			; Find the number of bytes
		Mov	BX,2			; Increment for next byte
		Mov	AL,196			; The horizontal line
		Mov	AH,179			; The vertical line
		Jae	DrawTheLine		; If CX > AX, left to right
		Jmp	Short ReverseLine	; Otherwise right to left

VertLine:	Mov	CX,DX			; This is the ending column
		Sub	CX,BX			; Subtract beginning from it
		Mov	BX,80 * 2		; Increment for next line
		Mov	AL,179			; The vertical line
		Mov	AH,196			; The horizontal line
		Jae	DrawTheLine		; If CX > BX, up to down

ReverseLine:	Neg	BX			; Reverse Increment
		Neg	CX			; Make a positive value

DrawTheLine:	Inc	CX			; One more byte than calced 

DrawLineLoop:	Cmp	Byte Ptr ES:[DI],197	; See if criss-cross there
		Je	DrawLineCont		; If so, branch around

		Cmp	ES:[DI],AH		; See if opposite line
		Jne	NoOverLap		; If not, skip next code

		Mov	Byte Ptr ES:[DI],197	; Write out criss-cross
		Jmp	Short DrawLineCont	; And continue

NoOverLap:	Mov	ES:[DI],AL		; Display line chararacter

DrawLineCont:	Add	DI,BX			; Next destination
		Loop	DrawLineLoop		; For CX repetitions

DrawLineEnd:	Ret
DrawLine	EndP

;	Call 5 -- Write Character -- DX, CX = row, col; BX = count,
;	-------------------------	AH = direction, AL = type

Direction	db	?

WriteChar	Proc	Far

		Push	BX			; Save count
		Add	BX,BX			; Initialize adjustment
		Mov	[Direction],AH		; Save direction

		Or	AL,AL			; Branch according to type
		Jz	WriteType0
		Dec	AL
		Jz	WriteType1
		Dec	AL
		Jz	WriteType2
		Dec	AL
		Jz	WriteType3

WriteType4:	Mov	AX,4			; Adjustment to row
		Jmp	Short WriteCharCont

WriteType3:	Add	BX,BX			; Center on column
WriteType2:	Sub	AX,AX			; No adjustment to row
		Jmp	Short WriteCharCont

WriteType1:	Sub	BX,BX			; No adjustment on column
WriteType0:	Mov	AX,2			; Adjustment to row

WriteCharCont:	Cmp	[Direction],0		; Check the direction
		Jz	HorizChars

		Sub	DX,BX			; Vertical -- adjust row
		Sub	DX,BX
		Sub	CX,AX			; Adjust column
		Mov	AX,80 * 2 - 1		; Increment for writes 
		Jmp	Short DoWriteChar		

HorizChars:	Sub	DX,AX			; Horizontal -- adjust row
		Sub	DX,AX			
		Sub	CX,BX			; Adjust column
		Mov	AX,1			; Increment for writes

DoWriteChar:	Call	AddrConvert		; Convert the address
		Les	DI,DWord Ptr [ScreenAddress]	; Get video address
		Cld
		Pop	CX			; Get back character count
		Jcxz	WriteCharEnd		; Do nothing if no characters

CharacterLoop:	Movsb				; Write character to display
		Add	DI,AX			; Increment address
		Loop	CharacterLoop		; Do it CX times

WriteCharEnd:	Ret
WriteChar	EndP

;	Call 6 -- Draw Block -- BX,DX = Rows; AX,CX = Columns
;	--------------------

DrawBlock	Proc	Far
		Call	ColConvertRnd		; AX now first char col
		Xchg	AX,CX			; Switch with 2nd graph col
		Call	ColConvertRnd		; AX now 2nd char col
		Cmp	AX,CX			; Compare two char cols
		Je	DrawBlockEnd		; End routine if the same
		Ja	NowDoRow		; If CX lowest, just continue

		Xchg	AX,CX			; Otherwise switch them

NowDoRow:	Xchg	AX,BX			; AX now 1st graph row
		Call	RowConvertRnd		; AX now 1st char row
		Xchg	AX,DX			; AX now 2nd graph row
		Call	RowConvertRnd		; AX now 2nd char row
		Cmp	AX,DX			; Compare two character columns
		Je	DrawBlockEnd		; End routine if the same
		Ja	BlockRowLoop		; If DX lowest, just continue

		Xchg	AX,DX			; Otherwise switch them

BlockRowLoop:	Push	CX			; Beginning Column
		Push	BX			; Ending Column 

BlockColLoop:	Call	CalcOffset		; Calculate screen address
		Les	DI,DWord Ptr [ScreenAddress]	; And set ES:DI

		Push	Word Ptr [CurrentColor]	; Push the current color
		Pop	ES:[DI]			; And Pop it on the screen

		Inc	CX			; Next Column
		Cmp	CX,BX			; Are we an end?
		Jb	BlockColLoop		; Nope -- loop again

		Pop	BX			; Get back beginning col
		Pop	CX			; And the end

		Inc	DX			; Prepare for next row
		Cmp	DX,AX			; Are we at the end?
		Jb	BlockRowLoop		; If not, loop 

DrawBlockEnd:	Ret
DrawBlock	EndP

		Org	$ + 16 - (($ - Beginning) Mod 16)
EndDriver	Label	Byte

CSEG		EndS
		End

[ RETURN TO DIRECTORY ]