Gestione di un interruzione della tastiera con Turbo C ++ 3.0
Domanda
Ho un progetto. Questo è un gioco semplice, "Falling Blocks". L'area di gioco è considerata una griglia, che ha una dimensione 20x20. Ci saranno blocchi che cadono dalla parte superiore dello schermo e un eroe in fondo, che spareranno i blocchi. Lo scopo del gioco è sparare blocchi prima di raggiungere i profitti. Rimane sempre in fondo. Ogni volta che l'utente premi il pulsante Spazio della tastiera genererò un proiettile e l'eroe si sposta sulla linea di fondo con i tasti freccia destro e sinistro. Non ho idea di gestire questi interrupt della tastiera con Turbo C ++ 3.0. È vietato anche che usando "dos.h" e "int 21h". Potresti darmi suggerimenti su questi progetti?
EDIT: ho trovato queste informazioni ma non riuscivo a capire come implementarle:
Quando viene premuto un tasto sulla tastiera, viene prodotto un interrupt insieme a un codice di scansione chiamato "Make Code" e quando il tasto rilascia un "codice di interruzione" viene prodotto dal controller della tastiera. Su un PC, la tastiera è controllata da un chip e assegnata ai numeri di porta 60H e 61H. Quando viene premuto un tasto sulla tastiera, il valore di scansione viene inserito nel registro a 60H. È possibile ottenere questo codice di scansione con il seguente comando: In AL, 60H Dopo aver ottenuto il codice di scansione, è necessario ripristinare la programmazione della tastiera il registro dei comandi del chip a 61 ore con i seguenti comandi: in AL, 61H o AL, 82H 61H , Al e Al, 7fh out 61h, Al alla fine di ogni routine di servizio di interruzione, cancella il bit di servizio PIC, inviando comando End of Interrupt (EOI), 20H alla porta Pic all'indirizzo 20h. mov al, 20h out 20h, al
Soluzione
File kbdc.c:
#include <stdio.h>
extern void SetNewIrq9Isr(void);
extern void RestoreOldIrq9Isr(void);
#define SCAN_BUF_SIZE 1024
extern volatile unsigned char ScanBuf[SCAN_BUF_SIZE];
extern volatile unsigned ScanReadIdx;
extern volatile unsigned ScanWriteIdx;
const char ScanToChar[] =
"??1234567890-=??"
"QWERTYUIOP[]??AS"
"DFGHJKL;\"`?\\ZXCV"
"BNM,./??? ";
int IsScanCodeAvailable(void)
{
return ((ScanWriteIdx - ScanReadIdx) & (SCAN_BUF_SIZE - 1)) != 0;
}
unsigned char GetScanCode(void)
{
unsigned char code;
while (!IsScanCodeAvailable());
code = ScanBuf[ScanReadIdx];
ScanReadIdx++;
ScanReadIdx &= SCAN_BUF_SIZE - 1;
return code;
}
int main(void)
{
SetNewIrq9Isr();
printf("Press keys to see scan codes.\nPress ESC to exit.\n");
for (;;)
{
unsigned code, symbol;
code = GetScanCode();
symbol = code & 0x7F;
symbol = (symbol < sizeof(ScanToChar)) ? ScanToChar[symbol] : '?';
printf("scan code: 0x%02X, symbol: \"%c\"\n", code, (char)symbol);
if (code == 1)
{
break;
}
}
RestoreOldIrq9Isr();
return 0;
}
File kbda.asm:
GLOBAL _SetNewIrq9Isr, _RestoreOldIrq9Isr
GLOBAL _ScanBuf, _ScanReadIdx, _ScanWriteIdx
SEGMENT _TEXT PUBLIC CLASS=CODE USE16
; void SetNewIrq9Isr(void);
_SetNewIrq9Isr:
push bx
push es
mov bx, 9 * 4
mov ax, 0
mov es, ax
cli
mov ax, [es:bx]
mov [_pOldIrq9Isr], ax
mov word [es:bx], _NewIrq9Isr
mov ax, [es:bx + 2]
mov [_pOldIrq9Isr + 2], ax
mov [es:bx + 2], cs
sti
pop es
pop bx
ret
; void RestoreOldIrq9Isr(void);
_RestoreOldIrq9Isr:
push bx
push es
mov bx, 9 * 4
mov ax, 0
mov es, ax
cli
mov ax, [_pOldIrq9Isr]
mov [es:bx], ax
mov ax, [_pOldIrq9Isr + 2]
mov [es:bx + 2], ax
sti
pop es
pop bx
ret
_NewIrq9Isr:
pusha
push ds
mov ax, _DATA
mov ds, ax
in al, 60h
push ax
in al, 061h
mov ah, al
or al, 080h
out 061h, al
mov al, ah
out 061h, al
pop ax
; ScanBuf[ScanWriteIdx] = scan code;
; ScanWriteIdx = (ScanWriteIdx + 1) & (SCAN_BUF_SIZE - 1);
mov bx, [_ScanWriteIdx]
mov [_ScanBuf + bx], al
inc bx
and bx, 1023
mov [_ScanWriteIdx], bx
mov al, 20h
out 20h, al
pop ds
popa
iret
SEGMENT _DATA PUBLIC CLASS=DATA
_pOldIrq9Isr resd 1
; #define SCAN_BUF_SIZE 1024
; volatile unsigned char ScanBuf[SCAN_BUF_SIZE];
; volatile unsigned ScanReadIdx = 0;
; volatile unsigned ScanWriteIdx = 0;
_ScanBuf resb 1024
_ScanReadIdx dw 0
_ScanWriteIdx dw 0
Produzione:
Press keys to see scan codes.
Press ESC to exit.
scan code: 0x10, symbol: "Q"
scan code: 0x90, symbol: "Q"
scan code: 0x11, symbol: "W"
scan code: 0x91, symbol: "W"
scan code: 0x12, symbol: "E"
scan code: 0x92, symbol: "E"
scan code: 0x02, symbol: "1"
scan code: 0x82, symbol: "1"
scan code: 0x03, symbol: "2"
scan code: 0x83, symbol: "2"
scan code: 0x04, symbol: "3"
scan code: 0x84, symbol: "3"
scan code: 0x01, symbol: "?"
Ora, alcune parole su come compilare questo.
Compilare il file di assemblaggio con Nasm usando nasm.exe -f obj kbda.asm
. Produrrà kbda.obj
. Crea un progetto in Borland/Turbo C/C ++ IDE, includi in esso kbdc.c
e kbda.obj
. Assicurati che il codice venga compilato nel modello di memoria piccolo o minuscolo (fondamentalmente, dobbiamo assicurarci SetNewIrq9Isr()
e RestoreOldIrq9Isr()
verranno chiamati come funzioni vicine). Compilalo.
Ci sono alcuni avvertimenti.
Innanzitutto, nessuno dei getc()
, gets()
, scanf()
, ecc. Le funzioni funzionano se chiamate tra SetNewIrq9Isr()
e RestoreOldIrq9Isr()
. Appenderanno il programma.
Secondo, il codice non tiene traccia del shift
, control
e alt
chiavi. Ciò che significa per te è che, se esegui questo programma dall'IDE premendo ctrl+F9
, quando il programma termina, l'IDE molto probabilmente penserà ctrl
viene ancora trattenuto. Per "sbloccare" la tastiera dovrai premere e rilasciare ctrl
. Lo stesso può applicarsi ad altre chiavi simili se vengono trattenuti quando inizia questo programma. Puoi includere codice extra per aspettare fino a tutti shift
, control
e alt
sono rilasciati. Credo che tu possa trovare il loro stato attuale nell'area dati del BIOS.
Ovviamente è possibile convertire il file di assemblaggio dalla sintassi NASM alla sintassi del tasmo e compilarlo con tasmo. Sto semplicemente usando strumenti gratuiti, Turbo C ++ 1.01 e NASM.
AGGIORNARE: Ecco il file ASM per TASM:
PUBLIC _SetNewIrq9Isr, _RestoreOldIrq9Isr
PUBLIC _ScanBuf, _ScanReadIdx, _ScanWriteIdx
.386
_TEXT SEGMENT PUBLIC 'CODE' USE16
ASSUME CS:_TEXT, DS:_DATA
; void SetNewIrq9Isr(void);
_SetNewIrq9Isr PROC NEAR
push bx
push es
mov bx, 9 * 4
mov ax, 0
mov es, ax
cli
mov ax, es:[bx]
mov _pOldIrq9IsrOfs, ax
mov word ptr es:[bx], offset _NewIrq9Isr
mov ax, es:[bx + 2]
mov _pOldIrq9IsrSeg, ax
mov es:[bx + 2], cs
sti
pop es
pop bx
ret
_SetNewIrq9Isr ENDP
; void RestoreOldIrq9Isr(void);
_RestoreOldIrq9Isr PROC NEAR
push bx
push es
mov bx, 9 * 4
mov ax, 0
mov es, ax
cli
mov ax, _pOldIrq9IsrOfs
mov es:[bx], ax
mov ax, _pOldIrq9IsrSeg
mov es:[bx + 2], ax
sti
pop es
pop bx
ret
_RestoreOldIrq9Isr ENDP
_NewIrq9Isr PROC NEAR
pusha
push ds
mov ax, _DATA
mov ds, ax
in al, 60h
push ax
in al, 061h
mov ah, al
or al, 080h
out 061h, al
mov al, ah
out 061h, al
pop ax
; ScanBuf[ScanWriteIdx] = scan code;
; ScanWriteIdx = (ScanWriteIdx + 1) & (SCAN_BUF_SIZE - 1);
mov bx, _ScanWriteIdx
mov _ScanBuf[bx], al
inc bx
and bx, 1023
mov _ScanWriteIdx, bx
mov al, 20h
out 20h, al
pop ds
popa
iret
_NewIrq9Isr ENDP
_TEXT ENDS
_DATA SEGMENT PUBLIC 'DATA' USE16
_pOldIrq9IsrOfs dw ?
_pOldIrq9IsrSeg dw ?
; #define SCAN_BUF_SIZE 1024
; volatile unsigned char ScanBuf[SCAN_BUF_SIZE];
; volatile unsigned ScanReadIdx = 0;
; volatile unsigned ScanWriteIdx = 0;
_ScanBuf db 1024 dup (?)
_ScanReadIdx dw 0
_ScanWriteIdx dw 0
_DATA ENDS
END
Lo compili usando tasm.exe /ml kbda.asm
. Il riposo è lo stesso.
Altri suggerimenti
Ho anche seguito un corso simile ai tempi. Fondamentalmente quello che devi fare è catturare l'interruzione della tastiera prima di gestire il gestore dell'interrupt della tastiera del sistema. Devi creare il tuo gestore di interrupt e il legarlo all'interrupt della tastiera. Una volta che hai finito con il tuo lavoro, chiama il gestore di interrupt della tastiera del sistema originale.