4

我正在开发一个汇编程序来获取系统时间和日期,将其转换为 ASCII,并将其显示在监视器上。我无法让它正确显示,并且找不到我出错的地方。这是一个任务,如果可能的话,我宁愿有解释而不仅仅是解决方案。这是我的代码:

TITLE GETDTTM
PAGE 60, 132

;   This program retrieve the system date and time,
;   converts it to ASCII, and displays it to the screen

;   Define constants
;
CR      EQU 0DH ;define carriage return
LF      EQU 0AH ;define line feed
EOM     EQU '$' ;define end of message marker
NULL    EQU 00H ;define NULL byte
;
;   Define variables
;
JMP START
PROMPT  DB  CR, LF, "The current time is: ",EOM
PROMPT2 DB  CR, LF, "The date is: ",EOM
TIME    DB  "00:00:00", CR, LF, EOM
DATE    DB  "00/00/0000", CR, LF, EOM
;
;   Program code
;
START:  
CALL GET_TIME   ;call function to get system time
CALL GET_DATE   ;call function to get system date

LEA DX, PROMPT  ;print time prompt to screen
MOV AH, 09H
INT 21H

LEA DX, TIME    ;print time
MOV AH, 09H
INT 21H

LEA DX, PROMPT2 ;print date prompt to screen
MOV AH, 09H
INT 21H

LEA DX, DATE    ;print date
MOV AH, 09H
INT 21H


CVT_TIME:   ;converts the time to ASCII
CALL CVT_HR
CALL CVT_MIN
CALL CVT_SEC
RET

CVT_HR:
MOV BH, CH  ;copy contents of hours to BH
SHR CH,4    ;convert high char to low order bits
ADD CH, 30H ;add 30H to convert to ASCII
MOV [TIME], CH  ;save it
AND BH, 0FH ;isolate lower 4 bits
ADD BH, 30H ;convert to ASCII
MOV [TIME+1], BH    ;save it
RET

CVT_MIN:
MOV BH, CL  ;copy contents of minutes to BH
SHR CL, 4   ;convert high char to low order bits
ADD CL, 30H ;add 30H to convert to ASCII
MOV [TIME+3], CL    ;save it
AND BH, 0FH ;isolate lower 4 bits
ADD BH, 30H ; convert to ASCII
MOV[TIME+4], BH ;save it

CVT_SEC:
MOV BH, DH  ;copy contents of seconds to BH
SHR DH, 4   ;convert high char to low order bits
ADD DH, 30H ;add 30H to convert to ASCII
MOV [TIME+6], DH    ;save it
AND BH, 0FH ;isolate lower 4 bits
ADD BH, 30H ;convert to ASCII
MOV[TIME+7], BH ;save it

GET_DATE:   ;get date from the system
    MOV AH, 04H    ;BIOS function to read date
    INT 1AH        ;call to BIOS, run 04H
    CALL CVT_DATE
    RET
;CH = Century
;CL = Year
;DH = Month
;DL = Day
;CF = 0 if clock is running, otherwise 1

CVT_DATE:
    CALL CVT_MO
    CALL CVT_DAY
    CALL CVT_YR
    CALL CVT_CT
    RET

CVT_MO:     ;convert the month to ASCII
MOV BH, DH  ;copy month to BH
SHR BH, 4   ;convert high char to low order bits
ADD BH, 30H ;add 30H to convert to ASCII
MOV [DATE], BH  ;save in DATE string
MOV BH, DH  ;copy month to BH
AND BH, 0FH ;isolate lower 4 bits
ADD BH, 30H ;convert lower bits to ASCII
MOV [DATE+1], BH;save in DATE string
RET

CVT_DAY:    ;convert the day to ASCII
MOV BH, DL  ;copy days to BH
SHR BH, 4   ;convert high char to low order bits
ADD BH, 30H ;add 30H to convert to ASCII
MOV [DATE+3], BH    ;save in DATE string
MOV BH, DL  ;copy days to BH
AND BH, 0FH ;isolate lower 4 bits
ADD BH, 30H ;convert lower bits to ASCII
MOV [DATE+4], BH;save in DATE string
RET

CVT_YR:     ;convert the year to ASCII
MOV BH, CL      ;copy year to BH
SHR BH, 4       ;convert high char to low order bits
ADD BH, 30H     ;convert to ASCII
MOV [DATE+8], BH    ;save it
MOV BH, CL      ;copy year to BH
AND BH, 0FH     ;isolate low order bits
ADD BH, 30H     ;convert to ASCII
MOV [DATE+9], BH    ;save in DATE string
RET

CVT_CT:     ;convert the century to ASCII
MOV BH, CH      ;copy century to BH
SHR BH, 4       ;convert high char to low order bits
ADD BH, 30H     ;convert to ASCII
MOV [DATE+6], BH    ;save it
MOV BH, CH      ;copy century to BH
AND BH, 0FH     ;isolate low order bits
ADD BH, 30H     ;convert to ASCII
MOV [DATE+7], BH    ;save it
RET
;
;Program End
;

End

这是我在 2015 年 2 月 19 日上午 9:11 运行它时得到的结果:

The current time is: 09:00:00
The date is: 02/09/0005

我尝试添加很多关于我的意图的评论,以便您了解我正在尝试做什么,并且更容易查看是否存在某种逻辑错误。我认为从输出中可以清楚地看出我错过了将我的分钟和秒数放入 TIME 并且对如何解决这个问题有一些想法,但是在中午之后,我遇到了一些奇怪的时间,我对正在发生的事情感到困惑我的约会。任何帮助深表感谢。

编辑:有时间通过​​拆分它并实际处理分钟和秒来工作......哎呀。现在我的输出如下:

于 2015 年 2 月 19 日上午 9:23 运行

The current time is: 09:23:02
The date is: 02/09/0005

EDIT2:越来越近了!感谢 [DATE] 捕获 - 我修复了这个问题并获得了正确的月份和日期值,并且更接近年份值。发现我移动得不够远,因为 year 有 4 个字符长 - 16 位,而不是 8 位!- 所以我无法仅通过 SHR 4 位来获得全部内容!我的输出现在看起来像:

The current time is: 09:43:02
The date is: 02/19/0015

编辑 3:添加 CVT_CT 将世纪转换为 ASCII 并将其添加到 [DATE] 字符串,但我仍然得到相同的输出......

The current time is: 10:06:02
The date is: 02/19/0015

编辑 4:我忘了给我的新函数添加一个调用......哇。现在工作!!!谢谢大家的帮助!

The current time is: 10:09:02
The date is: 02/19/2015

附带问题:知道为什么秒总是 02 吗?

4

1 回答 1

3

所有用于将 BCD 转换为字符的单个(但非常相似)函数都有些混乱,几乎可以保证您会搞砸一些小事情,例如当您以后可能没有寄存器中的值时忘记保留寄存器。

如果您有兴趣避免这种情况,请查看 DRY(不要重复自己)原则(而不是 WET(将所有内容写两次) 。DRY 的维基百科页面是一个好的开始。


如果您花一些时间考虑可以将哪些内容移至通用代码(即重构),那么您最终需要担心的代码会少得多,因此错误潜入的机会也会少得多。

您的案例中的主要示例是获取每个 BCD 值并从中创建两个字符的代码。这消耗了大约 40 行实际代码(这只是用于日期位,我假设当时还会有另外 30 多行,如果你证明了这一点)。

如果您查看下面的代码,您会发现我已经将其重构put_bcd2为总共 13 行代码 - 即使您因为调用它需要额外的行而将其增加到 27 行,那仍然是大幅减少。这极大地简化了进行转换的代码和使用它的代码。

; Main program.

    call    get_date            ; get date/time into string.
    call    get_time

    lea     dx, output          ; then output the string.
    mov     ah, 09h
    int     21h

    mov     ax, 4c00h           ; exit program.
    int     21h

; Variables.

output:
    db      "The current date is: "
date:
    db      "00/00/0000", 0dh, 0ah
    db      "The current time is: "
time:
    db      "00:00:00", 0dh, 0ah, '$'

; Subroutines.

; Gets the date and inlines it into the output.
get_date:
    mov     ah, 04h             ; get date from bios.
    int     1ah

    mov     bx, offset date     ; do day.
    mov     al, dl
    call    put_bcd2

    inc     bx                  ; do month.
    mov     al, dh
    call    put_bcd2

    inc     bx                  ; do year.
    mov     al, ch
    call    put_bcd2
    mov     al, cl
    call    put_bcd2

    ret

; Gets the time and inlines it into the output.
get_time:
    mov     ah, 02h             ; get time from bios.
    int     1ah

    mov     bx, offset time     ; do hour.
    mov     al, ch
    call    put_bcd2

    inc     bx                  ; do minute.
    mov     al, cl
    call    put_bcd2

    inc     bx                  ; do second.
    mov     al, dh
    call    put_bcd2

    ret

; Places two-digit BCD value (in al) as two characters to [bx].
;   bx is advanced by two, ax is destroyed.
put_bcd2:
    push    ax                  ; temporary save for low nybble.
    shr     ax, 4               ; get high nybble as digit.
    and     ax, 0fh
    add     ax, '0'
    mov     [bx], al            ; store that to string.
    inc     bx
    pop     ax                  ; recover low nybble.

    and     ax, 0fh             ; make it digit and store.
    add     ax, '0'
    mov     [bx], al

    inc     bx                  ; leave bx pointing at next char.

    ret
于 2017-08-30T04:55:02.963 回答