#include #include #include #include #include "control.h" #include "disk.h" #include "io.h" #include "bsp_driver_sd.h" #include "profiling.h" // The top 4 address bits determine which device is used. (16 pages, 256 devices) // 0xFF means that the device does not exist // WATCH OUT THE DEVICE ADDRESS NEED TO BE REVERSED (MSB IS ON THE RIGHT) uint8_t memory_map_0[16] = {0b00010000, 0b10001000, 0b01001000, 0b11001000, 0b00101000, 0b10101000, 0b01101000, 0b11101000, 0b00011000, 0b10011000, 0b01011000, 0b11011000, 0b00111000, 0b10111000, 0b01111000, 0b11111000}; uint8_t memory_map_1[16] = {0b00001000, 0b10001000, 0b01001000, 0b11001000, 0b00101000, 0b10101000, 0b01101000, 0b11101000, 0b00011000, 0b10011000, 0b01011000, 0b11011000, 0b00111000, 0b10111000, 0b01111000, 0b11111000}; Control control; extern UART_HandleTypeDef huart2; extern uint8_t ack[1]; extern uint8_t nack[1]; uint8_t get_device(uint16_t address) { uint8_t page = address >> 12; if (control.memory_config == 0) { return memory_map_0[page]; } else if (control.memory_config == 1) { return memory_map_1[page]; } return 0xFF; } void control_program_eeprom(uint8_t* data, uint16_t length) { // Take control of the bus send_busrq(1); while (!has_busak()) { control_cycle(); } enable_address_out(1); select_device(memory_map_0[0]); for (uint16_t i = 0; i < length; ++i) { write_address(i); write_data(data[i]); enable_data_out(1); send_memrq(1); send_wr(1); send_wr(0); enable_data_out(0); send_rd(1); for (;;) { uint8_t d = read_data(); if (d == data[i]) { break; } } send_rd(0); send_memrq(0); uint8_t progress[] = {(i+1) & 0xFF, (i+1) >> 8}; HAL_UART_Transmit(&huart2, progress, sizeof(progress), HAL_MAX_DELAY); } enable_address_out(0); // Release the bus again send_busrq(0); // Restart the z80 control_reset(); HAL_UART_Transmit(&huart2, ack, sizeof(ack), HAL_MAX_DELAY); } void handle_memrq() { uint16_t address = read_address(); uint8_t device = get_device(address); select_device(device); } void handle_io_read() { uint8_t address = read_address() & 0xFF; switch (address) { // @todo This should be detected on startup // Stand in for graphics hardware /* case 0x03: */ /* write_data(0x01); */ /* break; */ /* Stand in for the keyboard hardware */ /* case 0x1E: */ /* write_data(char_c); */ /* char_r = 0; */ /* break; */ /* Stand in for the keyboard hardware */ /* case 0x1F: */ /* #<{(| write_data(0x01 * char_r); |)}># */ /* write_data(0x00); */ /* break; */ // Read byte from disk case 0x08: write_data(disk_read_byte()); break; // Check if disk is ready case 0x0f: write_data(0x08*disk_is_ready()); break; default: { /* uint8_t value = read_data(); */ /* #<{(| if (value == 0) { |)}># */ /* printf("IO Read: %.2X @ %.2X\n\r", value, address); */ /* #<{(| } |)}># */ return; } } enable_data_out(1); } void handle_io_write() { uint8_t address = read_address() & 0xFF; uint8_t value = read_data(); switch (address) { case 0x00: control.memory_config = 0; break; case 0x01: control.memory_config = 1; break; case 0x02: printf("%c", value); break; // Write byte to disk case 0x08: disk_write_byte(value); break; case 0x0b: { disk_set_lba_part(value, 0); break; } case 0x0c: { disk_set_lba_part(value, 1); break; } case 0x0d: { disk_set_lba_part(value, 2); break; } // Receive disk command case 0x0f: { disk_receive_command(value); break; } default: printf("IO Write: %.2X @ %.2X\n\r", value, address); break; } } void handle_ioreq() { if (has_wr()) { handle_io_write(); } else if (has_rd()) { handle_io_read(); } } void control_cycle() { set_clock(1); // We need this not detect IO multiple times static uint8_t had_ioreq = 0; if (!has_ioreq()) { had_ioreq = 0; } // @todo We are forgetting to set this somewhere enable_data_out(0); if (has_memrq()) { handle_memrq(); } else if (has_ioreq() && !has_m1()) { had_ioreq++; if (had_ioreq == 3) { handle_ioreq(); } } else if (has_ioreq() && has_m1()) { printf("Interrupt ackknowledged\n\r"); } set_clock(0); } // @todo Properly reset everything void control_reset() { Control temp = {0}; control = temp; disk_free(); disk_init(); set_reset(1); for (int i = 0; i <= 10; ++i) { set_clock(i % 2); } set_reset(0); }