add flag computer wp temp
@@ -1 +1,53 @@
|
||||
# Flag 计算机
|
||||
|
||||
此题目基于 [https://github.com/skeeto/dosdefender-ld31](https://github.com/skeeto/dosdefender-ld31) 修改而成。
|
||||
|
||||
```
|
||||
由于我太菜了,所以引入了一个 BUG 导致题目下线修改后上线。给大家带来了不便,十分抱歉。
|
||||
```
|
||||
|
||||
这道题主要想展现的效果是在 DOS 和 Windows 上都能运行的程序。在 Windows 上运行将弹出一个对话框。
|
||||

|
||||
这和我们在 DOS 上运行 Windows PE 程序在 DOS 中输出"This program cannot be run in DOS mode"相照应。如果我们把程序载入 010editor就会发现一些猫腻。
|
||||

|
||||
我们会发现原来的 DOS stub 充满了代码。实际上我是先编译了一个 COM 可执行文件,然后手动为 COM 文件加上 DOS 头。所以在此过程中 DOS 中的一些值是手工进行设置的。
|
||||
然后我将能够运行的 DOS 可执行文件设置为 visual studio 2019 中的 exe 的 DOS stub。最终编译成了你们现在看到的样子。
|
||||
|
||||
## 逆向分析
|
||||
|
||||
我们将它拖入ida
|
||||

|
||||
我们选择让 IDA 分析 MS-DOS executeble。
|
||||
然后我们观察 start 函数。
|
||||

|
||||
我们发现它调用了一个函数,然后执行了 DOS exit 中断,IDA 已经将其标注了出来。
|
||||
|
||||
```
|
||||
这里用了一个 CPU 指令前缀的编译方法,使得我们的 80386 能够在实模式使用 32 位操作数。0x66
|
||||
:它属于 Prefix group 3,Operand-size override prefix。
|
||||
```
|
||||
有了这样一个 opcode 会有怎么样的行为呢?
|
||||

|
||||
可以看到 operand size 变为了 32bit。
|
||||
我们分析 102f 这个函数。
|
||||

|
||||
可以看到使用了大量使用这个技巧的痕迹,这就是为什么我们能在实模式使用 32bit 寄存器的原因。
|
||||
|
||||
```
|
||||
asm (".code16gcc\n"
|
||||
"call dosmain\n"
|
||||
"mov $0x4C,%ah\n"
|
||||
"int $0x21\n");
|
||||
```
|
||||
最先展开的 include 项目中有我们的 init.h 。
|
||||
这个文件中的一个伪指令 ".code16cc" 代表着让gcc生成 16bit realmode 模式的汇编指令。所以这是为什么大量指令前都有 0x66 的原因。当我们的操作数是类似于 uint32 声明时,0x66 就会起作用。
|
||||
逆向的核心过程就是明白程序干了哪些事情,橙色的操作数代表着地址,我们将鼠标悬停在上面,然后右键把它类型转换一下。
|
||||

|
||||
IDA 就能识别字符串了。
|
||||
ps: 作为一个CTF玩家,我最喜欢的事情就是按 F5 ,hackergame 是为了让人能学到东西,而不是千篇一律的难题,不是为了难而难。所以我程序在 realmode 下,IDA 失效了,这样大家都能从 “0” 接触汇编。
|
||||
显而易见的是,参数是字符串的一般就是我们的类似的 printf 函数。只不过这里是 vga 的输出而已。
|
||||

|
||||
我们看整个控制流,发现程序并不复杂,大多数都是控制显示的逻辑。
|
||||
大家可以对着源代码再看一遍,这个函数就是对应的 main 函数。
|
||||
|
||||
下面讲下程序的核心逻辑。
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 355 KiB |
|
After Width: | Height: | Size: 270 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 140 KiB |
|
After Width: | Height: | Size: 470 KiB |
|
After Width: | Height: | Size: 363 KiB |
|
After Width: | Height: | Size: 64 KiB |
@@ -0,0 +1,2 @@
|
||||
*.com
|
||||
*.o
|
||||
@@ -0,0 +1,20 @@
|
||||
CC = gcc
|
||||
DOS = dosbox
|
||||
CFLAGS = -std=gnu99 -w -O0 -nostdlib -m32 -march=i386 \
|
||||
-Wno-unused-function \
|
||||
-ffreestanding -fomit-frame-pointer -fwrapv -fno-strict-aliasing \
|
||||
-fno-leading-underscore -fno-pic -fno-stack-protector \
|
||||
-Wl,--nmagic,-static,-Tcom.ld
|
||||
|
||||
dosdef.com : dosdef.c *.h
|
||||
|
||||
.PHONY : all clean test
|
||||
|
||||
clean :
|
||||
$(RM) *.com
|
||||
|
||||
test : dosdef.com
|
||||
$(DOS) $^
|
||||
|
||||
%.com : %.c
|
||||
$(CC) -o $@ $(CFLAGS) $<
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
enabled: true
|
||||
name: Flag 计算机
|
||||
category: binary
|
||||
url: files/get_flag_system.exe
|
||||
index: 270
|
||||
flags:
|
||||
- name: ''
|
||||
score: 300
|
||||
type: text
|
||||
flag: flag{g3tfl4g_0p3r4t1ng_syst3m}
|
||||
---
|
||||
|
||||
一个玄学的 flag 计算机,平常的时候只会摸鱼,但如果在**合适的时间**运行它的话,它就可以告诉你真正的 flag。
|
||||
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef ALLOC_H
|
||||
#define ALLOC_H
|
||||
|
||||
#include "int.h"
|
||||
|
||||
extern char _heap;
|
||||
static char *hbreak = &_heap;
|
||||
|
||||
static void *sbrk(size_t size)
|
||||
{
|
||||
char *ptr = hbreak;
|
||||
for (size_t i = 0; i < size; i++)
|
||||
ptr[i] = 0;
|
||||
hbreak += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void free(void)
|
||||
{
|
||||
hbreak = &_heap;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
OUTPUT_FORMAT(binary)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x0100;
|
||||
.text :
|
||||
{
|
||||
*(.text);
|
||||
}
|
||||
.data :
|
||||
{
|
||||
*(.data);
|
||||
*(.bss);
|
||||
*(.rodata);
|
||||
}
|
||||
_heap = ALIGN(4);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# Suggested DOSBox configuration for DOS Defender
|
||||
# Usage: dosbox -conf dosbox.conf dosdef.com
|
||||
|
||||
[sdl]
|
||||
output=opengl
|
||||
windowresolution=1280x800
|
||||
|
||||
[cpu]
|
||||
core=dynamic
|
||||
cycles=max
|
||||
@@ -0,0 +1,261 @@
|
||||
#include "init.h"
|
||||
#include "print.h"
|
||||
#include "joystick.h"
|
||||
#include "vga.h"
|
||||
#include "rand.h"
|
||||
#include "time.h"
|
||||
#include "alloc.h"
|
||||
#include "keyboard.h"
|
||||
#include "speaker.h"
|
||||
|
||||
#define SCALE 1000
|
||||
#define BACKGROUND 17
|
||||
#define PLAYER 14
|
||||
#define BULLET_SPEED 3
|
||||
#define PARTICLE_MAX_AGE 50
|
||||
#define MAX_PLAYERS 2
|
||||
|
||||
typedef unsigned int tick_t;
|
||||
typedef void (*ai_t)(int id);
|
||||
typedef void (*power_t)(int id);
|
||||
|
||||
static tick_t ticks;
|
||||
static unsigned score;
|
||||
static unsigned best_score;
|
||||
static struct speaker speaker;
|
||||
|
||||
struct ship {
|
||||
int32_t x, y, dx, dy;
|
||||
tick_t last_fire;
|
||||
ai_t ai;
|
||||
struct sample *fx_fire;
|
||||
uint16_t score;
|
||||
uint16_t hp, hp_max;
|
||||
uint8_t radius;
|
||||
uint8_t fire_delay;
|
||||
uint8_t fire_damage;
|
||||
uint8_t drop_rate;
|
||||
uint8_t color_a, color_b;
|
||||
bool is_player;
|
||||
union {
|
||||
int target_ship;
|
||||
struct {
|
||||
uint32_t x, y;
|
||||
} target_position;
|
||||
};
|
||||
};
|
||||
|
||||
struct bullet {
|
||||
int32_t x, y, dx, dy;
|
||||
tick_t birthtick;
|
||||
uint8_t color;
|
||||
uint8_t damage;
|
||||
bool alive;
|
||||
};
|
||||
|
||||
struct particle {
|
||||
int32_t x, y;
|
||||
tick_t birthtick;
|
||||
bool alive;
|
||||
};
|
||||
|
||||
struct powerup {
|
||||
int32_t x, y;
|
||||
tick_t birthtick;
|
||||
power_t power;
|
||||
bool alive;
|
||||
uint8_t color;
|
||||
};
|
||||
|
||||
static struct bullet *bullets;
|
||||
static size_t bullets_max = 32;
|
||||
|
||||
static struct particle *particles;
|
||||
static size_t particles_max = 64;
|
||||
|
||||
static struct ship *ships;
|
||||
static size_t ships_max = 12;
|
||||
|
||||
static struct powerup *powerups;
|
||||
static size_t powerups_max = 8;
|
||||
|
||||
static bool joystick_detected()
|
||||
{
|
||||
struct joystick joystick;
|
||||
joystick_read(&joystick);
|
||||
return joystick.axis[0] != 0 || joystick.axis[1] != 0;
|
||||
}
|
||||
|
||||
static void burn(int32_t x, int32_t y);
|
||||
static void ship_draw(int id, bool clear);
|
||||
static void powerup_random(int id);
|
||||
|
||||
|
||||
// #define VGA_PWIDTH 320
|
||||
// #define VGA_PHEIGHT 200
|
||||
// 53 character a line
|
||||
|
||||
static unsigned short strlen(const char * in)
|
||||
{
|
||||
short ret=0;
|
||||
while(in[ret] != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned short str_line_middle(unsigned short length)
|
||||
{
|
||||
return (53 - length) / 2 * 6;
|
||||
}
|
||||
|
||||
static void delay()
|
||||
{
|
||||
for(unsigned short i = 0; i<0xf;i++)
|
||||
{
|
||||
usleep(800);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// static uint32_t rand_seed = 375226057;
|
||||
|
||||
// static uint32_t rand(void)
|
||||
// {
|
||||
// rand_seed = rand_seed * 1103515245 + 12345;
|
||||
// return rand_seed;
|
||||
// }
|
||||
|
||||
// static uint32_t randn(uint32_t n)
|
||||
// {
|
||||
// return (rand() >> 7) % n;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
uint32_t a,x0,b;
|
||||
|
||||
static uint32_t gen_random_a()
|
||||
{
|
||||
a = get_time() % 58379;
|
||||
// a = 26141;
|
||||
x0 = 1103515245;
|
||||
}
|
||||
|
||||
static uint32_t myrand(void)
|
||||
{
|
||||
x0 = x0 * a + 12345678;
|
||||
return x0;
|
||||
}
|
||||
|
||||
// flag{g3tfl4g_0p3r4t1ng_syst3m}
|
||||
uint32_t get_matrix[15];
|
||||
uint32_t my_matrix[15*15]={20597,19141,29258,17804,29076,28746,24890,28979,26196,31833,26624,24774,18916,29028,24033,22913,23436,25750,26539,21652,31296,22446,16506,21949,22761,30221,29477,29617,16497,23022,23179,30781,23877,29171,31665,26534,32159,22583,27525,28708,31216,17158,31988,32190,23747,21272,21278,24727,29984,25303,23445,23119,23155,26346,26389,30747,28948,31418,21323,31758,30911,18790,21312,25099,22348,25409,29357,22180,23588,28794,18133,25624,21972,23401,24821,31369,25187,31517,19840,28836,20794,20239,24523,30814,24016,17954,21227,16691,30290,23391,20482,24822,31968,30651,27908,22690,30875,31003,31747,19978,25482,18563,30143,27788,26658,26295,23244,27086,26456,24251,28647,22783,27460,19187,23252,24078,19203,26251,18113,19542,24533,16666,24038,32744,28670,30438,26379,18591,30109,26509,20947,27696,22945,27542,32128,25416,21675,19389,27085,29380,20163,21102,30936,30862,18230,21904,16938,16579,20641,27551,22740,24666,16836,23306,27661,26506,28623,29816,20166,29405,23982,30046,19365,24926,19029,32448,17567,17156,18678,28594,19769,28631,25769,31309,24457,30625,21825,29811,17112,31370,25345,24333,24005,31606,30942,21441,30599,22894,18015,19994,27901,26868,21948,27614,23449,21289,19588,19955,28133,16696,31509,26219,19946,27895,28760,28547,28315,16614,26006,17129,24769,24608,17714,17682,18532,17597,29247,28789,27011,29841,32640,17508,27662,23548,29514};
|
||||
|
||||
|
||||
int dosmain(void)
|
||||
{
|
||||
|
||||
vga_on();
|
||||
vga_clear(BACKGROUND);
|
||||
vga_vsync();
|
||||
|
||||
vga_print((struct point){str_line_middle(strlen("WWWWWWWWWWWWWOOOOOOOOOOWWWWWOO OOOOOOOWWWWWWWWWWWWWWW")), 14}, MAGENTA, "WWWWWWWWWWWWWOOOOOOOOOOWWWWWOO OOOOOOOWWWWWWWWWWWWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWWWWWWWOOO OONWWWWWOOO OOOWWWWWWWWWW")), 23}, MAGENTA, "WWWWWWWWOOO OONWWWWWOOO OOOWWWWWWWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWWWWWOOO OOOdOOWWWWMMWOOdOOO OdOWWWWWWWW")), 32}, MAGENTA, "WWWWWWOOO OOOdOOWWWWMMWOOdOOO OdOWWWWWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWWWWOO OOdONWOOOdOOOOOdOOOWNOOO OOWWWWWWW")), 41}, MAGENTA, "WWWWWOO OOdONWOOOdOOOOOdOOOWNOOO OOWWWWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWWNOO OONWOOOOO OOOOOWOOO OOWWWWWW")), 50}, MAGENTA, "WWWNOO OONWOOOOO OOOOOWOOO OOWWWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWWOO OOOWOOO OdOWOOO OOWWWWW")), 59}, MAGENTA, "WWWOO OOOWOOO OdOWOOO OOWWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWOO OONWOO OOWNdO OOWWWW")), 68}, MAGENTA, "WWOO OONWOO OOWNdO OOWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WNO OONWOO OOWNOO OONWWW")), 77}, MAGENTA, "WNO OONWOO OOWNOO OONWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WOO OOWOO OOWOO OOMWW")), 86}, MAGENTA, "WOO OOWOO OOWOO OOMWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WOO OOMOO OOWOO OOWWW")), 95}, MAGENTA, "WOO OOMOO OOWOO OOWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WOO OOWNdO OOWWOO ONMWO")), 104}, MAGENTA, "WOO OOWNdO OOWWOO ONMWO");
|
||||
vga_print((struct point){str_line_middle(strlen("WWOO OOWOOO OdNWOO OOWWWO")), 113}, MAGENTA, "WWOO OOWOOO OdNWOO OOWWWO");
|
||||
vga_print((struct point){str_line_middle(strlen("WWNOO OOWNOO OOOWNOO OdNMWWW")), 122}, MAGENTA, "WWNOO OOWNOO OOOWNOO OdNMWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWWOOO OOOWOOOO OOONWOOO OONWWWWW")), 131}, MAGENTA, "WWWOOO OOOWOOOO OOONWOOO OONWWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWWWOdO OOONWOOdOOOOOOOOOdOOWNOOO OdNWWWWWW")), 140}, MAGENTA, "WWWWOdO OOONWOOdOOOOOOOOOdOOWNOOO OdNWWWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWWWWNOOO OOOOOOWWNNNNWWNOOOOOO OOOWWWWWWWW")), 149}, MAGENTA, "WWWWWNOOO OOOOOOWWNNNNWWNOOOOOO OOOWWWWWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWWWWWWOOOO OOdNWWWWMNdOO OOONWWWWWWWWW")), 158}, MAGENTA, "WWWWWWWOOOO OOdNWWWWMNdOO OOONWWWWWWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWWWWWWWWWWWOOOOOO OOMWWWWOO OOOOOOWWWWWMWWWWWWWW")), 167}, MAGENTA, "WWWWWWWWWWWWOOOOOO OOMWWWWOO OOOOOOWWWWWMWWWWWWWW");
|
||||
vga_print((struct point){str_line_middle(strlen("WWWWWWWWWWWWWWWNOOOOOONWWWWWNOOdOOONWWWWWWWWWWWWWWWWW")), 176}, MAGENTA, "WWWWWWWWWWWWWWWNOOOOOONWWWWWNOOdOOONWWWWWWWWWWWWWWWWW");
|
||||
delay();
|
||||
vga_clear(BACKGROUND);
|
||||
gen_random_a();
|
||||
uint32_t rand_gen[15];
|
||||
for(uint32_t i=0;i<15;i++)
|
||||
{
|
||||
rand_gen[i] = myrand();
|
||||
}
|
||||
for(uint32_t i=0;i<15;i++)
|
||||
{
|
||||
for(uint32_t j=0;j<15;j++)
|
||||
{
|
||||
get_matrix[i] += (my_matrix[i*15+j] * rand_gen[j]) & (0xffff);
|
||||
get_matrix[i] = get_matrix[i] & 0xffff;
|
||||
vga_clear(BACKGROUND);
|
||||
vga_print((struct point){str_line_middle(strlen("GETFLAG OPERATING SYSTEM")), 5}, MAGENTA, "GETFLAG OPERATING SYSTEM");
|
||||
vga_print((struct point){0, 15}, MAGENTA, "OPERATING SYSTEM USE AI TECH");
|
||||
vga_print((struct point){0, 25}, MAGENTA, "SETTING A RANDOM FLAG SEED");
|
||||
vga_print((struct point){0, 35}, RED, "CHECKED YOU ARE PLAYING USTC HACKERGAME");
|
||||
vga_print((struct point){0, 45}, MAGENTA, "LET ME COMPUTE FLAG FOR YOU");
|
||||
vga_print((struct point){0, 55}, MAGENTA, "IF I FIND THE FLAG I WILL PRINT IT");
|
||||
for(short k=0; k<0xff;k++)
|
||||
{
|
||||
vga_print((struct point){0, 65}, RED, "COMPUTING PLEASE WAIT");
|
||||
usleep(800);
|
||||
vga_print((struct point){0, 65}, BLACK, "COMPUTING PLEASE WAIT");
|
||||
usleep(800);
|
||||
}
|
||||
}
|
||||
}
|
||||
vga_clear(BACKGROUND);
|
||||
vga_print((struct point){str_line_middle(strlen("GETFLAG OPERATING SYSTEM")), 5}, MAGENTA, "GETFLAG OPERATING SYSTEM");
|
||||
vga_print((struct point){0, 15}, MAGENTA, "FOUND A SOLUTION");
|
||||
vga_print((struct point){0, 25}, MAGENTA, "FLAG IS IN PRINTABLE FORMAT");
|
||||
vga_print((struct point){0, 35}, MAGENTA, "THE SYSTEM MAY GET WRONG SEED");
|
||||
vga_print((struct point){0, 45}, MAGENTA, "YOU SHOULD RESET IT");
|
||||
for(int i=0; i < 0xff; i++)
|
||||
{
|
||||
for(int j=0; j < 0xa0; j++)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
delay();
|
||||
vga_off();
|
||||
// 187 49114 12533 39320 44039 25566 22160 10990 15801 27249 45757 55153 58050 7065 14228
|
||||
// printl(get_matrix[0]);
|
||||
// printl(get_matrix[1]);
|
||||
// printl(get_matrix[2]);
|
||||
// printl(get_matrix[3]);
|
||||
// printl(get_matrix[4]);
|
||||
// printl(get_matrix[5]);
|
||||
// printl(get_matrix[6]);
|
||||
// printl(get_matrix[7]);
|
||||
// printl(get_matrix[8]);
|
||||
// printl(get_matrix[9]);
|
||||
// printl(get_matrix[10]);
|
||||
// printl(get_matrix[11]);
|
||||
// printl(get_matrix[12]);
|
||||
// printl(get_matrix[13]);
|
||||
// printl(get_matrix[14]);
|
||||
char out[40];
|
||||
|
||||
out[30] = '$';
|
||||
out[31] = '$';
|
||||
out[32] = '$';
|
||||
out[33] = '$';
|
||||
print("\nYOU SHOULD CHECK PRINT OUT STRING ASCII$$");
|
||||
print("\nDOS ONLY PRINT UPPERCASE LETTERS$$");
|
||||
print("\n$$$$");
|
||||
unsigned short aim[30]={221, 49078, 12436, 39423, 44156, 25529, 22179, 10906, 15839, 27165, 45705, 55062, 58013, 7081, 14308, 136, 49064, 12481, 39404, 44086, 25520, 22263, 10929, 15818, 27144, 45774, 55045, 58097, 7156, 14313};
|
||||
for(short i=0; i<30;i++)
|
||||
{
|
||||
out[i] = (char)(aim[i] ^ get_matrix[i%15]);
|
||||
}
|
||||
print(out);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/* NOTICE: This must be included first! */
|
||||
asm (".code16gcc\n"
|
||||
"call dosmain\n"
|
||||
"mov $0x4C,%ah\n"
|
||||
"int $0x21\n");
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef INT_H
|
||||
#define INT_H
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef char int8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef short int16_t;
|
||||
typedef unsigned long int uint32_t;
|
||||
typedef long int int32_t;
|
||||
typedef unsigned long long int uint64_t;
|
||||
typedef long long int int64_t;
|
||||
typedef unsigned short size_t;
|
||||
|
||||
#define true 1
|
||||
#define false 0
|
||||
typedef char bool;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "math.h"
|
||||
#include "int.h"
|
||||
|
||||
struct joystick {
|
||||
uint16_t axis[4];
|
||||
bool button[4];
|
||||
};
|
||||
|
||||
struct joystick_config {
|
||||
uint16_t min[4];
|
||||
uint16_t max[4];
|
||||
uint16_t center[4];
|
||||
};
|
||||
|
||||
static struct joystick_config joystick_config = {
|
||||
{0xffff, 0xffff, 0xffff, 0xffff},
|
||||
{0x0000, 0x0000, 0x0000, 0x0000},
|
||||
{0x0000, 0x0000, 0x0000, 0x0000},
|
||||
};
|
||||
|
||||
static void joystick_read(struct joystick *joystick)
|
||||
{
|
||||
asm volatile ("mov $0x84, %%ah\n"
|
||||
"mov $1, %%dx\n"
|
||||
"int $0x15\n"
|
||||
: "=a"(joystick->axis[0]), "=b"(joystick->axis[1]),
|
||||
"=c"(joystick->axis[2]), "=d"(joystick->axis[3]));
|
||||
for (int i = 0; i < 4; i++) {
|
||||
joystick_config.min[i] =
|
||||
min(joystick_config.min[i], joystick->axis[i]);
|
||||
joystick_config.max[i] =
|
||||
max(joystick_config.max[i], joystick->axis[i]);
|
||||
}
|
||||
uint16_t buttons = 0;
|
||||
asm volatile ("mov $0x84, %%ah\n"
|
||||
"mov $0, %%dx\n"
|
||||
"int $0x15\n"
|
||||
: "=a"(buttons)
|
||||
: /**/
|
||||
: "bx", "cx", "dx");
|
||||
for (int i = 0; i < 4; i++)
|
||||
joystick->button[i] = !(buttons & (1 << (i + 4)));
|
||||
}
|
||||
|
||||
#include "vga.h"
|
||||
#include "vga_font.h"
|
||||
|
||||
static void joystick_crosshair(struct point c, uint8_t color)
|
||||
{
|
||||
vga_line((struct point){c.x - 2, c.y},
|
||||
(struct point){c.x + 2, c.y}, color);
|
||||
vga_line((struct point){c.x, c.y - 2},
|
||||
(struct point){c.x, c.y + 2}, color);
|
||||
}
|
||||
|
||||
static void joystick_calibrate()
|
||||
{
|
||||
vga_clear(BLUE);
|
||||
int32_t scaled[4] = {};
|
||||
struct joystick joy;
|
||||
do {
|
||||
vga_vsync();
|
||||
joystick_crosshair((struct point){scaled[0], scaled[1]}, BLUE);
|
||||
joystick_crosshair((struct point){scaled[2], scaled[3]}, BLUE);
|
||||
joystick_read(&joy);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int32_t scale = joystick_config.max[i] - joystick_config.min[i];
|
||||
if (scale != 0) {
|
||||
scaled[i] = joy.axis[i] - joystick_config.min[i];
|
||||
scaled[i] *= (i & 0x01) ? VGA_PHEIGHT : VGA_PWIDTH;
|
||||
scaled[i] /= scale;
|
||||
}
|
||||
}
|
||||
joystick_crosshair((struct point){scaled[0], scaled[1]}, WHITE);
|
||||
joystick_crosshair((struct point){scaled[2], scaled[3]}, YELLOW);
|
||||
vga_print((struct point){55, 80}, YELLOW,
|
||||
"MOVE JOYSTICK AROUND ITS FULL RANGE");
|
||||
vga_print((struct point){43, 113}, YELLOW,
|
||||
"THEN CENTER JOYSTICK AND PRESS BUTTON A");
|
||||
vga_pixel((struct point){VGA_PWIDTH / 2, VGA_PHEIGHT / 2}, BLACK);
|
||||
} while (!joy.button[0]);
|
||||
for (int i = 0; i < 4; i++)
|
||||
joystick_config.center[i] = joy.axis[i];
|
||||
vga_clear(LIGHT_BLUE);
|
||||
do
|
||||
joystick_read(&joy);
|
||||
while (joy.button[0]);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "int.h"
|
||||
|
||||
static bool kbhit()
|
||||
{
|
||||
bool result;
|
||||
asm volatile ("mov $1, %%ah\n"
|
||||
"int $0x16\n"
|
||||
"jnz set%=\n"
|
||||
"mov $0, %0\n"
|
||||
"jmp done%=\n"
|
||||
"set%=:\n"
|
||||
"mov $1, %0\n"
|
||||
"done%=:\n"
|
||||
: "=rm"(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint16_t kb_read()
|
||||
{
|
||||
uint16_t key;
|
||||
asm volatile ("mov $1, %%ah\n"
|
||||
"int $0x16\n"
|
||||
"jnz get%=\n"
|
||||
"mov $0, %%ax\n"
|
||||
"jmp done%=\n"
|
||||
"get%=:\n"
|
||||
"mov $0, %%ah\n"
|
||||
"int $0x16\n"
|
||||
"jmp done%=\n"
|
||||
"done%=:\n"
|
||||
: "=a"(key));
|
||||
return key;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef MATH_H
|
||||
#define MATH_H
|
||||
|
||||
static inline int abs(int x)
|
||||
{
|
||||
return x < 0 ? -x : x;
|
||||
}
|
||||
|
||||
static inline int max(int a, int b)
|
||||
{
|
||||
return a < b ? b : a;
|
||||
}
|
||||
|
||||
static inline int min(int a, int b)
|
||||
{
|
||||
return b < a ? b : a;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
20597,19141,29258,17804,29076,28746,24890,28979,26196,31833,26624,24774,18916,29028,24033,22913,23436,25750,26539,21652,31296,22446,16506,21949,22761,30221,29477,29617,16497,23022,23179,30781,23877,29171,31665,26534,32159,22583,27525,28708,31216,17158,31988,32190,23747,21272,21278,24727,29984,25303,23445,23119,23155,26346,26389,30747,28948,31418,21323,31758,30911,18790,21312,25099,22348,25409,29357,22180,23588,28794,18133,25624,21972,23401,24821,31369,25187,31517,19840,28836,20794,20239,24523,30814,24016,17954,21227,16691,30290,23391,20482,24822,31968,30651,27908,22690,30875,31003,31747,19978,25482,18563,30143,27788,26658,26295,23244,27086,26456,24251,28647,22783,27460,19187,23252,24078,19203,26251,18113,19542,24533,16666,24038,32744,28670,30438,26379,18591,30109,26509,20947,27696,22945,27542,32128,25416,21675,19389,27085,29380,20163,21102,30936,30862,18230,21904,16938,16579,20641,27551,22740,24666,16836,23306,27661,26506,28623,29816,20166,29405,23982,30046,19365,24926,19029,32448,17567,17156,18678,28594,19769,28631,25769,31309,24457,30625,21825,29811,17112,31370,25345,24333,24005,31606,30942,21441,30599,22894,18015,19994,27901,26868,21948,27614,23449,21289,19588,19955,28133,16696,31509,26219,19946,27895,28760,28547,28315,16614,26006,17129,24769,24608,17714,17682,18532,17597,29247,28789,27011,29841,32640,17508,27662,23548,29514,
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef PORT_H
|
||||
#define PORT_H
|
||||
|
||||
#include "int.h"
|
||||
|
||||
static inline int16_t inportb(int16_t port)
|
||||
{
|
||||
volatile uint8_t value;
|
||||
asm volatile ("in %%dx, %%ax\n"
|
||||
: "=a"(value)
|
||||
: "d"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void outportb(int16_t port, uint8_t value)
|
||||
{
|
||||
asm volatile ("out %%al, %%dx\n"
|
||||
: /* no output */
|
||||
: "d"(port), "a"(value));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
static void print(char *string)
|
||||
{
|
||||
asm volatile ("mov $0x09, %%ah\n"
|
||||
"int $0x21\n"
|
||||
: /* no output */
|
||||
: "d"(string)
|
||||
: "ah");
|
||||
}
|
||||
|
||||
static void printl(unsigned long n)
|
||||
{
|
||||
volatile char buffer[12];
|
||||
int i = sizeof(buffer);
|
||||
buffer[--i] = '$';
|
||||
if (n == 0)
|
||||
buffer[--i] = '0';
|
||||
else
|
||||
for (; n > 0; n /= 10)
|
||||
buffer[--i] = '0' + (n % 10);
|
||||
print((char *) buffer + i);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef RANDOM_H
|
||||
#define RANDOM_H
|
||||
|
||||
#include "int.h"
|
||||
|
||||
#define RAND_MAX 4294967295
|
||||
|
||||
static uint32_t rand_seed = 375226057;
|
||||
|
||||
static uint32_t rand(void)
|
||||
{
|
||||
rand_seed = rand_seed * 1103515245 + 12345;
|
||||
return rand_seed;
|
||||
}
|
||||
|
||||
static uint32_t randn(uint32_t n)
|
||||
{
|
||||
return (rand() >> 7) % n;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,229 @@
|
||||
#pragma once
|
||||
|
||||
#include "int.h"
|
||||
#include "tone.h"
|
||||
|
||||
struct note {
|
||||
unsigned frequency;
|
||||
uint16_t length;
|
||||
};
|
||||
|
||||
struct sample {
|
||||
int priority;
|
||||
size_t length;
|
||||
struct note notes[];
|
||||
};
|
||||
|
||||
struct sample fx_menu_toggle = {
|
||||
.priority = 50,
|
||||
.length = 3,
|
||||
{
|
||||
{600, 2},
|
||||
{1000, 1},
|
||||
{700, 2}
|
||||
}
|
||||
};
|
||||
|
||||
struct sample fx_menu_select = {
|
||||
.priority = 55,
|
||||
.length = 4,
|
||||
{
|
||||
{800, 2},
|
||||
{900, 3},
|
||||
{1000, 4},
|
||||
{0, 10}
|
||||
}
|
||||
};
|
||||
|
||||
struct sample fx_intro_music = {
|
||||
.priority = 100,
|
||||
.length = 11,
|
||||
{
|
||||
{262, 10},
|
||||
{247, 10},
|
||||
{220, 10},
|
||||
{196, 10},
|
||||
{247, 10},
|
||||
{220, 10},
|
||||
{196, 10},
|
||||
{247, 10},
|
||||
{220, 10},
|
||||
{0, 10},
|
||||
{110, 20},
|
||||
}
|
||||
};
|
||||
|
||||
struct sample fx_end_music = {
|
||||
.priority = 200,
|
||||
.length = 15,
|
||||
{
|
||||
{62, 50},
|
||||
{0, 2},
|
||||
{62, 50},
|
||||
{0, 2},
|
||||
{62, 50},
|
||||
{0, 2},
|
||||
{73, 25},
|
||||
{0, 2},
|
||||
{65, 20},
|
||||
{0, 2},
|
||||
{62, 50},
|
||||
{0, 2},
|
||||
{55, 50},
|
||||
{0, 2},
|
||||
{62, 100},
|
||||
}
|
||||
};
|
||||
|
||||
struct sample fx_explode = {
|
||||
.priority = 10,
|
||||
.length = 11,
|
||||
{
|
||||
{62, 1},
|
||||
{47, 1},
|
||||
{20, 1},
|
||||
{96, 1},
|
||||
{47, 1},
|
||||
{20, 1},
|
||||
{96, 1},
|
||||
{47, 1},
|
||||
{20, 1},
|
||||
{89, 1},
|
||||
{60, 1},
|
||||
}
|
||||
};
|
||||
|
||||
struct sample fx_fire0 = {
|
||||
.priority = 0,
|
||||
.length = 4,
|
||||
{
|
||||
{200, 1},
|
||||
{190, 1},
|
||||
{180, 1},
|
||||
{170, 1}
|
||||
}
|
||||
};
|
||||
|
||||
struct sample fx_fire1 = {
|
||||
.priority = -10,
|
||||
.length = 5,
|
||||
{
|
||||
{150, 1},
|
||||
{120, 1},
|
||||
{100, 1},
|
||||
{120, 1},
|
||||
{135, 1}
|
||||
}
|
||||
};
|
||||
|
||||
struct sample fx_fire2 = {
|
||||
.priority = -5,
|
||||
.length = 5,
|
||||
{
|
||||
{300, 1},
|
||||
{320, 1},
|
||||
{330, 1},
|
||||
{335, 1},
|
||||
{340, 1}
|
||||
}
|
||||
};
|
||||
|
||||
struct sample fx_fire3 = {
|
||||
.priority = 1,
|
||||
.length = 14,
|
||||
{
|
||||
{50, 7},
|
||||
{75, 7},
|
||||
{100, 7},
|
||||
{150, 7},
|
||||
{200, 1},
|
||||
{250, 1},
|
||||
{300, 1},
|
||||
{370, 1},
|
||||
{360, 1},
|
||||
{350, 1},
|
||||
{340, 1},
|
||||
{330, 1},
|
||||
{320, 1},
|
||||
{310, 1}
|
||||
}
|
||||
};
|
||||
|
||||
struct sample fx_hit = {
|
||||
.priority = 5,
|
||||
.length = 5,
|
||||
{
|
||||
{120, 3},
|
||||
{130, 3},
|
||||
{140, 3},
|
||||
{150, 3},
|
||||
{160, 3}
|
||||
}
|
||||
};
|
||||
|
||||
struct sample fx_powerup = {
|
||||
.priority = 50,
|
||||
.length = 2,
|
||||
{
|
||||
{800, 3},
|
||||
{900, 3}
|
||||
}
|
||||
};
|
||||
|
||||
struct sample fx_boss = {
|
||||
.priority = 500,
|
||||
.length = 13,
|
||||
{
|
||||
{110, 15},
|
||||
{0, 2},
|
||||
{110, 15},
|
||||
{0, 2},
|
||||
{110, 5},
|
||||
{175, 30},
|
||||
{0, 2},
|
||||
{175, 15},
|
||||
{0, 2},
|
||||
{175, 15},
|
||||
{0, 2},
|
||||
{175, 5},
|
||||
{110, 30},
|
||||
}
|
||||
};
|
||||
|
||||
struct speaker {
|
||||
struct sample *sample;
|
||||
int index, step;
|
||||
};
|
||||
|
||||
void speaker_play(struct speaker *speaker, struct sample *sample)
|
||||
{
|
||||
if (!speaker->sample || speaker->sample->priority < sample->priority) {
|
||||
speaker->sample = sample;
|
||||
speaker->index = -1;
|
||||
speaker->step = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void speaker_step(struct speaker *speaker)
|
||||
{
|
||||
if (speaker->sample) {
|
||||
if (speaker->step) {
|
||||
speaker->step--;
|
||||
} else {
|
||||
speaker->index++;
|
||||
if (speaker->index >= speaker->sample->length) {
|
||||
speaker->sample = 0;
|
||||
tone_off();
|
||||
} else {
|
||||
struct note note = speaker->sample->notes[speaker->index];
|
||||
if (note.frequency == 0)
|
||||
tone_off();
|
||||
else {
|
||||
tone(note.frequency);
|
||||
tone_on();
|
||||
}
|
||||
speaker->step = note.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef TIME_H
|
||||
#define TIME_H
|
||||
|
||||
static uint32_t get_tick()
|
||||
{
|
||||
unsigned long result;
|
||||
asm volatile ("push %%es\n"
|
||||
"mov $0,%%bx\n"
|
||||
"mov %%bx,%%es\n"
|
||||
"mov $0x046C,%%bx\n"
|
||||
"mov %%es:(%%bx),%%eax\n"
|
||||
"pop %%es\n"
|
||||
: "=a"(result)
|
||||
: /* no inputs */
|
||||
: "bx");
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint32_t get_time()
|
||||
{
|
||||
uint16_t high, low;
|
||||
asm volatile ("mov $0x2C, %%ah\n"
|
||||
"int $0x21\n"
|
||||
: "=c"(high), "=d"(low)
|
||||
:
|
||||
: "ah");
|
||||
return (((uint32_t) high) << 16) | low;
|
||||
}
|
||||
|
||||
/* Granularity of 976 usec and doesn't work in DOSBox. */
|
||||
static void usleep(uint32_t us)
|
||||
{
|
||||
asm volatile ("mov $0x86, %%ah\n"
|
||||
"int $0x15\n"
|
||||
: /* no outputs */
|
||||
: "c"(us >> 16), "d"(us & 0x16)
|
||||
: "ah", "flags");
|
||||
}
|
||||
|
||||
/* Granularity of 55 msec. */
|
||||
static void msleep(int ms)
|
||||
{
|
||||
uint32_t count = ms / 54; // 18.2 Hz
|
||||
uint32_t last = get_tick();
|
||||
while (count) {
|
||||
uint32_t now = get_tick();
|
||||
if (now != last) {
|
||||
last = now;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "port.h"
|
||||
|
||||
static void tone_on()
|
||||
{
|
||||
outportb(0x61, inportb(0x61) | 0x03);
|
||||
}
|
||||
|
||||
static void tone_off()
|
||||
{
|
||||
outportb(0x61, inportb(0x61) & ~0x03);
|
||||
}
|
||||
|
||||
static void tone(unsigned frequency)
|
||||
{
|
||||
uint16_t period = 1193180 / frequency;
|
||||
outportb(0x42, period & 0xff);
|
||||
outportb(0x42, period >> 8);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#ifndef VGA_H
|
||||
#define VGA_H
|
||||
|
||||
#include "math.h"
|
||||
|
||||
#define VGA_PWIDTH 320
|
||||
#define VGA_PHEIGHT 200
|
||||
|
||||
enum VGA_COLOR {
|
||||
BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHT_GRAY, DARK_GRAY,
|
||||
LIGHT_BLUE, LIGHT_GREEN, LIGHT_CYAN, LIGHT_RED, LIGHT_MAGENTA,
|
||||
YELLOW, WHITE
|
||||
};
|
||||
|
||||
struct point {
|
||||
short x, y;
|
||||
};
|
||||
|
||||
struct rect {
|
||||
struct point tl, br;
|
||||
};
|
||||
|
||||
/* Switch to VGA mode 13 and set ES. */
|
||||
static void vga_on()
|
||||
{
|
||||
asm volatile ("mov $0x0013, %%ax\n"
|
||||
"int $0x10\n"
|
||||
"mov $0xA000, %%ax\n"
|
||||
"mov %%ax, %%es\n"
|
||||
: /* no outputs */
|
||||
: /* no inputs */
|
||||
: "ax");
|
||||
}
|
||||
|
||||
static void vga_off()
|
||||
{
|
||||
asm volatile ("mov $0x0003, %%ax\n"
|
||||
"int $0x10\n"
|
||||
"mov $0xA000, %%dx\n"
|
||||
"mov %%dx, %%es\n"
|
||||
: /* no outputs */
|
||||
: /* no inputs */
|
||||
: "ax", "dx");
|
||||
}
|
||||
|
||||
static void vga_pixel(volatile struct point p, uint8_t color)
|
||||
{
|
||||
if (p.x >= 0 && p.x < VGA_PWIDTH && p.y >= 0 && p.y < VGA_PHEIGHT)
|
||||
asm volatile ("imul $320, %%bx\n"
|
||||
"add %%ax, %%bx\n"
|
||||
"mov %%cl, %%es:(%%bx)\n"
|
||||
: /* no outputs */
|
||||
: "a"(p.x), "b"(p.y), "c"(color)
|
||||
: "dx");
|
||||
}
|
||||
|
||||
static void vga_clear(char color)
|
||||
{
|
||||
asm volatile ("mov %%al, %%ah\n"
|
||||
"mov $0, %%di\n"
|
||||
"push %%ax\n"
|
||||
"shl $16, %%eax\n"
|
||||
"pop %%ax\n"
|
||||
"mov $16000, %%cx\n"
|
||||
"rep\n"
|
||||
"stosl\n"
|
||||
: /* no outputs */
|
||||
: "a"(color)
|
||||
: "cx", "di");
|
||||
}
|
||||
|
||||
static void vga_line(struct point a, struct point b, uint8_t color)
|
||||
{
|
||||
int dx = abs(b.x - a.x), sx = a.x < b.x ? 1 : -1;
|
||||
int dy = abs(b.y - a.y), sy = a.y < b.y ? 1 : -1;
|
||||
int err = (dx > dy ? dx : -dy) / 2, e2;
|
||||
for(;;) {
|
||||
vga_pixel(a, color);
|
||||
if (a.x == b.x && a.y == b.y)
|
||||
break;
|
||||
e2 = err;
|
||||
if (e2 > -dx) {
|
||||
err -= dy;
|
||||
a.x += sx;
|
||||
}
|
||||
if (e2 < dy) {
|
||||
err += dx;
|
||||
a.y += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void vga_rect(struct rect r, uint8_t color)
|
||||
{
|
||||
vga_line(r.tl, (struct point){r.br.x, r.tl.y}, color);
|
||||
vga_line(r.tl, (struct point){r.tl.x, r.br.y}, color);
|
||||
vga_line(r.br, (struct point){r.br.x, r.tl.y}, color);
|
||||
vga_line(r.br, (struct point){r.tl.x, r.br.y}, color);
|
||||
}
|
||||
|
||||
static void vga_vsync()
|
||||
{
|
||||
asm volatile ("mov $0x03DA, %%dx\n"
|
||||
"current%=:"
|
||||
"in %%dx, %%al\n"
|
||||
"and $0x8, %%al\n"
|
||||
"jnz current%=\n"
|
||||
"restart%=:"
|
||||
"in %%dx, %%al\n"
|
||||
"and $0x8, %%al\n"
|
||||
"jz restart%=\n"
|
||||
: /* no outputs */
|
||||
: /* no inputs */
|
||||
: "al", "dx");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "int.h"
|
||||
#include "vga.h"
|
||||
|
||||
/* Letters are 7 pixels tall and 5 pixels wide. */
|
||||
|
||||
/* http://redd.it/2ba3g3 */
|
||||
static const char LINES[] = "BCDEIKOPQRSTUVYZ\\_`";
|
||||
static const char LETTERS[] =
|
||||
"DFJSJJJRJJRJJRGJIIIJGRJJJJJRSIIRIISSIIRIIIHIILJJHJJJSJJJGDDDD"
|
||||
"DGAAAAAJHJKMOMKJIIIIIISJQNJJJJJJPNLJJGJJJJJGRJJRIIIGJJJNGCRJJ"
|
||||
"RMKJGJIGAJGSDDDDDDJJJJJJGJJJJJFDJJJNQJJJJFDFJJJJFDDDDSABDEIS";
|
||||
|
||||
static int vga_font_line(int c, int row) {
|
||||
return c == ' ' ? 0 : LINES[LETTERS[(c - 'A') * 7 + row] - 'A'] - 'A';
|
||||
}
|
||||
|
||||
static void vga_print(struct point p, uint8_t color, const char *message)
|
||||
{
|
||||
for (int y = 0; y < 7; y++) {
|
||||
for (int x = 0; message[x]; x++) {
|
||||
int c = message[x];
|
||||
int line = vga_font_line(c, y);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if ((line >> i) & 0x01) {
|
||||
int dx = p.x + x * 6 + (4 - i);
|
||||
int dy = p.y + y;
|
||||
vga_pixel((struct point){dx, dy}, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||