使用中断在汇编中。

4
我被要求使用中断来重写下面的代码 (完整说明)。我们在课堂上实际上没有涉及到中断,我也不太确定它们是如何使用或如何实现的。这是我编写的没有使用中断的代码,基本上将键盘变成了一个钢琴或可以演奏一个歌曲(lamb,chromsc,hall),该代码是为motorola 68hc11编写的。
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  ;;;;;;;;;;

请问是否能给我一个有效实现的例子或者提示如何开始编写中断,这样我就可以用中断重写我的代码。


至少在典型情况下,中断仅是一个间接的函数调用。如果不知道所涉及的操作系统(或其他内容),就无法说更多。例如,您通常通过int 21h调用MS-DOS函数。在相同的处理器上,您将通过int 80h调用Linux。对于每个操作系统,您还将在不同的寄存器中提供不同的参数。 - Jerry Coffin
我们使用为我们学校编写的专有软件将代码编译到板子上。处理器是Motorola 68HC11,我链接了一个指令集,其中包含命令和寄存器信息。如果你说的是板子有操作系统,我认为它没有,因此要编写汇编语言。如果你问的是计算机操作系统,那就是Linux。 - Nick
1
不要轮询用户输入(无限循环直到“RDRF”表示数据已准备好),而是设置您的串行控制器(使用其控制寄存器)在获取数据时引发中断。然后在您的ISR中读取数据。然后在您的ISR中对数据进行操作。有很多内容需要涵盖,其中许多内容都是针对您的自定义硬件的,因此请向您的老师寻求帮助。 - indiv
2个回答

5
要在hc11上使用中断,你需要完成以下四个步骤:
1. 首先确定你想要使用哪种类型的中断。由于hc11有许多不同类型的中断,所以你需要查阅手册,找到适合你需求的类型。
2. 接下来我将假设你正在使用IRQ中断。它只是一个简单的中断引脚,当其电压降低时会打断系统。但对于任何其他中断,这些步骤都非常相似。
  1. You need to initialize the start location of the interrupt service routine. When the interrupt is triggered the processor will check a vector table to figure out where it needs to go. So for example when IRQ is triggered it will go to the table entry corresponding to IRQ ($FFF2) and then jump to the address stored there. We'll label the interrupt service routine IRQ_INT and store it in the table entry corresponding to IRQ so that when IRQ is triggered it will start executing the code at the label IRQ_INT.

    ORG   $FFF2
    FDB   IRQ_INT
    
  2. Next you need to enable the interrupt so that the processor will recognize it when it is triggered. Once again you will have to check the manual on what value and which register you have to store the value in to enable it. To enable IRQ the IRQ enable bit (IRQEN) must be set to 1 in the interrupt control register (INTCR). Then it can be enabled with the CLI command.

    INTCR    EQU   $001E   ;address of interrupt control register
    INTCR_IN EQU   $60     ;sets IRQEN bits
    
             LDAA  #INTCR_IN
             STAA  INTCR
             CLI
    
  3. Lastly you need to write your interrupt service routine. This is the code that will be executed every time the interrupt is triggered. It will start at the label we set earlier in the vector table and end when it hits the instruction RTI. The return from interrupt instruction tells the processor that the service routine is finished and it should return to the the whatever it was executing before the interrupt was triggered.

    IRQ_INT
           <instructions go here>
           RTI
    

我猜测您需要在键盘端口上设置某种中断,以便在按下键时触发。一旦按下键,中断将触发处理器进入中断服务例程。在那里,您需要编写代码来确定哪个键被按下,并播放正确的音符。


2
您需要编写两个ISR,即基本的子程序。每个子程序都似乎是由计时器触发的硬件中断。
其中一个ISR是切换扬声器音调并触发自身(开/关),从而生成N个周期的音调。首先要熟悉如何编程计时器TOC3。
另一个是定时器int循环,每1毫秒查找输入并监视RDRF标志,并通过之前的某个项目读取数据。要熟悉如何编程TOC2。
您需要初始化ISR,这意味着您必须设置CPU以告诉它ISR例程的位置。您的说明已经指示了如何为TOC3执行此操作,并且应该类似于TOC2,请阅读手册以查看toc2的jmp表在哪里,他已经给出了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中断例程 YourTOC3Isr,播放音调?

  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中断程序

保存寄存器和标志位

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

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

恢复寄存器和标志位 RTI指令

希望这有所帮助, M


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接