From 22b6c2439f1f6af94dc064d58a1ad35d12ba4952 Mon Sep 17 00:00:00 2001 From: Thomas Bernard Date: Sun, 22 Dec 2019 02:21:34 +0100 Subject: [PATCH] CPC: save autoload .SCR with a .BAS if needed --- src/cpc_scr_simple_loader.h | 28 ++++++++ src/cpcformats.c | 124 ++++++++++++++++++++++++++++++++---- src/impdraw_loader.h | 65 +++++++++++++++++++ src/oldies.c | 55 ++++++++++++++++ src/oldies.h | 10 +++ 5 files changed, 271 insertions(+), 11 deletions(-) create mode 100644 src/cpc_scr_simple_loader.h create mode 100644 src/impdraw_loader.h diff --git a/src/cpc_scr_simple_loader.h b/src/cpc_scr_simple_loader.h new file mode 100644 index 00000000..fd56ec0b --- /dev/null +++ b/src/cpc_scr_simple_loader.h @@ -0,0 +1,28 @@ +/* vim:expandtab:ts=2 sw=2: + */ +/** + * author : unknown + */ +static const unsigned char cpc_scr_simple_loader[] = { + 0x3a, 0xd0, 0xd7, // C7D0 LD A,(D7D0) + 0xcd, 0x1c, 0xbd, // C7D3 CALL BD1C ; Set screen mode + 0x21, 0xd1, 0xd7, // C7D6 LD HL,D7D1 + 0x46, // C7D9 LD B,(HL) + 0x48, // C7DA LD C,B + 0xcd, 0x38, 0xbc, // C7DB CALL BC38 ; Set border + 0xaf, // C7DE XOR A + 0x21, 0xd1, 0xd7, // C7DF LD HL,D7D1 + 0x46, // C7E2 LD B,(HL) <-------------------+ + 0x48, // C7E3 LD C,B | + 0xf5, // C7E4 PUSH AF | + 0xe5, // C7E5 PUSH HL | + 0xcd, 0x32, 0xbc, // C7E6 CALL BC32 ; Set ink A to color B,C | + 0xe1, // C7E9 POP HL | + 0xf1, // C7EA POP AF | + 0x23, // C7EB INC HL | + 0x3c, // C7EC INC A | + 0xfe, 0x10, // C7ED CP &10 | + 0x20, 0xf1, // C7EF JZ NZ, C7E2 -------------------+ + 0xc3, 0x18, 0xbb // C7F1 JP BB18 ; Wait key + +}; diff --git a/src/cpcformats.c b/src/cpcformats.c index ba2c92ec..b815023f 100644 --- a/src/cpcformats.c +++ b/src/cpcformats.c @@ -29,6 +29,7 @@ #include #include +#include #include "global.h" #include "fileformats.h" #include "io.h" @@ -607,6 +608,9 @@ void Load_SCR(T_IO_Context * context) free(cpc_ram); } +#include "impdraw_loader.h" +#include "cpc_scr_simple_loader.h" + /** * Save Amstrad SCR file * @@ -633,32 +637,43 @@ void Save_SCR(T_IO_Context * context) unsigned char r1 = 0; int cpc_mode; FILE* file; - + int cpc_plus_pal = 0; + unsigned short load_address = 0xC000; + unsigned short exec_address = 0xC7D0; + int overscan; + byte cpc_hw_pal[16]; + byte r12 = 0x0C | 0x30; + byte r13 = 0; switch(context->Ratio) { case PIXEL_WIDE: case PIXEL_WIDE2: cpc_mode = 0; + overscan = (context->Width * context->Height) > (16384 * 2); break; case PIXEL_TALL: case PIXEL_TALL2: case PIXEL_TALL3: cpc_mode = 2; + overscan = (context->Width * context->Height) > (16384 * 8); break; default: cpc_mode = 1; + overscan = (context->Width * context->Height) > (16384 * 4); break; } - - file = Open_file_write_with_alternate_ext(context, "pal"); - if (file == NULL) - return; - if (!Write_byte(file, cpc_mode) || !Write_byte(file, 0) || !Write_byte(file, 0)) + if (overscan) { - fclose(file); - return; + // format iMP v2 + load_address = 0x170; + // picture at 0x200 + r12 = 0x0C | (0x200 >> 9); + r13 = (0x200 >> 1) & 0xff; + exec_address = 0; // BASIC program ! } + + CPC_set_HW_palette(context->Palette + 0x40); for (i = 0; i < 16; i++) { // search for the color in the HW palette (0x40-0x5F) @@ -669,12 +684,28 @@ void Save_SCR(T_IO_Context * context) if (index >= 0x60) { GFX2_Log(GFX2_WARNING, "Save_SCR() color #%i not found in CPC HW palette.\n", i); - // TODO : set CPC Plus flag + cpc_plus_pal = 1; index = 0x54 - i; // default } + cpc_hw_pal[i] = index; + if (!CPC_is_CPC_old_color(context->Palette + i)) + cpc_plus_pal = 1; + } + + file = Open_file_write_with_alternate_ext(context, "pal"); + if (file == NULL) + return; + CPC_write_AMSDOS_header(file, context->File_name, "pal", 2, 0x8809, 0x8809, 239); + if (!Write_byte(file, cpc_mode) || !Write_byte(file, 0) || !Write_byte(file, 0)) + { + fclose(file); + return; + } + for (i = 0; i < 16; i++) + { for (j = 0; j < 12; j++) // write the same color for the 12 frames { - Write_byte(file, index); + Write_byte(file, cpc_hw_pal[i]); } } // border @@ -694,7 +725,7 @@ void Save_SCR(T_IO_Context * context) } fclose(file); - output = raw2crtc(context, cpc_mode, 7, &outsize, &r1, 0x0C, 0); + output = raw2crtc(context, cpc_mode, 7, &outsize, &r1, r12, r13); GFX2_Log(GFX2_DEBUG, "Save_SCR() output=%p outsize=%lu r1=$%02X\n", output, outsize, r1); if (output == NULL) @@ -706,9 +737,80 @@ void Save_SCR(T_IO_Context * context) else { File_error = 0; + CPC_write_AMSDOS_header(file, context->File_name, "SCR", overscan ? 0 : 2, load_address, exec_address, overscan ? 32400 : outsize); + if (overscan) + { + // write iMPdraw loader + byte buffer[0x90]; + memcpy(buffer, impdraw_loader, 0x90); + buffer[0x184 - 0x170] = 0x0e + cpc_mode; + buffer[0x18e - 0x170] = r1; + //buffer[0x190 - 0x170] = // R2 ? + buffer[0x192 - 0x170] = context->Height / 8; // r6 + //buffer[0x194 - 0x170] = // r7 ? + buffer[0x196 - 0x170] = r12; + buffer[0x198 - 0x170] = r13; + buffer[0x1ac - 0x170] = cpc_plus_pal; + if (!Write_bytes(file, buffer, 0x90)) + File_error = 1; + output = realloc(output, 32400 - 0x90); + memset(output + outsize, 0, 32400 - 0x90 - outsize); + outsize = 32400 - 0x90; + memcpy(output + 0x7F00 - 0x200, cpc_hw_pal, 16); + for (i = 0; i < 16; i++) + { + output[0x601 + i*2] = (context->Palette[i].R & 0xf0) | (context->Palette[i].B >> 4); + output[0x602 + i*2] = context->Palette[i].G >> 4; + } + } + else + { + memcpy(output + exec_address - load_address, cpc_scr_simple_loader, sizeof(cpc_scr_simple_loader)); + output[0xd7d0 - load_address] = cpc_mode; + //memcpy(output + 0xd7d1 - load_address, cpc_hw_pal, 16); + for (i = 0; i < 16; i++) + { + output[0xd7d1 - load_address + i] = (context->Palette[i].G / 86) * 9 + (context->Palette[i].R / 86) * 3 + (context->Palette[i].B / 86); + } + } if (!Write_bytes(file, output, outsize)) File_error = 1; fclose(file); + if (!overscan) + { + file = Open_file_write_with_alternate_ext(context, "BAS"); + if (file != NULL) + { + byte buffer[128]; + memset(buffer, 0, sizeof(buffer)); + buffer[2] = 10; // basic line number + buffer[4] = 0xad; // MODE + buffer[5] = ' '; + buffer[6] = 0x0e + cpc_mode; + buffer[7] = 0x01; // : + buffer[8] = 0xa8; // LOAD + buffer[9] = '"'; + for (i = 0; i < 8; i++) + { + if (context->File_name[i] == '\0' || context->File_name[i] == '.') + break; + buffer[10+i] = (byte)toupper(context->File_name[i]); + } + memcpy(buffer + 10 + i, ".SCR\"", 5); + i += 15; + buffer[i++] = 0x01; // : + buffer[i++] = 0x83; // CALL + buffer[i++] = 0x1c; // & + buffer[i++] = 0xd0; + buffer[i++] = 0xc7; + buffer[i++] = 0x00; + buffer[0] = (byte)i; // length of line data + CPC_write_AMSDOS_header(file, context->File_name, "BAS", 0, 0x170, 0, i + 2); + if (!Write_bytes(file, buffer, 128)) + File_error = 1; + fclose(file); + } + } } free (output); } diff --git a/src/impdraw_loader.h b/src/impdraw_loader.h new file mode 100644 index 00000000..f5965c7d --- /dev/null +++ b/src/impdraw_loader.h @@ -0,0 +1,65 @@ +/* vim:expandtab:ts=2 sw=2: + */ +/** + * Amstrad CPC Loader for overscan picture. + * Locomotive BASIC + Z80 Code + * (c) Ast/iMP4CT + * https://amstradplus.forumforever.com/t462-iMPdraw-v2-0.htm?start=45 + */ +static const unsigned char impdraw_loader[] = { + 0x0e, 0x00, 0x0a, 0x00, 0x01, 0xc0, 0x20, 0x69, // starts at &0170 + 0x4d, 0x50, 0x20, 0x76, 0x32, 0x00, 0x0d, 0x00, // 10 ' iMP v2 + 0x14, 0x00, 0xad, 0x20, 0x0e, 0x01, 0x83, 0x1c, // 20 MODE 0:CALL &1ad + 0xad, 0x01, 0x00, 0x00, 0x00, // &0184 : token 0x0e = 0, 0x0f = 1, 0x10 = 2 + 0x01, 0x30, 0x02, 0x32, 0x06, 0x22, 0x07, 0x23, // From &018d CRTC registers + 0x0c, 0x0d, 0xd0, 0x00, 0x00, 0x3f, + 0xff, 0x00, 0xff, 0x77, 0xb3, 0x51, 0xa8, 0xd4, // From &019b CPC Plus ASIC unlocking sequence + 0x62, 0x39, 0x9c, 0x46, 0x2b, 0x15, 0x8a, 0xcd, + 0xee, + 0x00, // &01ac = CPC PLUS flag (0=CPC old, 1=CPC+) + 0xf3, // 01ad di + 0x21, 0x8d, 0x01, // 01ae ld hl,&018d + 0x3e, 0x06, // 01b1 ld a,&06 + 0x01, 0xbe, 0xbd, // 01b3 ld bc,BDBE + 0xed, 0xa3, // 01b6 outi ; first decrements b <---+ + 0x41, // 01b8 ld b,c | + 0xed, 0xa3, // 01b9 outi | + 0x3d, // 01bb dec a | + 0x20, 0xf8, // 01bc jz nz,01B6 ; loop ------------+ + // load colors into gate array (from 7F00 ;) + 0x01, 0x00, 0x7f, // 01be ld bc,7F00 ; Gate array + 0x1e, 0x10, // 01c1 ld e,10 + 0x0a, // 01c3 ld a,(bc) <---+ + 0xed, 0x49, // 01c4 out (c),c ; select ink# | + 0xed, 0x79, // 01c6 out (c),a ; set ink | + 0x0c, // 01c8 inc c | + 0x1d, // 01c9 dec e | + 0x20, 0xf7, // 01ca jz nz,01C3 ; loop ---------+ + + 0x3a, 0xac, 0x01, // 01cc ld a,01AC ; CPC PLUS flag + 0xfe, 0x01, // 01cf cp 01 + 0x20, 0x22, // 01d1 jz nz,01F5 ; flag != 1 => skip CPC+ + // CPC Plus ASIC unlock + 0x21, 0x9b, 0x01, // 01d3 ld hl,019B + 0x01, 0x11, 0xbc, // 01d6 ld bc,BC11 + 0x7e, // 01d9 ld a,(hl) <---+ + 0xed, 0x79, // 01da out (c),a | + 0x2c, // 01dc inc l | + 0x0d, // 01dd dec c | + 0x20, 0xf9, // 01de jr nz,01D9 ; loop --+ + // set CPC Plus colors + 0x01, 0xb8, 0x7f, // 01e0 ld bc,7FB8 + 0xed, 0x49, // 01e3 out (c),c ; open ASIC I/O + 0x21, 0x01, 0x08, // 01e5 ld hl,0801 + 0x11, 0x00, 0x64, // 01e8 ld de,6400 + 0x01, 0x20, 0x00, // 01eb ld bc,0020 + 0xed, 0xb0, // 01ee ldir ; copy bc bytes from (hl) to (de) + 0x01, 0xa0, 0x7f, // 01f0 ld bc,7FA0 + 0xed, 0x49, // 01f3 out (c),c ; close ASIC I/O + + 0x21, 0xf9, 0xb7, // 01f5 ld hl,B7F9 ; SCR Event Block: Set Inks + 0xcd, 0xdd, 0xbc, // 01f8 call BCDD ; remove event block for CRTC IRQ + 0xfb, // 01fb ei + 0xc3, 0x18, 0xbb, // 01fc jp BB18 ; Wait Keypress + 0x00 // 01ff +}; diff --git a/src/oldies.c b/src/oldies.c index 86ca0d28..f78fad71 100644 --- a/src/oldies.c +++ b/src/oldies.c @@ -27,6 +27,7 @@ #endif #include #include +#include #include #include "struct.h" #include "oldies.h" @@ -724,6 +725,23 @@ int CPC_compare_colors(T_Components * col1, T_Components * col2) return v1 == v2; } +int CPC_is_CPC_old_color(T_Components * col) +{ + if (15 < col->R && col->R < 0x60) + return 0; + if (0x82 < col->R && col->R < 0xf0) + return 0; + if (15 < col->G && col->G < 0x60) + return 0; + if (0x82 < col->G && col->G < 0xf0) + return 0; + if (15 < col->B && col->B < 0x60) + return 0; + if (0x82 < col->B && col->B < 0xf0) + return 0; + return 1; +} + void CPC_set_default_BASIC_palette(T_Components * palette) { static const byte basic_colors[] = { @@ -808,6 +826,43 @@ int CPC_check_AMSDOS(FILE * file, word * loading_address, word * exec_address, u return 1; } +int CPC_write_AMSDOS_header(FILE * file, const char * filename, const char * ext, byte type, word load_address, word exec_address, word file_length) +{ + int i; + word checksum = 0; + byte header[128]; + + memset(header, 0, sizeof(header)); + memset(header + 1, ' ', 11); + for (i = 0 ; i < 8; i++) + { + if (filename[i] == '\0' || filename[i] == '.') + break; + header[1+i] = (byte)toupper(filename[i]); + } + for (i = 0 ; i < 3; i++) + { + if (ext[i] == '\0' || ext[i] == '.') + break; + header[9+i] = (byte)toupper(ext[i]); + } + header[18] = type; + header[21] = load_address & 0xff; + header[22] = load_address >> 8; + header[24] = file_length & 0xff; + header[25] = file_length >> 8; + header[26] = exec_address & 0xff; + header[27] = exec_address >> 8; + header[64] = file_length & 0xff; + header[65] = file_length >> 8; + for (i = 0; i < 67; i++) + checksum += (word)header[i]; + header[67] = checksum & 0xff; + header[68] = checksum >> 8; + GFX2_LogHexDump(GFX2_DEBUG, "AMSDOS ", header, 0, sizeof(header)); + return Write_bytes(file, header, sizeof(header)); +} + int DECB_Check_binary_file(FILE * f) { byte code; diff --git a/src/oldies.h b/src/oldies.h index 9c5f5b7a..fdff4669 100644 --- a/src/oldies.h +++ b/src/oldies.h @@ -109,6 +109,11 @@ void ZX_Spectrum_set_palette(T_Components * palette); */ void CPC_set_HW_palette(T_Components * palette); +/** + * Check if the color is likely to be a CPC RGB tri level color + */ +int CPC_is_CPC_old_color(T_Components * col); + /** * Set 16 color default Amstrad CPC BASIC palette * @@ -147,6 +152,11 @@ byte CPC_Firmware_to_Hardware_color(byte fw_color); */ int CPC_check_AMSDOS(FILE * file, word * loading_address, word * exec_address, unsigned long * file_length); +/** + * Write AMSDOS header + */ +int CPC_write_AMSDOS_header(FILE * file, const char * filename, const char * ext, byte type, word load_address, word save_address, word file_length); + /** @}*/ /** @defgroup decb DECB binary format