Skip to content

Commit 3df2eb4

Browse files
author
Stefan Schramm
committed
Implement AtariCasConverter
1 parent 3b2f12b commit 3df2eb4

File tree

10 files changed

+104
-20
lines changed

10 files changed

+104
-20
lines changed
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
include ../Makefile.inc
22

3-
all: rl.cas
3+
all: rl.bin rl.cas
44

55
# https://atari800.github.io/
66
run: rl.cas
@@ -10,11 +10,11 @@ run: rl.cas
1010
retroload $< -o $@
1111

1212
# https://a8cas.sourceforge.net/
13-
%.cas: %.img
13+
%.cas: %.bin
1414
a8cas-convert -f c -r $< $@
1515

16-
%.img: %.asm
16+
%.bin: %.asm
1717
$(65XX_ASM) $< -o $@
1818

1919
clean:
20-
rm -f *.wav *.cas *.img
20+
rm -f *.wav *.cas *.bin
File renamed without changes.
155 Bytes
Binary file not shown.
File renamed without changes.

retroload-lib/src/Examples.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const examples: ExampleDefinition[] = [
4646
},
4747
// Atari 800 XL
4848
{
49-
dir: 'atari_cas',
49+
dir: 'atari_bin',
5050
file: 'rl.cas',
5151
options: {},
5252
hash: '8d36a2a696c7e27807c4d1f058fdec34',

retroload-lib/src/common/Utils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ export function calculateChecksum8Xor(ba: BufferAccess, initial = 0x00) {
2727
return sum;
2828
}
2929

30+
export function calculateChecksum8WithCarry(ba: BufferAccess) {
31+
// 8 bit checksum with carry being added
32+
let sum = 0;
33+
for (let i = 0; i < ba.length(); i++) {
34+
sum += ba.getUint8(i);
35+
if (sum > 255) {
36+
sum = (sum & 0xff) + 1;
37+
}
38+
}
39+
40+
return sum;
41+
}
42+
3043
/**
3144
* https://gist.github.com/chitchcock/5112270?permalink_comment_id=3834064#gistcomment-3834064
3245
*

retroload-lib/src/conversion/ConverterManager.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,20 @@ import {BufferAccess} from '../common/BufferAccess.js';
33
import {convert} from './ConverterManager.js';
44
import * as fs from 'fs';
55

6-
test('ConverterManager calls convert function of ConverterDefinition', () => {
6+
test('Format kctap is converted correctly', () => {
77
const data = BufferAccess.createFromNodeBuffer(fs.readFileSync(getLocalPathByDirAndFile('kc851_tap', 'rl.com')));
88

99
const result = convert(data, 'kctap', {});
1010

1111
const expectedData = BufferAccess.createFromNodeBuffer(fs.readFileSync(getLocalPathByDirAndFile('kc851_tap', 'rl.tap')));
1212
expect(result.asHexDump()).toBe(expectedData.asHexDump());
1313
});
14+
15+
test('Format ataricas is converted correctly', () => {
16+
const data = BufferAccess.createFromNodeBuffer(fs.readFileSync(getLocalPathByDirAndFile('atari_bin', 'rl.bin')));
17+
18+
const result = convert(data, 'ataricas', {});
19+
20+
const expectedData = BufferAccess.createFromNodeBuffer(fs.readFileSync(getLocalPathByDirAndFile('atari_bin', 'rl.cas')));
21+
expect(result.asHexDump()).toBe(expectedData.asHexDump());
22+
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {type ConverterDefinition} from './converter/ConverterDefinition.js';
2+
import AtariCasConverter from './converter/AtariCasConverter.js';
23
import KcTapConverter from './converter/KcTapConverter.js';
34

45
export const converters: ConverterDefinition[] = [
6+
AtariCasConverter,
57
KcTapConverter,
68
];
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import {BufferAccess} from '../../common/BufferAccess.js';
2+
import {calculateChecksum8WithCarry} from '../../common/Utils.js';
3+
import {type OptionContainer} from '../../encoding/Options.js';
4+
import {type ConverterDefinition} from './ConverterDefinition.js';
5+
6+
const definition: ConverterDefinition = {
7+
name: 'Atari .CAS-File',
8+
identifier: 'ataricas',
9+
options: [],
10+
convert,
11+
};
12+
export default definition;
13+
14+
const markerByte = 0x55;
15+
const blockTypeFull = 0xfc;
16+
const blockTypePartial = 0xfa;
17+
const blockTypeEndOfFile = 0xfe;
18+
const dataBytesPerBlock = 128;
19+
20+
const pilotIrgLength = 20000;
21+
const defaultIrgLength = 250; // TODO: longer for 'ENTER'-loading
22+
23+
function convert(data: BufferAccess, _options: OptionContainer): BufferAccess {
24+
const chunks = data.chunks(dataBytesPerBlock);
25+
26+
// FUJI-Header, baud-block, data blocks, end of file block
27+
const outBa = BufferAccess.create(8 + 8 + (chunks.length + 1) * (8 + 132));
28+
29+
outBa.writeAsciiString('FUJI');
30+
outBa.writeUint16Le(0x0000);
31+
outBa.writeUint16Le(0x0000);
32+
33+
outBa.writeAsciiString('baud');
34+
outBa.writeUint16Le(0x0000);
35+
outBa.writeUint16Le(600); // default baud rate
36+
37+
for (let i = 0; i < chunks.length; i++) {
38+
const chunk = chunks[i];
39+
40+
outBa.writeAsciiString('data');
41+
outBa.writeUint16Le(132);
42+
outBa.writeUint16Le(i === 0 ? pilotIrgLength : defaultIrgLength);
43+
44+
const blockBa = BufferAccess.create(132);
45+
blockBa.writeUint8(markerByte);
46+
blockBa.writeUint8(markerByte);
47+
const partialBlock = chunk.length() !== dataBytesPerBlock;
48+
const blockType = partialBlock ? blockTypePartial : blockTypeFull;
49+
blockBa.writeUint8(blockType);
50+
blockBa.writeBa(partialBlock ? chunk.chunksPadded(dataBytesPerBlock, 0x00)[0] : chunk);
51+
if (partialBlock) {
52+
blockBa.setUint8(130, chunk.length());
53+
}
54+
blockBa.setUint8(131, calculateChecksum8WithCarry(blockBa));
55+
56+
outBa.writeBa(blockBa);
57+
}
58+
59+
// end of file block
60+
outBa.writeAsciiString('data');
61+
outBa.writeUint16Le(132);
62+
outBa.writeUint16Le(defaultIrgLength);
63+
const endBlock = BufferAccess.create(132);
64+
endBlock.writeUint8(markerByte);
65+
endBlock.writeUint8(markerByte);
66+
endBlock.writeUint8(blockTypeEndOfFile);
67+
endBlock.setUint8(131, calculateChecksum8WithCarry(endBlock));
68+
outBa.writeBa(endBlock);
69+
70+
return outBa;
71+
}

retroload-lib/src/encoding/adapter/atari/AtariGenericAdapter.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {type OptionContainer} from '../../Options.js';
44
import {type RecorderInterface} from '../../recorder/RecorderInterface.js';
55
import {unidentifiable, type FormatIdentification} from '../AdapterDefinition.js';
66
import {type AdapterDefinition} from '../AdapterDefinition.js';
7+
import {calculateChecksum8WithCarry} from '../../../common/Utils.js';
78

89
const definition: AdapterDefinition = {
910
name: 'Atari (Generic data)',
@@ -45,7 +46,7 @@ function encode(recorder: RecorderInterface, ba: BufferAccess, _options: OptionC
4546
blockBa.setUint8(130, chunkBa.length());
4647
}
4748

48-
blockBa.setUint8(131, calculateChecksum(blockBa));
49+
blockBa.setUint8(131, calculateChecksum8WithCarry(blockBa));
4950
e.recordIrg((blockId === 0) ? pilotIrgLength : defaultIrgLength); // TODO: create option (longer values are required for "ENTER-loading")
5051
e.recordBytes(blockBa);
5152
}
@@ -55,20 +56,8 @@ function encode(recorder: RecorderInterface, ba: BufferAccess, _options: OptionC
5556
eofBlockBa.writeUint8(markerByte);
5657
eofBlockBa.writeUint8(markerByte);
5758
eofBlockBa.writeUint8(blockTypeEndOfFile);
58-
eofBlockBa.setUint8(131, calculateChecksum(eofBlockBa));
59+
eofBlockBa.setUint8(131, calculateChecksum8WithCarry(eofBlockBa));
5960
e.recordIrg(defaultIrgLength); // TODO: create option (longer values are required for "ENTER-loading")
6061
e.recordBytes(eofBlockBa);
6162
}
6263

63-
function calculateChecksum(ba: BufferAccess) {
64-
// 8 bit checksum with carry being added
65-
let sum = 0;
66-
for (let i = 0; i < ba.length(); i++) {
67-
sum += ba.getUint8(i);
68-
if (sum > 255) {
69-
sum = (sum & 0xff) + 1;
70-
}
71-
}
72-
73-
return sum;
74-
}

0 commit comments

Comments
 (0)