From 41aacd0fc3d62bce1b40e4a96921d80e2b367ab3 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 4 Jun 2026 07:58:25 -0700 Subject: [PATCH] od-unix: add RTG and capture backends Add Unix RTG presentation, Picasso96 host glue, screenshot support, and AVI capture output. These backends connect the shared RTG and capture code to the Unix video surface and local file output. --- od-unix/avioutput.cpp | 655 +++++++++ od-unix/avioutput.h | 39 + od-unix/picasso96_unix.h | 78 ++ od-unix/rtg.cpp | 2819 ++++++++++++++++++++++++++++++++++++++ od-unix/screenshot.cpp | 748 ++++++++++ 5 files changed, 4339 insertions(+) create mode 100644 od-unix/avioutput.cpp create mode 100644 od-unix/avioutput.h create mode 100644 od-unix/picasso96_unix.h create mode 100644 od-unix/rtg.cpp create mode 100644 od-unix/screenshot.cpp diff --git a/od-unix/avioutput.cpp b/od-unix/avioutput.cpp new file mode 100644 index 00000000..d6608e31 --- /dev/null +++ b/od-unix/avioutput.cpp @@ -0,0 +1,655 @@ +#include "sysconfig.h" +#include "sysdeps.h" + +#include "audio.h" +#include "avioutput.h" +#include "custom.h" +#include "fsdb.h" +#include "options.h" +#include "uae.h" +#include "xwin.h" + +#include "sounddep/sound.h" + +#include +#include + +#define AVI_HAS_INDEX 0x10 + +extern int video_recording_active; + +struct UnixAviIndexEntry { + char id[4]; + uae_u32 flags; + uae_u32 offset; + uae_u32 size; +}; + +static FILE *avi_file; +static FILE *wav_file; +static std::vector avi_index; +static long riff_size_pos; +static long hdrl_size_pos; +static long movi_size_pos; +static long movi_data_start; +static long avih_total_frames_pos; +static long video_length_pos; +static long audio_length_pos; +static int avi_frame_count; +static uae_u32 avi_audio_bytes; +static int aviout_monid; +static int avioutput_fps = VBLANK_HZ_PAL; +static bool avi_header_written; +static bool avi_failed; + +int avioutput_audio, avioutput_video, avioutput_enabled, avioutput_requested; +int avioutput_width, avioutput_height, avioutput_bits; +int avioutput_framelimiter = 0, avioutput_nosoundoutput = 0; +int avioutput_nosoundsync = 1, avioutput_originalsize = 0; + +TCHAR avioutput_filename_gui[MAX_DPATH]; +TCHAR avioutput_filename_auto[MAX_DPATH]; +TCHAR avioutput_filename_inuse[MAX_DPATH]; + +static void put_u16(FILE *f, uae_u16 value) +{ + fputc(value & 0xff, f); + fputc((value >> 8) & 0xff, f); +} + +static void put_u32(FILE *f, uae_u32 value) +{ + fputc(value & 0xff, f); + fputc((value >> 8) & 0xff, f); + fputc((value >> 16) & 0xff, f); + fputc((value >> 24) & 0xff, f); +} + +static void put_fourcc(FILE *f, const char id[4]) +{ + fwrite(id, 1, 4, f); +} + +static void patch_u32(FILE *f, long pos, uae_u32 value) +{ + long current = ftell(f); + fseek(f, pos, SEEK_SET); + put_u32(f, value); + fseek(f, current, SEEK_SET); +} + +static bool unix_avi_ensure_parent_dir(const TCHAR *filename) +{ + TCHAR path[MAX_DPATH]; + + if (!filename || !filename[0]) { + return false; + } + _tcsncpy(path, filename, sizeof path / sizeof(TCHAR) - 1); + path[sizeof path / sizeof(TCHAR) - 1] = 0; + + TCHAR *slash = _tcsrchr(path, '/'); + if (!slash) { + return true; + } + *slash = 0; + if (!path[0] || my_existsdir(path)) { + return true; + } + + for (TCHAR *p = path + 1; *p; p++) { + if (*p != '/') { + continue; + } + *p = 0; + if (path[0] && !my_existsdir(path) && my_mkdir(path) < 0) { + *p = '/'; + return false; + } + *p = '/'; + } + return my_existsdir(path) || my_mkdir(path) == 0; +} + +static void unix_avi_default_filename(void) +{ + if (avioutput_filename_gui[0]) { + return; + } + TCHAR path[MAX_DPATH]; + fetch_videopath(path, sizeof path / sizeof(TCHAR)); + _sntprintf(avioutput_filename_gui, sizeof avioutput_filename_gui / sizeof(TCHAR), + _T("%soutput.avi"), path); + avioutput_filename_gui[sizeof avioutput_filename_gui / sizeof(TCHAR) - 1] = 0; +} + +static void unix_avi_prepare_filename(void) +{ + unix_avi_default_filename(); + _tcsncpy(avioutput_filename_inuse, avioutput_filename_gui, + sizeof avioutput_filename_inuse / sizeof(TCHAR) - 1); + avioutput_filename_inuse[sizeof avioutput_filename_inuse / sizeof(TCHAR) - 1] = 0; + + const TCHAR *wanted = avioutput_audio == AVIAUDIO_WAV ? _T(".wav") : _T(".avi"); + TCHAR *dot = _tcsrchr(avioutput_filename_inuse, '.'); + if (!dot) { + _tcsncat(avioutput_filename_inuse, wanted, + sizeof avioutput_filename_inuse / sizeof(TCHAR) - _tcslen(avioutput_filename_inuse) - 1); + } else if (_tcsicmp(dot, wanted)) { + _tcscpy(dot, wanted); + } + _tcsncpy(avioutput_filename_auto, avioutput_filename_inuse, + sizeof avioutput_filename_auto / sizeof(TCHAR) - 1); + avioutput_filename_auto[sizeof avioutput_filename_auto / sizeof(TCHAR) - 1] = 0; +} + +static int unix_avi_channels(void) +{ + int channels = get_audio_nativechannels(active_sound_stereo); + if (channels <= 0) { + channels = 2; + } + return channels; +} + +static int unix_avi_sample_rate(void) +{ + return currprefs.sound_freq > 0 ? currprefs.sound_freq : 44100; +} + +static void write_wave_header(FILE *f, uae_u32 data_size) +{ + int channels = unix_avi_channels(); + int sample_rate = unix_avi_sample_rate(); + int block_align = channels * 2; + int byte_rate = sample_rate * block_align; + + put_fourcc(f, "RIFF"); + put_u32(f, 36 + data_size); + put_fourcc(f, "WAVE"); + put_fourcc(f, "fmt "); + put_u32(f, 16); + put_u16(f, 1); + put_u16(f, channels); + put_u32(f, sample_rate); + put_u32(f, byte_rate); + put_u16(f, block_align); + put_u16(f, 16); + put_fourcc(f, "data"); + put_u32(f, data_size); +} + +static bool start_wav_if_needed(void) +{ + if (wav_file) { + return true; + } + + unix_avi_prepare_filename(); + if (!unix_avi_ensure_parent_dir(avioutput_filename_inuse)) { + write_log(_T("AVIOutput: can't create output directory for '%s'\n"), avioutput_filename_inuse); + avi_failed = true; + return false; + } + + wav_file = _tfopen(avioutput_filename_inuse, _T("wb")); + if (!wav_file) { + write_log(_T("AVIOutput: can't open '%s'\n"), avioutput_filename_inuse); + avi_failed = true; + return false; + } + write_wave_header(wav_file, 0); + avi_audio_bytes = 0; + write_log(_T("Wave output to '%s' started\n"), avioutput_filename_inuse); + return true; +} + +static bool prepare_video_frame(int monid, std::vector *frame, int *widthp, int *heightp) +{ + if (!frame || !widthp || !heightp) { + return false; + } + if (monid < 0 || monid >= MAX_AMIGADISPLAYS) { + monid = 0; + } + + struct vidbuf_description *vidinfo = &adisplays[monid].gfxvidinfo; + struct vidbuffer *vb = vidinfo->inbuffer ? vidinfo->inbuffer : &vidinfo->drawbuffer; + if (!vb || !vb->bufmem || vb->outwidth <= 0 || vb->outheight <= 0 || vb->rowbytes <= 0) { + return false; + } + if (vb->pixbytes != 4 && vb->pixbytes != 2) { + return false; + } + + int width = avioutput_originalsize || currprefs.aviout_width <= 0 ? vb->outwidth : currprefs.aviout_width; + int height = avioutput_originalsize || currprefs.aviout_height <= 0 ? vb->outheight : currprefs.aviout_height; + int src_x = avioutput_originalsize || currprefs.aviout_xoffset < 0 ? 0 : currprefs.aviout_xoffset; + int src_y = avioutput_originalsize || currprefs.aviout_yoffset < 0 ? 0 : currprefs.aviout_yoffset; + if (src_x >= vb->outwidth || src_y >= vb->outheight) { + return false; + } + width = std::min(width, vb->outwidth - src_x); + height = std::min(height, vb->outheight - src_y); + if (width <= 0 || height <= 0) { + return false; + } + + int rowbytes = (width * 3 + 3) & ~3; + frame->assign((size_t)rowbytes * (size_t)height, 0); + for (int y = 0; y < height; y++) { + const uae_u8 *src = vb->bufmem + (size_t)(src_y + height - 1 - y) * (size_t)vb->rowbytes + + (size_t)src_x * (size_t)vb->pixbytes; + uae_u8 *dst = frame->data() + (size_t)y * (size_t)rowbytes; + for (int x = 0; x < width; x++) { + uae_u8 r, g, b; + if (vb->pixbytes == 4) { + const uae_u32 pixel = ((const uae_u32 *)src)[x]; + b = pixel & 0xff; + g = (pixel >> 8) & 0xff; + r = (pixel >> 16) & 0xff; + } else { + const uae_u16 pixel = (uae_u16)src[x * 2] | ((uae_u16)src[x * 2 + 1] << 8); + r = (uae_u8)((((pixel >> 11) & 0x1f) * 255) / 31); + g = (uae_u8)((((pixel >> 5) & 0x3f) * 255) / 63); + b = (uae_u8)(((pixel & 0x1f) * 255) / 31); + } + dst[x * 3 + 0] = b; + dst[x * 3 + 1] = g; + dst[x * 3 + 2] = r; + } + } + *widthp = width; + *heightp = height; + return true; +} + +static void write_stream_header(FILE *f, const char type[4], const char handler[4], + uae_u32 scale, uae_u32 rate, long *length_pos, uae_u32 suggested_size, + uae_u32 sample_size, int width, int height) +{ + put_fourcc(f, "strh"); + put_u32(f, 56); + put_fourcc(f, type); + put_fourcc(f, handler); + put_u32(f, 0); + put_u16(f, 0); + put_u16(f, 0); + put_u32(f, 0); + put_u32(f, scale); + put_u32(f, rate); + put_u32(f, 0); + *length_pos = ftell(f); + put_u32(f, 0); + put_u32(f, suggested_size); + put_u32(f, 0xffffffffu); + put_u32(f, sample_size); + put_u32(f, 0); + put_u32(f, 0); + put_u32(f, width); + put_u32(f, height); +} + +static bool start_avi_if_needed(int width, int height, int frame_size) +{ + if (avi_file) { + return true; + } + if (width <= 0 || height <= 0 || frame_size <= 0) { + return false; + } + + unix_avi_prepare_filename(); + if (!unix_avi_ensure_parent_dir(avioutput_filename_inuse)) { + write_log(_T("AVIOutput: can't create output directory for '%s'\n"), avioutput_filename_inuse); + avi_failed = true; + return false; + } + + avi_file = _tfopen(avioutput_filename_inuse, _T("wb")); + if (!avi_file) { + write_log(_T("AVIOutput: can't open '%s'\n"), avioutput_filename_inuse); + avi_failed = true; + return false; + } + + avi_index.clear(); + avi_frame_count = 0; + avi_audio_bytes = 0; + avioutput_width = width; + avioutput_height = height; + avioutput_bits = 24; + + int channels = unix_avi_channels(); + int sample_rate = unix_avi_sample_rate(); + int block_align = channels * 2; + + put_fourcc(avi_file, "RIFF"); + riff_size_pos = ftell(avi_file); + put_u32(avi_file, 0); + put_fourcc(avi_file, "AVI "); + + put_fourcc(avi_file, "LIST"); + hdrl_size_pos = ftell(avi_file); + put_u32(avi_file, 0); + put_fourcc(avi_file, "hdrl"); + + put_fourcc(avi_file, "avih"); + put_u32(avi_file, 56); + put_u32(avi_file, 1000000 / std::max(1, avioutput_fps)); + put_u32(avi_file, (uae_u32)frame_size * (uae_u32)std::max(1, avioutput_fps)); + put_u32(avi_file, 0); + put_u32(avi_file, AVI_HAS_INDEX); + avih_total_frames_pos = ftell(avi_file); + put_u32(avi_file, 0); + put_u32(avi_file, 0); + put_u32(avi_file, avioutput_audio == AVIAUDIO_AVI ? 2 : 1); + put_u32(avi_file, frame_size); + put_u32(avi_file, width); + put_u32(avi_file, height); + for (int i = 0; i < 4; i++) { + put_u32(avi_file, 0); + } + + long video_list_start = ftell(avi_file); + put_fourcc(avi_file, "LIST"); + long video_list_size_pos = ftell(avi_file); + put_u32(avi_file, 0); + put_fourcc(avi_file, "strl"); + write_stream_header(avi_file, "vids", "DIB ", 1, std::max(1, avioutput_fps), + &video_length_pos, frame_size, 0, width, height); + put_fourcc(avi_file, "strf"); + put_u32(avi_file, 40); + put_u32(avi_file, width); + put_u32(avi_file, height); + put_u16(avi_file, 1); + put_u16(avi_file, 24); + put_u32(avi_file, 0); + put_u32(avi_file, frame_size); + put_u32(avi_file, 0); + put_u32(avi_file, 0); + put_u32(avi_file, 0); + put_u32(avi_file, 0); + patch_u32(avi_file, video_list_size_pos, (uae_u32)(ftell(avi_file) - video_list_start - 8)); + + if (avioutput_audio == AVIAUDIO_AVI) { + long audio_list_start = ftell(avi_file); + put_fourcc(avi_file, "LIST"); + long audio_list_size_pos = ftell(avi_file); + put_u32(avi_file, 0); + put_fourcc(avi_file, "strl"); + write_stream_header(avi_file, "auds", "\0\0\0\0", block_align, sample_rate * block_align, + &audio_length_pos, 0, block_align, 0, 0); + put_fourcc(avi_file, "strf"); + put_u32(avi_file, 16); + put_u16(avi_file, 1); + put_u16(avi_file, channels); + put_u32(avi_file, sample_rate); + put_u32(avi_file, sample_rate * block_align); + put_u16(avi_file, block_align); + put_u16(avi_file, 16); + patch_u32(avi_file, audio_list_size_pos, (uae_u32)(ftell(avi_file) - audio_list_start - 8)); + } else { + audio_length_pos = 0; + } + + patch_u32(avi_file, hdrl_size_pos, (uae_u32)(ftell(avi_file) - hdrl_size_pos - 4)); + + put_fourcc(avi_file, "LIST"); + movi_size_pos = ftell(avi_file); + put_u32(avi_file, 0); + put_fourcc(avi_file, "movi"); + movi_data_start = ftell(avi_file); + avi_header_written = true; + + write_log(_T("AVI output to '%s' started, %dx%d@%d raw video%s\n"), + avioutput_filename_inuse, width, height, avioutput_fps, + avioutput_audio == AVIAUDIO_AVI ? _T(" + PCM audio") : _T("")); + return true; +} + +static bool write_avi_chunk(const char id[4], const uae_u8 *data, size_t size, uae_u32 flags) +{ + if (!avi_file || !data) { + return false; + } + + long chunk_start = ftell(avi_file); + put_fourcc(avi_file, id); + put_u32(avi_file, (uae_u32)size); + if (size && fwrite(data, 1, size, avi_file) != size) { + avi_failed = true; + return false; + } + if (size & 1) { + fputc(0, avi_file); + } + + UnixAviIndexEntry entry; + memcpy(entry.id, id, 4); + entry.flags = flags; + entry.offset = (uae_u32)(chunk_start - movi_data_start); + entry.size = (uae_u32)size; + avi_index.push_back(entry); + return true; +} + +static void finish_avi(void) +{ + if (!avi_file) { + return; + } + + long movi_end = ftell(avi_file); + patch_u32(avi_file, movi_size_pos, (uae_u32)(movi_end - movi_size_pos - 4)); + + put_fourcc(avi_file, "idx1"); + put_u32(avi_file, (uae_u32)avi_index.size() * 16); + for (const UnixAviIndexEntry &entry : avi_index) { + put_fourcc(avi_file, entry.id); + put_u32(avi_file, entry.flags); + put_u32(avi_file, entry.offset); + put_u32(avi_file, entry.size); + } + + long end = ftell(avi_file); + patch_u32(avi_file, riff_size_pos, (uae_u32)(end - 8)); + patch_u32(avi_file, avih_total_frames_pos, avi_frame_count); + patch_u32(avi_file, video_length_pos, avi_frame_count); + if (audio_length_pos) { + int block_align = unix_avi_channels() * 2; + patch_u32(avi_file, audio_length_pos, block_align ? avi_audio_bytes / block_align : 0); + } + + fclose(avi_file); + avi_file = NULL; + avi_header_written = false; + avi_index.clear(); + write_log(_T("AVI output stopped: %d frames, %u audio bytes\n"), avi_frame_count, avi_audio_bytes); +} + +static void finish_wav(void) +{ + if (!wav_file) { + return; + } + fseek(wav_file, 0, SEEK_SET); + write_wave_header(wav_file, avi_audio_bytes); + fclose(wav_file); + wav_file = NULL; + write_log(_T("Wave output stopped: %u audio bytes\n"), avi_audio_bytes); +} + +static void start_if_requested(int monid, int width, int height, int frame_size) +{ + if (!avioutput_requested || avi_failed || avioutput_audio == AVIAUDIO_WAV) { + return; + } + avioutput_enabled = avioutput_audio || avioutput_video; + if (!avioutput_enabled) { + return; + } + if (avioutput_video && !avi_file && width > 0 && height > 0) { + aviout_monid = monid < 0 ? 0 : monid; + start_avi_if_needed(width, height, frame_size); + } +} + +void AVIOutput_GetSettings(void) +{ +} + +void AVIOutput_SetSettings(void) +{ +} + +int AVIOutput_GetAudioCodec(TCHAR *name, int len) +{ + if (name && len > 0) { + _tcsncpy(name, avioutput_audio == AVIAUDIO_WAV ? _T("Wave (internal)") : _T("PCM (internal)"), len - 1); + name[len - 1] = 0; + } + return avioutput_audio ? avioutput_audio : AVIAUDIO_AVI; +} + +int AVIOutput_ChooseAudioCodec(void *, TCHAR *name, int len) +{ + avioutput_audio = AVIAUDIO_AVI; + return AVIOutput_GetAudioCodec(name, len); +} + +int AVIOutput_GetVideoCodec(TCHAR *name, int len) +{ + if (name && len > 0) { + _tcsncpy(name, _T("DIB RGB (internal)"), len - 1); + name[len - 1] = 0; + } + return avioutput_video ? avioutput_video : 1; +} + +int AVIOutput_ChooseVideoCodec(void *, TCHAR *name, int len) +{ + avioutput_video = 1; + return AVIOutput_GetVideoCodec(name, len); +} + +void AVIOutput_RGBinfo(int, int, int, int, int, int, int, int) +{ +} + +void Screenshot_RGBinfo(int, int, int, int, int, int, int, int) +{ +} + +void AVIOutput_End(void) +{ + finish_avi(); + finish_wav(); + avioutput_enabled = 0; + avioutput_requested = 0; + avi_failed = false; + video_recording_active &= ~1; +} + +void AVIOutput_Begin(bool) +{ + if (!avioutput_audio && !avioutput_video) { + avioutput_video = 1; + } + avioutput_requested = 1; + avioutput_enabled = avioutput_audio || avioutput_video; + video_recording_active |= 1; +} + +void AVIOutput_Restart(bool) +{ + bool requested = avioutput_requested != 0; + AVIOutput_End(); + if (requested) { + AVIOutput_Begin(false); + } +} + +void AVIOutput_Release(void) +{ + AVIOutput_End(); +} + +void AVIOutput_Initialize(void) +{ +} + +void AVIOutput_Toggle(int mode, bool immediate) +{ + if (mode) { + if (!avioutput_requested) { + AVIOutput_Begin(immediate); + } + } else { + AVIOutput_End(); + } +} + +bool AVIOutput_WriteAudio(uae_u8 *sndbuffer, int sndbufsize) +{ + if (!sndbuffer || sndbufsize <= 0 || !avioutput_requested || !avioutput_audio || avi_failed) { + return false; + } + + if (avioutput_audio == AVIAUDIO_WAV) { + avioutput_enabled = 1; + video_recording_active |= 1; + if (!start_wav_if_needed()) { + return false; + } + if (fwrite(sndbuffer, 1, sndbufsize, wav_file) != (size_t)sndbufsize) { + avi_failed = true; + return false; + } + avi_audio_bytes += sndbufsize; + return true; + } + + if (!avioutput_video && !avi_file) { + start_avi_if_needed(1, 1, 4); + } + if (!avi_file || !avi_header_written) { + return true; + } + if (!write_avi_chunk("01wb", sndbuffer, sndbufsize, 0)) { + return false; + } + avi_audio_bytes += sndbufsize; + return true; +} + +bool frame_drawn(int monid) +{ + if (screenshot_multi) { + screenshot(monid, 1, 1); + if (screenshot_multi > 0) { + screenshot_multi--; + } + } + + if (!avioutput_requested || !avioutput_video || avi_failed) { + return false; + } + + std::vector frame; + int width = 0; + int height = 0; + if (!prepare_video_frame(monid, &frame, &width, &height)) { + return false; + } + start_if_requested(monid, width, height, (int)frame.size()); + if (!avi_file || !avi_header_written || monid != aviout_monid) { + return false; + } + if (!write_avi_chunk("00db", frame.data(), frame.size(), 0x10)) { + AVIOutput_End(); + return false; + } + avi_frame_count++; + return true; +} diff --git a/od-unix/avioutput.h b/od-unix/avioutput.h new file mode 100644 index 00000000..c1f57699 --- /dev/null +++ b/od-unix/avioutput.h @@ -0,0 +1,39 @@ +#ifndef UAE_UNIX_AVIOUTPUT_H +#define UAE_UNIX_AVIOUTPUT_H + +extern int avioutput_video, avioutput_audio, avioutput_enabled, avioutput_requested; + +extern int avioutput_width, avioutput_height, avioutput_bits; +extern int avioutput_framelimiter, avioutput_nosoundoutput; +extern int avioutput_nosoundsync, avioutput_originalsize; +extern int screenshot_originalsize; +extern int screenshot_paletteindexed; +extern int screenshot_clipmode; +extern int screenshot_multi; +extern int screenshotmode; + +extern TCHAR avioutput_filename_gui[MAX_DPATH]; +extern TCHAR avioutput_filename_auto[MAX_DPATH]; +extern TCHAR avioutput_filename_inuse[MAX_DPATH]; + +extern void AVIOutput_Toggle(int mode, bool immediate); +extern bool AVIOutput_WriteAudio(uae_u8 *sndbuffer, int sndbufsize); +extern int AVIOutput_ChooseAudioCodec(void *hwnd, TCHAR*, int); +extern int AVIOutput_GetAudioCodec(TCHAR*, int); +extern int AVIOutput_ChooseVideoCodec(void *hwnd, TCHAR*, int); +extern int AVIOutput_GetVideoCodec(TCHAR*, int); +extern void AVIOutput_Restart(bool); +extern void AVIOutput_End(void); +extern void AVIOutput_Begin(bool); +extern void AVIOutput_Release(void); +extern void AVIOutput_Initialize(void); +extern void AVIOutput_RGBinfo(int, int, int, int, int, int, int, int); +extern void AVIOutput_GetSettings(void); +extern void AVIOutput_SetSettings(void); + +extern void Screenshot_RGBinfo(int, int, int, int, int, int, int, int); + +#define AVIAUDIO_AVI 1 +#define AVIAUDIO_WAV 2 + +#endif /* UAE_UNIX_AVIOUTPUT_H */ diff --git a/od-unix/picasso96_unix.h b/od-unix/picasso96_unix.h new file mode 100644 index 00000000..260b3154 --- /dev/null +++ b/od-unix/picasso96_unix.h @@ -0,0 +1,78 @@ +#ifndef UAE_PICASSO96_UNIX_H +#define UAE_PICASSO96_UNIX_H + +struct picasso96_state_struct +{ + RGBFTYPE RGBFormat; /* true-colour, CLUT, hi-colour, etc. */ + struct MyCLUTEntry CLUT[2 * 256]; /* Duh! */ + uaecptr Address; /* Active screen address (Amiga-side) */ + uaecptr Extent; /* End address of screen (Amiga-side) */ + uae_u16 Width; /* Active display width (From SetGC) */ + uae_u16 VirtualWidth;/* Total screen width (From SetPanning) */ + uae_u16 BytesPerRow; /* Total screen width in bytes (From SetGC) */ + uae_u16 Height; /* Active display height (From SetGC) */ + uae_u16 VirtualHeight; /* Total screen height */ + uae_u8 GC_Depth; /* From SetGC() */ + uae_u8 GC_Flags; /* From SetGC() */ + int XOffset; /* From SetPanning() */ + int YOffset; /* From SetPanning() */ + uae_u8 SwitchState; /* From SetSwitch() - 0 is Amiga, 1 is Picasso */ + uae_u8 BytesPerPixel; + uae_u8 CardFound; + uae_u8 BigAssBitmap; + unsigned int Version; + uae_u8 *HostAddress; + int XYOffset; + bool dualclut, advDragging; + int HLineDBL, VLineDBL; + bool ModeChanged; +}; + +extern void InitPicasso96 (int monid); + +extern struct picasso96_state_struct picasso96_state[MAX_AMIGAMONITORS]; + +extern void picasso_enablescreen (int monid, int on); +extern void picasso_refresh (int monid); +extern void picasso_handle_vsync (void); +extern void picasso_allocatewritewatch (int index, int gfxmemsize); +extern int picasso_getwritewatch (int index, int offset, uae_u8 ***gwwbufp, uae_u8 **startp); +extern bool picasso_is_vram_dirty (int index, uaecptr addr, int size); +extern void picasso_invalidate (int monid, int x, int y, int w, int h); +extern void init_hz_p96 (int monid); + +/* This structure describes the UAE-side framebuffer for the Picasso + * screen. */ +struct picasso_vidbuf_description { + int width, height, depth; + int rowbytes, pixbytes, offset; + int maxwidth, maxheight; + int extra_mem; /* nonzero if there's a second buffer that must be updated */ + uae_u32 rgbformat; + uae_u32 selected_rgbformat; + uae_u32 clut[256 * 2]; + int picasso_convert[2], host_mode; + int ohost_mode, orgbformat; + int full_refresh; + int set_panning_called; + int rtg_clear_flag; + bool picasso_active; + bool picasso_changed; + uae_s16 splitypos; + uae_atomic picasso_state_change; + uae_u32 dacrgbformat[2]; +}; + +extern struct picasso_vidbuf_description picasso_vidinfo[MAX_AMIGAMONITORS]; + +extern void gfx_set_picasso_modeinfo (int monid, RGBFTYPE rgbfmt); +extern void gfx_set_picasso_colors (int monid, RGBFTYPE rgbfmt); +extern void gfx_set_picasso_baseaddr (uaecptr); +extern void gfx_set_picasso_state (int monid, int on); +extern uae_u8 *gfx_lock_picasso (int monid, bool); +extern void gfx_unlock_picasso (int monid, bool); +extern void fb_copyrow (int monid, uae_u8 *src, uae_u8 *dst, int x, int y, int width, int srcpixbytes, int dy); + +extern int p96refresh_active; + +#endif /* UAE_PICASSO96_UNIX_H */ diff --git a/od-unix/rtg.cpp b/od-unix/rtg.cpp new file mode 100644 index 00000000..ce574a41 --- /dev/null +++ b/od-unix/rtg.cpp @@ -0,0 +1,2819 @@ +#include "sysconfig.h" +#include "sysdeps.h" + +#include "options.h" +#include "memory.h" +#include "traps.h" +#include "autoconf.h" +#include "custom.h" +#include "picasso96.h" +#include "savestate.h" +#include "gfxboard.h" +#include "video.h" +#include "xwin.h" + +static uae_u32 REGPARAM3 gfxmem_lget(uaecptr) REGPARAM; +static uae_u32 REGPARAM3 gfxmem_wget(uaecptr) REGPARAM; +static uae_u32 REGPARAM3 gfxmem_bget(uaecptr) REGPARAM; +static void REGPARAM3 gfxmem_lput(uaecptr, uae_u32) REGPARAM; +static void REGPARAM3 gfxmem_wput(uaecptr, uae_u32) REGPARAM; +static void REGPARAM3 gfxmem_bput(uaecptr, uae_u32) REGPARAM; +static int REGPARAM3 gfxmem_check(uaecptr, uae_u32) REGPARAM; +static uae_u8 *REGPARAM3 gfxmem_xlate(uaecptr) REGPARAM; + +static uae_u32 REGPARAM3 gfxmem2_lget(uaecptr) REGPARAM; +static uae_u32 REGPARAM3 gfxmem2_wget(uaecptr) REGPARAM; +static uae_u32 REGPARAM3 gfxmem2_bget(uaecptr) REGPARAM; +static void REGPARAM3 gfxmem2_lput(uaecptr, uae_u32) REGPARAM; +static void REGPARAM3 gfxmem2_wput(uaecptr, uae_u32) REGPARAM; +static void REGPARAM3 gfxmem2_bput(uaecptr, uae_u32) REGPARAM; +static int REGPARAM3 gfxmem2_check(uaecptr, uae_u32) REGPARAM; +static uae_u8 *REGPARAM3 gfxmem2_xlate(uaecptr) REGPARAM; + +static uae_u32 REGPARAM3 gfxmem3_lget(uaecptr) REGPARAM; +static uae_u32 REGPARAM3 gfxmem3_wget(uaecptr) REGPARAM; +static uae_u32 REGPARAM3 gfxmem3_bget(uaecptr) REGPARAM; +static void REGPARAM3 gfxmem3_lput(uaecptr, uae_u32) REGPARAM; +static void REGPARAM3 gfxmem3_wput(uaecptr, uae_u32) REGPARAM; +static void REGPARAM3 gfxmem3_bput(uaecptr, uae_u32) REGPARAM; +static int REGPARAM3 gfxmem3_check(uaecptr, uae_u32) REGPARAM; +static uae_u8 *REGPARAM3 gfxmem3_xlate(uaecptr) REGPARAM; + +static uae_u32 REGPARAM3 gfxmem4_lget(uaecptr) REGPARAM; +static uae_u32 REGPARAM3 gfxmem4_wget(uaecptr) REGPARAM; +static uae_u32 REGPARAM3 gfxmem4_bget(uaecptr) REGPARAM; +static void REGPARAM3 gfxmem4_lput(uaecptr, uae_u32) REGPARAM; +static void REGPARAM3 gfxmem4_wput(uaecptr, uae_u32) REGPARAM; +static void REGPARAM3 gfxmem4_bput(uaecptr, uae_u32) REGPARAM; +static int REGPARAM3 gfxmem4_check(uaecptr, uae_u32) REGPARAM; +static uae_u8 *REGPARAM3 gfxmem4_xlate(uaecptr) REGPARAM; +static void unix_picasso_mark_dirty_offset(int index, uae_u32 offset, uae_u32 size); + +#define UNIX_RTG_MEMORY_FUNCTIONS(prefix, bank, index) \ +static uae_u32 REGPARAM2 prefix ## _lget(uaecptr addr) \ +{ \ + addr &= bank.mask; \ + if (!bank.baseaddr || addr + 4 > bank.allocated_size) { \ + return 0; \ + } \ + return do_get_mem_long((uae_u32 *)(bank.baseaddr + addr)); \ +} \ +static uae_u32 REGPARAM2 prefix ## _wget(uaecptr addr) \ +{ \ + addr &= bank.mask; \ + if (!bank.baseaddr || addr + 2 > bank.allocated_size) { \ + return 0; \ + } \ + return do_get_mem_word((uae_u16 *)(bank.baseaddr + addr)); \ +} \ +static uae_u32 REGPARAM2 prefix ## _bget(uaecptr addr) \ +{ \ + addr &= bank.mask; \ + if (!bank.baseaddr || addr >= bank.allocated_size) { \ + return 0; \ + } \ + return bank.baseaddr[addr]; \ +} \ +static void REGPARAM2 prefix ## _lput(uaecptr addr, uae_u32 value) \ +{ \ + addr &= bank.mask; \ + if (!bank.baseaddr || addr + 4 > bank.allocated_size) { \ + return; \ + } \ + do_put_mem_long((uae_u32 *)(bank.baseaddr + addr), value); \ + unix_picasso_mark_dirty_offset(index, addr, 4); \ +} \ +static void REGPARAM2 prefix ## _wput(uaecptr addr, uae_u32 value) \ +{ \ + addr &= bank.mask; \ + if (!bank.baseaddr || addr + 2 > bank.allocated_size) { \ + return; \ + } \ + do_put_mem_word((uae_u16 *)(bank.baseaddr + addr), value); \ + unix_picasso_mark_dirty_offset(index, addr, 2); \ +} \ +static void REGPARAM2 prefix ## _bput(uaecptr addr, uae_u32 value) \ +{ \ + addr &= bank.mask; \ + if (!bank.baseaddr || addr >= bank.allocated_size) { \ + return; \ + } \ + bank.baseaddr[addr] = (uae_u8)value; \ + unix_picasso_mark_dirty_offset(index, addr, 1); \ +} \ +static int REGPARAM2 prefix ## _check(uaecptr addr, uae_u32 size) \ +{ \ + addr &= bank.mask; \ + return bank.baseaddr && addr + size <= bank.allocated_size; \ +} \ +static uae_u8 *REGPARAM2 prefix ## _xlate(uaecptr addr) \ +{ \ + addr &= bank.mask; \ + if (!bank.baseaddr || addr >= bank.allocated_size) { \ + return NULL; \ + } \ + return bank.baseaddr + addr; \ +} + +// RTG RAM is allocated before autoconfig chooses the real Zorro address. +// mapped_malloc_dynamic() requires a non-zero start for its temporary label; +// the address is overwritten when expansion autoconfig maps the board. +addrbank gfxmem_bank = { + gfxmem_lget, gfxmem_wget, gfxmem_bget, + gfxmem_lput, gfxmem_wput, gfxmem_bput, + gfxmem_xlate, gfxmem_check, NULL, NULL, _T("RTG RAM"), + gfxmem_lget, gfxmem_wget, + ABFLAG_RAM | ABFLAG_RTG, S_READ, S_WRITE, + NULL, 0, 0, 1 +}; + +addrbank gfxmem2_bank = { + gfxmem2_lget, gfxmem2_wget, gfxmem2_bget, + gfxmem2_lput, gfxmem2_wput, gfxmem2_bput, + gfxmem2_xlate, gfxmem2_check, NULL, NULL, _T("RTG RAM #2"), + gfxmem2_lget, gfxmem2_wget, + ABFLAG_RAM | ABFLAG_RTG, S_READ, S_WRITE, + NULL, 0, 0, 1 +}; + +addrbank gfxmem3_bank = { + gfxmem3_lget, gfxmem3_wget, gfxmem3_bget, + gfxmem3_lput, gfxmem3_wput, gfxmem3_bput, + gfxmem3_xlate, gfxmem3_check, NULL, NULL, _T("RTG RAM #3"), + gfxmem3_lget, gfxmem3_wget, + ABFLAG_RAM | ABFLAG_RTG, S_READ, S_WRITE, + NULL, 0, 0, 1 +}; + +addrbank gfxmem4_bank = { + gfxmem4_lget, gfxmem4_wget, gfxmem4_bget, + gfxmem4_lput, gfxmem4_wput, gfxmem4_bput, + gfxmem4_xlate, gfxmem4_check, NULL, NULL, _T("RTG RAM #4"), + gfxmem4_lget, gfxmem4_wget, + ABFLAG_RAM | ABFLAG_RTG, S_READ, S_WRITE, + NULL, 0, 0, 1 +}; + +UNIX_RTG_MEMORY_FUNCTIONS(gfxmem, gfxmem_bank, 0) +UNIX_RTG_MEMORY_FUNCTIONS(gfxmem2, gfxmem2_bank, 1) +UNIX_RTG_MEMORY_FUNCTIONS(gfxmem3, gfxmem3_bank, 2) +UNIX_RTG_MEMORY_FUNCTIONS(gfxmem4, gfxmem4_bank, 3) + +addrbank *gfxmem_banks[MAX_RTG_BOARDS] = { + &gfxmem_bank, + &gfxmem2_bank, + &gfxmem3_bank, + &gfxmem4_bank +}; + +static const int unix_rtg_watch_page_size = 4096; +static uae_u8 *unix_rtg_dirty_pages[MAX_RTG_BOARDS]; +static uae_u8 **unix_rtg_writewatch_pages[MAX_RTG_BOARDS]; +static int unix_rtg_watch_page_count[MAX_RTG_BOARDS]; +static int unix_rtg_writewatch_count[MAX_RTG_BOARDS]; +static int unix_rtg_watch_offset[MAX_RTG_BOARDS]; + +static bool unix_picasso_valid_watch_index(int index) +{ + return index >= 0 && index < MAX_RTG_BOARDS && gfxmem_banks[index] != NULL; +} + +static void unix_picasso_mark_dirty_offset(int index, uae_u32 offset, uae_u32 size) +{ + if (!size || !unix_picasso_valid_watch_index(index) || !unix_rtg_dirty_pages[index]) { + return; + } + + addrbank *bank = gfxmem_banks[index]; + if (!bank->allocated_size || offset >= bank->allocated_size) { + return; + } + + uae_u32 end = offset + size; + if (end < offset || end > bank->allocated_size) { + end = bank->allocated_size; + } + if (end <= offset) { + return; + } + + int first_page = offset / unix_rtg_watch_page_size; + int last_page = (end - 1) / unix_rtg_watch_page_size; + if (last_page >= unix_rtg_watch_page_count[index]) { + last_page = unix_rtg_watch_page_count[index] - 1; + } + for (int page = first_page; page <= last_page; page++) { + unix_rtg_dirty_pages[index][page] = 1; + } +} + +static void unix_picasso_mark_dirty_addr(int index, uaecptr addr, uae_u32 size) +{ + if (!size || !unix_picasso_valid_watch_index(index)) { + return; + } + + addrbank *bank = gfxmem_banks[index]; + if (addr < bank->start || addr >= bank->start + bank->allocated_size) { + return; + } + unix_picasso_mark_dirty_offset(index, addr - bank->start, size); +} + +static void unix_picasso_mark_renderinfo_rect(const RenderInfo *ri, uae_u32 x, uae_u32 y, + uae_u32 width, uae_u32 height, int bytes_per_pixel) +{ + if (!ri || !width || !height || bytes_per_pixel <= 0) { + return; + } + + uae_u32 row_bytes = width * bytes_per_pixel; + int row_stride = ri->BytesPerRow > 0 ? ri->BytesPerRow : (int)row_bytes; + for (uae_u32 row = 0; row < height; row++) { + unix_picasso_mark_dirty_addr(0, + ri->AMemory + (y + row) * row_stride + x * bytes_per_pixel, + row_bytes); + } +} + +void picasso_allocatewritewatch(int index, int gfxmemsize) +{ + if (index < 0 || index >= MAX_RTG_BOARDS) { + return; + } + + xfree(unix_rtg_dirty_pages[index]); + xfree(unix_rtg_writewatch_pages[index]); + unix_rtg_dirty_pages[index] = NULL; + unix_rtg_writewatch_pages[index] = NULL; + unix_rtg_watch_page_count[index] = 0; + unix_rtg_writewatch_count[index] = 0; + unix_rtg_watch_offset[index] = 0; + + if (gfxmemsize <= 0) { + return; + } + + int pages = (gfxmemsize + unix_rtg_watch_page_size - 1) / unix_rtg_watch_page_size; + unix_rtg_dirty_pages[index] = xcalloc(uae_u8, pages); + unix_rtg_writewatch_pages[index] = xcalloc(uae_u8 *, pages); + if (!unix_rtg_dirty_pages[index] || !unix_rtg_writewatch_pages[index]) { + xfree(unix_rtg_dirty_pages[index]); + xfree(unix_rtg_writewatch_pages[index]); + unix_rtg_dirty_pages[index] = NULL; + unix_rtg_writewatch_pages[index] = NULL; + return; + } + unix_rtg_watch_page_count[index] = pages; +} + +int picasso_getwritewatch(int index, int offset, uae_u8 ***gwwbufp, uae_u8 **startp) +{ + if (gwwbufp) { + *gwwbufp = NULL; + } + if (startp) { + *startp = NULL; + } + if (!unix_picasso_valid_watch_index(index) || !unix_rtg_dirty_pages[index] || + !unix_rtg_writewatch_pages[index] || offset < 0) { + return -1; + } + + addrbank *bank = gfxmem_banks[index]; + if (!bank->baseaddr || offset >= (int)bank->allocated_size) { + unix_rtg_writewatch_count[index] = 0; + return -1; + } + + uae_u8 *start = bank->baseaddr + offset; + int first_page = offset / unix_rtg_watch_page_size; + int count = 0; + for (int page = first_page; page < unix_rtg_watch_page_count[index]; page++) { + if (!unix_rtg_dirty_pages[index][page]) { + continue; + } + uae_u32 page_offset = page * unix_rtg_watch_page_size; + if (page_offset < (uae_u32)offset) { + page_offset = offset; + } + unix_rtg_writewatch_pages[index][count++] = bank->baseaddr + page_offset; + unix_rtg_dirty_pages[index][page] = 0; + } + + unix_rtg_writewatch_count[index] = count; + unix_rtg_watch_offset[index] = offset; + if (gwwbufp) { + *gwwbufp = unix_rtg_writewatch_pages[index]; + } + if (startp) { + *startp = start; + } + return count; +} + +bool picasso_is_vram_dirty(int index, uaecptr addr, int size) +{ + if (!unix_picasso_valid_watch_index(index) || size <= 0) { + return false; + } + + addrbank *bank = gfxmem_banks[index]; + if (!bank->baseaddr || addr < bank->start) { + return false; + } + uae_u32 base_offset = addr - bank->start; + uae_u32 offset = base_offset + unix_rtg_watch_offset[index]; + if (offset < base_offset || offset >= bank->allocated_size) { + return false; + } + + uae_u8 *start = bank->baseaddr + offset; + uae_u8 *end = start + size; + for (int i = 0; i < unix_rtg_writewatch_count[index]; i++) { + uae_u8 *page = unix_rtg_writewatch_pages[index][i]; + uae_u8 *page_end = page + unix_rtg_watch_page_size; + if (start < page_end && end > page) { + return true; + } + } + return false; +} + +struct unix_rtg_board { + int id; + const TCHAR *name; + const TCHAR *manufacturer; + const TCHAR *config_name; + int config_type; +}; + +static const unix_rtg_board unix_rtg_boards[] = { + { GFXBOARD_UAE_Z2, _T("UAE [Zorro II]"), NULL, _T("ZorroII"), 2 }, + { GFXBOARD_UAE_Z3, _T("UAE [Zorro III]"), NULL, _T("ZorroIII"), 3 }, + { -1, NULL, NULL, NULL, 0 } +}; + +static const unix_rtg_board *unix_rtg_find_board(int id) +{ + for (int i = 0; unix_rtg_boards[i].name; i++) { + if (unix_rtg_boards[i].id == id) { + return &unix_rtg_boards[i]; + } + } + return NULL; +} + +enum { + UNIX_PICASSO_STATE_SETDISPLAY = 1, + UNIX_PICASSO_STATE_SETPANNING = 2, + UNIX_PICASSO_STATE_SETGC = 4, + UNIX_PICASSO_STATE_SETDAC = 8, + UNIX_PICASSO_STATE_SETSWITCH = 16, + UNIX_PICASSO_STATE_SPRITE = 32 +}; + +#define UNIX_RTG_DEFAULT_MODEFLAGS (RGBFF_CLUT | RGBFF_R5G6B5PC | RGBFF_B8G8R8A8) +#define UNIX_UAEGFX_VERSION 3 +#define UNIX_UAEGFX_REVISION 4 + +#define UNIX_LIB_SIZE 34 +#define UNIX_CARD_FLAGS UNIX_LIB_SIZE +#define UNIX_CARD_EXECBASE (UNIX_CARD_FLAGS + 2) +#define UNIX_CARD_EXPANSIONBASE (UNIX_CARD_EXECBASE + 4) +#define UNIX_CARD_SEGMENTLIST (UNIX_CARD_EXPANSIONBASE + 4) +#define UNIX_CARD_NAME (UNIX_CARD_SEGMENTLIST + 4) +#define UNIX_CARD_RESLIST (UNIX_CARD_NAME + 4) +#define UNIX_CARD_RESLISTSIZE (UNIX_CARD_RESLIST + 4) +#define UNIX_CARD_BOARDINFO (UNIX_CARD_RESLISTSIZE + 4) +#define UNIX_CARD_VBLANKIRQ (UNIX_CARD_BOARDINFO + 4) +#define UNIX_CARD_PORTSIRQ (UNIX_CARD_VBLANKIRQ + 22) +#define UNIX_CARD_IRQFLAG (UNIX_CARD_PORTSIRQ + 22) +#define UNIX_CARD_IRQPTR (UNIX_CARD_IRQFLAG + 4) +#define UNIX_CARD_IRQEXECBASE (UNIX_CARD_IRQPTR + 4) +#define UNIX_CARD_IRQCODE (UNIX_CARD_IRQEXECBASE + 4) +#define UNIX_CARD_SIZEOF (UNIX_CARD_IRQCODE + 2 * 11 * 2) + +#define UNIX_BIB_GRANTDIRECTACCESS 26 +#define UNIX_BIF_GRANTDIRECTACCESS (1 << UNIX_BIB_GRANTDIRECTACCESS) +#define UNIX_BIB_VBLANKINTERRUPT 4 +#define UNIX_BIF_VBLANKINTERRUPT (1 << UNIX_BIB_VBLANKINTERRUPT) +#define UNIX_BIB_DACSWITCH 28 +#define UNIX_BIF_DACSWITCH (1 << UNIX_BIB_DACSWITCH) + +#define UNIX_PSSO_BoardInfo_FreeCardMem (PSSO_BoardInfo_AllocCardMem + 4) +#define UNIX_PSSO_BoardInfo_SetSwitch (UNIX_PSSO_BoardInfo_FreeCardMem + 4) +#define UNIX_PSSO_BoardInfo_SetColorArray (UNIX_PSSO_BoardInfo_SetSwitch + 4) +#define UNIX_PSSO_BoardInfo_SetDAC (UNIX_PSSO_BoardInfo_SetColorArray + 4) +#define UNIX_PSSO_BoardInfo_SetGC (UNIX_PSSO_BoardInfo_SetDAC + 4) +#define UNIX_PSSO_BoardInfo_SetPanning (UNIX_PSSO_BoardInfo_SetGC + 4) +#define UNIX_PSSO_BoardInfo_CalculateBytesPerRow (UNIX_PSSO_BoardInfo_SetPanning + 4) +#define UNIX_PSSO_BoardInfo_CalculateMemory (UNIX_PSSO_BoardInfo_CalculateBytesPerRow + 4) +#define UNIX_PSSO_BoardInfo_GetCompatibleFormats (UNIX_PSSO_BoardInfo_CalculateMemory + 4) +#define UNIX_PSSO_BoardInfo_SetDisplay (UNIX_PSSO_BoardInfo_GetCompatibleFormats + 4) +#define UNIX_PSSO_BoardInfo_ResolvePixelClock (UNIX_PSSO_BoardInfo_SetDisplay + 4) +#define UNIX_PSSO_BoardInfo_GetPixelClock (UNIX_PSSO_BoardInfo_ResolvePixelClock + 4) +#define UNIX_PSSO_BoardInfo_SetClock (UNIX_PSSO_BoardInfo_GetPixelClock + 4) +#define UNIX_PSSO_BoardInfo_SetMemoryMode (UNIX_PSSO_BoardInfo_SetClock + 4) +#define UNIX_PSSO_BoardInfo_SetWriteMask (UNIX_PSSO_BoardInfo_SetMemoryMode + 4) +#define UNIX_PSSO_BoardInfo_SetClearMask (UNIX_PSSO_BoardInfo_SetWriteMask + 4) +#define UNIX_PSSO_BoardInfo_SetReadPlane (UNIX_PSSO_BoardInfo_SetClearMask + 4) +#define UNIX_PSSO_BoardInfo_WaitVerticalSync (UNIX_PSSO_BoardInfo_SetReadPlane + 4) +#define UNIX_PSSO_BoardInfo_SetInterrupt (UNIX_PSSO_BoardInfo_WaitVerticalSync + 4) +#define UNIX_PSSO_BoardInfo_WaitBlitter (UNIX_PSSO_BoardInfo_SetInterrupt + 4) +#define UNIX_PSSO_BoardInfo_ScrollPlanar (UNIX_PSSO_BoardInfo_WaitBlitter + 4) +#define UNIX_PSSO_BoardInfo_ScrollPlanarDefault (UNIX_PSSO_BoardInfo_ScrollPlanar + 4) +#define UNIX_PSSO_BoardInfo_UpdatePlanar (UNIX_PSSO_BoardInfo_ScrollPlanarDefault + 4) +#define UNIX_PSSO_BoardInfo_UpdatePlanarDefault (UNIX_PSSO_BoardInfo_UpdatePlanar + 4) +#define UNIX_PSSO_BoardInfo_BlitPlanar2Chunky (UNIX_PSSO_BoardInfo_UpdatePlanarDefault + 4) +#define UNIX_PSSO_BoardInfo_BlitPlanar2ChunkyDefault (UNIX_PSSO_BoardInfo_BlitPlanar2Chunky + 4) +#define UNIX_PSSO_BoardInfo_FillRect (UNIX_PSSO_BoardInfo_BlitPlanar2ChunkyDefault + 4) +#define UNIX_PSSO_BoardInfo_FillRectDefault (UNIX_PSSO_BoardInfo_FillRect + 4) +#define UNIX_PSSO_BoardInfo_InvertRect (UNIX_PSSO_BoardInfo_FillRectDefault + 4) +#define UNIX_PSSO_BoardInfo_InvertRectDefault (UNIX_PSSO_BoardInfo_InvertRect + 4) +#define UNIX_PSSO_BoardInfo_BlitRect (UNIX_PSSO_BoardInfo_InvertRectDefault + 4) +#define UNIX_PSSO_BoardInfo_BlitRectDefault (UNIX_PSSO_BoardInfo_BlitRect + 4) +#define UNIX_PSSO_BoardInfo_BlitTemplate (UNIX_PSSO_BoardInfo_BlitRectDefault + 4) +#define UNIX_PSSO_BoardInfo_BlitTemplateDefault (UNIX_PSSO_BoardInfo_BlitTemplate + 4) +#define UNIX_PSSO_BoardInfo_BlitPattern (UNIX_PSSO_BoardInfo_BlitTemplateDefault + 4) +#define UNIX_PSSO_BoardInfo_BlitPatternDefault (UNIX_PSSO_BoardInfo_BlitPattern + 4) +#define UNIX_PSSO_BoardInfo_DrawLine (UNIX_PSSO_BoardInfo_BlitPatternDefault + 4) +#define UNIX_PSSO_BoardInfo_DrawLineDefault (UNIX_PSSO_BoardInfo_DrawLine + 4) +#define UNIX_PSSO_BoardInfo_BlitRectNoMaskComplete (UNIX_PSSO_BoardInfo_DrawLineDefault + 4) +#define UNIX_PSSO_BoardInfo_BlitRectNoMaskCompleteDefault (UNIX_PSSO_BoardInfo_BlitRectNoMaskComplete + 4) +#define UNIX_PSSO_BoardInfo_BlitPlanar2Direct (UNIX_PSSO_BoardInfo_BlitRectNoMaskCompleteDefault + 4) +#define UNIX_PSSO_BoardInfo_BlitPlanar2DirectDefault (UNIX_PSSO_BoardInfo_BlitPlanar2Direct + 4) +#define UNIX_PSSO_BoardInfo_Reserved0 (UNIX_PSSO_BoardInfo_BlitPlanar2DirectDefault + 4) +#define UNIX_PSSO_BoardInfo_Reserved0Default (UNIX_PSSO_BoardInfo_Reserved0 + 4) +#define UNIX_PSSO_BoardInfo_Reserved1 (UNIX_PSSO_BoardInfo_Reserved0Default + 4) +#define UNIX_PSSO_SetSplitPosition (UNIX_PSSO_BoardInfo_Reserved1 + 4) +#define UNIX_PSSO_ReInitMemory (UNIX_PSSO_SetSplitPosition + 4) +#define UNIX_PSSO_BoardInfo_GetCompatibleDACFormats (UNIX_PSSO_ReInitMemory + 4) +#define UNIX_PSSO_BoardInfo_CoerceMode (UNIX_PSSO_BoardInfo_GetCompatibleDACFormats + 4) +#define UNIX_PSSO_BoardInfo_Reserved3Default (UNIX_PSSO_BoardInfo_CoerceMode + 4) +#define UNIX_PSSO_BoardInfo_Reserved4 (UNIX_PSSO_BoardInfo_Reserved3Default + 4) +#define UNIX_PSSO_BoardInfo_Reserved4Default (UNIX_PSSO_BoardInfo_Reserved4 + 4) +#define UNIX_PSSO_BoardInfo_Reserved5 (UNIX_PSSO_BoardInfo_Reserved4Default + 4) +#define UNIX_PSSO_BoardInfo_Reserved5Default (UNIX_PSSO_BoardInfo_Reserved5 + 4) +#define UNIX_PSSO_BoardInfo_SetDPMSLevel (UNIX_PSSO_BoardInfo_Reserved5Default + 4) +#define UNIX_PSSO_BoardInfo_ResetChip (UNIX_PSSO_BoardInfo_SetDPMSLevel + 4) +#define UNIX_PSSO_BoardInfo_GetFeatureAttrs (UNIX_PSSO_BoardInfo_ResetChip + 4) +#define UNIX_PSSO_BoardInfo_AllocBitMap (UNIX_PSSO_BoardInfo_GetFeatureAttrs + 4) +#define UNIX_PSSO_BoardInfo_FreeBitMap (UNIX_PSSO_BoardInfo_AllocBitMap + 4) +#define UNIX_PSSO_BoardInfo_GetBitMapAttr (UNIX_PSSO_BoardInfo_FreeBitMap + 4) +#define UNIX_PSSO_BoardInfo_SetSprite (UNIX_PSSO_BoardInfo_GetBitMapAttr + 4) +#define UNIX_PSSO_BoardInfo_SetSpritePosition (UNIX_PSSO_BoardInfo_SetSprite + 4) +#define UNIX_PSSO_BoardInfo_SetSpriteImage (UNIX_PSSO_BoardInfo_SetSpritePosition + 4) +#define UNIX_PSSO_BoardInfo_SetSpriteColor (UNIX_PSSO_BoardInfo_SetSpriteImage + 4) + +#define UNIX_RTG_CURSOR_MAXWIDTH 256 +#define UNIX_RTG_CURSOR_MAXHEIGHT 256 + +struct unix_rtg_sprite_state { + bool enabled; + bool visible; + bool image_valid; + int x; + int y; + int xoffset; + int yoffset; + int width; + int height; + uae_u8 pixels[UNIX_RTG_CURSOR_MAXWIDTH * UNIX_RTG_CURSOR_MAXHEIGHT]; + uae_u32 colors[4]; +}; + +static unix_rtg_sprite_state unix_rtg_sprites[MAX_AMIGAMONITORS]; +static uaecptr unix_uaegfx_vblankname; +static uaecptr unix_uaegfx_portsname; +static uaecptr unix_uaegfx_abi_interrupt; +static bool unix_uaegfx_interrupt_enabled; + +struct unix_rtg_mode_size { + int width; + int height; +}; + +static const unix_rtg_mode_size unix_rtg_standard_mode_sizes[] = { + { 320, 200 }, + { 320, 240 }, + { 320, 256 }, + { 640, 400 }, + { 640, 480 }, + { 640, 512 }, + { 800, 600 }, + { 1024, 768 }, + { 1280, 720 }, + { 1280, 1024 }, + { 0, 0 } +}; +static const int unix_rtg_max_mode_sizes = 128; +static unix_rtg_mode_size unix_rtg_mode_sizes[unix_rtg_max_mode_sizes]; +static int unix_rtg_mode_count; +static bool unix_rtg_mode_sizes_ready; + +static uaecptr unix_picasso_amem; +static uaecptr unix_picasso_amemend; +static uaecptr unix_uaegfx_resname; +static uaecptr unix_uaegfx_prefix; +static uaecptr unix_uaegfx_resid; +static uaecptr unix_uaegfx_base; +static uaecptr unix_uaegfx_rom; +static uaecptr unix_picasso_boardinfo; +static uae_u32 unix_p96_restored_flags; +static int unix_uaegfx_old; +static int unix_uaegfx_active; +static uae_u32 unix_reserved_gfxmem; + +enum { + UNIX_RTG_TRACE_FILLRECT = 1 << 0, + UNIX_RTG_TRACE_INVERTRECT = 1 << 1, + UNIX_RTG_TRACE_BLITRECT = 1 << 2, + UNIX_RTG_TRACE_BLITRECT_NOMASK = 1 << 3, + UNIX_RTG_TRACE_BLITTEMPLATE = 1 << 4, + UNIX_RTG_TRACE_BLITPATTERN = 1 << 5, + UNIX_RTG_TRACE_PLANAR2CHUNKY = 1 << 6, + UNIX_RTG_TRACE_PLANAR2DIRECT = 1 << 7 +}; + +static bool unix_rtg_trace_blits(void) +{ + static int enabled = -1; + if (enabled < 0) { + const char *value = getenv("WINUAE_UNIX_RTG_TRACE_BLITS"); + enabled = value && value[0] && strcmp(value, "0") != 0; + } + return enabled != 0; +} + +static void unix_rtg_trace_blit(uae_u32 bit, const TCHAR *name) +{ + static uae_u32 seen; + if (unix_rtg_trace_blits() && !(seen & bit)) { + seen |= bit; + write_log(_T("Unix RTG %s\n"), name); + } +} + +static uae_u32 unix_rtg_modeflags(void) +{ + return currprefs.picasso96_modeflags ? currprefs.picasso96_modeflags : UNIX_RTG_DEFAULT_MODEFLAGS; +} + +static void unix_rtg_add_mode_size(int width, int height) +{ + if (width <= 0 || height <= 0) { + return; + } + + int insert = 0; + while (insert < unix_rtg_mode_count) { + unix_rtg_mode_size *mode = &unix_rtg_mode_sizes[insert]; + if (mode->width == width && mode->height == height) { + return; + } + if (mode->width > width || (mode->width == width && mode->height > height)) { + break; + } + insert++; + } + if (unix_rtg_mode_count >= unix_rtg_max_mode_sizes) { + return; + } + for (int i = unix_rtg_mode_count; i > insert; i--) { + unix_rtg_mode_sizes[i] = unix_rtg_mode_sizes[i - 1]; + } + unix_rtg_mode_sizes[insert].width = width; + unix_rtg_mode_sizes[insert].height = height; + unix_rtg_mode_count++; +} + +static void unix_rtg_rebuild_mode_sizes(void) +{ + unix_rtg_mode_count = 0; + unix_rtg_mode_sizes_ready = true; + + for (int i = 0; unix_rtg_standard_mode_sizes[i].width; i++) { + unix_rtg_add_mode_size(unix_rtg_standard_mode_sizes[i].width, unix_rtg_standard_mode_sizes[i].height); + } + + struct unix_video_display_mode modes[unix_rtg_max_mode_sizes]; + int count = unix_video_get_display_modes(currprefs.gfx_apmode[APMODE_RTG].gfx_display, + modes, unix_rtg_max_mode_sizes); + for (int i = 0; i < count; i++) { + unix_rtg_add_mode_size(modes[i].width, modes[i].height); + } + + write_log(_T("Unix RTG host mode list: %d modes\n"), unix_rtg_mode_count); +} + +static void unix_rtg_ensure_mode_sizes(void) +{ + if (!unix_rtg_mode_sizes_ready) { + unix_rtg_rebuild_mode_sizes(); + } +} + +static int unix_picasso_bytes_per_pixel(uae_u32 rgbfmt) +{ + switch (rgbfmt) { + case RGBFB_CLUT: + case RGBFB_Y4U1V1: + return 1; + case RGBFB_R5G6B5: + case RGBFB_R5G5B5: + case RGBFB_R5G6B5PC: + case RGBFB_R5G5B5PC: + case RGBFB_B5G6R5PC: + case RGBFB_B5G5R5PC: + case RGBFB_Y4U2V2: + return 2; + case RGBFB_R8G8B8: + case RGBFB_B8G8R8: + return 3; + case RGBFB_A8R8G8B8: + case RGBFB_A8B8G8R8: + case RGBFB_R8G8B8A8: + case RGBFB_B8G8R8A8: + return 4; + } + return 0; +} + +static uae_u32 unix_picasso_rgbmask_for_format(uae_u32 rgbfmt) +{ + switch (rgbfmt) { + case RGBFB_CLUT: + return RGBMASK_8BIT; + case RGBFB_R5G5B5PC: + case RGBFB_R5G5B5: + case RGBFB_B5G5R5PC: + return RGBMASK_15BIT; + case RGBFB_R5G6B5PC: + case RGBFB_R5G6B5: + case RGBFB_B5G6R5PC: + return RGBMASK_16BIT; + case RGBFB_R8G8B8: + case RGBFB_B8G8R8: + return RGBMASK_24BIT; + case RGBFB_A8R8G8B8: + case RGBFB_A8B8G8R8: + case RGBFB_R8G8B8A8: + case RGBFB_B8G8R8A8: + return RGBMASK_32BIT; + } + return 0; +} + +static bool unix_picasso_renderinfo(TrapContext *ctx, uaecptr renderinfo, RenderInfo *ri) +{ + if (!ri || !trap_valid_address(ctx, renderinfo, PSSO_RenderInfo_sizeof)) { + write_log(_T("Unix RTG invalid RenderInfo: %08X\n"), renderinfo); + return false; + } + + uaecptr mem = trap_get_long(ctx, renderinfo + PSSO_RenderInfo_Memory); + int bytes_per_row = (uae_s16)trap_get_word(ctx, renderinfo + PSSO_RenderInfo_BytesPerRow); + RGBFTYPE rgbfmt = (RGBFTYPE)trap_get_long(ctx, renderinfo + PSSO_RenderInfo_RGBFormat); + + if (bytes_per_row < 0 || !trap_valid_address(ctx, mem, bytes_per_row > 0 ? bytes_per_row : 1)) { + write_log(_T("Unix RTG invalid RenderInfo memory: %08X bpr=%d fmt=%d\n"), + mem, bytes_per_row, rgbfmt); + return false; + } + + ri->AMemory = mem; + ri->Memory = get_real_address(mem); + ri->BytesPerRow = bytes_per_row; + ri->RGBFormat = rgbfmt; + return ri->Memory != NULL; +} + +static bool unix_picasso_validate_rect(RenderInfo *ri, uae_u32 rgbfmt, + uae_u32 *x, uae_u32 *y, uae_u32 *width, uae_u32 *height) +{ + if (!ri || !x || !y || !width || !height || + *x > 32767 || *y > 32767 || *width > 32767 || *height > 32767) { + return false; + } + if (!*width || !*height) { + return true; + } + + int bytes_per_pixel = unix_picasso_bytes_per_pixel(rgbfmt); + int bytes_per_row = ri->BytesPerRow; + if (!bytes_per_pixel || bytes_per_row < 0) { + return false; + } + if (!bytes_per_row) { + if (*x) { + return false; + } + bytes_per_row = *width * bytes_per_pixel; + } + if (*x * bytes_per_pixel >= (uae_u32)bytes_per_row) { + return false; + } + + uae_u32 x2 = *x + *width; + if (x2 * bytes_per_pixel > (uae_u32)bytes_per_row) { + x2 = bytes_per_row / bytes_per_pixel; + *width = x2 - *x; + } + + addrbank *bank = gfxmem_banks[0]; + if (!bank || !bank->baseaddr || ri->AMemory < bank->start || ri->AMemory >= bank->start + bank->allocated_size) { + return false; + } + uaecptr end = ri->AMemory + (*y + *height - 1) * ri->BytesPerRow + (*x + *width - 1) * bytes_per_pixel; + return end >= bank->start && end < bank->start + bank->allocated_size; +} + +static void unix_picasso_store_pen(uae_u8 *dst, uae_u32 pen, int bytes_per_pixel) +{ + switch (bytes_per_pixel) { + case 1: + dst[0] = (uae_u8)pen; + break; + case 2: + do_put_mem_word((uae_u16 *)dst, (uae_u16)pen); + break; + case 3: + dst[0] = (uae_u8)pen; + dst[1] = (uae_u8)(pen >> 8); + dst[2] = (uae_u8)(pen >> 16); + break; + case 4: + do_put_mem_long((uae_u32 *)dst, pen); + break; + } +} + +static void unix_picasso_store_pen_masked(uae_u8 *dst, uae_u32 pen, int bytes_per_pixel, uae_u8 mask) +{ + if (bytes_per_pixel == 1 && mask != 0xff) { + dst[0] = (uae_u8)((pen & mask) | (dst[0] & ~mask)); + return; + } + unix_picasso_store_pen(dst, pen, bytes_per_pixel); +} + +static uae_u8 unix_picasso_blit_op(uae_u8 src, uae_u8 dst, BLIT_OPCODE op) +{ + switch (op) { + case BLIT_FALSE: + return 0; + case BLIT_NOR: + return (uae_u8)~(src | dst); + case BLIT_ONLYDST: + return (uae_u8)(dst & ~src); + case BLIT_NOTSRC: + return (uae_u8)~src; + case BLIT_ONLYSRC: + return (uae_u8)(src & ~dst); + case BLIT_NOTDST: + return (uae_u8)~dst; + case BLIT_EOR: + return src ^ dst; + case BLIT_NAND: + return (uae_u8)~(src & dst); + case BLIT_AND: + return src & dst; + case BLIT_NEOR: + return (uae_u8)~(src ^ dst); + case BLIT_DST: + return dst; + case BLIT_NOTONLYSRC: + return (uae_u8)(~src | dst); + case BLIT_SRC: + return src; + case BLIT_NOTONLYDST: + return (uae_u8)(~dst | src); + case BLIT_OR: + return src | dst; + case BLIT_TRUE: + return 0xff; + default: + return dst; + } +} + +static uae_u32 unix_picasso_rgb_full_mask(uae_u32 rgbfmt) +{ + static const uae_u32 masks[] = { + 0x00000000, + 0xffffffff, + 0x00ffffff, + 0x00ffffff, + 0xffffffff, + 0x7fff7fff, + 0xffffff00, + 0xffffff00, + 0x00ffffff, + 0x00ffffff, + 0xffffffff, + 0xff7fff7f, + 0xffffffff, + 0x7fff7fff, + 0xffffffff, + 0xffffffff + }; + + if (rgbfmt < sizeof masks / sizeof masks[0]) { + return masks[rgbfmt]; + } + return 0xffffffff; +} + +static void unix_picasso_xor_pixel(uae_u8 *dst, int bytes_per_pixel, uae_u32 rgbfmt, uae_u8 mask) +{ + uae_u32 rgbmask = unix_picasso_rgb_full_mask(rgbfmt); + + switch (bytes_per_pixel) { + case 1: + dst[0] ^= (uae_u8)(rgbmask & mask); + break; + case 2: + do_put_mem_word((uae_u16 *)dst, do_get_mem_word((uae_u16 *)dst) ^ (uae_u16)rgbmask); + break; + case 3: + dst[0] ^= 0xff; + dst[1] ^= 0xff; + dst[2] ^= 0xff; + break; + case 4: + do_put_mem_long((uae_u32 *)dst, do_get_mem_long((uae_u32 *)dst) ^ rgbmask); + break; + } +} + +static uae_u32 unix_picasso_load_pen(const uae_u8 *src, int bytes_per_pixel) +{ + switch (bytes_per_pixel) { + case 1: + return src[0]; + case 2: + return do_get_mem_word((uae_u16 *)src); + case 3: + return src[0] | ((uae_u32)src[1] << 8) | ((uae_u32)src[2] << 16); + case 4: + return do_get_mem_long((uae_u32 *)src); + } + return 0; +} + +static uae_u32 unix_picasso_blit_op_long(uae_u32 src, uae_u32 src_inv, uae_u32 dst, uae_u32 dst_inv, + BLIT_OPCODE op) +{ + switch (op) { + case BLIT_FALSE: + return 0; + case BLIT_NOR: + return ~(src | dst); + case BLIT_ONLYDST: + return dst & src_inv; + case BLIT_NOTSRC: + return src_inv; + case BLIT_ONLYSRC: + return src & dst_inv; + case BLIT_NOTDST: + return dst_inv; + case BLIT_EOR: + return src ^ dst; + case BLIT_NAND: + return ~(src & dst); + case BLIT_AND: + return src & dst; + case BLIT_NEOR: + return ~(src ^ dst); + case BLIT_DST: + return dst; + case BLIT_NOTONLYSRC: + return src_inv | dst; + case BLIT_NOTONLYDST: + return dst_inv | src; + case BLIT_OR: + return src | dst; + case BLIT_TRUE: + return 0xffffffff; + default: + return dst; + } +} + +static int unix_picasso_depth_supported(int depth) +{ + uae_u32 flags = unix_rtg_modeflags(); + + if (depth == 8 && (flags & RGBFF_CLUT)) { + return 1; + } + if (depth == 15 && (flags & (RGBFF_R5G5B5PC | RGBFF_R5G5B5 | RGBFF_B5G5R5PC))) { + return 1; + } + if (depth == 16 && (flags & (RGBFF_R5G6B5PC | RGBFF_R5G6B5 | RGBFF_B5G6R5PC))) { + return 1; + } + if (depth == 24 && (flags & (RGBFF_R8G8B8 | RGBFF_B8G8R8))) { + return 1; + } + if (depth == 32 && (flags & (RGBFF_A8R8G8B8 | RGBFF_A8B8G8R8 | RGBFF_R8G8B8A8 | RGBFF_B8G8R8A8))) { + return 1; + } + return 0; +} + +static int unix_picasso_mode_depth_count(void) +{ + int depths = 0; + depths += unix_picasso_depth_supported(8); + depths += unix_picasso_depth_supported(15); + depths += unix_picasso_depth_supported(16); + depths += unix_picasso_depth_supported(24); + depths += unix_picasso_depth_supported(32); + return depths; +} + +static bool unix_picasso_mode_fits(int width, int height, int bytes_per_pixel) +{ + return bytes_per_pixel > 0 && + gfxmem_bank.allocated_size >= (uae_u32)width * (uae_u32)height * (uae_u32)bytes_per_pixel; +} + +static void unix_picasso_max_resolution(int mode, int *max_width, int *max_height) +{ + static const int mode_depths[MAXMODES][3] = { + { 0, 0, 0 }, + { 8, 0, 0 }, + { 15, 16, 0 }, + { 24, 0, 0 }, + { 32, 0, 0 } + }; + + *max_width = 0; + *max_height = 0; + if (mode < 0 || mode >= MAXMODES) { + return; + } + + unix_rtg_ensure_mode_sizes(); + for (int i = 0; i < unix_rtg_mode_count; i++) { + int width = unix_rtg_mode_sizes[i].width; + int height = unix_rtg_mode_sizes[i].height; + for (int depth_index = 0; mode_depths[mode][depth_index]; depth_index++) { + int depth = mode_depths[mode][depth_index]; + if (!unix_picasso_depth_supported(depth) || + !unix_picasso_mode_fits(width, height, (depth + 7) / 8)) { + continue; + } + if (width > *max_width) { + *max_width = width; + } + if (height > *max_height) { + *max_height = height; + } + } + } +} + +static int unix_picasso_resolution_count(void) +{ + int count = 0; + unix_rtg_ensure_mode_sizes(); + for (int i = 0; i < unix_rtg_mode_count; i++) { + int width = unix_rtg_mode_sizes[i].width; + int height = unix_rtg_mode_sizes[i].height; + if ((uae_u32)width * (uae_u32)height <= gfxmem_bank.allocated_size - 256) { + count++; + } + } + return count; +} + +static int unix_picasso_resolution_memory_size(void) +{ + if (currprefs.picasso96_noautomodes) { + return 0; + } + return unix_picasso_resolution_count() * + (PSSO_LibResolution_sizeof + unix_picasso_mode_depth_count() * PSSO_ModeInfo_sizeof); +} + +static int unix_picasso_mode_id(int width, int height, int index) +{ + static const struct { + int width; + int height; + int id; + } mode_ids[] = { + { 320, 240, 1 }, + { 640, 480, 3 }, + { 800, 600, 4 }, + { 1024, 768, 5 }, + { 1280, 1024, 7 }, + { 320, 256, 9 }, + { 640, 512, 10 }, + { 1280, 720, 142 }, + { 0, 0, 0 } + }; + + for (int i = 0; mode_ids[i].width; i++) { + if (mode_ids[i].width == width && mode_ids[i].height == height) { + return 0x50001000 | (mode_ids[i].id * 0x10000); + } + } + return 0x51001000 - index * 0x10000; +} + +static void unix_amiga_list_add_tail(TrapContext *ctx, uaecptr list, uaecptr node) +{ + trap_put_long(ctx, node + 0, list + 4); + trap_put_long(ctx, node + 4, trap_get_long(ctx, list + 8)); + trap_put_long(ctx, trap_get_long(ctx, list + 8), node); + trap_put_long(ctx, list + 8, node); +} + +static void unix_copy_lib_resolution(TrapContext *ctx, const struct LibResolution *res, uaecptr ptr) +{ + trap_set_bytes(ctx, ptr, 0, PSSO_LibResolution_sizeof); + for (int i = 0; i < 6; i++) { + trap_put_byte(ctx, ptr + PSSO_LibResolution_P96ID + i, res->P96ID[i]); + } + for (int i = 0; i < MAXRESOLUTIONNAMELENGTH && res->Name[i]; i++) { + trap_put_byte(ctx, ptr + PSSO_LibResolution_Name + i, res->Name[i]); + } + trap_put_long(ctx, ptr + PSSO_LibResolution_DisplayID, res->DisplayID); + trap_put_word(ctx, ptr + PSSO_LibResolution_Width, res->Width); + trap_put_word(ctx, ptr + PSSO_LibResolution_Height, res->Height); + trap_put_word(ctx, ptr + PSSO_LibResolution_Flags, res->Flags); + for (int i = 0; i < MAXMODES; i++) { + trap_put_long(ctx, ptr + PSSO_LibResolution_Modes + i * 4, res->Modes[i]); + } + trap_put_long(ctx, ptr + 10, ptr + PSSO_LibResolution_P96ID); + trap_put_long(ctx, ptr + PSSO_LibResolution_BoardInfo, res->BoardInfo); +} + +static void unix_fill_mode_info(TrapContext *ctx, uaecptr ptr, struct LibResolution *res, int width, int height, int depth) +{ + switch (depth) { + case 8: + res->Modes[CHUNKY] = ptr; + break; + case 15: + case 16: + res->Modes[HICOLOR] = ptr; + break; + case 24: + res->Modes[TRUECOLOR] = ptr; + break; + default: + res->Modes[TRUEALPHA] = ptr; + break; + } + + trap_set_bytes(ctx, ptr, 0, PSSO_ModeInfo_sizeof); + trap_put_word(ctx, ptr + PSSO_ModeInfo_Active, 1); + trap_put_word(ctx, ptr + PSSO_ModeInfo_Width, width); + trap_put_word(ctx, ptr + PSSO_ModeInfo_Height, height); + trap_put_byte(ctx, ptr + PSSO_ModeInfo_Depth, depth); + trap_put_word(ctx, ptr + PSSO_ModeInfo_HorTotal, width + 8); + trap_put_word(ctx, ptr + PSSO_ModeInfo_HorBlankSize, 8); + trap_put_word(ctx, ptr + PSSO_ModeInfo_HorSyncStart, 2); + trap_put_word(ctx, ptr + PSSO_ModeInfo_HorSyncSize, 2); + trap_put_word(ctx, ptr + PSSO_ModeInfo_VerTotal, height + 8); + trap_put_word(ctx, ptr + PSSO_ModeInfo_VerBlankSize, 8); + trap_put_word(ctx, ptr + PSSO_ModeInfo_VerSyncStart, 2); + trap_put_word(ctx, ptr + PSSO_ModeInfo_VerSyncSize, 2); + trap_put_byte(ctx, ptr + PSSO_ModeInfo_first_union, 98); + trap_put_byte(ctx, ptr + PSSO_ModeInfo_second_union, 14); + trap_put_long(ctx, ptr + PSSO_ModeInfo_PixelClock, width * height * 60); +} + +static bool unix_add_mode(TrapContext *ctx, uaecptr board_info, uaecptr *amem, int width, int height, int index) +{ + static const int depths[] = { 8, 15, 16, 24, 32 }; + struct LibResolution res = { 0 }; + bool added = false; + + memcpy(res.P96ID, "P96-0:", 6); + snprintf(res.Name, sizeof res.Name, "UAE:%4dx%4d", width, height); + res.DisplayID = unix_picasso_mode_id(width, height, index); + res.BoardInfo = board_info; + res.Width = width; + res.Height = height; + res.Flags = P96F_PUBLIC; + + for (int i = 0; i < (int)(sizeof depths / sizeof depths[0]); i++) { + int depth = depths[i]; + int bytes_per_pixel = (depth + 7) / 8; + if (!unix_picasso_depth_supported(depth)) { + continue; + } + if (!unix_picasso_mode_fits(width, height, bytes_per_pixel)) { + continue; + } + unix_fill_mode_info(ctx, *amem, &res, width, height, depth); + *amem += PSSO_ModeInfo_sizeof; + added = true; + } + + if (!added) { + return false; + } + + unix_copy_lib_resolution(ctx, &res, *amem); + unix_amiga_list_add_tail(ctx, board_info + PSSO_BoardInfo_ResolutionsList, *amem); + *amem += PSSO_LibResolution_sizeof; + write_log(_T("Unix RTG mode: %08X %dx%d\n"), res.DisplayID, width, height); + return true; +} + +static void unix_picasso_init_alloc(TrapContext *ctx, int size) +{ + unix_picasso_amem = 0; + unix_picasso_amemend = 0; + if (unix_uaegfx_base) { + size = trap_get_long(ctx, unix_uaegfx_base + UNIX_CARD_RESLISTSIZE); + unix_picasso_amem = trap_get_long(ctx, unix_uaegfx_base + UNIX_CARD_RESLIST); + } else if (unix_uaegfx_active) { + unix_reserved_gfxmem = size; + unix_picasso_amem = gfxmem_bank.start + gfxmem_bank.allocated_size - size; + } + unix_picasso_amemend = unix_picasso_amem + size; + write_log(_T("Unix RTG P96 RESINFO: %08X-%08X (%d bytes)\n"), + unix_picasso_amem, unix_picasso_amemend, size); + picasso_allocatewritewatch(0, gfxmem_bank.allocated_size); +} + +static uae_u32 REGPARAM2 unix_picasso_find_card(TrapContext *ctx) +{ + uaecptr board_info = trap_get_areg(ctx, 0); + struct picasso96_state_struct *state = &picasso96_state[currprefs.rtgboards[0].monitor_id]; + + if (!unix_uaegfx_active || !(gfxmem_bank.flags & ABFLAG_MAPPED)) { + return 0; + } + if (unix_uaegfx_base) { + trap_put_long(ctx, unix_uaegfx_base + UNIX_CARD_BOARDINFO, board_info); + } + unix_picasso_boardinfo = board_info; + + if (!gfxmem_bank.allocated_size || state->CardFound) { + return 0; + } + trap_put_long(ctx, board_info + PSSO_BoardInfo_MemoryBase, gfxmem_bank.start); + trap_put_long(ctx, board_info + PSSO_BoardInfo_MemorySize, gfxmem_bank.allocated_size - unix_reserved_gfxmem); + state->CardFound = 1; + write_log(_T("Unix RTG FindCard: boardinfo=%08X mem=%08X size=%u\n"), + board_info, gfxmem_bank.start, gfxmem_bank.allocated_size - unix_reserved_gfxmem); + return (uae_u32)-1; +} + +static uae_u32 REGPARAM2 unix_picasso_set_switch(TrapContext *ctx) +{ + int monid = currprefs.rtgboards[0].monitor_id; + struct amigadisplay *ad = &adisplays[monid]; + struct picasso96_state_struct *state = &picasso96_state[monid]; + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + bool oldstate = ad->picasso_on || ad->picasso_requested_on; + bool requested = (trap_get_dreg(ctx, 0) & 0xffff) != 0; + + if (state->SwitchState != (requested ? 1 : 0)) { + state->SwitchState = requested ? 1 : 0; + atomic_or(&vidinfo->picasso_state_change, UNIX_PICASSO_STATE_SETSWITCH); + } + ad->picasso_requested_on = requested; + write_log(_T("Unix RTG SetSwitch: %d old=%d\n"), requested ? 1 : 0, oldstate ? 1 : 0); + return oldstate ? 1 : 0; +} + +static uae_u32 REGPARAM2 unix_picasso_set_color_array(TrapContext *ctx) +{ + int monid = currprefs.rtgboards[0].monitor_id; + struct picasso96_state_struct *state = &picasso96_state[monid]; + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + uaecptr board_info = trap_get_areg(ctx, 0); + uae_u16 start = trap_get_dreg(ctx, 0); + uae_u16 count = trap_get_dreg(ctx, 1); + + if (start >= 256 || start + count > 256) { + return 0; + } + for (int i = 0; i < count; i++) { + uaecptr src = board_info + PSSO_BoardInfo_CLUT + (start + i) * 3; + state->CLUT[start + i].Red = trap_get_byte(ctx, src + 0); + state->CLUT[start + i].Green = trap_get_byte(ctx, src + 1); + state->CLUT[start + i].Blue = trap_get_byte(ctx, src + 2); + state->CLUT[start + i].Pad = 0; + vidinfo->clut[start + i] = + 0xff000000 | + ((uae_u32)state->CLUT[start + i].Red << 16) | + ((uae_u32)state->CLUT[start + i].Green << 8) | + state->CLUT[start + i].Blue; + } + vidinfo->full_refresh = 1; + return 1; +} + +static uae_u32 REGPARAM2 unix_picasso_set_dac(TrapContext *ctx) +{ + int monid = currprefs.rtgboards[0].monitor_id; + struct picasso96_state_struct *state = &picasso96_state[monid]; + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + uae_u16 index = trap_get_dreg(ctx, 0); + RGBFTYPE rgbfmt = (RGBFTYPE)trap_get_dreg(ctx, 7); + + if (state->advDragging) { + vidinfo->dacrgbformat[index ? 1 : 0] = rgbfmt; + } else { + vidinfo->dacrgbformat[0] = rgbfmt; + vidinfo->dacrgbformat[1] = rgbfmt; + } + atomic_or(&vidinfo->picasso_state_change, UNIX_PICASSO_STATE_SETDAC); + return 1; +} + +static uae_u32 REGPARAM2 unix_picasso_set_gc(TrapContext *ctx) +{ + int monid = currprefs.rtgboards[0].monitor_id; + struct picasso96_state_struct *state = &picasso96_state[monid]; + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + uaecptr board_info = trap_get_areg(ctx, 0); + uaecptr mode_info = trap_get_areg(ctx, 1); + + trap_put_long(ctx, board_info + PSSO_BoardInfo_ModeInfo, mode_info); + trap_put_word(ctx, board_info + PSSO_BoardInfo_Border, trap_get_dreg(ctx, 0)); + + uae_u16 width = trap_get_word(ctx, mode_info + PSSO_ModeInfo_Width); + if (width != state->Width) { + state->ModeChanged = true; + } + state->Width = width; + state->VirtualWidth = state->Width; + uae_u16 height = trap_get_word(ctx, mode_info + PSSO_ModeInfo_Height); + if (height != state->Height) { + state->ModeChanged = true; + } + state->Height = height; + state->VirtualHeight = state->Height; + state->GC_Depth = trap_get_byte(ctx, mode_info + PSSO_ModeInfo_Depth); + state->GC_Flags = trap_get_byte(ctx, mode_info + PSSO_ModeInfo_Flags); + state->HLineDBL = 1; + state->VLineDBL = 1; + state->HostAddress = NULL; + atomic_or(&vidinfo->picasso_state_change, UNIX_PICASSO_STATE_SETGC); + write_log(_T("Unix RTG SetGC: %dx%dx%d\n"), state->Width, state->Height, state->GC_Depth); + return 1; +} + +static void unix_picasso_set_panning_init(struct picasso96_state_struct *state) +{ + state->XYOffset = state->Address + (state->XOffset * state->BytesPerPixel) + (state->YOffset * state->BytesPerRow); + state->BigAssBitmap = state->VirtualWidth > state->Width || state->VirtualHeight > state->Height; +} + +static uae_u32 REGPARAM2 unix_picasso_set_panning(TrapContext *ctx) +{ + int monid = currprefs.rtgboards[0].monitor_id; + struct picasso96_state_struct *state = &picasso96_state[monid]; + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + uaecptr board_info = trap_get_areg(ctx, 0); + uaecptr bitmap_extra = trap_get_long(ctx, board_info + PSSO_BoardInfo_BitMapExtra); + + state->Address = trap_get_areg(ctx, 1); + state->XOffset = (uae_s16)(trap_get_dreg(ctx, 1) & 0xffff); + state->YOffset = (uae_s16)(trap_get_dreg(ctx, 2) & 0xffff); + trap_put_word(ctx, board_info + PSSO_BoardInfo_XOffset, (uae_u16)state->XOffset); + trap_put_word(ctx, board_info + PSSO_BoardInfo_YOffset, (uae_u16)state->YOffset); + + state->VirtualWidth = bitmap_extra ? trap_get_word(ctx, bitmap_extra + PSSO_BitMapExtra_Width) : trap_get_dreg(ctx, 0); + state->VirtualHeight = bitmap_extra ? trap_get_word(ctx, bitmap_extra + PSSO_BitMapExtra_Height) : state->Height; + if (!state->VirtualWidth) { + state->VirtualWidth = state->Width; + } + if (!state->VirtualHeight) { + state->VirtualHeight = state->Height; + } + state->RGBFormat = (RGBFTYPE)trap_get_dreg(ctx, 7); + state->BytesPerPixel = unix_picasso_bytes_per_pixel(state->RGBFormat); + state->BytesPerRow = state->VirtualWidth * state->BytesPerPixel; + unix_picasso_set_panning_init(state); + state->Extent = state->Address + state->BytesPerRow * state->VirtualHeight; + vidinfo->set_panning_called = 1; + + atomic_or(&vidinfo->picasso_state_change, UNIX_PICASSO_STATE_SETPANNING); + write_log(_T("Unix RTG SetPanning: addr=%08X xy=%d,%d virt=%dx%d bpr=%d fmt=%d\n"), + state->Address, state->XOffset, state->YOffset, state->VirtualWidth, state->VirtualHeight, + state->BytesPerRow, state->RGBFormat); + return 1; +} + +static uae_u32 REGPARAM2 unix_picasso_calculate_bytes_per_row(TrapContext *ctx) +{ + return (trap_get_dreg(ctx, 0) & 0xffff) * unix_picasso_bytes_per_pixel(trap_get_dreg(ctx, 7)); +} + +static uae_u32 REGPARAM2 unix_picasso_coerce_mode(TrapContext *ctx) +{ + uae_u16 board_width = trap_get_dreg(ctx, 2); + uae_u16 friend_width = trap_get_dreg(ctx, 3); + return board_width > friend_width ? board_width : friend_width; +} + +static uae_u32 REGPARAM2 unix_picasso_get_compatible_dac_formats(TrapContext *ctx) +{ + int monid = currprefs.rtgboards[0].monitor_id; + struct picasso96_state_struct *state = &picasso96_state[monid]; + uae_u32 rgbfmt = trap_get_dreg(ctx, 7); + + if (unix_picasso_rgbmask_for_format(rgbfmt)) { + state->advDragging = true; + return RGBMASK_8BIT | RGBMASK_15BIT | RGBMASK_16BIT | RGBMASK_24BIT | RGBMASK_32BIT; + } + return 0; +} + +static uae_u32 REGPARAM2 unix_picasso_fill_rect(TrapContext *ctx) +{ + unix_rtg_trace_blit(UNIX_RTG_TRACE_FILLRECT, _T("FillRect")); + + RenderInfo ri; + uaecptr renderinfo = trap_get_areg(ctx, 1); + uae_u32 x = (uae_u16)trap_get_dreg(ctx, 0); + uae_u32 y = (uae_u16)trap_get_dreg(ctx, 1); + uae_u32 width = (uae_u16)trap_get_dreg(ctx, 2); + uae_u32 height = (uae_u16)trap_get_dreg(ctx, 3); + uae_u32 pen = trap_get_dreg(ctx, 4); + uae_u8 mask = (uae_u8)trap_get_dreg(ctx, 5); + uae_u32 rgbfmt = trap_get_dreg(ctx, 7); + int bytes_per_pixel = unix_picasso_bytes_per_pixel(rgbfmt); + + if (!bytes_per_pixel || !unix_picasso_renderinfo(ctx, renderinfo, &ri)) { + return 0; + } + if (!unix_picasso_validate_rect(&ri, rgbfmt, &x, &y, &width, &height)) { + write_log(_T("Unix RTG FillRect invalid region: %08X:%d:%d (%dx%d)-(%dx%d)\n"), + ri.AMemory, ri.BytesPerRow, ri.RGBFormat, x, y, width, height); + return 1; + } + if (!width || !height) { + return 1; + } + + uae_u8 *dst = ri.Memory + y * ri.BytesPerRow + x * bytes_per_pixel; + for (uae_u32 row = 0; row < height; row++, dst += ri.BytesPerRow) { + if (bytes_per_pixel == 1 && mask != 0xff) { + for (uae_u32 col = 0; col < width; col++) { + dst[col] = (uae_u8)((pen & mask) | (dst[col] & ~mask)); + } + } else { + for (uae_u32 col = 0; col < width; col++) { + unix_picasso_store_pen(dst + col * bytes_per_pixel, pen, bytes_per_pixel); + } + } + } + unix_picasso_mark_renderinfo_rect(&ri, x, y, width, height, bytes_per_pixel); + return 1; +} + +static uae_u32 REGPARAM2 unix_picasso_invert_rect(TrapContext *ctx) +{ + unix_rtg_trace_blit(UNIX_RTG_TRACE_INVERTRECT, _T("InvertRect")); + + RenderInfo ri; + uaecptr renderinfo = trap_get_areg(ctx, 1); + uae_u32 x = (uae_u16)trap_get_dreg(ctx, 0); + uae_u32 y = (uae_u16)trap_get_dreg(ctx, 1); + uae_u32 width = (uae_u16)trap_get_dreg(ctx, 2); + uae_u32 height = (uae_u16)trap_get_dreg(ctx, 3); + uae_u8 mask = (uae_u8)trap_get_dreg(ctx, 4); + uae_u32 rgbfmt = trap_get_dreg(ctx, 7); + int bytes_per_pixel = unix_picasso_bytes_per_pixel(rgbfmt); + + if (!bytes_per_pixel || !unix_picasso_renderinfo(ctx, renderinfo, &ri)) { + return 0; + } + if (!unix_picasso_validate_rect(&ri, rgbfmt, &x, &y, &width, &height)) { + write_log(_T("Unix RTG InvertRect invalid region: %08X:%d:%d (%dx%d)-(%dx%d)\n"), + ri.AMemory, ri.BytesPerRow, ri.RGBFormat, x, y, width, height); + return 1; + } + if (!width || !height) { + return 1; + } + + if (bytes_per_pixel > 1) { + mask = 0xff; + } + if (!mask) { + return 1; + } + + uae_u32 width_in_bytes = width * bytes_per_pixel; + uae_u8 *dst = ri.Memory + y * ri.BytesPerRow + x * bytes_per_pixel; + for (uae_u32 row = 0; row < height; row++, dst += ri.BytesPerRow) { + for (uae_u32 col = 0; col < width_in_bytes; col++) { + dst[col] ^= mask; + } + } + unix_picasso_mark_renderinfo_rect(&ri, x, y, width, height, bytes_per_pixel); + return 1; +} + +static uae_u32 unix_picasso_blit_rect_common(TrapContext *ctx, uaecptr srcinfo, uaecptr dstinfo, + uae_u32 srcx, uae_u32 srcy, uae_u32 dstx, uae_u32 dsty, uae_u32 width, uae_u32 height, + uae_u8 mask, uae_u32 rgbfmt, BLIT_OPCODE opcode) +{ + RenderInfo src_ri; + RenderInfo dst_ri; + RenderInfo *dst = &src_ri; + int bytes_per_pixel = unix_picasso_bytes_per_pixel(rgbfmt); + + if (!bytes_per_pixel || !unix_picasso_renderinfo(ctx, srcinfo, &src_ri)) { + return 0; + } + if (dstinfo) { + if (!unix_picasso_renderinfo(ctx, dstinfo, &dst_ri)) { + return 0; + } + dst = &dst_ri; + } + if (bytes_per_pixel > 1 && mask != 0xff) { + return 0; + } + if (!unix_picasso_validate_rect(&src_ri, rgbfmt, &srcx, &srcy, &width, &height) || + !unix_picasso_validate_rect(dst, rgbfmt, &dstx, &dsty, &width, &height)) { + write_log(_T("Unix RTG BlitRect invalid region: %08X->%08X fmt=%d (%dx%d)\n"), + src_ri.AMemory, dst->AMemory, rgbfmt, width, height); + return 1; + } + if (!width || !height) { + return 1; + } + + uae_u32 width_in_bytes = width * bytes_per_pixel; + uae_u8 *srcbase = src_ri.Memory + srcy * src_ri.BytesPerRow + srcx * bytes_per_pixel; + uae_u8 *dstbase = dst->Memory + dsty * dst->BytesPerRow + dstx * bytes_per_pixel; + + if (opcode == BLIT_SRC && mask == 0xff) { + if (dstbase > srcbase && dstbase < srcbase + height * src_ri.BytesPerRow) { + for (uae_s32 row = height - 1; row >= 0; row--) { + memmove(dstbase + row * dst->BytesPerRow, srcbase + row * src_ri.BytesPerRow, width_in_bytes); + } + } else { + for (uae_u32 row = 0; row < height; row++) { + memmove(dstbase + row * dst->BytesPerRow, srcbase + row * src_ri.BytesPerRow, width_in_bytes); + } + } + unix_picasso_mark_renderinfo_rect(dst, dstx, dsty, width, height, bytes_per_pixel); + return 1; + } + + if (opcode < BLIT_FALSE || opcode >= BLIT_LAST) { + return 0; + } + for (uae_u32 row = 0; row < height; row++) { + uae_u8 *srcrow = srcbase + row * src_ri.BytesPerRow; + uae_u8 *dstrow = dstbase + row * dst->BytesPerRow; + for (uae_u32 col = 0; col < width_in_bytes; col++) { + uae_u8 olddst = dstrow[col]; + uae_u8 value = unix_picasso_blit_op(srcrow[col], olddst, opcode); + dstrow[col] = (uae_u8)((value & mask) | (olddst & ~mask)); + } + } + unix_picasso_mark_renderinfo_rect(dst, dstx, dsty, width, height, bytes_per_pixel); + return 1; +} + +static uae_u32 REGPARAM2 unix_picasso_blit_rect(TrapContext *ctx) +{ + unix_rtg_trace_blit(UNIX_RTG_TRACE_BLITRECT, _T("BlitRect")); + + uaecptr renderinfo = trap_get_areg(ctx, 1); + return unix_picasso_blit_rect_common(ctx, renderinfo, 0, + (uae_u16)trap_get_dreg(ctx, 0), + (uae_u16)trap_get_dreg(ctx, 1), + (uae_u16)trap_get_dreg(ctx, 2), + (uae_u16)trap_get_dreg(ctx, 3), + (uae_u16)trap_get_dreg(ctx, 4), + (uae_u16)trap_get_dreg(ctx, 5), + (uae_u8)trap_get_dreg(ctx, 6), + trap_get_dreg(ctx, 7), + BLIT_SRC); +} + +static uae_u32 REGPARAM2 unix_picasso_blit_rect_no_mask_complete(TrapContext *ctx) +{ + unix_rtg_trace_blit(UNIX_RTG_TRACE_BLITRECT_NOMASK, _T("BlitRectNoMaskComplete")); + + return unix_picasso_blit_rect_common(ctx, trap_get_areg(ctx, 1), trap_get_areg(ctx, 2), + (uae_u16)trap_get_dreg(ctx, 0), + (uae_u16)trap_get_dreg(ctx, 1), + (uae_u16)trap_get_dreg(ctx, 2), + (uae_u16)trap_get_dreg(ctx, 3), + (uae_u16)trap_get_dreg(ctx, 4), + (uae_u16)trap_get_dreg(ctx, 5), + 0xff, + trap_get_dreg(ctx, 7), + (BLIT_OPCODE)(trap_get_dreg(ctx, 6) & 0xff)); +} + +struct unix_picasso_pattern_info { + uaecptr memory; + uae_u16 xoffset; + uae_u16 yoffset; + uae_u32 fgpen; + uae_u32 bgpen; + uae_u8 size; + uae_u8 drawmode; +}; + +static bool unix_picasso_load_pattern_info(TrapContext *ctx, uaecptr pattern_info, unix_picasso_pattern_info *pattern) +{ + if (!pattern || !trap_valid_address(ctx, pattern_info, PSSO_Pattern_sizeof)) { + return false; + } + + pattern->memory = trap_get_long(ctx, pattern_info + PSSO_Pattern_Memory); + pattern->xoffset = trap_get_word(ctx, pattern_info + PSSO_Pattern_XOffset); + pattern->yoffset = trap_get_word(ctx, pattern_info + PSSO_Pattern_YOffset); + pattern->fgpen = trap_get_long(ctx, pattern_info + PSSO_Pattern_FgPen); + pattern->bgpen = trap_get_long(ctx, pattern_info + PSSO_Pattern_BgPen); + pattern->size = trap_get_byte(ctx, pattern_info + PSSO_Pattern_Size); + pattern->drawmode = trap_get_byte(ctx, pattern_info + PSSO_Pattern_DrawMode); + if (pattern->size > 16) { + return false; + } + return trap_valid_address(ctx, pattern->memory, (2u << pattern->size)); +} + +static uae_u32 REGPARAM2 unix_picasso_blit_pattern(TrapContext *ctx) +{ + unix_rtg_trace_blit(UNIX_RTG_TRACE_BLITPATTERN, _T("BlitPattern")); + + RenderInfo ri; + unix_picasso_pattern_info pattern; + uaecptr renderinfo = trap_get_areg(ctx, 1); + uaecptr pattern_info = trap_get_areg(ctx, 2); + uae_u32 x = (uae_u16)trap_get_dreg(ctx, 0); + uae_u32 y = (uae_u16)trap_get_dreg(ctx, 1); + uae_u32 width = (uae_u16)trap_get_dreg(ctx, 2); + uae_u32 height = (uae_u16)trap_get_dreg(ctx, 3); + uae_u8 mask = (uae_u8)trap_get_dreg(ctx, 4); + uae_u32 rgbfmt = trap_get_dreg(ctx, 7); + int bytes_per_pixel = unix_picasso_bytes_per_pixel(rgbfmt); + + if (!bytes_per_pixel || + !unix_picasso_renderinfo(ctx, renderinfo, &ri) || + !unix_picasso_load_pattern_info(ctx, pattern_info, &pattern)) { + return 0; + } + if (!unix_picasso_validate_rect(&ri, rgbfmt, &x, &y, &width, &height)) { + write_log(_T("Unix RTG BlitPattern invalid region: %08X:%d:%d (%dx%d)-(%dx%d)\n"), + ri.AMemory, ri.BytesPerRow, ri.RGBFormat, x, y, width, height); + return 1; + } + if (!width || !height) { + return 1; + } + + bool inverted = (pattern.drawmode & INVERS) != 0; + uae_u8 drawmode = pattern.drawmode & 3; + uae_u32 ymask = (1u << pattern.size) - 1; + uae_u8 *dst = ri.Memory + y * ri.BytesPerRow + x * bytes_per_pixel; + + for (uae_u32 row = 0; row < height; row++, dst += ri.BytesPerRow) { + uae_u32 pattern_row = (row + pattern.yoffset) & ymask; + uae_u16 data = trap_get_word(ctx, pattern.memory + pattern_row * 2); + uae_u8 *dstrow = dst; + + for (uae_u32 col = 0; col < width; col++, dstrow += bytes_per_pixel) { + uae_u32 bit_index = (col + pattern.xoffset) & 15; + bool bit_set = (data & (0x8000 >> bit_index)) != 0; + if (inverted) { + bit_set = !bit_set; + } + + switch (drawmode) { + case JAM1: + if (bit_set) { + unix_picasso_store_pen_masked(dstrow, pattern.fgpen, bytes_per_pixel, mask); + } + break; + case JAM2: + unix_picasso_store_pen_masked(dstrow, bit_set ? pattern.fgpen : pattern.bgpen, bytes_per_pixel, mask); + break; + case COMP: + if (bit_set) { + unix_picasso_xor_pixel(dstrow, bytes_per_pixel, rgbfmt, mask); + } + break; + } + } + } + unix_picasso_mark_renderinfo_rect(&ri, x, y, width, height, bytes_per_pixel); + return 1; +} + +struct unix_picasso_template_info { + uaecptr memory; + uae_s16 bytes_per_row; + uae_u8 xoffset; + uae_u8 drawmode; + uae_u32 fgpen; + uae_u32 bgpen; +}; + +static bool unix_picasso_load_template_info(TrapContext *ctx, uaecptr template_info, + unix_picasso_template_info *tmpl) +{ + if (!tmpl || !trap_valid_address(ctx, template_info, PSSO_Template_sizeof)) { + return false; + } + + tmpl->memory = trap_get_long(ctx, template_info + PSSO_Template_Memory); + tmpl->bytes_per_row = (uae_s16)trap_get_word(ctx, template_info + PSSO_Template_BytesPerRow); + tmpl->xoffset = trap_get_byte(ctx, template_info + PSSO_Template_XOffset); + tmpl->drawmode = trap_get_byte(ctx, template_info + PSSO_Template_DrawMode); + tmpl->fgpen = trap_get_long(ctx, template_info + PSSO_Template_FgPen); + tmpl->bgpen = trap_get_long(ctx, template_info + PSSO_Template_BgPen); + if (tmpl->bytes_per_row <= 0) { + return false; + } + return trap_valid_address(ctx, tmpl->memory, 1); +} + +static bool unix_picasso_validate_template_source(TrapContext *ctx, + const unix_picasso_template_info *tmpl, uae_u32 width, uae_u32 height) +{ + if (!height || !width) { + return trap_valid_address(ctx, tmpl->memory, 1); + } + + uae_u64 last_byte = (uae_u64)(height - 1) * tmpl->bytes_per_row + (tmpl->xoffset + width - 1) / 8; + return last_byte <= 0xffffffffu && trap_valid_address(ctx, tmpl->memory, (uae_u32)last_byte + 1); +} + +static uae_u32 REGPARAM2 unix_picasso_blit_template(TrapContext *ctx) +{ + unix_rtg_trace_blit(UNIX_RTG_TRACE_BLITTEMPLATE, _T("BlitTemplate")); + + RenderInfo ri; + unix_picasso_template_info tmpl; + uaecptr renderinfo = trap_get_areg(ctx, 1); + uaecptr template_info = trap_get_areg(ctx, 2); + uae_u32 x = (uae_u16)trap_get_dreg(ctx, 0); + uae_u32 y = (uae_u16)trap_get_dreg(ctx, 1); + uae_u32 width = (uae_u16)trap_get_dreg(ctx, 2); + uae_u32 height = (uae_u16)trap_get_dreg(ctx, 3); + uae_u8 mask = (uae_u8)trap_get_dreg(ctx, 4); + uae_u32 rgbfmt = trap_get_dreg(ctx, 7); + int bytes_per_pixel = unix_picasso_bytes_per_pixel(rgbfmt); + + if (!bytes_per_pixel || + !unix_picasso_renderinfo(ctx, renderinfo, &ri) || + !unix_picasso_load_template_info(ctx, template_info, &tmpl)) { + return 0; + } + if (!unix_picasso_validate_rect(&ri, rgbfmt, &x, &y, &width, &height)) { + write_log(_T("Unix RTG BlitTemplate invalid region: %08X:%d:%d (%dx%d)-(%dx%d)\n"), + ri.AMemory, ri.BytesPerRow, ri.RGBFormat, x, y, width, height); + return 1; + } + if (!width || !height) { + return 1; + } + if (!unix_picasso_validate_template_source(ctx, &tmpl, width, height)) { + return 0; + } + + bool inverted = (tmpl.drawmode & INVERS) != 0; + uae_u8 drawmode = tmpl.drawmode & 3; + uae_u8 *dst = ri.Memory + y * ri.BytesPerRow + x * bytes_per_pixel; + + for (uae_u32 row = 0; row < height; row++, dst += ri.BytesPerRow) { + uaecptr srcrow = tmpl.memory + row * tmpl.bytes_per_row; + uae_u8 *dstrow = dst; + + for (uae_u32 col = 0; col < width; col++, dstrow += bytes_per_pixel) { + uae_u32 bit_index = tmpl.xoffset + col; + uae_u8 data = trap_get_byte(ctx, srcrow + bit_index / 8); + bool bit_set = (data & (0x80 >> (bit_index & 7))) != 0; + if (inverted) { + bit_set = !bit_set; + } + + switch (drawmode) { + case JAM1: + if (bit_set) { + unix_picasso_store_pen_masked(dstrow, tmpl.fgpen, bytes_per_pixel, mask); + } + break; + case JAM2: + unix_picasso_store_pen_masked(dstrow, bit_set ? tmpl.fgpen : tmpl.bgpen, bytes_per_pixel, mask); + break; + case COMP: + if (bit_set) { + unix_picasso_xor_pixel(dstrow, bytes_per_pixel, rgbfmt, mask); + } + break; + } + } + } + unix_picasso_mark_renderinfo_rect(&ri, x, y, width, height, bytes_per_pixel); + return 1; +} + +struct unix_picasso_bitmap_info { + uae_u16 bytes_per_row; + uae_u16 rows; + uae_u8 depth; + uaecptr planes[8]; +}; + +static bool unix_picasso_load_bitmap_info(TrapContext *ctx, uaecptr bitmap_info, unix_picasso_bitmap_info *bitmap) +{ + if (!bitmap || !trap_valid_address(ctx, bitmap_info, PSSO_BitMap_sizeof)) { + return false; + } + + bitmap->bytes_per_row = trap_get_word(ctx, bitmap_info + PSSO_BitMap_BytesPerRow); + bitmap->rows = trap_get_word(ctx, bitmap_info + PSSO_BitMap_Rows); + bitmap->depth = trap_get_byte(ctx, bitmap_info + PSSO_BitMap_Depth); + if (!bitmap->bytes_per_row || !bitmap->depth || bitmap->depth > 8) { + return false; + } + for (int i = 0; i < bitmap->depth; i++) { + bitmap->planes[i] = trap_get_long(ctx, bitmap_info + PSSO_BitMap_Planes + i * 4); + } + return true; +} + +static bool unix_picasso_validate_bitmap_source(TrapContext *ctx, const unix_picasso_bitmap_info *bitmap, + uae_u32 srcx, uae_u32 srcy, uae_u32 width, uae_u32 height) +{ + if (!bitmap || !width || !height) { + return true; + } + if (srcy >= bitmap->rows || height > bitmap->rows - srcy) { + return false; + } + + uae_u64 last_byte = (uae_u64)(srcy + height - 1) * bitmap->bytes_per_row + (srcx + width - 1) / 8; + if (last_byte > 0xffffffffu) { + return false; + } + for (int i = 0; i < bitmap->depth; i++) { + uaecptr plane = bitmap->planes[i]; + if (plane != 0 && plane != 0xffffffff && !trap_valid_address(ctx, plane, (uae_u32)last_byte + 1)) { + return false; + } + } + return true; +} + +static uae_u8 unix_picasso_planar_pixel(TrapContext *ctx, const unix_picasso_bitmap_info *bitmap, + uae_u32 x, uae_u32 y) +{ + uae_u8 value = 0; + uaecptr row_offset = y * bitmap->bytes_per_row + x / 8; + uae_u8 bit = 0x80 >> (x & 7); + + for (int i = 0; i < bitmap->depth; i++) { + uaecptr plane = bitmap->planes[i]; + if (plane == 0xffffffff) { + value |= 1 << i; + } else if (plane != 0 && (trap_get_byte(ctx, plane + row_offset) & bit)) { + value |= 1 << i; + } + } + return value; +} + +static uae_u32 REGPARAM2 unix_picasso_blit_planar2chunky(TrapContext *ctx) +{ + unix_rtg_trace_blit(UNIX_RTG_TRACE_PLANAR2CHUNKY, _T("BlitPlanar2Chunky")); + + RenderInfo ri; + unix_picasso_bitmap_info bitmap; + uaecptr bitmap_info = trap_get_areg(ctx, 1); + uaecptr renderinfo = trap_get_areg(ctx, 2); + uae_u32 srcx = (uae_u16)trap_get_dreg(ctx, 0); + uae_u32 srcy = (uae_u16)trap_get_dreg(ctx, 1); + uae_u32 dstx = (uae_u16)trap_get_dreg(ctx, 2); + uae_u32 dsty = (uae_u16)trap_get_dreg(ctx, 3); + uae_u32 width = (uae_u16)trap_get_dreg(ctx, 4); + uae_u32 height = (uae_u16)trap_get_dreg(ctx, 5); + BLIT_OPCODE minterm = (BLIT_OPCODE)(trap_get_dreg(ctx, 6) & 0xff); + uae_u8 mask = (uae_u8)trap_get_dreg(ctx, 7); + + if (minterm < BLIT_FALSE || minterm >= BLIT_LAST || + !unix_picasso_renderinfo(ctx, renderinfo, &ri) || + !unix_picasso_load_bitmap_info(ctx, bitmap_info, &bitmap) || + unix_picasso_bytes_per_pixel(ri.RGBFormat) != 1) { + return 0; + } + if (!unix_picasso_validate_rect(&ri, ri.RGBFormat, &dstx, &dsty, &width, &height)) { + write_log(_T("Unix RTG BlitPlanar2Chunky invalid region: %08X:%d:%d (%dx%d)-(%dx%d)\n"), + ri.AMemory, ri.BytesPerRow, ri.RGBFormat, dstx, dsty, width, height); + return 1; + } + if (!width || !height) { + return 1; + } + if (!unix_picasso_validate_bitmap_source(ctx, &bitmap, srcx, srcy, width, height)) { + return 0; + } + + uae_u8 *dst = ri.Memory + dsty * ri.BytesPerRow + dstx; + for (uae_u32 row = 0; row < height; row++, dst += ri.BytesPerRow) { + for (uae_u32 col = 0; col < width; col++) { + uae_u8 src = unix_picasso_planar_pixel(ctx, &bitmap, srcx + col, srcy + row); + uae_u8 olddst = dst[col]; + uae_u8 value = unix_picasso_blit_op(src, olddst, minterm); + dst[col] = (uae_u8)((value & mask) | (olddst & ~mask)); + } + } + unix_picasso_mark_renderinfo_rect(&ri, dstx, dsty, width, height, 1); + return 1; +} + +static uae_u32 unix_picasso_cim_color(TrapContext *ctx, uaecptr cim, uae_u8 index) +{ + return trap_get_long(ctx, cim + 4 + index * 4); +} + +static uae_u32 REGPARAM2 unix_picasso_blit_planar2direct(TrapContext *ctx) +{ + unix_rtg_trace_blit(UNIX_RTG_TRACE_PLANAR2DIRECT, _T("BlitPlanar2Direct")); + + RenderInfo ri; + unix_picasso_bitmap_info bitmap; + uaecptr bitmap_info = trap_get_areg(ctx, 1); + uaecptr renderinfo = trap_get_areg(ctx, 2); + uaecptr cim = trap_get_areg(ctx, 3); + uae_u32 srcx = (uae_u16)trap_get_dreg(ctx, 0); + uae_u32 srcy = (uae_u16)trap_get_dreg(ctx, 1); + uae_u32 dstx = (uae_u16)trap_get_dreg(ctx, 2); + uae_u32 dsty = (uae_u16)trap_get_dreg(ctx, 3); + uae_u32 width = (uae_u16)trap_get_dreg(ctx, 4); + uae_u32 height = (uae_u16)trap_get_dreg(ctx, 5); + BLIT_OPCODE minterm = (BLIT_OPCODE)(trap_get_dreg(ctx, 6) & 0xff); + uae_u8 mask = (uae_u8)trap_get_dreg(ctx, 7); + + if (minterm < BLIT_FALSE || minterm >= BLIT_LAST || + !trap_valid_address(ctx, cim, sizeof(ColorIndexMapping)) || + !unix_picasso_renderinfo(ctx, renderinfo, &ri) || + !unix_picasso_load_bitmap_info(ctx, bitmap_info, &bitmap)) { + return 0; + } + + int bytes_per_pixel = unix_picasso_bytes_per_pixel(ri.RGBFormat); + if (bytes_per_pixel < 2) { + return 0; + } + if (!unix_picasso_validate_rect(&ri, ri.RGBFormat, &dstx, &dsty, &width, &height)) { + write_log(_T("Unix RTG BlitPlanar2Direct invalid region: %08X:%d:%d (%dx%d)-(%dx%d)\n"), + ri.AMemory, ri.BytesPerRow, ri.RGBFormat, dstx, dsty, width, height); + return 1; + } + if (!width || !height) { + return 1; + } + if (!unix_picasso_validate_bitmap_source(ctx, &bitmap, srcx, srcy, width, height)) { + return 0; + } + + uae_u8 depthmask = (1 << bitmap.depth) - 1; + uae_u32 rgbmask = unix_picasso_rgb_full_mask(ri.RGBFormat); + uae_u8 *dst = ri.Memory + dsty * ri.BytesPerRow + dstx * bytes_per_pixel; + for (uae_u32 row = 0; row < height; row++, dst += ri.BytesPerRow) { + uae_u8 *dstrow = dst; + for (uae_u32 col = 0; col < width; col++, dstrow += bytes_per_pixel) { + uae_u8 value = unix_picasso_planar_pixel(ctx, &bitmap, srcx + col, srcy + row) & depthmask; + uae_u8 inverted_value = (value ^ mask) & depthmask; + uae_u32 src = unix_picasso_cim_color(ctx, cim, value); + uae_u32 src_inv = unix_picasso_cim_color(ctx, cim, inverted_value); + uae_u32 olddst = unix_picasso_load_pen(dstrow, bytes_per_pixel); + uae_u32 out; + if (minterm == BLIT_FALSE) { + out = unix_picasso_cim_color(ctx, cim, 0); + } else if (minterm == BLIT_TRUE) { + out = unix_picasso_cim_color(ctx, cim, depthmask); + } else { + out = unix_picasso_blit_op_long(src, src_inv, olddst, olddst ^ rgbmask, minterm); + } + unix_picasso_store_pen(dstrow, out, bytes_per_pixel); + } + } + unix_picasso_mark_renderinfo_rect(&ri, dstx, dsty, width, height, bytes_per_pixel); + return 1; +} + +static void unix_picasso_reset_palette(struct picasso96_state_struct *state) +{ + for (int i = 0; i < 256 * 2; i++) { + state->CLUT[i].Pad = 0xff; + } +} + +static uae_u32 REGPARAM2 unix_picasso_set_display(TrapContext *ctx) +{ + int monid = currprefs.rtgboards[0].monitor_id; + struct picasso96_state_struct *state = &picasso96_state[monid]; + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + unix_picasso_reset_palette(state); + atomic_or(&vidinfo->picasso_state_change, UNIX_PICASSO_STATE_SETDISPLAY); + return !(trap_get_dreg(ctx, 0) != 0); +} + +static int unix_rtg_monitor_id(void) +{ + int monid = currprefs.rtgboards[0].monitor_id; + if (monid < 0 || monid >= MAX_AMIGAMONITORS) { + monid = 0; + } + return monid; +} + +static void unix_picasso_update_sprite_colors(unix_rtg_sprite_state *sprite) +{ + sprite->colors[0] &= 0x00ffffff; + for (int i = 1; i < 4; i++) { + sprite->colors[i] |= 0xff000000; + } +} + +static uae_u32 REGPARAM2 unix_picasso_set_sprite_position(TrapContext *ctx) +{ + int monid = unix_rtg_monitor_id(); + struct picasso96_state_struct *state = &picasso96_state[monid]; + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + unix_rtg_sprite_state *sprite = &unix_rtg_sprites[monid]; + uaecptr board_info = trap_get_areg(ctx, 0); + + int x = (uae_s16)trap_get_word(ctx, board_info + PSSO_BoardInfo_MouseX) - state->XOffset; + int y = (uae_s16)trap_get_word(ctx, board_info + PSSO_BoardInfo_MouseY) - state->YOffset; + if (vidinfo->splitypos >= 0) { + y += vidinfo->splitypos; + } + + sprite->x = x - sprite->xoffset; + sprite->y = y - sprite->yoffset; + atomic_or(&vidinfo->picasso_state_change, UNIX_PICASSO_STATE_SPRITE); + return sprite->enabled ? 1 : 0; +} + +static uae_u32 REGPARAM2 unix_picasso_set_sprite_color(TrapContext *ctx) +{ + int monid = unix_rtg_monitor_id(); + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + unix_rtg_sprite_state *sprite = &unix_rtg_sprites[monid]; + int index = (trap_get_dreg(ctx, 0) & 0xff) + 1; + uae_u8 red = trap_get_dreg(ctx, 1); + uae_u8 green = trap_get_dreg(ctx, 2); + uae_u8 blue = trap_get_dreg(ctx, 3); + + if (!sprite->enabled || index < 0 || index >= 4) { + return 0; + } + sprite->colors[index] = ((uae_u32)red << 16) | ((uae_u32)green << 8) | blue; + unix_picasso_update_sprite_colors(sprite); + atomic_or(&vidinfo->picasso_state_change, UNIX_PICASSO_STATE_SPRITE); + return 1; +} + +static uae_u32 unix_picasso_load_sprite_image(TrapContext *ctx, uaecptr board_info) +{ + int monid = unix_rtg_monitor_id(); + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + unix_rtg_sprite_state *sprite = &unix_rtg_sprites[monid]; + int width = trap_get_byte(ctx, board_info + PSSO_BoardInfo_MouseWidth); + int height = trap_get_byte(ctx, board_info + PSSO_BoardInfo_MouseHeight); + uae_u32 flags = trap_get_long(ctx, board_info + PSSO_BoardInfo_Flags); + int hiressprite = (flags & BIF_HIRESSPRITE) ? 2 : 1; + bool doubledsprite = (flags & BIF_BIGSPRITE) != 0; + uaecptr image = trap_get_long(ctx, board_info + PSSO_BoardInfo_MouseImage); + int datasize = 4 * hiressprite + height * 4 * hiressprite; + + sprite->image_valid = false; + sprite->width = 0; + sprite->height = 0; + sprite->xoffset = trap_get_byte(ctx, board_info + PSSO_BoardInfo_MouseXOffset); + sprite->yoffset = trap_get_byte(ctx, board_info + PSSO_BoardInfo_MouseYOffset); + unix_picasso_update_sprite_colors(sprite); + + if (!sprite->enabled) { + return 0; + } + if (width <= 0 || height <= 0 || width > UNIX_RTG_CURSOR_MAXWIDTH || + height > UNIX_RTG_CURSOR_MAXHEIGHT || image == 0 || + !trap_valid_address(ctx, image, datasize)) { + atomic_or(&vidinfo->picasso_state_change, UNIX_PICASSO_STATE_SPRITE); + return 1; + } + + memset(sprite->pixels, 0, sizeof sprite->pixels); + for (int y = 0, yy = 0; y < height; y++, yy++) { + uae_u8 *dst = sprite->pixels + y * UNIX_RTG_CURSOR_MAXWIDTH; + uae_u8 *previous = dst; + uaecptr src = image + 4 * hiressprite + yy * 4 * hiressprite; + int x = 0; + while (x < width) { + uae_u32 d1 = trap_get_long(ctx, src); + uae_u32 d2 = trap_get_long(ctx, src + 2 * hiressprite); + int maxbits = width - x; + if (maxbits > 16 * hiressprite) { + maxbits = 16 * hiressprite; + } + for (int bit = 0; bit < maxbits && x < width; bit++) { + uae_u8 color = ((d2 & 0x80000000) ? 2 : 0) + ((d1 & 0x80000000) ? 1 : 0); + d1 <<= 1; + d2 <<= 1; + *dst++ = color; + x++; + if (doubledsprite && x < width) { + *dst++ = color; + x++; + } + } + } + if (doubledsprite && y + 1 < height) { + y++; + memcpy(sprite->pixels + y * UNIX_RTG_CURSOR_MAXWIDTH, previous, width); + } + } + + sprite->width = width; + sprite->height = height; + sprite->image_valid = true; + atomic_or(&vidinfo->picasso_state_change, UNIX_PICASSO_STATE_SPRITE); + return 1; +} + +static uae_u32 REGPARAM2 unix_picasso_set_sprite_image(TrapContext *ctx) +{ + return unix_picasso_load_sprite_image(ctx, trap_get_areg(ctx, 0)); +} + +static uae_u32 REGPARAM2 unix_picasso_set_sprite(TrapContext *ctx) +{ + int monid = unix_rtg_monitor_id(); + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + unix_rtg_sprite_state *sprite = &unix_rtg_sprites[monid]; + bool activate = trap_get_dreg(ctx, 0) != 0; + + if (!sprite->enabled) { + return 0; + } + if (activate) { + unix_picasso_load_sprite_image(ctx, trap_get_areg(ctx, 0)); + sprite->visible = true; + } else { + sprite->visible = false; + } + atomic_or(&vidinfo->picasso_state_change, UNIX_PICASSO_STATE_SPRITE); + return 1; +} + +static uae_u32 REGPARAM2 unix_picasso_set_interrupt(TrapContext *ctx) +{ + unix_uaegfx_interrupt_enabled = trap_get_dreg(ctx, 0) != 0; + return unix_uaegfx_interrupt_enabled ? 1 : 0; +} + +static void unix_init_vblank_abi(TrapContext *ctx, uaecptr base, uaecptr ABI) +{ + if (!base || !ABI) { + return; + } + for (int i = 0; i < 22; i++) { + trap_put_byte(ctx, ABI + PSSO_BoardInfo_HardInterrupt + i, + trap_get_byte(ctx, base + UNIX_CARD_PORTSIRQ + i)); + } + unix_uaegfx_abi_interrupt = ABI; +} + +static void unix_init_vblank_irq(TrapContext *ctx, uaecptr base) +{ + uaecptr vblank_irq = base + UNIX_CARD_VBLANKIRQ; + uaecptr ports_irq = base + UNIX_CARD_PORTSIRQ; + uaecptr code = base + UNIX_CARD_IRQCODE; + uaecptr p = code; + + trap_put_word(ctx, vblank_irq + 8, 0x0205); + trap_put_long(ctx, vblank_irq + 10, unix_uaegfx_vblankname); + trap_put_long(ctx, vblank_irq + 14, base + UNIX_CARD_IRQFLAG); + trap_put_long(ctx, vblank_irq + 18, code); + + trap_put_word(ctx, ports_irq + 8, 0x0205); + trap_put_long(ctx, ports_irq + 10, unix_uaegfx_portsname); + trap_put_long(ctx, ports_irq + 14, base + UNIX_CARD_IRQFLAG); + trap_put_long(ctx, ports_irq + 18, code + 11 * 2); + + trap_put_long(ctx, p, 0x08910000); p += 4; // bclr #0,(a1) + trap_put_word(ctx, p, 0x670e); p += 2; // beq.s + trap_put_long(ctx, p, 0x2c690008); p += 4; // move.l 8(a1),a6 + trap_put_long(ctx, p, 0x22690004); p += 4; // move.l 4(a1),a1 + trap_put_long(ctx, p, 0x4eaeff4c); p += 4; // jsr Cause(a6) + trap_put_word(ctx, p, 0x7000); p += 2; // moveq #0,d0 + trap_put_word(ctx, p, RTS); p += 2; + + trap_put_long(ctx, p, 0x08910001); p += 4; // bclr #1,(a1) + trap_put_word(ctx, p, 0x670e); p += 2; // beq.s + trap_put_long(ctx, p, 0x2c690008); p += 4; // move.l 8(a1),a6 + trap_put_long(ctx, p, 0x22690004); p += 4; // move.l 4(a1),a1 + trap_put_long(ctx, p, 0x4eaeff4c); p += 4; // jsr Cause(a6) + trap_put_word(ctx, p, 0x7000); p += 2; // moveq #0,d0 + trap_put_word(ctx, p, RTS); + + trap_call_add_areg(ctx, 1, vblank_irq); + trap_call_add_dreg(ctx, 0, 5); + trap_call_lib(ctx, trap_get_long(ctx, 4), -168); + trap_call_add_areg(ctx, 1, ports_irq); + trap_call_add_dreg(ctx, 0, 3); + trap_call_lib(ctx, trap_get_long(ctx, 4), -168); +} + +void picasso_trigger_vblank(void) +{ + TrapContext *ctx = NULL; + if (!unix_uaegfx_abi_interrupt || !unix_uaegfx_base || !unix_uaegfx_interrupt_enabled || + !currprefs.rtg_hardwareinterrupt || currprefs.win32_rtgvblankrate == -2) { + return; + } + trap_put_long(ctx, unix_uaegfx_base + UNIX_CARD_IRQPTR, + unix_uaegfx_abi_interrupt + PSSO_BoardInfo_SoftInterrupt); + trap_put_byte(ctx, unix_uaegfx_base + UNIX_CARD_IRQFLAG, currprefs.win32_rtgvblankrate ? 2 : 1); + if (currprefs.win32_rtgvblankrate != 0) { + INTREQ(0x8000 | 0x0008); + } +} + +void unix_rtg_overlay_sprite(int monid, uae_u32 *dst, int width, int height, int rowpixels) +{ + if (monid < 0 || monid >= MAX_AMIGAMONITORS || !dst || width <= 0 || height <= 0 || + rowpixels <= 0 || !currprefs.rtg_hardwaresprite) { + return; + } + + unix_rtg_sprite_state *sprite = &unix_rtg_sprites[monid]; + if (!sprite->enabled || !sprite->visible || !sprite->image_valid || + sprite->width <= 0 || sprite->height <= 0) { + return; + } + + for (int sy = 0; sy < sprite->height; sy++) { + int dy = sprite->y + sy; + if (dy < 0 || dy >= height) { + continue; + } + const uae_u8 *src = sprite->pixels + sy * UNIX_RTG_CURSOR_MAXWIDTH; + uae_u32 *row = dst + dy * rowpixels; + for (int sx = 0; sx < sprite->width; sx++) { + int dx = sprite->x + sx; + if (dx < 0 || dx >= width) { + continue; + } + uae_u8 pen = src[sx]; + if (pen) { + row[dx] = sprite->colors[pen & 3]; + } + } + } +} + +static uae_u32 REGPARAM2 unix_picasso_default_unsupported(TrapContext *) +{ + return 0; +} + +static void unix_picasso_init_board(TrapContext *ctx, uaecptr board_info) +{ + int monid = currprefs.rtgboards[0].monitor_id; + struct picasso96_state_struct *state = &picasso96_state[monid]; + uae_u32 flags = trap_get_long(ctx, board_info + PSSO_BoardInfo_Flags); + + write_log(_T("Unix RTG mode mask: %x BI=%08x\n"), unix_rtg_modeflags(), board_info); + trap_put_word(ctx, board_info + PSSO_BoardInfo_BitsPerCannon, 8); + trap_put_word(ctx, board_info + PSSO_BoardInfo_RGBFormats, unix_rtg_modeflags()); + trap_put_long(ctx, board_info + PSSO_BoardInfo_BoardType, BT_uaegfx); + trap_put_long(ctx, board_info + PSSO_BoardInfo_GraphicsControllerType, 0); + trap_put_long(ctx, board_info + PSSO_BoardInfo_PaletteChipType, 0); + trap_put_long(ctx, board_info + PSSO_BoardInfo_BoardName, unix_uaegfx_prefix); + trap_put_long(ctx, board_info + PSSO_BoardInfo_MemoryClock, 200000000); + + for (int i = 0; i < MAXMODES; i++) { + trap_put_long(ctx, board_info + PSSO_BoardInfo_PixelClockCount + i * 4, 1); + trap_put_word(ctx, board_info + PSSO_BoardInfo_MaxHorValue + i * 2, 0x4000); + trap_put_word(ctx, board_info + PSSO_BoardInfo_MaxVerValue + i * 2, 0x4000); + } + + flags &= 0xffff0000; + flags |= BIF_BLITTER | BIF_NOMEMORYMODEMIX | BIF_INDISPLAYCHAIN | UNIX_BIF_GRANTDIRECTACCESS; + if (currprefs.rtg_dacswitch) { + flags |= UNIX_BIF_DACSWITCH; + } + if (currprefs.rtg_hardwaresprite) { + flags |= BIF_HARDWARESPRITE; + } + if (currprefs.rtg_hardwareinterrupt && !unix_uaegfx_old) { + flags |= UNIX_BIF_VBLANKINTERRUPT; + } + trap_put_long(ctx, board_info + PSSO_BoardInfo_Flags, flags); + write_log(_T("Unix RTG hardware sprite support %s\n"), + (flags & BIF_HARDWARESPRITE) ? _T("enabled") : _T("disabled")); + write_log(_T("Unix RTG vblank interrupt support %s\n"), + (flags & UNIX_BIF_VBLANKINTERRUPT) ? _T("enabled") : _T("disabled")); + + for (int mode = 0; mode < MAXMODES; mode++) { + int max_width; + int max_height; + unix_picasso_max_resolution(mode, &max_width, &max_height); + trap_put_word(ctx, board_info + PSSO_BoardInfo_MaxHorResolution + mode * 2, max_width); + trap_put_word(ctx, board_info + PSSO_BoardInfo_MaxVerResolution + mode * 2, max_height); + } + + state->CardFound = 1; + memset(&unix_rtg_sprites[monid], 0, sizeof unix_rtg_sprites[monid]); + unix_rtg_sprites[monid].enabled = currprefs.rtg_hardwaresprite; + unix_picasso_update_sprite_colors(&unix_rtg_sprites[monid]); +} + +#define UNIX_PUTABI(func) \ + do { \ + if (ABI) { \ + trap_put_long(ctx, ABI + (func), here()); \ + } \ + save_rom_absolute(ABI + (func)); \ + } while (0) + +#define UNIX_RTGCALL(func, fallback, call) \ + do { \ + UNIX_PUTABI(func); \ + dl(0x48e78000); \ + calltrap(deftrap(call)); \ + dw(0x4a80); \ + dl(0x4cdf0001); \ + dw(0x6604); \ + dw(0x2f28); \ + dw(fallback); \ + dw(RTS); \ + } while (0) + +#define UNIX_RTGCALL2(func, call) \ + do { \ + UNIX_PUTABI(func); \ + calltrap(deftrap(call)); \ + dw(RTS); \ + } while (0) + +#define UNIX_RTGCALLDEFAULT(func, fallback) \ + do { \ + UNIX_PUTABI(func); \ + dw(0x2f28); \ + dw(fallback); \ + dw(RTS); \ + } while (0) + +#define UNIX_RTGNONE(func) \ + do { \ + if (ABI) { \ + trap_put_long(ctx, ABI + (func), start); \ + } \ + save_rom_absolute(ABI + (func)); \ + } while (0) + +static void unix_init_uaegfx_funcs(TrapContext *ctx, uaecptr start, uaecptr ABI) +{ + if (unix_uaegfx_old || !ABI) { + return; + } + + org(start); + dw(RTS); + + UNIX_PUTABI(UNIX_PSSO_BoardInfo_ResolvePixelClock); + dl(0x2340002c); + dw(0x7000); + dl(0x137c0062); + dw(0x002a); + dl(0x137c000e); + dw(0x002b); + dw(RTS); + + UNIX_PUTABI(UNIX_PSSO_BoardInfo_GetPixelClock); + dw(0x203c); + dl(100227260); + dw(RTS); + + UNIX_PUTABI(UNIX_PSSO_BoardInfo_CalculateMemory); + dw(0x2009); + dw(RTS); + + UNIX_PUTABI(UNIX_PSSO_BoardInfo_GetCompatibleFormats); + dw(0x203c); + dl(RGBMASK_8BIT | RGBMASK_15BIT | RGBMASK_16BIT | RGBMASK_24BIT | RGBMASK_32BIT); + dw(RTS); + + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_CalculateBytesPerRow, unix_picasso_calculate_bytes_per_row); + UNIX_RTGNONE(UNIX_PSSO_BoardInfo_SetClock); + UNIX_RTGNONE(UNIX_PSSO_BoardInfo_SetMemoryMode); + UNIX_RTGNONE(UNIX_PSSO_BoardInfo_SetWriteMask); + UNIX_RTGNONE(UNIX_PSSO_BoardInfo_SetClearMask); + UNIX_RTGNONE(UNIX_PSSO_BoardInfo_SetReadPlane); + UNIX_RTGNONE(UNIX_PSSO_BoardInfo_WaitVerticalSync); + UNIX_RTGNONE(UNIX_PSSO_BoardInfo_WaitBlitter); + + UNIX_RTGCALL(UNIX_PSSO_BoardInfo_BlitPlanar2Direct, UNIX_PSSO_BoardInfo_BlitPlanar2DirectDefault, unix_picasso_blit_planar2direct); + UNIX_RTGCALL(UNIX_PSSO_BoardInfo_FillRect, UNIX_PSSO_BoardInfo_FillRectDefault, unix_picasso_fill_rect); + UNIX_RTGCALL(UNIX_PSSO_BoardInfo_BlitRect, UNIX_PSSO_BoardInfo_BlitRectDefault, unix_picasso_blit_rect); + UNIX_RTGCALL(UNIX_PSSO_BoardInfo_BlitPlanar2Chunky, UNIX_PSSO_BoardInfo_BlitPlanar2ChunkyDefault, unix_picasso_blit_planar2chunky); + UNIX_RTGCALL(UNIX_PSSO_BoardInfo_BlitTemplate, UNIX_PSSO_BoardInfo_BlitTemplateDefault, unix_picasso_blit_template); + UNIX_RTGCALL(UNIX_PSSO_BoardInfo_InvertRect, UNIX_PSSO_BoardInfo_InvertRectDefault, unix_picasso_invert_rect); + UNIX_RTGCALL(UNIX_PSSO_BoardInfo_BlitRectNoMaskComplete, UNIX_PSSO_BoardInfo_BlitRectNoMaskCompleteDefault, unix_picasso_blit_rect_no_mask_complete); + UNIX_RTGCALL(UNIX_PSSO_BoardInfo_BlitPattern, UNIX_PSSO_BoardInfo_BlitPatternDefault, unix_picasso_blit_pattern); + + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_SetSwitch, unix_picasso_set_switch); + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_SetColorArray, unix_picasso_set_color_array); + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_SetDAC, unix_picasso_set_dac); + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_SetGC, unix_picasso_set_gc); + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_SetPanning, unix_picasso_set_panning); + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_SetDisplay, unix_picasso_set_display); + + if (currprefs.rtg_hardwaresprite) { + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_SetSprite, unix_picasso_set_sprite); + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_SetSpritePosition, unix_picasso_set_sprite_position); + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_SetSpriteImage, unix_picasso_set_sprite_image); + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_SetSpriteColor, unix_picasso_set_sprite_color); + } + + UNIX_RTGCALLDEFAULT(UNIX_PSSO_BoardInfo_ScrollPlanar, UNIX_PSSO_BoardInfo_ScrollPlanarDefault); + UNIX_RTGCALLDEFAULT(UNIX_PSSO_BoardInfo_UpdatePlanar, UNIX_PSSO_BoardInfo_UpdatePlanarDefault); + UNIX_RTGCALLDEFAULT(UNIX_PSSO_BoardInfo_DrawLine, UNIX_PSSO_BoardInfo_DrawLineDefault); + + if (currprefs.rtg_dacswitch) { + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_GetCompatibleDACFormats, unix_picasso_get_compatible_dac_formats); + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_CoerceMode, unix_picasso_coerce_mode); + } + + if (currprefs.rtg_hardwareinterrupt) { + UNIX_RTGCALL2(UNIX_PSSO_BoardInfo_SetInterrupt, unix_picasso_set_interrupt); + unix_init_vblank_abi(ctx, unix_uaegfx_base, ABI); + } + + write_log(_T("Unix RTG uaegfx.card code: %08X-%08X BI=%08X\n"), start, here(), ABI); +} + +void restore_p96_finish(void) +{ + int monid = currprefs.rtgboards[0].monitor_id; + if (monid < 0 || monid >= MAX_AMIGAMONITORS) { + monid = 0; + } + struct amigadisplay *ad = &adisplays[monid]; + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + + if (unix_uaegfx_rom && unix_picasso_boardinfo) { + unix_init_uaegfx_funcs(NULL, unix_uaegfx_rom, unix_picasso_boardinfo); + ad->picasso_requested_on = (unix_p96_restored_flags & 1) != 0; + vidinfo->picasso_active = ad->picasso_requested_on; + atomic_or(&vidinfo->picasso_state_change, UNIX_PICASSO_STATE_SETGC | + UNIX_PICASSO_STATE_SETPANNING | UNIX_PICASSO_STATE_SETSWITCH); + set_config_changed(); + } +} + +uae_u8 *restore_p96(uae_u8 *src) +{ + int monid = currprefs.rtgboards[0].monitor_id; + if (monid < 0 || monid >= MAX_AMIGAMONITORS) { + monid = 0; + } + struct picasso96_state_struct *state = &picasso96_state[monid]; + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + + if (restore_u32() != 2) { + return src; + } + + InitPicasso96(monid); + unix_p96_restored_flags = restore_u32(); + changed_prefs.rtgboards[0].rtgmem_size = restore_u32(); + state->Address = restore_u32(); + state->RGBFormat = (RGBFTYPE)restore_u32(); + state->Width = restore_u16(); + state->Height = restore_u16(); + state->VirtualWidth = restore_u16(); + state->VirtualHeight = restore_u16(); + state->XOffset = (uae_s16)restore_u16(); + state->YOffset = (uae_s16)restore_u16(); + state->GC_Depth = restore_u8(); + state->GC_Flags = restore_u8(); + state->BytesPerRow = restore_u16(); + state->BytesPerPixel = restore_u8(); + unix_uaegfx_base = restore_u32(); + unix_uaegfx_rom = restore_u32(); + unix_picasso_boardinfo = restore_u32(); + + for (int i = 0; i < 4; i++) { + restore_u32(); + } + if (unix_p96_restored_flags & 64) { + for (int i = 0; i < 256; i++) { + state->CLUT[i].Red = restore_u8(); + state->CLUT[i].Green = restore_u8(); + state->CLUT[i].Blue = restore_u8(); + state->CLUT[i].Pad = 0; + vidinfo->clut[i] = 0xff000000 | + ((uae_u32)state->CLUT[i].Red << 16) | + ((uae_u32)state->CLUT[i].Green << 8) | + state->CLUT[i].Blue; + } + } + if (unix_p96_restored_flags & 128) { + for (int i = 0; i < 6; i++) { + restore_u32(); + } + restore_u8(); + restore_u8(); + for (int i = 0; i < 13; i++) { + restore_u16(); + } + for (int i = 0; i < 256; i++) { + restore_u8(); + restore_u8(); + restore_u8(); + } + } + + state->HostAddress = NULL; + state->HLineDBL = 1; + state->VLineDBL = 1; + unix_picasso_set_panning_init(state); + state->Extent = state->Address + state->BytesPerRow * state->VirtualHeight; + vidinfo->set_panning_called = (unix_p96_restored_flags & 4) != 0; + vidinfo->full_refresh = 1; + return src; +} + +uae_u8 *save_p96(size_t *len, uae_u8 *dstptr) +{ + int monid = currprefs.rtgboards[0].monitor_id; + if (monid < 0 || monid >= MAX_AMIGAMONITORS) { + monid = 0; + } + struct amigadisplay *ad = &adisplays[monid]; + struct picasso96_state_struct *state = &picasso96_state[monid]; + struct picasso_vidbuf_description *vidinfo = &picasso_vidinfo[monid]; + + if (len) { + *len = 0; + } + if (currprefs.rtgboards[0].rtgmem_size == 0) { + return NULL; + } + + uae_u8 *dstbak; + uae_u8 *dst; + if (dstptr) { + dstbak = dst = dstptr; + } else { + dstbak = dst = xmalloc(uae_u8, 2048); + } + + uae_u32 flags = (ad->picasso_on ? 1 : 0) | + ((state->Width && state->Height) ? 2 : 0) | + (vidinfo->set_panning_called ? 4 : 0) | + 64; + + save_u32(2); + save_u32(flags); + save_u32(currprefs.rtgboards[0].rtgmem_size); + save_u32(state->Address); + save_u32(state->RGBFormat); + save_u16(state->Width); + save_u16(state->Height); + save_u16(state->VirtualWidth); + save_u16(state->VirtualHeight); + save_u16((uae_u16)state->XOffset); + save_u16((uae_u16)state->YOffset); + save_u8(state->GC_Depth); + save_u8(state->GC_Flags); + save_u16(state->BytesPerRow); + save_u8(state->BytesPerPixel); + save_u32(unix_uaegfx_base); + save_u32(unix_uaegfx_rom); + save_u32(unix_picasso_boardinfo); + for (int i = 0; i < 4; i++) { + save_u32(0); + } + for (int i = 0; i < 256; i++) { + save_u8(state->CLUT[i].Red); + save_u8(state->CLUT[i].Green); + save_u8(state->CLUT[i].Blue); + } + + if (len) { + *len = dst - dstbak; + } + return dstbak; +} + +static uae_u32 REGPARAM2 unix_picasso_init_card(TrapContext *ctx) +{ + uaecptr board_info = trap_get_areg(ctx, 0); + uaecptr amem = unix_picasso_amem; + int count = 0; + + if (!amem) { + write_log(_T("Unix RTG InitCard without resolution memory\n")); + return 0; + } + + unix_picasso_init_board(ctx, board_info); + unix_init_uaegfx_funcs(ctx, unix_uaegfx_rom, board_info); + + unix_rtg_ensure_mode_sizes(); + for (int i = 0; i < unix_rtg_mode_count; i++) { + int width = unix_rtg_mode_sizes[i].width; + int height = unix_rtg_mode_sizes[i].height; + if ((uae_u32)width * (uae_u32)height > gfxmem_bank.allocated_size - 256) { + continue; + } + if (unix_add_mode(ctx, board_info, &amem, width, height, ++count)) { + continue; + } + } + + if (amem > unix_picasso_amemend) { + write_log(_T("Unix RTG resolution list overflow %08X > %08X\n"), amem, unix_picasso_amemend); + } + write_log(_T("Unix RTG InitCard: %d modes\n"), count); + return (uae_u32)-1; +} + +static uae_u32 REGPARAM2 unix_gfx_open(TrapContext *ctx) +{ + trap_put_word(ctx, unix_uaegfx_base + 32, trap_get_word(ctx, unix_uaegfx_base + 32) + 1); + return unix_uaegfx_base; +} + +static uae_u32 REGPARAM2 unix_gfx_close(TrapContext *ctx) +{ + trap_put_word(ctx, unix_uaegfx_base + 32, trap_get_word(ctx, unix_uaegfx_base + 32) - 1); + return 0; +} + +static uae_u32 REGPARAM2 unix_gfx_expunge(TrapContext *) +{ + return 0; +} + +static uaecptr unix_uaegfx_card_install(TrapContext *ctx, uae_u32 extrasize) +{ + uaecptr openfunc, closefunc, expungefunc, findcardfunc, initcardfunc; + uaecptr functable, datatable, exec, olda2; + + if (unix_uaegfx_old || !(gfxmem_bank.flags & ABFLAG_MAPPED)) { + return 0; + } + + exec = trap_get_long(ctx, 4); + unix_uaegfx_resid = ds(_T("UAE Graphics Card 4.0")); + unix_uaegfx_vblankname = ds(_T("UAE Graphics Card VBLANK")); + unix_uaegfx_portsname = ds(_T("UAE Graphics Card PORTS")); + + openfunc = here(); + calltrap(deftrap(unix_gfx_open)); + dw(RTS); + closefunc = here(); + calltrap(deftrap(unix_gfx_close)); + dw(RTS); + expungefunc = here(); + calltrap(deftrap(unix_gfx_expunge)); + dw(RTS); + findcardfunc = here(); + calltrap(deftrap(unix_picasso_find_card)); + dw(RTS); + initcardfunc = here(); + calltrap(deftrap(unix_picasso_init_card)); + dw(RTS); + + functable = here(); + dl(openfunc); + dl(closefunc); + dl(expungefunc); + dl(EXPANSION_nullfunc); + dl(findcardfunc); + dl(initcardfunc); + dl(0xffffffff); + + datatable = makedatatable(unix_uaegfx_resid, unix_uaegfx_resname, 0x09, -50, UNIX_UAEGFX_VERSION, UNIX_UAEGFX_REVISION); + olda2 = trap_get_areg(ctx, 2); + + trap_call_add_areg(ctx, 0, functable); + trap_call_add_areg(ctx, 1, datatable); + trap_call_add_areg(ctx, 2, 0); + trap_call_add_dreg(ctx, 0, UNIX_CARD_SIZEOF + extrasize); + trap_call_add_dreg(ctx, 1, 0); + unix_uaegfx_base = trap_call_lib(ctx, exec, -0x54); + trap_set_areg(ctx, 2, olda2); + if (!unix_uaegfx_base) { + return 0; + } + + trap_call_add_areg(ctx, 1, unix_uaegfx_base); + trap_call_lib(ctx, exec, -0x18c); + + trap_call_add_areg(ctx, 1, EXPANSION_explibname); + trap_call_add_dreg(ctx, 0, 0); + trap_put_long(ctx, unix_uaegfx_base + UNIX_CARD_EXPANSIONBASE, trap_call_lib(ctx, exec, -0x228)); + trap_put_long(ctx, unix_uaegfx_base + UNIX_CARD_EXECBASE, exec); + trap_put_long(ctx, unix_uaegfx_base + UNIX_CARD_IRQEXECBASE, exec); + trap_put_long(ctx, unix_uaegfx_base + UNIX_CARD_NAME, unix_uaegfx_resname); + trap_put_long(ctx, unix_uaegfx_base + UNIX_CARD_RESLIST, unix_uaegfx_base + UNIX_CARD_SIZEOF); + trap_put_long(ctx, unix_uaegfx_base + UNIX_CARD_RESLISTSIZE, extrasize); + + if (currprefs.rtg_hardwareinterrupt) { + unix_init_vblank_irq(ctx, unix_uaegfx_base); + } + + unix_uaegfx_active = 1; + write_log(_T("Unix uaegfx.card %d.%d init @%08X (%u bytes modes)\n"), + UNIX_UAEGFX_VERSION, UNIX_UAEGFX_REVISION, unix_uaegfx_base, extrasize); + return unix_uaegfx_base; +} + +static void unix_picasso_alloc2(TrapContext *ctx) +{ + int size = 0; + + unix_picasso_amem = 0; + unix_picasso_amemend = 0; + if (!gfxmem_bank.allocated_size) { + return; + } + unix_rtg_rebuild_mode_sizes(); + if (!currprefs.picasso96_noautomodes) { + size = unix_picasso_resolution_memory_size(); + } + unix_uaegfx_card_install(ctx, size); + unix_picasso_init_alloc(ctx, size); +} + +void picasso96_alloc(TrapContext *ctx) +{ + if (currprefs.rtgboards[0].rtgmem_type >= GFXBOARD_HARDWARE) { + return; + } + if (!currprefs.rtgboards[0].rtgmem_size) { + return; + } + unix_uaegfx_resname = ds(_T("uaegfx.card")); + unix_uaegfx_prefix = ds(_T("UAE")); + if (unix_uaegfx_old) { + return; + } + unix_picasso_alloc2(ctx); +} + +uae_u32 picasso_demux(uae_u32, TrapContext *ctx) +{ + uae_u32 num = trap_get_long(ctx, trap_get_areg(ctx, 7) + 4); + + if (unix_uaegfx_base && num >= 16 && num <= 39) { + write_log(_T("Unix RTG: obsolete Picasso96 uaelib hook ignored\n")); + return 0; + } + if (!unix_uaegfx_old) { + write_log(_T("Unix RTG: Picasso96 uaelib hook in use\n")); + unix_uaegfx_old = 1; + unix_uaegfx_active = 1; + } + + switch (num) { + case 16: + return unix_picasso_find_card(ctx); + case 18: + return unix_picasso_set_switch(ctx); + case 19: + return unix_picasso_set_color_array(ctx); + case 20: + return unix_picasso_set_dac(ctx); + case 21: + return unix_picasso_set_gc(ctx); + case 22: + return unix_picasso_set_panning(ctx); + case 23: + return unix_picasso_calculate_bytes_per_row(ctx); + case 26: + return unix_picasso_set_display(ctx); + case 29: + return unix_picasso_init_card(ctx); + case 35: + return gfxmem_bank.allocated_size ? 1 : 0; + default: + return 0; + } +} + +void uaegfx_install_code(uaecptr start) +{ + unix_uaegfx_rom = start; + org(start); +} + +#ifndef GFXBOARD +bool gfxboard_set(int, bool) +{ + return false; +} + +void gfxboard_refresh(int) {} +void gfxboard_reset_init(void) {} + +int gfxboard_get_configtype(struct rtgboardconfig *rbc) +{ + const unix_rtg_board *board = rbc ? unix_rtg_find_board(rbc->rtgmem_type) : NULL; + return board ? board->config_type : 0; +} + +int gfxboard_get_vram_min(struct rtgboardconfig *) +{ + return -1; +} + +int gfxboard_get_vram_max(struct rtgboardconfig *) +{ + return -1; +} + +uae_u32 gfxboard_get_romtype(struct rtgboardconfig *) +{ + return 0; +} + +const TCHAR *gfxboard_get_name(int id) +{ + const unix_rtg_board *board = unix_rtg_find_board(id); + return board ? board->name : NULL; +} + +const TCHAR *gfxboard_get_manufacturername(int id) +{ + const unix_rtg_board *board = unix_rtg_find_board(id); + return board ? board->manufacturer : NULL; +} + +const TCHAR *gfxboard_get_configname(int id) +{ + const unix_rtg_board *board = unix_rtg_find_board(id); + return board ? board->config_name : NULL; +} + +int gfxboard_get_index_from_id(int id) +{ + return unix_rtg_find_board(id) ? id : -1; +} + +int gfxboard_get_id_from_index(int index) +{ + return unix_rtg_find_board(index) ? index : -1; +} + +struct gfxboard_func *gfxboard_get_func(struct rtgboardconfig *) +{ + return NULL; +} + +bool gfxboard_get_switcher(struct rtgboardconfig *rbc) +{ + return rbc && unix_rtg_find_board(rbc->rtgmem_type); +} + +bool gfxboard_need_byteswap(struct rtgboardconfig *) +{ + return false; +} + +int gfxboard_get_autoconfig_size(struct rtgboardconfig *) +{ + return -1; +} + +int gfxboard_is_registers(struct rtgboardconfig *) +{ + return 0; +} + +int gfxboard_num_boards(struct rtgboardconfig *) +{ + return 1; +} + +int gfxboard_get_devnum(struct uae_prefs *, int index) +{ + return index; +} +#endif diff --git a/od-unix/screenshot.cpp b/od-unix/screenshot.cpp new file mode 100644 index 00000000..2b3c383d --- /dev/null +++ b/od-unix/screenshot.cpp @@ -0,0 +1,748 @@ +#include "sysconfig.h" +#include "sysdeps.h" + +#include "options.h" +#include "uae.h" +#include "xwin.h" +#include "avioutput.h" +#include "custom.h" +#include "fsdb.h" + +#include +#include +#include +#include + +#ifdef UAE_UNIX_WITH_SDL3 +#include +#if SDL_VERSION_ATLEAST(3, 2, 0) +#define UAE_UNIX_WITH_SDL3_CLIPBOARD_DATA 1 +#endif +#endif + +#ifdef WINUAE_UNIX_WITH_LIBPNG +#include +#endif + +extern int video_recording_active; +extern int get_custom_limits(int *pw, int *ph, int *pdx, int *pdy, int *prealh, + int *hres, int *vres); + +int screenshotmode = +#ifdef WINUAE_UNIX_WITH_LIBPNG + 1; +#else + 0; +#endif +int screenshot_originalsize = 0; +int screenshot_paletteindexed = 0; +int screenshot_clipmode = 0; +int screenshot_multi = 0; + +static int screenshot_multi_start; +static int filenumber; +static int dirnumber = 1; + +static void unix_tcslcpy(TCHAR *dst, const TCHAR *src, size_t size) +{ + if (!dst || !size) { + return; + } + if (!src) { + src = _T(""); + } + _tcsncpy(dst, src, size - 1); + dst[size - 1] = 0; +} + +static void unix_bmp_append_word(std::vector *out, uae_u16 value) +{ + out->push_back(value & 0xff); + out->push_back((value >> 8) & 0xff); +} + +static void unix_bmp_append_long(std::vector *out, uae_u32 value) +{ + out->push_back(value & 0xff); + out->push_back((value >> 8) & 0xff); + out->push_back((value >> 16) & 0xff); + out->push_back((value >> 24) & 0xff); +} + +static bool unix_ensure_directory(const TCHAR *path) +{ + TCHAR tmp[MAX_DPATH]; + + if (!path || !path[0]) { + return false; + } + if (my_existsdir(path)) { + return true; + } + + unix_tcslcpy(tmp, path, sizeof tmp / sizeof(TCHAR)); + int len = (int)_tcslen(tmp); + while (len > 1 && tmp[len - 1] == '/') { + tmp[--len] = 0; + } + + for (TCHAR *p = tmp + 1; *p; p++) { + if (*p != '/') { + continue; + } + *p = 0; + if (tmp[0] && !my_existsdir(tmp) && my_mkdir(tmp) < 0 && errno != EEXIST) { + *p = '/'; + return false; + } + *p = '/'; + } + + return my_existsdir(tmp) || my_mkdir(tmp) == 0 || errno == EEXIST; +} + +static void unix_screenshot_base_name(TCHAR *out, int out_size) +{ + const TCHAR *name = NULL; + + if (currprefs.floppyslots[0].dfxtype >= 0 && currprefs.floppyslots[0].df[0]) { + name = currprefs.floppyslots[0].df; + } else if (currprefs.cdslots[0].inuse && currprefs.cdslots[0].name[0]) { + name = currprefs.cdslots[0].name; + } + + if (name) { + getfilepart(out, out_size, name); + TCHAR *dot = _tcsrchr(out, '.'); + if (dot) { + *dot = 0; + } + } else { + unix_tcslcpy(out, _T("WinUAE"), out_size); + } + + if (!out[0]) { + unix_tcslcpy(out, _T("WinUAE"), out_size); + } + for (TCHAR *p = out; *p; p++) { + if (*p == '/' || *p == '\\' || *p == ':' || *p == '?' || *p == '*') { + *p = '_'; + } + } +} + +static bool unix_make_bmp(const struct vidbuffer *vb, std::vector *bmp) +{ + if (!bmp || !vb || !vb->bufmem || vb->outwidth <= 0 || vb->outheight <= 0 || vb->rowbytes <= 0) { + return false; + } + if (vb->pixbytes != 4 && vb->pixbytes != 2) { + write_log(_T("Unix screenshot: unsupported pixel size %d\n"), vb->pixbytes); + return false; + } + + const int width = vb->outwidth; + const int height = vb->outheight; + const int bmp_rowbytes = (width * 3 + 3) & ~3; + const uae_u32 image_size = (uae_u32)bmp_rowbytes * (uae_u32)height; + const uae_u32 file_size = 14 + 40 + image_size; + + bmp->clear(); + bmp->reserve(file_size); + unix_bmp_append_word(bmp, 0x4d42); + unix_bmp_append_long(bmp, file_size); + unix_bmp_append_word(bmp, 0); + unix_bmp_append_word(bmp, 0); + unix_bmp_append_long(bmp, 14 + 40); + unix_bmp_append_long(bmp, 40); + unix_bmp_append_long(bmp, (uae_u32)width); + unix_bmp_append_long(bmp, (uae_u32)height); + unix_bmp_append_word(bmp, 1); + unix_bmp_append_word(bmp, 24); + unix_bmp_append_long(bmp, 0); + unix_bmp_append_long(bmp, image_size); + unix_bmp_append_long(bmp, 0); + unix_bmp_append_long(bmp, 0); + unix_bmp_append_long(bmp, 0); + unix_bmp_append_long(bmp, 0); + + std::vector row((size_t)bmp_rowbytes); + for (int y = height - 1; y >= 0; y--) { + const uae_u8 *src = vb->bufmem + (size_t)y * (size_t)vb->rowbytes; + memset(row.data(), 0, row.size()); + for (int x = 0; x < width; x++) { + uae_u8 r, g, b; + if (vb->pixbytes == 4) { + const uae_u32 pixel = ((const uae_u32 *)src)[x]; + b = pixel & 0xff; + g = (pixel >> 8) & 0xff; + r = (pixel >> 16) & 0xff; + } else { + const uae_u16 pixel = (uae_u16)src[x * 2] | ((uae_u16)src[x * 2 + 1] << 8); + r = (uae_u8)((((pixel >> 11) & 0x1f) * 255) / 31); + g = (uae_u8)((((pixel >> 5) & 0x3f) * 255) / 63); + b = (uae_u8)(((pixel & 0x1f) * 255) / 31); + } + row[(size_t)x * 3 + 0] = b; + row[(size_t)x * 3 + 1] = g; + row[(size_t)x * 3 + 2] = r; + } + bmp->insert(bmp->end(), row.begin(), row.end()); + } + + return true; +} + +static bool unix_write_bmp(const TCHAR *filename, const struct vidbuffer *vb) +{ + std::vector bmp; + if (!filename || !unix_make_bmp(vb, &bmp)) { + return false; + } + + FILE *fp = _tfopen(filename, _T("wb")); + if (!fp) { + write_log(_T("Unix screenshot: can't open '%s'\n"), filename); + return false; + } + + if (fwrite(bmp.data(), 1, bmp.size(), fp) != bmp.size()) { + fclose(fp); + _tunlink(filename); + write_log(_T("Unix screenshot: failed writing '%s'\n"), filename); + return false; + } + fclose(fp); + return true; +} + +static uae_u32 unix_screenshot_get_pixel(const struct vidbuffer *vb, int x, int y); + +#ifdef WINUAE_UNIX_WITH_LIBPNG +struct UnixPngBuffer { + std::vector *bytes; +}; + +static void unix_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + UnixPngBuffer *buffer = (UnixPngBuffer *)png_get_io_ptr(png_ptr); + if (!buffer || !buffer->bytes) { + png_error(png_ptr, "missing output buffer"); + return; + } + buffer->bytes->insert(buffer->bytes->end(), data, data + length); +} + +static void unix_png_flush_data(png_structp) +{ +} + +static uae_u32 unix_rgb_key_from_pixel(const struct vidbuffer *vb, int x, int y) +{ + return unix_screenshot_get_pixel(vb, x, y) & 0x00ffffff; +} + +static bool unix_collect_png_palette(const struct vidbuffer *vb, std::vector *colors, + std::vector *indices) +{ + if (!vb || !colors || !indices || vb->outwidth <= 0 || vb->outheight <= 0) { + return false; + } + + colors->clear(); + indices->assign((size_t)vb->outwidth * (size_t)vb->outheight, 0); + + std::unordered_map palette_index; + for (int y = 0; y < vb->outheight; y++) { + for (int x = 0; x < vb->outwidth; x++) { + uae_u32 color = unix_rgb_key_from_pixel(vb, x, y); + auto it = palette_index.find(color); + if (it == palette_index.end()) { + if (colors->size() >= 256) { + colors->clear(); + indices->clear(); + return false; + } + uae_u8 index = (uae_u8)colors->size(); + colors->push_back(color); + palette_index[color] = index; + (*indices)[(size_t)y * (size_t)vb->outwidth + (size_t)x] = index; + } else { + (*indices)[(size_t)y * (size_t)vb->outwidth + (size_t)x] = it->second; + } + } + } + + return !colors->empty(); +} + +static bool unix_encode_png(std::vector *png, const struct vidbuffer *vb) +{ + if (!png || !vb || !vb->bufmem || vb->outwidth <= 0 || vb->outheight <= 0 || vb->rowbytes <= 0) { + return false; + } + if (vb->pixbytes != 4 && vb->pixbytes != 2) { + write_log(_T("Unix screenshot: unsupported pixel size %d\n"), vb->pixbytes); + return false; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + return false; + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr, NULL); + return false; + } + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + return false; + } + + const int width = vb->outwidth; + const int height = vb->outheight; + png->clear(); + UnixPngBuffer buffer = { png }; + png_set_write_fn(png_ptr, &buffer, unix_png_write_data, unix_png_flush_data); + + std::vector palette_colors; + std::vector palette_indices; + const bool use_palette = screenshot_paletteindexed && + unix_collect_png_palette(vb, &palette_colors, &palette_indices); + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, + use_palette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + if (use_palette) { + png_color palette[256]; + for (size_t i = 0; i < palette_colors.size(); i++) { + uae_u32 color = palette_colors[i]; + palette[i].red = (color >> 16) & 0xff; + palette[i].green = (color >> 8) & 0xff; + palette[i].blue = color & 0xff; + } + png_set_PLTE(png_ptr, info_ptr, palette, (int)palette_colors.size()); + } + png_write_info(png_ptr, info_ptr); + + std::vector row((size_t)width * (use_palette ? 1 : 3)); + for (int y = 0; y < height; y++) { + const uae_u8 *src = vb->bufmem + (size_t)y * (size_t)vb->rowbytes; + if (use_palette) { + memcpy(row.data(), palette_indices.data() + (size_t)y * (size_t)width, (size_t)width); + } else { + for (int x = 0; x < width; x++) { + uae_u8 r, g, b; + if (vb->pixbytes == 4) { + const uae_u32 pixel = ((const uae_u32 *)src)[x]; + b = pixel & 0xff; + g = (pixel >> 8) & 0xff; + r = (pixel >> 16) & 0xff; + } else { + const uae_u16 pixel = (uae_u16)src[x * 2] | ((uae_u16)src[x * 2 + 1] << 8); + r = (uae_u8)((((pixel >> 11) & 0x1f) * 255) / 31); + g = (uae_u8)((((pixel >> 5) & 0x3f) * 255) / 63); + b = (uae_u8)(((pixel & 0x1f) * 255) / 31); + } + row[(size_t)x * 3 + 0] = r; + row[(size_t)x * 3 + 1] = g; + row[(size_t)x * 3 + 2] = b; + } + } + png_write_row(png_ptr, row.data()); + } + + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + return true; +} + +static bool unix_write_png(const TCHAR *filename, const struct vidbuffer *vb) +{ + std::vector png; + if (!filename || !unix_encode_png(&png, vb)) { + return false; + } + + FILE *fp = _tfopen(filename, _T("wb")); + if (!fp) { + write_log(_T("Unix screenshot: can't open '%s'\n"), filename); + return false; + } + if (fwrite(png.data(), 1, png.size(), fp) != png.size()) { + fclose(fp); + _tunlink(filename); + write_log(_T("Unix screenshot: failed writing '%s'\n"), filename); + return false; + } + fclose(fp); + return true; +} +#endif + +static int unix_screenshot_clamp_multiplier(int value) +{ + if (value < 1) { + return 1; + } + if (value > 8) { + return 8; + } + return value; +} + +static uae_u32 unix_screenshot_get_pixel(const struct vidbuffer *vb, int x, int y) +{ + const uae_u8 *src = vb->bufmem + (size_t)y * (size_t)vb->rowbytes + (size_t)x * (size_t)vb->pixbytes; + + if (vb->pixbytes == 4) { + return ((const uae_u32 *)src)[0]; + } + + const uae_u16 pixel = (uae_u16)src[0] | ((uae_u16)src[1] << 8); + const uae_u8 r = (uae_u8)((((pixel >> 11) & 0x1f) * 255) / 31); + const uae_u8 g = (uae_u8)((((pixel >> 5) & 0x3f) * 255) / 63); + const uae_u8 b = (uae_u8)(((pixel & 0x1f) * 255) / 31); + return (uae_u32)b | ((uae_u32)g << 8) | ((uae_u32)r << 16); +} + +static bool unix_prepare_screenshot_buffer(const struct vidbuffer *src, struct vidbuffer *dst, + std::vector &storage, bool standard) +{ + if (!src || !dst || !src->bufmem || src->outwidth <= 0 || src->outheight <= 0 || src->rowbytes <= 0) { + return false; + } + if (src->pixbytes != 4 && src->pixbytes != 2) { + write_log(_T("Unix screenshot: unsupported pixel size %d\n"), src->pixbytes); + return false; + } + + int width = standard || currprefs.screenshot_width <= 0 ? src->outwidth : currprefs.screenshot_width; + int height = standard || currprefs.screenshot_height <= 0 ? src->outheight : currprefs.screenshot_height; + if (width <= 0 || height <= 0) { + return false; + } + + int xoffset = standard ? -1 : currprefs.screenshot_xoffset; + int yoffset = standard ? -1 : currprefs.screenshot_yoffset; + + if (!standard && screenshot_originalsize && screenshot_clipmode == 1) { + int cw, ch, cx, cy, crealh = 0, hres, vres; + if (get_custom_limits(&cw, &ch, &cx, &cy, &crealh, &hres, &vres)) { + int maxw = currprefs.screenshot_max_width << currprefs.gfx_resolution; + int maxh = currprefs.screenshot_max_height << currprefs.gfx_vresolution; + int minw = currprefs.screenshot_min_width << currprefs.gfx_resolution; + int minh = currprefs.screenshot_min_height << currprefs.gfx_vresolution; + if (minw > (maxhpos_display + 1) << currprefs.gfx_resolution) { + minw = (maxhpos_display + 1) << currprefs.gfx_resolution; + } + if (minh > (maxvsize_display + 1) << currprefs.gfx_vresolution) { + minh = (maxvsize_display + 1) << currprefs.gfx_vresolution; + } + if (maxw < minw) { + maxw = minw; + } + if (maxh < minh) { + maxh = minh; + } + width = cw; + height = ch; + xoffset = cx; + yoffset = cy; + if (width < minw && minw > 0) { + xoffset -= (minw - width) / 2; + width = minw; + } + if (height < minh && minh > 0) { + yoffset -= (minh - height) / 2; + height = minh; + } + if (width > maxw && maxw > 0) { + xoffset += (width - maxw) / 2; + width = maxw; + } + if (height > maxh && maxh > 0) { + yoffset += (height - maxh) / 2; + height = maxh; + } + } + } + + int xmult = standard ? 1 : unix_screenshot_clamp_multiplier(currprefs.screenshot_xmult + 1); + int ymult = standard ? 1 : unix_screenshot_clamp_multiplier(currprefs.screenshot_ymult + 1); + while (!standard && currprefs.screenshot_output_width > width * xmult && xmult < 8) { + xmult++; + } + while (!standard && currprefs.screenshot_output_height > height * ymult && ymult < 8) { + ymult++; + } + + const int output_width = width * xmult; + const int output_height = height * ymult; + const int output_rowbytes = output_width * 4; + storage.assign((size_t)output_rowbytes * (size_t)output_height, 0); + + xoffset = xoffset < 0 ? (width - src->outwidth) / 2 : -xoffset; + yoffset = yoffset < 0 ? (height - src->outheight) / 2 : -yoffset; + int dst_x = 0; + int dst_y = 0; + int src_x = 0; + int src_y = 0; + + if (xoffset > 0) { + dst_x = std::min(xoffset, std::max(0, width - src->outwidth)); + } else if (xoffset < 0) { + src_x = std::min(-xoffset, std::max(0, src->outwidth - width)); + } + if (yoffset > 0) { + dst_y = std::min(yoffset, std::max(0, height - src->outheight)); + } else if (yoffset < 0) { + src_y = std::min(-yoffset, std::max(0, src->outheight - height)); + } + + const int copy_width = std::min(width - dst_x, src->outwidth - src_x); + const int copy_height = std::min(height - dst_y, src->outheight - src_y); + if (copy_width > 0 && copy_height > 0) { + for (int y = 0; y < copy_height; y++) { + for (int x = 0; x < copy_width; x++) { + const uae_u32 pixel = unix_screenshot_get_pixel(src, src_x + x, src_y + y); + for (int yy = 0; yy < ymult; yy++) { + uae_u32 *out = (uae_u32 *)(storage.data() + + (size_t)((dst_y + y) * ymult + yy) * (size_t)output_rowbytes) + + (size_t)(dst_x + x) * (size_t)xmult; + for (int xx = 0; xx < xmult; xx++) { + out[xx] = pixel; + } + } + } + } + } + + memset(dst, 0, sizeof *dst); + dst->bufmem = storage.data(); + dst->outwidth = output_width; + dst->outheight = output_height; + dst->rowbytes = output_rowbytes; + dst->pixbytes = 4; + return true; +} + +static bool unix_prepare_active_screenshot(int monid, struct vidbuffer *prepared, + std::vector &prepared_storage, bool standard = false) +{ + if (monid < 0 || monid >= MAX_AMIGADISPLAYS) { + monid = 0; + } + struct vidbuf_description *vidinfo = &adisplays[monid].gfxvidinfo; + struct vidbuffer *vb = vidinfo->inbuffer ? vidinfo->inbuffer : &vidinfo->drawbuffer; + if (!vb || !vb->bufmem || vb->outwidth <= 0 || vb->outheight <= 0) { + write_log(_T("Unix screenshot: no active video buffer\n")); + return false; + } + if (!unix_prepare_screenshot_buffer(vb, prepared, prepared_storage, standard)) { + return false; + } + return true; +} + +#ifdef UAE_UNIX_WITH_SDL3_CLIPBOARD_DATA +struct UnixScreenshotClipboardData { + std::vector bmp; +}; + +static const void *SDLCALL unix_screenshot_clipboard_data(void *userdata, const char *, size_t *size) +{ + UnixScreenshotClipboardData *data = static_cast(userdata); + if (!data) { + if (size) { + *size = 0; + } + return NULL; + } + if (size) { + *size = data->bmp.size(); + } + return data->bmp.data(); +} + +static void SDLCALL unix_screenshot_clipboard_cleanup(void *userdata) +{ + delete static_cast(userdata); +} +#endif + +static bool unix_save_screenshot_clipboard(const struct vidbuffer *prepared) +{ +#ifdef UAE_UNIX_WITH_SDL3_CLIPBOARD_DATA + UnixScreenshotClipboardData *data = new UnixScreenshotClipboardData; + if (!unix_make_bmp(prepared, &data->bmp)) { + delete data; + return false; + } + + const char *mime_types[] = { "image/bmp", "image/x-bmp" }; + if (!SDL_SetClipboardData(unix_screenshot_clipboard_data, unix_screenshot_clipboard_cleanup, + data, mime_types, sizeof mime_types / sizeof mime_types[0])) { + write_log(_T("Unix screenshot: failed to copy BMP to clipboard: %s\n"), SDL_GetError()); + delete data; + return false; + } + + write_log(_T("Screenshot copied to clipboard\n")); + return true; +#else + write_log(_T("Unix screenshot: clipboard screenshots require SDL3 clipboard data support\n")); + return false; +#endif +} + +static bool unix_save_screenshot_file(int monid) +{ + TCHAR path[MAX_DPATH]; + TCHAR base[MAX_DPATH]; + TCHAR filename[MAX_DPATH]; + std::vector prepared_storage; + struct vidbuffer prepared; + + if (!unix_prepare_active_screenshot(monid, &prepared, prepared_storage)) { + return false; + } + fetch_screenshotpath(path, sizeof path / sizeof(TCHAR)); + if (!unix_ensure_directory(path)) { + write_log(_T("Unix screenshot: can't create screenshot directory '%s'\n"), path); + return false; + } + if (screenshot_multi) { + TCHAR *p = path + _tcslen(path); + while (dirnumber < 1000) { + _sntprintf(p, (sizeof path / sizeof(TCHAR)) - (p - path), _T("%03d/"), dirnumber); + if (!screenshot_multi_start) { + break; + } + filenumber = 0; + if (!my_existsdir(path) && !my_existsfile(path)) { + break; + } + dirnumber++; + } + screenshot_multi_start = 0; + if (dirnumber == 1000 || !unix_ensure_directory(path)) { + write_log(_T("Unix screenshot: can't create continuous screenshot directory '%s'\n"), path); + screenshot_multi = 0; + video_recording_active &= ~2; + return false; + } + } + unix_screenshot_base_name(base, sizeof base / sizeof(TCHAR)); + + for (int i = filenumber + 1; i < 100000; i++) { +#ifdef WINUAE_UNIX_WITH_LIBPNG + if (screenshotmode == 1) { + _sntprintf(filename, sizeof filename / sizeof(TCHAR), _T("%s%s_%05d.png"), path, base, i); + } else { + _sntprintf(filename, sizeof filename / sizeof(TCHAR), _T("%s%s_%05d.bmp"), path, base, i); + } +#else + _sntprintf(filename, sizeof filename / sizeof(TCHAR), _T("%s%s_%05d.bmp"), path, base, i); +#endif + FILE *existing = _tfopen(filename, _T("rb")); + if (existing) { + fclose(existing); + continue; + } +#ifdef WINUAE_UNIX_WITH_LIBPNG + if (screenshotmode == 1) { + if (!unix_write_png(filename, &prepared)) { + return false; + } + } else { + if (!unix_write_bmp(filename, &prepared)) { + return false; + } + } +#else + if (!unix_write_bmp(filename, &prepared)) { + return false; + } +#endif + filenumber = i; + write_log(_T("Screenshot saved as \"%s\"\n"), filename); + return true; + } + + write_log(_T("Unix screenshot: no free filename in '%s'\n"), path); + return false; +} + +void screenshot(int monid, int mode, int) +{ + if (monid < 0) { + monid = 0; + } + if (mode == 0) { + std::vector prepared_storage; + struct vidbuffer prepared; + if (unix_prepare_active_screenshot(monid, &prepared, prepared_storage)) { + unix_save_screenshot_clipboard(&prepared); + } + return; + } + if (mode == 2) { + screenshot_multi = 10; + video_recording_active |= 2; + screenshot_multi_start = 1; + return; + } + if (mode == 3) { + screenshot_multi = -1; + screenshot_multi_start = 1; + video_recording_active &= ~2; + return; + } + if (mode == 4) { + screenshot_multi = 0; + filenumber = 0; + video_recording_active &= ~2; + return; + } + unix_save_screenshot_file(monid); +} + +void screenshot_reset(void) +{ +} + +uae_u8 *save_screenshot(int monid, size_t *len) +{ + std::vector prepared_storage; + struct vidbuffer prepared; + + if (len) { + *len = 0; + } + if (!unix_prepare_active_screenshot(monid, &prepared, prepared_storage, true)) { + return NULL; + } + + std::vector bytes; +#ifdef WINUAE_UNIX_WITH_LIBPNG + if (!unix_encode_png(&bytes, &prepared)) { + return NULL; + } +#else + if (!unix_make_bmp(&prepared, &bytes)) { + return NULL; + } +#endif + if (bytes.empty()) { + return NULL; + } + + uae_u8 *out = xmalloc(uae_u8, bytes.size()); + memcpy(out, bytes.data(), bytes.size()); + if (len) { + *len = bytes.size(); + } + return out; +} -- 2.47.3