/* * Display.c - Darstellung der C64-Grafik, * Handhabung des Emulatorfensters * * Copyright (C) 1994-1996 by Christian Bauer */ /* * Anmerkungen: * ------------ * * - Die Farbpalette besteht aus den 16 C64-Farben, 16mal wiederholt. * Dadurch spart man sich das Ausmaskieren der unteren 4 Bit bei den * VIC-Farbcodes. Allerdings muß dieses bei der Chunky->Planar- * Konvertierung für die Amiga-Chips erfolgen (der Algorithmus * setzt voraus, daß die oberen Nibbles Null sind). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Display.h" #include "SAM.h" #include "Prefs.h" #define CATCOMP_NUMBERS 1 #include "LocStrings.h" // Aus Main.asm extern struct Library *GfxBase; extern struct Library *IntuitionBase; extern void ShowPrefs(void); extern void PutChProc(void); extern char *GetStr(int strnum); // Aus 6510.asm extern struct Task *CPUTask; extern void Pause6510(void); extern void Resume6510(void); extern ULONG InvokeSAMSet; // Aus 6526.asm extern void KeyPressed(char c); // Aus 6569.asm extern APTR CURRENTA5; extern BYTE ChunkyBuf[]; extern UWORD SkipLatch; extern UWORD LimitSpeed; // Aus 6581.asm extern void PauseSound(void); extern void ResumeSound(void); // Aus c2p4.asm extern void c2p4(UBYTE *fBUFFER, UBYTE *fBUFFER_CMP, APTR *planes, struct Task *task, ULONG signals); extern int Initc2p4(void); extern void Exitc2p4(void); // Prototypes void open_double_buf(int type, int width, int height); int handle_IDCMP(int done); int handle_menu(int menu, int item, int done); char has_gfx_39, has_gfx_40; // Flags für Version der graphics.library int current_type, current_width, current_height; struct Screen *the_screen; struct Window *the_window; struct VisualInfo *the_visual_info; struct RastPort *the_rast_port; struct ViewPort *the_view_port; struct TextFont *topaz_font; struct Menu *the_menus; // Für WritePixelArray8() struct BitMap *temp_bm; struct BitMap v37_temp_bm; struct RastPort temp_rp; // Double Buffering struct ScreenBuffer *scr_buf[2]; int inv_buf_num; char using_db; // Flag: Double Buffering wird benutzt // c2p4 APTR comparison_buf[2]; char c2p4_signal; // Signal: c2p4 fertig LONGBITS c2p4_set; char must_wait_for_c2p4; // Flag: Auf c2p4 muß gewartet werden // Geschwindigkeitsanzeige struct MsgPort *timer_port; struct timerequest *timer_io; const struct TextAttr topaz_attr = { "topaz.font", 8, FS_NORMAL, 0 }; struct NewMenu new_menus[] = { NM_TITLE, MSG_EMULATION_MENU, NULL, 0, 0, NULL, NM_ITEM, MSG_SETTINGS_MENU, NULL, 0, 0, NULL, NM_ITEM, MSG_SAM_MENU, "M", 0, 0, NULL, NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL, NM_ITEM, MSG_QUIT_MENU, "Q", 0, 0, NULL, NM_END, NULL, NULL, 0, 0, NULL }; const UBYTE palette_red[16] = { 0, 15, 12, 0, 15, 0, 0, 15, 15, 8, 15, 4, 8, 8, 8, 12 }; const UBYTE palette_green[16] = { 0, 15, 0, 15, 0, 12, 0, 15, 8, 4, 8, 4, 8, 15, 8, 12 }; const UBYTE palette_blue[16] = { 0, 15, 0, 12, 15, 0, 12, 0, 0, 0, 8, 4, 8, 8, 15, 12 }; /* * Screen und Fenster öffnen, alles vorbereiten * 0: OK, 1: Fehler beim Screen-Öffnen, 2: Kein Speicher */ int OpenDisplay(int type, ULONG display_id, UWORD overscan, int width, int height) { int depth; int i; has_gfx_39 = (GfxBase->lib_Version >= 39); has_gfx_40 = (GfxBase->lib_Version >= 40); must_wait_for_c2p4 = FALSE; using_db = FALSE; current_type = type; current_width = width; current_height = height; switch (type) { case SCRTYPE_8BIT: depth = 8; break; case SCRTYPE_4BIT: depth = 4; break; case SCRTYPE_1BIT: depth = 1; break; } if (!(the_screen = OpenScreenTags(NULL, SA_Width, width, SA_Height, height, SA_DisplayID, display_id, SA_Overscan, overscan, SA_Depth, depth, SA_Quiet, TRUE, SA_AutoScroll, TRUE, TAG_DONE))) return 1; the_rast_port = &the_screen->RastPort; the_view_port = &the_screen->ViewPort; if (topaz_font = OpenFont(&topaz_attr)) SetFont(the_rast_port, topaz_font); SetAPen(the_rast_port, 1); if (!(the_visual_info = GetVisualInfo(the_screen, NULL))) return 2; if (!(the_menus = CreateMenus(new_menus, GTMN_FullMenu, TRUE, TAG_DONE))) return 2; LayoutMenus(the_menus, the_visual_info, GTMN_NewLookMenus, TRUE, TAG_DONE); if (!(the_window = OpenWindowTags(NULL, WA_Left, 0, WA_Top, 0, WA_Width, width, WA_Height, height, WA_CustomScreen, the_screen, WA_IDCMP, IDCMP_RAWKEY | IDCMP_MENUPICK | IDCMP_MENUVERIFY, WA_Backdrop, TRUE, WA_Borderless, TRUE, WA_NoCareRefresh, TRUE, WA_Activate, TRUE, WA_NewLookMenus, TRUE, TAG_DONE))) return 2; SetMenuStrip(the_window, the_menus); switch (type) { case SCRTYPE_8BIT: // Farbpalette laden for (i=0; i<256; i++) SetRGB4(the_view_port, i, palette_red[i & 0xf], palette_green[i & 0xf], palette_blue[i & 0xf]); // Temporären RastPort für WritePixelArray8() anlegen if (has_gfx_39) { if (!(temp_bm = AllocBitMap(width, 1, 8, 0, NULL))) return 2; } else { InitBitMap(temp_bm = &v37_temp_bm, depth, width, 1); for (i=0; i<8; i++) if (!(v37_temp_bm.Planes[i] = AllocRaster(width, 1))) return 2; } InitRastPort(&temp_rp); temp_rp.BitMap = temp_bm; CURRENTA5 = ChunkyBuf; // Wird nicht mehr verändert break; case SCRTYPE_4BIT: if (!Initc2p4()) return 2; // Speicher für Comparison Buffer holen if (!(comparison_buf[0] = AllocVec(width * height, MEMF_PUBLIC | MEMF_CLEAR))) return 2; // Farbpalette laden for (i=0; i<16; i++) SetRGB4(the_view_port, i, palette_red[i & 0xf], palette_green[i & 0xf], palette_blue[i & 0xf]); CURRENTA5 = ChunkyBuf; open_double_buf(type, width, height); must_wait_for_c2p4 = FALSE; break; case SCRTYPE_1BIT: SetRGB4(the_view_port, 0, 0, 0, 0); // schwarz SetRGB4(the_view_port, 1, 15, 15, 15); // weiß CURRENTA5 = the_rast_port->BitMap->Planes[0]; open_double_buf(type, width, height); break; } return 0; } /* * Double Buffering einrichten */ void open_double_buf(int type, int width, int height) { using_db = FALSE; if (IntuitionBase->lib_Version >= 39) { if (!(scr_buf[0] = AllocScreenBuffer(the_screen, NULL, SB_SCREEN_BITMAP))) return; if (!(scr_buf[1] = AllocScreenBuffer(the_screen, NULL, SB_COPY_BITMAP))) return; WaitTOF(); WaitTOF(); inv_buf_num = 1; if (type == SCRTYPE_4BIT) { CURRENTA5 = ChunkyBuf; if (!(comparison_buf[1] = AllocVec(width * height, MEMF_PUBLIC | MEMF_CLEAR))) return; } else { CURRENTA5 = scr_buf[1]->sb_BitMap->Planes[0]; } using_db = TRUE; } } /* * Screen und Fenster schließen */ void CloseDisplay(void) { int i; if (current_type == SCRTYPE_4BIT) Exitc2p4(); if (comparison_buf[0]) { FreeVec(comparison_buf[0]); comparison_buf[0] = NULL; } if (comparison_buf[1]) { FreeVec(comparison_buf[1]); comparison_buf[1] = NULL; } FreeScreenBuffer(the_screen, scr_buf[0]); // NULL ist OK scr_buf[0] = NULL; FreeScreenBuffer(the_screen, scr_buf[1]); scr_buf[1] = NULL; if (the_menus) { ClearMenuStrip(the_window); FreeMenus(the_menus); the_menus = NULL; } if (the_window) { CloseWindow(the_window); the_window = NULL; } // Temporären RastPort schließen if (temp_bm) { if (has_gfx_39) FreeBitMap(temp_bm); else { for (i=0; i<8; i++) { FreeRaster(v37_temp_bm.Planes[i], current_width, current_height); v37_temp_bm.Planes[i] = NULL; } } temp_bm = NULL; } if (the_visual_info) { FreeVisualInfo(the_visual_info); the_visual_info = NULL; } if (topaz_font) { CloseFont(topaz_font); topaz_font = NULL; } if (the_screen) { CloseScreen(the_screen); the_screen = NULL; } } /* * Vom 6510-Task aus nötige Initialisierungen * Wird vom 6510-Task aufgerufen */ void InitDisplayFrom6510(void) { // Signal für c2p4 c2p4_signal = AllocSignal(-1); c2p4_set = 1 << c2p4_signal; // TimerIO einrichten if (timer_port = CreateMsgPort()) { if (timer_io = CreateIORequest(timer_port, sizeof(struct timerequest))) { OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)timer_io, 0); // timer_io für Speed Limiter starten timer_io->tr_node.io_Command = TR_ADDREQUEST; timer_io->tr_time.tv_secs = 0; timer_io->tr_time.tv_micro = SkipLatch * 20000; // 20ms pro Bild SendIO((struct IORequest *)timer_io); } } } /* * Vom 6510-Task aus nötige Aufräumarbeiten * Wird vom 6510-Task aufgerufen */ void ExitDisplayFrom6510(void) { if (timer_io) { if (!CheckIO((struct IORequest *)timer_io)) WaitIO((struct IORequest *)timer_io); CloseDevice((struct IORequest *)timer_io); DeleteIORequest((struct IORequest *)timer_io); timer_io = NULL; } if (timer_port) { DeleteMsgPort(timer_port); timer_port = NULL; } // Ggf. auf Signal von c2p4 warten if (must_wait_for_c2p4) Wait(c2p4_set); FreeSignal(c2p4_signal); } /* * C64-Grafik neu darstellen (VBlank, Double Buffering) * Wird vom 6510-Task aufgerufen */ struct timeval start_time, end_time; char speed_str[20]; int percent; void RedrawDisplay(void) { switch (current_type) { // SCRTYPE_8BIT: WritePixelLine in 6569.asm case SCRTYPE_4BIT: // Double Buffering und c2p-Konvertierung if (must_wait_for_c2p4) Wait(c2p4_set); if (using_db) { while (!ChangeScreenBuffer(the_screen, scr_buf[inv_buf_num])) WaitTOF(); inv_buf_num ^= 1; c2p4(ChunkyBuf, comparison_buf[inv_buf_num], scr_buf[inv_buf_num]->sb_BitMap->Planes, CPUTask, c2p4_set); } else c2p4(ChunkyBuf, comparison_buf[0], the_rast_port->BitMap->Planes, CPUTask, c2p4_set); must_wait_for_c2p4 = TRUE; CURRENTA5 = ChunkyBuf; break; case SCRTYPE_1BIT: // Double Buffering if (using_db) { while (!ChangeScreenBuffer(the_screen, scr_buf[inv_buf_num])) WaitTOF(); inv_buf_num ^= 1; CURRENTA5 = scr_buf[inv_buf_num]->sb_BitMap->Planes[0]; } else CURRENTA5 = the_rast_port->BitMap->Planes[0]; break; } if (timer_io) { // timer_io abbrechen, wenn Speed Limiter aus ist if (!LimitSpeed) if (!CheckIO((struct IORequest *)timer_io)) AbortIO((struct IORequest *)timer_io); WaitIO((struct IORequest *)timer_io); // timer_io neu starten timer_io->tr_node.io_Command = TR_ADDREQUEST; timer_io->tr_time.tv_secs = 0; timer_io->tr_time.tv_micro = SkipLatch * 20000; // 20ms pro Bild SendIO((struct IORequest *)timer_io); } // Zeit seit letztem Aufruf ermitteln GetSysTime(&end_time); SubTime(&end_time, &start_time); GetSysTime(&start_time); // Ein Bild sollte 20ms dauern percent = (20000 * 100 / end_time.tv_micro + 1) * SkipLatch; RawDoFmt("%ld%%", &percent, &PutChProc, speed_str); Move(the_rast_port, 4, current_height-6); Text(the_rast_port, speed_str, strlen(speed_str)); } /* * C64-Grafik nach hinten */ void EmulToBack(void) { ScreenToBack(the_screen); ModifyIDCMP(the_window, IDCMP_RAWKEY); ClearMenuStrip(the_window); } /* * C64-Grafik nach vorne */ void EmulToFront(void) { ScreenToFront(the_screen); ModifyIDCMP(the_window, IDCMP_RAWKEY | IDCMP_MENUPICK | IDCMP_MENUVERIFY); SetMenuStrip(the_window, the_menus); } /* * Event-Schleife des Fensters */ void EventLoop(void) { int done = FALSE; ULONG signals; while (!done) { signals = Wait((1 << the_window->UserPort->mp_SigBit) | InvokeSAMSet); if (signals & (1 << the_window->UserPort->mp_SigBit)) done = handle_IDCMP(done); if (signals & InvokeSAMSet) handle_menu(0, 1, done); } } /* * IDCMP-Messages handhaben */ int handle_IDCMP(int done) { struct IntuiMessage *msg; ULONG class; UWORD code; UWORD menu_number; struct MenuItem *item; while (!done && (msg = GetMsg(the_window->UserPort))) { class = msg->Class; code = msg->Code; if (class == IDCMP_MENUVERIFY) Pause6510(); // Subtask stoppen ReplyMsg((struct Message *)msg); switch (class) { case IDCMP_MENUPICK: Resume6510(); // Subtask fortführen menu_number = code; while (!done && (menu_number != MENUNULL)) { item = ItemAddress(the_window->MenuStrip, menu_number); done = handle_menu(MENUNUM(menu_number), ITEMNUM(menu_number), done); menu_number = item->NextSelect; } break; case IDCMP_RAWKEY: if (code == 0x58) // F9 -> Einstellungen handle_menu(0, 0, done); else KeyPressed(code); break; } } return done; } /* * Menü wurde ausgewählt */ int handle_menu(int menu, int item, int done) { if (menu == 0) { switch (item) { case 0: // Einstellungen Pause6510(); PauseSound(); EmulToBack(); ShowPrefs(); EmulToFront(); ResumeSound(); Resume6510(); break; case 1: // SAM Pause6510(); PauseSound(); EmulToBack(); SAM(); EmulToFront(); ResumeSound(); Resume6510(); break; case 3: // Quit done = TRUE; break; } } return done; } /* * Requester anzeigen */ int ShowRequester(int text, int gads, APTR args) { struct EasyStruct es; es.es_StructSize = sizeof(struct EasyStruct); es.es_Flags = 0; es.es_Title = GetStr(MSG_REQTITLE); es.es_TextFormat = GetStr(text); es.es_GadgetFormat = GetStr(gads); return EasyRequestArgs(NULL, &es, NULL, args); } /* * Lokalisierungen vornehmen */ void LocalizeDisplay(void) { int i; for (i=0; new_menus[i].nm_Type != NM_END; i++) if (new_menus[i].nm_Label != NM_BARLABEL) new_menus[i].nm_Label = GetStr((int)new_menus[i].nm_Label); }