So guys, those last years I've been always less and less into NESdev, and the probability that I ever release a full-featured game in my lifetime is getting smaller and smaller. In the last 2 years I have made pretty much zero (that is, 0) progress at all in my NESdev projects. Lack of time is not the only reason for that; lack of motivation and the start of other interest is the main reasons.
I don't know what I will do with my unfinished projects, but at least I'd like to share what I consider the more advanced and most "general purpose" thing I've developed. I developed this quite recently as I already started to slow down my progress on my main NESdev project, however I still think it is a great success.
The goal is to have a bytecode system that allows to waste less ROM memory to store programms as 6502 code, instead the 6502 interprets a byte-code having code in a more upper-level assembly language using 8 16-bit registers. This allows not only to save lots of ROM, but also to write some pieces of program more convieniently. The project is largely inspired by SWEET-16, however I found SWEET-16 was fairly lacking so I brought the following improvements over the concept :
The last point is what makes the project NES specific instead of 6502 specific. The idea is to code routines which are heavy in complex computations and VRAM access, such as loading routines, in bytecodes, and routines which needs to be fast and executed during normal gameplay in native 6502 assembly. Bytecode execution is around 10-20 times slower as native 6502 ASM, but can save PRG-ROM (up to 100%, since code can be stored in CHR-ROM and executed from there without using RAM).
I used WLA-DX marcos for coding, probably with some modifications it's easy to port this to other assembler's macros. To not confuse byte code with 6502 code, it uses 4-letter mnemonics. As a remainder it uses 8 registers, and most operations are operations are automatically operations between any register and R0 (just like the original SWEET-16). It does uses the same stack as 6502 code.
As an example code of what bytecode looks, here is an example from my game project that clears entiere name and attribute tables:
Here is the code and documentation. Note that some aspects of the bytecodes were engineered for this game specifically (in particular it relies on CNROM mapper, and some instructions that went unused in the game were removed, but could be added back).
I don't know what I will do with my unfinished projects, but at least I'd like to share what I consider the more advanced and most "general purpose" thing I've developed. I developed this quite recently as I already started to slow down my progress on my main NESdev project, however I still think it is a great success.
The goal is to have a bytecode system that allows to waste less ROM memory to store programms as 6502 code, instead the 6502 interprets a byte-code having code in a more upper-level assembly language using 8 16-bit registers. This allows not only to save lots of ROM, but also to write some pieces of program more convieniently. The project is largely inspired by SWEET-16, however I found SWEET-16 was fairly lacking so I brought the following improvements over the concept :
- Uses 32 instructions x 8 registers instead of 16 instructions x 16 registers
- More instructions means a more complete instruction set, as such logical operations are added
- Status registers are abandoned
- Fully mergeable with native 6502 code (bytecode and native 6502 code routines can call eachother transparently)
- Possibility to access to VRAM directly, as well as execute bytecode from CHR-ROM directly
The last point is what makes the project NES specific instead of 6502 specific. The idea is to code routines which are heavy in complex computations and VRAM access, such as loading routines, in bytecodes, and routines which needs to be fast and executed during normal gameplay in native 6502 assembly. Bytecode execution is around 10-20 times slower as native 6502 ASM, but can save PRG-ROM (up to 100%, since code can be stored in CHR-ROM and executed from there without using RAM).
I used WLA-DX marcos for coding, probably with some modifications it's easy to port this to other assembler's macros. To not confuse byte code with 6502 code, it uses 4-letter mnemonics. As a remainder it uses 8 registers, and most operations are operations are automatically operations between any register and R0 (just like the original SWEET-16). It does uses the same stack as 6502 code.
As an example code of what bytecode looks, here is an example from my game project that clears entiere name and attribute tables:
Code:
ClearVRAM
LDIW R6, $2000
LDZP M2000 ; Load previous $2000 value from M2000 in zeropage
LDIB R1, $80
LIOR R1
STZP M2000 ; M2000 |= $80
STMB R6 ; store it to actual $2000 registers
CALL PaletteFadeOut ; Call bytecode program that will fade out the palette over several frames
DECR R6 ; R6 = $2000 (compensate auto-increment from previous STMB instruction !)
CLER R0
STMB R6 ; $2000 := 0
STMB R6 ; $2001 := 0
STZP M2000 ; M2000 := 0
STZP M2001 ; M2001 := 0
CALL ClrNamTbl
Clr2ndNamTbl
LDIW R1, $2400 ; R1 points to second nametable
BRNZ R1, SetNamAdress
ClrNamTbl
LDIW R1, $2000 ; R1 points to first nametable
SetNamAdress
LDIB R0, $ff ; R3 (carry flag from 6502) nonzero -> Clear nametable with $ff
BRNZ R3, _ff
LDIB R0, $60 ; R3 (carry flag from 6502) zero -> Clear nametable with $60
_ff
LDIW R2, $3c0 ; R2 counts bytes to write to nametable
CALL _sub ;Clear name table
LDIB R2, $40
CLER R0 ;Clear attribute table
_sub
STVB R1 ; Store R0 to VRAM
DJNZ R2, _sub ; Loop until done
RETN
LDIW R6, $2000
LDZP M2000 ; Load previous $2000 value from M2000 in zeropage
LDIB R1, $80
LIOR R1
STZP M2000 ; M2000 |= $80
STMB R6 ; store it to actual $2000 registers
CALL PaletteFadeOut ; Call bytecode program that will fade out the palette over several frames
DECR R6 ; R6 = $2000 (compensate auto-increment from previous STMB instruction !)
CLER R0
STMB R6 ; $2000 := 0
STMB R6 ; $2001 := 0
STZP M2000 ; M2000 := 0
STZP M2001 ; M2001 := 0
CALL ClrNamTbl
Clr2ndNamTbl
LDIW R1, $2400 ; R1 points to second nametable
BRNZ R1, SetNamAdress
ClrNamTbl
LDIW R1, $2000 ; R1 points to first nametable
SetNamAdress
LDIB R0, $ff ; R3 (carry flag from 6502) nonzero -> Clear nametable with $ff
BRNZ R3, _ff
LDIB R0, $60 ; R3 (carry flag from 6502) zero -> Clear nametable with $60
_ff
LDIW R2, $3c0 ; R2 counts bytes to write to nametable
CALL _sub ;Clear name table
LDIB R2, $40
CLER R0 ;Clear attribute table
_sub
STVB R1 ; Store R0 to VRAM
DJNZ R2, _sub ; Loop until done
RETN
Here is the code and documentation. Note that some aspects of the bytecodes were engineered for this game specifically (in particular it relies on CNROM mapper, and some instructions that went unused in the game were removed, but could be added back).