📄 pcgen.s
字号:
include asmglobal.h
LOCALS
extrn ctick:proc
extrn kbpoll:proc
extrn kbsave:proc
extrn ksignal:proc
public eoi
; Hardware vector for timer linkage
; We use the timer hardware channel here instead of the indirect BIOS
; channel (1ch) because the latter is sluggish when running under DoubleDos
TIMEVEC EQU 08h
.DATA
public Intstk,Stktop,Spsave,Sssave,Mtasker,Hashtab,Kbvec
extrn Isat:word
Spsave dw ? ; Save location for SP during interrupts
Sssave dw ? ; Save location for SS during interrupts
Intstk dw 1024 dup(?) ; Interrupt working stack
Stktop equ $ ; SP set here when entering interrupt
Mtasker db ? ; Type of higher multitasker, if any
Hashtab db 256 dup(?) ; Modulus lookup table for iphash()
Kbvec dd ? ; Address of BIOS keyboard handler
.CODE
dbase dw @Data
jtable dw l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11,l12,l13,l14,l15
vector dd ? ; place to stash chained vector
vectlo equ word ptr vector
vecthi equ word ptr vector+2
; Re-arm 8259 interrupt controller(s)
; Should be called just after taking an interrupt, instead of just
; before returning. This is because the 8259 inputs are edge triggered, and
; new interrupts arriving during an interrupt service routine might be missed.
eoi proc
cmp Isat,1
jnz @@1 ; Only one 8259, so skip this stuff
mov al,0bh ; read in-service register from
out 0a0h,al ; secondary 8259
nop ; settling delay
nop
nop
in al,0a0h ; get it
or al,al ; Any bits set?
jz @@1 ; nope, not a secondary interrupt
mov al,20h ; Get EOI instruction
out 0a0h,al ; Secondary 8259 (PC/AT only)
@@1: mov al,20h ; 8259 end-of-interrupt command
out 20h,al ; Primary 8259
ret
eoi endp
; common routine for interrupt return
; Note that all hardware interrupt handlers are expected to return
; the original vector found when the device first attached. We branch
; to it just after we've cleaned up here -- this implements shared
; interrupts through vector chaining. If the original vector isn't
; available, the interrupt handler must return NULL to avoid a crash!
public doret
label doret far
cmp ax,0 ; is a chained vector present?
jne @@1 ; yes
if @Datasize NE 0
cmp dx,ax
jne @@1 ; yes
endif
pop es ; nope, return directly from interrupt
POPALL
mov ss,Sssave
mov sp,Spsave ; restore original stack context
pop ds
iret
; Code to handle vector chaining
@@1: mov cs:vectlo,ax ; stash vector for later branch
if @Datasize NE 0
mov cs:vecthi,dx
endif
pop es
POPALL
mov ss,Sssave
mov sp,Spsave ; restore original stack context
pop ds
if @Datasize NE 0
jmp cs:[vector] ; jump to the original interrupt handler
else
jmp cs:[vectlo]
endif
; istate - return current interrupt state
public istate
istate proc
pushf
pop ax
and ax,200h
jnz @@1
ret
@@1: mov ax,1
ret
istate endp
; dirps - disable interrupts and return previous state: 0 = disabled,
; 1 = enabled
public dirps
dirps proc
pushf ; save flags on stack
pop ax ; flags -> ax
and ax,200h ; 1<<9 is IF bit
jz @@1 ; ints are already off; return 0
mov ax,1
cli ; interrupts now off
@@1: ret
dirps endp
; restore - restore interrupt state: 0 = off, nonzero = on
public restore
restore proc
arg is:word
test is,0ffffh
jz @@1
sti
ret
@@1: cli ; should already be off, but just in case...
ret
restore endp
; multitasker types
NONE equ 0
DOUBLEDOS equ 1
DESQVIEW equ 2
WINDOWS3 equ 3
OS2 equ 4
; Relinquish processor so other task can run
public giveup
giveup proc
pushf ;save caller's interrupt state
sti ;re-enable interrupts
cmp mtasker, DOUBLEDOS
jnz @@1
mov al,2 ; 110 ms
mov ah,0eeh
int 21h
POPFLAGS ; restore caller's interrupt state
ret
@@1: cmp mtasker, DESQVIEW
jnz @@2
mov ax, 1000h
int 15h
POPFLAGS ; restore interrupts
ret
@@2: cmp mtasker, WINDOWS3
jnz @@3
mov ax, 1680h
int 2fh
cmp al, 80h ; call supported?
jz @@3 ; nope
POPFLAGS ; yes - restore interrupts
ret
@@3: cmp mtasker, OS2
jnz @@4
mov ax, 1680h
int 2fh
POPFLAGS ; restore interrupts
ret
@@4: hlt ; wait for an interrupt
POPFLAGS ; restore interrupts
ret
giveup endp
; check for a multitasker running
public chktasker
chktasker proc
mov mtasker,NONE
; Check for OS/2
mov ax,3000h ; Get MS-DOS Version Number call
int 21h
cmp al,20 ; Version 20 = OS/2 2.0
jnz @@5
mov mtasker, OS2
ret
; Check for Microsoft Windows
@@5: mov ax,1600h
; Check for Microsoft Windows
mov ax,1600h
int 2fh
cmp al, 00h ; 0 means windows multitasking not running
jz @@4
cmp al, 80h ; ditto for 80h return
jz @@4
mov mtasker, WINDOWS3
ret
; Check for DoubleDos
@@4: mov ah,0e4h
int 21h
cmp al,1
jz @@1
cmp al,2
jnz @@2
@@1: mov mtasker, DOUBLEDOS
ret
; Check for DESQVIEW
@@2: mov ax, 2b01h
mov cx, 4445h
mov dx, 5351h
int 21h
cmp al, 0ffh
jnz @@3
ret
@@3: mov mtasker, DESQVIEW
ret
chktasker endp
; getss - Read SS for debugging purposes
public getss
getss proc
mov ax,ss
ret
getss endp
; clockbits - Read low order bits of timer 0 (the TOD clock)
; This works only for the 8254 chips used in ATs and 386s.
;
; The timer runs in mode 3 (square wave mode), counting down
; by twos, twice for each cycle. So it is necessary to read back the
; OUTPUT pin to see which half of the cycle we're in. I.e., the OUTPUT
; pin forms the most significant bit of the count. Unfortunately,
; the 8253 in the PC/XT lacks a command to read the OUTPUT pin...
;
; The PC's clock design is soooo brain damaged...
public clockbits
clockbits proc
mov al,0c2h ; latch timer 0 count and status for reading
@@1: pushf
cli ; make chip references atomic
out 43h,al ; send latch command
in al,40h ; get status of timer 0
mov bl,al ; save status
in al,40h ; get lsb of count
mov ah,al ; save lsb
in al,40h ; get msb of count
POPFLAGS ; no more chip references
test bl,40h ; test NULL COUNT bit
jnz @@1 ; count is invalid, try again
and bl,80h ; we're only interested in the OUT bit
xchg ah,al ; ax = count in correct order
shr ax,1 ; count /= 2
jz @@3 ; zero count requires carry propagation
@@2: or ah,bl ; combine with OUT bit as most sig bit of count
ret
@@3: xor bl,80h ; propagate carry by toggling OUT bit when cnt == 0
or ah,bl ; combine with !OUT bit as most sig bit of count
ret
clockbits endp
; Internet checksum subroutine
; Compute 1's-complement sum of data buffer
; Uses an unwound loop inspired by "Duff's Device" for performance
;
; Called from C as
; unsigned short
; lcsum(buf,cnt)
; unsigned short *buf;
; unsigned short cnt;
public lcsum
lcsum proc
arg buf:ptr,cnt:word
if @Datasize NE 0
uses ds,si
lds si,buf ; ds:si = buf
else
uses si
mov si,buf ; ds:si = buf (ds already set)
endif
mov cx,cnt ; cx = cnt
cld ; autoincrement si
mov ax,cx
shr cx,1 ; cx /= 16, number of loop iterations
shr cx,1
shr cx,1
shr cx,1
inc cx ; make fencepost adjustment for 1st pass
and ax,15 ; ax = number of words modulo 16
shl ax,1 ; *=2 for word table index
lea bx,jtable ; bx -> branch table
add bx,ax ; index into jump table
clc ; initialize carry = 0
mov dx,0 ; clear accumulated sum
jmp word ptr cs:[bx] ; jump into loop
; Here the real work gets done. The numeric labels on the lodsw instructions
; are the targets for the indirect jump we just made.
;
; Each label corresponds to a possible remainder of (count / 16), while
; the number of times around the loop is determined by the quotient.
;
; The loop iteration count in cx has been incremented by one to adjust for
; the first pass.
;
deloop: lodsw
adc dx,ax
l15: lodsw
adc dx,ax
l14: lodsw
adc dx,ax
l13: lodsw
adc dx,ax
l12: lodsw
adc dx,ax
l11: lodsw
adc dx,ax
l10: lodsw
adc dx,ax
l9: lodsw
adc dx,ax
l8: lodsw
adc dx,ax
l7: lodsw
adc dx,ax
l6: lodsw
adc dx,ax
l5: lodsw
adc dx,ax
l4: lodsw
adc dx,ax
l3: lodsw
adc dx,ax
l2: lodsw
adc dx,ax
l1: lodsw
adc dx,ax
l0: loop deloop ; :-)
adc dx,0 ; get last carries
adc dx,0
mov ax,dx ; result into ax
xchg al,ah ; byte swap result (8088 is little-endian)
ret
lcsum endp
; Link timer handler into timer chain
; Arg == address of timer handler routine
; MUST be called exactly once before uchtimer is called!
toff dw ? ; save location for old vector
tseg dw ? ; must be in code segment
public chtimer
chtimer proc
arg vec:far ptr
uses ds
mov ah,35h ; get current vector
mov al,TIMEVEC
int 21h ; puts vector in es:bx
mov cs:tseg,es ; stash
mov cs:toff,bx
mov ah,25h
mov al,TIMEVEC
lds dx,vec ; ds:si = vec
int 21h ; set new vector
ret
chtimer endp
; unchain timer handler from timer chain
; MUST NOT be called before chtimer!
public uchtimer
uchtimer proc
uses ds
mov ah,25h
mov al,TIMEVEC
mov dx,toff
mov ds,tseg
int 21h ; restore old vector
ret
uchtimer endp
; Keyboard hardware interrupt handler.
; First pass the interrupt to the original handler in the BIOS,
; then do a ksignal() to wake up any task sleeping on it.
public kbint
label kbint far
cld
push ds
mov ds,cs:dbase ; establish interrupt data segment
pushf ; Make it look like a hardware interrupt
call dword ptr [Kbvec] ; Call BIOS handler
mov Sssave,ss ; stash user stack context
mov Spsave,sp
mov ss,cs:dbase ; switch to interrupt stack
lea sp,Stktop
PUSHALL
push es
; ksignal(&Kbvec,1);
mov ax,1
push ax
push ds
mov ax,offset DGROUP:Kbvec
push ax
call ksignal
add sp,6 ; remove args
pop es
POPALL
mov ss,Sssave
mov sp,Spsave ; restore original stack context
pop ds
iret
; Poll keyboard through BIOS. Returns ascii char in low byte, scan code
; in high byte. If low byte == 0, character is "extended ascii"
public kbraw
kbraw proc
mov ah,1 ; poll BIOS for character
int 16h
jz nochar ; no character available
mov ah,0
int 16h ; get it for real: ah = scan code, al = ascii char (or 0)
ret
nochar: xor ax,ax
ret
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -