diff --git a/cardBuilder.go b/cardBuilder.go index 95fff87..3b5c657 100644 --- a/cardBuilder.go +++ b/cardBuilder.go @@ -25,7 +25,7 @@ type cardBuilder struct { const noCardName = "empty" var commonParams = []paramSpec{ - {"debug", "Enable debug messages", "false"}, + {"trace", "Enable debug messages", "false"}, {"tracess", "Trace softswitches", "false"}, } @@ -118,7 +118,7 @@ func setupCard(a *Apple2, slot int, paramString string) (Card, error) { a.io.traceSlot(slot) } - debug := paramsGetBool(finalParams, "debug") + debug := paramsGetBool(finalParams, "trace") card.setName(builder.name) card.setDebug(debug) diff --git a/cardDan2Controller.go b/cardDan2Controller.go index 03658f7..45a3f04 100644 --- a/cardDan2Controller.go +++ b/cardDan2Controller.go @@ -11,6 +11,7 @@ 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 */ @@ -31,12 +32,17 @@ type CardDan2Controller struct { slotA *cardDan2ControllerSlot slotB *cardDan2ControllerSlot + + improved bool + a2slot uint8 } type cardDan2ControllerSlot struct { - path string - fileNo uint8 - fileName string + card *CardDan2Controller + path string + fileNo uint8 + fileName string + fileNameAlt string } func newCardDan2ControllerBuilder() *cardBuilder { @@ -44,6 +50,7 @@ func newCardDan2ControllerBuilder() *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", ""}, @@ -53,19 +60,27 @@ func newCardDan2ControllerBuilder() *cardBuilder { 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, _ := paramsGetInt(params, "slot1file") c.slotA.fileNo = uint8(num) c.slotA.initializeDrive() c.slotB = &cardDan2ControllerSlot{} + c.slotB.card = &c c.slotB.path = params["slot2"] num, _ = paramsGetInt(params, "slot2file") c.slotB.fileNo = uint8(num) c.slotB.initializeDrive() - err := c.loadRomFromResource("/Apple2CardFirmware.bin") + romFilename := "/Apple2CardFirmware.bin" + if c.improved { + romFilename = "/Apple2CardFirmwareImproved.bin" + } + err := c.loadRomFromResource(romFilename) if err != nil { return nil, err } @@ -79,17 +94,17 @@ 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.write(address, data) + c.writeSoftSwitch(address, data) return 0 } else { - return c.read(address) + return c.readSoftSwitch(address) } }, "DAN2CONTROLLER") c.cardBase.assign(a, slot) } -func (c *CardDan2Controller) write(address uint8, data uint8) { +func (c *CardDan2Controller) writeSoftSwitch(address uint8, data uint8) { switch address { case 0: // Port A if c.receivingWiteBuffer { @@ -100,6 +115,10 @@ func (c *CardDan2Controller) write(address uint8, data uint8) { } 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) @@ -134,7 +153,7 @@ func (c *CardDan2Controller) write(address uint8, data uint8) { } } -func (c *CardDan2Controller) read(address uint8) uint8 { +func (c *CardDan2Controller) readSoftSwitch(address uint8) uint8 { switch address { case 0: // Port A if len(c.responseBuffer) > 0 { @@ -164,8 +183,20 @@ func (s *cardDan2ControllerSlot) blockPosition(unit uint8, block uint16) int64 { } } -func (s *cardDan2ControllerSlot) status(unit uint8) error { +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 } @@ -174,7 +205,7 @@ func (s *cardDan2ControllerSlot) status(unit uint8) error { } func (s *cardDan2ControllerSlot) readBlock(unit uint8, block uint16) ([]uint8, error) { - file, err := os.OpenFile(s.fileName, os.O_RDWR, 0) + file, err := s.openFile() if err != nil { return nil, err } @@ -189,8 +220,21 @@ func (s *cardDan2ControllerSlot) readBlock(unit uint8, block uint16) ([]uint8, e 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 := os.OpenFile(s.fileName, os.O_RDWR, 0) + file, err := s.openFile() if err != nil { return err } @@ -204,6 +248,18 @@ func (s *cardDan2ControllerSlot) writeBlock(unit uint8, block uint16, data []uin 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 @@ -211,8 +267,10 @@ func (s *cardDan2ControllerSlot) initializeDrive() { 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)) } } @@ -227,6 +285,15 @@ func (c *CardDan2Controller) selectSlot(unit uint8) *cardDan2ControllerSlot { 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 { @@ -287,44 +354,76 @@ func (c *CardDan2Controller) processCommand() { c.commandBuffer = nil } - case 5: // Get volume + 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("5-Get Volume\n") + c.tracef("%v-Get Volume\n", command) c.sendResponse(c.slotA.fileNo, c.slotB.fileNo) c.commandBuffer = nil } - case 4, 6, 7: // Set volume + 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 { - _, _, block := c.getUnitBufBlk() + unit, _, block := c.getUnitBufBlk() + c.a2slot = (unit >> 4) & 0x3 c.slotA.fileNo = uint8(block & 0xff) c.slotA.initializeDrive() - c.slotA.fileNo = uint8((block >> 8) & 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.slotA.fileNo) + command, c.slotA.fileNo, c.slotB.fileNo) - if command == 4 { - c.responseBuffer = append(c.responseBuffer, 0x00) // Success code + if command == 4 || command == 8 { + c.sendResponseCode(0x00) // Success code } else { c.sendResponse() - // command 6 writes eeprom, not emulated + // 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(PROGMEM[:]...) + c.sendResponse(c.readBootBlock(block)...) c.commandBuffer = nil } default: // Unknown command c.tracef("Unknown command %v\n", command) c.sendResponseCode(0x27) + c.commandBuffer = nil } } @@ -387,3 +486,119 @@ var PROGMEM = [512]uint8{ 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, +} diff --git a/resources/Apple2CardFirmwareImproved.bin b/resources/Apple2CardFirmwareImproved.bin new file mode 100644 index 0000000..0eda6bc Binary files /dev/null and b/resources/Apple2CardFirmwareImproved.bin differ