2017-05-22 02:49:24 +00:00
//
// F U S E T e s t s . s w i f t
// C l o c k S i g n a l
//
// C r e a t e d b y T h o m a s H a r t e o n 2 1 / 0 5 / 2 0 1 7 .
// C o p y r i g h t © 2 0 1 7 T h o m a s H a r t e . A l l r i g h t s r e s e r v e d .
//
import XCTest
import Foundation
2017-05-24 02:32:36 +00:00
fileprivate func readHexInt16 ( from scanner : Scanner ) -> UInt16 {
var temporary : UInt32 = 0
scanner . scanHexInt32 ( & temporary )
return UInt16 ( temporary )
}
fileprivate func readHexInt8 ( from scanner : Scanner ) -> UInt8 {
var temporary : UInt32 = 0
scanner . scanHexInt32 ( & temporary )
return UInt8 ( temporary )
}
2017-05-24 02:05:33 +00:00
fileprivate struct RegisterState {
2017-05-24 02:32:36 +00:00
let af : UInt16 , bc : UInt16 , de : UInt16 , hl : UInt16
let afDash : UInt16 , bcDash : UInt16 , deDash : UInt16 , hlDash : UInt16
let ix : UInt16 , iy : UInt16 , sp : UInt16 , pc : UInt16
let i : UInt8 , r : UInt8
let iff1 : Bool , iff2 : Bool , interruptMode : Int
let isHalted : Bool
let tStates : Int
2017-05-24 02:05:33 +00:00
func set ( onMachine machine : CSTestMachineZ80 ) {
machine . setValue ( af , for : . AF )
machine . setValue ( bc , for : . BC )
machine . setValue ( de , for : . DE )
machine . setValue ( hl , for : . HL )
machine . setValue ( afDash , for : . afDash )
machine . setValue ( bcDash , for : . bcDash )
machine . setValue ( deDash , for : . deDash )
machine . setValue ( hlDash , for : . hlDash )
machine . setValue ( ix , for : . IX )
machine . setValue ( iy , for : . IY )
machine . setValue ( sp , for : . stackPointer )
machine . setValue ( pc , for : . programCounter )
machine . setValue ( UInt16 ( i ) , for : . I )
machine . setValue ( UInt16 ( r ) , for : . R )
machine . setValue ( iff1 ? 1 : 0 , for : . IFF1 )
machine . setValue ( iff2 ? 1 : 0 , for : . IFF2 )
machine . setValue ( UInt16 ( interruptMode ) , for : . IM )
// TODO: i s H a l t e d
}
2017-05-28 03:54:53 +00:00
/*
Re : bits 3 and 5 ; the FUSE tests seem to be inconsistent with other documentation
in expectations as to 5 and 3 from the FDCB and DDCB pages . So I ' ve disabled 3
and 5 testing until I can make a value judgment .
*/
2017-05-25 23:12:59 +00:00
init ( dictionary : [ String : Any ] ) {
2017-05-28 03:54:53 +00:00
// d o n ' t t e s t b i t s 3 a n d 5 f o r n o w
2017-10-05 20:45:13 +00:00
af = UInt16 ( truncating : dictionary [ " af " ] as ! NSNumber )
bc = UInt16 ( truncating : dictionary [ " bc " ] as ! NSNumber )
de = UInt16 ( truncating : dictionary [ " de " ] as ! NSNumber )
hl = UInt16 ( truncating : dictionary [ " hl " ] as ! NSNumber )
2017-05-24 02:05:33 +00:00
2017-10-05 20:45:13 +00:00
afDash = UInt16 ( truncating : dictionary [ " afDash " ] as ! NSNumber )
bcDash = UInt16 ( truncating : dictionary [ " bcDash " ] as ! NSNumber )
deDash = UInt16 ( truncating : dictionary [ " deDash " ] as ! NSNumber )
hlDash = UInt16 ( truncating : dictionary [ " hlDash " ] as ! NSNumber )
2017-05-24 02:05:33 +00:00
2017-10-05 20:45:13 +00:00
ix = UInt16 ( truncating : dictionary [ " ix " ] as ! NSNumber )
iy = UInt16 ( truncating : dictionary [ " iy " ] as ! NSNumber )
2017-05-24 02:05:33 +00:00
2017-10-05 20:45:13 +00:00
sp = UInt16 ( truncating : dictionary [ " sp " ] as ! NSNumber )
pc = UInt16 ( truncating : dictionary [ " pc " ] as ! NSNumber )
2017-05-24 02:05:33 +00:00
2017-10-05 20:45:13 +00:00
i = UInt8 ( truncating : dictionary [ " i " ] as ! NSNumber )
r = UInt8 ( truncating : dictionary [ " r " ] as ! NSNumber )
2017-05-24 02:05:33 +00:00
2017-05-25 23:12:59 +00:00
iff1 = ( dictionary [ " iff1 " ] as ! NSNumber ) . boolValue
iff2 = ( dictionary [ " iff2 " ] as ! NSNumber ) . boolValue
2017-05-24 02:05:33 +00:00
2017-05-25 23:12:59 +00:00
interruptMode = ( dictionary [ " im " ] as ! NSNumber ) . intValue
isHalted = ( dictionary [ " halted " ] as ! NSNumber ) . boolValue
2017-05-24 02:05:33 +00:00
2017-05-25 23:12:59 +00:00
tStates = ( dictionary [ " tStates " ] as ! NSNumber ) . intValue
2017-05-24 02:05:33 +00:00
}
init ( machine : CSTestMachineZ80 ) {
2017-05-28 03:54:53 +00:00
// d o n ' t t e s t b i t s 3 a n d 5 f o r n o w
2017-05-29 15:06:23 +00:00
af = machine . value ( for : . AF )
2017-05-24 02:05:33 +00:00
bc = machine . value ( for : . BC )
de = machine . value ( for : . DE )
hl = machine . value ( for : . HL )
2017-05-29 15:06:23 +00:00
afDash = machine . value ( for : . afDash )
2017-05-24 02:05:33 +00:00
bcDash = machine . value ( for : . bcDash )
deDash = machine . value ( for : . deDash )
hlDash = machine . value ( for : . hlDash )
ix = machine . value ( for : . IX )
iy = machine . value ( for : . IY )
sp = machine . value ( for : . stackPointer )
pc = machine . value ( for : . programCounter )
i = UInt8 ( machine . value ( for : . I ) )
r = UInt8 ( machine . value ( for : . R ) )
iff1 = machine . value ( for : . IFF1 ) != 0
iff2 = machine . value ( for : . IFF2 ) != 0
interruptMode = Int ( machine . value ( for : . IM ) )
2017-05-29 15:54:27 +00:00
isHalted = machine . isHalted
tStates = 0 // T O D O ( ? )
2017-05-24 02:05:33 +00:00
}
}
extension RegisterState : Equatable { }
fileprivate func = = ( lhs : RegisterState , rhs : RegisterState ) -> Bool {
2017-06-04 18:04:26 +00:00
return lhs . af = = rhs . af &&
2017-05-24 02:05:33 +00:00
lhs . bc = = rhs . bc &&
lhs . de = = rhs . de &&
lhs . hl = = rhs . hl &&
2017-05-29 15:06:23 +00:00
( lhs . afDash & ~ 0x0028 ) = = ( rhs . afDash & ~ 0x0028 ) &&
2017-05-24 02:05:33 +00:00
lhs . bcDash = = rhs . bcDash &&
lhs . deDash = = rhs . deDash &&
lhs . hlDash = = rhs . hlDash &&
lhs . ix = = rhs . ix &&
lhs . iy = = rhs . iy &&
lhs . sp = = rhs . sp &&
lhs . pc = = rhs . pc &&
lhs . i = = rhs . i &&
lhs . r = = rhs . r &&
lhs . iff1 = = rhs . iff1 &&
lhs . iff2 = = rhs . iff2 &&
2017-05-29 15:54:27 +00:00
lhs . interruptMode = = rhs . interruptMode &&
lhs . isHalted = = rhs . isHalted
2017-05-24 02:05:33 +00:00
}
class FUSETests : XCTestCase {
2017-05-22 02:49:24 +00:00
func testFUSE ( ) {
2017-05-25 23:12:59 +00:00
let inputFilename : String ! = Bundle ( for : type ( of : self ) ) . path ( forResource : " tests.in " , ofType : " json " )
let outputFilename : String ! = Bundle ( for : type ( of : self ) ) . path ( forResource : " tests.expected " , ofType : " json " )
XCTAssert ( inputFilename != nil && outputFilename != nil )
let inputData : Data ! = try ? Data ( contentsOf : URL ( fileURLWithPath : inputFilename ) )
let outputData : Data ! = try ? Data ( contentsOf : URL ( fileURLWithPath : outputFilename ) )
XCTAssert ( inputData != nil && outputData != nil )
let inputArray : [ Any ] ! = try ! JSONSerialization . jsonObject ( with : inputData , options : [ ] ) as ? [ Any ]
let outputArray : [ Any ] ! = try ! JSONSerialization . jsonObject ( with : outputData , options : [ ] ) as ? [ Any ]
XCTAssert ( inputArray != nil && outputArray != nil )
var index = 0
for item in inputArray {
let itemDictionary = item as ! [ String : Any ]
let outputDictionary = outputArray [ index ] as ! [ String : Any ]
2017-05-27 03:23:33 +00:00
index = index + 1
let name = itemDictionary [ " name " ] as ! String
2017-05-31 10:54:25 +00:00
// i f n a m e ! = " 0 2 " {
// c o n t i n u e ;
// }
2017-05-31 23:58:57 +00:00
// p r i n t ( " \ ( n a m e ) " )
2017-05-29 15:40:56 +00:00
2017-05-25 23:12:59 +00:00
let initialState = RegisterState ( dictionary : itemDictionary [ " state " ] as ! [ String : Any ] )
let targetState = RegisterState ( dictionary : outputDictionary [ " state " ] as ! [ String : Any ] )
let machine = CSTestMachineZ80 ( )
2017-05-29 19:57:27 +00:00
machine . captureBusActivity = true
2017-05-25 23:12:59 +00:00
initialState . set ( onMachine : machine )
2017-05-29 15:01:45 +00:00
let inputMemoryGroups = itemDictionary [ " memory " ] as ? [ Any ]
if let inputMemoryGroups = inputMemoryGroups {
for group in inputMemoryGroups {
2017-05-26 01:00:33 +00:00
let groupDictionary = group as ! [ String : Any ]
2017-10-05 20:45:13 +00:00
var address = UInt16 ( truncating : groupDictionary [ " address " ] as ! NSNumber )
2017-05-26 01:00:33 +00:00
let data = groupDictionary [ " data " ] as ! [ NSNumber ]
for value in data {
2017-10-05 20:45:13 +00:00
machine . setValue ( UInt8 ( truncating : value ) , atAddress : address )
2017-05-26 01:00:33 +00:00
address = address + 1
}
}
}
2017-05-25 23:12:59 +00:00
2017-05-27 03:23:33 +00:00
machine . runForNumber ( ofCycles : Int32 ( targetState . tStates ) )
2017-05-25 23:12:59 +00:00
2017-06-13 02:22:00 +00:00
// V e r i f y t h a t e x a c t l y t h e r i g h t n u m b e r o f c y c l e s w a s h i t ; t h i s i s a p r i m i t i v e c y c l e l e n g t h t e s t e r .
2017-07-28 00:17:13 +00:00
let halfCyclesRun = machine . completedHalfCycles
XCTAssert ( halfCyclesRun = = Int32 ( targetState . tStates ) * 2 , " Instruction length off; was \( machine . completedHalfCycles ) but should be \( targetState . tStates * 2 ) : \( name ) " )
2017-06-13 02:22:00 +00:00
2017-05-26 01:00:33 +00:00
let finalState = RegisterState ( machine : machine )
2017-05-29 15:01:45 +00:00
// C o m p a r e p r o c e s s o r s t a t e .
XCTAssertEqual ( finalState , targetState , " Failed processor state \( name ) " )
// C o m p a r e m e m o r y s t a t e .
let outputMemoryGroups = outputDictionary [ " memory " ] as ? [ Any ]
if let outputMemoryGroups = outputMemoryGroups {
for group in outputMemoryGroups {
let groupDictionary = group as ! [ String : Any ]
2017-10-05 20:45:13 +00:00
var address = UInt16 ( truncating : groupDictionary [ " address " ] as ! NSNumber )
2017-05-29 15:01:45 +00:00
let data = groupDictionary [ " data " ] as ! [ NSNumber ]
for value in data {
2017-10-05 20:45:13 +00:00
XCTAssert ( machine . value ( atAddress : address ) = = UInt8 ( truncating : value ) , " Failed memory state \( name ) " )
2017-05-29 15:01:45 +00:00
address = address + 1
}
}
}
2017-05-26 01:00:33 +00:00
2017-05-29 19:57:27 +00:00
// C o m p a r e b u s o p e r a t i o n s .
2017-06-17 22:19:25 +00:00
// l e t c a p t u r e d B u s A c t i v i t y = m a c h i n e . b u s O p e r a t i o n C a p t u r e s
// v a r c a p t u r e d B u s A c i v i t y I n d e x = 0 ;
2017-05-29 19:57:27 +00:00
2017-05-29 21:17:17 +00:00
// I p r e s e n t l y b e l i e v e t h e F U S E u n i t t e s t b u s r e s u l t s f o r D J N Z — o p c o d e 0 x 1 0 — t o b e
// i n e r r o r b y o m i t t i n g t h e f i n a l o f f s e t r e a d . T h e r e f o r e I a m s k i p p i n g t h a t .
// TODO: e n q u i r e w i t h t h e a u t h o r .
2017-06-17 22:19:25 +00:00
// i f n a m e = = " 1 0 " {
// c o n t i n u e
// }
2017-05-29 21:17:17 +00:00
2017-06-17 22:19:25 +00:00
/* l e t d e s i r e d B u s A c t i v i t y = o u t p u t D i c t i o n a r y [ " b u s A c t i v i t y " ] a s ? [ [ S t r i n g : A n y ] ]
2017-05-29 19:57:27 +00:00
if let desiredBusActivity = desiredBusActivity {
for action in desiredBusActivity {
let type = action [ " type " ] as ! String
let time = action [ " time " ] as ! Int32
let address = action [ " address " ] as ! UInt16
let value = action [ " value " ] as ? UInt8
if type = = " MC " || type = = " PC " {
// D o n ' t d o a n y t h i n g w i t h F U S E ' s c o n t e n d e d m e m o r y r e c o r d s ; i t ' s
// p r e s e n t l y u n c l e a r t o m e e x a c t l y w h a t t h e y ' r e s u p p o s e d t o c o m m u n i c a t e
continue
}
2017-05-29 20:44:07 +00:00
// F U S E c o u n t s a m e m o r y a c c e s s a s o c c u r r i n g a t t h e l a s t c y c l e o f i t s b u s o p e r a t i o n ;
// i t c o u n t s a p o r t a c c e s s a s o c c u r r i n g o n t h e s e c o n d . t i m e O f f s e t i s u s e d t o a d j u s t
// t h e F U S E n u m b e r s a s r e q u i r e d .
2017-05-29 19:57:27 +00:00
var operation : CSTestMachineZ80BusOperationCaptureOperation = . read
2017-06-16 00:59:59 +00:00
var alternativeOperation : CSTestMachineZ80BusOperationCaptureOperation = . read
2017-05-29 20:44:07 +00:00
var timeOffset : Int32 = 0
2017-05-29 19:57:27 +00:00
switch type {
case " MR " :
operation = . read
2017-06-16 00:59:59 +00:00
alternativeOperation = . readOpcode
2017-05-29 19:57:27 +00:00
case " MW " :
2017-06-16 00:59:59 +00:00
alternativeOperation = . write
2017-05-29 19:57:27 +00:00
operation = . write
case " PR " :
2017-06-16 00:59:59 +00:00
alternativeOperation = . portRead
2017-05-29 19:57:27 +00:00
operation = . portRead
2017-05-29 20:44:07 +00:00
timeOffset = 3
2017-05-29 19:57:27 +00:00
case " PW " :
2017-06-16 00:59:59 +00:00
alternativeOperation = . portWrite
2017-05-29 19:57:27 +00:00
operation = . portWrite
2017-05-29 20:44:07 +00:00
timeOffset = 3
2017-05-29 19:57:27 +00:00
default :
print ( " Unhandled activity type \( type ) ! " )
}
XCTAssert (
capturedBusActivity [ capturedBusAcivityIndex ] . address = = address &&
capturedBusActivity [ capturedBusAcivityIndex ] . value = = value ! &&
2017-05-29 20:44:07 +00:00
capturedBusActivity [ capturedBusAcivityIndex ] . timeStamp = = ( time + timeOffset ) &&
2017-06-16 00:59:59 +00:00
(
capturedBusActivity [ capturedBusAcivityIndex ] . operation = = operation ||
capturedBusActivity [ capturedBusAcivityIndex ] . operation = = alternativeOperation
) ,
2017-05-29 21:17:17 +00:00
" Failed bus operation match \( name ) (at time \( time ) with address \( address ) , value was \( value != nil ? value ! : 0 ) , tracking index \( capturedBusAcivityIndex ) amongst \( capturedBusActivity ) ) " )
2017-05-29 19:57:27 +00:00
capturedBusAcivityIndex += 1
}
2017-06-17 22:19:25 +00:00
} */
2017-05-22 02:49:24 +00:00
}
}
}