Compare commits
No commits in common. "e915a88cbf60ba0812ffa45df0c2e8889671c491" and "b032b50b6aaaab423164cedb6d07c6e45cfc2550" have entirely different histories.
e915a88cbf
...
b032b50b6a
14
Makefile
14
Makefile
|
|
@ -1,10 +1,12 @@
|
|||
.PHONY: all
|
||||
|
||||
all: bin/script-decode bin/sbutil bin/just-stats bin/extract-imgs
|
||||
all: bin/script-decode bin/sbutil bin/just-stats
|
||||
|
||||
bin/script-decode: script/*.go
|
||||
bin/sbutil: rom/*.go
|
||||
bin/just-stats: script/*.go
|
||||
|
||||
bin/%: cmd/%.go
|
||||
bin/script-decode: cmd/script-decode.go script/*.go
|
||||
go build -o $@ $<
|
||||
|
||||
bin/sbutil: cmd/sbutil.go rom/*.go
|
||||
go build -o $@ $<
|
||||
|
||||
bin/just-stats: cmd/just-stats.go script/*.go
|
||||
go build -o $@ $<
|
||||
|
|
|
|||
|
|
@ -1,575 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"image"
|
||||
"image/draw"
|
||||
"image/color"
|
||||
"image/png"
|
||||
//"strings"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/alexflint/go-arg"
|
||||
|
||||
nesimg "github.com/zorchenhimer/go-retroimg"
|
||||
//"github.com/zorchenhimer/go-retroimg/palette"
|
||||
)
|
||||
|
||||
type Arguments struct {
|
||||
Nametable string `arg:"--nt,required"`
|
||||
Chr string `arg:"--chr,required"`
|
||||
Output string `arg:"--output,required"`
|
||||
IsSprite bool `arg:"--sprites"`
|
||||
SpriteSheet bool `arg:"--sprite-sheet"`
|
||||
NoBg bool `arg:"--no-bg"` // don't draw hot-pink background
|
||||
}
|
||||
|
||||
var (
|
||||
tileMissing *nesimg.Tile
|
||||
)
|
||||
|
||||
func main() {
|
||||
args := &Arguments{}
|
||||
arg.MustParse(args)
|
||||
|
||||
var err error
|
||||
tileMissing, err = nesimg.NewTileFromPlanes([][]byte{
|
||||
{0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33},
|
||||
{0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F} })
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = run(args); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run(args *Arguments) error {
|
||||
fmt.Println("--")
|
||||
fmt.Println("Nametable:", args.Nametable)
|
||||
fmt.Println("CHR:", args.Chr)
|
||||
fmt.Println("Output:", args.Output)
|
||||
fmt.Println("IsSprite:", args.IsSprite)
|
||||
fmt.Println("SpriteSheet:", args.SpriteSheet)
|
||||
fmt.Println("--")
|
||||
|
||||
chrFile, err := os.Open(args.Chr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer chrFile.Close()
|
||||
|
||||
raw := nesimg.NewRawChr(chrFile)
|
||||
tiles, err := raw.ReadAllTiles(nesimg.BD_2bpp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//fmt.Printf("first tile: %#v\n", tiles[0])
|
||||
|
||||
//tileUniform_00 := image.NewUniformPaletted(nesimg.DefaultPal_2bpp, 0)
|
||||
//tileUniform_F0 := image.NewUniformPaletted(nesimg.DefaultPal_2bpp, 1)
|
||||
//tileUniform_0F := image.NewUniformPaletted(nesimg.DefaultPal_2bpp, 2)
|
||||
//tileUniform_FF := image.NewUniformPaletted(nesimg.DefaultPal_2bpp, 3)
|
||||
|
||||
tileUniform_00, err := nesimg.NewTileFromPlanes([][]byte{
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tileUniform_F0, err := nesimg.NewTileFromPlanes([][]byte{
|
||||
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tileUniform_0F, err := nesimg.NewTileFromPlanes([][]byte{
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tileUniform_FF, err := nesimg.NewTileFromPlanes([][]byte{
|
||||
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tiles = append([]*nesimg.Tile{
|
||||
tileUniform_00,
|
||||
tileUniform_F0,
|
||||
tileUniform_0F,
|
||||
tileUniform_FF,
|
||||
}, tiles...)
|
||||
|
||||
ntData, err := os.ReadFile(args.Nametable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
layers, err := ReadData(ntData, tiles, args.IsSprite)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ReadData() error: %w", err)
|
||||
}
|
||||
|
||||
uni := image.NewUniform(color.RGBA{0xFF, 0x00, 0xFF, 0xFF})
|
||||
var screen *image.RGBA
|
||||
if args.SpriteSheet {
|
||||
maxWidth := 0
|
||||
maxHeight := 0
|
||||
for _, l := range layers {
|
||||
maxWidth += l.Bounds().Dx()
|
||||
if l.Bounds().Dy() > maxHeight {
|
||||
maxHeight = l.Bounds().Dy()
|
||||
}
|
||||
}
|
||||
screen = image.NewRGBA(image.Rect(0, 0, maxWidth, maxHeight))
|
||||
if !args.NoBg {
|
||||
draw.Draw(screen, screen.Bounds(), uni, image.Pt(0, 0), draw.Over)
|
||||
}
|
||||
|
||||
loc := image.Pt(0, 0)
|
||||
for _, l := range layers {
|
||||
draw.Draw(screen, l.Bounds().Add(loc), l, image.Pt(0, 0), draw.Over)
|
||||
loc = loc.Add(image.Pt(l.Bounds().Dx(), 0))
|
||||
}
|
||||
|
||||
} else {
|
||||
screen = image.NewRGBA(image.Rect(0, 0, 32*8, 30*8))
|
||||
if !args.NoBg {
|
||||
draw.Draw(screen, screen.Bounds(), uni, image.Pt(0, 0), draw.Over)
|
||||
}
|
||||
|
||||
for _, l := range layers {
|
||||
if args.IsSprite {
|
||||
draw.Draw(screen, l.Bounds().Add(l.Location), l, image.Pt(0, 0), draw.Over)
|
||||
} else {
|
||||
draw.Draw(screen, screen.Bounds(), l, image.Pt(0, 0), draw.Over)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output, err := os.Create(args.Output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer output.Close()
|
||||
|
||||
err = png.Encode(output, screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//img := image.NewRGBA(image.Rect(0, 0, 32*8, 30*8))
|
||||
return nil
|
||||
}
|
||||
|
||||
//type UniformPaletted struct {
|
||||
// Palette color.Palette
|
||||
// Index int
|
||||
//}
|
||||
//
|
||||
//func NewUniformPaletted(palette color.Palette, idx int) *UniformPaletted {
|
||||
// if idx >= len(palette) {
|
||||
// panic("Index out of palette range")
|
||||
// }
|
||||
//
|
||||
// return &UniformPaletted{
|
||||
// Palette: palette,
|
||||
// Index: idx,
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (u *UniformPaletted) At(x, y int) color.Color {
|
||||
// if u.Index >= len(u.Palette) {
|
||||
// panic("Index out of palette range")
|
||||
// }
|
||||
//
|
||||
// return u.Palette[u.Index]
|
||||
//}
|
||||
//
|
||||
//func (u *UniformPaletted) ColorModel() color.Model {
|
||||
// return u.Palette
|
||||
//}
|
||||
//
|
||||
//func (u *UniformPaletted) Bounds() image.Rectangle {
|
||||
// // Copied from image.Uniform.Bounds()
|
||||
// return image.Rectangle{image.Point{-1e9, -1e9}, image.Point{1e9, 1e9}}
|
||||
//}
|
||||
|
||||
func BytesToInt(raw []byte) int {
|
||||
if len(raw) > 2 {
|
||||
panic("only 8 and 16 bit numbers for now")
|
||||
}
|
||||
|
||||
if len(raw) == 1 {
|
||||
return int(raw[0])
|
||||
}
|
||||
|
||||
return int(raw[1])<<8 | int(raw[0])
|
||||
}
|
||||
|
||||
type DataHeader struct {
|
||||
PaletteOffset uint16
|
||||
ArgB uint16
|
||||
ImageCount uint8
|
||||
}
|
||||
|
||||
func (h DataHeader) String() string {
|
||||
return fmt.Sprintf("{DataHeader PaletteOffset:$%04X ArgB:$%04X ImageCount:%d}",
|
||||
h.PaletteOffset,
|
||||
h.ArgB,
|
||||
h.ImageCount,
|
||||
)
|
||||
}
|
||||
|
||||
type ImageHeader struct {
|
||||
Width uint8
|
||||
Height uint8
|
||||
AttrLength uint16
|
||||
|
||||
// in pixels
|
||||
X uint8
|
||||
Y uint8
|
||||
}
|
||||
|
||||
func (h ImageHeader) String() string {
|
||||
return fmt.Sprintf("{ImageHeader Width:%d[%02X] Height:%d[%02X] AttrLength:$%04X XCoord:%d[%02X] YCoord:%d[%02X]}",
|
||||
h.Width, h.Width,
|
||||
h.Height, h.Height,
|
||||
h.AttrLength,
|
||||
h.X, h.X,
|
||||
h.Y, h.Y,
|
||||
)
|
||||
}
|
||||
|
||||
func ReadData(raw []byte, tiles []*nesimg.Tile, isSprites bool) ([]*Layer, error) {
|
||||
//raw, err := io.ReadAll(r)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
|
||||
if isSprites {
|
||||
tiles = tiles[4:]
|
||||
}
|
||||
|
||||
dataHeader := &DataHeader{}
|
||||
_, err := binary.Decode(raw, binary.LittleEndian, dataHeader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dataHeader.PaletteOffset += 4
|
||||
|
||||
fmt.Println(dataHeader)
|
||||
|
||||
imgHeaders := []*ImageHeader{}
|
||||
for i := 0; i < int(dataHeader.ImageCount); i++ {
|
||||
head := &ImageHeader{}
|
||||
_, err = binary.Decode(raw[4+(i*6)+1:], binary.LittleEndian, head)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println(head)
|
||||
imgHeaders = append(imgHeaders, head)
|
||||
}
|
||||
|
||||
palettes := []color.Palette{}
|
||||
colorIds := [][]string{}
|
||||
for i := 0; i < 4; i++ {
|
||||
p := color.Palette{}
|
||||
idlist := []string{}
|
||||
for j := 0; j < 4; j++ {
|
||||
v := raw[int(dataHeader.PaletteOffset)+(i*4)+j]
|
||||
if v == 0x3D {
|
||||
v = 0x0F
|
||||
}
|
||||
c, ok := NesColors[v]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Color value $%02X invalid", v)
|
||||
}
|
||||
p = append(p, c)
|
||||
idlist = append(idlist, fmt.Sprintf("%02X", v))
|
||||
}
|
||||
palettes = append(palettes, p)
|
||||
colorIds = append(colorIds, idlist)
|
||||
}
|
||||
|
||||
fmt.Println("Palettes:")
|
||||
for i := 0; i < len(palettes); i++ {
|
||||
fmt.Printf("%s: %v\n", colorIds[i], palettes[i])
|
||||
}
|
||||
|
||||
nesTileSize := image.Rect(0, 0, 8, 8)
|
||||
|
||||
offset := 5 + len(imgHeaders) * 6
|
||||
layers := []*Layer{}
|
||||
for _, head := range imgHeaders {
|
||||
rect := image.Rect(0, 0, 32, 30)
|
||||
if isSprites {
|
||||
rect = image.Rect(0, 0, int(head.Width), int(head.Height))
|
||||
}
|
||||
|
||||
l := NewLayer(
|
||||
rect,
|
||||
nesTileSize,
|
||||
palettes,
|
||||
)
|
||||
l.Transparency = isSprites
|
||||
l.Location = image.Pt(int(head.X), int(head.Y))
|
||||
l.IsSprite = isSprites
|
||||
//tileIds := []int{}
|
||||
|
||||
if isSprites {
|
||||
for idx, b := range raw[offset:offset+(int(head.Width)*int(head.Height))] {
|
||||
id := int(uint(b)) // is this required to ignore negatives?
|
||||
if id < len(tiles) {
|
||||
l.Tiles[idx] = tiles[id]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for idx, b := range raw[offset:offset+(int(head.Width)*int(head.Height))] {
|
||||
row := idx / int(head.Width)
|
||||
col := idx % int(head.Width)
|
||||
arrIdx := ((row+int(head.Y/8)) * 32) + (col+int(head.X/8))
|
||||
//fmt.Printf("%d ", arrIdx)
|
||||
id := int(uint(b)) // is this required to ignore negatives?
|
||||
if id < len(tiles) {
|
||||
l.Tiles[arrIdx] = tiles[id]
|
||||
//tileIds = append(tileIds, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//for idx, t := range tileIds {
|
||||
// fmt.Printf("[%d] %02X\n", idx, t)
|
||||
//}
|
||||
|
||||
offset += int(head.Width)*int(head.Height)
|
||||
|
||||
if isSprites {
|
||||
l.Attributes = raw[offset:offset+int(head.AttrLength)]
|
||||
} else {
|
||||
// FIXME: This will break on background chunks that aren't a full screen.
|
||||
// Need to verify the anchor point in the firmware for this case (for
|
||||
// when the tile anchor point isn't aligned to an attribute byte).
|
||||
// Partial screen attributes will also be a different size than 8*8.
|
||||
l.SetAttributes(raw[offset:offset+int(head.AttrLength)])
|
||||
}
|
||||
offset += int(head.AttrLength)
|
||||
|
||||
layers = append(layers, l)
|
||||
}
|
||||
|
||||
return layers, nil
|
||||
}
|
||||
|
||||
type Layer struct {
|
||||
Tiles []*nesimg.Tile
|
||||
Attributes []byte
|
||||
Palettes []color.Palette
|
||||
TileSize image.Rectangle
|
||||
|
||||
Location image.Point
|
||||
|
||||
Width int
|
||||
Height int
|
||||
Transparency bool // true for sprites
|
||||
IsSprite bool
|
||||
|
||||
Solid bool
|
||||
}
|
||||
|
||||
func NewLayer(layerSize image.Rectangle, tileSize image.Rectangle, palettes []color.Palette) *Layer {
|
||||
return &Layer{
|
||||
Tiles: make([]*nesimg.Tile, layerSize.Dx()*layerSize.Dy()),
|
||||
Attributes: make([]byte, layerSize.Dx()*layerSize.Dy()),
|
||||
TileSize: tileSize,
|
||||
Location: image.Pt(0, 0),
|
||||
Width: layerSize.Dx(),
|
||||
Height: layerSize.Dy(),
|
||||
Palettes: palettes,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Layer) At(x, y int) color.Color {
|
||||
if l.Solid {
|
||||
return color.RGBA{0x00, 0x00, 0x00, 0xFF}
|
||||
}
|
||||
|
||||
width, height := l.TileSize.Dx(), l.TileSize.Dy()
|
||||
|
||||
row := y / height
|
||||
col := x / width
|
||||
tx := x % width
|
||||
ty := y % height
|
||||
|
||||
tileIdx := (row*l.Width)+col
|
||||
|
||||
if x == 89 && y == 101 {
|
||||
fmt.Printf("row:%d col:%d tx:%d ty:%d tileIdx:%d width:%d height:%d l.Width:%d l.Height:%d\n",
|
||||
row, col, tx, ty, tileIdx, width, height, l.Width, l.Height,
|
||||
)
|
||||
}
|
||||
|
||||
if l.Tiles[tileIdx] == nil {
|
||||
return color.RGBA{0x00, 0x00, 0x00, 0x00}
|
||||
//return color.RGBA{0xFF, 0x00, 0xFF, 0xFF}
|
||||
}
|
||||
|
||||
colorIdx := l.Tiles[tileIdx].ColorIndexAt(tx, ty)
|
||||
if l.Transparency && colorIdx == 0 {
|
||||
return color.RGBA{0x00, 0x00, 0x00, 0x00}
|
||||
}
|
||||
|
||||
palIdx := l.Attributes[tileIdx]
|
||||
return l.Palettes[palIdx][colorIdx]
|
||||
}
|
||||
|
||||
func (l *Layer) Bounds() image.Rectangle {
|
||||
return image.Rect(0, 0, l.TileSize.Max.X*l.Width, l.TileSize.Max.Y*l.Height)
|
||||
}
|
||||
|
||||
func (l *Layer) ColorModel() color.Model {
|
||||
return color.RGBAModel
|
||||
}
|
||||
|
||||
func (sc *Layer) SetAttributes(data []byte) error {
|
||||
if len(data) != 64 {
|
||||
return fmt.Errorf("Attribute data must be 64 bytes")
|
||||
}
|
||||
|
||||
sc.Attributes = make([]byte, 32*30)
|
||||
for row := 0; row < 8; row++ {
|
||||
for col := 0; col < 8; col++ {
|
||||
src := row*8+col
|
||||
start := (row*32)*4 + (col*4)
|
||||
|
||||
raw := data[src]
|
||||
|
||||
br := (raw >> 6) & 0x03
|
||||
bl := (raw >> 4) & 0x03
|
||||
tr := (raw >> 2) & 0x03
|
||||
tl := raw & 0x03
|
||||
|
||||
//if row == 0 && col == 0 {
|
||||
// fmt.Printf("br:%02X bl:%02X tr:%02X tl:%02X\n", br, bl, tr, tl)
|
||||
//}
|
||||
|
||||
sc.Attributes[start+0+(32*0)] = tl
|
||||
sc.Attributes[start+1+(32*0)] = tl
|
||||
sc.Attributes[start+0+(32*1)] = tl
|
||||
sc.Attributes[start+1+(32*1)] = tl
|
||||
|
||||
sc.Attributes[start+2+(32*0)] = tr
|
||||
sc.Attributes[start+3+(32*0)] = tr
|
||||
sc.Attributes[start+2+(32*1)] = tr
|
||||
sc.Attributes[start+3+(32*1)] = tr
|
||||
|
||||
if row < 7 {
|
||||
sc.Attributes[start+0+(32*2)] = bl
|
||||
sc.Attributes[start+1+(32*2)] = bl
|
||||
sc.Attributes[start+0+(32*3)] = bl
|
||||
sc.Attributes[start+1+(32*3)] = bl
|
||||
|
||||
sc.Attributes[start+2+(32*2)] = br
|
||||
sc.Attributes[start+3+(32*2)] = br
|
||||
sc.Attributes[start+2+(32*3)] = br
|
||||
sc.Attributes[start+3+(32*3)] = br
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var NesColors map[byte]color.Color = map[byte]color.Color{
|
||||
0x00: color.RGBA{0x66, 0x66, 0x66, 0xFF},
|
||||
0x10: color.RGBA{0xAD, 0xAD, 0xAD, 0xFF},
|
||||
0x20: color.RGBA{0xFF, 0xFF, 0xEF, 0xFF},
|
||||
0x30: color.RGBA{0xFF, 0xFF, 0xEF, 0xFF},
|
||||
|
||||
0x01: color.RGBA{0x00, 0x2A, 0x88, 0xFF},
|
||||
0x11: color.RGBA{0x15, 0x5F, 0xD9, 0xFF},
|
||||
0x21: color.RGBA{0x64, 0xB0, 0xFF, 0xFF},
|
||||
0x31: color.RGBA{0xC0, 0xDF, 0xFF, 0xFF},
|
||||
|
||||
0x02: color.RGBA{0x14, 0x12, 0xA7, 0xFF},
|
||||
0x12: color.RGBA{0x42, 0x40, 0xFF, 0xFF},
|
||||
0x22: color.RGBA{0x92, 0x90, 0xFF, 0xFF},
|
||||
0x32: color.RGBA{0xD3, 0xD2, 0xFF, 0xFF},
|
||||
|
||||
0x03: color.RGBA{0x3B, 0x00, 0xA4, 0xFF},
|
||||
0x13: color.RGBA{0x75, 0x27, 0xFE, 0xFF},
|
||||
0x23: color.RGBA{0xC6, 0x76, 0xFF, 0xFF},
|
||||
0x33: color.RGBA{0xE8, 0xC8, 0xFF, 0xFF},
|
||||
|
||||
0x04: color.RGBA{0x5C, 0x00, 0x7E, 0xFF},
|
||||
0x14: color.RGBA{0xA0, 0x1A, 0xCC, 0xFF},
|
||||
0x24: color.RGBA{0xF3, 0x6A, 0xFF, 0xFF},
|
||||
0x34: color.RGBA{0xFB, 0xC2, 0xFF, 0xFF},
|
||||
|
||||
0x05: color.RGBA{0x6E, 0x00, 0x40, 0xFF},
|
||||
0x15: color.RGBA{0xB7, 0x1E, 0x7B, 0xFF},
|
||||
0x25: color.RGBA{0xFE, 0x6E, 0xCC, 0xFF},
|
||||
0x35: color.RGBA{0xFE, 0xC4, 0xEA, 0xFF},
|
||||
|
||||
0x06: color.RGBA{0x6C, 0x06, 0x00, 0xFF},
|
||||
0x16: color.RGBA{0xB5, 0x31, 0x20, 0xFF},
|
||||
0x26: color.RGBA{0xFE, 0x81, 0x70, 0xFF},
|
||||
0x36: color.RGBA{0xFE, 0xCC, 0xC5, 0xFF},
|
||||
|
||||
0x07: color.RGBA{0x56, 0x1D, 0x00, 0xFF},
|
||||
0x17: color.RGBA{0x99, 0x4E, 0x00, 0xFF},
|
||||
0x27: color.RGBA{0xEA, 0x9E, 0x22, 0xFF},
|
||||
0x37: color.RGBA{0xF7, 0xD8, 0xA5, 0xFF},
|
||||
|
||||
0x08: color.RGBA{0x33, 0x35, 0x00, 0xFF},
|
||||
0x18: color.RGBA{0x6B, 0x6D, 0x00, 0xFF},
|
||||
0x28: color.RGBA{0xBC, 0xBE, 0x00, 0xFF},
|
||||
0x38: color.RGBA{0xE4, 0xE5, 0x94, 0xFF},
|
||||
|
||||
0x09: color.RGBA{0x0B, 0x48, 0x00, 0xFF},
|
||||
0x19: color.RGBA{0x38, 0x87, 0x00, 0xFF},
|
||||
0x29: color.RGBA{0x88, 0xD8, 0x00, 0xFF},
|
||||
0x39: color.RGBA{0xCF, 0xEF, 0x96, 0xFF},
|
||||
|
||||
0x0A: color.RGBA{0x00, 0x52, 0x00, 0xFF},
|
||||
0x1A: color.RGBA{0x0C, 0x93, 0x00, 0xFF},
|
||||
0x2A: color.RGBA{0x5C, 0xE4, 0x30, 0xFF},
|
||||
0x3A: color.RGBA{0xBD, 0xF4, 0xAB, 0xFF},
|
||||
|
||||
0x0B: color.RGBA{0x00, 0x4F, 0x08, 0xFF},
|
||||
0x1B: color.RGBA{0x00, 0x8F, 0x32, 0xFF},
|
||||
0x2B: color.RGBA{0x45, 0xE0, 0x82, 0xFF},
|
||||
0x3B: color.RGBA{0xB3, 0xF3, 0xCC, 0xFF},
|
||||
|
||||
0x0C: color.RGBA{0x00, 0x40, 0x4D, 0xFF},
|
||||
0x1C: color.RGBA{0x00, 0x7C, 0x8D, 0xFF},
|
||||
0x2C: color.RGBA{0x48, 0xCD, 0xDE, 0xFF},
|
||||
0x3C: color.RGBA{0xB5, 0xEB, 0xF2, 0xFF},
|
||||
|
||||
0x0D: color.RGBA{0x00, 0x00, 0x00, 0xFF},
|
||||
0x1D: color.RGBA{0x00, 0x7C, 0x8D, 0xFF},
|
||||
0x2D: color.RGBA{0x4F, 0x4F, 0x4F, 0xFF},
|
||||
0x3D: color.RGBA{0xB8, 0xB8, 0xB8, 0xFF},
|
||||
|
||||
0x0E: color.RGBA{0x00, 0x00, 0x00, 0xFF},
|
||||
0x1E: color.RGBA{0x00, 0x00, 0x00, 0xFF},
|
||||
0x2E: color.RGBA{0x00, 0x00, 0x00, 0xFF},
|
||||
0x3E: color.RGBA{0x00, 0x00, 0x00, 0xFF},
|
||||
|
||||
0x0F: color.RGBA{0x00, 0x00, 0x00, 0xFF},
|
||||
0x1F: color.RGBA{0x00, 0x00, 0x00, 0xFF},
|
||||
0x2F: color.RGBA{0x00, 0x00, 0x00, 0xFF},
|
||||
0x3F: color.RGBA{0x00, 0x00, 0x00, 0xFF},
|
||||
}
|
||||
|
||||
9
go.mod
9
go.mod
|
|
@ -1,10 +1,7 @@
|
|||
module git.zorchenhimer.com/Zorchenhimer/go-studybox
|
||||
|
||||
go 1.24.1
|
||||
go 1.22.2
|
||||
|
||||
require (
|
||||
github.com/alexflint/go-arg v1.5.1
|
||||
github.com/zorchenhimer/go-retroimg v0.0.0-20251108020316-705ea2ebacd6
|
||||
)
|
||||
require github.com/alexflint/go-arg v1.4.3
|
||||
|
||||
require github.com/alexflint/go-scalar v1.2.0 // indirect
|
||||
require github.com/alexflint/go-scalar v1.1.0 // indirect
|
||||
|
|
|
|||
17
go.sum
17
go.sum
|
|
@ -1,15 +1,16 @@
|
|||
github.com/alexflint/go-arg v1.5.1 h1:nBuWUCpuRy0snAG+uIJ6N0UvYxpxA0/ghA/AaHxlT8Y=
|
||||
github.com/alexflint/go-arg v1.5.1/go.mod h1:A7vTJzvjoaSTypg4biM5uYNTkJ27SkNTArtYXnlqVO8=
|
||||
github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
|
||||
github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
|
||||
github.com/alexflint/go-arg v1.4.3 h1:9rwwEBpMXfKQKceuZfYcwuc/7YY7tWJbFsgG5cAU/uo=
|
||||
github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA=
|
||||
github.com/alexflint/go-scalar v1.1.0 h1:aaAouLLzI9TChcPXotr6gUhq+Scr8rl0P9P4PnltbhM=
|
||||
github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/zorchenhimer/go-retroimg v0.0.0-20251108020316-705ea2ebacd6 h1:94b43etKen/R0Ga+lxgfSMlYQicwsMvlFBizMOQ3loc=
|
||||
github.com/zorchenhimer/go-retroimg v0.0.0-20251108020316-705ea2ebacd6/go.mod h1:iQUJQkvvbgycl7TS2OWdSC0+kHYypOASX129xmnv+SE=
|
||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ func decodeDelay(page *Page, data []byte, idx int) (Packet, int, error) {
|
|||
count++
|
||||
}
|
||||
if count%2 != 0 {
|
||||
fmt.Printf("0xAA delay packet at offset %08X has odd number of 0xAA's\n", idx+page.FileOffset)
|
||||
fmt.Printf("0xAA delay packet at offset %08X has odd number of 0xAA's", idx+page.FileOffset)
|
||||
}
|
||||
pd := &packetDelay{
|
||||
Length: count,
|
||||
|
|
@ -123,10 +123,8 @@ func decodeDelay(page *Page, data []byte, idx int) (Packet, int, error) {
|
|||
|
||||
checksum := calcChecksum(data[idx : idx+count+3])
|
||||
if checksum != 0xC5 {
|
||||
// return nil, 0, fmt.Errorf("Invalid checksum for delay packet starting at offset %08X. Got %02X, expected %02X",
|
||||
// pd.address, checksum, 0xC5)
|
||||
fmt.Printf("WARN: Invalid checksum for delay packet starting at offset %08X. Got %02X, expected %02X\n",
|
||||
pd.address, checksum, 0xC5)
|
||||
return nil, 0, fmt.Errorf("Invalid checksum for delay packet starting at offset %08X. Got %02X, expected %02X",
|
||||
pd.address, checksum, 0xC5)
|
||||
}
|
||||
|
||||
idx += count + 3
|
||||
|
|
|
|||
|
|
@ -1,502 +0,0 @@
|
|||
## 0x80 Play Beep
|
||||
|
||||
Stack Arguments: 0
|
||||
Inline Arguments: 0
|
||||
|
||||
Vars used:
|
||||
|
||||
Byte_0493
|
||||
|
||||
Play's an audible beep on the Square 1 channel.
|
||||
|
||||
## 0x81 Halt
|
||||
|
||||
Stack Arguments: 0
|
||||
Inline Arguments: 0
|
||||
|
||||
Vars used:
|
||||
N/A
|
||||
|
||||
Infinite loop that does not return.
|
||||
|
||||
## 0x82 Tape NMI Shenanigans
|
||||
|
||||
Stack Arguments: 0
|
||||
Inline Arguments: 0
|
||||
|
||||
Vars:
|
||||
|
||||
Byte_E0_TapeCtrl_Cache
|
||||
Byte_EE
|
||||
Byte_F2
|
||||
|
||||
Byte_0740
|
||||
Byte_07EF
|
||||
Byte_07F3
|
||||
|
||||
JSRs:
|
||||
|
||||
L2706_SetupNMI_ED00_LongJump
|
||||
L2721_TurnOnNMI_LongJump
|
||||
L2724_TurnOffNMI_LongJump
|
||||
L2742
|
||||
|
||||
|
||||
## 0x83 Tape Wait
|
||||
|
||||
Stack Arguments: 0
|
||||
Inline Arguments: 0
|
||||
|
||||
Vars:
|
||||
|
||||
Byte_0740
|
||||
|
||||
JMPs to `L1329_WaitOn_EE`
|
||||
|
||||
## 0x84 Jump
|
||||
|
||||
Stack Arguments: 0
|
||||
Inline Arguments: 1 Word
|
||||
|
||||
Vars:
|
||||
|
||||
Code_Pointer
|
||||
Argument_A
|
||||
|
||||
Updates the script pointer to the inline address and continues script execution
|
||||
from the new address.
|
||||
|
||||
## 0x85 Call
|
||||
|
||||
Stack Arguments: 0
|
||||
Inline Arguments: 1 Word
|
||||
|
||||
Vars:
|
||||
|
||||
Code_Pointer
|
||||
Argument_A
|
||||
Stack_Pointer
|
||||
|
||||
Pushes return address to the stack and performs a Jump to the inline script
|
||||
address.
|
||||
|
||||
## 0x86 Return
|
||||
|
||||
Stack Arguments: 0 (1 Word, implied)
|
||||
Inline Arguments: 0
|
||||
|
||||
Vars:
|
||||
|
||||
Stack_Pointer
|
||||
Code_Pointer
|
||||
|
||||
Remove a script address from the stack and update the `Code_Pointer` to it
|
||||
before continuing execution.
|
||||
|
||||
## 0x87 Loop
|
||||
|
||||
Stack Arguments: 0 (4 implied)
|
||||
Inline Arguments: 0
|
||||
|
||||
Args manually pulled from stack:
|
||||
|
||||
- Limit
|
||||
- Increment
|
||||
- LoopVar
|
||||
- LoopEntry
|
||||
|
||||
ArgA `Stack_Pointer-6`
|
||||
ArgB `(ArgD)`
|
||||
ArgC `Stack_Pointer-2`
|
||||
ArgD `Stack_Pointer-4`
|
||||
ArgE `ArgA+1`
|
||||
|
||||
Vars:
|
||||
|
||||
Argument_A
|
||||
Argument_B
|
||||
Argument_C
|
||||
Argument_D
|
||||
Argument_E
|
||||
Stack_Pointer
|
||||
|
||||
JSRs:
|
||||
|
||||
Handler_CB_Sum
|
||||
Handler_C7_LessThan
|
||||
|
||||
JMPs to `L49CD_LessThan`
|
||||
|
||||
## 0x88 Play Sound
|
||||
|
||||
Stack Arguments: 32 bytes (string copied to `$0700`)
|
||||
Inline Arguments: 0
|
||||
|
||||
Plays a short SFX defined by a string.
|
||||
|
||||
Vars:
|
||||
|
||||
Pointer_A0
|
||||
Pointer_A2
|
||||
|
||||
Byte_0494
|
||||
Byte_0495
|
||||
Byte_0496
|
||||
Byte_0497
|
||||
Byte_0498
|
||||
Byte_0499
|
||||
Byte_04AC_AudioState
|
||||
Byte_04AD
|
||||
|
||||
Vars inside `L5C18_CopyPtrA0PtrA2`:
|
||||
|
||||
Byte_0490_PtrA0Len
|
||||
|
||||
Vars inside `L5A6F_DecodeAudioString_EntryPoint`:
|
||||
|
||||
Pointer_A2
|
||||
Byte_04AC_AudioState
|
||||
|
||||
Word_049A+0 (current channel ID)
|
||||
Word_049A+1 (current channel mask)
|
||||
|
||||
Byte_0497 Byte_0498 Byte_0499
|
||||
enable byte for each channel. RTS if != 1 on entry
|
||||
written to with value of Byte_04B3
|
||||
|
||||
Byte_0494 Byte_0495 Byte_0496
|
||||
Pointer_A2 low for channel. looks like it's updated after reading a
|
||||
string.
|
||||
|
||||
Byte_049F Added to note lookup index; used with O stuff?
|
||||
Byte_04B1 octave? value from Table_04A6
|
||||
Byte_04B2 T value
|
||||
Byte_04B3 = Byte_04B1 * Byte_04B2
|
||||
|
||||
Table_049C Y#
|
||||
Table_04A0 V##
|
||||
Table_04A3 M#
|
||||
Table_04A6 ?? note related. octave? value after letter
|
||||
Table_04A9 O#
|
||||
Table_04AE T#
|
||||
|
||||
Table_B391 G??
|
||||
|
||||
JSRs:
|
||||
|
||||
L5C08_DataLen_A0
|
||||
L5C18_CopyPtrA0PtrA2
|
||||
L5CC5_LongDelay
|
||||
L5A6F_DecodeAudioString_EntryPoint
|
||||
L5A8F_DecodeAudioString (if audio is turned on)
|
||||
L5C42 (for weird chars?)
|
||||
|
||||
Hard-coded addresses:
|
||||
|
||||
$0700 (Pointer_A0)
|
||||
$0420 (Pointer_A2)
|
||||
$0441 (Pointer_A2)
|
||||
$0462 (Pointer_A2)
|
||||
|
||||
Copies data currently at `$0700` to three locations (`$0420`, `$0441`, `$0462`)
|
||||
|
||||
### String format
|
||||
|
||||
Three channels are encoded in this string, separated by a colon (`:`). The
|
||||
string consists of letter and number pairs. The order of the individual pairs
|
||||
in the overal string don't seem to matter.
|
||||
|
||||
M# Loop & Constant volume
|
||||
number is 0 or 1.
|
||||
|
||||
V## Volume
|
||||
number between 0 and 15, inclusive
|
||||
|
||||
Y# Duty
|
||||
number between 0 and 3, inclusive
|
||||
|
||||
T# ?? $4001/$4006?
|
||||
number between 1 and 9, inclusive (verify this)
|
||||
|
||||
O# Note related (timer low/high stuff)
|
||||
number between 1 and 6, inclusive
|
||||
|
||||
A#-G# Notes and octaves?
|
||||
number between 0 and 10, inclusive
|
||||
|
||||
## 0x89
|
||||
|
||||
Stack Arguments: 3
|
||||
Inline Arguments: 0
|
||||
|
||||
## 0x8A Pop String to Address
|
||||
|
||||
Stack Arguments: 0
|
||||
Inline Arguments: 1 Word
|
||||
|
||||
Removes 32 bytes from the stack and writes them starting to the inline address.
|
||||
|
||||
## 0x8B
|
||||
|
||||
## 0x97
|
||||
|
||||
Arguments: 2
|
||||
|
||||
ArgA
|
||||
ArgB
|
||||
|
||||
Vars:
|
||||
|
||||
Byte_0740
|
||||
Byte_44FE
|
||||
Byte_4598 = Argument_B+0
|
||||
Byte_44FD
|
||||
|
||||
Argument_A
|
||||
Array_44FB+2 ($44FD)
|
||||
|
||||
JSRs:
|
||||
|
||||
L5592_CheckForZero
|
||||
|
||||
Conditionally sets up NMI stuff depending on byte $0740
|
||||
If ArgA >= 3, setup some more stuff
|
||||
|
||||
## 0xA9
|
||||
|
||||
Arguments: 1
|
||||
|
||||
ArgA
|
||||
|
||||
Some sort of nametable restore operation?
|
||||
|
||||
Writes smaller updates to the nametable for animation purposes.
|
||||
|
||||
## 0x9D Something Tape (draw screen?)
|
||||
|
||||
Arguments: 2
|
||||
|
||||
ArgA -> X
|
||||
AgrB
|
||||
|
||||
Vars:
|
||||
|
||||
Byte_44FE
|
||||
Array_44F8, X
|
||||
Array_44CF, X
|
||||
Array_457A, X
|
||||
|
||||
If ArgB != 0
|
||||
|
||||
ldx ArgA
|
||||
|
||||
lda #1
|
||||
sta Array_44F8, X
|
||||
sta Array_44CF, X
|
||||
|
||||
lda #0
|
||||
Array_457A, X
|
||||
|
||||
else, check for zero from $44F9 through $44FC.
|
||||
if non-zero, store #1 into `Byte_FF4E` and return.
|
||||
else:
|
||||
|
||||
ldx ArgA
|
||||
lda #1
|
||||
sta Array_44F8, X
|
||||
lda #0
|
||||
sta Byte_44F8
|
||||
|
||||
Then wait for `Array_44F8, X` to become zero. We're waiting for the IRQ to
|
||||
finish writing data from the tape to RAM.
|
||||
|
||||
After this, jump into opcode `0x9E` after the argument parsing to draw a
|
||||
screen.
|
||||
|
||||
## 0x9E Draw And Show Screen
|
||||
|
||||
Arguments: 2
|
||||
|
||||
ArgA
|
||||
ArgB
|
||||
|
||||
If ArgB == 0, clear out a bunch of arguments and call `Handler_DD`. Clears
|
||||
`Byte_44FE` afterwards and returns.
|
||||
|
||||
If ArgB != 0, make sure data has been loaded off the tape and is ready to draw.
|
||||
This is done by checking `Word_FF49, X` for a value of 1. If 1, increment it
|
||||
and store it in `Byte_44FE` before returning. Continue otherwise.
|
||||
|
||||
### Drawing
|
||||
|
||||
lda #0
|
||||
sta Byte_0750
|
||||
sta Byte_4579
|
||||
sta Byte_457A ("Array_457A")
|
||||
|
||||
lda ArgA
|
||||
sta Byte_4C
|
||||
|
||||
|
||||
### Data layout
|
||||
|
||||
Data in RAM, starting at CPU address $5000
|
||||
|
||||
$5000 Word offset to palette data. Added to #$5004.
|
||||
$5002 Word
|
||||
$5004 Byte loop counter apparently?? adds #6 to #$5004 this many times in a
|
||||
pointer. additional header data?
|
||||
|
||||
// generic image header data
|
||||
$5005 Byte Width (data row len (tile data len))
|
||||
$5006 Byte Height (row count (title count))
|
||||
Data length = Width*Height (stored in Word_61 and Word_6AFE)
|
||||
|
||||
$5007 Word data length? offset offset to attr data?
|
||||
|
||||
$5008 Byte X/Y coords
|
||||
$5009 Byte X/Y coords
|
||||
|
||||
|
||||
$500B Data start
|
||||
|
||||
## 0xBB Push String / Push Data
|
||||
|
||||
Stack Arguments: 0
|
||||
Inline Arguments: 32 bytes max (NULL terminated)
|
||||
|
||||
Push a NULL terminated string to the stack. This opperation will increment the
|
||||
stack pointer by 32 bytes, always. Even if more than 32 bytes are pushed to
|
||||
the stack. The code pointer is properly incremented, so long as there is a
|
||||
NULL byte within 255 bytes of the start of data.
|
||||
|
||||
## 0xC1 Jump Switch
|
||||
|
||||
Stack Arguments: 1
|
||||
Inline Arguments: 3+
|
||||
|
||||
ArgA
|
||||
|
||||
## 0xC5 Equal
|
||||
|
||||
If ArgA == ArgB
|
||||
push 1 to stack
|
||||
If ArgA != ArgB
|
||||
push 0 to stack
|
||||
|
||||
## 0xC6 Not Equal
|
||||
|
||||
Stack Arguments: 2
|
||||
Stack Result: 1
|
||||
|
||||
If ArgA != ArgB
|
||||
push 1 to stack
|
||||
If ArgA == ArgB
|
||||
push 0 to stack
|
||||
|
||||
## 0xC7 Less Than
|
||||
|
||||
If ArgA < ArgB
|
||||
push 1 to stack
|
||||
If ArgA >= ArgB
|
||||
push 0 to stack
|
||||
|
||||
## 0xC8 Less Than or Equal
|
||||
|
||||
If ArgA <= ArgB
|
||||
push 1 to stack
|
||||
If ArgA > ArgB
|
||||
push 0 to stack
|
||||
|
||||
## 0xC9 Greater Than
|
||||
|
||||
If ArgA > ArgB
|
||||
push 1 to stack
|
||||
If ArgA <= ArgB
|
||||
push 0 to stack
|
||||
|
||||
## 0xCA Greater Than or Equal To
|
||||
|
||||
If ArgA >= ArgB
|
||||
push 1 to stack
|
||||
If ArgA < ArgB
|
||||
push 0 to stack
|
||||
|
||||
## 0xCB Add
|
||||
|
||||
ArgA = ArgA + ArgB
|
||||
|
||||
## 0xCC Subtract
|
||||
|
||||
ArgA = ArgA - ArgB
|
||||
|
||||
## 0xCD Multiply
|
||||
|
||||
ArgA-ArgB = ArgA * ArgB
|
||||
|
||||
## 0xCE Signed Divide
|
||||
|
||||
ArgA = ArgA / ArgB
|
||||
|
||||
## 0xCF Negate
|
||||
|
||||
ArgA = 0 - ArgA
|
||||
|
||||
## 0xD1 Controller Stuff
|
||||
|
||||
Stack Arguments: 2
|
||||
Inline Arguments: 0
|
||||
|
||||
Vars:
|
||||
|
||||
Argument_C+0 = Argument_A+0
|
||||
Argument_A = Word_B1 + Argument_B
|
||||
Argument_B
|
||||
|
||||
|
||||
# 0xD4 Set Cursor Location
|
||||
|
||||
Stack Arguments: 3
|
||||
|
||||
Some sort of setup for 0xFE Draw Rom Character.
|
||||
|
||||
Byte_0606 = ArgA
|
||||
Byte_0604 = ArgB
|
||||
Byte_0605 = ArgC
|
||||
|
||||
## 0xE0 Modulo
|
||||
|
||||
ArgA = ArgA % ArgB
|
||||
|
||||
# 0xE7 Draw Metasprite
|
||||
|
||||
Stack Arguments: 7
|
||||
|
||||
ArgA sprite ID? (read as byte, but high is used as temp) Some sort of list size or count (header count?)
|
||||
This is used as a table lookup into a metasprite pointer table at $6980
|
||||
ArgB (byte) X Coord
|
||||
ArgC (byte) Y Coord
|
||||
ArgD (byte?) Palette override. If positive, bottom two bits are used directly for palette index.
|
||||
ArgE (byte) switch of some sort. sets X to $00 if zero, $20 if not zero
|
||||
ArgF (byte) Sprite flip. Uses two middle bits of value (`%0001_1000`)
|
||||
ArgG (byte) Extra args on HW stack??
|
||||
|
||||
Header data for metasprites. This location is pointed to by a table at $6980.
|
||||
|
||||
Width
|
||||
Height
|
||||
Count
|
||||
Palette??
|
||||
|
||||
There is a table at $0140 that keeps track of sprite allocations. Each byte
|
||||
corresponds to a hardware sprite and the value corresponds to a metasprite that
|
||||
that hardware sprite is a part of.
|
||||
|
||||
# 0xFE Draw Rom Character
|
||||
|
||||
Stack Arguments: 4
|
||||
Inline Arguments: 1 Word
|
||||
|
||||
Observed drawing a 1bpp kanji character taken from the SBX ROM charset.
|
||||
Inline word seems to be an ID or index for the character to draw.
|
||||
|
||||
|
|
@ -56,12 +56,10 @@ var Instructions []*Instruction = []*Instruction{
|
|||
&Instruction{ 0x96, 0, 2, 0, true, "set_word_4E"},
|
||||
&Instruction{ 0x97, 2, 0, 0, false, ""},
|
||||
&Instruction{ 0x98, 1, 0, 0, false, ""},
|
||||
&Instruction{ 0x99, 1, 0, 0, false, "enable_audio"},
|
||||
&Instruction{ 0x9A, 0, 0, 0, false, "disable_audio"},
|
||||
&Instruction{ 0x99, 1, 0, 0, false, ""},
|
||||
&Instruction{ 0x9A, 0, 0, 0, false, ""},
|
||||
&Instruction{ 0x9B, 0, 0, 0, false, "halt"},
|
||||
&Instruction{ 0x9C, 0, 0, 0, false, "toggle_44FE"},
|
||||
|
||||
// Waits for data from the tape then jumps into 0x9E code
|
||||
&Instruction{ 0x9D, 2, 0, 0, false, "something_tape"},
|
||||
|
||||
// Calls 0xEB draw_overlay. Draws the whole screen from data previously
|
||||
|
|
@ -107,10 +105,7 @@ var Instructions []*Instruction = []*Instruction{
|
|||
&Instruction{ 0xA7, 0, 0, 0, false, "call_asm"},
|
||||
|
||||
&Instruction{ 0xA8, 5, 0, 0, false, ""},
|
||||
|
||||
// Used when redrawing a portion of the screen after drawing a box or
|
||||
// some other image on top of the background.
|
||||
&Instruction{ 0xA9, 1, 0, 0, false, "restore_tiles"},
|
||||
&Instruction{ 0xA9, 1, 0, 0, false, ""},
|
||||
&Instruction{ 0xAA, 1, 0, 0, false, "long_jump"},
|
||||
&Instruction{ 0xAB, 1, 0, 0, false, "long_call"},
|
||||
&Instruction{ 0xAC, 0, 0, 0, false, "long_return"},
|
||||
|
|
@ -120,10 +115,7 @@ var Instructions []*Instruction = []*Instruction{
|
|||
|
||||
&Instruction{ 0xB0, 1, 0, 16, false, "arg_a_to_string"},
|
||||
&Instruction{ 0xB1, 1, 0, 16, false, "to_hex_string"},
|
||||
|
||||
// Reads the mic bit on $4016 and puts it on the stack
|
||||
&Instruction{ 0xB2, 0, 0, 1, false, "read_mic"},
|
||||
|
||||
&Instruction{ 0xB2, 0, 0, 1, false, ""},
|
||||
&Instruction{ 0xB3, 7, 0, 0, false, ""}, // possible 16-bit inline?
|
||||
|
||||
// If ($471A) is > #$60, copy to ($4E). Setup for some other bullshit?
|
||||
|
|
@ -204,7 +196,7 @@ var Instructions []*Instruction = []*Instruction{
|
|||
|
||||
&Instruction{ 0xDA, 1, 0, 16, false, "to_int_string"},
|
||||
&Instruction{ 0xDB, 3, 0, 0, false, ""},
|
||||
&Instruction{ 0xDC, 5, 0, 0, false, ""}, // fucks with attribute data
|
||||
&Instruction{ 0xDC, 5, 0, 0, false, ""},
|
||||
|
||||
// ArgA, ArgB: X,Y of corner A
|
||||
// ArgC, ArgD: X,Y of corner B
|
||||
|
|
@ -214,13 +206,7 @@ var Instructions []*Instruction = []*Instruction{
|
|||
&Instruction{ 0xDD, 5, 0, 0, false, "fill_box"},
|
||||
|
||||
&Instruction{ 0xDE, 3, 0, 0, false, ""},
|
||||
|
||||
// ArgA: ??
|
||||
// ArgB: X (tile coords)
|
||||
// ArgC: Y (tile coords)
|
||||
// Draws an image on top of the background using data already
|
||||
// loaded from *somewhere*. (seen using data from tape)
|
||||
&Instruction{ 0xDF, 3, 0, 0, false, "draw_image"},
|
||||
&Instruction{ 0xDF, 3, 0, 0, false, ""},
|
||||
|
||||
// Divide and return remainder
|
||||
&Instruction{ 0xE0, 2, 0, 1, false, "modulo"},
|
||||
|
|
|
|||
Loading…
Reference in New Issue