Skip to content

Instantly share code, notes, and snippets.

@lpproj
Last active June 11, 2020 12:50
Show Gist options
  • Save lpproj/634ed84262c8defa1f07 to your computer and use it in GitHub Desktop.
Save lpproj/634ed84262c8defa1f07 to your computer and use it in GitHub Desktop.
%if 0
----
ぷちどる (petit idle)
a minimal CPU cooler for DOS (mostly for NEC PC-98 series)
To build:
nasm -f bin -o petidle.com petidle.nas
License: Public Domain
(You can use/modify/re-distribute it freely BUT NO WARRANTY)
Description (in Japanese):
・PC-98 系 DOS 向けに試作してみた CPU 使用率軽減ツール
・いちおう 98 以外の DOS でも使えるはず(だが、IBM 系なら
他のツール使ったほうがいい)
・と言いつつ実機ではほぼ試していない
(hlt でほんとに CPU 止まる機種なら一応効くとは思う…)
Usage (in Japanese):
petidle [/e] [/d] [/r]
/e petidle 機能の有効化(常駐時デフォルト)
DOS のアイドル時間(キー入力待ちなどの int 28h 発行時)に
HLT 命令を実行し、CPU 使用率を下げます。
/d petidle 機能の一時的な無効化
もう一度有効にしたい時は、petidle /e を実行してください。
/r petidle の常駐解除
int 28h をフックするプログラムを petidle の後で常駐させた
場合、常駐解除はできません。
Note (in Japanese):
「キー入力をチェックしつつ処理を行う」タイプのアプリケーションの場合、
処理が通常に比べて非常に遅くなることがあります。
(典型的な例として、SCANDISK や PC-98シリーズ用の FORMAT コマンドなど)
そのようなアプリケーションの実行前には /d オプションで一時的に無効化して
おき、終了後に /e オプションで再び有効にしてください。
一時的な対処法として「処理に関係なさそうなキーを押しっぱなしにしておく」
という方法も使えます。
ユーザープログラムによる HLT 命令の実行を許可していないシステムプログラム
との共存はできません(IBM PC DOS付属のEMM386など)。
History:
2015-10-03 lpproj
initial build
2015-10-12 lpproj
support enable (/e), disable (/d) and release (/r) option
hook vsync handler (int 0Ah) on PC-98
2016-04-12 lpproj
add help msg
fix not able to release
remove vsync handler
increase idle_cnt on old DOS (2.x and 3.x)
2020-06-11 lpproj
fix wrong PIC configuration (PC-98)
shirink IOwait code
----
%endif
VERSION_MAJOR equ 1
VERSION_MINOR equ 0
VERSION_REV equ 1
IDLE_COUNT equ 1
IDLE_COUNT_OLDDOS equ 8
BITS 16
CPU 8086
ORG 100h
jmp near init_entry
id_str:
db 'PETITIDLE'
id_rev:
db '**'
id_str_end:
is_active db 1
is_nec98 db 0
org28 dd 0
idle_cnt dw 1
idle_init dw IDLE_COUNT
org0A dd 0
;------------------------------
tsr_entry:
New28:
cmp byte [cs: is_active], 0
je .chain
dec word [cs: idle_cnt]
jnz .chain
pushf
cli
push dx
mov dx, [cs: idle_init]
mov [cs: idle_cnt], dx
cmp byte [cs: is_nec98], 0
je .do_hlt
; NEC98 re-unmask v-sync interrupt
; in al, 02h
; call PICwait
; and al, 0fbh ; primary PIC IR2
; out 02h, al
out 64h, al ; NEC98: raise next v-sync interruption
.do_hlt:
sti
nop
hlt
pop dx
popf
.chain:
jmp far [cs: org28]
; align 16
tsr_bottom:
;------------------------------
PICwait:
jmp $+2 ; just to be safe... (should be enough by near call and ret)
ret
;tsr_bottom:
;------------------------------
optE db 0
optD db 0
optH db 0
optR db 0
optN dw 0
GetOpt:
.lp0:
lodsb
cmp al, '-'
je .opt
cmp al, '/'
je .opt
.next:
cmp al, 13
je .exit
loop .lp0
.exit:
ret
.opt:
loop .opt1 ; (dec cx)
ret
.opt1:
lodsb
cmp al, '?'
jne .opt2
.opt_h:
mov byte [optH], 1
jmp short .next
.opt2:
or al, 20h
cmp al, 'h'
je .opt_h
cmp al, 'r'
jne .opt3
mov byte [optR], 1
.opt3:
cmp al, 'd'
jne .opt4
mov byte [optD], 1
mov byte [optE], 0
.opt4:
cmp al, 'e'
jne .opt5
mov byte [optE], 1
mov byte [optD], 0
.opt5:
jmp short .next
ModifyState:
mov al, [optD]
mov ah, [optE]
or ax, ax
jnz .l2
ret
.l2:
or ah, ah
je .l3
mov byte [es: is_active], 1
.l3:
or al, al
je .l4
mov byte [es: is_active], 0
.l4:
ret
init_entry:
mov ax, cs
mov ds, ax
mov ax, [es: 002ch] ; get env seg (initial ES as PSP segment)
or ax, ax
je .l2
mov word [es: 002ch], 0 ; release environs
mov es, ax
mov ah, 49h
int 21h
.l2:
call CheckMachine
mov dx, msgOpening
cmp byte [is_nec98], 0
je .l3
mov dx, msgOpeningN
.l3:
call PutS
mov dx, msgOpening2
call PutS
mov si, 0080h
xor cx, cx
lodsb
mov cl, al
call GetOpt
%ifdef DEBUG
xor ax, ax
mov al, [optH]
call PutH8
call PutN
mov al, [optR]
call PutH8
call PutN
%endif
mov dx, msgHelp
cmp byte [optH], 0
jne .do_putmsg_and_exit
mov ax, 3528h
int 21h
mov word [org28], bx
mov word [org28 + 2], es
cmp byte [is_nec98], 0
je .l4
mov ax, 350ah
int 21h
mov word [org0A], bx
mov word [org0A + 2], es
.l4:
call GetProperDosVer
cmp ax, 0400h
jae .l5
mov word [idle_init], IDLE_COUNT_OLDDOS
.l5:
call FindMcbChain
jne .do_tsr
cmp byte [optR], 0
je .do_modify
jmp .do_release
.do_modify:
; just modify TSR state
call ModifyState
mov dx, msgDisable
cmp byte [es: is_active], 0
je .do_modify_l2
mov dx, msgEnable
.do_modify_l2:
.do_putmsg_and_exit:
call PutS
mov ax, 4c00h
int 21h
.do_tsr:
cmp byte [optR], 0
je .tsr_l2
mov dx, errTSRnoinst
call PutS
mov ax, 4c01h
int 21h
.tsr_l2:
push cs
pop es
call ModifyState
mov dx, New28
mov ax, 2528h
int 21h
cmp byte [is_nec98], 0
je .tsr_novsync
%ifdef HOOK_NEC98VSYNC
mov dx, New0A
mov ax, 250ah
int 21h
%endif
; NEC98 enable INT 0Ah (vsync)
in al, 02h
call PICwait
and al, 0fbh ; primary PIC IR2
out 02h, al
.tsr_novsync:
mov dx, msgInstalled
call PutS
mov dx, tsr_bottom
add dx, 15
mov cl, 4
shr dx, cl
mov ax, 3100h
int 21h
ret ; for a proof...
.do_release:
mov cx, es
cmp word [es: org0A], 0 ; int 0A hooked?
je .rel_l2
%ifdef HOOK_NEC98VSYNC
push es
mov ax, 350ah
int 21h
mov dx, es
pop es
cmp bx, New0A ; check int 0A hooked by anyone...
jne .err_hooked
cmp cx, dx
jne .err_hooked
%endif
.rel_l2:
push es
mov ax, 3528h
int 21h
mov dx, es
pop es
cmp bx, New28 ; check int 28 hooked by anyone...
jne .err_hooked
cmp cx, dx
jne .err_hooked
push ds ; then, restore vectors
%ifdef HOOK_NEC98VSYNC
lds dx, [es: org0A]
or dx, dx
jz .rel_l3
mov ax, 250ah
int 21h
.rel_l3:
%endif
lds dx, [es: org28]
mov ax, 2528h
int 21h
pop ds
mov ah, 49h ; finally release the code from memory
int 21h
mov dx, msgReleased
call PutS
mov ax, 4c00h
int 21h
.err_hooked:
mov dx, errVectorTrapped
call PutS
mov ax, 4c01h
int 21h
CheckMachine:
xor ax, ax
mov es, ax
; check FM-R at first
mov ax, word [es: 00afh * 4 + 2]
cmp ax, word [es: 00b0h * 4 + 2]
jne .no_nec98
; check PC-98 or IBM PC (compatibles)
; int 1Ah AH=0 result CX:DX = ticks(IBM) / no-op (PC98)
mov cx, 0ffffh
xor ax, ax
int 1ah
cmp cx, 0ffffh
jne .no_nec98
dec cx
xor ax, ax
int 1ah
cmp cx, 0fffeh
jne .no_nec98
mov byte [cs: is_nec98], 1
.no_nec98:
ret
GetProperDosVer:
xor bx, bx
mov ax, 3306h
int 21h
cmp al, 0ffh
je .nodos5p
cmp bl, 5
jb .nodos5p
cmp bl, 100
jae .nodos5p
mov ax, bx
jmp short .l2
.nodos5p:
mov ax, 3000h
int 21h
.l2:
cmp al, 10
jne .l3
mov ax, 1e03h ; OS/2 1.x as DOS 3.30
jmp short .exit
.l3:
cmp al, 20
jne .l4
mov ax, 0005h ; OS/2 2.x or warp as DOS 5.0
.l4:
.exit:
xchg al, ah
ret
CheckMcb:
mov dx, cs ; (do not) check myself
cmp ax, dx
je .not_this
cmp cx, 12h ; check minimal size (by paragraph)
jae .l2
.not_this:
or ax, ax ; zf=0 (because ax != 0)
ret
.l2:
mov si, id_str
mov di, si
mov cx, id_str_end - id_str
repe cmpsb
;or cx, cx
ret
umbchain dw 0
FindMcbChain:
%ifdef DEBUG
call FindMcbChain2
pushf
mov dx, .found
je .l2
mov dx, .notfound
.l2:
call PutS
popf
ret
.notfound:
db 'not '
.found:
db 'found', 13, 10, '$'
FindMcbChain2:
%endif
call GetProperDosVer
cmp al, 5
jb .l2
mov ax, 5802h
int 21h
jc .l2
cmp al, 0ffh
je .l2
mov byte [umbchain], al
mov ax, 5803h ; link UMB to MCB chain
mov bx, 1
int 21h
.l2:
mov ah, 52h
int 21h
mov es, word [es: bx - 2] ; first MCB
.lp:
mov ax, es
inc ax
%ifdef DEBUG
call PutH16
call PutN
%endif
mov cx, word [es: 0003h]
cmp ax, word [es: 0001h] ; check owner
jne .next
push cx
push es
mov es, ax
call CheckMcb
pop es
pop cx
je .exit
.next:
cmp byte [es: 0000h], 'M'
jne .exit
mov ax, es
add ax, cx
inc ax
mov es, ax
jmp short .lp
.exit:
pushf
mov ax, es
inc ax
mov es, ax
mov bx, [umbchain]
mov ax, 5803h
int 21h
popf
ret
PutS:
push ax
mov ah, 9
int 21h
pop ax
ret
%ifdef DEBUG
PutC:
push ax
push dx
mov dl, al
mov ah, 2
int 21h
pop dx
pop ax
ret
PutN:
push ax
mov al, 13
call PutC
mov al, 10
call PutC
pop ax
ret
put_h4:
push ax
and al, 0fh
add al, '0'
cmp al, '9'
jbe .l2
add al, 'A' - '9' - 1
.l2:
call PutC
pop ax
ret
PutH8:
push ax
push cx
mov cl, 4
shr al, cl
call put_h4
pop cx
pop ax
call put_h4
ret
PutH16:
xchg ah, al
call PutH8
xchg ah, al
call PutH8
ret
%endif ; DEBUG
msgOpeningN:
db 82h, 0d5h, 82h, 0bfh, 82h, 0c7h, 82h, 0e9h
db '(Petit Idle)'
db ' : a minimal CPU cooler for NEC PC-98', 13, 10
db '$'
msgOpening:
db 'Petit Idle'
db ' : a minimal CPU cooler for DOS', 13, 10
db '$'
msgOpening2:
db 'version ', '0'+VERSION_MAJOR, '.', '0'+VERSION_MINOR, '.', '0'+VERSION_REV
db ' (built at ', __DATE__, ' ', __TIME__, ')'
db 13, 10, '$'
msgEnable:
db 'Petidle is activated.', 13, 10, '$'
msgDisable:
db 'Petidle is inactivated.', 13, 10, '$'
msgInstalled:
db 'Petidle was installed.', 13, 10, '$'
msgReleased:
db 'Petidle is released from memory.', 13, 10, '$'
errTSRnoinst:
db 'error: Petidle is not installed.', 13, 10, '$'
errVectorTrapped:
db 'error: Interrupt vector(s) is trapped by other app(s)...', 13, 10, '$'
msgHelp:
db 'Usage: petidle [/e] [/d] [/r]', 13, 10
db ' /e Enable Petidle (reduce CPU usage)', 13, 10
db ' /d Disable Petidle (do not reduce the usage)', 13, 10
db ' /r Release Petidle from memory', 13, 10
db '$'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment