x86汇编:编写程序以测试整个1MB内存的内存功能

时间:2014-10-08 01:05:27

标签: memory assembly x86 intel masm

目标:
对于使用Intel 80186微处理器的系统,我需要编写一个程序,逐个字节地测试整个1MB内存的写入功能。换句话说,我需要在内存中的每个字节写一个0,然后检查是否实际写入了0。然后,我需要使用值1重复该过程。最后,在各自的写入操作期间未成功写入0或1的任何存储器位置需要存储在堆栈中。

讨论:
我是大学的电气工程专业的学生(非计算机科学),对x86汇编语言和MASM611相对较新。我不是在寻找一个完整的解决方案。但是,我需要一些指导。

在本学期的早些时候,我写了一个程序,用0填充了一部分内存。我相信这对我目前的项目来说是一个很好的起点。

早期计划的源代码:

;****************************************************************************
;Program Name: Zeros
;File Name: PROJ01.ASM
;DATE: 09/16/14
;FUNCTION: FILL A MEMORY SEGMENT WITH ZEROS
;HISTORY:
;AUTHOR(S):
;****************************************************************************

NAME ZEROS

MYDATA SEGMENT
MYDATA ENDS

MYSTACK SEGMENT STACK
                DB              0FFH  DUP(?)
                End_Of_Stack    LABEL BYTE
MYSTACK ENDS



ASSUME  SS:MYSTACK, DS:MYDATA, CS:MYCODE
MYCODE SEGMENT
START:  MOV AX, MYSTACK
        MOV SS, AX
        MOV SP, OFFSET End_Of_Stack
        MOV AX, MYDATA
        MOV DS, AX

        MOV AX, 0FFFFh                  ;Moves a Hex value of 65535 into AX
        MOV BX, 0000h                   ;Moves a Hex value of 0 into BX

        CALL Zero_fill                  ;Calls procedure Zero_fill

        MOV AX, 4C00H                   ;Performs a clean exit
        INT 21H

Zero_fill   PROC NEAR                   ;Declares procedure Zero_fill with near directive
                MOV DX, 0000h           ;Moves 0H into DX
                MOV CX, 0000h           ;Moves 0H into CX. This will act as a counter.
Start_Repeat:   INC CX                  ;Increments CX by 1
                MOV [BX], DX            ;Moves the contents of DX to the memory address of BX
                INC BX                  ;Increments BX by 1
                CMP CX, 10000h          ;Compares the value of CX with 10000H. If equal, Z-flag set to one.
                JNE Start_Repeat        ;Jumps to Start_Repeat if CX does not equal 10000H.
                RET                     ;Removes 16-bit value from stack and puts it in IP
Zero_fill   ENDP                        ;Ends procedure Zero_fill

MYCODE ENDS
END START

要求:
1.采用明确的细分结构 2.使用ES:DI寄存器对来寻址测试存储区 3.非破坏性访问:在测试每个内存位置之前,我需要存储该字节的原始内容。测试完成后需要恢复 4.我需要将未通过测试的任何内存位置的地址存储在堆栈中 5.我需要确定最高的RAM位置。

安排:
1.在循环中:如果检查失败,将0000H写入内存位置,检查该记忆位置的值,ES和DI的PUSH值到堆栈。
2.在循环中:将FFFFH写入内存位置,检查该mem位置的值,如果检查失败,则将ES和DI的PUSH值添加到堆栈。

源代码实施初步计划:

;****************************************************************************
;Program Name: Memory Test
;File Name: M_TEST.ASM
;DATE: 10/7/14
;FUNCTION: Test operational status of each byte of memory between a starting
;          location and an ending location
;HISTORY: Template code from Assembly Project 1
;AUTHOR(S):
;****************************************************************************

NAME M_TEST

MYDATA SEGMENT
MYDATA ENDS

MYSTACK SEGMENT STACK
                DB              0FFH  DUP(?)
                End_Of_Stack    LABEL BYTE
MYSTACK ENDS

ESTACK SEGMENT COMMON
ESTACK ENDS

ASSUME  SS:MYSTACK, DS:MYDATA, CS:MYCODE, ES:ESTACK
MYCODE SEGMENT
START:          MOV AX, MYSTACK
                MOV SS, AX
                MOV SP, OFFSET End_Of_Stack
                MOV AX, MYDATA
                MOV DS, AX

                MOV AX, FFFFH           ;Moves a Hex value of 65535 into AX
                MOV BX, 0000H           ;Moves a Hex value of 0 into BX

                CALL M_TEST             ;Calls procedure M_TEST

                MOV AX, 4C00H           ;Performs a clean exit
                INT 21H

M_TEST      PROC NEAR                   ;Declares procedure M_TEST with near directive
                MOV DX, 0000H           ;Fill DX with 0's
                MOV AX, FFFFH           ;Fill AX with 1's
                MOV CX, 0000H           ;Moves 0H into CX. This will act as a counter.
Start_Repeat:   MOV [BX], DX            ;Moves the contents of DX to the memory address of BX
                CMP [BX], 0000H         ;Compare value at memory location [BX] with 0H. If equal, Z-flag set to one.
                JNE SAVE                ;IF Z-Flag NOT EQUAL TO 0, Jump TO SAVE
                MOV [BX], AX            ;Moves the contents of AX to the memory address of BX
                CMP [BX], FFFFH         ;Compare value at memory location [BX] with FFFFH. If equal, Z-flag set to one.
                JNE SAVE                ;IF Z-Flag NOT EQUAL TO 0, Jump TO SAVE
                INC CX                  ;Increments CX by 1
                INC BX                  ;Increments BX by 1
                CMP CX, 10000H          ;Compares the value of CX with 10000H. If equal, Z-flag set to one.
                JNE Start_Repeat        ;Jumps to Start_Repeat if CX does not equal 10000H.
SAVE:           PUSH ES
                PUSH DI
                RET                     ;Removes 16-bit value from stack and puts it in IP
M_TEST      ENDP                        ;Ends procedure Zero_fill

MYCODE ENDS
END START

我的评论可能不准确。

问题:
1.如何使用ES:DI来寻址测试存储区?
2.保持初始内存值的最佳方法是什么,以便在测试特定内存位置时可以替换它?我相信寄存器AX-DX已经在使用中。

另外,如果我有更新的代码和问题,我应该在同一个帖子上发布,还是应该创建一个带有这个链接的新帖子?

非常感谢任何其他建议 提前致谢。

3 个答案:

答案 0 :(得分:0)

  

如何使用ES:DI来寻址测试存储区?

E.g。 mov al, es:[di]

  

保持初始内存值的最佳方法是什么,以便在我测试特定内存位置时可以替换它?我相信寄存器AX-DX已经在使用中。

右。您可以使用al存储原始值,并在01中预先加载blcl,然后执行此类操作(关闭我的头脑):

mov al, es:[di] // load/save original value

mov es:[di], bl // store zero
cmp bl, es:[di] // check that it sticks
jne @pushbad    // jump if it didn't

mov es:[di], cl // same for 'one'
cmp cl, es:[di]
jne @pushbad

mov es:[di], al // restore original value
jmp @nextAddr

@pushbad:
mov es:[di], al // restore original value (may be redundant as the mem is bad)
push es
push di

@nextAddr:
...

答案 1 :(得分:0)

还有一些关于测试我们自己的例程所声称的内存位置的话。我们可以将我们的例程复制并运行到显示设备的帧缓冲区中。

...

注意:如果我们想要存储或比较具有立即值的内存位置,那么我们必须指定我们想要访问的字节数。 (但与使用寄存器作为源或目标相反,汇编器已经知道它的大小,因此我们不需要指定。)

访问一个地址的一个字节(具有立即值):

CMP BYTE[BX], 0     ; with NASM (Netwide Assembler)
MOV BYTE[BX], 0

CMP BYTE PTR[BX], 0 ; with MASM (Microsoft Macro Assembler)
MOV BYTE PTR[BX], 0

一起访问两个地址的两个字节
(如果目标地址均匀对齐,执行速度更快):

CMP WORD[BX], 0     ; with NASM
MOV WORD[BX], 0

CMP WORD PTR[BX], 0 ; with MASM
MOV WORD PTR[BX], 0

答案 2 :(得分:0)

如果您首先假设RAM中的任何位置可能有问题;那么这意味着您无法使用RAM来存储代码或数据。这包括临时使用 - 例如,您不能将代码临时存储在RAM中,然后将其复制到显示内存,因为您可能会将损坏的代码从RAM复制到显示内存。

考虑到这一点;唯一有意义的情况是ROM中的代码测试RAM - 例如在固件的POST(开机自检)期间。此外;这意味着你根本不能使用堆栈 - 不是为了跟踪故障区域,甚至不是为了调用函数/例程。

请注意,您可能会假设您可以测试一个小区域(例如,找到第一个没有故障的KiB),然后使用该RAM存储结果等。这将是一个错误的假设。

对于RAM故障,有很多原因。第一组原因是地址总线或数据总线上的“开路连接”和“短路连接”。举一个简单的例子,如果地址线12恰好是开路,那么最终结果将是前4 KiB总是与第二个4 KiB的RAM具有相同的内容。您可以根据需要测试前4个KiB的RAM并确定它“好”,但是当您测试第二个4 KiB的RAM时,您会丢弃第一个4 KiB RAM的内容。

有一个“智能序列”的测试。具体来说,测试地址线从最高到最低(例如,将不同的值写入0x000000和0x800000并检查它们是否正确;然后对0x000000和0x400000执行相同操作,然后对0x000000和0x200000执行相同操作,依此类推,直到找到地址为止0x000000和0x000001)。但是,RAM芯片连接到CPU的方式不一定像直接映射那么简单。例如,地址的最高位可能选择哪个RAM存储区;在这种情况下,您必须测试0x000000和0x400000以及0x800000和0xC00000来测试两个库。

一旦确定地址线有效;那么你可以对数据线和RAM本身做类似的事情。最常见的测试称为“步行测试”;存储0x01,然后存储0x02,依此类推(最多0x80)。这会检测诸如“粘滞位”之类的内容(例如,某位的状态恰好“卡在”其邻居的状态)。如果你只写(例如)0x00并测试它然后写0xFF并测试它,那么你将错过大多数RAM故障。

也;小心“打开连接”。在某些机器上,总线电容可以起作用,你可以在那里写一个值,总线电容“存储”前一个值,这样当你读回它时,即使没有连接,它看起来也是正确的。为了避免这种风险,您需要在两者之间写入不同的值 - 例如将0x55写入您正在测试的地址,然后在其他地方写入0xAA,然后读取原始值(并希望您获得0x55,因为RAM工作,而不是0xAA)。考虑到这一点(为了性能),您可以考虑在RAM的一个区域中进行“步行”,同时在RAM的下一个区域中执行“步行零”;所以你总是在从一个区域读取一个值和从另一个区域读取倒置值之间交替。

最后,一些RAM问题取决于噪声,温度等。在这些情况下,你可以进行非常彻底的RAM测试,比如它完美,然后在2分钟后遭受RAM损坏。这就是为什么(例如)典型的建议是如果你真的想要正确地测试RAM那么运行像“memtest”这样的东西8小时左右。