Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
477 lines (387 sloc)
11.7 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| ** Oricutron | |
| ** Copyright (C) 2009-2014 Peter Gordon | |
| ** | |
| ** 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, version 2 | |
| ** of the License. | |
| ** | |
| ** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
| ** | |
| ** Software rendering | |
| */ | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "system.h" | |
| #include "6502.h" | |
| #include "via.h" | |
| #include "8912.h" | |
| #include "disk.h" | |
| #include "gui.h" | |
| #include "monitor.h" | |
| #include "6551.h" | |
| #include "machine.h" | |
| #include "render_sw8.h" | |
| #include "ula.h" | |
| // this *should* be 16, but on AmigaOS that messes | |
| // with the pointer colours.. | |
| #define GPAL_FIRSTPEN 20 | |
| static struct SDL_Surface *screen; | |
| static Uint32 offset_top; | |
| static Uint32 qpen[16*256]; | |
| extern SDL_bool fullscreen, hwsurface; | |
| static SDL_bool needclr; | |
| extern struct textzone *tz[NUM_TZ]; | |
| extern unsigned char sgpal[]; | |
| extern Uint8 oricpalette[]; | |
| extern struct guiimg gimgs[NUM_GIMG]; | |
| extern SDL_bool refreshstatus; | |
| static Uint8 *mgimg[NUM_GIMG]; | |
| static int next_gimgcol; | |
| static SDL_Color colours[256]; | |
| // Our "lovely" hand-coded font | |
| extern unsigned char thefont[]; | |
| // --- printchar template function -------------------------------------------- | |
| // Print a char onto a X-bit framebuffer, X being a power of 2 | |
| // ptr = where to draw it | |
| // ch = which char to draw | |
| // fcol = foreground colour | |
| // bcol = background colour | |
| // solidfont = use background colour | |
| static void printchar( Uint8* ptr, unsigned char ch, Uint8 fcol, Uint8 bcol, SDL_bool solidfont ) | |
| { | |
| int x, y, mask; | |
| const Uint8 *src_byte; | |
| Uint8 *dst_pixel; | |
| Uint8 *dst_scanline; | |
| if( ch > 127 ) return; | |
| src_byte = &thefont[ch*12]; | |
| dst_scanline = ptr; | |
| for( y=12; y!=0; --y, ++src_byte, dst_scanline+=screen->pitch ) | |
| { | |
| dst_pixel = dst_scanline; | |
| for( mask=0x80, x=8; x!=0; --x, ++dst_pixel, mask>>=1 ) | |
| { | |
| if( (*src_byte)&mask ) | |
| *dst_pixel = fcol; | |
| else if( solidfont ) | |
| *dst_pixel = bcol; | |
| } | |
| } | |
| } | |
| void render_begin_sw8( struct machine *oric ) | |
| { | |
| int y; | |
| Uint8 *dst_scanline; | |
| if( SDL_MUSTLOCK( screen ) ) | |
| SDL_LockSurface( screen ); | |
| if( oric->newpopupstr ) | |
| { | |
| dst_scanline = (Uint8*)screen->pixels; | |
| dst_scanline += 320; | |
| for( y=12; y!=0; --y, dst_scanline += screen->pitch) | |
| memset(dst_scanline, GPAL_FIRSTPEN, 320); | |
| oric->newpopupstr = SDL_FALSE; | |
| } | |
| if( oric->newstatusstr ) | |
| { | |
| refreshstatus = SDL_TRUE; | |
| oric->newstatusstr = SDL_FALSE; | |
| } | |
| } | |
| void render_end_sw8( struct machine *oric ) | |
| { | |
| int i; | |
| Uint32 char_pitch; | |
| Uint8 *dst_pixel; | |
| char_pitch = 8; | |
| if( oric->emu_mode == EM_RUNNING ) | |
| { | |
| if( oric->popupstr[0] ) | |
| { | |
| dst_pixel = (Uint8*)screen->pixels; | |
| dst_pixel += 320; | |
| for( i=0; oric->popupstr[i]; i++, dst_pixel += char_pitch ) | |
| printchar( dst_pixel, oric->popupstr[i], GPAL_FIRSTPEN+1, GPAL_FIRSTPEN, SDL_TRUE ); | |
| } | |
| if( oric->statusstr[0] ) | |
| { | |
| dst_pixel = (Uint8*)screen->pixels; | |
| dst_pixel += 466 * screen->pitch; | |
| for( i=0; oric->statusstr[i]; i++, dst_pixel += char_pitch ) | |
| printchar( dst_pixel, oric->statusstr[i], GPAL_FIRSTPEN+1, 0, SDL_FALSE ); | |
| } | |
| } | |
| if( SDL_MUSTLOCK( screen ) ) | |
| SDL_UnlockSurface( screen ); | |
| SDL_COMPAT_Flip( screen ); | |
| } | |
| void render_textzone_alloc_sw8( struct machine *oric, int i ) | |
| { | |
| } | |
| void render_textzone_free_sw8( struct machine *oric, int i ) | |
| { | |
| } | |
| void render_textzone_sw8( struct machine *oric, int i ) | |
| { | |
| int x, y, o; | |
| struct textzone *ptz = tz[i]; | |
| Uint8 *dst_scanline, *dst_pixel; | |
| dst_scanline = (Uint8 *)screen->pixels; | |
| dst_scanline += screen->pitch * ptz->y + ptz->x; | |
| o = 0; | |
| for( y=ptz->h; y!=0; --y, dst_scanline+=12*screen->pitch ) | |
| { | |
| dst_pixel = dst_scanline; | |
| for( x=ptz->w; x!=0; --x, ++o, dst_pixel+=8 ) | |
| printchar( dst_pixel, ptz->tx[o], GPAL_FIRSTPEN+ptz->fc[o], GPAL_FIRSTPEN+ptz->bc[o], SDL_TRUE ); | |
| } | |
| } | |
| // Clear an area to background | |
| void render_clear_area_sw8( int x, int y, int w, int h ) | |
| { | |
| Uint8 *dst_scanline; | |
| Sint32 i; | |
| dst_scanline = (Uint8 *)screen->pixels; | |
| dst_scanline += screen->pitch * y + x; | |
| for( i=0; i<h; i++, dst_scanline+=screen->pitch ) | |
| memset( dst_scanline, 0, w ); | |
| } | |
| // Draw a GUI image at X,Y | |
| void render_gimg_sw8( int img_id, Sint32 xp, Sint32 yp ) | |
| { | |
| struct guiimg *gi = &gimgs[img_id]; | |
| Uint8 *src_scanline, *dst_scanline; | |
| Sint32 i; | |
| src_scanline = mgimg[img_id]; | |
| dst_scanline = (Uint8 *)screen->pixels; | |
| dst_scanline += screen->pitch * yp + xp; | |
| for( i=gi->h; i!=0; --i, src_scanline+=gi->w, dst_scanline+=screen->pitch ) | |
| memcpy( dst_scanline, src_scanline, gi->w ); | |
| } | |
| // Draw part of an image (xp,yp = screen location, ox, oy = offset into image, w, h = dimensions) | |
| void render_gimgpart_sw8( int img_id, Sint32 xp, Sint32 yp, Sint32 ox, Sint32 oy, Sint32 w, Sint32 h ) | |
| { | |
| struct guiimg *gi = &gimgs[img_id]; | |
| Uint8 *src_scanline, *dst_scanline; | |
| Sint32 i; | |
| src_scanline = mgimg[img_id]; | |
| src_scanline += gi->w * oy + ox; | |
| dst_scanline = (Uint8 *)screen->pixels; | |
| dst_scanline += screen->pitch * yp + xp; | |
| for( i=h; i!=0; --i, src_scanline+=gi->w, dst_scanline+=screen->pitch ) | |
| memcpy( dst_scanline, src_scanline, w ); | |
| } | |
| // Copy the video output buffer to the SDL surface, assuming 16bpp video mode | |
| void render_video_sw8( struct machine *oric, SDL_bool doublesize ) | |
| { | |
| int x, y; | |
| Uint16 *src_pixel; | |
| Sint32 dst_pitch_x2; | |
| Uint32 c; | |
| Uint8 *dst_scanline, *dst_even_scanline, *dst_odd_scanline; | |
| Uint32 *dst_even_pixel, *dst_odd_pixel; | |
| if( !oric->scr ) | |
| return; | |
| if( doublesize ) | |
| { | |
| if( needclr ) | |
| { | |
| SDL_FillRect(screen, NULL, GPAL_FIRSTPEN); | |
| needclr = SDL_FALSE; | |
| } | |
| src_pixel = (Uint16 *)oric->scr; | |
| dst_pitch_x2 = 2 * screen->pitch; | |
| dst_even_scanline = ((Uint8*)screen->pixels) + offset_top; | |
| dst_odd_scanline = dst_even_scanline; | |
| dst_odd_scanline += screen->pitch; | |
| if( oric->scanlines ) | |
| { | |
| for( y=0; y<224; y++, dst_even_scanline+=dst_pitch_x2, dst_odd_scanline+=dst_pitch_x2 ) | |
| { | |
| if (!oric->vid_dirty[y]) | |
| { | |
| src_pixel += 120; | |
| continue; | |
| } | |
| dst_even_pixel = (Uint32*)dst_even_scanline; | |
| dst_odd_pixel = (Uint32*)dst_odd_scanline; | |
| for( x=0; x<120; x++ ) | |
| { | |
| c = *(src_pixel++); | |
| *(dst_even_pixel++) = qpen[c]; | |
| *(dst_odd_pixel++) = qpen[c+8*257]; | |
| } | |
| oric->vid_dirty[y] = SDL_FALSE; | |
| } | |
| } else { | |
| for( y=0; y<224; y++, dst_even_scanline+=dst_pitch_x2, dst_odd_scanline+=dst_pitch_x2 ) | |
| { | |
| if (!oric->vid_dirty[y]) | |
| { | |
| src_pixel += 120; | |
| continue; | |
| } | |
| dst_even_pixel = (Uint32*)dst_even_scanline; | |
| dst_odd_pixel = (Uint32*)dst_odd_scanline; | |
| for( x=0; x<120; x++ ) | |
| { | |
| c = qpen[*(src_pixel++)]; | |
| *(dst_even_pixel++) = c; | |
| *(dst_odd_pixel++) = c; | |
| } | |
| oric->vid_dirty[y] = SDL_FALSE; | |
| } | |
| } | |
| return; | |
| } | |
| needclr = SDL_TRUE; | |
| src_pixel = (Uint16 *)oric->scr; | |
| dst_scanline = (Uint8*)screen->pixels; | |
| for( y=0; y<4; y++ ) | |
| { | |
| memset( dst_scanline, 0, 240 ); | |
| dst_scanline += screen->pitch; | |
| } | |
| for( ; y<228; y++, dst_scanline+=screen->pitch ) | |
| { | |
| memcpy(dst_scanline, src_pixel, 240); | |
| src_pixel += 240; | |
| } | |
| } | |
| void preinit_render_sw8( struct machine *oric ) | |
| { | |
| // Screen surface is not set yet | |
| screen = NULL; | |
| } | |
| SDL_bool render_togglefullscreen_sw8( struct machine *oric ) | |
| { | |
| #if defined(__amigaos4__) || defined(__linux__) | |
| // Use SDL_WM_ToggleFullScreen on systems where it is supported | |
| if( SDL_COMPAT_WM_ToggleFullScreen( screen ) ) | |
| { | |
| fullscreen = !fullscreen; | |
| return SDL_TRUE; | |
| } | |
| return SDL_FALSE; | |
| #else | |
| oric->shut_render( oric ); | |
| fullscreen = !fullscreen; | |
| if( oric->init_render( oric ) ) return SDL_TRUE; | |
| set_render_mode( oric, RENDERMODE_NULL ); | |
| oric->emu_mode = EM_PLEASEQUIT; | |
| return SDL_FALSE; | |
| #endif | |
| } | |
| static int similar_colour(int i, int r, int g, int b) | |
| { | |
| return abs(r-(int)colours[i].r)+abs(g-(int)colours[i].g)+abs(b-(int)colours[i].b); | |
| } | |
| static int find_best_pen(Uint8 r, int g, int b) | |
| { | |
| int i; | |
| int closest_pen=-1, closest_distance=8000; | |
| /* First, look for an exact match */ | |
| for( i=0; i<next_gimgcol; i++ ) | |
| { | |
| if( (colours[i].r == r) && | |
| (colours[i].g == g) && | |
| (colours[i].b == b) ) | |
| return i; | |
| if (similar_colour(i, r, g, b) < closest_distance) | |
| { | |
| closest_pen = i; | |
| closest_distance = similar_colour(i, r, g, b); | |
| } | |
| } | |
| /* Any really close? */ | |
| if (closest_distance < 3) | |
| return closest_pen; | |
| /* Can we make a new pen? */ | |
| if (next_gimgcol >= 256) | |
| return closest_pen; | |
| colours[next_gimgcol].r = r; | |
| colours[next_gimgcol].g = g; | |
| colours[next_gimgcol].b = b; | |
| return next_gimgcol++; | |
| } | |
| static SDL_bool guiimg_to_img(Uint8** dst, const struct guiimg* src) | |
| { | |
| size_t i; | |
| const Uint8 *src_pixel; | |
| Uint8 *dst_pixel; | |
| (*dst) = (Uint8 *)malloc( src->w*src->h ); | |
| if ((*dst) == NULL) | |
| return SDL_FALSE; | |
| src_pixel = src->buf; | |
| dst_pixel = *dst; | |
| for( i=src->w*src->h; i!=0; --i, src_pixel += 3, ++dst_pixel ) | |
| (*dst_pixel) = find_best_pen(src_pixel[0], src_pixel[1], src_pixel[2]); | |
| return SDL_TRUE; | |
| } | |
| SDL_bool init_render_sw8( struct machine *oric ) | |
| { | |
| int i, j; | |
| Sint32 surfacemode; | |
| surfacemode = SDL_COMPAT_HWPALETTE; | |
| if( fullscreen ) surfacemode |= SDL_COMPAT_FULLSCREEN; | |
| if( hwsurface ) surfacemode |= SDL_COMPAT_HWSURFACE; | |
| // Try to setup the video display | |
| screen = SDL_COMPAT_SetVideoMode( 640, 480, 8, surfacemode ); | |
| if (oric->show_keyboard) { | |
| screen = SDL_COMPAT_SetVideoMode( 640, 480+240, 8, surfacemode ); | |
| } else { | |
| screen = SDL_COMPAT_SetVideoMode( 640, 480, 8, surfacemode ); | |
| } | |
| if( !screen ) | |
| { | |
| printf( "SDL video failed\n" ); | |
| return SDL_FALSE; | |
| } | |
| SDL_COMPAT_WM_SetCaption( APP_NAME_FULL, APP_NAME_FULL ); | |
| for( i=0; i<8; i++ ) | |
| { | |
| colours[i].r = oricpalette[i*3 ]; | |
| colours[i+8].r = oricpalette[i*3 ]/2; | |
| colours[i].g = oricpalette[i*3+1]; | |
| colours[i+8].g = oricpalette[i*3+1]/2; | |
| colours[i].b = oricpalette[i*3+2]; | |
| colours[i+8].b = oricpalette[i*3+2]/2; | |
| } | |
| for( i=0; i<NUM_GUI_COLS; i++ ) | |
| { | |
| colours[i+GPAL_FIRSTPEN].r = sgpal[i*3 ]; | |
| colours[i+GPAL_FIRSTPEN].g = sgpal[i*3+1]; | |
| colours[i+GPAL_FIRSTPEN].b = sgpal[i*3+2]; | |
| } | |
| next_gimgcol = GPAL_FIRSTPEN+NUM_GUI_COLS; | |
| // Get the images for the GUI | |
| for( i=0; i<NUM_GIMG; i++ ) | |
| if (!guiimg_to_img(mgimg + i, gimgs + i)) | |
| return SDL_FALSE; | |
| SDL_COMPAT_SetPalette( screen, SDL_COMPAT_LOGPAL|SDL_COMPAT_PHYSPAL, colours, 0, next_gimgcol); | |
| // Precompute pixel quads for efficient rendering double size | |
| for( i=0; i<8*2; i++ ) | |
| for( j=0; j<8*2; j++ ) | |
| qpen[(j<<8)|i] = (j<<24)|(j<<16)|(i<<8)|i; | |
| // For the first frame rendered, we need to clean the screen | |
| needclr = SDL_TRUE; | |
| refreshstatus = SDL_TRUE; | |
| // Calculate the offset to render the screen | |
| offset_top = (240 - 226) * screen->pitch + 80; | |
| ula_set_dirty( oric ); | |
| // Job done | |
| return SDL_TRUE; | |
| } | |
| void shut_render_sw8( struct machine *oric ) | |
| { | |
| // The surface will be freed by SDL_Quit, or a call to SDL_COMPAT_SetVideoMode from a different render module | |
| } |