Compare commits

...

12 Commits

Author SHA1 Message Date
Eric Helgeson bf5823468a Add 50pin 1.1 case back, was accidentally removed in 57ff0cd3b4 2024-02-22 07:30:57 -06:00
Eric Helgeson 2baa0155e1
Merge pull request #262 from dotsam/better-logging
Reformatted Logging
2024-02-17 15:59:33 -06:00
Sam Edwards 3a9db99723 Changing indentation on log lines 2024-02-17 11:23:17 -08:00
Sam Edwards 70eda86484 Ensure log output is correct with used IDs 2024-02-17 11:23:17 -08:00
Sam Edwards 9d4207ea14 Only read INI per-device config once in "finalize" function, include filenames in output 2024-02-17 11:23:17 -08:00
Sam Edwards 1d49003fe1 Clean up logging in setup functions 2024-02-17 11:22:27 -08:00
Sam Edwards 0a30396c3b Make better use of macros, functions, and dynamic char array lengths 2024-02-17 11:21:26 -08:00
Sam Edwards ba06827814 Whitespace/Type cleanup 2024-02-17 11:21:26 -08:00
Eric Helgeson a45af3edec
Merge pull request #261 from WilliamLeara/fix_typographical_errors
fix typographical mistakes in comments
2024-02-17 12:05:27 -06:00
William Leara 0be37ccde0 fix typographical mistakes in comments
Also, fix up some white-space issues.  No functional change here.
2024-02-14 13:31:35 -06:00
Eric Helgeson e236b5b054
Merge pull request #252 from dotsam/easy-leds
Easier to change LEDs
2024-02-09 20:11:36 -06:00
Sam Edwards 47179c5fd0
Easier to change LEDs
- For building for boards with different LEDs and configurations
 - Define mode for LED
 - Update GPIO macros for port C
 - Use macros to generate bits for registers
2024-02-09 17:42:07 -08:00
3 changed files with 287 additions and 263 deletions

View File

@ -116,7 +116,7 @@ SCSI_COMMAND_HANDLER(onSendFileEnd);
static void flashError(const unsigned error);
void onBusReset(void);
void initFileLog(void);
void finalizeFileLog(void);
void finalizeDevices(void);
void findDriveImages(FsFile root);
/*
@ -138,7 +138,7 @@ inline byte readIO(void)
// Read config file for per device settings
void readSCSIDeviceConfig(uint8_t scsi_id, SCSI_DEVICE *dev) {
SCSI_INQUIRY_DATA *iq = &dev->inquiry_block;
char section[6] = {'S', 'C', 'S', 'I', 0, 0};
char section[] = "SCSI0";
FsFile config_file;
char *buf = (char *)&m_scsi_buf;
@ -147,6 +147,9 @@ void readSCSIDeviceConfig(uint8_t scsi_id, SCSI_DEVICE *dev) {
return;
}
LOG_FILE.println();
LOG_FILE.print("\t\tINI Overrides: ");
// create section name from id
section[4] = INT_TO_CHAR(scsi_id);
@ -155,13 +158,13 @@ void readSCSIDeviceConfig(uint8_t scsi_id, SCSI_DEVICE *dev) {
case 0:
dev->m_type = SCSI_DEVICE_HDD;
memcpy(iq, &default_hdd, sizeof(default_hdd));
LOG_FILE.println("Forced HDD");
LOG_FILE.print("Forced HDD");
break;
case 2:
dev->m_type = SCSI_DEVICE_OPTICAL;
memcpy(iq, &default_optical, sizeof(default_optical));
LOG_FILE.println("Forced Optical");
LOG_FILE.print("Forced CDROM");
break;
case 99:
@ -169,25 +172,25 @@ void readSCSIDeviceConfig(uint8_t scsi_id, SCSI_DEVICE *dev) {
break;
default:
LOG_FILE.println("Unsupported override type");
LOG_FILE.print("Unsupported type override");
}
if(ini_gets(section, "vendor", NULL, buf, SCSI_BUF_SIZE, BLUESCSI_INI)) {
memcpy(iq->vendor, buf, SCSI_VENDOR_LENGTH);
LOG_FILE.print("vendor:");
LOG_FILE.println(buf);
LOG_FILE.print(" / Vendor: ");
LOG_FILE.print(buf);
}
if(ini_gets(section, "product", NULL, buf, SCSI_BUF_SIZE, BLUESCSI_INI)) {
memcpy(iq->product, buf, SCSI_PRODUCT_LENGTH);
LOG_FILE.print("product:");
LOG_FILE.println(buf);
LOG_FILE.print(" / Product: ");
LOG_FILE.print(buf);
}
if(ini_gets(section, "revision", NULL, buf, SCSI_BUF_SIZE, BLUESCSI_INI)) {
memcpy(iq->revision, buf, SCSI_REVISION_LENGTH);
LOG_FILE.print("revision:");
LOG_FILE.println(buf);
LOG_FILE.print(" / Revision: ");
LOG_FILE.print(buf);
}
}
@ -196,42 +199,39 @@ void readSDCardInfo(int success_mhz)
{
cid_t sd_cid;
LOG_FILE.println("SDCard Info:");
LOG_FILE.print(" Format:");
LOG_FILE.println("SD Card Info:");
LOG_FILE.print(" SPI Speed: ");
LOG_FILE.print(success_mhz);
LOG_FILE.println("MHz");
LOG_FILE.print(" Format: ");
switch(SD.vol()->fatType()) {
case FAT_TYPE_EXFAT:
LOG_FILE.println("exFAT");
break;
default:
LOG_FILE.print("FAT32/16/12 - exFAT may improve performance");
LOG_FILE.println("FAT32/16/12 - exFAT may improve performance");
}
LOG_FILE.print("SPI speed: ");
LOG_FILE.print(success_mhz);
LOG_FILE.println("Mhz");
LOG_FILE.print(" Max Filename Length:");
LOG_FILE.print(" Max Filename Length: ");
LOG_FILE.println(MAX_FILE_PATH);
if(SD.card()->readCID(&sd_cid))
{
LOG_FILE.print(" MID:");
LOG_FILE.print(" MID: ");
LOG_FILE.print(sd_cid.mid, 16);
LOG_FILE.print(" OID:");
LOG_FILE.print(sd_cid.oid[0]);
LOG_FILE.println(sd_cid.oid[1]);
LOG_FILE.print(" OID: ");
LOG_FILE.print(sd_cid.oid[0]); LOG_FILE.println(sd_cid.oid[1]);
LOG_FILE.print(" Name:");
LOG_FILE.print(sd_cid.pnm[0]);
LOG_FILE.print(sd_cid.pnm[1]);
LOG_FILE.print(sd_cid.pnm[2]);
LOG_FILE.print(sd_cid.pnm[3]);
LOG_FILE.print(sd_cid.pnm[4]);
LOG_FILE.print(" Name: ");
for (uint8_t i = 0; i < 5; i++) {
LOG_FILE.print(sd_cid.pnm[i]);
}
LOG_FILE.print(" Date:");
LOG_FILE.print(" Date: ");
LOG_FILE.print(sd_cid.mdtMonth());
LOG_FILE.print("/");
LOG_FILE.println(sd_cid.mdtYear());
LOG_FILE.print(" Serial:");
LOG_FILE.print(" Serial: ");
LOG_FILE.println(sd_cid.psn());
}
LOG_FILE.sync();
@ -271,16 +271,16 @@ bool hddimageOpen(SCSI_DEVICE *dev, FsFile *file,int id,int lun,int blocksize)
dev->m_fileSize = dev->m_file.size();
if(dev->m_fileSize < 1) {
LOG_FILE.println(" - file is 0 bytes, can not use.");
LOG_FILE.println(" ERROR: 0 byte file");
goto failed;
}
if(!dev->m_file.isContiguous())
{
LOG_FILE.println(" - file is fragmented, see https://github.com/erichelgeson/BlueSCSI/wiki/Image-File-Fragmentation");
LOG_FILE.println(" WARNING: Fragmented, see https://github.com/erichelgeson/BlueSCSI/wiki/Image-File-Fragmentation");
}
if(dev->m_type == SCSI_DEVICE_OPTICAL) {
LOG_FILE.print(" CDROM");
dev->m_blocksize = CDROM_COMMON_SECTORSIZE;
// Borrowed from PCEM
@ -303,6 +303,7 @@ bool hddimageOpen(SCSI_DEVICE *dev, FsFile *file,int id,int lun,int blocksize)
// Last ditch effort
// size must be less than 700MB
if(dev->m_fileSize > 912579600) {
LOG_FILE.println(" ERROR: ISO too large!");
goto failed;
}
@ -314,32 +315,29 @@ bool hddimageOpen(SCSI_DEVICE *dev, FsFile *file,int id,int lun,int blocksize)
dev->m_rawblocksize = CDROM_COMMON_SECTORSIZE;
} else {
// I give up!
LOG_FILE.println(" InvalidISO");
LOG_FILE.println(" ERROR: Invalid ISO!");
goto failed;
}
}
} else {
LOG_FILE.print(" HDD");
}
dev->m_blockcount = dev->m_fileSize / dev->m_blocksize;
// check blocksize dummy file
LOG_FILE.print(" / ");
LOG_FILE.print(" File size: ");
LOG_FILE.print(dev->m_fileSize);
LOG_FILE.print("bytes / ");
LOG_FILE.print(" bytes, ");
LOG_FILE.print(dev->m_fileSize / 1024);
LOG_FILE.print("KiB / ");
LOG_FILE.print("KiB, ");
LOG_FILE.print(dev->m_fileSize / 1024 / 1024);
LOG_FILE.println("MiB");
if(dev->m_type == SCSI_DEVICE_OPTICAL) {
LOG_FILE.print(" MODE2:");LOG_FILE.print(IS_MODE2(dev->flags));
LOG_FILE.print(" BlockSize:");LOG_FILE.println(IS_RAW(dev->flags));
LOG_FILE.print(" MODE2: ");LOG_FILE.print(IS_MODE2(dev->flags));
LOG_FILE.print(" BlockSize: ");LOG_FILE.println(IS_RAW(dev->flags));
}
return true; // File opened
failed:
dev->m_file.close();
dev->m_fileSize = dev->m_blocksize = 0; // no file
//delete dev->m_file;
@ -442,15 +440,15 @@ void setup()
#endif
// PIN initialization
gpio_mode(LED2, GPIO_OUTPUT_PP);
gpio_mode(LED, GPIO_OUTPUT_OD);
gpio_mode(LED2, LED2_MODE);
gpio_mode(LED, LED_MODE);
// Image Set Select Init
gpio_mode(IMAGE_SELECT1, GPIO_INPUT_PU);
gpio_mode(IMAGE_SELECT2, GPIO_INPUT_PU);
pinMode(IMAGE_SELECT1, INPUT);
pinMode(IMAGE_SELECT2, INPUT);
int image_file_set = ((digitalRead(IMAGE_SELECT1) == LOW) ? 1 : 0) | ((digitalRead(IMAGE_SELECT2) == LOW) ? 2 : 0);
// pinMode(IMAGE_SELECT1, INPUT);
// pinMode(IMAGE_SELECT2, INPUT);
int image_file_set = (isLow(gpio_read(IMAGE_SELECT1)) ? 1 : 0) | (isLow(gpio_read(IMAGE_SELECT2)) ? 2 : 0);
LED_OFF();
@ -463,12 +461,12 @@ void setup()
TRANSCEIVER_IO_SET(vTR_INITIATOR,TR_INPUT);
#endif
//GPIO(SCSI BUS)Initialization
//Port setting register (lower)
// GPIOB->regs->CRL |= 0x000000008; // SET INPUT W/ PUPD on PAB-PB0
//Port setting register (upper)
//GPIOB->regs->CRH = 0x88888888; // SET INPUT W/ PUPD on PB15-PB8
// GPIOB->regs->ODR = 0x0000FF00; // SET PULL-UPs on PB15-PB8
// GPIO(SCSI BUS)Initialization
// Port setting register (lower)
// GPIOB->regs->CRL |= 0x000000008; // SET INPUT W/ PUPD on PAB-PB0
// Port setting register (upper)
// GPIOB->regs->CRH = 0x88888888; // SET INPUT W/ PUPD on PB15-PB8
// GPIOB->regs->ODR = 0x0000FF00; // SET PULL-UPs on PB15-PB8
// DB and DP are input modes
SCSI_DB_INPUT()
@ -528,37 +526,40 @@ void setup()
#endif
flashError(ERROR_NO_SDCARD);
}
initFileLog();
readSDCardInfo(mhz);
//HD image file open
// HD image file open
scsi_id_mask = 0x00;
if(SD.exists("scsi-config.txt")) {
LOG_FILE.print("scsi-config.txt is deprecated, use ");
LOG_FILE.println(BLUESCSI_INI);
}
if(ini_getl("SCSI", "MapLunsToIDs", 0, BLUESCSI_INI))
{
LOG_FILE.println("IDs treated as LUNs for ID0");
ids_as_luns = true;
}
if(SD.exists("scsi-config.txt")) {
LOG_FILE.println("scsi-config.txt is deprecated, use bluescsi.ini");
LOG_FILE.println("Treating IDs as LUNs on ID0");
}
// Iterate over the root path in the SD card looking for candidate image files.
FsFile root;
char image_set_dir_name[] = "/ImageSetX/";
image_set_dir_name[9] = char(image_file_set) + 0x30;
image_set_dir_name[9] = INT_TO_CHAR(image_file_set);
root.open(image_set_dir_name);
if (root.isDirectory()) {
LOG_FILE.print("Looking for images in: ");
if (root.isDirectory()) {
LOG_FILE.println(image_set_dir_name);
LOG_FILE.sync();
} else {
root.close();
root.open("/");
LOG_FILE.println("/");
}
LOG_FILE.sync();
findDriveImages(root);
root.close();
@ -577,9 +578,9 @@ void setup()
flashError(ERROR_FALSE_INIT);
}
finalizeFileLog();
LED_OFF();
//Occurs when the RST pin state changes from HIGH to LOW
finalizeDevices();
// Occurs when the RST pin state changes from HIGH to LOW
attachInterrupt(RST, onBusReset, FALLING);
}
@ -595,10 +596,10 @@ void findDriveImages(FsFile root) {
// Directories can not be opened RDWR, so it will fail, but fails the same way with no file/dir, so we need to peek at the file first.
FsFile file_test = root.openNextFile(O_RDONLY);
char name[MAX_FILE_PATH+1];
file_test.getName(name, MAX_FILE_PATH+1);
file_test.getName(name, sizeof(name));
// Skip directories and already open files.
if(file_test.isDir() || strncmp(name, "LOG.txt", 7) == 0) {
if(file_test.isDir() || strncmp(name, LOG_FILENAME, (sizeof(LOG_FILENAME) - 1)) == 0) {
file_test.close();
continue;
}
@ -631,34 +632,36 @@ void findDriveImages(FsFile root) {
if(file && file.isFile()) {
// Defaults for Hard Disks
int id = 1; // 0 and 3 are common in Macs for physical HD and CD, so avoid them.
int lun = 0;
int blk = 512;
int id = DEFAULT_SCSI_ID; // 0 and 3 are common in Macs for physical HD and CD, so avoid them.
int lun = DEFAULT_SCSI_LUN;
int blk = HDD_BLOCK_SIZE;
// Positionally read in and coerase the chars to integers.
LOG_FILE.print(" "); LOG_FILE.println(name);
// Positionally read in and coerce the chars to integers.
// We only require the minimum and read in the next if provided.
int file_name_length = strlen(name);
if(file_name_length > 2) { // HD[N]
if(file_name_length > HDIMG_ID_POS) { // HD[N]
int tmp_id = CHAR_TO_INT(name[HDIMG_ID_POS]);
// If valid id, set it, else use default
if(tmp_id > -1 && tmp_id < 8) {
if(tmp_id > -1 && tmp_id <= MAX_SCSIID) {
id = tmp_id;
} else {
LOG_FILE.print(name);
LOG_FILE.println(" - bad SCSI id in filename, Using default ID 1");
LOG_FILE.print(" WARNING: Bad SCSI ID in filename, using ID ");
LOG_FILE.println(id);
}
}
if(file_name_length > 3) { // HDN[N]
if(file_name_length > HDIMG_LUN_POS) { // HDN[N]
int tmp_lun = CHAR_TO_INT(name[HDIMG_LUN_POS]);
// If valid lun, set it, else use default
if(tmp_lun == 0 || tmp_lun == 1) {
if(tmp_lun > -1 && tmp_lun <= NUM_SCSILUN) {
lun = tmp_lun;
} else {
LOG_FILE.print(name);
LOG_FILE.println(" - bad SCSI LUN in filename, Using default LUN ID 0");
LOG_FILE.print(" WARNING: Bad SCSI LUN in filename, using LUN ");
LOG_FILE.println(lun);
}
}
@ -678,10 +681,23 @@ void findDriveImages(FsFile root) {
blk = 2048;
}
if(id < NUM_SCSIID && lun < NUM_SCSILUN) {
LOG_FILE.print(" Parsed: ");
switch(device_type)
{
case SCSI_DEVICE_HDD:
LOG_FILE.print("HDD");
break;
case SCSI_DEVICE_OPTICAL:
LOG_FILE.print("CDROM");
break;
default:
LOG_FILE.print("UNKNOWN");
}
LOG_FILE.print(" ID "); LOG_FILE.print(id);
LOG_FILE.print(" LUN "); LOG_FILE.print(lun);
LOG_FILE.print(" Blocksize "); LOG_FILE.print(blk); LOG_FILE.println("k");
dev = &scsi_device_list[id][lun];
LOG_FILE.print(" - ");
LOG_FILE.print(name);
dev->m_type = device_type;
image_ready = hddimageOpen(dev, &file, id, lun, blk);
if(image_ready) { // Marked as a responsive ID
@ -699,10 +715,8 @@ void findDriveImages(FsFile root) {
dev->inquiry_block = default_optical;
break;
}
readSCSIDeviceConfig(id, dev);
}
}
LOG_FILE.println();
}
LOG_FILE.sync();
}
@ -718,41 +732,41 @@ void initFileLog() {
LOG_FILE.println("BlueSCSI https://github.com/erichelgeson/BlueSCSI");
LOG_FILE.print("VER: ");
LOG_FILE.print(VERSION);
LOG_FILE.println(BUILD_TAGS);
LOG_FILE.print("DEBUG:");
LOG_FILE.print(BUILD_TAGS);
LOG_FILE.print(" DEBUG: ");
LOG_FILE.println(DEBUG);
LOG_FILE.sync();
}
/*
* Finalize initialization logfile
* Check INI file for device overrides and print summary to logfile
*/
void finalizeFileLog() {
void finalizeDevices() {
// View support drive map
LOG_FILE.print("ID");
for(int lun=0;lun<NUM_SCSILUN;lun++)
{
LOG_FILE.print(":LUN");
LOG_FILE.print(lun);
}
LOG_FILE.println(":");
//
LOG_FILE.println("SCSI Devices");
LOG_FILE.println("ID\tLUN\tName");
for(int id=0;id<NUM_SCSIID;id++)
{
LOG_FILE.print(" ");
LOG_FILE.print(id);
for(int lun=0;lun<NUM_SCSILUN;lun++)
{
LOG_FILE.print(id);
LOG_FILE.print("\t");
LOG_FILE.print(lun);
LOG_FILE.print("\t");
SCSI_DEVICE *dev = &scsi_device_list[id][lun];
if( (lun<NUM_SCSILUN) && (dev->m_file))
if(dev->m_file)
{
LOG_FILE.print((dev->m_blocksize<1000) ? ": " : ":");
LOG_FILE.print(dev->m_blocksize);
char name[MAX_FILE_PATH+1];
dev->m_file.getName(name, sizeof(name));
LOG_FILE.print(name);
readSCSIDeviceConfig(id, dev);
}
else
LOG_FILE.print(":----");
LOG_FILE.print("-");
LOG_FILE.println();
}
LOG_FILE.println(":");
}
LOG_FILE.println("Finished configuration - Starting BlueSCSI");
LOG_FILE.sync();
@ -824,7 +838,7 @@ void onBusReset(void)
LOGN("BusReset!");
if (m_resetJmp) {
m_resetJmp = false;
// Jumping out of the interrupt handler, so need to clear the interupt source.
// Jumping out of the interrupt handler, so need to clear the interrupt source.
uint8 exti = PIN_MAP[RST].gpio_bit;
EXTI_BASE->PR = (1U << exti);
longjmpFromInterrupt(m_resetJmpBuf, 1);
@ -1250,7 +1264,7 @@ void loop()
LOG("CMD:");
SCSI_PHASE_CHANGE(SCSI_PHASE_COMMAND);
// Bus settle delay 400ns. The following code was measured at 20ns before REQ asserted. Added another 380ns. STM32F103.
asm("nop;nop;nop;nop;nop;nop;nop;nop");// This asm causes some code reodering, which adds 270ns, plus 8 nop cycles for an additional 110ns. STM32F103
asm("nop;nop;nop;nop;nop;nop;nop;nop");// This asm causes some code reordering, which adds 270ns, plus 8 nop cycles for an additional 110ns. STM32F103
int len;
byte cmd[20];
@ -1717,7 +1731,7 @@ byte onModeSense(SCSI_DEVICE *dev, const byte *cdb)
m_buf[a + 0] = SCSI_SENSE_MODE_CACHING;
m_buf[a + 1] = 0x0A; // Page length
if(pageControl != 1) {
m_buf[a + 2] = 0x01; // Disalbe Read Cache so no one asks for Cache Stats page.
m_buf[a + 2] = 0x01; // Disable Read Cache so no one asks for Cache Stats page.
}
a += 0x0C;
if(pageCode != SCSI_SENSE_MODE_ALL) break;
@ -1937,7 +1951,7 @@ File get_file_from_index(uint8_t index)
return file_test;
}
File file; // global so we can keep it open while transfering.
File file; // global so we can keep it open while transferring.
byte onGetFile(SCSI_DEVICE *dev, const byte *cdb) {
uint8_t index = cdb[1];
uint32_t offset = ((uint32_t)cdb[2] << 24) | ((uint32_t)cdb[3] << 16) | ((uint32_t)cdb[4] << 8) | cdb[5];
@ -1965,7 +1979,7 @@ byte onGetFile(SCSI_DEVICE *dev, const byte *cdb) {
}
/*
Prepares a file for receving. The file name is null terminated in the scsi data.
Prepares a file for receiving. The file name is null terminated in the scsi data.
*/
File receveFile;
byte onSendFilePrep(SCSI_DEVICE *dev, const byte *cdb)
@ -2011,7 +2025,7 @@ byte onSendFile10(SCSI_DEVICE *dev, const byte *cdb)
// 512 byte offset of where to put these bytes.
uint32_t offset = ((uint32_t)cdb[3] << 16) | ((uint32_t)cdb[4] << 8) | cdb[5];
uint16_t buf_size = SCSI_BUF_SIZE;
// Check if last block of file, and not the only bock in file.
// Check if last block of file, and not the only block in file.
if(bytes_sent < buf_size)
{
buf_size = bytes_sent;
@ -2121,7 +2135,7 @@ byte onModeSelect(SCSI_DEVICE *dev, const byte *cdb)
*/
byte onReZeroUnit(SCSI_DEVICE *dev, const byte *cdb) {
LOGN("-ReZeroUnit");
// Make sure we have an image with atleast a first byte.
// Make sure we have an image with at least a first byte.
// Actually seeking to the position wont do anything, so dont.
return checkBlockCommand(dev, 0, 0);
}

View File

@ -99,8 +99,6 @@ enum SCSI_DEVICE_TYPE
#define IO PB7 // SCSI:I/O
#define SD_CS PA4 // SDCARD:CS
#define LED PC13 // LED
#define LED2 PA0 // External LED
// Image Set Selector
#ifdef XCVR
@ -116,17 +114,29 @@ enum SCSI_DEVICE_TYPE
#define PBREG GPIOB->regs
#define PCREG GPIOC->regs
// LED control
#define LED_ON() PCREG->BSRR = 0b00100000000000000000000000000000; PAREG->BSRR = 0b00000000000000000000000000000001;
#define LED_OFF() PCREG->BSRR = 0b00000000000000000010000000000000; PAREG->BSRR = 0b00000000000000010000000000000000;
// Virtual pin (Arduio compatibility is slow, so make it MCU-dependent)
#define PA(BIT) (BIT)
#define PB(BIT) (BIT+16)
#define PC(BIT) (BIT+32)
// Virtual pin decoding
#define GPIOREG(VPIN) ((VPIN)>=16?PBREG:PAREG)
#define GPIOREG(VPIN) ((VPIN)>=16?((VPIN)>=32?PCREG:PBREG):PAREG)
#define BITMASK(VPIN) (1<<((VPIN)&15))
// Built-in LED
#define LED PC13
#define vLED PC(13)
#define LED_MODE GPIO_OUTPUT_OD
// External LED
#define LED2 PA0
#define vLED2 PA(0)
#define LED2_MODE GPIO_OUTPUT_PP
// LED control
#define LED_ON() GPIOREG(vLED)->BSRR = BITMASK(vLED) << (LED_MODE == GPIO_OUTPUT_PP ? 0 : 16); GPIOREG(vLED2)->BSRR = BITMASK(vLED2) << (LED2_MODE == GPIO_OUTPUT_PP ? 0 : 16);
#define LED_OFF() GPIOREG(vLED)->BSRR = BITMASK(vLED) << (LED_MODE == GPIO_OUTPUT_PP ? 16 : 0); GPIOREG(vLED2)->BSRR = BITMASK(vLED2) << (LED2_MODE == GPIO_OUTPUT_PP ? 16 : 0);
#define vATN PA(8) // SCSI:ATN
#define vBSY PA(9) // SCSI:BSY
#define vACK PA(10) // SCSI:ACK