MICE-5103, 萬利出品的 8051 EMULATOR, Divide Overflow 的原因


短網址    http://wp.me/ph3BR-Lj

.

.

nEO_IMG_understand why Div overflow_xlsx

REVIEW : http://wp.me/ph3BR-KM

MICE-5103, 萬利出品的 8051 EMULATOR, 解決 Divide Overflow

PC 的 CPU 速度隨著產品進步增加, 這個例子不管是有意製造, 還是無心遇上, 基本上就是2000年那種 [電腦千年蟲] 製造出來的大問題.

為什麼呢, 俺也是看完這段 Reverse Engineering 程序 (把 MBUG.EXE 反編譯後的程序), 讀了一些基本的 PC 架構和歷史資料, 綜合起來的結論.

下面理解一下俺讀它的意義, 集中精神, 因為 divide overflow 在 xxxx:0920 那行引致, 所以把這整段 SUB-ROUTINE 完整地解讀一次, 得到了下面這些帶有註解的ASM源碼. 中間也搜尋了很多 GOOGLE 上提供的資料.


seg002:08FD             ; =============== S U B R O U T I N E =======================================
seg002:08FD
seg002:08FD
seg002:08FD             read_ticks_bios_timer_area_and_count proc near
seg002:08FD                                                     ; CODE XREF: sub_2A4_75C:loc_2A4_799p
seg002:08FD B8 40 00                    mov     ax, 40h ; '@'
seg002:0900 8E D8                       mov     ds, ax          ; DS = 40h
seg002:0902                             assume ds:nothing
seg002:0902 BB 6C 00                    mov     bx, 6Ch ; 'l'   ; BX = 6C
seg002:0902                                                     ; 40:6C dword Daily timer counter,
seg002:0902                                                     ; equal to zero at midnight;
seg002:0902                                                     ; incremented by INT 8; read/set by INT 1A
seg002:0902                                                     ; ;---------------[Timer data area]---------------;
seg002:0902                                                     ;         dw      ?               ; 40:6C         ; Ticks since midnite (lo)
seg002:0902                                                     ;         dw      ?               ; 40:6E         ; Ticks since midnite (hi)
seg002:0902                                                     ;         db      ?               ; 40:70         ; Non-zero if new day
seg002:0902                                                     ;
seg002:0902                                                     ;
seg002:0902                                                     ; Read more: http://www.intel-assembler.it/portale/5/8088-bios-source-code-masm/8088-bios-source-code-masm.asp#ixzz2Hpwey0dE
seg002:0902                                                     ;
seg002:0902                                                     ; Read more: http://biosengineer.blogspot.tw/2008/05/bios-data-area.html
seg002:0902                                                     ;
seg002:0902                                                     ; 18.20648 ticks per second
seg002:0902                                                     ;
seg002:0902                                                     ; http://webpages.charter.net/danrollins/techhelp/0093.HTM
seg002:0902                                                     ;
seg002:0902                                                     ; 40:006c   4  Timer tick counter (count of 55ms ticks since CPU reset)
seg002:0902                                                     ;
seg002:0905 33 C0                       xor     ax, ax          ; DX:AX = 0, init CONUTER (divider / 40h)
seg002:0907 33 D2                       xor     dx, dx
seg002:0909
seg002:0909             timer_tick_count1:                      ; load time stamp 1
seg002:0909 8A 2F                       mov     ch, [bx]
seg002:090B
seg002:090B             wait_next_tick_start:                   ; CODE XREF: read_ticks_bios_timer_area_and_count+12j
seg002:090B 8A 0F                       mov     cl, [bx]        ; load 40:6c
seg002:090D 32 CD                       xor     cl, ch          ; CH = CL, xor CL,CH will be 0
seg002:090F 74 FA                       jz      short wait_next_tick_start ; load 40:6c
seg002:0911
seg002:0911             counts_for_timer_256ticks:
seg002:0911 8A 2F                       mov     ch, [bx]
seg002:0913
seg002:0913             count_CPU_cycles:                       ; CODE XREF: read_ticks_bios_timer_area_and_count+1Ej
seg002:0913 40                          inc     ax
seg002:0914 75 01                       jnz     short timer_tick_count4
seg002:0916 42                          inc     dx              ; AX count from 0 to FFFF, over flow here
seg002:0917
seg002:0917             timer_tick_count4:                      ; CODE XREF: read_ticks_bios_timer_area_and_count+17j
seg002:0917 8A 0F                       mov     cl, [bx]
seg002:0919 32 CD                       xor     cl, ch          ; when ch = cl, 256 ticks done
seg002:091B 74 F6                       jz      short count_CPU_cycles
seg002:091D
seg002:091D             ticks_count_256_done:                   ; DX:AX = total count of
seg002:091D B9 40 00                    mov     cx, 40h ; '@'   ; AX = DX:AX / 40, quotient
seg002:091D                                                     ; DX = DX:AX % 40, remainder
seg002:0920 F7 F1                       div     cx              ; divide overflow bug if 486 above
seg002:0920                                                     ;
seg002:0920                                                     ; because once DX:AX is greater than 0x3FFFFF,
seg002:0920                                                     ;
seg002:0920                                                     ; for example,
seg002:0920                                                     ;
seg002:0920                                                     ; 0x400000 / 0x40
seg002:0920                                                     ; quotient = 0x10000, AX is only capable for 0xFFFF, overflow error
seg002:0920                                                     ; remainder = 0
seg002:0920                                                     ;
seg002:0920                                                     ; 0x3FFFFF / 0x40
seg002:0920                                                     ; quotient = 0xFFFF, AX is good for 0xFFFF
seg002:0920                                                     ; remainder = 0x3F
seg002:0920                                                     ;
seg002:0920                                                     ;
seg002:0922 0E                          push    cs
seg002:0923 1F                          pop     ds
seg002:0924                             assume ds:seg002
seg002:0924 A3 51 07                    mov     counts_per_timer_256ticks, ax
seg002:0927 C3                          retn
seg002:0927             read_ticks_bios_timer_area_and_count endp

.

.
.
.

簡單蓋括上面的程序到底做啥,
1) 地址 40:6c (等同0:46c), 共4 BYTE, 是 ROM BIOS 的資料區範圍, PC 的計時器定期把這個地址內的資料增加1, 約每秒 18次, 時間間隔約 55ms, 暫且稱為 [碼表]

2) 這段程序讀取 [碼表], 第 [0秒] 啟動計數器 (DX:AX), 重複 255 次, 共耗時 256 / 18.20648  = 14 sec (?? 不太確定 ??)

3) 計數器 (DX:AX) 有總數 14 sec 內 CPU 計數總次數

4) 計數器 (DX:AX) 總數 除以 64, 商數存入 [速度系數], 結束.

慢速的 CPU 例如i486DX33, 計數器 (DX:AX) 總數會比較小, 相反, 高速的 CPU 例如i686, 計數器 (DX:AX) 總數會很大

一旦 計數器 (DX:AX) 總數 大於 (0x003F:0xFFFF ,  0x3FFFFF, 4194303), 則 除以 64 後, 商數大於 0xFFFF, 出現 divide overflow.

這就是問題的成因.
.
.

下面是 ASM

;
; +-------------------------------------------------------------------------+
; |   This file    has been generated by The Interactive Disassembler (IDA)    |
; |       Copyright (c) 2009 by Hex-Rays, <support@hex-rays.com>        |
; |             License info: xx-xxxx-xxxx-xx                |
; |                 Licensed xiaolaba                    |
; +-------------------------------------------------------------------------+
;

; =============== S U B    R O U T    I N E =======================================

read_ticks_bios_timer_area_and_count proc near ; CODE XREF: sub_2A4_75C:loc_2A4_799p
mov    ax, 40h    ; '@'
mov    ds, ax        ; DS = 40h
assume ds:nothing
mov    bx, 6Ch    ; 'l'   ; BX = 6C
; 40:6C    dword Daily timer counter,
; equal    to zero    at midnight;
; incremented by INT 8;    read/set by INT    1A
; ;---------------[Timer data area]---------------;
;      dw      ?          ; 40:6C      ; Ticks since    midnite    (lo)
;      dw      ?          ; 40:6E      ; Ticks since    midnite    (hi)
;      db      ?          ; 40:70      ; Non-zero if    new day
;
;
; Read more: http://www.intel-assembler.it/portale/5/8088-bios-source-code-masm/8088-bios-source-code-masm.asp#ixzz2Hpwey0dE
;
; Read more: http://biosengineer.blogspot.tw/2008/05/bios-data-area.html
;
; 18.20648 ticks per second
;
; http://webpages.charter.net/danrollins/techhelp/0093.HTM
;
; 40:006c   4  Timer tick counter (count of 55ms ticks since CPU reset)
;
xor    ax, ax        ; DX:AX    = 0, init CONUTER (divider / 40h)
xor    dx, dx

timer_tick_count1:            ; load time stamp 1
mov    ch, [bx]

wait_next_tick_start:            ; CODE XREF: read_ticks_bios_timer_area_and_count+12j
mov    cl, [bx]    ; load 40:6c
xor    cl, ch        ; CH = CL, xor CL,CH will be 0
jz    short wait_next_tick_start ; load 40:6c

counts_for_timer_256ticks:
mov    ch, [bx]

count_CPU_cycles:            ; CODE XREF: read_ticks_bios_timer_area_and_count+1Ej
inc    ax
jnz    short timer_tick_count4
inc    dx        ; AX count from    0 to FFFF, over    flow here

timer_tick_count4:            ; CODE XREF: read_ticks_bios_timer_area_and_count+17j
mov    cl, [bx]
xor    cl, ch        ; when ch = cl,    256 ticks done
jz    short count_CPU_cycles

ticks_count_256_done:            ; DX:AX    = total    count of
mov    cx, 40h    ; '@'   ; AX = DX:AX / 40, quotient
; DX = DX:AX % 40, remainder
div    cx        ; divide overflow bug if 486 above
;
; because once DX:AX is    greater    than 0x3FFFFF,
;
; for example,
;
; 0x400000 / 0x40
; quotient = 0x10000, AX is only capable for 0xFFFF, overflow error
; remainder = 0
;
; 0x3FFFFF / 0x40
; quotient = 0xFFFF, AX    is good    for 0xFFFF
; remainder = 0x3F
;
;
push    cs
pop    ds
assume ds:seg002
mov    counts_per_timer_256ticks, ax
retn
read_ticks_bios_timer_area_and_count endp

.
.
.
.
.
到底 0:46c 或者 40:6c 的時間資料如何被 PC 操控的呢, 讀完這個, 看起來多方面的資料都是吻合的解釋, 如下,

http://www.ousob.com/ng/asm/ng8dc91.php

INT 1Ah,  00h (0)        Read System-Timer Time Counter                   all

    Reports the current time of day, and whether 24 hours has passed since
    1) the last power-on, 2) the last system reset, or 3) the last system-
    timer time read or set.

       On entry:      AH         00h

       Returns:       CX         High-order part of clock count
                      DX         Low-order part of clock count
                      AL         0 if 24 hours has not passed; else 1

  --------------------------------------------------------------------------

       Notes:         The following formulas convert the clock count to
                      the time of day:

                                 Hour      = Clock / 65543 (1007h)
                                 Remainder = Clock MOD 65543

                                 Minutes   = Remainder / 1092 (444h)
                                 Remainder = Remainder MOD 1092

                                 Second    = Remainder / 18.21
                                 Remainder = Remainder MOD 18.21

                                 Hundredths = CINT(Remainder * 100)

                      The "system timer" (as distinguished from the real-
                      time clock) is the timer that's set when the system
                      is started. This time is temporary, lasting only as
                      long as the system is turned on.

                      The clock count may also be read as a 4-byte integer
                      at memory location 0:046C. This 4-byte value is
                      equal to the 4-byte integer in CX:DX after Service
                      00h has been called.

                      After the call, the flag (at 0:0470h) stating
                      whether 24 hours has passed or not, is cleared.

                      When TIME is typed at the command line, DOS gets the
                      time by means of this service.

                      Counts occur at the rate of 18.2 per second.
廣告

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s