248/256 wide resolutions on SMS

Yim
Posts: 125
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

248/256 wide resolutions on SMS

Unread post by Yim »

I've been playing around some with the MiSTer, thinking way too hard about SMS resolution. The thing is, the SMS runs at two resolutions: 256x192 and 248x192. I think in fact it's always running at 256x192, but whenever there's horizontal scrolling the system blacks out the leftmost tile column (8 pixels). MiSTer doesn't let you directly control the horizontal scaling by setting a multiple, but you can manipulate it via the custom aspect ratio setting (and setting vertical scaling to integer). The problem is that you can't set a different aspect ratio for 256 and 248 pixel wide resolutions, so you either end up downscaling 256 slightly or upscaling 248 slightly:

GcEaJsl.jpg
GcEaJsl.jpg (180.78 KiB) Viewed 11185 times

This is from Sonic 1, running at 248. In the above image the shadow with the arrow is noticeably wider than the others because of the upscaling of the image to a multiple of 256. In the lower, I've set the aspect ratio to 248:192, so it's been scaled up to a clean multiple of 248 and the shadows are even.

iVSdstZ.jpg
iVSdstZ.jpg (207.02 KiB) Viewed 11185 times

Here's the shop screen of Wonder Boy in Monster Land in extreme close-up. This is 256 pixels wide in the game, and the upper image is pretty even. In the lower, it's been downscaled to a multiple of 248 and the right side of the T is too narrow.

Games go back and forwards between resolutions on the fly, so whichever I choose it'll be scaled incorrectly some of the time. The mega drive core has different aspect ratio settings for its 256 and 320 wide modes, so maybe that could be done here too? Or, to be accurate to the original system:

2zNEHwk.jpg
2zNEHwk.jpg (127.02 KiB) Viewed 11185 times

Display 248 pixel wide images as 256 pixels with with the left column blacked out. This image is WBiML running on my Mega Drive. The gap on the left is wider in the lower image, because part of it is the left column that the game isn't showing. This would definitely lead to people complaining about the image being off-centre, so maybe it could be an option in the video settings and off by default.

Unfortunately I have no idea how to implement this myself, or how difficult it would be to do.
FoxbatStargazer
Top Contributor
Posts: 994
Joined: Thu Dec 10, 2020 5:44 pm
Has thanked: 309 times
Been thanked: 228 times

Re: 248/256 wide resolutions on SMS

Unread post by FoxbatStargazer »

Accurate should nearly always be the default, I.E. it should be off center.
Yim
Posts: 125
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: 248/256 wide resolutions on SMS

Unread post by Yim »

Well, it turns out this was pretty simple. Delete line 135 in /rtl/video.vhd, which starts horizontal lines from pixel 9 when masking is turn on:

Code: Select all

134		hbl_end <= conv_std_logic_vector(500,9) when border = '1' and gg = '0'
135				else conv_std_logic_vector(008,9) when (border xor gg) = '0' and mask_column = '1'  --this one
136				else conv_std_logic_vector(000,9) when (border xor gg) = '0'
137				else conv_std_logic_vector(048,9);
And:

TqchPa2.png
TqchPa2.png (3.02 KiB) Viewed 11183 times

We've got the blanked out left column visible! This then gives a consistent 256 pixel wide image, so the scaling isn't messed up by changing from 248 to 256. It is also, I believe, more accurate to how original hardware displayed. Wonder Boy in Monster Land actually took advantage of this:

bKRMNxK.png
bKRMNxK.png (3.5 KiB) Viewed 11183 times

Above is the title screen as it appears in the current version of the SMS core. The image is slightly off-centre to the left.

fpYoz3D.png
fpYoz3D.png (3.56 KiB) Viewed 11183 times

Here's the same thing with the line above deleted. The left column is displayed (in the background colour), so the image is more even. Why they didn't just use 256 mode for the title screen like in the shops I don't know.

22OeDuc.png
22OeDuc.png (3.04 KiB) Viewed 11183 times

Not every game seems to have been as careful with it as WBiML. This is Sonic 1 without the left column. Add it in:

R8mjxUv.png
R8mjxUv.png (3.11 KiB) Viewed 11183 times

And you get a column of blue. I think this is accurate to original hardware, but it doesn't actually look great in my opinion. But there's no distortion from uneven scaling if you turn on vertical integer and set the aspect ratio for horizontal integer.

Next step would be to make it optional. I think I have that mostly figured out: add a line to SMS.sv in the CONF_STR section:

Code: Select all

	"P1OS,Show masked left column,Yes,No;",
And instead of deleting the line in video.vhd, add a variable for showing the masked column or not, and change line 135 to

Code: Select all

	else conv_std_logic_vector(008,9) when (border xor gg) = '0' and mask_column = '1' and showmask = '0'
Only problem is, I cannot for the life of me figure out how to get information from CONF_STR into video.vhd to set the variable. It appears this is too basic to bother explaining in the documentation (or I can't find the explanation). I'm a nurse, not a programmer, so it's beyond me. Little help?

The other thing would be to have three options: not include the left column at all (current behaviour), include it drawn in the background colour (as in my screenshots above), or include it but always black it out (less accurate but I think would look better in most cases - in non-full screen aspect ratios it would just disappear into the existing black border).
FoxbatStargazer
Top Contributor
Posts: 994
Joined: Thu Dec 10, 2020 5:44 pm
Has thanked: 309 times
Been thanked: 228 times

Re: 248/256 wide resolutions on SMS

Unread post by FoxbatStargazer »

Sonic 1 should have a blue overscan border that left side vanishes into anyway. Don't recall if border is implemented in this core.
Yim
Posts: 125
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: 248/256 wide resolutions on SMS

Unread post by Yim »

I think I’ve figured out how the OSD options menu works. I’ll take a shot at making the left column optional when I get some time.
Yim
Posts: 125
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: 248/256 wide resolutions on SMS

Unread post by Yim »

OK, so I've done what I set out to do (along with some mission creep) with the SMS core. My personal build now has a menu item, "Masked left column", with the following options:

"BG", short for background, which uses the background/overscan colour when the left column is masked:
fAPrP9C.png
fAPrP9C.png (3.02 KiB) Viewed 9298 times
This is how it's handled on original hardware, and the default in my version.

"Black", which uses black to mask the left column:
8vdhJlh.png
8vdhJlh.png (3.09 KiB) Viewed 9298 times
This is not accurate to original hardware, but makes sense on MiSTer where the rest of the overscan area is blacked out unless you have the "Border" option turned on. This maintains the 256 pixel width of the game image.

And "Cut", which cuts off the left eight pixels when the left column is masked:
tTh0fES.png
tTh0fES.png (2.95 KiB) Viewed 9298 times
The difference between this and "Black" is subtle but significant: the image produced here is 248 pixels wide instead of 256. That means that scaling set up for 256 width will be distorted when this is set. Since many SMS games turn left column masking on and off depending on what's happening in-game, even if you change your aspect ratio to match the 248 width you'll wind up with distortions when 256 pixel images are scaled for that AR. The advantage of this setting is that it only includes the active area of the screen, I guess. It's the current behaviour of the official SMS core.

My new menu option becomes inaccessible when the "Border" option is turned on:
4vl7mKA.png
4vl7mKA.png (3.23 KiB) Viewed 9298 times
Because showing the borders already includes the masked column shown in background colour, my new menu item is redundant when they're on. But wait, I hear you say, why is that last image 239 pixels tall? Surely that should be a multiple of 8?
yXMPvUO.png
yXMPvUO.png (3.13 KiB) Viewed 9298 times
Well spotted, my observant friend. I also tweaked the overscan area a little - I added one more pixel row to the bottom of the image to make it 240 high, as in the image above. I could be completely wrong about this, but I believe the theoretical limit of SMS vertical resolution is 240 pixels, so this seemed more appropriate. Also 239 is a prime number, so if I want to fiddle with custom aspect ratios for border on mode (which I do), I won't be able to simplify them very well. 240 is very divisible, so I'll be able to have smaller numbers in my mister.ini. The horizontal overscan is kind of odd-seeming as well - twelve pixels on the left (20 if the column is masked) and 14 on the right - but the benefit of tweaking that is less obvious to me and I found the code that sets the sizes very confusing and I'm trying not to break anything, so I left it alone.

Anyways, here's how I did it. Bear in mind, I figured all this out by reading the code, trying stuff out, and looking up what I could find information on. I haven't made a serious effort at programming since 2001. What I'm saying is, if my code is sloppy or inefficient, or my explanations of why what I've done works is completely wrong, that's why. The target audience of this explanation is me a week ago when I was trying to figure this all out.

OK, first up: some changes to SMS.sv. I added the following below line 180:

Code: Select all

"D5P1OST,Masked left column,BG,Black,Cut;",
This is the menu option. D5 disables it when the fifth bit in the menumask is set, P1 nests it in the audio/video options submenu, OST makes it an option modifying bits S and T (28 and 29) in the CONF_STR, and the rest is the title and options. Next, change line 268 from

Code: Select all

    .status_menumask({~gun_en,~raw_serial,gg,~gg_avail,~bk_ena}),
to

Code: Select all

    .status_menumask({status[13],~gun_en,~raw_serial,gg,~gg_avail,~bk_ena}),
This sets the menumask's fifth bit to reflect whether the border is on or not. I need this so I can disable my menu option when the border is on. This is not strictly necessary, since my changes are ignored anyway when the border is on. OK, now for line 498:

Code: Select all

494    .x(x),
495    .y(y),
496    .color(color),
497    .mask_column(mask_column),
498    .black_column(status[28] && ~status[13]),
black_column here is new. This is how I convey the request to use black instead of the background colour to mask the left column. status[28] gets the state of bit 28 of the CONF_STR, which is set by my menu option. If BG is selected, it will be zero. If Black is selected, it's one. If Cut is selected, it doesn't matter because the column won't be shown (but it'll be zero). status[13] is the border option. The tilde inverts the result. So basically black_column is positive if Black is selected in the menu and Border is not. The reason I check on the border here is that an earlier build did this:
2D0pDZk.png
2D0pDZk.png (3.21 KiB) Viewed 9297 times
when the border was on. Undesirable. Anyway, I'll get in to how the mask colour is set a bit later, for now there's one more change in SMS.sv, at line 639:

Code: Select all

632  video video
633  (
634      .clk(clk_sys),
635      .ce_pix(ce_pix),
636      .pal(pal),
637      .gg(gg),
638      .border(status[13] & ~gg),
639      .mask_column(mask_column),
640      .cut_mask(status[29]),
cut_mask is the new item here. It reads bit 29 of the CONF_STR, which is 0 when BG or Black is set, and 1 when Cut is set. Putting this here sends that information to video.vhd, which is the part of the code that determines which parts of the screen are included in the image MiSTer produces. That's the last change to SMS.sv, so let's take a look at what I've done to video.vhd:

First off, I added a line at line 14:

Code: Select all

6  entity video is
7      Port (

...

14       cut_mask:    in    std_logic;
This is within the port section of the definition of entity video. So cut_mask takes the value specified in the .cut_mask line I put in to SMS.sv. "in" is for input, meaning it reads but cannot write the information. I think I know what std_logic is about, but not well enough to explain it. Mainly I just copied it because that's what all the other lines in this port section had. Anyway, the code in video.sys now has access to a variable called cut_mask that's set to 1 if the user wants to cut off the left side of the screen and 0 if they'd rather obscure it. Next, I changed line 135 from

Code: Select all

134    hbl_end <= conv_std_logic_vector(500,9) when border = '1' and gg = '0'
135            else conv_std_logic_vector(008,9) when (border xor gg) = '0' and mask_column = '1'
136            else conv_std_logic_vector(000,9) when (border xor gg) = '0'
to

Code: Select all

134    hbl_end <= conv_std_logic_vector(500,9) when border = '1' and gg = '0'
135            else conv_std_logic_vector(008,9) when (border xor gg) = '0' and mask_column = '1' and cut_mask = '1'
136            else conv_std_logic_vector(000,9) when (border xor gg) = '0'
By putting "and cut_mask = '1'" at the end, it checks whether that variable is set before implementing this. And what is it implementing? Well, here goes: hbl_end is the point in each row where it stops ignoring the pixels and starts writing them to the image. So line 135 is saying, if the border is off, the system isn't in game gear mode, the column is supposed to be masked, and the user has requested the masked bit be cut off, ignore the first eight pixels. Line 136 is saying if the border is off, it's not game gear, and either the mask is off or the user has asked to include it, then don't ignore the first eight pixels.

Note that the first line is saying that if the border is on and it's not a game gear, start reading pixels from number 500. This confused the heck out of me for a while, but each horizontal row goes up to 511, so when the border is on the overscan is actually read from the end of the previous line before it wraps back to zero. Sort of. The vertical count increments at horizontal count 487, so the end of the previous line is actually the start of the current one. Or something. The horizontal count also skips from 295 to 466. I'm not really sure why, and it doesn't matter for this.

Anyway, the above code either includes or cuts off the left column when it's masked, depending on menu setting. That's what I initially set out to do. The other thing I did in video.vhd was to change line 120:

Code: Select all

            else conv_std_logic_vector(215,9) when border = '1' and gg = '0'
to

Code: Select all

            else conv_std_logic_vector(216,9) when border = '1' and gg = '0'
This is setting vbl_st to 216 instead of 215. vbl_st is the setting that determines when the MiSTer starts ignoring rows of pixels. So by increasing it one I added an extra row to the bottom of the image when border = '1'. This is how I got my bordered screen's vertical resolution up to 240 instead of 239. 216 is 24 higher than the SMS's active vertical resolution of 192.

OK, so that's the mask cutoff and the extra row of pixels. How about the colour of the mask? Well, that was a little more complicated. If you remember back in SMS.sv I had a line ".black_column(status[28] && ~status[13]),"? This was under system, so it passed the result of that (should the masked column be black yes/no?) to system.vhd. So at the start of that file I added

Code: Select all

        black_column:        in STD_LOGIC;
As line 58, just under where mask_column is. I stuck it next to that already-extant variable because they seemed like an appropriate pair. This gets the information from the menu into system.vhd. But system.vhd doesn't do the masking. Further down in the file is "vdp_inst: entity work.vdp", and under that there's a port section. In there at line 247, just under mask_column again, I added

Code: Select all

        black_column => black_column,
which I think takes the information in system.vhd and passes it to vdp.vhd (vdp being video display processor). So then I open vdp.vhd and insert the same two lines at lines 28 and 141 respectively, taking the information and passing it on to vdp_main.vhd (which is where this will actually be done). In vdp_main.vhd I add "black_column: in STD_LOGIC;" as line 29. That gets the information in. Now, how exactly vdp_main.vhd does its thing is still a bit of a mystery to me. It sets a bunch of variables like cram_a and cram_d (I assume the address and data of the cram being worked on). Then there's the out_color and the vram. Which of these does what? I spent a while looking at it and the various if else statements relating to the mode of the SMS and so on, and then I noticed down the bottom:

Code: Select all

    color <= cram_D when smode_M4='1' else 
            "000000000000" when out_color="0000" else 
            "000000000000" when out_color="0001" else 
And the list of colours to set went on for all the values of out_color. Could it be that I just needed to ignore all the preceeding stuff and set color to black when the mask was on and set to be black? Let's try it out:

Code: Select all

color <= "000000000000" when black_column='1' and mask_column0='1' and x>0 and x<9 else
            cram_D when smode_M4='1' else 
            "000000000000" when out_color="0000" else
So here I've set it to make the colour black ("000000000000") if the mask is on, the colour is set to black, and the pixel currently under consideration is part of the leftmost column. I was worried this would screw up the image under the mask, which is required for processing of horizontal scrolling, but it doesn't seem to have. So far as I can tell, everything is working as intended.

Man, that's a lot of post for what would have been a very rudimentary programming job if I'd had any idea what I was doing. Hope my explanation makes sense. I think that this is an improvement to the core, and I hope people agree. If so, what do I need to do to get it incorporated into the official version?
Yim
Posts: 125
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: 248/256 wide resolutions on SMS

Unread post by Yim »

I've created a fork and pull request on github, so fingers crossed this gets in.
User avatar
Zorlac
Posts: 40
Joined: Mon May 25, 2020 2:05 am
Been thanked: 4 times

Re: 248/256 wide resolutions on SMS

Unread post by Zorlac »

My gawd man...this is brilliant! Thank you for figuring this out. I think maybe the other cores need looked-in to now if you ever get bored....hint-hint ;)

This was an enjoyable read...thank you again!!!!
I was me, but now he's gone!
Yim
Posts: 125
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: 248/256 wide resolutions on SMS

Unread post by Yim »

Thanks! This was an enjoyable process for me. I can’t guarantee I won’t get bored, but I think this was probably a one-off. I’m familiar with the changing resolution of SMS games from my time doing screenshot let’s plays of them, so I was actively looking for this issue when I got my MiSTer. If anything else starts bothering me maybe I’ll take another shot at a tweak, but I absolutely do not know what I’m doing.
User avatar
Zorlac
Posts: 40
Joined: Mon May 25, 2020 2:05 am
Been thanked: 4 times

Re: 248/256 wide resolutions on SMS

Unread post by Zorlac »

Not sure if aspect ratio code needs to be adjusted for your new changes...might be worth a look.
I was me, but now he's gone!
Yim
Posts: 125
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: 248/256 wide resolutions on SMS

Unread post by Yim »

It shouldn’t do. The point of this is to provide a consistent resolution, so the aspect ratio doesn’t need to change.

There is the question of what the correct aspect ratio is - a 1:1 pixel aspect ratio gives a 4:3 screen aspect ratio. This sounds right for old screens, but the SMS usually ran with borders above and below, so the actual aspect ratio of the active area of the screen was wider, particularly on PAL systems. What exactly it actually was depends on the model and settings of the CRT, so I don’t think there’s any one correct ratio. Personally I like integer scaling, so 4:3 is good for me, or alternatively 8:5 gives integer scaling with a pixel aspect ratio of 6:5, and 28:15 gives 7:5 (both in 1080p). They can be set in the custom aspect ratio part of mister.ini. If you turn the borders on then the ratios would have to be recalculated for that.
User avatar
Kitrinx
Core Developer
Posts: 185
Joined: Sat May 23, 2020 2:14 am
Location: NYC
Has thanked: 1 time
Been thanked: 147 times
Contact:

Re: 248/256 wide resolutions on SMS

Unread post by Kitrinx »

It would be more appropriate to run a wire from the video chip to the top level and use it to select between two correct aspect ratios, as the genesis and most other systems with variable visible pixels do. Showing masked borders creates unsightly extra padding in some 4:3 situations, and also if certain video filters are on.
Yim
Posts: 125
Joined: Mon Jan 18, 2021 11:51 pm
Has thanked: 3 times
Been thanked: 87 times

Re: 248/256 wide resolutions on SMS

Unread post by Yim »

I basically did it this way because this is the way I could figure out how to do it, so I'm not opposed to having separate aspect ratio settings for the two horizontal resolutions (can you have separate custom aspect ratios for the different resolutions, though?). That said, I do feel this is an accurate way to approach the original hardware since the left column is always there, it's just masked some of the time. On original hardware the 248 pixels of the 248 mode occupy the same space on screen as the rightmost 248 pixels of the 256 mode, so the extra padding is part of the original picture. On mega drive, the 256 pixels of 256 mode occupy the same space as the 320 of 320 mode, not just 256 of them, so it's a different behaviour.
User avatar
Zorlac
Posts: 40
Joined: Mon May 25, 2020 2:05 am
Been thanked: 4 times

Re: 248/256 wide resolutions on SMS

Unread post by Zorlac »

Yim - Just in case you decide to look at the Mega Drive / Mega-CD cores in the future ;)

https://misterfpga.org/viewtopic.php?f=16&t=1985
I was me, but now he's gone!
Post Reply