commit
b8df1232f9
|
@ -4,3 +4,7 @@
|
|||
*.vs
|
||||
*.dbg
|
||||
*.asm
|
||||
*.woz
|
||||
*.bin
|
||||
bug/*
|
||||
cc65/*
|
||||
|
|
22
README.md
22
README.md
|
@ -2,13 +2,23 @@
|
|||
|
||||
Library and demos for the Apple-1 TMS9918 video card by P-LAB.
|
||||
|
||||
# USAGE (Windows)
|
||||
|
||||
- `env_kick.bat` sets KickC compiler path variable (run it once)
|
||||
- `c.bat` compiles `test.c` for both VIC-20 and Apple-1
|
||||
- `test_vic20.prg` runs on the "hybrid" VIC-20 emulator
|
||||
- `node hexdump` puts `test_apple1.prg` into WOZMON format for the Apple-1
|
||||
# Building for the "Juke Box" EPROM expansion
|
||||
|
||||
The demo contained in the repo are meant to be launched from the Apple-1 "Juke Box"
|
||||
EPROM expansion at address $4000. The executables are built according to the
|
||||
following memory map:
|
||||
|
||||
$0000-$00FF zero page: holds most of C program variables
|
||||
$0280-$0FFF low RAM space: C program "Data" segment
|
||||
$4000-$7581 EPROM (Juke Box): C program "Code" segment
|
||||
$7582-$7FFF EPROM (Juke Box): C program "Data" segment (startup values)
|
||||
|
||||
The build script "mkeprom.js" creates a 16K binary file to be placed on the
|
||||
EPROM where the "Data" segment startup values are put at the end of the file itself.
|
||||
The user program need to manually copy such data from EPROM to low RAM once after
|
||||
the `main()` is launched. That can be easily done by simply calling the library
|
||||
function `apple1_eprom_init()`.
|
||||
|
||||
Note: Apple-1 start address is $4000, with TMS address range $C000-$C001
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
#pragma zp_reserve(0,1,2)
|
||||
|
||||
volatile word tick_counts;
|
||||
|
||||
__interrupt(hardware_all) void interrupt_handler() {
|
||||
tick_counts++;
|
||||
}
|
||||
|
||||
void install_interrupt() {
|
||||
*((word *)0x0001) = (word) &interrupt_handler;
|
||||
}
|
||||
|
||||
void main() {
|
||||
// make some use of "tick_counts"
|
||||
*((word *)0x0001) = tick_counts;
|
||||
|
||||
install_interrupt();
|
||||
}
|
||||
|
||||
/*
|
||||
const byte *VDP_DATA = 0xA000; // TMS9918 data port (VRAM)
|
||||
#define TMS_READ_DATA (*VDP_DATA);
|
||||
|
||||
void main() {
|
||||
byte x = TMS_READ_DATA();
|
||||
*((byte *)0xFFFF) = x;
|
||||
}
|
||||
*/
|
13
c.bat
13
c.bat
|
@ -1,13 +0,0 @@
|
|||
rem call kickc -t asm6502 -D=APPLE1 bug1.c -o bug1.prg -e
|
||||
rem goto uscita
|
||||
|
||||
@echo ======================== VIC20 ===================================================
|
||||
call kickc -t VIC20 -D=VIC20 test.c -o test_vic20.prg -e
|
||||
rem call kickc -vasmoptimize -vasmout -vcreate -vfragment -vliverange -vloop -vnonoptimize -voptimize -vparse -vsequence -vsizeinfo -vunroll -vuplift -t VIC20 -D=VIC20 test.c -o test_vic20.prg -e
|
||||
copy test.prg test_vic20.prg
|
||||
|
||||
@echo ======================== APPLE 1 =================================================
|
||||
call kickc -t asm6502 -D=APPLE1 test.c -o test_apple1.prg -e
|
||||
copy test.prg test_apple1.prg
|
||||
|
||||
:uscita
|
|
@ -0,0 +1,55 @@
|
|||
// TODO make screen tables parametric (fixed values calculated by macros)
|
||||
|
||||
#include "../lib/utils.h"
|
||||
#include "../lib/apple1.h"
|
||||
#include "../lib/tms9918.h"
|
||||
#include "../lib/font8x8.h"
|
||||
#include "../lib/tms_screen1.h"
|
||||
#include "../lib/tms_screen2.h"
|
||||
#include "../lib/interrupt.h"
|
||||
|
||||
#include "demo_screen1.h"
|
||||
#include "demo_screen2.h"
|
||||
#include "demo_amiga_hand.h"
|
||||
#include "demo_interrupt.h"
|
||||
#include "demo_extvid.h"
|
||||
#include "demo_blank.h"
|
||||
|
||||
void help() {
|
||||
woz_puts(
|
||||
"\rTMS9918 DEMOS\r"
|
||||
"=============\r"
|
||||
"1 SCREEN1\r"
|
||||
"2 SCREEN2\r"
|
||||
"A AMIGA HAND\r"
|
||||
"I INTERRUPT\r"
|
||||
"E FLIP EXT VIDEO\r"
|
||||
"B BLANK ON/OFF\r"
|
||||
"H HELP\r"
|
||||
"0 EXITS\r\r"
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
#ifdef APPLE1
|
||||
apple1_eprom_init();
|
||||
#endif
|
||||
|
||||
byte key = 'H';
|
||||
for(;;) {
|
||||
if(key == '1') prova_screen1();
|
||||
else if(key == '2') prova_screen2();
|
||||
else if(key == 'A') demo_amiga_hand();
|
||||
else if(key == 'I') demo_interrupt();
|
||||
else if(key == 'E') flip_external_input();
|
||||
else if(key == 'B') flip_blank();
|
||||
else if(key == 'H') help();
|
||||
else if(key == '0') break;
|
||||
else woz_putc(key);
|
||||
|
||||
key = apple1_getkey();
|
||||
}
|
||||
woz_puts("BYE\r");
|
||||
woz_mon();
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
byte amiga_data[612] = {
|
||||
107,35,130,35,
|
||||
130,35,130,57,
|
||||
130,57,185,57,
|
||||
185,57,185,35,
|
||||
185,35,197,35,
|
||||
197,35,208,46,
|
||||
208,46,208,118,
|
||||
208,118,199,118,
|
||||
199,118,199,80,
|
||||
199,80,136,80,
|
||||
136,80,134,78,
|
||||
134,78,125,78,
|
||||
125,78,124,80,
|
||||
124,80,117,80,
|
||||
117,80,117,89,
|
||||
117,89,107,96,
|
||||
107,96,107,35,
|
||||
105,96,105,34,
|
||||
105,34,198,34,
|
||||
198,34,210,46,
|
||||
210,46,210,119,
|
||||
210,119,158,119,
|
||||
158,119,158,124,
|
||||
158,124,154,132,
|
||||
154,132,150,137,
|
||||
150,137,146,140,
|
||||
146,140,140,149,
|
||||
140,149,132,153,
|
||||
132,153,132,164,
|
||||
132,164,82,164,
|
||||
82,164,82,133,
|
||||
82,133,81,133,
|
||||
81,133,81,105,
|
||||
81,105,85,99,
|
||||
85,99,92,93,
|
||||
92,93,93,89,
|
||||
93,89,97,82,
|
||||
97,82,102,79,
|
||||
102,79,105,78,
|
||||
105,78,105,78,
|
||||
105,78,102,80,
|
||||
102,80,98,82,
|
||||
98,82,94,89,
|
||||
94,89,93,93,
|
||||
93,93,86,99,
|
||||
86,99,82,105,
|
||||
82,105,82,132,
|
||||
82,132,83,133,
|
||||
83,133,83,163,
|
||||
83,163,112,163,
|
||||
112,163,112,142,
|
||||
112,142,120,142,
|
||||
120,142,124,138,
|
||||
124,138,124,119,
|
||||
124,119,122,116,
|
||||
122,116,122,106,
|
||||
122,106,137,93,
|
||||
137,93,137,81,
|
||||
137,81,134,79,
|
||||
134,79,131,79,
|
||||
131,79,134,82,
|
||||
134,82,134,89,
|
||||
134,89,133,90,
|
||||
133,90,126,90,
|
||||
126,90,123,87,
|
||||
123,87,114,94,
|
||||
114,94,102,100,
|
||||
102,100,90,109,
|
||||
90,109,90,108,
|
||||
90,108,102,99,
|
||||
102,99,98,98,
|
||||
98,98,95,95,
|
||||
95,95,98,97,
|
||||
98,97,102,98,
|
||||
102,98,105,96,
|
||||
122,85,124,78,
|
||||
124,78,132,79,
|
||||
132,79,133,82,
|
||||
133,82,133,89,
|
||||
133,89,126,89,
|
||||
126,89,122,85,
|
||||
123,116,123,106,
|
||||
123,106,138,93,
|
||||
138,93,138,81,
|
||||
138,81,197,81,
|
||||
197,81,197,118,
|
||||
197,118,124,118,
|
||||
124,118,123,114,
|
||||
132,35,183,35,
|
||||
183,35,183,56,
|
||||
183,56,132,56,
|
||||
132,56,132,35,
|
||||
168,38,179,38,
|
||||
179,38,179,52,
|
||||
179,52,168,52,
|
||||
168,52,168,38,
|
||||
170,39,177,39,
|
||||
177,39,177,51,
|
||||
177,51,170,51,
|
||||
170,51,170,39,
|
||||
119,81,122,81,
|
||||
122,81,122,83,
|
||||
122,83,119,87,
|
||||
119,87,119,81,
|
||||
113,163,113,143,
|
||||
113,143,120,143,
|
||||
120,143,125,138,
|
||||
125,138,125,129,
|
||||
125,129,129,131,
|
||||
129,131,137,131,
|
||||
137,131,137,133,
|
||||
137,133,141,138,
|
||||
141,138,145,138,
|
||||
145,138,145,140,
|
||||
145,140,139,149,
|
||||
139,149,131,152,
|
||||
131,152,131,163,
|
||||
131,163,113,163,
|
||||
125,119,125,124,
|
||||
125,124,130,121,
|
||||
130,121,125,119,
|
||||
129,122,125,124,
|
||||
125,124,125,119,
|
||||
125,119,146,119,
|
||||
146,119,136,129,
|
||||
136,129,135,129,
|
||||
135,129,137,127,
|
||||
137,127,132,122,
|
||||
132,122,129,122,
|
||||
150,119,157,119,
|
||||
157,119,157,124,
|
||||
157,124,153,132,
|
||||
153,132,150,136,
|
||||
150,136,145,137,
|
||||
145,137,142,137,
|
||||
142,137,139,133,
|
||||
139,133,139,130,
|
||||
139,130,150,119,
|
||||
140,130,140,133,
|
||||
140,133,142,136,
|
||||
142,136,144,136,
|
||||
144,136,148,135,
|
||||
148,135,149,132,
|
||||
149,132,145,129,
|
||||
145,129,140,130,
|
||||
126,128,134,130,
|
||||
134,130,136,127,
|
||||
136,127,132,123,
|
||||
132,123,129,123,
|
||||
129,123,126,125,
|
||||
126,125,126,128,
|
||||
198,35,209,46,
|
||||
209,46,209,118
|
||||
};
|
||||
|
||||
void demo_amiga_hand() {
|
||||
tms_init_regs(SCREEN2_TABLE);
|
||||
screen2_init_bitmap(FG_BG(COLOR_BLACK,COLOR_WHITE));
|
||||
|
||||
screen2_puts(0, 0, FG_BG(COLOR_BLACK,COLOR_WHITE), "*** P-LAB VIDEO CARD SYSTEM ***");
|
||||
screen2_puts(0, 2, FG_BG(COLOR_BLACK,COLOR_WHITE), "16K VRAM BYTES FREE");
|
||||
screen2_puts(0, 4, FG_BG(COLOR_BLACK,COLOR_WHITE), "READY.");
|
||||
|
||||
for(word p=0;p<612;p+=4) {
|
||||
vti_line(amiga_data[p],amiga_data[p+1],amiga_data[p+2],amiga_data[p+3]);
|
||||
}
|
||||
screen2_puts(18, 12, FG_BG(COLOR_DARK_BLUE, COLOR_WHITE), "APPLE1");
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// TODO make it static once KickC bug is fixed
|
||||
byte blank = BLANK_OFF;
|
||||
|
||||
void flip_blank() {
|
||||
blank ^= 1;
|
||||
|
||||
if(blank == BLANK_OFF) woz_puts("NORMAL\r");
|
||||
else woz_puts("BLANK\r");
|
||||
|
||||
// write "blank" bit
|
||||
tms_set_blank(blank);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// TODO make it static once KickC bug is fixed
|
||||
byte external_input = 0;
|
||||
|
||||
void flip_external_input() {
|
||||
external_input ^= 1;
|
||||
|
||||
if(external_input) woz_puts("EXT INPUT ON\r");
|
||||
else woz_puts("EXT INPUT OFF\r");
|
||||
|
||||
// fill color table with transparent color so that external input can be seen
|
||||
tms_set_vram_write_addr(SCREEN1_COLOR_TABLE);
|
||||
for(byte i=32;i!=0;i--) {
|
||||
TMS_WRITE_DATA_PORT(FG_BG(COLOR_DARK_YELLOW, COLOR_TRANSPARENT));
|
||||
}
|
||||
|
||||
tms_set_external_video(external_input); // write "external video input" bit
|
||||
tms_set_color(0); // transparent color
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// TODO make it static once KickC bug is fixed
|
||||
byte last_seconds = 0;
|
||||
|
||||
void demo_interrupt() {
|
||||
|
||||
// resets the watch to 00:00:00.0
|
||||
_ticks = 0;
|
||||
_seconds = 0;
|
||||
_minutes = 0;
|
||||
_hours = 0;
|
||||
|
||||
// installs the interrupt handler routine
|
||||
install_interrupt();
|
||||
|
||||
// enables interrupts on the TMS9918
|
||||
tms_set_interrupt_bit(INTERRUPT_ENABLED);
|
||||
|
||||
woz_puts("INTERRUPT INSTALLED\r");
|
||||
woz_puts("0 TURNS OFF\r");
|
||||
woz_puts("1 TURNS ON\r");
|
||||
woz_puts("E EXIT TO MAIN MENU\r");
|
||||
|
||||
for(;;) {
|
||||
if(apple1_iskeypressed()) {
|
||||
byte k = apple1_getkey();
|
||||
if(k=='1') { tms_set_interrupt_bit(INTERRUPT_ENABLED); woz_puts("INT ENABLED\r"); }
|
||||
else if(k=='0') { tms_set_interrupt_bit(INTERRUPT_DISABLED); woz_puts("INT DISABLED\r"); }
|
||||
else if(k=='E') break;
|
||||
}
|
||||
|
||||
if(last_seconds != _seconds) {
|
||||
woz_print_hex(_hours); woz_putc(':');
|
||||
woz_print_hex(_minutes); woz_putc(':');
|
||||
woz_print_hex(_seconds); woz_putc('.');
|
||||
woz_print_hex(_ticks); woz_putc('\r');
|
||||
last_seconds = _seconds;
|
||||
}
|
||||
}
|
||||
|
||||
// disables interrupts on the TMS9918
|
||||
tms_set_interrupt_bit(INTERRUPT_DISABLED);
|
||||
|
||||
woz_puts("INTERRUPT STOPPED\r");
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
void screen1_square_sprites() {
|
||||
// fills first sprite pattern with 255
|
||||
tms_set_vram_write_addr(SCREEN1_SPRITE_PATTERNS); // start writing in the sprite patterns
|
||||
for(byte i=0;i<8;i++) {
|
||||
TMS_WRITE_DATA_PORT(255);
|
||||
}
|
||||
|
||||
// set sprite coordinates
|
||||
tms_set_vram_write_addr(SCREEN1_SPRITE_ATTRS); // start writing in the sprite attribute
|
||||
for(byte i=0;i<32;i++) {
|
||||
TMS_WRITE_DATA_PORT((6+i)*8); NOP; NOP; NOP; NOP; // y coordinate
|
||||
TMS_WRITE_DATA_PORT((6+i)*8); NOP; NOP; NOP; NOP; // x coordinate
|
||||
TMS_WRITE_DATA_PORT(0); NOP; NOP; NOP; NOP; // name
|
||||
TMS_WRITE_DATA_PORT(i); NOP; NOP; NOP; NOP; // color
|
||||
}
|
||||
}
|
||||
|
||||
void prova_screen1() {
|
||||
tms_init_regs(SCREEN1_TABLE);
|
||||
screen1_prepare();
|
||||
screen1_load_font();
|
||||
|
||||
screen1_home(); screen1_puts("*** P-LAB VIDEO CARD SYSTEM ***");
|
||||
screen1_locate(0, 2); screen1_puts("16K VRAM BYTES FREE");
|
||||
screen1_locate(0, 4); screen1_puts("READY.");
|
||||
|
||||
screen1_locate(0, 10);
|
||||
for(word i=0;i<256;i++) screen1_putc((byte)i);
|
||||
|
||||
screen1_square_sprites();
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
void prova_screen2() {
|
||||
tms_init_regs(SCREEN2_TABLE);
|
||||
|
||||
byte text_color = FG_BG(COLOR_BLACK,COLOR_WHITE);
|
||||
|
||||
screen2_init_bitmap(text_color);
|
||||
screen2_puts(0, 0, text_color, "*** P-LAB VIDEO CARD SYSTEM ***");
|
||||
screen2_puts(0, 2, text_color, "16K VRAM BYTES FREE");
|
||||
screen2_puts(0, 4, text_color, "READY.");
|
||||
|
||||
// display all colors in the palette
|
||||
for(byte i=0;i<16;i++) {
|
||||
screen2_puts(5,(byte)(6+i),(byte)(((15-i)<<4)+i)," SCREEN 2 ");
|
||||
}
|
||||
|
||||
vti_line(18, 45,232,187);
|
||||
vti_line(18,187,232, 45);
|
||||
|
||||
SCREEN2_PLOT_MODE = PLOT_MODE_RESET;
|
||||
|
||||
vti_line(18+5, 45,232+5,187);
|
||||
vti_line(18+5,187,232+5, 45);
|
||||
|
||||
SCREEN2_PLOT_MODE = PLOT_MODE_INVERT;
|
||||
|
||||
vti_line(18+5+5, 45,232+5+5,187);
|
||||
vti_line(18+5+5,187,232+5+5, 45);
|
||||
|
||||
SCREEN2_PLOT_MODE = PLOT_MODE_SET;
|
||||
|
||||
//vti_ellipse_rect(7,9,202,167);
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
@call ..\tools\build demo
|
||||
|
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,3 @@
|
|||
path %path%;"c:\Program Files\Java\jdk1.8.0_161\bin"
|
||||
path %path%;C:\Users\Nino1\Desktop\kickc\bin
|
||||
path %path%;C:\Users\Nino1\Desktop\USB\kickc\bin
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
// Apple 1
|
||||
.file [name="%O", type="prg", segments="Program"]
|
||||
.segmentdef Program [segments="Code, Data"]
|
||||
.segmentdef Code [start=%P,outBin="apple1_codeseg.bin"]
|
||||
.segmentdef Data [start=$0280,min=$0280,max=$0fff,outBin="apple1_dataseg.bin"]
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"extension": "prg",
|
||||
"link": "apple1.ld",
|
||||
"start_address": "0x2000",
|
||||
"cpu": "MOS6502X",
|
||||
"interrupt": "hardware_all",
|
||||
"emulator": "x64sc"
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// Commodore VIC 20 8k executable PRG file
|
||||
.file [name="%O", type="prg", segments="Program"]
|
||||
.segmentdef Program [segments="Basic, Code, Data"]
|
||||
.segmentdef Basic [start=$1201]
|
||||
.segmentdef Code [start=%P]
|
||||
.segmentdef Data [startAfter="Code"]
|
||||
.segment Basic
|
||||
:BasicUpstart(%E)
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"description": "Commodore VIC 20 8K executable PRG file",
|
||||
"extension": "prg",
|
||||
"link": "vic20_8k.ld",
|
||||
"start_address": "0x120d",
|
||||
"cpu": "MOS6502X",
|
||||
"interrupt": "rom_min_vic20",
|
||||
"emulator": "xvic",
|
||||
"defines": {
|
||||
"__VIC20__": 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
#ifdef APPLE1
|
||||
// APPLE1
|
||||
#pragma start_address(0x4000)
|
||||
const word WOZMON = 0xFF1F; // enters monitor
|
||||
const word ECHO = 0xFFEF; // output ascii character in A (A not destroyed)
|
||||
const word PRBYTE = 0xFFDC; // print hex byte in A (A destroyed)
|
||||
const word KEY_DATA = 0xd010; // read key
|
||||
const word KEY_CTRL = 0xd011; // control port
|
||||
const word TERM_DATA = 0xd012; // write ascii
|
||||
const word TERM_CTRL = 0xd013; // control port
|
||||
#else
|
||||
// VIC20
|
||||
const word ECHO = 0xFFD2; // chrout routine in kernal rom
|
||||
const word GETIN = 0xFFE4; // GETIN keyboard read routine
|
||||
#endif
|
||||
|
||||
// prints a hex byte using the WOZMON routine
|
||||
void woz_print_hex(byte c) {
|
||||
#ifdef APPLE1
|
||||
asm {
|
||||
lda c
|
||||
jsr PRBYTE
|
||||
};
|
||||
#else
|
||||
asm {
|
||||
lda c
|
||||
jsr ECHO
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
// puts a character on the apple1 screen using the WOZMON routine
|
||||
void woz_putc(byte c) {
|
||||
asm {
|
||||
lda c
|
||||
jsr ECHO
|
||||
}
|
||||
}
|
||||
|
||||
// puts a 0-terminated string on the apple1 screen
|
||||
void woz_puts(byte *s) {
|
||||
byte c;
|
||||
while(c=*s++) woz_putc(c);
|
||||
}
|
||||
|
||||
// returns to WOZMON prompt
|
||||
void woz_mon() {
|
||||
#ifdef APPLE1
|
||||
asm {
|
||||
jmp WOZMON
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns nonzero if a key has been pressed
|
||||
inline byte apple1_iskeypressed() {
|
||||
#ifdef APPLE1
|
||||
return PEEK(KEY_CTRL) & 0x80;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// blocking keyboard read
|
||||
// reads a key from the apple-1 keyboard
|
||||
byte apple1_getkey() {
|
||||
#ifdef APPLE1
|
||||
asm {
|
||||
__wait:
|
||||
lda KEY_CTRL
|
||||
bpl __wait
|
||||
}
|
||||
return PEEK(KEY_DATA) & 0x7f;
|
||||
#else
|
||||
byte key;
|
||||
byte const *keyptr = &key;
|
||||
kickasm(uses keyptr, uses GETIN) {{
|
||||
__wait:
|
||||
jsr GETIN
|
||||
cmp #0
|
||||
beq __wait
|
||||
sta keyptr
|
||||
}}
|
||||
return key;
|
||||
#endif
|
||||
}
|
||||
|
||||
// non blocking keyboard read
|
||||
// reads a key and return 0 if no key is pressed
|
||||
byte apple1_readkey() {
|
||||
#ifdef APPLE1
|
||||
if((PEEK(KEY_CTRL) & 0x80)==0) return 0;
|
||||
else return PEEK(KEY_DATA) & 0x7f;
|
||||
#else
|
||||
byte key;
|
||||
byte const *keyptr = &key;
|
||||
kickasm(uses keyptr, uses GETIN) {{
|
||||
jsr GETIN
|
||||
cmp #0
|
||||
bne __keypress
|
||||
lda #0
|
||||
__keypress:
|
||||
sta keyptr
|
||||
}}
|
||||
return key;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef APPLE1
|
||||
|
||||
#include <stdlib.h> // for memcpy
|
||||
|
||||
#define LOWRAM_START 0x280
|
||||
#define LOWRAM_END 0x7FF
|
||||
#define LOWRAM_SIZE (LOWRAM_END - LOWRAM_START + 1)
|
||||
#define DATAINCODE (0x8000 - LOWRAM_SIZE)
|
||||
|
||||
inline void apple1_eprom_init() {
|
||||
// copy the initializaton data from ROM to lowram where "Data" segment is allocated
|
||||
memcpy(LOWRAM_START, DATAINCODE, LOWRAM_SIZE);
|
||||
}
|
||||
#endif
|
|
@ -1,3 +1,7 @@
|
|||
// font from LASER-500 (ASCII characters only)
|
||||
|
||||
#pragma data_seg(Code)
|
||||
|
||||
byte FONT[768] = {
|
||||
0
|
||||
, 0
|
||||
|
@ -854,3 +858,5 @@ byte FONT[768] = {
|
|||
, 0
|
||||
, 0
|
||||
};
|
||||
|
||||
#pragma data_seg(Data)
|
|
@ -0,0 +1,52 @@
|
|||
// reserve locations 0,1,2 used by the APPLE1 IRQ jump vector
|
||||
// these will contain a three byte instruction "JUMP <interrupt_handler>"
|
||||
// in order to make the interrupt routine not reside in zero page
|
||||
#pragma zp_reserve(0,1,2)
|
||||
|
||||
// acknowledge the interrupt by reading the status register on the TMS9918 (see manual section 2.3.1)
|
||||
inline void acknowledge_interrupt() {
|
||||
asm { lda VDP_REG };
|
||||
}
|
||||
|
||||
export __address(0) byte IRQ_JUMP_OPCODE; // location 0 contains the opcode for "JMP"
|
||||
export __address(1) word IRQ_JUMP_ADDRESS; // location 1 and 2 contain the jump address
|
||||
|
||||
// storage for a simple watch timer
|
||||
export volatile byte _ticks;
|
||||
export volatile byte _seconds;
|
||||
export volatile byte _minutes;
|
||||
export volatile byte _hours;
|
||||
export volatile byte _irq_trigger;
|
||||
|
||||
// interrupt routine called every 1/60th by the CPU after TMS9918 sets the /INT pin
|
||||
export __interrupt(hardware_all) void interrupt_handler() {
|
||||
// update the watch
|
||||
if(++_ticks == 60) {
|
||||
_ticks = 0;
|
||||
if(++_seconds == 60) {
|
||||
_seconds = 0;
|
||||
if(++_minutes == 60) {
|
||||
_minutes = 0;
|
||||
_hours++;
|
||||
}
|
||||
}
|
||||
}
|
||||
_irq_trigger = 1; // signals that an interrupt has been triggered
|
||||
|
||||
acknowledge_interrupt(); // acknowledge interrupt by reading the status register
|
||||
}
|
||||
|
||||
// safely installs the interrupt routine
|
||||
void install_interrupt() {
|
||||
asm { sei }; // disable 6502 interrupts
|
||||
IRQ_JUMP_OPCODE = 0x4C; // $4C = JUMP opcode
|
||||
IRQ_JUMP_ADDRESS = (word) &interrupt_handler; // JUMP interrupt_handler
|
||||
asm { cli }; // re-enable 6502 interrupts
|
||||
}
|
||||
|
||||
// waits until IRQ is triggered
|
||||
void wait_interrupt() {
|
||||
// _irq_trigger = 0;
|
||||
// while(_irq_trigger == 0); // waits until it's set to 1 from the interrupt handler
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
#pragma encoding(ascii) // encode strings in plain ascii
|
||||
|
||||
#ifdef APPLE1
|
||||
const byte *VDP_DATA = 0xCC00; // TMS9918 data port (VRAM)
|
||||
const byte *VDP_REG = 0xCC01; // TMS9918 register port (write) or status (read)
|
||||
#else
|
||||
const byte *VDP_DATA = 0xA000; // TMS9918 data port (VRAM)
|
||||
const byte *VDP_REG = 0xA001; // TMS9918 register port (write) or status (read)
|
||||
#endif
|
||||
|
||||
// control port bits
|
||||
const byte WRITE_TO_VRAM = 0b01000000; // write to VRAM command
|
||||
const byte READ_FROM_VRAM = 0b00000000; // read from VRAM command
|
||||
const byte HIADDRESS_MASK = 0b00111111; // bit mask for the high byte of the address
|
||||
|
||||
const byte WRITE_TO_REG = 0b10000000; // write to register command
|
||||
const byte REGNUM_MASK = 0b00000111; // bit mask for register number (0-7)
|
||||
|
||||
// register 0 masks
|
||||
const byte REG0_M3_MASK = 0b00000010;
|
||||
const byte REG0_EXTVID_MASK = 0b00000001;
|
||||
|
||||
// register 1 masks
|
||||
const byte REG1_16K_MASK = 0b10000000;
|
||||
const byte REG1_BLANK_MASK = 0b01000000;
|
||||
const byte REG1_IE_MASK = 0b00100000;
|
||||
const byte REG1_M1M2_MASK = 0b00011000;
|
||||
const byte REG1_UNUSED_MASK = 0b00000100;
|
||||
const byte REG1_SIZE_MASK = 0b00000010;
|
||||
const byte REG1_MAG_MASK = 0b00000001;
|
||||
|
||||
// TMS9918 color palette
|
||||
const byte COLOR_TRANSPARENT = 0x0;
|
||||
const byte COLOR_BLACK = 0x1;
|
||||
const byte COLOR_MEDIUM_GREEN = 0x2;
|
||||
const byte COLOR_LIGHT_GREEN = 0x3;
|
||||
const byte COLOR_DARK_BLUE = 0x4;
|
||||
const byte COLOR_LIGHT_BLUE = 0x5;
|
||||
const byte COLOR_DARK_RED = 0x6;
|
||||
const byte COLOR_CYAN = 0x7;
|
||||
const byte COLOR_MEDIUM_RED = 0x8;
|
||||
const byte COLOR_LIGHT_RED = 0x9;
|
||||
const byte COLOR_DARK_YELLOW = 0xA;
|
||||
const byte COLOR_LIGHT_YELLOW = 0xB;
|
||||
const byte COLOR_DARK_GREEN = 0xC;
|
||||
const byte COLOR_MAGENTA = 0xD;
|
||||
const byte COLOR_GREY = 0xE;
|
||||
const byte COLOR_WHITE = 0xF;
|
||||
|
||||
// macro for combining foreground and background into a single byte value
|
||||
#define FG_BG(f,b) (((f)<<4)|(b))
|
||||
|
||||
// status register bits (read only)
|
||||
#define FRAME_BIT(a) ((a) & 0b10000000)
|
||||
#define FIVESPR_BIT(a) ((a) & 0b01000000)
|
||||
#define COLLISION_BIT(a) ((a) & 0b00100000)
|
||||
#define SPRITE_NUM(a) ((a) & 0b00011111)
|
||||
|
||||
// read/write to TMS9918 macros
|
||||
#define TMS_WRITE_CTRL_PORT(a) (*VDP_REG=(byte)(a))
|
||||
#define TMS_WRITE_DATA_PORT(a) (*VDP_DATA=(byte)(a))
|
||||
#define TMS_READ_CTRL_PORT (*VDP_REG);
|
||||
#define TMS_READ_DATA_PORT (*VDP_DATA);
|
||||
|
||||
// sets the VRAM address on the TMS9918 for a write operation
|
||||
void tms_set_vram_write_addr(word addr) {
|
||||
TMS_WRITE_CTRL_PORT(LOBYTE(addr));
|
||||
TMS_WRITE_CTRL_PORT((HIBYTE(addr) & HIADDRESS_MASK)|WRITE_TO_VRAM);
|
||||
}
|
||||
|
||||
// sets the VRAM address on the TMS9918 for a read operation
|
||||
void tms_set_vram_read_addr(word addr) {
|
||||
TMS_WRITE_CTRL_PORT(LOBYTE(addr));
|
||||
TMS_WRITE_CTRL_PORT((HIBYTE(addr) & HIADDRESS_MASK)|READ_FROM_VRAM);
|
||||
}
|
||||
|
||||
// buffer containing the last register values, because TMS registers are write only
|
||||
byte tms_regs_latch[8];
|
||||
|
||||
// writes a value to a TMS9918 register (0-7)
|
||||
void tms_write_reg(byte regnum, byte val) {
|
||||
TMS_WRITE_CTRL_PORT(val);
|
||||
TMS_WRITE_CTRL_PORT((regnum & REGNUM_MASK)|WRITE_TO_REG);
|
||||
tms_regs_latch[regnum] = val; // save value to buffer
|
||||
}
|
||||
|
||||
// sets border color and background for mode 0
|
||||
inline void tms_set_color(byte col) {
|
||||
tms_write_reg(7, col);
|
||||
}
|
||||
|
||||
// initialize all registers from a table
|
||||
void tms_init_regs(byte *table) {
|
||||
for(byte i=0;i<8;i++) {
|
||||
tms_write_reg(i, table[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const byte INTERRUPT_ENABLED = 1;
|
||||
const byte INTERRUPT_DISABLED = 0;
|
||||
|
||||
// sets the interrupt enable bit on register 1
|
||||
void tms_set_interrupt_bit(byte val) {
|
||||
byte regvalue = tms_regs_latch[1] & (~REG1_IE_MASK);
|
||||
if(val) regvalue |= REG1_IE_MASK;
|
||||
tms_write_reg(1, regvalue);
|
||||
}
|
||||
|
||||
const byte BLANK_ON = 0;
|
||||
const byte BLANK_OFF = 1;
|
||||
|
||||
// sets the blank bit on register 1
|
||||
void tms_set_blank(byte val) {
|
||||
byte regvalue = tms_regs_latch[1] & (~REG1_BLANK_MASK);
|
||||
if(val) regvalue |= REG1_BLANK_MASK;
|
||||
tms_write_reg(1, regvalue);
|
||||
}
|
||||
|
||||
// sets the external video input bit on register 0
|
||||
void tms_set_external_video(byte val) {
|
||||
byte regvalue = tms_regs_latch[0] & (~REG0_EXTVID_MASK);
|
||||
if(val) regvalue |= REG0_EXTVID_MASK;
|
||||
tms_write_reg(0, regvalue);
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
byte SCREEN1_TABLE[8] = {
|
||||
0x00, 0xc0, 0x05, 0x80, 0x01, 0x20, 0x00, 0x25
|
||||
};
|
||||
|
||||
// SCREEN 1 VALUES
|
||||
|
||||
// sprite patterns: $0000
|
||||
// pattern table: $0800 (256*8)
|
||||
// sprite attributes: $1000
|
||||
// unused: $1080
|
||||
// name table: $1400 (32*24)
|
||||
// unused: $1800
|
||||
// color table: $2000 (32)
|
||||
// unused $2020-$3FFF
|
||||
|
||||
const word SCREEN1_PATTERN_TABLE = 0x0800;
|
||||
const word SCREEN1_NAME_TABLE = 0x1400;
|
||||
const word SCREEN1_COLOR_TABLE = 0x2000;
|
||||
const word SCREEN1_SPRITE_PATTERNS = 0x0000;
|
||||
const word SCREEN1_SPRITE_ATTRS = 0x1000;
|
||||
const word SCREEN1_SIZE = (32*24);
|
||||
|
||||
// loads the font on the pattern table
|
||||
void screen1_load_font() {
|
||||
|
||||
byte *source = FONT;
|
||||
|
||||
// start writing into VRAM from space character (32..127)
|
||||
tms_set_vram_write_addr(SCREEN1_PATTERN_TABLE+(32*8));
|
||||
for(word i=768;i!=0;i--) {
|
||||
TMS_WRITE_DATA_PORT(*source++);
|
||||
}
|
||||
|
||||
// reverse font (32..127)
|
||||
source = FONT;
|
||||
tms_set_vram_write_addr(SCREEN1_PATTERN_TABLE+((128+32)*8));
|
||||
for(word i=768;i!=0;i--) {
|
||||
TMS_WRITE_DATA_PORT(~(*source++));
|
||||
}
|
||||
}
|
||||
|
||||
// holds the cursor location for the putchar routine
|
||||
word screen1_cursor;
|
||||
|
||||
// prints character to TMS (SCREEN 1 MODE)
|
||||
void screen1_putc(byte c) {
|
||||
tms_set_vram_write_addr(screen1_cursor++);
|
||||
TMS_WRITE_DATA_PORT(c);
|
||||
}
|
||||
|
||||
// prints a 0 terminated string to TMS (SCREEN 1 MODE)
|
||||
void screen1_puts(byte *s) {
|
||||
byte c;
|
||||
while(c=*s++) {
|
||||
screen1_putc(c);
|
||||
}
|
||||
}
|
||||
|
||||
void screen1_home() {
|
||||
screen1_cursor = SCREEN1_NAME_TABLE;
|
||||
}
|
||||
|
||||
void screen1_locate(byte x, byte y) {
|
||||
screen1_cursor = SCREEN1_NAME_TABLE + ((word)y)*32 + x;
|
||||
}
|
||||
|
||||
void screen1_prepare() {
|
||||
// fills name table with spaces (32)
|
||||
tms_set_vram_write_addr(SCREEN1_NAME_TABLE);
|
||||
for(word i=SCREEN1_SIZE;i!=0;i--) {
|
||||
TMS_WRITE_DATA_PORT(32);
|
||||
}
|
||||
|
||||
// fill pattern table with 0
|
||||
tms_set_vram_write_addr(SCREEN1_PATTERN_TABLE);
|
||||
for(word i=256*8;i!=0;i--) {
|
||||
TMS_WRITE_DATA_PORT(0);
|
||||
}
|
||||
|
||||
// fill color table with black on white
|
||||
tms_set_vram_write_addr(SCREEN1_COLOR_TABLE);
|
||||
for(byte i=32;i!=0;i--) {
|
||||
TMS_WRITE_DATA_PORT(FG_BG(COLOR_BLACK,COLOR_WHITE));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
byte SCREEN2_TABLE[8] = {
|
||||
0x02, 0xc0, 0x0e, 0xff, 0x03, 0x76, 0x03, 0x25
|
||||
};
|
||||
|
||||
// SCREEN 2 VALUES
|
||||
|
||||
// pattern table: $0000-$17FF (256*8*3)
|
||||
// sprite patterns: $1800-$19FF
|
||||
// color table: $2000-$27FF (256*8*3)
|
||||
// name table: $3800-$3AFF (32*24 = 256*3 = 768)
|
||||
// sprite attributes: $3B00-$3BFF
|
||||
// unused $3C00-$3FFF
|
||||
//
|
||||
|
||||
const word SCREEN2_PATTERN_TABLE = 0x0000;
|
||||
const word SCREEN2_NAME_TABLE = 0x3800;
|
||||
const word SCREEN2_COLOR_TABLE = 0x2000;
|
||||
const word SCREEN2_SPRITE_PATTERNS = 0x1800;
|
||||
const word SCREEN2_SPRITE_ATTRS = 0x3b00;
|
||||
const word SCREEN2_SIZE = (32*24);
|
||||
|
||||
// prepare the screen 2 to be used as a bitmap
|
||||
void screen2_init_bitmap(byte color) {
|
||||
// erase the first sprite pattern
|
||||
tms_set_vram_write_addr(SCREEN2_SPRITE_PATTERNS); // start writing in the sprite patterns
|
||||
for(byte i=0;i<8;i++) {
|
||||
TMS_WRITE_DATA_PORT(0); NOP;
|
||||
}
|
||||
|
||||
// set all sprite coordinates to 0
|
||||
tms_set_vram_write_addr(SCREEN2_SPRITE_ATTRS); // start writing in the sprite attribute
|
||||
for(byte i=0;i<32;i++) {
|
||||
TMS_WRITE_DATA_PORT(0); NOP; // y coordinate
|
||||
TMS_WRITE_DATA_PORT(0); NOP; // x coordinate
|
||||
TMS_WRITE_DATA_PORT(0); NOP; // name
|
||||
TMS_WRITE_DATA_PORT(i); NOP; // color
|
||||
}
|
||||
|
||||
// fill pattern table with 0 (clear screen)
|
||||
tms_set_vram_write_addr(SCREEN2_PATTERN_TABLE);
|
||||
for(word i=768*8;i!=0;i--) {
|
||||
TMS_WRITE_DATA_PORT(0);
|
||||
NOP;
|
||||
}
|
||||
|
||||
// fill color table with black on white
|
||||
tms_set_vram_write_addr(SCREEN2_COLOR_TABLE);
|
||||
for(word i=768*8;i!=0;i--) {
|
||||
TMS_WRITE_DATA_PORT(color);
|
||||
NOP;
|
||||
}
|
||||
|
||||
// fills name table x3 with increasing numbers
|
||||
tms_set_vram_write_addr(SCREEN2_NAME_TABLE);
|
||||
for(word i=0;i<SCREEN2_SIZE;i++) {
|
||||
TMS_WRITE_DATA_PORT(i & 0xFF);
|
||||
NOP;
|
||||
}
|
||||
}
|
||||
|
||||
void screen2_putc(byte ch, byte x, byte y, byte col) {
|
||||
byte *source = &FONT[(word)(ch-32)*8];
|
||||
word addr = x*8 + y*256;
|
||||
tms_set_vram_write_addr(SCREEN2_PATTERN_TABLE + addr); for(byte i=0;i<8;i++) TMS_WRITE_DATA_PORT(source[i]);
|
||||
tms_set_vram_write_addr(SCREEN2_COLOR_TABLE + addr); for(byte i=0;i<8;i++) TMS_WRITE_DATA_PORT(col);
|
||||
}
|
||||
|
||||
void screen2_puts(byte x, byte y, byte col, char *s) {
|
||||
byte c;
|
||||
while(c=*s++) {
|
||||
screen2_putc(c, x++, y, col);
|
||||
}
|
||||
}
|
||||
|
||||
#define PLOT_MODE_RESET 0
|
||||
#define PLOT_MODE_SET 1
|
||||
#define PLOT_MODE_INVERT 2
|
||||
|
||||
byte SCREEN2_PLOT_MODE = PLOT_MODE_SET;
|
||||
|
||||
void screen2_plot(byte x, byte y) {
|
||||
byte pow2_table_reversed[8] = { 128,64,32,16,8,4,2,1 };
|
||||
word paddr = SCREEN2_PATTERN_TABLE + (word)(x & 0b11111000) + (word)(y & 0b11111000)*32 + y%8;
|
||||
tms_set_vram_read_addr(paddr);
|
||||
byte data = TMS_READ_DATA_PORT;
|
||||
byte mask = pow2_table_reversed[x%8];
|
||||
tms_set_vram_write_addr(paddr);
|
||||
switch(SCREEN2_PLOT_MODE) {
|
||||
case PLOT_MODE_RESET:
|
||||
data &= ~mask;
|
||||
break;
|
||||
case PLOT_MODE_SET:
|
||||
data |= mask;
|
||||
break;
|
||||
case PLOT_MODE_INVERT:
|
||||
data ^= mask;
|
||||
break;
|
||||
}
|
||||
TMS_WRITE_DATA_PORT(data);
|
||||
}
|
||||
|
||||
|
||||
signed int vti_abs(signed int x) {
|
||||
return x < 0 ? -x : x;
|
||||
}
|
||||
|
||||
// http://members.chello.at/~easyfilter/bresenham.html
|
||||
void vti_line(byte _x0, byte _y0, byte _x1, byte _y1) {
|
||||
|
||||
signed int x0 = (signed int) _x0;
|
||||
signed int x1 = (signed int) _x1;
|
||||
signed int y0 = (signed int) _y0;
|
||||
signed int y1 = (signed int) _y1;
|
||||
|
||||
signed int dx = vti_abs(x1-x0);
|
||||
signed int dy = -vti_abs(y1-y0);
|
||||
signed int err = dx+dy; /* error value e_xy */
|
||||
|
||||
bool ix = x0<x1;
|
||||
bool iy = y0<y1;
|
||||
signed int e2;
|
||||
|
||||
for(;;){ /* loop */
|
||||
screen2_plot((byte)x0, (byte)y0);
|
||||
if (x0==x1 && y0==y1) break;
|
||||
e2 = err<<1;//2*err;
|
||||
if (e2 >= dy) { err += dy; if(ix) ++x0; else --x0; } /* e_xy+e_x > 0 */
|
||||
if (e2 <= dx) { err += dx; if(iy) ++y0; else --y0; } /* e_xy+e_y < 0 */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// http://members.chello.at/~easyfilter/bresenham.html
|
||||
void vti_ellipse_rect(byte _x0, byte _y0, byte _x1, byte _y1)
|
||||
{
|
||||
//unsigned int x0,y0,x1,y1;
|
||||
signed int x0 = (signed int) _x0;
|
||||
signed int y0 = (signed int) _y0;
|
||||
signed int x1 = (signed int) _x1;
|
||||
signed int y1 = (signed int) _y1;
|
||||
|
||||
signed int a = vti_abs(x1-x0), b = vti_abs(y1-y0);
|
||||
signed int b1 = b&1; // values of diameter
|
||||
signed int dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment
|
||||
signed int err = dx+dy+b1*a*a, e2; // error of 1.step
|
||||
|
||||
if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points
|
||||
if (y0 > y1) y0 = y1; // .. exchange them
|
||||
y0 += (b+1)/2; y1 = y0-b1; // starting pixel
|
||||
a *= 8*a; b1 = 8*b*b;
|
||||
|
||||
do {
|
||||
screen2_plot((byte) x1, (byte) y0); // I. Quadrant
|
||||
screen2_plot((byte) x0, (byte) y0); // II. Quadrant
|
||||
screen2_plot((byte) x0, (byte) y1); // III. Quadrant
|
||||
screen2_plot((byte) x1, (byte) y1); // IV. Quadrant
|
||||
e2 = 2*err;
|
||||
if (e2 <= dy) { y0++; y1--; err += dy += a; } // y step
|
||||
if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } // x step
|
||||
} while (x0 <= x1);
|
||||
|
||||
while (y0-y1 < b) { // too early stop of flat ellipses a=1
|
||||
screen2_plot((byte) x0-1, (byte) (y0)); // -> finish tip of ellipse
|
||||
screen2_plot((byte) x1+1, (byte) (y0++));
|
||||
screen2_plot((byte) x0-1, (byte) (y1));
|
||||
screen2_plot((byte) x1+1, (byte) (y1--));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#define POKE(a,b) (*((byte *)(a))=(byte)(b))
|
||||
#define PEEK(a) (*((byte *)(a)))
|
||||
|
||||
#define HIBYTE(c) (>(c))
|
||||
#define LOBYTE(c) (<(c))
|
||||
|
||||
#define NOP asm { nop }
|
650
test.c
650
test.c
|
@ -1,650 +0,0 @@
|
|||
// TODO verificare NOPs
|
||||
|
||||
#ifdef APPLE1
|
||||
// APPLE1
|
||||
#pragma start_address(0x4000)
|
||||
const word WOZMON = 0xFF1F; // enters monitor
|
||||
const word ECHO = 0xFFEF; // output ascii character in A (A not destroyed)
|
||||
const word PRBYTE = 0xFFDC; // print hex byte in A (A destroyed)
|
||||
const word KEY_DATA = 0xd010; // read key
|
||||
const word KEY_CTRL = 0xd011; // control port
|
||||
const word TERM_DATA = 0xd012; // write ascii
|
||||
const word TERM_CTRL = 0xd013; // control port
|
||||
|
||||
const byte *VDP_DATA = 0xC000; // TMS9918 data port (VRAM)
|
||||
const byte *VDP_REG = 0xC001; // TMS9918 register port (write) or status (read)
|
||||
#else
|
||||
// VIC20
|
||||
const word ECHO = 0xFFD2; // chrout routine in kernal rom
|
||||
const word GETIN = 0xFFE4; // GETIN keyboard read routine
|
||||
const byte *VDP_DATA = 0xA000; // TMS9918 data port (VRAM)
|
||||
const byte *VDP_REG = 0xA001; // TMS9918 register port (write) or status (read)
|
||||
#endif
|
||||
|
||||
// typedef unsigned char byte;
|
||||
// typedef unsigned int word;
|
||||
|
||||
// TMS9918 interface flags
|
||||
const byte WRITE_TO_REG = 0b10000000;
|
||||
const byte WRITE_TO_VRAM = 0b01000000;
|
||||
const byte READ_FROM_VRAM = 0b00000000;
|
||||
|
||||
#define POKE(a,b) (*((byte *)(a))=(byte)(b))
|
||||
#define PEEK(a) (*((byte *)(a)))
|
||||
|
||||
#define NOP asm { nop }
|
||||
|
||||
#define TMS_WRITE_REG(a) (*VDP_REG=(byte)(a))
|
||||
#define TMS_WRITE_DATA(a) (*VDP_DATA=(byte)(a))
|
||||
#define TMS_READ_REG (*VDP_REG);
|
||||
#define TMS_READ_DATA (*VDP_DATA);
|
||||
|
||||
// status register
|
||||
#define FRAME_BIT(a) ((a) & 0b10000000)
|
||||
#define FIVESPR_BIT(a) ((a) & 0b01000000)
|
||||
#define COLLISION_BIT(a) ((a) & 0b00100000)
|
||||
#define SPRITE_NUM(a) ((a) & 0b00011111)
|
||||
|
||||
// sets the VRAM write address on the TMS9918
|
||||
void set_vram_write_addr(word addr) {
|
||||
TMS_WRITE_REG(<addr);
|
||||
TMS_WRITE_REG((>addr & 0b00111111)|WRITE_TO_VRAM);
|
||||
}
|
||||
|
||||
// sets the VRAM read address on the TMS9918
|
||||
void set_vram_read_addr(word addr) {
|
||||
TMS_WRITE_REG(<addr);
|
||||
TMS_WRITE_REG((>addr & 0b00111111)|READ_FROM_VRAM);
|
||||
}
|
||||
|
||||
// writes a value to a TMS9918 register (0-7)
|
||||
void write_reg(byte regnum, byte val) {
|
||||
TMS_WRITE_REG(val);
|
||||
TMS_WRITE_REG((regnum & 0b00001111)|WRITE_TO_REG);
|
||||
}
|
||||
|
||||
inline void set_color(byte col) {
|
||||
write_reg(7, col);
|
||||
}
|
||||
|
||||
byte SCREEN1_TABLE[8] = {
|
||||
0x00, 0xc0, 0x05, 0x80, 0x01, 0x20, 0x00, 0x25
|
||||
};
|
||||
|
||||
byte SCREEN2_TABLE[8] = {
|
||||
0x02, 0xc0, 0x0e, 0xff, 0x03, 0x76, 0x03, 0x25
|
||||
};
|
||||
|
||||
/*
|
||||
IRQ on the apple1 goes to 0
|
||||
- #pragma zp_reserve(0,1,2)
|
||||
- POKE 0,$4C (JUMP)
|
||||
- DOKE 1,address routine
|
||||
*/
|
||||
|
||||
/*
|
||||
word tick_counts;
|
||||
|
||||
__interrupt(hardware_all) void interrupt_handler() {
|
||||
tick_counts++;
|
||||
}
|
||||
|
||||
// reserve locations for the IRQ vector
|
||||
#pragma zp_reserve(0,1,2)
|
||||
|
||||
//__address(0x0001) volatile word KERNEL_IRQ;
|
||||
|
||||
void install_interrupt() {
|
||||
asm { sei }
|
||||
POKE(0,0x4C);
|
||||
//KERNEL_IRQ = (word) &interrupt_handler;
|
||||
// *((word *)0x0001) = (word) &interrupt_handler;
|
||||
asm { cli }
|
||||
}
|
||||
*/
|
||||
|
||||
word screen1_cursor;
|
||||
|
||||
#include "laser500_font.ascii.c"
|
||||
|
||||
// SCREEN 1 VALUES
|
||||
|
||||
// sprite patterns: $0000
|
||||
// pattern table: $0800 (256*8)
|
||||
// sprite attributes: $1000
|
||||
// unused: $1080
|
||||
// name table: $1400 (32*24)
|
||||
// unused: $1800
|
||||
// color table: $2000 (32)
|
||||
// unused $2020-$3FFF
|
||||
|
||||
const word SCREEN1_PATTERN_TABLE = 0x0800;
|
||||
const word SCREEN1_NAME_TABLE = 0x1400;
|
||||
const word SCREEN1_COLOR_TABLE = 0x2000;
|
||||
const word SCREEN1_SPRITE_PATTERNS = 0x0000;
|
||||
const word SCREEN1_SPRITE_ATTRS = 0x1000;
|
||||
const word SCREEN1_SIZE = (32*24);
|
||||
|
||||
// loads the Laser 500 font on the pattern table
|
||||
void SCREEN1_LOAD_FONT() {
|
||||
|
||||
static byte *source = FONT;
|
||||
static word i;
|
||||
|
||||
// start writing into VRAM from space character (32..127)
|
||||
set_vram_write_addr(SCREEN1_PATTERN_TABLE+(32*8));
|
||||
for(i=768;i!=0;i--) {
|
||||
TMS_WRITE_DATA(*source++);
|
||||
}
|
||||
|
||||
// reverse font (32..127)
|
||||
source = FONT;
|
||||
set_vram_write_addr(SCREEN1_PATTERN_TABLE+((128+32)*8));
|
||||
for(i=768;i!=0;i--) {
|
||||
TMS_WRITE_DATA(~(*source++));
|
||||
}
|
||||
}
|
||||
|
||||
// prints character to TMS (SCREEN 1 MODE)
|
||||
void SCREEN1_PUTCHAR(byte c) {
|
||||
set_vram_write_addr(screen1_cursor++);
|
||||
TMS_WRITE_DATA(c);
|
||||
}
|
||||
|
||||
// prints 0 terminated string pointed by YA
|
||||
void SCREEN1_PUTS(byte *s) {
|
||||
byte c;
|
||||
while(c=*s++) {
|
||||
SCREEN1_PUTCHAR(c);
|
||||
}
|
||||
}
|
||||
|
||||
void SCREEN1_HOME() {
|
||||
screen1_cursor = SCREEN1_NAME_TABLE;
|
||||
}
|
||||
|
||||
void SCREEN1_LOCATEXY(byte x, byte y) {
|
||||
screen1_cursor = SCREEN1_NAME_TABLE + ((word)y)*32 + x;
|
||||
}
|
||||
|
||||
void SCREEN1_FILL() {
|
||||
// fills name table with spaces (32)
|
||||
set_vram_write_addr(SCREEN1_NAME_TABLE);
|
||||
for(word i=SCREEN1_SIZE;i!=0;i--) {
|
||||
TMS_WRITE_DATA(32);
|
||||
}
|
||||
|
||||
// fill pattern table with 0
|
||||
set_vram_write_addr(SCREEN1_PATTERN_TABLE);
|
||||
for(word i=256*8;i!=0;i--) {
|
||||
TMS_WRITE_DATA(0);
|
||||
}
|
||||
|
||||
// fill color table with $1F
|
||||
set_vram_write_addr(SCREEN1_COLOR_TABLE);
|
||||
for(byte i=32;i!=0;i--) {
|
||||
TMS_WRITE_DATA(0x1f);
|
||||
}
|
||||
}
|
||||
|
||||
// SCREEN 2 VALUES
|
||||
|
||||
// pattern table: $0000-$17FF (256*8*3)
|
||||
// sprite patterns: $1800-$19FF
|
||||
// color table: $2000-$27FF (256*8*3)
|
||||
// name table: $3800-$3AFF (32*24 = 256*3 = 768)
|
||||
// sprite attributes: $3B00-$3BFF
|
||||
// unused $3C00-$3FFF
|
||||
//
|
||||
|
||||
const word SCREEN2_PATTERN_TABLE = 0x0000;
|
||||
const word SCREEN2_NAME_TABLE = 0x3800;
|
||||
const word SCREEN2_COLOR_TABLE = 0x2000;
|
||||
const word SCREEN2_SPRITE_PATTERNS = 0x1800;
|
||||
const word SCREEN2_SPRITE_ATTRS = 0x3b00;
|
||||
const word SCREEN2_SIZE = (32*24);
|
||||
|
||||
|
||||
void SCREEN_INIT(byte *table) {
|
||||
for(byte i=0;i<8;i++) {
|
||||
write_reg(i, table[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void SCREEN2_FILL() {
|
||||
// fills name table x3 with increasing numbers
|
||||
set_vram_write_addr(SCREEN2_NAME_TABLE);
|
||||
for(word i=0;i<SCREEN2_SIZE;i++) {
|
||||
TMS_WRITE_DATA(i & 0xFF);
|
||||
}
|
||||
|
||||
// fill pattern table with 0 (clear screen)
|
||||
set_vram_write_addr(SCREEN2_PATTERN_TABLE);
|
||||
for(word i=768*8;i!=0;i--) {
|
||||
TMS_WRITE_DATA(0);
|
||||
}
|
||||
|
||||
// fill color table with $1F
|
||||
set_vram_write_addr(SCREEN2_COLOR_TABLE);
|
||||
for(word i=768*8;i!=0;i--) {
|
||||
TMS_WRITE_DATA(0x1f);
|
||||
}
|
||||
}
|
||||
|
||||
void SCREEN2_PUTC(byte ch, byte x, byte y, byte col) {
|
||||
byte *source = &FONT[(word)(ch-32)*8];
|
||||
word addr = x*8 + y*256;
|
||||
set_vram_write_addr(SCREEN2_PATTERN_TABLE + addr); for(byte i=0;i<8;i++) TMS_WRITE_DATA(source[i]);
|
||||
set_vram_write_addr(SCREEN2_COLOR_TABLE + addr); for(byte i=0;i<8;i++) TMS_WRITE_DATA(col);
|
||||
}
|
||||
|
||||
void SCREEN2_PUTS(byte x, byte y, byte col, char *s) {
|
||||
byte c;
|
||||
while(c=*s++) {
|
||||
SCREEN2_PUTC(c, x++, y, col);
|
||||
}
|
||||
}
|
||||
|
||||
#define PLOT_MODE_RESET 0
|
||||
#define PLOT_MODE_SET 1
|
||||
#define PLOT_MODE_INVERT 2
|
||||
|
||||
byte SCREEN2_PLOT_MODE = PLOT_MODE_SET;
|
||||
|
||||
void SCREEN2_PLOT(byte x, byte y) {
|
||||
byte pow2_table_reversed[8] = { 128,64,32,16,8,4,2,1 };
|
||||
word paddr = SCREEN2_PATTERN_TABLE + (word)(x & 0b11111000) + (word)(y & 0b11111000)*32 + y%8;
|
||||
set_vram_read_addr(paddr);
|
||||
byte data = TMS_READ_DATA;
|
||||
byte mask = pow2_table_reversed[x%8];
|
||||
set_vram_write_addr(paddr);
|
||||
switch(SCREEN2_PLOT_MODE) {
|
||||
case PLOT_MODE_RESET:
|
||||
data &= ~mask;
|
||||
break;
|
||||
case PLOT_MODE_SET:
|
||||
data |= mask;
|
||||
break;
|
||||
case PLOT_MODE_INVERT:
|
||||
data ^= mask;
|
||||
break;
|
||||
}
|
||||
TMS_WRITE_DATA(data);
|
||||
}
|
||||
|
||||
void screen1_square_sprites() {
|
||||
// fills first sprite pattern with 255
|
||||
set_vram_write_addr(SCREEN1_SPRITE_PATTERNS); // start writing in the sprite patterns
|
||||
for(byte i=0;i<8;i++) {
|
||||
TMS_WRITE_DATA(255);
|
||||
}
|
||||
|
||||
// set sprite coordinates
|
||||
set_vram_write_addr(SCREEN1_SPRITE_ATTRS); // start writing in the sprite attribute
|
||||
for(byte i=0;i<32;i++) {
|
||||
TMS_WRITE_DATA((6+i)*8); NOP; NOP; NOP; NOP; // y coordinate
|
||||
TMS_WRITE_DATA((6+i)*8); NOP; NOP; NOP; NOP; // x coordinate
|
||||
TMS_WRITE_DATA(0); NOP; NOP; NOP; NOP; // name
|
||||
TMS_WRITE_DATA(i); NOP; NOP; NOP; NOP; // color
|
||||
}
|
||||
}
|
||||
|
||||
void screen2_square_sprites() {
|
||||
// fills first sprite pattern with 255
|
||||
set_vram_write_addr(SCREEN2_SPRITE_PATTERNS); // start writing in the sprite patterns
|
||||
for(byte i=0;i<8;i++) {
|
||||
TMS_WRITE_DATA(0);
|
||||
}
|
||||
|
||||
// set sprite coordinates
|
||||
set_vram_write_addr(SCREEN2_SPRITE_ATTRS); // start writing in the sprite attribute
|
||||
for(byte i=0;i<32;i++) {
|
||||
TMS_WRITE_DATA(0); NOP; NOP; NOP; NOP; // y coordinate
|
||||
TMS_WRITE_DATA(0); NOP; NOP; NOP; NOP; // x coordinate
|
||||
TMS_WRITE_DATA(0); NOP; NOP; NOP; NOP; // name
|
||||
TMS_WRITE_DATA(i); NOP; NOP; NOP; NOP; // color
|
||||
}
|
||||
}
|
||||
|
||||
void prova_screen1() {
|
||||
SCREEN_INIT(SCREEN1_TABLE);
|
||||
SCREEN1_FILL();
|
||||
SCREEN1_LOAD_FONT();
|
||||
|
||||
SCREEN1_HOME(); SCREEN1_PUTS("*** P-LAB VIDEO CARD SYSTEM ***");
|
||||
SCREEN1_LOCATEXY(0, 2); SCREEN1_PUTS("16K VRAM BYTES FREE");
|
||||
SCREEN1_LOCATEXY(0, 4); SCREEN1_PUTS("READY.");
|
||||
|
||||
SCREEN1_LOCATEXY(0, 10);
|
||||
for(word i=0;i<256;i++) SCREEN1_PUTCHAR((byte)i);
|
||||
|
||||
screen1_square_sprites();
|
||||
}
|
||||
|
||||
byte amiga_data[612] = {
|
||||
107,35,130,35,
|
||||
130,35,130,57,
|
||||
130,57,185,57,
|
||||
185,57,185,35,
|
||||
185,35,197,35,
|
||||
197,35,208,46,
|
||||
208,46,208,118,
|
||||
208,118,199,118,
|
||||
199,118,199,80,
|
||||
199,80,136,80,
|
||||
136,80,134,78,
|
||||
134,78,125,78,
|
||||
125,78,124,80,
|
||||
124,80,117,80,
|
||||
117,80,117,89,
|
||||
117,89,107,96,
|
||||
107,96,107,35,
|
||||
105,96,105,34,
|
||||
105,34,198,34,
|
||||
198,34,210,46,
|
||||
210,46,210,119,
|
||||
210,119,158,119,
|
||||
158,119,158,124,
|
||||
158,124,154,132,
|
||||
154,132,150,137,
|
||||
150,137,146,140,
|
||||
146,140,140,149,
|
||||
140,149,132,153,
|
||||
132,153,132,164,
|
||||
132,164,82,164,
|
||||
82,164,82,133,
|
||||
82,133,81,133,
|
||||
81,133,81,105,
|
||||
81,105,85,99,
|
||||
85,99,92,93,
|
||||
92,93,93,89,
|
||||
93,89,97,82,
|
||||
97,82,102,79,
|
||||
102,79,105,78,
|
||||
105,78,105,78,
|
||||
105,78,102,80,
|
||||
102,80,98,82,
|
||||
98,82,94,89,
|
||||
94,89,93,93,
|
||||
93,93,86,99,
|
||||
86,99,82,105,
|
||||
82,105,82,132,
|
||||
82,132,83,133,
|
||||
83,133,83,163,
|
||||
83,163,112,163,
|
||||
112,163,112,142,
|
||||
112,142,120,142,
|
||||
120,142,124,138,
|
||||
124,138,124,119,
|
||||
124,119,122,116,
|
||||
122,116,122,106,
|
||||
122,106,137,93,
|
||||
137,93,137,81,
|
||||
137,81,134,79,
|
||||
134,79,131,79,
|
||||
131,79,134,82,
|
||||
134,82,134,89,
|
||||
134,89,133,90,
|
||||
133,90,126,90,
|
||||
126,90,123,87,
|
||||
123,87,114,94,
|
||||
114,94,102,100,
|
||||
102,100,90,109,
|
||||
90,109,90,108,
|
||||
90,108,102,99,
|
||||
102,99,98,98,
|
||||
98,98,95,95,
|
||||
95,95,98,97,
|
||||
98,97,102,98,
|
||||
102,98,105,96,
|
||||
122,85,124,78,
|
||||
124,78,132,79,
|
||||
132,79,133,82,
|
||||
133,82,133,89,
|
||||
133,89,126,89,
|
||||
126,89,122,85,
|
||||
123,116,123,106,
|
||||
123,106,138,93,
|
||||
138,93,138,81,
|
||||
138,81,197,81,
|
||||
197,81,197,118,
|
||||
197,118,124,118,
|
||||
124,118,123,114,
|
||||
132,35,183,35,
|
||||
183,35,183,56,
|
||||
183,56,132,56,
|
||||
132,56,132,35,
|
||||
168,38,179,38,
|
||||
179,38,179,52,
|
||||
179,52,168,52,
|
||||
168,52,168,38,
|
||||
170,39,177,39,
|
||||
177,39,177,51,
|
||||
177,51,170,51,
|
||||
170,51,170,39,
|
||||
119,81,122,81,
|
||||
122,81,122,83,
|
||||
122,83,119,87,
|
||||
119,87,119,81,
|
||||
113,163,113,143,
|
||||
113,143,120,143,
|
||||
120,143,125,138,
|
||||
125,138,125,129,
|
||||
125,129,129,131,
|
||||
129,131,137,131,
|
||||
137,131,137,133,
|
||||
137,133,141,138,
|
||||
141,138,145,138,
|
||||
145,138,145,140,
|
||||
145,140,139,149,
|
||||
139,149,131,152,
|
||||
131,152,131,163,
|
||||
131,163,113,163,
|
||||
125,119,125,124,
|
||||
125,124,130,121,
|
||||
130,121,125,119,
|
||||
129,122,125,124,
|
||||
125,124,125,119,
|
||||
125,119,146,119,
|
||||
146,119,136,129,
|
||||
136,129,135,129,
|
||||
135,129,137,127,
|
||||
137,127,132,122,
|
||||
132,122,129,122,
|
||||
150,119,157,119,
|
||||
157,119,157,124,
|
||||
157,124,153,132,
|
||||
153,132,150,136,
|
||||
150,136,145,137,
|
||||
145,137,142,137,
|
||||
142,137,139,133,
|
||||
139,133,139,130,
|
||||
139,130,150,119,
|
||||
140,130,140,133,
|
||||
140,133,142,136,
|
||||
142,136,144,136,
|
||||
144,136,148,135,
|
||||
148,135,149,132,
|
||||
149,132,145,129,
|
||||
145,129,140,130,
|
||||
126,128,134,130,
|
||||
134,130,136,127,
|
||||
136,127,132,123,
|
||||
132,123,129,123,
|
||||
129,123,126,125,
|
||||
126,125,126,128,
|
||||
198,35,209,46,
|
||||
209,46,209,118
|
||||
};
|
||||
|
||||
void prova_screen2() {
|
||||
SCREEN_INIT(SCREEN2_TABLE);
|
||||
SCREEN2_FILL();
|
||||
screen2_square_sprites();
|
||||
|
||||
SCREEN2_PUTS(0,0,0x1F,"*** P-LAB VIDEO CARD SYSTEM ***");
|
||||
SCREEN2_PUTS(0,2,0x1F,"16K VRAM BYTES FREE");
|
||||
SCREEN2_PUTS(0,4,0x1F,"READY.");
|
||||
|
||||
for(byte i=0;i<16;i++) {
|
||||
SCREEN2_PUTS(5,(byte)(6+i),(byte)(((15-i)<<4)+i)," SCREEN 2 ");
|
||||
}
|
||||
|
||||
vti_line(18, 45,232,187);
|
||||
vti_line(18,187,232, 45);
|
||||
|
||||
SCREEN2_PLOT_MODE = PLOT_MODE_RESET;
|
||||
|
||||
vti_line(18+5, 45,232+5,187);
|
||||
vti_line(18+5,187,232+5, 45);
|
||||
|
||||
SCREEN2_PLOT_MODE = PLOT_MODE_INVERT;
|
||||
|
||||
vti_line(18+5+5, 45,232+5+5,187);
|
||||
vti_line(18+5+5,187,232+5+5, 45);
|
||||
|
||||
SCREEN2_PLOT_MODE = PLOT_MODE_SET;
|
||||
|
||||
//vti_ellipse_rect(7,9,202,167);
|
||||
}
|
||||
|
||||
void prova_screen3() {
|
||||
SCREEN_INIT(SCREEN2_TABLE);
|
||||
SCREEN2_FILL();
|
||||
screen2_square_sprites();
|
||||
|
||||
SCREEN2_PUTS(0,0,0x1F,"*** P-LAB VIDEO CARD SYSTEM ***");
|
||||
SCREEN2_PUTS(0,2,0x1F,"16K VRAM BYTES FREE");
|
||||
SCREEN2_PUTS(0,4,0x1F,"READY.");
|
||||
|
||||
for(word p=0;p<612;p+=4) {
|
||||
vti_line(amiga_data[p],amiga_data[p+1],amiga_data[p+2],amiga_data[p+3]);
|
||||
}
|
||||
SCREEN2_PUTS(18,12,0x4F,"APPLE1");
|
||||
}
|
||||
|
||||
// puts a character on the apple1 screen using the WOZMON routine
|
||||
void woz_putc(byte c) {
|
||||
asm {
|
||||
lda c
|
||||
jsr ECHO
|
||||
}
|
||||
}
|
||||
|
||||
// returns to WOZMON prompt
|
||||
void woz_mon() {
|
||||
#ifdef APPLE1
|
||||
asm {
|
||||
jmp WOZMON
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// reads a key from the apple-1 keyboard
|
||||
byte woz_getkey() {
|
||||
#ifdef APPLE1
|
||||
asm {
|
||||
__wait:
|
||||
lda KEY_CTRL
|
||||
bpl __wait
|
||||
}
|
||||
return PEEK(KEY_DATA) & 0x7f;
|
||||
#else
|
||||
byte key;
|
||||
byte const *keyptr = &key;
|
||||
kickasm(uses keyptr, uses GETIN) {{
|
||||
__wait:
|
||||
jsr GETIN
|
||||
cmp #0
|
||||
beq __wait
|
||||
sta keyptr
|
||||
}}
|
||||
return key;
|
||||
#endif
|
||||
}
|
||||
|
||||
signed int vti_abs(signed int x) {
|
||||
return x < 0 ? -x : x;
|
||||
}
|
||||
|
||||
// http://members.chello.at/~easyfilter/bresenham.html
|
||||
void vti_line(byte _x0, byte _y0, byte _x1, byte _y1) {
|
||||
|
||||
signed int x0 = (signed int) _x0;
|
||||
signed int x1 = (signed int) _x1;
|
||||
signed int y0 = (signed int) _y0;
|
||||
signed int y1 = (signed int) _y1;
|
||||
|
||||
signed int dx = vti_abs(x1-x0);
|
||||
signed int dy = -vti_abs(y1-y0);
|
||||
signed int err = dx+dy; /* error value e_xy */
|
||||
|
||||
bool ix = x0<x1;
|
||||
bool iy = y0<y1;
|
||||
signed int e2;
|
||||
|
||||
for(;;){ /* loop */
|
||||
SCREEN2_PLOT((byte)x0, (byte)y0);
|
||||
if (x0==x1 && y0==y1) break;
|
||||
e2 = err<<1;//2*err;
|
||||
if (e2 >= dy) { err += dy; if(ix) ++x0; else --x0; } /* e_xy+e_x > 0 */
|
||||
if (e2 <= dx) { err += dx; if(iy) ++y0; else --y0; } /* e_xy+e_y < 0 */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// http://members.chello.at/~easyfilter/bresenham.html
|
||||
void vti_ellipse_rect(byte _x0, byte _y0, byte _x1, byte _y1)
|
||||
{
|
||||
//unsigned int x0,y0,x1,y1;
|
||||
signed int x0 = (signed int) _x0;
|
||||
signed int y0 = (signed int) _y0;
|
||||
signed int x1 = (signed int) _x1;
|
||||
signed int y1 = (signed int) _y1;
|
||||
|
||||
signed int a = vti_abs(x1-x0), b = vti_abs(y1-y0);
|
||||
signed int b1 = b&1; // values of diameter
|
||||
signed int dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment
|
||||
signed int err = dx+dy+b1*a*a, e2; // error of 1.step
|
||||
|
||||
if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points
|
||||
if (y0 > y1) y0 = y1; // .. exchange them
|
||||
y0 += (b+1)/2; y1 = y0-b1; // starting pixel
|
||||
a *= 8*a; b1 = 8*b*b;
|
||||
|
||||
do {
|
||||
SCREEN2_PLOT((byte) x1, (byte) y0); // I. Quadrant
|
||||
SCREEN2_PLOT((byte) x0, (byte) y0); // II. Quadrant
|
||||
SCREEN2_PLOT((byte) x0, (byte) y1); // III. Quadrant
|
||||
SCREEN2_PLOT((byte) x1, (byte) y1); // IV. Quadrant
|
||||
e2 = 2*err;
|
||||
if (e2 <= dy) { y0++; y1--; err += dy += a; } // y step
|
||||
if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } // x step
|
||||
} while (x0 <= x1);
|
||||
|
||||
while (y0-y1 < b) { // too early stop of flat ellipses a=1
|
||||
SCREEN2_PLOT((byte) x0-1, (byte) (y0)); // -> finish tip of ellipse
|
||||
SCREEN2_PLOT((byte) x1+1, (byte) (y0++));
|
||||
SCREEN2_PLOT((byte) x0-1, (byte) (y1));
|
||||
SCREEN2_PLOT((byte) x1+1, (byte) (y1--));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void main() {
|
||||
//install_interrupt();
|
||||
|
||||
byte key = '1';
|
||||
for(;;) {
|
||||
if(key == '1') prova_screen1();
|
||||
else if(key == '2') prova_screen2();
|
||||
else if(key == '3') prova_screen3();
|
||||
else if(key == '0') break;
|
||||
else woz_putc(key);
|
||||
|
||||
key = woz_getkey();
|
||||
}
|
||||
woz_putc(42);
|
||||
woz_mon();
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
#ifndef CKBOARD_H
|
||||
#define CKBOARD_H
|
||||
|
||||
// #include <string.h> // for memcpy()
|
||||
|
||||
// #include "sprite.h"
|
||||
// #include "pieces.h"
|
||||
|
||||
#define BCOLS 10 // number of board columns
|
||||
#define BROWS 20 // number of board rows
|
||||
#define EMPTY NUMPIECES // the empty character is the character after all tetrominoes
|
||||
|
||||
// TODO KickC doesn't allow double arrays[][]
|
||||
|
||||
//byte board[32 /*BROWS*/ ][16 /*BCOLS*/]; // the board
|
||||
byte board[16*BROWS];
|
||||
|
||||
#define BOARD_INDEX(x,y) (((int)(y))*16+((int)(x)))
|
||||
#define WRITE_BOARD(x,y,c) board[BOARD_INDEX(x,y)]=(c)
|
||||
#define READ_BOARD(x,y) board[BOARD_INDEX(x,y)]
|
||||
|
||||
// prototypes
|
||||
void ck_init();
|
||||
void ck_drawpiece(sprite *pl);
|
||||
void ck_erasepiece(sprite *pl);
|
||||
int collides(sprite *pl);
|
||||
byte is_line_filled(byte line);
|
||||
void ck_erase_line(byte line);
|
||||
void ck_scroll_down(byte endline);
|
||||
|
||||
// fills the check board with EMPTY
|
||||
void ck_init() {
|
||||
for(byte r=0;r<BROWS;r++) {
|
||||
ck_erase_line(r);
|
||||
}
|
||||
}
|
||||
|
||||
// draw a piece on the check board
|
||||
void ck_drawpiece(sprite *pl) {
|
||||
tile_offset *data = get_piece_offsets(pl->piece, pl->angle);
|
||||
for(byte t=0; t<4; t++) {
|
||||
int x = pl->x; byte x1 = data->offset_x; x+= (int) x1;
|
||||
int y = pl->y; byte y1 = data->offset_y; y+= (int) y1;
|
||||
/*
|
||||
int x = pl->x + d->offset_x;
|
||||
int y = pl->y + d->offset_y;
|
||||
*/
|
||||
WRITE_BOARD(x,y,pl->piece);
|
||||
data++;
|
||||
}
|
||||
}
|
||||
|
||||
// erase a piece from the check board
|
||||
void ck_erasepiece(sprite *pl) {
|
||||
tile_offset *data = get_piece_offsets(pl->piece, pl->angle);
|
||||
for(byte t=0; t<4; t++) {
|
||||
int x = pl->x; byte x1 = data->offset_x; x+= (int) x1;
|
||||
int y = pl->y; byte y1 = data->offset_y; y+= (int) y1;
|
||||
/*
|
||||
int x = pl->x + (int) data->offset_x;
|
||||
int y = pl->y + (int) data->offset_y;
|
||||
*/
|
||||
WRITE_BOARD(x,y,EMPTY);
|
||||
data++;
|
||||
}
|
||||
}
|
||||
|
||||
// returns 1 if the piece collides with something
|
||||
int collides(sprite *pl) {
|
||||
tile_offset *data = get_piece_offsets(pl->piece, pl->angle);
|
||||
for(byte t=0; t<4; t++) {
|
||||
int x = pl->x; byte x1 = data->offset_x; x+= (int) x1;
|
||||
int y = pl->y; byte y1 = data->offset_y; y+= (int) y1;
|
||||
|
||||
//int x = pl->x + (int) data->offset_x;
|
||||
//int y = pl->y + (int) data->offset_y;
|
||||
if(x<0) return 1; // does it collide with left border?
|
||||
if(x>=BCOLS) return 1; // does it collide with right border?
|
||||
if(y>=BROWS) return 1; // does it collide with bottom?
|
||||
if(READ_BOARD(x,y) != EMPTY) return 1; // does it collide with something?
|
||||
data++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// returns 1 if the line is all filled
|
||||
byte is_line_filled(byte line) {
|
||||
for(byte t=0;t<BCOLS;t++) {
|
||||
if(READ_BOARD(t,line)==EMPTY) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// fills the specified line with an empty character
|
||||
void ck_erase_line(byte line) {
|
||||
for(byte t=0; t<BCOLS; t++) {
|
||||
WRITE_BOARD(t,line,EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
// scroll down the board by 1 position from top to specified line
|
||||
void ck_scroll_down(byte endline) {
|
||||
for(byte line=endline;line>0;line--) {
|
||||
for(byte x=0;x<BCOLS;x++) {
|
||||
WRITE_BOARD(x,line,READ_BOARD(x,line-1));
|
||||
}
|
||||
}
|
||||
// clears the top line
|
||||
ck_erase_line(0);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,87 @@
|
|||
// color escape codes that can be used with "print_string"
|
||||
#define BG_TRANSPARENT "\x1c\x00"
|
||||
#define BG_BLACK "\x1c\x01"
|
||||
#define BG_MEDIUM_GREEN "\x1c\x02"
|
||||
#define BG_LIGHT_GREEN "\x1c\x03"
|
||||
#define BG_DARK_BLUE "\x1c\x04"
|
||||
#define BG_LIGHT_BLUE "\x1c\x05"
|
||||
#define BG_DARK_RED "\x1c\x06"
|
||||
#define BG_CYAN "\x1c\x07"
|
||||
#define BG_MEDIUM_RED "\x1c\x08"
|
||||
#define BG_LIGHT_RED "\x1c\x09"
|
||||
#define BG_DARK_YELLOW "\x1c\x0A"
|
||||
#define BG_LIGHT_YELLOW "\x1c\x0B"
|
||||
#define BG_DARK_GREEN "\x1c\x0C"
|
||||
#define BG_MAGENTA "\x1c\x0D"
|
||||
#define BG_GREY "\x1c\x0E"
|
||||
#define BG_WHITE "\x1c\x0F"
|
||||
|
||||
#define FG_TRANSPARENT "\x1b\x00"
|
||||
#define FG_BLACK "\x1b\x10"
|
||||
#define FG_MEDIUM_GREEN "\x1b\x20"
|
||||
#define FG_LIGHT_GREEN "\x1b\x30"
|
||||
#define FG_DARK_BLUE "\x1b\x40"
|
||||
#define FG_LIGHT_BLUE "\x1b\x50"
|
||||
#define FG_DARK_RED "\x1b\x60"
|
||||
#define FG_CYAN "\x1b\x70"
|
||||
#define FG_MEDIUM_RED "\x1b\x80"
|
||||
#define FG_LIGHT_RED "\x1b\x90"
|
||||
#define FG_DARK_YELLOW "\x1b\xA0"
|
||||
#define FG_LIGHT_YELLOW "\x1b\xB0"
|
||||
#define FG_DARK_GREEN "\x1b\xC0"
|
||||
#define FG_MAGENTA "\x1b\xD0"
|
||||
#define FG_GREY "\x1b\xE0"
|
||||
#define FG_WHITE "\x1b\xF0"
|
||||
|
||||
|
||||
void draw_tile(byte x, byte y, byte ch, byte col) {
|
||||
byte *source = &FONTS[(word)ch*8];
|
||||
word addr = x*8 + y*256;
|
||||
tms_set_vram_write_addr(SCREEN2_PATTERN_TABLE + addr); for(byte i=0;i<8;i++) { TMS_WRITE_DATA_PORT(source[i]); NOP; /*NOP;*/ /*NOP; NOP;*/ /*NOP; NOP; NOP; NOP;*/ }
|
||||
tms_set_vram_write_addr(SCREEN2_COLOR_TABLE + addr); for(byte i=0;i<8;i++) { TMS_WRITE_DATA_PORT(col); NOP; /*NOP;*/ /*NOP; NOP;*/ /*NOP; NOP; NOP; NOP;*/ }
|
||||
}
|
||||
|
||||
void draw_string(byte x, byte y, byte *s, byte color) {
|
||||
byte c;
|
||||
while(c=*s++) {
|
||||
draw_tile(x++, y, c, color);
|
||||
}
|
||||
}
|
||||
|
||||
// print string with ESC + color support
|
||||
void print_string(byte x, byte y, byte *s, byte color) {
|
||||
byte c;
|
||||
while(c=*s++) {
|
||||
if(c==0x1b) color = *s++ | (color & 0x0F);
|
||||
else if(c==0x1c) color = *s++ | (color & 0xF0);
|
||||
else {
|
||||
draw_tile(x++, y, c, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
byte screen2_cursor_pos_x;
|
||||
byte screen2_cursor_pos_y;
|
||||
byte screen2_cursor_color;
|
||||
|
||||
void color(byte color) {
|
||||
screen2_cursor_color = color;
|
||||
}
|
||||
|
||||
void locate(byte x, byte y) {
|
||||
screen2_cursor_pos_x = x;
|
||||
screen2_cursor_pos_y = y;
|
||||
}
|
||||
|
||||
void print_string(byte *s) {
|
||||
byte c;
|
||||
while(c=*s++) {
|
||||
draw_tile(screen2_cursor_pos_x++, screen2_cursor_pos_y, c, screen2_cursor_color);
|
||||
if(screen2_cursor_pos_x>=32) {
|
||||
screen2_cursor_pos_x = 0;
|
||||
screen2_cursor_pos_y++;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,768 @@
|
|||
0b11111111,
|
||||
0b10000001,
|
||||
0b10000001,
|
||||
0b10000001,
|
||||
0b10000001,
|
||||
0b10000001,
|
||||
0b10000001,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b10000001,
|
||||
0b10111101,
|
||||
0b10100101,
|
||||
0b10100101,
|
||||
0b10111101,
|
||||
0b10000001,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b10000001,
|
||||
0b10111101,
|
||||
0b10111101,
|
||||
0b10111101,
|
||||
0b10111101,
|
||||
0b10000001,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b10000001,
|
||||
0b10000001,
|
||||
0b10011001,
|
||||
0b10011001,
|
||||
0b10000001,
|
||||
0b10000001,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b10000001,
|
||||
0b10100101,
|
||||
0b10000001,
|
||||
0b10000001,
|
||||
0b10100101,
|
||||
0b10000001,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b11000011,
|
||||
0b10100101,
|
||||
0b10011001,
|
||||
0b10011001,
|
||||
0b10100101,
|
||||
0b11000011,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b00000100,
|
||||
0b00000100,
|
||||
0b00000100,
|
||||
0b11111111,
|
||||
0b01000000,
|
||||
0b01000000,
|
||||
0b01000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b11111111,
|
||||
0b00000000,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00101100,
|
||||
0b00101100,
|
||||
0b00101100,
|
||||
0b00101100,
|
||||
0b00101100,
|
||||
0b00101100,
|
||||
0b00101100,
|
||||
0b00101100,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b11110000,
|
||||
0b00011000,
|
||||
0b11001100,
|
||||
0b11101100,
|
||||
0b00101100,
|
||||
0b00101100,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00001111,
|
||||
0b00010000,
|
||||
0b00100111,
|
||||
0b00101111,
|
||||
0b00101100,
|
||||
0b00101100,
|
||||
0b00101100,
|
||||
0b01101100,
|
||||
0b11001100,
|
||||
0b00011100,
|
||||
0b11111000,
|
||||
0b11110000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00101100,
|
||||
0b00101110,
|
||||
0b00100111,
|
||||
0b00110000,
|
||||
0b00011111,
|
||||
0b00001111,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b11000011,
|
||||
0b11000011,
|
||||
0b11000011,
|
||||
0b11000011,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b10011001,
|
||||
0b10100101,
|
||||
0b11000011,
|
||||
0b11000011,
|
||||
0b10100101,
|
||||
0b10011001,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b10100101,
|
||||
0b11100111,
|
||||
0b10000001,
|
||||
0b10000001,
|
||||
0b11100111,
|
||||
0b10100101,
|
||||
0b11111111,
|
||||
0b10000000,
|
||||
0b10000000,
|
||||
0b10000000,
|
||||
0b10000000,
|
||||
0b10000000,
|
||||
0b10000000,
|
||||
0b10000000,
|
||||
0b11111111,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00011100,
|
||||
0b00011100,
|
||||
0b00011100,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00000000,
|
||||
0b00011000,
|
||||
0b00000000,
|
||||
0b00110110,
|
||||
0b00110110,
|
||||
0b00100100,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00010011,
|
||||
0b00110011,
|
||||
0b01111110,
|
||||
0b00110110,
|
||||
0b00110111,
|
||||
0b01111110,
|
||||
0b00100100,
|
||||
0b00000000,
|
||||
0b00111100,
|
||||
0b01101110,
|
||||
0b01101000,
|
||||
0b00111110,
|
||||
0b00001011,
|
||||
0b01101011,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b00100001,
|
||||
0b01010010,
|
||||
0b00100100,
|
||||
0b00001000,
|
||||
0b00010010,
|
||||
0b00100101,
|
||||
0b01000010,
|
||||
0b00000000,
|
||||
0b00011000,
|
||||
0b00100100,
|
||||
0b00110100,
|
||||
0b00111000,
|
||||
0b01001101,
|
||||
0b01000110,
|
||||
0b00111001,
|
||||
0b00000000,
|
||||
0b00110000,
|
||||
0b00110000,
|
||||
0b00010000,
|
||||
0b00100000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000100,
|
||||
0b00001000,
|
||||
0b00011000,
|
||||
0b00010000,
|
||||
0b00011000,
|
||||
0b00001000,
|
||||
0b00000100,
|
||||
0b00000000,
|
||||
0b00010000,
|
||||
0b00001000,
|
||||
0b00001100,
|
||||
0b00000100,
|
||||
0b00001100,
|
||||
0b00001000,
|
||||
0b00010000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00010010,
|
||||
0b00001100,
|
||||
0b00111111,
|
||||
0b00001100,
|
||||
0b00010010,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00001000,
|
||||
0b00001000,
|
||||
0b00111110,
|
||||
0b00001000,
|
||||
0b00001000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00110000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00110000,
|
||||
0b00110000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000010,
|
||||
0b00000100,
|
||||
0b00001000,
|
||||
0b00010000,
|
||||
0b00100000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00011100,
|
||||
0b00100110,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b00110010,
|
||||
0b00011100,
|
||||
0b00000000,
|
||||
0b00001100,
|
||||
0b00011100,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00111111,
|
||||
0b00000000,
|
||||
0b00111110,
|
||||
0b01100011,
|
||||
0b00000111,
|
||||
0b00011110,
|
||||
0b00111100,
|
||||
0b01110000,
|
||||
0b01111111,
|
||||
0b00000000,
|
||||
0b00111111,
|
||||
0b00000110,
|
||||
0b00001100,
|
||||
0b00011110,
|
||||
0b00000011,
|
||||
0b01100011,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b00001110,
|
||||
0b00011110,
|
||||
0b00110110,
|
||||
0b01100110,
|
||||
0b01111111,
|
||||
0b00000110,
|
||||
0b00000110,
|
||||
0b00000000,
|
||||
0b01111110,
|
||||
0b01100000,
|
||||
0b01111110,
|
||||
0b00000011,
|
||||
0b00000011,
|
||||
0b01100011,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b00011110,
|
||||
0b00110000,
|
||||
0b01100000,
|
||||
0b01111110,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b01111111,
|
||||
0b00000011,
|
||||
0b00000110,
|
||||
0b00001100,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00000000,
|
||||
0b00111100,
|
||||
0b01100010,
|
||||
0b01110010,
|
||||
0b00111100,
|
||||
0b01001111,
|
||||
0b01000011,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b00111110,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b00111111,
|
||||
0b00000011,
|
||||
0b00000110,
|
||||
0b00111100,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00000000,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00000000,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00110000,
|
||||
0b00000000,
|
||||
0b00000110,
|
||||
0b00001100,
|
||||
0b00011000,
|
||||
0b00110000,
|
||||
0b00011000,
|
||||
0b00001100,
|
||||
0b00000110,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00110000,
|
||||
0b00011000,
|
||||
0b00001100,
|
||||
0b00000110,
|
||||
0b00001100,
|
||||
0b00011000,
|
||||
0b00110000,
|
||||
0b00000000,
|
||||
0b00111110,
|
||||
0b01111111,
|
||||
0b01100011,
|
||||
0b00000110,
|
||||
0b00011100,
|
||||
0b00000000,
|
||||
0b00011100,
|
||||
0b00000000,
|
||||
0b00111100,
|
||||
0b01000010,
|
||||
0b10011001,
|
||||
0b10100001,
|
||||
0b10100001,
|
||||
0b10011001,
|
||||
0b01000010,
|
||||
0b00111100,
|
||||
0b00011100,
|
||||
0b00110110,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01111111,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b00000000,
|
||||
0b01111110,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01111110,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01111110,
|
||||
0b00000000,
|
||||
0b00011110,
|
||||
0b00110011,
|
||||
0b01100000,
|
||||
0b01100000,
|
||||
0b01100000,
|
||||
0b00110011,
|
||||
0b00011110,
|
||||
0b00000000,
|
||||
0b01111100,
|
||||
0b01100110,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100110,
|
||||
0b01111100,
|
||||
0b00000000,
|
||||
0b01111111,
|
||||
0b01100000,
|
||||
0b01100000,
|
||||
0b01111110,
|
||||
0b01100000,
|
||||
0b01100000,
|
||||
0b01111111,
|
||||
0b00000000,
|
||||
0b01111111,
|
||||
0b01100000,
|
||||
0b01100000,
|
||||
0b01111110,
|
||||
0b01100000,
|
||||
0b01100000,
|
||||
0b01100000,
|
||||
0b00000000,
|
||||
0b00011111,
|
||||
0b00110000,
|
||||
0b01100000,
|
||||
0b01100111,
|
||||
0b01100011,
|
||||
0b00110011,
|
||||
0b00011111,
|
||||
0b00000000,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01111111,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b00000000,
|
||||
0b00111111,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00111111,
|
||||
0b00000000,
|
||||
0b00000011,
|
||||
0b00000011,
|
||||
0b00000011,
|
||||
0b00000011,
|
||||
0b00000011,
|
||||
0b01100011,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b01100011,
|
||||
0b01100110,
|
||||
0b01101100,
|
||||
0b01111000,
|
||||
0b01111100,
|
||||
0b01101110,
|
||||
0b01100111,
|
||||
0b00000000,
|
||||
0b00110000,
|
||||
0b00110000,
|
||||
0b00110000,
|
||||
0b00110000,
|
||||
0b00110000,
|
||||
0b00110000,
|
||||
0b00111111,
|
||||
0b00000000,
|
||||
0b01100011,
|
||||
0b01110111,
|
||||
0b01111111,
|
||||
0b01101011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b00000000,
|
||||
0b01100011,
|
||||
0b01110011,
|
||||
0b01111011,
|
||||
0b01111111,
|
||||
0b01101111,
|
||||
0b01100111,
|
||||
0b01100011,
|
||||
0b00000000,
|
||||
0b00111110,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b01111110,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01111110,
|
||||
0b01100000,
|
||||
0b01100000,
|
||||
0b00000000,
|
||||
0b00111110,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01101111,
|
||||
0b01100110,
|
||||
0b00111101,
|
||||
0b00000000,
|
||||
0b01111110,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100111,
|
||||
0b01111100,
|
||||
0b01101110,
|
||||
0b01100111,
|
||||
0b00000000,
|
||||
0b00111100,
|
||||
0b01100110,
|
||||
0b01100000,
|
||||
0b00111110,
|
||||
0b00000011,
|
||||
0b01100011,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b00111111,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00000000,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b00111110,
|
||||
0b00000000,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01110111,
|
||||
0b00111110,
|
||||
0b00011100,
|
||||
0b00001000,
|
||||
0b00000000,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01100011,
|
||||
0b01101011,
|
||||
0b01111111,
|
||||
0b01110111,
|
||||
0b01100011,
|
||||
0b00000000,
|
||||
0b01100011,
|
||||
0b01110111,
|
||||
0b00111110,
|
||||
0b00011100,
|
||||
0b00111110,
|
||||
0b01110111,
|
||||
0b01100011,
|
||||
0b00000000,
|
||||
0b00110011,
|
||||
0b00110011,
|
||||
0b00110011,
|
||||
0b00011110,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00001100,
|
||||
0b00000000,
|
||||
0b01111111,
|
||||
0b00000111,
|
||||
0b00001110,
|
||||
0b00011100,
|
||||
0b00111000,
|
||||
0b01110000,
|
||||
0b01111111,
|
||||
0b00000000,
|
||||
0b00011110,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00011110,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00011110,
|
||||
0b00000110,
|
||||
0b00000110,
|
||||
0b00000110,
|
||||
0b00000110,
|
||||
0b00000110,
|
||||
0b00011110,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00011000,
|
||||
0b00111100,
|
||||
0b01111110,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b00000000,
|
||||
0b00010000,
|
||||
0b00110000,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b00110000,
|
||||
0b00010000,
|
||||
0b00000000,
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef FONTS_H
|
||||
#define FONTS_H
|
||||
|
||||
#pragma data_seg(Code)
|
||||
|
||||
unsigned char FONTS[127*8] = {
|
||||
|
||||
#include "fonts.body.h"
|
||||
|
||||
};
|
||||
|
||||
#pragma data_seg(Data)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,246 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#define STARTBOARD_X 11 /* X start position of the board on the screen */
|
||||
#define STARTBOARD_Y 2 /* Y start position of the board on the screen */
|
||||
|
||||
#define BOARD_CHAR_LEFT 6
|
||||
#define BOARD_CHAR_RIGHT 6
|
||||
|
||||
#define CRUNCH_CHAR_1 13
|
||||
#define CRUNCH_COLOR_1 FG_BG(COLOR_BLACK, COLOR_GREY)
|
||||
|
||||
#define CRUNCH_CHAR_2 32
|
||||
#define CRUNCH_COLOR_2 FG_BG(COLOR_BLACK, COLOR_BLACK)
|
||||
|
||||
#define POS_SCORE_X 23
|
||||
#define POS_SCORE_Y 1
|
||||
|
||||
#define POS_LEVEL_X 23
|
||||
#define POS_LEVEL_Y 7
|
||||
|
||||
#define POS_LINES_X 23
|
||||
#define POS_LINES_Y 13
|
||||
|
||||
#define POS_NEXT_X 1
|
||||
#define POS_NEXT_Y 1
|
||||
|
||||
#define NEXT_X (POS_NEXT_X+2)
|
||||
#define NEXT_Y (POS_NEXT_Y+3)
|
||||
|
||||
void updateScore();
|
||||
void drawPlayground();
|
||||
void gameOver();
|
||||
|
||||
void gr_drawpiece(sprite *p);
|
||||
void gr_erasepiece(sprite *p);
|
||||
|
||||
void gr_update_board();
|
||||
void gr_crunch_line(byte line, byte crunch_char, byte crunch_color);
|
||||
|
||||
// grahpic board
|
||||
// #include <stdio.h> // for sprintf
|
||||
// #include <sound.h>
|
||||
|
||||
#include "keyboard_input.h"
|
||||
#include "fonts.h"
|
||||
#include "pieces.h"
|
||||
#include "ckboard.h"
|
||||
|
||||
extern unsigned long score;
|
||||
|
||||
void right_pad_number(unsigned long number) {
|
||||
ultoa(number, tmp, DECIMAL);
|
||||
|
||||
int l = (int) strlen(tmp);
|
||||
int offset = 6-l;
|
||||
|
||||
for(int i=l;i>=0;i--) tmp[i+offset] = tmp[i]; // move to the right
|
||||
for(int i=0;i<offset;i++) tmp[i] = 32; // fill with spaces
|
||||
}
|
||||
|
||||
// update score table
|
||||
char tmp[32];
|
||||
void updateScore() {
|
||||
byte color = FG_BG(COLOR_WHITE,COLOR_BLACK);
|
||||
|
||||
right_pad_number(score);
|
||||
draw_string(POS_SCORE_X+1,POS_SCORE_Y+2,tmp,color);
|
||||
|
||||
right_pad_number((unsigned long)total_lines);
|
||||
draw_string(POS_LINES_X+1,POS_LINES_Y+2,tmp,color);
|
||||
|
||||
right_pad_number((unsigned long)level);
|
||||
draw_string(POS_LEVEL_X+1,POS_LEVEL_Y+2,tmp,color);
|
||||
}
|
||||
|
||||
#define FRAME_VERT 7
|
||||
#define FRAME_HORIZ 8
|
||||
|
||||
#define FRAME_NW_CORNER 9
|
||||
#define FRAME_NE_CORNER 10
|
||||
#define FRAME_SW_CORNER 11
|
||||
#define FRAME_SE_CORNER 12
|
||||
|
||||
void drawFrame(byte x, byte y, byte w, byte h, byte color) {
|
||||
byte i;
|
||||
for (i=1; i<w-1; i++) {
|
||||
draw_tile(x+i, y , FRAME_VERT, color);
|
||||
draw_tile(x+i, y+h-1, FRAME_VERT, color);
|
||||
}
|
||||
for (i=1; i<h-1; i++) {
|
||||
draw_tile(x , y+i, FRAME_HORIZ, color);
|
||||
draw_tile(x+w-1, y+i, FRAME_HORIZ, color);
|
||||
}
|
||||
|
||||
draw_tile(x ,y , FRAME_NE_CORNER, color);
|
||||
draw_tile(x+w-1 ,y , FRAME_NW_CORNER, color);
|
||||
draw_tile(x ,y+h-1, FRAME_SE_CORNER, color);
|
||||
draw_tile(x+w-1 ,y+h-1, FRAME_SW_CORNER, color);
|
||||
}
|
||||
|
||||
void fillFrame(byte x, byte y, byte w, byte h, byte ch, byte color) {
|
||||
byte i,j;
|
||||
for (i=0; i<w; i++) {
|
||||
for (j=0; j<h; j++) {
|
||||
draw_tile(x+i, y+j, ch, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// draws the board
|
||||
void drawPlayground() {
|
||||
tms_set_color(COLOR_DARK_BLUE);
|
||||
byte frame_color = FG_BG(COLOR_GREY,COLOR_BLACK);
|
||||
byte text_color = FG_BG(COLOR_LIGHT_YELLOW,COLOR_BLACK);
|
||||
|
||||
// draw tetris board
|
||||
|
||||
byte bg = FG_BG(COLOR_BLACK, COLOR_DARK_BLUE);
|
||||
|
||||
// tetris frame
|
||||
fillFrame(0,0, 10,24, 16, bg);
|
||||
|
||||
fillFrame(STARTBOARD_X,STARTBOARD_Y,BCOLS,BROWS,32,FG_BG(COLOR_BLACK, COLOR_BLACK));
|
||||
drawFrame(STARTBOARD_X-1,STARTBOARD_Y-1,BCOLS+2,BROWS+2, frame_color);
|
||||
|
||||
fillFrame(22,0,10,24, 16, bg);
|
||||
fillFrame(0, 0,32, 1, 16, bg);
|
||||
fillFrame(0,23,32, 1, 16, bg);
|
||||
|
||||
drawFrame(POS_SCORE_X, POS_SCORE_Y, 8, 4, frame_color);
|
||||
drawFrame(POS_LEVEL_X, POS_LEVEL_Y, 8, 4, frame_color);
|
||||
drawFrame(POS_LINES_X, POS_LINES_Y, 8, 4, frame_color);
|
||||
|
||||
drawFrame(POS_NEXT_X , POS_NEXT_Y , 8, 8, frame_color);
|
||||
fillFrame(POS_NEXT_X+1 , POS_NEXT_Y+1 , 6, 6, 32, FG_BG(COLOR_BLACK, COLOR_BLACK));
|
||||
|
||||
draw_string(POS_SCORE_X+1, POS_SCORE_Y+1, "SCORE ", text_color);
|
||||
draw_string(POS_LEVEL_X+1, POS_LEVEL_Y+1, "LEVEL ", text_color);
|
||||
|
||||
draw_string(POS_LINES_X+1, POS_LINES_Y+1,"LINES ", text_color);
|
||||
draw_string(POS_NEXT_X +1, POS_NEXT_Y +1,"NEXT" , text_color);
|
||||
}
|
||||
|
||||
// displays "game over" and waits for return key
|
||||
void gameOver() {
|
||||
byte color = FG_BG(COLOR_LIGHT_YELLOW, COLOR_BLACK);
|
||||
byte frame_color = FG_BG(COLOR_GREY,COLOR_BLACK);
|
||||
|
||||
byte y = (STARTBOARD_Y+BROWS)/2;
|
||||
|
||||
drawFrame(STARTBOARD_X-2, y-1,14,3, frame_color);
|
||||
|
||||
draw_string(STARTBOARD_X-1,y-0," GAME OVER ", FG_BG(COLOR_LIGHT_YELLOW,COLOR_BLACK));
|
||||
|
||||
// sound effect
|
||||
// ERASED: bit_fx2(7);
|
||||
|
||||
// since it's game over, there's no next piece
|
||||
gr_erasepiece(&piece_preview);
|
||||
|
||||
byte flip = 0;
|
||||
while(1) {
|
||||
flip++;
|
||||
if(test_key(KEY_RETURN)) break;
|
||||
if(flip < 60 ) color = FG_BG(COLOR_LIGHT_YELLOW ,COLOR_BLACK);
|
||||
else if(flip < 120 ) color = FG_BG(COLOR_DARK_BLUE ,COLOR_BLACK);
|
||||
else flip = 0;
|
||||
draw_string(STARTBOARD_X,y-0,"GAME OVER", color);
|
||||
}
|
||||
|
||||
while(test_key(KEY_RETURN));
|
||||
}
|
||||
|
||||
// erase piece from the screen
|
||||
void gr_erasepiece(sprite *p) {
|
||||
tile_offset *data = get_piece_offsets(p->piece, p->angle);
|
||||
int px = p->x;
|
||||
int py = p->y;
|
||||
|
||||
// are we erasing the "next" piece ?
|
||||
if(py==PIECE_IS_NEXT) {
|
||||
px = NEXT_X;
|
||||
py = NEXT_Y;
|
||||
}
|
||||
else {
|
||||
px += STARTBOARD_X;
|
||||
py += STARTBOARD_Y;
|
||||
}
|
||||
|
||||
for(byte t=0; t<4; t++) {
|
||||
int x = px + data->offset_x;
|
||||
int y = py + data->offset_y;
|
||||
data++;
|
||||
draw_tile((byte)x,(byte)y,EMPTY_GR_CHAR,EMPTY_GR_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
// draw a piece on the screen
|
||||
void gr_drawpiece(sprite *p) {
|
||||
tile_offset *data = get_piece_offsets(p->piece, p->angle);
|
||||
int px = p->x;
|
||||
int py = p->y;
|
||||
|
||||
// are we drawing the "next" piece ?
|
||||
if(py==PIECE_IS_NEXT) {
|
||||
px = NEXT_X;
|
||||
py = NEXT_Y;
|
||||
}
|
||||
else {
|
||||
px += STARTBOARD_X;
|
||||
py += STARTBOARD_Y;
|
||||
}
|
||||
|
||||
byte piece = p->piece;
|
||||
for(byte t=0; t<4; t++) {
|
||||
int x = px; byte x1 = data->offset_x; x+= (int) x1;
|
||||
int y = py; byte y1 = data->offset_y; y+= (int) y1;
|
||||
data++;
|
||||
/*
|
||||
int x = px + data[t].offset_x;
|
||||
int y = py + data[t].offset_y;
|
||||
*/
|
||||
byte ch = piece_chars[piece]; //piece_chars[p->piece];
|
||||
byte col = piece_colors[piece]; //piece_colors[p->piece];
|
||||
draw_tile((byte)x,(byte)y,ch,col);
|
||||
}
|
||||
}
|
||||
|
||||
// fills the specified line with an empty character
|
||||
void gr_crunch_line(byte line, byte crunch_char, byte crunch_color) {
|
||||
for(byte i=0; i<BCOLS; i++) {
|
||||
draw_tile(STARTBOARD_X+i, STARTBOARD_Y+line, crunch_char, crunch_color);
|
||||
}
|
||||
}
|
||||
|
||||
void gr_update_board() {
|
||||
byte tile,ch,col;
|
||||
for(byte line=0;line<BROWS;line++) {
|
||||
for(byte column=0;column<BCOLS;column++) {
|
||||
tile = READ_BOARD(column,line);
|
||||
ch = piece_chars[tile];
|
||||
col = piece_colors[tile];
|
||||
draw_tile(STARTBOARD_X+column, STARTBOARD_Y+line, ch, col);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
#include <stdlib.h> // for rand()
|
||||
|
||||
#include "keyboard_input.h"
|
||||
|
||||
#pragma data_seg(Code)
|
||||
|
||||
const byte *logo =
|
||||
// 12345678901234567890123456789012
|
||||
"TTTTT EEEE XXXXX RRRR I SSS "
|
||||
" T E X R R I S S"
|
||||
" T E X R R I S "
|
||||
" T EEE X RRRR I SSS "
|
||||
" T E X R R I S"
|
||||
" T E X R R I S S"
|
||||
" T EEEE X R R I SSS ";
|
||||
|
||||
#pragma data_seg(Data)
|
||||
|
||||
void drawLogo() {
|
||||
byte *s = logo;
|
||||
for(byte r=0;r<7;r++) {
|
||||
for(byte c=0;c<32;c++) {
|
||||
byte tile = 0;
|
||||
switch(*s++) {
|
||||
case 'T': tile = 1; break;
|
||||
case 'E': tile = 2; break;
|
||||
case 'X': tile = 3; break;
|
||||
case 'R': tile = 4; break;
|
||||
case 'I': tile = 5; break;
|
||||
case 'S': tile = 6; break;
|
||||
}
|
||||
if(tile) {
|
||||
byte ch = piece_chars[tile];
|
||||
byte col = piece_colors[tile];
|
||||
draw_tile(c,r+1,ch,col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// introduction screen
|
||||
void introScreen() {
|
||||
tms_set_color(COLOR_BLACK);
|
||||
|
||||
// simulate cls (TODO improve speed)
|
||||
fillFrame(0, 0, 32, 24, 32, FG_BG(COLOR_BLACK, COLOR_BLACK));
|
||||
|
||||
drawLogo();
|
||||
|
||||
print_string(8, 10, " FOR THE APPLE-1", FG_BG(COLOR_WHITE, COLOR_BLACK));
|
||||
print_string(8, 14, " WRITTEN BY ", FG_BG(COLOR_WHITE, COLOR_BLACK));
|
||||
print_string(8, 16, "ANTONINO PORCINO", FG_BG(COLOR_WHITE, COLOR_BLACK));
|
||||
|
||||
print_string(7,21,"USE " BG_DARK_BLUE"I"BG_BLACK" "BG_DARK_BLUE"J"BG_BLACK" "BG_DARK_BLUE"K"BG_BLACK" "BG_DARK_BLUE"L"BG_BLACK" "BG_DARK_BLUE"SPACE"BG_BLACK , FG_BG(COLOR_DARK_YELLOW ,COLOR_BLACK));
|
||||
print_string(5,23,"PRESS " BG_DARK_BLUE "RETURN" BG_BLACK " TO START" , FG_BG(COLOR_DARK_YELLOW ,COLOR_BLACK));
|
||||
|
||||
// wait for key released
|
||||
while(test_key(KEY_RETURN));
|
||||
|
||||
// wait for key press
|
||||
while(!test_key(KEY_RETURN)) {
|
||||
// TODO music
|
||||
rand(); // extract random numbers, making rand() more "random"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef KEYBOARD_H
|
||||
#define KEYBOARD_H
|
||||
|
||||
/* keyboard defititions */
|
||||
#define KEY_LEFT 'J'
|
||||
#define KEY_RIGHT 'L'
|
||||
#define KEY_DOWN 'K'
|
||||
#define KEY_DROP ' '
|
||||
#define KEY_ROTATE 'I'
|
||||
#define KEY_RETURN 0x0d
|
||||
|
||||
byte test_key(byte key);
|
||||
byte read_keyboard();
|
||||
byte player_input();
|
||||
|
||||
// test a specific key
|
||||
byte test_key(byte key) {
|
||||
return read_keyboard() == key ? 1 : 0;
|
||||
}
|
||||
|
||||
// reads the keyboard and return the key pressed
|
||||
byte read_keyboard() {
|
||||
return apple1_readkey();
|
||||
}
|
||||
|
||||
byte player_input() {
|
||||
return read_keyboard();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,2 @@
|
|||
@rem === BUILD TETRIS ===
|
||||
call ..\tools\build tetris
|
|
@ -0,0 +1,590 @@
|
|||
function encode(s) {
|
||||
let b = 0;
|
||||
for(let t=0;t<s.length;t++) {
|
||||
|
||||
if(s.charAt(t) != ".") {
|
||||
b |= 1<<t;
|
||||
}
|
||||
}
|
||||
let msg = `00000000`+b.toString(2);
|
||||
console.log(` 0b${msg.substr(msg.length-8)},`);
|
||||
}
|
||||
|
||||
console.log("// file generated automatically by mkfont.js -- do not edit");
|
||||
|
||||
// char $0a.");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
// char $0aX");
|
||||
encode("...XXX..");
|
||||
encode("...XXX..");
|
||||
encode("...XXX..");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("........");
|
||||
encode("...XX...");
|
||||
encode("........");
|
||||
// char $0a2");
|
||||
encode("..XX.XX.");
|
||||
encode("..XX.XX.");
|
||||
encode("..X..X..");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
// char $0a3");
|
||||
encode("...X..XX");
|
||||
encode("..XX..XX");
|
||||
encode(".XXXXXX.");
|
||||
encode("..XX.XX.");
|
||||
encode("..XX.XXX");
|
||||
encode(".XXXXXX.");
|
||||
encode("..X..X..");
|
||||
encode("........");
|
||||
// char $0a4");
|
||||
encode("..XXXX..");
|
||||
encode(".XX.XXX.");
|
||||
encode(".XX.X...");
|
||||
encode("..XXXXX.");
|
||||
encode("....X.XX");
|
||||
encode(".XX.X.XX");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
// char $0a5");
|
||||
encode("..X....X");
|
||||
encode(".X.X..X.");
|
||||
encode("..X..X..");
|
||||
encode("....X...");
|
||||
encode("...X..X.");
|
||||
encode("..X..X.X");
|
||||
encode(".X....X.");
|
||||
encode("........");
|
||||
// char $0a6");
|
||||
encode("...XX...");
|
||||
encode("..X..X..");
|
||||
encode("..XX.X..");
|
||||
encode("..XXX...");
|
||||
encode(".X..XX.X");
|
||||
encode(".X...XX.");
|
||||
encode("..XXX..X");
|
||||
encode("........");
|
||||
// char $0a7");
|
||||
encode("..XX....");
|
||||
encode("..XX....");
|
||||
encode("...X....");
|
||||
encode("..X.....");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
// char $0a8");
|
||||
encode(".....X..");
|
||||
encode("....X...");
|
||||
encode("...XX...");
|
||||
encode("...X....");
|
||||
encode("...XX...");
|
||||
encode("....X...");
|
||||
encode(".....X..");
|
||||
encode("........");
|
||||
// char $0a9");
|
||||
encode("...X....");
|
||||
encode("....X...");
|
||||
encode("....XX..");
|
||||
encode(".....X..");
|
||||
encode("....XX..");
|
||||
encode("....X...");
|
||||
encode("...X....");
|
||||
encode("........");
|
||||
// char $0aa");
|
||||
encode("........");
|
||||
encode("...X..X.");
|
||||
encode("....XX..");
|
||||
encode("..XXXXXX");
|
||||
encode("....XX..");
|
||||
encode("...X..X.");
|
||||
encode("........");
|
||||
encode("........");
|
||||
// char $0ab");
|
||||
encode("........");
|
||||
encode("....X...");
|
||||
encode("....X...");
|
||||
encode("..XXXXX.");
|
||||
encode("....X...");
|
||||
encode("....X...");
|
||||
encode("........");
|
||||
encode("........");
|
||||
// char $0ac");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("..XX....");
|
||||
encode("........");
|
||||
// char $0ad");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
// char $0ae");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("..XX....");
|
||||
encode("..XX....");
|
||||
encode("........");
|
||||
// char $0af");
|
||||
encode("........");
|
||||
encode("......X.");
|
||||
encode(".....X..");
|
||||
encode("....X...");
|
||||
encode("...X....");
|
||||
encode("..X.....");
|
||||
encode("........");
|
||||
encode("........");
|
||||
// char $0b.");
|
||||
encode("...XXX..");
|
||||
encode("..X..XX.");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode("..XX..X.");
|
||||
encode("...XXX..");
|
||||
encode("........");
|
||||
// char $0bX");
|
||||
encode("....XX..");
|
||||
encode("...XXX..");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("..XXXXXX");
|
||||
encode("........");
|
||||
// char $0b2");
|
||||
encode("..XXXXX.");
|
||||
encode(".XX...XX");
|
||||
encode(".....XXX");
|
||||
encode("...XXXX.");
|
||||
encode("..XXXX..");
|
||||
encode(".XXX....");
|
||||
encode(".XXXXXXX");
|
||||
encode("........");
|
||||
// char $0b3");
|
||||
encode("..XXXXXX");
|
||||
encode(".....XX.");
|
||||
encode("....XX..");
|
||||
encode("...XXXX.");
|
||||
encode("......XX");
|
||||
encode(".XX...XX");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
// char $0b4");
|
||||
encode("....XXX.");
|
||||
encode("...XXXX.");
|
||||
encode("..XX.XX.");
|
||||
encode(".XX..XX.");
|
||||
encode(".XXXXXXX");
|
||||
encode(".....XX.");
|
||||
encode(".....XX.");
|
||||
encode("........");
|
||||
// char $0b5");
|
||||
encode(".XXXXXX.");
|
||||
encode(".XX.....");
|
||||
encode(".XXXXXX.");
|
||||
encode("......XX");
|
||||
encode("......XX");
|
||||
encode(".XX...XX");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
// char $0b6");
|
||||
encode("...XXXX.");
|
||||
encode("..XX....");
|
||||
encode(".XX.....");
|
||||
encode(".XXXXXX.");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
// char $0b7");
|
||||
encode(".XXXXXXX");
|
||||
encode("......XX");
|
||||
encode(".....XX.");
|
||||
encode("....XX..");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("........");
|
||||
// char $0b8");
|
||||
encode("..XXXX..");
|
||||
encode(".XX...X.");
|
||||
encode(".XXX..X.");
|
||||
encode("..XXXX..");
|
||||
encode(".X..XXXX");
|
||||
encode(".X....XX");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
// char $0b9");
|
||||
encode("..XXXXX.");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode("..XXXXXX");
|
||||
encode("......XX");
|
||||
encode(".....XX.");
|
||||
encode("..XXXX..");
|
||||
encode("........");
|
||||
// char $0ba");
|
||||
encode("........");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("........");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("........");
|
||||
encode("........");
|
||||
// char $0bb");
|
||||
encode("........");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("........");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("..XX....");
|
||||
encode("........");
|
||||
// char $0bc");
|
||||
encode(".....XX.");
|
||||
encode("....XX..");
|
||||
encode("...XX...");
|
||||
encode("..XX....");
|
||||
encode("...XX...");
|
||||
encode("....XX..");
|
||||
encode(".....XX.");
|
||||
encode("........");
|
||||
// char $0bd");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
// char $0be");
|
||||
encode("..XX....");
|
||||
encode("...XX...");
|
||||
encode("....XX..");
|
||||
encode(".....XX.");
|
||||
encode("....XX..");
|
||||
encode("...XX...");
|
||||
encode("..XX....");
|
||||
encode("........");
|
||||
// char $0bf");
|
||||
encode("..XXXXX.");
|
||||
encode(".XXXXXXX");
|
||||
encode(".XX...XX");
|
||||
encode(".....XX.");
|
||||
encode("...XXX..");
|
||||
encode("........");
|
||||
encode("...XXX..");
|
||||
encode("........");
|
||||
// char $08.");
|
||||
encode("..XXXX..");
|
||||
encode(".X....X.");
|
||||
encode("X..XX..X");
|
||||
encode("X.X....X");
|
||||
encode("X.X....X");
|
||||
encode("X..XX..X");
|
||||
encode(".X....X.");
|
||||
encode("..XXXX..");
|
||||
// char $08X");
|
||||
encode("...XXX..");
|
||||
encode("..XX.XX.");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XXXXXXX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode("........");
|
||||
// char $082");
|
||||
encode(".XXXXXX.");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XXXXXX.");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XXXXXX.");
|
||||
encode("........");
|
||||
// char $083");
|
||||
encode("...XXXX.");
|
||||
encode("..XX..XX");
|
||||
encode(".XX.....");
|
||||
encode(".XX.....");
|
||||
encode(".XX.....");
|
||||
encode("..XX..XX");
|
||||
encode("...XXXX.");
|
||||
encode("........");
|
||||
// char $084");
|
||||
encode(".XXXXX..");
|
||||
encode(".XX..XX.");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX..XX.");
|
||||
encode(".XXXXX..");
|
||||
encode("........");
|
||||
// char $085");
|
||||
encode(".XXXXXXX");
|
||||
encode(".XX.....");
|
||||
encode(".XX.....");
|
||||
encode(".XXXXXX.");
|
||||
encode(".XX.....");
|
||||
encode(".XX.....");
|
||||
encode(".XXXXXXX");
|
||||
encode("........");
|
||||
// char $086");
|
||||
encode(".XXXXXXX");
|
||||
encode(".XX.....");
|
||||
encode(".XX.....");
|
||||
encode(".XXXXXX.");
|
||||
encode(".XX.....");
|
||||
encode(".XX.....");
|
||||
encode(".XX.....");
|
||||
encode("........");
|
||||
// char $087");
|
||||
encode("...XXXXX");
|
||||
encode("..XX....");
|
||||
encode(".XX.....");
|
||||
encode(".XX..XXX");
|
||||
encode(".XX...XX");
|
||||
encode("..XX..XX");
|
||||
encode("...XXXXX");
|
||||
encode("........");
|
||||
// char $088");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XXXXXXX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode("........");
|
||||
// char $089");
|
||||
encode("..XXXXXX");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("..XXXXXX");
|
||||
encode("........");
|
||||
// char $08a");
|
||||
encode("......XX");
|
||||
encode("......XX");
|
||||
encode("......XX");
|
||||
encode("......XX");
|
||||
encode("......XX");
|
||||
encode(".XX...XX");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
// char $08b");
|
||||
encode(".XX...XX");
|
||||
encode(".XX..XX.");
|
||||
encode(".XX.XX..");
|
||||
encode(".XXXX...");
|
||||
encode(".XXXXX..");
|
||||
encode(".XX.XXX.");
|
||||
encode(".XX..XXX");
|
||||
encode("........");
|
||||
// char $08c");
|
||||
encode("..XX....");
|
||||
encode("..XX....");
|
||||
encode("..XX....");
|
||||
encode("..XX....");
|
||||
encode("..XX....");
|
||||
encode("..XX....");
|
||||
encode("..XXXXXX");
|
||||
encode("........");
|
||||
// char $08d");
|
||||
encode(".XX...XX");
|
||||
encode(".XXX.XXX");
|
||||
encode(".XXXXXXX");
|
||||
encode(".XX.X.XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode("........");
|
||||
// char $08e");
|
||||
encode(".XX...XX");
|
||||
encode(".XXX..XX");
|
||||
encode(".XXXX.XX");
|
||||
encode(".XXXXXXX");
|
||||
encode(".XX.XXXX");
|
||||
encode(".XX..XXX");
|
||||
encode(".XX...XX");
|
||||
encode("........");
|
||||
// char $08f");
|
||||
encode("..XXXXX.");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
// char $09.");
|
||||
encode(".XXXXXX.");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XXXXXX.");
|
||||
encode(".XX.....");
|
||||
encode(".XX.....");
|
||||
encode("........");
|
||||
// char $09X");
|
||||
encode("..XXXXX.");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX.XXXX");
|
||||
encode(".XX..XX.");
|
||||
encode("..XXXX.X");
|
||||
encode("........");
|
||||
// char $092");
|
||||
encode(".XXXXXX.");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX..XXX");
|
||||
encode(".XXXXX..");
|
||||
encode(".XX.XXX.");
|
||||
encode(".XX..XXX");
|
||||
encode("........");
|
||||
// char $093");
|
||||
encode("..XXXX..");
|
||||
encode(".XX..XX.");
|
||||
encode(".XX.....");
|
||||
encode("..XXXXX.");
|
||||
encode("......XX");
|
||||
encode(".XX...XX");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
// char $094");
|
||||
encode("..XXXXXX");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("........");
|
||||
// char $095");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode("..XXXXX.");
|
||||
encode("........");
|
||||
// char $096");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XXX.XXX");
|
||||
encode("..XXXXX.");
|
||||
encode("...XXX..");
|
||||
encode("....X...");
|
||||
encode("........");
|
||||
// char $097");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX...XX");
|
||||
encode(".XX.X.XX");
|
||||
encode(".XXXXXXX");
|
||||
encode(".XXX.XXX");
|
||||
encode(".XX...XX");
|
||||
encode("........");
|
||||
// char $098");
|
||||
encode(".XX...XX");
|
||||
encode(".XXX.XXX");
|
||||
encode("..XXXXX.");
|
||||
encode("...XXX..");
|
||||
encode("..XXXXX.");
|
||||
encode(".XXX.XXX");
|
||||
encode(".XX...XX");
|
||||
encode("........");
|
||||
// char $099");
|
||||
encode("..XX..XX");
|
||||
encode("..XX..XX");
|
||||
encode("..XX..XX");
|
||||
encode("...XXXX.");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("....XX..");
|
||||
encode("........");
|
||||
// char $09a");
|
||||
encode(".XXXXXXX");
|
||||
encode(".....XXX");
|
||||
encode("....XXX.");
|
||||
encode("...XXX..");
|
||||
encode("..XXX...");
|
||||
encode(".XXX....");
|
||||
encode(".XXXXXXX");
|
||||
encode("........");
|
||||
// char $09b");
|
||||
encode("...XXXX.");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("...XXXX.");
|
||||
encode("........");
|
||||
// char $09c");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
encode("........");
|
||||
// char $09d");
|
||||
encode("...XXXX.");
|
||||
encode(".....XX.");
|
||||
encode(".....XX.");
|
||||
encode(".....XX.");
|
||||
encode(".....XX.");
|
||||
encode(".....XX.");
|
||||
encode("...XXXX.");
|
||||
encode("........");
|
||||
// char $09e");
|
||||
encode("........");
|
||||
encode("...XX...");
|
||||
encode("..XXXX..");
|
||||
encode(".XXXXXX.");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
encode("...XX...");
|
||||
// char $09f");
|
||||
encode("........");
|
||||
encode("...X....");
|
||||
encode("..XX....");
|
||||
encode(".XXXXXXX");
|
||||
encode(".XXXXXXX");
|
||||
encode("..XX....");
|
||||
encode("...X....");
|
||||
encode("........");
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef PIECES_H
|
||||
#define PIECES_H
|
||||
|
||||
#define NUMPIECES 7 // number of tetrominos in tetris
|
||||
#define NUMTILES 4 // number of tiles in a piece
|
||||
#define NUMROT 4 // number of rotations of a piece
|
||||
|
||||
// offset of a single tile composing a piece
|
||||
typedef struct {
|
||||
byte offset_x;
|
||||
byte offset_y;
|
||||
} tile_offset;
|
||||
|
||||
tile_offset *get_piece_offsets(byte piece, byte angle);
|
||||
|
||||
#pragma data_seg(Code)
|
||||
|
||||
tile_offset pieces_XY[NUMPIECES*NUMTILES*NUMROT] = {
|
||||
{+1,+1} , {+2,+1} , {+3,+1} , {+1,+2} , // L
|
||||
{+2,+0} , {+2,+1} , {+2,+2} , {+3,+2} ,
|
||||
{+3,+0} , {+1,+1} , {+2,+1} , {+3,+1} ,
|
||||
{+1,+0} , {+2,+0} , {+2,+1} , {+2,+2} ,
|
||||
{+1,+1} , {+2,+1} , {+3,+1} , {+3,+2} , // J
|
||||
{+2,+0} , {+3,+0} , {+2,+1} , {+2,+2} ,
|
||||
{+1,+0} , {+1,+1} , {+2,+1} , {+3,+1} ,
|
||||
{+2,+0} , {+2,+1} , {+1,+2} , {+2,+2} ,
|
||||
{+1,+0} , {+0,+1} , {+1,+1} , {+2,+1} , // T
|
||||
{+1,+0} , {+0,+1} , {+1,+1} , {+1,+2} ,
|
||||
{+0,+1} , {+1,+1} , {+2,+1} , {+1,+2} ,
|
||||
{+1,+0} , {+1,+1} , {+2,+1} , {+1,+2} ,
|
||||
{+0,+1} , {+1,+1} , {+2,+1} , {+3,+1} , // I
|
||||
{+1,+0} , {+1,+1} , {+1,+2} , {+1,+3} ,
|
||||
{+0,+1} , {+1,+1} , {+2,+1} , {+3,+1} ,
|
||||
{+1,+0} , {+1,+1} , {+1,+2} , {+1,+3} ,
|
||||
{+1,+0} , {+2,+0} , {+1,+1} , {+2,+1} , // O
|
||||
{+1,+0} , {+2,+0} , {+1,+1} , {+2,+1} ,
|
||||
{+1,+0} , {+2,+0} , {+1,+1} , {+2,+1} ,
|
||||
{+1,+0} , {+2,+0} , {+1,+1} , {+2,+1} ,
|
||||
{+2,+0} , {+3,+0} , {+1,+1} , {+2,+1} , // S
|
||||
{+1,+0} , {+1,+1} , {+2,+1} , {+2,+2} ,
|
||||
{+2,+0} , {+3,+0} , {+1,+1} , {+2,+1} ,
|
||||
{+1,+0} , {+1,+1} , {+2,+1} , {+2,+2} ,
|
||||
{+1,+0} , {+2,+0} , {+2,+1} , {+3,+1} , // Z
|
||||
{+2,+0} , {+1,+1} , {+2,+1} , {+1,+2} ,
|
||||
{+1,+0} , {+2,+0} , {+2,+1} , {+3,+1} ,
|
||||
{+2,+0} , {+1,+1} , {+2,+1} , {+1,+2}
|
||||
};
|
||||
|
||||
#pragma data_seg(Data)
|
||||
|
||||
// given a piece number and an angle returns the 4 byte "offsets" of the piece
|
||||
tile_offset *get_piece_offsets(byte piece, byte angle) {
|
||||
return &pieces_XY[(piece*sizeof(tile_offset)*4*2)+angle*4];
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef SPRITE_H
|
||||
#define SPRITE_H
|
||||
|
||||
// tetromino sprite
|
||||
typedef struct {
|
||||
int x; // signed integers to allow negative board positions
|
||||
int y; // -1 means piece is preview ("next")
|
||||
byte piece;
|
||||
byte angle;
|
||||
} sprite;
|
||||
|
||||
inline void sprite_copy(sprite *to, sprite *from) {
|
||||
memcpy(to, from, sizeof(sprite));
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,338 @@
|
|||
// TODO-KICKC: NPE when -t VIC20_8K is missing
|
||||
// TODO-KICKC: NPE when array initializer contains unesisting macro
|
||||
// TODO-KICKC: #include "keyboard.h" switches to c64 ?
|
||||
// TODO-KICKC: conflict parameter if prototype has different parameter names
|
||||
// TODO-KICKC: missing fragment: int x = pl->x + data[t].offset_x; oppure int x = pl->x + d->offset_x;
|
||||
|
||||
// TODO: solve division by counter_factor
|
||||
// TODO: interrupt and wait_interrupt()
|
||||
|
||||
//
|
||||
// TETRIS for Apple1 + TMS9918 video card by P-LAB
|
||||
// Written by Antonino Porcino, Dec 2021
|
||||
// nino.porcino@gmail.com
|
||||
//
|
||||
// This tetris version has been derived from my
|
||||
// previous implementations for the Laser 310 and Laser 500
|
||||
//
|
||||
|
||||
// standard libraries
|
||||
#include <string.h> // memset, memcopy (memcopy no longer necessary)
|
||||
#include <stdlib.h> // for sprintf, rand
|
||||
|
||||
#define COUNTER_MAX 3000 // the speed counter at level 0
|
||||
#define COUNTER_FACTOR 8 // speed decrease factor: speed -= speed / factor
|
||||
|
||||
// TODO solve division by counter_factor
|
||||
|
||||
#include "../lib/utils.h"
|
||||
#include "../lib/apple1.h"
|
||||
#include "../lib/tms9918.h"
|
||||
#include "../lib/font8x8.h"
|
||||
#include "../lib/tms_screen1.h"
|
||||
#include "../lib/tms_screen2.h"
|
||||
#include "../lib/interrupt.h"
|
||||
|
||||
#include "draw.h"
|
||||
#include "pieces.h"
|
||||
|
||||
#define EMPTY_GR_CHAR 32
|
||||
#define EMPTY_GR_COLOR FG_BG(COLOR_BLACK, COLOR_BLACK)
|
||||
|
||||
byte piece_chars[NUMPIECES+1] = {
|
||||
0, // L (orange in the original tetris)
|
||||
3, // J
|
||||
2, // T
|
||||
1, // I
|
||||
2, // O
|
||||
1, // S
|
||||
0, // Z
|
||||
EMPTY_GR_CHAR // space
|
||||
};
|
||||
byte piece_colors[NUMPIECES+1] = {
|
||||
FG_BG( COLOR_GREY , COLOR_WHITE ), // L ORANGE
|
||||
FG_BG( COLOR_LIGHT_BLUE , COLOR_DARK_BLUE ), // J VIOLET
|
||||
FG_BG( COLOR_MAGENTA , COLOR_WHITE ), // T LIGHT_MAGENTA
|
||||
FG_BG( COLOR_CYAN , COLOR_LIGHT_BLUE ), // I LIGHT_CYAN
|
||||
FG_BG( COLOR_LIGHT_YELLOW , COLOR_DARK_YELLOW ), // O YELLOW
|
||||
FG_BG( COLOR_LIGHT_GREEN , COLOR_DARK_GREEN ), // S LIGHT_GREEN
|
||||
FG_BG( COLOR_LIGHT_RED , COLOR_DARK_RED ), // Z LIGHT_RED
|
||||
EMPTY_GR_COLOR // empty character
|
||||
};
|
||||
|
||||
void check_crunched_lines();
|
||||
|
||||
#define COLLIDES 1
|
||||
#define NOT_COLLIDES 0
|
||||
|
||||
#define PIECE_IS_NEXT 255 /* tells a piece is not on the board but on the "next" display */
|
||||
|
||||
#include "sprite.h"
|
||||
|
||||
sprite piece_preview; // the "next" piece
|
||||
sprite player; // the piece moved by the player
|
||||
sprite new_pos; // new player position when making a 1-step move
|
||||
|
||||
word drop_counter; // counter used to set the pace
|
||||
word drop_counter_max; // maximum value of the counter
|
||||
|
||||
unsigned long score; // player's score
|
||||
unsigned int level; // level
|
||||
unsigned int lines_remaining; // lines to complete the level
|
||||
unsigned int total_lines; // total number of lines
|
||||
|
||||
// game files
|
||||
#include "pieces.h"
|
||||
#include "ckboard.h"
|
||||
#include "fonts.h"
|
||||
#include "keyboard_input.h"
|
||||
#include "grboard.h"
|
||||
#include "intro.h"
|
||||
|
||||
// simulates rand() % 7, since KickC does not support % 7
|
||||
// extracts a random number % 8 and repeats if it's not below < 7
|
||||
byte rand_modulo_7() {
|
||||
for(;;) {
|
||||
byte p = (byte) rand() % 8;
|
||||
if(p!=7) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate a new piece after the last one can no longer move
|
||||
// piece is taken from the "next" which in turn is generated randomly
|
||||
// returns "COLLIDES" if a new piece can't be generated (the board is full)
|
||||
|
||||
byte generate_new_piece() {
|
||||
// move "next" piece onto the board
|
||||
player.piece = piece_preview.piece;
|
||||
player.angle = piece_preview.angle;
|
||||
player.x = 4;
|
||||
player.y = 0;
|
||||
|
||||
// get a new "next" piece
|
||||
gr_erasepiece(&piece_preview);
|
||||
piece_preview.piece = (byte) rand_modulo_7(); // rand() % NUMPIECES;
|
||||
piece_preview.angle = (byte) rand() % NUMROT;
|
||||
gr_drawpiece(&piece_preview);
|
||||
|
||||
if(collides(&player)) {
|
||||
// new piece can't be drawn => game over
|
||||
return COLLIDES;
|
||||
} else {
|
||||
// does not collide, draw it on the board
|
||||
ck_drawpiece(&player);
|
||||
gr_drawpiece(&player);
|
||||
return NOT_COLLIDES;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_player_input() {
|
||||
byte key = player_input();
|
||||
byte allowed = 0;
|
||||
|
||||
if(key == 0) return;
|
||||
|
||||
ck_erasepiece(&player);
|
||||
|
||||
// calculate the new position
|
||||
sprite_copy(&new_pos, &player);
|
||||
|
||||
if(key == KEY_LEFT) {
|
||||
new_pos.x--;
|
||||
if(!collides(&new_pos)) allowed = 1;
|
||||
}
|
||||
else if(key == KEY_RIGHT) {
|
||||
new_pos.x++;
|
||||
if(!collides(&new_pos)) allowed = 1;
|
||||
}
|
||||
else if(key == KEY_ROTATE) {
|
||||
new_pos.angle = (new_pos.angle + 1) & 3;
|
||||
if(!collides(&new_pos)) allowed = 1;
|
||||
}
|
||||
else if(key == KEY_DOWN) {
|
||||
drop_counter = drop_counter_max;
|
||||
return;
|
||||
}
|
||||
else if(key == KEY_DROP) {
|
||||
// animate the falling piece
|
||||
while(1) {
|
||||
ck_erasepiece(&player);
|
||||
sprite_copy(&new_pos, &player);
|
||||
new_pos.y++;
|
||||
if(collides(&new_pos)) {
|
||||
break;
|
||||
}
|
||||
gr_erasepiece(&player);
|
||||
gr_drawpiece(&new_pos);
|
||||
ck_drawpiece(&new_pos);
|
||||
sprite_copy(&player,&new_pos);
|
||||
}
|
||||
drop_counter=drop_counter_max; // force an automatic drop
|
||||
return;
|
||||
}
|
||||
|
||||
if(allowed == 1) {
|
||||
gr_erasepiece(&player);
|
||||
gr_drawpiece(&new_pos);
|
||||
sprite_copy(&player, &new_pos);
|
||||
}
|
||||
ck_drawpiece(&player);
|
||||
}
|
||||
|
||||
// the main game loop, exits when GAME OVER
|
||||
// if the speed counter reaches its max then the piece is automatically pushed down 1 position
|
||||
// else lets the player move the piece with keyboard/joystick commands
|
||||
void gameLoop() {
|
||||
while(1) {
|
||||
if(drop_counter++==drop_counter_max) {
|
||||
// automatic drop down
|
||||
drop_counter = 0;
|
||||
|
||||
// erase from the check board in order to make the move
|
||||
ck_erasepiece(&player);
|
||||
|
||||
// calculate the new position (1 square down)
|
||||
sprite_copy(&new_pos, &player);
|
||||
new_pos.y++;
|
||||
|
||||
if(collides(&new_pos)) {
|
||||
// collides, redraw it again on the check board
|
||||
ck_drawpiece(&player);
|
||||
// check if lines to be crunched
|
||||
check_crunched_lines();
|
||||
// generate a new piece if possible, otherwise exit to game over
|
||||
if(generate_new_piece()==COLLIDES) return;
|
||||
}
|
||||
else {
|
||||
// automatic drop does not collide, simply draw it
|
||||
gr_erasepiece(&player); // erase and draw are as close as possible
|
||||
gr_drawpiece(&new_pos);
|
||||
ck_drawpiece(&new_pos);
|
||||
sprite_copy(&player, &new_pos); // make player new pos
|
||||
}
|
||||
}
|
||||
else {
|
||||
handle_player_input();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int scores[5] = {0, 40, 100, 300, 1200}; // variable score on number of lines crunched
|
||||
|
||||
byte lines_cruched[BROWS]; // stores which lines have been crunched
|
||||
|
||||
// checks if player has made complete lines and "crunches" them
|
||||
void check_crunched_lines() {
|
||||
byte num_lines_crunched = 0;
|
||||
|
||||
// mark completed lines
|
||||
for(byte line=(BROWS-1);line>0;line--) {
|
||||
byte filled = is_line_filled(line);
|
||||
lines_cruched[line] = filled;
|
||||
if(filled) {
|
||||
ck_erase_line(line);
|
||||
gr_crunch_line(line, CRUNCH_CHAR_1, CRUNCH_COLOR_1);
|
||||
num_lines_crunched++;
|
||||
}
|
||||
}
|
||||
|
||||
if(num_lines_crunched == 0) return;
|
||||
|
||||
// wait 5 frames so the effect is visible
|
||||
for(byte t=1; t<5; t++) {
|
||||
wait_interrupt();
|
||||
}
|
||||
|
||||
// assign score
|
||||
|
||||
// does multiplication by repeat sums, as KickC does not support multiplication
|
||||
unsigned int s=0;
|
||||
for(byte t=0;t<(level+1);t++) s += scores[num_lines_crunched];
|
||||
score += (unsigned long) s;
|
||||
|
||||
// score += scores[num_lines_crunched] * (level+1);
|
||||
lines_remaining -= num_lines_crunched;
|
||||
total_lines += num_lines_crunched;
|
||||
|
||||
// advance level
|
||||
if(lines_remaining <= 0) {
|
||||
level = level + 1;
|
||||
lines_remaining += 10;
|
||||
drop_counter_max -= drop_counter_max/COUNTER_FACTOR;
|
||||
// TODO effect when advancing level?
|
||||
}
|
||||
|
||||
// update score
|
||||
updateScore();
|
||||
|
||||
// // marks the lines crunched with another character
|
||||
for(byte line=0; line<BROWS; line++) {
|
||||
if(lines_cruched[line]) {
|
||||
gr_crunch_line(line, CRUNCH_CHAR_2, CRUNCH_COLOR_2);
|
||||
}
|
||||
}
|
||||
|
||||
// wait 5 frames so the effect is visible
|
||||
for(byte t=1; t<5; t++) {
|
||||
wait_interrupt();
|
||||
}
|
||||
|
||||
// compact the heap of tetrominos, removing empty lines
|
||||
for(byte line=0; line<BROWS; line++) {
|
||||
if(lines_cruched[line]) {
|
||||
ck_scroll_down(line);
|
||||
}
|
||||
}
|
||||
|
||||
// update the screen
|
||||
gr_update_board();
|
||||
|
||||
// sound effect
|
||||
// ERASED: bit_fx3(0);
|
||||
}
|
||||
|
||||
// initializes a new game
|
||||
void initGame() {
|
||||
level = 0;
|
||||
score = 0;
|
||||
total_lines = 0;
|
||||
lines_remaining = 10;
|
||||
drop_counter_max = COUNTER_MAX;
|
||||
ck_init();
|
||||
|
||||
drawPlayground();
|
||||
updateScore();
|
||||
|
||||
piece_preview.x = PIECE_IS_NEXT;
|
||||
piece_preview.y = PIECE_IS_NEXT; // piece is on "next" display
|
||||
|
||||
// generate pieces twice: one for "next" and one for player
|
||||
generate_new_piece();
|
||||
ck_erasepiece(&player);
|
||||
gr_erasepiece(&player);
|
||||
generate_new_piece();
|
||||
}
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
// install the start-of-frame interrupt detection
|
||||
//install_interrupt();
|
||||
|
||||
#ifdef APPLE1
|
||||
apple1_eprom_init();
|
||||
#endif
|
||||
|
||||
// initialize the screen
|
||||
tms_init_regs(SCREEN2_TABLE);
|
||||
screen2_init_bitmap(FG_BG(COLOR_BLACK,COLOR_BLACK));
|
||||
|
||||
while(1) {
|
||||
introScreen();
|
||||
initGame();
|
||||
gameLoop();
|
||||
gameOver();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
@rem === BUILD SCRIPT ===
|
||||
|
||||
@SET TARGET=%1
|
||||
|
||||
@echo ======================== VIC20 ===================================================
|
||||
call kickc -targetdir ..\kickc\ -t VIC20_8K -D=VIC20 %TARGET%.c -o out\%TARGET%_vic20.prg -e
|
||||
copy out\%TARGET%.prg out\%TARGET%_vic20.prg
|
||||
|
||||
@echo ======================== APPLE 1 =================================================
|
||||
call kickc -targetdir ..\kickc\ -t apple1 -D=APPLE1 %TARGET%.c -o out\%TARGET%_apple1.prg -e
|
||||
|
||||
@rem builds the apple1 eprom file
|
||||
call node ..\tools\mkeprom out out\%TARGET%_apple1.bin
|
||||
|
||||
@rem clean up files
|
||||
@del out\apple1_codeseg.bin
|
||||
@del out\apple1_dataseg.bin
|
||||
@del out\*.vs
|
||||
@del out\*.klog
|
||||
@del out\*.vs
|
||||
@del out\*.dbg
|
||||
@del out\%TARGET%.prg
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
const fs = require('fs');
|
||||
function prg2bin(){
|
||||
let prg = fs.readFileSync("tetris_apple1.prg");
|
||||
prg = prg.slice(2);
|
||||
fs.writeFileSync("tetris_apple1.bin",prg);
|
||||
console.log("bin written");
|
||||
}
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
if(process.argv.length < 3) {
|
||||
console.log("usage: node mkeprom.js <inputdir> <outputfile>");
|
||||
console.log(" <inputdir> is the directory containing 'apple1_codeseg.bin' and 'apple1_dataseg.bin'");
|
||||
console.log(" <outputfile> is outfile binary eprom file ($4000-$7FFF)");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
let file_in = process.argv[2];
|
||||
let file_out = process.argv[3];
|
||||
|
||||
let code = fs.readFileSync(`${file_in}/apple1_codeseg.bin`);
|
||||
let data = fs.readFileSync(`${file_in}/apple1_dataseg.bin`);
|
||||
|
||||
let mem = new Uint8Array(65536).fill(0xAA);
|
||||
|
||||
let code_address = 0x4000;
|
||||
let data_address = 0x8000-0x7ff-0x280+1;
|
||||
|
||||
for(let i=0;i<code.length;i++) mem[code_address+i] = code[i];
|
||||
for(let i=0;i<data.length;i++) mem[data_address+i] = data[i];
|
||||
|
||||
let prg = mem.slice(0x4000,0x8000);
|
||||
|
||||
fs.writeFileSync(file_out, prg);
|
||||
|
||||
console.log(`bindump: written to ${file_out}`);
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
const fs = require('fs');
|
||||
|
||||
let prg = fs.readFileSync("test_apple1.prg");
|
||||
|
||||
prg = prg.slice(2);
|
||||
|
||||
fs.writeFileSync("test_apple1.bin",prg);
|
||||
|
||||
console.log("bin written");
|
Loading…
Reference in New Issue