diff --git a/MiSTer.vcxproj b/MiSTer.vcxproj
index e4f03a2..72fb3de 100644
--- a/MiSTer.vcxproj
+++ b/MiSTer.vcxproj
@@ -88,6 +88,7 @@
+
@@ -167,6 +168,8 @@
+
+
diff --git a/MiSTer.vcxproj.filters b/MiSTer.vcxproj.filters
index 39e1bab..6fcae57 100644
--- a/MiSTer.vcxproj.filters
+++ b/MiSTer.vcxproj.filters
@@ -172,6 +172,12 @@
Source Files\support
+
+ Source Files\support
+
+
+ Source Files\support
+
Source Files\support
diff --git a/menu.cpp b/menu.cpp
index a748665..d0bf2b0 100644
--- a/menu.cpp
+++ b/menu.cpp
@@ -203,6 +203,10 @@ enum MENU
// MT32-pi
MENU_MT32PI_MAIN1,
MENU_MT32PI_MAIN2,
+
+ // Atari 8bit cartridge type selection
+ MENU_ATARI8BIT_CART1,
+ MENU_ATARI8BIT_CART2,
};
static uint32_t menustate = MENU_NONE1;
@@ -2177,7 +2181,7 @@ void HandleUI(void)
if (is_gba() && FileExists(user_io_make_filepath(HomeDir(), "goomba.rom"))) strcat(ext, "GB GBC");
while (strlen(ext) % 3) strcat(ext, " ");
- fs_Options = SCANO_DIR | (is_neogeo() ? SCANO_NEOGEO | SCANO_NOENTER : 0) | (store_name ? SCANO_CLEAR : 0);
+ fs_Options = SCANO_DIR | (is_neogeo() ? SCANO_NEOGEO | SCANO_NOENTER : 0) | (store_name ? SCANO_CLEAR : 0) | (is_atari5200() || (is_atari800() && (ioctl_index == 8 || ioctl_index == 9)) ? SCANO_UMOUNT : 0);
fs_MenuSelect = MENU_GENERIC_FILE_SELECTED;
fs_MenuCancel = MENU_GENERIC_MAIN1;
strcpy(fs_pFileExt, ext);
@@ -2350,6 +2354,8 @@ void HandleUI(void)
if (is_saturn() && !bit) saturn_reset();
if (is_n64() && !bit) n64_reset();
if (is_psx() && !bit) psx_reset();
+ if (is_atari800() && !bit) atari800_reset();
+ if (is_atari5200() && !bit) atari5200_reset();
user_io_status_set(opt, 1, ex);
user_io_status_set(opt, 0, ex);
@@ -2404,7 +2410,7 @@ void HandleUI(void)
}
}
- MenuHide();
+ if(!(selPath[0] && (is_atari5200() || (is_atari800() && (ioctl_index == 8 || ioctl_index == 9))))) MenuHide();
printf("File selected: %s\n", selPath);
memcpy(Selected_F[ioctl_index & 15], selPath, sizeof(Selected_F[ioctl_index & 15]));
@@ -2446,6 +2452,38 @@ void HandleUI(void)
{
c64_open_file(selPath, idx);
}
+ else if (is_atari800() || is_atari5200())
+ {
+ if(is_atari800() && ioctl_index != 8 && ioctl_index != 9)
+ {
+ atari800_open_bios_file(selPath, idx);
+ }
+ else
+ {
+ int a8bit_cart_matches = is_atari5200() ? atari5200_check_cartridge_file(selPath, idx): atari800_check_cartridge_file(selPath, idx);
+ if(a8bit_cart_matches <= 1) MenuHide();
+ if(a8bit_cart_matches == 1)
+ {
+ if(is_atari5200())
+ {
+ atari5200_open_cartridge_file(selPath, 0);
+ }
+ else
+ {
+ atari800_open_cartridge_file(selPath, 0);
+ }
+ }
+ else if(a8bit_cart_matches > 1 && mgl->done)
+ {
+ menustate = MENU_ATARI8BIT_CART1;
+ menusub = 0;
+ }
+ else if(mgl->done)
+ {
+ Info("Unsupported cartridge type!", 2000);
+ }
+ }
+ }
else
{
user_io_file_tx(selPath, idx, opensave, 0, 0, load_addr);
@@ -2455,6 +2493,14 @@ void HandleUI(void)
if (addon[0] == 'f' && addon[1] == '1') process_addon(addon, idx);
}
+ else if(is_atari800() && (ioctl_index == 8 || ioctl_index == 9))
+ {
+ atari800_umount_cartridge(ioctl_index == 9);
+ }
+ else if(is_atari5200())
+ {
+ atari5200_umount_cartridge();
+ }
mgl->state = 3;
}
@@ -2530,6 +2576,10 @@ void HandleUI(void)
neocd_set_en(1);
neocd_set_image(selPath);
}
+ else if (is_atari800())
+ {
+ atari800_set_image(user_io_ext_idx(selPath, fs_pFileExt), ioctl_index, selPath);
+ }
else
{
user_io_set_index(user_io_ext_idx(selPath, fs_pFileExt) << 6 | (menusub + 1));
@@ -6984,6 +7034,61 @@ void HandleUI(void)
SelectFile("", 0, SCANO_CORES, MENU_CORE_FILE_SELECTED1, cp_MenuCancel);
break;
+ case MENU_ATARI8BIT_CART1:
+ helptext_idx = 0;
+ menumask = 0;
+ OsdSetSize(16);
+ OsdSetTitle("Cartridge Type");
+ menustate = MENU_ATARI8BIT_CART2;
+ parentstate = MENU_ATARI8BIT_CART1;
+
+ {
+ int entry = 0;
+ uint32_t selentry = 0;
+ int a8bit_match_count = is_atari5200() ? atari5200_get_match_cart_count() : atari800_get_match_cart_count();
+ for (int i = 0; i < a8bit_match_count; i++)
+ {
+ s[0] = ' ';
+ strcpy(s + 1, is_atari5200() ? atari5200_get_cart_match_name(i) : atari800_get_cart_match_name(i));
+ OsdWrite(entry, s, menusub == selentry);
+ menumask = (menumask << 1) | 1;
+ entry++;
+ selentry++;
+ }
+
+ while (entry < OsdGetSize() - 1) OsdWrite(entry++);
+
+ OsdWrite(entry, " Cancel", menusub == selentry);
+ menusub_last = selentry;
+ menumask = (menumask << 1) | 1;
+ }
+ break;
+
+ case MENU_ATARI8BIT_CART2:
+ if (menu || left)
+ {
+ menustate = MENU_GENERIC_MAIN1;
+ menusub = ioctl_index - 2;
+ }
+ else if (select)
+ {
+ menustate = MENU_NONE1;
+ if (menusub != menusub_last)
+ {
+ int match_index = menusub;
+ HandleUI(); // What MenuHide() would do...
+ if(is_atari5200())
+ {
+ atari5200_open_cartridge_file(selPath, match_index);
+ }
+ else
+ {
+ atari800_open_cartridge_file(selPath, match_index);
+ }
+ }
+ }
+ break;
+
/******************************************************************/
/* we should never come here */
/******************************************************************/
diff --git a/support.h b/support.h
index abfc2c6..e59bd87 100644
--- a/support.h
+++ b/support.h
@@ -40,6 +40,10 @@
// C64 support
#include "support/c64/c64.h"
+// Atari 8bit support
+#include "support/atari8bit/atari800.h"
+#include "support/atari8bit/atari5200.h"
+
// PCECD support
#include "support/pcecd/pcecd.h"
diff --git a/support/atari8bit/asm/Makefile b/support/atari8bit/asm/Makefile
new file mode 100644
index 0000000..93fb6e9
--- /dev/null
+++ b/support/atari8bit/asm/Makefile
@@ -0,0 +1,15 @@
+# This produces the supporting binary data for the Atari800 core,
+# the internal XEX loader and the PBI rom file boot3.rom. The
+# latter should be placed in games/ATARI800 folder of your MiSTer
+# SD card.
+
+all: ../xex_loader.h boot3.rom
+
+../xex_loader.h: xex_loader.asm
+ xa xex_loader.asm -o xex_loader.o65 -l xex_loader.lab
+ python3 process_xex_loader.py
+ rm xex_loader.o65 xex_loader.lab
+ mv xex_loader.h ..
+
+boot3.rom: pbi_bios.asm
+ xa -M -o boot3.rom pbi_bios.asm
\ No newline at end of file
diff --git a/support/atari8bit/asm/boot3.rom b/support/atari8bit/asm/boot3.rom
new file mode 100644
index 0000000..09857d3
Binary files /dev/null and b/support/atari8bit/asm/boot3.rom differ
diff --git a/support/atari8bit/asm/hsio_pbi.xex b/support/atari8bit/asm/hsio_pbi.xex
new file mode 100644
index 0000000..6745a4b
Binary files /dev/null and b/support/atari8bit/asm/hsio_pbi.xex differ
diff --git a/support/atari8bit/asm/pbi_bios.asm b/support/atari8bit/asm/pbi_bios.asm
new file mode 100644
index 0000000..d4d2c85
--- /dev/null
+++ b/support/atari8bit/asm/pbi_bios.asm
@@ -0,0 +1,206 @@
+; Links:
+; http://atariki.krap.pl/index.php/ROM_PBI
+
+#define VER_MAJOR 0
+#define VER_MINOR 9
+
+warmst = $008
+pdvmsk = $247
+pdvrs = $248
+ddevic = $300
+dunit = $301
+dstats = $303
+dtimlo = $306
+cdtma1 = $226
+setvbv = $e45c
+
+; PBI BIOS RAM
+pbi_magic = $d100 ; word
+pbi_splash_flag = $d102
+pbi_req_init_flag = $d103
+pbi_req_proc_res = $d104
+pbi_req_proc_flag = $d105
+pbi_stack_save = $d106
+pbi_drive_boot_act = $d10a
+pbi_drive_boot = $d10b
+pbi_drive_conf = $d10c
+; HSIO RAM variables sit upwards of $d1f0 incl.
+
+ * = $D800
+bios1_start
+ .byte 'M', 'S', 'T'
+ ; Magic 1
+ .byte $80
+ .byte $31 ; ddevic we are servicing
+pdior_vec
+ jmp pdior
+pdint_vec
+ rts : nop : nop
+ ; Magic 2
+ .byte $91
+ .byte $00 ; no CIO
+ .word pdint_vec
+ .word pdint_vec
+ .word pdint_vec
+ .word pdint_vec
+ .word pdint_vec
+ .word pdint_vec
+
+pdinit
+ lda pdvmsk : ora pdvrs : sta pdvmsk
+ lda #0 : ldx #$fe : sta $d100-1,x : dex : bne *-4
+ ; Marker for the core firmware
+ lda #$a5 : sta pbi_magic : sta pbi_magic+1
+ ; ask for init
+ inc pbi_req_init_flag : lda pbi_req_init_flag : bne *-3
+ lda warmst : bmi pdinit_1
+ lda pbi_drive_boot_act : beq pdinit_1
+ sta dunit
+pdinit_1
+ ; Do we want the splash?
+ lda pbi_splash_flag : beq pdinit_ret
+ jsr boot_screen_init
+ lda #$0c : sta $2c5 ; color 1
+ lda #$e0 : sta $d409 ; chbase
+ lda #display_list : sta $d403 ; display list
+ lda $14 : cmp $14 : beq *-2 : ldy #$22 : sty $d400 ; dmactl
+ clc : adc #100 : cmp $14 : bne *-2
+ lda #0 : sta $d400
+pdinit_ret
+ rts
+
+display_list
+ .byte $70, $70, $70
+ .byte $42 : .word display_text1
+ .byte $10
+ .byte $42 : .word display_text2
+ .byte $70
+ .byte $42 : .word display_text3
+ .byte $30
+ .byte $02
+ .byte $41 : .word display_list
+
+display_text1
+ .byte 0,0
+ .byte 'A'-$20,'tari','8'-$20,'0'-$20,'0'-$20,0,'M'-$20,'i','S'-$20,'T'-$20,'er',0,'core',0
+ .byte 'P'-$20,'B'-$20,'I'-$20,0,'B'-$20,'I'-$20,'O'-$20,'S'-$20,0
+ .byte 'v',VER_MAJOR+$10,'.'-$20,VER_MINOR+$10
+display_text1_len = *-display_text1
+ .dsb 40-display_text1_len,0
+display_text2
+ .byte 0,0
+ .byte '('-$20,'C'-$20,')'-$20,0,'2'-$20,'0'-$20,'2'-$20,'6'-$20,0,'woj','@'-$20,'A'-$20,'tari','A'-$20,'ge'
+display_text2_len = *-display_text2
+ .dsb 40-display_text2_len,0
+display_text3 = $d110
+
+drive_labels_1 .byte 'O'-$20, 'P'-$20, 'H'-$20
+drive_labels_2 .byte 'f', 'B'-$20, 'S'-$20
+drive_labels_3 .byte 'f', 'I'-$20, 'I'-$20
+drive_labels_4 .byte 0, 0, 'O'-$20
+
+boot_label_1 .byte 'B'-$20,'o','o','t',0,'D'-$20,'r','i','v','e',':'-$20
+boot_label_1_len = *-boot_label_1
+boot_label_2 .byte 'D'-$20,'e','f','a','u','l','t'
+boot_label_2_len = *-boot_label_2
+boot_label_3 .byte 'A'-$20,'P'-$20,'T'-$20
+boot_label_3_len = *-boot_label_3
+
+boot_screen_init
+ ldy #1
+boot_screen_init_loop
+ lda #'D'-$20 : sta display_text3+2,x : inx
+ tya : pha : ora #$10 : sta display_text3+2,x : inx
+ lda #':'-$20 : sta display_text3+2,x : inx : inx
+ lda pbi_drive_conf-1,y : tay
+ lda drive_labels_1,y : sta display_text3+2,x : inx
+ lda drive_labels_2,y : sta display_text3+2,x : inx
+ lda drive_labels_3,y : sta display_text3+2,x : inx
+ lda drive_labels_4,y : sta display_text3+2,x : inx : inx
+ pla : tay : iny : cpy #5 : bne boot_screen_init_loop
+ ldy #boot_label_1_len
+boot_screen_init_loop_2
+ lda boot_label_1-1,y : sta display_text3+41,y
+ dey : bne boot_screen_init_loop_2
+ ldx pbi_drive_boot : dex
+ bmi boot_drv_def
+ beq boot_drv_apt
+ lda #'D'-$20 : sta display_text3+42+boot_label_1_len+1
+ txa : ora #$10 : sta display_text3+42+boot_label_1_len+2
+ lda #':'-$20 : sta display_text3+42+boot_label_1_len+3
+ bne boot_screen_init_ret
+boot_drv_def
+ ldy #boot_label_2_len
+boot_drv_def_loop
+ lda boot_label_2-1,y : sta display_text3+42+boot_label_1_len,y
+ dey : bne boot_drv_def_loop
+ beq boot_screen_init_ret
+boot_drv_apt
+ ldy #boot_label_3_len
+boot_drv_apt_loop
+ lda boot_label_3-1,y : sta display_text3+42+boot_label_1_len,y
+ dey : bne boot_drv_apt_loop
+boot_screen_init_ret
+ rts
+
+; The main block I/O routine
+pdior
+ lda ddevic : and #$7F : cmp #$31 : beq pdior_2
+ cmp #$20 : bne pdior_bail
+pdior_2
+ lda dunit : beq pdior_bail
+ cmp #$10 : bcs pdior_bail
+ tsx : stx pbi_stack_save
+ lda dtimlo : ror : ror : tay : and #$3f : tax : tya : ror : and #$c0 : tay : lda #1
+ jsr setvbv
+ lda #pbi_time_out : sta cdtma1+1
+ inc pbi_req_proc_flag : lda pbi_req_proc_flag : bne *-3
+ ldx #0 : ldy #0 : lda #1 : jsr setvbv
+ lda pbi_req_proc_res : bmi pdior_bail ; the FW says either no PBI service or ATX (plain SIO)
+ beq pdior_pbi_ok ; the drive was in PBI mode and got serviced
+ ; otherwise call HSIO
+ jsr $dc00 : sec : rts
+pdior_pbi_ok
+ ldy dstats : sec : rts
+pdior_bail
+ ; We are not servicing this block I/O request
+ clc : rts
+pbi_time_out
+ lda #0 : sta pbi_req_proc_flag : ldx pbi_stack_save : txs
+ lda #$8a : sta dstats : bne pdior_pbi_ok
+bios1_end
+
+.dsb ($400-bios1_end+bios1_start),$ff
+
+hsio_start ; This should be $dc00
+.bin 6,0,"hsio_pbi.xex"
+hsio_end
+
+.dsb ($3AD-hsio_end+hsio_start),$ff
+
+.byte VER_MAJOR*16+VER_MINOR ; Version byte
+
+.byte (device_name_end-device_name-1)
+device_name
+.byte "SDHC MiSTer SDEMU v.",VER_MAJOR+$30,'.',VER_MINOR+$30,$9B
+device_name_end
+
+.dsb (40-device_name_end+device_name),$ff
+
+; .dsb ($3D7-hsio_end+hsio_start),$ff
+
+.byte (bios_name_end-bios_name-1)
+bios_name
+.byte "MiSTer core PBI BIOS v.",VER_MAJOR+$30,'.',VER_MINOR+$30,$9B
+bios_name_end
+
+.dsb (40-bios_name_end+bios_name),$ff
+
+ * = $D800
+.dsb $800,$FF
+
+ * = $D800
+.dsb $800,$FF
+
+ * = $D800
+.dsb $800,$FF
\ No newline at end of file
diff --git a/support/atari8bit/asm/process_xex_loader.py b/support/atari8bit/asm/process_xex_loader.py
new file mode 100644
index 0000000..26fea3d
--- /dev/null
+++ b/support/atari8bit/asm/process_xex_loader.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python3
+
+f = open("xex_loader.o65", "rb")
+loader = f.read()
+f.close()
+
+s = "const static uint8_t xex_loader[] =\n{\n\t"
+
+i = 0
+for b in loader:
+ s += f"0x{b:02X},"
+ i += 1
+ if i == len(loader):
+ s = s[:-1]
+ if i % 16 == 0:
+ s += "\n\t"
+
+if i % 16 != 0:
+ s += "\n"
+else:
+ s = s[:-1]
+
+s += "};\n\n"
+
+f = open("xex_loader.lab", "rt")
+l = f.read().split("\n")
+f.close()
+
+for ll in l:
+ if ll[:11] == "read_status":
+ read_status = ll[17:19]
+ elif ll[:5] == "init1":
+ init1 = ll[11:13]
+
+s += f"#define XEX_READ_STATUS 0x{read_status}\n"
+s += f"#define XEX_INIT1 0x{init1}\n\n"
+
+f = open("xex_loader.h", "wt")
+f.write(s)
+f.close()
diff --git a/support/atari8bit/asm/xex_loader.asm b/support/atari8bit/asm/xex_loader.asm
new file mode 100644
index 0000000..cec69ca
--- /dev/null
+++ b/support/atari8bit/asm/xex_loader.asm
@@ -0,0 +1,16 @@
+ * = $D100
+magic
+ .byte $61
+init1
+ ldx #0 : stx $09 : dex : txs
+ dec magic
+load_next_block
+ dec read_status
+ lda #1 : read_status = *-1 : beq *-2
+ bmi init_go
+ lda #>(load_next_block-1) : pha
+ lda #<(load_next_block-1) : pha
+ jmp ($2e2)
+init_go
+ dec magic
+ jmp ($2e0)
diff --git a/support/atari8bit/atari5200.cpp b/support/atari8bit/atari5200.cpp
new file mode 100644
index 0000000..e607c4b
--- /dev/null
+++ b/support/atari8bit/atari5200.cpp
@@ -0,0 +1,284 @@
+#include
+#include
+#include
+
+#include "../../hardware.h"
+#include "../../menu.h"
+#include "../../file_io.h"
+#include "../../debug.h"
+#include "../../user_io.h"
+#include "../../fpga_io.h"
+
+#include "atari5200.h"
+#include "atari8bit_defs.h"
+
+extern uint8_t a8bit_buffer[];
+
+void set_a8bit_reg(uint8_t reg, uint8_t val);
+uint16_t get_a8bit_reg(uint8_t reg);
+void atari8bit_dma_write(const uint8_t *buf, uint32_t addr, uint32_t len);
+void atari8bit_dma_zero(uint32_t addr, uint32_t len);
+
+static void reboot()
+{
+ set_a8bit_reg(REG_PAUSE, 1);
+
+ // Initialize the first 16K of Atari RAM with a pattern
+ for(int i = 0; i < BUFFER_SIZE; i += 2)
+ {
+ a8bit_buffer[i] = 0xFF;
+ a8bit_buffer[i+1] = 0x00;
+ }
+ user_io_set_index(99);
+ user_io_set_download(1, ATARI_BASE);
+ for(int i = 0; i < 0x4000 / BUFFER_SIZE; i++) user_io_file_tx_data(a8bit_buffer, BUFFER_SIZE);
+ user_io_set_upload(0);
+ set_a8bit_reg(REG_RESET, 1);
+ set_a8bit_reg(REG_RESET, 0);
+
+ set_a8bit_reg(REG_PAUSE, 0);
+}
+
+static int cart_matches_total;
+static uint8_t cart_matches_type[2];
+static uint8_t cart_match_car;
+static unsigned char cart_io_index;
+
+int atari5200_get_match_cart_count()
+{
+ return cart_matches_total;
+}
+
+static const char one_chip[16] = "One chip";
+static const char two_chip[16] = "Two chip";
+
+const char *atari5200_get_cart_match_name(int match_index)
+{
+ return match_index == 1 ? two_chip : one_chip;
+}
+
+void atari5200_umount_cartridge()
+{
+ set_a8bit_reg(REG_CART1_SELECT, 0);
+ reboot();
+}
+
+int atari5200_check_cartridge_file(const char* name, unsigned char index)
+{
+ fileTYPE f = {};
+
+ int file_size;
+ cart_match_car = 0;
+ cart_matches_total = 0;
+ cart_io_index = index;
+
+ int ext_index = index >> 6; // CAR is index 0
+
+ if (FileOpen(&f, name))
+ {
+ file_size = f.size;
+ if (!ext_index)
+ {
+ cart_match_car = 1;
+ FileReadAdv(&f, a8bit_buffer, 16);
+ }
+ FileClose(&f);
+
+ if(cart_match_car)
+ {
+ // CAR file, presumably, let's check further
+ if (a8bit_buffer[0] == 'C' && a8bit_buffer[1] == 'A' && a8bit_buffer[2] == 'R' && a8bit_buffer[3] == 'T')
+ {
+ switch(a8bit_buffer[7])
+ {
+ case 0x04:
+ case 0x06:
+ case 0x07:
+ case 0x10:
+ case 0x13:
+ case 0x14:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ cart_matches_type[0] = a8bit_buffer[7];
+ cart_matches_total = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ uint8_t type = 0xFF;
+ if (file_size == 0x10000) type = 0x47;
+ else if (file_size == 0x20000) type = 0x48;
+ else if (file_size == 0x40000) type = 0x49;
+ else if (file_size == 0x80000) type = 0x4A;
+ else if (file_size == 40960) type = 0x07;
+ else if (file_size == 0x8000) type = 0x04;
+ else if (file_size == 0x2000) type = 0x13;
+ else if (file_size == 0x1000) type = 0x14;
+ else if (file_size == 0x4000)
+ {
+ cart_matches_type[0] = 0x10;
+ cart_matches_type[1] = 0x06;
+ cart_matches_total = 2;
+ }
+
+ if(cart_matches_total == 0 && type != 0xFF)
+ {
+ cart_matches_total = 1;
+ cart_matches_type[0] = type;
+ }
+ }
+ }
+
+ return cart_matches_total;
+}
+
+void atari5200_open_cartridge_file(const char* name, int match_index)
+{
+ fileTYPE f = {};
+ int offset = cart_match_car ? 16 : 0;
+ uint8_t cart_type = cart_matches_type[match_index];
+
+ if (FileOpen(&f, name))
+ {
+ set_a8bit_reg(REG_PAUSE, 1);
+ set_a8bit_reg(REG_CART1_SELECT, 0);
+
+ ProgressMessage(0, 0, 0, 0);
+ FileSeek(&f, offset, SEEK_SET);
+
+ user_io_set_index(cart_io_index);
+ user_io_set_download(1, SDRAM_BASE + 0x4000);
+
+ if(cart_type == 0x06)
+ {
+ for(int i = 0; i < 4; i++)
+ {
+ ProgressMessage("Loading", f.name, i * 0x2000, 0x8000);
+ if(i % 2 == 0) FileReadAdv(&f, a8bit_buffer, 0x2000);
+ user_io_file_tx_data(a8bit_buffer, 0x2000);
+ }
+ }
+ else if(cart_type == 0x10)
+ {
+ for(int i = 0; i < 4; i++)
+ {
+ ProgressMessage("Loading", f.name, i * 0x2000, 0x8000);
+ if(i % 2 == 0) FileSeek(&f, offset, SEEK_SET);
+ FileReadAdv(&f, a8bit_buffer, 0x2000);
+ user_io_file_tx_data(a8bit_buffer, 0x2000);
+ }
+ }
+ else if(cart_type == 0x13 || cart_type == 0x14)
+ {
+ int block_size = f.size - offset;
+ FileReadAdv(&f, a8bit_buffer, block_size);
+ for(int i = 0; i < 0x8000 / block_size; i++)
+ {
+ ProgressMessage("Loading", f.name, i * block_size, 0x8000);
+ user_io_file_tx_data(a8bit_buffer, block_size);
+ }
+ }
+ else if(cart_type >= 0x47 && cart_type <= 0x4A)
+ {
+ int block_size = f.size - offset;
+ for(int i = 0; i < 0x80000 / block_size; i++)
+ {
+ FileSeek(&f, offset, SEEK_SET);
+ for(int j = 0; j < block_size / 0x2000; j++)
+ {
+ ProgressMessage("Loading", f.name, i * block_size + j * 0x2000, 0x80000);
+ FileReadAdv(&f, a8bit_buffer, 0x2000);
+ user_io_file_tx_data(a8bit_buffer, 0x2000);
+ }
+ }
+ }
+ else if(cart_type == 0x07)
+ {
+ FileSeek(&f, 0, SEEK_SET);
+ FileReadAdv(&f, a8bit_buffer, 512);
+ uint8_t bb_type1 = (a8bit_buffer[offset] == 0x2F);
+
+ FileSeek(&f, offset + (bb_type1 ? 0 : 0x2000), SEEK_SET);
+
+ ProgressMessage("Loading", f.name, 0, 0xC000);
+ FileReadAdv(&f, a8bit_buffer, 0x2000);
+ user_io_file_tx_data(a8bit_buffer, 0x2000);
+
+ ProgressMessage("Loading", f.name, 0x2000, 0xC000);
+ FileReadAdv(&f, a8bit_buffer, 0x2000);
+ user_io_file_tx_data(a8bit_buffer, 0x2000);
+
+ FileSeek(&f, offset + (bb_type1 ? 0x8000 : 0), SEEK_SET);
+
+ ProgressMessage("Loading", f.name, 0x4000, 0xC000);
+ FileReadAdv(&f, a8bit_buffer, 0x2000);
+ user_io_file_tx_data(a8bit_buffer, 0x2000);
+
+ ProgressMessage("Loading", f.name, 0x6000, 0xC000);
+ user_io_file_tx_data(a8bit_buffer, 0x2000);
+
+ FileSeek(&f, offset + (bb_type1 ? 0x4000 : 0x6000), SEEK_SET);
+
+ ProgressMessage("Loading", f.name, 0x8000, 0xC000);
+ FileReadAdv(&f, a8bit_buffer, 0x2000);
+ user_io_file_tx_data(a8bit_buffer, 0x2000);
+
+ ProgressMessage("Loading", f.name, 0xA000, 0xC000);
+ FileReadAdv(&f, a8bit_buffer, 0x2000);
+ user_io_file_tx_data(a8bit_buffer, 0x2000);
+ }
+ else if(cart_type == 0x04)
+ {
+ while (offset < f.size)
+ {
+ int to_read = f.size - offset;
+ if (to_read > BUFFER_SIZE) to_read = BUFFER_SIZE;
+ ProgressMessage("Loading", f.name, offset, f.size);
+ FileReadAdv(&f, a8bit_buffer, to_read);
+ user_io_file_tx_data(a8bit_buffer, to_read);
+ offset += to_read;
+ }
+ }
+ FileClose(&f);
+ user_io_set_download(0);
+ ProgressMessage(0, 0, 0, 0);
+ set_a8bit_reg(REG_CART1_SELECT, cart_type);
+ reboot();
+ }
+}
+
+void atari5200_poll()
+{
+ uint16_t atari_status1 = get_a8bit_reg(REG_ATARI_STATUS1);
+
+ set_a8bit_reg(REG_PAUSE, atari_status1 & STATUS1_MASK_HALT);
+ if (atari_status1 & STATUS1_MASK_COLDBOOT) reboot();
+}
+
+void atari5200_init()
+{
+ set_a8bit_reg(REG_PAUSE, 1);
+ cart_matches_total = 0;
+ cart_match_car = 0;
+
+ static char mainpath[512];
+ const char *home = HomeDir();
+
+ sprintf(mainpath, "%s/boot0.rom", home);
+ user_io_file_tx(mainpath, 0);
+ atari5200_reset();
+}
+
+void atari5200_reset()
+{
+ set_a8bit_reg(REG_PAUSE, 1);
+ set_a8bit_reg(REG_CART1_SELECT, 0);
+ atari8bit_dma_zero(SDRAM_BASE + 0x4000, 0x80000);
+ reboot();
+}
diff --git a/support/atari8bit/atari5200.h b/support/atari8bit/atari5200.h
new file mode 100644
index 0000000..e8072c4
--- /dev/null
+++ b/support/atari8bit/atari5200.h
@@ -0,0 +1,14 @@
+#ifndef __ATARI5200_H__
+#define __ATARI5200_H__
+
+int atari5200_get_match_cart_count();
+const char *atari5200_get_cart_match_name(int match_index);
+
+void atari5200_init();
+void atari5200_reset();
+void atari5200_poll();
+void atari5200_umount_cartridge();
+int atari5200_check_cartridge_file(const char* name, unsigned char index);
+void atari5200_open_cartridge_file(const char* name, int match_index);
+
+#endif
diff --git a/support/atari8bit/atari800.cpp b/support/atari8bit/atari800.cpp
new file mode 100644
index 0000000..3b19901
--- /dev/null
+++ b/support/atari8bit/atari800.cpp
@@ -0,0 +1,2190 @@
+#include
+#include
+#include
+#include
+
+#include "../../hardware.h"
+#include "../../menu.h"
+#include "../../file_io.h"
+#include "../../debug.h"
+#include "../../user_io.h"
+#include "../../fpga_io.h"
+#include "../../scheduler.h"
+#include "../../input.h"
+
+#include "atari800.h"
+#include "atari8bit_defs.h"
+
+#define ATARI_COLDST (ATARI_BASE + 0x244)
+#define ATARI_BASICF (ATARI_BASE + 0x3F8)
+#define ATARI_GINTLK (ATARI_BASE + 0x3FA)
+#define ATARI_PUPBT (ATARI_BASE + 0x33D)
+#define ATARI_BOOTFLAG (ATARI_BASE + 0x09)
+#define ATARI_CASINI (ATARI_BASE + 0x02)
+#define ATARI_DOSVEC (ATARI_BASE + 0x0A)
+#define ATARI_RUNAD (ATARI_BASE + 0x2E0)
+#define ATARI_INITAD (ATARI_BASE + 0x2E2)
+
+typedef struct {
+ uint8_t cart_type; // type from CAR header
+ char name[16]; // name of type
+ uint8_t cart_mode; // mode used in cartridge emulation
+ int size; // image size in k
+} cart_def_t;
+
+// Cart modes from the original ZCPU firmware
+
+// 8k modes (0xA000-$BFFF)
+#define TC_MODE_OFF 0x00 // cart disabled
+#define TC_MODE_8K 0x01 // 8k banks at $A000
+#define TC_MODE_ATARIMAX1 0x02 // 8k using Atarimax 1MBit compatible banking
+#define TC_MODE_ATARIMAX8 0x03 // 8k using Atarimax 8MBit compatible banking
+#define TC_MODE_ATARIMAX8_2 0x10 // 8k using Atarimax 8MBit compatible banking (new type)
+#define TC_MODE_DCART 0x11 // 512K DCart
+#define TC_MODE_OSS_16 0x04 // 16k OSS cart, M091 banking
+#define TC_MODE_OSS_8 0x05 // 8k OSS cart, M091 banking
+#define TC_MODE_OSS_043M 0x06 // 16k OSS cart, 043M banking
+
+#define TC_MODE_SDX64 0x08 // SDX 64k cart, $D5Ex banking
+#define TC_MODE_SDX128 0x09 // SDX 128k cart, $D5Ex banking
+#define TC_MODE_DIAMOND64 0x0A // Diamond GOS 64k cart, $D5Dx banking
+#define TC_MODE_EXPRESS64 0x0B // Express 64k cart, $D57x banking
+
+#define TC_MODE_ATRAX128 0x0C // Atrax 128k cart
+#define TC_MODE_WILLIAMS64 0x0D // Williams 64k cart
+#define TC_MODE_WILLIAMS32 0x0E // Williams 32k cart
+#define TC_MODE_WILLIAMS16 0x0F // Williams 16k cart
+
+// 16k modes (0x8000-$BFFF)
+//#define TC_MODE_FLEXI 0x20 // flexi mode
+#define TC_MODE_16K 0x21 // 16k banks at $8000-$BFFF
+#define TC_MODE_MEGAMAX16 0x22 // MegaMax 16k mode (up to 2MB)
+#define TC_MODE_BLIZZARD 0x23 // Blizzard 16k
+#define TC_MODE_SIC_128 0x24 // Sic!Cart 128k
+#define TC_MODE_SIC_256 0x25 // Sic!Cart 256k
+#define TC_MODE_SIC_512 0x26 // Sic!Cart 512k
+#define TC_MODE_SIC_1024 0x27 // Sic!Cart+ 1024k
+
+#define TC_MODE_BLIZZARD_4 0x12 // Blizzard 4k
+#define TC_MODE_BLIZZARD_32 0x13 // Blizzard 32k
+#define TC_MODE_RIGHT_8K 0x14
+#define TC_MODE_RIGHT_4K 0x15
+#define TC_MODE_2K 0x16
+#define TC_MODE_4K 0x17
+
+// J(atari)Cart versions
+#define TC_MODE_JATARI_8 0x18
+#define TC_MODE_JATARI_16 0x19
+#define TC_MODE_JATARI_32 0x1A
+#define TC_MODE_JATARI_64 0x1B
+#define TC_MODE_JATARI_128 0x1C
+#define TC_MODE_JATARI_256 0x1D
+#define TC_MODE_JATARI_512 0x1E
+#define TC_MODE_JATARI_1024 0x1F
+
+#define TC_MODE_MEGA_16 0x28 // switchable MegaCarts
+#define TC_MODE_MEGA_32 0x29
+#define TC_MODE_MEGA_64 0x2A
+#define TC_MODE_MEGA_128 0x2B
+#define TC_MODE_MEGA_256 0x2C
+#define TC_MODE_MEGA_512 0x2D
+#define TC_MODE_MEGA_1024 0x2E
+#define TC_MODE_MEGA_2048 0x2F
+#define TC_MODE_MEGA_4096 0x20
+
+#define TC_MODE_XEGS_32 0x30 // non-switchable XEGS carts
+#define TC_MODE_XEGS_64 0x31
+#define TC_MODE_XEGS_128 0x32
+#define TC_MODE_XEGS_256 0x33
+#define TC_MODE_XEGS_512 0x34
+#define TC_MODE_XEGS_1024 0x35
+#define TC_MODE_XEGS_64_2 0x36
+
+#define TC_MODE_SXEGS_32 0x38 // switchable XEGS carts
+#define TC_MODE_SXEGS_64 0x39
+#define TC_MODE_SXEGS_128 0x3A
+#define TC_MODE_SXEGS_256 0x3B
+#define TC_MODE_SXEGS_512 0x3C
+#define TC_MODE_SXEGS_1024 0x3D
+
+// XE Multicart versions
+#define TC_MODE_XEMULTI_8 0x68
+#define TC_MODE_XEMULTI_16 0x69
+#define TC_MODE_XEMULTI_32 0x6A
+#define TC_MODE_XEMULTI_64 0x6B
+#define TC_MODE_XEMULTI_128 0x6C
+#define TC_MODE_XEMULTI_256 0x6D
+#define TC_MODE_XEMULTI_512 0x6E
+#define TC_MODE_XEMULTI_1024 0x6F
+
+#define TC_MODE_PHOENIX 0x40
+#define TC_MODE_AST_32 0x41
+#define TC_MODE_ATRAX_INT128 0x42
+#define TC_MODE_ATRAX_SDX64 0x43
+#define TC_MODE_ATRAX_SDX128 0x44
+#define TC_MODE_TSOFT_64 0x45
+#define TC_MODE_TSOFT_128 0x46
+#define TC_MODE_ULTRA_32 0x47
+#define TC_MODE_DAWLI_32 0x48
+#define TC_MODE_DAWLI_64 0x49
+#define TC_MODE_JRC_LIN_64 0x4A
+#define TC_MODE_JRC_INT_64 0x4B
+#define TC_MODE_SDX_SIDE2 0x4C
+#define TC_MODE_SDX_U1MB 0x4D
+#define TC_MODE_DB_32 0x70
+#define TC_MODE_BOUNTY_40 0x73
+
+static const cart_def_t cart_def[] =
+{
+ { 1, "Standard ", TC_MODE_8K, 8 },
+ { 2, "Standard ", TC_MODE_16K, 16 },
+ // This below is intentional, for 034M carts we fix them
+ // (we also need to add 2 extra fake AND-ed banks for
+ // both 043M and 034M)
+ { 3, "OSS 2 Chip 034M", TC_MODE_OSS_043M, 16 },
+ { 5, "DB ", TC_MODE_DB_32, 32 },
+ { 8, "Williams ", TC_MODE_WILLIAMS64, 64 },
+ { 9, "Express ", TC_MODE_EXPRESS64, 64 },
+ { 10, "Diamond ", TC_MODE_DIAMOND64, 64 },
+ { 11, "Sparta DOS X ", TC_MODE_SDX64, 64 },
+ { 12, "XEGS ", TC_MODE_XEGS_32, 32 },
+ { 13, "XEGS Bank 0-7 ", TC_MODE_XEGS_64, 64 },
+ { 14, "XEGS ", TC_MODE_XEGS_128, 128 },
+ { 15, "OSS 1 Chip ", TC_MODE_OSS_16, 16 },
+ { 17, "Atrax Decoded ", TC_MODE_ATRAX128, 128 },
+ { 18, "Bounty Bob ", TC_MODE_BOUNTY_40, 40 },
+ { 21, "Right ", TC_MODE_RIGHT_8K, 8 },
+ { 22, "Williams ", TC_MODE_WILLIAMS32, 32 },
+ { 23, "XEGS ", TC_MODE_XEGS_256, 256 },
+ { 24, "XEGS ", TC_MODE_XEGS_512, 512 },
+ { 25, "XEGS ", TC_MODE_XEGS_1024, 1024 },
+ { 26, "MegaCart ", TC_MODE_MEGA_16, 16 },
+ { 27, "MegaCart ", TC_MODE_MEGA_32, 32 },
+ { 28, "MegaCart ", TC_MODE_MEGA_64, 64 },
+ { 29, "MegaCart ", TC_MODE_MEGA_128, 128 },
+ { 30, "MegaCart ", TC_MODE_MEGA_256, 256 },
+ { 31, "MegaCart ", TC_MODE_MEGA_512, 512 },
+ { 32, "MegaCart ", TC_MODE_MEGA_1024, 1024 },
+ { 33, "Super XEGS ", TC_MODE_SXEGS_32, 32 },
+ { 34, "Super XEGS ", TC_MODE_SXEGS_64, 64 },
+ { 35, "Super XEGS ", TC_MODE_SXEGS_128, 128 },
+ { 36, "Super XEGS ", TC_MODE_SXEGS_256, 256 },
+ { 37, "Super XEGS ", TC_MODE_SXEGS_512, 512 },
+ { 38, "Super XEGS ", TC_MODE_SXEGS_1024, 1024 },
+ { 39, "Phoenix ", TC_MODE_PHOENIX, 8 },
+ { 40, "Blizzard ", TC_MODE_BLIZZARD, 16 },
+ { 41, "Atarimax ", TC_MODE_ATARIMAX1, 128 },
+ { 42, "Atarimax Old ", TC_MODE_ATARIMAX8, 1024 },
+ { 43, "Sparta DOS X ", TC_MODE_SDX128, 128 },
+ { 44, "OSS 1 Chip ", TC_MODE_OSS_8, 8 },
+ { 45, "OSS 2 Chip 043M", TC_MODE_OSS_043M, 16 },
+ { 46, "Blizzard ", TC_MODE_BLIZZARD_4, 4 },
+ { 47, "AST ", TC_MODE_AST_32, 32 },
+ { 48, "Atrax SDX ", TC_MODE_ATRAX_SDX64, 64 },
+ { 49, "Atrax SDX ", TC_MODE_ATRAX_SDX128, 128 },
+ { 50, "TurboSoft ", TC_MODE_TSOFT_64, 64 },
+ { 51, "TurboSoft ", TC_MODE_TSOFT_128, 128 },
+ { 52, "UltraCart ", TC_MODE_ULTRA_32, 32 },
+ { 53, "Low Bank XL ", TC_MODE_RIGHT_8K, 8 },
+ { 54, "S.I.C. ", TC_MODE_SIC_128, 128 },
+ { 55, "S.I.C. ", TC_MODE_SIC_256, 256 },
+ { 56, "S.I.C. ", TC_MODE_SIC_512, 512 },
+ { 57, "Standard ", TC_MODE_2K, 2 },
+ { 58, "Standard ", TC_MODE_4K, 4 },
+ { 59, "Right ", TC_MODE_RIGHT_4K, 4 },
+ { 60, "Blizzard ", TC_MODE_BLIZZARD_32, 32 },
+ { 61, "MegaMax ", TC_MODE_MEGAMAX16, 2048 },
+ { 63, "MegaCart ", TC_MODE_MEGA_4096, 4096 },
+ { 64, "MegaCart ", TC_MODE_MEGA_2048, 2048 },
+ { 67, "XEGS Bank 8-15 ", TC_MODE_XEGS_64_2, 64 },
+ { 68, "Atrax Encoded ", TC_MODE_ATRAX_INT128, 128 },
+ { 69, "aDawliah ", TC_MODE_DAWLI_32, 32 },
+ { 70, "aDawliah ", TC_MODE_DAWLI_64, 64 },
+ { 75, "Atarimax New ", TC_MODE_ATARIMAX8_2, 1024 },
+ { 76, "Williams ", TC_MODE_WILLIAMS16, 16 },
+ { 80, "JRC-Linear ", TC_MODE_JRC_LIN_64, 64 },
+ { 83, "S.I.C.+ ", TC_MODE_SIC_1024, 1024 },
+ { 86, "XE Multicart ", TC_MODE_XEMULTI_8, 8 },
+ { 87, "XE Multicart ", TC_MODE_XEMULTI_16, 16 },
+ { 88, "XE Multicart ", TC_MODE_XEMULTI_32, 32 },
+ { 89, "XE Multicart ", TC_MODE_XEMULTI_64, 64 },
+ { 90, "XE Multicart ", TC_MODE_XEMULTI_128, 128 },
+ { 91, "XE Multicart ", TC_MODE_XEMULTI_256, 256 },
+ { 92, "XE Multicart ", TC_MODE_XEMULTI_512, 512 },
+ { 93, "XE Multicart ", TC_MODE_XEMULTI_1024, 1024 },
+ {104, "J(atari)cart ", TC_MODE_JATARI_8, 8 },
+ {105, "J(atari)cart ", TC_MODE_JATARI_16, 16 },
+ {106, "J(atari)cart ", TC_MODE_JATARI_32, 32 },
+ {107, "J(atari)cart ", TC_MODE_JATARI_64, 64 },
+ {108, "J(atari)cart ", TC_MODE_JATARI_128, 128 },
+ {109, "J(atari)cart ", TC_MODE_JATARI_256, 256 },
+ {110, "J(atari)cart ", TC_MODE_JATARI_512, 512 },
+ {111, "J(atari)cart ", TC_MODE_JATARI_1024, 1024 },
+ {112, "DCART ", TC_MODE_DCART, 512 },
+ {160, "JRC-Interleaved", TC_MODE_JRC_INT_64, 64 },
+ { 0, "", 0, 0 }
+};
+
+uint8_t a8bit_buffer[BUFFER_SIZE];
+
+#include "xex_loader.h"
+#include "boot_xex_loader.h"
+
+static fileTYPE xex_file = {};
+static uint8_t xex_file_first_block;
+
+void set_a8bit_reg(uint8_t reg, uint8_t val)
+{
+ EnableIO();
+ spi8(A800_SET_REGISTER);
+ spi_w((reg << 8) | val);
+ DisableIO();
+}
+
+uint16_t get_a8bit_reg(uint8_t reg)
+{
+ uint16_t r;
+ EnableIO();
+ spi8(A800_GET_REGISTER);
+ r = spi_w(reg << 8);
+ DisableIO();
+ return r;
+}
+
+static uint16_t get_a800_reg2(uint8_t reg)
+{
+ uint16_t r;
+ EnableIO();
+ spi8(reg);
+ r = spi_w(0);
+ DisableIO();
+ return r;
+}
+
+void atari8bit_dma_write(const uint8_t *buf, uint32_t addr, uint32_t len)
+{
+ user_io_set_index(99);
+ user_io_set_download(1, addr);
+ user_io_file_tx_data(buf, len);
+ user_io_set_download(0);
+}
+
+static void atari800_dma_read(uint8_t *buf, uint32_t addr, uint32_t len)
+{
+ user_io_set_index(99);
+ user_io_set_upload(1, addr);
+ user_io_file_rx_data(buf, len);
+ user_io_set_upload(0);
+}
+
+void atari8bit_dma_zero(uint32_t addr, uint32_t len)
+{
+ memset(a8bit_buffer, 0, BUFFER_SIZE);
+ uint32_t to_write = len > BUFFER_SIZE ? BUFFER_SIZE : len;
+
+ user_io_set_index(99);
+ user_io_set_download(1, addr);
+ while(len)
+ {
+ user_io_file_tx_data(a8bit_buffer, to_write);
+ len -= to_write;
+ to_write = len > BUFFER_SIZE ? BUFFER_SIZE : len;
+ }
+ user_io_set_upload(0);
+}
+
+static void reboot(uint8_t cold, uint8_t pause)
+{
+ int i;
+
+ set_a8bit_reg(REG_PAUSE, 1);
+ if (cold)
+ {
+ set_a8bit_reg(REG_FREEZER, 0);
+ // Initialize the first 64K of SDRAM with a pattern
+ for(i = 0; i < BUFFER_SIZE; i += 2)
+ {
+ a8bit_buffer[i] = 0xFF;
+ a8bit_buffer[i+1] = 0x00;
+ }
+ user_io_set_index(99);
+ user_io_set_download(1, SDRAM_BASE);
+ for(i = 0; i < 0x10000 / BUFFER_SIZE; i++) user_io_file_tx_data(a8bit_buffer, BUFFER_SIZE);
+ user_io_set_upload(0);
+ }
+ else
+ {
+ FileClose(&xex_file);
+ }
+
+ // Both cold==1 and pause==1 is a special case when
+ // the XEX loader performs a cold/warm boot to push
+ // in the loader, in this case on the 800 we just want
+ // the same effect as pressing the RESET (so soft)
+ // while we actually mean a power cycle with forced
+ // OS initialization. (In other words, on 800 a power
+ // cycle does not allow to pre-init the OS to do a warm
+ // start, it will always be cold).
+
+ set_a8bit_reg(REG_XEX_LOADER, 0);
+
+ if((get_a8bit_reg(REG_ATARI_STATUS1) & STATUS1_MASK_MODE800) && (!cold || pause))
+ {
+ set_a8bit_reg(REG_RESET_RNMI, 1);
+ set_a8bit_reg(REG_RESET_RNMI, 0);
+ }
+ else
+ {
+ set_a8bit_reg(REG_RESET, 1);
+ set_a8bit_reg(REG_RESET, 0);
+ }
+
+ if(cold)
+ {
+ set_a8bit_reg(REG_FREEZER, 1);
+ }
+ set_a8bit_reg(REG_PAUSE, pause);
+}
+
+static void uart_init(uint8_t divisor)
+{
+ set_a8bit_reg(REG_SIO_SETDIV, (divisor << 1) + 1);
+}
+
+static uint8_t uart_full()
+{
+ return (get_a800_reg2(A800_SIO_TX_STAT) >> 9) & 0x1;
+}
+
+static void uart_send(uint8_t data)
+{
+#ifdef USE_SCHEDULER
+ int counter = 0;
+#endif
+ while(uart_full())
+ {
+ usleep(100);
+#ifdef USE_SCHEDULER
+ counter++;
+ if(counter > 10)
+ {
+ counter = 0;
+ input_poll(0);
+ scheduler_yield();
+ }
+#endif
+ }
+ set_a8bit_reg(REG_SIO_TX, data);
+}
+
+static uint8_t uart_available()
+{
+ return !((get_a800_reg2(A800_SIO_RX_STAT) >> 8) & 0x1);
+}
+
+static uint16_t uart_receive()
+{
+#ifdef USE_SCHEDULER
+ int counter = 0;
+#endif
+ while(!uart_available())
+ {
+ usleep(100);
+ counter++;
+#ifdef USE_SCHEDULER
+ if(counter > 10)
+ {
+ counter = 0;
+ input_poll(0);
+ scheduler_yield();
+ }
+#endif
+ }
+ return get_a800_reg2(A800_SIO_RX);
+}
+
+static void uart_switch()
+{
+ // Working with this for a while now, I still have no clue what it does... :/
+ set_a8bit_reg(REG_SIO_SETDIV, (uint8_t)(get_a800_reg2(A800_SIO_GETDIV)-1));
+}
+
+static uint16_t uart_error()
+{
+ return get_a800_reg2(A800_SIO_ERROR);
+}
+
+static int cart_matches_total;
+static uint8_t cart_matches_mode[16]; // The are 15 supported 64K carts, this is the max ATM
+static int cart_matches_idx[16];
+static uint8_t cart_match_car;
+static int mounted_cart1_size;
+static unsigned char cart_io_index;
+
+int atari800_get_match_cart_count()
+{
+ return cart_matches_total;
+}
+
+const char *atari800_get_cart_match_name(int match_index)
+{
+ return cart_def[cart_matches_idx[match_index]].name;
+}
+
+void atari800_umount_cartridge(uint8_t stacked)
+{
+ // TODO Clever cart deselect 1 & 2 and reboot?
+ set_a8bit_reg(stacked ? REG_CART2_SELECT : REG_CART1_SELECT, 0);
+ if(!stacked)
+ {
+ mounted_cart1_size = 0;
+ }
+}
+
+int atari800_check_cartridge_file(const char* name, unsigned char index)
+{
+ fileTYPE f = {};
+
+ int to_read = 0x2000;
+ int idx = 0;
+ int file_size;
+
+ cart_match_car = 0;
+ cart_matches_total = 0;
+ cart_io_index = index;
+
+ int ext_index = index >> 6;
+ uint8_t stacked = (index & 0x3F) == 9;
+
+ if (!(stacked && mounted_cart1_size > 0x100000) && FileOpen(&f, name))
+ {
+ file_size = f.size;
+ if (!ext_index)
+ {
+ to_read = 16;
+ cart_match_car = 1;
+ }
+
+ if(to_read > f.size)
+ {
+ to_read = f.size;
+ }
+ FileReadAdv(&f, a8bit_buffer, to_read);
+ FileClose(&f);
+
+ if(cart_match_car)
+ {
+ // CAR file, presumably, let's check further
+ if (a8bit_buffer[0] == 'C' && a8bit_buffer[1] == 'A' && a8bit_buffer[2] == 'R' && a8bit_buffer[3] == 'T')
+ {
+ while (cart_def[idx].cart_type)
+ {
+ if(cart_def[idx].cart_type == a8bit_buffer[7])
+ {
+ cart_matches_idx[0] = idx;
+ cart_matches_mode[0] = cart_def[idx].cart_mode;
+ cart_matches_total = 1;
+ break;
+ }
+ idx++;
+ }
+ }
+ }
+ else
+ {
+ // First check for Ultimate & SIDE2 SDX cartridges
+ if(to_read >= 0x2000 && a8bit_buffer[0] == 'S' && a8bit_buffer[1] == 'D' && a8bit_buffer[2] == 'X' && (a8bit_buffer[0x1FF3] == 0xE0 || a8bit_buffer[0x1FF3] == 0xE1))
+ {
+ cart_matches_idx[0] = -1;
+ cart_matches_mode[0] = (a8bit_buffer[0x1FF3] == 0xE1) ? TC_MODE_SDX_SIDE2 : TC_MODE_SDX_U1MB;
+ cart_matches_total = 1;
+ }
+ else
+ {
+ while (cart_def[idx].cart_type)
+ {
+ if(cart_def[idx].size == (file_size >> 10))
+ {
+ cart_matches_idx[cart_matches_total] = idx;
+ cart_matches_mode[cart_matches_total] = cart_def[idx].cart_mode;
+ cart_matches_total++;
+ }
+ idx++;
+ }
+ }
+ }
+ }
+
+ return cart_matches_total;
+}
+
+void atari800_open_cartridge_file(const char* name, int match_index)
+{
+ uint8_t stacked = (cart_io_index & 0x3F) == 9;
+ uint8_t *buf = &a8bit_buffer[0];
+ uint8_t *buf2 = &a8bit_buffer[4096];
+ fileTYPE f = {};
+ int offset = cart_match_car ? 16 : 0;
+ uint8_t cart_type = cart_def[cart_matches_idx[match_index]].cart_type;
+
+ if (FileOpen(&f, name))
+ {
+ set_a8bit_reg(REG_PAUSE, 1);
+ set_a8bit_reg(REG_CART2_SELECT, 0);
+ if(!stacked) set_a8bit_reg(REG_CART1_SELECT, 0);
+
+ ProgressMessage(0, 0, 0, 0);
+ FileSeek(&f, offset, SEEK_SET);
+
+ user_io_set_index(cart_io_index);
+ user_io_set_download(1);
+
+ if(cart_type == 3 || cart_type == 45)
+ {
+ ProgressMessage("Loading", f.name, 0, 6);
+ FileReadAdv(&f, buf, 4096);
+ user_io_file_tx_data(buf, 4096);
+
+ if (cart_type == 3) FileSeek(&f, offset + 8192, SEEK_SET);
+ ProgressMessage("Loading", f.name, 1, 6);
+ FileReadAdv(&f, buf, 4096);
+ user_io_file_tx_data(buf, 4096);
+
+ if (cart_type == 3) FileSeek(&f, offset + 4096, SEEK_SET);
+ ProgressMessage("Loading", f.name, 2, 6);
+ FileReadAdv(&f, buf2, 4096); // NOTE different buffer!
+ user_io_file_tx_data(buf2, 4096);
+
+ if (cart_type == 3) FileSeek(&f, offset + 12288, SEEK_SET);
+ ProgressMessage("Loading", f.name, 3, 6);
+ FileReadAdv(&f, buf, 4096);
+ user_io_file_tx_data(buf, 4096);
+
+ FileSeek(&f, offset, SEEK_SET);
+ ProgressMessage("Loading", f.name, 4, 6);
+ FileReadAdv(&f, buf, 4096);
+ for(int i = 0; i < 4096; i++) buf[i] &= buf2[i];
+ user_io_file_tx_data(buf, 4096);
+
+ if (cart_type == 3) FileSeek(&f, offset + 8192, SEEK_SET);
+ ProgressMessage("Loading", f.name, 5, 6);
+ FileReadAdv(&f, buf, 4096);
+ for(int i = 0; i < 4096; i++) buf[i] &= buf2[i];
+ user_io_file_tx_data(buf, 4096);
+ }
+ else
+ {
+ while (offset < f.size)
+ {
+ int to_read = f.size - offset;
+ if (to_read > BUFFER_SIZE) to_read = BUFFER_SIZE;
+ ProgressMessage("Loading", f.name, offset, f.size);
+ FileReadAdv(&f, a8bit_buffer, to_read);
+ user_io_file_tx_data(a8bit_buffer, to_read);
+ offset += to_read;
+ }
+ }
+ FileClose(&f);
+ user_io_set_download(0);
+ ProgressMessage(0, 0, 0, 0);
+ set_a8bit_reg(stacked ? REG_CART2_SELECT : REG_CART1_SELECT, cart_matches_mode[match_index]);
+ if(!stacked)
+ {
+ mounted_cart1_size = cart_match_car ? f.size - 16 : f.size;
+ }
+
+ if(!stacked || (get_a8bit_reg(REG_ATARI_STATUS1) & STATUS1_MASK_MODE800))
+ {
+ reboot(1, 0);
+ }
+ }
+}
+
+void atari800_open_bios_file(const char* name, unsigned char index)
+{
+ uint8_t bios_index = (index & 0x3F);
+ uint16_t mode800 = get_a8bit_reg(REG_ATARI_STATUS1) & STATUS1_MASK_MODE800;
+ user_io_file_tx(name, index);
+ if((mode800 && bios_index == 6) || (!mode800 && (bios_index == 4 || bios_index == 5))) reboot(1, 0);
+}
+
+#define MAX_DRIVES 15
+
+typedef struct {
+ fileTYPE file;
+ uint8_t info;
+ uint8_t custom_loader;
+ uint32_t offset;
+ uint32_t meta_offset; // HDD only
+ uint16_t partition_id; // HDD only
+ uint32_t sector_count;
+ uint16_t sector_size;
+ uint8_t atari_sector_status;
+} drive_info_t;
+
+static drive_info_t drive_infos[MAX_DRIVES + 1] = {};
+
+#define INFO_RO 0x40
+#define INFO_HDD 0x80
+#define INFO_META 0x20 // if the HDD uses the meta information sectors
+#define INFO_SS 0x10 // mark that the sector is smaller than the SD card image sector
+
+typedef struct {
+ uint16_t wMagic;
+ uint16_t wPars;
+ uint16_t wSecSize;
+ uint8_t btParsHigh;
+ uint32_t dwCRC;
+ uint32_t dwUNUSED;
+ uint8_t btFlags;
+} __attribute__((packed)) atr_header_t;
+
+typedef struct {
+ uint8_t deviceId;
+ uint8_t command;
+ uint8_t aux1;
+ uint8_t aux2;
+ uint8_t chksum;
+ uint16_t auxab;
+} __attribute__((packed)) sio_command_t;
+
+typedef struct {
+ int bytes;
+ int success;
+ int speed;
+ int respond;
+ uint8_t *sector_buffer;
+} sio_action_t;
+
+typedef struct {
+ uint8_t signature[4];
+ uint16_t version;
+ uint16_t minVersion;
+ uint16_t creator;
+ uint16_t creatorVersion;
+ uint32_t flags;
+ uint16_t imageType;
+ uint8_t density;
+ uint8_t reserved0;
+ uint32_t imageId;
+ uint16_t imageVersion;
+ uint16_t reserved1;
+ uint32_t startData;
+ uint32_t endData;
+ uint8_t reserved2[12];
+} __attribute__((packed)) atxFileHeader;
+
+typedef struct {
+ uint32_t size;
+ uint16_t type;
+ uint16_t reserved0;
+ uint8_t trackNumber;
+ uint8_t reserved1;
+ uint16_t sectorCount;
+ uint16_t rate;
+ uint16_t reserved3;
+ uint32_t flags;
+ uint32_t headerSize;
+ uint8_t reserved4[8];
+} __attribute__((packed)) atxTrackHeader;
+
+typedef struct {
+ uint32_t next;
+ uint16_t type;
+ uint16_t pad0;
+} __attribute__((packed)) atxSectorListHeader;
+
+typedef struct {
+ uint8_t number;
+ uint8_t status;
+ uint16_t timev;
+ uint32_t data;
+} __attribute__((packed)) atxSectorHeader;
+
+typedef struct {
+ uint32_t size;
+ uint8_t type;
+ uint8_t sectorIndex;
+ uint16_t data;
+} __attribute__((packed)) atxTrackChunk;
+
+#define ATX_VERSION 0x01
+
+// number of angular units in a full disk rotation
+#define AU_FULL_ROTATION 26042
+
+#define US_CS_CALC_1050 270 // According to Altirra
+#define US_CS_CALC_810 5136 // According to Altirra
+
+#define US_TRACK_STEP_810 5300 // number of microseconds drive takes to step 1 track
+#define US_TRACK_STEP_1050 20120 // According to Avery / Altirra
+#define US_HEAD_SETTLE_1050 20000
+#define US_HEAD_SETTLE_810 10000
+
+#define US_3FAKE_ROT_810 1566000
+#define US_2FAKE_ROT_1050 942000
+
+// mask for checking FDC status "data lost" bit
+#define MASK_FDC_DLOST 0x04
+// mask for checking FDC status "missing" bit
+#define MASK_FDC_MISSING 0x10
+// mask for checking FDC status extended data bit
+#define MASK_EXTENDED_DATA 0x40
+
+#define MASK_FDC_BUSY 0x01
+#define MASK_FDC_DRQ 0x02
+#define MASK_FDC_CRC 0x08
+#define MASK_FDC_REC 0x20
+#define MASK_FDC_WP 0x40
+#define MASK_RESERVED 0x80
+
+#define MAX_RETRIES_1050 2
+#define MAX_RETRIES_810 4
+
+#define MAX_TRACK 42
+
+enum atx_density { atx_single, atx_medium, atx_double };
+
+#define NUM_ATX_DRIVES 4
+
+#define XEX_SECTOR_SIZE 128
+#define ATARI_SECTOR_BUFFER_SIZE 512
+
+static uint8_t atari_sector_buffer[ATARI_SECTOR_BUFFER_SIZE];
+static uint32_t pre_ce_delay;
+static uint32_t pre_an_delay;
+
+#define DELAY_T2_MIN 100 /* BiboDos needs at least 50us delay before ACK */
+#define DELAY_T5_MIN 600 /* It seems DOS2.0S needs at least 600us delay to function properly */
+#define DELAY_T3_PERIPH 150 /* QMEG OS 3 needs a delay of 150usec between complete and data */
+
+struct {
+ uint16_t bytesPerSector; // number of bytes per sector
+ uint8_t sectorsPerTrack; // number of sectors in each track
+ uint32_t trackOffset[MAX_TRACK]; // pre-calculated info for each track and drive
+ uint8_t currentHeadTrack;
+ uint8_t density;
+} atx_info[NUM_ATX_DRIVES];
+
+struct {
+ uint64_t stamp;
+ uint16_t angle;
+} headPosition;
+
+static uint64_t get_us(uint64_t offset)
+{
+ struct timespec tp;
+
+ clock_gettime(CLOCK_BOOTTIME, &tp);
+
+ uint64_t res;
+
+ res = tp.tv_sec;
+ res *= 1000000;
+ res += (tp.tv_nsec / 1000);
+
+ return (uint64_t)(res + offset);
+}
+
+static uint8_t check_us(uint64_t time)
+{
+ long int remaining = time - get_us(0);
+#ifdef USE_SCHEDULER
+ if(remaining >= (long int)10000)
+ {
+ input_poll(0);
+ scheduler_yield();
+ }
+#endif
+ return remaining <= 0;
+}
+
+static void wait_us(uint64_t time)
+{
+ time = get_us(time);
+ while (!check_us(time));
+}
+
+static void getCurrentHeadPosition()
+{
+ uint64_t s = get_us(0);
+ headPosition.stamp = s;
+ headPosition.angle = (uint16_t)((s >> 3) % AU_FULL_ROTATION);
+}
+
+static void wait_from_stamp(uint32_t us_delay)
+{
+ uint32_t t = get_us(0) - headPosition.stamp;
+ t = us_delay - t;
+ // If, for whatever reason, we are already too late, just skip
+ if(t <= us_delay) wait_us(t);
+}
+
+#define ATX_FILE_ACCESS_READ 1
+#define ATX_FILE_ACCESS_WRITE 2
+
+int atx_file_access(int drv_num, int type, int offset, int len)
+{
+ (void)type; // ATM we only support reading, but writing is potentially possible
+
+ FileSeek(&drive_infos[drv_num].file, offset, SEEK_SET);
+
+ return len == FileReadAdv(&drive_infos[drv_num].file, atari_sector_buffer, len);
+}
+
+static uint8_t loadAtxFile(int drv_num)
+{
+ atxFileHeader *fileHeader;
+ atxTrackHeader *trackHeader;
+ uint8_t r = 0;
+
+ if(!atx_file_access(drv_num, ATX_FILE_ACCESS_READ, 0, sizeof(atxFileHeader))) return r;
+
+ // validate the ATX file header
+ fileHeader = (atxFileHeader *) atari_sector_buffer;
+ if (fileHeader->signature[0] != 'A' || fileHeader->signature[1] != 'T' ||
+ fileHeader->signature[2] != '8' || fileHeader->signature[3] != 'X' ||
+ fileHeader->version != ATX_VERSION || fileHeader->minVersion != ATX_VERSION) return r;
+
+ r = fileHeader->density;
+
+ // enhanced density is 26 sectors per track, single and double density are 18
+ atx_info[drv_num].sectorsPerTrack = (r == atx_medium) ? 26 : 18;
+ // single and enhanced density are 128 bytes per sector, double density is 256
+ atx_info[drv_num].bytesPerSector = (r == atx_double) ? 256 : 128;
+ atx_info[drv_num].density = r;
+ atx_info[drv_num].currentHeadTrack = 0;
+
+ // calculate track offsets
+ uint32_t startOffset = fileHeader->startData;
+
+ for(int track = 0; track < MAX_TRACK ; track++) {
+ if (!atx_file_access(drv_num, ATX_FILE_ACCESS_READ, startOffset, sizeof(atxTrackHeader))) break;
+ trackHeader = (atxTrackHeader *) atari_sector_buffer;
+ atx_info[drv_num].trackOffset[track] = startOffset;
+ startOffset += trackHeader->size;
+ }
+
+ return r;
+}
+
+// Return 0 on full success, 1 on "Atari disk problem" (may have data)
+// -1 on internal storage problem (corrupt ATX)
+static int loadAtxSector(int drv_num, uint16_t num, uint8_t *status)
+{
+
+ atxTrackHeader *trackHeader;
+ atxSectorListHeader *slHeader;
+ atxSectorHeader *sectorHeader;
+ atxTrackChunk *extSectorData;
+
+ int r = 1;
+
+ uint8_t is1050 = get_a8bit_reg(REG_ATARI_STATUS1) & STATUS1_MASK_ATX1050 ? 1 : 0;
+
+ // calculate track and relative sector number from the absolute sector number
+ uint8_t tgtTrackNumber = (num - 1) / atx_info[drv_num].sectorsPerTrack;
+ uint8_t tgtSectorNumber = (num - 1) % atx_info[drv_num].sectorsPerTrack + 1;
+
+ // set initial status (in case the target sector is not found)
+ *status = MASK_FDC_MISSING;
+
+ uint16_t atxSectorSize = atx_info[drv_num].bytesPerSector;
+
+ // delay for track stepping if needed
+ int diff = tgtTrackNumber - atx_info[drv_num].currentHeadTrack;
+ if (diff)
+ {
+ if (diff > 0)
+ {
+ diff += (is1050 ? 1 : 0);
+ }
+ else
+ {
+ diff = -diff;
+ }
+ wait_us(is1050 ? (diff*US_TRACK_STEP_1050 + US_HEAD_SETTLE_1050) : (diff*US_TRACK_STEP_810 + US_HEAD_SETTLE_810));
+ }
+
+ getCurrentHeadPosition();
+
+ // set new head track position
+ atx_info[drv_num].currentHeadTrack = tgtTrackNumber;
+ uint16_t sectorCount = 0;
+ // read the track header
+ uint32_t currentFileOffset = atx_info[drv_num].trackOffset[tgtTrackNumber];
+
+ if (currentFileOffset)
+ {
+ if(atx_file_access(drv_num, ATX_FILE_ACCESS_READ, currentFileOffset, sizeof(atxTrackHeader)))
+ {
+ trackHeader = (atxTrackHeader *) atari_sector_buffer;
+ sectorCount = trackHeader->sectorCount;
+ }
+ else
+ {
+ r = -1;
+ }
+ }
+
+ if (trackHeader->trackNumber != tgtTrackNumber || atx_info[drv_num].density != ((trackHeader->flags & 0x2) ? atx_medium : atx_single))
+ {
+ sectorCount = 0;
+ }
+
+ uint32_t trackHeaderSize = trackHeader->headerSize;
+
+ if (sectorCount)
+ {
+ currentFileOffset += trackHeaderSize;
+ if(atx_file_access(drv_num, ATX_FILE_ACCESS_READ, currentFileOffset, sizeof(atxSectorListHeader)))
+ {
+ slHeader = (atxSectorListHeader *) atari_sector_buffer;
+ // sector list header is variable length, so skip any extra header bytes that may be present
+ currentFileOffset += slHeader->next - sectorCount * sizeof(atxSectorHeader);
+ }
+ else
+ {
+ sectorCount = 0;
+ r = -1;
+ }
+ }
+
+ uint32_t tgtSectorOffset; // the offset of the target sector data
+ int16_t weakOffset;
+
+ uint8_t retries = is1050 ? MAX_RETRIES_1050 : MAX_RETRIES_810;
+
+ uint32_t retryOffset = currentFileOffset;
+ uint16_t extSectorSize;
+
+ while (retries > 0)
+ {
+ retries--;
+ currentFileOffset = retryOffset;
+ int pTT;
+ uint16_t tgtSectorIndex = 0; // the index of the target sector within the sector list
+ tgtSectorOffset = 0;
+ weakOffset = -1;
+ // iterate through all sector headers to find the target sector
+
+ if(sectorCount)
+ {
+ for (int i = 0; i < sectorCount; i++)
+ {
+ if(!atx_file_access(drv_num, ATX_FILE_ACCESS_READ, currentFileOffset, sizeof(atxSectorHeader)))
+ {
+ r = -1;
+ break;
+ }
+ sectorHeader = (atxSectorHeader *)atari_sector_buffer;
+
+ // if the sector is not flagged as missing and its number matches the one we're looking for...
+ if (sectorHeader->number == tgtSectorNumber)
+ {
+ if(sectorHeader->status & MASK_FDC_MISSING)
+ {
+ currentFileOffset += sizeof(atxSectorHeader);
+ continue;
+ }
+ // check if it's the next sector that the head would encounter angularly...
+ int tt = sectorHeader->timev - headPosition.angle;
+ if (!tgtSectorOffset || (tt > 0 && pTT <= 0) || (tt > 0 && pTT > 0 && tt < pTT) || (tt <= 0 && pTT <= 0 && tt < pTT))
+ {
+ pTT = tt;
+ *status = sectorHeader->status;
+ tgtSectorIndex = i;
+ tgtSectorOffset = sectorHeader->data;
+ }
+ }
+ currentFileOffset += sizeof(atxSectorHeader);
+ }
+ }
+
+ uint16_t actSectorSize = atxSectorSize;
+ extSectorSize = 0;
+ // if an extended data record exists for this track, iterate through all track chunks to search
+ // for those records (note that we stop looking for chunks when we hit the 8-byte terminator; length == 0)
+ if (*status & MASK_EXTENDED_DATA)
+ {
+ currentFileOffset = atx_info[drv_num].trackOffset[tgtTrackNumber] + trackHeaderSize;
+ do {
+ if(!atx_file_access(drv_num, ATX_FILE_ACCESS_READ, currentFileOffset, sizeof(atxTrackChunk)))
+ {
+ r = -1;
+ break;
+ }
+ extSectorData = (atxTrackChunk *) atari_sector_buffer;
+ if (extSectorData->size)
+ {
+ // if the target sector has a weak data flag, grab the start weak offset within the sector data
+ if (extSectorData->sectorIndex == tgtSectorIndex)
+ {
+ if(extSectorData->type == 0x10)
+ { // weak sector
+ weakOffset = extSectorData->data;
+ }
+ else if(extSectorData->type == 0x11)
+ { // extended sector
+ extSectorSize = 128 << extSectorData->data;
+ // 1050 waits for long sectors, 810 does not
+ if(is1050 ? (extSectorSize > actSectorSize) : (extSectorSize < actSectorSize))
+ {
+ actSectorSize = extSectorSize;
+ }
+ }
+ }
+ currentFileOffset += extSectorData->size;
+ }
+ } while (extSectorData->size);
+ }
+
+ if (tgtSectorOffset)
+ {
+ if(!atx_file_access(drv_num, ATX_FILE_ACCESS_READ, atx_info[drv_num].trackOffset[tgtTrackNumber] + tgtSectorOffset, atxSectorSize))
+ {
+ r = -1;
+ tgtSectorOffset = 0;
+ }
+
+ uint16_t au_one_sector_read = (23+actSectorSize)*(atx_info[drv_num].density == atx_single ? 8 : 4)+2;
+ // We will need to circulate around the disk one more time if we are re-reading the just written sector
+ wait_from_stamp((au_one_sector_read + pTT + (pTT > 0 ? 0 : AU_FULL_ROTATION))*8);
+
+ if(*status)
+ {
+ // This is according to Altirra, but it breaks DjayBee's test J in 1050 mode?!
+ // wait_us(is1050 ? (US_TRACK_STEP_1050+US_HEAD_SETTLE_1050) : (AU_FULL_ROTATION*8));
+ // This is what seems to work:
+ wait_us(AU_FULL_ROTATION*8);
+ }
+ }
+ else
+ {
+ // No matching sector found at all or the track does not match the disk density
+ wait_from_stamp(is1050 ? US_2FAKE_ROT_1050 : US_3FAKE_ROT_810);
+ if(is1050 || retries == 2)
+ {
+ // Repositioning the head for the target track
+ if(!is1050)
+ {
+ wait_us((43+tgtTrackNumber)*US_TRACK_STEP_810+US_HEAD_SETTLE_810);
+ }
+ else if(tgtTrackNumber)
+ {
+ wait_us((2*tgtTrackNumber+1)*US_TRACK_STEP_1050+US_HEAD_SETTLE_1050);
+ }
+ }
+ }
+
+ getCurrentHeadPosition();
+
+ if(!*status || r < 0) break;
+ }
+
+ *status &= ~(MASK_RESERVED | MASK_EXTENDED_DATA);
+
+ if (*status & MASK_FDC_DLOST)
+ {
+ if(is1050)
+ {
+ *status |= MASK_FDC_DRQ;
+ }
+ else
+ {
+ *status &= ~(MASK_FDC_DLOST | MASK_FDC_CRC);
+ *status |= MASK_FDC_BUSY;
+ }
+ }
+ if(!is1050 && (*status & MASK_FDC_REC)) *status |= MASK_FDC_WP;
+
+ if (tgtSectorOffset && !*status && r >= 0) r = 0;
+
+ // if a weak offset is defined, randomize the appropriate data
+ if (weakOffset > -1)
+ {
+ for (int i = weakOffset; i < atxSectorSize; i++)
+ {
+ atari_sector_buffer[i] = rand();
+ }
+ }
+
+ wait_from_stamp(is1050 ? US_CS_CALC_1050 : US_CS_CALC_810);
+ // There is no file reading since last time stamp, so the alternative
+ // below is probably equally good
+ //wait_us(is1050 ? US_CS_CALC_1050 : US_CS_CALC_810);
+
+ // the Atari expects an inverted FDC status byte
+ *status = ~(*status);
+
+ // return the number of bytes read
+ return r;
+}
+
+static int speed_index = 0;
+static const uint8_t speeds[] = {0x28, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
+
+#define ui_speed_index ((get_a8bit_reg(REG_ATARI_STATUS1) >> 8) & 0x07)
+
+static uint8_t get_checksum(uint8_t* buf, int len)
+{
+ uint8_t sumo = 0;
+ uint8_t sum = 0;
+ for(int i = 0; i < len; i++)
+ {
+ sum += buf[i];
+ if(sum < sumo) sum++;
+ sumo = sum;
+ }
+ return sum;
+}
+
+static void uart_send_buffer(uint8_t *buf, int len)
+{
+ while(len-- > 0) uart_send(*buf++);
+}
+
+static void uart_send_cmpl_and_atari_sector_buffer_and_check_sum(uint8_t *buf, int len, int success)
+{
+ wait_us(pre_ce_delay);
+ uart_send(success ? 'C' : 'E');
+ wait_us(DELAY_T3_PERIPH);
+ uart_send_buffer(buf, len);
+ uart_send(get_checksum(buf, len));
+}
+
+static uint8_t hdd_partition_scan(fileTYPE *file, uint8_t info)
+{
+ int offset = 0;
+ FileSeek(file, offset, SEEK_SET);
+ if(FileReadAdv(file, atari_sector_buffer, 256) != 256) return 0;
+
+ if(atari_sector_buffer[1] != 'A' || atari_sector_buffer[2] != 'P' || atari_sector_buffer[3] != 'T')
+ {
+ if(FileReadAdv(file, atari_sector_buffer, 256) != 256) return 0;
+ offset = 0xC2;
+ while(offset < 0x100)
+ {
+ if(atari_sector_buffer[offset] == 0x7F)
+ {
+ offset = ((atari_sector_buffer[offset+4]) |
+ ((atari_sector_buffer[offset+5] << 8)) |
+ ((atari_sector_buffer[offset+6] << 16)) |
+ ((atari_sector_buffer[offset+7] << 24))) << 9;
+ FileSeek(file, offset, SEEK_SET);
+ if(FileReadAdv(file, atari_sector_buffer, 256) != 256 || atari_sector_buffer[1] != 'A' || atari_sector_buffer[2] != 'P' || atari_sector_buffer[3] != 'T') return 0;
+ break;
+ }
+ offset += 16;
+ }
+ }
+
+ if(atari_sector_buffer[0] == 0x10) info |= INFO_META;
+ else if(atari_sector_buffer[0]) return 0;
+
+ info |= (atari_sector_buffer[4] & 0xF);
+ for(int pidx = 0; pidx < 15; pidx++)
+ {
+ int i = (pidx + 1)*16;
+ if(!atari_sector_buffer[i])
+ {
+ // empty slot
+ if(drive_infos[pidx].info & INFO_HDD) drive_infos[pidx].info = 0;
+ }
+ else
+ {
+ drive_infos[pidx].info = (info & 0xC0) | (atari_sector_buffer[i] & 0x40 ? INFO_META : 0) |
+ ((atari_sector_buffer[i] & 0x30) || (atari_sector_buffer[i+12] & 0x80) ? INFO_RO : 0);
+ atari_sector_buffer[i] &= 0x8F;
+ if(atari_sector_buffer[i] > 3 || (atari_sector_buffer[i+1] != 0x00 && atari_sector_buffer[i+1] != 0x03) || !(atari_sector_buffer[i+12] & 0x40))
+ {
+ drive_infos[pidx].info = 0; // TODO ?! Is this enough to fully disable the drive?
+ }
+ else
+ {
+ drive_infos[pidx].sector_size = 128 << (atari_sector_buffer[i]-1);
+ if(drive_infos[pidx].sector_size != 512)
+ {
+ drive_infos[pidx].info |= INFO_SS;
+ }
+ drive_infos[pidx].offset =
+ ( atari_sector_buffer[i+2] |
+ (atari_sector_buffer[i+3] << 8) |
+ (atari_sector_buffer[i+4] << 16) |
+ (atari_sector_buffer[i+5] << 24) ) << 9;
+ drive_infos[pidx].sector_count =
+ atari_sector_buffer[i+6] |
+ (atari_sector_buffer[i+7] << 8) |
+ (atari_sector_buffer[i+8] << 16) |
+ (atari_sector_buffer[i+9] << 24);
+ drive_infos[pidx].partition_id = atari_sector_buffer[i+10] | (atari_sector_buffer[i+11] << 8);
+ if(atari_sector_buffer[i+1] == 0x00) // DOS partition
+ {
+ uint16_t p_offset =
+ (atari_sector_buffer[i+14] | (atari_sector_buffer[i+15] << 8)) << 9;
+ drive_infos[pidx].meta_offset = drive_infos[pidx].offset - 512;
+ drive_infos[pidx].offset += p_offset;
+ }
+ else // External partition
+ {
+ drive_infos[pidx].meta_offset =
+ (atari_sector_buffer[i+13] |
+ (atari_sector_buffer[i+14] << 8) |
+ (atari_sector_buffer[i+15] << 16)) << 9;
+ }
+ drive_infos[pidx].custom_loader = 0;
+ drive_infos[pidx].atari_sector_status = 0xFF;
+ }
+ }
+ }
+ return info;
+}
+
+static void set_drive_status(int drive_number, const char *name, uint8_t ext_index)
+{
+ uint8_t info = 0;
+ atr_header_t atr_header;
+
+ if(drive_number == 4) drive_number = MAX_DRIVES;
+
+ if(drive_number < MAX_DRIVES && (drive_infos[drive_number].info & INFO_HDD)) return;
+
+ if(!name[0])
+ {
+ FileClose(&drive_infos[drive_number].file);
+ if(drive_number == MAX_DRIVES)
+ {
+ for(int i = 0; i < MAX_DRIVES; i++)
+ {
+ if(drive_infos[i].info & INFO_HDD) drive_infos[i].info = 0;
+ }
+ }
+ return;
+ }
+
+ uint8_t read_only = (ext_index == 1) || (ext_index == 3) ||
+ !FileCanWrite(name) || (get_a8bit_reg(REG_ATARI_STATUS1) & STATUS1_MASK_RDONLY);
+
+
+ if(!FileOpenEx(&drive_infos[drive_number].file, name, read_only ? O_RDONLY : (O_RDWR | O_SYNC))) return;
+
+ if(read_only) info |= INFO_RO;
+
+ if(drive_number < MAX_DRIVES && ext_index == 0) // ATR only
+ {
+ if (FileReadAdv(&drive_infos[drive_number].file, (uint8_t *)&atr_header, 16) != 16)
+ {
+ FileClose(&drive_infos[drive_number].file);
+ return;
+ }
+ }
+
+ drive_infos[drive_number].custom_loader = 0;
+ drive_infos[drive_number].atari_sector_status = 0xff;
+
+ if (ext_index == 2) // XDF
+ {
+ drive_infos[drive_number].offset = 0;
+ drive_infos[drive_number].sector_count = drive_infos[drive_number].file.size / 0x80;
+ drive_infos[drive_number].sector_size = 0x80;
+ }
+ else if (ext_index == 3) // ATX
+ {
+ drive_infos[drive_number].custom_loader = 2;
+ uint8_t atxType = loadAtxFile(drive_number);
+ drive_infos[drive_number].sector_count = (atxType == atx_medium) ? 1040 : 720;
+ drive_infos[drive_number].sector_size = (atxType == atx_double) ? 256 : 128;
+ }
+ else if (ext_index == 1) // XEX
+ {
+ drive_infos[drive_number].custom_loader = 1;
+ drive_infos[drive_number].sector_count = 0x173+(drive_infos[drive_number].file.size+(XEX_SECTOR_SIZE-4))/(XEX_SECTOR_SIZE-3);
+ drive_infos[drive_number].sector_size = XEX_SECTOR_SIZE;
+ }
+ else // ATR or IMG
+ {
+ if(drive_number < MAX_DRIVES)
+ {
+ drive_infos[drive_number].offset = 16;
+ if(atr_header.wSecSize == 512)
+ {
+ drive_infos[drive_number].sector_count = (atr_header.wPars | (atr_header.btParsHigh << 16)) / 32;
+ }
+ else
+ {
+ drive_infos[drive_number].sector_count = 3 + ((atr_header.wPars | (atr_header.btParsHigh << 16))*16 - 128*3) / atr_header.wSecSize;
+ }
+ drive_infos[drive_number].sector_size = atr_header.wSecSize;
+ }
+ else
+ {
+ info |= INFO_HDD;
+ drive_infos[drive_number].sector_size = 512;
+ drive_infos[drive_number].sector_count = drive_infos[drive_number].file.size / 0x200;
+ drive_infos[drive_number].atari_sector_status = 0;
+ drive_infos[drive_number].offset = 0;
+ info = hdd_partition_scan(&drive_infos[drive_number].file, info);
+ if(!info)
+ {
+ FileClose(&drive_infos[drive_number].file);
+ return;
+ }
+ }
+ }
+ drive_infos[drive_number].info = info;
+}
+
+typedef void (*CommandHandler)(sio_command_t, int, fileTYPE *, sio_action_t *);
+
+static void handle_speed(sio_command_t command, int drive_number, fileTYPE *file, sio_action_t *action)
+{
+ (void)file;
+ // We should be guaranteed that this is not called in PBI mode,
+ // so no need to check the PBI bit
+ action->bytes = 1;
+ if(drive_infos[drive_number].custom_loader == 2)
+ {
+ speed_index = 0;
+ action->sector_buffer[0] = speeds[speed_index];
+ }
+ else
+ {
+ speed_index = command.aux2 ? 0 : ui_speed_index;
+ action->sector_buffer[0] = speeds[ui_speed_index];
+ }
+ action->speed = speeds[speed_index] + 6;
+}
+
+static void handle_format(sio_command_t command, int drive_number, fileTYPE *file, sio_action_t *action)
+{
+ (void)command;
+ memset(action->sector_buffer, 0, drive_infos[drive_number].sector_size);
+ int i = drive_infos[drive_number].offset;
+ FileSeek(file, i, SEEK_SET);
+ for (; i != file->size; i += 128)
+ {
+ FileWriteAdv(file, action->sector_buffer, 128);
+ }
+ action->sector_buffer[0] = 0xff;
+ action->sector_buffer[1] = 0xff;
+ action->bytes = drive_infos[drive_number].sector_size;
+}
+
+static void handle_read_percom(sio_command_t command, int drive_number, fileTYPE *file, sio_action_t *action)
+{
+ (void)command;
+ (void)file;
+ uint16_t total_sectors = drive_infos[drive_number].sector_count;
+ memset(action->sector_buffer, 0, 12);
+ action->sector_buffer[1] = 0x03;
+ action->sector_buffer[6] = drive_infos[drive_number].sector_size >> 8;
+ action->sector_buffer[7] = drive_infos[drive_number].sector_size & 0xff;
+ action->sector_buffer[8] = 0xff;
+
+ if(!(drive_infos[drive_number].info & INFO_HDD) && (total_sectors == 720 || total_sectors == 1040 || total_sectors == 1440))
+ {
+ total_sectors = total_sectors / 40;
+ if(total_sectors == 36)
+ {
+ total_sectors = total_sectors / 2;
+ action->sector_buffer[4] = 1;
+ }
+ action->sector_buffer[0] = 40;
+ action->sector_buffer[5] = (drive_infos[drive_number].sector_size == 256 || total_sectors == 26) ? 4 : 0;
+ }
+ else
+ {
+ action->sector_buffer[0] = 1;
+ action->sector_buffer[5] = (drive_infos[drive_number].sector_size == 128) ? 0 : 4;
+ }
+ action->sector_buffer[2] = total_sectors >> 8;
+ action->sector_buffer[3] = total_sectors & 0xff;
+ action->bytes = 12;
+}
+
+static void handle_force_media_change(sio_command_t command, int drive_number, fileTYPE *file, sio_action_t *action)
+{
+ (void)command;
+ action->respond = 0;
+ uint8_t info = hdd_partition_scan(file, INFO_HDD | ((file->mode & O_RDONLY) ? INFO_RO : 0));
+ if(info)
+ {
+ drive_infos[drive_number].info = info;
+ }
+ else
+ {
+ action->success = 0;
+ }
+}
+
+static void handle_device_info(sio_command_t command, int drive_number, fileTYPE *file, sio_action_t *action)
+{
+ (void)command;
+ memset(action->sector_buffer, 0, action->bytes);
+ action->sector_buffer[0] = 1;
+ action->sector_buffer[2] = 1;
+ action->sector_buffer[6] = drive_infos[drive_number].sector_size;
+ action->sector_buffer[7] = drive_infos[drive_number].sector_size >> 8;
+ action->sector_buffer[8] = drive_infos[drive_number].sector_count;
+ action->sector_buffer[9] = drive_infos[drive_number].sector_count >> 8;
+ action->sector_buffer[10] = drive_infos[drive_number].sector_count >> 16;
+ action->sector_buffer[11] = drive_infos[drive_number].sector_count >> 24;
+ if(drive_number == MAX_DRIVES)
+ {
+ atari800_dma_read(a8bit_buffer, ATARI_BASE + 0xDFA0, 0x60);
+ memcpy(&action->sector_buffer[0x10], &a8bit_buffer[0x0F], a8bit_buffer[0x0E]);
+ memcpy(&action->sector_buffer[0x38], &a8bit_buffer[0x38], a8bit_buffer[0x37]);
+ }
+ else
+ {
+ action->sector_buffer[3] = drive_infos[drive_number].partition_id;
+ action->sector_buffer[4] = drive_infos[drive_number].partition_id >> 8;
+ // TODO This made me realize that we are limited in the SD image size
+ action->sector_buffer[12] = drive_infos[drive_number].offset >> 9;
+ action->sector_buffer[13] = drive_infos[drive_number].offset >> 17;
+ action->sector_buffer[14] = drive_infos[drive_number].offset >> 25;
+ if(drive_infos[drive_number].info & INFO_META)
+ {
+ FileSeek(file, drive_infos[drive_number].meta_offset + 16, SEEK_SET);
+ if(FileReadAdv(file, &action->sector_buffer[0x10], 40) != 40) action->success = 0;
+ }
+ }
+}
+
+#if 0
+void handle_device_status(sio_command_t command, int drive_number, fileTYPE *file, sio_action_t *action)
+{
+ memset(action->sector_buffer, 0, action->bytes);
+ action->sector_buffer[0x0C] = 0x3F;
+}
+#endif
+
+static void handle_get_status(sio_command_t command, int drive_number, fileTYPE *file, sio_action_t *action)
+{
+ (void)command;
+ (void)file;
+ uint8_t status = 0x40;
+
+ if(drive_number != MAX_DRIVES)
+ {
+
+ status = 0x10; // Motor on;
+
+ if (drive_infos[drive_number].info & INFO_RO)
+ {
+ status |= 0x08; // write protected; // no write support yet...
+ }
+ if(drive_infos[drive_number].sector_count != 720)
+ {
+ status |= 0x80; // medium density - or a strange one...
+ }
+ if(drive_infos[drive_number].sector_size != 128)
+ {
+ status |= 0x20; // 256 byte sectors
+ }
+ }
+ action->sector_buffer[0] = status;
+ action->sector_buffer[1] = drive_infos[drive_number].atari_sector_status;
+ action->sector_buffer[2] = drive_number == MAX_DRIVES ? 0x10 : 0xe0; // What should be our ID?
+ if(drive_number == MAX_DRIVES)
+ {
+ atari800_dma_read(&action->sector_buffer[3], ATARI_BASE + 0xDFAD, 1);
+ }
+ else
+ {
+ action->sector_buffer[3] = 0;
+ }
+ action->bytes = 4;
+}
+
+static int set_location_offset(int drive_number, uint32_t sector, uint32_t *location)
+{
+ *location = drive_infos[drive_number].offset;
+ int sector_size = drive_infos[drive_number].info & (INFO_HDD | INFO_SS) ? 512 : drive_infos[drive_number].sector_size;
+ if(drive_infos[drive_number].sector_size == 512 || (drive_infos[drive_number].info & INFO_HDD))
+ {
+ if(drive_number != MAX_DRIVES)
+ {
+ sector--;
+ }
+ *location += sector * sector_size;
+ }
+ else
+ {
+ if(sector > 3)
+ {
+ *location += 128*3 + (sector-4) * sector_size;
+ }
+ else
+ {
+ *location = *location + 128 * (sector - 1);
+ sector_size = 128;
+ }
+ }
+ return sector_size;
+}
+
+static void handle_write(sio_command_t command, int drive_number, fileTYPE *file, sio_action_t *action)
+{
+ uint8_t pbi = command.deviceId & 0x40;
+ uint32_t sector = (command.auxab << 16) | command.aux1 | (command.aux2 << 8);
+ int sector_size = 0;
+ uint32_t location = 0;
+
+ action->respond = 0;
+
+ sector_size = set_location_offset(drive_number, sector, &location);
+
+ uint8_t checksum = 0;
+ uint8_t expchk = 0;
+
+ if(!pbi)
+ {
+ for (int i = 0; i < sector_size; i++)
+ {
+ action->sector_buffer[i] = uart_receive();
+ }
+ checksum = uart_receive();
+ expchk = get_checksum(action->sector_buffer, sector_size);
+ }
+ if (checksum == expchk)
+ {
+ if(!pbi)
+ {
+ wait_us(850);
+ uart_send('A');
+ }
+
+ FileSeek(file, location, SEEK_SET);
+ if(drive_infos[drive_number].info & INFO_SS)
+ {
+ int step = 512 / drive_infos[drive_number].sector_size;
+ sector_size = ATARI_SECTOR_BUFFER_SIZE;
+ memset(atari_sector_buffer, 0, sector_size);
+ int i = 0;
+ int written = 0;
+ while(written < sector_size)
+ {
+ atari_sector_buffer[written] = action->sector_buffer[i++];
+ written += step;
+ }
+ FileWriteAdv(file, atari_sector_buffer, sector_size);
+ }
+ else
+ {
+ FileWriteAdv(file, atari_sector_buffer, sector_size);
+ }
+
+ int ok = 1;
+
+ if (command.command == 0x57)
+ {
+ FileSeek(file, location, SEEK_SET);
+ FileReadAdv(file, a8bit_buffer, sector_size);
+
+ for (int i = 0; i < sector_size; i++)
+ {
+ if (a8bit_buffer[i] != action->sector_buffer[i]) ok = 0;
+ }
+ }
+
+ if(pbi)
+ {
+ action->success = ok;
+ }
+ else
+ {
+ wait_us(DELAY_T5_MIN);
+ uart_send(ok ? 'C' : 'E');
+ }
+ }
+ else
+ {
+ uart_send('N');
+ }
+}
+
+static void handle_read(sio_command_t command, int drive_number, fileTYPE *file, sio_action_t *action)
+{
+ uint32_t sector = (command.auxab << 16) | command.aux1 | (command.aux2<<8);
+
+ uint32_t location = 0;
+
+ if(drive_infos[drive_number].custom_loader == 1) // XEX file
+ {
+ int file_sectors;
+
+ if (sector <= 2)
+ {
+ memcpy(action->sector_buffer, &boot_xex_loader[(sector - 1) * XEX_SECTOR_SIZE], XEX_SECTOR_SIZE);
+ }
+ else if(sector == 0x168)
+ {
+ file_sectors = drive_infos[drive_number].sector_count;
+ int vtoc_sectors = file_sectors / 1024;
+ int rem = file_sectors - (vtoc_sectors * 1024);
+ if(rem > 943) {
+ vtoc_sectors += 2;
+ }
+ else if(rem)
+ {
+ vtoc_sectors++;
+ }
+ if(!(vtoc_sectors % 2))
+ {
+ vtoc_sectors++;
+ }
+
+ file_sectors -= (vtoc_sectors + 12);
+ action->sector_buffer[0] = (uint8_t)((vtoc_sectors + 3)/2);
+ goto set_number_of_sectors_to_buffer_1_2;
+ }
+ else if(sector == 0x169)
+ {
+ file_sectors = drive_infos[drive_number].sector_count - 0x173;
+ {
+ memset(&action->sector_buffer[5], ' ', 8);
+ int si = 0;
+ for(int i = 0; i < 11; i++)
+ {
+ char c = drive_infos[drive_number].file.name[si];
+ if(c == '.')
+ {
+ i = 7;
+ }
+ else
+ {
+ if(c >= 'a' && c <= 'z') c -= 32;
+ else if(c < 'A' || c > 'Z') c = '@';
+ action->sector_buffer[5+i] = c;
+ }
+ si = i == 7 ? strlen(drive_infos[drive_number].file.name) - 3 : si + 1;
+ }
+ }
+ memset(&action->sector_buffer[16], 0, XEX_SECTOR_SIZE-16);
+
+ action->sector_buffer[0]=(file_sectors > 0x28F) ? 0x46 : 0x42;
+
+ action->sector_buffer[3] = 0x71;
+ action->sector_buffer[4] = 0x01;
+set_number_of_sectors_to_buffer_1_2:
+ action->sector_buffer[1] = file_sectors;
+ action->sector_buffer[2] = (file_sectors >> 8);
+ }
+ else if(sector >= 0x171)
+ {
+ FileSeek(file, (sector - 0x171) * (XEX_SECTOR_SIZE - 3), SEEK_SET);
+ int read = FileReadAdv(file, action->sector_buffer, XEX_SECTOR_SIZE - 3);
+ sector = (read < (XEX_SECTOR_SIZE - 3)) ? 0 : sector + 1;
+ action->sector_buffer[XEX_SECTOR_SIZE - 3] = (sector >> 8);
+ action->sector_buffer[XEX_SECTOR_SIZE - 2] = sector;
+ action->sector_buffer[XEX_SECTOR_SIZE - 1] = (uint8_t)read;
+ }
+
+ action->bytes = XEX_SECTOR_SIZE;
+ }
+ else if (drive_infos[drive_number].custom_loader == 2) // ATX
+ {
+ pre_ce_delay = 0; // Taken care of in loadAtxSector
+ int res = loadAtxSector(drive_number, sector, &drive_infos[drive_number].atari_sector_status);
+ action->bytes = drive_infos[drive_number].sector_size;
+ action->success = (res == 0);
+ }
+ else
+ {
+ action->bytes = set_location_offset(drive_number, sector, &location);
+ FileSeek(file, location, SEEK_SET);
+ if(drive_infos[drive_number].info & INFO_SS)
+ {
+ uint8_t step = 512 / drive_infos[drive_number].sector_size;
+ FileReadAdv(file, atari_sector_buffer, ATARI_SECTOR_BUFFER_SIZE, -1);
+ int read = 0;
+ int n = 0;
+ while(read < ATARI_SECTOR_BUFFER_SIZE)
+ {
+ action->sector_buffer[n++] = atari_sector_buffer[read];
+ read += step;
+ }
+ }
+ else
+ {
+ FileReadAdv(file, action->sector_buffer, action->bytes, -1);
+ }
+ }
+}
+
+static CommandHandler get_command_handler(sio_command_t command, uint8_t dstats)
+{
+ CommandHandler res = NULL;
+ uint32_t sector = (command.auxab << 16) | command.aux1 | (command.aux2 << 8);
+ // The HDD SD card counts sectors from 0
+ uint8_t min_sector = (command.deviceId & 0x3F) == 0x20 ? 0 : 1;
+ int drive_number = min_sector ? (command.deviceId & 0xf) - 1 : MAX_DRIVES;
+ uint8_t pbi = command.deviceId & 0x40;
+ uint8_t writable = !(drive_infos[drive_number].info & INFO_RO);
+
+ switch (command.command)
+ {
+ case 0x3f:
+ if(!pbi) res = &handle_speed;
+ break;
+ case 0x21: // format single
+ case 0x22: // format enhanced
+ if(writable && !pbi) res = &handle_format;
+ break;
+ case 0x46:
+ if(pbi) res = &handle_force_media_change;
+ break;
+ case 0x4e: // read percom block
+ if(min_sector) res = &handle_read_percom;
+ break;
+ case 0x53: // get status
+ if(!pbi || dstats == 0x40) res = &handle_get_status;
+ break;
+ case 0x50: // write
+ case 0x57: // write with verify
+ if (writable && (!pbi || dstats == 0x80) && sector >= min_sector && sector - min_sector < drive_infos[drive_number].sector_count)
+ res = &handle_write;
+ break;
+ case 0x52: // read
+ if ((!pbi || dstats == 0x40) && sector >= min_sector && sector - min_sector < drive_infos[drive_number].sector_count)
+ {
+ if(drive_infos[drive_number].custom_loader == 2) // ATX!
+ {
+ pre_an_delay = 3220;
+ }
+ res = &handle_read;
+ }
+ break;
+
+ case 0x6E: // PBI device info
+ if(pbi && dstats == 0x40)
+ res = &handle_device_info;
+ break;
+#if 0
+ case 0xEC: // PBI device status
+ if(pbi && dstats == 0x40)
+ res = &handle_device_status;
+ break;
+#endif
+ }
+ return res;
+}
+
+int get_command(sio_command_t *cmd)
+{
+ if(!uart_available()) return 0;
+
+ int success = 1;
+ for (int i = 0; i < 5; i++)
+ {
+ uint16_t data = uart_receive(); // Timeout?
+ if (uart_error() || ((data >> 8) != (i+1)))
+ {
+ success = 0;
+ break;
+ }
+ ((uint8_t *)cmd)[i] = (uint8_t)(data & 0xff);
+ }
+
+ if(!success) return success;
+
+ uart_receive();
+
+ if (get_checksum((uint8_t *)cmd, 4) == cmd->chksum)
+ {
+ uart_switch();
+ }
+ else
+ {
+ success = 0;
+ }
+
+ return success;
+}
+
+static void process_command()
+{
+ sio_command_t command;
+
+ if(!get_command(&command)) { return; }
+ command.auxab = 0;
+
+ int drive = (command.deviceId & 0xf) -1;
+ if (command.deviceId >= 0x31 && command.deviceId <= 0x34 && drive_infos[drive].sector_size != 512)
+ {
+ fileTYPE *file = &drive_infos[drive].file;
+ if (!file->opened()) return;
+ set_a8bit_reg(REG_DRIVE_LED, 1);
+
+ pre_ce_delay = DELAY_T5_MIN;
+ pre_an_delay = DELAY_T2_MIN;
+
+ CommandHandler handle_command = get_command_handler(command, 0);
+
+ wait_us(pre_an_delay);
+
+ if (handle_command)
+ {
+ sio_action_t action;
+ action.bytes = 0;
+ action.success = 1;
+ action.speed = -1;
+ action.respond = 1;
+ action.sector_buffer = atari_sector_buffer;
+
+ uart_send('A');
+ memset(atari_sector_buffer, 0, ATARI_SECTOR_BUFFER_SIZE);
+
+ handle_command(command, drive, file, &action);
+
+ if (action.respond) uart_send_cmpl_and_atari_sector_buffer_and_check_sum(action.sector_buffer, action.bytes, action.success);
+ if (action.speed >= 0)
+ uart_init(action.speed);
+ }
+ else
+ {
+ uart_send('N');
+ }
+ set_a8bit_reg(REG_DRIVE_LED, 0);
+ }
+}
+
+void atari800_set_image(int ext_index, int file_index, const char *name)
+{
+ if(file_index == 5) // XEX file
+ {
+ if(name[0] && FileOpen(&xex_file, name))
+ {
+ xex_file_first_block = 1;
+ reboot(1, 1);
+ set_a8bit_reg(REG_XEX_LOADER, 1);
+ set_a8bit_reg(REG_CART1_SELECT, 0);
+ set_a8bit_reg(REG_CART2_SELECT, 0);
+ uint16_t atari_status1 = get_a8bit_reg(REG_ATARI_STATUS1);
+
+ atari8bit_dma_zero(SDRAM_BASE, 0x10000);
+ atari8bit_dma_write(xex_loader, ATARI_BASE + 0xD100, (uint32_t)sizeof(xex_loader));
+ static uint8_t write_bytes[4];
+
+ write_bytes[0] = 0;
+ atari8bit_dma_write(write_bytes, ATARI_COLDST, 1);
+ atari8bit_dma_write(write_bytes, ATARI_GINTLK, 1);
+ write_bytes[0] = 1;
+ atari8bit_dma_write(write_bytes, ATARI_BASICF, 1);
+ write_bytes[0] = 2;
+ atari8bit_dma_write(write_bytes, ATARI_BOOTFLAG, 1);
+ write_bytes[0] = XEX_INIT1;
+ write_bytes[1] = 0xD1;
+ atari8bit_dma_write(write_bytes, ATARI_CASINI, 2);
+ write_bytes[0] = 0x71;
+ write_bytes[1] = 0xE4;
+ atari8bit_dma_write(write_bytes, ATARI_DOSVEC, 2);
+
+ if(!(atari_status1 & STATUS1_MASK_MODE800))
+ {
+ write_bytes[0] = 0x5C;
+ write_bytes[1] = 0x93;
+ write_bytes[2] = 0x25;
+ atari8bit_dma_write(write_bytes, ATARI_PUPBT, 3);
+ }
+
+ set_a8bit_reg(REG_PAUSE, 0);
+
+ }
+ else
+ {
+ FileClose(&xex_file);
+ }
+ }
+ else if(file_index == 6) // D1: disk image with automatic boot
+ {
+ if(name[0])
+ {
+ set_a8bit_reg(REG_PAUSE, 1);
+ set_a8bit_reg(REG_CART1_SELECT, 0);
+ set_a8bit_reg(REG_CART2_SELECT, 0);
+ }
+ set_drive_status(0, name, ext_index);
+ if(name[0])
+ {
+ reboot(1, 0);
+ set_a8bit_reg(REG_OPTION_FORCE, 1);
+ set_a8bit_reg(REG_OPTION_FORCE, 0);
+ }
+ }
+ else if(file_index < 5) // 5 is the HDD and requires slightly different handling
+ {
+ set_drive_status(file_index, name, ext_index);
+ }
+}
+
+static uint8_t process_command_pbi(const uint8_t *drives_config)
+{
+ // We are more or less guranteed to serve the correct device id and
+ // drive unit number by now, no need to check here
+ // mark a bit (0x40) in deviceId to indicate this is PBI, this is not the same
+ // as the XDCB bit (0x80)
+
+ set_a8bit_reg(REG_DRIVE_LED, 1);
+
+ static uint8_t iocb[16];
+ atari800_dma_read(iocb, ATARI_BASE + 0x300, 16);
+
+ sio_command_t command;
+
+ uint8_t sd_device = (iocb[0] & 0x7F) == 0x20;
+ int drive = iocb[1] - 1;
+ command.deviceId = (iocb[0] + drive) | 0x40; // ddevic + dunit - 1 plus PBI marker
+
+ /*
+ This piece of admitedely contrived logic takes care of diverting or not further processing
+ to SIO routines. The procedure is not exactly the same as on, say, Ultimate 1MB PBI BIOS, nor
+ it is strictly according to the PBI API requirements, here we (safely?) assume there are no
+ other PBI devices (and hence ROM BIOSes), so this allows us to take some shortcuts (which also
+ speeds up things on the Atari / SDX side).
+ */
+
+ uint8_t mode = (sd_device && !drive) ? 1 : ((!sd_device && drive < 4) ? drives_config[drive] : ((iocb[0] & 0x80) >> 7));
+
+ if(sd_device && !drive) drive = MAX_DRIVES;
+
+ fileTYPE *file = &drive_infos[drive < MAX_DRIVES && (drive_infos[drive].info & INFO_HDD) ? MAX_DRIVES : drive].file;
+
+ if(file->opened())
+ {
+ if(drive_infos[drive].info & INFO_HDD) // The type should be then ATR
+ {
+ if(!sd_device) mode = 1;
+ }
+ else
+ {
+ if(drive_infos[drive].custom_loader == 2) mode = 0; // ATX -> Off
+ if(drive_infos[drive].custom_loader == 1) mode = 1; // XEX -> PBI
+ }
+ }
+
+ // HSIO does not handle 512-byte sector ATRs
+ if(!mode || (file->opened() && mode == 2 && drive_infos[drive].sector_size == 512))
+ return 0xFF;
+
+ mode--;
+ if (!file->opened() || mode == 1)
+ {
+ if(!mode) iocb[3] = 0x8A;
+ }
+ else
+ {
+ command.command = iocb[2];
+ command.aux1 = iocb[0xA];
+ command.aux2 = iocb[0xB];
+ command.auxab = (command.deviceId & 0x80) ? iocb[0xC] | (iocb[0xD] << 8) : 0;
+
+ CommandHandler handle_command = get_command_handler(command, iocb[3]);
+ if (handle_command)
+ {
+ sio_action_t action;
+ action.bytes = iocb[8] | (iocb[9] << 8);
+ action.success = 1;
+ action.respond = 1;
+ // Copy over 512 bytes from Atari to the buffer
+ if(action.bytes)
+ {
+ atari800_dma_read(atari_sector_buffer, ATARI_BASE + (iocb[4] | (iocb[5] << 8)), action.bytes);
+ action.sector_buffer = atari_sector_buffer;
+ }
+
+ handle_command(command, drive, file, &action);
+
+ if (action.respond)
+ {
+ iocb[8] = action.bytes & 0xFF;
+ iocb[9] = (action.bytes >> 8) & 0xFF;
+ atari8bit_dma_write(atari_sector_buffer, ATARI_BASE + (iocb[4] | (iocb[5] << 8)), action.bytes);
+ atari8bit_dma_write(&iocb[0x08], ATARI_BASE + 0x308, 2);
+ }
+ iocb[3] = action.success ? 0x01 : 0x90;
+ }
+ else
+ {
+ iocb[3] = 0x8B;
+ }
+ }
+ atari8bit_dma_write(&iocb[0x03], ATARI_BASE + 0x303, 1);
+ set_a8bit_reg(REG_DRIVE_LED, 0);
+ return mode;
+}
+
+void handle_pbi()
+{
+ if(!(get_a8bit_reg(REG_ATARI_STATUS1) & STATUS1_MASK_MODEPBI)) return;
+
+ static uint8_t pbi_ram_base[16];
+ static uint8_t pbi_drives_config[4];
+
+ atari800_dma_read(pbi_ram_base, ATARI_BASE + 0xD100, 16);
+
+ if(pbi_ram_base[0] == 0xa5 && pbi_ram_base[1] == 0xa5)
+ {
+ uint16_t atari_status2 = get_a8bit_reg(REG_ATARI_STATUS2);
+
+ if(pbi_ram_base[3] == 0x01)
+ {
+ pbi_drives_config[0] = (atari_status2 >> 0) & 0x3;
+ pbi_drives_config[1] = (atari_status2 >> 2) & 0x3;
+ pbi_drives_config[2] = (atari_status2 >> 4) & 0x3;
+ pbi_drives_config[3] = (atari_status2 >> 6) & 0x3;
+
+ memcpy(&pbi_ram_base[0x0C], pbi_drives_config, 4);
+ uint8_t boot_drv = (atari_status2 >> 8) & 0x07;
+
+ pbi_ram_base[0x0B] = boot_drv;
+ pbi_ram_base[0x0A] = 0x00;
+ if(boot_drv == 1 && drive_infos[MAX_DRIVES].file.opened())
+ {
+ // APT
+ pbi_ram_base[0x0A] = drive_infos[MAX_DRIVES].info & 0xF;
+ }
+ else if(boot_drv)
+ {
+ pbi_ram_base[0x0A] = boot_drv - 1;
+ }
+ atari8bit_dma_write(&pbi_ram_base[0x0A], ATARI_BASE + 0xD10A, 6);
+ pbi_ram_base[2] = atari_status2 & STATUS2_MASK_SPLASH ? 1 : 0;
+ // Important - this has to be alone and last!
+ pbi_ram_base[3] = 0;
+ atari8bit_dma_write(&pbi_ram_base[0x02], ATARI_BASE + 0xD102, 2);
+ }
+ else if(pbi_ram_base[5] == 0x01)
+ {
+ pbi_ram_base[4] = process_command_pbi(pbi_drives_config);
+ pbi_ram_base[5] = 0;
+ // Same here with the order
+ atari8bit_dma_write(&pbi_ram_base[0x04], ATARI_BASE + 0xD104, 2);
+ }
+ }
+}
+
+static void handle_xex()
+{
+ atari800_dma_read(a8bit_buffer, ATARI_BASE + 0xD100, XEX_READ_STATUS+1);
+
+ if(a8bit_buffer[0] == 0x60)
+ {
+ if(!a8bit_buffer[XEX_READ_STATUS])
+ {
+ uint8_t len_buf[2];
+ int read_offset, to_read, read_len;
+
+ len_buf[0] = 0xFF;
+ len_buf[1] = 0xFF;
+
+ // Point to rts
+ a8bit_buffer[0] = 0x00;
+ a8bit_buffer[1] = 0xD1;
+ atari8bit_dma_write(a8bit_buffer, ATARI_INITAD, 2);
+
+ while(len_buf[0] == 0xFF && len_buf[1] == 0xFF)
+ {
+ if(FileReadAdv(&xex_file, len_buf, 2) != 2) goto xex_eof;
+ }
+ read_offset = len_buf[0] | (len_buf[1] << 8);
+ if(xex_file_first_block)
+ {
+ xex_file_first_block = 0;
+ atari8bit_dma_write(len_buf, ATARI_RUNAD, 2);
+ }
+
+ if(FileReadAdv(&xex_file, len_buf, 2) != 2) goto xex_eof;
+
+ read_len = (len_buf[0] | (len_buf[1] << 8)) + 1 - read_offset;
+ if(read_len < 1) goto xex_eof;
+
+ to_read = read_len > BUFFER_SIZE ? BUFFER_SIZE : read_len;
+
+ while(read_len)
+ {
+ if(FileReadAdv(&xex_file, a8bit_buffer, to_read) != to_read) goto xex_eof;
+ atari8bit_dma_write(a8bit_buffer, ATARI_BASE + read_offset, to_read);
+ read_len -= to_read;
+ read_offset += to_read;
+ to_read = read_len > BUFFER_SIZE ? BUFFER_SIZE : read_len;
+ }
+
+ a8bit_buffer[0] = 0x01;
+ atari8bit_dma_write(a8bit_buffer, ATARI_BASE + 0xD100 + XEX_READ_STATUS, 1);
+ }
+ }
+ // Is loader done?
+ else if(a8bit_buffer[0] == 0x5F)
+xex_eof:
+ {
+ a8bit_buffer[0] = 0xFF;
+ atari8bit_dma_write(a8bit_buffer, ATARI_BASE + 0xD100 + XEX_READ_STATUS, 1);
+ FileClose(&xex_file);
+ }
+
+}
+
+void atari800_poll()
+{
+ uint16_t atari_status1 = get_a8bit_reg(REG_ATARI_STATUS1);
+
+ set_a8bit_reg(REG_PAUSE, atari_status1 & STATUS1_MASK_HALT);
+
+ if (atari_status1 & STATUS1_MASK_SOFTBOOT)
+ {
+ reboot(0, 0);
+ }
+ else if (atari_status1 & STATUS1_MASK_COLDBOOT)
+ {
+ reboot(1, 0);
+ }
+
+ if(xex_file.opened()) handle_xex();
+ handle_pbi();
+ process_command();
+}
+
+void atari800_init()
+{
+ set_a8bit_reg(REG_PAUSE, 1);
+ cart_matches_total = 0;
+ cart_match_car = 0;
+
+ // Try to load bootX.rom ? TODO - limit only to boot3? or require pbibios.rom name?
+ // and rely on the OSD menus?
+ // In any case PBI boot rom should be attempted regardless of the option setting
+ // (there is no menu entry to load it)
+ static char mainpath[512];
+ const char *home = HomeDir();
+
+ if(get_a8bit_reg(REG_ATARI_STATUS1) & STATUS1_MASK_BOOTX)
+ {
+ for(int i = 0; i < 3; i++)
+ {
+ sprintf(mainpath, "%s/boot%d.rom", home, i);
+ user_io_file_tx(mainpath, i << 6);
+ }
+ }
+ sprintf(mainpath, "%s/boot3.rom", home);
+ user_io_file_tx(mainpath, 3 << 6);
+ atari800_reset();
+}
+
+void atari800_reset()
+{
+ set_a8bit_reg(REG_PAUSE, 1);
+ set_a8bit_reg(REG_CART1_SELECT, 0);
+ mounted_cart1_size = 0;
+ set_a8bit_reg(REG_CART2_SELECT, 0);
+ set_a8bit_reg(REG_FREEZER, 0);
+ for(int i=0; i <= MAX_DRIVES; i++)
+ {
+ FileClose(&drive_infos[i].file);
+ }
+ FileClose(&xex_file);
+ speed_index = 0;
+ uart_init(speeds[speed_index] + 6);
+ reboot(1, 0);
+}
diff --git a/support/atari8bit/atari800.h b/support/atari8bit/atari800.h
new file mode 100644
index 0000000..d65c7a3
--- /dev/null
+++ b/support/atari8bit/atari800.h
@@ -0,0 +1,15 @@
+#ifndef __ATARI800_H__
+#define __ATARI800_H__
+
+int atari800_get_match_cart_count();
+const char *atari800_get_cart_match_name(int match_index);
+void atari800_init();
+void atari800_reset();
+void atari800_poll();
+void atari800_umount_cartridge(uint8_t stacked);
+int atari800_check_cartridge_file(const char* name, unsigned char index);
+void atari800_open_cartridge_file(const char* name, int match_index);
+void atari800_open_bios_file(const char* name, unsigned char index);
+void atari800_set_image(int ext_index, int file_index, const char *name);
+
+#endif
diff --git a/support/atari8bit/atari8bit_defs.h b/support/atari8bit/atari8bit_defs.h
new file mode 100644
index 0000000..512919d
--- /dev/null
+++ b/support/atari8bit/atari8bit_defs.h
@@ -0,0 +1,43 @@
+#define STATUS1_MASK_SOFTBOOT 0x0001
+#define STATUS1_MASK_COLDBOOT 0x0002
+#define STATUS1_MASK_HALT 0x0004
+#define STATUS1_MASK_MODE800 0x0008
+#define STATUS1_MASK_BOOTX 0x0010
+#define STATUS1_MASK_RDONLY 0x0040
+#define STATUS1_MASK_MODEPBI 0x0080
+#define STATUS1_MASK_ATX1050 0x8000
+
+#define STATUS2_MASK_SPLASH 0x0800
+
+#define BUFFER_SIZE 8192
+
+#if BUFFER_SIZE < 8192
+#error BUFFER_SIZE is too small!
+#endif
+
+#define A800_SIO_TX_STAT 0x03
+#define A800_SIO_RX 0x04
+#define A800_SIO_RX_STAT 0x05
+#define A800_SIO_GETDIV 0x06
+#define A800_SIO_ERROR 0x07
+
+#define A800_GET_REGISTER 0x08
+#define A800_SET_REGISTER 0x09
+
+#define REG_CART1_SELECT 0x01
+#define REG_CART2_SELECT 0x02
+#define REG_RESET 0x03
+#define REG_PAUSE 0x04
+#define REG_FREEZER 0x05
+#define REG_RESET_RNMI 0x06
+#define REG_OPTION_FORCE 0x07
+#define REG_DRIVE_LED 0x08
+#define REG_XEX_LOADER 0x09
+#define REG_SIO_TX 0x0A
+#define REG_SIO_SETDIV 0x0B
+
+#define REG_ATARI_STATUS1 0x01
+#define REG_ATARI_STATUS2 0x02
+
+#define SDRAM_BASE 0x2000000
+#define ATARI_BASE 0x0010000
diff --git a/support/atari8bit/boot_xex_loader.h b/support/atari8bit/boot_xex_loader.h
new file mode 100644
index 0000000..34c2548
--- /dev/null
+++ b/support/atari8bit/boot_xex_loader.h
@@ -0,0 +1,18 @@
+// This loader is most probably originally from SDrive(Max). Should probably be reviewed,
+// but then the best way to load XEX files on MiSTer is using the DMA loader anyways...
+
+const static uint8_t boot_xex_loader[384] =
+{
+ 0x72,0x02,0x5f,0x07,0xf8,0x07,0xa9,0x00,0x8d,0x04,0x03,0x8d,0x44,0x02,0xa9,0x07,
+ 0x8d,0x05,0x03,0xa9,0x70,0x8d,0x0a,0x03,0xa9,0x01,0x8d,0x0b,0x03,0x85,0x09,0x60,
+ 0x7d,0x8a,0x48,0x20,0x53,0xe4,0x88,0xd0,0xfa,0x68,0xaa,0x8c,0x8e,0x07,0xad,0x7d,
+ 0x07,0xee,0x8e,0x07,0x60,0xa9,0x93,0x8d,0xe2,0x02,0xa9,0x07,0x8d,0xe3,0x02,0xa2,
+ 0x02,0x20,0xda,0x07,0x95,0x43,0x20,0xda,0x07,0x95,0x44,0x35,0x43,0xc9,0xff,0xf0,
+ 0xf0,0xca,0xca,0x10,0xec,0x30,0x06,0xe6,0x45,0xd0,0x02,0xe6,0x46,0x20,0xda,0x07,
+ 0xa2,0x01,0x81,0x44,0xb5,0x45,0xd5,0x43,0xd0,0xed,0xca,0x10,0xf7,0x20,0xd2,0x07,
+ 0x4c,0x94,0x07,0xa9,0x03,0x8d,0x0f,0xd2,0x6c,0xe2,0x02,0xad,0x8e,0x07,0xcd,0x7f,
+ 0x07,0xd0,0xab,0xee,0x0a,0x03,0xd0,0x03,0xee,0x0b,0x03,0xad,0x7d,0x07,0x0d,0x7e,
+ 0x07,0xd0,0x8e,0x20,0xd2,0x07,0x6c,0xe0,0x02,0x20,0xda,0x07,0x8d,0xe0,0x02,0x20,
+ 0xda,0x07,0x8d,0xe1,0x02,0x2d,0xe0,0x02,0xc9,0xff,0xf0,0xed,0xa9,0x00,0x8d,0x8e,
+ 0x07,0xf0,0x82
+};
diff --git a/support/atari8bit/xex_loader.h b/support/atari8bit/xex_loader.h
new file mode 100644
index 0000000..8a2f9de
--- /dev/null
+++ b/support/atari8bit/xex_loader.h
@@ -0,0 +1,10 @@
+const static uint8_t xex_loader[] =
+{
+ 0x61,0xA2,0x00,0x86,0x09,0xCA,0x9A,0xCE,0x00,0xD1,0xCE,0x0E,0xD1,0xA9,0x01,0xF0,
+ 0xFC,0x30,0x09,0xA9,0xD1,0x48,0xA9,0x09,0x48,0x6C,0xE2,0x02,0xCE,0x00,0xD1,0x6C,
+ 0xE0,0x02
+};
+
+#define XEX_READ_STATUS 0x0e
+#define XEX_INIT1 0x01
+
diff --git a/user_io.cpp b/user_io.cpp
index 3ab151f..0934b19 100644
--- a/user_io.cpp
+++ b/user_io.cpp
@@ -317,6 +317,20 @@ char is_c128()
return (is_c128_type == 1);
}
+static int is_atari800_type = 0;
+char is_atari800()
+{
+ if (!is_atari800_type) is_atari800_type = strcasecmp(orig_name, "Atari800") ? 2 : 1;
+ return (is_atari800_type == 1);
+}
+
+static int is_atari5200_type = 0;
+char is_atari5200()
+{
+ if (!is_atari5200_type) is_atari5200_type = strcasecmp(orig_name, "Atari5200") ? 2 : 1;
+ return (is_atari5200_type == 1);
+}
+
static int is_psx_type = 0;
char is_psx()
{
@@ -398,6 +412,8 @@ void user_io_read_core_name()
is_gba_type = 0;
is_c64_type = 0;
is_c128_type = 0;
+ is_atari800_type = 0;
+ is_atari5200_type = 0;
is_psx_type = 0;
is_cdi_type = 0;
is_st_type = 0;
@@ -1523,6 +1539,14 @@ void user_io_init(const char *path, const char *xml)
printf("Identified Archimedes core");
archie_init();
}
+ else if (is_atari800())
+ {
+ atari800_init();
+ }
+ else if (is_atari5200())
+ {
+ atari5200_init();
+ }
else
{
const char *home = HomeDir();
@@ -3667,6 +3691,8 @@ void user_io_poll()
uint16_t save_req = spi_uio_cmd(UIO_CHK_UPLOAD);
if (save_req) c64_save_cart(save_req >> 8);
}
+ if (is_atari800()) atari800_poll();
+ if (is_atari5200()) atari5200_poll();
process_ss(0);
}
diff --git a/user_io.h b/user_io.h
index 5c31026..5b60aaf 100644
--- a/user_io.h
+++ b/user_io.h
@@ -289,6 +289,8 @@ char is_saturn();
char is_pcxt();
char is_n64();
char is_uneon();
+char is_atari800();
+char is_atari5200();
#define HomeDir(x) user_io_get_core_path(x)
#define CoreName user_io_get_core_name()