;****************************************************************************** ;MEMTEST1.ASM for CP/M-80. Written by Len Moskowitz and released to the ;public domain. October 1984 ; ; MEMTEST is a memory test program. It tests memory with ; twelve different patterns of bits. It does each test addressed ; both forwards and backwards through memory. ; The twelve patterns are: ; ; Bit 76543210 ; ------------ ; Pattern 0. 00000000 (00h, All zeros) ; 1. 00000001 (01h)___ ; 2. 00000010 (02h) | ; 3. 00000100 (04h) | ; 4. 00001000 (08h) | "Walking Ones" or ; 5. 00010000 (10h) | "Barber Pole" pattern ; 6. 00100000 (20h) | ; 7. 01000000 (40h) | ; 8. 10000000 (80h)__| ; 9. 01010101 (55h, Even alternating ones) ; 10. 10101010 (AAh, Odd alternating ones) ; 11. 11111111 (FFh, All ones) ; ; Before the test begins, the program tells you where it resides ; so that you can avoid overwriting it. You are then prompted for ; whether you want screen updates and for the first and last addresses ; of the memory block to be tested. Screen updates cause the program ; to run much, much slower than without updates. If you choose updates, ; the screen will show the current address being tested and the pattern ; being used. The program expects the addresses to be in Hex ; (radix 16) format. A control C aborts the program. ; For each pattern, the first memory location is written to and ; then read from. The value read from memory is compared to the test ; pattern. If they are identical, the test proceeds to the next memory ; location. If they are not the same, the address that failed is ; displayed along with the current test pattern and the erroneous value ; that the memory location contained. You are given a choice of ; continuing or quitting. ; When all the locations are tested, the test goes on to the next ; pattern. When all twelve patterns have been tested using forward ; addressing (from low address to high address), the twelve are then ; repeated using backward addressing (high address to low address). ; If the test completes without mishap, a message confirms this and ; the program exits to CP/M (provided you haven't overwritten it with ; the test) via a jump to location 0000. ; I have provided for I/O using either the standard BDOS calls ; or direct console I/O via routines internal to MEMTST. This version ; has the internal routines commented out so that the casual user can ; run it without change. If you wish to make this test able to ; check all memory except for the block where it resides, all console ; I/O should be done using the internal routines. To activate them, ; comment out the BDOS subroutines and remove the comment semicolons ; from the internal subroutines. You also must change the ; "instatusport", "outstatusport", "inreadymask", "outreadymask", ; "indataport", and "outdataport" equates to reflect your system ; requirements. ; If you use the BDOS calls, take pains not to ask MEMTST to test ; the locations where the BDOS and BIOS reside. You can find the ; address of the BDOS by looking at locations 0006 and 0007 for a ; standard CP/M installation. (Location 0005 is the "jump to BDOS" call ; address.) Testing these locations will result in an overwrittten ; operating system and you'll have to reboot to bring the system back up. ; Also be sure not to ask to test the memory where MEMTST resides. ; For 64K of memory, the test takes approximately 75 seconds to ; complete on a 4 megaHertz Z80A system with no wait states with no ; screen updates. With screen updates on, the test takes 20 Hours! ; I therefore recommend you run without screen updates if you're testing ; any block sized over 0200h. If an error occurs, probe that area ; with updates on. ; A shortcoming of any test program of this type is that it can't ; test the worst case memory access timing required for instruction ; fetch operations. If you find you have problems that this program does ; not isolate, try using WORM (of which the current version is WORM21), ; which is available via many RCPM systems. It checks for access ; timing sensitivity but does not do exhaustive pattern testing. Between ; the two programs you're likely to uncover any memory problems you might ; have. ; ; Len Moskowitz ; Fair Lawn, N.J. ; October 1984 ; ;11Nov84 Renamed MEMTST10.ASM to MEMTEST1 to avoid confusion ; with MEMTST11.ASM written by J. Kravitz, 08Dec76. ; Ken Stritzel, Flanders, NJ RCPM ; ;****************************************************************************** ; ;**************** ;***** Equates * ;**************** cpm: equ 0000h ;jump to CP/M address bdos: equ 0005h ;jump to BDOS address conout: equ 02h ;BDOS console out function code conin: equ 01h ;BDOS console in function code constat: equ 0bh ;BDOS get console status code tpa: equ 0100h ;CP/M programs start at this address cr: equ 0dh ;ASCII carriage return lf: equ 0ah ;ASCII line feed controlc: equ 03h ;ASCII control C del: equ 7fh ;ASCII delete no: equ 0 yes equ 0ffh ; Change the following equates to match your system if you are using the ; internal I/O subroutines. If you are using the CP/M BDOS calls you may ; leave these as is. outdataport: equ 0 indataport: equ 0 outstatusport: equ 1 instatusport: equ 1 outreadymask: equ 01 inreadymask: equ 02 memtst: org tpa ; Tell him where this program resides so he can avoid it during the test. lxi sp,stacktop lxi h,offlimits call print lxi h,repeat mov c,h call unpack mov c,l call unpack lxi h,andmessage call print lxi h,endprogram mov c,h call unpack mov c,l call unpack mvi a,'.' call sout ; Ask him if he wants the screen updated continuously, and store his ; answer in "screenupdate". lxi h,screenupdateQ call print call sin cpi 'Y' jz setupdateyes cpi 'y' jz setupdateyes cpi cr jz setupdateyes setupdateno: mvi a,no sta screenupdate jmp locationprompt setupdateyes: mvi a,yes sta screenupdate ; This section does initialization and gets the starting and ending ; locations from the fellow at the keyboard. locationprompt: lxi h,enterfirst call print call loca shld startloc lxi h,enterlast call print call loca call crlf mov e,l mov d,h inx d mvi a,0 lxi h,step mov m,a inx h mov m,a lhld startloc mov c,a mov b,a ; This is the core of the memory test routine. The section from ; "repeat" to "doit" does the screen update if requested. repeat: lda screenupdate cpi no jz doit push b push h lxi h,currentaddress call print pop h push h mov c,h call unpack mov c,l call unpack lxi h,currentpattern call print pop h pop b push b push h mov c,b call unpack mvi a,cr call sout pop h pop b ; The next four lines do the memory test doit: mov m,b ;move the pattern to memory mov a,m ;move it back to A cmp b ;compare it back to the pattern jnz error ;jump if they don't match ; Check if he wants to exit and get the next test location ready. reenter: call consolestatus ;check if he pressed a key jz nocontrolc ;if not jump around the next few lines call sin ;if he did, take the character in ;SIN exits if it was control C nocontrolc: lda step ;check the "backward" flag ani 80h ;if it's set jnz backwards ; jump around the forward code inx h ;we're going forward so increment HL jmp compareend ; and jump around the backward code backwards: dcx h ;we're going backward so decrement HL ; This section checks the next address to be tested with the end ; location. If we haven't hit the end location yet, we loop to repeat. ; If we have hit it we fall through. compareend: mov a,l cmp e jnz repeat mov a,h cmp d jnz repeat ; See if we've completed the last pattern. If we have, then jump ; to "checkforback" to see if we've done the backward addressing yet. ; If this is not the last pattern, go onto the next one by incrementing ; the step counter in register C and STEP (which holds the step and the ; "backward" flag. checkstep: mvi a,0bh cmp c jz checkforback inr c lda screenupdate cpi no jz noupdate call crlf noupdate: lxi h,step mvi a,80h ana m jz backflagnotset mvi a,80h ora c mov m,a jmp setnewpattern backflagnotset: mov m,c ; Check what step we're going to do and set the new pattern accordingly. setnewpattern: mvi a,1 cmp c jnz not1 mvi b,1 jmp loadpattern not1: mvi a,9h cmp c jnz not9 mvi b,0aah jmp loadpattern not9: jnc lessthan9 mvi a,0ah cmp c jnz not10mustbe11 mvi b,55h jmp loadpattern not10mustbe11: mvi b,0ffh jmp loadpattern lessthan9: mov a,b rlc mov b,a loadpattern: lxi h,pattern mov m,b lhld startloc jmp repeat ; We get to this next section when we've completed the last pattern. If ; the "backward" flag (bit 7 of STEP) is not set, it sets it, swaps the ; starting and ending locations and starts the background addressing ; cycle. If the flag was set, we're done so we exit via ENDOK checkforback: lda screenupdate cpi no jz updateno call crlf updateno: lda step ani 80h jnz endok mvi a,80h sta step lxi h,startloc mov e,m inx h mov d,m dcx d lhld endloc shld startloc mvi a,0 mov c,a mov b,a jmp repeat ; ENDOK prints the message "OK" and exits to CP/M. endok: call crlf mvi a,'O' call sout mvi a,'K' call sout jmp cpm ; ERROR prints the address of the memory cell that read back erroneous ; data, the expected pattern and the bad data. It then offers you ; the choice of continuing or exiting to CP/M. error: call crlf mov d,a push psw push b push d push h lxi h,errorat call print pop h push h mov c,h call unpack mov c,l call unpack lxi h,patternis call print mov c,b call unpack lxi h,memoryvalue call print mov c,d call unpack call crlf lxi h,continueQ call print call sin cpi 'Y' jz setuptorepeat cpi 'y' jz setuptorepeat cpi cr jz setuptorepeat jmp cpm setuptorepeat: mvi a,cr call sout pop h pop d pop b pop psw jmp reenter ;**************** ;***** MESSAGES * ;**************** ; errorat: db ' *** Error at location : ',0 patternis: db ' Pattern is : ',0 memoryvalue: db ' Memory value is : ',0 continueQ db 'Continue? (Y/N) : ',0 enterfirst: db cr,lf,'Enter first address : ',0 enterlast: db cr,lf,'Enter last address : ',0 currentaddress: db 'Current address : ',0 currentpattern: db ' Current pattern : ',0 screenupdateQ: db cr,lf,'Screen updates? (Runs much slower with updates' db ') (Y/N) : ',0 offlimits: db cr,lf,'The core of this program resides between ',0 andmessage: db ' and ',0 ;******************* ;***** SUBROUTINES * ;******************* ; CONSOLESTATUS checks to see if a key was pressed. If a key was ; pressed, the A register will contain 0FFh. Otherwise it will have ; 00h. ; ***** BDOS I/O routine consolestatus: push b push d push h mvi c,constat call bdos ora a pop h pop d pop b ret ; ***** Internal I/O routine ; ;consolestatus: in instatusport ; ani inreadymask ; jz notready ; mvi a,0ffh ; ret ; notready:mvi a,0 ; ret ; ; ; CRLF prints a carriage return and a line feed. Preserves all ; registers. crlf: push psw xra a adi cr call sout adi lf call sout pop psw ret ; HXTOASCI converts the lower nibble of the value in the A register ; into an ASCII numeric or alphabetic character. It expects the most ; significant nibble to be zeroed. hxtoasci: cpi 0ah jc numeric sui 9 adi 40h ret numeric:adi 30h ret ; PRINT prints the string pointed to by registers HL until a zero ; (0h) is encountered. ; print: mov a,m cpi 0 rz print1: call sout inx h mov a,m cpi 0 jnz print1 ret ; SIN gets one character from the console and echoes it back to the ; console. Character ends up in register A. ; ; ***** BDOS I/O version sin: push b push d push h mvi c,conin call bdos cpi controlc jz cpm pop h pop d pop b ret ; ***** Internal I/O version ;sin: in instatusport ; ani inreadymask ; jz sin ; in indataport ; cpi controlc ; jz cpm ; push psw ; call sout ; pop psw ; ret ; SOUT outputs whatever is the A register to the console. Returns with ; register A cleared to zero. ; ; ***** BDOS I/O version sout: push b push d push h mov e,a mvi c,conout call bdos pop h pop d pop b xra a ret ; ***** Internal I/O version ;sout: push psw ; sout1: in outstatusport ; ani outreadymask ; jz sout1 ; pop psw ; out outdataport ; xra a ; ret ; UNPACK takes what ever is in register C (which is supposed to be ; an address or memory pattern), unpacks it into two hex digits, ; converts them to ASCII and outputs them to the console. Modifies ; A register only. unpack: mov a,c ani 0f0h rrc rrc rrc rrc call hxtoasci call sout mov a,c ani 0fh call hxtoasci call sout ret ;*************** ;***** STORAGE * ;*************** ; Storage goes here. The subroutines after this section can be written ; over by MEMTST if you want to test those locations. They are only ; used in the initialization section at the top of the program. screenupdate ds 1 endloc: ds 2 startloc: ds 2 step: ds 1 pattern: ds 1 stack: ds 32 stacktop: equ $ endprogram: equ $ ; LOCA takes four ASCII characters in from the console (expecting them ; to be 0-9 or A-F), converts them to hex, packs them into two bytes, ; and stores them in "endloc" and registers HL. loca: call sin call pack mov h,c call sin call pack mov l,c shld endloc ret ; PACK expects an ASCII character in register A. If it's an alphabetic ; character (A-F) it converts it to a hex digit in the range of A hex ; to F hex. If it is a number it just strips off the most significant ; nibble leaving a hex digit in the range from 0 to 9. pack: cpi 41h jc label8 adi 09h label8: ani 0fh add a add a add a add a mov c,a call sin cpi del jnz norubout adi ' ' call sout call sin jmp pack norubout: cpi 41h jc label9 adi 09h label9: ani 0fh add c mov c,a ret