├── 6502 ├── ssc_show_hide.S ├── SSC.CF00.S ├── SSC.UTIL.S ├── SSC.C800.S ├── SSC.TERM.S ├── SSC.CN00.S ├── SSC.HILEV.S ├── SSC.S └── SSC.CORE.S ├── .gitignore ├── incbin.S ├── sd_spi ├── include │ ├── rtc.h │ ├── glue.h │ ├── f_util.h │ ├── my_debug.h │ ├── util.h │ └── ff_stdio.h ├── sd_driver │ ├── hw_config.h │ ├── crc.h │ ├── sd_spi.h │ ├── hw_config.c │ ├── spi.h │ ├── sd_card.h │ ├── sd_spi.c │ ├── crc.c │ └── spi.c └── src │ ├── my_debug.c │ ├── f_util.c │ ├── rtc.c │ └── glue.c ├── fatfs ├── source │ ├── 00readme.txt │ ├── diskio.h │ ├── diskio.c │ └── ffsystem.c └── LICENSE.txt ├── LICENSE ├── ser.h ├── main.h ├── board.h ├── config.h ├── firmware.h ├── usb_diskio.h ├── sp.h ├── hdd.h ├── ser.c ├── main.c ├── tusb_config.h ├── getting-started.md ├── msc_host.c ├── pico_sdk_import.cmake ├── usb_diskio.c ├── msc_device.c ├── diskio.c ├── CMakeLists.txt ├── usb_descriptors.c ├── hdd.c ├── board.c └── sp.c /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.o 3 | -------------------------------------------------------------------------------- /incbin.S: -------------------------------------------------------------------------------- 1 | .section .time_critical.firmware 2 | .global firmware 3 | .type firmware, %object 4 | .balign 4 5 | firmware: 6 | .incbin "../../firmware.rom" 7 | -------------------------------------------------------------------------------- /sd_spi/include/rtc.h: -------------------------------------------------------------------------------- 1 | /* rtc.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | #pragma once 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | void time_init(); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | -------------------------------------------------------------------------------- /sd_spi/include/glue.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level SD card interface modlue include file (C)ChaN, 2019 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _GLUE_DEFINED 6 | #define _GLUE_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /*------------------------------------------*/ 13 | /* Prototypes for SD card control functions */ 14 | 15 | 16 | DSTATUS sd_disk_initialize(BYTE pdrv); 17 | DSTATUS sd_disk_status(BYTE pdrv); 18 | DRESULT sd_disk_read(BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); 19 | DRESULT sd_disk_write(BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count); 20 | DRESULT sd_disk_ioctl(BYTE pdrv, BYTE cmd, void* buff); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /fatfs/source/00readme.txt: -------------------------------------------------------------------------------- 1 | FatFs Module Source Files R0.15 2 | 3 | 4 | FILES 5 | 6 | 00readme.txt This file. 7 | 00history.txt Revision history. 8 | ff.c FatFs module. 9 | ffconf.h Configuration file of FatFs module. 10 | ff.h Common include file for FatFs and application module. 11 | diskio.h Common include file for FatFs and disk I/O module. 12 | diskio.c An example of glue function to attach existing disk I/O module to FatFs. 13 | ffunicode.c Optional Unicode utility functions. 14 | ffsystem.c An example of optional O/S related functions. 15 | 16 | 17 | Low level disk I/O module is not included in this archive because the FatFs 18 | module is only a generic file system layer and it does not depend on any specific 19 | storage device. You need to provide a low level disk I/O module written to 20 | control the storage device that attached to the target system. 21 | 22 | -------------------------------------------------------------------------------- /sd_spi/sd_driver/hw_config.h: -------------------------------------------------------------------------------- 1 | /* hw_config.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | #pragma once 15 | 16 | #include "ff.h" 17 | #include "sd_card.h" 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | size_t sd_get_num(); 24 | sd_card_t *sd_get_by_num(size_t num); 25 | 26 | size_t spi_get_num(); 27 | spi_t *spi_get_by_num(size_t num); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | /* [] END OF FILE */ 34 | -------------------------------------------------------------------------------- /sd_spi/include/f_util.h: -------------------------------------------------------------------------------- 1 | /* f_util.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | #pragma once 15 | #include "ff.h" 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | const char *FRESULT_str(FRESULT i); 22 | FRESULT delete_node ( 23 | TCHAR* path, /* Path name buffer with the sub-directory to delete */ 24 | UINT sz_buff, /* Size of path name buffer (items) */ 25 | FILINFO* fno /* Name read buffer */ 26 | ); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Oliver Schmidt (https://a2retro.de/) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ser.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2025 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef _SER_H 28 | #define _SER_H 29 | 30 | void ser_task(void); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2025 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef _MAIN_H 28 | #define _MAIN_H 29 | 30 | void io_task(void); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /board.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2024 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef _BOARD_H 28 | #define _BOARD_H 29 | 30 | void board(void); 31 | 32 | uint8_t board_slot(void); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2025 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef _CONFIG_H 28 | #define _CONFIG_H 29 | 30 | #define MAX_DRIVES 8 31 | 32 | void config_reset(void); 33 | 34 | uint8_t config_drives(void); 35 | 36 | char *config_drivepath(uint8_t drive); 37 | 38 | void config(void); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /sd_spi/include/my_debug.h: -------------------------------------------------------------------------------- 1 | /* my_debug.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | #pragma once 15 | 16 | #include 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | void my_printf(const char *pcFormat, ...) __attribute__((format(__printf__, 1, 2))); 23 | 24 | void my_assert_func(const char *file, int line, const char *func, 25 | const char *pred); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | 32 | #ifdef NDEBUG /* required by ANSI standard */ 33 | # define DBG_PRINTF(fmt, args...) {} /* Don't do anything in release builds*/ 34 | #else 35 | # define DBG_PRINTF my_printf 36 | #endif 37 | 38 | #ifdef NDEBUG /* required by ANSI standard */ 39 | # define myASSERT(__e) ((void)0) 40 | #else 41 | # define myASSERT(__e) \ 42 | ((__e) ? (void)0 : my_assert_func(__FILE__, __LINE__, __func__, #__e)) 43 | #endif -------------------------------------------------------------------------------- /firmware.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2025 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef _FIRMWARE_H 28 | #define _FIRMWARE_H 29 | 30 | #define OFFSET_NORMAL 0x0000 31 | #define OFFSET_SERIAL 0x1000 32 | #define OFFSET_BANK_1 0x2000 33 | #define OFFSET_BANK_2 0x2800 34 | #define OFFSET_BANK_3 0x3000 35 | 36 | extern __attribute__((aligned(4))) uint8_t firmware[]; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /sd_spi/src/my_debug.c: -------------------------------------------------------------------------------- 1 | /* my_debug.c 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | #include 15 | #include 16 | #include "my_debug.h" 17 | 18 | void my_printf(const char *pcFormat, ...) { 19 | char pcBuffer[256] = {0}; 20 | va_list xArgs; 21 | va_start(xArgs, pcFormat); 22 | vsnprintf(pcBuffer, sizeof(pcBuffer), pcFormat, xArgs); 23 | va_end(xArgs); 24 | printf("%s", pcBuffer); 25 | fflush(stdout); 26 | } 27 | 28 | 29 | void my_assert_func(const char *file, int line, const char *func, 30 | const char *pred) { 31 | printf("assertion \"%s\" failed: file \"%s\", line %d, function: %s\n", 32 | pred, file, line, func); 33 | fflush(stdout); 34 | __asm volatile("cpsid i" : : : "memory"); /* Disable global interrupts. */ 35 | while (1) { 36 | __asm("bkpt #0"); 37 | }; // Stop in GUI as if at a breakpoint (if debugging, otherwise loop 38 | // forever) 39 | } 40 | -------------------------------------------------------------------------------- /usb_diskio.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2025 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef _USB_DISKIO_H 28 | #define _USB_DISKIO_H 29 | 30 | DSTATUS usb_disk_initialize(BYTE pdrv); 31 | 32 | DSTATUS usb_disk_status(BYTE pdrv); 33 | 34 | DRESULT usb_disk_read(BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); 35 | 36 | DRESULT usb_disk_write(BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count); 37 | 38 | DRESULT usb_disk_ioctl(BYTE pdrv, BYTE cmd, void* buff); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /sd_spi/sd_driver/crc.h: -------------------------------------------------------------------------------- 1 | /* crc.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | /* Derived from: 15 | * SD/MMC File System Library 16 | * Copyright (c) 2016 Neil Thiessen 17 | * 18 | * Licensed under the Apache License, Version 2.0 (the "License"); 19 | * you may not use this file except in compliance with the License. 20 | * You may obtain a copy of the License at 21 | * 22 | * http://www.apache.org/licenses/LICENSE-2.0 23 | * 24 | * Unless required by applicable law or agreed to in writing, software 25 | * distributed under the License is distributed on an "AS IS" BASIS, 26 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 27 | * See the License for the specific language governing permissions and 28 | * limitations under the License. 29 | */ 30 | 31 | #ifndef SD_CRC_H 32 | #define SD_CRC_H 33 | 34 | #include 35 | 36 | char crc7(const char* data, int length); 37 | unsigned short crc16(const char* data, int length); 38 | void update_crc16(unsigned short *pCrc16, const char data[], size_t length); 39 | 40 | #endif 41 | 42 | /* [] END OF FILE */ 43 | -------------------------------------------------------------------------------- /sp.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2024 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef _SP_H 28 | #define _SP_H 29 | 30 | #define CONTROL_NONE 0x00 31 | #define CONTROL_PRODOS 0x01 32 | #define CONTROL_SP 0x02 33 | #define CONTROL_CONFIG 0x40 34 | #define CONTROL_DONE 0x80 35 | 36 | extern volatile uint8_t sp_control; 37 | extern volatile uint8_t sp_buffer[0x0400]; 38 | extern volatile uint16_t sp_read_offset; 39 | extern volatile uint16_t sp_write_offset; 40 | 41 | void sp_init(void); 42 | 43 | void sp_reset(void); 44 | 45 | void sp_task(void); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /hdd.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2024 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef _HDD_H 28 | #define _HDD_H 29 | 30 | void hdd_init(void); 31 | 32 | void hdd_reset(void); 33 | 34 | void hdd_mount_usb(bool); 35 | 36 | bool hdd_sd_mounted(void); 37 | 38 | bool hdd_usb_mounted(void); 39 | 40 | uint8_t hdd_drives(void); 41 | 42 | bool hdd_protected(uint8_t drive); 43 | 44 | uint8_t hdd_status(uint8_t drive, uint8_t *data); 45 | 46 | uint8_t hdd_read(uint8_t drive, uint16_t block, uint8_t *data); 47 | 48 | uint8_t hdd_write(uint8_t drive, uint16_t block, const uint8_t *data); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /sd_spi/sd_driver/sd_spi.h: -------------------------------------------------------------------------------- 1 | /* sd_spi.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | 15 | #ifndef _SD_SPI_H_ 16 | #define _SD_SPI_H_ 17 | 18 | #include 19 | #include "sd_card.h" 20 | 21 | /* Transfer tx to SPI while receiving SPI to rx. 22 | tx or rx can be NULL if not important. */ 23 | bool sd_spi_transfer(sd_card_t *pSD, const uint8_t *tx, uint8_t *rx, size_t length); 24 | uint8_t sd_spi_write(sd_card_t *pSD, const uint8_t value); 25 | void sd_spi_deselect_pulse(sd_card_t *pSD); 26 | void sd_spi_acquire(sd_card_t *pSD); 27 | void sd_spi_release(sd_card_t *pSD); 28 | void sd_spi_go_low_frequency(sd_card_t *this); 29 | void sd_spi_go_high_frequency(sd_card_t *this); 30 | 31 | /* 32 | After power up, the host starts the clock and sends the initializing sequence on the CMD line. 33 | This sequence is a contiguous stream of logical ‘1’s. The sequence length is the maximum of 1msec, 34 | 74 clocks or the supply-ramp-uptime; the additional 10 clocks 35 | (over the 64 clocks after what the card should be ready for communication) is 36 | provided to eliminate power-up synchronization problems. 37 | */ 38 | void sd_spi_send_initializing_sequence(sd_card_t * pSD); 39 | 40 | #endif 41 | 42 | /* [] END OF FILE */ 43 | -------------------------------------------------------------------------------- /ser.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2025 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include "ser.h" 31 | 32 | void ser_task(void) { 33 | 34 | while (true) { 35 | if (!multicore_fifo_wready()) { 36 | break; 37 | } 38 | int32_t data = tud_cdc_read_char(); 39 | if (data == -1) { 40 | break; 41 | } 42 | multicore_fifo_push_blocking(data); 43 | } 44 | 45 | while (true) { 46 | if (!multicore_fifo_rvalid()) { 47 | break; 48 | } 49 | uint32_t data = multicore_fifo_pop_blocking(); 50 | tud_cdc_write_char(data); 51 | } 52 | tud_cdc_write_flush(); 53 | } 54 | -------------------------------------------------------------------------------- /fatfs/LICENSE.txt: -------------------------------------------------------------------------------- 1 | FatFs License 2 | 3 | FatFs has being developped as a personal project of the author, ChaN. It is free from the code anyone else wrote at current release. Following code block shows a copy of the FatFs license document that heading the source files. 4 | 5 | /*----------------------------------------------------------------------------/ 6 | / FatFs - Generic FAT Filesystem Module Rx.xx / 7 | /-----------------------------------------------------------------------------/ 8 | / 9 | / Copyright (C) 20xx, ChaN, all right reserved. 10 | / 11 | / FatFs module is an open source software. Redistribution and use of FatFs in 12 | / source and binary forms, with or without modification, are permitted provided 13 | / that the following condition is met: 14 | / 15 | / 1. Redistributions of source code must retain the above copyright notice, 16 | / this condition and the following disclaimer. 17 | / 18 | / This software is provided by the copyright holder and contributors "AS IS" 19 | / and any warranties related to this software are DISCLAIMED. 20 | / The copyright owner or contributors be NOT LIABLE for any damages caused 21 | / by use of this software. 22 | /----------------------------------------------------------------------------*/ 23 | 24 | Therefore FatFs license is one of the BSD-style licenses, but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code, binary library and any forms without source code, do not need to include about FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most of open source software licenses include GNU GPL. When you redistribute the FatFs source code with changes or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software license that not conflict with FatFs license. 25 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2024 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "board.h" 34 | #include "ser.h" 35 | #include "sp.h" 36 | 37 | #include "main.h" 38 | 39 | void io_task(void) { 40 | #if MEDIUM == SD 41 | tud_task(); 42 | ser_task(); 43 | #elif MEDIUM == USB 44 | tuh_task(); 45 | #endif 46 | } 47 | 48 | void main(void) { 49 | busctrl_hw->priority = BUSCTRL_BUS_PRIORITY_PROC1_BITS; 50 | multicore_launch_core1(board); 51 | 52 | set_sys_clock_khz(170000, false); 53 | 54 | stdio_init_all(); 55 | printf("*** A2retroNET ***\n"); 56 | 57 | tusb_init(); 58 | sp_init(); 59 | 60 | while (true) { 61 | io_task(); 62 | sp_task(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #if MEDIUM == SD 30 | #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED) 31 | #elif MEDIUM == USB 32 | #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | OPT_MODE_FULL_SPEED) 33 | #endif 34 | 35 | #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED 36 | #define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED 37 | 38 | #define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED 39 | #define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED 40 | 41 | #define CFG_TUD_MSC 1 42 | #define CFG_TUD_CDC 1 43 | 44 | #define CFG_TUH_MSC 1 45 | 46 | #define CFG_TUD_MSC_EP_BUFSIZE 8192 47 | 48 | #define CFG_TUD_CDC_EP_BUFSIZE 64 49 | #define CFG_TUD_CDC_RX_BUFSIZE 64 50 | #define CFG_TUD_CDC_TX_BUFSIZE 64 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /6502/ssc_show_hide.S: -------------------------------------------------------------------------------- 1 | .macpack apple2 2 | .feature c_comments 3 | 4 | /* 5 | 6 | MIT License 7 | 8 | Copyright (c) 2025 Oliver Schmidt (https://a2retro.de/) 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | sta $CFFF 31 | lda #<$C8F6 32 | sta $80 33 | lda #>$C8F6 34 | sta $81 35 | ldx #$08 36 | 37 | slot: dec $81 38 | dex 39 | beq quit 40 | 41 | ldy #$03 42 | byte: lda ($80),y 43 | cmp olsc,y 44 | bne slot 45 | dey 46 | bpl byte 47 | 48 | .ifdef hide 49 | sta $CFFD 50 | .else 51 | sta $CFFE 52 | .endif 53 | 54 | quit: 55 | .ifdef init 56 | rts 57 | .else 58 | jsr $BF00 ; MLI call entry point 59 | .byte $65 ; Quit 60 | .word param 61 | param: .byte $04 ; param_count 62 | .byte $00 ; quit_type 63 | .word $0000 ; reserved 64 | .byte $00 ; reserved 65 | .word $0000 ; reserved 66 | .endif 67 | 68 | olsc: scrcode "OlSc" 69 | -------------------------------------------------------------------------------- /getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | The firmware you selected at purchase is pre-flashed on your A2Pico. However, we recommend flashing A2Pico again if you're not 100% sure which firmware (variant) you chose, or simply want to make sure you're using the latest firmware. 4 | 5 | Flashing your A2Pico is a breeze. All you need is a standard USB cable: 6 | 1. Press and hold the `BOOTSEL` button on the A2Pico. 7 | 2. Connect the A2Pico to a PC. 8 | 3. Release the `BOOTSEL` button. A new drive, `RPI-RP2`, will appear. 9 | 3. Copy the firmware `.uf2` file to the `RPI-RP2` drive, e.g., by drag & drop. 10 | 4. Disconnect the A2Pico from the PC. Done. 11 | 12 | The latest A2retroNET firmware files can always be found at: https://github.com/oliverschmidt/a2retronet/releases/latest 13 | 14 | There are two variants of the A2retroNET firmware: 15 | * `A2retroNET.uf2` allows access to the Micro SD Card from a connected PC and also emulates a Super Serial Card. 16 | * `A2retroNET-USB.uf2` allows the use of a USB Thumb Drive in addition to the Micro SD Card. 17 | 18 | We recommend using a Micro SD Card for your first steps with both firmware variants. The Micro SD Card must be formatted with FAT, FAT32, or exFAT. 19 | 20 | Now, with the flashed A2Pico and the formatted Micro SD Card at hand, perform the following steps: 21 | 22 | 1. Download the Total Replay .hdv file from https://archive.org/details/TotalReplay and save it as the only file in the root directory of the Micro SD Card. 23 | 24 | 2. Insert the Micro SD Card into the A2Pico and plug the A2Pico into an Apple II slot of your choice. We recommend slot 7. 25 | 26 | 3. Turn on the Apple II. On an Enhanced //e or IIgs, you'll see a countdown starting with `3` in the lower right corner. On a ][+ or original //e, you'll need to type `PR#7` to start the countdown. 27 | 28 | 4. Press the `C` key during the countdown. 29 | 30 | 5. In the menu that now appears, drive 1 should be selected at the top and the Total Replay .hdv file should be selected at the bottom. Simply press `RETURN` to insert the .hdv file into drive 1. 31 | 32 | 6. Press `ESC` to exit the menu and begin the actual boot process. 33 | 34 | 7. The Total Replay splash screen should now appear. 35 | 36 | The A2retroNET README contains all the information you need to get the most out of it: https://github.com/oliverschmidt/a2retronet 37 | -------------------------------------------------------------------------------- /sd_spi/include/util.h: -------------------------------------------------------------------------------- 1 | /* util.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | #ifndef _UTIL_H_ 15 | #define _UTIL_H_ 16 | 17 | #include 18 | #include 19 | 20 | #include "hardware/structs/scb.h" 21 | 22 | // works with negative index 23 | static inline int wrap_ix(int index, int n) 24 | { 25 | return ((index % n) + n) % n; 26 | } 27 | 28 | __attribute__((always_inline)) static inline uint32_t calculate_checksum(uint32_t const *p, size_t const size){ 29 | uint32_t checksum = 0; 30 | for (uint32_t i = 0; i < (size/sizeof(uint32_t))-1; i++){ 31 | checksum ^= *p; 32 | p++; 33 | } 34 | return checksum; 35 | } 36 | 37 | 38 | // from Google Chromium's codebase: 39 | #ifndef COUNT_OF 40 | #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) 41 | #endif 42 | 43 | __attribute__((always_inline)) static inline void __DSB(void) { 44 | __asm volatile("dsb 0xF" ::: "memory"); 45 | } 46 | 47 | // Patterned after CMSIS NVIC_SystemReset 48 | __attribute__((__noreturn__)) static inline void system_reset() { 49 | __DSB(); /* Ensure all outstanding memory accesses included 50 | buffered write are completed before reset */ 51 | scb_hw->aircr = ((0x5FAUL << 16U) | (1UL << 2U)); 52 | __DSB(); /* Ensure completion of memory access */ 53 | for (;;) { 54 | __asm volatile("nop"); 55 | } 56 | } 57 | 58 | /** 59 | \brief Disable IRQ Interrupts 60 | \details Disables IRQ interrupts by setting the I-bit in the CPSR. 61 | Can only be executed in Privileged modes. 62 | */ 63 | __attribute__((always_inline)) static inline void __disable_irq(void) { 64 | __asm volatile("cpsid i" : : : "memory"); 65 | } 66 | 67 | #endif 68 | /* [] END OF FILE */ 69 | -------------------------------------------------------------------------------- /sd_spi/sd_driver/hw_config.c: -------------------------------------------------------------------------------- 1 | /* hw_config.c 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | /* 15 | 16 | This file should be tailored to match the hardware design. 17 | 18 | There should be one element of the spi[] array for each hardware SPI used. 19 | 20 | There should be one element of the sd_cards[] array for each SD card slot. 21 | The name is should correspond to the FatFs "logical drive" identifier. 22 | (See http://elm-chan.org/fsw/ff/doc/filename.html#vol) 23 | The rest of the constants will depend on the type of 24 | socket, which SPI it is driven by, and how it is wired. 25 | 26 | */ 27 | 28 | #include 29 | #include 30 | 31 | // Hardware Configuration of SPI "objects" 32 | // Note: multiple SD cards can be driven by one SPI if they use different slave 33 | // selects. 34 | static spi_t spis[] = { // One for each SPI. 35 | { 36 | .hw_inst = spi0, 37 | .miso_gpio = GPIO_SPI0_RX, 38 | .mosi_gpio = GPIO_SPI0_TX, 39 | .sck_gpio = GPIO_SPI0_SCK, 40 | .baud_rate = 12500 * 1000 41 | } 42 | }; 43 | 44 | // Hardware Configuration of the SD Card "objects" 45 | static sd_card_t sd_cards[] = { // One for each SD card 46 | { 47 | .pcName = "", // Name used to mount device 48 | .spi = &spis[0], // Pointer to the SPI driving this card 49 | .ss_gpio = GPIO_SPI0_CSN, // The SPI slave select for this SD card 50 | .use_card_detect = false, 51 | } 52 | }; 53 | 54 | /* ********************************************************************** */ 55 | 56 | size_t sd_get_num() { 57 | return count_of(sd_cards); 58 | } 59 | 60 | sd_card_t *sd_get_by_num(size_t num) { 61 | if (num <= sd_get_num()) { 62 | return &sd_cards[num]; 63 | } else { 64 | return NULL; 65 | } 66 | } 67 | 68 | size_t spi_get_num() { 69 | return count_of(spis); 70 | } 71 | 72 | spi_t *spi_get_by_num(size_t num) { 73 | if (num <= spi_get_num()) { 74 | return &spis[num]; 75 | } else { 76 | return NULL; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /msc_host.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2019 Ha Thach (tinyusb.org) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #include 28 | 29 | #include "hdd.h" 30 | 31 | // From https://github.com/hathach/tinyusb/ examples/host/msc_file_explorer/src/msc_app.c 32 | 33 | // Define the buffer to be place in USB/DMA memory with correct alignment/cache line size 34 | CFG_TUH_MEM_SECTION static struct { 35 | TUH_EPBUF_TYPE_DEF(scsi_inquiry_resp_t, inquiry); 36 | } scsi_resp; 37 | 38 | static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) { 39 | msc_cbw_t const* cbw = cb_data->cbw; 40 | msc_csw_t const* csw = cb_data->csw; 41 | 42 | if (csw->status != 0) { 43 | printf("inquiry failed\n"); 44 | return false; 45 | } 46 | 47 | // Print out Vendor ID, Product ID and Rev 48 | printf(" %.8s %.16s rev %.4s\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id, scsi_resp.inquiry.product_rev); 49 | 50 | // Get capacity of device 51 | uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun); 52 | uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun); 53 | 54 | printf(" Block Count:%lu, Block Size:%lu\n", block_count, block_size); 55 | printf(" Disk Size:%" PRIu32 " MB\n", block_count / ((1024*1024) / block_size)); 56 | 57 | hdd_mount_usb(true); 58 | return true; 59 | } 60 | 61 | void tuh_msc_mount_cb(uint8_t dev_addr) { 62 | printf("MSC Mount(Device=%d)\n", dev_addr); 63 | 64 | tuh_msc_inquiry(dev_addr, 0, &scsi_resp.inquiry, inquiry_complete_cb, 0); 65 | } 66 | 67 | void tuh_msc_umount_cb(uint8_t dev_addr) { 68 | printf("MSC Unmount(Device=%d)\n", dev_addr); 69 | 70 | hdd_mount_usb(false); 71 | } 72 | -------------------------------------------------------------------------------- /sd_spi/include/ff_stdio.h: -------------------------------------------------------------------------------- 1 | /* ff_stdio.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | // For compatibility with FreeRTOS+FAT API 15 | #include 16 | #include 17 | #include 18 | #include 19 | // 20 | #include "ff.h" 21 | // 22 | #include "my_debug.h" 23 | 24 | #define BaseType_t int 25 | #define FF_FILE FIL 26 | 27 | #define ff_rewind f_rewind 28 | #define pvPortMalloc malloc 29 | #define vPortFree free 30 | #define ffconfigMAX_FILENAME 250 31 | #define configASSERT myASSERT 32 | #define FF_PRINTF printf 33 | #define pdFREERTOS_ERRNO_NONE 0 34 | #define FF_EOF (-1) 35 | #define FF_SEEK_SET 0 36 | #define FF_SEEK_CUR 1 37 | #define FF_SEEK_END 2 38 | #define pdFALSE 0 39 | #define pdTRUE 1 40 | #define ff_filelength f_size 41 | #define ff_feof f_eof 42 | 43 | typedef struct FF_STAT { 44 | uint32_t st_size; /* Size of the object in number of bytes. */ 45 | // uint16_t st_mode; /* The mode (attribute bits) of this 46 | // file or directory. */ 47 | } FF_Stat_t; 48 | 49 | typedef struct { 50 | DIR dir; 51 | FILINFO fileinfo; 52 | const char *pcFileName; 53 | uint32_t ulFileSize; 54 | //uint8_t ucAttributes; 55 | } FF_FindData_t; 56 | 57 | FF_FILE *ff_fopen(const char *pcFile, const char *pcMode); 58 | int ff_fclose(FF_FILE *pxStream); 59 | int ff_stat(const char *pcFileName, FF_Stat_t *pxStatBuffer); 60 | size_t ff_fwrite(const void *pvBuffer, size_t xSize, size_t xItems, 61 | FF_FILE *pxStream); 62 | size_t ff_fread(void *pvBuffer, size_t xSize, size_t xItems, FF_FILE *pxStream); 63 | int ff_chdir(const char *pcDirectoryName); 64 | char *ff_getcwd(char *pcBuffer, size_t xBufferLength); 65 | int ff_mkdir(const char *pcPath); 66 | int ff_fputc(int iChar, FF_FILE *pxStream); 67 | int ff_fgetc(FF_FILE *pxStream); 68 | int ff_rmdir(const char *pcDirectory); 69 | int ff_remove(const char *pcPath); 70 | long ff_ftell(FF_FILE *pxStream); 71 | int ff_fseek(FF_FILE *pxStream, int iOffset, int iWhence); 72 | int ff_findfirst(const char *pcDirectory, FF_FindData_t *pxFindData); 73 | int ff_findnext( FF_FindData_t *pxFindData ); 74 | FF_FILE *ff_truncate( const char * pcFileName, long lTruncateSize ); 75 | int ff_seteof( FF_FILE *pxStream ); 76 | int ff_rename( const char *pcOldName, const char *pcNewName, int bDeleteIfExists ); 77 | char *ff_fgets(char *pcBuffer, size_t xCount, FF_FILE *pxStream); 78 | -------------------------------------------------------------------------------- /fatfs/source/diskio.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level disk interface modlue include file (C)ChaN, 2019 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /* Status of Disk Functions */ 13 | typedef BYTE DSTATUS; 14 | 15 | /* Results of Disk Functions */ 16 | typedef enum { 17 | RES_OK = 0, /* 0: Successful */ 18 | RES_ERROR, /* 1: R/W Error */ 19 | RES_WRPRT, /* 2: Write Protected */ 20 | RES_NOTRDY, /* 3: Not Ready */ 21 | RES_PARERR /* 4: Invalid Parameter */ 22 | } DRESULT; 23 | 24 | 25 | /*---------------------------------------*/ 26 | /* Prototypes for disk control functions */ 27 | 28 | 29 | DSTATUS disk_initialize (BYTE pdrv); 30 | DSTATUS disk_status (BYTE pdrv); 31 | DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); 32 | DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count); 33 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); 34 | 35 | 36 | /* Disk Status Bits (DSTATUS) */ 37 | 38 | #define STA_NOINIT 0x01 /* Drive not initialized */ 39 | #define STA_NODISK 0x02 /* No medium in the drive */ 40 | #define STA_PROTECT 0x04 /* Write protected */ 41 | 42 | 43 | /* Command code for disk_ioctrl fucntion */ 44 | 45 | /* Generic command (Used by FatFs) */ 46 | #define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */ 47 | #define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */ 48 | #define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */ 49 | #define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */ 50 | #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ 51 | 52 | /* Generic command (Not used by FatFs) */ 53 | #define CTRL_POWER 5 /* Get/Set power status */ 54 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 55 | #define CTRL_EJECT 7 /* Eject media */ 56 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 57 | 58 | /* MMC/SDC specific ioctl command */ 59 | #define MMC_GET_TYPE 10 /* Get card type */ 60 | #define MMC_GET_CSD 11 /* Get CSD */ 61 | #define MMC_GET_CID 12 /* Get CID */ 62 | #define MMC_GET_OCR 13 /* Get OCR */ 63 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 64 | #define ISDIO_READ 55 /* Read data form SD iSDIO register */ 65 | #define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ 66 | #define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ 67 | 68 | /* ATA/CF specific ioctl command */ 69 | #define ATA_GET_REV 20 /* Get F/W revision */ 70 | #define ATA_GET_MODEL 21 /* Get model name */ 71 | #define ATA_GET_SN 22 /* Get serial number */ 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /sd_spi/sd_driver/spi.h: -------------------------------------------------------------------------------- 1 | /* spi.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | 15 | #pragma once 16 | 17 | #include 18 | // 19 | // Pico includes 20 | #include "hardware/dma.h" 21 | #include "hardware/gpio.h" 22 | #include "hardware/irq.h" 23 | #include "hardware/spi.h" 24 | #include "pico/mutex.h" 25 | #include "pico/sem.h" 26 | #include "pico/types.h" 27 | 28 | #define SPI_FILL_CHAR (0xFF) 29 | 30 | // "Class" representing SPIs 31 | typedef struct { 32 | // SPI HW 33 | spi_inst_t *hw_inst; 34 | uint miso_gpio; // SPI MISO GPIO number (not pin number) 35 | uint mosi_gpio; 36 | uint sck_gpio; 37 | uint baud_rate; 38 | uint DMA_IRQ_num; // DMA_IRQ_0 or DMA_IRQ_1 39 | 40 | // Drive strength levels for GPIO outputs. 41 | // enum gpio_drive_strength { GPIO_DRIVE_STRENGTH_2MA = 0, GPIO_DRIVE_STRENGTH_4MA = 1, GPIO_DRIVE_STRENGTH_8MA = 2, 42 | // GPIO_DRIVE_STRENGTH_12MA = 3 } 43 | bool set_drive_strength; 44 | enum gpio_drive_strength mosi_gpio_drive_strength; 45 | enum gpio_drive_strength sck_gpio_drive_strength; 46 | 47 | // State variables: 48 | uint tx_dma; 49 | uint rx_dma; 50 | dma_channel_config tx_dma_cfg; 51 | dma_channel_config rx_dma_cfg; 52 | irq_handler_t dma_isr; // Ignored: no longer used 53 | bool initialized; 54 | semaphore_t sem; 55 | mutex_t mutex; 56 | } spi_t; 57 | 58 | #ifdef __cplusplus 59 | extern "C" { 60 | #endif 61 | 62 | bool __not_in_flash_func(spi_transfer)(spi_t *pSPI, const uint8_t *tx, uint8_t *rx, size_t length); 63 | void spi_lock(spi_t *pSPI); 64 | void spi_unlock(spi_t *pSPI); 65 | bool my_spi_init(spi_t *pSPI); 66 | void set_spi_dma_irq_channel(bool useChannel1, bool shared); 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | /* 73 | This uses the Pico LED to show SD card activity. 74 | You can use it to get a rough idea of utilization. 75 | Warning: Pico W uses GPIO 25 for SPI communication to the CYW43439. 76 | 77 | You can enable this by putting something like 78 | add_compile_definitions(USE_LED=1) 79 | in CMakeLists.txt, for example. 80 | */ 81 | #if !defined(NO_PICO_LED) && defined(USE_LED) && USE_LED && defined(PICO_DEFAULT_LED_PIN) 82 | # define LED_INIT() \ 83 | { \ 84 | gpio_init(PICO_DEFAULT_LED_PIN); \ 85 | gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); \ 86 | } 87 | # define LED_ON() gpio_put(PICO_DEFAULT_LED_PIN, 1) 88 | # define LED_OFF() gpio_put(PICO_DEFAULT_LED_PIN, 0) 89 | #else 90 | # define LED_ON() 91 | # define LED_OFF() 92 | # define LED_INIT() 93 | #endif 94 | 95 | /* [] END OF FILE */ 96 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | # GIT_SUBMODULES_RECURSE was added in 3.17 33 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") 34 | FetchContent_Declare( 35 | pico_sdk 36 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 37 | GIT_TAG master 38 | GIT_SUBMODULES_RECURSE FALSE 39 | ) 40 | else () 41 | FetchContent_Declare( 42 | pico_sdk 43 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 44 | GIT_TAG master 45 | ) 46 | endif () 47 | 48 | if (NOT pico_sdk) 49 | message("Downloading Raspberry Pi Pico SDK") 50 | FetchContent_Populate(pico_sdk) 51 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 52 | endif () 53 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 54 | else () 55 | message(FATAL_ERROR 56 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 57 | ) 58 | endif () 59 | endif () 60 | 61 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 62 | if (NOT EXISTS ${PICO_SDK_PATH}) 63 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 64 | endif () 65 | 66 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 67 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 68 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 69 | endif () 70 | 71 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 72 | 73 | include(${PICO_SDK_INIT_CMAKE_FILE}) 74 | -------------------------------------------------------------------------------- /usb_diskio.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2019 Ha Thach (tinyusb.org) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "usb_diskio.h" 32 | 33 | // From https://github.com/hathach/tinyusb/ examples/host/msc_file_explorer/src/msc_app.c 34 | 35 | static volatile bool disk_busy; 36 | 37 | static void wait_for_disk_io(BYTE pdrv) { 38 | while(disk_busy) { 39 | tuh_task(); 40 | } 41 | } 42 | 43 | static bool disk_io_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data) { 44 | disk_busy = false; 45 | return true; 46 | } 47 | 48 | DSTATUS usb_disk_status( 49 | BYTE pdrv // Physical drive number to identify the drive 50 | ) { 51 | return tuh_msc_mounted(pdrv) ? 0 : STA_NODISK; 52 | } 53 | 54 | DSTATUS usb_disk_initialize( 55 | BYTE pdrv // Physical drive number to identify the drive 56 | ) { 57 | return 0; // Nothing to do 58 | } 59 | 60 | DRESULT usb_disk_read( 61 | BYTE pdrv, // Physical drive number to identify the drive 62 | BYTE *buff, // Data buffer to store read data 63 | LBA_t sector, // Start sector in LBA 64 | UINT count // Number of sectors to read 65 | ) { 66 | disk_busy = true; 67 | 68 | tuh_msc_read10(pdrv, 0, buff, sector, count, disk_io_complete, 0); 69 | wait_for_disk_io(pdrv); 70 | 71 | return RES_OK; 72 | } 73 | 74 | #if FF_FS_READONLY == 0 75 | 76 | DRESULT usb_disk_write( 77 | BYTE pdrv, // Physical drive number to identify the drive 78 | const BYTE *buff, // Data to be written 79 | LBA_t sector, // Start sector in LBA 80 | UINT count // Number of sectors to write 81 | ) { 82 | disk_busy = true; 83 | 84 | tuh_msc_write10(pdrv, 0, buff, sector, count, disk_io_complete, 0); 85 | wait_for_disk_io(pdrv); 86 | 87 | return RES_OK; 88 | } 89 | 90 | #endif 91 | 92 | DRESULT usb_disk_ioctl( 93 | BYTE pdrv, // Physical drive number to identify the drive 94 | BYTE cmd, // Control code 95 | void *buff // Buffer to send/receive control data 96 | ) { 97 | switch (cmd) 98 | { 99 | case CTRL_SYNC: 100 | // Nothing to do since we do block 101 | return RES_OK; 102 | 103 | case GET_SECTOR_COUNT: 104 | *(DWORD*)buff = tuh_msc_get_block_count(pdrv, 0); 105 | return RES_OK; 106 | 107 | case GET_SECTOR_SIZE: 108 | *(WORD*)buff = tuh_msc_get_block_size(pdrv, 0); 109 | return RES_OK; 110 | 111 | case GET_BLOCK_SIZE: 112 | *(DWORD*)buff = 1; // Erase block size in units of sector size 113 | return RES_OK; 114 | 115 | default: 116 | return RES_PARERR; 117 | } 118 | 119 | return RES_OK; 120 | } 121 | -------------------------------------------------------------------------------- /msc_device.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2024 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "config.h" 32 | #include "hdd.h" 33 | 34 | int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { 35 | // printf("MSC Read(LBA=$%08X)\n", lba); 36 | 37 | if (offset || bufsize % FF_MAX_SS) { 38 | printf("read param error\n"); 39 | return -1; 40 | } 41 | 42 | if (disk_read(0, buffer, lba, bufsize / FF_MAX_SS) != RES_OK) { 43 | printf("disk_read() error\n"); 44 | return -1; 45 | } 46 | 47 | return bufsize; 48 | } 49 | 50 | int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { 51 | // printf("MSC Write(LBA=$%08X)\n", lba); 52 | 53 | if (offset || bufsize % FF_MAX_SS) { 54 | printf("write param error\n"); 55 | return -1; 56 | } 57 | 58 | // Avoid inconsistency in local FAT implementation 59 | hdd_reset(); 60 | config_reset(); 61 | 62 | if (disk_write(0, buffer, lba, bufsize / FF_MAX_SS) != RES_OK) { 63 | printf("disk_write() error\n"); 64 | return -1; 65 | } 66 | 67 | return bufsize; 68 | } 69 | 70 | void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { 71 | printf("MSC Inquiry\n"); 72 | 73 | const char vid[] = "A2retro"; 74 | const char pid[] = "A2retroNET"; 75 | const char rev[] = "1.0"; 76 | 77 | memcpy(vendor_id, vid, strlen(vid)); 78 | memcpy(product_id, pid, strlen(pid)); 79 | memcpy(product_rev, rev, strlen(rev)); 80 | } 81 | 82 | bool tud_msc_test_unit_ready_cb(uint8_t lun) { 83 | // printf("MSC Ready\n"); 84 | return true; 85 | } 86 | 87 | void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) { 88 | printf("MSC Capacity\n"); 89 | 90 | *block_size = FF_MAX_SS; 91 | 92 | LBA_t sector_count; 93 | if (disk_ioctl(0, GET_SECTOR_COUNT, §or_count) != RES_OK) { 94 | printf("disk_ioctl() error\n"); 95 | *block_count = 0; 96 | return; 97 | } 98 | 99 | *block_count = sector_count; 100 | } 101 | 102 | int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { 103 | if (scsi_cmd[0] == SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL) { 104 | 105 | printf("MSC Medium\n"); 106 | 107 | // Host is about to read/write etc... better not to disconnect disk 108 | return 0; 109 | } 110 | 111 | printf("MSC Other\n"); 112 | 113 | // Set Sense = Invalid Command Operation 114 | tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); 115 | 116 | // Negative means error -> TinyUSB could stall and/or response with failed status 117 | return -1; 118 | } 119 | -------------------------------------------------------------------------------- /6502/SSC.CF00.S: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ; ; 3 | ; APPLE II SSC FIRMWARE ; 4 | ; ; 5 | ; BY LARRY KENYON ; 6 | ; ; 7 | ; -JANUARY 1981- ;;;;;;;;;;;;;; 8 | ; ; 9 | ; (C) COPYRIGHT 1981 BY APPLE COMPUTER, INC. ; 10 | ; ; 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ; ; 13 | ; CF00 SPACE ; 14 | ; ; 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | 17 | ORG $CF00 18 | 19 | ;;;;;;;; 20 | ; BOOT ; 21 | ;;;;;;;; 22 | BOOT LDA #$01 ; SMARTPORT FIRMWARE 23 | STA BANKSET 24 | JMP BOOT1 25 | 26 | PDBOOT JSR PRODOS 27 | LDA #$01 ; SMARTPORT FIRMWARE 28 | STA BANKSET 29 | JMP BOOT2 30 | 31 | ;;;;;;;;;;;;;;;;;; 32 | ; COMMON HANDLER ; 33 | ;;;;;;;;;;;;;;;;;; 34 | COMMON LDA #$01 ; SMARTPORT FIRMWARE 35 | STA BANKSET 36 | JMP SWITCH 37 | 38 | ;;;;;;;;;;;;;;; 39 | ; COMMON QUIT ; 40 | ;;;;;;;;;;;;;;; 41 | QUIT STA BANKCLR 42 | RTS 43 | 44 | ;;;;;;;;;;;;;;;;;;;;;;; 45 | ; READ 512-BYTE BLOCK ; 46 | ;;;;;;;;;;;;;;;;;;;;;;; 47 | 48 | RDBLOCK LDA #$02 ; READ 1. PAGE CODE 49 | STA BANKSET 50 | JSR $C800 51 | LDA #$03 ; READ 2. PAGE CODE 52 | STA BANKSET 53 | JSR $C800 54 | LDA #$01 ; SMARTPORT FIRMWARE 55 | STA BANKSET 56 | RTS 57 | 58 | ;;;;;;;;;; 59 | ; CONFIG ; 60 | ;;;;;;;;;; 61 | CFG LDA #$01 ; SMARTPORT FIRMWARE 62 | STA BANKSET 63 | JSR CFGHDLR 64 | STA BANKCLR 65 | RTS 66 | 67 | ;;;;;;;;;;;;;;;;;;;;;;;; 68 | ; BASIC INITIALIZATION ; 69 | ;;;;;;;;;;;;;;;;;;;;;;;; 70 | BINIT STA BENTER ; ENTER A2RETRONET BASIC MODE 71 | BVC NORMIO 72 | ASL CMDBYTE,X ; ALWAYS ENABLE COMMANDS 73 | LSR CMDBYTE,X 74 | LDA CMDREG,Y ; JUST HAD A POWER-ON OR PROGRAM RESET? 75 | AND #$1F 76 | BNE BINIT1 77 | LDA #$EF ; IF SO, GO JOIN INIT IN PROGRESS 78 | JSR INIT1 79 | 80 | BINIT1 CPX CSWH 81 | BNE FROMIN 82 | LDA # 88 | FROMIN CPX KSWH ; MAKE SURE KSW POINTS HERE 89 | BNE FROMOUT ; 90 | LDA # FF_FS_LOCK"; 58 | case FR_INVALID_PARAMETER: 59 | return "Given parameter is invalid"; 60 | default: 61 | return "Unknown"; 62 | } 63 | } 64 | 65 | FRESULT delete_node ( 66 | TCHAR* path, /* Path name buffer with the sub-directory to delete */ 67 | UINT sz_buff, /* Size of path name buffer (items) */ 68 | FILINFO* fno /* Name read buffer */ 69 | ) 70 | { 71 | UINT i, j; 72 | FRESULT fr; 73 | DIR dir; 74 | 75 | 76 | fr = f_opendir(&dir, path); /* Open the sub-directory to make it empty */ 77 | if (fr != FR_OK) return fr; 78 | 79 | for (i = 0; path[i]; i++) ; /* Get current path length */ 80 | path[i++] = '/'; 81 | 82 | for (;;) { 83 | fr = f_readdir(&dir, fno); /* Get a directory item */ 84 | if (fr != FR_OK || !fno->fname[0]) break; /* End of directory? */ 85 | j = 0; 86 | do { /* Make a path name */ 87 | if (i + j >= sz_buff) { /* Buffer over flow? */ 88 | fr = 100; break; /* Fails with 100 when buffer overflow */ 89 | } 90 | path[i + j] = fno->fname[j]; 91 | } while (fno->fname[j++]); 92 | if (fno->fattrib & AM_DIR) { /* Item is a sub-directory */ 93 | fr = delete_node(path, sz_buff, fno); 94 | } else { /* Item is a file */ 95 | fr = f_unlink(path); 96 | } 97 | if (fr != FR_OK) break; 98 | } 99 | 100 | path[--i] = 0; /* Restore the path name */ 101 | f_closedir(&dir); 102 | 103 | if (fr == FR_OK) fr = f_unlink(path); /* Delete the empty sub-directory */ 104 | return fr; 105 | } 106 | -------------------------------------------------------------------------------- /sd_spi/src/rtc.c: -------------------------------------------------------------------------------- 1 | /* rtc.c 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | #include 15 | #include 16 | // 17 | #include "hardware/rtc.h" 18 | #include "pico/stdio.h" 19 | #include "pico/stdlib.h" 20 | #include "pico/util/datetime.h" 21 | // 22 | #include "ff.h" 23 | #include "util.h" // calculate_checksum 24 | // 25 | #include "rtc.h" 26 | 27 | static time_t epochtime; 28 | 29 | // Make an attempt to save a recent time stamp across reset: 30 | typedef struct rtc_save { 31 | uint32_t signature; 32 | datetime_t datetime; 33 | uint32_t checksum; // last, not included in checksum 34 | } rtc_save_t; 35 | static rtc_save_t rtc_save __attribute__((section(".uninitialized_data"))); 36 | 37 | static void update_epochtime() { 38 | bool rc = rtc_get_datetime(&rtc_save.datetime); 39 | if (rc) { 40 | rtc_save.signature = 0xBABEBABE; 41 | struct tm timeinfo = { 42 | .tm_sec = rtc_save.datetime 43 | .sec, /* Seconds. [0-60] (1 leap second) */ 44 | .tm_min = rtc_save.datetime.min, /* Minutes. [0-59] */ 45 | .tm_hour = rtc_save.datetime.hour, /* Hours. [0-23] */ 46 | .tm_mday = rtc_save.datetime.day, /* Day. [1-31] */ 47 | .tm_mon = rtc_save.datetime.month - 1, /* Month. [0-11] */ 48 | .tm_year = rtc_save.datetime.year - 1900, /* Year - 1900. */ 49 | .tm_wday = 0, /* Day of week. [0-6] */ 50 | .tm_yday = 0, /* Days in year.[0-365] */ 51 | .tm_isdst = -1 /* DST. [-1/0/1]*/ 52 | }; 53 | rtc_save.checksum = calculate_checksum((uint32_t *)&rtc_save, 54 | offsetof(rtc_save_t, checksum)); 55 | epochtime = mktime(&timeinfo); 56 | rtc_save.datetime.dotw = timeinfo.tm_wday; 57 | // configASSERT(-1 != epochtime); 58 | } 59 | } 60 | 61 | time_t time(time_t *pxTime) { 62 | update_epochtime(); 63 | if (pxTime) { 64 | *pxTime = epochtime; 65 | } 66 | return epochtime; 67 | } 68 | 69 | void time_init() { 70 | rtc_init(); 71 | datetime_t t = {0, 0, 0, 0, 0, 0, 0}; 72 | rtc_get_datetime(&t); 73 | if (!t.year && rtc_save.datetime.year) { 74 | uint32_t xor_checksum = calculate_checksum( 75 | (uint32_t *)&rtc_save, offsetof(rtc_save_t, checksum)); 76 | if (rtc_save.signature == 0xBABEBABE && 77 | rtc_save.checksum == xor_checksum) { 78 | // Set rtc 79 | rtc_set_datetime(&rtc_save.datetime); 80 | } 81 | } 82 | } 83 | 84 | // Called by FatFs: 85 | DWORD get_fattime(void) { 86 | datetime_t t = {0, 0, 0, 0, 0, 0, 0}; 87 | bool rc = rtc_get_datetime(&t); 88 | if (!rc) return 0; 89 | 90 | DWORD fattime = 0; 91 | // bit31:25 92 | // Year origin from the 1980 (0..127, e.g. 37 for 2017) 93 | uint8_t yr = t.year - 1980; 94 | fattime |= (0b01111111 & yr) << 25; 95 | // bit24:21 96 | // Month (1..12) 97 | uint8_t mo = t.month; 98 | fattime |= (0b00001111 & mo) << 21; 99 | // bit20:16 100 | // Day of the month (1..31) 101 | uint8_t da = t.day; 102 | fattime |= (0b00011111 & da) << 16; 103 | // bit15:11 104 | // Hour (0..23) 105 | uint8_t hr = t.hour; 106 | fattime |= (0b00011111 & hr) << 11; 107 | // bit10:5 108 | // Minute (0..59) 109 | uint8_t mi = t.min; 110 | fattime |= (0b00111111 & mi) << 5; 111 | // bit4:0 112 | // Second / 2 (0..29, e.g. 25 for 50) 113 | uint8_t sd = t.sec / 2; 114 | fattime |= (0b00011111 & sd); 115 | return fattime; 116 | } 117 | -------------------------------------------------------------------------------- /sd_spi/sd_driver/sd_card.h: -------------------------------------------------------------------------------- 1 | /* sd_card.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | 15 | // Note: The model used here is one FatFS per SD card. 16 | // Multiple partitions on a card are not supported. 17 | 18 | #pragma once 19 | 20 | #include 21 | // 22 | #include "hardware/gpio.h" 23 | #include "pico/mutex.h" 24 | // 25 | #include "ff.h" 26 | // 27 | #include "spi.h" 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | typedef struct sd_card_t sd_card_t; 34 | 35 | // "Class" representing SD Cards 36 | struct sd_card_t { 37 | const char *pcName; 38 | spi_t *spi; 39 | // Slave select is here instead of in spi_t because multiple SDs can share an SPI. 40 | uint ss_gpio; // Slave select for this SD card 41 | bool use_card_detect; 42 | uint card_detect_gpio; // Card detect; ignored if !use_card_detect 43 | uint card_detected_true; // Varies with card socket; ignored if !use_card_detect 44 | // Drive strength levels for GPIO outputs. 45 | // enum gpio_drive_strength { GPIO_DRIVE_STRENGTH_2MA = 0, GPIO_DRIVE_STRENGTH_4MA = 1, GPIO_DRIVE_STRENGTH_8MA = 2, 46 | // GPIO_DRIVE_STRENGTH_12MA = 3 } 47 | bool set_drive_strength; 48 | enum gpio_drive_strength ss_gpio_drive_strength; 49 | 50 | // Following fields are used to keep track of the state of the card: 51 | int m_Status; // Card status 52 | uint64_t sectors; // Assigned dynamically 53 | int card_type; // Assigned dynamically 54 | mutex_t mutex; 55 | FATFS fatfs; 56 | bool mounted; 57 | 58 | int (*init)(sd_card_t *sd_card_p); 59 | int (*write_blocks)(sd_card_t *sd_card_p, const uint8_t *buffer, 60 | uint64_t ulSectorNumber, uint32_t blockCnt); 61 | int (*read_blocks)(sd_card_t *sd_card_p, uint8_t *buffer, uint64_t ulSectorNumber, 62 | uint32_t ulSectorCount); 63 | 64 | // Useful when use_card_detect is false - call periodically to check for presence of SD card 65 | // Returns true if and only if SD card was sensed on the bus 66 | bool (*sd_test_com)(sd_card_t *sd_card_p); 67 | }; 68 | 69 | #define SD_BLOCK_DEVICE_ERROR_NONE 0 70 | #define SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK -5001 /*!< operation would block */ 71 | #define SD_BLOCK_DEVICE_ERROR_UNSUPPORTED -5002 /*!< unsupported operation */ 72 | #define SD_BLOCK_DEVICE_ERROR_PARAMETER -5003 /*!< invalid parameter */ 73 | #define SD_BLOCK_DEVICE_ERROR_NO_INIT -5004 /*!< uninitialized */ 74 | #define SD_BLOCK_DEVICE_ERROR_NO_DEVICE -5005 /*!< device is missing or not connected */ 75 | #define SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED -5006 /*!< write protected */ 76 | #define SD_BLOCK_DEVICE_ERROR_UNUSABLE -5007 /*!< unusable card */ 77 | #define SD_BLOCK_DEVICE_ERROR_NO_RESPONSE -5008 /*!< No response from device */ 78 | #define SD_BLOCK_DEVICE_ERROR_CRC -5009 /*!< CRC error */ 79 | #define SD_BLOCK_DEVICE_ERROR_ERASE -5010 /*!< Erase error: reset/sequence */ 80 | #define SD_BLOCK_DEVICE_ERROR_WRITE -5011 /*!< SPI Write error: !SPI_DATA_ACCEPTED */ 81 | 82 | ///* Disk Status Bits (DSTATUS) */ 83 | // See diskio.h. 84 | //enum { 85 | // STA_NOINIT = 0x01, /* Drive not initialized */ 86 | // STA_NODISK = 0x02, /* No medium in the drive */ 87 | // STA_PROTECT = 0x04 /* Write protected */ 88 | //}; 89 | 90 | bool sd_card_detect(sd_card_t *pSD); 91 | uint64_t sd_sectors(sd_card_t *pSD); 92 | 93 | bool sd_init_driver(); 94 | bool sd_card_detect(sd_card_t *sd_card_p); 95 | 96 | #ifdef __cplusplus 97 | } 98 | #endif 99 | 100 | /* [] END OF FILE */ 101 | -------------------------------------------------------------------------------- /sd_spi/sd_driver/sd_spi.c: -------------------------------------------------------------------------------- 1 | /* sd_spi.c 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | 15 | /* Standard includes. */ 16 | #include 17 | #include 18 | #include 19 | // 20 | #include "hardware/gpio.h" 21 | // 22 | #include "my_debug.h" 23 | #include "sd_card.h" 24 | #include "sd_spi.h" 25 | #include "spi.h" 26 | 27 | #define TRACE_PRINTF(fmt, args...) 28 | /* #define TRACE_PRINTF printf // task_printf */ 29 | 30 | #pragma GCC diagnostic push 31 | #pragma GCC diagnostic ignored "-Wunused-variable" 32 | 33 | void sd_spi_go_high_frequency(sd_card_t *pSD) { 34 | uint actual = spi_set_baudrate(pSD->spi->hw_inst, pSD->spi->baud_rate); 35 | TRACE_PRINTF("%s: Actual frequency: %lu\n", __FUNCTION__, (long)actual); 36 | } 37 | void sd_spi_go_low_frequency(sd_card_t *pSD) { 38 | uint actual = spi_set_baudrate(pSD->spi->hw_inst, 400 * 1000); // Actual frequency: 398089 39 | TRACE_PRINTF("%s: Actual frequency: %lu\n", __FUNCTION__, (long)actual); 40 | } 41 | 42 | #pragma GCC diagnostic pop 43 | 44 | static void sd_spi_lock(sd_card_t *pSD) { 45 | spi_lock(pSD->spi); 46 | } 47 | static void sd_spi_unlock(sd_card_t *pSD) { 48 | spi_unlock(pSD->spi); 49 | } 50 | 51 | // Would do nothing if pSD->ss_gpio were set to GPIO_FUNC_SPI. 52 | static void sd_spi_select(sd_card_t *pSD) { 53 | gpio_put(pSD->ss_gpio, 0); 54 | // A fill byte seems to be necessary, sometimes: 55 | uint8_t fill = SPI_FILL_CHAR; 56 | spi_write_blocking(pSD->spi->hw_inst, &fill, 1); 57 | LED_ON(); 58 | } 59 | 60 | static void sd_spi_deselect(sd_card_t *pSD) { 61 | gpio_put(pSD->ss_gpio, 1); 62 | LED_OFF(); 63 | /* 64 | MMC/SDC enables/disables the DO output in synchronising to the SCLK. This 65 | means there is a posibility of bus conflict with MMC/SDC and another SPI 66 | slave that shares an SPI bus. Therefore to make MMC/SDC release the MISO 67 | line, the master device needs to send a byte after the CS signal is 68 | deasserted. 69 | */ 70 | uint8_t fill = SPI_FILL_CHAR; 71 | spi_write_blocking(pSD->spi->hw_inst, &fill, 1); 72 | } 73 | /* Some SD cards want to be deselected between every bus transaction */ 74 | void sd_spi_deselect_pulse(sd_card_t *pSD) { 75 | sd_spi_deselect(pSD); 76 | // tCSH Pulse duration, CS high 200 ns 77 | sd_spi_select(pSD); 78 | } 79 | void sd_spi_acquire(sd_card_t *pSD) { 80 | sd_spi_lock(pSD); 81 | sd_spi_select(pSD); 82 | } 83 | 84 | void sd_spi_release(sd_card_t *pSD) { 85 | sd_spi_deselect(pSD); 86 | sd_spi_unlock(pSD); 87 | } 88 | 89 | bool sd_spi_transfer(sd_card_t *pSD, const uint8_t *tx, uint8_t *rx, 90 | size_t length) { 91 | return spi_transfer(pSD->spi, tx, rx, length); 92 | } 93 | 94 | uint8_t sd_spi_write(sd_card_t *pSD, const uint8_t value) { 95 | // TRACE_PRINTF("%s\n", __FUNCTION__); 96 | uint8_t received = SPI_FILL_CHAR; 97 | #if 0 98 | int num = spi_write_read_blocking(pSD->spi->hw_inst, &value, &received, 1); 99 | myASSERT(1 == num); 100 | #else 101 | bool success = spi_transfer(pSD->spi, &value, &received, 1); 102 | myASSERT(success); 103 | #endif 104 | return received; 105 | } 106 | 107 | void sd_spi_send_initializing_sequence(sd_card_t * pSD) { 108 | bool old_ss = gpio_get(pSD->ss_gpio); 109 | // Set DI and CS high and apply 74 or more clock pulses to SCLK: 110 | gpio_put(pSD->ss_gpio, 1); 111 | uint8_t ones[10]; 112 | memset(ones, 0xFF, sizeof ones); 113 | absolute_time_t timeout_time = make_timeout_time_ms(1); 114 | do { 115 | sd_spi_transfer(pSD, ones, NULL, sizeof ones); 116 | } while (0 < absolute_time_diff_us(get_absolute_time(), timeout_time)); 117 | gpio_put(pSD->ss_gpio, old_ss); 118 | } 119 | 120 | void sd_spi_init_pl022(sd_card_t *pSD) { 121 | // Let the PL022 SPI handle it. 122 | // the CS line is brought high between each byte during transmission. 123 | gpio_set_function(pSD->ss_gpio, GPIO_FUNC_SPI); 124 | } 125 | 126 | /* [] END OF FILE */ 127 | -------------------------------------------------------------------------------- /6502/SSC.UTIL.S: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ; ; 3 | ; APPLE II SSC FIRMWARE ; 4 | ; ; 5 | ; BY LARRY KENYON ; 6 | ; ; 7 | ; -JANUARY 1981- ;;;;;;;;;;;;;; 8 | ; ; 9 | ; (C) COPYRIGHT 1981 BY APPLE COMPUTER, INC. ; 10 | ; ; 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ; ; 13 | ; UTILITY ROUTINES ; 14 | ; ; 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | ; PASCAL-BASIC KEYBOARD FETCH ; 17 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 18 | CKKBD CLC ; RETURN CARRY CLEAR FOR NO DATA 19 | LDA MISCFLG,X 20 | AND #$04 ; ANSWER NO IF KEYBOARD IS DISABLED 21 | BEQ CKKBDXIT 22 | 23 | CKKBD1 LDA KBD 24 | BPL CKKBDXIT 25 | STA KBDSTRB 26 | SEC ; INDICATE DATA 27 | CKKBDXIT RTS 28 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 29 | ; GET A CHAR FROM KEYBOARD FOR BASIC ONLY ; 30 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 31 | GETKBD INC RNDL ; MIX UP RANDOM # SEED 32 | BNE GETKBD1 ; FOR BASIC 33 | INC RNDH 34 | GETKBD1 JSR CKKBD ; KEYBOARD FETCH ROUTINE 35 | CLV ; INDICATE NO ESCAPE SEQUENCE 36 | BCC CKKBDXIT ; EXIT IF NO KEY PRESS 37 | JSR RESTORE ; DO BASIC CURSED DUTY 38 | AND #$7F 39 | CMP CMDBYTE,X ; IS IT THE START OF A COMMAND? 40 | BNE GETKBDONE ; IF NOT, EXIT INDICATING DATA 41 | LDY SLOT16 42 | LDA DIPSW1,Y ; ONLY DO CMD ESC FOR PPC, SIC MODES 43 | LSR A 44 | BCS GETKBDONE 45 | ;;;;;;;;;;;;;;;;;;;;;;;;;;; 46 | ; KEYBOARD ESCAPE HANDLER ; 47 | ;;;;;;;;;;;;;;;;;;;;;;;;;;; 48 | KBDESC LDY #$A ; FIRST PRINT A PROMPT 49 | PROMPTLOOP LDA PROMPTBL,Y 50 | STA CHARACTER 51 | TYA 52 | PHA 53 | JSR SCREENOUT1 ; ALWAYS SEND TO SCREEN 54 | PLA 55 | TAY 56 | DEY 57 | BPL PROMPTLOOP 58 | 59 | LDA #1 ; START OUT IN COMMAND STATE 1 60 | JSR SETOSTATE 61 | 62 | GETCMD JSR CKKBD1 ; WAIT FOR KEYBOARD CHARACTER 63 | BPL GETCMD 64 | CMP #$88 ; BACKSPACE? 65 | BEQ KBDESC ; IF SO, THEN START OVER 66 | STA CHARACTER 67 | 68 | JSR SCREENOUT1 69 | JSR CMDSEQCK ; PUMP THRU CMD INTERPRETER 70 | 71 | LDA STATEFLG,X ; ARE WE DONE? 72 | AND #$07 73 | BNE GETCMD ; IF NOT, GO AGAIN 74 | 75 | LDA #$8D ; FORCE BACK A CARRIAGE RETURN 76 | STA CHARACTER 77 | BIT IORTS ; INDICATE THAT A CMD SEQ HAS OCCURRED 78 | GETKBDONE SEC ; INDICATE SUCCESS 79 | RTS 80 | 81 | PROMPTBL ASC ":CSS ELPPA" 82 | DFB $8D 83 | 84 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 85 | ; ROUTINE TO PRINT A CHARACTER ON THE CURRENT DISPLAY ; 86 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 87 | SCREENOUT LDA MISCFLG,X 88 | BPL NOOUT ; IF SCREEN DISABLED 89 | 90 | SCREENOUT1 LDA MISCFLG,X ; ENTRY AFTER ECHO CHECK 91 | AND #$02 ; IF IT ISN'T CIC MODE, 92 | BEQ ASCREEN ; ALWAYS USE THE APPLE SCREEN 93 | LDA STATEFLG,X ; CURRENT SCREEN = APPLE SCREEN? 94 | AND #$38 95 | BEQ ASCREEN ; SLOT O= APPLE SCREEN 96 | 97 | TXA ; JUMP TO CN00 SPACE 98 | PHA 99 | LDA #Z? 117 | BCS TOSCREEN 118 | EOR UCMASK,Y 119 | BCC TOSCREEN ; 120 | 121 | ; MASKS FOR CASE TRANSLATION 122 | LCMASK DFB $20,$00,$E0,$20 123 | UCMASK DFB $00,$00,$00,$C0 124 | REVMASK DFB $00,$00,$E0,$C0 125 | 126 | GETXLATE LDA DELAYFLG,X ; TRANSLATE OPTIONS IN B6-B7 127 | ROL A 128 | ROL A 129 | ROL A 130 | AND #$03 131 | TAY 132 | LDA CHARACTER 133 | RTS 134 | -------------------------------------------------------------------------------- /diskio.c: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------*/ 2 | /* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */ 3 | /*-----------------------------------------------------------------------*/ 4 | /* If a working storage control module is available, it should be */ 5 | /* attached to the FatFs via a glue function rather than modifying it. */ 6 | /* This is an example of glue functions to attach various exsisting */ 7 | /* storage control modules to the FatFs module with a defined API. */ 8 | /*-----------------------------------------------------------------------*/ 9 | 10 | #include // Obtains integer types 11 | #include // Declarations of disk functions 12 | #include // Declarations of SD card functions 13 | #include "usb_diskio.h" // Declarations of USB MSD functions 14 | 15 | // Definitions of physical drive number for each drive 16 | #define DEV_SD 0 // Map MMC/SD card to physical drive 0 17 | #define DEV_USB 1 // Map USB MSD to physical drive 1 18 | 19 | 20 | /*-----------------------------------------------------------------------*/ 21 | /* Get Drive Status */ 22 | /*-----------------------------------------------------------------------*/ 23 | 24 | DSTATUS disk_status( 25 | BYTE pdrv // Physical drive number to identify the drive 26 | ) { 27 | switch (pdrv) { 28 | case DEV_SD: 29 | return sd_disk_status(DEV_SD); 30 | 31 | #if MEDIUM == USB 32 | case DEV_USB: 33 | return usb_disk_status(DEV_USB); 34 | #endif 35 | 36 | default: 37 | return STA_NOINIT; 38 | } 39 | } 40 | 41 | 42 | /*-----------------------------------------------------------------------*/ 43 | /* Inidialize a Drive */ 44 | /*-----------------------------------------------------------------------*/ 45 | 46 | DSTATUS disk_initialize( 47 | BYTE pdrv // Physical drive number to identify the drive 48 | ) { 49 | switch (pdrv) { 50 | case DEV_SD: 51 | return sd_disk_initialize(DEV_SD); 52 | 53 | #if MEDIUM == USB 54 | case DEV_USB: 55 | return usb_disk_initialize(DEV_USB); 56 | #endif 57 | 58 | default: 59 | return STA_NOINIT; 60 | } 61 | } 62 | 63 | 64 | /*-----------------------------------------------------------------------*/ 65 | /* Read Sector(s) */ 66 | /*-----------------------------------------------------------------------*/ 67 | 68 | DRESULT disk_read( 69 | BYTE pdrv, // Physical drive number to identify the drive 70 | BYTE *buff, // Data buffer to store read data 71 | LBA_t sector, // Start sector in LBA 72 | UINT count // Number of sectors to read 73 | ) { 74 | switch (pdrv) { 75 | case DEV_SD: 76 | return sd_disk_read(DEV_SD, buff, sector, count); 77 | 78 | #if MEDIUM == USB 79 | case DEV_USB: 80 | return usb_disk_read(DEV_USB, buff, sector, count); 81 | #endif 82 | 83 | default: 84 | return RES_PARERR; 85 | } 86 | } 87 | 88 | 89 | /*-----------------------------------------------------------------------*/ 90 | /* Write Sector(s) */ 91 | /*-----------------------------------------------------------------------*/ 92 | 93 | #if FF_FS_READONLY == 0 94 | 95 | DRESULT disk_write( 96 | BYTE pdrv, // Physical drive number to identify the drive 97 | const BYTE *buff, // Data to be written 98 | LBA_t sector, // Start sector in LBA 99 | UINT count // Number of sectors to write 100 | ) { 101 | switch (pdrv) { 102 | case DEV_SD: 103 | return sd_disk_write(DEV_SD, buff, sector, count); 104 | 105 | #if MEDIUM == USB 106 | case DEV_USB: 107 | return usb_disk_write(DEV_USB, buff, sector, count); 108 | #endif 109 | 110 | default: 111 | return RES_PARERR; 112 | } 113 | } 114 | 115 | #endif 116 | 117 | 118 | /*-----------------------------------------------------------------------*/ 119 | /* Miscellaneous Functions */ 120 | /*-----------------------------------------------------------------------*/ 121 | 122 | DRESULT disk_ioctl( 123 | BYTE pdrv, // Physical drive number to identify the drive 124 | BYTE cmd, // Control code 125 | void *buff // Buffer to send/receive control data 126 | ) { 127 | switch (pdrv) { 128 | case DEV_SD: 129 | return sd_disk_ioctl(DEV_SD, cmd, buff); 130 | 131 | #if MEDIUM == USB 132 | case DEV_USB: 133 | return usb_disk_ioctl(DEV_USB, cmd, buff); 134 | #endif 135 | 136 | default: 137 | return RES_PARERR; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /6502/SSC.C800.S: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ; ; 3 | ; APPLE II SSC FIRMWARE ; 4 | ; ; 5 | ; BY LARRY KENYON ; 6 | ; ; 7 | ; -JANUARY 1981- ;;;;;;;;;;;;;; 8 | ; ; 9 | ; (C) COPYRIGHT 1981 BY APPLE COMPUTER, INC. ; 10 | ; ; 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ; ; 13 | ; C800 SPACE: HIGH LEVEL STUFF ; 14 | ; ; 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | ; PASCAL 1.0 INIT ENTRY ; 17 | ;;;;;;;;;;;;;;;;;;;;;;;;; 18 | 19 | ORG $C800 20 | 21 | PASCALINIT JSR PENTRY ; PASCAL 1.0 INITIALIZATION ENTRY 22 | LDA #$16 ; NO XOFF, ECHO, LF EAT, OR LF GEN 23 | INIT1 PHA ; GOES TO MISCFLG AFTER MODIFICATION 24 | LDA #0 25 | STA STATEFLG,X 26 | STA DELAYFLG,X 27 | STA HANDSHKE,X 28 | STA STSBYTE,X 29 | STA PWDBYTE,X 30 | STA COLBYTE,X 31 | LDA DIPSW2,Y ; SET LF GEN OPTION FROM D2-S5 32 | STA ZPTMP2 ; SAVE FOR LATER 33 | LSR A ; S5-> CARRY 34 | LSR A ; IF S5=ON=0 THEN LEAVE MISCFLG ALONE 35 | BCC INIT1A 36 | PLA ; OTHERWISE, MAKE SURE LF GEN 37 | AND #$FE ; ENABLE IS RESET 38 | PHA 39 | INIT1A CLV ; V WILL BE CLEAR FOR CIC MODE 40 | LDA DIPSW1,Y 41 | LSR A ; SIC MODES SET CARRY 42 | BCS INIT2 ; BRANCH FOR SIC MODES 43 | LSR A 44 | BCS INIT2B ; PPC MODE BRANCH 45 | LDA #$01 ; CTL-A 46 | BNE INIT5 ; CIC MODE BRANCH 47 | 48 | INIT2 LSR A ; SET CARRY FOR P8A 49 | LDA #$03 ; SET ETX AS DEFAULT INQUIRY CHAR 50 | BCS INIT2A ; BRANCH FOR P8A 51 | LDA #$80 ; FOR P8 SET AUTO CR GEN 52 | INIT2A STA STATEFLG,X 53 | INIT2B BIT IORTS ; SET V-FLAG FOR PPC, SIC MODES 54 | LDA ZPTMP2 55 | AND #$20 ; SET CR DELAY 56 | EOR #$20 ; SO 1=ENB, 0=DISABLE 57 | STA DELAYFLG,X ; FROM D2-S2 58 | 59 | BVS INIT3 ; BRANCH AROUND PASCAL 60 | ;;;;;;;;;;;;;;;;;;;;;;;;; 61 | ; PASCAL 1.0 READ ENTRY ; 62 | ; (MUST BE AT $C84D) ; 63 | ;;;;;;;;;;;;;;;;;;;;;;;;; 64 | PREAD0 JSR PASCALREAD ; DO PASCAL 1.1 READ 65 | LDX MSLOT ; MODIFY FOR 1.0 66 | STA STSBYTE,X ; CHARACTER READ 67 | RTS 68 | ;;;;;;;;;;;;;;;;;;;;;;;; 69 | ; NOW WHERE WERE WE??? ; 70 | ;;;;;;;;;;;;;;;;;;;;;;;; 71 | 72 | INIT3 LDA ZPTMP2 ; PPC, SIC MODES USE SWITCHES 73 | LSR A ; TO SET PWIDTH, CR DELAY 74 | LSR A 75 | AND #$03 76 | TAY 77 | BEQ INIT4 78 | 79 | PLA ; RESET VIDEO ENABLE FOR PWIDTH#40 80 | AND #$7F 81 | PHA 82 | 83 | INIT4 LDA PWDTBL,Y 84 | STA PWDBYTE,X 85 | LDY SLOT16 86 | 87 | PLA ; CLEAR CIC BIT IN FUTURE MISCFLG 88 | AND #$95 ; (AND TABBING, XOFF AND LF EAT BITS) 89 | PHA 90 | LDA #$09 ; CTL-I 91 | 92 | INIT5 STA CMDBYTE,X ; CMD ESC CHAR (IGNORED FOR SIC MODES) 93 | PLA 94 | STA MISCFLG,X ; SET MISCFLG FLAGS 95 | ; 96 | ; NOW FOR THE ACIA INITIALIZATION ROUTINE 97 | ; 98 | INITACIA LDA ZPTMP2 ; DIPSW2 99 | PHA 100 | AND #$A0 ; DATA BIT OPTIONS FOR CIC MODE 101 | BVC INITACIA1 ; BRANCH FOR CIC MODE 102 | AND #$80 ; 8 DATA, 1 OR 2 STOP FOR SIC, PPC 103 | INITACIA1 JSR DATACMD1 ; SET CONTROL REG 104 | JSR BAUDCMD1 ; SET DIPSWITCH BAUD RATE 105 | PLA 106 | AND #$0C ; PARITY OPTIONS FOR CIC MODE 107 | BVC INITACIA2 ; BRANCH FOR CIC MODE 108 | LDA #$0 ; DISABLE PARITY FOR SIC, PPC MODES 109 | INITACIA2 ASL A 110 | ASL A 111 | ASL A 112 | ORA #$0B 113 | STA CMDREG,Y 114 | LDA RDREG,Y ; THROW OUT THE STRANGE STUFF 115 | RTS 116 | ;;;;;;;;;;;;;;;;;;;;;;; 117 | ; PASCAL READ ROUTINE ; 118 | ;;;;;;;;;;;;;;;;;;;;;;; 119 | PASCALREAD JSR PENTRY ; SHARED BY BOTH PASCAL VERSIONS 120 | PASCALREAD1 JSR GETCHAR ; GET ACIA/KBD DATA 121 | AND #$7F ; CLEAR HIGH BIT FOR PASCAL 122 | PASEXIT LDY MSLOT 123 | LDX STSBYTE,Y ; ERROR STATUS-> X-REG 124 | RTS 125 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 126 | ; GETCHAR ROUTINE WAITS FOR ; 127 | ; THE NEXT CHAR FROM EITHER ; 128 | ; THE ACIA OR KEYBOARD (IF ; 129 | ; ENABLED). USED BY PASCAL ; 130 | ; READ ROUTINE, XON WAIT, ; 131 | ; AND ACK WAIT. DATA IS RE- ; 132 | ; TURNED IN THE A=REGISTER ; 133 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 134 | GETCHAR JSR INPUT ; ACIA DATA? 135 | BCS GETCHAR1 136 | JSR CKKBD ; KEYBOARD INPUT? 137 | BCC GETCHAR 138 | GETCHAR1 RTS ; EXIT WHEN WE HAVE SOMETHING 139 | -------------------------------------------------------------------------------- /6502/SSC.TERM.S: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ; ; 3 | ; APPLE II SSC FIRMWARE ; 4 | ; ; 5 | ; BY LARRY KENYON ; 6 | ; ; 7 | ; -JANUARY 1981- ;;;;;;;;;;;;;; 8 | ; ; 9 | ; (C) COPYRIGHT 1981 BY APPLE COMPUTER, INC. ; 10 | ; ; 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ; SHORT BLOCK MOVE ; 13 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 14 | BATCHIO TXA 15 | ASL A 16 | ASL A 17 | ASL A 18 | ASL A 19 | STA SLOT16 20 | LDA #0 21 | STA STSBYTE,X ; ZERO ERROR INDICATION 22 | BVS MOVIN 23 | 24 | MOVOUT LDY #0 25 | LDA (A1L),Y ; GET BUFFER DATA 26 | STA CHARACTER 27 | JSR ACIAOUT ; SEND IT OUT THE ACIA 28 | JSR NXTA1 29 | BCC MOVOUT 30 | RTS 31 | 32 | MOVIN JSR SRIN 33 | BCC MOVIN 34 | LDA RDREG,Y 35 | LDY #0 36 | STA (A1L),Y ; PUT ACIA DATA INTO BUFFER 37 | JSR NXTA1 38 | BCC MOVIN 39 | RTS 40 | 41 | ;;;;;;;;;;;;;;;;;;;;;;;;;; 42 | ; ; 43 | ; TERMINAL MODE ROUTINES ; 44 | ; ; 45 | ;;;;;;;;;;;;;;;;;;;;;;;;;; 46 | CHECKTERM LDA STATEFLG,X ; HAVE WE ENTERED TERMINAL MODE? 47 | BPL TERMRTS ; IF NOT, A SIMPLE RTS WILL DO. . . 48 | ; 49 | ; WE ENTER THE WORLD OF TERMINAL MODE 50 | ; 51 | TERMMODE LDA #$02 ; START IN SHIFT-LOCK STATE 52 | PHA ; SHIFT STATE IS SAVED ON STACK 53 | LDA #$7F 54 | JSR KCMD1 ; RESET ECHO (DEFAULT TO FULL DUP) 55 | 56 | TERMNEXT LDY CH 57 | LDA (BASL),Y 58 | STA CHARACTER ; SAVE SCREEN CHARACTER 59 | TERMNEXT1 LDA #$07 ; IMPLEMENT A FLASHING UNDERLINE 60 | AND RNDH ; FOR A CURSOR 61 | BNE TERMNEXT3 62 | LDY CH 63 | LDA #$DF 64 | CMP (BASL),Y ; IS UNDERLINE ON THE SCREEN? 65 | BNE TERMNEXT2 ; IF NOT, PUT IT THERE 66 | LDA CHARACTER ; OTHERWISE USE TRUE SCREEN CHAR 67 | TERMNEXT2 STA (BASL),Y 68 | INC RNDH ; MAKE IT FLASH, BUT 69 | INC RNDH ; NOT TOO SLOW AND NOT TOO FAST 70 | 71 | TERMNEXT3 LDA STATEFLG,X ; ARE WE STILL IN TERM MODE? 72 | BMI TERMACIAIN ; IF SO, GO CHECK ACIA 73 | 74 | TERMEXIT JSR RESTORE ; ALWAYS REPLACE OUR CURSOR 75 | PLA ; CLEAN UP THE STACK 76 | LDA #$8D ; RETURN A TO COVER UP 77 | STA CHARACTER 78 | TERMRTS RTS 79 | 80 | TERMACIAIN JSR INPUT ; ACIA INPUT? 81 | BCC TERMKBDIN ; IF NOT, GO CHECK KEYBOARD 82 | JSR RESTORE ; RESTORE CURSOR, INPUT->CHARACTER 83 | JSR CKINPUT ; CHECK FOR CTL-T, CTL-R 84 | JSR SCREENOUT1 ; INPUT->SCREEN ALWAYS 85 | JMP TERMNEXT 86 | 87 | TERMKBDIN JSR GETKBD ; KEYPRESS? 88 | BCC TERMNEXT1 ; SKIP IF NOT 89 | BVS TERMNEXT ; BRANCH IF WE DID A KBD ESCAPE SEQ. 90 | LDA MISCFLG,X ; SHIFTING ENABLED? 91 | ASL A 92 | BPL TERMSEND1 93 | PLA ; RECOVER TERMSTATE 94 | TAY 95 | LDA CHARACTER 96 | CPY #1 ; 1 = SHIFT LETTERS, XLATE NUMBERS 97 | BEQ TERMCAP 98 | BCS TERMLOCK ; 2 MEANS CAPS LOCK MODE 99 | 100 | TERMNORM CMP #$9B ; ESC? 101 | BNE TERMLETTER 102 | 103 | TERMINC INY ; INCREMENT STATE 104 | TERMINC1 TYA 105 | PHA ; PUT BACK ON STACK 106 | JMP TERMNEXT 107 | 108 | TERMLETTER CMP #$C1 ; Z? 111 | BCS TERMSEND 112 | ORA #$20 ; IT'S A LETTER SO TRANSLATE TO LC 113 | STA CHARACTER 114 | 115 | TERMSEND TYA 116 | PHA ; PUT STATE BACK ON STACK 117 | TERMSEND1 JSR OUTPUT1 ; GO OUTPUT 118 | JMP TERMNEXT 119 | 120 | TERMCAP CMP #$9B ; TWO ESCAPES? 121 | BEQ TERMINC 122 | CMP #$B0 ; <0? 123 | BCC TERMCAP1 124 | CMP #$BB ; >COLON? 125 | BCS TERMCAP1 126 | ; 127 | ; ESC SO TRANSLATE INTO MISSING ASCII CHAR 128 | ; 129 | TAY 130 | LDA TRANSLATE-$B0,Y 131 | STA CHARACTER 132 | TERMCAP1 LDY #0 ; BACK TO STATE 0 133 | BEQ TERMSEND ; 134 | 135 | TERMLOCK CMP #$9B ; ESC? 136 | BNE TERMSEND 137 | LDY #0 138 | BEQ TERMINC1 ; 139 | 140 | ;;;;;;;;;;;;;;;;;;; 141 | ; TRANSLATE TABLE ; 142 | ;;;;;;;;;;;;;;;;;;; 143 | TRANSLATE DFB $9B ; ESC 144 | DFB $9C ; FS 145 | DFB $9F ; US 146 | DFB $DB ; LEFT BRACKET 147 | DFB $DC ; LEFT SLASH 148 | DFB $DF ; UNDERSCORE 149 | DFB $FB ; LEFT ENCLOSE 150 | DFB $FC ; VERTICAL BAR 151 | DFB $FD ; RIGHT ENCLOSE 152 | DFB $FE ; TILDE 153 | DFB $FF ; RUB 154 | -------------------------------------------------------------------------------- /6502/SSC.CN00.S: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ; ; 3 | ; APPLE II SSC FIRMWARE ; 4 | ; ; 5 | ; BY LARRY KENYON ; 6 | ; ; 7 | ; -JANUARY 1981- ;;;;;;;;;;;;;; 8 | ; ; 9 | ; (C) COPYRIGHT 1981 BY APPLE COMPUTER, INC. ; 10 | ; ; 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ; ; 13 | ; CN00 SPACE CODE ; 14 | ; ; 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | 17 | ORG $C000+$0100*SLOT 18 | 19 | .IF SSC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 20 | 21 | BIT IORTS ; SET THE V-FLAG 22 | BVS BENTRY ; 23 | SEC 24 | DFB $90 ; OPCODE FOR BCC 25 | CLC 26 | CLV 27 | BVC BENTRY ; SKIP AROUND PASCAL 1.1 ENTRY 28 | 29 | .ELSE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 30 | 31 | CPX #$20 ; $CN01:$20 32 | LDX #$00 ; $CN03:$00 33 | CPY #$03 ; $CN05:$03 34 | LDY #$00 ; $CN07:$00 35 | CLV 36 | BVC DENTRY ; SKIP AROUND PASCAL 1.1 ENTRY 37 | 38 | .ENDIF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 39 | .ASSERT .LOBYTE(*)=$0B,ERROR 40 | 41 | DFB $01 ; GENERIC SIGNATURE BYTE 42 | DFB $31 ; DEVICE SIGNATURE BYTE 43 | DFB READY FOR OUTPUT? 97 | ; A-REG=1 -> HAS INPUT BEEN RECEIVED? 98 | ; 99 | PSTATUS LSR A ; SAVE REQUEST TYPE IN CARRY 100 | JSR PENTRY ; (PRESERVES CARRY) 101 | BCS PSTATIN 102 | JSR SROUT ; READY FOR OUTPUT? 103 | BEQ PSTATUS2 104 | CLC 105 | BCC PSTATUS2 ; CARRY CLEAR EOR NOT READY 106 | 107 | PSTATIN JSR SRIN ; SETS CARRY CORRECTLY 108 | PSTATUS2 LDA STSBYTE,X ; GET ERROR FLAGS 109 | TAX 110 | RTS 111 | 112 | ;;;;;;;;;;;;;;;;;;;;;;;; 113 | ; SHARED SETUP ROUTINE ; 114 | ;;;;;;;;;;;;;;;;;;;;;;;; 115 | BSETUP LDY #$10*SLOT ; Y-REG WILL GENERALLY BE $N0 116 | STY SLOT16 117 | DSETUP LDX #$C0+SLOT ; X-REG WILL GENERALLY BE $CN 118 | STX MSLOT 119 | STA ROMSOFF ; SWITCH OUT OTHER $C800 ROMS 120 | RTS 121 | 122 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 123 | ; ROUTINE TO SEND A CHARACTER TO ANOTHER CARD ; 124 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 125 | SENDCD LDX #3 126 | SAVEHOOK LDA CSWL,x 127 | PHA 128 | DEX 129 | BPL SAVEHOOK 130 | ; 131 | ; NOW PUT CARD ADDRESS IN HOOK 132 | ; 133 | LDX MSLOT 134 | LDA CHNBYTE,X 135 | STA CSWL 136 | LDA STATEFLG,X ; GET SLOT # 137 | AND #$38 138 | LSR A 139 | LSR A 140 | LSR A 141 | ORA #$C0 ; FORM $CN 142 | STA CSWH 143 | ; 144 | ; OUTPUT TO THE PERIPHERAL 145 | ; 146 | LDA CHARACTER 147 | PHA 148 | ORA #$80 ; 80 COL BOARDS WANT HI-BIT ON 149 | JSR COUT 150 | ; 151 | ; NOW RESTORE EVERYTHING THE OTHER CARD MAY HAVE CLOBBERED 152 | ; 153 | PLA 154 | STA CHARACTER 155 | JSR BSETUP 156 | ; 157 | ; PUT BACK CSWL INTO CHNBYTE 158 | ; 159 | LDA CSWL 160 | STA CHNBYTE,X 161 | 162 | LDX #0 163 | RESTORHOOK PLA 164 | STA CSWL,X 165 | INX 166 | CPX #4 167 | BCC RESTORHOOK 168 | 169 | LDX MSLOT 170 | RTS 171 | 172 | .RES $0100-$10-<* 173 | 174 | ;;;;;;;;;;;;;;;;;; 175 | ; ; 176 | ; CONFIG ENTRY ; 177 | ; ; 178 | ;;;;;;;;;;;;;;;;;; 179 | 180 | CONFIG JSR DSETUP 181 | JMP CFG 182 | 183 | ASC "OlSc" 184 | 185 | .IF SSC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 186 | 187 | ASC "APPLE" 188 | 189 | .ELSE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 190 | 191 | DFB $00 192 | DFB $00 ; SMARTPORT ID 193 | DFB $00,$00 ; TOTAL BLOCKS 194 | DFB $D7 ; STATUS BYTE 195 | 196 | .ENDIF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 197 | .ASSERT .LOBYTE(*)=$FF,ERROR 198 | 199 | DFB > 8) ^ data[i]) & 0x00FF]; 105 | } 106 | 107 | //Return the calculated checksum 108 | return crc; 109 | } 110 | 111 | void update_crc16(unsigned short *pCrc16, const char data[], size_t length) { 112 | for (size_t i = 0; i < length; i++) { 113 | *pCrc16 = (*pCrc16 << 8) ^ m_Crc16Table[((*pCrc16 >> 8) ^ data[i]) & 0x00FF]; 114 | } 115 | } 116 | /* [] END OF FILE */ 117 | -------------------------------------------------------------------------------- /fatfs/source/diskio.c: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------*/ 2 | /* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */ 3 | /*-----------------------------------------------------------------------*/ 4 | /* If a working storage control module is available, it should be */ 5 | /* attached to the FatFs via a glue function rather than modifying it. */ 6 | /* This is an example of glue functions to attach various exsisting */ 7 | /* storage control modules to the FatFs module with a defined API. */ 8 | /*-----------------------------------------------------------------------*/ 9 | 10 | #include "ff.h" /* Obtains integer types */ 11 | #include "diskio.h" /* Declarations of disk functions */ 12 | 13 | /* Definitions of physical drive number for each drive */ 14 | #define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */ 15 | #define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */ 16 | #define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */ 17 | 18 | 19 | /*-----------------------------------------------------------------------*/ 20 | /* Get Drive Status */ 21 | /*-----------------------------------------------------------------------*/ 22 | 23 | DSTATUS disk_status ( 24 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 25 | ) 26 | { 27 | DSTATUS stat; 28 | int result; 29 | 30 | switch (pdrv) { 31 | case DEV_RAM : 32 | result = RAM_disk_status(); 33 | 34 | // translate the reslut code here 35 | 36 | return stat; 37 | 38 | case DEV_MMC : 39 | result = MMC_disk_status(); 40 | 41 | // translate the reslut code here 42 | 43 | return stat; 44 | 45 | case DEV_USB : 46 | result = USB_disk_status(); 47 | 48 | // translate the reslut code here 49 | 50 | return stat; 51 | } 52 | return STA_NOINIT; 53 | } 54 | 55 | 56 | 57 | /*-----------------------------------------------------------------------*/ 58 | /* Inidialize a Drive */ 59 | /*-----------------------------------------------------------------------*/ 60 | 61 | DSTATUS disk_initialize ( 62 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 63 | ) 64 | { 65 | DSTATUS stat; 66 | int result; 67 | 68 | switch (pdrv) { 69 | case DEV_RAM : 70 | result = RAM_disk_initialize(); 71 | 72 | // translate the reslut code here 73 | 74 | return stat; 75 | 76 | case DEV_MMC : 77 | result = MMC_disk_initialize(); 78 | 79 | // translate the reslut code here 80 | 81 | return stat; 82 | 83 | case DEV_USB : 84 | result = USB_disk_initialize(); 85 | 86 | // translate the reslut code here 87 | 88 | return stat; 89 | } 90 | return STA_NOINIT; 91 | } 92 | 93 | 94 | 95 | /*-----------------------------------------------------------------------*/ 96 | /* Read Sector(s) */ 97 | /*-----------------------------------------------------------------------*/ 98 | 99 | DRESULT disk_read ( 100 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 101 | BYTE *buff, /* Data buffer to store read data */ 102 | LBA_t sector, /* Start sector in LBA */ 103 | UINT count /* Number of sectors to read */ 104 | ) 105 | { 106 | DRESULT res; 107 | int result; 108 | 109 | switch (pdrv) { 110 | case DEV_RAM : 111 | // translate the arguments here 112 | 113 | result = RAM_disk_read(buff, sector, count); 114 | 115 | // translate the reslut code here 116 | 117 | return res; 118 | 119 | case DEV_MMC : 120 | // translate the arguments here 121 | 122 | result = MMC_disk_read(buff, sector, count); 123 | 124 | // translate the reslut code here 125 | 126 | return res; 127 | 128 | case DEV_USB : 129 | // translate the arguments here 130 | 131 | result = USB_disk_read(buff, sector, count); 132 | 133 | // translate the reslut code here 134 | 135 | return res; 136 | } 137 | 138 | return RES_PARERR; 139 | } 140 | 141 | 142 | 143 | /*-----------------------------------------------------------------------*/ 144 | /* Write Sector(s) */ 145 | /*-----------------------------------------------------------------------*/ 146 | 147 | #if FF_FS_READONLY == 0 148 | 149 | DRESULT disk_write ( 150 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 151 | const BYTE *buff, /* Data to be written */ 152 | LBA_t sector, /* Start sector in LBA */ 153 | UINT count /* Number of sectors to write */ 154 | ) 155 | { 156 | DRESULT res; 157 | int result; 158 | 159 | switch (pdrv) { 160 | case DEV_RAM : 161 | // translate the arguments here 162 | 163 | result = RAM_disk_write(buff, sector, count); 164 | 165 | // translate the reslut code here 166 | 167 | return res; 168 | 169 | case DEV_MMC : 170 | // translate the arguments here 171 | 172 | result = MMC_disk_write(buff, sector, count); 173 | 174 | // translate the reslut code here 175 | 176 | return res; 177 | 178 | case DEV_USB : 179 | // translate the arguments here 180 | 181 | result = USB_disk_write(buff, sector, count); 182 | 183 | // translate the reslut code here 184 | 185 | return res; 186 | } 187 | 188 | return RES_PARERR; 189 | } 190 | 191 | #endif 192 | 193 | 194 | /*-----------------------------------------------------------------------*/ 195 | /* Miscellaneous Functions */ 196 | /*-----------------------------------------------------------------------*/ 197 | 198 | DRESULT disk_ioctl ( 199 | BYTE pdrv, /* Physical drive nmuber (0..) */ 200 | BYTE cmd, /* Control code */ 201 | void *buff /* Buffer to send/receive control data */ 202 | ) 203 | { 204 | DRESULT res; 205 | int result; 206 | 207 | switch (pdrv) { 208 | case DEV_RAM : 209 | 210 | // Process of the command for the RAM drive 211 | 212 | return res; 213 | 214 | case DEV_MMC : 215 | 216 | // Process of the command for the MMC/SD card 217 | 218 | return res; 219 | 220 | case DEV_USB : 221 | 222 | // Process of the command the USB drive 223 | 224 | return res; 225 | } 226 | 227 | return RES_PARERR; 228 | } 229 | 230 | -------------------------------------------------------------------------------- /usb_descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. 30 | * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. 31 | * 32 | * Auto ProductID layout's Bitmap: 33 | * [MSB] HID | MSC | CDC [LSB] 34 | */ 35 | #define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) 36 | #define USB_PID (0x4000 | \ 37 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4)) 38 | 39 | #define USB_VID 0x6502 40 | 41 | //--------------------------------------------------------------------+ 42 | // Device Descriptors 43 | //--------------------------------------------------------------------+ 44 | tusb_desc_device_t const desc_device = 45 | { 46 | .bLength = sizeof(tusb_desc_device_t), 47 | .bDescriptorType = TUSB_DESC_DEVICE, 48 | .bcdUSB = 0x0200, 49 | 50 | // Use Interface Association Descriptor (IAD) for CDC 51 | // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) 52 | .bDeviceClass = TUSB_CLASS_MISC, 53 | .bDeviceSubClass = MISC_SUBCLASS_COMMON, 54 | .bDeviceProtocol = MISC_PROTOCOL_IAD, 55 | 56 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 57 | 58 | .idVendor = USB_VID, 59 | .idProduct = USB_PID, 60 | .bcdDevice = 0x0100, 61 | 62 | .iManufacturer = 0x01, 63 | .iProduct = 0x02, 64 | .iSerialNumber = 0x03, 65 | 66 | .bNumConfigurations = 0x01 67 | }; 68 | 69 | // Invoked when received GET DEVICE DESCRIPTOR 70 | // Application return pointer to descriptor 71 | uint8_t const * tud_descriptor_device_cb(void) 72 | { 73 | return (uint8_t const *)&desc_device; 74 | } 75 | 76 | //--------------------------------------------------------------------+ 77 | // Configuration Descriptor 78 | //--------------------------------------------------------------------+ 79 | 80 | enum 81 | { 82 | ITF_NUM_CDC = 0, 83 | ITF_NUM_CDC_DATA, 84 | ITF_NUM_MSC, 85 | ITF_NUM_TOTAL 86 | }; 87 | 88 | #define EPNUM_CDC_NOTIF 0x81 89 | #define EPNUM_CDC_OUT 0x02 90 | #define EPNUM_CDC_IN 0x82 91 | 92 | #define EPNUM_MSC_OUT 0x03 93 | #define EPNUM_MSC_IN 0x83 94 | 95 | #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN \ 96 | + CFG_TUD_MSC * TUD_MSC_DESC_LEN) 97 | 98 | // Full speed configuration 99 | uint8_t const desc_fs_configuration[] = 100 | { 101 | // Config number, interface count, string index, total length, attribute, power in mA 102 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), 103 | 104 | // Interface number, string index, EP notification address and size, EP data address (out, in) and size. 105 | TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), 106 | 107 | // Interface number, string index, EP Out & EP In address, EP size 108 | TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), 109 | }; 110 | 111 | // Invoked when received GET CONFIGURATION DESCRIPTOR 112 | // Application return pointer to descriptor 113 | // Descriptor contents must exist long enough for transfer to complete 114 | uint8_t const * tud_descriptor_configuration_cb(uint8_t index) 115 | { 116 | (void)index; // for multiple configurations 117 | return desc_fs_configuration; 118 | } 119 | 120 | //--------------------------------------------------------------------+ 121 | // String Descriptors 122 | //--------------------------------------------------------------------+ 123 | 124 | // Buffer to hold flash ID 125 | static char serial[2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1]; 126 | 127 | // Array of pointer to string descriptors 128 | char const* string_desc_arr [] = 129 | { 130 | (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) 131 | "A2retro", // 1: Manufacturer 132 | "A2retroNET", // 2: Product 133 | serial, // 3: Serials, uses flash ID 134 | "A2retroNET CDC", // 4: CDC Interface 135 | "A2retroNET MSC", // 5: MSC Interface 136 | }; 137 | 138 | static uint16_t _desc_str[32]; 139 | 140 | // Invoked when received GET STRING DESCRIPTOR request 141 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 142 | uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) 143 | { 144 | (void)langid; 145 | 146 | uint8_t chr_count; 147 | 148 | if (index == 0) { 149 | memcpy(&_desc_str[1], string_desc_arr[0], 2); 150 | chr_count = 1; 151 | 152 | } else { 153 | // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. 154 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors 155 | 156 | if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) { 157 | return NULL; 158 | } 159 | 160 | if (index == 3 && !serial[0]) { 161 | pico_get_unique_board_id_string(serial, sizeof(serial)); 162 | } 163 | 164 | const char* str = string_desc_arr[index]; 165 | 166 | // Cap at max char 167 | chr_count = strlen(str); 168 | if (chr_count > 31) { 169 | chr_count = 31; 170 | } 171 | 172 | // Convert ASCII string into UTF-16 173 | for (uint8_t i = 0; i < chr_count; i++) { 174 | _desc_str[1 + i] = str[i]; 175 | } 176 | } 177 | 178 | // First byte is length (including header), second byte is string type 179 | _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); 180 | 181 | return _desc_str; 182 | } 183 | -------------------------------------------------------------------------------- /fatfs/source/ffsystem.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* A Sample Code of User Provided OS Dependent Functions for FatFs */ 3 | /*------------------------------------------------------------------------*/ 4 | 5 | #include "ff.h" 6 | 7 | 8 | #if FF_USE_LFN == 3 /* Use dynamic memory allocation */ 9 | 10 | /*------------------------------------------------------------------------*/ 11 | /* Allocate/Free a Memory Block */ 12 | /*------------------------------------------------------------------------*/ 13 | 14 | #include /* with POSIX API */ 15 | 16 | 17 | void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */ 18 | UINT msize /* Number of bytes to allocate */ 19 | ) 20 | { 21 | return malloc((size_t)msize); /* Allocate a new memory block */ 22 | } 23 | 24 | 25 | void ff_memfree ( 26 | void* mblock /* Pointer to the memory block to free (no effect if null) */ 27 | ) 28 | { 29 | free(mblock); /* Free the memory block */ 30 | } 31 | 32 | #endif 33 | 34 | 35 | 36 | 37 | #if FF_FS_REENTRANT /* Mutal exclusion */ 38 | /*------------------------------------------------------------------------*/ 39 | /* Definitions of Mutex */ 40 | /*------------------------------------------------------------------------*/ 41 | 42 | #define OS_TYPE 0 /* 0:Win32, 1:uITRON4.0, 2:uC/OS-II, 3:FreeRTOS, 4:CMSIS-RTOS */ 43 | 44 | 45 | #if OS_TYPE == 0 /* Win32 */ 46 | #include 47 | static HANDLE Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */ 48 | 49 | #elif OS_TYPE == 1 /* uITRON */ 50 | #include "itron.h" 51 | #include "kernel.h" 52 | static mtxid Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */ 53 | 54 | #elif OS_TYPE == 2 /* uc/OS-II */ 55 | #include "includes.h" 56 | static OS_EVENT *Mutex[FF_VOLUMES + 1]; /* Table of mutex pinter */ 57 | 58 | #elif OS_TYPE == 3 /* FreeRTOS */ 59 | #include "FreeRTOS.h" 60 | #include "semphr.h" 61 | static SemaphoreHandle_t Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */ 62 | 63 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 64 | #include "cmsis_os.h" 65 | static osMutexId Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */ 66 | 67 | #endif 68 | 69 | 70 | 71 | /*------------------------------------------------------------------------*/ 72 | /* Create a Mutex */ 73 | /*------------------------------------------------------------------------*/ 74 | /* This function is called in f_mount function to create a new mutex 75 | / or semaphore for the volume. When a 0 is returned, the f_mount function 76 | / fails with FR_INT_ERR. 77 | */ 78 | 79 | int ff_mutex_create ( /* Returns 1:Function succeeded or 0:Could not create the mutex */ 80 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 81 | ) 82 | { 83 | #if OS_TYPE == 0 /* Win32 */ 84 | Mutex[vol] = CreateMutex(NULL, FALSE, NULL); 85 | return (int)(Mutex[vol] != INVALID_HANDLE_VALUE); 86 | 87 | #elif OS_TYPE == 1 /* uITRON */ 88 | T_CMTX cmtx = {TA_TPRI,1}; 89 | 90 | Mutex[vol] = acre_mtx(&cmtx); 91 | return (int)(Mutex[vol] > 0); 92 | 93 | #elif OS_TYPE == 2 /* uC/OS-II */ 94 | OS_ERR err; 95 | 96 | Mutex[vol] = OSMutexCreate(0, &err); 97 | return (int)(err == OS_NO_ERR); 98 | 99 | #elif OS_TYPE == 3 /* FreeRTOS */ 100 | Mutex[vol] = xSemaphoreCreateMutex(); 101 | return (int)(Mutex[vol] != NULL); 102 | 103 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 104 | osMutexDef(cmsis_os_mutex); 105 | 106 | Mutex[vol] = osMutexCreate(osMutex(cmsis_os_mutex)); 107 | return (int)(Mutex[vol] != NULL); 108 | 109 | #endif 110 | } 111 | 112 | 113 | /*------------------------------------------------------------------------*/ 114 | /* Delete a Mutex */ 115 | /*------------------------------------------------------------------------*/ 116 | /* This function is called in f_mount function to delete a mutex or 117 | / semaphore of the volume created with ff_mutex_create function. 118 | */ 119 | 120 | void ff_mutex_delete ( /* Returns 1:Function succeeded or 0:Could not delete due to an error */ 121 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 122 | ) 123 | { 124 | #if OS_TYPE == 0 /* Win32 */ 125 | CloseHandle(Mutex[vol]); 126 | 127 | #elif OS_TYPE == 1 /* uITRON */ 128 | del_mtx(Mutex[vol]); 129 | 130 | #elif OS_TYPE == 2 /* uC/OS-II */ 131 | OS_ERR err; 132 | 133 | OSMutexDel(Mutex[vol], OS_DEL_ALWAYS, &err); 134 | 135 | #elif OS_TYPE == 3 /* FreeRTOS */ 136 | vSemaphoreDelete(Mutex[vol]); 137 | 138 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 139 | osMutexDelete(Mutex[vol]); 140 | 141 | #endif 142 | } 143 | 144 | 145 | /*------------------------------------------------------------------------*/ 146 | /* Request a Grant to Access the Volume */ 147 | /*------------------------------------------------------------------------*/ 148 | /* This function is called on enter file functions to lock the volume. 149 | / When a 0 is returned, the file function fails with FR_TIMEOUT. 150 | */ 151 | 152 | int ff_mutex_take ( /* Returns 1:Succeeded or 0:Timeout */ 153 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 154 | ) 155 | { 156 | #if OS_TYPE == 0 /* Win32 */ 157 | return (int)(WaitForSingleObject(Mutex[vol], FF_FS_TIMEOUT) == WAIT_OBJECT_0); 158 | 159 | #elif OS_TYPE == 1 /* uITRON */ 160 | return (int)(tloc_mtx(Mutex[vol], FF_FS_TIMEOUT) == E_OK); 161 | 162 | #elif OS_TYPE == 2 /* uC/OS-II */ 163 | OS_ERR err; 164 | 165 | OSMutexPend(Mutex[vol], FF_FS_TIMEOUT, &err)); 166 | return (int)(err == OS_NO_ERR); 167 | 168 | #elif OS_TYPE == 3 /* FreeRTOS */ 169 | return (int)(xSemaphoreTake(Mutex[vol], FF_FS_TIMEOUT) == pdTRUE); 170 | 171 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 172 | return (int)(osMutexWait(Mutex[vol], FF_FS_TIMEOUT) == osOK); 173 | 174 | #endif 175 | } 176 | 177 | 178 | 179 | /*------------------------------------------------------------------------*/ 180 | /* Release a Grant to Access the Volume */ 181 | /*------------------------------------------------------------------------*/ 182 | /* This function is called on leave file functions to unlock the volume. 183 | */ 184 | 185 | void ff_mutex_give ( 186 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 187 | ) 188 | { 189 | #if OS_TYPE == 0 /* Win32 */ 190 | ReleaseMutex(Mutex[vol]); 191 | 192 | #elif OS_TYPE == 1 /* uITRON */ 193 | unl_mtx(Mutex[vol]); 194 | 195 | #elif OS_TYPE == 2 /* uC/OS-II */ 196 | OSMutexPost(Mutex[vol]); 197 | 198 | #elif OS_TYPE == 3 /* FreeRTOS */ 199 | xSemaphoreGive(Mutex[vol]); 200 | 201 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 202 | osMutexRelease(Mutex[vol]); 203 | 204 | #endif 205 | } 206 | 207 | #endif /* FF_FS_REENTRANT */ 208 | 209 | -------------------------------------------------------------------------------- /hdd.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2024 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "config.h" 34 | #include "sp.h" 35 | 36 | #include "hdd.h" 37 | 38 | #define BLOCK_SIZE 512 39 | 40 | #define SUCCESS 0x00 41 | #define IO_ERROR 0x27 42 | #define WRITE_PROT 0x2B 43 | 44 | static bool sd, usb; 45 | 46 | static struct { 47 | FIL image; 48 | uint16_t offset; 49 | uint16_t blocks; 50 | bool error; 51 | bool prot; 52 | } hdd[MAX_DRIVES]; 53 | 54 | static uint16_t get_blocks(int drive) { 55 | if (!hdd[drive].error && !f_size(&hdd[drive].image)) { 56 | char *path = config_drivepath(drive); 57 | 58 | printf("HDD Open(Drive=%d,File=%s)\n", drive, path); 59 | 60 | FRESULT fr = f_open(&hdd[drive].image, path, FA_OPEN_EXISTING | FA_READ | FA_WRITE); 61 | if (fr == FR_DENIED) { 62 | printf(" Write-Protected\n"); 63 | fr = f_open(&hdd[drive].image, path, FA_OPEN_EXISTING | FA_READ); 64 | hdd[drive].prot = true; 65 | } 66 | if (fr != FR_OK) { 67 | printf("f_open(%s) error: %s (%d)\n", path, FRESULT_str(fr), fr); 68 | hdd[drive].error = true; 69 | } 70 | 71 | char *extension = strrchr(path, '.'); 72 | if (extension && strcasecmp(extension, ".2mg") == 0) { 73 | hdd[drive].offset = 0x40; 74 | printf(" 2MG\n"); 75 | } 76 | 77 | int32_t raw_blocks = (f_size(&hdd[drive].image) - hdd[drive].offset) / BLOCK_SIZE; 78 | hdd[drive].blocks = raw_blocks > 0xFFFF ? 0xFFFF: raw_blocks; 79 | printf(" %u Blocks\n", hdd[drive].blocks); 80 | } 81 | 82 | return hdd[drive].blocks; 83 | } 84 | 85 | static bool seek_block(int drive, uint16_t block) { 86 | if (block >= get_blocks(drive)) { 87 | return false; 88 | } 89 | 90 | FRESULT fr = f_lseek(&hdd[drive].image, hdd[drive].offset + block * BLOCK_SIZE); 91 | if (fr != FR_OK) { 92 | printf("f_lseek(%d) error: %s (%d)\n", drive, FRESULT_str(fr), fr); 93 | return false; 94 | } 95 | 96 | return true; 97 | } 98 | 99 | void hdd_init(void) { 100 | time_init(); 101 | 102 | sd_card_t *sd_card = sd_get_by_num(0); 103 | 104 | FRESULT fr = f_mount(&sd_card->fatfs, "SD:", 1); 105 | if (fr != FR_OK) { 106 | printf("f_mount(SD:) error: %s (%d)\n", FRESULT_str(fr), fr); 107 | return; 108 | } 109 | 110 | sd = true; 111 | } 112 | 113 | void hdd_reset(void) { 114 | for (int drive = 0; drive < MAX_DRIVES; drive++) { 115 | if (!f_size(&hdd[drive].image)) { 116 | continue; 117 | } 118 | 119 | printf("HDD Close(Drive=%d)\n", drive); 120 | 121 | FRESULT fr = f_close(&hdd[drive].image); 122 | if (fr != FR_OK) { 123 | printf("f_close(%d) error: %s (%d)\n", drive, FRESULT_str(fr), fr); 124 | } 125 | } 126 | memset(hdd, 0, sizeof(hdd)); 127 | } 128 | 129 | void hdd_mount_usb(bool mount) { 130 | // Make sure the upcoming new default drive is actually used 131 | hdd_reset(); 132 | config_reset(); 133 | 134 | if (mount) { 135 | static FATFS fatfs; 136 | 137 | FRESULT fr = f_mount(&fatfs, "USB:", 1); 138 | if (fr != FR_OK) { 139 | printf("f_mount(USB:) error: %s (%d)\n", FRESULT_str(fr), fr); 140 | return; 141 | } 142 | 143 | fr = f_chdrive("USB:"); 144 | if (fr != FR_OK) { 145 | printf("f_chdrive(USB:) error: %s (%d)\n", FRESULT_str(fr), fr); 146 | } 147 | 148 | usb = true; 149 | } else { 150 | 151 | FRESULT fr = f_chdrive("SD:"); 152 | if (fr != FR_OK) { 153 | printf("f_chdrive(SD:) error: %s (%d)\n", FRESULT_str(fr), fr); 154 | } 155 | 156 | fr = f_unmount("USB:"); 157 | if (fr != FR_OK) { 158 | printf("f_unmount(USB:) error: %s (%d)\n", FRESULT_str(fr), fr); 159 | } 160 | 161 | usb = false; 162 | } 163 | } 164 | 165 | bool hdd_sd_mounted(void) { 166 | return sd; 167 | } 168 | 169 | bool hdd_usb_mounted(void) { 170 | return usb; 171 | } 172 | 173 | bool hdd_protected(uint8_t drive) { 174 | return hdd[drive].prot; 175 | } 176 | 177 | uint8_t hdd_status(uint8_t drive, uint8_t *data) { 178 | // printf("HDD Status(Drive=%d)\n", drive); 179 | 180 | uint16_t blocks = get_blocks(drive); 181 | 182 | if (!blocks) { 183 | return IO_ERROR; 184 | } 185 | 186 | data[0] = blocks % 0x100; 187 | data[1] = blocks / 0x100; 188 | 189 | return SUCCESS; 190 | } 191 | 192 | uint8_t hdd_read(uint8_t drive, uint16_t block, uint8_t *data) { 193 | // printf("HDD Read(Drive=%d,Block=$%04X)\n", drive, block); 194 | 195 | if (!seek_block(drive, block)) { 196 | return IO_ERROR; 197 | } 198 | 199 | UINT br; 200 | FRESULT fr = f_read(&hdd[drive].image, data, BLOCK_SIZE, &br); 201 | if (fr != FR_OK || br != BLOCK_SIZE) { 202 | printf("f_read(%d) error: %s (%d)\n", drive, FRESULT_str(fr), fr); 203 | return IO_ERROR; 204 | } 205 | 206 | return SUCCESS; 207 | } 208 | 209 | 210 | uint8_t hdd_write(uint8_t drive, uint16_t block, const uint8_t *data) { 211 | // printf("HDD Write(Drive=d,Block=$%04X)\n", drive, block); 212 | 213 | if (!seek_block(drive, block)) { 214 | return IO_ERROR; 215 | } 216 | 217 | UINT bw; 218 | FRESULT fr = f_write(&hdd[drive].image, data, BLOCK_SIZE, &bw); 219 | if (fr != FR_OK || bw != BLOCK_SIZE) { 220 | if (fr == FR_DENIED) { 221 | return WRITE_PROT; 222 | } 223 | printf("f_write(%d) error: %s (%d)\n", drive, FRESULT_str(fr), fr); 224 | return IO_ERROR; 225 | } 226 | 227 | fr = f_sync(&hdd[drive].image); 228 | if (fr != FR_OK) { 229 | printf("f_sync(%d) error: %s (%d)\n", drive, FRESULT_str(fr), fr); 230 | return IO_ERROR; 231 | } 232 | 233 | return SUCCESS; 234 | } 235 | -------------------------------------------------------------------------------- /sd_spi/src/glue.c: -------------------------------------------------------------------------------- 1 | /* glue.c 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | /*-----------------------------------------------------------------------*/ 15 | /* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */ 16 | /*-----------------------------------------------------------------------*/ 17 | /* If a working storage control module is available, it should be */ 18 | /* attached to the FatFs via a glue function rather than modifying it. */ 19 | /* This is an example of glue functions to attach various exsisting */ 20 | /* storage control modules to the FatFs module with a defined API. */ 21 | /*-----------------------------------------------------------------------*/ 22 | #include 23 | // 24 | #include "ff.h" /* Obtains integer types */ 25 | // 26 | #include "diskio.h" /* Declarations of disk functions */ 27 | // 28 | #include "hw_config.h" 29 | #include "my_debug.h" 30 | #include "sd_card.h" 31 | 32 | #define TRACE_PRINTF(fmt, args...) 33 | //#define TRACE_PRINTF printf // task_printf 34 | 35 | /*-----------------------------------------------------------------------*/ 36 | /* Get Drive Status */ 37 | /*-----------------------------------------------------------------------*/ 38 | 39 | DSTATUS sd_disk_status(BYTE pdrv /* Physical drive nmuber to identify the drive */ 40 | ) { 41 | TRACE_PRINTF(">>> %s\n", __FUNCTION__); 42 | sd_card_t *p_sd = sd_get_by_num(pdrv); 43 | if (!p_sd) return RES_PARERR; 44 | sd_card_detect(p_sd); // Fast: just a GPIO read 45 | return p_sd->m_Status; // See http://elm-chan.org/fsw/ff/doc/dstat.html 46 | } 47 | 48 | /*-----------------------------------------------------------------------*/ 49 | /* Inidialize a Drive */ 50 | /*-----------------------------------------------------------------------*/ 51 | 52 | DSTATUS sd_disk_initialize( 53 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 54 | ) { 55 | TRACE_PRINTF(">>> %s\n", __FUNCTION__); 56 | 57 | bool rc = sd_init_driver(); 58 | if (!rc) return RES_NOTRDY; 59 | 60 | sd_card_t *p_sd = sd_get_by_num(pdrv); 61 | if (!p_sd) return RES_PARERR; 62 | // See http://elm-chan.org/fsw/ff/doc/dstat.html 63 | return p_sd->init(p_sd); 64 | } 65 | 66 | static int sdrc2dresult(int sd_rc) { 67 | switch (sd_rc) { 68 | case SD_BLOCK_DEVICE_ERROR_NONE: 69 | return RES_OK; 70 | case SD_BLOCK_DEVICE_ERROR_UNUSABLE: 71 | case SD_BLOCK_DEVICE_ERROR_NO_RESPONSE: 72 | case SD_BLOCK_DEVICE_ERROR_NO_INIT: 73 | case SD_BLOCK_DEVICE_ERROR_NO_DEVICE: 74 | return RES_NOTRDY; 75 | case SD_BLOCK_DEVICE_ERROR_PARAMETER: 76 | case SD_BLOCK_DEVICE_ERROR_UNSUPPORTED: 77 | return RES_PARERR; 78 | case SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED: 79 | return RES_WRPRT; 80 | case SD_BLOCK_DEVICE_ERROR_CRC: 81 | case SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK: 82 | case SD_BLOCK_DEVICE_ERROR_ERASE: 83 | case SD_BLOCK_DEVICE_ERROR_WRITE: 84 | default: 85 | return RES_ERROR; 86 | } 87 | } 88 | 89 | /*-----------------------------------------------------------------------*/ 90 | /* Read Sector(s) */ 91 | /*-----------------------------------------------------------------------*/ 92 | 93 | DRESULT sd_disk_read(BYTE pdrv, /* Physical drive nmuber to identify the drive */ 94 | BYTE *buff, /* Data buffer to store read data */ 95 | LBA_t sector, /* Start sector in LBA */ 96 | UINT count /* Number of sectors to read */ 97 | ) { 98 | TRACE_PRINTF(">>> %s\n", __FUNCTION__); 99 | sd_card_t *p_sd = sd_get_by_num(pdrv); 100 | if (!p_sd) return RES_PARERR; 101 | int rc = p_sd->read_blocks(p_sd, buff, sector, count); 102 | return sdrc2dresult(rc); 103 | } 104 | 105 | /*-----------------------------------------------------------------------*/ 106 | /* Write Sector(s) */ 107 | /*-----------------------------------------------------------------------*/ 108 | 109 | #if FF_FS_READONLY == 0 110 | 111 | DRESULT sd_disk_write(BYTE pdrv, /* Physical drive nmuber to identify the drive */ 112 | const BYTE *buff, /* Data to be written */ 113 | LBA_t sector, /* Start sector in LBA */ 114 | UINT count /* Number of sectors to write */ 115 | ) { 116 | TRACE_PRINTF(">>> %s\n", __FUNCTION__); 117 | sd_card_t *p_sd = sd_get_by_num(pdrv); 118 | if (!p_sd) return RES_PARERR; 119 | int rc = p_sd->write_blocks(p_sd, buff, sector, count); 120 | return sdrc2dresult(rc); 121 | } 122 | 123 | #endif 124 | 125 | /*-----------------------------------------------------------------------*/ 126 | /* Miscellaneous Functions */ 127 | /*-----------------------------------------------------------------------*/ 128 | 129 | DRESULT sd_disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */ 130 | BYTE cmd, /* Control code */ 131 | void *buff /* Buffer to send/receive control data */ 132 | ) { 133 | TRACE_PRINTF(">>> %s\n", __FUNCTION__); 134 | sd_card_t *p_sd = sd_get_by_num(pdrv); 135 | if (!p_sd) return RES_PARERR; 136 | switch (cmd) { 137 | case GET_SECTOR_COUNT: { // Retrieves number of available sectors, the 138 | // largest allowable LBA + 1, on the drive 139 | // into the LBA_t variable pointed by buff. 140 | // This command is used by f_mkfs and f_fdisk 141 | // function to determine the size of 142 | // volume/partition to be created. It is 143 | // required when FF_USE_MKFS == 1. 144 | static LBA_t n; 145 | n = sd_sectors(p_sd); 146 | *(LBA_t *)buff = n; 147 | if (!n) return RES_ERROR; 148 | return RES_OK; 149 | } 150 | case GET_BLOCK_SIZE: { // Retrieves erase block size of the flash 151 | // memory media in unit of sector into the DWORD 152 | // variable pointed by buff. The allowable value 153 | // is 1 to 32768 in power of 2. Return 1 if the 154 | // erase block size is unknown or non flash 155 | // memory media. This command is used by only 156 | // f_mkfs function and it attempts to align data 157 | // area on the erase block boundary. It is 158 | // required when FF_USE_MKFS == 1. 159 | static DWORD bs = 1; 160 | *(DWORD *)buff = bs; 161 | return RES_OK; 162 | } 163 | case CTRL_SYNC: 164 | return RES_OK; 165 | default: 166 | return RES_PARERR; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /6502/SSC.HILEV.S: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ; ; 3 | ; APPLE II SSC FIRMWARE ; 4 | ; ; 5 | ; BY LARRY KENYON ; 6 | ; ; 7 | ; -JANUARY 1981- ;;;;;;;;;;;;;; 8 | ; ; 9 | ; (C) COPYRIGHT 1981 BY APPLE COMPUTER, INC. ; 10 | ; ; 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ; ; 13 | ; CIC, SIC, PPC MODE HIGH-LEVEL ; 14 | ; ; 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | ; CIC EXIT ROUTINE . . . ; 17 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 18 | CICEXIT JSR CHECKTERM ; SEE IF WE'VE ENTERED TERMINAL MODE 19 | ;;;;;;;;;;;;;;;;;;;;;; 20 | ; BASIC EXIT ROUTINE ; 21 | ;;;;;;;;;;;;;;;;;;;;;; 22 | BASICEXIT PLA 23 | TAY 24 | PLA 25 | TAX 26 | JMP BEXIT 27 | ;;;;;;;;;;;;;;;;;;;;;;; 28 | ; BASIC INPUT ROUTINE ; 29 | ;;;;;;;;;;;;;;;;;;;;;;; 30 | BINPUT BEQ BINACIA ; BRANCH IF NOT CIC MODE 31 | LDA BUFBYTE,X ; INPUT BUFFER FULL? 32 | BPL BINKBD 33 | LSR BUFBYTE,X ; RESET BUFFER FULL 34 | BNE BINACIA1 ; 35 | 36 | BINKBD JSR GETKBD ; KEYBOARD DATA? 37 | BCC BINACIA 38 | 39 | BINEND LDA DELAYFLG,X 40 | AND #$C0 ; TRANSLATE LOWERCASE TO UPPERCASE? 41 | BEQ BINEND1 ; IF SO, LET THE MONITOR DO IT 42 | LDA CHARACTER ; IF NOT, SET FLAG IF 43 | CMP #$E0 ; THIS IS A LOWERCASE CHAR 44 | BCC BINEND1 ; FOR INPUT BUFFER CORRECTION 45 | LDA STATEFLG,X ; (CIRCUMVENT APPLE MONITOR) 46 | ORA #$40 47 | STA STATEFLG,X 48 | 49 | BINEND1 PLP 50 | BEQ BASICEXIT ; BRANCH IF NOT CIC MODE 51 | BNE CICEXIT ; CHECK TO SEE IF WE 52 | ; ENTERED TERM MODE (VIA KYBD ESCAPE) 53 | BINACIA JSR INPUT ; ACIA DATA? 54 | BCC BINKBD 55 | BINACIA1 JSR RESTORE ; DO BASIC CURSED DUTY 56 | PLP 57 | PHP ; GET CIC MODE INDICATOR 58 | BEQ BINEND ; SKIP IF NOT CIC MODE 59 | JSR CKINPUT ; LOOK FOR INPUT STREAM SPECIAL CHARS 60 | JMP BINEND 61 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 62 | ; SIC, PPC BASIC OUTPUT ROUTINE ; 63 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 64 | SEROUT JSR CMDSEQCK ; CHECK FOR A COMMAND SEQUENCE 65 | BCS BASICEXIT ; BRANCH IF WE WERE IN COMMAND MODE 66 | LDA CHARACTER ; SAVE CHAR ON STACK 67 | PHA 68 | LDA MISCFLG,X ; IF VIDEO OR TABBING ENABLED, 69 | AND #$C0 ; DON'T MESS WITH THE CURSOR 70 | BNE TABCHECK 71 | 72 | LDA CH ; CHECK FOR COMMA TABBING 73 | BEQ NOTAB ; IF CH=0, THERE WAS NO TAB OR COMMA 74 | CMP #8 ; INTEGER BASIC COMMA? 75 | BEQ COMMA 76 | CMP #16 ; APPLESOFT COMMA? 77 | BNE TABCHECK 78 | COMMA ORA #$F0 79 | AND COLBYTE,X ; SET COL TO PREVIOUS TAB 80 | CLC 81 | ADC CH ; THEN INCREMENT TO NEXT TAB 82 | STA CH 83 | 84 | TABCHECK LDA COLBYTE,X 85 | CMP CH ; IS TABBING NEEDED? 86 | BEQ NOTAB ; IF EQUAL THEN NO TAB NEEDED 87 | LDA #$A0 ; SPACE FOR FORWARD TAB 88 | BCC TAB1 89 | LDA MISCFLG,X ; DON'T BACKSPACE UNLESS TABBING 90 | ASL A ; OPTION IS ENABLED 91 | BPL NOTAB 92 | LDA #$88 ; BACKSPACE FOR BACKTAB 93 | TAB1 STA CHARACTER 94 | BIT IORTS ; SET V=1 TO INDICATE TABBING 95 | PHP ; SAVE TABBING INDICATOR 96 | BVS TAB2 ; AROUND BATCH MOVE ENTRY 97 | NOP 98 | ;;;;;;;;;;;;;;;;;;;;;;;; 99 | ; SHORT BATCH MOVE: ; 100 | ; LOCATE AT $C93D FOR ; 101 | ; COMPATIBILITY WITH ; 102 | ; SIC P8 BLOCK MOVE. ; 103 | ;;;;;;;;;;;;;;;;;;;;;;;; 104 | BATCHIN BIT IORTS 105 | DFB $50 ; DUMMY BVC 106 | BATCHOUT CLV ; V=0 FOR OUTPUT ENTRY 107 | LDX MSLOT 108 | JMP BATCHIO 109 | ;;;;;;;;;;;;;; 110 | ; BURP . . . ; 111 | ;;;;;;;;;;;;;; 112 | TAB2 JSR ADJUST ; ADJUST COLUMN COUNT 113 | JSR OUTPUT2 ; DON'T GO TO SCREEN WHEN TABBING 114 | JMP FORCECR ; SHARE SOME CODE. . . 115 | 116 | NOTAB PLA 117 | CLV 118 | PHP ; SAVE 'NO TAB' INDICATION 119 | NOTAB1 STA CHARACTER ; (FORCE CR REENTRY) 120 | PHA 121 | JSR OUTPUT1 ; ENTER AFTER CMD SEQ CHECK 122 | JSR ADJUST 123 | PLA 124 | EOR #$8D ; WAS IT A CR? 125 | ASL A 126 | BNE FORCECR 127 | STA COLBYTE,X ; IF SO, RESET COLUMN TO 0 128 | STA CH 129 | 130 | FORCECR LDA STATEFLG,X ; FORCE CR DISABLED? 131 | BPL SEREND 132 | LDA PWDBYTE,X ; FORCE CR IF LIMIT REACHED 133 | BEQ SEREND ; (FOR P8 POKE COMPATIBILITY) 134 | CLC 135 | SBC COLBYTE,X 136 | LDA #$8D 137 | BCC NOTAB1 ; BRANCH TO FORCE CR 138 | 139 | SEREND PLP 140 | BVS TABCHECK ; BRANCH IF TABBING 141 | 142 | LDA MISCFLG,X ; DON'T MESS WITH CURSOR 143 | BMI SEREND2 ; WHEN VIDEO IS ON 144 | LDY COLBYTE,X 145 | ASL A 146 | BMI SETCH ; SET CH TO VALUE OF COL FOR TABBING 147 | TYA 148 | LDY #0 149 | SEC 150 | SBC PWDBYTE,X 151 | CMP #$F8 ; WITHIN 8 CHARS OF PWIDTH? 152 | BCC SETCH 153 | ADC #$27 ; IF SO, ADJUST TO WITHIN 8 OF 40 154 | TAY 155 | SETCH STY CH 156 | 157 | SEREND2 JMP BASICEXIT ; THAT'S ALL 158 | 159 | ;;;;;;;;;;;;;;;;;;;;;;;; 160 | ; PASCAL ENTRY ROUTINE ; 161 | ;;;;;;;;;;;;;;;;;;;;;;;; 162 | PENTRY STX MSLOT 163 | STY SLOT16 164 | LDA #0 165 | STA STSBYTE,X 166 | RTS 167 | 168 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 169 | ; SIC MODE PRINTER WIDTH TABLE ; 170 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 171 | PWDTBL DFB $29 ; 40 COLUMNS 172 | DFB $48 ; 72 COLUMNS 173 | DFB $50 ; 80 COLUMNS 174 | DFB $84 ; 132 COLUMNS 175 | ;;;;;;;;;;;;;;;;;;;;;;;; 176 | ; PASCAL WRITE ROUTINE ; 177 | ; (DOUBLES AS PASCAL ; 178 | ; 1.0 ENTRY POINT) ; 179 | ; -MUST BE AT SC9AA- ; 180 | ;;;;;;;;;;;;;;;;;;;;;;;; 181 | PASCALWRITE STA CHARACTER 182 | JSR PENTRY 183 | JSR OUTPUT 184 | JMP PASEXIT ; LOAD X-REG WITH ERROR BYTE & RTS 185 | 186 | ;;;;;;;;;;;;;;;;;;;;;;;;; 187 | ; COLUMN ADJUST ROUTINE ; 188 | ; (PPC, SIC MODES ONLY) ; 189 | ;;;;;;;;;;;;;;;;;;;;;;;;; 190 | ADJUST LDA CHARACTER 191 | EOR #$08 ; BACKSPACE? 192 | ASL A 193 | BEQ DECRCOL ; IF SO, DECREMENT COLUMN 194 | EOR #$EE ; DELETE? ($FF, RUB) 195 | BNE CTRLTST 196 | DECRCOL DEC COLBYTE,X ; DECREMENT COLUMN COUNT 197 | BPL ADJRTS 198 | STA COLBYTE,X ; DON'T ALLOW TO GO BELOW 0 199 | ADJRTS RTS 200 | CTRLTST CMP #$C0 ; DON'T INCREMENT COLUMN COUNT FOR 201 | BCS ADJRTS ; CONTROL CHARACTERS 202 | INC COLBYTE,X 203 | RTS 204 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 205 | ; ROUTINE TO PROCESS SPECIAL INPUT CHARS ; 206 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 207 | CKINPUT LDA MISCFLG,X 208 | AND #$08 ; INPUT CTL CHARS ENABLED? 209 | BEQ CIEND 210 | 211 | LDA STATEFLG,X 212 | LDY CHARACTER 213 | CPY #$94 ; CTL-T? 214 | BNE CKINPUT1 215 | ORA #$80 ; SET TERMINAL MODE 216 | BNE CKINPUT2 ; 217 | 218 | CKINPUT1 CPY #$92 ; CONTROL-R? 219 | BNE CIEND 220 | AND #$7F ; RESET TERMINAL MODE 221 | CKINPUT2 STA STATEFLG,X 222 | CIEND RTS 223 | -------------------------------------------------------------------------------- /6502/SSC.S: -------------------------------------------------------------------------------- 1 | .FEATURE labels_without_colons 2 | .MACPACK apple2 3 | .DEFINE ASC scrcode 4 | .DEFINE EQU = 5 | .DEFINE DFB .byte 6 | .DEFINE ORG .org 7 | 8 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 9 | ; ; 10 | ; APPLE II SSC FIRMWARE ; 11 | ; ; 12 | ; BY LARRY KENYON ; 13 | ; ; 14 | ; -JANUARY 1981- ;;;;;;;;;;;;;; 15 | ; ; 16 | ; (C) COPYRIGHT 1981 BY APPLE COMPUTER, INC. ; 17 | ; ; 18 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 19 | ; ; 20 | ; VARIABLE DEFINITIONS ; 21 | ; ; 22 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 23 | ;;;;;;;;;;;;;;;;;; 24 | ; ZERO PAGE EQUS ; 25 | ;;;;;;;;;;;;;;;;;; 26 | LOC0 EQU $00 27 | LOC1 EQU $01 28 | CH EQU $24 ; CURSOR HORIZONTAL POSITION 29 | SLOT16 EQU $26 ; SAVE $N0 TO FREE UP Y-REG 30 | CHARACTER EQU $27 ; OUTPUT, SCREEN AND INPUT CHARS 31 | BASL EQU $28 ; BASE SCREEN ADDRESS POINTER 32 | ZPTEMP EQU $35 ; WORKHORSE TEMPORARY 33 | ZPTMP1 EQU $2A ; WHEN ZPTEMP ISN'T ENOUGH 34 | ZPTMP2 EQU $2B ; TEMPORARIES, TEMPORARIES! 35 | CSWL EQU $36 ; CHAR OUT VECTOR 36 | CSWH EQU $37 37 | KSWL EQU $38 ; CHAR IN VECTOR 38 | KSWH EQU $39 39 | A1L EQU $3C ; BATCH MOVE POINTER 40 | RNDL EQU $4E ; RANDOM NUMBER SEED 41 | RNDH EQU $4F 42 | ;;;;;;;;;;;;;;;;;;; 43 | ; GENERAL EQUATES ; 44 | ;;;;;;;;;;;;;;;;;;; 45 | STACK EQU $0100 ; SYSTEM STACK BLOCK 46 | INBUFF EQU $0200 ; SYSTEM INPUT BUFFER 47 | KBD EQU $C000 ; KEYBOARD INPUT 48 | KBDSTRB EQU $C010 ; KEYBOARD CLEAR 49 | ROMSOFF EQU $CFFF ; DISABLES C0-RES. $C800 ROMS 50 | ;;;;;;;;;;;;;;;;;;;;;; 51 | ; SSC CARD ADDRESSES ; 52 | ;;;;;;;;;;;;;;;;;;;;;; 53 | DIPSW1 EQU $C081 ; (+$N0) DIPSWITCH BLOCK 1 54 | DIPSW2 EQU $C082 ; (+$N0) DIPSWITCH BLOCK 2 55 | TDREG EQU $C088 ; (+$N0) TRANSMIT DATA REG (WRITE) 56 | RDREG EQU $C088 ; (+$N0) READ DATA REG (READ) 57 | STREG EQU $C089 ; (+$N0) STATUS REGISTER (READ) 58 | RESET EQU $C089 ; (+$N0) SOFTWARE RESET (WRITE) 59 | CMDREG EQU $C08A ; (+$N0) COMMAND REGISTER (R/W) 60 | CTLREG EQU $C08B ; (+$N0) CONTROL REGISTER (R/W) 61 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 62 | ; BIT-> B7 B6 B5 B4 B3 B2 B1 B0 63 | ; +----------------------------+ 64 | ; DIPSW1 S1 S2 S3 S4 Z Z S5 S6 (LEFT DIPSWITCH) 65 | ; 66 | ; (S1-S4 USED FOR BAUD RATE, S5-S6 FOR FIRMWARE MODE) 67 | ; 68 | ; DIPSW2 S1 Z S2 Z S3 S4 S5 CTS (RIGHT DIPSWITCH) 69 | ; 70 | ; STREG INT DSR DCD TDR RDR OVR FE PE 71 | ; 72 | ; CTLREG STB << WL >> CK << BAUD RATE >> 73 | ; 74 | ; CMDREG <> ECH <> RE DTR 75 | ; 76 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 77 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 78 | ; SCREEN VARIABLES: PPC AND SIC MODES ; 79 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 80 | CMDBYTE EQU $5F8-$C0 ; HOLDS COMMAND CHARACTER (PPC & CIC) 81 | HANDSHKE EQU $4F8-$C0 ; SIC P8A CHAR COUNTER FOR ETX/ACK 82 | PARAMETER EQU $4F8-$C0 ; ACCUMULATOR FOR CMD PARAMETER 83 | STATEFLG EQU $578-$C0 84 | ; B7=CR GEN ENB FLAG B6=AFTER LC INPUT FLG 85 | ; B2-B0=COMMAND INTERPRETER STATES 86 | ; 0 0 0 IDLE 87 | ; 0 0 1 CMD CHAR RECEIVED 88 | ; 0 1 0 COLLECT UNTIL CHAR THEN DO COMMAND 89 | ; 0 1 1 SKIP UNTIL SPACE, THEN GOTO STATE 4 90 | ; 1 0 0 E/D COMMANDS 91 | ; 1 0 1 UNUSED 92 | ; 1 1 0 WAIT UNTIL CR THEN SET STATE TO ZERO 93 | ; 1 1 1 WAIT UNTIL CR THEN DO PROC INDICATED BY PARM 94 | ; 95 | ; (B4-B0 DETERMINE ENQUIRE CHAR FOR P8A MODE) 96 | ; 97 | DELAYFLG EQU $478-$C0 98 | ; B7-B6=SCREEN TRANSLATION OPTIONS 99 | ; 0 0 LC->UC 100 | ; 0 1 NO TRANSLATION 101 | ; 1 0 LC->UC INVERSE 102 | ; 1 1 LC->UC, UC->UC INVERSE 103 | ; (1-3 WILL ALLOW LC CHARS TO PASS THRU MONITOR) 104 | ; 105 | ; B5-B4=CR DELAY 0 0 = NO DELAY 106 | ; B3-B2=LF DELAY 0 1 = 32 MILLISEC 107 | ; B1-BO=FF DELAY 1 0 = 1/4 SEC 108 | ; 1 1 = 2 SEC 109 | ; 110 | STSBYTE EQU $678-$C0 ; STATUS/IORESULT/INPUT BYTE 111 | PWDBYTE EQU $6F8-$C0 ; PRINTER (FORMAT) WIDTH 112 | COLBYTE EQU $778-$C0 ; COLUMN POSITION COUNTER 113 | MISCFLG EQU $7F8-$C0 114 | ; B7=ECHO BIT B6=TABBING OPTION ENABLE 115 | ; B5=LINEFEED EAT B4=PASCAL/BASIC FLAG 116 | ; B3=XOFF ENB FLAG B2=KEYBOARD ENB 117 | ; B1=PPC/CIC MODE B0=LF GENERATE ENB 118 | ; 119 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 120 | ; TEMP SCREEN VARS (SLOT INDEPENDENT) ; 121 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 122 | MSLOT EQU $7F8 ; BUFFER FOR HI SLOT ADDR (SCN) 123 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 124 | ; SCREEN VARIABLES: CIC MODE ; 125 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 126 | ; 127 | ; STATEFLG: B7=TERMINAL MODE FLAG 128 | ; B3-B5=CHAIN SLOT 129 | ; 130 | CHNBYTE EQU $6F8-$C0 ; CURRENT OUTPUT SCREEN ($CN00 ENTRY) 131 | ; 132 | ; B0-B7=CN00 ENTRY 133 | ; 134 | BUFBYTE EQU $778-$C0 ; BUFFER FOR ONE 135 | ; INPUT BYTE: HIGH BIT IS SET 136 | ; WHEN BUFFER IS FULL 137 | ; 138 | ; MISCFLG: B6=TERM MODE SHIFT ENB 139 | ; 140 | ; OTHER SLOT VARIABLES AS DEFINED FOR PPC AND SIC MODES 141 | ; 142 | ;;;;;;;;;;;;;;;;;;;;;;; 143 | ; MONITOR SUBROUTINES ; 144 | ;;;;;;;;;;;;;;;;;;;;;;; 145 | COUT EQU $FDED ; CHARACTER OUT (THRU CSW) 146 | SETKBD EQU $FE89 ; SETS KSW TO APPLE KEYBOARD 147 | IORTS EQU $FF58 ; KNOWN "RTS" LOCATION 148 | NXTA1 EQU $FCBA ; INCREMENT A1H,L AND CMP TO A2H,L 149 | SETSCR EQU $FE93 ; SETS CSW TO APPLE SCREEN 150 | VIDOUT EQU $FDF6 ; OUTPUT A CHAR TO APPLE SCREEN 151 | SLOOP EQU $FABA ; CONTINUE SLOT SCAN 152 | SETTXT EQU $FB39 ; SET TEXT MODE 153 | HOME EQU $FC58 ; CLEAR SCREEN AND HOME CURSOR 154 | PRERR EQU $FF2D ; DISPLAY "ERR" AND BEEP 155 | BASIC EQU $E000 ; BASIC INTERPRETER COLD START 156 | BASCALC EQU $FBC1 ; CALC SCREEN BASE ADR IN BASH,L 157 | 158 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 159 | ; ; 160 | ; VARIABLE DEFINITIONS ; 161 | ; ; 162 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 163 | ;;;;;;;;;;;;;;;;;; 164 | ; ZERO PAGE EQUS ; 165 | ;;;;;;;;;;;;;;;;;; 166 | CMD EQU $42 ; PRODOS COMMAND 167 | UNIT EQU $43 ; PRODOS UNIT 168 | ADDRL EQU $44 ; PRODOS / SMARTPORT BUFFER ADDRESS 169 | ADDRH EQU $45 170 | BLOCKL EQU $46 ; PRODOS BLOCK 171 | BLOCKH EQU $47 172 | SIZEL EQU $46 ; SMARTPORT BUFFER SIZE 173 | SIZEH EQU $47 174 | ;;;;;;;;;;;;;;;;;;;;;;; 175 | ; SMARTPORT ADDRESSES ; 176 | ;;;;;;;;;;;;;;;;;;;;;;; 177 | DATA EQU $CFF0 ; SMARTPORT DATA REGISTER 178 | CTRL EQU $CFF1 ; SMARTPORT CONTROL REGISTER 179 | ;;;;;;;;;;;;;;;;;;;;;;;; 180 | ; BASIC MODE ADDRESSES ; 181 | ;;;;;;;;;;;;;;;;;;;;;;;; 182 | BENTER EQU $CFF9 ; ENTER A2RETRONET BASIC MODE 183 | BLEAVE EQU $CFFA ; LEAVE A2RETRONET BASIC MODE 184 | ;;;;;;;;;;;;;;;;;;;;; 185 | ; BANKING ADDRESSES ; 186 | ;;;;;;;;;;;;;;;;;;;;; 187 | BANKCLR EQU $CFFB ; CLEAR EXPANSION ROM BANK 188 | BANKSET EQU $CFFC ; SET EXPANSION ROM BANK 189 | 190 | .REPEAT 2,_SSC 191 | .DEFINE SSC _SSC 192 | 193 | .REPEAT 8,_SLOT 194 | .DEFINE SLOT _SLOT 195 | 196 | .IF SSC .OR SLOT 197 | .SCOPE 198 | .ENDIF 199 | .INCLUDE "SSC.CN00.S" 200 | .IF SSC .OR SLOT 201 | .ENDSCOPE 202 | .ENDIF 203 | 204 | .UNDEF SLOT 205 | .ENDREP 206 | 207 | .IF SSC 208 | .SCOPE 209 | .ENDIF 210 | .INCLUDE "SSC.C800.S" 211 | .INCLUDE "SSC.HILEV.S" 212 | .INCLUDE "SSC.TERM.S" 213 | .INCLUDE "SSC.CORE.S" 214 | .INCLUDE "SSC.UTIL.S" 215 | .INCLUDE "SSC.CMD.S" 216 | .INCLUDE "SSC.CF00.S" 217 | .IF SSC 218 | .ENDSCOPE 219 | .ENDIF 220 | 221 | .UNDEF SSC 222 | .ENDREP 223 | 224 | ;;;;;;;;;; 225 | ; BANK 1 ; 226 | ;;;;;;;;;; 227 | .INCLUDE "SMARTPORT.S" 228 | 229 | .SCOPE 230 | .INCLUDE "SSC.CF00.S" 231 | .ENDSCOPE 232 | 233 | ;;;;;;;;;; 234 | ; BANK 2 ; 235 | ;;;;;;;;;; 236 | .RES $CF00-$C800 237 | 238 | .SCOPE 239 | .INCLUDE "SSC.CF00.S" 240 | .ENDSCOPE 241 | 242 | ;;;;;;;;;; 243 | ; BANK 3 ; 244 | ;;;;;;;;;; 245 | .RES $CF00-$C800 246 | 247 | .SCOPE 248 | .INCLUDE "SSC.CF00.S" 249 | .ENDSCOPE 250 | -------------------------------------------------------------------------------- /board.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2024 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "firmware.h" 32 | #include "sp.h" 33 | 34 | #include "board.h" 35 | 36 | static const uint8_t __not_in_flash("ser_bits") ser_bits[] = { 37 | 0b11111111, 38 | 0b01111111, 39 | 0b00111111, 40 | 0b00011111}; 41 | 42 | static volatile bool serial; 43 | static volatile bool active; 44 | 45 | static volatile uint32_t offset; 46 | 47 | static volatile uint32_t self; 48 | 49 | static volatile uint32_t ser_command; 50 | static volatile uint32_t ser_control; 51 | 52 | static volatile uint8_t ser_mask; 53 | static volatile uint8_t output_mask; 54 | 55 | static void __time_critical_func(reset)(bool asserted) { 56 | static absolute_time_t assert_time; 57 | 58 | if (asserted) { 59 | if (assert_time != nil_time) { 60 | // Ignore unstable RESET line during Apple II power-up 61 | return; 62 | } 63 | active = false; 64 | offset = serial ? OFFSET_SERIAL : OFFSET_NORMAL; 65 | 66 | ser_command = 0b00000000; 67 | ser_control = 0b00000000; 68 | ser_mask = ser_bits[0b00]; 69 | 70 | output_mask = 0b11111111; 71 | 72 | multicore_fifo_drain(); 73 | sp_reset(); 74 | 75 | assert_time = get_absolute_time(); 76 | } else { 77 | if (absolute_time_diff_us(assert_time, get_absolute_time()) > 200000) { 78 | serial = false; 79 | offset = OFFSET_NORMAL; 80 | } 81 | assert_time = nil_time; 82 | } 83 | } 84 | 85 | static void __time_critical_func(nop_get)(void) { 86 | } 87 | 88 | static void __time_critical_func(ser_dipsw1_get)(void) { 89 | // 0001 9600 baud 90 | // 00 91 | // 01 printer mode 92 | a2pico_putdata(pio0, 0b11101110); 93 | } 94 | 95 | static void __time_critical_func(ser_dipsw2_get)(void) { 96 | // 1 1 stop bit 97 | // 0 98 | // 0 no delay 99 | // 0 100 | // 11 40 cols 101 | // 1 auto-lf 102 | // 0 cts line (not dipsw2) 103 | a2pico_putdata(pio0, 0b01110000); 104 | } 105 | 106 | static void __time_critical_func(ser_data_get)(void) { 107 | a2pico_putdata(pio0, sio_hw->fifo_rd & ser_mask); 108 | } 109 | 110 | static void __time_critical_func(ser_status_get)(void) { 111 | // SIO_FIFO_ST_VLD_BITS _u(0x00000001) 112 | // SIO_FIFO_ST_RDY_BITS _u(0x00000002) 113 | a2pico_putdata(pio0, (sio_hw->fifo_st & 3) << 3); 114 | } 115 | 116 | static void __time_critical_func(ser_command_get)(void) { 117 | a2pico_putdata(pio0, ser_command); 118 | } 119 | 120 | static void __time_critical_func(ser_control_get)(void) { 121 | a2pico_putdata(pio0, ser_control); 122 | } 123 | 124 | static const void __not_in_flash("devsel_get")(*devsel_get[])(void) = { 125 | nop_get, ser_dipsw1_get, ser_dipsw2_get, nop_get, 126 | nop_get, nop_get, nop_get, nop_get, 127 | ser_data_get, ser_status_get, ser_command_get, ser_control_get, 128 | nop_get, nop_get, nop_get, nop_get 129 | }; 130 | 131 | static void __time_critical_func(nop_put)(uint32_t data) { 132 | } 133 | 134 | static void __time_critical_func(ser_data_put)(uint32_t data) { 135 | sio_hw->fifo_wr = data & ser_mask & output_mask; 136 | } 137 | 138 | static void __time_critical_func(ser_reset_put)(uint32_t data) { 139 | ser_command &= 0b11100000; 140 | } 141 | 142 | static void __time_critical_func(ser_command_put)(uint32_t data) { 143 | ser_command = data; 144 | } 145 | 146 | static void __time_critical_func(ser_control_put)(uint32_t data) { 147 | ser_control = data; 148 | ser_mask = ser_bits[(data >> 5) & 0b11]; 149 | } 150 | 151 | static const void __not_in_flash("devsel_put")(*devsel_put[])(uint32_t) = { 152 | nop_put, nop_put, nop_put, nop_put, 153 | nop_put, nop_put, nop_put, nop_put, 154 | ser_data_put, ser_reset_put, ser_command_put, ser_control_put, 155 | nop_put, nop_put, nop_put, nop_put 156 | }; 157 | 158 | static void __time_critical_func(sp_data_get)(void) { 159 | if (!active) { 160 | return; 161 | } 162 | a2pico_putdata(pio0, sp_buffer[sp_read_offset]); 163 | sp_read_offset++; 164 | } 165 | 166 | static void __time_critical_func(sp_control_get)(void) { 167 | if (!active) { 168 | return; 169 | } 170 | a2pico_putdata(pio0, sp_control); 171 | } 172 | 173 | static void __time_critical_func(deactivate_get)(void) { 174 | active = false; 175 | } 176 | 177 | static const void __not_in_flash("cffx_get")(*cffx_get[])(void) = { 178 | sp_data_get, sp_control_get, nop_get, nop_get, 179 | nop_get, nop_get, nop_get, nop_get, 180 | nop_get, nop_get, nop_get, nop_get, 181 | nop_get, nop_get, nop_get, deactivate_get 182 | }; 183 | 184 | static void __time_critical_func(sp_data_put)(uint32_t data) { 185 | if (!active) { 186 | return; 187 | } 188 | sp_buffer[sp_write_offset++] = data; 189 | } 190 | 191 | static void __time_critical_func(sp_control_put)(uint32_t data) { 192 | if (!active) { 193 | return; 194 | } 195 | sp_control = data; 196 | } 197 | 198 | static void __time_critical_func(basic_enter_put)(uint32_t data) { 199 | if (!active) { 200 | return; 201 | } 202 | output_mask = 0b01111111; 203 | } 204 | 205 | static void __time_critical_func(basic_leave_put)(uint32_t data) { 206 | if (!active) { 207 | return; 208 | } 209 | output_mask = 0b11111111; 210 | } 211 | 212 | static void __time_critical_func(bank_clr_put)(uint32_t data) { 213 | if (!active) { 214 | return; 215 | } 216 | offset = serial ? OFFSET_SERIAL : OFFSET_NORMAL; 217 | } 218 | 219 | static void __time_critical_func(bank_set_put)(uint32_t data) { 220 | if (!active) { 221 | return; 222 | } 223 | offset = OFFSET_SERIAL + (data << 11); 224 | } 225 | 226 | static void __time_critical_func(serial_false_put)(uint32_t data) { 227 | if (!active) { 228 | return; 229 | } 230 | serial = false; 231 | offset = OFFSET_NORMAL; 232 | } 233 | 234 | static void __time_critical_func(serial_true_put)(uint32_t data) { 235 | if (!active) { 236 | return; 237 | } 238 | serial = true; 239 | offset = OFFSET_SERIAL; 240 | } 241 | 242 | static void __time_critical_func(deactivate_put)(uint32_t data) { 243 | active = false; 244 | } 245 | 246 | static const void __not_in_flash("cffx_put")(*cffx_put[])(uint32_t) = { 247 | sp_data_put, sp_control_put, nop_put, nop_put, 248 | nop_put, nop_put, nop_put, nop_put, 249 | nop_put, basic_enter_put, basic_leave_put, bank_clr_put, 250 | bank_set_put, serial_false_put, serial_true_put, deactivate_put 251 | }; 252 | 253 | void __time_critical_func(board)(void) { 254 | 255 | a2pico_init(pio0); 256 | 257 | a2pico_resethandler(&reset); 258 | 259 | while (true) { 260 | uint32_t pico = a2pico_getaddr(pio0); 261 | uint32_t addr = pico & 0x0FFF; 262 | uint32_t io = pico & 0x0F00; // IOSTRB or IOSEL 263 | uint32_t strb = pico & 0x0800; // IOSTRB 264 | uint32_t read = pico & 0x1000; // R/W 265 | 266 | if (read) { 267 | if (addr >= 0x0FF0) { 268 | cffx_get[addr & 0xF](); 269 | } else if (!io) { 270 | devsel_get[addr & 0xF](); 271 | } else if (!strb || active) { 272 | a2pico_putdata(pio0, firmware[offset + addr]); 273 | } 274 | } else { 275 | uint32_t data = a2pico_getdata(pio0); 276 | if (addr >= 0x0FF0) { 277 | cffx_put[addr & 0xF](data); 278 | } else if (!io) { 279 | devsel_put[addr & 0xF](data); 280 | } 281 | } 282 | 283 | if (io && !strb) { 284 | active = true; 285 | self = addr; 286 | } 287 | } 288 | } 289 | 290 | uint8_t board_slot(void) { 291 | return self >> 8; 292 | } 293 | -------------------------------------------------------------------------------- /6502/SSC.CORE.S: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ; ; 3 | ; APPLE II SSC FIRMWARE ; 4 | ; ; 5 | ; BY LARRY KENYON ; 6 | ; ; 7 | ; -JANUARY 1981- ;;;;;;;;;;;;;; 8 | ; ; 9 | ; (C) COPYRIGHT 1981 BY APPLE COMPUTER, INC. ; 10 | ; ; 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ; ; 13 | ; CORE SUBROUTINES ; 14 | ; ; 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 17 | ; GENERAL PURPOSE WAIT ROUTINE ; 18 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 19 | ; 20 | ; WAITMS WAITS FOR [A-REG] MILLISECONDS (256 IF A-REG=0) 21 | ; 22 | WAITMS LDX #202 23 | WAITMS1 DEX ; 24 | BNE WAITMS1 ; 5 MICROSECOND LOOP 25 | SEC 26 | SBC #01 27 | BNE WAITMS 28 | LDX MSLOT 29 | RTS 30 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 31 | ; ACIA STATUS REGISTER READ ROUTINES ; 32 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 33 | ; 34 | ; SRIN USED TO CHECK ACIA INPUT STATUS 35 | ; 36 | SRIN LDY SLOT16 ; SLOT16=$N0 37 | LDA STREG,Y 38 | PHA 39 | AND #$20 ; DCD? 40 | LSR A ; AN ERROR IF NOT 41 | LSR A 42 | STA ZPTEMP 43 | PLA 44 | AND #$0F 45 | CMP #$08 ; SET CARRY IF RDR FULL, ELSE CLEAR 46 | BCC SRIN1 47 | AND #$07 ; PE, FE, OVR VALID ONLY WHEN RDR=1 48 | BCS SRIN2 ; 49 | SRIN1 LDA ZPTEMP 50 | SRIN2 ORA ZPTEMP ; GET DCD ERROR BIT 51 | BEQ SRIN3 ; BRANCH IF NO ERRORS FOUND 52 | ORA #$20 ; ELSE SET BIT 5 TO OFFSET FOR PASCAL 53 | STA STSBYTE,X ; AND SAVE IN STATUS TEMP 54 | SRIN3 RTS ; CY=1 MEANS DATA IS AVAILABLE 55 | ; 56 | ; SROUT CHECKS IF TDR IS EMPTY + HARDWARE HANDSHAKE IS OK 57 | ; 58 | SROUT LDY SLOT16 59 | LDA STREG,Y 60 | AND #$70 61 | CMP #$10 ; EQU IF TDR EMPTY, DCD, DSR, & CTS 62 | RTS 63 | 64 | ;;;;;;;;;;;;;;;;;;;;;;;;; 65 | ; GENERAL INPUT ROUTINE ; 66 | ;;;;;;;;;;;;;;;;;;;;;;;;; 67 | INPUT JSR SRIN 68 | BCC NOINPUT1 69 | 70 | LDA RDREG,Y ; GET THE ACIA INPUT 71 | ORA #$80 ; SET HI BIT FOR BASIC 72 | CMP #$8A ; LINEFEED? 73 | BNE INPUT2 74 | 75 | TAY 76 | LDA MISCFLG,X ; SEE IF WE SHOULD EAT IT 77 | AND #$20 78 | BNE NOINPUT ; IF SO, JUST KEEP IT A SECRET 79 | TYA 80 | 81 | INPUT2 SEC ; INDICATE DATA 82 | RTS 83 | 84 | NOINPUT CLC ; CARRY CLEAR FOR NO INPUT 85 | NOINPUT1 RTS 86 | 87 | ;;;;;;;;;;;;;;;;;;;;;;;;;; 88 | ; GENERAL OUTPUT ROUTINE ; 89 | ;;;;;;;;;;;;;;;;;;;;;;;;;; 90 | ; 91 | ; START OF COMMAND CHECK ROUTINE 92 | ; 93 | CMDSEQCK LDY SLOT16 94 | LDA DIPSW1,Y 95 | LSR A 96 | BCS NOCMD ; DON'T WORRY ABOUT CMD SEQ FOR SIC 97 | LDA STATEFLG,X 98 | AND #$07 ; ARE WE IN A COMMAND SEQUENCE? 99 | BEQ ESCCHECK 100 | JSR CMDPROC ; IF SO, GOTO COMMAND CENTRAL 101 | SEC ; INDICATE COMMAND 102 | RTS 103 | 104 | ESCCHECK LDA CHARACTER 105 | AND #$7F ; IGNORE HIGH BIT 106 | CMP CMDBYTE,X ; IS THIS BEGINNING OF A CMD SEQ? 107 | BNE XOFFCK 108 | INC STATEFLG,X ; START UP COMMAND MODES 109 | SEC ; INDICATE COMMAND 110 | RTS 111 | 112 | XOFFCK LDA MISCFLG,X ; IS XON ENABLED? 113 | AND #$08 114 | BEQ NOCMD ; SKIP THIS IF NOT 115 | 116 | JSR INPUT ; ANY INPUT? 117 | BCC NOCMD ; IF NOT, GO OUTPUT 118 | CMP #$93 ; IS IT AN XOFF? 119 | BEQ XONWAIT ; IF SO, GO WAIT FOR ANOTHER INPUT 120 | PHA 121 | LDA MISCFLG,X ; CIC MODE? 122 | LSR A 123 | LSR A 124 | PLA 125 | BCC ANRTS 126 | STA BUFBYTE,X ; IF SO, WE HAVE A BUFFER 127 | NOCMD CLC ; INDICATE NOT A CMD SEQ 128 | ANRTS RTS 129 | 130 | XONWAIT JSR GETCHAR ; GET ACIA/KBD DATA 131 | CMP #$91 ; IS IT AN XON? 132 | BNE XONWAIT ; IF NOT, WAIT 133 | CLC ; OTHERWISE, INDICATE NOT A CMD SEQ 134 | RTS ; AND RETURN 135 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 136 | ; NOW THE OUTPUT ROUTINE YOU'VE BEEN WAITING FOR ; 137 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 138 | OUTPUT JSR CMDSEQCK 139 | BCS ANRTS ; DON'T OUTPUT COMMAND SEQUENCES 140 | 141 | OUTPUT1 JSR SCREENOUT 142 | 143 | OUTPUT2 LDY SLOT16 144 | LDA DIPSW1,Y 145 | LSR A 146 | BCC OUTPUT3 ; SKIP ETX/ACK FOR NATIVE MODES 147 | LSR A 148 | BCC OUTPUT3 ; BRANCH IF NOT P8A EMULATION 149 | 150 | ;;;;;;;;;;;;;;;;;;;;; 151 | ; P8A ETX/ACK STUFF ; 152 | ;;;;;;;;;;;;;;;;;;;;; 153 | ; AFTER 148 CHARACTERS BUT NOT WITHIN AN ESCAPE SEQUENCE 154 | ; OF UP TO 5 CHARACTERS, THE HANDSHAKE IS PERFORMED 155 | ; (WILL DELAY UNTIL 'NOT ESC' AND THEN 4 MORE CHARS 156 | ; OR UNTIL AN 'ESC') 157 | 158 | P8AOUT1 LDA CHARACTER ; SAVE CHAR ON STACK 159 | PHA 160 | LDA HANDSHKE,X ; CHAR COUNT FOR BUFFER FULL 161 | CMP #103 ; IF <103 THEN 153 CHARS IN BUFFER 162 | BCC ETX 163 | CMP #108 ; IF >=108 THEN LESS THAN 149 CHARS 164 | BCS P8AOUT2 ; SO NO HANDSHAKE IS NEEDED YET 165 | CMP #107 ; SETS CARRY IF 107 (149 SENT) 166 | PLA 167 | PHA 168 | EOR #$9B ; ESC? 169 | AND #$7F ; IGNORE HI-BIT 170 | BNE P8AOUT2 ; COUNT AS 1 OF 5 IF NOT 'ESC' 171 | BCS P8AOUT3 ; DON'T COUNT IF 149TH CHAR IS 'ESC' 172 | 173 | ETX LDA STATEFLG,X ; SEND QUERY CHAR TO PRINTER 174 | AND #$1F ; (DEFAULT IS ETX) 175 | ORA #$80 176 | STA CHARACTER 177 | JSR ACIAOUT 178 | ACK JSR GETCHAR ; GET ACIA/KBD DATA 179 | EOR #$86 ; ACK? 180 | BNE ETX ; IF NOT ACK, REPEAT HANDSHAKE 181 | STA HANDSHKE,X ; INIT CHAR COUNT TO 255 182 | 183 | P8AOUT2 DEC HANDSHKE,X 184 | P8AOUT3 PLA ; GET REAL CHAR TO OUTPUT 185 | STA CHARACTER 186 | EOR #$8D ; IF CR AND CR DELAY MODE 187 | ASL A 188 | BNE P8AOUT4 ; THEN FAKE CHAR COUNT TO LESS THAN 189 | LDA DELAYFLG,X ; 48 TO FORCE HANDSHAKE ON NEXT 190 | AND #$30 ; CHARACTER OUT 191 | BEQ P8AOUT4 192 | STA HANDSHKE,X 193 | 194 | P8AOUT4 JSR ACIAOUT 195 | JMP LFGEN ; (SKIP DELAYS) 196 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 197 | ; AND BACK TO NORMAL OUTPUT ; 198 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 199 | OUTPUT3 JSR ACIAOUT ; OUTPUT THE CHARACTER 200 | ; 201 | ; NOW CHECK FOR CR, LF, AND FF DELAYS 202 | ; 203 | ASL A 204 | TAY 205 | LDA DELAYFLG,X ; GET DELAY FLAGS 206 | CPY #$18 ; FORM FEED? 207 | BEQ OUTDLY1 208 | LSR A 209 | LSR A ; RIGHT JUSTIFY LF DELAY 210 | CPY #$14 ; LINE FEED? 211 | BEQ OUTDLY1 212 | LSR A 213 | LSR A ; RIGHT JUSTIFY CR DELAY 214 | CPY #$1A ; CARRIAGE RETURN? 215 | BNE OUTPUTEND 216 | OUTDLY1 AND #$03 ; JUST WANT LOWEST 2 BITS 217 | BEQ LFGEN ; NO DELAY INDICATED 218 | TAY 219 | LDA DLYTBL-1,Y 220 | TAY ; DELAY IN 32 MSEC INCREMENTS 221 | OUTDLYLP LDA #32 222 | JSR WAITMS 223 | DEY 224 | BNE OUTDLYLP 225 | ; 226 | ; CHECK ON LF GENERATION OPTION 227 | ; 228 | LFGEN LDA CHARACTER 229 | ASL A 230 | CMP #$1A ; CARRIAGE RETURN? 231 | BNE OUTPUTEND 232 | LDA MISCFLG,X ; IS LF GENERATE ENABLED? 233 | ROR A 234 | BCC OUTPUTEND 235 | LDA #$8A 236 | STA CHARACTER ; LINE FEED 237 | JMP OUTPUT2 ; (DON'T ECHO IT) 238 | OUTPUTEND RTS 239 | 240 | DLYTBL DFB $01 ; 32 MSEC 241 | DFB $08 ; 1/4 SEC 242 | DFB $40 ; 2 SEC 243 | ;;;;;;;;;;;;;;;;;;;;;;; 244 | ; ACIA OUTPUT ROUTINE ; 245 | ;;;;;;;;;;;;;;;;;;;;;;; 246 | ACIAOUT JSR SROUT ; READY FOR OUTPUT? 247 | BNE ACIAOUT 248 | TYA 249 | ORA # 16 | #include 17 | // 18 | #include "pico/stdlib.h" 19 | #include "pico/mutex.h" 20 | #include "pico/sem.h" 21 | // 22 | #include "my_debug.h" 23 | #include "hw_config.h" 24 | // 25 | #include "spi.h" 26 | 27 | static bool irqChannel1 = false; 28 | static bool irqShared = true; 29 | 30 | static void in_spi_irq_handler(const uint DMA_IRQ_num, io_rw_32 *dma_hw_ints_p) { 31 | for (size_t i = 0; i < spi_get_num(); ++i) { 32 | spi_t *spi_p = spi_get_by_num(i); 33 | if (DMA_IRQ_num == spi_p->DMA_IRQ_num) { 34 | // Is the SPI's channel requesting interrupt? 35 | if (*dma_hw_ints_p & (1 << spi_p->rx_dma)) { 36 | *dma_hw_ints_p = 1 << spi_p->rx_dma; // Clear it. 37 | assert(!dma_channel_is_busy(spi_p->rx_dma)); 38 | assert(!sem_available(&spi_p->sem)); 39 | bool ok = sem_release(&spi_p->sem); 40 | assert(ok); 41 | } 42 | } 43 | } 44 | } 45 | static void __not_in_flash_func(spi_irq_handler_0)() { 46 | in_spi_irq_handler(DMA_IRQ_0, &dma_hw->ints0); 47 | } 48 | static void __not_in_flash_func(spi_irq_handler_1)() { 49 | in_spi_irq_handler(DMA_IRQ_1, &dma_hw->ints1); 50 | } 51 | 52 | void set_spi_dma_irq_channel(bool useChannel1, bool shared) { 53 | irqChannel1 = useChannel1; 54 | irqShared = shared; 55 | } 56 | 57 | // SPI Transfer: Read & Write (simultaneously) on SPI bus 58 | // If the data that will be received is not important, pass NULL as rx. 59 | // If the data that will be transmitted is not important, 60 | // pass NULL as tx and then the SPI_FILL_CHAR is sent out as each data 61 | // element. 62 | bool spi_transfer(spi_t *spi_p, const uint8_t *tx, uint8_t *rx, size_t length) { 63 | // assert(512 == length || 1 == length); 64 | assert(tx || rx); 65 | // assert(!(tx && rx)); 66 | 67 | // tx write increment is already false 68 | if (tx) { 69 | channel_config_set_read_increment(&spi_p->tx_dma_cfg, true); 70 | } else { 71 | static const uint8_t dummy = SPI_FILL_CHAR; 72 | tx = &dummy; 73 | channel_config_set_read_increment(&spi_p->tx_dma_cfg, false); 74 | } 75 | 76 | // rx read increment is already false 77 | if (rx) { 78 | channel_config_set_write_increment(&spi_p->rx_dma_cfg, true); 79 | } else { 80 | static uint8_t dummy = 0xA5; 81 | rx = &dummy; 82 | channel_config_set_write_increment(&spi_p->rx_dma_cfg, false); 83 | } 84 | 85 | dma_channel_configure(spi_p->tx_dma, &spi_p->tx_dma_cfg, 86 | &spi_get_hw(spi_p->hw_inst)->dr, // write address 87 | tx, // read address 88 | length, // element count (each element is of 89 | // size transfer_data_size) 90 | false); // start 91 | dma_channel_configure(spi_p->rx_dma, &spi_p->rx_dma_cfg, 92 | rx, // write address 93 | &spi_get_hw(spi_p->hw_inst)->dr, // read address 94 | length, // element count (each element is of 95 | // size transfer_data_size) 96 | false); // start 97 | 98 | switch (spi_p->DMA_IRQ_num) { 99 | case DMA_IRQ_0: 100 | assert(!dma_channel_get_irq0_status(spi_p->rx_dma)); 101 | break; 102 | case DMA_IRQ_1: 103 | assert(!dma_channel_get_irq1_status(spi_p->rx_dma)); 104 | break; 105 | default: 106 | assert(false); 107 | } 108 | sem_reset(&spi_p->sem, 0); 109 | 110 | // start them exactly simultaneously to avoid races (in extreme cases 111 | // the FIFO could overflow) 112 | dma_start_channel_mask((1u << spi_p->tx_dma) | (1u << spi_p->rx_dma)); 113 | 114 | /* Wait until master completes transfer or time out has occured. */ 115 | uint32_t timeOut = 1000; /* Timeout 1 sec */ 116 | bool rc = sem_acquire_timeout_ms( 117 | &spi_p->sem, timeOut); // Wait for notification from ISR 118 | if (!rc) { 119 | // If the timeout is reached the function will return false 120 | DBG_PRINTF("Notification wait timed out in %s\n", __FUNCTION__); 121 | return false; 122 | } 123 | // Shouldn't be necessary: 124 | dma_channel_wait_for_finish_blocking(spi_p->tx_dma); 125 | dma_channel_wait_for_finish_blocking(spi_p->rx_dma); 126 | 127 | assert(!sem_available(&spi_p->sem)); 128 | assert(!dma_channel_is_busy(spi_p->tx_dma)); 129 | assert(!dma_channel_is_busy(spi_p->rx_dma)); 130 | 131 | return true; 132 | } 133 | 134 | void spi_lock(spi_t *spi_p) { 135 | assert(mutex_is_initialized(&spi_p->mutex)); 136 | mutex_enter_blocking(&spi_p->mutex); 137 | } 138 | void spi_unlock(spi_t *spi_p) { 139 | assert(mutex_is_initialized(&spi_p->mutex)); 140 | mutex_exit(&spi_p->mutex); 141 | } 142 | 143 | bool my_spi_init(spi_t *spi_p) { 144 | auto_init_mutex(my_spi_init_mutex); 145 | mutex_enter_blocking(&my_spi_init_mutex); 146 | if (!spi_p->initialized) { 147 | //// The SPI may be shared (using multiple SSs); protect it 148 | //spi_p->mutex = xSemaphoreCreateRecursiveMutex(); 149 | //xSemaphoreTakeRecursive(spi_p->mutex, portMAX_DELAY); 150 | if (!mutex_is_initialized(&spi_p->mutex)) mutex_init(&spi_p->mutex); 151 | spi_lock(spi_p); 152 | 153 | // Default: 154 | if (!spi_p->baud_rate) 155 | spi_p->baud_rate = 10 * 1000 * 1000; 156 | // For the IRQ notification: 157 | sem_init(&spi_p->sem, 0, 1); 158 | 159 | /* Configure component */ 160 | // Enable SPI at 100 kHz and connect to GPIOs 161 | spi_init(spi_p->hw_inst, 100 * 1000); 162 | spi_set_format(spi_p->hw_inst, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); 163 | 164 | gpio_set_function(spi_p->miso_gpio, GPIO_FUNC_SPI); 165 | gpio_set_function(spi_p->mosi_gpio, GPIO_FUNC_SPI); 166 | gpio_set_function(spi_p->sck_gpio, GPIO_FUNC_SPI); 167 | // ss_gpio is initialized in sd_init_driver() 168 | 169 | // Slew rate limiting levels for GPIO outputs. 170 | // enum gpio_slew_rate { GPIO_SLEW_RATE_SLOW = 0, GPIO_SLEW_RATE_FAST = 1 } 171 | // void gpio_set_slew_rate (uint gpio,enum gpio_slew_rate slew) 172 | // Default appears to be GPIO_SLEW_RATE_SLOW. 173 | 174 | // Drive strength levels for GPIO outputs. 175 | // enum gpio_drive_strength { GPIO_DRIVE_STRENGTH_2MA = 0, GPIO_DRIVE_STRENGTH_4MA = 1, GPIO_DRIVE_STRENGTH_8MA = 2, 176 | // GPIO_DRIVE_STRENGTH_12MA = 3 } 177 | // enum gpio_drive_strength gpio_get_drive_strength (uint gpio) 178 | if (spi_p->set_drive_strength) { 179 | gpio_set_drive_strength(spi_p->mosi_gpio, spi_p->mosi_gpio_drive_strength); 180 | gpio_set_drive_strength(spi_p->sck_gpio, spi_p->sck_gpio_drive_strength); 181 | } 182 | 183 | // SD cards' DO MUST be pulled up. 184 | gpio_pull_up(spi_p->miso_gpio); 185 | 186 | // Grab some unused dma channels 187 | spi_p->tx_dma = dma_claim_unused_channel(true); 188 | spi_p->rx_dma = dma_claim_unused_channel(true); 189 | 190 | spi_p->tx_dma_cfg = dma_channel_get_default_config(spi_p->tx_dma); 191 | spi_p->rx_dma_cfg = dma_channel_get_default_config(spi_p->rx_dma); 192 | channel_config_set_transfer_data_size(&spi_p->tx_dma_cfg, DMA_SIZE_8); 193 | channel_config_set_transfer_data_size(&spi_p->rx_dma_cfg, DMA_SIZE_8); 194 | 195 | // We set the outbound DMA to transfer from a memory buffer to the SPI 196 | // transmit FIFO paced by the SPI TX FIFO DREQ The default is for the 197 | // read address to increment every element (in this case 1 byte - 198 | // DMA_SIZE_8) and for the write address to remain unchanged. 199 | channel_config_set_dreq(&spi_p->tx_dma_cfg, spi_get_index(spi_p->hw_inst) 200 | ? DREQ_SPI1_TX 201 | : DREQ_SPI0_TX); 202 | channel_config_set_write_increment(&spi_p->tx_dma_cfg, false); 203 | 204 | // We set the inbound DMA to transfer from the SPI receive FIFO to a 205 | // memory buffer paced by the SPI RX FIFO DREQ We coinfigure the read 206 | // address to remain unchanged for each element, but the write address 207 | // to increment (so data is written throughout the buffer) 208 | channel_config_set_dreq(&spi_p->rx_dma_cfg, spi_get_index(spi_p->hw_inst) 209 | ? DREQ_SPI1_RX 210 | : DREQ_SPI0_RX); 211 | channel_config_set_read_increment(&spi_p->rx_dma_cfg, false); 212 | 213 | /* Theory: we only need an interrupt on rx complete, 214 | since if rx is complete, tx must also be complete. */ 215 | 216 | /* Configure the processor to run dma_handler() when DMA IRQ 0/1 is asserted */ 217 | 218 | spi_p->DMA_IRQ_num = irqChannel1 ? DMA_IRQ_1 : DMA_IRQ_0; 219 | 220 | // Tell the DMA to raise IRQ line 0/1 when the channel finishes a block 221 | static void (*spi_irq_handler_p)(); 222 | switch (spi_p->DMA_IRQ_num) { 223 | case DMA_IRQ_0: 224 | spi_irq_handler_p = spi_irq_handler_0; 225 | dma_channel_set_irq0_enabled(spi_p->rx_dma, true); 226 | dma_channel_set_irq0_enabled(spi_p->tx_dma, false); 227 | break; 228 | case DMA_IRQ_1: 229 | spi_irq_handler_p = spi_irq_handler_1; 230 | dma_channel_set_irq1_enabled(spi_p->rx_dma, true); 231 | dma_channel_set_irq1_enabled(spi_p->tx_dma, false); 232 | break; 233 | default: 234 | assert(false); 235 | } 236 | if (irqShared) { 237 | irq_add_shared_handler( 238 | spi_p->DMA_IRQ_num, *spi_irq_handler_p, 239 | PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); 240 | } else { 241 | irq_set_exclusive_handler(spi_p->DMA_IRQ_num, *spi_irq_handler_p); 242 | } 243 | irq_set_enabled(spi_p->DMA_IRQ_num, true); 244 | LED_INIT(); 245 | spi_p->initialized = true; 246 | spi_unlock(spi_p); 247 | } 248 | mutex_exit(&my_spi_init_mutex); 249 | return true; 250 | } 251 | 252 | /* [] END OF FILE */ 253 | -------------------------------------------------------------------------------- /sp.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2024 Oliver Schmidt (https://a2retro.de/) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "board.h" 32 | #include "firmware.h" 33 | #include "config.h" 34 | #include "hdd.h" 35 | 36 | #include "sp.h" 37 | 38 | #define PRODOS_CMD_STATUS 0x00 39 | #define PRODOS_CMD_READ 0x01 40 | #define PRODOS_CMD_WRITE 0x02 41 | 42 | #define PRODOS_I_CMD 0 43 | #define PRODOS_I_UNIT 1 44 | #define PRODOS_I_BUFF 2 45 | #define PRODOS_I_BLOCK 4 46 | #define PRODOS_I_BUFFER 6 47 | 48 | #define PRODOS_O_RETVAL 0 49 | #define PRODOS_O_BUFFER 1 50 | 51 | #define SP_CMD_STATUS 0x00 52 | #define SP_CMD_READBLK 0x01 53 | #define SP_CMD_WRITEBLK 0x02 54 | #define SP_CMD_FORMAT 0x03 55 | #define SP_CMD_CONTROL 0x04 56 | #define SP_CMD_INIT 0x05 57 | #define SP_CMD_OPEN 0x06 58 | #define SP_CMD_CLOSE 0x07 59 | #define SP_CMD_READ 0x08 60 | #define SP_CMD_WRITE 0x09 61 | 62 | #define SP_I_CMD 0 63 | #define SP_I_PARAMS 2 64 | #define SP_I_BUFFER 10 65 | 66 | #define SP_O_RETVAL 0 67 | #define SP_O_BUFFER 1 68 | 69 | #define SP_PARAM_UNIT 0 70 | #define SP_PARAM_BUFF 1 71 | #define SP_PARAM_CODE 3 72 | #define SP_PARAM_BLOCK 3 73 | 74 | #define SP_STATUS_STS 0x00 75 | #define SP_STATUS_DCB 0x01 76 | #define SP_STATUS_NLS 0x02 77 | #define SP_STATUS_DIB 0x03 78 | 79 | #define SP_SUCCESS 0x00 80 | #define SP_BADCMD 0x01 81 | #define SP_BUSERR 0x06 82 | #define SP_BADCTL 0x21 83 | 84 | volatile uint8_t sp_control; 85 | volatile uint8_t sp_buffer[0x0400]; 86 | volatile uint16_t sp_read_offset; 87 | volatile uint16_t sp_write_offset; 88 | 89 | static uint8_t unit_to_drive(uint8_t unit) { 90 | uint8_t drive = unit >> 7; 91 | if ((unit >> 4 & 0x07) != board_slot()) { 92 | drive += 0x02; 93 | } 94 | return drive; 95 | } 96 | 97 | void sp_init(void) { 98 | hdd_init(); 99 | 100 | #ifdef PICO_DEFAULT_LED_PIN 101 | gpio_init(PICO_DEFAULT_LED_PIN); 102 | gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); 103 | #endif 104 | } 105 | 106 | void __time_critical_func(sp_reset)(void) { 107 | sp_control = CONTROL_NONE; 108 | sp_read_offset = sp_write_offset = 0; 109 | sp_buffer[0] = sp_buffer[1] = 0; 110 | } 111 | 112 | void static sp_gen_read_page(uint16_t addr, const uint8_t *buffer, int offset) { 113 | int last_data = -1; 114 | 115 | for (int i = 0; i < 0x0100; i++) { 116 | uint8_t data = buffer[i]; 117 | 118 | if (last_data != data) { 119 | last_data = data; 120 | 121 | firmware[offset++] = 0xA9; // LDA 122 | firmware[offset++] = data; 123 | } 124 | 125 | firmware[offset++] = 0x8D; // STA 126 | firmware[offset++] = addr & 0xFF; 127 | firmware[offset++] = addr >> 8; 128 | 129 | addr++; 130 | } 131 | 132 | firmware[offset] = 0x60; // RTS 133 | } 134 | 135 | void static sp_gen_read_block(uint16_t addr, const uint8_t *buffer) { 136 | // printf("SP Read(Addr=$%04X)\n", addr); 137 | sp_gen_read_page(addr + 0x0000, buffer + 0x0000, OFFSET_BANK_2); 138 | sp_gen_read_page(addr + 0x0100, buffer + 0x0100, OFFSET_BANK_3); 139 | } 140 | 141 | static uint8_t sp_stat(uint8_t *params, uint8_t *stat_list) { 142 | if (!params[SP_PARAM_UNIT]) { 143 | if (params[SP_PARAM_CODE] == SP_STATUS_STS) { 144 | printf("SP CmdStatus(Device=Smartport)\n"); 145 | stat_list[2 + 0] = config_drives(); 146 | stat_list[2 + 1] = 0b01000000; // no interrupt sent 147 | memset(&stat_list[2 + 2], 0x00, 6); 148 | stat_list[0] = 8; // size header low 149 | stat_list[1] = 0; // size header high 150 | } else { 151 | return SP_BADCTL; 152 | } 153 | } else { 154 | if (params[SP_PARAM_CODE] == SP_STATUS_STS || 155 | params[SP_PARAM_CODE] == SP_STATUS_DIB) { 156 | bool status = params[SP_PARAM_CODE] == SP_STATUS_STS; 157 | if (!hdd_status(params[SP_PARAM_UNIT] - 1, &stat_list[2 + 1])) { 158 | stat_list[2 + 0] = hdd_protected(params[SP_PARAM_UNIT] - 1) 159 | ? 0b11110100 // block, write, read, online, protected 160 | : 0b11110000; // block, write, read, online 161 | } else { 162 | stat_list[2 + 0] = 0b11100000; // block, write, read 163 | stat_list[2 + 1] = 0x00; // blocks low 164 | stat_list[2 + 2] = 0x00; // blocks mid 165 | } 166 | stat_list[2 + 3] = 0x00; // blocks high 167 | if (status) { 168 | stat_list[0] = 4; // size header low 169 | stat_list[1] = 0; // size header high 170 | } else { 171 | stat_list[2 + 4] = 0x0A; // id string length 172 | memcpy(&stat_list[2 + 5], "A2RETRONET ", 16); 173 | stat_list[2 + 21] = 0x02; // hard disk 174 | stat_list[2 + 22] = 0x00; // removable 175 | stat_list[2 + 23] = 0x01; // firmware version low 176 | stat_list[2 + 24] = 0x00; // firmware version high 177 | stat_list[0] = 25; // size header low 178 | stat_list[1] = 0; // size header high 179 | } 180 | } else { 181 | return SP_BADCTL; 182 | } 183 | } 184 | return SP_SUCCESS; 185 | } 186 | 187 | static uint8_t sp_readblk(uint8_t *params, uint8_t *buffer) { 188 | return hdd_read(params[SP_PARAM_UNIT] - 1, *(uint16_t*)¶ms[SP_PARAM_BLOCK], buffer); 189 | } 190 | 191 | static uint8_t sp_writeblk(uint8_t *params, const uint8_t *buffer) { 192 | return hdd_write(params[SP_PARAM_UNIT] - 1, *(uint16_t*)¶ms[SP_PARAM_BLOCK], buffer); 193 | } 194 | 195 | void sp_task(void) { 196 | 197 | if (sp_control == CONTROL_NONE || sp_control == CONTROL_DONE) { 198 | return; 199 | } 200 | 201 | if (!hdd_sd_mounted() && !hdd_usb_mounted()) { 202 | return; 203 | } 204 | 205 | if (sp_control == CONTROL_CONFIG) { 206 | config(); 207 | return; 208 | } 209 | 210 | #ifdef PICO_DEFAULT_LED_PIN 211 | gpio_put(PICO_DEFAULT_LED_PIN, true); 212 | #endif 213 | 214 | // printf("SP Cmd(Type=$%02X,Bytes=$%04X)\n", sp_control, sp_write_offset); 215 | switch (sp_control) { 216 | 217 | case CONTROL_PRODOS: 218 | switch (sp_buffer[PRODOS_I_CMD]) { 219 | case PRODOS_CMD_STATUS: 220 | sp_buffer[PRODOS_O_RETVAL] = hdd_status(unit_to_drive(sp_buffer[PRODOS_I_UNIT]), 221 | (uint8_t*)&sp_buffer[PRODOS_O_BUFFER]); 222 | break; 223 | case PRODOS_CMD_READ: 224 | sp_buffer[PRODOS_O_RETVAL] = hdd_read(unit_to_drive(sp_buffer[PRODOS_I_UNIT]), 225 | *(uint16_t*)&sp_buffer[PRODOS_I_BLOCK], 226 | (uint8_t*)&sp_buffer[0x0200]); 227 | sp_gen_read_block(*(uint16_t*)&sp_buffer[PRODOS_I_BUFF], 228 | (uint8_t*)&sp_buffer[0x0200]); 229 | break; 230 | case PRODOS_CMD_WRITE: 231 | sp_buffer[PRODOS_O_RETVAL] = hdd_write(unit_to_drive(sp_buffer[PRODOS_I_UNIT]), 232 | *(uint16_t*)&sp_buffer[PRODOS_I_BLOCK], 233 | (uint8_t*)&sp_buffer[PRODOS_I_BUFFER]); 234 | break; 235 | } 236 | break; 237 | 238 | case CONTROL_SP: 239 | switch (sp_buffer[SP_I_CMD]) { 240 | case SP_CMD_STATUS: 241 | // printf("SP CmdStatus(Device=$%02X)\n", sp_buffer[SP_I_PARAMS]); 242 | sp_buffer[SP_O_RETVAL] = sp_stat((uint8_t*)&sp_buffer[SP_I_PARAMS], 243 | (uint8_t*)&sp_buffer[SP_O_BUFFER]); 244 | break; 245 | case SP_CMD_READBLK: 246 | // printf("SP CmdReadBlock(Device=$%02X)\n", sp_buffer[SP_I_PARAMS]); 247 | sp_buffer[SP_O_RETVAL] = sp_readblk((uint8_t*)&sp_buffer[SP_I_PARAMS], 248 | (uint8_t*)&sp_buffer[0x0200]); 249 | sp_gen_read_block(*(uint16_t*)&sp_buffer[SP_I_PARAMS + SP_PARAM_BUFF], 250 | (uint8_t*)&sp_buffer[0x0200]); 251 | break; 252 | case SP_CMD_WRITEBLK: 253 | // printf("SP CmdWriteBlock(Device=$%02X)\n", sp_buffer[SP_I_PARAMS]); 254 | sp_buffer[SP_O_RETVAL] = sp_writeblk((uint8_t*)&sp_buffer[SP_I_PARAMS], 255 | (uint8_t*)&sp_buffer[SP_I_BUFFER]); 256 | break; 257 | case SP_CMD_FORMAT: 258 | printf("SP CmdFormat(Device=$%02X)\n", sp_buffer[SP_I_PARAMS]); 259 | sp_buffer[SP_O_RETVAL] = SP_BADCMD; 260 | break; 261 | case SP_CMD_CONTROL: 262 | printf("SP CmdControl(Device=$%02X)\n", sp_buffer[SP_I_PARAMS]); 263 | sp_buffer[SP_O_RETVAL] = SP_BADCMD; 264 | break; 265 | case SP_CMD_INIT: 266 | printf("SP CmdInit(Device=$%02X)\n", sp_buffer[SP_I_PARAMS]); 267 | sp_buffer[SP_O_RETVAL] = SP_SUCCESS; 268 | break; 269 | case SP_CMD_OPEN: 270 | printf("SP CmdOpen(Device=$%02X)\n", sp_buffer[SP_I_PARAMS]); 271 | sp_buffer[SP_O_RETVAL] = SP_BADCMD; 272 | break; 273 | case SP_CMD_CLOSE: 274 | printf("SP CmdClose(Device=$%02X)\n", sp_buffer[SP_I_PARAMS]); 275 | sp_buffer[SP_O_RETVAL] = SP_BADCMD; 276 | break; 277 | case SP_CMD_READ: 278 | printf("SP CmdRead(Device=$%02X)\n", sp_buffer[SP_I_PARAMS]); 279 | sp_buffer[SP_O_RETVAL] = SP_BADCMD; 280 | break; 281 | case SP_CMD_WRITE: 282 | printf("SP CmdWrite(Device=$%02X)\n", sp_buffer[SP_I_PARAMS]); 283 | sp_buffer[SP_O_RETVAL] = SP_BADCMD; 284 | break; 285 | } 286 | break; 287 | } 288 | 289 | sp_read_offset = sp_write_offset = 0; 290 | sp_control = CONTROL_DONE; 291 | 292 | #ifdef PICO_DEFAULT_LED_PIN 293 | gpio_put(PICO_DEFAULT_LED_PIN, false); 294 | #endif 295 | } 296 | --------------------------------------------------------------------------------