; 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