/*-------------------------------------------------------------------------
--
-- MonitorInfo.C
--
-- A small program to display a monitor's properties 
--
-- author: Gregor S. M. Kuhlmann
--
-- revision history:
-- 1994-Apr-01    37.0  created
-- 1994-Apr-02    37.1  added analogsignal period output in microseconds
-- 1994-Apr-03    37.2  added ms_Flags interpretation
-- 1994-Apr-04    37.3  added LIST option
-- 1994-Apr-09    37.4  added RAW option
-- 1994-Apr-21    37.5  some minor adjustments to interval calculation
-- 1994-May-04    37.6  now adds ".monitor" extension if necessary
-- 1994-Jun-01    37.7  fine adjustment in HB registers
-- 1994-Aug-24    37.8  now displays MINCOL as well
--
-------------------------------------------------------------------------*/

#define __USE_SYSBASE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <exec/types.h>
#include <exec/execbase.h>
#include <graphics/monitor.h>
#include <graphics/gfxbase.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>

#define BOLD_ON         "\x1b[1m"
#define BOLD_OFF        "\x1b[22m"
#define UNDERLINE_ON    "\x1b[4m"
#define UNDERLINE_OFF   "\x1b[24m"

extern long __oslibversion = 37;

extern char __stdiowin[] = "CON:0/0/300/160/";
extern char __stdiov37[] = "/AUTO/CLOSE/WAIT";

static char version[] = "$VER: MonitorInfo 37.8 (24.8.94)";

static char *beamcon0defs[16] =
{
   "HSYNCTRUE   - horizontal sync true",
   "VSYNCTRUE   - vertical sync true",
   "CSYNCTRUE   - composite sync true",
   "CSBLANK     - composite blank out to /CSY pin (ECS), unused (AA)",
   "VARCSYNC    - variable composite sync enabled",
   "DISPLAYPAL  - uses PAL color encoding",
   "DISPLAYDUAL - uses both UHRES and standard pointers",
   "VARBEAM     - variable beam counter enabled",
   "VARHSYNC    - variable horizontal sync enabled",
   "VARVSYNC    - variable vertical sync enabled",
   "CSCBLANKEN  - redirect composite sync",
   "LOLDIS      - long display lines disabled",
   "VARVBLANK   - variable vertical blank enabled",
   "LPENDIS     - disable lightpen position latch",
   "HARDDIS     - disable hardwired display window limits",
   NULL
};

float color_clock = 280e-9;   /* color clock default value */

float signalInterval(UWORD registerValue)
{
   float result;
   
   result = color_clock * (registerValue & 0xFF);
   if (registerValue & (1<<10)) result+=color_clock / 2.0;
   if (registerValue & (1<<9)) result+=color_clock / 4.0;
   if (registerValue & (1<<8)) result+=color_clock / 8.0;
   return(result);
}

void main(void)
{
   struct MonitorSpec *mspec;
   struct SpecialMonitor *sm;
   float base_frequency, v_freq;
   float ratioh, ratiov;
   UWORD i;
   char **monitorName;
   int numMonitors = 0;
   BOOL rawmode = FALSE;
   static char template[] = "MONITORS/M,LIST/S,RAW/S";
   LONG values[3] = {0,0,0};
   char buffer[32];
   char *bufptr;
   struct RDArgs *ra;
   long rc = RETURN_OK;
   
   printf("\n" BOLD_ON "%s" BOLD_OFF "   by Gregor S. M. Kuhlmann\n\n",&version[6]);
   
   if (ra=ReadArgs(template,values,0))
   {   
      if ((values[1]) || (values[0]==NULL))
      {   
         ObtainSemaphoreShared(GfxBase->MonitorListSemaphore);
         printf(BOLD_ON UNDERLINE_ON "Currently installed monitors:" BOLD_OFF UNDERLINE_OFF "\n");
         mspec = (struct MonitorSpec *)GfxBase->MonitorList.lh_Head;
         while (mspec->ms_Node.xln_Succ)
         {
            numMonitors++;
            printf("%s\n",mspec->ms_Node.xln_Name);
            mspec=(struct MonitorSpec *)mspec->ms_Node.xln_Succ;
         }
         if (numMonitors==0)
         {
            printf("< NONE >\n");
         }
         printf("\n");
         ReleaseSemaphore(GfxBase->MonitorListSemaphore);
      }
      else 
      {  
         if (values[2])
         {
            rawmode=TRUE;
         }
         monitorName=(char**)values[0];
/*--- determine color clock period display mode ---*/
         color_clock = 1.0 / (5 * SysBase->ex_EClockFrequency);
         printf("DMA Clock Cycle = %.2f ns\n\n",color_clock * 1e9);
         while(*monitorName)
         {
            strcpy(buffer,*monitorName);
            bufptr=buffer;
            while((*bufptr) && (*bufptr!='.')) bufptr++;
            if (*bufptr==0) strcat(buffer,".monitor");
            if (mspec=OpenMonitor(buffer,0L))
            {
               printf(BOLD_ON UNDERLINE_ON "Information for '%s':" BOLD_OFF UNDERLINE_OFF "\n\n",buffer);
/*--- print scan rates ---*/
               printf(BOLD_ON "Display scan rates:" BOLD_OFF "\n");
               if (mspec->BeamCon0 & (LOLDIS|DISPLAYPAL))
                  base_frequency = 1.0 / ((mspec->total_colorclocks + 1.0) * color_clock);
               else
                  base_frequency = 1.0 / ((mspec->total_colorclocks + 1.5) * color_clock);
               ratioh=mspec->ratioh/16.0;
               ratiov=mspec->ratiov/16.0;
               v_freq = base_frequency / mspec->total_rows;
               printf("horizontal sweep rate = %.2f kHz (scanline period = %.2f µs)\n",
                     base_frequency / 1000.0, (1.0 / base_frequency) * 1e6);
               printf("vertical sweep rate = %.2f Hz\n",v_freq);
               printf("lores pixel aspect ratio (w/h) = %.3f\n\n",ratiov/ratioh);
/*--- print analogsignal properties ---*/
               if (sm=mspec->ms_Special)
               {
                  if (rawmode)
                  {
                     printf(BOLD_ON "hardware timing values:" BOLD_OFF "\n");
                     printf("TOTAL_ROWS=%d, MIN_ROW=%d, TOTAL_COLORCLOCKS=%d, MIN_COL=%d\n\n",
                        mspec->total_rows,mspec->min_row,mspec->total_colorclocks,mspec->DeniseMinDisplayColumn);
                     printf(BOLD_ON "hardware sync/blank values:" BOLD_OFF "\n");
                     printf("HBSTRT=%d, HSSTRT=%d, HSSTOP=%d, HBSTOP=%d\n",
                        sm->hblank.asi_Start,sm->hsync.asi_Start,sm->hsync.asi_Stop,sm->hblank.asi_Stop);
                     printf("VBSTRT=%d, VSSTRT=%d, VSSTOP=%d, VBSTOP=%d\n\n",
                        sm->vblank.asi_Start,sm->vsync.asi_Start,sm->vsync.asi_Stop,sm->vblank.asi_Stop);
                  }     
                  else
                  {
                     printf(BOLD_ON "special signal timing info:" BOLD_OFF "\n");
                     printf("horizontal blanking period: %.2f µs\n",
                        (signalInterval(sm->hblank.asi_Stop)-signalInterval(sm->hblank.asi_Start)) * 1e6);
                     printf("horizontal sync period: %.2f µs (delay = %.2f µs)\n",
                        (signalInterval(sm->hsync.asi_Stop)-signalInterval(sm->hsync.asi_Start)) * 1e6,
                        (signalInterval(sm->hsync.asi_Start)-signalInterval(sm->hblank.asi_Start)) * 1e6);
                     printf("vertical blanking period: %.2f µs\n", 
                        (sm->vblank.asi_Stop-sm->vblank.asi_Start) * color_clock * 1e6);
                     printf("vertical sync period: %.2f µs (delay = %.2f µs)\n\n",
                        (sm->vsync.asi_Stop-sm->vsync.asi_Start) * color_clock * 1e6,
                        (sm->vsync.asi_Start-sm->vblank.asi_Start) * color_clock * 1e6);
                  }
               }
/*--- print beamcon properties ---*/               
               printf(BOLD_ON "signal properties" BOLD_OFF " (BEAMCON0=0x%0.4x):\n",mspec->BeamCon0);
               for (i=0;i<16;i++)
               {
                  if (mspec->BeamCon0 & (1<<i))
                  {
                     if (beamcon0defs[i])
                        printf("%s\n",beamcon0defs[i]);
                     else
                        printf("(#%d)\n",i);
                  }
               }
               printf("\n");
               CloseMonitor(mspec);
            }
            else
            {
               printf("ERROR: Couldn't open monitor '%s'\n",buffer);
               break;
            }
            monitorName++;
         }
      }
   }
   else
   {
      printf("ERROR: Arguments not suitable for template:\n%s\n",template);
      rc=RETURN_ERROR;
   }
   exit(rc);
}
