39 #define ROM_Z80_PC_LOAD_TRIGGER 0x3994 
   50 static Uint32 primo_palette_white;
 
   51 static Uint32 primo_palette[0x100];
 
   52 static int border_top, border_bottom, border_left, border_right;
 
   53 static int primo_screen = 0;
 
   54 static int nmi_enabled = 0;
 
   55 static int nmi_status = 0;
 
   56 static void (*renderer)(void);
 
   58 static int primo_model_set;
 
   59 static const char *primo_model_set_str;
 
   61 static int cpu_clocks_per_scanline;
 
   62 static int cpu_clocks_per_audio_sample;
 
   64 static int cpu_clock_wanted;
 
   66 static Uint64 all_cycles_spent;
 
   68 static int emu_loop_notification;
 
   70 #define EMU_LOOP_NMI_NOTIFY     1 
   71 #define EMU_LOOP_LOAD_NOTIFY    2 
   72 #define EMU_LOOP_UPDATE_NOTIFY  4 
   73 #define EMU_LOOP_DISASM_NOTIFY  8 
   77 #define VBLANK_START_SCANLINE   256 
   78 #define PAL_LINE_FREQ           15625 
   82 #define JOY_CLOCKING_TIMEOUT_MICROSECS 272 
   92 #define AUDIO_SAMPLING_FREQ     (PAL_LINE_FREQ * 2) 
   93 #define AUDIO_PULSE_SAMPLES_MAX_PASS    (AUDIO_SAMPLING_FREQ / 32) 
   97 static Uint64 beeper_last_changed;
 
   98 static SDL_AudioDeviceID audio;
 
   99 static int audio_enabled = 1;
 
  113 static int primo_read_joy ( 
int on, 
int off )
 
  147         if ((port16 & 0xFF) < 0x40) {
 
  148                 static int random_2 = 0;
 
  152                 return (((~
kbd_matrix[(port16 >> 3) & 7]) >> (port16 & 7)) & 1) | vblank;
 
  153         } 
else if ((port16 & 0xFF) < 0x80) {
 
  154                 DEBUG(
"JOY: read state at step=%d" NL, 
joy.step);
 
  155                 return primo_read_joy(0, 1) | 
serial;
 
  162 static void manage_nmi ( 
void )
 
  166         int new_status = nmi_enabled && vblank;
 
  167         if (new_status && !nmi_status) {        
 
  169                 DEBUG(
"NMI: triggering edge" NL);
 
  171         nmi_status = new_status;
 
  179         if ((port16 & 0xFF) < 0x40) {
 
  180                 primo_screen = (
value & 8) ? 0x2000 : 0x0000;
 
  181                 nmi_enabled = 
value & 128;
 
  183                 if ((
value & 16) != beeper) {
 
  184                         static int rounding_error = 0;
 
  185                         Uint64 no_of_samples = (rounding_error + all_cycles_spent - beeper_last_changed) / cpu_clocks_per_audio_sample;
 
  186                         rounding_error       = (rounding_error + all_cycles_spent - beeper_last_changed) % cpu_clocks_per_audio_sample;
 
  187                         if (no_of_samples <= AUDIO_PULSE_SAMPLES_MAX_PASS && no_of_samples > 0 && audio && audio_enabled) {
 
  188                                 Uint8 samples[no_of_samples];
 
  190                                 int ret = SDL_QueueAudio(audio, samples, no_of_samples);        
 
  192                                         DEBUGPRINT(
"AUDIO: DATA: ERROR: %s" NL, SDL_GetError());
 
  194                                         DEBUG(
"AUDIO: DATA: queued" NL);
 
  198                         beeper_last_changed = all_cycles_spent;
 
  203                                 joy.step = (all_cycles_spent - 
joy.last_clocked >= 
joy.clocking_timeout) ? 0 : (
joy.step + 1) & 7;
 
  204                                 joy.last_clocked = all_cycles_spent;
 
  209         } 
else if ((port16 & 0xFF) < 0x80) {
 
  211         } 
else if ((port16 & 0xFF) == 0xFD) {
 
  232 #define VIRTUAL_SHIFT_POS       0x03 
  248         { SDL_SCANCODE_Y,       0x00 }, 
 
  249         { SDL_SCANCODE_UP,      0x01 }, 
 
  250         { SDL_SCANCODE_S,       0x02 }, 
 
  251         { SDL_SCANCODE_LSHIFT,  0x03 }, { SDL_SCANCODE_RSHIFT,  0x03 }, 
 
  252         { SDL_SCANCODE_E,       0x04 }, 
 
  254         { SDL_SCANCODE_W,       0x06 }, 
 
  255         { SDL_SCANCODE_LCTRL,   0x07 }, 
 
  256         { SDL_SCANCODE_D,       0x10 }, 
 
  257         { SDL_SCANCODE_3,       0x11 }, 
 
  258         { SDL_SCANCODE_X,       0x12 }, 
 
  259         { SDL_SCANCODE_2,       0x13 }, 
 
  260         { SDL_SCANCODE_Q,       0x14 }, 
 
  261         { SDL_SCANCODE_1,       0x15 }, 
 
  262         { SDL_SCANCODE_A,       0x16 }, 
 
  263         { SDL_SCANCODE_DOWN,    0x17 }, 
 
  264         { SDL_SCANCODE_C,       0x20 }, 
 
  266         { SDL_SCANCODE_F,       0x22 }, 
 
  268         { SDL_SCANCODE_R,       0x24 }, 
 
  270         { SDL_SCANCODE_T,       0x26 }, 
 
  271         { SDL_SCANCODE_7,       0x27 }, 
 
  272         { SDL_SCANCODE_H,       0x30 }, 
 
  273         { SDL_SCANCODE_SPACE,   0x31 }, 
 
  274         { SDL_SCANCODE_B,       0x32 }, 
 
  275         { SDL_SCANCODE_6,       0x33 }, 
 
  276         { SDL_SCANCODE_G,       0x34 }, 
 
  277         { SDL_SCANCODE_5,       0x35 }, 
 
  278         { SDL_SCANCODE_V,       0x36 }, 
 
  279         { SDL_SCANCODE_4,       0x37 }, 
 
  280         { SDL_SCANCODE_N,       0x40 }, 
 
  281         { SDL_SCANCODE_8,       0x41 }, 
 
  282         { SDL_SCANCODE_Z,       0x42 }, 
 
  284         { SDL_SCANCODE_U,       0x44 }, 
 
  285         { SDL_SCANCODE_0,       0x45 }, 
 
  286         { SDL_SCANCODE_J,       0x46 }, 
 
  288         { SDL_SCANCODE_L,       0x50 }, 
 
  289         { SDL_SCANCODE_MINUS,   0x51 }, 
 
  290         { SDL_SCANCODE_K,       0x52 }, 
 
  291         { SDL_SCANCODE_PERIOD,  0x53 }, 
 
  292         { SDL_SCANCODE_M,       0x54 }, 
 
  293         { SDL_SCANCODE_9,       0x55 }, 
 
  294         { SDL_SCANCODE_I,       0x56 }, 
 
  295         { SDL_SCANCODE_COMMA,   0x57 }, 
 
  297         { SDL_SCANCODE_APOSTROPHE,      0x61 }, 
 
  298         { SDL_SCANCODE_P,       0x62 }, 
 
  300         { SDL_SCANCODE_O,       0x64 }, 
 
  301         { SDL_SCANCODE_HOME,    0x65 }, 
 
  303         { SDL_SCANCODE_RETURN,  0x67 }, 
 
  305         { SDL_SCANCODE_LEFT,    0x71 }, 
 
  309         { SDL_SCANCODE_RIGHT,   0x75 }, 
 
  311         { SDL_SCANCODE_ESCAPE,  0x77 }, 
 
  326 static void set_border_geometry ( 
int xres, 
int yres )
 
  333                 FATAL(
"Internal error: too small target texture!");
 
  337 #ifdef XEMU_FILES_SCREENSHOT_SUPPORT 
  339 static inline void do_pending_screenshot ( 
void )
 
  344         if (!xemu_screenshot_png(
 
  353                 const char *p = strrchr(xemu_screenshot_full_path, 
DIRSEP_CHR);
 
  355                         OSD(-1, -1, 
"%s", p + 1);
 
  364 static void render_primo_bw_screen ( 
void )
 
  369         for (
int y = 0; 
y < border_top; 
y++) {
 
  370                 for (
int x = 0; 
x < 256 + border_left + border_right; 
x++)
 
  371                         *pix++ = primo_palette[0];
 
  374         for (
int y = 0; 
y < 192; 
y++) {
 
  375                 for (
int x = 0; 
x < border_left; 
x++)
 
  376                         *pix++ = primo_palette[0];
 
  377                 for (
int x = 0; 
x < 32; 
x++)
 
  378                         for (
int z = 0, b = *scr++; z < 8; z++, b <<= 1)
 
  379                                 *pix++ = b & 0x80 ? primo_palette_white : primo_palette[0];
 
  380                 for (
int x = 0; 
x < border_right; 
x++)
 
  381                         *pix++ = primo_palette[0];
 
  384         for (
int y = 0; 
y < border_bottom; 
y++) {
 
  385                 for (
int x = 0; 
x < 256 + border_left + border_right; 
x++)
 
  386                         *pix++ = primo_palette[0];
 
  389 #ifdef XEMU_FILES_SCREENSHOT_SUPPORT 
  390         do_pending_screenshot();
 
  397 static  void render_primo_c_screen ( 
void )
 
  423         int attrstartpos = 1;
 
  424         int attrincelstart = 2;
 
  427         scr = 
memory.vid16k + primo_screen + 0x2000 - yres * 32;
 
  428         Uint8 *col = scr - (yres / attrh) * 32;
 
  429         Uint8 *pal_bg_sel = 
memory.vid16k + primo_screen + 32;
 
  430         Uint8 *pal_fg_sel = 
memory.vid16k + primo_screen + 32 + 16;
 
  431         set_border_geometry(256, yres);
 
  432         for (
int y = 0; 
y < border_top; 
y++) {
 
  433                 for (
int x = 0; 
x < 256 + border_left + border_right; 
x++)
 
  434                         *pix++ = primo_palette[0];
 
  437         Uint32 foregrounds[64], backgrounds[64];
 
  438         for (
int y = 0; 
y < yres; 
y++) {
 
  440                         for (
int x = 0; 
x < 64; ) {     
 
  442                                 foregrounds[
x] = primo_palette[pal_fg_sel[b >> 4]];
 
  443                                 backgrounds[
x] = primo_palette[pal_bg_sel[b >> 4]];
 
  445                                 foregrounds[
x] = primo_palette[pal_fg_sel[b & 15]];
 
  446                                 backgrounds[
x] = primo_palette[pal_bg_sel[b & 15]];
 
  449                 for (
int x = 0; 
x < border_left; 
x++)
 
  450                         *pix++ = primo_palette[0];
 
  451                 int attrpos = attrstartpos;
 
  452                 int attrcountincel = attrincelstart;
 
  453                 for (
int x = 0; 
x < 32; 
x++)
 
  454                         for (
int z = 0, b = *scr++; z < 8; z++, b <<= 1) {
 
  455                                 *pix++ = b & 0x80 ? foregrounds[attrpos] : backgrounds[attrpos];
 
  457                                 if (attrcountincel == attrw) {
 
  462                 for (
int x = 0; 
x < border_right; 
x++)
 
  463                         *pix++ = primo_palette[0];
 
  465                 if (attrline == attrh)
 
  469         for (
int y = 0; 
y < border_bottom; 
y++) {
 
  470                 for (
int x = 0; 
x < 256 + border_left + border_right; 
x++)
 
  471                         *pix++ = primo_palette[0];
 
  474 #ifdef XEMU_FILES_SCREENSHOT_SUPPORT 
  475         do_pending_screenshot();
 
  500                 int baddr = 
data[i + 1] | (
data[i + 2] << 8);
 
  505                 int bsize = 
data[i + 3] | (
data[i + 4] << 8);
 
  507                         baddr += 
memory.main[0x40A4] | (
memory.main[0x40A5] << 8);
 
  509                                 memory.main[0x40F9] = (baddr + bsize + 1) &  0xFF;
 
  510                                 memory.main[0x40FA] = (baddr + bsize + 1) >> 8;
 
  512                 } 
else if (btype != 0xD5 && btype != 0xD9) {
 
  513                         ERROR_WINDOW(
"Invalid PRI file, unknown block type $%02X @ %d", btype, i);
 
  516                 if (i >= 
size - bsize)
 
  518                 DEBUGPRINT(
"  ... ADDR=%04X SIZE=%04X" NL, baddr, bsize);
 
  527                         memcpy(
memory.main + baddr, 
data + i + 5, bsize);
 
  537 static int pri_load ( 
const char *file_name, 
int wet_run )
 
  539         if (!file_name || !*file_name)
 
  541         int file_size = 
xemu_load_file(file_name, NULL, 10, 0xC000, 
"Cannot open/use PRI file");
 
  550 static void set_title_model_details ( 
void )
 
  552         static char title_str_id[64];
 
  553         sprintf(title_str_id, 
"(model %s, %.2fMHz)", primo_model_set_str, (
float)cpu_clock / 1000000.0);
 
  558 static int set_cpu_hz ( 
int hz )
 
  560         cpu_clock_wanted = hz;
 
  563         DEBUGPRINT(
"CLOCK: CPU: clock speed set to %.2f MHz (%d CPU cycles per scanline)" NL, cpu_clock / 1000000.0, cpu_clocks_per_scanline);
 
  568         set_title_model_details();
 
  573 static void emulation_loop ( 
void )
 
  575         static int cycles = 0;
 
  577         Uint64 all_cycles_old = all_cycles_spent;
 
  582                 all_cycles_spent += op_cycles;
 
  607                                         all_cycles_spent += op_cycles;
 
  616                                         int ret = pri_load(
configdb.pri_name, 1);
 
  621                                                 DEBUGPRINT(
"PRI: loaded, no Z80 PC change requested" NL);
 
  643                         if (scanline == 311) {
 
  644                                 cycles += cpu_clocks_per_scanline / 2;  
 
  646                         } 
else if (scanline == 312) {   
 
  647                                 cycles += cpu_clocks_per_scanline;
 
  656                                 cycles += cpu_clocks_per_scanline;
 
  664                                 FATAL(
"Emulation problem, underflow of cpu cycles / scanline tracking counter!");
 
  677         static int choice = 0;
 
  679                 choice = 
QUESTION_WINDOW(
"Load as PRI now|Load as PRI always|Cancel for now", 
"What should I do with the dropped file?");
 
  691         OSD(-1, -1, 
"File has been dropped");
 
  696 static const char primo_model_name_0[] = 
"A32";
 
  697 static const char primo_model_name_1[] = 
"A48";
 
  698 static const char primo_model_name_2[] = 
"A64";
 
  699 static const char primo_model_name_3[] = 
"B32";
 
  700 static const char primo_model_name_4[] = 
"B48";
 
  701 static const char primo_model_name_5[] = 
"B64";
 
  702 static const char primo_model_name_6[] = 
"C";
 
  703 static const char *primo_model_names[] = { primo_model_name_0, primo_model_name_1, primo_model_name_2, primo_model_name_3, primo_model_name_4, primo_model_name_5, primo_model_name_6, NULL };
 
  706 static int set_model ( 
const char *model_id, 
int do_load_rom )
 
  709         while (primo_model_names[
id] && strcasecmp(model_id, primo_model_names[
id]))
 
  711         if (!primo_model_names[
id]) {
 
  712                 ERROR_WINDOW(
"Unknown Primo model requested: %s", model_id);
 
  716         sprintf(model_desc, 
"Primo-%c%s", toupper(primo_model_names[
id][0]), primo_model_names[
id] + 1);
 
  717         DEBUGPRINT(
"PRIMO: trying to initialize to model: %s" NL, model_desc);
 
  720                         DEBUGPRINT(
"ROM: trying to load forced (by config) ROM, regardless of the selected model" NL);
 
  721                         if (
xemu_load_file(
configdb.rom_fn, 
memory.main, 0x4000, 0x4000, 
"This is the selected primo ROM. Without it, Xemu won't work.\nInstall it, or use -rom CLI switch to specify another path.") < 0)
 
  726                         sprintf(rom_file, 
"#primo-%c%s.rom", tolower(primo_model_names[
id][0]), primo_model_names[
id] + 1);
 
  727                         sprintf(rom_err, 
"Cannot load default %s ROM file.\nYou can try to force one with the -rom CLI option.", model_desc);
 
  728                         DEBUGPRINT(
"ROM: trying to load model dependent default ROM file" NL);
 
  738         renderer = render_primo_bw_screen;      
 
  739         set_border_geometry(256, 192);  
 
  769                         renderer = render_primo_c_screen;
 
  772                         FATAL(
"Unknown primo model ID #%d", 
id);
 
  774         primo_model_set = 
id;
 
  775         primo_model_set_str = primo_model_names[
id];
 
  776         set_title_model_details();
 
  780 static void primo_reset ( 
void )
 
  788 static void ui_cb_set_model ( 
const struct menu_st *m, 
int *query )
 
  796 static void ui_load_pri ( 
void )
 
  798         static char fnbuf[PATH_MAX] = 
"";
 
  799         static char dir[PATH_MAX] = 
"";
 
  802                 "Select PRI file to load",
 
  807                 if (pri_load(fnbuf, 0) > 0) {
 
  816 static void primo_reset_asked ( 
void )
 
  823 static void ui_cb_set_cpu_clock ( 
const struct menu_st *m, 
int *query )
 
  830 static void ui_sound ( 
const struct menu_st *m, 
int *query )
 
  837         audio_enabled = !audio_enabled;
 
  842 static const struct menu_st menu_cpu_clock[] = {
 
  851 static const struct menu_st menu_models[] = {
 
  868 static const struct menu_st menu_display[] = {
 
  877 static const struct menu_st menu_main[] = {
 
  884 #ifdef XEMU_FILES_SCREENSHOT_SUPPORT 
  894 #ifdef HAVE_XEMU_EXEC_API 
  905         if (!pressed && pos == -2 && key == 0 && handled == SDL_BUTTON_RIGHT) {
 
  918 int main ( 
int argc, 
char **argv )
 
  922                 { 
"rom", NULL, 
"Select ROM to use", &
configdb.rom_fn },
 
  923                 { 
"exprom", NULL, 
"ROM expansion file selector (max 32K size)", &
configdb.exprom_fn },
 
  924                 { 
"pri", NULL, 
"Loads a PRI file", &
configdb.pri_name },
 
  925                 { 
"model", 
"b64", 
"Set Primo model: a32, a48, a64, b32, b48, b64, c", &
configdb.model_name },
 
  926                 { 
"gui", NULL, 
"Select GUI type for usage. Specify some insane str to get a list", &
configdb.
gui_selection }
 
  930                 { 
"syscon", 
"Keep system console open (Windows-specific effect only)", &
configdb.
syscon },
 
  931                 { 
"disasm", 
"Disassemble every CPU step (uber-spammy, uber-slow!)", &
configdb.disasm },
 
  954         for (
int a = 0; a < 0x100; a++) 
 
  955                 primo_palette[a] = SDL_MapRGBA(
sdl_pix_fmt, (a >> 5) * 0xFF / 7, ((a >> 2) & 7) * 0xFF / 7, ((a << 1) & 7) * 0xFF / 7, 0xFF);
 
  956         primo_palette_white = SDL_MapRGBA(
sdl_pix_fmt, 0xFF, 0xFF, 0xFF, 0xFF); 
 
  964         SDL_AudioSpec audio_want, audio_have;
 
  965         SDL_memset(&audio_want, 0, 
sizeof audio_want);
 
  967         audio_want.format = AUDIO_U8;
 
  968         audio_want.channels = 1;
 
  969         audio_want.samples = 4096;
 
  970         audio_want.callback = NULL;
 
  971         audio = SDL_OpenAudioDevice(NULL, 0, &audio_want, &audio_have, 0);
 
  972         DEBUGPRINT(
"AUDIO: dev=#%d driver=\"%s\" sampling at %d Hz" NL, audio, SDL_GetCurrentAudioDriver(), audio_have.freq);
 
  991         if (set_model(
configdb.model_name, 1))
 
  992                 FATAL(
"Bad Primo model specified and/or ROM not found (more details: the previous message)");
 
 1001         beeper_last_changed = 0;
 
 1004         joy.last_clocked = 0;
 
 1006         set_cpu_hz((
int)(
configdb.clock_mhz * 1000000.0));
 
 1012         emu_loop_notification = 0;
 
 1013         all_cycles_spent = 0;
 
 1018         SDL_PauseAudioDevice(audio, 0);