	page	60,132

	title	Prt480	- FX-80 - EGA - Windows print screen patch

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Written by:	Michael Geary					    ;;
;;		P.O. Box 1479					    ;;
;;		Los Gatos, CA 95031				    ;;
;;		(408) 354-0175					    ;;
;;								    ;;
;; This program is placed in the public domain without restriction. ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; macros

L	MACRO	name, type, value
	public	name
  ifb	  <type>
name	label	near
  else
    ifb     <value>
name	label	type
    else
name	type	value
    endif
  endif
	ENDM

print	MACRO	char
	  ifnb	<char>
	mov	al, char
	  endif
	call	prtChar
	ENDM

DOS	MACRO	fun
	mov	ah, fun
	int	21h
	ENDM

; character codes
CR	equ	13
LF	equ	10
FF	equ	12
ESC	equ	27

; interrupt numbers
PrtSc	equ	05h

; interrupt vectors and BIOS data in absolute zero segment
absSeg	segment at 0

	org	PrtSc * 4
L PrtScIntOffset, word
	org	PrtSc * 4 + 2
L PrtScIntSeg, word

	org	417h
L KbFlags, byte

	org	449h
L CrtMode, byte

	org	463h
L Addr6845, word

absSeg	endS

; screen addresses & stuff
EgaSeg		equ	0A000h
GrafAddr	equ	3CEh
ReadMapSelReg	equ	4

stkSize equ	256

codeSeg segment para public 'code'

theEnd	equ	offset top + stkSize + 15

	assume	cs:codeSeg, ds:codeSeg, ss:codeSeg, es:absSeg

	org	100h
L PrintScreen

	mov	dx, offset credit
	DOS	9

	mov	SPinit, theEnd
	mov	SSinit, cs

	DOS	34h
	mov	DosBusyOffset, bx
	mov	DosBusySeg, es

	xor	ax, ax
	mov	es, ax

	CLI
	mov	ax, PrtScIntOffset
	mov	SaveIntOffset, ax
	mov	ax, PrtScIntSeg
	mov	SaveIntSeg, ax

	mov	PrtScIntOffset, offset doPrint
	push	cs
	pop	PrtScIntSeg
	STI

	mov	dx, theEnd
	int	27h


L SPinit, dw, 0 		; value to load into SP
L SSinit, dw, 0 		; and SS when dump routine called

L SPsave, dw, 0 		; saved value of SP and SS at start of dump
L SSsave, dw, 0 		; routine (SSsave=0 means not currently active)

L SaveInt, dword		; holds the "other" PrtSc interrupt vector
SaveIntOffset	dw	?
SaveIntSeg	dw	?

L DosBusyX, dword		; long pointer to "DOS busy" flag
DosBusyOffset	dw	?
DosBusySeg	dw	?

L paper216ths, db, 0		; paper advance after pass (12/216 or 24/216 ")
L dotsPerPass, dw, 0		; # dot rows covered in a pass (4 or 8)

L toDisk, db, 0 		; 0=printer, 1=file

L fileHdr, byte
	db	044h, 061h, 06Eh, 04Dh, 080h, 002h, 0E0h, 001h
	db	029h, 000h, 029h, 000h, 029h, 000h, 029h, 000h
	db	080h, 002h, 0E0h, 001h, 000h, 000h, 000h, 000h
	db	02Ah, 02Ch, 073h, 000h, 05Bh, 000h, 001h, 074h

L credit, byte
	db	CR,LF,'Prt480 version 0.9 by Michael Geary',CR,LF,'$'

L fileName, byte
	db	'\screen.msp',0

L DotTable, byte	; Converts screen color to printer color (0, 1, 3)

	db	3	; 00 Black
	db	0	; 01 Red
	db	1	; 02 Green
	db	0	; 03 Yellow
	db	1	; 04 Blue
	db	1	; 05 Purple
	db	0	; 06 Light Blue
	db	0	; 07 White
	db	0	; 08
	db	0	; 09
	db	0	; 0A
	db	0	; 0B
	db	0	; 0C
	db	0	; 0D
	db	0	; 0E
	db	0	; 0F

; Prt480 entry point

L doPrint
	assume	cs:codeSeg, ds:nothing, ss:nothing, es:nothing

	cmp	SSsave, 0
	jz	startOK
	jmp	allDone 	    ; already running, no dice

L startOK
	CLI
	mov	SPsave, sp
	mov	SSsave, ss
	mov	ss, SSinit
	mov	sp, SPinit
	STI

	push	ds
	push	es
	push	ax
	push	bx
	push	cx
	push	dx
	push	si
	push	di
	push	bp

	xor	ax, ax
	mov	es, ax

	cmp	es:CrtMode, 10h
	je	newDump

	cmp	es:CrtMode, 7		; weird - Windows sets CrtMode=7
	jne	oldDump
	cmp	es:Addr6845, 3D4h	; and Addr6845=3D4
	je	newDump

L oldDump
	pushf
	call	SaveInt 		; call the other dump routine
	jmp	prtDone

L newDump
	push	cs
	pop	ds
	assume	ds:codeSeg		; leave DS alone from now on

	test	es:KbFlags, 20h 	; is Num Lock down?
	jz	prnDump
	jmp	fileDump		; yes, do file dump
L prnDump
	mov	toDisk, 0

	mov	cx, -1			; CX = dot col
	mov	paper216ths, 24
	mov	dotsPerPass, 8

	test	es:KbFlags, 1		; is right shift down?
	jz	doGraphics		; no, single strike

	sub	cx, 4
	mov	paper216ths, 12
	mov	dotsPerPass, 4

L doGraphics
	print	CR			; CR-LF to tension paper
	print	LF
	print	LF

L nextCol				; here to begin a row

	print	ESC
	print	'*'                     ; set 576 dot graphics mode
	print	5
	print	010h			; and print 529 dots
	print	2

	mov	dx, 47
L indent
	print	0
	dec	dx
	jnz	indent

	mov	dx, 480 		; DX = dot row

L nextRow				; here for each dot column
	call	GetDotRow
	print

	dec	dx
	jge	nextRow

L advPaper
	print	CR
	print	ESC
	print	'J'
	print	paper216ths

	add	cx, dotsPerPass
	cmp	cx, 640
	jle	nextCol

	print	FF

L prtDone
	pop	bp
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	pop	es
	pop	ds
	CLI
	mov	ss, cs: SSsave
	mov	sp, cs: SPsave
	mov	cs: SSsave, 0
L allDone
	STI
	iRet


L fileDump
	mov	toDisk, 1
	les	bx, DosBusyX
	cmp	byte ptr es:[bx], 0
	jnz	fileErr

	mov	dx, offset fileName
	xor	cx, cx
	DOS	3Ch			; create file
	jc	fileErr

	mov	bx, ax

	mov	cx, 32
	mov	dx, offset fileHdr
	DOS	40h			; write header
	jc	closeErr
	cmp	ax, cx
	jne	closeErr

	mov	cx, 1
	call	Beep

	xor	dx, dx			; DX = dot row
L fileLoop
	xor	cx, cx
	mov	di, offset rowBuf
L colLoop
	call	GetDotByte
	not	al
	mov	[di], al
	inc	di
	add	cx, 8
	cmp	cx, 640
	jb	colLoop

	mov	cx, 80
	push	dx
	mov	dx, offset rowBuf
	DOS	40h			; write row
	pop	dx
	jc	closeErr
	cmp	ax, cx
	jne	closeErr

	inc	dx
	cmp	dx, 480
	jl	fileLoop

	DOS	3Eh			; close file
	jc	fileErr

	mov	cx, 1
	call	Beep
	mov	cx, 9000h
	loop	$
	mov	cx, 1
	call	Beep

	jmp	prtDone

L closeErr
	DOS	3Eh			; close file
L fileErr
	mov	cx, 6
	call	Beep
	jmp	prtDone


; Call with DX=topmost dot row, CX=dot col.
; Returns with the dot row byte for printing in AL.

L GetDotRow

	push	bx
	push	bp
	xor	bp, bp			; BP=dot col offset
	xor	bl, bl			; BL=dot accumulator
L nextDot				; here for each dot horizontally
	push	cx
	add	cx, bp
	call	GetDot
	pop	cx

	cmp	bp, dotsPerPass
	jl	dotAndOr
	shr	al, 1			; pass 2
    L dotAndOr
	and	al, 1

	shl	bl, 1
	or	bl, al
	inc	bp
	cmp	bp, 8
	jl	nextDot

	mov	al, bl
	pop	bp
	pop	bx
	ret


L GetDotByte

	push	bx
	push	bp
	xor	bp, bp			; BP=dot col offset
	xor	bl, bl			; BL=dot accumulator
L bnextDot				; here for each dot hoizontally
	push	cx
	add	cx, bp
	call	GetDot
	pop	cx

	and	al, 1

	shl	bl, 1
	or	bl, al
	inc	bp
	cmp	bp, 8
	jl	bnextDot

	mov	al, bl
	pop	bp
	pop	bx
	ret


L GetDot

	push	bx
	push	cx
	push	dx

	xor	al, al

	; return 0 if outside screen bounds
	cmp	cx, -1
	jl	gdRet
	je	gdHack
	cmp	cx, 640
	jg	gdRet
	je	gdHack

	cmp	dx, -1
	jl	gdRet
	je	gdHack
	cmp	dx, 480
	jg	gdRet			; return 0 if outside screen bounds
	je	gdHack

	call	GetCrtDot

	; try a hack for RED
	cmp	toDisk, 0
	jnz	gdLookup
	cmp	al, 1
	jne	gdLookup

	mov	al, dl
	and	al, 1
	jmp	gdRet

;	mov	al, 0
;	test	dl, 3
;	jz	gdRet
;	mov	al, 1
;	jmp	gdRet

;	; try a similar one for LIGHT BLUE
;	cmp	al, 6
;	jne	gdLookup
;	test	cl, 1
;	jz	gdLookup
;
;	mov	al, 0
;	test	dl, 3
;	jnz	gdRet
;	mov	al, 1
;	jmp	gdRet

L gdLookup
	mov	bl, al
	xor	bh, bh
	mov	al, DotTable[bx]
	jmp	gdRet

L gdHack
	mov	al, 3			; black borders

L gdRet
	pop	dx
	pop	cx
	pop	bx
	ret


L GetCrtDot

	push	ds
	push	dx
	push	cx
	push	bx
	push	di

;   byteX bufX:DS:DI;

;	offset(bufX) = row*CrtCols + (col >> 3);
	    mov     di, cx
	    shr     di, 1
	    shr     di, 1
	    shr     di, 1
	    mov     ax, 80
	    mul     dx
	    add     di, ax

;	seg(bufX) = EgaSeg;
	    ; later

;	col &= 7;
	    and     cl, 7

;	    Out(GrafAddr,ReadMapSelReg);
		mov	dx, GrafAddr
		mov	al, ReadMapSelReg
		out	dx, al
		inc	dx

;	    bit = 1 << (7 - col);
		neg	cl
		add	cl, 7
		mov	bl, 1
		shl	bl, cl

;	    mask = 1;
		mov	ch, 1

;*	    seg(bufX) = EgaSeg;
		mov	ax, EgaSeg
		mov	ds, ax
		ASSUME	ds:nothing

;	    odot = map = 0;
		xor	ax, ax

;	    do {
	    rwdErL:

;		Out(GrafAddr+1,map);
		    out     dx, al

;		if( *bufX & bit )  odot |= mask;
		    test    bl, [di]
		    jz	    rwdErN
		    or	    ah, ch
		rwdErN:

;	    } while( mask <<= 1,  ++map < 4 );
		shl	ch, 1
		inc	al
		cmp	al, 4
		jb	rwdErL

	mov	al, ah

	pop	di
	pop	bx
	pop	cx
	pop	dx
	pop	ds

	ret

	ASSUME	ds:codeSeg


;prints the character in AL

L prtChar

	push	dx
	xor	dx, dx
	xor	ah, ah
	int	17h
	pop	dx
	test	ah, 25h
	jz	prtRet
	pop	dx		; get rid of our return address
	jmp	prtDone

L prtRet
	ret


L Beep
	push	bx
	push	cx

	mov	al, 0B6h
	out	43h, al

	xor	al, al
	out	42h, al

	mov	al, 6
	out	42h, al

	in	al, 61h
	mov	ch, al
	or	al, 3
	out	61h, al

	mov	bx, 2000h
    L beepLoop
	dec	bx
	jnz	beepLoop

	dec	cl
	jnz	beepLoop

	mov	al, ch
	out	61h, al

	pop	cx
	pop	bx

	ret


L rowBuf, byte

top	equ	rowBuf + 80

codeSeg endS

	end	PrintScreen
