home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
pcmag
/
asm4.arc
/
WAITASEC.ASM
< prev
Wrap
Assembly Source File
|
1986-02-05
|
15KB
|
450 lines
; WAITASEC.ASM -- Holds and Recalls Scrolling Screens
; ============
; (C) Copyright Charles Petzold, 1985
;
; COM file format
;
CSEG Segment
Assume CS:CSEG
Org 0080h
SWAP Label Byte ; Use this area for line swapping
Org 0100h
Entry: Jmp Initialize ; Entry: Jump over resident part
; All Data
; --------
Cols equ 80 ; You may want to set this to 40
ScrollLock equ 70 ; Scan code of Scroll Lock key
NumberOfScreens dw 8 ; Can be set up to 15 for 80-column
db '(C) Copyright Charles Petzold, 1985'
DisplaySegment dw ? ; Display address set by VideoCheck
OldInterrupt9 dd ? ; Saved address of real Interrupt 9
OldInterrupt10 dd ? ; Ditto for Interrupt 10h
TopHoldAddr dw Offset HoldArea ; Address of top of Hold area
BotHoldAddr dw ? ; Address of bottom of Hold area
SavePointer dw Offset HoldArea ; Points to where text is saved
RecallPointer dw Offset HoldArea ; Points to where text is recalled
LockStateOn db 0 ; Flag for lock or no lock
ScrollLockDown db 0 ; Current state of Scroll Lock key
KeyDispatch dw Home,Up,PgUp ; Dispatch addresss of routines
dw 5 Dup (Dummy) ; for cursor movement within
dw End,Down,PgDn ; recalled displays
; Intercept of Interrupt 10h -- BIOS Video Call
; --------------------------
NewInterrupt10: Cmp AX,0601h ; Check if this is a 1 line scroll up
Jne NoNeedToBother ; No, just continue to real interrupt
Cmp CX,0 ; Check if this is upper left corner
Jne NoNeedToBother ; No, don't want to touch it
Cmp DL,Cols - 1 ; Check if DL is right hand side
Jne NoNeedToBother ; Another obstacle that shuts us out
Call VideoCheck ; Returns Carry if graphics
Jc NoNeedToBother ; So we drop out here if Carry is set
Sti ; Allow interrupts
Cld ; And set direction to forward
Push CX ; Save all the registers we'll use
Push SI
Push DI
Push DS
Push ES
Push CS ; Push this code segment
Pop ES ; So we can set ES to it
Mov DI,CS:[SavePointer] ; This is the destination
Mov DS,CS:[DisplaySegment] ; This is the source segment
Sub SI,SI ; Source is top of display
Mov CX,Cols ; The number of characters
Call VideoOff ; Turn off light show if color
Rep Movsw ; Move 'em in!
Call VideoOn ; Turn display back on
Cmp DI,CS:[BotHoldAddr] ; Check the new destination
Jb NotAtBotYet ; Jump if it hasn't wrapped yet
Mov DI,CS:[TopHoldAddr] ; Set it to top if it has
NotAtBotYet: Mov CS:[SavePointer],DI ; And save it anyway
Pop ES ; Pop the registers we saved
Pop DS
Pop DI
Pop SI
Pop CX
NoNeedToBother: Jmp CS:[OldInterrupt10] ; And do the interrupt for real
; Intercept of Interrupt 9h -- Hardware Keyboard Interrupt
; --------------------------------------------------------
NewInterrupt9: Sti ; Allow other interrupts
Push AX ; Save the register for awhile
In AL,60h ; Get the pressed key scan code
Cmp AL,ScrollLock ; Check if it's the Scroll Lock
Jz GottaScrollLock ; If so, do special routine
Cmp AL,128 + ScrollLock ; Check if scroll lock release
Jz ScrollLockRlse ; Another special routine
Jmp AnyOtherKey ; Otherwise, it's some other key
; Scroll Lock key depressed
; -------------------------
GottaScrollLock:Mov CS:[ScrollLockDown],1 ; Flag Scroll Lock as depressed
Cmp CS:[LockStateOn],1 ; See if we're locked now
Jz AnyOtherKey ; If so, will just reset
Mov AH,2 ; Do keyboard interrupt
Int 16h ; to get shift states
Test AL,04 ; See if Ctrl has been pressed
Jnz NormalProcess ; Process normally if Break
Call VideoCheck ; See if video is OK for recall
Jc NormalProcess ; If not, ignore the key
Mov CS:[LockStateOn],1 ; Lock state now ON
Push CS:[SavePointer] ; Transfer value of SavePointer
Pop CS:[RecallPointer] ; ... to RecallPointer
PushF ; Simulate an interrupt
Call CS:[OldInterrupt9] ; Let Scroll Lock Register
Call VideoOn ; Video might be off - turn on
HoldingPattern: Cmp CS:[LockStateOn],1 ; Wait in this loop ...
Jz HoldingPattern ; ... until out of lock state
Jmp NormalProcess ; Then process last key
; Scroll Lock key released
; ------------------------
ScrollLockRlse: Mov CS:[ScrollLockDown],0 ; Register Scroll Lock release
Cmp CS:[LockStateOn],1 ; See if in lock state
Jnz NormalProcess ; If not, just process key
Mov AX,CS:[SavePointer] ; Check if the screen is in
Cmp AX,CS:[RecallPointer] ; normal position
Jnz NormalProcess ; If not, just process key
Mov CS:[LockStateOn],0 ; If so, turn lock state OFF
Pop AX ; Restore register
IRet ; Return to holding pattern
; All other keys
; --------------
AnyOtherKey: Cmp CS:[LockStateOn],1 ; For other keys, just do
Jnz NormalProcess ; normal processing if not lock
Call NowInLockState ; But call routine if locked
Pop AX ; Restore register
IRet ; Return to holding pattern
NormalProcess: Pop AX ; Get back the register
Jmp CS:[OldInterrupt9] ; Let the key process
; Routine for other keys during lock state (AL = Scan Code)
; ---------------------------------------------------------
NowInLockState: Cld ; String moves generally forward
Push CX ; We use all these registers
Push DX
Push SI
Push DI
Push DS
Push ES
Mov DX,CS ; Get value of CS
Mov DS,DX ; Set DS and
Mov ES,DX ; ES to this segment
Assume DS:CSEG, ES:CSEG ; And tell the assembler
Mov AH,AL ; AH is actual scan code
And AL,7Fh ; AL has release byte off
Sub AL,71 ; Subtract the 'Home' key value
Jb WrongKey ; No good if below
Cmp AL,(81 - 71) ; Above the 'PgDn' key?
Ja WrongKey ; You're not right either
Cmp AL,(73 - 71) ; Check if under or = 'PgUp'
Jbe AllRightKey ; Ok for this key
Cmp AL,(79 - 71) ; Check if over or = 'End'
Jae AllRightKey ; You pass too
WrongKey: Cmp [ScrollLockDown],1 ; See if scroll lock is down
Jz ResetKeyboard ; If so, just ignore the key
Call End ; If not, restore the display
Mov [LockStateOn],0 ; Turn off the lock state
Jmp KeyReturn ; And get out quickly
AllRightKey: Test AH,80h ; See if cursor key is release
Jnz ResetKeyboard ; If so, ignore the key
Cbw ; Convert scan code to word
Add AX,AX ; Double it because word access
Mov SI,AX ; Set SI to it
Call [KeyDispatch + SI] ; And do the routine
ResetKeyboard: In AL,61h ; These instructions
Mov AH,AL ; reset the keyboard
Or AL,80h
Out 61h,AL
Mov AL,AH
Out 61h,AL
Cli ; Disable interrupts
Mov AL,20h ; to reset the interrupt
Out 20h,AL ; controller
KeyReturn: Pop ES ; Get back all the pushes
Pop DS
Pop DI
Pop SI
Pop DX
Pop CX
Ret ; Return to previous routine
; Routines to scan up through saved display
; -----------------------------------------
Home: Mov CX,0FFFFh ; 'Home' key -- "infinite" lines up
Jmp Short GoingUp ; Do it
Up: Mov CX,1 ; 'Up' key -- 1 line up
Jmp Short GoingUp ; Go to it
PgUp: Mov CX,25 ; 'PgUp' key -- 25 lines (1 screen) up
GoingUp: Mov AX,[RecallPointer] ; Points to the text source
Cmp AX,[TopHoldAddr] ; Check if it's at the top
Ja NotScanUpTop ; If not there, no problem
Mov AX,[BotHoldAddr] ; Otherwise must wrap around
NotScanUpTop: Sub AX,Cols * 2 ; Go back one line for recall
Cmp AX,[SavePointer] ; Check if cycled through yet
Je EndGoingUp ; If so, abort this routine
Mov [RecallPointer],AX ; Save the new pointer value
Push CX ; And save our counter
Mov SI,2 * 24 * Cols ; Set source to bottom line
Call DisplayToSwap ; Transfer it to SWAP area
Mov SI,2 * 24 * Cols - 2 ; End of penultimate line
Mov DI,2 * 25 * Cols - 2 ; End of last line
Std ; Backwards string transfer
Call ScrollDisplay ; Do the screen scroll
Cld ; Direction back to forward
Mov DI,0 ; Destination is top line
Call HoldToDisplay ; Move line to display
Call SwapToHold ; And SWAP line to hold
Pop CX ; Get back the counter
Loop GoingUp ; And do CX times
Dummy: ; Should never get here
EndGoingUp: Ret ; But this is the end
; Routines to scan down through saved display
; -------------------------------------------
End: Mov CX,0FFFFh ; 'End' key -- infinite lines down
Jmp Short GoingDown ; Do it
Down: Mov CX,1 ; 'Down' key -- 1 line down
Jmp Short GoingDown ; All ready
PgDn: Mov CX,25 ; 'PgDn' key -- 25 lines down
GoingDown: Mov AX,[RecallPointer] ; Check if all through
Cmp AX,[SavePointer] ; by this comparison
Je EndGoingDown ; If so, exit this thing
Push CX ; Save the counter
Mov SI,0 ; Set source to top line
Call DisplayToSwap ; Transfer it to SWAP area
Mov SI,2 * 80 ; Source is second line
Mov DI,0 ; Destination is top line
Call ScrollDisplay ; So we can scroll up display
Mov DI,2 * 24 * Cols ; Destination is bottom line
Call HoldToDisplay ; For the saved text
Call SwapToHold ; Move original top line in
Mov AX,[RecallPointer] ; Adjust the recall pointer
Add AX,Cols * 2 ; By increasing by one line
Cmp AX,[BotHoldAddr] ; See if wrap around
Jb NotScanDownBot ; If not, skip a little
Mov AX,[TopHoldAddr] ; Get the top address
NotScanDownBot: Mov [RecallPointer],AX ; And set to new recall
Pop CX ; Get back the counter
Loop GoingDown ; Do this CX times
EndGoingDown: Ret ; Go back to caller
; Display to Swap -- Save top or bottom line of display (SI) in SWAP area
; -----------------------------------------------------------------------
DisplayToSwap: Push DS ; Save data segment
Mov DS,[DisplaySegment] ; And set it to display
Mov DI,Offset Swap ; This is the destination
Mov CX,Cols ; Do one line
Call VideoOff ; Turn off color video
Rep Movsw ; Do the line move
Pop DS ; Get back DS
Ret ; And end
; Scroll Video Display -- SI is source, DI destination
; ----------------------------------------------------
ScrollDisplay: Push DS ; Save both these segments
Push ES
Mov ES,[DisplaySegment] ; Set both to display
Mov DS,[DisplaySegment]
Mov CX,24 * Cols ; 24 lines must be moved
Rep Movsw ; This line does it
Pop ES ; Get back the segments
Pop DS
Ret ; And go back
; Hold To Display -- Move one saved line to video display (DI)
; ------------------------------------------------------------
HoldToDisplay: Push ES ; Now save just ES
Mov ES,[DisplaySegment] ; And set it to display
Mov SI,[RecallPointer] ; This is the source
Mov CX,Cols ; One line only
Rep Movsw ; Do the transfer
Call VideoOn ; Color display back on
Pop ES ; Get back ES
Ret ; And return
; Swap To Hold -- Completes the cycle
; -----------------------------------
SwapToHold: Mov SI,Offset Swap ; Source is SWAP
Mov DI,[RecallPointer] ; Destination in HoldArea
Mov CX,Cols ; One line transfer
Rep Movsw ; Do it
Ret ; And return
; Video Check -- Returns CY flag is graphics or 40 column or not page 0
; ---------------------------------------------------------------------
Assume DS:Nothing, ES:Nothing
VideoCheck: Push AX
Push BX
Mov CS:[DisplaySegment],0B000h ; Set up for monochrome
Mov AH,15 ; Get current video mode
Int 10h ; through display interrupt
Cmp AL,7 ; See if monochrome
Jz VideoAOK ; If so, skip out OK
Cmp AL,3 ; Check if it's graphics
Ja VideoNoGood ; If so, can't do anything
Cmp BH,0 ; Check if it's Page 0
Jnz VideoNoGood ; Nope? No bad
Cmp AH,Cols ; See if columns is right
Jne VideoNoGood ; No -- dishonorable discharge
Mov CS:[DisplaySegment],0B800h ; Change it to color
VideoAOK: Clc ; Flag for no error
Jmp Short VideoReturn ; Get out
VideoNoGood: Stc ; Flag for error
VideoReturn: Pop BX ; Get back register
Pop AX
Ret ; Now go back
; Video Off -- Turns off display for Color / Graphics
; ---------------------------------------------------
VideoOff: Cmp CS:[DisplaySegment],0B800h ; Check if color
Jnz NoColorTurnOff ; If not, punt
Push AX ; Just use two registers
Push DX
Mov DX,3DAh ; Color / Graphics Status Port
RetraceWait1: In AL,DX ; Get Color / Graphics Status
Test AL,08 ; Check the vertical retrace
Jnz RetraceWait1 ; Loop till it's over
RetraceWait2: In AL,DX ; Check status again
Test AL,08 ; In particular the retrace
Jz RetraceWait2 ; Loop till it comes
Mov DX,3D8h ; Video Mode Select
Mov AL,25h ; Byte to turn off video
Out DX,AL ; Turn it off
Pop DX ; Retrieve registers from stack
Pop AX
NoColorTurnOff: Ret
; Video On -- Turns Color Display back on
; ---------------------------------------
VideoOn: Cmp CS:[DisplaySegment],0B800h ; See if color
Jnz NoColorTurnOn ; If not do nothing
Push AX ; Save registers
Push DX
Mov DX,3D8h ; Video mode register port
Mov AL,28h - (Cols EQ 80) ; i.e., 29h for 80 columns
Out DX,AL ; Turn video back on
Pop DX ; Get back registers
Pop AX
NoColorTurnOn: Ret ; And leave
; Initialization Procedure
; ------------------------
Assume DS:CSEG, ES:CSEG
HoldArea Label Word ; Storage of screens also
Initialize: Mov AX,Cols * 25 * 2 ; Characters per screen
Mul [NumberOfScreens] ; AX = bytes for HoldArea
Add AX,Offset HoldArea ; Calculate bottom of HoldArea
Mov [BotHoldAddr],AX ; Save the bottom address
Mov DX,AX ; Last address for terminate
Sub AX,AX ; Zero out AX
Mov DS,AX ; To set DS to Vector Segment
Cli ; No Interrupts now
Les SI,DS:[9h * 4] ; Get Int 9 address
Mov Word Ptr CS:[OldInterrupt9],SI ; Save offset
Mov Word Ptr CS:[OldInterrupt9 + 2],ES ; Save segment
Mov Word Ptr DS:[9h * 4],Offset NewInterrupt9 ; New offset
Mov Word Ptr DS:[9h * 4 + 2],CS ; New segment
Les SI,DS:[10h * 4] ; Get Int 10 address
Mov Word Ptr CS:[OldInterrupt10],SI ; Save offset
Mov Word Ptr CS:[OldInterrupt10 + 2],ES ; Save segment
Mov Word Ptr DS:[10h * 4],Offset NewInterrupt10 ; New offset
Mov Word Ptr DS:[10h * 4 + 2],CS ; New segment
Sti ; Interrupts back on
Int 27h ; Terminate and stay resident
CSEG EndS
End Entry ; End denotes Entry point