Suggestion for unscramble_constant_data()

Juergen Buchmueller pullmoll at t-online.de
Fri Jun 15 12:44:26 PDT 2007


On Fri, 15 Jun 2007 10:46:37 -0700 (PDT)
"Eric Smith" <eric at brouhaha.com> wrote:

> By all means, please do explain how the video timing should work.
> I very much doubt that the video timing code in Altogether is correct,
> as it was based on guesswork.

Ok, I'll try to make it independent of how I implemented it, for the most
part. I'll copy+paste some comment sections of my display.c first:

/**
 * @brief PROM a38 contains the STOPWAKE' and MBEMBPTY' signals for the FIFO
 *
 * The inputs to a38 are the UNLOAD counter RA[0-3] and the DDR<- counter
 * WA[0-3], and the designer decided to reverse the address lines :-)
 *
 *	a38  counter
 *	-------------
 *	 A0  RA[0]
 *	 A1  RA[1]
 *	 A2  RA[2]
 *	 A3  RA[3]
 *	 A4  WA[0]
 *	 A5  WA[1]
 *	 A6  WA[2]
 *	 A7  WA[3]
 *
 * Only two bits of a38 are used:
 * 	O1 (002) = STOPWAKE'
 * 	O3 (010) = MBEMPTY'
 *
 * This dump is from PROM displ.a38:
 * 0000: 003,013,015,013,015,013,017,013,015,013,017,013,015,013,017,013,
 * 0020: 013,003,013,015,013,015,013,017,013,015,013,017,013,015,013,017,
 * 0040: 013,015,003,013,013,017,015,013,013,017,015,013,013,017,015,013,
 * 0060: 015,013,013,003,017,013,013,015,017,013,013,015,017,013,013,015,
 * 0100: 013,017,015,013,003,013,015,013,013,017,015,013,015,013,017,013,
 * 0120: 017,013,013,015,013,003,013,015,017,013,013,015,013,015,013,017,
 * 0140: 013,015,013,017,013,015,003,013,013,015,013,017,013,017,015,013,
 * 0160: 015,013,017,013,015,013,013,003,015,013,017,013,017,013,013,015,
 * 0200: 013,017,015,013,015,013,017,013,003,013,015,013,015,013,017,013,
 * 0220: 017,013,013,015,013,015,013,017,013,003,013,015,013,015,013,017,
 * 0240: 013,015,013,017,013,017,015,013,013,015,003,013,013,017,015,013,
 * 0260: 015,013,017,013,017,013,013,015,015,013,013,003,017,013,013,015,
 * 0300: 013,017,015,013,013,017,015,013,013,017,015,013,003,013,015,013,
 * 0320: 017,013,013,015,017,013,013,015,017,013,013,015,013,003,013,015,
 * 0340: 013,015,013,017,013,015,013,017,013,015,013,017,013,015,003,013,
 * 0360: 015,013,017,013,015,013,017,013,015,013,017,013,015,013,013,003
 *
 * Note: I swap the contents of displ_a38[] after loading it from a file,
 * so the access macros don't have to take care of the address line
 * reversal all the time.
 */
unsigned char displ_a38[256];

/** @brief PROM a38 bit O1 is STOPWAKE' (stop DWT if bit is zero) */
#define	FIFO_STOPWAKE_0	(displ_a38[dsp.fifo_out*16+dsp.fifo_in] & 002)
/** @brief PROM a38 bit O3 is MBEMPTY' (FIFO is empty if bit is zero) */
#define	FIFO_MBEMPTY_0	(displ_a38[dsp.fifo_out*16+dsp.fifo_in] & 010)



/**
 * @brief emulation of PROM a63 in the display schematics page 8
 *
 * The PROM's address lines are driven by a clock CLK, which is
 * pixel clock / 24, and an inverted half-scanline signal H[1]'.
 *
 * It is 32x8 bits and its output bits (B) are connected to the
 * signals, as well as its own address lines (A) through a latch
 * of the type SN74774 like this:
 *
 * B    174     A   others
 * ------------------------
 * 0     5      -   HBLANK
 * 1     0      -   HSYNC
 * 2     4      0
 * 3     1      1
 * 4     3      2
 * 5     2      3
 * 6     -      -   SCANEND
 * 7     -      -   HLCGATE
 * ------------------------
 * H[1]' -      4
 *
 * The display_state_machine() is called by a timer at a rate
 * of pixelclock/24.
 *
 * This dump is from PROM displ.a63:
 * 0000: 0007,0013,0015,0021,0024,0030,0034,0040,
 * 0010: 0044,0050,0054,0060,0064,0070,0074,0200,
 * 0020: 0004,0010,0014,0020,0024,0030,0034,0040,
 * 0030: 0044,0050,0054,0060,0064,0070,0175,0203
 */
unsigned char displ_a63[32];



/**
 * @brief PROM a66 is a 256x4 bit (type 3601)
 *
 * Address lines are driven by H[1] to H[128] of the the
 * horz. line counters. PROM is enabled when H[256] and H[512]
 * are both 0.
 *
 * Q1 is VSYNC for the odd field (with H1024=0)
 * Q2 is VSYNC for the even field (with H1024=1)
 * Q3 is VBLANK for the odd field (with H1024=0)
 * Q4 is VBLANK for the even field (with H1024=1)
 *
 * This dump is from PROM displ.a66:
 * 0000: 013,013,013,013,013,012,012,012,012,012,012,012,012,013,013,013,
 * 0020: 013,013,013,013,013,013,013,013,013,013,013,013,013,013,013,013,
 * 0040: 013,013,013,013,013,013,013,013,013,013,013,013,013,013,013,013,
 * 0060: 013,013,013,013,013,013,013,013,013,013,013,013,013,013,013,013,
 * 0100: 013,013,013,013,017,017,017,017,017,017,017,017,017,017,017,017,
 * 0120: 017,017,017,017,017,017,017,017,017,017,017,017,017,017,017,017,
 * 0140: 017,017,017,017,017,017,017,017,017,017,017,017,017,017,017,017,
 * 0160: 017,017,017,017,017,017,017,017,017,017,017,017,017,017,017,017,
 * 0200: 017,017,017,017,017,017,017,017,017,017,017,017,017,017,017,017,
 * 0220: 017,017,017,017,017,017,007,007,007,007,005,005,005,005,005,005,
 * 0240: 005,005,007,007,007,007,007,007,007,007,007,007,007,007,007,007,
 * 0260: 007,007,007,007,007,007,007,007,007,007,007,007,007,007,007,007,
 * 0300: 007,007,007,007,007,007,007,007,007,007,007,007,007,007,007,007,
 * 0320: 007,007,007,007,007,007,007,007,017,017,017,017,017,017,017,017,
 * 0340: 017,017,017,017,017,017,017,017,017,017,017,017,017,017,017,017,
 * 0360: 017,017,017,017,017,017,017,017,017,017,017,017,017,017,017,017
 */
unsigned char displ_a66[256];

/** @brief test mask for the VSYNC signal (depends on field: HLC1024) */
#define	A66_VSYNC	((hlc & 1024) ? 001 : 002)
/** @brief test mask the VBLANK signal (depends on field: HLC1024) */
#define	A66_VBLANK	((hlc & 1024) ? 004 : 010)

#define	VSYNC_HI(a)	(0==((a)&A66_VSYNC))
#define	VSYNC_LO(a)	(0!=((a)&A66_VSYNC))
#define	VBLANK_HI(a)	(0==((a)&A66_VBLANK))
#define	VBLANK_LO(a)	(0!=((a)&A66_VBLANK))


Now for the major changes compared to how altogether currently handles
scanlines, FIFO etc:
- no scanline global, but instead a hlc (or horz_line_count)
- fifo_depth no longer necessary, handled by prom_a38
- just two timing constants: one for 24 pixels, and one for 16 pixels

The new horz. line counter always counts from 150 to 1799 inclusively, then
resets to 150. You may want to define the constants like they are to be
found in the schematics:

/**
 * @brief start value for the horizontal line counter
 *
 * This value is loaded into the three 4 bit counters (type 9316)
 * with numbers 65, 67, and 75.
 * 65: A=0 B=1 C=1 D=0
 * 67: A=1 B=0 C=0 D=1
 * 75: A=0 B=0 C=0 D=0
 */
#define	DISPLAY_HLC_START	(2+4+16+128)

/**
 * @brief end value for the horizontal line counter
 *
 * This is decoded by H30, an 8 input NAND gate.
 */
#define	DISPLAY_HLC_END		(1+2+8+32+64+256+512+1024)



All video updating depends on a single function, which is called by a timer
at a rate that corresponds to 24 pixel clocks, i.e. 24*50ns.

The function, which I called it display_state_machine(int arg), has an
argument that is the current displ_a63[] PROM address, i.e. a value between
0 and 63. All horizontal signals are derived from the contents of displ_a63
[arg], so you may want to define some macros for accessing the array's
bits. I have these:

/** @brief PROM a63 B0 is latched as HBLANK signal */
#define	A63_HBLANK	0001
/** @brief PROM a63 B1 is latched as HSYNC signal */
#define	A63_HSYNC	0002
/** @brief PROM a63 B2 is the latched next address bit A0 */
#define	A63_A0		0004
/** @brief PROM a63 B3 is the latched next address bit A1 */
#define	A63_A1		0010
/** @brief PROM a63 B4 is the latched next address bit A2 */
#define	A63_A2		0020
/** @brief PROM a63 B5 is the latched next address bit A3 */
#define	A63_A3		0040
/** @brief PROM a63 B6 SCANEND signal, which resets the FIFO counters */
#define	A63_SCANEND	0100
/** @brief PROM a63 B6 HLCGATE signal, which enables counting the HLC */
#define	A63_HLCGATE	0200
/** @brief helper to extract A3-A0 from a PROM a63 value */
#define	A63_NEXT(n)	(((n) >> 2) & 017)

#define	HBLANK_HI(a)	(0!=((a)&A63_HBLANK))
#define	HBLANK_LO(a)	(0==((a)&A63_HBLANK))
#define	HSYNC_HI(a)	(0!=((a)&A63_HSYNC))
#define	HSYNC_LO(a)	(0==((a)&A63_HSYNC))
#define	SCANEND_HI(a)	(0!=((a)&A63_SCANEND))
#define	SCANEND_LO(a)	(0==((a)&A63_SCANEND))
#define	HLCGATE_HI(a)	(0!=((a)&A63_HLCGATE))
#define	HLCGATE_LO(a)	(0==((a)&A63_HLCGATE))


The state machine has a local static variable for the previous a63
state, and also the previous a66 state. I called them a63_latch and
a66_latch. Now for the state changes, which I will give in pseudo code,
using above macros where appropriate:

display_state_machine(int arg)
	static int a63_latch, a66_latch;
	int a63, a66, next;

	if (arg == 020)
		new scaline starts:
		its # is = ((hlc&~1) | (hlc>>10)) - DISPLAY_HLC_START
		wakeup MRT (memory refresh task)

	a63 = displ_a63[arg];
	if (HLCGATE_HI(a63))
		increment and wrap hlc:
		if (++hlc > DISPLAY_HLC_END)
			hlc = DISPLAY_HLC_START

	extract the 'next' from a63
	or with not(hlc bit 0) for address line A4

	next = A63_NEXT(a63) | (((hlc & 1) << 4) ^ 020);

	if ((hlc & 256) || (hlc & 512))
		prom a66 is disabled, use an all-ones value:
		a66 = 017
	else
		get prom a66 value for the LSB of hlc:
		a66 = displ_a66[hlc & 255]

	if (SCANEND_HI(a63))
		scanline end is high:
		increment your bitmap y by 2
		look at the recent SETMODE<- value and set the
		inverse and half clock flags accordingly

	if (HSYNC_HI(a63))
		if (HSYNC_LO(a63_latch))
			raising edge of hsync
			clear fifo, i.e. set fifo_in = fifo_out = 0
			set dwt_blocks flag = 0
			remove any pending unload_word() timer
		else
			we are in hsync time - nothing to do
	if (VSYNC_HI(a66))
		if (VSYNC_LO(a66_latch))
			raising edge of vsync
			wake the DVT
			wake the CURT
			update the bitmap to the screen
		else
			we are in vsync time - nothing to do

	if (VBLANK_HI(a66))
		if (VBLANK_LO(a66_latch))
			raising edge of vblank
			reset dht_blocks flag to 0
			reset dwt_blocks flag to 0
			wake the DHT
		else
			we are in vblank - nothing to do
		set your bitmap y to (hlc >> 10) - 2
	else
		if (HBLANK_LO(a63) && HBLANK_HI(a63_latch))
			falling edge of hblank
			start a timer for unload_word() function
			the argument I used is 256*y+x (see below)

	if (VBLANK_LO(a66) && !dwt_blocks && !dht_blocks)
		if (FIFO_STOPWAKE_0 != 0)
			wake the DWT

	a63_latch = a63
	a66_latch = a66

	re-insert timer for this function that fires after
	24*pixel time, argument is 'next'



The unload_word function looks like this:

unload_word(int arg)
	int y = arg / 256;
	int x = arg % 256;
	int word;

	if (FIFO_MBEMPTY_0 == 0)
		FIFO underrun, set word = inverse
	else
		word = FIFO[fifo_out++] ^ inverse;
		fifo_out %= FIFO_SIZE

	if (halfclock)
		put 16 2x1 pixels in word to to x*16, y
		x += 2
	else
		put 16 1x1 pixels in word to to x*16, y
		x += 1

	if (x >= DISPLAY_SCANLINE_WORDS)
		return

	re-insert a timer for unload_word after DISPLAY_WORD_TIME
	the argument is again y*256+x (x was incremented)
	remember the timer id, so it can be removed, if need be



The BLOCK function of DWT set the dwt_blocks = 1, and the DHT block sets
dht_blocks. Both are reset exclusively in the state machine.

The EVENFIELD branch just tests hlc & 1024 like this:
	branch = hlc & 1024 ? 0 : 1;

That's it. The actual implementation could be simpler than the current,
because you don't have to care about the FIFO, and you have just two
timers running. The display_state_machine() timer is running constanly,
while the unload_word() timer is started at a certain point and runs in
intervals of the 16 pixel time until the end of the line.

Juergen


More information about the Altogether-devel mailing list