• Skip to main content
  • Skip to header right navigation
  • Skip to site footer
Retro Game Coders

Retro Game Coders

Retro computer/console game + dev community

  • Home
  • About
  • Blog
  • Retro Resources
    • Retro Gaming Timeline
    • Browser C64 Emulator
    • Best Retro YouTube Channels
    • New Retro Books
    • Raspberry Pi Amiga Emulation
    • MiSTer FPGA Tutorial
    • BMC64 C64 Pi
  • Contact
Home » Programming

TRSE + 8-Bit Guy SNES Controller Adapter:Turbo Rascal Hardware Programming

getting-started-with-trse

Turbo Rascal (TRSE) Programming Tutorial Part 3 - Hardware Programming

8-Bit Guy released a SNES game pad adapter as part of his PETSCII Robots game launch.

Seemed a great project to demonstrate how we can do low-level programming in TRSE!

Missed earlier Turb0 Rascal posts? Previously we looked at getting started with TRSE and then the basic structure of Turbo Rascal programs.

One of the really cool things about TRSE is it crosses the entire spectrum of low to high level. At the lowest we have access to the target machine hardware directly and via inline assembler. One the other hand we have now a growing Object Orientation ability, which means we can create very abstracted classes and libraries.

Obviously in this example we are leaning on the features offered by the former, but at some point I hope to turn this example code into a useful class.

Hardware-Interfacing: SNES Adapter Unit Code

On the Commodore machines, accessing the hardware is as simple as knowing which memory addresses do what.

The user port is at address $DD01, but before we can use it we need to tell our machine that certain pins are inputs or outputs. This will be familiar to anyone who has done any Arduino or Raspberry Pi hardware hacking!

As the SNES controller has 12 buttons in total, we set up variables for each. Yes, I am wasting valuable memory – ideally they would have been bit flags but it is what it is.

Unlike, say, an Atari joystick, the SNES controller uses a Shift Register to send the status of each button one at a time. So to read the controller takes a POKE to pulse the GPIO to tell the controller to give us an update then a POKE pulse on the adapter pin for each individual read via PEEK.

unit SNES;
var
		// Memory location of the C64 user port B
		const USER_PORT: address=$DD01;
		// Port B data direction register
		const PORTB_DIR: address=$DD03;
		// Direction and button constants
		SNES_A: 		byte=0;
		SNES_B: 		byte=0;
		SNES_X: 		byte=0;
		SNES_Y: 		byte=0;
		SNES_UP: 		byte=0;
		SNES_DOWN: 		byte=0;
		SNES_LEFT: 		byte=0;
		SNES_RIGHT: 	byte=0;
		SNES_LS:		byte=0;
		SNES_RS: 		byte=0;
		SNES_SELECT:	byte=0;
		SNES_START: 	byte=0;

procedure init();
begin
	// Initialise GPIO pins
	// Set pins 3 and 5 as output
	poke(PORTB_DIR, 0, %00101000);
end;

// READ pin 6
function read_data():byte;
var
	pin_value: byte=0;

begin
	// Check the pin value then ping the clock
	if((peek(USER_PORT,0) & %01000000) = %01000000) then
	begin 
		pin_value:=0;
	end
	else
	begin
		pin_value:=1;	
	end;
	// Pulse the clock on pin 3
	poke(USER_PORT, 0, %00001000);
	poke(USER_PORT, 0, %00000000);	
	// Return pin status
	read_data:=pin_value;

end;

// Get the 12 value of the
// buttons on the SNES pad
procedure snes_read();
begin
	// Pulse GPIO pin 5
	poke(USER_PORT, 0, %00100000);
	poke(USER_PORT, 0, %00000000);	
	
	// Read from the serialized data
	SNES_B		:=read_data();
	SNES_Y		:=read_data();
	SNES_SELECT	:=read_data();
	SNES_START	:=read_data();
	SNES_UP		:=read_data();
	SNES_DOWN	:=read_data();
	SNES_LEFT	:=read_data();
	SNES_RIGHT	:=read_data();
	SNES_A		:=read_data();
	SNES_X		:=read_data();
	SNES_LS		:=read_data();
	SNES_RS		:=read_data();		
end;


end.

Using the SNES Unit

Using the unit is really easy. First you tell TRSE that you are using the SNES:

@use "snes"

Then you get access to the snes_read() function which populates the current status of the controller.

After that it just takes a check to see if any of the buttons are being pressed and take action accordingly:

	// Loop forever
	while(1) do
	begin
		SNES::snes_read();
		snes_print();
		
		if(SNES::SNES_A=1) then
		begin
				moveto(10,23,hi(SCREEN_CHAR_LOC));
				printString("AAAA!     ", 0, 11);	
		end
		else
		begin
				moveto(10,23,hi(SCREEN_CHAR_LOC));
				printString("          ", 0, 11);	
		end;		
	end;

Testing With a Game?

If you have been following me on social media, or have been following the TRSE chatter, you might have seen I have been developing a cross-platform text unit for TRSE (more on that in a future post) that has a demo game included.

How about we add SNES control to the game?

				// Read controller
				SNES::snes_read();
	
				// Check the pressed buttons
	    		 if(SNES::SNES_UP=1 and y>0) then dec(y);
			     if(SNES::SNES_DOWN=1 and y<23) then inc(y);
	   			 if(SNES::SNES_LEFT=1 and x>0) then dec(x);
			     if(SNES::SNES_RIGHT=1 and x<21) then inc(x);
			   
				
				// Find out if the space we want to move to
				// is empty or if it contains anything special
				charat:=txt::get_char_at(x,y);
				
				
				// $20 is space
				case charat of
				$20:		txt::put_char_at(x,y,64);

It works!

Ah crap, perhaps it works too well. Instead of one move to the left it massively over-shoots as if we held the button down for a long time.

The problem is my SNES controller code is checking to see if the button is held down, not checking for a press.

Not an ideal solution but I can quickly modify my code to make sure the button is released like so:

// "de-bounce"
		while(SNES::SNES_UP+SNES::SNES_DOWN+SNES::SNES_LEFT+SNES::SNES_RIGHT<>0) do
		begin
			// Read controller 
			SNES::snes_read();
	
		end;			   
yay! it works!

Bottom Line

OK, the game isn’t going to win awards, but hopefully my point is made that using TRSE we can not just do the usual things of text, sprites, etc but also we have full access to the target computer hardware, and not only that, but we can use that feature in as high or as low level as we want by building up libraries of code in Units!

Next up, we develop a new, cross-platform game!

Remember to check out (and share!) Part 1 and Part 2

If you like it, share it! (Please - because it really helps)

  • Tweet
Category: ProgrammingTag: 6502, atari, c64, pascal, TRSE (Turbo Rascal) Programming, turbo rascal, z80, zxspectrum
Previous Post: « C64 Poke Peek The Magic of Poke – Commodore BASIC Programming Part 4
Next Post: Low-Level and Assembly Programming in Turbo Rascal (TRSE) getting-started-with-trse »

Retro Game Coders

Retro computer/console game + dev community by Chris Garrett

  • Facebook
  • Twitter
  • Instagram
  • YouTube

Maker Hacks ・ Geeky Game Master

© Copyright 2021 Chris Garrett

Privacy ﹒ Terms of Service

Return to top