From 206e9668f1ae75f1998e0615f753d551fb121ebd Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 26 Jan 2020 02:58:18 -0800 Subject: [PATCH 01/20] achievement tracking oversight obj->record_achieve_special (overlay of obj->corpsenm) is no longer used. --- include/obj.h | 3 +-- src/sp_lev.c | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/obj.h b/include/obj.h index 6b0d9c5e9..1cf298dba 100644 --- a/include/obj.h +++ b/include/obj.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 obj.h $NHDT-Date: 1508827590 2017/10/24 06:46:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.60 $ */ +/* NetHack 3.6 obj.h $NHDT-Date: 1580036271 2020/01/26 10:57:51 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.66 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -114,7 +114,6 @@ struct obj { #define leashmon corpsenm /* gets m_id of attached pet */ #define fromsink corpsenm /* a potion from a sink */ #define novelidx corpsenm /* 3.6 tribute - the index of the novel title */ -#define record_achieve_special corpsenm int usecount; /* overloaded for various things that tally */ #define spestudied usecount /* # of times a spellbook has been studied */ unsigned oeaten; /* nutrition left in food, if partly eaten */ diff --git a/src/sp_lev.c b/src/sp_lev.c index 3a76dbb72..ee40ce331 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.c $NHDT-Date: 1574646949 2019/11/25 01:55:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.141 $ */ +/* NetHack 3.6 sp_lev.c $NHDT-Date: 1580036285 2020/01/26 10:58:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.148 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -1571,10 +1571,13 @@ struct mkroom *croom; /* * If this is a specific item of the right type and it is being - * created on the right level, flag it as the designated item + * created on the right level, record its obj->o_id to be able + * to recognize it as the designated item * used to detect a special achievement (to whit, reaching and * exploring the target level, although the exploration part * might be short-circuited if a monster brings object to hero). + * Achievement is accomplished and the recorded o_id is cleared + * if/when it gets added into hero's inventory. * * Random items of the appropriate type won't trigger a false * match--they'll fail the (id != -1) test above--but the level From ae843bc10a5c8310534aa0dc6958273c0b30160d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 26 Jan 2020 14:05:11 +0200 Subject: [PATCH 02/20] Allow wizmode wishing for cloud terrain --- doc/fixes37.0 | 4 ++-- src/objnam.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 0ab5454a3..fe3a7688e 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -112,8 +112,8 @@ when hallucinating, see hallucinated liquids when looking at water or lava on the map applying a spellbook hints about read charges left wizard mode wishing for level topology can now create hidden doors (ask for - "secret door" when at a door or wall location) and hidden corridor - spots ("secret corridor" at a corridor location) + "secret door" when at a door or wall location), hidden corridor + spots ("secret corridor" at a corridor location), and clouds tiny chance for randomly created spellbooks to be Discworld novels instead of having only one in the first book or scroll shop created (won't occur in hero's initial inventory or NPC priest inventory or be diff --git a/src/objnam.c b/src/objnam.c index b2897c906..7da3e4982 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -3046,6 +3046,10 @@ int locked, trapped; lev->typ = IRONBARS; pline("Iron bars."); madeterrain = TRUE; + } else if (!BSTRCMPI(bp, p - 5, "cloud")) { + lev->typ = CLOUD; + pline("A cloud."); + madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 11, "secret door")) { if (lev->typ == DOOR || (IS_WALL(lev->typ) && lev->typ != DBWALL)) { From aa7f098af1b6f72d3c775b0fefc0d7550530ea19 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 26 Jan 2020 14:33:11 +0200 Subject: [PATCH 03/20] Cloud wish should block vision --- src/objnam.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/objnam.c b/src/objnam.c index 7da3e4982..1777dab21 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -3048,6 +3048,7 @@ int locked, trapped; madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 5, "cloud")) { lev->typ = CLOUD; + block_point(x, y); pline("A cloud."); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 11, "secret door")) { From 9b2c9e79d89480965e51acee956a7d40c87130fa Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 26 Jan 2020 04:57:13 -0800 Subject: [PATCH 04/20] limit rate of long worm growth Make long worms grow more slowly (although that didn't seem to make as much a difference as expected) and limit the amount of HP they acculate if they shrink and then re-expand. Shrinking doesn't take away max HP but growing used to always add to max. Now it won't add to max HP unless the number of segments is at that worm's peak, so shrinking will inhibit the exhorbitant HP expansion that idle worms have had. --- doc/fixes37.0 | 3 +- src/worm.c | 84 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/doc/fixes37.0 b/doc/fixes37.0 index fe3a7688e..ade1e9fd9 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.74 $ $NHDT-Date: 1579914040 2020/01/25 01:00:40 $ +$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.76 $ $NHDT-Date: 1580043420 2020/01/26 12:57:00 $ General Fixes and Modified Features ----------------------------------- @@ -49,6 +49,7 @@ if at the edge of the map window, trying to move farther fails but used a turn hero can no longer wear blindfold/towel/lenses when poly'd into headless form revamp achievement tracking for exploring Mine's End and Sokoban (by acquiring luckstone and bag of holding or amulet of reflection, respectively) +throttle long worm growth rate and HP accumulation Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/src/worm.c b/src/worm.c index 113d00a32..920a2766f 100644 --- a/src/worm.c +++ b/src/worm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 worm.c $NHDT-Date: 1579990313 2020/01/25 22:11:53 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.41 $ */ +/* NetHack 3.6 worm.c $NHDT-Date: 1580043421 2020/01/26 12:57:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.42 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -218,26 +218,65 @@ struct monst *worm; seg->nseg = new_seg; /* attach it to the end of the list */ wheads[wnum] = new_seg; /* move the end pointer */ - /* - * [maybe] FIXME? - * scheduling wgrowtime[] seems to be based on normal movement - * speed (12) but long worms move at 1/4 of that (3), so they'll - * reach the scheduled 'moves' more quickly (in terms of their - * actual movement) and grow faster than was probably intended. - */ if (wgrowtime[wnum] <= g.moves) { - if (!wgrowtime[wnum]) + int whplimit, whpcap, wsegs = count_wsegs(worm); + + /* first set up for the next time to grow */ + if (!wgrowtime[wnum]) { + /* new worm; usually grow a tail segment on its next turn */ wgrowtime[wnum] = g.moves + rnd(5); - else - wgrowtime[wnum] += rn1(15, 3); - worm->mhp += 3; - if (worm->mhp > MHPMAX) - worm->mhp = MHPMAX; - if (worm->mhp > worm->mhpmax) - worm->mhpmax = worm->mhp; - } else - /* The worm doesn't grow, so the last segment goes away. */ + } else { + int mmove = mcalcmove(worm, FALSE), + /* prior to 3.7.0, next-grow increment was 3..17 but since + it got checked every 4th turn when the speed 3 worm got + to move, it was effectively 0..5; also, its usage was + 'wgrowtime += incr', so often 'wgrowtime' would be + exceeded by 'moves' on consecutive turns for the worm, + resulting in an excessively rapid growth cycle */ + incr = rn1(10, 2); /* 2..12; after adjusting for long worn + * speed of 3, effective value is 8..48 */ + + incr = (incr * NORMAL_SPEED) / max(mmove, 1); + wgrowtime[wnum] = g.moves + incr; + } + + /* increase HP based on number of segments; if it has shrunk, it + won't gain new HP until regaining previous peak segment count; + when wounded (whether from damage or from shrinking), the HP + which might have been 'new' will heal */ + whplimit = !worm->m_lev ? 4 : (8 * (int) worm->m_lev); + /* note: wsegs includes the hidden segment co-located with the head */ + if (wsegs > 33) + whplimit += 2 * (wsegs - 33), wsegs = 33; + if (wsegs > 22) + whplimit += 4 * (wsegs - 22), wsegs = 22; + if (wsegs > 11) + whplimit += 6 * (wsegs - 11), wsegs = 11; + whplimit += 8 * wsegs; + if (whplimit > MHPMAX) + whplimit = MHPMAX; + + worm->mhp += d(2, 2); /* 2..4, average 3 */ + whpcap = max(whplimit, worm->mhpmax); + if (worm->mhp < whpcap) { + /* can't exceed segment-derived limit unless level increase after + peak tail growth has already done so; when that isn't the case, + if segment growth exceeds current max HP then increase it */ + if (worm->mhp > whpcap) + worm->mhp = whpcap; + if (worm->mhp > worm->mhpmax) + worm->mhpmax = worm->mhp; + } else { + if (worm->mhp > worm->mhpmax) + worm->mhp = worm->mhpmax; + } + } else { + /* The worm doesn't grow, so the last segment goes away. + (Done after inserting an extra segment at the head, so it + isn't getting smaller here, just changing location without + having to move any of the intermediate segments.) */ shrink_worm(wnum); + } } /* @@ -253,10 +292,11 @@ struct monst *worm; { shrink_worm((int) worm->wormno); /* shrink */ - if (worm->mhp > 3) - worm->mhp -= 3; /* mhpmax not changed ! */ - else - worm->mhp = 1; + if (worm->mhp > count_wsegs(worm)) { + worm->mhp -= d(2, 2); /* 2..4, average 3; note: mhpmax not changed! */ + if (worm->mhp < 1) + worm->mhp = 1; + } } /* From 4505a8dc4c89f0c00a63be3b62a80d38915f3585 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 26 Jan 2020 05:12:55 -0800 Subject: [PATCH 05/20] left out of "limit rate of long worm growth" --- include/extern.h | 4 ++-- src/allmain.c | 6 +++--- src/mon.c | 29 ++++++++++++++++------------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/include/extern.h b/include/extern.h index a21b76988..ef9f64838 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1578895332 2020/01/13 06:02:12 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.785 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1580044333 2020/01/26 13:12:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.786 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1437,7 +1437,7 @@ E int FDECL(mfndpos, (struct monst *, coord *, long *, long)); E boolean FDECL(monnear, (struct monst *, int, int)); E void NDECL(dmonsfree); E void FDECL(elemental_clog, (struct monst *)); -E int FDECL(mcalcmove, (struct monst *)); +E int FDECL(mcalcmove, (struct monst *, BOOLEAN_P)); E void NDECL(mcalcdistress); E void FDECL(replmon, (struct monst *, struct monst *)); E void FDECL(relmon, (struct monst *, struct monst **)); diff --git a/src/allmain.c b/src/allmain.c index 5998e38b8..22faeabd7 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 allmain.c $NHDT-Date: 1578448653 2020/01/08 01:57:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.137 $ */ +/* NetHack 3.6 allmain.c $NHDT-Date: 1580044340 2020/01/26 13:12:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.138 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -125,7 +125,7 @@ boolean resuming; to skip dead monsters here because they will have been purged at end of their previous round of moving */ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) - mtmp->movement += mcalcmove(mtmp); + mtmp->movement += mcalcmove(mtmp, TRUE); /* occasionally add another monster; since this takes place after movement has been allotted, the new @@ -139,7 +139,7 @@ boolean resuming; /* calculate how much time passed. */ if (u.usteed && u.umoved) { /* your speed doesn't augment steed's speed */ - moveamt = mcalcmove(u.usteed); + moveamt = mcalcmove(u.usteed, TRUE); } else { moveamt = g.youmonst.data->mmove; diff --git a/src/mon.c b/src/mon.c index 6fd50d7f7..e2d571f5e 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1577759850 2019/12/31 02:37:30 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.319 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1580044343 2020/01/26 13:12:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.320 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -633,9 +633,11 @@ register struct monst *mtmp; return 0; } +/* calculate 'mon's movement for current turn; called from moveloop() */ int -mcalcmove(mon) +mcalcmove(mon, m_moving) struct monst *mon; +boolean m_moving; /* True: adjust for moving; False: just adjust for speed */ { int mmove = mon->data->mmove; int mmove_adj; @@ -656,17 +658,18 @@ struct monst *mon; mmove = ((rn2(2) ? 4 : 5) * mmove) / 3; } - /* Randomly round the monster's speed to a multiple of NORMAL_SPEED. - This makes it impossible for the player to predict when they'll get - a free turn (thus preventing exploits like "melee kiting"), while - retaining guarantees about shopkeepers not being outsped by a - normal-speed player, normal-speed players being unable to open up - a gap when fleeing a normal-speed monster, etc. */ - mmove_adj = mmove % NORMAL_SPEED; - mmove -= mmove_adj; - if (rn2(NORMAL_SPEED) < mmove_adj) - mmove += NORMAL_SPEED; - + if (m_moving) { + /* Randomly round the monster's speed to a multiple of NORMAL_SPEED. + This makes it impossible for the player to predict when they'll + get a free turn (thus preventing exploits like "melee kiting"), + while retaining guarantees about shopkeepers not being outsped + by a normal-speed player, normal-speed players being unable + to open up a gap when fleeing a normal-speed monster, etc. */ + mmove_adj = mmove % NORMAL_SPEED; + mmove -= mmove_adj; + if (rn2(NORMAL_SPEED) < mmove_adj) + mmove += NORMAL_SPEED; + } return mmove; } From c911446188ce049d7cb6301cfca1a8abd0746627 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 26 Jan 2020 12:23:50 -0800 Subject: [PATCH 06/20] more wizard mode wishing for terrain Try a lot harder to keep terrain/level flags in a sane state. They're overloaded so it's not simple. Creating a fountain or sink incremented the corresponding counter (for controlling ambient sounds) but removing one by wishing for something else in its place didn't decrement. Allow wish for "disturbed grave" to create a grave with the 'disturbed' flag set, similar to existing "magic fountain" and 'blessedftn' flag. (I didn't add "looted throne", "looted tree", and several other things that use the 'looted' overload of 'rm.flags'.) Automate block_point (tree, cloud, secret corridor, or secret door in open doorway) and add unblock_point (use Pass_wall to move into wall or tree or stone, or just walk onto a cloud, then make iron bars or almost any other wishable terrain to replace the blocking feature). --- include/rm.h | 23 +++++++------ src/objnam.c | 92 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 85 insertions(+), 30 deletions(-) diff --git a/include/rm.h b/include/rm.h index 09553de13..f660ec83b 100644 --- a/include/rm.h +++ b/include/rm.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 rm.h $NHDT-Date: 1578258722 2020/01/05 21:12:02 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.77 $ */ +/* NetHack 3.6 rm.h $NHDT-Date: 1580070206 2020/01/26 20:23:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.78 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -528,16 +528,19 @@ struct rm { #define SV7 0x80 #define SVALL 0xFF -#define doormask flags -#define altarmask flags -#define wall_info flags -#define ladder flags -#define drawbridgemask flags -#define looted flags -#define icedpool flags - +/* if these get changed or expanded, make sure wizard-mode wishing becomes + aware of the new usage */ +#define doormask flags /* door, sdoor (note conflict with wall_info) */ +#define altarmask flags /* alignment and maybe temple */ +#define wall_info flags /* wall, sdoor (note conflict with doormask) */ +#define ladder flags /* up or down */ +#define drawbridgemask flags /* what's underneath when the span is open */ +#define looted flags /* used for throne, tree, fountain, sink, door */ +#define icedpool flags /* used for ice (in case it melts) */ +/* horizonal applies to walls, doors (including sdoor); also to iron bars + even though they don't have separate symbols for horizontal and vertical */ #define blessedftn horizontal /* a fountain that grants attribs */ -#define disturbed horizontal /* a grave that has been disturbed */ +#define disturbed horizontal /* a grave that has been disturbed */ struct damage { struct damage *next; diff --git a/src/objnam.c b/src/objnam.c index 1777dab21..7708294f6 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 objnam.c $NHDT-Date: 1579261291 2020/01/17 11:41:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.288 $ */ +/* NetHack 3.7 objnam.c $NHDT-Date: 1580070220 2020/01/26 20:23:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.291 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2952,8 +2952,8 @@ char *bp, *p; int locked, trapped; { struct rm *lev; - boolean madeterrain = FALSE, badterrain = FALSE; - int trap, x = u.ux, y = u.uy; + boolean madeterrain = FALSE, badterrain = FALSE, didblock; + int trap, oldtyp, x = u.ux, y = u.uy; for (trap = NO_TRAP + 1; trap < TRAPNUM; trap++) { struct trap *t; @@ -2978,21 +2978,25 @@ int locked, trapped; /* furniture and terrain (use at your own risk; can clobber stairs or place furniture on existing traps which shouldn't be allowed) */ lev = &levl[x][y]; + oldtyp = lev->typ; + didblock = does_block(x, y, lev); p = eos(bp); if (!BSTRCMPI(bp, p - 8, "fountain")) { lev->typ = FOUNTAIN; g.level.flags.nfountains++; - if (!strncmpi(bp, "magic ", 6)) - lev->blessedftn = 1; + lev->looted = 0; /* overlays 'flags' */ + lev->blessedftn = !strncmpi(bp, "magic ", 6); pline("A %sfountain.", lev->blessedftn ? "magic " : ""); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 6, "throne")) { lev->typ = THRONE; + lev->looted = 0; /* overlays 'flags' */ pline("A throne."); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 4, "sink")) { lev->typ = SINK; g.level.flags.nsinks++; + lev->looted = 0; /* overlays 'flags' */ pline("A sink."); madeterrain = TRUE; @@ -3000,6 +3004,7 @@ int locked, trapped; } else if (!BSTRCMPI(bp, p - 4, "pool") || !BSTRCMPI(bp, p - 4, "moat")) { lev->typ = !BSTRCMPI(bp, p - 4, "pool") ? POOL : MOAT; + lev->flags = 0; del_engr_at(x, y); pline("A %s.", (lev->typ == POOL) ? "pool" : "moat"); /* Must manually make kelp! */ @@ -3009,6 +3014,7 @@ int locked, trapped; /* also matches "molten lava" */ } else if (!BSTRCMPI(bp, p - 4, "lava")) { lev->typ = LAVAPOOL; + lev->flags = 0; del_engr_at(x, y); pline("A pool of molten lava."); if (!(Levitation || Flying)) @@ -3028,34 +3034,51 @@ int locked, trapped; al = A_NONE; else /* -1 - A_CHAOTIC, 0 - A_NEUTRAL, 1 - A_LAWFUL */ al = !rn2(6) ? A_NONE : (rn2((int) A_LAWFUL + 2) - 1); - lev->altarmask = Align2amask(al); + lev->altarmask = Align2amask(al); /* overlays 'flags' */ pline("%s altar.", An(align_str(al))); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 5, "grave") || !BSTRCMPI(bp, p - 9, "headstone")) { make_grave(x, y, (char *) 0); - pline("%s.", IS_GRAVE(lev->typ) ? "A grave" - : "Can't place a grave here"); - madeterrain = TRUE; + if (IS_GRAVE(lev->typ)) { + lev->looted = 0; /* overlays 'flags' */ + lev->disturbed = !strncmpi(bp, "disturbed ", 10); + pline("A %sgrave.", lev->disturbed ? "disturbed " : ""); + madeterrain = TRUE; + } else { + pline("Can't place a grave here"); + badterrain = TRUE; + } } else if (!BSTRCMPI(bp, p - 4, "tree")) { lev->typ = TREE; + lev->looted = 0; /* overlays 'flags' */ pline("A tree."); - block_point(x, y); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 4, "bars")) { lev->typ = IRONBARS; + lev->flags = 0; + /* [FIXME: if this isn't a wall or door location where 'horizontal' + is already set up, that should be calculated for this spot. + Unforutnately, it can be tricky; placing one in open space + and then another adjacent might need to recalculate first one.] */ pline("Iron bars."); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 5, "cloud")) { lev->typ = CLOUD; - block_point(x, y); + lev->flags = 0; pline("A cloud."); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 11, "secret door")) { - if (lev->typ == DOOR - || (IS_WALL(lev->typ) && lev->typ != DBWALL)) { + /* require door or wall so that the 'horizontal' flag will + already have the correct value (it will matter once the + secret door is discovered and becomes a regular door); + player might choose to put SDOOR on top of existing SDOOR + to control its trapped state; iron bars are surrogate walls */ + if (lev->typ == DOOR || lev->typ == SDOOR + || (IS_WALL(lev->typ) && lev->typ != DBWALL) + || lev->typ == IRONBARS) { lev->typ = SDOOR; - lev->wall_info = 0; + lev->wall_info = 0; /* overlays 'flags' */ /* lev->horizontal stays as-is */ /* no special handling for rogue level is necessary; exposing a secret door there yields a doorless doorway */ @@ -3073,7 +3096,6 @@ int locked, trapped; #endif if (trapped) lev->doormask |= D_TRAPPED; - block_point(x, y); pline("Secret door."); madeterrain = TRUE; } else { @@ -3083,7 +3105,7 @@ int locked, trapped; } else if (!BSTRCMPI(bp, p - 15, "secret corridor")) { if (lev->typ == CORR) { lev->typ = SCORR; - block_point(x, y); + /* neither CORR nor SCORR uses 'flags' or 'horizontal' */ pline("Secret corridor."); madeterrain = TRUE; } else { @@ -3099,11 +3121,41 @@ int locked, trapped; if (u.uinwater && !is_pool(u.ux, u.uy)) { u.uinwater = 0; /* leave the water */ docrt(); - g.vision_full_recalc = 1; - } else if (u.utrap && u.utraptype == TT_LAVA - && !is_lava(u.ux, u.uy)) { - reset_utrap(FALSE); + /* [block/unblock_point was handled by docrt -> vision_recalc] */ + } else { + if (u.utrap && u.utraptype == TT_LAVA && !is_lava(u.ux, u.uy)) + reset_utrap(FALSE); + + if (does_block(x, y, lev)) { + if (!didblock) + block_point(x, y); + } else { + if (didblock) + unblock_point(x, y); + } } + /* fixups for replaced terrain that aren't handled above; + for fountain placed on fountain or sink placed on sink, the + increment above gets canceled out by the decrement here; + otherwise if fountain or sink was replaced, there's one less */ + if (IS_FOUNTAIN(oldtyp)) + g.level.flags.nfountains--; + else if (IS_SINK(oldtyp)) + g.level.flags.nsinks--; + /* horizontal is overlaid by fountain->blessedftn, grave->disturbed */ + if (IS_FOUNTAIN(oldtyp) || IS_GRAVE(oldtyp) + || IS_WALL(oldtyp) || oldtyp == IRONBARS + || IS_DOOR(oldtyp) || oldtyp == SDOOR) { + /* when new terrain is a fountain, 'blessedftn' was explicitly + set above; likewise for grave and 'disturbed'; when it's a + secret door, the old type was a wall or a door and we retain + the 'horizontal' value from those */ + if (!IS_FOUNTAIN(lev->typ) && !IS_GRAVE(lev->typ) + && lev->typ != SDOOR) + lev->horizontal = 0; /* also clears blessedftn, disturbed */ + } + /* note: lev->lit and lev->nondiggable retain their values even + though those might not make sense with the new terrain */ } if (madeterrain || badterrain) { /* cast 'const' away; caller won't modify this */ From c0d1c4efb7e10ecdfd50f9b3f315232a78c5dbc1 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 25 Jan 2020 13:38:02 -0500 Subject: [PATCH 07/20] Use 8 bit mode if available This was not previously done because the palette was not correctly set up, and QEMU gave incorrect colors. The palette setup function now falls back to the standard BIOS function if the VESA BIOS function fails, which resolves the issue with QEMU. The mode selection logic now favors 8 bit color if the tile set is compatible. Using 8 bit mode means fewer writes to memory and fewer uses of the BIOS window function. --- sys/msdos/vidvesa.c | 84 +++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/sys/msdos/vidvesa.c b/sys/msdos/vidvesa.c index 65ae2e845..f1e1848ef 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -788,8 +788,19 @@ vesa_Init(void) } #endif + vesa_mode = 0xFFFF; /* might want an 8 bit mode after loading tiles */ + vesa_detect(); if (vesa_mode == 0xFFFF) { - vesa_detect(); + raw_printf("Reverting to TTY mode, no VESA mode available.", + tilefailure); + wait_synch(); + iflags.usevga = 0; + iflags.tile_view = FALSE; + iflags.over_view = FALSE; + CO = 80; + LI = 25; + /* clear_screen() */ /* not vesa_clear_screen() */ + return; } vesa_SwitchMode(vesa_mode); windowprocs.win_cliparound = vesa_cliparound; @@ -813,7 +824,7 @@ vesa_Init(void) * * If mode == MODETEXT (0x03), then the card is placed into text * mode. Otherwise, the card is placed in the mode selected by - * vesa_detect. Supported modes are those with packed 8 bit pixels. + * vesa_detect. * */ static void @@ -910,6 +921,7 @@ vesa_detect() dosmemput(&vbe_info, sizeof(vbe_info), vbe_info_seg * 16L); /* Request VESA BIOS information */ + memset(®s, 0, sizeof(regs)); regs.x.ax = 0x4F00; regs.x.di = 0; regs.x.es = vbe_info_seg; @@ -928,7 +940,10 @@ vesa_detect() + (vbe_info.VideoModePtr & 0xFFFF); /* Scan the mode list for an acceptable mode */ - vesa_mode = vesa_FindMode(mode_addr, 32); + if (get_palette() != NULL) + vesa_mode = vesa_FindMode(mode_addr, 8); + if (vesa_mode == 0xFFFF) + vesa_mode = vesa_FindMode(mode_addr, 32); if (vesa_mode == 0xFFFF) vesa_mode = vesa_FindMode(mode_addr, 24); if (vesa_mode == 0xFFFF) @@ -1271,13 +1286,10 @@ static boolean vesa_SetHardPalette(palette) const struct Pixel *palette; { - const struct Pixel *p = palette; int palette_sel = -1; /* custodial */ int palette_seg; - unsigned long palette_ptr; + unsigned char p2[1024]; unsigned i, shift; - unsigned char r, g, b; - unsigned long color; __dpmi_regs regs; palette_seg = __dpmi_allocate_dos_memory( 1024 / 16, &palette_sel); @@ -1297,36 +1309,23 @@ const struct Pixel *palette; shift = 8 - regs.h.bh; } + /* First, try the VESA palette function */ /* Set the tile set and text colors */ - palette_ptr = palette_seg * 16L; #ifdef USE_TILES for (i = 0; i < FIRST_TEXT_COLOR; ++i) { - r = p->r >> shift; - g = p->g >> shift; - b = p->b >> shift; - color = ((unsigned long) r << 16) - | ((unsigned long) g << 8) - | ((unsigned long) b << 0); - _farpokel(_dos_ds, palette_ptr, color); - palette_ptr += 4; - ++p; + p2[i*4 + 0] = palette[i].b >> shift; + p2[i*4 + 1] = palette[i].g >> shift; + p2[i*4 + 2] = palette[i].r >> shift; } -#else - palette_ptr += FIRST_TEXT_COLOR * 4; #endif - p = defpalette; for (i = FIRST_TEXT_COLOR; i < 256; ++i) { - r = p->r >> shift; - g = p->g >> shift; - b = p->b >> shift; - color = ((unsigned long) r << 16) - | ((unsigned long) g << 8) - | ((unsigned long) b << 0); - _farpokel(_dos_ds, palette_ptr, color); - palette_ptr += 4; - ++p; + p2[i*4 + 0] = defpalette[i-FIRST_TEXT_COLOR].b >> shift; + p2[i*4 + 1] = defpalette[i-FIRST_TEXT_COLOR].g >> shift; + p2[i*4 + 2] = defpalette[i-FIRST_TEXT_COLOR].r >> shift; } + /* Call the BIOS */ + dosmemput(p2, 256*4, palette_seg*16L); memset(®s, 0, sizeof(regs)); regs.x.ax = 0x4F09; regs.h.bl = 0; @@ -1336,6 +1335,33 @@ const struct Pixel *palette; regs.x.es = palette_seg; (void) __dpmi_int(VIDEO_BIOS, ®s); + /* If that didn't work, use the original BIOS function */ + if (regs.x.ax != 0x004F) { + /* Set the tile set and text colors */ +#ifdef USE_TILES + for (i = 0; i < FIRST_TEXT_COLOR; ++i) { + p2[i*3 + 0] = palette[i].r >> shift; + p2[i*3 + 1] = palette[i].g >> shift; + p2[i*3 + 2] = palette[i].b >> shift; + } +#endif + for (i = FIRST_TEXT_COLOR; i < 256; ++i) { + p2[i*3 + 0] = defpalette[i-FIRST_TEXT_COLOR].r >> shift; + p2[i*3 + 1] = defpalette[i-FIRST_TEXT_COLOR].g >> shift; + p2[i*3 + 2] = defpalette[i-FIRST_TEXT_COLOR].b >> shift; + } + + /* Call the BIOS */ + dosmemput(p2, 256*3, palette_seg*16L); + memset(®s, 0, sizeof(regs)); + regs.x.ax = 0x1012; + regs.x.cx = 256; + regs.x.bx = 0; + regs.x.dx = 0; + regs.x.es = palette_seg; + (void) __dpmi_int(VIDEO_BIOS, ®s); + } + __dpmi_free_dos_memory(palette_sel); return TRUE; From ab6424b31c23dafc98aa7031b76446ad1f25fbb7 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 25 Jan 2020 13:49:26 -0500 Subject: [PATCH 08/20] Call the window function directly if supported Some BIOSes allow calling the window function directly, bypassing the logic that distinguishes this call from other BIOS calls. Should be faster on some systems. --- sys/msdos/vidvesa.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sys/msdos/vidvesa.c b/sys/msdos/vidvesa.c index f1e1848ef..07a76219e 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -120,6 +120,7 @@ static unsigned short vesa_y_center; /* Y centering offset */ static unsigned short vesa_scan_line; /* Bytes per scan line */ static int vesa_read_win; /* Select the read window */ static int vesa_write_win; /* Select the write window */ +static unsigned long vesa_win_func; static unsigned long vesa_win_pos[2]; /* Window position */ static unsigned long vesa_win_addr[2]; /* Window physical address */ static unsigned long vesa_win_size; /* Window size */ @@ -245,7 +246,14 @@ unsigned long offset; regs.h.bl = window; regs.x.dx = offset / vesa_win_gran; pos = regs.x.dx * vesa_win_gran; - (void) __dpmi_int(VIDEO_BIOS, ®s); + + if (vesa_win_func != 0) { + regs.x.cs = vesa_win_func >> 16; + regs.x.ip = vesa_win_func & 0xFFFF; + (void) __dpmi_simulate_real_mode_procedure_retf(®s); + } else { + (void) __dpmi_int(VIDEO_BIOS, ®s); + } vesa_win_pos[window] = pos; } @@ -921,7 +929,7 @@ vesa_detect() dosmemput(&vbe_info, sizeof(vbe_info), vbe_info_seg * 16L); /* Request VESA BIOS information */ - memset(®s, 0, sizeof(regs)); + memset(®s, 0, sizeof(regs)); regs.x.ax = 0x4F00; regs.x.di = 0; regs.x.es = vbe_info_seg; @@ -1004,6 +1012,7 @@ vesa_detect() break; } } + vesa_win_func = mode_info.WinFuncPtr; vesa_win_addr[0] = mode_info.WinASegment * 16L; vesa_win_addr[1] = mode_info.WinBSegment * 16L; vesa_win_pos[0] = 0xFFFFFFFF; /* position unknown */ From 8d9f31e91f0526a325054aba06b6037f95460585 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 25 Jan 2020 14:05:16 -0500 Subject: [PATCH 09/20] Support the linear frame buffer if available If the linear frame buffer is in use, the entire frame buffer is visible all at once, and no calls to the window function are needed. --- sys/msdos/vidvesa.c | 183 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 142 insertions(+), 41 deletions(-) diff --git a/sys/msdos/vidvesa.c b/sys/msdos/vidvesa.c index 07a76219e..9061d7ee7 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -123,6 +123,7 @@ static int vesa_write_win; /* Select the write window */ static unsigned long vesa_win_func; static unsigned long vesa_win_pos[2]; /* Window position */ static unsigned long vesa_win_addr[2]; /* Window physical address */ +static unsigned long vesa_segment; /* Selector of linear frame buffer */ static unsigned long vesa_win_size; /* Window size */ static unsigned long vesa_win_gran; /* Window granularity */ static unsigned char vesa_pixel_size; @@ -222,6 +223,20 @@ error: return FALSE; } +static unsigned +vesa_map_frame_buffer(unsigned phys_addr, unsigned size) +{ + __dpmi_meminfo info; + int rc; + + info.handle = 0; + info.address = phys_addr; + info.size = size; + rc = __dpmi_physical_address_mapping(&info); + + return rc == 0 ? info.address : 0; +} + /* Set the memory window and return the offset */ static unsigned long vesa_SetWindow(window, offset) @@ -276,31 +291,66 @@ unsigned x, y; unsigned long addr, color; unsigned i; - switch (vesa_pixel_size) { - case 8: - addr = vesa_SetWindow(vesa_read_win, offset); - color = _farpeekb(_dos_ds, addr); - break; + if (vesa_segment != 0) { + /* Linear frame buffer in use */ + switch (vesa_pixel_size) { + case 8: + color = _farpeekb(vesa_segment, offset); + break; - case 15: - case 16: - addr = vesa_SetWindow(vesa_read_win, offset); - color = _farpeekw(_dos_ds, addr); - break; + case 15: + case 16: + color = _farpeekw(vesa_segment, offset); + break; - case 24: - /* Pixel may cross a window boundary */ - color = 0; - for (i = 0; i < 3; ++i) { - addr = vesa_SetWindow(vesa_read_win, offset + i); - color |= (unsigned long) _farpeekb(_dos_ds, addr) << (i * 8); + case 24: + /* Don't cross a 4 byte boundary if it can be avoided */ + if (offset & 1) { + color = _farpeekl(vesa_segment, offset - 1) >> 8; + } else { + color = _farpeekl(vesa_segment, offset) & 0xFFFFFFFF; + } + break; + + case 32: + color = _farpeekl(vesa_segment, offset); + break; + + default: /* Shouldn't happen */ + color = 0; + break; } - break; + } else { + switch (vesa_pixel_size) { + case 8: + addr = vesa_SetWindow(vesa_read_win, offset); + color = _farpeekb(_dos_ds, addr); + break; - case 32: - addr = vesa_SetWindow(vesa_read_win, offset); - color = _farpeekl(_dos_ds, addr); - break; + case 15: + case 16: + addr = vesa_SetWindow(vesa_read_win, offset); + color = _farpeekw(_dos_ds, addr); + break; + + case 24: + /* Pixel may cross a window boundary */ + color = 0; + for (i = 0; i < 3; ++i) { + addr = vesa_SetWindow(vesa_read_win, offset + i); + color |= (unsigned long) _farpeekb(_dos_ds, addr) << (i * 8); + } + break; + + case 32: + addr = vesa_SetWindow(vesa_read_win, offset); + color = _farpeekl(_dos_ds, addr); + break; + + default: /* Shouldn't happen */ + color = 0; + break; + } } return color; } @@ -314,30 +364,62 @@ unsigned long color; unsigned long addr; unsigned i; - switch (vesa_pixel_size) { - case 8: - addr = vesa_SetWindow(vesa_write_win, offset); - _farpokeb(_dos_ds, addr, color); - break; + if (vesa_segment != 0) { + /* Linear frame buffer in use */ + switch (vesa_pixel_size) { + case 8: + _farpokeb(vesa_segment, offset, color); + break; - case 15: - case 16: - addr = vesa_SetWindow(vesa_write_win, offset); - _farpokew(_dos_ds, addr, color); - break; + case 15: + case 16: + _farpokew(vesa_segment, offset, color); + break; - case 24: - /* Pixel may cross a window boundary */ - for (i = 0; i < 3; ++i) { - addr = vesa_SetWindow(vesa_read_win, offset + i); - _farpokeb(_dos_ds, addr, (unsigned char) (color >> (i * 8))); + case 24: + if (offset & 1) { + _farpokeb(vesa_segment, offset + 0, color & 0xFF); + _farpokew(vesa_segment, offset + 1, color >> 8); + } else { + _farpokew(vesa_segment, offset + 0, color & 0xFFFF); + _farpokeb(vesa_segment, offset + 2, color >> 16); + } + break; + + case 32: + _farpokel(vesa_segment, offset, color); + break; + + default: /* Shouldn't happen */ + color = 0; + break; } - break; + } else { + switch (vesa_pixel_size) { + case 8: + addr = vesa_SetWindow(vesa_write_win, offset); + _farpokeb(_dos_ds, addr, color); + break; - case 32: - addr = vesa_SetWindow(vesa_write_win, offset); - _farpokel(_dos_ds, addr, color); - break; + case 15: + case 16: + addr = vesa_SetWindow(vesa_write_win, offset); + _farpokew(_dos_ds, addr, color); + break; + + case 24: + /* Pixel may cross a window boundary */ + for (i = 0; i < 3; ++i) { + addr = vesa_SetWindow(vesa_write_win, offset + i); + _farpokeb(_dos_ds, addr, (unsigned char) (color >> (i * 8))); + } + break; + + case 32: + addr = vesa_SetWindow(vesa_write_win, offset); + _farpokel(_dos_ds, addr, color); + break; + } } } @@ -1034,6 +1116,25 @@ vesa_detect() goto error; /* Shouldn't happen */ } + /* Configure a linear frame buffer if we have it */ + if ((mode_info.ModeAttributes & 0x80) != 0) { + unsigned sel = vesa_segment; + unsigned win_size = mode_info.BytesPerScanLine * mode_info.YResolution; + unsigned addr = vesa_map_frame_buffer(mode_info.PhysBasePtr, win_size); + if (sel == 0) { + sel = __dpmi_allocate_ldt_descriptors(1); + } + if (addr != 0) { + vesa_mode |= 0x4000; + vesa_segment = sel; + __dpmi_set_segment_base_address(sel, addr); + __dpmi_set_segment_limit(sel, (win_size - 1) | 0xFFF); + } else { + __dpmi_free_ldt_descriptor(sel); + vesa_segment = 0; + } + } + __dpmi_free_dos_memory(vbe_info_sel); return TRUE; From aa3303cafcf4fa5e4154ac6c6e4089f2e2a783c5 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 25 Jan 2020 14:23:04 -0500 Subject: [PATCH 10/20] More bits for linear frame buffer --- sys/msdos/vidvesa.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/sys/msdos/vidvesa.c b/sys/msdos/vidvesa.c index 9061d7ee7..0b9c61845 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -925,18 +925,21 @@ unsigned mode; if (mode == MODETEXT) { iflags.grmode = 0; + memset(®s, 0, sizeof(regs)); regs.x.ax = mode; (void) __dpmi_int(VIDEO_BIOS, ®s); } else if (mode >= 0x100) { iflags.grmode = 1; + memset(®s, 0, sizeof(regs)); regs.x.ax = 0x4F02; - regs.x.bx = mode & 0x81FF; + regs.x.bx = mode; (void) __dpmi_int(VIDEO_BIOS, ®s); /* Record that the window position is unknown */ vesa_win_pos[0] = 0xFFFFFFFF; vesa_win_pos[1] = 0xFFFFFFFF; } else { iflags.grmode = 0; /* force text mode for error msg */ + memset(®s, 0, sizeof(regs)); regs.x.ax = MODETEXT; (void) __dpmi_int(VIDEO_BIOS, ®s); g_attribute = attrib_text_normal; @@ -1057,12 +1060,21 @@ vesa_detect() vesa_pixel_size = mode_info.BitsPerPixel; vesa_pixel_bytes = (vesa_pixel_size + 7) / 8; if (vbe_info.VbeVersion >= 0x0300) { - vesa_red_pos = mode_info.RedFieldPosition; - vesa_red_size = mode_info.RedMaskSize; - vesa_green_pos = mode_info.GreenFieldPosition; - vesa_green_size = mode_info.GreenMaskSize; - vesa_blue_pos = mode_info.BlueFieldPosition; - vesa_blue_size = mode_info.BlueMaskSize; + if (mode_info.ModeAttributes & 0x80) { + vesa_red_pos = mode_info.LinRedFieldPosition; + vesa_red_size = mode_info.LinRedMaskSize; + vesa_green_pos = mode_info.LinGreenFieldPosition; + vesa_green_size = mode_info.LinGreenMaskSize; + vesa_blue_pos = mode_info.LinBlueFieldPosition; + vesa_blue_size = mode_info.LinBlueMaskSize; + } else { + vesa_red_pos = mode_info.RedFieldPosition; + vesa_red_size = mode_info.RedMaskSize; + vesa_green_pos = mode_info.GreenFieldPosition; + vesa_green_size = mode_info.GreenMaskSize; + vesa_blue_pos = mode_info.BlueFieldPosition; + vesa_blue_size = mode_info.BlueMaskSize; + } } else { switch (vesa_pixel_size) { case 15: From 9460796a2bcfc38b559cb838f3166b5b71e815a4 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 25 Jan 2020 19:00:02 -0500 Subject: [PATCH 11/20] MSDOS/VESA: Add video_width and video_height To be used to set the video mode --- include/flag.h | 4 ++++ src/options.c | 25 ++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/flag.h b/include/flag.h index 0cfd3dabc..d92bd5dc8 100644 --- a/include/flag.h +++ b/include/flag.h @@ -367,6 +367,10 @@ struct instance_flags { int wc_map_mode; /* specify map viewing options, mostly * for backward compatibility */ int wc_player_selection; /* method of choosing character */ +#if defined(MSDOS) + unsigned wc_video_width; /* X resolution of screen */ + unsigned wc_video_height; /* Y resolution of screen */ +#endif boolean wc_splash_screen; /* display an opening splash screen or not */ boolean wc_popup_dialog; /* put queries in pop up dialogs instead of * in the message window */ diff --git a/src/options.c b/src/options.c index f6f572689..fb839b540 100644 --- a/src/options.c +++ b/src/options.c @@ -3349,8 +3349,31 @@ boolean tinitial, tfrom_file; return retval; } #endif /* VIDEOSHADES */ - #ifdef MSDOS + fullname = "video_width"; + if (match_optname(opts, fullname, 7, TRUE)) { + if (duplicate) + complain_about_duplicate(opts, 1); + if (negated) { + bad_negation(fullname, FALSE); + return FALSE; + } + op = string_for_opt(opts, negated); + iflags.wc_video_width = strtol(op, NULL, 10); + return FALSE; + } + fullname = "video_height"; + if (match_optname(opts, fullname, 7, TRUE)) { + if (duplicate) + complain_about_duplicate(opts, 1); + if (negated) { + bad_negation(fullname, FALSE); + return FALSE; + } + op = string_for_opt(opts, negated); + iflags.wc_video_height = strtol(op, NULL, 10); + return FALSE; + } #ifdef NO_TERMS /* video:string -- must be after longer tests */ fullname = "video"; From 1870e5a41be029bbddc8627e45a944420c304645 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 25 Jan 2020 19:00:54 -0500 Subject: [PATCH 12/20] Fix bugs affecting full-color BMP files --- win/share/bmptiles.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/win/share/bmptiles.c b/win/share/bmptiles.c index 641415915..b75bbdc54 100644 --- a/win/share/bmptiles.c +++ b/win/share/bmptiles.c @@ -107,15 +107,6 @@ struct TileSetImage *image; if (!read_info_header(fp, &header2)) goto error; if (!check_info_header(&header2)) goto error; -#if 0 /* TODO */ - if (header2.Compression == BI_PNG) { - /* Image data is an embedded PNG bit stream */ - boolean ok = do_read_png_tiles(fp, image)); - fclose(fp); - return ok; - } -#endif - /* header2.Height < 0 means the Y coordinate is reversed; the origin is * top left rather than bottom left */ image->width = header2.Width; @@ -300,7 +291,7 @@ struct TileSetImage *image; break; case 32: for (x = 0; x < image->width; ++x) { - uint32 color = read_u32(row_bytes + x * 2); + uint32 color = read_u32(row_bytes + x * 4); row[x] = build_pixel(&header2, color); } break; @@ -609,7 +600,7 @@ uint32 color; if (mask == 0) return 0; bits = 0xFFFF; /* 0xFF, 0xF, 0x3, 0x1 */ shift = 16; /* 8, 4, 2, 1 */ - while (bits != 0) { + while (shift != 0) { if ((mask & bits) == 0) { mask >>= shift; color >>= shift; From b2c8d916f68f3d2d3e6125950d30106313885602 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 25 Jan 2020 19:02:18 -0500 Subject: [PATCH 13/20] Add set_tile_type, stretch_tile and free_tile set_tile_type frees tileset memory not in use for the current tile type (paletted or full color). stretch_tile and free_tile support resizing tiles at run time. --- include/tileset.h | 6 ++ win/share/tileset.c | 130 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/include/tileset.h b/include/tileset.h index 2ab145e66..9f6e86b5b 100644 --- a/include/tileset.h +++ b/include/tileset.h @@ -18,9 +18,15 @@ struct TileImage { boolean FDECL(read_tiles, (const char *filename, BOOLEAN_P true_color)); const struct Pixel *NDECL(get_palette); +boolean FDECL(set_tile_type, (BOOLEAN_P true_color)); void NDECL(free_tiles); const struct TileImage *FDECL(get_tile, (unsigned tile_index)); +/* For resizing tiles */ +struct TileImage *FDECL(stretch_tile, (const struct TileImage *, + unsigned, unsigned)); +void FDECL(free_tile, (struct TileImage *)); + /* Used internally by the tile set code */ struct TileSetImage { /* Image data */ diff --git a/win/share/tileset.c b/win/share/tileset.c index d37b91281..e9b448ab0 100644 --- a/win/share/tileset.c +++ b/win/share/tileset.c @@ -6,6 +6,7 @@ #include "tileset.h" static void FDECL(get_tile_map, (const char *)); +static unsigned FDECL(gcd, (unsigned, unsigned)); static void FDECL(split_tiles, (const struct TileSetImage *)); static void FDECL(free_image, (struct TileSetImage *)); @@ -100,6 +101,29 @@ error: return FALSE; } +/* Free tile memory not required by the chosen display mode */ +boolean +set_tile_type(true_color) +boolean true_color; +{ + unsigned i; + + if (tiles) { + if (true_color) { + for (i = 0; i < num_tiles; ++i) { + free((genericptr_t) tiles[i].indexes); + tiles[i].indexes = NULL; + } + have_palette = FALSE; + } else { + for (i = 0; i < num_tiles; ++i) { + free((genericptr_t) tiles[i].pixels); + tiles[i].pixels = NULL; + } + } + } +} + const struct Pixel * get_palette() { @@ -155,6 +179,112 @@ unsigned tile_index; return &tiles[tile_index]; } +/* Note that any tile returned by this function must be freed */ +struct TileImage * +stretch_tile(inp_tile, out_width, out_height) +const struct TileImage *inp_tile; +unsigned out_width, out_height; +{ + unsigned x_scale_inp, x_scale_out, y_scale_inp, y_scale_out; + unsigned divisor; + unsigned size; + struct TileImage *out_tile; + unsigned x_inp, y_inp, x2, y2, x_out, y_out; + unsigned pos; + + /* Derive the scale factors */ + divisor = gcd(out_width, inp_tile->width); + x_scale_inp = inp_tile->width / divisor; + x_scale_out = out_width / divisor; + divisor = gcd(out_height, inp_tile->height); + y_scale_inp = inp_tile->height / divisor; + y_scale_out = out_height / divisor; + + /* Derive the stretched tile */ + out_tile = (struct TileImage *) alloc(sizeof(struct TileImage)); + out_tile->width = out_width; + out_tile->height = out_height; + size = out_width * out_height; + if (inp_tile->pixels != NULL) { + out_tile->pixels = (struct Pixel *) alloc(size * sizeof(struct Pixel)); + divisor = x_scale_inp * y_scale_inp; + for (y_out = 0; y_out < out_height; ++y_out) { + for (x_out = 0; x_out < out_width; ++x_out) { + unsigned r, g, b, a; + + /* Derive output pixels by blending input pixels */ + r = 0; + g = 0; + b = 0; + a = 0; + for (y2 = 0; y2 < y_scale_inp; ++y2) { + y_inp = (y_out * y_scale_inp + y2) / y_scale_out; + for (x2 = 0; x2 < x_scale_inp; ++x2) { + x_inp = (x_out * x_scale_inp + x2) / x_scale_out; + pos = y_inp * inp_tile->width + x_inp; + r += inp_tile->pixels[pos].r; + g += inp_tile->pixels[pos].g; + b += inp_tile->pixels[pos].b; + a += inp_tile->pixels[pos].a; + } + } + + pos = y_out * out_width + x_out; + out_tile->pixels[pos].r = r / divisor; + out_tile->pixels[pos].g = g / divisor; + out_tile->pixels[pos].b = b / divisor; + out_tile->pixels[pos].a = a / divisor; + } + } + } else { + out_tile->pixels = NULL; + } + + /* If the output device uses a palette, we can't blend; just pick + a subset of the pixels */ + if (inp_tile->indexes != NULL) { + out_tile->indexes = (unsigned char *) alloc(size); + for (y_out = 0; y_out < out_height; ++y_out) { + for (x_out = 0; x_out < out_width; ++x_out) { + pos = y_out * out_width + x_out; + x_inp = x_out * x_scale_inp / x_scale_out; + y_inp = y_out * y_scale_inp / y_scale_out; + out_tile->indexes[pos] = + inp_tile->indexes[y_inp * inp_tile->width + x_inp]; + } + } + } else { + out_tile->indexes = NULL; + } + return out_tile; +} + +/* Free a tile returned by stretch_tile */ +/* Do NOT use this with tiles returned by get_tile */ +void +free_tile(tile) +struct TileImage *tile; +{ + if (tile != NULL) { + free(tile->indexes); + free(tile->pixels); + free(tile); + } +} + +/* Return the greatest common divisor */ +static unsigned +gcd(a, b) +unsigned a, b; +{ + while (TRUE) { + if (b == 0) return a; + a %= b; + if (a == 0) return b; + b %= a; + } +} + static void split_tiles(image) const struct TileSetImage *image; From 34da5ea3728924024dd4aaba5302f0ac4c075616 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 25 Jan 2020 19:03:48 -0500 Subject: [PATCH 14/20] MSDOS/VESA: Make clipy and clipymax available to MS-DOS VESA mode may need to scroll up and down as well as left and right. --- win/tty/wintty.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/win/tty/wintty.c b/win/tty/wintty.c index a6ad2f7a2..fda9f6aa3 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -155,11 +155,12 @@ char defmorestr[] = "--More--"; #if defined(USE_TILES) && defined(MSDOS) boolean clipping = FALSE; /* clipping on? */ int clipx = 0, clipxmax = 0; +int clipy = 0, clipymax = 0; #else static boolean clipping = FALSE; /* clipping on? */ static int clipx = 0, clipxmax = 0; -#endif static int clipy = 0, clipymax = 0; +#endif #endif /* CLIPPING */ #if defined(USE_TILES) && defined(MSDOS) From 82a219c2278dd4261a9f57a9ead8e84310d3d6fb Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 25 Jan 2020 19:04:44 -0500 Subject: [PATCH 15/20] Lift restrictions on tilesets Support full color tilesets with tiles of arbitrary size. --- sys/msdos/msdos.c | 5 + sys/msdos/pcvideo.h | 10 +- sys/msdos/vidvesa.c | 1114 +++++++++++++++++++++++++++++-------------- sys/msdos/vidvga.c | 10 +- 4 files changed, 779 insertions(+), 360 deletions(-) diff --git a/sys/msdos/msdos.c b/sys/msdos/msdos.c index b68041943..c3230140e 100644 --- a/sys/msdos/msdos.c +++ b/sys/msdos/msdos.c @@ -64,6 +64,11 @@ tgetch() { char ch; +#ifdef SCREEN_VESA + if (iflags.usevesa) { + vesa_flush_text(); + } +#endif /*SCREEN_VESA*/ /* BIOSgetch can use the numeric key pad on IBM compatibles. */ #ifdef SIMULATE_CURSOR if (iflags.grmode && cursor_flag) diff --git a/sys/msdos/pcvideo.h b/sys/msdos/pcvideo.h index bc2f876fd..cfe45c1eb 100644 --- a/sys/msdos/pcvideo.h +++ b/sys/msdos/pcvideo.h @@ -181,6 +181,10 @@ struct overview_planar_cell_struct { #define ATTRIB_VGA_INTENSE 14 /* Intense White 94/06/07 palette chg*/ #endif /*SCREEN_VGA || SCREEN_8514*/ +#if defined(SCREEN_VESA) +#define BACKGROUND_VESA_COLOR 240 +#endif /*SCREEN_VESA*/ + #if defined(PC9800) static unsigned char attr98[CLR_MAX] = { 0xe1, /* 0 white */ @@ -249,6 +253,7 @@ E void FDECL(txt_xputc, (CHAR_P, int)); /* ### vidvga.c ### */ +enum vga_pan_direction { pan_left, pan_up, pan_right, pan_down }; #ifdef SCREEN_VGA E void NDECL(vga_backsp); E void FDECL(vga_clear_screen, (int)); @@ -274,7 +279,7 @@ E void FDECL(vga_tty_startup, (int *, int *)); E void FDECL(vga_xputs, (const char *, int, int)); E void FDECL(vga_xputc, (CHAR_P, int)); E void FDECL(vga_xputg, (int, int, unsigned)); -E void FDECL(vga_userpan, (BOOLEAN_P)); +E void FDECL(vga_userpan, (enum vga_pan_direction)); E void FDECL(vga_overview, (BOOLEAN_P)); E void FDECL(vga_traditional, (BOOLEAN_P)); E void NDECL(vga_refresh); @@ -303,10 +308,11 @@ E void FDECL(vesa_tty_startup, (int *, int *)); E void FDECL(vesa_xputs, (const char *, int, int)); E void FDECL(vesa_xputc, (CHAR_P, int)); E void FDECL(vesa_xputg, (int, int, unsigned)); -E void FDECL(vesa_userpan, (BOOLEAN_P)); +E void FDECL(vesa_userpan, (enum vga_pan_direction)); E void FDECL(vesa_overview, (BOOLEAN_P)); E void FDECL(vesa_traditional, (BOOLEAN_P)); E void NDECL(vesa_refresh); +E void NDECL(vesa_flush_text); #endif /* SCREEN_VESA */ #endif /* NO_TERMS */ diff --git a/sys/msdos/vidvesa.c b/sys/msdos/vidvesa.c index 0b9c61845..b16b8e6d7 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -17,18 +17,23 @@ #include "wintty.h" #include "tileset.h" -#define BACKGROUND_VESA_COLOR 1 #define FIRST_TEXT_COLOR 240 +extern int total_tiles_used; /* tile.c */ + +struct VesaCharacter { + int colour; + int chr; +}; + static unsigned long FDECL(vesa_SetWindow, (int window, unsigned long offset)); static unsigned long FDECL(vesa_ReadPixel32, (unsigned x, unsigned y)); static void FDECL(vesa_WritePixel32, (unsigned x, unsigned y, unsigned long color)); static void FDECL(vesa_WritePixel, (unsigned x, unsigned y, unsigned color)); -static unsigned long FDECL(vesa_MakeColor, (unsigned r, unsigned g, unsigned b)); -static void FDECL(vesa_GetRGB, ( - unsigned long color, - unsigned char *rp, unsigned char *gp, unsigned char *bp)); +static void FDECL(vesa_WritePixelRow, (unsigned long offset, + unsigned char const *p_row, unsigned p_row_size)); +static unsigned long FDECL(vesa_MakeColor, (struct Pixel)); static void FDECL(vesa_FillRect, ( unsigned left, unsigned top, unsigned width, unsigned height, @@ -36,26 +41,33 @@ static void FDECL(vesa_FillRect, ( static void NDECL(vesa_redrawmap); static void FDECL(vesa_cliparound, (int, int)); +#if 0 static void FDECL(decal_packed, (const struct TileImage *tile, unsigned special)); +#endif static void FDECL(vesa_SwitchMode, (unsigned mode)); +static void NDECL(vesa_SetViewPort); static boolean FDECL(vesa_SetPalette, (const struct Pixel *)); static boolean FDECL(vesa_SetHardPalette, (const struct Pixel *)); static boolean FDECL(vesa_SetSoftPalette, (const struct Pixel *)); -static void FDECL(vesa_DisplayCell, (const struct TileImage *tile, int, int)); -static void FDECL(vesa_DisplayCellInMemory, (const struct TileImage *tile, - int, char buf[TILE_Y][640*2])); +static void FDECL(vesa_DisplayCell, (int, int, int)); static unsigned FDECL(vesa_FindMode, (unsigned long mode_addr, unsigned bits)); -static void FDECL(vesa_WriteChar, (int, int, int, int, BOOLEAN_P)); -static void FDECL(vesa_WriteCharInMemory, (int, int, char buf[TILE_Y][640*2], - int)); +static void FDECL(vesa_WriteChar, (int, int, int, int)); +static void FDECL(vesa_WriteCharXY, (int, int, int, int)); +static void FDECL(vesa_WriteCharTransparent, (int, int, int, int)); +static void FDECL(vesa_WriteTextRow, (int pixx, int pixy, + struct VesaCharacter const *t_row, unsigned t_row_width)); +static boolean FDECL(vesa_GetCharPixel, (int, unsigned, unsigned)); +static unsigned char FDECL(vesa_GetCharPixelRow, (int, unsigned, unsigned)); static void FDECL(vesa_WriteStr, (const char *, int, int, int, int)); -static char __far *NDECL(vesa_FontPtrs); +static unsigned char __far *NDECL(vesa_FontPtrs); +static void FDECL(vesa_process_tile, (struct TileImage *tile)); #ifdef POSITIONBAR static void NDECL(positionbar); #endif extern int clipx, clipxmax; /* current clipping column from wintty.c */ +extern int clipy, clipymax; /* current clipping row from wintty.c */ extern int curcol, currow; /* current column and row */ extern int g_attribute; extern int attrib_text_normal; /* text mode normal attribute */ @@ -90,7 +102,8 @@ static struct map_struct { } #define TOP_MAP_ROW 1 -static int viewport_size = 40; +static int viewport_cols = 40; +static int viewport_rows = ROWNO; static const struct Pixel defpalette[] = { /* Colors for text and the position bar */ { 0x18, 0x18, 0x18, 0xff }, /* CLR_BLACK */ @@ -129,12 +142,25 @@ static unsigned long vesa_win_gran; /* Window granularity */ static unsigned char vesa_pixel_size; static unsigned char vesa_pixel_bytes; static unsigned char vesa_red_pos; -static unsigned char vesa_red_size; +static unsigned char vesa_red_shift; static unsigned char vesa_green_pos; -static unsigned char vesa_green_size; +static unsigned char vesa_green_shift; static unsigned char vesa_blue_pos; -static unsigned char vesa_blue_size; +static unsigned char vesa_blue_shift; static unsigned long vesa_palette[256]; +static unsigned vesa_char_width = 8, vesa_char_height = 16; +static unsigned vesa_oview_width, vesa_oview_height; +static unsigned char **vesa_tiles; +static unsigned char **vesa_oview_tiles; + +#ifdef SIMULATE_CURSOR +static unsigned long *undercursor; +#endif + +/* Used to cache character writes for speed */ +static struct VesaCharacter chr_cache[100]; +static unsigned chr_cache_size; +static unsigned chr_cache_pixx, chr_cache_pixy, chr_cache_lastx; struct OldModeInfo { unsigned mode; @@ -435,54 +461,93 @@ unsigned color; } } -static unsigned long -vesa_MakeColor(r, g, b) -unsigned r, g, b; +static void +vesa_WritePixelRow(offset, p_row, p_row_size) +unsigned long offset; +unsigned char const *p_row; +unsigned p_row_size; { - r = (r & 0xFF) >> (8 - vesa_red_size); - g = (g & 0xFF) >> (8 - vesa_green_size); - b = (b & 0xFF) >> (8 - vesa_blue_size); - return ((unsigned long) r << vesa_red_pos) - | ((unsigned long) g << vesa_green_pos) - | ((unsigned long) b << vesa_blue_pos); + if (vesa_segment != 0) { + /* Linear frame buffer in use */ + movedata(_go32_my_ds(), (unsigned)p_row, vesa_segment, offset, p_row_size); + } else { + /* Windowed frame buffer in use */ + unsigned long addr = vesa_SetWindow(vesa_write_win, offset); + unsigned i = 0; + while (offset + p_row_size > vesa_win_pos[vesa_write_win] + vesa_win_size) { + /* The row passes the end of the current window. Write what we can, + * and advance the window */ + unsigned w = (vesa_win_pos[vesa_write_win] + vesa_win_size) + - (offset + i); + dosmemput(p_row + i, w, addr); + i += w; + addr = vesa_SetWindow(vesa_write_win, offset + i); + } + dosmemput(p_row + i, p_row_size - i, addr); + } } -static void -vesa_GetRGB(color, rp, gp, bp) -unsigned long color; -unsigned char *rp, *gp, *bp; +static unsigned long +vesa_MakeColor(p) +struct Pixel p; { - unsigned r, g, b; - - r = color >> vesa_red_pos; - g = color >> vesa_green_pos; - b = color >> vesa_blue_pos; - r <<= 8 - vesa_red_size; - g <<= 8 - vesa_green_size; - b <<= 8 - vesa_blue_size; - *rp = (unsigned char) r; - *gp = (unsigned char) g; - *bp = (unsigned char) b; + unsigned long r = p.r >> vesa_red_shift; + unsigned long g = p.g >> vesa_green_shift; + unsigned long b = p.b >> vesa_blue_shift; + return (r << vesa_red_pos) + | (g << vesa_green_pos) + | (b << vesa_blue_pos); } static void vesa_FillRect(left, top, width, height, color) unsigned left, top, width, height, color; { + unsigned p_row_size = width * vesa_pixel_bytes; + unsigned char *p_row = (unsigned char *) alloc(p_row_size); + unsigned long c32 = vesa_palette[color & 0xFF]; + unsigned long offset = top * (unsigned long)vesa_scan_line + left * vesa_pixel_bytes; unsigned x, y; - for (y = 0; y < height; ++y) { + switch (vesa_pixel_bytes) { + case 1: + memset(p_row, color, p_row_size); + break; + + case 2: for (x = 0; x < width; ++x) { - vesa_WritePixel(left + x, top + y, color); + ((uint16_t *)p_row)[x] = c32; } + break; + + case 3: + for (x = 0; x < width; ++x) { + p_row[3*x + 0] = c32 & 0xFF; + p_row[3*x + 1] = (c32 >> 8) & 0xFF; + p_row[3*x + 2] = c32 >> 16 ; + } + break; + + case 4: + for (x = 0; x < width; ++x) { + ((uint32_t *)p_row)[x] = c32; + } + break; } + + for (y = 0; y < height; ++y) { + vesa_WritePixelRow(offset, p_row, p_row_size); + offset += vesa_scan_line; + } + + free(p_row); } void vesa_get_scr_size() { - CO = 80; - LI = 29; + CO = vesa_x_res / vesa_char_width; + LI = vesa_y_res / vesa_char_height - 1; } void @@ -513,11 +578,12 @@ void vesa_cl_end(col, row) int col, row; { - unsigned left = vesa_x_center + col * 8; - unsigned top = vesa_y_center + row * 16; - unsigned width = (CO - 1 - col) * 8; - unsigned height = 16; + unsigned left = vesa_x_center + col * vesa_char_width; + unsigned top = vesa_y_center + row * vesa_char_height; + unsigned width = (CO - 1 - col) * vesa_char_width; + unsigned height = vesa_char_height; + vesa_flush_text(); vesa_FillRect(left, top, width, height, BACKGROUND_VESA_COLOR); } @@ -526,14 +592,12 @@ void vesa_cl_eos(cy) int cy; { - int count; - cl_end(); if (cy < LI - 1) { - unsigned left = vesa_x_center; - unsigned top = vesa_y_center + cy * 16; - unsigned width = 640; - unsigned height = (LI - 1 - cy) * 16; + unsigned left = 0; + unsigned top = vesa_y_center + cy * vesa_char_height; + unsigned width = vesa_x_res; + unsigned height = (LI - 1 - cy) * vesa_char_height; vesa_FillRect(left, top, width, height, BACKGROUND_VESA_COLOR); } @@ -611,7 +675,10 @@ int attr; ++row; break; default: - vesa_WriteChar((unsigned char) ch, col, row, attr, FALSE); + vesa_WriteChar((unsigned char) ch, col, row, attr); + if (ch == '.') { + vesa_flush_text(); + } if (col < (CO - 1)) ++col; break; @@ -632,7 +699,6 @@ unsigned special; /* special feature: corpse, invis, detected, pet, ridden - int col, row; int attr; int ry; - const struct TileImage *packcell; row = currow; col = curcol; @@ -646,13 +712,15 @@ unsigned special; /* special feature: corpse, invis, detected, pet, ridden - attr = (g_attribute == 0) ? attrib_gr_normal : g_attribute; map[ry][col].attr = attr; if (iflags.traditional_view) { - vesa_WriteChar((unsigned char) ch, col, row, attr, FALSE); + vesa_WriteChar((unsigned char) ch, col, row, attr); } else { - if ((col >= clipx) && (col <= clipxmax)) { - packcell = get_tile(glyph2tile[glyphnum]); + if ((col >= clipx) && (col <= clipxmax) + && (ry >= clipy) && (ry <= clipymax)) { +#if 0 if (!iflags.over_view && map[ry][col].special) decal_packed(packcell, special); - vesa_DisplayCell(packcell, col - clipx, row); +#endif + vesa_DisplayCell(glyph2tile[glyphnum], col - clipx, ry - clipy); } } if (col < (CO - 1)) @@ -686,19 +754,36 @@ static void vesa_cliparound(x, y) int x, y; { - int oldx = clipx; + int oldx = clipx, oldy = clipy; if (!iflags.tile_view || iflags.over_view || iflags.traditional_view) return; - if (x < clipx + 5) { - clipx = max(0, x - (viewport_size / 2)); - clipxmax = clipx + (viewport_size - 1); - } else if (x > clipxmax - 5) { - clipxmax = min(COLNO - 1, x + (viewport_size / 2)); - clipx = clipxmax - (viewport_size - 1); + if (viewport_cols < COLNO) { + if (x < clipx + 5) { + clipx = max(0, x - (viewport_cols / 2)); + clipxmax = clipx + (viewport_cols - 1); + } else if (x > clipxmax - 5) { + clipxmax = min(COLNO - 1, x + (viewport_cols / 2)); + clipx = clipxmax - (viewport_cols - 1); + } + } else { + clipx = 0; + clipxmax = COLNO - 1; } - if (clipx != oldx) { + if (viewport_rows < ROWNO) { + if (y < clipy + 5) { + clipy = max(0, y - (viewport_rows / 2)); + clipymax = clipy + (viewport_rows - 1); + } else if (y > clipymax - 5) { + clipymax = min(ROWNO, y + (viewport_rows / 2)); + clipy = clipymax - (viewport_rows - 1); + } + } else { + clipy = 0; + clipymax = ROWNO - 1; + } + if (clipx != oldx || clipy != oldy) { if (on_level(&u.uz0, &u.uz) && !g.restoring) /* (void) doredraw(); */ vesa_redrawmap(); @@ -708,70 +793,139 @@ int x, y; static void vesa_redrawmap() { - int x, y, t; - const struct TileImage *packcell; + unsigned y_top = TOP_MAP_ROW * vesa_char_height; + unsigned y_bottom = vesa_y_res - 5 * vesa_char_height; + unsigned x, y, cx, cy, px, py; + unsigned long color; + unsigned long offset = y_top * (unsigned long) vesa_scan_line; + unsigned char *p_row = NULL; + unsigned p_row_width; - /* y here is in screen rows*/ - /* Build each row in local memory, then write, to minimize use of the - window switch function */ - for (y = 0; y < ROWNO; ++y) { - char buf[TILE_Y][640*2]; + /* + * The map is drawn in pixel-row order (pixel row 0, then row 1, etc.), + * rather than cell by cell, to minimize calls to vesa_SetWindow. + */ + if (iflags.traditional_view) { + /* Text mode */ + y = y_top; + for (cy = clipy; cy <= clipymax && cy < ROWNO; ++cy) { + struct VesaCharacter t_row[COLNO]; + for (cx = clipx; cx <= clipxmax && cx < COLNO; ++cx) { + t_row[cx].chr = map[cy][cx].ch; + t_row[cx].colour = map[cy][cx].attr; + } + vesa_WriteTextRow(0, y, t_row + clipx, cx - clipx); + x = (cx - clipx) * vesa_char_width; + if (x < vesa_x_res) { + vesa_FillRect(x, y, vesa_x_res - x, vesa_char_height, BACKGROUND_VESA_COLOR); + } + y += vesa_char_height; + } + } else if (iflags.over_view) { + /* Overview mode */ + const unsigned char *tile; - for (x = clipx; x <= clipxmax; ++x) { - if (iflags.traditional_view) { - vesa_WriteCharInMemory((unsigned char) map[y][x].ch, x, - buf, map[y][x].attr); - } else { - t = map[y][x].glyph; - packcell = get_tile(glyph2tile[t]); - if (!iflags.over_view && map[y][x].special) - decal_packed(packcell, map[y][x].special); - vesa_DisplayCellInMemory(packcell, x - clipx, buf); + p_row_width = vesa_oview_width * vesa_pixel_bytes; + y = y_top; + for (cy = 0; cy < ROWNO; ++cy) { + for (py = 0; py < vesa_oview_height; ++py) { + for (cx = 0; cx < COLNO; ++cx) { + tile = vesa_oview_tiles[glyph2tile[map[cy][cx].glyph]]; + vesa_WritePixelRow(offset + p_row_width * cx, tile + p_row_width * py, p_row_width); + } + x = COLNO * vesa_oview_width; + if (x < vesa_x_res) { + vesa_FillRect(x, y, vesa_x_res - x, 1, BACKGROUND_VESA_COLOR); + } + offset += vesa_scan_line; + ++y; } } - if (iflags.over_view && vesa_pixel_size != 8) { - for (t = 0; t < TILE_Y; ++t) { - for (x = 0; x < 640; ++x) { - unsigned long c1 = vesa_palette[buf[t][x * 2 + 0]]; - unsigned long c2 = vesa_palette[buf[t][x * 2 + 1]]; - unsigned char r1, r2, g1, g2, b1, b2; + } else { + /* Normal tiled mode */ + const unsigned char *tile; - vesa_GetRGB(c1, &r1, &g1, &b1); - vesa_GetRGB(c2, &r2, &g2, &b2); - r1 = (r1 + r2) / 2; - g1 = (g1 + g2) / 2; - b1 = (b1 + b2) / 2; - vesa_WritePixel32(x, (y + TOP_MAP_ROW) * TILE_Y + t, - vesa_MakeColor(r1, g1, b1)); + p_row_width = iflags.wc_tile_width * vesa_pixel_bytes; + y = y_top; + for (cy = clipy; cy <= clipymax && cy < ROWNO; ++cy) { + for (py = 0; py < iflags.wc_tile_height; ++py) { + for (cx = clipx; cx <= clipxmax && cx < COLNO; ++cx) { + tile = vesa_tiles[glyph2tile[map[cy][cx].glyph]]; + vesa_WritePixelRow(offset + p_row_width * (cx - clipx), tile + p_row_width * py, p_row_width); } - } - } else { - for (t = 0; t < TILE_Y; ++t) { - for (x = 0; x < 640; ++x) { - vesa_WritePixel(x, (y + TOP_MAP_ROW) * TILE_Y + t, buf[t][x]); + x = (cx - clipx) * iflags.wc_tile_width; + if (x < vesa_x_res) { + vesa_FillRect(x, y, vesa_x_res - x, 1, BACKGROUND_VESA_COLOR); } + offset += vesa_scan_line; + ++y; } } } + /* Loops leave y as the start of any remaining unfilled space */ + if (y < y_bottom) { + vesa_FillRect(0, y, vesa_x_res, y_bottom - y, BACKGROUND_VESA_COLOR); + } + + free(p_row); } #endif /* USE_TILES && CLIPPING */ void -vesa_userpan(left) -boolean left; +vesa_userpan(pan) +enum vga_pan_direction pan; { - int x; - /* pline("Into userpan"); */ if (iflags.over_view || iflags.traditional_view) return; - if (left) - x = min(COLNO - 1, clipxmax + 10); - else - x = max(0, clipx - 10); - vesa_cliparound(x, 10); /* y value is irrelevant on VGA clipping */ - positionbar(); - vesa_DrawCursor(); + + switch (pan) { + case pan_left: + if (viewport_cols < COLNO) { + clipxmax = clipx - 1; + clipx = clipxmax - (viewport_cols - 1); + if (clipx < 0) { + clipx = 0; + clipxmax = viewport_cols - 1; + } + } + break; + + case pan_right: + if (viewport_cols < COLNO) { + clipx = clipxmax + 1; + clipxmax = clipx + (viewport_cols - 1); + if (clipxmax > COLNO - 1) { + clipxmax = COLNO - 1; + clipx = clipxmax - (viewport_cols - 1); + } + } + break; + + case pan_up: + if (viewport_rows < ROWNO) { + clipymax = clipy - 1; + clipy = clipymax - (viewport_rows - 1); + if (clipy < 0) { + clipy = 0; + clipymax = viewport_rows - 1; + } + } + break; + + case pan_down: + if (viewport_rows < ROWNO) { + clipy = clipymax + 1; + clipymax = clipy + (viewport_rows - 1); + if (clipymax > ROWNO - 1) { + clipymax = ROWNO - 1; + clipy = clipymax - (viewport_rows - 1); + } + } + break; + } + + vesa_refresh(); } void @@ -782,13 +936,29 @@ boolean on; if (on) { iflags.over_view = TRUE; clipx = 0; - clipxmax = CO - 1; + clipxmax = COLNO - 1; + clipy = 0; + clipymax = ROWNO - 1; } else { iflags.over_view = FALSE; - clipx = max(0, (curcol - viewport_size / 2)); - if (clipx > ((CO - 1) - viewport_size)) - clipx = (CO - 1) - viewport_size; - clipxmax = clipx + (viewport_size - 1); + if (viewport_cols < COLNO) { + clipx = max(0, (curcol - viewport_cols / 2)); + if (clipx > ((COLNO - 1) - viewport_cols)) + clipx = (COLNO - 1) - viewport_cols; + clipxmax = clipx + (viewport_cols - 1); + } else { + clipx = 0; + clipxmax = COLNO - 1; + } + if (viewport_rows < ROWNO) { + clipy = max(0, (currow - viewport_rows / 2)); + if (clipy > ((ROWNO - 1) - viewport_rows)) + clipy = (ROWNO - 1) - viewport_rows; + clipymax = clipy + (viewport_rows - 1); + } else { + clipy = 0; + clipymax = ROWNO - 1; + } } } @@ -801,14 +971,30 @@ boolean on; /* switch_symbols(FALSE); */ iflags.traditional_view = TRUE; clipx = 0; - clipxmax = CO - 1; + clipxmax = COLNO - 1; + clipy = 0; + clipymax = ROWNO - 1; } else { iflags.traditional_view = FALSE; if (!iflags.over_view) { - clipx = max(0, (curcol - viewport_size / 2)); - if (clipx > ((CO - 1) - viewport_size)) - clipx = (CO - 1) - viewport_size; - clipxmax = clipx + (viewport_size - 1); + if (viewport_cols < COLNO) { + clipx = max(0, (curcol - viewport_cols / 2)); + if (clipx > ((COLNO - 1) - viewport_cols)) + clipx = (COLNO - 1) - viewport_cols; + clipxmax = clipx + (viewport_cols - 1); + } else { + clipx = 0; + clipxmax = COLNO - 1; + } + if (viewport_rows < ROWNO) { + clipy = max(0, (currow - viewport_rows / 2)); + if (clipy > ((ROWNO - 1) - viewport_rows)) + clipy = (ROWNO - 1) - viewport_rows; + clipymax = clipy + (viewport_rows - 1); + } else { + clipy = 0; + clipymax = ROWNO - 1; + } } } } @@ -821,6 +1007,7 @@ vesa_refresh() vesa_DrawCursor(); } +#if 0 static void decal_packed(gp, special) const struct TileImage *gp; @@ -835,6 +1022,7 @@ unsigned special; } else if (special & MG_RIDDEN) { } } +#endif /* * Open tile files, @@ -846,10 +1034,17 @@ unsigned special; void vesa_Init(void) { + static boolean inited = FALSE; const struct Pixel *paletteptr; -#ifdef USE_TILES + unsigned i; + unsigned num_pixels, num_oview_pixels; const char *tile_file; int tilefailure = 0; + + if (inited) return; + inited = TRUE; + +#ifdef USE_TILES /* * Attempt to open the required tile files. If we can't * don't perform the video mode switch, use TTY code instead. @@ -859,9 +1054,9 @@ vesa_Init(void) if (tile_file == NULL || *tile_file == '\0') { tile_file = "nhtiles.bmp"; } - if (!read_tiles(tile_file, FALSE)) + if (!read_tiles(tile_file, vesa_pixel_size > 8)) tilefailure |= 1; - if (get_palette() == NULL) + if (vesa_pixel_size == 8 && get_palette() == NULL) tilefailure |= 4; if (tilefailure) { @@ -892,7 +1087,9 @@ vesa_Init(void) /* clear_screen() */ /* not vesa_clear_screen() */ return; } + vesa_SwitchMode(vesa_mode); + vesa_SetViewPort(); windowprocs.win_cliparound = vesa_cliparound; #ifdef USE_TILES paletteptr = get_palette(); @@ -906,7 +1103,90 @@ vesa_Init(void) font = vesa_FontPtrs(); clear_screen(); clipx = 0; - clipxmax = clipx + (viewport_size - 1); + clipxmax = clipx + (viewport_cols - 1); + clipy = 0; + clipymax = clipy + (viewport_rows - 1); + + /* Set the size of the tiles for the overview mode */ + vesa_oview_width = vesa_x_res / COLNO; + if (vesa_oview_width > iflags.wc_tile_width) { + vesa_oview_width = iflags.wc_tile_width; + } + vesa_oview_height = (vesa_y_res - (TOP_MAP_ROW + 4) * vesa_char_height) + / ROWNO; + if (vesa_oview_height > iflags.wc_tile_height) { + vesa_oview_height = iflags.wc_tile_height; + } + + /* Process tiles for the current video mode */ + vesa_tiles = (unsigned char **) alloc(total_tiles_used * sizeof(void *)); + vesa_oview_tiles = (unsigned char **) alloc(total_tiles_used * sizeof(void *)); + num_pixels = iflags.wc_tile_width * iflags.wc_tile_height; + num_oview_pixels = vesa_oview_width * vesa_oview_height; + set_tile_type(vesa_pixel_size > 8); + for (i = 0; i < total_tiles_used; ++i) { + const struct TileImage *tile = get_tile(i); + struct TileImage *ov_tile = stretch_tile(tile, vesa_oview_width, vesa_oview_height); + unsigned j; + unsigned char *t_img = (unsigned char *) alloc(num_pixels * vesa_pixel_bytes); + unsigned char *ot_img = (unsigned char *) alloc(num_oview_pixels * vesa_pixel_bytes); + vesa_tiles[i] = t_img; + vesa_oview_tiles[i] = ot_img; + switch (vesa_pixel_bytes) { + case 1: + memcpy(t_img, tile->indexes, num_pixels); + memcpy(ot_img, ov_tile->indexes, num_oview_pixels); + break; + + case 2: + for (j = 0; j < num_pixels; ++j) { + ((uint16_t *)t_img)[j] = vesa_MakeColor(tile->pixels[j]); + } + for (j = 0; j < num_oview_pixels; ++j) { + ((uint16_t *)ot_img)[j] = vesa_MakeColor(ov_tile->pixels[j]); + } + break; + + case 3: + for (j = 0; j < num_pixels; ++j) { + unsigned long color = vesa_MakeColor(tile->pixels[j]); + t_img[3*j + 0] = color & 0xFF; + t_img[3*j + 1] = (color >> 8) & 0xFF; + t_img[3*j + 2] = (color >> 16) & 0xFF; + } + for (j = 0; j < num_oview_pixels; ++j) { + unsigned long color = vesa_MakeColor(ov_tile->pixels[j]); + ot_img[3*j + 0] = color & 0xFF; + ot_img[3*j + 1] = (color >> 8) & 0xFF; + ot_img[3*j + 2] = (color >> 16) & 0xFF; + } + break; + + case 4: + for (j = 0; j < num_pixels; ++j) { + ((uint32_t *)t_img)[j] = vesa_MakeColor(tile->pixels[j]); + } + for (j = 0; j < num_oview_pixels; ++j) { + ((uint32_t *)ot_img)[j] = vesa_MakeColor(ov_tile->pixels[j]); + } + break; + } + free_tile(ov_tile); + } + free_tiles(); +} + +/* Set the size of the map viewport */ +static void +vesa_SetViewPort() +{ + unsigned y_reserved = (TOP_MAP_ROW + 5) * vesa_char_height; + unsigned y_map = vesa_y_res - y_reserved; + + viewport_cols = vesa_x_res / iflags.wc_tile_width; + viewport_rows = y_map / iflags.wc_tile_height; + if (viewport_cols > COLNO) viewport_cols = COLNO; + if (viewport_rows > ROWNO) viewport_rows = ROWNO; } /* @@ -928,6 +1208,10 @@ unsigned mode; memset(®s, 0, sizeof(regs)); regs.x.ax = mode; (void) __dpmi_int(VIDEO_BIOS, ®s); +#ifdef SIMULATE_CURSOR + free(undercursor); + undercursor = NULL; +#endif } else if (mode >= 0x100) { iflags.grmode = 1; memset(®s, 0, sizeof(regs)); @@ -955,7 +1239,14 @@ unsigned mode; void vesa_Finish(void) { - free_tiles(); + int i; + + for (i = 0; i < total_tiles_used; ++i) { + free(vesa_tiles[i]); + free(vesa_oview_tiles[i]); + } + free(vesa_tiles); + free(vesa_oview_tiles); vesa_SwitchMode(MODETEXT); windowprocs.win_cliparound = tty_cliparound; g_attribute = attrib_text_normal; @@ -973,18 +1264,18 @@ vesa_Finish(void) * address of the appropriate character definition table for * the current graphics mode into interrupt vector 0x43 (0000:010C). */ -static char __far * +static unsigned char __far * vesa_FontPtrs(void) { USHORT __far *tmp; - char __far *retval; + unsigned char __far *retval; USHORT fseg, foff; tmp = (USHORT __far *) MK_PTR(((USHORT) FONT_PTR_SEGMENT), ((USHORT) FONT_PTR_OFFSET)); foff = READ_ABSOLUTE_WORD(tmp); ++tmp; fseg = READ_ABSOLUTE_WORD(tmp); - retval = (char __far *) MK_PTR(fseg, foff); + retval = (unsigned char __far *) MK_PTR(fseg, foff); return retval; } @@ -1002,6 +1293,7 @@ vesa_detect() __dpmi_regs regs; unsigned long mode_addr; struct ModeInfoBlock mode_info; + const char *mode_str; vbe_info_seg = __dpmi_allocate_dos_memory( (sizeof(vbe_info) + 15) / 16, @@ -1032,8 +1324,26 @@ vesa_detect() mode_addr = (vbe_info.VideoModePtr >> 16) * 16L + (vbe_info.VideoModePtr & 0xFFFF); + /* Allow the user to select a specific mode */ + mode_str = getenv("NH_DISPLAY_MODE"); + if (mode_str != NULL) { + char *end; + unsigned long num = strtoul(mode_str, &end, 16); + if (*end == '\0') { + /* Can we select this mode? */ + if (vesa_GetModeInfo(num, &mode_info) + && mode_info.XResolution >= 640 + && mode_info.YResolution >= 480 + && mode_info.BitsPerPixel >= 8) { + vesa_mode = num & 0x47FF; + } + } + if (vesa_mode == 0xFFFF) + mode_str = NULL; + } + /* Scan the mode list for an acceptable mode */ - if (get_palette() != NULL) + if (get_palette() != NULL && vesa_mode == 0xFFFF) vesa_mode = vesa_FindMode(mode_addr, 8); if (vesa_mode == 0xFFFF) vesa_mode = vesa_FindMode(mode_addr, 32); @@ -1052,8 +1362,8 @@ vesa_detect() vesa_GetModeInfo(vesa_mode, &mode_info); vesa_x_res = mode_info.XResolution; vesa_y_res = mode_info.YResolution; - vesa_x_center = (vesa_x_res - 640) / 2; - vesa_y_center = (vesa_y_res - 480) / 2; + vesa_x_center = 0; + vesa_y_center = 0; vesa_scan_line = mode_info.BytesPerScanLine; vesa_win_size = mode_info.WinSize * 1024L; vesa_win_gran = mode_info.WinGranularity * 1024L; @@ -1062,47 +1372,47 @@ vesa_detect() if (vbe_info.VbeVersion >= 0x0300) { if (mode_info.ModeAttributes & 0x80) { vesa_red_pos = mode_info.LinRedFieldPosition; - vesa_red_size = mode_info.LinRedMaskSize; + vesa_red_shift = 8 - mode_info.LinRedMaskSize; vesa_green_pos = mode_info.LinGreenFieldPosition; - vesa_green_size = mode_info.LinGreenMaskSize; + vesa_green_shift = 8 - mode_info.LinGreenMaskSize; vesa_blue_pos = mode_info.LinBlueFieldPosition; - vesa_blue_size = mode_info.LinBlueMaskSize; + vesa_blue_shift = 8 - mode_info.LinBlueMaskSize; } else { vesa_red_pos = mode_info.RedFieldPosition; - vesa_red_size = mode_info.RedMaskSize; + vesa_red_shift = 8 - mode_info.RedMaskSize; vesa_green_pos = mode_info.GreenFieldPosition; - vesa_green_size = mode_info.GreenMaskSize; + vesa_green_shift = 8 - mode_info.GreenMaskSize; vesa_blue_pos = mode_info.BlueFieldPosition; - vesa_blue_size = mode_info.BlueMaskSize; + vesa_blue_shift = 8 - mode_info.BlueMaskSize; } } else { switch (vesa_pixel_size) { case 15: vesa_blue_pos = 0; - vesa_blue_size = 5; + vesa_blue_shift = 3; vesa_green_pos = 5; - vesa_green_size = 5; + vesa_green_shift = 3; vesa_red_pos = 10; - vesa_red_size = 5; + vesa_red_shift = 3; break; case 16: vesa_blue_pos = 0; - vesa_blue_size = 5; + vesa_blue_shift = 3; vesa_green_pos = 5; - vesa_green_size = 6; + vesa_green_shift = 2; vesa_red_pos = 11; - vesa_red_size = 5; + vesa_red_shift = 3; break; case 24: case 32: vesa_blue_pos = 0; - vesa_blue_size = 8; + vesa_blue_shift = 0; vesa_green_pos = 8; - vesa_green_size = 8; + vesa_green_shift = 0; vesa_red_pos = 16; - vesa_red_size = 8; + vesa_red_shift = 0; break; } } @@ -1129,7 +1439,8 @@ vesa_detect() } /* Configure a linear frame buffer if we have it */ - if ((mode_info.ModeAttributes & 0x80) != 0) { + if ((mode_info.ModeAttributes & 0x80) != 0 + && (mode_str == NULL || (vesa_mode & 0x4000) != 0)) { unsigned sel = vesa_segment; unsigned win_size = mode_info.BytesPerScanLine * mode_info.YResolution; unsigned addr = vesa_map_frame_buffer(mode_info.PhysBasePtr, win_size); @@ -1164,6 +1475,13 @@ unsigned bits; struct ModeInfoBlock mode_info0, mode_info; unsigned model = (bits == 8) ? 4 : 6; + if (iflags.wc_video_width < 640) { + iflags.wc_video_width = 640; + } + if (iflags.wc_video_height < 480) { + iflags.wc_video_height = 480; + } + memset(&mode_info, 0, sizeof(mode_info)); selected_mode = 0xFFFF; while (1) { @@ -1175,8 +1493,8 @@ unsigned bits; if (!vesa_GetModeInfo(mode, &mode_info0)) continue; /* Check that the mode is acceptable */ - if (mode_info0.XResolution < 640) continue; - if (mode_info0.YResolution < 480) continue; + if (mode_info0.XResolution < iflags.wc_video_width) continue; + if (mode_info0.YResolution < iflags.wc_video_height) continue; if (mode_info0.NumberOfPlanes != 1) continue; if (mode_info0.BitsPerPixel != bits) continue; if (mode_info0.NumberOfBanks != 1) continue; @@ -1202,66 +1520,182 @@ unsigned bits; * */ static void -vesa_WriteChar(chr, col, row, colour, transparent) +vesa_WriteChar(chr, col, row, colour) int chr, col, row, colour; -boolean transparent; { - int i, j; int pixx, pixy; - unsigned char __far *fp = font; - unsigned char fnt; + /* min() protects from callers */ + pixx = min(col, (CO - 1)) * vesa_char_width; + pixy = min(row, (LI - 1)) * vesa_char_height; - pixx = min(col, (CO - 1)) * 8; /* min() protects from callers */ - pixy = min(row, (LI - 1)) * 16; /* assumes 8 x 16 char set */ pixx += vesa_x_center; pixy += vesa_y_center; - for (i = 0; i < MAX_ROWS_PER_CELL; ++i) { - fnt = READ_ABSOLUTE((fp + chr * 16 + i)); - for (j = 0; j < 8; ++j) { - if (fnt & (0x80 >> j)) { - vesa_WritePixel(pixx + j, pixy + i, colour + FIRST_TEXT_COLOR); - } else if (!transparent) { - vesa_WritePixel(pixx + j, pixy + i, BACKGROUND_VESA_COLOR); + vesa_WriteCharXY(chr, pixx, pixy, colour); +} + +/* + * As vesa_WriteChar, but specify coordinates by pixel and allow + * transparency + */ +static void +vesa_WriteCharXY(chr, pixx, pixy, colour) +int chr, pixx, pixy, colour; +{ + /* Flush if cache is full or if not contiguous to the last character */ + if (chr_cache_size >= SIZE(chr_cache)) { + vesa_flush_text(); + } + if (chr_cache_size != 0 && chr_cache_lastx + vesa_char_width != pixx) { + vesa_flush_text(); + } + if (chr_cache_size != 0 && chr_cache_pixy != pixy) { + vesa_flush_text(); + } + /* Add to cache and write later */ + if (chr_cache_size == 0) { + chr_cache_pixx = pixx; + chr_cache_pixy = pixy; + } + chr_cache_lastx = pixx; + chr_cache[chr_cache_size].chr = chr; + chr_cache[chr_cache_size].colour = colour; + ++chr_cache_size; +} + +/* + * Draw a character with a transparent background + * Don't bother cacheing; only the position bar and the cursor use this + */ +static void +vesa_WriteCharTransparent(chr, pixx, pixy, colour) +int chr, pixx, pixy, colour; +{ + int px, py; + + for (py = 0; py < vesa_char_height; ++py) { + for (px = 0; px < vesa_char_width; ++px) { + if (vesa_GetCharPixel(chr, px, py)) { + vesa_WritePixel(pixx + px, pixy + py, colour + FIRST_TEXT_COLOR); } } } } -/* - * Like vesa_WriteChar, but draw the character in local memory rather than in - * the VGA frame buffer. - * - * vesa_redrawmap uses this to gather a row of cells in local memory and then - * draw them in strict row-major order, minimizing the use of the VESA - * windowing function. - * - */ -static void -vesa_WriteCharInMemory(chr, col, buf, colour) -int chr, col; -char buf[TILE_Y][640*2]; -int colour; +void +vesa_flush_text() { - int i, j; - int pixx; + if (chr_cache_size == 0) return; - unsigned char __far *fp = font; - unsigned char fnt; + vesa_WriteTextRow(chr_cache_pixx, chr_cache_pixy, chr_cache, chr_cache_size); + chr_cache_size = 0; +} - pixx = min(col, (CO - 1)) * 8; /* min() protects from callers */ +static void +vesa_WriteTextRow(pixx, pixy, t_row, t_row_width) +int pixx, pixy; +struct VesaCharacter const *t_row; +unsigned t_row_width; +{ + int x, px, py; + unsigned i; + unsigned p_row_width = t_row_width * vesa_char_width * vesa_pixel_bytes; + unsigned char *p_row = (unsigned char *) alloc(p_row_width); + unsigned long offset = pixy * (unsigned long)vesa_scan_line + pixx * vesa_pixel_bytes; + unsigned char fg[4], bg[4]; - for (i = 0; i < MAX_ROWS_PER_CELL; ++i) { - fnt = READ_ABSOLUTE((fp + chr * 16 + i)); - for (j = 0; j < 8; ++j) { - if (fnt & (0x80 >> j)) { - buf[i][pixx + j] = colour + FIRST_TEXT_COLOR; + /* Preprocess the background color */ + if (vesa_pixel_bytes == 1) { + bg[0] = BACKGROUND_VESA_COLOR; + } else { + unsigned long pix = vesa_palette[BACKGROUND_VESA_COLOR]; + bg[0] = pix & 0xFF; + bg[1] = (pix >> 8) & 0xFF; + bg[2] = (pix >> 16) & 0xFF; + bg[3] = (pix >> 24) & 0xFF; + } + + /* First loop: draw one raster line of all row entries */ + for (py = 0; py < vesa_char_height; ++py) { + /* Second loop: draw one raster line of one character */ + x = 0; + for (i = 0; i < t_row_width; ++i) { + int chr = t_row[i].chr; + int colour = t_row[i].colour + FIRST_TEXT_COLOR; + /* Preprocess the foreground color */ + if (vesa_pixel_bytes == 1) { + fg[0] = colour; } else { - buf[i][pixx + j] = BACKGROUND_VESA_COLOR; + unsigned long pix = vesa_palette[colour]; + fg[0] = pix & 0xFF; + fg[1] = (pix >> 8) & 0xFF; + fg[2] = (pix >> 16) & 0xFF; + fg[3] = (pix >> 24) & 0xFF; + } + /* Third loop: draw eight pixels */ + for (px = 0; px < vesa_char_width; px += 8) { + /* Fourth loop: draw one pixel */ + int px2; + unsigned char fnt = vesa_GetCharPixelRow(chr, px, py); + int l = vesa_char_width - px; + if (l > 8) { + l = 8; + } + for (px2 = 0; px2 < l; ++px2) { + if (fnt & 0x80) { + memcpy(p_row + x, fg, vesa_pixel_bytes); + } else { + memcpy(p_row + x, bg, vesa_pixel_bytes); + } + x += vesa_pixel_bytes; + fnt <<= 1; + } } } + vesa_WritePixelRow(offset, p_row, p_row_width); + offset += vesa_scan_line; } + free(p_row); +} + +static boolean +vesa_GetCharPixel(ch, x, y) +int ch; +unsigned x, y; +{ + unsigned x2; + unsigned char fnt; + + x2 = x % 8; + + fnt = vesa_GetCharPixelRow(ch, x, y); + return (fnt & (0x80 >> x2)) != 0; +} + +static unsigned char +vesa_GetCharPixelRow(ch, x, y) +int ch; +unsigned x, y; +{ + unsigned fnt_width; + unsigned x1; + unsigned char fnt; + size_t offset; + + if (x >= vesa_char_width) return FALSE; + if (y >= vesa_char_height) return FALSE; + + fnt_width = (vesa_char_width + 7) / 8; + x1 = x / 8; + + const unsigned char __far *fp; + + if (ch < 0 || 255 < ch) return FALSE; + offset = (ch * vesa_char_height + y) * fnt_width + x1; + fp = font; + fnt = READ_ABSOLUTE((fp + offset)); + return fnt; } /* @@ -1269,93 +1703,45 @@ int colour; * at the desired location (col,row). * * Note: (col,row) in this case refer to the coordinate location in - * NetHack character grid terms, (ie. the 40 x 25 character grid), + * NetHack character grid terms, relative to the map viewport, * not the x,y pixel location. * */ static void -vesa_DisplayCell(tile, col, row) -const struct TileImage *tile; +vesa_DisplayCell(tilenum, col, row) +int tilenum; int col, row; { - int i, j, pixx, pixy; + unsigned char const *tile; + unsigned t_width, t_height; + unsigned char const *tptr; + int px, py, pixx, pixy; + unsigned long offset; + unsigned p_row_width; - pixx = col * TILE_X; - pixy = row * TILE_Y; if (iflags.over_view) { - pixx /= 2; - pixx += vesa_x_center; - pixy += vesa_y_center; - if (vesa_pixel_size != 8) { - for (i = 0; i < TILE_Y; ++i) { - for (j = 0; j < TILE_X; j += 2) { - unsigned index = i * tile->width + j; - unsigned long c1 = vesa_palette[tile->indexes[index + 0]]; - unsigned long c2 = vesa_palette[tile->indexes[index + 1]]; - unsigned char r1, r2, g1, g2, b1, b2; - - vesa_GetRGB(c1, &r1, &g1, &b1); - vesa_GetRGB(c2, &r2, &g2, &b2); - r1 = (r1 + r2) / 2; - g1 = (g1 + g2) / 2; - b1 = (b1 + b2) / 2; - vesa_WritePixel32(pixx + j / 2, pixy + i, - vesa_MakeColor(r1, g1, b1)); - } - } - } else { - for (i = 0; i < TILE_Y; ++i) { - for (j = 0; j < TILE_X; j += 2) { - unsigned index = i * tile->width + j; - vesa_WritePixel(pixx + j / 2, pixy + i, tile->indexes[index]); - } - } - } + tile = vesa_oview_tiles[tilenum]; + t_width = vesa_oview_width; + t_height = vesa_oview_height; } else { - pixx += vesa_x_center; - pixy += vesa_y_center; - for (i = 0; i < TILE_Y; ++i) { - for (j = 0; j < TILE_X; ++j) { - unsigned index = i * tile->width + j; - vesa_WritePixel(pixx + j, pixy + i, tile->indexes[index]); - } - } + tile = vesa_tiles[tilenum]; + t_width = iflags.wc_tile_width; + t_height = iflags.wc_tile_height; } -} -/* - * Like vesa_DisplayCell, but draw the tile in local memory rather than in - * the VGA frame buffer. - * - * vesa_redrawmap uses this to gather a row of cells in local memory and then - * draw them in strict row-major order, minimizing the use of the VESA - * windowing function. - * - */ -static void -vesa_DisplayCellInMemory(tile, col, buf) -const struct TileImage *tile; -int col; -char buf[TILE_Y][640*2]; -{ - int i, j, pixx; + p_row_width = t_width * vesa_pixel_bytes; - pixx = col * TILE_X; - if (iflags.over_view && vesa_pixel_size == 8) { - pixx /= 2; - for (i = 0; i < TILE_Y; ++i) { - for (j = 0; j < TILE_X; j += 2) { - unsigned index = i * tile->width + j; - buf[i][pixx + j / 2] = tile->indexes[index]; - } - } - } else { - for (i = 0; i < TILE_Y; ++i) { - for (j = 0; j < TILE_X; ++j) { - unsigned index = i * tile->width + j; - buf[i][pixx + j] = tile->indexes[index]; - } - } + pixx = col * t_width; + pixy = row * t_height + TOP_MAP_ROW * vesa_char_height; + pixx += vesa_x_center; + pixy += vesa_y_center; + offset = pixy * (unsigned long)vesa_scan_line + pixx * vesa_pixel_bytes; + tptr = tile; + + for (py = 0; py < t_height; ++py) { + vesa_WritePixelRow(offset, tptr, p_row_width); + offset += vesa_scan_line; + tptr += p_row_width; } } @@ -1379,7 +1765,7 @@ int len, col, row, colour; i = 0; us = (const unsigned char *) s; while ((*us != 0) && (i < len) && (col < (CO - 1))) { - vesa_WriteChar(*us, col, row, colour, FALSE); + vesa_WriteChar(*us, col, row, colour); ++us; ++i; ++col; @@ -1389,8 +1775,8 @@ int len, col, row, colour; /* * Initialize the VGA palette with the desired colours. This * must be a series of 720 bytes for use with a card in 256 - * colour mode at 640 x 480. The first 240 palette entries are - * used by the tile set; the last 16 are reserved for text. + * colour mode. The first 240 palette entries are used by the + * tile set; the last 16 are reserved for text. * */ static boolean @@ -1398,9 +1784,9 @@ vesa_SetPalette(palette) const struct Pixel *palette; { if (vesa_pixel_size == 8) { - vesa_SetHardPalette(palette); + return vesa_SetHardPalette(palette); } else { - vesa_SetSoftPalette(palette); + return vesa_SetSoftPalette(palette); } } @@ -1498,33 +1884,29 @@ const struct Pixel *palette; { const struct Pixel *p; unsigned i; - unsigned char r, g, b; /* Set the tile set and text colors */ #ifdef USE_TILES - p = palette; - for (i = 0; i < FIRST_TEXT_COLOR; ++i) { - r = p->r; - g = p->g; - b = p->b; - vesa_palette[i] = vesa_MakeColor(r, g, b); - ++p; + if (palette != NULL) { + p = palette; + for (i = 0; i < FIRST_TEXT_COLOR; ++i) { + vesa_palette[i] = vesa_MakeColor(*p); + ++p; + } } #endif p = defpalette; for (i = FIRST_TEXT_COLOR; i < 256; ++i) { - r = p->r; - g = p->g; - b = p->b; - vesa_palette[i] = vesa_MakeColor(r, g, b); + vesa_palette[i] = vesa_MakeColor(*p); ++p; } + return TRUE; } #ifdef POSITIONBAR #define PBAR_ROW (LI - 4) -#define PBAR_COLOR_ON 16 /* slate grey background colour of tiles */ +#define PBAR_COLOR_ON 6 /* slate grey background colour of tiles */ #define PBAR_COLOR_OFF 0 /* bluish grey, used in old style only */ #define PBAR_COLOR_STAIRS CLR_BROWN /* brown */ #define PBAR_COLOR_HERO CLR_WHITE /* creamy white */ @@ -1535,7 +1917,7 @@ void vesa_update_positionbar(posbar) char *posbar; { - char *p = pbar; + unsigned char *p = pbar; if (posbar) while (*posbar) *p++ = *posbar++; @@ -1545,13 +1927,13 @@ char *posbar; static void positionbar() { - char *posbar = pbar; + unsigned char *posbar = pbar; int feature, ucol; - int k, x, y, colour, row; int startk, stopk; boolean nowhere = FALSE; - int pixy = (PBAR_ROW * MAX_ROWS_PER_CELL); + int pixx, col; + int pixy = (PBAR_ROW * vesa_char_height); int tmp; if (!iflags.grmode || !iflags.tile_view) @@ -1564,53 +1946,45 @@ positionbar() #endif return; } + startk = clipx * vesa_x_res / COLNO; + stopk = (clipxmax + 1) * vesa_x_res / COLNO; #ifdef OLD_STYLE - for (y = pixy; y < (pixy + MAX_ROWS_PER_CELL); ++y) { - for (x = 0; x < 640; ++x) { - k = x / 8; - if ((k < clipx) || (k > clipxmax)) { - colour = PBAR_COLOR_OFF; - } else - colour = PBAR_COLOR_ON; - vesa_WritePixel(x + vesa_x_center, y + vesa_y_center, colour); - } - } + vesa_FillRect(0, pixy, startk, vesa_char_height, + PBAR_COLOR_OFF + FIRST_TEXT_COLOR); + vesa_FillRect(startk, pixy, stopk - startk, vesa_char_height, + PBAR_COLOR_ON + FIRST_TEXT_COLOR); + vesa_FillRect(stopk, pixy, vesa_x_res - stopk, vesa_char_height, + PBAR_COLOR_OFF + FIRST_TEXT_COLOR); #else - for (y = pixy, row = 0; y < (pixy + MAX_ROWS_PER_CELL); ++y, ++row) { - if ((!row) || (row == (ROWS_PER_CELL - 1))) { - startk = 0; - stopk = SCREENBYTES; - } else { - startk = clipx; - stopk = clipxmax; - } - for (x = 0; x < 640; ++x) { - k = x / 8; - if ((k < startk) || (k > stopk)) - colour = BACKGROUND_VGA_COLOR; - else - colour = PBAR_COLOR_ON; - vesa_WritePixel(x + vesa_x_center, y + vesa_y_center, colour); - } - } + vesa_FillRect(0, pixy, vesa_x_res, 1, PBAR_COLOR_ON + FIRST_TEXT_COLOR); + vesa_FillRect(0, pixy + 1, startk, vesa_char_height - 2, + BACKGROUND_VESA_COLOR); + vesa_FillRect(startk, pixy + 1, stopk - startk, vesa_char_height - 2, + PBAR_COLOR_ON + FIRST_TEXT_COLOR); + vesa_FillRect(stopk, pixy + 1, vesa_x_res - stopk, vesa_char_height - 2, + BACKGROUND_VESA_COLOR); + vesa_FillRect(0, pixy + vesa_char_height - 1, vesa_x_res, 1, + PBAR_COLOR_ON + FIRST_TEXT_COLOR); #endif ucol = 0; if (posbar) { while (*posbar != 0) { feature = *posbar++; + col = *posbar++; + pixx = col * vesa_x_res / COLNO; switch (feature) { case '>': - vesa_WriteChar(feature, (int) *posbar++, PBAR_ROW, PBAR_COLOR_STAIRS, TRUE); + vesa_WriteCharTransparent(feature, pixx, pixy, PBAR_COLOR_STAIRS); break; case '<': - vesa_WriteChar(feature, (int) *posbar++, PBAR_ROW, PBAR_COLOR_STAIRS, TRUE); + vesa_WriteCharTransparent(feature, pixx, pixy, PBAR_COLOR_STAIRS); break; case '@': - ucol = (int) *posbar++; - vesa_WriteChar(feature, ucol, PBAR_ROW, PBAR_COLOR_HERO, TRUE); + ucol = col; + vesa_WriteCharTransparent(feature, pixx, pixy, PBAR_COLOR_HERO); break; default: /* unanticipated symbols */ - vesa_WriteChar(feature, (int) *posbar++, PBAR_ROW, PBAR_COLOR_STAIRS, TRUE); + vesa_WriteCharTransparent(feature, pixx, pixy, PBAR_COLOR_STAIRS); break; } } @@ -1619,51 +1993,73 @@ positionbar() if (inmap) { tmp = curcol + 1; if ((tmp != ucol) && (curcol >= 0)) - vesa_WriteChar('_', tmp, PBAR_ROW, PBAR_COLOR_HERO, TRUE); + vesa_WriteCharTransparent('_', tmp * vesa_x_res / COLNO, pixy, + PBAR_COLOR_HERO); } #endif + vesa_flush_text(); } #endif /*POSITIONBAR*/ #ifdef SIMULATE_CURSOR -static unsigned long undercursor[TILE_Y][TILE_X]; - void vesa_DrawCursor() { - unsigned x, y, left, top, right, bottom, width; + static boolean last_inmap = FALSE; + unsigned x, y, left, top, right, bottom, width, height; boolean isrogue = Is_rogue_level(&u.uz); boolean halfwidth = (isrogue || iflags.over_view || iflags.traditional_view || !inmap); int curtyp; + if (inmap && !last_inmap) { + vesa_redrawmap(); + } + last_inmap = inmap; + if (!cursor_type && inmap) return; /* CURSOR_INVIS - nothing to do */ + if (undercursor == NULL) { + /* size for the greater of one tile or one character */ + unsigned size1 = vesa_char_width * vesa_char_height; + unsigned size2 = iflags.wc_tile_width * iflags.wc_tile_height; + undercursor = (unsigned long *) alloc( + sizeof(undercursor[0]) * max(size1, size2)); + } x = min(curcol, (CO - 1)); /* protection from callers */ y = min(currow, (LI - 1)); /* protection from callers */ - if (!halfwidth && ((x < clipx) || (x > clipxmax))) + if (!halfwidth + && ((x < clipx) || (x > clipxmax) || (y < clipy) || (y > clipymax))) return; - if (inmap) + if (inmap) { x -= clipx; - left = x * TILE_X; /* convert to pixels */ - top = y * TILE_Y; - if (halfwidth) { - left /= 2; - width = TILE_X / 2; - } else { - width = TILE_X; + y -= clipy; + } + /* convert to pixels */ + if (!inmap || iflags.traditional_view) { + width = vesa_char_width; + height = vesa_char_height; + } else if (iflags.over_view) { + width = vesa_oview_width; + height = vesa_oview_height; + } else { + width = iflags.wc_tile_width; + height = iflags.wc_tile_height; + } + left = x * width + vesa_x_center; + top = y * height + vesa_y_center; + if (y >= TOP_MAP_ROW) { + top -= (height - vesa_char_height) * TOP_MAP_ROW; } - left += vesa_x_center; - top += vesa_y_center; right = left + width - 1; - bottom = top + TILE_Y - 1; + bottom = top + height - 1; - for (y = 0; y < ROWS_PER_CELL; ++y) { + for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { - undercursor[y][x] = vesa_ReadPixel32(left + x, top + y); + undercursor[y * width + x] = vesa_ReadPixel32(left + x, top + y); } } @@ -1725,35 +2121,45 @@ vesa_DrawCursor() void vesa_HideCursor() { - unsigned x, y, left, top, width; + unsigned x, y, left, top, width, height; boolean isrogue = Is_rogue_level(&u.uz); boolean halfwidth = (isrogue || iflags.over_view || iflags.traditional_view || !inmap); - int curtyp; if (!cursor_type && inmap) return; /* CURSOR_INVIS - nothing to do */ + if (undercursor == NULL) + return; x = min(curcol, (CO - 1)); /* protection from callers */ y = min(currow, (LI - 1)); /* protection from callers */ - if (!halfwidth && ((x < clipx) || (x > clipxmax))) + if (!halfwidth + && ((x < clipx) || (x > clipxmax) || (y < clipy) || (y > clipymax))) return; - if (inmap) + if (inmap) { x -= clipx; - left = x * TILE_X; /* convert to pixels */ - top = y * TILE_Y; - if (halfwidth) { - left /= 2; - width = TILE_X / 2; - } else { - width = TILE_X; + y -= clipy; + } + /* convert to pixels */ + if (!inmap || iflags.traditional_view) { + width = vesa_char_width; + height = vesa_char_height; + } else if (iflags.over_view) { + width = vesa_oview_width; + height = vesa_oview_height; + } else { + width = iflags.wc_tile_width; + height = iflags.wc_tile_height; + } + left = x * width + vesa_x_center; + top = y * height + vesa_y_center; + if (y >= TOP_MAP_ROW) { + top -= (height - vesa_char_height) * TOP_MAP_ROW; } - left += vesa_x_center; - top += vesa_y_center; - for (y = 0; y < ROWS_PER_CELL; ++y) { + for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { - vesa_WritePixel32(left + x, top + y, undercursor[y][x]); + vesa_WritePixel32(left + x, top + y, undercursor[y * width + x]); } } } diff --git a/sys/msdos/vidvga.c b/sys/msdos/vidvga.c index d945247d0..768f8d619 100644 --- a/sys/msdos/vidvga.c +++ b/sys/msdos/vidvga.c @@ -502,18 +502,20 @@ boolean clearfirst; #endif /* USE_TILES && CLIPPING */ void -vga_userpan(left) -boolean left; +vga_userpan(pan) +enum vga_pan_direction pan; { int x; /* pline("Into userpan"); */ if (iflags.over_view || iflags.traditional_view) return; - if (left) + if (pan == pan_left) x = min(COLNO - 1, clipxmax + 10); - else + else if (pan == pan_right) x = max(0, clipx - 10); + else + return; vga_cliparound(x, 10); /* y value is irrelevant on VGA clipping */ positionbar(); vga_DrawCursor(); From b296196a4aa1ba303e1631fea7deb5704278e6de Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 25 Jan 2020 19:55:05 -0500 Subject: [PATCH 16/20] Scale the font for large resolutions --- sys/msdos/vidvesa.c | 86 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/sys/msdos/vidvesa.c b/sys/msdos/vidvesa.c index b16b8e6d7..9294c3fb8 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -58,6 +58,8 @@ static void FDECL(vesa_WriteTextRow, (int pixx, int pixy, struct VesaCharacter const *t_row, unsigned t_row_width)); static boolean FDECL(vesa_GetCharPixel, (int, unsigned, unsigned)); static unsigned char FDECL(vesa_GetCharPixelRow, (int, unsigned, unsigned)); +static unsigned long FDECL(vesa_DoublePixels, (unsigned long)); +static unsigned long FDECL(vesa_TriplePixels, (unsigned long)); static void FDECL(vesa_WriteStr, (const char *, int, int, int, int)); static unsigned char __far *NDECL(vesa_FontPtrs); static void FDECL(vesa_process_tile, (struct TileImage *tile)); @@ -1118,6 +1120,23 @@ vesa_Init(void) vesa_oview_height = iflags.wc_tile_height; } + /* Use the map font size to set the font size */ + /* Supported sizes are 8x16, 16x32, 24x48 and 32x64 */ + vesa_char_height = iflags.wc_fontsiz_map; + if (vesa_char_height <= 0 || vesa_char_height > vesa_y_res / 30) { + vesa_char_height = vesa_y_res / 30; + } + if (vesa_char_height < 32) { + vesa_char_height = 16; + } else if (vesa_char_height < 48) { + vesa_char_height = 32; + } else if (vesa_char_height < 64) { + vesa_char_height = 48; + } else { + vesa_char_height = 64; + } + vesa_char_width = vesa_char_height / 2; + /* Process tiles for the current video mode */ vesa_tiles = (unsigned char **) alloc(total_tiles_used * sizeof(void *)); vesa_oview_tiles = (unsigned char **) alloc(total_tiles_used * sizeof(void *)); @@ -1678,26 +1697,83 @@ vesa_GetCharPixelRow(ch, x, y) int ch; unsigned x, y; { - unsigned fnt_width; unsigned x1; unsigned char fnt; size_t offset; - if (x >= vesa_char_width) return FALSE; - if (y >= vesa_char_height) return FALSE; + if (x >= vesa_char_width) return 0; + if (y >= vesa_char_height) return 0; - fnt_width = (vesa_char_width + 7) / 8; x1 = x / 8; const unsigned char __far *fp; if (ch < 0 || 255 < ch) return FALSE; - offset = (ch * vesa_char_height + y) * fnt_width + x1; + offset = ch * 16 + (y * 16 / vesa_char_height); fp = font; fnt = READ_ABSOLUTE((fp + offset)); + + if (vesa_char_width != 8) { + unsigned long fnt2 = fnt; + unsigned width = vesa_char_width; + if (width % 3 == 0) { + fnt2 = vesa_TriplePixels(fnt2); + width /= 3; + } + while (width > 8) { + fnt2 = vesa_DoublePixels(fnt2); + width /= 2; + } + fnt2 <<= 32 - vesa_char_width; + fnt = (unsigned char)(fnt2 >> (24 - 8 * x1)); + } + return fnt; } +/* Scale font pixels horizontally */ +static unsigned long +vesa_DoublePixels(fnt) +unsigned long fnt; +{ + static const unsigned char double_bits[] = { + 0x00, 0x03, 0x0C, 0x0F, + 0x30, 0x33, 0x3C, 0x3F, + 0xC0, 0xC3, 0xCC, 0xCF, + 0xF0, 0xF3, 0xFC, 0xFF + }; + unsigned i; + unsigned long fnt2; + + fnt2 = 0; + for (i = 0; i < 16; i += 4) { + unsigned long b4 = (fnt >> i) & 0xF; + fnt2 |= (unsigned long)double_bits[b4] << (i * 2); + } + return fnt2; +} + +static unsigned long +vesa_TriplePixels(fnt) +unsigned long fnt; +{ + static const unsigned short triple_bits[] = { + 00000, 00007, 00070, 00077, + 00700, 00707, 00770, 00777, + 07000, 07007, 07070, 07077, + 07700, 07707, 07770, 07777 + }; + unsigned i; + unsigned long fnt2; + + fnt2 = 0; + for (i = 0; i < 11; i += 4) { + unsigned long b4 = (fnt >> i) & 0xF; + fnt2 |= (unsigned long)triple_bits[b4] << (i * 3); + } + return fnt2; +} + /* * This is the routine that displays a high-res "cell" pointed to by 'gp' * at the desired location (col,row). From b52392a5bc63e8bf98fe55b6a1ef49fd024f2545 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 25 Jan 2020 20:44:17 -0500 Subject: [PATCH 17/20] Make background appear as a proper black --- sys/msdos/vidvesa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/msdos/vidvesa.c b/sys/msdos/vidvesa.c index 9294c3fb8..39b27c4b3 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -108,7 +108,7 @@ static int viewport_cols = 40; static int viewport_rows = ROWNO; static const struct Pixel defpalette[] = { /* Colors for text and the position bar */ - { 0x18, 0x18, 0x18, 0xff }, /* CLR_BLACK */ + { 0x00, 0x00, 0x00, 0xff }, /* CLR_BLACK */ { 0xaa, 0x00, 0x00, 0xff }, /* CLR_RED */ { 0x00, 0xaa, 0x00, 0xff }, /* CLR_GREEN */ { 0x99, 0x40, 0x00, 0xff }, /* CLR_BROWN */ From 1783b9de3ec45ff2ab97c9b586db5e64df8cf385 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 27 Jan 2020 10:05:08 +0200 Subject: [PATCH 18/20] Doc and sample config for MSDOS video resolution --- doc/Guidebook.mn | 4 ++++ doc/Guidebook.tex | 6 ++++++ sys/share/NetHack.cnf | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index d6594f328..fdaf540f7 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -3907,6 +3907,10 @@ Values are \(lqautodetect\(rq, \(lqdefault\(rq, or \(lqvga\(rq. Setting \(lqvga\(rq (or \(lqautodetect\(rq with vga hardware present) will cause the game to display tiles. Cannot be set with the \(oqO\(cq command. +.lp video_height +Set the VGA mode resolution height (MS-DOS only, with video:vga) +.lp video_width +Set the VGA mode resolution width (MS-DOS only, with video:vga) .lp videocolors Set the color palette for PC systems using NO_TERMS (default 4-2-6-1-5-3-15-12-10-14-9-13-11, (PC NetHack only). diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 649b09d80..a7772a034 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -4342,6 +4342,12 @@ Setting {\it vga\/} (or {\it autodetect\/} with vga hardware present) will cause the game to display tiles. Cannot be set with the `{\tt O}' command. %.lp +\item[\ib{video\verb+_+height}] +Set the VGA mode resolution height (MS-DOS only, with video:vga) +%.lp +\item[\ib{video\verb+_+width}] +Set the VGA mode resolution height (MS-DOS only, with video:vga) +%.lp \item[\ib{videocolors}] \begin{sloppypar} Set the color palette for PC systems using NO\verb+_+TERMS diff --git a/sys/share/NetHack.cnf b/sys/share/NetHack.cnf index 58154bfce..3d29538a0 100644 --- a/sys/share/NetHack.cnf +++ b/sys/share/NetHack.cnf @@ -27,6 +27,10 @@ OPTIONS=rawio,BIOS,symset:IBMGraphics_2,roguesymset:RogueEpyx # To use VGA graphical tiles on an MS-DOS PC with VGA or better,uncomment # this: #OPTIONS=video:autodetect +# And to explicitly set the resolution: +#OPTIONS=video_width:1024 +#OPTIONS=video_height:768 + # If your machine is NEC PC-9800, use: #OPTIONS=rawio,BIOS,video:default From 4add2cdc54027d0eae7a33c87811d1a4deffe7de Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 27 Jan 2020 10:20:31 +0200 Subject: [PATCH 19/20] Doc copypaste error --- doc/Guidebook.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index a7772a034..0d3771b6c 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -4346,7 +4346,7 @@ Cannot be set with the `{\tt O}' command. Set the VGA mode resolution height (MS-DOS only, with video:vga) %.lp \item[\ib{video\verb+_+width}] -Set the VGA mode resolution height (MS-DOS only, with video:vga) +Set the VGA mode resolution width (MS-DOS only, with video:vga) %.lp \item[\ib{videocolors}] \begin{sloppypar} From 61d0d21f472e813b3e9872d294b5e33dd8999b93 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 27 Jan 2020 08:23:56 -0500 Subject: [PATCH 20/20] avoid link error with gcc 10 which enables -fno-common by default --- src/windows.c | 1 - win/X11/winX.c | 3 --- win/gnome/gnbind.c | 3 --- win/tty/wintty.c | 3 --- 4 files changed, 10 deletions(-) diff --git a/src/windows.c b/src/windows.c index 5b0555189..ba7fb966e 100644 --- a/src/windows.c +++ b/src/windows.c @@ -860,7 +860,6 @@ const char *status_fieldnm[MAXBLSTATS]; const char *status_fieldfmt[MAXBLSTATS]; char *status_vals[MAXBLSTATS]; boolean status_activefields[MAXBLSTATS]; -NEARDATA winid WIN_STATUS; void genl_status_init() diff --git a/win/X11/winX.c b/win/X11/winX.c index 05d82e0eb..f6e537541 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -95,9 +95,6 @@ static XtSignalId X11_sig_id; #endif #endif -/* this is only needed until X11_status_* routines are written */ -extern NEARDATA winid WIN_STATUS; - /* Interface definition, for windows.c */ struct window_procs X11_procs = { "X11", diff --git a/win/gnome/gnbind.c b/win/gnome/gnbind.c index 0b99c2453..2c2182301 100644 --- a/win/gnome/gnbind.c +++ b/win/gnome/gnbind.c @@ -19,9 +19,6 @@ winid WIN_WORN = WIN_ERR; extern void tty_raw_print(const char *); extern void tty_raw_print_bold(const char *); -/* this is only needed until gnome_status_* routines are written */ -extern NEARDATA winid WIN_STATUS; - /* Interface definition, for windows.c */ struct window_procs Gnome_procs = { "Gnome", WC_COLOR | WC_HILITE_PET | WC_INVERSE, 0L, diff --git a/win/tty/wintty.c b/win/tty/wintty.c index fda9f6aa3..46a378088 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -72,9 +72,6 @@ extern short glyph2tile[]; #define HUPSKIP_RESULT(RES) /*empty*/ #endif /* ?HANGUP_HANDLING */ -/* this is only needed until tty_status_* routines are written */ -extern NEARDATA winid WIN_STATUS; - /* Interface definition, for windows.c */ struct window_procs tty_procs = { "tty",