Skrzynka z narzędziami
Ciekawe zastosowanie przerwań NMI
Komputer Commodore 64 wyposażony jest w dwa porty wejścia/wyjścia oznaczone CIA1 i CIA2. Są to dwa identyczne układy. Linia przerwań CIA1 podpięta jest do linii IRQ procesora, a linia przerwań CIA2 dołączona jest do linii NMI. Przerwanie NMI (niemaskowalne) ma wyższy priorytet od IRQ. Żądanie przerwania NMI zostanie zawsze wykonane. Przerwanie NMI generowane jest najczęściej po wciśnięciu klawisza RESTORE.
Poniżej prosty przykład procedury NMI reagującej na ten klawisz.
Prezentuję także pełne źródła do odczytu pod emulatorem bądź oryginalnym C64 - plik znajduje się tutaj (D64).
Drobna uwaga do oznaczeń w kodzie:
{ oznacza znak 'mniejsze od' - <, natomiast } oznacza 'większe od' - >
lda #{my_nmi ; wpisanie adresu
sta $318 ; przerwania NMI
lda #}my_nmi ; wskazującego
sta $319 ; na naszą procedurę
rts
my_nmi inc $d020 ; to się ma wykonać po wciśnięciu RESTORE
jmp $fe47 ; kontynuacja procedury NMI
Efektem działania powyższego programu będzie zmiana koloru ramki przy każdym wciśnięciu klawisza RESTORE.
Głównym zastosowaniem przerwań IRQ jest cykliczne wywoływanie jakiejś procedury. Kernal używa przerwań IRQ np. do odczytu klawiatury. Standardowo przerwanie IRQ wywoływane jest 60 razy na sekundę. Ponieważ układy CIA1 i CIA2 są identyczne, tego drugiego także można użyć do cyklicznego wywoływania jakiejś procedurki.
Poniżej zamieszczam prosty przykład:
lda #{my_nmi ; wpisanie adresu
sta $318 ; przerwania NMI
lda #}my_nmi ; wskazującego
sta $319 ; na naszą procedurę
lda #$ff ; ustawienie timera
sta $dd04 ; młodszy bajt
sta $dd05 ; starszy bajt
lda #%00000001 ; wystartowanie timera, odlicznie ciągłe
sta $dd0e
lda #$81 ; timer będzie generował przerwanie
sta $dd0d
rts
my_nmi pha ; zapamiętanie rejestrów na stosie
txa
pha
tya
pha
jsr my_proc ; wywołanie naszej procedury
lda #$81 ; ustawienie portu
jmp $fe4e ; kontynuacja procedury NMI (w ROMie)
my_proc inc $d020 ; nasza prcedura
rts
Nie możemy, tak jak w poprzednim przypadku, kontynuować procedurę w ROMie pod adresem $fe47, gdyż KERNAL wpisuje w rejestr $dd0d wartość $7f, co spowodowałoby jednorazowe wykonanie naszej procedury. Z tego powodu "wskakujemy" pod adres $fe4e.
Na koniec tego artykułu zamieszczam listing programu, którego zadaniem będzie wyświetlenie w okienku zawartości pamięci naszego komodorka. Aktywacja okienka odbywa się po wciśnięciu kombinacji klawiszy C=+Control. Klawiszami kursora zmieniamy adres o 1 lub 8 bajtów, klawiszami } i { zmieniamy adres o 64 bajty. Program nie jest w 100% przetestowany. Jak widać napisanie go zajęło mi 6 godzin, w tym przerwa na wizytę u teściów ;-).
;*****************************************
;* NMI Wglądownica *
;* *
;* (w) by Janusz Dąbrowski (jad64@wp.pl) *
;* *
;* ver 0.01b started 30-01-2002 17:52 *
;* ver 0.01b finished 30-01-2002 23:47 *
;*****************************************
!zn NmiWgl
!ct pet
!to "nmi_wgl.prg"
;************************************
;* CONSTS *
;************************************
winScrAdr = $401
winWid = 18
winHgt = 14
cntr = $02 ;(1)
hiLite = $02 ;(1)
bufPtr = $FB ;(2)
memPtr = $FB ;(2)
scrPtr = $FD ;(2)
;************************************
;* CODE *
;************************************
*=$801
; BASIC header
!word $80B,2002
!text $9E,"2061"
!byte 0,0,0
; Inicjalizacja
lda #myNmi
sta $319
lda #$FF
sta $DD04
sta $DD05
lda #%00000001
sta $DD0E
lda #$81
sta $DD0D
lda #14
jmp $FFD2
;************************************
;* Procedura NMI *
;************************************
myNmi=*
pha
txa
pha
tya
pha
lda wglActive
bne mn3
lda $28D
cmp #$06
bne mn3
cli
sta wglActive
jsr Wgladownica
lda #0
sta wglActive
mn3 lda #$81
jmp $FE4E
;************************************
;* Wglądownica - program *
;************************************
Wgladownica=*
jsr winOpen
; hex dump
dl0 lda #<(winScrAdr+41)
sta scrPtr
lda #>(winScrAdr+41)
sta scrPtr+1
ldx #0
dl1 ldy #0
dl2 lda #$00
cpx crsrPtr
bne *+4
lda #$80
sta hiLite
mPtr lda $1C0,x
inx
jsr hexDisp
cpy #16
bcc dl2
jsr scrNewLine
cpx #64
bcc dl1
; hex adres
jsr scrNewLine
ldy #0
sty hiLite
clc
lda mPtr+1
adc crsrPtr
sta decBuf
sta memPtr
tax
lda mPtr+2
adc #0
sta decBuf+1
sta memPtr+1
jsr hexDisp
txa
jsr hexDisp
; dziesiętny adres
jsr decConv
ldy #10
lda decBuf+4
jsr hexDisp
lda decBuf+3
jsr hexDisp
lda decBuf+2
jsr hexDisp
; ascii dump
jsr scrNewLine
jsr scrNewLine
ldy #15
dla lda (memPtr),y
jsr asc2scr
sta (scrPtr),y
dey
bpl dla
; sprawdzenie klawiatury
jsr $FFE4
beq dl0
cmp #95
bne dl3
jmp winClose
dl3 ldx #5
dl4 cmp keyTable,x
beq dl5
dex
bpl dl4
jmp dl0
dl5 txa
asl
tax
lda procTable,x
sta pAdr+1
lda procTable+1,x
sta pAdr+2
pAdr jsr *
jmp dl0
;************************************
;* Kursor w dół *
;************************************
csrDown=*
lda crsrPtr
cmp #56
bcs cd1
adc #8
sta crsrPtr
rts
cd1 clc
lda mPtr+1
adc #8
sta mPtr+1
bcc *+5
inc mPtr+2
rts
;************************************
;* Kursor w prawo *
;************************************
csrRight=*
lda crsrPtr
cmp #63
bcs cr1
inc crsrPtr
rts
cr1 inc mPtr+1
bne *+5
inc mPtr+2
rts
;************************************
;* Kursor w górę *
;************************************
csrUp=*
lda crsrPtr
cmp #8
bcs cu1
sec
lda mPtr+1
sbc #8
sta mPtr+1
bcs *+5
inc mPtr+2
rts
cu1 sbc #8
sta crsrPtr
rts
;************************************
;* Kursor w lewo *
;************************************
csrLeft=*
lda crsrPtr
bne cl1
lda mPtr+1
bne *+5
dec mPtr+2
dec mPtr+1
rts
cl1 dec crsrPtr
rts
;************************************
;* Strona do góry *
;************************************
pageUp=*
sec
lda mPtr+1
sbc #64
sta mPtr+1
bcs *+5
dec mPtr+2
rts
;************************************
;* Strona w dół *
;************************************
pageDown=*
clc
lda mPtr+1
adc #64
sta mPtr+1
bcc *+5
inc mPtr+2
rts
;************************************
;* Wyświetl liczbę hex *
;************************************
hexDisp=*
pha
lsr
lsr
lsr
lsr
jsr nibDisp
pla
;************************************
;* Wyświetl nibble hex *
;************************************
nibDisp=*
and #$0F
sed
cmp #10
adc #"0"
cld
ora hiLite
sta (scrPtr), y
iny
rts
;************************************
;* Konwersja na dziesiętne *
;************************************
decConv=*
lda #0
sta decBuf+2
sta decBuf+3
sta decBuf+4
ldx #16
sed
dc1 asl decBuf
rol decBuf+1
lda decBuf+2
adc decBuf+2
sta decBuf+2
lda decBuf+3
adc decBuf+3
sta decBuf+3
rol decBuf+4
dex
bne dc1
cld
rts
;************************************
;* Otwarcie okna *
;************************************
winOpen=*
lda #winBuf
sta bufPtr+1
lda #winScrAdr
sta scrPtr+1
; ekran pod oknem do bufora
ldx #winHgt
wo1 ldy #0
wo2 lda (scrPtr),y
sta (bufPtr),y
iny
cpy #winWid
bcc wo2
jsr scrNewLine
clc
lda bufPtr
adc #winWid
sta bufPtr
bcc *+4
inc bufPtr+1
dex
bne wo1
; rysowanie ramki
lda #winScrAdr
sta scrPtr+1
ldx #0
jsr winDrawLine
ldx #3
lda #winHgt-2
sta cntr
wo3 jsr winDrawLine
dec cntr
bne wo3
ldx #6
; jmp winDrawLine
;************************************
;* Rysowanie linii okna *
;************************************
winDrawLine=*
ldy #0
lda frameDef,x
sta (scrPtr),y
lda frameDef+1,x
wd1 iny
sta (scrPtr),y
cpy #winWid-2
bcc wd1
lda frameDef+2,x
iny
sta (scrPtr),y
; jmp scrNewLine
;************************************
;* Obliczenie adresu nowej linii *
;************************************
scrNewLine=*
clc
lda scrPtr
adc #40
sta scrPtr
bcc *+4
inc scrPtr+1
rts
;************************************
;* Zamknięcie okna *
;************************************
winClose=*
lda #winBuf
sta bufPtr+1
lda #winScrAdr
sta scrPtr+1
; odtworzenie ekranu po oknem
ldx #winHgt
wc1 ldy #0
wc2 lda (bufPtr),y
sta (scrPtr),y
iny
cpy #winWid
bcc wc2
jsr scrNewLine
clc
lda bufPtr
adc #winWid
sta bufPtr
bcc *+4
inc bufPtr+1
dex
bne wc1
rts
;************************************
;* Konwersja ASCII na kod ekranowy *
;************************************
asc2scr=*
ora #$00
bmi g80
cmp #$20
bcs g20
ora #$80
rts
g20 cmp #$60
bcc l60
and #$DF
rts
l60 and #$3F
rts
g80 and #$7F
cmp #$7F
bne *+4
lda #$5E
cmp #$20
bcs gA0
ora #$80
gA0 ora #$40
rts
;************************************
;* DATA *
;************************************
wglActive !byte 0
crsrPtr !byte 0
decBuf !fill 5
keyTable !byte 17,29,145,157,44,46
procTable !word csrDown,csrRight,csrUp
!word csrLeft,pageUp,pageDown
frameDef !byte $70,$40,$6e
!byte $5d,$20,$5d
!byte $6d,$40,$7d
winBuf=*
!eof
|