NAME Cookie ; Cookie monster. Memory resident program that wakes up after a while ; and demands a cookie using a digitized voice played on the PC's speaker. ; User must type "cookie" to make the cookie monster happy. TEXT SEGMENT BYTE PUBLIC 'CODE' TEXT ENDS STK SEGMENT STACK DW 128 DUP (0) STK ENDS ASSUME CS:TEXT, DS:TEXT, ES:TEXT, SS:STK countr EQU 72 ; 16572 Hz tcadrc EQU 43h tcadrd EQU 40h tcmode EQU 34h tc2mode EQU 0B8h ppiadr EQU 61h fuselen EQU 20 ; length of fuse EXTRN Giveme:NEAR, Givemelen:WORD EXTRN Yum:NEAR, Yumlen:WORD TEXT SEGMENT Messptr DW 0 Messcnt DW 0 Fuse1 DW 0 Fuse2 DW 0 start: push cs ; set ds pop ds ; Set initial short fuse. mov Fuse1,0 mov Fuse2,1 ; Add to int 28h chain. mov ax,3528h int 21h mov WORD PTR Oldint28,bx mov WORD PTR Oldint28+2,es mov dx,OFFSET int28 mov ax,2528h int 21h ; Terminate and stay resident. mov dx,SEG HIGHMEM mov ax,cs sub dx,ax mov ax,3100h int 21h ; int 28h interrupt routine. Oldint28 DD 0 int28 PROC FAR sti ; interrupts back on dec cs:Fuse1 ; decrement the fuse jnz notyet dec cs:Fuse2 ; secondary fuse jnz notyet push ax ; save regs push bx push cx push dx push bp push si push di push ds push es mov ax,cs ; set ds and es mov ds,ax mov es,ax ; Demand a cookie and wait for the response. call getcookie ; Got it. mov ax,OFFSET Yum ; say yum mov Messptr,ax mov ax,Yumlen ; number of bytes in Yum mov Messcnt,ax call play mov Fuse2,fuselen ; reset fuse pop es ; restore regs and continue pop ds pop di pop si pop bp pop dx pop cx pop bx pop ax notyet: cli ; interrupts off jmp cs:Oldint28 ; continue along interrupt chain int28 ENDP ; Get a cookie from the user. getcookie PROC NEAR mov ax,OFFSET Giveme ; ask for a cookie mov Messptr,ax mov ax,Givemelen ; number of bytes in Giveme mov Messcnt,ax call play mov di,OFFSET Buf ; point to buffer mov si,99 ; 99 chars and room for a nul at the end ; Read characters into a buffer up to a CR. getc: xor ax,ax int 16h cmp al,13 ; test for CR je gotit dec si ; buffer full? jz gotit cmp al,65 ; test for uppercase jl lowerc cmp al,90 jg lowerc add al,32 ; convert to lowercase lowerc: mov BYTE PTR [di],al ; store in buffer inc di jmp SHORT getc ; back for more gotit: mov BYTE PTR [di],0 ; nul terminate ; See if he typed "cookie". mov ax,OFFSET Cookie push ax mov ax,OFFSET Buf push ax call strcmp pop cx pop cx or ax,ax jne getcookie ; nope, start again ret getcookie ENDP Buf LABEL BYTE DB 100 DUP (?) Cookie LABEL BYTE DB "cookie", 0 strcmp PROC NEAR push bp ; get frame mov bp,sp cld mov di,[bp+6] ; get str2 mov si,di ; copy xor al,al xor cx,cx not cx repnz scasb ; find its length not cx mov di,si ; get str2 mov si,[bp+4] ; get str1 repz cmpsb ; compare strings mov al,[si-1] ; compare last byte sub al,[di-1] cbw pop bp ; restore regs and return ret strcmp ENDP ; Keyboard interrupt routine. Oldkbint DD 0 kbint PROC FAR push ax ; save reg in al,60h ; get char from keyboard - ignore it in al,61h ; clear keyboard mov ah,al or al,80h out 61h,al mov al,ah out 61h,al mov al,20h ; issue EOI out 20h,al pop ax ; return iret kbint ENDP play PROC NEAR ; Substitute local keyboard interrupt routine. push es mov ax,3509h int 21h mov WORD PTR Oldkbint,bx mov WORD PTR Oldkbint+2,es mov dx,OFFSET kbint mov ax,2509h int 21h ; Get vector of current timer interrupt. mov ax,3508h int 21h mov WORD PTR Oldtmrint,bx mov WORD PTR Oldtmrint+2,es ; Mask timer interrupt. in al,21h or al,01h out 21h,al ; Plug in the local timer interrupt routine. mov dx,OFFSET playtimerint mov ax,2508h int 21h ; Rev up timer. mov al,tcmode out tcadrc,al mov ax,countr out tcadrd,al mov al,ah out tcadrd,al ; Get pointer to message. mov bx,Messptr mov si,Messcnt ; Initialize registers. mov bp,0 ; done flag mov ch,[bx] ; get first byte mov ah,0 ; init count in al,ppiadr ; get normal ppi state and al,0FDh ; zero speaker bit mov dl,al ; save for later ; Unmask timer interrupt. in al,21h and al,0FEh out 21h,al sti ; Test for done. ptfd: or bp,bp jge ptfd ; bp negative means done ; Mask timer interrupt. cli in al,21h or al,01h out 21h,al sti ; Fix timer. mov al,tcmode out tcadrc,al mov al,0 out tcadrd,al out tcadrd,al ; Restore original timer interrupt vector. push ds lds dx,cs:Oldtmrint mov ax,2508h int 21h pop ds ; Unmask timer interrupt. in al,21h and al,0FEh out 21h,al ; Restore keyboard interrupt vector. push ds lds dx,cs:Oldkbint mov ax,2509h int 21h pop ds ; Figure out how long the system 18.2 Hz timer has been off. mov ax,countr shl ax,1 shl ax,1 shl ax,1 mov cx,Messcnt mul cx ; Adjust the bios time of day to catch up. mov ax,40h mov es,ax mov bx,6Ch cli add es:[bx],dx adc WORD PTR es:[bx+2],0 cmp WORD PTR es:[bx+2],18h jb biosfixed ja wrap cmp WORD PTR es:[bx],0B0h jb biosfixed wrap: sub WORD PTR es:[bx],0B0h sbb WORD PTR es:[bx+2],18h biosfixed: sti ; Exit. pop es ret play ENDP ; Timer interrupt routine. Oldtmrint DD 0 playtimerint PROC FAR or bp,bp jl ptmrint1 sub al,al rcl ch,1 rcl al,1 rcl al,1 or al,dl out ppiadr,al inc ah cmp ah,8 jne ptmrint1 sub ah,ah inc bx mov ch,[bx] dec si or si,si jnz ptmrint1 or bp,8000h ; done - ensure bp negative ptmrint1: mov al,20h out 20h,al iret playtimerint ENDP TEXT ENDS HIGHMEM SEGMENT BYTE HIGHMEM ENDS END start