6502汇编bne指令异常:方波发生器无法切换频率陷入循环
问题分析与修复方案
你的6502方波发生器程序卡在PLAY_TONE循环无法切换频率,核心是三个致命逻辑错误,以下是具体问题和修复方案:
核心问题1:INDEX地址存储与跳转错误
你错误地在INDEX中存储了jmp指令的操作码($4c),而非目标地址;同时使用了绝对跳转jmp INDEX(跳转到INDEX的内存位置),而非间接跳转(跳转到INDEX存储的地址)。
修复:
- 将
INDEX改为2字节(存储16位地址) - 初始化
INDEX为T1的正确地址(低字节在前,高字节在后) - 使用6502间接跳转指令
jmp (INDEX)跳转到INDEX指向的音调参数段
核心问题2:PLAY_TONE循环无限执行
你初始化Y为#$ff,当Y递减到0时,dey会将Y设为$ff(6502无符号减法特性),导致bne PLAY_TONE永远成立,循环无法退出到INC_INDEX。
修复:
- 给
Y设置有限初始值(比如#8,控制每个音调的持续循环次数) - 修改循环退出逻辑,当
Y减到0时直接跳转到INC_INDEX
核心问题3:缺少HLT触发逻辑
程序没有判断INDEX是否到达JH(跳转至HLT的入口),导致即使循环退出,也会无限切换频率。
修复:
- 在
INC_INDEX后检查INDEX是否指向JH的地址($c03c),如果是则直接进入HLT循环
修复后的完整代码
PORTA = $6001 PORTB = $6000 DDRA = $6003 DDRB = $6002 TONE = $0200 ;1 byte TEMPX = $0201 ;1 byte TEMPY = $0202 ;1 byte INDEX = $0203 ;2字节,存储16位跳转地址 CLEAR = %00000001 E = %10000000 RW = %01000000 RS = %00100000 .org $c000 MUSIC: T1: ldy #12 ldx #75 jmp WAIT_LOOP T2: ldy #8 ldx #5 jmp WAIT_LOOP T3: ldy #6 ldx #37 jmp WAIT_LOOP T4: ldy #5 ldx #1 jmp WAIT_LOOP T5: ldy #4 ldx #25 jmp WAIT_LOOP T6: ldy #3 ldx #64 jmp WAIT_LOOP T7: ldy #3 ldx #18 jmp WAIT_LOOP T8: ldy #2 ldx #83 jmp WAIT_LOOP T9: ldy #2 ldx #55 jmp WAIT_LOOP JH: jmp HLT .org $8000 RESET: ;程序入口 ldx #$ff txs cli lda #%11111111 ;设置端口B为全输出 sta DDRB lda #%11100001 ;设置端口A的第0、5、6、7位为输出 sta DDRA lda #%00111000 ;初始化LCD:8位模式,5x8字体,2行 jsr LCD_INSTRUCTION lda #%00001110 ;LCD开显示,显示光标 jsr LCD_INSTRUCTION lda #%00000110 ;光标右移,屏幕不滚动 jsr LCD_INSTRUCTION jsr LCD_CLEAR ;初始化INDEX为T1的地址$c000(低字节在前) lda #$00 sta INDEX lda #$c0 sta INDEX +1 lda #%01010101 sta TONE ldx #0 PRINT_MESSAGE: lda MESSAGE,x beq PLAY jsr PRINT_CHAR inx jmp PRINT_MESSAGE PLAY: ldy #8 ;每个音调持续8次循环,可按需调整 ldx #$ff ;每次循环内的音调翻转次数 PLAY_TONE: sty TEMPY stx TEMPX ;输出方波的一位 lda TONE and #%00000001 sta PORTA ;翻转TONE寄存器,准备下一次输出 lda TONE eor #$ff sta TONE ;等待指定时长 jsr WAIT ;恢复循环计数 ldy TEMPY ldx TEMPX dex bne PLAY_TONE ;X未减到0,继续循环 dey beq INC_INDEX ;Y减到0,切换频率 bne PLAY_TONE INC_INDEX: clc lda INDEX ;低字节加7 adc #7 sta INDEX lda INDEX +1 ;高字节处理进位 adc #0 sta INDEX +1 ;检查是否到达JH($c03c) lda INDEX cmp #$3c bne PLAY lda INDEX +1 cmp #$c0 beq HLT ;到达JH,进入停止循环 jmp PLAY HLT: jmp HLT MESSAGE: .asciiz "Frequency: var Amplitude: 5V " WAIT: jmp (INDEX) ;间接跳转到INDEX存储的地址 WAIT_LOOP: dex bne WAIT_LOOP dey bne WAIT_LOOP rts LCD_WAIT: pha lda #%00000000 ;设置端口B为全输入 sta DDRB LCD_BUSY: lda #RW sta PORTA lda #(RW|E) sta PORTA lda PORTB and #%10000000 ;仅检查忙标志 bne LCD_BUSY lda #RW sta PORTA lda #%11111111 ;恢复端口B为全输出 sta DDRB pla rts LCD_CLEAR: lda #CLEAR ;清屏 jsr LCD_INSTRUCTION LCD_INSTRUCTION: jsr LCD_WAIT sta PORTB lda #%0 ;清除RS/RW/E位 sta PORTA lda #E ;使能LCD接收指令 sta PORTA lda #%0 ;清除使能位 sta PORTA rts PRINT_CHAR: jsr LCD_WAIT sta PORTB lda #RS ;设置RS位,清除RW/E位 sta PORTA lda #(RS|E) ;使能LCD接收字符 sta PORTA lda #RS ;清除使能位 sta PORTA rts NMI: rti BRK_IRQ: rti EXIT_INT: rti VECTORS: .org $fffa .word NMI .org $fffc .word RESET .org $fffe .word BRK_IRQ
修复后程序流程
- 初始化硬件与LCD,打印初始信息
- 进入
PLAY循环,按INDEX指向的音调参数生成方波 - 每个音调循环指定次数后,更新
INDEX切换到下一个音调 - 当
INDEX指向JH时,自动进入HLT循环停止程序
内容的提问来源于stack exchange,提问作者ThatRandomOctoling
相关产品推荐
相关产品推荐

