Save_MOTO() for TO7 and MO5

This commit is contained in:
Thomas Bernard 2018-11-17 01:35:27 +01:00
parent 34b89a2768
commit a66d23a5e0
3 changed files with 397 additions and 108 deletions

View File

@ -51,6 +51,8 @@
#include "oldies.h"
#include "pages.h"
#include "keycodes.h"
#include "input.h"
#include "help.h"
#include "fileformats.h"
extern char Program_version[]; // generated in pversion.c
@ -4935,11 +4937,13 @@ void Load_MOTO(T_IO_Context * context)
byte bpp = 4;
byte code;
word length, address;
int transpose = 1;
enum MOTO_mode { F_40col, F_80col, F_bm4, F_bm16 } mode = F_40col;
enum PIXEL_RATIO ratio = PIXEL_SIMPLE;
int width = 320, height = 200, columns = 40;
static const unsigned char mo5palette[48] = {
// Taken from https://16couleurs.wordpress.com/2013/03/31/archeologie-infographique-le-pixel-art-pour-thomson/
// http://pulkomandy.tk/wiki/doku.php?id=documentations:devices:gate.arrays#video_generation
0, 0, 0, 255, 85, 85, 0, 255, 0, 255, 255, 0,
85, 85, 255, 255, 0, 255, 0, 255, 255, 255, 255, 255,
170, 170, 170, 255, 170, 255, 170, 255, 170, 255, 255, 170,
@ -4952,6 +4956,8 @@ void Load_MOTO(T_IO_Context * context)
return;
file_size = File_length_file(file);
// Load default palette
if (Config.Clear_palette)
memset(context->Palette,0,sizeof(T_Palette));
memcpy(context->Palette, mo5palette, sizeof(mo5palette));
file_type = MOTO_Check_binary_file(file);
@ -4961,7 +4967,6 @@ void Load_MOTO(T_IO_Context * context)
return;
}
if (file_type == 2) // MAP file
{
// http://collection.thomson.free.fr/code/articles/prehisto_bulletin/page.php?XI=0&XJ=13
@ -5133,6 +5138,9 @@ void Load_MOTO(T_IO_Context * context)
}
else if(file_type == 3 || file_type == 4)
{
if (file_type == 4) // MO file
transpose = 0;
do
{
if (!(Read_byte(file,&code) && Read_word_be(file,&length) && Read_word_be(file,&address)))
@ -5142,9 +5150,9 @@ void Load_MOTO(T_IO_Context * context)
fclose(file);
return;
}
// MO5 VRAM address is &H0000
// MO5/MO6 VRAM address is &H0000
// TO7/TO8/TO9 VRAM addres is &H4000
if (length >= 8000 && length <= 8192 && address == 0x4000) // TO7/TO8/TO9 VRAM address
if (length >= 8000 && length <= 8192 && (address == 0x4000 || address == 0))
{
if (vram_forme == NULL)
{
@ -5333,11 +5341,20 @@ void Load_MOTO(T_IO_Context * context)
break;
case F_40col:
default:
// the color plane byte is bfFFFBBB
// with the upper bits of both foreground (forme) and
// background (fond) inverted.
couleur_forme = ((couleurs & 0x78) >> 3) ^ 0x08;
couleur_fond = ((couleurs & 7) | ((couleurs & 0x80) >> 4)) ^ 0x08;
if (transpose)
{
// the color plane byte is bfFFFBBB (for TO7 compatibility)
// with the upper bits of both foreground (forme) and
// background (fond) inverted.
couleur_forme = ((couleurs & 0x78) >> 3) ^ 0x08;
couleur_fond = ((couleurs & 7) | ((couleurs & 0x80) >> 4)) ^ 0x08;
}
else
{
// MO5 : the color plane byte is FFFFBBBB
couleur_forme = couleurs >> 4;
couleur_fond = couleurs & 0x0F;
}
for (x = bx*8; x < bx*8+8; x++)
{
Set_pixel(context, x, y, (forme & 0x80)?couleur_forme:couleur_fond);
@ -5348,25 +5365,139 @@ void Load_MOTO(T_IO_Context * context)
}
}
/**
* GUI window to choose Thomson MO/TO saving parameters
*
* @param[out] machine target machine
* @param[out] format file format (0 = BIN, 1 = MAP)
* @param[in,out] mode video mode @ref MOTO_Graphic_Mode
*/
static int Save_MOTO_window(enum MOTO_Machine_Type * machine, int * format, enum MOTO_Graphic_Mode * mode)
{
int button;
T_Dropdown_button * machine_dd;
T_Dropdown_button * format_dd;
T_Dropdown_button * mode_dd;
static const char * mode_list[] = { "40col", "80col", "bm4", "bm16" };
Open_window(200, 125, "Thomson MO/TO Saving");
Window_set_normal_button(110,100,80,15,"Save",1,1,KEY_RETURN); // 1
Window_set_normal_button(10,100,80,15,"Cancel",1,1,KEY_ESCAPE); // 2
Print_in_window(13,18,"Target Machine:",MC_Dark,MC_Light);
machine_dd = Window_set_dropdown_button(10,28,110,15,100,
(*mode == MOTO_MODE_40col) ? "TO7/TO7-70" : "TO9/TO8/TO9+",
1, 0, 1, LEFT_SIDE,0); // 3
if (*mode == MOTO_MODE_40col)
Window_dropdown_add_item(machine_dd, MACHINE_TO7, "TO7/TO7-70");
Window_dropdown_add_item(machine_dd, MACHINE_TO8, "TO9/TO8/TO9+");
if (*mode == MOTO_MODE_40col)
Window_dropdown_add_item(machine_dd, MACHINE_MO5, "MO5");
Window_dropdown_add_item(machine_dd, MACHINE_MO6, "MO6");
Print_in_window(13,46,"Format:",MC_Dark,MC_Light);
format_dd = Window_set_dropdown_button(10,56,110,15,92,"BIN",1, 0, 1, LEFT_SIDE,0); // 4
Window_dropdown_add_item(format_dd, 0, "BIN");
Window_dropdown_add_item(format_dd, 1, "MAP/TO-SNAP");
Print_in_window(136,46,"Mode:",MC_Dark,MC_Light);
mode_dd = Window_set_dropdown_button(136,56,54,15,44,mode_list[*mode],1, 0, 1, LEFT_SIDE,0); // 5
if (*mode == MOTO_MODE_40col)
Window_dropdown_add_item(mode_dd, *mode, mode_list[*mode]);
if (*mode == MOTO_MODE_80col)
Window_dropdown_add_item(mode_dd, *mode, mode_list[*mode]);
if (*mode == MOTO_MODE_40col)
Window_dropdown_add_item(mode_dd, MOTO_MODE_bm4, mode_list[MOTO_MODE_bm4]);
if (*mode == MOTO_MODE_bm16)
Window_dropdown_add_item(mode_dd, *mode, mode_list[*mode]);
Update_window_area(0,0,Window_width,Window_height);
Display_cursor();
do
{
button = Window_clicked_button();
if (Is_shortcut(Key, 0x100+BUTTON_HELP))
{
Key = 0;
Window_help(BUTTON_SAVE, NULL);
}
else switch (button)
{
case 3:
*machine = (enum MOTO_Machine_Type)Window_attribute2;
break;
case 4:
*format = Window_attribute2;
break;
case 5:
*mode = (enum MOTO_Graphic_Mode)Window_attribute2;
break;
}
} while(button!=1 && button!=2);
Close_window();
Display_cursor();
return button==1;
}
void Save_MOTO(T_IO_Context * context)
{
int transpose = 1; // transpose upper bits in "couleur" vram
enum MOTO_Machine_Type target_machine = MACHINE_TO7;
int format = 0; // 0 = BIN, 1 = MAP
enum MOTO_Graphic_Mode mode;
FILE * file;
byte * vram_forme;
byte * vram_couleur;
int i, x, y, bx;
word reg_prc = 0xE7C3; // TO8 : PRC
word reg_prc = 0xE7C3; // PRC : TO7/8/9 0xE7C3 ; MO5/MO6 0xA7C0
byte prc_value = 0x65;// Value to write to PRC to select VRAM bank
// MO5 : 0x51
word vram_address = 0x4000; // 4000 on TO7/TO8/TO9, 0000 on MO5/MO6
File_error = 1;
if (context->Height != 200)
{
Warning_message("must be 640x200, 320x200 or 160x200");
return;
}
switch (context->Width)
{
case 160:
mode = MOTO_MODE_bm16;
break;
case 640:
mode = MOTO_MODE_80col;
break;
case 320:
mode = MOTO_MODE_40col; // or bm4
break;
default:
Warning_message("must be 640x200, 320x200 or 160x200");
return;
}
if (!Save_MOTO_window(&target_machine, &format, &mode))
return;
if (target_machine == MACHINE_MO5 || target_machine == MACHINE_MO6)
{
reg_prc = 0xA7C0; // PRC : MO5/MO6 0xA7C0
prc_value = 0x51;
vram_address = 0;
transpose = 0;
}
file = Open_file_write(context);
if (file == NULL )
{
File_error = 1;
return;
}
vram_forme = malloc(8192);
vram_couleur = malloc(8192);
switch (mode)
{
// 40col
case MOTO_MODE_40col:
{
/**
* The 40col encoding algorithm is optimized for further vertical
@ -5413,92 +5544,172 @@ void Save_MOTO(T_IO_Context * context)
}
}
GFX2_Log(GFX2_DEBUG, "Save_MOTO() most used color index %u, 2nd %u\n", previous_fond, previous_forme);
// encoding of each 8x1 block
for (bx = 0; bx < 40; bx++)
if (target_machine == MACHINE_MO5)
{
/**
* For MO5 we use a different 40col algorithm
* to make sure the last pixel of a GPL and the first the next
* are both FORME or both FOND, else we get an ugly glitch on the
* EFGJ033 Gate Array MO5!
*/
byte forme_byte = 0;
byte couleur_byte = 0x10;
GFX2_Log(GFX2_DEBUG, "Save_MOTO() 40col using MO5 algo\n");
for (y = 0; y < context->Height; y++)
{
byte forme_byte = 1;
byte col;
byte c1, c1_count = 1;
byte c2 = 0xff, c2_count = 0;
byte fond, forme;
x = bx * 8;
c1 = Get_pixel(context, x, y);
while (++x < bx * 8 + 8)
for (bx = 0; bx < 40; bx++)
{
forme_byte <<= 1;
col = Get_pixel(context, x, y);
if (col == c1)
{
forme_byte |= 1;
c1_count++;
}
byte fond = 0xff, forme = 0xff;
forme_byte &= 1; // Last bit of the previous FORME byte
x = bx*8;
if (forme_byte)
forme = Get_pixel(context, x, y);
else
fond = Get_pixel(context, x, y);
while (++x < bx * 8 + 8)
{
c2_count++;
if (c2 == 0xff)
c2 = col;
else if (col != c2)
byte col = Get_pixel(context, x, y);
forme_byte <<= 1;
if (col == forme)
forme_byte |= 1;
else if (col != fond)
{
GFX2_Log(GFX2_WARNING, "Save_MOTO() constraint error (%d,%d)\n", x, y);
goto error;
if (forme == 0xff)
{
forme_byte |= 1;
forme = col;
}
else if (fond == 0xff)
fond = col;
else
{
GFX2_Log(GFX2_WARNING, "Save_MOTO() constraint error (%d,%d)\n", x, y);
goto error;
}
}
}
if (forme != 0xff)
couleur_byte = (forme << 4) | (couleur_byte & 0x0f);
if (fond != 0xff)
couleur_byte = (couleur_byte & 0xf0) | fond;
vram_forme[bx+y*40] = forme_byte;
vram_couleur[bx+y*40] = couleur_byte;
}
if (c2 == 0xff)
}
}
else
{
GFX2_Log(GFX2_DEBUG, "Save_MOTO() 40col using optimized algo\n");
// encoding of each 8x1 block
for (bx = 0; bx < 40; bx++)
{
for (y = 0; y < context->Height; y++)
{
// Only one color in the 8x1 block
byte forme_byte = 1;
byte col;
byte c1, c1_count = 1;
byte c2 = 0xff, c2_count = 0;
byte fond, forme;
x = bx * 8;
c1 = Get_pixel(context, x, y);
while (++x < bx * 8 + 8)
{
forme_byte <<= 1;
col = Get_pixel(context, x, y);
if (col == c1)
{
forme_byte |= 1;
c1_count++;
}
else
{
c2_count++;
if (c2 == 0xff)
c2 = col;
else if (col != c2)
{
GFX2_Log(GFX2_WARNING, "Save_MOTO() constraint error (%d,%d)\n", x, y);
goto error;
}
}
}
if (c2 == 0xff)
{
// Only one color in the 8x1 block
if (c1 == previous_fond)
c2 = previous_forme;
else
c2 = previous_fond;
}
// select background color (fond)
// and foreground color (forme)
if (c1 == previous_fond)
c2 = previous_forme;
{
fond = c1;
forme = c2;
forme_byte = ~forme_byte;
}
else if (c2 == previous_fond)
{
fond = c2;
forme = c1;
}
else if (c1 == most_used_color)
{
fond = c1;
forme = c2;
forme_byte = ~forme_byte;
}
else if (c2 == most_used_color)
{
fond = c2;
forme = c1;
}
else if (c1_count >= c2_count)
{
fond = c1;
forme = c2;
forme_byte = ~forme_byte;
}
else
c2 = previous_fond;
{
fond = c2;
forme = c1;
}
// write to VRAM
vram_forme[bx+y*40] = forme_byte;
// transpose for TO7 compatibility
if (transpose)
vram_couleur[bx+y*40] = ((fond & 7) | ((fond & 8) << 4) | (forme << 3)) ^ 0xC0;
else
vram_couleur[bx+y*40] = fond | (forme << 4);
previous_fond = fond;
previous_forme = forme;
}
// select background color (fond)
// and foreground color (forme)
if (c1 == previous_fond)
if (transpose)
{
fond = c1;
forme = c2;
forme_byte = ~forme_byte;
}
else if (c2 == previous_fond)
{
fond = c2;
forme = c1;
}
else if (c1 == most_used_color)
{
fond = c1;
forme = c2;
forme_byte = ~forme_byte;
}
else if (c2 == most_used_color)
{
fond = c2;
forme = c1;
}
else if (c1_count >= c2_count)
{
fond = c1;
forme = c2;
forme_byte = ~forme_byte;
previous_fond = (vram_couleur[bx] & 7) | (~vram_couleur[bx] & 0x80) >> 4;
previous_forme = ((vram_couleur[bx] & 0x78) >> 3) ^ 8;
}
else
{
fond = c2;
forme = c1;
previous_fond = vram_couleur[bx] & 15;
previous_forme = vram_couleur[bx] >> 4;
}
// write to VRAM
vram_forme[bx+y*40] = forme_byte;
vram_couleur[bx+y*40] = ((fond & 7) | ((fond & 8) << 4) | (forme << 3)) ^ 0xC0;
previous_fond = fond;
previous_forme = forme;
}
previous_fond = (vram_couleur[bx-1] & 7) | (~vram_couleur[bx-1] & 0x80) >> 4;
previous_forme = ((vram_couleur[bx-1] & 0x78) >> 3) ^ 8;
}
}
break;
case MOTO_MODE_80col:
GFX2_Log(GFX2_WARNING, "80col not implemented!\n");
break;
case MOTO_MODE_bm4:
GFX2_Log(GFX2_WARNING, "bm4 not implemented!\n");
break;
case MOTO_MODE_bm16:
GFX2_Log(GFX2_WARNING, "bm16 not implemented!\n");
break;
}
// palette
for (i = 0; i < 16; i++)
@ -5507,26 +5718,41 @@ void Save_MOTO(T_IO_Context * context)
vram_forme[8000+i*2] = to8color >> 8;
vram_forme[8000+i*2+1] = to8color & 0xFF;
}
// Commentaire
if (context->Comment[0] != '\0')
strncpy((char *)vram_forme + 8032, context->Comment, 32);
if (1)
{
word chunk_length;
if (target_machine == MACHINE_TO7 || target_machine == MACHINE_MO5)
chunk_length = 8000; // Do not save palette
else
{
chunk_length = 8000 + 32 + 32; // data + palette + comment
// Commentaire
if (context->Comment[0] != '\0')
strncpy((char *)vram_forme + 8032, context->Comment, 32);
else
snprintf((char *)vram_forme + 8032, 32, "GrafX2 %s.%s", Program_version, SVN_revision);
memcpy(vram_couleur + 8000, vram_forme + 8000, 64);
}
// Format BIN
// TO8/TO9 : set LGAMOD 0xE7DC 40col=0 bm4=0x21 80col=0x2a bm16=0x7b
if (!DECB_BIN_Add_Chunk(file, 1, reg_prc, &prc_value))
goto error;
if (!DECB_BIN_Add_Chunk(file, chunk_length, vram_address, vram_forme))
goto error;
prc_value &= 0xFE; // select color data
if (!DECB_BIN_Add_Chunk(file, 1, reg_prc, &prc_value))
goto error;
if (!DECB_BIN_Add_Chunk(file, chunk_length, vram_address, vram_couleur))
goto error;
if (!DECB_BIN_Add_End(file, 0x0000))
goto error;
}
else
snprintf((char *)vram_forme + 8032, 32, "GrafX2 %s.%s", Program_version, SVN_revision);
memcpy(vram_couleur + 8000, vram_forme + 8000, 64);
// Format BIN
// TO8/TO9 : set LGAMOD 0xE7DC 40col=0 bm4=0x21 80col=0x2a bm16=0x7b
if (!DECB_BIN_Add_Chunk(file, 1, reg_prc, &prc_value))
goto error;
if (!DECB_BIN_Add_Chunk(file, 8000+64, 0x4000, vram_forme))
goto error;
prc_value &= 0xFE; // select color data
if (!DECB_BIN_Add_Chunk(file, 1, reg_prc, &prc_value))
goto error;
if (!DECB_BIN_Add_Chunk(file, 8000+64, 0x4000, vram_couleur))
goto error;
if (!DECB_BIN_Add_End(file, 0x0000))
goto error;
// TODO : format MAP
{
// TODO : format MAP
}
fclose(file);
File_error = 0;
return;

View File

@ -420,12 +420,11 @@ int C64_FLI_enforcer(void)
return 0;
}
/// @ingroup moto
int MOTO_Check_binary_file(FILE * f)
int DECB_Check_binary_file(FILE * f)
{
int type = 1; // BIN
byte code;
word length, address;
long end;
// Check end of file
if (fseek(f, -5, SEEK_END) < 0)
@ -434,6 +433,36 @@ int MOTO_Check_binary_file(FILE * f)
return 0;
if (code != 0xff || length != 0)
return 0;
end = ftell(f);
if (end < 0)
return 0;
if (fseek(f, 0, SEEK_SET) < 0)
return 0;
// Read Binary structure
do
{
if (!(Read_byte(f, &code) && Read_word_be(f, &length) && Read_word_be(f, &address)))
return 0;
// skip chunk content
if (fseek(f, length, SEEK_CUR) < 0)
return 0;
}
while(code == 0);
if (code != 0xff)
return 0;
if (ftell(f) != end)
return 0;
return 1;
}
int MOTO_Check_binary_file(FILE * f)
{
int type = 1; // BIN
byte code;
word length, address;
if (!DECB_Check_binary_file(f))
return 0;
if (fseek(f, 0, SEEK_SET) < 0)
return 0;
// Read Binary structure
@ -452,7 +481,7 @@ int MOTO_Check_binary_file(FILE * f)
if (!Read_bytes(f, map_header, 3))
return 0;
length -= 3;
if ((map_header[0] & 0x3F) == 0 && (map_header[0] & 0xC0) != 0xC0)
if ((map_header[0] & 0x3F) == 0 && (map_header[0] & 0xC0) != 0xC0) // 00, 40 or 80
{
GFX2_Log(GFX2_DEBUG, "Thomson MAP &H%02X %u %u\n", map_header[0], map_header[1], map_header[2]);
if (((map_header[1] < 80 && map_header[0] != 0) // <= 80col in 80col and bm16
@ -460,17 +489,24 @@ int MOTO_Check_binary_file(FILE * f)
&& map_header[2] < 25) // <= 200 pixels high
type = 2; // MAP file (SAVEP/LOADP format)
}
if (type == 1)
{
if (length == 8000 || length == 8032 || length == 8064)
type = 4; // MO autoloading picture
}
}
else if (length == 1)
{
if(address == 0xE7C3) // TO7/TO8/TO9 6846 PRC
type = 3; // TO autoloading picture
else if(address == 0xA7C0) // MO5/MO6 PRC
type = 4; // MO autoloading picture
}
else if (length == 1 && address == 0xE7C3) // TO8/TO9 6846 PRC
type = 3; // TO autoloading picture
// TODO : check autoloading MO5...
if (fseek(f, length, SEEK_CUR) < 0)
return 0;
}
while(code == 0);
if (code != 0xff)
return 0;
return type;
}
@ -489,8 +525,6 @@ int DECB_BIN_Add_End(FILE * f, word address)
&& Write_word_be(f, address);
}
/** @ingroup moto
* @{ */
word MOTO_gamma_correct_RGB_to_MOTO(const T_Components * color)
{
word r, g, b;
@ -511,4 +545,3 @@ void MOTO_gamma_correct_MOTO_to_RGB(T_Components * color, word bgr)
color->G = (byte)round(pow(((bgr >> 4)& 0x0F)/15.0, inv_gamma) * 255.0);
color->R = (byte)round(pow((bgr & 0x0F)/15.0, inv_gamma) * 255.0);
}
/** @} */

View File

@ -70,6 +70,12 @@ int DECB_BIN_Add_Chunk(FILE * f, word size, word address, const byte * data);
int DECB_BIN_Add_End(FILE * f, word address);
/**
* Check if the file is in the DECB Binary format
*
* @param f open file
* @return 1 if the file is in DECB Binary format, or else 0
*/
int DECB_Check_binary_file(FILE * f);
/** @}*/
@ -88,7 +94,31 @@ int DECB_Check_binary_file(FILE * f);
* - TO8/TO8D
* - TO9
* - Olivetti Prodest PC128 (a Thomson MO6 clone)
* @{
*/
/**
* to define a specific machine in the Thomson MO/TO range of machines
*/
enum MOTO_Machine_Type {
MACHINE_TO7, ///< original TO7 with 8 colors
MACHINE_TO770, ///< the TO7-70 had 16 colors
MACHINE_MO5,
MACHINE_MO6, ///<
MACHINE_TO9,
MACHINE_TO8 ///< TO8, TO8D and TO9+ are equivalent
};
/**
* Graphic modes available in BASIC 128/512 with CONSOLE,,,,X instruction
*/
enum MOTO_Graphic_Mode {
MOTO_MODE_40col = 0, ///< 320x200 16 colors with constraints
MOTO_MODE_80col = 1, ///< 640x200 2 colors
MOTO_MODE_bm4 = 2, ///< 320x200 4 colors without constraint
MOTO_MODE_bm16 = 3, ///< 160x200 16 colors without constraint
};
/**
* Checks if the file is a Thomson binary file (SAVEM/LOADM format)
*