forked from gardners/c65gs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
etherkick.c
186 lines (160 loc) · 5.36 KB
/
etherkick.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
/* Sample UDP client */
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <fcntl.h>
unsigned char all_done_routine[128]={
0xa9, 0x00, // LDA #$00 so that kickstart recognises packet
0x8d, 0x54, 0xd0, // Clear 16-bit character mode etc, just to be sure
0xee,0x27,0x04, // increment $0427 for visual debug indicator
0x4c, 0x1f, 0x08 // jmp to $081F, which should be mapped to $000081F.
};
// Routine to copy memory from $0004000-$0007FFF to $FFF8000-$FFFBFFF,
// and then jump to $8100 to simulate reset.
// Actually, we jump to $8200, which by convention must have a reset entry point
// that disables etherkick until next boot, so that no switch fiddling is required.
unsigned char kickstart_replace_routine[128]={
0xa9, 0x00, 0x5b, 0xa9, 0x00, 0x85, 0x80, 0xa9,
0x40, 0x85, 0x81, 0xa9, 0x00, 0x85, 0x82, 0xa9,
0x00, 0x85, 0x83, 0xa9, 0x00, 0x85, 0x84, 0xa9,
0x80, 0x85, 0x85, 0xa9, 0xff, 0x85, 0x86, 0xa9,
0x0f, 0x85, 0x87, 0xa3, 0x00, 0xea, 0xb2, 0x80,
0xea, 0x92, 0x84, 0x1b, 0xd0, 0xf7, 0xe6, 0x81,
0xe6, 0x85, 0xa5, 0x81, 0xc9, 0x80, 0xd0, 0xed,
0x4c, 0x00, 0x81
};
unsigned char dma_load_routine[128+1024]={
// Routine that copies packet contents by DMA
0xa9, 0xff, 0x8d, 0x05, 0xd7, 0xad, 0x68, 0x68,
0x8d, 0x06, 0xd7, 0xa9, 0x0d, 0x8d, 0x02, 0xd7,
0xa9, 0xe8, 0x8d, 0x01, 0xd7, 0xa9, 0xff, 0x8d,
0x04, 0xd7, 0xa9, 0x5c, 0x8d, 0x00, 0xd7, 0xae,
0x67, 0x68, 0xea, 0x9d, 0x80, 0x06, 0xee, 0x26,
0x04, 0xd0, 0x03, 0xee, 0x25, 0x04, 0x60, 0x00,
// DMA list begins at offset $0030
0x00, // DMA command ($0030)
#define BYTE_COUNT_OFFSET 0x31
0x00, 0x04, // DMA byte count ($0031-$0032)
0x80, 0xe8, 0x8d, // DMA source address (points to data in packet)
#define DESTINATION_ADDRESS_OFFSET 0x36
0x00, 0x10, // DMA Destination address (bottom 16 bits)
#define DESTINATION_BANK_OFFSET 0x38
0x00, // DMA Destination bank
0x00, 0x00, // DMA modulo (ignored)
// Packet ID number at offset $003B
#define PACKET_NUMBER_OFFSET 0x3b
0x30,
#define DESTINATION_MB_OFFSET 0x3c
// Destination MB at $003C
0x00,
0x00, 0x00, 0x00
#define DATA_OFFSET (0x80 - 0x2c)
};
// Test routine to increment border colour
unsigned char test_routine[64]={
0xa9,0x00,0xee,0x21,0xd0,0x60
};
int usage()
{
printf("usage: etherkick <run|kickup> <IP address> <programme>\n");
printf(" etherkick push <IP address> <file> <28-bit address (hex)>\n");
exit(1);
}
int main(int argc, char**argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc < 4)
{
printf("Too few arguments.\n");
usage();
}
int runmode=0;
int address=-1;
if (!strcmp(argv[1],"run")) runmode=1;
else if (!strcmp(argv[1],"kickup")) runmode=0;
else if (!strcmp(argv[1],"push")) {
runmode=2;
if (argc<5) {
printf("Too few arguments for push (argc=%d)\n",argc);
usage();
}
address=strtoll(argv[4],NULL,16);
} else {
usage();
}
sockfd=socket(AF_INET,SOCK_DGRAM,0);
int broadcastEnable=1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=inet_addr(argv[2]);
servaddr.sin_port=htons(4511);
int fd=open(argv[3],O_RDWR);
if (fd<0) {
fprintf(stderr,"Could not open file '%s'\n",argv[3]);
exit(-1);
}
unsigned char buffer[1024];
int offset=0;
int bytes;
if (runmode==1) {
// Read 2 byte load address
bytes=read(fd,buffer,2);
if (bytes<2) {
fprintf(stderr,"Failed to read load address from file '%s'\n",
argv[2]);
exit(-1);
}
address=buffer[0]+256*buffer[1];
printf("Load address of programme is $%04x\n",address);
} else if (runmode==0) {
printf("Upgrading kickstart: load address fixed at $4000\n");
address=0x4000;
} else {
printf("Load address is $%07x\n",address);
}
while((bytes=read(fd,buffer,1024))!=0)
{
printf("Read %d bytes at offset %d\n",bytes,offset);
offset+=bytes;
// Set load address of packet
dma_load_routine[DESTINATION_ADDRESS_OFFSET]=address&0xff;
dma_load_routine[DESTINATION_ADDRESS_OFFSET+1]=(address>>8)&0xff;
dma_load_routine[DESTINATION_BANK_OFFSET]=(address>>16)&0x0f;
dma_load_routine[DESTINATION_MB_OFFSET]=(address>>20)&0xff;
// Copy data into packet
bcopy(buffer,&dma_load_routine[DATA_OFFSET],bytes);
sendto(sockfd,dma_load_routine,sizeof dma_load_routine,0,
(struct sockaddr *)&servaddr,sizeof(servaddr));
usleep(150);
dma_load_routine[PACKET_NUMBER_OFFSET]++;
address+=bytes;
}
if (runmode==1) {
// Tell C65GS that we are all done
int i;
printf("Trying to start program ...\n");
for(i=0;i<10;i++) {
sendto(sockfd,all_done_routine,sizeof all_done_routine,0,
(struct sockaddr *)&servaddr,sizeof(servaddr));
usleep(150);
}
} else if (runmode==0) {
int i;
printf("Telling kickstart to upgrade ...\n");
for(i=0;i<10;i++) {
sendto(sockfd,kickstart_replace_routine,sizeof kickstart_replace_routine,0,
(struct sockaddr *)&servaddr,sizeof(servaddr));
usleep(150);
}
} else {
printf("Push mode -- leaving C65GS in etherkick.\n");
}
return 0;
}