package izapple2

import (
	"fmt"
	"os"
	"path/filepath"
)

/*
Apple II DAN ][ CONTROLLER CARD.]

See:
	https://github.com/profdc9/Apple2Card
	https://github.com/ThorstenBr/Apple2Card
	https://www.applefritter.com/content/dan-sd-card-disk-controller

*/

// CardDan2Controller represents a Dan ][ controller card
type CardDan2Controller struct {
	cardBase

	commandBuffer  []uint8
	responseBuffer []uint8

	receivingWiteBuffer bool
	writeBuffer         []uint8
	commitWrite         func([]uint8) error

	portB uint8
	portC uint8

	slotA *cardDan2ControllerSlot
	slotB *cardDan2ControllerSlot

	improved bool
	a2slot   uint8
}

type cardDan2ControllerSlot struct {
	card        *CardDan2Controller
	path        string
	fileNo      uint8
	fileName    string
	fileNameAlt string
}

func newCardDan2ControllerBuilder() *cardBuilder {
	return &cardBuilder{
		name:        "Dan ][ Controller card",
		description: "Apple II Peripheral Card that Interfaces to a ATMEGA328P for SD card storage",
		defaultParams: &[]paramSpec{
			{"improved", "Emulate improved firmware from ThorstenBr", "true"},
			{"slot1", "Image in slot 1. File for raw device, folder for fs mode using files as BLKDEV0x.PO", ""},
			{"slot1file", "Device selected in slot 1: 0 for raw device, 1 to 9 for file number", "0"},
			{"slot2", "Image in slot 2. File for raw device, folder for fs mode using files as BLKDEV0x.PO", ""},
			{"slot2file", "Device selected in slot 2: 0 for raw device, 1 to 9 for file number", "0"},
		},
		buildFunc: func(params map[string]string) (Card, error) {
			var c CardDan2Controller
			c.responseBuffer = make([]uint8, 0, 1000)

			c.improved = paramsGetBool(params, "improved")

			c.slotA = &cardDan2ControllerSlot{}
			c.slotA.card = &c
			c.slotA.path = params["slot1"]
			num, _ := paramsGetUInt8(params, "slot1file")
			c.slotA.fileNo = uint8(num)
			c.slotA.initializeDrive()

			c.slotB = &cardDan2ControllerSlot{}
			c.slotB.card = &c
			c.slotB.path = params["slot2"]
			num, _ = paramsGetUInt8(params, "slot2file")
			c.slotB.fileNo = uint8(num)
			c.slotB.initializeDrive()

			romFilename := "<internal>/Apple2CardFirmware.bin"
			if c.improved {
				romFilename = "<internal>/Apple2CardFirmwareImproved.bin"
			}
			err := c.loadRomFromResource(romFilename)
			if err != nil {
				return nil, err
			}

			return &c, nil
		},
	}
}

func (c *CardDan2Controller) assign(a *Apple2, slot int) {
	c.addCardSoftSwitches(func(address uint8, data uint8, write bool) uint8 {
		address &= 0x03 // only A0 and A1 are connected
		if write {
			c.writeSoftSwitch(address, data)
			return 0
		} else {
			return c.readSoftSwitch(address)
		}
	}, "DAN2CONTROLLER")

	c.cardBase.assign(a, slot)
}

func (c *CardDan2Controller) writeSoftSwitch(address uint8, data uint8) {
	switch address {
	case 0: // Port A
		if c.receivingWiteBuffer {
			c.writeBuffer = append(c.writeBuffer, data)
			if len(c.writeBuffer) == 512 {
				c.commitWrite(c.writeBuffer)
			}
		} else if c.commandBuffer == nil {
			if data == 0xac {
				c.commandBuffer = make([]uint8, 0)
			} else if data == 0x30 || data == 0x20 {
				c.tracef("Sync started to upload Atmega firmware, not supported\n")
			} else {
				c.tracef("Not supported command prefix $%02x\n", data)
			}
		} else {
			c.commandBuffer = append(c.commandBuffer, data)
			c.processCommand()
		}
	case 3: // Control
		if data&0x80 == 0 {
			bit := (data >> 1) & 0x08
			if data&1 == 0 {
				// Reset bit
				c.portC &^= uint8(1) << bit
			} else {
				// Set bit
				c.portC |= uint8(1) << bit
			}
			c.romCsxx.setPage((c.portC & 0x07) | ((c.portB << 4) & 0xf0))
		} else {
			if data != 0xfa {
				c.tracef("Not supported status %v, it must be 0xfa\n", data)
			}
			/* Sets the 8255 with status 0xfa, 1111_1010:
			1:  set mode
			11: port A mode 2
			1:  port A input
			1:  port C(upper) input
			0:  port B mode 0
			1:  port B input
			0:  port C(lower) output
			*/

		}
	}
}

func (c *CardDan2Controller) readSoftSwitch(address uint8) uint8 {
	switch address {
	case 0: // Port A
		if len(c.responseBuffer) > 0 {
			value := c.responseBuffer[0]
			c.responseBuffer = c.responseBuffer[1:]
			return value
		}
		return 0
	case 2: // Port C
		portC := uint8(0x80) // bit 7-nOBF is always 1, the output buffer is never full
		if len(c.responseBuffer) > 0 {
			portC |= 0x20 // bit 5-niBF is 1 if the input buffer has data
		}
		return portC
	}

	return 0
}

func (s *cardDan2ControllerSlot) blockPosition(unit uint8, block uint16) int64 {
	if s.fileNo == 0 {
		// Raw device
		return 512 * (int64(block) + (int64(unit&0x0f) << 12))
	} else {
		// File device
		return 512 * int64(block)
	}
}

func (s *cardDan2ControllerSlot) openFile() (*os.File, error) {
	file, err := os.OpenFile(s.fileName, os.O_RDWR, 0)
	if err == nil {
		return file, nil
	}

	if s.card.improved && s.fileNameAlt != s.fileName {
		return os.OpenFile(s.fileNameAlt, os.O_RDWR, 0)
	}
	return nil, err
}

func (s *cardDan2ControllerSlot) status(unit uint8) error {
	file, err := s.openFile()
	if err != nil {
		return err
	}
	defer file.Close()
	return nil
}

func (s *cardDan2ControllerSlot) readBlock(unit uint8, block uint16) ([]uint8, error) {
	file, err := s.openFile()
	if err != nil {
		return nil, err
	}
	defer file.Close()

	position := s.blockPosition(unit, block)
	buffer := make([]uint8, 512)
	_, err = file.ReadAt(buffer, position)
	if err != nil {
		return nil, err
	}
	return buffer, nil
}

func (c *CardDan2Controller) readBootBlock(block uint16) []uint8 {
	position := 512 * int64(block)
	program := PROGMEM[:]
	if c.improved {
		// If a file VOLA2.po is present, it is used. Not emulated.
		program = PROGMEM_v7[:]
	}
	if position+512 > int64(len(program)) {
		return []uint8{}
	}
	return program[position : position+512]
}

func (s *cardDan2ControllerSlot) writeBlock(unit uint8, block uint16, data []uint8) error {
	file, err := s.openFile()
	if err != nil {
		return err
	}
	defer file.Close()

	position := s.blockPosition(unit, block)
	_, err = file.WriteAt(data, position)
	if err != nil {
		return err
	}
	return nil
}

// Version info
func (c *CardDan2Controller) versionInfo() []uint8 {
	return []uint8{
		3, 4, 2, // Version 3.4.2
		3, //  DAN ][ CONTROLLER CARD with Atmega328P
		0x80 | // Support raw sd
			0x40, // Support fat
		// 0x20 | // Support Ethernet
		// 0x10 | // Support FTP
	}
}

func (s *cardDan2ControllerSlot) initializeDrive() {
	if s.fileNo == 255 {
		s.fileNo = 0 // Wide raw not supported, changed to raw
	}
	if s.fileNo == 0 {
		// Raw device
		s.fileName = s.path
		s.fileNameAlt = s.path
	} else {
		s.fileName = filepath.Join(s.path, fmt.Sprintf("BLKDEV%02X.PO", s.fileNo))
		s.fileNameAlt = filepath.Join(s.path, fmt.Sprintf("VOL%02X.PO", s.fileNo))
	}
}

func (c *CardDan2Controller) selectSlot(unit uint8) *cardDan2ControllerSlot {
	if unit&0x80 == 0 {
		return c.slotA
	} else {
		return c.slotB
	}
}

func (c *CardDan2Controller) processCommand() {
	// See : Apple2Arduino.ino::do_command()
	command := c.commandBuffer[0]

	if !c.improved && command >= 8 && command < 128 {
		// Command not supported in the original firmware
		c.tracef("Command %v not supported in the improved version\n", command)
		c.sendResponseCode(0x27)
		c.commandBuffer = nil
		return
	}

	switch command {
	case 0, 3: // Status and format
		if len(c.commandBuffer) == 6 {
			unit, _, _ := c.getUnitBufBlk()
			slot := c.selectSlot(unit)
			err := slot.status(unit)
			if err != nil {
				c.tracef("Error status : %v\n", err)
				c.sendResponseCode(0x28)
			} else {
				c.sendResponseCode(0x00)
			}

			if command == 0 {
				c.tracef("0-Status unit $%02x\n", unit)
			} else {
				c.tracef("3-Format unit $%02x\n", unit)
			}
			c.commandBuffer = nil
		}

	case 1: // Read block
		if len(c.commandBuffer) == 6 {
			unit, buffer, block := c.getUnitBufBlk()
			c.tracef("1-Read unit $%02x, buffer $%x, block %v\n", unit, buffer, block)

			slot := c.selectSlot(unit)
			data, err := slot.readBlock(unit, block)
			if err != nil {
				c.tracef("Error reading block : %v\n", err)
				c.sendResponseCode(0x28)
			} else {
				c.sendResponse(data...)
			}
			c.commandBuffer = nil
		}

	case 2: // Write block
		if len(c.commandBuffer) == 6 {
			unit, buffer, block := c.getUnitBufBlk()
			c.tracef("2-Write unit $%02x, buffer $%x, block %v\n", unit, buffer, block)

			c.receivingWiteBuffer = true
			c.writeBuffer = make([]uint8, 0, 512)
			c.commitWrite = func(data []uint8) error {
				slot := c.selectSlot(unit)
				err := slot.writeBlock(unit, block, c.writeBuffer)
				if err != nil {
					c.tracef("Error writing block : %v\n", err)
				}
				c.receivingWiteBuffer = false
				c.writeBuffer = nil
				c.commitWrite = nil
				return nil
			}
			c.sendResponseCode(0x00)

			c.commandBuffer = nil
		}

	case 5, 9: // Get volume
		// cmd=5: return eeprom volume configuration. (Not emulated: returns current)
		// cmd=9: return current volume selection
		if len(c.commandBuffer) == 6 {
			c.tracef("%v-Get Volume\n", command)

			c.sendResponse(c.slotA.fileNo, c.slotB.fileNo)
			c.commandBuffer = nil
		}

	case 4, 6, 7, 8: // Set volume
		// cmd=4: permanently selects drives 0+1, single byte response (used by eprom+bootpg for volume selection)
		// cmd=6: temporarily selects drives 0+1, 512byte response (used by bootpg for volume preview)
		// cmd=7: permanently selects drives 0+1, 512byte response (used by bootpg for volume selection)
		// cmd=8: permanently selects drive 0 only, single byte response (used by eprom for quick access keys)
		if len(c.commandBuffer) == 6 {
			unit, _, block := c.getUnitBufBlk()
			c.a2slot = (unit >> 4) & 0x3
			c.slotA.fileNo = uint8(block & 0xff)
			c.slotA.initializeDrive()
			if command != 8 {
				c.slotB.fileNo = uint8((block >> 8) & 0xff)
				c.slotB.initializeDrive()
			}

			c.tracef("%v-Set Volume %v and %v\n",
				command, c.slotA.fileNo, c.slotB.fileNo)

			if command == 4 || command == 8 {
				c.sendResponseCode(0x00) // Success code
			} else {
				c.sendResponse()
				// command 6 does not write eeprom, not emulated
			}
			c.commandBuffer = nil
		}

	case 0xa: // Read failsafe
		if len(c.commandBuffer) == 6 {
			unit, buffer, block := c.getUnitBufBlk()
			c.tracef("1-Read unit $%02x, buffer $%x, block %v\n", unit, buffer, block)

			slot := c.selectSlot(unit)
			data, err := slot.readBlock(unit, block)
			if err != nil {
				c.sendResponse(c.readBootBlock(block)...)
			} else {
				c.sendResponse(data...)
			}
			c.commandBuffer = nil
		}

	case 0x0b: // Version info
		c.tracef("0b-Version info\n")
		c.sendResponse(c.versionInfo()...)
		c.commandBuffer = nil

	case 13 + 128, 32 + 128: // Read bootblock
		if len(c.commandBuffer) == 6 {
			_, _, block := c.getUnitBufBlk()

			c.tracef("ac-Read bootblock\n")
			c.sendResponse(c.readBootBlock(block)...)
			c.commandBuffer = nil
		}

	default: // Unknown command
		c.tracef("Unknown command %v\n", command)
		c.sendResponseCode(0x27)
		c.commandBuffer = nil
	}
}

func (c *CardDan2Controller) sendResponseCode(code uint8) {
	c.responseBuffer = append(c.responseBuffer, code)
}

func (c *CardDan2Controller) sendResponse(response ...uint8) {
	c.responseBuffer = append(c.responseBuffer, 0x00) // Success code
	c.responseBuffer = append(c.responseBuffer, response...)
	rest := 512 - len(response)
	if rest > 0 {
		c.responseBuffer = append(c.responseBuffer, make([]uint8, rest)...)
	}
}

func (c *CardDan2Controller) getUnitBufBlk() (uint8, uint16, uint16) {
	return c.commandBuffer[1],
		uint16(c.commandBuffer[2]) + uint16(c.commandBuffer[3])<<8,
		uint16(c.commandBuffer[4]) + uint16(c.commandBuffer[5])<<8
}

var PROGMEM = [512]uint8{
	0xea, 0xa9, 0x20, 0x85, 0xf0, 0xa9, 0x60, 0x85, 0xf3, 0xa5, 0x43, 0x4a,
	0x4a, 0x4a, 0x4a, 0x29, 0x07, 0x09, 0xc0, 0x85, 0xf2, 0xa0, 0x00, 0x84,
	0xf1, 0x88, 0xb1, 0xf1, 0x85, 0xf1, 0x20, 0x93, 0xfe, 0x20, 0x89, 0xfe,
	0x20, 0x58, 0xfc, 0x20, 0xa2, 0x09, 0xa9, 0x00, 0x85, 0x25, 0x20, 0x22,
	0xfc, 0xa5, 0x25, 0x85, 0xf5, 0x85, 0xf6, 0x20, 0x90, 0x09, 0xa9, 0x00,
	0x85, 0x24, 0xa5, 0x25, 0x20, 0xe3, 0xfd, 0xe6, 0x24, 0x20, 0x7a, 0x09,
	0x20, 0x04, 0x09, 0xa9, 0x14, 0x85, 0x24, 0xa5, 0x25, 0x20, 0xe3, 0xfd,
	0xe6, 0x24, 0xa5, 0x43, 0x09, 0x80, 0x85, 0x43, 0x20, 0x7a, 0x09, 0x20,
	0x04, 0x09, 0xa5, 0x43, 0x29, 0x7f, 0x85, 0x43, 0xe6, 0x25, 0xa5, 0x25,
	0xc9, 0x10, 0x90, 0xbe, 0xa9, 0x00, 0x85, 0x24, 0xa9, 0x12, 0x85, 0x25,
	0x20, 0x22, 0xfc, 0xa2, 0x14, 0x20, 0x66, 0x09, 0x20, 0x61, 0x09, 0xa9,
	0x0a, 0x85, 0x24, 0xa5, 0xf7, 0x20, 0xf8, 0x08, 0xa9, 0x14, 0x85, 0x24,
	0x20, 0x5c, 0x09, 0xa9, 0x1e, 0x85, 0x24, 0xa5, 0xf8, 0x20, 0xf8, 0x08,
	0xa9, 0x0a, 0x85, 0x24, 0x20, 0xca, 0x08, 0x85, 0xf5, 0x20, 0xf8, 0x08,
	0xa9, 0x1e, 0x85, 0x24, 0x20, 0xca, 0x08, 0x85, 0xf6, 0x20, 0xf8, 0x08,
	0x20, 0x8c, 0x09, 0x4c, 0xb7, 0x09, 0xa5, 0xf7, 0x85, 0xf5, 0xa5, 0xf8,
	0x85, 0xf6, 0x20, 0x90, 0x09, 0x68, 0x68, 0x4c, 0xb7, 0x09, 0x20, 0x0c,
	0xfd, 0xc9, 0x9b, 0xf0, 0xe9, 0xc9, 0xa1, 0xf0, 0x20, 0xc9, 0xe1, 0x90,
	0x03, 0x38, 0xe9, 0x20, 0xc9, 0xc1, 0x90, 0x04, 0xc9, 0xc7, 0x90, 0x0b,
	0xc9, 0xb0, 0x90, 0xe2, 0xc9, 0xba, 0xb0, 0xde, 0x29, 0x0f, 0x60, 0x38,
	0xe9, 0x07, 0x29, 0x0f, 0x60, 0xa9, 0xff, 0x60, 0xc9, 0xff, 0xf0, 0x03,
	0x4c, 0xe3, 0xfd, 0xa9, 0xa1, 0x4c, 0xed, 0xfd, 0xa2, 0x00, 0xb0, 0x25,
	0xad, 0x05, 0x10, 0x30, 0x20, 0xad, 0x04, 0x10, 0x29, 0xf0, 0xc9, 0xf0,
	0xd0, 0x17, 0xad, 0x04, 0x10, 0x29, 0x0f, 0xf0, 0x10, 0x85, 0xf9, 0xbd,
	0x05, 0x10, 0x09, 0x80, 0x20, 0xed, 0xfd, 0xe8, 0xe4, 0xf9, 0xd0, 0xf3,
	0x60, 0x4c, 0x66, 0x09, 0xbc, 0xce, 0xcf, 0xa0, 0xd6, 0xcf, 0xcc, 0xd5,
	0xcd, 0xc5, 0xbe, 0x00, 0xc3, 0xc1, 0xd2, 0xc4, 0xa0, 0xb1, 0xba, 0x00,
	0xc4, 0xc1, 0xce, 0xa0, 0xdd, 0xdb, 0xa0, 0xd6, 0xcf, 0xcc, 0xd5, 0xcd,
	0xc5, 0xa0, 0xd3, 0xc5, 0xcc, 0xc5, 0xc3, 0xd4, 0xcf, 0xd2, 0x8d, 0x00,
	0xa9, 0xb2, 0x8d, 0x41, 0x09, 0xa2, 0x0c, 0x4c, 0x66, 0x09, 0xbd, 0x30,
	0x09, 0xf0, 0x0e, 0x20, 0xed, 0xfd, 0xe8, 0xd0, 0xf5, 0xa9, 0x00, 0x85,
	0x44, 0xa9, 0x10, 0x85, 0x45, 0x60, 0xa9, 0x01, 0x85, 0x42, 0x20, 0x71,
	0x09, 0xa9, 0x02, 0x85, 0x46, 0xa9, 0x00, 0x85, 0x47, 0x4c, 0xf0, 0x00,
	0xa9, 0x07, 0xd0, 0x02, 0xa9, 0x06, 0x85, 0x42, 0x20, 0x71, 0x09, 0xa5,
	0xf5, 0x85, 0x46, 0xa5, 0xf6, 0x85, 0x47, 0x4c, 0xf0, 0x00, 0xa9, 0x05,
	0x85, 0x42, 0x20, 0x71, 0x09, 0x20, 0xf0, 0x00, 0xad, 0x00, 0x10, 0x85,
	0xf7, 0xad, 0x01, 0x10, 0x85, 0xf8, 0x60, 0xa9, 0x00, 0x85, 0xf1, 0x6c,
	0xf1, 0x00,
}

var PROGMEM_v7 = [3 * 512]uint8{
	0xea, 0x8d, 0x0e, 0xc0, 0xa9, 0x20, 0x85, 0xf0, 0xa9, 0x60, 0x85, 0xf3,
	0xa5, 0x43, 0x4a, 0x4a, 0x4a, 0x4a, 0x29, 0x07, 0x09, 0xc0, 0x85, 0xf2,
	0xa0, 0x00, 0x84, 0xf1, 0x88, 0xb1, 0xf1, 0x85, 0xf1, 0xa9, 0x02, 0x85,
	0xf9, 0xe6, 0x46, 0xe6, 0x45, 0xe6, 0x45, 0x20, 0xf0, 0x00, 0x90, 0x06,
	0x20, 0xe2, 0xfb, 0x4c, 0xba, 0xfa, 0xc6, 0xf9, 0xd0, 0xeb, 0xa9, 0x00,
	0x85, 0xfc, 0x20, 0x93, 0xfe, 0x20, 0x89, 0xfe, 0x20, 0xc4, 0x0b, 0x20,
	0xb6, 0x08, 0x20, 0x01, 0x09, 0xa5, 0xf5, 0x85, 0x46, 0xa5, 0xf6, 0x85,
	0x47, 0x20, 0xab, 0x0b, 0x4c, 0xe6, 0x0c, 0x20, 0xd4, 0x08, 0x20, 0x6c,
	0x08, 0xa5, 0xf7, 0x85, 0x46, 0xa5, 0xf8, 0x85, 0x47, 0x4c, 0xaf, 0x0b,
	0xa9, 0x00, 0x85, 0xfb, 0xa9, 0x04, 0x85, 0x25, 0x20, 0x22, 0xfc, 0xa5,
	0xfb, 0x05, 0xfc, 0x85, 0x46, 0x09, 0x80, 0x85, 0x47, 0x20, 0xaf, 0x0b,
	0xa9, 0x00, 0x20, 0x42, 0x0a, 0xa9, 0xff, 0x85, 0x32, 0xa5, 0x43, 0x09,
	0x80, 0x85, 0x43, 0xa5, 0xfb, 0x09, 0x80, 0x85, 0xfb, 0xa9, 0x14, 0x20,
	0x42, 0x0a, 0xa9, 0xff, 0x85, 0x32, 0x4a, 0x25, 0x43, 0x85, 0x43, 0xe6,
	0x25, 0xe6, 0xfb, 0xa5, 0xfb, 0x29, 0x7f, 0x85, 0xfb, 0xc9, 0x10, 0x90,
	0xbf, 0x60, 0xa5, 0xf7, 0x85, 0xf5, 0xa5, 0xf8, 0x85, 0xf6, 0x60, 0xa0,
	0x00, 0x84, 0x24, 0xa0, 0x03, 0x84, 0x25, 0x20, 0x22, 0xfc, 0x20, 0x83,
	0x0b, 0xa0, 0x14, 0x84, 0x24, 0x4c, 0x7e, 0x0b, 0xa5, 0xfa, 0x48, 0xa9,
	0x15, 0x20, 0x62, 0x0b, 0x20, 0x8b, 0x0b, 0xa9, 0x14, 0x85, 0x24, 0x20,
	0x87, 0x0b, 0xa9, 0x01, 0x85, 0xfa, 0x20, 0x14, 0x0a, 0xc6, 0xfa, 0x10,
	0xf9, 0x68, 0x85, 0xfa, 0x60, 0x20, 0xc0, 0x0c, 0x20, 0xb6, 0x08, 0x68,
	0x68, 0x18, 0x4c, 0xe6, 0x0c, 0x20, 0x02, 0x0a, 0x20, 0xbf, 0x08, 0xa5,
	0xf5, 0x29, 0x70, 0x85, 0xfc, 0xa9, 0x00, 0x85, 0xfa, 0x20, 0x5b, 0x08,
	0x20, 0xda, 0x0b, 0xa9, 0x40, 0x20, 0x9e, 0x0a, 0x20, 0x14, 0x0a, 0x20,
	0x0c, 0xfd, 0x48, 0xa9, 0x01, 0x20, 0x9e, 0x0a, 0xa6, 0xfa, 0xb5, 0xf5,
	0x29, 0x8f, 0xa8, 0x68, 0xc9, 0x8d, 0xd0, 0x1f, 0xa9, 0x00, 0x20, 0x9e,
	0x0a, 0xa5, 0xfa, 0x49, 0x01, 0x85, 0xfa, 0xd0, 0x01, 0x60, 0xaa, 0xb5,
	0xf5, 0x29, 0x70, 0xc5, 0xfc, 0xf0, 0xcc, 0x85, 0xfc, 0x20, 0x5b, 0x08,
	0x4c, 0x17, 0x09, 0xc9, 0x9b, 0xf0, 0x9e, 0xc9, 0xa1, 0xd0, 0x05, 0xa9,
	0xff, 0x85, 0xf5, 0x60, 0xc9, 0xac, 0xf0, 0x04, 0xc9, 0x8b, 0xd0, 0x13,
	0x98, 0x0a, 0x08, 0x38, 0xe9, 0x02, 0x29, 0x1e, 0x28, 0x6a, 0x29, 0x8f,
	0x05, 0xfc, 0x95, 0xf5, 0x4c, 0x17, 0x09, 0xc9, 0xa0, 0xf0, 0x04, 0xc9,
	0x8a, 0xd0, 0x04, 0xc8, 0x98, 0xd0, 0xeb, 0xc9, 0x88, 0xd0, 0x1e, 0x98,
	0x49, 0x80, 0xa8, 0x05, 0xfc, 0x95, 0xf5, 0x10, 0xe3, 0xa9, 0xf0, 0x18,
	0x65, 0xfc, 0x29, 0x70, 0x85, 0xfc, 0x98, 0x48, 0x20, 0x5b, 0x08, 0xa6,
	0xfa, 0x68, 0x4c, 0x72, 0x09, 0xc9, 0x95, 0xd0, 0x0e, 0x98, 0x49, 0x80,
	0xa8, 0x05, 0xfc, 0x95, 0xf5, 0x30, 0xc1, 0xa9, 0x10, 0xd0, 0xdc, 0xc9,
	0xe1, 0x90, 0x03, 0x38, 0xe9, 0x20, 0xc9, 0xc1, 0x90, 0x09, 0xc9, 0xc7,
	0xb0, 0x05, 0x38, 0xe9, 0x07, 0xd0, 0x08, 0xc9, 0xb0, 0x90, 0x11, 0xc9,
	0xba, 0xb0, 0x0d, 0x29, 0x0f, 0x85, 0xf9, 0x98, 0x29, 0x80, 0x05, 0xf9,
	0xa8, 0x4c, 0x72, 0x09, 0xc9, 0xc9, 0xf0, 0x02, 0xd0, 0x8e, 0x20, 0x02,
	0x0a, 0x20, 0xda, 0x0b, 0x20, 0x0c, 0x0c, 0x4c, 0x01, 0x09, 0xc9, 0xff,
	0xf0, 0x03, 0x4c, 0xda, 0xfd, 0xa9, 0xa1, 0x4c, 0xed, 0xfd, 0x20, 0x58,
	0xfc, 0xa9, 0x3f, 0x20, 0x6b, 0x0b, 0xa9, 0x17, 0x20, 0x62, 0x0b, 0xa9,
	0x23, 0x4c, 0x6b, 0x0b, 0xa2, 0x0b, 0xa5, 0xfa, 0xf0, 0x02, 0xa2, 0x1f,
	0x86, 0x24, 0xa0, 0x15, 0x84, 0x25, 0x20, 0x22, 0xfc, 0xa4, 0xfa, 0xb9,
	0xf5, 0x00, 0x48, 0x30, 0x04, 0xa0, 0x01, 0xd0, 0x02, 0xa0, 0x02, 0x98,
	0x20, 0xe3, 0xfd, 0xe6, 0x24, 0x68, 0x29, 0x7f, 0x20, 0xf6, 0x09, 0xc6,
	0x24, 0x60, 0x85, 0x24, 0xa5, 0xfb, 0x05, 0xfc, 0x48, 0x29, 0x7f, 0x20,
	0xda, 0xfd, 0xa9, 0xba, 0x20, 0xed, 0xfd, 0xe6, 0x24, 0x68, 0xc5, 0xf5,
	0xf0, 0x04, 0xc5, 0xf6, 0xd0, 0x04, 0xa9, 0x3f, 0x85, 0x32, 0x20, 0x9e,
	0x0b, 0xb0, 0x32, 0xad, 0x05, 0x10, 0x30, 0x2d, 0xad, 0x04, 0x10, 0xaa,
	0x29, 0xf0, 0xc9, 0xf0, 0xd0, 0x23, 0x8a, 0x29, 0x0f, 0xf0, 0x1e, 0x85,
	0xf9, 0xa2, 0x00, 0xbd, 0x05, 0x10, 0x09, 0x80, 0x20, 0xed, 0xfd, 0xe8,
	0xe4, 0xf9, 0xd0, 0xf3, 0xe0, 0x0f, 0xd0, 0x01, 0x60, 0xa9, 0xa0, 0x20,
	0xed, 0xfd, 0xe8, 0xd0, 0xf3, 0xa2, 0x00, 0x4c, 0x92, 0x0b, 0xa6, 0xfa,
	0xc9, 0x01, 0xd0, 0x0a, 0xa9, 0x80, 0xa4, 0xf5, 0xc4, 0xf6, 0xd0, 0x02,
	0xa9, 0x00, 0x85, 0xf9, 0xb5, 0xf5, 0x30, 0x04, 0xa0, 0x04, 0xd0, 0x02,
	0xa0, 0x18, 0x84, 0x24, 0x29, 0x7f, 0x45, 0xfc, 0xc9, 0x10, 0xb0, 0x17,
	0x69, 0x04, 0x85, 0x25, 0x20, 0x22, 0xfc, 0xa4, 0x24, 0xa2, 0x0f, 0xb1,
	0x28, 0x29, 0x3f, 0x05, 0xf9, 0x91, 0x28, 0xc8, 0xca, 0xd0, 0xf4, 0x60,
	0xad, 0xad, 0xad, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0x00, 0xc4, 0xd2, 0xc9, 0xd6, 0xc5, 0xa0, 0xb1, 0xba,
	0xa0, 0xd3, 0xc4, 0xbf, 0xaf, 0x00, 0xd3, 0xc4, 0xb1, 0xba, 0x00, 0x20,
	0x20, 0x01, 0x10, 0x10, 0x0c, 0x05, 0x20, 0x09, 0x09, 0x20, 0x06, 0x0f,
	0x12, 0x05, 0x16, 0x05, 0x12, 0x21, 0x20, 0x20, 0x16, 0x33, 0x2e, 0x34,
	0x2e, 0x32, 0x00, 0x04, 0x01, 0x0e, 0x20, 0x09, 0x09, 0x20, 0x16, 0x0f,
	0x0c, 0x15, 0x0d, 0x05, 0x20, 0x13, 0x05, 0x0c, 0x05, 0x03, 0x14, 0x0f,
	0x12, 0x00, 0xa0, 0xcf, 0xcb, 0xa1, 0x00, 0xc5, 0xd2, 0xd2, 0xcf, 0xd2,
	0xa1, 0x87, 0x87, 0x87, 0x00, 0xc6, 0xd4, 0xd0, 0xaf, 0xc9, 0xd0, 0xba,
	0xa0, 0x00, 0xce, 0xc5, 0xd7, 0xa0, 0xc9, 0xd0, 0xba, 0xa0, 0xa0, 0xa0,
	0xa0, 0xae, 0xa0, 0xa0, 0xa0, 0xae, 0xa0, 0xa0, 0xa0, 0xae, 0xa0, 0xa0,
	0xa0, 0x00, 0x85, 0x25, 0xa9, 0x00, 0x85, 0x24, 0x4c, 0x22, 0xfc, 0x48,
	0xa2, 0x26, 0xa9, 0x20, 0x20, 0xed, 0xfd, 0xca, 0x10, 0xf8, 0x68, 0xaa,
	0xa9, 0x08, 0x85, 0x24, 0xd0, 0x14, 0xa9, 0xb2, 0x8d, 0xfc, 0x0a, 0xa2,
	0x1e, 0xd0, 0x0b, 0xa9, 0xb2, 0xd0, 0x02, 0xa9, 0xb1, 0x8d, 0xf2, 0x0a,
	0xa2, 0x10, 0xbd, 0xdc, 0x0a, 0xf0, 0x06, 0x20, 0xed, 0xfd, 0xe8, 0xd0,
	0xf5, 0x60, 0xa0, 0x00, 0x84, 0x47, 0xc8, 0x84, 0x42, 0xc8, 0x84, 0x46,
	0x4c, 0xb9, 0x0b, 0xa9, 0x07, 0xd0, 0x02, 0xa9, 0x06, 0x85, 0x42, 0xa5,
	0x47, 0x49, 0x80, 0x85, 0x47, 0xa9, 0x00, 0x85, 0x44, 0xa9, 0x10, 0x85,
	0x45, 0x4c, 0xf0, 0x00, 0xa9, 0x05, 0x85, 0x42, 0x20, 0xb9, 0x0b, 0xad,
	0x00, 0x10, 0x85, 0xf7, 0xad, 0x01, 0x10, 0x49, 0x80, 0x85, 0xf8, 0x4c,
	0xb6, 0x08, 0xa9, 0x21, 0x20, 0xb1, 0x0b, 0xb0, 0xbc, 0xad, 0x10, 0x10,
	0xf0, 0xb7, 0xa9, 0x01, 0x20, 0x62, 0x0b, 0xa2, 0x09, 0x86, 0x24, 0xa2,
	0x65, 0x20, 0x92, 0x0b, 0xad, 0x06, 0x10, 0x20, 0xb4, 0x0c, 0xad, 0x07,
	0x10, 0x20, 0xb4, 0x0c, 0xad, 0x08, 0x10, 0x20, 0xb4, 0x0c, 0xad, 0x09,
	0x10, 0x4c, 0x13, 0x0d, 0xa5, 0x43, 0x48, 0xa9, 0x0a, 0x20, 0x62, 0x0b,
	0xa2, 0x6e, 0x20, 0x92, 0x0b, 0xa9, 0x08, 0x85, 0x24, 0x20, 0x54, 0x0c,
	0xb0, 0xed, 0x85, 0x44, 0xe6, 0x24, 0x20, 0x54, 0x0c, 0xb0, 0xe4, 0x85,
	0x45, 0xe6, 0x24, 0x20, 0x54, 0x0c, 0xb0, 0xdb, 0x85, 0x46, 0xe6, 0x24,
	0x20, 0x54, 0x0c, 0xb0, 0xd2, 0x85, 0x47, 0x85, 0x43, 0xa9, 0x20, 0x85,
	0x42, 0x20, 0xf0, 0x00, 0x18, 0xaa, 0x68, 0x85, 0x43, 0xca, 0xf0, 0x01,
	0x38, 0x4c, 0xd0, 0x0c, 0xa9, 0x00, 0x85, 0xf9, 0xa2, 0x03, 0x8a, 0x48,
	0x20, 0x93, 0x0c, 0xb0, 0x17, 0xc9, 0xff, 0xf0, 0x17, 0xa8, 0x20, 0x84,
	0x0c, 0xb0, 0x0d, 0x98, 0x65, 0xf9, 0x85, 0xf9, 0xb0, 0x06, 0x68, 0xaa,
	0xca, 0xd0, 0xe3, 0x48, 0x68, 0xa5, 0xf9, 0x60, 0x68, 0x18, 0x65, 0x24,
	0x85, 0x24, 0x90, 0xf5, 0xa2, 0x0a, 0xa9, 0x00, 0x18, 0x65, 0xf9, 0xb0,
	0x05, 0xca, 0xd0, 0xf9, 0x85, 0xf9, 0x60, 0x20, 0x0c, 0xfd, 0xc9, 0xa0,
	0xd0, 0x04, 0xa9, 0xff, 0x18, 0x60, 0xc9, 0x8d, 0xf0, 0xf8, 0xc9, 0xae,
	0xf0, 0xf4, 0xc9, 0xb0, 0x30, 0xe9, 0xc9, 0xba, 0xb0, 0xe5, 0x20, 0xed,
	0xfd, 0x29, 0x0f, 0x60, 0x20, 0x13, 0x0d, 0xa9, 0xae, 0x4c, 0xed, 0xfd,
	0xa9, 0x00, 0xf0, 0x02, 0xa9, 0x80, 0xa2, 0x01, 0x86, 0xfa, 0x48, 0x20,
	0x9e, 0x0a, 0x68, 0xc6, 0xfa, 0x10, 0xf7, 0x60, 0xa2, 0x56, 0x90, 0x02,
	0xa2, 0x5b, 0x20, 0x92, 0x0b, 0xa2, 0x06, 0xa9, 0xff, 0x20, 0xa8, 0xfc,
	0xca, 0xd0, 0xf8, 0x4c, 0x58, 0xfc, 0x08, 0x20, 0xd4, 0x08, 0x20, 0xbc,
	0x0c, 0xa9, 0x16, 0x20, 0x62, 0x0b, 0xa9, 0x11, 0x85, 0x24, 0x28, 0x20,
	0xd0, 0x0c, 0xa9, 0x08, 0x48, 0x85, 0x45, 0xa9, 0x00, 0x48, 0x85, 0x44,
	0x85, 0x46, 0x85, 0x47, 0xa2, 0x01, 0x81, 0x44, 0xa9, 0x0a, 0x85, 0x42,
	0x6c, 0xf1, 0x00, 0xc9, 0x0a, 0x90, 0x1e, 0xa2, 0x64, 0x86, 0xf9, 0x20,
	0x38, 0x0d, 0xe0, 0x00, 0xf0, 0x06, 0x48, 0x8a, 0x20, 0xe3, 0xfd, 0x68,
	0xa2, 0x0a, 0x86, 0xf9, 0x20, 0x38, 0x0d, 0x48, 0x8a, 0x20, 0xe3, 0xfd,
	0x68, 0x4c, 0xe3, 0xfd, 0xa2, 0x00, 0xc5, 0xf9, 0x90, 0x06, 0xe8, 0x38,
	0xe5, 0xf9, 0xb0, 0xf6, 0x60,
}
