Welcome, Guest. Please login or register.
Did you miss your activation email?
June 24, 2018, 10:54:00 PM
Home Famicom World Search Calendar Login Register

+  Famicom World
|-+  Family Computer
| |-+  Famicom / Disk System (Moderators: manuel, L___E___T)
| | |-+  Controlling ROB with Family Basic
0 Members and 1 Guest are viewing this topic. « previous next »
Pages: [1] Print
Author Topic: Controlling ROB with Family Basic  (Read 4619 times)
UglyJoe
Administrator
Disk-kun
*****
Gender: Male
United States United States
Posts: 5344



WWW
« on: July 17, 2013, 02:43:37 AM »

So I wrote a Family Basic program to control ROB.  I'll jump straight to the code and a WAV file and tell my story after.

WAV file: robtest7.wav

Source:
Spoiler  :

` loads the assembly code for controlling ROB
1 POKE &H7700,&H2C,&H02,&H20,&H10,&HFB,&HAD,&H32,&H00,&H29,&H7F,&H8D,&H00,&H20,&HA0,&H08,&HA2,&H00,&H98,&HE0,&H05,&HD0,&H03,&HAD,&HD0,&H77,&H4A,&HA8,&HA9,&H3F,&H8D,&HD1,&H77,&H90,&H05,&HA9,&H2A,&H8D,&HD1,&H77,&HA9
2 POKE &H7728,&H80,&H2C,&H02,&H20,&HF0,&HF9,&HE0,&H0D,&HF0,&H26,&HAD,&H02,&H20,&HA9,&H3F,&H8D,&H06,&H20,&HA9,&H04,&H8D,&H06,&H20,&HAD,&HD1,&H77,&H8D,&H07,&H20,&HAD,&H02,&H20,&HA9,&H3F,&H8D,&H06,&H20,&HA9,&H04,&H8D
3 POKE &H7750,&H06,&H20,&H8D,&H01,&H20,&HE8,&HD0,&HB9,&HAD,&H33,&H00,&H8D,&H01,&H20,&H2C,&H05,&H20,&HAD,&H9D,&H00,&H8D,&H05,&H20,&HAD,&H69,&H00,&H8D,&H05,&H20,&HAD,&H32,&H00,&H09,&H80,&H8D,&H00,&H20,&H60

` prints out a simple menu
10 PRINT "H=LEFT"
12 PRINT "J=DOWN"
14 PRINT "K=UP"
16 PRINT "L=RIGHT"
17 PRINT
18 PRINT "M=DOWN 2"
20 PRINT "I=UP 2"
21 PRINT
22 PRINT "A=OPEN"
24 PRINT "S=CLOSE"
25 PRINT
26 PRINT "W=BLINK"
28 PRINT "Q=INIT"

` checks for user input
100 S$=INKEY$
120 IF(ASC(S$)=0) GOTO 100
123 RESTORE 3000
125 FOR I=1 TO 10
130 READ X$
140 READ R
150 IF (X$=S$) GOSUB 2000
160 NEXT
200 GOTO 100

` when called, will set the pattern for ROB and call the assembly routine
2000 POKE &H77D0,R
2010 CALL &H7700
2080 RETURN

` pairs of keyboard keys and ROB patterns
3000 DATA H,&H5D,J,&H75,K,&H5F,L,&H57,I,&HDD,M,&HDF,A,&H77,S,&H7D,Q,&HD5,W,&HD7


Story:

It's late here.  There are probably typos.

In another thread, P had mentioned the idea of controlling ROB with Family Basic.  I thought that was a pretty novel idea.  He told me that he didn't care if I went ahead and tried to do it (since he was going to try and figure it out, as well), so I decided to start tinkering with it in the evenings to see what I could come up with.

I actually already had a good idea of how ROB was controlled prior to this, since I started to write a ROB module for the FCEUX emulator a number of years ago.  I knew that Gyromite used a sequence of 13 black/green screens to send out a command to ROB.  I knew that it accomplished this by replacing the all of the palettes with either black or green during the vblank phase.  What I didn't know were all of the valid sequences -- ones that ROB would actually respond to.  I had kicked around the idea of creating a test ROM that would allow me to try all of the combinations.  Luckily, some one seems to have already done this by now, so I was able to acquire the list of valid commands pretty easily.

So I had the list of commands and I had a technique (thanks to Gyromite) for creating the sequences.  However, I wasn't too happy with Gyromite's technique.  To replace the palettes entirely with black or green would imply that I would need to restore the old palette values after I was done (assuming the player would actually like to be looking at something other than an all black/green screen!).  This would involve tracking down the palette colors currently being used (since Family Basic lets you set the palettes), store them, and then restore them afterwards.  I was trying to use as little memory as possible with this routine, so I wasn't keen on storing all that palette data.  (As it turns out, FB probably already has all of that backed up elsewhere in memory, but I didn't know that at the time).

I spent some time researching the NES PPU at Nesdev's wiki page.  I've been hacking away at ROMs since college so I'm not mystified by 6502 asm or the Famicom/NES hardware architecture (but I'm still very much a novice).  I noticed at the bottom of their PPU palettes article the background palette hack.  It sounded like I could use this hack instead of replacing the palettes.  Best of all, since I could store the color that I needed in normally unused palette locations, I wouldn't have to worry about backing up and restoring the original palette!

So now I was ready to start coding.  Although I've been hacking ROMs for fun for a while now, I've never created a ROM from scratch.  Enter the "Nerdy Nights" NES programming tutorials over on Nintendo Age.  I went through a few of the tutorials and eventually found one of the examples to be suitably hackable as to serve as a base for my ROB routine. 

I hacked away at this for about a week.  I would test my ROM in a couple of different emulators.  Nintendulator ended up being my most used, since it's accurate, has great Family Basic support, and has a debugger.  Once I was satisfied with a version of my routine, I would convert it to a series of POKE commands for Family Basic to use.  I wrote Java utility to handle this for me.  This would then go into Nestopia, since it has a "paste" command for Family Basic and I already knew how to convert its FB tape format into a wav file.  The wav file would go to my phone, me and my phone would go to the basement to my temporary Famicom setup, and I would test it.  And it wouldn't work.  Or it would kind of work, but not really.  Or only certain commands would work.  I'd think about the problem, tweak the code, and the start the process over again.  It took about seven real iterations to get it to where it is now.

There are still some more tweaks I could make to it to bring the size of the routine down a little bit.  I might do that at some point.  As a proof of concept, though, I'm pretty happy with it.  Here is the routine in non-POKE form for anyone who is interested.  (NESASM3  won't compile this on its own, though).

Spoiler  :

;;;;;;;;;;;;;; 

  .rsset $77D0

robPattern .rs 1  ; the pattern to flash to ROB (after preamble)
robPalette .rs 1  ; the color to flash to ROB next

FAMBASIC_PPUCTRL = $32 ; where Family Basic 2.1 stores its PPU ctrl
                       ; (it will always OR this with #$80 to keep NMI on)
FAMBASIC_PPUMASK = $33 ; where Family Basic 2.1 stores its PPU mask
FAMBASIC_SCROLLX = $9D ; where Family Basic 2.1 stores its X position for PPUSCROLL
FAMBASIC_SCROLLY = $69 ; where Family Basic 2.1 stores its Y position for PPUSCROLL

;;;;;;;;;;;;;;;   

  .org $E700   
vblankwait3:      ; Second wait for vblank, PPU is ready after this
  BIT $2002
  BPL vblankwait3


LoadROBPattern:
  LDA FAMBASIC_PPUCTRL ; load the PPUCTRL used by Family Basic
  AND #$7F     ; AND with %01111111 to disable NMI
  STA $2000    ; disable NMI
  LDY #$08     ; load preamble (%00001000)
  LDX #$00     ; rest step counter

NextROBBit:
  TYA          ; get the preamble/action into the Y register

  CPX #$05     ; is this the beginning of the action byte?
  BNE DoNextROBBit
  LDA robPattern ; beginning of action byte, load it up

DoNextROBBit:
  LSR A       ; push the next ROB bit into the carry flag
  TAY         ; store the preamble/action state into the Y register
  LDA #$3F    ; assume black
  STA robPalette
  BCC FlashROBBit ; if black, go flash it
  LDA #$2A    ; otherwise load green and go flash it
  STA robPalette

;
; Will load a palette byte (robPalette) into unused palette location
; $3F04.  Will then point the VRAM address to $3F04 and disable 
; sprite/bg rendering.  On real hardware and accurate emulators, this
; robPalette value at $3F04 will be used to fill the background.
;
FlashROBBit:
  LDA #$80
  BIT $2002
  BEQ FlashROBBit

  CPX #$0D     ; all bits loaded?
  BEQ DoneFlashing

  LDA $2002    ; read PPU status to reset the high/low latch
  LDA #$3F
  STA $2006    ; write the high byte of $3F04 address
  LDA #$04
  STA $2006    ; write the low byte of $3F04 address
  LDA robPalette     ;load palette byte
  STA $2007             ;write to PPU
  LDA $2002    ; read PPU status to reset the high/low latch
  LDA #$3F
  STA $2006    ; write the high byte of $3F04 address
  LDA #$04
  STA $2006    ; write the low byte of $3F04 address
  STA $2001    ; disable bg/sprite rendering while VRAM address is pointing
               ; at the robPalette byte

  INX          ; increment the step counter
  BNE NextROBBit ; attempt to go do the next one

DoneFlashing:
  LDA FAMBASIC_PPUMASK ; otherwise, restore the original PPU mask
  STA $2001

  BIT $2005            ; read PPUSTATUS to reset the addres latch
  LDA FAMBASIC_SCROLLX ; load X position
  STA $2005            ; store X position
  LDA FAMBASIC_SCROLLY ; load Y position
  STA $2005            ; store Y position

  LDA FAMBASIC_PPUCTRL ; load the PPUCTRL used by Family Basic
  ORA #$80     ; ORA with %10000000 to enable NMI
  STA $2000    ; enable NMI

  RTS          ; and get out of here


Omake:

I got side-tracked at one point and wondered if asm was needed at all.  Could this just be done in a for loop?  Here is my attempt at this.  Some of the commands work some of the time.  By stepping through things in an emulator (Nintendulator this time, since it has frame stepping), I was able to determine that the pattern is sometimes interrupted (by either an NMI routine or the BASIC interpreter, I'm guessing) and, since every frame counts, that's enough to make it unreliable  Oddly, Left/Right always seem to function as Close/Open.  That's an interesting problem that I never really found a solution to.  Anyway, here's the code.  I can explain it later if anyone is interested.

Spoiler  :

10 PRINT "H=LEFT"
12 PRINT "J=DOWN"
14 PRINT "K=UP"
16 PRINT "L=RIGHT"
17 PRINT
18 PRINT "M=DOWN 2"
20 PRINT "I=UP 2"
21 PRINT
22 PRINT "A=OPEN"
24 PRINT "S=CLOSE"
25 PRINT
26 PRINT "W=BLINK"
28 PRINT "Q=INIT"

100 S$=INKEY$
120 IF(ASC(S$)=0) GOTO 100
123 RESTORE 3000
125 FOR I=1 TO 10
130 READ X$
140 READ R
150 IF (X$=S$) GOSUB 2000
160 NEXT
200 GOTO 100

2000 FOR I=1 TO 13
2010 X=R MOD 2
2020 R=R/2
2030 Y=&H3F
2040 IF (X) Y=&H2A
2050 PALETB 0,Y,Y,Y,Y
2060 NEXT
2070 PALETB 0,&HF,&H30,&H21,&H2
2080 RETURN

3000 DATA H,&HBA8,J,&HEA8,K,&HBE8,L,&HAE8,I,&H1BA8,M,&H1BE8,A,&HEE8,S,&HFA8,Q,&H1AA8,W,&H1AE8
Logged
nerdynebraskan
Sharp C1
*****
Gender: Male
United States United States
Posts: 1171

I'm Jackson. I play and collect 8-bit Nintendo.


« Reply #1 on: July 17, 2013, 09:22:09 AM »

As someone who does not and will never have Family Basic, my question is: will this ever lead to a homebrew ROB game made available on a repro cart? And if so, when can I throw money at you to get one? In any event, it's super cool to see ROB getting some attention almost 30 years later.
Logged

P
FamicomBox
*****
Gender: Male
Sweden Sweden
Posts: 3135


« Reply #2 on: July 17, 2013, 01:03:20 PM »

Great job UglyJoe! Grin It would have taken me much longer to figure this out even though I also went through the "Nerdy Nights" and learned the basics of programming the Famicom in 6502. I tried the code in Nestopia and it seems to work (I can't test it with ROB that way though). When I have some more time to spare I will dig out my Robot and test this on real hardware. Smiley

As someone who does not and will never have Family Basic, my question is: will this ever lead to a homebrew ROB game made available on a repro cart? And if so, when can I throw money at you to get one? In any event, it's super cool to see ROB getting some attention almost 30 years later.
Of course! UglyJoe even made a NES ROM to test his code before he implemented it in his Family Basic code. With some modification you can take the subroutine posted here and put it in your ROM. I have a basic idea how to use it but we will see. You can also make your game in Family Basic and then use the program STTONES to convert it to a standalone NES file.
Logged

UglyJoe
Administrator
Disk-kun
*****
Gender: Male
United States United States
Posts: 5344



WWW
« Reply #3 on: July 17, 2013, 02:05:48 PM »

will this ever lead to a homebrew ROB game made available on a repro cart?

I have no short-term or long-term plans to make a full-blown game that utilizes ROB, but I guess anything is possible.
Logged
nerdynebraskan
Sharp C1
*****
Gender: Male
United States United States
Posts: 1171

I'm Jackson. I play and collect 8-bit Nintendo.


« Reply #4 on: July 17, 2013, 03:17:43 PM »

Well, I suppose by publishing your code, you may be paving the way for an actual game designer to borrow it to make a game. In that case, I'll thank you again anyway.
Logged

FamicomLover
Famicom
**
Gender: Male
United States United States
Posts: 29


Favorite Famicom Disk System Game: Otocky


« Reply #5 on: July 19, 2013, 04:00:52 AM »

Incredible! I Would also love to see some ROB homebrew!
Logged

I am the king of San Andreas
P
FamicomBox
*****
Gender: Male
Sweden Sweden
Posts: 3135


« Reply #6 on: July 21, 2014, 11:53:40 AM »

I tested this code on my Family Basic, and as I couldn't load the wav I had to type it in manually, ugh. But now I have it on tape at least. It works flawlessly with my Famicom Robot. Smiley

UglyJoe I noticed that Robot Block and Gyro uses the test command a bit differently, namely it can cause the LED to blink. When you start the test a window in the middle of the screen keeps blinking and you can adjust ROB's head until he looks at the screen. It's easy to tell because ROB's LED start blinking when he sees the window, and turns off if he stops seeing it. When you are done you press select and the screen will blink which will light up the LED permanently. Maybe it's doing this by keep sending the command over and over? I don't know.


Anyway, I made a working ROM that uses UglyJoe's routine that works on a real Famicom (I've tested it on the Everdrive at least). Only problem is that ROB doesn't respond 100% of the time and there's also something weird with the controller reading because my external controller doesn't work properly.

Download is in attachment of this post http://forums.nesdev.com/viewtopic.php?f=10&t=11445&p=131368#p131336 (Famicom World doesn't seem to allow this kind of file as an attachment). This shouldn't be used as a model for how to make proper homebrew, but it can be used if one wants to make a ROB homebrew, or if you just wants to test your ROB. Smiley
Logged

UglyJoe
Administrator
Disk-kun
*****
Gender: Male
United States United States
Posts: 5344



WWW
« Reply #7 on: July 21, 2014, 12:16:33 PM »

UglyJoe I noticed that Robot Block and Gyro uses the test command a bit differently ... Maybe it's doing this by keep sending the command over and over? I don't know.

I'm pretty sure the game repeats the test pattern over and over again in order to get ROB to blink the LED.  I don't know what it does to make the LED stay on.  Are you sure the test command isn't just a toggle for the LED (turns it on the first time it reads, then off the second time, then on the third time, etc)?  It's been a while since I tested any of it, so I'm not sure.  I never really tested the "test" command, anyway.

Anyway, I made a working ROM that uses UglyJoe's routine that works on a real Famicom (I've tested it on the Everdrive at least). Only problem is that ROB doesn't respond 100% of the time and there's also something weird with the controller reading because my external controller doesn't work properly.

I'll try and check it out soon.  I can't test the external controller thing since I don't have a FC flashcart, though.  Did you add additional logic to read in the controller input from external controllers (bit 1 on $4016 and bit 1 on $4017)?
Logged
P
FamicomBox
*****
Gender: Male
Sweden Sweden
Posts: 3135


« Reply #8 on: July 21, 2014, 01:46:48 PM »

UglyJoe I noticed that Robot Block and Gyro uses the test command a bit differently ... Maybe it's doing this by keep sending the command over and over? I don't know.

I'm pretty sure the game repeats the test pattern over and over again in order to get ROB to blink the LED.  I don't know what it does to make the LED stay on.  Are you sure the test command isn't just a toggle for the LED (turns it on the first time it reads, then off the second time, then on the third time, etc)?
Yes I'm sure it turns on permanently (like when you finished the test in Block/Gyro).

Anyway, I made a working ROM that uses UglyJoe's routine that works on a real Famicom (I've tested it on the Everdrive at least). Only problem is that ROB doesn't respond 100% of the time and there's also something weird with the controller reading because my external controller doesn't work properly.

I'll try and check it out soon.  I can't test the external controller thing since I don't have a FC flashcart, though.  Did you add additional logic to read in the controller input from external controllers (bit 1 on $4016 and bit 1 on $4017)?
Yes I did, the problem is much more complex. I'm using pretty much the same logic in my Pong game and the external controller have no problems there. What's new in this program is that I keep the previous frame's button states in a variable and use that to see what is currently pressed (and stores that too in a variable). Then when my program checks for inputs from the user in order to move the cursor it checks the variable that what is currently pressed, and that way the cursor will only move one notch per press even if you hold the button down. But my ext controller acts up and moves the cursor several notches up or down. A and SELECT buttons works like normal but not UP/DOWN buttons.

Maybe my Capcom Power Stick Fighter doesn't like this currently-pressed-check for some reason. UP/DOWN on controller 3 (which is same as ext controller) works in Nestopia.
Logged

metalarmor19
Famiclone
*
Gender: Male
United States United States
Posts: 6


« Reply #9 on: July 22, 2014, 06:37:05 PM »

This really starts opening doors for homebrewers to start making games using ROB. Now we an use it for something else other than just stack up and gyromite maybe. Great job  Grin
Logged
Pages: [1] Print 
« previous next »
Jump to:  


Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Valid XHTML 1.0! Valid CSS!