Nakazoto / Hellorld

A collection of the greatest programming on the planet!
20 stars 3 forks source link

Hellorld! on NES Emulator #5

Closed coopstools closed 10 months ago

coopstools commented 11 months ago

My code can be found here (please note this is on the hellorld branch). There is a .mov file included that shows it being run on an fceux NES emulator.

Now, the NES has no concept of ASCII or any letters. So, to display a character, you first need to program in the letters. Each character is a sprite, and below is an excerpt from the creation of those sprites.

  .byte $F8, $cc, $c6, $c6, $c6, $cc, $f8, $00  ; D ($03)
  .byte $00, $30, $20, $21, $21, $23, $06, $7c

  .byte $ff, $ff, $c0, $fc, $fc, $c0, $ff, $ff  ; E ($04)
  .byte $00, $00, $00, $00, $00, $00, $00, $00

  .byte $c3, $c3, $c3, $ff, $ff, $c3, $c3, $c3  ; H ($07)
  .byte $00, $00, $00, $00, $00, $00, $00, $00

  .byte $c0, $c0, $c0, $c0, $c0, $c0, $ff, $ff  ; L (0b)
  .byte $00, $00, $00, $00, $00, $00, $00, $00

  .byte $7e, $e7, $c3, $c3, $c3, $c3, $e7, $7e  ; O ($0e)
  .byte $00, $00, $00, $00, $00, $00, $00, $00

  .byte $fe, $c6, $c6, $f8, $dc, $ce, $c6, $00  ; R ($11)
  .byte $00, $39, $21, $07, $20, $20, $21, $63

The sprites and their location are then stored in rom. They are defined by their Y position, the character they display, the color pallet they will use (a sprite can only be 3 colors), and their X position.

hellorld: 
        ; Y  CHR  ATTR   X
  .byte $60, $07, $00, $68 ; 00-03 H
  .byte $60, $04, $00, $71 ; 04-07 E
  .byte $60, $0b, $00, $7a ; 08-0b L
  .byte $60, $0b, $00, $83 ; 0c-0f L
  .byte $60, $0e, $00, $8c ; 10-13 O
  .byte $69, $11, $00, $7a ; 14-17 R
  .byte $69, $0b, $00, $83 ; 18-1b L
  .byte $69, $03, $00, $8c ; 1c-1f D
  .byte $69, $1a, $00, $95 ; 20-23 !

In the startup code, the sprite are pulled out of ROM and into memory ($0200 through $02ff are the available addresses for usable memory at run time.)

  ldx #$00
@next_sprite:
  lda devexp, x
  sta $0200, x
  inx
  cpx #$20
  bne @next_sprite

And finally, there is an interrupt setup for each screen refresh that runs the code to pull the sprite info out of memory and send it to the graphics chip (PPU) through the register at $2004:

  ldx #$00  ; Set SPR-RAM address to 0
  stx $2003 ; store value in X in location $2003
@loop:
  lda $0200, x  ; Load the sprite info
  sta $2004
  inx
  cpx #$20 ; end of sprite memory
  bne @loop

This all comes together to produce the following image:

hellorld_screenShot

The rest of the code is either associated with startup, or with moving the sprites around the screen. I know the true hellorld would exclude the movement, but it's an NES. How could I not include some interactive element?

coopstools commented 11 months ago

The instructions of how to get this running locally are for a mac, but I believe both the assembler and the emulator work on *nix and PC. Of course the download instructions would be different, but a quick google search should show how to get it working.

Let me know if there are any specific screenshots you would like me to take, or any parts of the code you would like me to highlight.

coopstools commented 11 months ago

For background on defining the sprites, each pixel in a sprite, which is set as an 8 pixel by 8 pixel element, is defined by two bits, which allow it to select from the 3 colors in a fixed pallete defined elsewhere. Confusingly enough, the MSB and LSB are stored in two different locations.

  .byte $f8, $cc, $c6, $c6, $c6, $cc, $f8, $00 <- MSB
  .byte $00, $30, $20, $21, $21, $23, $06, $7c <- LSB

Each hex value then represents a row in the sprite. Converting the hexes to binary let's you see the image slightly (if you squint hard enough).

$f8 -> 11111000 -> 11111___
$cc -> 11001100 -> 11__11__
$c6 -> 11000110 -> 11___11_
$c6 -> 11000110 -> 11___11_
$c6 -> 11000110 -> 11___11_
$cc -> 11001100 -> 11__11__
$f8 -> 11111000 -> 11111___
$00 -> 00000000 -> ________

Then, we can take the LSB and do the same.

$00 -> 00000000
$30 -> 00110000
$20 -> 00100000
$21 -> 00100001
$21 -> 00100001
$23 -> 00100011
$06 -> 00000110
$7c -> 01111100

Then, to overlay them, I'll use 'O' to represent the MSB, '.' for the LSB, and leave a space for the zero value (represented as transparent in the PPU).

OOOOO   
OO..OO  
OO.  OO 
OO.  OO.
OO.  OO.  
OO. OO..
OOOOO..  
 .....  
Nakazoto commented 10 months ago

While I can't quite count emulators, I did include your work on the "Almost There," thank you! https://github.com/Nakazoto/Hellorld/wiki/Almost-There#nes-nintendo-entertainment-system