1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-01-13 15:27:09 +00:00

CPU20 power start/power fail

This commit is contained in:
Joerg Hoppe 2019-08-27 19:05:41 +02:00
parent 6f2adbd216
commit d058310e53
23 changed files with 523 additions and 193 deletions

View File

@ -116,7 +116,7 @@ void unibus_c::init(unsigned pulsewidth_ms) {
*/
void unibus_c::powercycle(void) {
timeout_c timeout;
const unsigned delay_ms = 100; // time between phases
const unsigned delay_ms = 200; // time between phases
mailbox->initializationsignal.id = INITIALIZATIONSIGNAL_ACLO;
mailbox->initializationsignal.val = 1;
mailbox_execute(ARM2PRU_INITALIZATIONSIGNAL_SET);

View File

@ -68,6 +68,8 @@ using namespace std;
#include "iopageregister.h"
#include "priorityrequest.hpp"
#include "unibusadapter.hpp"
#include "unibuscpu.hpp"
unibusadapter_c *unibusadapter; // another Singleton
// is registered in device_c.list<devices> ... order of static constructor calls ???
@ -85,11 +87,13 @@ unibusadapter_c::unibusadapter_c() :
devices[i] = NULL;
line_INIT = false;
line_DCLO = false;
line_ACLO = false ;
line_ACLO = false;
requests_mutex = PTHREAD_MUTEX_INITIALIZER;
requests_init();
the_cpu = NULL;
}
bool unibusadapter_c::on_param_changed(parameter_c *param) {
@ -214,6 +218,15 @@ bool unibusadapter_c::register_device(unibusdevice_c& device) {
register_handle++;
}
// if its a CPU, switch PRU to "with_CPU"
unibuscpu_c *cpu = dynamic_cast<unibuscpu_c*>(&device);
if (cpu) {
assert(the_cpu == NULL); // only one allowed!
the_cpu = cpu;
// TODO: PRU code debuggen
//mailbox->cpu_enable = 1 ;
//mailbox_execute(ARM2PRU_CPU_ENABLE) ;
}
return true;
}
@ -227,6 +240,14 @@ void unibusadapter_c::unregister_device(unibusdevice_c& device) {
INFO("UnibusAdapter: UnRegistering device %s.", device.name.value.c_str());
// if its a CPU, disable PRU to "with_CPU"
unibuscpu_c *cpu = dynamic_cast<unibuscpu_c*>(&device);
if (cpu) {
mailbox->cpu_enable = 0;
mailbox_execute(ARM2PRU_CPU_ENABLE);
the_cpu = NULL;
}
// remove "from backplane"
devices[device.handle] = NULL;
device.handle = 0;
@ -302,8 +323,8 @@ void unibusadapter_c::requests_cancel_scheduled(void) {
prl->slot_request[slot] = NULL;
// signal to blocking DMA() or INTR()
pthread_mutex_lock(&req->complete_mutex);
req->complete = true;
pthread_cond_signal(&req->complete_cond);
req->complete = true;
pthread_cond_signal(&req->complete_cond);
pthread_mutex_unlock(&req->complete_mutex);
}
}
@ -492,8 +513,8 @@ void unibusadapter_c::request_active_complete(unsigned level_index) {
// signal to DMA() or INTR()
pthread_mutex_lock(&tmprq->complete_mutex);
tmprq->complete = true;
pthread_cond_signal(&tmprq->complete_cond);
tmprq->complete = true;
pthread_cond_signal(&tmprq->complete_cond);
pthread_mutex_unlock(&tmprq->complete_mutex);
}
@ -525,7 +546,7 @@ void unibusadapter_c::DMA(dma_request_c& dma_request, bool blocking, uint8_t uni
// dma_request.level-index, priority_slot in constructor
dma_request.complete = false;
dma_request.success = false;
dma_request.success = false;
dma_request.executing_on_PRU = false;
dma_request.unibus_control = unibus_control;
dma_request.unibus_start_addr = unibus_addr;
@ -551,12 +572,13 @@ void unibusadapter_c::DMA(dma_request_c& dma_request, bool blocking, uint8_t uni
// DEBUG("device DMA start: %s @ %06o, len=%d", unibus->control2text(unibus_control), unibus_addr, wordcount);
if (blocking) {
pthread_mutex_lock(&dma_request.complete_mutex);
// DMA() is blocking: Wait for request to finish.
// pthread_mutex_lock(&dma_request.mutex);
while (!dma_request.complete) {
int res = pthread_cond_wait(&dma_request.complete_cond, &dma_request.complete_mutex);
assert(!res) ;
}
// DMA() is blocking: Wait for request to finish.
// pthread_mutex_lock(&dma_request.mutex);
while (!dma_request.complete) {
int res = pthread_cond_wait(&dma_request.complete_cond,
&dma_request.complete_mutex);
assert(!res);
}
pthread_mutex_unlock(&dma_request.complete_mutex);
}
}
@ -681,17 +703,18 @@ void unibusadapter_c::cancel_INTR(intr_request_c& intr_request) {
// both empty, or both filled
assert((prl->slot_request_mask == 0) == (prl->active == NULL));
pthread_mutex_lock(&intr_request.complete_mutex);
pthread_cond_signal(&intr_request.complete_cond);
pthread_mutex_lock(&intr_request.complete_mutex);
pthread_cond_signal(&intr_request.complete_cond);
pthread_mutex_unlock(&intr_request.complete_mutex);
pthread_mutex_unlock(&requests_mutex); // lock schedule table operations
}
// do DATO/DATI as master CPU.
// no NPR/NPG/SACK request, but waiting for BUS idle
// result: success, else BUS TIMOUT
// result: success, else BUS TIMEOUT
void unibusadapter_c::cpu_DATA_transfer(dma_request_c& dma_request, uint8_t unibus_control,
uint32_t unibus_addr, uint16_t *buffer) {
timeout_c timeout;
@ -721,17 +744,15 @@ void unibusadapter_c::cpu_DATA_transfer(dma_request_c& dma_request, uint8_t unib
// then ARM2PRU_DMA is answered with an error
// infinite time may have passed after that check above
} while (!success);
// wait for PRU complete event
//pthread_mutex_lock(&cpu_data_transfer_request->complete_mutex);
//pthread_mutex_unlock(&cpu_data_transfer_request->complete_mutex);
pthread_mutex_lock(&cpu_data_transfer_request->complete_mutex);
// DMA() is blocking: Wait for request to finish.
// pthread_mutex_lock(&dma_request.mutex);
while (!cpu_data_transfer_request->complete) {
int res = pthread_cond_wait(&cpu_data_transfer_request->complete_cond, &cpu_data_transfer_request->complete_mutex);
assert(!res) ;
}
pthread_mutex_unlock(&cpu_data_transfer_request->complete_mutex);
// wait for PRU complete event, no clear why that double lock() is working anyhow!
pthread_mutex_lock(&cpu_data_transfer_request->complete_mutex);
// DMA() is blocking: Wait for request to finish.
while (!cpu_data_transfer_request->complete) {
int res = pthread_cond_wait(&cpu_data_transfer_request->complete_cond,
&cpu_data_transfer_request->complete_mutex);
assert(!res);
}
pthread_mutex_unlock(&cpu_data_transfer_request->complete_mutex);
// Copy incoming data from mailbox DMA buffer
if (unibus_control == UNIBUS_CONTROL_DATI)
@ -1048,7 +1069,8 @@ void unibusadapter_c::worker(unsigned instance) {
"EVENT_INITIALIZATIONSIGNALS: (sigprev=0x%x,) cur=0x%x, init_raise=%d, init_fall=%d, dclo_raise/fall=%d%/d, aclo_raise/fall=%d/%d",
mailbox->events.initialization_signals_prev,
mailbox->events.initialization_signals_cur, init_raising_edge,
init_falling_edge, dclo_raising_edge, dclo_falling_edge, aclo_raising_edge, aclo_falling_edge);
init_falling_edge, dclo_raising_edge, dclo_falling_edge,
aclo_raising_edge, aclo_falling_edge);
}
if (dclo_raising_edge)
@ -1073,15 +1095,28 @@ void unibusadapter_c::worker(unsigned instance) {
pthread_mutex_unlock(&requests_mutex);
mailbox->events.event_dma = 0; // PRU may re-raise and change mailbox now
}
if (mailbox->events.event_intr) {
// INTRs are granted unpredictable by Arbitrator
if (mailbox->events.event_intr_master) {
// Device INTR was transmitted. INTRs are granted unpredictable by Arbitrator
any_event = true;
// INTR of which level? the .active rquest of the"
pthread_mutex_lock(&requests_mutex);
worker_intr_complete_event(mailbox->events.event_intr_level_index);
pthread_mutex_unlock(&requests_mutex);
mailbox->events.event_intr = 0; // PRU may re-raise and change mailbox now
mailbox->events.event_intr_master = 0; // PRU may re-raise and change mailbox now
}
if (mailbox->events.event_intr_slave) {
// If CPU emulation enabled: a device INTR was detected on bus,
assert(the_cpu); // if INTR events are enabled, cpu must be instantiated
// see register_device()
the_cpu->on_interrupt(mailbox->events.event_intr_vector);
// clear SSYN, INTR cycle completes
mailbox->events.event_intr_slave = 0;
// mailbox->arbitrator.cpu_priority_level now CPU_PRIORITY_LEVEL_FETCHING
// BG grants blocked
// PRU may re-raise and change mailbox now
}
if (init_raising_edge) // INIT deasserted -> asserted. DATI/DATO cycle only possible before that.
worker_init_event();
}

View File

@ -49,6 +49,9 @@ public:
void clear();
};
class unibuscpu_c ;
// is a device_c. need a thread (but no params)
class unibusadapter_c: public device_c {
private:
@ -61,6 +64,8 @@ private:
pthread_mutex_t requests_mutex;
unibuscpu_c *the_cpu ; // only one unibuscpu_c may be registered
void worker_init_event(void);
void worker_power_event(bool power_down);
void worker_deviceregister_event(void);

View File

@ -0,0 +1,54 @@
/* unibuscpu.cpp: base class for all CPU implementations
Copyright (c) 2019, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27-aug-2019 JH start
*/
#include "unibuscpu.hpp"
void unibuscpu_c::on_power_changed(void) {
// called within a bus_cycle, and initiates other cycles?!
//assert(dbg==0) ;
if (power_down) { // power-on defaults
INFO("CPU: ACLO failed");
power_event = power_event_down;
// ka11_pwrdown(&the_cpu->ka11);
// ACLO failed.
// CPU traps to vector 24 and has 2ms time to execute code
} else {
INFO("CPU: DCLO restored");
power_event = power_event_up;
// ka11_pwrup(&the_cpu->ka11);
// DCLO restored
// CPU loads PC and PSW from vector 24
// if HALTed: do nothing, user is expected to setup PC and PSW ?
}
}
// UNIBUS INIT: clear all registers
void unibuscpu_c::on_init_changed(void) {
// a CPU does not react to INIT ... else its own RESET would reset it.
}

View File

@ -0,0 +1,52 @@
/* unibuscpu.hpp: base class for all CPU implementations
Copyright (c) 2019, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27-aug-2019 JH start
*/
#ifndef _UNIBUSCPU_HPP_
#define _UNIBUSCPU_HPP_
#include "unibusdevice.hpp"
// a CPU is just a device with INTR facilities
class unibuscpu_c: public unibusdevice_c {
public:
unibuscpu_c(): unibusdevice_c() {
power_event = power_event_none ;
} ;
enum power_event_enum {power_event_none, power_event_up, power_event_down} ;
enum power_event_enum power_event ;
// called by PRU on INTR, returns new priority level
virtual void on_interrupt(uint16_t vector) = 0 ;
virtual void on_power_changed(void) ;
virtual void on_init_changed(void) ;
};
#endif

View File

@ -168,4 +168,5 @@ public:
};
#endif

View File

@ -71,8 +71,9 @@ OBJECTS_COMMON= \
$(OBJ_DIR)/pru1_pru_mailbox.object \
$(OBJ_DIR)/pru1_statemachine_arbitration.object \
$(OBJ_DIR)/pru1_statemachine_dma.object \
$(OBJ_DIR)/pru1_statemachine_intr_master.object \
$(OBJ_DIR)/pru1_statemachine_data_slave.object \
$(OBJ_DIR)/pru1_statemachine_intr_master.object \
$(OBJ_DIR)/pru1_statemachine_intr_slave.object \
$(OBJ_DIR)/pru1_timeouts.object \
$(OBJ_DIR)/pru1_utils.object

View File

@ -54,10 +54,10 @@
#include "pru1_statemachine_arbitration.h"
#include "pru1_statemachine_dma.h"
#include "pru1_statemachine_intr_master.h"
#include "pru1_statemachine_slave.h"
#include "pru1_statemachine_data_slave.h"
// Supress warnings about using void * as function pointers
// sm_slave_state = (statemachine_state_func)&sm_slave_start;
// sm_slave_state = (statemachine_state_func)&sm_data_slave_start;
// while (sm_slave_state = sm_slave_state()) << usage
#pragma diag_push
#pragma diag_remark=515
@ -203,7 +203,7 @@ void main(void) {
// writing into mailbox.arm2pru_req
while (mailbox.arm2pru_req == ARM2PRU_DDR_SLAVE_MEMORY) {
statemachine_state_func sm_slave_state =
(statemachine_state_func) &sm_slave_start;
(statemachine_state_func) &sm_data_slave_start;
// do all states of an access, start when MSYN found.
while (sm_slave_state = sm_slave_state())
;

View File

@ -58,11 +58,12 @@
#include "pru1_buslatches.h"
#include "pru1_statemachine_arbitration.h"
#include "pru1_statemachine_dma.h"
#include "pru1_statemachine_data_slave.h"
#include "pru1_statemachine_intr_master.h"
#include "pru1_statemachine_slave.h"
#include "pru1_statemachine_intr_slave.h"
// supress warnigns about using void * as function pointers
// sm_slave_state = (statemachine_state_func)&sm_slave_start;
// sm_slave_state = (statemachine_state_func)&sm_data_slave_start;
// while (sm_slave_state = sm_slave_state()) << usage
#pragma diag_push
#pragma diag_remark=515
@ -93,8 +94,11 @@ void main(void) {
statemachine_arb_worker_func sm_arb_worker = &sm_arb_worker_client;
statemachine_state_func sm_data_slave_state = NULL;
statemachine_state_func sm_data_master_state = NULL;
statemachine_state_func sm_intr_slave_state = NULL ;
// these are function pointers: could be 16bit on PRU?
bool emulate_cpu = false;
/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
@ -118,16 +122,27 @@ void main(void) {
uint8_t arm2pru_req_cached;
if (sm_data_master_state == NULL) {
// State 1 "SLAVE"
// State 1 "SLAVE"
// DATA or INTR for CPU?
// fast: a complete slave data cycle
if (!sm_data_slave_state)
sm_data_slave_state = (statemachine_state_func) &sm_slave_start;
sm_data_slave_state = (statemachine_state_func) &sm_data_slave_start;
while ((sm_data_slave_state = sm_data_slave_state())
&& !mailbox.events.event_deviceregister)
// throws signals to ARM,
// Acess to internal registers may may issue AMR2PRU opcode, so exit loop then
;// execute complete slave cycle, then check NPR/INTR
if (emulate_cpu) {
// same code loop as for DATA cycle
if (!sm_intr_slave_state)
sm_intr_slave_state = (statemachine_state_func) &sm_intr_slave_start;
while ((sm_intr_slave_state = sm_intr_slave_state())
&& !mailbox.events.event_intr_slave) ;
}
// signal INT or PWR FAIL to ARM
do_event_initializationsignals();
@ -160,10 +175,10 @@ void main(void) {
// we have been GRANTed bus mastership and are doing DMA or INTR
// SACK held here -> no further arbitration
// INTR is only 1 cycle, DMA has SACK set all the time, arbitration
// prohibited then.
// prohibited then.
sm_data_master_state = sm_data_master_state(); // execute only ONE state ,
// else DMA blocks will block prcoessing of other state machines
// else DMA blocks will block processing of other state machines
// throws signals to ARM, causes may issue mailbox.arm2pru_req
}
@ -193,7 +208,7 @@ void main(void) {
if (mailbox.arbitrator.cpu_BBSY) {
// ARM CPU emulation did a single word DATA transfer with cpu_DATA_transfer()
if (mailbox.arbitrator.device_BBSY) {
// ARM started cpu DATA transfer, but arbitration logic
// ARM started cpu DATA transfer, but arbitration logic
// GRANTed a device request in the mean time. Tell ARM.
mailbox.arm2pru_req = PRU2ARM_DMA_CPU_TRANSFER_BLOCKED; // error
mailbox.arbitrator.cpu_BBSY = false;
@ -227,7 +242,7 @@ void main(void) {
// vector of GRANted level is transfered with statemachine sm_intr_master
// Atomically change state in a device's associates interrupt register.
// The Interupt Register is set immediately. No wait for INTR GRANT,
// The Interupt Register is set immediately. No wait for INTR GRANT,
// INTR level may be blocked.
if (mailbox.intr.iopage_register_handle)
deviceregisters.registers[mailbox.intr.iopage_register_handle].value =
@ -259,7 +274,16 @@ void main(void) {
}
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break;
case ARM2PRU_HALT:
case ARM2PRU_CPU_ENABLE:
// bool flag much faster to access then shared mailbox member.
emulate_cpu = mailbox.cpu_enable ;
if (emulate_cpu)
sm_arb_worker = &sm_arb_worker_master;
else
sm_arb_worker = &sm_arb_worker_client;
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
break ;
case ARM2PRU_HALT:
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
__halt(); // LA: trigger on timeout of REG_WRITE
break;

View File

@ -238,7 +238,10 @@ PRU_DEBUG_PIN_PULSE_100NS ;
uint8_t requested_intr_level = requests_2_highests_intr[intr_request_mask];
// compare against cpu run level 4..7
if (requested_intr_level > mailbox.arbitrator.cpu_priority_level) {
// but do not GRANT anything if emulated CPU did not fetch new PSW yet,
// then cpu_priority_level is invalid
if (requested_intr_level > mailbox.arbitrator.cpu_priority_level
&& requested_intr_level != CPU_PRIORITY_LEVEL_FETCHING) {
// GRANT request, set GRANT line:
// BG4 is signal bit maskl 0x01, etc ...
sm_arb.arbitrator_grant_mask = BIT(requested_intr_level - 4);

View File

@ -1,6 +1,6 @@
/* pru1_statemachine_slave.c: state machine for execution of slave DATO* or DATI* cycles
/* pru1_statemachine_data_slave.c: state machine for execution of slave DATO* or DATI* cycles
Copyright (c) 2018, Joerg Hoppe
Copyright (c) 2018-2019, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
@ -34,8 +34,6 @@
- address is evaluated, ggf mem access
*/
#define _PRU1_STATEMACHINE_SLAVE_C_
#include <stdlib.h>
#include <stdint.h>
@ -46,16 +44,16 @@
#include "iopageregister.h"
#include "pru1_buslatches.h"
#include "pru1_statemachine_slave.h"
#include "pru1_statemachine_data_slave.h"
// forwards ;
//statemachine_state_func sm_slave_start(void);
static statemachine_state_func sm_slave_state_10(void);
static statemachine_state_func sm_slave_state_20(void);
//static statemachine_state_func sm_slave_state_99(void);
//statemachine_state_func sm_data_slave_start(void);
static statemachine_state_func sm_data_slave_state_10(void);
static statemachine_state_func sm_data_slave_state_20(void);
//static statemachine_state_func sm_data_slave_state_99(void);
// check for MSYN active
statemachine_state_func sm_slave_start() {
statemachine_state_func sm_data_slave_start() {
uint8_t latch2val, latch3val, latch4val;
// uint8_t iopage;
uint32_t addr;
@ -111,11 +109,11 @@ statemachine_state_func sm_slave_start() {
buslatches_setbyte(6, data >> 8);
// set SSYN = latch[4], bit 5
buslatches_setbits(4, BIT(5), BIT(5));
return (statemachine_state_func) &sm_slave_state_20;
return (statemachine_state_func) &sm_data_slave_state_20;
// perhaps PRU2ARM_INTERRUPT now active
} else
// no address match: wait for MSYN to go inactive
// return (statemachine_state_func) &sm_slave_state_99;
// return (statemachine_state_func) &sm_data_slave_state_99;
return NULL ;
case UNIBUS_CONTROL_DATO:
// fetch data in any case
@ -128,11 +126,11 @@ statemachine_state_func sm_slave_start() {
// SSYN = latch[4], bit 5
buslatches_setbits(4, BIT(5), BIT(5));
// wait for MSYN to go inactive, then SSYN inactive
return (statemachine_state_func) &sm_slave_state_10;
return (statemachine_state_func) &sm_data_slave_state_10;
// perhaps PRU2ARM_INTERRUPT now active
} else
// no address match: wait for MSYN to go inactive
// return (statemachine_state_func) &sm_slave_state_99;
// return (statemachine_state_func) &sm_data_slave_state_99;
return NULL ;
case UNIBUS_CONTROL_DATOB:
// A00 = 1, odd address: get upper byte
@ -149,11 +147,11 @@ statemachine_state_func sm_slave_start() {
// SSYN = latch[4], bit 5
buslatches_setbits(4, BIT(5), BIT(5));
// wait for MSYN to go inactive, then SSYN inactive
return (statemachine_state_func) &sm_slave_state_10;
return (statemachine_state_func) &sm_data_slave_state_10;
// perhaps PRU2ARM_INTERRUPT now active
} else
// no address match: wait for MSYN to go inactive
// return (statemachine_state_func) &sm_slave_state_99;
// return (statemachine_state_func) &sm_data_slave_state_99;
return NULL ;
}
return NULL; // not reached
@ -161,14 +159,14 @@ statemachine_state_func sm_slave_start() {
// End DATO: wait for MSYN to go inactive, then SSYN inactive
// also wait for EVENT ACK
static statemachine_state_func sm_slave_state_10() {
static statemachine_state_func sm_data_slave_state_10() {
// MSYN = latch[4], bit 4
if (buslatches_getbyte(4) & BIT(4))
return (statemachine_state_func) &sm_slave_state_10; // wait, MSYN still active
return (statemachine_state_func) &sm_data_slave_state_10; // wait, MSYN still active
if (mailbox.events.event_deviceregister)
// unibusadapter.worker() did not yet run on_after_register_access()
// => wait, long SSYN delay until ARM acknowledges event
return (statemachine_state_func) &sm_slave_state_10;
return (statemachine_state_func) &sm_data_slave_state_10;
// if ARM was triggered by event and changed the device state,
// now an Interrupt arbitration may be pending.
@ -180,14 +178,14 @@ static statemachine_state_func sm_slave_state_10() {
// End DATI: wait for MSYN to go inactive, then SSYN and DATA inactive
// also wait for EVENT ACK
static statemachine_state_func sm_slave_state_20() {
static statemachine_state_func sm_data_slave_state_20() {
// MSYN = latch[4], bit 4
if (buslatches_getbyte(4) & BIT(4))
return (statemachine_state_func) &sm_slave_state_20; // wait, MSYN still active
return (statemachine_state_func) &sm_data_slave_state_20; // wait, MSYN still active
if (mailbox.events.event_deviceregister)
// unibusadapter.worker() did not yet run on_after_register_access()
// => wait, long SSYN delay until ARM acknowledges event
return (statemachine_state_func) &sm_slave_state_20;
return (statemachine_state_func) &sm_data_slave_state_20;
// if ARM was triggered by event and changed the device state,
// now an Interrupt arbitration may be pending.
@ -204,10 +202,10 @@ static statemachine_state_func sm_slave_state_20() {
// end of inactive cycle: wait for MSYN to go inactive
// Not necessary, start() state simply checks addr again if MSYN still set.
static statemachine_state_func sm_slave_state_99() {
static statemachine_state_func sm_data_slave_state_99() {
// MSYN = latch[4], bit 4
if (buslatches_getbyte(4) & BIT(4)) {
return (statemachine_state_func) &sm_slave_state_99; // wait, MSYN still active
return (statemachine_state_func) &sm_data_slave_state_99; // wait, MSYN still active
}
// PRU_DEBUG_PIN(1) ;

View File

@ -1,6 +1,6 @@
/* pru1_statemachine_slave.h: state machine for execution of slave DATO* or DATI* cycles
Copyright (c) 2018, Joerg Hoppe
Copyright (c) 2018-2019, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
@ -24,12 +24,12 @@
29-jun-2019 JH rework: state returns ptr to next state func
12-nov-2018 JH entered beta phase
*/
#ifndef _PRU1_STATEMACHINE_SLAVE_H_
#define _PRU1_STATEMACHINE_SLAVE_H_
#ifndef _PRU1_STATEMACHINE_DATA_SLAVE_H_
#define _PRU1_STATEMACHINE_DATA_SLAVE_H_
#include <stdint.h>
#include "pru1_utils.h" // statemachine_state_func
statemachine_state_func sm_slave_start(void);
statemachine_state_func sm_data_slave_start(void);
#endif

View File

@ -50,8 +50,6 @@
! Uses single global timeout, don't run in parallel with other statemachines using timeout !
*/
#define _PRU1_STATEMACHINE_DMA_C_
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

View File

@ -37,9 +37,7 @@ typedef struct {
uint16_t cur_wordsleft; // # of words left to transfer
} statemachine_dma_t;
#ifndef _PRU1_STATEMACHINE_DMA_C_
extern statemachine_dma_t sm_dma;
#endif
statemachine_state_func sm_dma_start(void);

View File

@ -29,7 +29,6 @@
Precondition: BBSY already asserted (arbitration got)
*/
#define _PRU1_STATEMACHINE_INTR_C_
#include <stdlib.h>
#include <stdint.h>
@ -86,7 +85,7 @@ static statemachine_state_func sm_intr_master_state_2() {
// Complete and signal this INTR transaction only after ARM has processed the previous event.
// INTR may come faster than ARM Linux can process,
// especially if Arbitrator grants INTRs of multiple levels almost simultaneaously in parallel.
if (mailbox.events.event_intr)
if (mailbox.events.event_intr_master)
return (statemachine_state_func) &sm_intr_master_state_2; // wait
// remove vector
@ -106,7 +105,7 @@ static statemachine_state_func sm_intr_master_state_2() {
// signal to ARM which INTR was completed
// change mailbox only after ARM has ack'ed mailbox.events.event_intr
mailbox.events.event_intr_level_index = sm_intr_master.level_index;
mailbox.events.event_intr = 1;
mailbox.events.event_intr_master = 1;
// ARM is clearing this, before requesting new interrupt of same level
// so no concurrent ARP+PRU access
PRU2ARM_INTERRUPT

View File

@ -0,0 +1,93 @@
/* pru1_statemachine_intr_slave.c: CPU receives interrupt vector
Copyright (c) 2019, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26-aug-2019 JH start
State machines for CPU emulation to receive INTR vector placed onto bus by device.
All references "PDP11BUS handbook 1979"
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
//#include "devices.h"
#include "mailbox.h"
#include "pru1_buslatches.h"
#include "pru1_utils.h"
#include "pru1_statemachine_intr_slave.h"
// states
statemachine_intr_slave_t sm_intr_slave;
// forwards
static statemachine_state_func sm_intr_slave_state_1(void);
// Wait for BBSY deasserted, then assert, SACK already held asserted
statemachine_state_func sm_intr_slave_start() {
// WAIT for INTR
if ((buslatches_getbyte(7) & BIT(0)) == 0)
return NULL ; // still idle
// device has put vector onto DATA lines, fetch after 150ns
__delay_cycles(NANOSECS(150));
uint8_t latch5val = buslatches_getbyte(5) ;// DATA[0..7] = latch[5]
uint8_t latch6val = buslatches_getbyte(6) ; // DATA[8..15] = latch[6]
// set SSYN = latch[4], bit 5
buslatches_setbits(4, BIT(5), BIT(5));
// mark priority level as invalid, block more BG GRANTS until PSW fetched
mailbox.arbitrator.cpu_priority_level = CPU_PRIORITY_LEVEL_FETCHING ;
// signal ARM, wait for event to be processed
mailbox.events.event_intr_vector = (uint16_t) latch6val << 8 | latch5val ;
PRU2ARM_INTERRUPT ;
// wait until ARM acked
return (statemachine_state_func) &sm_intr_slave_state_1;
}
static statemachine_state_func sm_intr_slave_state_1() {
// wait until ARM acked the INTR vector
// event_intr_slave is hold until the CPU
//CPU has read the new PSW and new abritration level
// event_intr_slave co solved by
if (mailbox.events.event_intr_slave)
return (statemachine_state_func) &sm_intr_slave_state_1;
// wait if INTR still active
if (buslatches_getbyte(7) & BIT(0)) // check INTR
return (statemachine_state_func) &sm_intr_slave_state_1;
// clear SSYN = latch[4], bit 5
buslatches_setbits(4, BIT(5), 0);
// now CPU may do DATI to fetch PC and PSW
return NULL; // ready
}

View File

@ -0,0 +1,40 @@
/* pru1_statemachine_intr_slave.h: CPU receives interrupt vector
Copyright (c) 2018-2019, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26-aug-2019 JH start
*/
#ifndef _PRU1_STATEMACHINE_INTR_SLAVE_H_
#define _PRU1_STATEMACHINE_INTR_SLAVE_H_
#include "pru1_utils.h" // statemachine_state_func
typedef struct {
uint16_t vector; // interrupt vector to transfer
uint8_t level_index; // 0..3 = BR..BR7. to be returned to ARM on complete
} statemachine_intr_slave_t;
extern statemachine_intr_slave_t sm_intr_slave;
statemachine_state_func sm_intr_slave_start(void);
#endif

View File

@ -44,12 +44,13 @@
#define ARM2PRU_ARB_MODE_NONE 11 // DMA without NPR/NPG/SACK arbitration
#define ARM2PRU_ARB_MODE_CLIENT 12 // DMA with arbitration by external Arbitrator
#define ARM2PRU_ARB_MODE_MASTER 13 // DMA as Arbitrator
#define ARM2PRU_DMA 14 // DMA with selcted arbitration
#define ARM2PRU_DMA 14 // DMA with selected arbitration
#define PRU2ARM_DMA_CPU_TRANSFER_BLOCKED 15 // possible result of ARM2PRU_DMA
#define ARM2PRU_INTR 16 // INTR with arbitration by external Arbitrator
#define ARM2PRU_INTR_CANCEL 17 // clear INTR which has been requested
#define ARM2PRU_DDR_FILL_PATTERN 18 // fill DDR with test pattern
#define ARM2PRU_DDR_SLAVE_MEMORY 19 // use DDR as UNIBUS slave memory
#define ARM2PRU_CPU_ENABLE 18 // siwtch CPU master side functions ON/OFF
#define ARM2PRU_DDR_FILL_PATTERN 19 // fill DDR with test pattern
#define ARM2PRU_DDR_SLAVE_MEMORY 20 // use DDR as UNIBUS slave memory
// signal IDs for ARM2PRU_INITALIZATIONSIGNAL_*
@ -76,6 +77,10 @@
#define PRIORITY_ARBITRATION_INTR_MASK 0x0f // BR4|BR5|BR6|BR7
#define PRIORITY_ARBITRATION_BIT_MASK 0x1f
// CPU pririty level invalid between INTR receive and fetch of next PSW
#define CPU_PRIORITY_LEVEL_FETCHING 0xff
// data for a requested DMA operation
#define PRU_MAX_DMA_WORDCOUNT 512
@ -175,7 +180,7 @@ typedef struct {
// differemt events can be raised asynchronically and concurrent,
// but a single event type is sequentially raised by PRU and cleared by ARM.
/* Access to device register ***/
/*** Access to device register ***/
uint8_t event_deviceregister; // trigger flag
// info about register access
uint8_t unibus_control; // DATI,DATO,DATOB
@ -197,24 +202,28 @@ typedef struct {
uint8_t event_dma; // trigger flag
uint8_t event_dma_cpu_transfer ; // 1: ARM must process DMa as compelted cpu DATA transfer
/*** Event priority arbitration data transfer complete
/*** Event priority arbitration INTR transfer complete
After ARM2PRU_INTR, one of BR4/5/6/7 NP was requested,
granted, and the data transfer was handled as bus master.
*/
// ---dword---
uint8_t event_intr; // trigger flag: 1 = one of BR4,5,6,7 vector on UNIBUS
uint8_t event_intr_master; // trigger flag: 1 = one of BR4,5,6,7 vector on UNIBUS
uint8_t event_intr_level_index; // 0..3 -> BR4..BR7
uint8_t _dummy2, _dummy3;
/*** INTR transmitted by devices as master was received by CPU as slave ***/
uint8_t event_intr_slave; // trigger flag: 1 = one of BR4,5,6,7 vector on UNIBUS
uint8_t _dummy1 ;
// ---dword---
uint16_t event_intr_vector ; // received vector
/*** INIT or Power cycle seen on UNIBUS ***/
uint8_t event_init; // trigger flag
uint8_t event_power; // trigger flag
// ---dword---
uint8_t initialization_signals_prev; // on event: a signal changed from this ...
// ---dword---
uint8_t initialization_signals_cur; // ... to this
// uint8_t dummy[2]; // make record multiple of dword !!!
uint8_t _dummy2[2]; // make record multiple of dword !!!
} mailbox_events_t;
typedef struct {
@ -242,6 +251,7 @@ typedef struct {
mailbox_buslatch_test_t buslatch_test;
mailbox_buslatch_exerciser_t buslatch_exerciser;
mailbox_initializationsignal_t initializationsignal ;
uint32_t cpu_enable;
};
} mailbox_t;

View File

@ -41,8 +41,10 @@
*/
static cpu_c *the_cpu = NULL;
int dbg = 0;
cpu_c::cpu_c() :
unibusdevice_c() // super class constructor
unibuscpu_c() // super class constructor
{
// static config
name.value = "CPU20";
@ -81,7 +83,7 @@ bool cpu_c::on_param_changed(parameter_c *param) {
init.value = false;
}
}
return device_c::on_param_changed(param); // more actions (for enable)
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
}
// background worker.
@ -93,6 +95,8 @@ void cpu_c::worker(unsigned instance) {
unsigned dr = 0760102;
unsigned opcode = 0;
(void) opcode;
power_event = power_event_none;
while (!workers_terminate) {
// run full speed!
timeout.wait_us(1);
@ -105,6 +109,17 @@ void cpu_c::worker(unsigned instance) {
if (runmode.value != (ka11.state != 0))
runmode.value = ka11.state != 0;
// serialize asynchronous power events
if (runmode.value) {
// don't call power traps if HALTed. Also not on CONT.
if (power_event == power_event_down)
ka11_pwrdown(&the_cpu->ka11);
// stop stop some time after power down
else if (power_event == power_event_up)
ka11_pwrup(&the_cpu->ka11);
power_event = power_event_none; // processed
}
if (init.value) {
// user wants CPU reset
ka11_reset(&ka11);
@ -146,29 +161,14 @@ void cpu_c::on_after_register_access(unibusdevice_register_t *device_reg,
UNUSED(unibus_control);
}
// TODO
void cpu_c::on_power_changed(void) {
if (power_down) { // power-on defaults
INFO("CPU: ACLO failed");
ka11_pwrdown(&the_cpu->ka11);
// ACLO failed.
// CPU traps to vector 24 and has 2ms time to execute code
} else {
INFO("CPU: DCLO restored");
ka11_pwrup(&the_cpu->ka11);
// DCLO restored
// CPU loads PC and PSW from vector 24
}
}
// UNIBUS INIT: clear all registers
void cpu_c::on_init_changed(void) {
// a CPU does not react to INIT ... else its own RESET would reset it.
}
// TODO
// CPU received interrupt vector from UNIBUS
// PRU triggers this via unibusadapter,
// mailbox->arbitrator.cpu_priority_level is CPU_PRIORITY_LEVEL_FETCHING
// CPU fetches PSW and calls unibone_prioritylevelchange(), which
// sets mailbox->arbitrator.cpu_priority_level and
// PRU is allowed now to grant BGs again.
void cpu_c::on_interrupt(uint16_t vector) {
// CPU sequence:
// push PSW to stack
@ -184,35 +184,43 @@ extern "C" {
int unibone_dato(unsigned addr, unsigned data) {
uint16_t wordbuffer = (uint16_t) data;
dbg = 1;
unibusadapter->cpu_DATA_transfer(the_cpu->data_transfer_request, UNIBUS_CONTROL_DATO, addr,
&wordbuffer);
dbg = 0;
return the_cpu->data_transfer_request.success;
}
int unibone_datob(unsigned addr, unsigned data) {
uint16_t wordbuffer = (uint16_t) data;
// TODO DATOB als 1 byte-DMA !
dbg = 1;
unibusadapter->cpu_DATA_transfer(the_cpu->data_transfer_request, UNIBUS_CONTROL_DATOB, addr,
&wordbuffer);
dbg = 0;
return the_cpu->data_transfer_request.success;
}
int unibone_dati(unsigned addr, unsigned *data) {
uint16_t wordbuffer;
dbg = 1;
unibusadapter->cpu_DATA_transfer(the_cpu->data_transfer_request, UNIBUS_CONTROL_DATI, addr,
&wordbuffer);
*data = wordbuffer;
dbg = 0;
// printf("DATI; ba=%o, data=%o\n", addr, *data) ;
return the_cpu->data_transfer_request.success;
}
// CPU has changed the arbitration level, just forward
// if this is called as result of INTR fector PC and PSW fetch,
// mailbox->arbitrator.cpu_priority_level was CPU_PRIORITY_LEVEL_FETCHING
// In that case, PRU is allowed now to grant BGs again.
void unibone_prioritylevelchange(uint8_t level) {
mailbox->arbitrator.cpu_priority_level = level;
}
// TODO
// CPU executes RESET opcode -> pulses INIT line
void unibone_bus_init(unsigned pulsewidth_ms) {
unibus->init(pulsewidth_ms);

View File

@ -29,14 +29,15 @@
using namespace std;
#include "utils.hpp"
#include "unibusadapter.hpp"
#include "unibusdevice.hpp"
//#include "unibusadapter.hpp"
//#include "unibusdevice.hpp"
#include "unibuscpu.hpp"
extern "C" {
#include "cpu20/11.h"
#include "cpu20/ka11.h"
}
class cpu_c: public unibusdevice_c {
class cpu_c: public unibuscpu_c {
private:
//unibusdevice_register_t *switch_reg;
@ -67,8 +68,6 @@ public:
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
override;
void on_power_changed(void) override;
void on_init_changed(void) override;
void on_interrupt(uint16_t vector) ;
};

View File

@ -111,8 +111,9 @@ OBJECTS = $(OBJDIR)/application.o \
$(OBJDIR)/storagecontroller.o \
$(OBJDIR)/demo_io.o \
$(OBJDIR)/testcontroller.o \
$(OBJDIR)/unibusdevice.o \
$(OBJDIR)/device.o \
$(OBJDIR)/unibusdevice.o \
$(OBJDIR)/unibuscpu.o \
$(OBJDIR)/parameter.o \
$(OBJDIR)/panel.o \
$(OBJDIR)/priorityrequest.o \
@ -262,6 +263,9 @@ $(OBJDIR)/testcontroller.o : $(DEVICE_SRC_DIR)/testcontroller.cpp $(DEVICE_SRC_
$(OBJDIR)/unibusdevice.o : $(BASE_SRC_DIR)/unibusdevice.cpp $(BASE_SRC_DIR)/unibusdevice.hpp
$(CC) $(CCFLAGS) $< -o $@
$(OBJDIR)/unibuscpu.o : $(BASE_SRC_DIR)/unibuscpu.cpp $(BASE_SRC_DIR)/unibuscpu.hpp
$(CC) $(CCFLAGS) $< -o $@
$(OBJDIR)/device.o : $(BASE_SRC_DIR)/device.cpp $(BASE_SRC_DIR)/device.hpp
$(CC) $(CCFLAGS) $< -o $@

View File

@ -14,85 +14,89 @@
14
15 ; select one type of console at assembly time
16 177560 dladr = 177560 ; base addr of DEC DL11 console
17 ; dladr = 176500 ; DL11 #0
17 ; dladr = 176500 ; DL11 #0
18 ; dladr = 176510 ; DL11 #1
19 ; dladr = 176520 ; DL11 #2
20
21
22 000000 .=0
23 000000 000137 001000 jmp @#start ; early emulation started code execution from 0
23 000000 000137 001000 jmp @#start ; early emulation started code execution from 0
24
25 001000 .=1000
26
27 000776 stack = . - 2 ; stack growns down from start
25 000024 .=24 ; If not HALTed: start on power-up
26 000024 001000 .word start ; PC
27 000026 000340 .word 340 ; PSW with priority level 7
28
29 start:
30 001000 012706 000776 mov #stack,sp ; init stack
31
32 ; 1. print "Hello" msg
33 001004 012701 001126 mov #shello,r1
34 001010 004737 001054 call @#puts
29 001000 .=1000
30
31 000776 stack = . - 2 ; stack growns down from start
32
33 start:
34 001000 012706 000776 mov #stack,sp ; init stack
35
36 ; 2. echo chars until ^C hit
37 1$:
38 001014 004737 001110 call @#getc ; wait for char, return in r0
39 001020 042700 177600 bic #177600,r0 ; make 7bit: clear bits <15:7>
40 001024 120027 000003 cmpb r0,#3 ; break by ^C ?
41 001030 001403 beq 2$ ; yes: leave loop
42 001032 004737 001070 call @#putc ; no: echo char in r0 and loop
43 001036 000766 br 1$
44
45 2$:
46
47 ; 3. print "Bye bye" msg and HALT
48 001040 012701 001211 mov #sbye,r1
49 001044 004737 001054 call @#puts
50 001050 000000 halt
51
52 ; 4. loop on CONT
53 001052 000752 br start
54
36 ; 1. print "Hello" msg
37 001004 012701 001126 mov #shello,r1
38 001010 004737 001054 call @#puts
39
40 ; 2. echo chars until ^C hit
41 1$:
42 001014 004737 001110 call @#getc ; wait for char, return in r0
43 001020 042700 177600 bic #177600,r0 ; make 7bit: clear bits <15:7>
44 001024 120027 000003 cmpb r0,#3 ; break by ^C ?
45 001030 001403 beq 2$ ; yes: leave loop
46 001032 004737 001070 call @#putc ; no: echo char in r0 and loop
47 001036 000766 br 1$
48
49 2$:
50
51 ; 3. print "Bye bye" msg and HALT
52 001040 012701 001211 mov #sbye,r1
53 001044 004737 001054 call @#puts
54 001050 000000 halt
55
56 ; ----------------------
57 ; puts - print a string
58 ; r1 = pointer, r0,r1 changed
59 puts:
60 001054 112100 movb (r1)+,r0 ; load xmt char
61 001056 001403 beq 1$ ; string ends with 0
62 001060 004737 001070 call @#putc
63 001064 000773 br puts ; transmit nxt char of string
64 001066 000207 1$: return
65
66
67 ; ----------------------
68 ; putc - output a single char
69 ; r0 = char, r4 changed
70 putc:
71 001070 012704 177560 mov #dladr,r4 ; set base addr
72 001074 110064 000006 movb r0,6(r4) ; char into transmit buffer
73 001100 105764 000004 1$: tstb 4(r4) ; XMT RDY?
74 001104 100375 bpl 1$ ; no, loop
75 001106 000207 return
76
77 ; ----------------------
78 ; getc - input a single char
79 ; result in r0, r4 changed
80 getc:
81 001110 012704 177560 mov #dladr,r4 ; set base addr
82 001114 105714 1$: tstb (r4) ; RCVR DONE?
83 001116 100376 bpl 1$ ; no, loop
84 001120 016400 000002 mov 2(r4),r0 ; return data
85 001124 000207 return
86
87
88 shello:
89 001126 110 145 154 .ascii /Hello, World!/
56 ; 4. loop on CONT
57 001052 000752 br start
58
59
60 ; ----------------------
61 ; puts - print a string
62 ; r1 = pointer, r0,r1 changed
63 puts:
64 001054 112100 movb (r1)+,r0 ; load xmt char
65 001056 001403 beq 1$ ; string ends with 0
66 001060 004737 001070 call @#putc
67 001064 000773 br puts ; transmit nxt char of string
68 001066 000207 1$: return
69
70
71 ; ----------------------
72 ; putc - output a single char
73 ; r0 = char, r4 changed
74 putc:
75 001070 012704 177560 mov #dladr,r4 ; set base addr
76 001074 110064 000006 movb r0,6(r4) ; char into transmit buffer
77 001100 105764 000004 1$: tstb 4(r4) ; XMT RDY?
78 001104 100375 bpl 1$ ; no, loop
79 001106 000207 return
80
81 ; ----------------------
82 ; getc - input a single char
83 ; result in r0, r4 changed
84 getc:
85 001110 012704 177560 mov #dladr,r4 ; set base addr
86 001114 105714 1$: tstb (r4) ; RCVR DONE?
87 001116 100376 bpl 1$ ; no, loop
88 001120 016400 000002 mov 2(r4),r0 ; return data
89 001124 000207 return
90
91
92 shello:
93 001126 110 145 154 .ascii /Hello, World!/
001131 154 157 054
001134 040 127 157
001137 162 154 144
001142 041
90 001143 015 012 .byte 15,12 ; CR, LF,
91 001145 124 171 160 .ascii /Typed chars are echoed, ^C HALTs./
94 001143 015 012 .byte 15,12 ; CR, LF,
95 001145 124 171 160 .ascii /Typed chars are echoed, ^C HALTs./
001150 145 144 040
001153 143 150 141
001156 162 163 040
@ -103,13 +107,13 @@
001175 136 103 040
001200 110 101 114
001203 124 163 056
92 001206 015 012 000 .byte 15,12,0 ; CR, LF, NUL=end marker
93 sbye:
94 001211 015 012 .byte 15,12
95 001213 107 157 157 .ascii /Good bye!/
96 001206 015 012 000 .byte 15,12,0 ; CR, LF, NUL=end marker
97 sbye:
98 001211 015 012 .byte 15,12
99 001213 107 157 157 .ascii /Good bye!/
001216 144 040 142
001221 171 145 041
96 001224 015 012 000 .byte 15,12,0 ; CR, LF, NUL=end marker
97
98 .end
98
100 001224 015 012 000 .byte 15,12,0 ; CR, LF, NUL=end marker
101
102 .end
102

View File

@ -14,13 +14,17 @@
; select one type of console at assembly time
dladr = 177560 ; base addr of DEC DL11 console
; dladr = 176500 ; DL11 #0
; dladr = 176500 ; DL11 #0
; dladr = 176510 ; DL11 #1
; dladr = 176520 ; DL11 #2
.=0
jmp @#start ; early emulation started code execution from 0
jmp @#start ; early emulation started code execution from 0
.=24 ; If not HALTed: start on power-up
.word start ; PC
.word 340 ; PSW with priority level 7
.=1000