$MOD186 ;******************************************************************************* ; ; MEMORY_TEST ; ; Subroutine MEMORY_TEST tests a specified region of memory. The ; algorithm was collectively developed by W. Flynn, F. Peterson, and ; M. Wilke following months of intense research: ; ; Patterns: initial = F0h ; A = 55h ; B = 33h ; C = 0Fh ; ; 1. Fill memory with the initial test pattern. ; Memory: F0, F0, F0, F0, ... ; ; 2. Fill memory with patterns, verifying the initial test pattern as ; you fill. ; Memory: A, B, C, A, B, C, ... ; ; 3. Shuffle the patterns; i.e., {A,B,C} => {B,C,A}. ; ; 4. Fill memory with patterns, verifying the patterns from the previous ; pass as you fill. ; Memory: B, C, A, B, C, A, ... ; ; 5. Shuffle the patterns; i.e., {B,C,A} => {C,B,A}. ; ; 6. Fill memory with patterns, verifying the patterns from the previous ; pass as you fill. ; Memory: C, A, B, C, A, B, ... ; ; 7. Fill memory with zeroes, verifying the patterns from the previous ; pass as you fill. ; Memory: 0, 0, 0, 0, 0, 0, ... ; ; ; MEMORY_TEST makes the following use of the stack: ; ; Offset from BP Type Usage ; -------------- ---- ----- ; 22 POINTER START_ADDRESS argument. ; 18 POINTER STOP_ADDRESS argument. ; 14 POINTER Pointer to BAD_ADDRESS argument. ; 10 POINTER Pointer to EXPECTED_VALUE argument. ; 6 POINTER Subroutine return address (CS:IP). ; 4 WORD Flag register saved prior to disabling ; interrupts. ; 2 WORD Saved DS register (required by PL/M-86). ; 0 WORD Saved BP register. ; -4 DWORD 20-bit count of the number of bytes to ; be tested. ; -8 BYTE Array [0..2] of patterns to be verified. ; -12 BYTE Array [0..2] of patterns to be stored. ; ; ; In general, the registers are used as follows: ; ; Register Usage ; -------- ----- ; AL Pattern being stored or verified. ; CX Lower 16 bits of the 20-bit count of the number ; of bytes being tested. ; DL Upper 4 bits of the 20-bit count of the number ; of bytes being tested. ; ES Selector of the current memory location being ; tested. ; DI Offset of the current memory location being ; tested. ; SI Keeps track of which of the 3 test patterns ; (NOT the sequence) corresponds to the memory ; location being tested (indexes patterns in ; local storage). ; BP Frame pointer for calling arguments and local ; storage. ; ; ; CALLING SEQUENCE: ; ; status = memory_test (start_address, stop_address, ; @bad_address, @expected_value) ; ; ; where ; ; start_address (POINTER) - the beginning of the memory block ; to be tested. ; stop_address (POINTER) - the end of the memory block to be ; tested. ; @bad_address (POINTER) - returns the address where the first ; error was detected. ; @expected_value (POINTER) - returns the expected value (BYTE) ; when the error occurred. ; status (WORD) - returns E$OK (00H) if no error was detected; ; returns E$MEM (02H) if an error was detected. ; ;******************************************************************************* NAME MEMTST ; Procedure name. PUBLIC msuMemory_Test ; Subroutine arguments. start_address EQU 22 ; POINTER. stop_address EQU 18 ; POINTER. bad_address EQU 14 ; @POINTER. expected_value EQU 10 ; @BYTE. ; Local variables. count EQU -4 ; Size of memory block to be tested (DWORD). verify_pattern EQU -8 ; Array [0..2] of patterns to be verified. store_pattern EQU -12 ; Array [0..2] of patterns to be stored. ; Initial test patterns. initial_pattern EQU 0F0H pattern_0 EQU 00FH pattern_1 EQU 033H pattern_2 EQU 055H ; 8255A Programmable Peripheral Interface (PPI) port definitions. PPI_control EQU 0CEH ; Control register PPI_port_A EQU 0C8H ; Port A. PPI_port_B EQU 0CAH ; Port B. PPI_port_C EQU 0CCH ; Port C. $EJECT ;******************************************************************************* ; ; POPF Macro Definition: ; ; The B-2 stepping of the 80286 microprocessor has a bug in the POPF ; instruction. Under certain, unspecified conditions, POPF may ; temporarily enable interrupts, even if interrupts are disabled before ; the POPF is executed and the flags being popped disable interrupts. ; ; This problem manifested itself in the monitor (interrupts disabled) ; and the console would interrupt, stealing input characters. ; ; The following, position-independent CodeMacro was recommended by INTEL ; as a software work-around. iRMX 286R and PL/M-86-compiled programs are ; not affected by the POPF bug. ; ;******************************************************************************* CODEMACRO POPF ; Simulate POPF using IRET. ; Add a return address to the stack as if an interrupt had occurred and the ; flags, CS, and IP were pushed on the stack. DB 0EH ; PUSH CS DB 0E8H, 00H, 00H ; CALL $ + 3 ; Modify the pushed IP to point to the instruction following the IRET below. DB 55H ; PUSH BP DB 8BH, 0ECH ; MOV BP, SP DB 83H, 46H, 02H, 09H ; ADD WORD PTR [BP+2], 09H DB 5DH ; POP BP ; Pop the return address and the flags. DB 0CFH ; IRET ENDM $EJECT ;******************************************************************************* ; ; MEMORY_TEST - Procedure entry and initialization. ; ;******************************************************************************* MEMTST_CODE SEGMENT WORD PUBLIC 'CODE' ASSUME CS: MEMTST_CODE msuMemory_Test PROC FAR ; Procedure entry. PUSHF ; Disable interrupts. CLI PUSH DS ; Save the registers required by PL/M-86. PUSH BP MOV BP, SP ; Set up the frame pointer. ; "Normalize" the start address so that its offset is less than 16. MOV AX, [BP+start_address] ; OFFSET$OF (start_address). MOV BX, [BP+start_address+2] ; SELECTOR$OF (start_address). MOV DI, AX SHR DI, 4 ; Number of 16-byte paragraphs in the offset. ADD BX, DI AND AX, 000FH ; "Normalize" the end address so that its offset is less than 16. MOV CX, [BP+stop_address] ; OFFSET$OF (stop_address). MOV DX, [BP+stop_address+2] ; SELECTOR$OF (stop_address). MOV DI, CX SHR DI, 4 ; Number of 16-byte paragraphs in the offset. ADD DX, DI AND CX, 000FH ; Determine the size of the memory block to be tested. DEC DX ADD CX, 16 SUB CX, AX ; CX = diff_offset. SUB DX, BX ; DX = diff_selector. MOV DI, CX ; DI = mod (diff_offset, 16). AND DI, 000FH SHR CX, 4 ; CX = diff_offset / 16. ADD DX, CX ; DX = diff_selector + (diff_offset / 16). MOV CX, DX SHR DX, 12 ; DX = high 4 bits of 20-bit difference. SHL CX, 4 ; CX = low 16 bits of 20-bit difference. ADD CX, DI ; Add in lowest 4 bits. ADD CX, 1 ; Count = difference + 1. ADC DX, 0 PUSH DX ; Save the count on the stack. PUSH CX $EJECT ;******************************************************************************* ; ; This section of code fills the memory with the initial test pattern. ; Each byte stored is immediately verified, thus detecting non-existent ; memory and preventing the test from continuing. ; ; NOTE the handling of the loop count. The borrow from the high 4 bits ; of the loop count is taken when X:0001 is decremented to X:0000; NOT, ; as is normally done, when X:0000 is decremented to [X-1]:FFFF. Doing ; this allows the use of the LOOP instruction (much faster than DWORD ; decrements and tests) for 64K-1 out of 64K loops. NOTE that if the low ; 16 bits of the initial count are zero, then a "pre-borrow" must be made ; from the high 4 bits. ; ;******************************************************************************* fill_initial_test_pattern: MOV AL, initial_pattern ; Pattern. MOV ES, [BP+start_address+2] ; Start address of block being tested. MOV DI, [BP+start_address] MOV CX, [BP+count] ; Size of block being tested. MOV DL, [BP+count+2] OR CX, CX ; If low 16 bits of count = 0, JNE next_fill_byte ; then "pre-borrow" from high DEC DL ; 4 bits of count. next_fill_byte: MOV ES:[DI], AL ; Store the pattern. CMP AL, ES:[DI] ; Successfully stored? JNE fill_error INC DI ; Move on to the next byte. JNE next_fill_count MOV DI, ES ADD DI, 1000H ; 64K roll-over of DI. MOV ES, DI XOR DI, DI ; Restore DI to zero. next_fill_count: LOOP next_fill_byte ; Decrement low 16 bits of count. OR DL, DL ; If count = 0, then done. JE verify_patterns_in_memory DEC DL ; "Pre-borrow" from high 4 bits. JMP next_fill_byte fill_error: JMP memory_test_error $EJECT ;******************************************************************************* ; ; This section of code simultaneously verifies the patterns currently ; in memory and stores the new pattern. ; ; NOTE the handling of the loop count. The borrow from the high 4 bits ; of the loop count is taken when X:0001 is decremented to X:0000; NOT, ; as is normally done, when X:0000 is decremented to [X-1]:FFFF. Doing ; this allows the use of the LOOP instruction (much faster than DWORD ; decrements and tests) for 64K-1 out of 64K loops. NOTE that if the low ; 16 bits of the initial count are zero, then a "pre-borrow" must be made ; from the high 4 bits. ; ;******************************************************************************* verify_patterns_in_memory: MOV AH, initial_pattern ; Allocate and initialize verify patterns on the stack. MOV AL, initial_pattern PUSH AX PUSH AX MOV AL, pattern_2 ; Allocate and initialize store patterns on the stack. PUSH AX MOV AH, pattern_1 MOV AL, pattern_0 PUSH AX initialize_verify_pass: MOV SI, 3 ; Pattern count. MOV ES, [BP+start_address+2] ; Start address of block being tested. MOV DI, [BP+start_address] MOV CX, [BP+count] ; Size of block being tested. MOV DL, [BP+count+2] OR CX, CX ; If low 16 bits of count = 0, JNE next_verify_byte ; then "pre-borrow" from high DEC DL ; 4 bits of count. next_verify_byte: MOV AL, [BP+verify_pattern-1][SI] ; Load the verify pattern. CMP AL, ES:[DI] ; Does the pattern match the stored byte? JNE verify_error MOV AL, [BP+store_pattern-1][SI] ; Load the store pattern. MOV ES:[DI], AL ; Store the store pattern. INC DI ; Move on to the next byte. JNE next_verify_pattern MOV DI, ES ADD DI, 1000H ; 64K roll-over of DI. MOV ES, DI XOR DI, DI ; Restore DI to zero. next_verify_pattern: DEC SI ; Move on to the next one of the three patterns. JNE next_verify_count MOV SI, 3 next_verify_count: LOOP next_verify_byte ; Decrement low 16 bits of count. OR DL, DL ; If count = 0, then done. JE next_verify_pass DEC DL ; "Pre-borrow" from high 4 bits. JMP next_verify_byte next_verify_pass: OR AL, AL ; If zeroes were stored on the JE memory_test_complete ; just-completed pass, then done. MOV AX, [BP+store_pattern] ; Store current store patterns MOV [BP+verify_pattern], AX ; for verification on next pass. MOV AL, [BP+store_pattern+2] MOV [BP+verify_pattern+2], AL XCHG [BP+store_pattern], AL ; Shuffle the sequence to get XCHG [BP+store_pattern+1], AL ; the new store patterns. XCHG [BP+store_pattern+2], AL CMP AL, pattern_0 ; If back to original sequence, then set up to store zeroes. JNE initialize_verify_pass XOR AX, AX ; New store pattern = { 0, 0, 0 }. MOV [BP+store_pattern], AX MOV [BP+store_pattern+2], AL JMP initialize_verify_pass verify_error: JMP memory_test_error $EJECT ;******************************************************************************* ; ; The memory test was completed with no errors detected. Return a status ; of E$OK. ; ;******************************************************************************* memory_test_complete: MOV AX, 0 JMP return_from_memory_test ;******************************************************************************* ; ; An error was detected. The address at which the error was detected is ; in ES:DI and the expected value of the pattern in error is in AL. ; ;******************************************************************************* memory_test_error: MOV BX, [BP+bad_address+2] ; Address of BAD_ADDRESS variable. MOV DS, BX MOV BX, [BP+bad_address] MOV DS:[BX], DI ; Return the bad address. MOV DS:[BX+2], ES MOV BX, [BP+expected_value+2] MOV DS, BX ; Address of EXPECTED_VALUE variable. MOV BX, [BP+expected_value] MOV DS:[BX], AL ; Return the expected value. MOV AX, 02H ; Return status = E$MEM. ;******************************************************************************* ; ; Return from the routine. Clear the timeout interrupt (in case it was ; set), deallocate the stack storage of local variables, pop the saved ; registers, and return. ; ;******************************************************************************* return_from_memory_test: MOV BX, AX MOV AL, 06H ; Reset bit 3 of the PPI, clearing the OUT PPI_control, AL ; timeout interrupt and the yellow LED. MOV AL, 07H ; Set bit 3 of the PPI, re-enabling the OUT PPI_control, AL ; timeout interrupt. MOV AX, BX MOV SP, BP ; Deallocate the local storage. POP BP ; Restore the saved registers. POP DS POPF ; Re-enable interrupts. RET start_address+4-10 ; Return and pop arguments. msuMemory_Test ENDP MEMTST_CODE ENDS END