• Please review our updated Terms and Rules here

Tandy graphics 320x200 16 colors programming

Mills32

Experienced Member
Joined
Sep 25, 2018
Messages
149
Location
Spain
Hi. I was looking for samples of tandy graphics, and I read it can map any ram address to 0xB800 00000.

I didn't find any samples explaining how to do this, can it really map any address to that video ram?. If so, it would be able to do basic hardware scrolling, even if horizontal scrolling has to move 2 pixels at a time (because every byte stores two pixels).

Thanks!.
 
ftp://ftp.oldskool.org/pub/drivers/Tandy/1000/ should have the reference materials you need.

Hardware scrolling is possible, but it may not be as usable/granular as you think. The Tandy 1000 (and the PCjr from which it was designed) can map pages in 16K boundaries; you can't just choose an arbitrary area. And while the 6845 can change the start address of displayed memory, it does so in 16-bit words as you found out, so the scrolling would be 4 pixels at a time.

I don't recall the behavior of setting the 6845 start address register to point to something beyond what's visible; I don't recall if it shows memory beyond the area, or if it wraps around. Testing on real hardware might be necessary (ie. I'm not sure what emulators emulate Tandy video perfectly).
 
ftp://ftp.oldskool.org/pub/drivers/Tandy/1000/ should have the reference materials you need.

Hardware scrolling is possible, but it may not be as usable/granular as you think. The Tandy 1000 (and the PCjr from which it was designed) can map pages in 16K boundaries; you can't just choose an arbitrary area. And while the 6845 can change the start address of displayed memory, it does so in 16-bit words as you found out, so the scrolling would be 4 pixels at a time.

I don't recall the behavior of setting the 6845 start address register to point to something beyond what's visible; I don't recall if it shows memory beyond the area, or if it wraps around. Testing on real hardware might be necessary (ie. I'm not sure what emulators emulate Tandy video perfectly).

Thanks for the link. It is a bit confusing because I does not explain everything, (or I didn't find it).

I found the "CRT/Processor Page Register" (3DF).
Bits:
0 = CRT Page 0
1 = CRT Page 1
2 = CRT Page 2
3 = Processor Page 0
4 = Processor Page 1
5 = Processor Page 2
6 = Video Address Mode 0
7 = Video Address Mode 0

- CRT Page 0-2 select the 16K page used by the video. In 32K modes, bit 0 is ignored.
- Processor Page 0-2 are combined with the CPU address to select the 32K segment of memory accessed at B8000. If an odd page number is selected (1,3,5, etc.) the window is reduced to 16K.

I could not find what "CPU address" means :(.

I also found an old post from vogons talking about vertical scrolling being possible on tandy 1000, but there is no sample code to show how to do it.
 
Correct, page 57 of Tandy_1000_Technical_Reference_Manual is what you need to look at (and really, everything on and after page 45).

Both the PCjr and the Tandy 1000 use system ram as video ram. The page register points to eight 16k areas of a continuous 128KB area in both systems: The PCjr's area is the first 128K in the system, and the Tandy's is the *last* 128KB in the system (so on a 256K Tandy, it's from 128K-256K; on a 640K tandy, it's from 512K-640K).

You don't need to know where in memory the pages are on the Tandy 1000; the CRT page function specifies which 16K or 32k (2 x 16K) pages are being displayed, whereas the Processor page function specifies which 16K or 32K areas are accessible at B800h. So you typically set the CRT register to which page (16K) or pages (32k) you want to be visible on the screen, and the page register to the pages you want readable/writable at B800:0000.

(On a PCjr, you *do* need to know where in main memory the pages are if using 320x200x16 modes, as the PCjr can only access 16K at B800 instead of the full 32K. But this is not a concern for you if you're programming for Tandy.)

Vertical scrolling on Tandy would probably (I've never tried it) be done by picking a page that is not the last page in the system, then using the 6845 3D4/3D5 ports to change the registers 12 and 13 (Start Address) to scroll upward, and hoping that the page directly after it is what appears at the bottom as the current page is scrolled off the top. But I have never tested this.
 
Vertical scrolling on Tandy would probably (I've never tried it) be done by picking a page that is not the last page in the system, then using the 6845 3D4/3D5 ports to change the registers 12 and 13 (Start Address) to scroll upward, and hoping that the page directly after it is what appears at the bottom as the current page is scrolled off the top. But I have never tested this.

It looks like the same function I had to scroll EGA and VGA from my little game engine (Changing register 3d4) works also on Tandy, and even on CGA.

On CGA it scrolls in 8 pixel steps (horizontal), and in two line steps (vertical).
On tandy the screen moves in 4 pixel steps. Vertical scroll moves 4 lines at a time and wraps around the same image, I'll have to test if it can be configured to show two pages or to scroll 1 line at a time, because the memory is arranged interlacing 4 lines.

I tested this on dosbox, I suppose tandy emulation is good enough for this.
 
Last edited:
If you want to share a test program, I can tell you how it responds on second-generation Tandy hardware (1000RL) at least.

Sure, here it is, a little program that scrolls "titus the fox" intro. View attachment TANDY_SCROLL.zip
If tandy could increase "logical width" to update a column "off screen" it would be enough for the engine. I would also need to see two pages at the same time, so that I can update a row above or below the visible area.
 
Well, finally got a chance to copy this over and test it. Both horizontal and vertical scrolling seem to increase the step by a hair too much, because they gradually "creep" in the perpendicular direction every time they wrap around the screen - horizontal scrolling moves "down" in the playfield about 8px, while vertical scrolling moves left by around 64-ish (spitballing here.) Still, the basic movement is fast and fluid, and there may well be a way to work around the wrapping issue.
 
Postscript: I think I figured it out - is the scrolling just a dumb loop that doesn't properly reset to the start of the buffer? If that's the case, then cycling through two 320x200 bitmaps (32,000 bytes) in sequence would mean a remainder of 768 bytes out of 32KB, which would be 4.8 lines of vertical creep and 0.8 lines of horizontal offset, for a left-creep of 0.2 lines, or 64 pixels - more or less what I thought I observed.
 
I discovered that CGA/TANDY/EGA and VGA "scrolling" are the same,. They are not like scrolling in consoles or other devices, you simply change the start address shown on screen like this:

Code:
			Scroll = 0		Scroll = 1		Scroll = 4

Scanline 0		01234567		12345678		456789AB
Scanline 1		89ABCDEF		9ABCDEF0		CDEF0123

It's up to you to calculate the "x" and "y" positions, but it works.

and there may well be a way to work around the wrapping issue.

There is a way to update a column and/or a row, to redraw the edges so that it looks seamless (I did it with EGA/VGA).

EGA and VGA can scroll 1 pixel at a time using a special register, CGA scrolls 8 pixels at a time, and TANDY 4. Also CGA and Tandy skip lines because of the interlaced arrangement of the VRAM.

Postscript: I think I figured it out - is the scrolling just a dumb loop that doesn't properly reset to the start of the buffer? If that's the case, then cycling through two 320x200 bitmaps (32,000 bytes) in sequence would mean a remainder of 768 bytes out of 32KB, which would be 4.8 lines of vertical creep and 0.8 lines of horizontal offset, for a left-creep of 0.2 lines, or 64 pixels - more or less what I thought I observed.

There are some bytes at the end of the 16K page that I could not modify, and they show how the page does not end at a complete scanline, producing horizontal scrolling when going down.

tga_000.png
 
Just a few ideas:

While you can (and maybe must at some point) try on the real hardware, using an accurate emulator could help you to accelerate the development process. 86Box, I you don't know it, is a fork of PCem, much more accurate in terms of CPU speed and graphic chips behavior. It's able to run very specialized CPU and video card oriented software that intensively touches the registers. It even runs, with only a few minor glitches, 8088mph and INTROjr (hi, Trixter :)).


BTW, the executable you provided looked the same on DosBox and 86Box (both emulating Tandy). The most up to date version of 86Box is here: https://ci.86box.net/job/86Box as the Github releases are ages old.

Regarding the idea of horizontal scroll on Tandy 320x200x16 colors, as I'm aware of, there's no horizontal logical screen size register on the PCjr/Tandy. That makes hardware horizontal scrolling very difficult as there's no way to draw a column in advance, just like it can be made on EGA and VGA-ModeX. Maybe a solution for this would be manipulating the CRT registers to narrow the visible width, i.e. creating a custom 304x200 graphic mode. I speak theoretically as I didn't actually tried. The good part is that there are no known Tandy clones, and the only PCjr clone we know the existence of is the Tandy 1000, so there would be less problems of compatibility than with other cards.

Boulder Dash makes a lightning fast horizontal scroll, both on Tandy/PCjr and CGA:

https://www.youtube.com/watch?v=lAOWluiiYoc

How does it? If we run the game on DOSBox and on some moment the cycles are reduced to something very low (between 30-70 cycles), we can see a noticeable flicker on the right or left size (depending on where are you scrolling at). So it seems that the technique used here is:

- Drawing the next column in advance
- Change the start screen register for a few pixels
- Loop until the entire screen is scrolled

Because the CGA video memory is so limited, and due to the lack of a horizontal logical screen register, this is made very fast, much faster than 60-70 fps, in order to make the flicker unnoticeable to the human eye/brain. But I don't think this technique could be used for a smooth 30/60/70 fps horizontal scroll as the advanced column would be visible for a long time. In fact, the vertical scroll on the same game is slower and smoother. It also may be due to the fact that horizontally scrolls 4 pixels (1 byte) instead of 1 pixel.

Regarding the vertical scroll, it's been used on the PCjr since the beginning, at least on 160x200x16 colors mode. For example, on these games:

https://www.youtube.com/watch?v=ZP4EsbwN5mM

If you look at the first game, you will see that there's no horizontal scroll. When the stage changes, there's a very noticeable up to down drawing. But when the same game does vertical scroll, it's just great, so smooth, so perfect. As perfect as an arcade machine's. The second game (2:46) uses a perfect continuous vertical scroll. In my opinion, this is far easier to achieve on mode 8 (160x200) than in mode 9 (320x200) because in mode 8 you can have 2 pages using 32 kb of video RAM, while you can have only one page (and a few spare bytes) on mode 9. So, in mode 8 you can draw the row in advance to the invisible video memory and then change the start register for a few pixels, as you would do on EGA/VGA-ModeX. I'm not saying that making hardware scrolling on mode 9 would not be possible, I just don't know it. But, for making it possible, there would be made 64 kb available for video memory (or, at least, memory enough to draw a row in advance and then taking advantage of video memory wrapping). I don't know if it is possible nor how could it be done. The late Tandy 1000 models (SL/TL and later) can have access to 64 kb of video RAM (or, better said, they can assign 64 kb of system RAM to video, as there's no video RAM as it on the PCjr/Tandy). I just don't know if this can be done on earlier Tandys.

I wonder if Boulder Dash for CGA is manipulating the CRT registers in some way for achieving a custom 160x200x4 colors mode in order to have 8kb per page=2 video pages. The pixels are chunky and it looks like 160x200 but I don't know if the pixels are just doubled or, as I said before, it's a custom mode. If there's no custom mode, maybe the few spare bytes of the CGA buffer are enough to draw a row in advance, scroll and wrap the memory.

This other PCjr game (Touchdown Football) has a quite jerky horizontal scroll. My guess is that it's using a simple rep movsb or rep movsw to draw the scroll directly to the video memory.

P.S. I just saw that CGA Boulderdash uses a regular 320x200 mode (I noticed some of the letters at the bottom).
 
Last edited:
Thanks for the info, I tried 86Box some time ago, but I was not able to boot from any image, I'll have to test it later.

I wonder if Boulder Dash for CGA is manipulating the CRT registers in some way

I first tried to use MCGA mode for my engine, and I could shrink the visible area adding black bars. But it was very difficult to program for real CRT, and dosbox showed bars only at the right and bottom part of the screen, so it was not centered. Maybe CGA and Tandy have the same registers.

Looking at boulder dash at low speed, it scrolls left/right just as my test program, (4 pixels at a time), but the scroll is so fast it looks smooth :).

I didn't know about the 160x200 mode, it may be better because sprites and columns/rows could be drawn twice as fast.
 
I played boulder dash (CGA) I had never played it, really... And I'm amazed, it scrolls smoothly even with very very low cycles in dosbox, so thats surely another hardware scrolling in action, I have to test things and look for more info about it.
 
Boulder Dash makes a lightning fast horizontal scroll, both on Tandy/PCjr and CGA

Because the CGA video memory is so limited, and due to the lack of a horizontal logical screen register, this is made very fast, much faster than 60-70 fps, in order to make the flicker unnoticeable to the human eye/brain. But I don't think this technique could be used for a smooth 30/60/70 fps horizontal scroll as the advanced column would be visible for a long time.
It definitely can be used for that. 60fps is the rate at which CGA/Jr/Tandy update a full frame, but don't forget that during one 60Hz cycle the frame is also being drawn from top to bottom. So as long as you can "race the beam" (i.e. draw/copy to the screen faster than the beam is scanning, and never update scanlines *while* the beam is scanning them), there will be no flicker or tearing for the human eye/brain to notice.
If you change the start address during frame N, start drawing the new column during vblank/vsync, and keep your update small and fast enough, then during frame N+1 (where your new start address takes effect) each scanline will already have been updated by the time the CRT actually displays it.

I'm fairly sure that this is what Boulder Dash is doing. DOSBox doesn't update scanlines at 15KHz from top to bottom like a real CRT does; it simply updates the frame at the vsync rate (regardless of cycles), so what it shows you doesn't really reflect everything that happens at 'sub-frame' timings.

If there's no custom mode, maybe the few spare bytes of the CGA buffer are enough to draw a row in advance, scroll and wrap the memory.
You can use more than that: the update just has to be small enough to allow writing it to the screen buffer faster than it's actually displayed. How large that is depends on your code efficiency, timing routines, and of course on how much frame-time you have to reserve for the rest of the game logic.

I first tried to use MCGA mode for my engine, and I could shrink the visible area adding black bars. But it was very difficult to program for real CRT, and dosbox showed bars only at the right and bottom part of the screen, so it was not centered. Maybe CGA and Tandy have the same registers.
To center the screen you have to modify the CRTC Horizontal Sync position (there's some sample code of mine at https://github.com/viler-int10h/TVCGAFix... see the readme.txt for a more in-depth explanation). MCGA might need something extra - never messed with that so I have no idea. ;)

I played boulder dash (CGA) I had never played it, really... And I'm amazed, it scrolls smoothly even with very very low cycles in dosbox, so thats surely another hardware scrolling in action, I have to test things and look for more info about it.
Check out Prohibition too: https://www.youtube.com/watch?v=N842iHzWoyo
The video is 30fps only, and seems to be using the wrong machine/game speed... it's much better in a properly configured DOSBox, and especially on a real CRT. Still the gold standard for smooth 60Hz CGA scrolling as far as I'm concerned.
 
I got the 160x200 mode working on tandy, but there is something I don't understand.

Tandy graphics card should show two 16K pages in this mode, right?. It only shows one, and wraps around at the 16K boundary. That causes my drawing assembly function to stop working properly at the boundary, because the odd even lines get inverted :(.

See that sprite being drawn ok.
t1.png

Once it reaches the 16k boundary, lines get inverted.
tga3.png

This is How I enabled the 160x200 video mode, I read it should behave like the 320x200, (which shows 32K, so the bottom part of the screen would be black, containing the second 16K page in this mode).
Maybe I need something more.

Code:
	union REGS regs;
	regs.h.ah = 0x00;
	regs.h.al = 0x08;
	int86(0x10, &regs, &regs);
 
Last edited:
Back
Top