Page 3 of 4

Re: pyNES: writing NES games in Python

Posted: Mon Dec 03, 2012 8:08 pm
by gutomaia
Hi guys, sorry for the delay. I'm the author of the project.

First, I really would like to thanks for sharing.

Second, No it's not fake. pyNES is meant to be a python compiler for NES. However it is being built in a different way.

Usually compilers are formal. Take the "C" compiler for example, it evolves along with the processor in their time. The number of instructions and the optimizations where really tight couple with the processor instructions of their launch. As the language, "C" programmers also evolved. Changing the static linking for dynamic link as more memory became available. That leads to GRASP patterns and then OOP and C++. 'Course, even C and C++ have stopped as a language it self, however the compiler didn't stop. Nowadays processors have multicore specific instructions and of course compilers must deal with those optimizations using some kind of pattern matching. That leads as to Virtual machines like JVM, .NET, pypy. Witch can deal of optimizations in layers using hotspot.

Therefore, why not use pattern matching for transitions from a highlevel language to a limited processor?

NES developers knows how developing for a such limited machine differs from actual highlevel programming. Most of the logic is decoded into the most simplest instructions. Complex structure types are hacked into address and ways of reading it.

At first pyNES was just an ASM compiler compatible with nesasm. However after nodeNES (a javascript IDE, not finish also). I need to go further with pyNES. Then, Klaus Silveira, a friend cames if a challenge to write nes games in python. Okay, at the start he was just kidding, but after some rounds in street fighter (witch Zangief beats him pile dive after pile diver), we imagine that it was possible.

Okay

That was the intention was the aim of pyNES. Write NES games in Python. However, I need to add some constrains. And there goes:

1) The approach:

- It's not a formal compiler. Structures are not pre-defined. Same goes for types
- TDD drives the format, at the end, the software will emerge.
- I must keep the hack felling! It must feels like a hack.

2) The Values:
- I must respect python, although it's not a full python compiler and it will only use a subset of python. That subset must be respected.
- I must respect NES developers. When I started my NES ASM research, I saw how clever and fantastic was developing for NES. The referenced python code must generate an ASM code that a NES developer would write. A code that could be found here.

3) The Goals:
The main goal is to build 3 NES games entire in python:
- A Sidescroller Plataform
- A TopDown Shooter
- A RPG

Okay, so much for the compiler introduction and for the project goals. Let me explain in the bits:

How to map the types?

For example, how to map an Int?
- if it is used but not assined the value could be directed assigned in the asm, like in a local scope
- If it is used and assigned but not changed, it could be a const.
- If it is assigned and changed it must be used as an .RS

And for a more complex type, like a List? I have to put some extra constrains here
- List cannot be changed after used by a bitpak.
- Lists can have constants or int, cannot have objects or strings
- Lists are a dataset of sequencial address in the bank 1

And a String? more constraints here, cause String works like a list.
- String cannot be changed after used by a bitpak.
- String are mapped by a String that defines the charset and also end String char. So it can use the assets on the CHR

What the hell are those bitpak?
- Highlevel languages dosent provide much of label instructions. Therefore there is no GOTO. Instructions like a WAITVBLANK, would be very strange.The correct would be a While loop, but that is not what happen at hardware level. So I created a way to gather a bunch of asm instructions in a single function.

How FOR would be made? In python, for usually uses dynamic loops with a list. Like "for a in [1,2,3,4]:"
- I told, i must respect python, so loops must keep that way, but nes dosent have ways to store that dynamic part. And that must be hacked.
- if the for is in a sequential series of arguments, it must be just use a comparison with the last value.
- if the for is in a list that is stored in the cart, like a String or List
- If the list is not stored and the cart has space, so why not store it :D.

Well, I'm still refactoring most of the hacks right now and starting the docs. Klaus Silveira help me out with the site. Gabriel Ozeas help me with some erros on travis CI. Docs are coming, more examples also.

Right now: THE DOCUMENTATION IS IN ANOTHER CASTLE!

There are more features related with image and direct conversion to the chr and nametables.

Anyone can contribute with the project. If you dont know how, just ask me. If you have a ASM example that does compile with NESASM, just send me. I will add it as a project fixture.

PS: if some guys could really help me with some ASM questions and specific nes stuff.

Re: pyNES: writing NES games in Python

Posted: Tue Dec 04, 2012 7:08 am
by slobu
Thanks for your work gutomaia! This sounds like a VERY exciting project.

The only way I can help is by being a very "beginner" user of pyNES. If you've read this topic you can see I already have problems compiling mario.py

We are just guessing the correct way to install pyNES right now. I hope installation instructions are the first documentation to emerge from the castle :)

Also, what should I know before using pyNES for the first time? Any good Python tutorials you would recommend?

Re: pyNES: writing NES games in Python

Posted: Wed Aug 07, 2013 2:32 pm
by NESmaniac
Wait... That's a sign!

If the NES could have one, other systems can!

The Atari, SNES, Playstation, Dreamcast, even N64!!!!!

:D The possibilities are just endless!!! :D

...Anyways, this is super cool!

Re: pyNES: writing NES games in Python

Posted: Wed Aug 07, 2013 3:13 pm
by slobu
The last commit he made was from June 24, 2013 so it may not be dead. That said, the author hasn't posted here in quite awhile. No one really figured out how to install and get this working correctly either.

Re: pyNES: writing NES games in Python

Posted: Wed Aug 07, 2013 6:32 pm
by strat
I think some of us actually did; it's just not very interesting because it's all wrappers for pre-existing asm functions, not something that compiles python into asm code.

Re: pyNES: writing NES games in Python

Posted: Sun Oct 20, 2013 8:22 pm
by darkhog
I wish author would man up and write docs for its tool. I've started deciphering it and writing any findings to text file (mainly trial & error), but I've just learned about it few hours ago so I don't have much (i.e. none). Also bundled examples are uncompilable, because mario.chr file is missing from package (and also github repo).

Replaced it with some random chr (ripped from Super Mario Bros rom), but obviously it doesn't have proper graphics which makes further reverse-engineering difficult.

I'll try to do more research later and when I'm done documenting it, I'll distribute version with all fixes discussed earlier in this thread and proper installations instructions.

Note that aside of patching few things up and documenting stuff I'm in no position to actually maintain this as I don't know much about NES assembly (one of reasons I've seek tool like this) nor would be able to do compiler/assembler. So after I'm done someone with actual knowledge of python, writing custom compilers and NES assembly would need to take over.

I'm attaching file from my experiments (compiled version of mario.py with garbled graphics due to wrong CHR file which wasn't supplied by author). Can someone with access to flashcarts or things like that confirm if it run on real hardware?

Re: pyNES: writing NES games in Python

Posted: Mon Oct 21, 2013 9:51 am
by darkhog
OK, I've found out that "heart" of this thing (i.e. commands you'll be putting when making your own game) is in pynes/bitbag.py. I'll start deciphering it now and will post any findings.

Re: pyNES: writing NES games in Python

Posted: Mon Oct 21, 2013 11:09 am
by darkhog
Here is first rudimentary version of docs:
docs.txt
(2.6 KiB) Downloaded 165 times
Sadly, pyNES lacks any kind of sound-handling functions. Maybe someone with python and NES asm knowledge could write some functions that could load NSFs made with FamiTracker and play specific songs with ability of mixing some samples in? I'm unfortunately not up for the task.

Re: pyNES: writing NES games in Python

Posted: Mon Oct 21, 2013 4:04 pm
by darkhog
How to add own procedures/functions

OK, so I've figured out how code templates work in bitbag.py. It may be use for someone knowledgeable with NES assembly so we can make it somekind of forum project (so less knowledgeable people who would want to make NES games would be able to do so).

Each "function" you can use in your pyNES game is defined in bitbag.py as class. Example of one such class:

Code: Select all

class define_sprite(BitPak):

    def __init__(self, game):
        BitPak.__init__(self, game)

    def __call__(self, x, y, tile, attrib=0x80):
        assert isinstance(x, int)
        assert isinstance(y, int)
        assert isinstance(tile, int) or isinstance(tile, NesArray)
        return NesSprite(x, y, tile, attrib)
It is definition for define_sprite(). __init__(self,game) is used for initialization of procedure. BitPak.__init__(self,game) is always present so it is important. There can be another initializations, like here (__init__ from load_sprite() function):

Code: Select all

def __init__(self, game):
        BitPak.__init__(self, game)
        self.game.has_nmi = True  # TODO remove this
        self.game.ppu.sprite_enable = True
        self.game.ppu.nmi_enable = True
Now, I don't know yet how those interactions here exactly work, but in time I or someone else can figure it out. Though it seems name are self-explanatory.

Let's get back to our define_sprite class, shall we?

Code: Select all

class define_sprite(BitPak):

    def __init__(self, game):
        BitPak.__init__(self, game)

    def __call__(self, x, y, tile, attrib=0x80):
        assert isinstance(x, int)
        assert isinstance(y, int)
        assert isinstance(tile, int) or isinstance(tile, NesArray)
        return NesSprite(x, y, tile, attrib)
__call__ is executed when you call function. 'self' always seems to need to be present, but isn't part of call when you write game. You can define any number of parameters afterwards or don't define at all, as it is case for e.g. wait_vblank, which __call__ is defined as __call__(self).

There are also two optional functions you can define in class, "asm(self)" and "procedure(self)". This is actually meat of code, it is what makes code execute in game as it is part of asm generation. You just write your function in assembly here so you will not have to write it again each time and instead use easy Python syntax for game ;).

asm(self) seems to be for pre-call initialization and jump to your procedure, while procedure(self) contains your code along with label, though it is probably just nomenclature and you could just dump your code in either.

Also bonus, here is (probably working, didn't test it) definition for function that will run custom ASM

Code: Select all

class run_assembly(BitPak)
    def __init__(self, game):
        BitPak.__init__(self, game)
    def __call__(self,assembly):
       self.asm = assembly
    def asm(self)
       return self.asm
Note that assembly code should be written as in bitbag.py asm/procedure functions, i.e. no .org/etc directives, only pure opcodes delimited by new line (\n) character, with two spaces before actual opcode (though last one may be just for formatting sake and may not have influence over compiling it).

Re: pyNES: writing NES games in Python

Posted: Mon Oct 21, 2013 4:43 pm
by Shiru
darkhog wrote:Maybe someone with python and NES asm knowledge could write some functions that could load NSFs made with FamiTracker and play specific songs with ability of mixing some samples in? I'm unfortunately not up for the task.
It does not really works this way, with loading and mixing NSFs. NSF format is not designed to share system resources with a program, i.e. a program has to work around whatever NSFs requires (could be anything), in terms of memory allocation etc. FamiTracker player is not designed to be used in games, it does not care about memory (RAM/ROM) and CPU time consumption too much, and does not have working sound effects support. Samples could not be 'mixed in' on the NES. So if you want to go easy way with music authoring, you pay for it with high resource use and necessity to write extra code for sound effects support. Alternative is to use one of existing sound engines that were created by various people exactly to be used in games, i.e. they trying to save resources and allow to play sound effects. However, they usually less easy in the authoring part, although FamiTracker is still an (existing or implementable) option as an authoring tool, just with limited subset of its features supported.

Re: pyNES: writing NES games in Python

Posted: Mon Oct 21, 2013 4:52 pm
by darkhog
Samples could not be 'mixed in' on the NES
So why game like Mario, Megaman, etc. doesn't stop music when sound (jump, shoot, etc.) is played and play both simultaneously?

Anyway, I can live with using small subset of Famitracker's functionality (most of I can live without) and I've seen homebrew games with music made in FT so it is possible to make it work.

Also, Famitracker can export to BIN (raw music data) which can be then "interpreted" by pyNES compiler and incorporated in its own driver (when it will have one) or ASM source - from under File->Create a NSF... window, so it is not completely impossible to use it.

Re: pyNES: writing NES games in Python

Posted: Mon Oct 21, 2013 5:47 pm
by Shiru
It is not 'sample mixing', as there are no samples (waveform recordings) for sound effects in most NES games. Sound effects on NES is kind of a sub songs, sometimes interpreted by song player as one of channels, sometimes by its own separate player, in any case, using one of ways of channel replacement rather than actual mixing (addition).

If you look closer, homebrew games that has music played by actual FamiTracker player does not have sound effects. To play the BIN data that FamiTracker exports, you would need to use FamiTracker player, or create a substitute. If you want to just use FamiTracker as front end for your own player/format, a much easier and better way is to use text export (built-in in latest versions). That's how games that has music created in FamiTracker and sound effects usually works.

Re: pyNES: writing NES games in Python

Posted: Mon Oct 21, 2013 6:43 pm
by tepples
The closest you get to a "mixer" in NES audio is a channel priority system. My sound player chooses which pulse channel ($4000 or $4004) to play an effect on based on which has less time left in the effect already playing. On each channel, it runs a musical instrument and a sound effect in parallel and chooses which to play on the PSG based on whatever is louder at the moment, allowing a loud note attack to overpower the tail end of a sound effect. And it treats drums as pairs of sound effects, allowing triangle kick to interrupt a bass line in a sane way.

Re: pyNES: writing NES games in Python

Posted: Mon Oct 21, 2013 6:55 pm
by darkhog
Cool, so anyone with good knowledge of both Python and NES assembly (I have only former, that's why I was able to do these docs, however rudimentary they may seem) would be able to do sound driver for pyNES along with some functions that would make possible to play melodies/sound effects based on what I've discovered about inner workings of pyNES?

Re: pyNES: writing NES games in Python

Posted: Tue Oct 22, 2013 6:02 am
by darkhog
I've tweeted gutomaia about the subject. Seems alive, last tweet is from Sept 28th. Maybe he'll help fix up docs (they aren't in good state with many things I've just assumed and few things I had no idea what they do (and still don't). Also sent him a PM.

Well, I hope he'll be able to put some more details in the docs now most of it is written and fix invalid assumptions on my part.