示例#1
0
    def runBinary(self, pasfile, breakpointlabels, randoms, counters):    
    # breakpointlabels is a list of labels to stop emulation on program counter hit

        # prepare binary file
        binfile = self.buildBinary(pasfile)
        binstart = int(self.config.get('params','binaryLocation'),16)
        memsize = int(self.config.get('params','memorySize'),16)
        
        # load into emulator memory
        with open(binfile, "rb") as filedata:
            self.m = MMU([
                (0, binstart), 
                (binstart, binstart + memsize, False, filedata) 
            ])

        # initialize cpu
        self.c = CPU(self.m, self.labels['START'])
        self.cmdCount = 0

        # add user defined random registers
        if len(randoms) > 0:
            for item in randoms:
                self.randoms.append(item)

        # add user defined counters
        if len(counters) > 0:
            for item in counters:
                self.counters.append(item)

        # update randoms converting labels to direct addresses
        for value in self.randoms:
            if not isinstance(value, int):
                labelname = self.validateLabel(value.upper())
                self.randoms.remove(value)
                self.randoms.append(self.labels[labelname])
        
        # update counters converting labels to direct addresses
        for value in self.counters:
            if not isinstance(value, int):
                labelname = self.validateLabel(value.upper())
                self.counters.remove(value)
                self.counters.append(self.labels[labelname])

        # convert breakpoint labels into addresses
        self.breakpointadressess = []
        if len(breakpointlabels) > 0:
            for blabel in breakpointlabels:
                labelname = self.validateLabel(blabel.upper())
                self.breakpointadressess.append(self.labels[labelname])
                
        # make sure all elements are unique
        self.randoms = list(set(self.randoms))                
        self.counters = list(set(self.counters))                
        self.breakpointadressess = list(set(self.breakpointadressess))                

        self.runEmu()
        return [self.c,self.m,self.labels]
示例#2
0
    def test_create(self):
        MMU([
            (0, 128, False, None)
        ])

        MMU([
            (0, 128, False, None),
            (128, 128, True, None)
        ])
示例#3
0
 def test_index_error(self):
     m = MMU([(0, 128)])
     with self.assertRaises(IndexError):
         m.write(-1, 0)
     with self.assertRaises(IndexError):
         m.write(128, 0)
     with self.assertRaises(IndexError):
         m.read(-1)
     with self.assertRaises(IndexError):
         m.read(128)
示例#4
0
 def test_reset(self):
     m = MMU([(0, 16, True), (16, 16, False)])
     m.blocks[0]['memory'][0] = 5
     m.write(16, 10)
     m.reset()
     self.assertEqual(m.read(0), 5)
     self.assertEqual(m.read(16), 0)
示例#5
0
 def test_index_error(self):
     m = MMU([(0, 128)])
     with self.assertRaises(IndexError):
         m.write(-1, 0)
     with self.assertRaises(IndexError):
         m.write(128, 0)
     with self.assertRaises(IndexError):
         m.read(-1)
     with self.assertRaises(IndexError):
         m.read(128)
示例#6
0
 def test_addBlock_overlapping(self):
     m = MMU([])
     m.addBlock(128, 128)
     with self.assertRaises(MemoryRangeError):
         m.addBlock(0, 129)
     with self.assertRaises(MemoryRangeError):
         m.addBlock(255, 128)
示例#7
0
 def test_write_readonly(self):
     m = MMU([(0, 16, True), (16, 16), (32, 16, True)])
     with self.assertRaises(ReadOnlyError):
         m.write(8, 1)
     m.write(20, 1)
     with self.assertRaises(ReadOnlyError):
         m.write(40, 1)
示例#8
0
 def test_reset(self):
     m = MMU([(0, 16, True), (16, 16, False)])
     m.blocks[0]['memory'][0] = 5
     m.write(16, 10)
     m.reset()
     self.assertEqual(m.read(0), 5)
     self.assertEqual(m.read(16), 0)
示例#9
0
    def test_nestest(self):
        path = os.path.join(
            os.path.dirname(os.path.realpath(__file__)),
            "files", "nestest_mod.nes"
        )

        with open(path, "rb") as f:
            mmu = MMU([
                (0x0000, 0x800),  # RAM
                (0x2000, 0x8),  # PPU
                (0x4000, 0x18),
                (0x8000, 0xc000, True, f, 0x3ff0)  # ROM
            ])

        c = CPU(mmu, 0xc000)
        c.r.s = 0xfd  # Not sure why the stack starts here.

        while c.r.pc != 0xc66e:
            try:
                c.step()
            except Exception as e:
                print(c.r)
                print(traceback.format_exc())
                raise e

            self.assertEqual(c.mmu.read(0x2), 0x00, hex(c.mmu.read(0x2)))
            self.assertEqual(c.mmu.read(0x3), 0x00, hex(c.mmu.read(0x3)))
示例#10
0
def init_cpu(mem, pc_value):

    mmu = MMU([(0x00, len(mem), False, mem)  # readonly = False
               ])

    c = CPU(mmu, pc_value)

    return c
示例#11
0
    def test_create_with_list(self):
        m = MMU([
            (0, 128, False, [1, 2, 3])
        ])

        self.assertEqual(m.blocks[0]['memory'][0], 1)
        self.assertEqual(m.blocks[0]['memory'][2], 3)
        self.assertEqual(m.blocks[0]['memory'][3], 0)
示例#12
0
    def test_create_with_file(self):
        path = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                            "files", "test_load_file.bin")

        with open(path, "rb") as f:
            m = MMU([(0, 128, True, f)])

        self.assertEqual(m.blocks[0]['memory'][0], 0xa9)
示例#13
0
 def test_addBlock_overlapping(self):
     m = MMU([])
     m.addBlock(128, 128)
     with self.assertRaises(MemoryRangeError):
         m.addBlock(0, 129)
     with self.assertRaises(MemoryRangeError):
         m.addBlock(255, 128)
示例#14
0
 def test_write_readonly(self):
     m = MMU([(0, 16, True), (16, 16), (32, 16, True)])
     with self.assertRaises(ReadOnlyError):
         m.write(8, 1)
     m.write(20, 1)
     with self.assertRaises(ReadOnlyError):
         m.write(40, 1)
示例#15
0
out = a.assemble(open(f))

# Put the input into an array
input_path = '../input.txt'
with open(input_path, 'rb') as f:
    input_bytes = []
    b = f.read(1)
    while b:
        input_bytes.append(ord(b))
        b = f.read(1)

# Add another new line to terminate
input_bytes += [10]

# Initialize memory and the CPU
m = MMU([
    (0x00, 0x200),  # Create RAM with 512 bytes
    (0x1000, 0x1000 + len(input_bytes), True, input_bytes),  # Input bytes
    (0xC000, 0xFFFF, True, out
     )  # Create ROM starting at 0xC000 with your program.
])

c = CPU(m, 0xC000)

while not c.r.getFlag('B'):  # Run until we break
    c.step()

# Print out the 3-byte answer as decimal
print(m.read(0x10) + (m.read(0x11) << 8) + (m.read(0x12) << 16))
示例#16
0
 def test_addBlock(self):
     m = MMU([])
     m.addBlock(0, 128, False, None)
     m.addBlock(128, 128, True, None)
示例#17
0
 def test_read(self):
     m = MMU([(0, 128)])
     m.write(16, 5)
     m.write(64, 111)
     self.assertEqual(5, m.read(16))
     self.assertEqual(111, m.read(64))
示例#18
0
 def test_write_multiple_blocks(self):
     m = MMU([(0, 128), (1024, 128)])
     m.write(16, 25)
     self.assertEqual(m.blocks[0]['memory'][16], 25)
     m.write(1056, 55)
     self.assertEqual(m.blocks[1]['memory'][32], 55, m.blocks[1]['memory'])
示例#19
0
 def test_write(self):
     m = MMU([(0, 128)])
     m.write(16, 25)
     self.assertEqual(m.blocks[0]['memory'][16], 25)
示例#20
0
 def test_addBlock(self):
     m = MMU([])
     m.addBlock(0, 128, False, None)
     m.addBlock(128, 128, True, None)
示例#21
0
 def test_create_overlapping(self):
     with self.assertRaises(MemoryRangeError):
         MMU([(0, 129), (128, 128)])
示例#22
0
 def test_create_empty(self):
     MMU([])
示例#23
0
class testUtils:
    validatedAttribs = ['START', 'MAIN.@EXIT']

    def __init__(self):
        cfile = "config.ini"
        if not os.path.exists(cfile):
            raise FileNotFoundError('config.ini file not found')
        self.config = configparser.ConfigParser()
        self.config.read(cfile)
        if not os.path.exists(self.config.get('paths','mp')):
            raise FileNotFoundError('MadPascal compiler not found here: {}'.format(self.config.get('paths','mp')))
        if not os.path.exists(self.config.get('paths','mads')):
            raise FileNotFoundError('MadAssembler not found here: {}'.format(self.config.get('paths','mads')))
        if not os.path.exists(self.config.get('paths','base')):
            raise FileNotFoundError('base directory not found here: {}'.format(self.config.get('paths','base')))
        self.clearRandoms()
        self.clearCounters()

    def validateLabel(self, label):
        labelname = "MAIN.{}".format(label.upper())
        if not labelname in self.labels:
            raise IndexError("Label not found: {}".format(labelname))
        return labelname
        

    def getLabels(self, tabfile):
        labels = {}
        with open(tabfile) as f:
            lines = f.readlines()
            if len(lines)>2:
                for linenum in range(2,len(lines)):
                    elems = lines[linenum].strip().split('\t')
                    labels[elems[2]]=int(elems[1],16)
        return labels


    def clearDir(self, dirname):
        if os.path.exists(dirname):
            shutil.rmtree(dirname)
        os.makedirs(dirname)    
       
        
    def buildBinary(self, pasfile):
        # prepare paths
        dirname = os.path.dirname(pasfile)
        rawname = os.path.splitext(os.path.basename(pasfile))[0]
        srcasmfile = "{}/{}.a65".format(dirname, rawname)
        rawpath = "{}/{}".format(self.config.get('paths','tempdir'),rawname)
        asmfile = "{}.a65".format(rawpath)
        binfile = "{}.bin".format(rawpath)
        tabfile = "{}.tab".format(rawpath)
        
        # compile and validate
        rc = run("{} {} -t raw -o".format(self.config.get('paths','mp'), pasfile), shell = True)
        if rc.returncode != 0 :
            raise NotImplementedError("Mad-Pascal exit code = {}. Probably compilation error occured".format(rc.returncode))
        if not os.path.exists(srcasmfile):
            raise NotImplementedError("File {} not found! Probably compilation error occured".format(srcasmfile))
                        
        # copy assembly file to tempdir if needed
        if srcasmfile != asmfile: 
            rc = shutil.move(srcasmfile, asmfile)
            while not os.path.exists(asmfile):
                sleep(0.1)
        
        # assemby file and validate
        rc = run("{} {} -x -i:{} -t:{} -o:{}".format(self.config.get('paths','mads'), asmfile, self.config.get('paths','base'), tabfile, binfile), shell = True)
        if rc.returncode != 0:
            raise NotImplementedError("Mad-Assembler exit code = {}. Probably nasty assemblation error occured".format(rc.returncode))

        # parse all labels
        self.labels = self.getLabels(tabfile)

        # check for required labels
        for attrib in self.validatedAttribs:
            if not attrib in self.labels:
                raise IndexError("Label not found: {}".format(attrib))

        return binfile
        
  
    def runEmu(self):
    # this method executes code until reaches end of program, or hits breakpoint
    # it returns False on breakpoints and True on the end of code reached  
        timeout = int(self.config.get('params','cpuTimeout'), 16)
        while self.c.r.pc != self.labels['MAIN.@EXIT']:
            self.c.step()
            self.cmdCount += 1
            
            # check for timeout
            if self.cmdCount > timeout:
                raise TimeoutError("CPU timeouted after {} commands".format(self.cmdCount))
            
            # check for breakpoints
            if len(self.breakpointadressess) > 0:
                if self.c.r.pc in self.breakpointadressess:
                    return False

            # check if there is something to randomize
            if len(self.randoms) > 0:
                for address in self.randoms:
                    self.m.write(address, random.randint(255))
            
            # check if there are counters to increment
            if len(self.counters) > 0:
                for address in self.counters:
                    curval = self.m.read(address)
                    curval = (curval + 1) % 256
                    self.m.write(address, curval)

        return True
        
        
    def runBinary(self, pasfile, breakpointlabels, randoms, counters):    
    # breakpointlabels is a list of labels to stop emulation on program counter hit

        # prepare binary file
        binfile = self.buildBinary(pasfile)
        binstart = int(self.config.get('params','binaryLocation'),16)
        memsize = int(self.config.get('params','memorySize'),16)
        
        # load into emulator memory
        with open(binfile, "rb") as filedata:
            self.m = MMU([
                (0, binstart), 
                (binstart, binstart + memsize, False, filedata) 
            ])

        # initialize cpu
        self.c = CPU(self.m, self.labels['START'])
        self.cmdCount = 0

        # add user defined random registers
        if len(randoms) > 0:
            for item in randoms:
                self.randoms.append(item)

        # add user defined counters
        if len(counters) > 0:
            for item in counters:
                self.counters.append(item)

        # update randoms converting labels to direct addresses
        for value in self.randoms:
            if not isinstance(value, int):
                labelname = self.validateLabel(value.upper())
                self.randoms.remove(value)
                self.randoms.append(self.labels[labelname])
        
        # update counters converting labels to direct addresses
        for value in self.counters:
            if not isinstance(value, int):
                labelname = self.validateLabel(value.upper())
                self.counters.remove(value)
                self.counters.append(self.labels[labelname])

        # convert breakpoint labels into addresses
        self.breakpointadressess = []
        if len(breakpointlabels) > 0:
            for blabel in breakpointlabels:
                labelname = self.validateLabel(blabel.upper())
                self.breakpointadressess.append(self.labels[labelname])
                
        # make sure all elements are unique
        self.randoms = list(set(self.randoms))                
        self.counters = list(set(self.counters))                
        self.breakpointadressess = list(set(self.breakpointadressess))                

        self.runEmu()
        return [self.c,self.m,self.labels]

    def setRandomByte(self, address):
        self.randoms.append(address)
    
    def setCounterByte(self, address):
        self.counters.append(address)

    def clearRandoms(self):
        self.randoms = []
    
    def clearCounters(self):
        self.counters = []        

#############################        
# TEST RUNNER API           #      
#############################
            
    def runFile(self, pasfile, breakpoints = [], randoms = [], counters = []):
    # breakpointlabels is a list of labels to stop emulation on program counter hit
        self.clearDir(self.config.get('paths','tempdir'))
        return self.runBinary(pasfile, breakpoints, randoms, counters)
        
    def runCode(self, pascode, breakpoints = [], randoms = [], counters = []):
    # breakpointlabels is a list of labels to stop emulation on program counter hit
        self.clearDir(self.config.get('paths','tempdir'))
        pasfile = "{}/temp.pas".format(self.config.get('paths','tempdir'))
        with open(pasfile, 'w') as f:
            f.write(pascode)
        return self.runBinary(pasfile, breakpoints, randoms, counters)

    def resume(self):
        self.runEmu()
        return [self.c,self.m,self.labels]

#############################        
# VARIABLE AND DATA GETTERS #      
#############################
    
    def varByte(self, varlabel):
        labelname = self.validateLabel(varlabel)
        return self.m.read(self.labels[labelname])
     
    def varWord(self, varlabel):
        labelname = self.validateLabel(varlabel)
        return self.m.readWord(self.labels[labelname])

    def varCardinal(self, varlabel):
        labelname = self.validateLabel(varlabel)
        upper = self.m.readWord(self.labels[labelname]) 
        lower = self.m.readWord(self.labels[labelname] + 2) 
        return (lower << 16) + upper

    def getByte(self, address):
        return self.m.read(address)

    def getWord(self, address):
        return self.m.readWord(address)

    def getCardinal(self, address):
        upper = self.m.readWord(address) 
        lower = self.m.readWord(address + 2) 
        return (lower << 16) + upper
        
    def getArray(self, address, size, element_size = 1):
        resultArray = []
        elemAddress = address
        for i in range(size):
            byteshift = 0
            elem = 0
            for ebyte in range(element_size):
                elem += self.m.read(elemAddress) << byteshift
                byteshift += 8
                elemAddress += 1
                
            resultArray.append(elem)
        return resultArray

    def isVarTrue(self, varlabel):
        labelname = self.validateLabel(varlabel)
        return self.m.read(self.labels[labelname]) == self.labels['TRUE']

    def isTrue(self, address):
        return self.m.read(address) == self.labels['TRUE']
示例#24
0
 def test_write_multiple_blocks(self):
     m = MMU([(0, 128), (1024, 128)])
     m.write(16, 25)
     self.assertEqual(m.blocks[0]['memory'][16], 25)
     m.write(1056, 55)
     self.assertEqual(m.blocks[1]['memory'][32], 55, m.blocks[1]['memory'])
示例#25
0
def load_level(stage, prg_rom, chr_rom, sym_file, nes_palette):
    # Initialize the MMU / CPU
    mmu = MMU([(0x0, _WORKING_RAM_SIZE, False, []),
               (0x8000, 0x10000, True, list(prg_rom))])
    cpu = CPU(mmu, 0x0)

    # Execute some preamble subroutines which set up variables used by the main subroutines.
    if isinstance(stage, tuple):
        world_num, area_num = stage
        mmu.write(sym_file['WORLDNUMBER'], world_num - 1)
        mmu.write(sym_file['AREANUMBER'], area_num - 1)
        _execute_subroutine(cpu, sym_file['LOADAREAPOINTER'])
    else:
        area_pointer = stage
        mmu.write(sym_file['AREAPOINTER'], area_pointer)

    mmu.write(sym_file['HALFWAYPAGE'], 0)
    mmu.write(sym_file['ALTENTRANCECONTROL'], 0)
    mmu.write(sym_file['PRIMARYHARDMODE'], 0)
    mmu.write(sym_file['OPERMODE_TASK'], 0)
    _execute_subroutine(cpu, sym_file['INITIALIZEAREA'])

    # Extract the palette.
    palette = _load_palette(mmu, sym_file, nes_palette)

    # Repeatedly extract meta-tile columns, until the level starts repeating.
    cols = []
    for column_pos in range(1000):
        _execute_subroutine(cpu, sym_file['AREAPARSERCORE'])
        cols.append(_get_metatile_buffer(mmu, sym_file))
        _execute_subroutine(cpu, sym_file['INCREMENTCOLUMNPOS'])

        if len(cols) >= 96 and cols[-48:] == cols[-96:-48]:
            cols = cols[:-80]
            break
    level = np.array(cols).T

    # Render a dict of metatiles.
    mtiles = {
        mtile: _render_metatile(mmu, chr_rom, mtile, palette)
        for mtile in set(level.flatten())
    }

    return level, mtiles
示例#26
0
 def test_write(self):
     m = MMU([(0, 128)])
     m.write(16, 25)
     self.assertEqual(m.blocks[0]['memory'][16], 25)
示例#27
0
 def _cpu(self,
          ram=(0, 0x200, False),
          rom=(0x1000, 0x100),
          romInit=None,
          pc=0x1000):
     return CPU(MMU([ram, rom + (True, romInit)]), pc)
示例#28
0
 def test_read(self):
     m = MMU([(0, 128)])
     m.write(16, 5)
     m.write(64, 111)
     self.assertEqual(5, m.read(16))
     self.assertEqual(111, m.read(64))