/*   Virtual Nascom, a Nascom II emulator.

     Copyright (C) 2000,2009,2017  Tommy Thorn

     Z80 emulator portition Copyright (C) 1995,1998 Frank D. Cringle.

     NasEmu 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.

     This program 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 this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
     02111-1307, USA.


   A Nascom consists of:

    - a Z80 CPU,
    - an UART,
    - a bitmapped keyboard,
    - memory:
        0000 - 07ff  2 KB ROM monitor,
        0800 - 0bff  1 KB screen memory,
        0c00 - 0fff  1 KB workspace
        1000 - dfff       memory
        e000 - ffff  8 KB of MS Basic

  With the Z80 emulator in place the first thing to get working is the
  screen memory.  The "correct" way to simulate screen memory is to
  trap upon writes, but that would be slow.  We do it any just to get
  started.
*/
/*
 * To get this to build on Ubuntu and Raspberry Pi I needed to load SDL1.2
 * There is a later version of SDL
 * 
 * 
Download SDL1.2
Simple DirectMedia Layer - Homepage
https://www.libsdl.org/
Simple DirectMedia Layer is a cross-platform development library designed to provide
SDL officially supports Windows, Mac OS X, Linux, iOS, and Android.
‎SDL version 2.0.5 (stable) · ‎SDL Wiki · ‎SDL 1.2 · ‎License

download SDL1.2
install
./configure && make && 
sudo make install
*/
 /*
  * SDL version
  *  
  * gcc -O2 -Wall -I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT -lSDL -lSDL   -c -o font.o font.c
gcc -O2 -Wall -I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT -lSDL -lSDL   -c -o simz80.o simz80.c
gcc  -L/usr/lib/i386-linux-gnu -lSDL virtualnascom.o font.o simz80.o -o virtualnascom -lSDL -lSDL
doug@doug-laptop:~/Downloads/virtual-nascom$ 

*/

/*
 * Doug Rice, 2017
 * 
 * Possible changes:-
 * The W and R commands really need a tape recorder. 
 * A tape recorder has a stop, play, record, rewind button, and the
 * tape runs at a constant speed.
 * 
 * Maybe it would be okay if the "tape recorder" read from and appended
 * to files only when a character was read from or written to the UART.
 *  *
 * In the end this has two files.
 * 
 * I have added a line to the display to show the tape LED and tape counter.
 * 
 * There are two files:
 * serialout.bin - W appends 
 * serialin.bin  - V and R reads from this.
 * 
 * press F1 to swap files, rewind and reopen.
 *  
 * This allows your to use W, then press F4 and then use V and R
 * 
 * When using BLS pascal with the file names this works well.
 * 
 * use cat serial*.bin > serialtmp.bin
 *
 * rename serialtmp.bin to serialin.bin
 * rename serialout.bin to serialin.bin
 *
 * Added: code to output 0xXX to serialout.txt as well.
 * 
 * fixed defect where pressing F9 held z80 in reset. 
 */ 

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <ctype.h>
#include "simz80.h"
#include "nascom.h"
#include "ihex.h"
#include <SDL.h>


#define SLOW_DELAY  25000
#define FAST_DELAY 900000

static bool go_fast = false;
static int t_sim_delay = SLOW_DELAY;


#define FONT_H_PITCH 16
#define FONT_H       15
#define FONT_W        8

#define FONT_V        17


#ifdef POCKET_CHIP
#define DISPLAY_X_OFFSET 48
#define DISPLAY_Y_OFFSET 16
#define DISPLAY_WIDTH   480
#define DISPLAY_HEIGHT  272
#else
#define DISPLAY_WIDTH   480
//#define DISPLAY_HEIGHT  240
#define DISPLAY_HEIGHT  ( 272 + 4*16 )	
#define DISPLAY_X_OFFSET 0
#define DISPLAY_Y_OFFSET 0
#endif

extern uint8_t nascom_font_raw[];
extern uint8_t tape_font_raw[];
static SDL_Surface *screen;

#include <stdint.h>

/* 7 segment font
  *  _   _   _   _   _   _   _   _      a 
  * |_| |_| |_| |_| |_| |_| |_| |_|    fgb
  * |_|,|_|,|_|,|_|,|_|,|_|,|_|,|_|,   edch
  * 
  * segment names
  *  a
  * fgb
  * edch
  * 
  * bits:  h,g,f,e,d,c,b,a
*/

uint8_t seg7_font_raw[16*4] = {	
	/* ' ' */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

	/* '_' */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x7e, 0xff, 0x7e, 0x00, 0x00, 0x00,

	/* '|' */	
    0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
    0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00,
    
	/* '.' */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x60, 0xf0, 0xf0, 0x60, 0x00, 0x00
};

/*
 * defines for 7 segment display segment map. 
 * 
 */
 
#define SA ( 1<<0 )
#define SB ( 1<<1 )
#define SC ( 1<<2 )
#define SD ( 1<<3 )
#define SE ( 1<<4 )
#define SF ( 1<<5 )
#define SG ( 1<<6 )
#define SH ( 1<<7 )




static struct font {
    SDL_Surface *surf;
    int w, h, h_pitch;
} nascom_font,tape_font,seg7_font;

static FILE *serial_out, *serial_txt_out, *serial_in ;
static int tape_led = 0;
static int serial_input_available = 0;

static int  seg7dispA[10];
static char seg7segA[  8];
static char seg7hexA[  32]; /* lookup */


/*
 * defines for 7 segment display segment map. 
 * 
 */
 
#define SA ( 1<<0 )
#define SB ( 1<<1 )
#define SC ( 1<<2 )
#define SD ( 1<<3 )
#define SE ( 1<<4 )
#define SF ( 1<<5 )
#define SG ( 1<<6 )
#define SH ( 1<<7 )




static struct tape_S {
	/* tape recorder  */
	char * ser_dirP; /* */
	bool ser_dir; /* */
	char ser_label[100];
} tape;


static void RenderItem(struct font *font, int idx, int x, int y)
{
    auto SDL_Rect dest = { DISPLAY_X_OFFSET + x, DISPLAY_Y_OFFSET + y,
                           font->w, font->h };
    SDL_Rect clip = { 0, idx * font->h_pitch, font->w, font->h };
    SDL_BlitSurface(font->surf, &clip, screen, &dest);
}

/* The keyboard holds the state state of every depressed key and a
   current scanning pointer. */

static struct {
    unsigned char mask[9];
    unsigned char index;
} keyboard = {
    {
        0,  /*     ? Sh ?  ?  ?  ?  */
        0,  /* Up T  X  F  5  B  H  */
        0,  /* Lt Y  Z  D  6  N  J  */
        0,  /* Dn U  S  E  7  M  K  */
        0,  /* Rt I  A  W  8  ,  L  */
        0,  /* Gr O  Q  3  9  .  ;  */
        0,  /* [  P  1  2  0  /  :  */
        0,  /* ]  R  Sp C  4  V  G  */
        0   /* Ch @  Sh Ct -  Nl Bs */
    },
    0};

static char * kbd_translation[] = {
/* 0 */  "________",
/* 1 */  "__TXF5BH",
/* 2 */  "__YZD6NJ",
/* 3 */  "__USE7MK",
/* 4 */  "__IAW8,L",
/* 5 */  "__OQ39.;",
/* 6 */  "_[P120/:",
/* 7 */  "_]R C4VG",
/* 8 */  "_\t@__-\r\010"
};

#define ___ " " // Dummy
static const char
kbd_us_shift[] = ";:'\"[{]}\\|-_=+`~1!2@3#4$5%6^7&8*9(0),<.>/?";
static const char
kbd_spec          [] = ";" ":" "["    "]" "-" "," "." "/"    "0" "1" "2"  "3" "4" "5" "6" "7" "8" "9" " ";
static const char
kbd_spec_w_shift  [] = "+" "*" "\\"   "_" "=" "<" ">" "?"    "^" "!" "\"" "#" "$" "%" "&" "'" "(" ")" ___;
static const char
kbd_spec_w_ctrl   [] = "{" ___ "\033" ___ ___ ___ ___ ___    ___ ___ ___  ___ ___ ___ ___ ___ ___ ___ "`";
static const char
kbd_spec_w_shctrl [] = ___ ___ ___    ___ "}" "|" "~" "\177" ___ ___ ___  ___ ___ ___ ___ ___ ___ ___ ___;

typedef enum { CONT = 0, RESET = 1, DONE = -1 } sim_action_t;
static sim_action_t action = CONT;

// Ctr-Shift-Meta 0 -> the REAL # (instead of the pound symbol)
// Ctrl-Space -> `

static void handle_key_event_dwim(SDL_keysym keysym, bool keydown);
static void handle_key_event_raw(SDL_keysym keysym, bool keydown);

/* use function pointer to corrct routine */
static void (*handle_key_event)(SDL_keysym, bool) = handle_key_event_raw;

static void handle_app_control(SDL_keysym keysym, bool keydown)
{
	/* force out of reset */
	action = CONT;
	
    if (keydown)
        switch (keysym.sym) {
        case SDLK_END: {
            FILE *f;
            f = fopen("screendump", "a+");
            fwrite((const void *) (ram+0x800), 1, 1024, f);
            fclose(f);
            if (verbose) printf("Screen dumped\n");
            break;
        }

		/* add buttons for "tape recorder"  */
        case SDLK_F1: /* simple Tape deck - press F1 to rewind input */
			if ( serial_out ){
				printf(" closing serialout.bin\n");
				fclose( serial_out );
			}	
			if ( serial_in ){
				printf(" closing serialin.bin\n");
				fclose( serial_in );
			}
			
			/* toogle direction flag */	
			if ( tape.ser_dir ){			
				printf(" opening serialin.bin->NASCOM->serialout.bin\n");
				serial_in  = fopen("serialin.bin",  "r");		
				serial_out = fopen("serialout.bin", "a+");							
				tape.ser_dirP = "in.bin->[nascom]->out.bin                ";
			    SDL_WM_SetCaption("Nascom 2 tape: in->[]->out", "Nascom 2 in->out");
			} else {
				printf(" opening serialout.bin->NASCOM->serialin.bin\n");
				serial_in  = fopen("serialout.bin", "r");		
				serial_out = fopen("serialin.bin",  "a+");							
				tape.ser_dirP = "out.bin->[nascom]->in.bin              " ;					
			    SDL_WM_SetCaption("Nascom 2 tape: out->[]->in", "Nascom 2 out->in");
			}
			/* toggle direction flag */
			tape.ser_dir = ( 1 - tape.ser_dir );
						 
			if( serial_in ){ 					
				serial_input_available = !feof(serial_in);
			}	
				
			//SDL_WM_SetCaption( tape.ser_label, tape.ser_label );
			//SDL_WM_SetCaption("Nascom 2", "Nascom 2");

			break;

        case SDLK_F4:
            action = DONE;
            break;

        case SDLK_F5:
            go_fast = !go_fast;
            printf("Switch to %s\n", go_fast ? "fast" : "slow");

            t_sim_delay = go_fast ? FAST_DELAY : SLOW_DELAY;
            break;

        case SDLK_F9:
            action = RESET;
            break;

        case SDLK_F10:
            if (handle_key_event == handle_key_event_raw)
                handle_key_event = handle_key_event_dwim;
            else
                handle_key_event = handle_key_event_raw;

            printf("Switch to %s keyboard\n",
                   handle_key_event == handle_key_event_raw ? "raw" : "dwim");
            break;

        default:
            ;
        }
}

static void handle_key_event_dwim(SDL_keysym keysym, bool keydown)
{
    int i = -1, bit = 0;
    static bool ui_shift = false;
    static bool ui_ctrl  = false;
    static bool ui_graph = false;
    bool emu_shift = false;
    bool emu_ctrl  = false;
    bool emu_graph = false;
    int ch = toupper(keysym.sym);

    /* We are getting raw key code events, so first we need to handle
     * the UI a bit */

    switch (keysym.sym) {
    case SDLK_LSHIFT:
    case SDLK_RSHIFT:
        ui_shift = keydown;
        return;

    case SDLK_LCTRL:
    case SDLK_RCTRL:
        ui_ctrl = keydown;
        return;

    case SDLK_RMETA:
    case SDLK_LMETA:
    case SDLK_RALT:
    case SDLK_LALT:
        ui_graph = keydown;
        return;

    default:
        break;
    }

    emu_shift = !ui_shift && isalpha(keysym.sym);
    emu_ctrl  = ui_ctrl;
    emu_graph = ui_graph;

    if (ui_shift)
        for (int i = 0; kbd_us_shift[i]; i += 2) {
            if (kbd_us_shift[i] == ch) {
                ch = kbd_us_shift[i+1];
                break;
            }
        }

    /* Now translate the ASCII to Nascom keyboard events */

    // Quick hack to enable LF (which is shift-CH)
    if (ch == '\t' && ui_shift) {
        emu_shift = true;
    }

#if 0
    // HACKS THAT TECHNICALLY BREAK FAITHFUL EMULATION
    // The gylphs for '#' is £ but there exist a gylph that looks right
    if (ch == '#') {
        ch = '0';
        emu_shift = true;
        emu_ctrl  = true;
        emu_graph = true;
        goto search;
    }

    // The glyph for '^' is ↑ but there exist a gylph that looks right
    if (ch == '^') {
        emu_shift = false;
        emu_ctrl  = false;
        emu_graph = true;
        ch = '3';
        goto search;
    }

    // Other things: | and ~ doesn't look quite like their modern versions,
    // but there doesn't appear to be a good alternative
#endif

    // ' ' has to be special cased because it's the NOT_ITEM above
    if (ch == ' ')
        goto search;


    // '@' is also special
    if (ch == '@') {
        emu_shift = true;
        goto search;
    }

    for (int i = 0; kbd_spec_w_shift[i]; ++i)
        if (kbd_spec_w_shift[i] == ch) {
            emu_shift = true;
            ch = kbd_spec[i];
            goto search;
        }

    for (int i = 0; kbd_spec_w_ctrl[i]; ++i)
        if (kbd_spec_w_ctrl[i] == ch) {
            emu_shift = false;
            emu_ctrl = true;
            ch = kbd_spec[i];
            goto search;
        }

    for (int i = 0; kbd_spec_w_shctrl[i]; ++i)
        if (kbd_spec_w_shctrl[i] == ch) {
            emu_shift = true;
            emu_ctrl = true;
            ch = kbd_spec[i];
            goto search;
        }

    search:
    if (keysym.sym < 128) {
        for (i = 1; i < 9; ++i)
            for (bit = 0; bit < 7; ++bit)
                if (kbd_translation[i][7-bit] == ch) {
                    goto translate;
                }

        i = -1;
    } else {
        emu_shift = ui_shift;

        switch (keysym.sym) {
        case SDLK_UP:      i = 1, bit = 6; break;
        case SDLK_LEFT:    i = 2, bit = 6; break;
        case SDLK_DOWN:    i = 3, bit = 6; break;
        case SDLK_RIGHT:   i = 4, bit = 6; break;
        default:
            handle_app_control(keysym, keydown);
        }
    }

translate:
    if (emu_shift)
        keyboard.mask[0] |= 1 << 4;
    else
        keyboard.mask[0] &= ~(1 << 4);

    if (emu_ctrl)
        keyboard.mask[0] |= 1 << 3;
    else
        keyboard.mask[0] &= ~(1 << 3);

    if (emu_graph)
        keyboard.mask[5] |= 1 << 6;
    else
        keyboard.mask[5] &= ~(1 << 6);

    if (i != -1) {
        if (keydown)
            keyboard.mask[i] |= 1 << bit;
        else
            keyboard.mask[i] &= ~(1 << bit);
    }
}

static void handle_key_event_raw(SDL_keysym keysym, bool keydown)
{
    int i = -1, bit = 0;

    // We need this to be able to share kbd_translation between the
    // RAW and the DWIM variant
    if (keysym.sym == '\'')
        keysym.sym = ':';

    if (keysym.sym < 128) {
        int ch = toupper(keysym.sym);
        for (i = 1; i < 9; ++i)
            for (bit = 0; bit < 7; ++bit)
                if (kbd_translation[i][7-bit] == ch) {
                    goto translate;
                }
        i = -1;
        translate:;
    } else {
        switch (keysym.sym) {
        // case Newline  i = 0, bit = 5; break;
        // case '@':     i = 8, bit = 5; break;
        case SDLK_LCTRL:
        case SDLK_RCTRL:   i = 0, bit = 3; break;

        case SDLK_LSHIFT:
        case SDLK_RSHIFT:  i = 0, bit = 4; break;

        case SDLK_UP:      i = 1, bit = 6; break;
        case SDLK_LEFT:    i = 2, bit = 6; break;
        case SDLK_DOWN:    i = 3, bit = 6; break;
        case SDLK_RIGHT:   i = 4, bit = 6; break;

        case SDLK_LMETA:
        case SDLK_RMETA:
        case SDLK_LALT:
        case SDLK_RALT:    i = 5, bit = 6; break;

        case SDLK_KP_ENTER:i = 8, bit = 6; break;
        default:
            handle_app_control(keysym, keydown);
        }
    }

    if (i != -1) {
        if (keydown)
            keyboard.mask[i] |= 1 << bit;
        else
            keyboard.mask[i] &= ~(1 << bit);
    }
}

static void load_nascom(const char *file)
{
    FILE *f = fopen(file, "r");
    int a, b1, b2, b3, b4, b5, b6, b7, b8;
    int count = 0;
    int ch;

    if (!f) {
        perror(file);
        exit(1);
    }

    if (verbose)
        printf("Loading %s\n", file);

    ch = fgetc(f);
    rewind(f);

    if (ch == ':') {
        load_ihex(file, &RAM(0));
        return;
    }

    for (; !feof(f) ;) {
        if (fscanf(f, "%x %x %x %x %x %x %x %x %x",
                   &a, &b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8) == 9) {
            RAM(a)   = b1;
            RAM(a+1) = b2;
            RAM(a+2) = b3;
            RAM(a+3) = b4;
            RAM(a+4) = b5;
            RAM(a+5) = b6;
            RAM(a+6) = b7;
            RAM(a+7) = b8;
            count += 8;
        }

        do
            ch = fgetc(f);
        while (ch != -1 && ch != '\n');

        if (ch == -1)
            break;
    }

    fclose(f);
    if (verbose)
        printf(". Successfully loaded %d bytes\n", count);
}

static void save_nascom(int start, int end, const char *name)
{
    FILE *f = fopen(name, "w+");

    if (!f) {
        perror(name);
        return;
    }

    for (uint8_t *p = ram + start; start < end; p += 8, start += 8)
        fprintf(f, "%04X %02X %02X %02X %02X %02X %02X %02X %02X %02X%c%c\r\n",
                start, *p, p[1], p[2], p[3], p[4], p[5], p[6], p[7], 0, 8, 8);

    fclose(f);
}

static void ui_serve_input(void)
{
    SDL_Event event;

    if (SDL_PollEvent(&event)) {
        switch (event.type) {
        case SDL_MOUSEMOTION:
            /*printf("Mouse moved by %d,%d to (%d,%d)\n",
              event.motion.xrel, event.motion.yrel,
              event.motion.x, event.motion.y);*/
            break;
        case SDL_MOUSEBUTTONDOWN:
            /*printf("Mouse button %d pressed at (%d,%d)\n",
              event.button.button, event.button.x, event.button.y);*/
            break;
        case SDL_KEYDOWN:
        case SDL_KEYUP:
            handle_key_event(event.key.keysym, event.type == SDL_KEYDOWN);
            break;
        case SDL_QUIT:
            action = DONE;

            //printf("Quit\n");
            return;
        default:
            //printf("Unknown event: %d\n", event.type);
            break;
        }
    }
}

/**********************************************
 * prototypes
 * ********************************************/
 
static void ui_display_refresh_tape( void );
static void ui_display_refresh_seg7( void );


static void ui_display_refresh(void)
{
    static uint8_t screencache[1024] = { 0 };
    bool dirty = false;

    for (uint8_t *p0 = ram + 0x80A, *q0 = screencache + 0xA;
        p0 < ram + 0xC00; p0 += 64, q0 += 64){
        for (unsigned char *p = p0, *q = q0; p < p0 + 48; ++p, ++q){
            if (*q != *p) {
                *q = *p;
                unsigned index = p - ram - 0x800;
                unsigned x     = index % 64 - 10;
                unsigned y     = index / 64;
                y = (y + 1) % 16; // The last line is the first line

                RenderItem(&nascom_font, *p, x * FONT_W, y * FONT_H);
//                RenderItem(&tape_font, *p, x * FONT_W, y * FONT_H);
                dirty = true;
            }
        }
    }
    ui_display_refresh_tape();
	ui_display_refresh_seg7();

    if (dirty)
        // SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
        SDL_Flip(screen); // either seem to work

 }
	
static void ui_display_refresh_tape(void)
{
  //  static uint8_t screencache[1024] = { 0 };
  //  bool dirty = false;
    long int tape_counter = 0;
    char tape_led_char; 
    int x;
            
	/*
	* 
	* Add code for Tape recorder and its status on extra lines
	* 
	* display a tape_counter for serialin.bin 
	* Display: 
	* 	in ->[nascom]-> out 
	* or if swapped 
	* 	out-> [nascom]->in
	* 
	* */

	if(  serial_in ){
	  tape_counter = ftell( serial_in );
	};

    if ( tape_led ){
		tape_led_char = '*';
    } else {
		tape_led_char = ' ';
	} 
		
	sprintf(tape.ser_label,"tape_led:%c[%10ld] %s ",tape_led_char, tape_counter, tape.ser_dirP );
	for (x = 0; x < 48; ++x){
		RenderItem(&tape_font, ( tape.ser_label[x] ), x * FONT_W, 16 * FONT_H);
	}
	}
	
static void ui_display_refresh_seg7(void)
{
	/* ****************************************
	 * Try out 7 segment display for MK14 
	 * Display 10 7-segment displays 
	 * For now use address 0x0BCA and next 10 bytes. 
	 * 
	 ***************************************** */

/* 7 segment font
  *  _   _   _   _   _   _   _   _      a 
  * |_| |_| |_| |_| |_| |_| |_| |_|    fgb
  * |_|,|_|,|_|,|_|,|_|,|_|,|_|,|_|,   edch
  * 
  * segment names
  *  a
  * fgb
  * edch
  * 
  * bits:  h,g,f,e,d,c,b,a
*/
    int x;
	/* set up 7 segment display */
	/*
	 *    a
	 *  f g b
	 *  e d c
	 *  
	 * 0251   110B             ; ****************************************************************************
0252   110B             ;                               Segment Data
0253   110B             ; ****************************************************************************
0254   110B             SA      .equ    1                       ; Segment bit patterns
0255   110B             SB      .equ    2
0256   110B             SC      .equ    4
0257   110B             SD      .equ    8
0258   110B             SE      .equ    16
0259   110B             SF      .equ    32
0260   110B             SG      .equ    64
0261   110B             ; ****************************************************************************
0262   110B             ;                    Hex number to seven segment table
0263   110B             ; ****************************************************************************
0264   110B 3F          CRom:   .db     SA+SB+SC+SD+SE+SF
0265   110C 06                  .db     SB+SC
0266   110D 5B                  .db     SA+SB+SD+SE+SG
0267   110E 4F                  .db     SA+SB+SC+SD+SG
0268   110F 66                  .db     SB+SC+SF+SG
0269   1110 6D                  .db     SA+SC+SD+SF+SG
0270   1111 7D                  .db     SA+SC+SD+SE+SF+SG
0271   1112 07                  .db     SA+SB+SC
0272   1113 7F                  .db     SA+SB+SC+SD+SE+SF+SG
0273   1114 67                  .db     SA+SB+SC+SF+SG
0274   1115 77                  .db     SA+SB+SC+SE+SF+SG
0275   1116 7C                  .db     SC+SD+SE+SF+SG
0276   1117 39                  .db     SA+SD+SE+SF
0277   1118 5E                  .db     SB+SC+SD+SE+SG
0278   1119 79                  .db     SA+SD+SE+SF+SG
0279   111A 71                  .db     SA+SE+SF+SG

	 *    a
	 *  f g b
	 *  e d c
	 * 
	 */
	
	for (x = 0; x < 10; ++x){
     seg7dispA[ x ] = ram[0x0BCA+x];

#ifdef comment
     /* mask of bit and set character */
      seg7segA[0] = ( seg7dispA[ x ] & SA ) ? '_' : ' ' ; 
      seg7segA[1] = ( seg7dispA[ x ] & SB ) ? '|' : ' ' ; 
      seg7segA[2] = ( seg7dispA[ x ] & SC ) ? '|' : ' ' ; 
      seg7segA[3] = ( seg7dispA[ x ] & SD ) ? '_' : ' ' ; 
      seg7segA[4] = ( seg7dispA[ x ] & SE ) ? '|' : ' ' ; 
      seg7segA[5] = ( seg7dispA[ x ] & SF ) ? '|' : ' ' ; 
      seg7segA[6] = ( seg7dispA[ x ] & SG ) ? '_' : ' ' ; 
      seg7segA[7] = ( seg7dispA[ x ] & SH ) ? '.' : ' ' ; 

	/* print characters to screen */
	  RenderItem(&seg7_font, ( ' '          ), (x*4+0) * FONT_W, 17 * FONT_H+4);
	  RenderItem(&seg7_font, ( seg7segA[0]  ), (x*4+1) * FONT_W, 17 * FONT_H+4);
	  RenderItem(&seg7_font, ( ' '          ), (x*4+2) * FONT_W, 17 * FONT_H+4);
	  RenderItem(&seg7_font, ( ' '          ), (x*4+3) * FONT_W, 17 * FONT_H+4);

	  RenderItem(&seg7_font, ( seg7segA[5]  ), (x*4+0) * FONT_W, 18 * FONT_H);
	  RenderItem(&seg7_font, ( seg7segA[6]  ), (x*4+1) * FONT_W, 18 * FONT_H+2);
	  RenderItem(&seg7_font, ( seg7segA[1]  ), (x*4+2) * FONT_W, 18 * FONT_H);
	  RenderItem(&seg7_font, ( ' '          ), (x*4+3) * FONT_W, 18 * FONT_H);

	  RenderItem(&seg7_font, ( seg7segA[4]  ), (x*4+0) * FONT_W, 19 * FONT_H);
	  RenderItem(&seg7_font, ( seg7segA[3]  ), (x*4+1) * FONT_W, 19 * FONT_H+2);
	  RenderItem(&seg7_font, ( seg7segA[2]  ), (x*4+2) * FONT_W, 19 * FONT_H);
	  RenderItem(&seg7_font, ( seg7segA[7]  ), (x*4+3) * FONT_W, 19 * FONT_H);
#endif
      seg7segA[0] = ( seg7dispA[ x ] & SA ) ? 1 : 0 ; 
      seg7segA[1] = ( seg7dispA[ x ] & SB ) ? 2 : 0 ; 
      seg7segA[2] = ( seg7dispA[ x ] & SC ) ? 2 : 0 ; 
      seg7segA[3] = ( seg7dispA[ x ] & SD ) ? 1 : 0 ; 
      seg7segA[4] = ( seg7dispA[ x ] & SE ) ? 2 : 0 ; 
      seg7segA[5] = ( seg7dispA[ x ] & SF ) ? 2 : 0 ; 
      seg7segA[6] = ( seg7dispA[ x ] & SG ) ? 1 : 0 ; 
      seg7segA[7] = ( seg7dispA[ x ] & SH ) ? 3 : 0 ; 


	  /* print characters to screen */
	  RenderItem(&seg7_font, ( 0            ), (x*4+1) * FONT_W, (FONT_V+0) * FONT_H+4);
	  RenderItem(&seg7_font, ( seg7segA[0]  ), (x*4+2) * FONT_W, (FONT_V+0) * FONT_H+4);
	  RenderItem(&seg7_font, ( 0            ), (x*4+3) * FONT_W, (FONT_V+0) * FONT_H+4);
	  RenderItem(&seg7_font, ( 0            ), (x*4+4) * FONT_W, (FONT_V+0) * FONT_H+4);

	  RenderItem(&seg7_font, ( seg7segA[5]  ), (x*4+1) * FONT_W, (FONT_V+1) * FONT_H);
	  RenderItem(&seg7_font, ( seg7segA[6]  ), (x*4+2) * FONT_W, (FONT_V+1) * FONT_H+2);
	  RenderItem(&seg7_font, ( seg7segA[1]  ), (x*4+3) * FONT_W, (FONT_V+1) * FONT_H);
	  RenderItem(&seg7_font, ( 0            ), (x*4+4) * FONT_W, (FONT_V+1) * FONT_H);

	  RenderItem(&seg7_font, ( seg7segA[4]  ), (x*4+1) * FONT_W, (FONT_V+2) * FONT_H);
	  RenderItem(&seg7_font, ( seg7segA[3]  ), (x*4+2) * FONT_W, (FONT_V+2) * FONT_H+2);
	  RenderItem(&seg7_font, ( seg7segA[2]  ), (x*4+3) * FONT_W, (FONT_V+2) * FONT_H);
	  RenderItem(&seg7_font, ( seg7segA[7]  ), (x*4+4) * FONT_W, (FONT_V+2) * FONT_H);





	}
	
	
	
	

}




static int sim_delay(void)
{
    ui_display_refresh();

    if (!go_fast)
        SDL_Delay(50);

    return action;
}

static void
usage(void)
{
    fprintf(stderr,
 "This is Virtual Nascom.  Usage: %s {flags} files\n"
 "           -i <file>       take serial port input from file (if tape led is on)\n"
 "           -m <file>       use <file> as monitor (default is nassys3.nal)\n"
 "           -v              be verbose\n"
            ,progname);
    exit (1);
}

static int mysetup(int argc, char **argv);

int main(int argc, char **argv)
{
    int c;

    int count;
    serial_out     = fopen( "serialout.bin", "a+"); 
    serial_txt_out = fopen( "serialout.txt", "a+"); 
 
/*
    if (!serial_out)
        exit(3);
*/
    /* Tape recorder */
    serial_in = fopen("serialin.bin", "r");
    if ( serial_in ){
      serial_input_available = !feof(serial_in);
	};
 	tape.ser_dirP = "in.bin->[nascom]->out.bin                ";
	tape.ser_dir  = 0;
	sprintf( tape.ser_label, "                          "); /* tape lable */

    if (mysetup(argc, argv))
        return 1;

    monitor = "nassys3.nal";
    progname = argv[0];

seg7hexA[0]  =  SA+SB+SC+SD+SE+SF ;
seg7hexA[1]  =  SB+SC ;
seg7hexA[2]  =  SA+SB+SD+SE+SG ;
seg7hexA[3]  =  SA+SB+SC+SD+SG ;
seg7hexA[4]  =  SB+SC+SF+SG ;
seg7hexA[5]  =  SA+SC+SD+SF+SG ;
seg7hexA[6]  =  SA+SC+SD+SE+SF+SG ;
seg7hexA[7]  =  SA+SB+SC ;
seg7hexA[8]  =  SA+SB+SC+SD+SE+SF+SG ;
seg7hexA[9]  =  SA+SB+SC+SF+SG ;
seg7hexA[10] =  SA+SB+SC+SE+SF+SG ;
seg7hexA[11] =  SC+SD+SE+SF+SG ;
seg7hexA[12] =  SA+SD+SE+SF ;
seg7hexA[13] =  SB+SC+SD+SE+SG ;
seg7hexA[14] =  SA+SD+SE+SF+SG ;
seg7hexA[15] =  SA+SE+SF+SG ;




    seg7dispA[0] = SA;
    seg7dispA[1] = SB;
    seg7dispA[2] = SC;
    seg7dispA[3] = SD;
    seg7dispA[4] = SE;
    seg7dispA[5] = SF;
    seg7dispA[6] = SG;
    seg7dispA[7] = SH;


    seg7dispA[8] = 0xFF;
    seg7dispA[9] = 0xFF;

    seg7dispA[0] = 0xFF;
    seg7dispA[1] = 0xFF;
    seg7dispA[2] = 0xFF;
    seg7dispA[3] = 0xFF;
    seg7dispA[4] = 0xFF;
    seg7dispA[5] = 0xFF;
    seg7dispA[6] = 0xFF;
    seg7dispA[7] = 0xFF;


	for ( count = 0 ; count < 10 ; count++ ){
      seg7dispA[count] = seg7hexA[count];
    }



#ifdef MMU
    for (c=0; c<MEMSIZE/4; ++c) pagetable[c]=ram+(c<<12);
#endif

    while ((c = getopt(argc, argv, "i:m:v")) != EOF)
        switch (c) {
        case 'i':
			if ( serial_in ) {
				fclose( serial_in );
			}
            serial_in = fopen(optarg, "r");
            if (!serial_in)
                perror(optarg), exit(1);
            //printf("serial input %s -> %p\n", optarg, serial_in);
            serial_input_available = !feof(serial_in);
            break;
        case 'm':
            monitor = optarg;
            break;
        case 'v':
            verbose = 1;
            break;
        case '?':
            usage();
        }

    puts("Virtual Nascom, a Nascom 2 emulator version " VERSION "\n"
         "Copyright (C) 2000,2009,2017  Tommy Thorn.\n"
         "http://github.com/tommythorn/virtual-nascom.git\n"
         "Uses software from Yet Another Z80 Emulator version "YAZEVERSION
         ", Copyright (C) 1995,1998 Frank D. Cringle.\n"
         "Virtual Nascom comes with ABSOLUTELY NO WARRANTY; for details\n"
         "see the file \"COPYING\" in the distribution directory.\n"
         "\n"
         "The emulator dumps the memory state in `memorydump.nas`\n"
         "upon exit so one might resume execution later on.\n"
         "\n"
         "The following keys are supported:\n"
         "\n"
         "* END - leaves a screendump in `screendump`\n"
         "* F1 - Tape -swap files, rewind input and re-open.\n"
         "* F4 - exits the emulator\n"
         "* F5 - toggles between stupidly fast and \"normal\" speed\n"
         "* F9 - resets the emulated Nascom\n"
         "* F10 - toggles between \"raw\" and \"natural\" keyboard emulation\n"
         "\n"
//         "All serial output is appended to `serialout.txt` which may be fed back\n"
         "All serial output is appended to `serialout.bin` which may be fed back\n"
         "in on a subsequent launch via the `-i` option.\n");

    load_nascom(monitor);
    load_nascom("basic.nal");

    for (; optind < argc; optind++)
        load_nascom(argv[optind]);

    ram[0x10000] = ram[0]; // Make GetWord[0xFFFF) work correctly

    simz80(pc, t_sim_delay, sim_delay);

    save_nascom(0x800, 0x10000, "memorydump.nas");

    exit(0);
}

/*
 * 1.7 Input/output port addressing
 *
 *     Output Bit
 * P0  7 Not available          7 Unused
 *     6 Not used               6 Keyboard S6
 *     5 Unused                 5 Keyboard S3
 *     4 Tape drive LED         4 Keyboard S5
 *     3 Single step            3 Keyboard S4
 *     2 Unused                 2 Keyboard S0
 *     1 Reset keyb'd count     1 Keyboard S2
 *     0 Clock keyb'd count     0 Keyboard S1
 */

#define P0_OUT_TAPE_DRIVE_LED 16
#define P0_OUT_SINGLE_STEP     8
#define P0_OUT_KEYBOARD_RESET  2
#define P0_OUT_KEYBOARD_CLOCK  1

/*
 * P1  0 - 7 Data to UART       0 - 7 Data from UART
 *     (Serial port)            (Serial port)
 *
 * P2  0 - 7 Not assigned       7 Data received from UART
 *                              6 UART TBR empty
 *                              5 Not assigned
 *                              4 Not assigned
 *                              3 F error on UART
 *                              2 P error on UART
 *                              1 O error on UART
 *                              0 Not assigned
 */

#define UART_DATA_READY 128
#define UART_TBR_EMPTY   64
#define UART_F_ERROR      8
#define UART_P_ERROR      4
#define UART_O_ERROR      2

/*
 * P3  Not assigned             Not assigned
 *
 * P4  PIO port A data input and output
 *
 * P5  PIO port B data input and output
 *
 * P6  PIO port A control
 *
 * P7  PIO port B control
 */

void out(unsigned int port, unsigned char value)
{
    static unsigned char port0;

    unsigned int down_trans;

    if (0) fprintf(stdout, "[%02x] <- %02x\n", port, value);

    switch (port) {
    case 0:
        /* KBD */
        down_trans = port0 & ~value;
        port0 = value;

        if ((down_trans & P0_OUT_KEYBOARD_CLOCK) && keyboard.index < 9)
            keyboard.index++;
        if (down_trans & P0_OUT_KEYBOARD_RESET) {
            ui_serve_input();
            keyboard.index = 0;
        }
#if 0
        if (tape_led != !!(value & P0_OUT_TAPE_DRIVE_LED))
            fprintf(stderr, "Tape LED = %d\n", !!(value & P0_OUT_TAPE_DRIVE_LED));
#endif
        tape_led = !!(value & P0_OUT_TAPE_DRIVE_LED);
        break;

    case 1:
        if ( serial_out ){     fputc(value, serial_out); };
        /* append as hex */
        if ( serial_txt_out ){ fprintf( serial_txt_out," %02x",value );	};
        break;

    default:
        if (verbose)
            fprintf(stdout, "OUT [%02x] <- %02x\n", port, value);
    }
}

int in(unsigned int port)
{
    if (0) fprintf(stdout, "<- [%02x]\n", port);

    switch (port) {
    case 0:
        /* KBD */
        /* printf("[%d]", keyboard.index); */
        return ~keyboard.mask[keyboard.index];
    case 1:
        if (serial_input_available & tape_led) {
            char ch = fgetc(serial_in);
            serial_input_available = !feof(serial_in);
            return ch;
        }
        else
            return 0;
    case 2:
        /* Status port on the UART */
        return UART_TBR_EMPTY |
            (serial_input_available & tape_led ? UART_DATA_READY : 0);
    default:
        if (verbose)
            fprintf(stdout, "IN <- [%02x]\n", port);
        return 0;
    }
}

static int mysetup(int argc, char **argv)
{
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
        return 1;
    }

    atexit(SDL_Quit);

//https://wiki.libsdl.org/MigrationGuide

//    screen = SDL_SetVideoMode(DISPLAY_WIDTH, DISPLAY_HEIGHT, 8, SDL_SWSURFACE);
    /* set up Nascom screen + 1 line for Tape recorder status*/
    screen = SDL_SetVideoMode(DISPLAY_WIDTH, DISPLAY_HEIGHT, 8, SDL_SWSURFACE);

    if (screen == NULL) {
        fprintf(stderr, "Unable to set video: %s\n", SDL_GetError());
        return 1;
    }

    /* Set the window caption */
    SDL_WM_SetCaption("Nascom 2", "Nascom 2");
	sprintf(tape.ser_label,"Nascom 2-" );
	
#if 0
    /* Populate the palette */
    SDL_Color colors[256];

    colors[0].r = colors[0].g = colors[0].b = 0;
    colors[255].r = colors[255].b = 0;
    colors[255].g = 255;

    /* Set palette */
    if (!SDL_SetColors(screen, colors, 0, 256)) {
        fprintf(stderr, "Unable to create frame buffer palette: %s\n",
                SDL_GetError());
        screen = 0; //XXX should free it
        return 1;
    }
    
#endif

    /* Load font */
    nascom_font.surf =
        SDL_CreateRGBSurfaceFrom(
                nascom_font_raw,
                8 /* width */,
           256*16 /* height */,
                1 /* depth */,
                1 /* pitch */,
                0 /* Rmask */,
                1 /* Gmask */,
                0 /* Bmask */,
                0 /* Amask */);
    nascom_font.w = FONT_W;
    nascom_font.h = FONT_H;
    nascom_font.h_pitch = FONT_H_PITCH;


    if (!nascom_font.surf) {
        perror("Couldn't load the font\n");
        return 1;
    }


    tape_font.surf =
        SDL_CreateRGBSurfaceFrom(
                nascom_font_raw,
                //tape_font_raw,
                8 /* width */,
           256*16 /* height */,
                1 /* depth */,
                1 /* pitch */,
                1 /* Rmask */,
                1 /* Gmask */,
                1 /* Bmask */,
                0 /* Amask */);
    tape_font.w = FONT_W;
    tape_font.h = FONT_H;
    tape_font.h_pitch = FONT_H_PITCH;

#ifdef comment
    tape_font.surf =
        SDL_CreateRGBSurfaceFrom(
//                nascom_font_raw,
                tape_font_raw,
                8 /* width */,
            256*8 /* height */,
                1 /* depth */,
                1 /* pitch */,
                1 /* Rmask */,
                1 /* Gmask */,
                1 /* Bmask */,
                0 /* Amask */);
    tape_font.w = FONT_W;
    tape_font.h = FONT_H;
    tape_font.h_pitch = FONT_H_PITCH;

    tape_font.h = 8;
    tape_font.h_pitch = 8;
#endif 

    if (!tape_font.surf) {
        perror("Couldn't load the font\n");
        return 1;
    }

    seg7_font.surf =
        SDL_CreateRGBSurfaceFrom(
                seg7_font_raw,
                8 /* width */,
           256*16 /* height */,
                1 /* depth */,
                1 /* pitch */,
                1 /* Rmask */,
                0 /* Gmask */,
                0 /* Bmask */,
                0 /* Amask */);
    seg7_font.w = FONT_W;
    seg7_font.h = FONT_H;
    seg7_font.h_pitch = FONT_H_PITCH;

#ifdef comment
    seg7_font.surf =
        SDL_CreateRGBSurfaceFrom(
//                nascom_font_raw,
//                8 /* width */,
//           256*16 /* height */,
                tape_font_raw,
                8 /* width */,
            256*8 /* height */,

                1 /* depth */,
                1 /* pitch */,
                1 /* Rmask */,
                0 /* Gmask */,
                0 /* Bmask */,
                0 /* Amask */);
    seg7_font.w = FONT_W;
    seg7_font.h = FONT_H;
    seg7_font.h_pitch = FONT_H_PITCH;

    seg7_font.w = 8;
    seg7_font.h = 8;
    seg7_font.h_pitch = 8;
#endif

    if (!seg7_font.surf) {
        perror("Couldn't load the font\n");
        return 1;
    }


    nascom_font.surf = SDL_DisplayFormat(nascom_font.surf);
    tape_font.surf   = SDL_DisplayFormat(tape_font.surf);
    seg7_font.surf   = SDL_DisplayFormat(seg7_font.surf);

    return 0;
}
