#include <windows.h> #include <assert.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> #include "board/najv4.h" #include "jvs/jvs-bus.h" #include "jvs/jvs-cmd.h" #include "jvs/jvs-util.h" #include "util/dprintf.h" #include "util/dump.h" static void najv4_transact( struct jvs_node *node, const void *bytes, size_t nbytes, struct iobuf *resp); static bool najv4_sense(struct jvs_node *node); static HRESULT najv4_cmd( void *ctx, struct const_iobuf *req, struct iobuf *resp); static HRESULT najv4_cmd_read_id( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_get_cmd_version( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_get_jvs_version( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_get_comm_version( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_get_features( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_write_pcb_info( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_read_switches( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_read_coin( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_read_analogs( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_analog_out( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_write_gpio( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_reset(struct najv4 *najv4, struct const_iobuf *buf); static HRESULT najv4_cmd_assign_addr( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_namco_extend_cmd( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static HRESULT najv4_cmd_namco_extend_noop_cmd( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf); static const uint8_t najv4_ident[] = "namco ltd.;NA-JV;Ver4.00;JPN,Multipurpose"; static uint8_t najv4_features[] = { /* Feature : 0x01 : Players and switches Param1 : 1 : Number of players Param2 : 18 : Number of switches per player Param3 : 0 : N/A */ 0x01, 0x01, 0x18, 0x00, /* Feature : 0x02 : Coin slots Param1 : 2 : Number of coin slots Param2 : 0 : N/A Param3 : 0 : N/A */ 0x02, 0x02, 0x00, 0x00, /* Feature : 0x03 : Analog inputs Param1 : 8 : Number of ADC channels Param2 : 10 : Effective bits of resolution per ADC Param3 : 0 : N/A */ 0x03, 0x08, 0x10, 0x00, /* Feature : 0x04 : Rotary Input Param1 : 4 : Number of Rotary channels Param2 : 0 : N/A Param3 : 0 : N/A */ 0x04, 0x04, 0x00, 0x00, /* Feature : 0x12 : GPIO outputs Param1 : 3 : Number of ports (8 bits per port) Param2 : 0 : N/A Param3 : 0 : N/A */ 0x12, 0x12, 0x00, 0x00, /* Feature : 0x13 : Analog outputs Param1 : 2 : Number of channels Param2 : 0 : N/A Param3 : 0 : N/A */ 0x13, 0x02, 0x00, 0x00, /* Feature : 0x14 : Character Output Param1 : 0x50 : Width Param2 : 0x19 : Height Param3 : 0 : Type */ 0x14, 0x50, 0x19, 0x00, /* Feature : 0x00 : End of capabilities */ 0x00, }; void najv4_init( struct najv4 *najv4, struct jvs_node *next, const struct najv4_ops *ops, void *ops_ctx) { assert(najv4 != NULL); assert(ops != NULL); najv4->jvs.next = next; najv4->jvs.transact = najv4_transact; najv4->jvs.sense = najv4_sense; najv4->addr = 0xFF; najv4->ops = ops; najv4->ops_ctx = ops_ctx; } struct jvs_node *najv4_to_jvs_node(struct najv4 *najv4) { assert(najv4 != NULL); return &najv4->jvs; } static void najv4_transact( struct jvs_node *node, const void *bytes, size_t nbytes, struct iobuf *resp) { struct najv4 *najv4; assert(node != NULL); assert(bytes != NULL); assert(resp != NULL); najv4 = CONTAINING_RECORD(node, struct najv4, jvs); jvs_crack_request(bytes, nbytes, resp, najv4->addr, najv4_cmd, najv4); } static bool najv4_sense(struct jvs_node *node) { struct najv4 *najv4; assert(node != NULL); najv4 = CONTAINING_RECORD(node, struct najv4, jvs); return najv4->addr == 0xFF; } static HRESULT najv4_cmd( void *ctx, struct const_iobuf *req, struct iobuf *resp) { struct najv4 *najv4; najv4 = ctx; switch (req->bytes[req->pos]) { case JVS_CMD_READ_ID: return najv4_cmd_read_id(najv4, req, resp); case JVS_CMD_GET_CMD_VERSION: return najv4_cmd_get_cmd_version(najv4, req, resp); case JVS_CMD_GET_JVS_VERSION: return najv4_cmd_get_jvs_version(najv4, req, resp); case JVS_CMD_GET_COMM_VERSION: return najv4_cmd_get_comm_version(najv4, req, resp); case JVS_CMD_GET_FEATURES: return najv4_cmd_get_features(najv4, req, resp); case JVS_CMD_WRITE_PCB_INFO: return najv4_cmd_write_pcb_info(najv4, req, resp); case JVS_CMD_READ_SWITCHES: return najv4_cmd_read_switches(najv4, req, resp); case JVS_CMD_READ_COIN: return najv4_cmd_read_coin(najv4, req, resp); case JVS_CMD_READ_ANALOGS: return najv4_cmd_read_analogs(najv4, req, resp); case JVS_CMD_ANALOG_OUT: return najv4_cmd_analog_out(najv4, req, resp); case JVS_CMD_WRITE_GPIO: return najv4_cmd_write_gpio(najv4, req, resp); case JVS_CMD_RESET: return najv4_cmd_reset(najv4, req); case JVS_CMD_ASSIGN_ADDR: return najv4_cmd_assign_addr(najv4, req, resp); case JVS_CMD_NAMCO_EXTEND: return najv4_cmd_namco_extend_cmd(najv4, req, resp); default: dprintf("JVS I/O: Node %02x: Unhandled command byte %02x\n", najv4->addr, req->bytes[req->pos]); return E_NOTIMPL; } } static HRESULT najv4_cmd_read_id( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { uint8_t req; HRESULT hr; hr = iobuf_read_8(req_buf, &req); if (FAILED(hr)) { return hr; } dprintf("JVS I/O: Read ID\n"); /* Write report byte */ hr = iobuf_write_8(resp_buf, 0x01); if (FAILED(hr)) { return hr; } /* Write the identification string. The NUL terminator at the end of this C string is also sent, and it naturally terminates the response chunk. */ return iobuf_write(resp_buf, najv4_ident, sizeof(najv4_ident)); } static HRESULT najv4_cmd_get_cmd_version( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { uint8_t req; uint8_t resp[2]; HRESULT hr; hr = iobuf_read_8(req_buf, &req); if (FAILED(hr)) { return hr; } dprintf("JVS I/O: Get command format version\n"); resp[0] = 0x01; /* Report byte */ resp[1] = 0x13; /* Command format version BCD */ return iobuf_write(resp_buf, resp, sizeof(resp)); } static HRESULT najv4_cmd_get_jvs_version( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { uint8_t req; uint8_t resp[2]; HRESULT hr; hr = iobuf_read_8(req_buf, &req); if (FAILED(hr)) { return hr; } dprintf("JVS I/O: Get JVS version\n"); resp[0] = 0x01; /* Report byte */ resp[1] = 0x31; /* JVS version BCD */ return iobuf_write(resp_buf, resp, sizeof(resp)); } static HRESULT najv4_cmd_get_comm_version( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { uint8_t req; uint8_t resp[2]; HRESULT hr; hr = iobuf_read_8(req_buf, &req); if (FAILED(hr)) { return hr; } dprintf("JVS I/O: Get communication version\n"); resp[0] = 0x01; /* Report byte */ resp[1] = 0x10; /* "Communication version" BCD */ return iobuf_write(resp_buf, resp, sizeof(resp)); } static HRESULT najv4_cmd_get_features( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { uint8_t req; HRESULT hr; hr = iobuf_read_8(req_buf, &req); if (FAILED(hr)) { return hr; } dprintf("JVS I/O: Get features\n"); hr = iobuf_write_8(resp_buf, 0x01); /* Write report byte */ if (FAILED(hr)) { return hr; } return iobuf_write(resp_buf, najv4_features, sizeof(najv4_features)); } static HRESULT najv4_cmd_write_pcb_info( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { uint8_t strlen = (int)req_buf->nbytes - 4; // subtract 2 bytes at the begining, command byte, and sync byte uint8_t cmd; uint8_t info[100]; // No more then 100 characters dprintf("JVS I/O: PCB Info "); iobuf_read_8(req_buf, &cmd); iobuf_read(req_buf, info, strlen); dprintf("%s\n", info); // Just acknowlage we read it return iobuf_write_8(resp_buf, 0x01); } static HRESULT najv4_cmd_read_switches( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { struct jvs_req_read_switches req; struct najv4_switch_state state; HRESULT hr; /* Read req */ hr = iobuf_read(req_buf, &req, sizeof(req)); if (FAILED(hr)) { return hr; } #if 0 dprintf("JVS I/O: Read switches, np=%i, bpp=%i\n", req.num_players, req.bytes_per_player); #endif /* Build response */ hr = iobuf_write_8(resp_buf, 0x01); /* Report byte */ if (FAILED(hr)) { return hr; } memset(&state, 0, sizeof(state)); if (najv4->ops != NULL) { najv4->ops->read_switches(najv4->ops_ctx, &state); } hr = iobuf_write_8(resp_buf, state.system); /* Test, Tilt lines */ if (FAILED(hr)) { return hr; } if (req.num_players > 0) { hr = iobuf_write_be16(resp_buf, state.p1); if (FAILED(hr)) { return hr; } } if (req.num_players > 1) { hr = iobuf_write_be16(resp_buf, state.p2); if (FAILED(hr)) { return hr; } } hr = iobuf_write_8(resp_buf, 0x00); // Not sure what this byte is but it needs to be here return hr; } static HRESULT najv4_cmd_read_coin( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { struct jvs_req_read_coin req; uint16_t ncoins; uint8_t i; HRESULT hr; /* Read req */ hr = iobuf_read(req_buf, &req, sizeof(req)); if (FAILED(hr)) { return hr; } //dprintf("JVS I/O: Read coin, nslots=%i\n", req.nslots); /* Write report byte */ hr = iobuf_write_8(resp_buf, 0x01); if (FAILED(hr)) { return hr; } /* Write slot detail */ for (i = 0 ; i < req.nslots ; i++) { ncoins = 0; if (najv4->ops->read_coin_counter != NULL) { najv4->ops->read_coin_counter(najv4->ops_ctx, i, &ncoins); } hr = iobuf_write_be16(resp_buf, ncoins); if (FAILED(hr)) { return hr; } } return hr; } static HRESULT najv4_cmd_read_analogs( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { struct jvs_req_read_analogs req; uint16_t analogs[8]; uint8_t i; HRESULT hr; /* Read req */ hr = iobuf_read(req_buf, &req, sizeof(req)); if (FAILED(hr)) { return hr; } if (req.nanalogs > _countof(analogs)) { dprintf("JVS I/O: Invalid analog count %i\n", req.nanalogs); return E_FAIL; } //dprintf("JVS I/O: Read analogs, nanalogs=%i\n", req.nanalogs); /* Write report byte */ hr = iobuf_write_8(resp_buf, 0x01); if (FAILED(hr)) { return hr; } /* Write analogs */ memset(analogs, 0, sizeof(analogs)); if (najv4->ops->read_analogs != NULL) { najv4->ops->read_analogs(najv4->ops_ctx, analogs, req.nanalogs); } for (i = 0 ; i < req.nanalogs ; i++) { hr = iobuf_write_be16(resp_buf, analogs[i]); if (FAILED(hr)) { return hr; } } return hr; } static HRESULT najv4_cmd_analog_out( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { uint8_t cmd; uint8_t channel_ct; uint16_t bytes[MAX_PATH]; HRESULT hr; hr = iobuf_read_8(req_buf, &cmd); if (FAILED(hr)) { return hr; } hr = iobuf_read_8(req_buf, &channel_ct); if (FAILED(hr)) { return hr; } for (int i = 0; i < channel_ct; i++) { iobuf_read_be16(req_buf, &bytes[i]); } //dprintf("JVS I/O: Write Analog Out to %d channels\n", channel_ct); return iobuf_write_8(resp_buf, 0x01); } static HRESULT najv4_cmd_write_gpio( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { uint8_t cmd; uint8_t nbytes; uint8_t bytes[3]; HRESULT hr; /* Read request header */ hr = iobuf_read_8(req_buf, &cmd); if (FAILED(hr)) { return hr; } hr = iobuf_read_8(req_buf, &nbytes); if (FAILED(hr)) { return hr; } if (nbytes > 3) { dprintf("JVS I/O: Invalid GPIO write size %i\n", nbytes); hr = iobuf_write_8(resp_buf, 0x02); if (FAILED(hr)) { return hr; } return E_FAIL; } /* Read payload */ memset(bytes, 0, sizeof(bytes)); hr = iobuf_read(req_buf, bytes, nbytes); if (FAILED(hr)) { return hr; } if (najv4->ops->write_gpio != NULL) { najv4->ops->write_gpio( najv4->ops_ctx, bytes[0] | (bytes[1] << 8) | (bytes[2] << 16)); } /* Write report byte */ return iobuf_write_8(resp_buf, 0x01); } static HRESULT najv4_cmd_reset(struct najv4 *najv4, struct const_iobuf *req_buf) { struct jvs_req_reset req; HRESULT hr; hr = iobuf_read(req_buf, &req, sizeof(req)); if (FAILED(hr)) { return hr; } dprintf("JVS I/O: Reset (param %02x)\n", req.unknown); najv4->addr = 0xFF; if (najv4->ops->reset != NULL) { najv4->ops->reset(najv4->ops_ctx); } /* No ack for this since it really is addressed to everybody */ return S_OK; } static HRESULT najv4_cmd_assign_addr( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { struct jvs_req_assign_addr req; bool sense; HRESULT hr; hr = iobuf_read(req_buf, &req, sizeof(req)); if (FAILED(hr)) { return hr; } sense = jvs_node_sense(najv4->jvs.next); dprintf("JVS I/O: Assign addr %02x sense %i\n", req.addr, sense); if (sense) { /* That address is for somebody else */ return S_OK; } najv4->addr = req.addr; return iobuf_write_8(resp_buf, 0x01); } static HRESULT najv4_cmd_namco_extend_cmd( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { switch(req_buf->bytes[req_buf->pos + 1]) { case JVS_NAMCO_EXTEND_CMD_NOOP_18: return najv4_cmd_namco_extend_noop_cmd(najv4, req_buf, resp_buf); default: dprintf("JVS I/O: Node %X Unknown Namco Extended command %X\n", najv4->addr, req_buf->bytes[req_buf->pos + 1]); return E_NOTIMPL; } } static HRESULT najv4_cmd_namco_extend_noop_cmd( struct najv4 *najv4, struct const_iobuf *req_buf, struct iobuf *resp_buf) { uint8_t strlen = (int)req_buf->nbytes - 9; uint8_t info[100]; uint8_t cmd; uint8_t subcmd; uint16_t hardcode; uint16_t param; HRESULT hr; hr = iobuf_read_8(req_buf, &cmd); // cmd (0x70) hr = iobuf_read_8(req_buf, &subcmd); // subcmd (0x18) hr = iobuf_read_be16(req_buf, &hardcode); // some constant (0x504c) hr = iobuf_read_be16(req_buf, ¶m); // param1 if (FAILED(hr)) { return hr; } dprintf("JVS I/O: Namco Extended Command 0x18 param: %X", param); switch (param) { case 0x14d0: // read some string hr = iobuf_read(req_buf, info, strlen); if (FAILED(hr)) { return hr; } dprintf("\t%s\n", info); return iobuf_write_8(resp_buf, 1); case 0x14B4: case 0x143c: // Run it back?? hr = iobuf_read_8(req_buf, &cmd); // cmd (0x70) hr = iobuf_read_8(req_buf, &subcmd); // subcmd (0x18) hr = iobuf_read_be16(req_buf, &hardcode); // some constant (0x504c) hr = iobuf_read_be16(req_buf, ¶m); // param1 dprintf("\tsecond param: %X\n", param); return iobuf_write_8(resp_buf, 1); default: dprintf("\nJVS I/O: Namco Extended Command 0x18 param unknown! %04X\n", param); dump_const_iobuf(req_buf); return iobuf_write_8(resp_buf, 1); } }