汇编炸弹人游戏中lda b,x指令与调试器结果不符问题排查
lda b,x指令异常问题排查 问题描述
我正在用汇编语言开发一款炸弹人风格的游戏,地图由*、空格和b组成。使用player1和player2两个字节型变量存储玩家当前位置(最大值为250)。输入有效指令时,对应移动函数会将玩家位置加载到寄存器b,根据移动方向增减数值,并在当前位置留下痕迹(玩家1为.,玩家2为,),但该功能无法正常工作。
调用移动验证逻辑时,寄存器x存储地图起始地址,寄存器b存储玩家位置,但lda b,x指令有时会返回地图文本中不存在的ASCII码。奇怪的是,手动计算寄存器b与x的地址和,并用调试器查看该内存地址时,显示的却是正确字符。
核心代码
.area PROG (ABS) keyboard .equ 0xFF02 screen .equ 0xFF00 b1: .byte 57 b2: .byte 70 b3: .byte 141 b4: .byte 155 b5: .byte 216 m: .byte 19 player1: .byte 96 player2: .byte 80 statep1: .byte 1 ;0-exploted, 1-playing, 2-goal statep2: .byte 1 turn: .byte 0 ;0-P1, 1-P2 statemap: .byte 0 ;0-no_bombs, 1-w_bombs .org 0x100 .globl begin menu: .ascii "\n\n\n\n\n----------------------------------------------" .ascii "\n- Bomberman -" .ascii "\n----------------------------------------------" .ascii "\nModo de juego: player 1- W,A,S,D " .ascii "\n player 2- I,K,J,L " .ascii "\n " .ascii "\To exit the program press 'p' at any" .ascii "\nmoment. " .ascii "\nTo show the hidden bombs press 'b' at " .ascii "\any moment. " .ascii "\n----------------------------------------------" .asciz "\n to begin press 'b':" goal1: .asciz "\n\33[31mplayer 1\33[37m: goal!" goal2: .asciz "\n\33[34mplayer 2\33[37m: goal!" playing1: .asciz "\n\33[31mplayer 1\33[37m: playing..." playing2: .asciz "\n\33[34mplayer 2\33[37m: playing..." exploted1: .asciz "\n\33[31mplayer 1\33[37m: exploted" exploted2: .asciz "\n\33[34mplayer 2\33[37m: exploted!" t1: .asciz "\nturn \33[31mplayer 1\33[37m: (W,A,S,D...b...p): " t2: .asciz "\nturn \33[34mplayer 2\33[37m: (I,J,K,L...b...p): " map : .ascii "\n******************M*****" .ascii "\n* *" .ascii "\n* b b *" .ascii "\n* *" .ascii "\n* *" .ascii "\n* b *" .ascii "\n* b *" .ascii "\n* *" .ascii "\n* b *" .asciz "\n************************" wins1: .asciz "\nwins PLAYER 1!" wins2: .asciz "\nwins PLAYER 2!" begin: ldu 0xf000 lbsr read_menu program: clra clrb lbsr siguiente_turn lbsr validate_input lbsr pmap finish: clra sta 0xFF01 read_menu: ldx #menu lbsr loop lda keyboard cmpa #'b lbeq begin_program cmpa #'p lbeq finish1 bra begin begin_program: clra lda #'0 sta statemap lbsr pmap clra rts finish1: clra sta 0xFF01 exit1: clra bra program pmap: clra clrb ldx #map lda statemap cmpa #'0 beq no_bombs cmpa #'1 beq w_bombs w_bombs: lda ,x+ beq exit1 cmpb player1 beq is_1 cmpb player2 beq is_2 sta screen incb bra w_bombs no_bombs: lda ,x+ beq exit1 cmpb player1 beq is_1 cmpb player2 beq is_2 cmpa #'b beq is_b sta screen incb bra no_bombs is_1: lda #'1 sta screen incb clra lda statemap cmpa #'0 beq no_bombs cmpa #'1 beq w_bombs is_2: lda #'2 sta screen incb clra lda statemap cmpa #'0 beq no_bombs cmpa #'1 beq w_bombs is_b: lda #' sta screen incb bra no_bombs siguiente_turn: clra lda turn cmpa #'0 lbeq p1 cmpa #'1 lbeq p2 p1: lbsr imprimir_state ldx #t1 lbsr loop rts p2: lbsr imprimir_state ldx #t2 lbsr loop rts imprimir_state: lbsr pstate1 lbsr pstate2 rts pstate1: clrb ldb statep1 cmpb #0 lbeq e1 cmpb #1 lbeq j1 cmpb #2 lbeq m1 clrb pstate2: ldb statep2 cmpb #0 lbeq e2 cmpb #1 lbeq j2 cmpb #2 lbeq m2 clrb e1: ldx #exploted1 lbsr loop rts e2: ldx #exploted2 lbsr loop rts j1: ldx #playing1 lbsr loop rts j2: ldx #playing2 lbsr loop rts m1: ldx #goal1 lbsr loop rts m2: ldx #goal2 lbsr loop rts validate_input: lda keyboard ldb turn ldx #map cmpb #'0 lbeq validate_input_p1 cmpb #'1 lbeq validate_input_p2 clrb validate_input_p1: cmpa #'w lbeq move_up_p1 cmpa #'a lbeq move_left_p1 cmpa #'s lbeq move_down_p1 cmpa #'d lbeq move_right_p1 cmpa #'b lbeq changeb cmpa #'p lbeq finish2 lbsr program rts validate_input_p2: cmpa #'i lbeq move_up_p2 cmpa #'j lbeq move_left_p2 cmpa #'k lbeq move_down_p2 cmpa #'l lbeq move_right_p2 cmpa #'b lbeq changeb cmpa #'p lbeq finish2 lbsr program rts changeb: clra lda statemap cmpa #'0 lbeq change1 cmpa #'1 lbeq change0 change1: clra ldb #'1 stb statemap lbsr pmap clrb rts change0: clra ldb #'0 stb statemap lbsr pmap clrb rts exit_validate1: clra lda #'1 sta turn clra rts exit_validate2: clra lda #'0 sta turn clra rts finish2: clra sta 0xFF01 move_up_p1: ldb player1 lda #'. sta b,x ldb player1 subb #25 lbsr validate_pos stb player1 lbsr exit_validate1 rts move_left_p1: lda #'. sta b,x ldb player1 decb lbsr validate_pos stb player1 lbsr exit_validate1 rts move_right_p1: lda #'. sta b,x ldb player1 incb lbsr validate_pos stb player1 jsr exit_validate1 rts move_down_p1: lda #'. sta b,x ldb player1 addb #25 lbsr validate_pos stb player1 lbsr exit_validate1 rts move_right_p2: lda #', sta b,x ldb player2 incb lbsr validate_pos stb player2 lbsr exit_validate2 rts move_left_p2: lda #', sta b,x ldb player2 decb lbsr validate_pos stb player2 lbsr exit_validate2 rts move_up_p2: lda #', sta b,x ldb player2 subb #25 lbsr validate_pos stb player2 lbsr exit_validate2 rts move_down_p2: lda #', sta b,x ldb player2 addb #25 lbsr validate_pos stb player2 lbsr exit_validate2 rts validate_pos: lda b,x cmpb #225 lbhs program cmpb #25 lbls program cmpa #'* lbeq program cmpa #'1 lbeq program cmpa #'2 lbeq program cmpa #'M lbeq state_goal cmpa #'b lbeq state_exploted rts state_exploted: ldb #0 lda turn cmpa #1 lbeq goal_2 stb statep1 lbra exit_validate1 exploted_2: sta statep2 lbra exit_validate2 state_goal: ldb #2 lda turn cmpa #1 lbeq goal_2 stb statep1 lbra exit_validate1 goal_2: sta statep2 lbra exit_validate2 loop: lda ,x+ lbeq exit sta screen bra loop exit: rts .org 0xFFFE ;RESET .word begin
问题原因分析
寄存器B的赋值顺序错误
部分移动函数(如move_left_p1、move_right_p2等)中,执行sta b,x写入玩家痕迹时,B寄存器还未被加载为当前玩家位置,此时B的值是上一次操作残留的垃圾值,导致写入错误内存地址,同时可能干扰后续验证逻辑的寄存器状态。正确顺序应先加载玩家位置到B,再执行写入操作。变址寻址的符号扩展问题
如果使用的是Motorola 6809架构,lda b,x属于8位变址寻址,会将B寄存器的值作为有符号数进行16位扩展。当玩家位置(B的值)大于127时,符号扩展后会变成负数,导致实际计算的地址为X + 0xFFxx,和手动计算的无符号地址X + B完全不符,从而读取到地图外的内存数据。验证逻辑顺序颠倒
validate_pos中先执行lda b,x读取地图内容,再检查B的值是否越界(cmpb #225、cmpb #25)。若B的值已超出地图有效范围,lda b,x会访问地图外的内存,返回非地图内的字符。应先校验位置合法性,再执行读取操作。地图长度与位置计算的匹配问题
上下移动时按加减25计算行偏移,需确保地图每行恰好25个字符(包括换行符)。另外,.asciz会在地图末尾添加一个0字节,需确认玩家位置的最大值250是否在地图的有效字节范围内,避免访问到末尾的0或地图外区域。
内容的提问来源于stack exchange,提问作者Gonzalo Martín

