home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD1.iso / Emulatoren / FS_FRO23.LZX / Frodo / src / Display.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-13  |  14.5 KB  |  661 lines

  1. /*
  2.  *  Display.c - Darstellung der C64-Grafik,
  3.  *              Handhabung des Emulatorfensters
  4.  *
  5.  *  Copyright (C) 1994-1996 by Christian Bauer
  6.  */
  7.  
  8. /*
  9.  *  Anmerkungen:
  10.  *  ------------
  11.  *
  12.  *  - Die Farbpalette besteht aus den 16 C64-Farben, 16mal wiederholt.
  13.  *    Dadurch spart man sich das Ausmaskieren der unteren 4 Bit bei den
  14.  *    VIC-Farbcodes. Allerdings muß dieses bei der Chunky->Planar-
  15.  *    Konvertierung für die Amiga-Chips erfolgen (der Algorithmus
  16.  *    setzt voraus, daß die oberen Nibbles Null sind).
  17.  */
  18.  
  19. #include <exec/types.h>
  20. #include <exec/libraries.h>
  21. #include <exec/memory.h>
  22. #include <intuition/intuition.h>
  23. #include <intuition/screens.h>
  24. #include <libraries/gadtools.h>
  25. #include <devices/timer.h>
  26. #include <clib/exec_protos.h>
  27. #include <clib/intuition_protos.h>
  28. #include <clib/graphics_protos.h>
  29. #include <clib/gadtools_protos.h>
  30. #include <clib/timer_protos.h>
  31. #include <string.h>
  32.  
  33. #include "Display.h"
  34. #include "SAM.h"
  35. #include "Prefs.h"
  36. #define CATCOMP_NUMBERS 1
  37. #include "LocStrings.h"
  38.  
  39.  
  40. // Aus Main.asm
  41. extern struct Library *GfxBase;
  42. extern struct Library *IntuitionBase;
  43. extern void ShowPrefs(void);
  44. extern void PutChProc(void);
  45. extern char *GetStr(int strnum);
  46. extern void ResetC64(void);
  47.  
  48. // Aus 6510.asm
  49. extern struct Task *CPUTask;
  50. extern void Pause6510(void);
  51. extern void Resume6510(void);
  52. extern ULONG InvokeSAMSet;
  53.  
  54. // Aus 6526.asm
  55. extern void KeyPressed(char c);
  56. extern WORD JoystickSwap;
  57.  
  58. // Aus 6569.asm
  59. extern APTR CURRENTA5;
  60. extern BYTE ChunkyBuf[];
  61. extern UWORD SkipLatch;
  62. extern UWORD LimitSpeed;
  63.  
  64. // Aus 6581.asm
  65. extern void PauseSound(void);
  66. extern void ResumeSound(void);
  67.  
  68. // Aus c2p4.asm
  69. extern void c2p4(UBYTE *fBUFFER, UBYTE *fBUFFER_CMP, APTR *planes, struct Task *task, ULONG signals);
  70. extern int Initc2p4(void);
  71. extern void Exitc2p4(void);
  72.  
  73.  
  74. // Prototypes
  75. void open_double_buf(int type, int width, int height);
  76. int handle_IDCMP(int done);
  77. int handle_menu(int menu, int item, int done);
  78.  
  79.  
  80. char has_gfx_39, has_gfx_40;  // Flags für Version der graphics.library
  81.  
  82. int current_type, current_width, current_height;
  83.  
  84. struct Screen *the_screen;
  85. struct Window *the_window;
  86. struct VisualInfo *the_visual_info;
  87. struct RastPort *the_rast_port;
  88. struct ViewPort *the_view_port;
  89. struct TextFont *topaz_font;
  90. struct Menu *the_menus;
  91.  
  92. // Für WritePixelArray8()
  93. struct BitMap *temp_bm;
  94. struct BitMap v37_temp_bm;
  95. struct RastPort temp_rp;
  96.  
  97. // Double Buffering
  98. struct ScreenBuffer *scr_buf[2];
  99. int inv_buf_num;
  100. char using_db;                // Flag: Double Buffering wird benutzt
  101.  
  102. // c2p4
  103. APTR comparison_buf[2];
  104. char c2p4_signal;            // Signal: c2p4 fertig
  105. LONGBITS c2p4_set;
  106. char must_wait_for_c2p4;    // Flag: Auf c2p4 muß gewartet werden
  107.  
  108. // Geschwindigkeitsanzeige
  109. struct MsgPort *timer_port;
  110. struct timerequest *timer_io;
  111.  
  112.  
  113. const struct TextAttr topaz_attr = {
  114.   "topaz.font", 8, FS_NORMAL, 0
  115. };
  116.  
  117. struct NewMenu new_menus[] = {
  118.   NM_TITLE, MSG_EMULATION_MENU, NULL, 0, 0, NULL,
  119.   NM_ITEM, MSG_SETTINGS_MENU, "P", 0, 0, NULL,
  120.   NM_ITEM, MSG_SAM_MENU, "M", 0, 0, NULL,
  121.   NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL,
  122.   NM_ITEM, MSG_QUIT_MENU, "Q", 0, 0, NULL,
  123.   NM_END, NULL, NULL, 0, 0, NULL
  124. };
  125.  
  126. const UBYTE palette_red[16] = {
  127.   0, 15, 12, 0, 15, 0, 0, 15, 15, 8, 15, 4, 8, 8, 8, 12
  128. };
  129.  
  130. const UBYTE palette_green[16] = {
  131.   0, 15, 0, 15, 0, 12, 0, 15, 8, 4, 8, 4, 8, 15, 8, 12
  132. };
  133.  
  134. const UBYTE palette_blue[16] = {
  135.   0, 15, 0, 12, 15, 0, 12, 0, 0, 0, 8, 4, 8, 8, 15, 12
  136. };
  137.  
  138.  
  139. /*
  140.  *  Screen und Fenster öffnen, alles vorbereiten
  141.  *  0: OK, 1: Fehler beim Screen-Öffnen, 2: Kein Speicher
  142.  */
  143.  
  144. int OpenDisplay(int type, ULONG display_id, UWORD overscan, int width, int height)
  145. {
  146.   int depth;
  147.   int i;
  148.  
  149.   has_gfx_39 = (GfxBase->lib_Version >= 39);
  150.   has_gfx_40 = (GfxBase->lib_Version >= 40);
  151.  
  152.   must_wait_for_c2p4 = FALSE;
  153.   using_db = FALSE;
  154.  
  155.   current_type = type;
  156.   current_width = width;
  157.   current_height = height;
  158.  
  159.   switch (type) {
  160.     case SCRTYPE_8BIT: depth = 8; break;
  161.     case SCRTYPE_4BIT: depth = 4; break;
  162.     case SCRTYPE_1BIT: depth = 1; break;
  163.   }
  164.  
  165.   if (!(the_screen = OpenScreenTags(NULL,
  166.     SA_Width, width,
  167.     SA_Height, height,
  168.     SA_DisplayID, display_id,
  169.     SA_Overscan, overscan,
  170.     SA_Depth, depth,
  171.     SA_Quiet, TRUE,
  172.     SA_AutoScroll, TRUE,
  173.     TAG_DONE)))
  174.     return 1;
  175.  
  176.   the_rast_port = &the_screen->RastPort;
  177.   the_view_port = &the_screen->ViewPort;
  178.  
  179.   if (topaz_font = OpenFont(&topaz_attr))
  180.     SetFont(the_rast_port, topaz_font);
  181.   SetAPen(the_rast_port, 1);
  182.  
  183.   if (!(the_visual_info = GetVisualInfo(the_screen, NULL)))
  184.     return 2;
  185.  
  186.   if (!(the_menus = CreateMenus(new_menus, GTMN_FullMenu, TRUE, TAG_DONE)))
  187.     return 2;
  188.   LayoutMenus(the_menus, the_visual_info, GTMN_NewLookMenus, TRUE, TAG_DONE);
  189.  
  190.   if (!(the_window = OpenWindowTags(NULL,
  191.     WA_Left, 0,
  192.     WA_Top, 0,
  193.     WA_Width, width,
  194.     WA_Height, height,
  195.     WA_CustomScreen, the_screen,
  196.     WA_IDCMP, IDCMP_RAWKEY | IDCMP_MENUPICK | IDCMP_MENUVERIFY,
  197.     WA_Backdrop, TRUE,
  198.     WA_Borderless, TRUE,
  199.     WA_NoCareRefresh, TRUE,
  200.     WA_Activate, TRUE,
  201.     WA_NewLookMenus, TRUE,
  202.     TAG_DONE)))
  203.     return 2;
  204.  
  205.   SetMenuStrip(the_window, the_menus);
  206.  
  207.   switch (type) {
  208.     case SCRTYPE_8BIT:
  209.  
  210.       // Farbpalette laden
  211.       for (i=0; i<256; i++)
  212.         SetRGB4(the_view_port, i,
  213.           palette_red[i & 0xf], palette_green[i & 0xf], palette_blue[i & 0xf]);
  214.  
  215.       // Temporären RastPort für WritePixelArray8() anlegen
  216.       if (has_gfx_39) {
  217.         if (!(temp_bm = AllocBitMap(width, 1, 8, 0, NULL)))
  218.           return 2;
  219.       } else {
  220.         InitBitMap(temp_bm = &v37_temp_bm, depth, width, 1);
  221.         for (i=0; i<8; i++)
  222.           if (!(v37_temp_bm.Planes[i] = AllocRaster(width, 1)))
  223.             return 2;
  224.       }
  225.  
  226.       InitRastPort(&temp_rp);
  227.       temp_rp.BitMap = temp_bm;
  228.  
  229.       CURRENTA5 = ChunkyBuf;  // Wird nicht mehr verändert
  230.       break;
  231.  
  232.     case SCRTYPE_4BIT:
  233.  
  234.       if (!Initc2p4())
  235.         return 2;
  236.  
  237.       // Speicher für Comparison Buffer holen
  238.       if (!(comparison_buf[0] = AllocVec(width * height, MEMF_PUBLIC | MEMF_CLEAR)))
  239.         return 2;
  240.  
  241.       // Farbpalette laden
  242.       for (i=0; i<16; i++)
  243.         SetRGB4(the_view_port, i,
  244.           palette_red[i & 0xf], palette_green[i & 0xf], palette_blue[i & 0xf]);
  245.  
  246.       CURRENTA5 = ChunkyBuf;
  247.  
  248.       open_double_buf(type, width, height);
  249.       must_wait_for_c2p4 = FALSE;
  250.       break;
  251.  
  252.     case SCRTYPE_1BIT:
  253.  
  254.       SetRGB4(the_view_port, 0, 0, 0, 0);        // schwarz
  255.       SetRGB4(the_view_port, 1, 15, 15, 15);    // weiß
  256.  
  257.       CURRENTA5 = the_rast_port->BitMap->Planes[0];
  258.  
  259.       open_double_buf(type, width, height);
  260.       break;
  261.   }
  262.  
  263.   return 0;
  264. }
  265.  
  266.  
  267. /*
  268.  *  Double Buffering einrichten
  269.  */
  270.  
  271. void open_double_buf(int type, int width, int height)
  272. {
  273.   using_db = FALSE;
  274.   if (IntuitionBase->lib_Version >= 39) {
  275.  
  276.     if (!(scr_buf[0] = AllocScreenBuffer(the_screen, NULL, SB_SCREEN_BITMAP)))
  277.       return;
  278.  
  279.     if (!(scr_buf[1] = AllocScreenBuffer(the_screen, NULL, SB_COPY_BITMAP)))
  280.       return;
  281.  
  282.     WaitTOF(); WaitTOF();
  283.     inv_buf_num = 1;
  284.  
  285.     if (type == SCRTYPE_4BIT) {
  286.       CURRENTA5 = ChunkyBuf;
  287.       if (!(comparison_buf[1] = AllocVec(width * height, MEMF_PUBLIC | MEMF_CLEAR)))
  288.         return;
  289.     } else {
  290.       CURRENTA5 = scr_buf[1]->sb_BitMap->Planes[0];
  291.     }
  292.  
  293.     using_db = TRUE;
  294.   }
  295. }
  296.  
  297.  
  298. /*
  299.  *  Screen und Fenster schließen
  300.  */
  301.  
  302. void CloseDisplay(void)
  303. {
  304.   int i;
  305.  
  306.   if (current_type == SCRTYPE_4BIT)
  307.     Exitc2p4();
  308.  
  309.   if (comparison_buf[0]) {
  310.     FreeVec(comparison_buf[0]);
  311.     comparison_buf[0] = NULL;
  312.   }
  313.  
  314.   if (comparison_buf[1]) {
  315.     FreeVec(comparison_buf[1]);
  316.     comparison_buf[1] = NULL;
  317.   }
  318.  
  319.   FreeScreenBuffer(the_screen, scr_buf[0]);  // NULL ist OK
  320.   scr_buf[0] = NULL;
  321.   FreeScreenBuffer(the_screen, scr_buf[1]);
  322.   scr_buf[1] = NULL;
  323.  
  324.   if (the_menus) {
  325.     ClearMenuStrip(the_window);
  326.     FreeMenus(the_menus);
  327.     the_menus = NULL;
  328.   }
  329.  
  330.   if (the_window) {
  331.     CloseWindow(the_window);
  332.     the_window = NULL;
  333.   }
  334.  
  335.   // Temporären RastPort schließen
  336.   if (temp_bm) {
  337.     if (has_gfx_39)
  338.       FreeBitMap(temp_bm);
  339.     else {
  340.       for (i=0; i<8; i++) {
  341.         FreeRaster(v37_temp_bm.Planes[i], current_width, current_height);
  342.         v37_temp_bm.Planes[i] = NULL;
  343.       }
  344.     }
  345.     temp_bm = NULL;
  346.   }
  347.  
  348.   if (the_visual_info) {
  349.     FreeVisualInfo(the_visual_info);
  350.     the_visual_info = NULL;
  351.   }
  352.  
  353.   if (topaz_font) {
  354.     CloseFont(topaz_font);
  355.     topaz_font = NULL;
  356.   }
  357.  
  358.   if (the_screen) {
  359.     CloseScreen(the_screen);
  360.     the_screen = NULL;
  361.   }
  362. }
  363.  
  364.  
  365. /*
  366.  *  Vom 6510-Task aus nötige Initialisierungen
  367.  *  Wird vom 6510-Task aufgerufen
  368.  */
  369.  
  370. void InitDisplayFrom6510(void)
  371. {
  372.   // Signal für c2p4
  373.   c2p4_signal = AllocSignal(-1);
  374.   c2p4_set = 1 << c2p4_signal;
  375.  
  376.   // TimerIO einrichten
  377.   if (timer_port = CreateMsgPort()) {
  378.     if (timer_io = CreateIORequest(timer_port, sizeof(struct timerequest))) {
  379.       OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)timer_io, 0);
  380.  
  381.       // timer_io für Speed Limiter starten
  382.       timer_io->tr_node.io_Command = TR_ADDREQUEST;
  383.       timer_io->tr_time.tv_secs = 0;
  384.       timer_io->tr_time.tv_micro = SkipLatch * 20000;  // 20ms pro Bild
  385.       SendIO((struct IORequest *)timer_io);
  386.     }
  387.   }
  388. }
  389.  
  390.  
  391. /*
  392.  *  Vom 6510-Task aus nötige Aufräumarbeiten
  393.  *  Wird vom 6510-Task aufgerufen
  394.  */
  395.  
  396. void ExitDisplayFrom6510(void)
  397. {
  398.   if (timer_io) {
  399.     if (!CheckIO((struct IORequest *)timer_io))
  400.       WaitIO((struct IORequest *)timer_io);
  401.     CloseDevice((struct IORequest *)timer_io);
  402.     DeleteIORequest((struct IORequest *)timer_io);
  403.     timer_io = NULL;
  404.   }
  405.  
  406.   if (timer_port) {
  407.     DeleteMsgPort(timer_port);
  408.     timer_port = NULL;
  409.   }
  410.  
  411.   // Ggf. auf Signal von c2p4 warten
  412.   if (must_wait_for_c2p4)
  413.     Wait(c2p4_set);
  414.  
  415.   FreeSignal(c2p4_signal);
  416. }
  417.  
  418.  
  419. /*
  420.  *  C64-Grafik neu darstellen (VBlank, Double Buffering)
  421.  *  Wird vom 6510-Task aufgerufen
  422.  */
  423.  
  424. struct timeval start_time, end_time;
  425. char speed_str[20];
  426. int percent;
  427.  
  428. void RedrawDisplay(void)
  429. {
  430.   switch (current_type) {
  431.     // SCRTYPE_8BIT: WritePixelLine in 6569.asm
  432.  
  433.     case SCRTYPE_4BIT:        // Double Buffering und c2p-Konvertierung
  434.       if (must_wait_for_c2p4)
  435.         Wait(c2p4_set);
  436.  
  437.       if (using_db) {
  438.         while (!ChangeScreenBuffer(the_screen, scr_buf[inv_buf_num]))
  439.           WaitTOF();
  440.         inv_buf_num ^= 1;
  441.         c2p4(ChunkyBuf, comparison_buf[inv_buf_num], scr_buf[inv_buf_num]->sb_BitMap->Planes, CPUTask, c2p4_set);
  442.       } else
  443.         c2p4(ChunkyBuf, comparison_buf[0], the_rast_port->BitMap->Planes, CPUTask, c2p4_set);
  444.  
  445.       must_wait_for_c2p4 = TRUE;
  446.       CURRENTA5 = ChunkyBuf;
  447.       break;
  448.  
  449.     case SCRTYPE_1BIT:        // Double Buffering
  450.       if (using_db) {
  451.         while (!ChangeScreenBuffer(the_screen, scr_buf[inv_buf_num]))
  452.           WaitTOF();
  453.         inv_buf_num ^= 1;
  454.         CURRENTA5 = scr_buf[inv_buf_num]->sb_BitMap->Planes[0];
  455.       } else
  456.         CURRENTA5 = the_rast_port->BitMap->Planes[0];
  457.       break;
  458.   }
  459.  
  460.   if (timer_io) {
  461.     // timer_io abbrechen, wenn Speed Limiter aus ist
  462.     if (!LimitSpeed)
  463.       if (!CheckIO((struct IORequest *)timer_io))
  464.         AbortIO((struct IORequest *)timer_io);
  465.  
  466.     WaitIO((struct IORequest *)timer_io);
  467.  
  468.     // timer_io neu starten
  469.     timer_io->tr_node.io_Command = TR_ADDREQUEST;
  470.     timer_io->tr_time.tv_secs = 0;
  471.     timer_io->tr_time.tv_micro = SkipLatch * 20000;  // 20ms pro Bild
  472.     SendIO((struct IORequest *)timer_io);
  473.   }
  474.  
  475.   // Zeit seit letztem Aufruf ermitteln
  476.   GetSysTime(&end_time);
  477.   SubTime(&end_time, &start_time);
  478.   GetSysTime(&start_time);
  479.  
  480.   // Ein Bild sollte 20ms dauern
  481.   percent = (20000 * 100 / end_time.tv_micro + 1) * SkipLatch;
  482.  
  483.   RawDoFmt("%ld%%", &percent, &PutChProc, speed_str);
  484.   Move(the_rast_port, 4, current_height-6);
  485.   Text(the_rast_port, speed_str, strlen(speed_str));
  486. }
  487.  
  488.  
  489. /*
  490.  *  C64-Grafik nach hinten
  491.  */
  492.  
  493. void EmulToBack(void)
  494. {
  495.   ScreenToBack(the_screen);
  496.   ModifyIDCMP(the_window, IDCMP_RAWKEY);
  497.   ClearMenuStrip(the_window);
  498. }
  499.  
  500.  
  501. /*
  502.  *  C64-Grafik nach vorne
  503.  */
  504.  
  505. void EmulToFront(void)
  506. {
  507.   ScreenToFront(the_screen);
  508.   ModifyIDCMP(the_window, IDCMP_RAWKEY | IDCMP_MENUPICK | IDCMP_MENUVERIFY);
  509.   SetMenuStrip(the_window, the_menus);
  510. }
  511.  
  512.  
  513. /*
  514.  *  Event-Schleife des Fensters
  515.  */
  516.  
  517. void EventLoop(void)
  518. {
  519.   int done = FALSE;
  520.   ULONG signals;
  521.  
  522.   while (!done) {
  523.     signals = Wait((1 << the_window->UserPort->mp_SigBit) | InvokeSAMSet);
  524.  
  525.     if (signals & (1 << the_window->UserPort->mp_SigBit))
  526.       done = handle_IDCMP(done);
  527.  
  528.     if (signals & InvokeSAMSet)
  529.       handle_menu(0, 1, done);
  530.   }
  531. }
  532.  
  533.  
  534. /*
  535.  *  IDCMP-Messages handhaben
  536.  */
  537.  
  538. int handle_IDCMP(int done)
  539. {
  540.   struct IntuiMessage *msg;
  541.   ULONG class;
  542.   UWORD code;
  543.   UWORD menu_number;
  544.   struct MenuItem *item;
  545.  
  546.   while (!done && (msg = GetMsg(the_window->UserPort))) {
  547.     class = msg->Class;
  548.     code = msg->Code;
  549.  
  550.     if (class == IDCMP_MENUVERIFY)
  551.       Pause6510();    // Subtask stoppen
  552.  
  553.     ReplyMsg((struct Message *)msg);
  554.  
  555.     switch (class) {
  556.       case IDCMP_MENUPICK:
  557.         Resume6510();  // Subtask fortführen
  558.  
  559.         menu_number = code;
  560.         while (!done && (menu_number != MENUNULL)) {
  561.           item = ItemAddress(the_window->MenuStrip, menu_number);
  562.           done = handle_menu(MENUNUM(menu_number), ITEMNUM(menu_number), done);
  563.           menu_number = item->NextSelect;
  564.         }
  565.         break;
  566.  
  567.       case IDCMP_RAWKEY:
  568.         switch (code) {
  569.           case 0x59:    // F10: Reset
  570.             ResetC64();
  571.             break;
  572.           case 0x5f:    // Help: JoystickSwap umschalten
  573.             JoystickSwap = JoystickSwap ? 0 : -1;
  574.             break;
  575.           case 0x5e:    // + (NK): SkipLatch erhöhen
  576.             SkipLatch++;
  577.             break;
  578.           case 0x4a:    // - (NK): SkipLatch erniedrigen
  579.             if (SkipLatch > 1)
  580.               SkipLatch--;
  581.             break;
  582.           default:
  583.             KeyPressed(code);
  584.         }
  585.         break;
  586.     }
  587.   }
  588.  
  589.   return done;
  590. }
  591.  
  592.  
  593. /*
  594.  *  Menü wurde ausgewählt
  595.  */
  596.  
  597. int handle_menu(int menu, int item, int done)
  598. {
  599.   if (menu == 0) {
  600.     switch (item) {
  601.       case 0:        // Einstellungen
  602.         Pause6510();
  603.         PauseSound();
  604.         EmulToBack();
  605.         ShowPrefs();
  606.         EmulToFront();
  607.         ResumeSound();
  608.         Resume6510();
  609.         break;
  610.  
  611.       case 1:        // SAM
  612.         Pause6510();
  613.         PauseSound();
  614.         EmulToBack();
  615.         SAM();
  616.         EmulToFront();
  617.         ResumeSound();
  618.         Resume6510();
  619.         break;
  620.  
  621.       case 3:        // Quit
  622.         done = TRUE;
  623.         break;
  624.     }
  625.   }
  626.  
  627.   return done;
  628. }
  629.  
  630.  
  631. /*
  632.  *  Requester anzeigen
  633.  */
  634.  
  635. int ShowRequester(int text, int gads, APTR args)
  636. {
  637.   struct EasyStruct es;
  638.  
  639.   es.es_StructSize = sizeof(struct EasyStruct);
  640.   es.es_Flags = 0;
  641.   es.es_Title = GetStr(MSG_REQTITLE);
  642.   es.es_TextFormat = GetStr(text);
  643.   es.es_GadgetFormat = GetStr(gads);
  644.  
  645.   return EasyRequestArgs(NULL, &es, NULL, args);
  646. }
  647.  
  648.  
  649. /*
  650.  *  Lokalisierungen vornehmen
  651.  */
  652.  
  653. void LocalizeDisplay(void)
  654. {
  655.   int i;
  656.  
  657.   for (i=0; new_menus[i].nm_Type != NM_END; i++)
  658.     if (new_menus[i].nm_Label != NM_BARLABEL)
  659.       new_menus[i].nm_Label = GetStr((int)new_menus[i].nm_Label);
  660. }
  661.