; Z80TYPE.ASM - DETERMINE AND PRINT Z80 CPU TYPE ; WRITTEN BY SERGEY KISELEV ; Adapted by Jan Bobrowski, 2024-10-04 ; Use ; mktap -b -p ';;;' z80type 2 z80type.tap ; mktap z80type 24576 >z80type.tap ; [torinak.com/~jb/zx/#mktap] ;;; 1 GOTO 5 ;;; 2 CLEAR 24575 ;;; 3 LOAD ""CODE ;;; 4 CLS ;;; 5 RANDOMIZE USR 24576 CHAN_OPEN equ 0x1601 BORDCR equ 0x5C48 org 0x6000 ld a, 2 call CHAN_OPEN LD DE,MSGSIGNIN CALL PRINTSTR CALL TESTCMOS LD HL,ISCMOS LD (HL),A ; store result to ISCMOS CALL TESTU880 LD HL,ISU880 LD (HL),A ; store result to ISU880 CALL TESTXY LD HL,XYRESULT LD (HL),A ;------------------------------------------------------------------------- ; Debug LD HL,DEBUG LD A,(HL) CP 0 JP Z,DETECTCPU LD DE,MSGRAWCMOS ; display CMOS test result CALL PRINTSTR LD HL,ISCMOS LD A,(HL) cp 055h jr z, .unkn CALL PRINTHEX jr .next .unkn: ld de,msgunkn call PRINTSTR .next: LD HL,ISU880 LD A,(HL) ; store result to ISU880 LD DE,MSGRAWU880 ; display U880 test result CALL PRINTSTR CALL PRINTHEX LD HL,XYRESULT LD A,(HL) LD DE,MSGRAWXY ; display XF/YF flags test result CALL PRINTSTR CALL PRINTHEX LD DE,MSGCRLF CALL PRINTSTR CALL TESTFLAGS ; TEST HOW FLAGS SCF AFFECTS FLAGS ;------------------------------------------------------------------------- ; CPU detection logic DETECTCPU: LD DE,MSGCPUTYPE CALL PRINTSTR ; check for U880 CPU LD HL,ISU880 LD A,(HL) CP 0 ; Is it a U880? JP Z,CHECKZ80 ; check Z80 flavor LD HL,XYRESULT LD A,(HL) CP 0FFH ; does it always set XF/YF? LD DE,MSGU880NEW JP Z,DONE ; jump if a new U880/Thesys Z80 LD DE,MSGU880OLD JP DONE ; check for Z80 type CHECKZ80: LD HL,ISCMOS LD A,(HL) ; A value of 55 indicate no test performend; assuming NMOS CP 0ffh ; Is it a NMOS CPU? JP Z,CHECKCMOS ; check CMOS Z80 flavor ; check for Sharp LH5080A LD HL,XYRESULT LD A,(HL) CP 30H JP Z,SHARPLH5080A CP 0FFH ; does it always set XF/YF? JP Z,NMOSZ80 CP 0FDH ; does it sometimes not set XF when FLAGS.3=1? JP Z,NECU780C CP 0F4H JP Z,KR1858VM1 LD DE,MSGNMOSUNKNOWN JP DONE SHARPLH5080A: LD DE,MSGSHARPLH5080A JP DONE NMOSZ80: LD DE,MSGNMOSZ80 JP DONE NECU780C: LD DE,MSGNECD780C JP DONE KR1858VM1: LD DE,MSGKR1858VM1 JP DONE CHECKCMOS: LD HL,XYRESULT LD A,(HL) CP 0FFH ; does it always set XF/YF? JP Z,CMOSZ80 CP 3FH ; does it never set YF when A.5=1? JP Z,TOSHIBA ; test for NEC D70008AC. These CPUs seem to behave as following: ; A.5=1 & F.5=0 => YF=1 ; A.3=1 & F.3=0 => XF is not set at all, or only sometimes is set ; A.5=0 & F.5=1 => YF is sometimes set ; A.3=0 & F.3=1 => XF is sometimes set ; Note: All of 3 D70008AC that I have behave a bit differently here ; this might need to be updated when more tests are done CP 20H ; YF is often set when A.5=1? JP NC,CMOSUNKNOWN ; XYRESULT > 1Fh, not a NEC... AND 0FH ; F.5=1 & A.5=0 and F.3=1 & A.3=0 results CP 03H ; F.5=1 & A.5=0 never result in YF set? JP C,CMOSUNKNOWN AND 03H ; F.3=1 & A.3=0 results JP NZ,NEC CMOSUNKNOWN: LD DE,MSGCMOSUNKNOWN JP DONE CMOSZ80: LD DE,MSGCMOSZ80 JP DONE TOSHIBA: LD DE,MSGTOSHIBA JP DONE NEC: LD DE,MSGNECD70008AC JP DONE DONE: CALL PRINTSTR LD DE,MSGCRLF CALL PRINTSTR RET ;------------------------------------------------------------------------- ; TESTCMOS - Test if the CPU is a CMOS variety according to OUT (C),0 test ; Note: CMOS Sharp LH5080A is reported as NMOS ; Input: ; None ; Output: ; A = 00 - NMOS ; A = FF - CMOS ;------------------------------------------------------------------------- TESTCMOS: ld bc, 0FFFDh xor a out (c), a ld b, 0BFh ld a,55h out (c), a ld b, 0FFh in d, (c) cp d ret nz ; No such port ld b, 0BFh DB 0EDH, 071H ; UNDOCUMENTED OUT (C),<0|0FFH> INSTRUCTION ; WRITE 0 OR FF TO THE SIO INTERRUPT VECTOR ld b, 0FFh in a, (c) ret ;------------------------------------------------------------------------- ; TESTU880 - Check if the CPU is MME U880 or Thesys Z80 ; Input: ; None ; Output: ; A = 0 - Non-U880 ; A = 1 - U880 ;------------------------------------------------------------------------- TESTU880: LD HL,0FFFFH ld a, (BORDCR) rra rra rra ld (hl), a xor a ld bc, 0x01fe SCF outi adc a, 0 ret ;------------------------------------------------------------------------- ; TESTXY - Tests how SCF (STC) instruction affects FLAGS.5 (YF) and FLAGS.3 (XF) ; Input: ; None ; Output: ; A[7:6] - YF result of F = 0, A = C | 0x20 & 0xF7 ; A[5:4] - XF result of F = 0, A = C | 0x08 & 0xDF ; A[3:2] - YF result of F = C | 0x20 & 0xF7, A = 0 ; A[1:0] - XF result of F = C | 0x08 & 0xDF, A = 0 ; Where the result bits set as follows: ; 00 - YF/XF always set as 0 ; 11 - YF/XF always set as 1 ; 01 - YF/XF most of the time set as 0 ; 10 - YF/XF most of the time set as 1 ;------------------------------------------------------------------------- TESTXY: LD C,0FFH ; loop counter di TESTXY1: LD HL,XFYFCOUNT ; results stored here ; check F = 0, A = C | 0x20 & 0xF7 LD E,00H ; FLAGS = 0 LD A,C OR 020H ; A.5 = 1 AND 0F7H ; A.3 = 0 LD D,A ; A = C | 0x20 & 0xF7 PUSH DE ; PUSH DE TO THE STACK POP AF ; POP A AND FLAGS FROM THE STACK (DE) SCF ; SET CF FLAG, DEPENDING ON THE CPU TYPE THIS ; ALSO MIGHT CHANGE YF AND XF FLAGS CALL STOREYCOUNT ; check F = 0, A = C | 0x08 & 0xDF LD E,00H ; FLAGS = 0 LD A,C OR 08H ; A.3 = 1 AND 0DFH ; A.5 = 0 LD D,A ; A = C | 0x08 & 0xDF PUSH DE ; PUSH DE TO THE STACK POP AF ; POP A AND FLAGS FROM THE STACK (DE) SCF ; SET CF FLAG, DEPENDING ON THE CPU TYPE THIS ; ALSO MIGHT CHANGE YF AND XF FLAGS CALL STOREXCOUNT ; check F = C | 0x20 & 0xF7, A = 0 LD A,C OR 020H ; FLAGS.5 = 1 AND 0F7H ; FLAGS.3 = 0 LD E,A ; FLAGS = C | 0x20 & 0xF7 LD D,00H ; A = 0 PUSH DE ; PUSH DE TO THE STACK POP AF ; POP A AND FLAGS FROM THE STACK (DE) SCF ; SET CF FLAG, DEPENDING ON THE CPU TYPE THIS ; ALSO MIGHT CHANGE YF AND XF FLAGS CALL STOREYCOUNT ; check F = C | 0x08 & 0xDF, A = 0 LD A,C OR 08H ; FLAGS.3 = 1 AND 0DFH ; FLAGS.5 = 0 LD E,A ; FLAGS = C | 0x08 & 0xDF LD D,00H ; A = 0 PUSH DE ; PUSH DE TO THE STACK POP AF ; POP A AND FLAGS FROM THE STACK (DE) SCF ; SET CF FLAG, DEPENDING ON THE CPU TYPE THIS ; ALSO MIGHT CHANGE YF AND XF FLAGS CALL STOREXCOUNT DEC C JP NZ,TESTXY1 ei LD C,4 ; iteration count - number of bytes LD HL,XFYFCOUNT ; counters TESTXY2: RLA RLA AND 0FCH ; zero two least significant bits LD B,A ; store A to B LD A,(HL) CP 7FH JP NC,TESTXY3 ; jump if the count is 0x80 or more CP 0 JP Z,TESTXY5 ; the count is 0 leave bits at 0 LD A,1 ; the count is between 1 and 0x7F, set result bits to 01 JP TESTXY5 TESTXY3: CP 0FFH LD A,2 ; the count is between 0x80 and 0xFE, set result bits to 10 JP NZ,TESTXY4 LD A,3 ; the count is 0xFF, set result bits to 11 JP TESTXY5 TESTXY4: ; Bug! ; LD A,1 ; the count is 0x7F or less, set result bits to 01 TESTXY5: OR B INC HL DEC C JP NZ,TESTXY2 RET ;------------------------------------------------------------------------- ; STOREXCOUNT - Isolates and stores XF to the byte counter at (HL) ; Input: ; FLAGS - flags ; HL - pointer to the counters ; Output: ; HL - incremented by 1 (points to the next counter) ; Trashes A and DE ;------------------------------------------------------------------------- STOREXCOUNT: PUSH AF ; transfer flags POP DE ; to E register LD A,E AND 08H ; isolate XF JP Z,STOREXDONE INC (HL) ; increment the XF counter (HL) STOREXDONE: INC HL ; point to the next entry RET ;------------------------------------------------------------------------- ; STOREYCOUNT - Isolates and stores YF to the byte counter at (HL) ; Input: ; FLAGS - flags ; HL - pointer to the counters ; Output: ; HL - incremented by 1 (points to the next counter) ; Trashes A and DE ;------------------------------------------------------------------------- STOREYCOUNT: PUSH AF ; transfer flags POP DE ; to E register LD A,E AND 20H ; isolate YF JP Z,STOREYDONE INC (HL) ; increment the YF counter (HL) STOREYDONE: INC HL ; point to the next entry RET ;------------------------------------------------------------------------- ; TESTFLAGS - TEST HOW SCF INSTRUCTION AFFECTS YF AND XF FLAGS ; NOTE: YF IS FLAGS.5 AND XF IS FLAGS.3 ; INPUT: ; NONE ; OUTPUT: ; PRINTED ON CONSOLE ;------------------------------------------------------------------------- TESTFLAGS: LD DE,MSGFLAGS call PRINTSTR LD D,00H TFLOOP1: LD E,00H TFLOOP2: PUSH DE DI PUSH DE ; PUSH DE TO THE STACK POP AF ; POP A AND FLAGS FROM THE STACK (DE) CCF ; SET CF FLAG, DEPENDING ON THE CPU TYPE THIS ; ALSO MIGHT CHANGE YF AND XF FLAGS PUSH AF ; STORE A AND F POP DE ; NEW FLAGS IN E EI LD A,E ; FLAGS TO ACCUMULATOR POP DE JP CONT PRINTFLAGS: CALL PRINTHEX ; PRINT ACCUMULATOR LD A,E ; FLAGS TO ACCUMULATOR POP DE PUSH AF LD A,D ; PRINT ORIGINAL ACCUMULATOR(FLAGS) CALL PRINTHEX POP AF CALL PRINTHEX ; PRINT NEW FLAGS PUSH DE LD DE,MSGCRLF CALL PRINTSTR POP DE CONT: LD HL,XFCOUNT ; POINT TO XF COUNTER RRCA ; BIT 3 TO CF RRCA RRCA RRCA JP NC,TFLOOP4 INC (HL) ; INCREMENT COUNTER IF FLAG IS SET JP NZ,TFLOOP4 ; NO OVERFLOW INC HL ; MOVE TO THE HIGH BIT INC (HL) ; INCREMENT HIGHER BIT TFLOOP4: LD HL,YFCOUNT ; POINT TO YF COUNTER RRCA ; BIT 5 TO CF RRCA JP NC,TFLOOP5 INC (HL) ; INCREMENT COUNTER IF FLAG IS SET JP NZ,TFLOOP5 ; NO OVERFLOW INC HL ; MOVE TO THE HIGH BIT INC (HL) ; INCREMENT HIGHER BIT TFLOOP5: INC E JP NZ,TFLOOP2 INC D ; INCREMENT D JP NZ,TFLOOP1 ; PRINT VALUES LD C,4 ; 4 BYTES LD HL,YFCOUNT+1 ; POINT AT THE MSB TFLOOP6: LD A,(HL) CALL PRINTHEX DEC HL DEC C JP NZ,TFLOOP6 ; PRINT NEXT DIGIT LD DE,MSGCRLF jp PRINTSTR ; PRINT VALUES LD HL,YFCOUNT+1 ; MSB OF YF COUNT LD A,(HL) CALL PRINTHEX DEC HL ; LSB OF YF COUNT LD A,(HL) CALL PRINTHEX LD HL,XFCOUNT+1 ; MSB OF XF COUNT LD A,(HL) CALL PRINTHEX DEC HL ; LSB OF XF COUNT LD A,(HL) CALL PRINTHEX LD DE,MSGCRLF jp PRINTSTR ;------------------------------------------------------------------------- ; PRINTHEX - PRINT BYTE IN HEXADECIMAL FORMAT ; INPUT: ; A - BYTE TO PRINT ; OUTPUT: ; NONE ;------------------------------------------------------------------------- PRINTHEX: PUSH BC PUSH DE PUSH HL PUSH AF ; SAVE PRINTED VALUE ON THE STACK RRCA ; ROTATE HIGHER 4 BITS TO LOWER 4 BITS RRCA RRCA RRCA CALL PRINTDIGIT ; PRINT HIGHER 4 BITS POP AF ; RESTORE PRINTED VALUE PUSH AF ; PUSH IT TO THE STACK AGAIN CALL PRINTDIGIT ; PRINT LOWER 4 BITS POP AF POP HL POP DE POP BC RET ;------------------------------------------------------------------------- ; PRINTDIGIT - PRINT DIGIT IN HEXADECIMAL FORMAT ; INPUT: ; A - DIGIT TO PRINT, LOWER 4 BITS ; OUTPUT: ; NONE ; TRASHES REGISTERS A, FLAGS, BC, DE, HL ;------------------------------------------------------------------------- PRINTDIGIT: AND 0FH ; ISOLATE LOWER 4 BITS ADD A,'0' ; CONVERT TO ASCII CP '9'+1 ; GREATER THAN '9'? JP C,PRINTIT ADD A,'A'-'9'-1 ; CONVERT A-F TO ASCII PRINTIT: rst 10h RET ;------------------------------------------------------------------------- ; PRINTSTR - Print string ; INPUT: ; D - address of the string to print ; OUTPUT: ; None ; Note: String must be terminated with a dollar sign ;------------------------------------------------------------------------- PRINTSTR: PUSH AF PUSH BC PUSH DE PUSH HL .next: ld a, (de) inc de cp '$' jr nz, .print POP HL POP DE POP BC POP AF RET .print: rst 10h jr .next DEBUG DB 1 ISCMOS DB 0 ISU880 DB 0 XYRESULT DB 0 XFYFCOUNT DB 0,0,0,0 XFCOUNT DW 0 YFCOUNT DW 0 MSGSIGNIN DB 'Z80 Processor Type Detection',0dh,7fh,' 2024 Sergey Kiselev',0dh MSGCRLF DB 0DH,'$' MSGRAWCMOS DB 'Raw results:',0dh,' CMOS: $' MSGRAWU880 DB 0dh,' U880: $' MSGRAWXY DB 0dh,' XF/YF: $' msgunkn: db 'not tested, assuming NMOS$' MSGFLAGS DB 0dh,'XF/YF flags test:',0dh,' $' MSGCPUTYPE DB 0dh,'Detected CPU type:',0dh,' $' MSGU880NEW DB 'Newer MME U880, Thesys Z80, Microelectronica MMN 80CPU$' MSGU880OLD DB 'Older MME U880$' MSGSHARPLH5080A DB 'Sharp LH5080A$' MSGNMOSZ80 DB 'Zilog Z80, Zilog Z08400',0dh,' or similar NMOS CPU',0DH DB ' Mostek MK3880N, SGS/ST Z8400,',0dh,' Sharp LH0080A, KR1858VM1$' MSGNECD780C DB 'NEC D780C, GoldStar Z8400,',0dh,' possibly KR1858VM1$' MSGKR1858VM1 DB 'Overclocked KR1858VM1$' MSGNMOSUNKNOWN DB 'Unknown NMOS Z80 clone$' MSGCMOSZ80 DB 'Zilog Z84C00$' MSGTOSHIBA DB 'Toshiba TMPZ84C00AP, ST Z84C00AB$' MSGNECD70008AC DB 'NEC D70008AC$' MSGCMOSUNKNOWN DB 'Unknown CMOS Z80 clone$' END