diff -udBb frotz/borland.c frotzpl3/borland.c --- frotz/borland.c Sun Jun 16 21:01:54 1996 +++ frotzpl3/borland.c Fri Nov 22 08:18:02 1996 @@ -16,24 +16,28 @@ #include #include "frotz.h" + #define INFORMATION "\ \n\ -FROTZ V2.01 - an interpreter for all Infocom games. Complies with standard 0.2\n\ +FROTZ V2.01+ -an interpreter for all Infocom games. Complies with standard 0.2\n\ for all games except V6 (graphic) games. Written by Stefan Jokisch in 1995-96\n\ -\n\ -\t\-d # display mode (0 mono, 1 text, 2 CGA, 3 MCGA, 4 EGA)\n\ -\n\ +VESA mode added by Linards Ticmanis in 1996\n\ +\t\-d # display mode (0 mono, 1 text 80x25, 2 CGA, 3 MCGA, 4 EGA,\n\ +\t\ 5 SVGA text w/ MCGA pix, 6 SVGA text w/ EGA pix\n\ +\t\ 7 text 40x25)\n\ \t-f # foreground colour\n\ \t-b # background colour\n\ \t-F # reverse mode foreground colour\n\ \t-B # reverse mode background colour\n\ \t-e # emphasis colour\n\ -\n\ +\t-E # border colour (EGA colour number 0-63, not for Mono/CGA cards)\n\ \t-w # screen width\n\ \t-h # screen height\n\ \t-l # left margin\n\ \t-r # right margin\n\ \t-c # context lines\n\ +\t-s allow store outside dynamic memory (for buggy Z-code)\n\ +\t-q Don't treat '?' as space in player's input (V1-5 only)\n\ \n\ See the documentation for the more obscure command line switches." @@ -42,6 +46,9 @@ #define CGA_MODE 2 #define MCGA_MODE 3 #define EGA_MODE 4 +#define VESA_MODE 5 +#define VESA_EGA_MODE 6 +#define TEXT_40_MODE 7 #define byte0(v) ((unsigned char *)&v)[0] #define byte1(v) ((unsigned char *)&v)[1] @@ -59,6 +66,7 @@ static int user_reverse_fg = -1; static int user_reverse_bg = -1; static int user_emphasis = YELLOW; +static int user_overscan = -1; static int user_screen_width = -1; static int user_screen_height = -1; static int user_min_volume = 0; @@ -72,8 +80,8 @@ static int cursor_x = 0; static int cursor_y = 0; -static int current_style = 0; -static int current_font = TEXT_FONT; +int current_style = 0; +int current_font = TEXT_FONT; static int current_fg = DEFAULT_COLOUR; static int current_bg = DEFAULT_COLOUR; @@ -82,6 +90,13 @@ static unsigned char text_fg = 7; static unsigned char text_bg = 0; +static unsigned int vesa_granul; +static int scroll_x; +typedef void far (*vp)(void); +static vp flip; +static int has_flip; +static int has_6bit_dac = 1; + static unsigned char euro_map[] = { 0x84, 0x94, 0x81, 0x8e, 0x99, 0x9a, 0xe1, 0xaf, 0xae, 0x89, 0x8b, 0x98, 0xd3, 0xd8, 0xa0, 0x82, 0xa1, 0xa2, 0xa3, 0xec, @@ -160,6 +175,7 @@ static unsigned sound_irq = 0; static unsigned sound_dma = 0; static unsigned sound_int = 0; +static unsigned sound_ver = 0; static void *sample_data = 0; @@ -199,6 +215,8 @@ }/* htoi */ +#pragma option -r- + /* * start_of_dma * @@ -282,6 +300,8 @@ }/* end_of_dma */ +#pragma option -r + /* * reset_sound * @@ -321,6 +341,7 @@ sound_irq = atoi (strchr (blaster, 'I') + 1); sound_adr = htoi (strchr (blaster, 'A') + 1); sound_dma = atoi (strchr (blaster, 'D') + 1); + sound_ver = atoi (strchr (blaster, 'T') + 1); /* Reset the DSP */ @@ -489,7 +510,7 @@ /* Set repeats to play */ - play_repeats = 1; + play_repeats = sound_header.repeats; if (sound_header.repeats == 0) play_repeats = 255; @@ -503,10 +524,21 @@ if (volume == 255) volume = user_max_volume; + if (sound_ver < 6 ) { outportb (sound_adr + 4, 0x04); outportb (sound_adr + 5, (volume << 4) | volume); outportb (sound_adr + 4, 0x22); outportb (sound_adr + 5, 0xff); + } else { + outportb (sound_adr + 4, 0x32); + outportb (sound_adr + 5, 0x80 + (volume << 3) ); + outportb (sound_adr + 4, 0x33); + outportb (sound_adr + 5, 0x80 + (volume << 3) ); + outportb (sound_adr + 4, 0x30); + outportb (sound_adr + 5, 0xff); + outportb (sound_adr + 4, 0x31); + outportb (sound_adr + 5, 0xff); + } /* Play sample using DMA */ @@ -607,6 +638,87 @@ static int graphics_number = 0; /* + * set_vesa + * + * Take a pointer into video memory imagined continous from 0xa000 to 0xdfff + * bank switch the VESA according to it and return the "real" pointer + * + */ + +static unsigned last_vesa = 0xffff; + + +unsigned char *set_vesa(unsigned char *vidp) +{ + unsigned long temp; + unsigned page; + + temp = ((long) FP_SEG(vidp) << 4) + FP_OFF(vidp); + vidp = MK_FP ( (unsigned) ((temp >> 16) << 12), temp & 0xffff); + + page = (FP_SEG(vidp) - 0xa000) >> 12; + + if (page != last_vesa) { + last_vesa = page; + + if (has_flip) { + _DX = page << vesa_granul; + asm mov bx,0x0000 + (*flip)(); + _DX = page << vesa_granul; + asm mov bx,0x0001 + (*flip)(); + } else { + _DX = page << vesa_granul; + asm mov ax,0x4f05 + asm mov bx,0x0000 + asm int 0x10 + _DX = page << vesa_granul; + asm mov ax,0x4f05 + asm mov bx,0x0001 + asm int 0x10 + } + } + + FP_SEG(vidp) = 0xa000; + + return vidp; +} + +/* + * fake_set_vesa + * + * Take a pointer into video memory imagined continous from 0xa000 to 0xdfff + * return the "real" pointer without doing anything to bank switching + * (this saves a bit of time) + * + */ + +unsigned char *fake_set_vesa(unsigned char *vidp) +{ + long temp; + + temp = ((long) FP_SEG(vidp) << 4) + FP_OFF(vidp); + return MK_FP ( 0xa000, temp & 0xffff); +} + +/* + * vesa_page + * returns VESA Page Number for a pointer + * + */ + +unsigned vesa_page(unsigned char *vidp) +{ + unsigned long temp; + + temp = ((long) FP_SEG(vidp) << 4) + FP_OFF(vidp); + return (unsigned) (temp >> 16) - 0xa; + +} + + +/* * close_graphics_file * * Free resources allocated for the current graphics file. @@ -623,6 +735,7 @@ }/* close_graphics_file */ + /* * open_graphics_file * @@ -631,6 +744,9 @@ * */ + +static int files_for_vesa; + static int open_graphics_file (int number) { char graphics_name[MAX_FILE_NAME + 1]; @@ -659,6 +775,12 @@ graphics_name[1 + i] = 'm'; if (display == EGA_MODE) graphics_name[1 + i] = 'e'; + if (display == VESA_MODE) + if (files_for_vesa == EGA_MODE) + graphics_name[1 + i] = 'e'; + else + graphics_name[1 + i] = 'm'; + graphics_name[2 + i] = 'g'; graphics_name[3 + i] = '0' + number; @@ -810,16 +932,26 @@ * get_scrnptr * * Return a pointer to the given line in video RAM. + * For VESA, this is a pointer into an imaginary continous 0xa000 to 0xdfff + * video RAM, it's VERY important to use set_vesa to get the real pointer * */ static unsigned char *get_scrnptr (int y) { + unsigned long temp; + if (display == CGA_MODE) return MK_FP ((y & 1) ? 0xba00 : 0xb800, 40 * (y & ~1)); else if (display == MCGA_MODE) return MK_FP (0xa000, 320 * y); + + else if (display == VESA_MODE) { + temp = 0xa0000UL + 1280UL * y; + return MK_FP ( (unsigned) (temp >> 4), (unsigned) (temp & 0x000f) ); + } + else /* EGA_MODE */ return MK_FP (0xa000, 80 * y); @@ -839,6 +971,7 @@ unsigned char rgb[14][3]; unsigned char buf[512]; unsigned char *screen; + unsigned char *realscreen; unsigned char *table_val; unsigned *table_seq; long data; @@ -878,7 +1011,7 @@ return; /* When the given picture provides a colour map then activate it. - This is used only in MCGA mode; note that the same colour map + This is used only in MCGA/VESA modes; note that the same colour map is used for all pictures on the screen. The first colour to be defined is colour 2; colours 0 and 1 are not used. Each colour map may define up to 14 colours (i.e. colour 2 to 15). The @@ -896,12 +1029,14 @@ fread (rgb, 3, count, graphics_fp); - /* MCGA mode can only handle R-G-B values from 0 to 63. */ + /* MCGA mode can only handle R-G-B values from 0 to 63. + VESA Mode might be able to do 8 Bit, we checked when initing */ + if (has_6bit_dac) for (i = 0; i < 14; i++) { - rgb[i][0] >>= 2; - rgb[i][1] >>= 2; - rgb[i][2] >>= 2; + rgb[i][0] = (rgb[i][0] >> 2) + (rgb[i][0] << 6); + rgb[i][1] = (rgb[i][1] >> 2) + (rgb[i][1] << 6); + rgb[i][2] = (rgb[i][2] >> 2) + (rgb[i][2] << 6); } _ES = FP_SEG (rgb); @@ -1062,7 +1197,7 @@ if (val != transparent) - if (display != MCGA_MODE) { + if (display != MCGA_MODE && display != VESA_MODE) { asm mov ax,x asm add ax,pos @@ -1114,7 +1249,21 @@ asm mov es:[bx],al } - } else screen[x + pos] = val + 14; + } + else if (display == MCGA_MODE) screen[x + pos] = val + 14; + else { /* VESA mode */ + if (files_for_vesa == MCGA_MODE) { + realscreen = set_vesa( screen + 2 * (x + pos)); + realscreen[0] = val + 14; + realscreen[1] = val + 14; + realscreen = set_vesa( screen + 2 * (x + pos) + 640); + realscreen[0] = val + 14; + realscreen[1] = val + 14; + } else { + set_vesa( screen + x + pos )[0] = val; + set_vesa( screen + x + pos + 640 )[0] = val; + } + } /* Move to next position. */ @@ -1157,6 +1306,7 @@ int os_peek_colour (void) { + unsigned char *screen; if (display <= TEXT_MODE) { @@ -1173,12 +1323,20 @@ } else { + if (display <= EGA_MODE) { + asm mov ah,13 asm mov bh,0 - asm mov cx,cursor_x - asm mov dx,cursor_y + asm mov cx, cursor_x; + asm mov dx, cursor_y; asm int 0x10 asm mov ah,0 + } else { /* In the VESA modes, the BIOS isn't going to help us */ + + screen = get_scrnptr(cursor_y) + + (cursor_x << scroll_x); + _AL = *set_vesa(screen); + } } return _AX + 16; @@ -1206,6 +1364,8 @@ static int init_mouse (void) { + if (display == VESA_MODE) return 0; + /* A fact of DOS mouse drivers' life, sad but true - no VESA */ asm xor ax,ax asm int 0x33 @@ -1237,7 +1397,7 @@ right = 8 * right + 7; } - if (display == MCGA_MODE) { + if (display == MCGA_MODE || display == VESA_MODE) { left = 2 * left; right = 2 * right + 1; } @@ -1527,6 +1687,8 @@ 4, 4, 4, 4, 2, 4, 6, 4 }; +static unsigned char far *rom_chars; + /* * os_font_available * @@ -1547,7 +1709,8 @@ if (font == TEXT_FONT) return 1; - if (font == GRAPHICS_FONT && (display == CGA_MODE || display == EGA_MODE)) + if (font == GRAPHICS_FONT && (display == CGA_MODE || display == EGA_MODE + || display == VESA_MODE)) return 1; if (font == FIXED_WIDTH_FONT) return 1; @@ -1626,8 +1789,10 @@ if (display == MONO_MODE) text_fg = WHITE; - if (display == TEXT_MODE) + if (display == TEXT_MODE) { text_fg ^= 8; + if (text_fg == DARKGRAY) text_fg = LIGHTGRAY; + } } /* Set the screen attribute for scrolling and erasing */ @@ -1636,7 +1801,7 @@ screen_attr = (bg << 4) | fg; else if (display == CGA_MODE) screen_attr = (bg != BLACK ? 0xff : 0x00); - else /* MCGA and EGA modes */ + else /* MCGA, EGA and VESA modes */ screen_attr = bg; }/* adjust_style */ @@ -1656,7 +1821,7 @@ * CYAN_COLOUR * WHITE_COLOUR * - * MS-DOS 320 columns MCGA mode only: + * MS-DOS 320 columns MCGA mode or VESA mode only: * * GREY_COLOUR * @@ -1748,12 +1913,14 @@ void os_display_char (int c) { unsigned char *screen; + unsigned char *realscreen; unsigned char *table; unsigned val; int width; int shift; int c1, c2; - int i; + int i, j; + /* Return if we are discarding a debugging message */ @@ -1869,18 +2036,83 @@ asm mov dh,0x80 asm mov cx,width asm cld - loop: + loop_mcga: asm mov al,text_bg asm test dl,dh - asm jz draw_pixel + asm jz draw_pixel_mcga asm mov al,text_fg - draw_pixel: + draw_pixel_mcga: asm stosb asm shr dh,1 - asm loop loop + asm loop loop_mcga } } + if (display == VESA_MODE) { + + /* The Basic idea is the same as with MCGA, however we use the standard + 8x16 font whose adress was obtained during init. That means we must + have font data outsied of video RAM, so better use DISPLAY.SYS and + MODE to set a font, most other programs just kick it into video RAM + and then forget about it when a graphics mode is activated. */ + + + if (current_font == GRAPHICS_FONT) + table = graphics_font + 8 * (c - 32); + else + table = rom_chars + 16 * c; + + for (i = 0; i < 8; i++) { + + screen = get_scrnptr (cursor_y + i) + + ( (files_for_vesa == EGA_MODE) ? 1 : 2) * cursor_x - 640; + + for(shift = 0; shift < 2; shift++) { + + screen += 640; + + realscreen = set_vesa(screen); + + val = *table; + + if (shift || current_font != GRAPHICS_FONT) table++; + + if (i == 7 && shift && (current_style & EMPHASIS_STYLE)) + val = 0xff; + + if ( - FP_OFF (realscreen) < 8 ) + for (j = 7; j >= 0; j--) { + realscreen = set_vesa(screen + 7 - j); + if ( val & ( 1 << j ) ) + *realscreen = text_fg; + else + *realscreen = text_bg; + + } + + else { + + asm les di,realscreen + asm mov dl,byte ptr val + asm mov dh,0x80 + asm mov cx,0x08 + asm cld + loop_vesa: + asm mov al,text_bg + asm test dl,dh + asm jz draw_pixel_vesa + asm mov al,text_fg + draw_pixel_vesa: + asm stosb + asm shr dh,1 + asm loop loop_vesa + } + } + } + } + + + if (display == EGA_MODE) { outport (0x03ce, 0x0205); @@ -1989,6 +2221,9 @@ if (display <= TEXT_MODE) return 1; + if (display == VESA_MODE) + return ( files_for_vesa == EGA_MODE ) ? 8 : 4; + if (display == MCGA_MODE) if (current_font == FIXED_WIDTH_FONT || (current_style & FIXED_WIDTH_STYLE)) @@ -2060,6 +2295,15 @@ }/* os_set_cursor */ +void os_get_cursor (int *y, int *x) +{ + + *x = cursor_x + 1; + *y = cursor_y + 1; + +}/* os_get_cursor + + /* ========================================================================== *\ @@ -2305,12 +2549,17 @@ if (now.time == limit.time && now.millitm >= limit.millitm) key = 0; - /* Check for keypress */ + /* Check for keypress - to allow Linux DOSEMU and MS POWER.EXE + a chance, this now doesn't use raw BIOS calls any more, so + the infamous DOS multitasking approach "If the prog's reading + the keyboard all the time, it's obviously not overly busy + with important stuff" can get a chance. */ - if (_bios_keybrd (_KEYBRD_READY) == 0) + if ( kbhit() == 0) continue; - code = _bios_keybrd (_KEYBRD_READ); + code = getch(); + if ( code == 0 ) code = code + getch() * 0x100; if (byte0 (code) == 0) { @@ -2645,7 +2894,8 @@ /* * get_prev_entry * - * Fetch the previous history entry and display it on the input line. + * Fetch the previous history entry and display it on the input lines + . * */ @@ -2847,7 +3097,7 @@ /* * clear_byte * - * Helper function for clear_line. + * Helper function for clear_lines. * */ @@ -2885,18 +3135,45 @@ static void clear_line (int y, int left, int right) { + unsigned char *screen; + unsigned char *realscreen; + unsigned mask1; unsigned mask2; - int x; + int x, i; + unsigned diff, max; + unsigned char *count; screen = get_scrnptr (y); + if (display == MCGA_MODE) memset (screen + left, screen_attr, right - left + 1); + else if (display == VESA_MODE) { + + /* This one is pretty terrible as we have to deal with scan lines that + aren't wholly within one VESA memory page. Just wait, it gets worse + a bit later. */ + + for (i = 0; i <= 640; i += 640) { + count = screen + (left << scroll_x) + i; + realscreen = set_vesa(count); + diff = - FP_OFF (realscreen); + max = (right - left + 1) << scroll_x; + if (diff == 0 || diff >= max) + memset (realscreen, screen_attr, max ); else { + memset (realscreen, screen_attr, diff); + count += diff; + realscreen = set_vesa(count); + memset (realscreen, screen_attr, max - diff ); + } + } + + } else { mask1 = 0x00ff >> (left & 7); mask2 = 0xff80 >> (right & 7); @@ -3018,18 +3295,79 @@ { unsigned char *screen1; unsigned char *screen2; + unsigned char *realscreen1; + unsigned char *realscreen2; + static unsigned char buffer[640]; unsigned mask1; unsigned mask2; - int x; + int x, i; + unsigned diff1, diff2, diff3, max; + unsigned char *count1; + unsigned char *count2; screen1 = get_scrnptr (y1); screen2 = get_scrnptr (y2); + if (display == MCGA_MODE) memcpy (screen1 + left, screen2 + left, right - left + 1); + else if (display == VESA_MODE) { + + /* Urgh.. This is probably the messiest part of the VESA code. Both + scan lines we're considering here might have page breaks within + them, at different x positions. And they might not be within the + same page, so we might have to buffer stuff in main memory. + However, since this is the most time-consuming part of the whole + program, we don't want to buffer if it's not necessary, i.e. if + both scan lines are wholly within the same page. Inline functions + would be cool here, but unfortunately my Borland C++ 2.0 only + allows them in C++ code. We might also want a better 32-Bit transfer + routine, but I wanted to keep things 286 compatible. */ + + for (i = 0; i <= 640; i += 640) { + count1 = screen1 + (left << scroll_x) + i; + count2 = screen2 + (left << scroll_x) + i; + realscreen1 = fake_set_vesa(count1); + realscreen2 = set_vesa(count2); + diff1 = min ( (unsigned) (- FP_OFF (realscreen1) ), + (unsigned) (- FP_OFF (realscreen2)) ); + diff2 = max ( (unsigned) (- FP_OFF (realscreen1) ), + (unsigned) (- FP_OFF (realscreen2)) ); + max = (right - left + 1) << scroll_x; + if ( diff1 == 0 || diff1 > max ) diff1 = max; + if ( diff2 == 0 || diff2 > max ) diff2 = max; + if ( diff1 == max && diff2 == max ) { + if ( vesa_page (count1) == vesa_page (count2) ) + memcpy (realscreen1, realscreen2, max); else { + memcpy (buffer, realscreen2, max); + set_vesa (count1); + memcpy (realscreen1, buffer, max); + } + } else { + memcpy (buffer, realscreen2, diff1); + set_vesa(count1); + memcpy (realscreen1, buffer, diff1); + count1 += diff1; + count2 += diff1; + realscreen2 = set_vesa(count2); + memcpy (buffer, realscreen2, diff2 - diff1 ); + realscreen1 = set_vesa(count1); + memcpy (realscreen1, buffer, diff2 - diff1 ); + if ( diff2 < max ) { + diff3 = diff2 - diff1; + count1 += diff3; + count2 += diff3; + realscreen2 = set_vesa(count2); + memcpy (buffer, realscreen2, max - diff2 ); + realscreen1 = set_vesa(count1); + memcpy (realscreen1, buffer, max - diff2 ); + } + } + } + } else { mask1 = 0x00ff >> (left & 7); mask2 = 0xff80 >> (right & 7); @@ -3163,6 +3501,8 @@ * option_right_margin * option_piracy * option_undo_slots + * option_static_writable + * option_qmark_no_space * * The name of the story file is stored in "story_name". * @@ -3178,7 +3518,7 @@ do { - c = getopt (argc, argv, "aAb:B:c:d:e:f:F:h:l:oOpr:tu:v:V:w:"); + c = getopt (argc, argv, "aAb:B:c:d:e:E:f:F:h:l:oOpqr:stu:v:V:w:"); if (optarg != NULL) num = atoi (optarg); @@ -3197,6 +3537,8 @@ display = num; if (c == 'e') user_emphasis = num; + if (c == 'E') + user_overscan = num; if (c == 'f') user_foreground = num; if (c == 'F') @@ -3211,8 +3553,12 @@ option_object_locating = 1; if (c == 'p') option_piracy = 1; + if (c == 'q') + option_qmark_no_space = 1; if (c == 'r') option_right_margin = num; + if (c == 's') + option_static_writable = 1; if (c == 't') user_tandy_bit = 1; if (c == 'u') @@ -3292,8 +3638,10 @@ void os_init_screen (void) { static unsigned char video_mode[] = { - 7, 3, 6, 19, 14 + 7, 3, 6, 19, 14, 256, 256,1 }; + unsigned int * mode_info; /* No array to ensure far pointer */ + int i; /* Get the current video mode. This video mode will be selected when the program terminates. It's also useful to auto-detect @@ -3313,20 +3661,88 @@ if (old_video_mode == 7) display = MONO_MODE; else if (h_flags & GRAPHICS_FLAG) - display = EGA_MODE; + display = VESA_MODE; else display = TEXT_MODE; /* Activate the desired display mode. Turn off the cursor and enable bright background colours (text mode only). */ + + if ( display < VESA_MODE || display == TEXT_40_MODE ) { + new_video_mode = video_mode[display]; asm mov ah,0 asm mov al,new_video_mode asm int 0x10 - if (display <= TEXT_MODE) { + } else { + + /* VESA Modes. We choose either 100h (640x400) or, if that fails, + 101h (640x480). In the latter case the 80 lines at the bottom of + the screen will simply stay blank. */ + + + if ((mode_info = malloc(256)) == 0) /* If THAT Fails, I can't help */ + os_fatal("Out of memory"); + asm mov ax,0x4f01 + asm mov cx,0x100 + asm les di, mode_info + asm int 0x10 + if (_AX != 0x004f) { + asm mov ax,0x4f01 + asm mov cx,0x101 + asm les di, mode_info + asm int 0x10 + if (_AX != 0x004f) os_fatal("VESA BIOS mode 100h or 101h required !"); + } + asm mov bx,cx + asm mov ax,0x4f02 + asm int 0x10 + + /* Query various pieces of VESA Mode info: Granularity, pageflip far-call. + We assume: Granularity is a power of two <= 64K, page size is >= 64K. + That should work on nearly all cards. */ + + vesa_granul = 64 / mode_info[2]; + flip = ((vp *) mode_info)[3]; + has_flip = ( flip != 0); + for (i = 6; i >= 0; i--) + if (vesa_granul & (1 << i)) { + vesa_granul = i; + break; + } + + + free(mode_info); + + /* Try setting the DAC to 8 Bit per color. Does that actually work ? */ + + asm mov ax,0x4f08 + asm mov bx,0x0800 + asm int 0x10 + if (_AX == 0x004f && _BH == 0x08) has_6bit_dac = 0; + + /* Get pointer to 8x16 font patterns. */ + + asm mov ax, 0x1130 + asm mov bh, 6 + asm push bp + asm int 0x10 + asm mov word ptr rom_chars, bp + asm pop bp + asm mov word ptr rom_chars+2, es + + } + + if (user_overscan != -1) { + asm mov ax, 0x1001 + asm mov bh, byte ptr user_overscan + asm int 0x10 + } + + if (display <= TEXT_MODE || display == TEXT_40_MODE ) { asm mov ax,0x1003 asm mov bl,0 asm int 0x10 @@ -3344,7 +3760,7 @@ if (h_version == V3) h_config |= CONFIG_SPLITSCREEN; - if (h_version >= V4 && display <= TEXT_MODE) + if (h_version >= V4 && (display <= TEXT_MODE || display == TEXT_40_MODE)) h_config |= CONFIG_BOLDFACE; if (h_version >= V4) @@ -3357,8 +3773,19 @@ to use certain features. The flags must be cleared if the feature is not available. */ + if ( display >= VESA_MODE ) + if ( display == VESA_EGA_MODE ) { + files_for_vesa = EGA_MODE; + scroll_x = 0; + display = VESA_MODE; + } else { + files_for_vesa = MCGA_MODE; + scroll_x = 1; + } + if (h_flags & GRAPHICS_FLAG) - if (display <= TEXT_MODE || open_graphics_file (1) == 0) + if (display <= TEXT_MODE || display == TEXT_40_MODE + || open_graphics_file (1) == 0) h_flags &= ~GRAPHICS_FLAG; if (h_version == V3 && (h_flags & OLD_SOUND_FLAG)) @@ -3374,7 +3801,7 @@ h_flags &= ~SOUND_FLAG; if (h_flags & MOUSE_FLAG) - if (init_mouse () == 0) + if ( display == VESA_MODE || init_mouse () == 0) h_flags &= ~MOUSE_FLAG; if (h_flags & COLOUR_FLAG) @@ -3384,14 +3811,17 @@ h_flags &= ~MENU_FLAG; /* Provide default colours if these have not already been set by - the user. Uniquely, MCGA mode prints white text on a black + the user. MCGA and VESA mode print white text on a grey background, whereas all other modes use the well known light grey on blue. */ if (user_foreground == -1) - user_foreground = (display <= TEXT_MODE ? LIGHTGRAY : WHITE); + user_foreground = ( (display <= TEXT_MODE + || display == TEXT_40_MODE) + ? LIGHTGRAY : WHITE); if (user_background == -1) - user_background = (display != MCGA_MODE ? BLUE : DARKGRAY); + user_background = (display != MCGA_MODE && display !=VESA_MODE + ? BLUE : DARKGRAY); /* Monochrome boards print white on black. Note that setting these colours properly is essential for V6 games. Graphic games also @@ -3419,7 +3849,21 @@ h_screen_height = 200; h_font_height = 8; h_font_width = 5; - } else { /* MCGA and EGA modes */ + } else if (display == VESA_MODE && files_for_vesa == MCGA_MODE ) { + /* We fake to be MCGA, ech of these "Pixels" is really four pixels + in a square */ + h_screen_height = 200; + h_font_height = 8; + h_screen_width = 320; + h_font_width = 4; + } else if (display == TEXT_40_MODE) { + h_screen_width = 40; + h_screen_height = 25; + h_font_height = 1; + h_font_width = 1; + display = TEXT_MODE; + } else { /* CGA and EGA modes, SVGA with EGA pics - + in the later case we fake again, only not as much ;) */ h_screen_width = 640; h_screen_height = 200; h_font_height = 8; @@ -3428,7 +3872,10 @@ /* The user may override the default screen size by command line options. This might be useful for palmtop users, or for people - who want to run Frotz under Microsoft Windows. */ + who want to run Frotz under Microsoft Windows. Palmtop users might + also try the 40-characters-per-line mode, although it is sometimes + problematic with V5+ games, most often due to sloppy programming, + sometimes becaue ASCII art simply doesn't fit on the smaller screen */ if (user_screen_width != -1) h_screen_width = user_screen_width; @@ -3538,26 +3985,20 @@ \* ========================================================================== */ - -static int saved_style = 0; -static int saved_font = 0; - /* * os_more_prompt * * Display a MORE prompt, wait for a keypress and remove the MORE - * prompt from the screen. + * prompt from the screen. Changed to avoid unnecessary prompts if there's + * going to be a > prompt on the same line, so nothing is actually scrolled. * */ -void os_more_prompt (void) +void os_more_prompt (int left, int right) { - int saved_x; /* Prepare MORE prompt by selecting text font and plain style */ - saved_font = current_font; - saved_style = current_style; current_font = TEXT_FONT; current_style = 0; @@ -3566,7 +4007,12 @@ /* Print MORE message and wait for a key */ - saved_x = cursor_x; + os_erase_area (cursor_y + 1, + left, + cursor_y + h_font_height, + right); + + cursor_x = left - 1; os_display_string ("[MORE]"); @@ -3575,18 +4021,14 @@ /* Remove MORE prompt from the screen */ os_erase_area (cursor_y + 1, - saved_x + 1, + left, cursor_y + h_font_height, - cursor_x + 1); + right); - cursor_x = saved_x; - /* Restore text font and style */ - current_font = saved_font; - current_style = saved_style; - adjust_style (); + /* Restore text font and style */ }/* os_more_prompt */ @@ -3688,6 +4130,10 @@ }/* os_get_file_name */ +static int saved_font; +static int saved_style; + + /* * os_message_start * diff -udBb frotz/buffer.c frotzpl3/buffer.c --- frotz/buffer.c Tue Jun 11 17:47:56 1996 +++ frotzpl3/buffer.c Fri Nov 22 08:18:02 1996 @@ -8,6 +8,10 @@ #include "frotz.h" #define TEXT_BUFFER_SIZE 120 +#define SEC_BUFFER_SIZE 400 + +#define SEC_BUFFER (sec_buffer[cwin]) +#define SEC_POS (sec_pos[cwin]) extern zword script_width; extern zword redirect_size; @@ -20,7 +24,13 @@ static int locked = 0; static char buffer[TEXT_BUFFER_SIZE]; +static char sec_buffer[8][SEC_BUFFER_SIZE]; + static int buffer_pos = 0; +static int sec_pos[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +extern int current_font; +extern int current_style; /* * add_to_buffer @@ -40,9 +50,91 @@ }/* add_to_buffer */ /* + * add_to_sec + * + * Insert a caracter into the secondary buffer, used to restore the last + * line of the window after a MORE prompt. Each Window has it's own. + * TODO: Waste less memory and allocate the buffers dynamically. + * + */ + +static void add_to_sec (int c) +{ + + SEC_BUFFER[SEC_POS++] = c; + + if (SEC_POS == SEC_BUFFER_SIZE) + os_fatal ("Text buffer overflow"); + +}/* add_to_sec */ + +/* + * empty_sec_buffer + * + * Throws away the contents of the secondary buffer if there is + * no MORE prompt overlaying the buffered line. + * + */ + +void empty_sec_buffer (void) +{ + SEC_POS = 4; + SEC_BUFFER[0] = NEW_FONT; + SEC_BUFFER[1] = current_font; + SEC_BUFFER[2] = NEW_STYLE; + SEC_BUFFER[3] = current_style; +} + +/* + * print_sec_buffer + * + * Prints the backup buffer back to the screen after a MORE prompt + * + */ + + +void print_sec_buffer (void) +{ + int code; + int arg; + int i = 0; + + /* Return if buffer empty */ + + if (SEC_POS == 0) + return; + + /* Print the buffer */ + + while (i < SEC_POS) { + + code = (unsigned char) SEC_BUFFER[i++]; + + if (code == NEW_STYLE || code == NEW_FONT) { + + arg = (unsigned char) SEC_BUFFER[i++]; + + if (code == NEW_STYLE) + set_text_style (arg); + if (code == NEW_FONT) + set_font (arg); + + } else os_display_char (code); + + } + + /* Empty buffer */ + + SEC_POS = 0; + +}/* print_sec_buffer */ + + +/* * flush_buffer * - * Copy the contents of the output buffer to the output streams. + * Copy the contents of the output buffer to the output streams, and also to + * the secondary buffer. * */ @@ -104,10 +196,16 @@ code = (unsigned char) buffer[i++]; + if (enable_scrolling && enable_buffering && + stream_display) add_to_sec(code); + if (code == NEW_STYLE || code == NEW_FONT) { arg = (unsigned char) buffer[i++]; + if (enable_scrolling && enable_buffering && + stream_display) add_to_sec(arg); + if (code == NEW_STYLE) set_text_style (arg); if (code == NEW_FONT) @@ -182,7 +280,7 @@ if (code == ' ' || code == 11) flush_buffer (); - /* Add character to the buffer */ + /* Add character to the buffers */ add_to_buffer (code); diff -udBb frotz/frotz.h frotzpl3/frotz.h --- frotz/frotz.h Mon Jun 3 10:09:04 1996 +++ frotzpl3/frotz.h Fri Nov 22 08:18:16 1996 @@ -342,6 +342,8 @@ extern int option_right_margin; extern int option_piracy; extern int option_undo_slots; +extern int option_static_writable; +extern int option_qmark_no_space; extern long reserve_mem; @@ -483,6 +485,9 @@ void display_string (const char *); void display_rewind (const char *); +void print_sec_buffer (void); +void empty_sec_buffer (void); + void shift_cursor (int); zword font_available (zword); @@ -535,6 +540,7 @@ void os_display_string (const char *); void os_set_cursor (int, int); +void os_get_cursor (int *, int *); void os_erase_area (int, int, int, int); void os_scroll_area (int, int, int, int, int); @@ -543,7 +549,7 @@ void os_cursor_on (void); void os_cursor_off (void); -void os_more_prompt (void); +void os_more_prompt (int, int); int os_read (int, char *, int, int); int os_read_char (int); Only in frotz: guide.txt diff -udBb frotz/main.c frotzpl3/main.c --- frotz/main.c Fri Jun 14 12:29:32 1996 +++ frotzpl3/main.c Fri Nov 22 08:18:04 1996 @@ -30,6 +30,8 @@ #define DEFAULT_RIGHT_MARGIN 0 #define DEFAULT_PIRACY 0 #define DEFAULT_UNDO_SLOTS 20 +#define DEFAULT_STATIC_WRITABLE 0 +#define DEFAULT_QMARK_NO_SPACE 0 /* Story file header data */ @@ -112,6 +114,8 @@ int option_right_margin = DEFAULT_RIGHT_MARGIN; int option_piracy = DEFAULT_PIRACY; int option_undo_slots = DEFAULT_UNDO_SLOTS; +int option_static_writable = DEFAULT_STATIC_WRITABLE; +int option_qmark_no_space = DEFAULT_QMARK_NO_SPACE; /* Size of memory to reserve (in bytes) */ Only in frotz: porting.txt diff -udBb frotz/screen.c frotzpl3/screen.c --- frotz/screen.c Sun Jun 16 21:33:14 1996 +++ frotzpl3/screen.c Fri Nov 22 08:18:06 1996 @@ -131,6 +131,38 @@ void display_new_line (void) { + int y, x; + + /* See if we have filled the screen. The spare line is for the + MORE prompt. If a MORE has been displayed, the line counter + must be reset. Note that the line counter is supposed to be + a signed value; the game may set the line counter to -999 in + order to suppress MORE prompts for a while. + This has been modified to avoid useless MORE prompts where the + > prompt will appear, we now just print it over the last + line which is also kept in that window's sec_buffer[][] and then + print the buffer back. */ + + if (enable_scrolling) { + + wp[cwin].line_count++; + + if ((short) wp[cwin].line_count >= (short) wp[cwin].y_size / font_height) { + + if (!replaying) { + os_get_cursor (&y, &x); + os_more_prompt ((int) (wp[cwin].x_pos + wp[cwin].left), + (int) (wp[cwin].x_pos + wp[cwin].x_size - wp[cwin].right)); + os_set_cursor (y, (int) (wp[cwin].x_pos + wp[cwin].left)); + print_sec_buffer(); + os_set_cursor (y, x); + } + + wp[cwin].line_count = option_context_lines + 1; + } + } + + empty_sec_buffer(); /* When the cursor has not already reached the bottom line, then it is simply moved to the start of the next line. Otherwise @@ -150,24 +182,6 @@ update_cursor (); - /* See if we have filled the screen. The spare line is for the - MORE prompt. If a MORE has been displayed, the line counter - must be reset. Note that the line counter is supposed to be - a signed value; the game may set the line counter to -999 in - order to suppress MORE prompts for a while. */ - - if (enable_scrolling) { - - wp[cwin].line_count++; - - if ((short) wp[cwin].line_count >= (short) wp[cwin].y_size / font_height - 1) { - - if (!replaying) - os_more_prompt (); - - wp[cwin].line_count = option_context_lines; - } - } /* Handle newline interrupts. If the newline countdown of the current window is set to a non-zero value then decrement it. @@ -277,7 +291,8 @@ /* * reset_line_count * - * Reset the line counter after an input action took place. + * Reset the line counter after an input action took place. Also empty the + * sec_buffer of the current window. * */ @@ -286,6 +301,8 @@ wp[cwin].line_count = 0; + empty_sec_buffer(); + }/* reset_line_count */ /* @@ -392,6 +409,7 @@ wp[i].attribute = BUFFERING_ATTR; } + /* Prepare lower window */ wp[0].x_size = h_screen_width; @@ -1053,6 +1071,11 @@ if (window == cwin) update_cursor (); + /* Guess we don't need the stuff in the sec_buffer after all, there's + not going to be a MORE prompt */ + + empty_sec_buffer(); + }/* z_set_cursor */ /* diff -udBb frotz/table.c frotzpl3/table.c --- frotz/table.c Mon Apr 22 14:06:42 1996 +++ frotzpl3/table.c Fri Nov 22 08:18:08 1996 @@ -170,8 +170,8 @@ /* Are we writing outside of the dynamic memory? */ - if (array >= h_dynamic_size) - os_fatal ("Store out of dynamic memory"); + if (array >= h_dynamic_size && !option_static_writable ) + os_fatal("Store outside of dynamic memory !"); /* Store the byte */ diff -udBb frotz/text.c frotzpl3/text.c --- frotz/text.c Mon Jun 3 11:59:54 1996 +++ frotzpl3/text.c Fri Nov 22 08:18:08 1996 @@ -598,6 +598,11 @@ * unrecognised words cause "empty" slots in the token buffer. An * empty slot is left unchanged; this way it is possible to analyse * the same text string using several different dictionaries. + * Modified to treat question marks as spaces in V1-5,7-8 contrary to the + * Z-Machine specs but in accordance with original Infocom ZIPs. + * (at least on IBM and Commodore 64, I can't check the others.) + * This feature can be overridden on the command line. + * To see this, type "what is a grue ?" in original Zork. * */ @@ -675,7 +680,8 @@ /* This could be the start or the end of a word */ - if (isseparator == 0 && c != ' ' && c != 0) { + if (isseparator == 0 && c != ' ' && c != 0 && + ( c != '?' || h_version == V6 || option_qmark_no_space )) { if (start_addr == 0) start_addr = end_addr;