mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-06-07 01:29:30 +00:00
4188 lines
218 KiB
Plaintext
4188 lines
218 KiB
Plaintext
|
namespace icd2 {
|
|||
|
namespace vram {
|
|||
|
let BUFFER_COUNT = 4;
|
|||
|
let BUFFER_BYTE_SIZE = 320;
|
|||
|
let BUFFER_INDEX_MASK = 3;
|
|||
|
|
|||
|
// The tile row is the current GB scanline divided by 8.
|
|||
|
// Every time the tile row increases, the buffer index is advanced.
|
|||
|
// The buffer index is between 0 .. 3 and will wrap around.
|
|||
|
// The GB screen is 144 pixels, or 18 rows tall.
|
|||
|
// During vblank, the PPU will be on tile row 18.
|
|||
|
// While the tile row will reset to 0 when starting a new frame, the buffer index to be written carries across frames.
|
|||
|
// Since 18 / 4 has a remaninder of 2, this means the buffer index can be either buffer 0 or buffer 2 at the start of a new frame.
|
|||
|
//
|
|||
|
// It should be possible to read the buffer for the first row tiles when the PPU position reaches tile row 1, meaning tile row 0 was just fully drawn?
|
|||
|
//
|
|||
|
// The safest position to read appears the index immediately before the write position, since it gives the most time read the row without being written over.
|
|||
|
// If the SNES side can keep up fast, maybe could do 2 rows behind and read both without re-checking PPU position, but seems potentially more risky. Hmm.
|
|||
|
const ppu_position @ 0x6000 : u8;
|
|||
|
let PPU_POSITION_TILE_ROW_MASK = 0xF8;
|
|||
|
let PPU_POSITION_TILE_ROW_SHIFT = 3;
|
|||
|
let PPU_POSITION_BUFFER_INDEX_MASK = 0x3;
|
|||
|
|
|||
|
// Selects which of the buffers 0 .. 3 to read from, and resets the position in the buffer.
|
|||
|
writeonly read_buffer_index @ 0x6001 : u8;
|
|||
|
// Port to read the next value from the VRAM. The GB screen is 160 pixels, or 20 columns wide.
|
|||
|
// The data port will have 2 bytes/row * 8 rows/tile * 20 tiles = 320 bytes of data for a given buffer.
|
|||
|
const read_data @ 0x7800 : u8;
|
|||
|
}
|
|||
|
|
|||
|
namespace command {
|
|||
|
const ready @ 0x6002 : u8;
|
|||
|
const data @ 0x7000 : [u8; 16];
|
|||
|
}
|
|||
|
|
|||
|
namespace joy {
|
|||
|
namespace bit {
|
|||
|
let RIGHT = 0;
|
|||
|
let LEFT = 1;
|
|||
|
let UP = 2;
|
|||
|
let DOWN = 3;
|
|||
|
let A = 4;
|
|||
|
let B = 5;
|
|||
|
let SELECT = 6;
|
|||
|
let START = 7;
|
|||
|
}
|
|||
|
|
|||
|
namespace mask {
|
|||
|
let RIGHT = 0x01;
|
|||
|
let LEFT = 0x02;
|
|||
|
let UP = 0x04;
|
|||
|
let DOWN = 0x08;
|
|||
|
let A = 0x10;
|
|||
|
let B = 0x20;
|
|||
|
let SELECT = 0x40;
|
|||
|
let START = 0x80;
|
|||
|
}
|
|||
|
|
|||
|
writeonly data1 @ 0x6004 : u8;
|
|||
|
writeonly data2 @ 0x6005 : u8;
|
|||
|
writeonly data3 @ 0x6006 : u8;
|
|||
|
writeonly data4 @ 0x6007 : u8;
|
|||
|
}
|
|||
|
|
|||
|
writeonly ctrl @ 0x6003 : u8;
|
|||
|
let CTRL_RESET = 0x80;
|
|||
|
let CTRL_JOY_MODE_4P = 0x20;
|
|||
|
let CTRL_JOY_MODE_2P = 0x10;
|
|||
|
let CTRL_JOY_MODE_1P = 0x00;
|
|||
|
let CTRL_SPEED_VERY_SLOW = 0x03;
|
|||
|
let CTRL_SPEED_SLOW = 0x02;
|
|||
|
let CTRL_SPEED_NORMAL = 0x01;
|
|||
|
let CTRL_SPEED_FAST = 0x00;
|
|||
|
|
|||
|
const version @ 0x600F : u8;
|
|||
|
}import os
|
|||
|
import os.path
|
|||
|
import PIL.Image
|
|||
|
|
|||
|
def write_chr(w, h, data, f):
|
|||
|
for y in range(0, h, 8):
|
|||
|
for x in range(0, w, 8):
|
|||
|
for j in range(8):
|
|||
|
# Write bit 0 of all columns in this row.
|
|||
|
c = 0
|
|||
|
for i in range(8):
|
|||
|
c = (c * 2) | (data[x + i, y + j] & 1)
|
|||
|
f.write(bytearray([c]))
|
|||
|
|
|||
|
# Write bit 1 of all columns in this row.
|
|||
|
c = 0
|
|||
|
for i in range(8):
|
|||
|
c = (c * 2) | ((data[x + i, y + j] >> 1) & 1)
|
|||
|
f.write(bytearray([c]))
|
|||
|
for j in range(8):
|
|||
|
# Write bit 2 of all columns in this row.
|
|||
|
c = 0
|
|||
|
for i in range(8):
|
|||
|
c = (c * 2) | ((data[x + i, y + j] >> 2) & 1)
|
|||
|
f.write(bytearray([c]))
|
|||
|
|
|||
|
# Write bit 3 of all columns in this row.
|
|||
|
c = 0
|
|||
|
for i in range(8):
|
|||
|
c = (c * 2) | ((data[x + i, y + j] >> 3) & 1)
|
|||
|
f.write(bytearray([c]))
|
|||
|
|
|||
|
if __name__ == '__main__':
|
|||
|
import sys
|
|||
|
|
|||
|
if len(sys.argv) > 1:
|
|||
|
WIDTH = 128
|
|||
|
HEIGHT = None
|
|||
|
for arg in range(1, len(sys.argv)):
|
|||
|
filename = sys.argv[arg]
|
|||
|
|
|||
|
if filename[0] == '-':
|
|||
|
if filename == '-oldfart':
|
|||
|
HEIGHT = 192
|
|||
|
elif filename == '-newfart':
|
|||
|
HEIGHT = None
|
|||
|
else:
|
|||
|
exit('Invalid argument `' + filename + '`.')
|
|||
|
continue
|
|||
|
|
|||
|
try:
|
|||
|
img = PIL.Image.open(filename)
|
|||
|
except IOError as e:
|
|||
|
if os.path.isdir(filename):
|
|||
|
exit(filename + ' is a directory.')
|
|||
|
if os.path.exists(filename):
|
|||
|
exit(filename + ' has an unsupported filetype, or you lack permission to open it.')
|
|||
|
else:
|
|||
|
exit('File ' + filename + ' does not exist!')
|
|||
|
|
|||
|
w, h = img.size
|
|||
|
if w != WIDTH or h % 8 != 0 or HEIGHT and h != HEIGHT:
|
|||
|
exit('Image ' + filename + ' is not ' + str(WIDTH) + 'x' + str(HEIGHT) + ' pixels in size.')
|
|||
|
if not img.palette:
|
|||
|
exit('Image ' + filename + ' has no palette.')
|
|||
|
data = img.load()
|
|||
|
|
|||
|
save_filename = os.path.splitext(filename)[0] + '.chr'
|
|||
|
try:
|
|||
|
f = open(save_filename, 'wb')
|
|||
|
except Exception as e:
|
|||
|
exit('Failure attempting to write ' + save_filename)
|
|||
|
|
|||
|
write_chr(w, h, data, f)
|
|||
|
f.close()
|
|||
|
print(' ' + filename + ' -> ' + save_filename)
|
|||
|
else:
|
|||
|
print('Usage: ' + sys.argv[0] + ' file [file...]')
|
|||
|
print('Converts files like foo.png into SNES-friendly formats like foo.chr')
|
|||
|
<EFBFBD>PNG
|
|||
|
|
|||
|
|