program Scroller2x2;
@use "screen/screen"
@use "system/memory"
var
	// Values that will be printed to the screen
	val : byte;
	/// Pointer to the text
	pText : pointer;
	
	// ms_scroll corresponds to the scroll register
	ms_scroll : byte=7;
	// ms_shift is 0 or 1, corresponding to the current column in the 2x2 character set
	ms_sshift : byte=0;
@export "resources/charsets/charset_16x16.flf" "resources/charsets/charset_16x16.bin" 256
	charset: IncBin("resources/charsets/charset_16x16.bin",$2000);
	// Text to be rendered on-screen
	text : string=("THIS IS A TWO X TWO CHARACTER SCROLLER           ");

	p : ^byte;

// Character that points to a 2x2 block of black areas in the character map
	const clearChar : byte = $a0;
	const useKernal : byte = 0;


	const textColor : byte = green;
	const lineColor : byte = dark_grey;
	const scrollerLineY : byte = 5;
	const scrollerStartRaster : byte = 86;
	const scrollerEndRaster : byte = 110;
// Since textcolor1 sets the $D800 colors, it will be multicolor - 8 - dark blue

procedure MainScreen();

// bottom raster method. Does two things:
//  - draws the lower line
//  - Copies the screen content 1 column to the left when a new line is triggered 

interrupt MainScreenScrollerEnds();
begin
	// Line drawing trick : wait a couple of cycles, then change colors
	startirq(@useKernal);
	wait(6);
	Screen::background:=lineColor;
	Screen::Scrollx(0);
	Screen::WaitNoLines(1);
	wait(9);
	Screen::background:=black;
	// point to next raster 
	RasterIRQ(MainScreen(),130, @useKernal);
	// Copy content 1 to the left
	if (ms_scroll=1) then begin
		Memory::Copy(#Screen::screen0+ 40*scrollerLineY+1, #Screen::screen0+ 40*scrollerLineY, 39);
		Memory::Copy(#Screen::screen0+ 40*scrollerLineY+40+1, #Screen::screen0+ 40*scrollerLineY +40, 39);
	end;
	closeirq();
	
end;

function CalculatePositionInCharset(cc_val:byte):byte;
begin
		// If space then return 
	if (cc_val=key_space) then 
		returnValue(@clearChar);
		
// Somewhat advanced: convert from string char values to 
// the position in the character map. 
	if cc_val>64 then cc_val-=65;
	// If larger than O/P etc
	if cc_val>16 then cc_val+=24;  
	cc_val:=cc_val*2;
	// ms_shift is 0 or 1, corresponding to the current column in the 2x2 text		
	cc_val:=cc_val + ms_sshift;
	returnvalue(cc_val);
end;
/*
	This method is called whenever the scroll register is reset, and a new character has been drawn.
	After the character is drawn, the ms_shift register, scroll register and text pointer needs to be updated!
*/
procedure UpdateTextScroll();
begin
	ms_scroll := 7;
	ms_sshift += 1;
	// if column 2 has been rendered, increase pointer to the next character
	if (ms_sshift=2) then begin
		ms_sshift:=0;
		// Point to next character in the text
		pText+=1;
		// If the text pointer has reached the string terminal value 0, simply reset the text pointer
		if (pText[0]=0) then pText:=#text;
	end;
end;

// Renders the actual text to screen (if the scroll register is <0)
procedure DrawText();
begin
	// same as if scroll<0
	if (ms_scroll>127) then begin
		// Lookup character value
		// Move to y position
		p := #Screen::screen0 + Screen::tab40[scrollerLineY];
		
		val:=CalculatePositionInCharset(pText[0]);
		
		p[39]:=val;
		p[39+Screen::width]:=val+Screen::width;
		// Reset scrolling

		UpdateTextScroll();		
	end;

end;

// Main raster routine
interrupt MainRaster();
begin
	// A line drawing trick: wait some cycles, change background color
	startirq(@useKernal);
	wait(6);
	Screen::background:=lineColor;
	Screen::WaitNoLines(1);
	wait(10);
	// wait some more, return to black! 
	Screen::Background:=black;
	
	// Decrease scroll register by 2 (faster scrolling, less boring)
	ms_scroll:=ms_scroll-2;
	Screen::Scrollx(ms_scroll&7);
	// Update the text	
	DrawText();

	RasterIRQ(MainScreenScrollerEnds(),@scrollerEndRaster, @useKernal);


	closeirq();
end;


interrupt MainScreen();
begin
	startirq(@useKernal);
	RasterIRQ(MainRaster(), @scrollerStartRaster, @useKernal);
	CloseIRQ();

end;
begin
	Screen::background := black;
	Screen::border := black;
	Screen::SetCharsetLocation($2000);
	// Clear screen and color memory
	Screen::Clear(#Screen::screen0, clearChar);
	Screen::Clear(#Screen::color, textColor);

	// Initialize text pointer
	pText:=#text;
	// Hide the 0th and 39th column
	Screen::HideBorders();
	SetMemoryConfig(1,@useKernal,0);
	StartRasterChain(MainScreen(),1,@useKernal);
	Loop();

end.
