/*  checkbios.cpp
    Check for correct BIOS version and chip.ini settings

    01.10.2001: tk, ultra cool auto-upload feature added.
    27.08.2001: tk, ini string checking fixed. there was
                    an error with leading/trailing spaces.
    16.08.2001: tk, initial implementation.

    Copyright (c)2000 by Thomas Kindler, thomas.kindler@gmx.de

    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. Read the
    full License at http://www.gnu.org/copyleft for more details.
*/

// include files -----
//
#include "checkbios.h"

#include <stdio.h>
#include <string.h>
#include <dos.h>
#include <process.h>
#include <stdlib.h>


/**
 * Ultra cool auto upload feature. If MAGIC_AUTO_UPLOAD
 * is defined, the program will check if it is run on the
 * development PC, and will call a batch file to compress
 * and upload itself to the mp3 player. Yes. You're right.
 * This absolutely rocks, rules, impresses your friends,
 * and makes you more attractive to the other gender.
 */
#define  MAGIC_AUTO_UPLOAD  "cmd /c upload.cmd"


static void getFocus()
{
  REGPACK regs;
  regs.r_ax = 0x1100 | 2;
  intr( 0xa0, &regs );
}


static void releaseFocus()
{
  REGPACK regs;
  regs.r_ax = 0x1100 | 3;
  intr( 0xa0, &regs );
}


static int getBiosVersion()
{
  REGPACK regs;
  regs.r_ax = 0x1300;
  intr(SYSTEM_INT, &regs);

  return  regs.r_ax;
}


static int getBootVersion()
{
  REGPACK regs;
  regs.r_ax = 0x1200;
  intr(SYSTEM_INT, &regs);

  return  regs.r_ax;
}


static void getFeatures(int *ax, int *bx, int *dx)
{
  REGPACK regs;
  regs.r_ax = 0x1600;
  intr(SYSTEM_INT, &regs);

  *ax = regs.r_ax;
  *bx = regs.r_bx;
  *dx = regs.r_dx;
}


static int getIniString(char *section, char *item, char *target, int maxlen)
{
  REGPACK regs;

  regs.r_ax = 0x2400;

  regs.r_cx = maxlen;
  regs.r_bx = FP_SEG(section);
  regs.r_si = FP_OFF(section);

  regs.r_es = FP_SEG(item);
  regs.r_di = FP_OFF(item);

  regs.r_ds = FP_SEG(target);
  regs.r_dx = FP_OFF(target);

  intr(SYSTEM_INT, &regs);

  return regs.r_ax;
}


/**
 * Get BIOS equiment description.
 * \return equipment code, see BIOS interrupt
 *         0x11 documentation. (the @ChipSC12
 *         will always return 0x013C)
 */
static int getEquipment()
{
  REGPACK regs;
  intr(0x11, &regs);
  return regs.r_ax;
}


/**
 * Strip leading and trailing whitespaces
 */
static void trim(char *str)
{
  char *head = str;
  char *tail = &str[strlen(str)-1];
  while (*head == ' ') head++;
  while (*tail == ' ') tail--;

  ++tail = '\0';
  strcpy(str, head);
}


/**
 * Check bios and chip.ini settings.
 * Because our program needs some BIOS features
 * and relies on DMA channels, we have to check
 * some settings first.
 *
 * \todo  change function name to "BIOS_Init()", and
 *   perhaps adjust the filename, too. BIOS and system
 *   functions that aren't yet included in the clib
 *   should fit in here.
 */
bool CheckBIOS()
{
  if (getEquipment() != 0x013C) {
    printf( "This program only runs on an Beck IPC@Chip SC12.\n" );
#ifdef  MAGIC_AUTO_UPLOAD
    printf( "Trying to execute \""MAGIC_AUTO_UPLOAD"\"..\n");
    system(MAGIC_AUTO_UPLOAD);
#endif
    return false;
  }

  unsigned biosVersion = getBiosVersion();
  if (biosVersion < 0x0103) {
    printf( "You're using an outdated BIOS (%u.%02u).\n"
            "Please update your BIOS to at least v1.03b.\n",
            biosVersion >> 8, biosVersion & 255 );
    return false;
  }

  unsigned bootVersion = getBootVersion();
  if (bootVersion < 0x0208) {
    printf( "You're using an outdated Bootstrap loader (%u.%02u).\n"
            "Please update your Bootstrap to at least v2.08.\n",
            bootVersion >> 8, bootVersion & 255 );
    return false;
  }

  int ax, bx, dx;
  getFeatures(&ax, &bx, &dx);
  if ( !((ax & FEAT_AX_ETHERNET) &&
         (dx & FEAT_DX_I2C)      &&
         (dx & FEAT_DX_HWAPI)    &&
         (dx & FEAT_DX_RTOS)     &&
         (dx & FEAT_DX_EXTDISK))  )
  {
    printf( "Your BIOS lacks some features that this program needs.\n"
            "Please use one of the medium (or better) BIOS versions.\n" );
    return false;
  }


  char str1[80], str2[80];

  getIniString("SERIAL", "COM_DMA", str1, sizeof(str1));
  getIniString("SERIAL", "EXT_DMA", str2, sizeof(str2));
  trim(str1); trim(str2);

  if (stricmp(str1, "DISABLE") || stricmp(str2, "DISABLE")) {
    printf( "This program makes extensive use of the DMA channels but\n"
            "they're currently in use by the BIOS. Please add the following\n"
            "lines to your chip.ini, and reboot your chip:\n"
            "  [SERIAL]\n"
            "  COM_DMA=DISABLE\n"
            "  EXT_DMA=DISABLE\n"
          );
    return false;
  }

  // In fact, you can use the serial COM (not EXT!) port. But without
  // any hardware, or pullups connected, you will get garbage on the
  // console. Especially if the display backlight is switched on - the
  // lines are very close to each other on the PCB.
  //
  // You have to change this check, if you need to use the COM port
  // for console input.
  //
  getIniString("STDIO", "STDIN",  str1, sizeof(str1));
  getIniString("STDIO", "STDOUT", str2, sizeof(str2));
  trim(str1); trim(str2);

  if (stricmp(str1, "TELNET") || stricmp(str2, "TELNET")) {
    printf( "This PCB layout is not compatible with console in/output on\n"
            "the serial ports. Please add the following lines to your\n"
            "chip.ini, and reboot your chip:\n"
            "  [STDIO]\n"
            "  STDIN=TELNET\n"
            "  STDOUT=TELNET\n"
          );
    return false;
  }

  // get console focus
  // (and remember to give it back :)
  //
  getFocus();
  atexit(releaseFocus);  

  return true;
}

