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

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *