package turgen.plugin.plturbo;

import turgen.plugin.*;
import turgen.generator.SignalGenerator;
import turgen.config.Configuration;
import turgen.*;
import java.io.*;
import java.util.Iterator;
import javax.swing.*;
import turgen.config.BooleanConfigEntry;
import turgen.config.ConfigClass;
import turgen.config.IntegerConfigEntry;
import turgen.dtb.DOS2Binary;
import turgen.dtb.DOS2BinaryException;
import turgen.dtb.DOS2BinaryProcessingException;
import turgen.dtb.Segment;
import turgen.playlist.AbstractPlayListItem;
import turgen.tapeimage.DataChunk;
import turgen.tapeimage.TapeImageChunk;
import turgen.tapeimage.TurboChunk;

public class PlTurboROMPlugin implements Plugin {

    private String lastError;
    private PlTurboROMPluginPanel panel;
    private final int[] casChunkName={116,114,112,52};

    private ConfigClass cfcPlTurboROM;
    private BooleanConfigEntry cfgInvertPolarity;
    private IntegerConfigEntry cfgPilotToneHeader;
    private IntegerConfigEntry cfgPilotToneData;
    private IntegerConfigEntry cfgPilotToneBinaryLoad;
    private IntegerConfigEntry cfgSilenceAfterBinaryLoader;
    private IntegerConfigEntry cfgSilenceAfterHeader;
    private IntegerConfigEntry cfgBasicRunAddress;
    private IntegerConfigEntry cfgBasicLoadAddress;
    
    

    /** New instance*/
    public PlTurboROMPlugin() {
    }


    /** Get human readable name
     */
    public String getName() {
        return "Turbo ROM";
    }


    /**Generate instructions for generator
     * @param _pl Playlist item
     * @return Instructions or null
     */
    public int[] getInstructions(AbstractPlayListItem _pl) {


        PlTurboROMPlaylistItem pl;
        pl=(PlTurboROMPlaylistItem)_pl;

        try {

            /*Initialization of streams and initial setup instructions*/
            InstructionStream is = new InstructionStream();
            Utils.addSetupInstruction(is, pl.pulses, casChunkName,1-cfgInvertPolarity.getAsInt(),SignalGenerator.FLAG_ORDER_LH);

            /*Code dependent on the conversion type*/
            switch (pl.conversionType) {
                case PlTurboROMPlaylistItem.CV_COMPATIBLE_BINARY : {
                    convertBinaryFile(is,pl);
                    break;
                }
                case PlTurboROMPlaylistItem.CV_BASIC : {
                    convertBASICFile(is,pl);
                    break;
                }
                case PlTurboROMPlaylistItem.CV_BINARY: {
                    convertSegmentedBinaryFile(is, pl);
                    break;
                }
            }

            /*Finalization*/
            is.add(SignalGenerator.INSTR_END);
            
           return is.getInstructions();

        }

        catch(Exception e) {
            /*Print out*/
            e.printStackTrace();
            /*Create error message*/
            lastError="Unable to produce instructions:"+e.toString();
            return null;
        }


    }

    public String getLastError() {
        return lastError;
    }

    public AbstractPlayListItem createItem() {
        PlTurboROMPlaylistItem dpr = panel.getRecord();
        dpr.pluginClass=Turgen.pluginManager.getPluginClassName(this);
        return dpr;
    }

    public void resetPanel() {
        panel.clear();
    }

    public String validateInput() {
        return panel.verify();
    }

    public JPanel getItemPanel() {
        return panel;
    }

    public void init() {
        panel = new PlTurboROMPluginPanel();

        /*Initialize configuration class*/
        cfcPlTurboROM = new ConfigClass("PlTurboROM", "Turbo ROM");
        cfgInvertPolarity =new BooleanConfigEntry("invertPolarity","Invert polarity of pulses",false);
        cfgPilotToneHeader = new IntegerConfigEntry("pilotToneHeader","Header pilot tone length [pulse]",4884,1024,8192);
        cfgPilotToneData = new IntegerConfigEntry("pilotToneData","Data block pilot tone length [pulse]",516,128,2048);
        cfgSilenceAfterHeader = new IntegerConfigEntry("silenceAfterHeader","Silence after header [s]",2,0,30);
        cfgSilenceAfterBinaryLoader = new IntegerConfigEntry("silenceAfterBinaryLoader","Silence after binary loader [s]",2,0,30);
        cfgBasicLoadAddress = new IntegerConfigEntry("basicLoadAddress","LOAD address of a BASIC program",1792,512,65535);
        cfgBasicRunAddress = new IntegerConfigEntry("basicRunAddress","RUN address of a BASIC program", 0xA07E,0,65535);
        cfgPilotToneBinaryLoad = new IntegerConfigEntry("pilotToneBinaryLoad","Binary load pilot tone length [pulse]",1024,128,2048);
        
        /*Add entries to the class*/
        cfcPlTurboROM.addEntry(cfgInvertPolarity);
        cfcPlTurboROM.addEntry(cfgPilotToneHeader);
        cfcPlTurboROM.addEntry(cfgPilotToneData);
        cfcPlTurboROM.addEntry(cfgPilotToneBinaryLoad);
        cfcPlTurboROM.addEntry(cfgSilenceAfterHeader);
        cfcPlTurboROM.addEntry(cfgSilenceAfterBinaryLoader);
        cfcPlTurboROM.addEntry(cfgBasicLoadAddress);
        cfcPlTurboROM.addEntry(cfgBasicRunAddress);
        /*Register configuration class*/
        Configuration.registerClass(cfcPlTurboROM);
    }

    public void fillPanel(AbstractPlayListItem plr) {
        PlTurboROMPlaylistItem dpr = (PlTurboROMPlaylistItem)plr;
        panel.fill(dpr);
    }


    public void setInOutProvider(InOutProvider prov) {
        
        panel.ioProvider=prov;
    }

     public String[] getSupportedTapeImageChunks() {
        return new String[] {"trp4"};
    }

     public void addInstructionsForChunk(InstructionStream is, TapeImageChunk chunk) throws Exception {

        /*trXX chunk*/
        if (chunk instanceof TurboChunk) {
            Utils.addSetupInstruction(is, Turgen.pulseCache.getPulsesForPlugin(this)[0], this.casChunkName,(1-cfgInvertPolarity.getAsInt()),SignalGenerator.FLAG_ORDER_LH);
            return;
        }

        /*Only data chunk accepted*/
        if (!(chunk instanceof DataChunk)) return;
        DataChunk dataChunk = (DataChunk)chunk;

        /*Pilot tone*/
        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(dataChunk.getAux());
        /*Short pulse*/
        is.add(SignalGenerator.INSTR_NARROW);

        /*Data of the block*/
        is.add(SignalGenerator.INSTR_DATA);
        is.add(dataChunk.getLength());
        int[] dta = dataChunk.getData();
        is.add(dta);

        /*Termination*/
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_STOP);

    }

    public BinaryConvertor[] getBinaryConvertors() {
        
        BinaryConvertor[] convertors = new BinaryConvertor[2];
        convertors[0]= new PlTurboROMBinaryConvertor(this, "Turbo ROM - Compatible binary file");
        convertors[1]= new PlTurboROMSegmentedBinaryConvertor(this, "Turbo ROM - Segmented binary file");

        return convertors;
    }


    public String getCountry() {
        return "PL";
    }

    
    private void convertBinaryFile(InstructionStream is,PlTurboROMPlaylistItem pli) throws Exception {
        convertBinaryFileImpl(is, pli.inFile,pli.fname, null);
    }
    /** Convert binary file to Turbo ROM
     *
     * @param is Stream with instructions
     * @param inputFilespec Input file specifier
     * @param inputFileData Input file data. Can be null
     * @throws Exception
     */
    private void convertBinaryFileImpl(InstructionStream is, String inputFilespec,String fname,int[] inputFileData) throws Exception {

        /*Load and analyze binary file*/
        DOS2Binary dtb;
        if (inputFileData!=null) {
            dtb = new DOS2Binary("");
            dtb.analyzeFromData(inputFileData);
        }
        else {
            dtb = new DOS2Binary(inputFilespec);
            dtb.analyzeFromFile();
        }

        /*Reject binary file that is not compatible*/
        if (dtb.isPlTurboROMCompatible()==false) {
            throw new DOS2BinaryProcessingException("Binary file is not Turbo ROM compatible binary file");
        }

        /*Prepare header*/
        int[] dataBlock = dtb.getFirstDataSegment().data;
        int[] headerData = createBinaryHeader(getHeaderRunAddress(dtb),getHeaderInitAddress(dtb),dtb.getFirstDataSegment().start,dataBlock,fname);

        
        /*Write header block*/
        is.add(SignalGenerator.INSTR_LONGSILENCE);
        is.add(1);

        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(cfgPilotToneHeader.getAsInt());
        is.add(SignalGenerator.INSTR_NARROW);

        is.add(SignalGenerator.INSTR_DATA);
        is.add(headerData.length);
        is.add(headerData);
        

        /*Safety pulses and block separation*/
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_STOP);
        is.add(SignalGenerator.INSTR_LONGSILENCE);
        is.add(cfgSilenceAfterHeader.getAsInt());
        
        

        /*Data block*/
        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(cfgPilotToneData.getAsInt());
        is.add(SignalGenerator.INSTR_NARROW);

        is.add(SignalGenerator.INSTR_DATA);
        is.add(dataBlock.length);

        
        is.add(dataBlock);
        
        /*Safety pulses and block separation*/
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_STOP);
        

    }

    /** Get INIT address that will be used in header
     * @param dtb DOS2 binary file
     * @return Init address or -1 when there is no INIT address
     */
    private int getHeaderInitAddress(DOS2Binary dtb) {
        
        /*No INIT segment, no INIT address*/
        if (dtb.hasInitSegment()==false) return -1;

        /*Browse throught JUMP segments*/
        Iterator it = dtb.allSegments.iterator();
        Segment s;

        while(it.hasNext()) {
            s=(Segment)it.next();
            if (s.type==Segment.INIT_SEGMENT) {
                return s.jump;
            }
            if (s.type==Segment.RUNINIT_SEGMENT) {
                return s.jump2;
            }
        }

        return -1;
    }

    /** Get RUN  address that will be used in header*/
    private int getHeaderRunAddress(DOS2Binary dtb) {

        /*No RUN or RUNINIT segment*/
        if (dtb.hasRunSegment()==false) {
            return dtb.getFirstDataSegment().start;
        }
        /*RUN or RUNINIT segment*/
        else {
            /*Browse through all jump segments and get the address*/
            Iterator it = dtb.allSegments.iterator();
            Segment s;

            while(it.hasNext()) {
                s= (Segment)it.next();
                if (s.type==Segment.RUN_SEGMENT || s.type==Segment.RUNINIT_SEGMENT) {
                    return s.jump;
                }
            }
        }
        return 0;
    }

    private int[] createBinaryHeader(int headerRunAddress, int headerInitAddress, int loadAddress, int[] data,String fname) {

        int[] header = new int[41];

        /*Header load address*/
        header[1]= 0x01;
        header[2]= 0x06;

        /*Header length*/
        header[3]= 0x28;
        header[4]= 0x00;

        /*Run address*/
        header[6]= headerRunAddress % 256;
        header[7]= headerRunAddress / 256;

        /*Init address*/
        if (headerInitAddress==-1) {
            header[8]= 0;
            header[9]= 1;
        }
        else {
            header[8]=headerInitAddress % 256;
            header[9]=headerInitAddress / 256;
        }

        /*Load address*/
        header[10]=loadAddress % 256;
        header[11]=loadAddress / 256;

        /*Length of the block*/
        header[12]=data.length % 256;
        header[13]=data.length / 256;

        /*Padding*/
        header[14]=0;

        /*Spaces in the file name*/
        for(int i=15;i<35;i++) {
            header[i]=0;
        }

        /*Internal codes int the file name*/
        int len = Math.min(20,fname.length());
        char[] c = fname.toCharArray();

        for(int i=0;i<len;i++) {
            header[15+i]=Utils.ascii2Internal(c[i]);
        }

        /*Program type flag*/
        header[35]=1;

        /*INIT execution*/
        if (headerInitAddress==-1) header[36]=1; else header[36]=0;

        /*Padding bytes*/
        header[37]=0;
        header[38]=0;
        header[39]=0;

        /*RTS*/
        header[40]=0x60;

        /*Data block checksum*/
        int dataBlockChecksum = data[0];
        for(int i=1;i<data.length;i++) {
            dataBlockChecksum ^= data[i];
        }
        header[5]=dataBlockChecksum;

        /*Header block checksum*/
        int headerBlockChecksum=header[1];
        for(int i=2;i<header.length;i++) {
            headerBlockChecksum ^= header[i];
        }
        header[0]=headerBlockChecksum;


        return header;
    }

    /** Convert tokenized BASIC file*/
    private void convertBASICFile(InstructionStream is, PlTurboROMPlaylistItem pl) throws Exception {
        
        RandomAccessFile raf = new RandomAccessFile(pl.inFile,"r");
        if (raf.length()>1024*1024) throw new FileTooLargeException(pl.inFile);
        
        byte[] rawBytes = new byte[(int)raf.length()];
        int[] fileData = new int[rawBytes.length];
        
        raf.readFully(rawBytes);
        raf.close();
        
        /*Consolidate raw bytes so the can be processed as ints*/
        for(int i=0;i<rawBytes.length;i++) {
                fileData[i]=rawBytes[i]<0?rawBytes[i]+256:rawBytes[i];
        }
        
        int[] headerData = createBASICHeader(pl.fname,fileData);
        
        /*Write header block*/
        is.add(SignalGenerator.INSTR_LONGSILENCE);
        is.add(1);

        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(cfgPilotToneHeader.getAsInt());
        is.add(SignalGenerator.INSTR_NARROW);

        is.add(SignalGenerator.INSTR_DATA);
        is.add(headerData.length);
        
        is.add(headerData);
        

        /*Safety pulses and block separation*/
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_STOP);
        is.add(SignalGenerator.INSTR_LONGSILENCE);
        is.add(2);

        /*Data block*/
        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(cfgPilotToneData.getAsInt());
        is.add(SignalGenerator.INSTR_NARROW);

        is.add(SignalGenerator.INSTR_DATA);
        is.add(fileData.length-14);

        for(int i=14;i<fileData.length;i++) {
            is.add(fileData[i]);
        }
        /*Safety pulses and block separation*/
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_STOP);
        
    }

    private int[] createBASICHeader(String fname, int[] fileData) {
        int[] header = new int[78];
        
        int specialAddr = cfgBasicRunAddress.getAsInt();
        int loadAddr = cfgBasicLoadAddress.getAsInt();
        
        /*Clear header*/
        for(int i=0;i<header.length;i++) {
            header[i]=0;
        }
        
        /*Header load address*/
        header[1]=0x0601 % 0x100;
        header[2]=0x0601 / 0x100;
        
        /*Header length*/
        header[3]=0x004D % 0x100;
        header[4]=0x004D / 0x100;
        
        /*Data block Check sum*/
        int chsum = fileData[14];
        for(int i=15;i<fileData.length;i++) {
            chsum ^= fileData[i];
        }
        header[5]=chsum;
        
        /*Run address*/
        header[6]=specialAddr % 256;
        header[7]=specialAddr / 256;
        
        /*Load address*/
        int eLoadAdr=loadAddr+fileData[2]+fileData[3]*256;
        header[10]=eLoadAdr % 256; 
        header[11]=eLoadAdr / 256;
                
        
        /*Length of the main block. All data minus 14 bytes header*/
        header[12]=(fileData.length-14) % 256;
        header[13]=(fileData.length-14) / 256;
        
        /*File name*/
        int len = Math.min(20,fname.length());
        char[] c = fname.toCharArray();
        for(int i=0;i<len;i++) {
            header[15+i]=Utils.ascii2Internal(c[i]);
        }
        
        /*Program type and no init, basic initialization routine*/
        header[35]=0; header[36]=1;
        System.arraycopy(basInitRoutine,0 , header, 40, basInitRoutine.length);
        
        
        /*7 addresses to change*/
        int[] relo = new int[7];
        for(int i=0;i<7;i++) {
            relo[i]=fileData[2*i]+256*fileData[2*i+1];
            relo[i]+=loadAddr;
            header[60+2*i]=relo[i]%256;
            header[60+2*i+1]=relo[i]/256;
        }
        
        header[60+14]=(fileData.length+loadAddr-14)%256;
        header[60+15]=(fileData.length+loadAddr-14)/256;
        header[60+16]=header[60+14];
        header[60+17]=header[60+15];
        
        /*Header checksum*/
        chsum = header[1];
        for(int i=2;i<header.length;i++) {
            chsum ^= header[i];
        }
        header[0]=chsum;
        
        return header;
    }
    private static final int[] basInitRoutine = 
    {
     0xA2, 0x11, 0xBD, 0x3C, 0x06, 0x95,0x80, 0xCA, 0x10,0xF8,0x60
    };
    public boolean isUsingAlternateFileChooser() {
        return false;
    }
    
    private void convertSegmentedBinaryFile(InstructionStream is, PlTurboROMPlaylistItem pl) throws Exception{
        
        convertBinaryFileImpl(is, null, pl.fname, binaryLoaderData);
        is.add(SignalGenerator.INSTR_LONGSILENCE);
        is.add(cfgSilenceAfterBinaryLoader.getAsInt());
        
        /*Load and analyze binary file*/
        DOS2Binary dtb = new DOS2Binary((pl.inFile));
        dtb.analyzeFromFile();
        dtb.purify();

        int[] silenceList = pl.silenceList;
        if (silenceList==null) silenceList=new int[0];
        int silenceIndex=0;

        int blockPilotPulses=cfgPilotToneBinaryLoad.getAsInt();
        
        /*Prepare array of segments*/
        Segment[] segments = new Segment[dtb.getTotalSegmentCount()];
        for(int i=0;i<segments.length;i++) {
            segments[i]=(Segment)dtb.allSegments.get(i);
        }
        
        /*Generate segment header + segment data blocks*/
        int segCount=segments.length;
        for(int i=0;i<segCount;i++) {

            Segment seg = segments[i];
            generateSegment(seg,blockPilotPulses, is);
     
            /*Silence after INIT segments*/
            if (seg.type==Segment.INIT_SEGMENT || seg.type==Segment.RUNINIT_SEGMENT) {
                if (silenceIndex < silenceList.length) {
                    is.add(SignalGenerator.INSTR_LONGSILENCE);
                    is.add(Math.min(silenceList[silenceIndex],240));
                    silenceIndex++;
                }
            }
        }
        
        /*Generate termination header*/
        generateTermHeader(blockPilotPulses, is);
    }
    
    private void generateSegment(Segment s,int pilotTonePulses,InstructionStream is) {
        
        /*Generate header*/
        /* 00    - Header check sum
           01    - Block check sum
           02,03 - Block start address
           04,05 - Block length
        */
        int[] header = new int[6];
        int[] data = s.data;
       
        /*Data block check sum*/
        int dataChsum=data[0];
        for(int i=1;i<data.length;i++) {
            dataChsum=dataChsum^data[i];
        }
        
        /*Create header*/
        header[1]=dataChsum;
        header[2]=s.start % 256;
        header[3]=s.start / 256;
        header[4]=s.data.length % 256;
        header[5]=s.data.length / 256;
        
        int headerChsum=header[1];
        for(int i=2;i<header.length;i++) {
            headerChsum=headerChsum^header[i];
        }
        header[0]=headerChsum;
        
        addBlock(is,header,pilotTonePulses);
        addBlock(is,data,pilotTonePulses);
    }
    
    private void generateTermHeader(int pilotTonePulses,InstructionStream is) {
        int[] header = new int[6];
        for(int i=0;i<header.length;i++) {
            header[i]=0;
        }
        addBlock(is,header,pilotTonePulses);
    }
    
    
    private void addBlock(InstructionStream is,int[] data,int pilotTonePulses) {
        
        /*Pilot tone*/
        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(pilotTonePulses);
        
        /*Sync*/
        is.add(SignalGenerator.INSTR_NARROW);

        /*Data part*/
        is.add(SignalGenerator.INSTR_DATA);
        is.add(data.length);
        is.add(data);
        
        /*Tail*/
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_WIDE);
        is.add(SignalGenerator.INSTR_STOP);
        
        /*Block separator*/
        is.add(SignalGenerator.INSTR_BLOCKSEP);
    }
    
    
   /*Turbo ROM binary loader*/ 
   private static final int[] binaryLoaderData = {
 
    0xff, 0xff, 0xe0, 0x02, 0xe1, 0x02, 0xd7, 0x21, 0x00, 0x20, 0x0b, 0x22, 0x4c, 0x9c, 0x09, 0xa9, 0x00, 0x8d, 0x4f, 0x08,
    0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85, 0x09, 0x20, 0x51, 0x08, 0x20, 0xa5, 0x08, 0x20, 0x51, 0x08, 0xe6, 0x32, 0x20, 0xb6,
    0x08, 0xcd, 0x7e, 0x09, 0xf0, 0x03, 0x4c, 0x87, 0x09, 0x20, 0x77, 0x08, 0x20, 0x62, 0x08, 0xa9, 0x50, 0x8d, 0xe2, 0x02,
    0xa9, 0x08, 0x8d, 0xe3, 0x02, 0x20, 0xa5, 0x08, 0x20, 0x62, 0x08, 0x20, 0xb6, 0x08, 0xcd, 0x7f, 0x09, 0xf0, 0x03, 0x4c,
    0x87, 0x09, 0x20, 0x4c, 0x08, 0x4c, 0x0f, 0x08, 0x6c, 0xe2, 0x02, 0x00, 0x60, 0xa9, 0x7e, 0x85, 0x32, 0xa9, 0x09, 0x85,
    0x33, 0xa9, 0x05, 0x85, 0x34, 0xa9, 0x00, 0x85, 0x35, 0x60, 0xad, 0x80, 0x09, 0x85, 0x32, 0xad, 0x81, 0x09, 0x85, 0x33,
    0xad, 0x82, 0x09, 0x85, 0x34, 0xad, 0x83, 0x09, 0x85, 0x35, 0x60, 0xad, 0x82, 0x09, 0xd0, 0x0f, 0xad, 0x83, 0x09, 0xd0,
    0x0a, 0xad, 0x80, 0x09, 0xd0, 0x05, 0xad, 0x81, 0x09, 0xf0, 0x17, 0xad, 0x4f, 0x08, 0xd0, 0xc0, 0xad, 0x80, 0x09, 0x8d,
    0xe0, 0x02, 0xad, 0x81, 0x09, 0x8d, 0xe1, 0x02, 0xee, 0x4f, 0x08, 0x4c, 0x50, 0x08, 0x6c, 0xe0, 0x02, 0xa9, 0x01, 0x85,
    0x2f, 0x20, 0xe3, 0x08, 0x20, 0x4e, 0x09, 0x20, 0x12, 0x09, 0x20, 0x05, 0x09, 0x60, 0xa9, 0x00, 0x8d, 0x84, 0x09, 0x8d,
    0x85, 0x09, 0xa0, 0x00, 0x51, 0x32, 0xe6, 0x32, 0xd0, 0x02, 0xe6, 0x33, 0xee, 0x84, 0x09, 0xd0, 0x03, 0xee, 0x85, 0x09,
    0xa4, 0x34, 0xcc, 0x84, 0x09, 0xd0, 0xe7, 0xa4, 0x35, 0xcc, 0x85, 0x09, 0xd0, 0xe0, 0x85, 0x34, 0xa5, 0x34, 0x60, 0xa9,
    0x34, 0x8d, 0x02, 0xd3, 0xad, 0x0e, 0xd4, 0x8d, 0x86, 0x09, 0xa9, 0x00, 0x8d, 0x0e, 0xd4, 0x8d, 0x00, 0xd4, 0x8d, 0x08,
    0xd2, 0xa9, 0x03, 0x8d, 0x0f, 0xd2, 0x78, 0xa9, 0x34, 0x8d, 0x03, 0xd3, 0x60, 0xa9, 0x3c, 0x8d, 0x02, 0xd3, 0xad, 0x86,
    0x09, 0x8d, 0x0e, 0xd4, 0x58, 0x60, 0x20, 0x67, 0x09, 0xe0, 0x14, 0xb0, 0xf9, 0xe0, 0x08, 0x90, 0x30, 0xa0, 0x08, 0xa9,
    0x00, 0x48, 0x20, 0x67, 0x09, 0x68, 0xe0, 0x08, 0x90, 0x23, 0xe0, 0x2b, 0xb0, 0x1f, 0xe0, 0x14, 0x6a, 0x88, 0xf0, 0x07,
    0xa2, 0x06, 0xca, 0xd0, 0xfd, 0xf0, 0xe6, 0x91, 0x32, 0xee, 0x84, 0x09, 0xd0, 0x03, 0xee, 0x85, 0x09, 0xe6, 0x32, 0xd0,
    0x02, 0xe6, 0x33, 0xd0, 0xd0, 0x60, 0xa5, 0x2f, 0x85, 0x30, 0xa0, 0x00, 0x20, 0x67, 0x09, 0xe0, 0x14, 0x90, 0xf3, 0xe0,
    0x2b, 0xb0, 0xef, 0xc8, 0xd0, 0xf2, 0xc6, 0x30, 0xd0, 0xee, 0x60, 0xa2, 0x00, 0xa9, 0x10, 0x2c, 0x0f, 0xd2, 0xf0, 0xfb,
    0x8d, 0x1a, 0xd0, 0xe8, 0x2c, 0x0f, 0xd2, 0xd0, 0xfa, 0x4a, 0x8d, 0x1a, 0xd0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x20, 0x8e, 0xef, 0x20, 0x3e, 0xc6, 0xa9, 0xff, 0x8d, 0xfc, 0x02, 0xad, 0xfc, 0x02, 0xc9, 0xff, 0xf0,
    0xf9, 0x4c, 0x03, 0x08, 0xa9, 0x00, 0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85, 0x09, 0xa9, 0xff, 0x8d, 0x01, 0xd3, 0x8d, 0xf8,
    0x03, 0xa9, 0xc8, 0x85, 0x0c, 0xa9, 0x09, 0x85, 0x0d, 0xa9, 0x50, 0x8d, 0xe2, 0x02, 0x8d, 0xe0, 0x02, 0xa9, 0x08, 0x8d,
    0xe1, 0x02, 0x8d, 0xe3, 0x02, 0x20, 0x74, 0xe4, 0xa9, 0xd2, 0x85, 0x0a, 0xa9, 0x09, 0x85, 0x0b, 0x18, 0x60, 0x4c, 0x03,
    0x08, 0xd5, 0x01, 0xa9, 0x00, 0x85, 0x32, 0xa9, 0x20, 0x85, 0x33, 0xa9, 0x00, 0x85, 0x34, 0xa9, 0x08, 0x85, 0x35, 0xa0,
    0x00, 0xae, 0xd6, 0x21, 0xf0, 0x0e, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xd0, 0xf9, 0xe6, 0x33, 0xe6, 0x35, 0xca, 0xd0, 0xf2,
    0xae, 0xd5, 0x21, 0xf0, 0x08, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xca, 0xd0, 0xf8, 0x4c, 0x00, 0x08


};




}
