diff --git a/XTMax/Code/XTMax/XTMax.ino b/XTMax/Code/XTMax/XTMax.ino index d80ac98..600479d 100644 --- a/XTMax/Code/XTMax/XTMax.ino +++ b/XTMax/Code/XTMax/XTMax.ino @@ -39,12 +39,13 @@ // - Refactor SD card I/O // - Add support for 16-bit EMS page offsets // -// Revision 8 01/18/2024 -// - Add support for BIOS ROM extension (Boot ROM) -// // Revision 8 01/20/2025 // - Added chip select for a second PSRAM to allow access to 16 MB of Expanded RAM // +// Revision 9 01/26/2024 +// - Add support for BIOS ROM extension (Boot ROM) +// - Add scrach registers for Boot ROM hooking +// //------------------------------------------------------------------------ // // Copyright (c) 2024 Ted Fried @@ -171,7 +172,7 @@ #define EMS_TOTAL_SIZE (16*1024*1024) -#define SD_BASE 0x280 // Must be a multiple of 2. +#define SD_BASE 0x280 // Must be a multiple of 8. // -------------------------------------------------------------------------------------------------- @@ -196,6 +197,7 @@ uint8_t spi_shift_out =0; uint8_t sd_spi_datain =0; uint32_t sd_spi_cs_n = 0x0; uint32_t sd_spi_dataout =0; +uint8_t sd_scratch_register[4] = {0, 0, 0, 0}; uint8_t XTMax_MEM_Response_Array[16]; @@ -674,12 +676,16 @@ inline void IO_Read_Cycle() { } - else if ((isa_address&0x0FFE)==SD_BASE ) { // Location of SD Card registers + else if ((isa_address&0x0FF8)==SD_BASE ) { // Location of SD Card registers - // Both registers serve the same function (to allow use of Word I/O) - sd_spi_dataout = 0xff; - SD_SPI_Cycle(); - isa_data_out = sd_spi_datain; + switch (isa_address) { + case SD_BASE: // First two registers serve the same function (to allow use of Word I/O) + case SD_BASE+1: sd_spi_dataout = 0xff; SD_SPI_Cycle(); isa_data_out = sd_spi_datain; break; + case SD_BASE+4: isa_data_out = sd_scratch_register[0]; break; + case SD_BASE+5: isa_data_out = sd_scratch_register[1]; break; + case SD_BASE+6: isa_data_out = sd_scratch_register[2]; break; + case SD_BASE+7: isa_data_out = sd_scratch_register[3]; break; + } GPIO7_DR = GPIO7_DATA_OUT_UNSCRAMBLE + MUX_ADDR_n_LOW + CHRDY_OUT_LOW + trigger_out; GPIO8_DR = sd_pin_outputs + MUX_DATA_n_HIGH + CHRDY_OE_n_HIGH + DATA_OE_n_LOW; @@ -727,7 +733,7 @@ inline void IO_Write_Cycle() { } - else if ((isa_address&0x0FFC)==SD_BASE ) { // Location of SD Card registers + else if ((isa_address&0x0FF8)==SD_BASE ) { // Location of SD Card registers GPIO7_DR = GPIO7_DATA_OUT_UNSCRAMBLE + MUX_ADDR_n_HIGH + CHRDY_OUT_LOW + trigger_out; GPIO8_DR = sd_pin_outputs + MUX_DATA_n_LOW + CHRDY_OE_n_HIGH + DATA_OE_n_HIGH; @@ -739,6 +745,10 @@ inline void IO_Write_Cycle() { case SD_BASE: // First two registers serve the same function (to allow use of Word I/O) case SD_BASE+1: sd_spi_dataout = data_in; SD_SPI_Cycle(); break; case SD_BASE+2: sd_spi_cs_n = data_in&0x1; break; + case SD_BASE+4: sd_scratch_register[0] = data_in; break; + case SD_BASE+5: sd_scratch_register[1] = data_in; break; + case SD_BASE+6: sd_scratch_register[2] = data_in; break; + case SD_BASE+7: sd_scratch_register[3] = data_in; break; } //gpio9_int = GPIO9_DR; diff --git a/XTMax/Code/XTMax/bootrom.h b/XTMax/Code/XTMax/bootrom.h index 088eb8c..473c04a 100644 --- a/XTMax/Code/XTMax/bootrom.h +++ b/XTMax/Code/XTMax/bootrom.h @@ -1,9 +1,97 @@ #define BOOTROM_ADDR 0xCE000 unsigned char BOOTROM[] = { - 85, 170, 4, 184, 0, 192, 142, 216, 142, 192, 190, 33, 224, 232, 1, 0, - 203, 83, 49, 219, 172, 8, 192, 116, 6, 180, 14, 205, 16, 235, 245, 91, - 195, 66, 111, 111, 116, 82, 79, 77, 32, 102, 111, 114, 32, 88, 84, 77, - 97, 120, 32, 118, 48, 46, 49, 13, 10, 0, 0, 0, 0, 0, 0, 0, + 85, 170, 4, 6, 80, 82, 250, 184, 200, 228, 232, 54, 4, 232, 57, 3, + 115, 3, 233, 130, 0, 184, 10, 229, 232, 40, 4, 49, 192, 142, 192, 38, + 161, 78, 0, 186, 132, 2, 239, 232, 55, 4, 184, 181, 229, 232, 19, 4, + 38, 161, 76, 0, 186, 134, 2, 239, 232, 38, 4, 184, 185, 229, 232, 2, + 4, 184, 31, 229, 232, 252, 3, 184, 0, 192, 38, 163, 78, 0, 232, 16, + 4, 184, 181, 229, 232, 236, 3, 184, 156, 224, 38, 163, 76, 0, 232, 0, + 4, 184, 185, 229, 232, 220, 3, 184, 52, 229, 232, 214, 3, 184, 0, 192, + 38, 163, 6, 1, 232, 234, 3, 184, 181, 229, 232, 198, 3, 184, 81, 225, + 38, 163, 4, 1, 232, 218, 3, 184, 185, 229, 232, 182, 3, 184, 64, 0, + 142, 192, 38, 254, 6, 117, 0, 251, 90, 88, 7, 203, 85, 86, 128, 250, + 128, 116, 28, 137, 197, 137, 214, 156, 14, 184, 189, 224, 80, 156, 186, 132, + 2, 237, 80, 186, 134, 2, 237, 80, 137, 232, 137, 242, 207, 235, 56, 128, + 252, 21, 126, 5, 232, 112, 0, 235, 34, 128, 252, 1, 116, 10, 128, 252, + 21, 116, 5, 190, 235, 224, 235, 3, 190, 247, 224, 86, 83, 136, 227, 48, + 255, 208, 227, 137, 222, 91, 46, 255, 164, 11, 225, 6, 190, 64, 0, 142, + 198, 38, 136, 38, 116, 0, 7, 137, 229, 139, 118, 8, 156, 131, 230, 254, + 157, 131, 214, 0, 86, 157, 94, 93, 202, 2, 0, 221, 226, 97, 225, 117, + 225, 248, 225, 156, 226, 55, 225, 55, 225, 55, 225, 183, 226, 221, 226, 55, + 225, 55, 225, 207, 226, 221, 226, 55, 225, 55, 225, 221, 226, 221, 226, 55, + 225, 55, 225, 221, 226, 223, 226, 80, 184, 152, 229, 232, 5, 3, 88, 80, + 136, 224, 48, 228, 232, 26, 3, 184, 185, 229, 232, 246, 2, 88, 233, 155, + 1, 0, 4, 16, 0, 0, 255, 255, 0, 200, 0, 0, 0, 0, 0, 63, + 0, 6, 189, 64, 0, 142, 197, 48, 228, 38, 134, 38, 116, 0, 132, 228, + 116, 1, 249, 7, 195, 132, 192, 117, 3, 233, 112, 1, 80, 48, 228, 137, + 198, 83, 232, 115, 1, 137, 197, 1, 240, 137, 222, 131, 211, 0, 232, 154, + 1, 91, 88, 115, 3, 233, 88, 1, 83, 81, 82, 87, 80, 137, 193, 48, + 237, 137, 223, 186, 130, 2, 176, 0, 238, 81, 137, 232, 137, 243, 177, 81, + 232, 90, 2, 114, 50, 186, 128, 2, 185, 232, 3, 235, 3, 232, 117, 2, + 236, 60, 254, 224, 248, 117, 32, 185, 0, 1, 252, 237, 171, 226, 252, 237, + 131, 197, 1, 131, 214, 0, 89, 226, 208, 186, 130, 2, 176, 1, 238, 88, + 95, 90, 89, 91, 233, 13, 1, 186, 130, 2, 176, 1, 238, 89, 88, 40, + 200, 95, 90, 89, 91, 233, 240, 0, 132, 192, 117, 3, 233, 237, 0, 80, + 48, 228, 137, 198, 83, 232, 240, 0, 137, 197, 1, 240, 137, 222, 131, 211, + 0, 232, 23, 1, 91, 88, 115, 3, 233, 213, 0, 30, 83, 81, 82, 87, + 80, 137, 193, 48, 237, 137, 223, 140, 192, 142, 216, 186, 130, 2, 176, 0, + 238, 81, 137, 232, 137, 243, 177, 88, 232, 210, 1, 114, 78, 186, 128, 2, + 176, 254, 238, 185, 0, 1, 135, 247, 252, 173, 239, 226, 252, 135, 254, 186, + 128, 2, 185, 196, 9, 235, 3, 232, 219, 1, 236, 60, 255, 225, 248, 36, + 31, 60, 5, 117, 38, 185, 196, 9, 235, 3, 232, 200, 1, 236, 132, 192, + 225, 248, 116, 23, 131, 197, 1, 131, 214, 0, 89, 226, 180, 186, 130, 2, + 176, 1, 238, 88, 95, 90, 89, 91, 31, 235, 105, 186, 130, 2, 176, 1, + 238, 89, 88, 40, 200, 95, 90, 89, 91, 31, 235, 76, 132, 192, 116, 76, + 80, 48, 228, 137, 197, 83, 232, 79, 0, 1, 232, 131, 211, 0, 232, 122, + 0, 91, 88, 114, 59, 235, 61, 182, 15, 6, 184, 64, 0, 142, 192, 38, + 138, 22, 117, 0, 7, 254, 194, 181, 254, 177, 255, 49, 192, 248, 195, 80, + 83, 232, 36, 0, 232, 84, 0, 91, 88, 114, 21, 235, 23, 235, 21, 180, + 3, 232, 94, 0, 135, 209, 248, 195, 180, 170, 249, 195, 180, 1, 249, 195, + 180, 4, 249, 195, 48, 228, 248, 195, 49, 192, 49, 219, 82, 81, 82, 136, + 200, 36, 192, 209, 224, 209, 224, 136, 232, 185, 16, 0, 247, 225, 90, 136, + 241, 48, 237, 1, 200, 177, 63, 247, 225, 89, 81, 48, 237, 128, 225, 63, + 73, 1, 200, 131, 210, 0, 137, 211, 89, 90, 195, 81, 82, 232, 18, 0, + 57, 211, 114, 10, 119, 4, 57, 200, 114, 4, 249, 90, 89, 195, 248, 90, + 89, 195, 186, 15, 0, 185, 16, 188, 195, 80, 30, 83, 81, 82, 86, 184, + 0, 192, 142, 216, 186, 130, 2, 176, 1, 238, 49, 201, 186, 232, 3, 180, + 134, 205, 21, 186, 128, 2, 176, 255, 185, 80, 0, 238, 226, 253, 186, 130, + 2, 176, 0, 238, 190, 245, 227, 185, 1, 0, 180, 1, 232, 79, 0, 114, + 54, 190, 251, 227, 185, 5, 0, 180, 1, 232, 66, 0, 114, 41, 187, 100, + 0, 190, 1, 228, 185, 1, 0, 180, 1, 232, 50, 0, 190, 7, 228, 185, + 1, 0, 180, 0, 232, 39, 0, 115, 14, 156, 49, 201, 186, 232, 3, 180, + 134, 205, 21, 157, 75, 117, 218, 94, 90, 89, 91, 31, 114, 8, 184, 86, + 229, 232, 127, 0, 88, 195, 184, 121, 229, 232, 119, 0, 88, 195, 186, 128, + 2, 176, 255, 238, 81, 185, 6, 0, 252, 172, 238, 226, 252, 185, 8, 0, + 236, 60, 255, 225, 251, 89, 56, 224, 118, 3, 249, 235, 4, 248, 236, 226, + 253, 176, 255, 238, 195, 64, 0, 0, 0, 0, 149, 72, 0, 0, 1, 170, + 135, 119, 0, 0, 0, 0, 1, 105, 64, 0, 0, 0, 1, 186, 128, 2, + 80, 176, 255, 238, 136, 200, 238, 136, 248, 238, 136, 216, 238, 88, 134, 224, + 238, 134, 224, 238, 176, 1, 238, 185, 8, 0, 236, 60, 255, 225, 251, 132, + 192, 116, 1, 249, 195, 81, 82, 49, 201, 186, 100, 0, 180, 134, 205, 21, + 90, 89, 195, 156, 30, 83, 86, 137, 198, 184, 0, 192, 142, 216, 180, 14, + 49, 219, 252, 172, 8, 192, 116, 4, 205, 16, 235, 247, 94, 91, 31, 157, + 195, 156, 30, 83, 81, 82, 86, 137, 194, 184, 0, 192, 142, 216, 49, 219, + 252, 137, 214, 177, 12, 211, 238, 131, 230, 15, 138, 132, 184, 228, 180, 14, + 205, 16, 137, 214, 177, 8, 211, 238, 131, 230, 15, 138, 132, 184, 228, 180, + 14, 205, 16, 137, 214, 177, 4, 211, 238, 131, 230, 15, 138, 132, 184, 228, + 180, 14, 205, 16, 137, 214, 131, 230, 15, 138, 132, 184, 228, 180, 14, 205, + 16, 94, 90, 89, 91, 31, 157, 195, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 65, 66, 67, 68, 69, 70, 66, 111, 111, 116, 82, 79, 77, 32, + 102, 111, 114, 32, 88, 84, 77, 97, 120, 32, 118, 49, 46, 48, 13, 10, + 67, 111, 112, 121, 114, 105, 103, 104, 116, 32, 40, 99, 41, 32, 50, 48, + 50, 53, 32, 77, 97, 116, 116, 104, 105, 101, 117, 32, 66, 117, 99, 99, + 104, 105, 97, 110, 101, 114, 105, 13, 10, 0, 79, 108, 100, 32, 73, 78, + 84, 49, 51, 104, 32, 86, 101, 99, 116, 111, 114, 32, 61, 32, 0, 78, + 101, 119, 32, 73, 78, 84, 49, 51, 104, 32, 86, 101, 99, 116, 111, 114, + 32, 61, 32, 0, 78, 101, 119, 32, 70, 105, 120, 101, 100, 32, 68, 105, + 115, 107, 32, 80, 97, 114, 97, 109, 101, 116, 101, 114, 32, 84, 97, 98, + 108, 101, 32, 61, 32, 0, 83, 68, 32, 67, 97, 114, 100, 32, 105, 110, + 105, 116, 105, 97, 108, 105, 122, 101, 100, 32, 115, 117, 99, 99, 101, 115, + 115, 102, 117, 108, 108, 121, 13, 10, 0, 83, 68, 32, 67, 97, 114, 100, + 32, 102, 97, 105, 108, 101, 100, 32, 116, 111, 32, 105, 110, 105, 116, 105, + 97, 108, 105, 122, 101, 13, 10, 0, 85, 110, 115, 117, 112, 112, 111, 114, + 116, 101, 100, 32, 73, 78, 84, 49, 51, 104, 32, 70, 117, 110, 99, 116, + 105, 111, 110, 32, 0, 58, 0, 32, 0, 13, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -39,92 +127,4 @@ unsigned char BOOTROM[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93}; \ No newline at end of file + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216}; \ No newline at end of file diff --git a/XTMax/Drivers/BootROM/.gitignore b/XTMax/Drivers/BootROM/.gitignore index 28cb051..4ff3110 100644 --- a/XTMax/Drivers/BootROM/.gitignore +++ b/XTMax/Drivers/BootROM/.gitignore @@ -1,2 +1,3 @@ bootrom bootrom.com +*.exe diff --git a/XTMax/Drivers/BootROM/bootrom.asm b/XTMax/Drivers/BootROM/bootrom.asm index be59c27..695bd37 100644 --- a/XTMax/Drivers/BootROM/bootrom.asm +++ b/XTMax/Drivers/BootROM/bootrom.asm @@ -1,13 +1,68 @@ -bits 16 +; +; BIOS Extension ROM for XTMax SD Card support. +; Copyright (c) 2025 Matthieu Bucchianeri, All rights reserved. +; -%ifndef AS_COM +bits 16 +cpu 8086 ; ensure we remain compatible with 8086 + + +;%define DEBUG +;%define DEBUG_IO +;%define EXTRA_DEBUG + + +%define FIXED_DISK_0 (0) +%define FIXED_DISK_1 (1) +%define FLOPPY_DISK (-0x80) + +; +; Whether we will emulate the 1st or 2nd disk. +; +%define DISK_NUMBER FIXED_DISK_0 + +%define EMULATE_FIXED_DISK (DISK_NUMBER >= FIXED_DISK_0) + +; +; Whether we will use our own bootstrap code. +; +;%define USE_BOOTSTRAP + +; +; The properties of our emulated disk. +; +%if EMULATE_FIXED_DISK +%define NUM_CYLINDERS (1024) +%define NUM_HEADS (16) +%define SECTORS_PER_TRACK (63) +; the last cylinder is reserved on fixed disks +%define NUM_SECTORS (NUM_HEADS * (NUM_CYLINDERS - 1) * SECTORS_PER_TRACK) +%else +%define NUM_HEADS (2) +%define SECTORS_PER_TRACK (18) +%define NUM_CYLINDERS (80) +%define NUM_SECTORS (NUM_HEADS * NUM_CYLINDERS * SECTORS_PER_TRACK) +%endif + +%if NUM_HEADS > 16 +%error NUM_HEADS must be 4 bits +%endif +%if EMULATE_FIXED_DISK && SECTORS_PER_TRACK > 63 +%error SECTORS_PER_TRACK must be 6 bits +%endif +%if NUM_CYLINDERS > 1024 +%error NUM_CYLINDERS must be 10 bits +%endif + +%ifndef AS_COM_PROGRAM ; ; BIOS will look for the AA55 signature between C8000-DFFFF in 2KB increments. ; We choose an address in that range. ; -org 0xce000 -dw 0AA55h -db 4 ; times 512 bytes +%define ROM_SEGMENT (0xc000) +org (ROM_SEGMENT << 4 | 0xe000) +dw 0AA55h ; signature +db 4 ; size in blocks of 512 bytes %else ; @@ -18,55 +73,1178 @@ org 0x100 %endif entry: -%ifndef AS_COM - mov ax, 0xc000 - mov ds, ax - mov es, ax +%ifndef AS_COM_PROGRAM + push es + push ax + push dx + cli %endif - mov si, welcome + mov ax, welcome_msg call print_string + call init_sd -%ifndef AS_COM - retf +%ifndef AS_COM_PROGRAM + jc .skip +; +; Install our BIOS INT13h hook into the interrupt vector table. +; +.install_13h_vector: + mov ax, old_13h_msg + call print_string + + xor ax, ax ; INT vector segment + mov es, ax + + mov ax, es:[0x13*4+2] + mov dx, 0x284 ; scratch register 0 + out dx, ax ; save segment + call print_hex + mov ax, colon + call print_string + mov ax, es:[0x13*4] + mov dx, 0x286 ; scratch register 2 + out dx, ax ; save offset + call print_hex + mov ax, newline + call print_string + + mov ax, new_13h_msg + call print_string + + mov ax, ROM_SEGMENT + mov es:[0x13*4+2], ax ; store segment + call print_hex + mov ax, colon + call print_string + mov ax, int13h_entry + mov es:[0x13*4], ax ; store offset + call print_hex + mov ax, newline + call print_string + +%if EMULATE_FIXED_DISK +; +; Install our fixed disk parameter table. +; For the 1st disk, it is stored in the interrupt vector table, at vector 41h. +; For the 2nd disk, it is stored in the interrupt vector table, at vector 46h. +; +.install_fixed_disk_parameters_table: + mov ax, new_fdpt_msg + call print_string + + mov ax, ROM_SEGMENT + mov es:[(0x41+DISK_NUMBER*5)*4+2], ax ; store segment + call print_hex + mov ax, colon + call print_string + mov ax, fixed_disk_parameters_table + mov es:[(0x41+DISK_NUMBER*5)*4], ax ; store offset + call print_hex + mov ax, newline + call print_string + +%ifdef USE_BOOTSTRAP +; +; Install our BIOS INT18h hook into the interrupt vector table. +; +.install_18h_vector: + mov ax, new_18h_msg + call print_string + + mov ax, ROM_SEGMENT + mov es:[0x18*4+2], ax ; store segment + call print_hex + mov ax, colon + call print_string + mov ax, int18h_entry + mov es:[0x18*4], ax ; store offset + call print_hex + mov ax, newline + call print_string %else + +; +; Increment the number of fixed disks in the BIOS Data Area. +; + mov ax, 0x40 ; BIOS data area + mov es, ax + inc byte es:[0x75] ; HDNUM +%endif +%endif + +.skip: + sti + pop dx + pop ax + pop es + retf + +%else + +; +; Execute COM program testing. +; +%include "tests.inc" + ; ; DOS exit program. ; mov ah, 0x4c xor al, al int 0x21 + %endif ; -; Utilities +; The handler will preserve BP and SI across calls and therefore they can be used as temp. +; +%define TEMP0 bp +%define TEMP1 si +%define TEMP_LO TEMP0 +%define TEMP_HI TEMP1 + +; +; INT 13h entry point. +; +int13h_entry: +%ifdef EXTRA_DEBUG + call dump_regs +%endif + push TEMP0 + push TEMP1 + cmp dl, 0x80+DISK_NUMBER ; is this our drive? + je .check_function + +; +; This is not an operation for the SD Card. Forward to the BIOS INT 13h handler. +; +.forward_to_bios: +%ifdef EXTRA_DEBUG + push ax + mov ax, forward_msg + call print_string + pop ax + push ax + mov al, ah + xor ah, ah + call print_hex + mov ax, newline + call print_string + pop ax +%endif + mov TEMP0, ax ; save ax + mov TEMP1, dx ; save dx + pushf ; setup for iret from INT 13h handler + push cs ; setup for iret from INT 13h handler + mov ax, .return_from_int13h + push ax ; setup for iret from INT 13h handler +.simulate_int13h: +; +; Simulate INT 13h with the original vector. +; + pushf ; setup for iret below + mov dx, 0x284 ; scratch register 0 +%ifndef AS_COM_PROGRAM + in ax, dx +%else + mov ax, cs +%endif + push ax ; setup for iret below + mov dx, 0x286 ; scratch register 2 +%ifndef AS_COM_PROGRAM + in ax, dx +%else + mov ax, fake_int13h_entry +%endif + push ax ; setup for iret below + mov ax, TEMP0 ; restore ax + mov dx, TEMP1 ; restore dx + iret ; call the INT 13h handler +.return_from_int13h: + jmp .return_common + +; +; This is an operation for the SD Card. Use our own INT 13h logic. +; +.check_function: +%if EMULATE_FIXED_DISK + cmp ah, 0x15 ; is valid function? +%else + cmp ah, 0x16 ; is valid function? +%endif + jle .prepare_call + call func_unsupported + jmp .update_bda +.prepare_call: +.check_function_01_and_15: + cmp ah, 0x1 ; is read status? + je .call_no_update_bda + cmp ah, 0x15 ; is read size? + je .call_no_update_bda + mov TEMP1, .update_bda + jmp .call +.call_no_update_bda: + mov TEMP1, .return +.call: + push TEMP1 ; setup for ret from the handler + push bx + mov bl, ah + xor bh, bh + shl bl, 1 + mov TEMP1, bx + pop bx + jmp [cs:TEMP1+func_table] +.update_bda: + push es + mov TEMP1, 0x40 ; BIOS data area + mov es, TEMP1 + mov es:[0x74], ah ; store HDSTAT + pop es +.return: +%ifdef DEBUG + push ax + mov ax, status_msg + call print_string + pop ax + push ax + xchg ah, al + pushf + xor ah, ah + popf + call print_hex + mov ax, newline + call print_string + pop ax +%endif + +; +; Propagate the Carry Flag to the caller. +; MS-DOS hooks INT13h and will not perform a proper INT call, but instead a far call. +; Therefore, we handle the flags like we would for a far call. +; +.return_common: + mov TEMP0, sp + mov TEMP1, [TEMP0+8] ; grab the flags for iret + pushf + and TEMP1, 0xfffe ; clear carry + popf + adc TEMP1, 0 ; propagate carry + push TEMP1 + popf + pop TEMP1 + pop TEMP0 +%ifdef EXTRA_DEBUG + call dump_regs +%endif + retf 2 ; return and discard flags on the stack + +; +; INT 13h function table +; +func_table: + dw func_10_is_ready ; reset + dw func_01_read_status + dw func_02_read_sector + dw func_03_write_sector + dw func_04_verify_sector + dw func_unsupported ; format_cyl + dw func_unsupported ; format_bad_trk + dw func_unsupported ; format_drv + dw func_08_read_params + dw func_10_is_ready ; init_params + dw func_unsupported ; read_sector_long + dw func_unsupported ; write_sector_long + dw func_0c_seek_cyl + dw func_10_is_ready ; reset_alt + dw func_unsupported ; read_sector_buf + dw func_unsupported ; write_sector_buf + dw func_10_is_ready + dw func_10_is_ready ; recalibrate + dw func_unsupported ; diagnostics + dw func_unsupported ; diagnostics + dw func_10_is_ready ; diagnostics + dw func_15_read_size +%if !EMULATE_FIXED_DISK + dw func_10_is_ready ; detect_change +%endif + +func_unsupported: +%ifdef DEBUG + call debug_handler +%endif + push ax + mov ax, unsupported_msg + call print_string + pop ax + push ax + mov al, ah + xor ah, ah + call print_hex + mov ax, newline + call print_string + pop ax + jmp error_invalid_parameter + +; +; Fixed Disk Parameters Table +; +fixed_disk_parameters_table: + dw NUM_CYLINDERS ; cylinders + db NUM_HEADS ; heads + db 0, 0 ; reserved + dw 0xffff ; starting cylinder for write compensation + db 0 ; reserved + db 0xc0 | ((NUM_HEADS > 8) * 0x8) ; control byte (disable retries, >8 heads) + db 0, 0, 0 ; reserved + dw 0 ; landing zone + db SECTORS_PER_TRACK ; sectors per track + db 0 ; reserved + +; +; Function 01h: Read Disk Status +; in: AH = 01h +; DL = drive number (80h or 81h) +; out: AH = status +; CF = 0 (success), 1 (error) +; +func_01_read_status: +%ifdef DEBUG + call debug_handler +%endif + push es + mov TEMP0, 0x40 ; BIOS data area + mov es, TEMP0 + xor ah, ah + xchg ah, es:[0x74] ; load then clear HDSTAT + test ah, ah + jz .end + stc +.end: + pop es + ret + +; +; Function 02h: Read Disk Sectors +; in: AH = 02h +; AL = number of sectors to read +; CH = cylinder number (low 8 bits) +; CL = bits 7-6: cylinder number (high 2 bits) +; bits 5-0: sector number +; DH = head number +; DL = drive number (80h or 81h) +; ES:BX = destination buffer +; out: AH = status +; CF = 0 (success), 1 (error) +; +func_02_read_sector: +%ifdef DEBUG + call debug_handler +%endif + test al, al + jz error_invalid_parameter + push ax + xor ah, ah + mov TEMP_HI, ax + push bx + call compute_lba + mov TEMP_LO, ax ; save lba + add ax, TEMP_HI ; check the upper boundary + mov TEMP_HI, bx ; save lba + adc bx, 0 ; carry + call is_lba_valid + pop bx + pop ax + jc error_sector_not_found + ; TODO: (robustness) check buffer boundaries +.setup: + push bx + push cx + push dx + push di + push ax + mov cx, ax ; number of sectors to read + xor ch, ch + mov di, bx ; setup use of stosw +.assert_cs: + mov dx, 0x282 ; chip select port + mov al, 0 ; assert chip select + out dx, al +.cmd17: + push cx + ; TODO: (opt) use CMD18 for multiple blocks +%ifdef DEBUG_IO + mov ax, send_cmd17_msg + call print_string +%endif + mov ax, TEMP_LO ; restore lba + mov bx, TEMP_HI ; restore lba + mov cl, 0x51 ; CMD17 + call send_sd_read_write_cmd + jc .error +%ifdef DEBUG_IO + mov ax, wait_msg + call print_string +%endif + mov dx, 0x280 ; data port + mov cx, 1000 ; timeout + jmp .receive_token_no_delay +.receive_token: + call delay_100us +.receive_token_no_delay: + in al, dx + cmp al, 0xfe + loopne .receive_token + jne .error +%ifdef DEBUG_IO + mov ax, sd_token_msg + call print_string +%endif + mov cx, 256 ; block size (in words) + cld +.receive_block: + in ax, dx + stosw + loop .receive_block + in ax, dx ; discard CRC + add TEMP_LO, 1 ; next block + adc TEMP_HI, 0 ; carry + pop cx ; number of sectors left to read + loop .cmd17 +.success: +.deassert_cs1: + mov dx, 0x282 ; chip select port + mov al, 1 ; deassert chip select + out dx, al +.return1: + pop ax + pop di + pop dx + pop cx + pop bx + jmp succeeded +.error: +.deassert_cs2: + mov dx, 0x282 ; chip select port + mov al, 1 ; deassert chip select + out dx, al +.return2: + pop cx ; number of sectors not read successfully + pop ax + sub al, cl ; number of sectors read successfully + pop di + pop dx + pop cx + pop bx + jmp error_drive_not_ready + +; +; Function 03h: Write Disk Sectors +; in: AH = 03h +; AL = number of sectors to write +; CH = cylinder number (low 8 bits) +; CL = bits 7-6: cylinder number (high 2 bits) +; bits 5-0: sector number +; DH = head number +; DL = drive number (80h or 81h) +; ES:BX = source buffer +; out: AH = status +; CF = 0 (success), 1 (error) +; +func_03_write_sector: +%ifdef DEBUG + call debug_handler +%endif + test al, al + jz error_invalid_parameter + push ax + xor ah, ah + mov TEMP_HI, ax + push bx + call compute_lba + mov TEMP_LO, ax ; save lba + add ax, TEMP_HI ; check the upper boundary + mov TEMP_HI, bx ; save lba + adc bx, 0 ; carry + call is_lba_valid + pop bx + pop ax + jc error_sector_not_found + ; TODO: (robustness) check buffer boundaries +.setup: + push ds + push bx + push cx + push dx + push di + push ax + mov cx, ax ; number of sectors to write + xor ch, ch + mov di, bx ; destination address + mov ax, es + mov ds, ax +.assert_cs: + mov dx, 0x282 ; chip select port + mov al, 0 ; assert chip select + out dx, al +.cmd24: + push cx + ; TODO: (opt) use CMD25 for multiple blocks +%ifdef DEBUG_IO + mov ax, send_cmd24_msg + call print_string +%endif + mov ax, TEMP_LO ; restore lba + mov bx, TEMP_HI ; restore lba + mov cl, 0x58 ; CMD24 + call send_sd_read_write_cmd + jc .error + mov dx, 0x280 ; data port + mov al, 0xfe ; send token + out dx, al + mov cx, 256 ; block size (in words) + xchg di, si ; save si (aka TEMP1) + cld +.send_block: + lodsw + out dx, ax + loop .send_block + xchg si, di ; restore si (aka TEMP1) +%ifdef DEBUG_IO + mov ax, wait_msg + call print_string +%endif + mov dx, 0x280 ; data port + mov cx, 2500 ; timeout + jmp .receive_status_no_delay +.receive_status: + call delay_100us +.receive_status_no_delay: + in al, dx + cmp al, 0xff + loope .receive_status +%ifdef DEBUG_IO + push ax + mov ax, sd_status_msg + call print_string + pop ax + push ax + xor ah, ah + call print_hex + mov ax, newline + call print_string + pop ax +%endif + and al, 0x1F + cmp al, 0x5 + jne .error +%ifdef DEBUG_IO + mov ax, wait_msg + call print_string +%endif + mov cx, 2500 ; timeout + jmp .receive_finish_no_delay +.receive_finish: + call delay_100us +.receive_finish_no_delay: + in al, dx + test al, al + loope .receive_finish + jz .error +%ifdef DEBUG_IO + mov ax, sd_idle_msg + call print_string +%endif + add TEMP_LO, 1 ; next block + adc TEMP_HI, 0 ; carry + pop cx ; number of sectors left to write + loop .cmd24 +.success: +.deassert_cs1: + mov dx, 0x282 ; chip select port + mov al, 1 ; deassert chip select + out dx, al +.return1: + pop ax + pop di + pop dx + pop cx + pop bx + pop ds + jmp succeeded +.error: +.deassert_cs2: + mov dx, 0x282 ; chip select port + mov al, 1 ; deassert chip select + out dx, al +.return2: + pop cx ; number of sectors not written successfully + pop ax + sub al, cl ; number of sectors written successfully + pop di + pop dx + pop cx + pop bx + pop ds + jmp error_drive_not_ready + +; +; Function 04h: Verify Disk Sectors +; in: AH = 04h +; AL = number of sectors to verify +; CH = cylinder number (low 8 bits) +; CL = bits 7-6: cylinder number (high 2 bits) +; bits 5-0: sector number +; DH = head number +; DL = drive number (80h or 81h) +; out: AH = status +; CF = 0 (success), 1 (error) +; +func_04_verify_sector: +%ifdef DEBUG + call debug_handler +%endif + test al, al + jz error_invalid_parameter + push ax + xor ah, ah + mov TEMP0, ax + push bx + call compute_lba + add ax, TEMP0 ; check the upper boundary + adc bx, 0 ; carry + call is_lba_valid + pop bx + pop ax + jc error_sector_not_found + jmp succeeded + +; +; Function 08h: Read Drive Parameters +; in: AH = 08h +; DL = drive number (80h or 81h) +; out: AH = status +; AL = 0 +; CH = maximum usable cylinder number (low 8 bits) +; CL = bits 7-6: maximum usable cylinder number (high 2 bits) +; bits 5-0: maximum usable sector number +; DH = maximum usable head number +; DL = number of drives +; ES:DI = address of disk parameters table (floppies only) +; CF = 0 (success), 1 (error) +; +func_08_read_params: +%ifdef DEBUG + call debug_handler +%endif + mov dh, (NUM_HEADS - 1) + + push es + mov ax, 0x40 ; BIOS data area + mov es, ax + mov dl, es:[0x75] ; HDNUM + pop es +%ifndef USE_BOOTSTRAP + inc dl ; we never added ourselves +%endif + +%if EMULATE_FIXED_DISK + ; the last cylinder is reserved on fixed disks + mov ch, ((NUM_CYLINDERS - 2) & 0xff) + mov cl, (((NUM_CYLINDERS - 2) & 0x300) >> 2) | SECTORS_PER_TRACK + +%else + mov ch, ((NUM_CYLINDERS - 1) & 0xff) + mov cl, (((NUM_CYLINDERS - 1) & 0x300) >> 2) | SECTORS_PER_TRACK + + ; diskette drive parameters table, it is stored in the interrupt vector table, at vector 1Eh. + xor ax, ax ; INT vector segment + mov es, ax + mov ax, es:[0x1e*4] ; offset + mov di, ax + mov ax, es:[0x1e*4+2] ; segment + mov es, ax + mov bx, 0x4 ; 1.44 MB +%endif + + xor ax, ax + clc +.exit: + ret + +; +; Function 0Ch: Seek to Cylinder +; in: AH = 0Ch +; CH = cylinder number (low 8 bits) +; CL = bits 7-6: cylinder number (high 2 bits) +; bits 5-0: sector number +; DH = head number +; DL = drive number (80h or 81h) +; out: AH = status +; CF = 0 (success), 1 (error) +; +func_0c_seek_cyl: +%ifdef DEBUG + call debug_handler +%endif + push ax + push bx + call compute_lba + call is_lba_valid + pop bx + pop ax + jc error_sector_not_found + jmp succeeded + +; +; Function 10h: Test for Drive Ready +; in: AH = 10h +; DL = drive number (80h or 81h) +; out: AH = error code +; CF = 0 (success), 1 (error) +; +func_10_is_ready: +%ifdef DEBUG + call debug_handler +%endif + jmp succeeded + +; +; Function 15h: Read Disk Size +; in: AH = 15h +; DL = drive number (80h or 81h) +; out: AH = 00h (no drive present) +; 03h (drive present) +; CX:DX = number of 512-byte sectors +; CF = 0 (success), 1 (error) +; +func_15_read_size: +%ifdef DEBUG + call debug_handler +%endif +%if EMULATE_FIXED_DISK + mov ah, 0x3 ; drive present + call get_max_lba + xchg cx, dx +%else + mov ah, 0x1 ; diskette drive present +%endif + clc + ret + +; +; Common error handling +; +error_drive_not_ready: + mov ah, 0xaa + stc + ret + +error_invalid_parameter: + mov ah, 0x1 + stc + ret + +error_sector_not_found: + mov ah, 0x4 + stc + ret + +succeeded: + xor ah, ah + clc + ret + +%ifdef USE_BOOTSTRAP +; +; INT 18h entry point. +; +int18h_entry: + xor ax, ax + mov ds, ax + mov es, ax + mov cx, 256 + mov di, 0x7c00 + rep stosw + mov ax, 0x201 ; read 1 sector + mov dx, 0x80+DISK_NUMBER + mov cx, 1 ; sector 1 + mov bx, 0x7c00 + int 0x13 + cmp word [0x7c00+510], 0xaa55 + jne .no_boot + mov ax, boot_msg + call print_string + jmp 0:0x7c00 +.no_boot: + mov ax, no_boot_msg + call print_string + sti +.loop: + hlt + jmp .loop +%endif + +; +; Disk utilities ; -print_string: - push bx - xor bx,bx -.loop: - lodsb - or al, al - jz .done - mov ah, 0x0E - int 0x10 - jmp .loop -.done: - pop bx +; +; Compute LBA address based on CHS address +; in: CH = cylinder number (low 8 bits) +; CL = bits 7-6: cylinder number (high 2 bits) +; bits 5-0: sector number +; DH = head number +; out: BX:AX = LBA address +; FL = +; +compute_lba: + xor ax, ax + xor bx, bx + push dx + push cx + push dx +%if EMULATE_FIXED_DISK + mov al, cl + and al, 0xc0 + shl ax, 1 + shl ax, 1 +%endif + mov al, ch ; cylinder + mov cx, NUM_HEADS + mul cx ; cylinder * hpc + pop dx + mov cl, dh + xor ch, ch + add ax, cx ; cylinder * hpc + head + mov cl, SECTORS_PER_TRACK + mul cx ; (cylinder * hpc + head) * spt + pop cx + push cx + xor ch, ch +%if EMULATE_FIXED_DISK + and cl, 0x3f +%endif + dec cx ; sector - 1 + add ax, cx ; (cylinder * hpc + head) * spt + (sector - 1) + adc dx, 0 ; carry + mov bx, dx + pop cx + pop dx ret +; +; Check if an LBA address is within the drive +; in: BX:AX = LBA address +; out: CF = 0 (success), 1 (error) +; FL = +; +is_lba_valid: + push cx + push dx + call get_max_lba + cmp bx, dx ; check highest word + jb .success + ja .error + cmp ax, cx ; check lowest word + jb .success +.error: + stc + pop dx + pop cx + ret +.success: + clc + pop dx + pop cx + ret + +; +; Retrieve maximum LBA address for the drive +; out: DX:CX = maximum LBA address +; +get_max_lba: + mov dx, (NUM_SECTORS >> 16) + mov cx, (NUM_SECTORS & 0xFFFF) + ret + +; +; I/O utilities +; + +; +; Initialize the SD Card. +; out: CF = 0 (success), 1 (error) +; FL = +; +init_sd: + push ax + push ds + push bx + push cx + push dx + push si +%ifndef AS_COM_PROGRAM + mov ax, ROM_SEGMENT + mov ds, ax +%endif + mov dx, 0x282 ; chip select port + mov al, 1 ; deassert chip select + out dx, al + xor cx, cx + mov dx, 1000 ; microseconds + mov ah, 0x86 ; wait + int 0x15 + mov dx, 0x280 ; data port + mov al, 0xff + mov cx, 80 ; send 80 clock cycles +.synchronize: + out dx, al + loop .synchronize +.assert_cs: + mov dx, 0x282 ; chip select port + mov al, 0 ; assert chip select + out dx, al +.cmd0: +%ifdef DEBUG_IO + mov ax, send_cmd0_msg + call print_string +%endif + mov si, cmd0 + mov cx, 1 ; response is 1 byte + mov ah, 1 ; expect idle state + call send_sd_init_cmd + jc .exit +.cmd8: +%ifdef DEBUG_IO + mov ax, send_cmd8_msg + call print_string +%endif + mov si, cmd8 + mov cx, 5 ; response is 5 bytes + mov ah, 1 ; expect idle state + call send_sd_init_cmd + jc .exit +.acmd41: + mov bx, 100 ; retries +.retry_acmd41: +%ifdef DEBUG_IO + mov ax, send_acmd41_msg + call print_string +%endif + mov si, cmd55 + mov cx, 1 ; response is 1 byte + mov ah, 1 ; expect idle state + call send_sd_init_cmd + ; TODO: (older cards) on error, try CMD1 + mov si, acmd41 + mov cx, 1 ; response is 1 byte + mov ah, 0 ; expect ready state + call send_sd_init_cmd + jnc .exit + pushf + xor cx, cx + mov dx, 1000 ; microseconds + mov ah, 0x86 ; wait + int 0x15 + popf + dec bx + jnz .retry_acmd41 + ; TODO: (older cards) send CMD16 to set block size +.exit: + pop si + pop dx + pop cx + pop bx + pop ds + jc .error +.success: + mov ax, init_ok_msg + call print_string + pop ax + ret +.error: + mov ax, init_error_msg + call print_string + pop ax + ret + +; +; Send an initialization command to the SD card +; in: DS:SI = command buffer +; CX = response size (in bytes) +; AH = expected highest status +; out: AX = +; CX = +; DX = +; SI = +; CF = 0 (success), 1 (error) +; FL = +; +send_sd_init_cmd: + mov dx, 0x280 ; data port +.settle_before: + mov al, 0xff + out dx, al +.send_cmd: + push cx + mov cx, 6 ; command size + cld +.send_byte: + lodsb + out dx, al + loop .send_byte + mov cx, 8 ; retries +.receive_r1: + in al, dx + cmp al, 0xff + loope .receive_r1 + pop cx + cmp al, ah + jbe .receive_payload + stc + jmp .settle_after +.receive_payload: + clc +.receive_byte: + in al, dx + loop .receive_byte +.settle_after: + mov al, 0xff + out dx, al + ret + +cmd0 db 0x40, 0x00, 0x00, 0x00, 0x00, 0x95 +cmd8 db 0x48, 0x00, 0x00, 0x01, 0xAA, 0x87 +cmd55 db 0x77, 0x00, 0x00, 0x00, 0x00, 0x01 +acmd41 db 0x69, 0x40, 0x00, 0x00, 0x00, 0x01 + +; +; Send a read or write command to the SD card +; in: CL = command +; BX:AX = LBA address +; out: AX = +; BX = +; CX = +; DX = +; CF = 0 (success), 1 (error) +; FL = +; +send_sd_read_write_cmd: + mov dx, 0x280 ; data port + push ax +.settle_before: + mov al, 0xff + out dx, al +.send_cmd: + mov al, cl ; command byte + out dx, al + mov al, bh ; address byte 1 + out dx, al + mov al, bl ; address byte 2 + out dx, al + pop ax + xchg al, ah ; address byte 3 + out dx, al + xchg al, ah ; address byte 4 + out dx, al + mov al, 0x1 ; crc (dummy) + out dx, al + mov cx, 8 ; retries +.receive_r1: + in al, dx + cmp al, 0xff + loope .receive_r1 + test al, al + jz .exit + stc +.exit: + ret + +; +; General utilities +; + +; +; Wait 100 microseconds. +; out: AX = +; FL = +delay_100us: + push cx + push dx + xor cx, cx + mov dx, 100 ; microseconds + mov ah, 0x86 ; wait + int 0x15 + pop dx + pop cx + ret + +%include "utils.inc" + +%ifdef DEBUG +debug_handler: + push ax + mov ax, handler_msg + call print_string + pop ax + push ax + mov al, ah + xor ah, ah + call print_hex + mov ax, newline + call print_string + pop ax + ret +%endif + ; ; Strings ; -welcome db 'BootROM for XTMax v0.1', 0x0D, 0x0A, 0 +welcome_msg db 'BootROM for XTMax v1.0', 0xD, 0xA + db 'Copyright (c) 2025 Matthieu Bucchianeri', 0xD, 0xA, 0 +old_13h_msg db 'Old INT13h Vector = ', 0 +new_13h_msg db 'New INT13h Vector = ', 0 +new_fdpt_msg db 'New Fixed Disk Parameter Table = ', 0 +init_ok_msg db 'SD Card initialized successfully', 0xD, 0xA, 0 +init_error_msg db 'SD Card failed to initialize', 0xD, 0xA, 0 +unsupported_msg db 'Unsupported INT13h Function ', 0 +%ifdef USE_BOOTSTRAP +new_18h_msg db 'New INT18h Vector = ', 0 +boot_msg db 'Booting from SD Card...', 0xD, 0xA, 0 +no_boot_msg db 'Not bootable', 0xD, 0xA, 0 +%endif +colon db ':', 0 +space db ' ', 0 +newline db 0xD, 0xA, 0 -%ifndef AS_COM +%ifdef DEBUG +handler_msg db 'INT13h Function ', 0 +status_msg db 'Return with ', 0 + +%ifdef DEBUG_IO +send_cmd0_msg db 'Sending CMD0', 0xD, 0xA, 0 +send_cmd8_msg db 'Sending CMD8', 0xD, 0xA, 0 +send_cmd17_msg db 'Sending CMD17', 0xD, 0xA, 0 +send_cmd24_msg db 'Sending CMD24', 0xD, 0xA, 0 +send_acmd41_msg db 'Sending ACMD41', 0xD, 0xA, 0 +wait_msg db 'Waiting for SD Card', 0xD, 0xA, 0 +sd_token_msg db 'Received token', 0xD, 0xA, 0 +sd_status_msg db 'Received status ', 0 +sd_idle_msg db 'Received idle', 0xD, 0xA, 0 +%endif + +%ifdef EXTRA_DEBUG +forward_msg db 'Forward to BIOS INT13h handler ', 0 +%endif +%endif + +%ifndef AS_COM_PROGRAM ; ; Pad to 2KB. We will try to keep our ROM under that size. ; times 2047-($-$$) db 0 db 0 ; will be used to complete the checksum. -%endif \ No newline at end of file +%endif diff --git a/XTMax/Drivers/BootROM/build.cmd b/XTMax/Drivers/BootROM/build.cmd index 63e2c5a..f3f4a4e 100644 --- a/XTMax/Drivers/BootROM/build.cmd +++ b/XTMax/Drivers/BootROM/build.cmd @@ -1,2 +1,2 @@ -..\Driver_Build_Tools\NASM\nasm.exe -f bin -o bootrom.com -DAS_COM .\bootrom.asm +..\Driver_Build_Tools\NASM\nasm.exe -f bin -o bootrom.com -DAS_COM_PROGRAM .\bootrom.asm ..\Driver_Build_Tools\NASM\nasm.exe -f bin -o bootrom .\bootrom.asm & python checksum.py diff --git a/XTMax/Drivers/BootROM/tests.inc b/XTMax/Drivers/BootROM/tests.inc new file mode 100644 index 0000000..cea7f5d --- /dev/null +++ b/XTMax/Drivers/BootROM/tests.inc @@ -0,0 +1,168 @@ +; +; This is test code invoked when building with AS_COM +; + +;%define REAL_INT13 +%define DUMP_REGS + + + xor ax, ax + lahf + mov ax, 0x1122 + mov bx, 0x3344 + mov cx, 0x5566 + mov dx, 0x7788 + mov si, 0x99aa + mov di, 0xbbcc + mov bp, 0xddee + + mov dl, 0x80 + mov ah, 0x08 + + ;mov bx, buf_write + ;mov ch, 0 + ;mov cl, 1 + ;mov dh, 0 + ;mov al, 1 + + call do_int13h + + mov dl, 0x80 + mov ah, 0x02 + + mov bx, ds + mov es, bx + mov bx, buf_read + mov ch, 0 + mov cl, 1 + mov dh, 0 + mov al, 1 + + call do_int13h + +%if 1 + mov cx, 256 + mov si, buf_read +.dump: + lodsw + call print_hex + mov ax, space + call print_string + loop .dump + mov ax, newline + call print_string + + ; wait for kbd + mov ah, 0x01 + int 0x21 +%endif + + mov ah, 0x01 + call do_int13h + mov ah, 0x01 + call do_int13h + + ;mov ax, newline + ;call print_string + ;mov ch, 255 + ;mov cl, 255 + ;mov dh, NUM_HEADS-1 + ;call compute_lba + ;push ax + ;mov ax, bx + ;call print_hex + ;pop ax + ;call print_hex + ;mov ax, newline + ;call print_string + + jmp end + +do_int13h: +%ifdef DUMP_REGS + call dump_regs +%endif + + pushf ; save flags (to compare) +%ifndef REAL_INT13 + ; simulate vector call + pushf + push cs + call int13h_entry +%else + int 0x13 +%endif + pushf ; save flags (to compare) + push ax + jnc .success +.error: + mov ax, test_failed_msg + call print_string + jmp .finish +.success: + mov ax, test_success_msg + call print_string +.finish: + pop ax + push ax + mov al, ah + xor ah, ah + call print_hex + mov ax, newline + call print_string + pop ax + + ; dump registers +%ifdef DUMP_REGS + call dump_regs + pop ax + call print_hex + mov ax, colon + call print_string + pop ax + call print_hex + mov ax, newline + call print_string +%else + popf + popf +%endif + + ; wait for kbd + mov ah, 0x01 + int 0x21 + ret + +; +; A fake INT13h handler to test redirection of floppy service +; +fake_int13h_entry: + push ax + mov ax, fake_handler_msg + call print_string + pop ax + push ax + mov al, ah + xor ah, ah + call print_hex + mov ax, newline + call print_string + pop ax + mov ah, 0xaa + push bp + mov bp, sp + or byte [bp+6], 0x1 ; set carry of flags for iret + pop bp + iret + +buf_write db 1, 2, 3, 4, 5, 6, 7, 8 + times 496 db 0 + db 248, 249, 250, 251, 252, 253, 254, 255 + times 512 db 0 +buf_read times 1024 db 0 + +fake_handler_msg db 'BIOS INT13h Function ', 0 +test_success_msg db 'Call succeeded ', 0 +test_failed_msg db 'Call failed ', 0 + +end: diff --git a/XTMax/Drivers/BootROM/utils.inc b/XTMax/Drivers/BootROM/utils.inc new file mode 100644 index 0000000..930423d --- /dev/null +++ b/XTMax/Drivers/BootROM/utils.inc @@ -0,0 +1,139 @@ +; +; Display a string. +; in: AX = string pointer +; out: AX = +; +print_string: + pushf + push ds + push bx + push si + mov si, ax +%ifndef AS_COM_PROGRAM + mov ax, ROM_SEGMENT + mov ds, ax +%endif + mov ah, 0xe + xor bx, bx + cld +.loop: + lodsb + or al, al + jz .done + int 0x10 + jmp .loop +.done: + pop si + pop bx + pop ds + popf + ret + +; +; Display a 16-bit value in hex. +; in: AX = value +; out: AX = +; +print_hex: + pushf + push ds + push bx + push cx + push dx + push si + mov dx, ax +%ifndef AS_COM_PROGRAM + mov ax, ROM_SEGMENT + mov ds, ax +%endif + xor bx, bx + cld +.nibble1: + mov si, dx + mov cl, 12 + shr si, cl + and si, 0xf + mov al, [hex_map+si] + mov ah, 0xe + int 0x10 +.nibble2: + mov si, dx + mov cl, 8 + shr si, cl + and si, 0xf + mov al, [hex_map+si] + mov ah, 0xe + int 0x10 +.nibble3: + mov si, dx + mov cl, 4 + shr si, cl + and si, 0xf + mov al, [hex_map+si] + mov ah, 0xe + int 0x10 +.nibble4: + mov si, dx + and si, 0xf + mov al, [hex_map+si] + mov ah, 0xe + int 0x10 + pop si + pop dx + pop cx + pop bx + pop ds + popf + ret + +hex_map db '0123456789ABCDEF' + +%if %isdef(EXTRA_DEBUG) || %isdef(AS_COM_PROGRAM) +dump_regs: + push ax + mov ax, registers_msg + call print_string + pop ax + push ax + call print_hex + mov ax, space + call print_string + mov ax, bx + call print_hex + mov ax, space + call print_string + mov ax, cx + call print_hex + mov ax, space + call print_string + mov ax, dx + call print_hex + mov ax, space + call print_string + mov ax, ds + call print_hex + mov ax, space + call print_string + mov ax, si + call print_hex + mov ax, space + call print_string + mov ax, es + call print_hex + mov ax, space + call print_string + mov ax, di + call print_hex + mov ax, space + call print_string + mov ax, bp + call print_hex + mov ax, space + call print_string + mov ax, newline + call print_string + pop ax + ret + +registers_msg db ' AX BX CX DX DS SI ES DI BP', 0xD, 0xA, 0 +%endif