-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.c
196 lines (151 loc) · 5.03 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// Marcel Timm, RhinoDevel, 2017dec30
// Tested with:
//
// - Commodore/CBM 3001 Series Computer 3032, PET 2001-32N C with Basic 1.0 / ROM v2
// - PET's user port pins interpreted as outputs have LOW level, if PET is powered off.
// - They are "set" to HIGH level (interpreted as outputs) during PET booting up.
// - Initially, the I/O user port pins 0-7 are configured as inputs: PEEK(59459) => 0
// - Such a pin can be configured as output via poking to 59459. E.g. for pin 1: POKE 59459,(PEEK(59459) OR 2) => LOW level (should initially be low..).
// - Output level can be set by poking to 59471. E.g. for pin 1: POKE 59471,(PEEK(59471) OR 2) => HIGH level.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include "gpio/gpio.h"
#include "FileSys.h"
#include "Sys.h"
#include "ProgressBar.h"
static const int pin_0_data_to_pet = 4;
static const int pin_1_read_ack_from_pet = 17;
static const int pin_2_wrt_rdy_to_pet = 27;
static const long pet_max_rise_nanoseconds = 2000;
static const long pet_max_fall_nanoseconds = 50.0;
static void sleep_nanoseconds(long const nanoseconds)
{
struct timespec d;
clock_gettime(CLOCK_MONOTONIC, &d);
//long const before = d.tv_nsec,
// before_s = d.tv_sec;
d.tv_nsec += nanoseconds;
if(d.tv_nsec >= 1000000000L)
{
d.tv_nsec -= 1000000000L;
++d.tv_sec;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &d, NULL);
//clock_gettime(CLOCK_MONOTONIC, &d);
//long const after = d.tv_nsec,
// after_s = d.tv_sec;
//if(before_s==after_s)
//{
// printf("Waited %d nanoseconds.\n", (int)(after-before));
//}
}
static void setup_pins()
{
// DATA to PET (init. val. shouldn't matter):
//
gpio_set_output(pin_0_data_to_pet, false);
gpio_set_input_pull_down(pin_1_read_ack_from_pet); // READ ACK from PET.
gpio_set_output(pin_2_wrt_rdy_to_pet, true); // WRITE READY to PET.
}
static void setup()
{
assert(gpio_init());
setup_pins();
}
static void set_output(int const pin_nr, bool const val)
{
gpio_write(pin_nr, val);
}
static bool get_input(int const pin_nr)
{
return gpio_read(pin_nr);
}
static void send_bit(unsigned char const bit)
{
assert(bit==0 || bit==1);
bool const expected_read_ack = get_input(pin_2_wrt_rdy_to_pet),
wrt_rdy = !expected_read_ack, // Just toggles.
val = (bool)bit;
long const pet_max_nanoseconds = expected_read_ack
? pet_max_rise_nanoseconds
: pet_max_fall_nanoseconds;
set_output(pin_0_data_to_pet, val); // DATA to PET.
set_output(pin_2_wrt_rdy_to_pet, wrt_rdy); // WRITE READY to PET.
while(get_input(pin_1_read_ack_from_pet)!=expected_read_ack)
{
sleep_nanoseconds(pet_max_nanoseconds); // TODO: Actually takes ~70us!
}
}
static void send_byte(unsigned char const byte)
{
assert(!get_input(pin_1_read_ack_from_pet));
assert(get_input(pin_2_wrt_rdy_to_pet));
for(int i = 0;i<8;++i)
{
send_bit(byte>>i & 1);
}
}
int main(int const argc, char * const argv[])
{
off_t byte_count = 0;
unsigned char h = 0,
l = 0;
if(argc!=2)
{
printf(
"Error: Please call application with PRG source file path as its single argument!\n");
return EXIT_FAILURE;
}
printf("Reading file content..\n");
unsigned char * const bytes = FileSys_loadFile(argv[1], &byte_count);
if(bytes==NULL)
{
printf("Error: Failed to read file content!\n");
return EXIT_FAILURE;
}
printf("Starting setup..\n");
setup();
unsigned int const start_addr = bytes[0]+256*bytes[1],
payload_len = byte_count-2;
if(!get_input(pin_1_read_ack_from_pet))
{
printf("Error: Input must be set to HIGH!\n");
free(bytes);
return EXIT_FAILURE;
}
printf("Waiting for READ ACK from PET to fall..\n");
while(get_input(pin_1_read_ack_from_pet))
{
sleep_nanoseconds(pet_max_fall_nanoseconds);
}
printf("Starting transfer of 2+2+%d bytes..\n", (int)payload_len);
h = start_addr/256;
l = start_addr-256*h;
printf("Sending start address low byte: %X..\n", l);
send_byte(l);
printf("Sending start address high byte: %X..\n", h);
send_byte(h);
h = payload_len/256;
l = payload_len-256*h;
printf("Sending payload length low byte: %X..\n", l);
send_byte(l);
printf("Sending payload length high byte: %X..\n", h);
send_byte(h);
uint64_t const t0 = Sys_get_posix_clock_time_ms();
printf("Sending payload (%d bytes)..\n", (int)payload_len);
for(int i = 0;i<payload_len;++i)
{
send_byte(bytes[i+2]);
ProgressBar_print(0, i+1, payload_len, 50, true);
}
printf("\n");
printf("Transfer done..\n");
uint64_t const t_diff = Sys_get_posix_clock_time_ms()-t0;
double const seconds = (double)t_diff/1000.0;
printf("Elapsed seconds for payload transfer: %f\n", seconds);
printf("Bytes per second for payload transfer: %f\n", payload_len/seconds);
printf("Done.\n");
return EXIT_SUCCESS;
}