Captured by a FTT spectral analysis system |

|
A 4-bit ~1000 Hz sine wave, sampled at 8x rate, as it is reconstructed by Apollo181 DAC |
How to invoke subprograms without Call/Return statements
As usual with APOLLO181, it is a bit of a challenge to write effective software in
only 256 available memory locations . The major limitation is the no provision at all of calling subroutine
instructions. Since the delay between samples must be identical, it would be a waste of memory
writing the same delay routine many times, across the whole program.
Since in the next exercise we are going to produce a cycle of 32
different samples to reconstruct a complex periodic waveform, by using 256 Bytes of RAM we would
need to write 32 nearly identical subprograms, each occupying maximum eight locations of memory (256/32=8), that generate the
sample and compute the delay between two adjacent samples. This is not only impractical, but also
impossible to perform.
The solution (source: Choosing and using 4 bit microcontrollers, by Philip
McDowell, 1993, pg. 116) is making use of a table of jumps, where the return statement would be. The 'calling' section
of code loads a progressive number into a dedicated register prior to jump to the 'subroutine', and at the end of the routine,
this number is used as an index to a table of jumps. This means that every use of the routine must have an entry
in the table, and this restricts the number of 'calls' to a maximum of sixteen for a 4-bit processor; in our case
the maximum number of 'calls' is only eight, because APOLLO181's jump statement requires two instructions to be performed:
the first to load the accumulator with the low-order nibble of the RAM location, and the second for the jump instruction
itself, which contains the high-order nibble.
Thus, in our exercise, we will need to repeat the pseudo-call routines four
times inside the main algorithm (32/8=4), that is wasteful, but anyway more efficient than writing thirty-two identical
delay routines.
Here below you may find the structure of the algorithm for calling a
subprogram many times, , by using a table of jumps without the use of Call/Return statements:
0)
Return_index = 0
1)
Load accumulator with sample n.1
2)
Output accumulator to Port 0
3)
Goto Delay_subroutine
4)
Load accumulator with sample n.2
5)
Output accumulator to Port 0
6)
Goto Delay_subroutine
7)
Load accumulator with sample n.3
8)
…
…
…
Delay_subroutine:
CalculateDelay
Increment Return_index
Goto
Return_index
Return_index 1):
Goto 4)
Return_index 2):
Goto 7)
Return_index 3):
Goto ...
…
…
Sound recontruction by additive synthesis
Joseph Fourier, mathematician and physicist of the 18th century, realized that any signal,
no matter what looks the shape of the waveform, can be expressed as the sum of sinusoidal waves at various amplitudes
and frequencies.
Thus, theoretically, it is possible to synthesize and reproduce any complex
sound from the combination of pure sinusoidal tones. This is the concept behind additive synthesis, which involves constructing a
wide range of sounds by adding up multiple sine waves at different frequencies and amplitude.
Unfortunately, a person’s voice or natural sounds are so complicated
that require a nearly infinite amount of data processing to be recreated accurately. In practice, of course, the sum of sinusoidal
waves is truncated to a finite number of terms, in order to considerably reduce the computational load.
|

|
C Major chord made up of three notes sounding simultaneously, transponed from C4 to C6 (at ~1kHz) |
In this exercise we will compute an algorithm to play a major triad, that is a chord composed of three notes
alone, played simultaneously.
In particular, we are going to synthesize a C Major chord, that is one of the most common chord, used in
many songs. This chord is basic because it comprises only three notes: C, E, and G. These, physically, have frequencies
in the ratio of 4:5:6 and when they are played together, the three notes blend very well and are pleasant to the human ear.
To simplify all calculations, from now on we will adopt the scientific scale, based on 'Middle
C' having a frequency of 256 Hz. This is also known as the diatonic scale, that is an eight-note musical scale composed of
seven pitches and a repeated octave.
The frequencies of the three notes in the C Major chord (starting at 'Middle C') are: C4= 256 Hz, E4= 320
Hz and G4= 384 Hz.
The ratio of E to C is 5/4 (or 1,25). The ratio of G to C is 6/4= 3/2 (or 1,5) and the ratio of G to E is 6/5 (or 1,2). Since the ratio between every note can be expressed as the ratio of
two small whole numbers, and the frequencies of the notes are equally distant from each other, they all sound pleasant
together; hence the entire major triad is consonant.
The resulting waveform of C+E+G, sum of sinusoids, is like:
C + E + G = sin(x) + sin(5/4 x) + sin(3/2 x)
where:
C4
= sin (2πf
t)
E4
= sin (5/4 * 2πf
t)
G4 = sin (3/2 * 2πf t)
and f = 256 Hz.
Since the period of C is 2π, the period of E is 4/3π
and the period of G is 8/5π, the resulting period of the sum C+E+G is 8π (that is the lowest common multiple of
2π, 4/3π and 8/5π). This means that the period of the C Major chord is 4 times (8π / 2π = 4)
the period of the lowest note in the chord (i.e. four times the period of C4 note).
Since frequency is equal to the reciprocal of the period, the fundamental frequency for the C Major chord
is one fourth the frequency of the lowest note in the chord, that's one fourth of C4: thus,
in the C Major chord the fundamental frequency is missing from the tone and there is no spectral energy at
that frequency.
The fundamental missing frequency for the C Major chord results to be 256/4= 64 Hz: of course
if we need to digitize such a chord with 8kHz sample rate, we would need 8000/64= 125 different samples to reconstruct it.
Since we have only 256 bytes of RAM available for the program, we have to transpose the C Major chord two
octaves up: by transposing sound two octaves up (that means four times the frequency) we need only one fourth
of the number of samples while maintaining constant the sampling rate.
The frequencies of the three notes in the transposed C Major chord (starting now at 'Top C') are:
C6= 1024 Hz, E6= 1280 Hz and G6= 1536 Hz, and the resulting chord has a missing fundamental frequency of 1024/4= 256
Hz.
Since in the C Major chord, the fundamental frequency has no spectral energy, the chord can be anyway reproduced
by APOLLO181, even if the small speaker cannot reproduce sounds lower than 350 Hz.
In order to digitize such a chord using a sampling rate of 8000 Hz, we need now to generate only 8000/256
~ 32 different samples useful to reconstruct the whole tone.
Graphs plotted in MS Excel |
|
C Major Chord is synthetized by adding together bit-by-bit the samples of the 3 notes |
Due to the lack of hardware resources, all the cumbersome
calculations and the digital additive synthesis were made outside APOLLO181, under MS Excel. The algorithm is limited
only to read the samples already stored in the program memory and issue them cyclically to the DAC, with the proper
delay.
In particular I plotted for several periods each of the three sinusoids in MS
Excel with +30 degrees phase shift, and I digitized, with an Excel formula, each period of the slowest wave (~1KHz sine wave) into eight
samples (that means 8kHz of sample rate), by using sixteen rounded integer levels (that means 4-bit accuracy). I
digitized the other two waves with the same sample rate, so, at the end, all the samples of the three waves last
the same time and result synchronous. I then added together bit-by-bit the level of the corresponding
samples, till covering a full period of the waveform. In fact, we have seen that the resulting wave
is still periodic, with period equal to four periods of the slowest wave. For each sample I then divided the result of the
sum by 3 and then I rounded it to an integer, in order to normalize the level to a 4-bit precision (that means
that the peak amplitude of the level of any sample, so of the entire resulting waveform, cannot exceed binary 15).
The following table shows all the 32 samples (=8 samples*4
periods) which I have calculated:
Sample
number |
Degrees |
D/A |
D/A
binary |
|
Sample
number |
Degrees |
D/A |
D/A
binary |
1 |
0 |
11 |
1011 |
|
17 |
720 |
9 |
1001 |
2 |
45 |
15 |
1111 |
|
18 |
765 |
10 |
1010 |
3 |
90 |
12 |
1100 |
|
19 |
810 |
9 |
1001 |
4 |
135 |
5 |
0101 |
|
20 |
855 |
7 |
0111 |
5 |
180 |
2 |
0010 |
|
21 |
900 |
6 |
0110 |
6 |
225 |
4 |
0100 |
|
22 |
945 |
7 |
0111 |
7 |
270 |
8 |
1000 |
|
23 |
990 |
7 |
0111 |
8 |
315 |
11 |
1011 |
|
24 |
1035 |
6 |
0110 |
9 |
360 |
10 |
1010 |
|
25 |
1080 |
5 |
0101 |
10 |
405 |
8 |
1000 |
|
26 |
1125 |
7 |
0111 |
11 |
450 |
7 |
0111 |
|
27 |
1170 |
11 |
1011 |
12 |
495 |
8 |
1000 |
|
28 |
1215 |
12 |
1100 |
13 |
540 |
8 |
1000 |
|
29 |
1260 |
9 |
1001 |
14 |
585 |
6 |
0110 |
|
30 |
1305 |
3 |
0011 |
15 |
630 |
5 |
0101 |
|
31 |
1350 |
0 |
0000 |
16 |
675 |
6 |
0110 |
|
32 |
1395 |
4 |
0100 |
Graphs plotted in MS Excel |
|
The resulting C6+E6+G6 Chord is still a periodic wave, that can be digitized with 32 samples |
Captured by a FTT spectral analysis system |

|
C6+E6+G6 Chord, sampled at 8x rate, 4-bit accuracy, as it is reconstructed by Apollo181 DAC |
Here you can listen to APOLLO181 C Major chord outcome and below
the page you may check the full program.
PLAY APOLLO181 reconstructed chord
Sound
reconstruction: a pencil-paper method
The last exercise is easy and practical. The waveform of a single tone
of a real violin was recorded and printed on a paper. After that, with the use of a special
grid 32x16, it was sampled with a red pencil. The 32 samples, having each 4-bit accuracy, were loaded into
APOLLO181 RAM and played by using the same algorithm as was used before.
Here you can listen to the true tone of the original violin first
and, after a brief pause, to APOLLO181 playing the same but reconstructed tone.
PLAY VIOLIN tone comparison
A part the natural vibrato timbre, characteristic envelope modulation of the real violin’s waveform, the two tones sound very similar to the human ear.
|
A real violin was recorded and digitized by hand using 32 samples with 4-bit precision |
Captured by a FTT spectral analysis system |

|
The violin waveform, sampled at 8x rate, as it is reconstructed by Apollo181 DAC |
Address
Description
APOLLO181 Code
00 |
|
49 |
SET
ALU to 9# |
01 |
|
1E |
ACC
= E# |
02 |
|
20 |
REG
[0] = ACC |
03 |
|
21 |
REG
[1] = ACC |
04 |
|
22 |
REG
[2] = ACC |
05 |
|
23 |
REG
[3] = ACC |
06 |
Output
sample 1 |
1B |
ACC
= B# |
07 |
|
F0 |
OUT
PORT [0] = ACC |
08 |
|
16 |
ACC
= 6# |
09 |
|
08 |
GOTO
86# |
0A |
Output
sample 2 |
1F |
ACC
= F# |
0B |
|
F0 |
OUT
PORT [0] = ACC |
0C |
|
16 |
ACC
= 6# |
0D |
|
08 |
GOTO
86# |
0E |
Output
sample 3 |
1C |
ACC
= C# |
0F |
|
F0 |
OUT
PORT [0] = ACC |
10 |
|
16 |
ACC
= 6# |
11 |
|
08 |
GOTO
86# |
12 |
Output
sample 4 |
15 |
ACC
= 5# |
13 |
|
F0 |
OUT
PORT [0] = ACC |
14 |
|
16 |
ACC
= 6# |
15 |
|
08 |
GOTO
86# |
16 |
Output
sample 5 |
12 |
ACC
= 2# |
17 |
|
F0 |
OUT
PORT [0] = ACC |
18 |
|
16 |
ACC
= 6# |
19 |
|
08 |
GOTO
86# |
1A |
Output
sample 6 |
14 |
ACC
= 4# |
1B |
|
F0 |
OUT
PORT [0] = ACC |
1C |
|
16 |
ACC
= 6# |
1D |
|
08 |
GOTO
86# |
1E |
Output
sample 7 |
18 |
ACC
= 8# |
1F |
|
F0 |
OUT
PORT [0] = ACC |
20 |
|
16 |
ACC
= 6# |
21 |
|
08 |
GOTO
86# |
22 |
Output
sample 8 |
1B |
ACC
= B# |
23 |
|
F0 |
OUT
PORT [0] = ACC |
24 |
|
16 |
ACC
= 6# |
25 |
|
08 |
GOTO
86# |
26 |
Output
sample 9 |
1A |
ACC
= A# |
27 |
|
F0 |
OUT
PORT [0] = ACC |
28 |
|
14 |
ACC
= 4# |
29 |
|
09 |
GOTO
94# |
2A |
Output
sample 10 |
18 |
ACC
= 8# |
2B |
|
F0 |
OUT
PORT [0] = ACC |
2C |
|
14 |
ACC
= 4# |
2D |
|
09 |
GOTO
94# |
2E |
Output
sample 11 |
17 |
ACC
= 7# |
2F |
|
F0 |
OUT
PORT [0] = ACC |
30 |
|
14 |
ACC
= 4# |
31 |
|
09 |
GOTO
94# |
32 |
Output
sample 12 |
18 |
ACC
= 8# |
33 |
|
F0 |
OUT
PORT [0] = ACC |
34 |
|
14 |
ACC
= 4# |
35 |
|
09 |
GOTO
94# |
36 |
Output
sample 13 |
18 |
ACC
= 8# |
37 |
|
F0 |
OUT
PORT [0] = ACC |
38 |
|
14 |
ACC
= 4# |
39 |
|
09 |
GOTO
94# |
3A |
Output
sample 14 |
16 |
ACC
= 6# |
3B |
|
F0 |
OUT
PORT [0] = ACC |
3C |
|
14 |
ACC
= 4# |
3D |
|
09 |
GOTO
94# |
3E |
Output
sample 15 |
15 |
ACC
= 5# |
3F |
|
F0 |
OUT
PORT [0] = ACC |
40 |
|
14 |
ACC
= 4# |
41 |
|
09 |
GOTO
94# |
42 |
Output
sample 16 |
16 |
ACC
= 6# |
43 |
|
F0 |
OUT
PORT [0] = ACC |
44 |
|
14 |
ACC
= 4# |
45 |
|
09 |
GOTO
94# |
46 |
Output
sample 17 |
19 |
ACC
= 9# |
47 |
|
F0 |
OUT
PORT [0] = ACC |
48 |
|
12 |
ACC
= 2# |
49 |
|
0A |
GOTO
A2# |
4A |
Output
sample 18 |
1A |
ACC
= A# |
4B |
|
F0 |
OUT
PORT [0] = ACC |
4C |
|
12 |
ACC
= 2# |
4D |
|
0A |
GOTO
A2# |
4E |
Output
sample 19 |
19 |
ACC
= 9# |
4F |
|
F0 |
OUT
PORT [0] = ACC |
50 |
|
12 |
ACC
= 2# |
51 |
|
0A |
GOTO
A2# |
52 |
Output
sample 20 |
17 |
ACC
= 7# |
53 |
|
F0 |
OUT
PORT [0] = ACC |
54 |
|
12 |
ACC
= 2# |
55 |
|
0A |
GOTO
A2# |
56 |
Output
sample 21 |
16 |
ACC
= 6# |
57 |
|
F0 |
OUT
PORT [0] = ACC |
58 |
|
12 |
ACC
= 2# |
59 |
|
0A |
GOTO
A2# |
5A |
Output
sample 22 |
17 |
ACC
= 7# |
5B |
|
F0 |
OUT
PORT [0] = ACC |
5C |
|
12 |
ACC
= 2# |
5D |
|
0A |
GOTO
A2# |
5E |
Output
sample 23 |
17 |
ACC
= 7# |
5F |
|
F0 |
OUT
PORT [0] = ACC |
60 |
|
12 |
ACC
= 2# |
61 |
|
0A |
GOTO
A2# |
62 |
Output
sample 24 |
16 |
ACC
= 6# |
63 |
|
F0 |
OUT
PORT [0] = ACC |
64 |
|
12 |
ACC
= 2# |
65 |
|
0A |
GOTO
A2# |
66 |
Output
sample 25 |
15 |
ACC
= 5# |
67 |
|
F0 |
OUT
PORT [0] = ACC |
68 |
|
10 |
ACC
= 0# |
69 |
|
0B |
GOTO
B0# |
6A |
Output
sample 26 |
17 |
ACC
= 7# |
6B |
|
F0 |
OUT
PORT [0] = ACC |
6C |
|
10 |
ACC
= 0# |
6D |
|
0B |
GOTO
B0# |
6E |
Output
sample 27 |
1B |
ACC
= B# |
6F |
|
F0 |
OUT
PORT [0] = ACC |
70 |
|
10 |
ACC
= 0# |
71 |
|
0B |
GOTO
B0# |
72 |
Output
sample 28 |
1C |
ACC
= C# |
73 |
|
F0 |
OUT
PORT [0] = ACC |
74 |
|
10 |
ACC
= 0# |
75 |
|
0B |
GOTO
B0# |
76 |
Output
sample 29 |
19 |
ACC
= 9# |
77 |
|
F0 |
OUT
PORT [0] = ACC |
78 |
|
10 |
ACC
= 0# |
79 |
|
0B |
GOTO
B0# |
7A |
Output
sample 30 |
13 |
ACC
= 3# |
7B |
|
F0 |
OUT
PORT [0] = ACC |
7C |
|
10 |
ACC
= 0# |
7D |
|
0B |
GOTO
B0# |
7E |
Output
sample 31 |
10 |
ACC
= 0# |
7F |
|
F0 |
OUT
PORT [0] = ACC |
80 |
|
10 |
ACC
= 0# |
81 |
|
0B |
GOTO
B0# |
82 |
Output
sample 32 |
14 |
ACC
= 4# |
83 |
|
F0 |
OUT
PORT [0] = ACC |
84 |
|
10 |
ACC
= 0# |
85 |
|
0B |
GOTO
B0# |
86 |
1st
Delay routine |
90 |
SET
NO CARRY |
87 |
|
30 |
ACC
= REG [0] |
88 |
|
52 |
ACC = Arithm f (ACC, 2#, CARRY) |
89 |
|
20 |
REG
[0] = ACC |
8A |
|
90 |
SET
NO CARRY |
8B |
|
E7 |
ACC
= IN PORT [7] |
8C |
|
2F |
REG
[F] = ACC |
8D |
|
3F |
ACC
= REG [F] |
8E |
|
51 |
ACC = Arithm f (ACC, 1#, CARRY) |
8F |
|
2F |
REG
[F] = ACC |
90 |
|
30 |
ACC
= REG [0] |
91 |
|
DC |
IF
CARRY GOTO Cx# |
92 |
|
1D |
ACC
= D# |
93 |
|
08 |
GOTO
8D# |
94 |
2nd
Delay routine |
90 |
SET
NO CARRY |
95 |
|
31 |
ACC
= REG [1] |
96 |
|
52 |
ACC = Arithm f (ACC, 2#, CARRY) |
97 |
|
21 |
REG
[1] = ACC |
98 |
|
90 |
SET
NO CARRY |
99 |
|
E7 |
ACC
= IN PORT [7] |
9A |
|
2F |
REG
[F] = ACC |
9B |
|
3F |
ACC
= REG [F] |
9C |
|
51 |
ACC = Arithm f (ACC, 1#, CARRY) |
9D |
|
2F |
REG
[F] = ACC |
9E |
|
31 |
ACC
= REG [1] |
9F |
|
DD |
IF
CARRY GOTO Dx# |
A0 |
|
1B |
ACC
= B# |
A1 |
|
09 |
GOTO
9B# |
A2 |
3rd
Delay routine |
90 |
SET
NO CARRY |
A3 |
|
32 |
ACC
= REG [2] |
A4 |
|
52 |
ACC = Arithm f (ACC, 2#, CARRY) |
A5 |
|
22 |
REG
[2] = ACC |
A6 |
|
90 |
SET
NO CARRY |
A7 |
|
E7 |
ACC
= IN PORT [7] |
A8 |
|
2F |
REG
[F] = ACC |
A9 |
|
3F |
ACC
= REG [F] |
AA |
|
51 |
ACC = Arithm f (ACC, 1#, CARRY) |
AB |
|
2F |
REG
[F] = ACC |
AC |
|
32 |
ACC
= REG [2] |
AD |
|
DE |
IF
CARRY GOTO Ex# |
AE |
|
19 |
ACC
= 9# |
AF |
|
0A |
GOTO
A9# |
B0 |
4th
Delay routine |
90 |
SET
NO CARRY |
B1 |
|
33 |
ACC
= REG [3] |
B2 |
|
52 |
ACC = Arithm f (ACC, 2#, CARRY) |
B3 |
|
23 |
REG
[3] = ACC |
B4 |
|
90 |
SET
NO CARRY |
B5 |
|
E7 |
ACC
= IN PORT [7] |
B6 |
|
2F |
REG
[F] = ACC |
B7 |
|
3F |
ACC
= REG [F] |
B8 |
|
51 |
ACC = Arithm f (ACC, 1#, CARRY) |
B9 |
|
2F |
REG
[F] = ACC |
BA |
|
33 |
ACC
= REG [3] |
BB |
|
DF |
IF
CARRY GOTO Fx# |
BC |
|
17 |
ACC
= 7# |
BD |
|
0B |
GOTO
B7# |
BE |
NOP |
10 |
ACC
= 0# |
BF |
NOP |
10 |
ACC
= 0# |
C0 |
1st
Jump Table |
1A |
ACC
= A# |
C1 |
|
00 |
GOTO
0A# |
C2 |
|
1E |
ACC
= E# |
C3 |
|
00 |
GOTO
0E# |
C4 |
|
12 |
ACC
= 2# |
C5 |
|
01 |
GOTO
12# |
C6 |
|
16 |
ACC
= 6# |
C7 |
|
01 |
GOTO
16# |
C8 |
|
1A |
ACC
= A# |
C9 |
|
01 |
GOTO
1A# |
CA |
|
1E |
ACC
= E# |
CB |
|
01 |
GOTO
1E# |
CC |
|
12 |
ACC
= 2# |
CD |
|
02 |
GOTO
22# |
CE |
|
16 |
ACC
= 6# |
CF |
|
02 |
GOTO
26# |
D0 |
2nd
Jump Table |
1A |
ACC
= A# |
D1 |
|
02 |
GOTO
2A# |
D2 |
|
1E |
ACC
= E# |
D3 |
|
02 |
GOTO
2E# |
D4 |
|
12 |
ACC
= 2# |
D5 |
|
03 |
GOTO
32# |
D6 |
|
16 |
ACC
= 6# |
D7 |
|
03 |
GOTO
36# |
D8 |
|
1A |
ACC
= A# |
D9 |
|
03 |
GOTO
3A# |
DA |
|
1E |
ACC
= E# |
DB |
|
03 |
GOTO
3E# |
DC |
|
12 |
ACC
= 2# |
DD |
|
04 |
GOTO
42# |
DE |
|
16 |
ACC
= 6# |
DF |
|
04 |
GOTO
46# |
E0 |
3rd
Jump Table |
1A |
ACC
= A# |
E1 |
|
04 |
GOTO
4A# |
E2 |
|
1E |
ACC
= E# |
E3 |
|
04 |
GOTO
4E# |
E4 |
|
12 |
ACC
= 2# |
E5 |
|
05 |
GOTO
52# |
E6 |
|
16 |
ACC
= 6# |
E7 |
|
05 |
GOTO
56# |
E8 |
|
1A |
ACC
= A# |
E9 |
|
05 |
GOTO
5A# |
EA |
|
1E |
ACC
= E# |
EB |
|
05 |
GOTO
5E# |
EC |
|
12 |
ACC
= 2# |
ED |
|
06 |
GOTO
62# |
EE |
|
16 |
ACC
= 6# |
EF |
|
06 |
GOTO
66# |
F0 |
4th
Jump Table |
1A |
ACC
= A# |
F1 |
|
06 |
GOTO
6A# |
F2 |
|
1E |
ACC
= E# |
F3 |
|
06 |
GOTO
6E# |
F4 |
|
12 |
ACC
= 2# |
F5 |
|
07 |
GOTO
72# |
F6 |
|
16 |
ACC
= 6# |
F7 |
|
07 |
GOTO
76# |
F8 |
|
1A |
ACC
= A# |
F9 |
|
07 |
GOTO
7A# |
FA |
|
1E |
ACC
= E# |
FB |
|
07 |
GOTO
7E# |
FC |
|
12 |
ACC
= 2# |
FD |
|
08 |
GOTO
82# |
FE |
|
16 |
ACC
= 6# |
FF |
|
00 |
GOTO
06# |
|