TITLE HARD DECISION ROM SOURCE REV 0.0 May 3, 1983 ; HARD-DECISION ; ROM REV. 0.0 ; COPYRIGHT 1983 ; MORROW DESIGNS, INC. ; 5/3/83 ; REV equ 20h ; ROM EQU 0 ; .Z80 ASEG ; ; ORG ROM JP START JP CENTOUT ; Module name: Microtest ; ; 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 ; ; system equates ; diskbuf equ 0fc00h ; start of disk sector buffer memtop equ 0ffffh ; top of ram port equ memtop-1 ; port to test stored here by diagnose bgnchar equ memtop-2 ; starting character for barber-pole test mempass equ memtop-1 ; number of memory passes errors equ memtop-2 ; number of memory errors made bankstb equ 041h ; bank select register port s1data equ 060h ; console port data s1stat equ 061h ; console port status s2data equ 062h ; printer port data s2stat equ 063h ; printer port status cdata equ 042h ; centronics data port cstat equ 042h ; centronics status port cr equ 0dh ; carriage return lf equ 0ah ; line feed bell equ 07h ; bell wr equ 0 ; for building test pattern.. rd equ 40h ; ..for memory diagnostics ramchk equ 0ffh ; set to checksum for ram romchk equ 0ffh ; set to checksum for rom ; ; start: im 0 ; set interrupt mode 0 ld a,10h out (bankstb),a ; set auto enable xor a out (bankstb),a ; clear auto enable ld sp,00h ; assume that ram is ok call initctc ; initialize counter-timer chip call inituart ; initialize UARTS ld de,signon ; point to signon message call clrmsg ; and print signon call romtst ; check rom call move ; move memtest call bufchk ; check disk buffer ; ram space call stblk ; test rest of ram if ; necessary, then boot ; ; warm: call diagnose ; call diagnostics test ; end of main ; ; Subroutine: INITUART ; ; Function: This routine initializes both channels of the DARTS. ; They are set for 8 bits, no parity, x1 clock rate, and ; 2 stop bits. In addition, DTR, and RTS, are programmed ; to be on. ; inituart: ld a,18h ; send channel reset commands out (s1stat),a out (s2stat),a ld a,10h ;reset ext/status interrupts out (s1stat),a out (s2stat),a ; end of reset sequence ; ld a,04h ;select register 4 out (s1stat),a out (s2stat),a ld a,4ch ;no par, x16, 2 stops out (s1stat),a out (s2stat),a ; ld a,03h ;select register 3 out (s1stat),a out (s2stat),a ld a,0c1h ;Rx 8 bits, auto off, Rx enabled out (s1stat),a out (s2stat),a ; ld a,05h ;select register 5 out (s1stat),a out (s2stat),a ld a,068h ;Tx 8 bits, DTR/RTS off, Tx enabled out (s1stat),a out (s2stat),a ; ld a,10h ;reset ext/status interupts again out (s1stat),a out (s2stat),a ; ld a,01h ;select register 1 out (s1stat),a out (s2stat),a ld a,0h ;disable all interrupts here out (s1stat),a out (s2stat),a RET ; ; Subroutine: INITCTC ; ; Function: This routine initializes the counter timer chip to provide ; baud rate clocks for the DARTS. The definition of the use of ; the channels is as follows: ; 0 - Baud rate for serial port 2 ; ( set for 9600 baud) ; 1 - Baud rate for serial port 1 ; ( set for 9600 baud) ; 2 - Baud rate for serial port 0 ; ( set for 9600 baud) ; baudset equ 053h ; ctc channel select/mode port baud2 equ 050h ; baud 2 rate control port baud1 equ 051h ; baud 1 rate control port baud0 equ 052h ; baud 0 rate control port ; initctc: ld a,3eh ;set mode 3, channel 0 out (baudset),a ;used for baud 2 ld a,1Ah out (baud2),a ld a,00H out (baud2),a ld a,7eh ;set mode 3, channel 1 out (baudset),a ;set to 9600 baud ld a,1Ah ;this is baud 1 out (baud1),a ld a,00h out (baud1),a ld a,0beh ;set mode 3, channel 2 out (baudset),a ;set to 9600 baud ld a,1Ah ;this is baud 0 out (baud0),a ld a,00h out (baud0),a ; channel two set to 1200 baud ret ; ; ; Subroutine: ROMTST ; ; 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: ; perform a checksum on the rom ret ld hl,0 ; starting address in rom ld bc,0ffdh ; number of bytes to check 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 jr memerr ; print it, and halt ; end of romtst ; ; Subroutine: MEMERR ; ; Function: When a bad memory location is detected during ; the memory test, execution comes here. This routine ; prints an error message, and halts the processor. ; memerr: ; in a,(0f5h) ; check if diagnostics selected ; bit 5,a ; jp z,warm ; if diagnostics selected, go to diag mode ld de,rerr ; point to fatal error message call mesg halt ; halt processor ; end of memerr ; ; Subroutine BUFCHK ; ; Function: This routine calculates a checksum on the disk ; buffer RAM at location FC00h to FFFFh as an indication ; of system integrity. If the test passes, then the routine ; will return, and the accumulator will be equal to zero. ; If a bad checksum is computed, then execution will be ; passed to memerr. ; bufchk: ; set up pointers to test top of RAM ret ld hl,diskbuf ; low address ld bc,3f8h ; # of bytes to test call chksum ; test diskbuf area ret z ; return if O.K. ld de,rambad ; point to rammsg call mesg ; print ram bad message jr memerr ; jmp to error ; end of bufchk ; ; 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 ; end of chksum ; ; ; Subroutine: MOVE ; ; Function: This routine moves the memory test program to ; the disk buffer area, and pads the disk buffer ram ; such that when a checksum is computed on the buffer ; area, the result will be zero. ; move: ; set up to move prog to ram ld hl,block ; from.... ld de,diskbuf ; to.... ld bc,endblk-stblk ; how many... ldir ; move it!!!! ; ld bc,3f7h-endblk+stblk ; number of bytes to pad with push de ; save this pop hl ; get it for source of ldir inc de ; dest for ldir ld (hl), 0ffh ; initial padd ldir ; walk up and fill ld (hl),ramchk ; set checksum ret ; ; ; ;Following are output routines for both serial ports. When SEROUT is called, ; it expects the character to output to be in the C register, and the B reg. ; is to contain the port address for the serial port to be used. ; ser2out: push bc ld b,s2stat ;use port 2 jr serout ; use general purpose output routine ; CNOUT: push bc ld b,s1stat ; use console port for I/O ; serout: ld a,b ; switch port and character registers ld b,c ; now, b has character to output ld c,a ; c has port to use ; chkstatO: ; check output status IN A,(C) ;CONSOLE OUTPUT ROUTINE BIT 2,A ;SEE IF RDY JR Z,chkstatO ;JMP IF NOT RDY ; LD A,B ;OUTPUT CHAR. dec c ; point to data port OUT (C),A pop bc RET ; ;General purpose serial input routines ; ser2in: ld b,s2stat ;point to 2nd status port jr serin ;use general purpose input routine ; CNIN: ld b,s1stat ;set up for 1st status port serin: ld c,b ;move port to use to C reg chkstatI: ; check input status IN A,(C) ;serial port INPUT ROUTINE BIT 0,A ;SEE IF RDY JR Z,chkstatI ;JMP IF NOT RDY ; dec c IN A,(C) ;GET CHAR. AND 7FH ;MASK OFF PARITY RET ; ; Centronics port driver ; This routine outputs the character in register C to the Centronics port. ; After the character is output, the routine will monitor the ACK Bit from ; the port, and if no acknowledge is found within 1ms, the routine returns ; with the Acc. non zero. If the character is acknowledged, then the Acc. ; will be 0 upon return. ; acktime equ 064 ; centout: push bc in a,(cstat) ; check printer RDY line bit 0,a jr nz,centout ; loop until printer ready ; ld a,c out (cdata),a ; output char to Cent. data port ld a,08h ; send strobe to printer out (bankstb),a xor a out (bankstb),a ; strobe done ; ld hl,acktime ; load timeout value for ACK ; acklp: in a,(cstat) ; check ACK bit bit 3,a jr z,ackok ; if cleared, return success flag dec hl ; if not cleared, update timeer ld a,l or h ; see if hl=0 => timeout jr nz,acklp ; if not timeout, keep looking dec a ; if timeout, set Acc. to 0ffh pop bc ret ackok: xor a ; Acknowledged, so set for printer pop bc ret ; ok, then return ; CLRMSG: LD B,50 ;LINE COUNT CLRLP: LD c,LF CALL cnout DJNZ CLRLP ; MESG: LD A,(DE) OR A RET Z ld c,a CALL cnout INC DE JR MESG ; ; ; CNTJP: jp (hl) ; ; ; ; Subroutine: DIAGNOSE ; ; This routine is used to test the various functional blocks of the hardware. ; 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 cnin ; get test to run cp '1' ; adjust and check if in range jp m,inlp ; between 1, and... cp 3ah ; ... 9 jp p,inlp push af call cnout ; output test number ld de,crlf ; if valid #, send cr/lf call mesg pop af sub '0' ; A = test number (1-9) cp 4 ; if bpole test, set up port jp p,notbp ; don't set up port if not bpole test ld (port),a ; set up port to use for bpole test notbp: dec a ; set base offset of 0 rlca ; calculate offset in dispatch 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 = address of vector for selected ; diagnostic test ld e,(hl) inc hl ld d,(hl) ex de,hl call cntjp ; to indirect jump jr diagnose ; then return to menu ; dsptch: dw bpole ; test 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 null ; boot system ; ; 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 (bgnchar),a ; a = character to print prntlp: ld a,05fh ; b = number of characters before cr/lf ld b,a ld a,(bgnchar) ; 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 keystat ; 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,(bgnchar) ; change starting character inc a jp m,bpole ; if 80h, then re-start ld (bgnchar),a jr prntlp ; then print next line ; ; 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 cnout ; 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 ser2out ; yes => use 2nd serial port jr exitout ; then restore and return cent: call centout ; must be centronics test exitout: pop bc pop af ; restore registers before return ret ; ; ; keystat: in a,(s1stat) ; read console status bit 0,a ; see if key hit ret ; ; Subroutine: LOOPBK ; ; Perform a loop-back test on serial port 2. Test ends when a key is pressed. ; loopbk: in a,(s2data) ; clear any character in buffer xor a ; starting character for test is null looptst: ld d,a ; character to look for ld c,a ; character to send in C call ser2out ; output char in C call ser2in ; get char from port 2 to acc. cp d ; did we get what we sent? jr nz,looperr ; jump if not call keystat ; 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,loopmsg call mesg ret ; ; Subroutine: MTEST ; ; Perform memory test continually, until a key is hit on keyboard. ; mtest: ld de,tstmsg ; print memory test msg call mesg call clrerr ; clear error count and pass count memlp: ld hl,0000h ; lowest address to test ld bc,diskbuf-1 ; highest address to test call memtest ; test memory or a ; test flags jr z,noerr ; if no errors, increment pass call incerr ; else, increment error count noerr: call prntpass ; print pass info call keystat ; see if key pressed jr z,memlp ; keep testing if no key ret ; else, return to test menu ; ; clrerr: xor a ; clear errors and pass ld (mempass),a ld (errors),a ret ; ; incerr: ld a,(errors) ; increment error count inc a ld (errors),a ret ; prntpass: ; print pass info for test ld de,passmsg ; point to pass message call mesg ; print it ld a,(mempass) ; get number of passes made inc a ; increment by one ld (mempass),a ; save it call outbyte ; print it ld de,coln call mesg ld a,(errors) ; get number of errors found call outbyte ; print them ld de,errnum ; and a message call mesg ret ; ; ; null: ret ; ; ; This section of code is run at FC00h!!!! ; ; block: .phase diskbuf ; stblk: ld a,01h out (bankstb),a ; turn off rom ld a,(00) ; look at location 0 in RAM cp 0c3h ; see if it is a jump ld a,0h ; turn ROM back on out (bankstb),a ; jr nz,test ; if not a jump, then test memory ; ; ; halt ; ; ; test: ld de,tstmsg ; point to test msg. call mesg ; print it ld hl,0000h ; low addr to test ld bc,diskbuf-1 ; highest to test call memtest ; test memory or a ; test flags jp nz,memerr ; if memfail, then jump ld de,romok ; point to memory ok message call mesg ; and print it jp warm ; if ok, then go ; ; Subroutine: MEMTEST ; ; Function: This routine performs a memory test on a block of memory. ; The block of memory may be of any size, and is determined ; by the values passed upon entry. ; When called, register pair HL points to the start of the ; block to be tested, and register pair BC points to the last ; location to be tested. When an error is encountered, the ; routine will return a non-zero value in the accumulator. ; When no errors are found, the routine will return with ; the accumulator equal to zero. ; memtest: ld a,3 out (bankstb),a ; turn off ROM ld (smem),hl ; save start pointer ld hl,patlst ; point to test table testlp: ld d,(hl) ; get data to test inc hl bit 7,(hl) ; check if end of table jr nz,pass ; if so, then passed ld e,(hl) ; get offset(0,1,2)& rd/wr bit 6,e ; test if read or write res 6,e ; reset the bit inc hl push hl ; save table pointer jr z,fill ; 0==> fill memory call check ; 1==> check memory jr over fill: ld ix,wrmem ; calculate start call testm ; test memory over: pop hl ; restore table pointer jr testlp ; loop while not done pass: xor a ; set accumulator to pass out (bankstb),a ; turn ROM back on ret ; return success check: ld ix,rdmem ; set for read testm: ld hl,(smem) ; get where to start LD A,E ; get offset from start ADD A,L ; compute actual starting LD L,A ; address ; hl<==hl+offset LD A,H ; ADC A,0 LD H,A ; lp: call indjp ; indirect jump via ix ; ; INC HL ; skip two bytes ; INC HL INC HL LD A,B CP H RET C jr NZ,lp ; if top not reached, keep going LD A,C CP L RET C jr lp ; indjp: jp (ix) ; perform jump to wr or rd ; wrmem: LD (HL),D ; write test value ; ld a,(hl) ret ; rdmem: ld a,(hl) ; get test value cp d jr nz,err ret ; ERR: xor a out (bankstb),a ; made error, so turn on ROM call outhl ; output full error msg if in ; diagnostics mode ; ; ld a,1 out (bankstb),a ; turn off rom rdbad: ld (hl),00 ; read bad address ld a,(hl) halt jr rdbad ; ; ; pop af pop af pop af LD A,0FFH ret ; ; Subroutine: OUTHL ; ; 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. ; outhl: push de ld de,readmsg ; point to message call mesg ; print it ld a,01h out (bankstb),a ; get ROM out of the way ld a,(hl) ; get value read ld c,a xor a out (bankstb),a ; turn ROM back on ld a,c call outbyte ; output it in hex ld de,expctmsg ; point to message call mesg ; print it pop de ; get back expected value ld a,d call outbyte ; 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 outbyte ; print it ld a,l ; get low byte of address call outbyte ; print it ret ; ; Subroutine: OUTBYTE ; ; This will print the hex value of the accumulator. ; outbyte: push af ; save value rrca ; get upper nibble rrca rrca rrca and 0fh ; mask off the rest call outasc ; print ascii hex digit pop af ; get value back and 0fh ; mask call outasc ; print ascii hex digit ret ; ; Subroutine: OUTASC ; ; This routine outputs the lower nibble of the accumulator as a hex digit. ; outasc: cp 0ah ; see if digit or alpha jp m,num ; jump if digit add a,07h ; adjust for alpha num: add a,30h ; convert to ascii ld c,a call cnout ; print it ret ; ; ; ; ; ; ; ; MEMORY PATTERN TEST TABLE ; ; format is: ; 1st byte -- value to use (0-ffh) ; 2nd byte -- bit 6 r/w flag 0 => write ; 1 => read ; bits [0,1] offset (0,1,2) ; ; patlst: db 0ffh,wr or 0 ; db 00,wr or 1 ; db 0ffh,wr or 2 ; db 00,rd or 1 ; db 0ffh,rd or 2 db 0ffh,rd or 0 db 00,wr or 0 ; db 0ffh,wr or 1 ; db 00,wr or 2 ; db 00,rd or 2 db 00,rd or 0 ; db 0ffh,rd or 1 db 0aah,wr or 0 ; db 55h,wr or 2 ; db 0aah,wr or 1 ; db 0aah,rd or 1 ; db 55h,wr or 1 db 0aah,rd or 0 db 55h,wr or 0 ; db 55h,rd or 2 ; db 55h,rd or 1 db 55h,rd or 0 db 00,80h ; msb=1 ==> end of pattern list ; SMEM equ $ endblk: ; .dephase ; ; ----------------- Messages --------------- RERR: DW 0A0DH DC 'Push reset to try again. ' DB 0 ; signon: db cr,'Hard-Decision Rom Copyright Fluglle enterprizes, Inc.' db cr,lf,0 ; tstmsg: db 'Testing memory -- please stand by: ',0 romok: db 'Memory O.K.',cr,lf,lf,0 bootmsg: db 'Insert CP/M system diskette in Drive A' rtrnmsg: db ' and press [RETURN] ',bell,0 rommsg: db bell,bell,'ROM Memory error',0 rambad: db bell,bell,'RAM Memory error',0 readmsg: db bell,bell,cr,lf,'Read ',0 expctmsg: db ' expected ',0 rammsg: db ' at RAM location ',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 passmsg: db cr,lf,'End of pass ',0 errnum: db ' errors so far.',0 loopmsg: db 'loop test.',0 failed: db cr,lf,'Failed ',0 passed: db cr,lf,'Passed ',0 drvmsg: db 'Enter drive to test (A-D):',0 insrt: db cr,lf,'Insert a formatted diskette in drive ',0 wrtmsg: db cr,lf,'Writing...',0 rdmsg: db 'reading',0 CRLF: DW 0A0DH DB 0 ; ; COLN: DC ': ' DB 0 ; ; ; END