starting on hardware sprites

This commit is contained in:
John Bintz 2024-02-23 21:29:36 -05:00
parent 3e18d9cdb2
commit 23bc034f6f
9 changed files with 1739 additions and 14 deletions

20
arena.c
View File

@ -1,13 +1,14 @@
#include "bmpload.h"
#include "const.h"
#include "arena.h"
#include "system/vga.h"
char arenaLayout[10][10] = {
{ 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 },
{ 1, 2, 2, 2, 0, 0, 2, 2, 2, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 2, 0, 0, 0, 0, 0, 0, 0, 0, 2 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
@ -37,9 +38,14 @@ void setupWallSprites(struct BMPImage *spritesheetImage) {
);
}
extern void renderFloorTile(byte *);
// push items onto stack
#pragma aux (__cdecl) renderFloorTile "renderFloorTile_"
void renderArenaTile(int x, int y) {
char tile;
struct SpriteRender *target;
byte *drawBuffer;
tile = arenaLayout[y][x];
@ -58,7 +64,15 @@ void renderArenaTile(int x, int y) {
target->x = x * TILE_SIZE;
target->y = y * TILE_SIZE;
drawSprite(target);
if (tile == 0) {
drawBuffer = getDrawBuffer();
renderFloorTile(
drawBuffer + target->x + target->y * VGA_DISPLAY_WIDTH
);
} else {
drawSprite(target);
}
}
char arenaRedrawRequests[ARENA_HEIGHT_TILES][ARENA_WIDTH_TILES];

174
bin/build_spritesheet_asm.rb Executable file
View File

@ -0,0 +1,174 @@
#!/usr/bin/env ruby
require 'rmagick'
require 'yaml'
require 'erb'
data = YAML.load(File.read('spritesheet.yml'))
class Sprite
def initialize(image:, sx:, sy:, width:, height:)
@image = image
@sx = sx
@sy = sy
@width = width
@height = height
end
def pixel_data
@height.times.each_with_object([]) do |y, obj|
obj << @width.times.map do |x|
color = @image.pixel_color(x + @sx, y + @sy)
colormap.find_index { |c| c == color }
end
end
end
private
def colormap
@colormap ||= @image.colors.times.map do |i|
Magick::Pixel.from_color(@image.colormap(i))
end
end
end
MovBytePtr = Data.define(:offset, :data)
ASM_TEMPLATE = <<~ASM
<% sprites.each do |sprite| %>
PUBLIC <%= sprite.exported_name %><% end %>
.386
.model flat,c
.CODE
<% sprites.each do |sprite| %>
<%= sprite.exported_name %>:
push ebp
mov ebp, esp
push ebx
mov eax, [ebp + 8]
<% sprite.bytes.each do |byte| %>
mov BYTE PTR [eax + <%= byte.offset %>], <%= byte.data %><% end %>
pop ebx
pop ebp
ret
<% end %>
end
ASM
C_TEMPLATE = <<~C
<% sprites.each do |sprite| %>
extern void <%= sprite.function_name %>(byte *);
#pragma aux (__cdecl) <%= sprite.function_name %> "<%= sprite.exported_name %>"
<% end %>
C
class AssemblerSprite
def initialize(
name:,
pixel_data:,
screen_width:,
transparent_color:,
offset_x:,
offset_y:,
prefix:
)
@name = name
@pixel_data = pixel_data
@screen_width = screen_width
@transparent_color = transparent_color
@offset_x = offset_x
@offset_y = offset_y
@prefix = prefix
end
def exported_name
"#{function_name}_"
end
def function_name
"#{@prefix}_#{@name}"
end
def bytes
return @bytes if defined?(@bytes)
bytes = []
@pixel_data.each_with_index do |row, y|
row.each_with_index do |column, x|
next if @pixel_data[y][x] == @transparent_color
bytes << MovBytePtr.new(
data: @pixel_data[y][x],
offset: x - @offset_x + (y - @offset_y) * @screen_width
)
end
end
bytes
end
end
Screen = Data.define(:width, :transparent_color)
SpriteData = Data.define(:image, :name, :position, :dimensions, :offset, :prefix)
screen = Screen.new(
width: data["screen"]["width"],
transparent_color: data["screen"]["transparentColor"]
)
sprite_data = []
data["files"].each do |spritesheet|
image = Magick::Image.read(spritesheet["file"]).first
spritesheet["sprites"].each do |name, details|
sprite_data << SpriteData.new(
image:,
name:,
prefix: spritesheet["prefix"],
position: details["position"],
dimensions: details["dimensions"],
offset: details["offset"]
)
end
end
sprites = sprite_data.map do |sd|
sprite = Sprite.new(
image: sd.image,
sx: sd.position[0],
sy: sd.position[1],
width: sd.dimensions[0],
height: sd.dimensions[1],
)
AssemblerSprite.new(
name: sd.name,
prefix: sd.prefix,
pixel_data: sprite.pixel_data,
screen_width: screen.width,
transparent_color: screen.transparent_color,
offset_x: sd.offset[0],
offset_y: sd.offset[1],
)
end
File.open('sprites.asm', 'w') do |fh|
fh.puts ERB.new(ASM_TEMPLATE).result_with_hash(sprites:)
end
File.open('sprites.h', 'w') do |fh|
fh.puts ERB.new(C_TEMPLATE).result_with_hash(sprites:)
end
puts "sprites.{asm,h} written"

7
game.c
View File

@ -331,16 +331,11 @@ int currentSpeedCalc = 0;
double averageSpeedCalc;
clock_t startTime;
extern int doAThing(int);
#pragma aux (__cdecl) doAThing "doAThing_"
// non cdecl is put params into eax, ebx, and ecx
int main(void) {
int keepRunning = 1;
fprintf(stderr, "%d\n", doAThing(1000));
return 0;
if (setupGame()) return 1;
maybeSpawnEnemy();

View File

@ -10,6 +10,9 @@ system/system.lib: .SYMBOLIC
.c.o:
wcc386 -q -bt=dos $<
.asm.o:
wasm $<
game.exe: $(obj) system/system.lib
wcl386 -q -bt=dos -l=dos4g $(obj) system/system.lib

1434
sprites.asm Normal file

File diff suppressed because it is too large Load Diff

22
sprites.h Normal file
View File

@ -0,0 +1,22 @@
extern void sprite_arenaWallTop(byte *);
#pragma aux (__cdecl) sprite_arenaWallTop "sprite_arenaWallTop_"
extern void sprite_arenaWallSide(byte *);
#pragma aux (__cdecl) sprite_arenaWallSide "sprite_arenaWallSide_"
extern void sprite_arenaFloor(byte *);
#pragma aux (__cdecl) sprite_arenaFloor "sprite_arenaFloor_"
extern void sprite_rabbit(byte *);
#pragma aux (__cdecl) sprite_rabbit "sprite_rabbit_"
extern void sprite_mouse(byte *);
#pragma aux (__cdecl) sprite_mouse "sprite_mouse_"
extern void sprite_bullet(byte *);
#pragma aux (__cdecl) sprite_bullet "sprite_bullet_"
extern void sprite_enemy(byte *);
#pragma aux (__cdecl) sprite_enemy "sprite_enemy_"

35
spritesheet.yml Normal file
View File

@ -0,0 +1,35 @@
screen:
width: 320
transparentColor: 0
files:
- file: sprtsht.bmp
prefix: "sprite"
sprites:
arenaWallTop:
position: [0, 0]
dimensions: [20, 20]
offset: [10, 10]
arenaWallSide:
position: [20, 0]
dimensions: [20, 20]
offset: [0, 0]
arenaFloor:
position: [40, 0]
dimensions: [20, 20]
offset: [0, 0]
rabbit:
position: [0, 20]
dimensions: [16, 16]
offset: [8, 8]
mouse:
position: [16, 20]
dimensions: [8, 8]
offset: [4, 4]
bullet:
position: [16, 28]
dimensions: [4, 4]
offset: [1, 1]
enemy:
position: [0, 20]
dimensions: [16, 16]
offset: [8, 8]

View File

@ -22,7 +22,7 @@ byte *initializeDrawBuffer() {
}
void copyDrawBufferToDisplay() {
memcpy(VGA, drawBuffer, 320 * 200);
memcpy(VGA, drawBuffer, VGA_DISPLAY_WIDTH * VGA_DISPLAY_HEIGHT);
}
void freeDrawBuffer() {

View File

@ -1,10 +1,11 @@
PUBLIC doAThing_
#PUBLIC doAThing_
#PUBLIC renderTopTile_
PUBLIC renderFloorTile_
.386
.model flat,c
_TEXT segment byte public 'CODE'
.CODE
doAThing_:
push ebp
mov ebp, esp
@ -14,6 +15,53 @@ doAThing_:
pop ebp
ret
_TEXT ends
renderTopTile_:
push ebp
mov ebp, esp
push ebx
mov eax, [ebp + 8]
mov ebx, 20
_renderNextLine:
mov DWORD PTR [eax], 02020202h
mov DWORD PTR [eax + 4], 02020202h
mov DWORD PTR [eax + 8], 02020202h
mov DWORD PTR [eax + 12], 02020202h
mov DWORD PTR [eax + 16], 02020202h
add eax, 320
dec ebx
cmp ebx, 0
jne _renderNextLine
pop ebx
pop ebp
ret
renderFloorTile_:
push ebp
mov ebp, esp
push ebx
mov eax, [ebp + 8]
mov ebx, 20
_renderNextFloorLine:
mov DWORD PTR [eax], 04040404h
mov DWORD PTR [eax + 4], 04040404h
mov DWORD PTR [eax + 8], 04040404h
mov DWORD PTR [eax + 12], 04040404h
mov DWORD PTR [eax + 16], 04040404h
add eax, 320
dec ebx
cmp ebx, 0
jne _renderNextFloorLine
_done:
pop ebx
pop ebp
ret
end