I had some fun in the last days while investigating the USER SUBROUTINE of the Neo Geo MVS system (https://wiki.neogeodev.org/index.php?title=USER_subroutine).
The goal was to find out the System BIOS reactions on a coin throw-in and how to integrate this into C code.
I have created three program loops - DEMO. TITLE and GAME:
DEMO mode (blue background)
In the DEMO mode usally some game play, the game title and a highscore list is shown until a coin has been inserted into the MVS machine.
When the demo program is finished and no coin has been inserted, the game software gives the control back to the BIOS. If it is a single-slot system
the BIOS will restart the DEMO mode again. If it is an multi-slot MVS system the BIOS will switch to the DEMO of the next game in the next game slot.
Depending on the cabinet settings the user can switch through all game demos by pressing the SELECT_BUTTON or JOYSTICK_DOWN.
TITLE mode (green background)
When a coin has been inserted, the BIOS exits the DEMO mode and switches to the TITLE mode where the game title is displayed and 1 credit is added
to the player. In this stage the user can still switch to an other game in multi-slot MVS systems by pressing SELECT_BUTTON or JOYSTICK_DOWN.
Also, it is possible for the user to insert additional coins to gain more credits. If the user presses the START_BUTTON, 1 credit is decremented and the
BIOS exits the TITLE mode and switches to the GAME mode. When the TITLE mode is started the BIOS starts a timer (BIOS_COMPULSION_TIMER)
which forces a game start even if the user DID NOT press the START_BUTTON. This timer can be adjusted in the cabinet settings by the cabinet owner
(SETTING UP THE SOFT DIP > SETTING UP THE CABINET).
GAME mode (yellow background)
In the GAME mode the game is started and the user can insert coins anytime to gain more credits. The following part doesn't work in my
code because it is possible for the user to press the START_BUTTON anytime and anytime a credit is decremented. In the actual code the user can exit
the GAME mode by pressing the A_BUTTON and the game software gives the control back to the BIOS. The BIOS switches to the DEMO mode if there is
no credit left or to the TITLE mode if there are at least one credit left.
But it should work like this: If a game is lost a CONTINUE option is shown which counts down from 10 to 0. If the user presses the START_BUTTON in this
stage (only then and not during the game play) 1 credit is decremented and the game continues, if not a "GAME OVER" message/screen
is displayed and the game software gives the control back to the BIOS.
Does somebody have an idea how to disable the START_BUTTON during the gameplay?
Here is a video of the current state:
And this is the C code:
#include <stdio.h> #include <stdlib.h> #include <input.h> #include <DATlib.h> #include "externs.h" typedef struct bkp_ram_info { WORD debug_dips; BYTE stuff[254]; //256 bytes backup block } bkp_ram_info; bkp_ram_info bkp_data; BYTE p1,p2,ps,p1e,p2e; const int hexdec_to_dec_tbl[160] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 15 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, // 31 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, // 47 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, // 63 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 0, 0, 0, 0, 0, // 79 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0, 0, 0, 0, 0, 0, // 95 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0, 0, 0, 0, 0, 0, // 111 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0, 0, 0, 0, 0, 0, // 127 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0, 0, 0, 0, 0, 0, // 143 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 0, 0, 0, 0, 0, 0, // 159 }; // Demo Loop /////////////////////////////////////////////////////////////////////////////////////////// void demo() { int demo_duration=1200; volMEMWORD(0x401ffe)=0x7022; // background color volMEMWORD(0x400002)=0x79BB; // fix layer font color volMEMWORD(0x400004)=0x7022; // fix layer background color LSPCmode=0x900; initGfx(); clearFixLayer(); clearSprites(1, 381); SCClose(); fixPrintf( 2, 3,0,0,"USER subroutine 0.01"); fixPrintf( 2, 4,0,0,"------------------------------------"); volMEMBYTE(0x10FDAF)=0x01; // sets BIOS-USER-MODE to 1 (Title/Demo), volMEMBYTE(0x10FDB6)=0x00; // sets BIOS-PLAYER-MOD1 (Player 1 status) to 0 (Never played) do{ wait_vblank(); SCClose(); demo_duration-=1; if(demo_duration==0) { demo_duration=1200; asm("jmp 0xC00444"); // BIOSF_SYSTEM_RETURN tells the system the demo loop has been finished, will switch to the next game in multi-slot systems } fixPrintf( 2, 6,0,0,"DEMONSTRATION LOOP"); fixPrintf( 2, 7,0,0,"(restarts in %02d sec)", demo_duration/60); fixPrintf( 2,10,0,0,"INSERT COIN (P1)"); fixPrintf( 2,14,0,0,"CREDITS PLAYER 1 : %02d", hexdec_to_dec_tbl[volMEMBYTE(0xD00034)]); // credit counter player 1 fixPrintf( 2,15,0,0,"COIN TIMER : %02d", volMEMBYTE(0xD00038)); // counts down 30 frames after coin throw-in fixPrintf( 2,18,0,0,"BIOS-USER-REQUEST: %02d", volMEMBYTE(0x10FDAE)); // 0 = Init, 1 = Boot animation, 2 = Demo, 3 = Game (set by SYSTEM BIOS) fixPrintf( 2,19,0,0,"BIOS-USER-MODE : %02d", volMEMBYTE(0x10FDAF)); // Used by the game to tell what it's doing: 0:Init/Boot animation, 1:Title/Demo, 2:Game fixPrintf( 2,20,0,0,"BIOS-PLAYER-MOD1 : %02d", volMEMBYTE(0x10FDB6)); // Player 1 status. 0:Never played, 1:Playing, 2:Continue option being displayed, 3:Game over }while(!(hexdec_to_dec_tbl[volMEMBYTE(0xD00034)]>0)); // exit loop if credit counter is bigger than zero } // Title Loop /////////////////////////////////////////////////////////////////////////////////////////// void title() { volMEMWORD(0x401ffe)=0x1351; // background color volMEMWORD(0x400002)=0x6BDA; // fix layer font color volMEMWORD(0x400004)=0x1351; // fix layer background color LSPCmode=0x900; initGfx(); clearFixLayer(); clearSprites(1, 381); SCClose(); fixPrintf( 2, 3,0,0,"USER subroutine 0.01"); fixPrintf( 2, 4,0,0,"------------------------------------"); volMEMBYTE(0x10FDAF)=0x01; // sets BIOS-USER-MODE to 1 (Title/Demo), volMEMBYTE(0x10FDB6)=0x00; // sets BIOS-PLAYER-MOD1 (Player 1 status) to 0 (Never played) do{ wait_vblank(); SCClose(); fixPrintf( 2, 6,0,0,"TITLE SCREEN LOOP"); fixPrintf( 2,10,0,0,"PRESS START (P1)"); fixPrintf( 2,11,0,0,"BIOS-COMPULSION-TIMER: %02d", hexdec_to_dec_tbl[volMEMBYTE(0x10FDDA)]); // BIOS_COMPULSION_TIMER - forced start when counter reaches zero fixPrintf( 2,14,0,0,"CREDITS PLAYER 1 : %02d", hexdec_to_dec_tbl[volMEMBYTE(0xD00034)]); // credit counter player 1 fixPrintf( 2,15,0,0,"COIN TIMER : %02d", volMEMBYTE(0xD00038)); // counts down 30 frames after coin throw-in fixPrintf( 2,18,0,0,"BIOS-USER-REQUEST: %02d", volMEMBYTE(0x10FDAE)); // 0 = Init, 1 = Boot animation, 2 = Demo, 3 = Game (set by SYSTEM BIOS) fixPrintf( 2,19,0,0,"BIOS-USER-MODE : %02d", volMEMBYTE(0x10FDAF)); // Used by the game to tell what it's doing: 0:Init/Boot animation, 1:Title/Demo, 2:Game fixPrintf( 2,20,0,0,"BIOS-PLAYER-MOD1 : %02d", volMEMBYTE(0x10FDB6)); // Player 1 status. 0:Never played, 1:Playing, 2:Continue option being displayed, 3:Game over }while(!(hexdec_to_dec_tbl[volMEMBYTE(0x10FDDA)]==0)); // exit loop if BIOS-COMPULSION-TIMER is equal to zero } // Game Loop /////////////////////////////////////////////////////////////////////////////////////////// void game() { volMEMWORD(0x401ffe)=0x5872; // background color volMEMWORD(0x400002)=0x4ED9; // fix layer font color volMEMWORD(0x400004)=0x5872; // fix layer background color LSPCmode=0x900; initGfx(); clearFixLayer(); clearSprites(1, 381); SCClose(); fixPrintf( 2, 3,0,0,"USER subroutine 0.01"); fixPrintf( 2, 4,0,0,"------------------------------------"); volMEMBYTE(0x10FDAF)=0x02; // change BIOS-USER-MODE to 2 (Game), it stops the BIOS_COMPULSION_TIMER volMEMBYTE(0x10FDB6)=0x01; // sets Player 1 status to playing volMEMBYTE(0x10FEC5)=0x01; // stops the bios calling command 3 twice after game over if credits are already in the system (skip attract mode) do{ wait_vblank(); SCClose(); p1=volMEMBYTE(P1_CURRENT); // read P1 inputs // ps=volMEMBYTE(PS_CURRENT); if(p1&JOY_A) { asm("jmp 0xC00444"); // BIOSF_SYSTEM_RETURN } fixPrintf( 2, 6,0,0,"GAME PLAY LOOP"); fixPrintf( 2, 7,0,0,"FRAMES: %06d", DAT_frameCounter); fixPrintf( 2,10,0,0,"PRESS A-BUTTON (P1) TO EXIT"); fixPrintf( 2,11,0,0,"BIOS-COMPULSION-TIMER: %06d", hexdec_to_dec_tbl[volMEMBYTE(0x10FDDA)]); // BIOS_COMPULSION_TIMER fixPrintf( 2,14,0,0,"CREDITS PLAYER 1 : %02d", hexdec_to_dec_tbl[volMEMBYTE(0xD00034)]); // credit counter player 1 fixPrintf( 2,15,0,0,"COIN TIMER : %02d", hexdec_to_dec_tbl[volMEMBYTE(0xD00038)]); // counts down 30 frames after coin throw-in fixPrintf( 2,18,0,0,"BIOS-USER-REQUEST: %02d", volMEMBYTE(0x10FDAE)); // 0 = Init, 1 = Boot animation, 2 = Demo, 3 = Game (set by SYSTEM BIOS) fixPrintf( 2,19,0,0,"BIOS-USER-MODE : %02d", volMEMBYTE(0x10FDAF)); // Used by the game to tell what it's doing: 0:Init/Boot animation, 1:Title/Demo, 2:Game fixPrintf( 2,20,0,0,"BIOS-PLAYER-MOD1 : %02d", volMEMBYTE(0x10FDB6)); // Player 1 status. 0:Never played, 1:Playing, 2:Continue option being displayed, 3:Game over }while(!(p1&JOY_A)); // exit loop if BUTTON A is pressed } // Main Loop /////////////////////////////////////////////////////////////////////////////////////////// int main(void) { while(1) { if(volMEMBYTE(0xD00034)==0){demo();} // switch to demo if credits are zero title(); game(); } }