This article is suitable for friends with C language
Here is the "explanation of open source project" series in Hellogithub. In this issue, the childhood memory after 80, 90 is born in the 1978 classic arcade game "space aggressor" is also called "small bee" C language recovery. Sound versionSi78c.
This game can be described as a very popular at the time. I believe many friends have been playing. Now grow up, I don’t know how many friends are interested in it!
The original "space aggressor" is written by the 8080 assembly code of approximately 2K lines, but the assembly language is too late to read. The open source project Si78C explained in accordance with the original assembly code in accordance with the C language in accordance with the original assembly code. The interruption, equilation logic of the original arcade hardware,At runtime, its memory status is almost the same as the original version. Almost reached the perfect replica, really let me look at it!
Let’s take a sliver with Hellogithub, run this open source project, read the source code, through history and experience the exquisiteness of the game design!
The experimental environment in this article is Ubuntu 20.04 Ltd, the GCC version is greater than GCC 3
first Si78c Use SDL2 to draw game windows, so you need to install dependencies:
Then download the source code from the warehouse:
In addition, the project will extract the original game from the original ROM, the font, so you need to download the original ROM file.
New names INV1 and BIN in the Si78C source folder
Then extract the contents of the Invaders.zip to INV1, and the last directory structure is as follows:
Compile with Make:
The executable will be generated in the bin folder, running to start the game:
The game control button is as follows:
The "Space Aggressor" original code is running on the 8080 processor, and its content is written by assembly code and involves some hardware operations. In order to simulate the logic of the original arithm code, Si78c is most likely to convert assembly code to C language and use A MEM structure simulates the hardware of the original arcade, so some code is more strange and even incredible from the perspective of pure software, but the reason for the reason is not possible to explain the code all the articles, so readers will cooperate with myself. Read this article in detail.
Si78c Use the UContex library to simulate the process scheduling and interrupt operation of the original arithcher.
Agreement: The weigh is more light and fast, saving resources, and the science will be equivalent to the thread for the process.
Ucontext provides GetContext, MakeContext, SwapContext, and STCONText functions to create and switch,Si78c The initialization function is init_thread. Below we are directly to see the examples in the source code:
If you are not enough here, you can look at the back state transition diagram, and the graphic combines more intuitive.
Each time you call Yield will use SwapContext to switch between two cables:
Please see the details
Due to the limited article space, only the key source section is shown below. More detailed source code progressive Chinese Note:
The foregoing said that Si78c is the original altokyPixel levelEven most of the memory data is also equal. In order to do this Si78C simulated part of the hardware of the arcade: RAM, ROM, and memory, they are packaged into a large structure called MEM, the memory allocation is as follows :
- 0000-1FFF 8K ROM
- 2000-23FF 1K RAM
- 2400-3FFF 7K Video Ram
- 4000 – Ram Mirror
It can be seen that the RAM of the machine is only a poor 1kb size, and each bit is as long as the precious need to carefully plan. Here is a RAM allocation table, more details
Before detailed explanation of the game animation, we need to understand how the material is stored:
Image from arcade assembly code interpretation
In the original ROM of the arcade, the game material is stored in memory directly in the binary format, among whicheveryoneBinary means that the current position pixel is black or white
for example Figure 2-1 Display 0x1ba0 position memory data is 00 03 04 78 14 13 08 1A 3D 68 FC FC 68 3D 1A 00Eight-line The arrangement and coming is an alien with a picture of an upside down letter "Y" (the content in the figure looks like a rotation 90 degrees because the picture is a list of stores,Every 8 bit represents a column of pixels.
The authors of the Si78C exchange the X Y axis directly exchange the X Y axis while displaying pictures to achieve the effect of rotating pictures.
We can find a structure called MEM, where M.vram (0x2400 to 0x3FFF) simulates the memory of the arcade, which isEvery bit represents a pixelBlack (0) white (1), rendering from the lower left corner, its correspondenceFigure 2-2:
All the code related to animated drawings in the game are data modified in this part of the area, such as Drawchar, ClearplayField, DrawSImpSprite, and more. So how do you let the simulated existing content display on the player’s screen? Watch carefullyCode 3-1 The render function is called at the end of the loop, which is responsible for reading the contents of the analog memory and rendering a pixel block on the window on the window.
If you think about it, it is not difficult to find that this method of modifying analog appearance and renovation, there is no more province, even a bit weird. This is because Si78C simulates the display process of arcade hardware: modify the corresponding display and then the hardware will automatically display the content in the memory to the screen.
Code 3-1 The input function is responsible for detecting and storeing the user’s button information, which relies on the SDL library.
Third, the first start
Si78c Like all C processes, they are starting from the main function:
The startup process is as shown in the figure:
The game original code (8080 assembly) is used byInterrupt drive(This programming method is related to hardware, the specific content can be understood by itself. Interrupt)CooperateTrust operation. In order to simulate the logic author of the original game, the author is a large cycle as a hardware behavior simulation center (implement interrupt management, sweeping, screen rendering). The game is about one-third of the time in running the main thread, and the main thread will be seized by two interrupts of Midscreen and Vblank.Code 3-1 The two IRQs achieve an analog to the interrupt (set the corresponding variable as a flag).
exist the first time When entering loop_core, its procedure is as follows:
Because Yield_Rason this variable is a static typeThe default is zero
It should be noted that the switching switch is performed in Execute. At this time, the execute of Execute is saved in the variable frontend_ctx, the pointer prev_ctx update is to the Frontend_CTX, the pointer curr_ctx is updated to point_ctx, the process is as shown Show:
Implementation, please see Code 2-2
When EXECUTE returns, he will return to loop_core according to the normal execution process, just like it has never been paused.
Carefully observe the main loop in main_init we can find that it multiple times the TimeSlice function (such as OneSecdelay), through this function we can implement the time slice rotation operation between Main_CTX and Frontend_CTX, the process is as follows:
It mainly has made the following in main_init:
Before the player coin coin, the game relies on the main_init loop play animation to attract players.
If you only turn on the function appearing in main_init that we will send the current code that there is no too much game logic, such as aliens move, shoot, players, etc., it seems that there is no existence, more time is in manipulation Memory, set the flag. So where is the function related to game game logic processing? This part of the content will be revealed below.
Fourth, simulation interruption
exist Code 3-1 The loop_core function is separated by two IRQs. We mentioned before The large cycle in main is in nature is the hardware behavior of the simulated arcade.In the real machine, the interrupt is only executed when trigger, but we can only call IRQ between Loop_core.Simulation is interruptedAnd poll the interrupt state in Execute to determine whether it enters the interrupt processing function, the process is as follows:
At this time it is as follows:
There are two interrupts: MidScreen_INT and VBLANK_INT will appear in turn.
Let’s first look at Midscreen_Int:
In this section, the RungameObjs function basically includes players’s mobile and drawing, player bullets and aliens’ bullets, collision detection, drawing, etc. All game logic, CursorNextalien finds the next live alien to be drawn People set the flag to wait for the drawing, and detect whether the alien spacecraft encounters the bottom of the screen.
After running, return to run_int_ctx Continue to run until Yield (Yield_INTFIN) indicating that the execove back EXECUTE, and re-set the next to main_ctx in Execute to make main_init to continue to run (seeCode 3-2.
The next is VBLANK_INT:
Its main role is whether the player wants to exit the game or carry out the coin operation. If it is already in the game mode, play the fleet sound in sequence, draw the aliens who recorded in MidScreen_INT, running RungameObjs to handle players and alien The people are fired and movement events, and Timetosaucer randomly generate mysterious flying saucer. If it is not in the game mode, enter isrsPLTASKS to adjust the animation that should play on the current screen.
We can notice that if the player has incorporated into if (M.NumCoinS!=0), and then call Yield (Yield_Wait_for_start) will prompt this function to return. There will be such a prompt in many places in the code of Si78c, which is not a simple call to a function that does not return to make a knot.
observe Code 3-2 Can be found in YIELD_PLAYER_DEATH, YIELD_WAIT_FOR_START, YIELD_INVADED, YIELD_TILT these four branches are called init_threads (yield_reason), this function will reset in int_ctx and main_ctx stack and re-binding parameters when calling run_main_ctx is yield_reason, so next time When executed, run_main_ctx will jump to the appropriate branch to run according to the interrupt indication.
When the opening, it mentioned that the RAM of the arcade only has poor 1kb size, so small places must not let us store information about each object on the screen, but the player’s position, the location of the alien, and their bullets, screen The shield damage will be updated in real time, how to do this?
I found that the "space aggressors" game area content is still very regular, special spacecraft (UFO) will only appear on the top of the screen, the position of the shield and players will not change, only the position of the bullet is not good, so carefully study the code carefully It can be seen from DrawSpriteGeneric that the game is only simple to determine whether the pixel block is complicated. When the player bullet is hit, when it is judged in the Playershot function, it is only necessary to judge the vertical direction coordinate (Y coordinate), if >=216 is hitting the top,>=206 is hitting mysterious flying saucer, others are hitting the shield or alien bullets. And because the alien spacecraft is a group of exercises, only one of the positions can be calculated out of the coordinates of each alien spacecraft.
This is calculated, the program only needs to save the survival status of the alien spacecraft, the current fleet relative movement position, player and aliens bullet information, when you need to detect the collision, the pixel information in the memory is used to compare the current, then push the current When there is a collision of two objects, this method saves many resources compared to information of each object.
Si78c Unlike other code, it is essentially simulation of hardware and assembly code, hoping to explain the source code of this article, let more people see the difficulties of making excellent games under limited resources, as well as code design Exquisite.
Finally, I would like to thank the author of this project. There is no such article without his payment. If you feel that this article is not bad, welcome to share to more people.