Static lifter for Chip8 assembly into LLVM IR
Most definitely early-alpha. Doesn't support any kind of branching, yet.
All the fun IR manipulation is in lib/src/IREmitter.cpp.
draw_space_invader.ch8:
6200: SETI | V2 = 000
6300: SETI | V3 = 000
A20A: ISETI | I = 0x20a
D236: DRAW | draw(V2, V3, 0x6)
0000: //indicate end of program
BA7C: //sprite data
D6FE
54AA
The V2 and V3 registers are set to 0 to indicate the sprite should be drawn at (0,0).
Chip8 programs are traditionally mapped starting at 0x200, so 0x20A corresponds to the location of the sprite data. In this case the data is 6 bytes long.
examples/print_ir ../../roms/draw_space_invader.ch8
Listing:
6200: SETI | V2 = 0x00
6300: SETI | V3 = 0x00
A20A: ISETI | I = 0x20a
D236: DRAW | draw(V2, V3, 6)
; ModuleID = 'module'
source_filename = "module"
define void @f(i8* %MEM) local_unnamed_addr {
entry:
%"&MEM[I]" = getelementptr i8, i8* %MEM, i64 522
%0 = tail call i1 @draw(i8 0, i8 0, i8* %"&MEM[I]", i8 6)
ret void
}
declare i1 @draw(i8, i8, i8*, i8) local_unnamed_addr
The resulting LLVM IR is functionally identical to the following C++ program:
#include <cstdint>
#include <cstring>
extern "C" bool draw(std::uint8_t, std::uint8_t, std::uint8_t*, std::uint8_t);
void f(std::uint8_t* MEM) {
std::uint8_t x=0,y=0;
std::uint16_t I = 0x20A;
std::uint8_t* offset = &MEM[I];
draw(x, y, offset, 6);
}
which produces the following (cleaned-up) (-O3) LLVM IR:
define dso_local void @_Z1fPh(i8* %0) local_unnamed_addr #0 !dbg !185 {
%2 = getelementptr inbounds i8, i8* %0, i64 522, !dbg !197
%3 = tail call zeroext i1 @draw(i8 zeroext 0, i8 zeroext 0, i8* nonnull %2, i8 zeroext 6), !dbg !198
ret void, !dbg !199
}