Initial version
This commit is contained in:
commit
4c3ec80b05
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.tap
|
||||||
|
ttttt
|
39
Makefile
Normal file
39
Makefile
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
linuxmatters.tap: header.block body.block lmtheme.zx0.block
|
||||||
|
cat header.block body.block lmtheme.zx0.block > linuxmatters.tap
|
||||||
|
|
||||||
|
lmtheme.zx0.block: lmtheme.zx0 ttttt
|
||||||
|
./ttttt lmtheme.zx0 data
|
||||||
|
|
||||||
|
lmtheme.zx0: lmtheme
|
||||||
|
zx0 lmtheme
|
||||||
|
|
||||||
|
lmtheme: lmtheme.asm
|
||||||
|
pasmo lmtheme.asm lmtheme
|
||||||
|
|
||||||
|
header.block: header ttttt
|
||||||
|
./ttttt header header
|
||||||
|
|
||||||
|
header: header.asm body.block
|
||||||
|
pasmo header.asm headerlong
|
||||||
|
dd if=headerlong of=header bs=17 count=1
|
||||||
|
rm -f headerlong
|
||||||
|
|
||||||
|
body.block: body ttttt
|
||||||
|
./ttttt body data
|
||||||
|
|
||||||
|
body: remload.asm code.asm dzx0_turbo.asm lm1.zx0 lm2.zx0 lm3.zx0 lm4.zx0 lm5.zx0 lm6.zx0 lm7.zx0
|
||||||
|
pasmo remload.asm body
|
||||||
|
|
||||||
|
ttttt: ttttt.c
|
||||||
|
cc ttttt.c -o ttttt
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.tap
|
||||||
|
rm -f *.block
|
||||||
|
rm -f body
|
||||||
|
rm -f header
|
||||||
|
rm -f ttttt
|
||||||
|
rm -f lmtheme.zx0
|
||||||
|
rm -f lmtheme
|
||||||
|
|
||||||
|
.PHONY: clean
|
26
README.md
Normal file
26
README.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Linux Matters Podcast Theme Demo
|
||||||
|
|
||||||
|
This is a ZX Spectrum program which plays the title and theme from the [Linux Matters Podcast](https://linuxmatters.sh/). It uses my [ZX Spectrum REMLoad routine](https://boarstone.mcphail.uk/mcphail/spectrum_remload), and should serve as an example of how to adapt it to your own projects.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
On a Linux machine, run `make`. I have no idea if it will build on Windows or MacOS. Patches welcome!
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- A C compiler
|
||||||
|
- `pasmo`
|
||||||
|
- `dd`
|
||||||
|
- Einar Saukas's `zx0` compression tool ( [Github Repository](https://github.com/einar-saukas/ZX0) )
|
||||||
|
- `make`
|
||||||
|
|
||||||
|
## Thanks
|
||||||
|
|
||||||
|
- [Mark](https://indieweb.social/@marxjohnson), [Wimpy](https://wimpysworld.com/) and [Popey](https://popey.com/) from the Linux Matters show
|
||||||
|
- [Joe Ressington](https://joeress.com/) for the music, artwork and for producing the show
|
||||||
|
- [Einar Saukas](https://github.com/einar-saukas) for the `zx0` compression routines
|
||||||
|
- [Shiru](https://shiru.untergrund.net/software.shtml) for the `BeepFX` tool used to sample and play the music
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
In keeping with the podcast, this is released under the [Creative Commons Attribution-NonCommercial 4.0 International license](https://creativecommons.org/licenses/by-nc/4.0/). Please consult the `zx0` repository for the requirements for that code.
|
70
code.asm
Normal file
70
code.asm
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
; This is the main file you will edit to add your machine code to the REM statement.
|
||||||
|
; You _must_ include the "entry_point" tag to point to the address which will called by the BASIC "PRINT USR" statement.
|
||||||
|
; The default code below is for illustration only, and can be deleted.
|
||||||
|
; But REMEMBER TO ADD THE entry_point TAG TO YOUR OWN CODE!
|
||||||
|
|
||||||
|
loadmus EQU 55425
|
||||||
|
music EQU #8000
|
||||||
|
midscreen EQU #4800
|
||||||
|
|
||||||
|
data_start: ;if your routine starts here, move the entry_point tag here too
|
||||||
|
dw lm1,lm1,lm1,lm1,lm1,lm1,lm1,lm1,lm1
|
||||||
|
dw lm2,lm3,lm4,lm5,lm6,lm7,lm1,0
|
||||||
|
entry_point: ;move this tag to the start point of your code
|
||||||
|
ld hl, lm1
|
||||||
|
ld de, midscreen
|
||||||
|
call dzx0_turbo
|
||||||
|
|
||||||
|
scf
|
||||||
|
ld a, #ff
|
||||||
|
ld ix, loadmus
|
||||||
|
ld de, 10109
|
||||||
|
call #0556
|
||||||
|
|
||||||
|
ld hl, loadmus
|
||||||
|
ld de, music
|
||||||
|
call dzx0_turbo
|
||||||
|
|
||||||
|
repeat:
|
||||||
|
xor a
|
||||||
|
call music
|
||||||
|
ld hl, data_start
|
||||||
|
|
||||||
|
anim:
|
||||||
|
ld e, (hl)
|
||||||
|
inc hl
|
||||||
|
ld d, (hl)
|
||||||
|
inc hl
|
||||||
|
push hl
|
||||||
|
ld hl, -1
|
||||||
|
add hl, de
|
||||||
|
pop hl
|
||||||
|
jr nc, repeat
|
||||||
|
ex de, hl
|
||||||
|
push de
|
||||||
|
ld de, midscreen
|
||||||
|
halt
|
||||||
|
halt
|
||||||
|
halt
|
||||||
|
halt
|
||||||
|
halt
|
||||||
|
call dzx0_turbo
|
||||||
|
pop hl
|
||||||
|
jr anim
|
||||||
|
|
||||||
|
INCLUDE dzx0_turbo.asm
|
||||||
|
|
||||||
|
lm1:
|
||||||
|
INCBIN lm1.zx0
|
||||||
|
lm2:
|
||||||
|
INCBIN lm2.zx0
|
||||||
|
lm3:
|
||||||
|
INCBIN lm3.zx0
|
||||||
|
lm4:
|
||||||
|
INCBIN lm4.zx0
|
||||||
|
lm5:
|
||||||
|
INCBIN lm5.zx0
|
||||||
|
lm6:
|
||||||
|
INCBIN lm6.zx0
|
||||||
|
lm7:
|
||||||
|
INCBIN lm7.zx0
|
100
dzx0_turbo.asm
Normal file
100
dzx0_turbo.asm
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; ZX0 decoder by Einar Saukas & introspec
|
||||||
|
; "Turbo" version (126 bytes, 21% faster)
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Parameters:
|
||||||
|
; HL: source address (compressed data)
|
||||||
|
; DE: destination address (decompressing)
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
dzx0_turbo:
|
||||||
|
ld bc, $ffff ; preserve default offset 1
|
||||||
|
ld (dzx0t_last_offset+1), bc
|
||||||
|
inc bc
|
||||||
|
ld a, $80
|
||||||
|
jr dzx0t_literals
|
||||||
|
dzx0t_new_offset:
|
||||||
|
ld c, $fe ; prepare negative offset
|
||||||
|
add a, a
|
||||||
|
jp nz, dzx0t_new_offset_skip
|
||||||
|
ld a, (hl) ; load another group of 8 bits
|
||||||
|
inc hl
|
||||||
|
rla
|
||||||
|
dzx0t_new_offset_skip:
|
||||||
|
call nc, dzx0t_elias ; obtain offset MSB
|
||||||
|
inc c
|
||||||
|
ret z ; check end marker
|
||||||
|
ld b, c
|
||||||
|
ld c, (hl) ; obtain offset LSB
|
||||||
|
inc hl
|
||||||
|
rr b ; last offset bit becomes first length bit
|
||||||
|
rr c
|
||||||
|
ld (dzx0t_last_offset+1), bc ; preserve new offset
|
||||||
|
ld bc, 1 ; obtain length
|
||||||
|
call nc, dzx0t_elias
|
||||||
|
inc bc
|
||||||
|
dzx0t_copy:
|
||||||
|
push hl ; preserve source
|
||||||
|
dzx0t_last_offset:
|
||||||
|
ld hl, 0 ; restore offset
|
||||||
|
add hl, de ; calculate destination - offset
|
||||||
|
ldir ; copy from offset
|
||||||
|
pop hl ; restore source
|
||||||
|
add a, a ; copy from literals or new offset?
|
||||||
|
jr c, dzx0t_new_offset
|
||||||
|
dzx0t_literals:
|
||||||
|
inc c ; obtain length
|
||||||
|
add a, a
|
||||||
|
jp nz, dzx0t_literals_skip
|
||||||
|
ld a, (hl) ; load another group of 8 bits
|
||||||
|
inc hl
|
||||||
|
rla
|
||||||
|
dzx0t_literals_skip:
|
||||||
|
call nc, dzx0t_elias
|
||||||
|
ldir ; copy literals
|
||||||
|
add a, a ; copy from last offset or new offset?
|
||||||
|
jr c, dzx0t_new_offset
|
||||||
|
inc c ; obtain length
|
||||||
|
add a, a
|
||||||
|
jp nz, dzx0t_last_offset_skip
|
||||||
|
ld a, (hl) ; load another group of 8 bits
|
||||||
|
inc hl
|
||||||
|
rla
|
||||||
|
dzx0t_last_offset_skip:
|
||||||
|
call nc, dzx0t_elias
|
||||||
|
jp dzx0t_copy
|
||||||
|
dzx0t_elias:
|
||||||
|
add a, a ; interlaced Elias gamma coding
|
||||||
|
rl c
|
||||||
|
add a, a
|
||||||
|
jr nc, dzx0t_elias
|
||||||
|
ret nz
|
||||||
|
ld a, (hl) ; load another group of 8 bits
|
||||||
|
inc hl
|
||||||
|
rla
|
||||||
|
ret c
|
||||||
|
add a, a
|
||||||
|
rl c
|
||||||
|
add a, a
|
||||||
|
ret c
|
||||||
|
add a, a
|
||||||
|
rl c
|
||||||
|
add a, a
|
||||||
|
ret c
|
||||||
|
add a, a
|
||||||
|
rl c
|
||||||
|
add a, a
|
||||||
|
ret c
|
||||||
|
dzx0t_elias_loop:
|
||||||
|
add a, a
|
||||||
|
rl c
|
||||||
|
rl b
|
||||||
|
add a, a
|
||||||
|
jr nc, dzx0t_elias_loop
|
||||||
|
ret nz
|
||||||
|
ld a, (hl) ; load another group of 8 bits
|
||||||
|
inc hl
|
||||||
|
rla
|
||||||
|
jr nc, dzx0t_elias_loop
|
||||||
|
ret
|
||||||
|
; -----------------------------------------------------------------------------
|
21
header.asm
Normal file
21
header.asm
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
blocktype:
|
||||||
|
db 0 ;basic program
|
||||||
|
|
||||||
|
filename:
|
||||||
|
db "LOADER "
|
||||||
|
|
||||||
|
payloadlength:
|
||||||
|
dw endbodytap - bodytap - 4
|
||||||
|
|
||||||
|
autorunlinenumber:
|
||||||
|
dw 0
|
||||||
|
|
||||||
|
variablearea:
|
||||||
|
dw endbodytap - bodytap - 4
|
||||||
|
|
||||||
|
checksum:
|
||||||
|
db 0
|
||||||
|
|
||||||
|
bodytap:
|
||||||
|
INCBIN body.block
|
||||||
|
endbodytap:
|
2071
lmtheme.asm
Normal file
2071
lmtheme.asm
Normal file
File diff suppressed because it is too large
Load Diff
58
remload.asm
Normal file
58
remload.asm
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
org #5ccb
|
||||||
|
|
||||||
|
linenumber:
|
||||||
|
db #00 ;MSB
|
||||||
|
db #00 ;LSB
|
||||||
|
|
||||||
|
linelength:
|
||||||
|
dw eol - border ;will need calculated and adjusted post hoc. Length of text including ENTER
|
||||||
|
border:
|
||||||
|
db #e7, '0' ;BORDER 0
|
||||||
|
db #0e ;number
|
||||||
|
db 0,0 ;mantissa
|
||||||
|
dw 0 ;number
|
||||||
|
db 0
|
||||||
|
db ':'
|
||||||
|
|
||||||
|
paper:
|
||||||
|
db #da, '0' ;PAPER 0
|
||||||
|
db #0e
|
||||||
|
db 0,0
|
||||||
|
dw 0
|
||||||
|
db 0
|
||||||
|
db ':'
|
||||||
|
|
||||||
|
ink:
|
||||||
|
db #d9, '6' ;INK 7
|
||||||
|
db #0e
|
||||||
|
db 0,0
|
||||||
|
dw 6
|
||||||
|
db 0
|
||||||
|
db ':'
|
||||||
|
|
||||||
|
clear:
|
||||||
|
db #fd, "32767" ;CLEAR 59999 - presumably code will be loaded somewhere?
|
||||||
|
db #0e
|
||||||
|
db 0,0
|
||||||
|
dw 32767
|
||||||
|
db 0
|
||||||
|
db ':'
|
||||||
|
|
||||||
|
printusr:
|
||||||
|
db #f5, #c0 ;PRINT USR
|
||||||
|
db "0" ;don't know if actual value is important
|
||||||
|
db #0e
|
||||||
|
db 0,0
|
||||||
|
dw entry_point ;actual call to REM statement code
|
||||||
|
db 0
|
||||||
|
db ':'
|
||||||
|
|
||||||
|
remcode:
|
||||||
|
db #ea ;REM
|
||||||
|
|
||||||
|
code:
|
||||||
|
INCLUDE code.asm
|
||||||
|
|
||||||
|
enter:
|
||||||
|
db #0d
|
||||||
|
eol:
|
95
ttttt.c
Normal file
95
ttttt.c
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/* McPhail's Tip-Top TAP Top-Tailer
|
||||||
|
* Takes a raw code file as input
|
||||||
|
* Prepends data length and appends xor checksum
|
||||||
|
* Outputs to new file with .block suffix */
|
||||||
|
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int fdin, fdout;
|
||||||
|
char outfilename[PATH_MAX];
|
||||||
|
unsigned int count = 0;
|
||||||
|
unsigned char flagopt = 0;
|
||||||
|
unsigned char tally = 0;
|
||||||
|
unsigned char next = 0;
|
||||||
|
if ((argc<2) || (argc>3)) {
|
||||||
|
printf("Please specify file to read.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (3==argc) {
|
||||||
|
if (!strcmp(argv[2], "header")) flagopt = 1;
|
||||||
|
if (!strcmp(argv[2], "data")) flagopt = 2;
|
||||||
|
if (!flagopt) {
|
||||||
|
printf("Optional flags are \"header\" or \"data\"\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! flagopt) {
|
||||||
|
printf("*** WARNING ***\n");
|
||||||
|
printf("*** ttttt is running in RAW mode.\n");
|
||||||
|
printf("*** Have you manually included the format flag as the first byte of the block?\n");
|
||||||
|
printf("*** If not, run again passing 'header' or 'data' as the second parameter.\n");
|
||||||
|
printf("*** WARNING ***\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fdin = open(argv[1], O_RDONLY);
|
||||||
|
if (fdin<0) {
|
||||||
|
printf("Couldn't open %s. for reading\n", argv[1]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(outfilename, PATH_MAX, "%s.block", argv[1]);
|
||||||
|
outfilename[PATH_MAX - 1] = '\0';
|
||||||
|
if (! strcmp(argv[1], outfilename)) {
|
||||||
|
printf("Filename too long - would clobber existing.\n");
|
||||||
|
close(fdin);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdout = open(outfilename, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
|
||||||
|
if (fdout<0) {
|
||||||
|
printf("Couldn't open %s for writing.\n", outfilename);
|
||||||
|
close(fdin);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (read(fdin, &next, 1)) {
|
||||||
|
tally ^= next;
|
||||||
|
count ++;
|
||||||
|
};
|
||||||
|
lseek(fdin, 0, SEEK_SET);
|
||||||
|
|
||||||
|
if (flagopt) count++;
|
||||||
|
|
||||||
|
char lsb = (count+1)&255;
|
||||||
|
char msb = ((count+1)>>8)&255;
|
||||||
|
write(fdout, &lsb, 1);
|
||||||
|
write(fdout, &msb, 1);
|
||||||
|
next = 0;
|
||||||
|
if (flagopt == 2) next = 255;
|
||||||
|
if (flagopt) {
|
||||||
|
write(fdout, &next, 1);
|
||||||
|
tally ^= next;
|
||||||
|
}
|
||||||
|
while (read(fdin, &next, 1)) {
|
||||||
|
write(fdout, &next, 1);
|
||||||
|
}
|
||||||
|
write(fdout, &tally, 1);
|
||||||
|
|
||||||
|
close(fdout);
|
||||||
|
close(fdin);
|
||||||
|
|
||||||
|
printf("File %s written.\n", outfilename);
|
||||||
|
count--;
|
||||||
|
printf("Code length is %d (0x%.4X).\n", count, count);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user