发新话题
打印

[转载]修复磁盘0磁道损坏的基本代码及其详解

[转载]修复磁盘0磁道损坏的基本代码及其详解

信息来源:黑客防线

code segment 'code'
    assume cs:code,ds:code
    org 100h
start:
    jmp begin
msg1 db 'Please Insert a DISK in drive A:',0ah,0dh
    db 'Please any key to continue...',0ah,0dh,'$'
msg2 db 'Format Success!',0ah,0dh,'$'
msg3 db 'Format Failed!',0ah,0dh,'$'
id db 0,0,2,2
  db 0,0,4,2
  db 0,0,6,2
  db 0,0,8,2
  db 0,0,10,2
  db 0,0,12,2
  db 0,0,14,2
  db 0,0,16,2
  db 0,0,18,2
  db 0,0,1,2
  db 0,0,3,2
  db 0,0,5,2
  db 0,0,7,2
  db 0,0,9,2
  db 0,0,11,2
  db 0,0,13,2
  db 0,0,15,2
  db 0,0,17,2;格式化地址控制标识
date db 0eh dup (0);从此处开始的512个字节用于存放引导程序及BPB
reserved dw  0
     db  200h-0eh-2h dup (0)
fat db 0f0h,0ffh,0ffh;从此处开始的512个字节用于存放FAT域
   db 200h-3h dup (0)
bedsector db 18 dup (0);用于记录当前扇区是否是坏扇区
bedsectorsize db 0;用于记录坏扇区的大小
bedsectorhead db 0;用于记录坏扇区头的扇区号
bedsectortail db 0;用于记录坏扇区尾的扇区号
goodsectorsize db 0;用于记录好扇区的大小
sumsectorsize db 0;用于记录总扇区的大小
formattimes db 0;用于记录格式化的次数
changeid db 1;用于记录需更改的是磁道号还是磁头号
inccl db 0;用于记录是否出现计算磁头、磁道、扇区号的特殊情况
reformattimes db 0;用于记录重试次数
push_a_b_c_dx proc near
   push bx
   mov bx,sp
   inc bx
   inc bx
   xchg ax,ss:[bx];交换ax寄存器和返回地址的值
   push cx
   push dx;到此以完成将ax,bx,cx,dx寄存器的内容压入堆栈
   push ax;将返回地址压入堆栈
   ret;返回调用程序
push_a_b_c_dx endp;此子程序用于保存4个通用寄存器
pop_d_c_b_ax proc near
   pop ax;ax的值为返回地址
   pop dx
   pop cx
   mov bx,sp
   inc bx
   inc bx
   xchg ax,ss:[bx];交换原ax寄存器和返回地址的值
   pop bx
   ret
pop_d_c_b_ax endp;此子程序用于还原4个通用寄存器
re_disk proc near
  call push_a_b_c_dx
  xor ax,ax
  xor dx,dx
  int 13h
  call pop_d_c_b_ax
  ret
re_disk endp;此子程序用于复位软盘驱动器
newint13 proc near
  mov [reformattimes],2;将重复次数写入内存
  re_newint13:
  push ax
  int 13h
  jc newint13_error;功能调用出错则跳转到出错例程
  pop ax
  ret;成功则返回
  newint13_error:
  pop ax
  call re_disk
  dec [reformattimes]
  jnz re_newint13;复位软盘驱动器后,检测重复次数是否为0,不是则跳转
  stc;是则置进位标志,以示功能调用不成功,再返回
  ret
newint13 endp
incsector proc near
   call push_a_b_c_dx
   lea bx,id;得到扇区控制地址标识所在的地址
   inc bx
   inc bx;使bx指向扇区控制地址标识中的扇区号部分
   mov cx,18;置循环次数
loop_inc_id_0_0:
inc byte ptr [bx];使扇区号加1
   cmp byte ptr [bx],18
   jbe no_above_18;比较扇区号是否小于等于18,是则跳转
   mov byte ptr [bx],1;不是,则将扇区号置1
   no_above_18:
   add bx,4;得到下一个要修改的扇区号的地址
   loop loop_inc_id_0_0;循环修改扇区号
   call pop_d_c_b_ax
   ret
incsector endp
format_succ proc near
  lea dx,msg2
  mov ah,9
  int 21h
  ret
format_succ endp;显示格式化成功信息
format_fail proc near
  lea dx,msg3
  mov ah,9
  int 21h
  ret
format_fail endp;显示格式化失败信息
writeboot proc near
  mov ax,301h
  lea bx,date
  mov cx,1
  xor dx,dx
  call newint13
  ret;将磁盘引导信息及磁盘参数块信息写入逻辑0扇区即0磁头1磁道1扇区
writeboot endp
writefat_no_ax proc near
  lea bx,fat;根据公式:磁道号=(保留扇区数+1)/36取商
  re_calculation:;磁头号=((保留扇区数+1)/36的余数)/18取商
  push ax;扇区号=((保留扇区数+1)/36的余数)/18取余数
  mov cl,36;保留扇区数由调用程序存放在ax寄存器中
  div cl;当ax中的值为18的倍数时为特例,需单独处理
  mov ch,al;考虑ax=18的情况,磁道号=0    磁头号=1     扇区号=0
  xor al,al;这将导致软盘驱动器找不到相应的扇区而失败
  xchg al,ah;正确值应为,磁道号=0  磁头号=0  扇区号=18
  mov cl,18;要算出正确值,则要处理这种特殊情况
  div cl;可以将ax的值减1,再参加运算,这时可以算出
  mov dh,al;磁道号=0  磁头号=0  扇区号=17,这显然也是错的
  mov cl,ah;为此可以设立一个标志,当扇区号为0时,将标志位置1
  pop ax;仅当标志位为1时将扇区号加1,并将标志位置0
  cmp cl,0
  jne cl_no_equ_0
  dec ax
  mov byte ptr [inccl],1
  jmp re_calculation
  cl_no_equ_0:
  cmp byte ptr [inccl],1
  jne no_inccl_1
  inc cl
  mov byte ptr [inccl],0
  no_inccl_1:
  mov ax,301h
  xor dl,dl
  call newint13
  ret
writefat_no_ax endp;此子程序将写FAT表,但差磁道、磁头、扇区号
writefat proc near
  mov ax,word ptr cs:[reserved]
  inc ax;将保留扇区数加1,得到第一个FAT表的位置
  call writefat_no_ax;调用写FAT表子程序
  jc write_fat_error;出错则退出
  mov ax,word ptr cs:[reserved]
  add ax,10; 将保留扇区数加10,得到第二个FAT表的位置
  call writefat_no_ax;调用写FAT表子程序
  write_fat_error:
  ret
writefat endp  
setreserved proc near
  call push_a_b_c_dx
  lea bx,sumsectorsize
  mov al,byte ptr [bx]
  dec bx;此时bx指向goodsectorsize
  mov ah,byte ptr [bx]
  sub al,ah;根据公式:保留扇区数=格式化扇区总数-好扇区数
  xor ah,ah;计算保留扇区数
  lea bx,reserved
  mov word ptr [bx],ax
  call pop_d_c_b_ax
  ret
setreserved endp
setbedsectorinformation proc near
  call push_a_b_c_dx
  lea bx,bedsector
  xor ax,ax;ax的值用于判断是要修改坏扇区头(0),还是坏扇区尾(1)
  mov cl,1;cl的值用于循环计数及指示当前扇区号
  findbedsector_0_0:
  cmp byte ptr [bx],1;比较坏扇区标识,1表示是坏扇区,0表示不是坏扇区
  jne no_bedsector_0_0
  push bx;保存坏扇区标识地址
  lea bx,bedsectorsize;得到坏扇区大小地址
  inc byte ptr [bx];将坏扇区大小加1
  inc bx;得到坏扇区头地址
  cmp ax,1
  je no_set_bedsectorhead_0_0;ax=0表示设置坏扇区头
  mov ax,1; ax=1表示设置坏扇区尾
  mov byte ptr [bx],cl;设置坏扇区头
  no_set_bedsectorhead_0_0:
  inc bx;得到坏扇区尾地址
  mov byte ptr [bx],cl;设置坏扇区尾
  pop bx
  no_bedsector_0_0:
  inc cl;扇区号及循环标识加1
  inc bx;得到下一个坏扇区地址
  cmp cl,19
  jne findbedsector_0_0;循环设置坏扇区大小及头尾标识
  call pop_d_c_b_ax
  ret
setbedsectorinformation endp
format proc near
  mov ax,501h;格式化软盘,磁头号及磁道号由调用者提供
  lea bx,id
  xor dl,dl
  call newint13
  ret
format endp
testsector proc near
  mov ax,401h;测试指定扇区是否完好,磁道号、磁头号及扇区号由调用者提供
  xor dl,dl
  call newint13
  ret
testsector endp
changeheadortrack proc near
  call push_a_b_c_dx
  lea bx,changeid;得到更改磁道号或磁头号标识,1表示更改磁头号
  mov al,byte ptr [bx];0表示更改磁道号及磁头号
  xor ah,ah
  xor byte ptr [bx],1;标识取反
  mov cx,18;循环修改磁道或磁头号18次
  lea bx,id;得到扇区控制地址标识起始地址
  add bx,ax;ax的值不是0,就是1,加上起始地址后得到要修改的磁道(AX+0)或磁头地址(AX+1)
  loop_change_0_0:
  cmp al,1
  je changehead_0_0;AL为1则修改磁头号
  inc byte ptr [bx]
  xor byte ptr [bx+1],1;修改磁头及磁道号
  jmp loop_start_0_0
  changehead_0_0:
  xor byte ptr [bx],1;修改磁头号
  loop_start_0_0:
  add bx,4;计算得到下一个要修改的地址
  loop loop_change_0_0
  call pop_d_c_b_ax
  ret
changeheadortrack endp
formatandtest proc near
  call format;调用格式化磁道例程
  pushf;保存标志寄存器
  jc format_error_0_0 ;错误则退出
  push cx
  push bx
  mov cx,21;设置初始化坏扇区次数
  lea bx,bedsector
  init_bedsector_0_0:
  mov byte ptr [bx],0;初始化为0
  inc bx;指向下一个要修改的地址
  loop init_bedsector_0_0
  pop bx
  pop cx
  test_next_sector_0_0:
  call testsector;测试扇区
  jnc test_succ_0_0;成功则转移,失败则设置坏扇区信息
  push bx
  lea bx,bedsector
  push cx
  and cx,0ffh;得到测试扇区号
  add bx,cx;得到上次要修改的地址加扇区号
  dec bx;得到本次要修改的地址,减1是因为扇区号从1开始偏移地址由0开始
  mov byte ptr [bx],1;设置此存储单元代表的扇区为坏扇区
  pop cx
  pop bx
  test_succ_0_0:
  inc cl;循环及扇区标识加1
  cmp cl,19
  jne test_next_sector_0_0;循环设置坏扇区
  call setbedsectorinformation;设置坏扇区信息
  call sum_goodsectorsize;计算好扇区的大小
  cmp byte ptr [goodsectorsize],32;比较好扇区数是否大于等于32
  jae goodsecotrsize_jae_32;是则跳转
  add_sumsectorsize_18:
  add byte ptr [sumsectorsize],18;不是则总扇区数加18
  jmp format_error_0_0;完成设置总扇区数
  goodsectorsize_jae_32:
  mov bl,byte ptr [bedsectorhead];是则得到坏扇区头的扇区号
  cmp bl,0;比较是否没有坏扇区
  je add_sumsectorsize_18;是则将总扇区数加18
  dec bl;不是则将坏扇区头的扇区号减1,得到此次格式化所得的好扇区数
  add byte ptr [sumsectorsize],bl;总扇区数加此次所得的好扇区数
  format_error_0_0:
  popf;还原标志寄存器
  ret
formatandtest endp
sum_goodsectorsize proc near
  call push_a_b_c_dx
  mov ah,18
  lea bx,bedsectortail
  mov al,byte ptr [bx];得到坏扇区尾,无坏扇区时为0,有则为扇区号
  mov dl,al;dl为坏扇区号
  sub ah,al;计算得到好扇区数
  lea bx,formattimes
  mov al,byte ptr [bx];得到格式化次数
  lea bx,goodsectorsize
  cmp al,1
  jne formattimes_no_1;格式化次数不为1时跳转,此时不要修改好扇区数
  dec ah;格式化次数为1则好扇区数减1,因为有一个保留扇区
  formattimes_no_1:
  cmp dl,0
  jne have_bedsector_0;坏扇区数不为0则跳转,表示用坏扇区
  add byte ptr [bx],ah;无坏扇区则好扇区数加ah,即加18
  jmp sum_goodsectorsize_end
  have_bedsector_0:
  mov dl,byte ptr [bedsectorhead];有坏扇区则的到坏扇区头的扇区号
  dec dl;坏扇区头减1表示在坏扇区头前拥有的好扇区数
  add byte ptr [bx],dl;原来的好扇区数加上这次得到的好扇区数
  cmp byte ptr [bx],32;比较是否有了32个连续的好扇区
  jae sum_goodsectorsize_end;有则计算结束
  cmp ah,0ffh;没有则比较ah的值是否为0FFh,因为格式化次数为1时好扇区数减了1,倘若没有好扇区,则有可能使ah的值为0FFh
  jne ah_no_equ_0ffh
  inc ah;ah为0ffh时加1
  ah_no_equ_0ffh:
  mov byte ptr [bx],ah;将新的连续的好扇区数存放在BX指示的存储单元
  sum_goodsectorsize_end:
  call pop_d_c_b_ax
  ret
sum_goodsectorsize endp
begin:
  push cs
  push cs
  pop ds
  pop es;使代码段数据段附加段指向同一段
  mov ah,9
  lea dx,msg1
  int 21h;显示提示信息
  mov ah,7
  int 21h;等待用户击键
  push ds
  xor ax,ax
  mov ds,ax
  mov bx,526h
  mov byte ptr [bx],18;设置软盘参数表的每道扇区数为18
  mov byte ptr [bx+4],0;设置格式化填充字节为0
  pop ds
  call re_disk;软驱复位
  mov cx,14;因总扇区数占用1个字节,而每次格式化将产生18个扇区,将256/18取商则可得到循环测试不能超过14
  reformatandtest:
  lea bx,goodsectorsize
  mov byte ptr [bx],0
  inc bx;bx指向sumsectorsize
  mov byte ptr [bx],0
  inc bx;bx指向formattimes
  mov byte ptr [bx],0
  loop_formatandtest:
  push cx
  lea bx,formattimes
  inc byte ptr [bx];格式化次数加1
  lea bx,id;得到格式化控制地址标识的起始地址(磁道号地址)
  mov ch,byte ptr [bx];设置格式化时用到的磁道号
  inc bx;得到格式化控制地址标识中的磁头号地址
  mov dh,byte ptr [bx];设置格式化时用到的磁头号
  mov cl,1;设置测试磁道时用到的磁道号
  call formatandtest;调用格式化及测试磁道子程序
  jc error;格式化出错则跳转
  lea bx,formattimes
  mov ah,byte ptr [bx];得到格式化次数
  lea bx,goodsectorsize
  mov al,byte ptr [bx];得到好扇区的大小
  cmp al,32;比较好扇区大小是否大于等于32(2*FAT+FCT=32)
  jae loop_formatandtest_end;大于等于则循环格式化及测试完成
  lea bx,bedsectorhead
  mov al,byte ptr [bx];得到坏扇区头的扇区号
  push si
  cmp al,1
  pushf;比较扇区号是否为1,并保存标志寄存器
  cmp ah,1
  pushf;比较格式化次数是否为1,并保存标志寄存器
  pop ax;将标志寄存器的值存入ax
  pop si;将标志寄存器的值存入si
  and ax,si;将ax,si进行与操作,主要用于确定标志位Z是否为0
  push ax;保存标志
  popf;还原标志寄存器
  pop si;还原si寄存器
  jne loop_formatandtest1;标志位Z不为0,则继续格式化并测试磁道
  call incsector;为0表示0道1扇区损坏,则扇区号加1
  pop cx;还原格式化计数器
  loop reformatandtest;循环重新格式化0磁头0磁道;请不要使用jmp指令,因为可能会出现18个扇区全部损坏的情况,那样将会陷入死循环
  loop_formatandtest1:
  call changeheadortrack;更改磁头或磁头、磁道号
  pop cx
  loop loop_formatandtest;循环格式化并测试磁道
  jmp error
  loop_formatandtest_end:
  pop cx;平衡堆栈
  call setreserved;计算保留扇区的大小
  call writeboot;写引导扇区
  jc error
  call writefat;写2个FAT表区
  jc error
  call format_succ;显示格式化成功信息
  jmp program_end
  error:
  call format_fail;显示格式化失败信息
  program_end:
  mov ax,4c00h
  int 21h;程序正常退出
code ends
    end start

TOP

发新话题