Jump to content
  • entries
    62
  • comments
    464
  • views
    86,883

New kernel--What the Dickens?


supercat

1,452 views

At first glance, this kernel may look like any other 24-column text kernel. But there are a few differences. It should probably be suitable for use in a text adventure game (with a nice scrolling output, even on an unexpanded 2600!) though I may have to increase the line spacing slightly for that.

 

For a variety of reasons, this is not "just another 24-column kernel":

 

-1- Minimal flicker thanks to a variant of David Crane's "flicker blinds" trick and an anti-flicker font.

 

-2- Minimal space between rows. Three scan lines, which I could probably maintain even fetching text from multiple banks. In a text adventure game, I'd probably increase paragraph spacing by five scan lines (half normal line spacing) and count everything in half lines.

 

-3- No HMOVE bars; I think this is the first flicker-blinds program to avoid those.

 

There's another reason this is most emphatically not "just another 24-column kernel". Can anyone spot it?

13 Comments


Recommended Comments

It's actually 26?

And here I was looking at the fine details (i.e. the code) trying to figure it out, and missed the obvious. The 26 chars are definitely not evident in the code, at least not to me (I would have expected those to be drawn with missiles and the ball or something, but there are no references to these objects that I can see.)

 

How did you get those extra two letters in there?

Link to comment

No, he's repositioning mid-line, not touching NUSIZ at all.

 

Stepping through with Stella...

 

Font is 8-lines tall, two 4-line sections that go like this:

 

				position
Line	 P0				  P1
1		26->58 (RESP0)	  42
2		52				  36
3		42				  26->58 (RESP1)
4		36				  52

The two RESPx writes are marked, and are mid-line, all other repositioning occurs with HMOVEs (left 6 at the end of lines 1 and 3 and left 10 (!) at the end of lines 2 and 4) and occurs at the end of the scanline.

 

Very clever! :) :)

Link to comment
The two RESPx writes are marked, and are mid-line, all other repositioning occurs with HMOVEs (left 6 at the end of lines 1 and 3 and left 10 (!) at the end of lines 2 and 4) and occurs at the end of the scanline.

 

On Z26 and actual hardware, all the HMOVEs move both players eight pixels left. I'm not sure what's with those numbers you gave.

 

The simplest way to describe the sprite handling of this kernel is to observe that each sprite gets hit every four lines, always in the exact same spot, and moves left 8 pixels on each of those lines. Consequently, the sprite will have moved 32 pixels left before it gets hit, and so the second copy of the sprite before the hit will be in the same place as the first copy will be afterward.

 

There are a few spare cycles in the kernel, since it's not necessary to write any of the motion registers. I don't have anything particularly in mind for them in this application, but the approach might allow me to expand the Ruby Runner kernel from ten columns to eleven or twelve. That could be pretty sweet if it works.

 

The code checks for loop exit after each of the four scan line routines. Since each line of text is seven lines rather than eight, each line of text will start with the sprites in a different place. Each time I want to start displaying text I use an indirect jump to go to one of four 'start showing text' routines; each of the four loop exits sets up the pointer for the next one.

 

Since there are an odd number of lines displayed (21 in this case) the screen naturally alternates between the two visible frames. Indeed, it cycles among all four, as may be confirmed by disabling a player sprite. If an even number of lines were displayed, it would be necessary to add explicit code to 'nudge' things between frames. The way fonts are stored, I could make the first or last line of text be 8 scan lines instead of 7 (with a blank line at the top or bottom as needed); that would reduce my allowable character set size from 250 characters to 249.

 

BTW, I would expect the kernel could use a different character set for each line of text without too much difficulty; I wonder what the best way would be to partition character pairs into character sets. Supporting more than 250 (or 249) characters within a line would be irksome, though it might be possible to support up to about 500 if I stored text using two bytes per character (i.e. pair of 4x7 characters) rather than one. Line spacing would probably have to increase in that case, though, since I'd no longer be able to use my 12-cycle-per-character pointer setup:

  lda #7; Just do this once
 ldx chardata,y
 stx pointer_low
 sbx #$10
 stx pointer_high

Pretty sweet use of sbx, no?

Link to comment

I wonder whether it would be practical to encode three twelve-bit pointers per four bytes? That might be a workable approach. I don't think it could be done with just three blank scan lines between text rows, but maybe with four, or almost certainly with five.

 

  lda #7;  Once only

 ldx msbdataA,y
 stx ijmp1
 jmp (ijmp)
 jmp real_spot

 ldx lsbdataA1,y
 stx ptrA1
 sbx #$10; or #$00
 stx ptrA1+1
 ldx lsbdataA2,y
 stx ptrA2
 sbx #$10; or #$00
 stx ptrA2+1
 ldx lsbdataA3,y
 stx ptrA3
 sbx #$10; or #$00
 stx ptrA3+1

 ldx msbdataB,y; Next one
 stx ijmp
 jmp (ijmp)

The cost of this would be an extra 15 cycles per three characters, but the code bloat would be substantial since there would need to be 32 (8 jump targets, and four groups of characters) copies of the above routine to handle 12 characters (I'd figure the rightmost column could fit within a 249-character set). It would probably be possible to improve things quite a bit, but I don't know exactly how much.

Link to comment

BTW, I chose the text for the silly subject-line pun, but it's quite something to observe that even with that great new kernel I couldn't even get one complete sentence on the screen! Even Paul Clifford isn't that bad.

Link to comment

OK, I have been looking at this for some time and I just can't figure out how the setup code works! In my own attempt at a 24-char kernel, I fill a buffer with text using the code shown below. This code loads a pair of characters from the Message, combines the data from LeftChar and RightChar and stores it in the buffer for display. Unfortunately this code uses 56 cycles and must be repeated for each pair of characters (12 for a 24-char kernel). This comes to a huge 672 cycles (approx 9 scanlines)!

 

txs; X holds the position in the message		 
ldy Message+1,X; Load first character
lda Message+0,X; Load second character
tax
lda LeftChar+3,X; Load data for first character
ora RightChar+3,Y; Merge with data from second character
sta BUFFER+3; Store in buffer
lda LeftChar+2,X 
ora RightChar+2,Y
sta BUFFER+2
lda LeftChar+1,X 
ora RightChar+1,Y
sta BUFFER+1
lda LeftChar+0,X
ora RightChar+0,Y
sta BUFFER+0

 

The only ways that I can think to speed this up are:

  1. Pre-calculate all possible combinations of letters in ROM (would require a huge table).
  2. Move some of the code into the display kernel (e.g. the OR operation).

It looks like your code does things differently, but I can't see how to avoid the above steps? It seems that you are using SBX in some clever way, but how is the character data stored in the first place? Any clues would be most welcome!

 

Chris

Link to comment
The only ways that I can think to speed this up are:

  1. Pre-calculate all possible combinations of letters in ROM (would require a huge table).
  2. Move some of the code into the display kernel (e.g. the OR operation).

It looks like your code does things differently, but I can't see how to avoid the above steps? It seems that you are using SBX in some clever way, but how is the character data stored in the first place? Any clues would be most welcome!

 

It uses a huge table, but is set up in such a way as to make a large character set practical. If all of the lines of text are predetermined, precomputing the character set shouldn't be a problem. Every 250 characters will take up 2K (the code can't quite handle 256). While 256 characters isn't quite enough, it's close. I would expect it should be possible to code all the text for a reasonable text adventure such that all but one of the characters on each line would be within a 250-character set. Each line would then have an extra byte to indicate which character (if any) was the oddball.

Link to comment
It uses a huge table, but is set up in such a way as to make a large character set practical. If all of the lines of text are predetermined, precomputing the character set shouldn't be a problem. Every 250 characters will take up 2K (the code can't quite handle 256). While 256 characters isn't quite enough, it's close. I would expect it should be possible to code all the text for a reasonable text adventure such that all but one of the characters on each line would be within a 250-character set. Each line would then have an extra byte to indicate which character (if any) was the oddball.

 

Thanks for the explanation - I now see the point of the sbx operation in your example - very clever!

 

Chris

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...