/* * Author: Pawel Jablonski * E-mail: pj@xirx.net * WWW: xirx.net * GIT: git.xirx.net * * License: You can use this code however you like * but leave information about the original author. * Code is free for non-commercial and commercial use. */ #include "io.h" /** * Constructor for the IO class. It connects all communication classes for IO operations. * * @param parent Parent object */ IO::IO(QObject *parent) : QObject(parent) { this->reset(); QObject::connect(&this->led, SIGNAL(updateRunSignal(bool)), this, SIGNAL(updateLEDRunSignal(bool))); QObject::connect(&this->led, SIGNAL(updateErrorSignal(bool)), this, SIGNAL(updateLEDErrorSignal(bool))); QObject::connect(&this->lcd, SIGNAL(updateBufferSignal(LCD::Buffer)), this, SIGNAL(updateLCDBufferSignal(LCD::Buffer))); QObject::connect(&this->rs232, SIGNAL(updateTxSignal(unsigned char)), this, SIGNAL(updateRS232TxSignal(unsigned char))); QObject::connect(&this->rs232, SIGNAL(updateRxSignal(unsigned char)), this, SIGNAL(updateRS232RxSignal(unsigned char))); QObject::connect(&this->rtc, SIGNAL(updateDateTimeSignal(QDateTime)), this, SIGNAL(updateRTCDateTimeSignal(QDateTime))); QObject::connect(&this->speaker, SIGNAL(updateStatusSignal(int)), this, SIGNAL(updateSpeakerStatusSignal(int))); QObject::connect(&this->keyboard, SIGNAL(dataReadySignal(bool)), this, SLOT(keyboardDataReadySlot(bool))); QObject::connect(&this->rs232, SIGNAL(dataReadySignal(bool)), this, SLOT(rs232DataReadySlot(bool))); } //! Destructor for the IO class IO::~IO() { QObject::disconnect(this); } //! Reset the IO buffers and trigger reset for all connected communication classes void IO::reset() { this->regSelected = 0; this->reg = {}; this->in = 0; this->keyboard.reset(); this->led.reset(); this->lcd.reset(); this->rs232.reset(); this->rtc.reset(); this->speaker.reset(); this->fs.reset(); } /** * Process a key press event * * @param key Pressed key code * @param modifiers Optional modifiers to pressed key */ void IO::keyboardKeyPress(int key, Qt::KeyboardModifiers modifiers) { this->keyboard.keyPress(key, modifiers); } /** * Set enable status of the run LED * * @param enable Enable status */ void IO::ledSetRun(bool enable) { this->led.setRun(enable); } /** * Set enable status of the error LED * * @param enable Enable status */ void IO::ledSetError(bool enable) { this->led.setError(enable); } /** * Process a received text event * * @param text Received text */ void IO::rs232Receive(const QString &text) { this->rs232.rx(text); } /** * Set date and time * * @param dateTime Date and time to set in own RTC format */ void IO::rtcSetDateTime(const QDateTime &dateTime) { this->rtc.setDateTime(dateTime); } /** * Set volume * * @param volume Volume level in range 0-100 */ void IO::speakerSetVolume(unsigned int volume) { this->speaker.setVolume(volume); } /** * Set path to the emulated file system on the local disk * * @param path Path to emulated file system */ void IO::fsSetPath(const QString &path) { this->fs.setPath(path); } /** * Process "keyboard data is ready to get" event * * @param ready Status of ready data to get */ void IO::keyboardDataReadySlot(bool ready) { if(ready) { this->in |= IN_KEYBOARD_READY_BIT; } else { this->in &= ~IN_KEYBOARD_READY_BIT; } emit inSignal(this->in); } /** * Process "RS232 receive data is ready to get" event * * @param ready Status of ready data to get */ void IO::rs232DataReadySlot(bool ready) { if(ready) { this->in |= IN_RS232_READY_BIT; } else { this->in &= ~IN_RS232_READY_BIT; } emit inSignal(this->in); } /** * Process "update the output register value" event. * Fill up the output register data half with corresponding data from selected virtual register. * * @param out Output register value with set up data of the flags half */ void IO::outSlot(unsigned char out) { if((out & OUT_DATA_READY_BIT) != 0) { unsigned char value = out; bool half = ((value & OUT_HALF_BIT) != 0); bool mode = ((value & OUT_MODE_BIT) != 0); bool rw = ((value & OUT_RW_BIT) != 0); if(!mode) { // Select register to work regSelected = (value & REG_SELECT_MASK); } else { if(!rw) { unsigned char dataOut = 0x00; // Read register value switch(regSelected) { case REG_KEYBOARD : { if(!half) { this->reg.keyboard = this->keyboard.getKey(); } dataOut = this->reg.keyboard; } break; case REG_RS232_RX : { if(!half) { this->reg.rs232Rx = this->rs232.receive(); } dataOut = this->reg.rs232Rx; } break; case REG_RTC_FIELD : switch(this->reg.address.low) { case RTC_YEAR : dataOut = this->reg.rtcField.year; break; case RTC_MONTH : dataOut = this->reg.rtcField.month; break; case RTC_DAY : dataOut = this->reg.rtcField.day; break; case RTC_HOUR : dataOut = this->reg.rtcField.hour; break; case RTC_MINUTE : dataOut = this->reg.rtcField.minute; break; case RTC_SECOND : dataOut = this->reg.rtcField.second; break; default : dataOut = 0; break; } break; case REG_SPEAKER_FIELD : { if(!half) { this->reg.speakerBufferFree = this->speaker.getBufferFree(); } dataOut = this->reg.speakerBufferFree; } break; case REG_FS_DATA : { dataOut = this->reg.fsBlock.data[this->reg.address.low]; if(half) { this->reg.address.low++; } } break; case REG_FS_COMMAND : switch(this->reg.address.low) { case FS_OPEN_APP : { this->reg.fsName.data[FS::NAME_MAX_LENGTH] = 0; if(this->fs.open("app", this->reg.fsName, this->reg.fsSize)) { dataOut = 1; } else { dataOut = 0; } } break; case FS_OPEN_FILE : { this->reg.fsName.data[FS::NAME_MAX_LENGTH] = 0; if(this->fs.open("file", this->reg.fsName, this->reg.fsSize)) { dataOut = 1; } else { dataOut = 0; } } break; case FS_LIST_APP : { if(this->fs.list("app", this->reg.fsSize)) { dataOut = 1; } else { dataOut = 0; } } break; case FS_LIST_FILE : { if(this->fs.list("file", this->reg.fsSize)) { dataOut = 1; } else { dataOut = 0; } } break; case FS_SIZE : switch(this->reg.address.high) { case 0 : dataOut = this->reg.fsSize.low; break; case 1 : dataOut = this->reg.fsSize.high; break; default: dataOut = 0; break; } break; default : dataOut = 0; break; } break; default : dataOut = 0; break; } if(half) { dataOut >>= 4; } // Register value to out this->in = ((this->in & IN_CONTROL_MASK) | (dataOut & IN_DATA_MASK)); } else { unsigned char dataInRaw = (value & IN_DATA_MASK); unsigned char dataIn = dataInRaw; unsigned char mask = HALF_UP_DATA_MASK; if(half) { dataIn <<= HALF_DATA_OFFSET; mask >>= HALF_DATA_OFFSET; } // Write register value switch(this->regSelected) { case REG_LED_COMMAND : switch(dataInRaw) { case LED_RUN_OFF : this->led.setRun(false); break; case LED_RUN_ON : this->led.setRun(true); break; case LED_ERROR_OFF : this->led.setError(false); break; case LED_ERROR_ON : this->led.setError(true); break; } break; case REG_ADDRESS_LOW : this->reg.address.low = ((this->reg.address.low & mask) | dataIn); break; case REG_ADDRESS_HIGH : this->reg.address.high = ((this->reg.address.high & mask) | dataIn); break; case REG_LCD_COLOR : if(!half) { this->reg.lcdForegroundColor = dataInRaw; } else { this->reg.lcdBackgroundColor = dataInRaw; } break; case REG_LCD_CHAR : this->reg.lcdChar = ((this->reg.lcdChar & mask) | dataIn); if(half) { this->lcd.setChar(this->reg.lcdChar, this->reg.address.low, this->reg.address.high, this->reg.lcdForegroundColor, this->reg.lcdBackgroundColor); } break; case REG_LCD_COMMAND : switch(dataIn) { case LCD_CLEAR : this->lcd.clear(); this->lcd.refresh(); this->lcd.update(); break; case LCD_REFRESH : this->lcd.refresh(); this->lcd.update(); break; case LCD_SCROLL : this->lcd.scroll(); break; } break; case REG_RS232_TX : this->reg.rs232Tx = ((this->reg.rs232Tx & mask) | dataIn); if(half) { this->rs232.send(this->reg.rs232Tx); } break; case REG_RTC_FIELD : switch(this->reg.address.low) { case RTC_YEAR : this->reg.rtcField.year = ((this->reg.rtcField.year & mask) | dataIn); break; case RTC_MONTH : this->reg.rtcField.month = ((this->reg.rtcField.month & mask) | dataIn); break; case RTC_DAY : this->reg.rtcField.day = ((this->reg.rtcField.day & mask) | dataIn); break; case RTC_HOUR : this->reg.rtcField.hour = ((this->reg.rtcField.hour & mask) | dataIn); break; case RTC_MINUTE : this->reg.rtcField.minute = ((this->reg.rtcField.minute & mask) | dataIn); break; case RTC_SECOND : this->reg.rtcField.second = ((this->reg.rtcField.second & mask) | dataIn); break; } break; case REG_RTC_COMMAND : switch(dataInRaw) { case RTC_READ : this->reg.rtcField = this->rtc.getDateTime(); break; case RTC_WRITE : this->rtc.setDateTime(this->reg.rtcField); break; } break; case REG_SPEAKER_FIELD : switch(this->reg.address.low) { case SPEAKER_NOTE : this->reg.speakerField.note = ((this->reg.speakerField.note & mask) | dataIn); break; case SPEAKER_TIME : this->reg.speakerField.time = ((this->reg.speakerField.time & mask) | dataIn); break; case SPEAKER_FILL : this->reg.speakerField.fill = dataInRaw; break; case SPEAKER_VOLUME : this->reg.speakerField.volume = dataInRaw; break; } break; case REG_SPEAKER_COMMAND : switch(dataInRaw) { case SPEAKER_CLEAR : this->speaker.clear(); break; case SPEAKER_ADD_NOTE : this->speaker.addNote(this->reg.speakerField); break; } break; case REG_FS_NAME : if(this->reg.address.low < FS::NAME_MAX_LENGTH) { this->reg.fsName.data[this->reg.address.low] = ((this->reg.fsName.data[this->reg.address.low] & mask) | dataIn); } break; case REG_FS_COMMAND : switch(dataInRaw) { case FS_READ_BLOCK : this->fs.readBlock(this->reg.fsBlock, this->reg.address.low); break; } break; } } } // Operation is completed this->in ^= IN_OPERATION_COMPLETE_BIT; emit inSignal(this->in); } }