4

我被要求用中断(完整说明)重写下面的代码。我们还没有真正在课堂上讨论过中断,我不确定它们是如何使用或如何实现的。这是我在没有使用中断的情况下编写的代码,这些中断基本上使键盘变成钢琴或可以播放为摩托罗拉 68hc11编写的歌曲(lamb,chromsc,hall) :

PORTA      EQU  $1000  ;The speaker port
SPEAKER    EQU  PORTA
SPEAKERBIT   EQU  %00100000

SCSR    EQU  $6400    ; Serial communications status register
SCDR    EQU  $6401    ; Serial communcations data register
TDRE    EQU  %00000010    ; mask for TDRE flag in SCSR
RDRF    EQU  %00000001    ; mask for RDRF flag in SCSR


ORG     $8000;  Place data at 8000
LAMB    FCB  'trerttttrrrrtuuutrerttttrrtre',$00
CHROMSC FCB  'q2we4r5ty7u8i9op-[=]',$00
HALL    FCB  'qwertet5w5r2rqwertetiutetu',$00

KEYMAP  FDB  'q', 220, 758  ;  A    ;key, frequency, and 1/4 second "period"
        FDB  '2', 233, 715  ;  A#
        FDB  'w', 247, 675  ;  B
        FDB  'e', 262, 636  ;  C
        FDB  '4', 277, 602  ;  C#
        FDB  'r', 294, 567  ;  D
        FDB  '5', 311, 536  ;  D#
        FDB  't', 330, 505  ;  E
        FDB  'y', 349, 478  ;  F
        FDB  '7', 370, 450  ;  F#
        FDB  'u', 392, 425  ;  G
        FDB  '8', 415, 402  ;  G#
        FDB  'i', 440, 379  ;  A
        FDB  '9', 466, 358  ;  A#
        FDB  'o', 494, 337  ;  B
        FDB  'p', 523, 319  ;  C
        FDB  '-', 554, 301  ;  C#
        FDB  '[', 587, 284  ;  D
        FDB  '=', 622, 268  ;  D#
        FDB  ']', 659, 253  ;  E
        FDB  $00            ;  Null termination character

PROMPT  FCB  $0D, 'Piano program - use QWERTY row to play notes', $0D, $00  ;Prompt String

        ORG  $8800
        LDS  #$DFFF

;;;;;;;;;;  Main Start  ;;;;;;;;;;
        LDX    #PROMPT

        PSHX  ;Push the argument to the stack
        JSR    printString  ;Print the promp string
        PULX

ALWAYS  DES
        JSR    getChar  ;Get a character from the keyboard
        JSR    putChar
        PULA  ;put the character in A

        PSHA  ;Push character to the stack
        JSR    playTone  ;Play the tone
        PULA

        CMPA   #'a'
        BNE    SKIPLAMB

        LDX    #HALL
        PSHX
        JSR    playSong
        PULX

SKIPLAMB CMPA   #'s'
        BNE    BRAALW

        LDX    #LAMB
        PSHX
        JSR    playSong
        PULX

BRAALW  BRA    ALWAYS  ;Loop to the top and continue playing
;;;;;;;;;;  Main End  ;;;;;;;;;;

;;;;;;;;;;  playTone Start  ;;;;;;;;;; Passed an ascii character and a length on the stack
playTone    PSHB  ;for transparency
            PSHA
            PSHX
            PSHY

            TSY    ;make Y point to the top of the stack

            LDAA   8,Y  ;load A with the passed argument
            LDX    #KEYMAP  ;make Y point to the KEYMAP
CBAALWAYS   LDAB   1,X  ;load B with the ascii value
            BEQ    EXITPT  ;If current value is $00, end of table, no key match, exit routine
            CBA        ;Compare B to A
            BEQ    SKIPTESTS  ;If value are equal, skip rest of test to play tone
            XGDX
            ADDD   #6  ;Make X point to the next character to compare
            XGDX
            BRA    CBAALWAYS  ;Branch until the end of table is reached

SKIPTESTS   LDD     2,X  ;Load D with the frequency
            LSRD  ;Number of times to toggle the speaker in a 1/4 second
            LSRD    ;this shortens the tone to an 1/8 note
            ;LSRD    ;this plays a 1/16 note
            ;LSRD    ;this plays a 1/32 note
PERIODLOOP  LDY     4,X  ;Load Y with the delay between toggles
FREQLOOP    DEY    ;Decrement Y until it's 0

            BNE   FREQLOOP    ;Branch until X is 0

            PSHB    ;preserve D
            PSHA

            LDAA  PORTA  ;Load A with the speaker
            EORA  #SPEAKERBIT  ;Toggle the speaker bit
            STAA  PORTA    ;Store back into the speaker

            PULA    ;restore D
            PULB

            SUBD  #1  ;Decrement D
            CPD    #0  ;Compare D to 0
            BNE    PERIODLOOP    ;Branch until D is 0

EXITPT      PULY
            PULX
            PULA
            PULB  ;return the stack to normal

            RTS    ;return to the main program
;;;;;;;;;;  playTone End  ;;;;;;;;;;

;;;;;;;;;;  playSong Start;;;;;;;;;;
playSong    PSHB    ;Reference is passed on the stack
            PSHA    ;Pushes for transparency
            PSHX
            PSHY

            TSX
            LDX     8,x     ;Load X with the passed value

LOOPSTRING  LDAA    0,X     ;Load A with the ith character of the string
            BEQ     ENDPSTRING          ;Skips to end of subroutine if current character is null character
            PSHA            ;Pass the argument in A to putChar
            JSR     playTone
            INS             ;Return the stack to normal
            INX             ;increments X to point to the next character
            BRA     LOOPSTRING

ENDPSTRING  PULY
            PULX
            PULA
            PULB

            RTS


;;;;;;;;;;  playSong End  ;;;;;;;;;;

;;;;;;;;;;  putChar start  ;;;;;;;;;; Passed argument should be an ascii value
putChar     PSHB    ;is passed an argument on the stack
            PSHA    ;for transparency
            PSHX
            PSHY
            TSY     ;stack frame

            LDX     #SCSR            ;Load in address of SCSR (Serial Communication Status Register)
GCWAIT      BRCLR   0,X TDRE GCWAIT    ;Loop
            LDAA    8,Y   ;Load A with the passed value
            STAA    SCDR  ;Write A to the SCDR

            PULY
            PULX
            PULA
            PULB

            RTS
;;;;;;;;;;  putChar end  ;;;;;;;;;;

;;;;;;;;;;  getChar start  ;;;;;;;;;; ascii value is returned
getChar     PSHB    ;No argument. Passes result back on the stack.
            PSHA    ;For transparency
            PSHX
            PSHY
            TSY

            LDX     #SCSR            ;Load in address of SCSR (Serial Communication Status Register)
PCWAIT      BRCLR   0,X RDRF PCWAIT    ;Loop when the
            LDAA    SCDR  ;Load A with what's in the SCDR (should be the pressed key)
            STAA    8,Y   ;Store it to the stack to be passed back

            PULY
            PULX
            PULA
            PULB

            RTS
;;;;;;;;;;  getChar  end  ;;;;;;;;;;

;;;;;;;;;;  printString start   ;;;;;;;;;;  argument passed on the stack in ascii
printString PSHB    ;Reference is passed on the stack
            PSHA    ;Pushes for transparency
            PSHX
            PSHY

            TSX
            LDX     8,x     ;Load X with the passed value

LOOPSTRING1 LDAA    0,X     ;Load A with the ith character of the string
            BEQ    ENDPSTRING1          ;Skips to end of subroutine if current character is null character
            PSHA            ;Pass the argument in A to putChar
            JSR     putChar
            INS             ;Return the stack to normal
            INX             ;increments X to point to the next character
            BRA    LOOPSTRING1

ENDPSTRING1 PULY
            PULX
            PULA
            PULB

            RTS
;;;;;;;;;;  printString end  ;;;;;;;;;;

有人可以给我一个如何有效实现的例子,或者只是提示如何开始编码中断,以便我可以用它们重写我的代码。

4

2 回答 2

5

要在 hc11 上使用中断,您必须做四件事:

  1. 首先决定你想使用什么样的中断。hc11 有许多不同的类型,因此您必须查阅手册以找到适合您要执行的操作的类型。

对于接下来的步骤,我假设您正在使用 IRQ 中断。它只是 hc11 上的一个简单中断引脚,当系统电压降至低时会中断系统。然而,对于任何其他中断,这些步骤将非常相似。

  1. 您需要初始化中断服务程序的起始位置。当中断被触发时,处理器将检查一个向量表以确定它需要去哪里。因此,例如,当触发 IRQ 时,它将转到对应于 IRQ ($FFF2) 的表条目,然后跳转到存储在那里的地址。我们将标记中断服务程序IRQ_INT并将其存储在对应于 IRQ 的表条目中,以便在触发 IRQ 时它将开始执行 label 处的代码IRQ_INT

    ORG   $FFF2
    FDB   IRQ_INT
    
  2. 接下来,您需要启用中断,以便处理器在触发时识别它。您将不得不再次查看手册,了解您必须将值存储在哪个寄存器中才能启用它。要启用 IRQ,必须在中断控制寄存器 (INTCR) 中将 IRQ 启用位 (IRQEN) 设置为 1。然后可以使用CLI命令启用它。

    INTCR    EQU   $001E   ;address of interrupt control register
    INTCR_IN EQU   $60     ;sets IRQEN bits
    
             LDAA  #INTCR_IN
             STAA  INTCR
             CLI
    
  3. 最后,您需要编写中断服务程序。这是每次触发中断时将执行的代码。它将从我们之前在向量表中设置的标签开始,并在到达指令时结束RTI。从中断返回指令告诉处理器服务程序已经完成,它应该返回到中断触发之前它正在执行的任何内容。

    IRQ_INT
           <instructions go here>
           RTI
    

我的猜测是,您会希望在键盘端口上出现某种中断,该中断会在按下键时触发。一旦按键被击中,中断将触发处理器进入中断服务程序。在那里,您必须编写代码来确定按下哪个键并播放正确的音符。

于 2012-04-25T22:09:52.487 回答
2

您必须编写两个 ISR,基本上是子程序。每个似乎都是由计时器触发的硬件中断。

一个 isr 是切换扬声器音调并自行触发(开/关)从而产生 N 个周期的音调的那个。首先熟悉了解如何对定时器 TOC3 进行编程

另一个是计时器 int 循环,每 1 ms 查找输入并监视 RDRF 标志并通过您之前所做的某个项目读取数据。熟悉 TOC2 编程

您需要初始化 ISR,这意味着您必须设置 CPU 以告诉它 ISR 例程所在的位置。您的说明已指示如何为 TOC3 执行此操作,并且应该类似于 TOC2,阅读您的讲义以查看 jmp 表在 toc2 的位置,他告诉您 toc3 在哪里。

 Memory $FFE4 contains 
 $00D9, so insert a JMP XXXX instruction at $00D9 (where XXXX is the 
 address of your TOC3 service routine.  Note: the opcode for JMP is $7E. 

所以做这样的事情:

 org $00d9 
 jmp YourTOC3ISR

;;;;;;;;;;;;;; 主程序

 initialize timer registers ie setup toc2 and toc3
 initialize interrupt registers

 2) Read the current Timer Count from TCNT (pg. 374), add your DELAY to it, 
 and store the result in TOC3 (pg. 409).  This defines when you want the 
 first interrupt to occur.
 3) Set OC3I bit in TMSK1 to enable TOC3 interrupts (pg. 410).
 4) Clear the OC3F bit in TFLG1 to clear any previous interrupt condition.
 (pg. 410).  IMPORTANT!!!  Note that you must write a 1 to this bit to 
 clear it!  Do not use BSET!  See the discussion on page 387.
 5) Optional: Write to OM3 and OL3 bits in TCTL1 register to define how the 
 OC3 output pin will behave when the interrupt occurs (pg. 412).
 6) Enable interrupts globally. (See SEI and CLI instructions.)


 do lab 7 code

;;;;;;;;;;;;;;;;; toc3 isr 例程 YourTOC3Isr, playtone ?

  save the regs and flags

 1) Define when the next interrupt should occur by adding your DELAY to TOC3.
 2) Do whatever it is you want done in the service routine ie speaker togggle stuff
 3) Clear the OC3F bit in TFLG1 to clear the interrupt condition (SEE ABOVE).
4) Return from interrupt when done (pg. 181).
unsave reg and flags
rti

;;;;;;;;;;;;;;; toc2 isr 例程

保存注册和标志

然后它将检查 RDRF 标志,如果未设置则返回。如果设置,它将从接收寄存器读取字符,将其写入发送寄存器(您可以使用旧的 putchar 例程),然后调用 playtone 并从中断返回。

这部分听起来他想让你调用另一个中断例程 toc3,所以只需写入启动定时器的 TOC3 控制寄存器,中断应该注意它自己。

取消保存 reg 和标志 rti

希望这会有所帮助,M

于 2012-04-25T22:38:11.137 回答