Category : Files from Magazines
Archive   : PCTJ8805.ZIP
Filename : VGALOAD.ASM

 
Output of file : VGALOAD.ASM contained in archive : PCTJ8805.ZIP
;
; Name: VGALOAD
;
; Desc: Simulates a small portion of the IBM Display
; Adapter 8514/A's software interface on the
; Video Graphics Array. This module is a TSR
; program which allows the HDIBENCH benchmark
; to be run on the VGA for comparison. Once
; assembly and link is complete, create executable
; VGALOAD.COM from VGALOAD.EXE using EXE2BIN.
;
;
XRES equ 640
YRES equ 480
;
DOS_CONOUT equ 09H
DOS_SETVEC equ 25H
DOS_TERMRES equ 31H
;
HDI_VECTOR equ 7FH
HDI_MAGIC equ 0105H
;
GRAPHICS_MODE equ 13H
BYTESPERLINE equ 80
SCREEN equ 0A000H
;
EGA_BITMASK equ 8 ; Index of bit mask register
EGA_GRAFPORT equ 3CEH ; Port address of index register
EGA_ALLBITS equ 0FF08H ; Bit mask index with 0xFF bit mask
;
OFFMASK macro ; Calculate byte offset and bit mask
mov CH, AL ; Save the mask value
mov CL, 5 ; Shift offset into high byte
shl AX, CL ; Byte offset is in AH
and CH, 7 ; Mask to the bit offset in that byte
mov AL, CH ; And put the mask in AL
endm
;
_VGA segment
assume CS:_VGA, DS:Nothing, ES:Nothing, SS:Nothing
org 100H ; We'll convert this to a .COM file
Begin: jmp Start ; Skip over data area
;
; funcTable is a table of pointers to far functions called
; by the adapter interface CALLAFI.ASM code
;
funcTable label dword
dw offset HLINE, ? ; HLINE(X)
dw offset Unimp, ? ; HCLINE(X)
dw offset Unimp, ? ; HRLINE(X)
dw offset Unimp, ? ; HCRLINE(X)
dw offset Unimp, ? ; HSCP(X)
dw offset Unimp, ? ; HBAR()
dw offset Unimp, ? ; HEAR(X)
dw offset HSCOL, ? ; HSCOL(X)
dw offset HOPEN, ? ; HOPEN(X)
dw offset HSMX, ? ; HSMX(X)
dw offset Unimp, ? ; HSBCOL(X)
dw offset Unimp, ? ; HSLT(X)
dw offset Unimp, ? ; HSLW(X)
dw offset HEGS, ? ; HEGS()
dw offset Unimp, ? ; HSGQ(X)
dw offset Unimp, ? ; HSCMP(X)
dw offset Unimp, ? ; HINT(X)
dw offset Unimp, ? ; HSPATTO(X)
dw offset Unimp, ? ; HSPATT(X)
dw offset Unimp, ? ; HLDPAL(X)
dw offset Unimp, ? ; HSHS(X)
dw offset Unimp, ? ; HBBW(X)
dw offset Unimp, ? ; HCBBW(X)
dw offset Unimp, ? ; HBBR(X)
dw offset Unimp, ? ; HBBCHN(X)
dw offset Unimp, ? ; HBBC(X)
dw offset Unimp, ? ; HSCOORD(X)
dw offset Unimp, ? ; HQCOORD(X)
dw offset Unimp, ? ; HSMODE(X)
dw offset HQMODE, ? ; HQMODE(X)
dw offset Unimp, ? ; HQMODES(X)
dw offset HQDPS, ? ; HQDPS(X)
dw offset HRECT, ? ; HRECT(X)
dw offset Unimp, ? ; HSBP(X)
dw offset HCLOSE, ? ; HCLOSE(X)
dw offset Unimp, ? ; HESC()
dw offset Unimp, ? ; HXLATE(X)
dw offset Unimp, ? ; HSCS(X)
dw offset Unimp, ? ; HCHST(X)
dw offset Unimp, ? ; HCCHST(X)
dw offset Unimp, ? ; ABLKMFI(X)
dw offset Unimp, ? ; ABLKCGA(X)
dw offset Unimp, ? ; AERASE(X)
dw offset Unimp, ? ; ASCROLL(X)
dw offset Unimp, ? ; ACURSOR(X)
dw offset Unimp, ? ; ASCUR(X)
dw offset Unimp, ? ; ASFONT(X)
dw offset Unimp, ? ; AXLATE(X)
dw offset HINIT, ? ; HINIT(X)
dw offset Unimp, ? ; HSYNC(X)
dw offset Unimp, ? ; HMRK(X)
dw offset Unimp, ? ; HCMRK(X)
dw offset Unimp, ? ; HSMARK(X)
dw offset Unimp, ? ; HSLPC()
dw offset Unimp, ? ; HRLPC()
dw offset Unimp, ? ; HQCP(X)
dw offset Unimp, ? ; HQDFPAL(X)
dw offset Unimp, ? ; HSPAL(X)
dw offset Unimp, ? ; HRPAL(X)
funcEnd equ $
funcCount equ (funcEnd - funcTable) / 4
;
firstB db 0FFH, 07FH, 03FH, 01FH, 00FH, 007H, 003H, 001H
lastB db 080H, 0C0H, 0E0H, 0F0H, 0F8H, 0FCH, 0FEH, 0FFH
begX dw 0
endX dw 0
saveLen dw 0
;
SignDeltaX dw ?
deltax dw ?
deltay dw ?
incX dw ?
incY dw ?
XCoord dw ?
YCoord dw ?
counter dw ?
increment dw ?
LineHeight dw ?
;
savedMode db ?
curColor dw ?
message db 'VGA Adapter Interface Simulation loaded'
db 13, 10, '$'
;
; Interrupt handler
;
Handler: cmp AX, HDI_MAGIC ; Check for magic command number
jne NotLegal ; It's the only thing we understand
lea DX, funcTable ; Return CX:DX pointing to func table
mov CX, CS
clc ; Indicate success by clearing carry
NotLegal: iret
;
; Function Handlers
;
HArgs STRUC
hArgLen dw ?
HStartX dw ?
HStartY dw ?
HWidth dw ?
HHeight dw ?
HArgs ENDS
;
LArgs STRUC

argLen dw ?
StartX dw ?
StartY dw ?
LastX dw ?
EndY dw ?
LArgs ENDS
;
HLINE proc far
push bp
mov bp, sp
mov ax, 0003H
mov dx, 03CEH
out dx, ax ; Set REPLACE writing mode
les DI, [BP + 6]
mov ax, ES:[DI].EndY
mov dx, ES:[DI].StartY
mov cx, ES:[DI].LastX
mov bx, ES:[DI].StartX
cmp ax, dx
jg AANoSwap
jnz AANotHorz
jmp AAThinHorzLine
AANotHorz:
mov ES:[DI].EndY, dx
mov ES:[DI].StartY, ax
xchg ax, dx
mov ES:[DI].LastX, bx
mov ES:[DI].StartX, cx
mov si, cx
mov cx, bx
mov bx, si
AANoSwap:
sub ax, dx ; deltax = endX - StartX
mov deltay, ax
sub cx, bx ; deltay = endY - StartY
mov deltax, cx
jnz AADeltaXOK ; Nor do we do vertical ones
jmp AAThinVertLine
AADeltaXOK:
mov si, 1 ; if (dx > 0)
cmp cx, 0 ; SignDeltaX = 1, incX = DeltaX
jg AAPosDeltaX ; else
mov si, -1 ; SignDeltaX = -1, incX = -DeltaX
neg cx
AAPosDeltaX:
mov SignDeltaX, si
mov incX, cx

mov incY, ax ; incY = dy

mov si, cx ; if (incX > incY)
cmp cx, ax ; increment = IncX
jg AAIncIsIncX ; else
mov si, ax ; increment = IncY
AAIncIsIncX:
mov increment, si

mov ax, ES:[DI].StartY
mov cx, BYTESPERLINE
mul cx
mov dx, ES:[DI].StartX
mov cx, dx
shr dx, 1
shr dx, 1
shr dx, 1
add ax, dx
mov di, ax
mov dx, 3CEh
mov ax, 0005H ; Set Write Mode 0
out dx, ax
mov bx, curColor
mov ah, bl
mov al, 0
out dx, ax ; Set Set/Reset registers
mov ax, 0FF01H
out dx, ax ; Enable Set/Reset for all planes
;
mov ax, SCREEN
mov es, ax
;
and cx, 7
mov ah, 80h
shr ah, cl
mov al, 8
mov dx, 3CEh
mov si, increment
xor cx, cx
mov counter, cx
mov XCoord, cx
mov YCoord, cx

AANextPixel:
out dx, ax
or es:[di], bl

mov cx, XCoord ; XCoord += incX
add cx, incX
mov XCoord, cx

cmp cx, si ; if (XCoord > inc) {
jle AADontIncX ; XCoord -= inc;
sub XCoord, si ; XCoord += SignDeltX
test SignDeltaX, 8000h ; }
jnz AAShiftLeft
ror ah, 1
adc di, 0
jmp AADontIncX
AAShiftLeft:
rol ah, 1
sbb di, 0
AADontIncX:
mov cx, YCoord ; YCoord += incY
add cx, incY
mov YCoord, cx

cmp cx, si
jle AADontIncY
sub cx, si
mov YCoord, cx
add di, BytesPerLine
AADontIncY:
mov cx, counter
cmp cx, si
jg AAQuitDiagLine
inc cx
mov counter, cx
jmp AANextPixel
AAQuitDiagLine:
pop bp
ret 4
;
; Special case #1 - Thin Vertical Line
;
AAThinVertLine:
mov ax, ES:[DI].StartY
cmp ax, ES:[DI].EndY
jle AAYSortOK
xchg ax, ES:[DI].EndY
mov ES:[DI].StartY, ax
AAYSortOK:
mov ax, ES:[DI].StartY
mov LineHeight, ax
mov bx, BytesPerLine
mul bx
mov cx, ES:[DI].StartX
mov dx, cx
shr cx, 1
shr cx, 1
shr cx, 1
add ax, cx
mov si, ax
mov ax, ES:[DI].EndY
cmp ax, LineHeight
neg LineHeight
add LineHeight, ax
inc LineHeight
and dx, 7
mov cl, dl
mov ah, 80H
shr ah, cl
mov dx, 3CEh
mov al, 8
out dx, ax ; Set Bit Mask register
mov ax, 0005H
out dx, ax ; Set Write Mode 0
;
mov bx, curColor
mov ah, bl
mov al, 0
out dx, ax ; Set Set/Reset register
mov ax, 0FF01H
out dx, ax ; Enable Set/Reset for all planes
mov cx, LineHeight
;
mov ax, SCREEN
mov es, ax
;
AAVLNextPixel: or byte ptr es:[si], bl
add si, BYTESPERLINE
loop AAVLNextPixel

pop bp
ret 4
;
; Special Case #2 - Thin Horizontal Line
;
AAThinHorzLine:
mov dx, 3CEh
mov ax, 0001H ; Clear Set/Reset for all planes
out dx, ax
mov ax, 0205H
out dx, ax ; Set Write Mode 2
;
; Calculate the byte offset into the display buffer of the start of
; the scan line indicated by cury. At 80 bytes per scan line, we
; can multiply the cury value by 80 (do shifts and adds).
;
mov SI, DI
mov AX, ES:[SI].StartY ; Get the parameter value
mov CL, 4 ; Multiply by 16
shl AX, CL
mov BX, AX ; Save the temporary value
shl AX, 1 ; Multiply the temp by 4
shl AX, 1 ; To get original times 64
add BX, AX ; And add temp (64x + 16x = 80x)
mov DI, BX ; Then save the temp
;
; Get the offset into the line of the first byte and get the bit
; mask to be used for that byte. Since there are only 8 possible
; values, the masks are stored in tables.
;
mov DX, ES:[SI].StartX ; Get the X coordinate of the start
mov AX, ES:[SI].LastX ; Calculate the endpoint first
OFFMASK ; Convert to byte offset and bit mask
mov endX, AX ; And save those values
mov AX, DX ; Do the same for the start X
OFFMASK
mov begX, AX ; Save the ending X coordinate
mov BL, AL ; Get the first byte mask index
mov BH, 0
mov AL, firstB[BX] ; And look it up in the table
;
; Check to see if the entire line lies within one byte by comparing
; the start and end offsets.
;
mov CH, 0
mov CL, byte ptr endX + 1 ; Get the offset of the end
sub CL, AH ; Subtract the offset of the start
jnz AAtwoB ; If nonzero, it's at least two bytes
;
; This is the first special case - the line fits entirely in one byte
; of the display buffer. We find the bit pattern for that byte by
; ANDing the first byte and last byte together, then we write it.
;
mov BL, byte ptr endX ; Find the bitmask for the end byte
mov BH, 0
mov AH, lastB[BX] ; From the last byte mask table
and AH, AL ; Calculate the bit mask in AH
mov AL, EGA_BITMASK ; We'll write that to the bit mask
mov DX, EGA_GRAFPORT ; In the graphics control port
out DX, AX
mov BL, byte ptr begX + 1 ; Get the byte offset of the start
mov BH, 0
add DI, BX ; Add the offset of the line's start
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the EGA latches
mov AX, curColor ; Get the current drawing color
stosb ; Then write the pixel data
jmp AASLXDone ; And we're done.....
;
; The line crosses at least one byte boundary. Calculate how many
; bytes are in the middle, then draw the first byte, fill the middle
; ones with solid data, then draw the last byte.
;
AAtwoB: mov BL, AH ; Save the byte offset of the start
mov AH, AL ; Set up to write the bit mask
mov AL, EGA_BITMASK ; register with the first byte
mov DX, EGA_GRAFPORT
out DX, AX
mov BH, 0 ; Set up the byte offset in the line
add DI, BX ; Add the start of the scan line
mov DX, curColor ; Use the proper current color
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the latches
mov AL, DL ; Get the current color value
stosb ; And write the whole byte
;
; We've got the first byte done - find out how many solid bytes
; are in the interior portion of the line. The CX register still
; holds the difference between the start and end byte offsets.
;
dec CX ; # of bytes is difference - 1
jcxz AAnoMid ; There are no middle bytes
;
; There are solid bytes in the interior, so fill them.
;
mov BL, DL ; Save the current color
mov AX, EGA_ALLBITS ; Bitmask and 0xFF mask value
mov DX, EGA_GRAFPORT
out DX, AX ; Write both port and mask value
mov DL, BL ; Restore the color for the end
;
; Set up for fastest possible filling. If the interior is only one
; byte, this won't quite be the fastest possible, but if we took the
; time to test for one byte and then did it the faster way it would
; override any benefit we got from the special case anyway.
;
;
; Since this is a solid fill in REPLACE mode, we only have to load
; the latches on the EGA once at the start. This practically cuts
; the inner loop time in half, since we only write - no reading.
;
mov AL, BL ; Restore the current color index
rep stosb ; Eight pixels in 13 clocks
;
; Write the last byte on the line, whether or not there was an
; interior region to fill.
;
AAnoMid: mov BL, byte ptr endX ; Find the last byte bitmask
mov BH, 0
mov AH, lastB[BX] ; From the byte mask table
mov BL, DL ; Save the color again
mov AL, EGA_BITMASK ; Write to the bit mask register
mov DX, EGA_GRAFPORT ; In the EGA
out DX, AX ; Write index and mask value
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the latches
mov AL, BL ; Set the proper color
stosb ; And write the last one
;
; That should be enough for now. Clean up and get out of here.
;
AASLXDone:
pop BP
ret 4
;
HLINE endp
;
; XOR
;
HLINEX proc far
push bp
mov bp, sp
mov ax, 01803H
mov dx, 03CEH
out dx, ax ; Set XOR writing mode
les DI, [BP + 6]
mov ax, ES:[DI].EndY
mov dx, ES:[DI].StartY
mov cx, ES:[DI].LastX
mov bx, ES:[DI].StartX
cmp ax, dx
jg ABNoSwap
jnz ABNotHorz
jmp ABThinHorzLine
ABNotHorz:
mov ES:[DI].EndY, dx
mov ES:[DI].StartY, ax
xchg ax, dx
mov ES:[DI].LastX, bx
mov ES:[DI].StartX, cx
mov si, cx
mov cx, bx
mov bx, si
ABNoSwap:
sub ax, dx ; deltax = endX - StartX
mov deltay, ax
sub cx, bx ; deltay = endY - StartY
mov deltax, cx
jnz ABDeltaXOK ; Nor do we do vertical ones
jmp ABThinVertLine
ABDeltaXOK:
mov si, 1 ; if (dx > 0)
cmp cx, 0 ; SignDeltaX = 1, incX = DeltaX
jg ABPosDeltaX ; else
mov si, -1 ; SignDeltaX = -1, incX = -DeltaX
neg cx
ABPosDeltaX:
mov SignDeltaX, si
mov incX, cx

mov incY, ax ; incY = dy

mov si, cx ; if (incX > incY)
cmp cx, ax ; increment = IncX
jg ABIncIsIncX ; else
mov si, ax ; increment = IncY
ABIncIsIncX:
mov increment, si

mov ax, ES:[DI].StartY
mov cx, BYTESPERLINE
mul cx
mov dx, ES:[DI].StartX
mov cx, dx
shr dx, 1
shr dx, 1
shr dx, 1
add ax, dx
mov di, ax
mov dx, 3CEh
mov ax, 0005H ; Set Write Mode 0
out dx, ax
mov bx, curColor
mov ah, bl
mov al, 0
out dx, ax ; Set Set/Reset registers
mov ax, 0FF01H
out dx, ax ; Enable Set/Reset for all planes
;
mov ax, SCREEN
mov es, ax
;
and cx, 7
mov ah, 80h
shr ah, cl
mov al, 8
mov dx, 3CEh
mov si, increment
xor cx, cx
mov counter, cx
mov XCoord, cx
mov YCoord, cx

ABNextPixel:
out dx, ax
or es:[di], bl

mov cx, XCoord ; XCoord += incX
add cx, incX
mov XCoord, cx

cmp cx, si ; if (XCoord > inc) {
jle ABDontIncX ; XCoord -= inc;
sub XCoord, si ; XCoord += SignDeltX
test SignDeltaX, 8000h ; }
jnz ABShiftLeft
ror ah, 1
adc di, 0
jmp ABDontIncX
ABShiftLeft:
rol ah, 1
sbb di, 0
ABDontIncX:
mov cx, YCoord ; YCoord += incY
add cx, incY
mov YCoord, cx

cmp cx, si
jle ABDontIncY
sub cx, si
mov YCoord, cx
add di, BytesPerLine
ABDontIncY:
mov cx, counter
cmp cx, si
jg ABQuitDiagLine
inc cx
mov counter, cx
jmp ABNextPixel
ABQuitDiagLine:
pop bp
ret 4
;
; Special case #1 - Thin Vertical Line
;
ABThinVertLine:
mov ax, ES:[DI].StartY
cmp ax, ES:[DI].EndY
jle ABYSortOK
xchg ax, ES:[DI].EndY
mov ES:[DI].StartY, ax
ABYSortOK:
mov ax, ES:[DI].StartY
mov LineHeight, ax
mov bx, BytesPerLine
mul bx
mov cx, ES:[DI].StartX
mov dx, cx
shr cx, 1
shr cx, 1
shr cx, 1
add ax, cx
mov si, ax
mov ax, ES:[DI].EndY
cmp ax, LineHeight
neg LineHeight
add LineHeight, ax
inc LineHeight
and dx, 7
mov cl, dl
mov ah, 80H
shr ah, cl
mov dx, 3CEh
mov al, 8
out dx, ax ; Set Bit Mask register
mov ax, 0005H
out dx, ax ; Set Write Mode 0
;
mov bx, curColor
mov ah, bl
mov al, 0
out dx, ax ; Set Set/Reset register
mov ax, 0FF01H
out dx, ax ; Enable Set/Reset for all planes
mov cx, LineHeight
;
mov ax, SCREEN
mov es, ax
;
ABVLNextPixel: or byte ptr es:[si], bl
add si, BYTESPERLINE
loop ABVLNextPixel

pop bp
ret 4
;
; Special Case #2 - Thin Horizontal Line
;
ABThinHorzLine:
mov dx, 3CEh
mov ax, 0001H ; Clear Set/Reset for all planes
out dx, ax
mov ax, 0205H
out dx, ax ; Set Write Mode 2
;
; Calculate the byte offset into the display buffer of the start of
; the scan line indicated by cury. At 80 bytes per scan line, we
; can multiply the cury value by 80 (do shifts and adds).
;
mov SI, DI
mov AX, ES:[SI].StartY ; Get the parameter value
mov CL, 4 ; Multiply by 16
shl AX, CL
mov BX, AX ; Save the temporary value
shl AX, 1 ; Multiply the temp by 4
shl AX, 1 ; To get original times 64
add BX, AX ; And add temp (64x + 16x = 80x)
mov DI, BX ; Then save the temp
;
; Get the offset into the line of the first byte and get the bit
; mask to be used for that byte. Since there are only 8 possible
; values, the masks are stored in tables.
;
mov DX, ES:[SI].StartX ; Get the X coordinate of the start
mov AX, ES:[SI].LastX ; Calculate the endpoint first
OFFMASK ; Convert to byte offset and bit mask
mov endX, AX ; And save those values
mov AX, DX ; Do the same for the start X
OFFMASK
mov begX, AX ; Save the ending X coordinate
mov BL, AL ; Get the first byte mask index
mov BH, 0
mov AL, firstB[BX] ; And look it up in the table
;
; Check to see if the entire line lies within one byte by comparing
; the start and end offsets.
;
mov CH, 0
mov CL, byte ptr endX + 1 ; Get the offset of the end
sub CL, AH ; Subtract the offset of the start
jnz ABtwoB ; If nonzero, it's at least two bytes
;
; This is the first special case - the line fits entirely in one byte
; of the display buffer. We find the bit pattern for that byte by
; ANDing the first byte and last byte together, then we write it.
;
mov BL, byte ptr endX ; Find the bitmask for the end byte
mov BH, 0
mov AH, lastB[BX] ; From the last byte mask table
and AH, AL ; Calculate the bit mask in AH
mov AL, EGA_BITMASK ; We'll write that to the bit mask
mov DX, EGA_GRAFPORT ; In the graphics control port
out DX, AX
mov BL, byte ptr begX + 1 ; Get the byte offset of the start
mov BH, 0
add DI, BX ; Add the offset of the line's start
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the EGA latches
mov AX, curColor ; Get the current drawing color
stosb ; Then write the pixel data
jmp ABSLXDone ; And we're done.....
;
; The line crosses at least one byte boundary. Calculate how many
; bytes are in the middle, then draw the first byte, fill the middle
; ones with solid data, then draw the last byte.
;
ABtwoB: mov BL, AH ; Save the byte offset of the start
mov AH, AL ; Set up to write the bit mask
mov AL, EGA_BITMASK ; register with the first byte
mov DX, EGA_GRAFPORT
out DX, AX
mov BH, 0 ; Set up the byte offset in the line
add DI, BX ; Add the start of the scan line
mov DX, curColor ; Use the proper current color
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the latches
mov AL, DL ; Get the current color value
stosb ; And write the whole byte
;
; We've got the first byte done - find out how many solid bytes
; are in the interior portion of the line. The CX register still
; holds the difference between the start and end byte offsets.
;
dec CX ; # of bytes is difference - 1
jcxz ABnoMid ; There are no middle bytes
;
; There are solid bytes in the interior, so fill them.
;
mov BL, DL ; Save the current color
mov AX, EGA_ALLBITS ; Bitmask and 0xFF mask value
mov DX, EGA_GRAFPORT
out DX, AX ; Write both port and mask value
mov DL, BL ; Restore the color for the end
;
; Set up for fastest possible filling. If the interior is only one
; byte, this won't quite be the fastest possible, but if we took the
; time to test for one byte and then did it the faster way it would
; override any benefit we got from the special case anyway.
;
;
; Since this is a solid fill in REPLACE mode, we only have to load
; the latches on the EGA once at the start. This practically cuts
; the inner loop time in half, since we only write - no reading.
;
mov AL, BL ; Restore the current color index
rep stosb ; Eight pixels in 13 clocks
;
; Write the last byte on the line, whether or not there was an
; interior region to fill.
;
ABnoMid: mov BL, byte ptr endX ; Find the last byte bitmask
mov BH, 0
mov AH, lastB[BX] ; From the byte mask table
mov BL, DL ; Save the color again
mov AL, EGA_BITMASK ; Write to the bit mask register
mov DX, EGA_GRAFPORT ; In the EGA
out DX, AX ; Write index and mask value
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the latches
mov AL, BL ; Set the proper color
stosb ; And write the last one
;
; That should be enough for now. Clean up and get out of here.
;
ABSLXDone:
pop BP
ret 4
;
HLINEX endp
;
; ADD
;
HLINEA proc far
push bp
mov bp, sp
mov ax, 0003H
mov dx, 03CEH
out dx, ax ; Set REPLACE writing mode
les DI, [BP + 6]
mov ax, ES:[DI].EndY
mov dx, ES:[DI].StartY
mov cx, ES:[DI].LastX
mov bx, ES:[DI].StartX
cmp ax, dx
jg ACNoSwap
jnz ACNotHorz
jmp ACThinHorzLine
ACNotHorz:
mov ES:[DI].EndY, dx
mov ES:[DI].StartY, ax
xchg ax, dx
mov ES:[DI].LastX, bx
mov ES:[DI].StartX, cx
mov si, cx
mov cx, bx
mov bx, si
ACNoSwap:
sub ax, dx ; deltax = endX - StartX
mov deltay, ax
sub cx, bx ; deltay = endY - StartY
mov deltax, cx
jnz ACDeltaXOK ; Nor do we do vertical ones
jmp ACThinVertLine
ACDeltaXOK:
mov si, 1 ; if (dx > 0)
cmp cx, 0 ; SignDeltaX = 1, incX = DeltaX
jg ACPosDeltaX ; else
mov si, -1 ; SignDeltaX = -1, incX = -DeltaX
neg cx
ACPosDeltaX:
mov SignDeltaX, si
mov incX, cx

mov incY, ax ; incY = dy

mov si, cx ; if (incX > incY)
cmp cx, ax ; increment = IncX
jg ACIncIsIncX ; else
mov si, ax ; increment = IncY
ACIncIsIncX:
mov increment, si

mov ax, ES:[DI].StartY
mov cx, BYTESPERLINE
mul cx
mov dx, ES:[DI].StartX
mov cx, dx
shr dx, 1
shr dx, 1
shr dx, 1
add ax, dx
mov di, ax
mov dx, 3CEh
mov ax, 0005H ; Set Write Mode 0
out dx, ax
mov bx, curColor
mov ah, bl
mov al, 0
out dx, ax ; Set Set/Reset registers
mov ax, 0FF01H
out dx, ax ; Enable Set/Reset for all planes
;
mov ax, SCREEN
mov es, ax
;
and cx, 7
mov ah, 80h
shr ah, cl
mov al, 8
mov dx, 3CEh
mov si, increment
xor cx, cx
mov counter, cx
mov XCoord, cx
mov YCoord, cx

ACNextPixel:
out dx, ax
mov bh, es:[di]
add bh, bl
mov es:[di], bh
;
mov cx, XCoord ; XCoord += incX
add cx, incX
mov XCoord, cx

cmp cx, si ; if (XCoord > inc) {
jle ACDontIncX ; XCoord -= inc;
sub XCoord, si ; XCoord += SignDeltX
test SignDeltaX, 8000h ; }
jnz ACShiftLeft
ror ah, 1
adc di, 0
jmp ACDontIncX
ACShiftLeft:
rol ah, 1
sbb di, 0
ACDontIncX:
mov cx, YCoord ; YCoord += incY
add cx, incY
mov YCoord, cx

cmp cx, si
jle ACDontIncY
sub cx, si
mov YCoord, cx
add di, BytesPerLine
ACDontIncY:
mov cx, counter
cmp cx, si
jg ACQuitDiagLine
inc cx
mov counter, cx
jmp ACNextPixel
ACQuitDiagLine:
pop bp
ret 4
;
; Special case #1 - Thin Vertical Line
;
ACThinVertLine:
mov ax, ES:[DI].StartY
cmp ax, ES:[DI].EndY
jle ACYSortOK
xchg ax, ES:[DI].EndY
mov ES:[DI].StartY, ax
ACYSortOK:
mov ax, ES:[DI].StartY
mov LineHeight, ax
mov bx, BytesPerLine
mul bx
mov cx, ES:[DI].StartX
mov dx, cx
shr cx, 1
shr cx, 1
shr cx, 1
add ax, cx
mov si, ax
mov ax, ES:[DI].EndY
cmp ax, LineHeight
neg LineHeight
add LineHeight, ax
inc LineHeight
and dx, 7
mov cl, dl
mov ah, 80H
shr ah, cl
mov dx, 3CEh
mov al, 8
out dx, ax ; Set Bit Mask register
mov ax, 0005H
out dx, ax ; Set Write Mode 0
;
mov bx, curColor
mov ah, bl
mov al, 0
out dx, ax ; Set Set/Reset register
mov ax, 0FF01H
out dx, ax ; Enable Set/Reset for all planes
mov cx, LineHeight
;
mov ax, SCREEN
mov es, ax
;
ACVLNextPixel:
mov bh, es:[si]
add bh, bl
mov es:[si], bh
add si, BYTESPERLINE
loop ACVLNextPixel

pop bp
ret 4
;
; Special Case #2 - Thin Horizontal Line
;
ACThinHorzLine:
mov dx, 3CEh
mov ax, 0001H ; Clear Set/Reset for all planes
out dx, ax
mov ax, 0205H
out dx, ax ; Set Write Mode 2
;
; Calculate the byte offset into the display buffer of the start of
; the scan line indicated by cury. At 80 bytes per scan line, we
; can multiply the cury value by 80 (do shifts and adds).
;
mov SI, DI
mov AX, ES:[SI].StartY ; Get the parameter value
mov CL, 4 ; Multiply by 16
shl AX, CL
mov BX, AX ; Save the temporary value
shl AX, 1 ; Multiply the temp by 4
shl AX, 1 ; To get original times 64
add BX, AX ; And add temp (64x + 16x = 80x)
mov DI, BX ; Then save the temp
;
; Get the offset into the line of the first byte and get the bit
; mask to be used for that byte. Since there are only 8 possible
; values, the masks are stored in tables.
;
mov DX, ES:[SI].StartX ; Get the X coordinate of the start
mov AX, ES:[SI].LastX ; Calculate the endpoint first
OFFMASK ; Convert to byte offset and bit mask
mov endX, AX ; And save those values
mov AX, DX ; Do the same for the start X
OFFMASK
mov begX, AX ; Save the ending X coordinate
mov BL, AL ; Get the first byte mask index
mov BH, 0
mov AL, firstB[BX] ; And look it up in the table
;
; Check to see if the entire line lies within one byte by comparing
; the start and end offsets.
;
mov CH, 0
mov CL, byte ptr endX + 1 ; Get the offset of the end
sub CL, AH ; Subtract the offset of the start
jnz ACtwoB ; If nonzero, it's at least two bytes
;
; This is the first special case - the line fits entirely in one byte
; of the display buffer. We find the bit pattern for that byte by
; ANDing the first byte and last byte together, then we write it.
;
mov BL, byte ptr endX ; Find the bitmask for the end byte
mov BH, 0
mov AH, lastB[BX] ; From the last byte mask table
and AH, AL ; Calculate the bit mask in AH
mov AL, EGA_BITMASK ; We'll write that to the bit mask
mov DX, EGA_GRAFPORT ; In the graphics control port
out DX, AX
mov BL, byte ptr begX + 1 ; Get the byte offset of the start
mov BH, 0
add DI, BX ; Add the offset of the line's start
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the EGA latches
add AX, curColor ; Add the current drawing color
stosb ; Then write the pixel data
jmp ACSLXDone ; And we're done.....
;
; The line crosses at least one byte boundary. Calculate how many
; bytes are in the middle, then draw the first byte, fill the middle
; ones with solid data, then draw the last byte.
;
ACtwoB: mov BL, AH ; Save the byte offset of the start
mov AH, AL ; Set up to write the bit mask
mov AL, EGA_BITMASK ; register with the first byte
mov DX, EGA_GRAFPORT
out DX, AX
mov BH, 0 ; Set up the byte offset in the line
add DI, BX ; Add the start of the scan line
mov DX, curColor ; Use the proper current color
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the latches
add AL, DL ; Get the current color value
stosb ; And write the whole byte
;
; We've got the first byte done - find out how many solid bytes
; are in the interior portion of the line. The CX register still
; holds the difference between the start and end byte offsets.
;
dec CX ; # of bytes is difference - 1
jcxz ACnoMid ; There are no middle bytes
;
; There are solid bytes in the interior, so fill them.
;
mov BL, DL ; Save the current color
mov AX, EGA_ALLBITS ; Bitmask and 0xFF mask value
mov DX, EGA_GRAFPORT
out DX, AX ; Write both port and mask value
mov DL, BL ; Restore the color for the end
;
; Set up for fastest possible filling. If the interior is only one
; byte, this won't quite be the fastest possible, but if we took the
; time to test for one byte and then did it the faster way it would
; override any benefit we got from the special case anyway.
;
;
; Since this is a solid fill in REPLACE mode, we only have to load
; the latches on the EGA once at the start. This practically cuts
; the inner loop time in half, since we only write - no reading.
;
ACFIll: mov AL, BL ; Restore the current color index
add AL, ES:[DI]
stosb
loop ACFill
;
; Write the last byte on the line, whether or not there was an
; interior region to fill.
;
ACnoMid: mov BL, byte ptr endX ; Find the last byte bitmask
mov BH, 0
mov AH, lastB[BX] ; From the byte mask table
mov BL, DL ; Save the color again
mov AL, EGA_BITMASK ; Write to the bit mask register
mov DX, EGA_GRAFPORT ; In the EGA
out DX, AX ; Write index and mask value
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the latches
add AL, BL ; Set the proper color
stosb ; And write the last one
;
; That should be enough for now. Clean up and get out of here.
;
ACSLXDone:
pop BP
ret 4
;
HLINEA endp
;
; MEAN
;
HLINEM proc far
push bp
mov bp, sp
mov ax, 0003H
mov dx, 03CEH
out dx, ax ; Set REPLACE writing mode
les DI, [BP + 6]
mov ax, ES:[DI].EndY
mov dx, ES:[DI].StartY
mov cx, ES:[DI].LastX
mov bx, ES:[DI].StartX
cmp ax, dx
jg ADNoSwap
jnz ADNotHorz
jmp ADThinHorzLine
ADNotHorz:
mov ES:[DI].EndY, dx
mov ES:[DI].StartY, ax
xchg ax, dx
mov ES:[DI].LastX, bx
mov ES:[DI].StartX, cx
mov si, cx
mov cx, bx
mov bx, si
ADNoSwap:
sub ax, dx ; deltax = endX - StartX
mov deltay, ax
sub cx, bx ; deltay = endY - StartY
mov deltax, cx
jnz ADDeltaXOK ; Nor do we do vertical ones
jmp ADThinVertLine
ADDeltaXOK:
mov si, 1 ; if (dx > 0)
cmp cx, 0 ; SignDeltaX = 1, incX = DeltaX
jg ADPosDeltaX ; else
mov si, -1 ; SignDeltaX = -1, incX = -DeltaX
neg cx
ADPosDeltaX:
mov SignDeltaX, si
mov incX, cx

mov incY, ax ; incY = dy

mov si, cx ; if (incX > incY)
cmp cx, ax ; increment = IncX
jg ADIncIsIncX ; else
mov si, ax ; increment = IncY
ADIncIsIncX:
mov increment, si

mov ax, ES:[DI].StartY
mov cx, BYTESPERLINE
mul cx
mov dx, ES:[DI].StartX
mov cx, dx
shr dx, 1
shr dx, 1
shr dx, 1
add ax, dx
mov di, ax
mov dx, 3CEh
mov ax, 0005H ; Set Write Mode 0
out dx, ax
mov bx, curColor
mov ah, bl
mov al, 0
out dx, ax ; Set Set/Reset registers
mov ax, 0FF01H
out dx, ax ; Enable Set/Reset for all planes
;
mov ax, SCREEN
mov es, ax
;
and cx, 7
mov ah, 80h
shr ah, cl
mov al, 8
mov dx, 3CEh
mov si, increment
xor cx, cx
mov counter, cx
mov XCoord, cx
mov YCoord, cx

ADNextPixel:
out dx, ax
mov bh, es:[di]
add bh, bl
shr bh, 1
mov es:[di], bh
;
mov cx, XCoord ; XCoord += incX
add cx, incX
mov XCoord, cx

cmp cx, si ; if (XCoord > inc) {
jle ADDontIncX ; XCoord -= inc;
sub XCoord, si ; XCoord += SignDeltX
test SignDeltaX, 8000h ; }
jnz ADShiftLeft
ror ah, 1
adc di, 0
jmp ADDontIncX
ADShiftLeft:
rol ah, 1
sbb di, 0
ADDontIncX:
mov cx, YCoord ; YCoord += incY
add cx, incY
mov YCoord, cx

cmp cx, si
jle ADDontIncY
sub cx, si
mov YCoord, cx
add di, BytesPerLine
ADDontIncY:
mov cx, counter
cmp cx, si
jg ADQuitDiagLine
inc cx
mov counter, cx
jmp ADNextPixel
ADQuitDiagLine:
pop bp
ret 4
;
; Special case #1 - Thin Vertical Line
;
ADThinVertLine:
mov ax, ES:[DI].StartY
cmp ax, ES:[DI].EndY
jle ADYSortOK
xchg ax, ES:[DI].EndY
mov ES:[DI].StartY, ax
ADYSortOK:
mov ax, ES:[DI].StartY
mov LineHeight, ax
mov bx, BytesPerLine
mul bx
mov cx, ES:[DI].StartX
mov dx, cx
shr cx, 1
shr cx, 1
shr cx, 1
add ax, cx
mov si, ax
mov ax, ES:[DI].EndY
cmp ax, LineHeight
neg LineHeight
add LineHeight, ax
inc LineHeight
and dx, 7
mov cl, dl
mov ah, 80H
shr ah, cl
mov dx, 3CEh
mov al, 8
out dx, ax ; Set Bit Mask register
mov ax, 0005H
out dx, ax ; Set Write Mode 0
;
mov bx, curColor
mov ah, bl
mov al, 0
out dx, ax ; Set Set/Reset register

mov ax, 0FF01H
out dx, ax ; Enable Set/Reset for all planes
mov cx, LineHeight
;
mov ax, SCREEN
mov es, ax
;
ADVLNextPixel:
mov bh, es:[si]
add bh, bl
shr bh, 1
mov es:[si], bh
add si, BYTESPERLINE
loop ADVLNextPixel

pop bp
ret 4
;
; Special Case #2 - Thin Horizontal Line
;
ADThinHorzLine:
mov dx, 3CEh
mov ax, 0001H ; Clear Set/Reset for all planes
out dx, ax
mov ax, 0205H
out dx, ax ; Set Write Mode 2
;
; Calculate the byte offset into the display buffer of the start of
; the scan line indicated by cury. At 80 bytes per scan line, we
; can multiply the cury value by 80 (do shifts and adds).
;
mov SI, DI
mov AX, ES:[SI].StartY ; Get the parameter value
mov CL, 4 ; Multiply by 16
shl AX, CL
mov BX, AX ; Save the temporary value
shl AX, 1 ; Multiply the temp by 4
shl AX, 1 ; To get original times 64
add BX, AX ; And add temp (64x + 16x = 80x)
mov DI, BX ; Then save the temp
;
; Get the offset into the line of the first byte and get the bit
; mask to be used for that byte. Since there are only 8 possible
; values, the masks are stored in tables.
;
mov DX, ES:[SI].StartX ; Get the X coordinate of the start
mov AX, ES:[SI].LastX ; Calculate the endpoint first
OFFMASK ; Convert to byte offset and bit mask
mov endX, AX ; And save those values
mov AX, DX ; Do the same for the start X
OFFMASK
mov begX, AX ; Save the ending X coordinate
mov BL, AL ; Get the first byte mask index
mov BH, 0
mov AL, firstB[BX] ; And look it up in the table
;
; Check to see if the entire line lies within one byte by comparing
; the start and end offsets.
;
mov CH, 0
mov CL, byte ptr endX + 1 ; Get the offset of the end
sub CL, AH ; Subtract the offset of the start
jnz ADtwoB ; If nonzero, it's at least two bytes
;
; This is the first special case - the line fits entirely in one byte
; of the display buffer. We find the bit pattern for that byte by
; ANDing the first byte and last byte together, then we write it.
;
mov BL, byte ptr endX ; Find the bitmask for the end byte
mov BH, 0
mov AH, lastB[BX] ; From the last byte mask table
and AH, AL ; Calculate the bit mask in AH
mov AL, EGA_BITMASK ; We'll write that to the bit mask
mov DX, EGA_GRAFPORT ; In the graphics control port
out DX, AX
mov BL, byte ptr begX + 1 ; Get the byte offset of the start
mov BH, 0
add DI, BX ; Add the offset of the line's start
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the EGA latches
add AX, curColor ; Add the current drawing color
shr AL, 1
stosb ; Then write the pixel data
jmp ADSLXDone ; And we're done.....
;
; The line crosses at least one byte boundary. Calculate how many
; bytes are in the middle, then draw the first byte, fill the middle
; ones with solid data, then draw the last byte.
;
ADtwoB: mov BL, AH ; Save the byte offset of the start
mov AH, AL ; Set up to write the bit mask
mov AL, EGA_BITMASK ; register with the first byte
mov DX, EGA_GRAFPORT
out DX, AX
mov BH, 0 ; Set up the byte offset in the line
add DI, BX ; Add the start of the scan line
mov DX, curColor ; Use the proper current color
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the latches
add AL, DL ; Get the current color value
shr AL, 1
stosb ; And write the whole byte
;
; We've got the first byte done - find out how many solid bytes
; are in the interior portion of the line. The CX register still
; holds the difference between the start and end byte offsets.
;
dec CX ; # of bytes is difference - 1
jcxz ADnoMid ; There are no middle bytes
;
; There are solid bytes in the interior, so fill them.
;
mov BL, DL ; Save the current color
mov AX, EGA_ALLBITS ; Bitmask and 0xFF mask value
mov DX, EGA_GRAFPORT
out DX, AX ; Write both port and mask value
mov DL, BL ; Restore the color for the end
;
; Set up for fastest possible filling. If the interior is only one
; byte, this won't quite be the fastest possible, but if we took the
; time to test for one byte and then did it the faster way it would
; override any benefit we got from the special case anyway.
;
;
; Since this is a solid fill in REPLACE mode, we only have to load
; the latches on the EGA once at the start. This practically cuts
; the inner loop time in half, since we only write - no reading.
;
ADFIll: mov AL, BL ; Restore the current color index
add AL, ES:[DI]
shr AL, 1
stosb
loop ADFill
;
; Write the last byte on the line, whether or not there was an
; interior region to fill.
;
ADnoMid: mov BL, byte ptr endX ; Find the last byte bitmask
mov BH, 0
mov AH, lastB[BX] ; From the byte mask table
mov BL, DL ; Save the color again
mov AL, EGA_BITMASK ; Write to the bit mask register
mov DX, EGA_GRAFPORT ; In the EGA
out DX, AX ; Write index and mask value
mov AX, SCREEN
mov ES, AX ; Point to the display buffer
mov AL, ES:[DI] ; Preload the latches
add AL, BL ; Set the proper color
shr AL, 1
stosb ; And write the last one
;
; That should be enough for now. Clean up and get out of here.
;
ADSLXDone:
pop BP
ret 4
;
HLINEM endp
;
HQMODE proc far
push BP
mov BP, SP
les BX, [BP + 6]
mov word ptr ES:[BX][10], XRES
mov word ptr ES:[BX][12], YRES
pop BP
ret 4
HQMODE endp
;
HRECT proc far
push bp
mov bp, sp
sub sp, 8
les DI, [BP + 6]
mov cx, ES:[DI].HHeight
mov ax, ES:[DI].HWidth
mov bx, ES:[DI].HStartX
mov dx, ES:[DI].HStartY
mov [BP - 2], ax
mov [BP - 4], cx
mov [BP - 6], bx
mov [BP - 8], dx
add ax, ES:[DI].HStartX
dec ax
mov ES:[DI].HWidth, ax
mov ax, ES:[DI].HStartY
mov ES:[DI].HHeight, ax
;
RectLoop: push cx
push es
push di
call dword ptr funcTable
pop cx
les DI, [BP + 6]
inc ES:[DI].HStartY
inc ES:[DI].HHeight
loop RectLoop
;
les DI, [BP + 6]
mov ax, [BP - 2]
mov cx, [BP - 4]
mov bx, [BP - 6]
mov dx, [BP - 8]
mov ES:[DI].HWidth, ax
mov ES:[DI].HHeight, cx
mov ES:[DI].HStartX, bx
mov ES:[DI].HStartY, dx
;
add sp, 8
pop bp
ret 4
HRECT endp
;
HSMX proc far
push BP
mov BP, SP
les BX, [BP + 6]
mov BL, ES:[BX + 2] ; Get new color mix
cmp BL, 4 ; Is it XOR?
jnz NotXOR
lea AX, HLINEX ; Get pointer to XOR function
jmp short HSMXD
NotXOR:
cmp BL, 8 ; Is it ADD?
jnz NotAdd
lea AX, HLINEA
jmp short HSMXD
NotAdd:
cmp BL, 0BH ; Is it MEAN?
jnz NotMean
lea AX, HLINEM
jmp short HSMXD
NotMean:
lea AX, HLINE ; Everything else maps to REPLACE
HSMXD:
lea BX, funcTable ; HLINE is at the start of the table
mov CS:[BX], AX ; Stuff the new function pointer
pop BP
ret 4
HSMX endp
;
HOPEN proc far
push BP
mov BP, SP
les BX, [BP + 6]
mov byte ptr ES:[BX][4], 0
pop BP
ret 4
HOPEN endp
;
HINIT proc far
push BP
mov BP, SP
mov AH, 0FH
int 10H
mov savedMode, AL
mov AH, 0
mov AL, GRAPHICS_MODE
int 10H
pop BP
ret 4
HINIT endp
;
HEGS proc far
push BP
mov BP, SP
mov AH, 0FH
int 10H
mov AH, 0
int 10H
pop BP
ret 4
HEGS endp
;
HCLOSE proc far
push BP
mov BP, SP
mov AH, 0
mov AL, savedMode
int 10H
pop BP
ret 4
HCLOSE endp
;
HQDPS proc far
push BP
mov BP, SP
les BX, [BP + 6]
mov word ptr ES:[BX][2], 1
pop BP
ret 4
HQDPS endp
;
HSCOL proc far
push BP
mov BP, SP
les BX, [BP + 6]
mov AL, ES:[BX][2]
mov CL, 4
shr AL, CL
mov curColor, AX
pop BP
ret 4
HSCOL endp
;
Unimp proc far
ret 4 ; All unimplemented functions
Unimp endp
;
; Initialization Code
;
Start: mov AH, DOS_SETVEC ; Set the 8514/A interrupt vector
mov AL, HDI_VECTOR
lea DX, Handler ; Point to interrupt handler code
int 21H
;
; Loop through the vector table and store our loaded segment
; value in the segment portion of each pointer.
;
lea BX, funcTable
add BX, 2 ; Skip to the segment portion
mov AX, DS ; Get our segment address
mov CX, funcCount ; Number of entries in the table
LoadSeg: mov [BX], AX ; Store the segment pointer
add BX, 4 ; Move to the next entry
loop LoadSeg
;
; Initialization is complete, display a message for the user
; and terminate, leaving all but the initialization code
; resident.
;
mov AH, DOS_CONOUT ; Print string to the console
lea DX, message
int 21H
;
mov AH, DOS_TERMRES ; Terminate but stay resident
lea DX, Start ; Get the address of init code
mov CL, 4 ; Convert to paragraph count
shr DX, CL
inc DX ; Allow for rounding downward
mov AL, 0 ; Return errorlevel 0
int 21H
;
_VGA ends
end Begin


  3 Responses to “Category : Files from Magazines
Archive   : PCTJ8805.ZIP
Filename : VGALOAD.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/