VIA/TMS interrupts, SD card "test" command

This commit is contained in:
nino-porcino 2022-02-22 14:15:30 +01:00
parent 0caee6fc3a
commit c39132e78f
9 changed files with 260 additions and 39 deletions

View File

@ -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);

View File

@ -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);

29
demos/sdcard/cmd_test.h Normal file
View File

@ -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() {
}

View File

@ -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();

View File

@ -38,13 +38,25 @@
#include <apple1.h>
#include <string.h>
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
}

112
demos/viatimer/viatimer.c Normal file
View File

@ -0,0 +1,112 @@
#define APPLE1_USE_WOZ_MONITOR 1
//#pragma start_address(0x8000)
//#pragma start_address(0x0280)
#include <string.h>
#include <utils.h>
#include <apple1.h>
#include <via.h>
#include <interrupt.h>
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();
}

View File

@ -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
}

View File

@ -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)

26
lib/via.h Normal file
View File

@ -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