diff --git a/NEWS b/NEWS index 98c201ed..86ad8c47 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,7 @@ - New fastloader: Ultraboot - New fastloader: Hypra-Load - create D64 images in the FAT FS with N: if no image is mounted + - Krill's IRQ loader 2012-02-26 - release 0.10.3 - Bugfix: Un-break I2C display communication diff --git a/README b/README index e983bbfd..c89fa999 100644 --- a/README +++ b/README @@ -741,6 +741,42 @@ Note: Using sd2iec without an external crystal or similiar precise Hypra-Load uses the ATN line for handshake, so it only works if no other device is connected to the IEC bus. + Krill's IRQ Loader + ------------------ + The sd2iec implementation supports loading both from D41/D71/D81 + images and files extracted to the SD card. Extracting the files + in a useable form can be a challenge for some releases, though: + Some use directory entries on a non-standard track, payload might + be read from files marked as deleted in the directory and the + "load next file" functionality of the loader relies on the order + of the files in the directory. + + A small number of releases use track/sector addressing to load + files; these only work from a D64 image. + + See doc/krills-loader.txt for additional remarks regarding specific + releases. + + The loader is captive, but the sd2iec implementation should most + of the time be able to detect a host reset or uninstall and exit + accordingly. + + As the ATN line is used as a clock line for transfers, no other + drives might be active on the bus. Starting with revision r164, + the loader tries to silence other devices by installing an "ATN + responder". In the sd2iec implementation this activates "Sleep + Mode" (see below), which has to be ended manually by holding the + disk change button. + + On a C128 host the loader uses the burst protocol if connected to + a 1571 or 1581. This is not yet supported by this implementation. + (The only known C128 release using this loader is "Colour Spectrum", + CSDb Release ID 205653). + + Only productions released on CSDb which credit Krill for their + loader were tested. Other productions might use configuration + options which are not supported and therefore might not work. + JiffyDOS: ========= The JiffyDOS protocol has very relaxed timing constraints compared to diff --git a/configs/config-arm2iec1 b/configs/config-arm2iec1 index df52eca1..65e24b75 100644 --- a/configs/config-arm2iec1 +++ b/configs/config-arm2iec1 @@ -72,3 +72,4 @@ CONFIG_LOADER_N0SDOS=y CONFIG_LOADER_SAMSJOURNEY=y CONFIG_LOADER_ULTRABOOT=n CONFIG_LOADER_HYPRALOAD=n +CONFIG_LOADER_KRILL=n diff --git a/configs/config-example b/configs/config-example index 2dfd6722..9acb7d32 100644 --- a/configs/config-example +++ b/configs/config-example @@ -123,6 +123,9 @@ CONFIG_LOADER_ULTRABOOT=n # Enable Hypra-Load fastloader CONFIG_LOADER_HYPRALOAD=n +# Enable Krill's loader +CONFIG_LOADER_KRILL=n + # Enable DolphinDOS parallel speeder CONFIG_PARALLEL_DOLPHIN=y diff --git a/configs/config-larsp b/configs/config-larsp index a59a16af..7f78cffa 100644 --- a/configs/config-larsp +++ b/configs/config-larsp @@ -33,7 +33,7 @@ CONFIG_MCU_FREQ=8000000 CONFIG_BOOTLOADER=y CONFIG_BOOT_DEVID=0x5053524c CONFIG_COMMAND_CHANNEL_DUMP=y -CONFIG_BUS_SILENCE_REQ=n +CONFIG_BUS_SILENCE_REQ=y # In case someone added a crystal to his board CONFIG_LOADER_TURBODISK=y CONFIG_LOADER_FC3=y @@ -68,3 +68,4 @@ CONFIG_LOADER_N0SDOS=y CONFIG_LOADER_SAMSJOURNEY=y CONFIG_LOADER_ULTRABOOT=y CONFIG_LOADER_HYPRALOAD=y +CONFIG_LOADER_KRILL=y diff --git a/configs/config-mbed b/configs/config-mbed index a8ace531..33f036a0 100644 --- a/configs/config-mbed +++ b/configs/config-mbed @@ -48,6 +48,7 @@ CONFIG_LOADER_AR6=y CONFIG_LOADER_ELOAD1=y CONFIG_LOADER_ULTRABOOT=n CONFIG_LOADER_HYPRALOAD=n +CONFIG_LOADER_KRILL=n CONFIG_HARDWARE_VARIANT=100 CONFIG_HARDWARE_NAME=sd2iec-mbed CONFIG_SD_AUTO_RETRIES=10 diff --git a/configs/config-sw1 b/configs/config-sw1 index 57cd3908..e16988b4 100644 --- a/configs/config-sw1 +++ b/configs/config-sw1 @@ -35,7 +35,7 @@ CONFIG_BOOTLOADER=y CONFIG_BOOT_DEVID=0x49454321 CONFIG_UART_DEBUG=n CONFIG_COMMAND_CHANNEL_DUMP=n -CONFIG_BUS_SILENCE_REQ=n +CONFIG_BUS_SILENCE_REQ=y # Turbodisk requires a crystal which is usually only present on 1.8 or later CONFIG_LOADER_TURBODISK=y CONFIG_LOADER_FC3=y @@ -66,3 +66,4 @@ CONFIG_LOADER_N0SDOS=y CONFIG_LOADER_SAMSJOURNEY=y CONFIG_LOADER_ULTRABOOT=y CONFIG_LOADER_HYPRALOAD=y +CONFIG_LOADER_KRILL=y diff --git a/configs/config-sw2 b/configs/config-sw2 index 905cd512..96f488bc 100644 --- a/configs/config-sw2 +++ b/configs/config-sw2 @@ -34,7 +34,7 @@ CONFIG_MCU_FREQ=8000000 CONFIG_BOOTLOADER=y CONFIG_BOOT_DEVID=0x31434549 CONFIG_COMMAND_CHANNEL_DUMP=y -CONFIG_BUS_SILENCE_REQ=n +CONFIG_BUS_SILENCE_REQ=y CONFIG_LOADER_TURBODISK=y CONFIG_LOADER_FC3=y CONFIG_LOADER_DREAMLOAD=y @@ -68,3 +68,4 @@ CONFIG_LOADER_N0SDOS=y CONFIG_LOADER_SAMSJOURNEY=y CONFIG_LOADER_ULTRABOOT=y CONFIG_LOADER_HYPRALOAD=y +CONFIG_LOADER_KRILL=y diff --git a/configs/config-uIEC b/configs/config-uIEC index eabe2581..0209ad57 100644 --- a/configs/config-uIEC +++ b/configs/config-uIEC @@ -38,7 +38,7 @@ CONFIG_UART_BAUDRATE=38400 CONFIG_UART_BUF_SHIFT=6 CONFIG_DEADLOCK_ME_HARDER=n CONFIG_COMMAND_CHANNEL_DUMP=y -CONFIG_BUS_SILENCE_REQ=n +CONFIG_BUS_SILENCE_REQ=y CONFIG_LOADER_TURBODISK=y CONFIG_LOADER_FC3=y CONFIG_LOADER_DREAMLOAD=y @@ -70,3 +70,4 @@ CONFIG_LOADER_N0SDOS=y CONFIG_LOADER_SAMSJOURNEY=y CONFIG_LOADER_ULTRABOOT=y CONFIG_LOADER_HYPRALOAD=y +CONFIG_LOADER_KRILL=y diff --git a/configs/config-uIEC3 b/configs/config-uIEC3 index a7dd9ea1..d9fdd0e5 100644 --- a/configs/config-uIEC3 +++ b/configs/config-uIEC3 @@ -38,7 +38,7 @@ CONFIG_UART_BAUDRATE=38400 CONFIG_UART_BUF_SHIFT=6 CONFIG_DEADLOCK_ME_HARDER=n CONFIG_COMMAND_CHANNEL_DUMP=y -CONFIG_BUS_SILENCE_REQ=n +CONFIG_BUS_SILENCE_REQ=y CONFIG_LOADER_TURBODISK=y CONFIG_LOADER_FC3=y CONFIG_LOADER_DREAMLOAD=y @@ -68,3 +68,4 @@ CONFIG_LOADER_N0SDOS=y CONFIG_LOADER_SAMSJOURNEY=y CONFIG_LOADER_ULTRABOOT=y CONFIG_LOADER_HYPRALOAD=y +CONFIG_LOADER_KRILL=y diff --git a/doc/krills-loader.txt b/doc/krills-loader.txt new file mode 100644 index 00000000..7c433232 --- /dev/null +++ b/doc/krills-loader.txt @@ -0,0 +1,760 @@ +Krill's Loader Protocols +============================= +& sd2iec Implementation Notes + +Documented by Martin Thierer + +Note this is the IRQ-loader, *not* Transwarp (also by Krill, but an +entirely different loader). It is mostly used by demos and some games. +You can find a list of known productions using it at the end of this +document. + +Loader Revisions +================ + +Various revisions have been publicly released, but some productions +use in-between revisions, which protocol-wise in most cases match a +released version. + + 58pre protocol version that predates r58, no public release + 58 https://csdb.dk/release/?id=78348 +146 https://csdb.dk/release/?id=118713 protocol matches r58 +159 no public release +164 https://csdb.dk/release/?id=167152 +166 https://csdb.dk/release/?id=169364 protocol matches r164 +184 https://csdb.dk/release/?id=189130 +186 no public release +192 https://csdb.dk/release/?id=220685 + +Byte-Transfer Protocols +======================= + +The actual bus lines used vary between revisions, but otherwise the +protocols are similar. + +Most protocols use a clock line, which is always driven by the host. +The only exception is the "resend" protocol (only available as an +option in r146), which uses cycle-exact timing instead. + +1-Bit Receive Protocol +---------------------- + +Used for drivecode download, the filename of file requests and the +save plugin of revisions >= 186. + +r164/r166 uses this protocol with different lines when receiving the +drivecode and for request filenames. + + | Clock | Data +--------------------|--------|-------- +<= 146 | CLK | DATA +159 | DATA | CLK +164/166 (drivecode) | DATA | CLK +164/166 (filenames) | CLK | ATN +>= 184 | DATA | CLK + +The byte transfer starts on a negative edge of the respective clock +line, with the exception of r164/r166, which starts on the positive +edge. + +Bytes are sent LSB-first with the bit values inverted (data line +low is bit == 1 and vice versa). + +The host acknowledges each bit with a clock edge. After 8 bits the +clock line is in the same state as in the beginning of the byte +transfer. + +1-Bit Send Protocol +------------------- + +Only used by the save plugin (added in r186, first used in Scramble +Infinity 1.2 [010]), to send a drive memory backup and metadata to +the host. + +CLK is the clock line (driven by the host) and DATA is the data line. + +Bytes are sent MSB-first with the bit values inverted (data line +low is bit == 1 and vice versa). + +The byte transfer starts with CLK high and the DATA line set to !b7. + +2-Bit Send "ATN" Protocol +------------------------- + +Used during file transfers for sending both the payload and the +metadata bytes. This protocol is identical for all revisions but +58pre, which uses a different bit-order and -polarity. + +ATN is used as the clock line and CLK & DATA are the data lines. + + | c0/d0 | c1/d1 | c2/d2 | c3/d3 +------|---------|---------|---------|--------- +58pre | !b7/!b5 | !b6/!b4 | !b3/!b1 | !b2/!b0 +>= 58 | b0/b1 | b2/b3 | b4/b5 | b6/b7 + +2-Bit Send "Resend" Protocol +---------------------------- + +Alternative 2-bit send protocol, only available in r146. + +This is the only protocol which doesn't utilize a clock line to fully +synchronize the byte transfer, but instead sends the bits with a +fixed timing and just keeps trying if the host was interrupted +during the transfer. + +The host triggers the start of the byte by releasing ATN and keeps +it released during the transfer. If ATN is not set in time after the +last bit was sent, it's assumed the host was interrupted and the same +byte is sent again. This is signaled to the host by setting CLK. + + [us] | ATN | CLK | DATA | +---------------------------- + 0 | 0->1 | 1 | | timing reference + 14 | 1 | b0 | b1 | + 22 | 1 | b2 | b3 | + 30 | 1 | b4 | b4 | + 38 | 1 | b6 | b7 | +42/46 | 1 | 1 | | 42us if TWO_BITS_RESEND_OPTIMIZE is set + + 50 | (1) | 1->0 | | if ATN was not set by host -> send again + +Build-Time Options +================== + +Some of the loader's many configuration options are relevant for the +transfer protocol. + +Revision r192 sends the values of these options together with the M-E +command which starts the request loop (see "ID-Strings", below). + +For legacy productions the values are extracted from the drivecode +download, if the specific variant is known. For unknown variants +default values are used, which might not be correct and therefore not +work. + +Maximum Filename Length +----------------------- + +For protocol version r58pre the maximum length is always 2, for later +revisions it is a configuration option. + +If the configured value is less than the CBM DOS standard of 16 bytes, +the filename used for the request only has to match as a prefix for a +directory entry. + +The option value is included in the ID-String present starting with +r192 (see below). For earlier revisions, the value is extracted from +the drivecode download if the variant is known, otherwise it defaults +to 16. + +Non-standard Directory +---------------------- + +The loader can be configured to use a non-standard location for the +directory ("shadow directory"). Early releases (until r146) allowed +both the track and sector to be configured. In this case the sector +denotes the equivalent of the BAM sector, so the first directory +sector is the one referenced from this sector. In later releases +it's only possible to change the directory track, in this case the +directory always starts at sector 1 of that track. + +The directory track is only relevant if a D64 image is mounted. It +is included in the ID-String present starting in r192 and later. For +earlier revisions, the value is extracted from the drivecode download +if the variant is known, otherwise it defaults to the mounted image +type's standard. + +Productions using a non-standard directory start sector are marked +with a "D" in the rightmost column of the "Known Productions" table +below. + +2-Bit Protocol Variant +---------------------- + +The few known productions using the "resend" protocol are detected +by their respective, distinct installer stub CRC values. They are +marked with a "R" in the rightmost column of the "Known Productions" +table below. + +Other, unknown productions using this protocol might not work. + +File Addressing Mode +-------------------- + +A few productions use the start sector instead of the filename for +addressing files. + +The sd2iec implementation tries to detect these by checking if the +first two bytes are a valid track and sector, if the first requested +file wasn't found by name. This isn't 100% reliable, but works for +all known productions using this loader. Not supported for D71 and +D81 images. + +Productions using start sector addressing are marked with a "T" in +the rightmost column in the "Known Productions" table below. + +Loader revisions r164 and later only support filename addressing. + +Installation +============ + +The initial installation of the loader code in the drive has the +following phases: + +1) Various checks are performed to identify the drive and make sure + it is able to execute custom code. In the early revisions (up to + r146) this code is transferred using M-W commands, later revisions + send it as extra payload in the M-E command. + + The sd2iec implementation simulates the drive type according to + the mounted image's type: + - none or D64/D41 -> 1541 + - D71 -> 1571 + - D81 -> 1581 + +2) A loader stub is transferred to the drive using a couple of M-W + commands. + +3) The loader stub is started with a M-E command and takes over the + transfer of the rest of the drive code. On a 1541 the drive code + uses most of the available drive memory, including the zero page. + +4) After the drivecode has been installed, the drive releases its + "busy" line, then the host releases a different line to start a + request. The actual bus lines used for both purposes differ + between revisions. + +ID-Strings +---------- + +Starting with r192, an ID-String is added to some of the M-E commands +sent during installation, to simplify identification of the loader +without relying on crcs (which otherwise might require a sd2iec +firmware update for new loader revisions): + +- Drive Identification + + 4d 2d 45 0a 02 4b 52 49 4c 4c + M - E $020a K R I L L + +- Request Loop Start + + This M-E command also includes the values of some configuration + options. + + Example: + + 4d 2d 45 09 02 4b 52 49 4c 4c xx xx c0 00 40 29 12 10 00 + M - E $0209 K R I L L AD AD RV RV PF DR DT FL CF + + AD Drivecode start-address low/high (variable) + + RV Loader revision low/high (here: $00c0 == 192) + PF Platform (here: $40 == 64 == C64) + DR Drive model (here: $29 == 41 == 1541) + DT Directory track (here: $12 == 18) + FL Maximum filename length (here: $10 == 16) + CF Internal configuration flags + + Currently only the directory track and the maximum filename length + are actually used by the sd2iec implementation. + +- ATN Responder + + If the loader detects a secondary device on the bus, it tries to + silence it by installing an ATN responder. The M-E command which + then starts the ATN responder also has an ID-String. + + 4d 2d 45 0b 02 4b 52 49 4c 4c + M - E $020b K R I L L + +Drivecode Download +------------------ + +1) Drive sets CLK when it's ready to receive +2) Host sets either ATN (r184 and later) or DATA (earlier revisions) +3) Drive releases CLK +4) Drive receives drivecode using the 1-bit receive protocol (see + "Transfer Protocols", above). +5) Only <= r146: Host waits for drive to set CLK. + +The length of the drivecode is implicitly embedded in both the first +byte of the drivecode (only high byte) and the loop instructions of +the loader stub sent in M-W commands before the drivecode (only low +byte) and is therefore cumbersome and unreliable to extract. + +So the sd2iec implementation uses a 90ms timeout waiting for a change +on the clock line instead. + +This works for revisions r146 and earlier, as the host waits for +the drive to set CLK before continuing (step 5 above). + +Later revisions don't have this handshake, so the host might start +its first request immediately after the transfer is complete. To +prevent that, the sd2iec firmware sets the "busy" line as soon as +the last build-time option value has been extracted from the +drivecode download. + +Request Handling +================ + +After the drivecode has been transferred, the drive enters the request +loop. + +There are four types of requests: + - File request + - "File exists" check + - Uninstall command (drive resets) + - Custom drivecode upload + +In the sd2iec firmware, custom drivecode is generally not supported, +only the save plugin introduced in r186 is emulated (see below). + +To signal a request, the host releases the respective "request" line. +The drive responds by releasing the "busy" line. + + | request | busy +---------------|---------|--------- +<= 146 | ATN | CLK +159 | DATA | CLK +164/166 | ATN | DATA +>= 184 | DATA | CLK + +If the host sets the "busy" line when releasing the "request" line, +this signals either a uninstall request (the drive does a reset) or +a custom drivecode upload (see below). + +Otherwise it's a file request. + +File Transfer +------------- + +For a file request, the host usually sends a filename, with the +following exceptions/peculiarities: + +- Filename bytes are transferred until either a zero byte is sent or + the maximum filename length (see "Loader Configuration Option", + above) is reached. The r192 revision always sends a trailing zero + byte. + +- An empty filename (only a zero byte is sent) denotes a "next file" + request. + +- File types are ignored, even directory entries with file type zero + are considered, as long as the entry has a non-zero track number + for the first file sector. + +- If Track/Sector addressing is used (only <= r146), the protocol + is the same as for the filename, except the transfer always stops + after the second byte. + +After the filename was transferred, the drive sets the busy line +again, until the first block is available. + +If there was an error when opening the file ("file not found"), only +one byte of metadata ($ff) is sent as an error code (see below). + +The transfer always starts with the first sector of the file, after +that the sectors might be transferred out of order, as they are read +from the disk. + +All revisions generally transfer 254 bytes for full blocks and +possibly fewer for the file's last sector. + +The sector link bytes are not transferred and instead replaced with +up to two bytes of metadata. The significance and encoding of these +two bytes differs between protocol revisions, but generally this +information is included, sometimes implicitly: + +- Index of current block (or delta to previous block as 2s-complement) +- Flag for the file's last block and in this case also the number of + payload bytes (which might be fewer than the standard of 254) +- Number of contiguous blocks transferred (total number of transferred + blocks might be higher for out-of-order transfers) +- EOF indicator + +If the first byte of metadata indicates either "file not found" ($ff) +or "end of file" ($fe or $00, depending on the protocol version) no +more bytes are sent for the request (not even a second metadata byte). + +<= r146: + Byte 0: $ff: "file not found" + $fe: "end of file" + otherwise: 0-based blockindex + Byte 1: number of bytes in block + +r159, r164/r166: + Byte 0: $ff: "file not found" + $00: "end of file" + otherwise: + Bits 7-1: 0x40 ^ blockindex delta in respect to previous + block (0 for last block) + Bit 0: flag for "last block" + Byte 1: last block: 1 - number of bytes in block + otherwise: 1 + number of contiguous blocks + +r184: + Byte 0: $ff: "file not found" + $00: "end of file" + otherwise: + Bits 7-1: blockindex delta in respect to previous block + Bit 0: flag for "last block" + Byte 1: last block: 0 - number of bytes in block + otherwise: 1 + number of contiguous blocks + +>= r186: + Byte 0: $ff: "file not found" + $00: "end of file" + otherwise: + last block: 0 - number of bytes in block + otherwise: number of contiguous blocks + Byte 1: Bits 7-1: blockindex delta in respect to previous block + Bit 0: flag for "last block" + +The 2-bit protocol is used for the entire transfer. After each block +the drive sets the busy line. If either "file not found" or "end of +file" was sent, the transfer is complete. Otherwise the next block +is fetched and sent. + +Note that the final block of the transfer isn't necessarily the last +(logical) block of the file, as blocks might be sent out of order. + +"File exists" Check +------------------- + +Revision r192 adds a "file exists" check: If the host set CLK before +releasing ATN when the drive is about to send the first metadata byte, +the transfer is aborted and the drive instead only indicates if the +requested file exists (DATA released) or not (DATA set). + +Custom Code Upload +------------------ + +Universal support of custom code would require specific detection and +emulation of the respective custom code (or a full drive emulation). + +Therefore only the official save plugin introduced with r186 is +supported. + +In r186, the host signals a custom code upload request by setting +CLK when releasing the request line. In r192 the code upload is just +sent like the filename. If the "filename" is longer than 16+1 bytes, +then it is a code upload. + +Before installing the actual custom code, the plugin framework first +sends a backup of most of the drive memory to the host. This isn't +really relevant for the sd2iec implementation, but still has to +be simulated. The protocol of r186 (used for the save plugin of +Scramble Infinity 1.2 [010]) relies on the drive sending the exact +number of bytes expected for the backup, which therefore has to be +extracted from the code download. The protocol was redesigned for +r192 to work with a timeout, similar to how the initial drivecode +download is handled. + +Save-Plugin +----------- + +The save plugin is restricted in that it can only overwrite (not +create or extend) an existing file. This isn't fully implemented in +the sd2iec emulation, which deletes and re-creates the file, but +still refuses to write to a non-existing file. + +Communication with the host is using the 1-bit send and receive +protocols. The filename is sent as part of the save plugin code. + +The save plugin then sends a metadata byte, which is either an error +code ($fe "write protect on" or $ff "file not found"), or the length +of the file in blocks. + +After that, the host sends the new data for the file in blocks of +a maximum of 254 bytes. Every block is preceded by one byte for +the number of bytes in the block (only relevant for the last block, +otherwise always $fe). + +After the file has been sent, the drive restores the memory backup +it sent before downloading the custom code and then re-enters the +request loop. + +ATN Responder +============= + +Starting with release r164, the loader checks for other active devices +on the bus during installation. If found (and if they pass the drive +detection) an "ATN responder" is installed, which monitors the ATN IN +signal and sets ATNA accordingly, to minimize the effect on the DATA +line. + +The sd2iec firmware also detects this and enters "sleep mode", which +is indicated by the static error LED and which can be deactivated by +a long press of the disk change button. + +Known Productions using this Loader (as of September 2022) +========================================================== + +Note: The table below is compiled from releases on CSDb crediting + Krill for the loader. Some (mostly pre-2007) productions use + a different codebase/protocol and therefore are neither + supported by the sd2iec implementation nor listed here. + +The protocol version listed in the table doesn't necessarily match +the actual loader revision used, but the public loader release that +uses this protocol. Exceptions are revisions "58pre", "159" and "186", +which all don't have public stand-alone loader releases. + +Reference: + ! = See specific comment at end of table + D = Non-standard directory location ("shadow directory") + R = Resend protocol + T = Track/Sector addressing + S = Save plugin + 4 = 40 Track diskimage + +Ref | Title | CSDb | Prot. | N +----|--------------------------------------------|--------|-------|--- +000 | Strawberry Strings | 221819 | 184 | +001 | Sloth Party Scroller | 221531 | 192 | D +002 | Quo Vadis | 220616 | 146 | +003 | Strikeback Preview Trainer Loader +3D | 220532 | 184 | +004 | Madwoods Ahoy | 220443 | 192 | D +005 | Fleshlite | 220016 | 184 | +006 | Retro SID-Cover Essentials | 219251 | 184 | +007 | Pro Patria | 218352 | 184 | +008 | Vandalism News #72 | 216880 | 192 | D +009 | Salvaged | 216865 | 164 | +010 | Scramble Infinity V1.2 | 212590 | 186 | DS +011 | Sonic the Hedgehog V1.2 | 212523 | 184 | +012 | Merry Twistmas | 212283 | 184 | +013 | Sonic the Hedgehog V1.1 | 212277 | 184 | ! +014 | Scramble Infinity | 212252 | 184 | D +015 | Sonic the Hedgehog | 212190 | 184 | +016 | Attitude #21 | 212061 | 184 | +017 | Sidmachine 3003 | 211728 | 184 | +018 | SUPER 16 | 211717 | 186 | +019 | Submerged | 211702 | 146 | +020 | Secam | 211354 | 184 | +021 | 30 Years Excess - The Member Presentation | 211126 | 184 | +022 | Thir(s)ty | 208403 | 186 | D +023 | Colour Spectrum | 205653 | 184 | ! +024 | Trapped in a Box | 205541 | 184 | +025 | Lost Realms of Murkasada Episode 1 | 205262 | 184 | +026 | Dutch Blue | 203384 | 164 | D +027 | Party Animals | 203375 | 184 | +028 | Seaworld | 203081 | 184 | +029 | None of Us Are Getting Out of This Life... | 200581 | 184 | +030 | Hardhat Construction Co. +2 | 199239 | 184 | +031 | Protogeo 100% | 198547 | 184 | ! +032 | Vandalism News #71 | 197870 | 184 | D +033 | Fantomas | 196684 | 184 | +034 | Snack | 195618 | 184 | +035 | Freaky Fish DX V1.1 | 195586 | 184 | +036 | Freaky Fish DX | 195108 | 184 | +037 | The Residents | 192920 | 184 | D +038 | DiverSIDty #1 | 192504 | 184 | +039 | Fight! | 189818 | 184 | D +040 | PSI-5 Trading Company +4D | 188012 | 164 | +041 | Limbo Living | 187562 | 164 | +042 | Gerposaurus | 187537 | 164 | +043 | Attitude #20 | 186926 | 164 | +044 | Vandalism News #70 | 183550 | 164 | D +045 | Out of Contex | 182438 | 164 | +046 | Demolution | 180332 | 146 | +047 | It's Magic 2 +6HDP [cartridge] | 180059 | 164 | +048 | It's Magic 2 +6HDP [cartridge] | 180008 | 164 | +049 | It's Magic 2 +6HDP [cartridge] | 179930 | 164 | +050 | Endless Forms Most Beautiful 64 V1.1 | 179817 | 164 | +051 | Endless Forms Most Beautiful 64 | 179794 | 164 | +052 | NGC 1277 100% | 179107 | 164 | +053 | Vandalism News #69 | 178594 | 164 | D +054 | NGC 1277 80% | 178035 | 164 | ! +055 | Space Beer | 175656 | 164 | +056 | Week in Progress | 175483 | 146 | +057 | Beezarro | 175475 | 164 | D +058 | ZX Art | 174536 | 164 | +059 | 4M Arena Demo1 | 173646 | 164 | +060 | Attitude #19 | 173486 | 164 | +061 | Vandalism News #68 | 171312 | 164 | D +062 | Pixels 35 | 170952 | 164 | +063 | Hi Five | 170951 | 164 | +064 | Go Gray | 167234 | 146 | +065 | Attitude #18 | 163647 | 146 | +066 | VF-SSDPCM1 Super Plus | 163621 | 159 | 4 +067 | SSDPCM2 V3 - 16khz | 162796 | 159 | +068 | CRX Race [unprotected original] | 160634 | 146 | +069 | Vandalism News #67 | 160588 | 146 | D +070 | Dreamtime 2K17 | 158080 | 146 | +071 | Madonna Music Collection 2017 | 152477 | 146 | +072 | Vandalism News #66 | 151569 | 146 | D +073 | Pixels | 151304 | 146 | +074 | Fallen Stars | 151293 | 146 | +075 | Prime Time | 151289 | 146 | +076 | Hoaxagon | 151282 | 146 | +077 | Vandalism News #65 | 150629 | 146 | D +078 | The Social Demo | 150284 | 146 | +079 | Attitude #17 | 149222 | 146 | +080 | We Miss You | 147974 | 146 | +081 | We Are Demo | 146723 | 146 | +082 | ABBA Gold - The Collection | 146701 | 146 | +083 | David Bowie Tribute | 145646 | 146 | +084 | Tangled Up | 145635 | 146 | +085 | From Berlin to Paris and Back | 145600 | 146 | +086 | The Mayday Show | 145596 | 146 | +087 | Yaemon's Tunebox 2 | 144617 | 146 | +088 | Rock Goes 80's | 144428 | 146 | +089 | 20 Years Onslaught | 144034 | 146 | +090 | Attitude #16 | 143966 | 146 | +091 | Zookeeper | 142269 | 146 | +092 | Ascetic | 141134 | 146 | +093 | Demo of the Year 2014 [102% final version] | 139705 | 146 | +094 | Demo of the Year 2014 | 139568 | 146 | +095 | Vandalism News #64 | 138976 | 146 | D +096 | GoatLight | 137495 | 146 | +097 | BCC Faces | 136809 | 146 | +098 | Pagan's Mind | 136795 | 146 | +099 | Magical Wizard | 133946 | 146 | +100 | Frantic 3: Nightmare | 133941 | 146 | +101 | Bee Together | 133939 | 146 | R +102 | RGB | 133937 | 146 | +103 | Pandas | 133331 | 146 | +104 | Attitude #15 | 133118 | 146 | +105 | Shine Logo Show | 132775 | 146 | +106 | VOA | 132252 | 146 | 4 +107 | Bad Apple 64 | 131628 | 146 | 4 +108 | Eclectic | 131301 | 146 | 4 +109 | Free Beer! | 129287 | 146 | +110 | Durch den Monsun | 129286 | 146 | +111 | Serpent | 129098 | 146 | +112 | Goats Who Stare at Men | 129092 | 146 | +113 | We Are All Connected | 129091 | 146 | +114 | Musik Run/Stop | 129090 | 146 | R +115 | Continuum | 129088 | 146 | RT +116 | Nordic Scene Review #8 | 128133 | 146 | +117 | Attitude #14 | 121481 | 146 | +118 | Solaris | 120911 | 146 | +119 | Wonderland XII | 120907 | 146 | +120 | Frontier | 120458 | 146 | +121 | Pieces of 8-bit | 119543 | 146 | R +122 | Famous Australians Vol.1 | 119441 | 146 | +123 | Sidelined | 118375 | 146 | +124 | Digital Underground | 117360 | 146 | +125 | Melangerie Mediocre | 116206 | 146 | R +126 | What Is the Matrix | 115684 | 146 | +127 | Too Old to Ror and Rol | 115677 | 146 | +128 | Revolved | 115653 | 146 | +129 | Demolicious | 115649 | 146 | +130 | Legacy | 113521 | 146 | +131 | 8bit Passion | 112872 | 146 | 4 +132 | Naked Grinder 2 | 112697 | 146 | +133 | Trick and Treat | 112424 | 146 | T +134 | Wonderland XI | 112423 | 146 | +135 | Cause of Death | 112422 | 146 | D +136 | Coma Light 13 | 112378 | 146 | D +137 | Vicious Sid 2 | 112365 | 146 | +138 | Artphosis | 112337 | 146 | +139 | Protogeo 80% | 112330 | 146 | ! +140 | Krawall Deluxe | 107788 | 146 | +141 | Flashbang | 107787 | 146 | +142 | Algotecher | 105196 | 146 | 4 +143 | Fragment | 105055 | 146 | +144 | Fifteen Minutes of Lame | 105054 | 146 | +145 | 64 Seconds | 105052 | 58 | +146 | VQ-Mania | 104219 | 146 | 4 +147 | 2011 - A Press Space Odyssey | 103742 | 146 | +148 | Introducing Audio VQ | 103099 | 146 | 4 +149 | Apparatus | 102318 | 146 | +150 | Snolgkos | 101521 | 146 | +151 | The Beating Heart | 101520 | 146 | +152 | Toxyc Taste | 101511 | 58 | +153 | Arpeggio | 100263 | 58pre | +154 | High Five | 98732 | 146 | +155 | Lash | 97772 | 146 | T +156 | SIDrip Alliance Music Collection 2010 | 96621 | 58 | ! +157 | The Richard Joseph Tribute | 94536 | 146 | +158 | Lifework | 94459 | 146 | T +159 | Another Beginning | 94448 | 146 | +160 | We Are New | 94445 | 146 | +161 | NESsivE ATtaCK | 94434 | 146 | +162 | Recollection #3 | 94424 | 146 | ! +163 | No Way | 94259 | 58pre | +164 | Black Spark 100% | 91494 | 146 | +165 | Anomaly | 91032 | 58pre | +166 | Black Spark 90% | 90335 | 146 | +167 | White | 87982 | 58 | +168 | Patterns | 87980 | 58 | +169 | Scramble 2010 Preview V2 | 86061 | 146 | +170 | Xmas 2009 | 85939 | 58 | +171 | Song of Fall - Broad Edition | 82096 | 58 | +172 | Scramble 2010 Preview | 81924 | 58pre | +173 | Faux Visage | 81165 | 58 | +174 | Still Ready | 81158 | 58 | +175 | Cast to See | 79054 | 58pre | +176 | Crepusculo 100% | 72756 | 58 | +177 | Crepusculo | 72556 | 58 | +178 | Pearls for Pigs | 72553 | 58 | 4 +179 | We Are One Family | 71406 | 58pre | +180 | Party Groove | 70212 | 58 | +181 | That Way | 69932 | 58pre | +182 | Gdzie Jest Ssijlont? | 52446 | 58pre | +183 | Sharp | 50575 | 58 | +184 | 0ldsk00l 4ever! | 45250 | 58pre | +185 | Oblique | 43583 | 58pre | +186 | The Wild Bunch | 41462 | 58 | ! +187 | Aurora 85%-90% | 41457 | 58pre | +188 | Industrial Terror | 38953 | 58pre | +189 | Trans*Form | 36851 | 58pre | +190 | Unicorn, the Collectors Edition | 33556 | 58pre | +191 | Unicorn | 32438 | 58pre | +192 | Recollection #1 | 29897 | 58pre | ! +193 | Psyche | 29844 | 58pre | +194 | 10/5 | 26006 | 58pre | +195 | LCP Memories | 21146 | 58pre | +196 | The Works | 21122 | 58pre | +197 | You Can't Stop Us! | 20167 | 58pre | +198 | Sphaeristerium | 19417 | 58pre | +199 | One Million Lightyears from Earth | 17292 | 58pre | +200 | Real | 15103 | 58pre | +201 | Dialogue | 15095 | 58pre | + +Production-specific Remarks +--------------------------- + +[013] Sonic the Hedgehog V1.1 + Should work, but not tested, as no longer available for download. + Both V1.0 [015] and V1.2 [011] are tested and work. + +[023] Colour Spectrum + This is a C128 release. The D64 images work, the D71 and D81 + trigger the use of the burst protocol, which isn't supported. + +[031] Protogeo 100% + The interactive part at the end of the demo uses unsupported custom + drivecode and therefore doesn't work. + +[045] Out of Contex + The side b diskimage contains a duplicate, illegal directory entry + for the file "WKQ", which only affects the sd2iec implementation + because of a slight difference in how directory lookup works. + + The image can be easily fixed, e.g. with sed, like so: + $ sed -bie 's/\x15\(\x05\x57\)/\x00\1/' ALD_Out_of_Contex_SIDEB.d64 + +[054] NGC 1277 80% + Hangs at some point for unknown reasons. 100% version [052] works. + +[139] Protogeo 80% + Hangs at some point for unknown reasons. 100% version [031] works + for the most part (see remarks above). + +[156] SIDrip Alliance Music Collection 2010 + Uses additional, unsupported custom drivecode and therefore doesn't + work. + +[162] Recollection #3 + To make it work, rename the loader PRG (the first directory entry) + to something that doesn't start with "< ", like "- RECOLLECTION -". + +[186] The Wild Bunch + Hangs at some point because it uses unsupported custom drivecode. + +[192] Recollection #1 + Krill's loader is only used for the intro. The diskmag itself uses + some different, unsupported loader and therefore doesn't work. diff --git a/scripts/Makefile.main b/scripts/Makefile.main index 9e96c587..e4f50a03 100644 --- a/scripts/Makefile.main +++ b/scripts/Makefile.main @@ -59,7 +59,7 @@ ifeq ($(CONFIG_HAVE_IEC),y) SRC += fl-epyxcart.c fl-fc3exos.c fl-geos.c fl-gijoe.c SRC += fl-mmzak.c fl-nippon.c fl-turbodisk.c fl-ulm3.c SRC += fl-n0sdos.c fl-samsjourney.c fl-ultraboot.c - SRC += fl-hypraload.c + SRC += fl-hypraload.c fl-krill.c endif ifneq ($(CONFIG_NO_SD),y) diff --git a/scripts/lpc17xx/variables.mk b/scripts/lpc17xx/variables.mk index 9ed5295e..f3e94a74 100644 --- a/scripts/lpc17xx/variables.mk +++ b/scripts/lpc17xx/variables.mk @@ -17,6 +17,7 @@ SRC += lpc17xx/llfl-parallel.c SRC += lpc17xx/llfl-n0sdos.c SRC += lpc17xx/llfl-ultraboot.c SRC += lpc17xx/llfl-hypraload.c +SRC += lpc17xx/llfl-krill.c ifeq ($(CONFIG_UART_DEBUG),y) SRC += lpc17xx/printf.c diff --git a/src/avr/fastloader-ll.S b/src/avr/fastloader-ll.S index d73f0f15..14dad8b2 100644 --- a/src/avr/fastloader-ll.S +++ b/src/avr/fastloader-ll.S @@ -1964,6 +1964,60 @@ hypraload_send_byte: sei ret +#endif + +#ifdef CONFIG_LOADER_KRILL + ;; + ;; send a byte using Krill's loader's resend protocol + ;; called with byte in r24, always returns zero + ;; + + ;; uint8_t krill_send_byte_resend(uint8_t byte) + .global krill_send_byte_resend +krill_send_byte_resend: + ;; called from an ATOMIC_BLOCK, so no need to inhibit interrupts + + ;; set up data byte for transmission + com r24 +1: + mov r0, r24 + + rcall wait_atn_low + rcall wait_atn_high + + ;; send bits 0+1 + delay_us 10, -16-RET_OFFSET-RCALL_OFFSET + rcall send_bits_to_clk_data ; 10+6 or 11+7 + + ;; send bits 2+3 + delay_us 18-10, -16-RET_OFFSET-RCALL_OFFSET + rcall send_bits_to_clk_data ; 10+6 or 11+7 + + ;; send bits 4+5 + delay_us 26-18, -16-RET_OFFSET-RCALL_OFFSET + rcall send_bits_to_clk_data ; 10+6 or 11+7 + + ;; send bits 6+7 + delay_us 34-26, -16-RET_OFFSET-RCALL_OFFSET + rcall send_bits_to_clk_data ; 10+6 or 11+7 + + delay_us 42-34, -7-RET_OFFSET + cbi _SFR_IO_ADDR(IEC_OUTPUT), IEC_OPIN_CLOCK ; 2 + + delay_us 50-42, -2 + + ;; return if the host has set ATN in time + sbis _SFR_IO_ADDR(IEC_INPUT), IEC_PIN_ATN + rjmp 2f + + ;; otherwise set clock to signal that we are going to send again + sbi _SFR_IO_ADDR(IEC_OUTPUT), IEC_OPIN_CLOCK + rjmp 1b + +2: + clr r24 + ret + #endif .end diff --git a/src/doscmd.c b/src/doscmd.c index 0b742fe6..ad6c30a4 100644 --- a/src/doscmd.c +++ b/src/doscmd.c @@ -74,6 +74,10 @@ enum { RXTX_FC3OF_PAL, RXTX_FC3OF_NTSC, + + RXTX_KRILL_DATA, + RXTX_KRILL_CLOCK, + RXTX_KRILL_RESEND, }; typedef uint8_t (*fastloader_rx_t)(void); @@ -101,6 +105,11 @@ static const PROGMEM struct fastloader_rxtx_s fl_rxtx_table[] = { [RXTX_FC3OF_PAL] = { NULL, fc3_oldfreeze_pal_send }, [RXTX_FC3OF_NTSC] = { NULL, fc3_oldfreeze_ntsc_send }, #endif +#ifdef CONFIG_LOADER_KRILL + [RXTX_KRILL_DATA] = { krill_get_byte_clk_data, krill_send_byte_atn }, + [RXTX_KRILL_RESEND] = { krill_get_byte_clk_data, krill_send_byte_resend }, + [RXTX_KRILL_CLOCK] = { krill_get_byte_data_clk, krill_send_byte_atn }, +#endif }; struct fastloader_crc_s { @@ -192,6 +201,53 @@ static const PROGMEM struct fastloader_crc_s fl_crc_table[] = { #ifdef CONFIG_LOADER_HYPRALOAD { 0xd2f2, FL_HYPRALOAD, RXTX_NONE }, #endif +#ifdef CONFIG_LOADER_KRILL + /* CRCs of the first respective M-W chunk except where noted */ + { 0x8227, FL_KRILL_R58, RXTX_NONE }, // r58/r146 drvchkme + { 0xe300, FL_KRILL_R186, RXTX_KRILL_CLOCK }, // second chunk + { 0x19a4, FL_KRILL_R184, RXTX_KRILL_CLOCK }, // second chunk + { 0x741d, FL_KRILL_R184, RXTX_KRILL_CLOCK }, + { 0xf7e4, FL_KRILL_R184, RXTX_KRILL_CLOCK }, + { 0x1eec, FL_KRILL_R164, RXTX_KRILL_CLOCK }, + { 0x4393, FL_KRILL_R164, RXTX_KRILL_CLOCK }, + { 0x6c47, FL_KRILL_R164, RXTX_KRILL_CLOCK }, + { 0xd9f1, FL_KRILL_R164, RXTX_KRILL_CLOCK }, + { 0xa905, FL_KRILL_R159, RXTX_KRILL_CLOCK }, + { 0xe7f6, FL_KRILL_R159, RXTX_KRILL_CLOCK }, + { 0x2028, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0x2c29, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0x4eb4, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0x5668, FL_KRILL_R146, RXTX_KRILL_DATA }, // second chunk + { 0x6a90, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0x74aa, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0x7c5e, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0x7e28, FL_KRILL_R146, RXTX_KRILL_DATA }, // second chunk + { 0xa1e7, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0xa350, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0xb0e4, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0xb340, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0xc1dc, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0xeb28, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0xf5a8, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0xfc9a, FL_KRILL_R146, RXTX_KRILL_DATA }, + { 0x03a5, FL_KRILL_R146, RXTX_KRILL_RESEND }, + { 0xba1f, FL_KRILL_R146, RXTX_KRILL_RESEND }, + { 0xca68, FL_KRILL_R146, RXTX_KRILL_RESEND }, + { 0x2fca, FL_KRILL_R58, RXTX_KRILL_DATA }, + { 0xb4ce, FL_KRILL_R58 , RXTX_KRILL_DATA }, // second chunk + { 0xe530, FL_KRILL_R58, RXTX_KRILL_DATA }, + { 0xf7aa, FL_KRILL_R58PRE, RXTX_KRILL_DATA }, + { 0x379d, FL_KRILL_R58PRE, RXTX_KRILL_DATA }, +#endif +#ifdef CONFIG_BUS_SILENCE_REQ + /* The loader uses a different method for drive identification */ + /* when installing the ATN responder. That's why the sd2iec is */ + /* identified as a 1541 even when a D81 image is mounted. */ + /* So only the 1541 code's CRCs are needed. */ + { 0x607d, FL_KRILL_SLEEP, RXTX_NONE }, // >= r186 + { 0x40c3, FL_KRILL_SLEEP, RXTX_NONE }, // r184 + { 0x5088, FL_KRILL_SLEEP, RXTX_NONE }, // r164 +#endif { 0, FL_NONE, 0 }, // end marker }; @@ -271,6 +327,49 @@ static const PROGMEM struct fastloader_handler_s fl_handler_table[] = { #ifdef CONFIG_LOADER_HYPRALOAD { 0x048b, FL_HYPRALOAD, load_hypraload, 0 }, #endif +#if defined(CONFIG_LOADER_KRILL) || defined(CONFIG_BUS_SILENCE_REQ) + { 0x0205, FL_NONE, drvchkme_krill, 1 }, // < r192 drvchkme + { 0x020a, FL_NONE, drvchkme_krill, 2 }, // >= r192 drvchkme + { 0x0205, FL_KRILL_SLEEP, bus_sleep_krill, 0 }, // < r192 ATN responder + { 0x020b, FL_NONE, bus_sleep_krill, 1 }, // >= r192 ATN responder +#endif +#ifdef CONFIG_LOADER_KRILL + { 0x0300, FL_KRILL_R58, drvchkme_krill, 0 }, // <= r146 drvchkme + { 0x0209, FL_NONE, load_krill, 0 }, // >= r192 load + { 0x0770, FL_KRILL_R186, load_krill, 0 }, + { 0x0758, FL_KRILL_R184, load_krill, 0 }, + { 0x0770, FL_KRILL_R184, load_krill, 0 }, + { 0x07a8, FL_KRILL_R184, load_krill, 0 }, + { 0x06d8, FL_KRILL_R164, load_krill, 0 }, + { 0x077e, FL_KRILL_R164, load_krill, 0 }, + { 0x07aa, FL_KRILL_R164, load_krill, 0 }, + { 0x07ac, FL_KRILL_R164, load_krill, 0 }, + { 0x07a5, FL_KRILL_R159, load_krill, 0 }, + { 0x07b1, FL_KRILL_R159, load_krill, 0 }, + { 0x056f, FL_KRILL_R146, load_krill, 0 }, + { 0x0570, FL_KRILL_R146, load_krill, 0 }, + { 0x0577, FL_KRILL_R146, load_krill, 0 }, + { 0x05e9, FL_KRILL_R146, load_krill, 0 }, + { 0x05ea, FL_KRILL_R146, load_krill, 0 }, + { 0x05ec, FL_KRILL_R146, load_krill, 0 }, + { 0x05ee, FL_KRILL_R146, load_krill, 0 }, + { 0x05ef, FL_KRILL_R146, load_krill, 0 }, + { 0x05fc, FL_KRILL_R146, load_krill, 0 }, + { 0x05fe, FL_KRILL_R146, load_krill, 0 }, + { 0x0610, FL_KRILL_R146, load_krill, 0 }, + { 0x066e, FL_KRILL_R146, load_krill, 0 }, + { 0x06a4, FL_KRILL_R146, load_krill, 0 }, + { 0x06b6, FL_KRILL_R146, load_krill, 0 }, + { 0x05fc, FL_KRILL_R58, load_krill, 0 }, + { 0x05fe, FL_KRILL_R58, load_krill, 0 }, + { 0x05ff, FL_KRILL_R58, load_krill, 0 }, + { 0x0626, FL_KRILL_R58, load_krill, 0 }, + { 0x0668, FL_KRILL_R58, load_krill, 0 }, + { 0x05da, FL_KRILL_R58PRE, load_krill, 0 }, + { 0x05f1, FL_KRILL_R58PRE, load_krill, 0 }, + { 0x05f4, FL_KRILL_R58PRE, load_krill, 0 }, + { 0x0600, FL_KRILL_R58PRE, load_krill, 0 }, +#endif { 0, FL_NONE, NULL, 0 }, // end marker }; @@ -299,9 +398,14 @@ static const PROGMEM struct fastloader_capture_s fl_capture_table[] = { static const PROGMEM magic_value_t drive_magics[] = { /* used by DreamLoad and ULoad Model 3 */ { 0xfea0, { 0x0d, 0xed }, DRIVE_1541|DRIVE_1571 }, + /* used by DreamLoad, ULoad Model 3 and Krill's loader */ { 0xe5c6, { 0x34, 0xb1 }, DRIVE_1541 }, /* Disable AR6 fastloader; entry no longer needed, as 0 is the default now */ // { 0xfffe, { 0x00, 0x00 }, 0xff }, + /* used by Krill's loader */ + { 0xeaa3, { 0xff, 0x4c }, DRIVE_1541 }, + { 0xe5c6, { 0x37, 0xb1 }, DRIVE_1571 }, + { 0xa6e9, { 0x38, 0xb1 }, DRIVE_1581 }, /* end mark */ { 0, { 0, 0 }, 0 } diff --git a/src/fastloader-ll.h b/src/fastloader-ll.h index 6289a71c..315c18b0 100644 --- a/src/fastloader-ll.h +++ b/src/fastloader-ll.h @@ -69,6 +69,11 @@ uint8_t ultraboot_send_byte(uint8_t byte); void hypraload_send_byte(uint8_t byte); +uint8_t krill_get_byte_data_clk(void); +uint8_t krill_get_byte_clk_data(void); +uint8_t krill_send_byte_atn(uint8_t byte); +uint8_t krill_send_byte_resend(uint8_t byte); + typedef enum { PARALLEL_DIR_IN = 0, PARALLEL_DIR_OUT } parallel_dir_t; diff --git a/src/fastloader.h b/src/fastloader.h index ea6603dc..33129b6c 100644 --- a/src/fastloader.h +++ b/src/fastloader.h @@ -65,6 +65,15 @@ typedef enum { FL_SAMSJOURNEY, FL_ULTRABOOT, FL_HYPRALOAD, + FL_KRILL_SLEEP, + FL_KRILL_R58PRE, + FL_KRILL_R58, + FL_KRILL_R146, + FL_KRILL_R159, + FL_KRILL_R164, + FL_KRILL_R184, + FL_KRILL_R186, + FL_KRILL_R192, } fastloaderid_t; extern fastloaderid_t detected_loader; @@ -100,6 +109,9 @@ bool load_ultraboot(uint8_t); bool write_ultraboot(uint8_t); bool format_ultraboot(uint8_t); bool load_hypraload(uint8_t); +bool drvchkme_krill(uint8_t); +bool bus_sleep_krill(uint8_t); +bool load_krill(uint8_t); int16_t dolphin_getc(void); uint8_t dolphin_putc(uint8_t data, uint8_t with_eoi); diff --git a/src/fl-krill.c b/src/fl-krill.c new file mode 100644 index 00000000..b0f99878 --- /dev/null +++ b/src/fl-krill.c @@ -0,0 +1,1184 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-2022 Ingo Korb + Final Cartridge III, DreamLoad, ELoad fastloader support: + Copyright (C) 2008-2011 Thomas Giesel + Nippon Loader support: + Copyright (C) 2010 Joerg Jungermann + Krill's Loader support: + Copyright (C) 2022 Martin Thierer + + Inspired by MMC2IEC by Lars Pontoppidan et al. + + FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + fl-krill.c: High level handling of Krill's loader + +*/ + + +#include +#include +#include "config.h" +#include "crc.h" +#include "buffers.h" +#include "d64ops.h" +#include "doscmd.h" +#include "errormsg.h" +#include "fastloader-ll.h" +#include "iec-bus.h" +#include "parser.h" +#include "timer.h" +#include "uart.h" +#include "ustring.h" +#include "wrapops.h" +#include "fastloader.h" + + +/* magic string used in M-E commands of >= r192 */ +#define KRILL_MAGIC PSTR("KRILL") + +/* offsets of the various configuration parameters into the ID string */ +enum { + ID_REPO_VER = 12, + ID_PLATFORM = ID_REPO_VER + 2, + ID_DRIVE, + ID_DIRTRACK, + ID_FN_MAXLEN, + ID_CONFIG_INT, +}; + +/* handling of the various loader/protocol variants */ + +/* session state */ +typedef struct { + dh_t dh; /* directory handle (current seek state) */ + path_t path; + uint8_t dir_track; /* directory track (255 => default) */ + uint8_t bam_sector; /* BAM sector on directory track (0 => default) */ + uint8_t fn_maxlength; /* Max. filename length (<= 16) */ + uint8_t file_count; /* number of files loaded in the current session */ + uint16_t file_crc; /* crc of the current file, used for file quirks */ + uint16_t backup_len; /* length of the (simulated) drive memory backup */ + int ts_load:1; /* files are addressed using track and sector */ +} session_t; + +/* must be less than the minimum parameter offset in ld_variants[] */ +#define DC_CRC_LEN 0xf0 + +typedef struct { + uint16_t crc; /* crc16 of the first DC_CRC_LEN bytes of the drivcode */ + uint16_t fn_maxlength_offs; + uint16_t dir_track_offs; +} ld_variant_t; + +/* only variants that have known variations from the defaults are listed */ +static const PROGMEM ld_variant_t ld_variants[] = { + { 0x4a88, 0x216, 0x3a1 }, // r186 + { 0x8ad2, 0x215, 0x384 }, // r184 + { 0xf4a2, 0x435, 0x1c5 }, // r164 + { 0x8d19, 0x435, 0x1c5 }, // r164 + { 0xec9c, 0x435, 0x1c5 }, // r164 + { 0x0519, 0x3b4, 0x3da }, // r146 + { 0x214c, 0x3a5, 0x3c8 }, // r146 + { 0x3154, 0x3a8, 0x3e6 }, // r146 + { 0x3e7a, 0x3c4, 0x3f9 }, // r146 + { 0x4c28, 0x3a3, 0x3c9 }, // r146 + { 0xcba6, 0x3b2, 0x3d8 }, // r146 + { 0xea6c, 0x3a4, 0x3ca }, // r146 + { 0x49e4, 0x318, 0x342 }, // r58 + + { 0, 0, 0 } // end marker +}; + +/* hacks to make specific releases work */ + +/* resolution in [ms] for block_delay in file_quirks_t */ +#define BDEL_TIME 20 + +typedef struct { + uint16_t crc; /* crc of the *previous* file */ + uint8_t block_delay; /* delay between block transfers in ms */ +} file_quirks_t; + +static const PROGMEM file_quirks_t file_quirks[] = { + { 0x1ba6, 20 }, /* coma light 13 / "SAMPLE" */ + { 0xe5ac, 80 }, /* coma light 13 / "PICDAT" */ + { 0xfe43, 80 }, /* protogeo 100% / 13th file */ + { 0x7f19, 120 }, /* pearls for pigs / "03" */ + { 0x8e1e, 20 }, /* cause of death / "PLOTBALL" */ + + { 0, 0 } // end marker +}; + +/* handlers for different custom code blocks; only used for r186 */ +typedef uint16_t (*cc_handler_t)(session_t *); + +static uint16_t cc_read_length(session_t *); +static uint16_t cc_mem_backup(session_t *); +static uint16_t cc_save_plugin(session_t *); + +struct cc_handler_s { + uint16_t crc; + cc_handler_t handler; + uint8_t params; // number of bytes to leave for the handler to process +}; + +#define CC_STAGE0_LEN 0x29 + +#define CC_CRC_LEN (CC_STAGE0_LEN-2) // stage 0 loader minus parameters + +static const PROGMEM struct cc_handler_s cc_handler_table[] = { + { 0xf923, cc_read_length, 0x02 }, // custom drivecode loader stage 0 + { 0xab48, cc_mem_backup, 0x07 }, + { 0x5e85, cc_save_plugin, 0x11 }, + + { 0, 0, 0 }, // end marker +}; + +/* check if the magic string is present in the command_buffer */ +static bool magic_string_matches(void) { + return ustrncmp_P(command_buffer+5, KRILL_MAGIC, strlen_P(KRILL_MAGIC)) == 0; +} + +/* Universal handler for possible drvchkme memexecs */ +/* f == 0: drvchkme of <= r146; detected by M-W, no further checks necessary */ +/* f == 1: possible drvchkme of <= r186; check command crc */ +/* f == 2: possible drvchkme of >= r192; check command for magic string */ +bool drvchkme_krill(uint8_t f) { + uint8_t i; + uint16_t crc; + + switch (f) { + case 0: /* <= r146: M-W drvchkme crc matched */ + break; + + case 1: /* check command length and crc for drvchkme */ + if (command_length != 0x1a && command_length != 0x17) /* r159 is 0x17 */ + return false; + + crc = 0xffff; + + for (i = 5; i < command_length; i++) + crc = crc16_update(crc, command_buffer[i]); + + if (crc != 0xca5b && crc != 0xf35b) /* drvchkme; r159 is 0xf35b */ + return false; + + break; + + case 2: /* >= r192 */ + if (!magic_string_matches()) + return false; + + break; + + default: + return false; + } + + custom_magic.address = 0x300; + custom_magic.val[0] = ~0; /* the first read will have returned 0 */ + custom_magic.val[1] = 0; + custom_magic.drives = 0xff; /* applicable for all drive types */ + + return true; +} + +bool bus_sleep_krill(uint8_t check_magic) { + if (check_magic) { /* >= r192; M-W crc not matched, check magic string */ + if (!magic_string_matches()) + return false; + } else { + /* we don't want FL_KRILL_SLEEP to persist */ + detected_loader = FL_NONE; + } + + /* activate bus sleep */ + return bus_sleep(0); +} + +static uint8_t wait_atn_low(void) { + int16_t to; + + /* botch a ~1s timeout using multiple 16ms timeouts (max. duration on AVR) */ + for (to = 1000; to > 0; to -= 16) { + start_timeout(16000); + + while (!has_timed_out()) { + if (!IEC_ATN) + return 0; + } + } + + return 1; /* timed out */ +} + +static uint8_t get_byte_1bit(iec_bus_t clk, iec_bus_t data) { + uint8_t tc, i, b = 0; + iec_bus_t bus; + + bus = iec_bus_read(); + + for (i = 8; i != 0; i--) { + tc = 9; +timeout_loop: + start_timeout(10000); + + /* wait for respective clock edge */ + while ((iec_bus_read() & clk) == (bus & clk)) { + if (has_timed_out()) { + /* Abort if the clock line hasn't changed for 90ms (9 * 10ms) */ + if (--tc == 0) + return 0; + + goto timeout_loop; + } + } + + delay_us(2); + bus = iec_bus_read(); + + b = b >> 1 | (bus & data ? 0 : 0x80); + } + + return b; +} + +/* used by <= r146 */ +uint8_t krill_get_byte_clk_data(void) { + return get_byte_1bit(IEC_BIT_CLOCK, IEC_BIT_DATA); +} + +/* used by r164 for filenames */ +uint8_t krill_get_byte_clk_atn(void) { + return get_byte_1bit(IEC_BIT_CLOCK, IEC_BIT_ATN); +} + +/* used by >r146 (in r164 only used for drivecode install) */ +uint8_t krill_get_byte_data_clk(void) { + return get_byte_1bit(IEC_BIT_DATA, IEC_BIT_CLOCK); +} + +/* used by the save plugin for status byte and drive memory backup */ +static uint8_t send_byte_1bit(uint8_t b) { + uint8_t tc, i; + + for (i = 8; i != 0; i--) { + set_data(!(b&0x80)); + b <<= 1; + + tc = 9; +timeout_loop: + start_timeout(10000); + + while ((!IEC_CLOCK) == (i&1)) { + if (IEC_ATN) + return 1; + if (has_timed_out()) { + /* Abort if the clock line hasn't changed for 90ms (9 * 10ms) */ + if (--tc == 0) + return 1; + + goto timeout_loop; + } + } + } + + return 0; +} + +static void send_bitpair_r58pre(uint8_t *b, uint8_t i) { + switch (i) { + case 4: + set_clock(*b&0x80); + set_data(*b&0x20); + break; + case 3: + set_clock(*b&0x40); + set_data(*b&0x10); + break; + case 2: + set_clock(*b&0x08); + set_data(*b&0x02); + break; + case 1: + set_clock(*b&0x04); + set_data(*b&0x01); + break; + } +} + +static void send_bitpair(uint8_t *b, uint8_t i) { + set_clock(*b&1); + set_data(*b&2); + *b >>= 2; +} + +uint8_t krill_send_byte_atn(uint8_t b) { + uint8_t i; + void (*send_fn)(uint8_t *, uint8_t); + + if (detected_loader >= FL_KRILL_R58) { + send_fn = send_bitpair; + } else { + b = ~b; + send_fn = send_bitpair_r58pre; + } + + for (i = 4; i != 0; i--) { + if (i&1) { /* wait for ATN low with ~1s timeout */ + if (wait_atn_low()) + break; /* timed out */ + } else { + while (!IEC_ATN); /* no timeout needed for ATN low -> high transition */ + } + + send_fn(&b, i); + } + + return IEC_ATN != 0; /* if ATN is not set here, something is wrong */ +} + +static uint8_t load_drivecode(session_t *s) { + uint16_t i, len, crc, tmp; + uint8_t b, bp; + iec_bus_t mask; + const ld_variant_t *ptr, *detected_variant; + + crc = 0xffff; + ptr = ld_variants; + len = DC_CRC_LEN + 1; + detected_variant = NULL; + bp = 0; + + set_clock(0); + + /* wait for either ATN or DATA low, depending on the protocol version */ + mask = detected_loader >= FL_KRILL_R184 ? IEC_BIT_ATN : IEC_BIT_DATA; + + while ((iec_bus_read() & mask) == mask) + if (check_keys()) + return 1; + + set_clock(1); + set_data(1); + + delay_us(2); + + for (i = 0; ; i++) { + b = fast_get_byte(); + + if (has_timed_out()) { + set_clock(0); + if (detected_loader < FL_KRILL_R184) + set_data(0); + + if (detected_variant == NULL) { + uart_puts_P(PSTR("Unknown drivecode, CRC ")); + uart_puthex(crc >> 8); + uart_puthex(crc & 0xff); + uart_puts_P(PSTR(" loader ")); + uart_puthex(detected_loader); + uart_putcrlf(); + uart_flush(); + } + + /* end of drivecode */ + break; + } + + if (i > len) + continue; /* ignore the rest with busy line set and wait for timeout */ + + if (i == len) { + /* set the respective busy line so the first request isn't made early */ + if (detected_loader >= FL_KRILL_R184) set_clock(0); + else if (detected_loader == FL_KRILL_R164) set_data(0); + } + + /* read DC_CRC_LEN bytes and try to match the crc with a known variant. */ + /* versions >= r192 are configured from the id string; will never match. */ + if (detected_variant != NULL) { + if (i == pgm_read_word(&detected_variant->dir_track_offs)) { + s->dir_track = b; + } else if (i == pgm_read_word(&detected_variant->fn_maxlength_offs)) { + switch (detected_loader) { + case FL_KRILL_R58PRE: /* should not happen, always 2 */ + tmp = 2; + break; + case FL_KRILL_R146: + tmp = (b + 1) & 0x7f; + break; + case FL_KRILL_R164: + tmp = (~b + 1) & 0xff; + break; + default: // r58, r184, r186 + tmp = b; + break; + } + + if (tmp > 0 && tmp < s->fn_maxlength) + s->fn_maxlength = tmp; + } else if (b < 13 && bp == 0xa0 && detected_loader <= FL_KRILL_R146 && + i == pgm_read_word(&detected_variant->dir_track_offs)+2) { + /* r58 and r146 support a custom dir start sector */ + s->bam_sector = b; + } + + bp = b; /* remember previous byte for potential dir sector check */ + continue; + } + + if (i < DC_CRC_LEN) { + crc = crc16_update(crc, b); + continue; + } + + /* AVR at 8 MHz is too slow to check the whole table between */ + /* 2 bytes, so limit the time spent for searching to ~25 us. */ + for (start_timeout(25);; ptr++) { + if ( (tmp = pgm_read_word(&ptr->crc)) == 0 ) /* end of table reached */ + break; + + if (has_timed_out()) { + len++; /* continue searching the table after the next byte */ + break; + } + + if (tmp != crc) + continue; + + /* Found! Adjust len if parameter offsets are defined for this variant */ + if ( (tmp = pgm_read_word(&ptr->dir_track_offs) + 2) > len ) + len = tmp; + if ( (tmp = pgm_read_word(&ptr->fn_maxlength_offs)) > len ) + len = tmp; + + detected_variant = ptr; + break; + } + } + + return wait_atn_low(); +} + +/* set up the directory state after a disk change */ +static void update_path(session_t *s) { + partition_t *part; + buffer_t *buf; + + part = &partition[current_part]; + + s->path.part = current_part; + s->path.dir = part->current_dir; + + if (part->fop == &d64ops && s->dir_track <= part->d64data.last_track) { + switch (part->imagetype & D64_TYPE_MASK) { + case D64_TYPE_D41: + case D64_TYPE_D71: + /* use custom dir track and sector*/ + s->path.dir.dxx.track = s->dir_track; + + if (s->bam_sector == 0) + break; /* use standard dir start sector */ + + /* read BAM sector to find the first dir sector */ + buf = alloc_buffer(); + if (!buf) + break; + + read_sector(buf, current_part, s->dir_track, s->bam_sector); + if (buf->data[0] == s->dir_track) /* safety check */ + s->path.dir.dxx.sector = buf->data[1]; + + free_buffer(buf); + break; + + case D64_TYPE_D81: + /* use custom dir track */ + s->path.dir.dxx.track = s->dir_track; + break; + + default: + break; + } + } + + dir_changed = 0; +} + +static uint8_t find_file(session_t *s, cbmdirent_t *dent) { + if (command_buffer[0] != '*') { + /* make sure the dir handle and the path are in a usable state */ + if (dir_changed) + update_path(s); + + if (opendir(&s->dh, &s->path)) + return 1; + + /* force inclusion of entries with type 0 as hidden files for D64 images */ + if (partition[current_part].fop == &d64ops) { + switch (partition[current_part].imagetype & D64_TYPE_MASK) { + case D64_TYPE_D41: + case D64_TYPE_D71: + case D64_TYPE_D81: + s->dh.dir.d64.hidden = 1; + break; + default: + break; + } + } + } + + return next_match(&s->dh, command_buffer, NULL, NULL, FLAG_HIDDEN, dent); +} + +/* Simple check for possible T/S adressing. Not very */ +/* reliable, but works for all known productions. */ +static bool is_valid_ts(void) { + return detected_loader <= FL_KRILL_R146 && + command_buffer[1] <= 20 && command_buffer[0] <= 41 && command_buffer[0] > 0; +} + +/* emulate the required minimum of d64_read() */ +static uint8_t next_sector(buffer_t *buf) { + read_sector(buf, current_part, buf->data[0], buf->data[1]); + buf->sendeoi = buf->data[0] == 0; + buf->lastused = buf->sendeoi ? buf->data[1] : 255; + + return current_error != ERROR_OK; +} + +/* Read a filename into the command_buffer using */ +/* the respective 1 bit receive protocol. */ +/* Returns the number of bytes read (which might */ +/* be more than fn_maxlength bytes for >= r192, */ +/* if it's really a custom drivecode upload). */ +static uint8_t read_filename(session_t *s) { + uint8_t i, max_len; + + /* r192 might send more than 16+1 bytes, if */ + /* it is in fact a custom drivecode upload. */ + if (detected_loader >= FL_KRILL_R192) + max_len = CBM_NAME_LENGTH+2; + else + max_len = s->fn_maxlength; + + set_clock(1); + set_data(1); + + /* read filename or track/sector */ + for (i = 0; i < max_len; i++) { + if ( (command_buffer[i] = fast_get_byte()) == 0 ) // could also be timeout + break; + + /* Stop early if T/S loading or first file and valid T/S for D41. */ + /* This auto-detection of T/S addressing would fail for one-character */ + /* filenames with a PETSCII code <= 41, but no affected productions */ + /* are known. Also wouldn't work for valid track and sector ranges of */ + /* productions using D71 or D81 images, but none are known, either. */ + if (i == 1 && (s->ts_load || (s->file_count == 0 && is_valid_ts()))) + break; + } + + if (detected_loader != FL_KRILL_R164) set_clock(0); + set_data(0); + + if (i == 0) { + command_buffer[0] = '*'; /* match next */ + command_buffer[1] = '\0'; + } else if (s->fn_maxlength < CBM_NAME_LENGTH) { + /* Only fn_maxlength bytes have to match, even though on >= r192 */ + /* the host could in theory send a longer filename/pattern. */ + command_buffer[s->fn_maxlength] = '*'; /* match prefix */ + command_buffer[s->fn_maxlength+1] = '\0'; + } + + return i; +} + +/* Open a buffer for the file specified in command_buffer: */ +/* Either a string (null-terminated if less than 16 bytes) */ +/* or two bytes specifying start-track and -sector. */ +static buffer_t *get_file_buf(session_t *s) { + buffer_t *buf; + cbmdirent_t dent; + + buf = alloc_buffer(); + if (!buf) + return NULL; + + if (!s->ts_load && !find_file(s, &dent)) { + open_read(&s->path, &dent, buf); + } else { + if (!s->ts_load) { + /* switch to T/S addressing if first file and valid T/S, else error */ + if (s->file_count > 0 || !is_valid_ts()) { + free_buffer(buf); + return NULL; + } + + s->ts_load = 1; + } + + /* track/sector load */ + buf->data[0] = command_buffer[0]; + buf->data[1] = command_buffer[1]; + + /* read first sector */ + if (next_sector(buf)) + return NULL; + + buf->refill = next_sector; + } + + s->file_count++; + + return buf; +} + +static uint8_t get_block_delay(uint16_t crc) { + uint16_t c; + const file_quirks_t *ptr; + + for (ptr = file_quirks;; ptr++) { + c = pgm_read_word(&ptr->crc); + + if (c == 0) + break; + + if (c == crc) + return pgm_read_byte(&ptr->block_delay); + } + + return 0; +} + +static uint8_t send_file(session_t *s) { + buffer_t *buf; + uint8_t hd[2] = {0xff, 0}; + uint8_t bi; + uint8_t to = 0; + uint16_t i; + uint8_t bdel; + + bdel = get_block_delay(s->file_crc); + + buf = get_file_buf(s); + /* no "file not found" error for "next file" at the end of the directory */ + if (!buf && !dir_changed && command_buffer[0] == '*') + hd[0] = detected_loader > FL_KRILL_R146 ? 0 : 0xfe; + + bi = 0; + + s->file_crc = 0xffff; + + while (true) { + if (buf) { + /* prepare block metadata */ + switch (detected_loader) { + case FL_KRILL_R58PRE: + case FL_KRILL_R58: + case FL_KRILL_R146: + /* the EOF marker is the status byte (0xfe) */ + hd[0] = bi; + hd[1] = buf->lastused-2; + break; + case FL_KRILL_R159: + case FL_KRILL_R164: + hd[0] = 0x82 - buf->sendeoi; + hd[1] = !buf->sendeoi ? bi+2 : ~buf->lastused+2; + break; + case FL_KRILL_R184: + hd[0] = 2 | buf->sendeoi; + hd[1] = !buf->sendeoi ? bi+2: ~buf->lastused+1; + break; + default: // >= R186 + hd[0] = !buf->sendeoi ? bi+1: ~buf->lastused+1; + hd[1] = 2 | buf->sendeoi; + break; + } + } + + ATOMIC_BLOCK( ATOMIC_FORCEON ) { + /* data ready */ + set_data(detected_loader == FL_KRILL_R164); + set_clock(detected_loader != FL_KRILL_R164); + + if (detected_loader <= FL_KRILL_R146) + while (IEC_ATN); + + /* check for "file exists" test (CLK set by host) */ + if (detected_loader >= FL_KRILL_R192) { + while (!IEC_ATN && IEC_CLOCK); + + if (!IEC_CLOCK) { + while (!IEC_ATN); + set_data(buf != NULL); + /* buffer will be cleaned up by main loop */ + buf = NULL; + goto abort; + } + } + + to = fast_send_byte(hd[0]); + if (buf != NULL && !to) { + to = fast_send_byte(hd[1]); + + for (i = 2; i <= buf->lastused && !to; i++) { + to = fast_send_byte(buf->data[i]); + s->file_crc = crc16_update(s->file_crc, buf->data[i]); + } + } + + /* fast_send_byte() exits with ATN low (bitpair not yet ackowledged) */ + while (!IEC_ATN); + + /* busy */ + set_clock(buf != NULL && detected_loader == FL_KRILL_R164); + set_data(buf != NULL && detected_loader != FL_KRILL_R164); + + if (buf != NULL) { + for (i = 0; i < bdel; i += BDEL_TIME) + delay_ms(BDEL_TIME); + } + } + +abort: + if (buf == NULL && detected_loader == FL_KRILL_R58PRE) + while (IEC_ATN); + + if (to || ((detected_loader > FL_KRILL_R146 || buf == NULL) && wait_atn_low()) || buf == NULL) + break; + + if (!buf->sendeoi) { + if (!buf->refill(buf)) { + bi++; + continue; + } + + hd[0] = 0xff; /* error */ + } else { + /* this was the last block, status ok */ + hd[0] = detected_loader > FL_KRILL_R146 ? 0 : 0xfe; + } + + cleanup_and_free_buffer(buf); + buf = NULL; + } + + return to; +} + +static uint16_t cc_read_length(session_t *s) { + uint8_t i; + uint16_t w; + + w = 0; + + for (i = 0; i < 2; i++) { + w = w>>8 | fast_get_byte()<<8; + if (IEC_ATN) + return 0; + } + + return ~w + 1; +} + +static uint16_t cc_mem_backup(session_t *s) { + uint16_t i; + + for (i = 0; i < 5; i++) { + fast_get_byte(); + if (IEC_ATN) + return 0; + } + + i = cc_read_length(s); + if (i == 0) + return 0; + + while (!IEC_DATA || !IEC_CLOCK) + if (IEC_ATN) + return 0; + + /* simulate drive memory backup */ + for (s->backup_len = 0; ; s->backup_len++) { + /* send 0xff so DATA is kept low and we can wait for the timeout */ + if (send_byte_1bit(0xff)) { + if (IEC_ATN) + return 0; + + break; + } + } + + set_clock(0); + set_data(0); + + delay_us(20); + + return i; +} + +/* Save file with save plugin protocol, filename in command_buffer */ +static uint8_t cc_save_file(session_t *s) { + uint8_t i; + buffer_t *buf; + cbmdirent_t dent; + uint8_t st; + + if (!find_file(s, &dent)) { + /* don't rely on file_delete() and open_write() preserving the filesize */ + st = dent.blocksize; + + /* allocate the buffer first to make sure we can create the file later */ + buf = alloc_buffer(); + + if (buf != NULL && file_delete(&s->path, &dent) == 1) { + open_write(&s->path, &dent, dent.typeflags & TYPE_MASK, buf, 0); + } else { + st = 0xfe; /* write protect */ + } + } else { + st = 0xff; /* not found */ + } + + set_clock(1); + delay_us(2); + + if (send_byte_1bit(st) || st >= 0xfe) /* send timed out or error */ + return 1; + + while (true) { + set_clock(1); + set_data(1); + + delay_us(2); + + i = fast_get_byte(); + if (IEC_ATN || has_timed_out()) /* just to be safe, should not happen */ + return 1; + + if (i > 0) { + mark_buffer_dirty(buf); + + while (i--) { + buf->data[buf->position++] = fast_get_byte(); + if (IEC_ATN) + return 1; + } + } + + set_clock(0); + set_data(0); + + buf->lastused = buf->position-1; + buf->mustflush = buf->position == 0; + + if (--st == 0) { + /* clear remaining buffer data */ + memset(buf->data + buf->position, 0, 0x100 - buf->position); + cleanup_and_free_buffer(buf); + break; + } + + if (buf->refill(buf)) + return 1; + } + + return 0; +} + +/* save plugin handler specific to r186 (only Scramble Infinity 1.2) */ +static uint16_t cc_save_plugin(session_t *s) { + uint8_t i; + + for (i = 0; i < CBM_NAME_LENGTH+1; i++) { + command_buffer[i] = fast_get_byte(); + if (IEC_ATN) + return 0; + } + + while (!IEC_CLOCK) + if (IEC_ATN) + return 0; + + set_clock(0); + set_data(0); + + if (cc_save_file(s)) + return 0; + + set_clock(1); + set_data(1); + + delay_us(2); + + /* simulate drive memory restore */ + while (s->backup_len--) { + fast_get_byte(); + if (IEC_ATN) + break; + } + + return 0; +} + +/* Fallback (assuming save plugin) for unknown custom drivecode. Only */ +/* works with protocol used by r192 and later. Others will error out. */ +static uint8_t cc_fallback(session_t *s) { + uint8_t i, b; + + set_clock(0); + + /* plugin loader code */ + do { + fast_get_byte(); + } while (!has_timed_out()); + + set_clock(1); + set_data(0); + while (IEC_CLOCK); + + /* simulate drive memory backup */ + do { + /* send 0xff so DATA is kept low and we can wait for the timeout */ + send_byte_1bit(0xff); + } while (!has_timed_out()); + + set_clock(1); + set_data(1); + + /* save plugin code & filename (last fn_maxlength+1 bytes). */ + /* collect the last fn_maxlength+1 bytes in a ring buffer. */ + for (i = 0;; i = (i == s->fn_maxlength ? 0 : i+1)) { + b = fast_get_byte(); + if (has_timed_out()) + break; + + ops_scratch[i] = b; + } while (!has_timed_out()); + + /* handshake before starting payload transfer */ + set_data(0); + while (IEC_CLOCK && !IEC_ATN); + set_clock(0); + set_data(1); + while (!IEC_DATA && !IEC_ATN); + if (IEC_ATN) + return 1; + + /* extract filename from ring buffer */ + for (b = 0; ops_scratch[i] != 0; i = (i == s->fn_maxlength ? 0 : i+1)) + command_buffer[b++] = ops_scratch[i]; + + if (s->fn_maxlength < CBM_NAME_LENGTH) + command_buffer[b++] = '*'; /* match prefix */ + command_buffer[b] = '\0'; + + /* receive payload and write to file */ + if (cc_save_file(s)) + return 1; + + set_clock(1); + set_data(1); + + /* simulate restore drive memory */ + do { + fast_get_byte(); + set_clock(0); /* set busy line after the first byte to enable timeout */ + } while (!has_timed_out()); + + set_data(0); + + return 0; +} + +static uint8_t custom_code_handler(session_t *s) { + int16_t len; + uint16_t crc; + uint16_t i; + uint8_t b; + const struct cc_handler_s *ptr; + cc_handler_t handler; + + set_data(0); + while (!IEC_CLOCK) + if (IEC_ATN) + return 1; + + len = CC_STAGE0_LEN; + + while (len != 0) { + crc = 0xffff; + handler = NULL; + + set_clock(1); + set_data(1); + + delay_us(2); + + for (i = 0; i < len; i++) { + b = fast_get_byte(); + if (IEC_ATN) + return 1; + + if (i >= CC_CRC_LEN) + continue; + + crc = crc16_update(crc, b); + + if (i == CC_CRC_LEN-1) { + /* try to find a handler */ + for (ptr = cc_handler_table;; ptr++) { + handler = (cc_handler_t)pgm_read_word(&ptr->handler); + + if (handler == NULL) { + uart_puts_P(PSTR("Unhandled custom drivecode, CRC ")); + uart_puthex(crc >> 8); + uart_puthex(crc & 0xff); + uart_putcrlf(); + + return cc_fallback(s); + } + + if (crc == pgm_read_word(&ptr->crc)) + break; + } + + /* stop early so the handler can read its parameters */ + len -= pgm_read_byte(&ptr->params); + + if (len < i) /* should not happen */ + return 1; + } + } + + len = handler(s); + } + + return 0; +} + +bool load_krill(UNUSED_PARAMETER) { + int8_t fn_len; + iec_bus_t req_line; + session_t session; + + memset(&session, 0, sizeof(session)); + + + if (detected_loader == FL_NONE && command_length >= 19) { + if (ustrncmp_P(command_buffer+5, KRILL_MAGIC, strlen_P(KRILL_MAGIC))) + return false; /* magic string didn't match */ + + detected_loader = FL_KRILL_R192; + fast_get_byte = krill_get_byte_data_clk; + fast_send_byte = krill_send_byte_atn; + + /* M-E with ID-string; extract parameters */ + session.dir_track = command_buffer[ID_DIRTRACK]; + session.fn_maxlength = command_buffer[ID_FN_MAXLEN]; + } else { + /* set defaults; actual parameters will be extracted from the drivecode */ + session.dir_track = 255; + session.fn_maxlength = detected_loader != FL_KRILL_R58PRE ? CBM_NAME_LENGTH : 2; + } + + set_atn_irq(0); + + if (load_drivecode(&session)) + goto exit; + + /* r164 uses DATA as data line for the drivecode, but ATN for requests */ + if (detected_loader == FL_KRILL_R164) + fast_get_byte = krill_get_byte_clk_atn; + + session.file_count = 0; + dir_changed = 1; /* force directory update */ + + if (detected_loader == FL_KRILL_R159 || detected_loader >= FL_KRILL_R184) { + req_line = IEC_BIT_DATA; + } else { + req_line = IEC_BIT_ATN; + } + + while (!IEC_ATN) { + set_clock(detected_loader == FL_KRILL_R164); + set_data(detected_loader != FL_KRILL_R164); + + free_multiple_buffers(FMB_USER_CLEAN); + + /* wait for host request while checking for abort and diskchange */ + while (!(iec_bus_read() & req_line)) + if (check_keys()) /* exit loop on long key press */ + goto exit; + + delay_us(10); + /* abort if both ATN and DATA were released */ + if ((iec_bus_read() & (IEC_BIT_DATA|IEC_BIT_ATN)) == (IEC_BIT_DATA|IEC_BIT_ATN)) + goto exit; + + if (detected_loader >= FL_KRILL_R184) { + set_clock(1); + delay_us(2); + } + + /* versions < r186 also have custom code upload, but we don't support it */ + if (detected_loader < FL_KRILL_R186 || IEC_CLOCK) { + fn_len = read_filename(&session); + + if (fn_len == -1) /* timed out */ + goto exit; + + if (fn_len <= CBM_NAME_LENGTH) { + if (send_file(&session)) + break; + } else { + /* More than CBM_NAME_LENGTH (16) bytes received. Can only */ + /* happen with r192 and indicates a custom drivecode upload. */ + if (cc_fallback(&session)) + break; + + /* The host might have already signaled the next request while */ + /* cc_fackback() waited for the faked drive memory restore to */ + /* time out, so skip the wait for the request line to be set. */ + continue; + } + } else { + /* custom drivecode handler for r186 (only Scramble Infinity 1.2) */ + if (custom_code_handler(&session)) + break; + } + + /* Wait for the request line to be set */ + while (iec_bus_read() & req_line) + if (check_keys()) /* exit loop on long key press */ + goto exit; + } + +exit: + set_clock(1); + set_data(1); + set_atn_irq(1); + + /* loader no longer active past this point */ + detected_loader = FL_NONE; + + return true; +} diff --git a/src/lpc17xx/llfl-krill.c b/src/lpc17xx/llfl-krill.c new file mode 100644 index 00000000..d029193e --- /dev/null +++ b/src/lpc17xx/llfl-krill.c @@ -0,0 +1,66 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-2021 Ingo Korb + Krill's Loader support: + Copyright (C) 2022 Martin Thierer + + Inspired by MMC2IEC by Lars Pontoppidan et al. + + FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + llfl-krill.c: Low level handling of Krill's loader "resend" transfers + +*/ + +#include "config.h" +#include +#include +#include "llfl-common.h" +#include "system.h" +#include "fastloader-ll.h" + +static const generic_2bit_t krill_resend_def = { + .pairtimes = {100, 180, 260, 340}, + .clockbits = {0, 2, 4, 6}, + .databits = {1, 3, 5, 7}, + .eorvalue = 0x00 +}; + +/* Send a byte in the "resend" protocol used in Krill's loader r146 */ +uint8_t krill_send_byte_resend(uint8_t byte) { + llfl_setup(); + disable_interrupts(); + + for (;;) { + llfl_wait_atn(1); + + /* transmit data */ + llfl_generic_load_2bit(&krill_resend_def, byte); + llfl_set_clock_at(420, 1, NO_WAIT); + + /* We are done if host sets ATN in time; otherwise send same byte again */ + if ((llfl_read_bus_at(500) & IEC_BIT_ATN) == 0) + break; + + /* signal to host that we are going to resend */ + set_clock(0); + } + + enable_interrupts(); + llfl_teardown(); + + return 0; +} diff --git a/src/lpc17xx/progmem.h b/src/lpc17xx/progmem.h index d37fe757..d5e543c6 100644 --- a/src/lpc17xx/progmem.h +++ b/src/lpc17xx/progmem.h @@ -36,5 +36,7 @@ #define memcmp_P(s1,s2,n) memcmp(s1,s2,n) #define strcpy_P(dest,src) strcpy(dest,src) #define strcmp_P(s1,s2) strcmp(s1,s2) +#define strncmp_P(s1,s2,n) strncmp(s1,s2,n) +#define strlen_P(s) strlen(s) #endif diff --git a/src/ustring.h b/src/ustring.h index 7077f4da..fe012e2f 100644 --- a/src/ustring.h +++ b/src/ustring.h @@ -32,6 +32,7 @@ #define ustrchr(s,c) ((uint8_t *)strchr((char *)(s), (c))) #define ustrcmp(s1,s2) (strcmp((char *)(s1), (char *)(s2))) #define ustrcmp_P(s1,s2) (strcmp_P((char *)(s1), (s2))) +#define ustrncmp_P(s1,s2,n) (strncmp_P((char *)(s1), (s2), (n))) #define ustrcpy(s1,s2) (strcpy((char *)(s1), (char *)(s2))) #define ustrcpy_P(s1,s2) (strcpy_P((char *)(s1), (s2))) #define ustrncpy(s1,s2,n) (strncpy((char *)(s1), (char *)(s2),(n)))