m0_73544147 2024-01-29 11:23 采纳率: 0%
浏览 6
已结题

x86的引导区与加载区汇编输出有问题

#有偿10元求答案,首先,通过bootloader引导区输出4个字符,再加载LBA1到内存中,跳转,输出4个字符(通过Bochs调试,每个代码都能运行,代码没问题),但只有BootLoader的4个字符能输出到屏幕上,跳转的工作程序不能输出。
!在bochs可以显示全部内容,但在virtualBox上只能显示第一行内容!
!!求解决方法!

img


img


```X86
;代码清单9-1
         ;文件名:c09_mbr.asm
         ;文件说明:硬盘主引导扇区代码(加载程序) 
         ;创建日期:2011-5-5 18:17,修改于2021-10-3 15:35
         
         app_lba_start equ 1           ;声明常数(用户程序起始逻辑扇区号)
                                         ;常数的声明不会占用汇编地址
                                    
SECTION mbr align=16 vstart=0x7c00                                     


         mov ax,0xb800
         mov es,ax
     
         mov byte[es:0x00],'L'
         mov byte[es:0x01],0x07
         mov byte[es:0x02],'W'
         mov byte[es:0x03],0x07

         mov byte[es:0x04],'G'
         mov byte[es:0x05],0x07

         mov byte[es:0x06],'L'
         mov byte[es:0x07],0x07

    

         ;设置堆栈段和栈指针 
         mov ax,0      
         mov ss,ax
         mov sp,ax
      
         mov ax,[cs:phy_base]            ;计算用于加载用户程序的逻辑段地址 
         mov dx,[cs:phy_base+0x02]
         mov bx,16        
         div bx            
         mov ds,ax                       ;令DS和ES指向该段以进行操作
         mov es,ax                        
    
         ;以下读取程序的起始部分 
         xor di,di
         mov si,app_lba_start            ;程序在硬盘上的起始逻辑扇区号 
         xor bx,bx                       ;加载到DS:0x0000处 
         call read_hard_disk_0
      
         ;以下判断整个程序有多大
         mov dx,[2]                      ;曾经把dx写成了ds,花了二十分钟排错 
         mov ax,[0]
         mov bx,512                      ;512字节每扇区
         div bx
         cmp dx,0
         jnz @1                          ;未除尽,因此结果比实际扇区数少1 
         dec ax                          ;已经读了一个扇区,扇区总数减1 
   @1:
         cmp ax,0                        ;考虑实际长度小于等于512个字节的情况 
         jz direct
         
         ;读取剩余的扇区
         push ds                         ;以下要用到并改变DS寄存器 

         mov cx,ax                       ;循环次数(剩余扇区数)
   @2:
         mov ax,ds
         add ax,0x20                     ;得到下一个以512字节为边界的段地址
         mov ds,ax  
                              
         xor bx,bx                       ;每次读时,偏移地址始终为0x0000 
         inc si                          ;下一个逻辑扇区 
         call read_hard_disk_0
         loop @2                         ;循环读,直到读完整个功能程序 

         pop ds                          ;恢复数据段基址到用户程序头部段 
      
         ;计算入口点代码段基址 
   direct:
         mov dx,[0x08]
         mov ax,[0x06]
         call calc_segment_base
         mov [0x06],ax                   ;回填修正后的入口点代码段基址 
      
         ;开始处理段重定位表
         mov cx,[0x0a]                   ;需要重定位的项目数量
         mov bx,0x0c                     ;重定位表首地址
          
 realloc:
         mov dx,[bx+0x02]                ;32位地址的高16位 
         mov ax,[bx]
         call calc_segment_base
         mov [bx],ax                     ;回填段的基址
         add bx,4                        ;下一个重定位项(每项占4个字节) 
         loop realloc 
      
         jmp far [0x04]                  ;转移到用户程序  
 
;-------------------------------------------------------------------------------
read_hard_disk_0:                        ;从硬盘读取一个逻辑扇区
                                         ;输入:DI:SI=起始逻辑扇区号
                                         ;      DS:BX=目标缓冲区地址
         push ax
         push bx
         push cx
         push dx
      
         mov dx,0x1f2
         mov al,1
         out dx,al                       ;读取的扇区数

         inc dx                          ;0x1f3
         mov ax,si
         out dx,al                       ;LBA地址7~0

         inc dx                          ;0x1f4
         mov al,ah
         out dx,al                       ;LBA地址15~8

         inc dx                          ;0x1f5
         mov ax,di
         out dx,al                       ;LBA地址23~16

         inc dx                          ;0x1f6
         mov al,0xe0                     ;LBA28模式,主盘
         or al,ah                        ;LBA地址27~24
         out dx,al

         inc dx                          ;0x1f7
         mov al,0x20                     ;读命令
         out dx,al

  .waits:
         in al,dx
         and al,0x88
         cmp al,0x08
         jnz .waits                      ;不忙,且硬盘已准备好数据传输 

         mov cx,256                      ;总共要读取的字数
         mov dx,0x1f0
  .readw:
         in ax,dx
         mov [bx],ax
         add bx,2
         loop .readw

         pop dx
         pop cx
         pop bx
         pop ax
      
         ret

;-------------------------------------------------------------------------------
calc_segment_base:                       ;计算16位段地址
                                         ;输入:DX:AX=32位物理地址
                                         ;返回:AX=16位段基地址 
         push dx                          
        
         add ax,[cs:phy_base]
         adc dx,[cs:phy_base+0x02]
         shr ax,4
         ror dx,4
         and dx,0xf000
         or ax,dx
         
         pop dx
         
         ret

;-------------------------------------------------------------------------------
         phy_base dd 0x10000             ;用户程序被加载的物理起始地址
         
 times 510-($-$$) db 0
                  db 0x55,0xaa





 ;代码清单9-2
         ;文件名:c09.asm
         ;文件说明:用户程序 
         ;创建日期:2011-5-5 18:17,修改于2021-10-3 15:35
         
;===============================================================================
SECTION header vstart=0                     ;定义用户程序头部段 
    program_length  dd program_end          ;程序总长度[0x00]
    
    ;用户程序入口点
    code_entry      dw start                ;偏移地址[0x04]
                    dd section.code_1.start ;段地址[0x06] 
    
    realloc_tbl_len dw (header_end-code_1_segment)/4
                                            ;段重定位表项个数[0x0a]
    
    ;段重定位表           
    code_1_segment  dd section.code_1.start ;[0x0c]
    code_2_segment  dd section.code_2.start ;[0x10]
    data_1_segment  dd section.data_1.start ;[0x14]
    data_2_segment  dd section.data_2.start ;[0x18]
    stack_segment   dd section.stack.start  ;[0x1c]
    
    header_end:                
    
;===============================================================================
SECTION code_1 align=16 vstart=0         ;定义代码段1(16字节对齐) 

  start:
       
         
       
         mov ax,0xb800
         mov es,ax
     

       
    
         mov byte[es:0x08],'L'
         mov byte[es:0x09],0x07
         mov byte[es:0x0a],'L'
         mov byte[es:0x0b],0x07

         mov byte[es:0x0c],'L'
         mov byte[es:0x0d],0x07

         mov byte[es:0x0e],'L'
         mov byte[es:0x0f],0x07

         jmp $ 

     
;===========================================================================
SECTION code_2 align=16 vstart=0          ;定义代码段2(16字节对齐)

 
         
;===============================================================================
SECTION data_1 align=16 vstart=0

    msg0 db 'This is NASM '
         db 0
        

;===============================================================================
SECTION data_2 align=16 vstart=0

    msg1 db '  The above contents is written by LeeChung. '
         db '2011-05-06'
         db 0

;===============================================================================
SECTION stack align=16 vstart=0
           
         resb 256

stack_end:  

;===============================================================================
SECTION trail align=16
program_end:



```x86


  • 写回答

13条回答 默认 最新

  • 专家-赤兔[在线] 优质创作者: 编程框架技术领域 2024-01-29 11:29
    关注

    引自免费微信小程序:皆我百晓生

    根据您提供的信息,问题在于尽管您能够成功通过BootLoader在屏幕上输出字符,并且也加载了LBA1扇区的内容到内存中,但是在跳转到用户程序后无法继续输出字符。从图1和图2以及代码清单9-1与代码清单9-2来看,以下是我对问题的分析:

    首先,在BootLoader部分(即代码清单9-1),您成功地设置了显示缓冲区地址并输出了字符'LWGL',这部分工作正常。

    然后,BootLoader读取了用户程序的第一扇区到内存,并尝试进行重定位以正确执行用户程序。但是,用户程序中的字符输出并未如预期那样出现在屏幕上。

    为了找出问题所在,我们需要检查几个关键点:

    1. 用户程序是否被正确加载:从您的描述看,Bochs调试器显示的内存区域内容没有明显错误,这表明LBA1扇区的内容已经被正确加载到了内存中。

    2. 用户程序入口点设置:在引导加载程序中,使用jmp far [0x04]跳转到了用户程序的入口点。这里的关键是确保入口点地址计算无误,且目标地址处确实包含了有效的可执行指令。

    3. 用户程序自身的问题

      • 在用户程序(代码清单9-2)中,起始部分试图在0xb800:0x08开始的位置输出'LLLL',但这段代码没有完成任务切换到图形模式或更新光标位置,所以可能由于显示位置的原因导致看不到输出。
      • 用户程序的start标签后面没有实际的执行流来继续执行输出操作,而是直接进入了一个无限循环jmp $。这意味着即使前面的字符输出指令有效,它们也不会被执行到。

    解决办法可能是:

    • 确保用户程序中有正确的跳转逻辑,例如跳过初始化代码段而直接执行后续的显示输出代码。
    • 检查用户程序中用于控制显示的指令是否正确,比如需要更新光标位置以便新输出的字符能在屏幕可见区域内。
    • 如果用户程序有依赖于特定硬件环境的设置,比如显存模式、中断向量表重置等,需要确保这些设置也在引导加载程序或者用户程序启动时完成。

    针对上述分析,请您检查用户程序(c09.asm)中的start标签之后的逻辑,修改为包含能正确执行字符输出的代码块,并移除不必要的无限循环,以确保程序能够按预期执行。

    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 1月30日
  • 修改了问题 1月29日
  • 赞助了问题酬金15元 1月29日
  • 修改了问题 1月29日
  • 展开全部