So, dann möchte ich hier auch mal etwas vorstellen, was für meinen Beitrag zu "BASIC-Weihnachten" entstanden ist. Ich hatte ja früh die Idee, "nette" Musik einzubinden und dazu einen möglichst minimalen SID-Player für BASIC zu schreiben (der wird später auch noch separat veröffentlicht). Problem war dann, obwohl der nicht allzu viel kann, war der Code doch für ein abzutippendes BASIC-Programm recht groß -- 64 Zeilen DATA-Wüste nur für den Player waren es beim ersten Versuch. Außerdem dauerte da natürlich auch der Start recht lange .. DATA/READ und POKE sind nicht gerade flott.
Also habe ich mir etwas überlegt, um das besser zu machen. Die Idee war schnell, dass in den BASIC Zeilen einfach direkt der MC-Code als hex steht. Leider pfuscht da der Tokenizer rein, die Sequenz "DEF" wird z.B. in ein Token übersetzt. Also mussten noch Anführungszeichen drumherum.
Das Ergebnis ist ein kleines (PC) Tool, das ein PRG mit dem Maschinencode nimmt und passenden BASIC-Source ausgibt, inklusive einer kleinen Laderoutine, die "klassisch" per DATA/POKE in den Datasettenpuffer geschrieben wird. Diese Routine liest dann die Hex-Strings und springt am Ende direkt zu dem geladenen Maschinencode (es wird erwartet, dass die Ladeadresse auch die Einsprungadresse ist).
Im Ergebnis wird das BASIC Programm ein gutes Stück kleiner als mit klassischem DATA/POKE und die Geschwindigkeit ist um ein Vielfaches besser. Falls man viele sich wiederholende Bytes hat wird das Abtippen schwieriger (Nullen zählen) -- das kann man dann aber elegant durch crunchen (z.B. mit exomizer) umgehen.
Source: https://github.com/Zirias/c64_basicload
Da ist auch ein kleines README -- Aufruf z.B. mit basicload <mycode.prg >loader.bas.
Einen win32 build hänge ich mal an
Hier noch der kommentierte Source der Laderoutine:
- .code
- .word $0334 ; load address (datasette buffer)
- ldx #$1 ; index for writing start address
- stx $2 ; init marker for hi-/lo- nibble
- lineloop: jsr $0073 ; get next character from BASIC
- beq eol ; 0 -> end of BASIC line
- hexloop: jsr $0073 ; next char
- beq done ; 0 -> end of BASIC program
- cmp #'"'
- beq checkend ; another quote, check whether done
- sbc #$30 ; subtract offset for '0'
- cmp #$11
- bcc digit ; if below 10, it was a decimal digit
- sbc #$7 ; otherwise subtract diff '10' to 'A'
- digit: lsr $2 ; check bit #0 of marker
- bcc seconddigit ; not set -> second digit (lo nibble)
- asl ; otherwise shift 4 bits
- asl
- asl
- asl
- sta $fb ; store to temporary
- bcc hexloop ; and continue at next digit
- seconddigit: ora $fb ; combine with hi nibble
- sec
- rol $2 ; shift a 1 back into bit #0
- cpx #$3 ; start address already read?
- bne setpointers ; if not, set pointers
- write: sta $0101 ; store to dest (placeholder)
- inc write+1 ; next address
- bne hexloop ; and repeat
- inc write+2 ; on overflow, also increment hi byte
- bne hexloop
- setpointers: sta done,x ; write pointer to start program
- sta write,x ; write pointer to store program
- inx ; next index
- bne hexloop ; and repeat
- checkend: jsr $0073 ; on '"', get next character
- beq eol ; 0 -> end of BASIC line
- done: jmp $af08 ; start our program (placeholder)
- eol: ldy #$5 ; load first character of next
- lda ($7a),y ; BASIC line
- cmp #'"'
- bne done ; if it's not '"', we're done loading
- ldy #$2 ; check next BASIC line pointer
- lda ($7a),y
- beq done ; 0 -> end of BASIC program, so done
- iny
- lda ($7a),y ; copy next BASIC line number
- sta $39 ; to ZP location where BASIC expects it
- iny
- lda ($7a),y
- sta $3a
- clc
- tya
- adc $7a ; move CHRGET pointer to start of
- sta $7a ; next BASIC line
- bcc lineloop ; and continue reading
- inc $7b ; adjust hi-byte on carry
- bne lineloop