diff --git a/demos/demo/demo_interrupt.h b/demos/demo/demo_interrupt.h index 6987759..e58fec8 100644 --- a/demos/demo/demo_interrupt.h +++ b/demos/demo/demo_interrupt.h @@ -10,7 +10,7 @@ void demo_interrupt() { _hours = 0; // installs the interrupt handler routine - install_interrupt(); + install_interrupt((word) &time_interrupt_handler); // enables interrupts on the TMS9918 tms_set_interrupt_bit(INTERRUPT_ENABLED); diff --git a/demos/sdcard/apple1_sdcard/apple1_sdcard.ino b/demos/sdcard/apple1_sdcard/apple1_sdcard.ino index 6c95db1..0c77584 100644 --- a/demos/sdcard/apple1_sdcard/apple1_sdcard.ino +++ b/demos/sdcard/apple1_sdcard/apple1_sdcard.ino @@ -177,6 +177,7 @@ const int CMD_CD = 13; const int CMD_MKDIR = 14; const int CMD_PWD = 19; const int CMD_RMDIR = 15; +const int CMD_TEST = 20; const int ERR_RESPONSE = 255; const int OK_RESPONSE = 0; @@ -833,7 +834,13 @@ void loop() { else if(data == CMD_CD) comando_cd(); else if(data == CMD_PWD) comando_pwd(); else if(data == CMD_DIR) comando_dir(data); - else if(data == CMD_LS) comando_dir(data); + else if(data == CMD_LS) comando_dir(data); + else if(data == CMD_TEST) { + while(!TIMEOUT) { + int data = receive_byte_from_cpu(); + send_byte_to_cpu(data ^ 0xFF); + } + } else { Serial.print(F("unknown command ")); Serial.print(data); diff --git a/demos/sdcard/cmd_test.h b/demos/sdcard/cmd_test.h new file mode 100644 index 0000000..3127ec7 --- /dev/null +++ b/demos/sdcard/cmd_test.h @@ -0,0 +1,29 @@ + +void comando_test() { + + woz_puts("\rTESTING\r"); + + send_byte_to_MCU(CMD_TEST); + + byte dsend = 0; + byte drec = 0; + + while(1) { + send_byte_to_MCU(dsend); + if(TIMEOUT) return; + + drec = receive_byte_from_MCU(); + drec ^= 0xff; + + if(drec != dsend) { + woz_puts("\rTRANSFER ERROR\r"); + return; + } + dsend++; + if(dsend == 0x00) woz_putc('*'); + } +} + +void test_via() { + +} \ No newline at end of file diff --git a/demos/sdcard/console.h b/demos/sdcard/console.h index 2d155b4..dbc1a10 100644 --- a/demos/sdcard/console.h +++ b/demos/sdcard/console.h @@ -38,8 +38,9 @@ const byte CMD_RMDIR = 15; const byte CMD_RM = 16; const byte CMD_MD = 17; const byte CMD_RD = 18; -const byte CMD_PWD = 19; -const byte CMD_EXIT = 20; +const byte CMD_PWD = 19; +const byte CMD_TEST = 20; +const byte CMD_EXIT = 21; // the list of recognized commands byte *DOS_COMMANDS[] = { @@ -63,6 +64,7 @@ byte *DOS_COMMANDS[] = { "MD", "RD", "PWD", + "TEST", "EXIT" }; @@ -137,8 +139,7 @@ void hex_to_word(byte *str) { #include "cmd_rmdir.h" #include "cmd_chdir.h" #include "cmd_pwd.h" - -#define PROMPT_ALWAYS 1 +#include "cmd_test.h" void console() { @@ -324,6 +325,9 @@ void console() { else if(cmd == CMD_PWD) { comando_pwd(); } + else if(cmd == CMD_TEST) { + comando_test(); + } else if(cmd == CMD_EXIT) { woz_puts("BYE\r"); woz_mon(); diff --git a/demos/sdcard/sdcard.c b/demos/sdcard/sdcard.c index cd04003..4ea855a 100644 --- a/demos/sdcard/sdcard.c +++ b/demos/sdcard/sdcard.c @@ -38,13 +38,25 @@ #include #include -byte *const PORTB = (byte *) 0xA000; // port B register -byte *const PORTA = (byte *) 0xA001; // port A register -byte *const DDRB = (byte *) 0xA002; // port A data direction register -byte *const DDRA = (byte *) 0xA003; // port B data direction register +byte *const VIA_PORTB = (byte *) 0xA000; // port B register +byte *const VIA_PORTA = (byte *) 0xA001; // port A register +byte *const VIA_DDRB = (byte *) 0xA002; // port A data direction register +byte *const VIA_DDRA = (byte *) 0xA003; // port B data direction register +byte *const VIA_T1CL = (byte *) 0xA004; // +byte *const VIA_T1CH = (byte *) 0xA005; // +byte *const VIA_T1LL = (byte *) 0xA006; // +byte *const VIA_T1LH = (byte *) 0xA007; // +byte *const VIA_T2CL = (byte *) 0xA008; // +byte *const VIA_T2CH = (byte *) 0xA009; // +byte *const VIA_SR = (byte *) 0xA00A; // +byte *const VIA_ACR = (byte *) 0xA00B; // +byte *const VIA_PCR = (byte *) 0xA00C; // +byte *const VIA_IFR = (byte *) 0xA00D; // +byte *const VIA_IER = (byte *) 0xA00E; // +byte *const VIA_PORTANH = (byte *) 0xA00F; // -#define CPU_STROBE(v) (*PORTB = (v)) /* CPU strobe is bit 0 OUTPUT */ -#define MCU_STROBE (*PORTB & 128) /* MCU strobe is bit 7 INPUT */ +#define CPU_STROBE(v) (*VIA_PORTB = (v)) /* CPU strobe is bit 0 OUTPUT */ +#define MCU_STROBE (*VIA_PORTB & 128) /* MCU strobe is bit 7 INPUT */ // global variables that are shared among the functions __address( 3) byte TIMEOUT; @@ -58,6 +70,10 @@ __address(16) byte hex_to_word_ok; __address(17) byte cmd; __address(18) byte *token_ptr; +#define MCU_STROBE_HIGH 128 +#define MCU_STROBE_LOW 0 + +/* void wait_mcu_strobe(byte v) { if(TIMEOUT) return; @@ -66,34 +82,47 @@ void wait_mcu_strobe(byte v) { TIMEOUT_CNT++; if(TIMEOUT_CNT > TIMEOUT_MAX) { TIMEOUT = 1; - return; + break; } - } + } +} +*/ + +void wait_mcu_strobe(byte v) { + if(TIMEOUT) return; + + TIMEOUT_CNT = 0; + while(v ^ MCU_STROBE) { + TIMEOUT_CNT++; + if(TIMEOUT_CNT > TIMEOUT_MAX) { + TIMEOUT = 1; + break; + } + } } -void send_byte_to_MCU(byte data) { - *DDRA = 0xFF; // set port A as output - *PORTA = data; // deposit byte on the data port - CPU_STROBE(1); // set strobe high - wait_mcu_strobe(1); // wait for the MCU to read the data - CPU_STROBE(0); // set strobe low - wait_mcu_strobe(0); // wait for the MCU to set strobe low +void send_byte_to_MCU(byte data) { + *VIA_DDRA = 0xFF; // set port A as output + *VIA_PORTA = data; // deposit byte on the data port + CPU_STROBE(1); // set strobe high + wait_mcu_strobe(MCU_STROBE_HIGH); // wait for the MCU to read the data + CPU_STROBE(0); // set strobe low + wait_mcu_strobe(MCU_STROBE_LOW); // wait for the MCU to set strobe low } -// note: allocates 1 byte for return value byte receive_byte_from_MCU() { - *DDRA = 0; // set port A as input - CPU_STROBE(0); // set listen - wait_mcu_strobe(1); // wait for the MCU to deposit data - byte data = *PORTA; // read data - CPU_STROBE(1); // set strobe high - wait_mcu_strobe(0); // wait for the MCU to set strobe low - CPU_STROBE(0); // set strobe low + *VIA_DDRA = 0; // set port A as input + CPU_STROBE(0); // set listen + wait_mcu_strobe(MCU_STROBE_HIGH); // wait for the MCU to deposit data + byte data = *VIA_PORTA; // read data + CPU_STROBE(1); // set strobe high + wait_mcu_strobe(MCU_STROBE_LOW); // wait for the MCU to set strobe low + CPU_STROBE(0); // set strobe low return data; } void VIA_init() { - *DDRB = 1; // pin 1 is output (CPU_STROBE), pin7 is input (MCU_STROBE) + *VIA_DDRB = 1; // pin 1 is output (CPU_STROBE), pin7 is input (MCU_STROBE) CPU_STROBE(0); // initial state } diff --git a/demos/viatimer/viatimer.c b/demos/viatimer/viatimer.c new file mode 100644 index 0000000..eb84af8 --- /dev/null +++ b/demos/viatimer/viatimer.c @@ -0,0 +1,112 @@ +#define APPLE1_USE_WOZ_MONITOR 1 + +//#pragma start_address(0x8000) +//#pragma start_address(0x0280) + +#include + +#include +#include +#include +#include + +const word ONE_TICK = 16666; // timer constant for 1/60 second + +byte last_sec = 0xFF; + +void print_clock() { + if(last_sec != _seconds) { + last_sec = _seconds; + woz_print_hex(_hours); woz_putc('.'); + woz_print_hex(_minutes); woz_putc('.'); + woz_print_hex(_seconds); woz_putc('\r'); + } +} + +// same routine as interrupt handler, but does not use RTI +void time_count() { + // update the watch + if(++_ticks == 60) { + _ticks = 0; + if(++_seconds == 60) { + _seconds = 0; + if(++_minutes == 60) { + _minutes = 0; + _hours++; + } + } + } + _irq_trigger = 1; // signals that an interrupt has been triggered + + #ifdef VIA6522 + VIA_ACK_INTERRUPT; + #endif +} + +void test_timer_poll() { + + woz_puts("*** TIMER IN POLL MODE ***\r\rPRESS X TO EXIT\r\r"); + + *VIA_IER = 0; // disable T1 interrupts (poll mode) + *VIA_ACR = 0b01000000; // timer 1 generates continuous interrupts + *VIA_T1CL = BYTE0(ONE_TICK); // programs the counter to 1/60 + *VIA_T1CH = BYTE1(ONE_TICK); // and starts counting + + while(1) + { + // wait until IFR bit 6 is set by the counter reaching zero + while(!(*VIA_IFR & VIA_IFR_MASK_T1)); + + // do the time counting as if it was a normal iterrupt + time_count(); + print_clock(); + if(apple1_readkey()=='X') break; + } + + *VIA_ACR = 0; // stop T1 (makes it one-shot) +} + + +void test_timer_interrupt() { + + woz_puts("*** TIMER IN INTERRUPT MODE ***\r\rPRESS X TO EXIT\r\r"); + + // install the interrupt handler + install_interrupt((word) &time_interrupt_handler); + + *VIA_IER = 0b11000000; // enable T1 interrupts + *VIA_ACR = 0b01000000; // T1 continous, PB7 disabled + *VIA_T1CL = BYTE0(ONE_TICK); // programs the counter to 1/60 + *VIA_T1CH = BYTE1(ONE_TICK); // and starts counting + + while(1) + { + print_clock(); + if(apple1_readkey()=='X') break; + } + + *VIA_IER = 0b01000000; // disable T1 interrupts + *VIA_ACR = 0; // stop T1 (makes it one-shot) +} + +void main() { + woz_puts("\rTESTING THE VIA 6522\r"); + + while(1) { + woz_puts("\r" + "P - POLL MODE\r" + "I - INTERRUPT MODE\r" + "X - EXIT\r\r" + ); + + byte k = apple1_getkey(); + + if(k=='P') test_timer_poll(); + else if(k=='I') test_timer_interrupt(); + else if(k=='X') break; + } + + woz_puts("BYE!\r"); + woz_mon(); +} + diff --git a/lib/interrupt.h b/lib/interrupt.h index 8f8d02a..4365458 100644 --- a/lib/interrupt.h +++ b/lib/interrupt.h @@ -6,14 +6,19 @@ // in order to make the interrupt routine not reside in zero page #pragma zp_reserve(0,1,2) -// acknowledge the interrupt by reading the status register on the TMS9918 (see manual section 2.3.1) -inline void acknowledge_interrupt() { - asm { lda VDP_REG }; -} +// acknowledge the TMS9918 interrupt by reading the status register (see manual section 2.3.1) +#define TMS_ACK_INTERRUPT asm { lda VDP_REG } + +// acknowledge the VIA 6522 interrupt by reading the T1CL register +#define VIA_ACK_INTERRUPT asm { bit VIA_T1CL } EXPORT __address(0) byte IRQ_JUMP_OPCODE; // location 0 contains the opcode for "JMP" EXPORT __address(1) word IRQ_JUMP_ADDRESS; // location 1 and 2 contain the jump address +#pragma zp_reserve(1) +#pragma zp_reserve(2) +#pragma zp_reserve(0) + // storage for a simple watch timer EXPORT volatile byte _ticks; EXPORT volatile byte _seconds; @@ -22,7 +27,7 @@ EXPORT volatile byte _hours; EXPORT volatile byte _irq_trigger; // interrupt routine called every 1/60th by the CPU after TMS9918 sets the /INT pin -EXPORT __interrupt(hardware_all) void interrupt_handler() { +EXPORT __interrupt(hardware_all) void time_interrupt_handler() { // update the watch if(++_ticks == 60) { _ticks = 0; @@ -34,16 +39,22 @@ EXPORT __interrupt(hardware_all) void interrupt_handler() { } } } - _irq_trigger = 1; // signals that an interrupt has been triggered + _irq_trigger = 1; // signals that an interrupt has been triggered - acknowledge_interrupt(); // acknowledge interrupt by reading the status register + #ifdef TMS9918 + TMS_ACK_INTERRUPT; + #endif + + #ifdef VIA6522 + VIA_ACK_INTERRUPT; + #endif } // safely installs the interrupt routine -void install_interrupt() { +void install_interrupt(word interrupt_handler) { asm { sei }; // disable 6502 interrupts IRQ_JUMP_OPCODE = 0x4C; // $4C = JUMP opcode - IRQ_JUMP_ADDRESS = (word) &interrupt_handler; // JUMP interrupt_handler + IRQ_JUMP_ADDRESS = interrupt_handler; // JUMP interrupt_handler asm { cli }; // re-enable 6502 interrupts } diff --git a/lib/tms9918.h b/lib/tms9918.h index f97733a..d533917 100644 --- a/lib/tms9918.h +++ b/lib/tms9918.h @@ -1,5 +1,8 @@ #ifndef TMS9918_H #define TMS9918_H + +#define TMS9918 1 + // TODO a bitmapped text writing routine (double size ecc) // TODO console like text output in screen 2 // TODO more fonts (C64 and PET/VIC20) diff --git a/lib/via.h b/lib/via.h new file mode 100644 index 0000000..fb26c00 --- /dev/null +++ b/lib/via.h @@ -0,0 +1,26 @@ +#ifndef VIA_H +#define VIA_H + +#define VIA6522 1 + +byte *const VIA_PORTB = (byte *) 0xA000; +byte *const VIA_PORTA = (byte *) 0xA001; +byte *const VIA_DDRB = (byte *) 0xA002; +byte *const VIA_DDRA = (byte *) 0xA003; +byte *const VIA_T1CL = (byte *) 0xA004; +byte *const VIA_T1CH = (byte *) 0xA005; +byte *const VIA_T1LL = (byte *) 0xA006; +byte *const VIA_T1LH = (byte *) 0xA007; +byte *const VIA_T2CL = (byte *) 0xA008; +byte *const VIA_T2CH = (byte *) 0xA009; +byte *const VIA_SR = (byte *) 0xA00A; +byte *const VIA_ACR = (byte *) 0xA00B; +byte *const VIA_PCR = (byte *) 0xA00C; +byte *const VIA_IFR = (byte *) 0xA00D; +byte *const VIA_IER = (byte *) 0xA00E; +byte *const VIA_PORTANH = (byte *) 0xA00F; + +byte VIA_IFR_MASK_T1 = 0b01000000; +byte VIA_IFR_MASK_T2 = 0b00100000; + +#endif \ No newline at end of file