Last active
June 11, 2020 12:50
-
-
Save lpproj/634ed84262c8defa1f07 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%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