NOLIST

;; Vintage Computing Christmas Challenge 2024 (VC3 2024)
;;
;;   Object   : Christmas Present
;;   Category : Main Challenge
;;   System   : Amstrad CPC 6128
;;   Language : Z80 Assembler (WinAPE/Maxam)
;;   Author   : lightforce6128
;;
;; Either run with WinAPE assembler (F3, then F9),
;;
;; or assemble with WinAPE assembler (F3, then Ctrl+F9)
;; optionally clear screen via CLS:LOCATE 1,22
;; and execute via CALL 11529,
;;
;; or assemble with WinAPE assembler (F3, then Ctrl+F9)
;; save via SAVE "present.bin",B,11529,43
;; load via MEMORY 11529:LOAD"present
;; optionally clear screen via CLS:LOCATE 1,22
;; and execute via CALL 11529
;;
;; The program itself does not clear the screen and it does not halt.
;; Both is allowed in the rules for this challenge, although both is
;; not quite user-friendly.


;; This special starting address moves parts of the program
;; to memory locations that allow to make special use of the
;; registers containing pointers to these memory locations.
;; It is checked in an assertion-like statement below.
ORG &2D00

;; Define some constants for OS access.
CHR_LOCATE EQU 31 ;; also requires column and row
LO_SIDE_CALL EQU &10
BASIC_PRINT_STRING EQU &C38B ;; call with RST LO_SIDE_CALL
TXT_CURSOR_POSITION EQU &B726
TXT_OUTPUT EQU &BB5A

;; This entrypoint for execution directly from WinAPE assembler
;; sets some registers as they would be set if program had been
;; called from command line. This setup code is not counted for
;; size and is not saved to disk by the third starting option
;; as described in the header. It is removed from the shortened
;; version of the source code.
RUN winapeAssemblerEntryPoint
winapeAssemblerEntryPoint:
	LD BC,&20FF
	LD DE,&2D09
	LD HL,&0045

;; From here every byte is counted.
binaryStart:

;; This is also the official starting point of this program
;; as reached by the command CALL 11529.
callEntryPoint:

	;; A:00 BC:20FF DE:2D09 HL:0045 IX:BFFE IY:0000 SP:BFF8

	bow:
		;; Define the control string to print the bow.
		;; This is also a short nonsense code snippet
		;; that does not interfere with the rest
		;; of the program. But it sets up a needed
		;; value in register H.
		DEFB CHR_LOCATE, 9, 1, "\O/" ;; RRA : ADD HL,BC : LD BC,&4F5C : CPL
		DEFB 0                       ;; NOP

		;; Check if this is placed at the right position.
		;; Its memory address is later used also for
		;; another purpose.
		bowExpected EQU "-"*256+9
		IF bow-bowExpected
			PRINT "Wrong program location: Should be $bowExpected, but is $bow."
			STOP
		ENDIF

	;; A:FF BC:4F5C DE:2D09 HL:2144 IX:BFFE IY:0000 SP:BFF8

	;; Print the bow. The system call will not alter register HL,
	;; so it can be used below in a second role.
	EX DE,HL : RST LO_SIDE_CALL : DEFW BASIC_PRINT_STRING

	;; A:FF BC:4F5C DE:2144 HL:2D09 IX:BFFE IY:0000 SP:BFF8

	;; Complete setting up of two small, two-element queues,
	;; one with characters, one with step sizes.
	;;     HD:2D21 ("-!")   LE:0901
	LD E,1

	;; First draw all the horizontal lines with dashes,
	;; then the vertical lines with exclamation marks,
	;; and finally the corners with plus signs.
	characterLoop:
		LD B,18
		columnLoop:
			LD C,18
			rowLoop:
				;; Position the cursor. Adjust the row to
				;; keep space for the bow.
				INC C : LD (TXT_CURSOR_POSITION),BC : DEC C
				;; Print the character stored in the front
				;; element of queue HD.
				LD A,H : CALL TXT_OUTPUT
			;; Use the front element of queue LE as row step size.
			LD A,C : SUB A,L : LD C,A
			JR NC,rowLoop
		;; Use the second element of queue LE as column step size.
		LD A,B : SUB A,E : LD B,A
		JR NC,columnLoop
	;; Shift both queues to update character and step sizes.
	;; Fill up the queues with character "+" and step size 9.
	;;     initial          : HD:2D21 ("-!")   LE:0901
	;;     first iteration  : HD:212B ("!+")   LE:0109
	;;     second iteration : HD:2B2B ("++")   LE:0909
	;;     no change afterwards
	EX DE,HL : LD DE,"+"*256+9
	JR characterLoop

;; Here counting of bytes end.
binaryEnd:

;; Print out size and commands to save, load, and start program.
IF1
	binaryLength EQU binaryEnd - binaryStart
	PRINT
	PRINT 'address range: &binaryStart - &binaryEnd (&binaryLength bytes)'
	PRINT
	PRINT 'save with: SAVE"present.bin",B,&binaryStart,&binaryLength'
	PRINT 'load with: MEMORY &binaryStart:LOAD"present"'
	PRINT 'optionally clear screen: CLS:LOCATE 1,22'
	PRINT 'start with: CALL &callEntryPoint'
ENDIF
