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.config.PulseCorrectionConfigEntry;
import turgen.dtb.DOS2Binary;
import turgen.dtb.DOS2BinaryProcessingException;
import turgen.dtb.Segment;
import turgen.generator.PulseCrate;
import turgen.playlist.AbstractPlayListItem;
import turgen.plugin.standard.StandardPlugin;
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;
    private BooleanConfigEntry cfgLoaderAsStandardBootFile;
    private BooleanConfigEntry cfgPrependTurboROMLoaderAsStandardBootFile;
    private BooleanConfigEntry cfgUseDiagnosticTurboROMLoader;
    private PulseCorrectionConfigEntry cfgPulseCorrections;
    private BooleanConfigEntry cfgGenerateLoaderOnly;

    /**
     * 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[] convertItem(AbstractPlayListItem _pl) throws Exception {

        PlTurboROMPlaylistItem pl;
        pl = (PlTurboROMPlaylistItem) _pl;

        pl.pulses.applyCorrections(cfgPulseCorrections.getAsPulseCrate(), false);
        
        /*Initialization of streams and initial setup instructions*/
        InstructionStream is = new InstructionStream();


        /*Code dependent on the conversion type*/
        switch (pl.conversionType) {
            case PlTurboROMPlaylistItem.CV_COMPATIBLE_BINARY: {
                converCompatibleBinaryFile(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();

    }

    public String getLastError() {
        return lastError;
    }

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

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

    public String validateItemOnPanel() {
        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);
        cfgLoaderAsStandardBootFile = new BooleanConfigEntry("loaderAsStandardBootFile", "Convert binary loader to a standard cassette boot file", false);
        cfgPrependTurboROMLoaderAsStandardBootFile = new BooleanConfigEntry("prependTurboROMLoaderAsStandardBootFile", "Prepend Turbo ROM loader", false);
        cfgUseDiagnosticTurboROMLoader = new BooleanConfigEntry("useDiagnosticTurboROMLoader", "Use diagnostic Turbo ROM loader", false);
        cfgGenerateLoaderOnly = new BooleanConfigEntry("generateLoaderOnly","Generate loader only",false);
        cfgPulseCorrections = new PulseCorrectionConfigEntry("pulseCorrections", "Pulse corrections");

        /*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(cfgLoaderAsStandardBootFile);
        cfcPlTurboROM.addEntry(cfgPrependTurboROMLoaderAsStandardBootFile);
        cfcPlTurboROM.addEntry(cfgUseDiagnosticTurboROMLoader);
        cfcPlTurboROM.addEntry(cfgBasicLoadAddress);
        cfcPlTurboROM.addEntry(cfgBasicRunAddress);
        cfcPlTurboROM.addEntry(cfgGenerateLoaderOnly);
        cfcPlTurboROM.addEntry(cfgPulseCorrections);

        /*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 converCompatibleBinaryFile(InstructionStream is, PlTurboROMPlaylistItem pli) throws Exception {
        if (cfgPrependTurboROMLoaderAsStandardBootFile.getAsBoolean() == true) {
            prependTurboROMLoader(is);
        }
        if (cfgGenerateLoaderOnly.getAsBoolean()==true) return;
        convertCompatibleBinaryFileImpl(is, pli.inFile, pli.fname, null, pli.pulses);
    }

    /**
     * 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 convertCompatibleBinaryFileImpl(InstructionStream is, String inputFilespec, String fname, int[] inputFileData, PulseCrate pulses) 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);

        /*Setup*/
        Utils.addSetupInstruction(is, pulses, casChunkName, 1 - cfgInvertPolarity.getAsInt(), SignalGenerator.FLAG_ORDER_LH);

        /*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);

        /*Setup*/
        Utils.addSetupInstruction(is, pl.pulses, casChunkName, 1 - cfgInvertPolarity.getAsInt(), SignalGenerator.FLAG_ORDER_LH);

        /*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 {

        /*Prepend binary loader as a natural Turbo ROM file or as a standard cassette boot file*/
        if (cfgLoaderAsStandardBootFile.getAsBoolean() == false) {
            if (cfgPrependTurboROMLoaderAsStandardBootFile.getAsBoolean() == true) {
                prependTurboROMLoader(is);
            }
            convertCompatibleBinaryFileImpl(is, null, pl.fname, binaryLoaderData, pl.pulses);
            if (cfgGenerateLoaderOnly.getAsBoolean()==true) {
                return;
            }
        }
        else {
            /*Find standard plugin*/
            Plugin stdPlugin = Turgen.pluginManager.getPlugin("turgen.plugin.standard.StandardPlugin");
            if (stdPlugin == null) {
                throw new PluginException("The Standard plugin required to convert the binary loader is not present");
            }

            /*Prepare copy of the loader data*/
            int[] loaderData = new int[simpleBinaryLoaderData.length];
            System.arraycopy(simpleBinaryLoaderData, 0, loaderData, 0, simpleBinaryLoaderData.length);

            String fName = pl.fname;
            if (fName.length() > 20) {
                fName = fName.substring(0, 20);
            }
            char[] chars = fName.toCharArray();

            /*Place name*/
            for (int i = 0; i < chars.length; i++) {
                loaderData[0x09E9 - 0x0800 + 6 + 6 + i] = chars[i];
            }
            /*Allow loader to display the name - place NOPs*/
            for (int i = 0; i < 3; i++) {
                loaderData[0x099D - 0x0800 + 6 + 6 + i] = 0xEA;
            }

            ((StandardPlugin) stdPlugin).convertDirtyMonolithicBinaryUsingData(loaderData, is);
            
            if (cfgGenerateLoaderOnly.getAsBoolean()==true) {
                return;
            }

            /*Setup*/
            Utils.addSetupInstruction(is, pl.pulses, casChunkName, 1 - cfgInvertPolarity.getAsInt(), SignalGenerator.FLAG_ORDER_LH);
        }
        
        

        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);
    }

    private void prependTurboROMLoader(InstructionStream is) throws Exception {
        /*Find standard plugin*/
        Plugin stdPlugin = Turgen.pluginManager.getPlugin("turgen.plugin.standard.StandardPlugin");
        if (stdPlugin == null) {
            throw new PluginException("The Standard plugin required to convert  loader is not present");
        }
        ((StandardPlugin) stdPlugin).convertDirtyMonolithicBinaryUsingData(
                cfgUseDiagnosticTurboROMLoader.getAsBoolean() ? turboROMDiagnosticLoaderData : turboROMNaturalLoaderData,
                is);
    }

    /*Turbo ROM binary loader*/
    private static final int[] binaryLoaderData = {
        0xff, 0xff, 0xe0, 0x02, 0xe1, 0x02, 0x00, 0x22, 0x00, 0x20, 0x34, 0x22, 0x4c, 0x9d, 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, 0x88, 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,
        0x88, 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, 0x85, 0x09, 0x8d,
        0x86, 0x09, 0xa0, 0x00, 0x51, 0x32, 0xe6, 0x32, 0xd0, 0x02, 0xe6, 0x33, 0xee, 0x85, 0x09, 0xd0, 0x03, 0xee, 0x86, 0x09,
        0xa4, 0x34, 0xcc, 0x85, 0x09, 0xd0, 0xe7, 0xa4, 0x35, 0xcc, 0x86, 0x09, 0xd0, 0xe0, 0x85, 0x34, 0xa5, 0x34, 0x60, 0xa9,
        0x34, 0x8d, 0x02, 0xd3, 0xad, 0x0e, 0xd4, 0x8d, 0x87, 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, 0x87,
        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, 0x85, 0x09, 0xd0, 0x03, 0xee, 0x86, 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, 0x00, 0x20, 0x8e, 0xef, 0x20, 0x3e, 0xc6, 0xa9, 0xff, 0x8d, 0xfc, 0x02, 0xad, 0xfc, 0x02, 0xc9, 0xff,
        0xf0, 0xf9, 0x4c, 0x03, 0x08, 0x4c, 0xaf, 0x09, 0xa2, 0xe8, 0xa0, 0x09, 0x20, 0x42, 0xc6, 0xa9, 0x01, 0x8d, 0xfc, 0x02,
        0x20, 0xfc, 0xfd, 0xa9, 0x00, 0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85, 0x09, 0xa9, 0xff, 0x8d, 0x01, 0xd3, 0x8d, 0xf8, 0x03,
        0xa9, 0xdb, 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, 0xe5, 0x85, 0x0a, 0xa9, 0x09, 0x85, 0x0b, 0x18, 0x60, 0x4c, 0x03, 0x08,
        0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x9b, 0xfe, 0x01, 0xa9, 0x00, 0x85, 0x32, 0xa9, 0x20, 0x85, 0x33, 0xa9, 0x00, 0x85, 0x34, 0xa9, 0x08, 0x85, 0x35,
        0xa0, 0x00, 0xae, 0xff, 0x21, 0xf0, 0x0e, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xd0, 0xf9, 0xe6, 0x33, 0xe6, 0x35, 0xca, 0xd0,
        0xf2, 0xae, 0xfe, 0x21, 0xf0, 0x08, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xca, 0xd0, 0xf8, 0x4c, 0x00, 0x08

    };

    /*Simpler binary loader data*/
    private static final int[] simpleBinaryLoaderData = {
        0xff, 0xff, 0xe0, 0x02, 0xe1, 0x02, 0x00, 0x08, 0x00, 0x08, 0xfd, 0x09, 0x4c, 0x9d, 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, 0x88, 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,
        0x88, 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, 0x85, 0x09, 0x8d,
        0x86, 0x09, 0xa0, 0x00, 0x51, 0x32, 0xe6, 0x32, 0xd0, 0x02, 0xe6, 0x33, 0xee, 0x85, 0x09, 0xd0, 0x03, 0xee, 0x86, 0x09,
        0xa4, 0x34, 0xcc, 0x85, 0x09, 0xd0, 0xe7, 0xa4, 0x35, 0xcc, 0x86, 0x09, 0xd0, 0xe0, 0x85, 0x34, 0xa5, 0x34, 0x60, 0xa9,
        0x34, 0x8d, 0x02, 0xd3, 0xad, 0x0e, 0xd4, 0x8d, 0x87, 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, 0x87,
        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, 0x85, 0x09, 0xd0, 0x03, 0xee, 0x86, 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, 0x00, 0x20, 0x8e, 0xef, 0x20, 0x3e, 0xc6, 0xa9, 0xff, 0x8d, 0xfc, 0x02, 0xad, 0xfc, 0x02, 0xc9, 0xff,
        0xf0, 0xf9, 0x4c, 0x03, 0x08, 0x4c, 0xaf, 0x09, 0xa2, 0xe8, 0xa0, 0x09, 0x20, 0x42, 0xc6, 0xa9, 0x01, 0x8d, 0xfc, 0x02,
        0x20, 0xfc, 0xfd, 0xa9, 0x00, 0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85, 0x09, 0xa9, 0xff, 0x8d, 0x01, 0xd3, 0x8d, 0xf8, 0x03,
        0xa9, 0xdb, 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, 0xe5, 0x85, 0x0a, 0xa9, 0x09, 0x85, 0x0b, 0x18, 0x60, 0x4c, 0x03, 0x08,
        0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x9b
    };

    private static final int[] turboROMNaturalLoaderData = {
        0xff, 0xff, 0xe0, 0x02, 0xe1, 0x02, 0x9a, 0x21, 0x00, 0x20, 0x39, 0x22, 0xa9, 0x00, 0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85,
        0x09, 0x20, 0x7e, 0xcc, 0x20, 0xa7, 0xcc, 0x20, 0x7e, 0xcc, 0xe6, 0x32, 0x20, 0xb8, 0xcc, 0xcd, 0xc0, 0x03, 0xf0, 0x03,
        0x4c, 0x09, 0xcc, 0xa0, 0x14, 0xb9, 0xce, 0x03, 0x91, 0x58, 0x88, 0xd0, 0xf8, 0xa9, 0x00, 0x85, 0x14, 0xa5, 0x14, 0xc9,
        0x4b, 0xd0, 0xfa, 0x20, 0x92, 0xcc, 0x20, 0xa7, 0xcc, 0x20, 0x92, 0xcc, 0x20, 0xb8, 0xcc, 0xcd, 0xc5, 0x03, 0xf0, 0x03,
        0x4c, 0x83, 0xcd, 0xa2, 0x10, 0xbd, 0x6d, 0xcc, 0x9d, 0xe8, 0x03, 0xca, 0xd0, 0xf7, 0xa9, 0xf6, 0x8d, 0xf1, 0x03, 0xa9,
        0x03, 0x8d, 0xf2, 0x03, 0xad, 0xe4, 0x03, 0xf0, 0x0a, 0xa9, 0xe8, 0x8d, 0xc8, 0x03, 0xa9, 0x03, 0x8d, 0xc9, 0x03, 0x4c,
        0xe9, 0x03, 0x78, 0xa9, 0xb3, 0x8d, 0x01, 0xd3, 0x58, 0x20, 0x7b, 0xcc, 0x6c, 0xc6, 0x03, 0x6c, 0xc8, 0x03, 0xa9, 0xc0,
        0x85, 0x32, 0xa9, 0x03, 0x85, 0x33, 0xa9, 0x29, 0x85, 0x34, 0xa9, 0x00, 0x85, 0x35, 0x8d, 0xe9, 0x03, 0x60, 0xad, 0xca,
        0x03, 0x85, 0x32, 0xad, 0xcb, 0x03, 0x85, 0x33, 0xad, 0xcc, 0x03, 0x85, 0x34, 0xad, 0xcd, 0x03, 0x85, 0x35, 0x60, 0xa9,
        0x01, 0x85, 0x2f, 0x20, 0xe5, 0xcc, 0x20, 0x50, 0xcd, 0x20, 0x14, 0xcd, 0x20, 0x07, 0xcd, 0x60, 0xa9, 0x00, 0x8d, 0x80,
        0xcd, 0x8d, 0x81, 0xcd, 0xa0, 0x00, 0x51, 0x32, 0xe6, 0x32, 0xd0, 0x02, 0xe6, 0x33, 0xee, 0x80, 0xcd, 0xd0, 0x03, 0xee,
        0x81, 0xcd, 0xa4, 0x34, 0xcc, 0x80, 0xcd, 0xd0, 0xe7, 0xa4, 0x35, 0xcc, 0x81, 0xcd, 0xd0, 0xe0, 0x85, 0x34, 0xa5, 0x34,
        0x60, 0xa9, 0x34, 0x8d, 0x02, 0xd3, 0xad, 0x0e, 0xd4, 0x8d, 0x82, 0xcd, 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, 0x82, 0xcd, 0x8d, 0x0e, 0xd4, 0x58, 0x60, 0x20, 0x69, 0xcd, 0xe0, 0x14, 0xb0, 0xf9, 0xe0, 0x08, 0x90, 0x30, 0xa0,
        0x08, 0xa9, 0x00, 0x48, 0x20, 0x69, 0xcd, 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, 0x80, 0xcd, 0xd0, 0x03, 0xee, 0x81, 0xcd, 0xe6,
        0x32, 0xd0, 0x02, 0xe6, 0x33, 0xd0, 0xd0, 0x60, 0xa5, 0x2f, 0x85, 0x30, 0xa0, 0x00, 0x20, 0x69, 0xcd, 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, 0x20,
        0x8e, 0xef, 0x20, 0x3e, 0xc6, 0xa9, 0xff, 0x8d, 0xfc, 0x02, 0xad, 0xfc, 0x02, 0xc9, 0xff, 0xf0, 0xf9, 0x4c, 0x00, 0xcc,
        0x98, 0x01, 0xa9, 0x00, 0x85, 0xcb, 0x85, 0xcd, 0xa9, 0xc0, 0x85, 0xcc, 0xa9, 0x40, 0x85, 0xce, 0xa0, 0x00, 0xb1, 0xcb,
        0x91, 0xcd, 0xc8, 0xd0, 0xf9, 0xe6, 0xce, 0xe6, 0xcc, 0xf0, 0x0c, 0xa5, 0xcc, 0xc9, 0xd0, 0xd0, 0xed, 0xa9, 0xd8, 0x85,
        0xcc, 0xd0, 0xe7, 0x08, 0x78, 0xad, 0x0e, 0xd4, 0x48, 0xa9, 0x00, 0x8d, 0x0e, 0xd4, 0xad, 0x01, 0xd3, 0x29, 0xfe, 0x8d,
        0x01, 0xd3, 0xa9, 0xc0, 0x85, 0xce, 0xa9, 0x40, 0x85, 0xcc, 0xb1, 0xcb, 0x8d, 0x1a, 0xd0, 0x91, 0xcd, 0xc8, 0xd0, 0xf6,
        0xe6, 0xcc, 0xe6, 0xce, 0xf0, 0x0c, 0xa5, 0xce, 0xc9, 0xd0, 0xd0, 0xea, 0xa9, 0xd8, 0x85, 0xce, 0xd0, 0xe4, 0x68, 0x8d,
        0x0e, 0xd4, 0x78, 0xa9, 0xb2, 0x8d, 0x01, 0xd3, 0x58, 0xa9, 0x00, 0x85, 0x32, 0xa9, 0x20, 0x85, 0x33, 0xa9, 0x00, 0x85,
        0x34, 0xa9, 0xcc, 0x85, 0x35, 0xa0, 0x00, 0xae, 0x99, 0x21, 0xf0, 0x0e, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xd0, 0xf9, 0xe6,
        0x33, 0xe6, 0x35, 0xca, 0xd0, 0xf2, 0xae, 0x98, 0x21, 0xf0, 0x08, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xca, 0xd0, 0xf8, 0x4c,
        0x00, 0xcc

    };

    int[] turboROMDiagnosticLoaderData = {
        0xff, 0xff, 0xe0, 0x02, 0xe1, 0x02, 0x89, 0x22, 0x00, 0x20, 0x28, 0x23, 0xa9, 0x00, 0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85,
        0x09, 0xa2, 0xda, 0xa0, 0xcd, 0x20, 0x42, 0xc6, 0x20, 0xfd, 0xcd, 0x20, 0x97, 0xcc, 0x20, 0xc0, 0xcc, 0xa5, 0x32, 0x8d,
        0xfe, 0x03, 0xa5, 0x33, 0x8d, 0xff, 0x03, 0x20, 0x97, 0xcc, 0xe6, 0x32, 0x20, 0xd1, 0xcc, 0x8d, 0xfd, 0x03, 0x20, 0x15,
        0xce, 0xad, 0xfd, 0x03, 0xcd, 0x00, 0x04, 0xf0, 0x03, 0x4c, 0x08, 0xce, 0xa0, 0x14, 0xb9, 0x0e, 0x04, 0x91, 0x58, 0x88,
        0xd0, 0xf8, 0x20, 0xf0, 0xcd, 0x20, 0xfd, 0xcd, 0x20, 0xab, 0xcc, 0x20, 0xc0, 0xcc, 0x20, 0xab, 0xcc, 0x20, 0xd1, 0xcc,
        0xcd, 0x05, 0x04, 0xf0, 0x03, 0x4c, 0xe4, 0xcd, 0xa2, 0x10, 0xbd, 0x86, 0xcc, 0x9d, 0x28, 0x04, 0xca, 0xd0, 0xf7, 0xa9,
        0x36, 0x8d, 0x31, 0x04, 0xa9, 0x04, 0x8d, 0x32, 0x04, 0xad, 0x24, 0x04, 0xf0, 0x0a, 0xa9, 0x28, 0x8d, 0x08, 0x04, 0xa9,
        0x04, 0x8d, 0x09, 0x04, 0x4c, 0x29, 0x04, 0x78, 0xa9, 0xb3, 0x8d, 0x01, 0xd3, 0x58, 0x20, 0x94, 0xcc, 0x6c, 0x06, 0x04,
        0x6c, 0x08, 0x04, 0xa9, 0x00, 0x85, 0x32, 0xa9, 0x04, 0x85, 0x33, 0xa9, 0x29, 0x85, 0x34, 0xa9, 0x00, 0x85, 0x35, 0x8d,
        0x29, 0x04, 0x60, 0xad, 0x0a, 0x04, 0x85, 0x32, 0xad, 0x0b, 0x04, 0x85, 0x33, 0xad, 0x0c, 0x04, 0x85, 0x34, 0xad, 0x0d,
        0x04, 0x85, 0x35, 0x60, 0xa9, 0x01, 0x85, 0x2f, 0x20, 0xfe, 0xcc, 0x20, 0x69, 0xcd, 0x20, 0x2d, 0xcd, 0x20, 0x20, 0xcd,
        0x60, 0xa9, 0x00, 0x8d, 0x99, 0xcd, 0x8d, 0x9a, 0xcd, 0xa0, 0x00, 0x51, 0x32, 0xe6, 0x32, 0xd0, 0x02, 0xe6, 0x33, 0xee,
        0x99, 0xcd, 0xd0, 0x03, 0xee, 0x9a, 0xcd, 0xa4, 0x34, 0xcc, 0x99, 0xcd, 0xd0, 0xe7, 0xa4, 0x35, 0xcc, 0x9a, 0xcd, 0xd0,
        0xe0, 0x85, 0x34, 0xa5, 0x34, 0x60, 0xa9, 0x34, 0x8d, 0x02, 0xd3, 0xad, 0x0e, 0xd4, 0x8d, 0x9b, 0xcd, 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, 0x9b, 0xcd, 0x8d, 0x0e, 0xd4, 0x58, 0x60, 0x20, 0x82, 0xcd, 0xe0, 0x14, 0xb0, 0xf9,
        0xe0, 0x08, 0x90, 0x30, 0xa0, 0x08, 0xa9, 0x00, 0x48, 0x20, 0x82, 0xcd, 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, 0x99, 0xcd, 0xd0,
        0x03, 0xee, 0x9a, 0xcd, 0xe6, 0x32, 0xd0, 0x02, 0xe6, 0x33, 0xd0, 0xd0, 0x60, 0xa5, 0x2f, 0x85, 0x30, 0xa0, 0x00, 0x20,
        0x82, 0xcd, 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, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x20, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x9b, 0x30, 0x31, 0x32,
        0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x9b, 0x00, 0x00, 0x54, 0x75, 0x72, 0x62, 0x6f, 0x20, 0x52, 0x4f, 0x4d, 0x9b, 0x20, 0x8e, 0xef, 0x20,
        0x3e, 0xc6, 0x20, 0xf0, 0xcd, 0x4c, 0x00, 0xcc, 0xa9, 0xff, 0x8d, 0xfc, 0x02, 0xad, 0xfc, 0x02, 0xc9, 0xff, 0xf0, 0xf9,
        0x60, 0x48, 0xa9, 0x01, 0x8d, 0xfc, 0x02, 0x20, 0xfc, 0xfd, 0x68, 0x60, 0xa2, 0x9c, 0xa0, 0xcd, 0x20, 0x42, 0xc6, 0x20,
        0xf0, 0xcd, 0x4c, 0x00, 0xcc, 0xa2, 0xd7, 0xa0, 0xcd, 0x20, 0x42, 0xc6, 0xa9, 0xfd, 0x85, 0x00, 0xa9, 0x03, 0x85, 0x01,
        0xa0, 0x00, 0xa2, 0x00, 0xa9, 0x03, 0x8d, 0xd9, 0xcd, 0x4c, 0x35, 0xce, 0xa9, 0x0a, 0x8d, 0xd9, 0xcd, 0xb1, 0x00, 0x29,
        0xf0, 0x4a, 0x4a, 0x4a, 0x4a, 0x8c, 0xd8, 0xcd, 0xa8, 0xb9, 0xa9, 0xcd, 0x9d, 0xb9, 0xcd, 0xe8, 0xac, 0xd8, 0xcd, 0xb1,
        0x00, 0x29, 0x0f, 0x8c, 0xd8, 0xcd, 0xa8, 0xb9, 0xa9, 0xcd, 0x9d, 0xb9, 0xcd, 0xe8, 0xe8, 0xac, 0xd8, 0xcd, 0xc8, 0xc0,
        0x2c, 0xf0, 0x05, 0xce, 0xd9, 0xcd, 0xd0, 0xcd, 0x8c, 0xd8, 0xcd, 0xa2, 0xb9, 0xa0, 0xcd, 0x20, 0x42, 0xc6, 0xac, 0xd8,
        0xcd, 0xc0, 0x2c, 0xd0, 0x01, 0x60, 0xa2, 0x1e, 0xa9, 0x20, 0x9d, 0xb8, 0xcd, 0xca, 0xd0, 0xfa, 0x4c, 0x30, 0xce, 0x87,
        0x02, 0xa9, 0x00, 0x85, 0xcb, 0x85, 0xcd, 0xa9, 0xc0, 0x85, 0xcc, 0xa9, 0x40, 0x85, 0xce, 0xa0, 0x00, 0xb1, 0xcb, 0x91,
        0xcd, 0xc8, 0xd0, 0xf9, 0xe6, 0xce, 0xe6, 0xcc, 0xf0, 0x0c, 0xa5, 0xcc, 0xc9, 0xd0, 0xd0, 0xed, 0xa9, 0xd8, 0x85, 0xcc,
        0xd0, 0xe7, 0x08, 0x78, 0xad, 0x0e, 0xd4, 0x48, 0xa9, 0x00, 0x8d, 0x0e, 0xd4, 0xad, 0x01, 0xd3, 0x29, 0xfe, 0x8d, 0x01,
        0xd3, 0xa9, 0xc0, 0x85, 0xce, 0xa9, 0x40, 0x85, 0xcc, 0xb1, 0xcb, 0x8d, 0x1a, 0xd0, 0x91, 0xcd, 0xc8, 0xd0, 0xf6, 0xe6,
        0xcc, 0xe6, 0xce, 0xf0, 0x0c, 0xa5, 0xce, 0xc9, 0xd0, 0xd0, 0xea, 0xa9, 0xd8, 0x85, 0xce, 0xd0, 0xe4, 0x68, 0x8d, 0x0e,
        0xd4, 0x78, 0xa9, 0xb2, 0x8d, 0x01, 0xd3, 0x58, 0xa9, 0x00, 0x85, 0x32, 0xa9, 0x20, 0x85, 0x33, 0xa9, 0x00, 0x85, 0x34,
        0xa9, 0xcc, 0x85, 0x35, 0xa0, 0x00, 0xae, 0x88, 0x22, 0xf0, 0x0e, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xd0, 0xf9, 0xe6, 0x33,
        0xe6, 0x35, 0xca, 0xd0, 0xf2, 0xae, 0x87, 0x22, 0xf0, 0x08, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xca, 0xd0, 0xf8, 0x4c, 0x00,
        0xcc
    };

}
