Skrzynka z narzędziami
Ciekawe zastosowanie przerwań NMI

Janusz 'jad' Dąbrowski
jad64@wp.pl

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

 © 1999-2019 Wszystkie prawa zastrzeżone
 Webmaster: Mariusz "Flooder" Młynek