- Videomation and CrossPaint are paint programs letting the player draw arbitrary primitives.
- Qix lets the player draw fences over a playfield, much like an Etch-A-Sketch art toy. When the player divides the floor in two with a fence, the game fills in pixels on the side farther from the enemy.
- Elite is a space trading game with wireframe vector graphics. The released version relies on PAL NES PPU timing but could be ported to Dendy with little trouble if lead developer David Braben wanted to. Elite co-developer Ian Bell also produced Tank Demo, a tech demo of a Battlezone clone using the same principle as Elite.
NES lookalike graphics
Moderator: Moderators
Re: NES lookalike graphics
Dendy and other PAL famiclones have the same capability as NES in this respect. Games that draw primitives, such as points, lines, and ellipses, do so by plotting pixels into tiles for the PPU to display.
Re: NES lookalike graphics
Thank you so much for the answer! Could you give an example of code that draws a line or at least a dot on the screen? I started studying cc65 for NES but I don't understand yet how to draw a point with certain coordinates. Maybe it needs to be done in assembler?
Re: NES lookalike graphics
The NES, by default, does not provide enough video memory to draw arbitrarily single pixels.
(Math: 256 × 240 pixels × 2 bits per pixel ÷ 8 bits per byte = 15360 bytes)
This is why Elite and Color a Dinosaur have so much empty space: they only have 8192 bytes for all the lines they've drawn, and so they fill most of the screen real estate with blank.
So your options, in order of increasing software complexity, are:
1- Don't draw arbitrary single pixels. Instead, use a fixed library of tiles made up of multiple native pixels, like the Game Genie software does. (The Game Genie's library of tiles is the 16 combinations of 4x4 pixel squares in each corner.)
2- Use extra hardware on the cartridge to permit addressing more video memory, like mappers 13 or 96, or change the interpretation of the video memory to 1bpp, like mapper 371.
3- Dynamically allocate tiles as you draw single pixels, keeping track of both the tilemap and the tiles, and update both. This is what commercial software did, due to the other options being too coarse or too expensive.
(Math: 256 × 240 pixels × 2 bits per pixel ÷ 8 bits per byte = 15360 bytes)
This is why Elite and Color a Dinosaur have so much empty space: they only have 8192 bytes for all the lines they've drawn, and so they fill most of the screen real estate with blank.
So your options, in order of increasing software complexity, are:
1- Don't draw arbitrary single pixels. Instead, use a fixed library of tiles made up of multiple native pixels, like the Game Genie software does. (The Game Genie's library of tiles is the 16 combinations of 4x4 pixel squares in each corner.)
2- Use extra hardware on the cartridge to permit addressing more video memory, like mappers 13 or 96, or change the interpretation of the video memory to 1bpp, like mapper 371.
3- Dynamically allocate tiles as you draw single pixels, keeping track of both the tilemap and the tiles, and update both. This is what commercial software did, due to the other options being too coarse or too expensive.
Re: NES lookalike graphics
It means to operate only 4x4 pixels and only ready-made sprites. I am saddened :о(
Re: NES lookalike graphics
Because Videomation is one of the exceptions that uses a mapper (in this case, Mapper 13) to increase the amount of VRAM the NES can use.
Videomation uses 16k of additional VRAM in the cartridge, which is then addressed as 4 kilobyte banks (because the NES can only see 8 kilobytes of graphics data from the cartridge at one time, and Videomation uses half of that space to point to a single fixed bank) that are swapped multiple times during display to bypass the NES's limitation of 256 characters in a tile map. (In this case, the tilemap is composed mainly of the 256 tile values repeated multiple times in different sections, and the VRAM banks are swapped so those values point to different tiles for each section of the "virtual" framebuffer)
Re: NES lookalike graphics
Okay, let's say I invent a NES-CPROM board, how do I program a color dot on the screen?
Re: NES lookalike graphics
If you wanted to put a dot on screen, you would need to both follow these same steps on almost any other CHR-RAM (CHR-RAM means external graphics VRAM in NESdev lingo) NES board, including modern ones like UNROM-512, and some specialized steps for the NES-CPROM board. This assumes you have code that initializes the system first, before doing this.
You'd set up a palette with the colors and a tile map that contains an entry for the tile you want to write to that is set to the correct palette, and during vertical blank, you'd start by write tile data to the CHR-RAM by first setting up the PPU data address to point to the tile you want to change. (through writing the PPU address register PPUADDR twice, to write two 8 bit halves of the 16 bit PPU address)
Then (still during vertical blank), you would write the graphics data using the PPUDATA register, making sure to write to both of the tile's bit planes.
The unique part for the NES-CPROM board, if you're writing a function that plots a dot anywhere on a screen set up like Videomation, is that before writing the graphics data, you need to write the CHR-RAM mapping register to select the CHR-RAM bank that corresponds with the region of screen you want to draw to.
Keep in mind that, as with all programmable tile map or character map displays, on the NES with any CHR-RAM board you can easily plot a single dot anywhere on screen, but if you want to draw hundreds of dots randomly across the screen you'll likely run into the 256 character limit of the NES (that other early tile/character map systems like the VIC-20 also have) and will need a board that can bypass that limit by banking CHR-RAM, like the NES-CPROM board or the modern UNROM 512 board.
Re: NES lookalike graphics
How can a tail be rewritten pixel to VRAM? For example: before drawing a sprite from tiles, I change the pixels in an 8x8 tile, save and output the sprite. How to do it?
Re: NES lookalike graphics
Do you mind telling us what exactly you're trying to make for the NES? I'm asking because plotting pixels is not something the NES was designed to do, so it's a very slow process that's hard to integrate into an actual game.
While the basic idea behind it doesn't change much, depending on how fast or dynamic you need things to be, we might have different suggestions on how to approach this task.
Do you know how to use tiles in a normal NES program? A program where you can manipulate the tiles is exactly the same, you still have to setup the name tables, attribute tables, palettes and OAM in order to make the NES draw the tiles, but since you're using CHR-RAM, you can edit the tiles as the program runs. Whenever you modify a tile in the pattern tables, all instances of that tile used in the background or on sprites will automatically change.
If you just want a "blank canvas" to draw pixels on, the first thing to do is to setup the name table to use tiles 0, 1, 2, 3, 4, etc. all the way up to 255. If your "canvas" is bigger than 256 tiles, you're gonna need extra VRAM, and raster effects to swap the patterns mid-screen. Don't forget to also setup the corresponding attribute table to assign palettes to the tiles, and the palette itself.
Once the background is all set up, you can start updating the pattern table to draw pixels. The exact formula to calculate the address of the tile to change will vary depending on the size and position of your "canvas", so I need you to tell me more about your project in order to provide more useful information. The general idea is that you use the higher bits of the pixel's X and Y coordinates to calculate the address of tile that needs to be updated, the 3 lowest bits of the Y coordinate to select a line in that tile, and the 3 lowest bits of the X coordinate to select bitmasks that can be used to manipulate the bits/pixels in that line.
While the basic idea behind it doesn't change much, depending on how fast or dynamic you need things to be, we might have different suggestions on how to approach this task.
Do you know how to use tiles in a normal NES program? A program where you can manipulate the tiles is exactly the same, you still have to setup the name tables, attribute tables, palettes and OAM in order to make the NES draw the tiles, but since you're using CHR-RAM, you can edit the tiles as the program runs. Whenever you modify a tile in the pattern tables, all instances of that tile used in the background or on sprites will automatically change.
If you just want a "blank canvas" to draw pixels on, the first thing to do is to setup the name table to use tiles 0, 1, 2, 3, 4, etc. all the way up to 255. If your "canvas" is bigger than 256 tiles, you're gonna need extra VRAM, and raster effects to swap the patterns mid-screen. Don't forget to also setup the corresponding attribute table to assign palettes to the tiles, and the palette itself.
Once the background is all set up, you can start updating the pattern table to draw pixels. The exact formula to calculate the address of the tile to change will vary depending on the size and position of your "canvas", so I need you to tell me more about your project in order to provide more useful information. The general idea is that you use the higher bits of the pixel's X and Y coordinates to calculate the address of tile that needs to be updated, the 3 lowest bits of the Y coordinate to select a line in that tile, and the 3 lowest bits of the X coordinate to select bitmasks that can be used to manipulate the bits/pixels in that line.
Re: NES lookalike graphics
The idea is this: I want to create an application with primitive pseudo 3D objects that can be zoomed in, rotated, and zoomed out. Objects should be as primitive as possible, from straight lines. For example, a person draws a rectangle of lines, and the program zooms out or zooms in on this object. It also gives the impression that this is a 3D object, because it can also spin.
Re: NES lookalike graphics
I see... so it's basically wireframe 3D then, like in Elite? I guess you can prototype it using basic pixel and line drawing code, and optimize later if you need more speed.
In the simplest possible case (no obscure mappers, raster effects or other extra things to worry about), you'd have a 16x16-tile reagion somewhere in the name table, whith tiles numbered 0 through 255, from left to right, top to bottom. This is a 128x128-pixel region, so coordinates will range from 0 to 127, so 7 bits each:
X coordinate: *XXXXxxx
Y coordinate: *YYYYyyy
The upper case bits will be used to locate the tile that needs to be edited, while the lower case bits will be used to locate the pixel.
To calculate the address of the tile, you must shift and combine the bits into the following format: YYYYXXXX0000
NES tiles are 2bpp, and they're stored as 2 consecutive 1bpp tiles, so first you'll see the 8 lines of the first plane, then the 8 lines of the second plane. The yyy bits will tell you which of the 8 lines you have to change. For example, if yyy is %101 (5), you'll have to change 1 bit in the 5th byte of plane 0, and 1 bit in the 5th byte of plane 1, in order to draw a complete pixel.
Here's a straightforward subroutine in assembly that will write a pixel anywhere on a 16x16-tile (128x128-pixel) region:
Note that this is editing a buffered copy of the tiles in RAM (PatternBuffer), which can be accessed at any time. To actually see the changes you have to upload the buffered data to VRAM during vblank or forced blanking. Updating all 256 tiles takes a lot longer than the duration of vblank, so you will have to spread the update across multiple frames.
Also note that drawing anything with this code will be incredibly slow.
In the simplest possible case (no obscure mappers, raster effects or other extra things to worry about), you'd have a 16x16-tile reagion somewhere in the name table, whith tiles numbered 0 through 255, from left to right, top to bottom. This is a 128x128-pixel region, so coordinates will range from 0 to 127, so 7 bits each:
X coordinate: *XXXXxxx
Y coordinate: *YYYYyyy
The upper case bits will be used to locate the tile that needs to be edited, while the lower case bits will be used to locate the pixel.
To calculate the address of the tile, you must shift and combine the bits into the following format: YYYYXXXX0000
NES tiles are 2bpp, and they're stored as 2 consecutive 1bpp tiles, so first you'll see the 8 lines of the first plane, then the 8 lines of the second plane. The yyy bits will tell you which of the 8 lines you have to change. For example, if yyy is %101 (5), you'll have to change 1 bit in the 5th byte of plane 0, and 1 bit in the 5th byte of plane 1, in order to draw a complete pixel.
Here's a straightforward subroutine in assembly that will write a pixel anywhere on a 16x16-tile (128x128-pixel) region:
Code: Select all
DrawPixel:
;X = pixel X (0 to 127)
;Y = pixel Y (0 to 127)
;A = pixel color (0 to 3)
;saves the inverted color for later
eor #$ff
sta PixelColor
;calculates the low byte of the pointer to the tile
txa
and #%01111000
asl
sta Pointer+0
;calculates the high byte of the pointer to the tile
tya
and #%01111000
lsr
lsr
lsr
ora #>PatternBuffer
sta Pointer+1
;gets the position of the line within the tile
tya
and #%00000111
tay
;gets the position of the pixel within the line
txa
and #%00000111
tax
;creates a byte with bit 0 of the color in the correct position
lda #$00
lsr PixelColor
adc #$ff
and PositiveMasks, x
sta PixelPlane0
;creates a byte with bit 1 of the color in the correct position
lda #$00
lsr PixelColor
adc #$ff
and PositiveMasks, x
sta PixelPlane1
;updates the tile's first plane
lda (Pointer), y
and NegativeMasks, x
ora PixelPlane0
sta (Pointer), y
;updates the line index to access the second plane
tya
ora #%00001000
tay
;updates the tile's second plane
lda (Pointer), y
and NegativeMasks, x
ora PixelPlane1
sta (Pointer), y
;returns
rts
PositiveMasks:
.byte %10000000
.byte %01000000
.byte %00100000
.byte %00010000
.byte %00001000
.byte %00000100
.byte %00000010
.byte %00000001
NegativeMasks:
.byte %01111111
.byte %10111111
.byte %11011111
.byte %11101111
.byte %11110111
.byte %11111011
.byte %11111101
.byte %11111110
Also note that drawing anything with this code will be incredibly slow.
Last edited by tokumaru on Tue Dec 27, 2022 12:15 pm, edited 1 time in total.
Re: NES lookalike graphics
void main (void) {
All_Off(); // turn off screen
X1 = 0x7f; // starting position of the top left sprite
Y1 = 0x77; // near the middle of screen
Load_Palette();
Reset_Scroll();
All_On(); // turn on screen
while (1){ // infinite loop
while (NMI_flag == 0); // wait till NMI
NMI_flag = 0;
}
}
All_Off(); // turn off screen
X1 = 0x7f; // starting position of the top left sprite
Y1 = 0x77; // near the middle of screen
Load_Palette();
Reset_Scroll();
All_On(); // turn on screen
while (1){ // infinite loop
while (NMI_flag == 0); // wait till NMI
Code: Select all
//I can't figure out how to insert assembler code here?
NMI_flag = 0;
}
}
Re: NES lookalike graphics
Code: Select all
lda (Pointer), y
Re: NES lookalike graphics
added #include "nes.h" and error while compiling
"This module may only be used when compiling for the NES!"
"This module may only be used when compiling for the NES!"