/*
Copywrite 1994 by Kevin P. Lawton 

This file is part of the IODEV (Input Output DEVices) component of BOCHS.

The IODEV component 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, or (at your option)
any later version.

The IODEV component 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 the IODEV component; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/


#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

#ifdef __linux__
#include <ncurses.h>


/* ncurses doesn't seem to define these */
extern int notimeout(WINDOW *win, bool bf);
extern char *keyname(int c);
extern void wtimeout(WINDOW *win, int delay);
#else
#include <curses.h>
#endif  /* __linux__ */



#include "iodev.h"


extern Boolean debug_on;

extern Boolean bx_use_video;


static WINDOW *mda_win;
static char char_buf[256];
static Boolean mda_initialized = 0;

void mda_fd_handler(void);


  Bit8u
bx_mda_io_read_handler(Bit32u address)
{
  bx_printf(0, "*** io read from MDA port %x\n", (int) address);
  return(0);
}

  void
bx_mda_io_write_handler(Bit32u address, Bit8u value)
{
  bx_printf(0, "*** io write to MDA port %x, %x\n", (int) address,
    (int) value);
}

  void
bx_init_mda(void)
{
  Bit8u  zero8, video_mode, internal_mode_reg;
  Bit16u zero16, equip_word, video_bytes_per_page;
  Bit16u video_columns, video_cursor_shape, video_port_no;
  Bit32u i;

  typical_int_code[2] = 0x10;
  bx_register_int_vector(0x10, typical_int_code, sizeof(typical_int_code),
      bx_mda_int10h_handler);

  zero8 = 0;
  zero16 = 0;

  /* video mode */
  video_mode = 7; /* MDA */
  bx_access_physical(0x449, 1, BX_WRITE, &video_mode);

  /* video number of columns */
  video_columns = 80;
  bx_access_physical(0x44A, 2, BX_WRITE, &video_columns);

  /* video total number of bytes per page */
  video_bytes_per_page = 4000; /* 2 * 80 * 25 */
  bx_access_physical(0x44C, 2, BX_WRITE, &video_bytes_per_page);

  /* video current page offset */
  bx_access_physical(0x44E, 2, BX_WRITE, &zero16);

  /* video cursor position pages 0 to 7 */
  for (i=0; i<8; i++)
    bx_access_physical(0x450 + i*2, 2, BX_WRITE, &zero16);

  /* video cursor shape */
  video_cursor_shape = 0x0607; /* single underline */
  bx_access_physical(0x460, 2, BX_WRITE, &video_cursor_shape);

  /* video active display page */
  bx_access_physical(0x462, 1, BX_WRITE, &zero8);

  /* video I/O port number base */
  video_port_no = 0x3B4;
  bx_access_physical(0x463, 2, BX_WRITE, &video_port_no);

  /* video internal mode register */
  internal_mode_reg = 0x2D;
  bx_access_physical(0x465, 1, BX_WRITE, &internal_mode_reg);

  /* video color palatte */
  bx_access_physical(0x466, 1, BX_WRITE, &zero8);

  /* now, set the initial video mode type field in the equipment word */
  bx_access_physical(0x410, 2, BX_READ, &equip_word);
  equip_word |= (3 << 4); /* monochrome 80cols x 25rows */
  bx_access_physical(0x410, 2, BX_WRITE, &equip_word);

  /* video initialization data pointer (uses INT 1Dh location).  it
     should point to the 16-byte video initialization array to be
     sent to ports 3b4/3b5 */
  bx_set_interrupt_vector(0x1d, BX_VIDEO_INIT_ARRAY);
  for (i=0; i<16; i++) {
    bx_access_physical(BX_VIDEO_INIT_ARRAY, 1, BX_WRITE, &zero8);
    }
  

if (bx_use_video) {
  mda_win = initscr();

  (void) nonl();
  (void) cbreak();  /* raw() when ctrl chars handled locally */
  (void) noecho();
  (void) keypad(mda_win, FALSE);

  /* don't set a timer waiting for remainder of function key codes */
  (void) notimeout(mda_win, TRUE);

  /* use non-blocking call for getch() */
  nodelay(mda_win, TRUE);
  wtimeout(mda_win, 0);


#if 0
  ptr = (char *) tigetstr("khome");

  for (i=1; i<=strlen(ptr); i++) {
    ptr2 = unctrl(ptr[i-1]);
    waddstr(mda_win, ptr2);
    waddstr(mda_win, " ");
    }
#endif

  wrefresh(mda_win);
  mda_initialized = 1;
  }

  /*bx_add_input(0, BX_FD_EVENT_READ, mda_fd_handler);*/
}

  void
mda_fd_handler(void)
{
  char ch;

  /* ch = wgetch(mda_win); */
  while ( (ch = getc(stdin)) >= 0 ) {
    sprintf(char_buf, "[%d] ", (int) ch);
    waddstr(mda_win, char_buf);
    wrefresh(mda_win);
    }
}

  void
bx_video_message(char *message)
{
  if (mda_initialized) {
    waddstr(mda_win, message);
    wrefresh(mda_win);
    }
}

  Bit8u
bx_mda_mem_read(Bit32u addr)
{
  chtype ch;
  Bit32u offset;
  int x, y;

  offset = addr - 0xB0000;
  y = offset / COLS;
  x = offset % COLS;
  if (y > LINES) {
    bx_printf(0, "DMA: read access beyond MDA video memory.\n");
    return(0);
    }
  ch = mvwinch(mda_win, y, x);
  return( (Bit8u) ch );

  /*sprintf(char_buf, "read from MDA[%d]\n", (int) addr);*/
}

  void
bx_mda_mem_write(Bit32u addr, Bit8u value)
{
/*
  sprintf(char_buf, "write to MDA[%d]\n", (int) addr);
  waddstr(mda_win, char_buf);
  wrefresh(mda_win);
*/
  chtype ch;
  Bit32u offset;
  int x, y;

  offset = addr - 0xB0000;
  y = offset / COLS;
  x = offset % COLS;
  if (y > LINES) {
    bx_printf(0, "DMA: write access beyond MDA video memory.\n");
    }

  ch = (chtype) value;
  mvwaddch(mda_win, y, x, ch);
  wrefresh(mda_win);
}

  void
bx_mda_int10h_handler(int vector)
{
  int y,x;

  if (!mda_initialized) return;

  if (AH == 0x0e) { /* teletype output */
    switch (AL) {
      case 0x0d: /* CR */
        getyx(mda_win, y, x);
        wmove(mda_win, y, 0);
        break;
      case 0x0a: /* LF */
        getyx(mda_win, y, x);
        if (y >= (LINES-1)) {
          scrollok(mda_win, 1);
          scroll(mda_win);
          scrollok(mda_win, 0);
          wmove(mda_win, y, x);
          }
        else {
          wmove(mda_win, y+1, x);
          }
        wrefresh(mda_win);
        break;
      default:
/*if (AL == '>') debug_on = !debug_on;*/
        waddch(mda_win, (char) AL);
        wrefresh(mda_win);
        break;
      }
    }
  else
    bx_printf(1, "*** mda_int10h handler called, unsupported function, AH=%d\n",
      (int) AH);
}
