Sunday, April 27, 2014

CannonBoard Latest Hardware Design

Colin is continuing to make great progress designing the CannonBoard hardware. Below you can see the latest iteration. You'll note that the Arduino is no-more, and a CPU fully integrated with the design. So to use CannonBall in a cabinet, you'll need a PC, the CannonBoard interface, and a VGA to CGA card / Ultimarc Arcade VGA or video card with soft 15khz.

It's possible that I'll port CannonBall to something like an ODroid so that you don't even need a bulky PC but that will come later.

I've been implementing some diagnostics options in CannonBall that will make it easier to work with a real cabinet. Not particularly exciting, but certainly useful. 

The outputs, including the start lamp and brake light should also now be hooked up.

Wednesday, April 23, 2014

3DS OutRun

M2's version of OutRun for the Nintendo 3DS is out in Japan. M2 have done a fantastic job with the previous SEGA ports, so it's exciting that OutRun has finally made it. Playing these titles in 3D for the first time is an awesome experience. This is also the first new port of the original OutRun for around ten years.

Whilst I haven't tried the 3DS version yet, I've checked out the videos and information online. There's a good interview with M2 in Japanese. Part 1 and Part 2.

There are plenty of solid features: 3D graphics, 60 FPS gameplay, widescreen mode, optional car colours, car handling settings and the usual difficulty and time DIP switch options from the arcade machine.

In terms of bugs, interestingly, M2 opted to fix the timing issues that we discussed in a previous post. However, the arcade mode uses the original timing. It's a confusing way of doing things I think as it creates two separate OutRun timing systems. The sprite scaling bug is fixed and the level object issue with the broken Gateway arches also appear fixed. Regarding the audio, the corrupt PCM samples are fixed.

So, you're probably thinking - is this the best best version of OutRun ever? Unfortunately, there are a few drawbacks if you're looking for a truly faithful experience. The biggest gripe is the Ferrari substitution, an inevitability due to licensing issues.

Whilst the car is purely cosmetic, it's also THE memorable graphic from the original game. It would be disappointing to replace Sonic the Hedgehog with a blue badger for example, even if the gameplay was identical.

Other graphics that have been changed include the Porsche graphic, which has had the spoiler removed.

The Marlboro lookalike sign on the start banner has been altered further to differentiate it from the cigarette brand. I spotted a sign that had been edited to contain an M2 logo also. 

There appears to be a sprite clipping bug on Stage 2 with the large stone graphics. It's possible this is an optimization to help the game run faster on the 3DS, but the large stones are disabled as they get closer to the camera. It's also possible M2 forgot to update a hard-coded value in the drawing routine for this object for widescreen displays. 

The shadows also seem to be too dark, so I'm not sure what's happened there. Maybe a palette optimization with the translucency for speed purposes on the 3DS? The Saturn conversion got this right, so I'm guessing it's not an oversight.  

Regarding the extras to change the car handling, I have mixed views on their merits and will wait to see how they play. Maybe being able to bump into traffic with no ill effects, drive off-road at full speed, grip corners more tightly and drive at a higher speed will be awesome and add a new dimension to the game. Maybe these are just cheap and easy features for M2 to implement! ;) Either way, they are pretty easy additions to CannonBall if they prove to be popular and there is a genuine demand for them.

In terms of a commercial OutRun release, this is probably as good as it's ever going to get so please buy a copy when it's released in your territory and show M2 and SEGA some support.

I'll keep this post updated as I find out more information!

Monday, April 21, 2014

Time Keeping

I've been chatting to Ade recently about the timing bugs present in OutRun. We initially discussed a timing bug that another blog reader, James Pearce submitted a fix for previously. Ade writes:

I believe the lap timing code in Out Run to be incorrect and would like your opinion on it. The timers are incremented every vertical blank so that should mean that seconds should increase by 1 every 60 vertical blanks.  I've looked at the original 68000 code and also your Cannonball source and it would appear that there's a bug in the code that handles the overflow from hundredths of seconds into full seconds.

I don't need to tell you that the 68000 code that increments the timers is at 0x007f4c in main cpu address space.  Specifically the bit of code that handles the overflow of hundredths to seconds is :-

007f58: cmpi.b   #$40, ($2,A1)       ; compare hundreds with 64 (should this be 60??)
007f5e: blt   $7f90                  ; branch if less than else reset hundredths and increment full seconds

Surely if the increment timers routine is called every vertical blank then it should be checking if hundredths are less than 60 and NOT less than 64?

I confirmed that this is indeed a bug in the Sega code. Ade looked into this further: 

I've been looking more and more into the timing issues and have come to the conclusion that Out Run timing is in quite a bad way and littered with bugs!

After doing the $40 to $3c alteration at $7f5b the timer still wasn't right, if I started a game with the dips set to normal (i.e. start time of 75 seconds on stage 1) the lap timer showed 1:18:50 when time ran out.  This was in comparison with 1:13:62 without the $40/$3c fix.

So I looked deeper into it and found another bug, this time in decrement_timers (0xb736).  The 68k here is :

subq.w #1, $60864.l            ; subtract 1 from the frame_counter
bge       $b760                ; branch if greater than or equal to zero

The Cannonball source is :-

if (--ostats.frame_counter >= 0)
    return false;

This is basically saying that the routine should still return false even if the frame_counter has reached zero, this is incorrect.  This means that the next time this routine is called the frame_counter (which is already at zero) will have another 1 subtracted from it, leaving a value of $ffff at $60864.w and it will have taken 31 iterations of this routine to reduce the time_counter by 1 whereby it should be 30 iterations.  The bge instruction should be a bgt instruction.  The Cannonball source should be '> 0' NOT '>= 0'.  By fixing this ($6c to $6e @ $b73c) I was getting closer to perfection!  Now when I started a game with 75 seconds on the timer the lap timer showed exactly 1:16:00 when time ran out.  The timing was now exactly 1 second out from where it should be.

The next bug I found was also in decrement_timers.  This time at the very end of the routine.  The Cannonball source is :-

return (ostats.time_counter < 0);

This should be <= 0 as, in its current guise, it will still count a whole second beyond the expiration of the timer before it returns a false value.  The 68k code uses the bcs (branch if carry set) instruction to test if the sbcd instruction has caused a carry by subtracting 1 from a value of 0.  To fix it in 68k requires a little more work but can be achieved by the fact that the sbcd instruction clears the Z flag if the result of the subtract operation is non-zero, otherwise it leaves it unchanged.  If the Z flag is set immediately before the sbcd instruction then it can be tested afterwards and if it's still set then we know that the result of the subtract was zero and can return a true value to indicate this.

I came up with this fix :-

00B74A: moveq #1, D1           ; replace moveq #0, D1 and addq.w #1, D1 with this single instruction
00B74C: move.w $60860.l, D0    ; move 16-bits from $60860 into D0 (make double sure that the sbcd instruction works correctly)
00B752: moveq #0, D5           ; trash D5 to set the Z flag in the CCR (D5 is unused so this is OK)
00B754: sbcd D1, D0            ; subtract D1 from D0 and set Z flag accordingly
00B756: beq $b764              ; branch if Z flag is still set after the sbcd instruction (counter has reached zero)

The instruction at $b74c (move.w $60860.l, D0) probably isn't needed and I could probably have got away with using the original instruction (move.b $60861.l, D0) as the sbcd instruction is byte-sized, not word-sized, but I erred on the side of caution, just to be double sure.

The above works well and I finally have a situation whereby I start a game with 75 seconds on the timer and the lap timer says 1:15:00 when time runs out!

There is a slight drawback though....  When the timer expires the game engine state changes from $c (GS_INGAME) to $f (GS_INIT_GAMEOVER).  This has the side effect of not updating the countdown timer at the top left of the screen (via the HUD display routine which checks the game engine state) and so it stays on 1 instead of being updated to 0.  So I need to alter the exit condition of the decrement_timers routine to set $60860.w to be 0 and then call draw_timer1 ($8216) before the game engine state is changed to $f, to force the game engine to update the HUD timer at the top left of the screen to zero.  I haven't done this yet, that's the next task!

I know that all your resource is currently tied up in Cannonboard but I was wondering whether you'd ever consider including fixes to the timing code in a future update to the enhanced roms.  I'm obviously intending on fixing up my own roms so that the total of all 5 laps exactly equals the overall game time when I cross the finish line, which will also agree to my stopwatch!

But I'm wondering how many other people out there are as bothered about it as I am?  It's a tough one to call as, like you say, the timing is so fundamental to Out Run and it's been wrong ever since day one so why bother to fix it now when people have got used to how it is.  It's a shame there's no more unused dip switches available where you could give people the option to toggle it fixed or original.

Now, we could fix these bugs in CannonBall. However, my original thinking was that having two timing systems would be confusing. I assumed players would want to compare times with the original machine and for a time to be consistent (even if it does not reflect the actual time you took to race the course!)

The only bug I have fixed relating to timing was a display bug with the EXTEND TIME overlay. What do you all think? 

Wednesday, April 16, 2014

CannonBoard Motion Control Part 2

The CannonBoard interface is progressing really nicely. Colin sent me this video earlier of the full moving cabinet working in conjunction with CannonBall.

You can see the motor calibration code is ported which moves the cabinet left and right at startup before finally centering. The in-game motor code, which I'd originally ported for force-feedback PC based steering wheels, can now be used as originally intended - to drive the cabinet's motor!

The dream is slowly becoming a reality and it's great to see the level of care and attention Colin has been pouring in to get the hardware just right. He is continuing to work on the audio interface next. 

Wednesday, April 09, 2014

CannonBoard Motion Control

In between changing nappies, I've had a small amount of time to work on the software side of CannonBoard. We're starting to get full motion control working with the sitdown cabinet.

The below video shows Colin demonstrating his OutRun cabinet connected to the Cannonboard interface. Currently the Arduino is running a ported version of the motor calibration code. This shows the interface is working and we'll be able to control the motor hardware from a PC running CannonBall.

In addition to this, we've verified that the previous work on the controls works via the original cabinet. It's a bit tricky to demonstrate though as the monitor output is to the PC currently.

My next steps are to finish the in-game motion control code and ensure the cabinet is moving correctly with the interface. Then controls and movement will be proven.

Colin is working on a version of the interface that removes the Arduino entirely. He is also working on the cabinet audio hardware interface.