From 8fdd71b416123b00ac0c171e5fe56cc15f6d9942 Mon Sep 17 00:00:00 2001 From: hernandp Date: Wed, 22 Sep 2021 10:32:44 -0300 Subject: [PATCH 01/35] MEGA65: Big VIC-IV Emulation Improvement --- targets/mega65/vic4.c | 1495 +++++++++++++++++++++++++---------------- targets/mega65/vic4.h | 204 +++++- 2 files changed, 1121 insertions(+), 578 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index e9bc5c43..80c34e48 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -1,15 +1,7 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu Copyright (C)2016-2021 LGB (Gábor Lénárt) - - This is the VIC-IV "emulation". Currently it does one-frame-at-once - kind of horrible work, and only a subset of VIC2 and VIC3 knowledge - is implemented, with some light VIC-IV features, to be able to "boot" - of MEGA65 with standard configuration (kickstart, SD-card). - Some of the missing features (VIC-2/3): hardware attributes, - DAT, sprites, screen positioning, H1280 mode, V400 mode, interlace, - chroma killer, VIC2 MCM, ECM, 38/24 columns mode, border. - VIC-4: almost everything :( + Copyright (C)2020 Hernán Di Pietro This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,51 +17,74 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - #include "xemu/emutools.h" -#include "xemu/emutools_files.h" +#include "xemu/emutools_config.h" #include "mega65.h" #include "xemu/cpu65.h" #include "vic4.h" #include "vic4_palette.h" #include "memory_mapper.h" -#include "xemu/f011_core.h" -#include "configdb.h" - -//#define RGB(r,g,b) rgb_palette[((r) << 8) | ((g) << 4) | (b)] - -const char *iomode_names[4] = { "VIC2", "VIC3", "BAD!", "VIC4" }; - -//static Uint32 rgb_palette[4096]; // all the C65 palette, 4096 colours (SDL pixel format related form) -//static Uint32 vic3_palette[0x100]; // VIC3 palette in SDL pixel format related form (can be written into the texture directly to be rendered) -//static Uint32 vic3_rom_palette[0x100]; // the "ROM" palette, for C64 colours (with some ticks, ie colours above 15 are the same as the "normal" programmable palette) -//static Uint32 *palette; // the selected palette ... -//static Uint8 vic3_palette_nibbles[0x300]; - -Uint8 vic_registers[0x80]; // VIC-4 registers -int vic_iomode; // VIC2/VIC3/VIC4 mode -int scanline; // current scan line number +#include + +extern int in_hypervisor; + +static const char *iomode_names[4] = { "VIC2", "VIC3", "BAD!", "VIC4" }; + +// (SDL) target texture rendering pointers +static Uint32 *current_pixel; // current_pixel pointer to the rendering target (one current_pixel: 32 bit) +static Uint32 *pixel_end, *pixel_start; // points to the end and start of the buffer +static Uint32 *pixel_raster_start; // first pixel of current raster +Uint8 vic_registers[0x80]; // VIC-3 registers. It seems $47 is the last register. But to allow address the full VIC3 reg I/O space, we use $80 here +int vic_iomode; // VIC2/VIC3/VIC4 mode +int force_fast; // POKE 0,64 and 0,65 trick ... +Uint8 vic_registers[0x80]; // VIC-4 registers +int vic_iomode; // VIC2/VIC3/VIC4 mode +int force_fast; // POKE 0,64 and 0,65 trick ... +int scanline; // current scan line number int cpu_cycles_per_scanline; -static int compare_raster; // raster compare (9 bits width) data -static int interrupt_status; // Interrupt status of VIC -int vic2_16k_bank; // VIC-2 modes' 16K BANK address within 64K (NOT the traditional naming of banks with 0,1,2,3) -static Uint8 *sprite_pointers; // Pointer to sprite pointers :) -static Uint8 *sprite_bank; -static Uint8 *vic_bitplane_starting_bank_p = main_ram; -int vic3_blink_phase; // blinking attribute helper, state. -static Uint8 raster_colours[512]; -Uint8 c128_d030_reg; // C128-like register can be only accessed in VIC-II mode but not in others, quite special! -static Uint32 black_colour, red_colour; // needed for drive LED - -int vic_vidp_legacy = 1, vic_chrp_legacy = 1, vic_sprp_legacy = 1; - -#if 0 -// UGLY: decides to use VIC-II/III method (val!=0), or the VIC-IV "precise address" selection (val == 0) -// this is based on the idea that VIC-II compatible register writing will set that, overriding the "precise" setting if there was any, before. -static int vic2_vidp_method = 1; -static int vic2_chrp_method = 1; -#endif - +static int compare_raster; // raster compare (9 bits width) data +static int logical_raster = 0; +static int interrupt_status; // Interrupt status of VIC +static int vic4_blink_phase = 0; // blinking attribute helper, state. +Uint8 c128_d030_reg; // C128-like register can be only accessed in VIC-II mode but not in others, quite special! +static Uint8 reg_d018_screen_addr = 0; // Legacy VIC-II $D018 screen address register +static int vic_hotreg_touched = 0; // If any "legacy" registers were touched +static int vic4_sideborder_touched = 0; // If side-border register were touched +static int border_x_left= 0; // Side border left +static int border_x_right= 0; // Side border right +static int xcounter = 0, ycounter = 0; // video counters +static int frame_counter = 0; +static int char_row = 0, display_row = 0; +static Uint8 bg_pixel_state[1024]; // See FOREGROUND_PIXEL and BACKGROUND_PIXEL constants +static Uint8* screen_ram_current_ptr = NULL; +static Uint8* colour_ram_current_ptr = NULL; +extern int user_scanlines_setting; +float char_x_step = 0.0; +static int enable_bg_paint = 1; +static int display_row_count = 0; +static int max_rasters = PHYSICAL_RASTERS_DEFAULT; +static int visible_area_height = SCREEN_HEIGHT_VISIBLE_DEFAULT; +static int vicii_first_raster = 7; // Default for NTSC +static Uint8 *bitplane_bank_p = main_ram; + +void vic4_render_char_raster(); +void vic4_render_bitplane_raster(); +static void (* vic4_raster_renderer_path)(void) = &vic4_render_char_raster; + +// VIC-IV Modeline Parameters +// ---------------------------------------------------- +#define DISPLAY_HEIGHT ((max_rasters-1)-20) +#define TEXT_HEIGHT_200 400 +#define TEXT_HEIGHT_400 400 +#define CHARGEN_Y_SCALE_200 2 +#define CHARGEN_Y_SCALE_400 1 +#define chargen_y_pixels 0 +#define TOP_BORDERS_HEIGHT_200 (DISPLAY_HEIGHT - TEXT_HEIGHT_200) +#define TOP_BORDERS_HEIGHT_400 (DISPLAY_HEIGHT - TEXT_HEIGHT_400) +#define SINGLE_TOP_BORDER_200 (TOP_BORDERS_HEIGHT_200 >> 1) +#define SINGLE_TOP_BORDER_400 (TOP_BORDERS_HEIGHT_400 >> 1) + +#define MAX(a,b) ((a)>(b)?(a):(b)) //#define CHECK_PIXEL_POINTER @@ -103,43 +118,100 @@ static inline void PIXEL_POINTER_FINAL_ASSERT ( Uint32 *p ) # define PIXEL_POINTER_FINAL_ASSERT(p) #endif +// Lookup-based bit reversal. (TODO: Move this to a proper file) -void vic_reset ( void ) +static inline Uint8 reverse_byte(unsigned char x) { - vic2_16k_bank = 0; + static const Uint8 table[] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, //0 + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, //8 + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, //16 + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, //24 + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, //32 + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, //40 + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, //48 + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, //56 + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, //64 + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, //72 + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, //80 + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, //88 + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, //96 + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, //104 + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, //112 + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, //120 + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, //128 + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, + }; + return table[x]; +} + + +void vic_init ( void ) +{ + vic4_init_palette(); + force_fast = 0; + // *** Init VIC3 registers and palette vic_iomode = VIC2_IOMODE; interrupt_status = 0; + scanline = 0; compare_raster = 0; // *** Just a check to try all possible regs (in VIC2,VIC3 and VIC4 modes), it should not panic ... // It may also sets/initializes some internal variables sets by register writes, which would cause a crash on screen rendering without prior setup! - for (int i = 0; i < 0x140; i++) { + for (int i = 0; i < 0x140; i++) { // $140=the last $40 register for VIC-2 mode, when we have fewer ones vic_write_reg(i, 0); (void)vic_read_reg(i); } -} + c128_d030_reg = 0xFE; // this may be set to 2MHz in the previous step, so be sure to set to FF here, BUT FIX: bit 0 should be inverted!! + machine_set_speed(0); + + screen_ram_current_ptr = main_ram + SCREEN_ADDR; + colour_ram_current_ptr = colour_ram; + DEBUG("VIC4: has been initialized." NL); +} -void vic_init ( void ) +// This function allows to switch between NTSC/PAL on-the-fly (NTSC = 1. PAL = 0) +void vic4_switch_display_mode(int ntsc) { - // Needed to render "drive LED" feature - red_colour = SDL_MapRGBA(sdl_pix_fmt, 0xFF, 0x00, 0x00, 0xFF); - black_colour = SDL_MapRGBA(sdl_pix_fmt, 0x00, 0x00, 0x00, 0xFF); - // Init VIC4 palette - vic4_init_palette(); - // *** Init VIC4 registers - scanline = 0; - vic_reset(); - c128_d030_reg = 0xFE; // this may be set to 2MHz in the previous step, so be sure to set to FF here, BUT FIX: bit 0 should be inverted!! - machine_set_speed(0); - //vic_registers[0x30] = 4; // ROM palette? - //palette = vic_palettes + 0x400; - DEBUG("VIC4: has been initialized." NL); + DEBUGPRINT("VIC: switch_display_mode NTSC=%d" NL, ntsc); + xemu_change_display_mode(SCREEN_WIDTH, ntsc ? PHYSICAL_RASTERS_NTSC : PHYSICAL_RASTERS_PAL, // texture sizes + SCREEN_WIDTH, SCREEN_HEIGHT,// logical size (used with keeping aspect ratio by the SDL render stuffs) + SCREEN_WIDTH, SCREEN_HEIGHT,// window size + SCREEN_FORMAT, + USE_LOCKED_TEXTURE); + + if(!xemucfg_get_bool("fullborders")) + xemu_set_viewport(48, 32, SCREEN_WIDTH - 48, SCREEN_HEIGHT, 1); + + vic4_open_frame_access(); } +void vic4_open_frame_access() +{ + int tail_sdl; + current_pixel = pixel_start = xemu_start_pixel_buffer_access(&tail_sdl); + pixel_end = current_pixel + (SCREEN_WIDTH * max_rasters); + if (tail_sdl) + FATAL("tail_sdl is not zero!"); +} -static void vic3_interrupt_checker ( void ) +static void vic4_interrupt_checker ( void ) { int vic_irq_old = cpu65.irqLevel & 2; int vic_irq_new; @@ -159,38 +231,152 @@ static void vic3_interrupt_checker ( void ) } } - - -void vic3_check_raster_interrupt ( void ) +static void vic4_check_raster_interrupt(int nraster) { - raster_colours[scanline] = vic_registers[0x21]; // ugly hack to make some kind of raster-bars visible :-/ - if (scanline == compare_raster) + if (nraster == compare_raster) interrupt_status |= 1; else interrupt_status &= 0xFE; - vic3_interrupt_checker(); + + vic4_interrupt_checker(); +} + +inline static void vic4_calculate_char_x_step() +{ + char_x_step = (REG_CHARXSCALE / 120.0f) / (REG_H640 ? 1 : 2); +} + +static void vic4_reset_display_counters() +{ + xcounter = 0; + display_row = 0; + char_row = 0; + ycounter = 0; } +static void vic4_update_sideborder_dimensions() +{ + if (REG_CSEL) // 40-columns? + { + border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER; + + if (!REG_H640) + { + border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER - 1; + } + else //80-col mode + { + border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER; + } + } + else // 38-columns + { + border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER - 18; + + if (!REG_H640) + { + border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER + 14; + } + else //78-col mode + { + border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER + 15; + } + } +} -// FIXME: preliminary DAT support. For real, these should be mostly calculated at writing -// DAT X/Y registers, bitplane selection registers etc (also true for the actual renderer!), -// would give much better emulator performace. Though for now, that's a naive preliminary -// way to support DAT at all! -static XEMU_INLINE Uint8 *get_dat_addr ( unsigned int bpn ) +static void vic4_interpret_legacy_mode_registers() { - unsigned int x = vic_registers[0x3C]; - unsigned int y = vic_registers[0x3D] + ((x << 1) & 0x100); - unsigned int h640 = (vic_registers[0x31] & 128); - x &= 0x7F; - //DEBUGPRINT("VIC-IV: DAT: accessing DAT for bitplane #%u at X,Y of %u,%u in H%u mode" NL, bpn, x, y, h640 ? 640 : 320); - return - vic_bitplane_starting_bank_p + // MEGA65 feature (WANNABE feature!) to support relocatable bitplane bank by the DAT! (this also a pointer, not an integer!) - ((vic_registers[0x33 + bpn] & (h640 ? 12 : 14)) << 12) + // bitplane address - ((bpn & 1) ? 0x10000 : 0) + // odd/even bitplane selection - (((y >> 3) * (h640 ? 640 : 320)) + (x << 3) + (y & 7)) // position within the bitplane given by the X/Y info - ; + // See https://github.com/MEGA65/mega65-core/blob/257d78aa6a21638cb0120fd34bc0e6ab11adfd7c/src/vhdl/viciv.vhdl#L1277 + + vic4_update_sideborder_dimensions(); + + if (REG_CSEL) // 40-columns? + { + if (!REG_H640) + { + SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL)); + } + else //80-col mode + { + SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL) - 2); + } + } + else // 38-columns + { + if (!REG_H640) + { + SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL)); + } + else //78-col mode + { + SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL) - 2); + } + } + + if (!REG_V400) // Standard mode (200-lines) + { + if (REG_RSEL) // 25-row + { + SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster)); + SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) - 1); + display_row_count = 25; + } + else + { + SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) + 8); + SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - (2 * vicii_first_raster) - SINGLE_TOP_BORDER_200 - 7); + display_row_count = 24; + } + + SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) - 6 + REG_VIC2_YSCROLL * 2); + } + else // V400 + { + if (REG_RSEL) // 25-line+V400 + { + SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster)); + SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 1); + display_row_count = 25*2; + } + else + { + SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) + 8); + SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - (2 * vicii_first_raster) - SINGLE_TOP_BORDER_200 - 7); + display_row_count = 24*2; + } + + SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 6 + (REG_VIC2_YSCROLL * 2)); + } + + Uint8 width = REG_H640 ? 80 : 40; + REG_CHRCOUNT = width; + SET_CHARSTEP_BYTES(width);// * (REG_16BITCHARSET ? 2 : 1)); + + REG_SCRNPTR_B0 = 0; + REG_SCRNPTR_B1 &= 0xC0; + REG_SCRNPTR_B1 |= REG_H640 ? ((reg_d018_screen_addr & 14) << 2) : (reg_d018_screen_addr << 2); + REG_SCRNPTR_B2 = 0; + vic_registers[0x63] &= 0b11110000; + + REG_SPRPTR_B0 = 0xF8; + REG_SPRPTR_B1 = (reg_d018_screen_addr << 2) | 0x3; + if (REG_H640 | REG_V400) + REG_SPRPTR_B1 |= 4; + vic_registers[0x6E] &= 128; + + REG_SPRPTR_B1 = (~last_dd00_bits << 6) | (REG_SPRPTR_B1 & 0x3F); + REG_SCRNPTR_B1 = (~last_dd00_bits << 6) | (REG_SCRNPTR_B1 & 0x3F); + REG_CHARPTR_B1 = (~last_dd00_bits << 6) | (REG_CHARPTR_B1 & 0x3F); + + SET_COLORRAM_BASE(0); + DEBUGPRINT("VIC4: 16bit=%d, chrcount=%d, charstep=%d bytes, charscale=%d, vic_ii_first_raster=%d, ras_src=%d," + "border yt=%d, yb=%d, xl=%d, xr=%d, textxpos=%d, textypos=%d," + "screen_ram=$%06x, charset/bitmap=$%06x, sprite=$%06x" NL, REG_16BITCHARSET , REG_CHRCOUNT,CHARSTEP_BYTES,REG_CHARXSCALE, + vicii_first_raster, REG_FNRST, BORDER_Y_TOP, BORDER_Y_BOTTOM, border_x_left, border_x_right, CHARGEN_X_START, CHARGEN_Y_START, + SCREEN_ADDR, CHARSET_ADDR, SPRITE_POINTER_ADDR); } + /* DESIGN of vic_read_reg() and vic_write_reg() functions: addr = 00-7F, VIC-IV registers 00-7F (ALWAYS, regardless of current I/O mode!) addr = 80-FF, VIC-III registers 00-7F (ALWAYS, regardless of current I/O mode!) [though for VIC-III, many registers are ignored after the last one] @@ -214,10 +400,17 @@ static const char vic_registers_internal_mode_names[] = {'4', '3', '2'}; #define CASE_VIC_ALL(n) CASE_VIC_2(n): CASE_VIC_3(n): CASE_VIC_4(n) #define CASE_VIC_3_4(n) CASE_VIC_3(n): CASE_VIC_4(n) +/* - If HOTREG register is enabled, VICIV will trigger recalculation of border and such on next raster, + on any "legacy" register write. For the VIC-IV such "hot" registers are: + -- @IO:C64 $D011 VIC-II control register + -- @IO:C64 $D016 VIC-II control register + -- @IO:C64 $D018 VIC-II RAM addresses + -- @IO:C65 $D031 VIC-III Control Register B +*/ void vic_write_reg ( unsigned int addr, Uint8 data ) { - DEBUG("VIC%c: write reg $%02X (internally $%03X) with data $%02X" NL, XEMU_LIKELY(addr < 0x180) ? vic_registers_internal_mode_names[addr >> 7] : '?', addr & 0x7F, addr, data); + //DEBUGPRINT("VIC%c: write reg $%02X (internally $%03X) with data $%02X" NL, XEMU_LIKELY(addr < 0x180) ? vic_registers_internal_mode_names[addr >> 7] : '?', addr & 0x7F, addr, data); // IMPORTANT NOTE: writing of vic_registers[] happens only *AFTER* this switch/case construct! This means if you need to do this before, you must do it manually at the right "case"!!!! // if you do so, you can even use "return" instead of "break" to save the then-redundant write of the register switch (addr) { @@ -226,37 +419,49 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) CASE_VIC_ALL(0x10): break; // Sprite coordinates: simple write the VIC reg in all I/O modes. CASE_VIC_ALL(0x11): + if (vic_registers[0x11] ^ data) + { + vic_hotreg_touched = 1; + } compare_raster = (compare_raster & 0xFF) | ((data & 0x80) << 1); - DEBUG("VIC: compare raster is now %d" NL, compare_raster); + DEBUGPRINT("VIC: compare raster is now %d" NL, compare_raster); break; CASE_VIC_ALL(0x12): compare_raster = (compare_raster & 0xFF00) | data; - DEBUG("VIC: compare raster is now %d" NL, compare_raster); + DEBUGPRINT("VIC: compare raster is now %d" NL, compare_raster); break; CASE_VIC_ALL(0x13): CASE_VIC_ALL(0x14): return; // FIXME: writing light-pen registers????? CASE_VIC_ALL(0x15): // sprite enabled + break; CASE_VIC_ALL(0x16): // control-reg#2, we allow write even if non-used bits here + if (vic_registers[0x16] ^ data) + { + vic_hotreg_touched = 1; + } + break; CASE_VIC_ALL(0x17): // sprite-Y expansion break; - CASE_VIC_ALL(0x18): // memory pointers - if (!vic_vidp_legacy) { - vic_vidp_legacy = 1; - DEBUGPRINT("VIC4: compatibility screen address mode" NL); - } - if (!vic_chrp_legacy) { - vic_chrp_legacy = 1; - DEBUGPRINT("VIC4: compatibility character address mode" NL); - } - if (!vic_sprp_legacy) { - vic_sprp_legacy = 1; - DEBUGPRINT("VIC4: compatibility sprite pointer address mode" NL); + CASE_VIC_ALL(0x18): // memory pointers. + // (See vic4_interpret_legacy_mode_registers () for later REG_SCRNPTR_ adjustments) + // Reads are mapped to extended registers. + // So we just store the D018 Legacy Screen Address to be referenced elsewhere. + // + if (vic_registers[0x18] ^ data) + { + REG_CHARPTR_B2 = 0; + REG_CHARPTR_B1 = (data & 14) << 2; + REG_CHARPTR_B0 = 0; + REG_SCRNPTR_B2 &= 0xF0; + reg_d018_screen_addr = (data & 0xF0) >> 4; + vic_hotreg_touched = 1; } + data &= 0xFE; break; CASE_VIC_ALL(0x19): interrupt_status = interrupt_status & (~data) & 0xF; - vic3_interrupt_checker(); + vic4_interrupt_checker(); break; CASE_VIC_ALL(0x1A): data &= 0xF; @@ -298,6 +503,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) } while(0); break; CASE_VIC_2(0x30): // this register is _SPECIAL_, and exists only in VIC-II (C64) I/O mode: C128-style "2MHz fast" mode ... + DEBUGPRINT("VIC: Write 0xD030: $%02x" NL, data); c128_d030_reg = data; machine_set_speed(0); return; // it IS important to have return here, since it's not a "real" VIC-4 mode register's view in another mode!! @@ -307,59 +513,117 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) check_if_rom_palette(data & 4); break; CASE_VIC_3_4(0x31): + // (!) NOTE: + // According to Paul, speed change should trigger "HOTREG" touched notification but no VIC legacy register "interpret" + // So probably we need a separate (cpu_speed_hotreg) var? + // + if ( (vic_registers[0x31] & 0xBF) ^ (data & 0xBF) ) + { + vic_hotreg_touched = 1; + } + + vic4_raster_renderer_path = ( (data & 0x10) == 0) ? vic4_render_char_raster : vic4_render_bitplane_raster; + vic_registers[0x31] = data; // we need this work-around, since reg-write happens _after_ this switch statement, but machine_set_speed above needs it ... machine_set_speed(0); - return; // since we DID the write, it's OK to return here and not using "break" + + vic4_calculate_char_x_step(); + break; //We did the write, but we need to trigger vichot_reg if should + CASE_VIC_3_4(0x32): CASE_VIC_3_4(0x33): CASE_VIC_3_4(0x34): CASE_VIC_3_4(0x35): CASE_VIC_3_4(0x36): CASE_VIC_3_4(0x37): CASE_VIC_3_4(0x38): CASE_VIC_3_4(0x39): CASE_VIC_3_4(0x3A): CASE_VIC_3_4(0x3B): CASE_VIC_3_4(0x3C): CASE_VIC_3_4(0x3D): CASE_VIC_3_4(0x3E): CASE_VIC_3_4(0x3F): - break; - // DAT read/write bitplanes port CASE_VIC_3_4(0x40): CASE_VIC_3_4(0x41): CASE_VIC_3_4(0x42): CASE_VIC_3_4(0x43): CASE_VIC_3_4(0x44): CASE_VIC_3_4(0x45): CASE_VIC_3_4(0x46): CASE_VIC_3_4(0x47): - *get_dat_addr(addr & 7) = data; // write pixels via the DAT! break; /* --- NO MORE VIC-III REGS FROM HERE --- */ - CASE_VIC_4(0x48): CASE_VIC_4(0x49): CASE_VIC_4(0x4A): CASE_VIC_4(0x4B): CASE_VIC_4(0x4C): CASE_VIC_4(0x4D): CASE_VIC_4(0x4E): CASE_VIC_4(0x4F): - CASE_VIC_4(0x50): CASE_VIC_4(0x51): CASE_VIC_4(0x52): CASE_VIC_4(0x53): + CASE_VIC_4(0x48): CASE_VIC_4(0x49): CASE_VIC_4(0x4A): CASE_VIC_4(0x4B): + CASE_VIC_4(0x4C): CASE_VIC_4(0x4D): CASE_VIC_4(0x4E): CASE_VIC_4(0x4F): + break; + CASE_VIC_4(0x50): CASE_VIC_4(0x51): + return; // Writing to XPOS register is no-op + CASE_VIC_4(0x52): CASE_VIC_4(0x53): break; CASE_VIC_4(0x54): vic_registers[0x54] = data; // we need this work-around, since reg-write happens _after_ this switch statement, but machine_set_speed above needs it ... machine_set_speed(0); return; // since we DID the write, it's OK to return here and not using "break" - CASE_VIC_4(0x55): CASE_VIC_4(0x56): CASE_VIC_4(0x57): CASE_VIC_4(0x58): CASE_VIC_4(0x59): CASE_VIC_4(0x5A): CASE_VIC_4(0x5B): CASE_VIC_4(0x5C): - CASE_VIC_4(0x5D): CASE_VIC_4(0x5E): CASE_VIC_4(0x5F): /*CASE_VIC_4(0x60): CASE_VIC_4(0x61): CASE_VIC_4(0x62): CASE_VIC_4(0x63):*/ CASE_VIC_4(0x64): - CASE_VIC_4(0x65): CASE_VIC_4(0x66): CASE_VIC_4(0x67): /*CASE_VIC_4(0x68): CASE_VIC_4(0x69): CASE_VIC_4(0x6A):*/ CASE_VIC_4(0x6B): /*CASE_VIC_4(0x6C): - CASE_VIC_4(0x6D): CASE_VIC_4(0x6E):*/ CASE_VIC_4(0x6F): /*CASE_VIC_4(0x70):*/ CASE_VIC_4(0x71): CASE_VIC_4(0x72): CASE_VIC_4(0x73): CASE_VIC_4(0x74): - CASE_VIC_4(0x75): CASE_VIC_4(0x76): CASE_VIC_4(0x77): CASE_VIC_4(0x78): CASE_VIC_4(0x79): CASE_VIC_4(0x7A): CASE_VIC_4(0x7B): + CASE_VIC_4(0x55): CASE_VIC_4(0x56): CASE_VIC_4(0x57): break; + CASE_VIC_4(0x58): CASE_VIC_4(0x59): + DEBUGPRINT("VIC: Write $%04x CHARSTEP: $%02x" NL, addr, data); break; - CASE_VIC_4(0x7C): - if ((data & 7) <= 2) { - // The lower 3 bits of $7C set's the number of "128K slice" of the main RAM to be used with bitplanes - vic_bitplane_starting_bank_p = main_ram + ((data & 7) << 17); - DEBUG("VIC4: bitmap bank offset is $%X" NL, (unsigned int)(vic_bitplane_starting_bank_p - main_ram)); - } else - WARNING_WINDOW("VIC-IV bitplane selection 128K-bank tried to set over 2.\nRefused to do so."); + CASE_VIC_4(0x5A): + //DEBUGPRINT("WRITE $%04x CHARXSCALE: $%02x" NL, addr, data); + vic_registers[0x5A] = data; // Write now and calculate step. + vic4_calculate_char_x_step(); + return; + CASE_VIC_4(0x5B): break; - CASE_VIC_4(0x7D): CASE_VIC_4(0x7E): CASE_VIC_4(0x7F): + CASE_VIC_4(0x5C): + vic4_sideborder_touched = 1; + break; + + CASE_VIC_4(0x5D): + DEBUGPRINT("VIC: Write $%04x SIDEBORDER/HOTREG: $%02x" NL, addr, data); + + if((vic_registers[0x5D] & 0x1F) ^ (data & 0x1F)) // sideborder MSB (0..5) modified ? + vic4_sideborder_touched = 1; + break; + + CASE_VIC_4(0x5E): + DEBUGPRINT("VIC: Write $%04x CHARCOUNT: $%02x" NL, addr, data); + break; + CASE_VIC_4(0x5F): break; CASE_VIC_4(0x60): CASE_VIC_4(0x61): CASE_VIC_4(0x62): CASE_VIC_4(0x63): - if (vic_vidp_legacy) { - vic_vidp_legacy = 0; - DEBUGPRINT("VIC4: precise video address mode" NL); - } + DEBUGPRINT("VIC: Write SCREENADDR byte 0xD0%02x: $%02x" NL, addr, data); + break; + CASE_VIC_4(0x64): + CASE_VIC_4(0x65): CASE_VIC_4(0x66): CASE_VIC_4(0x67): /*CASE_VIC_4(0x68): CASE_VIC_4(0x69): CASE_VIC_4(0x6A):*/ CASE_VIC_4(0x6B): /*CASE_VIC_4(0x6C): + CASE_VIC_4(0x6D): CASE_VIC_4(0x6E):*//*CASE_VIC_4(0x70):*/ CASE_VIC_4(0x71): CASE_VIC_4(0x72): CASE_VIC_4(0x73): CASE_VIC_4(0x74): + CASE_VIC_4(0x75): CASE_VIC_4(0x76): CASE_VIC_4(0x77): CASE_VIC_4(0x78): CASE_VIC_4(0x79): CASE_VIC_4(0x7A): CASE_VIC_4(0x7B): /*CASE_VIC_4(0x7C):*/ + CASE_VIC_4(0x7D): CASE_VIC_4(0x7E): CASE_VIC_4(0x7F): break; + CASE_VIC_4(0x68): CASE_VIC_4(0x69): CASE_VIC_4(0x6A): - if (vic_chrp_legacy) { - vic_chrp_legacy = 0; - DEBUGPRINT("VIC4: precise character address mode" NL); - } break; CASE_VIC_4(0x6C): CASE_VIC_4(0x6D): CASE_VIC_4(0x6E): - if (vic_sprp_legacy) { - vic_sprp_legacy = 0; - DEBUGPRINT("VIC4: precise sprite pointer address mode" NL); - } + vic_registers[addr & 0x7F] = data; + // if (SPRITE_POINTER_ADDR > 384*1024) { + // DEBUGPRINT("WARNING !!! : SPRITE_POINTER_ADDR at $%08X exceeds 384K chip RAM!!!! Current behavior is undefined." NL, SPRITE_POINTER_ADDR); + // } + + // DEBUGPRINT("SPRPTRADR/SPRPTRBNK Modified. Sprite Data Pointers now: " NL); + + // for (int i = 0; i < 8; ++i) { + // const Uint8 *sprite_data_pointer = main_ram + SPRITE_POINTER_ADDR + i * ((SPRITE_16BITPOINTER >> 7) + 1); + // const Uint32 dataptr = SPRITE_16BITPOINTER ? 64 * ( ((*(sprite_data_pointer+1) << 8)) + (*(sprite_data_pointer))) : 64 * (*sprite_data_pointer); + // DEBUGPRINT("Sprite #%d data @ $%08X %s" NL , i, dataptr, dataptr > 384*1024 ? "!!! OUT OF 384K main RAM !!!" : ""); + // } + break; + CASE_VIC_4(0x6F): + // Trigger video mode change. + + max_rasters = data & 0x80 ? PHYSICAL_RASTERS_NTSC : PHYSICAL_RASTERS_PAL; + visible_area_height = data & 0x80 ? SCREEN_HEIGHT_VISIBLE_NTSC : SCREEN_HEIGHT_VISIBLE_PAL; + + if ((vic_registers[0x6F] & 0x80) ^ (data & 0x80)) + { + // Change video mode + vic4_reset_display_counters(); + vic4_switch_display_mode(data & 0x80); + } + + vicii_first_raster = data & 0x1F; + + if (!in_hypervisor) + { + vic4_sideborder_touched = 1; + vic4_interpret_legacy_mode_registers(); + } + + break; + CASE_VIC_4(0x70): // VIC-IV palette selection register altpalette = ((data & 0x03) << 8) + vic_palettes; spritepalette = ((data & 0x0C) << 6) + vic_palettes; @@ -367,6 +631,14 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) palregaccofs = ((data & 0xC0) << 2); check_if_rom_palette(vic_registers[0x30] & 4); break; + CASE_VIC_4(0x7C): + if ((data & 7) <= 2) { + // The lower 3 bits of $7C set's the number of "128K slice" of the main RAM to be used with bitplanes + bitplane_bank_p = main_ram + ((data & 7) << 17); + DEBUG("VIC4: bitmap bank offset is $%X" NL, (unsigned int)(bitplane_bank_p - main_ram)); + } else + DEBUGPRINT("VIC4: bitplane selection 128K-bank tried to set over 2. Refused to do so." NL); + break; /* --- NON-EXISTING REGISTERS --- */ CASE_VIC_2(0x31): CASE_VIC_2(0x32): CASE_VIC_2(0x33): CASE_VIC_2(0x34): CASE_VIC_2(0x35): CASE_VIC_2(0x36): CASE_VIC_2(0x37): CASE_VIC_2(0x38): CASE_VIC_2(0x39): CASE_VIC_2(0x3A): CASE_VIC_2(0x3B): CASE_VIC_2(0x3C): CASE_VIC_2(0x3D): CASE_VIC_2(0x3E): CASE_VIC_2(0x3F): @@ -386,6 +658,24 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) FATAL("Xemu: invalid VIC internal register numbering on write: $%X", addr); } vic_registers[addr & 0x7F] = data; + if (REG_HOTREG) + { + if (vic_hotreg_touched) + { + //DEBUGPRINT("VIC: vic_hotreg_touched triggered (WRITE $D0%02x, $%02x)" NL, addr & 0x7F, data ); + vic4_interpret_legacy_mode_registers(); + vic_hotreg_touched = 0; + vic4_sideborder_touched = 0; + } + + if (vic4_sideborder_touched) + { + //DEBUGPRINT("VIC: vic4_sideborder_touched triggered (WRITE $D0%02x, $%02x)" NL, addr & 0x7F, data ); + + vic4_update_sideborder_dimensions(); + vic4_sideborder_touched = 0; + } + } } @@ -399,10 +689,10 @@ Uint8 vic_read_reg ( int unsigned addr ) CASE_VIC_ALL(0x10): break; // Sprite coordinates CASE_VIC_ALL(0x11): - result = (result & 0x7F) | ((scanline & 0x100) >> 1); + result = (result & 0x7F) | ((logical_raster & 0x100) >> 1); break; CASE_VIC_ALL(0x12): - result = scanline & 0xFF; + result = logical_raster & 0xFF; break; CASE_VIC_ALL(0x13): CASE_VIC_ALL(0x14): break; // light-pen registers @@ -415,6 +705,9 @@ Uint8 vic_read_reg ( int unsigned addr ) break; CASE_VIC_ALL(0x18): // memory pointers result |= 1; + // Always mapped to VIC-IV extended "precise" registers + // result = ((REG_SCRNPTR_B1 & 60) << 2) | ((REG_CHARPTR_B1 & 60) >> 2); + // DEBUGPRINT("READ 0x81: $%02x" NL, result); break; CASE_VIC_ALL(0x19): result = interrupt_status | (64 + 32 + 16); @@ -456,28 +749,30 @@ Uint8 vic_read_reg ( int unsigned addr ) break; CASE_VIC_3_4(0x32): CASE_VIC_3_4(0x33): CASE_VIC_3_4(0x34): CASE_VIC_3_4(0x35): CASE_VIC_3_4(0x36): CASE_VIC_3_4(0x37): CASE_VIC_3_4(0x38): CASE_VIC_3_4(0x39): CASE_VIC_3_4(0x3A): CASE_VIC_3_4(0x3B): CASE_VIC_3_4(0x3C): CASE_VIC_3_4(0x3D): CASE_VIC_3_4(0x3E): CASE_VIC_3_4(0x3F): - break; - // DAT read/write bitplanes port CASE_VIC_3_4(0x40): CASE_VIC_3_4(0x41): CASE_VIC_3_4(0x42): CASE_VIC_3_4(0x43): CASE_VIC_3_4(0x44): CASE_VIC_3_4(0x45): CASE_VIC_3_4(0x46): CASE_VIC_3_4(0x47): - result = *get_dat_addr(addr & 7); // read pixels via the DAT! break; /* --- NO MORE VIC-III REGS FROM HERE --- */ CASE_VIC_4(0x48): CASE_VIC_4(0x49): CASE_VIC_4(0x4A): CASE_VIC_4(0x4B): CASE_VIC_4(0x4C): CASE_VIC_4(0x4D): CASE_VIC_4(0x4E): CASE_VIC_4(0x4F): - CASE_VIC_4(0x50): CASE_VIC_4(0x51): + CASE_VIC_4(0x50): break; - CASE_VIC_4(0x52): - result = (scanline << 1) & 0xFF; // hack: report phys raster always double of vic-II raster + CASE_VIC_4(0x51): + result = vic_registers[0x51]++; break; - CASE_VIC_4(0x53): - result = ((scanline << 1) >> 8) & 7; + CASE_VIC_4(0x52): CASE_VIC_4(0x53): break; CASE_VIC_4(0x54): break; CASE_VIC_4(0x55): CASE_VIC_4(0x56): CASE_VIC_4(0x57): CASE_VIC_4(0x58): CASE_VIC_4(0x59): CASE_VIC_4(0x5A): CASE_VIC_4(0x5B): CASE_VIC_4(0x5C): CASE_VIC_4(0x5D): CASE_VIC_4(0x5E): CASE_VIC_4(0x5F): CASE_VIC_4(0x60): CASE_VIC_4(0x61): CASE_VIC_4(0x62): CASE_VIC_4(0x63): CASE_VIC_4(0x64): CASE_VIC_4(0x65): CASE_VIC_4(0x66): CASE_VIC_4(0x67): CASE_VIC_4(0x68): CASE_VIC_4(0x69): CASE_VIC_4(0x6A): CASE_VIC_4(0x6B): CASE_VIC_4(0x6C): - CASE_VIC_4(0x6D): CASE_VIC_4(0x6E): CASE_VIC_4(0x6F): CASE_VIC_4(0x70): CASE_VIC_4(0x71): CASE_VIC_4(0x72): CASE_VIC_4(0x73): CASE_VIC_4(0x74): + CASE_VIC_4(0x6D): + break; + CASE_VIC_4(0x6E): + + break; + + CASE_VIC_4(0x6F): CASE_VIC_4(0x70): CASE_VIC_4(0x71): CASE_VIC_4(0x72): CASE_VIC_4(0x73): CASE_VIC_4(0x74): CASE_VIC_4(0x75): CASE_VIC_4(0x76): CASE_VIC_4(0x77): CASE_VIC_4(0x78): CASE_VIC_4(0x79): CASE_VIC_4(0x7A): CASE_VIC_4(0x7B): CASE_VIC_4(0x7C): CASE_VIC_4(0x7D): CASE_VIC_4(0x7E): CASE_VIC_4(0x7F): break; @@ -502,7 +797,6 @@ Uint8 vic_read_reg ( int unsigned addr ) FATAL("Xemu: invalid VIC internal register numbering on read: $%X", addr); } DEBUG("VIC%c: read reg $%02X (internally $%03X) with result $%02X" NL, XEMU_LIKELY(addr < 0x180) ? vic_registers_internal_mode_names[addr >> 7] : '?', addr & 0x7F, addr, result); - vic_registers[0x51]++; //ugly hack, MEGAWAT wants this to change or what?! return result; } @@ -513,479 +807,540 @@ Uint8 vic_read_reg ( int unsigned addr ) #undef CASE_VIC_ALL #undef CASE_VIC_3_4 - -static inline Uint8 *vic2_get_chargen_pointer ( void ) +static inline Uint32 get_charset_effective_addr() { - if (vic_chrp_legacy) { - int offs = (vic_registers[0x18] & 14) << 10; // character generator address address within the current VIC2 bank - //int crom = vic_registers[0x30] & 64; - //DEBUG("VIC2: chargen: BANK=%04X OFS=%04X CROM=%d" NL, vic2_16k_bank, offs, crom); - if ((vic2_16k_bank == 0x0000 || vic2_16k_bank == 0x8000) && (offs == 0x1000 || offs == 0x1800)) { // check if chargen info is in ROM - // In case of MEGA65, fetching char-info from ROM means to access the "WOM" - // FIXME: what should I do with bit 6 of VIC-III register $30 ["CROM"] ?! - return char_wom + offs - 0x1000; - } else - return main_ram + vic2_16k_bank + offs; - } else { - return main_ram + ((vic_registers[0x68] | (vic_registers[0x69] << 8) | (vic_registers[0x6A] << 16)) & ((512 << 10) - 1)); + // cache this? + switch (CHARSET_ADDR) + { + case 0x1000: + return 0x2D000; + case 0x9000: + return 0x29000; + case 0x1800: + return 0x2D800; + case 0x9800: + return 0x29800; } + return CHARSET_ADDR; } - -//#define BG_FOR_Y(y) vic_registers[0x21] -#define BG_FOR_Y(y) raster_colours[(y) + 50] - - - -/* At-frame-at-once (thus incorrect implementation) renderer for H640 (80 column) - and "normal" (40 column) text VIC modes. Hardware attributes are not supported! - No support for MCM and ECM! */ -static inline void vic2_render_screen_text ( Uint32 *p, int tail ) +static void vic4_draw_sprite_row_16color(int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale) { - Uint32 bg; - Uint8 *vidp, *colp = colour_ram; - int x = 0, y = 0, xlim, ylim, charline = 0; - Uint8 *chrg = vic2_get_chargen_pointer(); - int inc_p = (vic_registers[0x54] & 1) ? 2 : 1; // VIC-IV (MEGA65) 16 bit text mode? - int scanline = 0; - if (vic_registers[0x31] & 128) { // check H640 bit: 80 column mode? - xlim = 79; - ylim = 24; - // Note: VIC2 sees ROM at some addresses thing is not emulated yet for other thing than chargen memory! - // Note: according to the specification bit 4 has no effect in 80 columns mode! - vidp = main_ram + ((vic_registers[0x18] & 0xE0) << 6) + vic2_16k_bank; - sprite_pointers = vidp + 2040; - } else { - xlim = 39; - ylim = 24; - // Note: VIC2 sees ROM at some addresses thing is not emulated yet for other thing than chargen memory! - vidp = main_ram + ((vic_registers[0x18] & 0xF0) << 6) + vic2_16k_bank; - sprite_pointers = vidp + 1016; - } - // Ugly hack, override video ram if no legacy starting address policy applied - if (!vic_vidp_legacy) { - vidp = main_ram + ((vic_registers[0x60] | (vic_registers[0x61] << 8) | (vic_registers[0x62] << 16)) & ((512 << 10) - 1)); - } - if (!vic_sprp_legacy) { - sprite_pointers = main_ram + ((vic_registers[0x6C] | (vic_registers[0x6D] << 8) | (vic_registers[0x6E] << 16)) & ((512 << 10) - 1)); - } - //DEBUGPRINT("VIC4: vidp = $%X, vic_vidp_legacy=%X" NL, (unsigned int)(vidp - main_ram), vic_vidp_legacy); - // Target SDL pixel related format for the background colour - bg = palette[BG_FOR_Y(0)]; - PIXEL_POINTER_CHECK_INIT(p, tail, "vic2_render_screen_text"); - for (;;) { - Uint8 coldata = *colp; - Uint32 fg; - if ( - inc_p == 2 && ( // D054 bit 0 controlled stuff (16bit mode) - (vidp[1] == 0 && (vic_registers[0x54] & 2)) || // enabled for =<$FF chars - (vidp[1] && (vic_registers[0x54] & 4)) // enabled for >$FF chars - )) { - if (vidp[0] == 0xFF && vidp[1] == 0xFF) { - // end of line marker, let's use background to fill the rest of the line ... - // FIXME: however in the current situation we can't do that because of the "fixed" line length for 80 or 40 chars ... :( - p += xlim == 39 ? 16 : 8; // so we just ignore ... FIXME !! - } else { - int a; - Uint8 *cp = main_ram + (((vidp[0] << 6) + (charline << 3) + (vidp[1] << 14)) & 0x7ffff); // and-mask: wrap-around @ 512K of RAM [though only 384K is used by M65] - for (a = 0; a < 8; a++) { - if (xlim != 79) - *(p++) = palette[*cp]; - *(p++) = palette[*(cp++)]; + const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; + const int palindexbase = sprnum * 16 + 128 * (SPRITE_BITPLANE_ENABLE(sprnum) >> sprnum); + + for (int byte = 0; byte < totalBytes; ++byte) + { + const Uint8 c0 = (*(row_data_ptr + byte)) >> 4; + const Uint8 c1 = (*(row_data_ptr + byte)) & 0xF; + for (int p = 0; p < xscale && x_display_pos < border_x_right; ++p, ++x_display_pos) + { + if (c0) + { + if (x_display_pos >= border_x_left && + (!SPRITE_IS_BACK(sprnum) || + (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL))) + { + *(pixel_raster_start + x_display_pos) = spritepalette[palindexbase + c0]; } } - } else { - Uint8 chrdata = chrg[(*vidp << 3) + charline]; - if (vic_registers[0x31] & 32) { // ATTR bit mode - if ((coldata & 0xF0) == 0x10) { // only the blink bit for the character is set - if (vic3_blink_phase) - chrdata = 0; // blinking character, in one phase, the character "disappears", ie blinking - coldata &= 15; - } else if ((!(coldata & 0x10)) || vic3_blink_phase) { - if (coldata & 0x80 && charline == 7) // underline (must be before reverse, as underline can be reversed as well!) - chrdata = 0XFF; // the underline - if (coldata & 0x20) // reverse bit for char - chrdata = ~chrdata; - if (coldata & 0x40) // highlight, this must be the LAST, since it sets the low nibble of coldata ... - coldata = 0x10 | (coldata & 15); - else - coldata &= 15; - } else - coldata &= 15; - } else - coldata &= 15; - fg = palette[coldata]; - // FIXME: no ECM, MCM stuff ... - if (xlim == 79) { - PIXEL_POINTER_CHECK_ASSERT(p + 7); - *(p++) = chrdata & 128 ? fg : bg; - *(p++) = chrdata & 64 ? fg : bg; - *(p++) = chrdata & 32 ? fg : bg; - *(p++) = chrdata & 16 ? fg : bg; - *(p++) = chrdata & 8 ? fg : bg; - *(p++) = chrdata & 4 ? fg : bg; - *(p++) = chrdata & 2 ? fg : bg; - *(p++) = chrdata & 1 ? fg : bg; - } else { - PIXEL_POINTER_CHECK_ASSERT(p + 15); - p[ 0] = p[ 1] = chrdata & 128 ? fg : bg; - p[ 2] = p[ 3] = chrdata & 64 ? fg : bg; - p[ 4] = p[ 5] = chrdata & 32 ? fg : bg; - p[ 6] = p[ 7] = chrdata & 16 ? fg : bg; - p[ 8] = p[ 9] = chrdata & 8 ? fg : bg; - p[10] = p[11] = chrdata & 4 ? fg : bg; - p[12] = p[13] = chrdata & 2 ? fg : bg; - p[14] = p[15] = chrdata & 1 ? fg : bg; - p += 16; - } } - colp += inc_p; - vidp += inc_p; - if (x == xlim) { - p += tail; - x = 0; - if (charline == 7) { - if (y == ylim) - break; - y++; - charline = 0; - } else { - charline++; - vidp -= (xlim + 1) * inc_p; - colp -= (xlim + 1) * inc_p; + + for (int p = 0; p < xscale && x_display_pos < border_x_right; ++p, ++x_display_pos) + { + if (c1) + { + if (x_display_pos >= border_x_left && + (!SPRITE_IS_BACK(sprnum) || + (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL))) + { + *(pixel_raster_start + x_display_pos) = spritepalette[palindexbase + c1]; + } } - bg = palette[BG_FOR_Y(++scanline)]; - } else - x++; + } } - PIXEL_POINTER_FINAL_ASSERT(p); } +static void vic4_draw_sprite_row_multicolor(int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale) +{ + const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; + for (int byte = 0; byte < totalBytes; ++byte) + { + for (int xbit = 0; xbit < 8; xbit += 2) + { + const Uint8 p0 = *row_data_ptr & (0x80 >> xbit); + const Uint8 p1 = *row_data_ptr & (0x40 >> xbit); + + Uint8 pixel = 0; // TODO: See generated code -- use lookup instead of branch? + if (!p0 && p1) + pixel = SPRITE_MULTICOLOR_1; + else if (p0 && !p1) + pixel = SPRITE_COLOR(sprnum); + else if (p0 && p1) + pixel = SPRITE_MULTICOLOR_2; + + for (int p = 0; p < xscale && x_display_pos < border_x_right; ++p, x_display_pos += 2) + { + if (pixel) + { + if (x_display_pos >= border_x_left && + (!SPRITE_IS_BACK(sprnum) || + (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL))) + { + *(pixel_raster_start + x_display_pos) = spritepalette[pixel]; + } + if (x_display_pos+1 >= border_x_left && + (!SPRITE_IS_BACK(sprnum) || + (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos + 1] != FOREGROUND_PIXEL))) + { + *(pixel_raster_start + x_display_pos + 1) = spritepalette[pixel]; + } + } + } + } + row_data_ptr++; + } +} -// VIC2 bitmap mode, now only HIRES mode (no MCM yet), without H640 VIC3 feature!! -// I am not even sure if H640 would work here, as it needs almost all the 16K of area what VIC-II can see, -// that is, not so much RAM for the video matrix left would be used for the attribute information. -// Note: VIC2 sees ROM at some addresses thing is not emulated yet! -static inline void vic2_render_screen_bmm ( Uint32 *p, int tail ) +static void vic4_draw_sprite_row_mono(int sprnum, int x_display_pos, const Uint8 *row_data_ptr, int xscale) { - int x = 0, y = 0, charline = 0; - Uint8 *vidp, *chrp; - vidp = main_ram + ((vic_registers[0x18] & 0xF0) << 6) + vic2_16k_bank; - sprite_pointers = vidp + 1016; - chrp = main_ram + ((vic_registers[0x18] & 8) ? 8192 : 0) + vic2_16k_bank; - PIXEL_POINTER_CHECK_INIT(p, tail, "vic2_render_screen_bmm"); - for (;;) { - Uint8 data = *(vidp++); - Uint32 bg = palette[data & 15]; - Uint32 fg = palette[data >> 4]; - data = *chrp; - chrp += 8; - PIXEL_POINTER_CHECK_ASSERT(p); - p[ 0] = p[ 1] = data & 128 ? fg : bg; - p[ 2] = p[ 3] = data & 64 ? fg : bg; - p[ 4] = p[ 5] = data & 32 ? fg : bg; - p[ 6] = p[ 7] = data & 16 ? fg : bg; - p[ 8] = p[ 9] = data & 8 ? fg : bg; - p[10] = p[11] = data & 4 ? fg : bg; - p[12] = p[13] = data & 2 ? fg : bg; - p[14] = p[15] = data & 1 ? fg : bg; - p += 16; - if (x == 39) { - p += tail; - x = 0; - if (charline == 7) { - if (y == 24) - break; - y++; - charline = 0; - chrp -= 7; - } else { - charline++; - vidp -= 40; - chrp -= 319; + const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; + for (int byte = 0; byte < totalBytes; ++byte) + { + for (int xbit = 0; xbit < 8; ++xbit) + { + const Uint8 pixel = *row_data_ptr & (0x80 >> xbit); + for (int p = 0; p < xscale && x_display_pos < border_x_right; ++p, ++x_display_pos) + { + if (x_display_pos >= border_x_left && + pixel && + (!SPRITE_IS_BACK(sprnum) || + (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL))) + { + *(pixel_raster_start + x_display_pos) = spritepalette[SPRITE_COLOR(sprnum)]; + } } - } else - x++; + } + row_data_ptr++; } - PIXEL_POINTER_FINAL_ASSERT(p); } +static void vic4_do_sprites() +{ + // Fetch and sequence sprites. + // + // NOTE about Text/Bitmap Graphics Background/foreground semantics: + // In multicolor mode (MCM=1), the bit combinations “00” and “01” belong to the background + // and “10” and “11” to the foreground whereas in standard mode (MCM=0), + // cleared pixels belong to the background and set pixels to the foreground. + // + for (int sprnum = 7; sprnum >= 0; --sprnum) + { + if (REG_SPRITE_ENABLE & (1 << sprnum)) + { + const int spriteHeight = SPRITE_EXTHEIGHT(sprnum) ? REG_SPRHGHT : 21; + int x_display_pos = border_x_left + ((SPRITE_POS_X(sprnum) - SPRITE_X_BASE_COORD) * (REG_SPR640 ? 1 : 2)); // in display units + int y_logical_pos = SPRITE_POS_Y(sprnum) - SPRITE_Y_BASE_COORD +(BORDER_Y_TOP / (REG_V400 ? 1 : 2)); // in logical units + + int sprite_row_in_raster = logical_raster - y_logical_pos; + + if (SPRITE_VERT_2X(sprnum)) + sprite_row_in_raster = sprite_row_in_raster >> 1; + + if (sprite_row_in_raster >= 0 && sprite_row_in_raster < spriteHeight) + { + const int widthBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; + const Uint8 *sprite_data_pointer = main_ram + SPRITE_POINTER_ADDR + sprnum * ((SPRITE_16BITPOINTER >> 7) + 1); + const Uint32 sprite_data_addr = SPRITE_16BITPOINTER ? + 64 * ((*(sprite_data_pointer + 1) << 8) | (*sprite_data_pointer)) + : ((64 * (*sprite_data_pointer)) | ( ((~last_dd00_bits) & 0x3)) << 14); + + //DEBUGPRINT("VIC: Sprite %d data at $%08X " NL, sprnum, sprite_data_addr); + const Uint8 *sprite_data = main_ram + sprite_data_addr; + const Uint8 *row_data = sprite_data + widthBytes * sprite_row_in_raster; + int xscale = (REG_SPR640 ? 1 : 2) * (SPRITE_HORZ_2X(sprnum) ? 2 : 1); + if (SPRITE_MULTICOLOR(sprnum)) + vic4_draw_sprite_row_multicolor(sprnum, x_display_pos, row_data, xscale); + else if (SPRITE_16COLOR(sprnum)) + vic4_draw_sprite_row_16color(sprnum, x_display_pos, row_data, xscale); + else + vic4_draw_sprite_row_mono(sprnum, x_display_pos, row_data, xscale); + } + } + } +} +// Render a monochrome character cell row +// flip = 00 Dont flip, 01 = flip vertical, 10 = flip horizontal, 11 = flip both -// Renderer for bit-plane mode -// NOTE: currently H1280 and V400 is NOT implemented -// Note: I still think that bitplanes are children of evil, my brain simply cannot get them -// takes hours and many confusions all the time, even if I *know* what they are :) -// And hey dude, if it's not enough, there is time multiplex of bitplanes (not supported), -// V400 + interlace odd/even scan addresses, and the original C64-like non-linear build-up -// of the bitplane structure. Phewwww .... -static inline void vic3_render_screen_bpm ( Uint32 *p, int tail ) +static void vic4_render_mono_char_row(Uint8 char_byte, int glyph_width, Uint8 bg_color, Uint8 fg_color, Uint8 vic3attr) { - int bitpos = 128, charline = 0, offset = 0; - int xlim, x = 0, y = 0, h640 = (vic_registers[0x31] & 128); - Uint8 bpe, *bp[8]; - bp[0] = vic_bitplane_starting_bank_p + ((vic_registers[0x33] & (h640 ? 12 : 14)) << 12); - bp[1] = vic_bitplane_starting_bank_p + ((vic_registers[0x34] & (h640 ? 12 : 14)) << 12) + 0x10000; - bp[2] = vic_bitplane_starting_bank_p + ((vic_registers[0x35] & (h640 ? 12 : 14)) << 12); - bp[3] = vic_bitplane_starting_bank_p + ((vic_registers[0x36] & (h640 ? 12 : 14)) << 12) + 0x10000; - bp[4] = vic_bitplane_starting_bank_p + ((vic_registers[0x37] & (h640 ? 12 : 14)) << 12); - bp[5] = vic_bitplane_starting_bank_p + ((vic_registers[0x38] & (h640 ? 12 : 14)) << 12) + 0x10000; - bp[6] = vic_bitplane_starting_bank_p + ((vic_registers[0x39] & (h640 ? 12 : 14)) << 12); - bp[7] = vic_bitplane_starting_bank_p + ((vic_registers[0x3A] & (h640 ? 12 : 14)) << 12) + 0x10000; - bpe = vic_registers[0x32]; // bit planes enabled mask - if (h640) { - bpe &= 15; // it seems, with H640, only 4 bitplanes can be used (on lower 4 ones) - xlim = 79; - sprite_pointers = bp[2] + 0x3FF8; // FIXME: just guessing - } else { - xlim = 39; - sprite_pointers = bp[2] + 0x1FF8; // FIXME: just guessing + if (vic3attr) + { + if (char_row == 7 && VIC3_ATTR_UNDERLINE(vic3attr)) + char_byte = 0xFF; + + if (VIC3_ATTR_REVERSE(vic3attr)) + char_byte = ~char_byte; + + if (VIC3_ATTR_BLINK(vic3attr) && vic4_blink_phase) + char_byte = VIC3_ATTR_REVERSE(vic3attr) ? ~char_byte : 0; + + if (VIC3_ATTR_BOLD(vic3attr)) + { + fg_color |= 0x10; + } } - DEBUG("VIC3: bitplanes: enable_mask=$%02X comp_mask=$%02X H640=%d" NL, - bpe, vic_registers[0x3B], h640 ? 1 : 0 - ); - PIXEL_POINTER_CHECK_INIT(p, tail, "vic3_render_screen_bpm"); - for (;;) { - Uint32 col = palette[(( // Do not try this at home ... - (((*(bp[0] + offset)) & bitpos) ? 1 : 0) | - (((*(bp[1] + offset)) & bitpos) ? 2 : 0) | - (((*(bp[2] + offset)) & bitpos) ? 4 : 0) | - (((*(bp[3] + offset)) & bitpos) ? 8 : 0) | - (((*(bp[4] + offset)) & bitpos) ? 16 : 0) | - (((*(bp[5] + offset)) & bitpos) ? 32 : 0) | - (((*(bp[6] + offset)) & bitpos) ? 64 : 0) | - (((*(bp[7] + offset)) & bitpos) ? 128 : 0) - ) & bpe) ^ vic_registers[0x3B] - ]; - PIXEL_POINTER_CHECK_ASSERT(p); - *(p++) = col; - if (!h640) { - PIXEL_POINTER_CHECK_ASSERT(p); - *(p++) = col; + + if (enable_bg_paint) + { + for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) + { + const Uint8 char_pixel = (char_byte & (0x80 >> (int)cx)); + Uint32 pixel_color = char_pixel ? palette[fg_color] : palette[bg_color]; + *(current_pixel++) = pixel_color; + bg_pixel_state[xcounter++] = char_pixel ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; } - if (bitpos == 1) { - if (x == xlim) { - if (charline == 7) { - if (y == 24) - break; - y++; - charline = 0; - offset -= 7; - } else { - charline++; - offset -= h640 ? 639 : 319; - } - p += tail; - x = 0; - } else - x++; - bitpos = 128; - offset += 8; - } else - bitpos >>= 1; } - PIXEL_POINTER_FINAL_ASSERT(p); + else // HACK!! to support MEGAMAZE GOTOX+VFLIP bits that ignore the background paint until + // next raster. + { + for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) + { + const Uint8 char_pixel = (char_byte & (0x80 >> (int)cx)); + if (char_pixel) + *current_pixel = palette[fg_color]; + + current_pixel++; + bg_pixel_state[xcounter++] = char_pixel ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; + } + } + } +static void vic4_render_multicolor_char_row(Uint8 char_byte, int glyph_width, const Uint8 color_source[4]) +{ + for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) + { + const Uint8 bitsel = 2 * (int)(cx / 2); + const Uint8 bit_pair = (char_byte & (0x80 >> bitsel)) >> (6-bitsel) | (char_byte & (0x40 >> bitsel)) >> (6-bitsel); + + Uint8 pixel = color_source[bit_pair]; + const Uint8 layer = bit_pair & 2 ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; + *(current_pixel++) = palette[pixel]; + bg_pixel_state[xcounter++] = layer; + } +} -#define SPRITE_X_START_SCREEN 24 -#define SPRITE_Y_START_SCREEN 50 - +// 8-bytes per row +static void vic4_render_fullcolor_char_row(const Uint8* char_row, int glyph_width) +{ + for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) + { + Uint32 pixel_color = palette[char_row[(int)cx]]; + *(current_pixel++) = pixel_color; + bg_pixel_state[xcounter++] = pixel_color ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; + } +} -#if 0 -/* Extremely incorrect sprite emulation! BUGS: - * Sprites cannot be behind the background (sprite priority) - * Multicolour sprites are not supported - * No sprite-background collision detection - * No sprite-sprite collision detection - * This is a simple, after-the-rendered-frame render-sprites one-by-one algorithm - * This also requires to give up direct rendering if a sprite is enabled - * Very ugly, quick&dirty hack, not so optimal either, even without the other mentioned bugs ... -*/ -static void render_sprite ( int sprite_no, int sprite_mask, Uint8 *data, Uint32 *p, int tail ) +// Render a bitplane-mode character cell row +// +static void vic4_render_bitplane_char_row(Uint8* bp_base[8], int glyph_width) { - int sprite_y = vic_registers[sprite_no * 2 + 1] - SPRITE_Y_START_SCREEN; - int sprite_x = ((vic_registers[sprite_no * 2] | ((vic_registers[16] & sprite_mask) ? 0x100 : 0)) - SPRITE_X_START_SCREEN) * 2; - Uint32 colour = palette[vic_registers[39 + sprite_no] & 15]; - int expand_x = vic_registers[29] & sprite_mask; - int expand_y = vic_registers[23] & sprite_mask; - int lim_y = sprite_y + ((expand_y) ? 42 : 21); - int y; - p += (640 + tail) * sprite_y; - for (y = sprite_y; y < lim_y; y += (expand_y ? 2 : 1), p += (640 + tail) * (expand_y ? 2 : 1)) - if (y < 0 || y >= 200) - data += 3; // skip one line (three bytes) of sprite data if outside of screen - else { - int mask, a, x = sprite_x; - for (a = 0; a < 3; a++) { - for (mask = 128; mask; mask >>= 1) { - if (*data & mask) { - if (x >= 0 && x < 640) { - p[x] = p[x + 1] = colour; - if (expand_y && y < 200) - p[x + 640 + tail] = p[x + 641 + tail] = colour; - } - x += 2; - if (expand_x && x >= 0 && x < 640) { - p[x] = p[x + 1] = colour; - if (expand_y && y < 200) - p[x + 640 + tail] = p[x + 641 + tail] = colour; - x += 2; - } - } else - x += expand_x ? 4 : 2; - } - data++; - } - } + const Uint8 bpe_mask = vic_registers[0x32] & (REG_H640 ? 15 : 255); + const Uint8 bp_comp = vic_registers[0x3B]; + + for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) + { + const Uint8 bitsel = 0x80 >> ((int)cx); + const Uint32 pixel_color = palette[ + ((((*bp_base[0] & bitsel) ? 1 : 0) | + ((*bp_base[1] & bitsel) ? 2 : 0) | + ((*bp_base[2] & bitsel) ? 4 : 0) | + ((*bp_base[3] & bitsel) ? 8 : 0) | + ((*bp_base[4] & bitsel) ? 16 : 0) | + ((*bp_base[5] & bitsel) ? 32 : 0) | + ((*bp_base[6] & bitsel) ? 64 : 0) | + ((*bp_base[7] & bitsel) ? 128 : 0)) & bpe_mask) ^ bp_comp]; + *(current_pixel++) = pixel_color; + bg_pixel_state[xcounter++] = *bp_base[2] & bitsel ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; + } } +void vic4_render_bitplane_raster() +{ + Uint8* bp_base[8]; + + // Get Bitplane source addresses + /* TODO: Cache the following reads & EA calculation */ + + const Uint32 offset = display_row * REG_CHRCOUNT * 8 + char_row ; + bp_base[0] = bitplane_bank_p + ((vic_registers[0x33] & (REG_H640 ? 12 : 14)) << 12) + offset; + bp_base[1] = bitplane_bank_p + ((vic_registers[0x34] & (REG_H640 ? 12 : 14)) << 12) + 0x10000 + offset; + bp_base[2] = bitplane_bank_p + ((vic_registers[0x35] & (REG_H640 ? 12 : 14)) << 12) + offset; + bp_base[3] = bitplane_bank_p + ((vic_registers[0x36] & (REG_H640 ? 12 : 14)) << 12) + 0x10000 + offset; + bp_base[4] = bitplane_bank_p + ((vic_registers[0x37] & (REG_H640 ? 12 : 14)) << 12) + offset; + bp_base[5] = bitplane_bank_p + ((vic_registers[0x38] & (REG_H640 ? 12 : 14)) << 12) + 0x10000 + offset; + bp_base[6] = bitplane_bank_p + ((vic_registers[0x39] & (REG_H640 ? 12 : 14)) << 12) + offset; + bp_base[7] = bitplane_bank_p + ((vic_registers[0x3A] & (REG_H640 ? 12 : 14)) << 12) + 0x10000 + offset; + + int line_char_index = 0; + while(line_char_index < REG_CHRCOUNT) + { + vic4_render_bitplane_char_row(bp_base, 8); + bp_base[0] += 8; + bp_base[1] += 8; + bp_base[2] += 8; + bp_base[3] += 8; + bp_base[4] += 8; + bp_base[5] += 8; + bp_base[6] += 8; + bp_base[7] += 8; + line_char_index++; + } -#else + if (++char_row > 7) + { + char_row = 0; + display_row++; + } -// kust temporaty to bridge the differences between my C65 emu (where I copy this code from) -// and current M65 emu implementation. This WILL change a lot in the future, the whole VIC-II/III/IV stuff ... -#define TOP_BORDER_SIZE 0 -#define LEFT_BORDER_SIZE 0 -//#define VIC_REG_COLOUR(n) palette[vic_registers[n] & 15] -#define VIC_REG_COLOUR(n) palette[vic_registers[n]] - -/* Extremely incorrect sprite emulation! BUGS: - * Sprites cannot be behind the background (sprite priority) - * No sprite-background collision detection - * No sprite-sprite collision detection - * This is a simple, after-the-rendered-frame render-sprites one-by-one algorithm - * Very ugly, quick&dirty hack, not so optimal either, even without the other mentioned bugs ... -*/ -static void render_sprite ( int sprite_no, int sprite_mask, Uint8 *data, Uint32 *p, int tail ) + while (xcounter++ < border_x_right) + *current_pixel++ = palette[REG_SCREEN_COLOR]; + +} + +// +// +// The character rendering engine. Most features are shared between +// all graphic modes. Basically, the VIC-IV supports the following character +// color modes: +// +// - Monochrome (Bg/Fg) +// - VICII Multicolor +// - 16-color +// - 256-color +// +// It's interesting to see that the four modes can be selected in +// bitmap or text modes. +// +// VIC-III Extended attributes are applied to characters if properly set, +// except in Multicolor modes. +// + +void vic4_render_char_raster() { - Uint32 colours[4]; - int sprite_y = vic_registers[sprite_no * 2 + 1] - SPRITE_Y_START_SCREEN; - int sprite_x = ((vic_registers[sprite_no * 2] | ((vic_registers[16] & sprite_mask) ? 0x100 : 0)) - SPRITE_X_START_SCREEN) * 2; - int expand_x = vic_registers[29] & sprite_mask; - int expand_y = vic_registers[23] & sprite_mask; - int lim_y = sprite_y + ((expand_y) ? 42 : 21); - int mcm = vic_registers[0x1C] & sprite_mask; - int y; - colours[2] = VIC_REG_COLOUR(39 + sprite_no); - if (mcm) { - colours[0] = 0; // transparent, not a real colour, just signaling of transparency - colours[1] = VIC_REG_COLOUR(0x25); - colours[3] = VIC_REG_COLOUR(0x26); - } - p += TEXTURE_WIDTH * (sprite_y + TOP_BORDER_SIZE) + LEFT_BORDER_SIZE; - for (y = sprite_y; y < lim_y; y += (expand_y ? 2 : 1), p += TEXTURE_WIDTH * (expand_y ? 2 : 1)) - if (y < 0 || y >= 200) - data += 3; // skip one line (three bytes) of sprite data if outside of screen - else { - int mask, a, x = sprite_x; - for (a = 0; a < 3; a++) { - if (mcm) { - for (mask = 6; mask >=0; mask -= 2) { - Uint32 col = colours[(*data >> mask) & 3]; - if (col) { - if (x >= 0 && x < 640) { - p[x] = p[x + 1] = p[x + 2] = p[x + 3] = col; - if (expand_y && y < 200) - p[x + TEXTURE_WIDTH] = p[x + TEXTURE_WIDTH + 1] = p[x + TEXTURE_WIDTH + 2] = p[x + TEXTURE_WIDTH + 3] = col; - } - x += 4; - if (expand_x && x >= 0 && x < 640) { - p[x] = p[x + 1] = p[x + 2] = p[x + 3] = col; - if (expand_y && y < 200) - p[x + TEXTURE_WIDTH] = p[x + TEXTURE_WIDTH + 1] = p[x + TEXTURE_WIDTH + 2] = p[x + TEXTURE_WIDTH + 3] = col; - x += 4; - } - } else - x += expand_x ? 8 : 4; - } - } else { - for (mask = 128; mask; mask >>= 1) { - if (*data & mask) { - if (x >= 0 && x < 640) { - p[x] = p[x + 1] = colours[2]; - if (expand_y && y < 200) - p[x + TEXTURE_WIDTH] = p[x + TEXTURE_WIDTH + 1] = colours[2]; - } - x += 2; - if (expand_x && x >= 0 && x < 640) { - p[x] = p[x + 1] = colours[2]; - if (expand_y && y < 200) - p[x + TEXTURE_WIDTH] = p[x + TEXTURE_WIDTH + 1] = colours[2]; - x += 2; - } - } else - x += expand_x ? 4 : 2; + int line_char_index = 0; + enable_bg_paint = 1; + + // Account for negative Y-displacement (positive is taken into account in outer-loop) + + const int row_offset = (BORDER_Y_TOP - CHARGEN_Y_START) / 8; + const int adj_display_row = row_offset + display_row; + + if (adj_display_row >= 0 && adj_display_row < display_row_count) + { + const int char_row_offset = (BORDER_Y_TOP - CHARGEN_Y_START) % 8; + colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET + (adj_display_row * CHARSTEP_BYTES); + screen_ram_current_ptr = main_ram + SCREEN_ADDR + (adj_display_row * CHARSTEP_BYTES); + const Uint8* row_data_base_addr = main_ram + (REG_BMM ? VIC2_BITMAP_ADDR : get_charset_effective_addr()); + + // Account for Chargen X-displacement + + for(Uint32* p = current_pixel; p < current_pixel + (CHARGEN_X_START - border_x_left); ++p) + *p = palette[REG_SCREEN_COLOR]; + + current_pixel += (CHARGEN_X_START - border_x_left); + xcounter += (CHARGEN_X_START - border_x_left); + const int xcounter_start = xcounter; + + // Chargen starts here. + + while (line_char_index < REG_CHRCOUNT) + { + Uint16 color_data = *(colour_ram_current_ptr++); + Uint16 char_value = *(screen_ram_current_ptr++); + + if (REG_16BITCHARSET) + { + color_data = (color_data << 8) | (*(colour_ram_current_ptr++)); + char_value = char_value | (*(screen_ram_current_ptr++) << 8); + + if (SXA_GOTO_X(color_data)) + { + current_pixel = pixel_raster_start + xcounter_start + (char_value & 0x3FF); + xcounter = xcounter_start + (char_value & 0x3FF); + line_char_index++; + + if (SXA_VERTICAL_FLIP(color_data)) + { + enable_bg_paint = 0; } + + continue; + } + } + + // Background and foreground colors + + const Uint8 char_fgcolor = color_data & 0xF; + const Uint8 vic3_attr = REG_VICIII_ATTRIBS && !REG_MCM ? (color_data >> 4) : 0; + const Uint16 char_id = REG_EBM ? (char_value & 0x3f) : char_value & 0x1fff; // up to 8192 characters (13-bit) + const Uint8 char_bgcolor = REG_EBM ? vic_registers[0x21 + ((char_value >> 6) & 3)] : REG_SCREEN_COLOR; + + // Calculate character-width + + Uint8 glyph_width_deduct = SXA_TRIM_RIGHT_BITS012(char_value) + (SXA_TRIM_RIGHT_BIT3(char_value) ? 8 : 0); + Uint8 glyph_width = (SXA_4BIT_PER_PIXEL(color_data) ? 16 : 8) - glyph_width_deduct; + + // Default fetch from char mode. + Uint8 char_byte; + int sel_char_row = char_row + char_row_offset; + + if (SXA_VERTICAL_FLIP(color_data)) + sel_char_row = 7 - char_row + char_row_offset; + + if (REG_BMM) + { + char_byte = *(row_data_base_addr + display_row * 320 + 8 * line_char_index + sel_char_row); + } + else + { + char_byte = *(row_data_base_addr + (char_id * 8) + sel_char_row); + } + + if (SXA_HORIZONTAL_FLIP(color_data)) + char_byte = reverse_byte(char_byte); + + // Render character cell row + + if (SXA_4BIT_PER_PIXEL(color_data)) // 16-color character + { + + } + else if (CHAR_IS256_COLOR(char_id)) // 256-color character + { + vic4_render_fullcolor_char_row(main_ram + (((char_id * 64) + (sel_char_row * 8) ) & 0x7FFFF), 8); + } + else if ((REG_MCM && (char_fgcolor & 8)) || (REG_MCM && REG_BMM)) // Multicolor character + { + if (REG_BMM) + { + const Uint8 color_source[4] = { + REG_SCREEN_COLOR, //00 + char_value >> 4, //01 + char_value & 0xF, //10 + color_data & 0xF //11 + }; + vic4_render_multicolor_char_row(char_byte, glyph_width, color_source); + } + else + { + const Uint8 color_source[4] = { + REG_SCREEN_COLOR, //00 + REG_MULTICOLOR_1, //01 + REG_MULTICOLOR_2, //10 + char_fgcolor & 7 //11 + }; + vic4_render_multicolor_char_row(char_byte, glyph_width, color_source); } - data++; } + else // Single color character + { + if (!REG_BMM) + { + vic4_render_mono_char_row(char_byte, glyph_width, char_bgcolor, char_fgcolor, vic3_attr); + } + else + { + vic4_render_mono_char_row(char_byte, glyph_width, char_value & 0xF, char_value >> 4, vic3_attr ); + } + } + line_char_index++; } -} + } + if (++char_row > 7) + { + char_row = 0; + display_row++; + } -#endif + // Fill screen color after chargen phase + while (xcounter++ < border_x_right) + *current_pixel++ = palette[REG_SCREEN_COLOR]; +} -/* This is the one-frame-at-once (highly incorrect implementation, that is) - renderer. It will call legacy VIC2 text mode render (optionally with - 80 columns mode, though, ECM, MCM, hardware attributes are not supported), - VIC2 legacy HIRES mode (MCM is not supported), or bitplane modes (V400, - H1280, odd scanning/interlace is not supported). Sprites, screen positioning, - etc is not supported */ -void vic_render_screen ( void ) +int vic4_render_scanline() { - int tail_sdl; - Uint32 *p_sdl = xemu_start_pixel_buffer_access(&tail_sdl); - int sprites = vic_registers[0x15]; - if (vic_registers[0x31] & 16) { - sprite_bank = main_ram + ((vic_registers[0x35] & 12) << 12); // FIXME: just guessing: sprite bank is bitplane 2 area, always 16K regardless of H640? - vic3_render_screen_bpm(p_sdl, tail_sdl); - } else { - sprite_bank = vic2_16k_bank + main_ram; // VIC2 legacy modes uses the VIC2 bank for sure, as the sprite bank too - if (vic_registers[0x11] & 32) - vic2_render_screen_bmm(p_sdl, tail_sdl); - else - vic2_render_screen_text(p_sdl, tail_sdl); + // Work this first. DO NOT OPTIMIZE EARLY. + + xcounter = 0; + current_pixel = pixel_start + ycounter * SCREEN_WIDTH; + pixel_raster_start = current_pixel; + + SET_PHYSICAL_RASTER(ycounter); + logical_raster = ycounter >> (REG_V400 ? 0 : 1); + + if (!(ycounter & 1)) // VIC-II raster source: We shall check FNRST ? + vic4_check_raster_interrupt(logical_raster); + + // "Double-scan hack" + if (!REG_V400 && (ycounter & 1)) + { + for (int i = 0; i < SCREEN_WIDTH; i++, current_pixel++) + *current_pixel = /* user_scanlines_setting ? 0 : */ *(current_pixel - SCREEN_WIDTH) ; } - if (sprites) { // Render sprites. VERY BAD. We ignore sprite priority as well (cannot be behind the background) - //if (warn_sprites) { - // INFO_WINDOW("WARNING: Sprite emulation is really bad! (enabled_mask=$%02X)", sprites); - // warn_sprites = 0; - //} - for (int a = 7; a >= 0; a--) { - int mask = 1 << a; - if ((sprites & mask)) - render_sprite(a, mask, sprite_bank + (sprite_pointers[a] << 6), p_sdl, tail_sdl); // sprite_pointers are set by the renderer functions above! + else + { + + // Top and bottom borders + + if (ycounter < BORDER_Y_TOP || ycounter >= BORDER_Y_BOTTOM || !REG_DISPLAYENABLE) + { + for (int i = 0; i < SCREEN_WIDTH; ++i) + *(current_pixel++) = palette[REG_BORDER_COLOR & 0xF]; + } + else + { + // Render visible display first and render side-borders later to cover X-displaced + // character generator if needed. + + xcounter += border_x_left; + current_pixel += border_x_left; + + vic4_raster_renderer_path(); + vic4_do_sprites(); + + for (Uint32 *p = pixel_raster_start; p < pixel_raster_start + border_x_left; ++p) + *p = palette[REG_BORDER_COLOR & 0xF]; + + for (Uint32 *p = current_pixel; p < current_pixel + border_x_right; ++p) + *p = palette[REG_BORDER_COLOR & 0xF]; } } -#ifdef XEMU_FILES_SCREENSHOT_SUPPORT - // Screenshot - if (XEMU_UNLIKELY(register_screenshot_request)) { - register_screenshot_request = 0; - if (!xemu_screenshot_png( - NULL, NULL, - 1, - 2, - NULL, // allow function to figure it out ;) - TEXTURE_WIDTH, - TEXTURE_HEIGHT, - TEXTURE_WIDTH - )) { - const char *p = strrchr(xemu_screenshot_full_path, DIRSEP_CHR); - if (p) - OSD(-1, -1, "%s", p + 1); + ycounter++; + + // End of frame? + if (ycounter == max_rasters) + { + vic4_reset_display_counters(); + + screen_ram_current_ptr = main_ram + SCREEN_ADDR; + colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET; + frame_counter++; + if (frame_counter == VIC4_BLINK_INTERVAL) + { + frame_counter = 0; + vic4_blink_phase = !vic4_blink_phase; } + return 1; } -#endif - if (configdb.show_drive_led && fdc_get_led_state(16)) - for (int y = 0; y < 8; y++) - for (int x = 0; x < 8; x++) - *(p_sdl + (TEXTURE_WIDTH) - 10 + x + (y + 2) * (TEXTURE_WIDTH)) = x > 1 && x < 7 && y > 1 && y < 7 ? red_colour : black_colour; - xemu_update_screen(); -} + return 0; +} /* --- SNAPSHOT RELATED --- */ diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index 3267a220..1cf86617 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -1,6 +1,7 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2021 LGB (Gábor Lénárt) + Copyright (C)2016,2017 LGB (Gábor Lénárt) + Copyright (C)2020 Hernán Di Pietro This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,31 +20,218 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef XEMU_MEGA65_VIC4_H_INCLUDED #define XEMU_MEGA65_VIC4_H_INCLUDED +#include + #define VIC2_IOMODE 0 #define VIC3_IOMODE 1 #define VIC_BAD_IOMODE 2 #define VIC4_IOMODE 3 -#define USE_HOTREGS (vic_registers[0x5D] & 0x80) +// +// Output window is fixed at 800x600 to support MegaPHONE, PAL-MEGA65 +// and NTSC_MEGA65 modes. Internally, the VIC-IV chip draws 800-pixel +// wide buffers and traverses up to 526/624 physical rasters, as we do here. +// The user can select a clipped borders view (called "normal borders") which shows +// the real visible resolution of PAL (720x576) or NSTC(720x480). +// + +#define SCREEN_WIDTH 800 +#define SCREEN_HEIGHT 600 +#define PHYSICAL_RASTERS_DEFAULT PHYSICAL_RASTERS_NTSC +#define SCREEN_HEIGHT_VISIBLE_DEFAULT SCREEN_HEIGHT_VISIBLE_NTSC +#define SCREEN_HEIGHT_VISIBLE_NTSC 480 +#define SCREEN_HEIGHT_VISIBLE_PAL 576 +#define PHYSICAL_RASTERS_NTSC 526 +#define PHYSICAL_RASTERS_PAL 624 +#define NTSC_RATIO PHYSICAL_RASTERS_NTSC / float(SCREEN_WIDTH) +#define PAL_RATIO PHYSICAL_RASTERS_PAL / float(SCREEN_WIDTH) +#define FRAME_H_FRONT 0 +#define RASTER_CORRECTION 3 +#define VIC4_BLINK_INTERVAL 25 + +// Register defines +// +// Ref: +// https://github.com/MEGA65/mega65-core/blob/138-hdmi-audio-27mhz/iomap.txt +// ---------------------------------------------------- +// _Un suffix indicates upper n bits of register +// +#define REG_EBM (vic_registers[0x11] & 0x40) +#define REG_MCM (vic_registers[0x16] & 0x10) +#define REG_BMM (vic_registers[0x11] & 0x20) +#define REG_SPRITE_ENABLE vic_registers[0x15] +#define REG_BORDER_COLOR vic_registers[0x20] +#define REG_SCREEN_COLOR vic_registers[0x21] +#define REG_MULTICOLOR_1 vic_registers[0x22] +#define REG_MULTICOLOR_2 vic_registers[0x23] +#define REG_MULTICOLOR_3 vic_registers[0x24] +#define REG_H640 (vic_registers[0x31] & 128) +#define REG_V400 (vic_registers[0x31] & 8) +#define REG_VICIII_ATTRIBS (vic_registers[0x31] & 0x20) +#define REG_RSEL (vic_registers[0x11] & 8) +#define REG_CSEL (vic_registers[0x16] & 8) +#define REG_DISPLAYENABLE (vic_registers[0x11] & 0x10) +#define REG_VIC2_XSCROLL (vic_registers[0x16] & 7) +#define REG_VIC2_YSCROLL (vic_registers[0x11] & 7) +#define REG_CRAM2K (vic_registers[0x30] & 1) +#define REG_TBRDPOS (vic_registers[0x48]) +#define REG_SPRBPMEN_0_3 (vic_registers[0x49] >> 4) +#define REG_SPRBPMEN_4_7 (vic_registers[0x4B] >> 4) +#define REG_TBRDPOS_U4 (vic_registers[0x49] & 0xF) +#define REG_BBRDPOS (vic_registers[0x4A]) +#define REG_BBRDPOS_U4 (vic_registers[0x4B] & 0xF) +#define REG_TEXTXPOS (vic_registers[0x4C]) +#define REG_TEXTXPOS_U4 (vic_registers[0x4D] & 0xF) +#define REG_TEXTYPOS (vic_registers[0x4E]) +#define REG_TEXTYPOS_U4 (vic_registers[0x4F] & 0xF) +#define REG_XPOS (vic_registers[0x51]) +#define REG_XPOS_U6 (vic_registers[0x50] & 0x3F) +#define REG_FNRST (vic_registers[0x53] & 0x80) +#define REG_16BITCHARSET (vic_registers[0x54] & 1) +#define REG_FCLRLO (vic_registers[0x54] & 2) +#define REG_FCLRHI (vic_registers[0x54] & 4) +#define REG_SPR640 (vic_registers[0x54] & 0x10) +#define REG_SPRHGHT (vic_registers[0x56]) +#define REG_CHRXSCL (vic_registers[0x5A]) +#define REG_CHRYSCL (vic_registers[0x5B]) +#define REG_SIDBDRWD (vic_registers[0x5C]) +#define REG_SIDBDRWD_U5 (vic_registers[0x5D] & 0x3F) +#define REG_HOTREG (vic_registers[0x5D] & 0x80) +#define REG_CHARSTEP vic_registers[0x58] +#define REG_CHARSTEP_U8 vic_registers[0x59] +#define REG_CHARXSCALE vic_registers[0x5A] +#define REG_CHRCOUNT vic_registers[0x5E] +#define REG_SCRNPTR_B0 vic_registers[0x60] +#define REG_SCRNPTR_B1 vic_registers[0x61] +#define REG_SCRNPTR_B2 vic_registers[0x62] +#define REG_SCRNPTR_B3 (vic_registers[0x63] & 0xF) +#define REG_COLPTR vic_registers[0x64] +#define REG_COLPTR_MSB vic_registers[0x65] +#define REG_CHARPTR_B0 vic_registers[0x68] +#define REG_CHARPTR_B1 vic_registers[0x69] +#define REG_CHARPTR_B2 vic_registers[0x6A] +#define REG_SPRPTR_B0 vic_registers[0x6C] +#define REG_SPRPTR_B1 vic_registers[0x6D] +#define REG_SPRPTR_B2 (vic_registers[0x6E] & 0x7F) +#define REG_SCREEN_ROWS vic_registers[0x7B] +#define REG_PALNTSC (vic_registers[0x6f] & 0x80) +#define REG_PAL_RED_BASE (vic_registers[0x100]) +#define REG_PAL_GREEN_BASE (vic_registers[0x200]) +#define REG_PAL_BLUE_BASE (vic_registers[0x300]) + + +// Helper macros for accessing multi-byte registers +// and other similar functionality for convenience +// ----------------------------------------------------- +#define PHYS_RASTER_COUNT (REG_PALNTSC ? NTSC_PHYSICAL_RASTERS : PAL_PHYSICAL_RASTERS) +#define SINGLE_SIDE_BORDER (((Uint16)REG_SIDBDRWD) | (REG_SIDBDRWD_U5) << 8) +#define BORDER_Y_TOP (((Uint16)REG_TBRDPOS) | (REG_TBRDPOS_U4) << 8) +#define BORDER_Y_BOTTOM (((Uint16)REG_BBRDPOS) | (REG_BBRDPOS_U4) << 8) +#define CHARGEN_Y_START (((Uint16)REG_TEXTYPOS) | (REG_TEXTYPOS_U4) << 8) +#define CHARGEN_X_START (((Uint16)REG_TEXTXPOS) | (REG_TEXTXPOS_U4) << 8) +#define CHARSTEP_BYTES (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) +#define SCREEN_RAM_ADDR_VIC (REG_SCREEN_ADDR * 1024) +#define SCREEN_ADDR ((Uint32)REG_SCRNPTR_B0 | (REG_SCRNPTR_B1<<8) | (REG_SCRNPTR_B2 <<16) | (REG_SCRNPTR_B3 << 24)) +#define CHARSET_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) +#define VIC2_BITMAP_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) +#define SPRITE_POINTER_ADDR ((Uint32)REG_SPRPTR_B0 | (REG_SPRPTR_B1<<8) | (REG_SPRPTR_B2 <<16)) +#define COLOUR_RAM_OFFSET ((((Uint16)REG_COLPTR) | (REG_COLPTR_MSB) << 8)) +#define IS_NTSC_MODE (REG_PALNTSC ^ 0x80) +#define SCREEN_STEP (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) +#define SPRITE_POS_Y(n) (vic_registers[1 + (n)*2]) +#define SPRITE_POS_X(n) (((Uint16)vic_registers[(n)*2]) | ( (vic_registers[0x10] & (1 << (n)) ? 0x100 : 0))) +#define SPRITE_COLOR(n) (vic_registers[0x27+(n)] & 0xF) +#define SPRITE_MULTICOLOR_1 (vic_registers[0x25] & 0xF) +#define SPRITE_MULTICOLOR_2 (vic_registers[0x26] & 0xF) +#define SPRITE_IS_BACK(n) (vic_registers[0x1B] & (1 << (n))) +#define SPRITE_HORZ_2X(n) (vic_registers[0x1D] & (1 << (n))) +#define SPRITE_VERT_2X(n) (vic_registers[0x17] & (1 << (n))) +#define SPRITE_MULTICOLOR(n) (vic_registers[0x1C] & (1 << (n))) +#define SPRITE_16COLOR(n) (vic_registers[0x6B] & (1 << (n))) +#define SPRITE_EXTWIDTH(n) (SPRITE_16COLOR(n) | (vic_registers[0x57] & (1 << (n)))) +#define SPRITE_EXTHEIGHT(n) (vic_registers[0x55] & (1 << (n))) +#define SPRITE_BITPLANE_ENABLE(n) (((REG_SPRBPMEN_4_7) << 4 | REG_SPRBPMEN_0_3) & (1 << (n))) +#define SPRITE_16BITPOINTER (vic_registers[0x6E] & 0x80) +#define TEXT_MODE (!REG_BMM) +#define HIRES_BITMAP_MODE (REG_BMM & !REG_MCM & !REG_EBM) +#define MULTICOLOR_BITMAP_MODE (REG_BMM & REG_MCM & !REG_EBM) +#define VIC3_ATTR_BLINK(c) ((c) & 0x1) +#define VIC3_ATTR_REVERSE(c) ((c) & 0x2) +#define VIC3_ATTR_BOLD(c) ((c) & 0x4) +#define VIC3_ATTR_UNDERLINE(c) ((c) & 0x8) +#define CHAR_IS256_COLOR(ch) (REG_FCLRLO && (ch) < 0x100) || (REG_FCLRHI && (ch) > 0x0FF) + +// "Super-Extended character attributes" (see https://github.com/MEGA65/mega65-core/blob/master/docs/viciv-modes.md) +// cw is color-word (16-bit from Color RAM). sw is screen-ram-word (16bit from Screen RAM) +#define SXA_TRIM_RIGHT_BITS012(sw) ((sw) >> 13) +#define SXA_VERTICAL_FLIP(cw) ((cw) & 0x8000) +#define SXA_HORIZONTAL_FLIP(cw) ((cw) & 0x4000) +#define SXA_ALPHA_BLEND(cw) ((cw) & 0x2000) +#define SXA_GOTO_X(cw) ((cw) & 0x1000) +#define SXA_4BIT_PER_PIXEL(cw) ((cw) & 0x0800) +#define SXA_TRIM_RIGHT_BIT3(cw) ((cw) & 0x0400) +#define SXA_TRIM_TOP_BOTTOM(cw) ((cw) & 0x0300) >> 8) + + +// Multi-byte register write helpers +// --------------------------------------------------- +#define SET_11BIT_REG(basereg,x) vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0x700) >> 8); \ + vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; +#define SET_12BIT_REG(basereg,x) vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0xF00) >> 8); \ + vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; +#define SET_14BIT_REG(basereg,x) vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ + vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; +#define SET_14BIT_REGI(basereg,x) vic_registers[(basereg)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ + vic_registers[((basereg)+1)] = (Uint8) ((Uint16)(x)) & 0x00FF; +#define SET_16BIT_REG(basereg,x) vic_registers[((basereg) + 1)] = (Uint8) ((((Uint16)(x)) & 0xFF00) >> 8); \ + vic_registers[(basereg)]= ((Uint16)(x)) & 0x00FF; + +// 11-bit registers + +#define SET_PHYSICAL_RASTER(x) SET_11BIT_REG(0x52, (x)) +#define SET_RASTER_XPOS(x) SET_14BIT_REGI(0x50, (x)) + +// 12-bit registers + + +#define SET_BORDER_Y_TOP(x) SET_12BIT_REG(0x48, (x)) +#define SET_BORDER_Y_BOTTOM(x) SET_12BIT_REG(0x4A, (x)) +#define SET_CHARGEN_X_START(x) SET_12BIT_REG(0x4C, (x)) +#define SET_CHARGEN_Y_START(x) SET_12BIT_REG(0x4E, (x)) + +//16-bit registers + +#define SET_COLORRAM_BASE(x) SET_16BIT_REG(0x64,(x)) +#define SET_CHARSTEP_BYTES(x) SET_16BIT_REG(0x58,(x)) + +// Pixel foreground/background indicator for aiding in sprite rendering +#define FOREGROUND_PIXEL 1 +#define BACKGROUND_PIXEL 0 + +// Review this! (VIC-II values) +#define SPRITE_X_BASE_COORD 24 +#define SPRITE_Y_BASE_COORD 50 +#define SPRITE_X_UPPER_COORD 250 +#define SPRITE_Y_UPPER_COORD 344 + +// Current state +// ------------- extern int vic_iomode; extern int scanline; extern Uint8 vic_registers[]; extern int cpu_cycles_per_scanline; extern int vic2_16k_bank; -extern int vic3_blink_phase; +extern int force_fast; extern Uint8 c128_d030_reg; extern int vic_vidp_legacy, vic_chrp_legacy, vic_sprp_legacy; -extern const char *iomode_names[4]; - extern void vic_init ( void ); -extern void vic_reset ( void ); extern void vic_write_reg ( unsigned int addr, Uint8 data ); extern Uint8 vic_read_reg ( unsigned int addr ); -extern void vic_render_screen ( void ); -extern void vic3_check_raster_interrupt ( void ); +extern int vic4_render_scanline ( void ); +extern void vic4_open_frame_access(); #ifdef XEMU_SNAPSHOT_SUPPORT #include "xemu/emutools_snapshot.h" From 31d1dc15a406bc3b15353adebbf884a844778338 Mon Sep 17 00:00:00 2001 From: lgblgblgb Date: Wed, 22 Sep 2021 15:57:20 +0200 Subject: [PATCH 02/35] MEGA65: VIC-IV update reformat/etc #29 --- .github/workflows/{main.yml => main.yml.OFF} | 0 .travis.yml => .travis.yml.OFF | 0 targets/mega65/vic4.c | 860 +++++++++---------- targets/mega65/vic4.h | 340 ++++---- 4 files changed, 576 insertions(+), 624 deletions(-) rename .github/workflows/{main.yml => main.yml.OFF} (100%) rename .travis.yml => .travis.yml.OFF (100%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml.OFF similarity index 100% rename from .github/workflows/main.yml rename to .github/workflows/main.yml.OFF diff --git a/.travis.yml b/.travis.yml.OFF similarity index 100% rename from .travis.yml rename to .travis.yml.OFF diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 80c34e48..fc185539 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -1,7 +1,7 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu Copyright (C)2016-2021 LGB (Gábor Lénárt) - Copyright (C)2020 Hernán Di Pietro + Copyright (C)2020-2021 Hernán Di Pietro This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,67 +24,70 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "vic4.h" #include "vic4_palette.h" #include "memory_mapper.h" -#include - -extern int in_hypervisor; +#include "hypervisor.h" +//#include static const char *iomode_names[4] = { "VIC2", "VIC3", "BAD!", "VIC4" }; // (SDL) target texture rendering pointers -static Uint32 *current_pixel; // current_pixel pointer to the rendering target (one current_pixel: 32 bit) -static Uint32 *pixel_end, *pixel_start; // points to the end and start of the buffer -static Uint32 *pixel_raster_start; // first pixel of current raster -Uint8 vic_registers[0x80]; // VIC-3 registers. It seems $47 is the last register. But to allow address the full VIC3 reg I/O space, we use $80 here -int vic_iomode; // VIC2/VIC3/VIC4 mode -int force_fast; // POKE 0,64 and 0,65 trick ... -Uint8 vic_registers[0x80]; // VIC-4 registers +static Uint32 *current_pixel; // current_pixel pointer to the rendering target (one current_pixel: 32 bit) +static Uint32 *pixel_end, *pixel_start; // points to the end and start of the buffer +static Uint32 *pixel_raster_start; // first pixel of current raster +Uint8 vic_registers[0x80]; // VIC-3 registers. It seems $47 is the last register. But to allow address the full VIC3 reg I/O space, we use $80 here int vic_iomode; // VIC2/VIC3/VIC4 mode int force_fast; // POKE 0,64 and 0,65 trick ... int scanline; // current scan line number int cpu_cycles_per_scanline; -static int compare_raster; // raster compare (9 bits width) data +static int compare_raster; // raster compare (9 bits width) data static int logical_raster = 0; -static int interrupt_status; // Interrupt status of VIC -static int vic4_blink_phase = 0; // blinking attribute helper, state. -Uint8 c128_d030_reg; // C128-like register can be only accessed in VIC-II mode but not in others, quite special! -static Uint8 reg_d018_screen_addr = 0; // Legacy VIC-II $D018 screen address register -static int vic_hotreg_touched = 0; // If any "legacy" registers were touched -static int vic4_sideborder_touched = 0; // If side-border register were touched -static int border_x_left= 0; // Side border left -static int border_x_right= 0; // Side border right -static int xcounter = 0, ycounter = 0; // video counters +static int interrupt_status; // Interrupt status of VIC +static int vic4_blink_phase = 0; // blinking attribute helper, state. +Uint8 c128_d030_reg; // C128-like register can be only accessed in VIC-II mode but not in others, quite special! +static Uint8 reg_d018_screen_addr = 0; // Legacy VIC-II $D018 screen address register +static int vic_hotreg_touched = 0; // If any "legacy" registers were touched +static int vic4_sideborder_touched = 0; // If side-border register were touched +static int border_x_left= 0; // Side border left +static int border_x_right= 0; // Side border right +static int xcounter = 0, ycounter = 0; // video counters static int frame_counter = 0; static int char_row = 0, display_row = 0; -static Uint8 bg_pixel_state[1024]; // See FOREGROUND_PIXEL and BACKGROUND_PIXEL constants +static Uint8 bg_pixel_state[1024]; // See FOREGROUND_PIXEL and BACKGROUND_PIXEL constants static Uint8* screen_ram_current_ptr = NULL; static Uint8* colour_ram_current_ptr = NULL; -extern int user_scanlines_setting; +int user_scanlines_setting = 0; float char_x_step = 0.0; static int enable_bg_paint = 1; static int display_row_count = 0; static int max_rasters = PHYSICAL_RASTERS_DEFAULT; static int visible_area_height = SCREEN_HEIGHT_VISIBLE_DEFAULT; -static int vicii_first_raster = 7; // Default for NTSC +static int vicii_first_raster = 7; // Default for NTSC static Uint8 *bitplane_bank_p = main_ram; +// --- these things are altered by vic4_open_frame_access() ONLY at every fame ONLY based on PAL or NTSC selection +Uint8 videostd_id = 0xFF; // 0=PAL, 1=NTSC [give some insane value by default to force the change at the fist frame after starting Xemu] +const char *videostd_name = ""; // PAL or NTSC, however initially is not yet set +int videostd_frametime = NTSC_FRAME_TIME; // time in microseconds for a frame to produce +static const char NTSC_STD_NAME[] = "NTSC"; +static const char PAL_STD_NAME[] = "PAL"; + void vic4_render_char_raster(); void vic4_render_bitplane_raster(); -static void (* vic4_raster_renderer_path)(void) = &vic4_render_char_raster; +static void (*vic4_raster_renderer_path)(void) = &vic4_render_char_raster; // VIC-IV Modeline Parameters // ---------------------------------------------------- #define DISPLAY_HEIGHT ((max_rasters-1)-20) -#define TEXT_HEIGHT_200 400 -#define TEXT_HEIGHT_400 400 -#define CHARGEN_Y_SCALE_200 2 -#define CHARGEN_Y_SCALE_400 1 +#define TEXT_HEIGHT_200 400 +#define TEXT_HEIGHT_400 400 +#define CHARGEN_Y_SCALE_200 2 +#define CHARGEN_Y_SCALE_400 1 #define chargen_y_pixels 0 -#define TOP_BORDERS_HEIGHT_200 (DISPLAY_HEIGHT - TEXT_HEIGHT_200) -#define TOP_BORDERS_HEIGHT_400 (DISPLAY_HEIGHT - TEXT_HEIGHT_400) -#define SINGLE_TOP_BORDER_200 (TOP_BORDERS_HEIGHT_200 >> 1) -#define SINGLE_TOP_BORDER_400 (TOP_BORDERS_HEIGHT_400 >> 1) +#define TOP_BORDERS_HEIGHT_200 (DISPLAY_HEIGHT - TEXT_HEIGHT_200) +#define TOP_BORDERS_HEIGHT_400 (DISPLAY_HEIGHT - TEXT_HEIGHT_400) +#define SINGLE_TOP_BORDER_200 (TOP_BORDERS_HEIGHT_200 >> 1) +#define SINGLE_TOP_BORDER_400 (TOP_BORDERS_HEIGHT_400 >> 1) -#define MAX(a,b) ((a)>(b)?(a):(b)) +//#define MAX(a,b) ((a)>(b)?(a):(b)) //#define CHECK_PIXEL_POINTER @@ -118,53 +121,48 @@ static inline void PIXEL_POINTER_FINAL_ASSERT ( Uint32 *p ) # define PIXEL_POINTER_FINAL_ASSERT(p) #endif -// Lookup-based bit reversal. (TODO: Move this to a proper file) +static const Uint8 reverse_byte_table[] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, //0 + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, //8 + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, //16 + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, //24 + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, //32 + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, //40 + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, //48 + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, //56 + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, //64 + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, //72 + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, //80 + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, //88 + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, //96 + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, //104 + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, //112 + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, //120 + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, //128 + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +}; -static inline Uint8 reverse_byte(unsigned char x) -{ - static const Uint8 table[] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, //0 - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, //8 - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, //16 - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, //24 - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, //32 - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, //40 - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, //48 - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, //56 - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, //64 - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, //72 - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, //80 - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, //88 - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, //96 - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, //104 - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, //112 - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, //120 - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, //128 - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, - }; - return table[x]; -} void vic_init ( void ) { vic4_init_palette(); force_fast = 0; - // *** Init VIC3 registers and palette + // *** Init VIC4 registers and palette vic_iomode = VIC2_IOMODE; interrupt_status = 0; scanline = 0; @@ -177,7 +175,7 @@ void vic_init ( void ) } c128_d030_reg = 0xFE; // this may be set to 2MHz in the previous step, so be sure to set to FF here, BUT FIX: bit 0 should be inverted!! machine_set_speed(0); - + screen_ram_current_ptr = main_ram + SCREEN_ADDR; colour_ram_current_ptr = colour_ram; @@ -185,21 +183,23 @@ void vic_init ( void ) } +#if 0 // This function allows to switch between NTSC/PAL on-the-fly (NTSC = 1. PAL = 0) void vic4_switch_display_mode(int ntsc) { DEBUGPRINT("VIC: switch_display_mode NTSC=%d" NL, ntsc); xemu_change_display_mode(SCREEN_WIDTH, ntsc ? PHYSICAL_RASTERS_NTSC : PHYSICAL_RASTERS_PAL, // texture sizes - SCREEN_WIDTH, SCREEN_HEIGHT,// logical size (used with keeping aspect ratio by the SDL render stuffs) - SCREEN_WIDTH, SCREEN_HEIGHT,// window size + SCREEN_WIDTH, SCREEN_HEIGHT, // logical size (used with keeping aspect ratio by the SDL render stuffs) + SCREEN_WIDTH, SCREEN_HEIGHT, // window size SCREEN_FORMAT, - USE_LOCKED_TEXTURE); - + USE_LOCKED_TEXTURE + ); if(!xemucfg_get_bool("fullborders")) xemu_set_viewport(48, 32, SCREEN_WIDTH - 48, SCREEN_HEIGHT, 1); - vic4_open_frame_access(); } +#endif + void vic4_open_frame_access() @@ -209,8 +209,52 @@ void vic4_open_frame_access() pixel_end = current_pixel + (SCREEN_WIDTH * max_rasters); if (tail_sdl) FATAL("tail_sdl is not zero!"); + // Now check the video mode: NTSC or PAL + // Though it can be changed any time, this kind of information really only can be applied + // at frame level. Thus we check here, if during the previous frame there was change + // and apply the video mode set for our just started new frame. + Uint8 new_mode = !!(vic_registers[0x6F] & 0x80); + if (XEMU_UNLIKELY(new_mode != videostd_id)) { + // We have video mode change! + videostd_id = new_mode; + const char *new_name; + float cycles_at_1mhz; // 1MHz CPU cycles equalient of a scanline time + if (videostd_id) { + // --- NTSC --- + new_name = NTSC_STD_NAME; + videostd_frametime = NTSC_FRAME_TIME; + cycles_at_1mhz = 1000000.0 / NTSC_LINE_FREQ; + max_rasters = PHYSICAL_RASTERS_NTSC; + visible_area_height = SCREEN_HEIGHT_VISIBLE_NTSC; + } else { + // --- PAL --- + new_name = PAL_STD_NAME; + videostd_frametime = PAL_FRAME_TIME; + cycles_at_1mhz = 1000000.0 / PAL_LINE_FREQ; + max_rasters = PHYSICAL_RASTERS_PAL; + visible_area_height = SCREEN_HEIGHT_VISIBLE_PAL; + } + DEBUGPRINT("VIC: switching video standard from %s to %s (1MHz line cycle count is %f, frame time is %dusec)" NL, videostd_name, new_name, cycles_at_1mhz, videostd_frametime); + videostd_name = new_name; +#if 0 + // TODO: recalculate cpu_cycles/line "constants" for each CPU speed modes (1, 2, 3.5, ~40 MHz) + cpu_cycles_per_line_c64 = (int)roundf(cycles_at_1mhz); + cpu_cycles_per_line_c128 = (int)roundf(cycles_at_1mhz * 2.0); + cpu_cycles_per_line_c65 = (int)roundf(cycles_at_1mhz * 3.5); + cpu_cycles_per_line_m65 = (int)roundf(cycles_at_1mhz * 40.0); // FIXME: configdb.fastclock for newer Xemu! This is BAD since it can be other than 40!!!! +#endif + } + // FIXME: do we need this here? Ie, should this always bound to video mode change (only at frame boundary!) or not ... +#if 0 + vicii_first_raster = vic_registers[0x6F] & 0x1F; + if (!in_hypervisor) { + vic4_sideborder_touched = 1; + vic4_interpret_legacy_mode_registers(); + } +#endif } + static void vic4_interrupt_checker ( void ) { int vic_irq_old = cpu65.irqLevel & 2; @@ -223,7 +267,7 @@ static void vic4_interrupt_checker ( void ) vic_irq_new = 0; } if (vic_irq_old != vic_irq_new) { - DEBUG("VIC3: interrupt change %s -> %s" NL, vic_irq_old ? "active" : "inactive", vic_irq_new ? "active" : "inactive"); + DEBUG("VIC4: interrupt change %s -> %s" NL, vic_irq_old ? "active" : "inactive", vic_irq_new ? "active" : "inactive"); if (vic_irq_new) cpu65.irqLevel |= 2; else @@ -231,21 +275,23 @@ static void vic4_interrupt_checker ( void ) } } + static void vic4_check_raster_interrupt(int nraster) { if (nraster == compare_raster) interrupt_status |= 1; else interrupt_status &= 0xFE; - vic4_interrupt_checker(); } + inline static void vic4_calculate_char_x_step() { char_x_step = (REG_CHARXSCALE / 120.0f) / (REG_H640 ? 1 : 2); } + static void vic4_reset_display_counters() { xcounter = 0; @@ -254,107 +300,70 @@ static void vic4_reset_display_counters() ycounter = 0; } + static void vic4_update_sideborder_dimensions() { - if (REG_CSEL) // 40-columns? - { + if (REG_CSEL) { // 40-columns? border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER; - if (!REG_H640) - { border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER - 1; - } - else //80-col mode - { + else // 80-col mode border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER; - } - } - else // 38-columns - { + } else { // 38-columns border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER - 18; - if (!REG_H640) - { border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER + 14; - } - else //78-col mode - { + else // 78-col mode border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER + 15; - } } } + static void vic4_interpret_legacy_mode_registers() { // See https://github.com/MEGA65/mega65-core/blob/257d78aa6a21638cb0120fd34bc0e6ab11adfd7c/src/vhdl/viciv.vhdl#L1277 - vic4_update_sideborder_dimensions(); - - if (REG_CSEL) // 40-columns? - { - if (!REG_H640) - { + if (REG_CSEL) { // 40-columns? + if (!REG_H640) SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL)); - } - else //80-col mode - { + else // 80-col mode SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL) - 2); - } - } - else // 38-columns - { - if (!REG_H640) - { + } else { // 38-columns + if (!REG_H640) SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL)); - } - else //78-col mode - { + else // 78-col mode SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL) - 2); - } } - - if (!REG_V400) // Standard mode (200-lines) - { - if (REG_RSEL) // 25-row - { + if (!REG_V400) { // Standard mode (200-lines) + if (REG_RSEL) { // 25-row SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster)); SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) - 1); display_row_count = 25; - } - else - { + } else { SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) + 8); SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - (2 * vicii_first_raster) - SINGLE_TOP_BORDER_200 - 7); display_row_count = 24; } - SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) - 6 + REG_VIC2_YSCROLL * 2); - } - else // V400 - { - if (REG_RSEL) // 25-line+V400 - { + } else { // V400 + if (REG_RSEL) { // 25-line+V400 SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster)); SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 1); display_row_count = 25*2; - } - else - { + } else { SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) + 8); SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - (2 * vicii_first_raster) - SINGLE_TOP_BORDER_200 - 7); display_row_count = 24*2; } - SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 6 + (REG_VIC2_YSCROLL * 2)); } - Uint8 width = REG_H640 ? 80 : 40; REG_CHRCOUNT = width; SET_CHARSTEP_BYTES(width);// * (REG_16BITCHARSET ? 2 : 1)); - + REG_SCRNPTR_B0 = 0; REG_SCRNPTR_B1 &= 0xC0; - REG_SCRNPTR_B1 |= REG_H640 ? ((reg_d018_screen_addr & 14) << 2) : (reg_d018_screen_addr << 2); + REG_SCRNPTR_B1 |= REG_H640 ? ((reg_d018_screen_addr & 14) << 2) : (reg_d018_screen_addr << 2); REG_SCRNPTR_B2 = 0; vic_registers[0x63] &= 0b11110000; @@ -367,13 +376,36 @@ static void vic4_interpret_legacy_mode_registers() REG_SPRPTR_B1 = (~last_dd00_bits << 6) | (REG_SPRPTR_B1 & 0x3F); REG_SCRNPTR_B1 = (~last_dd00_bits << 6) | (REG_SCRNPTR_B1 & 0x3F); REG_CHARPTR_B1 = (~last_dd00_bits << 6) | (REG_CHARPTR_B1 & 0x3F); - + SET_COLORRAM_BASE(0); - DEBUGPRINT("VIC4: 16bit=%d, chrcount=%d, charstep=%d bytes, charscale=%d, vic_ii_first_raster=%d, ras_src=%d," - "border yt=%d, yb=%d, xl=%d, xr=%d, textxpos=%d, textypos=%d," - "screen_ram=$%06x, charset/bitmap=$%06x, sprite=$%06x" NL, REG_16BITCHARSET , REG_CHRCOUNT,CHARSTEP_BYTES,REG_CHARXSCALE, + DEBUGPRINT( + "VIC4: 16bit=%d, chrcount=%d, charstep=%d bytes, charscale=%d, vic_ii_first_raster=%d, ras_src=%d, " + "border yt=%d, yb=%d, xl=%d, xr=%d, textxpos=%d, textypos=%d, " + "screen_ram=$%06x, charset/bitmap=$%06x, sprite=$%06x" NL, + REG_16BITCHARSET, REG_CHRCOUNT, CHARSTEP_BYTES, REG_CHARXSCALE, vicii_first_raster, REG_FNRST, BORDER_Y_TOP, BORDER_Y_BOTTOM, border_x_left, border_x_right, CHARGEN_X_START, CHARGEN_Y_START, - SCREEN_ADDR, CHARSET_ADDR, SPRITE_POINTER_ADDR); + SCREEN_ADDR, CHARSET_ADDR, SPRITE_POINTER_ADDR + ); +} + + +// FIXME: preliminary DAT support. For real, these should be mostly calculated at writing +// DAT X/Y registers, bitplane selection registers etc (also true for the actual renderer!), +// would give much better emulator performace. Though for now, that's a naive preliminary +// way to support DAT at all! +static XEMU_INLINE Uint8 *get_dat_addr ( unsigned int bpn ) +{ + unsigned int x = vic_registers[0x3C]; + unsigned int y = vic_registers[0x3D] + ((x << 1) & 0x100); + unsigned int h640 = (vic_registers[0x31] & 128); + x &= 0x7F; + //DEBUGPRINT("VIC-IV: DAT: accessing DAT for bitplane #%u at X,Y of %u,%u in H%u mode" NL, bpn, x, y, h640 ? 640 : 320); + return + bitplane_bank_p + // MEGA65 feature (WANNABE feature!) to support relocatable bitplane bank by the DAT! (this is a pointer, not an integer!) + ((vic_registers[0x33 + bpn] & (h640 ? 12 : 14)) << 12) + // bitplane address + ((bpn & 1) ? 0x10000 : 0) + // odd/even bitplane selection + (((y >> 3) * (h640 ? 640 : 320)) + (x << 3) + (y & 7)) // position within the bitplane given by the X/Y info + ; } @@ -391,14 +423,14 @@ static void vic4_interpret_legacy_mode_registers() for the I/O mode used on the "classic $D000 area" and DMA I/O access only */ - static const char vic_registers_internal_mode_names[] = {'4', '3', '2'}; -#define CASE_VIC_2(n) case n+0x100 -#define CASE_VIC_3(n) case n+0x080 -#define CASE_VIC_4(n) case n -#define CASE_VIC_ALL(n) CASE_VIC_2(n): CASE_VIC_3(n): CASE_VIC_4(n) -#define CASE_VIC_3_4(n) CASE_VIC_3(n): CASE_VIC_4(n) +#define CASE_VIC_2(n) case n+0x100 +#define CASE_VIC_3(n) case n+0x080 +#define CASE_VIC_4(n) case n +#define CASE_VIC_ALL(n) CASE_VIC_2(n): CASE_VIC_3(n): CASE_VIC_4(n) +#define CASE_VIC_3_4(n) CASE_VIC_3(n): CASE_VIC_4(n) + /* - If HOTREG register is enabled, VICIV will trigger recalculation of border and such on next raster, on any "legacy" register write. For the VIC-IV such "hot" registers are: @@ -420,9 +452,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) break; // Sprite coordinates: simple write the VIC reg in all I/O modes. CASE_VIC_ALL(0x11): if (vic_registers[0x11] ^ data) - { vic_hotreg_touched = 1; - } compare_raster = (compare_raster & 0xFF) | ((data & 0x80) << 1); DEBUGPRINT("VIC: compare raster is now %d" NL, compare_raster); break; @@ -436,9 +466,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) break; CASE_VIC_ALL(0x16): // control-reg#2, we allow write even if non-used bits here if (vic_registers[0x16] ^ data) - { vic_hotreg_touched = 1; - } break; CASE_VIC_ALL(0x17): // sprite-Y expansion break; @@ -447,8 +475,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) // Reads are mapped to extended registers. // So we just store the D018 Legacy Screen Address to be referenced elsewhere. // - if (vic_registers[0x18] ^ data) - { + if (vic_registers[0x18] ^ data) { REG_CHARPTR_B2 = 0; REG_CHARPTR_B1 = (data & 14) << 2; REG_CHARPTR_B0 = 0; @@ -456,7 +483,6 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) reg_d018_screen_addr = (data & 0xF0) >> 4; vic_hotreg_touched = 1; } - data &= 0xFE; break; CASE_VIC_ALL(0x19): @@ -514,32 +540,32 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) break; CASE_VIC_3_4(0x31): // (!) NOTE: - // According to Paul, speed change should trigger "HOTREG" touched notification but no VIC legacy register "interpret" + // According to Paul, speed change should trigger "HOTREG" touched notification but no VIC legacy register "interpret" // So probably we need a separate (cpu_speed_hotreg) var? - // - if ( (vic_registers[0x31] & 0xBF) ^ (data & 0xBF) ) - { + if ((vic_registers[0x31] & 0xBF) ^ (data & 0xBF)) vic_hotreg_touched = 1; - } - vic4_raster_renderer_path = ( (data & 0x10) == 0) ? vic4_render_char_raster : vic4_render_bitplane_raster; - + vic4_raster_renderer_path = ( (data & 0x10) == 0) ? vic4_render_char_raster : vic4_render_bitplane_raster; + vic_registers[0x31] = data; // we need this work-around, since reg-write happens _after_ this switch statement, but machine_set_speed above needs it ... machine_set_speed(0); - + vic4_calculate_char_x_step(); - break; //We did the write, but we need to trigger vichot_reg if should + break; // we did the write, but we need to trigger vichot_reg if should CASE_VIC_3_4(0x32): CASE_VIC_3_4(0x33): CASE_VIC_3_4(0x34): CASE_VIC_3_4(0x35): CASE_VIC_3_4(0x36): CASE_VIC_3_4(0x37): CASE_VIC_3_4(0x38): CASE_VIC_3_4(0x39): CASE_VIC_3_4(0x3A): CASE_VIC_3_4(0x3B): CASE_VIC_3_4(0x3C): CASE_VIC_3_4(0x3D): CASE_VIC_3_4(0x3E): CASE_VIC_3_4(0x3F): + break; + // DAT read/write bitplanes port CASE_VIC_3_4(0x40): CASE_VIC_3_4(0x41): CASE_VIC_3_4(0x42): CASE_VIC_3_4(0x43): CASE_VIC_3_4(0x44): CASE_VIC_3_4(0x45): CASE_VIC_3_4(0x46): CASE_VIC_3_4(0x47): + *get_dat_addr(addr & 7) = data; // write pixels via the DAT! break; /* --- NO MORE VIC-III REGS FROM HERE --- */ - CASE_VIC_4(0x48): CASE_VIC_4(0x49): CASE_VIC_4(0x4A): CASE_VIC_4(0x4B): + CASE_VIC_4(0x48): CASE_VIC_4(0x49): CASE_VIC_4(0x4A): CASE_VIC_4(0x4B): CASE_VIC_4(0x4C): CASE_VIC_4(0x4D): CASE_VIC_4(0x4E): CASE_VIC_4(0x4F): break; - CASE_VIC_4(0x50): CASE_VIC_4(0x51): + CASE_VIC_4(0x50): CASE_VIC_4(0x51): return; // Writing to XPOS register is no-op CASE_VIC_4(0x52): CASE_VIC_4(0x53): break; @@ -547,32 +573,32 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) vic_registers[0x54] = data; // we need this work-around, since reg-write happens _after_ this switch statement, but machine_set_speed above needs it ... machine_set_speed(0); return; // since we DID the write, it's OK to return here and not using "break" - CASE_VIC_4(0x55): CASE_VIC_4(0x56): CASE_VIC_4(0x57): break; - CASE_VIC_4(0x58): CASE_VIC_4(0x59): + CASE_VIC_4(0x55): CASE_VIC_4(0x56): CASE_VIC_4(0x57): break; + CASE_VIC_4(0x58): CASE_VIC_4(0x59): DEBUGPRINT("VIC: Write $%04x CHARSTEP: $%02x" NL, addr, data); break; - CASE_VIC_4(0x5A): + CASE_VIC_4(0x5A): //DEBUGPRINT("WRITE $%04x CHARXSCALE: $%02x" NL, addr, data); - vic_registers[0x5A] = data; // Write now and calculate step. + vic_registers[0x5A] = data; // write now and calculate step vic4_calculate_char_x_step(); return; - CASE_VIC_4(0x5B): + CASE_VIC_4(0x5B): break; CASE_VIC_4(0x5C): vic4_sideborder_touched = 1; break; - CASE_VIC_4(0x5D): + CASE_VIC_4(0x5D): DEBUGPRINT("VIC: Write $%04x SIDEBORDER/HOTREG: $%02x" NL, addr, data); - if((vic_registers[0x5D] & 0x1F) ^ (data & 0x1F)) // sideborder MSB (0..5) modified ? + if ((vic_registers[0x5D] & 0x1F) ^ (data & 0x1F)) // sideborder MSB (0..5) modified ? vic4_sideborder_touched = 1; - break; - - CASE_VIC_4(0x5E): + break; + + CASE_VIC_4(0x5E): DEBUGPRINT("VIC: Write $%04x CHARCOUNT: $%02x" NL, addr, data); break; - CASE_VIC_4(0x5F): + CASE_VIC_4(0x5F): break; CASE_VIC_4(0x60): CASE_VIC_4(0x61): CASE_VIC_4(0x62): CASE_VIC_4(0x63): DEBUGPRINT("VIC: Write SCREENADDR byte 0xD0%02x: $%02x" NL, addr, data); @@ -589,45 +615,46 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) CASE_VIC_4(0x6C): CASE_VIC_4(0x6D): CASE_VIC_4(0x6E): vic_registers[addr & 0x7F] = data; // if (SPRITE_POINTER_ADDR > 384*1024) { - // DEBUGPRINT("WARNING !!! : SPRITE_POINTER_ADDR at $%08X exceeds 384K chip RAM!!!! Current behavior is undefined." NL, SPRITE_POINTER_ADDR); + // DEBUGPRINT("WARNING !!! : SPRITE_POINTER_ADDR at $%08X exceeds 384K chip RAM!!!! Current behavior is undefined." NL, SPRITE_POINTER_ADDR); // } // DEBUGPRINT("SPRPTRADR/SPRPTRBNK Modified. Sprite Data Pointers now: " NL); - // for (int i = 0; i < 8; ++i) { - // const Uint8 *sprite_data_pointer = main_ram + SPRITE_POINTER_ADDR + i * ((SPRITE_16BITPOINTER >> 7) + 1); + // for (int i = 0; i < 8; i++) { + // const Uint8 *sprite_data_pointer = main_ram + SPRITE_POINTER_ADDR + i * ((SPRITE_16BITPOINTER >> 7) + 1); // const Uint32 dataptr = SPRITE_16BITPOINTER ? 64 * ( ((*(sprite_data_pointer+1) << 8)) + (*(sprite_data_pointer))) : 64 * (*sprite_data_pointer); // DEBUGPRINT("Sprite #%d data @ $%08X %s" NL , i, dataptr, dataptr > 384*1024 ? "!!! OUT OF 384K main RAM !!!" : ""); // } break; CASE_VIC_4(0x6F): +#if 0 // Trigger video mode change. - + // LGB: this must be handled at opening new frame and NOT here. + // though I am still in doubts, what parts should be still handled + // here anyway, like this lines after #endif, see below max_rasters = data & 0x80 ? PHYSICAL_RASTERS_NTSC : PHYSICAL_RASTERS_PAL; visible_area_height = data & 0x80 ? SCREEN_HEIGHT_VISIBLE_NTSC : SCREEN_HEIGHT_VISIBLE_PAL; - - if ((vic_registers[0x6F] & 0x80) ^ (data & 0x80)) - { + + if ((vic_registers[0x6F] & 0x80) ^ (data & 0x80)) { // Change video mode vic4_reset_display_counters(); vic4_switch_display_mode(data & 0x80); } - +#endif vicii_first_raster = data & 0x1F; - if (!in_hypervisor) - { + if (!in_hypervisor) { vic4_sideborder_touched = 1; vic4_interpret_legacy_mode_registers(); } - break; + break; CASE_VIC_4(0x70): // VIC-IV palette selection register - altpalette = ((data & 0x03) << 8) + vic_palettes; + palette = ((data & 0x03) << 8) + vic_palettes; spritepalette = ((data & 0x0C) << 6) + vic_palettes; - palette = ((data & 0x30) << 4) + vic_palettes; + altpalette = ((data & 0x30) << 4) + vic_palettes; palregaccofs = ((data & 0xC0) << 2); check_if_rom_palette(vic_registers[0x30] & 4); break; @@ -658,20 +685,15 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) FATAL("Xemu: invalid VIC internal register numbering on write: $%X", addr); } vic_registers[addr & 0x7F] = data; - if (REG_HOTREG) - { - if (vic_hotreg_touched) - { + if (REG_HOTREG) { + if (vic_hotreg_touched) { //DEBUGPRINT("VIC: vic_hotreg_touched triggered (WRITE $D0%02x, $%02x)" NL, addr & 0x7F, data ); vic4_interpret_legacy_mode_registers(); vic_hotreg_touched = 0; vic4_sideborder_touched = 0; } - - if (vic4_sideborder_touched) - { + if (vic4_sideborder_touched) { //DEBUGPRINT("VIC: vic4_sideborder_touched triggered (WRITE $D0%02x, $%02x)" NL, addr & 0x7F, data ); - vic4_update_sideborder_dimensions(); vic4_sideborder_touched = 0; } @@ -749,12 +771,15 @@ Uint8 vic_read_reg ( int unsigned addr ) break; CASE_VIC_3_4(0x32): CASE_VIC_3_4(0x33): CASE_VIC_3_4(0x34): CASE_VIC_3_4(0x35): CASE_VIC_3_4(0x36): CASE_VIC_3_4(0x37): CASE_VIC_3_4(0x38): CASE_VIC_3_4(0x39): CASE_VIC_3_4(0x3A): CASE_VIC_3_4(0x3B): CASE_VIC_3_4(0x3C): CASE_VIC_3_4(0x3D): CASE_VIC_3_4(0x3E): CASE_VIC_3_4(0x3F): + break; + // DAT read/write bitplanes port CASE_VIC_3_4(0x40): CASE_VIC_3_4(0x41): CASE_VIC_3_4(0x42): CASE_VIC_3_4(0x43): CASE_VIC_3_4(0x44): CASE_VIC_3_4(0x45): CASE_VIC_3_4(0x46): CASE_VIC_3_4(0x47): + result = *get_dat_addr(addr & 7); // read pixels via the DAT! break; /* --- NO MORE VIC-III REGS FROM HERE --- */ CASE_VIC_4(0x48): CASE_VIC_4(0x49): CASE_VIC_4(0x4A): CASE_VIC_4(0x4B): CASE_VIC_4(0x4C): CASE_VIC_4(0x4D): CASE_VIC_4(0x4E): CASE_VIC_4(0x4F): - CASE_VIC_4(0x50): + CASE_VIC_4(0x50): break; CASE_VIC_4(0x51): result = vic_registers[0x51]++; @@ -766,12 +791,12 @@ Uint8 vic_read_reg ( int unsigned addr ) CASE_VIC_4(0x55): CASE_VIC_4(0x56): CASE_VIC_4(0x57): CASE_VIC_4(0x58): CASE_VIC_4(0x59): CASE_VIC_4(0x5A): CASE_VIC_4(0x5B): CASE_VIC_4(0x5C): CASE_VIC_4(0x5D): CASE_VIC_4(0x5E): CASE_VIC_4(0x5F): CASE_VIC_4(0x60): CASE_VIC_4(0x61): CASE_VIC_4(0x62): CASE_VIC_4(0x63): CASE_VIC_4(0x64): CASE_VIC_4(0x65): CASE_VIC_4(0x66): CASE_VIC_4(0x67): CASE_VIC_4(0x68): CASE_VIC_4(0x69): CASE_VIC_4(0x6A): CASE_VIC_4(0x6B): CASE_VIC_4(0x6C): - CASE_VIC_4(0x6D): + CASE_VIC_4(0x6D): break; - CASE_VIC_4(0x6E): + CASE_VIC_4(0x6E): break; - + CASE_VIC_4(0x6F): CASE_VIC_4(0x70): CASE_VIC_4(0x71): CASE_VIC_4(0x72): CASE_VIC_4(0x73): CASE_VIC_4(0x74): CASE_VIC_4(0x75): CASE_VIC_4(0x76): CASE_VIC_4(0x77): CASE_VIC_4(0x78): CASE_VIC_4(0x79): CASE_VIC_4(0x7A): CASE_VIC_4(0x7B): CASE_VIC_4(0x7C): CASE_VIC_4(0x7D): CASE_VIC_4(0x7E): CASE_VIC_4(0x7F): @@ -809,51 +834,46 @@ Uint8 vic_read_reg ( int unsigned addr ) static inline Uint32 get_charset_effective_addr() { - // cache this? - switch (CHARSET_ADDR) - { - case 0x1000: - return 0x2D000; - case 0x9000: - return 0x29000; - case 0x1800: - return 0x2D800; - case 0x9800: - return 0x29800; + // cache this? + switch (CHARSET_ADDR) { + case 0x1000: + return 0x2D000; + case 0x9000: + return 0x29000; + case 0x1800: + return 0x2D800; + case 0x9800: + return 0x29800; } return CHARSET_ADDR; } -static void vic4_draw_sprite_row_16color(int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale) + +static void vic4_draw_sprite_row_16color( int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale ) { const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; const int palindexbase = sprnum * 16 + 128 * (SPRITE_BITPLANE_ENABLE(sprnum) >> sprnum); - - for (int byte = 0; byte < totalBytes; ++byte) - { + for (int byte = 0; byte < totalBytes; byte++) { const Uint8 c0 = (*(row_data_ptr + byte)) >> 4; const Uint8 c1 = (*(row_data_ptr + byte)) & 0xF; - for (int p = 0; p < xscale && x_display_pos < border_x_right; ++p, ++x_display_pos) - { - if (c0) - { - if (x_display_pos >= border_x_left && - (!SPRITE_IS_BACK(sprnum) || - (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL))) - { + for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { + if (c0) { + if ( + x_display_pos >= border_x_left && ( + !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL) + ) + ) { *(pixel_raster_start + x_display_pos) = spritepalette[palindexbase + c0]; } } } - - for (int p = 0; p < xscale && x_display_pos < border_x_right; ++p, ++x_display_pos) - { - if (c1) - { - if (x_display_pos >= border_x_left && - (!SPRITE_IS_BACK(sprnum) || - (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL))) - { + for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { + if (c1) { + if ( + x_display_pos >= border_x_left && ( + !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL) + ) + ) { *(pixel_raster_start + x_display_pos) = spritepalette[palindexbase + c1]; } } @@ -861,39 +881,36 @@ static void vic4_draw_sprite_row_16color(int sprnum, int x_display_pos, const Ui } } -static void vic4_draw_sprite_row_multicolor(int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale) + +static void vic4_draw_sprite_row_multicolor ( int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale ) { const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; - for (int byte = 0; byte < totalBytes; ++byte) - { - for (int xbit = 0; xbit < 8; xbit += 2) - { + for (int byte = 0; byte < totalBytes; byte++) { + for (int xbit = 0; xbit < 8; xbit += 2) { const Uint8 p0 = *row_data_ptr & (0x80 >> xbit); const Uint8 p1 = *row_data_ptr & (0x40 >> xbit); - - Uint8 pixel = 0; // TODO: See generated code -- use lookup instead of branch? - if (!p0 && p1) + Uint8 pixel = 0; // TODO: See generated code -- use lookup instead of branch? + if (!p0 && p1) pixel = SPRITE_MULTICOLOR_1; else if (p0 && !p1) pixel = SPRITE_COLOR(sprnum); else if (p0 && p1) pixel = SPRITE_MULTICOLOR_2; - for (int p = 0; p < xscale && x_display_pos < border_x_right; ++p, x_display_pos += 2) - { - if (pixel) - { - if (x_display_pos >= border_x_left && - (!SPRITE_IS_BACK(sprnum) || - (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL))) - { + for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos += 2) { + if (pixel) { + if ( + x_display_pos >= border_x_left && ( + !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL) + ) + ) { *(pixel_raster_start + x_display_pos) = spritepalette[pixel]; } - if (x_display_pos+1 >= border_x_left && - (!SPRITE_IS_BACK(sprnum) || - (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos + 1] != FOREGROUND_PIXEL))) - { + if (x_display_pos+1 >= border_x_left && ( + !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos + 1] != FOREGROUND_PIXEL) + ) + ) { *(pixel_raster_start + x_display_pos + 1) = spritepalette[pixel]; } } @@ -903,21 +920,21 @@ static void vic4_draw_sprite_row_multicolor(int sprnum, int x_display_pos, const } } -static void vic4_draw_sprite_row_mono(int sprnum, int x_display_pos, const Uint8 *row_data_ptr, int xscale) + +static void vic4_draw_sprite_row_mono ( int sprnum, int x_display_pos, const Uint8 *row_data_ptr, int xscale ) { const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; - for (int byte = 0; byte < totalBytes; ++byte) - { - for (int xbit = 0; xbit < 8; ++xbit) - { + for (int byte = 0; byte < totalBytes; byte++) { + for (int xbit = 0; xbit < 8; xbit++) { const Uint8 pixel = *row_data_ptr & (0x80 >> xbit); - for (int p = 0; p < xscale && x_display_pos < border_x_right; ++p, ++x_display_pos) - { - if (x_display_pos >= border_x_left && - pixel && - (!SPRITE_IS_BACK(sprnum) || - (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL))) - { + for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { + if ( + x_display_pos >= border_x_left && + pixel && ( + !SPRITE_IS_BACK(sprnum) || + (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL) + ) + ) { *(pixel_raster_start + x_display_pos) = spritepalette[SPRITE_COLOR(sprnum)]; } } @@ -926,33 +943,30 @@ static void vic4_draw_sprite_row_mono(int sprnum, int x_display_pos, const Uint8 } } + static void vic4_do_sprites() { // Fetch and sequence sprites. - // + // // NOTE about Text/Bitmap Graphics Background/foreground semantics: - // In multicolor mode (MCM=1), the bit combinations “00” and “01” belong to the background - // and “10” and “11” to the foreground whereas in standard mode (MCM=0), + // In multicolor mode (MCM=1), the bit combinations "00" and "01" belong to the background + // and "10" and "11" to the foreground whereas in standard mode (MCM=0), // cleared pixels belong to the background and set pixels to the foreground. - // - for (int sprnum = 7; sprnum >= 0; --sprnum) - { - if (REG_SPRITE_ENABLE & (1 << sprnum)) - { + for (int sprnum = 7; sprnum >= 0; --sprnum) { + if (REG_SPRITE_ENABLE & (1 << sprnum)) { const int spriteHeight = SPRITE_EXTHEIGHT(sprnum) ? REG_SPRHGHT : 21; - int x_display_pos = border_x_left + ((SPRITE_POS_X(sprnum) - SPRITE_X_BASE_COORD) * (REG_SPR640 ? 1 : 2)); // in display units - int y_logical_pos = SPRITE_POS_Y(sprnum) - SPRITE_Y_BASE_COORD +(BORDER_Y_TOP / (REG_V400 ? 1 : 2)); // in logical units + int x_display_pos = border_x_left + ((SPRITE_POS_X(sprnum) - SPRITE_X_BASE_COORD) * (REG_SPR640 ? 1 : 2)); // in display units + int y_logical_pos = SPRITE_POS_Y(sprnum) - SPRITE_Y_BASE_COORD +(BORDER_Y_TOP / (REG_V400 ? 1 : 2)); // in logical units int sprite_row_in_raster = logical_raster - y_logical_pos; - + if (SPRITE_VERT_2X(sprnum)) sprite_row_in_raster = sprite_row_in_raster >> 1; - if (sprite_row_in_raster >= 0 && sprite_row_in_raster < spriteHeight) - { + if (sprite_row_in_raster >= 0 && sprite_row_in_raster < spriteHeight) { const int widthBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; - const Uint8 *sprite_data_pointer = main_ram + SPRITE_POINTER_ADDR + sprnum * ((SPRITE_16BITPOINTER >> 7) + 1); - const Uint32 sprite_data_addr = SPRITE_16BITPOINTER ? + const Uint8 *sprite_data_pointer = main_ram + SPRITE_POINTER_ADDR + sprnum * ((SPRITE_16BITPOINTER >> 7) + 1); + const Uint32 sprite_data_addr = SPRITE_16BITPOINTER ? 64 * ((*(sprite_data_pointer + 1) << 8) | (*sprite_data_pointer)) : ((64 * (*sprite_data_pointer)) | ( ((~last_dd00_bits) & 0x3)) << 14); @@ -971,61 +985,46 @@ static void vic4_do_sprites() } } + // Render a monochrome character cell row // flip = 00 Dont flip, 01 = flip vertical, 10 = flip horizontal, 11 = flip both - -static void vic4_render_mono_char_row(Uint8 char_byte, int glyph_width, Uint8 bg_color, Uint8 fg_color, Uint8 vic3attr) +static void vic4_render_mono_char_row ( Uint8 char_byte, int glyph_width, Uint8 bg_color, Uint8 fg_color, Uint8 vic3attr ) { - if (vic3attr) - { + if (vic3attr) { if (char_row == 7 && VIC3_ATTR_UNDERLINE(vic3attr)) char_byte = 0xFF; - if (VIC3_ATTR_REVERSE(vic3attr)) char_byte = ~char_byte; - - if (VIC3_ATTR_BLINK(vic3attr) && vic4_blink_phase) + if (VIC3_ATTR_BLINK(vic3attr) && vic4_blink_phase) char_byte = VIC3_ATTR_REVERSE(vic3attr) ? ~char_byte : 0; - if (VIC3_ATTR_BOLD(vic3attr)) - { fg_color |= 0x10; - } } - - if (enable_bg_paint) - { - for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) - { + if (enable_bg_paint) { + for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { const Uint8 char_pixel = (char_byte & (0x80 >> (int)cx)); Uint32 pixel_color = char_pixel ? palette[fg_color] : palette[bg_color]; *(current_pixel++) = pixel_color; bg_pixel_state[xcounter++] = char_pixel ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; } - } - else // HACK!! to support MEGAMAZE GOTOX+VFLIP bits that ignore the background paint until - // next raster. - { - for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) - { + } else { // HACK!! to support MEGAMAZE GOTOX+VFLIP bits that ignore the background paint until next raster. + for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { const Uint8 char_pixel = (char_byte & (0x80 >> (int)cx)); if (char_pixel) *current_pixel = palette[fg_color]; - current_pixel++; bg_pixel_state[xcounter++] = char_pixel ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; } } - } -static void vic4_render_multicolor_char_row(Uint8 char_byte, int glyph_width, const Uint8 color_source[4]) + +static void vic4_render_multicolor_char_row ( Uint8 char_byte, int glyph_width, const Uint8 color_source[4] ) { - for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) - { - const Uint8 bitsel = 2 * (int)(cx / 2); + for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { + const Uint8 bitsel = 2 * (int)(cx / 2); const Uint8 bit_pair = (char_byte & (0x80 >> bitsel)) >> (6-bitsel) | (char_byte & (0x40 >> bitsel)) >> (6-bitsel); - + Uint8 pixel = color_source[bit_pair]; const Uint8 layer = bit_pair & 2 ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; *(current_pixel++) = palette[pixel]; @@ -1033,48 +1032,49 @@ static void vic4_render_multicolor_char_row(Uint8 char_byte, int glyph_width, co } } + // 8-bytes per row -static void vic4_render_fullcolor_char_row(const Uint8* char_row, int glyph_width) -{ - for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) - { +static void vic4_render_fullcolor_char_row ( const Uint8* char_row, int glyph_width ) +{ + for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { Uint32 pixel_color = palette[char_row[(int)cx]]; *(current_pixel++) = pixel_color; bg_pixel_state[xcounter++] = pixel_color ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; } } + // Render a bitplane-mode character cell row -// -static void vic4_render_bitplane_char_row(Uint8* bp_base[8], int glyph_width) +static void vic4_render_bitplane_char_row ( Uint8* bp_base[8], int glyph_width ) { const Uint8 bpe_mask = vic_registers[0x32] & (REG_H640 ? 15 : 255); - const Uint8 bp_comp = vic_registers[0x3B]; + const Uint8 bp_comp = vic_registers[0x3B]; - for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) - { + for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { const Uint8 bitsel = 0x80 >> ((int)cx); const Uint32 pixel_color = palette[ - ((((*bp_base[0] & bitsel) ? 1 : 0) | - ((*bp_base[1] & bitsel) ? 2 : 0) | - ((*bp_base[2] & bitsel) ? 4 : 0) | - ((*bp_base[3] & bitsel) ? 8 : 0) | - ((*bp_base[4] & bitsel) ? 16 : 0) | - ((*bp_base[5] & bitsel) ? 32 : 0) | - ((*bp_base[6] & bitsel) ? 64 : 0) | - ((*bp_base[7] & bitsel) ? 128 : 0)) & bpe_mask) ^ bp_comp]; + (( + ((*bp_base[0] & bitsel) ? 1 : 0) | + ((*bp_base[1] & bitsel) ? 2 : 0) | + ((*bp_base[2] & bitsel) ? 4 : 0) | + ((*bp_base[3] & bitsel) ? 8 : 0) | + ((*bp_base[4] & bitsel) ? 16 : 0) | + ((*bp_base[5] & bitsel) ? 32 : 0) | + ((*bp_base[6] & bitsel) ? 64 : 0) | + ((*bp_base[7] & bitsel) ? 128 : 0) + ) & bpe_mask) ^ bp_comp + ]; *(current_pixel++) = pixel_color; bg_pixel_state[xcounter++] = *bp_base[2] & bitsel ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; } } + void vic4_render_bitplane_raster() { Uint8* bp_base[8]; - // Get Bitplane source addresses /* TODO: Cache the following reads & EA calculation */ - const Uint32 offset = display_row * REG_CHRCOUNT * 8 + char_row ; bp_base[0] = bitplane_bank_p + ((vic_registers[0x33] & (REG_H640 ? 12 : 14)) << 12) + offset; bp_base[1] = bitplane_bank_p + ((vic_registers[0x34] & (REG_H640 ? 12 : 14)) << 12) + 0x10000 + offset; @@ -1084,10 +1084,8 @@ void vic4_render_bitplane_raster() bp_base[5] = bitplane_bank_p + ((vic_registers[0x38] & (REG_H640 ? 12 : 14)) << 12) + 0x10000 + offset; bp_base[6] = bitplane_bank_p + ((vic_registers[0x39] & (REG_H640 ? 12 : 14)) << 12) + offset; bp_base[7] = bitplane_bank_p + ((vic_registers[0x3A] & (REG_H640 ? 12 : 14)) << 12) + 0x10000 + offset; - - int line_char_index = 0; - while(line_char_index < REG_CHRCOUNT) - { + int line_char_index = 0; + while(line_char_index < REG_CHRCOUNT) { vic4_render_bitplane_char_row(bp_base, 8); bp_base[0] += 8; bp_base[1] += 8; @@ -1099,181 +1097,125 @@ void vic4_render_bitplane_raster() bp_base[7] += 8; line_char_index++; } - - if (++char_row > 7) - { + if (++char_row > 7) { char_row = 0; display_row++; } - while (xcounter++ < border_x_right) *current_pixel++ = palette[REG_SCREEN_COLOR]; - } -// -// + // The character rendering engine. Most features are shared between -// all graphic modes. Basically, the VIC-IV supports the following character -// color modes: +// all graphic modes. Basically, the VIC-IV supports the following character +// color modes: // // - Monochrome (Bg/Fg) // - VICII Multicolor -// - 16-color -// - 256-color +// - 16-color +// - 256-color // -// It's interesting to see that the four modes can be selected in +// It's interesting to see that the four modes can be selected in // bitmap or text modes. // -// VIC-III Extended attributes are applied to characters if properly set, +// VIC-III Extended attributes are applied to characters if properly set, // except in Multicolor modes. -// - void vic4_render_char_raster() { int line_char_index = 0; enable_bg_paint = 1; - // Account for negative Y-displacement (positive is taken into account in outer-loop) - - const int row_offset = (BORDER_Y_TOP - CHARGEN_Y_START) / 8; - const int adj_display_row = row_offset + display_row; - - if (adj_display_row >= 0 && adj_display_row < display_row_count) - { - const int char_row_offset = (BORDER_Y_TOP - CHARGEN_Y_START) % 8; - colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET + (adj_display_row * CHARSTEP_BYTES); - screen_ram_current_ptr = main_ram + SCREEN_ADDR + (adj_display_row * CHARSTEP_BYTES); - const Uint8* row_data_base_addr = main_ram + (REG_BMM ? VIC2_BITMAP_ADDR : get_charset_effective_addr()); - + if (display_row >= 0 && display_row < display_row_count) { + colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET + (display_row * CHARSTEP_BYTES); + screen_ram_current_ptr = main_ram + SCREEN_ADDR + (display_row * CHARSTEP_BYTES); + const Uint8 *row_data_base_addr = main_ram + (REG_BMM ? VIC2_BITMAP_ADDR : get_charset_effective_addr()); // Account for Chargen X-displacement - - for(Uint32* p = current_pixel; p < current_pixel + (CHARGEN_X_START - border_x_left); ++p) + for (Uint32 *p = current_pixel; p < current_pixel + (CHARGEN_X_START - border_x_left); p++) *p = palette[REG_SCREEN_COLOR]; - - current_pixel += (CHARGEN_X_START - border_x_left); + current_pixel += (CHARGEN_X_START - border_x_left); xcounter += (CHARGEN_X_START - border_x_left); const int xcounter_start = xcounter; - // Chargen starts here. - - while (line_char_index < REG_CHRCOUNT) - { + while (line_char_index < REG_CHRCOUNT) { Uint16 color_data = *(colour_ram_current_ptr++); Uint16 char_value = *(screen_ram_current_ptr++); - if (REG_16BITCHARSET) - { + if (REG_16BITCHARSET) { color_data = (color_data << 8) | (*(colour_ram_current_ptr++)); char_value = char_value | (*(screen_ram_current_ptr++) << 8); - if (SXA_GOTO_X(color_data)) - { + if (SXA_GOTO_X(color_data)) { current_pixel = pixel_raster_start + xcounter_start + (char_value & 0x3FF); xcounter = xcounter_start + (char_value & 0x3FF); line_char_index++; if (SXA_VERTICAL_FLIP(color_data)) - { enable_bg_paint = 0; - } - continue; } } - // Background and foreground colors - - const Uint8 char_fgcolor = color_data & 0xF; + const Uint8 char_fgcolor = color_data & 0xF; const Uint8 vic3_attr = REG_VICIII_ATTRIBS && !REG_MCM ? (color_data >> 4) : 0; const Uint16 char_id = REG_EBM ? (char_value & 0x3f) : char_value & 0x1fff; // up to 8192 characters (13-bit) const Uint8 char_bgcolor = REG_EBM ? vic_registers[0x21 + ((char_value >> 6) & 3)] : REG_SCREEN_COLOR; - // Calculate character-width - Uint8 glyph_width_deduct = SXA_TRIM_RIGHT_BITS012(char_value) + (SXA_TRIM_RIGHT_BIT3(char_value) ? 8 : 0); Uint8 glyph_width = (SXA_4BIT_PER_PIXEL(color_data) ? 16 : 8) - glyph_width_deduct; - // Default fetch from char mode. Uint8 char_byte; - int sel_char_row = char_row + char_row_offset; - + int sel_char_row = char_row; if (SXA_VERTICAL_FLIP(color_data)) - sel_char_row = 7 - char_row + char_row_offset; - + sel_char_row = 7 - char_row; if (REG_BMM) - { - char_byte = *(row_data_base_addr + display_row * 320 + 8 * line_char_index + sel_char_row); - } + char_byte = *(row_data_base_addr + display_row * 320 + 8 * line_char_index + sel_char_row); else - { char_byte = *(row_data_base_addr + (char_id * 8) + sel_char_row); - } - if (SXA_HORIZONTAL_FLIP(color_data)) - char_byte = reverse_byte(char_byte); - + char_byte = reverse_byte_table[char_byte]; // LGB: I killed the function, and type-conv, as char_byte is byte, OK to index as-is // Render character cell row - - if (SXA_4BIT_PER_PIXEL(color_data)) // 16-color character - { - - } - else if (CHAR_IS256_COLOR(char_id)) // 256-color character - { + if (SXA_4BIT_PER_PIXEL(color_data)) { // 16-color character + // FIXME: TODO?? + } else if (CHAR_IS256_COLOR(char_id)) { // 256-color character vic4_render_fullcolor_char_row(main_ram + (((char_id * 64) + (sel_char_row * 8) ) & 0x7FFFF), 8); - } - else if ((REG_MCM && (char_fgcolor & 8)) || (REG_MCM && REG_BMM)) // Multicolor character - { - if (REG_BMM) - { + } else if ((REG_MCM && (char_fgcolor & 8)) || (REG_MCM && REG_BMM)) { // Multicolor character + if (REG_BMM) { const Uint8 color_source[4] = { - REG_SCREEN_COLOR, //00 - char_value >> 4, //01 - char_value & 0xF, //10 - color_data & 0xF //11 + REG_SCREEN_COLOR, // 00 + char_value >> 4, // 01 + char_value & 0xF, // 10 + color_data & 0xF // 11 }; vic4_render_multicolor_char_row(char_byte, glyph_width, color_source); - } - else - { + } else { const Uint8 color_source[4] = { - REG_SCREEN_COLOR, //00 - REG_MULTICOLOR_1, //01 - REG_MULTICOLOR_2, //10 - char_fgcolor & 7 //11 + REG_SCREEN_COLOR, // 00 + REG_MULTICOLOR_1, // 01 + REG_MULTICOLOR_2, // 10 + char_fgcolor & 7 // 11 }; vic4_render_multicolor_char_row(char_byte, glyph_width, color_source); } - } - else // Single color character - { + } else { // Single color character if (!REG_BMM) - { vic4_render_mono_char_row(char_byte, glyph_width, char_bgcolor, char_fgcolor, vic3_attr); - } else - { vic4_render_mono_char_row(char_byte, glyph_width, char_value & 0xF, char_value >> 4, vic3_attr ); - } } line_char_index++; } } - - if (++char_row > 7) - { + if (++char_row > 7) { char_row = 0; display_row++; } - // Fill screen color after chargen phase - while (xcounter++ < border_x_right) *current_pixel++ = palette[REG_SCREEN_COLOR]; } -int vic4_render_scanline() + +int vic4_render_scanline() { // Work this first. DO NOT OPTIMIZE EARLY. @@ -1284,64 +1226,56 @@ int vic4_render_scanline() SET_PHYSICAL_RASTER(ycounter); logical_raster = ycounter >> (REG_V400 ? 0 : 1); - if (!(ycounter & 1)) // VIC-II raster source: We shall check FNRST ? + if (!(ycounter & 1)) // VIC-II raster source: We shall check FNRST ? vic4_check_raster_interrupt(logical_raster); - // "Double-scan hack" - if (!REG_V400 && (ycounter & 1)) - { + if (!REG_V400 && (ycounter & 1)) { for (int i = 0; i < SCREEN_WIDTH; i++, current_pixel++) *current_pixel = /* user_scanlines_setting ? 0 : */ *(current_pixel - SCREEN_WIDTH) ; - } - else - { - + } else { // Top and bottom borders - - if (ycounter < BORDER_Y_TOP || ycounter >= BORDER_Y_BOTTOM || !REG_DISPLAYENABLE) - { - for (int i = 0; i < SCREEN_WIDTH; ++i) - *(current_pixel++) = palette[REG_BORDER_COLOR & 0xF]; + if (ycounter < BORDER_Y_TOP || ycounter >= BORDER_Y_BOTTOM || !REG_DISPLAYENABLE) { + for (int i = 0; i < SCREEN_WIDTH; i++) + *(current_pixel++) = palette[REG_BORDER_COLOR]; } - else - { - // Render visible display first and render side-borders later to cover X-displaced - // character generator if needed. - + if (ycounter >= CHARGEN_Y_START && ycounter < BORDER_Y_BOTTOM) { + // Render chargen area and render side-borders later to cover X-displaced + // character generator if needed. Chargen area maybe covered by top/bottom + // borders also if y-offset applies. xcounter += border_x_left; current_pixel += border_x_left; - vic4_raster_renderer_path(); vic4_do_sprites(); - - for (Uint32 *p = pixel_raster_start; p < pixel_raster_start + border_x_left; ++p) - *p = palette[REG_BORDER_COLOR & 0xF]; - - for (Uint32 *p = current_pixel; p < current_pixel + border_x_right; ++p) - *p = palette[REG_BORDER_COLOR & 0xF]; } + // Paint screen color if positive y-offset (CHARGEN_Y_START > BORDER_Y_TOP) + if (ycounter >= BORDER_Y_TOP && ycounter < CHARGEN_Y_START) { + while (xcounter++ < border_x_right) + *current_pixel++ = palette[REG_SCREEN_COLOR]; + // for (int i = 0; i < SCREEN_WIDTH - border_x_right; i++, current_pixel++) + // *current_pixel = palette[REG_SCREEN_COLOR]; + } + for (Uint32 *p = pixel_raster_start; p < pixel_raster_start + border_x_left; p++) + *p = palette[REG_BORDER_COLOR]; + for (Uint32 *p = current_pixel; p < current_pixel + border_x_right; p++) + *p = palette[REG_BORDER_COLOR]; } ycounter++; - // End of frame? - if (ycounter == max_rasters) - { + if (ycounter == max_rasters) { vic4_reset_display_counters(); - screen_ram_current_ptr = main_ram + SCREEN_ADDR; colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET; frame_counter++; - if (frame_counter == VIC4_BLINK_INTERVAL) - { + if (frame_counter == VIC4_BLINK_INTERVAL) { frame_counter = 0; vic4_blink_phase = !vic4_blink_phase; } return 1; } - return 0; } + /* --- SNAPSHOT RELATED --- */ @@ -1382,7 +1316,7 @@ int vic4_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ) if (a) return a; memset(buffer, 0xFF, sizeof buffer); /* saving state ... */ - memcpy(buffer + 0x80, vic_registers, 0x80); // $80 bytes + memcpy(buffer + 0x80, vic_registers, 0x80); // $80 bytes buffer[0x7F] = c128_d030_reg; memcpy(buffer + 0x100 , vic_palette_bytes_red, NO_OF_PALETTE_REGS); memcpy(buffer + 0x100 + NO_OF_PALETTE_REGS, vic_palette_bytes_green, NO_OF_PALETTE_REGS); diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index 1cf86617..43ee26ea 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -1,7 +1,7 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016,2017 LGB (Gábor Lénárt) - Copyright (C)2020 Hernán Di Pietro + Copyright (C)2016-2021 LGB (Gábor Lénárt) + Copyright (C)2020-2021 Hernán Di Pietro This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,202 +20,215 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef XEMU_MEGA65_VIC4_H_INCLUDED #define XEMU_MEGA65_VIC4_H_INCLUDED -#include - #define VIC2_IOMODE 0 #define VIC3_IOMODE 1 #define VIC_BAD_IOMODE 2 #define VIC4_IOMODE 3 -// +// Horizontal sync frequencies (in Hertz) for NTSC and PAL video output of MEGA65. Must be float. +#define PAL_LINE_FREQ 31250.0 +#define NTSC_LINE_FREQ 31468.5 +// Frame times (in microseconds) for NTSC and PAL video output of MEGA65. Must be integer. +#define PAL_FRAME_TIME 20000 +#define NTSC_FRAME_TIME ((int)(16683.35)) + // Output window is fixed at 800x600 to support MegaPHONE, PAL-MEGA65 // and NTSC_MEGA65 modes. Internally, the VIC-IV chip draws 800-pixel -// wide buffers and traverses up to 526/624 physical rasters, as we do here. -// The user can select a clipped borders view (called "normal borders") which shows +// wide buffers and traverses up to 526/624 physical rasters, as we do here. +// The user can select a clipped borders view (called "normal borders") which shows // the real visible resolution of PAL (720x576) or NSTC(720x480). -// -#define SCREEN_WIDTH 800 -#define SCREEN_HEIGHT 600 -#define PHYSICAL_RASTERS_DEFAULT PHYSICAL_RASTERS_NTSC -#define SCREEN_HEIGHT_VISIBLE_DEFAULT SCREEN_HEIGHT_VISIBLE_NTSC -#define SCREEN_HEIGHT_VISIBLE_NTSC 480 -#define SCREEN_HEIGHT_VISIBLE_PAL 576 -#define PHYSICAL_RASTERS_NTSC 526 -#define PHYSICAL_RASTERS_PAL 624 -#define NTSC_RATIO PHYSICAL_RASTERS_NTSC / float(SCREEN_WIDTH) -#define PAL_RATIO PHYSICAL_RASTERS_PAL / float(SCREEN_WIDTH) -#define FRAME_H_FRONT 0 -#define RASTER_CORRECTION 3 -#define VIC4_BLINK_INTERVAL 25 - -// Register defines -// -// Ref: +#define SCREEN_WIDTH 800 +#define SCREEN_HEIGHT 600 +#define PHYSICAL_RASTERS_DEFAULT PHYSICAL_RASTERS_NTSC +#define SCREEN_HEIGHT_VISIBLE_DEFAULT SCREEN_HEIGHT_VISIBLE_NTSC +#define SCREEN_HEIGHT_VISIBLE_NTSC 480 +#define SCREEN_HEIGHT_VISIBLE_PAL 576 +#define PHYSICAL_RASTERS_NTSC 526 +#define PHYSICAL_RASTERS_PAL 624 +#define NTSC_RATIO (PHYSICAL_RASTERS_NTSC / float(SCREEN_WIDTH)) +#define PAL_RATIO (PHYSICAL_RASTERS_PAL / float(SCREEN_WIDTH)) +#define FRAME_H_FRONT 0 +#define RASTER_CORRECTION 3 +#define VIC4_BLINK_INTERVAL 25 + +// Register defines +// +// Ref: // https://github.com/MEGA65/mega65-core/blob/138-hdmi-audio-27mhz/iomap.txt // ---------------------------------------------------- // _Un suffix indicates upper n bits of register -// -#define REG_EBM (vic_registers[0x11] & 0x40) -#define REG_MCM (vic_registers[0x16] & 0x10) -#define REG_BMM (vic_registers[0x11] & 0x20) -#define REG_SPRITE_ENABLE vic_registers[0x15] -#define REG_BORDER_COLOR vic_registers[0x20] -#define REG_SCREEN_COLOR vic_registers[0x21] -#define REG_MULTICOLOR_1 vic_registers[0x22] -#define REG_MULTICOLOR_2 vic_registers[0x23] -#define REG_MULTICOLOR_3 vic_registers[0x24] -#define REG_H640 (vic_registers[0x31] & 128) -#define REG_V400 (vic_registers[0x31] & 8) -#define REG_VICIII_ATTRIBS (vic_registers[0x31] & 0x20) -#define REG_RSEL (vic_registers[0x11] & 8) -#define REG_CSEL (vic_registers[0x16] & 8) -#define REG_DISPLAYENABLE (vic_registers[0x11] & 0x10) -#define REG_VIC2_XSCROLL (vic_registers[0x16] & 7) -#define REG_VIC2_YSCROLL (vic_registers[0x11] & 7) -#define REG_CRAM2K (vic_registers[0x30] & 1) -#define REG_TBRDPOS (vic_registers[0x48]) -#define REG_SPRBPMEN_0_3 (vic_registers[0x49] >> 4) -#define REG_SPRBPMEN_4_7 (vic_registers[0x4B] >> 4) -#define REG_TBRDPOS_U4 (vic_registers[0x49] & 0xF) -#define REG_BBRDPOS (vic_registers[0x4A]) -#define REG_BBRDPOS_U4 (vic_registers[0x4B] & 0xF) -#define REG_TEXTXPOS (vic_registers[0x4C]) -#define REG_TEXTXPOS_U4 (vic_registers[0x4D] & 0xF) -#define REG_TEXTYPOS (vic_registers[0x4E]) -#define REG_TEXTYPOS_U4 (vic_registers[0x4F] & 0xF) -#define REG_XPOS (vic_registers[0x51]) -#define REG_XPOS_U6 (vic_registers[0x50] & 0x3F) -#define REG_FNRST (vic_registers[0x53] & 0x80) -#define REG_16BITCHARSET (vic_registers[0x54] & 1) -#define REG_FCLRLO (vic_registers[0x54] & 2) -#define REG_FCLRHI (vic_registers[0x54] & 4) -#define REG_SPR640 (vic_registers[0x54] & 0x10) -#define REG_SPRHGHT (vic_registers[0x56]) -#define REG_CHRXSCL (vic_registers[0x5A]) -#define REG_CHRYSCL (vic_registers[0x5B]) -#define REG_SIDBDRWD (vic_registers[0x5C]) -#define REG_SIDBDRWD_U5 (vic_registers[0x5D] & 0x3F) -#define REG_HOTREG (vic_registers[0x5D] & 0x80) -#define REG_CHARSTEP vic_registers[0x58] -#define REG_CHARSTEP_U8 vic_registers[0x59] -#define REG_CHARXSCALE vic_registers[0x5A] -#define REG_CHRCOUNT vic_registers[0x5E] -#define REG_SCRNPTR_B0 vic_registers[0x60] -#define REG_SCRNPTR_B1 vic_registers[0x61] -#define REG_SCRNPTR_B2 vic_registers[0x62] -#define REG_SCRNPTR_B3 (vic_registers[0x63] & 0xF) -#define REG_COLPTR vic_registers[0x64] -#define REG_COLPTR_MSB vic_registers[0x65] -#define REG_CHARPTR_B0 vic_registers[0x68] -#define REG_CHARPTR_B1 vic_registers[0x69] -#define REG_CHARPTR_B2 vic_registers[0x6A] -#define REG_SPRPTR_B0 vic_registers[0x6C] -#define REG_SPRPTR_B1 vic_registers[0x6D] -#define REG_SPRPTR_B2 (vic_registers[0x6E] & 0x7F) -#define REG_SCREEN_ROWS vic_registers[0x7B] -#define REG_PALNTSC (vic_registers[0x6f] & 0x80) -#define REG_PAL_RED_BASE (vic_registers[0x100]) -#define REG_PAL_GREEN_BASE (vic_registers[0x200]) -#define REG_PAL_BLUE_BASE (vic_registers[0x300]) +#define REG_EBM (vic_registers[0x11] & 0x40) +#define REG_MCM (vic_registers[0x16] & 0x10) +#define REG_BMM (vic_registers[0x11] & 0x20) +#define REG_SPRITE_ENABLE vic_registers[0x15] +#define REG_BORDER_COLOR vic_registers[0x20] +#define REG_SCREEN_COLOR vic_registers[0x21] +#define REG_MULTICOLOR_1 vic_registers[0x22] +#define REG_MULTICOLOR_2 vic_registers[0x23] +#define REG_MULTICOLOR_3 vic_registers[0x24] +#define REG_H640 (vic_registers[0x31] & 128) +#define REG_V400 (vic_registers[0x31] & 8) +#define REG_VICIII_ATTRIBS (vic_registers[0x31] & 0x20) +#define REG_RSEL (vic_registers[0x11] & 8) +#define REG_CSEL (vic_registers[0x16] & 8) +#define REG_DISPLAYENABLE (vic_registers[0x11] & 0x10) +#define REG_VIC2_XSCROLL (vic_registers[0x16] & 7) +#define REG_VIC2_YSCROLL (vic_registers[0x11] & 7) +#define REG_CRAM2K (vic_registers[0x30] & 1) +#define REG_TBRDPOS (vic_registers[0x48]) +#define REG_SPRBPMEN_0_3 (vic_registers[0x49] >> 4) +#define REG_SPRBPMEN_4_7 (vic_registers[0x4B] >> 4) +#define REG_TBRDPOS_U4 (vic_registers[0x49] & 0xF) +#define REG_BBRDPOS (vic_registers[0x4A]) +#define REG_BBRDPOS_U4 (vic_registers[0x4B] & 0xF) +#define REG_TEXTXPOS (vic_registers[0x4C]) +#define REG_TEXTXPOS_U4 (vic_registers[0x4D] & 0xF) +#define REG_TEXTYPOS (vic_registers[0x4E]) +#define REG_TEXTYPOS_U4 (vic_registers[0x4F] & 0xF) +#define REG_XPOS (vic_registers[0x51]) +#define REG_XPOS_U6 (vic_registers[0x50] & 0x3F) +#define REG_FNRST (vic_registers[0x53] & 0x80) +#define REG_16BITCHARSET (vic_registers[0x54] & 1) +#define REG_FCLRLO (vic_registers[0x54] & 2) +#define REG_FCLRHI (vic_registers[0x54] & 4) +#define REG_SPR640 (vic_registers[0x54] & 0x10) +#define REG_SPRHGHT (vic_registers[0x56]) +#define REG_CHRXSCL (vic_registers[0x5A]) +#define REG_CHRYSCL (vic_registers[0x5B]) +#define REG_SIDBDRWD (vic_registers[0x5C]) +#define REG_SIDBDRWD_U5 (vic_registers[0x5D] & 0x3F) +#define REG_HOTREG (vic_registers[0x5D] & 0x80) +#define REG_CHARSTEP vic_registers[0x58] +#define REG_CHARSTEP_U8 vic_registers[0x59] +#define REG_CHARXSCALE vic_registers[0x5A] +#define REG_CHRCOUNT vic_registers[0x5E] +#define REG_SCRNPTR_B0 vic_registers[0x60] +#define REG_SCRNPTR_B1 vic_registers[0x61] +#define REG_SCRNPTR_B2 vic_registers[0x62] +#define REG_SCRNPTR_B3 (vic_registers[0x63] & 0xF) +#define REG_COLPTR vic_registers[0x64] +#define REG_COLPTR_MSB vic_registers[0x65] +#define REG_CHARPTR_B0 vic_registers[0x68] +#define REG_CHARPTR_B1 vic_registers[0x69] +#define REG_CHARPTR_B2 vic_registers[0x6A] +#define REG_SPRPTR_B0 vic_registers[0x6C] +#define REG_SPRPTR_B1 vic_registers[0x6D] +#define REG_SPRPTR_B2 (vic_registers[0x6E] & 0x7F) +#define REG_SCREEN_ROWS vic_registers[0x7B] +#define REG_PAL_RED_BASE (vic_registers[0x100]) +#define REG_PAL_GREEN_BASE (vic_registers[0x200]) +#define REG_PAL_BLUE_BASE (vic_registers[0x300]) // Helper macros for accessing multi-byte registers // and other similar functionality for convenience -// ----------------------------------------------------- -#define PHYS_RASTER_COUNT (REG_PALNTSC ? NTSC_PHYSICAL_RASTERS : PAL_PHYSICAL_RASTERS) -#define SINGLE_SIDE_BORDER (((Uint16)REG_SIDBDRWD) | (REG_SIDBDRWD_U5) << 8) -#define BORDER_Y_TOP (((Uint16)REG_TBRDPOS) | (REG_TBRDPOS_U4) << 8) -#define BORDER_Y_BOTTOM (((Uint16)REG_BBRDPOS) | (REG_BBRDPOS_U4) << 8) -#define CHARGEN_Y_START (((Uint16)REG_TEXTYPOS) | (REG_TEXTYPOS_U4) << 8) -#define CHARGEN_X_START (((Uint16)REG_TEXTXPOS) | (REG_TEXTXPOS_U4) << 8) -#define CHARSTEP_BYTES (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) -#define SCREEN_RAM_ADDR_VIC (REG_SCREEN_ADDR * 1024) -#define SCREEN_ADDR ((Uint32)REG_SCRNPTR_B0 | (REG_SCRNPTR_B1<<8) | (REG_SCRNPTR_B2 <<16) | (REG_SCRNPTR_B3 << 24)) -#define CHARSET_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) -#define VIC2_BITMAP_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) -#define SPRITE_POINTER_ADDR ((Uint32)REG_SPRPTR_B0 | (REG_SPRPTR_B1<<8) | (REG_SPRPTR_B2 <<16)) -#define COLOUR_RAM_OFFSET ((((Uint16)REG_COLPTR) | (REG_COLPTR_MSB) << 8)) -#define IS_NTSC_MODE (REG_PALNTSC ^ 0x80) -#define SCREEN_STEP (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) -#define SPRITE_POS_Y(n) (vic_registers[1 + (n)*2]) -#define SPRITE_POS_X(n) (((Uint16)vic_registers[(n)*2]) | ( (vic_registers[0x10] & (1 << (n)) ? 0x100 : 0))) -#define SPRITE_COLOR(n) (vic_registers[0x27+(n)] & 0xF) -#define SPRITE_MULTICOLOR_1 (vic_registers[0x25] & 0xF) -#define SPRITE_MULTICOLOR_2 (vic_registers[0x26] & 0xF) -#define SPRITE_IS_BACK(n) (vic_registers[0x1B] & (1 << (n))) -#define SPRITE_HORZ_2X(n) (vic_registers[0x1D] & (1 << (n))) -#define SPRITE_VERT_2X(n) (vic_registers[0x17] & (1 << (n))) -#define SPRITE_MULTICOLOR(n) (vic_registers[0x1C] & (1 << (n))) -#define SPRITE_16COLOR(n) (vic_registers[0x6B] & (1 << (n))) -#define SPRITE_EXTWIDTH(n) (SPRITE_16COLOR(n) | (vic_registers[0x57] & (1 << (n)))) -#define SPRITE_EXTHEIGHT(n) (vic_registers[0x55] & (1 << (n))) -#define SPRITE_BITPLANE_ENABLE(n) (((REG_SPRBPMEN_4_7) << 4 | REG_SPRBPMEN_0_3) & (1 << (n))) -#define SPRITE_16BITPOINTER (vic_registers[0x6E] & 0x80) -#define TEXT_MODE (!REG_BMM) -#define HIRES_BITMAP_MODE (REG_BMM & !REG_MCM & !REG_EBM) -#define MULTICOLOR_BITMAP_MODE (REG_BMM & REG_MCM & !REG_EBM) -#define VIC3_ATTR_BLINK(c) ((c) & 0x1) -#define VIC3_ATTR_REVERSE(c) ((c) & 0x2) -#define VIC3_ATTR_BOLD(c) ((c) & 0x4) -#define VIC3_ATTR_UNDERLINE(c) ((c) & 0x8) -#define CHAR_IS256_COLOR(ch) (REG_FCLRLO && (ch) < 0x100) || (REG_FCLRHI && (ch) > 0x0FF) + +//#define PHYS_RASTER_COUNT (videostd_id ? NTSC_PHYSICAL_RASTERS : PAL_PHYSICAL_RASTERS) +#define SINGLE_SIDE_BORDER (((Uint16)REG_SIDBDRWD) | (REG_SIDBDRWD_U5) << 8) +#define BORDER_Y_TOP (((Uint16)REG_TBRDPOS) | (REG_TBRDPOS_U4) << 8) +#define BORDER_Y_BOTTOM (((Uint16)REG_BBRDPOS) | (REG_BBRDPOS_U4) << 8) +#define CHARGEN_Y_START (((Uint16)REG_TEXTYPOS) | (REG_TEXTYPOS_U4) << 8) +#define CHARGEN_X_START (((Uint16)REG_TEXTXPOS) | (REG_TEXTXPOS_U4) << 8) +#define CHARSTEP_BYTES (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) +#define SCREEN_RAM_ADDR_VIC (REG_SCREEN_ADDR * 1024) +#define SCREEN_ADDR ((Uint32)REG_SCRNPTR_B0 | (REG_SCRNPTR_B1<<8) | (REG_SCRNPTR_B2 <<16) | (REG_SCRNPTR_B3 << 24)) +#define CHARSET_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) +#define VIC2_BITMAP_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) +#define SPRITE_POINTER_ADDR ((Uint32)REG_SPRPTR_B0 | (REG_SPRPTR_B1<<8) | (REG_SPRPTR_B2 <<16)) +#define COLOUR_RAM_OFFSET ((((Uint16)REG_COLPTR) | (REG_COLPTR_MSB) << 8)) +//#define IS_NTSC_MODE (videostd_id) +#define SCREEN_STEP (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) +#define SPRITE_POS_Y(n) (vic_registers[1 + (n)*2]) +#define SPRITE_POS_X(n) (((Uint16)vic_registers[(n)*2]) | ( (vic_registers[0x10] & (1 << (n)) ? 0x100 : 0))) +#define SPRITE_COLOR(n) (vic_registers[0x27+(n)] & 0xF) +#define SPRITE_MULTICOLOR_1 (vic_registers[0x25] & 0xF) +#define SPRITE_MULTICOLOR_2 (vic_registers[0x26] & 0xF) +#define SPRITE_IS_BACK(n) (vic_registers[0x1B] & (1 << (n))) +#define SPRITE_HORZ_2X(n) (vic_registers[0x1D] & (1 << (n))) +#define SPRITE_VERT_2X(n) (vic_registers[0x17] & (1 << (n))) +#define SPRITE_MULTICOLOR(n) (vic_registers[0x1C] & (1 << (n))) +#define SPRITE_16COLOR(n) (vic_registers[0x6B] & (1 << (n))) +#define SPRITE_EXTWIDTH(n) (SPRITE_16COLOR(n) | (vic_registers[0x57] & (1 << (n)))) +#define SPRITE_EXTHEIGHT(n) (vic_registers[0x55] & (1 << (n))) +#define SPRITE_BITPLANE_ENABLE(n) (((REG_SPRBPMEN_4_7) << 4 | REG_SPRBPMEN_0_3) & (1 << (n))) +#define SPRITE_16BITPOINTER (vic_registers[0x6E] & 0x80) +#define TEXT_MODE (!REG_BMM) +#define HIRES_BITMAP_MODE (REG_BMM & !REG_MCM & !REG_EBM) +#define MULTICOLOR_BITMAP_MODE (REG_BMM & REG_MCM & !REG_EBM) +#define VIC3_ATTR_BLINK(c) ((c) & 0x1) +#define VIC3_ATTR_REVERSE(c) ((c) & 0x2) +#define VIC3_ATTR_BOLD(c) ((c) & 0x4) +#define VIC3_ATTR_UNDERLINE(c) ((c) & 0x8) +#define CHAR_IS256_COLOR(ch) (REG_FCLRLO && (ch) < 0x100) || (REG_FCLRHI && (ch) > 0x0FF) // "Super-Extended character attributes" (see https://github.com/MEGA65/mega65-core/blob/master/docs/viciv-modes.md) // cw is color-word (16-bit from Color RAM). sw is screen-ram-word (16bit from Screen RAM) -#define SXA_TRIM_RIGHT_BITS012(sw) ((sw) >> 13) -#define SXA_VERTICAL_FLIP(cw) ((cw) & 0x8000) -#define SXA_HORIZONTAL_FLIP(cw) ((cw) & 0x4000) -#define SXA_ALPHA_BLEND(cw) ((cw) & 0x2000) -#define SXA_GOTO_X(cw) ((cw) & 0x1000) -#define SXA_4BIT_PER_PIXEL(cw) ((cw) & 0x0800) -#define SXA_TRIM_RIGHT_BIT3(cw) ((cw) & 0x0400) -#define SXA_TRIM_TOP_BOTTOM(cw) ((cw) & 0x0300) >> 8) + +#define SXA_TRIM_RIGHT_BITS012(sw) ((sw) >> 13) +#define SXA_VERTICAL_FLIP(cw) ((cw) & 0x8000) +#define SXA_HORIZONTAL_FLIP(cw) ((cw) & 0x4000) +#define SXA_ALPHA_BLEND(cw) ((cw) & 0x2000) +#define SXA_GOTO_X(cw) ((cw) & 0x1000) +#define SXA_4BIT_PER_PIXEL(cw) ((cw) & 0x0800) +#define SXA_TRIM_RIGHT_BIT3(cw) ((cw) & 0x0400) +//FIXME: this last one was bad, and also, seems to be not used? +//#define SXA_TRIM_TOP_BOTTOM(cw) (((cw) & 0x0300) >> 8) // Multi-byte register write helpers -// --------------------------------------------------- -#define SET_11BIT_REG(basereg,x) vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0x700) >> 8); \ - vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; -#define SET_12BIT_REG(basereg,x) vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0xF00) >> 8); \ - vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; -#define SET_14BIT_REG(basereg,x) vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ - vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; -#define SET_14BIT_REGI(basereg,x) vic_registers[(basereg)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ - vic_registers[((basereg)+1)] = (Uint8) ((Uint16)(x)) & 0x00FF; -#define SET_16BIT_REG(basereg,x) vic_registers[((basereg) + 1)] = (Uint8) ((((Uint16)(x)) & 0xFF00) >> 8); \ - vic_registers[(basereg)]= ((Uint16)(x)) & 0x00FF; + +#define SET_11BIT_REG(basereg,x) do { \ + vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0x700) >> 8); \ + vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; \ + } while(0) +#define SET_12BIT_REG(basereg,x) do { \ + vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0xF00) >> 8); \ + vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; \ + } while(0) +#define SET_14BIT_REG(basereg,x) do { \ + vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ + vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; \ + } while(0) +#define SET_14BIT_REGI(basereg,x) do { \ + vic_registers[(basereg)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ + vic_registers[((basereg)+1)] = (Uint8) ((Uint16)(x)) & 0x00FF; \ + } while(0) +#define SET_16BIT_REG(basereg,x) do { \ + vic_registers[((basereg) + 1)] = (Uint8) ((((Uint16)(x)) & 0xFF00) >> 8); \ + vic_registers[(basereg)]= ((Uint16)(x)) & 0x00FF; \ + } while(0) // 11-bit registers -#define SET_PHYSICAL_RASTER(x) SET_11BIT_REG(0x52, (x)) -#define SET_RASTER_XPOS(x) SET_14BIT_REGI(0x50, (x)) +#define SET_PHYSICAL_RASTER(x) SET_11BIT_REG(0x52, (x)) +#define SET_RASTER_XPOS(x) SET_14BIT_REGI(0x50, (x)) // 12-bit registers - -#define SET_BORDER_Y_TOP(x) SET_12BIT_REG(0x48, (x)) -#define SET_BORDER_Y_BOTTOM(x) SET_12BIT_REG(0x4A, (x)) -#define SET_CHARGEN_X_START(x) SET_12BIT_REG(0x4C, (x)) -#define SET_CHARGEN_Y_START(x) SET_12BIT_REG(0x4E, (x)) +#define SET_BORDER_Y_TOP(x) SET_12BIT_REG(0x48, (x)) +#define SET_BORDER_Y_BOTTOM(x) SET_12BIT_REG(0x4A, (x)) +#define SET_CHARGEN_X_START(x) SET_12BIT_REG(0x4C, (x)) +#define SET_CHARGEN_Y_START(x) SET_12BIT_REG(0x4E, (x)) //16-bit registers -#define SET_COLORRAM_BASE(x) SET_16BIT_REG(0x64,(x)) -#define SET_CHARSTEP_BYTES(x) SET_16BIT_REG(0x58,(x)) +#define SET_COLORRAM_BASE(x) SET_16BIT_REG(0x64,(x)) +#define SET_CHARSTEP_BYTES(x) SET_16BIT_REG(0x58,(x)) // Pixel foreground/background indicator for aiding in sprite rendering -#define FOREGROUND_PIXEL 1 -#define BACKGROUND_PIXEL 0 + +#define FOREGROUND_PIXEL 1 +#define BACKGROUND_PIXEL 0 // Review this! (VIC-II values) -#define SPRITE_X_BASE_COORD 24 -#define SPRITE_Y_BASE_COORD 50 -#define SPRITE_X_UPPER_COORD 250 -#define SPRITE_Y_UPPER_COORD 344 + +#define SPRITE_X_BASE_COORD 24 +#define SPRITE_Y_BASE_COORD 50 +#define SPRITE_X_UPPER_COORD 250 +#define SPRITE_Y_UPPER_COORD 344 // Current state -// ------------- extern int vic_iomode; extern int scanline; @@ -225,6 +238,11 @@ extern int vic2_16k_bank; extern int force_fast; extern Uint8 c128_d030_reg; +extern const char *videostd_name; +extern int videostd_frametime; + +extern int user_scanlines_setting; + extern int vic_vidp_legacy, vic_chrp_legacy, vic_sprp_legacy; extern void vic_init ( void ); From 831b366a7b643ae33d070db5b6ad18246568c09e Mon Sep 17 00:00:00 2001 From: lgblgblgb Date: Wed, 22 Sep 2021 16:25:03 +0200 Subject: [PATCH 03/35] MEGA65: VIC-IV updates merging #29 --- targets/mega65/configdb.c | 7 +- targets/mega65/configdb.h | 4 + targets/mega65/hypervisor.c | 31 +- targets/mega65/hypervisor.h | 3 +- targets/mega65/mega65.c | 267 ++++++++------- targets/mega65/mega65.h | 19 +- targets/mega65/uart_monitor.c | 608 +++++++++++----------------------- targets/mega65/uart_monitor.h | 12 +- targets/mega65/vic4.h | 100 +++--- 9 files changed, 414 insertions(+), 637 deletions(-) diff --git a/targets/mega65/configdb.c b/targets/mega65/configdb.c index 53e796a7..a5f21711 100644 --- a/targets/mega65/configdb.c +++ b/targets/mega65/configdb.c @@ -86,8 +86,10 @@ static const struct xemutools_configdef_switch_st switch_options[] = { { "syscon", "Keep system console open (Windows-specific effect only)", &configdb.syscon }, { "besure", "Skip asking \"are you sure?\" on RESET or EXIT", &i_am_sure_override }, { "skipunhandledmem", "Do not even ask on unhandled memory access (hides problems!!)", &configdb.skip_unhandled_mem }, + { "fullborders", "Show non-clipped display borders", &configdb.fullborders }, { "nosound", "Disables audio output generation", &configdb.nosound }, { "noopl3", "Disables OPL3 emulation", &configdb.noopl3 }, + { "soundresetbug", "Disables audio during reset (workaround)", &configdb.soundresetbug }, { NULL } }; @@ -96,13 +98,16 @@ static const struct xemutools_configdef_num_st num_options[] = { { "model", 0xFF, "Emulated MEGA65 model (255=custom/Xemu)", &configdb.mega65_model, 0, 0xFF }, { "kicked", 0x0, "Answer to KickStart upgrade (128=ask user in a pop-up window)", &configdb.kicked, 0, 0xFF }, { "prgmode", 0, "Override auto-detect option for -prg (64 or 65 for C64/C65 modes, 0 = default, auto detect)", &configdb.prgmode, 0, 65 }, - { "rtchofs", 0, "RTC (and CIA TOD) default offset to real-time (mostly for testing!)", &configdb.rtc_hour_offset, -24, 24 }, + { "rtchofs", 0, "RTC (and CIA TOD) default hour offset to real-time -24 ... 24 (for testing!)", &configdb.rtc_hour_offset, -24, 24 }, #ifdef HAVE_XEMU_UMON { "umon", 0, "TCP-based dual mode (http / text) monitor port number [NOT YET WORKING]", &configdb.umon, 0, 0xFFFF }, #endif { "sdlrenderquality", RENDER_SCALE_QUALITY, "Setting SDL hint for scaling method/quality on rendering (0, 1, 2)", &configdb.sdlrenderquality, 0, 2 }, { "stereoseparation", AUDIO_DEFAULT_SEPARATION, "Audio stereo separation; 100(hard-stereo) ... 0(mono) ... -100(hard-reversed-stereo); default: " STRINGIFY(AUDIO_DEFAULT_SEPARATION), &configdb.stereoseparation, -100, 100 }, { "mastervolume", AUDIO_DEFAULT_VOLUME, "Audio emulation mixing final volume (100=unchanged ... 0=silence); default: " STRINGIFY(AUDIO_DEFAULT_VOLUME), &configdb.mastervolume, 0, 100 }, + { "forcevideostd", -1, "Force video standard (0 = PAL, 1 = NTSC, -1 = default switchable by VIC-IV)", &configdb.force_videostd, -1, 1 }, + // FIXME: as a workaround, I set this to "0" PAL, as newer MEGA65's default is this. HOWEVER this should be not handled this way but using a newer Hyppo! + { "initvideostd", 0, "Use given video standard as the startup one (0 = PAL, 1 = NTSC, -1 = Hyppo default)", &configdb.init_videostd, -1, 1 }, { "sidmask", 15, "Enabled SIDs of the four, in form of a bitmask", &configdb.sidmask, 0, 15 }, { NULL } }; diff --git a/targets/mega65/configdb.h b/targets/mega65/configdb.h index eca95d22..cb7b7f80 100644 --- a/targets/mega65/configdb.h +++ b/targets/mega65/configdb.h @@ -58,6 +58,9 @@ struct configdb_st { char *keymap; #endif char *selectedgui; + int force_videostd; + int init_videostd; + int fullborders; int show_drive_led; int hyperdebug; int hyperserialascii; @@ -87,6 +90,7 @@ struct configdb_st { int nosound; int noopl3; int sidmask; + int soundresetbug; }; extern struct configdb_st configdb; diff --git a/targets/mega65/hypervisor.c b/targets/mega65/hypervisor.c index ccee0780..f7c90efb 100644 --- a/targets/mega65/hypervisor.c +++ b/targets/mega65/hypervisor.c @@ -38,6 +38,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ int in_hypervisor; // mega65 hypervisor mode +int hypervisor_to_enable_audio = 0; static char debug_lines[0x4000][2][INFO_MAX_SIZE]; // I know. UGLY! and wasting memory. But this is only a HACK :) static int resolver_ok = 0; @@ -152,7 +153,7 @@ void hypervisor_enter_via_write_trap ( int trapno ) if (do_nop_check) { // FIXME: for real there should be a memory reading function independent to the one used by the CPU, since // this has some side effects to just fetch a byte to check something, which is otherwise used normally to fetch CPU opcodes and such - const Uint8 skipped_byte = cpu65_read_callback(cpu65.pc); + Uint8 skipped_byte = cpu65_read_callback(cpu65.pc); if (XEMU_UNLIKELY(skipped_byte != 0xEA)) { // $EA = opcode of NOP char msg[256]; snprintf(msg, sizeof msg, @@ -278,7 +279,7 @@ void hypervisor_leave ( void ) memory_set_vic3_rom_mapping(vic_registers[0x30]); // restore possible active VIC-III mapping memory_set_do_map(); // restore mapping ... if (XEMU_UNLIKELY(first_hypervisor_leave)) { - DEBUGPRINT("HYPERVISOR: first return after RESET." NL); + DEBUGPRINT("HYPERVISOR: first return after RESET, start of processing workarounds." NL); first_hypervisor_leave = 0; int new_pc = refill_c65_rom_from_preinit_cache(); // this function should decide then, if it's really a (forced) thing to do ... if (new_pc >= 0) { @@ -289,7 +290,20 @@ void hypervisor_leave ( void ) cpu65.pc = new_pc; } else DEBUGPRINT("MEM: no forced ROM re-apply policy was requested" NL); - dma_init_set_rev(configdb.dmarev, main_ram + 0x20000 + 0x16); + dma_init_set_rev(configdb.dmarev, main_ram + 0x20000); + if (configdb.init_videostd >= 0) { + DEBUGPRINT("VIC: setting %s mode as initial-default based on request" NL, configdb.init_videostd ? "NTSC" : "PAL"); + if (configdb.init_videostd) + vic_registers[0x6F] |= 0x80; + else + vic_registers[0x6F] &= 0x7F; + } + if (hypervisor_to_enable_audio) { + hypervisor_to_enable_audio = 0; + configdb.nosound = 0; + DEBUGPRINT("HYPERVISOR: enabling audio (workaround)" NL); + } + DEBUGPRINT("HYPERVISOR: first return after RESET, end of processing workarounds." NL); } if (XEMU_UNLIKELY(hypervisor_queued_trap >= 0)) { // Not so much used currently ... @@ -299,20 +313,13 @@ void hypervisor_leave ( void ) } } -void write_hypervisor_byte(char byte); + void hypervisor_serial_monitor_push_char ( Uint8 chr ) { if (hypervisor_monout_p >= hypervisor_monout - 1 + sizeof hypervisor_monout) - { - // NOTE: For long mega65_ftp actions like 'get MYDISK.D81`, a lot of - // serial chars will be sent that are raw binary, and there's a good chance - // it could overflow the 'hypervisor_monout' buffer. - // So in such cases, I'll simply empty hypervisor_monout. - hypervisor_monout_p = hypervisor_monout; - } + return; int flush = (chr == 0x0A || chr == 0x0D || chr == 0x8A || chr == 0x8D); - write_hypervisor_byte(chr); if (hypervisor_monout_p == hypervisor_monout && flush) return; if (flush) { diff --git a/targets/mega65/hypervisor.h b/targets/mega65/hypervisor.h index 1f28d7a0..d66cfa1b 100644 --- a/targets/mega65/hypervisor.h +++ b/targets/mega65/hypervisor.h @@ -23,7 +23,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define TRAP_RESTORE 0x42 #define TRAP_ALTTAB 0x43 -extern int in_hypervisor; +extern int in_hypervisor; +extern int hypervisor_to_enable_audio; extern int hypervisor_debug_init ( const char *fn, int hypervisor_debug, int use_hypervisor_serial_out_asciizer ); extern void hypervisor_debug ( void ); diff --git a/targets/mega65/mega65.c b/targets/mega65/mega65.c index 7c6e3786..4eab2606 100644 --- a/targets/mega65/mega65.c +++ b/targets/mega65/mega65.c @@ -41,28 +41,30 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "audio65.h" #include "inject.h" #include "configdb.h" +#include "xemu/emutools_socketapi.h" static int nmi_level; // please read the comment at nmi_set() below int newhack = 0; -unsigned int frames_total_counter = 0; -static int cpu_cycles_per_scanline_for_fast_mode, speed_current; -static char fast_mhz_in_string[16]; -static int frame_counter; -static int paused = 0, paused_old = 0; -static int breakpoint_pc = -1; +static int speed_current = -1; +static int paused = 0, paused_old = 0; +static int breakpoint_pc = -1; #ifdef TRACE_NEXT_SUPPORT -static int orig_sp = 0; -static int trace_next_trigger = 0; +static int orig_sp = 0; +static int trace_next_trigger = 0; #endif -static int trace_step_trigger = 0; +static int trace_step_trigger = 0; #ifdef HAS_UARTMON_SUPPORT static void (*m65mon_callback)(void) = NULL; #endif static const char emulator_paused_title[] = "TRACE/PAUSE"; -static char emulator_speed_title[64] = "?MHz"; +static char emulator_speed_title[64] = ""; +static char fast_mhz_in_string[16] = ""; +static const char *cpu_clock_speed_strs[4] = { "1MHz", "2MHz", "3.5MHz", fast_mhz_in_string }; +static unsigned int cpu_clock_speed_str_index = 0; +static unsigned int cpu_cycles_per_scanline; static int cpu_cycles_per_step = 100; // some init value, will be overriden, but it must be greater initially than "only a few" anyway static int force_external_rom = 0; @@ -70,7 +72,9 @@ static int force_external_rom = 0; static Uint8 nvram_original[sizeof nvram]; static int uuid_must_be_saved = 0; -int register_screenshot_request = 0; +int registered_screenshot_request = 0; + +Uint8 last_dd00_bits = 3; // Bank 0 @@ -101,35 +105,39 @@ void machine_set_speed ( int verbose ) ); // ^1 at c128... because it was inverted :-O --> FIXME: this is ugly workaround, the switch statement should be re-organized speed_wanted = (in_hypervisor || (D6XX_registers[0x7D] & 16)) ? 7 : ((((c128_d030_reg & 1) ^ 1) << 2) | ((vic_registers[0x31] & 64) >> 5) | ((vic_registers[0x54] & 64) >> 6)); - if (speed_wanted != speed_current) { + // videostd_changed: we also want to force recalulation if PAL/NTSC change happened, even if the speed setting remains the same! + if (speed_wanted != speed_current || videostd_changed) { speed_current = speed_wanted; + videostd_changed = 0; switch (speed_wanted) { + // NOTE: videostd_1mhz_cycles_per_scanline is set by vic4.c and also includes the video standard case 4: // 100 - 1MHz case 5: // 101 - 1MHz - cpu_cycles_per_scanline = CPU_C64_CYCLES_PER_SCANLINE; - strcpy(emulator_speed_title, "1MHz"); + cpu_cycles_per_scanline = (unsigned int)(videostd_1mhz_cycles_per_scanline * (float)(C64_MHZ_CLOCK)); + cpu_clock_speed_str_index = 0; cpu65_set_ce_timing(0); break; case 0: // 000 - 2MHz - cpu_cycles_per_scanline = CPU_C128_CYCLES_PER_SCANLINE; - strcpy(emulator_speed_title, "2MHz"); + cpu_cycles_per_scanline = (unsigned int)(videostd_1mhz_cycles_per_scanline * (float)(C128_MHZ_CLOCK)); + cpu_clock_speed_str_index = 1; cpu65_set_ce_timing(0); break; case 2: // 010 - 3.5MHz case 6: // 110 - 3.5MHz - cpu_cycles_per_scanline = CPU_C65_CYCLES_PER_SCANLINE; - strcpy(emulator_speed_title, "3.5MHz"); + cpu_cycles_per_scanline = (unsigned int)(videostd_1mhz_cycles_per_scanline * (float)(C65_MHZ_CLOCK)); + cpu_clock_speed_str_index = 2; cpu65_set_ce_timing(1); break; case 1: // 001 - 40MHz (or Xemu specified custom speed) case 3: // 011 - -- "" -- case 7: // 111 - -- "" -- - cpu_cycles_per_scanline = cpu_cycles_per_scanline_for_fast_mode; - strcpy(emulator_speed_title, fast_mhz_in_string); + cpu_cycles_per_scanline = (unsigned int)(videostd_1mhz_cycles_per_scanline * (float)(configdb.fast_mhz)); + cpu_clock_speed_str_index = 3; cpu65_set_ce_timing(1); break; } - DEBUG("SPEED: CPU speed is set to %s" NL, emulator_speed_title); + // XXX use only DEBUG() here! + DEBUGPRINT("SPEED: CPU speed is set to %s, cycles per scanline: %d in %s (1MHz cycles per scanline: %f)" NL, cpu_clock_speed_strs[cpu_clock_speed_str_index], cpu_cycles_per_scanline, videostd_name, videostd_1mhz_cycles_per_scanline); if (cpu_cycles_per_step > 1) // if in trace mode, do not set this! cpu_cycles_per_step = cpu_cycles_per_scanline; } @@ -177,12 +185,26 @@ static void cia2_setint_cb ( int level ) static void cia2_out_a ( Uint8 data ) { + // XXX My code +#if 0 vic2_16k_bank = ((~(data | (~cia2.DDRA))) & 3) << 14; vic_vidp_legacy = 1; vic_chrp_legacy = 1; vic_sprp_legacy = 1; // TODO FIXME: add sprites pointers! DEBUG("VIC2: 16K BANK is set to $%04X (CIA mask=$%02X)" NL, vic2_16k_bank, cia2.DDRA); +#endif + // Code from HMW, XXX FIXME + // Note, I have removed the REG_CRAM2K since it's not possible to hit this callback anyways if CIA is "covered" by colour RAM + if (REG_HOTREG) { + // Bank select + data &= (cia2.DDRA & 3); // Mask bank bits through CIA DDR register bits + REG_SCRNPTR_B1 = (~data << 6) | (REG_SCRNPTR_B1 & 0x3F); + REG_CHARPTR_B1 = (~data << 6) | (REG_CHARPTR_B1 & 0x3F); + REG_SPRPTR_B1 = (~data << 6) | (REG_SPRPTR_B1 & 0x3F); + last_dd00_bits = data; + //DEBUGPRINT("VIC2: (hotreg)Wrote to $DD00: $%02x screen=$%08x char=$%08x spr=$%08x" NL, data, SCREEN_ADDR, CHARSET_ADDR, SPRITE_POINTER_ADDR); + } } @@ -383,13 +405,12 @@ static void mega65_init ( void ) // Initialize FDC fdc_init(disk_buffers + FD_BUFFER_POS); // - sdcard_hack_mount_drive_9_now(configdb.disk9); + sdcard_hack_mount_drive_9_now(configdb.disk9); // FIXME: Ugly hack to support CLI forced drive-9 disk #ifdef HAS_UARTMON_SUPPORT uartmon_init(configdb.uartmon); #endif sprintf(fast_mhz_in_string, "%.2fMHz", configdb.fast_mhz); - cpu_cycles_per_scanline_for_fast_mode = 64 * configdb.fast_mhz; - DEBUGPRINT("SPEED: fast clock is set to %.2fMHz, %d CPU cycles per scanline." NL, configdb.fast_mhz, cpu_cycles_per_scanline_for_fast_mode); + DEBUGPRINT("SPEED: fast clock is set to %.2fMHz." NL, configdb.fast_mhz); cpu65_reset(); // reset CPU (though it fetches its reset vector, we don't use that on M65, but the KS hypervisor trap) rom_protect = 0; hypervisor_start_machine(); @@ -440,6 +461,12 @@ static void shutdown_callback ( void ) #endif #ifdef HAS_UARTMON_SUPPORT uartmon_close(); +#endif +#ifdef HAVE_XEMU_UMON + xumon_stop(); +#endif +#ifdef XEMU_HAS_SOCKET_API + xemusock_uninit(); #endif DEBUG("Execution has been stopped at PC=$%04X" NL, cpu65.pc); } @@ -448,6 +475,10 @@ static void shutdown_callback ( void ) void reset_mega65 ( void ) { + if (!configdb.nosound && configdb.soundresetbug) { + configdb.nosound = 1; + hypervisor_to_enable_audio = 1; + } eth65_reset(); D6XX_registers[0x7D] &= ~16; // FIXME: other default speed controls on reset? c128_d030_reg = 0xFF; @@ -464,7 +495,7 @@ void reset_mega65 ( void ) nmi_level = 0; D6XX_registers[0x7E] = configdb.kicked; hypervisor_start_machine(); - DEBUGPRINT("SYSTEM RESET." NL); + DEBUGPRINT("SYSTEM: RESET" NL); } @@ -478,55 +509,17 @@ int reset_mega65_asked ( void ) } -static void update_emulator ( void ) -{ - hid_handle_all_sdl_events(); - xemugui_iteration(); - nmi_set(IS_RESTORE_PRESSED(), 2); // Custom handling of the restore key ... - // this part is used to trigger 'RESTORE trap' with long press on RESTORE. - // see input_devices.c for more information - kbd_trigger_restore_trap(); -#ifdef HAS_UARTMON_SUPPORT - uartmon_update(); -#endif - // Screen rendering: begin - vic_render_screen(); - // Screen rendering: end - xemu_timekeeping_delay(40000); - // Ugly CIA trick to maintain realtime TOD in CIAs :) -// if (seconds_timer_trigger) { - const struct tm *t = xemu_get_localtime(); - const Uint8 sec10ths = xemu_get_microseconds() / 100000; - // UPDATE CIA TODs: - cia_ugly_tod_updater(&cia1, t, sec10ths, configdb.rtc_hour_offset); - cia_ugly_tod_updater(&cia2, t, sec10ths, configdb.rtc_hour_offset); - // UPDATE the RTC too: - rtc_regs[0] = XEMU_BYTE_TO_BCD(t->tm_sec); // seconds - rtc_regs[1] = XEMU_BYTE_TO_BCD(t->tm_min); // minutes - //rtc_regs[2] = xemu_hour_to_bcd12h(t->tm_hour, configdb.rtc_hour_offset); // hours - rtc_regs[2] = XEMU_BYTE_TO_BCD((t->tm_hour + configdb.rtc_hour_offset + 24) % 24) | 0x80; // hours (24H format, bit 7 always set) - rtc_regs[3] = XEMU_BYTE_TO_BCD(t->tm_mday); // day of mounth - rtc_regs[4] = XEMU_BYTE_TO_BCD(t->tm_mon) + 1; // month - rtc_regs[5] = XEMU_BYTE_TO_BCD(t->tm_year - 100); // year -// } -} - - #ifdef HAS_UARTMON_SUPPORT void m65mon_show_regs ( void ) { Uint8 pf = cpu65_get_pf(); umon_printf( - "\r\n" "PC A X Y Z B SP MAPL MAPH LAST-OP P P-FLAGS RGP uS IO\r\n" "%04X %02X %02X %02X %02X %02X %04X " // register banned message and things from PC to SP "%04X %04X %02X %02X %02X " // from MAPL to P - "%c%c%c%c%c%c%c%c \r\n" // P-FLAGS - ",0777%04X\r\n", // TODO: single line of disassembly + "%c%c%c%c%c%c%c%c ", // P-FLAGS cpu65.pc, cpu65.a, cpu65.x, cpu65.y, cpu65.z, cpu65.bphi >> 8, cpu65.sphi | cpu65.s, - ((map_mask & 0xf0) << 8) | (map_offset_low >> 8), - ((map_mask & 0x0f) << 12) | (map_offset_high >> 8), - cpu65.op, + map_offset_low >> 8, map_offset_high >> 8, cpu65.op, pf, 0, // flags (pf & CPU65_PF_N) ? 'N' : '-', (pf & CPU65_PF_V) ? 'V' : '-', @@ -535,8 +528,7 @@ void m65mon_show_regs ( void ) (pf & CPU65_PF_D) ? 'D' : '-', (pf & CPU65_PF_I) ? 'I' : '-', (pf & CPU65_PF_Z) ? 'Z' : '-', - (pf & CPU65_PF_C) ? 'C' : '-', - cpu65.pc + (pf & CPU65_PF_C) ? 'C' : '-' ); } @@ -552,30 +544,17 @@ void m65mon_dumpmem28 ( int addr ) { int n = 16; addr &= 0xFFFFFFF; - umon_printf(":%08X:", addr); + umon_printf(":%07X:", addr); while (n--) - { - if ( (addr >> 16) == 0x777) - { - umon_printf("%02X", cpu65_read_callback(addr & 0xffff)); - addr++; - } - else - umon_printf("%02X", memory_debug_read_phys_addr(addr++)); - } + umon_printf("%02X", memory_debug_read_phys_addr(addr++)); } void m65mon_setmem28( int addr, int cnt, Uint8* vals ) { - for (int k = 0; k < cnt; k++) - { - memory_debug_write_phys_addr(addr++, vals[k]); - } -} - -void m65mon_setpc(int addr) -{ - cpu65.pc = addr; + for (int k = 0; k < cnt; k++) + { + memory_debug_write_phys_addr(addr++, vals[k]); + } } void m65mon_set_trace ( int m ) @@ -601,7 +580,7 @@ void m65mon_do_next ( void ) void m65mon_do_trace ( void ) { if (paused) { - set_umon_send_ok(0); // delay command execution! + umon_send_ok = 0; // delay command execution! m65mon_callback = m65mon_show_regs; // register callback trace_step_trigger = 1; // trigger one step } else { @@ -636,10 +615,59 @@ void m65mon_breakpoint ( int brk ) } #endif -static int cycles, frameskip; + +static void update_emulator ( void ) +{ + vic4_close_frame_access(); + // XXX: some things has been moved here from the main loop, however update_emulator is called from other places as well, FIXME check if it causes problems or not! + if (XEMU_UNLIKELY(inject_ready_check_status)) + inject_ready_check_do(); + audio65_sid_inc_framecount(); + strcpy(emulator_speed_title, cpu_clock_speed_strs[cpu_clock_speed_str_index]); + strcat(emulator_speed_title, " "); + strcat(emulator_speed_title, videostd_name); + hid_handle_all_sdl_events(); + xemugui_iteration(); + nmi_set(IS_RESTORE_PRESSED(), 2); // Custom handling of the restore key ... + // this part is used to trigger 'RESTORE trap' with long press on RESTORE. + // see input_devices.c for more information + kbd_trigger_restore_trap(); +#ifdef HAS_UARTMON_SUPPORT + uartmon_update(); +#endif + // Screen updating, final phase + //vic4_close_frame_access(); + // Let's sleep ... + xemu_timekeeping_delay(videostd_frametime); + // Ugly CIA trick to maintain realtime TOD in CIAs :) +// if (seconds_timer_trigger) { + const struct tm *t = xemu_get_localtime(); + const Uint8 sec10ths = xemu_get_microseconds() / 100000; + // UPDATE CIA TODs: + cia_ugly_tod_updater(&cia1, t, sec10ths, configdb.rtc_hour_offset); + cia_ugly_tod_updater(&cia2, t, sec10ths, configdb.rtc_hour_offset); + // UPDATE the RTC too: + rtc_regs[0] = XEMU_BYTE_TO_BCD(t->tm_sec); // seconds + rtc_regs[1] = XEMU_BYTE_TO_BCD(t->tm_min); // minutes + //rtc_regs[2] = xemu_hour_to_bcd12h(t->tm_hour, configdb.rtc_hour_offset); // hours + rtc_regs[2] = XEMU_BYTE_TO_BCD((t->tm_hour + configdb.rtc_hour_offset + 24) % 24) | 0x80; // hours (24H format, bit 7 always set) + rtc_regs[3] = XEMU_BYTE_TO_BCD(t->tm_mday); // day of mounth + rtc_regs[4] = XEMU_BYTE_TO_BCD(t->tm_mon) + 1; // month + rtc_regs[5] = XEMU_BYTE_TO_BCD(t->tm_year - 100); // year +// } +} + static void emulation_loop ( void ) { + static int cycles = 0; // used for "balance" CPU cycles per scanline, must be static! + xemu_window_snap_to_optimal_size(0); + vic4_open_frame_access(); + // video standard (PAL/NTSC) affects the "CPU cycles per scanline" variable, which is used in this main emulation loop below. + // thus, if vic-4 emulation set videostd_changed, we should react with enforce a re-calibration. + // videostd_changed is set by vic4_open_frame_access() in vic4.c, thus we do here right after calling it. + // machine_set_speed() will react to videostd_changed flag, so it's just enough to call it from here + machine_set_speed(0); for (;;) { #ifdef TRACE_NEXT_SUPPORT if (trace_next_trigger == 2) { @@ -663,12 +691,13 @@ static void emulation_loop ( void ) if (m65mon_callback) { // delayed uart monitor command should be finished ... m65mon_callback(); m65mon_callback = NULL; - uartmons_finish_command(); + uartmon_finish_command(); } #endif - // we still need to feed our emulator with update events ... It also slows this pause-busy-loop down to every full frames (~25Hz) + // we still need to feed our emulator with update events ... It also slows this pause-busy-loop down to every full frames (~25Hz) <--- XXX totally inaccurate now! // note, that it messes timing up a bit here, as there is update_emulator() calls later in the "normal" code as well // this can be a bug, but real-time emulation is not so much an issue if you eg doing trace of your code ... + // XXX it's maybe a problem to call this!!! update_emulator() is called here which closes frame but no no reopen then ... FIXME: handle this somehow! update_emulator(); if (trace_step_trigger) { // if monitor trigges a step, break the pause loop, however we will get back the control on the next @@ -699,7 +728,6 @@ static void emulation_loop ( void ) } if (XEMU_UNLIKELY(breakpoint_pc == cpu65.pc)) { DEBUGPRINT("TRACE: Breakpoint @ $%04X hit, Xemu moves to trace mode after the execution of this opcode." NL, cpu65.pc); - m65mon_show_regs(); paused = 1; } cycles += XEMU_UNLIKELY(dma_status) ? dma_update_multi_steps(cpu_cycles_per_scanline) : cpu65_step( @@ -708,40 +736,20 @@ static void emulation_loop ( void ) #endif ); // FIXME: this is maybe not correct, that DMA's speed depends on the fast/slow clock as well? if (cycles >= cpu_cycles_per_scanline) { - scanline++; - //DEBUG("VIC3: new scanline (%d)!" NL, scanline); cycles -= cpu_cycles_per_scanline; - cia_tick(&cia1, 64); - cia_tick(&cia2, 64); - if (scanline == 312) { - if (XEMU_UNLIKELY(inject_ready_check_status)) - inject_ready_check_do(); - //DEBUG("VIC3: new frame!" NL); - frameskip = !frameskip; - scanline = 0; - if (!frameskip) // well, let's only render every full frames (~ie 25Hz) - update_emulator(); - audio65_sid_inc_framecount(); - frame_counter++; - if (frame_counter == 25) { - frame_counter = 0; - vic3_blink_phase = !vic3_blink_phase; - } - frames_total_counter++; - if (!frameskip) // FIXME: do this better!!!!!! - return; - } - //DEBUG("RASTER=%d COMPARE=%d" NL,scanline,compare_raster); - //vic_interrupt(); - vic3_check_raster_interrupt(); + cia_tick(&cia1, 32); // FIXME: why 32?????? why fixed????? what should be the CIA "tick" frequency for real? Is it dependent on NTSC/PAL? + cia_tick(&cia2, 32); + if (XEMU_UNLIKELY(vic4_render_scanline())) + break; // break the (main, "for") loop, if frame is over! } } + update_emulator(); } int main ( int argc, char **argv ) { - xemu_pre_init(APP_ORG, TARGET_NAME, "The Incomplete MEGA65 emulator from LGB"); + xemu_pre_init(APP_ORG, TARGET_NAME, "The Evolving MEGA65 emulator from LGB"); configdb_define_emulator_options(sizeof configdb); if (xemucfg_parse_all(argc, argv)) return 1; @@ -759,8 +767,8 @@ int main ( int argc, char **argv ) TARGET_DESC APP_DESC_APPEND, // window title 1, // resizable window TEXTURE_WIDTH, TEXTURE_HEIGHT, // texture sizes - TEXTURE_WIDTH, TEXTURE_HEIGHT * 2,// logical size (used with keeping aspect ratio by the SDL render stuffs) - TEXTURE_WIDTH, TEXTURE_HEIGHT * 2,// window size + TEXTURE_WIDTH, TEXTURE_HEIGHT, // logical size (used with keeping aspect ratio by the SDL render stuffs) + TEXTURE_WIDTH, TEXTURE_HEIGHT, // window size TEXTURE_FORMAT, // pixel format 0, // we have *NO* pre-defined colours as with more simple machines (too many we need). we want to do this ourselves! NULL, // -- "" -- @@ -793,19 +801,9 @@ int main ( int argc, char **argv ) #endif ); #ifdef HAVE_XEMU_UMON - if (configdb.umon != 0) { - int port = configdb.umon; - int threaded; - if (port < 0) { - port = -port; - threaded = 0; - } else - threaded = 1; - if (port > 1023 && port < 65536) - xumon_init(port, threaded); - else - ERROR_WINDOW("UMON: Invalid TCP port: %d", port); - } + if (configdb.umon == 1) + configdb.umon = XUMON_DEFAULT_PORT; + xumon_init(configdb.umon); #endif if (configdb.prg) inject_register_prg(configdb.prg, configdb.prgmode); @@ -818,15 +816,12 @@ int main ( int argc, char **argv ) } else if (configdb.autoload) c64_register_fake_typing(fake_typing_for_load65); #endif - cycles = 0; - frameskip = 0; - frame_counter = 0; - vic3_blink_phase = 0; audio65_start(); xemu_set_full_screen(configdb.fullscreen_requested); if (!configdb.syscon) sysconsole_close(NULL); xemu_timekeeping_start(); + // FIXME: for emscripten (anyway it does not work too much currently) there should be 50 or 60 (PAL/NTSC) instead of (fixed, and wrong!) 25!!!!!! XEMU_MAIN_LOOP(emulation_loop, 25, 1); return 0; } diff --git a/targets/mega65/mega65.h b/targets/mega65/mega65.h index 2cd0a830..6508a8a6 100644 --- a/targets/mega65/mega65.h +++ b/targets/mega65/mega65.h @@ -35,23 +35,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define CHAR_ROM_NAME "CHARROM.ROM" #define CHAR_ROM_SIZE 0x2000 -/* Do *NOT* modify these, as other parts of the emulator currently depends on these values ... - You can try RENDER_SCALE_QUALITY though with values 0, 1, 2 */ +// Do *NOT* modify these, as other parts of the emulator currently depends on these values ... #define TEXTURE_FORMAT SDL_PIXELFORMAT_ARGB8888 #define USE_LOCKED_TEXTURE 1 #define RENDER_SCALE_QUALITY 0 -#define TEXTURE_WIDTH 640 -#define TEXTURE_HEIGHT 200 +#define C64_MHZ_CLOCK 1.0 +#define C128_MHZ_CLOCK 2.0 +#define C65_MHZ_CLOCK 3.5 // Default fast clock of M65, in MHz (can be overriden with CLI switch) #define MEGA65_DEFAULT_FAST_CLOCK 40.5 -// Needed CPU cycles for a (PAL) scanline for a given mode. -// For "fast clock", it's calculated, see MEGA65_DEFAULT_FAST_CLOCK -#define CPU_C65_CYCLES_PER_SCANLINE 227 -#define CPU_C128_CYCLES_PER_SCANLINE 128 -#define CPU_C64_CYCLES_PER_SCANLINE 64 - #define SID_CYCLES_PER_SEC 1000000 #define AUDIO_SAMPLE_FREQ 44100 @@ -59,7 +53,6 @@ extern void m65mon_show_regs ( void ); extern void m65mon_dumpmem16 ( Uint16 addr ); extern void m65mon_dumpmem28 ( int addr ); extern void m65mon_setmem28 ( int addr, int cnt, Uint8* vals ); -extern void m65mon_setpc(int addr); extern void m65mon_set_trace ( int m ); extern void m65mon_do_trace ( void ); #ifdef TRACE_NEXT_SUPPORT @@ -79,7 +72,7 @@ extern int dump_memory ( const char *fn ); extern int refill_c65_rom_from_preinit_cache ( void ); extern int newhack; -extern unsigned int frames_total_counter; -extern int register_screenshot_request; +extern int registered_screenshot_request; +extern Uint8 last_dd00_bits; #endif diff --git a/targets/mega65/uart_monitor.c b/targets/mega65/uart_monitor.c index 1d9b794a..781b083e 100644 --- a/targets/mega65/uart_monitor.c +++ b/targets/mega65/uart_monitor.c @@ -44,6 +44,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //#include #endif +int umon_write_size; +int umon_send_ok; +char umon_write_buffer[UMON_WRITE_BUFFER_SIZE]; #define UNCONNECTED XS_INVALID_SOCKET @@ -53,44 +56,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ # define PRINTF_SOCK "%d" #endif -typedef struct -{ - int umon_write_size; - int umon_send_ok; - char umon_write_buffer[UMON_WRITE_BUFFER_SIZE]; - - xemusock_socket_t sock_server; - xemusock_socklen_t sock_len; - xemusock_socket_t sock_client; - - int umon_write_pos, umon_read_pos; - int umon_echo; - char umon_read_buffer[0x1000]; - int loadcmdflag; - int loadcmdcurraddr; - int loadcmdendaddr; +static xemusock_socket_t sock_server = UNCONNECTED; +static xemusock_socklen_t sock_len; +static xemusock_socket_t sock_client = UNCONNECTED; - char * locptr; -} comms_details_type; +static int umon_write_pos, umon_read_pos; +static int umon_echo; +static char umon_read_buffer [0x1000]; -#define MAXPORTS 2 - -static comms_details_type comdet[MAXPORTS] = -{ - [0].sock_server = UNCONNECTED, - [0].sock_client = UNCONNECTED, - [0].loadcmdflag = 0, - [0].loadcmdcurraddr = 0, - [0].loadcmdendaddr = 0, - [0].locptr = 0, - [1].sock_server = UNCONNECTED, - [1].sock_client = UNCONNECTED, - [1].loadcmdflag = 0, - [1].loadcmdcurraddr = 0, - [1].loadcmdendaddr = 0, - [1].locptr = 0 -}; // WARNING: This source is pretty ugly, ie not so much check of overflow of the output (write) buffer. @@ -163,7 +137,7 @@ static void setmem28 ( char *param, int addr ) } -static void execute_command ( comms_details_type *cd, char *cmd ) +static void execute_command ( char *cmd ) { int bank; int par1; @@ -201,7 +175,10 @@ static void execute_command ( comms_details_type *cd, char *cmd ) bank = par1 >> 16; if (cmd && check_end_of_command(cmd, 1)) { - m65mon_dumpmem28(par1); + if (bank == 0x777) + m65mon_dumpmem16(par1); + else + m65mon_dumpmem28(par1); } break; case 'M': @@ -209,7 +186,7 @@ static void execute_command ( comms_details_type *cd, char *cmd ) bank = par1 >> 16; if (cmd && check_end_of_command(cmd, 1)) { - for (int k = 0; k < 16; k++) + for (int k = 0; k < 32; k++) { if (bank == 0x777) m65mon_dumpmem16(par1); @@ -224,16 +201,6 @@ static void execute_command ( comms_details_type *cd, char *cmd ) cmd = parse_hex_arg(cmd, &par1, 0, 0xFFFFFFF); setmem28(cmd, par1); break; - case 'l': - cd->loadcmdflag = 1; - cmd = parse_hex_arg(cmd, &cd->loadcmdcurraddr, 0, 0xFFFFFFF); - cmd = parse_hex_arg(cmd, &cd->loadcmdendaddr, 0, 0xFFFF); - cd->loadcmdendaddr += (cd->loadcmdcurraddr & 0xFFF0000); - break; - case 'g': - cmd = parse_hex_arg(cmd, &par1, 0, 0xFFFF); - m65mon_setpc(par1); - break; case 't': if (!*cmd) m65mon_do_trace(); @@ -272,11 +239,7 @@ static void execute_command ( comms_details_type *cd, char *cmd ) int uartmon_is_active ( void ) { - for (int idx = 0; idx < MAXPORTS; idx++) - if (comdet[idx].sock_server != UNCONNECTED) - return 1; - - return 0; + return sock_server != UNCONNECTED; } @@ -289,420 +252,229 @@ int uartmon_init ( const char *fn ) ERROR_WINDOW("UARTMON: already activated on %s", fn_stored); return 1; } - for (int idx = 0; idx < MAXPORTS; idx++) - { - comdet[idx].sock_server = UNCONNECTED; - comdet[idx].sock_client = UNCONNECTED; - } + sock_server = UNCONNECTED; + sock_client = UNCONNECTED; if (!fn || !*fn) { DEBUGPRINT("UARTMON: disabled, no name is specified to bind to." NL); return 0; } - if (xemusock_init(NULL)) { - ERROR_WINDOW("Cannot initialize network, uart_mon won't be availbale"); + const char *init_status = xemusock_init(); + if (init_status) { + ERROR_WINDOW("Cannot initialize network, uart_mon won't be availbale\n%s", init_status); return 1; } - for (int idx = 0; idx < MAXPORTS; idx++) - { - if (fn[0] == ':') { - int port = atoi(fn + 1); - if (port < 1024 || port > 65535) { - ERROR_WINDOW("uartmon: invalid port specification %d (1024-65535 is allowed) from string %s", port, fn); - return 1; - } - struct sockaddr_in sock_st; - comdet[idx].sock_len = sizeof(struct sockaddr_in); - //sock = socket(AF_INET, SOCK_STREAM, 0); - - sock = xemusock_create_for_inet(XEMUSOCK_TCP, XEMUSOCK_BLOCKING, &xerr); - if (sock == XS_INVALID_SOCKET) { - ERROR_WINDOW("Cannot create TCP socket: %s", xemusock_strerror(xerr)); - return 1; - } - if (xemusock_setsockopt_reuseaddr(sock, &xerr)) { - ERROR_WINDOW("UARTMON: setsockopt for SO_REUSEADDR failed with %s", xemusock_strerror(xerr)); - } - //sock_st.sin_family = AF_INET; - //sock_st.sin_addr.s_addr = htonl(INADDR_ANY); - //sock_st.sin_port = htons(curport); - xemusock_fill_servaddr_for_inet_ip_native(&sock_st, 0, port+idx); - if (xemusock_bind(sock, (struct sockaddr*)&sock_st, comdet[idx].sock_len, &xerr)) { - ERROR_WINDOW("Cannot bind TCP socket %d, UART monitor cannot be used: %s", port+idx, xemusock_strerror(xerr)); - xemusock_close(sock, NULL); - return 1; - } - } else { -#if !defined(XEMU_ARCH_UNIX) - ERROR_WINDOW("On non-UNIX systems, you must use TCP/IP sockets, so uartmon parameter must be in form of :n (n=port number to bind to)\nUARTMON is not available because of bad syntax."); + if (fn[0] == ':') { + int port = atoi(fn + 1); + if (port < 1024 || port > 65535) { + ERROR_WINDOW("uartmon: invalid port specification %d (1024-65535 is allowed) from string %s", port, fn); return 1; -#else - if (idx != 0) // just open one socket for unix - continue; - // This is UNIX specific code (UNIX named socket) thus it's OK not use Xemu socket API calls here. - // Note: on longer term, we want to drop this, and allow only TCP sockets to be more unified and simple. - struct sockaddr_un sock_st; - comdet[idx].sock_len = sizeof(struct sockaddr_un); - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - ERROR_WINDOW("Cannot create named socket %s, UART monitor cannot be used: %s", fn, strerror(errno)); - return 1; - } - sock_st.sun_family = AF_UNIX; - strcpy(sock_st.sun_path, fn); - unlink(sock_st.sun_path); - if (bind(sock, (struct sockaddr*)&sock_st, comdet[idx].sock_len)) { - ERROR_WINDOW("Cannot bind named socket %s, UART monitor cannot be used: %s", fn, strerror(errno)); - xemusock_close(sock, NULL); - return 1; - } -#endif } - if (xemusock_listen(sock, 5, &xerr)) { - ERROR_WINDOW("Cannot listen socket %s, UART monitor cannot be used: %s", fn, xemusock_strerror(xerr)); + struct sockaddr_in sock_st; + sock_len = sizeof(struct sockaddr_in); + //sock = socket(AF_INET, SOCK_STREAM, 0); + sock = xemusock_create_for_inet(XEMUSOCK_TCP, XEMUSOCK_BLOCKING, &xerr); + if (sock == XS_INVALID_SOCKET) { + ERROR_WINDOW("Cannot create TCP socket: %s", xemusock_strerror(xerr)); + return 1; + } + if (xemusock_setsockopt_reuseaddr(sock, &xerr)) { + ERROR_WINDOW("UARTMON: setsockopt for SO_REUSEADDR failed with %s", xemusock_strerror(xerr)); + } + //sock_st.sin_family = AF_INET; + //sock_st.sin_addr.s_addr = htonl(INADDR_ANY); + //sock_st.sin_port = htons(port); + xemusock_fill_servaddr_for_inet_ip_native(&sock_st, 0, port); + if (xemusock_bind(sock, (struct sockaddr*)&sock_st, sock_len, &xerr)) { + ERROR_WINDOW("Cannot bind TCP socket %d, UART monitor cannot be used: %s", port, xemusock_strerror(xerr)); xemusock_close(sock, NULL); return 1; } - if (xemusock_set_nonblocking(sock, XEMUSOCK_NONBLOCKING, &xerr)) { - ERROR_WINDOW("Cannot set socket %s into non-blocking mode, UART monitor cannot be used: %s", fn, xemusock_strerror(xerr)); + } else { +#if !defined(XEMU_ARCH_UNIX) + ERROR_WINDOW("On non-UNIX systems, you must use TCP/IP sockets, so uartmon parameter must be in form of :n (n=port number to bind to)\nUARTMON is not available because of bad syntax."); + return 1; +#else + // This is UNIX specific code (UNIX named socket) thus it's OK not use Xemu socket API calls here. + // Note: on longer term, we want to drop this, and allow only TCP sockets to be more unified and simple. + struct sockaddr_un sock_st; + sock_len = sizeof(struct sockaddr_un); + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + ERROR_WINDOW("Cannot create named socket %s, UART monitor cannot be used: %s", fn, strerror(errno)); + return 1; + } + sock_st.sun_family = AF_UNIX; + strcpy(sock_st.sun_path, fn); + unlink(sock_st.sun_path); + if (bind(sock, (struct sockaddr*)&sock_st, sock_len)) { + ERROR_WINDOW("Cannot bind named socket %s, UART monitor cannot be used: %s", fn, strerror(errno)); xemusock_close(sock, NULL); return 1; } - DEBUG("UARTMON: monitor is listening on socket %s" NL, fn); - comdet[idx].sock_client = UNCONNECTED; // no client connection yet - comdet[idx].sock_server = sock; // now set the server socket visible outside of this function too - comdet[idx].umon_echo = 1; - comdet[idx].umon_send_ok = 1; - strcpy(fn_stored, fn); +#endif + } + if (xemusock_listen(sock, 5, &xerr)) { + ERROR_WINDOW("Cannot listen socket %s, UART monitor cannot be used: %s", fn, xemusock_strerror(xerr)); + xemusock_close(sock, NULL); + return 1; + } + if (xemusock_set_nonblocking(sock, XEMUSOCK_NONBLOCKING, &xerr)) { + ERROR_WINDOW("Cannot set socket %s into non-blocking mode, UART monitor cannot be used: %s", fn, xemusock_strerror(xerr)); + xemusock_close(sock, NULL); + return 1; } + DEBUG("UARTMON: monitor is listening on socket %s" NL, fn); + sock_client = UNCONNECTED; // no client connection yet + sock_server = sock; // now set the server socket visible outside of this function too + umon_echo = 1; + umon_send_ok = 1; + strcpy(fn_stored, fn); return 0; } void uartmon_close ( void ) { - for (int idx = 0; idx < MAXPORTS; idx++) - { - if (comdet[idx].sock_server != UNCONNECTED) { - xemusock_close(comdet[idx].sock_server, NULL); - comdet[idx].sock_server = UNCONNECTED; - } - if (comdet[idx].sock_client != UNCONNECTED) { - xemusock_close(comdet[idx].sock_client, NULL); - comdet[idx].sock_client = UNCONNECTED; - } + if (sock_server != UNCONNECTED) { + xemusock_close(sock_server, NULL); + sock_server = UNCONNECTED; + } + if (sock_client != UNCONNECTED) { + xemusock_close(sock_client, NULL); + sock_client = UNCONNECTED; } } -void uartmon_finish_command ( comms_details_type *cd ) + +void uartmon_finish_command ( void ) { - cd->umon_send_ok = 1; - if (cd->umon_write_buffer[cd->umon_write_size - 1] != '\n') { + umon_send_ok = 1; + if (umon_write_buffer[umon_write_size - 1] != '\n') { // if generated message wasn't closed with CRLF (well, only LF is checked), we do so here - cd->umon_write_buffer[cd->umon_write_size++] = '\r'; - cd->umon_write_buffer[cd->umon_write_size++] = '\n'; + umon_write_buffer[umon_write_size++] = '\r'; + umon_write_buffer[umon_write_size++] = '\n'; } // umon_trigger_end_of_answer = 1; - cd->umon_write_buffer[cd->umon_write_size++] = '.'; // add the 'dot prompt'! (m65dbg seems to check LF + dot for end of the answer) - cd->umon_write_buffer[cd->umon_write_size++] = '\r'; // I can't seem to see the dot over tcp unless I add a CRLF... - cd->umon_write_buffer[cd->umon_write_size++] = '\n'; - cd->umon_read_pos = 0; - cd->umon_echo = 1; + umon_write_buffer[umon_write_size++] = '.'; // add the 'dot prompt'! (m65dbg seems to check LF + dot for end of the answer) + umon_read_pos = 0; + umon_echo = 1; } -void uartmons_finish_command (void) -{ - for (int idx = 0; idx < MAXPORTS; idx++) - if (comdet[idx].sock_server != UNCONNECTED) - uartmon_finish_command(&comdet[idx]); -} - -void echo_command(comms_details_type* cd, char* command, int ret); -int connect_unix_socket(comms_details_type *cd) +// Non-blocky I/O for UART monitor emulation. +// Note: you need to call it "quite often" or it will be terrible slow ... +// From emulator main update, aka etc 25Hz rate should be Okey ... +void uartmon_update ( void ) { - int xerr; - //struct sockaddr_un sock_st; - xemusock_socklen_t len = cd->sock_len; - union { + int xerr, ret; + // If there is no server socket, we can't do anything! + if (sock_server == UNCONNECTED) + return; + // Try to accept new connection, if not yet have one (we handle only *ONE* connection!!!!) + if (sock_client == UNCONNECTED) { + //struct sockaddr_un sock_st; + xemusock_socklen_t len = sock_len; + union { #ifdef XEMU_ARCH_UNIX - struct sockaddr_un un; + struct sockaddr_un un; #endif - struct sockaddr_in in; - } sock_st; - xemusock_socket_t ret_sock = xemusock_accept(cd->sock_server, (struct sockaddr *)&sock_st, &len, &xerr); - if (ret_sock != XS_INVALID_SOCKET || (ret_sock == XS_INVALID_SOCKET && !xemusock_should_repeat_from_error(xerr))) - DEBUG("UARTMON: accept()=" PRINTF_SOCK " error=%s" NL, - ret_sock, - ret_sock != XS_INVALID_SOCKET ? "OK" : xemusock_strerror(xerr) - ); - if (ret_sock != XS_INVALID_SOCKET) { - if (xemusock_set_nonblocking(ret_sock, 1, &xerr)) { - DEBUGPRINT("UARTMON: error, cannot make socket non-blocking %s", xemusock_strerror(xerr)); - xemusock_close(ret_sock, NULL); - return 0; - } else { - cd->sock_client = ret_sock; // "publish" new client socket - // Reset reading/writing information - cd->umon_write_size = 0; - cd->umon_read_pos = 0; - DEBUGPRINT("UARTMON: new connection established on socket " PRINTF_SOCK NL, cd->sock_client); - return 1; - } - } - return 0; -} - -void umon_printf(const char* format, ...) -{ - va_list args; - va_start(args, format); - for (int idx = 0; idx < MAXPORTS; idx++) - if (comdet[idx].sock_server != UNCONNECTED) - comdet[idx].umon_write_size += vsprintf(comdet[idx].umon_write_buffer + comdet[idx].umon_write_size, format, args); - - va_end(args); -} - -void write_hypervisor_byte(char byte) -{ - for (int idx = 0; idx < MAXPORTS; idx++) - if (comdet[idx].sock_server != UNCONNECTED) - { - //DEBUGPRINT("UARTMON: write_hypervisor(%d) - umon_send_ok=%d\n", byte, umon_send_ok); - if (!comdet[idx].umon_send_ok) - continue; - - int xerr; - - xemusock_send(comdet[idx].sock_client, &byte, 1, &xerr); + struct sockaddr_in in; + } sock_st; + xemusock_socket_t ret_sock = xemusock_accept(sock_server, (struct sockaddr *)&sock_st, &len, &xerr); + if (ret_sock != XS_INVALID_SOCKET || (ret_sock == XS_INVALID_SOCKET && !xemusock_should_repeat_from_error(xerr))) + DEBUG("UARTMON: accept()=" PRINTF_SOCK " error=%s" NL, + ret_sock, + ret_sock != XS_INVALID_SOCKET ? "OK" : xemusock_strerror(xerr) + ); + if (ret_sock != XS_INVALID_SOCKET) { + if (xemusock_set_nonblocking(ret_sock, 1, &xerr)) { + DEBUGPRINT("UARTMON: error, cannot make socket non-blocking %s", xemusock_strerror(xerr)); + xemusock_close(ret_sock, NULL); + return; + } else { + sock_client = ret_sock; // "publish" new client socket + // Reset reading/writing information + umon_write_size = 0; + umon_read_pos = 0; + DEBUGPRINT("UARTMON: new connection established on socket " PRINTF_SOCK NL, sock_client); + } } -} - -int write_to_socket(comms_details_type *cd) -{ - int xerr, ret; - - if (!cd->umon_send_ok) - return 0; - ret = xemusock_send(cd->sock_client, cd->umon_write_buffer + cd->umon_write_pos, cd->umon_write_size, &xerr); - if (ret != XS_SOCKET_ERROR || (ret == XS_SOCKET_ERROR && !xemusock_should_repeat_from_error(xerr))) { - DEBUG("UARTMON: write(" PRINTF_SOCK ",buffer+%d,%d)=%d (%s)" NL, - cd->sock_client, cd->umon_write_pos, cd->umon_write_size, - ret, ret == XS_SOCKET_ERROR ? xemusock_strerror(xerr) : "OK" - ); - } - if (ret == 0 || xerr == XSECONNRESET || xerr == XSECONNABORTED) { // client socket closed - xemusock_close(cd->sock_client, NULL); - cd->sock_client = UNCONNECTED; - DEBUGPRINT("UARTMON: connection closed by peer while writing" NL); - return 0; } - if (ret > 0) { - //debug_buffer_slice(umon_write_buffer + umon_write_pos, ret); - cd->umon_write_pos += ret; - cd->umon_write_size -= ret; - if (cd->umon_write_size < 0) - FATAL("FATAL: negative umon_write_size!"); - } - if (cd->umon_write_size) - return 0; // if we still have bytes to write, return and leave the work for the next update - - return 1; -} - -int is_received_string_fully_parsed(comms_details_type* cd, int ret) -{ - return cd->locptr == &cd->umon_read_buffer[cd->umon_read_pos+ret]; -} - -int get_unparsed_bytes_remaining_count(comms_details_type* cd, int ret) -{ - return (int)(&cd->umon_read_buffer[cd->umon_read_pos+ret] - cd->locptr); -} - -// had to create my own strtok() equivalent that would tokenise on *either* '\r' or '\n' -char * find_next_cmd(comms_details_type* cd, char *loc) -{ - - if (loc != NULL) - cd->locptr = loc; - - loc = cd->locptr; - - char *p = loc; - // assure that looking forward into this string, we locate a '\r' or '\n' - while (*p != 0) - { - if (*p == '\r' || *p == '\n') - { - cd->locptr = p+1; - return loc; + // If no established connection, return + if (sock_client == UNCONNECTED) + return; + // If there is data to write, try to write + if (umon_write_size) { + if (!umon_send_ok) + return; + ret = xemusock_send(sock_client, umon_write_buffer + umon_write_pos, umon_write_size, &xerr); + if (ret != XS_SOCKET_ERROR || (ret == XS_SOCKET_ERROR && !xemusock_should_repeat_from_error(xerr))) + DEBUG("UARTMON: write(" PRINTF_SOCK ",buffer+%d,%d)=%d (%s)" NL, + sock_client, umon_write_pos, umon_write_size, + ret, ret == XS_SOCKET_ERROR ? xemusock_strerror(xerr) : "OK" + ); + if (ret == 0) { // client socket closed + xemusock_close(sock_client, NULL); + sock_client = UNCONNECTED; + DEBUGPRINT("UARTMON: connection closed by peer while writing" NL); + return; } - p++; - } - - return 0; -} - -int read_loadcmd_data(comms_details_type *cd, char* buff, int count) -{ - char *p = buff; - - while (count != 0) - { - m65mon_setmem28(cd->loadcmdcurraddr, 1, (Uint8*)p); - cd->loadcmdcurraddr++; - p++; - count--; - if (cd->loadcmdcurraddr == cd->loadcmdendaddr) - { - cd->loadcmdflag = 0; - uartmon_finish_command(cd); - break; + if (ret > 0) { + //debug_buffer_slice(umon_write_buffer + umon_write_pos, ret); + umon_write_pos += ret; + umon_write_size -= ret; + if (umon_write_size < 0) + FATAL("FATAL: negative umon_write_size!"); } + if (umon_write_size) + return; // if we still have bytes to write, return and leave the work for the next update } - - return count; -} - -// return: 1=we loaded stuff into memory -// 0=we didn't -int check_loadcmd(comms_details_type* cd, char* buff, int ret) -{ - if (cd->loadcmdflag) - { - int remaining = read_loadcmd_data(cd, buff, ret); - // TODO: I should probably see if there is a command immediately after this, but for now, I'll assume there isn't. - // NOTE2: Yes, this is critical, I need to fix this for tools like mega65_ftp, as the pump out commands very fast - // (especially as xemu comms is so fast compared to real hardware) - return 1; - } - - return 0; -} - -void read_from_socket(comms_details_type *cd) -{ - int xerr, ret; - ret = xemusock_recv(cd->sock_client, cd->umon_read_buffer + cd->umon_read_pos, sizeof(cd->umon_read_buffer) - cd->umon_read_pos - 1, &xerr); + umon_write_pos = 0; + // Try to read data + ret = xemusock_recv(sock_client, umon_read_buffer + umon_read_pos, sizeof(umon_read_buffer) - umon_read_pos - 1, &xerr); if (ret != XS_SOCKET_ERROR || (ret == XS_SOCKET_ERROR && !xemusock_should_repeat_from_error(xerr))) DEBUG("UARTMON: read(" PRINTF_SOCK ",buffer+%d,%d)=%d (%s)" NL, - cd->sock_client, cd->umon_read_pos, (int)sizeof(cd->umon_read_buffer) - cd->umon_read_pos - 1, - ret, ret == XS_SOCKET_ERROR ? xemusock_strerror(xerr) : "OK" + sock_client, umon_read_pos, (int)sizeof(umon_read_buffer) - umon_read_pos - 1, + ret, ret == XS_SOCKET_ERROR ? xemusock_strerror(xerr) : "OK" ); - if (ret == 0 || xerr == XSECONNRESET || xerr == XSECONNABORTED) { // client socket closed - xemusock_close(cd->sock_client, NULL); - cd->sock_client = UNCONNECTED; + if (ret == 0) { // client socket closed + xemusock_close(sock_client, NULL); + sock_client = UNCONNECTED; DEBUGPRINT("UARTMON: connection closed by peer while reading" NL); return; } if (ret > 0) { - - // assure a null terminator at end of data - cd->umon_read_buffer[cd->umon_read_pos+ret] = 0; - - - if (check_loadcmd(cd, &cd->umon_read_buffer[cd->umon_read_pos], ret)) - return; - - char *p; - if (cd->umon_read_pos == 0) - p = find_next_cmd(cd, cd->umon_read_buffer); - else - p = find_next_cmd(cd, NULL); - - while (p) - { - DEBUG("UARTMON: find_next_cmd p = %08X\n", (unsigned int)p); - cd->umon_echo = 1; - echo_command(cd, p, ret); - - //debug_buffer(umon_read_buffer); - if (!cd->umon_echo || sizeof(cd->umon_read_buffer) - cd->umon_read_pos - 1 == 0) { - cd->umon_read_buffer[sizeof(cd->umon_read_buffer) - 1] = 0; // just in case of a "mega long command" with filled rx buffer ... - cd->umon_write_buffer[cd->umon_write_size++] = '\r'; // mega65_ftp seemed to prefer only '\n' (it choked on '\r') - cd->umon_write_buffer[cd->umon_write_size++] = '\n'; - cd->umon_send_ok = 1; // by default, command is finished after the execute_command() - execute_command(cd, p); // Execute our command! - // command may delay (like with trace) the finish of the command with - // setting umon_send_ok to zero. In this case, some need to call - // uartmon_finish_command() some time otherwise the monitor connection - // will just hang! - } - - if (check_loadcmd(cd, cd->locptr, get_unparsed_bytes_remaining_count(cd, ret))) - { - cd->umon_read_pos = 0; - cd->umon_read_buffer[cd->umon_read_pos] = 0; - return; - } - p = find_next_cmd(cd, NULL); // prepare to read next command on next iteration (if there is one) - } - - // only finish command if we geniunely found a carriage return as the last character - if (is_received_string_fully_parsed(cd, ret)) - uartmon_finish_command(cd); - else - { - cd->umon_read_pos += ret; - cd->umon_read_buffer[cd->umon_read_pos] = 0; - } - } -} - - -// Non-blocky I/O for UART monitor emulation. -// Note: you need to call it "quite often" or it will be terrible slow ... -// From emulator main update, aka etc 25Hz rate should be Okey ... -void uartmon_update ( void ) -{ - for (int idx = 0; idx < MAXPORTS; idx++) - { - comms_details_type* cd = &comdet[idx]; - // If there is no server socket, we can't do anything! - if (cd->sock_server == UNCONNECTED) - continue; - - // Try to accept new connection, if not yet have one (we handle only *ONE* connection!!!!) - if (cd->sock_client == UNCONNECTED) { - if (!connect_unix_socket(cd)) - continue; - } - - // If no established connection, return - if (cd->sock_client == UNCONNECTED) - continue; - - // If there is data to write, try to write - if (cd->umon_write_size) { - if (!write_to_socket(cd)) - continue; + /* ECHO: provide echo for the client */ + if (umon_echo) { + char*p = umon_read_buffer + umon_read_pos; + int n = ret; + while (n--) + if (*p != 13 && *p != 10) { + umon_write_buffer[umon_write_size++] = *(p++); + } else { + umon_echo = 0; // setting to zero avoids more input to echo, and also signs a complete command + *p = 0; // terminate string in read buffer + break; + } } - - cd->umon_write_pos = 0; - - // Try to read data - read_from_socket(cd); - } -} - -void echo_command(comms_details_type* cd, char* command, int ret) -{ - /* ECHO: provide echo for the client */ - if (cd->umon_echo) { - char*p = command; - while (*p != 0 && *p != '\r' && *p != '\n') { - cd->umon_write_buffer[cd->umon_write_size++] = *(p++); + /* ECHO: end */ + umon_read_pos += ret; + umon_read_buffer[umon_read_pos] = 0; + //debug_buffer(umon_read_buffer); + if (!umon_echo || sizeof(umon_read_buffer) - umon_read_pos - 1 == 0) { + umon_read_buffer[sizeof(umon_read_buffer) - 1] = 0; // just in case of a "mega long command" with filled rx buffer ... + umon_write_buffer[umon_write_size++] = '\r'; + umon_write_buffer[umon_write_size++] = '\n'; + umon_send_ok = 1; // by default, command is finished after the execute_command() + execute_command(umon_read_buffer); // Execute our command! + // command may delay (like with trace) the finish of the command with + // setting umon_send_ok to zero. In this case, some need to call + // uartmon_finish_command() some time otherwise the monitor connection + // will just hang! + if (umon_send_ok) + uartmon_finish_command(); } - cd->umon_echo = 0; // setting to zero avoids more input to echo, and also signs a complete command - *p = 0; // terminate string in read buffer } } -void set_umon_send_ok(int val) -{ - for (int idx = 0; idx < MAXPORTS; idx++) - if (comdet[idx].sock_server != UNCONNECTED) - comdet[idx].umon_send_ok = val; -} #endif diff --git a/targets/mega65/uart_monitor.h b/targets/mega65/uart_monitor.h index 31489034..010a5a74 100644 --- a/targets/mega65/uart_monitor.h +++ b/targets/mega65/uart_monitor.h @@ -25,18 +25,18 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define UMON_DEFAULT_PORT ":4510" -// seems as though m65.c actions like fetch_ram(0xFFF8000, 0x4000, hyppo_data) -// cause a lot of umon_writes to accumulate quickly, so had to increase this buffer -#define UMON_WRITE_BUFFER_SIZE 0x10000 +#define UMON_WRITE_BUFFER_SIZE 0x4000 +#define umon_printf(...) umon_write_size += sprintf(umon_write_buffer + umon_write_size, __VA_ARGS__) -extern void umon_printf(const char* format, ...); +extern int umon_write_size; +extern int umon_send_ok; +extern char umon_write_buffer[UMON_WRITE_BUFFER_SIZE]; extern int uartmon_init ( const char *fn ); extern int uartmon_is_active ( void ); extern void uartmon_update ( void ); extern void uartmon_close ( void ); -extern void uartmons_finish_command ( void ); -extern void set_umon_send_ok(int val); +extern void uartmon_finish_command ( void ); #endif diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index 43ee26ea..2aa2a17e 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -38,19 +38,18 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // The user can select a clipped borders view (called "normal borders") which shows // the real visible resolution of PAL (720x576) or NSTC(720x480). -#define SCREEN_WIDTH 800 -#define SCREEN_HEIGHT 600 +#define TEXTURE_WIDTH 800 +#define TEXTURE_HEIGHT 625 + #define PHYSICAL_RASTERS_DEFAULT PHYSICAL_RASTERS_NTSC #define SCREEN_HEIGHT_VISIBLE_DEFAULT SCREEN_HEIGHT_VISIBLE_NTSC #define SCREEN_HEIGHT_VISIBLE_NTSC 480 #define SCREEN_HEIGHT_VISIBLE_PAL 576 #define PHYSICAL_RASTERS_NTSC 526 #define PHYSICAL_RASTERS_PAL 624 -#define NTSC_RATIO (PHYSICAL_RASTERS_NTSC / float(SCREEN_WIDTH)) -#define PAL_RATIO (PHYSICAL_RASTERS_PAL / float(SCREEN_WIDTH)) #define FRAME_H_FRONT 0 #define RASTER_CORRECTION 3 -#define VIC4_BLINK_INTERVAL 25 +#define VIC4_BLINK_INTERVAL 30 // Register defines // @@ -63,11 +62,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_MCM (vic_registers[0x16] & 0x10) #define REG_BMM (vic_registers[0x11] & 0x20) #define REG_SPRITE_ENABLE vic_registers[0x15] -#define REG_BORDER_COLOR vic_registers[0x20] -#define REG_SCREEN_COLOR vic_registers[0x21] -#define REG_MULTICOLOR_1 vic_registers[0x22] -#define REG_MULTICOLOR_2 vic_registers[0x23] -#define REG_MULTICOLOR_3 vic_registers[0x24] +#define REG_BORDER_COLOR (vic_registers[0x20] & vic_color_register_mask) +#define REG_SCREEN_COLOR (vic_registers[0x21] & vic_color_register_mask) +#define REG_MULTICOLOR_1 (vic_registers[0x22] & vic_color_register_mask) +#define REG_MULTICOLOR_2 (vic_registers[0x23] & vic_color_register_mask) +//#define REG_MULTICOLOR_3 (vic_registers[0x24] & vic_color_register_mask) #define REG_H640 (vic_registers[0x31] & 128) #define REG_V400 (vic_registers[0x31] & 8) #define REG_VICIII_ATTRIBS (vic_registers[0x31] & 0x20) @@ -76,7 +75,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_DISPLAYENABLE (vic_registers[0x11] & 0x10) #define REG_VIC2_XSCROLL (vic_registers[0x16] & 7) #define REG_VIC2_YSCROLL (vic_registers[0x11] & 7) -#define REG_CRAM2K (vic_registers[0x30] & 1) +//#define REG_CRAM2K (vic_registers[0x30] & 1) #define REG_TBRDPOS (vic_registers[0x48]) #define REG_SPRBPMEN_0_3 (vic_registers[0x49] >> 4) #define REG_SPRBPMEN_4_7 (vic_registers[0x4B] >> 4) @@ -85,23 +84,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_BBRDPOS_U4 (vic_registers[0x4B] & 0xF) #define REG_TEXTXPOS (vic_registers[0x4C]) #define REG_TEXTXPOS_U4 (vic_registers[0x4D] & 0xF) +#define REG_SPRTILEN ((vic_registers[0x4D] >> 4) | (vic_registers[0x4F] & 0xF0)) #define REG_TEXTYPOS (vic_registers[0x4E]) #define REG_TEXTYPOS_U4 (vic_registers[0x4F] & 0xF) -#define REG_XPOS (vic_registers[0x51]) -#define REG_XPOS_U6 (vic_registers[0x50] & 0x3F) +//#define REG_XPOS (vic_registers[0x51]) +//#define REG_XPOS_U6 (vic_registers[0x50] & 0x3F) #define REG_FNRST (vic_registers[0x53] & 0x80) #define REG_16BITCHARSET (vic_registers[0x54] & 1) #define REG_FCLRLO (vic_registers[0x54] & 2) #define REG_FCLRHI (vic_registers[0x54] & 4) #define REG_SPR640 (vic_registers[0x54] & 0x10) #define REG_SPRHGHT (vic_registers[0x56]) -#define REG_CHRXSCL (vic_registers[0x5A]) +//#define REG_CHRXSCL (vic_registers[0x5A]) #define REG_CHRYSCL (vic_registers[0x5B]) #define REG_SIDBDRWD (vic_registers[0x5C]) #define REG_SIDBDRWD_U5 (vic_registers[0x5D] & 0x3F) #define REG_HOTREG (vic_registers[0x5D] & 0x80) -#define REG_CHARSTEP vic_registers[0x58] -#define REG_CHARSTEP_U8 vic_registers[0x59] +#define REG_LINESTEP vic_registers[0x58] +#define REG_LINESTEP_U8 vic_registers[0x59] #define REG_CHARXSCALE vic_registers[0x5A] #define REG_CHRCOUNT vic_registers[0x5E] #define REG_SCRNPTR_B0 vic_registers[0x60] @@ -116,10 +116,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_SPRPTR_B0 vic_registers[0x6C] #define REG_SPRPTR_B1 vic_registers[0x6D] #define REG_SPRPTR_B2 (vic_registers[0x6E] & 0x7F) -#define REG_SCREEN_ROWS vic_registers[0x7B] -#define REG_PAL_RED_BASE (vic_registers[0x100]) -#define REG_PAL_GREEN_BASE (vic_registers[0x200]) -#define REG_PAL_BLUE_BASE (vic_registers[0x300]) +//#define REG_SCREEN_ROWS vic_registers[0x7B] +//#define REG_PAL_RED_BASE (vic_registers[0x100]) +//#define REG_PAL_GREEN_BASE (vic_registers[0x200]) +//#define REG_PAL_BLUE_BASE (vic_registers[0x300]) // Helper macros for accessing multi-byte registers // and other similar functionality for convenience @@ -130,20 +130,21 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define BORDER_Y_BOTTOM (((Uint16)REG_BBRDPOS) | (REG_BBRDPOS_U4) << 8) #define CHARGEN_Y_START (((Uint16)REG_TEXTYPOS) | (REG_TEXTYPOS_U4) << 8) #define CHARGEN_X_START (((Uint16)REG_TEXTXPOS) | (REG_TEXTXPOS_U4) << 8) -#define CHARSTEP_BYTES (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) -#define SCREEN_RAM_ADDR_VIC (REG_SCREEN_ADDR * 1024) +#define LINESTEP_BYTES (((Uint16)REG_LINESTEP) | (REG_LINESTEP_U8) << 8) +//#define SCREEN_RAM_ADDR_VIC (REG_SCREEN_ADDR * 1024) #define SCREEN_ADDR ((Uint32)REG_SCRNPTR_B0 | (REG_SCRNPTR_B1<<8) | (REG_SCRNPTR_B2 <<16) | (REG_SCRNPTR_B3 << 24)) #define CHARSET_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) -#define VIC2_BITMAP_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) +#define VIC2_BITMAP_ADDR ((CHARSET_ADDR) & 0xFFE000) #define SPRITE_POINTER_ADDR ((Uint32)REG_SPRPTR_B0 | (REG_SPRPTR_B1<<8) | (REG_SPRPTR_B2 <<16)) #define COLOUR_RAM_OFFSET ((((Uint16)REG_COLPTR) | (REG_COLPTR_MSB) << 8)) //#define IS_NTSC_MODE (videostd_id) -#define SCREEN_STEP (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) +//#define SCREEN_STEP (((Uint16)REG_LINESTEP) | (REG_LINESTEP_U8) << 8) #define SPRITE_POS_Y(n) (vic_registers[1 + (n)*2]) #define SPRITE_POS_X(n) (((Uint16)vic_registers[(n)*2]) | ( (vic_registers[0x10] & (1 << (n)) ? 0x100 : 0))) -#define SPRITE_COLOR(n) (vic_registers[0x27+(n)] & 0xF) -#define SPRITE_MULTICOLOR_1 (vic_registers[0x25] & 0xF) -#define SPRITE_MULTICOLOR_2 (vic_registers[0x26] & 0xF) +#define SPRITE_COLOR(n) (vic_registers[0x27+(n)] & vic_color_register_mask) +#define SPRITE_COLOR_4BIT(n) (vic_registers[0x27+(n)] & 0xF) +#define SPRITE_MULTICOLOR_1 (vic_registers[0x25] & vic_color_register_mask) +#define SPRITE_MULTICOLOR_2 (vic_registers[0x26] & vic_color_register_mask) #define SPRITE_IS_BACK(n) (vic_registers[0x1B] & (1 << (n))) #define SPRITE_HORZ_2X(n) (vic_registers[0x1D] & (1 << (n))) #define SPRITE_VERT_2X(n) (vic_registers[0x17] & (1 << (n))) @@ -153,9 +154,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SPRITE_EXTHEIGHT(n) (vic_registers[0x55] & (1 << (n))) #define SPRITE_BITPLANE_ENABLE(n) (((REG_SPRBPMEN_4_7) << 4 | REG_SPRBPMEN_0_3) & (1 << (n))) #define SPRITE_16BITPOINTER (vic_registers[0x6E] & 0x80) -#define TEXT_MODE (!REG_BMM) -#define HIRES_BITMAP_MODE (REG_BMM & !REG_MCM & !REG_EBM) -#define MULTICOLOR_BITMAP_MODE (REG_BMM & REG_MCM & !REG_EBM) +//#define TEXT_MODE (!REG_BMM) +//#define HIRES_BITMAP_MODE (REG_BMM & !REG_MCM & !REG_EBM) +//#define MULTICOLOR_BITMAP_MODE (REG_BMM & REG_MCM & !REG_EBM) #define VIC3_ATTR_BLINK(c) ((c) & 0x1) #define VIC3_ATTR_REVERSE(c) ((c) & 0x2) #define VIC3_ATTR_BOLD(c) ((c) & 0x4) @@ -168,11 +169,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SXA_TRIM_RIGHT_BITS012(sw) ((sw) >> 13) #define SXA_VERTICAL_FLIP(cw) ((cw) & 0x8000) #define SXA_HORIZONTAL_FLIP(cw) ((cw) & 0x4000) -#define SXA_ALPHA_BLEND(cw) ((cw) & 0x2000) +//#define SXA_ALPHA_BLEND(cw) ((cw) & 0x2000) #define SXA_GOTO_X(cw) ((cw) & 0x1000) #define SXA_4BIT_PER_PIXEL(cw) ((cw) & 0x0800) #define SXA_TRIM_RIGHT_BIT3(cw) ((cw) & 0x0400) -//FIXME: this last one was bad, and also, seems to be not used? +#define SXA_ATTR_BOLD(cw) ((cw) & 0x0040) +#define SXA_ATTR_REVERSE(cw) ((cw) & 0x0020) //#define SXA_TRIM_TOP_BOTTOM(cw) (((cw) & 0x0300) >> 8) @@ -190,10 +192,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; \ } while(0) -#define SET_14BIT_REGI(basereg,x) do { \ +/* #define SET_14BIT_REGI(basereg,x) do { \ vic_registers[(basereg)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ vic_registers[((basereg)+1)] = (Uint8) ((Uint16)(x)) & 0x00FF; \ - } while(0) + } while(0) */ #define SET_16BIT_REG(basereg,x) do { \ vic_registers[((basereg) + 1)] = (Uint8) ((((Uint16)(x)) & 0xFF00) >> 8); \ vic_registers[(basereg)]= ((Uint16)(x)) & 0x00FF; \ @@ -202,7 +204,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // 11-bit registers #define SET_PHYSICAL_RASTER(x) SET_11BIT_REG(0x52, (x)) -#define SET_RASTER_XPOS(x) SET_14BIT_REGI(0x50, (x)) +//#define SET_RASTER_XPOS(x) SET_14BIT_REGI(0x50, (x)) // 12-bit registers @@ -214,42 +216,40 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //16-bit registers #define SET_COLORRAM_BASE(x) SET_16BIT_REG(0x64,(x)) -#define SET_CHARSTEP_BYTES(x) SET_16BIT_REG(0x58,(x)) - -// Pixel foreground/background indicator for aiding in sprite rendering - -#define FOREGROUND_PIXEL 1 -#define BACKGROUND_PIXEL 0 +#define SET_LINESTEP_BYTES(x) SET_16BIT_REG(0x58,(x)) // Review this! (VIC-II values) #define SPRITE_X_BASE_COORD 24 #define SPRITE_Y_BASE_COORD 50 -#define SPRITE_X_UPPER_COORD 250 -#define SPRITE_Y_UPPER_COORD 344 +//#define SPRITE_X_UPPER_COORD 250 +//#define SPRITE_Y_UPPER_COORD 344 // Current state extern int vic_iomode; -extern int scanline; +//extern int scanline; extern Uint8 vic_registers[]; -extern int cpu_cycles_per_scanline; -extern int vic2_16k_bank; -extern int force_fast; extern Uint8 c128_d030_reg; extern const char *videostd_name; extern int videostd_frametime; - -extern int user_scanlines_setting; +extern int videostd_changed; +extern Uint8 videostd_id; +extern float videostd_1mhz_cycles_per_scanline; +extern int vic_readjust_sdl_viewport; extern int vic_vidp_legacy, vic_chrp_legacy, vic_sprp_legacy; +extern const char *iomode_names[4]; + extern void vic_init ( void ); +extern void vic_reset ( void ); extern void vic_write_reg ( unsigned int addr, Uint8 data ); extern Uint8 vic_read_reg ( unsigned int addr ); extern int vic4_render_scanline ( void ); -extern void vic4_open_frame_access(); +extern void vic4_open_frame_access ( void ); +extern void vic4_close_frame_access (void ); #ifdef XEMU_SNAPSHOT_SUPPORT #include "xemu/emutools_snapshot.h" From c1f0f3deeee8a6e857711913a5dd073da863dbb1 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 18:16:38 +0200 Subject: [PATCH 04/35] EP128: update from merger --- targets/ep128/enterprise128.h | 3 +- targets/ep128/epnet.c | 8 +-- targets/ep128/epnet.h | 2 +- targets/ep128/nick.c | 121 ++++++++++++++++++---------------- targets/ep128/ui.c | 21 +++++- 5 files changed, 89 insertions(+), 66 deletions(-) diff --git a/targets/ep128/enterprise128.h b/targets/ep128/enterprise128.h index d0343f09..b6c423d9 100644 --- a/targets/ep128/enterprise128.h +++ b/targets/ep128/enterprise128.h @@ -22,8 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SCREEN_WIDTH 736 #define SCREEN_HEIGHT 288 #define SCREEN_FORMAT SDL_PIXELFORMAT_ARGB8888 -// !!! currently ep128 emu does NOT work if you modify this !!! -#define USE_LOCKED_TEXTURE 0 +#define USE_LOCKED_TEXTURE 1 #define RENDER_SCALE_QUALITY 0 #define DEFAULT_ROM_FN "#exos.rom" diff --git a/targets/ep128/epnet.c b/targets/ep128/epnet.c index 04f964df..8e97cf98 100644 --- a/targets/ep128/epnet.c +++ b/targets/ep128/epnet.c @@ -1,6 +1,6 @@ /* Minimalistic Enterprise-128 emulator with focus on "exotic" hardware Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2015-2016,2020 LGB (Gábor Lénárt) + Copyright (C)2015-2016,2020-2021 LGB (Gábor Lénárt) Partial Wiznet W5300 emulation, using the host OS (which runs the emulator) TCP/IP API. Thus, many of the W5300 features won't work, or limited, like: @@ -641,9 +641,9 @@ void epnet_init ( void (*cb)(int) ) { w5300_does_work = 0; patch_rom(); - char sockapi_error_msg[256]; - if (xemusock_init(sockapi_error_msg)) { - ERROR_WINDOW("Cannot intiailize socket API:\n%s", sockapi_error_msg); + const char *init_status = xemusock_init(); + if (init_status) { + ERROR_WINDOW("Cannot intiailize socket API:\n%s", init_status); w5300_does_work = 0; } else if (!start_net_thread()) { w5300_does_work = 1; diff --git a/targets/ep128/epnet.h b/targets/ep128/epnet.h index 434f4895..4dec9a92 100644 --- a/targets/ep128/epnet.h +++ b/targets/ep128/epnet.h @@ -1,6 +1,6 @@ /* Minimalistic Enterprise-128 emulator with focus on "exotic" hardware Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2015-2016,2020 LGB (Gábor Lénárt) + Copyright (C)2015-2016,2020-2021 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/targets/ep128/nick.c b/targets/ep128/nick.c index 89b4fd9a..e20bb821 100644 --- a/targets/ep128/nick.c +++ b/targets/ep128/nick.c @@ -62,23 +62,34 @@ Uint32 raster_time = 1; #define RASTER_FORCE_VSYNC 326 -static int nick_addressing_init ( Uint32 *pixels_buffer, int line_size ) +static void nick_open_frame_access ( void ) { - if (line_size < 736) { - ERROR_WINDOW("NICK: SDL: FATAL ERROR: target SDL surface has width (or pitch?) smaller than 736 pixels [%d]!", line_size); + int tail_sdl; + Uint32 *pixels_buffer = xemu_start_pixel_buffer_access(&tail_sdl); + pixels = pixels_init = pixels_buffer - RASTER_FIRST_VISIBLE * SCREEN_WIDTH; + if (tail_sdl) + FATAL("tail_sdl is not zero!"); + pixels_limit_up = pixels_buffer; + pixels_limit_bottom = pixels_init + RASTER_LAST_VISIBLE * SCREEN_WIDTH; + pixels_limit_vsync_shortest = pixels_init + RASTER_NO_VSYNC_BEFORE * SCREEN_WIDTH; + pixels_limit_vsync_long_force = pixels_init + RASTER_FORCE_VSYNC * SCREEN_WIDTH; + //pixels_gap = line_size - SCREEN_WIDTH; + pixels_gap = 0; +} + + +static int nick_addressing_init ( void ) +{ + if (SCREEN_WIDTH < 736) { + ERROR_WINDOW("NICK: SDL: FATAL ERROR: target SDL surface has width (or pitch?) smaller than 736 pixels [%d]!", SCREEN_WIDTH); return 1; } - if (line_size & 3) { + if (SCREEN_WIDTH & 3) { ERROR_WINDOW("NICK: SDL: FATAL ERROR: line size bytes not 4 bytes aligned!"); return 1; } DEBUG("NICK: first visible scanline = %d, last visible scanline = %d, line pitch pixels = %d" NL, RASTER_FIRST_VISIBLE, RASTER_LAST_VISIBLE, 0); - pixels = pixels_init = (pixels_buffer - RASTER_FIRST_VISIBLE * line_size); - pixels_limit_up = pixels_buffer; - pixels_limit_bottom = pixels_init + RASTER_LAST_VISIBLE * line_size; - pixels_limit_vsync_shortest = pixels_init + RASTER_NO_VSYNC_BEFORE * line_size; - pixels_limit_vsync_long_force = pixels_init + RASTER_FORCE_VSYNC * line_size; - pixels_gap = line_size - SCREEN_WIDTH; + nick_open_frame_access(); return 0; } @@ -103,9 +114,8 @@ void screenshot ( void ) int nick_init ( void ) { - Uint32 *buf = xemu_frame_pixel_access_p; // sdl_pixel_buffer pixels = NULL; // no previous state of buffer before the next function - if (nick_addressing_init(buf, SCREEN_WIDTH)) + if (nick_addressing_init()) return 1; for (int a = 0; a < 256; a++) { // RGB colours for the target screen @@ -460,45 +470,7 @@ static void _render_char_16 ( void ) { TODO(); } static void _render_char_256 ( void ) { TODO(); } - - - -static void (*_render)(void); -static void (*render_modes[])(void) = { -//static const void (*render_modes)(void)[] = { - _render_vsync, // col-2 vsync - _render_pixel_2, // col-2 pixel - _render_attrib_2, // col-2 attrib - _render_char_2, // col-2 ch256 - _render_char_2, // col-2 ch128 - _render_char_2, // col-2 ch64 - _render_invalid, // col-2 invalid - _render_lpixel_2, // col-2 lpixel - _render_vsync, // col-4 vsync - _render_pixel_4, // col-4 pixel - _render_attrib_2, // col-4 attrib TODO - _render_char_4, // col-4 ch256 - _render_char_4, // col-4 ch128 - _render_char_4, // col-4 ch64 - _render_invalid, // col-4 invalid - _render_lpixel_4, // col-4 lpixel - _render_vsync, // col-16 vsync - _render_pixel_16, // col-16 pixel - _render_attrib_2, // col-16 attrib TODO - _render_char_16, // col-16 ch256 - _render_char_16, // col-16 ch128 - _render_char_16, // col-16 ch64 - _render_invalid, // col-16 invalid - _render_lpixel_16, // col-16 lpixel - _render_vsync, // col-256 vsync - _render_pixel_256, // col-256 pixel - _render_attrib_2, // col-256 attrib TODO - _render_char_256, // col-256 ch256 - _render_char_256, // col-256 ch128 - _render_char_256, // col-256 ch64 - _render_invalid, // col-256 invalid - _render_lpixel_256 // col-256 lpixel -}; +static int _render_selection; //static const int chs_for_modes[] = { 0, 0, 0, 256, 128, 64, 0, 0 }; static const int chb_for_modes[] = { 0, 0, 0, 8, 7, 6, 0, 0 }; @@ -522,8 +494,8 @@ static inline void _update ( void ) pixels_limit_up[100*736+400+a]=omg++;*/ emu_one_frame(all_rasters, frameskip); all_rasters = 0; - pixels = pixels_init; frames++; + nick_open_frame_access(); } @@ -613,9 +585,9 @@ void nick_render_slot ( void ) if (pixels >= pixels_limit_vsync_long_force) _update(); visible = (pixels >= pixels_limit_up && pixels < pixels_limit_bottom && (!frameskip)); - if (XEMU_UNLIKELY((vm | ((a >> 2) & 0x18)) >= 8*4)) - FATAL("FATAL ERROR: NICK: render funcarray bound check failure!"); - _render = render_modes[vm | ((a >> 2) & 0x18)]; + _render_selection = vm | ((a >> 2) & 0x18); + if (XEMU_UNLIKELY(_render_selection >= 8 * 4)) + FATAL("NICK: render funcarray bound check failure!"); break; case 1: a = NICK_READ(lpt_a++); @@ -684,8 +656,43 @@ void nick_render_slot ( void ) _render_vsync(); else _render_border(); - } else - _render(); + } else { + switch (_render_selection) { + case 0: _render_vsync(); break; // col-2 vsync + case 1: _render_pixel_2(); break; // col-2 pixel + case 2: _render_attrib_2(); break; // col-2 attrib + case 3: _render_char_2(); break; // col-2 ch256 + case 4: _render_char_2(); break; // col-2 ch128 + case 5: _render_char_2(); break; // col-2 ch64 + case 6: _render_invalid(); break; // col-2 invalid + case 7: _render_lpixel_2(); break; // col-2 lpixel + case 8: _render_vsync(); break; // col-4 vsync + case 9: _render_pixel_4(); break; // col-4 pixel + case 10: _render_attrib_2(); break; // col-4 attrib TODO + case 11: _render_char_4(); break; // col-4 ch256 + case 12: _render_char_4(); break; // col-4 ch128 + case 13: _render_char_4(); break; // col-4 ch64 + case 14: _render_invalid(); break; // col-4 invalid + case 15: _render_lpixel_4(); break; // col-4 lpixel + case 16: _render_vsync(); break; // col-16 vsync + case 17: _render_pixel_16(); break; // col-16 pixel + case 18: _render_attrib_2(); break; // col-16 attrib TODO + case 19: _render_char_16(); break; // col-16 ch256 + case 20: _render_char_16(); break; // col-16 ch128 + case 21: _render_char_16(); break; // col-16 ch64 + case 22: _render_invalid(); break; // col-16 invalid + case 23: _render_lpixel_16(); break; // col-16 lpixel + case 24: _render_vsync(); break; // col-256 vsync + case 25: _render_pixel_256(); break; // col-256 pixel + case 26: _render_attrib_2(); break; // col-256 attrib TODO + case 27: _render_char_256(); break; // col-256 ch256 + case 28: _render_char_256(); break; // col-256 ch128 + case 29: _render_char_256(); break; // col-256 ch64 + case 30: _render_invalid(); break; // col-256 invalid + case 31: _render_lpixel_256(); break; // col-256 lpixel + default: XEMU_UNREACHABLE(); + } + } break; default: FATAL("NICK: FATAL ERROR: invalid slot number for rendering: %d", slot); diff --git a/targets/ep128/ui.c b/targets/ep128/ui.c index 4376224e..fe706ccf 100644 --- a/targets/ep128/ui.c +++ b/targets/ep128/ui.c @@ -31,6 +31,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "primoemu.h" #include "emu_monitor.h" #include "dave.h" +#include "configdb.h" static void ui_hard_reset ( void ) @@ -90,12 +91,28 @@ static void ui_cb_sound ( const struct menu_st *m, int *query ) } +static void ui_cb_render_scale_quality ( const struct menu_st *m, int *query ) +{ + XEMUGUI_RETURN_CHECKED_ON_QUERY(query, VOIDPTR_TO_INT(m->user_data) == configdb.sdlrenderquality); + char req_str[] = { VOIDPTR_TO_INT(m->user_data) + '0', 0 }; + SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY, req_str, SDL_HINT_OVERRIDE); + configdb.sdlrenderquality = VOIDPTR_TO_INT(m->user_data); + register_new_texture_creation = 1; +} /**** MENU SYSTEM ****/ - - +static const struct menu_st menu_render_scale_quality[] = { + { "Nearest pixel sampling", XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_render_scale_quality, (void*)0 }, + { "Linear filtering", XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_render_scale_quality, (void*)1 }, + { "Anisotropic (Direct3D only)",XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_render_scale_quality, (void*)2 }, + { NULL } +}; static const struct menu_st menu_display[] = { + { "Render scale quality", XEMUGUI_MENUID_SUBMENU, NULL, menu_render_scale_quality }, { "Fullscreen", XEMUGUI_MENUID_CALLABLE, xemugui_cb_windowsize, (void*)0 }, { "Window - 100%", XEMUGUI_MENUID_CALLABLE, xemugui_cb_windowsize, (void*)1 }, { "Window - 200%", XEMUGUI_MENUID_CALLABLE | From 9d9c9158d7529c5965d8d4a6e65963f2ebe0d6e5 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 19:18:55 +0200 Subject: [PATCH 05/35] CORE: update socketAPI from 'merger' branch --- xemu/emutools_socketapi.c | 46 ++++++++++++++++++++------------------- xemu/emutools_socketapi.h | 11 ++++------ 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/xemu/emutools_socketapi.c b/xemu/emutools_socketapi.c index eb8e5126..e0038071 100644 --- a/xemu/emutools_socketapi.c +++ b/xemu/emutools_socketapi.c @@ -1,5 +1,5 @@ /* Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2020 LGB (Gábor Lénárt) + Copyright (C)2016-2021 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -122,40 +122,40 @@ const char *xemusock_strerror ( int err ) #endif -static int _winsock_init_status = 1; // 1 = todo, 0 = was OK, -1 = error! +static int _winsock_init_status = 1; // 1 = todo, 0 = was OK, -1 = error! +static char _winsock_errmsg[512]; -int xemusock_init ( char *msg ) +const char *xemusock_init ( void ) { - //*msg = '\0'; // WTF it was?! - if (_winsock_init_status <= 0) - return _winsock_init_status; + if (_winsock_init_status == 0) + return NULL; + if (_winsock_init_status < 0) + return _winsock_errmsg; #ifdef XEMU_ARCH_WIN WSADATA wsa; if (WSAStartup(MAKEWORD(WINSOCK_VERSION_MAJOR, WINSOCK_VERSION_MINOR), &wsa)) { - if (msg) { - int err = SOCK_ERR(); - sprintf(msg, "WINSOCK: ERROR: Failed to initialize winsock2, [%d]: %s", err, xemusock_strerror(err)); - } + int err = SOCK_ERR(); + snprintf(_winsock_errmsg, sizeof _winsock_errmsg, "WINSOCK: ERROR: Failed to initialize winsock2, [%d]: %s", err, xemusock_strerror(err)); _winsock_init_status = -1; - return -1; + DEBUGPRINT("%s" NL, _winsock_errmsg); + return _winsock_errmsg; } if (LOBYTE(wsa.wVersion) != WINSOCK_VERSION_MAJOR || HIBYTE(wsa.wVersion) != WINSOCK_VERSION_MINOR) { WSACleanup(); - if (msg) - sprintf(msg, - "WINSOCK: ERROR: No suitable winsock API in the implemantion DLL (we need v%d.%d, we got: v%d.%d), windows system error ...", - WINSOCK_VERSION_MAJOR, WINSOCK_VERSION_MINOR, - HIBYTE(wsa.wVersion), LOBYTE(wsa.wVersion) - ); + snprintf(_winsock_errmsg, sizeof _winsock_errmsg, + "WINSOCK: ERROR: No suitable winsock API in the implemantion DLL (we need v%d.%d, we got: v%d.%d), windows system error ...", + WINSOCK_VERSION_MAJOR, WINSOCK_VERSION_MINOR, + HIBYTE(wsa.wVersion), LOBYTE(wsa.wVersion) + ); _winsock_init_status = -1; - return -1; + DEBUGPRINT("%s" NL, _winsock_errmsg); + return _winsock_errmsg; } - if (msg) - sprintf(msg, "WINSOCK: OK: initialized, version %d.%d", HIBYTE(wsa.wVersion), LOBYTE(wsa.wVersion)); + DEBUGPRINT("WINSOCK: OK: initialized, version %d.%d" NL, HIBYTE(wsa.wVersion), LOBYTE(wsa.wVersion)); #endif _winsock_init_status = 0; - return 0; + return NULL; } @@ -380,7 +380,7 @@ int xemusock_setsockopt_reuseaddr ( xemusock_socket_t sock, int *xerrno ) } -int xemusock_select_1 ( xemusock_socket_t sock, int usec, int what ) +int xemusock_select_1 ( xemusock_socket_t sock, int usec, int what, int *xerrno ) { for (;;) { int ret; @@ -402,6 +402,8 @@ int xemusock_select_1 ( xemusock_socket_t sock, int usec, int what ) int err = SOCK_ERR(); if (err == XSEINTR) continue; + if (xerrno) + *xerrno = SOCK_ERR(); return -1; } if (ret == 0) diff --git a/xemu/emutools_socketapi.h b/xemu/emutools_socketapi.h index 74449b73..1382ee3d 100644 --- a/xemu/emutools_socketapi.h +++ b/xemu/emutools_socketapi.h @@ -1,5 +1,5 @@ /* Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016,2019-2020 LGB (Gábor Lénárt) + Copyright (C)2016,2019-2021 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,8 +29,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ # define XSEINPROGRESS WSAEINPROGRESS # define XSEALREADY WSAEALREADY # define XSEINTR WSAEINTR -# define XSECONNRESET WSAECONNRESET -# define XSECONNABORTED WSAECONNABORTED # define XS_INVALID_SOCKET INVALID_SOCKET # define XS_SOCKET_ERROR SOCKET_ERROR # define SHUT_RDWR SD_BOTH @@ -47,8 +45,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ # define XSEINPROGRESS EINPROGRESS # define XSEALREADY EALREADY # define XSEINTR EINTR -# define XSECONNRESET ECONNRESET -# define XSECONNABORTED ECONNABORTED # define XS_INVALID_SOCKET -1 # define XS_SOCKET_ERROR -1 # define xemusock_strerror(_n) strerror(_n) @@ -64,9 +60,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define XEMUSOCK_NONBLOCKING 1 extern int xemusock_close ( xemusock_socket_t sock, int *xerrno ); -extern int xemusock_init ( char *msg ); +extern const char *xemusock_init( void ); extern void xemusock_uninit ( void ); -extern int xemusock_select_1 ( xemusock_socket_t sock, int usec, int what ); +extern int xemusock_select_1 ( xemusock_socket_t sock, int usec, int what, int *xerrno ); extern void xemusock_fill_servaddr_for_inet_ip_netlong ( struct sockaddr_in *servaddr, unsigned int ip_netlong, int port ); extern void xemusock_fill_servaddr_for_inet_ip_native ( struct sockaddr_in *servaddr, unsigned int ip_native, int port ); extern int xemusock_set_nonblocking ( xemusock_socket_t sock, int is_nonblock, int *xerrno ); @@ -75,6 +71,7 @@ extern int xemusock_send ( xemusock_socket_t sock, const void *buffer, int len extern int xemusock_sendto ( xemusock_socket_t sock, const void *buffer, int length, struct sockaddr_in *servaddr, int *xerrno ); extern int xemusock_recv ( xemusock_socket_t sock, void *buffer, int length, int *xerrno ); extern int xemusock_recvfrom ( xemusock_socket_t sock, void *buffer, int length, struct sockaddr_in *servaddr, int *xerrno ); +extern int xemusock_shutdown ( xemusock_socket_t sock, int *xerrno ); extern int xemusock_bind ( xemusock_socket_t sock, struct sockaddr *addr, xemusock_socklen_t addrlen, int *xerrno ); extern int xemusock_listen ( xemusock_socket_t sock, int backlog, int *xerrno ); extern int xemusock_setsockopt ( xemusock_socket_t sock, int level, int option, const void *value, int len, int *xerrno ); From 29a84d33f6115d1aeb2f22acd8f7c1d58d6adc4a Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 19:20:04 +0200 Subject: [PATCH 06/35] CORE: update future-umon from 'merger' branch --- xemu/emutools_umon.c | 378 +++++++++++++++++++++++++++++++++++++------ xemu/emutools_umon.h | 11 +- 2 files changed, 332 insertions(+), 57 deletions(-) diff --git a/xemu/emutools_umon.c b/xemu/emutools_umon.c index 203a4053..93927fe5 100644 --- a/xemu/emutools_umon.c +++ b/xemu/emutools_umon.c @@ -1,5 +1,5 @@ /* Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2017-2020 LGB (Gábor Lénárt) + Copyright (C)2017-2021 LGB (Gábor Lénárt) !! NOTE: I AM NOT a windows programmer, not even a user ... !! These are my best tries with winsock to be usable also on the win32/64 platform ... @@ -22,6 +22,27 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "xemu/emutools.h" #include "xemu/emutools_umon.h" +#include "xemu/emutools_socketapi.h" +#include +#include + + +//#define UNCONNECTED XS_INVALID_SOCKET +static xemusock_socket_t sock_server; + +static SDL_atomic_t thread_counter; +static SDL_atomic_t thread_stop_trigger; +static jmp_buf jmp_finish_client_thread; + +#define END_CLIENT_THREAD(n) do { longjmp(jmp_finish_client_thread, n); XEMU_UNREACHABLE(); } while(0) +#define CHECK_STOP_TRIGGER() do { \ + if (XEMU_UNLIKELY(SDL_AtomicGet(&thread_stop_trigger))) \ + END_CLIENT_THREAD(1); \ + } while(0) + +#if 0 + + #include #include @@ -516,6 +537,226 @@ static int xumon_main ( void *user_data_unused ) #undef FINISH_THREAD +#endif + + +static int recv_raw ( xemusock_socket_t sock, void *buffer, int min_size, int max_size ) +{ + int size = 0; + while (size < min_size && max_size > 0) { + CHECK_STOP_TRIGGER(); + int xerr; + int ret = xemusock_select_1(sock, 100000, XEMUSOCK_SELECT_R, &xerr); + if (ret < 0) { + if (xerr == XSEINTR) { + DEBUGPRINT("UMON: client: recv_raw: select() got EINTR, restarting" NL); + continue; + } + DEBUGPRINT("UMON: client: recv_raw: select() returned with error: %s" NL, xemusock_strerror(xerr)); + END_CLIENT_THREAD(1); + } + if (!ret) + continue; + ret = xemusock_recv(sock, buffer, max_size, &xerr); + if (ret == 0) { + // successfull receiving of zero bytes usually (?? FIXME ??) means socket has been closed + DEBUGPRINT("UMON: client: recv_raw: recv() returned with zero" NL); + END_CLIENT_THREAD(1); + } + if (ret < 0) { + if (xemusock_should_repeat_from_error(xerr)) { + DEBUGPRINT("UMON: client: recv_raw: recv() non-fatal error, restarting: %s" NL, xemusock_strerror(xerr)); + continue; + } + DEBUGPRINT("UMON: client: recv_raw: recv() returned with error: %s" NL, xemusock_strerror(xerr)); + END_CLIENT_THREAD(1); + } + buffer += ret; + size += ret; + max_size -= ret; + } + return size; +} + + +static void send_raw ( xemusock_socket_t sock, const void *buffer, int size ) +{ + while (size > 0) { + CHECK_STOP_TRIGGER(); + int xerr; + int ret = xemusock_select_1(sock, 100000, XEMUSOCK_SELECT_W, &xerr); + if (ret < 0) { + if (xerr == XSEINTR) { + DEBUGPRINT("UMON: client: send_raw: select() got EINTR, restarting" NL); + continue; + } + DEBUGPRINT("UMON: client: send_raw: select() returned with error: %s" NL, xemusock_strerror(xerr)); + END_CLIENT_THREAD(1); + } + if (!ret) + continue; + ret = xemusock_send(sock, buffer, size, &xerr); + if (ret == 0) { + // successfull sending of zero bytes usually (?? FIXME ??) means socket has been closed + DEBUGPRINT("UMON: client: send_raw: send() returned with zero" NL); + END_CLIENT_THREAD(1); + } + if (ret < 0) { + if (xemusock_should_repeat_from_error(xerr)) { + DEBUGPRINT("UMON: client: send_raw: send() non-fatal error, restarting: %s" NL, xemusock_strerror(xerr)); + continue; + } + DEBUGPRINT("UMON: client: send_raw: send() returned with error: %s" NL, xemusock_strerror(xerr)); + END_CLIENT_THREAD(1); + } + buffer += ret; + size -= ret; + } +} + + +static inline void send_string ( xemusock_socket_t sock, const char *p ) +{ + send_raw(sock, p, strlen(p)); +} + + +static void client_run ( xemusock_socket_t sock ) +{ + char buffer[8192]; + int read_size = 0; + int xerr; + for (;;) { + CHECK_STOP_TRIGGER(); + if (read_size >= sizeof(buffer) - 1) + break; + int ret = xemusock_recv(sock, buffer + read_size, sizeof(buffer) - read_size - 1, &xerr); + DEBUGPRINT("UMON: client: result of recv() = %d, error = %s" NL, ret, ret == -1 ? xemusock_strerror(xerr) : "OK"); + if (ret == 0) + break; + if (ret > 0) { + read_size += ret; + buffer[read_size] = 0; + const char *p = strstr(buffer, "\r\n\r\n"); + if (p) { + + char outbuffer[8192]; + sprintf(outbuffer, + "HTTP/1.1 200 OK\r\n" + "Host: 127.0.0.1\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n" + "Connection: close\r\n" + "Cache-Control: no-store, no-cache, must-revalidate, max-age=0\r\n" + "Cache-Control: post-check=0, pre-check=0\r\n" + "Pragma: no-cache\r\n" + "Expires: Tue, 19 Sep 2017 19:08:16 GMT\r\n" + "X-UA-Compatible: IE=edge\r\n" + "X-Powered-By: The Powerpuff Girls\r\n" + "X-Content-Type-Options: nosniff\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Server: Xemu/0.1\r\n" + "\r\n" + "Hello, world ;)\r\n" + ); + send_string(sock, outbuffer); + //p = outbuffer; + //while (*p) { + // ret = xemusock_send(sock, p, strlen(p), &xerr); + // DEBUGPRINT("UMON: client: result of send() = %d, error = %s" NL, ret, ret == -1 ? xemusock_strerror(xerr) : "OK"); + // if (ret == 0) + // break; + // if (ret > 0) + // p += ret; + // SDL_Delay(100); + //} + return; + } + } + SDL_Delay(100); + } +} + + +#undef END_CLIENT_THREAD +#undef CHECK_STOP_TRIGGER + + +// Client handling thread, for the life-time of a given connection only. +// It calls function client_run() to actually handle the connection. +// It's a very trivial function, just "extracts" the client socket from the thread parameter, +// and sets client socket to non-blocking mode, besides calling the mentioned real handler function. +// Also, this function sets a jump point for longjmp() thus, it's possible to return and finish thread +// without "walking backwards" in the call chain to be able to return from the thread handler itself. +static int client_thread_initiate ( void *user_param ) +{ + int num_of_threads = SDL_AtomicAdd(&thread_counter, 1) + 1; // increment thread counter (and remember) + xemusock_socket_t sock = (xemusock_socket_t)(uintptr_t)user_param; + DEBUGPRINT("UMON: client: new connection on socket %d" NL, (int)sock); + if (num_of_threads > XUMON_MAX_THREADS) { + DEBUGPRINT("UMON: client: too many threads (%d > %d), aborting connection." NL, num_of_threads, XUMON_MAX_THREADS); + } else { + int xerr; + if (xemusock_set_nonblocking(sock, XEMUSOCK_NONBLOCKING, &xerr)) { + DEBUGPRINT("UMON: client: Cannot set socket %d into non-blocking mode:\n%s" NL, (int)sock, xemusock_strerror(xerr)); + } else { + if (!setjmp(jmp_finish_client_thread)) + client_run(sock); + xemusock_set_nonblocking(sock, XEMUSOCK_BLOCKING, NULL); + } + } + xemusock_shutdown(sock, NULL); + xemusock_close(sock, NULL); + (void)SDL_AtomicAdd(&thread_counter, -1); // decrement thread counter + return 0; +} + + +// Main server thread, running during the full life-time of UMON subsystem. +// It accepts incoming connections and creating new threads to handle the given connection then. +static int main_thread ( void *user_param ) +{ + int client_seq = 0; + SDL_AtomicSet(&thread_counter, 1); // the main thread counts as the first one already + while (!SDL_AtomicGet(&thread_stop_trigger)) { + struct sockaddr_in sock_st; + int xerr; + // Wait for socket event with select, with 0.1sec timeout + // We need timeout, to check thread_stop_trigger condition + int select_result = xemusock_select_1(sock_server, 100000, XEMUSOCK_SELECT_R | XEMUSOCK_SELECT_E, &xerr); + if (!select_result) + continue; + if (select_result < 0) { + if (xerr == XSEINTR) + continue; + DEBUGPRINT("UMON: client: select() error: %s" NL, xemusock_strerror(xerr)); + SDL_Delay(100); + continue; + } + xemusock_socklen_t len = sizeof(struct sockaddr_in); + xemusock_socket_t sock = xemusock_accept(sock_server, (struct sockaddr *)&sock_st, &len, &xerr); + if (sock != XS_INVALID_SOCKET && sock != XS_SOCKET_ERROR) { // FIXME: both conditions needed? maybe others as well? + char thread_name[64]; + sprintf(thread_name, "Xemu-Umon-%d-%d", SDL_AtomicGet(&thread_counter), client_seq); + SDL_Thread *thread = SDL_CreateThread(client_thread_initiate, thread_name, (void*)(uintptr_t)sock); + if (thread) { + client_seq++; + SDL_DetachThread(thread); + } else { + DEBUGPRINT("UMON: client: cannot create thread for incomming connection" NL); + xemusock_shutdown(sock, NULL); + xemusock_close(sock, NULL); + } + } else { + DEBUGPRINT("UMON: client: accept() error: %s" NL, xemusock_strerror(xerr)); + SDL_Delay(10); + } + } + (void)SDL_AtomicAdd(&thread_counter, -1); // for the main thread itself + return 0; +} + + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * * END CRITICAL PART: these part ABOVE of the code runs in a *THREAD*. * * The rest of this file is about creating the thread and it's enivornment first, * @@ -523,72 +764,103 @@ static int xumon_main ( void *user_data_unused ) * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ -int xumon_init ( int port, int threaded ) +int xumon_init ( int port ) { - int optval; - struct sockaddr_in serveraddr; - DEBUGPRINT("UMON: requested monitor service on TCP port %d (%s)" NL, port, threaded ? "in dedicated thread" : "DEVELOPER: IN-MAIN-THREAD"); - if (xemu_use_sockapi()) - return -1; - if (server_sock != XEMUNET_INVALID_SOCKET || xumon_is_running) - return 0; // already initialized?? - server_sock = socket(AF_INET, SOCK_STREAM, 0); - if (server_sock == XEMUNET_INVALID_SOCKET) { - ERROR_WINDOW("UMON: Cannot create TCP socket: %s", xemunet_strneterror()); - return -1; + SDL_AtomicSet(&thread_counter, 0); + sock_server = XS_INVALID_SOCKET; + if (!port) { + DEBUGPRINT("UMON: not enabled" NL); + return 0; } - optval = 1; - if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (const void*)&optval, sizeof(int)) < 0) { - // Hmm, we may not want to treat this error as fatal, though SO_REUSEADDR is handy if you have crashed etc and be able to re-bind on the port - // without long minutes to wait :-O - xemunet_perror("setsockopt()"); + static const char err_msg[] = "UMON initialization problem, UMON won't be available:\n"; + if (port < 1024 || port > 0xFFFF) { + ERROR_WINDOW("%sInvalid port (must be between 1024 and 65535): %d", err_msg, port); + goto error; } - memset(&serveraddr, 0, sizeof serveraddr); - serveraddr.sin_family = AF_INET; - serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); - serveraddr.sin_port = htons((unsigned short)port); - if (bind(server_sock, (struct sockaddr *)&serveraddr, sizeof serveraddr) < 0) { - ERROR_WINDOW("UMON: Cannot bind TCP socket to port %d: %s", port, xemunet_strneterror()); - xemunet_close_socket(server_sock); - server_sock = XEMUNET_INVALID_SOCKET; - return -1; + const char *sock_init_status = xemusock_init(); + if (sock_init_status) { + ERROR_WINDOW("%sCannot initialize network library:\n%s", err_msg, sock_init_status); + goto error; } - if (listen(server_sock, 32) < 0) { - ERROR_WINDOW("UMON: Cannot listen to socket on TCP port %d: %s", port, xemunet_strneterror()); - xemunet_close_socket(server_sock); - server_sock = XEMUNET_INVALID_SOCKET; - return -1; + int xerr; + sock_server = xemusock_create_for_inet(XEMUSOCK_TCP, XEMUSOCK_BLOCKING, &xerr); + if (sock_server == XS_INVALID_SOCKET) { + ERROR_WINDOW("%sCannot create TCP socket:\n%s", err_msg, xemusock_strerror(xerr)); + goto error; } - if (threaded) { - xumon_thread_id = SDL_CreateThread(xumon_main, "Xemu-uMonitor", NULL); - if (!xumon_thread_id) { - ERROR_WINDOW("UMON: cannot create monitor thread: %s" NL, SDL_GetError()); - xemunet_close_socket(server_sock); - server_sock = XEMUNET_INVALID_SOCKET; - return -1; + if (xemusock_setsockopt_reuseaddr(sock_server, &xerr)) { + ERROR_WINDOW("UMON setsockopt for SO_REUSEADDR failed:\n%s", xemusock_strerror(xerr)); + goto error; + } + struct sockaddr_in sock_st; + xemusock_fill_servaddr_for_inet_ip_native(&sock_st, 0, port); + xemusock_socklen_t sock_len = sizeof(struct sockaddr_in); + if (xemusock_bind(sock_server, (struct sockaddr*)&sock_st, sock_len, &xerr)) { + ERROR_WINDOW("%sCannot bind TCP socket %d:\n%s", err_msg, port, xemusock_strerror(xerr)); + goto error; + } + if (xemusock_listen(sock_server, 5, &xerr)) { + ERROR_WINDOW("%sCannot listen socket %d:\n%s", err_msg, (int)sock_server, xemusock_strerror(xerr)); + goto error; + } + if (xemusock_set_nonblocking(sock_server, XEMUSOCK_NONBLOCKING, &xerr)) { + ERROR_WINDOW("%sCannot set socket %d into non-blocking mode:\n%s", err_msg, (int)sock_server, xemusock_strerror(xerr)); + goto error; + } + // Create thread to handle incoming connections on our brand new server socket we've just created for this purpose + SDL_AtomicSet(&thread_stop_trigger, 0); + Uint32 passed_time = 0, start_time = SDL_GetTicks(); + SDL_Thread *thread = SDL_CreateThread(main_thread, "Xemu-Umon-Main", NULL); + if (!thread) { + ERROR_WINDOW("%sCannot create monitor thread:\n%s", err_msg, SDL_GetError()); + goto error; + } + SDL_DetachThread(thread); + while (!SDL_AtomicGet(&thread_counter)) { + SDL_Delay(1); + passed_time = SDL_GetTicks() - start_time; + if (passed_time > 500) { + DEBUGPRINT("UMON: timeout while waiting for thread to start! UMON won't be available!" NL); + goto error; } - DEBUGPRINT("UMON: thread started with thread id %p: \"%s\"" NL, xumon_thread_id, SDL_GetThreadName(xumon_thread_id)); - //SDL_DetachThread(xumon_thread_id); - return 0; - } else { - ERROR_WINDOW("!!Developer ONLY mode activated with negative port number!!"); - return xumon_main(NULL); } + // Everything is OK, return with success. + DEBUGPRINT("UMON: has been initialized for TCP/IP port %d, on-line within %d msecs." NL, port, passed_time); return 0; +error: + SDL_AtomicSet(&thread_stop_trigger, 1); + if (sock_server != XS_INVALID_SOCKET) { + int xerr; + if (xemusock_close(sock_server, &xerr)) + DEBUGPRINT("UMON: warning, could not close server socket after error: %s" NL, xemusock_strerror(xerr)); + sock_server = XS_INVALID_SOCKET; + } + return 1; } int xumon_stop ( void ) { - if (xumon_thread_id && !xumon_is_running) { - int ret; - SDL_WaitThread(xumon_thread_id, &ret); - xumon_thread_id = NULL; - return ret; + int count = SDL_AtomicGet(&thread_counter); + if (!count) + return 0; + Uint32 passed_time = 0, start_time = SDL_GetTicks(); + SDL_AtomicSet(&thread_stop_trigger, 1); + while (SDL_AtomicGet(&thread_counter) > 0) { + SDL_Delay(1); + passed_time = SDL_GetTicks() - start_time; + if (passed_time > 500) { + DEBUGPRINT("UMON: timeout while waiting for threads to stop!" NL); + break; + } } - if (!xumon_is_running || !xumon_thread_id) - return -1; - thread_stop_trigger = 1; + xemusock_set_nonblocking(sock_server, XEMUSOCK_BLOCKING, NULL); + xemusock_shutdown(sock_server, NULL); + xemusock_close(sock_server, NULL); + sock_server = XS_INVALID_SOCKET; + int count2 = SDL_AtomicGet(&thread_counter); + DEBUGPRINT("UMON: shutdown, %d thread(s) (%d client) exited, %d thread(s) has timeout condition, %d msecs." NL, count - count2, count - count2 - 1, count2, passed_time); + return 0; } #endif diff --git a/xemu/emutools_umon.h b/xemu/emutools_umon.h index 1e9251ce..a1340957 100644 --- a/xemu/emutools_umon.h +++ b/xemu/emutools_umon.h @@ -1,5 +1,5 @@ /* Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2017-2020 LGB (Gábor Lénárt) + Copyright (C)2017-2021 LGB (Gábor Lénárt) The goal of emutools.c is to provide a relative simple solution for relative simple emulators using SDL2. @@ -21,13 +21,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __XEMU_COMMON_EMUTOOLS_UMON_H_INCLUDED #define __XEMU_COMMON_EMUTOOLS_UMON_H_INCLUDED #ifdef HAVE_XEMU_UMON -#ifndef HAVE_XEMU_SOCKET_API +#ifndef XEMU_HAS_SOCKET_API #error "Need HAVE_XEMU_SOCKET_API for HAVE_XEMU_UMON to be enabled at the target!" #endif -extern volatile int xumon_is_running; +#define XUMON_DEFAULT_PORT 9000 +#define XUMON_MAX_THREADS 32 +#define XUMON_STACK_SIZE (8*1024*1024) -extern int xumon_init ( int port, int threaded ); +extern int xumon_init ( int port ); +extern int xumon_stop ( void ); #endif #endif From e5c62eceac9ea5043a276aa4822e6f7dc3f24243 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 19:22:09 +0200 Subject: [PATCH 07/35] CORE/HID: sdl fake key event from 'merger' branch --- xemu/emutools_hid.c | 20 ++++++++++++++++++++ xemu/emutools_hid.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/xemu/emutools_hid.c b/xemu/emutools_hid.c index 118ca81b..c611a14f 100644 --- a/xemu/emutools_hid.c +++ b/xemu/emutools_hid.c @@ -114,6 +114,26 @@ int hid_key_event ( SDL_Scancode key, int pressed ) } +void hid_sdl_synth_key_event ( int matrix_pos, int is_press ) +{ + const struct KeyMappingUsed *map = key_map; + while (map->pos >= 0) { + if (map->pos == matrix_pos) { + SDL_Event sdlevent = {}; + sdlevent.type = is_press ? SDL_KEYDOWN : SDL_KEYUP; + sdlevent.key.repeat = 0; + sdlevent.key.windowID = sdl_winid; + sdlevent.key.state = is_press ? SDL_PRESSED : SDL_RELEASED; + sdlevent.key.keysym.scancode = map->scan; + SDL_PushEvent(&sdlevent); + return; + } + map++; + } +} + + + // Reset all HID events. // Ie: it's usefull for initialization, and in the case when the emulator pops a window, // in this case SDL may detect the event used to ack the window causing problems. So those diff --git a/xemu/emutools_hid.h b/xemu/emutools_hid.h index 470e36ea..cc9a7be9 100644 --- a/xemu/emutools_hid.h +++ b/xemu/emutools_hid.h @@ -62,6 +62,8 @@ extern void emu_callback_key_raw_sdl ( SDL_KeyboardEvent *ev ); extern void emu_callback_key_texteditng_sdl ( SDL_TextEditingEvent *ev ); extern void emu_callback_key_textinput_sdl ( SDL_TextInputEvent *ev ); +extern void hid_sdl_synth_key_event ( int matrix_pos, int is_press ); + // Provided HID functions: extern void hid_set_autoreleased_key ( int key ); extern int hid_key_event ( SDL_Scancode key, int pressed ) ; From 17522f59588908c2c6a0af770ca1e550dd510223 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 19:44:27 +0200 Subject: [PATCH 08/35] CORE: emutools update from 'merger' branch --- xemu/emutools.c | 160 +++++++++++++++++++++++++++++++++++++++++------- xemu/emutools.h | 9 +++ 2 files changed, 147 insertions(+), 22 deletions(-) diff --git a/xemu/emutools.c b/xemu/emutools.c index c82b13cc..3c6b5f4b 100644 --- a/xemu/emutools.c +++ b/xemu/emutools.c @@ -31,9 +31,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef XEMU_ARCH_UNIX # include #endif -#ifdef HAVE_XEMU_SOCKET_API -# include "xemu/emutools_socketapi.h" -#endif #ifdef XEMU_MISSING_BIGGEST_ALIGNMENT_WORKAROUND # warning "System did not define __BIGGEST_ALIGNMENT__ Xemu assumes some default value." @@ -82,7 +79,9 @@ SDL_Window *sdl_win = NULL; SDL_Renderer *sdl_ren = NULL; SDL_Texture *sdl_tex = NULL; SDL_PixelFormat *sdl_pix_fmt; +static Uint32 sdl_pixel_format_id; static const char default_window_title[] = "XEMU"; +int register_new_texture_creation = 0; char *xemu_app_org = NULL, *xemu_app_name = NULL; #ifdef XEMU_ARCH_HTML static const char *emscripten_sdl_base_dir = EMSCRIPTEN_SDL_BASE_DIR; @@ -112,9 +111,13 @@ FILE *debug_fp = NULL; int chatty_xemu = 1; int sdl_default_win_x_size; int sdl_default_win_y_size; +static SDL_Rect sdl_viewport, *sdl_viewport_ptr = NULL; +static unsigned int sdl_texture_x_size, sdl_texture_y_size; static SDL_bool grabbed_mouse = SDL_FALSE, grabbed_mouse_saved = SDL_FALSE; int allow_mouse_grab = 1; +static int sdl_viewport_changed; +static int follow_win_size; #if !SDL_VERSION_ATLEAST(2, 0, 4) #error "At least SDL version 2.0.4 is needed!" @@ -502,9 +505,6 @@ static void shutdown_emulator ( void ) sdl_win = NULL; } atexit_callback_for_console(); -#ifdef HAVE_XEMU_SOCKET_API - xemusock_uninit(); -#endif //SDL_Quit(); if (td_stat_counter) { char td_stat_str[XEMU_CPU_STAT_INFO_BUFFER_SIZE]; @@ -687,16 +687,25 @@ void xemu_pre_init ( const char *app_organization, const char *app_name, const c int xemu_init_sdl ( void ) { #ifndef XEMU_ARCH_HTML - if (!SDL_WasInit(SDL_INIT_EVERYTHING)) { + const Uint32 XEMU_SDL_INIT_EVERYTHING = +#if defined(XEMU_ARCH_WIN) && defined(SDL_INIT_SENSOR) + // FIXME: SDL or Windows has the bug that SDL_INIT_SENSOR when used, there is some "sensor manager" problem, so we left it out + // SDL_INIT_SENSOR was introduced somewhere in 2.0.9, however since it's a macro, it's safer not to test actual SDL version number + SDL_INIT_EVERYTHING & (~SDL_INIT_SENSOR); +#warning "Activating windows + SDL sensor init problem workaround ..." +#else + SDL_INIT_EVERYTHING; +#endif + if (!SDL_WasInit(XEMU_SDL_INIT_EVERYTHING)) { DEBUGPRINT("SDL: no SDL subsystem initialization has been done yet, do it!" NL); SDL_Quit(); // Please read the long comment at the pre-init func above to understand this SDL_Quit() here and then the SDL_Init() right below ... DEBUG("SDL: before SDL init" NL); - if (SDL_Init(SDL_INIT_EVERYTHING)) { + if (SDL_Init(XEMU_SDL_INIT_EVERYTHING)) { ERROR_WINDOW("Cannot initialize SDL: %s", SDL_GetError()); return 1; } DEBUG("SDL: after SDL init" NL); - if (!SDL_WasInit(SDL_INIT_EVERYTHING)) + if (!SDL_WasInit(XEMU_SDL_INIT_EVERYTHING)) FATAL("SDL_WasInit()=0 after init??"); } else DEBUGPRINT("SDL: no SDL subsystem initialization has been done already." NL); @@ -734,6 +743,105 @@ int xemu_init_sdl ( void ) } +void xemu_window_snap_to_optimal_size ( int forced ) +{ + // XXX TODO check if fullscreen state is active? + // though it must be checked if it's needed at all (ie: SDL is OK with resizing window in fullscreen mode without any effect BEFORE switcing back from fullscreen) + static Uint32 last_resize = 0; + Uint32 now = 0; + if (!forced && sdl_viewport_changed && follow_win_size) { + now = SDL_GetTicks(); + if (now - last_resize >= 1000) { + sdl_viewport_changed = 0; + forced = 1; + } + } + if (!forced) + return; + int w, h; + SDL_GetWindowSize(sdl_win, &w, &h); + float rat = (float)w / (float)sdl_viewport.w; + const float rat2 = (float)h / (float)sdl_viewport.h; + if (rat2 > rat) + rat = rat2; + rat = roundf(rat); // XXX TODO: depends on math.h mingw warning! + // XXX TODO: check if window is not larger than the screen itself + if (rat < 1) + rat = 1; + const int w2 = rat * sdl_viewport.w; + const int h2 = rat * sdl_viewport.h; + if (w != w2 || h != h2) { + last_resize = now; + SDL_SetWindowSize(sdl_win, w2, h2); + DEBUGPRINT("SDL: auto-resizing window to %d x %d (zoom level approximated: %d)" NL, w2, h2, (int)rat); + } else + DEBUGPRINT("SDL: no auto-resizing was needed (same size)" NL); +} + + +void xemu_set_viewport ( unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int flags ) +{ + if (XEMU_UNLIKELY(x1 == 0 && y1 == 0 && x2 == 0 && y2 == 0)) { + sdl_viewport_ptr = NULL; + sdl_viewport.x = 0; + sdl_viewport.y = 0; + sdl_viewport.w = sdl_texture_x_size; + sdl_viewport.h = sdl_texture_y_size; + } else { + if (XEMU_UNLIKELY(x1 > x2 || y1 > y2 || x1 >= sdl_texture_x_size || y1 >= sdl_texture_y_size || x2 >= sdl_texture_x_size || y2 >= sdl_texture_y_size)) { + FATAL("Invalid xemu_set_viewport(%d,%d,%d,%d) for texture (%d x %d)", x1, y1, x2, y2, sdl_texture_x_size, sdl_texture_y_size); + } else { + sdl_viewport_ptr = &sdl_viewport; + sdl_viewport.x = x1; + sdl_viewport.y = y1; + sdl_viewport.w = x2 - x1 + 1; + sdl_viewport.h = y2 - y1 + 1; + } + } + sdl_viewport_changed = 1; + follow_win_size = 0; + if ((flags & XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE)) { + SDL_RenderSetLogicalSize(sdl_ren, sdl_viewport.w, sdl_viewport.h); + // XXX this should be not handled this way + sdl_default_win_x_size = sdl_viewport.w; + sdl_default_win_y_size = sdl_viewport.h; + //if ((flags & XEMU_VIEWPORT_WIN_SIZE_FOLLOW_LOGICAL)) + //XXX remove this XEMU_VIEWPORT_WIN_SIZE_FOLLOW_LOGICAL then! + follow_win_size = 1; + } +} + + +void xemu_get_viewport ( unsigned int *x1, unsigned int *y1, unsigned int *x2, unsigned int *y2 ) +{ + if (x1) + *x1 = sdl_viewport.x; + if (y1) + *y1 = sdl_viewport.y; + if (x2) + *x2 = sdl_viewport.x + sdl_viewport.w - 1; + if (y2) + *y2 = sdl_viewport.y + sdl_viewport.h - 1; +} + + +static int xemu_create_main_texture ( void ) +{ + DEBUGPRINT("SDL: creating main texture %d x %d" NL, sdl_texture_x_size, sdl_texture_y_size); + SDL_Texture *new_tex = SDL_CreateTexture(sdl_ren, sdl_pixel_format_id, SDL_TEXTUREACCESS_STREAMING, sdl_texture_x_size, sdl_texture_y_size); + if (!new_tex) { + DEBUGPRINT("SDL: cannot create main texture: %s" NL, SDL_GetError()); + return 1; + } + if (sdl_tex) { + DEBUGPRINT("SDL: destroying old main texture" NL); + SDL_DestroyTexture(sdl_tex); + } + sdl_tex = new_tex; + return 0; +} + + /* Return value: 0 = ok, otherwise: ERROR, caller must exit, and can't use any other functionality, otherwise crash would happen.*/ int xemu_post_init ( const char *window_title, // title of our window @@ -866,8 +974,11 @@ int xemu_post_init ( DEBUGPRINT(")" NL); } SDL_RenderSetLogicalSize(sdl_ren, logical_x_size, logical_y_size); // this helps SDL to know the "logical ratio" of screen, even in full screen mode when scaling is needed! - sdl_tex = SDL_CreateTexture(sdl_ren, pixel_format, SDL_TEXTUREACCESS_STREAMING, texture_x_size, texture_y_size); - if (!sdl_tex) { + sdl_texture_x_size = texture_x_size; + sdl_texture_y_size = texture_y_size; + sdl_pixel_format_id = pixel_format; + xemu_set_viewport(0, 0, 0, 0, 0); + if (xemu_create_main_texture()) { ERROR_WINDOW("Cannot create SDL texture: %s", SDL_GetError()); return 1; } @@ -979,6 +1090,10 @@ void xemu_render_dummy_frame ( Uint32 colour, int texture_x_size, int texture_y_ tail is meant in 4 bytes (ie Uint32 pointer)! */ Uint32 *xemu_start_pixel_buffer_access ( int *texture_tail ) { + if (register_new_texture_creation) { + register_new_texture_creation = 0; + xemu_create_main_texture(); + } if (sdl_pixel_buffer) { *texture_tail = 0; // using non-locked texture access, "tail" is always zero xemu_frame_pixel_access_p = sdl_pixel_buffer; @@ -1014,7 +1129,7 @@ void xemu_update_screen ( void ) } //if (seconds_timer_trigger) SDL_RenderClear(sdl_ren); // Note: it's not needed at any price, however eg with full screen or ratio mismatches, unused screen space will be corrupted without this! - SDL_RenderCopy(sdl_ren, sdl_tex, NULL, NULL); + SDL_RenderCopy(sdl_ren, sdl_tex, sdl_viewport_ptr, NULL); #ifdef XEMU_OSD_SUPPORT _osd_render(); #endif @@ -1223,22 +1338,23 @@ void sysconsole_close ( const char *waitmsg ) // So instead of a GUI element here with a dialog box, we must rely on the console to press a key to continue ... printf("\n\n*** %s\nPress SPACE to continue.", waitmsg); while (sysconsole_getch() != 32) - ; + SDL_Delay(1); } - // redirect std file handled to "NUL" to avoid strange issues after closing the console, like corrupting - // other files (for unknown reasons) by further I/O after FreeConsole() ... - if ( - file_handle_redirect(NULL_DEVICE, "stderr", "w", stderr) || - file_handle_redirect(NULL_DEVICE, "stdout", "w", stdout) || - file_handle_redirect(NULL_DEVICE, "stdin", "r", stdin ) - ) - return; // we want to be sure to abort closing console, if redirection didn't worked for some reason!! if (!FreeConsole()) { if (!waitmsg) ERROR_WINDOW("Cannot release windows console!"); } else { sysconsole_is_open = 0; - DEBUGPRINT("WINDOWS: console is closed" NL); +#if 1 + // redirect std file handled to "NUL" to avoid strange issues after closing the console, like corrupting + // other files (for unknown reasons) by further I/O after FreeConsole() ... + int ret = file_handle_redirect(NULL_DEVICE, "stderr", "w", stderr); + ret |= file_handle_redirect(NULL_DEVICE, "stdout", "w", stdout); + ret |= file_handle_redirect(NULL_DEVICE, "stdin", "r", stdin ); + DEBUG("WINDOWS: console has been closed (file_handle_redirect: %s)" NL, ret ? "ERROR" : "OK"); +#else + DEBUGPRINT("WINDOWS: console has been closed" NL); +#endif } #elif defined(XEMU_ARCH_MAC) if (macos_gui_started) { diff --git a/xemu/emutools.h b/xemu/emutools.h index 647f36c2..b380e99c 100644 --- a/xemu/emutools.h +++ b/xemu/emutools.h @@ -138,9 +138,18 @@ extern int seconds_timer_trigger; extern char *sdl_pref_dir, *sdl_base_dir, *sdl_inst_dir; extern int sysconsole_is_open; extern int sdl_default_win_x_size, sdl_default_win_y_size; +extern int register_new_texture_creation; extern SDL_version sdlver_compiled, sdlver_linked; extern Uint32 *xemu_frame_pixel_access_p; +#define XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE 1 +//#define XEMU_VIEWPORT_WIN_SIZE_FOLLOW_LOGICAL 2 + +extern void xemu_set_viewport ( unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int flags ); +extern void xemu_get_viewport ( unsigned int *x1, unsigned int *y1, unsigned int *x2, unsigned int *y2 ); + +extern void xemu_window_snap_to_optimal_size ( int forced ); + extern int xemu_init_debug ( const char *fn ); extern time_t xemu_get_unixtime ( void ); extern struct tm *xemu_get_localtime ( void ); From bfdcda7f32d118aae57c6354a29ce0c69c45d326 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 19:55:56 +0200 Subject: [PATCH 09/35] CORE: build sys updates from branch 'merger' --- build/Makefile.common | 4 ++++ rom/Makefile | 22 +++++++++++++--------- rom/c65-minimal-rom.asm | 31 +++++++++++++++++++++++++++++++ rom/c65-minimal-rom.cdata | 1 + rom/c65-minimal-rom.rom | 1 + rom/clcd-u104-parasite.asm | 2 ++ 6 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 rom/c65-minimal-rom.asm create mode 100644 rom/c65-minimal-rom.cdata create mode 100644 rom/c65-minimal-rom.rom diff --git a/build/Makefile.common b/build/Makefile.common index d820eabd..088134df 100644 --- a/build/Makefile.common +++ b/build/Makefile.common @@ -87,6 +87,10 @@ endif ifeq ($(SUPERPICKY), yes) CONFIG_CFLAGS += $(CONFIG_CFLAGS_SUPERPICKY) endif +ifeq ($(STACKPROTECTOR), yes) +CONFIG_CFLAGS += -fstack-protector-strong +CONFIG_CFLAGS_LINK += -fstack-protector-strong +endif CFLAGS = $(CONFIG_CFLAGS) $(CFLAGS_OPT) $(CFLAGS_TARGET) -I. -I$(TOPDIR) -include build/configure/config-$(ARCH).h LDFLAGS = $(CONFIG_CFLAGS_LINK) $(LDFLAGS_TARGET) diff --git a/rom/Makefile b/rom/Makefile index 72dc9815..c940e495 100644 --- a/rom/Makefile +++ b/rom/Makefile @@ -21,12 +21,10 @@ SHELL = sh AWK = awk CL65 = cl65 URL_LIST = rom-fetch-list.txt -#OUR_ROMS = clcd-u104-parasite.rom vic20-emulator-tool.rom -OUR_ROMS = vic20-emulator-tool.rom -#FETCH_ROMS = vic20-basic.rom vic20-kernal.rom vic20-chargen.rom clcd-u105.rom clcd-u102.rom clcd-u104.rom clcd-u103.rom c65-system.rom c64-chargen.rom c64-kernal.rom c64-basic.rom primo-b64.rom -#FETCH_ROMS = $(shell awk '{ print $$1 }' $(URL_LIST)) +OUR_ROMS = vic20-emulator-tool.rom c65-minimal-rom.rom clcd-u104-parasite.rom +OUR_INCS = c65-minimal-rom.cdata -all: roms $(OUR_ROMS) +all: $(OUR_ROMS) $(OUR_INCS) clean: rm -f *.o @@ -35,10 +33,16 @@ distclean: $(MAKE) clean rm -f `$(AWK) 'NF == 3 && $$1 ~ /^[a-zA-Z0-9]/ { print $$1 }' < $(URL_LIST)` -%.rom: %.asm - $(CL65) -t none --cpu 65c02 -o $@ $< +%.rom: %.asm Makefile + @echo "<<< Crafting $@ >>>" + $(CL65) -t none -o $@ $< + rm -f $(<:.asm=.o) -roms: +%.cdata: %.rom Makefile + @echo "<<< Crafting $@ >>>" + cat $< | xxd -i > $@ + +fetchroms: $(AWK) 'NF == 3 && $$1 ~ /^[a-zA-Z0-9]/ { print "test -s " $$1 " || { rm -f " $$1 ".tmp && $(WGET) -O " $$1 ".tmp " $$2 " && mv " $$1 ".tmp " $$1 "; } || exit $$?" }' < $(URL_LIST) | $(SHELL) -.PHONY: all clean distclean roms +.PHONY: all clean distclean fetchroms diff --git a/rom/c65-minimal-rom.asm b/rom/c65-minimal-rom.asm new file mode 100644 index 00000000..a6878ff0 --- /dev/null +++ b/rom/c65-minimal-rom.asm @@ -0,0 +1,31 @@ +; This is a very simple C65 (or MEGA65) ROM, which is totally unusable, +; just enough to display a message for Xemu users, so they get to know +; what they should do. This is not a "normal" ROM either by layout, +; but has "ID points". Only useful really, inside Xemu. +; +; Copyright (C)2021 LGB (Gábor Lénárt) +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, write to the Free Software +; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +.SETCPU "4510" +.ORG 0 + +; "C65 ROM version" style of string, since it's used by Xemu to figure out +; the needed DMA revision for the given ROM. + +.BYTE "V911111" + + + diff --git a/rom/c65-minimal-rom.cdata b/rom/c65-minimal-rom.cdata new file mode 100644 index 00000000..e544468d --- /dev/null +++ b/rom/c65-minimal-rom.cdata @@ -0,0 +1 @@ + 0x56, 0x39, 0x31, 0x31, 0x31, 0x31, 0x31 diff --git a/rom/c65-minimal-rom.rom b/rom/c65-minimal-rom.rom new file mode 100644 index 00000000..a9b861cb --- /dev/null +++ b/rom/c65-minimal-rom.rom @@ -0,0 +1 @@ +V911111 \ No newline at end of file diff --git a/rom/clcd-u104-parasite.asm b/rom/clcd-u104-parasite.asm index 6936e4b1..7c72d09f 100644 --- a/rom/clcd-u104-parasite.asm +++ b/rom/clcd-u104-parasite.asm @@ -32,6 +32,8 @@ ; along with this program; if not, write to the Free Software ; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +.SETCPU "65C02" + START = $4000 .ORG START From 30f145415aaad53952fba6076d84d21f09733a34 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 19:57:47 +0200 Subject: [PATCH 10/35] CORE: updates from branch 'merger' --- xemu/basic_text.c | 165 +++++++++++++++++++++++++++++++++------------- xemu/cia6526.c | 10 ++- xemu/cpu65.c | 8 +-- 3 files changed, 131 insertions(+), 52 deletions(-) diff --git a/xemu/basic_text.c b/xemu/basic_text.c index a4fef99b..0dc5df4c 100644 --- a/xemu/basic_text.c +++ b/xemu/basic_text.c @@ -21,6 +21,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "xemu/emutools.h" #include "xemu/basic_text.h" +#include +#include + + #ifdef CBM_BASIC_TEXT_SUPPORT @@ -382,57 +386,79 @@ int xemu_basic_to_text ( Uint8 *output, int output_size, const Uint8 *prg, int r #endif -#define DUMP_SCR_CODE() t += sprintf(t, "{$%02X}", c) +static const struct { + const Uint8 screen_code; + const char *text; +} conv_tab_screen[] = { + { 0x00, "@" }, + { 0x1B, "[" }, + { 0x1C, "\\" }, // pound symbol, mapped to backslash + { 0x1C, "{pound}" }, // pound symbol, alternative representation + { 0x1C, "£" }, // pound symbol (UTF8!) + { 0x1D, "]" }, + { 0x1E, "^" }, // up-arrow symbol + { 0x1F, "_" }, // left-arrow symbol, mapped to underscore + { 0x40, "{dash}" }, + { 0x5E, "{pi}" }, + { 0x93, "{home}" }, + // $80-$9F + { 0x82, "{uloff}"}, { 0x83, "{stop}" }, { 0x85, "{wht}" }, { 0x87, "{bell}" }, + { 0x89, "{ht}" }, { 0x8A, "{lf}" }, { 0x8B, "{shen}" }, { 0x8C, "{shdi}" }, { 0x8D, "{ret}" }, { 0x8E, "{text}" }, { 0x8F, "{flon}" }, + { 0x90, "{f9}" }, { 0x91, "{down}" }, { 0x92, "{rvon}" }, { 0x93, "{home}" }, { 0x94, "{del}" }, { 0x95, "{f10}" }, { 0x96, "{f11}" }, { 0x97, "{f12}" }, + { 0x98, "{tab}" }, { 0x99, "{f13}" }, { 0x9A, "{f14}" }, { 0x9B, "{esc}" }, { 0x9C, "{red}" }, { 0x9D, "{right}"}, { 0x9E, "{grn}" }, { 0x9F, "{blu}" }, + // $E0-$DF + { 0xE1, "{orng}" }, { 0xE2, "{ulon}" }, { 0xE3, "{run}" }, { 0xE4, "{help}" }, { 0xE5, "{f1}" }, { 0xE6, "{f3}" }, { 0xE7, "{f5}" }, + { 0xE8, "{f7}" }, { 0xE9, "{f2}" }, { 0xEA, "{f4}" }, { 0xEB, "{f6}" }, { 0xEC, "{f8}" }, { 0xED, "{sret}" }, { 0xEE, "{gfx}" }, { 0xEF, "{floff}"}, + { 0xD0, "{blk}" }, { 0xD1, "{up}" }, { 0xD2, "{rvoff}"}, { 0xD3, "{clr}" }, { 0xD4, "{inst}" }, { 0xD5, "{brn}" }, { 0xD6, "{lred}" }, { 0xD7, "{gry1}" }, + { 0xD8, "{gry2}" }, { 0xD9, "{lgrn}" }, { 0xDA, "{lblu}" }, { 0xDB, "{gry3}" }, { 0xDC, "{pur}" }, { 0xDD, "{left}" }, { 0xDE, "{yel}" }, { 0xDF, "{cyn}" }, + { 0x00, NULL } +}; + + + char *xemu_cbm_screen_to_text ( char *buffer, const int buffer_size, const Uint8 *v, const int cols, const int rows, const int lowercase ) { - static const char *rvs_msgs[] = { "{RVS-OFF}", "{RVS-ON}" }; char *t = buffer; for (int y = 0; y < rows; y++) { - int rvs = 0; for (int x = 0; x < cols; x++) { if (XEMU_UNLIKELY(t - buffer > buffer_size - 16)) { ERROR_WINDOW("Sorry, ASCII converted screen does not fit into the output buffer"); return NULL; } - Uint8 c = (*v++); - if (XEMU_UNLIKELY((c & 0x80) != rvs)) { - rvs = (c & 0x80); - t = xemu_strcpy_special(t, rvs_msgs[!!rvs]); + Uint8 c = *v++; + // first, check out translation table for special cases + for (int a = 0; conv_tab_screen[a].text; a++) { + if (conv_tab_screen[a].screen_code == c) { + t += sprintf(t, "%s", conv_tab_screen[a].text); + goto next_char; + } + } + //const Uint8 inv = c & 0x80; + //c &= 0x7F; + if (c >= 0x01 && c <= 0x1A) { // Capital A-Z (in uppercase mode), a-z (in lower case mode) + *t++ = c - 1 + (lowercase ? 'a' : 'A'); + continue; } - c &= 0x7F; // we can't convert reverse visually per chars, so let's forget the upper bit - if (c == 0) { - *t++ = '@'; - } else if (c < 27) { - *t++ = c + (lowercase ? 'a' : 'A') - 1; - } else if (c == 27) { - *t++ = '['; - } else if (c == 28) { // pound - DUMP_SCR_CODE(); - } else if (c == 29) { - *t++ = ']'; - } else if (c == 30) { // up arrow - DUMP_SCR_CODE(); - } else if (c == 31) { // left arrow - DUMP_SCR_CODE(); - } else if (c < 64) { // space, signs, numbers ... identical position :D :D + if (c >= 0x20 && c <= 0x3F) { // space, various common marks, numbers: at the same place as in ASCII! *t++ = c; - } else if (c == 64) { // "bold minus" like entity ... - DUMP_SCR_CODE(); - } else if (c < 91) { // symbols, or capital letters (the 2nd: if in lower-case charset mode!) + continue; + } + if (c >= 0x41 && c <= 0x5A) { // Gfx chars (in uppercase mode), A-Z (in lower case mode) if (lowercase) - *t++ = c - 65 + 'A'; + *t++ = c; else - t += sprintf(t, "{%c}", c - 65 + 'A'); - } else { - DUMP_SCR_CODE(); + t += sprintf(t, "{%c}", c); + continue; } + // Missing policy for remaining characters, let's dump with its screen code + t += sprintf(t, "{$%02X}", c); + // well yeah, do not say anything ... C does not leave too much choice to continue from a nested loop ... + next_char: + continue; } - if (XEMU_UNLIKELY(rvs)) - t = xemu_strcpy_special(t, rvs_msgs[0]); - else - while (t > buffer && t[-1] == ' ') // remove trailing spaces - t--; - t = xemu_strcpy_special(t, NL); // put a newline + while (t > buffer && t[-1] == ' ') // remove trailing spaces + t--; + t += sprintf(t, "%s", NL); // put a newline } // remove empty lines from the end of our capture while (t > buffer && (t[-1] == '\r' || t[-1] == '\n')) @@ -444,7 +470,6 @@ char *xemu_cbm_screen_to_text ( char *buffer, const int buffer_size, const Uint8 // return our result! return buffer; } -#undef DUMP_SCR_CODE int xemu_cbm_text_to_screen ( Uint8 *v, const int cols, const int rows, const char *buffer, const int lowercase ) @@ -452,10 +477,44 @@ int xemu_cbm_text_to_screen ( Uint8 *v, const int cols, const int rows, const ch const Uint8 *start = v; const Uint8 *end = v + (cols * rows); char ch_prev, ch = 0; - v += cols; // do not use the first line, we expect use may have the cursor there + v += cols; // do not use the first line, we expect user to have the cursor there, to be safe while (*buffer && v < end) { ch_prev = ch; + // first, check out translation table for special cases + for (int a = 0; conv_tab_screen[a].text; a++) { + const int l = strlen(conv_tab_screen[a].text); + if (!strncmp(conv_tab_screen[a].text, buffer, l)) { + *v++ = conv_tab_screen[a].screen_code; + buffer += l; + ch = 0x40; // something, which is not newline ;) + goto next_char; + } + } + // fetch next to-be-pasted ASCII character ch = *buffer++; + // Special sequences which are NOT handled by the "conv_tab_screen" loop above + if (ch == '{') { + const char *p = strchr(buffer, '}'); + if (!p) { + *v++ = 0x1B; // just fake a '[' because ... + continue; // ... closing pair of '{' is not found, ignore this character + } + const int l = (int)(p - buffer); + if (l == 1) { // single char {X} case + *v++ = *buffer; + } else if (l == 3 && *buffer == '$') { + char *e; + const Uint8 h = (Uint8)strtol(buffer + 1, &e, 16); + if (e == p) + *v++ = h; + } + buffer = p + 1; // move to the next ASCII to-be-pasted character after '}' + continue; + } + if (ch == '}') { // if there is a '}' for whatever reason, translate into ']' + *v++ = 0x1D; + continue; + } if (ch == '\n' || ch == '\r') { if ((ch_prev == '\n' || ch_prev == '\r') && ch_prev != ch) { ch_prev = 0; @@ -466,15 +525,29 @@ int xemu_cbm_text_to_screen ( Uint8 *v, const int cols, const int rows, const ch *v++ = 32; continue; } - if (ch == '\t') // space (also TAB is rendered as space for now ...) - ch = 32; - else if (ch == '@') - ch = 0; - else if (ch >= 97 && ch <= 122) // 'a' ... 'z' - ch -= 97 - 1; - else if ((signed char)ch < 32) + if (ch == '\t') { // space (also TAB is rendered as space for now ...) + *v++ = 32; continue; - *v++ = ch; + } + if ((signed char)ch < 32) // ignore invalid characters (as a signed byte value this is also true for >=128 unsigned ones) + continue; + if (ch >= 0x61 && ch <= 0x7A) { // ASCII small letters + *v++ = ch - 0x61 + 1; + continue; + } + if (ch >= 0x41 && ch <= 0x5A) { // ASCII capital letters + *v++ = lowercase ? ch : ch - 0x41 + 1; + continue; + } + if (ch >= 0x20 && ch <= 0x3F) { // space, various common marks, numbers: at the same place as in ASCII! + *v++ = ch; + continue; + } + // The unknown stuff ... Now mark with '@' + DEBUGPRINT("PASTE: unknown ASCII character: %c (%d)" NL, ch, (unsigned int)ch); + *v++ = 0; + next_char: + continue; } while (v < end && ((v - start) % cols)) *v++ = 32; diff --git a/xemu/cia6526.c b/xemu/cia6526.c index 7d711d91..ae7f661d 100644 --- a/xemu/cia6526.c +++ b/xemu/cia6526.c @@ -284,11 +284,13 @@ void cia_tick ( struct Cia6526 *cia, int ticks ) /* Timer A */ if (cia->CRA & 1) { cia->TCA -= ticks; - if (cia->TCA < 0) { + if (cia->TCA <= 0) { timer_a_underflow = 1; DEBUG("%s timer-A expired!" NL, cia->name); ICR_SET(1); cia->TCA += cia->TLAL | (cia->TLAH << 8); + if (cia->TCA < 0) + cia->TCA = 0; if (cia->CRA & 8) cia->CRA &= 254; // one shot mode: reset bit 1 (timer stop) } @@ -299,13 +301,17 @@ void cia_tick ( struct Cia6526 *cia, int ticks ) if ((cia->CRB & 64)) { // linked timers mode: timer-B counts of underflows of timer-A if (timer_a_underflow) cia->TCB--; + else + return; } else { // independent timer-B: works the same as timer-A cia->TCB -= ticks; } - if (cia->TCB < 0) { + if (cia->TCB <= 0) { DEBUG("%s timer-B expired!" NL, cia->name); ICR_SET(2); cia->TCB += cia->TLBL | (cia->TLBH << 8); + if (cia->TCB < 0) + cia->TCB = 0; if (cia->CRB & 8) cia->CRB &= 254; // one shot mode: reset bit 1 (timer stop) } diff --git a/xemu/cpu65.c b/xemu/cpu65.c index 1fcdd787..59513665 100644 --- a/xemu/cpu65.c +++ b/xemu/cpu65.c @@ -212,7 +212,7 @@ static XEMU_INLINE Uint16 readWord(Uint16 addr) { #ifdef MEGA65 -static Uint32 readLong ( Uint16 addr ) { +static Uint32 readQuad ( Uint16 addr ) { return readByte(addr ) | (readByte(addr + 1) << 8 ) | @@ -221,7 +221,7 @@ static Uint32 readLong ( Uint16 addr ) { ; } -static void writeLong ( Uint16 addr, Uint32 data ) { +static void writeQuad ( Uint16 addr, Uint32 data ) { writeByte(addr , data & 0xFF); writeByte(addr + 1, (data >> 8) & 0xFF); writeByte(addr + 2, (data >> 16) & 0xFF); @@ -700,7 +700,7 @@ int cpu65_step ( && CPU65.op_cycles != 1 && !CPU65.cpu_inhibit_interrupts #endif #ifdef MEGA65 - && !in_hypervisor + && !in_hypervisor && CPU65.prefix == PREFIX_NOTHING #endif )) { #ifdef DEBUG_CPU @@ -724,7 +724,7 @@ int cpu65_step ( && CPU65.op_cycles != 1 && !CPU65.cpu_inhibit_interrupts #endif #ifdef MEGA65 - && !in_hypervisor + && !in_hypervisor && CPU65.prefix == PREFIX_NOTHING #endif )) { #ifdef DEBUG_CPU From f24478a86aba4b80eff325647244dac8b893b821 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:00:55 +0200 Subject: [PATCH 11/35] C65: UI update from branch 'merger' --- targets/c65/ui.c | 16 ++++++++++++++-- targets/ep128/.gitignore | 1 - 2 files changed, 14 insertions(+), 3 deletions(-) delete mode 100644 targets/ep128/.gitignore diff --git a/targets/c65/ui.c b/targets/c65/ui.c index eb7824f9..3e489863 100644 --- a/targets/c65/ui.c +++ b/targets/c65/ui.c @@ -56,6 +56,12 @@ static void ui_attach_d81_by_browsing ( int drive ) static void ui_attach_d81_by_browsing_8 ( void ) { ui_attach_d81_by_browsing(0); } static void ui_attach_d81_by_browsing_9 ( void ) { ui_attach_d81_by_browsing(1); } +static void ui_cb_detach_d81 ( const struct menu_st *m, int *query ) +{ + XEMUGUI_RETURN_CHECKED_ON_QUERY(query, 0); + d81access_close(VOIDPTR_TO_INT(m->user_data)); +} + static void ui_run_prg_by_browsing ( void ) { char fnbuf[PATH_MAX + 1]; @@ -291,13 +297,19 @@ static const struct menu_st menu_rom[] = { { "Load Xemu default ROM", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_load_rom_default }, { NULL } }; +static const struct menu_st menu_drives[] = { + { "Attach D81 to drive 8", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_attach_d81_by_browsing_8 }, + { "Detach D81 from drive 8", XEMUGUI_MENUID_CALLABLE, ui_cb_detach_d81, (void*)0 }, + { "Attach D81 to drive 9", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_attach_d81_by_browsing_9 }, + { "Detach D81 from drive 9", XEMUGUI_MENUID_CALLABLE, ui_cb_detach_d81, (void*)1 }, + { NULL } +}; static const struct menu_st menu_main[] = { { "Display", XEMUGUI_MENUID_SUBMENU, NULL, menu_display }, { "Reset", XEMUGUI_MENUID_SUBMENU, NULL, menu_reset }, { "Debug", XEMUGUI_MENUID_SUBMENU, NULL, menu_debug }, { "ROM", XEMUGUI_MENUID_SUBMENU, NULL, menu_rom }, - { "Attach D81 to drive 8", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_attach_d81_by_browsing_8 }, - { "Attach D81 to drive 9", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_attach_d81_by_browsing_9 }, + { "Drives / D81 images", XEMUGUI_MENUID_SUBMENU, NULL, menu_drives }, { "Run PRG directly", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_run_prg_by_browsing }, #ifdef XEMU_ARCH_WIN { "System console", XEMUGUI_MENUID_CALLABLE | diff --git a/targets/ep128/.gitignore b/targets/ep128/.gitignore deleted file mode 100644 index d5a70866..00000000 --- a/targets/ep128/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/screenshot-*.png From b2a6d4b0fb30b968684eeac7a45ff4f8c92076d2 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Sun, 23 May 2021 20:18:17 +0200 Subject: [PATCH 12/35] 1st try to fix palette precision #259 --- targets/mega65/vic4_palette.c | 17 ++++++++++------- targets/mega65/vic4_palette.h | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/targets/mega65/vic4_palette.c b/targets/mega65/vic4_palette.c index 68ef9aa6..bad05b6c 100644 --- a/targets/mega65/vic4_palette.c +++ b/targets/mega65/vic4_palette.c @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2020 LGB (Gábor Lénárt) + Copyright (C)2016-2021 LGB (Gábor Lénárt) MEGA65 palette handling for VIC-IV with compatibility for C65 style VIC-III palette. @@ -103,9 +103,9 @@ void vic4_init_palette ( void ) 11, 11, 11 // light grey }; for (int i = 0; i < NO_OF_PALETTE_REGS; i++) { - vic_palette_bytes_red[i] = (def_pal[(i & 0xF) * 3 + 0] * 17) & 0xEF; - vic_palette_bytes_green[i] = def_pal[(i & 0xF) * 3 + 1] * 17; - vic_palette_bytes_blue[i] = def_pal[(i & 0xF) * 3 + 2] * 17; + vic_palette_bytes_red[i] = (def_pal[(i & 0xF) * 3 + 0] * 16) & 0xEF; + vic_palette_bytes_green[i] = def_pal[(i & 0xF) * 3 + 1] * 16; + vic_palette_bytes_blue[i] = def_pal[(i & 0xF) * 3 + 2] * 16; } #else for (int i = 0; i < NO_OF_PALETTE_REGS; i++) { @@ -158,17 +158,20 @@ void vic4_write_palette_reg_blue ( unsigned int num, Uint8 data ) void vic3_write_palette_reg_red ( unsigned int num, Uint8 data ) { - vic4_write_palette_reg_red (num, (((data & 0xF) * 17) & 0xEF) | (data & 0x10)); + //vic4_write_palette_reg_red (num, (((data & 0xF) * 17) & 0xEF) | (data & 0x10)); + vic4_write_palette_reg_red(num, data & 0x1F); } void vic3_write_palette_reg_green ( unsigned int num, Uint8 data ) { - vic4_write_palette_reg_green(num, (data & 0xF) * 17); + //vic4_write_palette_reg_green(num, (data & 0xF) * 17); + vic4_write_palette_reg_green(num, data & 0xF); } void vic3_write_palette_reg_blue ( unsigned int num, Uint8 data ) { - vic4_write_palette_reg_blue (num, (data & 0xF) * 17); + //vic4_write_palette_reg_blue (num, (data & 0xF) * 17); + vic4_write_palette_reg_blue (num, data & 0xF); } Uint8 vic4_read_palette_reg_red ( unsigned int num ) diff --git a/targets/mega65/vic4_palette.h b/targets/mega65/vic4_palette.h index c786bf29..c6116abb 100644 --- a/targets/mega65/vic4_palette.h +++ b/targets/mega65/vic4_palette.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016-2020 LGB (Gábor Lénárt) + Copyright (C)2016-2021 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From 539af733cece9f8d1a929c73f65719085cf90fc1 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Fri, 3 Sep 2021 22:36:41 +0200 Subject: [PATCH 13/35] M65: dma rev after reset fix, thx Gurce --- targets/mega65/dma65.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/targets/mega65/dma65.c b/targets/mega65/dma65.c index 26724184..549a4a59 100644 --- a/targets/mega65/dma65.c +++ b/targets/mega65/dma65.c @@ -664,6 +664,7 @@ void dma_init_set_rev ( unsigned int revision, Uint8 *rom_ver_signature ) WARNING_WINDOW("DMA revision is forced to be %d, while ROM version (%d)\nsuggested revision is %d. Using the forced revision %d.\nWarning, this may cause incorrect behaviour!", dma_chip_revision, rom_date, rom_suggested_dma_revision, dma_chip_revision); DEBUGPRINT("DMA: setting chip revision to #%d based on configuration/command line request (forced). Suggested revision by ROM date: #%d" NL, dma_chip_initial_revision, rom_suggested_dma_revision); } + dma_registers[3] = dma_chip_revision; } @@ -709,6 +710,7 @@ void dma_reset ( void ) dma_registers[0x0B] = 1; // fixpoint math target step integer part (1), fractional (reg#A) is already zero by memset() above dma_chip_revision_override = -1; dma_transparency = 0x100; // disable transparency by default + dma_registers[3] = dma_chip_revision; } From af511b488d754c7a6b5c21984e512acc5e92942f Mon Sep 17 00:00:00 2001 From: lgblgblgb Date: Thu, 16 Sep 2021 10:49:08 +0200 Subject: [PATCH 14/35] MEGA65: detect OpenROMs as well --- targets/mega65/dma65.c | 51 +++++++++++++++++++++++++----------------- targets/mega65/dma65.h | 5 +++-- targets/mega65/ui.c | 11 +++++++-- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/targets/mega65/dma65.c b/targets/mega65/dma65.c index 549a4a59..fce6a4d4 100644 --- a/targets/mega65/dma65.c +++ b/targets/mega65/dma65.c @@ -57,6 +57,7 @@ int dma_chip_revision_is_dynamic; // allowed to change DMA chip revision (norm int dma_chip_revision_override; int dma_chip_initial_revision; int rom_date = 0; +int rom_is_openroms = 0; // Hacky stuff: // low byte: the transparent byte value // bit 8: zero = transprent mode is used, 1 = no DMA transparency is in used @@ -617,41 +618,49 @@ int dma_update_multi_steps ( int do_for_cycles ) } -void detect_rom_date ( Uint8 *p ) +void detect_rom_date ( const Uint8 *rom ) { - if (p == NULL) { + if (!rom) { DEBUGPRINT("ROM: version check is disabled (NULL pointer), previous version info: %d" NL, rom_date); - } else if (p[0] == 0x56) { // 'V' - rom_date = 0; - for (int a = 0; a < 6; a++) { - p++; - if (*p >= '0' && *p <= '9') - rom_date = rom_date * 10 + *p - '0'; - else { - rom_date = -1; - DEBUGPRINT("ROM: version check failed (num-numberic character)" NL); - return; - } - } - DEBUGPRINT("ROM: version check succeeded, detected version: %d" NL, rom_date); + return; + } + rom_is_openroms = 0; + if (rom[0x16] == 0x56) { // 'V' at ofs $16 for closed ROMs + rom += 0x16; + } else if (rom[0x10] == 0x4F) { // 'O' at ofs $10 for open ROMs + rom += 0x10; + rom_is_openroms = 1; } else { - DEBUGPRINT("ROM: version check failed (no leading 'V')" NL); + DEBUGPRINT("ROM: version check failed (no leading 'V' or 'O' at ROM ofs $10/$16)" NL); rom_date = -1; + return; + } + rom_date = 0; + for (int a = 0; a < 6; a++) { + rom++; + if (*rom >= '0' && *rom <= '9') + rom_date = rom_date * 10 + *rom - '0'; + else { + rom_date = -1; + DEBUGPRINT("ROM: version check failed (num-numberic character)" NL); + return; + } } + DEBUGPRINT("ROM: version check succeeded, detected version: %d (%s)" NL, rom_date, rom_is_openroms ? "Open-ROMs" : "Closed-ROMs"); } -void dma_init_set_rev ( unsigned int revision, Uint8 *rom_ver_signature ) +void dma_init_set_rev ( unsigned int revision, const Uint8 *rom ) { - detect_rom_date(rom_ver_signature); - int rom_suggested_dma_revision = (rom_date < 900000 || rom_date > 910522); + detect_rom_date(rom); + const int rom_suggested_dma_revision = (rom_date < 900000 || rom_date > 910522 || rom_is_openroms); DEBUGPRINT("ROM: version check suggests DMA revision %d" NL, rom_suggested_dma_revision); revision &= 0xFF; if (revision > 2) { FATAL("Unknown DMA revision value tried to be set (%d)!", revision); } else if (revision == 2) { - if (!rom_ver_signature) - FATAL("dma_ini_set_rev(): revision == 2 (auto-detect) but rom_ver_signature == NULL (cannot auto-detect)"); + if (!rom) + FATAL("dma_ini_set_rev(): revision == 2 (auto-detect) but rom == NULL (cannot auto-detect)"); if (rom_date <= 0) WARNING_WINDOW("ROM version cannot be detected, and DMA revision auto-detection was requested.\nDefaulting to revision %d.\nWarning, this may cause incorrect behaviour!", rom_suggested_dma_revision); dma_chip_revision = rom_suggested_dma_revision; diff --git a/targets/mega65/dma65.h b/targets/mega65/dma65.h index beaf9509..fde46c39 100644 --- a/targets/mega65/dma65.h +++ b/targets/mega65/dma65.h @@ -31,19 +31,20 @@ extern Uint8 dma_status; extern Uint8 dma_registers[16]; extern int dma_chip_revision; extern int rom_date; +extern int rom_is_openroms; /* Functions: */ extern void dma_write_reg ( int addr, Uint8 data ); extern Uint8 dma_read_reg ( int reg ); extern void dma_init ( unsigned int revision ); -extern void dma_init_set_rev ( unsigned int revision, Uint8 *rom_ver_signature ); +extern void dma_init_set_rev ( unsigned int revision, const Uint8 *rom ); extern void dma_reset ( void ); extern int dma_update ( void ); extern int dma_update_multi_steps ( int do_for_cycles ); extern int dma_is_in_use ( void ); -extern void detect_rom_date ( Uint8 *p ); +extern void detect_rom_date ( const Uint8 *rom ); /* Things should be provided by the emulator: */ diff --git a/targets/mega65/ui.c b/targets/mega65/ui.c index c5861e22..debb9adb 100644 --- a/targets/mega65/ui.c +++ b/targets/mega65/ui.c @@ -331,9 +331,16 @@ static void ui_emu_info ( void ) xemu_get_timing_stat_string(td_stat_str, sizeof td_stat_str); char uname_str[100]; xemu_get_uname_string(uname_str, sizeof uname_str); + const char *rom_class; + if (rom_is_openroms && rom_date > 0) + rom_class = "Open-ROMs"; + else if (!rom_is_openroms && rom_date > 0) + rom_class = "Closed-ROMs"; + else + rom_class = "UNKNOWN"; INFO_WINDOW( "DMA chip current revision: %d (F018 rev-%s)\n" - "ROM version detected: %d%s\n" + "ROM version detected: %d%s %s\n" "C64 'CPU' I/O port (low 3 bits): DDR=%d OUT=%d\n" "Current VIC I/O mode: %s, hot registers are %s\n" "\n" @@ -341,7 +348,7 @@ static void ui_emu_info ( void ) "Xemu's host OS: %s" , dma_chip_revision, dma_chip_revision ? "B, new" : "A, old", - rom_date, rom_date > 0 ? "" : " (unknown or bad ROM signature)", + rom_date, rom_date > 0 ? "" : " (unknown or bad ROM signature)", rom_class, memory_get_cpu_io_port(0) & 7, memory_get_cpu_io_port(1) & 7, vic_iomode < 4 ? iomode_names[vic_iomode] : "?INVALID?", (vic_registers[0x5D] & 0x80) ? "enabled" : "disabled", td_stat_str, From 3eb8ba1ab00402795b622c4ca2e661130ef1804d Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:19:41 +0200 Subject: [PATCH 15/35] MEGA65: updates from branch 'merger' --- targets/mega65/input_devices.c | 53 +++++++++++++ targets/mega65/input_devices.h | 5 ++ targets/mega65/io_mapper.c | 28 ++++--- targets/mega65/m65_snapshot.c | 5 +- targets/mega65/m65_snapshot.h | 8 +- targets/mega65/ui.c | 140 ++++++++++++++++++++++++++------- targets/mega65/xemu-target.h | 10 +-- 7 files changed, 193 insertions(+), 56 deletions(-) diff --git a/targets/mega65/input_devices.c b/targets/mega65/input_devices.c index 3eb577b3..f75164e1 100644 --- a/targets/mega65/input_devices.c +++ b/targets/mega65/input_devices.c @@ -78,6 +78,7 @@ static struct { } hwa_kbd; static int restore_is_held = 0; +static Uint8 virtkey_state[3] = { 0xFF, 0xFF, 0xFF }; void hwa_kbd_fake_key ( Uint8 k ) @@ -182,6 +183,13 @@ void clear_emu_events ( void ) hwa_kbd.modifiers = 0; hwa_kbd.next = 0; hwa_kbd.last = 0; + for (int a = 0; a < 3; a++) { + if (virtkey_state[0] != 0xFF) { + hid_sdl_synth_key_event(virtkey_state[a], 0); + virtkey_state[a] = 0xFF; + } + + } } @@ -192,6 +200,25 @@ void input_toggle_joy_emu ( void ) } +void virtkey ( Uint8 rno, Uint8 scancode ) +{ + // Convert scancode to "Xemu kind of scan code" ... + if (scancode >= MAT2ASC_TAB_SIZE) + scancode = 0xFF; + else if (scancode < 64) + scancode = ((scancode & (32 + 16 + 8)) << 1) | (scancode & 7); + else + scancode += C65_KEYBOARD_EXTRA_POS - 64; + if (virtkey_state[rno] == scancode) + return; + if (virtkey_state[rno] != 0xFF) + hid_sdl_synth_key_event(virtkey_state[rno], 0); + virtkey_state[rno] = scancode; + if (scancode != 0xFF) + hid_sdl_synth_key_event(scancode, 1); +} + + Uint8 cia1_in_b ( void ) { #ifdef FAKE_TYPING_SUPPORT @@ -223,12 +250,16 @@ void kbd_trigger_restore_trap ( void ) restore_is_held++; if (restore_is_held >= 20) { restore_is_held = 0; +#ifdef FREEZER_WORKS if (!in_hypervisor) { DEBUGPRINT("KBD: RESTORE trap has been triggered." NL); KBD_RELEASE_KEY(RESTORE_KEY_POS); hypervisor_enter(TRAP_RESTORE); } else DEBUGPRINT("KBD: *IGNORING* RESTORE trap trigger, already in hypervisor mode!" NL); +#else + WARNING_WINDOW("Long press of RESTORE would trigger FREEZER.\nHowever FREEZER is not yet implemented in Xemu :-("); +#endif } } } @@ -324,3 +355,25 @@ int emu_callback_key ( int pos, SDL_Scancode key, int pressed, int handled ) } return 0; } + + +Uint8 get_mouse_x_via_sid ( void ) +{ + if (!is_mouse_grab()) + return 0xFF; + static int mouse_x = 0; + mouse_x = (mouse_x + hid_read_mouse_rel_x(-31, 31)) & 63; + DEBUG("MOUSE-X: reading X as %d" NL, mouse_x << 1); + return mouse_x << 1; +} + + +Uint8 get_mouse_y_via_sid ( void ) +{ + if (!is_mouse_grab()) + return 0xFF; + static int mouse_y = 0; + mouse_y = (mouse_y - hid_read_mouse_rel_y(-31, 31)) & 63; + DEBUG("MOUSE-Y: reading Y as %d" NL, mouse_y << 1); + return mouse_y << 1; +} diff --git a/targets/mega65/input_devices.h b/targets/mega65/input_devices.h index b6706263..4079180b 100644 --- a/targets/mega65/input_devices.h +++ b/targets/mega65/input_devices.h @@ -31,8 +31,13 @@ extern Uint8 hwa_kbd_get_modifiers ( void ); extern void hwa_kbd_move_next ( void ); extern void hwa_kbd_fake_key ( Uint8 k ); +extern void virtkey ( Uint8 rno, Uint8 scancode ); + extern Uint8 kbd_directscan_query ( Uint8 row ); extern void kbd_trigger_restore_trap ( void ); +extern Uint8 get_mouse_x_via_sid ( void ); +extern Uint8 get_mouse_y_via_sid ( void ); + #endif diff --git a/targets/mega65/io_mapper.c b/targets/mega65/io_mapper.c index 20500660..857f47a0 100644 --- a/targets/mega65/io_mapper.c +++ b/targets/mega65/io_mapper.c @@ -22,8 +22,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "memory_mapper.h" #include "xemu/f011_core.h" #include "dma65.h" -#include "xemu/emutools_hid.h" -//#include "xemu/cpu65.h" #include "vic4.h" #include "vic4_palette.h" #include "sdcard.h" @@ -38,7 +36,6 @@ int fpga_switches = 0; // State of FPGA board switches (bits 0 - 15), set sw Uint8 D6XX_registers[0x100]; // mega65 specific D6XX range, excluding the UART part (not used here!) Uint8 D7XX[0x100]; // FIXME: hack for future M65 stuffs like ALU! FIXME: no snapshot on these! struct Cia6526 cia1, cia2; // CIA emulation structures for the two CIAs -static int mouse_x = 0, mouse_y = 0; // for our primitive C1351 mouse emulation int cpu_mega65_opcodes = 0; // used by the CPU emu as well! static int bigmult_valid_result = 0; int port_d607 = 0xFF; // ugly hack to be able to read extra char row of C65 keyboard @@ -144,15 +141,9 @@ Uint8 io_read ( unsigned int addr ) case 0x15: // $D500-$D5FF ~ C65 I/O mode case 0x34: // $D400-$D4FF ~ M65 I/O mode case 0x35: // $D500-$D5FF ~ M65 I/O mode - if (is_mouse_grab()) { // Rudimentary C1351 mouse emulation (on SID#1) ... - switch (addr & 0x5F) { - case 0x19: - mouse_x = (mouse_x + hid_read_mouse_rel_x(-31, 31)) & 63; - return mouse_x << 1; - case 0x1A: - mouse_y = (mouse_y - hid_read_mouse_rel_y(-31, 31)) & 63; - return mouse_y << 1; - } + switch (addr & 0x5F) { + case 0x19: return get_mouse_x_via_sid(); + case 0x1A: return get_mouse_y_via_sid(); } return 0xFF; case 0x16: // $D600-$D6FF ~ C65 I/O mode @@ -193,6 +184,9 @@ Uint8 io_read ( unsigned int addr ) case 0x0F: // D60F bit 5, real hardware (1), emulation (0), other bits are not emulated yet by Xemu, so I give simply zero return 0; + case 0x1B: + // D61B amiga / 1531 mouse auto-detect. FIXME XXX what value we should return at this point? :-O + return 0xFF; case 0x32: // D632-D635: FPGA firmware ID case 0x33: case 0x34: @@ -383,7 +377,10 @@ void io_write ( unsigned int addr, Uint8 data ) case 0x15: // $D500-$D5FF ~ C65 I/O mode case 0x34: // $D400-$D4FF ~ M65 I/O mode case 0x35: // $D500-$D5FF ~ M65 I/O mode - audio65_sid_write(addr, data); // We need full addr, audio65_sid_write will decide the SID instance from that! + //sid_write_reg(addr & 0x40 ? &sid[1] : &sid[0], addr & 31, data); + //DEBUGPRINT("SID #%d reg#%02X data=%02X" NL, (addr >> 5) & 3, addr & 0x1F, data); + //sid_write_reg(&sid[(addr >> 5) & 3], addr & 0x1F, data); + audio65_sid_write(addr, data); // We need full addr, audio65_sid_write will decide the SID instance from that! return; case 0x16: // $D600-$D6FF ~ C65 I/O mode if ((addr & 0xFF) == 0x07) { @@ -419,6 +416,11 @@ void io_write ( unsigned int addr, Uint8 data ) case 0x10: // ASCII kbd last press value to zero whatever the written data would be hwa_kbd_move_next(); return; + case 0x15: + case 0x16: + case 0x17: + virtkey(addr - 0x15, data & 0x7F); + return; case 0x7C: // hypervisor serial monitor port hypervisor_serial_monitor_push_char(data); return; diff --git a/targets/mega65/m65_snapshot.c b/targets/mega65/m65_snapshot.c index b034cd53..6fb260cb 100644 --- a/targets/mega65/m65_snapshot.c +++ b/targets/mega65/m65_snapshot.c @@ -34,6 +34,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "m65_snapshot.h" #include "memory_mapper.h" #include "audio65.h" +#include "io_mapper.h" #include #define M65_MEMORY_BLOCK_VERSION 1 @@ -82,8 +83,8 @@ const struct xemu_snapshot_definition_st m65_snapshot_definition[] = { { "M65", NULL, m65emu_snapshot_load_state, m65emu_snapshot_save_state }, { "SID#1", &sid[0], sid_snapshot_load_state, sid_snapshot_save_state }, { "SID#2", &sid[1], sid_snapshot_load_state, sid_snapshot_save_state }, - { "SID#3", &sid[2], sid_snapshot_load_state, sid_snapshot_save_state }, - { "SID#4", &sid[3], sid_snapshot_load_state, sid_snapshot_save_state }, + { "SID#3", &sid[3], sid_snapshot_load_state, sid_snapshot_save_state }, + { "SID#4", &sid[4], sid_snapshot_load_state, sid_snapshot_save_state }, { "DMAgic", NULL, dma_snapshot_load_state, dma_snapshot_save_state }, { "SDcard", NULL, sdcard_snapshot_load_state, sdcard_snapshot_save_state }, { "FDC-F011", NULL, fdc_snapshot_load_state, fdc_snapshot_save_state }, diff --git a/targets/mega65/m65_snapshot.h b/targets/mega65/m65_snapshot.h index e53cf28f..0c5910bb 100644 --- a/targets/mega65/m65_snapshot.h +++ b/targets/mega65/m65_snapshot.h @@ -1,6 +1,6 @@ /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu - Copyright (C)2016,2017 LGB (Gábor Lénárt) + Copyright (C)2016,2017,2021 LGB (Gábor Lénárt) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,15 +16,13 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef __XEMU_M65_SNAPSHOT_H_INCLUDED -#define __XEMU_M65_SNAPSHOT_H_INCLUDED +#ifndef XEMU_MEGA65_SNAPSHOT_H_INCLUDED +#define XEMU_MEGA65_SNAPSHOT_H_INCLUDED #ifdef XEMU_SNAPSHOT_SUPPORT #include "xemu/emutools_snapshot.h" // From other modules ... -extern struct Cia6526 cia1, cia2; -extern struct SidEmulation sid1, sid2;; extern int m65emu_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block ); extern int m65emu_snapshot_save_state ( const struct xemu_snapshot_definition_st *def ); extern int m65emu_snapshot_loading_finalize ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block ); diff --git a/targets/mega65/ui.c b/targets/mega65/ui.c index debb9adb..6b09fb1d 100644 --- a/targets/mega65/ui.c +++ b/targets/mega65/ui.c @@ -103,7 +103,7 @@ static void ui_attach_d81 ( const struct menu_st *m, int *query ) } else { /*int ret =*/ sdcard_hack_mount_drive_9_now(fnbuf); //if (ret) - // DEBUGPRINT("SDCARD: D81: couldn't mount external D81 image" NL); + // DEBUGPRINT("SDCARD: D81: couldn't mount external D81 image" NL); } } else { DEBUGPRINT("UI: file selection for D81 mount was cancelled." NL); @@ -114,7 +114,14 @@ static void ui_attach_d81 ( const struct menu_st *m, int *query ) static void ui_detach_d81 ( const struct menu_st *m, int *query ) { XEMUGUI_RETURN_CHECKED_ON_QUERY(query, 0); - forget_external_d81(); + const int drive = VOIDPTR_TO_INT(m->user_data); + if (drive == 0) { + forget_external_d81(); + } else { + // Again ugly hack ... + // to handle drive-0 and 1 (well, 8 and 9) in comepletely different ways + d81access_close(1); + } } @@ -311,6 +318,20 @@ static void ui_dump_memory ( void ) } } +static void ui_dump_colram ( void ) +{ + char fnbuf[PATH_MAX + 1]; + if (!xemugui_file_selector( + XEMUGUI_FSEL_SAVE | XEMUGUI_FSEL_FLAG_STORE_DIR, + "Dump colour memory content into file", + last_used_dump_directory, + fnbuf, + sizeof fnbuf + )) { + xemu_save_file(fnbuf, colour_ram, sizeof colour_ram, "Cannot dump colour RAM content into file"); + } +} + static void ui_dump_hyperram ( void ) { char fnbuf[PATH_MAX + 1]; @@ -331,26 +352,19 @@ static void ui_emu_info ( void ) xemu_get_timing_stat_string(td_stat_str, sizeof td_stat_str); char uname_str[100]; xemu_get_uname_string(uname_str, sizeof uname_str); - const char *rom_class; - if (rom_is_openroms && rom_date > 0) - rom_class = "Open-ROMs"; - else if (!rom_is_openroms && rom_date > 0) - rom_class = "Closed-ROMs"; - else - rom_class = "UNKNOWN"; INFO_WINDOW( "DMA chip current revision: %d (F018 rev-%s)\n" "ROM version detected: %d%s %s\n" "C64 'CPU' I/O port (low 3 bits): DDR=%d OUT=%d\n" - "Current VIC I/O mode: %s, hot registers are %s\n" + "Current VIC and I/O mode: %s %s, hot registers are %s\n" "\n" "Xemu host CPU usage so far: %s\n" "Xemu's host OS: %s" , dma_chip_revision, dma_chip_revision ? "B, new" : "A, old", - rom_date, rom_date > 0 ? "" : " (unknown or bad ROM signature)", rom_class, + rom_date, rom_date > 0 ? "" : " (unknown or bad ROM signature)", rom_date > 0 ? (rom_is_openroms ? "Open-ROMs" : "Closed-ROMs") : "UNKNOWN", memory_get_cpu_io_port(0) & 7, memory_get_cpu_io_port(1) & 7, - vic_iomode < 4 ? iomode_names[vic_iomode] : "?INVALID?", (vic_registers[0x5D] & 0x80) ? "enabled" : "disabled", + vic_iomode < 4 ? iomode_names[vic_iomode] : "?INVALID?", videostd_name, (vic_registers[0x5D] & 0x80) ? "enabled" : "disabled", td_stat_str, uname_str ); @@ -420,6 +434,27 @@ static void ui_cb_audio_volume ( const struct menu_st *m, int *query ) } +static void ui_video_standard ( const struct menu_st *m, int *query ) +{ + XEMUGUI_RETURN_CHECKED_ON_QUERY(query, VOIDPTR_TO_INT(m->user_data) == videostd_id); + Uint8 reg = vic_read_reg(0x6F); + if (m->user_data) + reg |= 0x80; + else + reg &= 0x7F; + configdb.force_videostd = -1; // turn off possible CLI/config dictated force video mode, otherwise it won't work to change video standard ... + vic_write_reg(0x6F, reg); // write VIC-IV register to trigger the stuff +} + + +static void ui_cb_fullborders ( const struct menu_st *m, int *query ) +{ + XEMUGUI_RETURN_CHECKED_ON_QUERY(query, configdb.fullborders); + configdb.fullborders = !configdb.fullborders; + vic_readjust_sdl_viewport = 1; // To force readjust viewport on the next frame open. +} + + // FIXME: should be renamed with better name ;) // FIXME: should be moved into the core static void ui_cb_toggle_int_inverted ( const struct menu_st *m, int *query ) @@ -445,19 +480,61 @@ static void ui_cb_sids_enabled ( const struct menu_st *m, int *query ) configdb.sidmask ^= mask; } +static void ui_cb_render_scale_quality ( const struct menu_st *m, int *query ) +{ + XEMUGUI_RETURN_CHECKED_ON_QUERY(query, VOIDPTR_TO_INT(m->user_data) == configdb.sdlrenderquality); + char req_str[] = { VOIDPTR_TO_INT(m->user_data) + '0', 0 }; + SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY, req_str, SDL_HINT_OVERRIDE); + configdb.sdlrenderquality = VOIDPTR_TO_INT(m->user_data); + register_new_texture_creation = 1; +} + /**** MENU SYSTEM ****/ -static const struct menu_st menu_display[] = { +static const struct menu_st menu_video_standard[] = { + { "PAL", XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, ui_video_standard, (void*)0 }, + { "NTSC", XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, ui_video_standard, (void*)1 }, + { NULL } +}; +static const struct menu_st menu_window_size[] = { + // TODO: unfinished work, see: https://github.com/lgblgblgb/xemu/issues/246 +#if 0 + { "Fullscreen", XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, xemugui_cb_windowsize, (void*)0 }, + { "Window - 100%", XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, xemugui_cb_windowsize, (void*)1 }, + { "Window - 200%", XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, xemugui_cb_windowsize, (void*)2 }, +#endif { "Fullscreen", XEMUGUI_MENUID_CALLABLE, xemugui_cb_windowsize, (void*)0 }, { "Window - 100%", XEMUGUI_MENUID_CALLABLE, xemugui_cb_windowsize, (void*)1 }, - { "Window - 200%", XEMUGUI_MENUID_CALLABLE | - XEMUGUI_MENUFLAG_SEPARATOR, xemugui_cb_windowsize, (void*)2 }, + { "Window - 200%", XEMUGUI_MENUID_CALLABLE, xemugui_cb_windowsize, (void*)2 }, + { NULL } +}; +static const struct menu_st menu_render_scale_quality[] = { + { "Nearest pixel sampling", XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_render_scale_quality, (void*)0 }, + { "Linear filtering", XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_render_scale_quality, (void*)1 }, + { "Anisotropic (Direct3D only)",XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_render_scale_quality, (void*)2 }, + { NULL } +}; +static const struct menu_st menu_display[] = { + { "Render scale quality", XEMUGUI_MENUID_SUBMENU, NULL, menu_render_scale_quality }, + { "Window size / fullscreen", XEMUGUI_MENUID_SUBMENU, NULL, menu_window_size }, + { "Video standard", XEMUGUI_MENUID_SUBMENU, NULL, menu_video_standard }, + { "Show full borders", XEMUGUI_MENUID_CALLABLE | + XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_fullborders, NULL }, { "Show drive LED", XEMUGUI_MENUID_CALLABLE | - XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_toggle_int, (void*)&configdb.show_drive_led }, + XEMUGUI_MENUFLAG_QUERYBACK | + XEMUGUI_MENUFLAG_SEPARATOR, ui_cb_toggle_int, (void*)&configdb.show_drive_led }, #ifdef XEMU_FILES_SCREENSHOT_SUPPORT - { "Screenshot", XEMUGUI_MENUID_CALLABLE, xemugui_cb_set_integer_to_one, ®ister_screenshot_request }, + { "Screenshot", XEMUGUI_MENUID_CALLABLE, xemugui_cb_set_integer_to_one, ®istered_screenshot_request }, #endif { "Screen to OS paste buffer", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_put_screen_text_into_paste_buffer }, { "OS paste buffer to screen", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_put_paste_buffer_into_screen_text }, @@ -476,11 +553,14 @@ static const struct menu_st menu_reset[] = { { NULL } }; static const struct menu_st menu_inputdevices[] = { - { "Swap emulated joystick port",XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, input_toggle_joy_emu }, { "Enable mouse grab + emu", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, xemugui_cb_set_mouse_grab, NULL }, { "Use OSD key debugger", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, xemugui_cb_osd_key_debugger, NULL }, + { "Swap emulated joystick port",XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, input_toggle_joy_emu }, +#if 0 + { "Devices as joy port 2 (vs 1)", XEMUGUI_MENUID_SUBMENU, NULL, menu_joy_devices }, +#endif { NULL } }; static const struct menu_st menu_debug[] = { @@ -489,7 +569,8 @@ static const struct menu_st menu_debug[] = { XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_start_umon, NULL }, #endif - { "Dump main memory info file", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_dump_memory }, + { "Dump main RAM info file", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_dump_memory }, + { "Dump colour RAM into file", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_dump_colram }, { "Dump hyperRAM into file", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_dump_hyperram }, { "Emulation state info", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_emu_info }, { NULL } @@ -508,30 +589,31 @@ static const struct menu_st menu_d81[] = { { "Attach user D81 on drv-8", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_attach_d81, (void*)0 }, { "Use internal D81 on drv-8", XEMUGUI_MENUID_CALLABLE | - XEMUGUI_MENUFLAG_QUERYBACK, ui_detach_d81, NULL }, + XEMUGUI_MENUFLAG_QUERYBACK, ui_detach_d81, (void*)0 }, { "Attach user D81 on drv-9", XEMUGUI_MENUID_CALLABLE, ui_attach_d81, (void*)1 }, + { "Detach user D81 on drv-9", XEMUGUI_MENUID_CALLABLE, ui_detach_d81, (void*)1 }, { NULL } }; static const struct menu_st menu_audio_stereo[] = { { "Hard stereo separation", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_mono_downmix, (void*) 100 }, - { "Mono downmix 80%", XEMUGUI_MENUID_CALLABLE | + { "Stereo separation 80%", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_mono_downmix, (void*) 80 }, - { "Mono downmix 60%", XEMUGUI_MENUID_CALLABLE | + { "Stereo separation 60%", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_mono_downmix, (void*) 60 }, - { "Mono downmix 40%", XEMUGUI_MENUID_CALLABLE | + { "Stereo separation 40%", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_mono_downmix, (void*) 40 }, - { "Mono downmix 20%", XEMUGUI_MENUID_CALLABLE | + { "Stereo separation 20%", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_mono_downmix, (void*) 20 }, - { "Mono downmix", XEMUGUI_MENUID_CALLABLE | + { "Full mono downmix (0%)", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_mono_downmix, (void*) 0 }, - { "Mono downmix -20%", XEMUGUI_MENUID_CALLABLE | + { "Stereo separation -20%", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_mono_downmix, (void*) -20 }, - { "Mono downmix -40%", XEMUGUI_MENUID_CALLABLE | + { "Stereo separation -40%", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_mono_downmix, (void*) -40 }, - { "Mono downmix -60%", XEMUGUI_MENUID_CALLABLE | + { "Stereo separation -60%", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_mono_downmix, (void*) -60 }, - { "Mono downmix -80%", XEMUGUI_MENUID_CALLABLE | + { "Stereo separation -80%", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_mono_downmix, (void*) -80 }, { "Hard stereo - reserved", XEMUGUI_MENUID_CALLABLE | XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_mono_downmix, (void*)-100 }, diff --git a/targets/mega65/xemu-target.h b/targets/mega65/xemu-target.h index 422844c8..f5423f50 100644 --- a/targets/mega65/xemu-target.h +++ b/targets/mega65/xemu-target.h @@ -14,9 +14,9 @@ #define HAVE_XEMU_EXEC_API -#ifdef HAVE_SOCKET_OS_API -//#define HAVE_XEMU_SOCKET_API -//#define HAVE_XEMU_UMON +#ifdef XEMU_HAS_SOCKET_API +#define HAS_UARTMON_SUPPORT +#define HAVE_XEMU_UMON #endif #define HAVE_XEMU_INSTALLER @@ -70,10 +70,6 @@ #define XEMU_FILES_SCREENSHOT_SUPPORT #endif -#ifdef XEMU_HAS_SOCKET_API -#define HAS_UARTMON_SUPPORT -#endif - #define CONFIG_EMSCRIPTEN_OK #define XEMU_CONFIGDB_SUPPORT From 34b136760d70aa85b6587b2904d6fe0218727474 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:25:45 +0200 Subject: [PATCH 16/35] MEGA65: my VIC-IV updates from branch 'merger' --- targets/mega65/vic4.c | 172 +++++++++++++++++++++++++----------------- targets/mega65/vic4.h | 84 +++++++++++---------- 2 files changed, 147 insertions(+), 109 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index fc185539..fccbabe0 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -18,26 +18,26 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "xemu/emutools.h" -#include "xemu/emutools_config.h" #include "mega65.h" #include "xemu/cpu65.h" #include "vic4.h" #include "vic4_palette.h" #include "memory_mapper.h" #include "hypervisor.h" -//#include +#include "configdb.h" +#include "xemu/f011_core.h" +#include "xemu/emutools_files.h" -static const char *iomode_names[4] = { "VIC2", "VIC3", "BAD!", "VIC4" }; + +const char *iomode_names[4] = { "VIC2", "VIC3", "BAD!", "VIC4" }; // (SDL) target texture rendering pointers static Uint32 *current_pixel; // current_pixel pointer to the rendering target (one current_pixel: 32 bit) static Uint32 *pixel_end, *pixel_start; // points to the end and start of the buffer static Uint32 *pixel_raster_start; // first pixel of current raster -Uint8 vic_registers[0x80]; // VIC-3 registers. It seems $47 is the last register. But to allow address the full VIC3 reg I/O space, we use $80 here +Uint8 vic_registers[0x80]; // VIC-4 registers int vic_iomode; // VIC2/VIC3/VIC4 mode int force_fast; // POKE 0,64 and 0,65 trick ... -int scanline; // current scan line number -int cpu_cycles_per_scanline; static int compare_raster; // raster compare (9 bits width) data static int logical_raster = 0; static int interrupt_status; // Interrupt status of VIC @@ -54,24 +54,27 @@ static int char_row = 0, display_row = 0; static Uint8 bg_pixel_state[1024]; // See FOREGROUND_PIXEL and BACKGROUND_PIXEL constants static Uint8* screen_ram_current_ptr = NULL; static Uint8* colour_ram_current_ptr = NULL; -int user_scanlines_setting = 0; -float char_x_step = 0.0; +static float char_x_step = 0.0; static int enable_bg_paint = 1; static int display_row_count = 0; static int max_rasters = PHYSICAL_RASTERS_DEFAULT; static int visible_area_height = SCREEN_HEIGHT_VISIBLE_DEFAULT; static int vicii_first_raster = 7; // Default for NTSC static Uint8 *bitplane_bank_p = main_ram; +static Uint32 red_colour, black_colour; // used by "drive LED" stuff // --- these things are altered by vic4_open_frame_access() ONLY at every fame ONLY based on PAL or NTSC selection Uint8 videostd_id = 0xFF; // 0=PAL, 1=NTSC [give some insane value by default to force the change at the fist frame after starting Xemu] const char *videostd_name = ""; // PAL or NTSC, however initially is not yet set int videostd_frametime = NTSC_FRAME_TIME; // time in microseconds for a frame to produce +float videostd_1mhz_cycles_per_scanline = 32.0; // have *some* value to jumpstart emulation, it will be overriden sooner or later XXX FIXME: why it does not work with zero value when it's overriden anyway?!?! +int videostd_changed = 0; static const char NTSC_STD_NAME[] = "NTSC"; static const char PAL_STD_NAME[] = "PAL"; +int vic_readjust_sdl_viewport = 0; -void vic4_render_char_raster(); -void vic4_render_bitplane_raster(); +void vic4_render_char_raster(void); +void vic4_render_bitplane_raster(void); static void (*vic4_raster_renderer_path)(void) = &vic4_render_char_raster; // VIC-IV Modeline Parameters @@ -157,52 +160,88 @@ static const Uint8 reverse_byte_table[] = { }; - -void vic_init ( void ) +void vic_reset ( void ) { - vic4_init_palette(); - force_fast = 0; - // *** Init VIC4 registers and palette vic_iomode = VIC2_IOMODE; interrupt_status = 0; - scanline = 0; compare_raster = 0; // *** Just a check to try all possible regs (in VIC2,VIC3 and VIC4 modes), it should not panic ... // It may also sets/initializes some internal variables sets by register writes, which would cause a crash on screen rendering without prior setup! - for (int i = 0; i < 0x140; i++) { // $140=the last $40 register for VIC-2 mode, when we have fewer ones + for (int i = 0; i < 0x140; i++) { vic_write_reg(i, 0); (void)vic_read_reg(i); } +} + + +static void vic4_reset_display_counters ( void ) +{ + xcounter = 0; + display_row = 0; + char_row = 0; + ycounter = 0; +} + + +void vic_init ( void ) +{ + // Needed to render "drive LED" feature + red_colour = SDL_MapRGBA(sdl_pix_fmt, 0xFF, 0x00, 0x00, 0xFF); + black_colour = SDL_MapRGBA(sdl_pix_fmt, 0x00, 0x00, 0x00, 0xFF); + // Init VIC4 stuffs + vic4_init_palette(); + force_fast = 0; + vic_reset(); c128_d030_reg = 0xFE; // this may be set to 2MHz in the previous step, so be sure to set to FF here, BUT FIX: bit 0 should be inverted!! machine_set_speed(0); - screen_ram_current_ptr = main_ram + SCREEN_ADDR; colour_ram_current_ptr = colour_ram; - + vic4_reset_display_counters(); DEBUG("VIC4: has been initialized." NL); } -#if 0 -// This function allows to switch between NTSC/PAL on-the-fly (NTSC = 1. PAL = 0) -void vic4_switch_display_mode(int ntsc) +// Pair of vic4_open_frame_access() and the place when screen is updated at SDL level, finally. +// Do NOT call this function from vic4.c! It must be used by the emulator's main loop! +void vic4_close_frame_access ( void ) { - DEBUGPRINT("VIC: switch_display_mode NTSC=%d" NL, ntsc); - xemu_change_display_mode(SCREEN_WIDTH, ntsc ? PHYSICAL_RASTERS_NTSC : PHYSICAL_RASTERS_PAL, // texture sizes - SCREEN_WIDTH, SCREEN_HEIGHT, // logical size (used with keeping aspect ratio by the SDL render stuffs) - SCREEN_WIDTH, SCREEN_HEIGHT, // window size - SCREEN_FORMAT, - USE_LOCKED_TEXTURE - ); - if(!xemucfg_get_bool("fullborders")) - xemu_set_viewport(48, 32, SCREEN_WIDTH - 48, SCREEN_HEIGHT, 1); - vic4_open_frame_access(); -} +#ifdef XEMU_FILES_SCREENSHOT_SUPPORT + // Screenshot + if (XEMU_UNLIKELY(registered_screenshot_request)) { + unsigned int x1, y1, x2, y2; + xemu_get_viewport(&x1, &y1, &x2, &y2); + registered_screenshot_request = 0; + if (!xemu_screenshot_png( + NULL, NULL, + 1, 1, // no ratio/zoom correction is applied + pixel_start + y1 * SCREEN_WIDTH + x1, // pixel pointer corresponding to the top left corner of the viewport + x2 - x1 + 1, // width + y2 - y1 + 1, // height + SCREEN_WIDTH // full width (ie, width of the texture) + )) { + const char *p = strrchr(xemu_screenshot_full_path, DIRSEP_CHR); + if (p) + OSD(-1, -1, "%s", p + 1); + } + } #endif + // Render "drive LED" if it was requested at all + if (configdb.show_drive_led && fdc_get_led_state(16)) { + unsigned int x_origin, y_origin; // top right corner of the viewport + xemu_get_viewport(NULL, &y_origin, &x_origin, NULL); + for (unsigned int y = 0; y < 8; y++) + for (unsigned int x = 0; x < 8; x++) + *(pixel_start + x_origin - 10 + x + (y + 2 + y_origin) * (SCREEN_WIDTH)) = (x > 1 && x < 7 && y > 1 && y < 7) ? red_colour : black_colour; + } + // FINALLY .... + xemu_update_screen(); +} - -void vic4_open_frame_access() +// Must be called before using the texture at all, otherwise crash will happen, or nothing at all. +// Access must be closed with vic4_close_frame_access(). +// Do NOT call this function from vic4.c! It must be used by the emulator's main loop! +void vic4_open_frame_access ( void ) { int tail_sdl; current_pixel = pixel_start = xemu_start_pixel_buffer_access(&tail_sdl); @@ -213,36 +252,41 @@ void vic4_open_frame_access() // Though it can be changed any time, this kind of information really only can be applied // at frame level. Thus we check here, if during the previous frame there was change // and apply the video mode set for our just started new frame. - Uint8 new_mode = !!(vic_registers[0x6F] & 0x80); + Uint8 new_mode = (configdb.force_videostd >= 0) ? configdb.force_videostd : !!(vic_registers[0x6F] & 0x80); if (XEMU_UNLIKELY(new_mode != videostd_id)) { // We have video mode change! videostd_id = new_mode; + // signal that video standard has been changed, it's handled in the main emulation stuff then (reacted with recalculated timing change, and such) + // ... including the task of resetting this signal variable! + videostd_changed = 1; const char *new_name; - float cycles_at_1mhz; // 1MHz CPU cycles equalient of a scanline time if (videostd_id) { // --- NTSC --- new_name = NTSC_STD_NAME; videostd_frametime = NTSC_FRAME_TIME; - cycles_at_1mhz = 1000000.0 / NTSC_LINE_FREQ; + videostd_1mhz_cycles_per_scanline = 1000000.0 / (float)(NTSC_LINE_FREQ); max_rasters = PHYSICAL_RASTERS_NTSC; visible_area_height = SCREEN_HEIGHT_VISIBLE_NTSC; } else { // --- PAL --- new_name = PAL_STD_NAME; videostd_frametime = PAL_FRAME_TIME; - cycles_at_1mhz = 1000000.0 / PAL_LINE_FREQ; + videostd_1mhz_cycles_per_scanline = 1000000.0 / (float)(PAL_LINE_FREQ); max_rasters = PHYSICAL_RASTERS_PAL; visible_area_height = SCREEN_HEIGHT_VISIBLE_PAL; } - DEBUGPRINT("VIC: switching video standard from %s to %s (1MHz line cycle count is %f, frame time is %dusec)" NL, videostd_name, new_name, cycles_at_1mhz, videostd_frametime); + DEBUGPRINT("VIC: switching video standard from %s to %s (1MHz line cycle count is %f, frame time is %dusec, max raster is %d, visible area height is %d)" NL, videostd_name, new_name, videostd_1mhz_cycles_per_scanline, videostd_frametime, max_rasters, visible_area_height); videostd_name = new_name; -#if 0 - // TODO: recalculate cpu_cycles/line "constants" for each CPU speed modes (1, 2, 3.5, ~40 MHz) - cpu_cycles_per_line_c64 = (int)roundf(cycles_at_1mhz); - cpu_cycles_per_line_c128 = (int)roundf(cycles_at_1mhz * 2.0); - cpu_cycles_per_line_c65 = (int)roundf(cycles_at_1mhz * 3.5); - cpu_cycles_per_line_m65 = (int)roundf(cycles_at_1mhz * 40.0); // FIXME: configdb.fastclock for newer Xemu! This is BAD since it can be other than 40!!!! -#endif + vic_readjust_sdl_viewport = 1; + } + // handle this via vic_readjust_sdl_viewport variable (not directly above) so external stuff (like UI) can also + // force to adjust viewport, not just the PAL/NTSC change itself (ie: fullborder/clipped border change) + if (XEMU_UNLIKELY(vic_readjust_sdl_viewport)) { + vic_readjust_sdl_viewport = 0; + if (configdb.fullborders) // XXX FIXME what should be the correct values for full borders and without that?! + xemu_set_viewport(0, 0, SCREEN_WIDTH - 1, max_rasters - 1, XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE); + else + xemu_set_viewport(48, 0, SCREEN_WIDTH - 48, visible_area_height - 1, XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE); } // FIXME: do we need this here? Ie, should this always bound to video mode change (only at frame boundary!) or not ... #if 0 @@ -276,7 +320,7 @@ static void vic4_interrupt_checker ( void ) } -static void vic4_check_raster_interrupt(int nraster) +static void vic4_check_raster_interrupt ( int nraster ) { if (nraster == compare_raster) interrupt_status |= 1; @@ -286,22 +330,13 @@ static void vic4_check_raster_interrupt(int nraster) } -inline static void vic4_calculate_char_x_step() +inline static void vic4_calculate_char_x_step ( void ) { char_x_step = (REG_CHARXSCALE / 120.0f) / (REG_H640 ? 1 : 2); } -static void vic4_reset_display_counters() -{ - xcounter = 0; - display_row = 0; - char_row = 0; - ycounter = 0; -} - - -static void vic4_update_sideborder_dimensions() +static void vic4_update_sideborder_dimensions ( void ) { if (REG_CSEL) { // 40-columns? border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER; @@ -319,7 +354,7 @@ static void vic4_update_sideborder_dimensions() } -static void vic4_interpret_legacy_mode_registers() +static void vic4_interpret_legacy_mode_registers ( void ) { // See https://github.com/MEGA65/mega65-core/blob/257d78aa6a21638cb0120fd34bc0e6ab11adfd7c/src/vhdl/viciv.vhdl#L1277 vic4_update_sideborder_dimensions(); @@ -701,7 +736,6 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) } - Uint8 vic_read_reg ( int unsigned addr ) { Uint8 result = vic_registers[addr & 0x7F]; // read the answer by default (mostly this will be), allow to override/modify in the switch construct if needed @@ -832,7 +866,8 @@ Uint8 vic_read_reg ( int unsigned addr ) #undef CASE_VIC_ALL #undef CASE_VIC_3_4 -static inline Uint32 get_charset_effective_addr() + +static inline Uint32 get_charset_effective_addr ( void ) { // cache this? switch (CHARSET_ADDR) { @@ -929,8 +964,7 @@ static void vic4_draw_sprite_row_mono ( int sprnum, int x_display_pos, const Uin const Uint8 pixel = *row_data_ptr & (0x80 >> xbit); for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { if ( - x_display_pos >= border_x_left && - pixel && ( + x_display_pos >= border_x_left && pixel && ( !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL) ) @@ -944,7 +978,7 @@ static void vic4_draw_sprite_row_mono ( int sprnum, int x_display_pos, const Uin } -static void vic4_do_sprites() +static void vic4_do_sprites ( void ) { // Fetch and sequence sprites. // @@ -952,7 +986,7 @@ static void vic4_do_sprites() // In multicolor mode (MCM=1), the bit combinations "00" and "01" belong to the background // and "10" and "11" to the foreground whereas in standard mode (MCM=0), // cleared pixels belong to the background and set pixels to the foreground. - for (int sprnum = 7; sprnum >= 0; --sprnum) { + for (int sprnum = 7; sprnum >= 0; sprnum--) { if (REG_SPRITE_ENABLE & (1 << sprnum)) { const int spriteHeight = SPRITE_EXTHEIGHT(sprnum) ? REG_SPRHGHT : 21; int x_display_pos = border_x_left + ((SPRITE_POS_X(sprnum) - SPRITE_X_BASE_COORD) * (REG_SPR640 ? 1 : 2)); // in display units @@ -1070,7 +1104,7 @@ static void vic4_render_bitplane_char_row ( Uint8* bp_base[8], int glyph_width ) } -void vic4_render_bitplane_raster() +void vic4_render_bitplane_raster ( void ) { Uint8* bp_base[8]; // Get Bitplane source addresses @@ -1120,7 +1154,7 @@ void vic4_render_bitplane_raster() // // VIC-III Extended attributes are applied to characters if properly set, // except in Multicolor modes. -void vic4_render_char_raster() +void vic4_render_char_raster ( void ) { int line_char_index = 0; enable_bg_paint = 1; @@ -1215,7 +1249,7 @@ void vic4_render_char_raster() } -int vic4_render_scanline() +int vic4_render_scanline ( void ) { // Work this first. DO NOT OPTIMIZE EARLY. diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index 2aa2a17e..6b56598a 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -38,18 +38,19 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // The user can select a clipped borders view (called "normal borders") which shows // the real visible resolution of PAL (720x576) or NSTC(720x480). -#define TEXTURE_WIDTH 800 -#define TEXTURE_HEIGHT 625 - +#define SCREEN_WIDTH 800 +#define SCREEN_HEIGHT 625 #define PHYSICAL_RASTERS_DEFAULT PHYSICAL_RASTERS_NTSC #define SCREEN_HEIGHT_VISIBLE_DEFAULT SCREEN_HEIGHT_VISIBLE_NTSC #define SCREEN_HEIGHT_VISIBLE_NTSC 480 #define SCREEN_HEIGHT_VISIBLE_PAL 576 #define PHYSICAL_RASTERS_NTSC 526 #define PHYSICAL_RASTERS_PAL 624 +#define NTSC_RATIO (PHYSICAL_RASTERS_NTSC / float(SCREEN_WIDTH)) +#define PAL_RATIO (PHYSICAL_RASTERS_PAL / float(SCREEN_WIDTH)) #define FRAME_H_FRONT 0 #define RASTER_CORRECTION 3 -#define VIC4_BLINK_INTERVAL 30 +#define VIC4_BLINK_INTERVAL 25 // Register defines // @@ -62,11 +63,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_MCM (vic_registers[0x16] & 0x10) #define REG_BMM (vic_registers[0x11] & 0x20) #define REG_SPRITE_ENABLE vic_registers[0x15] -#define REG_BORDER_COLOR (vic_registers[0x20] & vic_color_register_mask) -#define REG_SCREEN_COLOR (vic_registers[0x21] & vic_color_register_mask) -#define REG_MULTICOLOR_1 (vic_registers[0x22] & vic_color_register_mask) -#define REG_MULTICOLOR_2 (vic_registers[0x23] & vic_color_register_mask) -//#define REG_MULTICOLOR_3 (vic_registers[0x24] & vic_color_register_mask) +#define REG_BORDER_COLOR vic_registers[0x20] +#define REG_SCREEN_COLOR vic_registers[0x21] +#define REG_MULTICOLOR_1 vic_registers[0x22] +#define REG_MULTICOLOR_2 vic_registers[0x23] +#define REG_MULTICOLOR_3 vic_registers[0x24] #define REG_H640 (vic_registers[0x31] & 128) #define REG_V400 (vic_registers[0x31] & 8) #define REG_VICIII_ATTRIBS (vic_registers[0x31] & 0x20) @@ -75,7 +76,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_DISPLAYENABLE (vic_registers[0x11] & 0x10) #define REG_VIC2_XSCROLL (vic_registers[0x16] & 7) #define REG_VIC2_YSCROLL (vic_registers[0x11] & 7) -//#define REG_CRAM2K (vic_registers[0x30] & 1) +#define REG_CRAM2K (vic_registers[0x30] & 1) #define REG_TBRDPOS (vic_registers[0x48]) #define REG_SPRBPMEN_0_3 (vic_registers[0x49] >> 4) #define REG_SPRBPMEN_4_7 (vic_registers[0x4B] >> 4) @@ -84,24 +85,23 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_BBRDPOS_U4 (vic_registers[0x4B] & 0xF) #define REG_TEXTXPOS (vic_registers[0x4C]) #define REG_TEXTXPOS_U4 (vic_registers[0x4D] & 0xF) -#define REG_SPRTILEN ((vic_registers[0x4D] >> 4) | (vic_registers[0x4F] & 0xF0)) #define REG_TEXTYPOS (vic_registers[0x4E]) #define REG_TEXTYPOS_U4 (vic_registers[0x4F] & 0xF) -//#define REG_XPOS (vic_registers[0x51]) -//#define REG_XPOS_U6 (vic_registers[0x50] & 0x3F) +#define REG_XPOS (vic_registers[0x51]) +#define REG_XPOS_U6 (vic_registers[0x50] & 0x3F) #define REG_FNRST (vic_registers[0x53] & 0x80) #define REG_16BITCHARSET (vic_registers[0x54] & 1) #define REG_FCLRLO (vic_registers[0x54] & 2) #define REG_FCLRHI (vic_registers[0x54] & 4) #define REG_SPR640 (vic_registers[0x54] & 0x10) #define REG_SPRHGHT (vic_registers[0x56]) -//#define REG_CHRXSCL (vic_registers[0x5A]) +#define REG_CHRXSCL (vic_registers[0x5A]) #define REG_CHRYSCL (vic_registers[0x5B]) #define REG_SIDBDRWD (vic_registers[0x5C]) #define REG_SIDBDRWD_U5 (vic_registers[0x5D] & 0x3F) #define REG_HOTREG (vic_registers[0x5D] & 0x80) -#define REG_LINESTEP vic_registers[0x58] -#define REG_LINESTEP_U8 vic_registers[0x59] +#define REG_CHARSTEP vic_registers[0x58] +#define REG_CHARSTEP_U8 vic_registers[0x59] #define REG_CHARXSCALE vic_registers[0x5A] #define REG_CHRCOUNT vic_registers[0x5E] #define REG_SCRNPTR_B0 vic_registers[0x60] @@ -116,10 +116,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_SPRPTR_B0 vic_registers[0x6C] #define REG_SPRPTR_B1 vic_registers[0x6D] #define REG_SPRPTR_B2 (vic_registers[0x6E] & 0x7F) -//#define REG_SCREEN_ROWS vic_registers[0x7B] -//#define REG_PAL_RED_BASE (vic_registers[0x100]) -//#define REG_PAL_GREEN_BASE (vic_registers[0x200]) -//#define REG_PAL_BLUE_BASE (vic_registers[0x300]) +#define REG_SCREEN_ROWS vic_registers[0x7B] +#define REG_PAL_RED_BASE (vic_registers[0x100]) +#define REG_PAL_GREEN_BASE (vic_registers[0x200]) +#define REG_PAL_BLUE_BASE (vic_registers[0x300]) // Helper macros for accessing multi-byte registers // and other similar functionality for convenience @@ -130,21 +130,20 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define BORDER_Y_BOTTOM (((Uint16)REG_BBRDPOS) | (REG_BBRDPOS_U4) << 8) #define CHARGEN_Y_START (((Uint16)REG_TEXTYPOS) | (REG_TEXTYPOS_U4) << 8) #define CHARGEN_X_START (((Uint16)REG_TEXTXPOS) | (REG_TEXTXPOS_U4) << 8) -#define LINESTEP_BYTES (((Uint16)REG_LINESTEP) | (REG_LINESTEP_U8) << 8) -//#define SCREEN_RAM_ADDR_VIC (REG_SCREEN_ADDR * 1024) +#define CHARSTEP_BYTES (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) +#define SCREEN_RAM_ADDR_VIC (REG_SCREEN_ADDR * 1024) #define SCREEN_ADDR ((Uint32)REG_SCRNPTR_B0 | (REG_SCRNPTR_B1<<8) | (REG_SCRNPTR_B2 <<16) | (REG_SCRNPTR_B3 << 24)) #define CHARSET_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) -#define VIC2_BITMAP_ADDR ((CHARSET_ADDR) & 0xFFE000) +#define VIC2_BITMAP_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) #define SPRITE_POINTER_ADDR ((Uint32)REG_SPRPTR_B0 | (REG_SPRPTR_B1<<8) | (REG_SPRPTR_B2 <<16)) #define COLOUR_RAM_OFFSET ((((Uint16)REG_COLPTR) | (REG_COLPTR_MSB) << 8)) //#define IS_NTSC_MODE (videostd_id) -//#define SCREEN_STEP (((Uint16)REG_LINESTEP) | (REG_LINESTEP_U8) << 8) +#define SCREEN_STEP (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) #define SPRITE_POS_Y(n) (vic_registers[1 + (n)*2]) #define SPRITE_POS_X(n) (((Uint16)vic_registers[(n)*2]) | ( (vic_registers[0x10] & (1 << (n)) ? 0x100 : 0))) -#define SPRITE_COLOR(n) (vic_registers[0x27+(n)] & vic_color_register_mask) -#define SPRITE_COLOR_4BIT(n) (vic_registers[0x27+(n)] & 0xF) -#define SPRITE_MULTICOLOR_1 (vic_registers[0x25] & vic_color_register_mask) -#define SPRITE_MULTICOLOR_2 (vic_registers[0x26] & vic_color_register_mask) +#define SPRITE_COLOR(n) (vic_registers[0x27+(n)] & 0xF) +#define SPRITE_MULTICOLOR_1 (vic_registers[0x25] & 0xF) +#define SPRITE_MULTICOLOR_2 (vic_registers[0x26] & 0xF) #define SPRITE_IS_BACK(n) (vic_registers[0x1B] & (1 << (n))) #define SPRITE_HORZ_2X(n) (vic_registers[0x1D] & (1 << (n))) #define SPRITE_VERT_2X(n) (vic_registers[0x17] & (1 << (n))) @@ -154,9 +153,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SPRITE_EXTHEIGHT(n) (vic_registers[0x55] & (1 << (n))) #define SPRITE_BITPLANE_ENABLE(n) (((REG_SPRBPMEN_4_7) << 4 | REG_SPRBPMEN_0_3) & (1 << (n))) #define SPRITE_16BITPOINTER (vic_registers[0x6E] & 0x80) -//#define TEXT_MODE (!REG_BMM) -//#define HIRES_BITMAP_MODE (REG_BMM & !REG_MCM & !REG_EBM) -//#define MULTICOLOR_BITMAP_MODE (REG_BMM & REG_MCM & !REG_EBM) +#define TEXT_MODE (!REG_BMM) +#define HIRES_BITMAP_MODE (REG_BMM & !REG_MCM & !REG_EBM) +#define MULTICOLOR_BITMAP_MODE (REG_BMM & REG_MCM & !REG_EBM) #define VIC3_ATTR_BLINK(c) ((c) & 0x1) #define VIC3_ATTR_REVERSE(c) ((c) & 0x2) #define VIC3_ATTR_BOLD(c) ((c) & 0x4) @@ -169,12 +168,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SXA_TRIM_RIGHT_BITS012(sw) ((sw) >> 13) #define SXA_VERTICAL_FLIP(cw) ((cw) & 0x8000) #define SXA_HORIZONTAL_FLIP(cw) ((cw) & 0x4000) -//#define SXA_ALPHA_BLEND(cw) ((cw) & 0x2000) +#define SXA_ALPHA_BLEND(cw) ((cw) & 0x2000) #define SXA_GOTO_X(cw) ((cw) & 0x1000) #define SXA_4BIT_PER_PIXEL(cw) ((cw) & 0x0800) #define SXA_TRIM_RIGHT_BIT3(cw) ((cw) & 0x0400) -#define SXA_ATTR_BOLD(cw) ((cw) & 0x0040) -#define SXA_ATTR_REVERSE(cw) ((cw) & 0x0020) +//FIXME: this last one was bad, and also, seems to be not used? //#define SXA_TRIM_TOP_BOTTOM(cw) (((cw) & 0x0300) >> 8) @@ -192,10 +190,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; \ } while(0) -/* #define SET_14BIT_REGI(basereg,x) do { \ +#define SET_14BIT_REGI(basereg,x) do { \ vic_registers[(basereg)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ vic_registers[((basereg)+1)] = (Uint8) ((Uint16)(x)) & 0x00FF; \ - } while(0) */ + } while(0) #define SET_16BIT_REG(basereg,x) do { \ vic_registers[((basereg) + 1)] = (Uint8) ((((Uint16)(x)) & 0xFF00) >> 8); \ vic_registers[(basereg)]= ((Uint16)(x)) & 0x00FF; \ @@ -204,7 +202,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // 11-bit registers #define SET_PHYSICAL_RASTER(x) SET_11BIT_REG(0x52, (x)) -//#define SET_RASTER_XPOS(x) SET_14BIT_REGI(0x50, (x)) +#define SET_RASTER_XPOS(x) SET_14BIT_REGI(0x50, (x)) // 12-bit registers @@ -216,20 +214,26 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //16-bit registers #define SET_COLORRAM_BASE(x) SET_16BIT_REG(0x64,(x)) -#define SET_LINESTEP_BYTES(x) SET_16BIT_REG(0x58,(x)) +#define SET_CHARSTEP_BYTES(x) SET_16BIT_REG(0x58,(x)) + +// Pixel foreground/background indicator for aiding in sprite rendering + +#define FOREGROUND_PIXEL 1 +#define BACKGROUND_PIXEL 0 // Review this! (VIC-II values) #define SPRITE_X_BASE_COORD 24 #define SPRITE_Y_BASE_COORD 50 -//#define SPRITE_X_UPPER_COORD 250 -//#define SPRITE_Y_UPPER_COORD 344 +#define SPRITE_X_UPPER_COORD 250 +#define SPRITE_Y_UPPER_COORD 344 // Current state extern int vic_iomode; //extern int scanline; extern Uint8 vic_registers[]; +extern int force_fast; extern Uint8 c128_d030_reg; extern const char *videostd_name; From ce2a181af6fc5da583840de91cfeae153973c1d4 Mon Sep 17 00:00:00 2001 From: hernandp Date: Mon, 22 Mar 2021 23:28:46 -0300 Subject: [PATCH 17/35] NTSC/PAL border setup at frame change. --- targets/mega65/vic4.c | 215 ++++++++++++++++++++---------------------- 1 file changed, 101 insertions(+), 114 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index fccbabe0..8651243c 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -238,6 +238,101 @@ void vic4_close_frame_access ( void ) } +static void vic4_update_sideborder_dimensions ( void ) +{ + if (REG_CSEL) { // 40-columns? + border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER; + if (!REG_H640) + border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER - 1; + else // 80-col mode + border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER; + } else { // 38-columns + border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER - 18; + if (!REG_H640) + border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER + 14; + else // 78-col mode + border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER + 15; + } +} + +static void vic4_update_vertical_borders( void ) +{ + if (REG_CSEL) { // 40-columns? + if (!REG_H640) + SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL)); + else // 80-col mode + SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL) - 2); + } else { // 38-columns + if (!REG_H640) + SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL)); + else // 78-col mode + SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL) - 2); + } + if (!REG_V400) { // Standard mode (200-lines) + if (REG_RSEL) { // 25-row + SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster)); + SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) - 1); + display_row_count = 25; + } else { + SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) + 8); + SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - (2 * vicii_first_raster) - SINGLE_TOP_BORDER_200 - 7); + display_row_count = 24; + } + SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) - 6 + REG_VIC2_YSCROLL * 2); + } else { // V400 + if (REG_RSEL) { // 25-line+V400 + SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster)); + SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 1); + display_row_count = 25*2; + } else { + SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) + 8); + SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - (2 * vicii_first_raster) - SINGLE_TOP_BORDER_200 - 7); + display_row_count = 24*2; + } + SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 6 + (REG_VIC2_YSCROLL * 2)); + } +} + +static void vic4_interpret_legacy_mode_registers ( void ) +{ + // See https://github.com/MEGA65/mega65-core/blob/257d78aa6a21638cb0120fd34bc0e6ab11adfd7c/src/vhdl/viciv.vhdl#L1277 + vic4_update_sideborder_dimensions(); + vic4_update_vertical_borders(); + + Uint8 width = REG_H640 ? 80 : 40; + REG_CHRCOUNT = width; + SET_CHARSTEP_BYTES(width);// * (REG_16BITCHARSET ? 2 : 1)); + + REG_SCRNPTR_B0 = 0; + REG_SCRNPTR_B1 &= 0xC0; + REG_SCRNPTR_B1 |= REG_H640 ? ((reg_d018_screen_addr & 14) << 2) : (reg_d018_screen_addr << 2); + REG_SCRNPTR_B2 = 0; + vic_registers[0x63] &= 0b11110000; + + REG_SPRPTR_B0 = 0xF8; + REG_SPRPTR_B1 = (reg_d018_screen_addr << 2) | 0x3; + if (REG_H640 | REG_V400) + REG_SPRPTR_B1 |= 4; + vic_registers[0x6E] &= 128; + + REG_SPRPTR_B1 = (~last_dd00_bits << 6) | (REG_SPRPTR_B1 & 0x3F); + REG_SCRNPTR_B1 = (~last_dd00_bits << 6) | (REG_SCRNPTR_B1 & 0x3F); + REG_CHARPTR_B1 = (~last_dd00_bits << 6) | (REG_CHARPTR_B1 & 0x3F); + + SET_COLORRAM_BASE(0); + DEBUGPRINT( + "VIC4: 16bit=%d, chrcount=%d, charstep=%d bytes, charscale=%d, vic_ii_first_raster=%d, ras_src=%d, " + "border yt=%d, yb=%d, xl=%d, xr=%d, textxpos=%d, textypos=%d, " + "screen_ram=$%06x, charset/bitmap=$%06x, sprite=$%06x" NL, + REG_16BITCHARSET, REG_CHRCOUNT, CHARSTEP_BYTES, REG_CHARXSCALE, + vicii_first_raster, REG_FNRST, BORDER_Y_TOP, BORDER_Y_BOTTOM, border_x_left, border_x_right, CHARGEN_X_START, CHARGEN_Y_START, + SCREEN_ADDR, CHARSET_ADDR, SPRITE_POINTER_ADDR + ); +} + + + + // Must be called before using the texture at all, otherwise crash will happen, or nothing at all. // Access must be closed with vic4_close_frame_access(). // Do NOT call this function from vic4.c! It must be used by the emulator's main loop! @@ -267,6 +362,7 @@ void vic4_open_frame_access ( void ) videostd_1mhz_cycles_per_scanline = 1000000.0 / (float)(NTSC_LINE_FREQ); max_rasters = PHYSICAL_RASTERS_NTSC; visible_area_height = SCREEN_HEIGHT_VISIBLE_NTSC; + vicii_first_raster = 7; } else { // --- PAL --- new_name = PAL_STD_NAME; @@ -274,6 +370,7 @@ void vic4_open_frame_access ( void ) videostd_1mhz_cycles_per_scanline = 1000000.0 / (float)(PAL_LINE_FREQ); max_rasters = PHYSICAL_RASTERS_PAL; visible_area_height = SCREEN_HEIGHT_VISIBLE_PAL; + vicii_first_raster = 0; } DEBUGPRINT("VIC: switching video standard from %s to %s (1MHz line cycle count is %f, frame time is %dusec, max raster is %d, visible area height is %d)" NL, videostd_name, new_name, videostd_1mhz_cycles_per_scanline, videostd_frametime, max_rasters, visible_area_height); videostd_name = new_name; @@ -288,14 +385,12 @@ void vic4_open_frame_access ( void ) else xemu_set_viewport(48, 0, SCREEN_WIDTH - 48, visible_area_height - 1, XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE); } - // FIXME: do we need this here? Ie, should this always bound to video mode change (only at frame boundary!) or not ... -#if 0 + vicii_first_raster = vic_registers[0x6F] & 0x1F; if (!in_hypervisor) { - vic4_sideborder_touched = 1; - vic4_interpret_legacy_mode_registers(); + vic4_update_sideborder_dimensions(); + vic4_update_vertical_borders(); } -#endif } @@ -336,94 +431,6 @@ inline static void vic4_calculate_char_x_step ( void ) } -static void vic4_update_sideborder_dimensions ( void ) -{ - if (REG_CSEL) { // 40-columns? - border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER; - if (!REG_H640) - border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER - 1; - else // 80-col mode - border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER; - } else { // 38-columns - border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER - 18; - if (!REG_H640) - border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER + 14; - else // 78-col mode - border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER + 15; - } -} - - -static void vic4_interpret_legacy_mode_registers ( void ) -{ - // See https://github.com/MEGA65/mega65-core/blob/257d78aa6a21638cb0120fd34bc0e6ab11adfd7c/src/vhdl/viciv.vhdl#L1277 - vic4_update_sideborder_dimensions(); - if (REG_CSEL) { // 40-columns? - if (!REG_H640) - SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL)); - else // 80-col mode - SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL) - 2); - } else { // 38-columns - if (!REG_H640) - SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL)); - else // 78-col mode - SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL) - 2); - } - if (!REG_V400) { // Standard mode (200-lines) - if (REG_RSEL) { // 25-row - SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster)); - SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) - 1); - display_row_count = 25; - } else { - SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) + 8); - SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - (2 * vicii_first_raster) - SINGLE_TOP_BORDER_200 - 7); - display_row_count = 24; - } - SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) - 6 + REG_VIC2_YSCROLL * 2); - } else { // V400 - if (REG_RSEL) { // 25-line+V400 - SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster)); - SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 1); - display_row_count = 25*2; - } else { - SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) + 8); - SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - (2 * vicii_first_raster) - SINGLE_TOP_BORDER_200 - 7); - display_row_count = 24*2; - } - SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 6 + (REG_VIC2_YSCROLL * 2)); - } - Uint8 width = REG_H640 ? 80 : 40; - REG_CHRCOUNT = width; - SET_CHARSTEP_BYTES(width);// * (REG_16BITCHARSET ? 2 : 1)); - - REG_SCRNPTR_B0 = 0; - REG_SCRNPTR_B1 &= 0xC0; - REG_SCRNPTR_B1 |= REG_H640 ? ((reg_d018_screen_addr & 14) << 2) : (reg_d018_screen_addr << 2); - REG_SCRNPTR_B2 = 0; - vic_registers[0x63] &= 0b11110000; - - REG_SPRPTR_B0 = 0xF8; - REG_SPRPTR_B1 = (reg_d018_screen_addr << 2) | 0x3; - if (REG_H640 | REG_V400) - REG_SPRPTR_B1 |= 4; - vic_registers[0x6E] &= 128; - - REG_SPRPTR_B1 = (~last_dd00_bits << 6) | (REG_SPRPTR_B1 & 0x3F); - REG_SCRNPTR_B1 = (~last_dd00_bits << 6) | (REG_SCRNPTR_B1 & 0x3F); - REG_CHARPTR_B1 = (~last_dd00_bits << 6) | (REG_CHARPTR_B1 & 0x3F); - - SET_COLORRAM_BASE(0); - DEBUGPRINT( - "VIC4: 16bit=%d, chrcount=%d, charstep=%d bytes, charscale=%d, vic_ii_first_raster=%d, ras_src=%d, " - "border yt=%d, yb=%d, xl=%d, xr=%d, textxpos=%d, textypos=%d, " - "screen_ram=$%06x, charset/bitmap=$%06x, sprite=$%06x" NL, - REG_16BITCHARSET, REG_CHRCOUNT, CHARSTEP_BYTES, REG_CHARXSCALE, - vicii_first_raster, REG_FNRST, BORDER_Y_TOP, BORDER_Y_BOTTOM, border_x_left, border_x_right, CHARGEN_X_START, CHARGEN_Y_START, - SCREEN_ADDR, CHARSET_ADDR, SPRITE_POINTER_ADDR - ); -} - - // FIXME: preliminary DAT support. For real, these should be mostly calculated at writing // DAT X/Y registers, bitplane selection registers etc (also true for the actual renderer!), // would give much better emulator performace. Though for now, that's a naive preliminary @@ -663,27 +670,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) break; CASE_VIC_4(0x6F): -#if 0 - // Trigger video mode change. - // LGB: this must be handled at opening new frame and NOT here. - // though I am still in doubts, what parts should be still handled - // here anyway, like this lines after #endif, see below - max_rasters = data & 0x80 ? PHYSICAL_RASTERS_NTSC : PHYSICAL_RASTERS_PAL; - visible_area_height = data & 0x80 ? SCREEN_HEIGHT_VISIBLE_NTSC : SCREEN_HEIGHT_VISIBLE_PAL; - - if ((vic_registers[0x6F] & 0x80) ^ (data & 0x80)) { - // Change video mode - vic4_reset_display_counters(); - vic4_switch_display_mode(data & 0x80); - } -#endif - vicii_first_raster = data & 0x1F; - - if (!in_hypervisor) { - vic4_sideborder_touched = 1; - vic4_interpret_legacy_mode_registers(); - } - + // We trigger video setup at next frame. break; CASE_VIC_4(0x70): // VIC-IV palette selection register From dbb4a28b980fa7019667fb5de50cea175211c962 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:30:43 +0200 Subject: [PATCH 18/35] MEGA65: my VIC-IV updates from branch 'merger' --- targets/mega65/vic4.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 8651243c..2983f904 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -253,8 +253,10 @@ static void vic4_update_sideborder_dimensions ( void ) else // 78-col mode border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER + 15; } + DEBUGPRINT("VIC4: set border left=%d, right=%d, textxpos=%d" NL, border_x_left, border_x_right, CHARGEN_X_START); } + static void vic4_update_vertical_borders( void ) { if (REG_CSEL) { // 40-columns? @@ -291,8 +293,11 @@ static void vic4_update_vertical_borders( void ) } SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 6 + (REG_VIC2_YSCROLL * 2)); } + DEBUGPRINT("VIC4: set border top=%d, bottom=%d, textypos=%d, display_row_count=%d vic_ii_first_raster=%d" NL, BORDER_Y_TOP, BORDER_Y_BOTTOM, + CHARGEN_Y_START, display_row_count, vicii_first_raster); } + static void vic4_interpret_legacy_mode_registers ( void ) { // See https://github.com/MEGA65/mega65-core/blob/257d78aa6a21638cb0120fd34bc0e6ab11adfd7c/src/vhdl/viciv.vhdl#L1277 @@ -320,19 +325,13 @@ static void vic4_interpret_legacy_mode_registers ( void ) REG_CHARPTR_B1 = (~last_dd00_bits << 6) | (REG_CHARPTR_B1 & 0x3F); SET_COLORRAM_BASE(0); - DEBUGPRINT( - "VIC4: 16bit=%d, chrcount=%d, charstep=%d bytes, charscale=%d, vic_ii_first_raster=%d, ras_src=%d, " - "border yt=%d, yb=%d, xl=%d, xr=%d, textxpos=%d, textypos=%d, " + DEBUGPRINT("VIC4: 16bit=%d, chrcount=%d, charstep=%d bytes, charxscale=%d, ras_src=%d " "screen_ram=$%06x, charset/bitmap=$%06x, sprite=$%06x" NL, - REG_16BITCHARSET, REG_CHRCOUNT, CHARSTEP_BYTES, REG_CHARXSCALE, - vicii_first_raster, REG_FNRST, BORDER_Y_TOP, BORDER_Y_BOTTOM, border_x_left, border_x_right, CHARGEN_X_START, CHARGEN_Y_START, - SCREEN_ADDR, CHARSET_ADDR, SPRITE_POINTER_ADDR - ); + REG_16BITCHARSET, REG_CHRCOUNT, CHARSTEP_BYTES, REG_CHARXSCALE, + REG_FNRST, SCREEN_ADDR, CHARSET_ADDR, SPRITE_POINTER_ADDR); } - - // Must be called before using the texture at all, otherwise crash will happen, or nothing at all. // Access must be closed with vic4_close_frame_access(). // Do NOT call this function from vic4.c! It must be used by the emulator's main loop! @@ -375,6 +374,11 @@ void vic4_open_frame_access ( void ) DEBUGPRINT("VIC: switching video standard from %s to %s (1MHz line cycle count is %f, frame time is %dusec, max raster is %d, visible area height is %d)" NL, videostd_name, new_name, videostd_1mhz_cycles_per_scanline, videostd_frametime, max_rasters, visible_area_height); videostd_name = new_name; vic_readjust_sdl_viewport = 1; + vicii_first_raster = vic_registers[0x6F] & 0x1F; + if (!in_hypervisor) { + vic4_update_sideborder_dimensions(); + vic4_update_vertical_borders(); + } } // handle this via vic_readjust_sdl_viewport variable (not directly above) so external stuff (like UI) can also // force to adjust viewport, not just the PAL/NTSC change itself (ie: fullborder/clipped border change) @@ -385,12 +389,6 @@ void vic4_open_frame_access ( void ) else xemu_set_viewport(48, 0, SCREEN_WIDTH - 48, visible_area_height - 1, XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE); } - - vicii_first_raster = vic_registers[0x6F] & 0x1F; - if (!in_hypervisor) { - vic4_update_sideborder_dimensions(); - vic4_update_vertical_borders(); - } } From d01fc437b056e89fbd42ecfce0643c3bd829c6ee Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Mon, 29 Mar 2021 09:23:02 +0200 Subject: [PATCH 19/35] M65: rename SCREEN_{WIDTH,HEIGHT} to TEXTURE_ #29 --- targets/mega65/vic4.c | 28 ++++++++++++++-------------- targets/mega65/vic4.h | 7 +++---- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 2983f904..dbd5287b 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -214,10 +214,10 @@ void vic4_close_frame_access ( void ) if (!xemu_screenshot_png( NULL, NULL, 1, 1, // no ratio/zoom correction is applied - pixel_start + y1 * SCREEN_WIDTH + x1, // pixel pointer corresponding to the top left corner of the viewport + pixel_start + y1 * TEXTURE_WIDTH + x1, // pixel pointer corresponding to the top left corner of the viewport x2 - x1 + 1, // width y2 - y1 + 1, // height - SCREEN_WIDTH // full width (ie, width of the texture) + TEXTURE_WIDTH // full width (ie, width of the texture) )) { const char *p = strrchr(xemu_screenshot_full_path, DIRSEP_CHR); if (p) @@ -231,7 +231,7 @@ void vic4_close_frame_access ( void ) xemu_get_viewport(NULL, &y_origin, &x_origin, NULL); for (unsigned int y = 0; y < 8; y++) for (unsigned int x = 0; x < 8; x++) - *(pixel_start + x_origin - 10 + x + (y + 2 + y_origin) * (SCREEN_WIDTH)) = (x > 1 && x < 7 && y > 1 && y < 7) ? red_colour : black_colour; + *(pixel_start + x_origin - 10 + x + (y + 2 + y_origin) * (TEXTURE_WIDTH)) = (x > 1 && x < 7 && y > 1 && y < 7) ? red_colour : black_colour; } // FINALLY .... xemu_update_screen(); @@ -243,11 +243,11 @@ static void vic4_update_sideborder_dimensions ( void ) if (REG_CSEL) { // 40-columns? border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER; if (!REG_H640) - border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER - 1; + border_x_right = FRAME_H_FRONT + TEXTURE_WIDTH - SINGLE_SIDE_BORDER - 1; else // 80-col mode - border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER; + border_x_right = FRAME_H_FRONT + TEXTURE_WIDTH - SINGLE_SIDE_BORDER; } else { // 38-columns - border_x_right = FRAME_H_FRONT + SCREEN_WIDTH - SINGLE_SIDE_BORDER - 18; + border_x_right = FRAME_H_FRONT + TEXTURE_WIDTH - SINGLE_SIDE_BORDER - 18; if (!REG_H640) border_x_left = FRAME_H_FRONT + SINGLE_SIDE_BORDER + 14; else // 78-col mode @@ -339,7 +339,7 @@ void vic4_open_frame_access ( void ) { int tail_sdl; current_pixel = pixel_start = xemu_start_pixel_buffer_access(&tail_sdl); - pixel_end = current_pixel + (SCREEN_WIDTH * max_rasters); + pixel_end = current_pixel + (TEXTURE_WIDTH * max_rasters); if (tail_sdl) FATAL("tail_sdl is not zero!"); // Now check the video mode: NTSC or PAL @@ -385,9 +385,9 @@ void vic4_open_frame_access ( void ) if (XEMU_UNLIKELY(vic_readjust_sdl_viewport)) { vic_readjust_sdl_viewport = 0; if (configdb.fullborders) // XXX FIXME what should be the correct values for full borders and without that?! - xemu_set_viewport(0, 0, SCREEN_WIDTH - 1, max_rasters - 1, XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE); + xemu_set_viewport(0, 0, TEXTURE_WIDTH - 1, max_rasters - 1, XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE); else - xemu_set_viewport(48, 0, SCREEN_WIDTH - 48, visible_area_height - 1, XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE); + xemu_set_viewport(48, 0, TEXTURE_WIDTH - 48, visible_area_height - 1, XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE); } } @@ -1239,7 +1239,7 @@ int vic4_render_scanline ( void ) // Work this first. DO NOT OPTIMIZE EARLY. xcounter = 0; - current_pixel = pixel_start + ycounter * SCREEN_WIDTH; + current_pixel = pixel_start + ycounter * TEXTURE_WIDTH; pixel_raster_start = current_pixel; SET_PHYSICAL_RASTER(ycounter); @@ -1249,12 +1249,12 @@ int vic4_render_scanline ( void ) vic4_check_raster_interrupt(logical_raster); // "Double-scan hack" if (!REG_V400 && (ycounter & 1)) { - for (int i = 0; i < SCREEN_WIDTH; i++, current_pixel++) - *current_pixel = /* user_scanlines_setting ? 0 : */ *(current_pixel - SCREEN_WIDTH) ; + for (int i = 0; i < TEXTURE_WIDTH; i++, current_pixel++) + *current_pixel = /* user_scanlines_setting ? 0 : */ *(current_pixel - TEXTURE_WIDTH) ; } else { // Top and bottom borders if (ycounter < BORDER_Y_TOP || ycounter >= BORDER_Y_BOTTOM || !REG_DISPLAYENABLE) { - for (int i = 0; i < SCREEN_WIDTH; i++) + for (int i = 0; i < TEXTURE_WIDTH; i++) *(current_pixel++) = palette[REG_BORDER_COLOR]; } if (ycounter >= CHARGEN_Y_START && ycounter < BORDER_Y_BOTTOM) { @@ -1270,7 +1270,7 @@ int vic4_render_scanline ( void ) if (ycounter >= BORDER_Y_TOP && ycounter < CHARGEN_Y_START) { while (xcounter++ < border_x_right) *current_pixel++ = palette[REG_SCREEN_COLOR]; - // for (int i = 0; i < SCREEN_WIDTH - border_x_right; i++, current_pixel++) + // for (int i = 0; i < TEXTURE_WIDTH - border_x_right; i++, current_pixel++) // *current_pixel = palette[REG_SCREEN_COLOR]; } for (Uint32 *p = pixel_raster_start; p < pixel_raster_start + border_x_left; p++) diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index 6b56598a..15f7f122 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -38,16 +38,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // The user can select a clipped borders view (called "normal borders") which shows // the real visible resolution of PAL (720x576) or NSTC(720x480). -#define SCREEN_WIDTH 800 -#define SCREEN_HEIGHT 625 +#define TEXTURE_WIDTH 800 +#define TEXTURE_HEIGHT 625 + #define PHYSICAL_RASTERS_DEFAULT PHYSICAL_RASTERS_NTSC #define SCREEN_HEIGHT_VISIBLE_DEFAULT SCREEN_HEIGHT_VISIBLE_NTSC #define SCREEN_HEIGHT_VISIBLE_NTSC 480 #define SCREEN_HEIGHT_VISIBLE_PAL 576 #define PHYSICAL_RASTERS_NTSC 526 #define PHYSICAL_RASTERS_PAL 624 -#define NTSC_RATIO (PHYSICAL_RASTERS_NTSC / float(SCREEN_WIDTH)) -#define PAL_RATIO (PHYSICAL_RASTERS_PAL / float(SCREEN_WIDTH)) #define FRAME_H_FRONT 0 #define RASTER_CORRECTION 3 #define VIC4_BLINK_INTERVAL 25 From 5e74c2859299c62958992b7b808cd058fe1b89af Mon Sep 17 00:00:00 2001 From: hernandp Date: Tue, 13 Apr 2021 21:36:12 -0300 Subject: [PATCH 20/35] NEW Nybl16-color mode. --- targets/mega65/vic4.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index dbd5287b..578bdce5 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -1063,6 +1063,22 @@ static void vic4_render_fullcolor_char_row ( const Uint8* char_row, int glyph_wi } +// 16-color (Nybl) mode (4-bit per pixel / 16 pixel wide characters) +static void vic4_render_16color_char_row ( const Uint8* char_row, int glyph_width ) +{ + for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { + Uint8 char_data = char_row[((int)cx) / 2]; + if (((int)cx) & 1) + char_data >>= 4; + else + char_data &= 0xf; + Uint32 pixel_color = palette[char_data]; + *(current_pixel++) = pixel_color; + bg_pixel_state[xcounter++] = pixel_color ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; + } +} + + // Render a bitplane-mode character cell row static void vic4_render_bitplane_char_row ( Uint8* bp_base[8], int glyph_width ) { @@ -1194,7 +1210,7 @@ void vic4_render_char_raster ( void ) char_byte = reverse_byte_table[char_byte]; // LGB: I killed the function, and type-conv, as char_byte is byte, OK to index as-is // Render character cell row if (SXA_4BIT_PER_PIXEL(color_data)) { // 16-color character - // FIXME: TODO?? + vic4_render_16color_char_row(main_ram + (((char_id * 64) + (sel_char_row * 8) ) & 0x7FFFF), glyph_width); } else if (CHAR_IS256_COLOR(char_id)) { // 256-color character vic4_render_fullcolor_char_row(main_ram + (((char_id * 64) + (sel_char_row * 8) ) & 0x7FFFF), 8); } else if ((REG_MCM && (char_fgcolor & 8)) || (REG_MCM && REG_BMM)) { // Multicolor character From 644ae83174867a894cbf2907e833fda21bc7dc28 Mon Sep 17 00:00:00 2001 From: hernandp Date: Wed, 14 Apr 2021 14:11:00 -0300 Subject: [PATCH 21/35] FIX: TEXTURE_WIDTH typo. --- targets/mega65/vic4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 578bdce5..d0cb1c26 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -292,7 +292,7 @@ static void vic4_update_vertical_borders( void ) display_row_count = 24*2; } SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 6 + (REG_VIC2_YSCROLL * 2)); - } + } DEBUGPRINT("VIC4: set border top=%d, bottom=%d, textypos=%d, display_row_count=%d vic_ii_first_raster=%d" NL, BORDER_Y_TOP, BORDER_Y_BOTTOM, CHARGEN_Y_START, display_row_count, vicii_first_raster); } From 01973c583e0ee9eaaf5f374e91ae3712de7f34f7 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:34:53 +0200 Subject: [PATCH 22/35] MEGA65: my VIC-IV updates from branch 'merger' --- targets/mega65/vic4.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index d0cb1c26..b3ba4ccb 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -37,7 +37,6 @@ static Uint32 *pixel_end, *pixel_start; // points to the end and start of the static Uint32 *pixel_raster_start; // first pixel of current raster Uint8 vic_registers[0x80]; // VIC-4 registers int vic_iomode; // VIC2/VIC3/VIC4 mode -int force_fast; // POKE 0,64 and 0,65 trick ... static int compare_raster; // raster compare (9 bits width) data static int logical_raster = 0; static int interrupt_status; // Interrupt status of VIC @@ -190,7 +189,6 @@ void vic_init ( void ) black_colour = SDL_MapRGBA(sdl_pix_fmt, 0x00, 0x00, 0x00, 0xFF); // Init VIC4 stuffs vic4_init_palette(); - force_fast = 0; vic_reset(); c128_d030_reg = 0xFE; // this may be set to 2MHz in the previous step, so be sure to set to FF here, BUT FIX: bit 0 should be inverted!! machine_set_speed(0); @@ -292,8 +290,8 @@ static void vic4_update_vertical_borders( void ) display_row_count = 24*2; } SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 6 + (REG_VIC2_YSCROLL * 2)); - } - DEBUGPRINT("VIC4: set border top=%d, bottom=%d, textypos=%d, display_row_count=%d vic_ii_first_raster=%d" NL, BORDER_Y_TOP, BORDER_Y_BOTTOM, + } + DEBUGPRINT("VIC4: set border top=%d, bottom=%d, textypos=%d, display_row_count=%d vic_ii_first_raster=%d" NL, BORDER_Y_TOP, BORDER_Y_BOTTOM, CHARGEN_Y_START, display_row_count, vicii_first_raster); } @@ -327,7 +325,7 @@ static void vic4_interpret_legacy_mode_registers ( void ) SET_COLORRAM_BASE(0); DEBUGPRINT("VIC4: 16bit=%d, chrcount=%d, charstep=%d bytes, charxscale=%d, ras_src=%d " "screen_ram=$%06x, charset/bitmap=$%06x, sprite=$%06x" NL, - REG_16BITCHARSET, REG_CHRCOUNT, CHARSTEP_BYTES, REG_CHARXSCALE, + REG_16BITCHARSET, REG_CHRCOUNT, CHARSTEP_BYTES, REG_CHARXSCALE, REG_FNRST, SCREEN_ADDR, CHARSET_ADDR, SPRITE_POINTER_ADDR); } @@ -672,9 +670,9 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) break; CASE_VIC_4(0x70): // VIC-IV palette selection register - palette = ((data & 0x03) << 8) + vic_palettes; + altpalette = ((data & 0x03) << 8) + vic_palettes; spritepalette = ((data & 0x0C) << 6) + vic_palettes; - altpalette = ((data & 0x30) << 4) + vic_palettes; + palette = ((data & 0x30) << 4) + vic_palettes; palregaccofs = ((data & 0xC0) << 2); check_if_rom_palette(vic_registers[0x30] & 4); break; @@ -1064,7 +1062,7 @@ static void vic4_render_fullcolor_char_row ( const Uint8* char_row, int glyph_wi // 16-color (Nybl) mode (4-bit per pixel / 16 pixel wide characters) -static void vic4_render_16color_char_row ( const Uint8* char_row, int glyph_width ) +static void vic4_render_16color_char_row ( const Uint8* char_row, int glyph_width ) { for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { Uint8 char_data = char_row[((int)cx) / 2]; From 9ed9a5e47e71f60ed9bd55910e17d37e5bc79a9f Mon Sep 17 00:00:00 2001 From: hernandp Date: Mon, 17 May 2021 23:38:22 -0300 Subject: [PATCH 23/35] Fix #245 (SPRPTR16 correct behavior) --- targets/mega65/vic4.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index b3ba4ccb..33deb3ea 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -652,18 +652,6 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) break; CASE_VIC_4(0x6C): CASE_VIC_4(0x6D): CASE_VIC_4(0x6E): vic_registers[addr & 0x7F] = data; - // if (SPRITE_POINTER_ADDR > 384*1024) { - // DEBUGPRINT("WARNING !!! : SPRITE_POINTER_ADDR at $%08X exceeds 384K chip RAM!!!! Current behavior is undefined." NL, SPRITE_POINTER_ADDR); - // } - - // DEBUGPRINT("SPRPTRADR/SPRPTRBNK Modified. Sprite Data Pointers now: " NL); - - // for (int i = 0; i < 8; i++) { - // const Uint8 *sprite_data_pointer = main_ram + SPRITE_POINTER_ADDR + i * ((SPRITE_16BITPOINTER >> 7) + 1); - // const Uint32 dataptr = SPRITE_16BITPOINTER ? 64 * ( ((*(sprite_data_pointer+1) << 8)) + (*(sprite_data_pointer))) : 64 * (*sprite_data_pointer); - // DEBUGPRINT("Sprite #%d data @ $%08X %s" NL , i, dataptr, dataptr > 384*1024 ? "!!! OUT OF 384K main RAM !!!" : ""); - // } - break; CASE_VIC_4(0x6F): // We trigger video setup at next frame. @@ -982,7 +970,9 @@ static void vic4_do_sprites ( void ) if (sprite_row_in_raster >= 0 && sprite_row_in_raster < spriteHeight) { const int widthBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; - const Uint8 *sprite_data_pointer = main_ram + SPRITE_POINTER_ADDR + sprnum * ((SPRITE_16BITPOINTER >> 7) + 1); + // Mask-out bits 0-3, 23-19 if SPRPTR16 enabled + const Uint32 sprite_pointer_addr = SPRITE_16BITPOINTER ? (SPRITE_POINTER_ADDR & 0x8FFFF0) : SPRITE_POINTER_ADDR; + const Uint8 *sprite_data_pointer = main_ram + sprite_pointer_addr + sprnum * ((SPRITE_16BITPOINTER >> 7) + 1); const Uint32 sprite_data_addr = SPRITE_16BITPOINTER ? 64 * ((*(sprite_data_pointer + 1) << 8) | (*sprite_data_pointer)) : ((64 * (*sprite_data_pointer)) | ( ((~last_dd00_bits) & 0x3)) << 14); From 23577c60d865870fe0e68f2c617949eec9dee7fa Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:36:37 +0200 Subject: [PATCH 24/35] MEGA65: my VIC-IV updates from branch 'merger' --- targets/mega65/vic4.c | 409 ++++++++++++++++++++---------------------- 1 file changed, 194 insertions(+), 215 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 33deb3ea..9272eefd 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -27,20 +27,21 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "configdb.h" #include "xemu/f011_core.h" #include "xemu/emutools_files.h" +#include "io_mapper.h" const char *iomode_names[4] = { "VIC2", "VIC3", "BAD!", "VIC4" }; // (SDL) target texture rendering pointers static Uint32 *current_pixel; // current_pixel pointer to the rendering target (one current_pixel: 32 bit) -static Uint32 *pixel_end, *pixel_start; // points to the end and start of the buffer +static Uint32 *pixel_start; // points to the end and start of the buffer static Uint32 *pixel_raster_start; // first pixel of current raster Uint8 vic_registers[0x80]; // VIC-4 registers int vic_iomode; // VIC2/VIC3/VIC4 mode static int compare_raster; // raster compare (9 bits width) data static int logical_raster = 0; static int interrupt_status; // Interrupt status of VIC -static int vic4_blink_phase = 0; // blinking attribute helper, state. +static int blink_phase = 0; // blinking attribute helper, state. Uint8 c128_d030_reg; // C128-like register can be only accessed in VIC-II mode but not in others, quite special! static Uint8 reg_d018_screen_addr = 0; // Legacy VIC-II $D018 screen address register static int vic_hotreg_touched = 0; // If any "legacy" registers were touched @@ -48,11 +49,8 @@ static int vic4_sideborder_touched = 0; // If side-border register were touch static int border_x_left= 0; // Side border left static int border_x_right= 0; // Side border right static int xcounter = 0, ycounter = 0; // video counters -static int frame_counter = 0; static int char_row = 0, display_row = 0; -static Uint8 bg_pixel_state[1024]; // See FOREGROUND_PIXEL and BACKGROUND_PIXEL constants -static Uint8* screen_ram_current_ptr = NULL; -static Uint8* colour_ram_current_ptr = NULL; +static Uint8 is_fg[1024]; // this cache helps in sprite rendering, zero means background state, other value: foreground FIXME: how long this should be? really 1024? static float char_x_step = 0.0; static int enable_bg_paint = 1; static int display_row_count = 0; @@ -60,20 +58,23 @@ static int max_rasters = PHYSICAL_RASTERS_DEFAULT; static int visible_area_height = SCREEN_HEIGHT_VISIBLE_DEFAULT; static int vicii_first_raster = 7; // Default for NTSC static Uint8 *bitplane_bank_p = main_ram; -static Uint32 red_colour, black_colour; // used by "drive LED" stuff +static Uint8 *bitplane_p[8]; +static Uint32 red_colour, black_colour; // used by "drive LED", and cross-hair (only the red) for debug pixel read +static Uint8 vic_pixel_readback_result[4]; +static Uint8 vic_color_register_mask = 0xFF; // --- these things are altered by vic4_open_frame_access() ONLY at every fame ONLY based on PAL or NTSC selection Uint8 videostd_id = 0xFF; // 0=PAL, 1=NTSC [give some insane value by default to force the change at the fist frame after starting Xemu] const char *videostd_name = ""; // PAL or NTSC, however initially is not yet set int videostd_frametime = NTSC_FRAME_TIME; // time in microseconds for a frame to produce -float videostd_1mhz_cycles_per_scanline = 32.0; // have *some* value to jumpstart emulation, it will be overriden sooner or later XXX FIXME: why it does not work with zero value when it's overriden anyway?!?! +float videostd_1mhz_cycles_per_scanline = 32.0; // have some value to jumpstart emulation, it will be overriden sooner or later XXX FIXME: why it does not work with zero value when it's overriden anyway?! int videostd_changed = 0; static const char NTSC_STD_NAME[] = "NTSC"; static const char PAL_STD_NAME[] = "PAL"; int vic_readjust_sdl_viewport = 0; -void vic4_render_char_raster(void); -void vic4_render_bitplane_raster(void); +static void vic4_render_char_raster(void); +static void vic4_render_bitplane_raster(void); static void (*vic4_raster_renderer_path)(void) = &vic4_render_char_raster; // VIC-IV Modeline Parameters @@ -88,40 +89,8 @@ static void (*vic4_raster_renderer_path)(void) = &vic4_render_char_raster; #define TOP_BORDERS_HEIGHT_400 (DISPLAY_HEIGHT - TEXT_HEIGHT_400) #define SINGLE_TOP_BORDER_200 (TOP_BORDERS_HEIGHT_200 >> 1) #define SINGLE_TOP_BORDER_400 (TOP_BORDERS_HEIGHT_400 >> 1) +// TODO: move as many things as possible from vic4.h to here which is only used by vic4.c to avoid confusions ... -//#define MAX(a,b) ((a)>(b)?(a):(b)) - -//#define CHECK_PIXEL_POINTER - - -#ifdef CHECK_PIXEL_POINTER -/* Temporary hack to be used in renders. Asserts out-of-texture accesses */ -static Uint32 *pixel_pointer_check_base; -static Uint32 *pixel_pointer_check_end; -static const char *pixel_pointer_check_modn; -static inline void PIXEL_POINTER_CHECK_INIT( Uint32 *base, int tail, const char *module ) -{ - pixel_pointer_check_base = base; - pixel_pointer_check_end = base + (640 + tail) * 200; - pixel_pointer_check_modn = module; -} -static inline void PIXEL_POINTER_CHECK_ASSERT ( Uint32 *p ) -{ - if (p < pixel_pointer_check_base) - FATAL("FATAL ASSERT: accessing texture (%p) under the base limit (%p).\nIn program module: %s", p, pixel_pointer_check_base, pixel_pointer_check_modn); - if (p >= pixel_pointer_check_end) - FATAL("FATAL ASSERT: accessing texture (%p) above the upper limit (%p).\nIn program module: %s", p, pixel_pointer_check_end, pixel_pointer_check_modn); -} -static inline void PIXEL_POINTER_FINAL_ASSERT ( Uint32 *p ) -{ - if (p != pixel_pointer_check_end) - FATAL("FATAL ASSERT: final texture pointer (%p) is not the same as the desired one (%p),\nIn program module %s", p, pixel_pointer_check_end, pixel_pointer_check_modn); -} -#else -# define PIXEL_POINTER_CHECK_INIT(base,tail,mod) -# define PIXEL_POINTER_CHECK_ASSERT(p) -# define PIXEL_POINTER_FINAL_ASSERT(p) -#endif static const Uint8 reverse_byte_table[] = { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, //0 @@ -170,10 +139,14 @@ void vic_reset ( void ) vic_write_reg(i, 0); (void)vic_read_reg(i); } + // to deactivate the pixel readback crosshair by default, ie X/Y pos that never meet + vic_registers[0x7D] = 0xFF; + vic_registers[0x7E] = 0xFF; + vic_registers[0x7F] = 0xFF; } -static void vic4_reset_display_counters ( void ) +static inline void vic4_reset_display_counters ( void ) { xcounter = 0; display_row = 0; @@ -184,6 +157,7 @@ static void vic4_reset_display_counters ( void ) void vic_init ( void ) { + vic_pixel_readback_result[0] = 0xFF; // "hyperram access count" or what, not so much emulated // Needed to render "drive LED" feature red_colour = SDL_MapRGBA(sdl_pix_fmt, 0xFF, 0x00, 0x00, 0xFF); black_colour = SDL_MapRGBA(sdl_pix_fmt, 0x00, 0x00, 0x00, 0xFF); @@ -192,17 +166,43 @@ void vic_init ( void ) vic_reset(); c128_d030_reg = 0xFE; // this may be set to 2MHz in the previous step, so be sure to set to FF here, BUT FIX: bit 0 should be inverted!! machine_set_speed(0); - screen_ram_current_ptr = main_ram + SCREEN_ADDR; - colour_ram_current_ptr = colour_ram; vic4_reset_display_counters(); DEBUG("VIC4: has been initialized." NL); } +// Debug pixel-read back feature of MEGA65 +static XEMU_INLINE void pixel_readback ( void ) +{ + // FIXME: this is surely wrong, and we should not use texture coords directly. We must fix this somehow (offsets?) + const int pix_readback_x = (vic_registers[0x7D] | ((vic_registers[0x7F] & 0x0F) << 8)) - 8 + 2; + const int pix_readback_y = vic_registers[0x7E] | ((vic_registers[0x7F] & 0xF0) << 4); + if (XEMU_UNLIKELY(pix_readback_y >= 0 && pix_readback_x >= 0 && pix_readback_y < max_rasters && pix_readback_x < TEXTURE_WIDTH)) { + const Uint32 texpixcol = xemu_frame_pixel_access_p[TEXTURE_WIDTH * pix_readback_y + pix_readback_x]; + // the array will be used on reading $D70D indexed by the top two bits of $D07C, element "0" is "hyperram access count" and not handled here + // FIXME Warning: this code assumes that R/G/B SDL components are exactly 8 bit + vic_pixel_readback_result[1] = (texpixcol >> sdl_pix_fmt->Rshift) & 0xFF; // red channel + vic_pixel_readback_result[2] = (texpixcol >> sdl_pix_fmt->Gshift) & 0xFF; // green channel + vic_pixel_readback_result[3] = (texpixcol >> sdl_pix_fmt->Bshift) & 0xFF; // blue channel + // draw red coloured cross-hair + Uint32 *p = xemu_frame_pixel_access_p + TEXTURE_WIDTH * pix_readback_y; + for (int a = 0; a < TEXTURE_WIDTH; a++) + *p++ = red_colour; + p = xemu_frame_pixel_access_p + pix_readback_x; + for (int a = 0; a < max_rasters; a++) { + *p = red_colour; + p+= TEXTURE_WIDTH; + } + } +} + + // Pair of vic4_open_frame_access() and the place when screen is updated at SDL level, finally. // Do NOT call this function from vic4.c! It must be used by the emulator's main loop! void vic4_close_frame_access ( void ) { + // Debug pixel-read back feature of MEGA65 + pixel_readback(); #ifdef XEMU_FILES_SCREENSHOT_SUPPORT // Screenshot if (XEMU_UNLIKELY(registered_screenshot_request)) { @@ -233,6 +233,7 @@ void vic4_close_frame_access ( void ) } // FINALLY .... xemu_update_screen(); + D7XX[0xFA]++; // D7FA: elapsed number of frames counter } @@ -337,8 +338,7 @@ void vic4_open_frame_access ( void ) { int tail_sdl; current_pixel = pixel_start = xemu_start_pixel_buffer_access(&tail_sdl); - pixel_end = current_pixel + (TEXTURE_WIDTH * max_rasters); - if (tail_sdl) + if (XEMU_UNLIKELY(tail_sdl)) FATAL("tail_sdl is not zero!"); // Now check the video mode: NTSC or PAL // Though it can be changed any time, this kind of information really only can be applied @@ -374,6 +374,8 @@ void vic4_open_frame_access ( void ) vic_readjust_sdl_viewport = 1; vicii_first_raster = vic_registers[0x6F] & 0x1F; if (!in_hypervisor) { + // FIXME: later it can be a problem that a very brief transition to hypervisor mode and back (ie a quick trap) may be triggered within a single frame without + // ever hitting this point (?!) vic4_update_sideborder_dimensions(); vic4_update_vertical_borders(); } @@ -541,25 +543,27 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) CASE_VIC_2(0x28): CASE_VIC_2(0x29): CASE_VIC_2(0x2A): CASE_VIC_2(0x2B): CASE_VIC_2(0x2C): CASE_VIC_2(0x2D): CASE_VIC_2(0x2E): data &= 0xF; // colour-related registers are 4 bit only for VIC-II break; + // Colour registers of VIC seems to be always 8 in VIC-III. It's may be in conflict with c65manual.txt + // But anyway, this seems to be MEGA65's way. + // Previously this was "gated" with D031.5, however that was not correct! CASE_VIC_3(0x20): CASE_VIC_3(0x21): CASE_VIC_3(0x22): CASE_VIC_3(0x23): CASE_VIC_3(0x24): CASE_VIC_3(0x25): CASE_VIC_3(0x26): CASE_VIC_3(0x27): CASE_VIC_3(0x28): CASE_VIC_3(0x29): CASE_VIC_3(0x2A): CASE_VIC_3(0x2B): CASE_VIC_3(0x2C): CASE_VIC_3(0x2D): CASE_VIC_3(0x2E): - // FIXME TODO IS VIC-III also 4 bit only for colour regs?! according to c65manual.txt it seems! However according to M65's implementation it seems not ... - // It seems, M65 policy for this VIC-III feature is: enable 8 bit colour entires if D031.5 is set (also extended attributes) - if (!(vic_registers[0x31] & 32)) - data &= 0xF; - break; CASE_VIC_4(0x20): CASE_VIC_4(0x21): CASE_VIC_4(0x22): CASE_VIC_4(0x23): CASE_VIC_4(0x24): CASE_VIC_4(0x25): CASE_VIC_4(0x26): CASE_VIC_4(0x27): CASE_VIC_4(0x28): CASE_VIC_4(0x29): CASE_VIC_4(0x2A): CASE_VIC_4(0x2B): CASE_VIC_4(0x2C): CASE_VIC_4(0x2D): CASE_VIC_4(0x2E): - break; // colour-related registers are full 8 bit for VIC-IV + break; // colour-related registers are full 8 bit for VIC-IV and VIC-III CASE_VIC_ALL(0x2F): // the KEY register, it must be handled in ALL VIC modes, to be able to set VIC I/O mode do { int vic_new_iomode; - if (data == 0x96 && vic_registers[0x2F] == 0xA5) + if (data == 0x96 && vic_registers[0x2F] == 0xA5) { vic_new_iomode = VIC3_IOMODE; - else if (data == 0x53 && vic_registers[0x2F] == 0x47) + vic_color_register_mask = 0xFF; + } else if (data == 0x53 && vic_registers[0x2F] == 0x47) { vic_new_iomode = VIC4_IOMODE; - else + vic_color_register_mask = 0xFF; + } else { vic_new_iomode = VIC2_IOMODE; + vic_color_register_mask = 0x0F; + } if (vic_new_iomode != vic_iomode) { DEBUG("VIC: changing I/O mode %d(%s) -> %d(%s)" NL, vic_iomode, iomode_names[vic_iomode], vic_new_iomode, iomode_names[vic_new_iomode]); vic_iomode = vic_new_iomode; @@ -574,7 +578,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) /* --- NO MORE VIC-II REGS FROM HERE --- */ CASE_VIC_3_4(0x30): memory_set_vic3_rom_mapping(data); - check_if_rom_palette(data & 4); + check_if_rom_palette(!(data & 4)); break; CASE_VIC_3_4(0x31): // (!) NOTE: @@ -662,7 +666,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) spritepalette = ((data & 0x0C) << 6) + vic_palettes; palette = ((data & 0x30) << 4) + vic_palettes; palregaccofs = ((data & 0xC0) << 2); - check_if_rom_palette(vic_registers[0x30] & 4); + check_if_rom_palette(!(vic_registers[0x30] & 4)); break; CASE_VIC_4(0x7C): if ((data & 7) <= 2) { @@ -752,18 +756,14 @@ Uint8 vic_read_reg ( int unsigned addr ) break; CASE_VIC_2(0x20): CASE_VIC_2(0x21): CASE_VIC_2(0x22): CASE_VIC_2(0x23): CASE_VIC_2(0x24): CASE_VIC_2(0x25): CASE_VIC_2(0x26): CASE_VIC_2(0x27): CASE_VIC_2(0x28): CASE_VIC_2(0x29): CASE_VIC_2(0x2A): CASE_VIC_2(0x2B): CASE_VIC_2(0x2C): CASE_VIC_2(0x2D): CASE_VIC_2(0x2E): + // XXX check this!!! I'm not sure if MEGA65 really does this, even though on C64, unused top 4 bits are read as '1'! result |= 0xF0; // colour-related registers are 4 bit only for VIC-II break; CASE_VIC_3(0x20): CASE_VIC_3(0x21): CASE_VIC_3(0x22): CASE_VIC_3(0x23): CASE_VIC_3(0x24): CASE_VIC_3(0x25): CASE_VIC_3(0x26): CASE_VIC_3(0x27): CASE_VIC_3(0x28): CASE_VIC_3(0x29): CASE_VIC_3(0x2A): CASE_VIC_3(0x2B): CASE_VIC_3(0x2C): CASE_VIC_3(0x2D): CASE_VIC_3(0x2E): - // FIXME TODO IS VIC-III also 4 bit only for colour regs?! according to c65manual.txt it seems! However according to M65's implementation it seems not ... - // It seems, M65 policy for this VIC-III feature is: enable 8 bit colour entires if D031.5 is set (also extended attributes) - if (!(vic_registers[0x31] & 32)) - result |= 0xF0; - break; CASE_VIC_4(0x20): CASE_VIC_4(0x21): CASE_VIC_4(0x22): CASE_VIC_4(0x23): CASE_VIC_4(0x24): CASE_VIC_4(0x25): CASE_VIC_4(0x26): CASE_VIC_4(0x27): CASE_VIC_4(0x28): CASE_VIC_4(0x29): CASE_VIC_4(0x2A): CASE_VIC_4(0x2B): CASE_VIC_4(0x2C): CASE_VIC_4(0x2D): CASE_VIC_4(0x2E): - break; // colour-related registers are full 8 bit for VIC-IV + break; // colour-related registers are full 8 bit for VIC-IV and VIC-III CASE_VIC_ALL(0x2F): // the KEY register break; CASE_VIC_2(0x30): // this register is _SPECIAL_, and exists only in VIC-II (C64) I/O mode: C128-style "2MHz fast" mode ... @@ -799,12 +799,14 @@ Uint8 vic_read_reg ( int unsigned addr ) CASE_VIC_4(0x6D): break; CASE_VIC_4(0x6E): - break; - CASE_VIC_4(0x6F): CASE_VIC_4(0x70): CASE_VIC_4(0x71): CASE_VIC_4(0x72): CASE_VIC_4(0x73): CASE_VIC_4(0x74): CASE_VIC_4(0x75): CASE_VIC_4(0x76): CASE_VIC_4(0x77): CASE_VIC_4(0x78): CASE_VIC_4(0x79): CASE_VIC_4(0x7A): CASE_VIC_4(0x7B): CASE_VIC_4(0x7C): - CASE_VIC_4(0x7D): CASE_VIC_4(0x7E): CASE_VIC_4(0x7F): + break; + CASE_VIC_4(0x7D): + result = vic_pixel_readback_result[vic_registers[0x7C] >> 6]; + break; + CASE_VIC_4(0x7E): CASE_VIC_4(0x7F): break; /* --- NON-EXISTING REGISTERS --- */ CASE_VIC_2(0x31): CASE_VIC_2(0x32): CASE_VIC_2(0x33): CASE_VIC_2(0x34): CASE_VIC_2(0x35): CASE_VIC_2(0x36): CASE_VIC_2(0x37): CASE_VIC_2(0x38): @@ -838,110 +840,71 @@ Uint8 vic_read_reg ( int unsigned addr ) #undef CASE_VIC_3_4 -static inline Uint32 get_charset_effective_addr ( void ) -{ - // cache this? - switch (CHARSET_ADDR) { - case 0x1000: - return 0x2D000; - case 0x9000: - return 0x29000; - case 0x1800: - return 0x2D800; - case 0x9800: - return 0x29800; - } - return CHARSET_ADDR; -} - - -static void vic4_draw_sprite_row_16color( int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale ) +static XEMU_INLINE void vic4_draw_sprite_row_16color( int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale ) { const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; const int palindexbase = sprnum * 16 + 128 * (SPRITE_BITPLANE_ENABLE(sprnum) >> sprnum); + // LGB: in 16 colour sprite mode, sprite colour register gives the transparent colour index + // We always use the lower 4 bit only at this very specific case, that's the reason for SPRITE_COLOR_4BIT() macro and not SPRITE_COLOR() [which can be 4/8 bit depending on curretn VIC mode) + const Uint8 transparency_palette_index = SPRITE_COLOR_4BIT(sprnum); for (int byte = 0; byte < totalBytes; byte++) { const Uint8 c0 = (*(row_data_ptr + byte)) >> 4; const Uint8 c1 = (*(row_data_ptr + byte)) & 0xF; for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { - if (c0) { - if ( - x_display_pos >= border_x_left && ( - !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL) - ) - ) { - *(pixel_raster_start + x_display_pos) = spritepalette[palindexbase + c0]; - } - } + if (c0 != transparency_palette_index && x_display_pos >= border_x_left && ( + !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) + )) + *(pixel_raster_start + x_display_pos) = spritepalette[palindexbase + c0]; } for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { - if (c1) { - if ( - x_display_pos >= border_x_left && ( - !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL) - ) - ) { - *(pixel_raster_start + x_display_pos) = spritepalette[palindexbase + c1]; - } - } + if (c1 != transparency_palette_index && x_display_pos >= border_x_left && ( + !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) + )) + *(pixel_raster_start + x_display_pos) = spritepalette[palindexbase + c1]; } } } -static void vic4_draw_sprite_row_multicolor ( int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale ) +static XEMU_INLINE void vic4_draw_sprite_row_multicolor ( int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale ) { const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; + const Uint8 mcm_spr_pal_indices[4] = { 0, SPRITE_MULTICOLOR_1, SPRITE_COLOR(sprnum), SPRITE_MULTICOLOR_2 }; // entry zero is not used for (int byte = 0; byte < totalBytes; byte++) { - for (int xbit = 0; xbit < 8; xbit += 2) { - const Uint8 p0 = *row_data_ptr & (0x80 >> xbit); - const Uint8 p1 = *row_data_ptr & (0x40 >> xbit); - Uint8 pixel = 0; // TODO: See generated code -- use lookup instead of branch? - if (!p0 && p1) - pixel = SPRITE_MULTICOLOR_1; - else if (p0 && !p1) - pixel = SPRITE_COLOR(sprnum); - else if (p0 && p1) - pixel = SPRITE_MULTICOLOR_2; - + const Uint8 row_data = *row_data_ptr++; + for (int shift = 6; shift >= 0; shift -= 2) { + const int mcm_pixel_value = (row_data >> shift) & 3; + const Uint32 sdl_pixel = spritepalette[mcm_spr_pal_indices[mcm_pixel_value]]; for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos += 2) { - if (pixel) { - if ( - x_display_pos >= border_x_left && ( - !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL) - ) - ) { - *(pixel_raster_start + x_display_pos) = spritepalette[pixel]; - } - - if (x_display_pos+1 >= border_x_left && ( - !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos + 1] != FOREGROUND_PIXEL) - ) - ) { - *(pixel_raster_start + x_display_pos + 1) = spritepalette[pixel]; - } + if (mcm_pixel_value) { + if (x_display_pos >= border_x_left && ( + !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) + )) + *(pixel_raster_start + x_display_pos) = sdl_pixel; + if (x_display_pos + 1 >= border_x_left && ( + !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos + 1]) + )) + *(pixel_raster_start + x_display_pos + 1) = sdl_pixel; } } } - row_data_ptr++; } } -static void vic4_draw_sprite_row_mono ( int sprnum, int x_display_pos, const Uint8 *row_data_ptr, int xscale ) +static XEMU_INLINE void vic4_draw_sprite_row_mono ( int sprnum, int x_display_pos, const Uint8 *row_data_ptr, int xscale ) { const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; + const Uint32 sdl_pixel = spritepalette[SPRITE_COLOR(sprnum)]; for (int byte = 0; byte < totalBytes; byte++) { for (int xbit = 0; xbit < 8; xbit++) { - const Uint8 pixel = *row_data_ptr & (0x80 >> xbit); + const Uint8 sprite_bit = *row_data_ptr & (0x80 >> xbit); for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { - if ( - x_display_pos >= border_x_left && pixel && ( - !SPRITE_IS_BACK(sprnum) || - (SPRITE_IS_BACK(sprnum) && bg_pixel_state[x_display_pos] != FOREGROUND_PIXEL) - ) - ) { - *(pixel_raster_start + x_display_pos) = spritepalette[SPRITE_COLOR(sprnum)]; - } + if (x_display_pos >= border_x_left && sprite_bit && ( + !SPRITE_IS_BACK(sprnum) || + (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) + )) + *(pixel_raster_start + x_display_pos) = sdl_pixel; } } row_data_ptr++; @@ -949,7 +912,7 @@ static void vic4_draw_sprite_row_mono ( int sprnum, int x_display_pos, const Uin } -static void vic4_do_sprites ( void ) +static XEMU_INLINE void vic4_do_sprites ( void ) { // Fetch and sequence sprites. // @@ -1002,7 +965,7 @@ static void vic4_render_mono_char_row ( Uint8 char_byte, int glyph_width, Uint8 char_byte = 0xFF; if (VIC3_ATTR_REVERSE(vic3attr)) char_byte = ~char_byte; - if (VIC3_ATTR_BLINK(vic3attr) && vic4_blink_phase) + if (VIC3_ATTR_BLINK(vic3attr) && blink_phase) char_byte = VIC3_ATTR_REVERSE(vic3attr) ? ~char_byte : 0; if (VIC3_ATTR_BOLD(vic3attr)) fg_color |= 0x10; @@ -1012,7 +975,7 @@ static void vic4_render_mono_char_row ( Uint8 char_byte, int glyph_width, Uint8 const Uint8 char_pixel = (char_byte & (0x80 >> (int)cx)); Uint32 pixel_color = char_pixel ? palette[fg_color] : palette[bg_color]; *(current_pixel++) = pixel_color; - bg_pixel_state[xcounter++] = char_pixel ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; + is_fg[xcounter++] = char_pixel; } } else { // HACK!! to support MEGAMAZE GOTOX+VFLIP bits that ignore the background paint until next raster. for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { @@ -1020,39 +983,36 @@ static void vic4_render_mono_char_row ( Uint8 char_byte, int glyph_width, Uint8 if (char_pixel) *current_pixel = palette[fg_color]; current_pixel++; - bg_pixel_state[xcounter++] = char_pixel ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; + is_fg[xcounter++] = char_pixel; } } } -static void vic4_render_multicolor_char_row ( Uint8 char_byte, int glyph_width, const Uint8 color_source[4] ) +static inline void vic4_render_multicolor_char_row ( const Uint8 char_byte, const int glyph_width, const Uint8 color_source[4] ) { for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { const Uint8 bitsel = 2 * (int)(cx / 2); const Uint8 bit_pair = (char_byte & (0x80 >> bitsel)) >> (6-bitsel) | (char_byte & (0x40 >> bitsel)) >> (6-bitsel); - - Uint8 pixel = color_source[bit_pair]; - const Uint8 layer = bit_pair & 2 ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; - *(current_pixel++) = palette[pixel]; - bg_pixel_state[xcounter++] = layer; + *(current_pixel++) = palette[color_source[bit_pair]]; + is_fg[xcounter++] = (bit_pair & 2); } } // 8-bytes per row -static void vic4_render_fullcolor_char_row ( const Uint8* char_row, int glyph_width ) +static inline void vic4_render_fullcolor_char_row ( const Uint8* char_row, const int glyph_width ) { for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { - Uint32 pixel_color = palette[char_row[(int)cx]]; - *(current_pixel++) = pixel_color; - bg_pixel_state[xcounter++] = pixel_color ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; + const Uint8 char_data = char_row[(int)cx]; + *(current_pixel++) = palette[char_data]; + is_fg[xcounter++] = char_data; } } // 16-color (Nybl) mode (4-bit per pixel / 16 pixel wide characters) -static void vic4_render_16color_char_row ( const Uint8* char_row, int glyph_width ) +static XEMU_INLINE void vic4_render_16color_char_row ( const Uint8* char_row, const int glyph_width, const Uint32 bg_sdl_color, const Uint32 *palette16 ) { for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { Uint8 char_data = char_row[((int)cx) / 2]; @@ -1060,64 +1020,66 @@ static void vic4_render_16color_char_row ( const Uint8* char_row, int glyph_widt char_data >>= 4; else char_data &= 0xf; - Uint32 pixel_color = palette[char_data]; - *(current_pixel++) = pixel_color; - bg_pixel_state[xcounter++] = pixel_color ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; + is_fg[xcounter++] = char_data; + if (char_data) + *current_pixel = palette16[char_data]; + else if (enable_bg_paint) + *current_pixel = bg_sdl_color; + current_pixel++; } } +static XEMU_INLINE void set_bitplane_pointers ( void ) +{ + // Get Bitplane source addresses + /* TODO: Cache the following reads & EA calculation */ + const int and_mask = (REG_H640 ? 12 : 14); + //for (int i = 0; i < 8; i++) + // bitplane_p[i] = bitplane_bank_p + ((vic_registers[0x33 + i] & and_mask) << 12) + ((i & 1) << 16); + bitplane_p[0] = bitplane_bank_p + ((vic_registers[0x33] & and_mask) << 12); + bitplane_p[1] = bitplane_bank_p + ((vic_registers[0x34] & and_mask) << 12) + 0x10000; + bitplane_p[2] = bitplane_bank_p + ((vic_registers[0x35] & and_mask) << 12); + bitplane_p[3] = bitplane_bank_p + ((vic_registers[0x36] & and_mask) << 12) + 0x10000; + bitplane_p[4] = bitplane_bank_p + ((vic_registers[0x37] & and_mask) << 12); + bitplane_p[5] = bitplane_bank_p + ((vic_registers[0x38] & and_mask) << 12) + 0x10000; + bitplane_p[6] = bitplane_bank_p + ((vic_registers[0x39] & and_mask) << 12); + bitplane_p[7] = bitplane_bank_p + ((vic_registers[0x3A] & and_mask) << 12) + 0x10000; +} + + // Render a bitplane-mode character cell row -static void vic4_render_bitplane_char_row ( Uint8* bp_base[8], int glyph_width ) +static XEMU_INLINE void vic4_render_bitplane_char_row ( const Uint32 offset, const int glyph_width ) { const Uint8 bpe_mask = vic_registers[0x32] & (REG_H640 ? 15 : 255); - const Uint8 bp_comp = vic_registers[0x3B]; - for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { const Uint8 bitsel = 0x80 >> ((int)cx); - const Uint32 pixel_color = palette[ - (( - ((*bp_base[0] & bitsel) ? 1 : 0) | - ((*bp_base[1] & bitsel) ? 2 : 0) | - ((*bp_base[2] & bitsel) ? 4 : 0) | - ((*bp_base[3] & bitsel) ? 8 : 0) | - ((*bp_base[4] & bitsel) ? 16 : 0) | - ((*bp_base[5] & bitsel) ? 32 : 0) | - ((*bp_base[6] & bitsel) ? 64 : 0) | - ((*bp_base[7] & bitsel) ? 128 : 0) - ) & bpe_mask) ^ bp_comp + *(current_pixel++) = palette[(( // Do not try this at home ... + ((*(bitplane_p[0] + offset) & bitsel) ? 1 : 0) | + ((*(bitplane_p[1] + offset) & bitsel) ? 2 : 0) | + ((*(bitplane_p[2] + offset) & bitsel) ? 4 : 0) | + ((*(bitplane_p[3] + offset) & bitsel) ? 8 : 0) | + ((*(bitplane_p[4] + offset) & bitsel) ? 16 : 0) | + ((*(bitplane_p[5] + offset) & bitsel) ? 32 : 0) | + ((*(bitplane_p[6] + offset) & bitsel) ? 64 : 0) | + ((*(bitplane_p[7] + offset) & bitsel) ? 128 : 0) + ) & bpe_mask) ^ vic_registers[0x3B] ]; - *(current_pixel++) = pixel_color; - bg_pixel_state[xcounter++] = *bp_base[2] & bitsel ? FOREGROUND_PIXEL : BACKGROUND_PIXEL; + is_fg[xcounter++] = (*(bitplane_p[2] + offset) & bitsel); } } -void vic4_render_bitplane_raster ( void ) +static void vic4_render_bitplane_raster ( void ) { - Uint8* bp_base[8]; - // Get Bitplane source addresses - /* TODO: Cache the following reads & EA calculation */ - const Uint32 offset = display_row * REG_CHRCOUNT * 8 + char_row ; - bp_base[0] = bitplane_bank_p + ((vic_registers[0x33] & (REG_H640 ? 12 : 14)) << 12) + offset; - bp_base[1] = bitplane_bank_p + ((vic_registers[0x34] & (REG_H640 ? 12 : 14)) << 12) + 0x10000 + offset; - bp_base[2] = bitplane_bank_p + ((vic_registers[0x35] & (REG_H640 ? 12 : 14)) << 12) + offset; - bp_base[3] = bitplane_bank_p + ((vic_registers[0x36] & (REG_H640 ? 12 : 14)) << 12) + 0x10000 + offset; - bp_base[4] = bitplane_bank_p + ((vic_registers[0x37] & (REG_H640 ? 12 : 14)) << 12) + offset; - bp_base[5] = bitplane_bank_p + ((vic_registers[0x38] & (REG_H640 ? 12 : 14)) << 12) + 0x10000 + offset; - bp_base[6] = bitplane_bank_p + ((vic_registers[0x39] & (REG_H640 ? 12 : 14)) << 12) + offset; - bp_base[7] = bitplane_bank_p + ((vic_registers[0x3A] & (REG_H640 ? 12 : 14)) << 12) + 0x10000 + offset; + // FIXME: do not call this function here, but from actual register writes only + // which can affect the result of this function!! + set_bitplane_pointers(); + Uint32 offset = display_row * REG_CHRCOUNT * 8 + char_row ; int line_char_index = 0; - while(line_char_index < REG_CHRCOUNT) { - vic4_render_bitplane_char_row(bp_base, 8); - bp_base[0] += 8; - bp_base[1] += 8; - bp_base[2] += 8; - bp_base[3] += 8; - bp_base[4] += 8; - bp_base[5] += 8; - bp_base[6] += 8; - bp_base[7] += 8; + while (line_char_index < REG_CHRCOUNT) { + vic4_render_bitplane_char_row(offset, 8); + offset += 8; line_char_index++; } if (++char_row > 7) { @@ -1129,6 +1091,24 @@ void vic4_render_bitplane_raster ( void ) } +// TODO: make this register-write time event rather than calling by the scanline renderer again and again ... +static XEMU_INLINE Uint8 *get_charset_effective_addr ( void ) +{ + //const Uint8 *row_data_base_addr = main_ram + (REG_BMM ? VIC2_BITMAP_ADDR : get_charset_effective_addr()); + int addr = VIC2_BITMAP_ADDR; + // Note: in theory on C65 there is a bit for choose between two charsets (rather than only lower/upper case) + // See: https://github.com/lgblgblgb/xemu/issues/213 + // However it seems even MEGA65 does not support this. + if (!REG_BMM && (addr == 0x1000 || addr == 0x9000 || addr == 0x1800 || addr == 0x9800)) + return char_wom + (addr & 0xFFF); + // FIXME XXX this is a fixed constant for checking. + if (XEMU_UNLIKELY(addr > 0x60000)) // this is valid since we still have got some extra unused RAM left to go beyond actual RAM while bulding the frame + return main_ram + 0x60000; // give some unused ram array of emulaton, thus whatever high value set by user as ADDR, won't overflow during the frame + else + return main_ram + addr; +} + + // The character rendering engine. Most features are shared between // all graphic modes. Basically, the VIC-IV supports the following character // color modes: @@ -1143,15 +1123,14 @@ void vic4_render_bitplane_raster ( void ) // // VIC-III Extended attributes are applied to characters if properly set, // except in Multicolor modes. -void vic4_render_char_raster ( void ) +static void vic4_render_char_raster ( void ) { int line_char_index = 0; enable_bg_paint = 1; - if (display_row >= 0 && display_row < display_row_count) { - colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET + (display_row * CHARSTEP_BYTES); - screen_ram_current_ptr = main_ram + SCREEN_ADDR + (display_row * CHARSTEP_BYTES); - const Uint8 *row_data_base_addr = main_ram + (REG_BMM ? VIC2_BITMAP_ADDR : get_charset_effective_addr()); + Uint8 *colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET + (display_row * CHARSTEP_BYTES); + Uint8 *screen_ram_current_ptr = main_ram + SCREEN_ADDR + (display_row * CHARSTEP_BYTES); + const Uint8 *row_data_base_addr = get_charset_effective_addr(); // Account for Chargen X-displacement for (Uint32 *p = current_pixel; p < current_pixel + (CHARGEN_X_START - border_x_left); p++) *p = palette[REG_SCREEN_COLOR]; @@ -1168,8 +1147,9 @@ void vic4_render_char_raster ( void ) char_value = char_value | (*(screen_ram_current_ptr++) << 8); if (SXA_GOTO_X(color_data)) { - current_pixel = pixel_raster_start + xcounter_start + (char_value & 0x3FF); - xcounter = xcounter_start + (char_value & 0x3FF); + // FIXME: I am not sure if it cannot cause out-of-bound access later with some extreme "GOTOX" in H320 mode + xcounter = xcounter_start + (char_value & 0x3FF) * (REG_H640 ? 1 : 2); + current_pixel = pixel_raster_start + xcounter; line_char_index++; if (SXA_VERTICAL_FLIP(color_data)) @@ -1191,14 +1171,14 @@ void vic4_render_char_raster ( void ) if (SXA_VERTICAL_FLIP(color_data)) sel_char_row = 7 - char_row; if (REG_BMM) - char_byte = *(row_data_base_addr + display_row * 320 + 8 * line_char_index + sel_char_row); + char_byte = *(row_data_base_addr + display_row * 320 + 8 * line_char_index + sel_char_row); // this is BAD I guess assuming 320 pixel, can be anything ... (?) else char_byte = *(row_data_base_addr + (char_id * 8) + sel_char_row); if (SXA_HORIZONTAL_FLIP(color_data)) char_byte = reverse_byte_table[char_byte]; // LGB: I killed the function, and type-conv, as char_byte is byte, OK to index as-is // Render character cell row if (SXA_4BIT_PER_PIXEL(color_data)) { // 16-color character - vic4_render_16color_char_row(main_ram + (((char_id * 64) + (sel_char_row * 8) ) & 0x7FFFF), glyph_width); + vic4_render_16color_char_row(main_ram + (((char_id * 64) + (sel_char_row * 8) ) & 0x7FFFF), glyph_width, palette[char_bgcolor], palette + (color_data & 0xF0)); } else if (CHAR_IS256_COLOR(char_id)) { // 256-color character vic4_render_fullcolor_char_row(main_ram + (((char_id * 64) + (sel_char_row * 8) ) & 0x7FFFF), 8); } else if ((REG_MCM && (char_fgcolor & 8)) || (REG_MCM && REG_BMM)) { // Multicolor character @@ -1207,7 +1187,7 @@ void vic4_render_char_raster ( void ) REG_SCREEN_COLOR, // 00 char_value >> 4, // 01 char_value & 0xF, // 10 - color_data & 0xF // 11 + color_data & 0xF // 11 - FIXME: is this &0xF always? ie what about 256 colours, does not apply here EVER? }; vic4_render_multicolor_char_row(char_byte, glyph_width, color_source); } else { @@ -1286,12 +1266,11 @@ int vic4_render_scanline ( void ) // End of frame? if (ycounter == max_rasters) { vic4_reset_display_counters(); - screen_ram_current_ptr = main_ram + SCREEN_ADDR; - colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET; - frame_counter++; - if (frame_counter == VIC4_BLINK_INTERVAL) { - frame_counter = 0; - vic4_blink_phase = !vic4_blink_phase; + static int blink_frame_counter = 0; + blink_frame_counter++; + if (blink_frame_counter == VIC4_BLINK_INTERVAL) { + blink_frame_counter = 0; + blink_phase = !blink_phase; } return 1; } From 327c8f23fa2fca53e0365e85352faf4d7469c847 Mon Sep 17 00:00:00 2001 From: smnjameson Date: Wed, 2 Jun 2021 18:37:14 +0100 Subject: [PATCH 25/35] Added char_fetch_offset to facilitate RRB Y positioning offsets --- targets/mega65/vic4.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 9272eefd..a7382925 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -1137,6 +1137,7 @@ static void vic4_render_char_raster ( void ) current_pixel += (CHARGEN_X_START - border_x_left); xcounter += (CHARGEN_X_START - border_x_left); const int xcounter_start = xcounter; + Uint8 char_fetch_offset = 0; // Chargen starts here. while (line_char_index < REG_CHRCOUNT) { Uint16 color_data = *(colour_ram_current_ptr++); @@ -1151,7 +1152,7 @@ static void vic4_render_char_raster ( void ) xcounter = xcounter_start + (char_value & 0x3FF) * (REG_H640 ? 1 : 2); current_pixel = pixel_raster_start + xcounter; line_char_index++; - + char_fetch_offset = char_value >> 13; if (SXA_VERTICAL_FLIP(color_data)) enable_bg_paint = 0; continue; @@ -1178,9 +1179,9 @@ static void vic4_render_char_raster ( void ) char_byte = reverse_byte_table[char_byte]; // LGB: I killed the function, and type-conv, as char_byte is byte, OK to index as-is // Render character cell row if (SXA_4BIT_PER_PIXEL(color_data)) { // 16-color character - vic4_render_16color_char_row(main_ram + (((char_id * 64) + (sel_char_row * 8) ) & 0x7FFFF), glyph_width, palette[char_bgcolor], palette + (color_data & 0xF0)); + vic4_render_16color_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), glyph_width, palette[char_bgcolor], palette + (color_data & 0xF0)); } else if (CHAR_IS256_COLOR(char_id)) { // 256-color character - vic4_render_fullcolor_char_row(main_ram + (((char_id * 64) + (sel_char_row * 8) ) & 0x7FFFF), 8); + vic4_render_fullcolor_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), 8); } else if ((REG_MCM && (char_fgcolor & 8)) || (REG_MCM && REG_BMM)) { // Multicolor character if (REG_BMM) { const Uint8 color_source[4] = { From d9b1d35afbadb331aca0c9f7cb42e6cfba49865f Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:37:51 +0200 Subject: [PATCH 26/35] MEGA65: my VIC-IV updates from branch 'merger' --- targets/mega65/vic4.c | 162 ++++++++++++++++++++++++++---------------- 1 file changed, 100 insertions(+), 62 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index a7382925..5b6fe682 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -50,10 +50,13 @@ static int border_x_left= 0; // Side border left static int border_x_right= 0; // Side border right static int xcounter = 0, ycounter = 0; // video counters static int char_row = 0, display_row = 0; -static Uint8 is_fg[1024]; // this cache helps in sprite rendering, zero means background state, other value: foreground FIXME: how long this should be? really 1024? +// FIXME: really, it's 2048 now, since in H320, GOTOX value is multiplied with 2 and may overflow this array even if it's not so much used this way, we want avoid crash ... +// FIXME: should be rethought!!!! +static Uint8 is_fg[2048]; // this cache helps in sprite rendering, zero means background state, other value: foreground static float char_x_step = 0.0; static int enable_bg_paint = 1; -static int display_row_count = 0; +//static int display_row_count = 0; +#define display_row_count vic_registers[0x7B] static int max_rasters = PHYSICAL_RASTERS_DEFAULT; static int visible_area_height = SCREEN_HEIGHT_VISIBLE_DEFAULT; static int vicii_first_raster = 7; // Default for NTSC @@ -158,7 +161,7 @@ static inline void vic4_reset_display_counters ( void ) void vic_init ( void ) { vic_pixel_readback_result[0] = 0xFF; // "hyperram access count" or what, not so much emulated - // Needed to render "drive LED" feature + // Needed to render "drive LED" feature + debug pixel-read back cross-hair (only the red colour) red_colour = SDL_MapRGBA(sdl_pix_fmt, 0xFF, 0x00, 0x00, 0xFF); black_colour = SDL_MapRGBA(sdl_pix_fmt, 0x00, 0x00, 0x00, 0xFF); // Init VIC4 stuffs @@ -843,8 +846,10 @@ Uint8 vic_read_reg ( int unsigned addr ) static XEMU_INLINE void vic4_draw_sprite_row_16color( int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale ) { const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; - const int palindexbase = sprnum * 16 + 128 * (SPRITE_BITPLANE_ENABLE(sprnum) >> sprnum); - // LGB: in 16 colour sprite mode, sprite colour register gives the transparent colour index + //const int palindexbase = sprnum * 16 + 128 * (SPRITE_BITPLANE_ENABLE(sprnum) >> sprnum); + // pal16 is a pointer corrected by "palindexbase" already, so ready to be indexed with the 4 bit (16) colour + const Uint32 *pal16 = spritepalette + (sprnum * 16 + 128 * (SPRITE_BITPLANE_ENABLE(sprnum) >> sprnum)); + // in 16 colour sprite mode, sprite colour register gives the transparent colour index // We always use the lower 4 bit only at this very specific case, that's the reason for SPRITE_COLOR_4BIT() macro and not SPRITE_COLOR() [which can be 4/8 bit depending on curretn VIC mode) const Uint8 transparency_palette_index = SPRITE_COLOR_4BIT(sprnum); for (int byte = 0; byte < totalBytes; byte++) { @@ -854,13 +859,13 @@ static XEMU_INLINE void vic4_draw_sprite_row_16color( int sprnum, int x_display_ if (c0 != transparency_palette_index && x_display_pos >= border_x_left && ( !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) )) - *(pixel_raster_start + x_display_pos) = spritepalette[palindexbase + c0]; + *(pixel_raster_start + x_display_pos) = pal16[c0]; } for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { if (c1 != transparency_palette_index && x_display_pos >= border_x_left && ( !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) )) - *(pixel_raster_start + x_display_pos) = spritepalette[palindexbase + c1]; + *(pixel_raster_start + x_display_pos) = pal16[c1]; } } } @@ -958,9 +963,9 @@ static XEMU_INLINE void vic4_do_sprites ( void ) // Render a monochrome character cell row // flip = 00 Dont flip, 01 = flip vertical, 10 = flip horizontal, 11 = flip both -static void vic4_render_mono_char_row ( Uint8 char_byte, int glyph_width, Uint8 bg_color, Uint8 fg_color, Uint8 vic3attr ) +static XEMU_INLINE void vic4_render_mono_char_row ( Uint8 char_byte, const int glyph_width, const Uint8 bg_color, Uint8 fg_color, const Uint8 vic3attr ) { - if (vic3attr) { + if (XEMU_UNLIKELY(vic3attr)) { if (char_row == 7 && VIC3_ATTR_UNDERLINE(vic3attr)) char_byte = 0xFF; if (VIC3_ATTR_REVERSE(vic3attr)) @@ -970,18 +975,19 @@ static void vic4_render_mono_char_row ( Uint8 char_byte, int glyph_width, Uint8 if (VIC3_ATTR_BOLD(vic3attr)) fg_color |= 0x10; } - if (enable_bg_paint) { + const Uint32 sdl_fg_color = palette[fg_color]; + if (XEMU_LIKELY(enable_bg_paint)) { + const Uint32 sdl_bg_color = palette[bg_color]; for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { const Uint8 char_pixel = (char_byte & (0x80 >> (int)cx)); - Uint32 pixel_color = char_pixel ? palette[fg_color] : palette[bg_color]; - *(current_pixel++) = pixel_color; + *(current_pixel++) = char_pixel ? sdl_fg_color : sdl_bg_color; is_fg[xcounter++] = char_pixel; } - } else { // HACK!! to support MEGAMAZE GOTOX+VFLIP bits that ignore the background paint until next raster. + } else { for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { const Uint8 char_pixel = (char_byte & (0x80 >> (int)cx)); if (char_pixel) - *current_pixel = palette[fg_color]; + *current_pixel = sdl_fg_color; current_pixel++; is_fg[xcounter++] = char_pixel; } @@ -989,37 +995,54 @@ static void vic4_render_mono_char_row ( Uint8 char_byte, int glyph_width, Uint8 } -static inline void vic4_render_multicolor_char_row ( const Uint8 char_byte, const int glyph_width, const Uint8 color_source[4] ) +static XEMU_INLINE void vic4_render_multicolor_char_row ( const Uint8 char_byte, const int glyph_width, const Uint8 color_source[4] ) { for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { const Uint8 bitsel = 2 * (int)(cx / 2); const Uint8 bit_pair = (char_byte & (0x80 >> bitsel)) >> (6-bitsel) | (char_byte & (0x40 >> bitsel)) >> (6-bitsel); - *(current_pixel++) = palette[color_source[bit_pair]]; + if (XEMU_LIKELY(bit_pair || enable_bg_paint)) + *current_pixel = palette[color_source[bit_pair]]; + current_pixel++; is_fg[xcounter++] = (bit_pair & 2); } } // 8-bytes per row -static inline void vic4_render_fullcolor_char_row ( const Uint8* char_row, const int glyph_width ) +static XEMU_INLINE void vic4_render_fullcolor_char_row ( const Uint8* char_row, const int glyph_width, const Uint32 bg_sdl_color, const Uint32 fg_sdl_color, const int hflip ) { for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { - const Uint8 char_data = char_row[(int)cx]; - *(current_pixel++) = palette[char_data]; + const Uint8 char_data = char_row[XEMU_LIKELY(!hflip) ? (int)cx : glyph_width - 1 - (int)cx]; + if (char_data == 0xFF) + *current_pixel = fg_sdl_color; + else if (XEMU_LIKELY(char_data)) + *current_pixel = palette[char_data]; + else if (XEMU_LIKELY(enable_bg_paint)) + *current_pixel = bg_sdl_color; + current_pixel++; is_fg[xcounter++] = char_data; } } // 16-color (Nybl) mode (4-bit per pixel / 16 pixel wide characters) -static XEMU_INLINE void vic4_render_16color_char_row ( const Uint8* char_row, const int glyph_width, const Uint32 bg_sdl_color, const Uint32 *palette16 ) +static XEMU_INLINE void vic4_render_16color_char_row ( const Uint8* char_row, const int glyph_width, const Uint32 bg_sdl_color, const Uint32 *palette16, const int hflip ) { for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { - Uint8 char_data = char_row[((int)cx) / 2]; - if (((int)cx) & 1) - char_data >>= 4; - else - char_data &= 0xf; + Uint8 char_data; + if (XEMU_LIKELY(!hflip)) { + char_data = char_row[((int)cx) / 2]; + if (((int)cx) & 1) + char_data >>= 4; + else + char_data &= 0xf; + } else { + char_data = char_row[glyph_width / 2 - 1 - (((int)cx) / 2)]; + if (((int)cx) & 1) + char_data &= 0xf; + else + char_data >>= 4; + } is_fg[xcounter++] = char_data; if (char_data) *current_pixel = palette16[char_data]; @@ -1099,6 +1122,7 @@ static XEMU_INLINE Uint8 *get_charset_effective_addr ( void ) // Note: in theory on C65 there is a bit for choose between two charsets (rather than only lower/upper case) // See: https://github.com/lgblgblgb/xemu/issues/213 // However it seems even MEGA65 does not support this. + // FIXME: how we can be sure, there won't be any out-of-bound access for the relative small WOM then? if (!REG_BMM && (addr == 0x1000 || addr == 0x9000 || addr == 0x1800 || addr == 0x9800)) return char_wom + (addr & 0xFFF); // FIXME XXX this is a fixed constant for checking. @@ -1127,10 +1151,10 @@ static void vic4_render_char_raster ( void ) { int line_char_index = 0; enable_bg_paint = 1; + const Uint8 *row_data_base_addr = get_charset_effective_addr(); // FIXME: is it OK that I moved here, before the loop? if (display_row >= 0 && display_row < display_row_count) { Uint8 *colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET + (display_row * CHARSTEP_BYTES); Uint8 *screen_ram_current_ptr = main_ram + SCREEN_ADDR + (display_row * CHARSTEP_BYTES); - const Uint8 *row_data_base_addr = get_charset_effective_addr(); // Account for Chargen X-displacement for (Uint32 *p = current_pixel; p < current_pixel + (CHARGEN_X_START - border_x_left); p++) *p = palette[REG_SCREEN_COLOR]; @@ -1142,14 +1166,12 @@ static void vic4_render_char_raster ( void ) while (line_char_index < REG_CHRCOUNT) { Uint16 color_data = *(colour_ram_current_ptr++); Uint16 char_value = *(screen_ram_current_ptr++); - if (REG_16BITCHARSET) { color_data = (color_data << 8) | (*(colour_ram_current_ptr++)); char_value = char_value | (*(screen_ram_current_ptr++) << 8); - - if (SXA_GOTO_X(color_data)) { - // FIXME: I am not sure if it cannot cause out-of-bound access later with some extreme "GOTOX" in H320 mode - xcounter = xcounter_start + (char_value & 0x3FF) * (REG_H640 ? 1 : 2); + if (XEMU_UNLIKELY(SXA_GOTO_X(color_data))) { + // FIXME: I am not sure if it cannot cause out-of-bound access later in some cases, somewhere, caused by GOTOX stuff before + xcounter = xcounter_start + ((char_value & 0x3FF) << (REG_H640 ? 0 : 1)); current_pixel = pixel_raster_start + xcounter; line_char_index++; char_fetch_offset = char_value >> 13; @@ -1160,51 +1182,63 @@ static void vic4_render_char_raster ( void ) } // Background and foreground colors const Uint8 char_fgcolor = color_data & 0xF; - const Uint8 vic3_attr = REG_VICIII_ATTRIBS && !REG_MCM ? (color_data >> 4) : 0; const Uint16 char_id = REG_EBM ? (char_value & 0x3f) : char_value & 0x1fff; // up to 8192 characters (13-bit) const Uint8 char_bgcolor = REG_EBM ? vic_registers[0x21 + ((char_value >> 6) & 3)] : REG_SCREEN_COLOR; // Calculate character-width Uint8 glyph_width_deduct = SXA_TRIM_RIGHT_BITS012(char_value) + (SXA_TRIM_RIGHT_BIT3(char_value) ? 8 : 0); Uint8 glyph_width = (SXA_4BIT_PER_PIXEL(color_data) ? 16 : 8) - glyph_width_deduct; // Default fetch from char mode. - Uint8 char_byte; int sel_char_row = char_row; - if (SXA_VERTICAL_FLIP(color_data)) + if (XEMU_UNLIKELY(SXA_VERTICAL_FLIP(color_data))) sel_char_row = 7 - char_row; - if (REG_BMM) - char_byte = *(row_data_base_addr + display_row * 320 + 8 * line_char_index + sel_char_row); // this is BAD I guess assuming 320 pixel, can be anything ... (?) - else - char_byte = *(row_data_base_addr + (char_id * 8) + sel_char_row); - if (SXA_HORIZONTAL_FLIP(color_data)) - char_byte = reverse_byte_table[char_byte]; // LGB: I killed the function, and type-conv, as char_byte is byte, OK to index as-is // Render character cell row if (SXA_4BIT_PER_PIXEL(color_data)) { // 16-color character - vic4_render_16color_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), glyph_width, palette[char_bgcolor], palette + (color_data & 0xF0)); + vic4_render_16color_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), glyph_width, palette[char_bgcolor], palette + (color_data & 0xF0), SXA_HORIZONTAL_FLIP(color_data)); } else if (CHAR_IS256_COLOR(char_id)) { // 256-color character - vic4_render_fullcolor_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), 8); + // fgcolor in case of FCM should mean colour index $FF + // FIXME: check if the passed palette[char_fgcolor] is correct or another index should be used for that $FF colour stuff + vic4_render_fullcolor_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), 8, palette[char_bgcolor], palette[char_fgcolor], SXA_HORIZONTAL_FLIP(color_data)); } else if ((REG_MCM && (char_fgcolor & 8)) || (REG_MCM && REG_BMM)) { // Multicolor character + // using static vars: faster in a rapid loop like this, no need to re-adjust stack pointer all the time to allocate space and this way using constant memory address + // also, as an optimization, later, some value can be re-used and not always initialized here, when in reality VIC + // registers in current Xemu cannot change within a scanline anyway (ie, scanline precision based emulation/rendering) + static Uint8 color_source_mcm[4]; + Uint8 char_byte; + color_source_mcm[0] = REG_SCREEN_COLOR; if (REG_BMM) { - const Uint8 color_source[4] = { - REG_SCREEN_COLOR, // 00 - char_value >> 4, // 01 - char_value & 0xF, // 10 - color_data & 0xF // 11 - FIXME: is this &0xF always? ie what about 256 colours, does not apply here EVER? - }; - vic4_render_multicolor_char_row(char_byte, glyph_width, color_source); + // value 00 is common /w or w/o BMM so not initialized here + color_source_mcm[1] = char_value >> 4; // 01 + color_source_mcm[2] = char_value & 0xF; // 10 + color_source_mcm[3] = color_data & 0xF; // 11 + char_byte = *(row_data_base_addr + display_row * 320 + 8 * line_char_index + sel_char_row); // this is BAD I guess assuming 320 pixel, can be anything ... (?) } else { - const Uint8 color_source[4] = { - REG_SCREEN_COLOR, // 00 - REG_MULTICOLOR_1, // 01 - REG_MULTICOLOR_2, // 10 - char_fgcolor & 7 // 11 - }; - vic4_render_multicolor_char_row(char_byte, glyph_width, color_source); + // value 00 is common /w or w/o BMM so not initialized here + color_source_mcm[1] = REG_MULTICOLOR_1; // 01 + color_source_mcm[2] = REG_MULTICOLOR_2; // 10 + color_source_mcm[3] = char_fgcolor & 7; // 11 + char_byte = *(row_data_base_addr + (char_id * 8) + sel_char_row); } + // FIXME: is this really a thing to have FLIP in bitmap mode AS WELL?! + // FIXME: also this is WRONG, MCM data cannot be reversed with this table!! + if (XEMU_UNLIKELY(SXA_HORIZONTAL_FLIP(color_data))) + char_byte = reverse_byte_table[char_byte]; + vic4_render_multicolor_char_row(char_byte, glyph_width, color_source_mcm); } else { // Single color character - if (!REG_BMM) - vic4_render_mono_char_row(char_byte, glyph_width, char_bgcolor, char_fgcolor, vic3_attr); - else - vic4_render_mono_char_row(char_byte, glyph_width, char_value & 0xF, char_value >> 4, vic3_attr ); + Uint8 char_byte, char_bgcolor_now, char_fgcolor_now; + if (!REG_BMM) { + char_bgcolor_now = char_bgcolor; + char_fgcolor_now = char_fgcolor; + char_byte = *(row_data_base_addr + (char_id * 8) + sel_char_row); + } else { + char_bgcolor_now = char_value & 0xF; + char_fgcolor_now = char_value >> 4; + char_byte = *(row_data_base_addr + display_row * 320 + 8 * line_char_index + sel_char_row); // this is BAD I guess assuming 320 pixel, can be anything ... (?) + } + // FIXME: is this really a thing to have FLIP in bitmap mode AS WELL?! + if (XEMU_UNLIKELY(SXA_HORIZONTAL_FLIP(color_data))) + char_byte = reverse_byte_table[char_byte]; + // FIXME: do vic3 attributes work with bitmap mode as well??? + vic4_render_mono_char_row(char_byte, glyph_width, char_bgcolor_now, char_fgcolor_now, (REG_VICIII_ATTRIBS && !REG_MCM) ? (color_data >> 4) : 0); } line_char_index++; } @@ -1233,9 +1267,13 @@ int vic4_render_scanline ( void ) if (!(ycounter & 1)) // VIC-II raster source: We shall check FNRST ? vic4_check_raster_interrupt(logical_raster); // "Double-scan hack" + // FIXME: is this really correct? ie even sprites cannot be set to Y pos finer than V200 or ... + // ... having resolution finer than V200 with some "VIC-IV magic"? if (!REG_V400 && (ycounter & 1)) { - for (int i = 0; i < TEXTURE_WIDTH; i++, current_pixel++) - *current_pixel = /* user_scanlines_setting ? 0 : */ *(current_pixel - TEXTURE_WIDTH) ; + //for (int i = 0; i < TEXTURE_WIDTH; i++, current_pixel++) + // *current_pixel = /* user_scanlines_setting ? 0 : */ *(current_pixel - TEXTURE_WIDTH); + memcpy(current_pixel, current_pixel - TEXTURE_WIDTH, TEXTURE_WIDTH * 4); + current_pixel += TEXTURE_WIDTH; } else { // Top and bottom borders if (ycounter < BORDER_Y_TOP || ycounter >= BORDER_Y_BOTTOM || !REG_DISPLAYENABLE) { From b0924ca13b083197f77be8e60774dd6a0e60df91 Mon Sep 17 00:00:00 2001 From: smnjameson Date: Mon, 7 Jun 2021 18:18:36 +0100 Subject: [PATCH 27/35] Fix #273 - Added check for alternate palette in NCM GOTOX (#274) * Added check for alternate palette in NCM RRB GOTOX * Added check for alternate palette on GOTOX when using RRB in NCM mode * removed my local build.sh script .. doh Co-authored-by: smnjameson --- targets/mega65/vic4.c | 7 ++++++- targets/mega65/vic4.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 5b6fe682..1e8577fe 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -1162,6 +1162,7 @@ static void vic4_render_char_raster ( void ) xcounter += (CHARGEN_X_START - border_x_left); const int xcounter_start = xcounter; Uint8 char_fetch_offset = 0; + int ncm_alt_palette = 0; // Chargen starts here. while (line_char_index < REG_CHRCOUNT) { Uint16 color_data = *(colour_ram_current_ptr++); @@ -1177,6 +1178,10 @@ static void vic4_render_char_raster ( void ) char_fetch_offset = char_value >> 13; if (SXA_VERTICAL_FLIP(color_data)) enable_bg_paint = 0; + if (SXA_ATTR_BOLD(color_data) && SXA_ATTR_REVERSE(color_data) && !REG_VICIII_ATTRIBS) + ncm_alt_palette = 1; + else + ncm_alt_palette = 0; continue; } } @@ -1193,7 +1198,7 @@ static void vic4_render_char_raster ( void ) sel_char_row = 7 - char_row; // Render character cell row if (SXA_4BIT_PER_PIXEL(color_data)) { // 16-color character - vic4_render_16color_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), glyph_width, palette[char_bgcolor], palette + (color_data & 0xF0), SXA_HORIZONTAL_FLIP(color_data)); + vic4_render_16color_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), glyph_width, palette[char_bgcolor], (ncm_alt_palette ? altpalette : palette) + (color_data & 0xF0), SXA_HORIZONTAL_FLIP(color_data)); } else if (CHAR_IS256_COLOR(char_id)) { // 256-color character // fgcolor in case of FCM should mean colour index $FF // FIXME: check if the passed palette[char_fgcolor] is correct or another index should be used for that $FF colour stuff diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index 15f7f122..0abd7aca 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -171,6 +171,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SXA_GOTO_X(cw) ((cw) & 0x1000) #define SXA_4BIT_PER_PIXEL(cw) ((cw) & 0x0800) #define SXA_TRIM_RIGHT_BIT3(cw) ((cw) & 0x0400) +#define SXA_ATTR_BOLD(cw) ((cw) & 0x0040) +#define SXA_ATTR_REVERSE(cw) ((cw) & 0x0020) //FIXME: this last one was bad, and also, seems to be not used? //#define SXA_TRIM_TOP_BOTTOM(cw) (((cw) & 0x0300) >> 8) From 16351684de5ca36198db8e757fa6b2690e7827a2 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:39:48 +0200 Subject: [PATCH 28/35] MEGA65: my VIC-IV updates from branch 'merger' --- targets/mega65/vic4.c | 48 ++++++++++++++++++++++++++++++++----------- targets/mega65/vic4.h | 23 ++++++++------------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 1e8577fe..007b0409 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -65,6 +65,7 @@ static Uint8 *bitplane_p[8]; static Uint32 red_colour, black_colour; // used by "drive LED", and cross-hair (only the red) for debug pixel read static Uint8 vic_pixel_readback_result[4]; static Uint8 vic_color_register_mask = 0xFF; +static Uint32 *used_palette; // normally the same value as "palette" from vic4_palette.c but GOTOX RRB token can modify this! So this should be used // --- these things are altered by vic4_open_frame_access() ONLY at every fame ONLY based on PAL or NTSC selection Uint8 videostd_id = 0xFF; // 0=PAL, 1=NTSC [give some insane value by default to force the change at the fist frame after starting Xemu] @@ -975,9 +976,9 @@ static XEMU_INLINE void vic4_render_mono_char_row ( Uint8 char_byte, const int g if (VIC3_ATTR_BOLD(vic3attr)) fg_color |= 0x10; } - const Uint32 sdl_fg_color = palette[fg_color]; + const Uint32 sdl_fg_color = used_palette[fg_color]; if (XEMU_LIKELY(enable_bg_paint)) { - const Uint32 sdl_bg_color = palette[bg_color]; + const Uint32 sdl_bg_color = used_palette[bg_color]; for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) { const Uint8 char_pixel = (char_byte & (0x80 >> (int)cx)); *(current_pixel++) = char_pixel ? sdl_fg_color : sdl_bg_color; @@ -1001,7 +1002,7 @@ static XEMU_INLINE void vic4_render_multicolor_char_row ( const Uint8 char_byte, const Uint8 bitsel = 2 * (int)(cx / 2); const Uint8 bit_pair = (char_byte & (0x80 >> bitsel)) >> (6-bitsel) | (char_byte & (0x40 >> bitsel)) >> (6-bitsel); if (XEMU_LIKELY(bit_pair || enable_bg_paint)) - *current_pixel = palette[color_source[bit_pair]]; + *current_pixel = used_palette[color_source[bit_pair]]; current_pixel++; is_fg[xcounter++] = (bit_pair & 2); } @@ -1016,7 +1017,7 @@ static XEMU_INLINE void vic4_render_fullcolor_char_row ( const Uint8* char_row, if (char_data == 0xFF) *current_pixel = fg_sdl_color; else if (XEMU_LIKELY(char_data)) - *current_pixel = palette[char_data]; + *current_pixel = used_palette[char_data]; else if (XEMU_LIKELY(enable_bg_paint)) *current_pixel = bg_sdl_color; current_pixel++; @@ -1162,7 +1163,6 @@ static void vic4_render_char_raster ( void ) xcounter += (CHARGEN_X_START - border_x_left); const int xcounter_start = xcounter; Uint8 char_fetch_offset = 0; - int ncm_alt_palette = 0; // Chargen starts here. while (line_char_index < REG_CHRCOUNT) { Uint16 color_data = *(colour_ram_current_ptr++); @@ -1171,17 +1171,39 @@ static void vic4_render_char_raster ( void ) color_data = (color_data << 8) | (*(colour_ram_current_ptr++)); char_value = char_value | (*(screen_ram_current_ptr++) << 8); if (XEMU_UNLIKELY(SXA_GOTO_X(color_data))) { - // FIXME: I am not sure if it cannot cause out-of-bound access later in some cases, somewhere, caused by GOTOX stuff before - xcounter = xcounter_start + ((char_value & 0x3FF) << (REG_H640 ? 0 : 1)); + // Start of the GOTOX re-positioning functionality implementation, tricky one. + xcounter = (char_value & 0x3FF); // first, extract the goto to X value as an usigned number + if (REG_H640) { + // Interpret as a "negative" value compared to xcounter_start if it would fit into the real range of 0-xcounter_start, + // otherwise interpret that as a positive offset compared to xcounter_start + if (0x3FF - xcounter < xcounter_start) + xcounter = xcounter_start - (0x3FF - xcounter); + else + xcounter += xcounter_start; + } else { + xcounter <<= 1; // multiply by 2, if !H640 (as the pixel is double width for lower resolution) + if (0x7FE - xcounter < xcounter_start) + xcounter = xcounter_start - (0x7FE - xcounter); + else + xcounter += xcounter_start; + } + // The ugly: too large goto X values may cause out-of-bound access on eg is_fg buffer. Thus, if the result is larger than + // the width of the SDL texture, it won't be seen anyway, so we "clamp" it for the NEXT raster as an ugly solution, which + // will be overwritten anyway on rendering in the next raster. This way we don't need checking of out-of-bound access (faster + // code). + if (xcounter > TEXTURE_WIDTH) + xcounter = TEXTURE_WIDTH; + // Align current_pixel pointer according the calculated xcounter "horror show" above current_pixel = pixel_raster_start + xcounter; + // End of the GOTOX re-positioning functionality implementation line_char_index++; char_fetch_offset = char_value >> 13; if (SXA_VERTICAL_FLIP(color_data)) enable_bg_paint = 0; if (SXA_ATTR_BOLD(color_data) && SXA_ATTR_REVERSE(color_data) && !REG_VICIII_ATTRIBS) - ncm_alt_palette = 1; - else - ncm_alt_palette = 0; + used_palette = altpalette; // use the alternate palette from now in the scanline + else + used_palette = palette; // we do this as well, since there can be "double GOTOX" so we want back to "original" palette ... continue; } } @@ -1198,11 +1220,11 @@ static void vic4_render_char_raster ( void ) sel_char_row = 7 - char_row; // Render character cell row if (SXA_4BIT_PER_PIXEL(color_data)) { // 16-color character - vic4_render_16color_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), glyph_width, palette[char_bgcolor], (ncm_alt_palette ? altpalette : palette) + (color_data & 0xF0), SXA_HORIZONTAL_FLIP(color_data)); + vic4_render_16color_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), glyph_width, used_palette[char_bgcolor], used_palette + (color_data & 0xF0), SXA_HORIZONTAL_FLIP(color_data)); } else if (CHAR_IS256_COLOR(char_id)) { // 256-color character // fgcolor in case of FCM should mean colour index $FF // FIXME: check if the passed palette[char_fgcolor] is correct or another index should be used for that $FF colour stuff - vic4_render_fullcolor_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), 8, palette[char_bgcolor], palette[char_fgcolor], SXA_HORIZONTAL_FLIP(color_data)); + vic4_render_fullcolor_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), 8, used_palette[char_bgcolor], used_palette[char_fgcolor], SXA_HORIZONTAL_FLIP(color_data)); } else if ((REG_MCM && (char_fgcolor & 8)) || (REG_MCM && REG_BMM)) { // Multicolor character // using static vars: faster in a rapid loop like this, no need to re-adjust stack pointer all the time to allocate space and this way using constant memory address // also, as an optimization, later, some value can be re-used and not always initialized here, when in reality VIC @@ -1262,6 +1284,7 @@ int vic4_render_scanline ( void ) { // Work this first. DO NOT OPTIMIZE EARLY. + used_palette = palette; // may be overriden later by GOTOX token! xcounter = 0; current_pixel = pixel_start + ycounter * TEXTURE_WIDTH; pixel_raster_start = current_pixel; @@ -1295,6 +1318,7 @@ int vic4_render_scanline ( void ) vic4_do_sprites(); } // Paint screen color if positive y-offset (CHARGEN_Y_START > BORDER_Y_TOP) + // FIXME: in case of changed palette by GOTOX, maybe this must be dependent on bg_paint to use the new palette or the old?? if (ycounter >= BORDER_Y_TOP && ycounter < CHARGEN_Y_START) { while (xcounter++ < border_x_right) *current_pixel++ = palette[REG_SCREEN_COLOR]; diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index 0abd7aca..9c381d8f 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -62,11 +62,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_MCM (vic_registers[0x16] & 0x10) #define REG_BMM (vic_registers[0x11] & 0x20) #define REG_SPRITE_ENABLE vic_registers[0x15] -#define REG_BORDER_COLOR vic_registers[0x20] -#define REG_SCREEN_COLOR vic_registers[0x21] -#define REG_MULTICOLOR_1 vic_registers[0x22] -#define REG_MULTICOLOR_2 vic_registers[0x23] -#define REG_MULTICOLOR_3 vic_registers[0x24] +#define REG_BORDER_COLOR (vic_registers[0x20] & vic_color_register_mask) +#define REG_SCREEN_COLOR (vic_registers[0x21] & vic_color_register_mask) +#define REG_MULTICOLOR_1 (vic_registers[0x22] & vic_color_register_mask) +#define REG_MULTICOLOR_2 (vic_registers[0x23] & vic_color_register_mask) +#define REG_MULTICOLOR_3 (vic_registers[0x24] & vic_color_register_mask) #define REG_H640 (vic_registers[0x31] & 128) #define REG_V400 (vic_registers[0x31] & 8) #define REG_VICIII_ATTRIBS (vic_registers[0x31] & 0x20) @@ -140,9 +140,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SCREEN_STEP (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) #define SPRITE_POS_Y(n) (vic_registers[1 + (n)*2]) #define SPRITE_POS_X(n) (((Uint16)vic_registers[(n)*2]) | ( (vic_registers[0x10] & (1 << (n)) ? 0x100 : 0))) -#define SPRITE_COLOR(n) (vic_registers[0x27+(n)] & 0xF) -#define SPRITE_MULTICOLOR_1 (vic_registers[0x25] & 0xF) -#define SPRITE_MULTICOLOR_2 (vic_registers[0x26] & 0xF) +#define SPRITE_COLOR(n) (vic_registers[0x27+(n)] & vic_color_register_mask) +#define SPRITE_COLOR_4BIT(n) (vic_registers[0x27+(n)] & 0xF) +#define SPRITE_MULTICOLOR_1 (vic_registers[0x25] & vic_color_register_mask) +#define SPRITE_MULTICOLOR_2 (vic_registers[0x26] & vic_color_register_mask) #define SPRITE_IS_BACK(n) (vic_registers[0x1B] & (1 << (n))) #define SPRITE_HORZ_2X(n) (vic_registers[0x1D] & (1 << (n))) #define SPRITE_VERT_2X(n) (vic_registers[0x17] & (1 << (n))) @@ -217,11 +218,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SET_COLORRAM_BASE(x) SET_16BIT_REG(0x64,(x)) #define SET_CHARSTEP_BYTES(x) SET_16BIT_REG(0x58,(x)) -// Pixel foreground/background indicator for aiding in sprite rendering - -#define FOREGROUND_PIXEL 1 -#define BACKGROUND_PIXEL 0 - // Review this! (VIC-II values) #define SPRITE_X_BASE_COORD 24 @@ -234,7 +230,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ extern int vic_iomode; //extern int scanline; extern Uint8 vic_registers[]; -extern int force_fast; extern Uint8 c128_d030_reg; extern const char *videostd_name; From 4a68c694f6bf036207911520ca688b240079fc66 Mon Sep 17 00:00:00 2001 From: smnjameson Date: Mon, 7 Jun 2021 23:57:58 +0100 Subject: [PATCH 29/35] MEGA65 VIC-IV fix for horizontally tiled sprites (#276) * Added check for alternate palette in NCM RRB GOTOX * Added check for alternate palette on GOTOX when using RRB in NCM mode * removed my local build.sh script .. doh * Fix for horizontally tiled sprites Co-authored-by: smnjameson --- targets/mega65/vic4.c | 32 +++++++++++++++++--------------- targets/mega65/vic4.h | 1 + 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 007b0409..cce4a57c 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -853,22 +853,24 @@ static XEMU_INLINE void vic4_draw_sprite_row_16color( int sprnum, int x_display_ // in 16 colour sprite mode, sprite colour register gives the transparent colour index // We always use the lower 4 bit only at this very specific case, that's the reason for SPRITE_COLOR_4BIT() macro and not SPRITE_COLOR() [which can be 4/8 bit depending on curretn VIC mode) const Uint8 transparency_palette_index = SPRITE_COLOR_4BIT(sprnum); - for (int byte = 0; byte < totalBytes; byte++) { - const Uint8 c0 = (*(row_data_ptr + byte)) >> 4; - const Uint8 c1 = (*(row_data_ptr + byte)) & 0xF; - for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { - if (c0 != transparency_palette_index && x_display_pos >= border_x_left && ( - !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) - )) - *(pixel_raster_start + x_display_pos) = pal16[c0]; - } - for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { - if (c1 != transparency_palette_index && x_display_pos >= border_x_left && ( - !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) - )) - *(pixel_raster_start + x_display_pos) = pal16[c1]; + do { + for (int byte = 0; byte < totalBytes; byte++) { + const Uint8 c0 = (*(row_data_ptr + byte)) >> 4; + const Uint8 c1 = (*(row_data_ptr + byte)) & 0xF; + for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { + if (c0 != transparency_palette_index && x_display_pos >= border_x_left && ( + !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) + )) + *(pixel_raster_start + x_display_pos) = pal16[c0]; + } + for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { + if (c1 != transparency_palette_index && x_display_pos >= border_x_left && ( + !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) + )) + *(pixel_raster_start + x_display_pos) = pal16[c1]; + } } - } + } while ((REG_SPRTILEN & (1 << sprnum)) && x_display_pos < border_x_right); } diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index 9c381d8f..b4890f39 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -84,6 +84,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_BBRDPOS_U4 (vic_registers[0x4B] & 0xF) #define REG_TEXTXPOS (vic_registers[0x4C]) #define REG_TEXTXPOS_U4 (vic_registers[0x4D] & 0xF) +#define REG_SPRTILEN ((vic_registers[0x4D] & 0xF0) >> 4 | (vic_registers[0x4F] & 0xF0)) #define REG_TEXTYPOS (vic_registers[0x4E]) #define REG_TEXTYPOS_U4 (vic_registers[0x4F] & 0xF) #define REG_XPOS (vic_registers[0x51]) From 0aaafd799e05425c884ce8473a5c2a31bc9e43fb Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:41:13 +0200 Subject: [PATCH 30/35] MEGA65: my VIC-IV updates from branch 'merger' --- targets/mega65/vic4.c | 257 ++++++++++++++++++++++++++++++++---------- targets/mega65/vic4.h | 4 +- 2 files changed, 197 insertions(+), 64 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index cce4a57c..14e35772 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -30,6 +30,20 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "io_mapper.h" +#define SPRITE_SPRITE_COLLISION +#define SPRITE_FG_COLLISION + + +#ifdef XEMU_RELEASE_BUILD +# ifdef SPRITE_SPRITE_COLLISION +# undef SPRITE_SPRITE_COLLISION +# endif +# ifdef SPRITE_FG_COLLISION +# undef SPRITE_FG_COLLISION +# endif +#endif + + const char *iomode_names[4] = { "VIC2", "VIC3", "BAD!", "VIC4" }; // (SDL) target texture rendering pointers @@ -53,6 +67,9 @@ static int char_row = 0, display_row = 0; // FIXME: really, it's 2048 now, since in H320, GOTOX value is multiplied with 2 and may overflow this array even if it's not so much used this way, we want avoid crash ... // FIXME: should be rethought!!!! static Uint8 is_fg[2048]; // this cache helps in sprite rendering, zero means background state, other value: foreground +#ifdef SPRITE_SPRITE_COLLISION +static Uint8 is_sprite[1024]; +#endif static float char_x_step = 0.0; static int enable_bg_paint = 1; //static int display_row_count = 0; @@ -66,6 +83,7 @@ static Uint32 red_colour, black_colour; // used by "drive LED", and cross-hai static Uint8 vic_pixel_readback_result[4]; static Uint8 vic_color_register_mask = 0xFF; static Uint32 *used_palette; // normally the same value as "palette" from vic4_palette.c but GOTOX RRB token can modify this! So this should be used +static int EFFECTIVE_V400; // --- these things are altered by vic4_open_frame_access() ONLY at every fame ONLY based on PAL or NTSC selection Uint8 videostd_id = 0xFF; // 0=PAL, 1=NTSC [give some insane value by default to force the change at the fist frame after starting Xemu] @@ -147,6 +165,9 @@ void vic_reset ( void ) vic_registers[0x7D] = 0xFF; vic_registers[0x7E] = 0xFF; vic_registers[0x7F] = 0xFF; + // turn off possible remained sprite collision info + vic_registers[0x1E] = 0; + vic_registers[0x1F] = 0; } @@ -205,6 +226,7 @@ static XEMU_INLINE void pixel_readback ( void ) // Do NOT call this function from vic4.c! It must be used by the emulator's main loop! void vic4_close_frame_access ( void ) { + DEBUG("FRAME CLOSED" NL); // Debug pixel-read back feature of MEGA65 pixel_readback(); #ifdef XEMU_FILES_SCREENSHOT_SUPPORT @@ -273,7 +295,7 @@ static void vic4_update_vertical_borders( void ) else // 78-col mode SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL) - 2); } - if (!REG_V400) { // Standard mode (200-lines) + if (!EFFECTIVE_V400) { // Standard mode (200-lines) if (REG_RSEL) { // 25-row SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster)); SET_BORDER_Y_BOTTOM(RASTER_CORRECTION + DISPLAY_HEIGHT - SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) - 1); @@ -319,7 +341,7 @@ static void vic4_interpret_legacy_mode_registers ( void ) REG_SPRPTR_B0 = 0xF8; REG_SPRPTR_B1 = (reg_d018_screen_addr << 2) | 0x3; - if (REG_H640 | REG_V400) + if (REG_H640 | EFFECTIVE_V400) REG_SPRPTR_B1 |= 4; vic_registers[0x6E] &= 128; @@ -344,6 +366,9 @@ void vic4_open_frame_access ( void ) current_pixel = pixel_start = xemu_start_pixel_buffer_access(&tail_sdl); if (XEMU_UNLIKELY(tail_sdl)) FATAL("tail_sdl is not zero!"); + // The V400 hack ... + // V400 + Yscale=0 + Bit6 of $D051 is handled as V200 ... + EFFECTIVE_V400 = (REG_V400 && REG_CHRYSCL == 0 && (vic_registers[0x51] & 0x40)) ? 0 : !!REG_V400; // Now check the video mode: NTSC or PAL // Though it can be changed any time, this kind of information really only can be applied // at frame level. Thus we check here, if during the previous frame there was change @@ -396,7 +421,7 @@ void vic4_open_frame_access ( void ) } -static void vic4_interrupt_checker ( void ) +static void interrupt_checker ( void ) { int vic_irq_old = cpu65.irqLevel & 2; int vic_irq_new; @@ -417,17 +442,17 @@ static void vic4_interrupt_checker ( void ) } -static void vic4_check_raster_interrupt ( int nraster ) +static inline void check_raster_interrupt ( int nraster ) { if (nraster == compare_raster) interrupt_status |= 1; else interrupt_status &= 0xFE; - vic4_interrupt_checker(); + interrupt_checker(); } -inline static void vic4_calculate_char_x_step ( void ) +static inline void calculate_char_x_step ( void ) { char_x_step = (REG_CHARXSCALE / 120.0f) / (REG_H640 ? 1 : 2); } @@ -442,11 +467,26 @@ static XEMU_INLINE Uint8 *get_dat_addr ( unsigned int bpn ) unsigned int x = vic_registers[0x3C]; unsigned int y = vic_registers[0x3D] + ((x << 1) & 0x100); unsigned int h640 = (vic_registers[0x31] & 128); + unsigned int and_mask, bit_shifter; x &= 0x7F; //DEBUGPRINT("VIC-IV: DAT: accessing DAT for bitplane #%u at X,Y of %u,%u in H%u mode" NL, bpn, x, y, h640 ? 640 : 320); + // In V400 modes, odd/even scanlines should be considered as well! + if (EFFECTIVE_V400) { + if ((y & 1)) { + and_mask = h640 ? 12 << 4 : 14 << 4; + bit_shifter = 12 - 4; + } else { + and_mask = h640 ? 12 : 14 ; + bit_shifter = 12; + } + y >>= 1; + } else { + and_mask = h640 ? 12 : 14; + bit_shifter = 12; + } return - bitplane_bank_p + // MEGA65 feature (WANNABE feature!) to support relocatable bitplane bank by the DAT! (this is a pointer, not an integer!) - ((vic_registers[0x33 + bpn] & (h640 ? 12 : 14)) << 12) + // bitplane address + bitplane_bank_p + // MEGA65 feature to support relocatable bitplane bank by the DAT! (this is a pointer, not an integer!) + ((vic_registers[0x33 + bpn] & and_mask) << bit_shifter) + // bitplane address ((bpn & 1) ? 0x10000 : 0) + // odd/even bitplane selection (((y >> 3) * (h640 ? 640 : 320)) + (x << 3) + (y & 7)) // position within the bitplane given by the X/Y info ; @@ -531,7 +571,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) break; CASE_VIC_ALL(0x19): interrupt_status = interrupt_status & (~data) & 0xF; - vic4_interrupt_checker(); + interrupt_checker(); break; CASE_VIC_ALL(0x1A): data &= 0xF; @@ -596,7 +636,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) vic_registers[0x31] = data; // we need this work-around, since reg-write happens _after_ this switch statement, but machine_set_speed above needs it ... machine_set_speed(0); - vic4_calculate_char_x_step(); + calculate_char_x_step(); break; // we did the write, but we need to trigger vichot_reg if should CASE_VIC_3_4(0x32): CASE_VIC_3_4(0x33): CASE_VIC_3_4(0x34): CASE_VIC_3_4(0x35): CASE_VIC_3_4(0x36): CASE_VIC_3_4(0x37): CASE_VIC_3_4(0x38): @@ -611,8 +651,13 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) CASE_VIC_4(0x48): CASE_VIC_4(0x49): CASE_VIC_4(0x4A): CASE_VIC_4(0x4B): CASE_VIC_4(0x4C): CASE_VIC_4(0x4D): CASE_VIC_4(0x4E): CASE_VIC_4(0x4F): break; - CASE_VIC_4(0x50): CASE_VIC_4(0x51): - return; // Writing to XPOS register is no-op + CASE_VIC_4(0x50): + // Writing to XPOS register is no-op + return; + CASE_VIC_4(0x51): + // Writing to XPOS register (high bits) is no-op, BUT the two top bits are writable! + vic_registers[0x51] = (data & 0xC0) | (vic_registers[0x51] & 0x3F); + return; CASE_VIC_4(0x52): CASE_VIC_4(0x53): break; CASE_VIC_4(0x54): @@ -626,7 +671,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) CASE_VIC_4(0x5A): //DEBUGPRINT("WRITE $%04x CHARXSCALE: $%02x" NL, addr, data); vic_registers[0x5A] = data; // write now and calculate step - vic4_calculate_char_x_step(); + calculate_char_x_step(); return; CASE_VIC_4(0x5B): break; @@ -757,6 +802,7 @@ Uint8 vic_read_reg ( int unsigned addr ) CASE_VIC_ALL(0x1E): // sprite-sprite collision CASE_VIC_ALL(0x1F): // sprite-data collision vic_registers[addr & 0x7F] = 0; // 1E and 1F registers are cleared on read! + // FIXME: needs to re-check interrupts! break; CASE_VIC_2(0x20): CASE_VIC_2(0x21): CASE_VIC_2(0x22): CASE_VIC_2(0x23): CASE_VIC_2(0x24): CASE_VIC_2(0x25): CASE_VIC_2(0x26): CASE_VIC_2(0x27): CASE_VIC_2(0x28): CASE_VIC_2(0x29): CASE_VIC_2(0x2A): CASE_VIC_2(0x2B): CASE_VIC_2(0x2C): CASE_VIC_2(0x2D): CASE_VIC_2(0x2E): @@ -789,9 +835,14 @@ Uint8 vic_read_reg ( int unsigned addr ) /* --- NO MORE VIC-III REGS FROM HERE --- */ CASE_VIC_4(0x48): CASE_VIC_4(0x49): CASE_VIC_4(0x4A): CASE_VIC_4(0x4B): CASE_VIC_4(0x4C): CASE_VIC_4(0x4D): CASE_VIC_4(0x4E): CASE_VIC_4(0x4F): CASE_VIC_4(0x50): + // XPOS low byte break; CASE_VIC_4(0x51): - result = vic_registers[0x51]++; + // XPOS high bits + others + // FIXME XXX super ugly hack to have something XPOS register changing. (some programs wait that to be changed) + // Note, that bit 6 and 7 is different and not part of the XPOS info. + result = (result & 0xC0) | ((result + 1) & 0x3F); + vic_registers[0x51] = result; break; CASE_VIC_4(0x52): CASE_VIC_4(0x53): break; @@ -836,7 +887,6 @@ Uint8 vic_read_reg ( int unsigned addr ) return result; } - #undef CASE_VIC_2 #undef CASE_VIC_3 #undef CASE_VIC_4 @@ -844,7 +894,42 @@ Uint8 vic_read_reg ( int unsigned addr ) #undef CASE_VIC_3_4 -static XEMU_INLINE void vic4_draw_sprite_row_16color( int sprnum, int x_display_pos, const Uint8* row_data_ptr, int xscale ) + +#ifdef SPRITE_SPRITE_COLLISION +# warning "Sprite-sprite collision is an experimental feature (SPRITE_SPRITE_COLLISION is defined)!" +# define DO_SPRITE_SPRITE_COLLISION(pos,cond) do { \ + if (cond) { \ + const Uint8 sp = is_sprite[pos]; \ + is_sprite[pos] = sp | sprbmask; \ + if (sp) \ + vic_registers[0x1E] |= sp | sprbmask; \ + } \ + } while (0) +#ifndef SPRITE_ANY_COLLISION +#define SPRITE_ANY_COLLISION +#endif +#else + // dummy macro for the case when SPRITE_SPRITE_COLLISION was not requested +# define DO_SPRITE_SPRITE_COLLISION(pos,cond) +#endif + + +#ifdef SPRITE_FG_COLLISION +# warning "Sprite-foreground collision is an experimental feature (SPRITE_FG_COLLISION is defined)!" +# define DO_SPRITE_FG_COLLISION(pos,cond) do { \ + if (is_fg[pos] && (cond)) \ + vic_registers[0x1F] |= sprbmask; \ + } while (0) +#ifndef SPRITE_ANY_COLLISION +#define SPRITE_ANY_COLLISION +#endif +#else + // dummy macro for the case when SPRITE_FG_COLLISION was not requested +# define DO_SPRITE_FG_COLLISION(pos,cond) +#endif + + +static XEMU_INLINE void vic4_draw_sprite_row_16color ( const int sprnum, int x_display_pos, const Uint8* row_data_ptr, const int xscale, const int do_tiling ) { const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; //const int palindexbase = sprnum * 16 + 128 * (SPRITE_BITPLANE_ENABLE(sprnum) >> sprnum); @@ -853,6 +938,9 @@ static XEMU_INLINE void vic4_draw_sprite_row_16color( int sprnum, int x_display_ // in 16 colour sprite mode, sprite colour register gives the transparent colour index // We always use the lower 4 bit only at this very specific case, that's the reason for SPRITE_COLOR_4BIT() macro and not SPRITE_COLOR() [which can be 4/8 bit depending on curretn VIC mode) const Uint8 transparency_palette_index = SPRITE_COLOR_4BIT(sprnum); +# ifdef SPRITE_ANY_COLLISION + const Uint8 sprbmask = 1 << sprnum; +# endif do { for (int byte = 0; byte < totalBytes; byte++) { const Uint8 c0 = (*(row_data_ptr + byte)) >> 4; @@ -860,17 +948,23 @@ static XEMU_INLINE void vic4_draw_sprite_row_16color( int sprnum, int x_display_ for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { if (c0 != transparency_palette_index && x_display_pos >= border_x_left && ( !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) - )) + )) { *(pixel_raster_start + x_display_pos) = pal16[c0]; + DO_SPRITE_SPRITE_COLLISION(x_display_pos, 1); + DO_SPRITE_FG_COLLISION(x_display_pos, 1); + } } for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) { if (c1 != transparency_palette_index && x_display_pos >= border_x_left && ( !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) - )) + )) { *(pixel_raster_start + x_display_pos) = pal16[c1]; + DO_SPRITE_SPRITE_COLLISION(x_display_pos, 1); + DO_SPRITE_FG_COLLISION(x_display_pos, 1); + } } } - } while ((REG_SPRTILEN & (1 << sprnum)) && x_display_pos < border_x_right); + } while (XEMU_UNLIKELY(do_tiling && x_display_pos < border_x_right)); } @@ -878,6 +972,9 @@ static XEMU_INLINE void vic4_draw_sprite_row_multicolor ( int sprnum, int x_disp { const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; const Uint8 mcm_spr_pal_indices[4] = { 0, SPRITE_MULTICOLOR_1, SPRITE_COLOR(sprnum), SPRITE_MULTICOLOR_2 }; // entry zero is not used +# ifdef SPRITE_ANY_COLLISION + const Uint8 sprbmask = 1 << sprnum; +# endif for (int byte = 0; byte < totalBytes; byte++) { const Uint8 row_data = *row_data_ptr++; for (int shift = 6; shift >= 0; shift -= 2) { @@ -887,12 +984,18 @@ static XEMU_INLINE void vic4_draw_sprite_row_multicolor ( int sprnum, int x_disp if (mcm_pixel_value) { if (x_display_pos >= border_x_left && ( !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) - )) + )) { *(pixel_raster_start + x_display_pos) = sdl_pixel; + DO_SPRITE_SPRITE_COLLISION(x_display_pos, mcm_pixel_value & 2); + DO_SPRITE_FG_COLLISION(x_display_pos, mcm_pixel_value & 2); + } if (x_display_pos + 1 >= border_x_left && ( !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos + 1]) - )) + )) { *(pixel_raster_start + x_display_pos + 1) = sdl_pixel; + DO_SPRITE_SPRITE_COLLISION(x_display_pos + 1, mcm_pixel_value & 2); + DO_SPRITE_FG_COLLISION(x_display_pos + 1, mcm_pixel_value & 2); + } } } } @@ -904,6 +1007,9 @@ static XEMU_INLINE void vic4_draw_sprite_row_mono ( int sprnum, int x_display_po { const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3; const Uint32 sdl_pixel = spritepalette[SPRITE_COLOR(sprnum)]; +# ifdef SPRITE_ANY_COLLISION + const Uint8 sprbmask = 1 << sprnum; +# endif for (int byte = 0; byte < totalBytes; byte++) { for (int xbit = 0; xbit < 8; xbit++) { const Uint8 sprite_bit = *row_data_ptr & (0x80 >> xbit); @@ -911,8 +1017,11 @@ static XEMU_INLINE void vic4_draw_sprite_row_mono ( int sprnum, int x_display_po if (x_display_pos >= border_x_left && sprite_bit && ( !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos]) - )) + )) { *(pixel_raster_start + x_display_pos) = sdl_pixel; + DO_SPRITE_SPRITE_COLLISION(x_display_pos, 1); + DO_SPRITE_FG_COLLISION(x_display_pos, 1); + } } } row_data_ptr++; @@ -928,11 +1037,12 @@ static XEMU_INLINE void vic4_do_sprites ( void ) // In multicolor mode (MCM=1), the bit combinations "00" and "01" belong to the background // and "10" and "11" to the foreground whereas in standard mode (MCM=0), // cleared pixels belong to the background and set pixels to the foreground. + const int reg_tiling = REG_SPRTILEN; for (int sprnum = 7; sprnum >= 0; sprnum--) { if (REG_SPRITE_ENABLE & (1 << sprnum)) { const int spriteHeight = SPRITE_EXTHEIGHT(sprnum) ? REG_SPRHGHT : 21; - int x_display_pos = border_x_left + ((SPRITE_POS_X(sprnum) - SPRITE_X_BASE_COORD) * (REG_SPR640 ? 1 : 2)); // in display units - int y_logical_pos = SPRITE_POS_Y(sprnum) - SPRITE_Y_BASE_COORD +(BORDER_Y_TOP / (REG_V400 ? 1 : 2)); // in logical units + const int x_display_pos = border_x_left + ((SPRITE_POS_X(sprnum) - SPRITE_X_BASE_COORD) * (REG_SPR640 ? 1 : 2)); // in display units + const int y_logical_pos = SPRITE_POS_Y(sprnum) - SPRITE_Y_BASE_COORD +(BORDER_Y_TOP / (EFFECTIVE_V400 ? 1 : 2)); // in logical units int sprite_row_in_raster = logical_raster - y_logical_pos; @@ -951,11 +1061,11 @@ static XEMU_INLINE void vic4_do_sprites ( void ) //DEBUGPRINT("VIC: Sprite %d data at $%08X " NL, sprnum, sprite_data_addr); const Uint8 *sprite_data = main_ram + sprite_data_addr; const Uint8 *row_data = sprite_data + widthBytes * sprite_row_in_raster; - int xscale = (REG_SPR640 ? 1 : 2) * (SPRITE_HORZ_2X(sprnum) ? 2 : 1); + const int xscale = (REG_SPR640 ? 1 : 2) * (SPRITE_HORZ_2X(sprnum) ? 2 : 1); if (SPRITE_MULTICOLOR(sprnum)) vic4_draw_sprite_row_multicolor(sprnum, x_display_pos, row_data, xscale); else if (SPRITE_16COLOR(sprnum)) - vic4_draw_sprite_row_16color(sprnum, x_display_pos, row_data, xscale); + vic4_draw_sprite_row_16color(sprnum, x_display_pos, row_data, xscale, reg_tiling & (1 << sprnum)); else vic4_draw_sprite_row_mono(sprnum, x_display_pos, row_data, xscale); } @@ -966,17 +1076,22 @@ static XEMU_INLINE void vic4_do_sprites ( void ) // Render a monochrome character cell row // flip = 00 Dont flip, 01 = flip vertical, 10 = flip horizontal, 11 = flip both -static XEMU_INLINE void vic4_render_mono_char_row ( Uint8 char_byte, const int glyph_width, const Uint8 bg_color, Uint8 fg_color, const Uint8 vic3attr ) +static XEMU_INLINE void vic4_render_mono_char_row ( Uint8 char_byte, const int glyph_width, const Uint8 bg_color, Uint8 fg_color, Uint8 vic3attr ) { + Uint32* active_palette = used_palette; if (XEMU_UNLIKELY(vic3attr)) { - if (char_row == 7 && VIC3_ATTR_UNDERLINE(vic3attr)) - char_byte = 0xFF; - if (VIC3_ATTR_REVERSE(vic3attr)) - char_byte = ~char_byte; - if (VIC3_ATTR_BLINK(vic3attr) && blink_phase) - char_byte = VIC3_ATTR_REVERSE(vic3attr) ? ~char_byte : 0; - if (VIC3_ATTR_BOLD(vic3attr)) - fg_color |= 0x10; + if(!VIC3_ATTR_BLINK(vic3attr) || blink_phase) { + if (XEMU_UNLIKELY(VIC3_ATTR_BOLD(vic3attr) && VIC3_ATTR_REVERSE(vic3attr))) + used_palette = altpalette; + else if (VIC3_ATTR_REVERSE(vic3attr)) + char_byte = ~char_byte; + if (VIC3_ATTR_BOLD(vic3attr)) + fg_color |= 0x10; + if (char_row == 7 && VIC3_ATTR_UNDERLINE(vic3attr)) + char_byte = 0xFF; + } else if (VIC3_ATTR_BLINK(vic3attr) && vic3attr == 0x1) { + char_byte = 0; + } } const Uint32 sdl_fg_color = used_palette[fg_color]; if (XEMU_LIKELY(enable_bg_paint)) { @@ -995,6 +1110,7 @@ static XEMU_INLINE void vic4_render_mono_char_row ( Uint8 char_byte, const int g is_fg[xcounter++] = char_pixel; } } + used_palette = active_palette; } @@ -1060,17 +1176,27 @@ static XEMU_INLINE void set_bitplane_pointers ( void ) { // Get Bitplane source addresses /* TODO: Cache the following reads & EA calculation */ - const int and_mask = (REG_H640 ? 12 : 14); - //for (int i = 0; i < 8; i++) - // bitplane_p[i] = bitplane_bank_p + ((vic_registers[0x33 + i] & and_mask) << 12) + ((i & 1) << 16); - bitplane_p[0] = bitplane_bank_p + ((vic_registers[0x33] & and_mask) << 12); - bitplane_p[1] = bitplane_bank_p + ((vic_registers[0x34] & and_mask) << 12) + 0x10000; - bitplane_p[2] = bitplane_bank_p + ((vic_registers[0x35] & and_mask) << 12); - bitplane_p[3] = bitplane_bank_p + ((vic_registers[0x36] & and_mask) << 12) + 0x10000; - bitplane_p[4] = bitplane_bank_p + ((vic_registers[0x37] & and_mask) << 12); - bitplane_p[5] = bitplane_bank_p + ((vic_registers[0x38] & and_mask) << 12) + 0x10000; - bitplane_p[6] = bitplane_bank_p + ((vic_registers[0x39] & and_mask) << 12); - bitplane_p[7] = bitplane_bank_p + ((vic_registers[0x3A] & and_mask) << 12) + 0x10000; + int and_mask, bit_shifter; + if (EFFECTIVE_V400) { + if (!(ycounter & 1)) { + and_mask = (REG_H640 ? 12 : 14); + bit_shifter = 12; + } else { + and_mask = (REG_H640 ? 12 << 4 : 14 << 4); + bit_shifter = 12 - 4; + } + } else { + and_mask = (REG_H640 ? 12 : 14); + bit_shifter = 12; + } + bitplane_p[0] = bitplane_bank_p + ((vic_registers[0x33] & and_mask) << bit_shifter); + bitplane_p[1] = bitplane_bank_p + ((vic_registers[0x34] & and_mask) << bit_shifter) + 0x10000; + bitplane_p[2] = bitplane_bank_p + ((vic_registers[0x35] & and_mask) << bit_shifter); + bitplane_p[3] = bitplane_bank_p + ((vic_registers[0x36] & and_mask) << bit_shifter) + 0x10000; + bitplane_p[4] = bitplane_bank_p + ((vic_registers[0x37] & and_mask) << bit_shifter); + bitplane_p[5] = bitplane_bank_p + ((vic_registers[0x38] & and_mask) << bit_shifter) + 0x10000; + bitplane_p[6] = bitplane_bank_p + ((vic_registers[0x39] & and_mask) << bit_shifter); + bitplane_p[7] = bitplane_bank_p + ((vic_registers[0x3A] & and_mask) << bit_shifter) + 0x10000; } @@ -1101,16 +1227,18 @@ static void vic4_render_bitplane_raster ( void ) // FIXME: do not call this function here, but from actual register writes only // which can affect the result of this function!! set_bitplane_pointers(); - Uint32 offset = display_row * REG_CHRCOUNT * 8 + char_row ; + Uint32 offset = display_row * REG_CHRCOUNT * 8 + char_row; int line_char_index = 0; while (line_char_index < REG_CHRCOUNT) { vic4_render_bitplane_char_row(offset, 8); offset += 8; line_char_index++; } - if (++char_row > 7) { - char_row = 0; - display_row++; + if (!EFFECTIVE_V400 || (ycounter & 1)) { + if (++char_row > 7) { + char_row = 0; + display_row++; + } } while (xcounter++ < border_x_right) *current_pixel++ = palette[REG_SCREEN_COLOR]; @@ -1173,31 +1301,32 @@ static void vic4_render_char_raster ( void ) color_data = (color_data << 8) | (*(colour_ram_current_ptr++)); char_value = char_value | (*(screen_ram_current_ptr++) << 8); if (XEMU_UNLIKELY(SXA_GOTO_X(color_data))) { - // Start of the GOTOX re-positioning functionality implementation, tricky one. - xcounter = (char_value & 0x3FF); // first, extract the goto to X value as an usigned number + // ---- Start of the GOTOX re-positioning functionality implementation, tricky one ---- + xcounter = (char_value & 0x3FF); // first, extract the goto 'X' value as an usigned number + // Check the given value as "signed" as well, decide if it's "negative" or not if (REG_H640) { // Interpret as a "negative" value compared to xcounter_start if it would fit into the real range of 0-xcounter_start, // otherwise interpret that as a positive offset compared to xcounter_start - if (0x3FF - xcounter < xcounter_start) - xcounter = xcounter_start - (0x3FF - xcounter); + if (0x400 - xcounter <= xcounter_start) + xcounter = xcounter_start - (0x400 - xcounter); else xcounter += xcounter_start; } else { xcounter <<= 1; // multiply by 2, if !H640 (as the pixel is double width for lower resolution) - if (0x7FE - xcounter < xcounter_start) - xcounter = xcounter_start - (0x7FE - xcounter); + if (0x800 - xcounter <= xcounter_start) + xcounter = xcounter_start - (0x800 - xcounter); else xcounter += xcounter_start; } // The ugly: too large goto X values may cause out-of-bound access on eg is_fg buffer. Thus, if the result is larger than // the width of the SDL texture, it won't be seen anyway, so we "clamp" it for the NEXT raster as an ugly solution, which // will be overwritten anyway on rendering in the next raster. This way we don't need checking of out-of-bound access (faster - // code). + // code) _everywhere_ ... if (xcounter > TEXTURE_WIDTH) xcounter = TEXTURE_WIDTH; // Align current_pixel pointer according the calculated xcounter "horror show" above current_pixel = pixel_raster_start + xcounter; - // End of the GOTOX re-positioning functionality implementation + // ---- End of the GOTOX re-positioning functionality implementation ---- line_char_index++; char_fetch_offset = char_value >> 13; if (SXA_VERTICAL_FLIP(color_data)) @@ -1292,14 +1421,15 @@ int vic4_render_scanline ( void ) pixel_raster_start = current_pixel; SET_PHYSICAL_RASTER(ycounter); - logical_raster = ycounter >> (REG_V400 ? 0 : 1); + logical_raster = ycounter >> (EFFECTIVE_V400 ? 0 : 1); - if (!(ycounter & 1)) // VIC-II raster source: We shall check FNRST ? - vic4_check_raster_interrupt(logical_raster); + // FIXME: this is probably a bad fix ... Trying to remedy that in V400, no raster interrupts seems to work ... XXX + if (!(ycounter & 1) || EFFECTIVE_V400) // VIC-II raster source: We shall check FNRST ? + check_raster_interrupt(logical_raster); // "Double-scan hack" // FIXME: is this really correct? ie even sprites cannot be set to Y pos finer than V200 or ... // ... having resolution finer than V200 with some "VIC-IV magic"? - if (!REG_V400 && (ycounter & 1)) { + if (!EFFECTIVE_V400 && (ycounter & 1)) { //for (int i = 0; i < TEXTURE_WIDTH; i++, current_pixel++) // *current_pixel = /* user_scanlines_setting ? 0 : */ *(current_pixel - TEXTURE_WIDTH); memcpy(current_pixel, current_pixel - TEXTURE_WIDTH, TEXTURE_WIDTH * 4); @@ -1317,6 +1447,9 @@ int vic4_render_scanline ( void ) xcounter += border_x_left; current_pixel += border_x_left; vic4_raster_renderer_path(); +# ifdef SPRITE_SPRITE_COLLISION + memset(is_sprite, 0, sizeof is_sprite); +# endif vic4_do_sprites(); } // Paint screen color if positive y-offset (CHARGEN_Y_START > BORDER_Y_TOP) diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index b4890f39..2045760b 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -49,7 +49,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define PHYSICAL_RASTERS_PAL 624 #define FRAME_H_FRONT 0 #define RASTER_CORRECTION 3 -#define VIC4_BLINK_INTERVAL 25 +#define VIC4_BLINK_INTERVAL 30 // Register defines // @@ -84,7 +84,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_BBRDPOS_U4 (vic_registers[0x4B] & 0xF) #define REG_TEXTXPOS (vic_registers[0x4C]) #define REG_TEXTXPOS_U4 (vic_registers[0x4D] & 0xF) -#define REG_SPRTILEN ((vic_registers[0x4D] & 0xF0) >> 4 | (vic_registers[0x4F] & 0xF0)) +#define REG_SPRTILEN ((vic_registers[0x4D] >> 4) | (vic_registers[0x4F] & 0xF0)) #define REG_TEXTYPOS (vic_registers[0x4E]) #define REG_TEXTYPOS_U4 (vic_registers[0x4F] & 0xF) #define REG_XPOS (vic_registers[0x51]) From 5aaa6ad4ad9187d0ac03a924a5f3c6e6f19d7eac Mon Sep 17 00:00:00 2001 From: hernandp Date: Wed, 21 Jul 2021 12:52:25 -0300 Subject: [PATCH 31/35] Proposal to fix #284: VICII bitmap address --- targets/mega65/vic4.c | 2 +- targets/mega65/vic4.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 14e35772..b8d87e15 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -1249,7 +1249,7 @@ static void vic4_render_bitplane_raster ( void ) static XEMU_INLINE Uint8 *get_charset_effective_addr ( void ) { //const Uint8 *row_data_base_addr = main_ram + (REG_BMM ? VIC2_BITMAP_ADDR : get_charset_effective_addr()); - int addr = VIC2_BITMAP_ADDR; + int addr = REG_BMM ? VIC2_BITMAP_ADDR : CHARSET_ADDR; // Note: in theory on C65 there is a bit for choose between two charsets (rather than only lower/upper case) // See: https://github.com/lgblgblgb/xemu/issues/213 // However it seems even MEGA65 does not support this. diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index 2045760b..d0acf4a1 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -134,7 +134,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SCREEN_RAM_ADDR_VIC (REG_SCREEN_ADDR * 1024) #define SCREEN_ADDR ((Uint32)REG_SCRNPTR_B0 | (REG_SCRNPTR_B1<<8) | (REG_SCRNPTR_B2 <<16) | (REG_SCRNPTR_B3 << 24)) #define CHARSET_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) -#define VIC2_BITMAP_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) +#define VIC2_BITMAP_ADDR ((CHARSET_ADDR) & 0xFFE000) #define SPRITE_POINTER_ADDR ((Uint32)REG_SPRPTR_B0 | (REG_SPRPTR_B1<<8) | (REG_SPRPTR_B2 <<16)) #define COLOUR_RAM_OFFSET ((((Uint16)REG_COLPTR) | (REG_COLPTR_MSB) << 8)) //#define IS_NTSC_MODE (videostd_id) From 79382fd726dfcde23b929332c269a48c55ec00ac Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:42:25 +0200 Subject: [PATCH 32/35] MEGA65: my VIC-IV updates from branch 'merger' --- targets/mega65/vic4.c | 37 ++++++++++--------------------------- targets/mega65/vic4.h | 41 ++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 48 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index b8d87e15..919e39b8 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -34,16 +34,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SPRITE_FG_COLLISION -#ifdef XEMU_RELEASE_BUILD -# ifdef SPRITE_SPRITE_COLLISION -# undef SPRITE_SPRITE_COLLISION -# endif -# ifdef SPRITE_FG_COLLISION -# undef SPRITE_FG_COLLISION -# endif -#endif - - const char *iomode_names[4] = { "VIC2", "VIC3", "BAD!", "VIC4" }; // (SDL) target texture rendering pointers @@ -95,10 +85,6 @@ static const char NTSC_STD_NAME[] = "NTSC"; static const char PAL_STD_NAME[] = "PAL"; int vic_readjust_sdl_viewport = 0; -static void vic4_render_char_raster(void); -static void vic4_render_bitplane_raster(void); -static void (*vic4_raster_renderer_path)(void) = &vic4_render_char_raster; - // VIC-IV Modeline Parameters // ---------------------------------------------------- #define DISPLAY_HEIGHT ((max_rasters-1)-20) @@ -538,11 +524,11 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) if (vic_registers[0x11] ^ data) vic_hotreg_touched = 1; compare_raster = (compare_raster & 0xFF) | ((data & 0x80) << 1); - DEBUGPRINT("VIC: compare raster is now %d" NL, compare_raster); + DEBUG("VIC: compare raster is now %d" NL, compare_raster); break; CASE_VIC_ALL(0x12): compare_raster = (compare_raster & 0xFF00) | data; - DEBUGPRINT("VIC: compare raster is now %d" NL, compare_raster); + DEBUG("VIC: compare raster is now %d" NL, compare_raster); break; CASE_VIC_ALL(0x13): CASE_VIC_ALL(0x14): return; // FIXME: writing light-pen registers????? @@ -630,12 +616,8 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) // So probably we need a separate (cpu_speed_hotreg) var? if ((vic_registers[0x31] & 0xBF) ^ (data & 0xBF)) vic_hotreg_touched = 1; - - vic4_raster_renderer_path = ( (data & 0x10) == 0) ? vic4_render_char_raster : vic4_render_bitplane_raster; - vic_registers[0x31] = data; // we need this work-around, since reg-write happens _after_ this switch statement, but machine_set_speed above needs it ... machine_set_speed(0); - calculate_char_x_step(); break; // we did the write, but we need to trigger vichot_reg if should @@ -692,7 +674,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) CASE_VIC_4(0x5F): break; CASE_VIC_4(0x60): CASE_VIC_4(0x61): CASE_VIC_4(0x62): CASE_VIC_4(0x63): - DEBUGPRINT("VIC: Write SCREENADDR byte 0xD0%02x: $%02x" NL, addr, data); + DEBUG("VIC: Write SCREENADDR byte 0xD0%02x: $%02x" NL, addr, data); break; CASE_VIC_4(0x64): CASE_VIC_4(0x65): CASE_VIC_4(0x66): CASE_VIC_4(0x67): /*CASE_VIC_4(0x68): CASE_VIC_4(0x69): CASE_VIC_4(0x6A):*/ CASE_VIC_4(0x6B): /*CASE_VIC_4(0x6C): @@ -1222,7 +1204,7 @@ static XEMU_INLINE void vic4_render_bitplane_char_row ( const Uint32 offset, con } -static void vic4_render_bitplane_raster ( void ) +static XEMU_INLINE void vic4_render_bitplane_raster ( void ) { // FIXME: do not call this function here, but from actual register writes only // which can affect the result of this function!! @@ -1278,7 +1260,7 @@ static XEMU_INLINE Uint8 *get_charset_effective_addr ( void ) // // VIC-III Extended attributes are applied to characters if properly set, // except in Multicolor modes. -static void vic4_render_char_raster ( void ) +static XEMU_INLINE void vic4_render_char_raster ( void ) { int line_char_index = 0; enable_bg_paint = 1; @@ -1346,9 +1328,7 @@ static void vic4_render_char_raster ( void ) Uint8 glyph_width_deduct = SXA_TRIM_RIGHT_BITS012(char_value) + (SXA_TRIM_RIGHT_BIT3(char_value) ? 8 : 0); Uint8 glyph_width = (SXA_4BIT_PER_PIXEL(color_data) ? 16 : 8) - glyph_width_deduct; // Default fetch from char mode. - int sel_char_row = char_row; - if (XEMU_UNLIKELY(SXA_VERTICAL_FLIP(color_data))) - sel_char_row = 7 - char_row; + const int sel_char_row = (XEMU_UNLIKELY(SXA_VERTICAL_FLIP(color_data)) ? 7 - char_row : char_row); // Render character cell row if (SXA_4BIT_PER_PIXEL(color_data)) { // 16-color character vic4_render_16color_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), glyph_width, used_palette[char_bgcolor], used_palette + (color_data & 0xF0), SXA_HORIZONTAL_FLIP(color_data)); @@ -1446,7 +1426,10 @@ int vic4_render_scanline ( void ) // borders also if y-offset applies. xcounter += border_x_left; current_pixel += border_x_left; - vic4_raster_renderer_path(); + if (XEMU_LIKELY(!(vic_registers[0x31] & 0x10))) + vic4_render_char_raster(); + else + vic4_render_bitplane_raster(); # ifdef SPRITE_SPRITE_COLLISION memset(is_sprite, 0, sizeof is_sprite); # endif diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index d0acf4a1..d997df7e 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -66,7 +66,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_SCREEN_COLOR (vic_registers[0x21] & vic_color_register_mask) #define REG_MULTICOLOR_1 (vic_registers[0x22] & vic_color_register_mask) #define REG_MULTICOLOR_2 (vic_registers[0x23] & vic_color_register_mask) -#define REG_MULTICOLOR_3 (vic_registers[0x24] & vic_color_register_mask) +//#define REG_MULTICOLOR_3 (vic_registers[0x24] & vic_color_register_mask) #define REG_H640 (vic_registers[0x31] & 128) #define REG_V400 (vic_registers[0x31] & 8) #define REG_VICIII_ATTRIBS (vic_registers[0x31] & 0x20) @@ -75,7 +75,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_DISPLAYENABLE (vic_registers[0x11] & 0x10) #define REG_VIC2_XSCROLL (vic_registers[0x16] & 7) #define REG_VIC2_YSCROLL (vic_registers[0x11] & 7) -#define REG_CRAM2K (vic_registers[0x30] & 1) +//#define REG_CRAM2K (vic_registers[0x30] & 1) #define REG_TBRDPOS (vic_registers[0x48]) #define REG_SPRBPMEN_0_3 (vic_registers[0x49] >> 4) #define REG_SPRBPMEN_4_7 (vic_registers[0x4B] >> 4) @@ -87,15 +87,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_SPRTILEN ((vic_registers[0x4D] >> 4) | (vic_registers[0x4F] & 0xF0)) #define REG_TEXTYPOS (vic_registers[0x4E]) #define REG_TEXTYPOS_U4 (vic_registers[0x4F] & 0xF) -#define REG_XPOS (vic_registers[0x51]) -#define REG_XPOS_U6 (vic_registers[0x50] & 0x3F) +//#define REG_XPOS (vic_registers[0x51]) +//#define REG_XPOS_U6 (vic_registers[0x50] & 0x3F) #define REG_FNRST (vic_registers[0x53] & 0x80) #define REG_16BITCHARSET (vic_registers[0x54] & 1) #define REG_FCLRLO (vic_registers[0x54] & 2) #define REG_FCLRHI (vic_registers[0x54] & 4) #define REG_SPR640 (vic_registers[0x54] & 0x10) #define REG_SPRHGHT (vic_registers[0x56]) -#define REG_CHRXSCL (vic_registers[0x5A]) +//#define REG_CHRXSCL (vic_registers[0x5A]) #define REG_CHRYSCL (vic_registers[0x5B]) #define REG_SIDBDRWD (vic_registers[0x5C]) #define REG_SIDBDRWD_U5 (vic_registers[0x5D] & 0x3F) @@ -116,10 +116,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_SPRPTR_B0 vic_registers[0x6C] #define REG_SPRPTR_B1 vic_registers[0x6D] #define REG_SPRPTR_B2 (vic_registers[0x6E] & 0x7F) -#define REG_SCREEN_ROWS vic_registers[0x7B] -#define REG_PAL_RED_BASE (vic_registers[0x100]) -#define REG_PAL_GREEN_BASE (vic_registers[0x200]) -#define REG_PAL_BLUE_BASE (vic_registers[0x300]) +//#define REG_SCREEN_ROWS vic_registers[0x7B] +//#define REG_PAL_RED_BASE (vic_registers[0x100]) +//#define REG_PAL_GREEN_BASE (vic_registers[0x200]) +//#define REG_PAL_BLUE_BASE (vic_registers[0x300]) // Helper macros for accessing multi-byte registers // and other similar functionality for convenience @@ -131,14 +131,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define CHARGEN_Y_START (((Uint16)REG_TEXTYPOS) | (REG_TEXTYPOS_U4) << 8) #define CHARGEN_X_START (((Uint16)REG_TEXTXPOS) | (REG_TEXTXPOS_U4) << 8) #define CHARSTEP_BYTES (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) -#define SCREEN_RAM_ADDR_VIC (REG_SCREEN_ADDR * 1024) +//#define SCREEN_RAM_ADDR_VIC (REG_SCREEN_ADDR * 1024) #define SCREEN_ADDR ((Uint32)REG_SCRNPTR_B0 | (REG_SCRNPTR_B1<<8) | (REG_SCRNPTR_B2 <<16) | (REG_SCRNPTR_B3 << 24)) #define CHARSET_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) #define VIC2_BITMAP_ADDR ((CHARSET_ADDR) & 0xFFE000) #define SPRITE_POINTER_ADDR ((Uint32)REG_SPRPTR_B0 | (REG_SPRPTR_B1<<8) | (REG_SPRPTR_B2 <<16)) #define COLOUR_RAM_OFFSET ((((Uint16)REG_COLPTR) | (REG_COLPTR_MSB) << 8)) //#define IS_NTSC_MODE (videostd_id) -#define SCREEN_STEP (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) +//#define SCREEN_STEP (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) #define SPRITE_POS_Y(n) (vic_registers[1 + (n)*2]) #define SPRITE_POS_X(n) (((Uint16)vic_registers[(n)*2]) | ( (vic_registers[0x10] & (1 << (n)) ? 0x100 : 0))) #define SPRITE_COLOR(n) (vic_registers[0x27+(n)] & vic_color_register_mask) @@ -154,9 +154,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SPRITE_EXTHEIGHT(n) (vic_registers[0x55] & (1 << (n))) #define SPRITE_BITPLANE_ENABLE(n) (((REG_SPRBPMEN_4_7) << 4 | REG_SPRBPMEN_0_3) & (1 << (n))) #define SPRITE_16BITPOINTER (vic_registers[0x6E] & 0x80) -#define TEXT_MODE (!REG_BMM) -#define HIRES_BITMAP_MODE (REG_BMM & !REG_MCM & !REG_EBM) -#define MULTICOLOR_BITMAP_MODE (REG_BMM & REG_MCM & !REG_EBM) +//#define TEXT_MODE (!REG_BMM) +//#define HIRES_BITMAP_MODE (REG_BMM & !REG_MCM & !REG_EBM) +//#define MULTICOLOR_BITMAP_MODE (REG_BMM & REG_MCM & !REG_EBM) #define VIC3_ATTR_BLINK(c) ((c) & 0x1) #define VIC3_ATTR_REVERSE(c) ((c) & 0x2) #define VIC3_ATTR_BOLD(c) ((c) & 0x4) @@ -169,13 +169,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SXA_TRIM_RIGHT_BITS012(sw) ((sw) >> 13) #define SXA_VERTICAL_FLIP(cw) ((cw) & 0x8000) #define SXA_HORIZONTAL_FLIP(cw) ((cw) & 0x4000) -#define SXA_ALPHA_BLEND(cw) ((cw) & 0x2000) +//#define SXA_ALPHA_BLEND(cw) ((cw) & 0x2000) #define SXA_GOTO_X(cw) ((cw) & 0x1000) #define SXA_4BIT_PER_PIXEL(cw) ((cw) & 0x0800) #define SXA_TRIM_RIGHT_BIT3(cw) ((cw) & 0x0400) #define SXA_ATTR_BOLD(cw) ((cw) & 0x0040) #define SXA_ATTR_REVERSE(cw) ((cw) & 0x0020) -//FIXME: this last one was bad, and also, seems to be not used? //#define SXA_TRIM_TOP_BOTTOM(cw) (((cw) & 0x0300) >> 8) @@ -193,10 +192,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ vic_registers[((basereg)+1)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ vic_registers[(basereg)] = (Uint8) ((Uint16)(x)) & 0x00FF; \ } while(0) -#define SET_14BIT_REGI(basereg,x) do { \ +/* #define SET_14BIT_REGI(basereg,x) do { \ vic_registers[(basereg)] = (Uint8) ((((Uint16)(x)) & 0x3F00) >> 8); \ vic_registers[((basereg)+1)] = (Uint8) ((Uint16)(x)) & 0x00FF; \ - } while(0) + } while(0) */ #define SET_16BIT_REG(basereg,x) do { \ vic_registers[((basereg) + 1)] = (Uint8) ((((Uint16)(x)) & 0xFF00) >> 8); \ vic_registers[(basereg)]= ((Uint16)(x)) & 0x00FF; \ @@ -205,7 +204,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // 11-bit registers #define SET_PHYSICAL_RASTER(x) SET_11BIT_REG(0x52, (x)) -#define SET_RASTER_XPOS(x) SET_14BIT_REGI(0x50, (x)) +//#define SET_RASTER_XPOS(x) SET_14BIT_REGI(0x50, (x)) // 12-bit registers @@ -223,8 +222,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SPRITE_X_BASE_COORD 24 #define SPRITE_Y_BASE_COORD 50 -#define SPRITE_X_UPPER_COORD 250 -#define SPRITE_Y_UPPER_COORD 344 +//#define SPRITE_X_UPPER_COORD 250 +//#define SPRITE_Y_UPPER_COORD 344 // Current state From 4ea14d64692b11c14c869c4e29e7738f8806af66 Mon Sep 17 00:00:00 2001 From: hernandp Date: Mon, 30 Aug 2021 22:13:14 -0300 Subject: [PATCH 33/35] Fix #287 --- targets/mega65/vic4.c | 16 ++++++++-------- targets/mega65/vic4.h | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index 919e39b8..c4bb3412 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -317,7 +317,7 @@ static void vic4_interpret_legacy_mode_registers ( void ) Uint8 width = REG_H640 ? 80 : 40; REG_CHRCOUNT = width; - SET_CHARSTEP_BYTES(width);// * (REG_16BITCHARSET ? 2 : 1)); + SET_LINESTEP_BYTES(width);// * (REG_16BITCHARSET ? 2 : 1)); REG_SCRNPTR_B0 = 0; REG_SCRNPTR_B1 &= 0xC0; @@ -336,9 +336,9 @@ static void vic4_interpret_legacy_mode_registers ( void ) REG_CHARPTR_B1 = (~last_dd00_bits << 6) | (REG_CHARPTR_B1 & 0x3F); SET_COLORRAM_BASE(0); - DEBUGPRINT("VIC4: 16bit=%d, chrcount=%d, charstep=%d bytes, charxscale=%d, ras_src=%d " + DEBUGPRINT("VIC4: 16bit=%d, chrcount=%d, linestep=%d bytes, charxscale=%d, ras_src=%d " "screen_ram=$%06x, charset/bitmap=$%06x, sprite=$%06x" NL, - REG_16BITCHARSET, REG_CHRCOUNT, CHARSTEP_BYTES, REG_CHARXSCALE, + REG_16BITCHARSET, REG_CHRCOUNT, LINESTEP_BYTES, REG_CHARXSCALE, REG_FNRST, SCREEN_ADDR, CHARSET_ADDR, SPRITE_POINTER_ADDR); } @@ -648,7 +648,7 @@ void vic_write_reg ( unsigned int addr, Uint8 data ) return; // since we DID the write, it's OK to return here and not using "break" CASE_VIC_4(0x55): CASE_VIC_4(0x56): CASE_VIC_4(0x57): break; CASE_VIC_4(0x58): CASE_VIC_4(0x59): - DEBUGPRINT("VIC: Write $%04x CHARSTEP: $%02x" NL, addr, data); + DEBUGPRINT("VIC: Write $%04x LINESTEP: $%02x" NL, addr, data); break; CASE_VIC_4(0x5A): //DEBUGPRINT("WRITE $%04x CHARXSCALE: $%02x" NL, addr, data); @@ -1266,8 +1266,8 @@ static XEMU_INLINE void vic4_render_char_raster ( void ) enable_bg_paint = 1; const Uint8 *row_data_base_addr = get_charset_effective_addr(); // FIXME: is it OK that I moved here, before the loop? if (display_row >= 0 && display_row < display_row_count) { - Uint8 *colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET + (display_row * CHARSTEP_BYTES); - Uint8 *screen_ram_current_ptr = main_ram + SCREEN_ADDR + (display_row * CHARSTEP_BYTES); + Uint8 *colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET + (display_row * LINESTEP_BYTES); + Uint8 *screen_ram_current_ptr = main_ram + SCREEN_ADDR + (display_row * LINESTEP_BYTES); // Account for Chargen X-displacement for (Uint32 *p = current_pixel; p < current_pixel + (CHARGEN_X_START - border_x_left); p++) *p = palette[REG_SCREEN_COLOR]; @@ -1348,7 +1348,7 @@ static XEMU_INLINE void vic4_render_char_raster ( void ) color_source_mcm[1] = char_value >> 4; // 01 color_source_mcm[2] = char_value & 0xF; // 10 color_source_mcm[3] = color_data & 0xF; // 11 - char_byte = *(row_data_base_addr + display_row * 320 + 8 * line_char_index + sel_char_row); // this is BAD I guess assuming 320 pixel, can be anything ... (?) + char_byte = *(row_data_base_addr + display_row * (LINESTEP_BYTES * 8) + 8 * line_char_index + sel_char_row); } else { // value 00 is common /w or w/o BMM so not initialized here color_source_mcm[1] = REG_MULTICOLOR_1; // 01 @@ -1370,7 +1370,7 @@ static XEMU_INLINE void vic4_render_char_raster ( void ) } else { char_bgcolor_now = char_value & 0xF; char_fgcolor_now = char_value >> 4; - char_byte = *(row_data_base_addr + display_row * 320 + 8 * line_char_index + sel_char_row); // this is BAD I guess assuming 320 pixel, can be anything ... (?) + char_byte = *(row_data_base_addr + display_row * (LINESTEP_BYTES * 8) + 8 * line_char_index + sel_char_row); } // FIXME: is this really a thing to have FLIP in bitmap mode AS WELL?! if (XEMU_UNLIKELY(SXA_HORIZONTAL_FLIP(color_data))) diff --git a/targets/mega65/vic4.h b/targets/mega65/vic4.h index d997df7e..2aa2a17e 100644 --- a/targets/mega65/vic4.h +++ b/targets/mega65/vic4.h @@ -100,8 +100,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define REG_SIDBDRWD (vic_registers[0x5C]) #define REG_SIDBDRWD_U5 (vic_registers[0x5D] & 0x3F) #define REG_HOTREG (vic_registers[0x5D] & 0x80) -#define REG_CHARSTEP vic_registers[0x58] -#define REG_CHARSTEP_U8 vic_registers[0x59] +#define REG_LINESTEP vic_registers[0x58] +#define REG_LINESTEP_U8 vic_registers[0x59] #define REG_CHARXSCALE vic_registers[0x5A] #define REG_CHRCOUNT vic_registers[0x5E] #define REG_SCRNPTR_B0 vic_registers[0x60] @@ -130,7 +130,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define BORDER_Y_BOTTOM (((Uint16)REG_BBRDPOS) | (REG_BBRDPOS_U4) << 8) #define CHARGEN_Y_START (((Uint16)REG_TEXTYPOS) | (REG_TEXTYPOS_U4) << 8) #define CHARGEN_X_START (((Uint16)REG_TEXTXPOS) | (REG_TEXTXPOS_U4) << 8) -#define CHARSTEP_BYTES (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) +#define LINESTEP_BYTES (((Uint16)REG_LINESTEP) | (REG_LINESTEP_U8) << 8) //#define SCREEN_RAM_ADDR_VIC (REG_SCREEN_ADDR * 1024) #define SCREEN_ADDR ((Uint32)REG_SCRNPTR_B0 | (REG_SCRNPTR_B1<<8) | (REG_SCRNPTR_B2 <<16) | (REG_SCRNPTR_B3 << 24)) #define CHARSET_ADDR ((Uint32)REG_CHARPTR_B0 | (REG_CHARPTR_B1<<8) | (REG_CHARPTR_B2 <<16)) @@ -138,7 +138,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SPRITE_POINTER_ADDR ((Uint32)REG_SPRPTR_B0 | (REG_SPRPTR_B1<<8) | (REG_SPRPTR_B2 <<16)) #define COLOUR_RAM_OFFSET ((((Uint16)REG_COLPTR) | (REG_COLPTR_MSB) << 8)) //#define IS_NTSC_MODE (videostd_id) -//#define SCREEN_STEP (((Uint16)REG_CHARSTEP) | (REG_CHARSTEP_U8) << 8) +//#define SCREEN_STEP (((Uint16)REG_LINESTEP) | (REG_LINESTEP_U8) << 8) #define SPRITE_POS_Y(n) (vic_registers[1 + (n)*2]) #define SPRITE_POS_X(n) (((Uint16)vic_registers[(n)*2]) | ( (vic_registers[0x10] & (1 << (n)) ? 0x100 : 0))) #define SPRITE_COLOR(n) (vic_registers[0x27+(n)] & vic_color_register_mask) @@ -216,7 +216,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //16-bit registers #define SET_COLORRAM_BASE(x) SET_16BIT_REG(0x64,(x)) -#define SET_CHARSTEP_BYTES(x) SET_16BIT_REG(0x58,(x)) +#define SET_LINESTEP_BYTES(x) SET_16BIT_REG(0x58,(x)) // Review this! (VIC-II values) From 201069572ac13c7bfc98cc87b8d73ada971c60a5 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:44:09 +0200 Subject: [PATCH 34/35] MEGA65: my VIC-IV updates from branch 'merger' --- targets/mega65/vic4.c | 51 ++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/targets/mega65/vic4.c b/targets/mega65/vic4.c index c4bb3412..835774e9 100644 --- a/targets/mega65/vic4.c +++ b/targets/mega65/vic4.c @@ -270,6 +270,11 @@ static void vic4_update_sideborder_dimensions ( void ) static void vic4_update_vertical_borders( void ) { + // FIXME: it seems we need this line here! Otherwise EFFECTIVE_V400 may not reflect what + // it should be, if just updated in vic4_open_frame_access(). This seems to fix the OpenROMs + // issue that the bottom half of the screen is invisible, since the wrong condition below + // is taken for setting display_row_count based on V400 from EFFECTIVE_V400 ... + EFFECTIVE_V400 = (REG_V400 && REG_CHRYSCL == 0 && (vic_registers[0x51] & 0x40)) ? 0 : !!REG_V400; if (REG_CSEL) { // 40-columns? if (!REG_H640) SET_CHARGEN_X_START(FRAME_H_FRONT + SINGLE_SIDE_BORDER + (2 * REG_VIC2_XSCROLL)); @@ -304,8 +309,8 @@ static void vic4_update_vertical_borders( void ) } SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 6 + (REG_VIC2_YSCROLL * 2)); } - DEBUGPRINT("VIC4: set border top=%d, bottom=%d, textypos=%d, display_row_count=%d vic_ii_first_raster=%d" NL, BORDER_Y_TOP, BORDER_Y_BOTTOM, - CHARGEN_Y_START, display_row_count, vicii_first_raster); + DEBUGPRINT("VIC4: set border top=%d, bottom=%d, textypos=%d, display_row_count=%d vic_ii_first_raster=%d EFFECTIVE_V400=%d REG_V400=%d" NL, BORDER_Y_TOP, BORDER_Y_BOTTOM, + CHARGEN_Y_START, display_row_count, vicii_first_raster, EFFECTIVE_V400, REG_V400); } @@ -323,13 +328,13 @@ static void vic4_interpret_legacy_mode_registers ( void ) REG_SCRNPTR_B1 &= 0xC0; REG_SCRNPTR_B1 |= REG_H640 ? ((reg_d018_screen_addr & 14) << 2) : (reg_d018_screen_addr << 2); REG_SCRNPTR_B2 = 0; - vic_registers[0x63] &= 0b11110000; + vic_registers[0x63] &= 0b11110000; // clear VIC-IV precise screen addr bits 31-24 (from post bits 3-0) as it does not make sense; MEGA65 does not have enough fast RAM to use it REG_SPRPTR_B0 = 0xF8; REG_SPRPTR_B1 = (reg_d018_screen_addr << 2) | 0x3; if (REG_H640 | EFFECTIVE_V400) REG_SPRPTR_B1 |= 4; - vic_registers[0x6E] &= 128; + vic_registers[0x6E] &= 128; // hmmm, clearing VIC-IV sprite pointer bits 22-16 (bits 0-6 of this reg) REG_SPRPTR_B1 = (~last_dd00_bits << 6) | (REG_SPRPTR_B1 & 0x3F); REG_SCRNPTR_B1 = (~last_dd00_bits << 6) | (REG_SCRNPTR_B1 & 0x3F); @@ -1324,18 +1329,28 @@ static XEMU_INLINE void vic4_render_char_raster ( void ) const Uint8 char_fgcolor = color_data & 0xF; const Uint16 char_id = REG_EBM ? (char_value & 0x3f) : char_value & 0x1fff; // up to 8192 characters (13-bit) const Uint8 char_bgcolor = REG_EBM ? vic_registers[0x21 + ((char_value >> 6) & 3)] : REG_SCREEN_COLOR; - // Calculate character-width - Uint8 glyph_width_deduct = SXA_TRIM_RIGHT_BITS012(char_value) + (SXA_TRIM_RIGHT_BIT3(char_value) ? 8 : 0); - Uint8 glyph_width = (SXA_4BIT_PER_PIXEL(color_data) ? 16 : 8) - glyph_width_deduct; + const Uint8 glyph_trim = SXA_TRIM_RIGHT_BITS012(char_value) + (SXA_TRIM_RIGHT_BIT3(color_data) ? 8 : 0); // Default fetch from char mode. const int sel_char_row = (XEMU_UNLIKELY(SXA_VERTICAL_FLIP(color_data)) ? 7 - char_row : char_row); // Render character cell row if (SXA_4BIT_PER_PIXEL(color_data)) { // 16-color character - vic4_render_16color_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), glyph_width, used_palette[char_bgcolor], used_palette + (color_data & 0xF0), SXA_HORIZONTAL_FLIP(color_data)); + vic4_render_16color_char_row( + main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8)) & 0x7FFFF), + 16 - glyph_trim, + used_palette[char_bgcolor], // bg SDL colour + used_palette + (color_data & 0xF0), // palette(16) pointer + SXA_HORIZONTAL_FLIP(color_data) // hflip? + ); } else if (CHAR_IS256_COLOR(char_id)) { // 256-color character // fgcolor in case of FCM should mean colour index $FF // FIXME: check if the passed palette[char_fgcolor] is correct or another index should be used for that $FF colour stuff - vic4_render_fullcolor_char_row(main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8) ) & 0x7FFFF), 8, used_palette[char_bgcolor], used_palette[char_fgcolor], SXA_HORIZONTAL_FLIP(color_data)); + vic4_render_fullcolor_char_row( + main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8)) & 0x7FFFF), + 8 - glyph_trim, + used_palette[char_bgcolor], // bg SDL colour + used_palette[char_fgcolor], // fg SDL colour + SXA_HORIZONTAL_FLIP(color_data) // hflip? + ); } else if ((REG_MCM && (char_fgcolor & 8)) || (REG_MCM && REG_BMM)) { // Multicolor character // using static vars: faster in a rapid loop like this, no need to re-adjust stack pointer all the time to allocate space and this way using constant memory address // also, as an optimization, later, some value can be re-used and not always initialized here, when in reality VIC @@ -1348,7 +1363,7 @@ static XEMU_INLINE void vic4_render_char_raster ( void ) color_source_mcm[1] = char_value >> 4; // 01 color_source_mcm[2] = char_value & 0xF; // 10 color_source_mcm[3] = color_data & 0xF; // 11 - char_byte = *(row_data_base_addr + display_row * (LINESTEP_BYTES * 8) + 8 * line_char_index + sel_char_row); + char_byte = *(row_data_base_addr + display_row * (LINESTEP_BYTES * 8) + 8 * line_char_index + sel_char_row); } else { // value 00 is common /w or w/o BMM so not initialized here color_source_mcm[1] = REG_MULTICOLOR_1; // 01 @@ -1360,7 +1375,11 @@ static XEMU_INLINE void vic4_render_char_raster ( void ) // FIXME: also this is WRONG, MCM data cannot be reversed with this table!! if (XEMU_UNLIKELY(SXA_HORIZONTAL_FLIP(color_data))) char_byte = reverse_byte_table[char_byte]; - vic4_render_multicolor_char_row(char_byte, glyph_width, color_source_mcm); + vic4_render_multicolor_char_row( + char_byte, + 8 - glyph_trim, // glyph_width + color_source_mcm // 4 element (legacy) MCM colour index table + ); } else { // Single color character Uint8 char_byte, char_bgcolor_now, char_fgcolor_now; if (!REG_BMM) { @@ -1370,13 +1389,19 @@ static XEMU_INLINE void vic4_render_char_raster ( void ) } else { char_bgcolor_now = char_value & 0xF; char_fgcolor_now = char_value >> 4; - char_byte = *(row_data_base_addr + display_row * (LINESTEP_BYTES * 8) + 8 * line_char_index + sel_char_row); + char_byte = *(row_data_base_addr + display_row * (LINESTEP_BYTES * 8) + 8 * line_char_index + sel_char_row); } // FIXME: is this really a thing to have FLIP in bitmap mode AS WELL?! if (XEMU_UNLIKELY(SXA_HORIZONTAL_FLIP(color_data))) char_byte = reverse_byte_table[char_byte]; // FIXME: do vic3 attributes work with bitmap mode as well??? - vic4_render_mono_char_row(char_byte, glyph_width, char_bgcolor_now, char_fgcolor_now, (REG_VICIII_ATTRIBS && !REG_MCM) ? (color_data >> 4) : 0); + vic4_render_mono_char_row( + char_byte, + 8 - glyph_trim, // glyph_width + char_bgcolor_now, // bg colour index + char_fgcolor_now, // fg colour index + (REG_VICIII_ATTRIBS && !REG_MCM) ? (color_data >> 4) : 0 // VIC-III hardware attribute info + ); } line_char_index++; } From c4f1552912b89210d1bf168e93375676041046c8 Mon Sep 17 00:00:00 2001 From: "LGB (Gabor Lenart)" Date: Wed, 22 Sep 2021 20:50:32 +0200 Subject: [PATCH 35/35] PROJECT: close merging, CI back #29 --- .github/workflows/{main.yml.OFF => main.yml} | 0 .travis.yml.OFF => .travis.yml | 16 +++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) rename .github/workflows/{main.yml.OFF => main.yml} (100%) rename .travis.yml.OFF => .travis.yml (94%) diff --git a/.github/workflows/main.yml.OFF b/.github/workflows/main.yml similarity index 100% rename from .github/workflows/main.yml.OFF rename to .github/workflows/main.yml diff --git a/.travis.yml.OFF b/.travis.yml similarity index 94% rename from .travis.yml.OFF rename to .travis.yml index 59cc2021..b3db0a95 100644 --- a/.travis.yml.OFF +++ b/.travis.yml @@ -35,9 +35,11 @@ matrix: - zip compiler: gcc before_script: + - echo "127.126.125.124 lgb" | sudo tee -a /etc/hosts 2>/dev/null || true + - cat /etc/hosts || true + - hostname - set +e - uname -a ; lsb_release -a || true ; hostname ; pwd ; df -h ; id -a - - hostname - set -e - sdl2-config --version --prefix --cflags --libs --static-libs - pkg-config --cflags-only-I --libs gtk+-3.0 @@ -77,6 +79,7 @@ matrix: - dev - vic - hmw + - merger after_deploy: - build/deploy/discord-webhook.sh success "Ubuntu Linux DEB" ANY:DISCORD_XEMU_SERVER_TEST_CHANNEL_WEBHOOK master,hmw,next:DISCORD_MEGA65_SERVER_XEMU_CHANNEL_WEBHOOK || true - name: Windows cross-compilation on Linux @@ -102,10 +105,12 @@ matrix: - wget - zip before_script: + - echo "127.126.125.124 lgb" | sudo tee -a /etc/hosts 2>/dev/null || true + - cat /etc/hosts || true + - hostname - set +e - uname -a ; pwd ; df -h ; id -a - set -e - - hostname - build/install-cross-win-mingw-sdl-on-linux.sh /usr/bin - set +e - ps auxwwww || true @@ -151,6 +156,7 @@ matrix: - dev - vic - hmw + - merger after_deploy: - build/deploy/discord-webhook.sh success "Windows" ANY:DISCORD_XEMU_SERVER_TEST_CHANNEL_WEBHOOK master,hmw,next:DISCORD_MEGA65_SERVER_XEMU_CHANNEL_WEBHOOK || true - name: MacOS native compilation @@ -163,9 +169,11 @@ matrix: - create-dmg - sdl2 before_script: + - echo "127.126.125.124 lgb" | sudo tee -a /etc/hosts 2>/dev/null || true + - cat /etc/hosts || true + - hostname - set +e - uname -a ; pwd ; df -h ; id -a - - hostname - set -e - sdl2-config --version --prefix --cflags --libs --static-libs - set +e @@ -203,6 +211,8 @@ matrix: - dev - vic - hmw + - discord + - merger after_deploy: - build/deploy/discord-webhook.sh success "MacOS(x86)" ANY:DISCORD_XEMU_SERVER_TEST_CHANNEL_WEBHOOK master,hmw,next:DISCORD_MEGA65_SERVER_XEMU_CHANNEL_WEBHOOK || true