#include "unalf.h"
#include "addrs.h"

extern void chksum_err(void);
extern void truncated_err(void);
extern void stack_underrun(void);
extern void stack_overrun(void);

void uncrunch_file(void) {
	lda_i(0x00);
	sta(L71AF);
	sta(L71B0);
	sta(outbuf_len_l);
	sta(outbuf_len_h);
	sta(L71B6);
	sta(L71B7);
	sta(L718C);
	sta(cksum_l);
	sta(cksum_h);
	lda_i(0x09);
	sta(shift_counter);
	lda_i(0x00);
	sta(L71AD);
	lda_i(0x02);
	sta(L71AE);
	lda_i(0x00);
	sta(stackptr_l);
	lda_i(0x60);
	sta(stackptr_h);
	jsr(setup_io_bufs);
	ldx_i(0x10);
	lda(inbuf_adr_l);
	sta(buf_adr_l);
	lda(inbuf_adr_h);
	sta(buf_adr_h);
	lda(inbuf_len_l);
	sta(buf_len_l);
	lda(inbuf_len_h);
	sta(buf_len_h);
	jsr(L7A19);
	sty(L718C);
	jsr(L79E7);
	lda(buf_len_l);
	ora(buf_len_h);
	bne(L75B1);
	rts();

L75B1:
	jsr(L76D0);
	lda(acc16_h);
	cmp_i(0x01);
	bne(uncrunch_blk);
	lda(acc16_l);
	cmp_i(0x01);
	bne(uncrunch_blk);
	jsr(write_output);
	lda(alf_hdr_cksum_l);
	cmp(cksum_l);
	bne(print_emsg_checksum);
	lda(alf_hdr_cksum_h);
	cmp(cksum_h);
	beq(cksum_ok);
print_emsg_checksum:
	chksum_err(); /* does not exit */
cksum_ok:
	rts();

uncrunch_blk:
	lda(acc16_l);
	cmp_i(0x00);
	bne(L760B);
	lda(acc16_h);
	cmp_i(0x01);
	bne(L760B);
	jsr(init_counters);
	jsr(L76D0);
	lda(acc16_l);
	sta(L717D);
	sta(L7179);
	lda(acc16_h);
	sta(L717E);
	sta(L717A);
	lda(acc16_l);
	sta(L7177);
	sta(L7178);
	jsr(store_outbyte);
	jmp(L75B1);

L760B:
	lda(acc16_l);
	sta(L717D);
	sta(L7175);
	lda(acc16_h);
	sta(L717E);
	sta(L7176);
	lda(acc16_h);
	cmp(L717C);
	bcc(L7641);
	lda(acc16_l);
	cmp(L717B);
	bcc(L7641);
	lda(L7179);
	sta(acc16_l);
	sta(L717D);
	lda(L717A);
	sta(acc16_h);
	sta(L717E);
	lda(L7178);
	sta(acc16_l);
	jsr(push_acc16);

L7641:
	lda(L717E);
	beq(L7670);
	lda(L717D);
	sta(zp_b4);
	lda(L717E);
	sta(zp_b5);
	jsr(L7899);
	ldy_i(0x02);
	lda_ind_y(zp_b0);
	sta(acc16_l);
	jsr(push_acc16);
	ldy_i(0x00);
	lda_ind_y(zp_b0);
	sta(acc16_l);
	sta(L717D);
	iny();
	lda_ind_y(zp_b0);
	sta(acc16_h);
	sta(L717E);
	jmp(L7641);

L7670:
	lda(L717D);
	sta(acc16_l);
	sta(L7178);
	sta(L7177);
	lda(L717E);
	sta(acc16_h);
	jsr(push_acc16);
L7683:
	lda(L71AF);
	ora(L71B0);
	beq(L7694);
	jsr(pop_acc16);
	jsr(store_outbyte);
	jmp(L7683);

L7694:
	jsr(L78C2);
	lda(L7175);
	sta(acc16_l);
	sta(L7179);
	lda(L7176);
	sta(acc16_h);
	sta(L717A);
	lda(L717B);
	sta(zp_b4);
	lda(L717C);
	sta(zp_b5);
	cmp(L71AE);
	bcc(L76CD);
	lda(zp_b4);
	cmp(L71AD);
	bcc(L76CD);
	lda(shift_counter);
	cmp_i(0x0C);
	beq(L76CD);
	inc(shift_counter);
	asl(L71AD);
	rol(L71AE);
L76CD:
	jmp(L75B1);
}

void L76D0(void) {
L76D0:
	lda(L71B6);
	sta(zp_b8);
	lda(L71B7);
	sta(zp_b9);
	ldx_i(0x02);
L76DC:
	lsr(zp_b9);
	ror(zp_b8);
	dex();
	bpl(L76DC);
	lda(zp_b8);
	sta(L71AA);
	lda(zp_b9);
	sta(L71AB);
	lda(inbuf_len_l);
	sec();
	sbc(zp_b8);
	sta(zp_bc);
	lda(inbuf_len_h);
	sbc(zp_b9);
	sta(zp_bd);
	lda(zp_bd);
	bne(L770F);
	lda(zp_bc);
	cmp_i(0x03);
	bcs(L770F);
	ldx(L718C);
	bpl(L771C);
	cmp_i(0x02);
	bcc(L7712);
L770F:
	jmp(L779B);
/* ---------------------------------------------------------------------------- */
L7712:
	truncated_err(); /* exits */
/* ---------------------------------------------------------------------------- */
L771C:
	tay();
	dey();
	ldx(inbuf_adr_l);
	stx(zp_be);
	ldx(inbuf_adr_h);
	stx(zp_bf);
	lda(inbuf_adr_l);
	clc();
	adc(zp_b8);
	sta(zp_b8);
	lda(inbuf_adr_h);
	adc(zp_b9);
	sta(zp_b9);
L7737:
	lda_ind_y(zp_b8);
	sta_ind_y(zp_be);
	dey();
	bpl(L7737);
	lda(inbuf_len_l);
	sec();
	sbc(zp_bc);
	sta(buf_len_l);
	lda(inbuf_len_h);
	sbc_i(0x00);
	sta(buf_len_h);
	lda(inbuf_adr_l);
	clc();
	adc(zp_bc);
	sta(buf_adr_l);
	lda(inbuf_adr_h);
	adc_i(0x00);
	sta(buf_adr_h);
	ldx_i(0x10);
	jsr(L7A19);
	sty(L718C);
	bpl(L7771);
	cpy_i(0x88);
	beq(L7771);
	// jmp(cleanup_and_exit);
	// printf("got here, Y is %d, C is %d\n", Y, CF);
	// exit(0); /* was causing early exit with large (5+MB) files */
/* ---------------------------------------------------------------------------- */
L7771:
	jsr(L79E7);
	lda(L71AA);
	sta(zp_b8);
	lda(L71AB);
	sta(zp_b9);
	ldx_i(0x02);
L7780:
	asl(zp_b8);
	rol(zp_b9);
	dex();
	bpl(L7780);
	lda(L71B6);
	sec();
	sbc(zp_b8);
	sta(L71B6);
	lda(L71B7);
	sbc(zp_b9);
	sta(L71B7);
	jmp(L76D0);
/* ---------------------------------------------------------------------------- */
L779B:
	lda(zp_b8);
	sta(zp_bc);
	clc();
	adc(inbuf_adr_l);
	sta(zp_b8);
	lda(zp_b9);
	sta(zp_bd);
	adc(inbuf_adr_h);
	sta(zp_b9);
	ldy_i(0x00);
	lda(L71B6);
	and_i(0x07);
	bne(L77E1);
	lda_ind_y(zp_b8);
	sta(acc16_h);
	iny();
	lda_ind_y(zp_b8);
	sta(acc16_l);
L77C0:
	lda_i(0x0F);
	sec();
	sbc(shift_counter);
	tax();
L77C7:
	lsr(acc16_h);
	ror(acc16_l);
	dex();
	bpl(L77C7);
	lda(shift_counter);
	clc();
	adc(L71B6);
	sta(L71B6);
	lda_i(0x00);
	adc(L71B7);
	sta(L71B7);
	rts();

/* ---------------------------------------------------------------------------- */
L77E1:
	ldx_i(0x02);
L77E3:
	lda_ind_y(zp_b8);
	sta_abs_x(L7189);
	iny();
	dex();
	bpl(L77E3);
	lda(L71B6);
	and_i(0x07);
	tax();
	dex();
L77F3:
	asl(L7189);
	rol(L718A);
	rol(L718B);
	dex();
	bpl(L77F3);
	lda(L718A);
	sta(acc16_l);
	lda(L718B);
	sta(acc16_h);
	jmp(L77C0);
}

void L7A19(void) {
	lda(alf_hdr_compsize2);
	ora(alf_hdr_compsize3);
	beq(L7A28);
L7A21:
	jsr(readblock);
	// Y = 1; /* CIO would set this */
	jsr(L7A5D);
	rts();
L7A28:
	lda(alf_hdr_compsize1);
	cmp(buf_len_h);
	bcc(L7A40);
	beq(L7A34);
	bcs(L7A21);
L7A34:
	lda(alf_hdr_compsize0);
	cmp(buf_len_l);
	bcc(L7A40);
	beq(L7A40);
	bcs(L7A21);
L7A40:
	lda(alf_hdr_compsize0);
	sta(buf_len_l);
	lda(alf_hdr_compsize1);
	sta(buf_len_h);
	lda(buf_len_l);
	ora(buf_len_h);
	beq(L7A57);
	jsr(readblock);
	// Y = 1; /* CIO would set this */
L7A57:
	ldy_i(0x88);
	jsr(L7A5D);
	rts();
}

void L7A5D(void) {
	lda(alf_hdr_compsize0);
	sec();
	sbc(buf_len_l);
	sta(alf_hdr_compsize0);
	lda(alf_hdr_compsize1);
	sbc(buf_len_h);
	sta(alf_hdr_compsize1);
	lda(alf_hdr_compsize2);
	sbc_i(0x00);
	sta(alf_hdr_compsize2);
	rts();
}

void setup_io_bufs(void) {
	lda(MEMTOP_lo);
	sec();
	sbc_i(0xDC);
	sta(inbuf_len_l);
	lda(MEMTOP_hi);
	sbc_i(0x7F);
	sta(inbuf_len_h);
	lsr(inbuf_len_h);
	ror(inbuf_len_l);
	lda(inbuf_len_h);
	cmp_i(0x1F);
	bcc(L79A0);
	lda_i(0x00);
	sta(inbuf_len_l);
	lda_i(0x1F);
	sta(inbuf_len_h);
L79A0:
	lda_i(0xDC);
	sta(inbuf_adr_l);
	lda_i(0x7F);
	sta(inbuf_adr_h);
	lda_i(0xDC);
	clc();
	adc(inbuf_len_l);
	sta(outbuf_adr_l);
	sta(outbuf_ptr_l);
	lda_i(0x7F);
	adc(inbuf_len_h);
	sta(outbuf_adr_h);
	sta(outbuf_ptr_h);
	rts();
}

void init_counters(void) {
	lda_i(0x09);
	sta(shift_counter);
	lda_i(0x00);
	sta(L71AD);
	lda_i(0x02);
	sta(L71AE);
	lda_i(0x02);
	sta(L717B);
	lda_i(0x01);
	sta(L717C);
	rts();
}

/* save decrunched byte in outbuf, update checksum, write outbuf if full */
void store_outbyte(void) {
	ldy_i(0x00);
	lda(acc16_l);
	sta_ind_y(outbuf_ptr_l);
	clc();
	adc(cksum_l);
	sta(cksum_l);
	lda_i(0x00);
	adc(cksum_h);
	sta(cksum_h);
	inc(outbuf_ptr_l);
	bne(out_ptr_hi_ok);
	inc(outbuf_ptr_h);
out_ptr_hi_ok:
	inc(outbuf_len_l);
	bne(out_len_hi_ok);
	inc(outbuf_len_h);
out_len_hi_ok:
	lda(outbuf_len_h);
	cmp(inbuf_len_h);
	bcc(outbuf_not_full);
	lda(outbuf_len_l);
	cmp(inbuf_len_l);
	bcc(outbuf_not_full);
	lda(outbuf_adr_l);
	sta(buf_adr_l);
	lda(outbuf_adr_h);
	sta(buf_adr_h);
	lda(outbuf_len_l);
	sta(buf_len_l);
	lda(outbuf_len_h);
	sta(buf_len_h);
	ldx_i(0x30);
	jsr(writeblock);
	bpl(init_outbuf); /* writeblock() never sets N flag, so always branch */
	chksum_err(); /* does not exit */
/* ---------------------------------------------------------------------------- */
init_outbuf:
	lda(outbuf_adr_l);
	sta(outbuf_ptr_l);
	lda(outbuf_adr_h);
	sta(outbuf_ptr_h);
	lda_i(0x00);
	sta(outbuf_len_l);
	sta(outbuf_len_h);
outbuf_not_full:
	rts();
}


/* ---------------------------------------------------------------------------- */
/* push 2 byte 'register' to software stack */
void push_acc16(void) {
	ldy_i(0x00);
	lda(acc16_l);
	sta_ind_y(stackptr_l);
	iny();
	lda(acc16_h);
	sta_ind_y(stackptr_l);
	lda(stackptr_l);
	clc();
	adc_i(0x02);
	sta(stackptr_l);
	bcc(L790C);
	inc(stackptr_h);
L790C:
	lda(stackptr_h);
	cmp_i(0x70);
	bcc(L791C);
	stack_overrun(); /* exits! */
/* ---------------------------------------------------------------------------- */
L791C:
	inc(L71AF);
	bne(L7924);
	inc(L71B0);
L7924:
	rts();
}

void pop_acc16(void) {
/* pop 2 byte 'register' from software stack */
	lda(stackptr_l);
	sec();
	sbc_i(0x02);
	sta(stackptr_l);
	lda(stackptr_h);
	sbc_i(0x00);
	sta(stackptr_h);
	ldy_i(0x00);
	lda_ind_y(stackptr_l);
	sta(acc16_l);
	iny();
	lda_ind_y(stackptr_l);
	sta(acc16_h);
	lda(stackptr_h);
	cmp_i(0x60);
	bcs(L796C);
	stack_underrun(); /* exits! */
/* ---------------------------------------------------------------------------- */
L796C:
	lda(L71AF);
	bne(L7974);
	dec(L71B0);
L7974:
	dec(L71AF);
	rts();
}

void L79E7(void) {
	lda(buf_adr_l);
	clc();
	adc(buf_len_l);
	sta(zp_be);
	lda(buf_adr_h);
	adc(buf_len_h);
	sta(zp_bf);
	ldx_i(0x02);
	bne(L7A0A);
L79FC:
	ldy_i(0x00);
	tya();
	sta_ind_y(zp_be);
	inc(zp_be);
	bne(L7A07);
	inc(zp_bf);
L7A07:
	dex();
	bmi(L7A18);
L7A0A:
	lda(zp_bf);
	cmp(outbuf_adr_h);
	bcc(L79FC);
	lda(zp_be);
	cmp(outbuf_adr_l);
	bcc(L79FC);
L7A18:
	rts();
}

void L7899(void) {
	lda(zp_b4);
	sta(zp_b0);
	lda(zp_b5);
	sta(zp_b1);
	asl(zp_b0);
	rol(zp_b1);
	lda(zp_b0);
	clc();
	adc(zp_b4);
	sta(zp_b0);
	lda(zp_b1);
	adc(zp_b5);
	sta(zp_b1);
	lda(zp_b0);
	clc();
	adc(L7181);
	sta(zp_b0);
	lda(zp_b1);
	adc(L7182);
	sta(zp_b1);
	rts();
}

void L78C2(void) {
	lda(L717B);
	sta(zp_b4);
	lda(L717C);
	sta(zp_b5);
	jsr(L7899);
	lda(L7177);
	sta(acc16_l);
	ldy_i(0x02);
	sta_ind_y(zp_b0);
	lda(L7179);
	sta(acc16_l);
	lda(L717A);
	sta(acc16_h);
	ldy_i(0x00);
	lda(acc16_l);
	sta_ind_y(zp_b0);
	iny();
	lda(acc16_h);
	sta_ind_y(zp_b0);
	inc(L717B);
	bne(L78F5);
	inc(L717C);
L78F5:
	rts();
}

void write_output(void) {
	lda(outbuf_len_l);
	ora(outbuf_len_h);
	bne(have_output);
	rts();
/* ---------------------------------------------------------------------------- */
have_output:
	lda(outbuf_adr_l);
	sta(buf_adr_l);
	lda(outbuf_adr_h);
	sta(buf_adr_h);
	lda(outbuf_len_l);
	sta(buf_len_l);
	lda(outbuf_len_h);
	sta(buf_len_h);
	ldx_i(0x30);
	jsr(writeblock);
	rts();
}
