/* main.c -- Bad Apple payload: network receive, then tile playback. */ #include "movie.h" #include "romapi.h" #include "modecfg.h" #ifndef NET_DEFAULT #define NET_DEFAULT 0 #endif #define NET_LEN 2401942 #define NET_PORT 7001 #define MOVIE_BASE 0x00080000 #define MOVIE_MAX 0x00308000 #define VRAM_BASE 0x04000000 #define ROW_BITS 2048 /* The marker/control block lives in .bss, not at bit 0x01810000: a 2.4 MB * heap allocation can cross that address and would corrupt the movie. */ unsigned int marker_page[16]; #define MARK ((unsigned int)marker_page) #define CTRL_START (MARK + 0x20 * 8) #define CTRL_COUNT (MARK + 0x24 * 8) #define CTRL_PACE (MARK + 0x28 * 8) #define CTRL_MOVIE (MARK + 0x2C * 8) #define CTRL_MOVIELEN (MARK + 0x30 * 8) #define CTRL_WAIT (MARK + 0x34 * 8) #define CTRL_READY (MARK + 0x38 * 8) #define CTRL_NET (MARK + 0x3C * 8) #define CTRL_MAGIC (MARK + 0x1C * 8) #define MAGIC_VALUE 0x42414450 extern void fill_run_(); extern void fill_init_(); extern void vid_dint_(); unsigned int fill_daddr; unsigned int fill_dydx; unsigned int fill_color; void fill_rect(x, y, w, h, color) int x; int y; int w; int h; int color; { if (w <= 0 || h <= 0) { return; } fill_daddr = VRAM_BASE + (unsigned int)y * ROW_BITS + (unsigned int)x; fill_dydx = ((unsigned int)h << 16) | (unsigned int)w; fill_color = color ? 0x0000 : 0xFFFF; fill_run_(); } static void paint(x, y, w, h, color, ctx) int x; int y; int w; int h; int color; void *ctx; { fill_rect(x, y, w, h, color); } static void show_fault(code) unsigned int code; { fill_init_(); fill_rect(0, 0, 1280, 64, 1); *(unsigned int *)MARK = code; for (;;) { } } static unsigned char rxbuf[1600]; static unsigned char sa[16]; static unsigned char from[16]; static int fromlen; static unsigned int rx_error; /* Receive movie.bin over the OS UDP stack into a malloc'd bit buffer. * Packet: u32 offset, u16 length, u16 seq, u16 crc16, then bytes. * A final offset==expected/length==4 packet carries the 32-bit file checksum. */ static unsigned int receive_movie(expected) unsigned int expected; { unsigned int ptr; unsigned int offset; unsigned int length; unsigned int sent_sum; unsigned int i; unsigned char *dst; int fd; int n; ptr = malloc_(expected); if (ptr == 0) { rx_error = 3; return 0; } fd = socket_(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd < 0) { rx_error = 4; return 0; } sa_any(sa, NET_PORT); if (bind_(fd, sa, SOCKADDR_LEN) < 0) { close_(fd); rx_error = 5; return 0; } sent_sum = 0; dst = (unsigned char *)ptr; for (;;) { fromlen = SOCKADDR_LEN; n = recvfrom_(fd, rxbuf, (int)sizeof(rxbuf), 0, from, &fromlen); if (n < 10) { continue; } offset = (unsigned int)rxbuf[0] | ((unsigned int)rxbuf[1] << 8) | ((unsigned int)rxbuf[2] << 16) | ((unsigned int)rxbuf[3] << 24); length = (unsigned int)rxbuf[4] | ((unsigned int)rxbuf[5] << 8); if (offset == expected && length == 4 && n >= 14) { sent_sum = (unsigned int)rxbuf[10] | ((unsigned int)rxbuf[11] << 8) | ((unsigned int)rxbuf[12] << 16) | ((unsigned int)rxbuf[13] << 24); if (movie_checksum(dst, expected) == sent_sum) { break; } sent_sum = 0; continue; } if (length > (unsigned int)(n - 10)) { continue; } if (offset > expected || length > expected - offset) { continue; } for (i = 0; i < length; i++) { dst[offset + i] = rxbuf[10 + i]; } } close_(fd); if (sent_sum == 0) { rx_error = 6; return 0; } return ptr; } /* Poll the display interrupt pending bit. Interrupts are disabled; this is * a status bit, not an ISR. A missing DPYINT only costs a bounded spin. */ static void wait_vsync() { unsigned int i; unsigned short pending; *(unsigned short *)0xC0000120 = 0; for (i = 0; i < 200000; i++) { pending = *(unsigned short *)0xC0000120; if (pending & 0x0400) { return; } } } int main() { struct movie_header header; unsigned int magic; unsigned int frame; unsigned int last; unsigned int start; unsigned int count; unsigned int pace; unsigned int net; unsigned int wait; unsigned int movie_base; unsigned int movie_len; unsigned int ticks; unsigned int now; unsigned int next; int rc; magic = *(unsigned int *)CTRL_MAGIC; if (magic == MAGIC_VALUE) { start = *(unsigned int *)CTRL_START; count = *(unsigned int *)CTRL_COUNT; pace = *(unsigned int *)CTRL_PACE; net = *(unsigned int *)CTRL_NET; wait = *(unsigned int *)CTRL_WAIT; movie_base = *(unsigned int *)CTRL_MOVIE; movie_len = *(unsigned int *)CTRL_MOVIELEN; } else { start = 0; count = 0; pace = 1; net = NET_DEFAULT; wait = 0; movie_base = MOVIE_BASE; movie_len = MOVIE_MAX; } if (net == 0 && NET_DEFAULT) { net = 1; } if (net != 0) { movie_len = NET_LEN; *(unsigned int *)(MARK + 0x10 * 8) = 0x20; movie_base = receive_movie(movie_len); if (movie_base == 0) { show_fault(0xBAD00000 | rx_error); } vid_dint_(); } else { vid_dint_(); if (wait != 0) { *(unsigned int *)(MARK + 0x10 * 8) = 0x10; while (*(unsigned int *)CTRL_READY == 0) { } } } fill_init_(); *(unsigned short *)0xC00000A0 = 1; fill_rect(0, 0, 1280, 1024, 0); rc = movie_open((unsigned char *)movie_base, movie_len, &header); if (rc != 0) { show_fault(0xBAD00001); } last = header.frame_count; if (last != 0) { last = last - 1; } if (count != 0 && start + count - 1 < last) { last = start + count - 1; } now = 0; next = 0; frame = 0; while (frame <= last) { if (pace != 0 && frame >= start) { wait_vsync(); now += 5; /* 60/72 in 1/360 s units */ if (now < next) { continue; } } rc = movie_decode_frame((unsigned char *)movie_base, movie_len, frame, &header, paint, 0); if (rc != 0) { show_fault(0xBAD00002); } ticks = movie_frame_ticks((unsigned char *)movie_base, movie_len, frame, &header); if (ticks < 1) { ticks = 1; } next += 6 * ticks; if (next + 360 < now) { next = now; } *(unsigned int *)MARK = frame; frame++; } *(unsigned int *)(MARK + 0x10 * 8) = (count != 0) ? 2 : 1; for (;;) { } }