News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_Prodatron

The Vintage Computing Christmas Challenge 2024

Started by Prodatron, 00:57, 03 December 24

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

arnolde

Wow, this is so amazing!

Kudos to Overflow for taking advantage of "!"-1=" ", for the clever way of putting the bow in a buffer and display it at every row and of course for all the opcode trickery... "Z80A Cathedral" is a very appropriate description.

Congrats to Longshot for using mode 0 in order to do newline by only displaying a space (while one might argue that including the mode instruction in the basic stub is a little bit of cheating); for finding the string display routine in ROM (I also had approaches that filled a text buffer but the output loop just made the code too big... and to elegantly realize the 1-8 pattern with a 9-bitplane.

Bravo to lightforce6128, very elegant, I have to study it in depth later.

I am quite humbled. The only thing I still am proud about is my discovery that #2120 ("!-") + #0A0D (newline) = #2B2D ("+-").
Here is my code if anyone is interested:

;============================================
; Vintage Computing Christmas Challenge 2024
;============================================
; Submission by Arnolde of Leosoft
;   "Je m'appelle boite" (I call myself box)
;============================================
; Z80 Machine Code for Amstrad CPC, 51 bytes
;============================================
; Main loop consists of fall-through calls to
;   realize the 1-8-1-8-1 structure. It calls
;   itself recusively to serve both for the
;   line setup and the character output.
; Carry flag indicates recursion depth:
;   set = line setup mode (outer loop)
;   reset = char output mode (inner loop)
; Zero flag indicates if the current item is
;   set = a single
;   reset = an octuplet
; As the characters always cone in pairs,
;   they are passed via DE
;============================================
; To launch from Basic:
;   MEMORY &5C1F
;   LOAD "VCCC24.BIN",&5C20
;   CALL &5C20
; or run the basic loader:
;   RUN "VCCC24.BAS"
;============================================

org #5C20

; input conditions:
  ; Carry is reset -> char mode enabled
  ; DE = #5C20 = "\ "

; draw bow
  call do_8e_1d   ; output 8 spaces + "\"
  ld de,"/O"      ; (#2F4F)
  call do_1e_1d   ; output "O/"
  scf             ; set line mode
; draw box
loop:             ; 1-8-1-8-1:
  call do_1d      ; 1-
  call do_8e_1d   ; -8-1-
do_8e_1d:         ; -8-1
  ld b,7          ; will be inc'ed to 8
do_1e_1d:
  ld a,e          ; load octuplet character
  inc b           ; =1 if called directly
  repet:
    call do_it
    dec b         ; not djnz because
    jr nz,repet   ; we need zero flag
do_1d:
  ld a,d          ; load single character
do_it:            ; either...
  jp nc,#BB5A     ; output character, or...
  ; set up next line ("outer loop")
  ccf             ; set char output mode
  ld c,b          ; backup outer counter
  ld de,#0A0D     ; = LF-CR (newline)
  ld hl,"! "      ; octuplet (#2120)
  jr nz,$+3       ; z set -> setup single:
    add hl,de     ; = #2B2D = "+-" = single *
  call do_1e_1d   ; go down one text line **
  ex de,hl        ; line chars -> de
  call loop       ; output current char line
  ld b,c          ; restore outer counter
  scf             ; back to line mode
ret

; *) Yes, "! " + #0A0D = "+-". Was that an
;    intentional easter egg, Logiker?
; **) We can keep b as is, because even if
;     b>1, the repeated character is CR
  

Longshot

It's always impressive to see the multitude of logics to solve the same problem.

Quote(while one might argue that including the mode instruction in the basic stub is a little bit of cheating)

I thought about the mode change. I saw that some machines use different languages or OS whose "text mode" is not necessarily the one in force on the stock machine (for example on CP/M or on Symbos) and which do not change text mode. Clearing the screen was also allowed in launchers of previous productions (including on Cpc), and it was not specified that changing modes is prohibited before the run.

If the mode change must be integrated into the code, it takes 2 more bytes (in the bow string) or in a mode 1/mode 2 version that adds CR/LF instead of Space. In this case, the shortest program on Cpc is the Overflow Z80A cathedral :laugh: .

We can discuss other things too. I didn't play on the executability of the characters or on the gap between the ascii codes, because I found the rule a bit convoluted:

On Cpc, with a French rom, the character "\" appears as "ç", which is not very aesthetic. The rules allow "replacing" the character by its equivalent provided that it does not advantage the code.... On Cpc, there are really a lot of replacement choices. (&CD/&CC for \ / for example)

In other words, you can advantage your code with a rotten display, but not take advantage of an ascii code more graphically consistent with the expected pattern if the code takes advantage of it....

In short, the rules are still not very strict, especially since it is quite pointless to compare the different systems with each other. But hey, the ZX spectrum lost this year. :laugh:

Do any of you know whether APL exists on CPC?
Rhaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!

arnolde

#52
Quote from: Longshot on 14:13, 29 December 24On Cpc, with a French rom, the character "\" appears as "ç", which is not very aesthetic.

Ah, oui, I noticed that, but it is of course a good reason to replace with &CD/&CC – and as you didn't use these bytes for other purposes, c'est completement OK donc. (And as you were one of the big heroes of my CTM640-lit teenager room, I forgive you anything anyway.)

Quote from: Longshot on 14:13, 29 December 24In short, the rules are still not very strict, especially since it is quite pointless to compare the different systems with each other. But hey, the ZX spectrum lost this year. :laugh:
And that, obviously, is the most important thing  ;D ;D

You're right, this years pattern was great because there were so many ways to tackle it. I had another approach, also with a recursive loop, starting with A=236 and adding 28 every time. Thus, Carry is set every 9 times and Carry+Zero the 19th time. But I wasn't able to get it small enough...

GUNHED

#53
That's the source code I used, it takes advantage of control codes... the result is 52 bytes long:


TFM's "Zeige Geschenk" Programm für FutureOS auf CPC6128 oder 6128plus :-)

Das Programm benötigt 52 Bytes im Speicher - Version 3 :-)



  ARNOR Z80 ASSEMBLER version 1.15                              Page 001

00003  7F74  (7F74)                ORG  &7F74        ;Source Code 'GESCHENK.MAX' -> Assembliert in 52 Bytes
00004                              NOLIST            ;2024.12.12
00005  7F74                        WRITE "-RUN-ME."  ;Zeilen 52 / Code-Bytes 52
00006                   
00007                   
00008                    ;OS-Variablen und OS-Funktionen
00009                   
00010  7F74  (FF22)        OSRON_A EQU  &FF22
00011  7F74  (FBFA)        TERM_2  EQU  &FBFA
00012                   
00013                   
00014                    ;ZEIge GEschenk
00015                   
00016  7F74  69            ZEI_GE  LD    L,C
00016  7F75  CD FA FB              CALL  TERM_2
00017                   
00018  7F78  3E 0A                LD    A,&0A
00019                   
00020  7F7A  21 9B 7F      ZGL    LD    HL,G_STR_2+&01
00021                   
00022  7F7D  77                    LD    (HL),A
00023  7F7E  2D                    DEC  L
00024  7F7F  CD FA FB              CALL  TERM_2
00025                   
00026  7F82  3E 13                LD    A,&13
00027  7F84  18 F4                JR    ZGL
00028                   
00029                   
00030                    ;Zeichenketten
00031                   
00032  7F86  02            G_STR_1 DB    &02          ;ROM Zeichensatz
00033                   
00034  7F87  0C                    DB    &0C          ;Cursor Heim
00035                   
00036  7F88  09                    DB    &09          ;Tabulator
00037                   
00038  7F89  5C 4F 2F 0E          DB    "\O/",&0E
00039                   
00040  7F8D  05 13 21              DB    &05,&13,"!"  ;Vertikal 19x "!" ausgeben
00041                   
00042  7F90  08 08                DB    &08,&08      ;Cursor-Position +8
00043  7F92  05 13 21              DB    &05,&13,"!"  ;Vertikal 19x "!" ausgeben
00044                   
00045  7F95  08 08                DB    &08,&08      ;Cursor-Position +8
00046  7F97  05 13 21              DB    &05,&13,"!"  ;Vertikal 19x "!" ausgeben
00047                   
00048  7F9A  1E 01 00      G_STR_2 DB    &1E,&01,&00
00049                   
00050  7F9D  2B 07 08 00          DB    "+",&07,&08,&00,"-"
      7FA1  2D
00051  7FA2  2B 07 08 00          DB    "+",&07,&08,&00,"-"
      7FA6  2D
00052  7FA7  2B                    DB    "+"
00053                   

Errors: 00000  Warnings: 00000 



http://futureos.de --> Get the revolutionary FutureOS (Update: 2024.10.27)
http://futureos.cpc-live.com/files/LambdaSpeak_RSX_by_TFM.zip --> Get the RSX-ROM for LambdaSpeak :-) (Updated: 2021.12.26)

lightforce6128

I'm trying to understand how the other entries work. Each one has its own special tricks.

@Longshot : During work on this challenge I always cursed the fact the I need a modulo 9 operation, but thought that only a modulo 8 operation can easily be created by bit operations. Until I saw the RLA in your source code ... 8-bit accumulator and 1-bit carry flag. Really nice!

To also get a small source code size, I compressed my source code heavily. I also have a longer version of my source code with many comments that might help in understanding:

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

Longshot

#55
Displaying the buffer via the low rom with rst#28 has the advantage of gaining a "ret" to return to the basic prompt.

@lightforce6128
You can reach the 42-bytes score by replacing the #01 position of the bow position with #EB (same position, but here a useful "EX DE,HL") and removing the one following the #00 :

DEFB CHR_LOCATE, 9, #eb, "\O/" ;; RRA : ADD HL,BC : EX DE,HL : LD E,H : LD C,A : CPL
;; so it can be used below in a second role.
    RST LO_SIDE_CALL : DEFW BASIC_PRINT_STRING

Rhaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!

Prodatron

#56
Shit, we didn't know about RST #10, that it can be used to call the Basic routine as well.
Otherwise we would have reached 44 bytes, and it would return to the prompt.

;** 44 BYTES **

txtout  equ #bb5a

adr_run equ 19*256+255      ;call address is #13ff and predefines DE;
                            ;A=0, CF=0, D=row counter, E,CF=9bit row pattern
adr_org equ #1f00-32        ;31 (see "len_sub") is the size until "txt_chars"
                            ;which has to be placed at #1f00
        org adr_org

        ld hl,txt_bow       ;plot bow (H=#1f)
gift1   rst #10             ;plot bow or just cr/lf
        dw #c38b
        ld bc,19*256+255    ;B=column counter, C,CF=9bit column pattern
        adc a               ;build row index (A=rowbit9), CF=0
gift2   rla                 ;add column index (A=2*rowbit9+1*columnbit9)
        ld l,a              ;HL=#1f0x
        ld a,(hl)           ;load char from indexed table
        call txtout         ;print it
        ld a,l              ;restore A
        rra                 ;restore column CF from A, A=row index
        rr c                ;move through column 9bit pattern
        djnz gift2          ;column loop
        rra                 ;restore row CF from A
        ld a,b              ;A is 0 again
        rl e                ;move through row 9bit pattern
        ld l,10             ;HL=#1f0a=txt_crlf
        dec d
        jr nz,gift1         ;row loop
        ret

len_sub equ $-adr_org
                            ;here the address is #xx00
txt_chars
        db "+","-","!"," "
txt_bow
        db #1f,9,25,"\o/"   ;placing and plotting the bow
txt_crlf
        db 13,10            ;CR/LF for both the bow and the rows
                            ;(always followed by zeros)

GRAPHICAL Z80 MULTITASKING OPERATING SYSTEM

logiker

Quote from: ZorrO on 23:08, 28 December 24But it seems to me that Logiker made a mistake in counting sources and omitted spaces that are part of the program. After entering it in the emulator and of course removing unnecessary spaces after PRINT, and before TO and before THEN, it comes out to 98 bytes. Which gives it third place in CPC BASIC, not first.
Thanks for this hint. Measuring the size is in the responsibility of each participant. For many systems, I even don't know how to measure them, so it depends on communities like this one to regulate it a little bit. And of course the rules are not extra strict, as it should be mainly about the fun with coding.
The entry supplied by Azicuetano in this case is inadequate. It lacks a .dsk or .bin file and the counting is not understandable. I wrote to him. Either the entry has to be corrected or it needs to be disqualified. 

logiker

Hello!

I found out that getting the file size on the Amstrad is very tricky, because the main binary format used for emulation is dsk and tools (especially the cat command in BASIC) mostly don't show the filesize. It's not really important for the challenge, because the filesize usually is higher than the size of the source code (typed characters). But still, I want to add one thing or the other.

Quote from: lightforce6128 on 01:49, 17 December 24
  • Edit the source code with an editor / on an OS where the size in bytes is printed out.
  • Store it on disc and look into the header.
  • Use FRE(0) before and after writing the program.
  • Use this: ?PEEK(&AE66)+256*PEEK(&AE67)-PEEK(&AE64)-256*PEEK(&AE65)
  • Something that is simpler to use and that I do not know.
Thanks for this list. Let me add what I found out:

  • Using FRE(0) you have to add 2 bytes: FRE(0) before minus FRE(0) after + 2
    • Why? An empty BASIC program requires 2 bytes. So does the end of each program.
    • Code: ?42251-FRE(0)
  • Using ?PEEK(&AE66)+256*PEEK(&AE67)-PEEK(&AE64)-256*PEEK(&AE65) you must add one byte.
    • Why? The used pointers are before and after the BASIC area. When using minus, only one border must be "outside".
    • Solution 1: ?PEEK(&AE66)+256*PEEK(&AE67)-PEEK(&AE64)-256*PEEK(&AE65)
    • Solution 2: ?PEEK(&AE66)+256*PEEK(&AE67)-368
  • Another method is exporting the BASIC file out of the disk into a bin/bas file (however you want to name it).
    • Use a tool like ManageDsk or idsk
    • Check the file size. If you exported including header, subtract 128 from the file size.

And here once more in short as checklist to get the file size:

  • ?42251-FRE(0)
  • ?PEEK(&AE66)+256*PEEK(&AE67)-368
  • Export binary from dsk, check the file size and if it includes the header, subtract 128

I hope this helps.

Longshot

Quote from: Prodatron on 16:58, 30 December 24Shit, we didn't know about RST #10, that it can be used to call the Basic routine as well.
Who said that Z80A coding is not art? ;)

It is brilliantly done, this waltz of the CF bit with E and C which is common to the lines and columns, and which allows to index the charset. :o

It is also very clever to have reused the display function for the gift bow and the common Cr+Lf.

Quote from: logiker on 16:09, 01 January 25For many systems, I even don't know how to measure them, so it depends on communities like this one to regulate it a little bit
And imagine the calculation if a participant presents a cat'art, or it would be enough just to do a CAT of the DSK to display the object. :-\
Rhaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!

Prodatron

Quote from: Longshot on 13:52, 05 January 25Who said that Z80A coding is not art? ;)
It is brilliantly done, this waltz of the CF bit with E and C which is common to the lines and columns, and which allows to index the charset. :o
It is also very clever to have reused the display function for the gift bow and the common Cr+Lf.
Thank you @Longshot for your very kind words!
Your winning approach and the others are so fascinating to me, Z80 Asm and this challenge are always just crazy, I love it.

GRAPHICAL Z80 MULTITASKING OPERATING SYSTEM

logiker

The size of Azicuetano's entry is now corrected. He forgot to count 6 spaces at the end of the first line as well as a new line.

Some entries might have a slightly wrong size measurement as far as Locomotive BASIC is concerned. See my post above. Still, here are the CPC specific results.

Assembler

01 Christmas Present (Amstrad CPC 6128/Z80 Assembler) by Longshot / Logon System.......41
02 Christmas Present (Amstrad CPC/Z80 Assembler) by Overflow...........................42
03 Christmas Present (Amstrad CPC/Z80 Assembler) by lightforce6128.....................43
04 Christmas Present (Amstrad CPC 6128/Z80 Assembler) by SymbiosiS.....................46
05 Christmas Present (Amstrad CPC/Z80 Assembler) by Arnolde of Leosoft.................51
06 Christmas Present (Amstrad CPC 6128/MAXAM Z80 Assem..) by TFM of FutureSoft.........52
07 Christmas Present (Amstrad CPC 6128/Z80 Assembler) by Prodatron.....................54
08 Christmas Present (Amstrad CPC/Z80 Assembler) by Gallegux...........................62
09 Christmas Present (Amstrad CPC/Z80 Assembler) by Retropoke..........................63
10 Christmas Present (Amstrad CPC/Z80 Assembler) by issalig............................85
11 Christmas Present (Amstrad CPC/CP/M 8080 Assembler) by DrSnuggles..................110
11 Christmas Present (Amstrad CPC/Z80 Assembler) by DrSnuggles........................110


BASIC

01 Christmas Present (Amstrad CPC/Locomotive BASIC) by Logiker.........................90
02 Christmas Present (Amstrad CPC/Locomotive BASIC) by Azicuetano......................93
03 Christmas Present (Amstrad CPC/Locomotive BASIC) by Arnolde of Leosoft..............97
04 Christmas Present (Amstrad CPC/Locomotive BASIC) by lightforce6128.................114
05 Christmas Present (Amstrad CPC/Locomotive BASIC) by issalig........................115
06 Christmas Present (Amstrad CPC/Locomotive BASIC) by DrSnuggles.....................127
07 Christmas Present (Amstrad CPC/Locomotive BASIC) by Zima...........................128
08 Christmas Present (Amstrad CPC/Locomotive BASIC) by Retropoke......................131
09 Christmas Present (Amstrad CPC/Locomotive BASIC) by zombieprozess..................203
10 Christmas Present (Amstrad CPC/Locomotive BASIC) by Pararaum/T7D...................407


BSC

Quote from: Longshot on 13:52, 05 January 25It is brilliantly done, this waltz of the CF bit with E and C which is common to the lines and columns, and which allows to index the charset. :o

It is also very clever to have reused the display function for the gift bow and the common Cr+Lf.
The waltzing CF was one of the first ideas I had, way before Prodatron and I decided to merge our approaches.
But apparently going solo is much more fruitful and also supports an appropriate share (or rather no need for it) of credit, so that's what I am going to do next time. 
** My website ** Some music

My hardware: ** Schneider CPC 464 with colour screen, 64k extension, 3" and 5,25 drives and more ** Amstrad CPC 6128 with M4 board, GreaseWeazle.

Overflow

#63
38  :D

Back to business - I've been seriously busy since Xmas.

Thanks to lightforce6128's trick about rst#10, we are now down to 38 bytes : I used his rst call within my own code.

Note : source code for Winape, assemble and CALL &901 from Basic.



    org #2100; call #0901=de
loop_y  ld hl,#2121 - 2
        rst #10
        dw #c38b   
        ld l,h
        inc (hl); (#2121) counts -Y
        ret z
        dec e
        jr nz,jr_Y
            ld e,d; =9
            ld hl,#2B2D; "+-"
jr_Y equ $ -1; #2B == dec hl           
        ld bc,#100 +19; b counts X
loop_X      ld a,l
            djnz jr_X
                ld b,d; =9
                ld a,h
jr_X        call #BB5A
            dec c
            jr nz,loop_X
    db #C3; jp #091F=loop_Y
    db #1F,#09; locate 9,n n=<0
    db -20; counts -Y at org #2121
    db #CD,"o",#CC; knot
    db #0D; start of line
Unregistered from CPCwiki forum.

Prodatron


GRAPHICAL Z80 MULTITASKING OPERATING SYSTEM

Powered by SMFPacks Menu Editor Mod