Subttl -Firmware Test and Diagnostics (Rev_20 21_Dec_83) ; ; Copyright 1983, 1984 ; Morrow Designs, Inc. ; San Leandro, Ca. ; David Block, John Zalabak, Bill Smith ; .z80 ; switch set to assemble z80 nemonics include HD20DEF.MAC ; Include Global Equates aseg org o_test ;See the hd*def.mac module for definition ; ; Function: This module tests the RAM and ROM of the Micro Decision ; prior to booting. The module is entered any time the MD ; is RESET. Upon entry, a determination is made as to whether ; a power-on RESET ( cold reset ), or a " warm reset " is ; taking place. If it is a cold reset, than the RAM and ROM ; is tested, prior to booting, and if it is a warm reset, ; the diagnostics are skipped, and the system is booted. ; As the tests progress, the active test is displayed on ; the terminal. Upon completion of a test, a message is ; printed to indicate the passed test. If the test fails, ; then a message is printed, and the machine halts. ; ; Rev: 00 David Block 5/3/83 ; ; ; Index: Diagnostic Module ;------------------------- ; ; inttst Initial Diagnostics Main Line (26_Aug_83) ; romtst Do a Checksum test of the Rom ; comtst Setup test routine and do a checksum on the common area ; ; diagnose Main line of the Diagnostic Routines ; CNTJP Indirect Jump (hl) ; null return instruction for un-implemented tests ; bpole perform a barberpole test on the selected I/O port ; testout output a character to the selected I/O port ; loopbk Perform a loop-back test on serial port 2 ; mtest Perform memory test ; clrerr clear errors and pass ; incerr increment error count ; prnpas print pass info for test ; memerr print a memory error message and then halt ; chksum perform a vertical parity checksum on a block of memory ; ; diag1 Perform Ram test if necessary and then boot ; ramtst Test a block of memory ; indjp perform jump (IX) to wrmem or rdmem ; wrmem write memory test ; rdmem read memory test ; prnerr Print a failed memory location and the proper value ; patlst memory test pattern table page 64 ;---------------------------------------------------------------------- ; Linkage Definitions ;-------------------- ; public block ;Starting address of Common test code in Rom public diag1 ;Start of diagnostics buffer (for ID frame in main) public endblk ;End of diagnostics buffer (for ID frame in main) public inttst ;Initial Diagnostics Main Line ; In the Main Module external xfrdat ;Transfer ramdatX, ramdatY and MTABs to ram external fatal ;Fatal Error Termination external ramchk ;Value of checksum for ram external romchk ;Value of checksum for rom ; In the I/O Module external sp1sts ;read port 1 status (the default console) external sp2sts ;read port 2 status external sp3sts ;read port 3 status external sp1in ;input from serial port 1 (the default console) external sp2in ;input from serial port 2 external sp3in ;input from serial port 3 external sp1out ;Output to serial port 1 (the default console) external sp2out ;Output to serial port 2 external sp3out ;Output to serial port 3 external cinIY ;Console Input Routine external coutIY ;Console output routine external cstsIY ;Console input status routine external mesg ;Print a message external cntout ;Centronics port out external outbyt ;Output the accm as two hex digits ; In the Dsk1 Module external PlsMtr ;Pulse the Motor On Line ;---------------------------------------------------------------------- ; Local Equates ;-------------- ; stkspc equ 10h ;8 level stack reserved bufsiz equ 0-combuf-stkspc ;size of area to test tleng equ endblk-diag1 ;length of code residing in the top of common romsiz equ 8*1024 ;length of rom ; Flag definitions for Ram Memory Test wr equ 0 ;Flag to Write memory rd equ 40h ;Flag to Read and Compare memory page ;====================================================================== ; Initial Diagnostics Main Line (15_Nov_83) ;========================================== ; inttst: call romtst ;Test the Rom call comtst ;Test the Common Area (stack must be above this) call diag1 ;Test two banks of memory call xfrdat ;Move ramdatX, ramdatY and MTABs into ram in a,(cstat) ;If (the test and diagnostics mode enabled) and $DIAGM call z,diagnose ; enter diagnostics mode ret ;Return ;---------------------------------------------------------------------- ; ROMTST Do a Checksum test of the Rom ;------------------------------------- ; ; Function: This routine initializes the hl, and bc register ; pairs and calls chksum. It is used to test the system ; integrity. If the test passed, then a message is printed ; and the diagnostics continue. If the test failed, then ; execution will be passed to the memerr routine. ; Memerr will print a message indicating the problem found, ; and then abort the boot process. ; romtst: ld hl,0 ; starting address in rom ld bc,romsiz ; number of bytes to check (all of rom) call chksum ; compute checksum of rom ret z ; return if checksum was o.k ld de,rommsg ; point to rommsg call mesg ; print ROM error message jp memerr ; print it, and halt page ;---------------------------------------------------------------------- ; COMTST Setup test routine and do a checksum on the common area ;--------------------------------------------------------------- ; 1) This program moves a ram test program into the common memory, ; padding the buffer so that it will return a checksum of zero. ; Then the checksum test is run on the common buffer. ; 2) If this test is successful then the accm is returned equal to 0. ; 3) If this test fails then execution will pass to 'memerr'. ; Move the test program into the Common Area comtst: ld hl,block ; from.... ld de,combuf ; to.... ld bc,tleng ; how many... ldir ; move it to common area ; Pad with emptyness ld bc,bufsiz-tleng-1 ; pad all but checksum byte ld h,d ld l,e ; source = ending dest (crawling fill) inc de ; dest for ldir ld (hl), 0ffh ; initial pad to copy upward ldir ; crawling fill ld a,(ramchk) ; get checksum ld (hl),a ; set into highest byte ; Test the checksum of the program just moved into the common area ld hl,combuf ; low address ld bc,bufsiz ; # of bytes to test call chksum ; test common area ret z ; return if O.K. ld de,rambad ; point to rammsg call mesg ; print ram bad message jp memerr ; jmp to error page ;---------------------------------------------------------------------- ; DIAGNOSE - Main line of the Internal Diagnostics Routines (18_Dec_83) ;---------------------------------------------------------------------- ; 1) This routine is used to test the various functional blocks of ; the hardware. ; 2) It displays a menu, from which you can select the test to ; be performed. ; diagnose: ld de,diagmsg ; print diagnostics signon msg call mesg ; and menu inlp: call cinIY ;Repeat Get the number of a test to run cp '1' jp m,inlp cp '9' + 1 jp p,inlp ;Until ((number ge 1)and(number le 9)) push af ;Acknowledge a valid test number ld c,a call coutIY ;(Output test number) ld de,crlf call mesg ;(Output cr/lf) pop af sub '0' ;Translate test number (1<=result<=9) cp 4 ;If (this is a bpole test) jp p,notbp ld (port),a ; set up port to use notbp: dec a ;set base offset of 0 rlca ;get Execution Address from the table ld c,a xor a ld b,a ;BC:= offset in dispatch table ld hl,dsptch ;HL:= base of dispatch table add hl,bc ;HL:= base + offset (address of vector) ld e,(hl) inc hl ld d,(hl) ex de,hl ;HL:= Execution Vector call cntjp ;Execute the Test jr diagnose CNTJP: jp (hl) ;Vector to the Test through the HL pair ; Vector Table dsptch: dw bpole ; 1 = bpole on serial port 1 dw bpole ; 2 = bpole on serial port 2 dw bpole ; 3 = bpole on centronics port dw loopbk ; 4 = loop back test on serial port 2 dw mtest ; 5 = memory test dw null ; 6 = floppy r/w test dw null ; 7 = floppy seek test dw null ; 8 = vfo test dw diagE ; 9 = boot system null: ld de,NrMsg ;DE:= Routine not installed call mesg ret diagE: pop hl ;Pull Return address from the stack ret ;Return to routine that called diagnose page ;---------------------------------------------------------------------- ; Subroutine: BPOLE ;------------------ ; ; This will print a barber-pole test pattern on the ports. The port ; to test is stored in location 'PORT'. 1 => serial port 1 ; 2 => serial port 2 ; 3 => centronics port ; ; bpole: ld a,20h ; initialize barberpole test ld (bgnchr),a ; a = character to print prntlp: ld a,05fh ; b = number of characters before cr/lf ld b,a ld a,(bgnchr) ; get character to start with outlp: call testout ; output the character dec b ; adjust character count jr z,shift ; if end, change start for next line inc a ; else, next character jp p,outlp ; send next character ld a,20h ; jump over control codes jr outlp ; then continue sending shift: call cstsIY ; see if a key has been typed ret nz ; if so, then end test ld a,cr ; send out cr, lf call testout ; output the character ld a,lf call testout ld a,(bgnchr) ; change starting character inc a jp m,bpole ; if 80h, then re-start ld (bgnchr),a jr prntlp ; then print next line page ;---------------------------------------------------------------------- ; Subroutine: TESTOUT ;-------------------- ; ; Output a character to a port, where port saved in memory location 'PORT' ; On entry, A = character to output. This routine calls the appropriate ; output handler. ; testout: push af push bc ld c,a ld a,(port) ; get port to test cp 1 ; see if port 1 jr nz,port2 ; if not, check if port 2 call sp1out ; if 1, test port 1 jr exitout ; restore, and return port2: cp 2 ; see if test for port 2 jr nz,cent ; no? then centronics call sp2out ; yes => use 2nd serial port jr exitout ; then restore and return cent: call cntout ; must be centronics test exitout: pop bc pop af ; restore registers before return ret page ;---------------------------------------------------------------------- ; Subroutine: LOOPBK ;------------------- ; ; Perform a loop-back test on serial port 2. Test ends when a key is pressed. ; loopbk: call sp2sts ;If (Serial port 2 is active) call nz,sp2in ; Flush the character looptst: ld d,a ; character to look for ld c,a ; character to send in C call sp2out ; output char in C call sp2in ; get char from port 2 to acc. cp d ; did we get what we sent? jr nz,looperr ; jump if not call cstsIY ; else, was console key hit? jr nz,loopgd ld a,d ; get next char to send inc a jp p,looptst ; and send it loopgd: ld de,passed ; point to success mesg jr msgout ; and send message looperr: ld de,failed ; point to fail message msgout: call mesg ; print message ld de,lpmsg call mesg ret page ;---------------------------------------------------------------------- ; Subroutine: MTEST (14_Nov_83) ;------------------------------ ; 1) Perform memory test continually, until a key is hit on keyboard. ; 2) Note that both banks of memory are tested; However, in this ; incarnation, if there is an error in testing bank1 then the test ; of bank 2 will be skipped. ; mtest: ld de,tstmsg ; print memory test msg call mesg call clrerr ;clear error count and pass count memlp: ld hl,0000h ;Repeat (lowest address to test) ld bc,combuf-1 ; (highest address to test) ld a,$BANK1 ; (Select Bank 1 for test) call ramtst ; test memory or a ; If (There were NO errors made) jr nz,memsk1 ld a,$BANK2 ; (Select Bank 2 for test) call ramtst ; test memory or a ; If (There were errors made) jr z,noerr memsk1: call incerr ; increment error count noerr: call xfrdat ; Move ramdatY & MTABs into ram call prnpas ; print pass info call cstsIY ; (see if key pressed) jr z,memlp ;Until (key pressed) ret ;Return ; clear errors and pass clrerr: xor a ld (mempas),a ld (errors),a ret ;increment error count incerr: ld a,(errors) inc a ld (errors),a ret ; print pass info for test prnpas: ld de,pasmsg ; point to pass message call mesg ; print it ld a,(mempas) ; get number of passes made inc a ; increment by one ld (mempas),a ; save it call outbyt ; print it ld de,coln call mesg ld a,(errors) ; get number of errors found call outbyt ; print them ld de,errnum ; and a message call mesg ret page ;====================================================================== ; MEMERR - Memory/Fatal Error (18_Dec_83) ;======================================== ; Function: When a bad memory location is detected during the memory test, ; execution comes here. If the diagnostics mode is set then control ; will pass to the start of the diagnostics routine; Else, an error ; message will be printed and the processer will be halted. ; memerr: ld a,0 ;select bank 1 and rom out (bnkstb),a in a,(cstat) ;If (diagnostics mode selected) and $DIAGM jp z,diagnose ; goto diagnostics jp fatal ;Else GOTO Fatal Error Termination ;---------------------------------------------------------------------- ; Subroutine: Chksum ;------------------- ; ; Function: This routine performs a checksum on a given block of ; memory. The memory may be ROM or RAM, but must be set ; such that the correct checksum is zero. The start of ; the block to be tested should be addressed by the HL ; register pair, and the number of bytes to check should ; be in the BC register pair. If the checksum is correct, ; then the accumulator will be zero upon return. ; If the accumulator is non-zero, then the memory is bad. ; The test used is a parity test, by column. The parity ; is computed by xor'ing all the bytes together. The last ; byte in the memory being tested is chosen to cause ; correct parity to yield a 0 after the xor's. ; chksum: xor a ; initialize checksum push af ; because it's popped later chk: pop af ; get current check byte value xor (hl) ; calculate new check byte inc hl ; next location to test dec bc ; decrement byte count push af ; save check byte ld a,b ; test if bc=0 or c ; b or c =0 ==> done jr nz,chk ; if not done, loop pop af ; get completed check byte ret ; and return it page ; ; *********************************** ; THIS CODE RUNS IN THE COMMON BUFFER ; *********************************** block: .phase combuf ;---------------------------------------------------------------------- ; DIAG1 - Initial Ram Test (23_Dec_83) ;------------------------------------- ; 1) This routine checks ram memory from location 0 to the start ; of the common area - 1. ; 2) This test is only performed if the hard disk drive is NOT ready. ; 3) The stack pointer must be up in the common area for this routine ; to return properly. ; 4) Notice that if there's an error then execution transferrs to the ; routine 'memerr' which outputs an error message and halts. ; diag1: ld a,0 set @HRDDSK,a out (drvstb),a ;Select the hard disk call PlsMtr ;Pulse the Motor On Line in a,(drvsts) ;If (the hard disk is ready) and $READY ld a,0 ; (Error:= None) ret nz ; Return test: ld de,tstmsg ;Else call mesg ; announce start of memory test ; Select bank 1 and test ld hl,0 ; HL:= starting address of test ld bc,combuf-1 ; BC:= highest address to test ld a,$BANK1 ; (select bank 1 for test) call ramtst ; Test Ram Memory or a ; If (memory failed) jp nz,memerr ; goto 'memerr' ; Select Bank 2 and test ld hl,0 ; HL:= starting address of test ld bc,combuf-1 ; BC:= highest address to test ld a,$BANK2 ; (Select bank 2 for test) call ramtst ; Test Ram Memory or a ; If (memory failed) jp nz,memerr ; goto 'memerr' ; RAM is OK ld de,romok ; Else call mesg ; Print memory ok message ld a,0 ret ; Return (test ok) page ;---------------------------------------------------------------------- ; RAMTST - General Purpose Ram Test (14_Nov_83) ;---------------------------------------------- ; 1) This routine tests a block of ram. The block can be of any ; length and start at any address so long as it doesn't go into ; the common area. ; 2) The pointer to the start of the pattern table is retrieved after ; the starting address saved, . The program then enters testlp where ; it reads and writes values into memroy according to the pattern ; table. ; 3) Before using this routine the bank strobe register must be loaded ; with the appropriate bank mask. ; 4) Register Usage: ; A -> Returned equal to 0 if the were NO errors (else non-0) ; BC -> Pointer to the last location to test ; HL -> Pointer to the first location to test ; ramtst: or $_RMENB ;(add rom disable to mem bank select) out (bnkstb),a ;Select Memory Bank_n, and disable rom ld (smem),hl ;smem:= Pointer to starting location ld hl,patlst ;HL:= pointer to the test table testlp: ld d,(hl) ;Loop D:= test value inc hl ; HL:= second byte of table entry bit 7,(hl) ; If (this is end of the table) jr nz,pass ; goto passed (memory ok) ld e,(hl) ; E:= initial offset + rd/wr bit 6,e ; test if read or write res 6,e ; remove the flag bit inc hl ; HL:= start of next table entry push hl ; save table pointer jr z,fill ; If (rd eq true) call check ; read & compare memory jr over ; Else fill: ld ix,wrmem ; (vector = write memory) call testm ; write memory over: pop hl ; restore table pointer jr testlp pass: ld a,00000000b ;set accumulator to pass out (bnkstb),a ;Enable the ROM and Bank 1 ret ;return success page ;---------------------------------------------------------------------- ; CHECK/TESTM - Read/Write the block of memory under test ;-------------------------------------------------------- ; 1) This routine either writes or reads and checks the value passed in ; the D register. Only every third location, from the start of the ; test memory (smem) up through the last location (HL), is accessed. ; 2) Register Usage: ; A -> General Purpose ; BC -> Pointer to the last location to test ; D -> The value to Write or the value to match ; E -> Offset from the starting address (0, 1 or 2) ; HL -> Pointer to the current test location ; Entry point #1 - jam IX to read and check a memory location routine check: ld ix,rdmem ;IX:= Read and compare a ram location ; Entry point #2 - get starting address of ram to test (IX already set) testm: ld hl,(smem) ;HL:= Starting address ld a,e ;A:= intial offset from start (0,1or2) add a,l ld l,a ld a,h adc a,0 ld h,a ;HL:= Starting address + offset tstlp: ld a,b ;While (current_addr(HL) le end_addr(BC)) cp h ; (ret c is insurance against ret c ; starting test past page limit) jr nz,tstskp ld a,c cp l ret c tstskp: call indjp ; Write or Read & test ram via IX inc hl inc hl inc hl ; HL:= HL + 3 jr tstlp ; Execute eiter wrmem or rdmem indjp: jp (ix) ;Write or Read and check a Ram location ; Write to a Ram location wrmem: ld (hl),d ;Write a ram location ret ; Read and Check a Ram location rdmem: ld a,(hl) ;Read a Ram location cp d ;If (value-read eq value-expected(D)) jr nz,err ret ; Return (accm=0) ; Memory Test Error err: ld a,00000000b ;Else [Error] out (bnkstb),a ; turn on rom call prnerr ; output full error msg pop af pop af pop af ; fix stack ld a,0FFH ret ; Return (accm-0FFh) page ;---------------------------------------------------------------------- ; PRNERR Print the memory error location ;--------------------------------------- ; ; This routine prints out the memory location at which the error occured, ; followed by the data read from RAM, and what it expected to read. ; prnerr: push de ld de,rdmsg ; point to message call mesg ; print it ld a,00000000b or $BANK2 out (bnkstb),a ; get ROM out of the way ld a,(hl) ; get value read ld c,a ld a,00000000b out (bnkstb),a ; turn ROM back on ld a,c call outbyt ; output it in hex ld de,expmsg ; point to message call mesg ; print it pop de ; get back expected value ld a,d call outbyt ; print expected value in hex ld de,rammsg ; point to ram error mesg call mesg ; print it, ld a,h ; get high byte of address call outbyt ; print it ld a,l ; get low byte of address call outbyt ; print it ret page ;---------------------------------------------------------------------- ; PATLST Memory Test Patterns (1_Sept_83) ;---------------------------------------- ; 1) Each Table Entry consists of two bytes ; 1st byte = value to use (0-ffh) ; 2nd byte = combination of offset and read(rd)/write(wr) flag ; bit ; 7 - ; 6 Read(1)/Write(0) Flag ; 5 - ; 4 - ; 3 - ; 2 - ; 0 - 1 Initial Offset from starting address (0,1 or 2) ; 2) The table is terminated by a two byte entry whose second byte's ; most signifigant bit is set (00,80h). ; patlst: db 0AAh, wr or 0 db 55h, wr or 1 db 0AAh, wr or 2 db 55h, rd or 1 db 0AAh, rd or 2 db 0AAh, rd or 0 db 55h, wr or 0 db 0AAh, wr or 1 db 55h, wr or 2 db 55h, rd or 2 db 55h, rd or 0 db 0AAh, rd or 1 db 00, 80h ; msb=1 ==> end of pattern list ;---------------------------------------------------------------------- ; Test Module Address Definitons (12_Nov_83) ;------------------------------------------- ; smem: dw 0 ;Holds address of the first location to test port: db 0 ;port to test stored here by diagnose bgnchr: db 0 ;starting character for barber-pole test mempas: db 0 ;number of memory passes errors: db 0 ;number of memory errors made endblk: .dephase page ;---------------------------------------------------------------------- ; Text Strings (17_Nov_83) ;------------------------- ; rommsg: db bell,bell,'ROM Memory error',0 rambad: db bell,bell,'RAM Memory error',0 diagmsg: db cr,lf,'Diagnostics Menu',cr,lf,lf db '1) Port 1 \',cr,lf db '2) Port 2 > Barber-Pole pattern test',cr,lf db '3) Centronics port /',cr,lf db '4) Loop back on port 2',cr,lf db '5) RAM test',cr,lf db '6) FDC R/W',cr,lf db '7) FDC seek test',cr,lf db '8) VFO test',cr,lf db '9) Boot',cr,lf,lf db 'Enter #:',0 passed: db cr,lf,'Passed ',0 failed: db cr,lf,'Failed ',0 lpmsg: db 'loop test.',0 tstmsg: db 'Testing memory -- please stand by: ',0 pasmsg: db cr,lf,'End of pass ',0 errnum: db ' errors so far.',0 romok: db 'Memory O.K.',cr,lf,lf,0 rdmsg: db bell,bell,cr,lf,'Read ',0 expmsg: db ' expected ',0 rammsg: db ' at RAM location ',0 TeMesg: db 'Testing Error ',0 NrMsg: db 'Routine Not Installed ',0 ; Print a Carriage Return Line Feed ;---------------------------------- ; crlf: db 0Ah, 0Dh, 0 ; Print a Colon Followed by a Space ;---------------------------------- ; coln: db ': ',0 if ($ lt o_hirm - 1) ;If (we haven't overwritten hd*hirm) e_test: ds (o_hirm - $) - 1, 0 ; Zero Fill to the hirm module else ;Else if2 ; print an error message on pass2 .printx "The test module has overflowed into the hirm module" endif endif end