mirror of
https://github.com/nippur72/apple1-videocard-lib.git
synced 2025-02-06 09:30:28 +00:00
VIA/TMS interrupts, SD card "test" command
This commit is contained in:
parent
0caee6fc3a
commit
c39132e78f
@ -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);
|
||||
|
@ -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
29
demos/sdcard/cmd_test.h
Normal 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() {
|
||||
|
||||
}
|
@ -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();
|
||||
|
@ -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
112
demos/viatimer/viatimer.c
Normal 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();
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
26
lib/via.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user