You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

修复后程序流程

  1. 初始化硬件与LCD,打印初始信息
  2. 进入PLAY循环,按INDEX指向的音调参数生成方波
  3. 每个音调循环指定次数后,更新INDEX切换到下一个音调
  4. 当INDEX指向JH时,自动进入HLT循环停止程序

内容的提问来源于stack exchange,提问作者ThatRandomOctoling

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 18:45:55