Emulating 8237A DMA Controller?

I've written a script emulating a DMA controller. Anyone knows if this is correct?

Sources:
http://pdos.csail.mit.edu/6.828/2004/readings/hardware/8237A.pdf
http://wiki.osdev.org/DMA

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
197
198
199
200
201
202
203
204
205
/*

DMA Controller (8237A)

*/

#include "headers/types.h" //Basic types!
#include "headers/hardware/ports.h" //Port support!

typedef void (*DMAWriteBHandler)(byte *EOP, byte data); //Write handler to DMA hardware!
typedef byte (*DMAReadBHandler)(byte *EOP); //Read handler from DMA hardware!
typedef void (*DMAWriteWHandler)(byte *EOP, word data); //Write handler to DMA hardware!
typedef word (*DMAReadWHandler)(byte *EOP); //Read handler from DMA hardware!


typedef struct
{
	DMAModeRegister ModeRegister; //Our mode register!
	word CurrentAddressRegister; //Start address register!
	word BaseAddressRegister; //Start address base register when resetting (base)! Written together with the start address!
	word CurrentCountRegister; //Count register set by the user and counting!
	word BaseCountRegister; //Current count register when resetting (base)! Written together with the counter!
	byte PageAddressRegister; //Upper 8 bits of the 24-bit transfer memory address!
	DMAWriteBHandler WriteBHandler; //Write handler 8-bit!
	DMAReadBHandler ReadBHandler; //Read handler 8-bit
	DMAWriteWHandler WriteWHandler; //Write handler 16-bit!
	DMAReadWHandler ReadWHandler; //Read handler 16-bit!
} DMAChannelTYPE; //Contains all info about an DMA channel!

typedef union
{
	struct
	{
		byte SEL : 2; //Which channel are we?
		byte TransferType : 2; //00=Self test, 1=Write to memory, 2=Read from memory, 3=Invalid.
		byte Auto : 1; //After the transfer has completed, Reset to the address and count values originally programmed.
		byte Down : 1; //Top-down processing if set, else increment addresses.
		byte Mode : 2; //Mode: 0=Transfer on Demand, 1=Single DMA Transfer, 2=Block DMA transfer, 3=Cascade Mode (Used to 

cascade another DMA controller).
	}; //The mode register!
	byte ModeRegisterB; //Mode register for all 4 channels!
} DMAModeRegister;

typedef struct
{
	//Public registers
	DMAChannelTYPE DMAChannel[4]; //4 DMA Channels per Controller!
	union
	{
		struct
		{
			byte TransferComplete : 4; //Transfer complete for 4 channels (high to low, bit 0=TC0.)
			byte RequestPending : 4; //Request pending for 4 channels (high to low, bit 0=REQ0.)
		};
		byte StatusRegister; //Status register for a DMA controller!
	}; //A Status register!
	byte CommandRegister; //Command Register
	byte RequestRegister; //Request Register!
	byte MultiChannelMaskRegister; //MultiChennel Mask Register, bit0-3=channel 0-3; 0=unmask (enabled), 1=mask (disabled)
	
	//Internal registers!
	byte FlipFlop; //Current flipflop: Cannot be accessed by software!
	byte IntermediateRegister; //Intermediate register!
	//Master reset register doesn't store!
	//MaskResetRegister doesn't store!
} DMAControllerTYPE; //Contains a DMA Controller's registers!

DMAControllerTYPE DMAController[2]; //We have 2 DMA Controllers!

void initDMAControllers() //Init function for BIOS!
{
memset(&DMAController[0],0,sizeof(DMAController)); //Init DMA Controller channels 0-3 (0 unused: for DRAM Refresh)
memset(&DMAController[1],0,sizeof(DMAController)); //Init DMA Controller channels 4-7 (4 unused: for DMA Controller coupling)
}

//Easy sets of high and low nibbles (word data)!
#define SETHIGH(b,v) b=((b&0xFF)|(v<<8))
#define SETLOW(b,v) b = ((b&0xFF00)|v)

void DMA_WriteIO(word port, byte value) //Handles OUT instructions to I/O ports.
{
byte controller = (port>=0xC0)?1:0; //What controller?
byte reg = port; //What register is selected, default to 1:1 mapping?
if (controller) //16-bit register (second DMA controller)?
{
	reg -= 0xC0; //Take the base!
	reg >>= 1; //Every port is on a offset of 2!
	//Now reg is on 1:1 mapping too!
}
byte channel; //Which channel to use?
switch (port) //What port?
{
	//Extra 8 bits for addresses:
	case 0x87: //
		DMAController[0].DMAChannel[0].PageAddressRegister = value; //Set!
		break;
	case 0x83: //
		DMAController[0].DMAChannel[1].PageAddressRegister = value; //Set!
		break;
	case 0x81: //
		DMAController[0].DMAChannel[2].PageAddressRegister = value; //Set!
		break;
	case 0x82: //
		DMAController[0].DMAChannel[3].PageAddressRegister = value; //Set!
		break;
	//Extra 8 bits for addresses:
	case 0x8F: //
		DMAController[1].DMAChannel[0].PageAddressRegister = value; //Set!
		break;
	case 0x8B: //
		DMAController[1].DMAChannel[1].PageAddressRegister = value; //Set!
		break;
	case 0x89: //
		DMAController[1].DMAChannel[2].PageAddressRegister = value; //Set!
		break;
	case 0x8A: //
		DMAController[1].DMAChannel[3].PageAddressRegister = value; //Set!
		break;
	default: //Unknown port?
		break;
	default: //Non-page register!
		switch (reg) //What register is selected?
		{
			case 0x00:
			case 0x02:
			case 0x04:
			case 0x06: //Address register?
				channel = port>>1; //What channel?
				if (DMAController[controller].FlipFlop) //High?
				{
					SETHIGH(DMAController[controller].DMAChannel[channel].CurrentAddressRegister,value); //Set high 

nibble!
					SETHIGH(DMAController[controller].DMAChannel[channel].BaseAddressRegister,value); //Set high 

nibble!
				}
				else //Low?
				{
					SETLOW(DMAController[controller].DMAChannel[channel].CurrentAddressRegister,value); //Set low 

nibble!
					SETLOW(DMAController[controller].DMAChannel[channel].BaseAddressRegister,value); //Set low 

nibble!
				}
				DMAController[controller].FlipFlop = !DMAController[controller].FlipFlop; //Flipflop!
				break;
				
			case 0x01:
			case 0x03:
			case 0x05:
			case 0x07: //Count register?
				channel = (port-1)>>1; //What channel?
				if (DMAController[controller].FlipFlop) //High?
				{
					SETHIGH(DMAController[controller].DMAChannel[channel].CurrentCountRegister,value); //Set high 

nibble!
					SETHIGH(DMAController[controller].DMAChannel[channel].BaseCountRegister,value); //Set high 

nibble!
				}
				else //Low?
				{
					SETLOW(DMAController[controller].DMAChannel[channel].CurrentCountRegister,value); //Set low 

nibble!
					SETLOW(DMAController[controller].DMAChannel[channel].BaseCountRegister,value); //Set low nibble!
				}
				DMAController[controller].FlipFlop = !DMAController[controller].FlipFlop; //Flipflop!
				break;

			case 0x08: //Command register!
				DMAController[controller].CommandRegister = value; //Set!
				break;
			case 0x09: //Request register!
				DMAController[controller].RequestRegister = value; //Set!
				break;
			case 0x0A: //Single Channel Mask Register!
				DMAController[controller].MultiChannelMaskRegister &= ~(1<<(value&3)); //Turn off the channel!
				DMAController[controller].MultiChannelMaskRegister |= ((value&4)>>2)<<(value&3)); //Mask it if needed!
				break;
			case 0x0B: //Mode Register!
				DMAController[controller].ModeRegisters[value&0x3].ModeRegister = value; //Set!
				break;
			case 0x0C: //Flip-Flop Reset Register!
				DMAController[controller].FlipFlop = 0; //Reset!
				break;
			case 0x0D: //Master Reset Register!
				DMAController[controller].FlipFlop = 0; //Reset!
				DMAController[controller].StatusRegister = 0; //Reset!
				DMAController[controller].MultiChannelMaskRegister |= 0xF; //Set the masks!
				break;
			case 0x0E: //Mask Reset Register!
				DMAController[controller].MultiChannelMaskRegister &= ~0xF; //Clear the masks!
				break;
			case 0x0F: //MultiChannel Mask Register!
				DMAController[controller].MultiChannelMaskRegister = value; //Set!
				break;
		}
		break;
	}
}
Continuation of the file:

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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
byte DMA_ReadIO(word port) //Handles IN instruction from CPU I/O ports
{
byte result; //To hold the result!
byte controller = (port>=0xC0)?1:0; //What controller?
byte reg = port; //What register is selected, default to 1:1 mapping?
if (controller) //16-bit register (second DMA controller)?
{
	reg -= 0xC0; //Take the base!
	reg >>= 1; //Every port is on a offset of 2!
	//Now reg is on 1:1 mapping too!
}
switch (port) //What port?
{
	//Extra 8 bits for addresses:
	case 0x87: //
		return DMAController[0].DMAChannel[0].PageAddressRegister; //Get!
		break;
	case 0x83: //
		return DMAController[0].DMAChannel[1].PageAddressRegister; //Get!
		break;
	case 0x81: //
		return DMAController[0].DMAChannel[2].PageAddressRegister; //Get!
		break;
	case 0x82: //
		return DMAController[0].DMAChannel[3].PageAddressRegister; //Get!
		break;
	//Extra 8 bits for addresses:
	case 0x8F: //
		return DMAController[1].DMAChannel[0].PageAddressRegister; //Get!
		break;
	case 0x8B: //
		return DMAController[1].DMAChannel[1].PageAddressRegister; //Get!
		break;
	case 0x89: //
		return DMAController[1].DMAChannel[2].PageAddressRegister; //Get!
		break;
	case 0x8A: //
		return DMAController[1].DMAChannel[3].PageAddressRegister; //Get!
		break;	
	default: //Non-page register!
		switch (reg) //What register is selected?
		{
			//Controller 0!
			case 0x08: //Status Register!
				result = DMAController[0].StatusRegister; //Get!
				DMAController[0].StatusRegister &= ~0xF; //Clear TC bits!
				return result; //Get!
				break;
			case 0x0D: //Intermediate Register!
				return DMAController[0].IntermediateRegister; //Get!
				break;
			case 0x0F: //MultiChannel Mask Register!
				return DMAController[0].MultiChannelMaskRegister; //Get!
				break;
			default: //Unknown port?
				break;
		}
		break;
}
return ~0; //Unknown port!
}

void DMA_autoinit(byte controller, byte channel) //Autoinit functionality.
{
	DMAControllers[controller].DMAChannel[channel].CurrentAddressRegister = DMAControllers[controller].DMAChannel

[channel].CurrentAddressRegisterReset; //Reset address register!
	DMAControllers[controller].DMAChannel[channel].CurrentCountRegister = DMAControllers[controller].DMAChannel

[channel].CurrentCountRegisterReset; //Reset count register!
}

//Flags for different responses that might need to be met.
#define FLAG_TC 1
#define FLAG_EOP 2

/* Main DMA Controller processing thread */
void DMA_Thread()
{
	byte current = 0; //Current channel in total (0-7)
	nextcycle: //Next cycle to process!
		byte controller = ((current&4)>>2); //Init controller
		byte channel = (current&3); //Channel to use! Channels 0 are unused (DRAM memory refresh (controller 0) and cascade DMA 

controller (controller 1))
		if (!(DMAControllers[controller].CommandRegister&4) && channel) //Controller not disabled and valid channel to transfer?
		{
			DMAModeRegister moderegister;
			moderegister.ModeRegisterB = DMAControllers[controller].ModeRegisters[channel].ModeRegisterB; //Read the mode 

register to use!
			if (moderegister.Mode==3) goto nextchannel; //Skip channel: invalid!
			if (DMAControllers[controller].RequestPending&(1<<channel) && (DMAControllers[controller].TransferComplete&

(1<<channel))) //Request pending and doing nothing atm?
			{
				DMAControllers[controller].TransferComplete &= ~(1<<channel); //Clear transfer complete: we're pending!
				DMAControllers[controller].RequestPending &= ~(1<<channel); //Clear pending: we're not pending anymore!
			}
			if (!DMAControllers[controller].MultiChannelMaskRegister&(1<<channel)) //Channel not masked off?
			{
				if (!(DMAControllers[controller].TransferComplete&(1<<channel))) //Transfer not yet complete? We're 

running the transfer a bit!
				{
					byte processed = 0; //Default: nothing going on!
					/*
					processed bits:
					bit 0: TC (Terminal Count) occurred.
					bit 1: External EOP encountered.
					*/
					uint_32 address; //The address to use!
					if (controller) //16-bits transfer has a special addressing scheme?
					{
						address = DMAControllers[controller].DMAChannel[channel].StartAddress; //Load the start 

address!
						address <<= 1; //Shift left by 1 to obtain a multiple of 2!
						address &= 0xFFFF; //Clear the overflowing bit, if any!
					}
					else //8-bit has normal addressing!
					{
						address = DMAControllers[controller].DMAChannel[channel].StartAddress; //Normal 

addressing!
					}
					address |= (DMAControllers[controller].DMAChannel[channel].PageAddressRegister<<16); //Apply 

page address to get the full address!
					byte EOP = 0; //EOP detected?
					switch (moderegister.TransferType)
					{
						case 0: //???
							break;
						case 1: //Writing to memory? (Reading from device)
							if (controller) //16-bits?
							{
								if (DMAControllers[controller].DMAChannel[channel].ReadWHandler) //Valid 

handler?
								{
									MMU_directww(address,DMAControllers[controller].DMAChannel

[channel].ReadWHandler(&EOP)); //Read using handler!
								}
							}
							else //8-bits?
							{
								if (DMAControllers[controller].DMAChannel[channel].ReadBHandler) //Valid 

handler?
								{
									MMU_directwb(address,DMAControllers[controller].DMAChannel

[channel].ReadBHandler(&EOP)); //Read using handler!
								}
							}
							break;
						case 2: //Reading from memory? (Writing to device)
							if (controller) //16-bits?
							{
								if (DMAControllers[controller].DMAChannel[channel].WriteWHandler) 

//Valid handler?
								{
									DMAControllers[controller].DMAChannel[channel].WriteWHandler

(&EOP,MMU_directrw(address)); //Read using handler!
								}
							}
							else //8-bits?
							{
								if (DMAControllers[controller].DMAChannel[channel].WriteBHandler) 

//Valid handler?
								{
									DMAControllers[controller].DMAChannel[channel].WriteBHandler

(&EOP,MMU_directrb(address)); //Read using handler!
								}
							}
							break;
						default: //Invalid?
							break;
					}
					if (EOP)
					{
						processed |= FLAG_EOP; //Set EOP flag!
					}

					//Process the address counter step: we've been processed and ready to move on!
					if (moderegister.Down) //Decrease address?
					{
						--DMAControllers[controller].DMAChannel[channel].CurrentAddressRegister; //Decrease 

counter!
					}
					else //Increase counter?
					{
						++DMAControllers[controller].DMAChannel[channel].CurrentAddressRegister; //Decrease 

counter!
					}
					--DMAControllers[controller].DMAChannel[channel].CurrentCountRegister; //Next step calculated!
					if (DMAControllers[controller].DMAChannel[channel].CurrentCountRegister==0xFFFF) //Finished when 

overflows below 0!
					{
						processed |= FLAG_TC; //Set flag: terminal count occurred!
						DMAControllers[controller].TransferComplete |= (1<<channel); //Transfer complete!		

					}
						//Process all flags that has occurred!
				
					switch (moderegister.Mode) //What mode are we processing in?
					{
						case 0: //Single Transfer Mode
							if (moderegister.Auto && (processed&FLAG_TC)) //AutoInit on TC!
							{
								DMA_AutoInit(controller,channel); //Perform utoInit!
							}
							break;
						case 1: //Block Transfer Mode
							if (moderegister.Auto && (processed&(FLAG_TC|FLAG_EOP))) //AutoInit on TC/EOP!
							{
								DMA_AutoInit(controller,channel); //Perform autoinit!
							}
							break;
						case 2: //Demand Transfer Mode
							if (moderegister.Auto && (processed&(FLAG_TC|FLAG_EOP))) //AutoInit on TC/EOP!
							{
								DMA_AutoInit(controller,channel); //Perform autoinit!
							}
							break;
					}
				}
			}
		}
	nextchannel: //Skipping this channel (used by cascade mode channels)
		if (++current&(~0x7)) //Last controller finished (overflow channel counter)?
		{
			current = 0; //Reset controller!
			delay(1); //Wait a bit to give other threads some time!
		}
		goto nextcycle; //Next cycle!
}
Topic archived. No new replies allowed.