It is currently Wed Aug 21, 2019 6:57 am

All times are UTC - 7 hours



Forum rules





Post new topic Reply to topic  [ 10 posts ] 
Author Message
PostPosted: Tue Mar 26, 2019 7:58 pm 
Offline

Joined: Tue Mar 26, 2019 5:49 pm
Posts: 7
Location: Canada
Hello,

(Disclaimer: I am making some assumptions in my post so if anything is wrong corrections are much appreciated)

I am at the beginner stages of learning 65816 for SNES (Been at it for about a week). I've been through and re-read as many newbie friendly documents & tutorials as I can find and will continue to do so. These forums have also been a great help in learning. I'm basically hoarding any information I can find, making my own notes from them, going through examples, and reading as much as I can every day.

Everything has been going quite well, but I also feel a lot of concepts are still lost on me particularly when it comes to graphics for the SNES. I've been trying to learn about DMA & HDMA in particular. The process of performing a DMA transfer makes sense, and the examples I've seen I can understand. What I don't fully understand is how everything (NMI, VBlank, DMA, & HDMA) fits in with each other.

For example I've been looking through this tutorial of making a tic-tac-toe game: https://wiki.superfamicom.org/making-a- ... ic-tac-toe

A question about the code:

- Inside .SNESNATIVEVECTOR of header.inc there is this line:
Code:
NMI VBlank
This means the game goes to the VBlank: routine every single time it is in VBlank? Is the "wai" in the forever: routine necessary in order for this to happen?

A few general questions:

- It seems like DMA is commonly used in conjunction with NMI & VBlank. In other words a DMA transfer is done in the given period of time in which VBlank occurs is that correct? For example if I want to copy tiles from ROM to VRAM, I would wait for VBlank and then initiate a DMA transfer?

- Assuming the above statement is correct, when should HDMA be used? Nearly all the examples I've seen so far only use DMA. Is HDMA meant to be used in conjunction with DMA? For example could I start a DMA transfer on channel 0 and then an HDMA transfer on channel 1?

- In trying to research this topic a bit I've seen posts saying VBlank is longer than HBlank, is that true? If it is true, does that mean DMA is preferred over HDMA as you'll have a longer amount of time which allows more data to be transferred?

I have so many more questions but lots of them will probably be answered in time and this post is already longer than I wanted it to be. Any help and/or answers are much appreciated. In the meantime I am going to keep researching. Cheers!


Top
 Profile  
 
PostPosted: Tue Mar 26, 2019 8:25 pm 
Offline

Joined: Tue Oct 24, 2017 11:07 pm
Posts: 26
1. If you have the vbank interrupt enabled (bit 7 of $4210 set), the processor jumps to wherever in bank 0 you have the NMI vector set to. "wai" isn't necessary for this, it just makes the processor stop execution until it gets an interrupt. If your game loop takes too long it'll also get interrupted (why pushing all the registers to the stack is advisable in your vblank routine).

2. That's correct, since you aren't able to access VRAM outside of vblank (and hblank, but that doesn't give you enough time to do much) or when you turn off display entirely (bit 7 of $2100). Because of the short available time, you want to use DMA as much as possible.

3. HDMA is used for visual effects during display, stuff like messing with the scroll value to do parallax scrolling, changing the window size for a non-square window, etc. You don't want to use it at the same time as DMA because there's a bug in first-revision SNESes that causes the system to lock up when a DMA and HDMA transfer finish at the same time.

4. You might want to look up how analog television works. Horizontal blanking is when drawing is disabled to give time for the TV's electron beam to return to the left side of the screen. It only takes around 10 microseconds (not much time to transfer much data on a system as slow as the SNES). Meanwhile, vertical blanking is when drawing is disabled to give time for the TV's electron beam to return to the top left corner of the screen once it reaches the bottom. It takes around 11% of a frame, or around 1833 microseconds.

Generally the workflow on a SNES game is to get input and process character movement, collision detection, enemy behavior, etc during when the frame is being drawn and then copy the new data into VRAM during vblank.


Top
 Profile  
 
PostPosted: Tue Mar 26, 2019 8:37 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 8533
Location: Seattle
The SNES is a particularly awkward beast to program, and many of the concepts will make more sense if you're more familiar with any other 2nd through 4th generation console first.

JG92 wrote:
Is the "wai" in the forever: routine necessary in order for this to happen?
No. "wai" just means "don't do anything until we're interrupted by some kind of interrupt".

I'm going to reorder your questions:
Quote:
- In trying to research this topic a bit I've seen posts saying VBlank is longer than HBlank, is that true?
Substantially longer.

To understand why, you need to understand how old NTSC/PAL TVs work: a beam of electrons are bent left and right repeatedly, very slowly proceeding down the screen. When those electrons hit the phosphor-coated screen, they become photons, emitting light that you see. example on youtube.

In all of these older consoles, there's a predictable mapping between time, pixels, and CPU instructions.

Because the left-and-right part is faster, there are fewer pixels and fewer CPU instructions available to do things during horizontal blanking.

Quote:
a DMA transfer is done in the given period of time in which VBlank occurs is that correct? For example if I want to copy tiles from ROM to VRAM, I would wait for VBlank and then initiate a DMA transfer?
DMA can be done at any time. DMA is just a way to move bulk data from one location to another; in the SNES it's usually used to copy data from ROM or RAM to RAM or the PPU. However, the PPU can only accept data that controls what's being displayed (pedantically, vs. how it's being displayed) during vertical blanking, when it's not doing anything else.

Quote:
- Assuming the above statement is correct, when should HDMA be used?
HDMA is used for reconfiguring properties of the PPU on a scanline-by-scanline basis.
Quote:
Nearly all the examples I've seen so far only use DMA.
That's because DMA can transfer a single block of up to 65536 bytes, while HDMA can only transfer 4 per scanline.
Quote:
Is HDMA meant to be used in conjunction with DMA?
No, they are almost entirely unrelated. However, a bug in the earliest version of the SNES's CPU will cause a crash if both are used at the same time.
Quote:
For example could I start a DMA transfer on channel 0 and then an HDMA transfer on channel 1?
Only one thing can be using the SNES's data bus at a time; I believe HDMA has precedence over DMA, which in turn has precedence over the CPU. After that, a lower numbered DMA channel has precedence over a higher-numbered one. Because DMA has precedence over the CPU, you can't do anything at all, let laone schedule another thing to start, while DMA is running.

Quote:
does that mean DMA is preferred over HDMA as you'll have a longer amount of time which allows more data to be transferred?
They do entirely different things; they're not really comparable in behavior or intended purpose.


Top
 Profile  
 
PostPosted: Tue Mar 26, 2019 8:57 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4208
Location: A world gone mad
1. I don't know what NMI VBlank in the code would do without knowing what assembler was used and looking at other macros. NMI itself is not a valid 65816 opcode. Given the name of SNESNATIVEVECTOR, this likely implies it's the 65816 interrupt vector table. This is an important CPU-level detail that, if you do not currently understand it, need to stop everything you're doing and do so.

The 65816 has several interrupt vectors. These are 16-bit pointers to code that is run every time a particular interrupt fires. Those vectors, and their addresses in bank $00 in ROM, are as follows:

Code:
       Emulation Mode   Native Mode
IRQ    $FFFE-FFFF       $FFEE-FFEF
RESET  $FFFC-FFFD       n/a
NMI    $FFFA-FFFB       $FFEA-FFEB
ABORT  $FFF8-FFF9       $FFE8-FFE9
BRK    n/a              $FFE6-FFE7
COP    $FFF4-FFF5       $FFE4-FFE5

The part that's going to confuse you is the Emulation Mode vs. Native Mode nomenclature. The 65816 has a 6502/65c02 emulation mode that can be enabled during run-time via something like sec / xce. The CPU, when reset (including power-on), starts with this mode enabled. The CPU sets the PC to the address contained within the vector and begins executing code there; you can think of it like jmp ($fffc) in actual code.

The IRQ vector points to code that should run any time a hardware IRQ fires (this is not the same as NMI) -- think sei and cli (but not always; it's complicated). The purposes of all the other vectors are identical; NMI vector for any time an NMI fires, ABORT vector any time the ABORT signal fires (this is not worth explaining early on; you won't need it), COP vector any time the cop instruction is executed, and BRK vector for any time the brk opcode is executed. And, of course, RESET is the vector that is used when the system is powered on/reset.

The reason there's no BRK vector in Emulation Mode is because the 6502/65c02 lacks such; instead, IRQ and BRK share the same vector.

Your code at those vectors (except for RESET) return with rti.

For vectors which aren't used (ex. ABORT), you should point the vector to an address of code that does rti and nothing else. Do not set them to $0000 or something like that; that would actually cause the interrupt to try and use the value/pointer at $0000-0001 in RAM and then execute code there (e.g. jmp ($0000)). You *can* set the vectors to RAM locations if needed, though, but that's an advanced topic and not worth explaining when trying to learn the system.

Usually what you see when making a vector table is something like this, but it varies per assembler/project/how the coder chose to do it/etc.:

Code:
.org $8000

Vect_RESET:
  ; Your power-on and RESET code here

; This is to keep code from ending up hitting the "rti" below.
infinite_loop:
  jmp infinite_loop

Vect_Unused:
  rti

Vect_NMI:
  ; Your NMI/VBlank code here
  rti

Vect_IRQ:
  ; Your IRQ code here
  rti

.org $FFE4
.dw Vect_Unused    ; $FFE4 / COP native
.dw Vect_Unused    ; $FFE6 / BRK native
.dw Vect_Unused    ; $FFE8 / ABORT native
.dw Vect_NMI       ; $FFEA / NMI native
.dw Vect_Unused    ; $FFEC / unused space
.dw Vect_IRQ       ; $FFEE / IRQ native

.org $FFF4
.dw Vect_Unused    ; $FFF4 / COP emul
.dw Vect_Unused    ; $FFF6 / unused space
.dw Vect_Unused    ; $FFF8 / ABORT emul
.dw Vect_NMI       ; $FFFA / NMI emul
.dw Vect_RESET     ; $FFFC / RESET emul
.dw Vect_IRQ       ; $FFFE / IRQ/BRK emul



2. On to NMI itself: NMI is a hardware interrupt (some may call it a "hardware IRQ" but it differs from the IRQ signal above). It can be used for anything technically, but on the SNES it's wired/tied to VBlank, as long as you've set bit 7 of $4200 to 1 (it defaults to 0). That means any time VBlank happens, the code at the NMI vector is executed. Now you understand "how things happen" at 60 frames a second (or 50 if PAL).

The wai instruction on the 65816 simply waits for a hardware interrupt to fire -- that is NMI, IRQ, ABORT, or RESET. Thus, with the above paragraph's details, you can use this opcode to wait for the start of VBlank. It's more efficient to use this than a pollin loop in 65816, and the CPU itself actually sleeps (goes into a low-power mode) and does nothing else until the interrupt happens.

The amount of time in VBlank -- that is, the time from the start of VBlank until the end (when the PPU begins drawing the screen) -- is limited. It's up to you ensure you don't exceed this. There is no "native way" to determine this, so you'll just have to be careful with what you do in NMI/VBlank. Don't go shoving everything in there! Your main game core/code should be operating *outside* of NMI, and reserve NMI for doing things like PPU RAM and OBJ updates and the like.

3. You can do general DMA any time you wish. If you want to do it in NMI, go right ahead. There may be reasons to do general DMA outside of NMI, depending on what you want to do. But yes, you understand the purpose of it correctly.

As for "when" to do PPU RAM updates: you should not write to PPU RAM while the screen is being drawn. It doesn't matter if you do this natively with code or with DMA. Doing so can/will corrupt what gets drawn on screen.

The rest of your questions lidnariq answered, so no point in covering them.

Edit: couple fix-ups for the demonstration code.


Top
 Profile  
 
PostPosted: Tue Mar 26, 2019 10:34 pm 
Offline

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 1076
lidnariq wrote:
That's because DMA can transfer a single block of up to 65536 bytes, while HDMA can only transfer 4 per scanline.

Note that that refers to one HDMA channel. HDMA is probably the reason there are eight DMA channels; when set up for HDMA, each channel can transfer 1, 2, or 4 bytes during each HBlank, and each channel has its own source data and destination. This allows you to do up to eight different things with HDMA at the same time, depending on what they are (some tasks, such as perspective using Mode 7, require more data per scanline than one HDMA channel can transfer).

With all eight channels doing four bytes each, you can transfer more data with HDMA during all the HBlanks in a frame than you can with DMA during one normal* VBlank.

However, as mentioned, the purpose of HDMA is different, and it's not really useful for the same things as regular DMA. For one thing, with a DMA transfer you can simply start it and write subsequent code under the assumption that it has completed, but with HDMA it transfers a little at a time at intervals tied to PPU timing, making it hard to work into an algorithm as a data mover. As for filling video memory, it is not possible to do VRAM or OAM writes during HBlank (leaving aside nearly useless edge cases like the Uniracers HiOAM trick). CGRAM, on the other hand, is open for business during HBlank, as are a lot of the PPU registers. Any changes will show up on the next scanline, which is another reason HDMA is used mostly for poking registers to produce raster effects rather than for bulk data transfer.

...

HDMA doesn't run during VBlank. It starts at the top of the frame, every frame, and does its thing, ending at VBlank or when all the channels have run out of data, whichever comes first. So if you want to use all eight channels for HDMA and use DMA during VBlank, you can. To do a regular DMA during VBlank, you'd just change the settings of one channel and run it as a DMA channel. Then at the end of your VBlank routine, you'd reset the settings on that channel back to what they were, and everything should work fine as long as your VBlank routine doesn't run long. If you can't guarantee that your VBlank routine won't run long, it's probably best not to use all eight channels for HDMA.

...

HDMA is not, technically, exclusively a graphical tool. It also has access to the WRAM gate and audio I/O ports. Applications are possible. However, they are advanced material, and it's much better to learn HDMA as a tool to produce raster effects because that's mostly what it's for (and because it's easy to see what it's doing).


* You can use forced blank to extend VBlank if you have to, resulting in black bars at the top and/or bottom of the screen.


Last edited by 93143 on Tue Mar 26, 2019 10:49 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Tue Mar 26, 2019 10:49 pm 
Offline

Joined: Tue Mar 26, 2019 5:49 pm
Posts: 7
Location: Canada
First and foremost thank you to all 4 of you for your replies. Each one has helped me understand much better certain things I didn't understand before. I'll definitely have to come back and re-read them numerous times though until it's hammered into my brain. However, my initial takeaways are:

- All "wai" does is stop program execution until an interrupt is fired.

- Setting the 7th bit of $4200 enables NMI. I can see this in the tic-tac-toe code (also joypads apparently but I haven't focused on that yet):
Code:
lda #%10000001   ; enable NMI and joypads
sta $4200

$4210 sets the 7th bit to 1 when NMI is to ocurr? When it's read, that 7th bit(of $4210) gets set back to 0?

- DMA can be done any time, but for the purpose of transferring graphical data (not sure if this is the right term?) to update the screen this needs to be done during VBlank.

- DMA and HDMA are used for completely different things. Sounds like I probably want to wait until I'm more familiar with the basics to learn more about HDMA. I just watched this video that goes into HDMA. Talks about manipulation and effects that can be done with it and mode 7 although it's way beyond my skill level right now (still great visuals that help in understanding though). Also don't try running DMA & HDMA concurrently as there were bugs causing SNES to crash.

- VBlank duration is much longer than HBlank and does allow for more data to be transferred (in terms of a 1:1 comparison between VBlank and HBlank). That slomo video was extremely helpful in understanding that (thank you)

- Need to do some more research on interrupt vectors and 65816 interrupt vector table.

Quote:
The SNES is a particularly awkward beast to program, and many of the concepts will make more sense if you're more familiar with any other 2nd through 4th generation console first.


Unfortunately I am not really familiar with any other consoles of those generations :( The only thing I have coming into this is some experience in a different instruction set (68HC12) although it's quite a stretch coming to this and I haven't done anything with 68HC12 in years. I was considering trying to learn NES/6502 first although SNES holds a special place in my heart and I'm enjoying it so far.

Quote:
1. I don't know what NMI VBlank in the code would do without knowing what assembler was used and looking at other macros. NMI itself is not a valid 65816 opcode. Given the name of SNESNATIVEVECTOR, this likely implies it's the 65816 interrupt vector table. This is an important CPU-level detail that, if you do not currently understand it, need to stop everything you're doing and do so.


I will definitely look more into what you've mentioned. For reference I am using the WLA Assembler version 9.8a. Before posting I had a brief look at the documentation here which provides a short description, although if I'm being completely honest I didn't understand what I was reading. With your extremely helpful answer I have a much better idea of what those are but I'll also study it some more. Assembler specific concepts confused me a good bit in the beginning, especially since I am saving any and all information I can find. Most tutorials/guides I've seen have used WLA (particularly on the SFC Dev Wiki) so I've stuck with that so far, although I've also seen xkas, xa, & cc65.

Once again if anything in my notes is wrong any corrections are much appreciated. I know it probably looks redundant writing out what everyone has already said, but for my benefit it definitely helps and I can add them to my existing notes. Also who knows maybe it will help someone else googling in the future. I stumbled upon more than a few forum posts on here that have helped me out immensely so far :D Anywho thanks again for the replies and assistance.


Top
 Profile  
 
PostPosted: Tue Mar 26, 2019 11:36 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4208
Location: A world gone mad
JG92 wrote:
- Setting the 7th bit of $4200 enables NMI. I can see this in the tic-tac-toe code (also joypads apparently but I haven't focused on that yet):
Code:
lda #%10000001   ; enable NMI and joypads
sta $4200

$4210 sets the 7th bit to 1 when NMI is to ocurr? When it's read, that 7th bit(of $4210) gets set back to 0?

That's correct. The documentation on all of this is not exactly clear, so I'll try to explain it:

When MMIO register $4200 bit 7 is 1, MMIO register $4210 bit 7 will tell you the current status of NMI/VBlank (1=in VBlank, 0=out of VBlank). However, reading $4210 has a downside: if you read it, bit 7 will get automatically reset (back to 0). Thus, you really should not be "polling" this register to determine "am I in VBlank" -- you don't need to: you know you're in VBlank when $4200 bit 7 is 1 and NMI fires. Keep it simple. That said: there is one thing you need to do: in your NMI routine, you need to read $4210 once at the very end of your NMI routine (i.e. before rti) to clear the NMI status flag itself. This is important for reasons I'd rather not get into, but the NES PPU (bit 7 of $2002) works the same way. There is a wiki page about this for the SNES.

As for bit 0 of $4200: when set to 1, this enables the SNES to start auto-populating MMIO registers $4218-421f (joypad data for controllers 1 through 4). However, before polling the data from those registers, you should check $4212 bit 0 to see if the SNES is in the process of doing the auto-population or not (1=it's busy, 0=it's done). A simple wait loop like loop: lda $4212 / and #1 / bne loop is sufficient. You can do this in NMI/VBlank, however you should do it at the very end of the routine because it takes the SNES a bit of time before the data is ready. There is a wiki page about all of this as well (though no mention of the don't-do-this-at-the-very-start-of-VBlank delay I speak of). Some people don't bother to do the wait loop at all because they have lots of previous operations in NMI that take up the required time. There's no harm in having it though.

JG92 wrote:
- DMA can be done any time, but for the purpose of transferring graphical data (not sure if this is the right term?) to update the screen this needs to be done during VBlank.

That's the correct phrase. Because of some of your grammar, I have to ask you: is English your native language? If not, what is your native tongue? We have many people here who speak multiple languages and sometimes explaining things natively is easier.

JG92 wrote:
- DMA and HDMA are used for completely different things. Sounds like I probably want to wait until I'm more familiar with the basics to learn more about HDMA. I just watched this video that goes into HDMA. Talks about manipulation and effects that can be done with it and mode 7 although it's way beyond my skill level right now (still great visuals that help in understanding though). Also don't try running DMA & HDMA concurrently as there were bugs causing SNES to crash.

Correct. And yes, do not bother looking at HDMA right now -- it's an advanced topic. You're trying to learn the system, don't bother with HDMA right now. Understanding general purpose DMA is good enough and is important to learn.

JG92 wrote:
- Need to do some more research on interrupt vectors and 65816 interrupt vector table.

65816 CPU documentation will cover this. Here are some reference materials.

Quote:
I will definitely look more into what you've mentioned. For reference I am using the WLA Assembler version 9.8a. Before posting I had a brief look at the documentation here which provides a short description, although if I'm being completely honest I didn't understand what I was reading. With your extremely helpful answer I have a much better idea of what those are but I'll also study it some more. Assembler specific concepts confused me a good bit in the beginning, especially since I am saving any and all information I can find. Most tutorials/guides I've seen have used WLA (particularly on the SFC Dev Wiki) so I've stuck with that so far, although I've also seen xkas, xa, & cc65.

So, the answer is this: .SNESNATIVEVECTOR / .ENDNATIVEVECTOR and .SNESEMUVECTOR / .ENDEMUVECTOR are "section-ised" pseudo-ops that use their own unique format to define the vectors I spoke of. The way I demonstrated shows you exactly how to do it regardless of assembler, but WLA DX offers a way to make this easier on the programmer. So, the WLA DX version of what I wrote would be this:

Code:
.SNESNATIVEVECTOR
COP    Vect_Unused
BRK    Vect_Unused
ABORT  Vect_Unused
NMI    Vect_NMI
UNUSED $0000
IRQ    Vect_IRQ
.ENDNATIVEVECTOR

.SNESEMUVECTOR
COP    Vect_Unused
UNUSED $0000
ABORT  Vect_Unused
NMI    Vect_NMI
RESET  Vect_RESET
IRQBRK Vect_IRQ
.ENDEMUVECTOR

The UNUSED label you see within those sections refers to a 2-byte "part" that isn't used (in each mode) -- i.e. $FFEC-FFED in native mode, and $FFF6-FFF7 in emulation mode. The examples set these to $0000 and that's fine; just don't set the non-UNUSED ones to $0000 (see previous post, re: RAM).

Good luck.


Top
 Profile  
 
PostPosted: Wed Mar 27, 2019 10:13 am 
Offline

Joined: Tue Mar 26, 2019 5:49 pm
Posts: 7
Location: Canada
This is excellent information thank you. I'll keep reviewing, re-reading, and checking out the links provided in the thread.

Quote:
That's the correct phrase. Because of some of your grammar, I have to ask you: is English your native language? If not, what is your native tongue? We have many people here who speak multiple languages and sometimes explaining things natively is easier.


The simplest answer is I just write poorly and/or awkwardly sometimes. The longer answer is English is one of my two native languages. I live in a bilingual part of Canada near Quebec where it's pretty close to 50% French and 50% English. I grew up learning everything in French as a kid but primarily using English with friends and family. There's also Spanish and to a lesser extent Norwegian in the mix but not to the same degree of proficiency as the other two. These days I mostly use English and Spanish between work, home and friends.

All that being said I am alright with learning in English. I see there's an international section on the forum which could be fun for posting things in various languages when I'm more knowledgeable on 65816. I was also originally planning to compile a bunch of my notes, translate them, and post them if that sort of stuff is encouraged. (These days Google Translate is ... OK'ish so I haven't translated anything yet)

Edit: When I say Google Translate is OK'ish I only mean for Spanish and French in that it can usually give you the gist of what something says more or less. :D Granted there's plenty of exceptions to that rule.


Top
 Profile  
 
PostPosted: Wed Mar 27, 2019 12:12 pm 
Offline

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 1076
lidnariq wrote:
The SNES is a particularly awkward beast to program, and many of the concepts will make more sense if you're more familiar with any other 2nd through 4th generation console first.

Wait a minute. Let's not get him busy learning the Atari 2600, shall we? Speaking of awkward beasts to program...

The second generation was way nastier than the SNES IMO. The standard tile-based video chip concept used in everything from the Famicom to the Neo Geo didn't really take hold until the early '80s, so a lot of the classic 2nd-gen consoles did their own weird thing. The Atari VCS (aka Atari 2600, by far the most popular 2nd-gen console) required the programmer to spend the entire active frame riding the graphics chip because it only had enough memory for half a scanline. The Fairchild Channel F and Bally Astrocade were framebuffer-based and thus had more in common with the PlayStation 4 than with the SNES. The Vectrex didn't use raster graphics at all. The Intellivision, ColecoVision and Atari 5200 were the only ones that seem to have used a proper tiled sprite/BG system, and they all came in towards the end.


Top
 Profile  
 
PostPosted: Wed Mar 27, 2019 1:02 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 8533
Location: Seattle
Getting a simple still picture drawn on the 2600 isn't hard at all; the hard part is juggling everything to differ between vertical retraces.

My point was mostly avoiding any console that used a framebuffer, which was ubiquitous starting with the 5th generation, but there are a number of also-rans during the 3rd (some GX4000 and some XEGS games) and 4th (CD-i) generation that were also framebuffer-based.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: Google Adsense [Bot] and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group